dendroid 0.0.11 → 0.1.00

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.
@@ -8,6 +8,12 @@ describe Dendroid::Recognizer::Recognizer do
8
8
  include SampleGrammars
9
9
  let(:grammar1) { grammar_l1 }
10
10
 
11
+ def comp_expected_actuals(chart, expectations)
12
+ expectations.each_with_index do |set, rank|
13
+ expect(chart[rank].to_s).to eq(set.join("\n"))
14
+ end
15
+ end
16
+
11
17
  # Implements a dotted item: expression => NUMBER . PLUS NUMBER
12
18
  subject { described_class.new(grammar1, tokenizer_l1) }
13
19
 
@@ -44,7 +50,7 @@ describe Dendroid::Recognizer::Recognizer do
44
50
  't => INTEGER . @ 0',
45
51
  'm => t . @ 0',
46
52
  's => m . @ 0',
47
- #'m => m . STAR t @ 0',
53
+ # 'm => m . STAR t @ 0',
48
54
  'p => s . @ 0',
49
55
  's => s . PLUS m @ 0'
50
56
  ]
@@ -59,8 +65,8 @@ describe Dendroid::Recognizer::Recognizer do
59
65
  'm => t . @ 2',
60
66
  's => s PLUS m . @ 0',
61
67
  'm => m . STAR t @ 2',
62
- 'p => s . @ 0',
63
- # 's => s . PLUS m @ 0'
68
+ 'p => s . @ 0'
69
+ # 's => s . PLUS m @ 0'
64
70
  ]
65
71
  set4 = [ # 2 + 3 * . 4'
66
72
  'm => m STAR . t @ 2',
@@ -72,7 +78,7 @@ describe Dendroid::Recognizer::Recognizer do
72
78
  's => s PLUS m . @ 0',
73
79
  # 'm => m . STAR t @ 2',
74
80
  'p => s . @ 0'
75
- # 's => s . PLUS m @ 0'
81
+ # 's => s . PLUS m @ 0'
76
82
  ]
77
83
  [set0, set1, set2, set3, set4, set5].each_with_index do |set, rank|
78
84
  expect(chart[rank].to_s).to eq(set.join("\n"))
@@ -102,16 +108,16 @@ describe Dendroid::Recognizer::Recognizer do
102
108
  # 'product => product . STAR factor @ 0',
103
109
  # 'product => product . SLASH factor @ 0',
104
110
  'p => sum . @ 0',
105
- 'sum => sum . PLUS product @ 0',
106
- # 'sum => sum . MINUS product @ 0'
111
+ 'sum => sum . PLUS product @ 0'
112
+ # 'sum => sum . MINUS product @ 0'
107
113
  ]
108
114
  set2 = [ # 1 + . (2 * 3 - 4)
109
115
  'sum => sum PLUS . product @ 0',
110
116
  'product => . product STAR factor @ 2',
111
117
  'product => . product SLASH factor @ 2',
112
118
  'product => . factor @ 2',
113
- 'factor => . LPAREN sum RPAREN @ 2',
114
- # 'factor => . NUMBER @ 2'
119
+ 'factor => . LPAREN sum RPAREN @ 2'
120
+ # 'factor => . NUMBER @ 2'
115
121
  ]
116
122
  set3 = [ # 1 + (. 2 * 3 - 4)
117
123
  'factor => LPAREN . sum RPAREN @ 2',
@@ -128,11 +134,11 @@ describe Dendroid::Recognizer::Recognizer do
128
134
  'factor => NUMBER . @ 3',
129
135
  'product => factor . @ 3',
130
136
  'sum => product . @ 3',
131
- 'product => product . STAR factor @ 3',
132
- # 'product => product . SLASH factor @ 3',
133
- # 'factor => LPAREN sum . RPAREN @ 2',
134
- # 'sum => sum . PLUS product @ 3',
135
- # 'sum => sum . MINUS product @ 3'
137
+ 'product => product . STAR factor @ 3'
138
+ # 'product => product . SLASH factor @ 3',
139
+ # 'factor => LPAREN sum . RPAREN @ 2',
140
+ # 'sum => sum . PLUS product @ 3',
141
+ # 'sum => sum . MINUS product @ 3'
136
142
  ]
