rockit 0.7.1

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.
Files changed (57) hide show
  1. data/BUGS +13 -0
  2. data/LICENSE +280 -0
  3. data/README +172 -0
  4. data/TODO +53 -0
  5. data/VERSION +1 -0
  6. data/lib/packrat/grammar.rb +537 -0
  7. data/lib/rockit/prettyprint/box.rb +60 -0
  8. data/lib/rockit/prettyprint/renderer.rb +41 -0
  9. data/lib/rockit/prettyprint/text_renderer.rb +47 -0
  10. data/lib/rockit/tree/base.rb +223 -0
  11. data/lib/rockit/tree/enter_leave_visitor.rb +12 -0
  12. data/lib/rockit/tree/graphviz.rb +69 -0
  13. data/lib/rockit/tree/visitor.rb +12 -0
  14. data/lib/util/array_alternatives.rb +20 -0
  15. data/lib/util/enter_leave_visitor.rb +69 -0
  16. data/lib/util/graphviz_dot.rb +182 -0
  17. data/lib/util/string_location.rb +42 -0
  18. data/lib/util/visitor.rb +49 -0
  19. data/lib/util/visitor_combinators.rb +14 -0
  20. data/rakefile +200 -0
  21. data/tests/acceptance/packrat/minibasic/atest_minibasic.rb +45 -0
  22. data/tests/acceptance/packrat/minibasic/minibasic.rb +137 -0
  23. data/tests/acceptance/rockit/dparser/atest_any_operator.rb +33 -0
  24. data/tests/acceptance/rockit/dparser/atest_arithmetic_grammar.rb +30 -0
  25. data/tests/acceptance/rockit/dparser/atest_list_operator.rb +57 -0
  26. data/tests/acceptance/rockit/dparser/atest_mult_operator.rb +60 -0
  27. data/tests/acceptance/rockit/dparser/atest_operator_grammar.rb +61 -0
  28. data/tests/acceptance/rockit/dparser/atest_plus_operator.rb +55 -0
  29. data/tests/acceptance/rockit/dparser/atest_samples_calculator.rb +14 -0
  30. data/tests/acceptance/rockit/dparser/atest_samples_minibasic.rb +20 -0
  31. data/tests/acceptance/rockit/dparser/atest_samples_multifunccalculator.rb +36 -0
  32. data/tests/acceptance/rockit/dparser/atest_simple_grammar.rb +34 -0
  33. data/tests/acceptance/rockit/dparser/atest_speculative_code_action.rb +128 -0
  34. data/tests/acceptance/rockit/dparser/calc_tests_common.rb +103 -0
  35. data/tests/unit/packrat/test_interpreting_parser.rb +296 -0
  36. data/tests/unit/parse/utest_ebnf_grammar.rb +50 -0
  37. data/tests/unit/parse/utest_expand_grammar.rb +23 -0
  38. data/tests/unit/parse/utest_grammar.rb +160 -0
  39. data/tests/unit/rockit/assembler/llvm/utest_instructions.rb +41 -0
  40. data/tests/unit/rockit/assembler/llvm/utest_module.rb +19 -0
  41. data/tests/unit/rockit/prettyprint/utest_box.rb +44 -0
  42. data/tests/unit/rockit/tree/utest_tree_base.rb +301 -0
  43. data/tests/unit/rockit/tree/utest_tree_enter_leave_visitor.rb +69 -0
  44. data/tests/unit/rockit/tree/utest_tree_visitor.rb +63 -0
  45. data/tests/unit/rockit/utest_grammar.rb +145 -0
  46. data/tests/unit/rockit/utest_grammar_symbol.rb +11 -0
  47. data/tests/unit/rockit/utest_maybe_operator.rb +12 -0
  48. data/tests/unit/rockit/utest_regexp_terminal.rb +45 -0
  49. data/tests/unit/rockit/utest_repetition_operators.rb +35 -0
  50. data/tests/unit/rockit/utest_rule.rb +23 -0
  51. data/tests/unit/rockit/utest_string_terminal.rb +40 -0
  52. data/tests/unit/util/utest_array_alternatives.rb +23 -0
  53. data/tests/unit/util/utest_enter_leave_visitor.rb +89 -0
  54. data/tests/unit/util/utest_string_location.rb +42 -0
  55. data/tests/unit/util/utest_visitor.rb +92 -0
  56. data/tests/unit/util/utest_visitor_combinators.rb +64 -0
  57. metadata +112 -0
