modl 0.3.10 → 0.3.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,30 +12,29 @@ LSBRAC=11
12
12
  RSBRAC=12
13
13
  NUMBER=13
14
14
  COMMENT=14
15
- STRING=15
16
- HASH_PREFIX=16
17
- QUOTED=17
18
- GRAVED=18
19
- LCBRAC=19
20
- CWS=20
21
- QMARK=21
22
- FSLASH=22
23
- GTHAN=23
24
- LTHAN=24
25
- ASTERISK=25
26
- AMP=26
27
- PIPE=27
28
- EXCLAM=28
29
- CCOMMENT=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
- '{'=19
33
- '?'=21
34
- '/'=22
35
- '>'=23
36
- '<'=24
37
- '*'=25
38
- '&'=26
39
- '|'=27
40
- '!'=28
41
- '}'=30
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.index << item.value.text
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.sub(s1, s2)
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
- return @@mthd_names.include?(mthd)
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
@@ -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
- if value.is_a?(String) && (value.include?('%') || value.start_with?('`'))
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 = Sutil.toptail(ctx_quoted.text) # remove the quotes
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
- @global.pair(key, item.structures[0].pair)
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
- @global.pair(key, last_item.structures[0].pair)
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('((`?\%[0-9][0-9.][a-zA-Z0-9.(),]*`?)|(`?\%[0-9][0-9]*`?)|(`?\%[_a-zA-Z][_a-zA-Z0-9.%(),]*`?)|(`.*`\.[_a-zA-Z0-9.(),%]+)|(`.*`))')
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 if trivial_reject(str)
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
- new_value, remainder = expand(global, ref)
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
- if result.text
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
- result.find_property(p)
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
- global.pair(p)
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 = str
82
- str = str.sub(s[0], s[1])
83
- break unless str && str != prev
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
- str
91
+ new_str
87
92
  end
88
93
  end
89
94
  end
@@ -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
@@ -24,6 +24,6 @@
24
24
 
25
25
  module MODL
26
26
  module Parser
27
- VERSION = "0.3.10"
27
+ VERSION = "0.3.11"
28
28
  end
29
29
  end
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.10
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-07 00:00:00.000000000 Z
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