citrus 2.2.2 → 2.3.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 +160 -98
- data/doc/background.markdown +16 -17
- data/doc/example.markdown +86 -46
- data/doc/syntax.markdown +59 -36
- data/examples/calc.citrus +9 -3
- data/examples/calc.rb +9 -3
- data/examples/ip.rb +2 -0
- data/lib/citrus.rb +576 -473
- data/lib/citrus/file.rb +80 -71
- data/test/alias_test.rb +8 -2
- data/test/and_predicate_test.rb +13 -2
- data/test/but_predicate_test.rb +9 -3
- data/test/calc_file_test.rb +9 -4
- data/test/calc_test.rb +4 -4
- data/test/choice_test.rb +11 -5
- data/test/extension_test.rb +2 -12
- data/test/file_test.rb +215 -175
- data/test/grammar_test.rb +1 -1
- data/test/helper.rb +2 -2
- data/test/input_test.rb +44 -48
- data/test/label_test.rb +14 -17
- data/test/match_test.rb +21 -63
- data/test/not_predicate_test.rb +13 -2
- data/test/repeat_test.rb +20 -20
- data/test/sequence_test.rb +22 -8
- data/test/string_terminal_test.rb +10 -5
- data/test/super_test.rb +19 -16
- data/test/terminal_test.rb +7 -2
- metadata +21 -12
- data/benchmark/after.dat +0 -192
- data/benchmark/before.dat +0 -192
- data/test/_files/grammar3.citrus +0 -112
data/test/calc_test.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require File.expand_path('../helper', __FILE__)
|
2
2
|
|
3
|
-
if defined?(Calc)
|
4
|
-
Object.__send__(:remove_const, :Calc)
|
5
|
-
end
|
6
|
-
|
7
3
|
require File.expand_path('../../examples/calc', __FILE__)
|
8
4
|
|
9
5
|
class CalcTest < Test::Unit::TestCase
|
10
6
|
include CalcTestMethods
|
7
|
+
|
8
|
+
def do_test(expr)
|
9
|
+
super(expr, Calc)
|
10
|
+
end
|
11
11
|
end
|
data/test/choice_test.rb
CHANGED
@@ -7,8 +7,8 @@ class ChoiceTest < Test::Unit::TestCase
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_exec
|
10
|
-
a = Rule.
|
11
|
-
b = Rule.
|
10
|
+
a = Rule.for('a')
|
11
|
+
b = Rule.for('b')
|
12
12
|
rule = Choice.new([ a, b ])
|
13
13
|
|
14
14
|
events = rule.exec(Input.new(''))
|
@@ -16,11 +16,11 @@ class ChoiceTest < Test::Unit::TestCase
|
|
16
16
|
|
17
17
|
events = rule.exec(Input.new('a'))
|
18
18
|
assert(events)
|
19
|
-
assert_equal([rule
|
19
|
+
assert_equal([rule, a, CLOSE, 1, CLOSE, 1], events)
|
20
20
|
|
21
21
|
events = rule.exec(Input.new('b'))
|
22
22
|
assert(events)
|
23
|
-
assert_equal([rule
|
23
|
+
assert_equal([rule, b, CLOSE, 1, CLOSE, 1], events)
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_to_s
|
@@ -28,10 +28,16 @@ class ChoiceTest < Test::Unit::TestCase
|
|
28
28
|
assert_equal('"a" | "b"', rule.to_s)
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def test_to_embedded_s
|
32
32
|
rule1 = Choice.new(%w<a b>)
|
33
33
|
rule2 = Choice.new(%w<c d>)
|
34
34
|
rule = Choice.new([rule1, rule2])
|
35
35
|
assert_equal('("a" | "b") | ("c" | "d")', rule.to_s)
|
36
36
|
end
|
37
|
+
|
38
|
+
def test_to_s_with_label
|
39
|
+
rule = Choice.new(%w<a b>)
|
40
|
+
rule.label = 'a_label'
|
41
|
+
assert_equal('a_label:("a" | "b")', rule.to_s)
|
42
|
+
end
|
37
43
|
end
|
data/test/extension_test.rb
CHANGED
@@ -7,13 +7,11 @@ class ExtensionTest < Test::Unit::TestCase
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
module NumericModule
|
11
11
|
def add_one
|
12
12
|
to_i + 1
|
13
13
|
end
|
14
|
-
|
15
|
-
|
16
|
-
NumericModule = Module.new(&NumericProc)
|
14
|
+
end
|
17
15
|
|
18
16
|
NumericProcBare = Proc.new {
|
19
17
|
to_i + 1
|
@@ -27,14 +25,6 @@ class ExtensionTest < Test::Unit::TestCase
|
|
27
25
|
assert_equal(:test, match.a_test)
|
28
26
|
end
|
29
27
|
|
30
|
-
def test_numeric_proc
|
31
|
-
rule = StringTerminal.new('1')
|
32
|
-
rule.extension = NumericProc
|
33
|
-
match = rule.parse('1')
|
34
|
-
assert(match)
|
35
|
-
assert_equal(2, match.add_one)
|
36
|
-
end
|
37
|
-
|
38
28
|
def test_numeric_module
|
39
29
|
rule = StringTerminal.new('1')
|
40
30
|
rule.extension = NumericModule
|
data/test/file_test.rb
CHANGED
@@ -5,14 +5,14 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
5
5
|
## File tests
|
6
6
|
|
7
7
|
def run_file_test(file, root)
|
8
|
-
match = File.parse(
|
8
|
+
match = File.parse(::File.read(file), :root => root)
|
9
9
|
assert(match)
|
10
10
|
end
|
11
11
|
|
12
12
|
%w<rule grammar>.each do |type|
|
13
|
-
Dir[
|
13
|
+
Dir[::File.dirname(__FILE__) + "/_files/#{type}*.citrus"].each do |path|
|
14
14
|
module_eval(<<-CODE.gsub(/^ /, ''), __FILE__, __LINE__ + 1)
|
15
|
-
def test_#{
|
15
|
+
def test_#{::File.basename(path, '.citrus')}
|
16
16
|
run_file_test("#{path}", :#{type})
|
17
17
|
end
|
18
18
|
CODE
|
@@ -24,173 +24,171 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
24
24
|
def test_rule_body_alias
|
25
25
|
match = File.parse('rule_name', :root => :rule_body)
|
26
26
|
assert(match)
|
27
|
-
assert_kind_of(Rule, match.value)
|
28
27
|
assert_instance_of(Alias, match.value)
|
29
28
|
end
|
30
29
|
|
31
|
-
def
|
30
|
+
def test_rule_body_dot
|
32
31
|
match = File.parse('.', :root => :rule_body)
|
33
32
|
assert(match)
|
34
|
-
assert_kind_of(Rule, match.value)
|
35
33
|
assert_instance_of(Terminal, match.value)
|
34
|
+
end
|
36
35
|
|
36
|
+
def test_rule_body_character_range
|
37
37
|
match = File.parse('[a-z]', :root => :rule_body)
|
38
38
|
assert(match)
|
39
|
-
assert_kind_of(Rule, match.value)
|
40
39
|
assert_instance_of(Terminal, match.value)
|
40
|
+
end
|
41
41
|
|
42
|
+
def test_rule_body_terminal
|
42
43
|
match = File.parse('/./', :root => :rule_body)
|
43
44
|
assert(match)
|
44
|
-
assert_kind_of(Rule, match.value)
|
45
|
-
assert_instance_of(Terminal, match.value)
|
46
|
-
|
47
|
-
match = File.parse("[0-9] {\n def value\n text.to_i\n end\n}\n", :root => :rule_body)
|
48
|
-
assert(match)
|
49
|
-
assert_kind_of(Rule, match.value)
|
50
45
|
assert_instance_of(Terminal, match.value)
|
51
46
|
end
|
52
47
|
|
53
|
-
def
|
48
|
+
def test_rule_body_string_terminal_empty
|
54
49
|
match = File.parse('""', :root => :rule_body)
|
55
50
|
assert(match)
|
56
|
-
assert_kind_of(Rule, match.value)
|
57
51
|
assert_instance_of(StringTerminal, match.value)
|
52
|
+
end
|
58
53
|
|
54
|
+
def test_rule_body_string_terminal
|
59
55
|
match = File.parse('"a"', :root => :rule_body)
|
60
56
|
assert(match)
|
61
|
-
assert_kind_of(Rule, match.value)
|
62
57
|
assert_instance_of(StringTerminal, match.value)
|
58
|
+
end
|
63
59
|
|
60
|
+
def test_rule_body_string_terminal_empty_block
|
64
61
|
match = File.parse('"" {}', :root => :rule_body)
|
65
62
|
assert(match)
|
66
|
-
assert_kind_of(Rule, match.value)
|
67
63
|
assert_instance_of(StringTerminal, match.value)
|
68
64
|
end
|
69
65
|
|
70
|
-
def
|
66
|
+
def test_rule_body_repeat_string_terminal
|
71
67
|
match = File.parse('"a"*', :root => :rule_body)
|
72
68
|
assert(match)
|
73
|
-
assert_kind_of(Rule, match.value)
|
74
69
|
assert_instance_of(Repeat, match.value)
|
75
|
-
|
76
|
-
assert_equal(Infinity, match.value.max)
|
70
|
+
end
|
77
71
|
|
72
|
+
def test_rule_body_repeat_empty_string_terminal_block
|
78
73
|
match = File.parse('""* {}', :root => :rule_body)
|
79
74
|
assert(match)
|
80
|
-
assert_kind_of(Rule, match.value)
|
81
75
|
assert_instance_of(Repeat, match.value)
|
76
|
+
end
|
82
77
|
|
78
|
+
def test_rule_body_repeat_sequence
|
83
79
|
match = File.parse('("a" "b")*', :root => :rule_body)
|
84
80
|
assert(match)
|
85
|
-
assert_kind_of(Rule, match.value)
|
86
81
|
assert_instance_of(Repeat, match.value)
|
82
|
+
end
|
87
83
|
|
84
|
+
def test_rule_body_repeat_choice
|
88
85
|
match = File.parse('("a" | "b")*', :root => :rule_body)
|
89
86
|
assert(match)
|
90
|
-
assert_kind_of(Rule, match.value)
|
91
87
|
assert_instance_of(Repeat, match.value)
|
88
|
+
end
|
92
89
|
|
90
|
+
def test_rule_body_repeat_sequence_block
|
93
91
|
match = File.parse('("a" "b")* {}', :root => :rule_body)
|
94
92
|
assert(match)
|
95
|
-
assert_kind_of(Rule, match.value)
|
96
93
|
assert_instance_of(Repeat, match.value)
|
94
|
+
end
|
97
95
|
|
96
|
+
def test_rule_body_repeat_choice_block
|
98
97
|
match = File.parse('("a" | "b")* {}', :root => :rule_body)
|
99
98
|
assert(match)
|
100
|
-
assert_kind_of(Rule, match.value)
|
101
99
|
assert_instance_of(Repeat, match.value)
|
100
|
+
end
|
102
101
|
|
102
|
+
def test_rule_body_repeat_sequence_extension
|
103
103
|
match = File.parse('("a" "b")* <Module>', :root => :rule_body)
|
104
104
|
assert(match)
|
105
|
-
assert_kind_of(Rule, match.value)
|
106
105
|
assert_instance_of(Repeat, match.value)
|
106
|
+
end
|
107
107
|
|
108
|
+
def test_rule_body_repeat_sequence_extension_spaced
|
108
109
|
match = File.parse('( "a" "b" )* <Module>', :root => :rule_body)
|
109
110
|
assert(match)
|
110
|
-
assert_kind_of(Rule, match.value)
|
111
111
|
assert_instance_of(Repeat, match.value)
|
112
|
+
end
|
112
113
|
|
114
|
+
def test_rule_body_repeat_choice_extension
|
113
115
|
match = File.parse('("a" | "b")* <Module>', :root => :rule_body)
|
114
116
|
assert(match)
|
115
|
-
assert_kind_of(Rule, match.value)
|
116
|
-
assert_instance_of(Repeat, match.value)
|
117
|
-
|
118
|
-
match = File.parse("[0-9]+ {\n def value\n text.to_i\n end\n}\n", :root => :rule_body)
|
119
|
-
assert(match)
|
120
|
-
assert_kind_of(Rule, match.value)
|
121
117
|
assert_instance_of(Repeat, match.value)
|
122
118
|
end
|
123
119
|
|
124
|
-
def
|
125
|
-
match = File.parse('
|
120
|
+
def test_rule_body_choice_terminal
|
121
|
+
match = File.parse('/./ | /./', :root => :rule_body)
|
126
122
|
assert(match)
|
127
|
-
assert_kind_of(Rule, match.value)
|
128
123
|
assert_instance_of(Choice, match.value)
|
124
|
+
end
|
129
125
|
|
130
|
-
|
126
|
+
def test_rule_body_choice_string_terminal
|
127
|
+
match = File.parse('"a" | "b"', :root => :rule_body)
|
131
128
|
assert(match)
|
132
|
-
assert_kind_of(Rule, match.value)
|
133
129
|
assert_instance_of(Choice, match.value)
|
134
|
-
|
135
|
-
assert_equal(2, match.find(:regular_expression).length)
|
136
|
-
assert_equal(0, match.find(:dot).length)
|
130
|
+
end
|
137
131
|
|
132
|
+
def test_rule_body_choice_mixed
|
138
133
|
match = File.parse('("a" | /./)', :root => :rule_body)
|
139
134
|
assert(match)
|
140
|
-
assert_kind_of(Rule, match.value)
|
141
135
|
assert_instance_of(Choice, match.value)
|
136
|
+
end
|
142
137
|
|
138
|
+
def test_rule_body_choice_extended
|
143
139
|
match = File.parse('("a" | "b") <Module>', :root => :rule_body)
|
144
140
|
assert(match)
|
145
|
-
assert_kind_of(Rule, match.value)
|
146
141
|
assert_instance_of(Choice, match.value)
|
147
142
|
end
|
148
143
|
|
149
|
-
def
|
150
|
-
match = File.parse('
|
144
|
+
def test_rule_body_sequence_terminal
|
145
|
+
match = File.parse('/./ /./', :root => :rule_body)
|
151
146
|
assert(match)
|
152
|
-
assert_kind_of(Rule, match.value)
|
153
147
|
assert_instance_of(Sequence, match.value)
|
148
|
+
end
|
154
149
|
|
155
|
-
|
150
|
+
def test_rule_body_sequence_string_terminal
|
151
|
+
match = File.parse('"a" "b"', :root => :rule_body)
|
156
152
|
assert(match)
|
157
|
-
assert_kind_of(Rule, match.value)
|
158
153
|
assert_instance_of(Sequence, match.value)
|
159
|
-
|
154
|
+
end
|
160
155
|
|
156
|
+
def test_rule_body_sequence_extension
|
161
157
|
match = File.parse('( "a" "b" ) <Module>', :root => :rule_body)
|
162
158
|
assert(match)
|
163
|
-
assert_kind_of(Rule, match.value)
|
164
159
|
assert_instance_of(Sequence, match.value)
|
160
|
+
end
|
165
161
|
|
162
|
+
def test_rule_body_sequence_mixed
|
166
163
|
match = File.parse('"a" ("b" | /./)* <Module>', :root => :rule_body)
|
167
164
|
assert(match)
|
168
|
-
assert_kind_of(Rule, match.value)
|
169
165
|
assert_instance_of(Sequence, match.value)
|
166
|
+
end
|
170
167
|
|
168
|
+
def test_rule_body_sequence_block
|
171
169
|
match = File.parse('"a" ("b" | /./)* {}', :root => :rule_body)
|
172
170
|
assert(match)
|
173
|
-
assert_kind_of(Rule, match.value)
|
174
171
|
assert_instance_of(Sequence, match.value)
|
175
172
|
end
|
176
173
|
|
177
|
-
def
|
174
|
+
def test_precedence_sequence_before_choice
|
178
175
|
# Sequence should bind more tightly than Choice.
|
179
176
|
match = File.parse('"a" "b" | "c"', :root => :rule_body)
|
180
177
|
assert(match)
|
181
|
-
assert_kind_of(Rule, match.value)
|
182
178
|
assert_instance_of(Choice, match.value)
|
179
|
+
end
|
183
180
|
|
181
|
+
def test_precedence_parentheses
|
184
182
|
# Parentheses should change binding precedence.
|
185
183
|
match = File.parse('"a" ("b" | "c")', :root => :rule_body)
|
186
184
|
assert(match)
|
187
|
-
assert_kind_of(Rule, match.value)
|
188
185
|
assert_instance_of(Sequence, match.value)
|
186
|
+
end
|
189
187
|
|
188
|
+
def test_precedence_repeat_before_predicate
|
190
189
|
# Repeat should bind more tightly than AndPredicate.
|
191
190
|
match = File.parse("&'a'+", :root => :rule_body)
|
192
191
|
assert(match)
|
193
|
-
assert_kind_of(Rule, match.value)
|
194
192
|
assert_instance_of(AndPredicate, match.value)
|
195
193
|
end
|
196
194
|
|
@@ -202,31 +200,39 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
202
200
|
def test_choice
|
203
201
|
match = File.parse('"a" | "b"', :root => :choice)
|
204
202
|
assert(match)
|
205
|
-
assert_equal(2, match.rules.length)
|
206
203
|
assert_instance_of(Choice, match.value)
|
207
204
|
end
|
208
205
|
|
209
206
|
def test_choice_embedded_sequence
|
210
207
|
match = File.parse('"a" | ("b" "c")', :root => :choice)
|
211
208
|
assert(match)
|
212
|
-
assert_equal(2, match.rules.length)
|
213
209
|
assert_instance_of(Choice, match.value)
|
214
210
|
end
|
215
211
|
|
216
212
|
def test_sequence
|
217
213
|
match = File.parse('"" ""', :root => :sequence)
|
218
214
|
assert(match)
|
219
|
-
assert_equal(2, match.rules.length)
|
220
215
|
assert_instance_of(Sequence, match.value)
|
221
216
|
end
|
222
217
|
|
223
218
|
def test_sequence_embedded_choice
|
224
219
|
match = File.parse('"a" ("b" | "c")', :root => :sequence)
|
225
220
|
assert(match)
|
226
|
-
assert_equal(2, match.rules.length)
|
227
221
|
assert_instance_of(Sequence, match.value)
|
228
222
|
end
|
229
223
|
|
224
|
+
def test_label_expression
|
225
|
+
match = File.parse('label:""', :root => :label_expression)
|
226
|
+
assert(match)
|
227
|
+
assert_instance_of(StringTerminal, match.value)
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_label_expression_space
|
231
|
+
match = File.parse('label :"" ', :root => :label_expression)
|
232
|
+
assert(match)
|
233
|
+
assert_instance_of(StringTerminal, match.value)
|
234
|
+
end
|
235
|
+
|
230
236
|
def test_expression_tag
|
231
237
|
match = File.parse('"" <Module>', :root => :expression)
|
232
238
|
assert(match)
|
@@ -251,84 +257,66 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
251
257
|
def test_prefix_and
|
252
258
|
match = File.parse('&""', :root => :prefix)
|
253
259
|
assert(match)
|
254
|
-
assert_kind_of(Rule, match.value)
|
255
260
|
assert_instance_of(AndPredicate, match.value)
|
256
261
|
end
|
257
262
|
|
258
263
|
def test_prefix_not
|
259
264
|
match = File.parse('!""', :root => :prefix)
|
260
265
|
assert(match)
|
261
|
-
assert_kind_of(Rule, match.value)
|
262
266
|
assert_instance_of(NotPredicate, match.value)
|
263
267
|
end
|
264
268
|
|
265
|
-
def
|
266
|
-
match = File.parse('
|
269
|
+
def test_prefix_but
|
270
|
+
match = File.parse('~""', :root => :prefix)
|
267
271
|
assert(match)
|
268
|
-
|
269
|
-
assert_instance_of(Label, match.value)
|
270
|
-
end
|
271
|
-
|
272
|
-
def test_prefix_label_space
|
273
|
-
match = File.parse('label :"" ', :root => :prefix)
|
274
|
-
assert(match)
|
275
|
-
assert_kind_of(Rule, match.value)
|
276
|
-
assert_instance_of(Label, match.value)
|
272
|
+
assert_instance_of(ButPredicate, match.value)
|
277
273
|
end
|
278
274
|
|
279
275
|
def test_suffix_plus
|
280
276
|
match = File.parse('""+', :root => :suffix)
|
281
277
|
assert(match)
|
282
|
-
assert_kind_of(Rule, match.value)
|
283
278
|
assert_instance_of(Repeat, match.value)
|
284
279
|
end
|
285
280
|
|
286
281
|
def test_suffix_question
|
287
|
-
match = File.parse('""?
|
282
|
+
match = File.parse('""?', :root => :suffix)
|
288
283
|
assert(match)
|
289
|
-
assert_kind_of(Rule, match.value)
|
290
284
|
assert_instance_of(Repeat, match.value)
|
291
285
|
end
|
292
286
|
|
293
287
|
def test_suffix_star
|
294
288
|
match = File.parse('""*', :root => :suffix)
|
295
289
|
assert(match)
|
296
|
-
assert_kind_of(Rule, match.value)
|
297
290
|
assert_instance_of(Repeat, match.value)
|
298
291
|
end
|
299
292
|
|
300
293
|
def test_suffix_n_star
|
301
294
|
match = File.parse('""1*', :root => :suffix)
|
302
295
|
assert(match)
|
303
|
-
assert_kind_of(Rule, match.value)
|
304
296
|
assert_instance_of(Repeat, match.value)
|
305
297
|
end
|
306
298
|
|
307
299
|
def test_suffix_star_n
|
308
300
|
match = File.parse('""*2', :root => :suffix)
|
309
301
|
assert(match)
|
310
|
-
assert_kind_of(Rule, match.value)
|
311
302
|
assert_instance_of(Repeat, match.value)
|
312
303
|
end
|
313
304
|
|
314
305
|
def test_suffix_n_star_n
|
315
306
|
match = File.parse('""1*2', :root => :suffix)
|
316
307
|
assert(match)
|
317
|
-
assert_kind_of(Rule, match.value)
|
318
308
|
assert_instance_of(Repeat, match.value)
|
319
309
|
end
|
320
310
|
|
321
311
|
def test_primary_alias
|
322
312
|
match = File.parse('rule_name', :root => :primary)
|
323
313
|
assert(match)
|
324
|
-
assert_kind_of(Rule, match.value)
|
325
314
|
assert_instance_of(Alias, match.value)
|
326
315
|
end
|
327
316
|
|
328
317
|
def test_primary_string_terminal
|
329
318
|
match = File.parse('"a"', :root => :primary)
|
330
319
|
assert(match)
|
331
|
-
assert_kind_of(Rule, match.value)
|
332
320
|
assert_instance_of(StringTerminal, match.value)
|
333
321
|
end
|
334
322
|
|
@@ -393,42 +381,36 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
393
381
|
def test_terminal_character_class
|
394
382
|
match = File.parse('[a-z]', :root => :terminal)
|
395
383
|
assert(match)
|
396
|
-
assert_kind_of(Rule, match.value)
|
397
384
|
assert_instance_of(Terminal, match.value)
|
398
385
|
end
|
399
386
|
|
400
387
|
def test_terminal_dot
|
401
388
|
match = File.parse('.', :root => :terminal)
|
402
389
|
assert(match)
|
403
|
-
assert_kind_of(Rule, match.value)
|
404
390
|
assert_instance_of(Terminal, match.value)
|
405
391
|
end
|
406
392
|
|
407
393
|
def test_terminal_regular_expression
|
408
394
|
match = File.parse('/./', :root => :terminal)
|
409
395
|
assert(match)
|
410
|
-
assert_kind_of(Rule, match.value)
|
411
396
|
assert_instance_of(Terminal, match.value)
|
412
397
|
end
|
413
398
|
|
414
399
|
def test_string_terminal_single_quoted
|
415
400
|
match = File.parse("'a'", :root => :string_terminal)
|
416
401
|
assert(match)
|
417
|
-
assert_kind_of(Rule, match.value)
|
418
402
|
assert_instance_of(StringTerminal, match.value)
|
419
403
|
end
|
420
404
|
|
421
405
|
def test_string_terminal_double_quoted
|
422
406
|
match = File.parse('"a"', :root => :string_terminal)
|
423
407
|
assert(match)
|
424
|
-
assert_kind_of(Rule, match.value)
|
425
408
|
assert_instance_of(StringTerminal, match.value)
|
426
409
|
end
|
427
410
|
|
428
411
|
def test_string_terminal_case_insensitive
|
429
412
|
match = File.parse('`a`', :root => :string_terminal)
|
430
413
|
assert(match)
|
431
|
-
assert_kind_of(Rule, match.value)
|
432
414
|
assert_instance_of(StringTerminal, match.value)
|
433
415
|
end
|
434
416
|
|
@@ -498,122 +480,96 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
498
480
|
assert_equal('&', match.value)
|
499
481
|
end
|
500
482
|
|
501
|
-
def test_character_class
|
502
|
-
match = File.parse('[_]', :root => :character_class)
|
503
|
-
assert(match)
|
504
|
-
assert_equal(/\A[_]/, match.value)
|
505
|
-
end
|
506
|
-
|
507
|
-
def test_character_class_a_z
|
508
|
-
match = File.parse('[a-z]', :root => :character_class)
|
509
|
-
assert(match)
|
510
|
-
assert_equal(/\A[a-z]/, match.value)
|
511
|
-
end
|
512
|
-
|
513
|
-
def test_character_class_a_z_0_9
|
514
|
-
match = File.parse('[a-z0-9]', :root => :character_class)
|
515
|
-
assert(match)
|
516
|
-
assert_equal(/\A[a-z0-9]/, match.value)
|
517
|
-
end
|
518
|
-
|
519
|
-
def test_character_class_nested_square_brackets
|
520
|
-
match = File.parse('[\[-\]]', :root => :character_class)
|
521
|
-
assert(match)
|
522
|
-
assert_equal(/\A[\[-\]]/, match.value)
|
523
|
-
end
|
524
|
-
|
525
|
-
def test_character_class_hex_range
|
526
|
-
match = File.parse('[\\x26-\\x29]', :root => :character_class)
|
527
|
-
assert(match)
|
528
|
-
assert_equal(/\A[\x26-\x29]/, match.value)
|
529
|
-
end
|
530
|
-
|
531
|
-
def test_dot
|
532
|
-
match = File.parse('.', :root => :dot)
|
533
|
-
assert(match)
|
534
|
-
assert_equal(DOT, match.value)
|
535
|
-
end
|
536
|
-
|
537
483
|
def test_regular_expression
|
538
484
|
match = File.parse('/./', :root => :regular_expression)
|
539
485
|
assert(match)
|
540
|
-
|
486
|
+
rule = match.value
|
487
|
+
assert_instance_of(Terminal, rule)
|
488
|
+
assert_equal(/./, rule.regexp)
|
541
489
|
end
|
542
490
|
|
543
491
|
def test_regular_expression_escaped_forward_slash
|
544
492
|
match = File.parse('/\\//', :root => :regular_expression)
|
545
493
|
assert(match)
|
546
|
-
|
494
|
+
rule = match.value
|
495
|
+
assert_instance_of(Terminal, rule)
|
496
|
+
assert_equal(/\//, rule.regexp)
|
547
497
|
end
|
548
498
|
|
549
499
|
def test_regular_expression_escaped_backslash
|
550
500
|
match = File.parse('/\\\\/', :root => :regular_expression)
|
551
501
|
assert(match)
|
552
|
-
|
502
|
+
rule = match.value
|
503
|
+
assert_instance_of(Terminal, rule)
|
504
|
+
assert_equal(/\\/, rule.regexp)
|
553
505
|
end
|
554
506
|
|
555
507
|
def test_regular_expression_hex
|
556
508
|
match = File.parse('/\\x26/', :root => :regular_expression)
|
557
509
|
assert(match)
|
558
|
-
|
510
|
+
rule = match.value
|
511
|
+
assert_instance_of(Terminal, rule)
|
512
|
+
assert_equal(/\x26/, rule.regexp)
|
559
513
|
end
|
560
514
|
|
561
515
|
def test_regular_expression_with_flag
|
562
516
|
match = File.parse('/a/i', :root => :regular_expression)
|
563
517
|
assert(match)
|
564
|
-
|
518
|
+
rule = match.value
|
519
|
+
assert_instance_of(Terminal, rule)
|
520
|
+
assert_equal(/a/i, rule.regexp)
|
565
521
|
end
|
566
522
|
|
567
|
-
def
|
568
|
-
match = File.parse('
|
569
|
-
assert(match)
|
570
|
-
assert_kind_of(Rule, match.value(''))
|
571
|
-
end
|
572
|
-
|
573
|
-
def test_predicate_not
|
574
|
-
match = File.parse('!', :root => :predicate)
|
523
|
+
def test_character_class
|
524
|
+
match = File.parse('[_]', :root => :character_class)
|
575
525
|
assert(match)
|
576
|
-
|
526
|
+
rule = match.value
|
527
|
+
assert_instance_of(Terminal, rule)
|
528
|
+
assert_equal(/[_]/n, rule.regexp)
|
577
529
|
end
|
578
530
|
|
579
|
-
def
|
580
|
-
match = File.parse('
|
531
|
+
def test_character_class_a_z
|
532
|
+
match = File.parse('[a-z]', :root => :character_class)
|
581
533
|
assert(match)
|
582
|
-
|
534
|
+
rule = match.value
|
535
|
+
assert_instance_of(Terminal, rule)
|
536
|
+
assert_equal(/[a-z]/n, rule.regexp)
|
583
537
|
end
|
584
538
|
|
585
|
-
def
|
586
|
-
match = File.parse('
|
539
|
+
def test_character_class_nested_square_brackets
|
540
|
+
match = File.parse('[\[-\]]', :root => :character_class)
|
587
541
|
assert(match)
|
588
|
-
|
542
|
+
rule = match.value
|
543
|
+
assert_instance_of(Terminal, rule)
|
544
|
+
assert_equal(/[\[-\]]/n, rule.regexp)
|
589
545
|
end
|
590
546
|
|
591
|
-
def
|
592
|
-
match = File.parse('
|
547
|
+
def test_character_class_hex_range
|
548
|
+
match = File.parse('[\\x26-\\x29]', :root => :character_class)
|
593
549
|
assert(match)
|
594
|
-
|
550
|
+
rule = match.value
|
551
|
+
assert_instance_of(Terminal, rule)
|
552
|
+
assert_equal(/[\x26-\x29]/, rule.regexp)
|
595
553
|
end
|
596
554
|
|
597
|
-
def
|
598
|
-
match = File.parse('
|
555
|
+
def test_dot
|
556
|
+
match = File.parse('.', :root => :dot)
|
599
557
|
assert(match)
|
600
|
-
|
558
|
+
rule = match.value
|
559
|
+
assert_instance_of(Terminal, rule)
|
560
|
+
assert_equal(DOT, rule.regexp)
|
601
561
|
end
|
602
562
|
|
603
563
|
def test_label
|
604
564
|
match = File.parse('label:', :root => :label)
|
605
565
|
assert(match)
|
606
|
-
|
607
|
-
assert_instance_of(Label, v)
|
608
|
-
assert_equal(:label, v.label)
|
566
|
+
assert_equal(:label, match.value)
|
609
567
|
end
|
610
568
|
|
611
569
|
def test_label_spaced
|
612
570
|
match = File.parse('a_label : ', :root => :label)
|
613
571
|
assert(match)
|
614
|
-
|
615
|
-
assert_instance_of(Label, v)
|
616
|
-
assert_equal(:a_label, v.label)
|
572
|
+
assert_equal(:a_label, match.value)
|
617
573
|
end
|
618
574
|
|
619
575
|
def test_tag
|
@@ -672,9 +628,21 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
672
628
|
end
|
673
629
|
|
674
630
|
def test_block_def
|
631
|
+
match = File.parse("{def value; 'a' end}", :root => :block)
|
632
|
+
assert(match)
|
633
|
+
assert(match.value)
|
634
|
+
assert_instance_of(Module, match.value)
|
635
|
+
method_names = match.value.instance_methods.map {|m| m.to_sym }
|
636
|
+
assert_equal([:value], method_names)
|
637
|
+
end
|
638
|
+
|
639
|
+
def test_block_def_multiline
|
675
640
|
match = File.parse("{\n def value\n 'a'\n end\n} ", :root => :block)
|
676
641
|
assert(match)
|
677
642
|
assert(match.value)
|
643
|
+
assert_instance_of(Module, match.value)
|
644
|
+
method_names = match.value.instance_methods.map {|m| m.to_sym }
|
645
|
+
assert_equal([:value], method_names)
|
678
646
|
end
|
679
647
|
|
680
648
|
def test_block_with_interpolation
|
@@ -683,6 +651,60 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
683
651
|
assert(match.value)
|
684
652
|
end
|
685
653
|
|
654
|
+
def test_predicate_and
|
655
|
+
match = File.parse('&', :root => :predicate)
|
656
|
+
assert(match)
|
657
|
+
assert_instance_of(AndPredicate, match.value(''))
|
658
|
+
end
|
659
|
+
|
660
|
+
def test_predicate_not
|
661
|
+
match = File.parse('!', :root => :predicate)
|
662
|
+
assert(match)
|
663
|
+
assert_instance_of(NotPredicate, match.value(''))
|
664
|
+
end
|
665
|
+
|
666
|
+
def test_predicate_but
|
667
|
+
match = File.parse('~', :root => :predicate)
|
668
|
+
assert(match)
|
669
|
+
assert_instance_of(ButPredicate, match.value(''))
|
670
|
+
end
|
671
|
+
|
672
|
+
def test_and
|
673
|
+
match = File.parse('&', :root => :and)
|
674
|
+
assert(match)
|
675
|
+
assert_instance_of(AndPredicate, match.value(''))
|
676
|
+
end
|
677
|
+
|
678
|
+
def test_and_space
|
679
|
+
match = File.parse('& ', :root => :and)
|
680
|
+
assert(match)
|
681
|
+
assert_instance_of(AndPredicate, match.value(''))
|
682
|
+
end
|
683
|
+
|
684
|
+
def test_not
|
685
|
+
match = File.parse('!', :root => :not)
|
686
|
+
assert(match)
|
687
|
+
assert_instance_of(NotPredicate, match.value(''))
|
688
|
+
end
|
689
|
+
|
690
|
+
def test_not_space
|
691
|
+
match = File.parse('! ', :root => :not)
|
692
|
+
assert(match)
|
693
|
+
assert_instance_of(NotPredicate, match.value(''))
|
694
|
+
end
|
695
|
+
|
696
|
+
def test_but
|
697
|
+
match = File.parse('~', :root => :but)
|
698
|
+
assert(match)
|
699
|
+
assert_instance_of(ButPredicate, match.value(''))
|
700
|
+
end
|
701
|
+
|
702
|
+
def test_but_space
|
703
|
+
match = File.parse('~ ', :root => :but)
|
704
|
+
assert(match)
|
705
|
+
assert_instance_of(ButPredicate, match.value(''))
|
706
|
+
end
|
707
|
+
|
686
708
|
def test_repeat_question
|
687
709
|
match = File.parse('?', :root => :repeat)
|
688
710
|
assert(match)
|
@@ -722,64 +744,82 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
722
744
|
def test_question
|
723
745
|
match = File.parse('?', :root => :question)
|
724
746
|
assert(match)
|
725
|
-
|
726
|
-
|
747
|
+
rule = match.value('')
|
748
|
+
assert_instance_of(Repeat, rule)
|
749
|
+
assert_equal(0, rule.min)
|
750
|
+
assert_equal(1, rule.max)
|
727
751
|
end
|
728
752
|
|
729
753
|
def test_question_space
|
730
754
|
match = File.parse('? ', :root => :question)
|
731
755
|
assert(match)
|
732
|
-
|
733
|
-
|
756
|
+
rule = match.value('')
|
757
|
+
assert_instance_of(Repeat, rule)
|
758
|
+
assert_equal(0, rule.min)
|
759
|
+
assert_equal(1, rule.max)
|
734
760
|
end
|
735
761
|
|
736
762
|
def test_plus
|
737
763
|
match = File.parse('+', :root => :plus)
|
738
764
|
assert(match)
|
739
|
-
|
740
|
-
|
765
|
+
rule = match.value('')
|
766
|
+
assert_instance_of(Repeat, rule)
|
767
|
+
assert_equal(1, rule.min)
|
768
|
+
assert_equal(Infinity, rule.max)
|
741
769
|
end
|
742
770
|
|
743
771
|
def test_plus_space
|
744
772
|
match = File.parse('+ ', :root => :plus)
|
745
773
|
assert(match)
|
746
|
-
|
747
|
-
|
774
|
+
rule = match.value('')
|
775
|
+
assert_instance_of(Repeat, rule)
|
776
|
+
assert_equal(1, rule.min)
|
777
|
+
assert_equal(Infinity, rule.max)
|
748
778
|
end
|
749
779
|
|
750
780
|
def test_star
|
751
781
|
match = File.parse('*', :root => :star)
|
752
782
|
assert(match)
|
753
|
-
|
754
|
-
|
783
|
+
rule = match.value('')
|
784
|
+
assert_instance_of(Repeat, rule)
|
785
|
+
assert_equal(0, rule.min)
|
786
|
+
assert_equal(Infinity, rule.max)
|
755
787
|
end
|
756
788
|
|
757
789
|
def test_n_star
|
758
790
|
match = File.parse('1*', :root => :star)
|
759
791
|
assert(match)
|
760
|
-
|
761
|
-
|
792
|
+
rule = match.value('')
|
793
|
+
assert_instance_of(Repeat, rule)
|
794
|
+
assert_equal(1, rule.min)
|
795
|
+
assert_equal(Infinity, rule.max)
|
762
796
|
end
|
763
797
|
|
764
798
|
def test_star_n
|
765
799
|
match = File.parse('*2', :root => :star)
|
766
800
|
assert(match)
|
767
|
-
|
768
|
-
|
801
|
+
rule = match.value('')
|
802
|
+
assert_instance_of(Repeat, rule)
|
803
|
+
assert_equal(0, rule.min)
|
804
|
+
assert_equal(2, rule.max)
|
769
805
|
end
|
770
806
|
|
771
807
|
def test_n_star_n
|
772
808
|
match = File.parse('1*2', :root => :star)
|
773
809
|
assert(match)
|
774
|
-
|
775
|
-
|
810
|
+
rule = match.value('')
|
811
|
+
assert_instance_of(Repeat, rule)
|
812
|
+
assert_equal(1, rule.min)
|
813
|
+
assert_equal(2, rule.max)
|
776
814
|
end
|
777
815
|
|
778
816
|
def test_n_star_n_space
|
779
817
|
match = File.parse('1*2 ', :root => :star)
|
780
818
|
assert(match)
|
781
|
-
|
782
|
-
|
819
|
+
rule = match.value('')
|
820
|
+
assert_instance_of(Repeat, rule)
|
821
|
+
assert_equal(1, rule.min)
|
822
|
+
assert_equal(2, rule.max)
|
783
823
|
end
|
784
824
|
|
785
825
|
def test_module_name
|