kpeg 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,81 @@
1
+ require 'test/unit'
2
+ require 'kpeg'
3
+ require 'kpeg/compiled_parser'
4
+ require 'stringio'
5
+
6
+ class TestKPegCompiledParser < Test::Unit::TestCase
7
+
8
+ gram = <<-GRAM
9
+ letter = [a-z]
10
+ root = letter
11
+ GRAM
12
+
13
+ KPeg.compile gram, "TestParser", self
14
+
15
+ gram = <<-GRAM
16
+ %test = TestKPegCompiledParser::TestParser
17
+ root = %test.letter "!"
18
+ GRAM
19
+
20
+ KPeg.compile gram, "CompTestParser", self
21
+
22
+ def test_current_column
23
+ r = TestParser.new "hello\nsir"
24
+ assert_equal 2, r.current_column(1)
25
+ assert_equal 6, r.current_column(5)
26
+ assert_equal 1, r.current_column(7)
27
+ assert_equal 4, r.current_column(10)
28
+ end
29
+
30
+ def test_failed_rule
31
+ r = TestParser.new "9"
32
+ assert !r.parse, "shouldn't parse"
33
+
34
+ assert_equal :_letter, r.failed_rule
35
+ end
36
+
37
+ def test_failure_info
38
+ r = TestParser.new "9"
39
+ assert !r.parse, "shouldn't parse"
40
+
41
+ expected = "line 1, column 1: failed rule 'letter' = '[a-z]'"
42
+ assert_equal 0, r.failing_rule_offset
43
+ assert_equal expected, r.failure_info
44
+ end
45
+
46
+ def test_failure_caret
47
+ r = TestParser.new "9"
48
+ assert !r.parse, "shouldn't parse"
49
+
50
+ assert_equal "9\n^", r.failure_caret
51
+ end
52
+
53
+ def test_failure_character
54
+ r = TestParser.new "9"
55
+ assert !r.parse, "shouldn't parse"
56
+
57
+ assert_equal "9", r.failure_character
58
+ end
59
+
60
+ def test_failure_oneline
61
+ r = TestParser.new "9"
62
+ assert !r.parse, "shouldn't parse"
63
+
64
+ expected = "@1:1 failed rule 'letter', got '9'"
65
+ assert_equal expected, r.failure_oneline
66
+ end
67
+
68
+ def test_composite_grammar
69
+ r = CompTestParser.new "l!"
70
+ assert r.parse, "should parse"
71
+ end
72
+
73
+ def test_composite_grammar_failure
74
+ r = CompTestParser.new "9"
75
+ assert !r.parse, "should parse"
76
+
77
+ expected = "@1:1 failed rule 'TestKPegCompiledParser::TestParser#_letter', got '9'"
78
+ assert_equal expected, r.failure_oneline
79
+ end
80
+
81
+ end
@@ -0,0 +1,467 @@
1
+ require 'test/unit'
2
+ require 'kpeg'
3
+ require 'kpeg/format_parser'
4
+ require 'kpeg/grammar_renderer'
5
+ require 'stringio'
6
+ require 'rubygems'
7
+
8
+ class TestKPegFormat < Test::Unit::TestCase
9
+ G = KPeg::Grammar.new
10
+
11
+ gram = File.read File.expand_path("../../lib/kpeg/format.kpeg", __FILE__)
12
+ KPeg.compile gram, "TestParser", self
13
+
14
+ def match(str, gram=nil, log=false)
15
+ parc = TestParser.new str
16
+ parc.raise_error unless parc.parse
17
+
18
+ return parc.grammar
19
+ end
20
+
21
+ def assert_rule(expect, gram, name="a")
22
+ actual = gram.find name.to_s
23
+ assert_equal expect, actual.op
24
+ end
25
+
26
+ def test_assignment
27
+ assert_rule G.ref("b"), match("a=b"), "a"
28
+ end
29
+
30
+ def test_invoke
31
+ assert_rule G.invoke("b"), match("a=@b"), "a"
32
+ end
33
+
34
+ def test_assignment_hyphen_only
35
+ assert_rule G.ref("b"), match("-=b"), "-"
36
+ end
37
+
38
+ def test_assigment_sp
39
+ assert_rule G.ref("b"), match(" a=b")
40
+ assert_rule G.ref("b"), match(" a =b")
41
+ assert_rule G.ref("b"), match(" a = b")
42
+ assert_rule G.ref("b"), match(" a = b ")
43
+ end
44
+
45
+ def test_assign_with_arg
46
+ gram = match("a(t) = b")
47
+ rule = gram.find "a"
48
+ assert_equal ["t"], rule.arguments
49
+ end
50
+
51
+ def test_assign_with_arg_disambiguated_from_grouping
52
+ str = <<-STR
53
+ a = c
54
+ b(p) = x
55
+ STR
56
+
57
+ gram = match(str)
58
+ end
59
+
60
+ def test_assign_with_multiple_args
61
+ gram = match("a(t,x) = b")
62
+ rule = gram.find "a"
63
+ assert_equal ["t", "x"], rule.arguments
64
+ end
65
+
66
+ def test_assign_with_args_spacing
67
+ gram = match("a( t) = b")
68
+ rule = gram.find "a"
69
+ assert_equal ["t"], rule.arguments
70
+
71
+ gram = match("a( t ) = b")
72
+ rule = gram.find "a"
73
+ assert_equal ["t"], rule.arguments
74
+
75
+ gram = match("a( t,x) = b")
76
+ rule = gram.find "a"
77
+ assert_equal ["t", "x"], rule.arguments
78
+
79
+ gram = match("a( t,x ) = b")
80
+ rule = gram.find "a"
81
+ assert_equal ["t", "x"], rule.arguments
82
+
83
+ gram = match("a( t ,x ) = b")
84
+ rule = gram.find "a"
85
+ assert_equal ["t", "x"], rule.arguments
86
+
87
+ gram = match("a( t , x ) = b")
88
+ rule = gram.find "a"
89
+ assert_equal ["t", "x"], rule.arguments
90
+ end
91
+
92
+ def test_invoke_with_arg
93
+ gram = match("a=b(1)")
94
+ rule = gram.find "a"
95
+ assert_equal "(1)", rule.op.arguments
96
+ end
97
+
98
+ def test_invoke_with_multiple_args
99
+ assert_rule G.invoke("b", "(1,2)"), match("a=b(1,2)"), "a"
100
+ end
101
+
102
+ def test_invoke_foreign_rule
103
+ assert_rule G.foreign_invoke("blah", "letters"),
104
+ match("a=%blah.letters"), "a"
105
+ end
106
+
107
+ def test_add_foreign_grammar
108
+ gram = match "%blah = OtherGrammar"
109
+ assert_equal "OtherGrammar", gram.foreign_grammars["blah"]
110
+ end
111
+
112
+ def test_invoke_parent_rule
113
+ assert_rule G.foreign_invoke("parent", "letters"),
114
+ match("a=^letters"), "a"
115
+ end
116
+
117
+ def test_dot
118
+ assert_rule G.dot, match("a=.")
119
+ end
120
+
121
+ def test_string
122
+ assert_rule G.str("hello"), match('a="hello"')
123
+ assert_rule G.str("h\"ello"), match('a="h\"ello"')
124
+ end
125
+
126
+ def test_regexp
127
+ assert_rule G.reg(/foo/), match('a=/foo/')
128
+ assert_rule G.reg(/foo\/bar/), match('a=/foo\/bar/')
129
+ assert_rule G.reg(/[^"]/), match('a=/[^"]/')
130
+ end
131
+
132
+ def test_regexp_options
133
+ assert_rule G.reg(/foo/u), match('a=/foo/u')
134
+ end
135
+
136
+ def test_char_range
137
+ assert_rule G.range("a", "z"), match('a=[a-z]')
138
+ end
139
+
140
+ def test_maybe
141
+ assert_rule G.maybe(:b), match('a=b?')
142
+ end
143
+
144
+ def test_many
145
+ assert_rule G.many(:b), match('a=b+')
146
+ end
147
+
148
+ def test_many_sequence
149
+ assert_rule G.many([:b, :c]), match('a=(b c)+')
150
+ end
151
+
152
+ def test_many_sequence_with_action
153
+ assert_rule G.seq(G.many([:b, :c]), G.action(" 1 ")),
154
+ match('a=(b c)+ { 1 }')
155
+ end
156
+
157
+ def test_kleene
158
+ assert_rule G.kleene(:b), match('a=b*')
159
+ end
160
+
161
+ def test_arbitrary_multiple
162
+ assert_rule G.multiple(:b, 5, 9), match('a=b[5,9]')
163
+ end
164
+
165
+ def test_no_max_multiple
166
+ assert_rule G.multiple(:b, 5, nil), match('a=b[5,*]')
167
+ end
168
+
169
+ def test_no_max_multiple_sp
170
+ assert_rule G.multiple(:b, 5, nil), match('a=b[5, *]')
171
+ assert_rule G.multiple(:b, 5, nil), match('a=b[5, * ]')
172
+ assert_rule G.multiple(:b, 5, nil), match('a=b[5 , * ]')
173
+ assert_rule G.multiple(:b, 5, nil), match('a=b[ 5 , * ]')
174
+ end
175
+
176
+ def test_andp
177
+ assert_rule G.andp(:c), match('a=&c')
178
+ end
179
+
180
+ def test_notp
181
+ assert_rule G.notp(:c), match('a=!c')
182
+ end
183
+
184
+ def test_choice
185
+ assert_rule G.any(:b, :c), match('a=b|c')
186
+ end
187
+
188
+ def test_choice_seq_priority
189
+ assert_rule G.any([:num, :b], :c), match('a=num b|c')
190
+ end
191
+
192
+ def test_choice_sp
193
+ m = match 'a=num "+" dig | dig'
194
+ expected = G.any([:num, "+", :dig], :dig)
195
+ assert_rule expected, m
196
+ end
197
+
198
+ def test_choice_sp2
199
+ str = <<-STR
200
+ Stmt = - Expr:e EOL
201
+ | ( !EOL . )* EOL
202
+ STR
203
+ m = match str
204
+ expected = G.any(
205
+ [:"-", G.t(:Expr, "e"), :EOL],
206
+ [G.kleene([G.notp(:EOL), G.dot]), :EOL])
207
+
208
+ assert_rule expected, m, "Stmt"
209
+ end
210
+
211
+ def test_choice_with_actions
212
+ str = <<-STR
213
+ Stmt = - Expr:e EOL { p e }
214
+ | ( !EOL . )* EOL { puts "error" }
215
+ STR
216
+ m = match str
217
+ expected = G.any(
218
+ [:"-", G.t(:Expr, "e"), :EOL, G.action(" p e ")],
219
+ [G.kleene([G.notp(:EOL), G.dot]), :EOL,
220
+ G.action(" puts \"error\" ")])
221
+
222
+ assert_rule expected, m, "Stmt"
223
+ end
224
+
225
+ def test_multiline_seq
226
+ str = <<-STR
227
+ Sum = Product:l
228
+ ( PLUS Product:r { l += r }
229
+ | MINUS Product:r { l -= r }
230
+ )* { l }
231
+ STR
232
+ m = match str
233
+ expected = G.seq(
234
+ G.t(:Product, "l"),
235
+ G.kleene(
236
+ G.any(
237
+ [:PLUS, G.t(:Product, "r"), G.action(" l += r ")],
238
+ [:MINUS, G.t(:Product, "r"), G.action(" l -= r ")]
239
+ )),
240
+ G.action(" l "))
241
+
242
+ assert_rule expected, m, "Sum"
243
+ end
244
+
245
+ def test_multiline_seq2
246
+ str = <<-STR
247
+ Value = NUMBER:i { i }
248
+ | ID:i !ASSIGN { vars[i] }
249
+ | OPEN Expr:i CLOSE { i }
250
+ STR
251
+ m = match(str)
252
+ end
253
+
254
+ def test_seq
255
+ m = match 'a=b c'
256
+ assert_rule G.seq(:b, :c), m
257
+
258
+ m = match 'a=b c d'
259
+ assert_rule G.seq(:b, :c, :d), m
260
+
261
+ m = match 'a=b c d e f'
262
+ assert_rule G.seq(:b, :c, :d, :e, :f), m
263
+ end
264
+
265
+ def test_tag
266
+ m = match 'a=b:x'
267
+ assert_rule G.t(:b, "x"), m
268
+ end
269
+
270
+ def test_tag_parens
271
+ m = match 'a=(b c):x'
272
+ assert_rule G.t([:b, :c], "x"), m
273
+ end
274
+
275
+ def test_tag_priority
276
+ m = match 'a=d (b c):x'
277
+ assert_rule G.seq(:d, G.t([:b, :c], "x")), m
278
+
279
+ m = match 'a=d c*:x'
280
+ assert_rule G.seq(:d, G.t(G.kleene(:c), "x")), m
281
+ end
282
+
283
+ def test_parens
284
+ m = match 'a=(b c)'
285
+ assert_rule G.seq(:b, :c), m
286
+ end
287
+
288
+ def test_parens_sp
289
+ m = match 'a=( b c )'
290
+ assert_rule G.seq(:b, :c), m
291
+ end
292
+
293
+ def test_parens_as_outer
294
+ m = match 'a=b (c|d)'
295
+ assert_rule G.seq(:b, G.any(:c, :d)), m
296
+ end
297
+
298
+ def test_action
299
+ m = match 'a=b c { b + c }'
300
+ assert_rule G.seq(:b, :c, G.action(" b + c ")), m
301
+ end
302
+
303
+ def test_action_nested_curly
304
+ m = match 'a=b c { b + { c + d } }'
305
+ assert_rule G.seq(:b, :c, G.action(" b + { c + d } ")), m
306
+ end
307
+
308
+ def test_collect
309
+ m = match 'a = < b c >'
310
+ assert_rule G.collect(G.seq(:b, :c)), m
311
+ end
312
+
313
+ def test_comment
314
+ m = match "a=b # this is a comment\n"
315
+ assert_rule G.ref('b'), m
316
+ end
317
+
318
+ def test_comment_span
319
+ m = match "a=b # this is a comment\n c"
320
+ assert_rule G.seq(G.ref('b'), G.ref("c")), m
321
+ end
322
+
323
+ def test_parser_setup
324
+ m = match "%% { def initialize; end }\na=b"
325
+ assert_rule G.ref("b"), m
326
+ assert_equal " def initialize; end ", m.setup_actions.first.action
327
+ end
328
+
329
+ def test_parser_name
330
+ m = match "%%name = BlahParser"
331
+ assert_equal "BlahParser", m.variables["name"]
332
+ end
333
+
334
+ def test_multiple_rules
335
+ m = match "a=b\nc=d\ne=f"
336
+ assert_rule G.ref("b"), m, "a"
337
+ assert_rule G.ref("d"), m, "c"
338
+ assert_rule G.ref("f"), m, "e"
339
+ end
340
+
341
+ def test_multiline_choice
342
+ gram = <<-GRAM
343
+ expr = num "+" num
344
+ | num "-" num
345
+ GRAM
346
+
347
+ m = match gram
348
+ expected = G.seq(:num, "+", :num) |
349
+ G.seq(:num, "-", :num)
350
+ assert_rule expected, m, "expr"
351
+ end
352
+
353
+ def test_multiline_choice_many2
354
+ gram = <<-GRAM
355
+ term = term "+" fact
356
+ | term "-" fact
357
+ | fact
358
+ fact = fact "*" num
359
+ | fact "/" num
360
+ | num
361
+ GRAM
362
+
363
+ m = match gram
364
+ term = G.any([:term, "+", :fact],
365
+ [:term, "-", :fact],
366
+ :fact)
367
+ fact = G.any([:fact, "*", :num],
368
+ [:fact, "/", :num],
369
+ :num)
370
+
371
+ assert_equal term, m.find("term").op
372
+ assert_equal fact, m.find("fact").op
373
+ end
374
+
375
+ def test_multiline_choice_many
376
+ gram = <<-GRAM
377
+ term = term "+" fact
378
+ | term "-" fact
379
+ fact = fact "*" num
380
+ | fact "/" num
381
+ GRAM
382
+
383
+ m = match gram
384
+ term = G.any([:term, "+", :fact],
385
+ [:term, "-", :fact])
386
+ fact = G.any([:fact, "*", :num],
387
+ [:fact, "/", :num])
388
+
389
+ assert_equal term, m.find("term").op
390
+ assert_equal fact, m.find("fact").op
391
+ end
392
+
393
+ def make_parser(str, gram, debug=false)
394
+ cg = KPeg::CodeGenerator.new "Test", gram, debug
395
+ inst = cg.make(str)
396
+ return inst
397
+ end
398
+
399
+ def test_roundtrip
400
+ path = File.expand_path("../../lib/kpeg/format.kpeg", __FILE__)
401
+ parser = KPeg::FormatParser.new File.read(path)
402
+ assert parser.parse, "Unable to parse"
403
+
404
+ start = parser.grammar
405
+
406
+ gr = KPeg::GrammarRenderer.new(start)
407
+ io = StringIO.new
408
+ gr.render(io)
409
+
410
+ scan = make_parser io.string, start
411
+ unless scan.parse
412
+ puts io.string
413
+ scan.show_error
414
+ assert !scan.failed?, "parsing the grammar"
415
+ end
416
+
417
+ g2 = scan.grammar
418
+
419
+ gr2 = KPeg::GrammarRenderer.new(g2)
420
+ io2 = StringIO.new
421
+ gr2.render(io2)
422
+
423
+ unless io.string == io2.string
424
+ require 'tempfile'
425
+
426
+ Tempfile.open "diff" do |f1|
427
+ f1 << io.string
428
+ f1.close
429
+
430
+ Tempfile.open "diff" do |f2|
431
+ f2 << io2.string
432
+ f2.close
433
+
434
+ system "diff -u #{f1.path} #{f2.path}"
435
+ end
436
+ end
437
+ end
438
+
439
+ assert_equal io.string, io2.string
440
+
441
+ # Go for a 3rd generation!
442
+ scan2 = make_parser io2.string, g2
443
+ assert scan2.parse, "parsing the grammar"
444
+
445
+ g3 = scan2.grammar
446
+
447
+ unless g3.rules.empty?
448
+ gr3 = KPeg::GrammarRenderer.new(g3)
449
+ io3 = StringIO.new
450
+ gr3.render(io3)
451
+
452
+ assert_equal io2.string, io3.string
453
+
454
+ # INCEPTION! 4! go for 4!
455
+ scan3 = make_parser io3.string, g3
456
+ assert scan3.parse, "parsing the grammar"
457
+
458
+ g4 = scan3.grammar
459
+
460
+ gr4 = KPeg::GrammarRenderer.new(g4)
461
+ io4 = StringIO.new
462
+ gr4.render(io4)
463
+
464
+ assert_equal io3.string, io4.string
465
+ end
466
+ end
467
+ end