modl 0.3.6 → 0.3.7

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.
@@ -66,5 +66,12 @@
66
66
  "*V=test",
67
67
  "*VERSION=0",
68
68
  "*VERSION=0.1",
69
- "a=b;*VERSION=1"
69
+ "a=b;*VERSION=1",
70
+ "*class(*id=a;*name=alpha;*superclass=str);a=null",
71
+ "*class(*id=a;*name=alpha;*superclass=num);a=null",
72
+ "*class(*id=a;*name=alpha;*superclass=num);a=x",
73
+ "*class(*id=a;*name=alpha;*superclass=num);a=null",
74
+ "*class(*id=a;*name=alpha;*superclass=map);a=[1;2;3]",
75
+ "*class(*id=a;*name=alpha;*superclass=arr);a=(c=d)",
76
+ "*L=grammar_tests/test_import_dir/nested_import1.txt;files=%*load"
70
77
  ]
@@ -307,7 +307,7 @@ class MODLParser < Antlr4::Runtime::Parser
307
307
  @_state_number = 69
308
308
  modl_pair()
309
309
  else
310
- raise Antlr4::Runtime::NoViableAltException self
310
+ raise Antlr4::Runtime::NoViableAltException, self
311
311
  end
312
312
  rescue Antlr4::Runtime::RecognitionException => re
313
313
  _localctx.exception = re
@@ -621,7 +621,7 @@ class MODLParser < Antlr4::Runtime::Parser
621
621
  match(COLON)
622
622
 
623
623
  else
624
- raise Antlr4::Runtime::NoViableAltException self
624
+ raise Antlr4::Runtime::NoViableAltException, self
625
625
  end
626
626
  @_state_number = 118
627
627
  @_err_handler.sync(self)
@@ -630,7 +630,7 @@ class MODLParser < Antlr4::Runtime::Parser
630
630
  end
631
631
 
632
632
  else
633
- raise Antlr4::Runtime::NoViableAltException self
633
+ raise Antlr4::Runtime::NoViableAltException, self
634
634
  end
635
635
  @_state_number = 122
636
636
  @_err_handler.sync(self)
@@ -1240,7 +1240,7 @@ class MODLParser < Antlr4::Runtime::Parser
1240
1240
  @_state_number = 192
1241
1241
  modl_map_conditional()
1242
1242
  else
1243
- raise Antlr4::Runtime::NoViableAltException self
1243
+ raise Antlr4::Runtime::NoViableAltException, self
1244
1244
  end
1245
1245
  rescue Antlr4::Runtime::RecognitionException => re
1246
1246
  _localctx.exception = re
@@ -1476,7 +1476,7 @@ class MODLParser < Antlr4::Runtime::Parser
1476
1476
  @_state_number = 218
1477
1477
  modl_array_conditional()
1478
1478
  else
1479
- raise Antlr4::Runtime::NoViableAltException self
1479
+ raise Antlr4::Runtime::NoViableAltException, self
1480
1480
  end
1481
1481
  rescue Antlr4::Runtime::RecognitionException => re
1482
1482
  _localctx.exception = re
@@ -4,7 +4,7 @@ module MODL
4
4
  # It works recursively since class usage can be nested.
5
5
  class ClassProcessor
6
6
  # How deep can the class structure be?
7
- MAX_RECURSION_DEPTH = 20
7
+ MAX_RECURSION_DEPTH = 50
8
8
  # global is a GlobalParseContext and obj is the extracted Array or Hash from MODL::Parser::Parsed.extract_json
9
9
  def self.process(global, obj)
10
10
  # Process each object in the array or just process the object if its a hash.
@@ -22,29 +22,8 @@ module MODL
22
22
 
23
23
  private
24
24
 
25
- def self.check_class_type(global, key, value)
26
- clazz = global.classs(key)
27
- top = top_class(clazz, global)
28
- case top
29
- when 'num'
30
- unless value.is_a?(Numeric)
31
- raise InterpreterError, 'Interpreter Error: Numeric value expected, but found: ' + value.to_s + ' of type ' + value.class.to_s
32
- end
33
- return value
34
- when 'str'
35
- return value.to_s
36
- end
37
- value
38
- end
39
-
40
25
  # Process the contents of the supplied hash obj
41
26
  def self.process_obj(global, obj)
42
- if obj.length == 1
43
- k = obj.keys[0]
44
- nv = check_class_type(global, k, obj[k])
45
- obj[k] = nv
46
- end
47
-
48
27
  obj.keys.each do |k|
