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