rattler 0.3.0 → 0.4.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/README.rdoc +57 -37
- data/features/command_line/dest_option.feature +8 -21
- data/features/command_line/lib_option.feature +37 -0
- data/features/command_line/parser_generator.feature +7 -4
- data/features/grammar/back_reference.feature +37 -0
- data/features/grammar/fail.feature +3 -3
- data/features/grammar/labels.feature +11 -3
- data/features/grammar/list_matching.feature +14 -5
- data/features/grammar/literal.feature +30 -4
- data/features/grammar/nonterminal.feature +1 -1
- data/features/grammar/ordered_choice.feature +2 -2
- data/features/grammar/skip_operator.feature +1 -1
- data/features/grammar/symantic_action.feature +7 -7
- data/features/grammar/whitespace.feature +2 -2
- data/features/step_definitions/grammar_steps.rb +2 -2
- data/lib/rattler/back_end.rb +1 -0
- data/lib/rattler/back_end/compiler.rb +19 -20
- data/lib/rattler/back_end/optimizer.rb +100 -0
- data/lib/rattler/back_end/optimizer/composite_reducing.rb +18 -0
- data/lib/rattler/back_end/optimizer/flatten_choice.rb +31 -0
- data/lib/rattler/back_end/optimizer/flatten_sequence.rb +59 -0
- data/lib/rattler/back_end/optimizer/flattening.rb +17 -0
- data/lib/rattler/back_end/optimizer/inline_regular_rules.rb +46 -0
- data/lib/rattler/back_end/optimizer/join_match_capturing_sequence.rb +71 -0
- data/lib/rattler/back_end/optimizer/join_match_choice.rb +37 -0
- data/lib/rattler/back_end/optimizer/join_match_matching_sequence.rb +38 -0
- data/lib/rattler/back_end/optimizer/join_match_sequence.rb +17 -0
- data/lib/rattler/back_end/optimizer/join_predicate_bare_match.rb +68 -0
- data/lib/rattler/back_end/optimizer/join_predicate_match.rb +17 -0
- data/lib/rattler/back_end/optimizer/join_predicate_nested_match.rb +37 -0
- data/lib/rattler/back_end/optimizer/join_predicate_or_bare_match.rb +68 -0
- data/lib/rattler/back_end/optimizer/join_predicate_or_match.rb +17 -0
- data/lib/rattler/back_end/optimizer/join_predicate_or_nested_match.rb +36 -0
- data/lib/rattler/back_end/optimizer/match_joining.rb +60 -0
- data/lib/rattler/back_end/optimizer/optimization.rb +94 -0
- data/lib/rattler/back_end/optimizer/optimization_context.rb +72 -0
- data/lib/rattler/back_end/optimizer/optimization_sequence.rb +37 -0
- data/lib/rattler/back_end/optimizer/optimize_children.rb +46 -0
- data/lib/rattler/back_end/optimizer/reduce_repeat_match.rb +44 -0
- data/lib/rattler/back_end/optimizer/remove_meaningless_wrapper.rb +32 -0
- data/lib/rattler/back_end/optimizer/simplify_redundant_repeat.rb +43 -0
- data/lib/rattler/back_end/optimizer/simplify_token_match.rb +38 -0
- data/lib/rattler/back_end/parser_generator.rb +21 -14
- data/lib/rattler/back_end/parser_generator/apply_generator.rb +35 -35
- data/lib/rattler/back_end/parser_generator/assert_generator.rb +29 -30
- data/lib/rattler/back_end/parser_generator/back_reference_generator.rb +93 -0
- data/lib/rattler/back_end/parser_generator/choice_generator.rb +33 -49
- data/lib/rattler/back_end/parser_generator/direct_action_generator.rb +14 -14
- data/lib/rattler/back_end/parser_generator/disallow_generator.rb +29 -30
- data/lib/rattler/back_end/parser_generator/dispatch_action_generator.rb +11 -13
- data/lib/rattler/back_end/parser_generator/expr_generator.rb +36 -56
- data/lib/rattler/back_end/parser_generator/fail_generator.rb +18 -18
- data/lib/rattler/back_end/parser_generator/group_match.rb +18 -0
- data/lib/rattler/back_end/parser_generator/group_match_generator.rb +76 -0
- data/lib/rattler/back_end/parser_generator/label_generator.rb +25 -6
- data/lib/rattler/back_end/parser_generator/list1_generator.rb +7 -7
- data/lib/rattler/back_end/parser_generator/list_generating.rb +19 -20
- data/lib/rattler/back_end/parser_generator/list_generator.rb +5 -5
- data/lib/rattler/back_end/parser_generator/match_generator.rb +52 -52
- data/lib/rattler/back_end/parser_generator/one_or_more_generator.rb +6 -6
- data/lib/rattler/back_end/parser_generator/optional_generator.rb +30 -29
- data/lib/rattler/back_end/parser_generator/predicate_propogating.rb +8 -8
- data/lib/rattler/back_end/parser_generator/repeat_generating.rb +23 -25
- data/lib/rattler/back_end/parser_generator/rule_generator.rb +27 -79
- data/lib/rattler/back_end/parser_generator/rule_set_generator.rb +102 -0
- data/lib/rattler/back_end/parser_generator/sequence_generator.rb +49 -41
- data/lib/rattler/back_end/parser_generator/skip_generator.rb +14 -20
- data/lib/rattler/back_end/parser_generator/skip_propogating.rb +4 -4
- data/lib/rattler/back_end/parser_generator/sub_generating.rb +6 -0
- data/lib/rattler/back_end/parser_generator/token_generator.rb +12 -12
- data/lib/rattler/back_end/parser_generator/token_propogating.rb +2 -2
- data/lib/rattler/back_end/parser_generator/zero_or_more_generator.rb +4 -4
- data/lib/rattler/grammar.rb +4 -3
- data/lib/rattler/grammar/analysis.rb +91 -0
- data/lib/rattler/grammar/grammar.rb +37 -25
- data/lib/rattler/grammar/grammar_parser.rb +19 -11
- data/lib/rattler/grammar/metagrammar.rb +569 -800
- data/lib/rattler/grammar/rattler.rtlr +162 -144
- data/lib/rattler/parsers.rb +5 -1
- data/lib/rattler/parsers/action_code.rb +29 -15
- data/lib/rattler/parsers/apply.rb +5 -5
- data/lib/rattler/parsers/assert.rb +4 -18
- data/lib/rattler/parsers/back_reference.rb +46 -0
- data/lib/rattler/parsers/choice.rb +6 -39
- data/lib/rattler/parsers/combinator_parser.rb +32 -0
- data/lib/rattler/parsers/combining.rb +3 -29
- data/lib/rattler/parsers/direct_action.rb +27 -30
- data/lib/rattler/parsers/disallow.rb +4 -18
- data/lib/rattler/parsers/dispatch_action.rb +30 -25
- data/lib/rattler/parsers/label.rb +9 -18
- data/lib/rattler/parsers/list.rb +3 -34
- data/lib/rattler/parsers/list1.rb +4 -36
- data/lib/rattler/parsers/list_parser.rb +64 -0
- data/lib/rattler/parsers/match.rb +7 -42
- data/lib/rattler/parsers/node_code.rb +44 -0
- data/lib/rattler/parsers/one_or_more.rb +7 -27
- data/lib/rattler/parsers/optional.rb +5 -25
- data/lib/rattler/parsers/parser.rb +16 -44
- data/lib/rattler/parsers/parser_dsl.rb +13 -3
- data/lib/rattler/parsers/predicate.rb +4 -12
- data/lib/rattler/parsers/rule.rb +18 -19
- data/lib/rattler/parsers/rule_set.rb +63 -0
- data/lib/rattler/parsers/sequence.rb +12 -46
- data/lib/rattler/parsers/skip.rb +12 -26
- data/lib/rattler/parsers/token.rb +6 -21
- data/lib/rattler/parsers/zero_or_more.rb +6 -26
- data/lib/rattler/runner.rb +66 -28
- data/lib/rattler/runtime/extended_packrat_parser.rb +26 -20
- data/lib/rattler/runtime/packrat_parser.rb +17 -21
- data/lib/rattler/runtime/parser.rb +12 -2
- data/lib/rattler/runtime/recursive_descent_parser.rb +3 -11
- data/lib/rattler/util.rb +2 -1
- data/lib/rattler/util/graphviz.rb +29 -0
- data/lib/rattler/util/graphviz/digraph_builder.rb +71 -0
- data/lib/rattler/util/graphviz/node_builder.rb +84 -0
- data/lib/rattler/util/node.rb +37 -19
- data/lib/rattler/util/parser_spec_helper.rb +61 -35
- data/spec/rattler/back_end/compiler_spec.rb +6 -860
- data/spec/rattler/back_end/optimizer/flatten_choice_spec.rb +70 -0
- data/spec/rattler/back_end/optimizer/flatten_sequence_spec.rb +130 -0
- data/spec/rattler/back_end/optimizer/inline_regular_rules_spec.rb +80 -0
- data/spec/rattler/back_end/optimizer/join_match_capturing_sequence_spec.rb +241 -0
- data/spec/rattler/back_end/optimizer/join_match_choice_spec.rb +100 -0
- data/spec/rattler/back_end/optimizer/join_match_matching_sequence_spec.rb +112 -0
- data/spec/rattler/back_end/optimizer/join_predicate_bare_match_spec.rb +194 -0
- data/spec/rattler/back_end/optimizer/join_predicate_nested_match_spec.rb +180 -0
- data/spec/rattler/back_end/optimizer/join_predicate_or_bare_match_spec.rb +153 -0
- data/spec/rattler/back_end/optimizer/join_predicate_or_nested_match_spec.rb +153 -0
- data/spec/rattler/back_end/optimizer/reduce_repeat_match_spec.rb +98 -0
- data/spec/rattler/back_end/optimizer/simplify_redundant_repeat_spec.rb +226 -0
- data/spec/rattler/back_end/optimizer/simplify_token_match_spec.rb +85 -0
- data/spec/rattler/back_end/parser_generator/apply_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/assert_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/back_reference_generator_spec.rb +181 -0
- data/spec/rattler/back_end/parser_generator/choice_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/direct_action_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/disallow_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/dispatch_action_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/group_match_generator_spec.rb +185 -0
- data/spec/rattler/back_end/parser_generator/label_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/list1_generator_spec.rb +10 -5
- data/spec/rattler/back_end/parser_generator/list_generator_spec.rb +10 -5
- data/spec/rattler/back_end/parser_generator/match_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/one_or_more_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/optional_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/rule_generator_spec.rb +13 -46
- data/spec/rattler/back_end/parser_generator/rule_set_generator_spec.rb +97 -0
- data/spec/rattler/back_end/parser_generator/sequence_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/skip_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/token_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/zero_or_more_generator_spec.rb +39 -34
- data/spec/rattler/back_end/shared_compiler_examples.rb +885 -0
- data/spec/rattler/grammar/analysis_spec.rb +167 -0
- data/spec/rattler/grammar/grammar_parser_spec.rb +169 -179
- data/spec/rattler/grammar/grammar_spec.rb +24 -21
- data/spec/rattler/parsers/action_code_spec.rb +64 -19
- data/spec/rattler/parsers/apply_spec.rb +9 -9
- data/spec/rattler/parsers/back_reference_spec.rb +38 -0
- data/spec/rattler/parsers/combinator_parser_spec.rb +14 -0
- data/spec/rattler/parsers/direct_action_spec.rb +16 -2
- data/spec/rattler/parsers/dispatch_action_spec.rb +15 -32
- data/spec/rattler/parsers/fail_spec.rb +6 -4
- data/spec/rattler/parsers/label_spec.rb +10 -28
- data/spec/rattler/parsers/node_code_spec.rb +48 -0
- data/spec/rattler/parsers/parser_dsl_spec.rb +1 -1
- data/spec/rattler/parsers/rule_set_spec.rb +35 -0
- data/spec/rattler/parsers/sequence_spec.rb +15 -24
- data/spec/rattler/runtime/extended_packrat_parser_spec.rb +22 -17
- data/spec/rattler/runtime/packrat_parser_spec.rb +1 -1
- data/spec/rattler/runtime/parse_node_spec.rb +15 -19
- data/spec/rattler/runtime/recursive_descent_parser_spec.rb +1 -1
- data/spec/rattler/runtime/shared_parser_examples.rb +61 -28
- data/spec/rattler/util/graphviz/node_builder_spec.rb +84 -0
- data/spec/rattler/util/node_spec.rb +92 -65
- data/spec/rattler_spec.rb +16 -16
- data/spec/support/combinator_parser_spec_helper.rb +19 -18
- data/spec/support/compiler_spec_helper.rb +56 -87
- data/spec/support/runtime_parser_spec_helper.rb +6 -14
- metadata +117 -22
- data/features/grammar/regex.feature +0 -24
- data/lib/rattler/parsers/match_joining.rb +0 -67
- data/lib/rattler/parsers/rules.rb +0 -43
@@ -1,22 +1,23 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
2
|
|
3
|
+
include Rattler::BackEnd::ParserGenerator
|
3
4
|
include Rattler::Parsers
|
4
5
|
|
5
|
-
describe
|
6
|
-
|
6
|
+
describe TokenGenerator do
|
7
|
+
|
7
8
|
include ParserGeneratorSpecHelper
|
8
|
-
|
9
|
+
|
9
10
|
let(:token) { Token[Match[/\w+/]] }
|
10
|
-
|
11
|
+
|
11
12
|
describe '#gen_basic' do
|
12
|
-
|
13
|
+
|
13
14
|
context 'when nested' do
|
14
15
|
it 'generates nested token matching code' do
|
15
16
|
nested_code {|g| g.gen_basic token }.
|
16
17
|
should == '@scanner.scan(/\w+/)'
|
17
18
|
end
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
context 'when top-level' do
|
21
22
|
it 'generates top level token matching code' do
|
22
23
|
top_level_code {|g| g.gen_basic token }.
|
@@ -24,16 +25,16 @@ describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
|
-
|
28
|
+
|
28
29
|
describe '#gen_assert' do
|
29
|
-
|
30
|
+
|
30
31
|
context 'when nested' do
|
31
32
|
it 'generates nested positive lookahead code' do
|
32
33
|
nested_code {|g| g.gen_assert token }.
|
33
34
|
should == '(@scanner.skip(/(?=\w+)/) && true)'
|
34
35
|
end
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
context 'when top-level' do
|
38
39
|
it 'generates top level positive lookahead code' do
|
39
40
|
top_level_code {|g| g.gen_assert token }.
|
@@ -41,16 +42,16 @@ describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
44
|
-
|
45
|
+
|
45
46
|
describe '#gen_disallow' do
|
46
|
-
|
47
|
+
|
47
48
|
context 'when nested' do
|
48
49
|
it 'generates nested negative lookahead code' do
|
49
50
|
nested_code {|g| g.gen_disallow token }.
|
50
51
|
should == '(@scanner.skip(/(?!\w+)/) && true)'
|
51
52
|
end
|
52
53
|
end
|
53
|
-
|
54
|
+
|
54
55
|
context 'when top-level' do
|
55
56
|
it 'generates top level negative lookahead code' do
|
56
57
|
top_level_code {|g| g.gen_disallow token }.
|
@@ -58,12 +59,14 @@ describe Rattler::BackEnd::ParserGenerator::TokenGenerator do
|
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|
61
|
-
|
62
|
+
|
62
63
|
describe '#gen_dispatch_action' do
|
63
|
-
|
64
|
+
|
65
|
+
let(:code) { NodeCode.new('Word', 'parsed') }
|
66
|
+
|
64
67
|
context 'when nested' do
|
65
68
|
it 'generates nested matching code with a dispatch action' do
|
66
|
-
nested_code {|g| g.gen_dispatch_action token,
|
69
|
+
nested_code {|g| g.gen_dispatch_action token, code }.
|
67
70
|
should == (<<-CODE).strip
|
68
71
|
begin
|
69
72
|
(r = @scanner.scan(/\\w+/)) &&
|
@@ -72,10 +75,10 @@ end
|
|
72
75
|
CODE
|
73
76
|
end
|
74
77
|
end
|
75
|
-
|
78
|
+
|
76
79
|
context 'when top-level' do
|
77
80
|
it 'generates top level matching code with a dispatch action' do
|
78
|
-
top_level_code {|g| g.gen_dispatch_action token,
|
81
|
+
top_level_code {|g| g.gen_dispatch_action token, code }.
|
79
82
|
should == (<<-CODE).strip
|
80
83
|
(r = @scanner.scan(/\\w+/)) &&
|
81
84
|
Word.parsed([r])
|
@@ -83,12 +86,14 @@ Word.parsed([r])
|
|
83
86
|
end
|
84
87
|
end
|
85
88
|
end
|
86
|
-
|
89
|
+
|
87
90
|
describe '#gen_direct_action' do
|
88
|
-
|
91
|
+
|
92
|
+
let(:code) { ActionCode.new('|_| _.to_sym') }
|
93
|
+
|
89
94
|
context 'when nested' do
|
90
95
|
it 'generates nested matching code with a direct action' do
|
91
|
-
nested_code {|g| g.gen_direct_action token,
|
96
|
+
nested_code {|g| g.gen_direct_action token, code }.
|
92
97
|
should == (<<-CODE).strip
|
93
98
|
begin
|
94
99
|
(r = @scanner.scan(/\\w+/)) &&
|
@@ -97,10 +102,10 @@ end
|
|
97
102
|
CODE
|
98
103
|
end
|
99
104
|
end
|
100
|
-
|
105
|
+
|
101
106
|
context 'when top-level' do
|
102
107
|
it 'generates top level matching code with a direct action' do
|
103
|
-
top_level_code {|g| g.gen_direct_action token,
|
108
|
+
top_level_code {|g| g.gen_direct_action token, code }.
|
104
109
|
should == (<<-CODE).strip
|
105
110
|
(r = @scanner.scan(/\\w+/)) &&
|
106
111
|
(r.to_sym)
|
@@ -108,16 +113,16 @@ end
|
|
108
113
|
end
|
109
114
|
end
|
110
115
|
end
|
111
|
-
|
116
|
+
|
112
117
|
describe '#gen_token' do
|
113
|
-
|
118
|
+
|
114
119
|
context 'when nested' do
|
115
120
|
it 'generates nested token matching code' do
|
116
121
|
nested_code {|g| g.gen_token token }.
|
117
122
|
should == '@scanner.scan(/\w+/)'
|
118
123
|
end
|
119
124
|
end
|
120
|
-
|
125
|
+
|
121
126
|
context 'when top-level' do
|
122
127
|
it 'generates top level token matching code' do
|
123
128
|
top_level_code {|g| g.gen_token token }.
|
@@ -125,16 +130,16 @@ end
|
|
125
130
|
end
|
126
131
|
end
|
127
132
|
end
|
128
|
-
|
133
|
+
|
129
134
|
describe '#gen_skip' do
|
130
|
-
|
135
|
+
|
131
136
|
context 'when nested' do
|
132
137
|
it 'generates nested skipping code' do
|
133
138
|
nested_code {|g| g.gen_skip token }.
|
134
139
|
should == '(@scanner.skip(/\w+/) && true)'
|
135
140
|
end
|
136
141
|
end
|
137
|
-
|
142
|
+
|
138
143
|
context 'when top-level' do
|
139
144
|
it 'generates top level skipping code' do
|
140
145
|
top_level_code {|g| g.gen_skip token }.
|
@@ -142,33 +147,33 @@ end
|
|
142
147
|
end
|
143
148
|
end
|
144
149
|
end
|
145
|
-
|
150
|
+
|
146
151
|
describe '#gen_intermediate' do
|
147
152
|
it 'generates nested token matching code' do
|
148
153
|
nested_code {|g| g.gen_intermediate token }.
|
149
154
|
should == '@scanner.scan(/\w+/)'
|
150
155
|
end
|
151
156
|
end
|
152
|
-
|
157
|
+
|
153
158
|
describe '#gen_intermediate_assert' do
|
154
159
|
it 'generates intermediate positive lookahead code' do
|
155
160
|
nested_code {|g| g.gen_intermediate_assert token }.
|
156
161
|
should == '@scanner.skip(/(?=\w+)/)'
|
157
162
|
end
|
158
163
|
end
|
159
|
-
|
164
|
+
|
160
165
|
describe '#gen_intermediate_disallow' do
|
161
166
|
it 'generates intermediate negative lookahead code' do
|
162
167
|
nested_code {|g| g.gen_intermediate_disallow token }.
|
163
168
|
should == '@scanner.skip(/(?!\w+)/)'
|
164
169
|
end
|
165
170
|
end
|
166
|
-
|
171
|
+
|
167
172
|
describe '#gen_intermediate_skip' do
|
168
173
|
it 'generates intermediate skipping code' do
|
169
174
|
nested_code {|g| g.gen_intermediate_skip token }.
|
170
175
|
should == '@scanner.skip(/\w+/)'
|
171
176
|
end
|
172
177
|
end
|
173
|
-
|
178
|
+
|
174
179
|
end
|
@@ -1,19 +1,20 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
2
|
|
3
|
+
include Rattler::BackEnd::ParserGenerator
|
3
4
|
include Rattler::Parsers
|
4
5
|
|
5
|
-
describe
|
6
|
-
|
6
|
+
describe ZeroOrMoreGenerator do
|
7
|
+
|
7
8
|
include ParserGeneratorSpecHelper
|
8
|
-
|
9
|
+
|
9
10
|
let(:zero_or_more) { ZeroOrMore[Match[/w+/]] }
|
10
|
-
|
11
|
+
|
11
12
|
describe '#gen_basic' do
|
12
|
-
|
13
|
+
|
13
14
|
let :zero_or_more do
|
14
15
|
ZeroOrMore[Choice[Match[/[[:alpha:]]/], Match[/[[:digit:]]/]]]
|
15
16
|
end
|
16
|
-
|
17
|
+
|
17
18
|
context 'when nested' do
|
18
19
|
it 'generates nested zero-or-more matching code' do
|
19
20
|
nested_code {|g| g.gen_basic zero_or_more }.
|
@@ -31,7 +32,7 @@ end
|
|
31
32
|
CODE
|
32
33
|
end
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
context 'when top-level' do
|
36
37
|
it 'generates top level zero-or-more matching code' do
|
37
38
|
top_level_code {|g| g.gen_basic zero_or_more }.
|
@@ -48,42 +49,44 @@ a
|
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
51
|
-
|
52
|
+
|
52
53
|
describe '#gen_assert' do
|
53
|
-
|
54
|
+
|
54
55
|
context 'when nested' do
|
55
56
|
it 'generates "true"' do
|
56
57
|
nested_code {|g| g.gen_assert zero_or_more }.should == 'true'
|
57
58
|
end
|
58
59
|
end
|
59
|
-
|
60
|
+
|
60
61
|
context 'when top-level' do
|
61
62
|
it 'generates "true"' do
|
62
63
|
top_level_code {|g| g.gen_assert zero_or_more }.should == 'true'
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
describe '#gen_disallow' do
|
68
|
-
|
69
|
+
|
69
70
|
context 'when nested' do
|
70
71
|
it 'generates "false"' do
|
71
72
|
nested_code {|g| g.gen_disallow zero_or_more }.should == 'false'
|
72
73
|
end
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
context 'when top-level' do
|
76
77
|
it 'generates "false"' do
|
77
78
|
top_level_code {|g| g.gen_disallow zero_or_more }.should == 'false'
|
78
79
|
end
|
79
80
|
end
|
80
81
|
end
|
81
|
-
|
82
|
+
|
82
83
|
describe '#gen_dispatch_action' do
|
83
|
-
|
84
|
+
|
85
|
+
let(:code) { NodeCode.new('Word', 'parsed') }
|
86
|
+
|
84
87
|
context 'when nested' do
|
85
88
|
it 'generates nested zero-or-more matching code with a dispatch action' do
|
86
|
-
nested_code {|g| g.gen_dispatch_action zero_or_more,
|
89
|
+
nested_code {|g| g.gen_dispatch_action zero_or_more, code }.
|
87
90
|
should == (<<-CODE).strip
|
88
91
|
begin
|
89
92
|
a = []
|
@@ -95,10 +98,10 @@ end
|
|
95
98
|
CODE
|
96
99
|
end
|
97
100
|
end
|
98
|
-
|
101
|
+
|
99
102
|
context 'when top-level' do
|
100
103
|
it 'generates top level zero-or-more matching code with a dispatch action' do
|
101
|
-
top_level_code {|g| g.gen_dispatch_action zero_or_more,
|
104
|
+
top_level_code {|g| g.gen_dispatch_action zero_or_more, code }.
|
102
105
|
should == (<<-CODE).strip
|
103
106
|
a = []
|
104
107
|
while r = @scanner.scan(/w+/)
|
@@ -109,12 +112,14 @@ Word.parsed(select_captures(a))
|
|
109
112
|
end
|
110
113
|
end
|
111
114
|
end
|
112
|
-
|
115
|
+
|
113
116
|
describe '#gen_direct_action' do
|
114
|
-
|
117
|
+
|
118
|
+
let(:code) { ActionCode.new('|_| _.size') }
|
119
|
+
|
115
120
|
context 'when nested' do
|
116
121
|
it 'generates nested zero-or-more matching code with a direct action' do
|
117
|
-
nested_code {|g| g.gen_direct_action zero_or_more,
|
122
|
+
nested_code {|g| g.gen_direct_action zero_or_more, code }.
|
118
123
|
should == (<<-CODE).strip
|
119
124
|
begin
|
120
125
|
a = []
|
@@ -126,10 +131,10 @@ end
|
|
126
131
|
CODE
|
127
132
|
end
|
128
133
|
end
|
129
|
-
|
134
|
+
|
130
135
|
context 'when top-level' do
|
131
136
|
it 'generates top level zero-or-more matching code with a direct action' do
|
132
|
-
top_level_code {|g| g.gen_direct_action zero_or_more,
|
137
|
+
top_level_code {|g| g.gen_direct_action zero_or_more, code }.
|
133
138
|
should == (<<-CODE).strip
|
134
139
|
a = []
|
135
140
|
while r = @scanner.scan(/w+/)
|
@@ -140,9 +145,9 @@ end
|
|
140
145
|
end
|
141
146
|
end
|
142
147
|
end
|
143
|
-
|
148
|
+
|
144
149
|
describe '#gen_token' do
|
145
|
-
|
150
|
+
|
146
151
|
context 'when nested' do
|
147
152
|
it 'generates nested zero-or-more matching code' do
|
148
153
|
nested_code {|g| g.gen_token zero_or_more }.
|
@@ -157,7 +162,7 @@ end
|
|
157
162
|
CODE
|
158
163
|
end
|
159
164
|
end
|
160
|
-
|
165
|
+
|
161
166
|
context 'when top-level' do
|
162
167
|
it 'generates top level zero-or-more matching code' do
|
163
168
|
top_level_code {|g| g.gen_token zero_or_more }.
|
@@ -171,9 +176,9 @@ a.join
|
|
171
176
|
end
|
172
177
|
end
|
173
178
|
end
|
174
|
-
|
179
|
+
|
175
180
|
describe '#gen_skip' do
|
176
|
-
|
181
|
+
|
177
182
|
context 'when nested' do
|
178
183
|
it 'generates nested zero-or-more skipping code' do
|
179
184
|
nested_code {|g| g.gen_skip zero_or_more }.
|
@@ -185,7 +190,7 @@ end
|
|
185
190
|
CODE
|
186
191
|
end
|
187
192
|
end
|
188
|
-
|
193
|
+
|
189
194
|
context 'when top-level' do
|
190
195
|
it 'generates top level zero-or-more skipping code' do
|
191
196
|
top_level_code {|g| g.gen_skip zero_or_more }.
|
@@ -196,7 +201,7 @@ true
|
|
196
201
|
end
|
197
202
|
end
|
198
203
|
end
|
199
|
-
|
204
|
+
|
200
205
|
describe '#gen_intermediate' do
|
201
206
|
it 'generates nested zero-or-more matching code' do
|
202
207
|
nested_code {|g| g.gen_intermediate zero_or_more }.
|
@@ -211,19 +216,19 @@ end
|
|
211
216
|
CODE
|
212
217
|
end
|
213
218
|
end
|
214
|
-
|
219
|
+
|
215
220
|
describe '#gen_intermediate_assert' do
|
216
221
|
it 'generates "true"' do
|
217
222
|
nested_code {|g| g.gen_intermediate_assert zero_or_more }.should == 'true'
|
218
223
|
end
|
219
224
|
end
|
220
|
-
|
225
|
+
|
221
226
|
describe '#gen_intermediate_disallow' do
|
222
227
|
it 'generates "false"' do
|
223
228
|
nested_code {|g| g.gen_intermediate_disallow zero_or_more }.should == 'false'
|
224
229
|
end
|
225
230
|
end
|
226
|
-
|
231
|
+
|
227
232
|
describe '#gen_intermediate_skip' do
|
228
233
|
it 'generates nested zero-or-more skipping code' do
|
229
234
|
nested_code {|g| g.gen_intermediate_skip zero_or_more }.
|
@@ -235,5 +240,5 @@ end
|
|
235
240
|
CODE
|
236
241
|
end
|
237
242
|
end
|
238
|
-
|
243
|
+
|
239
244
|
end
|
@@ -0,0 +1,885 @@
|
|
1
|
+
shared_examples_for 'a compiled parser' do
|
2
|
+
include CompilerSpecHelper
|
3
|
+
|
4
|
+
subject do
|
5
|
+
described_class.compile_parser(compiled_parser_base, grammar)
|
6
|
+
end
|
7
|
+
|
8
|
+
let :reference_parser do
|
9
|
+
Rattler::Parsers::CombinatorParser.as_class(grammar.rules.first, grammar.rules)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:compiled_parser_base) { Rattler::Runtime::RecursiveDescentParser }
|
13
|
+
|
14
|
+
########## match ##########
|
15
|
+
context 'with a match rule' do
|
16
|
+
let(:grammar) { define_grammar do
|
17
|
+
rule(:digit) { match /\d/ }
|
18
|
+
end }
|
19
|
+
it { should parse('451').succeeding.twice.like reference_parser }
|
20
|
+
it { should parse(' 4').failing.like reference_parser }
|
21
|
+
it { should parse('foo').failing.like reference_parser }
|
22
|
+
end
|
23
|
+
|
24
|
+
########## choice ##########
|
25
|
+
context 'with a choice rule' do
|
26
|
+
let(:grammar) { define_grammar do
|
27
|
+
rule(:atom) { match(/[[:alpha:]]+/) | match(/[[:digit:]]+/) }
|
28
|
+
end }
|
29
|
+
it { should parse('abc123').succeeding.twice.like reference_parser }
|
30
|
+
it { should parse('==').failing.like reference_parser }
|
31
|
+
|
32
|
+
context 'with nested choices' do
|
33
|
+
let(:grammar) { define_grammar do
|
34
|
+
rule(:foo) do
|
35
|
+
( (match('a') | match('b')) \
|
36
|
+
| (match('c') | match('d')) )
|
37
|
+
end
|
38
|
+
end }
|
39
|
+
it { should parse('abcd').succeeding(4).times.like reference_parser }
|
40
|
+
it { should parse('123').failing.like reference_parser }
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with nested sequences' do
|
44
|
+
let(:grammar) { define_grammar do
|
45
|
+
rule(:foo) do
|
46
|
+
( match('a') & match('b') \
|
47
|
+
| match('c') & match('d') )
|
48
|
+
end
|
49
|
+
end }
|
50
|
+
it { should parse('abcd').succeeding.twice.like reference_parser }
|
51
|
+
it { should parse('123').failing.like reference_parser }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with nested optional parsers' do
|
55
|
+
let(:grammar) { define_grammar do
|
56
|
+
rule(:foo) do
|
57
|
+
optional('a') | optional('b')
|
58
|
+
end
|
59
|
+
end }
|
60
|
+
it { should parse('abcd').succeeding(3).times.like reference_parser }
|
61
|
+
it { should parse('123').succeeding.like reference_parser }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
########## sequence ##########
|
66
|
+
context 'with a sequence rule' do
|
67
|
+
let(:grammar) { define_grammar do
|
68
|
+
rule :assignment do
|
69
|
+
match(/[[:alpha:]]+/) & match('=') & match(/[[:digit:]]+/)
|
70
|
+
end
|
71
|
+
end }
|
72
|
+
it { should parse('val=42 ').succeeding.like reference_parser }
|
73
|
+
it { should parse('val=x ').failing.like reference_parser }
|
74
|
+
|
75
|
+
context 'with non-capturing parsers' do
|
76
|
+
let(:grammar) { define_grammar do
|
77
|
+
rule :foo do
|
78
|
+
match(/[[:alpha:]]+/) & skip(/\s+/) & match(/[[:digit:]]+/)
|
79
|
+
end
|
80
|
+
end }
|
81
|
+
it { should parse('foo 42').succeeding.like reference_parser }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with only one capturing parser' do
|
85
|
+
let(:grammar) { define_grammar do
|
86
|
+
rule :foo do
|
87
|
+
skip(/\s+/) & match(/\w+/)
|
88
|
+
end
|
89
|
+
end }
|
90
|
+
it { should parse(' abc123').succeeding.like reference_parser }
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with no capturing parsers' do
|
94
|
+
let(:grammar) { define_grammar do
|
95
|
+
rule :foo do
|
96
|
+
skip(/\s*/) & skip(/#[^\n]+/)
|
97
|
+
end
|
98
|
+
end }
|
99
|
+
it { should parse(' # foo').succeeding.like reference_parser }
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'with an apply referencing a non-capturing rule' do
|
103
|
+
let(:grammar) { define_grammar do
|
104
|
+
rule :foo do
|
105
|
+
match(/[[:alpha:]]+/) & match(:ws) & match(/[[:digit:]]+/)
|
106
|
+
end
|
107
|
+
rule :ws do
|
108
|
+
skip(/\s+/)
|
109
|
+
end
|
110
|
+
end }
|
111
|
+
it { should parse('foo 42').succeeding.like reference_parser }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
########## optional ##########
|
116
|
+
context 'with an optional rule' do
|
117
|
+
let(:grammar) { define_grammar do
|
118
|
+
rule :foo do
|
119
|
+
optional(/\w+/)
|
120
|
+
end
|
121
|
+
end }
|
122
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
123
|
+
it { should parse(' ').succeeding.like reference_parser }
|
124
|
+
|
125
|
+
context 'with a non-capturing parser' do
|
126
|
+
let(:grammar) { define_grammar do
|
127
|
+
rule :foo do
|
128
|
+
optional(skip(/\w+/))
|
129
|
+
end
|
130
|
+
end }
|
131
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
132
|
+
it { should parse(' ').succeeding.like reference_parser }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
########## zero-or-more ##########
|
137
|
+
context 'with an zero-or-more rule' do
|
138
|
+
let(:grammar) { define_grammar do
|
139
|
+
rule :foo do
|
140
|
+
zero_or_more(/\w/)
|
141
|
+
end
|
142
|
+
end }
|
143
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
144
|
+
it { should parse(' ').succeeding.like reference_parser }
|
145
|
+
|
146
|
+
context 'with a non-capturing parser' do
|
147
|
+
let(:grammar) { define_grammar do
|
148
|
+
rule :foo do
|
149
|
+
zero_or_more(skip(/\w/))
|
150
|
+
end
|
151
|
+
end }
|
152
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
153
|
+
it { should parse(' ').succeeding.like reference_parser }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
########## one-or-more ##########
|
158
|
+
context 'with an one-or-more rule' do
|
159
|
+
let(:grammar) { define_grammar do
|
160
|
+
rule :foo do
|
161
|
+
one_or_more(/\w/)
|
162
|
+
end
|
163
|
+
end }
|
164
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
165
|
+
it { should parse(' ').failing.like reference_parser }
|
166
|
+
|
167
|
+
context 'with a non-capturing parser' do
|
168
|
+
let(:grammar) { define_grammar do
|
169
|
+
rule :foo do
|
170
|
+
one_or_more(skip(/\w/))
|
171
|
+
end
|
172
|
+
end }
|
173
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
174
|
+
it { should parse(' ').failing.like reference_parser }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
########## list ##########
|
179
|
+
context 'given a list rule' do
|
180
|
+
let(:grammar) { define_grammar do
|
181
|
+
rule :foo do
|
182
|
+
list(/\w+/, /[,;]/)
|
183
|
+
end
|
184
|
+
end }
|
185
|
+
it { should parse(' ').succeeding.like reference_parser }
|
186
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
187
|
+
it { should parse('foo,bar;baz ').succeeding.like reference_parser }
|
188
|
+
it { should parse('foo,bar, ').succeeding.like reference_parser }
|
189
|
+
|
190
|
+
context 'with a non-capturing parser' do
|
191
|
+
let(:grammar) { define_grammar do
|
192
|
+
rule :foo do
|
193
|
+
list(skip(/\w+/), /[,;]/)
|
194
|
+
end
|
195
|
+
end }
|
196
|
+
it { should parse(' ').succeeding.like reference_parser }
|
197
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
198
|
+
it { should parse('foo,bar;baz ').succeeding.like reference_parser }
|
199
|
+
it { should parse('foo,bar, ').succeeding.like reference_parser }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
########## list1 ##########
|
204
|
+
context 'given a list1 rule' do
|
205
|
+
let(:grammar) { define_grammar do
|
206
|
+
rule :foo do
|
207
|
+
list1(/\w+/, /[,;]/)
|
208
|
+
end
|
209
|
+
end }
|
210
|
+
it { should parse(' ').failing.like reference_parser }
|
211
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
212
|
+
it { should parse('foo,bar;baz ').succeeding.like reference_parser }
|
213
|
+
it { should parse('foo,bar, ').succeeding.like reference_parser }
|
214
|
+
|
215
|
+
context 'with a non-capturing parser' do
|
216
|
+
let(:grammar) { define_grammar do
|
217
|
+
rule :foo do
|
218
|
+
list1(skip(/\w+/), /[,;]/)
|
219
|
+
end
|
220
|
+
end }
|
221
|
+
it { should parse(' ').failing.like reference_parser }
|
222
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
223
|
+
it { should parse('foo,bar;baz ').succeeding.like reference_parser }
|
224
|
+
it { should parse('foo,bar, ').succeeding.like reference_parser }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
########## apply ##########
|
229
|
+
context 'given an apply rule' do
|
230
|
+
let(:grammar) { define_grammar do
|
231
|
+
rule(:digit) { match /\d/ }
|
232
|
+
rule(:foo) { match :digit }
|
233
|
+
end }
|
234
|
+
it { should parse('451 ').twice.succeeding.like reference_parser }
|
235
|
+
it { should parse('hi').failing.like reference_parser }
|
236
|
+
end
|
237
|
+
|
238
|
+
########## assert ##########
|
239
|
+
context 'given an assert rule' do
|
240
|
+
|
241
|
+
context 'with a nested match rule' do
|
242
|
+
let(:grammar) { define_grammar do
|
243
|
+
rule(:word) { assert /\w+/ }
|
244
|
+
end }
|
245
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
246
|
+
it { should parse(' ').failing.like reference_parser }
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'with a nested choice rule' do
|
250
|
+
let(:grammar) { define_grammar do
|
251
|
+
rule(:word) { assert(match(/[[:alpha:]]+/) | match(/[[:digit:]]+/)) }
|
252
|
+
end }
|
253
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
254
|
+
it { should parse(' ').failing.like reference_parser }
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'with a nested sequence rule' do
|
258
|
+
let(:grammar) { define_grammar do
|
259
|
+
rule(:word) { assert(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/)) }
|
260
|
+
end }
|
261
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
262
|
+
it { should parse(' ').failing.like reference_parser }
|
263
|
+
end
|
264
|
+
|
265
|
+
context 'with a nested optional rule' do
|
266
|
+
let(:grammar) { define_grammar do
|
267
|
+
rule(:word) { assert(optional(/\w+/)) }
|
268
|
+
end }
|
269
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
270
|
+
it { should parse(' ').succeeding.like reference_parser }
|
271
|
+
end
|
272
|
+
|
273
|
+
context 'with a nested zero_or_more rule' do
|
274
|
+
let(:grammar) { define_grammar do
|
275
|
+
rule(:word) { assert(zero_or_more(/\w/)) }
|
276
|
+
end }
|
277
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
278
|
+
it { should parse(' ').succeeding.like reference_parser }
|
279
|
+
end
|
280
|
+
|
281
|
+
context 'with a nested one_or_more rule' do
|
282
|
+
let(:grammar) { define_grammar do
|
283
|
+
rule(:word) { assert(one_or_more(/\w/)) }
|
284
|
+
end }
|
285
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
286
|
+
it { should parse(' ').failing.like reference_parser }
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'with a nested list rule' do
|
290
|
+
let(:grammar) { define_grammar do
|
291
|
+
rule(:word) { assert(list(/\w+/, /,/)) }
|
292
|
+
end }
|
293
|
+
it { should parse('abc,123 ').succeeding.like reference_parser }
|
294
|
+
it { should parse(' ').succeeding.like reference_parser }
|
295
|
+
end
|
296
|
+
|
297
|
+
context 'with a nested list1 rule' do
|
298
|
+
let(:grammar) { define_grammar do
|
299
|
+
rule(:word) { assert(list1(/\w+/, /,/)) }
|
300
|
+
end }
|
301
|
+
it { should parse('abc,123 ').succeeding.like reference_parser }
|
302
|
+
it { should parse(' ').failing.like reference_parser }
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'with a nested apply rule' do
|
306
|
+
let(:grammar) { define_grammar do
|
307
|
+
rule(:foo) { match :word }
|
308
|
+
rule(:word) { assert /\w+/ }
|
309
|
+
end }
|
310
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
311
|
+
it { should parse(' ').failing.like reference_parser }
|
312
|
+
end
|
313
|
+
|
314
|
+
context 'with a nested dispatch-action rule' do
|
315
|
+
let(:grammar) { define_grammar do
|
316
|
+
rule(:word) { assert(dispatch_action(/\w+/)) }
|
317
|
+
end }
|
318
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
319
|
+
it { should parse(' ').failing.like reference_parser }
|
320
|
+
end
|
321
|
+
|
322
|
+
context 'with a nested token rule' do
|
323
|
+
let(:grammar) { define_grammar do
|
324
|
+
rule(:word) { assert(token(match(/\w+/))) }
|
325
|
+
end }
|
326
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
327
|
+
it { should parse(' ').failing.like reference_parser }
|
328
|
+
end
|
329
|
+
|
330
|
+
context 'with a nested skip rule' do
|
331
|
+
let(:grammar) { define_grammar do
|
332
|
+
rule(:word) { assert(skip(/\w+/)) }
|
333
|
+
end }
|
334
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
335
|
+
it { should parse(' ').failing.like reference_parser }
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
########## disallow ##########
|
340
|
+
context 'given a disallow rule' do
|
341
|
+
|
342
|
+
context 'with a nested match rule' do
|
343
|
+
let(:grammar) { define_grammar do
|
344
|
+
rule(:word) { disallow /\w+/ }
|
345
|
+
end }
|
346
|
+
it { should parse(' ').succeeding.like reference_parser }
|
347
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
348
|
+
end
|
349
|
+
|
350
|
+
context 'with a nested choice rule' do
|
351
|
+
let(:grammar) { define_grammar do
|
352
|
+
rule(:word) { disallow(match(/[[:alpha:]]/) | match(/[[:digit:]]/)) }
|
353
|
+
end }
|
354
|
+
it { should parse(' ').succeeding.like reference_parser }
|
355
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
356
|
+
end
|
357
|
+
|
358
|
+
context 'with a nested sequence rule' do
|
359
|
+
let(:grammar) { define_grammar do
|
360
|
+
rule(:word) { disallow(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/)) }
|
361
|
+
end }
|
362
|
+
it { should parse(' ').succeeding.like reference_parser }
|
363
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
364
|
+
end
|
365
|
+
|
366
|
+
context 'with a nested optional rule' do
|
367
|
+
let(:grammar) { define_grammar do
|
368
|
+
rule(:word) { disallow(optional(/\w+/)) }
|
369
|
+
end }
|
370
|
+
it { should parse(' ').failing.like reference_parser }
|
371
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
372
|
+
end
|
373
|
+
|
374
|
+
context 'with a nested zero_or_more rule' do
|
375
|
+
let(:grammar) { define_grammar do
|
376
|
+
rule(:word) { disallow(zero_or_more(/\w/)) }
|
377
|
+
end }
|
378
|
+
it { should parse(' ').failing.like reference_parser }
|
379
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
380
|
+
end
|
381
|
+
|
382
|
+
context 'with a nested one_or_more rule' do
|
383
|
+
let(:grammar) { define_grammar do
|
384
|
+
rule(:word) { disallow(one_or_more(/\w/)) }
|
385
|
+
end }
|
386
|
+
it { should parse(' ').succeeding.like reference_parser }
|
387
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
388
|
+
end
|
389
|
+
|
390
|
+
context 'with a nested list rule' do
|
391
|
+
let(:grammar) { define_grammar do
|
392
|
+
rule(:word) { disallow(list(/\w+/, /,/)) }
|
393
|
+
end }
|
394
|
+
it { should parse(' ').failing.like reference_parser }
|
395
|
+
it { should parse('abc,123 ').failing.like reference_parser }
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'with a nested list1 rule' do
|
399
|
+
let(:grammar) { define_grammar do
|
400
|
+
rule(:word) { disallow(list1(/\w+/, /,/)) }
|
401
|
+
end }
|
402
|
+
it { should parse(' ').succeeding.like reference_parser }
|
403
|
+
it { should parse('abc,123 ').failing.like reference_parser }
|
404
|
+
end
|
405
|
+
|
406
|
+
context 'with a nested apply rule' do
|
407
|
+
let(:grammar) { define_grammar do
|
408
|
+
rule(:foo) { match :word }
|
409
|
+
rule(:word) { disallow /\w+/ }
|
410
|
+
end }
|
411
|
+
it { should parse(' ').succeeding.like reference_parser }
|
412
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
413
|
+
end
|
414
|
+
|
415
|
+
context 'with a nested dispatch-action rule' do
|
416
|
+
let(:grammar) { define_grammar do
|
417
|
+
rule(:word) { disallow(dispatch_action(/\w+/)) }
|
418
|
+
end }
|
419
|
+
it { should parse(' ').succeeding.like reference_parser }
|
420
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
421
|
+
end
|
422
|
+
|
423
|
+
context 'with a nested token rule' do
|
424
|
+
let(:grammar) { define_grammar do
|
425
|
+
rule(:word) { disallow(token(match(/\w+/))) }
|
426
|
+
end }
|
427
|
+
it { should parse(' ').succeeding.like reference_parser }
|
428
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
429
|
+
end
|
430
|
+
|
431
|
+
context 'with a nested skip rule' do
|
432
|
+
let(:grammar) { define_grammar do
|
433
|
+
rule(:word) { disallow(skip(/\w+/)) }
|
434
|
+
end }
|
435
|
+
it { should parse(' ').succeeding.like reference_parser }
|
436
|
+
it { should parse('abc123 ').failing.like reference_parser }
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
########## dispatch_action ##########
|
441
|
+
context 'given a dispatch-action rule' do
|
442
|
+
|
443
|
+
context 'with a nested match rule' do
|
444
|
+
let(:grammar) { define_grammar do
|
445
|
+
rule(:digits) { dispatch_action(/\d+/) }
|
446
|
+
end }
|
447
|
+
it { should parse('451a').succeeding.like reference_parser }
|
448
|
+
it { should parse(' ').failing.like reference_parser }
|
449
|
+
end
|
450
|
+
|
451
|
+
context 'with a nested choice rule' do
|
452
|
+
let(:grammar) { define_grammar do
|
453
|
+
rule :atom do
|
454
|
+
dispatch_action(match(/[[:alpha:]]+/) | match(/[[:digit:]]+/))
|
455
|
+
end
|
456
|
+
end }
|
457
|
+
|
458
|
+
it { should parse('451a').succeeding.like reference_parser }
|
459
|
+
it { should parse(' ').failing.like reference_parser }
|
460
|
+
end
|
461
|
+
|
462
|
+
context 'with a nested sequence rule' do
|
463
|
+
let(:grammar) { define_grammar do
|
464
|
+
rule :assignment do
|
465
|
+
dispatch_action(
|
466
|
+
match(/[[:alpha:]]+/) &
|
467
|
+
match('=') &
|
468
|
+
match(/[[:digit:]]+/)
|
469
|
+
)
|
470
|
+
end
|
471
|
+
end }
|
472
|
+
|
473
|
+
it { should parse('val=42 ').succeeding.like reference_parser }
|
474
|
+
it { should parse('val=x').failing.like reference_parser }
|
475
|
+
|
476
|
+
context 'with labels' do
|
477
|
+
let(:grammar) { define_grammar do
|
478
|
+
rule :assignment do
|
479
|
+
dispatch_action(
|
480
|
+
label(:name, /[[:alpha:]]+/) &
|
481
|
+
match('=') &
|
482
|
+
label(:value, /[[:digit:]]+/)
|
483
|
+
)
|
484
|
+
end
|
485
|
+
end }
|
486
|
+
it { should parse('val=42 ').succeeding.like reference_parser }
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
context 'with a nested optional rule' do
|
491
|
+
let(:grammar) { define_grammar do
|
492
|
+
rule :foo do
|
493
|
+
dispatch_action(optional(/\w+/))
|
494
|
+
end
|
495
|
+
end }
|
496
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
497
|
+
it { should parse(' ').succeeding.like reference_parser }
|
498
|
+
end
|
499
|
+
|
500
|
+
context 'with a nested zero-or-more rule' do
|
501
|
+
let(:grammar) { define_grammar do
|
502
|
+
rule :foo do
|
503
|
+
dispatch_action(zero_or_more(/\w/))
|
504
|
+
end
|
505
|
+
end }
|
506
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
507
|
+
it { should parse(' ').succeeding.like reference_parser }
|
508
|
+
end
|
509
|
+
|
510
|
+
context 'with a nested one-or-more rule' do
|
511
|
+
let(:grammar) { define_grammar do
|
512
|
+
rule :foo do
|
513
|
+
dispatch_action(one_or_more(/\w/))
|
514
|
+
end
|
515
|
+
end }
|
516
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
517
|
+
it { should parse(' ').failing.like reference_parser }
|
518
|
+
end
|
519
|
+
|
520
|
+
context 'with a nested apply rule' do
|
521
|
+
let(:grammar) { define_grammar do
|
522
|
+
rule(:digit) { match /\d/ }
|
523
|
+
rule(:foo) { dispatch_action :digit }
|
524
|
+
end }
|
525
|
+
it { should parse('451a').succeeding.twice.like reference_parser }
|
526
|
+
it { should parse(' ').failing.like reference_parser }
|
527
|
+
end
|
528
|
+
|
529
|
+
context 'with a nested token rule' do
|
530
|
+
let(:grammar) { define_grammar do
|
531
|
+
rule :foo do
|
532
|
+
dispatch_action(token(match(/\w+/)))
|
533
|
+
end
|
534
|
+
end }
|
535
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
536
|
+
it { should parse(' ').failing.like reference_parser }
|
537
|
+
end
|
538
|
+
|
539
|
+
context 'with a nested skip rule' do
|
540
|
+
let(:grammar) { define_grammar do
|
541
|
+
rule :foo do
|
542
|
+
dispatch_action(skip(/\w+/))
|
543
|
+
end
|
544
|
+
end }
|
545
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
546
|
+
it { should parse(' ').failing.like reference_parser }
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
########## direct_action ##########
|
551
|
+
context 'given a direct-action rule' do
|
552
|
+
|
553
|
+
context 'with a nested match rule' do
|
554
|
+
let(:grammar) { define_grammar do
|
555
|
+
rule(:num) { direct_action(/\d+/, '|_| _.to_i') }
|
556
|
+
end }
|
557
|
+
it { should parse('451a').succeeding.like reference_parser }
|
558
|
+
it { should parse(' ').failing.like reference_parser }
|
559
|
+
end
|
560
|
+
|
561
|
+
context 'with a nested choice rule' do
|
562
|
+
let(:grammar) { define_grammar do
|
563
|
+
rule :foo do
|
564
|
+
direct_action(
|
565
|
+
match(/[[:alpha:]]+/) | match(/[[:digit:]]+/),
|
566
|
+
'|_| _.size'
|
567
|
+
)
|
568
|
+
end
|
569
|
+
end }
|
570
|
+
|
571
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
572
|
+
it { should parse('451a').succeeding.like reference_parser }
|
573
|
+
it { should parse(' ').failing.like reference_parser }
|
574
|
+
end
|
575
|
+
|
576
|
+
context 'with a nested sequence rule' do
|
577
|
+
let(:grammar) { define_grammar do
|
578
|
+
rule :assignment do
|
579
|
+
direct_action(
|
580
|
+
match(/[[:alpha:]]+/) & match('=') & match(/[[:digit:]]+/),
|
581
|
+
'|l,_,r| "#{r} -> #{l}"'
|
582
|
+
)
|
583
|
+
end
|
584
|
+
end }
|
585
|
+
|
586
|
+
it { should parse('val=42 ').succeeding.like reference_parser }
|
587
|
+
it { should parse('val=x').failing.like reference_parser }
|
588
|
+
|
589
|
+
context 'with labels' do
|
590
|
+
let(:grammar) { define_grammar do
|
591
|
+
rule :assignment do
|
592
|
+
direct_action(
|
593
|
+
label(:name, /[[:alpha:]]+/) & match('=') & label(:value, /[[:digit:]]+/),
|
594
|
+
'"#{value} -> #{name}"'
|
595
|
+
)
|
596
|
+
end
|
597
|
+
end }
|
598
|
+
it { should parse('val=42 ').succeeding.like reference_parser }
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
context 'with a nested optional rule' do
|
603
|
+
let(:grammar) { define_grammar do
|
604
|
+
rule :foo do
|
605
|
+
direct_action(optional(/\w+/), '|_| _.size')
|
606
|
+
end
|
607
|
+
end }
|
608
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
609
|
+
it { should parse(' ').succeeding.like reference_parser }
|
610
|
+
end
|
611
|
+
|
612
|
+
context 'with a nested zero-or-more rule' do
|
613
|
+
let(:grammar) { define_grammar do
|
614
|
+
rule :foo do
|
615
|
+
direct_action(zero_or_more(/\w/), '|_| _.size')
|
616
|
+
end
|
617
|
+
end }
|
618
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
619
|
+
it { should parse(' ').succeeding.like reference_parser }
|
620
|
+
end
|
621
|
+
|
622
|
+
context 'with a nested one-or-more rule' do
|
623
|
+
let(:grammar) { define_grammar do
|
624
|
+
rule :foo do
|
625
|
+
direct_action(one_or_more(/\w/), '|_| _.size')
|
626
|
+
end
|
627
|
+
end }
|
628
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
629
|
+
it { should parse(' ').failing.like reference_parser }
|
630
|
+
end
|
631
|
+
|
632
|
+
context 'with a nested apply rule' do
|
633
|
+
let(:grammar) { define_grammar do
|
634
|
+
rule(:digit) { match /\d/ }
|
635
|
+
rule(:foo) { direct_action :digit, '|_| _.to_i' }
|
636
|
+
end }
|
637
|
+
it { should parse('451a').succeeding.twice.like reference_parser }
|
638
|
+
it { should parse(' ').failing.like reference_parser }
|
639
|
+
end
|
640
|
+
|
641
|
+
context 'with a nested token rule' do
|
642
|
+
let(:grammar) { define_grammar do
|
643
|
+
rule :foo do
|
644
|
+
direct_action(token(/\w+/), '|_| _.size')
|
645
|
+
end
|
646
|
+
end }
|
647
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
648
|
+
it { should parse(' ').failing.like reference_parser }
|
649
|
+
end
|
650
|
+
|
651
|
+
context 'with a nested skip rule' do
|
652
|
+
let(:grammar) { define_grammar do
|
653
|
+
rule :foo do
|
654
|
+
direct_action(skip(/\w+/), '42' )
|
655
|
+
end
|
656
|
+
end }
|
657
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
658
|
+
it { should parse(' ').failing.like reference_parser }
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
########## token ##########
|
663
|
+
context 'given a token rule' do
|
664
|
+
|
665
|
+
context 'with a nested match rule' do
|
666
|
+
let(:grammar) { define_grammar do
|
667
|
+
rule(:digits) { token(match(/\d+/)) }
|
668
|
+
end }
|
669
|
+
it { should parse('451a').succeeding.like reference_parser }
|
670
|
+
it { should parse('hi').failing.like reference_parser }
|
671
|
+
end
|
672
|
+
|
673
|
+
context 'with a nested choice rule' do
|
674
|
+
let(:grammar) { define_grammar do
|
675
|
+
rule(:atom) do
|
676
|
+
token(match(/[[:alpha:]]+/) | match(/[[:digit:]]+/))
|
677
|
+
end
|
678
|
+
end }
|
679
|
+
|
680
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
681
|
+
it { should parse('==').failing.like reference_parser }
|
682
|
+
|
683
|
+
context 'with non-capturing choices' do
|
684
|
+
let(:grammar) { define_grammar do
|
685
|
+
rule(:atom) do
|
686
|
+
token(skip(/[[:alpha:]]+/) | match(/[[:digit:]]+/))
|
687
|
+
end
|
688
|
+
end }
|
689
|
+
it { should parse('abc123 ').succeeding.like reference_parser }
|
690
|
+
it { should parse('==').failing.like reference_parser }
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
context 'with a nested sequence rule' do
|
695
|
+
let(:grammar) { define_grammar do
|
696
|
+
rule(:atom) do
|
697
|
+
token(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/))
|
698
|
+
end
|
699
|
+
end }
|
700
|
+
|
701
|
+
it { should parse('foo42!').succeeding.like reference_parser }
|
702
|
+
it { should parse('val=x').failing.like reference_parser }
|
703
|
+
|
704
|
+
context 'with non-capturing parsers' do
|
705
|
+
let(:grammar) { define_grammar do
|
706
|
+
rule :foo do
|
707
|
+
token(match(/[[:alpha:]]+/) & skip(/\s+/) & match(/[[:digit:]]+/))
|
708
|
+
end
|
709
|
+
end }
|
710
|
+
it { should parse('foo 42').succeeding.like reference_parser }
|
711
|
+
it { should parse('foo bar').failing.like reference_parser }
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
context 'with a nested optional rule' do
|
716
|
+
let(:grammar) { define_grammar do
|
717
|
+
rule :foo do
|
718
|
+
token(optional(/\w+/))
|
719
|
+
end
|
720
|
+
end }
|
721
|
+
|
722
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
723
|
+
it { should parse(' ').succeeding.like reference_parser }
|
724
|
+
|
725
|
+
context 'with a non-capturing rule' do
|
726
|
+
let(:grammar) { define_grammar do
|
727
|
+
rule :foo do
|
728
|
+
token(optional(skip(/\w+/)))
|
729
|
+
end
|
730
|
+
end }
|
731
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
732
|
+
it { should parse(' ').succeeding.like reference_parser }
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
context 'with a nested zero-or-more rule' do
|
737
|
+
let(:grammar) { define_grammar do
|
738
|
+
rule :foo do
|
739
|
+
token(zero_or_more(/\w/))
|
740
|
+
end
|
741
|
+
end }
|
742
|
+
|
743
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
744
|
+
it { should parse(' ').succeeding.like reference_parser }
|
745
|
+
|
746
|
+
context 'with a non-capturing rule' do
|
747
|
+
let(:grammar) { define_grammar do
|
748
|
+
rule :foo do
|
749
|
+
token(zero_or_more(skip(/\w/)))
|
750
|
+
end
|
751
|
+
end }
|
752
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
753
|
+
it { should parse(' ').succeeding.like reference_parser }
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
context 'with a nested one-or-more rule' do
|
758
|
+
let(:grammar) { define_grammar do
|
759
|
+
rule :foo do
|
760
|
+
token(one_or_more(/\w/))
|
761
|
+
end
|
762
|
+
end }
|
763
|
+
|
764
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
765
|
+
it { should parse(' ').failing.like reference_parser }
|
766
|
+
|
767
|
+
context 'with a non-capturing rule' do
|
768
|
+
let(:grammar) { define_grammar do
|
769
|
+
rule :foo do
|
770
|
+
token(one_or_more(skip(/\w/)))
|
771
|
+
end
|
772
|
+
end }
|
773
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
774
|
+
it { should parse(' ').failing.like reference_parser }
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
context 'with a nested apply rule' do
|
779
|
+
let(:grammar) { define_grammar do
|
780
|
+
rule(:foo) { token(match(:digits)) }
|
781
|
+
rule(:digits) { match(/\d+/) }
|
782
|
+
end }
|
783
|
+
|
784
|
+
it { should parse('451a').succeeding.like reference_parser }
|
785
|
+
it { should parse('hi').failing.like reference_parser }
|
786
|
+
|
787
|
+
context 'applying a non-capturing rule' do
|
788
|
+
let(:grammar) { define_grammar do
|
789
|
+
rule(:foo) { token(match(:digits)) }
|
790
|
+
rule(:digits) { skip(/\d+/) }
|
791
|
+
end }
|
792
|
+
it { should parse('451a').succeeding.like reference_parser }
|
793
|
+
it { should parse('hi').failing.like reference_parser }
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
context 'with a nested dispatch-action rule' do
|
798
|
+
let(:grammar) { define_grammar do
|
799
|
+
rule(:foo) { token(dispatch_action(/\w+/)) }
|
800
|
+
end }
|
801
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
802
|
+
it { should parse(' ').failing.like reference_parser }
|
803
|
+
end
|
804
|
+
|
805
|
+
context 'with a nested skip rule' do
|
806
|
+
let(:grammar) { define_grammar do
|
807
|
+
rule(:foo) { token(skip(/\w+/)) }
|
808
|
+
end }
|
809
|
+
it { should parse('abc123').succeeding.like reference_parser }
|
810
|
+
it { should parse(' ').failing.like reference_parser }
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
########## skip ##########
|
815
|
+
context 'given a skip rule' do
|
816
|
+
|
817
|
+
context 'with a nested match rule' do
|
818
|
+
let(:grammar) { define_grammar do
|
819
|
+
rule(:ws) { skip(/\s+/) }
|
820
|
+
end }
|
821
|
+
it { should parse(' foo').succeeding.like reference_parser }
|
822
|
+
it { should parse('hi').failing.like reference_parser }
|
823
|
+
end
|
824
|
+
|
825
|
+
context 'with a nested choice rule' do
|
826
|
+
let(:grammar) { define_grammar do
|
827
|
+
rule(:ws) do
|
828
|
+
skip(match(/\s+/) | match(/\#[^\n]*/))
|
829
|
+
end
|
830
|
+
end }
|
831
|
+
it { should parse(' # hi there ').succeeding.twice.like reference_parser }
|
832
|
+
it { should parse('hi').failing.like reference_parser }
|
833
|
+
end
|
834
|
+
|
835
|
+
context 'with a nested sequence rule' do
|
836
|
+
let(:grammar) { define_grammar do
|
837
|
+
rule :foo do
|
838
|
+
skip(match(/[[:alpha:]]+/) & match(/[[:digit:]]+/))
|
839
|
+
end
|
840
|
+
end }
|
841
|
+
it { should parse('foo42!').succeeding.like reference_parser }
|
842
|
+
it { should parse('val=x').failing.like reference_parser }
|
843
|
+
end
|
844
|
+
|
845
|
+
context 'with a nested optional rule' do
|
846
|
+
let(:grammar) { define_grammar do
|
847
|
+
rule :foo do
|
848
|
+
skip(optional(/\w+/))
|
849
|
+
end
|
850
|
+
end }
|
851
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
852
|
+
it { should parse(' ').succeeding.like reference_parser }
|
853
|
+
end
|
854
|
+
|
855
|
+
context 'with a nested zero-or-more rule' do
|
856
|
+
let(:grammar) { define_grammar do
|
857
|
+
rule :foo do
|
858
|
+
skip(zero_or_more(/\w/))
|
859
|
+
end
|
860
|
+
end }
|
861
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
862
|
+
it { should parse(' ').succeeding.like reference_parser }
|
863
|
+
end
|
864
|
+
|
865
|
+
context 'with a nested one-or-more rule' do
|
866
|
+
let(:grammar) { define_grammar do
|
867
|
+
rule :foo do
|
868
|
+
skip(one_or_more(/\w/))
|
869
|
+
end
|
870
|
+
end }
|
871
|
+
it { should parse('foo ').succeeding.like reference_parser }
|
872
|
+
it { should parse(' ').failing.like reference_parser }
|
873
|
+
end
|
874
|
+
|
875
|
+
context 'with a nested apply rule' do
|
876
|
+
let(:grammar) { define_grammar do
|
877
|
+
rule(:digits) { match(/\d+/) }
|
878
|
+
rule(:foo) { skip(:digits) }
|
879
|
+
end }
|
880
|
+
it { should parse('451a').succeeding.like reference_parser }
|
881
|
+
it { should parse('hi').failing.like reference_parser }
|
882
|
+
end
|
883
|
+
end
|
884
|
+
|
885
|
+
end
|