alphalang 0.1.5 → 0.1.7
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 +3 -8
- data/lib/alpha.rb +5 -4
- data/lib/lang_creator.rb +1 -7
- 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 +4 -3
- data/lib/nodes/scopemanager.rb +28 -16
- data/lib/nodes/stmtnodes.rb +3 -0
- data/lib/rdparse.rb +111 -17
- data/lib/tester/test_unit.rb +155 -131
- metadata +3 -4
- 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: 1f69af7ab6e40605d9118d7125caf0c99ea961bd20108210a91d77ea790e0489
|
|
4
|
+
data.tar.gz: 0bd9593abf719ba7e5704a873f3d5d14aff15a20d424bcc858e7d9a7e45b22f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 34c909f692a34e3eae601ec4eddcb6361e46480d74a857b277fdbe652d65b2cfa99b6c427661fea5c64720a2122b28e8644f079df2e76b267e53c9bf11c8d26f
|
|
7
|
+
data.tar.gz: 0aebab81771209c1f955abd3954e0ba7b9d4c8544fe2d308779d56d19669085b8cb925e6fbdda1cedd3c6b4a7755b95caa17b509bc9f948e85359025aefaf530
|
data/bin/alphalang
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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
|
-
ABORT_ANSWERS = [' ', '', 'none', 'abort'].freeze
|
|
6
|
+
ABORT_ANSWERS = [' ', '', 'none', 'abort'].freeze
|
|
6
7
|
LOCALES_PATH = File.join(__dir__, '../lib/locales')
|
|
7
8
|
PROTECTED_LOCALES = ['.', '..', 'locale_template', 'default', 'default.old', 'en', 'sv', 'de'].freeze
|
|
8
9
|
|
|
@@ -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,8 @@
|
|
|
1
1
|
require_relative './nodes/scopemanager'
|
|
2
2
|
|
|
3
3
|
class LangParser
|
|
4
|
-
def initialize(locale = 'default')
|
|
5
|
-
@langParser = Parser.new('lang parser', locale) do
|
|
4
|
+
def initialize(locale = 'default', debug_mode = false)
|
|
5
|
+
@langParser = Parser.new('lang parser', debug_mode, locale) do
|
|
6
6
|
token(/;;.*$/)
|
|
7
7
|
token(/while/) { |_| :WHILE }
|
|
8
8
|
token(/print/) { |_| :PRINT }
|
|
@@ -23,7 +23,7 @@ class LangParser
|
|
|
23
23
|
token(/./) { |m| m }
|
|
24
24
|
|
|
25
25
|
start :program do
|
|
26
|
-
match(:comp_stmt)
|
|
26
|
+
match(:comp_stmt)
|
|
27
27
|
match(:terminal)
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -136,7 +136,8 @@ class LangParser
|
|
|
136
136
|
match(/\d+/, '.', /\d+/) { |a, dot, b| NumberNode.new((a+dot+b)) }
|
|
137
137
|
match('-', /\d+/) { |neg, a| NumberNode.new(neg + a) }
|
|
138
138
|
match(/\d+/) { |a| NumberNode.new(a) }
|
|
139
|
-
match(
|
|
139
|
+
match(ScopeManager.true_value) { |a| BoolNode.new(a) }
|
|
140
|
+
match(ScopeManager.false_value) { |a| BoolNode.new(a) }
|
|
140
141
|
match(:PRINT, :member) { |_, a| PrintNode.new(a) }
|
|
141
142
|
match(:PRINT, :expr) { |_, a| PrintNode.new(a) }
|
|
142
143
|
match(:member)
|
data/lib/lang_creator.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
require_relative 'locale_lister'
|
|
3
3
|
|
|
4
|
-
# Function to read user input for translation
|
|
5
4
|
def read_translation(line)
|
|
6
5
|
puts "Enter the translation for: '#{line}' <RET> to accept '#{line}'"
|
|
7
6
|
translation = gets.chomp
|
|
@@ -73,21 +72,16 @@ def create_locale_file()
|
|
|
73
72
|
return
|
|
74
73
|
end
|
|
75
74
|
|
|
76
|
-
# prompt user for translations
|
|
77
75
|
prompt_user(new_locale_file_path)
|
|
78
|
-
|
|
79
|
-
# Clear the screen
|
|
80
76
|
system('clear')
|
|
81
77
|
|
|
82
|
-
# Display the contents of the translation file
|
|
83
78
|
locale_as_array = clean_locale_file_to_array(new_locale_name)
|
|
84
79
|
print_clean_locale_array(new_locale_name, locale_as_array)
|
|
85
80
|
|
|
86
|
-
# Ask user if the translation is correct
|
|
87
81
|
puts 'Is this correct? [Y/n]'
|
|
88
82
|
answer = gets.chomp
|
|
89
83
|
|
|
90
|
-
if answer.downcase
|
|
84
|
+
if answer.downcase.match? /y|Y/ or answer.empty?
|
|
91
85
|
puts "Locale is saved as #{new_locale_name} in #{LOCALES_PATH}"
|
|
92
86
|
else
|
|
93
87
|
puts 'Translation removed'
|
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
|
|
@@ -149,12 +149,13 @@ class CompStmtNode < Node
|
|
|
149
149
|
|
|
150
150
|
def evaluate
|
|
151
151
|
@comp_statements[0].evaluate
|
|
152
|
-
@comp_statements[1].evaluate
|
|
152
|
+
@comp_statements[1].evaluate
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
####################################################
|
|
157
157
|
|
|
158
|
+
#Arraynoden är ofärdig, används inte korrekt
|
|
158
159
|
class ArrayNode < Node
|
|
159
160
|
attr_accessor :lhs, :rhs
|
|
160
161
|
|
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/nodes/stmtnodes.rb
CHANGED
|
@@ -72,12 +72,14 @@ class FuncCallNode < Node
|
|
|
72
72
|
ScopeManager.decrement_scope_level
|
|
73
73
|
|
|
74
74
|
# If function return value is an "Assign" then we declare that variable in the global scope.
|
|
75
|
+
# This should be a ScopeManager method, add_variable_to_global_scope is missing.
|
|
75
76
|
old_scope_lvl = ScopeManager.scope_lvl
|
|
76
77
|
if func_return_value.is_a?(VariableDecNode)
|
|
77
78
|
ScopeManager.scope_lvl = 0
|
|
78
79
|
func_return_value.evaluate
|
|
79
80
|
ScopeManager.scope_lvl = old_scope_lvl
|
|
80
81
|
end
|
|
82
|
+
|
|
81
83
|
return func_return_value
|
|
82
84
|
end
|
|
83
85
|
end
|
|
@@ -196,6 +198,7 @@ class PauseNode < Node
|
|
|
196
198
|
end
|
|
197
199
|
|
|
198
200
|
def evaluate
|
|
201
|
+
@value = 0 if @value.evaluate.negative?
|
|
199
202
|
sleep @value.evaluate
|
|
200
203
|
end
|
|
201
204
|
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,46 @@ 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
|
+
@file_string = ''
|
|
136
|
+
|
|
137
|
+
create_tokens_from_locale(locale)
|
|
129
138
|
|
|
130
|
-
|
|
139
|
+
false_value = @token_list['(false|true)'].split('|')[0]# Hacky solution to convoluted tokens
|
|
140
|
+
true_value = @token_list['(false|true)'].split('|')[1]
|
|
141
|
+
true_value = true_value[0..-2]
|
|
142
|
+
false_value = false_value[1..]
|
|
143
|
+
|
|
144
|
+
ScopeManager.new(true_value, false_value) # Hacky solution to convoluted tokens
|
|
145
|
+
|
|
146
|
+
instance_eval(&block)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Recreate the tokenlist using the chosen locale file
|
|
150
|
+
def create_tokens_from_locale(locale)
|
|
151
|
+
if locale == 'default'
|
|
152
|
+
lang_file = File.read("#{LOCALES_PATH}/#{locale}")
|
|
153
|
+
token_pairs = File.readlines("#{LOCALES_PATH}/#{lang_file}")
|
|
154
|
+
else
|
|
155
|
+
token_pairs = File.readlines("#{LOCALES_PATH}/#{locale}")
|
|
156
|
+
end
|
|
131
157
|
@token_list = Hash.new
|
|
132
158
|
token_pairs.each do |pair|
|
|
133
159
|
default_value, locale_value = pair.split(' ')
|
|
134
160
|
@token_list[default_value] = locale_value
|
|
135
161
|
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
162
|
end
|
|
144
163
|
|
|
145
164
|
# Tokenize the string into small pieces
|
|
@@ -153,7 +172,7 @@ class Parser
|
|
|
153
172
|
match = tok.pattern.match(string)
|
|
154
173
|
# The regular expression of a token has matched the beginning of 'string'
|
|
155
174
|
if match
|
|
156
|
-
@logger.debug("Token #{match[0]} consumed as #{match[0]}")
|
|
175
|
+
@logger.debug("Token #{match[0]} consumed as #{match[0]}") if @debug_mode
|
|
157
176
|
# Also, evaluate this expression by using the block
|
|
158
177
|
# associated with the token
|
|
159
178
|
@tokens << tok.block.call(match.to_s) if tok.block
|
|
@@ -168,9 +187,78 @@ class Parser
|
|
|
168
187
|
end # until
|
|
169
188
|
end
|
|
170
189
|
|
|
190
|
+
def find_faulty_expression_within_block(block_start_line, block_end_line, problematic_token)
|
|
191
|
+
index = 0
|
|
192
|
+
result = []
|
|
193
|
+
@file_string.each_line do |line|
|
|
194
|
+
if index > block_start_line and index < block_end_line
|
|
195
|
+
|
|
196
|
+
if line.match?(/;;.*$/)
|
|
197
|
+
line = line.split(';;')[0]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
if line.match?(/#{problematic_token}\s+$/)
|
|
201
|
+
result << "ParseError, #{line.strip}() is missing an expression on line #{index + 1}"
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
index += 1
|
|
205
|
+
end
|
|
206
|
+
raise ParseError, result[-1]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def find_faulty_expression_within_ends
|
|
210
|
+
puts 'TODO: Implement search for faulty token between two of the same guides'
|
|
211
|
+
raise ParseError, "Most likely mismatched '#{@token_list['END'.downcase]}' token."
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def find_faulty_line
|
|
215
|
+
problematic_token = "#{@tokens[@max_pos - 1].downcase}"
|
|
216
|
+
before_problem = "#{@tokens[@max_pos - 2].downcase}"
|
|
217
|
+
after_problem = "#{@tokens[@max_pos - 0].downcase}"
|
|
218
|
+
|
|
219
|
+
if @max_pos - 5 <= 0
|
|
220
|
+
puts 'problem in the beginning of file, figure it out'
|
|
221
|
+
return
|
|
222
|
+
elsif @max_pos + 5 < @max_pos
|
|
223
|
+
puts 'problem in the end of file, figure it out'
|
|
224
|
+
return
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
translated_problematic_token = @token_list[problematic_token]
|
|
228
|
+
|
|
229
|
+
if translated_problematic_token.is_a?(NilClass)
|
|
230
|
+
raise ParseError, "Faulty expression likely between two '#{@token_list['END'.downcase]}' tokens."
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
index = 0
|
|
234
|
+
problem_match_hash = {}
|
|
235
|
+
found_end = false
|
|
236
|
+
@file_string.each_line do |line|
|
|
237
|
+
if line.match?(before_problem) && found_end == false
|
|
238
|
+
problem_match_hash[before_problem] = index
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
if line.match?(after_problem) && index > problem_match_hash[before_problem]
|
|
242
|
+
problem_match_hash[after_problem] = index
|
|
243
|
+
found_end = true
|
|
244
|
+
end
|
|
245
|
+
index += 1
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
non_empty_line_before_problem = problem_match_hash[before_problem]
|
|
249
|
+
non_empty_line_after_problem = problem_match_hash[after_problem]
|
|
250
|
+
|
|
251
|
+
unless after_problem == before_problem
|
|
252
|
+
find_faulty_expression_within_block(non_empty_line_before_problem, non_empty_line_after_problem, problematic_token)
|
|
253
|
+
else
|
|
254
|
+
find_faulty_expression_within_ends
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
171
258
|
def parse(string)
|
|
172
259
|
# First, split the string according to the "token" instructions given.
|
|
173
260
|
# Afterwards @tokens contains all tokens that are to be parsed.
|
|
261
|
+
@file_string = string
|
|
174
262
|
tokenize(string)
|
|
175
263
|
|
|
176
264
|
# These variables are used to match if the total number of tokens
|
|
@@ -182,7 +270,13 @@ class Parser
|
|
|
182
270
|
result = @start.parse
|
|
183
271
|
# If there are unparsed extra tokens, signal error
|
|
184
272
|
if @pos != @tokens.size
|
|
185
|
-
|
|
273
|
+
|
|
274
|
+
if @tokens[@max_pos] == '(' || @tokens[@max_pos] == ')'
|
|
275
|
+
raise ParseError, 'Mismatched parenthesis! In Emacs: M-x check-parens RET'
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# raise ParseError, "Parse error. expected: '#{@expected.join(', ')}', found '#{@tokens[@max_pos]}'"
|
|
279
|
+
return find_faulty_line
|
|
186
280
|
end
|
|
187
281
|
return result
|
|
188
282
|
end
|
|
@@ -223,7 +317,7 @@ class Parser
|
|
|
223
317
|
end
|
|
224
318
|
|
|
225
319
|
def rule(name,&block)
|
|
226
|
-
@current_rule = Rule.new(name, self)
|
|
320
|
+
@current_rule = Rule.new(name, self, @debug_mode)
|
|
227
321
|
@rules[name] = @current_rule
|
|
228
322
|
instance_eval &block # In practise, calls match 1..N times
|
|
229
323
|
@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.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
- mattias
|
|
7
|
+
- mattias, victor
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-05-
|
|
11
|
+
date: 2024-05-07 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
|