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.
- data/BUGS +13 -0
- data/LICENSE +280 -0
- data/README +172 -0
- data/TODO +53 -0
- data/VERSION +1 -0
- data/lib/packrat/grammar.rb +537 -0
- data/lib/rockit/prettyprint/box.rb +60 -0
- data/lib/rockit/prettyprint/renderer.rb +41 -0
- data/lib/rockit/prettyprint/text_renderer.rb +47 -0
- data/lib/rockit/tree/base.rb +223 -0
- data/lib/rockit/tree/enter_leave_visitor.rb +12 -0
- data/lib/rockit/tree/graphviz.rb +69 -0
- data/lib/rockit/tree/visitor.rb +12 -0
- data/lib/util/array_alternatives.rb +20 -0
- data/lib/util/enter_leave_visitor.rb +69 -0
- data/lib/util/graphviz_dot.rb +182 -0
- data/lib/util/string_location.rb +42 -0
- data/lib/util/visitor.rb +49 -0
- data/lib/util/visitor_combinators.rb +14 -0
- data/rakefile +200 -0
- data/tests/acceptance/packrat/minibasic/atest_minibasic.rb +45 -0
- data/tests/acceptance/packrat/minibasic/minibasic.rb +137 -0
- data/tests/acceptance/rockit/dparser/atest_any_operator.rb +33 -0
- data/tests/acceptance/rockit/dparser/atest_arithmetic_grammar.rb +30 -0
- data/tests/acceptance/rockit/dparser/atest_list_operator.rb +57 -0
- data/tests/acceptance/rockit/dparser/atest_mult_operator.rb +60 -0
- data/tests/acceptance/rockit/dparser/atest_operator_grammar.rb +61 -0
- data/tests/acceptance/rockit/dparser/atest_plus_operator.rb +55 -0
- data/tests/acceptance/rockit/dparser/atest_samples_calculator.rb +14 -0
- data/tests/acceptance/rockit/dparser/atest_samples_minibasic.rb +20 -0
- data/tests/acceptance/rockit/dparser/atest_samples_multifunccalculator.rb +36 -0
- data/tests/acceptance/rockit/dparser/atest_simple_grammar.rb +34 -0
- data/tests/acceptance/rockit/dparser/atest_speculative_code_action.rb +128 -0
- data/tests/acceptance/rockit/dparser/calc_tests_common.rb +103 -0
- data/tests/unit/packrat/test_interpreting_parser.rb +296 -0
- data/tests/unit/parse/utest_ebnf_grammar.rb +50 -0
- data/tests/unit/parse/utest_expand_grammar.rb +23 -0
- data/tests/unit/parse/utest_grammar.rb +160 -0
- data/tests/unit/rockit/assembler/llvm/utest_instructions.rb +41 -0
- data/tests/unit/rockit/assembler/llvm/utest_module.rb +19 -0
- data/tests/unit/rockit/prettyprint/utest_box.rb +44 -0
- data/tests/unit/rockit/tree/utest_tree_base.rb +301 -0
- data/tests/unit/rockit/tree/utest_tree_enter_leave_visitor.rb +69 -0
- data/tests/unit/rockit/tree/utest_tree_visitor.rb +63 -0
- data/tests/unit/rockit/utest_grammar.rb +145 -0
- data/tests/unit/rockit/utest_grammar_symbol.rb +11 -0
- data/tests/unit/rockit/utest_maybe_operator.rb +12 -0
- data/tests/unit/rockit/utest_regexp_terminal.rb +45 -0
- data/tests/unit/rockit/utest_repetition_operators.rb +35 -0
- data/tests/unit/rockit/utest_rule.rb +23 -0
- data/tests/unit/rockit/utest_string_terminal.rb +40 -0
- data/tests/unit/util/utest_array_alternatives.rb +23 -0
- data/tests/unit/util/utest_enter_leave_visitor.rb +89 -0
- data/tests/unit/util/utest_string_location.rb +42 -0
- data/tests/unit/util/utest_visitor.rb +92 -0
- data/tests/unit/util/utest_visitor_combinators.rb +64 -0
- 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
|