rtext 0.5.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rtext/parser.rb CHANGED
@@ -1,28 +1,21 @@
1
1
  require 'rtext/generic'
2
- require 'rtext/tokenizer'
3
2
 
4
3
  module RText
5
4
 
6
5
  class Parser
7
- include RText::Tokenizer
8
-
9
6
  Problem = Struct.new(:message, :line)
10
7
 
11
- def initialize(reference_regexp)
12
- @reference_regexp = reference_regexp
13
- end
14
-
15
- def parse(str, options)
8
+ def parse(tokens, options)
16
9
  @dsc_visitor = options[:descent_visitor]
17
10
  @asc_visitor = options[:ascent_visitor]
18
11
  @problems = options[:problems] || []
19
12
  @non_consume_count = 0
20
13
  @consume_problem_reported = false
21
- @tokens = tokenize(str, @reference_regexp, :on_command_token => options[:on_command_token])
14
+ @tokens = tokens
22
15
  @last_line = @tokens.last && @tokens.last.line
23
16
  #@debug = true
24
17
  begin
25
- while next_token
18
+ while next_token_kind
26
19
  statement = parse_statement(true, true)
27
20
  end
28
21
  rescue InternalError
@@ -34,7 +27,7 @@ class Parser
34
27
  comments = []
35
28
  comment = parse_comment
36
29
  annotation = parse_annotation
37
- if (next_token && next_token == :identifier) || !allow_unassociated_comment
30
+ if (next_token_kind && next_token_kind == :identifier) || !allow_unassociated_comment
38
31
  comments << [ comment, :above] if comment
39
32
  command = consume(:identifier)
40
33
  if command
@@ -42,12 +35,10 @@ class Parser
42
35
  arg_list = []
43
36
  parse_argument_list(arg_list)
44
37
  element_list = []
45
- if next_token == "{"
38
+ if next_token_kind == "{"
46
39
  parse_statement_block(element_list, comments)
47
40
  end
48
- eol_comment = parse_eol_comment
49
- comments << [ eol_comment, :eol ] if eol_comment
50
- consume(:newline)
41
+ parse_eol_comment(comments)
51
42
  @asc_visitor.call(command, arg_list, element_list, comments, annotation, is_root)
52
43
  else
53
44
  discard_until(:newline)
@@ -68,7 +59,7 @@ class Parser
68
59
 
69
60
  def parse_comment
70
61
  result = nil
71
- while next_token == :comment
62
+ while next_token_kind == :comment
72
63
  result ||= []
73
64
  result << consume(:comment)
74
65
  consume(:newline)
@@ -78,7 +69,7 @@ class Parser
78
69
 
79
70
  def parse_annotation
80
71
  result = nil
81
- while next_token == :annotation
72
+ while next_token_kind == :annotation
82
73
  result ||= []
83
74
  result << consume(:annotation)
84
75
  consume(:newline)
@@ -86,27 +77,26 @@ class Parser
86
77
  result
87
78
  end
88
79
 
89
- def parse_eol_comment
90
- if next_token == :comment
91
- consume(:comment)
92
- else
93
- nil
80
+ def parse_eol_comment(comments)
81
+ if next_token_kind == :comment
82
+ comment = consume(:comment)
83
+ comments << [comment, :eol]
94
84
  end
85
+ nl = consume(:newline)
86
+ discard_until(:newline) unless nl
95
87
  end
96
88
 
97
89
  def parse_statement_block(element_list, comments)
98
90
  consume("{")
99
- eol_comment = parse_eol_comment
100
- comments << [ eol_comment, :eol ] if eol_comment
101
- consume(:newline)
102
- while next_token && next_token != "}"
91
+ parse_eol_comment(comments)
92
+ while next_token_kind && next_token_kind != "}"
103
93
  parse_block_element(element_list, comments)
104
94
  end
105
95
  consume("}")
106
96
  end
107
97
 
108
98
  def parse_block_element(element_list, comments)
109
- if next_token == :label
99
+ if next_token_kind == :label
110
100
  label = consume(:label)
111
101
  element_list << [label, parse_labeled_block_element(comments)]
112
102
  else
@@ -116,38 +106,30 @@ class Parser
116
106
  end
117
107
 
118
108
  def parse_labeled_block_element(comments)
119
- if next_token == "["
109
+ if next_token_kind == "["
120
110
  parse_element_list(comments)
121
111
  else
