dhaka 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,42 +6,54 @@ require "fake_logger"
6
6
 
7
7
  class TestChittagongParser < Test::Unit::TestCase
8
8
  def setup
9
- @parser = Dhaka::Parser.new(ChittagongGrammar, FakeLogger.new)
9
+ fake_logger = FakeLogger.new
10
+ @parser = Dhaka::Parser.new(ChittagongGrammar, fake_logger)
11
+ assert_equal(80, fake_logger.warnings.size)
12
+ assert_equal(0, fake_logger.errors.size)
13
+ eval(@parser.compile_to_ruby_source_as(:ChittagongParser)) unless Module.const_defined? :ChittagongParser
10
14
  end
11
15
 
12
16
  def test_parses_a_series_of_statements
13
17
  token_stream = build_tokens(
14
18
  'newline',
15
- 'var_name', '=', 'int_literal', 'newline',
16
- 'print', 'var_name', 'newline',
19
+ 'word_literal', '=', 'int_literal', 'newline',
20
+ 'print', 'word_literal', 'newline',
17
21
  'newline',
18
- 'var_name', '=', 'var_name', 'newline',
19
- 'newline'
22
+ 'word_literal', '=', 'word_literal', 'newline',
23
+ 'newline', Dhaka::END_SYMBOL_NAME
20
24
  )
21
25
 
22
26
  result = @parser.parse(token_stream)
23
27
 
24
28
  assert_equal(["single_term",
25
29
  "some_terms",
30
+ "variable_name",
26
31
  "literal",
27
- "assignment_expression",
28
- "single_statement",
32
+ "assignment_statement",
33
+ "main_body_simple_statement",
34
+ "single_main_body_statement",
29
35
  "single_term",
36
+ "variable_name",
30
37
  "variable_reference",
31
38
  "print_statement",
32
- "multiple_statements",
39
+ "main_body_simple_statement",
40
+ "multiple_main_body_statements",
33
41
  "single_term",
34
42
  "multiple_terms",
43
+ "variable_name",
44
+ "variable_name",
35
45
  "variable_reference",
36
- "assignment_expression",
37
- "multiple_statements",
46
+ "assignment_statement",
47
+ "main_body_simple_statement",
48
+ "multiple_main_body_statements",
38
49
  "single_term",
39
50
  "multiple_terms",
40
51
  "some_terms",
41
- "program"], result.syntax_tree.linearize)
52
+ "program"], result.parse_tree.linearize.collect {|node| node.production.name})
53
+
42
54
  end
43
55
 
44
56
  def build_tokens *symbol_names
45
- symbol_names.collect {|symbol_name| Dhaka::Token.new(ChittagongGrammar.symbol_for_name(symbol_name), nil)}
57
+ symbol_names.collect {|symbol_name| Dhaka::Token.new(symbol_name, nil, nil)}
46
58
  end
47
59
  end
@@ -5,14 +5,28 @@ require "chittagong_tokenizer"
5
5
  require "chittagong_evaluator"
6
6
  require "fake_logger"
7
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
+
8
13
  class TestChittagong < Test::Unit::TestCase
9
14
 
10
- def setup
11
- @parser = Dhaka::Parser.new(ChittagongGrammar, FakeLogger.new)
15
+ def fact(n)
16
+ return 1 if n==1
17
+ n * fact(n-1)
18
+ end
19
+
20
+ def program_output program
21
+ output_stream = []
22
+ parse_result = ChittagongParser.parse(ChittagongTokenizer.tokenize(program))
23
+ result = ChittagongEvaluator.new([{}], output_stream).evaluate(parse_result.parse_tree)
24
+ return result, output_stream
12
25
  end
13
26
 
14
- def test_tokenizes_parses_and_evaluates_a_small_program
27
+ def test_iterative_fibonacci_without_functions
15
28
  program = "
29
+
16
30
  n = 1
17
31
  a = 0
18
32
  b = 1
@@ -25,9 +39,137 @@ class TestChittagong < Test::Unit::TestCase
25
39
  end
26
40
 
27
41
  "
