alphalang 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/bin/alphalang +2 -7
- data/lib/alpha.rb +5 -2
- data/lib/locale_defaulter.rb +1 -2
- data/lib/locale_lister.rb +11 -4
- data/lib/locales/default +1 -18
- data/lib/nodes/basenodes.rb +2 -2
- data/lib/nodes/scopemanager.rb +28 -16
- data/lib/rdparse.rb +39 -16
- data/lib/tester/test_unit.rb +155 -131
- metadata +2 -3
- data/lib/rdparse_quiet.rb +0 -236
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: daa1ca143b51a415a7888d29f6cec78e41fddaa637685af3534708c40cc6de5a
|
4
|
+
data.tar.gz: b2c26c887f7fcf6f14f4efab2787d61a1ce9f7df8e6cb872bfc0b2b7184fa961
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f811511f84666e1148ea41d5706ed8289482dd8df76d2f0c3f25ebc8513ccf79b1869f83a35f586cbc6af0c1ded8f3ecf28403dc8495562a7f0d034daa0cc16b
|
7
|
+
data.tar.gz: e62cf96b852544722eb6aa3c33e9cbbc32bb5eb3fa03aec71d89121736be7c78f2a027e0b1885419eec53385afa2787a1a8b9d5953aa6720cb8571bd3749b8d2
|
data/bin/alphalang
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'optparse'
|
3
3
|
require File.expand_path('../lib/alpha', __dir__)
|
4
|
+
require_relative '../lib/rdparse'
|
4
5
|
ALPHA_VER = '0.1.5'.freeze
|
5
6
|
ABORT_ANSWERS = [' ', '', 'none', 'abort'].freeze # something here? Some cases empty means yes tho..
|
6
7
|
LOCALES_PATH = File.join(__dir__, '../lib/locales')
|
@@ -98,12 +99,6 @@ raise OptionParser::InvalidArgument, 'Too many files provided.
|
|
98
99
|
Did you mean to use an --[option]? | alphalang -h' if ARGV.length > 1
|
99
100
|
raise OptionParser::InvalidArgument, 'File must end with .alpha' unless verify_flag or ARGV[0].end_with?('.alpha')
|
100
101
|
|
101
|
-
if verbose_flag
|
102
|
-
require_relative '../lib/rdparse'
|
103
|
-
else
|
104
|
-
require_relative '../lib/rdparse_quiet'
|
105
|
-
end
|
106
|
-
|
107
102
|
if verify_flag
|
108
103
|
unless ARGV.empty?
|
109
104
|
puts 'Flag for verification found. Ignoring input file.'
|
@@ -111,5 +106,5 @@ if verify_flag
|
|
111
106
|
end
|
112
107
|
require_relative '../lib/tester/test_unit'
|
113
108
|
else
|
114
|
-
LangParser.new(language_flag).parse_file(ARGV[0])
|
109
|
+
LangParser.new(language_flag, verbose_flag).parse_file(ARGV[0])
|
115
110
|
end
|
data/lib/alpha.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require_relative './nodes/scopemanager'
|
2
2
|
|
3
3
|
class LangParser
|
4
|
-
def initialize(locale = 'default')
|
5
|
-
|
4
|
+
def initialize(locale = 'default', debug_mode = false)
|
5
|
+
|
6
|
+
@langParser = Parser.new('lang parser', debug_mode, locale) do
|
6
7
|
token(/;;.*$/)
|
7
8
|
token(/while/) { |_| :WHILE }
|
8
9
|
token(/print/) { |_| :PRINT }
|
@@ -137,6 +138,8 @@ class LangParser
|
|
137
138
|
match('-', /\d+/) { |neg, a| NumberNode.new(neg + a) }
|
138
139
|
match(/\d+/) { |a| NumberNode.new(a) }
|
139
140
|
match(/(false|true)/) { |a| BoolNode.new(a) }
|
141
|
+
match(ScopeManager.true_value) { |a| BoolNode.new(a) }
|
142
|
+
match(ScopeManager.false_value) { |a| BoolNode.new(a) }
|
140
143
|
match(:PRINT, :member) { |_, a| PrintNode.new(a) }
|
141
144
|
match(:PRINT, :expr) { |_, a| PrintNode.new(a) }
|
142
145
|
match(:member)
|
data/lib/locale_defaulter.rb
CHANGED
@@ -7,8 +7,7 @@ def set_default_locale(new_locale)
|
|
7
7
|
|
8
8
|
available_locales.each do |locale|
|
9
9
|
if locale == new_locale
|
10
|
-
File.
|
11
|
-
File.copy_stream("#{LOCALES_PATH}/#{new_locale}", "#{LOCALES_PATH}/default")
|
10
|
+
File.open("#{LOCALES_PATH}/default", 'w') { |f| f.write(new_locale)}
|
12
11
|
puts "[alphalang] Default syntax locale is now set to #{locale}."
|
13
12
|
return
|
14
13
|
end
|
data/lib/locale_lister.rb
CHANGED
@@ -20,7 +20,7 @@ def clean_locale_file_to_array(locale_name)
|
|
20
20
|
|
21
21
|
clean_locale_file_array = []
|
22
22
|
locale_file.each do |line|
|
23
|
-
line.scan(/\
|
23
|
+
line.scan(/\p{Word}+[|]\p{Word}+|\p{Word}+/) do |word|
|
24
24
|
clean_locale_file_array << word if word.size > 1
|
25
25
|
end
|
26
26
|
end
|
@@ -28,15 +28,18 @@ def clean_locale_file_to_array(locale_name)
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def print_clean_locale_array(locale_name, clean_array)
|
31
|
-
|
31
|
+
longest_word = clean_array.max_by(&:length).size
|
32
|
+
padding = ' ' * (longest_word/2-5)
|
33
|
+
|
34
|
+
header = "#{padding}[alphalang] Syntax for locale <#{locale_name}>.#{padding}"
|
32
35
|
puts header
|
33
36
|
puts '+' * (header.size - 2)
|
34
37
|
clean_line = ''
|
35
38
|
clean_array.each_with_index do |word, index|
|
36
39
|
if index.even?
|
37
|
-
clean_line += "+
|
40
|
+
clean_line += "+ #{word}"
|
38
41
|
else
|
39
|
-
clean_line += (' ' * (20 -
|
42
|
+
clean_line += (' ' * (20 -clean_line.size)) + "#{word}"
|
40
43
|
clean_line += (' ' * (header.size - clean_line.size - 3) + '+')
|
41
44
|
puts clean_line
|
42
45
|
clean_line = ''
|
@@ -49,6 +52,10 @@ def list_specific_locale_file()
|
|
49
52
|
list_locale_files
|
50
53
|
specific_locale = gets.chomp
|
51
54
|
|
55
|
+
if specific_locale == 'default'
|
56
|
+
specific_locale = File.read("#{LOCALES_PATH}/#{specific_locale}")
|
57
|
+
end
|
58
|
+
|
52
59
|
return if ABORT_ANSWERS.include?(specific_locale)
|
53
60
|
|
54
61
|
specific_locale_array = clean_locale_file_to_array(specific_locale)
|
data/lib/locales/default
CHANGED
@@ -1,18 +1 @@
|
|
1
|
-
|
2
|
-
while while
|
3
|
-
print print
|
4
|
-
pause pause
|
5
|
-
def def
|
6
|
-
end end
|
7
|
-
if if
|
8
|
-
elseif elseif
|
9
|
-
else else
|
10
|
-
(false|true) (false|true)
|
11
|
-
(==|<=|>=) (==|<=|>=)
|
12
|
-
(not|!) (not|!)
|
13
|
-
(and|&&) (and|&&)
|
14
|
-
(or|\|\|) (or|\|\|)
|
15
|
-
\s+ \s+
|
16
|
-
\d+ \d+
|
17
|
-
\w+ \w+
|
18
|
-
. .
|
1
|
+
en
|
data/lib/nodes/basenodes.rb
CHANGED
@@ -59,8 +59,8 @@ end
|
|
59
59
|
class BoolNode < Node
|
60
60
|
def initialize(value)
|
61
61
|
@value = false
|
62
|
-
@value = true if value ==
|
63
|
-
@value = false if value ==
|
62
|
+
@value = true if value == ScopeManager.true_value
|
63
|
+
@value = false if value == ScopeManager.false_value
|
64
64
|
end
|
65
65
|
|
66
66
|
def evaluate
|
data/lib/nodes/scopemanager.rb
CHANGED
@@ -2,52 +2,64 @@ require_relative 'stmtnodes'
|
|
2
2
|
|
3
3
|
####################################################
|
4
4
|
|
5
|
-
$scopes = [{}]
|
6
|
-
$scope_lvl = 0
|
7
5
|
$test_nodes = false
|
8
6
|
|
9
|
-
# Need to figure out how to move the scopes in here as class members in a sane way
|
10
7
|
class ScopeManager
|
8
|
+
def initialize(true_value, false_value)
|
9
|
+
@@true_value = true_value
|
10
|
+
@@false_value = false_value
|
11
|
+
@@scopes = [{}]
|
12
|
+
@@scope_lvl = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.true_value
|
16
|
+
@@true_value
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.false_value
|
20
|
+
@@false_value
|
21
|
+
end
|
22
|
+
|
11
23
|
def self.scope_lvl
|
12
|
-
|
24
|
+
@@scope_lvl
|
13
25
|
end
|
14
26
|
|
15
27
|
def self.scope_lvl=(value)
|
16
|
-
|
28
|
+
@@scope_lvl = value
|
17
29
|
end
|
18
30
|
|
19
31
|
def self.lookup_var(name)
|
20
|
-
temp_scope_lvl =
|
32
|
+
temp_scope_lvl = @@scope_lvl
|
21
33
|
while temp_scope_lvl >= 0
|
22
|
-
if
|
34
|
+
if !@@scopes[temp_scope_lvl].key?(name)
|
23
35
|
temp_scope_lvl -= 1
|
24
36
|
else
|
25
|
-
return
|
37
|
+
return @@scopes[temp_scope_lvl][name]
|
26
38
|
end
|
27
39
|
end
|
28
40
|
raise ArgumentError, "Variable '#{name}' is not defined" unless @value
|
29
41
|
end
|
30
42
|
|
31
43
|
def self.increment_scope_level
|
32
|
-
|
33
|
-
|
44
|
+
@@scope_lvl += 1
|
45
|
+
@@scopes.push({})
|
34
46
|
end
|
35
47
|
|
36
48
|
def self.decrement_scope_level
|
37
|
-
|
38
|
-
|
49
|
+
@@scope_lvl -= 1
|
50
|
+
@@scopes.pop
|
39
51
|
end
|
40
52
|
|
41
53
|
def self.lookup_func(name)
|
42
|
-
raise ArgumentError, "Function '#{name}' is not defined" if
|
43
|
-
return
|
54
|
+
raise ArgumentError, "Function '#{name}' is not defined" if @@scopes[0][name].is_a?(NilClass)
|
55
|
+
return @@scopes[0][name]
|
44
56
|
end
|
45
57
|
|
46
58
|
def self.add_to_current_scope(name, value)
|
47
|
-
|
59
|
+
@@scopes[@@scope_lvl][name.name] = value.evaluate
|
48
60
|
end
|
49
61
|
|
50
62
|
def self.add_func_to_global_scope(name, value, args)
|
51
|
-
|
63
|
+
@@scopes[0][name.name] = [value, args]
|
52
64
|
end
|
53
65
|
end
|
data/lib/rdparse.rb
CHANGED
@@ -20,8 +20,9 @@ class Rule
|
|
20
20
|
|
21
21
|
Match = Struct.new :pattern, :block
|
22
22
|
|
23
|
-
def initialize(name, parser)
|
23
|
+
def initialize(name, parser, debug_bool)
|
24
24
|
@logger = parser.logger
|
25
|
+
@debug_mode = debug_bool
|
25
26
|
# The name of the expressions this rule matches
|
26
27
|
@name = name
|
27
28
|
# We need the parser to recursively parse sub-expressions occurring
|
@@ -80,7 +81,7 @@ class Rule
|
|
80
81
|
result = nil
|
81
82
|
break
|
82
83
|
end
|
83
|
-
@logger.debug("Matched '#{@name} = #{match.pattern[index..-1].inspect}'")
|
84
|
+
@logger.debug("Matched '#{@name} = #{match.pattern[index..-1].inspect}'") if @debug_mode
|
84
85
|
else
|
85
86
|
# Otherwise, we consume the token as part of applying this rule
|
86
87
|
nt = @parser.expect(token)
|
@@ -91,7 +92,7 @@ class Rule
|
|
91
92
|
else
|
92
93
|
pattern = match.pattern
|
93
94
|
end
|
94
|
-
@logger.debug("Matched token '#{nt}' as part of rule '#{@name} <= #{pattern.inspect}'")
|
95
|
+
@logger.debug("Matched token '#{nt}' as part of rule '#{@name} <= #{pattern.inspect}'") if @debug_mode
|
95
96
|
else
|
96
97
|
result = nil
|
97
98
|
break
|
@@ -104,7 +105,7 @@ class Rule
|
|
104
105
|
else
|
105
106
|
match_result = result[0]
|
106
107
|
end
|
107
|
-
@logger.debug("'#{@parser.string[start..@parser.pos-1]}' matched '#{@name}' and generated '#{match_result.inspect}'") unless match_result.nil?
|
108
|
+
@logger.debug("'#{@parser.string[start..@parser.pos-1]}' matched '#{@name}' and generated '#{match_result.inspect}'") unless match_result.nil? if @debug_mode
|
108
109
|
break
|
109
110
|
else
|
110
111
|
# If this rule did not match the current token list, move
|
@@ -118,28 +119,45 @@ class Rule
|
|
118
119
|
end
|
119
120
|
|
120
121
|
class Parser
|
121
|
-
|
122
122
|
attr_accessor :pos
|
123
123
|
attr_reader :rules, :string, :logger
|
124
124
|
|
125
125
|
class ParseError < RuntimeError
|
126
126
|
end
|
127
127
|
|
128
|
-
def initialize(language_name, locale
|
128
|
+
def initialize(language_name, debug_bool, locale, &block)
|
129
|
+
@debug_mode = debug_bool
|
130
|
+
@logger = Logger.new(STDOUT) if @debug_mode
|
131
|
+
@lex_tokens = []
|
132
|
+
@rules = {}
|
133
|
+
@start = nil
|
134
|
+
@language_name = language_name
|
135
|
+
|
136
|
+
create_tokens_from_locale(locale)
|
137
|
+
|
138
|
+
false_value = @token_list['(false|true)'].split('|')[0]# Hacky solution to convoluted tokens
|
139
|
+
true_value = @token_list['(false|true)'].split('|')[1]
|
140
|
+
true_value = true_value[0..-2]
|
141
|
+
false_value = false_value[1..]
|
142
|
+
|
143
|
+
ScopeManager.new(true_value, false_value) # Hacky solution to convoluted tokens
|
144
|
+
|
145
|
+
instance_eval(&block)
|
146
|
+
end
|
129
147
|
|
130
|
-
|
148
|
+
# Recreate the tokenlist using locale_template and the chosen locale file
|
149
|
+
def create_tokens_from_locale(locale)
|
150
|
+
if locale == 'default'
|
151
|
+
lang_file = File.read("#{LOCALES_PATH}/#{locale}")
|
152
|
+
token_pairs = File.readlines("#{LOCALES_PATH}/#{lang_file}")
|
153
|
+
else
|
154
|
+
token_pairs = File.readlines("#{LOCALES_PATH}/#{locale}")
|
155
|
+
end
|
131
156
|
@token_list = Hash.new
|
132
157
|
token_pairs.each do |pair|
|
133
158
|
default_value, locale_value = pair.split(' ')
|
134
159
|
@token_list[default_value] = locale_value
|
135
160
|
end
|
136
|
-
|
137
|
-
@logger = Logger.new(STDOUT)
|
138
|
-
@lex_tokens = []
|
139
|
-
@rules = {}
|
140
|
-
@start = nil
|
141
|
-
@language_name = language_name
|
142
|
-
instance_eval(&block)
|
143
161
|
end
|
144
162
|
|
145
163
|
# Tokenize the string into small pieces
|
@@ -153,7 +171,7 @@ class Parser
|
|
153
171
|
match = tok.pattern.match(string)
|
154
172
|
# The regular expression of a token has matched the beginning of 'string'
|
155
173
|
if match
|
156
|
-
@logger.debug("Token #{match[0]} consumed as #{match[0]}")
|
174
|
+
@logger.debug("Token #{match[0]} consumed as #{match[0]}") if @debug_mode
|
157
175
|
# Also, evaluate this expression by using the block
|
158
176
|
# associated with the token
|
159
177
|
@tokens << tok.block.call(match.to_s) if tok.block
|
@@ -182,6 +200,11 @@ class Parser
|
|
182
200
|
result = @start.parse
|
183
201
|
# If there are unparsed extra tokens, signal error
|
184
202
|
if @pos != @tokens.size
|
203
|
+
|
204
|
+
if @tokens[@max_pos] == '(' || @tokens[@max_pos] == ')'
|
205
|
+
raise ParseError, 'Mismatched parenthesis! In Emacs: M-x check-parens RET'
|
206
|
+
end
|
207
|
+
|
185
208
|
raise ParseError, "Parse error. expected: '#{@expected.join(', ')}', found '#{@tokens[@max_pos]}'"
|
186
209
|
end
|
187
210
|
return result
|
@@ -223,7 +246,7 @@ class Parser
|
|
223
246
|
end
|
224
247
|
|
225
248
|
def rule(name,&block)
|
226
|
-
@current_rule = Rule.new(name, self)
|
249
|
+
@current_rule = Rule.new(name, self, @debug_mode)
|
227
250
|
@rules[name] = @current_rule
|
228
251
|
instance_eval &block # In practise, calls match 1..N times
|
229
252
|
@current_rule = nil
|
data/lib/tester/test_unit.rb
CHANGED
@@ -1,217 +1,241 @@
|
|
1
|
-
require
|
1
|
+
require "test/unit"
|
2
|
+
require_relative "../locale_lister"
|
3
|
+
|
4
|
+
locale_used = File.read("#{LOCALES_PATH}/#{"default"}")
|
5
|
+
words_array = clean_locale_file_to_array(locale_used)
|
6
|
+
word_pair_hash = {}
|
7
|
+
words_array.each_with_index do |word, index|
|
8
|
+
if index.even?
|
9
|
+
word_pair_hash[word] = words_array[index + 1]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
$while = word_pair_hash["while"]
|
14
|
+
$print = word_pair_hash["print"]
|
15
|
+
$pause = word_pair_hash["pause"]
|
16
|
+
$def = word_pair_hash["def"]
|
17
|
+
$end = word_pair_hash["end"]
|
18
|
+
$if = word_pair_hash["if"]
|
19
|
+
$else_if = word_pair_hash["elseif"]
|
20
|
+
$else = word_pair_hash["else"]
|
21
|
+
$false = word_pair_hash["false|true"].split("|")[0]
|
22
|
+
$true = word_pair_hash["false|true"].split("|")[1]
|
23
|
+
$not = word_pair_hash["not"]
|
24
|
+
$and = word_pair_hash["and"]
|
25
|
+
$or = word_pair_hash["or"]
|
2
26
|
|
3
27
|
class TestArithmetic < Test::Unit::TestCase
|
4
28
|
def test_addition
|
5
|
-
assert_equal(3, LangParser.new.calc_test(
|
6
|
-
assert_equal(3, LangParser.new.calc_test(
|
7
|
-
assert_equal(3, LangParser.new.calc_test(
|
8
|
-
|
9
|
-
assert_equal(6, LangParser.new.calc_test(
|
10
|
-
assert_equal(6, LangParser.new.calc_test(
|
11
|
-
assert_equal(6, LangParser.new.calc_test(
|
12
|
-
assert_equal(10, LangParser.new.calc_test(
|
13
|
-
assert_equal(10, LangParser.new.calc_test(
|
29
|
+
assert_equal(3, LangParser.new.calc_test("1 + 2"))
|
30
|
+
assert_equal(3, LangParser.new.calc_test("(1 + 2)"))
|
31
|
+
assert_equal(3, LangParser.new.calc_test("((1 + 2))"))
|
32
|
+
|
33
|
+
assert_equal(6, LangParser.new.calc_test("1 + 2 + 3"))
|
34
|
+
assert_equal(6, LangParser.new.calc_test("(1 + 2) + 3"))
|
35
|
+
assert_equal(6, LangParser.new.calc_test("((1 + 2) + 3)"))
|
36
|
+
assert_equal(10, LangParser.new.calc_test("((1 + 2) + 3) + 4"))
|
37
|
+
assert_equal(10, LangParser.new.calc_test("4 + ((3) + (2 + 1))"))
|
14
38
|
end
|
15
39
|
|
16
40
|
def test_subtraction
|
17
|
-
assert_equal(1, LangParser.new.calc_test(
|
18
|
-
assert_equal(-3, LangParser.new.calc_test(
|
19
|
-
assert_equal(1, LangParser.new.calc_test(
|
20
|
-
assert_equal(1, LangParser.new.calc_test(
|
21
|
-
|
22
|
-
assert_equal(0, LangParser.new.calc_test(
|
23
|
-
assert_equal(0, LangParser.new.calc_test(
|
24
|
-
assert_equal(0, LangParser.new.calc_test(
|
25
|
-
assert_equal(2, LangParser.new.calc_test(
|
26
|
-
assert_equal(2, LangParser.new.calc_test(
|
27
|
-
assert_equal(4, LangParser.new.calc_test(
|
41
|
+
assert_equal(1, LangParser.new.calc_test("2 - 1"))
|
42
|
+
assert_equal(-3, LangParser.new.calc_test("(-2) - 1"))
|
43
|
+
assert_equal(1, LangParser.new.calc_test("(2 - 1)"))
|
44
|
+
assert_equal(1, LangParser.new.calc_test("((2 - 1))"))
|
45
|
+
|
46
|
+
assert_equal(0, LangParser.new.calc_test("3 - 2 - 1"))
|
47
|
+
assert_equal(0, LangParser.new.calc_test("(3 - 2) - 1"))
|
48
|
+
assert_equal(0, LangParser.new.calc_test("((3 - 2)) - 1"))
|
49
|
+
assert_equal(2, LangParser.new.calc_test("(4 - (3 - 2)) - 1"))
|
50
|
+
assert_equal(2, LangParser.new.calc_test("(4 - ((3) - 2)) - 1"))
|
51
|
+
assert_equal(4, LangParser.new.calc_test("4 - (((3) - 2) - 1)"))
|
28
52
|
end
|
29
53
|
|
30
54
|
def test_multiply
|
31
|
-
assert_equal(2, LangParser.new.calc_test(
|
32
|
-
assert_equal(2, LangParser.new.calc_test(
|
33
|
-
assert_equal(2, LangParser.new.calc_test(
|
34
|
-
assert_equal(2, LangParser.new.calc_test(
|
35
|
-
|
36
|
-
assert_equal(6, LangParser.new.calc_test(
|
37
|
-
assert_equal(6, LangParser.new.calc_test(
|
38
|
-
assert_equal(6, LangParser.new.calc_test(
|
39
|
-
assert_equal(18, LangParser.new.calc_test(
|
40
|
-
assert_equal(-6, LangParser.new.calc_test(
|
55
|
+
assert_equal(2, LangParser.new.calc_test("2 * 1"))
|
56
|
+
assert_equal(2, LangParser.new.calc_test("1 * 2"))
|
57
|
+
assert_equal(2, LangParser.new.calc_test("1 * (2)"))
|
58
|
+
assert_equal(2, LangParser.new.calc_test("(1) * (2)"))
|
59
|
+
|
60
|
+
assert_equal(6, LangParser.new.calc_test("(1) * (2 * 3)"))
|
61
|
+
assert_equal(6, LangParser.new.calc_test("(1 * 2 * 3)"))
|
62
|
+
assert_equal(6, LangParser.new.calc_test("(1 * 2) * 3"))
|
63
|
+
assert_equal(18, LangParser.new.calc_test("(3) * 2 * (3)"))
|
64
|
+
assert_equal(-6, LangParser.new.calc_test("(3) * (0 - 2) * (1)"))
|
41
65
|
end
|
42
66
|
|
43
67
|
def test_division
|
44
|
-
assert_equal(2, LangParser.new.calc_test(
|
45
|
-
assert_equal(1, LangParser.new.calc_test(
|
46
|
-
assert_equal(0.5, LangParser.new.calc_test(
|
47
|
-
assert_equal(-0.5, LangParser.new.calc_test(
|
48
|
-
assert_equal(-2.5, LangParser.new.calc_test(
|
49
|
-
|
50
|
-
assert_equal(0, LangParser.new.calc_test(
|
51
|
-
assert_equal(1, LangParser.new.calc_test(
|
52
|
-
assert_equal(1, LangParser.new.calc_test(
|
53
|
-
assert_equal(3, LangParser.new.calc_test(
|
68
|
+
assert_equal(2, LangParser.new.calc_test("2 / 1"))
|
69
|
+
assert_equal(1, LangParser.new.calc_test("2 / 2"))
|
70
|
+
assert_equal(0.5, LangParser.new.calc_test("1 / 2"))
|
71
|
+
assert_equal(-0.5, LangParser.new.calc_test("-1 / 2"))
|
72
|
+
assert_equal(-2.5, LangParser.new.calc_test("-5 / 2"))
|
73
|
+
|
74
|
+
assert_equal(0, LangParser.new.calc_test("(-1 + 1) / 2"))
|
75
|
+
assert_equal(1, LangParser.new.calc_test("(3 / (3 / 1)) / 1"))
|
76
|
+
assert_equal(1, LangParser.new.calc_test("(3 / (3 / 1)) / (1)"))
|
77
|
+
assert_equal(3, LangParser.new.calc_test("(3 / (3 / 1-1-1)) / (1)"))
|
54
78
|
end
|
55
79
|
end
|
56
80
|
|
57
81
|
class TestLogic < Test::Unit::TestCase
|
58
82
|
def test_logic
|
59
|
-
assert_equal(true, LangParser.new.calc_test(
|
60
|
-
assert_equal(true, LangParser.new.calc_test(
|
61
|
-
assert_equal(true, LangParser.new.calc_test(
|
62
|
-
assert_equal(true, LangParser.new.calc_test(
|
63
|
-
assert_equal(true, LangParser.new.calc_test(
|
83
|
+
assert_equal(true, LangParser.new.calc_test("#{$true}"))
|
84
|
+
assert_equal(true, LangParser.new.calc_test("#{$true} #{$and} #{$true}"))
|
85
|
+
assert_equal(true, LangParser.new.calc_test("#{$true} #{$or} #{$true}"))
|
86
|
+
assert_equal(true, LangParser.new.calc_test("#{$true} #{$or} #{$false}"))
|
87
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} #{$false}"))
|
64
88
|
|
65
|
-
assert_equal(false, LangParser.new.calc_test(
|
66
|
-
assert_equal(false, LangParser.new.calc_test(
|
67
|
-
assert_equal(false, LangParser.new.calc_test(
|
68
|
-
assert_equal(false, LangParser.new.calc_test(
|
89
|
+
assert_equal(false, LangParser.new.calc_test("#{$false}"))
|
90
|
+
assert_equal(false, LangParser.new.calc_test("#{$false} #{$and} #{$false}"))
|
91
|
+
assert_equal(false, LangParser.new.calc_test("#{$false} #{$and} #{$true}"))
|
92
|
+
assert_equal(false, LangParser.new.calc_test("#{$not} #{$true}"))
|
69
93
|
|
70
|
-
assert_equal(true, LangParser.new.calc_test(
|
71
|
-
assert_equal(true, LangParser.new.calc_test(
|
72
|
-
assert_equal(true, LangParser.new.calc_test(
|
94
|
+
assert_equal(true, LangParser.new.calc_test("(#{$not} #{$false}) #{$and} #{$true}"))
|
95
|
+
assert_equal(true, LangParser.new.calc_test("(#{$not} #{$false}) #{$or} #{$false}"))
|
96
|
+
assert_equal(true, LangParser.new.calc_test("#{$true} #{$and} #{$true} #{$or} #{$false}"))
|
73
97
|
|
74
98
|
end
|
75
99
|
end
|
76
100
|
|
77
101
|
class TestComparisons < Test::Unit::TestCase
|
78
102
|
def test_comparisons
|
79
|
-
assert_equal(true, LangParser.new.calc_test(
|
80
|
-
assert_equal(false, LangParser.new.calc_test(
|
81
|
-
assert_equal(true, LangParser.new.calc_test(
|
82
|
-
assert_equal(false, LangParser.new.calc_test(
|
83
|
-
assert_equal(false, LangParser.new.calc_test(
|
84
|
-
assert_equal(true, LangParser.new.calc_test(
|
85
|
-
assert_equal(true, LangParser.new.calc_test(
|
86
|
-
assert_equal(true, LangParser.new.calc_test(
|
87
|
-
assert_equal(true, LangParser.new.calc_test(
|
88
|
-
|
89
|
-
assert_equal(true, LangParser.new.calc_test(
|
90
|
-
assert_equal(true, LangParser.new.calc_test(
|
91
|
-
assert_equal(true, LangParser.new.calc_test(
|
92
|
-
|
93
|
-
assert_equal(true, LangParser.new.calc_test(
|
94
|
-
assert_equal(false, LangParser.new.calc_test(
|
95
|
-
assert_equal(true, LangParser.new.calc_test(
|
96
|
-
assert_equal(false, LangParser.new.calc_test(
|
97
|
-
|
98
|
-
assert_equal(true, LangParser.new.calc_test(
|
99
|
-
assert_equal(false, LangParser.new.calc_test(
|
100
|
-
assert_equal(true, LangParser.new.calc_test(
|
101
|
-
|
102
|
-
assert_equal(true, LangParser.new.calc_test(
|
103
|
-
assert_equal(true, LangParser.new.calc_test(
|
104
|
-
assert_equal(false, LangParser.new.calc_test(
|
103
|
+
assert_equal(true, LangParser.new.calc_test("1 < 2"))
|
104
|
+
assert_equal(false, LangParser.new.calc_test("1 > 2"))
|
105
|
+
assert_equal(true, LangParser.new.calc_test("1 < 2 #{$and} #{$true}"))
|
106
|
+
assert_equal(false, LangParser.new.calc_test("1 > 2 #{$and} #{$true}"))
|
107
|
+
assert_equal(false, LangParser.new.calc_test("1 > 2 + 3 #{$and} #{$true}"))
|
108
|
+
assert_equal(true, LangParser.new.calc_test("1+2 < 2+2 #{$and} #{$true}"))
|
109
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} 1 > 2 #{$and} #{$true}"))
|
110
|
+
assert_equal(true, LangParser.new.calc_test(" #{$true} #{$and} #{$not} 1 > 2"))
|
111
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} 1 > 2 #{$and} #{$true}"))
|
112
|
+
|
113
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} 1 > 2 #{$and} #{$true} #{$or} #{$false}"))
|
114
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} 1 > 2 #{$and} (#{$true} #{$or} #{$false})"))
|
115
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} 1 > 2 #{$and} #{$false} #{$or} #{$true} #{$and} #{$true}"))
|
116
|
+
|
117
|
+
assert_equal(true, LangParser.new.calc_test(" 1 == 1"))
|
118
|
+
assert_equal(false, LangParser.new.calc_test(" 1 == 2"))
|
119
|
+
assert_equal(true, LangParser.new.calc_test(" #{$not} 1 == 2"))
|
120
|
+
assert_equal(false, LangParser.new.calc_test(" #{$not} 1 == 1"))
|
121
|
+
|
122
|
+
assert_equal(true, LangParser.new.calc_test(" 1 <= 1"))
|
123
|
+
assert_equal(false, LangParser.new.calc_test(" 3 <= 2"))
|
124
|
+
assert_equal(true, LangParser.new.calc_test(" 1 <= 2"))
|
125
|
+
|
126
|
+
assert_equal(true, LangParser.new.calc_test(" 1 >= 1"))
|
127
|
+
assert_equal(true, LangParser.new.calc_test(" 3 >= 2"))
|
128
|
+
assert_equal(false, LangParser.new.calc_test(" 1 >= 2"))
|
105
129
|
end
|
106
130
|
end
|
107
131
|
|
108
132
|
class TestIf < Test::Unit::TestCase
|
109
133
|
def test_if
|
110
|
-
assert_equal(2, LangParser.new.calc_test(
|
134
|
+
assert_equal(2, LangParser.new.calc_test(" #{$if} #{$true}
|
111
135
|
1+1
|
112
|
-
end
|
136
|
+
#{$end}"))
|
113
137
|
|
114
|
-
assert_equal(2, LangParser.new.calc_test(
|
138
|
+
assert_equal(2, LangParser.new.calc_test(" #{$if} 3 == 3
|
115
139
|
1+1
|
116
|
-
end
|
140
|
+
#{$end}"))
|
117
141
|
|
118
|
-
assert_equal(nil, LangParser.new.calc_test(
|
142
|
+
assert_equal(nil, LangParser.new.calc_test("#{$if} 3 == 2
|
119
143
|
1+1
|
120
|
-
end
|
144
|
+
#{$end}"))
|
121
145
|
end
|
122
146
|
end
|
123
147
|
class TestVariables < Test::Unit::TestCase
|
124
148
|
def test_variables
|
125
|
-
assert_equal(1, LangParser.new.calc_test(
|
126
|
-
x
|
149
|
+
assert_equal(1, LangParser.new.calc_test("x = 1
|
150
|
+
x"))
|
127
151
|
|
128
|
-
assert_equal(1, LangParser.new.calc_test(
|
152
|
+
assert_equal(1, LangParser.new.calc_test("x = 1
|
129
153
|
x
|
130
|
-
x
|
154
|
+
x"))
|
131
155
|
|
132
|
-
assert_equal(1, LangParser.new.calc_test(
|
156
|
+
assert_equal(1, LangParser.new.calc_test("x = 1
|
133
157
|
x
|
134
158
|
x + 1
|
135
|
-
x
|
159
|
+
x"))
|
136
160
|
|
137
|
-
assert_equal(3, LangParser.new.calc_test(
|
138
|
-
x + 2
|
161
|
+
assert_equal(3, LangParser.new.calc_test("x = 1
|
162
|
+
x + 2"))
|
139
163
|
|
140
|
-
assert_equal(2, LangParser.new.calc_test(
|
164
|
+
assert_equal(2, LangParser.new.calc_test("x = 1
|
141
165
|
x = 2
|
142
|
-
x
|
166
|
+
x"))
|
143
167
|
|
144
|
-
assert_equal(5, LangParser.new.calc_test(
|
145
|
-
x + 2
|
168
|
+
assert_equal(5, LangParser.new.calc_test("x = 1 + 2
|
169
|
+
x + 2"))
|
146
170
|
|
147
|
-
assert_equal(5, LangParser.new.calc_test(
|
171
|
+
assert_equal(5, LangParser.new.calc_test("x = 1 + 2
|
148
172
|
x = x + 2
|
149
|
-
x
|
173
|
+
x"))
|
150
174
|
end
|
151
175
|
end
|
152
176
|
|
153
177
|
class TestFunction < Test::Unit::TestCase
|
154
178
|
def test_function
|
155
|
-
assert_equal(nil, LangParser.new.calc_test(
|
179
|
+
assert_equal(nil, LangParser.new.calc_test(" #{$def} foo()
|
156
180
|
5+5
|
157
|
-
end
|
181
|
+
#{$end}"))
|
158
182
|
|
159
|
-
assert_equal(10, LangParser.new.calc_test(
|
183
|
+
assert_equal(10, LangParser.new.calc_test(" #{$def} foo()
|
160
184
|
5+5
|
161
|
-
end
|
162
|
-
foo()
|
185
|
+
#{$end}
|
186
|
+
foo()"))
|
163
187
|
|
164
|
-
assert_equal(10, LangParser.new.calc_test(
|
188
|
+
assert_equal(10, LangParser.new.calc_test(" #{$def} boo()
|
165
189
|
a = 5
|
166
190
|
a
|
167
|
-
end
|
191
|
+
#{$end}
|
168
192
|
b = 5 + boo()
|
169
|
-
b
|
193
|
+
b"))
|
170
194
|
end
|
171
195
|
end
|
172
196
|
|
173
197
|
class TestLoop < Test::Unit::TestCase
|
174
198
|
def test_while
|
175
|
-
assert_equal(WhileLoopNode, LangParser.new.calc_test(
|
176
|
-
while x < 10
|
199
|
+
assert_equal(WhileLoopNode, LangParser.new.calc_test("x = 5
|
200
|
+
#{$while} x < 10
|
177
201
|
x = x + 1
|
178
|
-
end
|
202
|
+
#{$end}"))
|
179
203
|
|
180
|
-
assert_equal(10, LangParser.new.calc_test(
|
181
|
-
while x < 10
|
204
|
+
assert_equal(10, LangParser.new.calc_test("x = 5
|
205
|
+
#{$while} x < 10
|
182
206
|
x = x + 1
|
183
|
-
end
|
184
|
-
x
|
207
|
+
#{$end}
|
208
|
+
x"))
|
185
209
|
|
186
|
-
assert_equal(-10, LangParser.new.calc_test(
|
187
|
-
while x > -10
|
210
|
+
assert_equal(-10, LangParser.new.calc_test("x = 0
|
211
|
+
#{$while} x > -10
|
188
212
|
x = x - 1
|
189
|
-
end
|
190
|
-
x
|
213
|
+
#{$end}
|
214
|
+
x"))
|
191
215
|
|
192
|
-
# assert_equal(0, LangParser.new.calc_test(
|
193
|
-
# while -x < 0 # fundera på negering av variabler
|
216
|
+
# assert_equal(0, LangParser.new.calc_test("x = 10
|
217
|
+
# #{$while} -x < 0 # fundera på negering av variabler
|
194
218
|
# x = x + 1
|
195
|
-
# end
|
196
|
-
# x
|
219
|
+
# #{$end}
|
220
|
+
# x"))
|
197
221
|
end
|
198
222
|
end
|
199
223
|
|
200
224
|
class TesCompactIf < Test::Unit::TestCase
|
201
225
|
def test_CompactIf
|
202
|
-
assert_equal(4, LangParser.new.calc_test(
|
203
|
-
if 7 <= 10 and > 5
|
226
|
+
assert_equal(4, LangParser.new.calc_test("x = 4
|
227
|
+
#{$if} 7 <= 10 #{$and} > 5
|
204
228
|
x
|
205
|
-
end
|
229
|
+
#{$end}"))
|
206
230
|
|
207
|
-
assert_equal(8, LangParser.new.calc_test(
|
208
|
-
if x < 10 and > 5
|
231
|
+
assert_equal(8, LangParser.new.calc_test("x = 8
|
232
|
+
#{$if} x < 10 #{$and} > 5
|
209
233
|
x
|
210
|
-
end
|
234
|
+
#{$end}"))
|
211
235
|
|
212
|
-
assert_equal(nil, LangParser.new.calc_test(
|
213
|
-
if x <= 10 and >= 5
|
236
|
+
assert_equal(nil, LangParser.new.calc_test("x = 2
|
237
|
+
#{$if} x <= 10 #{$and} >= 5
|
214
238
|
x
|
215
|
-
end
|
239
|
+
#{$end}"))
|
216
240
|
end
|
217
241
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alphalang
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mattias
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logger
|
@@ -73,7 +73,6 @@ files:
|
|
73
73
|
- lib/nodes/scopemanager.rb
|
74
74
|
- lib/nodes/stmtnodes.rb
|
75
75
|
- lib/rdparse.rb
|
76
|
-
- lib/rdparse_quiet.rb
|
77
76
|
- lib/tester/demo_de.alpha
|
78
77
|
- lib/tester/demo_sv.alpha
|
79
78
|
- lib/tester/fibonacci.alpha
|
data/lib/rdparse_quiet.rb
DELETED
@@ -1,236 +0,0 @@
|
|
1
|
-
# This file is called rdparse.rb because it implements a Recursive
|
2
|
-
# Descent Parser. Read more about the theory on e.g.
|
3
|
-
# http://en.wikipedia.org/wiki/Recursive_descent_parser
|
4
|
-
|
5
|
-
# 2010-02-11 New version of this file for the 2010 instance of TDP007
|
6
|
-
# which handles false return values during parsing, and has an easy way
|
7
|
-
# of turning on and off debug messages.
|
8
|
-
# 2014-02-16 New version that handles { false } blocks and :empty tokens.
|
9
|
-
|
10
|
-
require 'logger'
|
11
|
-
|
12
|
-
|
13
|
-
class Rule
|
14
|
-
|
15
|
-
# A rule is created through the rule method of the Parser class, like this:
|
16
|
-
# rule :term do
|
17
|
-
# match(:term, '*', :dice) {|a, _, b| a * b }
|
18
|
-
# match(:term, '/', :dice) {|a, _, b| a / b }
|
19
|
-
# match(:dice)
|
20
|
-
# end
|
21
|
-
|
22
|
-
Match = Struct.new :pattern, :block
|
23
|
-
|
24
|
-
def initialize(name, parser)
|
25
|
-
|
26
|
-
# The name of the expressions this rule matches
|
27
|
-
@name = name
|
28
|
-
# We need the parser to recursively parse sub-expressions occurring
|
29
|
-
# within the pattern of the match objects associated with this rule
|
30
|
-
@parser = parser
|
31
|
-
@matches = []
|
32
|
-
# Left-recursive matches
|
33
|
-
@lrmatches = []
|
34
|
-
end
|
35
|
-
|
36
|
-
# Add a matching expression to this rule, as in this example:
|
37
|
-
# match(:term, '*', :dice) {|a, _, b| a * b }
|
38
|
-
# The arguments to 'match' describe the constituents of this expression.
|
39
|
-
def match(*pattern, &block)
|
40
|
-
match = Match.new(pattern, block)
|
41
|
-
# If the pattern is left-recursive, then add it to the left-recursive set
|
42
|
-
if pattern[0] == @name
|
43
|
-
pattern.shift
|
44
|
-
@lrmatches << match
|
45
|
-
else
|
46
|
-
@matches << match
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def parse
|
51
|
-
# Try non-left-recursive matches first, to avoid infinite recursion
|
52
|
-
match_result = try_matches(@matches)
|
53
|
-
return nil if match_result.nil?
|
54
|
-
loop do
|
55
|
-
result = try_matches(@lrmatches, match_result)
|
56
|
-
return match_result if result.nil?
|
57
|
-
match_result = result
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
# Try out all matching patterns of this rule
|
64
|
-
def try_matches(matches, pre_result = nil)
|
65
|
-
match_result = nil
|
66
|
-
# Begin at the current position in the input string of the parser
|
67
|
-
start = @parser.pos
|
68
|
-
matches.each do |match|
|
69
|
-
# pre_result is a previously available result from evaluating expressions
|
70
|
-
result = pre_result.nil? ? [] : [pre_result]
|
71
|
-
|
72
|
-
# We iterate through the parts of the pattern, which may be e.g.
|
73
|
-
# [:expr,'*',:term]
|
74
|
-
match.pattern.each_with_index do |token,index|
|
75
|
-
|
76
|
-
# If this "token" is a compound term, add the result of
|
77
|
-
# parsing it to the "result" array
|
78
|
-
if @parser.rules[token]
|
79
|
-
result << @parser.rules[token].parse
|
80
|
-
if result.last.nil?
|
81
|
-
result = nil
|
82
|
-
break
|
83
|
-
end
|
84
|
-
|
85
|
-
else
|
86
|
-
# Otherwise, we consume the token as part of applying this rule
|
87
|
-
nt = @parser.expect(token)
|
88
|
-
if nt
|
89
|
-
result << nt
|
90
|
-
if @lrmatches.include?(match.pattern) then
|
91
|
-
pattern = [@name]+match.pattern
|
92
|
-
else
|
93
|
-
pattern = match.pattern
|
94
|
-
end
|
95
|
-
|
96
|
-
else
|
97
|
-
result = nil
|
98
|
-
break
|
99
|
-
end
|
100
|
-
end # pattern.each
|
101
|
-
end # matches.each
|
102
|
-
if result
|
103
|
-
if match.block
|
104
|
-
match_result = match.block.call(*result)
|
105
|
-
else
|
106
|
-
match_result = result[0]
|
107
|
-
end
|
108
|
-
|
109
|
-
break
|
110
|
-
else
|
111
|
-
# If this rule did not match the current token list, move
|
112
|
-
# back to the scan position of the last match
|
113
|
-
@parser.pos = start
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
return match_result
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class Parser
|
122
|
-
|
123
|
-
attr_accessor :pos
|
124
|
-
attr_reader :rules, :string, :logger
|
125
|
-
|
126
|
-
class ParseError < RuntimeError
|
127
|
-
end
|
128
|
-
|
129
|
-
def initialize(language_name, locale = 'default', &block)
|
130
|
-
|
131
|
-
token_pairs = File.readlines("#{LOCALES_PATH}/#{locale}")
|
132
|
-
@token_list = Hash.new
|
133
|
-
token_pairs.each do |pair|
|
134
|
-
default_value, locale_value = pair.split(' ')
|
135
|
-
@token_list[default_value] = locale_value
|
136
|
-
end
|
137
|
-
|
138
|
-
@lex_tokens = []
|
139
|
-
@rules = {}
|
140
|
-
@start = nil
|
141
|
-
@language_name = language_name
|
142
|
-
instance_eval(&block)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Tokenize the string into small pieces
|
146
|
-
def tokenize(string)
|
147
|
-
@tokens = []
|
148
|
-
@string = string.clone
|
149
|
-
until string.empty?
|
150
|
-
# Unless any of the valid tokens of our language are the prefix of
|
151
|
-
# 'string', we fail with an exception
|
152
|
-
raise ParseError, "unable to lex '#{string}" unless @lex_tokens.any? do |tok|
|
153
|
-
match = tok.pattern.match(string)
|
154
|
-
# The regular expression of a token has matched the beginning of 'string'
|
155
|
-
if match
|
156
|
-
|
157
|
-
# Also, evaluate this expression by using the block
|
158
|
-
# associated with the token
|
159
|
-
@tokens << tok.block.call(match.to_s) if tok.block
|
160
|
-
# consume the match and proceed with the rest of the string
|
161
|
-
string = match.post_match
|
162
|
-
true
|
163
|
-
else
|
164
|
-
# this token pattern did not match, try the next
|
165
|
-
false
|
166
|
-
end # if
|
167
|
-
end # raise
|
168
|
-
end # until
|
169
|
-
end
|
170
|
-
|
171
|
-
def parse(string)
|
172
|
-
# First, split the string according to the "token" instructions given.
|
173
|
-
# Afterwards @tokens contains all tokens that are to be parsed.
|
174
|
-
tokenize(string)
|
175
|
-
|
176
|
-
# These variables are used to match if the total number of tokens
|
177
|
-
# are consumed by the parser
|
178
|
-
@pos = 0
|
179
|
-
@max_pos = 0
|
180
|
-
@expected = []
|
181
|
-
# Parse (and evaluate) the tokens received
|
182
|
-
result = @start.parse
|
183
|
-
# If there are unparsed extra tokens, signal error
|
184
|
-
if @pos != @tokens.size
|
185
|
-
raise ParseError, "Parse error. expected: '#{@expected.join(', ')}', found '#{@tokens[@max_pos]}'"
|
186
|
-
end
|
187
|
-
return result
|
188
|
-
end
|
189
|
-
|
190
|
-
def next_token
|
191
|
-
@pos += 1
|
192
|
-
return @tokens[@pos - 1]
|
193
|
-
end
|
194
|
-
|
195
|
-
# Return the next token in the queue
|
196
|
-
def expect(tok)
|
197
|
-
return tok if tok == :empty
|
198
|
-
t = next_token
|
199
|
-
if @pos - 1 > @max_pos
|
200
|
-
@max_pos = @pos - 1
|
201
|
-
@expected = []
|
202
|
-
end
|
203
|
-
return t if tok === t
|
204
|
-
@expected << tok if @max_pos == @pos - 1 && !@expected.include?(tok)
|
205
|
-
return nil
|
206
|
-
end
|
207
|
-
|
208
|
-
def to_s
|
209
|
-
"Parser for #{@language_name}"
|
210
|
-
end
|
211
|
-
|
212
|
-
private
|
213
|
-
|
214
|
-
LexToken = Struct.new(:pattern, :block)
|
215
|
-
|
216
|
-
def token(pattern, &block)
|
217
|
-
@lex_tokens << LexToken.new(Regexp.new('\\A' + @token_list[pattern.source]), block)
|
218
|
-
end
|
219
|
-
|
220
|
-
def start(name, &block)
|
221
|
-
rule(name, &block)
|
222
|
-
@start = @rules[name]
|
223
|
-
end
|
224
|
-
|
225
|
-
def rule(name,&block)
|
226
|
-
@current_rule = Rule.new(name, self)
|
227
|
-
@rules[name] = @current_rule
|
228
|
-
instance_eval &block # In practise, calls match 1..N times
|
229
|
-
@current_rule = nil
|
230
|
-
end
|
231
|
-
|
232
|
-
def match(*pattern, &block)
|
233
|
-
# Basically calls memberfunction "match(*pattern, &block)
|
234
|
-
@current_rule.send(:match, *pattern, &block)
|
235
|
-
end
|
236
|
-
end
|