rtext 0.5.1 → 0.7.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/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