122
- eol_comment = parse_eol_comment
123
- comments << [ eol_comment, :eol ] if eol_comment
124
- nl = consume(:newline)
125
- discard_until(:newline) unless nl
112
+ parse_eol_comment(comments)
126
113
  parse_statement
127
114
  end
128
115
  end
129
116
 
130
117
  def parse_element_list(comments)
131
118
  consume("[")
132
- eol_comment = parse_eol_comment
133
- comments << [ eol_comment, :eol ] if eol_comment
134
- nl = consume(:newline)
135
- discard_until(:newline) unless nl
119
+ parse_eol_comment(comments)
136
120
  result = []
137
- while next_token && next_token != "]"
121
+ while next_token_kind && next_token_kind != "]"
138
122
  statement = parse_statement(false, true)
139
123
  result << statement if statement
140
124
  end
141
125
  consume("]")
142
- eol_comment = parse_eol_comment
143
- comments << [ eol_comment, :eol ] if eol_comment
144
- consume(:newline)
126
+ parse_eol_comment(comments)
145
127
  result
146
128
  end
147
129
 
148
130
  def parse_argument_list(arg_list)
149
131
  first = true
150
- while (AnyValue + [",", "[", :label, :error]).include?(next_token)
132
+ while (AnyValue + [",", "[", :label, :error]).include?(next_token_kind)
151
133
  consume(",") unless first
152
134
  first = false
153
135
  parse_argument(arg_list)
@@ -155,7 +137,7 @@ class Parser
155
137
  end
156
138
 
157
139
  def parse_argument(arg_list)
158
- if next_token == :label
140
+ if next_token_kind == :label
159
141
  label = consume(:label)
160
142
  arg_list << [label, parse_argument_value]
161
143
  else
@@ -164,7 +146,7 @@ class Parser
164
146
  end
165
147
 
166
148
  def parse_argument_value
167
- if next_token == "["
149
+ if next_token_kind == "["
168
150
  parse_argument_value_list
169
151
  else
170
152
  parse_value
@@ -175,7 +157,7 @@ class Parser
175
157
  consume("[")
176
158
  first = true
177
159
  result = []
178
- while (AnyValue + [",", :error]).include?(next_token)
160
+ while (AnyValue + [",", :error]).include?(next_token_kind)
179
161
  consume(",") unless first
180
162
  first = false
181
163
  result << parse_value
@@ -190,7 +172,7 @@ class Parser
190
172
  consume(*AnyValue)
191
173
  end
192
174
 
193
- def next_token
175
+ def next_token_kind
194
176
  @tokens.first && @tokens.first.kind
195
177
  end
196
178
 
@@ -1,3 +1,4 @@
1
+ require 'bigdecimal'
1
2
  require 'rtext/language'
2
3
  require 'rtext/generic'
3
4
 
@@ -47,6 +48,7 @@ class Serializer
47
48
  end
48
49
 
49
50
  def serialize_element(element)
51
+ # TODO: remove setting of fragment ref (doesn't belong into serializer)
50
52
  set_fragment_ref(element)
51
53
  set_line_number(element, @line_number) if @set_line_number
52
54
  clazz = element.class.ecore
@@ -89,7 +91,8 @@ class Serializer
89
91
  childs = contained_elements[f]
90
92
  if childs.size > 0
91
93
  child_classes = childs.collect{|c| c.class.ecore}.uniq
92
- if child_classes.any?{|c| @lang.containments_by_target_type(element.class.ecore, c).size > 1}
94
+ if @lang.labeled_containment?(clazz, f) ||
95
+ child_classes.any?{|c| @lang.containments_by_target_type(element.class.ecore, c).size > 1}
93
96
  if childs.size > 1
94
97
  write("#{f.name}: [")
95
98
  iinc
@@ -142,13 +145,18 @@ class Serializer
142
145
  elsif feature.eType.instanceClass == RGen::MetamodelBuilder::DataTypes::Boolean
143
146
  result << v.to_s
144
147
  elsif feature.eType.instanceClass == Float
145
- if arg_format
146
- result << sprintf(arg_format, v)
148
+ if v.is_a?(BigDecimal)
149
+ result << v.to_s("F")
150
+ # formatting not available for BigDecimals
147
151
  else
148
- result << v.to_s
152
+ if arg_format
153
+ result << sprintf(arg_format, v)
154
+ else
155
+ result << v.to_s
156
+ end
149
157
  end
150
158
  elsif feature.eType.is_a?(RGen::ECore::EEnum)
151
- if v.to_s =~ /^\d|\W/
159
+ if v.to_s =~ /^\d|\W/ || v.to_s == "true" || v.to_s == "false"
152
160
  result << "\"#{v.to_s.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
