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.
@@ -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
- for_multiple_statements do
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
- for_print_statement do
21
- @output_stream << evaluate(child_nodes[1]).to_s
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
- for_subtraction do
25
- evaluate(child_nodes[0]) - evaluate(child_nodes[2])
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
- for_addition do
29
- evaluate(child_nodes[0]) + evaluate(child_nodes[2])
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
- for_division do
33
- evaluate(child_nodes[0]).to_f/evaluate(child_nodes[2])
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
- for_multiplication do
37
- evaluate(child_nodes[0]) * evaluate(child_nodes[2])
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
- for_literal do
41
- child_nodes[0].token.value
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
- for_parenthetized_expression do
45
- evaluate(child_nodes[1])
103
+ for_function_call_statement do
104
+ checked_function_call
46
105
  end
47
106
 
48
- for_negated_expression do
49
- -evaluate(child_nodes[1])
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
- for_power do
53
- evaluate(child_nodes[0])**evaluate(child_nodes[2])
135
+ for_return_statement do
136
+ checked_unary_operation(child_nodes[1]){|x| x}
54
137
  end
55
138
 
56
- for_negation do
57
- !evaluate(child_nodes[1])
139
+ for_single_arg_declaration do
140
+ [evaluate(child_nodes[0])]
58
141
  end
59
142
 
60
- for_if_else_statement do
61
- if evaluate(child_nodes[1])
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
- for_if_statement do
69
- if evaluate(child_nodes[1])
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
- for_while_statement do
75
- while evaluate(child_nodes[1])
76
- evaluate(child_nodes[3])
77
- end
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
- evaluate(child_nodes[0]) < evaluate(child_nodes[2])
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
- evaluate(child_nodes[0]) > evaluate(child_nodes[2])
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
- evaluate(child_nodes[0]) == evaluate(child_nodes[2])
202
+ checked_binary_operation(child_nodes[0], child_nodes[2]){|a, b| a == b}
90
203
  end
91
204
 
92
- for_variable_reference do
93
- @symbol_table[child_nodes[0].token.value]
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
- for_assignment_expression do
97
- @symbol_table[child_nodes[0].token.value] = evaluate(child_nodes[2])
239
+ for_arg_declaration do
240
+ child_nodes[0].token.value
98
241
  end
99
242
 
100
- def initialize(symbol_table, output_stream)
101
- @symbol_table = symbol_table
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
- class TestChittagongEvaluator < Test::Unit::TestCase
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
- def setup
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
- ['var_name', 'x'], ['='], ['int_literal', 23], ['newline'],
19
- ['print'], ['var_name', 'x'], ['newline'],
15
+ ['word_literal', 'x'], ['='], ['int_literal', 23], ['newline'],
16
+ ['print'], ['word_literal', 'x'], ['newline'],
20
17
  ['newline'],
21
- ['var_name', 'y'], ['='], ['var_name', 'x'], ['+'], ['var_name', 'x'], ['newline'],
22
- ['print'], ['var_name', 'y'], ['*'], ['var_name', 'x'], ['newline'],
23
- ['if'], ['var_name', 'x'], ['>'], ['var_name', 'y'], ['newline'],
24
- ['print'], ['var_name', 'x'], ['newline'],
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'], ['var_name', 'y'], ['newline'],
23
+ ['print'], ['word_literal', 'y'], ['newline'],
27
24
  ['end'], ['newline'],
28
- ['newline']
25
+ ['newline'],
26
+ [Dhaka::END_SYMBOL_NAME]
29
27
  )
30
- symbol_table = {}
28
+ stack = [{}]
31
29
  output_stream = []
32
- ChittagongEvaluator.new(symbol_table, output_stream).evaluate(@parser.parse(token_stream).syntax_tree)
33
- assert_equal(23, symbol_table['x'])
34
- assert_equal(46, symbol_table['y'])
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(ChittagongGrammar.symbol_for_name(token[0]), token[1])}
37
+ tokens.collect {|token| Dhaka::Token.new(token[0], token[1], nil)}
40
38
  end
41
39
  end
@@ -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', 'statements', 'opt_terms']
15
+ program ['opt_terms', 'main_body', 'opt_terms']
13
16
  end
14
17
 
15
- for_symbol('statements') do
16
- single_statement ['statement']
17
- multiple_statements ['statements', 'terms', 'statement']
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('statement') do
21
- assignment_expression ['var_name', '=', 'expression']
34
+ for_symbol('simple_statement') do
35
+ assignment_statement ['var_name', '=', 'expression']
22
36
  print_statement ['print', 'expression']
23
- if_statement ['if', 'boolean_expression', 'terms', 'statements', 'terms', 'end']
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('boolean_expression') do
29
- negation ['!', 'boolean_expression']
30
- equality_comparison ['expression', '=', '=', 'expression']
31
- greater_than_comparison ['expression', '>', 'expression']
32
- less_than_comparison ['expression', '<', 'expression']
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 => '*'