modl 0.3.10 → 0.3.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/grammar_tests/b.modl +1 -1
- data/grammar_tests/base_tests.json +2964 -1340
- data/grammar_tests/c.modl +1 -1
- data/grammar_tests/error_tests.json +3 -3
- data/grammar_tests/files/a-b.txt +1 -0
- data/grammar_tests/files/c-d.txt +1 -0
- data/lib/modl/parser/MODLLexer.interp +6 -10
- data/lib/modl/parser/MODLLexer.rb +226 -228
- data/lib/modl/parser/MODLLexer.tokens +25 -26
- data/lib/modl/parser/MODLParser.interp +2 -4
- data/lib/modl/parser/MODLParser.rb +356 -370
- data/lib/modl/parser/MODLParser.tokens +25 -26
- data/lib/modl/parser/evaluator.rb +4 -3
- data/lib/modl/parser/file_importer.rb +2 -2
- data/lib/modl/parser/modl_index.rb +1 -1
- data/lib/modl/parser/modl_method.rb +11 -7
- data/lib/modl/parser/parsed.rb +30 -14
- data/lib/modl/parser/ref_processor.rb +47 -11
- data/lib/modl/parser/substitutions.rb +9 -4
- data/lib/modl/parser/sutil.rb +8 -0
- data/lib/modl/parser/version.rb +1 -1
- metadata +4 -2
@@ -12,30 +12,29 @@ LSBRAC=11
|
|
12
12
|
RSBRAC=12
|
13
13
|
NUMBER=13
|
14
14
|
COMMENT=14
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
RCBRAC=30
|
15
|
+
QUOTED=15
|
16
|
+
STRING=16
|
17
|
+
HASH_PREFIX=17
|
18
|
+
LCBRAC=18
|
19
|
+
CWS=19
|
20
|
+
QMARK=20
|
21
|
+
FSLASH=21
|
22
|
+
GTHAN=22
|
23
|
+
LTHAN=23
|
24
|
+
ASTERISK=24
|
25
|
+
AMP=25
|
26
|
+
PIPE=26
|
27
|
+
EXCLAM=27
|
28
|
+
CCOMMENT=28
|
29
|
+
RCBRAC=29
|
31
30
|
','=8
|
32
|
-
'{'=
|
33
|
-
'?'=
|
34
|
-
'/'=
|
35
|
-
'>'=
|
36
|
-
'<'=
|
37
|
-
'*'=
|
38
|
-
'&'=
|
39
|
-
'|'=
|
40
|
-
'!'=
|
41
|
-
'}'=
|
31
|
+
'{'=18
|
32
|
+
'?'=20
|
33
|
+
'/'=21
|
34
|
+
'>'=22
|
35
|
+
'<'=23
|
36
|
+
'*'=24
|
37
|
+
'&'=25
|
38
|
+
'|'=26
|
39
|
+
'!'=27
|
40
|
+
'}'=29
|
@@ -54,7 +54,7 @@ module MODL
|
|
54
54
|
while i < condition.values.length
|
55
55
|
item = condition.values[i]
|
56
56
|
if item.primitive.constant
|
57
|
-
value2 = item.text
|
57
|
+
value2 = Substitutions.process(item.text)
|
58
58
|
else
|
59
59
|
value2, success = value(global, item.text)
|
60
60
|
end
|
@@ -105,11 +105,12 @@ module MODL
|
|
105
105
|
if ikey.to_s == key
|
106
106
|
index_val = global.index[ikey]
|
107
107
|
value1 = index_val.respond_to?(:text) ? index_val.text : nil
|
108
|
+
value1 = Substitutions.process(value1)
|
108
109
|
else
|
109
110
|
pair = global.pair(key)
|
110
|
-
return k unless pair
|
111
|
+
return Substitutions.process(k) unless pair
|
111
112
|
|
112
|
-
value1 = pair.text
|
113
|
+
value1 = Substitutions.process(pair.text)
|
113
114
|
end
|
114
115
|
success = true
|
115
116
|
end
|
@@ -70,8 +70,8 @@ module MODL
|
|
70
70
|
else
|
71
71
|
txt = File.readlines(file_name).join
|
72
72
|
end
|
73
|
-
rescue
|
74
|
-
raise InterpreterError, 'File not found: ' + file_name
|
73
|
+
rescue StandardError => e
|
74
|
+
raise InterpreterError, 'File not found: ' + file_name + ', error: ' + e.message
|
75
75
|
end
|
76
76
|
global.loaded_file(file_name)
|
77
77
|
|
@@ -33,7 +33,7 @@ module MODL
|
|
33
33
|
# collect all values from the object
|
34
34
|
if item.is_a? Parsed::ParsedValueItem
|
35
35
|
if item&.value&.text
|
36
|
-
global.
|
36
|
+
global.add_to_index(item.value.text)
|
37
37
|
elsif item&.value&.array
|
38
38
|
item.value.array.abstractArrayItems.each do |avi|
|
39
39
|
global.add_to_index(avi.arrayValueItem)
|
@@ -40,18 +40,18 @@ module MODL
|
|
40
40
|
transform = @transform
|
41
41
|
while transform && transform.length > 0
|
42
42
|
if transform.start_with? 'replace'
|
43
|
-
close_bracket = transform.index('
|
43
|
+
close_bracket = transform.index('>')
|
44
44
|
m = Sutil.head(transform, close_bracket + 1).sub!('replace', 'r')
|
45
45
|
str = StandardMethods.run_method(m, str)
|
46
46
|
# Consume the subst clause
|
47
|
-
close_bracket = transform.index('
|
47
|
+
close_bracket = transform.index('>')
|
48
48
|
transform = Sutil.tail(transform, close_bracket + 2)
|
49
49
|
elsif transform.start_with? 'trim'
|
50
|
-
close_bracket = transform.index('
|
50
|
+
close_bracket = transform.index('>')
|
51
51
|
m = Sutil.head(transform, close_bracket + 1).sub!('trim', 't')
|
52
52
|
str = StandardMethods.run_method(m, str)
|
53
53
|
# Consume the trunc clause
|
54
|
-
close_bracket = transform.index('
|
54
|
+
close_bracket = transform.index('>')
|
55
55
|
transform = Sutil.tail(transform, close_bracket + 2)
|
56
56
|
elsif transform.start_with? 'initcap'
|
57
57
|
str = str.split.map(&:capitalize) * ' '
|
@@ -129,7 +129,7 @@ module MODL
|
|
129
129
|
CGI.escape(str)
|
130
130
|
when 'r', 'replace'
|
131
131
|
s1, s2 = get_subst_parts(mthd)
|
132
|
-
str.
|
132
|
+
str.gsub(s1, s2)
|
133
133
|
when 't', 'trim'
|
134
134
|
s1 = extract_params mthd
|
135
135
|
i = str.index(s1)
|
@@ -153,11 +153,15 @@ module MODL
|
|
153
153
|
# Extract the method parameter
|
154
154
|
def self.extract_params(str)
|
155
155
|
# should be of the form .r(s1,s2)
|
156
|
-
Sutil.between(str, '
|
156
|
+
Sutil.between(str, '<', '>')
|
157
157
|
end
|
158
158
|
|
159
159
|
def self.valid_method?(mthd)
|
160
|
-
|
160
|
+
m = mthd
|
161
|
+
if m.include?('<')
|
162
|
+
m = Sutil.until(m, '<')
|
163
|
+
end
|
164
|
+
return @@mthd_names.include?(m)
|
161
165
|
end
|
162
166
|
end
|
163
167
|
end
|
data/lib/modl/parser/parsed.rb
CHANGED
@@ -202,12 +202,15 @@ module MODL
|
|
202
202
|
attr_accessor :type # A string set to the type of pair that we have found bases on its key
|
203
203
|
attr_accessor :text # The simple text value rather than the object
|
204
204
|
attr_accessor :final
|
205
|
+
attr_accessor :loaded # For *load instructions so we don't load twice
|
206
|
+
|
205
207
|
|
206
208
|
def initialize(global)
|
207
209
|
@global = global
|
208
210
|
@needs_defref = true
|
209
211
|
@final = false
|
210
212
|
@file_importer = FileImporter.instance
|
213
|
+
@loaded = false
|
211
214
|
end
|
212
215
|
|
213
216
|
def find_property(key)
|
@@ -271,11 +274,7 @@ module MODL
|
|
271
274
|
value = @valueItem.extract_hash if @valueItem
|
272
275
|
value = @map.extract_hash if @map
|
273
276
|
|
274
|
-
|
275
|
-
@text, _ignore = RefProcessor.deref(@text, @global)
|
276
|
-
else
|
277
|
-
@text = value
|
278
|
-
end
|
277
|
+
@text = value
|
279
278
|
|
280
279
|
return if @type == 'index'
|
281
280
|
return if @type == 'hidden'
|
@@ -294,8 +293,6 @@ module MODL
|
|
294
293
|
|
295
294
|
ctx_string = ctx.STRING
|
296
295
|
@key = ctx_string.to_s unless ctx_string.nil?
|
297
|
-
ctx_string = ctx.NUMBER
|
298
|
-
@key = ctx_string.to_s unless ctx_string.nil?
|
299
296
|
ctx_quoted = ctx.QUOTED
|
300
297
|
unless ctx_quoted.nil?
|
301
298
|
@key = ctx_quoted.to_s
|
@@ -365,7 +362,7 @@ module MODL
|
|
365
362
|
when 'import'
|
366
363
|
files = @valueItem.extract_hash if @valueItem
|
367
364
|
files = @array.extract_hash if @array
|
368
|
-
@file_importer.import_files files, @global
|
365
|
+
@file_importer.import_files files, @global unless @global.in_condition?
|
369
366
|
when 'index'
|
370
367
|
IndexExtractor.extract(self, @global)
|
371
368
|
when 'hidden'
|
@@ -664,7 +661,7 @@ module MODL
|
|
664
661
|
if user_method
|
665
662
|
return user_method.run(@string.string)
|
666
663
|
end
|
667
|
-
return StandardMethods.run_method(key, @string.string)
|
664
|
+
return StandardMethods.run_method(key, Substitutions.process(@string.string))
|
668
665
|
end
|
669
666
|
end
|
670
667
|
|
@@ -707,13 +704,11 @@ module MODL
|
|
707
704
|
@text = ctx_string.text
|
708
705
|
|
709
706
|
@constant = @text.start_with?('`') && !@text.include?('%') && !@text.include?('`.')
|
710
|
-
@text = Parsed.additional_string_processing(@text)
|
711
707
|
@string = ParsedString.new(@text)
|
712
708
|
@text = @string.string
|
713
709
|
elsif !ctx_quoted.nil?
|
714
710
|
@constant = true
|
715
|
-
@text =
|
716
|
-
@text = Parsed.additional_string_processing(@text)
|
711
|
+
@text = ctx_quoted.text
|
717
712
|
@quoted = ParsedQuoted.new(@text)
|
718
713
|
elsif !ctx_null.nil?
|
719
714
|
@nilVal = ParsedNull.instance
|
@@ -1023,6 +1018,7 @@ module MODL
|
|
1023
1018
|
@global = global
|
1024
1019
|
@topLevelConditionalReturns = []
|
1025
1020
|
@conditionTests = []
|
1021
|
+
@file_importer = FileImporter.instance
|
1026
1022
|
end
|
1027
1023
|
|
1028
1024
|
def extract_hash
|
@@ -1033,7 +1029,17 @@ module MODL
|
|
1033
1029
|
if item.structures[0].pair
|
1034
1030
|
key = item.structures[0].pair.key
|
1035
1031
|
key = Sutil.tail(key) if key[0] == '_'
|
1036
|
-
|
1032
|
+
pair = item.structures[0].pair
|
1033
|
+
@global.pair(key, pair)
|
1034
|
+
|
1035
|
+
if key.downcase.start_with? '*l'
|
1036
|
+
files = pair.valueItem.extract_hash if pair.valueItem
|
1037
|
+
files = pair.array.extract_hash if pair.array
|
1038
|
+
unless pair.loaded
|
1039
|
+
@file_importer.import_files files, @global
|
1040
|
+
pair.loaded = true
|
1041
|
+
end
|
1042
|
+
end
|
1037
1043
|
end
|
1038
1044
|
return item.extract_hash
|
1039
1045
|
end
|
@@ -1043,7 +1049,17 @@ module MODL
|
|
1043
1049
|
if last_item.structures[0].pair
|
1044
1050
|
key = last_item.structures[0].pair.key
|
1045
1051
|
key = Sutil.tail(key) if key[0] == '_'
|
1046
|
-
|
1052
|
+
pair = last_item.structures[0].pair
|
1053
|
+
@global.pair(key, pair)
|
1054
|
+
|
1055
|
+
if key.downcase.start_with? '*l'
|
1056
|
+
files = pair.valueItem.extract_hash if pair.valueItem
|
1057
|
+
files = pair.array.extract_hash if pair.array
|
1058
|
+
unless pair.loaded
|
1059
|
+
@file_importer.import_files files, @global
|
1060
|
+
pair.loaded = true
|
1061
|
+
end
|
1062
|
+
end
|
1047
1063
|
end
|
1048
1064
|
last_item.extract_hash
|
1049
1065
|
end
|
@@ -32,18 +32,19 @@ module MODL
|
|
32
32
|
class RefProcessor
|
33
33
|
|
34
34
|
NESTED_SEPARATOR = '.'
|
35
|
-
MATCHER = Regexp.new('((
|
35
|
+
MATCHER = Regexp.new('((%\w+)(\.\w*<`?\w*`?,`\w*`>)+|(%` ?[\w-]+`[\w.<>,]*%?)|(%\*?[\w]+(\.%?\w*<?[\w,]*>?)*%?))')
|
36
|
+
MAX_RECURSE_DEPTH = 10
|
36
37
|
|
37
38
|
def self.trivial_reject(str)
|
38
39
|
# do a fast check to see if we need to deref - save processing the regex if we don't have to.
|
39
|
-
str.is_a?(String) && (str.nil? || str.include?('%') || str.include?('`'))
|
40
|
+
!(str.is_a?(String) && !str.start_with?('%*') && (str.nil? || str.include?('%') || str.include?('`')))
|
40
41
|
end
|
41
42
|
|
42
43
|
# Check str for references and process them.
|
43
44
|
# Return the processed string and a new_value if there is one.
|
44
45
|
def self.deref(str, global)
|
45
46
|
obj = str
|
46
|
-
obj, new_value = split_by_ref_tokens str, global
|
47
|
+
obj, new_value = split_by_ref_tokens str, global unless trivial_reject(str)
|
47
48
|
[obj, new_value]
|
48
49
|
end
|
49
50
|
|
@@ -80,9 +81,9 @@ module MODL
|
|
80
81
|
|
81
82
|
|
82
83
|
ref = match[0]
|
83
|
-
ref = Sutil.head(ref) if ref.end_with?('%')
|
84
84
|
text = Sutil.after(text, ref)
|
85
|
-
|
85
|
+
|
86
|
+
new_value, remainder = expand(0, global, ref)
|
86
87
|
ref = Sutil.until(ref, remainder)
|
87
88
|
if new_value.is_a?(String)
|
88
89
|
str = str.sub(ref, new_value)
|
@@ -101,7 +102,7 @@ module MODL
|
|
101
102
|
str = if ref == str
|
102
103
|
new_value.text
|
103
104
|
else
|
104
|
-
str.sub(ref, new_value.text.to_s)
|
105
|
+
str.sub(ref, Sutil.unquote(new_value.text.to_s))
|
105
106
|
end
|
106
107
|
new_value = nil
|
107
108
|
else
|
@@ -115,7 +116,10 @@ module MODL
|
|
115
116
|
return new_value, str
|
116
117
|
end
|
117
118
|
|
118
|
-
def self.expand(global, ref)
|
119
|
+
def self.expand(depth, global, ref)
|
120
|
+
if depth > MAX_RECURSE_DEPTH
|
121
|
+
raise InterpreterError, 'Recursing too deep to resolve: "' + ref + '"'
|
122
|
+
end
|
119
123
|
result = nil
|
120
124
|
prev = nil
|
121
125
|
|
@@ -123,12 +127,13 @@ module MODL
|
|
123
127
|
|
124
128
|
parts = Sutil.tail(degraved).split('.') if degraved[0] == '%'
|
125
129
|
parts = degraved.split('.') unless degraved[0] == '%'
|
130
|
+
parts[-1] = Sutil.head(parts[-1]) if parts[-1].end_with?('%')
|
126
131
|
|
127
132
|
if degraved.include?('%')
|
128
133
|
resolved = 0
|
129
134
|
parts.each do |p|
|
130
135
|
if p.include?('%')
|
131
|
-
p, _ignore = expand(global, p)
|
136
|
+
p, _ignore = expand(depth + 1, global, p)
|
132
137
|
if p.is_a?(MODL::Parser::MODLParserBaseListener)
|
133
138
|
p = p.text
|
134
139
|
end
|
@@ -154,7 +159,8 @@ module MODL
|
|
154
159
|
end
|
155
160
|
end
|
156
161
|
elsif result.is_a? Parsed::ParsedPair
|
157
|
-
|
162
|
+
prop = result.find_property(p)
|
163
|
+
if result.text && !prop
|
158
164
|
if StandardMethods.valid_method?(p)
|
159
165
|
StandardMethods.run_method(p, result.text)
|
160
166
|
else
|
@@ -166,7 +172,27 @@ module MODL
|
|
166
172
|
end
|
167
173
|
end
|
168
174
|
else
|
169
|
-
|
175
|
+
prop
|
176
|
+
end
|
177
|
+
elsif result.is_a? Parsed::ParsedArrayValueItem
|
178
|
+
prop = result.find_property(p)
|
179
|
+
if result.text && !prop
|
180
|
+
if StandardMethods.valid_method?(p)
|
181
|
+
result_text = result.text
|
182
|
+
if result_text.start_with?('`') && result_text.end_with?('`')
|
183
|
+
result_text = Sutil.toptail(result_text)
|
184
|
+
end
|
185
|
+
StandardMethods.run_method(p, result_text)
|
186
|
+
else
|
187
|
+
mthd = global.user_method(p)
|
188
|
+
if !mthd.nil?
|
189
|
+
mthd.run(result.text)
|
190
|
+
else
|
191
|
+
mthd
|
192
|
+
end
|
193
|
+
end
|
194
|
+
else
|
195
|
+
prop
|
170
196
|
end
|
171
197
|
elsif result.is_a? Array
|
172
198
|
nil
|
@@ -175,7 +201,14 @@ module MODL
|
|
175
201
|
raise InterpreterError, 'Interpreter Error: Invalid object reference: ' + degraved
|
176
202
|
end
|
177
203
|
if result.nil?
|
178
|
-
|
204
|
+
unless ref.start_with?('%`')
|
205
|
+
a_pair = global.pair(p)
|
206
|
+
end
|
207
|
+
if a_pair.nil?
|
208
|
+
p
|
209
|
+
else
|
210
|
+
a_pair
|
211
|
+
end
|
179
212
|
else
|
180
213
|
result.find_property(p)
|
181
214
|
end
|
@@ -192,6 +225,9 @@ module MODL
|
|
192
225
|
else
|
193
226
|
remainder = resolved < parts.length ? '.' + parts[resolved..parts.length].join('.') : ''
|
194
227
|
end
|
228
|
+
if (prev == Sutil.between(ref, '%', '%')) || (ref.start_with?('%') && prev == Sutil.tail(ref))
|
229
|
+
prev = ref
|
230
|
+
end
|
195
231
|
[prev, remainder]
|
196
232
|
else
|
197
233
|
# Remove the graves if there are any.
|
@@ -76,14 +76,19 @@ module MODL
|
|
76
76
|
# Replace all escape sequences in the supplied string and return the new value.
|
77
77
|
def self.process(str)
|
78
78
|
return str unless str.is_a? String
|
79
|
+
|
80
|
+
# Remove unescaped graves and double quotes
|
81
|
+
new_str = Sutil.unquote(str)
|
82
|
+
|
83
|
+
# Handle escape sequences
|
79
84
|
@@subs.each do |s|
|
80
85
|
loop do
|
81
|
-
prev =
|
82
|
-
|
83
|
-
break unless
|
86
|
+
prev = new_str
|
87
|
+
new_str = new_str.sub(s[0], s[1])
|
88
|
+
break unless new_str && new_str != prev
|
84
89
|
end
|
85
90
|
end
|
86
|
-
|
91
|
+
new_str
|
87
92
|
end
|
88
93
|
end
|
89
94
|
end
|
data/lib/modl/parser/sutil.rb
CHANGED
@@ -97,4 +97,12 @@ module Sutil
|
|
97
97
|
end
|
98
98
|
result
|
99
99
|
end
|
100
|
+
|
101
|
+
def self.unquote(str)
|
102
|
+
new_str = str
|
103
|
+
if (str.start_with?('`') && str.end_with?('`')) || (str.start_with?('"') && str.end_with?('"'))
|
104
|
+
new_str = Sutil.toptail(str)
|
105
|
+
end
|
106
|
+
new_str
|
107
|
+
end
|
100
108
|
end
|
data/lib/modl/parser/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Walmsley
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: antlr4-runtime
|
@@ -109,6 +109,8 @@ files:
|
|
109
109
|
- grammar_tests/c.modl
|
110
110
|
- grammar_tests/demo_config.modl
|
111
111
|
- grammar_tests/error_tests.json
|
112
|
+
- grammar_tests/files/a-b.txt
|
113
|
+
- grammar_tests/files/c-d.txt
|
112
114
|
- grammar_tests/import_config.modl
|
113
115
|
- grammar_tests/test_import_dir/nested_import1.txt
|
114
116
|
- grammar_tests/test_import_dir/nested_import2.txt
|