modl 0.3.26 → 0.3.28

Sign up to get free protection for your applications and to get access to all the features.
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 +280 -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