137
143
  set5 = [ # 1 + (2 * . 3 - 4)
138
144
  'product => product STAR . factor @ 3',
@@ -163,9 +169,9 @@ describe Dendroid::Recognizer::Recognizer do
163
169
  'sum => sum MINUS product . @ 3',
164
170
  # 'product => product . STAR factor @ 7',
165
171
  # 'product => product . SLASH factor @ 7',
166
- 'factor => LPAREN sum . RPAREN @ 2',
167
- # 'sum => sum . PLUS product @ 3',
168
- # 'sum => sum . MINUS product @ 3'
172
+ 'factor => LPAREN sum . RPAREN @ 2'
173
+ # 'sum => sum . PLUS product @ 3',
174
+ # 'sum => sum . MINUS product @ 3'
169
175
  ]
170
176
  set9 = [ # 1 + (2 * 3 - 4 ).
171
177
  'factor => LPAREN sum RPAREN . @ 2',
@@ -173,9 +179,9 @@ describe Dendroid::Recognizer::Recognizer do
173
179
  'sum => sum PLUS product . @ 0',
174
180
  # 'product => product . STAR factor @ 2',
175
181
  # 'product => product . SLASH factor @ 2',
176
- 'p => sum . @ 0',
177
- # 'sum => sum . PLUS product @ 0',
178
- # 'sum => sum . MINUS product @ 0'
182
+ 'p => sum . @ 0'
183
+ # 'sum => sum . PLUS product @ 0',
184
+ # 'sum => sum . MINUS product @ 0'
179
185
  ]
180
186
  expectations = [set0, set1, set2, set3, set4, set5, set6, set7, set8, set9]
181
187
  expectations.each_with_index do |set, rank|
@@ -183,4 +189,573 @@ describe Dendroid::Recognizer::Recognizer do
183
189
  end
184
190
  end
185
191
  end # context