49
28
  value = obj[k]
50
29
  # Does the key refer to a class that we have parsed or loaded?
@@ -76,7 +55,32 @@ module MODL
76
55
  end
77
56
  end
78
57
 
79
- # Convert the supplied object val into an instance of the class with key k
58
+ def self.has_assign_statement?(clazz, global, depth = 0)
59
+ # Check for *assign statements
60
+ return if depth > MAX_RECURSION_DEPTH
61
+ return nil? if clazz.nil?
62
+ return true unless clazz.assign.nil?
63
+
64
+ superclass = clazz.superclass
65
+ c = global.classs(superclass)
66
+ return has_assign_statement?(c, global, depth + 1) if c
67
+
68
+ false
69
+ end
70
+
71
+ def self.has_inherited_pairs?(clazz, global, depth = 0)
72
+ # Check for *assign statements
73
+ return if depth > MAX_RECURSION_DEPTH
74
+ return nil? if clazz.nil?
75
+ return true unless clazz.content.empty?
76
+
77
+ superclass = clazz.superclass
78
+ c = global.classs(superclass)
79
+ return has_inherited_pairs?(c, global, depth + 1) if c
80
+
81
+ false
82
+ end
83
+
80
84
  def self.process_class(global, k, v)
81
85
  clazz = global.classs(k)
82
86
  if k != clazz.id && !(v.is_a?(Hash) || v.is_a?(Array))
@@ -99,22 +103,78 @@ module MODL
99
103
  elsif v.is_a?(String)
100
104
  # Safe to ignore
101
105
  else
102
- # Check the top class and do some type-specific processing
103
- tc = top_class(clazz, global)
104
- if tc == 'str'
105
- new_value = v.to_s
106
- elsif tc == 'num' && !v.is_a?(Numeric)
107
- raise InterpreterError, 'Superclass of "' + clazz.id + '" is num - cannot assign String value "' + v.to_s + '"'
108
- elsif tc == 'map'
109
- if v.is_a? Hash
110
- # Bring down values from the superclass hierarchy
111
- new_value = copy_from_superclasses(clazz, global, new_value, v)
106
+ # Safe to ignore
107
+ end
108
+
109
+ # Check the top class and do some type-specific processing
110
+ tc = top_class(clazz, global)
111
+ if tc.nil?
112
+ # There is no defined top class so we need to infer it base on the value
113
+ # and the rules defined here: https://github.com/MODLanguage/grammar/wiki/Class-Supertype-Processing
114
+ #
115
+ if has_assign_statement?(clazz, global)
116
+ tc = 'map'
117
+ else
118
+ if has_inherited_pairs?(clazz, global)
119
+ tc = 'map'
112
120
  else
113
- new_value[k] = v
121
+ if v.is_a? String
122
+ tc = 'str'
123
+ elsif v.is_a? Numeric
124
+ tc = 'num'
125
+ elsif (v.is_a? TrueClass) || (v.is_a? FalseClass)
126
+ tc = 'bool'
127
+ elsif v.nil?
128
+ tc = 'null'
129
+ elsif v.is_a? Array
130
+ tc = 'arr'
131
+ elsif v.is_a? Hash
132
+ tc = 'map'
133
+ end
114
134
  end
135
+ end
136
+ end
137
+ if tc == 'str'
138
+ raise InterpreterError, "Interpreter Error: Cannot convert null value to string." if v.nil?
139
+ new_value = v.to_s
140
+ elsif tc == 'num'
141
+ if (v.is_a? String) && (v.to_i.to_s == v.to_s)
142
+ new_value = v.to_i
143
+ elsif v.is_a? TrueClass
144
+ new_value = 1
145
+ elsif v.is_a? FalseClass
146
+ new_value = 0
147
+ elsif v.is_a? Numeric
148
+ new_value = v
115
149
  else
150
+ raise InterpreterError, 'Superclass of "' + clazz.id + '" is num - cannot assign value "' + v.to_s + '"'
151
+ end
152
+ elsif tc == 'bool'
153
+ new_value = v
154
+ elsif tc == 'null'
155
+ new_value = nil
156
+ elsif tc == 'arr'
157
+ if v.is_a? Array
116
158
  new_value = v
