modl 0.3.25 → 0.3.27

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 -140
  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 +272 -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 -84
  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 -1470
  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,81 +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
- module MODL
26
- module Parser
27
- # Extracts an index definition from a ParsedPair
28
- class KeylistExtractor
29
- def self.extract(pair, item)
30
- # the item must be an array of arrays
31
- pair.key_lists = []
32
- last_keylist_len = 0
33
- if item.is_a?(Parsed::ParsedValueItem) && item.value.is_a?(Parsed::ParsedValue) && item.value.array
34
- item.value.array.abstractArrayItems.each do |avi|
35
- key_list = []
36
- avi.arrayValueItem.array.abstractArrayItems.each do |key|
37
- key_list << key.arrayValueItem.primitive.string.string if key.arrayValueItem.primitive.string
38
- key_list << key.arrayValueItem.primitive.number.num if key.arrayValueItem.primitive.number
39
- end
40
- if key_list.length > last_keylist_len
41
- last_keylist_len = key_list.length
42
- else
43
- raise InterpreterError, 'Error: Key lists in *assign are not in ascending order of list length: ' + key_list.to_s
44
- end
45
- pair.key_lists << key_list
46
- end
47
- elsif item.is_a?(Parsed::ParsedArray)
48
- item.abstractArrayItems.each do |avi|
49
- key_list = []
50
- avi.arrayValueItem.array.abstractArrayItems.each do |key|
51
- key_list << key.arrayValueItem.primitive.string.string if key.arrayValueItem.primitive.string
52
- key_list << key.arrayValueItem.primitive.number.num if key.arrayValueItem.primitive.number
53
- end
54
- if key_list.length > last_keylist_len
55
- last_keylist_len = key_list.length
56
- else
57
- raise InterpreterError, 'Error: Key lists in *assign are not in ascending order of list length.'
58
- end
59
- pair.key_lists << key_list
60
- end
61
- elsif item.is_a?(Parsed::ParsedValueItem) && !item.value.nbArray.nil?
62
- item.value.nbArray.arrayItems.each do |avi|
63
- key_list = []
64
- avi.arrayValueItem.array.abstractArrayItems.each do |key|
65
- key_list << key.arrayValueItem.primitive.string.string if key.arrayValueItem.primitive.string
66
- key_list << key.arrayValueItem.primitive.number.num if key.arrayValueItem.primitive.number
67
- end
68
- if key_list.length > last_keylist_len
69
- last_keylist_len = key_list.length
70
- else
71
- raise InterpreterError, 'Error: Key lists in *assign are not in ascending order of list length.'
72
- end
73
- pair.key_lists << key_list
74
- end
75
- else
76
- raise InterpreterError, 'Array of arrays expected for: ' + pair.key
77
- end
78
- end
79
- end
80
- end
81
- end
@@ -1,172 +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
- module MODL
26
- module Parser
27
- # Represents a *method defined by a MODL document.
28
- class MODLMethod
29
- attr_accessor :id
30
- attr_accessor :name
31
- attr_accessor :transform
32
-
33
- def name_or_id
34
- @name.nil? ? @id : @name
35
- end
36
-
37
- # There is a user-defined method transform to run on the str
38
- def run(str)
39
- # Consume the elements of the transform spec until there are none left.
40
- transform = @transform
41
- while transform && transform.length > 0
42
- if transform.start_with?('replace<') || transform.start_with?('r<')
43
- close_bracket = transform.index('>')
44
- m = Sutil.head(transform, close_bracket + 1).sub!('replace', 'r')
45
- str = StandardMethods.run_method(m, str)
46
- # Consume the subst clause
47
- close_bracket = transform.index('>')
48
- transform = Sutil.tail(transform, close_bracket + 2)
49
- elsif transform.start_with?('trim') || transform.start_with?('t<')
50
- close_bracket = transform.index('>')
51
- m = Sutil.head(transform, close_bracket + 1).sub!('trim', 't')
52
- str = StandardMethods.run_method(m, str)
53
- # Consume the trunc clause
54
- close_bracket = transform.index('>')
55
- transform = Sutil.tail(transform, close_bracket + 2)
56
- elsif transform.start_with?('initcap') || transform.start_with?('i')
57
- str = StandardMethods.run_method('i', str)
58
- transform = Sutil.after(transform, '.')
59
- elsif transform.start_with?('upcase') || transform.start_with?('u')
60
- str = StandardMethods.run_method('u', str)
61
- transform = Sutil.after(transform, '.')
62
- elsif transform.start_with?('downcase') || transform.start_with?('d')
63
- str = StandardMethods.run_method('d', str)
64
- transform = Sutil.after(transform, '.')
65
- elsif transform.start_with?('sentence') || transform.start_with?('s')
66
- str = StandardMethods.run_method('s', str)
67
- transform = Sutil.after(transform, '.')
68
- elsif transform.start_with?('urlencode') || transform.start_with?('e')
69
- str = StandardMethods.run_method('e', str)
70
- transform = Sutil.after(transform, '.')
71
- else
72
- raise InterpreterError, 'NOT IMPLEMENTED'
73
- end
74
- end
75
- str
76
- end
77
-
78
- end
79
-
80
- # Extracts a method definition from a ParsedPair
81
- class MethodExtractor
82
- def self.extract(pair, global)
83
- return unless pair.type == 'method'
84
-
85
- mthd = MODLMethod.new
86
- map = pair.map if pair.map
87
- map = pair.valueItem&.value&.map if pair.valueItem&.value&.map
88
-
89
- map.mapItems.each do |item|
90
- next unless item&.pair&.type
91
-
92
- case item&.pair&.type
93
- when 'id'
94
- mthd.id = item.pair.valueItem.value.primitive.string.string
95
- when 'transform'
96
- mthd.transform = item.pair.valueItem.value.primitive.string.string
97
- when 'name'
98
- mthd.name = item.pair.valueItem.value.primitive.string.string
99
- else
100
- raise InterpreterError, 'Invalid *method - only *id, *name, and *transform fields expected'
101
- end
102
- end
103
-
104
- raise InterpreterError, 'Missing id for method' if mthd.id.nil?
105
- raise InterpreterError, 'Missing name for method' if mthd.name.nil?
106
- raise InterpreterError, 'Duplicate method name or id: ' + mthd.name if global.has_user_method?(mthd.name)
107
- raise InterpreterError, 'Duplicate method name or id: ' + mthd.id if global.has_user_method?(mthd.id)
108
-
109
- # store the methods by id and name to make them easier to find later
110
- global.user_method_id(mthd.id, mthd)
111
- global.user_method(mthd.name, mthd)
112
- end
113
- end
114
-
115
- class StandardMethods
116
-
117
- @@mthd_names = %w(u d i s e r t p upcase downcase initcap sentence urlencode replace trim punydecode)
118
-
119
- def self.run_method(mthd, str)
120
- m = mthd.match(/\w*/)[0]
121
- case m
122
- when 'u', 'upcase'
123
- str.upcase
124
- when 'd', 'downcase'
125
- str.downcase
126
- when 'i', 'initcap'
127
- str.split.map(&:capitalize) * ' '
128
- when 's', 'sentence'
129
- split = str.split
130
- split[0].capitalize!
131
- split.join(' ')
132
- when 'e', 'urlencode'
133
- CGI.escape(str)
134
- when 'r', 'replace'
135
- s1, s2 = get_subst_parts(mthd)
136
- str.gsub(s1, s2)
137
- when 't', 'trim'
138
- s1 = extract_params mthd
139
- i = str.index(s1)
140
- Sutil.head(str, i)
141
- when 'p', 'punydecode'
142
- Punycode.decode(str)
143
- else
144
- str.nil? ? '.' + mthd : str.to_s + '.' + mthd
145
- end
146
- end
147
-
148
- # Extract the method parameters
149
- def self.get_subst_parts(s)
150
- # should be of the form .r(s1,s2)
151
- result = extract_params(s).split(',')
152
- result[0] = '' if result.length.zero? || result[0].nil?
153
- result[1] = '' if result.length == 1 || result[1].nil?
154
- result
155
- end
156
-
157
- # Extract the method parameter
158
- def self.extract_params(str)
159
- # should be of the form .r(s1,s2)
160
- Sutil.between(str, '<', '>')
161
- end
162
-
163
- def self.valid_method?(mthd)
164
- m = mthd
165
- if m.include?('<')
166
- m = Sutil.until(m, '<')
167
- end
168
- return @@mthd_names.include?(m)
169
- end
170
- end
171
- end
172
- end
@@ -1,88 +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
- module MODL
26
- module Parser
27
- # Store any files for up to 1 hour by default.
28
- class ObjectCache
29
- # A cache record to keep track of the time since an object was last cached.
30
- class CacheEntry
31
- TTL_ONE_HOUR = 3_600 # seconds
32
-
33
- attr_reader :object
34
-
35
- # Initialiase the CacheEntry with an object and an optional ttl in seconds (default 1 hour)
36
- def initialize(object, ttl = nil)
37
- ttl = TTL_ONE_HOUR if ttl.nil?
38
- @object = object
39
- @expiry_time = Time.now + ttl
40
- end
41
-
42
- # Check whether the CacheEntry is live
43
- def expired?
44
- @expiry_time < Time.now
45
- end
46
- end
47
-
48
- # Set up and empty cache.
49
- def initialize
50
- @cache = {}
51
- end
52
-
53
- # Cache an object with the given key and optional ttl in seconds (default 1 hour)
54
- def put(key, object, ttl = nil)
55
- @cache[key] = CacheEntry.new(object, ttl) unless key.nil? || object.nil?
56
- end
57
-
58
- # Evict a cache entry
59
- def evict(key)
60
- @cache.delete(key) unless key.nil?
61
- end
62
-
63
- # Return the object with the given key if one exists and has not expired.
64
- def get(key)
65
- # Return nothing if not in the cache or it has expired.
66
- return if key.nil?
67
-
68
- entry = @cache[key]
69
- return unless entry
70
- return if entry.expired?
71
-
72
- # Otherwise return the cached object.
73
- # We don't delete the cached entry because we might need to force its use if its expired and offline
74
- entry.object
75
- end
76
-
77
- # If the file is offline this can be used to retrieve the cached version if we have one.
78
- def force_get(key)
79
- # Return nothing if not in the cache or it has expired.
80
- return if key.nil?
81
-
82
- entry = @cache[key]
83
- return unless entry
84
- entry.object
85
- end
86
- end
87
- end
88
- end
@@ -1,98 +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
- module MODL
26
- module Parser
27
- class OrphanHandler
28
- #
29
- # Return true if all strings start with '_'
30
- #
31
- def self.all_hidden(str_array)
32
- if str_array && str_array.length > 0
33
- str_array.each do |s|
34
- return false unless s.start_with?('_')
35
- end
36
- end
37
- true
38
- end
39
-
40
- #
41
- # Look for any orphan pairs at the top level and adopt them into a map
42
- # Its an error if there are duplicate keys or mixed types at the top.
43
- #
44
- def self.adopt(global, structures)
45
- #
46
- # Separate out any top-level pairs into a separate hash, checking for duplicates on the way.
47
- #
48
- if structures
49
- pairs = Hash.new
50
-
51
- # This will replace the existing structures array
52
- new_structures = []
53
-
54
- structures.each do |s|
55
- if s.pair
56
- # skip hidden pairs and instructions
57
- if s.pair.key.start_with?('*') || s.pair.key.start_with?('_') || s.pair.key == '?'
58
- new_structures.push(s)
59
- next
60
- end
61
-
62
- if pairs.has_key?(s.pair.key)
63
- raise InterpreterError, 'Duplicate top level keys are not allowed.'
64
- else
65
- pairs[s.pair.key] = s
66
- end
67
- else
68
- if pairs.length > 0 && !all_hidden(pairs.keys) && !s.top_level_conditional
69
- raise InterpreterError, 'Mixed top-level types are not allowed.'
70
- else
71
- new_structures.push(s)
72
- end
73
- end
74
- end
75
-
76
- if pairs.length > 0
77
- #
78
- # Create a map for the pairs and insert them into it.
79
- #
80
- new_map = MODL::Parser::Parsed::ParsedMap.new(global)
81
- pairs.values.each do |p|
82
- new_map.mapItems.push p unless p.pair.key.start_with?('_')
83
- end
84
-
85
- # Add the map to a new structure and insert it at the front of the structures list
86
- new_struct = MODL::Parser::Parsed::ParsedStructure.new(global)
87
- new_struct.map = new_map
88
- new_structures.unshift(new_struct)
89
-
90
- # Replace the existing structures with the new structures.
91
- return new_structures
92
- end
93
- end
94
- structures
95
- end
96
- end
97
- end
98
- end