modl 0.3.26 → 0.3.27

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -149
  3. data/Gemfile +4 -2
  4. data/LICENSE.txt +1 -1
  5. data/README.md +19 -11
  6. data/Rakefile +5 -3
  7. data/lib/modl/interpreter.rb +38 -0
  8. data/lib/modl/model/model.rb +264 -0
  9. data/lib/modl/parser/parser.rb +272 -59
  10. data/lib/modl/tokeniser/context.rb +113 -0
  11. data/lib/modl/tokeniser/tokeniser.rb +28 -0
  12. data/lib/modl/util/functions.rb +74 -0
  13. data/lib/modl/util/unicode.rb +44 -0
  14. data/lib/modl/version.rb +5 -0
  15. data/lib/modl.rb +7 -32
  16. data/modl.gemspec +8 -11
  17. metadata +16 -75
  18. data/.DS_Store +0 -0
  19. data/.idea/vcs.xml +0 -6
  20. data/.rspec +0 -3
  21. data/.rubocop.yml +0 -5
  22. data/.travis.yml +0 -7
  23. data/bin/console +0 -14
  24. data/bin/setup +0 -8
  25. data/lib/modl/parser/MODLLexer.interp +0 -132
  26. data/lib/modl/parser/MODLLexer.rb +0 -324
  27. data/lib/modl/parser/MODLLexer.tokens +0 -40
  28. data/lib/modl/parser/MODLParser.interp +0 -93
  29. data/lib/modl/parser/MODLParser.rb +0 -2492
  30. data/lib/modl/parser/MODLParser.tokens +0 -40
  31. data/lib/modl/parser/MODLParserBaseListener.rb +0 -164
  32. data/lib/modl/parser/MODLParserBaseVisitor.rb +0 -107
  33. data/lib/modl/parser/MODLParserListener.rb +0 -151
  34. data/lib/modl/parser/MODLParserVisitor.rb +0 -56
  35. data/lib/modl/parser/class_processor.rb +0 -411
  36. data/lib/modl/parser/evaluator.rb +0 -125
  37. data/lib/modl/parser/file_importer.rb +0 -101
  38. data/lib/modl/parser/global_parse_context.rb +0 -318
  39. data/lib/modl/parser/instruction_processor.rb +0 -82
  40. data/lib/modl/parser/interpreter.rb +0 -75
  41. data/lib/modl/parser/modl_class.rb +0 -138
  42. data/lib/modl/parser/modl_index.rb +0 -54
  43. data/lib/modl/parser/modl_keylist.rb +0 -81
  44. data/lib/modl/parser/modl_method.rb +0 -172
  45. data/lib/modl/parser/object_cache.rb +0 -88
  46. data/lib/modl/parser/orphan_handler.rb +0 -98
  47. data/lib/modl/parser/parsed.rb +0 -1469
  48. data/lib/modl/parser/ref_processor.rb +0 -258
  49. data/lib/modl/parser/substitutions.rb +0 -101
  50. data/lib/modl/parser/sutil.rb +0 -108
  51. data/lib/modl/parser/throwing_error_listener.rb +0 -44
  52. data/lib/modl/parser/unicode_escape_replacer.rb +0 -148
  53. data/lib/modl/parser/unicode_escapes.rb +0 -112
  54. data/lib/modl/parser/version.rb +0 -29
