rtext 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +9 -0
- data/RText_Plugin_Implementation_Guide +23 -2
- data/Rakefile +1 -1
- data/lib/rtext/completer.rb +4 -7
- data/lib/rtext/default_loader.rb +46 -8
- data/lib/rtext/default_service_provider.rb +8 -4
- data/lib/rtext/instantiator.rb +23 -23
- data/lib/rtext/parser.rb +80 -25
- data/lib/rtext/serializer.rb +1 -1
- data/lib/rtext/service.rb +18 -8
- data/test/completer_test.rb +10 -10
- data/test/instantiator_test.rb +372 -7
- data/test/serializer_test.rb +4 -2
- metadata +10 -5
data/CHANGELOG
CHANGED
@@ -14,3 +14,12 @@
|
|
14
14
|
* Added result limit option to DefaultServiceProvider
|
15
15
|
* Removed short_class_names option from Language
|
16
16
|
|
17
|
+
=0.4.0
|
18
|
+
|
19
|
+
* Made instantiator a lot more robust against parse errors
|
20
|
+
* Added DefaultLoader option to not reload fragments with errors
|
21
|
+
* Added service load progress indication and custom problem severity
|
22
|
+
* Fixed serialization of enum literals starting with a digit
|
23
|
+
* Fixed used port detection in service
|
24
|
+
* Fixed completion option order to match order defined in lanugage
|
25
|
+
|
@@ -188,13 +188,34 @@ The command implicitly reloads the model from the filesystem before checking for
|
|
188
188
|
no parameters
|
189
189
|
|
190
190
|
Response Format:
|
191
|
-
result line
|
192
|
-
result line
|
191
|
+
result line 1: <file name>
|
192
|
+
result line 2..n <line number>;<message>
|
193
193
|
|
194
194
|
Note that the file name is repeated only once for all problems within a file to reduce the amout of
|
195
195
|
result data which needs to be transmitted.
|
196
196
|
|
197
197
|
|
198
|
+
===Command: Show Problems 2
|
199
|
+
|
200
|
+
Since protocol version 1
|
201
|
+
|
202
|
+
Like the first version but provides progress information while the model is loaded and problem severity.
|
203
|
+
|
204
|
+
Request Format:
|
205
|
+
command id: "show_problems2"
|
206
|
+
no parameters
|
207
|
+
|
208
|
+
Response Format:
|
209
|
+
result line 1..n: progress:<[0..100]>
|
210
|
+
result line n+1: <file name>
|
211
|
+
result line n+2..m <[diwef]>;<line number>;<message>
|
212
|
+
|
213
|
+
Progress is expressed by a value between 0 and 100. The backend may choose to return any number
|
214
|
+
of progress lines. It should start with 0 and end with 100 and progress should not go backwards.
|
215
|
+
|
216
|
+
Problem severity is expressed with one of the following flags: d (debug), i (info), w (warn), e (error), f (fatal)
|
217
|
+
|
218
|
+
|
198
219
|
===Command: Reference Targets
|
199
220
|
|
200
221
|
This command is used to retrieve the targets of a given reference. The reference location is expressed
|
data/Rakefile
CHANGED
data/lib/rtext/completer.rb
CHANGED
@@ -34,7 +34,7 @@ class Completer
|
|
34
34
|
else
|
35
35
|
# all target types which don't need a label
|
36
36
|
# and all lables which are needed by a potential target type
|
37
|
-
clazz.
|
37
|
+
@lang.containments(clazz).each do |r|
|
38
38
|
([r.eType] + r.eType.eAllSubTypes).select{|t| !t.abstract}.each do |t|
|
39
39
|
if @lang.containments_by_target_type(clazz, t).size > 1
|
40
40
|
labled_refs << r
|
@@ -48,8 +48,7 @@ class Completer
|
|
48
48
|
sort{|a,b| a.name <=> b.name}.collect do |c|
|
49
49
|
class_completion_option(c)
|
50
50
|
end +
|
51
|
-
labled_refs.uniq.select{|r| r.name.index(context.prefix) == 0}.
|
52
|
-
sort!{|a,b| a.name <=> b.name}.collect do |r|
|
51
|
+
labled_refs.uniq.select{|r| r.name.index(context.prefix) == 0}.collect do |r|
|
53
52
|
CompletionOption.new("#{r.name}:", "<#{r.eType.name}>")
|
54
53
|
end
|
55
54
|
else
|
@@ -86,16 +85,14 @@ class Completer
|
|
86
85
|
context.element.getGenericAsArray(f.name).size > 0}
|
87
86
|
result += @lang.unlabled_arguments(clazz).
|
88
87
|
select{|f| f.name.index(context.prefix) == 0 &&
|
89
|
-
context.element.getGenericAsArray(f.name).empty?}[0..0].
|
90
|
-
sort{|a,b| a.name <=> b.name}.collect do |f|
|
88
|
+
context.element.getGenericAsArray(f.name).empty?}[0..0].collect do |f|
|
91
89
|
CompletionOption.new("<#{f.name}>", "<#{f.eType.name}>")
|
92
90
|
end
|
93
91
|
end
|
94
92
|
# label completion
|
95
93
|
result += @lang.labled_arguments(clazz).
|
96
94
|
select{|f| f.name.index(context.prefix) == 0 &&
|
97
|
-
context.element.getGenericAsArray(f.name).empty?}.
|
98
|
-
sort{|a,b| a.name <=> b.name}.collect do |f|
|
95
|
+
context.element.getGenericAsArray(f.name).empty?}.collect do |f|
|
99
96
|
CompletionOption.new("#{f.name}:", "<#{f.eType.name}>")
|
100
97
|
end
|
101
98
|
result
|
data/lib/rtext/default_loader.rb
CHANGED
@@ -29,6 +29,10 @@ class DefaultLoader
|
|
29
29
|
# :cache
|
30
30
|
# a fragment cache to be used for loading
|
31
31
|
#
|
32
|
+
# :dont_reload_with_errors
|
33
|
+
# if set to true, don't reload fragments which have parse errors
|
34
|
+
# instead keep the existing fragment but attach the new problem list
|
35
|
+
#
|
32
36
|
def initialize(language, fragmented_model, options={})
|
33
37
|
@lang = language
|
34
38
|
@model = fragmented_model
|
@@ -40,6 +44,7 @@ class DefaultLoader
|
|
40
44
|
@fragment_by_file = {}
|
41
45
|
pattern = options[:pattern]
|
42
46
|
@file_provider = options[:file_provider] || proc { Dir.glob(pattern) }
|
47
|
+
@dont_reload_with_errors = options[:dont_reload_with_errors]
|
43
48
|
end
|
44
49
|
|
45
50
|
# Loads or reloads model fragments from files using the file patterns or file provider
|
@@ -48,16 +53,19 @@ class DefaultLoader
|
|
48
53
|
# :before_load
|
49
54
|
# a proc which is called before a file is actually loaded, receives the fragment to load
|
50
55
|
# into and a symbol indicating the kind of loading: :load, :load_cached, :load_update_cache
|
56
|
+
# optionally, the proc may take third argument which is the overall number of files
|
51
57
|
# default: no before load proc
|
52
58
|
#
|
53
59
|
# :after_load
|
54
60
|
# a proc which is called after a file has been loaded, receives the fragment loaded
|
61
|
+
# optionally, the proc may take second argument which is the overall number of files
|
55
62
|
# default: no after load proc
|
56
63
|
#
|
57
64
|
def load(options={})
|
58
65
|
@before_load_proc = options[:before_load]
|
59
66
|
@after_load_proc = options[:after_load]
|
60
67
|
files = @file_provider.call
|
68
|
+
@num_files = files.size
|
61
69
|
@change_detector.check_files(files)
|
62
70
|
@model.resolve(:fragment_provider => method(:fragment_provider),
|
63
71
|
:use_target_type => @lang.per_type_identifier)
|
@@ -79,8 +87,18 @@ class DefaultLoader
|
|
79
87
|
end
|
80
88
|
|
81
89
|
def file_changed(file)
|
82
|
-
|
83
|
-
|
90
|
+
fragment = RGen::Fragment::ModelFragment.new(file,
|
91
|
+
:identifier_provider => @lang.identifier_provider)
|
92
|
+
load_fragment_cached(fragment)
|
93
|
+
if @dont_reload_with_errors && fragment.data[:problems].size > 0
|
94
|
+
# keep old fragment but attach new problems
|
95
|
+
old_fragment = @fragment_by_file[file]
|
96
|
+
old_fragment.data[:problems] = fragment.data[:problems]
|
97
|
+
else
|
98
|
+
file_removed(file)
|
99
|
+
@model.add_fragment(fragment)
|
100
|
+
@fragment_by_file[file] = fragment
|
101
|
+
end
|
84
102
|
end
|
85
103
|
|
86
104
|
def fragment_provider(element)
|
@@ -101,18 +119,38 @@ class DefaultLoader
|
|
101
119
|
end
|
102
120
|
end
|
103
121
|
if result == :invalid
|
104
|
-
|
122
|
+
call_before_load_proc(fragment, :load_update_cache)
|
105
123
|
load_fragment(fragment)
|
106
124
|
@cache.store(fragment)
|
107
|
-
|
125
|
+
call_after_load_proc(fragment)
|
108
126
|
else
|
109
|
-
|
110
|
-
|
127
|
+
call_before_load_proc(fragment, :load_cached)
|
128
|
+
call_after_load_proc(fragment)
|
111
129
|
end
|
112
130
|
else
|
113
|
-
|
131
|
+
call_before_load_proc(fragment, :load)
|
114
132
|
load_fragment(fragment)
|
115
|
-
|
133
|
+
call_after_load_proc(fragment)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def call_before_load_proc(fragment, kind)
|
138
|
+
if @before_load_proc
|
139
|
+
if @before_load_proc.arity == 3
|
140
|
+
@before_load_proc.call(fragment, kind, @num_files)
|
141
|
+
else
|
142
|
+
@before_load_proc.call(fragment, kind)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def call_after_load_proc(fragment)
|
148
|
+
if @after_load_proc
|
149
|
+
if @after_load_proc.arity == 2
|
150
|
+
@after_load_proc.call(fragment, @num_files)
|
151
|
+
else
|
152
|
+
@after_load_proc.call(fragment)
|
153
|
+
end
|
116
154
|
end
|
117
155
|
end
|
118
156
|
|
@@ -13,8 +13,12 @@ class DefaultServiceProvider
|
|
13
13
|
})
|
14
14
|
end
|
15
15
|
|
16
|
-
def load_model
|
17
|
-
|
16
|
+
def load_model(options={})
|
17
|
+
if options[:on_progress]
|
18
|
+
@loader.load(:after_load => options[:on_progress])
|
19
|
+
else
|
20
|
+
@loader.load
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
ReferenceCompletionOption = Struct.new(:identifier, :type)
|
@@ -81,8 +85,8 @@ class DefaultServiceProvider
|
|
81
85
|
|
82
86
|
FileProblems = Struct.new(:file, :problems)
|
83
87
|
Problem = Struct.new(:severity, :line, :message)
|
84
|
-
def get_problems
|
85
|
-
load_model
|
88
|
+
def get_problems(options={})
|
89
|
+
load_model(options)
|
86
90
|
result = []
|
87
91
|
@model.fragments.sort{|a,b| a.location <=> b.location}.each do |fragment|
|
88
92
|
problems = []
|
data/lib/rtext/instantiator.rb
CHANGED
@@ -46,28 +46,27 @@ class Instantiator
|
|
46
46
|
@fragment_ref = options[:fragment_ref]
|
47
47
|
@context_class_stack = []
|
48
48
|
parser = Parser.new(@lang.reference_regexp)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@root_elements.clear
|
49
|
+
@root_elements.clear
|
50
|
+
parser_problems = []
|
51
|
+
parser.parse(str,
|
52
|
+
:descent_visitor => lambda do |command|
|
53
|
+
clazz = @lang.class_by_command(command.value, @context_class_stack.last)
|
54
|
+
# in case no class is found, nil will be pushed, this will case the next command
|
55
|
+
# lookup to act as if called from toplevel
|
56
|
+
@context_class_stack.push(clazz)
|
57
|
+
end,
|
58
|
+
:ascent_visitor => lambda do |*args|
|
59
|
+
if args[0]
|
60
|
+
element =create_element(*args)
|
61
|
+
@context_class_stack.pop
|
62
|
+
element
|
63
|
+
else
|
64
|
+
unassociated_comments(args[3])
|
65
|
+
end
|
66
|
+
end,
|
67
|
+
:problems => parser_problems)
|
68
|
+
parser_problems.each do |p|
|
69
|
+
problem(p.message, p.line)
|
71
70
|
end
|
72
71
|
if @unresolved_refs
|
73
72
|
@unresolved_refs.each do |ur|
|
@@ -115,7 +114,7 @@ class Instantiator
|
|
115
114
|
if di_index < unlabled_args.size
|
116
115
|
set_argument(element, unlabled_args[di_index], a, defined_args, command.line)
|
117
116
|
di_index += 1
|
118
|
-
|
117
|
+
elsif a != nil
|
119
118
|
problem("Unexpected unlabled argument, #{unlabled_args.size} unlabled arguments expected", command.line)
|
120
119
|
end
|
121
120
|
end
|
@@ -203,6 +202,7 @@ class Instantiator
|
|
203
202
|
return
|
204
203
|
end
|
205
204
|
value = [value] unless value.is_a?(Array)
|
205
|
+
value.compact!
|
206
206
|
if value.size > 1 && !feature.many
|
207
207
|
problem("Argument '#{name}' can take only one value", line)
|
208
208
|
return
|
data/lib/rtext/parser.rb
CHANGED
@@ -2,6 +2,8 @@ module RText
|
|
2
2
|
|
3
3
|
class Parser
|
4
4
|
|
5
|
+
Problem = Struct.new(:message, :line)
|
6
|
+
|
5
7
|
def initialize(reference_regexp)
|
6
8
|
@reference_regexp = reference_regexp
|
7
9
|
end
|
@@ -9,10 +11,17 @@ class Parser
|
|
9
11
|
def parse(str, options)
|
10
12
|
@dsc_visitor = options[:descent_visitor]
|
11
13
|
@asc_visitor = options[:ascent_visitor]
|
14
|
+
@problems = options[:problems] || []
|
15
|
+
@non_consume_count = 0
|
16
|
+
@consume_problem_reported = false
|
12
17
|
@tokens = tokenize(str, @reference_regexp)
|
13
18
|
@last_line = @tokens.last && @tokens.last.line
|
14
|
-
|
15
|
-
|
19
|
+
#@debug = true
|
20
|
+
begin
|
21
|
+
while next_token
|
22
|
+
statement = parse_statement(true, true)
|
23
|
+
end
|
24
|
+
rescue InternalError
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
@@ -23,17 +32,22 @@ class Parser
|
|
23
32
|
if (next_token && next_token == :identifier) || !allow_unassociated_comment
|
24
33
|
comments << [ comment, :above] if comment
|
25
34
|
command = consume(:identifier)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
35
|
+
if command
|
36
|
+
@dsc_visitor.call(command)
|
37
|
+
arg_list = []
|
38
|
+
parse_argument_list(arg_list)
|
39
|
+
element_list = []
|
40
|
+
if next_token == "{"
|
41
|
+
parse_statement_block(element_list, comments)
|
42
|
+
end
|
43
|
+
eol_comment = parse_eol_comment
|
44
|
+
comments << [ eol_comment, :eol ] if eol_comment
|
45
|
+
consume(:newline)
|
46
|
+
@asc_visitor.call(command, arg_list, element_list, comments, is_root)
|
47
|
+
else
|
48
|
+
discard_until(:newline)
|
49
|
+
nil
|
32
50
|
end
|
33
|
-
eol_comment = parse_eol_comment
|
34
|
-
comments << [ eol_comment, :eol ] if eol_comment
|
35
|
-
consume(:newline)
|
36
|
-
@asc_visitor.call(command, arg_list, element_list, comments, is_root)
|
37
51
|
elsif comment
|
38
52
|
# if there is no statement, the comment is non-optional
|
39
53
|
comments << [ comment, :unassociated ]
|
@@ -42,6 +56,8 @@ class Parser
|
|
42
56
|
else
|
43
57
|
# die expecting an identifier (next token is not an identifier)
|
44
58
|
consume(:identifier)
|
59
|
+
discard_until(:newline)
|
60
|
+
nil
|
45
61
|
end
|
46
62
|
end
|
47
63
|
|
@@ -90,7 +106,8 @@ class Parser
|
|
90
106
|
else
|
91
107
|
eol_comment = parse_eol_comment
|
92
108
|
comments << [ eol_comment, :eol ] if eol_comment
|
93
|
-
consume(:newline)
|
109
|
+
nl = consume(:newline)
|
110
|
+
discard_until(:newline) unless nl
|
94
111
|
parse_statement
|
95
112
|
end
|
96
113
|
end
|
@@ -99,7 +116,8 @@ class Parser
|
|
99
116
|
consume("[")
|
100
117
|
eol_comment = parse_eol_comment
|
101
118
|
comments << [ eol_comment, :eol ] if eol_comment
|
102
|
-
consume(:newline)
|
119
|
+
nl = consume(:newline)
|
120
|
+
discard_until(:newline) unless nl
|
103
121
|
result = []
|
104
122
|
while next_token && next_token != "]"
|
105
123
|
statement = parse_statement(false, true)
|
@@ -114,7 +132,7 @@ class Parser
|
|
114
132
|
|
115
133
|
def parse_argument_list(arg_list)
|
116
134
|
first = true
|
117
|
-
while
|
135
|
+
while (AnyValue + [",", "[", :label, :error]).include?(next_token)
|
118
136
|
consume(",") unless first
|
119
137
|
first = false
|
120
138
|
parse_argument(arg_list)
|
@@ -142,7 +160,7 @@ class Parser
|
|
142
160
|
consume("[")
|
143
161
|
first = true
|
144
162
|
result = []
|
145
|
-
while
|
163
|
+
while (AnyValue + [",", :error]).include?(next_token)
|
146
164
|
consume(",") unless first
|
147
165
|
first = false
|
148
166
|
result << parse_value
|
@@ -151,34 +169,71 @@ class Parser
|
|
151
169
|
result
|
152
170
|
end
|
153
171
|
|
172
|
+
AnyValue = [:identifier, :integer, :float, :string, :boolean, :reference]
|
173
|
+
|
154
174
|
def parse_value
|
155
|
-
consume(
|
175
|
+
consume(*AnyValue)
|
156
176
|
end
|
157
177
|
|
158
178
|
def next_token
|
159
179
|
@tokens.first && @tokens.first.kind
|
160
180
|
end
|
161
181
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
182
|
+
def discard_until(kind)
|
183
|
+
t = @tokens.shift
|
184
|
+
if t
|
185
|
+
puts "discarding #{t.kind} #{t.value}" if @debug
|
186
|
+
while t.kind != kind
|
187
|
+
t = @tokens.shift
|
188
|
+
break unless t
|
189
|
+
puts "discarding #{t.kind} #{t.value}" if @debug
|
190
|
+
end
|
166
191
|
end
|
167
192
|
end
|
168
193
|
|
169
194
|
def consume(*args)
|
170
|
-
t = @tokens.
|
195
|
+
t = @tokens.first
|
171
196
|
if t.nil?
|
172
|
-
|
197
|
+
@non_consume_count += 1
|
198
|
+
report_consume_problem("Unexpected end of file, expected #{args.join(", ")}", @last_line)
|
199
|
+
return nil
|
173
200
|
end
|
174
201
|
if args.include?(t.kind)
|
202
|
+
@tokens.shift
|
203
|
+
@consume_problem_reported = false
|
204
|
+
@non_consume_count = 0
|
205
|
+
puts "consuming #{t.kind} #{t.value}" if @debug
|
175
206
|
t
|
176
207
|
else
|
177
208
|
if t.kind == :error
|
178
|
-
|
209
|
+
@tokens.shift
|
210
|
+
@non_consume_count = 0
|
211
|
+
report_consume_problem("Parse error on token '#{t.value}'", t.line)
|
212
|
+
return nil
|
179
213
|
else
|
180
214
|
value = " '#{t.value}'" if t.value
|
181
|
-
|
215
|
+
@non_consume_count += 1
|
216
|
+
report_consume_problem("Unexpected #{t.kind}#{value}, expected #{args.join(", ")}", t.line)
|
217
|
+
return nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class InternalError < Exception
|
223
|
+
end
|
224
|
+
|
225
|
+
def report_consume_problem(message, line)
|
226
|
+
problem = Problem.new(message, line)
|
227
|
+
if @non_consume_count > 100
|
228
|
+
# safety check, stop reoccuring problems to avoid endless loops
|
229
|
+
@problems << Problem.new("Internal error", line)
|
230
|
+
puts [@problems.last.message, @problems.last.line].inspect if @debug
|
231
|
+
raise InternalError.new
|
232
|
+
else
|
233
|
+
if !@consume_problem_reported
|
234
|
+
@consume_problem_reported = true
|
235
|
+
@problems << problem
|
236
|
+
puts [@problems.last.message, @problems.last.line].inspect if @debug
|
182
237
|
end
|
183
238
|
end
|
184
239
|
end
|
data/lib/rtext/serializer.rb
CHANGED
@@ -133,7 +133,7 @@ class Serializer
|
|
133
133
|
result << v.to_s
|
134
134
|
end
|
135
135
|
elsif feature.eType.is_a?(RGen::ECore::EEnum)
|
136
|
-
if v.to_s =~
|
136
|
+
if v.to_s =~ /^\d|\W/
|
137
137
|
result << "\"#{v.to_s.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
|
138
138
|
gsub("\r","\\r").gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")}\""
|
139
139
|
else
|
data/lib/rtext/service.rb
CHANGED
@@ -54,13 +54,23 @@ class Service
|
|
54
54
|
cmd = lines.shift
|
55
55
|
invocation_id = lines.shift
|
56
56
|
response = nil
|
57
|
+
progress_index = 0
|
57
58
|
case cmd
|
59
|
+
when "protocol_version"
|
60
|
+
response = ["1"]
|
58
61
|
when "refresh"
|
59
62
|
response = refresh(lines)
|
60
63
|
when "complete"
|
61
64
|
response = complete(lines)
|
62
65
|
when "show_problems"
|
63
66
|
response = get_problems(lines)
|
67
|
+
when "show_problems2"
|
68
|
+
response = get_problems(lines, :with_severity => true, :on_progress => lambda do |frag, num_frags|
|
69
|
+
progress_index += 1
|
70
|
+
num_frags = 1 if num_frags < 1
|
71
|
+
progress = ["progress: #{progress_index*100/num_frags}"]
|
72
|
+
send_response(progress, invocation_id, socket, from, :incremental => true)
|
73
|
+
end)
|
64
74
|
when "get_reference_targets"
|
65
75
|
response = get_reference_targets(lines)
|
66
76
|
when "get_elements"
|
@@ -79,7 +89,7 @@ class Service
|
|
79
89
|
|
80
90
|
private
|
81
91
|
|
82
|
-
def send_response(response, invocation_id, socket, from)
|
92
|
+
def send_response(response, invocation_id, socket, from, options={})
|
83
93
|
@logger.debug(response.inspect) if @logger
|
84
94
|
loop do
|
85
95
|
packet_lines = []
|
@@ -88,7 +98,7 @@ class Service
|
|
88
98
|
size += response.first.size
|
89
99
|
packet_lines << response.shift
|
90
100
|
end
|
91
|
-
if response.size > 0
|
101
|
+
if options[:incremental] || response.size > 0
|
92
102
|
packet_lines.unshift("more\n")
|
93
103
|
else
|
94
104
|
packet_lines.unshift("last\n")
|
@@ -104,7 +114,7 @@ class Service
|
|
104
114
|
port = PortRangeStart
|
105
115
|
begin
|
106
116
|
socket.bind("localhost", port)
|
107
|
-
rescue Errno::EADDRINUSE
|
117
|
+
rescue Errno::EADDRINUSE, Errno::EAFNOSUPPORT
|
108
118
|
port += 1
|
109
119
|
retry if port <= PortRangeEnd
|
110
120
|
raise
|
@@ -120,7 +130,7 @@ class Service
|
|
120
130
|
def complete(lines)
|
121
131
|
linepos = lines.shift.to_i
|
122
132
|
context = ContextBuilder.build_context(@lang, lines, linepos)
|
123
|
-
@logger.debug("context element: #{@lang.identifier_provider.call(context.element, nil)}") if @logger
|
133
|
+
@logger.debug("context element: #{@lang.identifier_provider.call(context.element, nil)}") if context && @logger
|
124
134
|
current_line = lines.pop
|
125
135
|
current_line ||= ""
|
126
136
|
options = @completer.complete(context, lambda {|ref|
|
@@ -132,13 +142,13 @@ class Service
|
|
132
142
|
}
|
133
143
|
end
|
134
144
|
|
135
|
-
def get_problems(lines)
|
136
|
-
# TODO: severity
|
145
|
+
def get_problems(lines, options={})
|
137
146
|
result = []
|
138
|
-
|
147
|
+
severity = options[:with_severity] ? "e;" : ""
|
148
|
+
@service_provider.get_problems(:on_progress => options[:on_progress]).each do |fp|
|
139
149
|
result << fp.file+"\n"
|
140
150
|
fp.problems.each do |p|
|
141
|
-
result << "#{p.line};#{p.message}\n"
|
151
|
+
result << "#{severity}#{p.line};#{p.message}\n"
|
142
152
|
end
|
143
153
|
end
|
144
154
|
result
|
data/test/completer_test.rb
CHANGED
@@ -41,10 +41,10 @@ def test_after_command
|
|
41
41
|
END
|
42
42
|
assert_options([
|
43
43
|
["<unlabled1>", "<EString>"],
|
44
|
+
["text:", "<EString>"],
|
44
45
|
["nums:", "<EInt>"],
|
45
|
-
["others:", "<TestNode>"],
|
46
46
|
["related:", "<TestNode>"],
|
47
|
-
["
|
47
|
+
["others:", "<TestNode>"]
|
48
48
|
], options)
|
49
49
|
end
|
50
50
|
|
@@ -71,9 +71,9 @@ def test_after_labled_value
|
|
71
71
|
TestNode nums: 1, |
|
72
72
|
END
|
73
73
|
assert_options([
|
74
|
-
["
|
74
|
+
["text:", "<EString>"],
|
75
75
|
["related:", "<TestNode>"],
|
76
|
-
["
|
76
|
+
["others:", "<TestNode>"]
|
77
77
|
], options)
|
78
78
|
end
|
79
79
|
|
@@ -83,10 +83,10 @@ def test_after_unlabled_value
|
|
83
83
|
END
|
84
84
|
assert_options([
|
85
85
|
["<unlabled2>", "<EInt>"],
|
86
|
+
["text:", "<EString>"],
|
86
87
|
["nums:", "<EInt>"],
|
87
|
-
["others:", "<TestNode>"],
|
88
88
|
["related:", "<TestNode>"],
|
89
|
-
["
|
89
|
+
["others:", "<TestNode>"]
|
90
90
|
], options)
|
91
91
|
end
|
92
92
|
|
@@ -95,10 +95,10 @@ def test_after_unlabled_value2
|
|
95
95
|
TestNode "bla", 1, |
|
96
96
|
END
|
97
97
|
assert_options([
|
98
|
+
["text:", "<EString>"],
|
98
99
|
["nums:", "<EInt>"],
|
99
|
-
["others:", "<TestNode>"],
|
100
100
|
["related:", "<TestNode>"],
|
101
|
-
["
|
101
|
+
["others:", "<TestNode>"]
|
102
102
|
], options)
|
103
103
|
end
|
104
104
|
|
@@ -107,9 +107,9 @@ def test_after_array
|
|
107
107
|
TestNode nums: [1, 2], |
|
108
108
|
END
|
109
109
|
assert_options([
|
110
|
-
["
|
110
|
+
["text:", "<EString>"],
|
111
111
|
["related:", "<TestNode>"],
|
112
|
-
["
|
112
|
+
["others:", "<TestNode>"]
|
113
113
|
], options)
|
114
114
|
end
|
115
115
|
|
data/test/instantiator_test.rb
CHANGED
@@ -12,7 +12,7 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
12
12
|
module TestMM
|
13
13
|
extend RGen::MetamodelBuilder::ModuleExtension
|
14
14
|
class TestNode < RGen::MetamodelBuilder::MMBase
|
15
|
-
SomeEnum = RGen::MetamodelBuilder::DataTypes::Enum.new([:A, :B, :'non-word*chars'])
|
15
|
+
SomeEnum = RGen::MetamodelBuilder::DataTypes::Enum.new([:A, :B, :'non-word*chars', :'2you'])
|
16
16
|
has_attr 'text', String
|
17
17
|
has_attr 'integer', Integer
|
18
18
|
has_attr 'boolean', Boolean
|
@@ -199,7 +199,7 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
199
199
|
TestNode text: B
|
200
200
|
TestNode a problem here
|
201
201
|
), TestMM, :file_name => "some_file")
|
202
|
-
assert_equal
|
202
|
+
assert_equal "some_file", problems.first.file
|
203
203
|
end
|
204
204
|
|
205
205
|
def test_file_name_setter
|
@@ -323,6 +323,19 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
323
323
|
assert_model_simple(env)
|
324
324
|
end
|
325
325
|
|
326
|
+
def test_no_newline_at_eof
|
327
|
+
env, problems = instantiate(%Q(
|
328
|
+
TestNode), TestMM)
|
329
|
+
assert_no_problems(problems)
|
330
|
+
end
|
331
|
+
|
332
|
+
def test_no_newline_at_eof2
|
333
|
+
env, problems = instantiate(%Q(
|
334
|
+
TestNode {
|
335
|
+
}), TestMM)
|
336
|
+
assert_no_problems(problems)
|
337
|
+
end
|
338
|
+
|
326
339
|
#
|
327
340
|
# references
|
328
341
|
#
|
@@ -585,7 +598,7 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
585
598
|
}
|
586
599
|
), TestMM2)
|
587
600
|
assert_problems([
|
588
|
-
/unexpected }, expected identifier/i
|
601
|
+
/unexpected \}, expected identifier/i
|
589
602
|
], problems)
|
590
603
|
end
|
591
604
|
|
@@ -707,6 +720,351 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
707
720
|
assert_problems([/command 'NonRootClass' can not be used on root level/i], problems)
|
708
721
|
end
|
709
722
|
|
723
|
+
#
|
724
|
+
# problem recovery
|
725
|
+
#
|
726
|
+
|
727
|
+
def test_missing_value
|
728
|
+
root_elements = []
|
729
|
+
env, problems = instantiate(%Q(
|
730
|
+
TestNode nums: 1, text:
|
731
|
+
TestNode nums: 2, text: {
|
732
|
+
SubNode
|
733
|
+
}
|
734
|
+
TestNode text: ,nums: 3 {
|
735
|
+
SubNode
|
736
|
+
}
|
737
|
+
TestNode nums: , text: , bla:
|
738
|
+
), TestMM, :root_elements => root_elements)
|
739
|
+
assert_equal 4, root_elements.size
|
740
|
+
assert_equal [1], root_elements[0].nums
|
741
|
+
assert_nil root_elements[0].text
|
742
|
+
assert_equal [2], root_elements[1].nums
|
743
|
+
assert_equal 1, root_elements[1].childs.size
|
744
|
+
assert_equal [3], root_elements[2].nums
|
745
|
+
assert_equal 1, root_elements[2].childs.size
|
746
|
+
assert_problems([
|
747
|
+
[/unexpected newline, expected.*integer/i, 2],
|
748
|
+
[/unexpected \{, expected.*integer/i, 3],
|
749
|
+
[/unexpected ,, expected.*integer/i, 6],
|
750
|
+
[/unexpected ,, expected.*integer/i, 9],
|
751
|
+
[/unexpected ,, expected.*integer/i, 9],
|
752
|
+
[/unexpected newline, expected.*integer/i, 9],
|
753
|
+
[/unknown argument 'bla'/i, 9],
|
754
|
+
], problems)
|
755
|
+
end
|
756
|
+
|
757
|
+
def test_missing_comma
|
758
|
+
root_elements = []
|
759
|
+
env, problems = instantiate(%Q(
|
760
|
+
TestNode nums: 1 text: "bla"
|
761
|
+
), TestMM, :root_elements => root_elements)
|
762
|
+
assert_equal 1, root_elements.size
|
763
|
+
assert_equal [1], root_elements[0].nums
|
764
|
+
assert_equal "bla", root_elements[0].text
|
765
|
+
assert_problems([
|
766
|
+
/unexpected label .*, expected ,/i,
|
767
|
+
], problems)
|
768
|
+
end
|
769
|
+
|
770
|
+
def test_missing_label
|
771
|
+
root_elements = []
|
772
|
+
env, problems = instantiate(%Q(
|
773
|
+
TestNode nums: 1 "bla"
|
774
|
+
), TestMM, :root_elements => root_elements)
|
775
|
+
assert_equal 1, root_elements.size
|
776
|
+
assert_equal [1], root_elements[0].nums
|
777
|
+
assert_problems([
|
778
|
+
/unexpected string 'bla', expected ,/i,
|
779
|
+
/unexpected unlabled argument/i
|
780
|
+
], problems)
|
781
|
+
end
|
782
|
+
|
783
|
+
def test_unclosed_bracket
|
784
|
+
root_elements = []
|
785
|
+
env, problems = instantiate(%Q(
|
786
|
+
TestNode nums: [1, "bla"
|
787
|
+
TestNode nums: [1, text: "bla"
|
788
|
+
TestNode nums: [1 text: "bla"
|
789
|
+
TestNode nums: [1 "bla"
|
790
|
+
TestNode [1, "bla"
|
791
|
+
TestNode [1, "bla" [
|
792
|
+
TestNode [1, "bla", [
|
793
|
+
), TestMM, :root_elements => root_elements)
|
794
|
+
assert_equal 7, root_elements.size
|
795
|
+
assert_equal [1], root_elements[0].nums
|
796
|
+
assert_nil root_elements[0].text
|
797
|
+
assert_equal [1], root_elements[1].nums
|
798
|
+
assert_equal "bla", root_elements[1].text
|
799
|
+
assert_equal [1], root_elements[2].nums
|
800
|
+
assert_equal "bla", root_elements[2].text
|
801
|
+
assert_equal [1], root_elements[3].nums
|
802
|
+
assert_nil root_elements[3].text
|
803
|
+
assert_equal [], root_elements[4].nums
|
804
|
+
assert_nil root_elements[4].text
|
805
|
+
assert_problems([
|
806
|
+
[/unexpected newline, expected \]/i, 2],
|
807
|
+
[/argument 'nums' can not take a string, expected integer/i, 2],
|
808
|
+
[/unexpected label 'text', expected identifier/i, 3],
|
809
|
+
[/unexpected label 'text', expected \]/i, 4],
|
810
|
+
[/unexpected string 'bla', expected ,/i, 5],
|
811
|
+
[/argument 'nums' can not take a string, expected integer/i, 5],
|
812
|
+
[/unexpected newline, expected \]/i, 5],
|
813
|
+
[/unexpected newline, expected \]/i, 6],
|
814
|
+
[/unexpected unlabled argument/i, 6],
|
815
|
+
[/unexpected \[, expected \]/i, 7],
|
816
|
+
[/unexpected newline, expected \]/i, 7],
|
817
|
+
[/unexpected unlabled argument/i, 7],
|
818
|
+
[/unexpected unlabled argument/i, 7],
|
819
|
+
[/unexpected \[, expected identifier/i, 8],
|
820
|
+
[/unexpected unlabled argument/i, 8],
|
821
|
+
[/unexpected unlabled argument/i, 8],
|
822
|
+
[/unexpected newline, expected \]/i, 8],
|
823
|
+
], problems)
|
824
|
+
end
|
825
|
+
|
826
|
+
def test_closing_bracket
|
827
|
+
root_elements = []
|
828
|
+
env, problems = instantiate(%Q(
|
829
|
+
TestNode ]
|
830
|
+
TestNode 1 ]
|
831
|
+
TestNode 1, ]
|
832
|
+
TestNode nums: ]1, "bla"
|
833
|
+
TestNode text: "bla" ]
|
834
|
+
), TestMM, :root_elements => root_elements)
|
835
|
+
assert_equal 5, root_elements.size
|
836
|
+
assert_equal [], root_elements[3].nums
|
837
|
+
assert_equal "bla", root_elements[4].text
|
838
|
+
assert_problems([
|
839
|
+
[/unexpected \], expected newline/i, 2],
|
840
|
+
[/unexpected \], expected newline/i, 3],
|
841
|
+
[/unexpected unlabled argument/i, 3],
|
842
|
+
[/unexpected \], expected identifier/i, 4],
|
843
|
+
[/unexpected unlabled argument/i, 4],
|
844
|
+
[/unexpected \], expected identifier/i, 5],
|
845
|
+
[/unexpected \], expected newline/i, 6],
|
846
|
+
], problems)
|
847
|
+
end
|
848
|
+
|
849
|
+
def test_closing_brace
|
850
|
+
root_elements = []
|
851
|
+
env, problems = instantiate(%Q(
|
852
|
+
TestNode }
|
853
|
+
TestNode 1 }
|
854
|
+
TestNode 1, }
|
855
|
+
TestNode nums: }1, "bla"
|
856
|
+
TestNode text: "bla" }
|
857
|
+
), TestMM, :root_elements => root_elements)
|
858
|
+
assert_equal 5, root_elements.size
|
859
|
+
assert_equal [], root_elements[3].nums
|
860
|
+
assert_equal "bla", root_elements[4].text
|
861
|
+
assert_problems([
|
862
|
+
[/unexpected \}, expected newline/i, 2],
|
863
|
+
[/unexpected \}, expected newline/i, 3],
|
864
|
+
[/unexpected unlabled argument/i, 3],
|
865
|
+
[/unexpected \}, expected identifier/i, 4],
|
866
|
+
[/unexpected unlabled argument/i, 4],
|
867
|
+
[/unexpected \}, expected identifier/i, 5],
|
868
|
+
[/unexpected \}, expected newline/i, 6],
|
869
|
+
], problems)
|
870
|
+
end
|
871
|
+
|
872
|
+
def test_starting_non_command
|
873
|
+
root_elements = []
|
874
|
+
env, problems = instantiate(%Q(
|
875
|
+
\)
|
876
|
+
TestNode
|
877
|
+
*
|
878
|
+
TestNode
|
879
|
+
$
|
880
|
+
TestNode
|
881
|
+
,
|
882
|
+
TestNode
|
883
|
+
[
|
884
|
+
TestNode
|
885
|
+
{
|
886
|
+
TestNode
|
887
|
+
]
|
888
|
+
TestNode
|
889
|
+
}
|
890
|
+
TestNode
|
891
|
+
}}
|
892
|
+
), TestMM, :root_elements => root_elements)
|
893
|
+
assert_equal 8, root_elements.size
|
894
|
+
assert_problems([
|
895
|
+
[/parse error on token '\)'/i, 2],
|
896
|
+
[/parse error on token '\*'/i, 4],
|
897
|
+
[/parse error on token '\$'/i, 6],
|
898
|
+
[/unexpected ,, expected identifier/i, 8],
|
899
|
+
[/unexpected \[, expected identifier/i, 10],
|
900
|
+
[/unexpected \{, expected identifier/i, 12],
|
901
|
+
[/unexpected \], expected identifier/i, 14],
|
902
|
+
[/unexpected \}, expected identifier/i, 16],
|
903
|
+
[/unexpected \}, expected identifier/i, 18],
|
904
|
+
], problems)
|
905
|
+
end
|
906
|
+
|
907
|
+
def test_parse_error_in_argument_list
|
908
|
+
root_elements = []
|
909
|
+
env, problems = instantiate(%Q(
|
910
|
+
TestNode text: "bla", * nums: 1
|
911
|
+
TestNode text: "bla" * , nums: 1
|
912
|
+
TestNode ?text: "bla"
|
913
|
+
TestNode nums: [1, * 3]
|
914
|
+
), TestMM, :root_elements => root_elements)
|
915
|
+
assert_equal 4, root_elements.size
|
916
|
+
assert_equal "bla", root_elements[0].text
|
917
|
+
assert_equal [1], root_elements[0].nums
|
918
|
+
assert_equal "bla", root_elements[1].text
|
919
|
+
assert_equal [1], root_elements[1].nums
|
920
|
+
assert_equal [1, 3], root_elements[3].nums
|
921
|
+
assert_problems([
|
922
|
+
[/parse error on token '\*'/i, 2],
|
923
|
+
[/parse error on token '\*'/i, 3],
|
924
|
+
[/parse error on token '\?text:'/i, 4],
|
925
|
+
[/unexpected unlabled argument/i, 4],
|
926
|
+
[/parse error on token '\*'/i, 5],
|
927
|
+
], problems)
|
928
|
+
end
|
929
|
+
|
930
|
+
def test_unclosed_brace
|
931
|
+
root_elements = []
|
932
|
+
env, problems = instantiate(%Q(
|
933
|
+
TestNode {
|
934
|
+
), TestMM, :root_elements => root_elements)
|
935
|
+
assert_equal 1, root_elements.size
|
936
|
+
assert_problems([
|
937
|
+
[/unexpected end of file, expected \}/i, 2]
|
938
|
+
], problems)
|
939
|
+
end
|
940
|
+
|
941
|
+
def test_unclosed_brace2
|
942
|
+
root_elements = []
|
943
|
+
env, problems = instantiate(%Q(
|
944
|
+
TestNode {
|
945
|
+
*
|
946
|
+
), TestMM, :root_elements => root_elements)
|
947
|
+
assert_equal 1, root_elements.size
|
948
|
+
assert_problems([
|
949
|
+
[/parse error on token '\*'/i, 3]
|
950
|
+
], problems)
|
951
|
+
end
|
952
|
+
|
953
|
+
def test_unclosed_brace3
|
954
|
+
root_elements = []
|
955
|
+
env, problems = instantiate(%Q(
|
956
|
+
TestNode {
|
957
|
+
childs:
|
958
|
+
), TestMM, :root_elements => root_elements)
|
959
|
+
assert_equal 1, root_elements.size
|
960
|
+
assert_problems([
|
961
|
+
[/unexpected end of file, expected identifier/i, 3]
|
962
|
+
], problems)
|
963
|
+
end
|
964
|
+
|
965
|
+
def test_label_without_child
|
966
|
+
root_elements = []
|
967
|
+
env, problems = instantiate(%Q(
|
968
|
+
TestNode {
|
969
|
+
childs:
|
970
|
+
}
|
971
|
+
), TestMM, :root_elements => root_elements)
|
972
|
+
assert_equal 1, root_elements.size
|
973
|
+
assert_problems([
|
974
|
+
[/unexpected \}, expected identifier/i, 4]
|
975
|
+
], problems)
|
976
|
+
end
|
977
|
+
|
978
|
+
def test_unclosed_bracket
|
979
|
+
root_elements = []
|
980
|
+
env, problems = instantiate(%Q(
|
981
|
+
TestNode {
|
982
|
+
childs: [
|
983
|
+
), TestMM, :root_elements => root_elements)
|
984
|
+
assert_equal 1, root_elements.size
|
985
|
+
assert_problems([
|
986
|
+
[/unexpected end of file, expected \]/i, 3]
|
987
|
+
], problems)
|
988
|
+
end
|
989
|
+
|
990
|
+
def test_child_label_problems
|
991
|
+
root_elements = []
|
992
|
+
env, problems = instantiate(%Q(
|
993
|
+
TestNode {
|
994
|
+
childs: x
|
995
|
+
SubNode
|
996
|
+
childs: *
|
997
|
+
SubNode
|
998
|
+
childs: &
|
999
|
+
}
|
1000
|
+
), TestMM, :root_elements => root_elements)
|
1001
|
+
assert_equal 1, root_elements.size
|
1002
|
+
assert_equal 2, root_elements[0].childs.size
|
1003
|
+
assert_problems([
|
1004
|
+
[/unexpected identifier 'x', expected newline/i, 3],
|
1005
|
+
[/parse error on token '\*'/i, 5],
|
1006
|
+
[/parse error on token '&'/i, 7]
|
1007
|
+
], problems)
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def test_child_label_problems_with_bracket
|
1011
|
+
root_elements = []
|
1012
|
+
env, problems = instantiate(%Q(
|
1013
|
+
TestNode {
|
1014
|
+
childs: [ x
|
1015
|
+
SubNode
|
1016
|
+
]
|
1017
|
+
childs: [ *
|
1018
|
+
SubNode
|
1019
|
+
]
|
1020
|
+
childs: [&
|
1021
|
+
]
|
1022
|
+
}
|
1023
|
+
), TestMM, :root_elements => root_elements)
|
1024
|
+
assert_equal 1, root_elements.size
|
1025
|
+
assert_equal 2, root_elements[0].childs.size
|
1026
|
+
assert_problems([
|
1027
|
+
[/unexpected identifier 'x', expected newline/i, 3],
|
1028
|
+
[/parse error on token '\*'/i, 6],
|
1029
|
+
[/parse error on token '&'/i, 9]
|
1030
|
+
], problems)
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
def test_missing_closing_bracket
|
1034
|
+
root_elements = []
|
1035
|
+
env, problems = instantiate(%Q(
|
1036
|
+
TestNode {
|
1037
|
+
childs: [
|
1038
|
+
SubNode
|
1039
|
+
childs: [
|
1040
|
+
SubNode
|
1041
|
+
SubNode
|
1042
|
+
}
|
1043
|
+
), TestMM, :root_elements => root_elements)
|
1044
|
+
assert_equal 1, root_elements.size
|
1045
|
+
assert_equal 3, root_elements[0].childs.size
|
1046
|
+
assert_problems([
|
1047
|
+
[/unexpected label 'childs', expected identifier/i, 5],
|
1048
|
+
[/unexpected \}, expected identifier/i, 8],
|
1049
|
+
], problems)
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
def test_missing_closing_brace
|
1053
|
+
root_elements = []
|
1054
|
+
env, problems = instantiate(%Q(
|
1055
|
+
TestNode {
|
1056
|
+
TestNode {
|
1057
|
+
TestNode
|
1058
|
+
}
|
1059
|
+
), TestMM, :root_elements => root_elements)
|
1060
|
+
assert_equal 1, root_elements.size
|
1061
|
+
assert_equal 1, root_elements[0].childs.size
|
1062
|
+
assert_equal 1, root_elements[0].childs[0].childs.size
|
1063
|
+
assert_problems([
|
1064
|
+
[/unexpected end of file, expected \}/i, 5],
|
1065
|
+
], problems)
|
1066
|
+
end
|
1067
|
+
|
710
1068
|
#
|
711
1069
|
# command name provider
|
712
1070
|
#
|
@@ -940,10 +1298,11 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
940
1298
|
TestNode enum: A
|
941
1299
|
TestNode enum: B
|
942
1300
|
TestNode enum: "non-word*chars"
|
1301
|
+
TestNode enum: "2you"
|
943
1302
|
}
|
944
1303
|
), TestMM)
|
945
1304
|
assert_no_problems(problems)
|
946
|
-
assert_equal [:A, :B, :'non-word*chars'], env.find(:text => "root").first.childs.collect{|c| c.enum}
|
1305
|
+
assert_equal [:A, :B, :'non-word*chars', :'2you'], env.find(:text => "root").first.childs.collect{|c| c.enum}
|
947
1306
|
end
|
948
1307
|
|
949
1308
|
#
|
@@ -975,7 +1334,7 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
975
1334
|
c.name == "TestNode" || c.name == "Data" || c.name == "TestNodeSub" || c.name == "SubNode"}))
|
976
1335
|
inst = RText::Instantiator.new(lang)
|
977
1336
|
problems = []
|
978
|
-
inst.instantiate(text, options.merge({:env => env, :problems => problems}))
|
1337
|
+
inst.instantiate(text, options.merge({:env => env, :problems => problems, :root_elements => options[:root_elements]}))
|
979
1338
|
return env, problems
|
980
1339
|
end
|
981
1340
|
|
@@ -987,9 +1346,15 @@ class InstantiatorTest < Test::Unit::TestCase
|
|
987
1346
|
remaining = problems.dup
|
988
1347
|
probs = []
|
989
1348
|
expected.each do |e|
|
990
|
-
|
1349
|
+
if e.is_a?(Array)
|
1350
|
+
p = remaining.find{|p| p.message =~ e[0] && p.line == e[1]}
|
1351
|
+
else
|
1352
|
+
p = remaining.find{|p| p.message =~ e}
|
1353
|
+
end
|
991
1354
|
probs << "expected problem not present: #{e}" if !p
|
992
|
-
|
1355
|
+
# make sure to not delete duplicate problems at once
|
1356
|
+
idx = remaining.index(p)
|
1357
|
+
remaining.delete_at(idx) if idx
|
993
1358
|
end
|
994
1359
|
remaining.each do |p|
|
995
1360
|
probs << "unexpected problem: #{p.message}, line: #{p.line}"
|
data/test/serializer_test.rb
CHANGED
@@ -15,7 +15,7 @@ class SerializerTest < Test::Unit::TestCase
|
|
15
15
|
module TestMM
|
16
16
|
extend RGen::MetamodelBuilder::ModuleExtension
|
17
17
|
SomeEnum = RGen::MetamodelBuilder::DataTypes::Enum.new(
|
18
|
-
:name => "SomeEnum", :literals => [:A, :B, :'non-word*chars'])
|
18
|
+
:name => "SomeEnum", :literals => [:A, :B, :'non-word*chars', :'2you'])
|
19
19
|
class TestNode < RGen::MetamodelBuilder::MMBase
|
20
20
|
has_attr 'text', String
|
21
21
|
has_attr 'integer', Integer
|
@@ -355,7 +355,8 @@ TestNode {
|
|
355
355
|
testModel = [
|
356
356
|
TestMM::TestNode.new(:enum => :A),
|
357
357
|
TestMM::TestNode.new(:enum => :B),
|
358
|
-
TestMM::TestNode.new(:enum => :'non-word*chars')
|
358
|
+
TestMM::TestNode.new(:enum => :'non-word*chars'),
|
359
|
+
TestMM::TestNode.new(:enum => :'2you')
|
359
360
|
]
|
360
361
|
output = StringWriter.new
|
361
362
|
serialize(testModel, TestMM, output)
|
@@ -363,6 +364,7 @@ TestNode {
|
|
363
364
|
TestNode enum: A
|
364
365
|
TestNode enum: B
|
365
366
|
TestNode enum: "non-word*chars"
|
367
|
+
TestNode enum: "2you"
|
366
368
|
), output
|
367
369
|
end
|
368
370
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rtext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09
|
12
|
+
date: 2012-10-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rgen
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,12 @@ dependencies:
|
|
21
21
|
version: 0.6.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.6.0
|
25
30
|
description: RText can be used to derive textual languages from an RGen metamodel
|
26
31
|
with very little effort.
|
27
32
|
email: martin dot thiede at gmx de
|
@@ -79,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
84
|
version: '0'
|
80
85
|
requirements: []
|
81
86
|
rubyforge_project:
|
82
|
-
rubygems_version: 1.
|
87
|
+
rubygems_version: 1.8.23
|
83
88
|
signing_key:
|
84
89
|
specification_version: 3
|
85
90
|
summary: Ruby Textual Modelling
|