modl 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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