dhaka 0.0.6 → 1.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/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
|