@@ -1,1469 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The MIT License (MIT)
4
- #
5
- # Copyright (c) 2019 NUM Technology Ltd
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the "Software"), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in
15
- # all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- # THE SOFTWARE.
24
-
25
- require 'modl/parser/MODLParserBaseListener'
26
- require 'modl/parser/global_parse_context'
27
- require 'modl/parser/ref_processor'
28
- require 'modl/parser/unicode_escapes'
29
- require 'modl/parser/substitutions'
30
- require 'modl/parser/file_importer'
31
- require 'antlr4/runtime/parse_cancellation_exception'
32
- require 'modl/parser/sutil'
33
- require 'modl/parser/modl_class'
34
- require 'modl/parser/modl_method'
35
- require 'modl/parser/modl_index'
36
- require 'modl/parser/modl_keylist'
37
- require 'modl/parser/evaluator'
38
- require 'cgi'
39
- require 'net/http'
40
-
41
- module MODL
42
- module Parser
43
- # This class represents a MODL parse tree for a given MODL object.
44
- # It tries to process the parse tree as it is generated as much as
45
- # possible to save revisiting nodes unnecessarily.
46
- #
47
- # Many of the method names are generated by ANTLR4 so are not ruby style.
48
- class Parsed < MODL::Parser::MODLParserBaseListener
49
- attr_accessor :structures
50
- attr_accessor :global
51
-
52
- def initialize(global = nil)
53
- @global = global
54
- @structures = []
55
- end
56
-
57
- def enterModl(ctx)
58
-
59
- @global = GlobalParseContext.new if @global.nil?
60
-
61
- ctx_modl_structure = ctx.modl_structure
62
- ctx_modl_structure.each do |str|
63
- structure = ParsedStructure.new @global
64
- str.enter_rule(structure)
65
- @structures << structure
66
- end
67
-
68
- @structures = @global.structures + @structures
69
-
70
- @global
71
- end
72
-
73
- def self.additional_string_processing(text)
74
- # Special case for a possibly empty graved string ``
75
- unless text.nil?
76
- match_data = /^`([^`]*)`$/.match text
77
- return match_data[1] if match_data&.length&.positive?
78
- end
79
- text
80
- end
81
-
82
- # Class to represent a parsed grammar object
83
- class ParsedMap < MODL::Parser::MODLParserBaseListener
84
- attr_accessor :mapItems
85
-
86
- def initialize(global)
87
- @global = global
88
- @mapItems = []
89
- end
90
-
91
- def find_property(key)
92
- if key.is_a? Integer
93
- return @mapItems[key]
94
- else
95
- @mapItems.each do |mi|
96
- return mi.pair if mi.pair.key == key
97
- end
98
- end
99
- end
100
-
101
- def enterModl_map(ctx)
102
- modl_map_item = ctx.modl_map_item
103
- return if modl_map_item.nil?
104
-
105
- modl_map_item.each do |mi|
106
- map_item = ParsedMapItem.new @global
107
- mi.enter_rule(map_item)
108
- @mapItems << map_item
109
- end
110
- end
111
-
112
- def extract_hash
113
- result = {}
114
- @mapItems.each do |i|
115
- i_hash = i.extract_hash
116
- next unless i_hash.is_a? Hash
117
-
118
- i_hash.keys.each do |k|
119
- result[k] = i_hash[k]
120
- end
121
- end
122
- result.is_a?(Array) && result.length == 1 ? result[0] : result
123
- end
124
-
125
- end
126
-
127
- # Class to represent a parsed grammar object
128
- class ParsedMapItem < MODL::Parser::MODLParserBaseListener
129
- attr_accessor :pair
130
- attr_accessor :mapConditional
131
-
132
- def initialize(global)
133
- @global = global
134
- end
135
-
136
- def enterModl_map_item(ctx)
137
- modl_pair = ctx.modl_pair
138
- unless modl_pair.nil?
139
- @pair = ParsedPair.new @global
140
- modl_pair.enter_rule(@pair)
141
- end
142
- modl_map_conditional = ctx.modl_map_conditional
143
- return if modl_map_conditional.nil?
144
-
145
- @mapConditional = ParsedMapConditional.new @global
146
- modl_map_conditional.enter_rule(@mapConditional)
147
- end
148
-
149
- def extract_hash
150
- return @pair.extract_hash if @pair
151
- return @mapConditional.extract_hash if @mapConditional
152
- end
153
-
154
- end
155
-
156
- # Class to represent a parsed grammar object
157
- class ParsedStructure < MODL::Parser::MODLParserBaseListener
158
- attr_accessor :array
159
- attr_accessor :pair
160
- attr_accessor :top_level_conditional
161
- attr_accessor :map
162
-
163
- def initialize(global)
164
- @global = global
165
- end
166
-
167
- def enterModl_structure(ctx)
168
- modl_pair = ctx.modl_pair
169
- modl_top_level_conditional = ctx.modl_top_level_conditional
170
- modl_map = ctx.modl_map
171
- modl_array = ctx.modl_array
172
-
173
- if !modl_pair.nil?
174
- @pair = ParsedPair.new @global
175
- modl_pair.enter_rule(@pair)
176
- elsif !modl_top_level_conditional.nil?
177
- @top_level_conditional = ParsedTopLevelConditional.new @global
178
- modl_top_level_conditional.enter_rule(@top_level_conditional)
179
- elsif !modl_map.nil?
180
- @map = ParsedMap.new @global
181
- modl_map.enter_rule(@map)
182
- elsif !modl_array.nil?
183
- @array = ParsedArray.new @global
184
- modl_array.enter_rule(@array)
185
- end
186
- end
187
-
188
- def extract_hash
189
- return @array.extract_hash if @array
190
- return @pair.extract_hash if @pair
191
- return @top_level_conditional.extract_hash if @top_level_conditional
192
- return @map.extract_hash if @map
193
- end
194
- end
195
-
196
- # Class to represent a parsed grammar object
197
- class ParsedPair < MODL::Parser::MODLParserBaseListener
198
- attr_accessor :key
199
- attr_accessor :map
200
- attr_accessor :array
201
- attr_accessor :valueItem
202
- attr_accessor :key_lists
203
- attr_accessor :type # A string set to the type of pair that we have found bases on its key
204
- attr_accessor :text # The simple text value rather than the object
205
- attr_accessor :final
206
- attr_accessor :loaded # For *load instructions so we don't load twice
207
-
208
-
209
- def initialize(global)
210
- @global = global
211
- @needs_defref = true
212
- @final = false
213
- @file_importer = FileImporter.instance
214
- @loaded = false
215
- @array = nil
216
- @map = nil
217
- end
218
-
219
- def find_property(key)
220
- return self if key == @key
221
-
222
- return @map.find_property(key) if @map
223
- return @array.find_property(key) if @array
224
- return @valueItem.find_property(key) if @valueItem
225
- end
226
-
227
- # Set the appropriate field base on the value type
228
- def set_value(value)
229
- if value.is_a? Array
230
- @map = nil
231
- @array = ParsedArray.new @global
232
- @array.abstractArrayItems = []
233
- value.each do |item|
234
- array_item = ParsedArrayItem.new @global
235
- array_item.arrayValueItem = ParsedArrayValueItem.new @global
236
- array_item.arrayValueItem.primitive = ParsedPrimitive.new(@global)
237
- array_item.arrayValueItem.primitive.string = ParsedString.new(item)
238
- array_item.arrayValueItem.primitive.text = item
239
- @array.abstractArrayItems << array_item
240
- end
241
- @valueItem = nil
242
- @text = @array.extract_hash
243
- return
244
- elsif value.is_a?(ParsedPair)
245
- @map = value.map ? value.map : nil
246
- @array = value.array ? value.array : nil
247
- @valueItem = value.valueItem ? value.valueItem : nil
248
- return
249
- elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
250
- @map = nil
251
- @array = nil
252
- @valueItem = ParsedValueItem.new @global
253
- @valueItem.value = ParsedValue.new @global
254
- @valueItem.value.primitive = ParsedPrimitive.new(@global)
255
- if value
256
- @valueItem.value.primitive.trueVal = ParsedTrue.instance
257
- else
258
- @valueItem.value.primitive.falseVal = ParsedFalse.instance
259
- end
260
- @valueItem.value.primitive.text = value
261
- @valueItem.value.text = value
262
- @text = value
263
- return
264
- end
265
- value = value.extract_hash unless value.is_a?(String) || value.is_a?(Integer)
266
- @map = nil
267
- @array = nil
268
- @valueItem = ParsedValueItem.new @global
269
- @valueItem.value = ParsedString.new(value)
270
- @text = value
271
- end
272
-
273
- # Convert this object to a simple hash ready for JSON.generate
274
- def extract_hash
275
-
276
- value = @array.extract_hash if @array
277
- value = @valueItem.extract_hash if @valueItem
278
- value = @map.extract_hash if @map
279
-
280
- @text = value
281
-
282
- return if @type == 'index'
283
- return if @type == 'hidden'
284
- return if @type == 'version'
285
- return if @type == 'class'
286
- return if @type == 'method'
287
- return if @type == 'import'
288
- return if @type == 'allow'
289
- return if @type == 'expect'
290
-
291
- formatted_key = Substitutions.process UnicodeEscapes.process @key
292
- {formatted_key => @text}
293
- end
294
-
295
- def enterModl_pair(ctx)
296
- @type = 'pair' # default the type to an ordinary pair
297
-
298
- ctx_string = ctx.STRING
299
- @key = ctx_string.to_s unless ctx_string.nil?
300
- ctx_quoted = ctx.QUOTED
301
- unless ctx_quoted.nil?
302
- @key = ctx_quoted.to_s
303
- @key = Sutil.toptail(@key) # remove the quotes
304
- end
305
-
306
- raise InterpreterError, 'Invalid key - null, true, or false keys are not allowed.' if @key.nil?
307
-
308
- if @key.include?('%') || @key.include?('`')
309
- @key, new_value = RefProcessor.deref @key, @global
310
- unless @key.is_a?(String)
311
- raise InterpreterError, "Error: '" + @key.to_s + "' is an invalid key." if new_value.nil?
312
- @key = new_value.is_a?(String) ? new_value : new_value.text
313
- end
314
- raise InterpreterError, "Error: '" + @key.to_s + "' should de-ref to a string." unless key.is_a?(String)
315
- end
316
-
317
- @final = true if @key.upcase == @key
318
-
319
- modl_array = ctx.modl_array
320
- modl_map = ctx.modl_map
321
- modl_value_item = ctx.modl_value_item
322
-
323
- if !modl_array.nil?
324
- @array = ParsedArray.new @global
325
- modl_array.enter_rule(@array)
326
- elsif !modl_map.nil?
327
- @map = ParsedMap.new @global
328
- modl_map.enter_rule(@map)
329
- elsif !modl_value_item.nil?
330
- @valueItem = ParsedValueItem.new @global
331
- modl_value_item.enter_rule(@valueItem)
332
- end
333
-
334
- set_pair_type
335
- raise InterpreterError, 'Invalid keyword: ' + @key if @type == 'pair' && @key.start_with?('*')
336
-
337
- validate_key if @type == 'pair' || @type == 'hidden'
338
-
339
- # Type-specific processing
340
- case @type
341
- when 'class'
342
- ClassExtractor.extract(self, @global)
343
- when 'id'
344
- extract_value
345
- when 'name'
346
- extract_value
347
- when 'superclass'
348
- extract_value
349
- when 'allow'
350
- extract_value
351
- when 'keylist'
352
- KeylistExtractor.extract(self, @valueItem) if @valueItem
353
- KeylistExtractor.extract(self, @array) if @array
354
- when 'version'
355
- extract_value
356
-
357
- raise InterpreterError, 'Invalid MODL version: ' + @valueItem.value.primitive.text if @valueItem.value.primitive.number.nil?
358
- raise InterpreterError, 'Invalid MODL version: ' + @valueItem.value.primitive.number.num.to_s if @valueItem.value.primitive.number.num.is_a? Float
359
- raise InterpreterError, 'Invalid MODL version: ' + @valueItem.value.primitive.number.num.to_s if @valueItem.value.primitive.number.num.zero?
360
- raise InterpreterError, 'MODL version should be on the first line if specified.' if @global.has_pairs?
361
- @global.syntax_version = @valueItem.value.primitive.number.num
362
-
363
- when 'method'
364
- MethodExtractor.extract(self, @global)
365
- when 'transform'
366
- extract_transform @valueItem
367
- when 'import'
368
- files = @valueItem.extract_hash if @valueItem
369
- files = @array.extract_hash if @array
370
- @file_importer.import_files files, @global unless @global.in_condition?
371
- when 'index'
372
- IndexExtractor.extract(self, @global)
373
- when 'hidden'
374
- extract_value
375
- else
376
- extract_value
377
- end
378
-
379
- return if @global.in_condition? # Don't store pairs in conditionals until we evaluate the conditions
380
-
381
- if @key.start_with? '_'
382
- k = Sutil.tail(@key)
383
- existing = @global.pair(k)
384
- raise InterpreterError, 'Already defined ' + k + ' as final.' if existing&.final && @type != "import"
385
- raise InterpreterError, 'Cannot load multiple files after *LOAD instruction' if existing&.final && @type == "import"
386
-
387
- @global.pair(k, self)
388
- end
389
- existing = @global.pair(@key)
390
- raise InterpreterError, 'Already defined ' + @key + ' as final.' if existing&.final && @type != "import"
391
- raise InterpreterError, 'Cannot load multiple files after *LOAD instruction' if existing&.final && @type == "import"
392
-
393
- @global.pair(@key, self)
394
- end
395
-
396
- private
397
-
398
- def extract_value
399
- item = @valueItem
400
- @text = item.value.text if item.is_a?(ParsedValueItem) && item.value
401
- @text = item.valueItem.value.text if item.is_a?(ParsedPair)
402
- invoke_deref
403
- end
404
-
405
- def extract_transform item
406
- @transform = item.value.primitive.string.string
407
- end
408
-
409
- def validate_key
410
- invalid_chars = "£!$@-+'*#^&"
411
- invalid_chars.each_char do |c|
412
- next unless @key.include?(c)
413
-
414
- raise InterpreterError, 'Invalid key - "' + c + '" character not allowed: ' + @key
415
- end
416
-
417
- key = @key.start_with?('_') ? Sutil.tail(@key) : @key
418
- raise InterpreterError, 'Invalid key - "' + key + '" - entirely numeric keys are not allowed: ' + @key if key == key.to_i.to_s
419
- end
420
-
421
- def invoke_deref
422
- return unless @needs_defref && !@text.nil? && @text.is_a?(String) && @text.include?('%')
423
-
424
- @needs_defref = false
425
- @text, new_value = RefProcessor.deref @text, @global
426
-
427
- if new_value.is_a? ParsedMap
428
- @map = new_value
429
- @valueItem = nil
430
- @array = nil
431
- elsif new_value.is_a? ParsedArray
432
- @array = new_value
433
- @valueItem = nil
434
- @map = nil
435
- elsif new_value.is_a? ParsedValueItem
436
- @valueItem = new_value
437
- elsif new_value.nil?
438
- set_value @text
439
- elsif new_value.is_a? ParsedPair
440
- set_value @text if @text
441
- set_value new_value if @text.nil?
442
- elsif new_value.is_a? String
443
- set_value @text
444
- else
445
- set_value(new_value)
446
- end
447
- end
448
-
449
- # Set the pair type if its a 'special' type
450
- def set_pair_type
451
- @type = 'class' if @key == '*c' || @key == '*class'
452
- if @key == '*C' || @key == '*CLASS'
453
- @type = 'class'
454
- @key = @key.downcase
455
- end
456
- @type = 'id' if @key == '*i' || @key == '*id'
457
- @type = 'name' if @key == '*n' || @key == '*name'
458
- @type = 'name' if @key == '*N' || @key == '*NAME'
459
- @type = 'superclass' if @key == '*S' || @key == '*SUPERCLASS'
460
- @type = 'superclass' if @key == '*s' || @key == '*superclass'
461
- @type = 'keylist' if @key == '*a' || @key == '*assign'
462
- @type = 'version' if @key == '*V' || @key == '*VERSION'
463
- @type = 'method' if @key == '*m' || @key == '*method'
464
- @type = 'transform' if @key == '*t' || @key == '*transform'
465
- if @key == '*L' || @key == '*LOAD'
466
- @key = @key.downcase
467
- @type = 'import'
468
- if @array
469
- @global.freeze_max_files(@array.abstractArrayItems.length)
470
- elsif @valueItem&.value&.array
471
- @global.freeze_max_files(@valueItem&.value&.array.abstractArrayItems.length)
472
- elsif @valueItem&.value&.nbArray
473
- @global.freeze_max_files(@valueItem&.value&.nbArray.arrayItems.length)
474
- else
475
- @global.freeze_max_files(1)
476
- end
477
- end
478
- if @key == '*l' || @key == '*load'
479
- @type = 'import'
480
- end
481
- @type = 'index' if @key == '?'
482
- @type = 'hidden' if @key.start_with? '_'
483
- @type = 'allow' if @key.downcase == '*allow'
484
- end
485
- end
486
-
487
- # Class to represent a parsed grammar object
488
- class ParsedArrayValueItem < MODL::Parser::MODLParserBaseListener
489
- attr_accessor :map
490
- attr_accessor :array
491
- attr_accessor :pair
492
- attr_accessor :primitive
493
- attr_accessor :text # The simple text value rather than the object
494
-
495
- def initialize(global)
496
- @global = global
497
- end
498
-
499
- def find_property(key)
500
- return @map.find_property(key) if @map
501
- return @array.find_property(key) if @array
502
- return @nbArray.find_property(key) if @nbArray
503
- return @pair.find_property(key) if @pair
504
- return @primitive.find_property(key) if @primitive
505
- end
506
-
507
- def extract_hash
508
- return @map.extract_hash if @map
509
- return @array.extract_hash if @array
510
- return @nbArray.extract_hash if @nbArray
511
- return @pair.extract_hash if @pair
512
- return @primitive.extract_hash if @primitive
513
-
514
- @text
515
- end
516
-
517
- def enterModl_array_value_item(ctx)
518
- @text = nil
519
- modl_map = ctx.modl_map
520
- modl_array = ctx.modl_array
521
- modl_pair = ctx.modl_pair
522
- modl_primitive = ctx.modl_primitive
523
-
524
- if !modl_map.nil?
525
- @map = ParsedMap.new @global
526
- modl_map.enter_rule(@map)
527
- elsif !modl_array.nil?
528
- @array = ParsedArray.new @global
529
- modl_array.enter_rule(@array)
530
- elsif !modl_pair.nil?
531
- @pair = ParsedPair.new @global
532
- modl_pair.enter_rule(@pair)
533
- elsif !modl_primitive.nil?
534
- @primitive = ParsedPrimitive.new @global
535
- modl_primitive.enter_rule(@primitive)
536
- @text = @primitive.text
537
- end
538
-
539
- # ignoring comments!
540
- end
541
- end
542
-
543
- # Class to represent a parsed grammar object
544
- class ParsedValueItem < MODL::Parser::MODLParserBaseListener
545
- attr_accessor :value
546
- attr_accessor :valueConditional
547
-
548
- def initialize(global)
549
- @global = global
550
- end
551
-
552
- def find_property(key)
553
- @value.find_property(key) if @value
554
- end
555
-
556
- def enterModl_value_item(ctx)
557
- modl_value_conditional = ctx.modl_value_conditional
558
- unless modl_value_conditional.nil?
559
- @valueConditional = ParsedValueConditional.new @global
560
- modl_value_conditional.enter_rule(@valueConditional)
561
- end
562
- modl_value = ctx.modl_value
563
- return if modl_value.nil?
564
- @value = ParsedValue.new @global
565
- modl_value.enter_rule(@value)
566
- end
567
-
568
- def extract_hash
569
- return @value.extract_hash if @value
570
- return @valueConditional.extract_hash if @valueConditional
571
- end
572
-
573
- end
574
-
575
- # Class to represent a parsed grammar object
576
- class ParsedValue < MODL::Parser::MODLParserBaseListener
577
- attr_accessor :map
578
- attr_accessor :array
579
- attr_accessor :nbArray
580
- attr_accessor :pair
581
- attr_accessor :primitive
582
- attr_accessor :text # The simple text value rather than the object
583
-
584
- def initialize(global)
585
- @global = global
586
- end
587
-
588
- def find_property(key)
589
- return @map.find_property(key) if @map
590
- return @array.find_property(key) if @array
591
- return @nbArray.find_property(key) if @nbArray
592
- return @pair.find_property(key) if @pair
593
- return @primitive.find_property(key) if @primitive
594
- end
595
-
596
- def extract_hash
597
- return @map.extract_hash if @map
598
- return @array.extract_hash if @array
599
- return @nbArray.extract_hash if @nbArray
600
- return @pair.extract_hash if @pair
601
- return @primitive.extract_hash if @primitive
602
-
603
- @text
604
- end
605
-
606
- def evaluate
607
- return @primitive.evaluate if @primitive
608
-
609
- true
610
- end
611
-
612
- def value_obj
613
- return @map if @map
614
- return @array if @array
615
- return @nbArray if @nbArray
616
- return @pair if @pair
617
- return @primitive if @primitive
618
- end
619
-
620
- def enterModl_value(ctx)
621
- modl_map = ctx.modl_map
622
- modl_nb_array = ctx.modl_nb_array
623
- modl_array = ctx.modl_array
624
- modl_pair = ctx.modl_pair
625
- modl_primitive = ctx.modl_primitive
626
-
627
- if !modl_map.nil?
628
- @map = ParsedMap.new @global
629
- modl_map.enter_rule(@map)
630
- elsif !modl_nb_array.nil?
631
- @nbArray = ParsedNbArray.new @global
632
- modl_nb_array.enter_rule(@nbArray)
633
- elsif !modl_array.nil?
634
- @array = ParsedArray.new @global
635
- modl_array.enter_rule(@array)
636
- elsif !modl_pair.nil?
637
- @pair = ParsedPair.new @global
638
- modl_pair.enter_rule(@pair)
639
- elsif !modl_primitive.nil?
640
- @primitive = ParsedPrimitive.new @global
641
- modl_primitive.enter_rule(@primitive)
642
- @text = @primitive.text
643
- end
644
- # ignoring comments!
645
- end
646
- end
647
-
648
- # Class to represent a parsed grammar object
649
- class ParsedPrimitive < MODL::Parser::MODLParserBaseListener
650
- attr_accessor :quoted
651
- attr_accessor :number
652
- attr_accessor :trueVal
653
- attr_accessor :falseVal
654
- attr_accessor :nilVal
655
- attr_accessor :string
656
- attr_accessor :constant
657
- attr_accessor :text # The simple text value rather than the object
658
-
659
- def initialize(global)
660
- @global = global
661
- @constant = false
662
- end
663
-
664
- def find_property(key)
665
- if @string
666
- user_method = @global.user_method(key)
667
- if user_method
668
- return user_method.run(@string.string)
669
- end
670
- StandardMethods.run_method(key, Substitutions.process(UnicodeEscapes.process(@string.string)))
671
- end
672
- end
673
-
674
- def extract_hash
675
- result, _ignore = RefProcessor.deref(@text, @global) unless @constant
676
- result = @text if @constant
677
- Substitutions.process UnicodeEscapes.process result
678
- end
679
-
680
- def evaluate
681
- return false if @nilVal
682
- return false if @falseVal
683
-
684
- true
685
- end
686
-
687
- def value_obj
688
- return @quoted if @quoted
689
- return @number if @number
690
- return @trueVal if @trueVal
691
- return @falseVal if @falseVal
692
- return @nilVal if @nilVal
693
- return @string if @string
694
-
695
- @text
696
- end
697
-
698
- def enterModl_primitive(ctx)
699
- ctx_number = ctx.NUMBER
700
- ctx_string = ctx.STRING
701
- ctx_quoted = ctx.QUOTED
702
- ctx_null = ctx.NULL
703
- ctx_true = ctx.TRUE
704
- ctx_false = ctx.FALSE
705
-
706
- if !ctx_number.nil?
707
- @number = ParsedNumber.new(ctx_number.text)
708
- @text = @number.num
709
- elsif !ctx_string.nil?
710
- @text = ctx_string.text
711
-
712
- @constant = @text.start_with?('`') && !@text.include?('%') && !@text.include?('`.')
713
- @string = ParsedString.new(@text)
714
- @text = @string.string
715
- elsif !ctx_quoted.nil?
716
- @constant = true
717
- @text = Sutil.toptail ctx_quoted.text
718
- @quoted = ParsedQuoted.new(@text)
719
- elsif !ctx_null.nil?
720
- @nilVal = ParsedNull.instance
721
- @text = nil
722
- elsif !ctx_true.nil?
723
- @trueVal = ParsedTrue.instance
724
- @text = true
725
- elsif !ctx_false.nil?
726
- @falseVal = ParsedFalse.instance
727
- @text = false
728
- end
729
- # ignoring comments!
730
- end
731
- end
732
-
733
- # Class to represent a parsed grammar object
734
- class ParsedString
735
- attr_accessor :string
736
-
737
- def initialize(string)
738
- @string = string
739
- end
740
-
741
- def text
742
- @string
743
- end
744
-
745
- def extract_hash
746
- @string = Substitutions.process UnicodeEscapes.process @string
747
- end
748
- end
749
-
750
- # Class to represent a parsed grammar object
751
- class ParsedNumber
752
- attr_accessor :num
753
-
754
- def initialize(string)
755
- @num = string.include?('.') ? string.to_f : string.to_i
756
- end
757
- end
758
-
759
- # Class to represent a parsed grammar object
760
- class ParsedQuoted
761
- attr_accessor :string
762
-
763
- def initialize(string)
764
- @string = string
765
- end
766
- end
767
-
768
- # Class to represent a parsed grammar object
769
- class ParsedConditionTest < MODL::Parser::MODLParserBaseListener
770
- attr_accessor :subConditionList
771
-
772
- def initialize(global)
773
- @global = global
774
- @subConditionList = []
775
- end
776
-
777
- def evaluate
778
- result = false
779
- @subConditionList.each do |s|
780
- last_operator = s.b.a
781
- should_negate = s.b.b
782
-
783
- partial = s.a.evaluate
784
- case last_operator
785
- when '&'
786
- result &= should_negate ? !partial : partial
787
- when '|'
788
- result |= should_negate ? !partial : partial
789
- else
790
- result |= should_negate ? !partial : partial
791
- end
792
- end
793
- result
794
- end
795
-
796
- def enterModl_condition_test(ctx)
797
- ctx_children = ctx.children
798
- unless ctx_children.empty?
799
- last_operator = nil
800
- should_negate = false
801
- ctx_children.each do |child|
802
- if child.is_a? MODLParser::Modl_condition_groupContext
803
- condition_group = ParsedConditionGroup.new @global
804
- child.enter_rule(condition_group)
805
-
806
- p2 = OpenStruct.new
807
- p2.a = last_operator
808
- p2.b = should_negate
809
-
810
- p1 = OpenStruct.new
811
- p1.a = condition_group
812
- p1.b = p2
813
-
814
- @subConditionList << p1
815
-
816
- last_operator = nil
817
- should_negate = false
818
- elsif child.is_a? MODLParser::Modl_conditionContext
819
- condition = ParsedCondition.new @global
820
- child.enter_rule(condition)
821
- p2 = OpenStruct.new
822
- p2.a = last_operator
823
- p2.b = should_negate
824
-
825
- p1 = OpenStruct.new
826
- p1.a = condition
827
- p1.b = p2
828
-
829
- @subConditionList << p1
830
-
831
- last_operator = nil
832
- should_negate = false
833
- else
834
- if child.text == '!'
835
- should_negate = true
836
- else
837
- last_operator = child.text
838
- end
839
- end
840
- end
841
- end
842
- end
843
- end
844
-
845
- # Class to represent a parsed grammar object
846
- class ParsedConditionGroup < MODL::Parser::MODLParserBaseListener
847
- attr_accessor :conditionsTestList
848
-
849
- def initialize(global)
850
- @global = global
851
- @conditionsTestList = []
852
- end
853
-
854
- def evaluate
855
- result = false
856
- @conditionsTestList.each do |s|
857
- partial = s.a.evaluate
858
- result |= partial
859
- end
860
- result
861
- end
862
-
863
- def enterModl_condition_group(ctx)
864
- ctx_children = ctx.children
865
- return if ctx_children.empty?
866
-
867
- last_operator = nil
868
- ctx_children.each do |child|
869
- if child.is_a? MODLParser::Modl_condition_testContext
870
- condition_test = ParsedConditionTest.new @global
871
- child.enter_rule(condition_test)
872
- p = OpenStruct.new
873
- p.a = condition_test
874
- p.b = last_operator
875
- @conditionsTestList << p
876
- last_operator = nil
877
- else
878
- last_operator = child.text if (child.text != '') && (child.text != '}')
879
- end
880
- end
881
- end
882
- end
883
-
884
- # Class to represent a parsed grammar object
885
- class ParsedCondition < MODL::Parser::MODLParserBaseListener
886
- attr_accessor :values
887
- attr_accessor :operator
888
- attr_accessor :text
889
-
890
- def initialize(global)
891
- @global = global
892
- @values = []
893
- end
894
-
895
- def evaluate
896
- Evaluator.evaluate(@global, self)
897
- end
898
-
899
- def enterModl_condition(ctx)
900
- modl_operator = ctx.modl_operator
901
- @operator = modl_operator.text unless modl_operator.nil?
902
- modl_value = ctx.modl_value
903
- modl_value.each do |v|
904
- value = ParsedValue.new @global
905
- v.enter_rule(value)
906
- @values << value
907
- end
908
- ctx_string = ctx.STRING
909
- if !ctx_string.nil?
910
- @text = Parsed.additional_string_processing(ctx_string.text)
911
- @string = ParsedString.new(@text)
912
- @text = @string.string
913
- end
914
- end
915
- end
916
-
917
- # Class to represent a parsed grammar object
918
- class ParsedMapConditionalReturn < MODL::Parser::MODLParserBaseListener
919
- attr_accessor :mapItems
920
-
921
- def initialize(global)
922
- @global = global
923
- @mapItems = []
924
- end
925
-
926
- def extract_hash
927
- @mapItems[0].extract_hash
928
- end
929
-
930
- def enterModl_map_conditional_return(ctx)
931
- modl_map_item = ctx.modl_map_item
932
- return if modl_map_item.empty?
933
-
934
- modl_map_item.each do |mi|
935
- map_item = ParsedMapItem.new @global
936
- mi.enter_rule(map_item)
937
- @mapItems << map_item
938
- end
939
- end
940
- end
941
-
942
- # Class to represent a parsed grammar object
943
- class ParsedMapConditional < MODL::Parser::MODLParserBaseListener
944
- attr_accessor :conditionTests
945
- attr_accessor :mapConditionalReturns
946
-
947
- def initialize(global)
948
- @global = global
949
- @conditionTests = []
950
- @mapConditionalReturns = []
951
- end
952
-
953
- def extract_hash
954
- return @mapConditionalReturns[0].extract_hash if @result
955
-
956
- @mapConditionalReturns[1].extract_hash if @mapConditionalReturns.length > 1
957
- end
958
-
959
- def enterModl_map_conditional(ctx)
960
- i = 0
961
- modl_condition_test = ctx.modl_condition_test
962
- ctx_modl_map_conditional_return = ctx.modl_map_conditional_return
963
-
964
- while i < modl_condition_test.size
965
- condition_test = ParsedConditionTest.new @global
966
- ctx.modl_condition_test_i(i).enter_rule(condition_test)
967
-
968
- conditional_return = ParsedMapConditionalReturn.new @global
969
- ctx.modl_map_conditional_return_i(i).enter_rule(conditional_return)
970
- @conditionTests[i] = condition_test
971
- @mapConditionalReturns[i] = conditional_return
972
-
973
- if ctx_modl_map_conditional_return.size > modl_condition_test.size
974
- i += 1
975
- conditional_return = ParsedMapConditionalReturn.new @global
976
- ctx.modl_map_conditional_return_i(ctx_modl_map_conditional_return.size - 1).enter_rule(conditional_return)
977
- @mapConditionalReturns[i] = conditional_return
978
- end
979
- i += 1
980
- end
981
- @result = @conditionTests[0].evaluate
982
- end
983
- end
984
-
985
- # Class to represent a parsed grammar object
986
- class ParsedTopLevelConditionalReturn < MODL::Parser::MODLParserBaseListener
987
- attr_accessor :structures
988
-
989
- def initialize(global)
990
- @global = global
991
- @structures = []
992
- end
993
-
994
- def extract_hash
995
- return @structures[0].extract_hash if @structures.length == 1
996
-
997
- result = []
998
- @structures.each do |s|
999
- hash = s.extract_hash
1000
- result << hash unless hash.nil?
1001
- end
1002
- return result unless result.length == 1
1003
- return result[0] if result.length == 1
1004
- end
1005
-
1006
- def enterModl_top_level_conditional_return(ctx)
1007
- modl_structure = ctx.modl_structure
1008
- return if modl_structure.empty?
1009
-
1010
- modl_structure.each do |str|
1011
- structure = ParsedStructure.new @global
1012
- str.enter_rule(structure)
1013
- @structures << structure
1014
- end
1015
- end
1016
- end
1017
-
1018
- # Class to represent a parsed grammar object
1019
- class ParsedTopLevelConditional < MODL::Parser::MODLParserBaseListener
1020
- attr_accessor :conditionTests
1021
- attr_accessor :topLevelConditionalReturns
1022
-
1023
- def initialize(global)
1024
- @global = global
1025
- @topLevelConditionalReturns = []
1026
- @conditionTests = []
1027
- @file_importer = FileImporter.instance
1028
- end
1029
-
1030
- def extract_hash
1031
- @conditionTests.each_index do |i|
1032
- next unless @conditionTests[i].evaluate
1033
-
1034
- item = @topLevelConditionalReturns[i]
1035
- if item.structures[0].pair
1036
- key = item.structures[0].pair.key
1037
- key = Sutil.tail(key) if key[0] == '_'
1038
- pair = item.structures[0].pair
1039
- @global.pair(key, pair)
1040
-
1041
- if key.downcase.start_with? '*l'
1042
- files = pair.valueItem.extract_hash if pair.valueItem
1043
- files = pair.array.extract_hash if pair.array
1044
- unless pair.loaded
1045
- @file_importer.import_files files, @global
1046
- pair.loaded = true
1047
- end
1048
- end
1049
- end
1050
- item.structures = OrphanHandler.adopt(@global, item.structures)
1051
- return item.extract_hash
1052
- end
1053
- return unless @topLevelConditionalReturns.length > @conditionTests.length
1054
-
1055
- last_item = @topLevelConditionalReturns[-1]
1056
- if last_item.structures[0].pair
1057
- key = last_item.structures[0].pair.key
1058
- key = Sutil.tail(key) if key[0] == '_'
1059
- pair = last_item.structures[0].pair
1060
- @global.pair(key, pair)
1061
-
1062
- if key.downcase.start_with? '*l'
1063
- files = pair.valueItem.extract_hash if pair.valueItem
1064
- files = pair.array.extract_hash if pair.array
1065
- unless pair.loaded
1066
- @file_importer.import_files files, @global
1067
- pair.loaded = true
1068
- end
1069
- end
1070
- end
1071
- last_item.extract_hash
1072
- end
1073
-
1074
- def enterModl_top_level_conditional(ctx)
1075
- @global.enter_condition
1076
- i = 0
1077
- modl_condition_test = ctx.modl_condition_test
1078
- ctx_modl_top_level_conditional_return = ctx.modl_top_level_conditional_return
1079
-
1080
- while i < modl_condition_test.size
1081
- condition_test = ParsedConditionTest.new @global
1082
- ctx.modl_condition_test_i(i).enter_rule(condition_test)
1083
-
1084
- conditional_return = ParsedTopLevelConditionalReturn.new @global
1085
- ctx.modl_top_level_conditional_return_i(i).enter_rule(conditional_return)
1086
- @conditionTests[i] = condition_test
1087
- @topLevelConditionalReturns[i] = conditional_return
1088
- i += 1
1089
- end
1090
- if ctx_modl_top_level_conditional_return.size > modl_condition_test.size
1091
- conditional_return = ParsedTopLevelConditionalReturn.new @global
1092
- ctx.modl_top_level_conditional_return_i(ctx_modl_top_level_conditional_return.size - 1).enter_rule(conditional_return)
1093
- @topLevelConditionalReturns[i] = conditional_return
1094
- end
1095
- extract_hash
1096
- @global.exit_condition
1097
- end
1098
- end
1099
-
1100
- # Class to represent a parsed grammar object
1101
- class ParsedArrayConditionalReturn < MODL::Parser::MODLParserBaseListener
1102
- attr_accessor :arrayItems
1103
-
1104
- def initialize(global)
1105
- @global = global
1106
- @arrayItems = []
1107
- end
1108
-
1109
- def extract_hash
1110
- @arrayItems[0].arrayValueItem.text
1111
- end
1112
-
1113
- def enterModl_array_conditional_return(ctx)
1114
- modl_array_item = ctx.modl_array_item
1115
- return if modl_array_item.empty?
1116
-
1117
- modl_array_item.each do |ai|
1118
- array_item = ParsedArrayItem.new @global
1119
- ai.enter_rule(array_item)
1120
- @arrayItems << array_item
1121
- end
1122
- end
1123
- end
1124
-
1125
- # Class to represent a parsed grammar object
1126
- class ParsedArrayConditional < MODL::Parser::MODLParserBaseListener
1127
- attr_accessor :conditionTest
1128
- attr_accessor :arrayConditionalReturns
1129
-
1130
- def initialize(global)
1131
- @global = global
1132
- @conditionTests = []
1133
- @arrayConditionalReturns = []
1134
- end
1135
-
1136
- def extract_hash
1137
- result = @conditionTests[0].evaluate
1138
- return @arrayConditionalReturns[0].extract_hash if result
1139
- @arrayConditionalReturns[1].extract_hash
1140
- end
1141
-
1142
- def enterModl_array_conditional(ctx)
1143
- i = 0
1144
- ctx_modl_condition_test = ctx.modl_condition_test
1145
- ctx_modl_array_conditional_return = ctx.modl_array_conditional_return
1146
-
1147
- while i < ctx_modl_condition_test.size
1148
- condition_test = ParsedConditionTest.new @global
1149
- ctx.modl_condition_test_i(i).enter_rule(condition_test)
1150
-
1151
- conditional_return = ParsedArrayConditionalReturn.new @global
1152
- ctx.modl_array_conditional_return_i(i).enter_rule(conditional_return)
1153
- @conditionTests[i] = condition_test
1154
- @arrayConditionalReturns[i] = conditional_return
1155
-
1156
- if ctx_modl_array_conditional_return.size > ctx_modl_condition_test.size
1157
- i += 1
1158
- condition_test = ParsedConditionTest.new @global
1159
- conditional_return = ParsedArrayConditionalReturn.new @global
1160
- ctx.modl_array_conditional_return_i(ctx_modl_array_conditional_return.size - 1).enter_rule(conditional_return)
1161
- @conditionTests[i] = condition_test
1162
- @arrayConditionalReturns[i] = conditional_return
1163
- end
1164
-
1165
-
1166
- i += 1
1167
- end
1168
- end
1169
- end
1170
-
1171
- # Class to represent a parsed grammar object
1172
- class ParsedValueConditionalReturn < MODL::Parser::MODLParserBaseListener
1173
- attr_accessor :valueItems
1174
-
1175
- def initialize(global)
1176
- @global = global
1177
- @valueItems = []
1178
- end
1179
-
1180
- def extract_hash
1181
- return @valueItems[0].value.text if @valueItems[0].value.text
1182
-
1183
- return @valueItems[0].value.extract_hash
1184
- end
1185
-
1186
- def enterModl_value_conditional_return(ctx)
1187
- modl_value_item = ctx.modl_value_item
1188
- return if modl_value_item.empty?
1189
-
1190
- modl_value_item.each do |vi|
1191
- valueItem = ParsedValueItem.new @global
1192
- vi.enter_rule(valueItem)
1193
- @valueItems << valueItem
1194
- end
1195
- end
1196
- end
1197
-
1198
- # Class to represent a parsed grammar object
1199
- class ParsedValueConditional < MODL::Parser::MODLParserBaseListener
1200
- attr_accessor :conditionTests
1201
- attr_accessor :valueConditionalReturns
1202
-
1203
- def initialize(global)
1204
- @global = global
1205
- @conditionTests = []
1206
- @valueConditionalReturns = []
1207
- end
1208
-
1209
- def extract_hash
1210
- return @result if @valueConditionalReturns.length == 0
1211
- return @valueConditionalReturns[0].extract_hash if @result
1212
- return @valueConditionalReturns[1].extract_hash
1213
- end
1214
-
1215
- def enterModl_value_conditional(ctx)
1216
- i = 0
1217
- ctx_modl_condition_test = ctx.modl_condition_test
1218
- ctx_modl_value_conditional_return = ctx.modl_value_conditional_return
1219
-
1220
- while i < ctx_modl_condition_test.size
1221
- condition_test = ParsedConditionTest.new @global
1222
- ctx.modl_condition_test_i(i).enter_rule(condition_test)
1223
-
1224
- @conditionTests[i] = condition_test
1225
-
1226
- break if ctx.modl_value_conditional_return_i(i).nil?
1227
-
1228
- conditional_return = ParsedValueConditionalReturn.new @global
1229
-
1230
- ctx.modl_value_conditional_return_i(i).enter_rule(conditional_return)
1231
-
1232
- @valueConditionalReturns[i] = conditional_return
1233
-
1234
- if ctx_modl_value_conditional_return.size > ctx_modl_condition_test.size
1235
- condition_test = ParsedConditionTest.new @global
1236
- conditional_return = ParsedValueConditionalReturn.new @global
1237
- ctx.modl_value_conditional_return_i(ctx_modl_value_conditional_return.size - 1).enter_rule(conditional_return)
1238
- @conditionTests[i + 1] = condition_test
1239
- @valueConditionalReturns[i + 1] = conditional_return
1240
- end
1241
-
1242
- i += 1
1243
- end
1244
- @result = @conditionTests[0].evaluate
1245
- end
1246
- end
1247
-
1248
- # Class to represent a parsed grammar object
1249
- class ParsedNbArray < MODL::Parser::MODLParserBaseListener
1250
- attr_accessor :arrayItems
1251
-
1252
- def initialize(global)
1253
- @global = global
1254
- @arrayItems = []
1255
- end
1256
-
1257
- def find_property(key)
1258
- if key.is_a? Integer
1259
- return @arrayItems[key].arrayValueItem
1260
- else
1261
- @arrayItems.each do |mi|
1262
- return mi.arrayValueItem.pair if mi.arrayValueItem.pair && mi.arrayValueItem.pair.key == key
1263
- end
1264
- nil
1265
- end
1266
- end
1267
-
1268
- def extract_hash
1269
- result = []
1270
-
1271
- @arrayItems.each do |i|
1272
- result << i.extract_hash
1273
- end
1274
-
1275
- result
1276
- end
1277
-
1278
- def enterModl_nb_array(ctx)
1279
- i = 0
1280
- previous = nil
1281
- ctx_children = ctx.children
1282
- ctx_children.each do |pt|
1283
- if pt.is_a? MODLParser::Modl_array_itemContext
1284
- array_item = ParsedArrayItem.new @global
1285
- pt.enter_rule(array_item)
1286
- @arrayItems[i] = array_item
1287
- i += 1
1288
- elsif pt.is_a? Antlr4::Runtime::TerminalNode
1289
- if !previous.nil? && previous.is_a?(Antlr4::Runtime::TerminalNode) && pt.is_a?(Antlr4::Runtime::TerminalNode)
1290
- # If we get here then we have two terminal nodes in a row, so we need to output something unless # the terminal symbols are newlines
1291
- #
1292
- prev_symbol = previous.symbol.type
1293
- current_symbol = pt.symbol.type
1294
-
1295
- if prev_symbol == MODLLexer::COLON && current_symbol == MODLLexer::COLON
1296
- array_item = Parsed.handle_empty_array_item
1297
- @arrayItems[i] = array_item
1298
- i += 1
1299
- end
1300
- end
1301
- end
1302
- previous = pt
1303
- end
1304
- end
1305
- end
1306
-
1307
- def self.handle_empty_array_item
1308
- # Create something for the blank array item
1309
- #
1310
- # The problem is that we might not have any context to tell us what type we need to create
1311
- # so this currently defaults to the nil value
1312
- #
1313
- # TODO : Is there a way to know the type to create or is nil always acceptable?
1314
- array_item = ParsedArrayItem.new @global
1315
- array_item.arrayValueItem = ParsedArrayValueItem.new @global
1316
- array_item.arrayValueItem.primitive = ParsedPrimitive.new @global
1317
- array_item.arrayValueItem.primitive.nilVal = ParsedNull.instance
1318
- array_item
1319
- end
1320
-
1321
- # Class to represent a parsed grammar object
1322
- class ParsedArray < MODL::Parser::MODLParserBaseListener
1323
- # We now have a list of < array_item | nb_array >
1324
- attr_accessor :abstractArrayItems
1325
-
1326
- def initialize(global)
1327
- @global = global
1328
- @abstractArrayItems = []
1329
- end
1330
-
1331
- def find_property(key)
1332
- if key.is_a? Integer
1333
- return @abstractArrayItems[key]
1334
- else
1335
- @abstractArrayItems.each do |mi|
1336
- return mi.arrayValueItem.pair if mi.arrayValueItem.pair && mi.arrayValueItem.pair.key == key
1337
- end
1338
- nil
1339
- end
1340
- end
1341
-
1342
- def extract_hash
1343
- result = []
1344
-
1345
- abstractArrayItems.each do |i|
1346
- result << i.extract_hash
1347
- end
1348
-
1349
- result
1350
- end
1351
-
1352
- def enterModl_array(ctx)
1353
- # Create the new abstractArrayItems list first, sized to the total of array_item.size and nb_array.size
1354
- i = 0
1355
- previous = nil
1356
- ctx_children = ctx.children
1357
- ctx_children.each do |pt|
1358
- if pt.is_a? MODLParser::Modl_array_itemContext
1359
- array_item = ParsedArrayItem.new @global
1360
- pt.enter_rule(array_item)
1361
- @abstractArrayItems[i] = array_item
1362
- i += 1
1363
- elsif pt.is_a? MODLParser::Modl_nb_arrayContext
1364
- nb_array = ParsedNbArray.new @global
1365
- pt.enter_rule(nb_array)
1366
- @abstractArrayItems[i] = nb_array
1367
- i += 1
1368
- elsif pt.is_a? Antlr4::Runtime::TerminalNode
1369
- if !previous.nil? && previous.is_a?(Antlr4::Runtime::TerminalNode) && pt.is_a?(Antlr4::Runtime::TerminalNode)
1370
-
1371
- # If we get here then we have two terminal nodes in a row, so we need to output something unless # the terminal symbols are newlines
1372
- #
1373
- prev_symbol = previous.symbol.type
1374
- current_symbol = pt.symbol.type
1375
-
1376
- if prev_symbol == MODLLexer::LSBRAC && current_symbol == MODLLexer::RSBRAC
1377
- next # This allows empty arrays
1378
- end
1379
-
1380
- if prev_symbol == MODLLexer::STRUCT_SEP && current_symbol == MODLLexer::STRUCT_SEP
1381
-
1382
- # Create something for the blank array item
1383
- #
1384
- # The problem is that we might not have any context to tell us what type we need to create
1385
- # so this currently defaults to the nil
1386
- #
1387
- # TODO : Is there a way to know the type to create or is nil always acceptable?
1388
- array_item = Parsed.handle_empty_array_item
1389
-
1390
- @abstractArrayItems[i] = array_item
1391
- i += 1
1392
- end
1393
- end
1394
- end
1395
- previous = pt
1396
- end
1397
- end
1398
- end
1399
-
1400
- # Class to represent a parsed grammar object
1401
- class ParsedArrayItem < MODL::Parser::MODLParserBaseListener
1402
- attr_accessor :arrayValueItem
1403
- attr_accessor :arrayConditional
1404
-
1405
- def initialize(global)
1406
- @global = global
1407
- end
1408
-
1409
- def find_property(key)
1410
- return @arrayValueItem.find_property(key)
1411
- end
1412
-
1413
- def enterModl_array_item(ctx)
1414
- ctx_modl_array_conditional = ctx.modl_array_conditional
1415
- unless ctx_modl_array_conditional.nil?
1416
- @arrayConditional = ParsedArrayConditional.new @global
1417
- ctx_modl_array_conditional.enter_rule(@arrayConditional)
1418
- end
1419
- ctx_modl_array_value_item = ctx.modl_array_value_item
1420
- unless ctx_modl_array_value_item.nil?
1421
- @arrayValueItem = ParsedArrayValueItem.new @global
1422
- ctx_modl_array_value_item.enter_rule(@arrayValueItem)
1423
- end
1424
- end
1425
-
1426
- def extract_hash
1427
- return @arrayValueItem.extract_hash if @arrayValueItem
1428
- return @arrayConditional.extract_hash if @arrayConditional
1429
- end
1430
-
1431
- end
1432
-
1433
- # Singleton class to represent a true value
1434
- class ParsedTrue < MODL::Parser::MODLParserBaseListener
1435
- include Singleton
1436
- end
1437
-
1438
- # Singleton class to represent a false value
1439
- class ParsedFalse < MODL::Parser::MODLParserBaseListener
1440
- include Singleton
1441
- end
1442
-
1443
- # Singleton class to represent a null value
1444
- class ParsedNull < MODL::Parser::MODLParserBaseListener
1445
- include Singleton
1446
- end
1447
-
1448
- # Convert the parse tree to a simpler structure suitable for JSON.generate.
1449
- def extract_hash
1450
- result = []
1451
- if @structures.length.positive?
1452
- @structures.each do |s|
1453
- value = s.extract_hash
1454
- result << value unless value.nil?
1455
- end
1456
- else
1457
- result = {}
1458
- end
1459
- case result.length
1460
- when 0
1461
- return nil
1462
- when 1
1463
- return result[0]
1464
- end
1465
- result
1466
- end
1467
- end
1468
- end
1469
- end