dhaka 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/dhaka.rb +1 -4
- data/lib/evaluator/evaluator.rb +65 -15
- data/lib/grammar/grammar.rb +30 -0
- data/lib/grammar/grammar_symbol.rb +1 -1
- data/lib/grammar/production.rb +1 -1
- data/lib/parser/action.rb +1 -3
- data/lib/parser/parse_result.rb +9 -7
- data/lib/parser/parse_tree.rb +9 -2
- data/lib/parser/parser.rb +7 -0
- data/lib/parser/parser_run.rb +12 -19
- data/lib/parser/token.rb +10 -7
- data/lib/tokenizer/tokenizer.rb +90 -17
- data/test/all_tests.rb +7 -6
- data/test/arithmetic_evaluator_test.rb +20 -20
- data/test/arithmetic_precedence_evaluator.rb +1 -1
- data/test/arithmetic_precedence_parser_test.rb +7 -7
- data/test/arithmetic_precedence_tokenizer.rb +3 -9
- data/test/arithmetic_test_methods.rb +2 -2
- data/test/arithmetic_tokenizer.rb +3 -9
- data/test/arithmetic_tokenizer_test.rb +14 -10
- data/test/bracket_tokenizer.rb +1 -1
- data/test/chittagong_driver_test.rb +261 -0
- data/test/chittagong_evaluator.rb +218 -47
- data/test/chittagong_evaluator_test.rb +18 -20
- data/test/chittagong_grammar.rb +61 -15
- data/test/chittagong_parser_test.rb +24 -12
- data/test/chittagong_test.rb +148 -6
- data/test/chittagong_tokenizer.rb +33 -21
- data/test/chittagong_tokenizer_test.rb +16 -8
- data/test/compiled_parser_test.rb +14 -12
- data/test/parser_test.rb +16 -16
- metadata +3 -2
@@ -6,102 +6,273 @@ class ChittagongEvaluator < Dhaka::Evaluator
|
|
6
6
|
self.grammar = ChittagongGrammar
|
7
7
|
|
8
8
|
define_evaluation_rules do
|
9
|
+
|
10
|
+
# No-ops
|
9
11
|
['no_terms', 'multiple_terms', 'single_term', 'some_terms'].each {|production_name| eval("for_#{production_name} do end")}
|
10
12
|
|
11
13
|
for_program do
|
12
14
|
evaluate(child_nodes[1])
|
13
15
|
end
|
14
|
-
|
15
|
-
|
16
|
-
evaluate(child_nodes[0])
|
16
|
+
|
17
|
+
for_multiple_main_body_statements do
|
18
|
+
first = evaluate(child_nodes[0])
|
19
|
+
return first if first.exception
|
20
|
+
evaluate(child_nodes[2])
|
21
|
+
end
|
22
|
+
|
23
|
+
for_multiple_function_body_statements do
|
24
|
+
first = evaluate(child_nodes[0])
|
25
|
+
return first if (first.exception || first.result)
|
17
26
|
evaluate(child_nodes[2])
|
18
27
|
end
|
19
28
|
|
20
|
-
|
21
|
-
|
29
|
+
for_function_definition do
|
30
|
+
function_name = evaluate(child_nodes[1])
|
31
|
+
arg_declarations = evaluate(child_nodes[3])
|
32
|
+
body = child_nodes[6]
|
33
|
+
@function_table[function_name] = Function.new(arg_declarations, body)
|
34
|
+
ChittagongSuccessResult.new(nil)
|
22
35
|
end
|
23
36
|
|
24
|
-
|
25
|
-
|
37
|
+
for_main_body_if_statement do
|
38
|
+
checked_if_statement
|
39
|
+
end
|
40
|
+
|
41
|
+
for_function_body_if_statement do
|
42
|
+
checked_if_statement
|
26
43
|
end
|
27
44
|
|
28
|
-
|
29
|
-
|
45
|
+
def checked_if_statement
|
46
|
+
condition_eval = evaluate(child_nodes[1])
|
47
|
+
return condition_eval if condition_eval.exception
|
48
|
+
return evaluate(child_nodes[3]) if condition_eval.result
|
49
|
+
ChittagongSuccessResult.new(nil)
|
50
|
+
end
|
51
|
+
|
52
|
+
for_main_body_if_else_statement do
|
53
|
+
checked_if_else_statement
|
30
54
|
end
|
31
55
|
|
32
|
-
|
33
|
-
|
56
|
+
for_function_body_if_else_statement do
|
57
|
+
checked_if_else_statement
|
58
|
+
end
|
59
|
+
|
60
|
+
def checked_if_else_statement
|
61
|
+
condition_eval = evaluate(child_nodes[1])
|
62
|
+
return condition_eval if condition_eval.exception
|
63
|
+
if condition_eval.result
|
64
|
+
evaluate(child_nodes[3])
|
65
|
+
else
|
66
|
+
evaluate(child_nodes[7])
|
67
|
+
end
|
34
68
|
end
|
35
69
|
|
36
|
-
|
37
|
-
|
70
|
+
for_main_body_while_statement do
|
71
|
+
checked_while_statement
|
72
|
+
end
|
73
|
+
|
74
|
+
for_function_body_while_statement do
|
75
|
+
checked_while_statement
|
76
|
+
end
|
77
|
+
|
78
|
+
def checked_while_statement
|
79
|
+
while true
|
80
|
+
condition_eval = evaluate(child_nodes[1])
|
81
|
+
return condition_eval if condition_eval.exception
|
82
|
+
break unless condition_eval.result
|
83
|
+
body_eval = evaluate(child_nodes[3])
|
84
|
+
return body_eval if (body_eval.exception || body_eval.result)
|
85
|
+
end
|
86
|
+
ChittagongSuccessResult.new(nil)
|
38
87
|
end
|
39
88
|
|
40
|
-
|
41
|
-
child_nodes[
|
89
|
+
for_assignment_statement do
|
90
|
+
rhs = evaluate(child_nodes[2])
|
91
|
+
return rhs if rhs.exception
|
92
|
+
@stack[-1][evaluate(child_nodes[0])] = rhs.result
|
93
|
+
ChittagongSuccessResult.new(nil)
|
94
|
+
end
|
95
|
+
|
96
|
+
for_print_statement do
|
97
|
+
rhs = evaluate(child_nodes[1])
|
98
|
+
return rhs if rhs.exception
|
99
|
+
@output_stream << rhs.result.to_s
|
100
|
+
ChittagongSuccessResult.new(nil)
|
42
101
|
end
|
43
102
|
|
44
|
-
|
45
|
-
|
103
|
+
for_function_call_statement do
|
104
|
+
checked_function_call
|
46
105
|
end
|
47
106
|
|
48
|
-
|
49
|
-
|
107
|
+
for_function_call_expression do
|
108
|
+
checked_function_call
|
109
|
+
end
|
110
|
+
|
111
|
+
def checked_function_call
|
112
|
+
function_name = evaluate(child_nodes[0])
|
113
|
+
return ChittagongExceptionResult.new("Undefined function #{function_name}",
|
114
|
+
child_nodes[0]) unless @function_table.has_key?(function_name)
|
115
|
+
|
116
|
+
arg_values = evaluate(child_nodes[2])
|
117
|
+
return arg_values if arg_values.exception
|
118
|
+
|
119
|
+
function = @function_table[function_name]
|
120
|
+
return ChittagongExceptionResult.new(
|
121
|
+
"Wrong number of arguments", child_nodes[1]
|
122
|
+
) unless function.args.size == arg_values.result.size
|
123
|
+
new_frame = {}
|
124
|
+
function.args.zip(arg_values.result).each do |arg_name, arg_value|
|
125
|
+
new_frame[arg_name] = arg_value
|
126
|
+
end
|
127
|
+
|
128
|
+
@stack << new_frame
|
129
|
+
result = evaluate(function.body)
|
130
|
+
@stack.pop
|
131
|
+
|
132
|
+
result
|
50
133
|
end
|
51
134
|
|
52
|
-
|
53
|
-
|
135
|
+
for_return_statement do
|
136
|
+
checked_unary_operation(child_nodes[1]){|x| x}
|
54
137
|
end
|
55
138
|
|
56
|
-
|
57
|
-
|
139
|
+
for_single_arg_declaration do
|
140
|
+
[evaluate(child_nodes[0])]
|
58
141
|
end
|
59
142
|
|
60
|
-
|
61
|
-
|
62
|
-
evaluate(child_nodes[3])
|
63
|
-
else
|
64
|
-
evaluate(child_nodes[7])
|
65
|
-
end
|
143
|
+
for_no_arg_decl do
|
144
|
+
[]
|
66
145
|
end
|
67
146
|
|
68
|
-
|
69
|
-
|
70
|
-
evaluate(child_nodes[3])
|
71
|
-
end
|
147
|
+
for_multiple_arg_declarations do
|
148
|
+
evaluate(child_nodes[0]) + [evaluate(child_nodes[2])]
|
72
149
|
end
|
73
150
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
151
|
+
for_no_args do
|
152
|
+
ChittagongSuccessResult.new([])
|
153
|
+
end
|
154
|
+
|
155
|
+
for_single_arg do
|
156
|
+
checked_unary_operation(child_nodes[0]) {|x| [x]}
|
157
|
+
end
|
158
|
+
|
159
|
+
for_multiple_args do
|
160
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]) {|a, b| a + [b]}
|
161
|
+
end
|
162
|
+
|
163
|
+
for_variable_reference do
|
164
|
+
variable_name = evaluate(child_nodes[0])
|
165
|
+
return ChittagongExceptionResult.new("Undefined variable #{variable_name}", child_nodes[0]) unless @stack[-1].has_key? variable_name
|
166
|
+
ChittagongSuccessResult.new(@stack[-1][variable_name])
|
167
|
+
end
|
168
|
+
|
169
|
+
for_literal do
|
170
|
+
ChittagongSuccessResult.new(child_nodes[0].token.value.to_i)
|
171
|
+
end
|
172
|
+
|
173
|
+
for_subtraction do
|
174
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a - b}
|
175
|
+
end
|
176
|
+
|
177
|
+
for_addition do
|
178
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a + b}
|
179
|
+
end
|
180
|
+
|
181
|
+
for_division do
|
182
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a.to_f / b}
|
183
|
+
end
|
184
|
+
|
185
|
+
for_multiplication do
|
186
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a * b}
|
187
|
+
end
|
188
|
+
|
189
|
+
for_power do
|
190
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a ** b}
|
78
191
|
end
|
79
192
|
|
80
193
|
for_less_than_comparison do
|
81
|
-
|
194
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a < b}
|
82
195
|
end
|
83
196
|
|
84
197
|
for_greater_than_comparison do
|
85
|
-
|
198
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a > b}
|
86
199
|
end
|
87
200
|
|
88
201
|
for_equality_comparison do
|
89
|
-
|
202
|
+
checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a == b}
|
90
203
|
end
|
91
204
|
|
92
|
-
|
93
|
-
|
205
|
+
def checked_binary_operation node1, node2
|
206
|
+
node1_eval = evaluate(node1)
|
207
|
+
node2_eval = evaluate(node2)
|
208
|
+
return node1_eval if node1_eval.exception
|
209
|
+
return node2_eval if node2_eval.exception
|
210
|
+
ChittagongSuccessResult.new(yield(node1_eval.result, node2_eval.result))
|
211
|
+
end
|
212
|
+
|
213
|
+
for_negated_expression do
|
214
|
+
checked_unary_operation(child_nodes[1]){|x| -x}
|
215
|
+
end
|
216
|
+
|
217
|
+
for_negation do
|
218
|
+
checked_unary_operation(child_nodes[1]){|b| !b}
|
219
|
+
end
|
220
|
+
|
221
|
+
for_parenthetized_expression do
|
222
|
+
checked_unary_operation(child_nodes[1]){|x| x}
|
223
|
+
end
|
224
|
+
|
225
|
+
def checked_unary_operation node
|
226
|
+
node_eval = evaluate(node)
|
227
|
+
return node_eval if node_eval.exception
|
228
|
+
ChittagongSuccessResult.new(yield(node_eval.result))
|
229
|
+
end
|
230
|
+
|
231
|
+
for_function_name do
|
232
|
+
child_nodes[0].token.value
|
233
|
+
end
|
234
|
+
|
235
|
+
for_variable_name do
|
236
|
+
child_nodes[0].token.value
|
94
237
|
end
|
95
238
|
|
96
|
-
|
97
|
-
|
239
|
+
for_arg_declaration do
|
240
|
+
child_nodes[0].token.value
|
98
241
|
end
|
99
242
|
|
100
|
-
def initialize(
|
101
|
-
@
|
243
|
+
def initialize(stack, output_stream)
|
244
|
+
@stack = stack
|
245
|
+
@function_table = {}
|
102
246
|
@output_stream = output_stream
|
103
247
|
end
|
104
248
|
|
105
249
|
end
|
106
250
|
|
107
|
-
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class ChittagongResult
|
254
|
+
attr_reader :exception
|
255
|
+
end
|
256
|
+
|
257
|
+
class ChittagongSuccessResult < ChittagongResult
|
258
|
+
attr_reader :result
|
259
|
+
def initialize(result)
|
260
|
+
@result = result
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class ChittagongExceptionResult < ChittagongResult
|
265
|
+
attr_reader :node
|
266
|
+
def initialize(exception, node)
|
267
|
+
@exception = exception
|
268
|
+
@node = node
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
class Function
|
273
|
+
attr_reader :args, :body
|
274
|
+
def initialize(args, body)
|
275
|
+
@args = args
|
276
|
+
@body = body
|
277
|
+
end
|
278
|
+
end
|
@@ -3,39 +3,37 @@ require "test/unit"
|
|
3
3
|
require "chittagong_evaluator"
|
4
4
|
require "fake_logger"
|
5
5
|
|
6
|
-
|
6
|
+
unless Object.const_defined? :ChittagongParser
|
7
|
+
eval(Dhaka::Parser.new(ChittagongGrammar, FakeLogger.new).compile_to_ruby_source_as(:ChittagongParser))
|
8
|
+
end
|
7
9
|
|
8
|
-
|
9
|
-
@logger = FakeLogger.new
|
10
|
-
@parser = Dhaka::Parser.new(ChittagongGrammar, @logger)
|
11
|
-
end
|
10
|
+
class TestChittagongEvaluator < Test::Unit::TestCase
|
12
11
|
|
13
12
|
def test_evaluates_a_simple_program
|
14
|
-
assert_equal(30, @logger.warnings.size)
|
15
|
-
assert_equal(0, @logger.errors.size)
|
16
13
|
token_stream = build_tokens(
|
17
14
|
['newline'],
|
18
|
-
['
|
19
|
-
['print'], ['
|
15
|
+
['word_literal', 'x'], ['='], ['int_literal', 23], ['newline'],
|
16
|
+
['print'], ['word_literal', 'x'], ['newline'],
|
20
17
|
['newline'],
|
21
|
-
['
|
22
|
-
['print'], ['
|
23
|
-
['if'], ['
|
24
|
-
['print'], ['
|
18
|
+
['word_literal', 'y'], ['='], ['word_literal', 'x'], ['+'], ['word_literal', 'x'], ['newline'],
|
19
|
+
['print'], ['word_literal', 'y'], ['*'], ['word_literal', 'x'], ['newline'],
|
20
|
+
['if'], ['word_literal', 'x'], ['>'], ['word_literal', 'y'], ['newline'],
|
21
|
+
['print'], ['word_literal', 'x'], ['newline'],
|
25
22
|
['else'], ['newline'],
|
26
|
-
['print'], ['
|
23
|
+
['print'], ['word_literal', 'y'], ['newline'],
|
27
24
|
['end'], ['newline'],
|
28
|
-
['newline']
|
25
|
+
['newline'],
|
26
|
+
[Dhaka::END_SYMBOL_NAME]
|
29
27
|
)
|
30
|
-
|
28
|
+
stack = [{}]
|
31
29
|
output_stream = []
|
32
|
-
ChittagongEvaluator.new(
|
33
|
-
assert_equal(23,
|
34
|
-
assert_equal(46,
|
30
|
+
ChittagongEvaluator.new(stack, output_stream).evaluate(ChittagongParser.parse(token_stream).parse_tree)
|
31
|
+
assert_equal(23, stack[0]['x'])
|
32
|
+
assert_equal(46, stack[0]['y'])
|
35
33
|
assert_equal(['23', '1058', '46'], output_stream)
|
36
34
|
end
|
37
35
|
|
38
36
|
def build_tokens *tokens
|
39
|
-
tokens.collect {|token| Dhaka::Token.new(
|
37
|
+
tokens.collect {|token| Dhaka::Token.new(token[0], token[1], nil)}
|
40
38
|
end
|
41
39
|
end
|
data/test/chittagong_grammar.rb
CHANGED
@@ -3,43 +3,89 @@ require File.dirname(__FILE__)+'/../lib/dhaka'
|
|
3
3
|
class ChittagongGrammar < Dhaka::Grammar
|
4
4
|
|
5
5
|
precedences do
|
6
|
+
nonassoc ['==']
|
7
|
+
nonassoc ['<', '>']
|
6
8
|
left ['+', '-']
|
7
9
|
left ['*', '/']
|
8
10
|
nonassoc ['^']
|
11
|
+
nonassoc ['!']
|
9
12
|
end
|
10
13
|
|
11
14
|
for_symbol(Dhaka::START_SYMBOL_NAME) do
|
12
|
-
program ['opt_terms', '
|
15
|
+
program ['opt_terms', 'main_body', 'opt_terms']
|
13
16
|
end
|
14
17
|
|
15
|
-
for_symbol('
|
16
|
-
|
17
|
-
|
18
|
+
for_symbol('main_body') do
|
19
|
+
single_main_body_statement ['main_body_statement']
|
20
|
+
multiple_main_body_statements ['main_body', 'terms', 'main_body_statement']
|
21
|
+
end
|
22
|
+
|
23
|
+
for_symbol('main_body_statement') do
|
24
|
+
main_body_simple_statement ['simple_statement']
|
25
|
+
function_definition ['def', 'function_name', '(', 'arg_declarations', ')', 'terms',
|
26
|
+
'function_body', 'terms', 'end']
|
27
|
+
main_body_if_statement ['if', 'expression', 'terms', 'main_body', 'terms', 'end']
|
28
|
+
main_body_if_else_statement ['if', 'expression', 'terms', 'main_body',
|
29
|
+
'terms', 'else', 'terms', 'main_body', 'terms', 'end']
|
30
|
+
main_body_while_statement ['while', 'expression', 'terms', 'main_body', 'terms',
|
31
|
+
'end']
|
18
32
|
end
|
19
33
|
|
20
|
-
for_symbol('
|
21
|
-
|
34
|
+
for_symbol('simple_statement') do
|
35
|
+
assignment_statement ['var_name', '=', 'expression']
|
22
36
|
print_statement ['print', 'expression']
|
23
|
-
|
24
|
-
if_else_statement ['if', 'boolean_expression', 'terms', 'statements', 'terms', 'else', 'terms', 'statements', 'terms', 'end']
|
25
|
-
while_statement ['while', 'boolean_expression', 'terms', 'statements', 'terms', 'end']
|
37
|
+
function_call_statement ['function_name', '(', 'arg_list', ')']
|
26
38
|
end
|
27
39
|
|
28
|
-
for_symbol('
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
for_symbol('function_body') do
|
41
|
+
single_function_body_statement ['function_body_statement']
|
42
|
+
multiple_function_body_statements ['function_body', 'terms', 'function_body_statement']
|
43
|
+
end
|
44
|
+
|
45
|
+
for_symbol('function_body_statement') do
|
46
|
+
function_body_simple_statement ['simple_statement']
|
47
|
+
return_statement ['return', 'expression']
|
48
|
+
function_body_if_statement ['if', 'expression', 'terms', 'function_body', 'terms', 'end']
|
49
|
+
function_body_if_else_statement ['if', 'expression', 'terms', 'function_body', 'terms', 'else', 'terms', 'function_body', 'terms', 'end']
|
50
|
+
function_body_while_statement ['while', 'expression', 'terms', 'function_body', 'terms', 'end']
|
51
|
+
end
|
52
|
+
|
53
|
+
for_symbol('function_name') do
|
54
|
+
function_name ['word_literal']
|
55
|
+
end
|
56
|
+
|
57
|
+
for_symbol('var_name') do
|
58
|
+
variable_name ['word_literal']
|
59
|
+
end
|
60
|
+
|
61
|
+
for_symbol('arg_declarations') do
|
62
|
+
no_arg_decl []
|
63
|
+
single_arg_declaration ['arg_decl']
|
64
|
+
multiple_arg_declarations ['arg_declarations', ',', 'arg_decl']
|
65
|
+
end
|
66
|
+
|
67
|
+
for_symbol('arg_decl') do
|
68
|
+
arg_declaration ['word_literal']
|
69
|
+
end
|
70
|
+
|
71
|
+
for_symbol('arg_list') do
|
72
|
+
no_args []
|
73
|
+
single_arg ['expression']
|
74
|
+
multiple_args ['arg_list', ',', 'expression']
|
33
75
|
end
|
34
76
|
|
35
|
-
|
36
77
|
for_symbol('expression') do
|
78
|
+
negation ['!', 'expression']
|
79
|
+
equality_comparison ['expression', '==', 'expression']
|
80
|
+
greater_than_comparison ['expression', '>', 'expression']
|
81
|
+
less_than_comparison ['expression', '<', 'expression']
|
37
82
|
addition ['expression', '+', 'expression']
|
38
83
|
subtraction ['expression', '-', 'expression']
|
39
84
|
multiplication ['expression', '*', 'expression']
|
40
85
|
division ['expression', '/', 'expression']
|
41
86
|
power ['expression', '^', 'expression']
|
42
87
|
literal ['int_literal']
|
88
|
+
function_call_expression ['function_name', '(', 'arg_list', ')']
|
43
89
|
variable_reference ['var_name']
|
44
90
|
parenthetized_expression ['(', 'expression', ')']
|
45
91
|
negated_expression ['-', 'expression'], :prec => '*'
|