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,42 @@
1
+ require 'modl/parser/throwing_error_listener'
2
+ require 'modl/parser/parsed'
3
+
4
+ module Modl
5
+ module Parser
6
+
7
+ # This class converts the input string into a Modl:Parser::Parsed object for further processing.
8
+ class Parser
9
+ def self.parse(str, global = nil)
10
+ begin
11
+ lexer = Modl::Parser::MODLLexer.new(Antlr4::Runtime::CharStreams.from_string(str, 'String'))
12
+ lexer.remove_error_listeners
13
+ lexer.add_error_listener ThrowingErrorListener.instance
14
+
15
+ tokens = Antlr4::Runtime::CommonTokenStream.new(lexer)
16
+
17
+ parser = Modl::Parser::MODLParser.new(tokens)
18
+ parser.remove_error_listeners
19
+ parser.add_error_listener ThrowingErrorListener.instance
20
+
21
+ global = GlobalParseContext.new if global.nil?
22
+
23
+ parsed = Parsed.new(global)
24
+ parser.modl.enter_rule(parsed)
25
+ parsed
26
+ rescue StandardError => e
27
+ puts e.message
28
+ puts e.backtrace
29
+
30
+ if global.syntax_version > global.interpreter_syntax_version
31
+ raise InterpreterError, 'MODL Version ' +
32
+ global.interpreter_syntax_version.to_s +
33
+ ' interpreter cannot process this MODL Version ' +
34
+ global.syntax_version.to_s + ' file.'
35
+ end
36
+
37
+ raise InterpreterError, 'Parser Error: ' + e.message
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modl/parser/parsed'
4
+ require 'punycode'
5
+ require 'modl/parser/sutil'
6
+
7
+ module Modl
8
+ module Parser
9
+ # Convert MODL reference to the replacement value
10
+ class RefProcessor
11
+
12
+ NESTED_SEPARATOR = '.'
13
+ MATCHER = Regexp.new('((`?\%[0-9][0-9.][a-zA-Z0-9.(),]*`?)|(`?\%[0-9][0-9]*`?)|(`?\%[_a-zA-Z][_a-zA-Z0-9.%(),]*`?)|(`.*`\.[_a-zA-Z0-9.(),%]+)|(`.*`))')
14
+
15
+ def self.trivial_reject(str)
16
+ # do a fast check to see if we need to deref - save processing the regex if we don't have to.
17
+ str.is_a?(String) && (str.nil? || str.include?('%') || str.include?('`'))
18
+ end
19
+
20
+ # Check str for references and process them.
21
+ # Return the processed string and a new_value if there is one.
22
+ def self.deref(str, global)
23
+ obj = str
24
+ obj, new_value = split_by_ref_tokens str, global if trivial_reject(str)
25
+ [obj, new_value]
26
+ end
27
+
28
+ # Process the next %ref token
29
+ def self.split_by_ref_tokens(str, global)
30
+ new_value = nil
31
+
32
+ text = str
33
+ original = str
34
+
35
+ new_value, str = process_tokens(global, original, str, text) if new_value.nil?
36
+
37
+ [str, new_value]
38
+ end
39
+
40
+ private
41
+
42
+ def self.process_tokens(global, original, str, text)
43
+ new_value = nil
44
+ loop do
45
+ match = MATCHER.match(text.to_s)
46
+ break if match.nil?
47
+
48
+ ref = match[0]
49
+ text = Sutil.after(text, ref)
50
+ new_value, remainder = expand(global, ref)
51
+ ref = Sutil.until(ref, remainder)
52
+ if new_value.is_a?(String)
53
+ str = str.sub(ref, new_value)
54
+ elsif new_value.is_a?(Parsed::ParsedArrayItem)
55
+ nv_text = new_value.arrayValueItem.text
56
+ str = if ref == str
57
+ nv_text
58
+ else
59
+ str.sub(ref, nv_text.to_s)
60
+ end
61
+ new_value = nil
62
+ elsif new_value.is_a?(Modl::Parser::MODLParserBaseListener)
63
+ if new_value.text
64
+ str = if ref == str
65
+ new_value.text
66
+ else
67
+ str.sub(ref, new_value.text.to_s)
68
+ end
69
+ new_value = nil
70
+ else
71
+ str = nil
72
+ end
73
+ else
74
+ new_value = nil
75
+ raise InterpreterError, 'Cannot resolve reference in : "' + str + '"' if str == original
76
+ end
77
+ end
78
+ return new_value, str
79
+ end
80
+
81
+ def self.expand(global, ref)
82
+ result = nil
83
+ prev = nil
84
+
85
+ degraved = Sutil.replace(ref, '`', '')
86
+
87
+ parts = Sutil.tail(degraved).split('.') if degraved[0] == '%'
88
+ parts = degraved.split('.') unless degraved[0] == '%'
89
+
90
+ if degraved.include?('%')
91
+ resolved = 0
92
+ parts.each do |p|
93
+ if p.include?('%')
94
+ p, _ignore = expand(global, p)
95
+ if p.is_a?(Modl::Parser::MODLParserBaseListener)
96
+ p = p.text
97
+ end
98
+ end
99
+ n = p.to_i
100
+ result = if n.to_s == p
101
+ # Numeric ref
102
+ result.nil? ? global.index_value(n, degraved) : result.find_property(n)
103
+ else
104
+ # String ref
105
+ if result.is_a? String
106
+ StandardMethods.run_method(p, result)
107
+ elsif result.is_a? Array
108
+ nil
109
+ else
110
+ result.nil? ? global.pair(p) : result.find_property(p)
111
+ end
112
+ end
113
+ break if result.nil?
114
+
115
+ prev = result
116
+ resolved += 1
117
+ end
118
+ if prev.nil?
119
+ remainder = ''
120
+ prev = degraved
121
+ else
122
+ remainder = resolved < parts.length ? '.' + parts[resolved..parts.length].join('.') : ''
123
+ end
124
+ [prev, remainder]
125
+ else
126
+ # Remove the graves if there are any.
127
+ result = parts[0]
128
+ i = 1
129
+ while i < parts.length
130
+ result = StandardMethods.run_method(parts[i], result)
131
+ i += 1
132
+ end
133
+ [result, '']
134
+ end
135
+ end
136
+
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Modl
4
+ module Parser
5
+ # Escape-sequence replacements for MODL files.
6
+ class Substitutions
7
+
8
+ @@subs = [
9
+ ['~\\', '\\'],
10
+ ['\\\\', '\\'],
11
+ ['~~', '~'],
12
+ ['\\~', '~'],
13
+ ['~(', '('],
14
+ ['\\(', '('],
15
+ ['~)', ')'],
16
+ ['\\)', ')'],
17
+ ['~[', '['],
18
+ ['\\[', '['],
19
+ ['~]', ']'],
20
+ ['\\]', ']'],
21
+ ['~{', '{'],
22
+ ['\\{', '{'],
23
+ ['~}', '}'],
24
+ ['\\}', '}'],
25
+ ['~;', ';'],
26
+ ['\\;', ';'],
27
+ ['~:', ':'],
28
+ ['\\:', ':'],
29
+ ['~`', '`'],
30
+ ['\\`', '`'],
31
+ ['~"', '"'],
32
+ ['\\"', '"'],
33
+ ['~=', '='],
34
+ ['\\=', '='],
35
+ ['~/', '/'],
36
+ ['\\/', '/'],
37
+ ['<', '<'],
38
+ ['\\<', '<'],
39
+ ['~>', '>'],
40
+ ['\\>', '>'],
41
+ ['~&', '&'],
42
+ ['\\&', '&'],
43
+ ['!', '!'],
44
+ ['\\!', '!'],
45
+ ['~|', '|'],
46
+ ['\\|', '|'],
47
+ ['\\t', "\t"],
48
+ ['\\n', "\n"],
49
+ ['\\b', "\b"],
50
+ ['\\f', "\f"],
51
+ ['\\r', "\r"]
52
+ ]
53
+
54
+ # Replace all escape sequences in the supplied string and return the new value.
55
+ def self.process(str)
56
+ @@subs.each do |s|
57
+ loop do
58
+ prev = str
59
+ str = str.sub(s[0], s[1])
60
+ break unless str && str != prev
61
+ end
62
+ end
63
+ str
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sutil
4
+ # Remove the first n characters from a string
5
+ def self.tail(str, n = 1)
6
+ return if n.negative?
7
+
8
+ str&.slice(n, str.length)
9
+ end
10
+
11
+ # Keep the first n characters of a string
12
+ def self.head(str, n = nil)
13
+ return if str.nil?
14
+
15
+ n = str.length - 1 if n.nil? # Strip last char by default
16
+
17
+ str&.slice(0, n)
18
+ end
19
+
20
+ # Keep everything until char c
21
+ def self.until(str, c)
22
+ return str if c.nil? || c.empty?
23
+
24
+ i = str.index(c)
25
+ i ? str&.slice(0, i) : str
26
+ end
27
+
28
+ # Get everything after char c
29
+ def self.after(str, c)
30
+ return if str.nil?
31
+ return str if c.nil?
32
+
33
+ i = str.index(c)
34
+ return '' if i.nil?
35
+
36
+ str.slice(i + c.length, str.length)
37
+ end
38
+
39
+ # Get everything between ch1 and ch2
40
+ # ch1 and ch2 must be different and ch1 must be before ch2 in the string
41
+ # e.g. Sutil.between('func(p1,p2)', '(', ')') returns 'p1,p2'
42
+ def self.between(str, ch1, ch2)
43
+ return if str.nil?
44
+ return str if ch1.nil? || ch2.nil? || ch1 == '' || ch2 == ''
45
+ return str if str.length < 3
46
+
47
+ first = str.index(ch1)
48
+ second = str.rindex(ch2)
49
+ if first > second
50
+ tmp = first
51
+ first = second
52
+ second = tmp
53
+ end
54
+ return str if first == second
55
+
56
+ str.slice(first + 1, second - first - 1)
57
+ end
58
+
59
+ # Remove the first and last chars from a string.
60
+ def self.toptail(str)
61
+ return str if str.nil?
62
+ return '' if str.length < 3
63
+
64
+ str&.slice(1, str.length - 2)
65
+ end
66
+
67
+ def self.replace(str, old, new)
68
+ return str if old.nil? || new.nil? || old.empty? || old == new
69
+
70
+ result = str
71
+ loop do
72
+ break unless result.include?(old)
73
+
74
+ result = result.sub(old, new)
75
+ end
76
+ result
77
+ end
78
+ end
@@ -0,0 +1,20 @@
1
+ require 'antlr4/runtime/parse_cancellation_exception'
2
+
3
+ module Modl::Parser
4
+ class ThrowingErrorListener
5
+ include Singleton
6
+
7
+ def syntax_error(_recognizer, _offending_symbol, _line, _char_position_in_line, _msg, _e)
8
+ raise Antlr4::Runtime::ParseCancellationException, 'line' + _line.to_s + ':' + _char_position_in_line.to_s + ' ' + _msg.to_s
9
+ end
10
+
11
+ def report_ambiguity(_recognizer, _dfa, _start_index, _stop_index, _exact, _ambig_ilts, _configs)
12
+ end
13
+
14
+ def report_attempting_full_context(_recognizer, _dfa, _start_index, _stop_index, _conflicting_alts, _configs)
15
+ end
16
+
17
+ def report_context_sensitivity(_recognizer, _dfa, _start_index, _stop_index, _prediction, _configs)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module Modl
2
+ module Parser
3
+ VERSION = "0.3.0"
4
+ end
5
+ end
data/modl.gemspec ADDED
@@ -0,0 +1,32 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "modl/parser/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "modl"
8
+ spec.version = Modl::Parser::VERSION
9
+ spec.authors = ["Tony Walmsley"]
10
+ spec.email = ["tony@aosd.co.uk"]
11
+
12
+ spec.summary = %q{The ANTLR4 Lexer and Parser for MODL generated using the Ruby language target.}
13
+ spec.description = %q{Contains the base Lexer, Parser, and supporting classes for a MODL parser.}
14
+ spec.homepage = "https://github.com/MODLanguage/ruby-interpreter"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ #spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ spec.add_development_dependency 'antlr4-runtime', '>= 0.0.1'
30
+ spec.add_runtime_dependency 'antlr4-runtime', '>= 0.0.1'
31
+ spec.add_runtime_dependency 'punycode4r', '>= 0.2.0'
32
+ end
metadata CHANGED
@@ -1,23 +1,150 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
- - Elliott Brown
7
+ - Tony Walmsley
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-24 00:00:00.000000000 Z
12
- dependencies: []
13
- description: A MODL interpreter
14
- email: gem@modl.uk
11
+ date: 2019-05-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: antlr4-runtime
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: antlr4-runtime
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: punycode4r
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.0
83
+ description: Contains the base Lexer, Parser, and supporting classes for a MODL parser.
84
+ email:
85
+ - tony@aosd.co.uk
15
86
  executables: []
