modl 0.3.15 → 0.3.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/grammar_tests/base_tests.json +10 -1
- data/lib/modl/parser/array_processor.rb +120 -0
- data/lib/modl/parser/class_processor.rb +3 -3
- data/lib/modl/parser/file_importer.rb +1 -1
- data/lib/modl/parser/global_parse_context.rb +18 -0
- data/lib/modl/parser/interpreter.rb +2 -0
- data/lib/modl/parser/modl_array.rb +84 -0
- data/lib/modl/parser/parsed.rb +8 -0
- data/lib/modl/parser/ref_processor.rb +0 -1
- data/lib/modl/parser/version.rb +1 -1
- data/modl.gemspec +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de1a3cee98476becc60a2c7dab825c65c30b47e5e8b017ae30c23b8505937e41
|
4
|
+
data.tar.gz: 85150763932b4136124d120017881baaf34c528cc989709ecc139eaf563bc868
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0276b480cfe85fa983b0deac3ca030f5a79d25e6676c781cf3ca8868162adfb30d837d29af4d26d935e41888819b57b9f83141d41d0311dd2fb8ab634e2799c5
|
7
|
+
data.tar.gz: d2e5547d0fd8225d3739d8d2c3754f40b7b77f0df6f5d415a42201f5dd696beb8f1d25e33c2ee18b635efc15bdc268df20ad97c7188c742825d498234312314f
|
data/CHANGELOG.md
CHANGED
@@ -1667,7 +1667,7 @@
|
|
1667
1667
|
},
|
1668
1668
|
{
|
1669
1669
|
"id": "176",
|
1670
|
-
"input": "_var=2;\n*L=\"http://
|
1670
|
+
"input": "_var=2;\n*L=\"http://modl.uk/tests/testing.txt!\";\nprint=%update_date\n",
|
1671
1671
|
"expected_output": "{\n \"print\": \"20180921 08:20 2\"\n}",
|
1672
1672
|
"tested_features": [
|
1673
1673
|
"object_ref",
|
@@ -3011,5 +3011,14 @@
|
|
3011
3011
|
"escape"
|
3012
3012
|
],
|
3013
3013
|
"minimised_modl": "_letters=abc;key=\"\\%letters\""
|
3014
|
+
},
|
3015
|
+
{
|
3016
|
+
"id": "316",
|
3017
|
+
"input": "*array(\n *id=p;\n *name=people;\n *of=person\n );\n\n *class(\n *id=n;\n *name=name\n );\n\n *class(\n *id=a;\n *name=age\n );\n\n *class(\n *id=person;\n *assign=[\n [n;a]\n ]\n );\n\n data(\n p[[John;18];[Jane;20]];\n person=[Fred;21]\n );\n p[[Mary;18];[Mungo;19];[Midge;20]];\n person=[Rod;23]\n",
|
3018
|
+
"expected_output": "{\n \"data\": {\n \"people\": [\n {\n \"name\": \"John\",\n \"age\": 18\n },\n {\n \"name\": \"Jane\",\n \"age\": 20\n }\n ],\n \"person\": {\n \"name\": \"Fred\",\n \"age\": 21\n }\n },\n \"people\": [\n {\n \"name\": \"Mary\",\n \"age\": 18\n },\n {\n \"name\": \"Mungo\",\n \"age\": 19\n },\n {\n \"name\": \"Midge\",\n \"age\": 20\n }\n ],\n \"person\": {\n \"name\": \"Rod\",\n \"age\": 23\n }\n}",
|
3019
|
+
"tested_features": [
|
3020
|
+
"*arrays"
|
3021
|
+
],
|
3022
|
+
"minimised_modl": "*array(*id=p;*name=people;*of=person);*class(*id=n;*name=name);*class(*id=a;*name=age);*class(*id=person;*assign=[[n;a]]);data(p[[John;18];[Jane;20]];person=[Fred;21]);p[[Mary;18];[Mungo;19];[Midge;20]];person=[Rod;23]"
|
3014
3023
|
}
|
3015
3024
|
]
|
@@ -0,0 +1,120 @@
|
|
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
|
+
module MODL
|
26
|
+
module Parser
|
27
|
+
# This class handles the conversion of objects that refer to arrays into instances of those arrays.
|
28
|
+
# It works recursively since array usage can be nested.
|
29
|
+
class ArrayProcessor
|
30
|
+
# How deep can the class structure be?
|
31
|
+
MAX_RECURSION_DEPTH = 50
|
32
|
+
# global is a GlobalParseContext and obj is the extracted Array or Hash from MODL::Parser::Parsed.extract_json
|
33
|
+
def self.process(global, obj)
|
34
|
+
# Process each object in the array or just process the object if its a hash.
|
35
|
+
# Any other object is ignored.
|
36
|
+
raise StandardError, 'parameter "global" should be a GlobalParseContext' unless global.is_a?(GlobalParseContext)
|
37
|
+
|
38
|
+
if obj.is_a? Array
|
39
|
+
obj.each do |o|
|
40
|
+
process_obj global, o if o.is_a? Hash
|
41
|
+
end
|
42
|
+
elsif obj.is_a? Hash
|
43
|
+
process_obj global, obj
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Process the contents of the supplied array
|
50
|
+
def self.process_array(global, the_array, k, value)
|
51
|
+
|
52
|
+
return [k, value] if value.length.zero?
|
53
|
+
field_count = value[0].length
|
54
|
+
return [k, value] if field_count.zero?
|
55
|
+
|
56
|
+
# Get the class for the 'of' field of the array.
|
57
|
+
clazz = global.classs the_array.of
|
58
|
+
raise StandardError, 'No class with id or name =' + the_array.of + ' could be found.' if clazz.nil?
|
59
|
+
|
60
|
+
# Get the *assign array from the class and make sure we have an entry of size field_count
|
61
|
+
assignment_list = clazz.keylist_of_length field_count
|
62
|
+
raise StandardError, 'No assignment list of length ' + field_count.to_s + ' for class with id or name =' + the_array.of + ' could be found.' if assignment_list.nil?
|
63
|
+
|
64
|
+
result = []
|
65
|
+
|
66
|
+
value.each do |record|
|
67
|
+
object = {}
|
68
|
+
i = 0
|
69
|
+
assignment_list.each do |field|
|
70
|
+
field_class = global.classs field
|
71
|
+
if field_class
|
72
|
+
object[field_class.name_or_id] = record[i]
|
73
|
+
else
|
74
|
+
object[field] = record[i]
|
75
|
+
end
|
76
|
+
i += 1
|
77
|
+
end
|
78
|
+
result << object
|
79
|
+
end
|
80
|
+
|
81
|
+
return [the_array.name_or_id, result]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Replace the existing object with the new array instance and a new key
|
85
|
+
# We need to keep the same key order, hence this method below
|
86
|
+
def self.replace_value(obj, old_k, new_k, new_v)
|
87
|
+
tmp = obj.dup
|
88
|
+
obj.clear
|
89
|
+
tmp.keys.each do |tmpk|
|
90
|
+
tmpv = tmp[tmpk]
|
91
|
+
if tmpk == old_k
|
92
|
+
obj[new_k] = new_v
|
93
|
+
else
|
94
|
+
obj[tmpk] = tmpv
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.process_obj(global, obj)
|
100
|
+
obj.keys.each do |k|
|
101
|
+
value = obj[k]
|
102
|
+
# Does the key refer to an array that we have parsed or loaded?
|
103
|
+
the_array = global.arrays(k)
|
104
|
+
if the_array
|
105
|
+
# Yes so convert this value to an instance of that array
|
106
|
+
new_k, new_v = process_array global, the_array, k, value
|
107
|
+
# Replace the existing object with the new array instance and a new key
|
108
|
+
# We need to keep the same key order, hence this method below
|
109
|
+
replace_value obj, k, new_k, new_v
|
110
|
+
else
|
111
|
+
new_v = value
|
112
|
+
end
|
113
|
+
# Recurse into the value in case it has contents that also refer to arrays.
|
114
|
+
process global, new_v
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -57,7 +57,7 @@ module MODL
|
|
57
57
|
new_k, new_v = process_class global, k, value
|
58
58
|
# Replace the existing object with the new class instance and a new key
|
59
59
|
# We need to keep the same key order, hence this method below
|
60
|
-
|
60
|
+
replace_value(obj, k, new_k, new_v)
|
61
61
|
else
|
62
62
|
new_v = value
|
63
63
|
end
|
@@ -66,7 +66,7 @@ module MODL
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
def self.
|
69
|
+
def self.replace_value(obj, old_k, new_k, new_v)
|
70
70
|
tmp = obj.dup
|
71
71
|
obj.clear
|
72
72
|
tmp.keys.each do |tmpk|
|
@@ -252,7 +252,7 @@ module MODL
|
|
252
252
|
|
253
253
|
# Replace the value for this key if we've changed anything.
|
254
254
|
if new_value[new_k] != new_v
|
255
|
-
|
255
|
+
replace_value(new_value, new_k, new_k, new_v)
|
256
256
|
end
|
257
257
|
end
|
258
258
|
elsif new_value.is_a?(Array)
|
@@ -49,7 +49,7 @@ module MODL
|
|
49
49
|
force = file_name.end_with?('!')
|
50
50
|
file_name = Sutil.head(file_name) if force
|
51
51
|
file_name << '.modl' unless file_name.end_with?('.txt', '.modl')
|
52
|
-
file_name,
|
52
|
+
file_name, _new_val = RefProcessor.deref file_name, global if file_name.include?('%')
|
53
53
|
if force
|
54
54
|
# Don't use the cache if we're forcing a reload.
|
55
55
|
@cache.evict(file_name)
|
@@ -56,6 +56,9 @@ module MODL
|
|
56
56
|
@syntax_version = 1
|
57
57
|
@interpreter_syntax_version = 1
|
58
58
|
@loaded_files = []
|
59
|
+
# Arrays
|
60
|
+
@arrays_by_id = {}
|
61
|
+
@arrays_by_name = {}
|
59
62
|
end
|
60
63
|
|
61
64
|
def loaded_file(str)
|
@@ -103,6 +106,17 @@ module MODL
|
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
109
|
+
def arrays(key)
|
110
|
+
if key.is_a? String
|
111
|
+
result = @arrays_by_id[key]
|
112
|
+
result = @arrays_by_name[key] if result.nil?
|
113
|
+
result
|
114
|
+
elsif key.is_a? MODLArray
|
115
|
+
@arrays_by_id[key.id] = key if key.id
|
116
|
+
@arrays_by_name[key.name] = key if key.name
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
106
120
|
def merge_pairs(other)
|
107
121
|
@pairs.merge!(other.all_pairs)
|
108
122
|
end
|
@@ -125,6 +139,10 @@ module MODL
|
|
125
139
|
@classes_by_id.keys.include?(key) || @classes_by_name.keys.include?(key)
|
126
140
|
end
|
127
141
|
|
142
|
+
def has_array?(key)
|
143
|
+
@arrays_by_id.keys.include?(key) || @arrays_by_name.keys.include?(key)
|
144
|
+
end
|
145
|
+
|
128
146
|
def has_user_method?(key)
|
129
147
|
@methods_hash.keys.include?(key)
|
130
148
|
end
|
@@ -27,6 +27,7 @@ require 'modl/parser/MODLParserVisitor'
|
|
27
27
|
require 'modl/parser/MODLLexer'
|
28
28
|
require 'modl/parser/MODLParser'
|
29
29
|
require 'modl/parser/class_processor'
|
30
|
+
require 'modl/parser/array_processor'
|
30
31
|
require 'modl/parser/orphan_handler'
|
31
32
|
require 'modl/parser/parser'
|
32
33
|
require 'json'
|
@@ -68,6 +69,7 @@ module MODL
|
|
68
69
|
|
69
70
|
# Process any class definitions used by the MODL file.
|
70
71
|
MODL::Parser::ClassProcessor.process(parsed.global, interpreted)
|
72
|
+
MODL::Parser::ArrayProcessor.process(parsed.global, interpreted)
|
71
73
|
MODL::Parser::InstructionProcessor.process(parsed.global, interpreted)
|
72
74
|
# If the result is a simple string then just return it.
|
73
75
|
interpreted
|
@@ -0,0 +1,84 @@
|
|
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
|
+
module MODL
|
26
|
+
module Parser
|
27
|
+
# Represents a *array defined, or loaded by, a MODL document.
|
28
|
+
class MODLArray
|
29
|
+
attr_accessor :id
|
30
|
+
attr_accessor :name
|
31
|
+
attr_accessor :of
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@content = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def name_or_id
|
38
|
+
@name.nil? ? @id : @name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Extract an array from a ParsedPair object
|
43
|
+
class ArrayExtractor
|
44
|
+
def self.extract(pair, global)
|
45
|
+
return unless pair.type == 'array'
|
46
|
+
|
47
|
+
the_array = MODLArray.new
|
48
|
+
map = pair.map if pair.map
|
49
|
+
map = pair.valueItem&.value&.map if pair.valueItem&.value&.map
|
50
|
+
|
51
|
+
map.mapItems.each do |item|
|
52
|
+
next unless item&.pair&.type
|
53
|
+
|
54
|
+
case item&.pair&.type
|
55
|
+
when 'id'
|
56
|
+
str_value = item.pair.valueItem.value.primitive.string.string
|
57
|
+
the_array.id = str_value
|
58
|
+
when 'name'
|
59
|
+
str_value = item.pair.valueItem.value.primitive.string.string
|
60
|
+
the_array.name = str_value
|
61
|
+
when 'of'
|
62
|
+
str_value = item.pair.valueItem.value.primitive.string.string
|
63
|
+
the_array.of = str_value
|
64
|
+
else
|
65
|
+
# Ignore
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
raise InterpreterError, 'Missing id for *array' if the_array.id.nil?
|
70
|
+
|
71
|
+
# Make sure the array name isn't redefining an existing array
|
72
|
+
if !global.has_array?(the_array.id) && !global.has_array?(the_array.name)
|
73
|
+
|
74
|
+
# store the arrays by id and name to make them easier to find later
|
75
|
+
global.arrays(the_array)
|
76
|
+
else
|
77
|
+
id = the_array.id.nil? ? 'undefined' : the_array.id
|
78
|
+
name = the_array.name.nil? ? 'undefined' : the_array.name
|
79
|
+
raise InterpreterError, '*Array name or id already defined - cannot redefine: ' + id + ', ' + name
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/modl/parser/parsed.rb
CHANGED
@@ -30,6 +30,7 @@ require 'modl/parser/file_importer'
|
|
30
30
|
require 'antlr4/runtime/parse_cancellation_exception'
|
31
31
|
require 'modl/parser/sutil'
|
32
32
|
require 'modl/parser/modl_class'
|
33
|
+
require 'modl/parser/modl_array'
|
33
34
|
require 'modl/parser/modl_method'
|
34
35
|
require 'modl/parser/modl_index'
|
35
36
|
require 'modl/parser/modl_keylist'
|
@@ -211,6 +212,8 @@ module MODL
|
|
211
212
|
@final = false
|
212
213
|
@file_importer = FileImporter.instance
|
213
214
|
@loaded = false
|
215
|
+
@array = nil
|
216
|
+
@map = nil
|
214
217
|
end
|
215
218
|
|
216
219
|
def find_property(key)
|
@@ -284,6 +287,7 @@ module MODL
|
|
284
287
|
return if @type == 'import'
|
285
288
|
return if @type == 'allow'
|
286
289
|
return if @type == 'expect'
|
290
|
+
return if @type == 'array'
|
287
291
|
|
288
292
|
{@key => @text}
|
289
293
|
end
|
@@ -336,6 +340,8 @@ module MODL
|
|
336
340
|
case @type
|
337
341
|
when 'class'
|
338
342
|
ClassExtractor.extract(self, @global)
|
343
|
+
when 'array'
|
344
|
+
ArrayExtractor.extract(self, @global)
|
339
345
|
when 'id'
|
340
346
|
extract_value
|
341
347
|
when 'name'
|
@@ -478,6 +484,8 @@ module MODL
|
|
478
484
|
@type = 'hidden' if @key.start_with? '_'
|
479
485
|
@type = 'allow' if @key.downcase == '*allow'
|
480
486
|
@type = 'expect' if @key.downcase == '*expect'
|
487
|
+
@type = 'of' if @key.downcase == '*of'
|
488
|
+
@type = 'array' if @key.downcase == '*array'
|
481
489
|
end
|
482
490
|
end
|
483
491
|
|
data/lib/modl/parser/version.rb
CHANGED
data/modl.gemspec
CHANGED
@@ -25,6 +25,6 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
27
27
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
28
|
-
spec.add_runtime_dependency 'antlr4-runtime', '= 0.2.
|
28
|
+
spec.add_runtime_dependency 'antlr4-runtime', '= 0.2.9'
|
29
29
|
spec.add_runtime_dependency 'punycode4r', '>= 0.2.0'
|
30
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Walmsley
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.2.
|
47
|
+
version: 0.2.9
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.2.
|
54
|
+
version: 0.2.9
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: punycode4r
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -113,12 +113,14 @@ files:
|
|
113
113
|
- lib/modl/parser/MODLParserBaseVisitor.rb
|
114
114
|
- lib/modl/parser/MODLParserListener.rb
|
115
115
|
- lib/modl/parser/MODLParserVisitor.rb
|
116
|
+
- lib/modl/parser/array_processor.rb
|
116
117
|
- lib/modl/parser/class_processor.rb
|
117
118
|
- lib/modl/parser/evaluator.rb
|
118
119
|
- lib/modl/parser/file_importer.rb
|
119
120
|
- lib/modl/parser/global_parse_context.rb
|
120
121
|
- lib/modl/parser/instruction_processor.rb
|
121
122
|
- lib/modl/parser/interpreter.rb
|
123
|
+
- lib/modl/parser/modl_array.rb
|
122
124
|
- lib/modl/parser/modl_class.rb
|
123
125
|
- lib/modl/parser/modl_index.rb
|
124
126
|
- lib/modl/parser/modl_keylist.rb
|