153
161
  gsub("\r","\\r").gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")}\""
154
162
  else
data/lib/rtext/service.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  require 'socket'
2
- require 'rtext/completer'
3
2
  require 'rtext/context_builder'
4
3
  require 'rtext/message_helper'
5
4
  require 'rtext/link_detector'
6
5
 
6
+ # optimization: garbage collect while service is idle
7
+
7
8
  module RText
8
9
 
9
10
  class Service
@@ -93,6 +94,7 @@ class Service
93
94
 
94
95
  def message_received(sock, obj)
95
96
  if check_request(obj)
97
+ request_start = Time.now
96
98
  @logger.debug("request: "+obj.inspect) if @logger
97
99
  response = { "type" => "response", "invocation_id" => obj["invocation_id"] }
98
100
  case obj["command"]
@@ -112,8 +114,10 @@ class Service
112
114
  response["type"] = "unknown_command_error"
113
115
  response["command"] = obj["command"]
114
116
  end
115
- @logger.debug("response: "+response.inspect) if response && @logger
117
+ @logger.debug("response: "+truncate_response_for_debug_output(response).inspect) \
118
+ if response && @logger
116
119
  send_response(sock, response)
120
+ @logger.info("request complete (#{Time.now-request_start}s)")
117
121
  end
118
122
  end
119
123
 
@@ -154,6 +158,9 @@ class Service
154
158
  response["total_problems"] = total
155
159
  end
156
160
 
161
+ InsertString = "insert"
162
+ DisplayString = "display"
163
+
157
164
  def content_complete(sock, request, response)
158
165
  # column numbers start at 1
159
166
  linepos = request["column"]-1
@@ -164,13 +171,11 @@ class Service
164
171
  context = ContextBuilder.build_context(lang, lines, linepos)
165
172
  @logger.debug("context element: #{lang.identifier_provider.call(context.element, nil, nil, nil)}") \
166
173
  if context && context.element && @logger
167
- completer = RText::Completer.new(lang)
168
- options = completer.complete(context, lambda {|ref|
169
- @service_provider.get_reference_completion_options(ref, context).collect {|o|
170
- Completer::CompletionOption.new(o.identifier, "<#{o.type}>")}
171
- })
174
+ options = @service_provider.get_completion_options(context)
175
+ insert_str = "insert"
176
+ display_str = "display"
172
177
  response["options"] = options.collect do |o|
173
- { "insert" => o.text, "display" => "#{o.text} #{o.extra}" }
178
+ { insert_str => o.text, display_str => "#{o.text} #{o.extra}" }
174
179
  end
175
180
  end
176
181
 
@@ -186,16 +191,8 @@ class Service
186
191
  response["begin_column"] = link_descriptor.scol
187
192
  response["end_column"] = link_descriptor.ecol
188
193
  targets = []
189
- if link_descriptor.backward
190
- @service_provider.get_referencing_elements(
191
- link_descriptor.value, link_descriptor.element, link_descriptor.feature, link_descriptor.index).each do |t|
192
- targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
193
- end
194
- else
195
- @service_provider.get_reference_targets(
196
- link_descriptor.value, link_descriptor.element, link_descriptor.feature, link_descriptor.index).each do |t|
197
- targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
198
- end
194
+ @service_provider.get_link_targets(link_descriptor).each do |t|
195
+ targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
199
196
  end
200
197
  response["targets"] = targets
201
198
  end
@@ -225,6 +222,18 @@ class Service
225
222
  end
226
223
  end
227
224
 
225
+ def truncate_response_for_debug_output(response_hash)
226
+ result = {}
227
+ response_hash.each_pair do |k,v|
228
+ if v.is_a?(Array) && v.size > 100
229
+ result[k] = v[0..99] + ["<truncated>"]
230
+ else
231
+ result[k] = v
232
+ end
233
+ end
234
+ result
235
+ end
236
+
228
237
  def create_server
229
238
  port = PortRangeStart
230
239
  serv = nil
@@ -1,4 +1,5 @@
1
1
  require 'rtext/generic'
2
+ require 'bigdecimal'
2
3
 
3
4
  module RText
4
5
 
@@ -11,6 +12,13 @@ module Tokenizer
11
12
  on_command_token_proc = options[:on_command_token]
12
13
  str.split(/\r?\n/).each_with_index do |str, idx|
13
14
  idx += 1