28
- symbol_table = {}
29
- output_stream = []
30
- ChittagongEvaluator.new(symbol_table, output_stream).evaluate(@parser.parse(ChittagongTokenizer.tokenize(program)).syntax_tree)
42
+
43
+ result, output_stream = program_output(program)
44
+ assert_equal(["1", "1", "2", "3", "5", "8", "13", "21", "34"], output_stream)
45
+ end
46
+
47
+ def test_iterative_fibonacci_with_functions
48
+ program = "
49
+
50
+ def fib(n)
51
+ i = 0
52
+ a = 0
53
+ b = 1
54
+ while i < n
55
+ c = a
56
+ a = b
57
+ b = c + b
58
+ i = i + 1
59
+ end
60
+ return b
61
+ end
62
+
63
+ x = 0
64
+ while x < 9
65
+ print fib(x)
66
+ x = x + 1
67
+ end
68
+
69
+ "
70
+ result, output_stream = program_output(program)
71
+ assert_equal(["1", "1", "2", "3", "5", "8", "13", "21", "34"], output_stream)
72
+ end
73
+
74
+ def test_recursive_fibonacci
75
+ program = "
76
+
77
+ def fib(n)
78
+ if n == 0
79
+ return 1
80
+ end
81
+ if n == -1
82
+ return 0
83
+ end
84
+ return fib(n-1) + fib(n-2)
85
+ end
86
+
87
+ x = 0
88
+ while x < 9
89
+ print fib(x)
90
+ x = x + 1
91
+ end
92
+
93
+ "
94
+ result, output_stream = program_output(program)
31
95
  assert_equal(["1", "1", "2", "3", "5", "8", "13", "21", "34"], output_stream)
32
96
  end
97
+
98
+ def test_recursive_factorial
99
+ program = "
100
+ def fact(n)
101
+ if n == 1
102
+ return 1
103
+ end
104
+ return n * fact(n-1)
105
+ end
106
+
107
+ n = 1
108
+ while n < 11
109
+ print fact(n)
110
+ n = n+1
111
+ end"
112
+
113
+ result, output_stream = program_output(program)
114
+ assert_equal((1..10).collect {|i| fact(i).to_s}, output_stream)
115
+ end
116
+
117
+ def test_various_things
118
+ program = "
119
+
120
+ a = 1
121
+ b = 2
122
+ c = 3
123
+
124
+ def foo(a, b, c)
125
+ print a
126
+ print b
127
+ print c
128
+ return c
129
+ print 999
130
+ end
131
+
132
+ foo(4, a, 6)
133
+
134
+ "
135
+
136
+ result, output_stream = program_output(program)
137
+ assert_equal(["4", "1", "6"], output_stream)
138
+ end
139
+
140
+ def test_if_else_block
141
+ program = "
142
+
143
+ def foo(a, b)
144
+ if a < b
145
+ print 1
146
+ else
147
+ print 2
148
+ end
149
+ end
150
+
151
+ foo(1, 2)
152
+ foo(2, 1)
153
+
154
+ "
155
+
156
+ result, output_stream = program_output(program)
157
+ assert_equal(["1", "2"], output_stream)
158
+ end
159
+
160
+ def test_no_arg_functions
161
+ program = "
162
+
163
+ def foo()
164
+ print 1
165
+ print 2
166
+ end
167
+ foo()
168
+
169
+ "
170
+
171
+ result, output_stream = program_output(program)
172
+ assert_equal(["1", "2"], output_stream)
173
+ end
174
+
33
175
  end
@@ -4,32 +4,39 @@ require 'chittagong_grammar'
4
4
 
5
5
  class ChittagongTokenizer < Dhaka::Tokenizer
6
6
 
7
- KEYWORDS = ['print', 'if', 'else', 'end', 'while']
7
+ KEYWORDS = ['print', 'if', 'else', 'end', 'while', 'def', 'return']
8
8
 
9
9
  digits = ('0'..'9').to_a
10
10
  letters = ('a'..'z').to_a
11
11
  parenths = ['(', ')']
12
- operators = ['-', '+', '/', '*', '^', '!', '>', '<', '=']
12
+ operators = ['-', '+', '/', '*', '^', '!', '>', '<']
13
+ equal_sign = ['=']
13
14
  whitespace = [' ']
15
+ arg_separator = [',']
14
16
  newline = ["\n"]
15
17
 
16
- all_characters = digits + letters + parenths + operators + whitespace + newline
18
+ all_characters = digits + letters + parenths + operators + whitespace + newline + arg_separator + equal_sign
17
19
 
