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
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
include Rattler::Parsers
|
4
|
+
|
5
|
+
describe Rattler::Grammar::Analysis do
|
6
|
+
|
7
|
+
subject { described_class.new(grammar.rules) }
|
8
|
+
|
9
|
+
let(:grammar) { Rattler::Grammar::Grammar[rule_set] }
|
10
|
+
|
11
|
+
describe '#recursive?' do
|
12
|
+
|
13
|
+
context 'given a directly recursive rule' do
|
14
|
+
|
15
|
+
let(:rule_set) { RuleSet[
|
16
|
+
Rule[:a, Sequence[Match[/a/], Apply[:a]]]
|
17
|
+
] }
|
18
|
+
|
19
|
+
it 'returns true' do
|
20
|
+
subject.recursive?(:a).should be_true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'given an indirectly recursive rule' do
|
25
|
+
|
26
|
+
let(:rule_set) { RuleSet[
|
27
|
+
Rule[:a, Sequence[Match[/a/], Apply[:b]]],
|
28
|
+
Rule[:b, Sequence[Match[/b/], Apply[:c]]],
|
29
|
+
Rule[:c, Sequence[Match[/c/], Apply[:a]]]
|
30
|
+
] }
|
31
|
+
|
32
|
+
it 'returns true' do
|
33
|
+
subject.recursive?(:a).should be_true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'given a non-recursive rule' do
|
38
|
+
|
39
|
+
let(:rule_set) { RuleSet[
|
40
|
+
Rule[:a, Sequence[Match[/a/], Apply[:b]]],
|
41
|
+
Rule[:b, Sequence[Match[/b/], Apply[:c]]],
|
42
|
+
Rule[:c, Sequence[Match[/c/], Apply[:b]]]
|
43
|
+
] }
|
44
|
+
|
45
|
+
it 'returns false' do
|
46
|
+
subject.recursive?(:a).should be_false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#left_recursive?' do
|
52
|
+
|
53
|
+
context 'given a directly left-recursive rule' do
|
54
|
+
|
55
|
+
let(:rule_set) { RuleSet[
|
56
|
+
Rule[:a, Apply[:a]]
|
57
|
+
] }
|
58
|
+
|
59
|
+
it 'returns true' do
|
60
|
+
subject.left_recursive?(:a).should be_true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'given an indirectly left-recursive rule' do
|
65
|
+
|
66
|
+
let(:rule_set) { RuleSet[
|
67
|
+
Rule[:a, Apply[:b]],
|
68
|
+
Rule[:b, Apply[:c]],
|
69
|
+
Rule[:c, Apply[:a]]
|
70
|
+
] }
|
71
|
+
|
72
|
+
it 'returns true' do
|
73
|
+
subject.left_recursive?(:a).should be_true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'given a non-recursive rule' do
|
78
|
+
|
79
|
+
let(:rule_set) { RuleSet[
|
80
|
+
Rule[:a, Apply[:b]],
|
81
|
+
Rule[:b, Apply[:c]],
|
82
|
+
Rule[:c, Apply[:b]]
|
83
|
+
] }
|
84
|
+
|
85
|
+
it 'returns false' do
|
86
|
+
subject.left_recursive?(:a).should be_false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'given a recursive but not left-recursive rule' do
|
91
|
+
|
92
|
+
let(:rule_set) { RuleSet[
|
93
|
+
Rule[:a, Sequence[Match[/a/], Apply[:a]]]
|
94
|
+
] }
|
95
|
+
|
96
|
+
it 'returns false' do
|
97
|
+
subject.left_recursive?(:a).should be_false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#referenced_from?' do
|
103
|
+
|
104
|
+
context 'with non-recursive rules' do
|
105
|
+
|
106
|
+
let(:rule_set) { RuleSet[
|
107
|
+
Rule[:a, Apply[:b]],
|
108
|
+
Rule[:b, Apply[:c]],
|
109
|
+
Rule[:c, Match[/a/]],
|
110
|
+
Rule[:d, Match[/b/]]
|
111
|
+
] }
|
112
|
+
|
113
|
+
context 'given a rule name and itself' do
|
114
|
+
it 'returns false' do
|
115
|
+
subject.referenced_from?(:a, :a).should be_false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'given a directly referenced rule name' do
|
120
|
+
it 'returns true' do
|
121
|
+
subject.referenced_from?(:a, :b).should be_true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'given an indirectly referenced rule name' do
|
126
|
+
it 'returns true' do
|
127
|
+
subject.referenced_from?(:a, :c).should be_true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'given a non-referenced rule name' do
|
132
|
+
it 'returns false' do
|
133
|
+
subject.referenced_from?(:a, :d).should be_false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'with recursive rules' do
|
139
|
+
|
140
|
+
let(:rule_set) { RuleSet[
|
141
|
+
Rule[:a, Apply[:a]],
|
142
|
+
Rule[:b, Apply[:c]],
|
143
|
+
Rule[:c, Apply[:b]]
|
144
|
+
] }
|
145
|
+
|
146
|
+
context 'given a directly recursive rule name and itself' do
|
147
|
+
it 'returns true' do
|
148
|
+
subject.referenced_from?(:a, :a).should be_true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'given an indirectly recursive rule name and itself' do
|
153
|
+
it 'returns true' do
|
154
|
+
subject.referenced_from?(:b, :b).should be_true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'given a referenced rule name' do
|
159
|
+
it 'returns true' do
|
160
|
+
subject.referenced_from?(:b, :c).should be_true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
@@ -6,282 +6,272 @@ describe Rattler::Grammar::GrammarParser do
|
|
6
6
|
include Rattler::Util::ParserSpecHelper
|
7
7
|
|
8
8
|
let :posix_names do
|
9
|
-
%w{alnum alpha blank cntrl digit graph lower print punct space upper xdigit
|
9
|
+
%w{alnum alpha blank cntrl digit graph lower print punct space upper xdigit}
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
parsing(" \n\t foo").as(:identifier).should result_in('foo').at(8)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'skips comments' do
|
17
|
-
parsing("\n# a comment\n\t foo").as(:identifier).
|
18
|
-
should result_in('foo').at(18)
|
19
|
-
end
|
20
|
-
|
21
|
-
describe '#match(:identifier)' do
|
22
|
-
it 'recognizes identifiers' do
|
23
|
-
parsing(' fooBar ').as(:identifier).should result_in('fooBar').at(7)
|
24
|
-
parsing(' FooBar ').as(:identifier).should result_in('FooBar').at(7)
|
25
|
-
parsing(' EO ').as(:identifier).should result_in('EO').at(3)
|
26
|
-
parsing(' EOFA ').as(:identifier).should result_in('EOFA').at(5)
|
27
|
-
end
|
12
|
+
describe '#match(:expression)' do
|
28
13
|
|
29
|
-
|
30
|
-
parsing('EOF').as(:identifier).should fail
|
31
|
-
end
|
32
|
-
end
|
14
|
+
context 'given a string literal' do
|
33
15
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
16
|
+
context 'delimited by double quotes' do
|
17
|
+
it 'parses as a regex match' do
|
18
|
+
matching(%{ "a string" }).as(:expression).
|
19
|
+
should result_in(Match[/a\ string/]).at(11)
|
20
|
+
end
|
21
|
+
end
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
23
|
+
context 'delimited by single quotes' do
|
24
|
+
it 'parses as a regex match' do
|
25
|
+
matching(%{ 'a string' }).as(:expression).
|
26
|
+
should result_in(Match[/a\ string/]).at(11)
|
27
|
+
end
|
28
|
+
end
|
44
29
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
30
|
+
context 'using "general delimited text" syntax' do
|
31
|
+
|
32
|
+
context 'with "(" and ")"' do
|
33
|
+
it 'parses as a regex match' do
|
34
|
+
matching(' %(a string) ').as(:expression).
|
35
|
+
should result_in(Match[/a\ string/]).at(12)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with "{" and "}"' do
|
40
|
+
it 'parses as a regex match' do
|
41
|
+
matching(' %{a string} ').as(:expression).
|
42
|
+
should result_in(Match[/a\ string/]).at(12)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with "[" and "]"' do
|
47
|
+
it 'parses as a regex match' do
|
48
|
+
matching(' %[a string] ').as(:expression).
|
49
|
+
should result_in(Match[/a\ string/]).at(12)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with "<" and ">"' do
|
54
|
+
it 'parses as a regex match' do
|
55
|
+
matching(' %<a string> ').as(:expression).
|
56
|
+
should result_in(Match[/a\ string/]).at(12)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with arbitrary delimiters' do
|
61
|
+
it 'parses as a regex match' do
|
62
|
+
matching(' %!a string! ').as(:expression).
|
63
|
+
should result_in(Match[/a\ string/]).at(12)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
51
67
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
68
|
+
context 'delimited by backquotes' do
|
69
|
+
it 'parses as a word literal' do
|
70
|
+
matching(%{ `where` }).as(:expression).should result_in(
|
71
|
+
Token[Sequence[Match[/where/], Disallow[Match[/[[:alnum:]_]/]]]]
|
72
|
+
).at(8)
|
73
|
+
end
|
74
|
+
end
|
59
75
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
context 'with special characters' do
|
77
|
+
it 'escapes the special characters' do
|
78
|
+
matching(%{ "[...]" }).as(:expression).
|
79
|
+
should result_in(Match[/\[\.\.\.\]/]).at(8)
|
80
|
+
end
|
81
|
+
end
|
64
82
|
end
|
65
|
-
end
|
66
83
|
|
67
|
-
|
68
|
-
it 'recognizes string literals' do
|
69
|
-
parsing(%{ "a string" }).as(:literal).should result_in(%{"a string"}).at(11)
|
70
|
-
parsing(%{ 'a string' }).as(:literal).should result_in(%{'a string'}).at(11)
|
71
|
-
parsing(%q{ "a \"string\"" }).as(:literal).
|
72
|
-
should result_in(%q{"a \"string\""}).at(15)
|
73
|
-
end
|
74
|
-
end
|
84
|
+
context 'given a character class expression' do
|
75
85
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
86
|
+
context 'with a simple list of characters' do
|
87
|
+
it 'parses as a regex match' do
|
88
|
+
matching(' [abc123] ').as(:expression).
|
89
|
+
should result_in(Match[/[abc123]/]).at(9)
|
90
|
+
end
|
91
|
+
end
|
81
92
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
93
|
+
context 'with octal codes' do
|
94
|
+
it 'parses as a regex match' do
|
95
|
+
matching(' [\010\012\015] ').as(:expression).
|
96
|
+
should result_in(Match[/[\010\012\015]/]).at(15)
|
97
|
+
end
|
98
|
+
end
|
86
99
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
100
|
+
context 'with hex codes' do
|
101
|
+
it 'parses as a regex match' do
|
102
|
+
matching(' [\x08\x0A\x0D] ').as(:expression).
|
103
|
+
should result_in(Match[/[\x08\x0A\x0D]/]).at(15)
|
104
|
+
end
|
105
|
+
end
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
107
|
+
context 'with a range of characters' do
|
108
|
+
it 'parses as a regex match' do
|
109
|
+
matching(' [A-F] ').as(:expression).
|
110
|
+
should result_in(Match[/[A-F]/]).at(6)
|
111
|
+
end
|
112
|
+
end
|
96
113
|
|
97
|
-
|
98
|
-
|
99
|
-
|
114
|
+
context 'with a range of octal codes' do
|
115
|
+
it 'parses as a regex match' do
|
116
|
+
matching(' [\000-\177] ').as(:expression).
|
117
|
+
should result_in(Match[/[\000-\177]/]).at(12)
|
118
|
+
end
|
119
|
+
end
|
100
120
|
|
101
|
-
|
102
|
-
|
103
|
-
|
121
|
+
context 'with a range of hex codes' do
|
122
|
+
it 'parses as a regex match' do
|
123
|
+
matching(' [\x00-\x7F] ').as(:expression).
|
124
|
+
should result_in(Match[/[\x00-\x7F]/]).at(12)
|
125
|
+
end
|
126
|
+
end
|
104
127
|
end
|
105
|
-
end
|
106
128
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
parsing(name).as(:posix_name).should result_in(name).at(name.length)
|
111
|
-
parsing(name + 'a').as(:posix_name).should fail
|
112
|
-
parsing(name + '0').as(:posix_name).should fail
|
113
|
-
parsing(name + '_').as(:posix_name).should fail
|
129
|
+
context 'given a "."' do
|
130
|
+
it 'parses as Match[/./]' do
|
131
|
+
matching(' . ').as(:expression).should result_in(Match[/./]).at(2)
|
114
132
|
end
|
115
133
|
end
|
116
|
-
end
|
117
134
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
parsing("[:#{name}:] ").as(:range).should result_in("[:#{name}:]").
|
122
|
-
at(name.length + 4)
|
135
|
+
context 'given a lower-case word' do
|
136
|
+
it 'parses as an Apply' do
|
137
|
+
matching(' fooBar ').as(:expression).should result_in(Apply[:fooBar]).at(7)
|
123
138
|
end
|
124
|
-
parsing('[:foo:]').as(:range).should result_in('[').at(1)
|
125
139
|
end
|
126
140
|
|
127
|
-
|
128
|
-
|
141
|
+
context 'given a upper-case word' do
|
142
|
+
it 'parses it as an Apply' do
|
143
|
+
matching(' FooBar ').as(:expression).should result_in(Apply[:FooBar]).at(7)
|
144
|
+
end
|
129
145
|
end
|
130
146
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
describe '#match(:class)' do
|
137
|
-
it 'recognizes character classes' do
|
138
|
-
parsing(' [A-Za-z_] ').as(:class).should result_in('[A-Za-z_]').at(10)
|
147
|
+
context 'given the EOF keyword' do
|
148
|
+
it 'parses as Eof[]' do
|
149
|
+
matching(' EOF ').as(:expression).should result_in(Eof[]).at(4)
|
150
|
+
end
|
139
151
|
end
|
140
|
-
end
|
141
152
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
153
|
+
context 'given an upper-case POSIX class name' do
|
154
|
+
it 'parses as a Match of a POSIX character class' do
|
155
|
+
for name in posix_names
|
156
|
+
matching(" #{name.upcase} ").as(:expression).
|
157
|
+
should result_in(Match[Regexp.new("[[:#{name}:]]")]).
|
158
|
+
at(name.length + 1)
|
159
|
+
end
|
160
|
+
end
|
146
161
|
end
|
147
|
-
end
|
148
162
|
|
149
|
-
|
150
|
-
|
151
|
-
|
163
|
+
context 'given the non-POSIX WORD class name' do
|
164
|
+
it 'parses as syntactic sugar for [[:alnum:]_]' do
|
165
|
+
matching(' WORD ').as(:expression).
|
166
|
+
should result_in(Match[/[[:alnum:]_]/]).at(5)
|
167
|
+
end
|
152
168
|
end
|
153
169
|
|
154
|
-
it '
|
155
|
-
|
170
|
+
it 'parses dispatch-action-attributed expressions' do
|
171
|
+
matching(' expr <Expr> ').as(:expression).
|
172
|
+
should result_in(DispatchAction[Apply[:expr], {:target => 'Expr', :method => 'parsed'}]).
|
173
|
+
at(12)
|
156
174
|
end
|
157
175
|
|
158
|
-
it '
|
159
|
-
|
160
|
-
|
161
|
-
should result_in(Match[Regexp.compile("[[:#{name.downcase}:]]")]).
|
162
|
-
at(name.length + 1)
|
163
|
-
end
|
176
|
+
it 'parses direct-action-attributed expressions' do
|
177
|
+
matching(' digits {|_| _.to_i} ').as(:expression).
|
178
|
+
should result_in(DirectAction[Apply[:digits], '|_| _.to_i']).at(20)
|
164
179
|
end
|
165
180
|
|
166
|
-
it '
|
167
|
-
|
181
|
+
it 'parses sequence expressions' do
|
182
|
+
matching(' name "=" value ').as(:expression).
|
183
|
+
should result_in(Sequence[Apply[:name], Match[%r{=}], Apply[:value]]).at(15)
|
168
184
|
end
|
169
185
|
|
170
|
-
it '
|
171
|
-
|
186
|
+
it 'parses attributed sequence expressions' do
|
187
|
+
matching(' name "=" value <Assign>').as(:expression).
|
188
|
+
should result_in(DispatchAction[
|
189
|
+
Sequence[Apply[:name], Match[%r{=}], Apply[:value]],
|
190
|
+
{:target => 'Assign', :method => 'parsed'}]).
|
191
|
+
at(24)
|
172
192
|
end
|
173
193
|
|
174
|
-
it '
|
175
|
-
|
194
|
+
it 'parses ordered choice expressions' do
|
195
|
+
matching(' string / number ').as(:expression).
|
196
|
+
should result_in(Choice[Apply[:string], Apply[:number]]).at(16)
|
176
197
|
end
|
177
198
|
|
178
|
-
it '
|
179
|
-
|
180
|
-
should result_in(Token[Sequence[Match[/then/], Disallow[Match[/[[:alnum:]_]/]]]]).at(7)
|
199
|
+
it 'skips normal whitespace' do
|
200
|
+
matching(" \n\t foo").as(:expression).should result_in(Apply[:foo]).at(8)
|
181
201
|
end
|
182
202
|
|
183
|
-
it '
|
184
|
-
|
203
|
+
it 'skips comments' do
|
204
|
+
matching("\n# a comment\n\t foo").as(:expression).
|
205
|
+
should result_in(Apply[:foo]).at(18)
|
185
206
|
end
|
186
207
|
end
|
187
208
|
|
188
209
|
describe '#match(:attributed)' do
|
189
210
|
it 'recognizes dispatch-action-attributed expressions' do
|
190
|
-
|
211
|
+
matching(' expr <Expr> ').as(:attributed).
|
191
212
|
should result_in(DispatchAction[Apply[:expr], {:target => 'Expr', :method => 'parsed'}]).
|
192
213
|
at(12)
|
193
214
|
end
|
194
215
|
|
195
216
|
it 'recognizes direct-action-attributed expressions' do
|
196
|
-
|
217
|
+
matching(' digits {|_| _.to_i} ').as(:attributed).
|
197
218
|
should result_in(DirectAction[Apply[:digits], '|_| _.to_i']).at(20)
|
198
219
|
end
|
199
220
|
end
|
200
221
|
|
201
222
|
describe '#match(:term)' do
|
202
223
|
it 'recognizes optional terms' do
|
203
|
-
|
224
|
+
matching(' expr? ').as(:term).should result_in(Optional[Apply[:expr]]).at(6)
|
204
225
|
end
|
205
226
|
|
206
227
|
it 'recognizes zero-or-more terms' do
|
207
|
-
|
228
|
+
matching(' expr* ').as(:term).should result_in(ZeroOrMore[Apply[:expr]]).at(6)
|
208
229
|
end
|
209
230
|
|
210
231
|
it 'recognizes one-or-more terms' do
|
211
|
-
|
232
|
+
matching(' expr+ ').as(:term).should result_in(OneOrMore[Apply[:expr]]).at(6)
|
212
233
|
end
|
213
234
|
|
214
235
|
it 'recognizes assert terms' do
|
215
|
-
|
236
|
+
matching(' &expr ').as(:term).should result_in(Assert[Apply[:expr]]).at(6)
|
216
237
|
end
|
217
238
|
|
218
239
|
it 'recognizes disallow terms' do
|
219
|
-
|
240
|
+
matching(' !expr ').as(:term).should result_in(Disallow[Apply[:expr]]).at(6)
|
220
241
|
end
|
221
242
|
|
222
243
|
it 'recognizes skip terms' do
|
223
|
-
|
244
|
+
matching(' ~expr ').as(:term).should result_in(Skip[Apply[:expr]]).at(6)
|
224
245
|
end
|
225
246
|
|
226
247
|
it 'recognizes token terms' do
|
227
|
-
|
248
|
+
matching(' @expr ').as(:term).should result_in(Token[Apply[:expr]]).at(6)
|
228
249
|
end
|
229
250
|
|
230
251
|
it 'recognizes labeled terms' do
|
231
|
-
|
252
|
+
matching(' val:expr ').as(:term).should result_in(Label[:val, Apply[:expr]]).at(9)
|
232
253
|
end
|
233
254
|
|
234
255
|
it 'recognizes fail expressions' do
|
235
|
-
|
256
|
+
matching(' fail("bad!") ').as(:term).
|
236
257
|
should result_in(Fail[:expr, 'bad!']).at(13)
|
237
|
-
|
258
|
+
matching(' fail "bad!" ').as(:term).
|
238
259
|
should result_in(Fail[:expr, 'bad!']).at(12)
|
239
260
|
end
|
240
261
|
|
241
262
|
it 'recognizes fail_rule expressions' do
|
242
|
-
|
263
|
+
matching(' fail_rule("bad!") ').as(:term).
|
243
264
|
should result_in(Fail[:rule, 'bad!']).at(18)
|
244
|
-
|
265
|
+
matching(' fail_rule "bad!" ').as(:term).
|
245
266
|
should result_in(Fail[:rule, 'bad!']).at(17)
|
246
267
|
end
|
247
268
|
|
248
269
|
it 'recognizes fail_parse expressions' do
|
249
|
-
|
270
|
+
matching(' fail_parse("bad!") ').as(:term).
|
250
271
|
should result_in(Fail[:parse, 'bad!']).at(19)
|
251
|
-
|
272
|
+
matching(' fail_parse "bad!" ').as(:term).
|
252
273
|
should result_in(Fail[:parse, 'bad!']).at(18)
|
253
274
|
end
|
254
275
|
end
|
255
276
|
|
256
|
-
describe '#match(:expression)' do
|
257
|
-
it 'recognizes dispatch-action-attributed expressions' do
|
258
|
-
parsing(' expr <Expr> ').as(:expression).
|
259
|
-
should result_in(DispatchAction[Apply[:expr], {:target => 'Expr', :method => 'parsed'}]).
|
260
|
-
at(12)
|
261
|
-
end
|
262
|
-
|
263
|
-
it 'recognizes direct-action-attributed expressions' do
|
264
|
-
parsing(' digits {|_| _.to_i} ').as(:expression).
|
265
|
-
should result_in(DirectAction[Apply[:digits], '|_| _.to_i']).at(20)
|
266
|
-
end
|
267
|
-
|
268
|
-
it 'recognizes sequence expressions' do
|
269
|
-
parsing(' name "=" value ').as(:expression).
|
270
|
-
should result_in(Sequence[Apply[:name], Match[%r{=}], Apply[:value]]).at(15)
|
271
|
-
end
|
272
|
-
|
273
|
-
it 'recognizes attributed sequence expressions' do
|
274
|
-
parsing(' name "=" value <Assign>').as(:expression).
|
275
|
-
should result_in(DispatchAction[
|
276
|
-
Sequence[Apply[:name], Match[%r{=}], Apply[:value]],
|
277
|
-
{:target => 'Assign', :method => 'parsed'}]).
|
278
|
-
at(24)
|
279
|
-
end
|
280
|
-
|
281
|
-
it 'recognizes choice expressions' do
|
282
|
-
parsing(' string | number ').as(:expression).
|
283
|
-
should result_in(Choice[Apply[:string], Apply[:number]]).at(16)
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
277
|
end
|