modl 0.3.10 → 0.3.11

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.
@@ -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