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.
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'test/unit'
3
-
4
3
  require 'grammar_test'
4
+ require 'malformed_grammar_test'
5
5
  require 'parser_test'
6
- require 'arithmetic_evaluator_test'
7
- require 'compiled_parser_test'
8
6
  require 'evaluator_test'
7
+ require 'compiled_parser_test'
8
+ require 'arithmetic_evaluator_test'
9
+ require 'arithmetic_grammar_test'
9
10
  require 'arithmetic_tokenizer_test'
10
- require 'malformed_grammar_test'
11
- require 'brackets_test'
12
11
  require 'arithmetic_precedence_grammar_test'
13
12
  require 'arithmetic_precedence_parser_test'
13
+ require 'brackets_test'
14
14
  require 'precedence_grammar_test'
15
15
  require 'chittagong_parser_test'
16
16
  require 'chittagong_evaluator_test'
17
17
  require 'chittagong_tokenizer_test'
18
- require 'chittagong_test'
18
+ require 'chittagong_test'
19
+ require 'chittagong_driver_test'
@@ -11,43 +11,43 @@ class TestArithmeticEvaluator < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
 
14
- def test_results_simple_arithmetic_given_tokens_and_syntax_tree_1
14
+ def test_results_simple_arithmetic_given_tokens_and_parse_tree_1
15
15
 
16
- token_stream = [token('n', 2), token('-', nil), token('n', 4)]
17
- syntax_tree = parse(token_stream)
18
- assert_equal -2, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
16
+ token_stream = [token('n', 2), token('-', nil), token('n', 4), token(Dhaka::END_SYMBOL_NAME, nil)]
17
+ parse_tree = parse(token_stream)
18
+ assert_equal -2, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(parse_tree)
19
19
 
20
20
  end
21
21
 
22
- def test_results_simple_arithmetic_given_tokens_and_syntax_tree_2
22
+ def test_results_simple_arithmetic_given_tokens_and_parse_tree_2
23
23
 
24
- token_stream = [token('n', 2), token('-', nil), token('(', nil), token('n', 3), token('/', nil), token('n', 4), token(')', nil)]
25
- syntax_tree = parse(token_stream)
26
- assert_equal 1.25, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
24
+ token_stream = [token('n', 2), token('-', nil), token('(', nil), token('n', 3), token('/', nil), token('n', 4), token(')', nil), token(Dhaka::END_SYMBOL_NAME, nil)]
25
+ parse_tree = parse(token_stream)
26
+ assert_equal 1.25, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(parse_tree)
27
27
 
28
28
  end
29
29
 
30
- def test_results_simple_arithmetic_given_tokens_and_syntax_tree_3
30
+ def test_results_simple_arithmetic_given_tokens_and_parse_tree_3
31
31
 
32
- token_stream = [token('n', 2), token('+', nil), token('(', nil), token('n', 3), token('/', nil), token('(', nil), token('n', 7), token('-', nil), token('n', 5), token(')', nil) , token(')', nil)]
33
- syntax_tree = parse(token_stream)
34
- assert_equal 3.5, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
32
+ token_stream = [token('n', 2), token('+', nil), token('(', nil), token('n', 3), token('/', nil), token('(', nil), token('n', 7), token('-', nil), token('n', 5), token(')', nil) , token(')', nil), token(Dhaka::END_SYMBOL_NAME, nil)]
33
+ parse_tree = parse(token_stream)
34
+ assert_equal 3.5, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(parse_tree)
35
35
 
36
36
  end
37
37
 
38
- def test_results_simple_arithmetic_given_tokens_and_syntax_tree_4
38
+ def test_results_simple_arithmetic_given_tokens_and_parse_tree_4
39
39
 
40
- token_stream = [token('n', 2), token('+', nil), token('h', nil), token('(', nil), token('n', 3), token(',', nil), token('n', 4), token(')', nil)]
41
- syntax_tree = parse(token_stream)
42
- assert_equal 6, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
40
+ token_stream = [token('n', 2), token('+', nil), token('h', nil), token('(', nil), token('n', 3), token(',', nil), token('n', 4), token(')', nil), token(Dhaka::END_SYMBOL_NAME, nil)]
41
+ parse_tree = parse(token_stream)
42
+ assert_equal 6, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(parse_tree)
43
43
 
44
44
  end
45
45
 
