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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/grammar_tests/base_tests.json +1231 -867
- data/grammar_tests/error_tests.json +8 -1
- data/lib/modl/parser/MODLParser.rb +5 -5
- data/lib/modl/parser/class_processor.rb +100 -35
- data/lib/modl/parser/evaluator.rb +0 -67
- data/lib/modl/parser/file_importer.rb +15 -10
- data/lib/modl/parser/global_parse_context.rb +18 -0
- data/lib/modl/parser/modl_method.rb +4 -0
- data/lib/modl/parser/parsed.rb +27 -16
- data/lib/modl/parser/ref_processor.rb +31 -2
- data/lib/modl/parser/substitutions.rb +1 -0
- data/lib/modl/parser/version.rb +1 -1
- data/modl.gemspec +1 -1
- metadata +4 -4
@@ -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 =
|
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
|
-
|
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
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
39
|
+
if parsed.nil? || CACHE_DISABLED
|
40
40
|
# No.
|
41
41
|
|
42
42
|
begin
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
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
|
data/lib/modl/parser/parsed.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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, '']
|
data/lib/modl/parser/version.rb
CHANGED
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.
|
29
|
+
spec.add_runtime_dependency 'antlr4-runtime', '= 0.2.6'
|
30
30
|
spec.add_runtime_dependency 'punycode4r', '>= 0.2.0'
|
31
31
|
end
|