159
+ elsif v.is_a? Hash
160
+ raise InterpreterError, 'Interpreter Error: Cannot convert map to array: ' + v.to_s
161
+ else
162
+ new_value = [v]
163
+ end
164
+ elsif tc == 'map'
165
+ if new_value.is_a? Hash
166
+ # Bring down values from the superclass hierarchy
167
+ new_value = copy_from_superclasses(clazz, global, new_value, v)
168
+ elsif v.is_a? Array
169
+ raise InterpreterError, 'Interpreter Error: Cannot convert array to map: ' + v.to_s
170
+ else
171
+ new_value = {}
172
+ new_value['value'] = v
173
+ # Bring down values from the superclass hierarchy
174
+ new_value = copy_from_superclasses(clazz, global, new_value, v)
117
175
  end
176
+ elsif tc.nil? && (v.is_a? Hash)
177
+ new_value = v
118
178
  end
119
179
 
120
180
  [clazz.name_or_id, new_value]
@@ -122,7 +182,12 @@ module MODL
122
182
 
123
183
  # Bring down values from the superclass hierarchy
124
184
  def self.copy_from_superclasses(clazz, global, new_value, v)
125
- new_value = v.merge(new_value)
185
+ if v.is_a? Hash
186
+ new_value = v.merge(new_value)
187
+ end
188
+
189
+ clazz.merge_content(new_value)
190
+
126
191
  depth = 0
127
192
  loop do
128
193
  clazz = global.classs(clazz.superclass)
@@ -91,73 +91,6 @@ module MODL
91
91
  end
92
92
  [value1, success]
93
93
  end
94
-
95
- def evaluate_old
96
- result = false
97
- if @key
98
- if @key.include?('%')
99
- value1, _ignore = MODL::Parser::RefProcessor.deref(@key, @global)
100
- else
101
- key = @key
102
- ikey = key.to_i
103
- if ikey.to_s == key
104
- index_val = @global.index[ikey]
105
- value1 = index_val.respond_to?(:text) ? index_val.text : nil
106
- else
107
- pair = @global.pair(key)
108
- return false unless pair
109
-
110
- value1 = pair.text
111
- end
112
- end
113
-
114
- @values.each do |value|
115
- value2 = value.text
116
- value2, _ignore = MODL::Parser::RefProcessor.deref(value2, @global) if value2.is_a?(String) && value2.include?('%')
117
- value2 = @global.pair(value.text).text if @global.pair(value.text)
118
-
119
- case @operator
120
- when '='
121
- wild = value2.is_a?(String) && value2.include?('*') ? true : false
122
- if wild
123
- regex = '^'
124
- value2.each_char do |c|
125
- regex << (c == '*' ? '.*' : c)
126
- end
127
- result |= !value1.match(regex).nil?
128
- else
129
- result |= value1 == value2
130
- end
131
- when '>'
132
- result |= value1 > value2
133
- when '<'
134
- result |= value1 < value2
135
- when '>='
136
- result |= value1 >= value2
137
- when '<='
138
- result |= value1 <= value2
139
- end
140
- break if result # shortcut if we have a matching value
141
- end
142
- elsif @values.length == 1
143
- key = @values[0].text
144
- if key.is_a?(String)
145
- key = key.start_with?('%') ? Sutil.tail(key) : key
146
- end
147
- the_pair = @global.pair(key)
148
- if the_pair
149
- result = the_pair.text
150
- else
151
- return true if @values[0].trueVal
152
- return false if @values[0].falseVal
153
- return false if @values[0].string
154
-
155
- result = @values[0].evaluate
156
- end
157
- end
158
- result
159
- end
160
-
161
94
  end
162
95
  end
163
96
  end
@@ -9,7 +9,7 @@ module MODL
9
9
  class FileImporter
10
10
  include Singleton
11
11
 
12
- CACHE_DISABLED = true
12
+ CACHE_DISABLED = false
13
13
 
14
14
  def initialize
15
15
  @cache = ObjectCache.new
@@ -36,31 +36,36 @@ module MODL
36
36
  end
37
37
 
38
38
  # Did we hit the cache?
39
- if (parsed.nil? && CACHE_DISABLED) || (CACHE_DISABLED)
39
+ if parsed.nil? || CACHE_DISABLED
40
40
  # No.
41
41
 
42
42
  begin
43
- uri = URI(file_name)
44
- txt = Net::HTTP.get(uri)
45
- rescue
46
- begin
43
+ if file_name.start_with?('http')
44
+ uri = URI(file_name)
45
+ txt = Net::HTTP.get(uri)
46
+ else
47
47
  txt = File.readlines(file_name).join
48
- rescue
49
- raise InterpreterError, 'File not found: ' + file_name
50
48
  end
49
+ rescue
50
+ raise InterpreterError, 'File not found: ' + file_name
51
51
  end
