kpeg 0.7

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.
@@ -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