@@ -0,0 +1,20 @@
1
+ require 'test/unit'
2
+ require 'rockit/dparser'
3
+
4
+ require 'minibasic/minibasic_grammar'
5
+
6
+ class ATestSamplesMinibasic < Test::Unit::TestCase
7
+ # Test the String regexp by itself since it is special
8
+ StringTermGrammar = Rockit::DParser::Grammar.new do
9
+ start :S, [["PRINT", :String, value(1)]]
10
+ term :String, /"[^"]*"/
11
+ end
12
+
13
+ def test_01_string_term
14
+ parser = StringTermGrammar.new_parser
15
+ value = parser.parse 'PRINT "a"'
16
+ assert_equal('"a"', value)
17
+ end
18
+
19
+ MiniBasicParser = Samples::MiniBasicGrammar.new_parser
20
+ end
@@ -0,0 +1,36 @@
1
+ require 'test/unit'
2
+ require 'rockit/dparser'
3
+ require 'calculator/multifunc_calculator'
4
+
5
+ require File.join(File.dirname(__FILE__), "calc_tests_common")
6
+
7
+ class ATestSamplesMultiFuncCalc < Test::Unit::TestCase
8
+ include CalcTestsCommon
9
+
10
+ def setup
11
+ @grammar = Samples::MultiFuncCalcGrammar
12
+ @calculator = Samples::MultiFuncCalculator.new
13
+ end
14
+
15
+ def test_10_eval
16
+ assert_calc(8, "2^3")
17
+
18
+ # The calculator is the same between successive calls to assert_calc so
19
+ # the variables should retain their values
20
+ assert_calc(1, "a = 1")
21
+ assert_calc(-2, "b=-2")
22
+ assert_calc(-1, "a+b")
23
+ assert_calc(-97, "c = (3 - 100)")
24
+ assert_calc(-2*-97+(4*1), "(b*c)+(4*a)")
25
+
26
+ assert_calc(Math.log(2), "ln(2)")
27
+ end
28
+
29
+ def test_11_exponentiation_is_right_associative
30
+ assert_calc(2**(3**3), "2^3^3")
31
+ end
32
+
33
+ def test_12_precedence_and_associativity
34
+ assert_calc(15, "1+2*3^2-4")
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ require 'test/unit'
2
+ require 'rockit/dparser'
3
+
4
+ class ATestSimpleGrammar < Test::Unit::TestCase
5
+ SimpleTestGrammar = Rockit::DParser::Grammar.new do
6
+ start :Str, [[:A, :B, :A, :C, ast(:Str, 0, 1, 3)]]
7
+ term :A, ["a"]
8
+ term :B, ["b"]
9
+ term :C, ["c"]
10
+ end
11
+
12
+ def test_01_creation_of_grammar
13
+ assert_kind_of(Rockit::DParser::Grammar, SimpleTestGrammar)
14
+ assert_equal(1, SimpleTestGrammar.rules.length)
15
+ assert_equal(3, SimpleTestGrammar.terms.values.length)
16
+ assert_equal(:Str, SimpleTestGrammar.start_symbol)
17
+ end
18
+
19
+ def test_02_creation_of_parser
20
+ parser = SimpleTestGrammar.new_parser
21
+ assert_kind_of(Rockit::DParser::Parser, parser)
22
+ end
23
+
24
+ def test_03_use_parser
25
+ parser = SimpleTestGrammar.new_parser
26
+ ast = parser.parse "ab a\tc"
27
+ assert_equal("Str", ast.name)
28
+ assert_kind_of(parser.astclass_of_name(:Str), ast)
29
+ assert_equal(3, ast.num_children)
30
+ assert_equal("a", ast[0])
31
+ assert_equal("b", ast[1])
32
+ assert_equal("c", ast[2])
33
+ end
34
+ end
@@ -0,0 +1,128 @@
1
+ require 'test/unit'
2
+ require 'rockit/dparser'
3
+ require 'rockit/symbol_table'
4
+
5
+ class ATestSpeculativeCodeAction < Test::Unit::TestCase
6
+ TestGrammar = Rockit::DParser::Grammar.new do
7
+ start :S, [[:Id, ast(:S, 0)]]
8
+ rule :Id, [[:AIdent]]
9
+
10
+ rule :AIdent, [
11
+ [:Ident,
12
+ # Speculative code that rejects match unless the ident starts with "A"
13
+ speculative do |parser|
14
+ parser.reject if parser.get_value_of_child(0)[0,1] != "A"
15
+ end,
16
+ # Final code
17
+ ast(:A, 0),
18
+ ]
19
+ ]
20
+
21
+ term :Ident, /[A-Za-z]+/
22
+ end
23
+
24
+ def test_01_no_reject
25
+ parser = TestGrammar.new_parser
26
+ ast = parser.parse "ABBA"
27
+ assert_equal("S", ast.name)
28
+ assert_kind_of(parser.astclass_of_name(:S), ast)
29
+ assert_equal(1, ast.num_children)
30
+ assert_kind_of(parser.astclass_of_name(:A), ast[0])
31
+ assert_equal(1, ast[0].num_children)
32
+ assert_equal("ABBA", ast[0][0])
33
+ end
34
+
35
+ def test_02_reject
36
+ parser = TestGrammar.new_parser
37
+ # Make sure "CDE" is not a valid parse even though it matches Ident
38
+ # since it should be rejected by the speculative action code
39
+ assert_raises(RuntimeError) {ast = parser.parse "CDE"}
40
+ assert_raises(RuntimeError) {ast = parser.parse "B"}
41
+ assert_raises(RuntimeError) {ast = parser.parse "D"}
42
+ assert_raises(RuntimeError) {ast = parser.parse "G"}
43
+ end
44
+
45
+ # This example is a more realistic one with a SymbolTable that saves
46
+ # symbols and indicates that they are type names.
47
+ SymbolTableTestGrammar = Rockit::DParser::Grammar.new do
48
+ parser_include Rockit::ParserSymbolTable
49
+
50
+ start :TranslationUnit, [
51
+ [mult(:Declaration), plus(:Assignment)],
52
+ ], ast(:TU, 0, 1)
53
+
54
+ rule :Assignment, [
55
+ [:Identifier, "=", :Identifier, ast(:Assign, 0, 2)],
56
+ ]
57
+
58
+ rule :Declaration, [
59
+ [plus(:StorageClassSpecifier), :Identifier, ";",
60
+
61
+ # Speculative code that inserts identifier into symbol table
62
+ # if it is a typedef.
63
+ speculative do |parser|
64
+ e0, e1 = parser.get_value_of_child(0), parser.get_value_of_child(1)
65
+ if e0.include?("typedef") && e1.name == "Identifier"
66
+ parser.symbol_table.add_typename(e1[0])
67
+ end
68
+ end,
69
+
70
+ ast(:Declaration, 0, 1),
71
+ ],
72
+ ]
73
+
74
+ rule :StorageClassSpecifier, [
75
+ ['auto'], ['register'], ['static'], ['extern'], ['typedef'],
76
+ ]
77
+
78
+ rule :Identifier, [
79
+ [:Ident,
80
+
81
+ # Speculative code here that rejects the ident if it is in the
82
+ # symbol table and is a type name
83
+ speculative do |parser|
84
+ e0 = parser.get_value_of_child(0)
85
+ parser.reject if parser.symbol_table.typename?(e0)
86
+ end,
87
+
88
+ ast(:Identifier, 0)
89
+ ]
90
+ ]
91
+
92
+ term :Ident, /[A-Za-z]+/
93
+ end
94
+
95
+ def test_03_valid_with_symbol_table_but_without_typedef
96
+ parser = SymbolTableTestGrammar.new_parser
97
+ ast = parser.parse "A = B"
98
+ assert_equal("TU", ast.name)
99
+ assert_kind_of(parser.astclass_of_name(:TU), ast)
100
+ assert_equal(2, ast.num_children)
101
+ assert_equal(0, ast[0].length)
102
+ assert_equal(1, ast[1].length)
103
+ assert_equal("Assign", ast[1][0].name)
104
+ assert_equal("A", ast[1][0][0][0])
105
+ assert_equal("B", ast[1][0][1][0])
106
+ end
107
+
108
+ def test_03_valid_with_symbol_table_and_typedef
109
+ parser = SymbolTableTestGrammar.new_parser
110
+ ast = parser.parse "typedef C; A = B"
111
+ assert_equal("TU", ast.name)
112
+ assert_kind_of(parser.astclass_of_name(:TU), ast)
113
+ assert_equal(2, ast.num_children)
114
+ assert_equal(1, ast[0].length)
115
+ assert_equal("Declaration", ast[0][0].name)
116
+ assert_equal("C", ast[0][0][1][0])
117
+ assert_equal(1, ast[1].length)
118
+ assert_equal("Assign", ast[1][0].name)
119
+ end
120
+
121
+ def test_04_invalid_with_typedef_and_symbol_table
122
+ parser = SymbolTableTestGrammar.new_parser
123
+ # Should not parse ok since the typedef means the A cannot later be an
124
+ # identifier.
125
+ assert_raises(RuntimeError) {ast = parser.parse "typedef A; A = B"}
126
+ assert_raises(RuntimeError) {ast = parser.parse "typedef B; A = B"}
127
+ end
128
+ end
@@ -0,0 +1,103 @@
1
+ # Tests common to several calculators
2
+ module CalcTestsCommon
3
+ def parser
4
+ @parser ||= @grammar.new_parser
5
+ end
6
+
7
+ def test_01_grammar
8
+ assert_kind_of(Rockit::DParser::Grammar, @grammar)
9
+ end
10
+
11
+ def test_02_creation_of_parser
12
+ assert_kind_of(Rockit::DParser::Parser, parser)
13
+ end
14
+
15
+ def test_03_constant
16
+ ast = parser.parse "1234567890"
17
+ assert_equal("Constant", ast.name)
18
+ assert_kind_of(parser.astclass_of_name(:Constant), ast)
19
+ assert_equal(1, ast.num_children)
20
+ assert_equal("1234567890", ast[0])
21
+ assert_equal("1234567890", ast.num)
22
+ end
23
+
24
+ Ops = [["+", :Plus], ["-", :Minus], ["*", :Mul], ["/", :Div]]
25
+
26
+ def test_04_operators
27
+ Ops.each do |op, n|
28
+ num1, num2 = rand(10).to_s, rand(10).to_s
29
+ str = num1 + op + num2
30
+ ast = parser.parse str
31
+ assert_equal(n.to_s, ast.name)
32
+ assert_kind_of(parser.astclass_of_name(n), ast)
33
+ assert_equal(2, ast.num_children)
34
+
35
+ assert_kind_of(parser.astclass_of_name(:Constant), ast[0])
36
+ assert_equal(num1, ast[0].num)
37
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.left)
38
+ assert_equal(num1, ast.left.num)
39
+
40
+ assert_kind_of(parser.astclass_of_name(:Constant), ast[1])
41
+ assert_equal(num2, ast[1].num)
42
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.right)
43
+ assert_equal(num2, ast.right.num)
44
+ end
45
+ end
46
+
47
+ def test_05_paren
48
+ ast = parser.parse "2 * (4 + 67)"
49
+ assert_kind_of(parser.astclass_of_name(:Mul), ast)
50
+ assert_equal(2, ast.num_children)
51
+
52
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.left)
53
+ assert_equal("2", ast.left.num)
54
+
55
+ assert_kind_of(parser.astclass_of_name(:Plus), ast.right)
56
+
57
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.right.left)
58
+ assert_equal("4", ast.right.left.num)
59
+
60
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.right.right)
61
+ assert_equal("67", ast.right.right.num)
62
+
63
+ assert_equal('Mul[Constant["2"], Plus[Constant["4"], Constant["67"]]]',
64
+ ast.inspect)
65
+ end
66
+
67
+ def assert_calc(expected, str)
68
+ assert_equal(expected, @calculator.eval(parser.parse(str)))
69
+ end
70
+
71
+ def test_06_left_associative_operators
72
+ Ops.each do |op, name|
73
+ ast = parser.parse "1 #{op} 2 #{op} 3"
74
+ assert_kind_of(parser.astclass_of_name(name), ast)
75
+
76
+ assert_kind_of(parser.astclass_of_name(name), ast.left)
77
+ assert_equal("1", ast.left.left.num)
78
+ assert_equal("2", ast.left.right.num)
79
+
80
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.right)
81
+ assert_equal("3", ast.right.num)
82
+ end
83
+ end
84
+
85
+ def test_07_mul_higher_precedence_than_plus
86
+ ast = parser.parse "1 + 2 * 3"
87
+ assert_kind_of(parser.astclass_of_name(:Plus), ast)
88
+
89
+ assert_kind_of(parser.astclass_of_name(:Constant), ast.left)
90
+ assert_equal("1", ast.left.num)
91
+
92
+ assert_kind_of(parser.astclass_of_name(:Mul), ast.right)
93
+ assert_equal("2", ast.right.left.num)
94
+ assert_equal("3", ast.right.right.num)
95
+ end
96
+
97
+ def test_08_eval
98
+ assert_calc(2, "2")
99
+ assert_calc(2 * (4+67), "2 * (4 + 67)")
100
+ assert_calc(987 * (456+671), "987*(456+671)")
101
+ assert_calc((9-5) * (100/10), "(9-5) * (100/10)")
102
+ end
103
+ end
@@ -0,0 +1,296 @@
1
+ require 'test/unit'
2
+
3
+ require 'packrat/grammar'
4
+
5
+ class TestInterpretingParser < Test::Unit::TestCase
6
+ def test_01_grammar_with_single_production_with_single_regexp
7
+ g = Packrat::Grammar.new do
8
+ start_symbol :s
9
+ prod :s, [/a+/]
10
+ end
11
+ ip = g.interpreting_parser
12
+
13
+ res1 = ip.parse_string("a")
14
+ assert_equal([:s, "a"], res1)
15
+
16
+ res2 = ip.parse_string("aaaa")
17
+ assert_equal([:s, "aaaa"], res2)
18
+
19
+ res3 = ip.parse_string("b")
20
+ assert_equal(false, res3)
21
+ end
22
+
23
+ def test_02_grammar_with_single_production_with_multiple_regexps
24
+ g = Packrat::Grammar.new do
25
+ start_symbol :first
26
+ prod :first, [/a+/, /b+/, /dc*/]
27
+ end
28
+ ip = g.interpreting_parser
29
+
30
+ res1 = ip.parse_string("abbd")
31
+ assert_equal([:first, "a", "bb", "d"], res1)
32
+
33
+ res2 = ip.parse_string("aaabdcc")
34
+ assert_equal([:first, "aaa", "b", "dcc"], res2)
35
+
36
+ res2 = ip.parse_string("aaabdcc")
37
+ assert_equal([:first, "aaa", "b", "dcc"], res2)
38
+ end
39
+
40
+ def test_03_grammar_with_single_rule_with_multi_prods
41
+ g = Packrat::Grammar.new do
42
+ start_symbol :s
43
+ rule :s, [
44
+ [/a+/, /b+/, /dc*/],
45
+ [/a+/, /d+/]
46
+ ]
47
+ end
48
+ ip = g.interpreting_parser
49
+
50
+ res1 = ip.parse_string("aaabdcc")
51
+ assert_equal([:s, "aaa", "b", "dcc"], res1)
52
+
53
+ res2 = ip.parse_string("ad")
54
+ assert_equal([:s, "a", "d"], res2)
55
+
56
+ res3 = ip.parse_string("abc")
57
+ assert_equal(false, res3)
58
+ end
59
+
60
+ def test_04_grammar_with_single_rule_and_ref_to_one_external_prod
61
+ g = Packrat::Grammar.new do
62
+ start_symbol :s
63
+ rule :s, [
64
+ [:a, /b+/, /dc*/],
65
+ [:a, /d+/]
66
+ ]
67
+ prod :a, [/a+/]
68
+ end
69
+ ip = g.interpreting_parser
70
+
71
+ res1 = ip.parse_string("aaabdcc")
72
+ assert_equal([:s, [:a, "aaa"], "b", "dcc"], res1)
73
+
74
+ res2 = ip.parse_string("ad")
75
+ assert_equal([:s, [:a, "a"], "d"], res2)
76
+
77
+ res3 = ip.parse_string("abc")
78
+ assert_equal(false, res3)
79
+ end
80
+
81
+ def test_05_grammar_with_multi_rules
82
+ g = Packrat::Grammar.new do
83
+ start_symbol :Constant
84
+ rule :Constant, [
85
+ [:DecimalInt],
86
+ [:HexInt],
87
+ [:OctalInt],
88
+ ]
89
+ rule :DecimalInt, [[/[1-9][0-9]*[uUlL]?/]]
90
+ rule :HexInt, [[/(0x|0X)[0-9a-fA-F]+[uUlL]?/]]
91
+ prod :OctalInt, [/0[0-7]*[uUlL]?/]
92
+ end
93
+ ip = g.interpreting_parser
94
+
95
+ res1 = ip.parse_string("123L")
96
+ assert_equal([:Constant, [:DecimalInt, "123L"]], res1)
97
+
98
+ res2 = ip.parse_string("0xad97")
99
+ assert_equal([:Constant, [:HexInt, "0xad97"]], res2)
100
+
101
+ res3 = ip.parse_string("02376")
102
+ assert_equal([:Constant, [:OctalInt, "02376"]], res3)
103
+
104
+ res4 = ip.parse_string("xg")
105
+ assert_equal(false, res4)
106
+ end
107
+
108
+ def test_06_plus
109
+ g = Packrat::Grammar.new do
110
+ start_symbol :s
111
+ prod :s, [plus(/ab/), /c/]
112
+ end
113
+ ip = g.interpreting_parser
114
+
115
+ res1 = ip.parse_string("abababc")
116
+ assert_equal([:s, ["ab", "ab", "ab"], "c"], res1)
117
+
118
+ res2 = ip.parse_string("c")
119
+ assert_equal(false, res2)
120
+ end
121
+
122
+ def test_07_mult
123
+ g = Packrat::Grammar.new do
124
+ start_symbol :s
125
+ prod :s, [mult(/ab/), /c/]
126
+ end
127
+ ip = g.interpreting_parser
128
+
129
+ res1 = ip.parse_string("abababc")
130
+ assert_equal([:s, ["ab", "ab", "ab"], "c"], res1)
131
+
132
+ res2 = ip.parse_string("c")
133
+ assert_equal([:s, [], "c"], res2)
134
+ end
135
+
136
+ def test_08_rep
137
+ g = Packrat::Grammar.new do
138
+ start_symbol :s
139
+ prod :s, [rep(2, 4, /ab/), /c/]
140
+ end
141
+ ip = g.interpreting_parser
142
+
143
+ r = ip.parse_string("ababc")
144
+ assert_equal([:s, ["ab", "ab"], "c"], r)
145
+
146
+ r = ip.parse_string("abababc")
147
+ assert_equal([:s, ["ab", "ab", "ab"], "c"], r)
148
+
149
+ r = ip.parse_string("ababababc")
150
+ assert_equal([:s, ["ab", "ab", "ab", "ab"], "c"], r)
151
+
152
+ r = ip.parse_string("c")
153
+ assert_equal(false, r)
154
+
155
+ r = ip.parse_string("abc")
156
+ assert_equal(false, r)
157
+
158
+ r = ip.parse_string("abababababc")
159
+ assert_equal(false, r)
160
+ end
161
+
162
+ def test_09_maybe
163
+ g = Packrat::Grammar.new do
164
+ start_symbol :s
165
+ prod :s, [maybe(/ab/), /c/]
166
+ end
167
+ ip = g.interpreting_parser
168
+
169
+ r = ip.parse_string("c")
170
+ assert_equal([:s, nil, "c"], r)
171
+
172
+ r = ip.parse_string("abc")
173
+ assert_equal([:s, "ab", "c"], r)
174
+ end
175
+
176
+ def test_10_string_literal
177
+ g = Packrat::Grammar.new do
178
+ start_symbol :s
179
+ prod :s, ["a*b+()", /c/]
180
+ end
181
+ ip = g.interpreting_parser
182
+
183
+ r = ip.parse_string("c")
184
+ assert_equal(false, r)
185
+
186
+ r = ip.parse_string("abbc")
187
+ assert_equal(false, r)
188
+
189
+ r = ip.parse_string("a*b+()c")
190
+ assert_equal([:s, "a*b+()", "c"], r)
191
+ end
192
+
193
+ def test_11_lift
194
+ g = Packrat::Grammar.new do
195
+ start_symbol :s
196
+ prod :s, [/a+/, /b+/, lift(1)]
197
+ end
198
+ ip = g.interpreting_parser
199
+
200
+ r = ip.parse_string("abb")
201
+ assert_equal("bb", r)
202
+
203
+ r = ip.parse_string("bb")
204
+ assert_equal(false, r)
205
+ end
206
+
207
+ def test_12_sexpr
208
+ g = Packrat::Grammar.new do
209
+ start_symbol :s
210
+ prod :s, [/a+/, /b+/, sexpr(:a)]
211
+ end
212
+ ip = g.interpreting_parser
213
+
214
+ r = ip.parse_string("abb")
215
+ assert_equal([:a, "a", "bb"], r)
216
+
217
+ r = ip.parse_string("bb")
218
+ assert_equal(false, r)
219
+ end
220
+
221
+ def test_13_any_with_strings
222
+ g = Packrat::Grammar.new do
223
+ start_symbol :s
224
+ prod :s, [/\d+/, any('+', '*', '/'), /\d+/]
225
+ end
226
+ ip = g.interpreting_parser
227
+
228
+ r = ip.parse_string("12+34")
229
+ assert_equal([:s, "12", "+", "34"], r)
230
+
231
+ r = ip.parse_string("5*6")
232
+ assert_equal([:s, "5", "*", "6"], r)
233
+
234
+ r = ip.parse_string("7/18")
235
+ assert_equal([:s, "7", "/", "18"], r)
236
+ end
237
+
238
+ def test_13_any_with_prods
239
+ g = Packrat::Grammar.new do
240
+ start_symbol :s
241
+ prod :s, [/\d+/, any(:plus, :mult, "/"), /\d+/]
242
+ prod :plus, ["+", lift(0)]
243
+ prod :mult, ["*", lift(0)]
244
+ end
245
+ ip = g.interpreting_parser
246
+
247
+ r = ip.parse_string("12+34")
248
+ assert_equal([:s, "12", "+", "34"], r)
249
+
250
+ r = ip.parse_string("5*6")
251
+ assert_equal([:s, "5", "*", "6"], r)
252
+
253
+ r = ip.parse_string("7/18")
254
+ assert_equal([:s, "7", "/", "18"], r)
255
+ end
256
+
257
+ def test_14_ast
258
+ g = Packrat::Grammar.new do
259
+ start_symbol :s
260
+ rule :s, [
261
+ [:Num, lift(0)],
262
+ [:Id, lift(0)],
263
+ ]
264
+ prod :Num, [/\d+/, ast(:Num)]
265
+ prod :Id, ["ID", :Num, maybe("?"), ast(:Id)]
266
+ end
267
+ ip = g.interpreting_parser
268
+
269
+ mAst = g::ASTs
270
+ assert_kind_of(Module, mAst)
271
+
272
+ r = ip.parse_string("1")
273
+
274
+ # The AST classes are added during the parse so now we can see
275
+ # that they are there.
276
+ assert_kind_of(Class, mAst.const_get("Num"))
277
+ assert(mAst::Num.ancestors.include?(Packrat::AST))
278
+
279
+ as = mAst::Num[]
280
+ assert_kind_of(mAst::Num, as)
281
+
282
+ assert_kind_of(Packrat::AST, r)
283
+ assert_equal('Num["1"]', r.inspect)
284
+ assert_equal("1", r[0])
285
+
286
+ r = ip.parse_string("ID10")
287
+ assert_kind_of(Class, mAst.const_get("Id"))
288
+ assert(mAst::Id.ancestors.include?(Packrat::AST))
289
+ assert_kind_of(Packrat::AST, r)
290
+ assert_equal('Id["ID", Num["10"], nil]', r.inspect)
291
+ assert_equal("ID", r[0])
292
+ assert_equal("10", r[1][0])
293
+ assert_equal(nil, r[2])
294
+ assert_equal("10", r.num[0])
295
+ end
296
+ end
@@ -0,0 +1,50 @@
1
+ require 'test/unit'
2
+
3
+ require 'parse/ebnf_grammar'
4
+ include Parse::Grammar
5
+
6
+ class UT_Parse_ebnf_grammar < Test::Unit::TestCase
7
+ def test_01_Maybe_expansion
8
+ g = Grammar.new do
9
+ r :Start, [
10
+ [maybe(:A), "1"]
11
+ ]
12
+ r :A, ["a"]
13
+ end
14
+ g.expand!
15
+ assert_equal(3, g.nonterminals.length)
16
+ end
17
+
18
+ def test_02_Plus_expansion
19
+ g = Grammar.new do
20
+ r :Start, [
21
+ [plus(:A), "1"]
22
+ ]
23
+ r :A, ["a"]
24
+ end
25
+ g.expand!
26
+ assert_equal(4, g.nonterminals.length)
27
+ end
28
+
29
+ def test_03_Mult_expansion
30
+ g = Grammar.new do
31
+ r :Start, [
32
+ [mult(:A), "1"]
33
+ ]
34
+ r :A, ["a"]
35
+ end
36
+ g.expand!
37
+ assert_equal(3, g.nonterminals.length)
38
+ end
39
+
40
+ def test_04_Group_expansion
41
+ g = Grammar.new do
42
+ r :Start, [
43
+ [group("1", "2"), "3"]
44
+ ]
45
+ end
46
+ g.expand!
47
+ puts g.inspect
48
+ assert_equal(2, g.nonterminals.length)
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/unit'
2
+
3
+ require 'parse/expand_grammar'
4
+ include Parse::Grammar
5
+
6
+ class UT_Parse_expand_grammar < Test::Unit::TestCase
7
+ def test_01_internal_nonterm_num
8
+ g = Grammar.new
9
+ assert_equal(1, g.next_free_internal_nonterminal_num)
10
+ assert_equal(2, g.next_free_internal_nonterminal_num)
11
+ assert_equal(3, g.next_free_internal_nonterminal_num)
12
+ end
13
+
14
+ def test_02_create_fresh_nonterminal
15
+ g = Grammar.new
16
+ nt = g.create_fresh_nonterminal
17
+ assert_kind_of(NonTerminal, nt)
18
+
19
+ # The name should start with the prefix
20
+ re = Regexp.new("^" + Grammar::InternalNontermNamePrefix)
21
+ assert_match(re, nt.name.to_s)
22
+ end
23
+ end