52
-
53
52
  global.loaded_file(file_name)
54
53
 
55
54
  # Parse the downloaded file ands extract the classes
56
- parsed = MODL::Parser::Parser.parse txt, global
55
+ new_parse_context = GlobalParseContext.new
56
+ new_parse_context.merge_pairs(global)
57
+ parsed = MODL::Parser::Parser.parse txt, new_parse_context
57
58
  # Save it for next time
58
59
  @cache.put(file_name, parsed) unless CACHE_DISABLED
60
+ else
61
+ global.loaded_file(file_name)
59
62
  end
60
63
  # Extract the JSON content and add the classes and pairs to the existing GlobalParseContext hashes.
61
64
  parsed.extract_hash
62
65
  global.merge_classes(parsed.global)
63
66
  global.merge_pairs(parsed.global)
67
+ global.merge_loaded_files(parsed.global)
68
+ global.structures.concat(parsed.structures)
64
69
  end
65
70
  end
66
71
  end
@@ -6,9 +6,16 @@ module MODL
6
6
  class GlobalParseContext
7
7
 
8
8
  attr_accessor :syntax_version
9
+ attr_accessor :max_files_allowed
10
+ attr_accessor :structures
9
11
  attr_reader :interpreter_syntax_version
12
+ attr_reader :loaded_files
10
13
 
11
14
  def initialize
15
+ # This keeps track of all loaded structures from parsed files.
16
+ @structures = []
17
+ # This is set to true if we encounter a *L[OAD] instruction
18
+ @max_files_allowed = 2147483647
12
19
  # Holds the index array from a MODL file, e.g. '?=a:b:c:d'
13
20
  @index = []
14
21
  # Contains all pairs as they are encountered in the parsing process.
@@ -29,6 +36,8 @@ module MODL
29
36
 
30
37
  def loaded_file(str)
31
38
  @loaded_files << str unless str.nil?
39
+ raise InterpreterError, 'Cannot load multiple files after *LOAD instruction' if @loaded_files.length > @max_files_allowed
40
+
32
41
  end
33
42
 
34
43
  def index_value(n, default)
@@ -41,6 +50,10 @@ module MODL
41
50
  @conditional.positive?
42
51
  end
43
52
 
53
+ def freeze_max_files(plus)
54
+ @max_files_allowed = @loaded_files.length + plus
55
+ end
56
+
44
57
  def enter_condition
45
58
  @conditional += 1
46
59
  end
@@ -75,6 +88,11 @@ module MODL
75
88
  @classes_by_name.merge!(other.all_classes_by_name)
76
89
  end
77
90
 
91
+ def merge_loaded_files(other)
92
+ @loaded_files.concat(other.loaded_files)
93
+ raise InterpreterError, 'Cannot load multiple files after *LOAD instruction' if @loaded_files.length > @max_files_allowed
94
+ end
95
+
78
96
  def has_pairs?
79
97
  @pairs.length.positive?
80
98
  end
@@ -127,6 +127,10 @@ module MODL
127
127
  # should be of the form .r(s1,s2)
128
128
  Sutil.between(str, '(', ')')
129
129
  end
130
+
131
+ def self.valid_method?(mthd)
132
+ return 'udisertp'.include?(mthd)
133
+ end
130
134
  end
131
135
  end
132
136
  end
@@ -40,11 +40,12 @@ module MODL
40
40
  @structures << structure
41
41
  end
42
42
 
43
+ @structures = global.structures + structures
44
+
43
45
  @global
44
46
  end
45
47
 
46
48
  def self.additional_string_processing(text)
47
- text = Substitutions.process text
48
49
  # Special case for a possibly empty graved string ``
49
50
  unless text.nil?
50
51
  match_data = /^`([^`]*)`$/.match text
@@ -246,7 +247,7 @@ module MODL
246
247
  value = @valueItem.extract_hash if @valueItem
247
248
  value = @map.extract_hash if @map
248
249
 
249
- if value.is_a?(String) && (value.start_with?('%') || value.start_with?('`'))
250
+ if value.is_a?(String) && (value.include?('%') || value.start_with?('`'))
250
251
  @text, _ignore = RefProcessor.deref(@text, @global)
251
252
  else
252
253
  @text = value
@@ -277,6 +278,8 @@ module MODL
277
278
  @key = Sutil.toptail(@key) # remove the quotes
278
279
  end
279
280
 
