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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19862a8b989bb41687946af65e7368ee2c9ab88794498d9bc0383dba94bbd1ad
4
- data.tar.gz: e1b01ea144720b3b96f7ee412c843154fdb10acf6f84ebd72e24e23a5aef7f25
3
+ metadata.gz: de1a3cee98476becc60a2c7dab825c65c30b47e5e8b017ae30c23b8505937e41
4
+ data.tar.gz: 85150763932b4136124d120017881baaf34c528cc989709ecc139eaf563bc868
5
5
  SHA512:
6
- metadata.gz: eea7c86691b1e027737cbefbb92644731f96d049fbdf2bf894c0ea971679536f9111ff0c670ce2785b6346664df90e813bd5cce2c3f8b6360e1d91e2cbe94446
7
- data.tar.gz: e059d05d456ab4f73bc11b5dc62f50e7a291d00158063c4cc43ca7d3a3f5b9b1da09457bf3bf3f44d41d896576e3879efb49398434aa088b3229b64d50a12882
6
+ metadata.gz: 0276b480cfe85fa983b0deac3ca030f5a79d25e6676c781cf3ca8868162adfb30d837d29af4d26d935e41888819b57b9f83141d41d0311dd2fb8ab634e2799c5
7
+ data.tar.gz: d2e5547d0fd8225d3739d8d2c3754f40b7b77f0df6f5d415a42201f5dd696beb8f1d25e33c2ee18b635efc15bdc268df20ad97c7188c742825d498234312314f
@@ -1,3 +1,8 @@
1
+ 0.3.16
2
+ ===
3
+ - Use latest ruby runtime.
4
+ - Add support for `*array` keyword.
5
+
1
6
  0.3.15
2
7
  ===
3
8
  - Interpreter Error: undefined method `text' for nil:NilClass. GitHub issue #23
@@ -1667,7 +1667,7 @@
1667
1667
  },
1668
1668
  {
1669
1669
  "id": "176",
1670
- "input": "_var=2;\n*L=\"http://s3-eu-west-1.amazonaws.com/modltestfiles/testing.txt!\";\nprint=%update_date\n",
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
- replaceValue(obj, k, new_k, new_v)
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.replaceValue(obj, old_k, new_k, new_v)
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
- replaceValue(new_value, new_k, new_k, new_v)
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, new_val = RefProcessor.deref file_name, global if file_name.include?('%')
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
@@ -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
 
@@ -22,7 +22,6 @@
22
22
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  # THE SOFTWARE.
24
24
 
25
- require 'modl/parser/parsed'
26
25
  require 'punycode'
27
26
  require 'modl/parser/sutil'
28
27
 
@@ -24,6 +24,6 @@
24
24
 
25
25
  module MODL
26
26
  module Parser
27
- VERSION = "0.3.15"
27
+ VERSION = "0.3.16"
28
28
  end
29
29
  end
@@ -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.8'
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.15
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-07-05 00:00:00.000000000 Z
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.8
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.8
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