192
+
193
+ context 'Handle empty rules' do
194
+ it 'can cope with an empty rule' do
195
+ recognizer = described_class.new(grammar_l7, tokenizer_l7)
196
+ chart = recognizer.run('a a')
197
+ expect(chart).to be_successful
198
+
199
+ set0 = [ # . a a
200
+ 'S => . S T @ 0',
201
+ 'S => . a @ 0'
202
+ ]
203
+ set1 = [ # a . a
204
+ 'S => a . @ 0',
205
+ 'S => S . T @ 0',
206
+ 'T => . a B @ 1',
207
+ 'T => . a @ 1'
208
+ ]
209
+ set2 = [ # a a .
210
+ 'T => a . B @ 1',
211
+ 'T => a . @ 1',
212
+ 'B => . @ 2',
213
+ 'T => a B . @ 1',
214
+ 'S => S T . @ 0',
215
+ 'S => S . T @ 0'
216
+ # 'T => . a B @ 2',
217
+ # 'T => . a @ 2'
218
+ ]
219
+
220
+ expectations = [set0, set1, set2]
221
+ comp_expected_actuals(chart, expectations)
222
+ end
223
+
224
+ it 'can cope with a nullable symbol' do
225
+ recognizer = described_class.new(grammar_l14, tokenizer_l14)
226
+ chart = recognizer.run('a a / a')
227
+ expect(chart).to be_successful
228
+
229
+ set0 = [ # . a a / a
230
+ 'S => . E @ 0',
231
+ 'E => . E Q F @ 0',
232
+ 'E => . F @ 0',
233
+ 'F => . a @ 0'
234
+ ]
235
+ set1 = [ # a . a / a
236
+ 'F => a . @ 0',
237
+ 'E => F . @ 0',
238
+ 'S => E . @ 0',
239
+ 'E => E . Q F @ 0',
240
+ # 'Q => . star @ 1',
241
+ # 'Q => . slash @ 1',
242
+ 'Q => . @ 1',
243
+ 'E => E Q . F @ 0',
244
+ 'F => . a @ 1'
245
+ ]
246
+ set2 = [ # a a . / a
247
+ 'F => a . @ 1',
248
+ 'E => E Q F . @ 0',
249
+ 'S => E . @ 0',
250
+ 'E => E . Q F @ 0',
251
+ # 'Q => . star @ 2',
252
+ 'Q => . slash @ 2',
253
+ 'Q => . @ 2',
254
+ 'E => E Q . F @ 0'
255
+ # 'F => . a @ 2'
256
+ ]
257
+ set3 = [ # a a . / a
258
+ 'Q => slash . @ 2',
259
+ 'E => E Q . F @ 0',
260
+ 'F => . a @ 3'
261
+ ]
262
+ set4 = [ # a a / . a
263
+ 'F => a . @ 3',
264
+ 'E => E Q F . @ 0',
265
+ 'S => E . @ 0',
266
+ 'E => E . Q F @ 0',
267
+ # 'Q => . star @ 4',
268
+ # 'Q => . slash @ 4',
269
+ 'Q => . @ 4',
270
+ 'E => E Q . F @ 0'
271
+ # 'F => . a @ 4'
272
+ ]
273
+ expectations = [set0, set1, set2, set3, set4]
274
+ comp_expected_actuals(chart, expectations)
275
+ end
276
+ end # context
277
+
278
+ context 'Recognizer and ambiguous grammars:' do
279
+ it 'can handle ambiguous input (I)' do
280
+ recognizer = described_class.new(grammar_l31, tokenizer_l1)
281
+ chart = recognizer.run('2 + 3 * 4')
282
+ expect(chart).to be_successful
283
+
284
+ set0 = [ # . 2 + 3 * 4
285
+ 'p => . s @ 0',
286
+ 's => . s PLUS s @ 0',
287
+ 's => . s STAR s @ 0',
288
+ 's => . INTEGER @ 0'
289
+ ]
290
+ set1 = [ # 2 . + 3 * 4
291
+ 's => INTEGER . @ 0',
292
+ 'p => s . @ 0',
293
+ 's => s . PLUS s @ 0'
294
+ # 's => s . STAR s @ 0',
295
+ ]
296
+ set2 = [ # 2 + . 3 * 4
297
+ 's => s PLUS . s @ 0',
298
+ 's => . s PLUS s @ 2',
299
+ 's => . s STAR s @ 2',
300
+ 's => . INTEGER @ 2'
301
+ ]
302
+ set3 = [ # 2 + 3 . * 4
303
+ 's => INTEGER . @ 2',
304
+ 's => s PLUS s . @ 0',
305
+ # 's => s . PLUS s @ 2',
306
+ 's => s . STAR s @ 2',
307
+ 'p => s . @ 0',
308
+ # 's => s . PLUS s @ 0',
309
+ 's => s . STAR s @ 0'
310
+ ]
311
+ set4 = [ # 2 + 3 * . 4
312
+ 's => s STAR . s @ 2',
313
+ 's => s STAR . s @ 0',
314
+ 's => . s PLUS s @ 4',
315
+ 's => . s STAR s @ 4',
316
+ 's => . INTEGER @ 4'
317
+ ]
318
+ set5 = [ # 2 + 3 * 4 .
319
+ 's => INTEGER . @ 4',
320
+ 's => s STAR s . @ 2',
321
+ 's => s STAR s . @ 0',
322
+ # 's => s . PLUS s @ 4',
323
+ # 's => s . STAR s @ 4',
324
+ 's => s PLUS s . @ 0',
325
+ # 's => s . PLUS s @ 2',
326
+ # 's => s . STAR s @ 2',
327
+ 'p => s . @ 0'
328
+ # 's => s . PLUS s @ 0',
329
+ # 's => s . STAR s @ 0'
330
+ ]
331
+ expectations = [set0, set1, set2, set3, set4, set5]
332
+ comp_expected_actuals(chart, expectations)
333
+ end
334
+
335
+ it 'can handle ambiguous input (II)' do
336
+ recognizer = described_class.new(grammar_l4, tokenizer_l4)
337
+ chart = recognizer.run('abc + def + ghi')
338
+ expect(chart).to be_successful
339
+
340
+ set0 = [ # . abc + def + ghi
341
+ 'S => . E @ 0',
342
+ 'E => . E plus E @ 0',
343
+ 'E => . id @ 0'
344
+ ]
345
+ set1 = [ # abc . + def + ghi
346
+ 'E => id . @ 0',
347
+ 'S => E . @ 0',
348
+ 'E => E . plus E @ 0'
349
+ ]
350
+ set2 = [ # abc + . def + ghi
351
+ 'E => E plus . E @ 0',
352
+ 'E => . E plus E @ 2',
353
+ 'E => . id @ 2'
354
+ ]
355
+ set3 = [ # abc + def . + ghi
356
+ 'E => id . @ 2',
357
+ 'E => E plus E . @ 0',
358
+ 'E => E . plus E @ 2',
359
+ 'S => E . @ 0',
360
+ 'E => E . plus E @ 0'
361
+
362
+ ]
363
+ set4 = [ # abc + def + . ghi
364
+ 'E => E plus . E @ 2',
365
+ 'E => E plus . E @ 0',
366
+ 'E => . E plus E @ 4',
367
+ 'E => . id @ 4'
368
+ ]
369
+ set5 = [ # abc + def + ghi .
370
+ 'E => id . @ 4',
371
+ 'E => E plus E . @ 2',
372
+ 'E => E plus E . @ 0',
373
+ # 'E => E . plus E @ 4',
374
+ # 'E => E . plus E @ 2',
375
+ 'S => E . @ 0'
376
+ # 'E => E . plus E @ 0'
377
+ ]
378
+ expectations = [set0, set1, set2, set3, set4, set5]
379
+ comp_expected_actuals(chart, expectations)
380
+ end
381
+
382
+ it 'copes with the dangling else ambiguity' do
383
+ recognizer = described_class.new(grammar_l6, tokenizer_l6)
384
+ chart = recognizer.run('if E then if E then other else other')
385
+ expect(chart).to be_successful
386
+ end
387
+
388
+ it 'swallows an input that failed with the Earley parsing approach' do
389
+ recognizer = described_class.new(grammar_l8, tokenizer_l8)
390
+ chart = recognizer.run('x x x')
391
+ expect(chart).to be_successful
392
+
393
+ set0 = [ # . x x x
394
+ 'S => . S S @ 0',
395
+ 'S => . x @ 0'
396
+ ]
397
+ set1 = [ # x . x x
398
+ 'S => x . @ 0',
399
+ 'S => S . S @ 0',
400
+ 'S => . S S @ 1',
401
+ 'S => . x @ 1'
402
+ ]
403
+ set2 = [ # x x . x
404
+ 'S => x . @ 1',
405
+ 'S => S S . @ 0',
406
+ 'S => S . S @ 1',
407
+ 'S => S . S @ 0',
408
+ 'S => . S S @ 2',
409
+ 'S => . x @ 2'
410
+ ]
411
+ set3 = [ # x x x .
412
+ 'S => x . @ 2',
413
+ 'S => S S . @ 1',
414
+ 'S => S S . @ 0',
415
+ 'S => S . S @ 2',
416
+ 'S => S . S @ 1',
417
+ 'S => S . S @ 0',
418
+ 'S => . S S @ 3'
419
+ # 'S => . x @ 3'
420
+ ]
421
+ expectations = [set0, set1, set2, set3]
422
+ comp_expected_actuals(chart, expectations)
423
+ end
424
+
425
+ it 'swallows the input from an infinite ambiguity grammar' do
426
+ recognizer = described_class.new(grammar_l9, tokenizer_l9)
427
+ chart = recognizer.run('x x x')
428
+ expect(chart).to be_successful
429
+
430
+ set0 = [ # . x x x
431
+ 'S => . S S @ 0',
432
+ 'S => . @ 0',
433
+ 'S => . x @ 0',
434
+ 'S => S . S @ 0',
435
+ 'S => S S . @ 0'
436
+ ]
437
+ set1 = [ # x . x x
438
+ 'S => x . @ 0',
439
+ 'S => S . S @ 0',
440
+ 'S => S S . @ 0',
441
+ 'S => . S S @ 1',
442
+ 'S => . @ 1',
443
+ 'S => . x @ 1',
444
+ 'S => S . S @ 1'
445
+ ]
446
+ set2 = [ # x x . x
447
+ 'S => x . @ 1',
448
+ 'S => S S . @ 0',
449
+ 'S => S . S @ 1',
450
+ 'S => S S . @ 1',
451
+ 'S => S . S @ 0',
452
+ 'S => . S S @ 2',
453
+ 'S => . @ 2',
454
+ 'S => . x @ 2',
455
+ 'S => S . S @ 2'
456
+ ]
457
+ set3 = [ # x x x .
458
+ 'S => x . @ 2',
459
+ 'S => S S . @ 1',
460
+ 'S => S S . @ 0',
461
+ 'S => S . S @ 2',
462
+ 'S => S S . @ 2',
463
+ 'S => S . S @ 1',
464
+ 'S => S . S @ 0',
465
+ 'S => . S S @ 3',
466
+ 'S => . @ 3',
467
+ # 'S => . x @ 3',
468
+ 'S => S . S @ 3'
469
+ ]
470
+ expectations = [set0, set1, set2, set3]
471
+ comp_expected_actuals(chart, expectations)
472
+ end
473
+ end # context
474
+
475
+ context 'Recognizer and recursive rules:' do
476
+ it 'can handle left-recursion' do
477
+ recognizer = described_class.new(grammar_l10, tokenizer_l10)
478
+ chart = recognizer.run('a a a a a')
479
+ expect(chart).to be_successful
480
+
481
+ set0 = [ # . a a a a a
482
+ 'A => . A a @ 0',
483
+ 'A => . @ 0',
484
+ 'A => A . a @ 0'
485
+ ]
486
+ set1 = [ # a . a a a a
487
+ 'A => A a . @ 0',
488
+ 'A => A . a @ 0'
489
+ ]
490
+ set2 = [ # a a . a a a
491
+ 'A => A a . @ 0',
492
+ 'A => A . a @ 0'
493
+ ]
494
+ set3 = [ # a a a . a a
495
+ 'A => A a . @ 0',
496
+ 'A => A . a @ 0'
497
+ ]
498
+ set4 = [ # a a a a . a
499
+ 'A => A a . @ 0',
500
+ 'A => A . a @ 0'
501
+ ]
502
+ set5 = [ # a a a a a .
503
+ 'A => A a . @ 0'
504
+ # 'A => A . a @ 0'
505
+ ]
506
+ expectations = [set0, set1, set2, set3, set4, set5]
507
+ comp_expected_actuals(chart, expectations)
508
+ end
509
+
510
+ it 'supports right-recursive rules' do
511
+ recognizer = described_class.new(grammar_l11, tokenizer_l11)
512
+ chart = recognizer.run('a a a a a')
513
+ expect(chart).to be_successful
514
+ set0 = [ # . a a a a a
515
+ 'A => . a A @ 0',
516
+ 'A => . @ 0'
517
+ ]
518
+ set1 = [ # a . a a a a
519
+ 'A => a . A @ 0',
520
+ 'A => . a A @ 1',
521
+ 'A => . @ 1',
522
+ 'A => a A . @ 0'
523
+ ]
524
+ set2 = [ # a a . a a a
525
+ 'A => a . A @ 1',
526
+ 'A => . a A @ 2',
527
+ 'A => . @ 2',
528
+ 'A => a A . @ 1',
529
+ 'A => a A . @ 0'
530
+ ]
531
+ set3 = [ # a a a . a a
532
+ 'A => a . A @ 2',
533
+ 'A => . a A @ 3',
534
+ 'A => . @ 3',
535
+ 'A => a A . @ 2',
536
+ 'A => a A . @ 1',
537
+ 'A => a A . @ 0'
538
+ ]
539
+ set4 = [ # a a a a . a
540
+ 'A => a . A @ 3',
541
+ 'A => . a A @ 4',
542
+ 'A => . @ 4',
543
+ 'A => a A . @ 3',
544
+ 'A => a A . @ 2',
545
+ 'A => a A . @ 1',
546
+ 'A => a A . @ 0'
547
+ ]
548
+ set5 = [ # a a a a a .
549
+ 'A => a . A @ 4',
550
+ # 'A => . a A @ 5',
551
+ 'A => . @ 5',
552
+ 'A => a A . @ 4',
553
+ 'A => a A . @ 3',
554
+ 'A => a A . @ 2',
555
+ 'A => a A . @ 1',
556
+ 'A => a A . @ 0'
557
+ ]
558
+ expectations = [set0, set1, set2, set3, set4, set5]
559
+ comp_expected_actuals(chart, expectations)
560
+ end
561
+
562
+ it 'supports mid-recursive rules' do
563
+ recognizer = described_class.new(grammar_l5, tokenizer_l5)
564
+ chart = recognizer.run('a a b c c')
565
+ expect(chart).to be_successful
566
+ set0 = [ # . a a b c c
567
+ 'S => . A @ 0',
568
+ 'A => . a A c @ 0'
569
+ # 'A => . b @ 0'
570
+ ]
571
+ set1 = [ # a . a b c c
572
+ 'A => a . A c @ 0',
573
+ 'A => . a A c @ 1'
574
+ # 'A => . b @ 1'
575
+ ]
576
+ set2 = [ # a a . b c c
577
+ 'A => a . A c @ 1',
578
+ # 'A => . a A c @ 2',
579
+ 'A => . b @ 2'
580
+ ]
581
+ set3 = [ # a a b . c c
582
+ 'A => b . @ 2',
583
+ 'A => a A . c @ 1'
584
+ ]
585
+ set4 = [ # a a b c . c
586
+ 'A => a A c . @ 1',
587
+ 'A => a A . c @ 0'
588
+ ]
589
+ set5 = [ # a a b c c .
590
+ 'A => a A c . @ 0',
591
+ 'S => A . @ 0'
592
+ ]
593
+ expectations = [set0, set1, set2, set3, set4, set5]
594
+ comp_expected_actuals(chart, expectations)
595
+ end
596
+
597
+ it 'can handle hidden left-recursion' do
598
+ recognizer = described_class.new(grammar_l12, tokenizer_l12)
599
+ chart = recognizer.run('a b b b')
600
+ expect(chart).to be_successful
601
+
602
+ set0 = [ # . a b b b
603
+ 'S => . A T @ 0',
604
+ 'S => . a T @ 0',
605
+ 'A => . a @ 0',
606
+ 'A => . B A @ 0',
607
+ 'B => . @ 0',
608
+ 'A => B . A @ 0'
609
+ ]
610
+ set1 = [ # a . b b b
611
+ 'S => a . T @ 0',
612
+ 'A => a . @ 0',
613
+ 'T => . b b b @ 1',
614
+ 'S => A . T @ 0',
615
+ 'A => B A . @ 0'
616
+ ]
617
+ set2 = [ # a b . b b
618
+ 'T => b . b b @ 1'
619
+ ]
620
+ set3 = [ # a b b . b
621
+ 'T => b b . b @ 1'
622
+ ]
623
+ set4 = [ # a b b b .
624
+ 'T => b b b . @ 1',
625
+ 'S => a T . @ 0',
626
+ 'S => A T . @ 0'
627
+ ]
628
+ expectations = [set0, set1, set2, set3, set4]
629
+ comp_expected_actuals(chart, expectations)
630
+ end
631
+
632
+ it 'can handle right-recursion (II)' do
633
+ recognizer = described_class.new(grammar_l13, tokenizer_l13)
634
+ chart = recognizer.run('x x x')
635
+ expect(chart).to be_successful
636
+ set0 = [ # . x x x
637
+ 'A => . x A @ 0',
638
+ 'A => . x @ 0'
639
+ ]
640
+ set1 = [ # x . x x
641
+ 'A => x . A @ 0',
642
+ 'A => x . @ 0',
643
+ 'A => . x A @ 1',
644
+ 'A => . x @ 1'
645
+ ]
646
+ set2 = [ # x x . x
647
+ 'A => x . A @ 1',
648
+ 'A => x . @ 1',
649
+ 'A => . x A @ 2',
650
+ 'A => . x @ 2',
651
+ 'A => x A . @ 0'
652
+ ]
653
+ set3 = [ # x x x .
654
+ 'A => x . A @ 2',
655
+ 'A => x . @ 2',
656
+ # 'A => . x A @ 3',
657
+ # 'A => . x @ 3',
658
+ 'A => x A . @ 1',
659
+ 'A => x A . @ 0'
660
+ ]
661
+ expectations = [set0, set1, set2, set3]
662
+ comp_expected_actuals(chart, expectations)
663
+ end
664
+
665
+ # TODO: Use grammars from "The Structure of Shared Forests in Ambiguous Parsing"
666
+ # Grammar UBDA == grammar_l8
667
+ # Grammar RR == grammar_l13
668
+ end # context
669
+
670
+ context 'Error reporting:' do
671
+ it 'should parse an invalid simple input' do
672
+ recognizer = described_class.new(grammar_l5, tokenizer_l5)
673
+ # Parse an erroneous input (b is missing)
674
+ chart = recognizer.run('a a c c')
675
+ expect(chart).not_to be_successful
676
+
677
+ # TODO
678
+ # err_msg = <<-MSG
679
+ # Syntax error at or near token line 1, column 5 >>>c<<<
680
+ # Expected one of: ['a', 'b'], found a 'c' instead.
681
+ # MSG
682
+ # expect(parse_result.failure_reason.message).to eq(err_msg.chomp)
683
+ end
684
+ end # context
685
+
686
+ context 'Error at start of input' do
687
+ it 'raises an error if input is empty and grammar disallows this' do
688
+ err_msg = 'Error: Input may not be empty nor blank.'
689
+ recognizer = described_class.new(grammar_l5, tokenizer_l5)
690
+
691
+ ['', " \t \n"].each do |input|
692
+ chart = recognizer.run(input)
693
+ expect(chart).not_to be_successful
694
+ expect(chart.failure_class).to eq(StandardError)
695
+ expect(chart.failure_reason).to eq(err_msg)
696
+ end
697
+ end
698
+
699
+ it 'raises an error if encounters an unexpected token' do
700
+ recognizer = described_class.new(grammar_l5, tokenizer_l5)
701
+ chart = recognizer.run('a a c c')
702
+ expect(chart).not_to be_successful
703
+ set0 = [ # . a a c c
704
+ 'S => . A @ 0',
705
+ 'A => . a A c @ 0'
706
+ # 'A => . b @ 0'
707
+ ]
708
+ set1 = [ # a . a c c
709
+ 'A => a . A c @ 0',
710
+ 'A => . a A c @ 1'
711
+ # 'A => . b @ 1'
712
+ ]
713
+ set2 = [ # a a . c c
714
+ 'A => a . A c @ 1',
715
+ 'A => . a A c @ 2', # State is not pruned (in error state)
716
+ 'A => . b @ 2' # State is not pruned (in error state)
717
+ ]
718
+ [set0, set1, set2].each_with_index do |set, rank|
719
+ expect(chart[rank].to_s).to eq(set.join("\n"))
720
+ end
721
+ expect(chart.failure_class).to eq(StandardError)
722
+ err_msg = 'Syntax error at or near token line 1, column 5 >>>c<<< Expected one of: [a, b], found a c instead.'
723
+ expect(chart.failure_reason).to eq(err_msg)
724
+ end
725
+
726
+ it "reports an error when last token isn't final state" do
727
+ recognizer = described_class.new(grammar_l5, tokenizer_l5)
728
+ chart = recognizer.run('aabc')
729
+ expect(chart).not_to be_successful
730
+ set0 = [ # . a a b c
731
+ 'S => . A @ 0',
732
+ 'A => . a A c @ 0'
733
+ # 'A => . b @ 0'
734
+ ]
735
+ set1 = [ # a . a b c
736
+ 'A => a . A c @ 0',
737
+ 'A => . a A c @ 1'
738
+ # 'A => . b @ 1'
739
+ ]
740
+ set2 = [ # a a . b c
741
+ 'A => a . A c @ 1',
742
+ # 'A => . a A c @ 2',
743
+ 'A => . b @ 2'
744
+ ]
745
+ set3 = [ # a a b . c
746
+ 'A => b . @ 2',
747
+ 'A => a A . c @ 1'
748
+ ]
749
+ set4 = [ # a a b c .
750
+ 'A => a A c . @ 1',
751
+ 'A => a A . c @ 0'
752
+ ]
753
+ [set0, set1, set2, set3, set4].each_with_index do |set, rank|
754
+ expect(chart[rank].to_s).to eq(set.join("\n"))
755
+ end
756
+ expect(chart.failure_class).to eq(StandardError)
757
+ err_msg = "Line 1, column 4: Premature end of input after 'c', expected: c."
758
+ expect(chart.failure_reason).to eq(err_msg)
759
+ end
760
+ end # context
186
761
  end # describe