281
+ raise InterpreterError, 'Invalid key - null, true, or false keys are not allowed.' if @key.nil?
282
+
280
283
  if @key.include?('%') || @key.include?('`')
281
284
  @key, new_value = RefProcessor.deref @key, @global
282
285
  unless @key.is_a?(String)
@@ -287,10 +290,6 @@ module MODL
287
290
 
288
291
  @final = true if @key.upcase == @key
289
292
 
290
- set_pair_type
291
-
292
- raise InterpreterError, 'Invalid keyword: ' + @key if @type == 'pair' && @key.start_with?('*')
293
-
294
293
  modl_array = ctx.modl_array
295
294
  modl_map = ctx.modl_map
296
295
  modl_value_item = ctx.modl_value_item
@@ -306,6 +305,9 @@ module MODL
306
305
  modl_value_item.enter_rule(@valueItem)
307
306
  end
308
307
 
308
+ set_pair_type
309
+ raise InterpreterError, 'Invalid keyword: ' + @key if @type == 'pair' && @key.start_with?('*')
310
+
309
311
  validate_key if @type == 'pair' || @type == 'hidden'
310
312
 
311
313
  # Type-specific processing
@@ -377,7 +379,7 @@ module MODL
377
379
  end
378
380
 
379
381
  def validate_key
380
- invalid_chars = "!$@-+'*#^&"
382
+ invalid_chars = "£!$@-+'*#^&"
381
383
  invalid_chars.each_char do |c|
382
384
  next unless @key.include?(c)
383
385
 
@@ -435,6 +437,15 @@ module MODL
435
437
  if @key == '*L' || @key == '*LOAD'
436
438
  @key = @key.downcase
437
439
  @type = 'import'
440
+ if @array
441
+ @global.freeze_max_files(@array.abstractArrayItems.length)
442
+ elsif @valueItem&.value&.array
443
+ @global.freeze_max_files(@valueItem&.value&.array.abstractArrayItems.length)
444
+ elsif @valueItem&.value&.nbArray
445
+ @global.freeze_max_files(@valueItem&.value&.nbArray.arrayItems.length)
446
+ else
447
+ @global.freeze_max_files(1)
448
+ end
438
449
  end
439
450
  if @key == '*l' || @key == '*load'
440
451
  @type = 'import'
@@ -636,7 +647,7 @@ module MODL
636
647
  def extract_hash
637
648
  result, _ignore = RefProcessor.deref(@text, @global) unless @constant
638
649
  result = @text if @constant
639
- result
650
+ Substitutions.process result
640
651
  end
641
652
 
642
653
  def evaluate
@@ -670,6 +681,7 @@ module MODL
670
681
  @text = @number.num
671
682
  elsif !ctx_string.nil?
672
683
  @text = ctx_string.text
684
+
673
685
  @constant = @text.start_with?('`') && !@text.include?('%') && !@text.include?('`.')
674
686
  @text = Parsed.additional_string_processing(@text)
675
687
  @string = ParsedString.new(@text)
@@ -706,7 +718,7 @@ module MODL
706
718
  end
707
719
 
708
720
  def extract_hash
709
- @string
721
+ @string = Substitutions.process @string
710
722
  end
711
723
  end
712
724
 
@@ -914,8 +926,7 @@ module MODL
914
926
  end
915
927
 
916
928
  def extract_hash
917
- result = @conditionTests[0].evaluate
918
- return @mapConditionalReturns[0].extract_hash if result
929
+ return @mapConditionalReturns[0].extract_hash if @result
919
930
 
920
931
  @mapConditionalReturns[1].extract_hash if @mapConditionalReturns.length > 1
921
932
  end
@@ -942,6 +953,7 @@ module MODL
942
953
  end
943
954
  i += 1
944
955
  end
956
+ @result = @conditionTests[0].evaluate
945
957
  end
946
958
  end
947
959
 
@@ -1147,10 +1159,8 @@ module MODL
1147
1159
  end
1148
1160
 
1149
1161
  def extract_hash
1150
- result = @conditionTests[0].evaluate
1151
-
1152
- return result if @valueConditionalReturns.length == 0
1153
- return @valueConditionalReturns[0].extract_hash if result
1162
+ return @result if @valueConditionalReturns.length == 0
1163
+ return @valueConditionalReturns[0].extract_hash if @result
1154
1164
  return @valueConditionalReturns[1].extract_hash
1155
1165
  end
1156
1166
 
@@ -1165,7 +1175,7 @@ module MODL
1165
1175
 
1166
1176
  @conditionTests[i] = condition_test
