modl 0.0.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,41 @@
1
+ WS=1
2
+ NULL=2
3
+ TRUE=3
4
+ FALSE=4
5
+ COLON=5
6
+ EQUALS=6
7
+ STRUCT_SEP=7
8
+ ARR_SEP=8
9
+ LBRAC=9
10
+ RBRAC=10
11
+ LSBRAC=11
12
+ RSBRAC=12
13
+ NUMBER=13
14
+ COMMENT=14
15
+ STRING=15
16
+ HASH_PREFIX=16
17
+ QUOTED=17
18
+ GRAVED=18
19
+ LCBRAC=19
20
+ CWS=20
21
+ QMARK=21
22
+ FSLASH=22
23
+ GTHAN=23
24
+ LTHAN=24
25
+ ASTERISK=25
26
+ AMP=26
27
+ PIPE=27
28
+ EXCLAM=28
29
+ CCOMMENT=29
30
+ RCBRAC=30
31
+ ','=8
32
+ '{'=19
33
+ '?'=21
34
+ '/'=22
35
+ '>'=23
36
+ '<'=24
37
+ '*'=25
38
+ '&'=26
39
+ '|'=27
40
+ '!'=28
41
+ '}'=30
@@ -0,0 +1,164 @@
1
+ # Generated from MODLParser.g4 by ANTLR 4.7.2
2
+ require 'antlr4/runtime'
3
+
4
+ module Modl::Parser
5
+
6
+ class MODLParserBaseListener < MODLParserListener
7
+ def enterModl(ctx)
8
+ end
9
+
10
+ def exitModl(ctx)
11
+ end
12
+
13
+ def enterModl_structure(ctx)
14
+ end
15
+
16
+ def exitModl_structure(ctx)
17
+ end
18
+
19
+ def enterModl_map(ctx)
20
+ end
21
+
22
+ def exitModl_map(ctx)
23
+ end
24
+
25
+ def enterModl_array(ctx)
26
+ end
27
+
28
+ def exitModl_array(ctx)
29
+ end
30
+
31
+ def enterModl_nb_array(ctx)
32
+ end
33
+
34
+ def exitModl_nb_array(ctx)
35
+ end
36
+
37
+ def enterModl_pair(ctx)
38
+ end
39
+
40
+ def exitModl_pair(ctx)
41
+ end
42
+
43
+ def enterModl_value_item(ctx)
44
+ end
45
+
46
+ def exitModl_value_item(ctx)
47
+ end
48
+
49
+ def enterModl_top_level_conditional(ctx)
50
+ end
51
+
52
+ def exitModl_top_level_conditional(ctx)
53
+ end
54
+
55
+ def enterModl_top_level_conditional_return(ctx)
56
+ end
57
+
58
+ def exitModl_top_level_conditional_return(ctx)
59
+ end
60
+
61
+ def enterModl_map_conditional(ctx)
62
+ end
63
+
64
+ def exitModl_map_conditional(ctx)
65
+ end
66
+
67
+ def enterModl_map_conditional_return(ctx)
68
+ end
69
+
70
+ def exitModl_map_conditional_return(ctx)
71
+ end
72
+
73
+ def enterModl_map_item(ctx)
74
+ end
75
+
76
+ def exitModl_map_item(ctx)
77
+ end
78
+
79
+ def enterModl_array_conditional(ctx)
80
+ end
81
+
82
+ def exitModl_array_conditional(ctx)
83
+ end
84
+
85
+ def enterModl_array_conditional_return(ctx)
86
+ end
87
+
88
+ def exitModl_array_conditional_return(ctx)
89
+ end
90
+
91
+ def enterModl_array_item(ctx)
92
+ end
93
+
94
+ def exitModl_array_item(ctx)
95
+ end
96
+
97
+ def enterModl_value_conditional(ctx)
98
+ end
99
+
100
+ def exitModl_value_conditional(ctx)
101
+ end
102
+
103
+ def enterModl_value_conditional_return(ctx)
104
+ end
105
+
106
+ def exitModl_value_conditional_return(ctx)
107
+ end
108
+
109
+ def enterModl_condition_test(ctx)
110
+ end
111
+
112
+ def exitModl_condition_test(ctx)
113
+ end
114
+
115
+ def enterModl_operator(ctx)
116
+ end
117
+
118
+ def exitModl_operator(ctx)
119
+ end
120
+
121
+ def enterModl_condition(ctx)
122
+ end
123
+
124
+ def exitModl_condition(ctx)
125
+ end
126
+
127
+ def enterModl_condition_group(ctx)
128
+ end
129
+
130
+ def exitModl_condition_group(ctx)
131
+ end
132
+
133
+ def enterModl_value(ctx)
134
+ end
135
+
136
+ def exitModl_value(ctx)
137
+ end
138
+
139
+ def enterModl_array_value_item(ctx)
140
+ end
141
+
142
+ def exitModl_array_value_item(ctx)
143
+ end
144
+
145
+ def enterModl_primitive(ctx)
146
+ end
147
+
148
+ def exitModl_primitive(ctx)
149
+ end
150
+
151
+ def enter_every_rule(ctx)
152
+ end
153
+
154
+ def exit_every_rule(ctx)
155
+ end
156
+
157
+ def visit_terminal(node)
158
+ end
159
+
160
+ def visit_error_node(node)
161
+ end
162
+
163
+ end
164
+ end
@@ -0,0 +1,107 @@
1
+ # Generated from MODLParser.g4 by ANTLR 4.7.2
2
+
3
+ require 'antlr4/runtime'
4
+
5
+
6
+ module Modl::Parser
7
+
8
+ class MODLParserBaseVisitor < Antlr4::Runtime::AbstractParseTreeVisitor
9
+ def visitModl( ctx)
10
+ return visit_children(ctx)
11
+ end
12
+
13
+ def visitModl_structure( ctx)
14
+ return visit_children(ctx)
15
+ end
16
+
17
+ def visitModl_map( ctx)
18
+ return visit_children(ctx)
19
+ end
20
+
21
+ def visitModl_array( ctx)
22
+ return visit_children(ctx)
23
+ end
24
+
25
+ def visitModl_nb_array( ctx)
26
+ return visit_children(ctx)
27
+ end
28
+
29
+ def visitModl_pair( ctx)
30
+ return visit_children(ctx)
31
+ end
32
+
33
+ def visitModl_value_item( ctx)
34
+ return visit_children(ctx)
35
+ end
36
+
37
+ def visitModl_top_level_conditional( ctx)
38
+ return visit_children(ctx)
39
+ end
40
+
41
+ def visitModl_top_level_conditional_return( ctx)
42
+ return visit_children(ctx)
43
+ end
44
+
45
+ def visitModl_map_conditional( ctx)
46
+ return visit_children(ctx)
47
+ end
48
+
49
+ def visitModl_map_conditional_return( ctx)
50
+ return visit_children(ctx)
51
+ end
52
+
53
+ def visitModl_map_item( ctx)
54
+ return visit_children(ctx)
55
+ end
56
+
57
+ def visitModl_array_conditional( ctx)
58
+ return visit_children(ctx)
59
+ end
60
+
61
+ def visitModl_array_conditional_return( ctx)
62
+ return visit_children(ctx)
63
+ end
64
+
65
+ def visitModl_array_item( ctx)
66
+ return visit_children(ctx)
67
+ end
68
+
69
+ def visitModl_value_conditional( ctx)
70
+ return visit_children(ctx)
71
+ end
72
+
73
+ def visitModl_value_conditional_return( ctx)
74
+ return visit_children(ctx)
75
+ end
76
+
77
+ def visitModl_condition_test( ctx)
78
+ return visit_children(ctx)
79
+ end
80
+
81
+ def visitModl_operator( ctx)
82
+ return visit_children(ctx)
83
+ end
84
+
85
+ def visitModl_condition( ctx)
86
+ return visit_children(ctx)
87
+ end
88
+
89
+ def visitModl_condition_group( ctx)
90
+ return visit_children(ctx)
91
+ end
92
+
93
+ def visitModl_value( ctx)
94
+ return visit_children(ctx)
95
+ end
96
+
97
+ def visitModl_array_value_item( ctx)
98
+ return visit_children(ctx)
99
+ end
100
+
101
+ def visitModl_primitive( ctx)
102
+ return visit_children(ctx)
103
+ end
104
+
105
+
106
+ end
107
+ end
@@ -0,0 +1,151 @@
1
+ # Generated from MODLParser.g4 by ANTLR 4.7.2
2
+ module Modl::Parser
3
+ require 'antlr4/runtime'
4
+
5
+ class MODLParserListener < Antlr4::Runtime::ParseTreeListener
6
+ def enterModl(ctx)
7
+ end
8
+
9
+ def exitModl(ctx)
10
+ end
11
+
12
+ def enterModl_structure(ctx)
13
+ end
14
+
15
+ def exitModl_structure(ctx)
16
+ end
17
+
18
+ def enterModl_map(ctx)
19
+ end
20
+
21
+ def exitModl_map(ctx)
22
+ end
23
+
24
+ def enterModl_array(ctx)
25
+ end
26
+
27
+ def exitModl_array(ctx)
28
+ end
29
+
30
+ def enterModl_nb_array(ctx)
31
+ end
32
+
33
+ def exitModl_nb_array(ctx)
34
+ end
35
+
36
+ def enterModl_pair(ctx)
37
+ end
38
+
39
+ def exitModl_pair(ctx)
40
+ end
41
+
42
+ def enterModl_value_item(ctx)
43
+ end
44
+
45
+ def exitModl_value_item(ctx)
46
+ end
47
+
48
+ def enterModl_top_level_conditional(ctx)
49
+ end
50
+
51
+ def exitModl_top_level_conditional(ctx)
52
+ end
53
+
54
+ def enterModl_top_level_conditional_return(ctx)
55
+ end
56
+
57
+ def exitModl_top_level_conditional_return(ctx)
58
+ end
59
+
60
+ def enterModl_map_conditional(ctx)
61
+ end
62
+
63
+ def exitModl_map_conditional(ctx)
64
+ end
65
+
66
+ def enterModl_map_conditional_return(ctx)
67
+ end
68
+
69
+ def exitModl_map_conditional_return(ctx)
70
+ end
71
+
72
+ def enterModl_map_item(ctx)
73
+ end
74
+
75
+ def exitModl_map_item(ctx)
76
+ end
77
+
78
+ def enterModl_array_conditional(ctx)
79
+ end
80
+
81
+ def exitModl_array_conditional(ctx)
82
+ end
83
+
84
+ def enterModl_array_conditional_return(ctx)
85
+ end
86
+
87
+ def exitModl_array_conditional_return(ctx)
88
+ end
89
+
90
+ def enterModl_array_item(ctx)
91
+ end
92
+
93
+ def exitModl_array_item(ctx)
94
+ end
95
+
96
+ def enterModl_value_conditional(ctx)
97
+ end
98
+
99
+ def exitModl_value_conditional(ctx)
100
+ end
101
+
102
+ def enterModl_value_conditional_return(ctx)
103
+ end
104
+
105
+ def exitModl_value_conditional_return(ctx)
106
+ end
107
+
108
+ def enterModl_condition_test(ctx)
109
+ end
110
+
111
+ def exitModl_condition_test(ctx)
112
+ end
113
+
114
+ def enterModl_operator(ctx)
115
+ end
116
+
117
+ def exitModl_operator(ctx)
118
+ end
119
+
120
+ def enterModl_condition(ctx)
121
+ end
122
+
123
+ def exitModl_condition(ctx)
124
+ end
125
+
126
+ def enterModl_condition_group(ctx)
127
+ end
128
+
129
+ def exitModl_condition_group(ctx)
130
+ end
131
+
132
+ def enterModl_value(ctx)
133
+ end
134
+
135
+ def exitModl_value(ctx)
136
+ end
137
+
138
+ def enterModl_array_value_item(ctx)
139
+ end
140
+
141
+ def exitModl_array_value_item(ctx)
142
+ end
143
+
144
+ def enterModl_primitive(ctx)
145
+ end
146
+
147
+ def exitModl_primitive(ctx)
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,56 @@
1
+ # Generated from MODLParser.g4 by ANTLR 4.7.2
2
+ require 'antlr4/runtime'
3
+
4
+ module Modl::Parser
5
+
6
+ class MODLParserVisitor < Antlr4::Runtime::ParseTreeVisitor
7
+ def visitModl(ctx)
8
+ end
9
+ def visitModl_structure(ctx)
10
+ end
11
+ def visitModl_map(ctx)
12
+ end
13
+ def visitModl_array(ctx)
14
+ end
15
+ def visitModl_nb_array(ctx)
16
+ end
17
+ def visitModl_pair(ctx)
18
+ end
19
+ def visitModl_value_item(ctx)
20
+ end
21
+ def visitModl_top_level_conditional(ctx)
22
+ end
23
+ def visitModl_top_level_conditional_return(ctx)
24
+ end
25
+ def visitModl_map_conditional(ctx)
26
+ end
27
+ def visitModl_map_conditional_return(ctx)
28
+ end
29
+ def visitModl_map_item(ctx)
30
+ end
31
+ def visitModl_array_conditional(ctx)
32
+ end
33
+ def visitModl_array_conditional_return(ctx)
34
+ end
35
+ def visitModl_array_item(ctx)
36
+ end
37
+ def visitModl_value_conditional(ctx)
38
+ end
39
+ def visitModl_value_conditional_return(ctx)
40
+ end
41
+ def visitModl_condition_test(ctx)
42
+ end
43
+ def visitModl_operator(ctx)
44
+ end
45
+ def visitModl_condition(ctx)
46
+ end
47
+ def visitModl_condition_group(ctx)
48
+ end
49
+ def visitModl_value(ctx)
50
+ end
51
+ def visitModl_array_value_item(ctx)
52
+ end
53
+ def visitModl_primitive(ctx)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,159 @@
1
+ module Modl
2
+ module Parser
3
+ # This class handles the conversion of objects that refer to classes into instances of those classes.
4
+ # It works recursively since class usage can be nested.
5
+ class ClassProcessor
6
+ # How deep can the class structure be?
7
+ MAX_RECURSION_DEPTH = 20
8
+ # global is a GlobalParseContext and obj is the extracted Array or Hash from Modl::Parser::Parsed.extract_json
9
+ def self.process(global, obj)
10
+ # Process each object in the array or just process the object if its a hash.
11
+ # Any other object is ignored.
12
+ raise StandardError, 'parameter "global" should be a GlobalParseContext' unless global.is_a?(GlobalParseContext)
13
+
14
+ if obj.is_a? Array
15
+ obj.each do |o|
16
+ process_obj global, o if o.is_a? Hash
17
+ end
18
+ elsif obj.is_a? Hash
19
+ process_obj global, obj
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # Process the contents of the supplied hash obj
26
+ def self.process_obj(global, obj)
27
+ obj.keys.each do |k|
28
+ value = obj[k]
29
+ # Does the key refer to a class that we have parsed or loaded?
30
+ clazz = global.classs(k)
31
+ if clazz
32
+ # Yes so convert this value to an instance of that class
33
+ new_k, new_v = process_class global, k, value
34
+ # Replace the existing object with the new class instance and a new key
35
+ obj.delete k
36
+ obj[new_k] = new_v
37
+ end
38
+ # Recurse into the value in case it has contents that also refer to classes.
39
+ process global, value
40
+ end
41
+ end
42
+
43
+ # Convert the supplied object val into an instance of the class with key k
44
+ def self.process_class(global, k, v)
45
+ clazz = global.classs(k)
46
+ new_value = transform_to_class(clazz, global, v)
47
+
48
+ if v.is_a?(Array)
49
+ new_value = v if new_value.empty?
50
+ else
51
+ # Check the top class and do some type-specific processing
52
+ tc = top_class(clazz, global)
53
+ if tc == 'str'
54
+ new_value = v.to_s
55
+ elsif tc == 'num' && !v.is_a?(Numeric)
56
+ raise InterpreterError, 'Superclass of "' + clazz.id + '" is num - cannot assign String value "' + v.to_s + '"'
57
+ elsif tc == 'map'
58
+ if v.is_a? Hash
59
+ # Bring down values from the superclass hierarchy
60
+ new_value = copy_from_superclasses(clazz, global, new_value, v)
61
+ else
62
+ new_value[k] = v
63
+ end
64
+ else
65
+ new_value = v
66
+ end
67
+ end
68
+
69
+ process_nested_classes(global, new_value)
70
+ [clazz.name_or_id, new_value]
71
+ end
72
+
73
+ # Bring down values from the superclass hierarchy
74
+ def self.copy_from_superclasses(clazz, global, new_value, v)
75
+ new_value = v.merge(new_value)
76
+ depth = 0
77
+ loop do
78
+ clazz = global.classs(clazz.superclass)
79
+ break if clazz.nil? || depth > MAX_RECURSION_DEPTH
80
+
81
+ clazz.merge_content(new_value)
82
+ depth += 1
83
+ end
84
+ new_value
85
+ end
86
+
87
+ # Transfer the keys from val to the new_value object.
88
+ def self.copy_keys_to_new_value(new_value, val)
89
+ val.each do |value|
90
+ next unless value&.is_a?(Hash)
91
+
92
+ new_value.merge!(value)
93
+ end
94
+ end
95
+
96
+ # If the new_value has nested class references then process those recursively as well.
97
+ def self.process_nested_classes(global, new_value)
98
+ return unless new_value.is_a? Hash
99
+
100
+ new_value.keys.each do |nk|
101
+ clazz = global.classs(nk)
102
+ nv = new_value[nk]
103
+ next unless clazz # skip it if it doesn't refer to a class
104
+
105
+ if !nv.nil? && !nv.is_a?(String) && !nv.is_a?(Numeric)
106
+ new_k, new_v = process_class global, nk, nv
107
+ else
108
+ new_k = clazz.name_or_id
109
+ new_v = nv
110
+ end
111
+
112
+ # Replace the value for this key if we've changed anything.
113
+ if new_value[new_k] != new_v
114
+ new_value.delete nk
115
+ new_value[new_k] = new_v
116
+ end
117
+ end
118
+ end
119
+
120
+ # Process the *assign lists ('keylist' in this code) and any extra pairs defined by the class.
121
+ # The id, name, and superclass can be ignored here.
122
+ def self.transform_to_class(clazz, global, v)
123
+ new_value = {} # the replacement for val after conversion to a class instance
124
+
125
+ # Process the key list if we found one otherwise raise an error
126
+ # Slightly different processing for hashes and arrays
127
+ if v.is_a? Hash
128
+ keys = clazz.keylist_of_length(v.length)
129
+ lam = ->(i) {v[v.keys[i]]}
130
+ elsif v.is_a? Array
131
+ keys = clazz.keylist_of_length(v.length)
132
+ lam = ->(i) {v[i]}
133
+ end
134
+
135
+ keys&.each_index do |i|
136
+ new_value[keys[i]] = lam.call(i)
137
+ end
138
+
139
+ new_value.keys do |nk|
140
+ process_obj global, new_value[nk]
141
+ end
142
+
143
+ clazz.merge_content(new_value)
144
+ end
145
+
146
+ # Get the top level class for the supplied class
147
+ def self.top_class(clazz, global, depth = 0)
148
+ # Check for self-referential classes that cause infinite recursion
149
+ return if depth > MAX_RECURSION_DEPTH
150
+
151
+ superclass = clazz.superclass
152
+ c = global.classs(superclass)
153
+ return top_class(c, global, depth + 1) if c
154
+
155
+ superclass
156
+ end
157
+ end
158
+ end
159
+ end