modl 0.0.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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