46
- def test_results_simple_arithmetic_given_tokens_and_syntax_tree_5
46
+ def test_results_simple_arithmetic_given_tokens_and_parse_tree_5
47
47
 
48
- token_stream = [token('n', 2), token('+', nil), token('l', nil), token('(', nil), token('n', 3), token(',', nil), token('n', 4), token(')', nil)]
49
- syntax_tree = parse(token_stream)
50
- assert_equal 5, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
48
+ token_stream = [token('n', 2), token('+', nil), token('l', nil), token('(', nil), token('n', 3), token(',', nil), token('n', 4), token(')', nil), token(Dhaka::END_SYMBOL_NAME, nil)]
49
+ parse_tree = parse(token_stream)
50
+ assert_equal 5, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(parse_tree)
51
51
 
52
52
  end
53
53
 
@@ -24,7 +24,7 @@ class ArithmeticPrecedenceEvaluator < Dhaka::Evaluator
24
24
  end
25
25
 
26
26
  for_literal do
27
- child_nodes[0].token.value
27
+ child_nodes[0].token.value.to_i
28
28
  end
29
29
 
30
30
  for_parenthetized_expression do
@@ -13,11 +13,11 @@ class TestArithmeticPrecedenceParser < Test::Unit::TestCase
13
13
  assert_equal(30, fake_logger.warnings.size)
14
14
  assert_equal(0, fake_logger.errors.size)
15
15
 
16
- assert_equal(-8, evaluate(parse("5 * -14/(2*7 - 7) + 2").syntax_tree))
17
- assert_equal(-4, evaluate(parse("-2^2").syntax_tree))
18
- assert_equal(10, evaluate(parse("2+2^3").syntax_tree))
19
- assert_equal(64, evaluate(parse("(2+2)^3").syntax_tree))
20
- assert_equal(128, evaluate(parse("(2+2)^3*2").syntax_tree))
16
+ assert_equal(-8, evaluate(parse("5 * -14/(2*7 - 7) + 2").parse_tree))
17
+ assert_equal(-4, evaluate(parse("-2^2").parse_tree))
18
+ assert_equal(10, evaluate(parse("2+2^3").parse_tree))
19
+ assert_equal(64, evaluate(parse("(2+2)^3").parse_tree))
20
+ assert_equal(128, evaluate(parse("(2+2)^3*2").parse_tree))
21
21
  assert(parse("(2+2)^3^2").has_error?)
22
22
  end
23
23
 
@@ -25,8 +25,8 @@ class TestArithmeticPrecedenceParser < Test::Unit::TestCase
25
25
  ArithmeticPrecedenceParser.parse(ArithmeticPrecedenceTokenizer.tokenize(input))
26
26
  end
27
27
 
28
- def evaluate(syntax_tree)
29
- ArithmeticPrecedenceEvaluator.new.evaluate(syntax_tree)
28
+ def evaluate(parse_tree)
29
+ ArithmeticPrecedenceEvaluator.new.evaluate(parse_tree)
30
30
  end
31
31
 
32
32
  end
@@ -15,11 +15,11 @@ class ArithmeticPrecedenceTokenizer < Dhaka::Tokenizer
15
15
 
16
16
  for_state Dhaka::TOKENIZER_IDLE_STATE do
17
17
  for_characters(all_characters - (digits + whitespace)) do
18
- tokens << Dhaka::Token.new(ArithmeticPrecedenceGrammar.symbol_for_name(curr_char), nil)
18
+ create_token(curr_char, nil)
19
19
  advance
20
20
  end
21
21
  for_characters digits do
22
- self.accumulator = ''
22
+ create_token('n', '')
23
23
  switch_to :get_integer_literal
24
24
  end
25
25
  for_character whitespace do
@@ -29,19 +29,13 @@ class ArithmeticPrecedenceTokenizer < Dhaka::Tokenizer
29
29
 
30
30
  for_state :get_integer_literal do
31
31
  for_characters all_characters - digits do
32
- tokens << integer_literal_token(accumulator.to_i)
33
32
  switch_to Dhaka::TOKENIZER_IDLE_STATE
34
33
  end
35
34
  for_characters digits do
36
- self.accumulator += curr_char
35
+ curr_token.value += curr_char
37
36
  advance
38
- tokens << integer_literal_token(accumulator.to_i) unless curr_char
39
37
  end
40
38
  end
41
39
 
42
- def integer_literal_token(value)
43
- Dhaka::Token.new(ArithmeticPrecedenceGrammar.symbol_for_name('n'), value)
44
- end
45
-
46
40
  end