1167
1177
 
1168
- return if ctx.modl_value_conditional_return_i(i).nil?
1178
+ break if ctx.modl_value_conditional_return_i(i).nil?
1169
1179
 
1170
1180
  conditional_return = ParsedValueConditionalReturn.new @global
1171
1181
 
@@ -1183,6 +1193,7 @@ module MODL
1183
1193
 
1184
1194
  i += 1
1185
1195
  end
1196
+ @result = @conditionTests[0].evaluate
1186
1197
  end
1187
1198
  end
1188
1199
 
@@ -42,10 +42,23 @@ module MODL
42
42
  def self.process_tokens(global, original, str, text)
43
43
  new_value = nil
44
44
  loop do
45
- match = MATCHER.match(text.to_s)
45
+ text_s = text.to_s
46
+ match = MATCHER.match(text_s)
46
47
  break if match.nil?
47
48
 
49
+ match_index = text_s.index(match[0])
50
+ if match_index > 0
51
+ if text_s[match_index - 1] == '~' || text_s[match_index - 1] == '\\'
52
+ break
53
+ end
54
+ if text_s[match_index + match.length] == '~' || text_s[match_index + match.length] == '\\'
55
+ break
56
+ end
57
+ end
58
+
59
+
48
60
  ref = match[0]
61
+ ref = Sutil.head(ref) if ref.end_with?('%')
49
62
  text = Sutil.after(text, ref)
50
63
  new_value, remainder = expand(global, ref)
51
64
  ref = Sutil.until(ref, remainder)
@@ -59,6 +72,8 @@ module MODL
59
72
  str.sub(ref, nv_text.to_s)
60
73
  end
61
74
  new_value = nil
75
+ elsif new_value.is_a?(Parsed::ParsedMapItem)
76
+ raise InterpreterError, 'Interpreter Error: Found a map when expecting an array'
62
77
  elsif new_value.is_a?(MODL::Parser::MODLParserBaseListener)
63
78
  if new_value.text
64
79
  str = if ref == str
@@ -99,6 +114,9 @@ module MODL
99
114
  n = p.to_i
100
115
  result = if n.to_s == p
101
116
  # Numeric ref
117
+ if !result.nil? && !result.respond_to?(:find_property)
118
+ raise InterpreterError, 'Interpreter Error: Invalid obejct reference: ' + degraved
119
+ end
102
120
  result.nil? ? global.index_value(n, degraved) : result.find_property(n)
103
121
  else
104
122
  # String ref
@@ -107,6 +125,9 @@ module MODL
107
125
  elsif result.is_a? Array
108
126
  nil
109
127
  else
128
+ if !result.nil? && !result.respond_to?(:find_property)
129
+ raise InterpreterError, 'Interpreter Error: Invalid obejct reference: ' + degraved
130
+ end
110
131
  result.nil? ? global.pair(p) : result.find_property(p)
111
132
  end
112
133
  end
@@ -126,8 +147,16 @@ module MODL
126
147
  # Remove the graves if there are any.
127
148
  result = parts[0]
128
149
  i = 1
150
+ stalled = false
129
151
  while i < parts.length
130
- result = StandardMethods.run_method(parts[i], result)
152
+ stalled |= StandardMethods.valid_method?(parts[i]) ? false : true
153
+
154
+ if stalled
155
+ result << '.'
156
+ result << parts[i]
157
+ else
158
+ result = StandardMethods.run_method(parts[i], result)
159
+ end
131
160
  i += 1
132
161
  end
133
162
  [result, '']
@@ -53,6 +53,7 @@ module MODL
53
53
 
54
54
  # Replace all escape sequences in the supplied string and return the new value.
55
55
  def self.process(str)
56
+ return str unless str.is_a? String
56
57
  @@subs.each do |s|
57
58
  loop do
58
59
  prev = str
@@ -1,5 +1,5 @@
1
1
  module MODL
2
2
  module Parser
3
- VERSION = "0.3.6"
3
+ VERSION = "0.3.7"
4
4
  end
5
5
  end
data/modl.gemspec CHANGED
@@ -26,6 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'antlr4-runtime', '>= 0.0.1'
27
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
28
  spec.add_development_dependency 'rspec', '~> 3.0'
29
- spec.add_runtime_dependency 'antlr4-runtime', '= 0.2.3'
29
+ spec.add_runtime_dependency 'antlr4-runtime', '= 0.2.6'
30
30
  spec.add_runtime_dependency 'punycode4r', '>= 0.2.0'
31
31
  end