modl 0.0.2 → 0.3.0

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