dhaka 0.0.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/evaluator/evaluator.rb +11 -18
- data/lib/tokenizer/tokenizer.rb +1 -1
- data/test/all_tests.rb +5 -1
- data/test/arithmetic_evaluator.rb +9 -10
- data/test/arithmetic_evaluator_test.rb +5 -5
- data/test/arithmetic_precedence_evaluator.rb +7 -7
- data/test/arithmetic_precedence_parser_test.rb +1 -1
- data/test/chittagong_evaluator.rb +107 -0
- data/test/chittagong_evaluator_test.rb +41 -0
- data/test/chittagong_grammar.rb +58 -0
- data/test/chittagong_parser_test.rb +47 -0
- data/test/chittagong_test.rb +33 -0
- data/test/chittagong_tokenizer.rb +76 -0
- data/test/chittagong_tokenizer_test.rb +45 -0
- metadata +9 -2
data/lib/evaluator/evaluator.rb
CHANGED
@@ -13,31 +13,24 @@ module Dhaka
|
|
13
13
|
|
14
14
|
# Instantiates a new evaluator with the syntax tree of a parsed expression. Only subclasses
|
15
15
|
# of Evaluator are directly instantiated.
|
16
|
-
def initialize(syntax_tree)
|
17
|
-
@syntax_tree = syntax_tree
|
18
|
-
@node_stack = []
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns the evaluation result.
|
22
|
-
def result
|
23
|
-
evaluate(@syntax_tree)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
16
|
|
28
|
-
|
29
|
-
@node_stack[-1]
|
30
|
-
end
|
31
|
-
|
17
|
+
# Evaluate a syntax tree node.
|
32
18
|
def evaluate node
|
33
|
-
|
34
|
-
@node_stack << node.child_nodes
|
19
|
+
@node_stack ||= []
|
20
|
+
@node_stack << node.child_nodes
|
35
21
|
proc = self.class.actions[node.production.name]
|
36
22
|
result = self.instance_eval(&proc)
|
37
23
|
@node_stack.pop
|
38
24
|
result
|
39
25
|
end
|
40
26
|
|
27
|
+
# Returns the array of child nodes of the node being currently evaluated.
|
28
|
+
def child_nodes
|
29
|
+
@node_stack[-1]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
41
34
|
def self.inherited(evaluator)
|
42
35
|
class << evaluator
|
43
36
|
attr_accessor :grammar, :actions
|
@@ -45,7 +38,7 @@ module Dhaka
|
|
45
38
|
end
|
46
39
|
|
47
40
|
def self.define_evaluation_rules
|
48
|
-
default_action = Proc.new { child_nodes[0] }
|
41
|
+
default_action = Proc.new { evaluate(child_nodes[0]) }
|
49
42
|
self.actions = Hash.new { |hash, key| default_action }
|
50
43
|
yield
|
51
44
|
check_definitions
|
data/lib/tokenizer/tokenizer.rb
CHANGED
@@ -12,7 +12,7 @@ module Dhaka
|
|
12
12
|
@char_index = char_index
|
13
13
|
end
|
14
14
|
def to_s
|
15
|
-
"Unrecognized character #{input[char_index].chr} encountered while tokenizing:\n #{input}"
|
15
|
+
"Unrecognized character '#{input[char_index].chr}' encountered while tokenizing:\n #{input} at index #{char_index}"
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
data/test/all_tests.rb
CHANGED
@@ -11,4 +11,8 @@ require 'malformed_grammar_test'
|
|
11
11
|
require 'brackets_test'
|
12
12
|
require 'arithmetic_precedence_grammar_test'
|
13
13
|
require 'arithmetic_precedence_parser_test'
|
14
|
-
require 'precedence_grammar_test'
|
14
|
+
require 'precedence_grammar_test'
|
15
|
+
require 'chittagong_parser_test'
|
16
|
+
require 'chittagong_evaluator_test'
|
17
|
+
require 'chittagong_tokenizer_test'
|
18
|
+
require 'chittagong_test'
|
@@ -8,19 +8,19 @@ class ArithmeticEvaluator < Dhaka::Evaluator
|
|
8
8
|
define_evaluation_rules do
|
9
9
|
|
10
10
|
for_subtraction do
|
11
|
-
child_nodes[0] - child_nodes[2]
|
11
|
+
evaluate(child_nodes[0]) - evaluate(child_nodes[2])
|
12
12
|
end
|
13
13
|
|
14
14
|
for_addition do
|
15
|
-
child_nodes[0] + child_nodes[2]
|
15
|
+
evaluate(child_nodes[0]) + evaluate(child_nodes[2])
|
16
16
|
end
|
17
17
|
|
18
18
|
for_division do
|
19
|
-
child_nodes[0].to_f/child_nodes[2]
|
19
|
+
evaluate(child_nodes[0]).to_f/evaluate(child_nodes[2])
|
20
20
|
end
|
21
21
|
|
22
22
|
for_multiplication do
|
23
|
-
child_nodes[0] * child_nodes[2]
|
23
|
+
evaluate(child_nodes[0]) * evaluate(child_nodes[2])
|
24
24
|
end
|
25
25
|
|
26
26
|
for_getting_literals do
|
@@ -28,7 +28,7 @@ class ArithmeticEvaluator < Dhaka::Evaluator
|
|
28
28
|
end
|
29
29
|
|
30
30
|
for_unpacking_parenthetized_expression do
|
31
|
-
child_nodes[1]
|
31
|
+
evaluate(child_nodes[1])
|
32
32
|
end
|
33
33
|
|
34
34
|
for_empty_args do
|
@@ -36,15 +36,15 @@ class ArithmeticEvaluator < Dhaka::Evaluator
|
|
36
36
|
end
|
37
37
|
|
38
38
|
for_evaluating_function do
|
39
|
-
child_nodes[0].call child_nodes[2]
|
39
|
+
evaluate(child_nodes[0]).call evaluate(child_nodes[2])
|
40
40
|
end
|
41
41
|
|
42
42
|
for_concatenating_args do
|
43
|
-
[child_nodes[0]]+child_nodes[2]
|
43
|
+
[evaluate(child_nodes[0])]+evaluate(child_nodes[2])
|
44
44
|
end
|
45
45
|
|
46
46
|
for_single_args do
|
47
|
-
[child_nodes[0]]
|
47
|
+
[evaluate(child_nodes[0])]
|
48
48
|
end
|
49
49
|
|
50
50
|
for_min_function do
|
@@ -57,10 +57,9 @@ class ArithmeticEvaluator < Dhaka::Evaluator
|
|
57
57
|
|
58
58
|
end
|
59
59
|
|
60
|
-
def initialize(
|
60
|
+
def initialize(min_function, max_function)
|
61
61
|
@min_function = min_function
|
62
62
|
@max_function = max_function
|
63
|
-
super(syntax_tree)
|
64
63
|
end
|
65
64
|
|
66
65
|
end
|
@@ -15,7 +15,7 @@ class TestArithmeticEvaluator < Test::Unit::TestCase
|
|
15
15
|
|
16
16
|
token_stream = [token('n', 2), token('-', nil), token('n', 4)]
|
17
17
|
syntax_tree = parse(token_stream)
|
18
|
-
assert_equal -2, ArithmeticEvaluator.new(
|
18
|
+
assert_equal -2, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
|
19
19
|
|
20
20
|
end
|
21
21
|
|
@@ -23,7 +23,7 @@ class TestArithmeticEvaluator < Test::Unit::TestCase
|
|
23
23
|
|
24
24
|
token_stream = [token('n', 2), token('-', nil), token('(', nil), token('n', 3), token('/', nil), token('n', 4), token(')', nil)]
|
25
25
|
syntax_tree = parse(token_stream)
|
26
|
-
assert_equal 1.25, ArithmeticEvaluator.new(
|
26
|
+
assert_equal 1.25, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
|
27
27
|
|
28
28
|
end
|
29
29
|
|
@@ -31,7 +31,7 @@ class TestArithmeticEvaluator < Test::Unit::TestCase
|
|
31
31
|
|
32
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
33
|
syntax_tree = parse(token_stream)
|
34
|
-
assert_equal 3.5, ArithmeticEvaluator.new(
|
34
|
+
assert_equal 3.5, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
|
35
35
|
|
36
36
|
end
|
37
37
|
|
@@ -39,7 +39,7 @@ class TestArithmeticEvaluator < Test::Unit::TestCase
|
|
39
39
|
|
40
40
|
token_stream = [token('n', 2), token('+', nil), token('h', nil), token('(', nil), token('n', 3), token(',', nil), token('n', 4), token(')', nil)]
|
41
41
|
syntax_tree = parse(token_stream)
|
42
|
-
assert_equal 6, ArithmeticEvaluator.new(
|
42
|
+
assert_equal 6, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
|
43
43
|
|
44
44
|
end
|
45
45
|
|
@@ -47,7 +47,7 @@ class TestArithmeticEvaluator < Test::Unit::TestCase
|
|
47
47
|
|
48
48
|
token_stream = [token('n', 2), token('+', nil), token('l', nil), token('(', nil), token('n', 3), token(',', nil), token('n', 4), token(')', nil)]
|
49
49
|
syntax_tree = parse(token_stream)
|
50
|
-
assert_equal 5, ArithmeticEvaluator.new(
|
50
|
+
assert_equal 5, ArithmeticEvaluator.new(@min_func, @max_func).evaluate(syntax_tree)
|
51
51
|
|
52
52
|
end
|
53
53
|
|
@@ -8,19 +8,19 @@ class ArithmeticPrecedenceEvaluator < Dhaka::Evaluator
|
|
8
8
|
define_evaluation_rules do
|
9
9
|
|
10
10
|
for_subtraction do
|
11
|
-
child_nodes[0] - child_nodes[2]
|
11
|
+
evaluate(child_nodes[0]) - evaluate(child_nodes[2])
|
12
12
|
end
|
13
13
|
|
14
14
|
for_addition do
|
15
|
-
child_nodes[0] + child_nodes[2]
|
15
|
+
evaluate(child_nodes[0]) + evaluate(child_nodes[2])
|
16
16
|
end
|
17
17
|
|
18
18
|
for_division do
|
19
|
-
child_nodes[0].to_f/child_nodes[2]
|
19
|
+
evaluate(child_nodes[0]).to_f/evaluate(child_nodes[2])
|
20
20
|
end
|
21
21
|
|
22
22
|
for_multiplication do
|
23
|
-
child_nodes[0] * child_nodes[2]
|
23
|
+
evaluate(child_nodes[0]) * evaluate(child_nodes[2])
|
24
24
|
end
|
25
25
|
|
26
26
|
for_literal do
|
@@ -28,15 +28,15 @@ class ArithmeticPrecedenceEvaluator < Dhaka::Evaluator
|
|
28
28
|
end
|
29
29
|
|
30
30
|
for_parenthetized_expression do
|
31
|
-
child_nodes[1]
|
31
|
+
evaluate(child_nodes[1])
|
32
32
|
end
|
33
33
|
|
34
34
|
for_negated_expression do
|
35
|
-
-child_nodes[1]
|
35
|
+
-evaluate(child_nodes[1])
|
36
36
|
end
|
37
37
|
|
38
38
|
for_power do
|
39
|
-
child_nodes[0]**child_nodes[2]
|
39
|
+
evaluate(child_nodes[0])**evaluate(child_nodes[2])
|
40
40
|
end
|
41
41
|
|
42
42
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/dhaka'
|
2
|
+
require 'chittagong_grammar'
|
3
|
+
|
4
|
+
class ChittagongEvaluator < Dhaka::Evaluator
|
5
|
+
|
6
|
+
self.grammar = ChittagongGrammar
|
7
|
+
|
8
|
+
define_evaluation_rules do
|
9
|
+
['no_terms', 'multiple_terms', 'single_term', 'some_terms'].each {|production_name| eval("for_#{production_name} do end")}
|
10
|
+
|
11
|
+
for_program do
|
12
|
+
evaluate(child_nodes[1])
|
13
|
+
end
|
14
|
+
|
15
|
+
for_multiple_statements do
|
16
|
+
evaluate(child_nodes[0])
|
17
|
+
evaluate(child_nodes[2])
|
18
|
+
end
|
19
|
+
|
20
|
+
for_print_statement do
|
21
|
+
@output_stream << evaluate(child_nodes[1]).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
for_subtraction do
|
25
|
+
evaluate(child_nodes[0]) - evaluate(child_nodes[2])
|
26
|
+
end
|
27
|
+
|
28
|
+
for_addition do
|
29
|
+
evaluate(child_nodes[0]) + evaluate(child_nodes[2])
|
30
|
+
end
|
31
|
+
|
32
|
+
for_division do
|
33
|
+
evaluate(child_nodes[0]).to_f/evaluate(child_nodes[2])
|
34
|
+
end
|
35
|
+
|
36
|
+
for_multiplication do
|
37
|
+
evaluate(child_nodes[0]) * evaluate(child_nodes[2])
|
38
|
+
end
|
39
|
+
|
40
|
+
for_literal do
|
41
|
+
child_nodes[0].token.value
|
42
|
+
end
|
43
|
+
|
44
|
+
for_parenthetized_expression do
|
45
|
+
evaluate(child_nodes[1])
|
46
|
+
end
|
47
|
+
|
48
|
+
for_negated_expression do
|
49
|
+
-evaluate(child_nodes[1])
|
50
|
+
end
|
51
|
+
|
52
|
+
for_power do
|
53
|
+
evaluate(child_nodes[0])**evaluate(child_nodes[2])
|
54
|
+
end
|
55
|
+
|
56
|
+
for_negation do
|
57
|
+
!evaluate(child_nodes[1])
|
58
|
+
end
|
59
|
+
|
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
|
66
|
+
end
|
67
|
+
|
68
|
+
for_if_statement do
|
69
|
+
if evaluate(child_nodes[1])
|
70
|
+
evaluate(child_nodes[3])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
for_while_statement do
|
75
|
+
while evaluate(child_nodes[1])
|
76
|
+
evaluate(child_nodes[3])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
for_less_than_comparison do
|
81
|
+
evaluate(child_nodes[0]) < evaluate(child_nodes[2])
|
82
|
+
end
|
83
|
+
|
84
|
+
for_greater_than_comparison do
|
85
|
+
evaluate(child_nodes[0]) > evaluate(child_nodes[2])
|
86
|
+
end
|
87
|
+
|
88
|
+
for_equality_comparison do
|
89
|
+
evaluate(child_nodes[0]) == evaluate(child_nodes[2])
|
90
|
+
end
|
91
|
+
|
92
|
+
for_variable_reference do
|
93
|
+
@symbol_table[child_nodes[0].token.value]
|
94
|
+
end
|
95
|
+
|
96
|
+
for_assignment_expression do
|
97
|
+
@symbol_table[child_nodes[0].token.value] = evaluate(child_nodes[2])
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize(symbol_table, output_stream)
|
101
|
+
@symbol_table = symbol_table
|
102
|
+
@output_stream = output_stream
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require "chittagong_evaluator"
|
4
|
+
require "fake_logger"
|
5
|
+
|
6
|
+
class TestChittagongEvaluator < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@logger = FakeLogger.new
|
10
|
+
@parser = Dhaka::Parser.new(ChittagongGrammar, @logger)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_evaluates_a_simple_program
|
14
|
+
assert_equal(30, @logger.warnings.size)
|
15
|
+
assert_equal(0, @logger.errors.size)
|
16
|
+
token_stream = build_tokens(
|
17
|
+
['newline'],
|
18
|
+
['var_name', 'x'], ['='], ['int_literal', 23], ['newline'],
|
19
|
+
['print'], ['var_name', 'x'], ['newline'],
|
20
|
+
['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'],
|
25
|
+
['else'], ['newline'],
|
26
|
+
['print'], ['var_name', 'y'], ['newline'],
|
27
|
+
['end'], ['newline'],
|
28
|
+
['newline']
|
29
|
+
)
|
30
|
+
symbol_table = {}
|
31
|
+
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'])
|
35
|
+
assert_equal(['23', '1058', '46'], output_stream)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_tokens *tokens
|
39
|
+
tokens.collect {|token| Dhaka::Token.new(ChittagongGrammar.symbol_for_name(token[0]), token[1])}
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/dhaka'
|
2
|
+
|
3
|
+
class ChittagongGrammar < Dhaka::Grammar
|
4
|
+
|
5
|
+
precedences do
|
6
|
+
left ['+', '-']
|
7
|
+
left ['*', '/']
|
8
|
+
nonassoc ['^']
|
9
|
+
end
|
10
|
+
|
11
|
+
for_symbol(Dhaka::START_SYMBOL_NAME) do
|
12
|
+
program ['opt_terms', 'statements', 'opt_terms']
|
13
|
+
end
|
14
|
+
|
15
|
+
for_symbol('statements') do
|
16
|
+
single_statement ['statement']
|
17
|
+
multiple_statements ['statements', 'terms', 'statement']
|
18
|
+
end
|
19
|
+
|
20
|
+
for_symbol('statement') do
|
21
|
+
assignment_expression ['var_name', '=', 'expression']
|
22
|
+
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']
|
26
|
+
end
|
27
|
+
|
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']
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
for_symbol('expression') do
|
37
|
+
addition ['expression', '+', 'expression']
|
38
|
+
subtraction ['expression', '-', 'expression']
|
39
|
+
multiplication ['expression', '*', 'expression']
|
40
|
+
division ['expression', '/', 'expression']
|
41
|
+
power ['expression', '^', 'expression']
|
42
|
+
literal ['int_literal']
|
43
|
+
variable_reference ['var_name']
|
44
|
+
parenthetized_expression ['(', 'expression', ')']
|
45
|
+
negated_expression ['-', 'expression'], :prec => '*'
|
46
|
+
end
|
47
|
+
|
48
|
+
for_symbol('terms') do
|
49
|
+
single_term ['newline']
|
50
|
+
multiple_terms ['terms', 'newline']
|
51
|
+
end
|
52
|
+
|
53
|
+
for_symbol('opt_terms') do
|
54
|
+
some_terms ['terms']
|
55
|
+
no_terms []
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/dhaka'
|
2
|
+
|
3
|
+
require 'chittagong_grammar'
|
4
|
+
require "test/unit"
|
5
|
+
require "fake_logger"
|
6
|
+
|
7
|
+
class TestChittagongParser < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@parser = Dhaka::Parser.new(ChittagongGrammar, FakeLogger.new)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_parses_a_series_of_statements
|
13
|
+
token_stream = build_tokens(
|
14
|
+
'newline',
|
15
|
+
'var_name', '=', 'int_literal', 'newline',
|
16
|
+
'print', 'var_name', 'newline',
|
17
|
+
'newline',
|
18
|
+
'var_name', '=', 'var_name', 'newline',
|
19
|
+
'newline'
|
20
|
+
)
|
21
|
+
|
22
|
+
result = @parser.parse(token_stream)
|
23
|
+
|
24
|
+
assert_equal(["single_term",
|
25
|
+
"some_terms",
|
26
|
+
"literal",
|
27
|
+
"assignment_expression",
|
28
|
+
"single_statement",
|
29
|
+
"single_term",
|
30
|
+
"variable_reference",
|
31
|
+
"print_statement",
|
32
|
+
"multiple_statements",
|
33
|
+
"single_term",
|
34
|
+
"multiple_terms",
|
35
|
+
"variable_reference",
|
36
|
+
"assignment_expression",
|
37
|
+
"multiple_statements",
|
38
|
+
"single_term",
|
39
|
+
"multiple_terms",
|
40
|
+
"some_terms",
|
41
|
+
"program"], result.syntax_tree.linearize)
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_tokens *symbol_names
|
45
|
+
symbol_names.collect {|symbol_name| Dhaka::Token.new(ChittagongGrammar.symbol_for_name(symbol_name), nil)}
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require "chittagong_grammar"
|
4
|
+
require "chittagong_tokenizer"
|
5
|
+
require "chittagong_evaluator"
|
6
|
+
require "fake_logger"
|
7
|
+
|
8
|
+
class TestChittagong < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@parser = Dhaka::Parser.new(ChittagongGrammar, FakeLogger.new)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_tokenizes_parses_and_evaluates_a_small_program
|
15
|
+
program = "
|
16
|
+
n = 1
|
17
|
+
a = 0
|
18
|
+
b = 1
|
19
|
+
while n < 10
|
20
|
+
print b
|
21
|
+
c = a
|
22
|
+
a = b
|
23
|
+
b = c + b
|
24
|
+
n = n + 1
|
25
|
+
end
|
26
|
+
|
27
|
+
"
|
28
|
+
symbol_table = {}
|
29
|
+
output_stream = []
|
30
|
+
ChittagongEvaluator.new(symbol_table, output_stream).evaluate(@parser.parse(ChittagongTokenizer.tokenize(program)).syntax_tree)
|
31
|
+
assert_equal(["1", "1", "2", "3", "5", "8", "13", "21", "34"], output_stream)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/dhaka'
|
2
|
+
require 'chittagong_grammar'
|
3
|
+
|
4
|
+
|
5
|
+
class ChittagongTokenizer < Dhaka::Tokenizer
|
6
|
+
|
7
|
+
KEYWORDS = ['print', 'if', 'else', 'end', 'while']
|
8
|
+
|
9
|
+
digits = ('0'..'9').to_a
|
10
|
+
letters = ('a'..'z').to_a
|
11
|
+
parenths = ['(', ')']
|
12
|
+
operators = ['-', '+', '/', '*', '^', '!', '>', '<', '=']
|
13
|
+
whitespace = [' ']
|
14
|
+
newline = ["\n"]
|
15
|
+
|
16
|
+
all_characters = digits + letters + parenths + operators + whitespace + newline
|
17
|
+
|
18
|
+
for_state Dhaka::TOKENIZER_IDLE_STATE do
|
19
|
+
for_characters(all_characters - (digits + letters + newline + whitespace)) do
|
20
|
+
tokens << Dhaka::Token.new(ChittagongGrammar.symbol_for_name(curr_char), nil)
|
21
|
+
advance
|
22
|
+
end
|
23
|
+
for_characters digits do
|
24
|
+
self.accumulator = ''
|
25
|
+
switch_to :get_integer_literal
|
26
|
+
end
|
27
|
+
for_characters letters do
|
28
|
+
self.accumulator = ''
|
29
|
+
switch_to :get_word_literal
|
30
|
+
end
|
31
|
+
for_character newline do
|
32
|
+
tokens << Dhaka::Token.new(ChittagongGrammar.symbol_for_name('newline'), nil)
|
33
|
+
advance
|
34
|
+
end
|
35
|
+
for_character whitespace do
|
36
|
+
advance
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
for_state :get_integer_literal do
|
41
|
+
for_characters all_characters - digits do
|
42
|
+
tokens << integer_literal_token(accumulator.to_i)
|
43
|
+
switch_to Dhaka::TOKENIZER_IDLE_STATE
|
44
|
+
end
|
45
|
+
for_characters digits do
|
46
|
+
self.accumulator += curr_char
|
47
|
+
advance
|
48
|
+
tokens << integer_literal_token(accumulator.to_i) unless curr_char
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
for_state :get_word_literal do
|
53
|
+
for_characters all_characters - letters do
|
54
|
+
tokens << word_literal_token(accumulator)
|
55
|
+
switch_to Dhaka::TOKENIZER_IDLE_STATE
|
56
|
+
end
|
57
|
+
for_characters letters do
|
58
|
+
self.accumulator += curr_char
|
59
|
+
advance
|
60
|
+
tokens << word_literal_token(accumulator) unless curr_char
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def word_literal_token(value)
|
65
|
+
if KEYWORDS.include? value
|
66
|
+
Dhaka::Token.new(ChittagongGrammar.symbol_for_name(value), nil)
|
67
|
+
else
|
68
|
+
Dhaka::Token.new(ChittagongGrammar.symbol_for_name('var_name'), value)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def integer_literal_token(value)
|
73
|
+
Dhaka::Token.new(ChittagongGrammar.symbol_for_name('int_literal'), value)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require "chittagong_tokenizer"
|
4
|
+
|
5
|
+
class TestChittagongTokenizer < Test::Unit::TestCase
|
6
|
+
def test_tokenizes_a_program
|
7
|
+
input = "
|
8
|
+
x = 2 * 4
|
9
|
+
y = 2 * x
|
10
|
+
if x > y
|
11
|
+
print x
|
12
|
+
else
|
13
|
+
print y
|
14
|
+
end
|
15
|
+
"
|
16
|
+
assert_equal(["newline",
|
17
|
+
"var_name",
|
18
|
+
"=",
|
19
|
+
"int_literal",
|
20
|
+
"*",
|
21
|
+
"int_literal",
|
22
|
+
"newline",
|
23
|
+
"var_name",
|
24
|
+
"=",
|
25
|
+
"int_literal",
|
26
|
+
"*",
|
27
|
+
"var_name",
|
28
|
+
"newline",
|
29
|
+
"if",
|
30
|
+
"var_name",
|
31
|
+
">",
|
32
|
+
"var_name",
|
33
|
+
"newline",
|
34
|
+
"print",
|
35
|
+
"var_name",
|
36
|
+
"newline",
|
37
|
+
"else",
|
38
|
+
"newline",
|
39
|
+
"print",
|
40
|
+
"var_name",
|
41
|
+
"newline",
|
42
|
+
"end",
|
43
|
+
"newline"], ChittagongTokenizer.tokenize(input).collect {|token| token.grammar_symbol.name})
|
44
|
+
end
|
45
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: dhaka
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date: 2007-01-
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2007-01-08 00:00:00 -05:00
|
8
8
|
summary: An LALR1 parser generator written in Ruby
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -65,6 +65,13 @@ files:
|
|
65
65
|
- test/bracket_grammar.rb
|
66
66
|
- test/bracket_tokenizer.rb
|
67
67
|
- test/brackets_test.rb
|
68
|
+
- test/chittagong_evaluator.rb
|
69
|
+
- test/chittagong_evaluator_test.rb
|
70
|
+
- test/chittagong_grammar.rb
|
71
|
+
- test/chittagong_parser_test.rb
|
72
|
+
- test/chittagong_test.rb
|
73
|
+
- test/chittagong_tokenizer.rb
|
74
|
+
- test/chittagong_tokenizer_test.rb
|
68
75
|
- test/compiled_parser_test.rb
|
69
76
|
- test/evaluator_test.rb
|
70
77
|
- test/fake_logger.rb
|