citrus 2.1.2 → 2.2.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 +106 -49
- data/benchmark/after.dat +192 -0
- data/benchmark/before.dat +192 -0
- data/citrus.gemspec +0 -1
- data/doc/extras.markdown +16 -0
- data/doc/syntax.markdown +76 -29
- data/doc/testing.markdown +12 -20
- data/examples/calc.citrus +12 -11
- data/examples/calc.rb +12 -11
- data/lib/citrus.rb +416 -253
- data/lib/citrus/file.rb +66 -33
- data/test/_files/super.citrus +1 -1
- data/test/_files/super2.citrus +13 -0
- data/test/alias_test.rb +18 -34
- data/test/and_predicate_test.rb +15 -10
- data/test/but_predicate_test.rb +22 -17
- data/test/calc_file_test.rb +1 -1
- data/test/choice_test.rb +12 -37
- data/test/{rule_test.rb → extension_test.rb} +17 -16
- data/test/file_test.rb +350 -244
- data/test/grammar_test.rb +5 -11
- data/test/helper.rb +1 -17
- data/test/input_test.rb +172 -2
- data/test/label_test.rb +0 -10
- data/test/match_test.rb +91 -35
- data/test/multibyte_test.rb +4 -4
- data/test/not_predicate_test.rb +15 -10
- data/test/parse_error_test.rb +1 -3
- data/test/repeat_test.rb +59 -32
- data/test/sequence_test.rb +19 -31
- data/test/string_terminal_test.rb +55 -0
- data/test/super_test.rb +31 -31
- data/test/terminal_test.rb +12 -37
- metadata +13 -23
- data/lib/citrus/debug.rb +0 -69
- data/test/debug_test.rb +0 -23
@@ -1,7 +1,6 @@
|
|
1
1
|
require File.expand_path('../helper', __FILE__)
|
2
2
|
|
3
|
-
class
|
4
|
-
|
3
|
+
class ExtensionTest < Test::Unit::TestCase
|
5
4
|
module MatchModule
|
6
5
|
def a_test
|
7
6
|
:test
|
@@ -16,37 +15,39 @@ class RuleTest < Test::Unit::TestCase
|
|
16
15
|
|
17
16
|
NumericModule = Module.new(&NumericProc)
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
match = rule.match(input('a'))
|
23
|
-
assert(match)
|
24
|
-
end
|
18
|
+
NumericProcBare = Proc.new {
|
19
|
+
to_i + 1
|
20
|
+
}
|
25
21
|
|
26
22
|
def test_match_module
|
27
|
-
rule =
|
23
|
+
rule = StringTerminal.new('abc')
|
28
24
|
rule.extension = MatchModule
|
29
|
-
match = rule.
|
25
|
+
match = rule.parse('abc')
|
30
26
|
assert(match)
|
31
27
|
assert_equal(:test, match.a_test)
|
32
28
|
end
|
33
29
|
|
34
30
|
def test_numeric_proc
|
35
|
-
rule =
|
31
|
+
rule = StringTerminal.new('1')
|
36
32
|
rule.extension = NumericProc
|
37
|
-
match = rule.
|
33
|
+
match = rule.parse('1')
|
38
34
|
assert(match)
|
39
35
|
assert_equal(2, match.add_one)
|
40
|
-
assert_instance_of(Float, match.to_f)
|
41
36
|
end
|
42
37
|
|
43
38
|
def test_numeric_module
|
44
|
-
rule =
|
39
|
+
rule = StringTerminal.new('1')
|
45
40
|
rule.extension = NumericModule
|
46
|
-
match = rule.
|
41
|
+
match = rule.parse('1')
|
47
42
|
assert(match)
|
48
43
|
assert_equal(2, match.add_one)
|
49
|
-
assert_instance_of(Float, match.to_f)
|
50
44
|
end
|
51
45
|
|
46
|
+
def test_numeric_proc_bare
|
47
|
+
rule = StringTerminal.new('1')
|
48
|
+
rule.extension = NumericProcBare
|
49
|
+
match = rule.parse('1')
|
50
|
+
assert(match)
|
51
|
+
assert_equal(2, match.value)
|
52
|
+
end
|
52
53
|
end
|
data/test/file_test.rb
CHANGED
@@ -1,324 +1,335 @@
|
|
1
1
|
require File.expand_path('../helper', __FILE__)
|
2
|
-
require 'citrus/file'
|
3
2
|
|
4
3
|
class CitrusFileTest < Test::Unit::TestCase
|
5
4
|
|
6
|
-
# A shortcut for creating a grammar that includes Citrus::File but uses a
|
7
|
-
# different root.
|
8
|
-
def file(root_rule)
|
9
|
-
Grammar.new do
|
10
|
-
include Citrus::File
|
11
|
-
root root_rule
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
5
|
## File tests
|
16
6
|
|
17
7
|
def run_file_test(file, root)
|
18
|
-
|
19
|
-
code = F.read(file)
|
20
|
-
match = grammar.parse(code)
|
8
|
+
match = File.parse(F.read(file), :root => root)
|
21
9
|
assert(match)
|
22
10
|
end
|
23
11
|
|
24
|
-
%w<
|
12
|
+
%w<rule grammar>.each do |type|
|
25
13
|
Dir[F.dirname(__FILE__) + "/_files/#{type}*.citrus"].each do |path|
|
26
|
-
module_eval(<<-
|
14
|
+
module_eval(<<-CODE.gsub(/^ /, ''), __FILE__, __LINE__ + 1)
|
27
15
|
def test_#{F.basename(path, '.citrus')}
|
28
16
|
run_file_test("#{path}", :#{type})
|
29
17
|
end
|
30
|
-
|
18
|
+
CODE
|
31
19
|
end
|
32
20
|
end
|
33
21
|
|
34
22
|
## Hierarchical syntax
|
35
23
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
match = grammar.parse('"a" | "b"')
|
40
|
-
assert(match)
|
41
|
-
assert_kind_of(Rule, match.value)
|
42
|
-
assert_instance_of(Choice, match.value)
|
43
|
-
|
44
|
-
match = grammar.parse('rule_name')
|
24
|
+
def test_rule_body_alias
|
25
|
+
match = File.parse('rule_name', :root => :rule_body)
|
45
26
|
assert(match)
|
46
27
|
assert_kind_of(Rule, match.value)
|
47
28
|
assert_instance_of(Alias, match.value)
|
29
|
+
end
|
48
30
|
|
49
|
-
|
31
|
+
def test_rule_body_terminal
|
32
|
+
match = File.parse('.', :root => :rule_body)
|
50
33
|
assert(match)
|
51
34
|
assert_kind_of(Rule, match.value)
|
52
35
|
assert_instance_of(Terminal, match.value)
|
53
36
|
|
54
|
-
match =
|
37
|
+
match = File.parse('[a-z]', :root => :rule_body)
|
55
38
|
assert(match)
|
56
39
|
assert_kind_of(Rule, match.value)
|
57
40
|
assert_instance_of(Terminal, match.value)
|
58
41
|
|
59
|
-
match =
|
42
|
+
match = File.parse('/./', :root => :rule_body)
|
60
43
|
assert(match)
|
61
44
|
assert_kind_of(Rule, match.value)
|
62
|
-
assert_instance_of(
|
45
|
+
assert_instance_of(Terminal, match.value)
|
63
46
|
|
64
|
-
match =
|
47
|
+
match = File.parse("[0-9] {\n def value\n text.to_i\n end\n}\n", :root => :rule_body)
|
65
48
|
assert(match)
|
66
49
|
assert_kind_of(Rule, match.value)
|
67
|
-
assert_instance_of(
|
50
|
+
assert_instance_of(Terminal, match.value)
|
51
|
+
end
|
68
52
|
|
69
|
-
|
53
|
+
def test_rule_body_string_terminal
|
54
|
+
match = File.parse('""', :root => :rule_body)
|
70
55
|
assert(match)
|
71
56
|
assert_kind_of(Rule, match.value)
|
72
|
-
assert_instance_of(
|
57
|
+
assert_instance_of(StringTerminal, match.value)
|
73
58
|
|
74
|
-
match =
|
59
|
+
match = File.parse('"a"', :root => :rule_body)
|
75
60
|
assert(match)
|
76
61
|
assert_kind_of(Rule, match.value)
|
77
|
-
assert_instance_of(
|
62
|
+
assert_instance_of(StringTerminal, match.value)
|
78
63
|
|
79
|
-
match =
|
64
|
+
match = File.parse('"" {}', :root => :rule_body)
|
80
65
|
assert(match)
|
81
66
|
assert_kind_of(Rule, match.value)
|
82
|
-
assert_instance_of(
|
67
|
+
assert_instance_of(StringTerminal, match.value)
|
68
|
+
end
|
83
69
|
|
84
|
-
|
70
|
+
def test_rule_body_repeat
|
71
|
+
match = File.parse('"a"*', :root => :rule_body)
|
85
72
|
assert(match)
|
86
73
|
assert_kind_of(Rule, match.value)
|
87
|
-
assert_instance_of(
|
88
|
-
assert_equal(
|
74
|
+
assert_instance_of(Repeat, match.value)
|
75
|
+
assert_equal(0, match.value.min)
|
76
|
+
assert_equal(Infinity, match.value.max)
|
89
77
|
|
90
|
-
match =
|
78
|
+
match = File.parse('""* {}', :root => :rule_body)
|
91
79
|
assert(match)
|
92
80
|
assert_kind_of(Rule, match.value)
|
93
|
-
assert_instance_of(
|
94
|
-
assert_equal(1, match.find(:bar).length)
|
95
|
-
assert_equal(2, match.find(:regular_expression).length)
|
96
|
-
assert_equal(0, match.find(:dot).length)
|
81
|
+
assert_instance_of(Repeat, match.value)
|
97
82
|
|
98
|
-
match =
|
83
|
+
match = File.parse('("a" "b")*', :root => :rule_body)
|
99
84
|
assert(match)
|
100
85
|
assert_kind_of(Rule, match.value)
|
101
|
-
assert_instance_of(
|
86
|
+
assert_instance_of(Repeat, match.value)
|
102
87
|
|
103
|
-
match =
|
88
|
+
match = File.parse('("a" | "b")*', :root => :rule_body)
|
104
89
|
assert(match)
|
105
90
|
assert_kind_of(Rule, match.value)
|
106
91
|
assert_instance_of(Repeat, match.value)
|
107
92
|
|
108
|
-
match =
|
93
|
+
match = File.parse('("a" "b")* {}', :root => :rule_body)
|
109
94
|
assert(match)
|
110
95
|
assert_kind_of(Rule, match.value)
|
111
96
|
assert_instance_of(Repeat, match.value)
|
112
|
-
assert_equal(0, match.value.min)
|
113
|
-
assert_equal(Infinity, match.value.max)
|
114
97
|
|
115
|
-
match =
|
98
|
+
match = File.parse('("a" | "b")* {}', :root => :rule_body)
|
116
99
|
assert(match)
|
117
100
|
assert_kind_of(Rule, match.value)
|
118
101
|
assert_instance_of(Repeat, match.value)
|
119
102
|
|
120
|
-
match =
|
103
|
+
match = File.parse('("a" "b")* <Module>', :root => :rule_body)
|
121
104
|
assert(match)
|
122
105
|
assert_kind_of(Rule, match.value)
|
123
|
-
assert_instance_of(
|
106
|
+
assert_instance_of(Repeat, match.value)
|
124
107
|
|
125
|
-
match =
|
108
|
+
match = File.parse('( "a" "b" )* <Module>', :root => :rule_body)
|
126
109
|
assert(match)
|
127
110
|
assert_kind_of(Rule, match.value)
|
128
111
|
assert_instance_of(Repeat, match.value)
|
129
112
|
|
130
|
-
match =
|
113
|
+
match = File.parse('("a" | "b")* <Module>', :root => :rule_body)
|
131
114
|
assert(match)
|
132
115
|
assert_kind_of(Rule, match.value)
|
133
116
|
assert_instance_of(Repeat, match.value)
|
134
117
|
|
135
|
-
match =
|
118
|
+
match = File.parse("[0-9]+ {\n def value\n text.to_i\n end\n}\n", :root => :rule_body)
|
136
119
|
assert(match)
|
137
120
|
assert_kind_of(Rule, match.value)
|
138
121
|
assert_instance_of(Repeat, match.value)
|
122
|
+
end
|
139
123
|
|
140
|
-
|
124
|
+
def test_rule_body_choice
|
125
|
+
match = File.parse('"a" | "b"', :root => :rule_body)
|
141
126
|
assert(match)
|
142
127
|
assert_kind_of(Rule, match.value)
|
143
128
|
assert_instance_of(Choice, match.value)
|
144
129
|
|
145
|
-
match =
|
130
|
+
match = File.parse('/./ | /./', :root => :rule_body)
|
146
131
|
assert(match)
|
147
132
|
assert_kind_of(Rule, match.value)
|
148
|
-
assert_instance_of(
|
133
|
+
assert_instance_of(Choice, match.value)
|
134
|
+
assert_equal(1, match.find(:bar).length)
|
135
|
+
assert_equal(2, match.find(:regular_expression).length)
|
136
|
+
assert_equal(0, match.find(:dot).length)
|
149
137
|
|
150
|
-
match =
|
138
|
+
match = File.parse('("a" | /./)', :root => :rule_body)
|
151
139
|
assert(match)
|
152
140
|
assert_kind_of(Rule, match.value)
|
153
|
-
assert_instance_of(
|
141
|
+
assert_instance_of(Choice, match.value)
|
154
142
|
|
155
|
-
match =
|
143
|
+
match = File.parse('("a" | "b") <Module>', :root => :rule_body)
|
156
144
|
assert(match)
|
157
145
|
assert_kind_of(Rule, match.value)
|
158
|
-
assert_instance_of(
|
146
|
+
assert_instance_of(Choice, match.value)
|
147
|
+
end
|
159
148
|
|
160
|
-
|
149
|
+
def test_rule_body_sequence
|
150
|
+
match = File.parse('"a" "b"', :root => :rule_body)
|
161
151
|
assert(match)
|
162
152
|
assert_kind_of(Rule, match.value)
|
163
153
|
assert_instance_of(Sequence, match.value)
|
164
154
|
|
165
|
-
match =
|
166
|
-
assert(match)
|
167
|
-
assert_kind_of(Rule, match.value)
|
168
|
-
assert_instance_of(Repeat, match.value)
|
169
|
-
|
170
|
-
match = grammar.parse('("a" | "b") <Module>')
|
155
|
+
match = File.parse('/./ /./', :root => :rule_body)
|
171
156
|
assert(match)
|
172
157
|
assert_kind_of(Rule, match.value)
|
173
|
-
assert_instance_of(
|
158
|
+
assert_instance_of(Sequence, match.value)
|
159
|
+
assert_equal(2, match.find(:regular_expression).length)
|
174
160
|
|
175
|
-
match =
|
161
|
+
match = File.parse('( "a" "b" ) <Module>', :root => :rule_body)
|
176
162
|
assert(match)
|
177
163
|
assert_kind_of(Rule, match.value)
|
178
164
|
assert_instance_of(Sequence, match.value)
|
179
165
|
|
180
|
-
match =
|
166
|
+
match = File.parse('"a" ("b" | /./)* <Module>', :root => :rule_body)
|
181
167
|
assert(match)
|
182
168
|
assert_kind_of(Rule, match.value)
|
183
|
-
assert_instance_of(
|
169
|
+
assert_instance_of(Sequence, match.value)
|
184
170
|
|
185
|
-
match =
|
171
|
+
match = File.parse('"a" ("b" | /./)* {}', :root => :rule_body)
|
186
172
|
assert(match)
|
187
173
|
assert_kind_of(Rule, match.value)
|
188
|
-
assert_instance_of(
|
174
|
+
assert_instance_of(Sequence, match.value)
|
189
175
|
end
|
190
176
|
|
191
177
|
def test_precedence
|
192
|
-
grammar = file(:rule_body)
|
193
|
-
|
194
178
|
# Sequence should bind more tightly than Choice.
|
195
|
-
match =
|
179
|
+
match = File.parse('"a" "b" | "c"', :root => :rule_body)
|
196
180
|
assert(match)
|
197
181
|
assert_kind_of(Rule, match.value)
|
198
182
|
assert_instance_of(Choice, match.value)
|
199
183
|
|
200
184
|
# Parentheses should change binding precedence.
|
201
|
-
match =
|
185
|
+
match = File.parse('"a" ("b" | "c")', :root => :rule_body)
|
202
186
|
assert(match)
|
203
187
|
assert_kind_of(Rule, match.value)
|
204
188
|
assert_instance_of(Sequence, match.value)
|
205
189
|
|
206
190
|
# Repeat should bind more tightly than AndPredicate.
|
207
|
-
match =
|
191
|
+
match = File.parse("&'a'+", :root => :rule_body)
|
208
192
|
assert(match)
|
209
193
|
assert_kind_of(Rule, match.value)
|
210
194
|
assert_instance_of(AndPredicate, match.value)
|
211
195
|
end
|
212
196
|
|
213
|
-
def
|
214
|
-
|
215
|
-
|
216
|
-
match = grammar.parse('')
|
197
|
+
def test_rule_body_empty
|
198
|
+
match = File.parse('', :root => :rule_body)
|
217
199
|
assert(match)
|
218
200
|
end
|
219
201
|
|
220
202
|
def test_choice
|
221
|
-
|
222
|
-
|
223
|
-
match = grammar.parse('"a" | "b"')
|
203
|
+
match = File.parse('"a" | "b"', :root => :choice)
|
224
204
|
assert(match)
|
225
205
|
assert_equal(2, match.rules.length)
|
226
206
|
assert_instance_of(Choice, match.value)
|
207
|
+
end
|
227
208
|
|
228
|
-
|
209
|
+
def test_choice_embedded_sequence
|
210
|
+
match = File.parse('"a" | ("b" "c")', :root => :choice)
|
229
211
|
assert(match)
|
230
212
|
assert_equal(2, match.rules.length)
|
231
213
|
assert_instance_of(Choice, match.value)
|
232
214
|
end
|
233
215
|
|
234
216
|
def test_sequence
|
235
|
-
|
236
|
-
|
237
|
-
match = grammar.parse('"" ""')
|
217
|
+
match = File.parse('"" ""', :root => :sequence)
|
238
218
|
assert(match)
|
239
219
|
assert_equal(2, match.rules.length)
|
240
220
|
assert_instance_of(Sequence, match.value)
|
221
|
+
end
|
241
222
|
|
242
|
-
|
223
|
+
def test_sequence_embedded_choice
|
224
|
+
match = File.parse('"a" ("b" | "c")', :root => :sequence)
|
243
225
|
assert(match)
|
244
|
-
assert_equal(
|
226
|
+
assert_equal(2, match.rules.length)
|
245
227
|
assert_instance_of(Sequence, match.value)
|
246
228
|
end
|
247
229
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
251
|
-
match = grammar.parse('"" <Module>')
|
230
|
+
def test_expression_tag
|
231
|
+
match = File.parse('"" <Module>', :root => :expression)
|
252
232
|
assert(match)
|
253
233
|
assert_kind_of(Rule, match.value)
|
254
234
|
assert_kind_of(Module, match.value.extension)
|
235
|
+
end
|
255
236
|
|
256
|
-
|
237
|
+
def test_expression_block
|
238
|
+
match = File.parse('"" {}', :root => :expression)
|
257
239
|
assert(match)
|
258
240
|
assert_kind_of(Rule, match.value)
|
259
241
|
assert_kind_of(Module, match.value.extension)
|
242
|
+
end
|
260
243
|
|
261
|
-
|
244
|
+
def test_expression_block_space
|
245
|
+
match = File.parse('"" {} ', :root => :expression)
|
262
246
|
assert(match)
|
263
247
|
assert_kind_of(Rule, match.value)
|
264
248
|
assert_kind_of(Module, match.value.extension)
|
265
249
|
end
|
266
250
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
match = grammar.parse('&""')
|
251
|
+
def test_prefix_and
|
252
|
+
match = File.parse('&""', :root => :prefix)
|
271
253
|
assert(match)
|
272
254
|
assert_kind_of(Rule, match.value)
|
273
255
|
assert_instance_of(AndPredicate, match.value)
|
256
|
+
end
|
274
257
|
|
275
|
-
|
258
|
+
def test_prefix_not
|
259
|
+
match = File.parse('!""', :root => :prefix)
|
276
260
|
assert(match)
|
277
261
|
assert_kind_of(Rule, match.value)
|
278
262
|
assert_instance_of(NotPredicate, match.value)
|
263
|
+
end
|
279
264
|
|
280
|
-
|
265
|
+
def test_prefix_label
|
266
|
+
match = File.parse('label:""', :root => :prefix)
|
281
267
|
assert(match)
|
282
268
|
assert_kind_of(Rule, match.value)
|
283
269
|
assert_instance_of(Label, match.value)
|
270
|
+
end
|
284
271
|
|
285
|
-
|
272
|
+
def test_prefix_label_space
|
273
|
+
match = File.parse('label :"" ', :root => :prefix)
|
286
274
|
assert(match)
|
287
275
|
assert_kind_of(Rule, match.value)
|
288
276
|
assert_instance_of(Label, match.value)
|
289
277
|
end
|
290
278
|
|
291
|
-
def
|
292
|
-
|
279
|
+
def test_suffix_plus
|
280
|
+
match = File.parse('""+', :root => :suffix)
|
281
|
+
assert(match)
|
282
|
+
assert_kind_of(Rule, match.value)
|
283
|
+
assert_instance_of(Repeat, match.value)
|
284
|
+
end
|
293
285
|
|
294
|
-
|
286
|
+
def test_suffix_question
|
287
|
+
match = File.parse('""? ', :root => :suffix)
|
295
288
|
assert(match)
|
296
289
|
assert_kind_of(Rule, match.value)
|
297
290
|
assert_instance_of(Repeat, match.value)
|
291
|
+
end
|
298
292
|
|
299
|
-
|
293
|
+
def test_suffix_star
|
294
|
+
match = File.parse('""*', :root => :suffix)
|
300
295
|
assert(match)
|
301
296
|
assert_kind_of(Rule, match.value)
|
302
297
|
assert_instance_of(Repeat, match.value)
|
298
|
+
end
|
303
299
|
|
304
|
-
|
300
|
+
def test_suffix_n_star
|
301
|
+
match = File.parse('""1*', :root => :suffix)
|
305
302
|
assert(match)
|
306
303
|
assert_kind_of(Rule, match.value)
|
307
304
|
assert_instance_of(Repeat, match.value)
|
308
305
|
end
|
309
306
|
|
310
|
-
def
|
311
|
-
|
307
|
+
def test_suffix_star_n
|
308
|
+
match = File.parse('""*2', :root => :suffix)
|
309
|
+
assert(match)
|
310
|
+
assert_kind_of(Rule, match.value)
|
311
|
+
assert_instance_of(Repeat, match.value)
|
312
|
+
end
|
312
313
|
|
313
|
-
|
314
|
+
def test_suffix_n_star_n
|
315
|
+
match = File.parse('""1*2', :root => :suffix)
|
316
|
+
assert(match)
|
317
|
+
assert_kind_of(Rule, match.value)
|
318
|
+
assert_instance_of(Repeat, match.value)
|
319
|
+
end
|
320
|
+
|
321
|
+
def test_primary_alias
|
322
|
+
match = File.parse('rule_name', :root => :primary)
|
314
323
|
assert(match)
|
315
324
|
assert_kind_of(Rule, match.value)
|
316
325
|
assert_instance_of(Alias, match.value)
|
326
|
+
end
|
317
327
|
|
318
|
-
|
328
|
+
def test_primary_string_terminal
|
329
|
+
match = File.parse('"a"', :root => :primary)
|
319
330
|
assert(match)
|
320
331
|
assert_kind_of(Rule, match.value)
|
321
|
-
assert_instance_of(
|
332
|
+
assert_instance_of(StringTerminal, match.value)
|
322
333
|
end
|
323
334
|
|
324
335
|
|
@@ -326,223 +337,279 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
326
337
|
|
327
338
|
|
328
339
|
def test_require
|
329
|
-
|
330
|
-
|
331
|
-
match = grammar.parse('require "some/file"')
|
340
|
+
match = File.parse('require "some/file"', :root => :require)
|
332
341
|
assert(match)
|
333
342
|
assert_equal('some/file', match.value)
|
343
|
+
end
|
334
344
|
|
335
|
-
|
345
|
+
def test_require_no_space
|
346
|
+
match = File.parse('require"some/file"', :root => :require)
|
336
347
|
assert(match)
|
337
348
|
assert_equal('some/file', match.value)
|
349
|
+
end
|
338
350
|
|
339
|
-
|
351
|
+
def test_require_single_quoted
|
352
|
+
match = File.parse("require 'some/file'", :root => :require)
|
340
353
|
assert(match)
|
341
354
|
assert_equal('some/file', match.value)
|
342
355
|
end
|
343
356
|
|
344
357
|
def test_include
|
345
|
-
|
346
|
-
|
347
|
-
match = grammar.parse('include Module')
|
358
|
+
match = File.parse('include Module', :root => :include)
|
348
359
|
assert(match)
|
349
360
|
assert_equal(Module, match.value)
|
361
|
+
end
|
350
362
|
|
351
|
-
|
363
|
+
def test_include_colon_colon
|
364
|
+
match = File.parse('include ::Module', :root => :include)
|
352
365
|
assert(match)
|
353
366
|
assert_equal(Module, match.value)
|
354
367
|
end
|
355
368
|
|
356
369
|
def test_root
|
357
|
-
|
358
|
-
|
359
|
-
match = grammar.parse('root some_rule')
|
370
|
+
match = File.parse('root some_rule', :root => :root)
|
360
371
|
assert(match)
|
361
372
|
assert_equal('some_rule', match.value)
|
373
|
+
end
|
362
374
|
|
375
|
+
def test_root_invalid
|
363
376
|
assert_raise ParseError do
|
364
|
-
|
377
|
+
File.parse('root :a_root', :root => :root)
|
365
378
|
end
|
366
379
|
end
|
367
380
|
|
368
381
|
def test_rule_name
|
369
|
-
|
370
|
-
|
371
|
-
match = grammar.parse('some_rule')
|
382
|
+
match = File.parse('some_rule', :root => :rule_name)
|
372
383
|
assert(match)
|
373
384
|
assert('some_rule', match.value)
|
385
|
+
end
|
374
386
|
|
375
|
-
|
387
|
+
def test_rule_name_space
|
388
|
+
match = File.parse('some_rule ', :root => :rule_name)
|
376
389
|
assert(match)
|
377
390
|
assert('some_rule', match.value)
|
378
391
|
end
|
379
392
|
|
380
|
-
def
|
381
|
-
|
382
|
-
|
383
|
-
match = grammar.parse('"a"')
|
393
|
+
def test_terminal_character_class
|
394
|
+
match = File.parse('[a-z]', :root => :terminal)
|
384
395
|
assert(match)
|
385
396
|
assert_kind_of(Rule, match.value)
|
386
397
|
assert_instance_of(Terminal, match.value)
|
387
|
-
|
398
|
+
end
|
388
399
|
|
389
|
-
|
400
|
+
def test_terminal_dot
|
401
|
+
match = File.parse('.', :root => :terminal)
|
390
402
|
assert(match)
|
391
403
|
assert_kind_of(Rule, match.value)
|
392
404
|
assert_instance_of(Terminal, match.value)
|
393
|
-
|
405
|
+
end
|
394
406
|
|
395
|
-
|
407
|
+
def test_terminal_regular_expression
|
408
|
+
match = File.parse('/./', :root => :terminal)
|
396
409
|
assert(match)
|
397
410
|
assert_kind_of(Rule, match.value)
|
398
411
|
assert_instance_of(Terminal, match.value)
|
399
|
-
|
412
|
+
end
|
400
413
|
|
401
|
-
|
414
|
+
def test_string_terminal_single_quoted
|
415
|
+
match = File.parse("'a'", :root => :string_terminal)
|
402
416
|
assert(match)
|
403
417
|
assert_kind_of(Rule, match.value)
|
404
|
-
assert_instance_of(
|
405
|
-
assert(match.value.terminal?)
|
418
|
+
assert_instance_of(StringTerminal, match.value)
|
406
419
|
end
|
407
420
|
|
408
|
-
def
|
409
|
-
|
421
|
+
def test_string_terminal_double_quoted
|
422
|
+
match = File.parse('"a"', :root => :string_terminal)
|
423
|
+
assert(match)
|
424
|
+
assert_kind_of(Rule, match.value)
|
425
|
+
assert_instance_of(StringTerminal, match.value)
|
426
|
+
end
|
427
|
+
|
428
|
+
def test_string_terminal_case_insensitive
|
429
|
+
match = File.parse('`a`', :root => :string_terminal)
|
430
|
+
assert(match)
|
431
|
+
assert_kind_of(Rule, match.value)
|
432
|
+
assert_instance_of(StringTerminal, match.value)
|
433
|
+
end
|
410
434
|
|
411
|
-
|
435
|
+
def test_single_quoted_string
|
436
|
+
match = File.parse("'test'", :root => :quoted_string)
|
412
437
|
assert(match)
|
413
438
|
assert_equal('test', match.value)
|
439
|
+
end
|
414
440
|
|
415
|
-
|
441
|
+
def test_single_quoted_string_embedded_single_quote
|
442
|
+
match = File.parse("'te\\'st'", :root => :quoted_string)
|
416
443
|
assert(match)
|
417
444
|
assert_equal("te'st", match.value)
|
445
|
+
end
|
418
446
|
|
419
|
-
|
447
|
+
def test_single_quoted_string_embedded_double_quote
|
448
|
+
match = File.parse("'te\"st'", :root => :quoted_string)
|
420
449
|
assert(match)
|
421
450
|
assert_equal('te"st', match.value)
|
422
451
|
end
|
423
452
|
|
424
453
|
def test_double_quoted_string
|
425
|
-
|
426
|
-
|
427
|
-
match = grammar.parse('"test"')
|
454
|
+
match = File.parse('"test"', :root => :quoted_string)
|
428
455
|
assert(match)
|
429
456
|
assert_equal('test', match.value)
|
457
|
+
end
|
430
458
|
|
431
|
-
|
459
|
+
def test_double_quoted_string_embedded_double_quote
|
460
|
+
match = File.parse('"te\\"st"', :root => :quoted_string)
|
432
461
|
assert(match)
|
433
462
|
assert_equal('te"st', match.value)
|
463
|
+
end
|
434
464
|
|
435
|
-
|
465
|
+
def test_double_quoted_string_embedded_single_quote
|
466
|
+
match = File.parse('"te\'st"', :root => :quoted_string)
|
436
467
|
assert(match)
|
437
468
|
assert_equal("te'st", match.value)
|
469
|
+
end
|
438
470
|
|
439
|
-
|
471
|
+
def test_double_quoted_string_hex
|
472
|
+
match = File.parse('"\\x26"', :root => :quoted_string)
|
440
473
|
assert(match)
|
441
474
|
assert_equal('&', match.value)
|
442
475
|
end
|
443
476
|
|
444
|
-
def
|
445
|
-
|
477
|
+
def test_case_insensitive_string
|
478
|
+
match = File.parse('`test`', :root => :case_insensitive_string)
|
479
|
+
assert(match)
|
480
|
+
assert_equal('test', match.value)
|
481
|
+
end
|
482
|
+
|
483
|
+
def test_case_insensitive_string_embedded_double_quote
|
484
|
+
match = File.parse('`te\\"st`', :root => :case_insensitive_string)
|
485
|
+
assert(match)
|
486
|
+
assert_equal('te"st', match.value)
|
487
|
+
end
|
446
488
|
|
447
|
-
|
489
|
+
def test_case_insensitive_string_embedded_backtick
|
490
|
+
match = File.parse('`te\`st`', :root => :case_insensitive_string)
|
491
|
+
assert(match)
|
492
|
+
assert_equal("te`st", match.value)
|
493
|
+
end
|
494
|
+
|
495
|
+
def test_case_insensitive_string_hex
|
496
|
+
match = File.parse('`\\x26`', :root => :case_insensitive_string)
|
497
|
+
assert(match)
|
498
|
+
assert_equal('&', match.value)
|
499
|
+
end
|
500
|
+
|
501
|
+
def test_character_class
|
502
|
+
match = File.parse('[_]', :root => :character_class)
|
448
503
|
assert(match)
|
449
504
|
assert_equal(/\A[_]/, match.value)
|
505
|
+
end
|
450
506
|
|
451
|
-
|
507
|
+
def test_character_class_a_z
|
508
|
+
match = File.parse('[a-z]', :root => :character_class)
|
452
509
|
assert(match)
|
453
510
|
assert_equal(/\A[a-z]/, match.value)
|
511
|
+
end
|
454
512
|
|
455
|
-
|
513
|
+
def test_character_class_a_z_0_9
|
514
|
+
match = File.parse('[a-z0-9]', :root => :character_class)
|
456
515
|
assert(match)
|
457
516
|
assert_equal(/\A[a-z0-9]/, match.value)
|
517
|
+
end
|
458
518
|
|
459
|
-
|
519
|
+
def test_character_class_nested_square_brackets
|
520
|
+
match = File.parse('[\[-\]]', :root => :character_class)
|
460
521
|
assert(match)
|
461
522
|
assert_equal(/\A[\[-\]]/, match.value)
|
523
|
+
end
|
462
524
|
|
463
|
-
|
525
|
+
def test_character_class_hex_range
|
526
|
+
match = File.parse('[\\x26-\\x29]', :root => :character_class)
|
464
527
|
assert(match)
|
465
528
|
assert_equal(/\A[\x26-\x29]/, match.value)
|
466
529
|
end
|
467
530
|
|
468
531
|
def test_dot
|
469
|
-
|
470
|
-
|
471
|
-
match = grammar.parse('.')
|
532
|
+
match = File.parse('.', :root => :dot)
|
472
533
|
assert(match)
|
473
534
|
assert_equal(DOT, match.value)
|
474
535
|
end
|
475
536
|
|
476
537
|
def test_regular_expression
|
477
|
-
|
478
|
-
|
479
|
-
match = grammar.parse('/./')
|
538
|
+
match = File.parse('/./', :root => :regular_expression)
|
480
539
|
assert(match)
|
481
540
|
assert_equal(/./, match.value)
|
541
|
+
end
|
482
542
|
|
483
|
-
|
543
|
+
def test_regular_expression_escaped_forward_slash
|
544
|
+
match = File.parse('/\\//', :root => :regular_expression)
|
484
545
|
assert(match)
|
485
546
|
assert_equal(/\//, match.value)
|
547
|
+
end
|
486
548
|
|
487
|
-
|
549
|
+
def test_regular_expression_escaped_backslash
|
550
|
+
match = File.parse('/\\\\/', :root => :regular_expression)
|
488
551
|
assert(match)
|
489
552
|
assert_equal(/\\/, match.value)
|
553
|
+
end
|
490
554
|
|
491
|
-
|
555
|
+
def test_regular_expression_hex
|
556
|
+
match = File.parse('/\\x26/', :root => :regular_expression)
|
492
557
|
assert(match)
|
493
558
|
assert_equal(/\x26/, match.value)
|
559
|
+
end
|
494
560
|
|
495
|
-
|
561
|
+
def test_regular_expression_with_flag
|
562
|
+
match = File.parse('/a/i', :root => :regular_expression)
|
496
563
|
assert(match)
|
497
564
|
assert_equal(/a/i, match.value)
|
498
565
|
end
|
499
566
|
|
500
|
-
def
|
501
|
-
|
502
|
-
|
503
|
-
match = grammar.parse('&')
|
567
|
+
def test_predicate_and
|
568
|
+
match = File.parse('&', :root => :predicate)
|
504
569
|
assert(match)
|
505
570
|
assert_kind_of(Rule, match.value(''))
|
571
|
+
end
|
506
572
|
|
507
|
-
|
573
|
+
def test_predicate_not
|
574
|
+
match = File.parse('!', :root => :predicate)
|
508
575
|
assert(match)
|
509
576
|
assert_kind_of(Rule, match.value(''))
|
510
577
|
end
|
511
578
|
|
512
579
|
def test_and
|
513
|
-
|
514
|
-
|
515
|
-
match = grammar.parse('&')
|
580
|
+
match = File.parse('&', :root => :and)
|
516
581
|
assert(match)
|
517
582
|
assert_instance_of(AndPredicate, match.value(''))
|
583
|
+
end
|
518
584
|
|
519
|
-
|
585
|
+
def test_and_space
|
586
|
+
match = File.parse('& ', :root => :and)
|
520
587
|
assert(match)
|
521
588
|
assert_instance_of(AndPredicate, match.value(''))
|
522
589
|
end
|
523
590
|
|
524
591
|
def test_not
|
525
|
-
|
526
|
-
|
527
|
-
match = grammar.parse('!')
|
592
|
+
match = File.parse('!', :root => :not)
|
528
593
|
assert(match)
|
529
594
|
assert_instance_of(NotPredicate, match.value(''))
|
595
|
+
end
|
530
596
|
|
531
|
-
|
597
|
+
def test_not_space
|
598
|
+
match = File.parse('! ', :root => :not)
|
532
599
|
assert(match)
|
533
600
|
assert_instance_of(NotPredicate, match.value(''))
|
534
601
|
end
|
535
602
|
|
536
603
|
def test_label
|
537
|
-
|
538
|
-
|
539
|
-
match = grammar.parse('label:')
|
604
|
+
match = File.parse('label:', :root => :label)
|
540
605
|
assert(match)
|
541
606
|
v = match.value('')
|
542
607
|
assert_instance_of(Label, v)
|
543
608
|
assert_equal(:label, v.label)
|
609
|
+
end
|
544
610
|
|
545
|
-
|
611
|
+
def test_label_spaced
|
612
|
+
match = File.parse('a_label : ', :root => :label)
|
546
613
|
assert(match)
|
547
614
|
v = match.value('')
|
548
615
|
assert_instance_of(Label, v)
|
@@ -550,43 +617,51 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
550
617
|
end
|
551
618
|
|
552
619
|
def test_tag
|
553
|
-
|
554
|
-
|
555
|
-
match = grammar.parse('<Module>')
|
620
|
+
match = File.parse('<Module>', :root => :tag)
|
556
621
|
assert(match)
|
557
622
|
assert_equal(Module, match.value)
|
623
|
+
end
|
558
624
|
|
559
|
-
|
625
|
+
def test_tag_inner_space
|
626
|
+
match = File.parse('< Module >', :root => :tag)
|
560
627
|
assert(match)
|
561
628
|
assert_equal(Module, match.value)
|
629
|
+
end
|
562
630
|
|
563
|
-
|
631
|
+
def test_tag_space
|
632
|
+
match = File.parse('<Module> ', :root => :tag)
|
564
633
|
assert(match)
|
565
634
|
assert_equal(Module, match.value)
|
566
635
|
end
|
567
636
|
|
568
637
|
def test_block
|
569
|
-
|
570
|
-
|
571
|
-
match = grammar.parse('{}')
|
638
|
+
match = File.parse('{}', :root => :block)
|
572
639
|
assert(match)
|
573
640
|
assert(match.value)
|
641
|
+
end
|
574
642
|
|
575
|
-
|
643
|
+
def test_block_space
|
644
|
+
match = File.parse("{} \n", :root => :block)
|
576
645
|
assert(match)
|
577
646
|
assert(match.value)
|
647
|
+
end
|
578
648
|
|
579
|
-
|
649
|
+
def test_block_n
|
650
|
+
match = File.parse('{ 2 }', :root => :block)
|
580
651
|
assert(match)
|
581
652
|
assert(match.value)
|
582
653
|
assert_equal(2, match.value.call)
|
654
|
+
end
|
583
655
|
|
584
|
-
|
656
|
+
def test_block_with_hash
|
657
|
+
match = File.parse("{ {:a => :b}\n}", :root => :block)
|
585
658
|
assert(match)
|
586
659
|
assert(match.value)
|
587
660
|
assert_equal({:a => :b}, match.value.call)
|
661
|
+
end
|
588
662
|
|
589
|
-
|
663
|
+
def test_block_proc
|
664
|
+
match = File.parse("{|b|\n Proc.new(&b)\n}", :root => :block)
|
590
665
|
assert(match)
|
591
666
|
assert(match.value)
|
592
667
|
|
@@ -594,121 +669,152 @@ class CitrusFileTest < Test::Unit::TestCase
|
|
594
669
|
|
595
670
|
assert(b)
|
596
671
|
assert_equal(:hi, b.call)
|
672
|
+
end
|
597
673
|
|
598
|
-
|
674
|
+
def test_block_def
|
675
|
+
match = File.parse("{\n def value\n 'a'\n end\n} ", :root => :block)
|
599
676
|
assert(match)
|
600
677
|
assert(match.value)
|
601
|
-
|
602
678
|
end
|
603
679
|
|
604
680
|
def test_block_with_interpolation
|
605
|
-
|
606
|
-
|
607
|
-
match = grammar.parse('{ "#{number}" }')
|
681
|
+
match = File.parse('{ "#{number}" }', :root => :block)
|
608
682
|
assert(match)
|
609
683
|
assert(match.value)
|
610
684
|
end
|
611
685
|
|
612
|
-
def
|
613
|
-
|
686
|
+
def test_repeat_question
|
687
|
+
match = File.parse('?', :root => :repeat)
|
688
|
+
assert(match)
|
689
|
+
assert_instance_of(Repeat, match.value(''))
|
690
|
+
end
|
691
|
+
|
692
|
+
def test_repeat_plus
|
693
|
+
match = File.parse('+', :root => :repeat)
|
694
|
+
assert(match)
|
695
|
+
assert_instance_of(Repeat, match.value(''))
|
696
|
+
end
|
614
697
|
|
615
|
-
|
698
|
+
def test_repeat_star
|
699
|
+
match = File.parse('*', :root => :repeat)
|
616
700
|
assert(match)
|
617
|
-
assert_instance_of(Repeat, match.
|
701
|
+
assert_instance_of(Repeat, match.value(''))
|
702
|
+
end
|
618
703
|
|
619
|
-
|
704
|
+
def test_repeat_n_star
|
705
|
+
match = File.parse('1*', :root => :repeat)
|
620
706
|
assert(match)
|
621
|
-
assert_instance_of(Repeat, match.
|
707
|
+
assert_instance_of(Repeat, match.value(''))
|
708
|
+
end
|
622
709
|
|
623
|
-
|
710
|
+
def test_repeat_star_n
|
711
|
+
match = File.parse('*2', :root => :repeat)
|
624
712
|
assert(match)
|
625
|
-
assert_instance_of(Repeat, match.
|
713
|
+
assert_instance_of(Repeat, match.value(''))
|
626
714
|
end
|
627
715
|
|
628
|
-
def
|
629
|
-
|
716
|
+
def test_repeat_n_star_n
|
717
|
+
match = File.parse('1*2', :root => :repeat)
|
718
|
+
assert(match)
|
719
|
+
assert_instance_of(Repeat, match.value(''))
|
720
|
+
end
|
630
721
|
|
631
|
-
|
722
|
+
def test_question
|
723
|
+
match = File.parse('?', :root => :question)
|
632
724
|
assert(match)
|
633
725
|
assert_equal(0, match.min)
|
634
726
|
assert_equal(1, match.max)
|
727
|
+
end
|
635
728
|
|
636
|
-
|
729
|
+
def test_question_space
|
730
|
+
match = File.parse('? ', :root => :question)
|
637
731
|
assert(match)
|
638
732
|
assert_equal(0, match.min)
|
639
733
|
assert_equal(1, match.max)
|
640
734
|
end
|
641
735
|
|
642
736
|
def test_plus
|
643
|
-
|
644
|
-
|
645
|
-
match = grammar.parse('+')
|
737
|
+
match = File.parse('+', :root => :plus)
|
646
738
|
assert(match)
|
647
739
|
assert_equal(1, match.min)
|
648
740
|
assert_equal(Infinity, match.max)
|
741
|
+
end
|
649
742
|
|
650
|
-
|
743
|
+
def test_plus_space
|
744
|
+
match = File.parse('+ ', :root => :plus)
|
651
745
|
assert(match)
|
652
746
|
assert_equal(1, match.min)
|
653
747
|
assert_equal(Infinity, match.max)
|
654
748
|
end
|
655
749
|
|
656
|
-
def
|
657
|
-
|
658
|
-
|
659
|
-
match = grammar.parse('*')
|
750
|
+
def test_star
|
751
|
+
match = File.parse('*', :root => :star)
|
660
752
|
assert(match)
|
661
753
|
assert_equal(0, match.min)
|
662
754
|
assert_equal(Infinity, match.max)
|
755
|
+
end
|
663
756
|
|
664
|
-
|
757
|
+
def test_n_star
|
758
|
+
match = File.parse('1*', :root => :star)
|
665
759
|
assert(match)
|
666
760
|
assert_equal(1, match.min)
|
667
761
|
assert_equal(Infinity, match.max)
|
762
|
+
end
|
668
763
|
|
669
|
-
|
764
|
+
def test_star_n
|
765
|
+
match = File.parse('*2', :root => :star)
|
670
766
|
assert(match)
|
671
767
|
assert_equal(0, match.min)
|
672
768
|
assert_equal(2, match.max)
|
769
|
+
end
|
673
770
|
|
674
|
-
|
771
|
+
def test_n_star_n
|
772
|
+
match = File.parse('1*2', :root => :star)
|
675
773
|
assert(match)
|
676
774
|
assert_equal(1, match.min)
|
677
775
|
assert_equal(2, match.max)
|
776
|
+
end
|
678
777
|
|
679
|
-
|
778
|
+
def test_n_star_n_space
|
779
|
+
match = File.parse('1*2 ', :root => :star)
|
680
780
|
assert(match)
|
681
781
|
assert_equal(1, match.min)
|
682
782
|
assert_equal(2, match.max)
|
683
783
|
end
|
684
784
|
|
685
785
|
def test_module_name
|
686
|
-
|
786
|
+
match = File.parse('Module', :root => :module_name)
|
787
|
+
assert(match)
|
788
|
+
assert_equal('Module', match)
|
789
|
+
end
|
687
790
|
|
688
|
-
|
791
|
+
def test_module_name_space
|
792
|
+
match = File.parse('Module ', :root => :module_name)
|
689
793
|
assert(match)
|
794
|
+
assert_equal('Module', match.first)
|
795
|
+
end
|
690
796
|
|
691
|
-
|
797
|
+
def test_module_name_colon_colon
|
798
|
+
match = File.parse('::Proc', :root => :module_name)
|
692
799
|
assert(match)
|
800
|
+
assert_equal('::Proc', match)
|
693
801
|
end
|
694
802
|
|
695
803
|
def test_constant
|
696
|
-
|
697
|
-
|
698
|
-
match = grammar.parse('Math')
|
804
|
+
match = File.parse('Math', :root => :constant)
|
699
805
|
assert(match)
|
806
|
+
assert_equal('Math', match)
|
807
|
+
end
|
700
808
|
|
809
|
+
def test_constant_invalid
|
701
810
|
assert_raise ParseError do
|
702
|
-
|
811
|
+
File.parse('math', :root => :constant)
|
703
812
|
end
|
704
813
|
end
|
705
814
|
|
706
815
|
def test_comment
|
707
|
-
|
708
|
-
|
709
|
-
match = grammar.parse('# A comment.')
|
816
|
+
match = File.parse('# A comment.', :root => :comment)
|
710
817
|
assert(match)
|
711
818
|
assert_equal('# A comment.', match)
|
712
819
|
end
|
713
|
-
|
714
820
|
end
|