16
87
  extensions: []
17
88
  extra_rdoc_files: []
18
89
  files:
19
- - lib/modl.rb
20
- homepage: http://rubygems.org/gems/modl
90
+ - ".gitignore"
91
+ - ".idea/vcs.xml"
92
+ - ".rspec"
93
+ - ".rubocop.yml"
94
+ - ".travis.yml"
95
+ - CHANGELOG.md
96
+ - CODE_OF_CONDUCT.md
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/console
102
+ - bin/setup
103
+ - grammar_tests/1.modl
104
+ - grammar_tests/2.modl
105
+ - grammar_tests/3.modl
106
+ - grammar_tests/a.modl
107
+ - grammar_tests/b.modl
108
+ - grammar_tests/base_tests.json
109
+ - grammar_tests/c.modl
110
+ - grammar_tests/demo_config.modl
111
+ - grammar_tests/error_tests.json
112
+ - grammar_tests/import_config.modl
113
+ - grammar_tests/test_import_dir/nested_import1.txt
114
+ - grammar_tests/test_import_dir/nested_import2.txt
115
+ - grammar_tests/test_import_dir/nested_import3.txt
116
+ - grammar_tests/test_import_dir/test_import.txt
117
+ - lib/modl/interpreter.rb
118
+ - lib/modl/parser/MODLLexer.interp
119
+ - lib/modl/parser/MODLLexer.rb
120
+ - lib/modl/parser/MODLLexer.tokens
121
+ - lib/modl/parser/MODLParser.interp
122
+ - lib/modl/parser/MODLParser.rb
123
+ - lib/modl/parser/MODLParser.tokens
124
+ - lib/modl/parser/MODLParserBaseListener.rb
125
+ - lib/modl/parser/MODLParserBaseVisitor.rb
126
+ - lib/modl/parser/MODLParserListener.rb
127
+ - lib/modl/parser/MODLParserVisitor.rb
128
+ - lib/modl/parser/class_processor.rb
129
+ - lib/modl/parser/evaluator.rb
130
+ - lib/modl/parser/file_importer.rb
131
+ - lib/modl/parser/global_parse_context.rb
132
+ - lib/modl/parser/instruction_processor.rb
133
+ - lib/modl/parser/interpreter.rb
134
+ - lib/modl/parser/modl_class.rb
135
+ - lib/modl/parser/modl_index.rb
136
+ - lib/modl/parser/modl_keylist.rb
137
+ - lib/modl/parser/modl_method.rb
138
+ - lib/modl/parser/object_cache.rb
139
+ - lib/modl/parser/parsed.rb
140
+ - lib/modl/parser/parser.rb
141
+ - lib/modl/parser/ref_processor.rb
142
+ - lib/modl/parser/substitutions.rb
143
+ - lib/modl/parser/sutil.rb
144
+ - lib/modl/parser/throwing_error_listener.rb
145
+ - lib/modl/parser/version.rb
146
+ - modl.gemspec
147
+ homepage: https://github.com/MODLanguage/ruby-interpreter
21
148
  licenses:
22
149
  - MIT
23
150
  metadata: {}
@@ -37,8 +164,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
37
164
  version: '0'
38
165
  requirements: []
39
166
  rubyforge_project:
40
- rubygems_version: 2.6.12
167
+ rubygems_version: 2.7.9
41
168
  signing_key:
42
169
  specification_version: 4
43
- summary: MODL
170
+ summary: The ANTLR4 Lexer and Parser for MODL generated using the Ruby language target.
44
171
  test_files: []
data/lib/modl.rb DELETED
@@ -1,5 +0,0 @@
1
- class Modl
2
- def self.hi
3
- puts "MODL"
4
- end
5
- end