18
20
  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
+ for_characters(all_characters - (digits + letters + newline + whitespace + equal_sign)) do
22
+ create_token(curr_char, nil)
21
23
  advance
22
24
  end
25
+ for_characters(equal_sign) do
26
+ create_token('=', nil)
27
+ advance
28
+ switch_to :get_equal_sign_operator
29
+ end
23
30
  for_characters digits do
24
- self.accumulator = ''
31
+ create_token('int_literal', '')
25
32
  switch_to :get_integer_literal
26
33
  end
27
34
  for_characters letters do
28
- self.accumulator = ''
35
+ create_token(nil, '')
29
36
  switch_to :get_word_literal
30
37
  end
31
38
  for_character newline do
32
- tokens << Dhaka::Token.new(ChittagongGrammar.symbol_for_name('newline'), nil)
39
+ create_token('newline', nil)
33
40
  advance
34
41
  end
35
42
  for_character whitespace do
@@ -37,40 +44,45 @@ class ChittagongTokenizer < Dhaka::Tokenizer
37
44
  end
38
45
  end
39
46
 
47
+ for_state :get_equal_sign_operator do
48
+ for_characters all_characters - equal_sign do
49
+ switch_to Dhaka::TOKENIZER_IDLE_STATE
50
+ end
51
+ for_character equal_sign do
52
+ curr_token.symbol_name += '='
53
+ advance
54
+ switch_to Dhaka::TOKENIZER_IDLE_STATE
55
+ end
56
+ end
57
+
40
58
  for_state :get_integer_literal do
41
59
  for_characters all_characters - digits do
42
- tokens << integer_literal_token(accumulator.to_i)
43
60
  switch_to Dhaka::TOKENIZER_IDLE_STATE
44
61
  end
45
62
  for_characters digits do
46
- self.accumulator += curr_char
63
+ curr_token.value += curr_char
47
64
  advance
48
- tokens << integer_literal_token(accumulator.to_i) unless curr_char
49
65
  end
50
66
  end
51
67
 
52
68
  for_state :get_word_literal do
53
69
  for_characters all_characters - letters do
54
- tokens << word_literal_token(accumulator)
70
+ curr_token.symbol_name = word_literal_symbol(curr_token.value)
55
71
  switch_to Dhaka::TOKENIZER_IDLE_STATE
56
72
  end
57
73
  for_characters letters do
58
- self.accumulator += curr_char
74
+ curr_token.value += curr_char
59
75
  advance
60
- tokens << word_literal_token(accumulator) unless curr_char
76
+ curr_token.symbol_name = word_literal_symbol(curr_token.value) unless curr_char
61
77
  end
62
78
  end
63
79
 
64
- def word_literal_token(value)
80
+ def word_literal_symbol(value)
65
81
  if KEYWORDS.include? value
66
- Dhaka::Token.new(ChittagongGrammar.symbol_for_name(value), nil)
82
+ value
67
83
  else
68
- Dhaka::Token.new(ChittagongGrammar.symbol_for_name('var_name'), value)
84
+ 'word_literal'
69
85
  end
70
86
  end
71
87
 
72
- def integer_literal_token(value)
73
- Dhaka::Token.new(ChittagongGrammar.symbol_for_name('int_literal'), value)
74
- end
75
-
76
88
  end
@@ -7,6 +7,7 @@ class TestChittagongTokenizer < Test::Unit::TestCase
7
7
  input = "
8
8
  x = 2 * 4
9
9
  y = 2 * x
10
+ v = x == y
10
11
  if x > y
11
12
  print x
12
13
  else
@@ -14,32 +15,39 @@ class TestChittagongTokenizer < Test::Unit::TestCase
14
15
  end
15
16
  "