47
41
 
@@ -1,11 +1,11 @@
1
1
  module ArithmeticTestMethods
2
2
 
3
3
  def parse(token_stream)
4
- CompiledArithmeticParser.parse(token_stream).syntax_tree
4
+ CompiledArithmeticParser.parse(token_stream).parse_tree
5
5
  end
6
6
 
7
7
  def token(symbol_name, value)
8
- Dhaka::Token.new(CompiledArithmeticParser.grammar.symbol_for_name(symbol_name), value)
8
+ Dhaka::Token.new(symbol_name, value, nil)
9
9
  end
10
10
 
11
11
  end
@@ -15,11 +15,11 @@ class ArithmeticTokenizer < Dhaka::Tokenizer
15
15
 
16
16
  for_state Dhaka::TOKENIZER_IDLE_STATE do
17
17
  for_characters(all_characters - (digits + whitespace)) do
18
- tokens << Dhaka::Token.new(ArithmeticGrammar.symbol_for_name(curr_char), nil)
18
+ create_token(curr_char, nil)
19
19
  advance
20
20
  end
21
21
  for_characters digits do
22
- self.accumulator = ''
22
+ create_token('n', '')
23
23
  switch_to :get_integer_literal
24
24
  end
25
25
  for_character whitespace do
@@ -29,19 +29,13 @@ class ArithmeticTokenizer < Dhaka::Tokenizer
29
29
 
30
30
  for_state :get_integer_literal do
31
31
  for_characters all_characters - digits do
32
- tokens << integer_literal_token(accumulator.to_i)
33
32
  switch_to Dhaka::TOKENIZER_IDLE_STATE
34
33
  end
35
34
  for_characters digits do
36
- self.accumulator += curr_char
35
+ curr_token.value += curr_char
37
36
  advance
38
- tokens << integer_literal_token(accumulator.to_i) unless curr_char
39
37
  end
40
38
  end
41
39
 
42
- def integer_literal_token(value)
43
- Dhaka::Token.new(ArithmeticGrammar.symbol_for_name('n'), value)
44
- end
45
-
46
40
  end
47
41
 
@@ -2,31 +2,35 @@ require "test/unit"
2
2
  require "arithmetic_tokenizer"
3
3
 
4
4
  class TestArithmeticTokenizer < Test::Unit::TestCase
5
- def test_returns_empty_array_for_empty_input
6
- assert_equal([], ArithmeticTokenizer.tokenize([]))
5
+ def test_returns_end_of_input_token_for_empty_input
6
+ assert_equal([token(Dhaka::END_SYMBOL_NAME, nil)], ArithmeticTokenizer.tokenize([]).to_a)
7
7
  end
8
8
  def test_tokenizes_given_a_string_input
9
- assert_equal([token('n', 2), token('-', nil), token('n', 4)], ArithmeticTokenizer.tokenize('2 - 4'))
9
+ assert_equal([token('n', 2), token('-', nil), token('n', 4), token(Dhaka::END_SYMBOL_NAME, nil)], ArithmeticTokenizer.tokenize('2 - 4').to_a)
10
10
  end
11
11
  def test_a_longer_input
12
- actual = ArithmeticTokenizer.tokenize('2+(3 / (7 - 5))')
13
- assert_equal([token('n', 2), token('+', nil), token('(', nil), token('n', 3), token('/', nil), token('(', nil), token('n', 7), token('-', nil), token('n', 5), token(')', nil) , token(')', nil)], actual)
12
+ actual = ArithmeticTokenizer.tokenize('2+(3 / (7 - 5))').to_a
13
+ assert_equal([token('n', 2), token('+', nil), token('(', nil), token('n', 3), token('/', nil), token('(', nil), token('n', 7), token('-', nil), token('n', 5), token(')', nil) , token(')', nil), token(Dhaka::END_SYMBOL_NAME, nil)], actual)
14
14
  end
15
15
 
16
16
  def test_another_input_with_multi_digit_numbers