15
+ if idx == 1
16
+ # remove UTF-8 BOM if present
17
+ enc = str.encoding
18
+ str.force_encoding("binary")
19
+ str = str[3..-1] if str.index("\xEF\xBB\xBF".force_encoding("binary")) == 0
20
+ str.force_encoding(enc)
21
+ end
14
22
  if str =~ /^\s*([\#@])(.*)/
15
23
  if $1 == "#"
16
24
  result << Token.new(:comment, $2, idx, str.size-$2.size, str.size)
@@ -29,7 +37,15 @@ module Tokenizer
29
37
  col += $&.size
30
38
  when /\A[-+]?\d+\.\d+(?:e[+-]\d+)?\b/
31
39
  str = $'
32
- result << Token.new(:float, $&.to_f, idx, col, col+$&.size-1)
40
+ val = $&
41
+ # if string size isn't more than 16, Float precision will be sufficient
42
+ # otherwise use BigDecimal even though the value might still fit in a Float
43
+ if val.size <= 16
44
+ val = val.to_f
45
+ else
46
+ val = BigDecimal.new(val)
47
+ end
48
+ result << Token.new(:float, val, idx, col, col+$&.size-1)
33
49
  col += $&.size
34
50
  when /\A0[xX][0-9a-fA-F]+\b/
35
51
  str = $'
@@ -82,7 +98,7 @@ module Tokenizer
82
98
  str = $'
83
99
  result << Token.new(:generic, RText::Generic.new($1), idx, col, col+$&.size-1)
84
100
  col += $&.size
85
- when /\A\S+/
101
+ when /\A\S/
86
102
  str = $'
87
103
  result << Token.new(:error, $&, idx, col, col+$&.size-1)
88
104
  col += $&.size
@@ -4,7 +4,7 @@ require 'test/unit'
4
4
  require 'rgen/metamodel_builder'
5
5
  require 'rtext/language'
6
6
  require 'rtext/context_builder'
7
- require 'rtext/completer'
7
+ require 'rtext/default_completer'
8
8
 
9
9
  class CompleterTest < Test::Unit::TestCase
10
10
 
@@ -14,6 +14,8 @@ module TestMM
14
14
  has_attr 'text', String
15
15
  has_attr 'unlabled', String
16
16
  end
17
+ class TestNode3 < RGen::MetamodelBuilder::MMBase
18
+ end
17
19
  class TestNode < RGen::MetamodelBuilder::MMBase
18
20
  has_attr 'text', String
19
21
  has_attr 'unlabled1', String
@@ -24,6 +26,7 @@ module TestMM
24
26
  contains_many 'childs', TestNode, 'parent'
25
27
  contains_one 'child2RoleA', TestNode2, 'parentA'
26
28
  contains_many 'child2RoleB', TestNode2, 'parentB'
29
+ contains_one_uni 'testNode3', TestNode3
27
30
  end
28
31
  SomeEnum = RGen::MetamodelBuilder::DataTypes::Enum.new(
29
32
  :name => "SomeEnum", :literals => [:A, :B, :'non-word*chars'])
@@ -271,7 +274,7 @@ TestNode3 enum: |
271
274
  assert_options([
272
275
  ["A", "<SomeEnum>"],
273
276
  ["B", "<SomeEnum>"],
274
- ["non-word*chars", "<SomeEnum>"]
277
+ ["\"non-word*chars\"", "<SomeEnum>"]
275
278
  ], options)
276
279
  end
277
280
 
@@ -282,7 +285,7 @@ TestNode3 enum: A|
282
285
  assert_options([
283
286
  ["A", "<SomeEnum>"],
284
287
  ["B", "<SomeEnum>"],
285
- ["non-word*chars", "<SomeEnum>"]
288
+ ["\"non-word*chars\"", "<SomeEnum>"]
286
289
  ], options)
287
290
  end
288
291
 
@@ -308,8 +311,8 @@ def test_reference_value
308
311
  options = complete(TestMM, %Q(\
309
312
  TestNode related: |\
310
313
  ), lambda { |r| [
311
- RText::Completer::CompletionOption.new("A", "a"),
312
- RText::Completer::CompletionOption.new("B", "b") ] })
314
+ RText::DefaultCompleter::CompletionOption.new("A", "a"),
315
+ RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
313
316
  assert_options([
314
317
  ["A", "a"],
315
318
  ["B", "b"],
@@ -320,8 +323,8 @@ def test_reference_value_part
320
323
  options = complete(TestMM, %Q(\
321
324
  TestNode related: /My/|\
322
325
  ), lambda { |r| [
323
- RText::Completer::CompletionOption.new("/My/Target", "a"),
324
- RText::Completer::CompletionOption.new("/MyOther/Target", "b") ] })
326
+ RText::DefaultCompleter::CompletionOption.new("/My/Target", "a"),
327
+ RText::DefaultCompleter::CompletionOption.new("/MyOther/Target", "b") ] })
325
328
  assert_options([
326
329
  ["/My/Target", "a"],
327
330
  ["/MyOther/Target", "b"]
@@ -340,8 +343,8 @@ def test_reference_value_in_array
340
343
  options = complete(TestMM, %Q(\
341
344
  TestNode others: |
342
345
  ), lambda { |r| [
343
- RText::Completer::CompletionOption.new("A", "a"),
344
- RText::Completer::CompletionOption.new("B", "b") ] })
346
+ RText::DefaultCompleter::CompletionOption.new("A", "a"),
347
+ RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
345
348
  assert_options([
346
349
  ["A", "a"],
347
350
  ["B", "b"],
@@ -352,8 +355,8 @@ def test_reference_value_in_array_after_bracket
352
355
  options = complete(TestMM, %Q(\
353
356
  TestNode others: [|
354
357
  ), lambda { |r| [
355
- RText::Completer::CompletionOption.new("A", "a"),
356
- RText::Completer::CompletionOption.new("B", "b") ] })
358
+ RText::DefaultCompleter::CompletionOption.new("A", "a"),
359
+ RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
357
360
  assert_options([
358
361
  ["A", "a"],
359
362
  ["B", "b"],
@@ -364,8 +367,8 @@ def test_reference_value_in_array_second_value
364
367
  options = complete(TestMM, %Q(\
365
368
  TestNode others: [xxx, |
366
369
  ), lambda { |r| [
367
- RText::Completer::CompletionOption.new("A", "a"),
368
- RText::Completer::CompletionOption.new("B", "b") ] })
370
+ RText::DefaultCompleter::CompletionOption.new("A", "a"),
371
+ RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
369
372
  assert_options([
370
373
  ["A", "a"],
371
374
  ["B", "b"],
@@ -377,8 +380,8 @@ def test_reference_value_nested
377
380
  TestNode {
378
381
  TestNode SimpleState, others: [|/StatemachineMM/State]
379
382
  ), lambda { |r| [
380
- RText::Completer::CompletionOption.new("A", "a"),
381
- RText::Completer::CompletionOption.new("B", "b") ] })
383
+ RText::DefaultCompleter::CompletionOption.new("A", "a"),
384
+ RText::DefaultCompleter::CompletionOption.new("B", "b") ] })
382
385
  assert_options([
383
386
  ["A", "a"],
384
387
  ["B", "b"],
@@ -401,7 +404,8 @@ TestNode {
401
404
  assert_options([
402
405
  ["TestNode", "<unlabled1>, <unlabled2>"],
403
406
  ["child2RoleA:", "<TestNode2>"],
404
- ["child2RoleB:", "<TestNode2>"]
407
+ ["child2RoleB:", "<TestNode2>"],
408
+ ["testNode3:", "<TestNode3>"]
405
409
  ], options)
406
410
  end
407
411
 
@@ -580,9 +584,22 @@ def complete(mm, text, ref_comp_option_provider=nil)
580
584
  lang = RText::Language.new(mm.ecore,
581
585
  :root_classes => mm.ecore.eAllClasses,
582
586
  :unlabled_arguments => lambda {|c| ["unlabled1", "unlabled2", "unlabled"]},
583
- :unquoted_arguments => lambda {|c| c.name == "TestNode2" ? ["text", "unlabled"] : []})
587
+ :unquoted_arguments => lambda {|c| c.name == "TestNode2" ? ["text", "unlabled"] : []},
588
+ :labeled_containments => lambda {|c| ["testNode3"]})
584
589
  context = RText::ContextBuilder.build_context(lang, context_lines, pos_in_line)
585
- RText::Completer.new(lang).complete(context, ref_comp_option_provider)
590
+ completer = RText::DefaultCompleter.new(lang)
591
+ class << completer
592
+ attr_accessor :ref_comp_option_provider
593
+ def reference_options(context)
594
+ if ref_comp_option_provider
595
+ ref_comp_option_provider.call(context.feature)
596
+ else
597
+ super
598
+ end
599
+ end
600
+ end
601
+ completer.ref_comp_option_provider = ref_comp_option_provider
602
+ completer.complete(context)
586
603
  end
587
604
 
588
605
  end