16
17
  assert_equal(["newline",
17
- "var_name",
18
+ "word_literal",
18
19
  "=",
19
20
  "int_literal",
20
21
  "*",
21
22
  "int_literal",
22
23
  "newline",
23
- "var_name",
24
+ "word_literal",
24
25
  "=",
25
26
  "int_literal",
26
27
  "*",
27
- "var_name",
28
+ "word_literal",
29
+ "newline",
30
+ "word_literal",
31
+ "=",
32
+ "word_literal",
33
+ "==",
34
+ "word_literal",
28
35
  "newline",
29
36
  "if",
30
- "var_name",
37
+ "word_literal",
31
38
  ">",
32
- "var_name",
39
+ "word_literal",
33
40
  "newline",
34
41
  "print",
35
- "var_name",
42
+ "word_literal",
36
43
  "newline",
37
44
  "else",
38
45
  "newline",
39
46
  "print",
40
- "var_name",
47
+ "word_literal",
41
48
  "newline",
42
49
  "end",
43
- "newline"], ChittagongTokenizer.tokenize(input).collect {|token| token.grammar_symbol.name})
50
+ "newline",
51
+ Dhaka::END_SYMBOL_NAME], ChittagongTokenizer.tokenize(input).collect {|token| token.symbol_name})
44
52
  end
45
53
  end
@@ -9,8 +9,8 @@ eval(Dhaka::Parser.new(ArithmeticGrammar).compile_to_ruby_source_as('CompiledAri
9
9
  class TestCompiledParser < Test::Unit::TestCase
10
10
  include ArithmeticTestMethods
11
11
 
12
- def test_compiled_parser_generates_syntax_tree_for_simple_grammar
13
- syntax_tree = Foo.parse(build_tokens(['(','n','-','(','n','-','n',')',')','-','n','#'], Foo.grammar)).syntax_tree
12
+ def test_compiled_parser_generates_parse_tree_for_simple_grammar
13
+ parse_tree = Foo.parse(build_tokens(['(','n','-','(','n','-','n',')',')','-','n','#',Dhaka::END_SYMBOL_NAME])).parse_tree
14
14
  assert_equal \
15
15
  ["literal",
16
16
  "term",
@@ -25,11 +25,11 @@ class TestCompiledParser < Test::Unit::TestCase
25
25
  "literal",
26
26
  "subtraction",
27
27
  "expression",
28
- "start"], syntax_tree.linearize
28
+ "start"], parse_tree.linearize.collect {|node| node.production.name}
29
29
  end
30
30
 
31
- def test_compiled_parser_generates_syntax_tree_for_arithmetic_grammar
32
- parser_input = ['(','n','-','(','n','/','n','-','n',')','/','n',')']
31
+ def test_compiled_parser_generates_parse_tree_for_arithmetic_grammar
32
+ parser_input = ['(','n','-','(','n','/','n','-','n',')','/','n',')',Dhaka::END_SYMBOL_NAME]
33
33
  assert_equal \
34
34
  ["getting_literals",
35
35
  "factor",
@@ -50,21 +50,23 @@ class TestCompiledParser < Test::Unit::TestCase
50
50
  "unpacking_parenthetized_expression",
51
51
  "factor",
52
52
  "term",
53
- "expression"], parse(build_tokens(parser_input, CompiledArithmeticParser.grammar)).linearize
53
+ "expression"], parse(build_tokens(parser_input)).linearize.collect {|node| node.production.name}
54
54
  end
55
55
 
56
- def test_parse_result_has_error_if_empty_token_array
57
- assert CompiledArithmeticParser.parse([]).has_error?
56
+ def test_parse_result_has_nil_parse_tree_if_empty_token_array
57
+ parse_result = CompiledArithmeticParser.parse([])
58
+ assert !CompiledArithmeticParser.parse([]).has_error?
59
+ assert_nil parse_result.parse_tree
58
60
  end
59
61
 
60
62
  def test_parser_returns_error_result_with_index_of_bad_token_if_parse_error
61
- parse_result = CompiledArithmeticParser.parse(build_tokens(['(', '-', ')'], CompiledArithmeticParser.grammar))
63
+ parse_result = CompiledArithmeticParser.parse(build_tokens(['(', '-', ')',Dhaka::END_SYMBOL_NAME]))
62
64
  assert parse_result.has_error?
63
- assert_equal 1, parse_result.bad_token_index
65
+ assert_equal '-', parse_result.unexpected_token.symbol_name
64
66
  end
65
67
 
66
- def build_tokens(token_symbol_names, grammar)
67
- token_symbol_names.collect {|symbol_name| Dhaka::Token.new(grammar.symbol_for_name(symbol_name), nil)}
68
+ def build_tokens(token_symbol_names)
69
+ token_symbol_names.collect {|symbol_name| Dhaka::Token.new(symbol_name, nil, nil)}
68
70
  end
69
71
 
70
72
  end