17
- actual = ArithmeticTokenizer.tokenize('2034 +(3433 / (7 - 5))')
18
- assert_equal([token('n', 2034), token('+', nil), token('(', nil), token('n', 3433), token('/', nil), token('(', nil), token('n', 7), token('-', nil), token('n', 5), token(')', nil) , token(')', nil)], actual)
17
+ actual = ArithmeticTokenizer.tokenize('2034 +(3433 / (7 - 5))').to_a
18
+ assert_equal([token('n', 2034), token('+', nil), token('(', nil), token('n', 3433), token('/', nil), token('(', nil), token('n', 7), token('-', nil), token('n', 5), token(')', nil) , token(')', nil), token(Dhaka::END_SYMBOL_NAME, nil)], actual)
19
19
  end
20
20
 
21
21
  def test_an_input_with_unrecognized_characters
22
- assert_raise(Dhaka::UnrecognizedInputCharacterException) {ArithmeticTokenizer.tokenize('2+(3 / (7 -& 5))')}
22
+ result = ArithmeticTokenizer.tokenize('2+(3 / (7 -& 5))')
23
+ assert(result.has_error?)
24
+ assert_equal(11, result.unexpected_char_index)
23
25
  end
24
26
 
25
27
  def test_another_input_with_illegal_characters
26
- assert_raise(Dhaka::UnrecognizedInputCharacterException) {ArithmeticTokenizer.tokenize('2034 +(34b3 / (7 - 5))')}
28
+ result = ArithmeticTokenizer.tokenize('2034 +(34b3 / (7 - 5))')
29
+ assert(result.has_error?)
30
+ assert_equal(9, result.unexpected_char_index)
27
31
  end
28
32
 
29
33
  def token(symbol_name, value)
30
- Dhaka::Token.new(ArithmeticGrammar.symbol_for_name(symbol_name), value)
34
+ Dhaka::Token.new(symbol_name, value ? value.to_s : nil, nil)
31
35
  end
32
36
  end
@@ -8,7 +8,7 @@ class BracketTokenizer < Dhaka::Tokenizer
8
8
 
9
9
  for_state Dhaka::TOKENIZER_IDLE_STATE do
10
10
  for_characters(all_characters) do
11
- tokens << Dhaka::Token.new(BracketGrammar.symbol_for_name(curr_char), nil)
11
+ create_token(curr_char, nil)
12
12
  advance
13
13
  end
14
14
  end
@@ -0,0 +1,261 @@
1
+ require "test/unit"
2
+ require "yaml"
3
+ require "chittagong_grammar"
4
+ require "chittagong_tokenizer"
5
+ require "chittagong_evaluator"
6
+ require "fake_logger"
7
+
8
+ unless Object.const_defined? :ChittagongParser
9
+ eval(Dhaka::Parser.new(ChittagongGrammar, FakeLogger.new).compile_to_ruby_source_as(:ChittagongParser))
10
+ end
11
+
12
+ class ChittagongDriver
13
+ def marked_program(program, error_position)
14
+ if error_position > 0
15
+ program[0..(error_position-1)] + ">>>" + program[error_position..-1]
16
+ else
17
+ ">>>"+program
18
+ end
19
+ end
20
+
21
+ def parse_error_message unexpected_token, program
22
+ if unexpected_token.symbol_name == Dhaka::END_SYMBOL_NAME
23
+ "Unexpected end of file."
24
+ else
25
+ "Unexpected token #{unexpected_token.symbol_name}:\n#{marked_program(program, unexpected_token.input_position)}"
26
+ end
27
+ end
28
+
29
+ def tokenize_error_message unexpected_char_index, program
30
+ "Unexpected character #{program[unexpected_char_index].chr}:\n#{marked_program(program, unexpected_char_index)}"
31
+ end
32
+
33
+ def evaluation_error_message evaluation_result, program
34
+ "#{evaluation_result.exception}:\n#{marked_program(program, evaluation_result.node.tokens[0].input_position)}"
35
+ end
36
+
37
+ def run(program)
38
+ tokenize_result = ChittagongTokenizer.tokenize(program)
39
+ if tokenize_result.has_error?
40
+ return tokenize_error_message(tokenize_result.unexpected_char_index, program)
41
+ end
42
+
43
+ parse_result = ChittagongParser.parse(tokenize_result)
44
+ if parse_result.has_error?
45
+ return parse_error_message(parse_result.unexpected_token, program)
46
+ end
47
+
48
+ evaluation_result = ChittagongEvaluator.new([{}], output_stream = []).
49
+ evaluate(parse_result.parse_tree)
50
+ if evaluation_result.exception
51
+ return (output_stream << evaluation_error_message(evaluation_result, program)).
52
+ join("\n")
53
+ end
54
+
55
+ return output_stream.join("\n")
56
+ end
57
+
58
+ end
59
+
60
+
61
+ class TestChittagongDriver < Test::Unit::TestCase
62
+
63
+ def setup
64
+ @driver = ChittagongDriver.new
65
+ end
66
+
67
+ def test_return_statement_not_allowed_in_main
68
+ # Programs with problems
69
+ program = "
70
+
71
+ if 1 > 2
72
+ return 5
73
+ end
74
+
75
+ print 2
76
+ "
77
+ assert_equal(
78
+ "Unexpected token return:
79
+
80
+
81
+ if 1 > 2
82
+ >>>return 5
83
+ end
84
+
85
+ print 2
86
+ ", @driver.run(program))
87
+ end
88
+
89
+ def test_catches_unexpected_end_of_input
90
+ program = "
91
+ def"
92
+ assert_equal("Unexpected end of file.", @driver.run(program))
93
+ end
94
+
95
+ def test_catches_unexpected_characters
96
+ program = "
97
+ print 2
98
+ def #}
99
+ "
100
+ assert_equal(
101
+ "Unexpected character #:
102
+
103
+ print 2
104
+ def >>>#}
105
+ ", @driver.run(program))
106
+ end
107
+
108
+ def test_catches_undefined_variables
109
+ program = "
110
+ x = 1
111
+ y = 2
112
+ def foo(n)
113
+ return 2
114
+ end
115
+ foo(1)
116
+
117
+ if x > y
118
+ print 999
119
+ print 777
120
+ else
121
+ print 66
122
+ end
123
+
124
+ print x
125
+ print y
126
+ print 2*3+c
127
+ print 888
128
+
129
+ "
130
+
131
+ assert_equal(
132
+ "66
133
+ 1
134
+ 2
135
+ Undefined variable c:
136
+
137
+ x = 1
138
+ y = 2
139
+ def foo(n)
140
+ return 2
141
+ end
142
+ foo(1)
143
+
144
+ if x > y
145
+ print 999
146
+ print 777
147
+ else
148
+ print 66
149
+ end
150
+
151
+ print x
152
+ print y
153
+ print 2*3+>>>c
154
+ print 888
155
+
156
+ ", @driver.run(program))
157
+ end
158
+
159
+ def test_catches_undefined_functions
160
+ program = "
161
+
162
+ def foo(n)
163
+ bar(x)
164
+ end
165
+
166
+ foo(2)
167
+
168
+ "
169
+ assert_equal(
170
+ "Undefined function bar:
171
+
172
+
173
+ def foo(n)
174
+ >>>bar(x)
175
+ end
176
+
177
+ foo(2)
178
+
179
+ ", @driver.run(program))
180
+ end
181
+
182
+ def test_variable_scope
183
+ program = "
184
+ x = 1
185
+
186
+ def bar(n)
187
+ print 999
188
+ end
189
+
190
+ def foo(n)
191
+ bar(x)
192
+ end
193
+
194
+ foo(2)
195
+
196
+ "
197
+ assert_equal(
198
+ "Undefined variable x:
199
+
200
+ x = 1
201
+
202
+ def bar(n)
203
+ print 999
204
+ end
205
+
206
+ def foo(n)
207
+ bar(>>>x)
208
+ end
209
+
210
+ foo(2)
211
+
212
+ ", @driver.run(program))
213
+ end
214
+
215
+ def test_nested_function_calls
216
+
217
+ program = "
218
+
219
+ def foo(a)
220
+ return a + 2
221
+ end
222
+
223
+ def bar(b, c)
224
+ return b + c
225
+ end
226
+
227
+ def baz(x, y, z)
228
+ return foo(y) + bar(x, z)
229
+ end
230
+
231
+ print baz(foo(1), bar(2, 3), bar(6, 2))
232
+
233
+ "
234
+ assert_equal("18", @driver.run(program))
235
+ end
236
+
237
+ def test_wrong_number_of_arguments
238
+
239
+ program = "
240
+
241
+ def whatever(a, b)
242
+ return a + b
243
+ end
244
+
245
+ whatever(1, 2, 3)
246
+
247
+ "
248
+ assert_equal(
249
+ "Wrong number of arguments:
250
+
251
+
252
+ def whatever(a, b)
253
+ return a + b
254
+ end
255
+
256
+ whatever>>>(1, 2, 3)
257
+
258
+ ", @driver.run(program))
259
+
260
+ end
261
+ end