aboutsummaryrefslogtreecommitdiffstats
path: root/MenhirLib/Interpreter_complete.v
blob: 51f2524b7fb45f4508f87c94013430a01eae6cb6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
(****************************************************************************)
(*                                                                          *)
(*                                   Menhir                                 *)
(*                                                                          *)
(*           Jacques-Henri Jourdan, CNRS, LRI, Université Paris Sud         *)
(*                                                                          *)
(*  Copyright Inria. All rights reserved. This file is distributed under    *)
(*  the terms of the GNU Lesser General Public License as published by the  *)
(*  Free Software Foundation, either version 3 of the License, or (at your  *)
(*  option) any later version, as described in the file LICENSE.            *)
(*                                                                          *)
(****************************************************************************)

From Coq Require Import List Arith.
Import ListNotations.
From Coq.ssr Require Import ssreflect.
Require Import Alphabet Grammar.
Require Automaton Interpreter Validator_complete.

Module Make(Import A:Automaton.T) (Import Inter:Interpreter.T A).
Module Import Valid := Validator_complete.Make A.

(** * Completeness Proof **)

Section Completeness_Proof.

Hypothesis safe: Inter.ValidSafe.safe.
Hypothesis complete: complete.

(* Properties of the automaton deduced from completeness validation. *)
Proposition nullable_stable: nullable_stable.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition first_stable: first_stable.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition start_future: start_future.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition terminal_shift: terminal_shift.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition end_reduce: end_reduce.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition start_goto: start_goto.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition non_terminal_goto: non_terminal_goto.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.
Proposition non_terminal_closed: non_terminal_closed.
Proof. pose proof complete; unfold Valid.complete in H; intuition. Qed.

(** If the nullable predicate has been validated, then it is correct. **)
Lemma nullable_correct head word :
  word = [] -> parse_tree head word -> nullable_symb head = true
with nullable_correct_list heads word :
  word = [] ->
  parse_tree_list heads word -> nullable_word heads = true.
Proof.
  - destruct 2=>//. assert (Hnull := nullable_stable prod).
    erewrite nullable_correct_list in Hnull; eauto.
  - intros Hword. destruct 1=>//=. destruct (app_eq_nil _ _ Hword).
    eauto using andb_true_intro.
Qed.

(** Auxiliary lemma for first_correct.  *)
Lemma first_word_set_app t word1 word2 :
  TerminalSet.In t (first_word_set (word1 ++ word2)) <->
  TerminalSet.In t (first_word_set word1) \/
  TerminalSet.In t (first_word_set word2) /\ nullable_word (rev word1) = true.
Proof.
  induction word1 as [|s word1 IH]=>/=.
  - split; [tauto|]. move=>[/TerminalSet.empty_1 ?|[? _]]//.
  - rewrite /nullable_word forallb_app /=. destruct nullable_symb=>/=.
    + rewrite Bool.andb_true_r. split.
      * move=>/TerminalSet.union_1. rewrite IH.
        move=>[?|[?|[??]]]; auto using TerminalSet.union_2, TerminalSet.union_3.
      * destruct IH.
        move=>[/TerminalSet.union_1 [?|?]|[??]];
        auto using TerminalSet.union_2, TerminalSet.union_3.
    + rewrite Bool.andb_false_r. by intuition.
Qed.

(** If the first predicate has been validated, then it is correct. **)
Lemma first_correct head word t q :
  word = t::q ->
  parse_tree head word ->
  TerminalSet.In (token_term t) (first_symb_set head)
with first_correct_list heads word t q :
  word = t::q ->
  parse_tree_list heads word ->
  TerminalSet.In (token_term t) (first_word_set (rev' heads)).
Proof.
  - intros Hword. destruct 1=>//.
    + inversion Hword. subst. apply TerminalSet.singleton_2, compare_refl.
    + eapply first_stable. eauto.
  - intros Hword. destruct 1 as [|symq wordq ptl symt wordt pt]=>//=.
    rewrite /rev' -rev_alt /= first_word_set_app /= rev_involutive rev_alt.
    destruct wordq; [right|left].
    + destruct nullable_symb; eauto using TerminalSet.union_2, nullable_correct_list.
    + inversion Hword. subst. fold (rev' symq). eauto.
Qed.

(** A PTL is compatible with a stack if the top of the stack contains
  data representing to this PTL. *)
Fixpoint ptl_stack_compat {symbs word}
         (stk0 : stack) (ptl : parse_tree_list symbs word) (stk : stack) : Prop :=
  match ptl with
  | Nil_ptl => stk0 = stk
  | @Cons_ptl _ _ ptl sym _ pt =>
    match stk with
    | [] => False
    | existT _ _ sem::stk =>
      ptl_stack_compat stk0 ptl stk /\
      exists e,
        sem = eq_rect _ symbol_semantic_type (pt_sem pt) _ e
    end
  end.

(** .. and when a PTL is compatible with a stack, then calling the pop
  function return the semantic value of this PTL. *)
Lemma pop_stack_compat_pop_spec {A symbs word}
    (ptl:parse_tree_list symbs word) (stk:stack) (stk0:stack) action :
  ptl_stack_compat stk0 ptl stk ->
  pop_spec symbs stk action stk0 (ptl_sem (A:=A) ptl action).
Proof.
  revert stk. induction ptl=>stk /= Hstk.
  - subst. constructor.
  - destruct stk as [|[st sem] stk]=>//. destruct Hstk as [Hstk [??]]. subst.
    simpl. constructor. eauto.
Qed.

Variable init: initstate.

(** In order to prove compleness, we first fix a word to be parsed
  together with the content of the parser at the end of the parsing. *)
Variable full_word: list token.
Variable buffer_end: buffer.

(** Completeness is proved by following the traversal of the parse
  tree which is performed by the parser. Each step of parsing
  correspond to one step of traversal. In order to represent the state
  of the traversal, we define the notion of "dotted" parse tree, which
  is a parse tree with one dot on one of its node. The place of the
  dot represents the place of the next action to be executed.

  Such a dotted parse tree is decomposed into two part: a "regular"
  parse tree, which is the parse tree placed under the dot, and a
  "parse tree zipper", which is the part of the parse tree placed
  above the dot. Therefore, a parse tree zipper is a parse tree with a
  hole. Moreover, for easier manipulation, a parse tree zipper is
  represented "upside down". That is, the root of the parse tree is
  actually a leaf of the zipper, while the root of the zipper is the
  hole.
 *)
Inductive pt_zipper:
  forall (hole_symb:symbol) (hole_word:list token), Type :=
| Top_ptz:
  pt_zipper (NT (start_nt init)) full_word
| Cons_ptl_ptz:
  forall {head_symbolsq:list symbol} {wordq:list token},
    parse_tree_list head_symbolsq wordq ->

  forall {head_symbolt:symbol} {wordt:list token},

    ptl_zipper (head_symbolt::head_symbolsq) (wordq++wordt) ->
    pt_zipper head_symbolt wordt
with ptl_zipper:
  forall (hole_symbs:list symbol) (hole_word:list token), Type :=
| Non_terminal_pt_ptlz:
  forall {p:production} {word:list token},
    pt_zipper (NT (prod_lhs p)) word ->
    ptl_zipper (prod_rhs_rev p) word

| Cons_ptl_ptlz:
  forall {head_symbolsq:list symbol} {wordq:list token},

  forall {head_symbolt:symbol} {wordt:list token},
    parse_tree head_symbolt wordt ->

  ptl_zipper (head_symbolt::head_symbolsq) (wordq++wordt) ->

  ptl_zipper head_symbolsq wordq.

(** A dotted parse tree is the combination of a parse tree zipper with
  a parse tree. It can be intwo flavors, depending on which is the next
  action to be executed (shift or reduce). *)
Inductive pt_dot: Type :=
| Reduce_ptd: forall {prod word},
    parse_tree_list (prod_rhs_rev prod) word ->
    pt_zipper (NT (prod_lhs prod)) word ->
    pt_dot
| Shift_ptd: forall (tok : token) {symbolsq wordq},
    parse_tree_list symbolsq wordq ->
    ptl_zipper (T (token_term tok)::symbolsq) (wordq++[tok]) ->
    pt_dot.

(** We can compute the full semantic value of a parse tree when
  represented as a dotted ptd. *)

Fixpoint ptlz_sem {hole_symbs hole_word}
         (ptlz:ptl_zipper hole_symbs hole_word) :
         (forall A, arrows_right A (map symbol_semantic_type hole_symbs) -> A) ->
         (symbol_semantic_type (NT (start_nt init))) :=
  match ptlz with
  | @Non_terminal_pt_ptlz prod _ ptz =>
    fun k => ptz_sem ptz (k _ (prod_action prod))
  | Cons_ptl_ptlz pt ptlz =>
    fun k => ptlz_sem ptlz (fun _ f => k _ (f (pt_sem pt)))
  end
with ptz_sem {hole_symb hole_word}
             (ptz:pt_zipper hole_symb hole_word):
  symbol_semantic_type hole_symb -> symbol_semantic_type (NT (start_nt init)) :=
  match ptz with
  | Top_ptz => fun sem => sem
  | Cons_ptl_ptz ptl ptlz =>
    fun sem => ptlz_sem ptlz (fun _ f => ptl_sem ptl (f sem))
  end.

Definition ptd_sem (ptd : pt_dot) :=
  match ptd with
  | @Reduce_ptd prod _ ptl ptz =>
    ptz_sem ptz (ptl_sem ptl (prod_action prod))
  | Shift_ptd tok ptl ptlz =>
    ptlz_sem ptlz (fun _ f => ptl_sem ptl (f (token_sem tok)))
  end.

(** The buffer associated with a dotted parse tree corresponds to the
  buffer left to be read by the parser when at the state represented
  by the dotted parse tree. *)
Fixpoint ptlz_buffer {hole_symbs hole_word}
         (ptlz:ptl_zipper hole_symbs hole_word): buffer :=
  match ptlz with
  | Non_terminal_pt_ptlz ptz =>
    ptz_buffer ptz
  | @Cons_ptl_ptlz _ _ _ wordt _ ptlz' =>
    wordt ++ ptlz_buffer ptlz'
  end
with ptz_buffer {hole_symb hole_word}
                (ptz:pt_zipper hole_symb hole_word): buffer :=
  match ptz with
  | Top_ptz => buffer_end
  | Cons_ptl_ptz _ ptlz =>
    ptlz_buffer ptlz
  end.

Definition ptd_buffer (ptd:pt_dot) :=
  match ptd with
  | Reduce_ptd _ ptz => ptz_buffer ptz
  | @Shift_ptd tok _ wordq _ ptlz => (tok::ptlz_buffer ptlz)%buf
  end.

(** We are now ready to define the main invariant of the proof of
  completeness: we need to specify when a stack is compatible with a
  dotted parse tree. Informally, a stack is compatible with a dotted
  parse tree when it is the concatenation stack fragments which are
  compatible with each of the partially recognized productions
  appearing in the parse tree zipper. Moreover, the head of each of
  these stack fragment contains a state which has an item predicted by
  the corresponding zipper.

  More formally, the compatibility relation first needs the following
  auxiliary definitions: *)
Fixpoint ptlz_prod {hole_symbs hole_word}
         (ptlz:ptl_zipper hole_symbs hole_word): production :=
  match ptlz with
  | @Non_terminal_pt_ptlz prod _ _ => prod
  | Cons_ptl_ptlz _ ptlz' => ptlz_prod ptlz'
  end.

Fixpoint ptlz_future {hole_symbs hole_word}
  (ptlz:ptl_zipper hole_symbs hole_word): list symbol :=
  match ptlz with
  | Non_terminal_pt_ptlz _ => []
  | @Cons_ptl_ptlz _ _ s _ _ ptlz' => s::ptlz_future ptlz'
  end.

Fixpoint ptlz_lookahead {hole_symbs hole_word}
  (ptlz:ptl_zipper hole_symbs hole_word) : terminal :=
  match ptlz with
  | Non_terminal_pt_ptlz ptz => token_term (buf_head (ptz_buffer ptz))
  | Cons_ptl_ptlz _ ptlz' => ptlz_lookahead ptlz'
  end.

Fixpoint ptz_stack_compat {hole_symb hole_word}
         (stk : stack) (ptz : pt_zipper hole_symb hole_word) : Prop :=
  match ptz with
  | Top_ptz => stk = []
  | Cons_ptl_ptz ptl ptlz =>
    exists stk0,
      state_has_future (state_of_stack init stk) (ptlz_prod ptlz)
                       (hole_symb::ptlz_future ptlz) (ptlz_lookahead ptlz) /\
      ptl_stack_compat stk0 ptl stk /\
      ptlz_stack_compat stk0 ptlz
  end
with ptlz_stack_compat {hole_symbs hole_word}
     (stk : stack) (ptlz : ptl_zipper hole_symbs hole_word) : Prop :=
  match ptlz with
  | Non_terminal_pt_ptlz ptz => ptz_stack_compat stk ptz
  | Cons_ptl_ptlz _ ptlz => ptlz_stack_compat stk ptlz
  end.

Definition ptd_stack_compat (ptd:pt_dot) (stk:stack): Prop :=
  match ptd with
  | @Reduce_ptd prod _ ptl ptz =>
    exists stk0,
      state_has_future (state_of_stack init stk) prod []
                       (token_term (buf_head (ptz_buffer ptz))) /\
      ptl_stack_compat stk0 ptl stk /\
      ptz_stack_compat stk0 ptz
  | Shift_ptd tok ptl ptlz =>
    exists stk0,
      state_has_future (state_of_stack init stk) (ptlz_prod ptlz)
                       (T (token_term tok) :: ptlz_future ptlz) (ptlz_lookahead ptlz) /\
      ptl_stack_compat stk0 ptl stk /\
      ptlz_stack_compat stk0 ptlz
  end.

Lemma ptz_stack_compat_cons_state_has_future {symbsq wordq symbt wordt} stk
      (ptl : parse_tree_list symbsq wordq)
      (ptlz : ptl_zipper (symbt :: symbsq) (wordq ++ wordt)) :
  ptz_stack_compat stk (Cons_ptl_ptz ptl ptlz) ->
  state_has_future (state_of_stack init stk) (ptlz_prod ptlz)
                   (symbt::ptlz_future ptlz) (ptlz_lookahead ptlz).
Proof. move=>[stk0 [? [? ?]]] //. Qed.

Lemma ptlz_future_ptlz_prod hole_symbs hole_word
      (ptlz:ptl_zipper hole_symbs hole_word) :
  rev_append (ptlz_future ptlz) hole_symbs = prod_rhs_rev (ptlz_prod ptlz).
Proof. induction ptlz=>//=. Qed.

Lemma ptlz_future_first {symbs word} (ptlz : ptl_zipper symbs word) :
  TerminalSet.In (token_term (buf_head (ptlz_buffer ptlz)))
    (first_word_set (ptlz_future ptlz)) \/
  token_term (buf_head (ptlz_buffer ptlz)) = ptlz_lookahead ptlz /\
  nullable_word (ptlz_future ptlz) = true.
Proof.
  induction ptlz as [|??? [|tok] pt ptlz IH]; [by auto| |]=>/=.
  - rewrite (nullable_correct _ _ eq_refl pt).
    destruct IH as [|[??]]; [left|right]=>/=; auto using TerminalSet.union_3.
  - left. destruct nullable_symb; eauto using TerminalSet.union_2, first_correct.
Qed.

(** We now want to define what is the next dotted parse tree which is
  to be handled after one action. Such dotted parse is built in two
  steps: Not only we have to perform the action by completing the
  parse tree, but we also have to prepare for the following step by
  moving the dot down to place it in front of the next action to be
  performed.
*)
Fixpoint build_pt_dot_from_pt {symb word}
         (pt : parse_tree symb word) (ptz : pt_zipper symb word)
  : pt_dot :=
  match pt in parse_tree symb word
    return pt_zipper symb word -> pt_dot
  with
  | Terminal_pt tok =>
    fun ptz =>
      let X :=
          match ptz in pt_zipper symb word
            return match symb with T term => True | NT _ => False end ->
                   { symbsq : list symbol &
                     { wordq : list token &
                       (parse_tree_list symbsq wordq *
                        ptl_zipper (symb :: symbsq) (wordq ++ word))%type } }
          with
          | Top_ptz => fun F => False_rect _ F
          | Cons_ptl_ptz ptl ptlz => fun _ =>
            existT _ _ (existT _ _ (ptl, ptlz))
          end I
      in
      Shift_ptd tok (fst (projT2 (projT2 X))) (snd (projT2 (projT2 X)))
  | Non_terminal_pt prod ptl => fun ptz =>
    let is_notnil :=
      match ptl in parse_tree_list w _
        return option (match w return Prop with [] => False | _ => True end)
      with
      | Nil_ptl => None
      | _ => Some I
      end
    in
    match is_notnil with
    | None => Reduce_ptd ptl ptz
    | Some H => build_pt_dot_from_pt_rec ptl H (Non_terminal_pt_ptlz ptz)
    end
  end ptz
with build_pt_dot_from_pt_rec {symbs word}
       (ptl : parse_tree_list symbs word)
       (Hsymbs : match symbs with [] => False | _ => True end)
       (ptlz : ptl_zipper symbs word)
  : pt_dot :=
  match ptl in parse_tree_list symbs word
    return match symbs with [] => False | _ => True end ->
           ptl_zipper symbs word ->
           pt_dot
  with
  | Nil_ptl => fun Hsymbs _ => False_rect _ Hsymbs
  | Cons_ptl ptl' pt => fun _ =>
    match ptl' in parse_tree_list symbsq wordq
      return parse_tree_list symbsq wordq ->
             ptl_zipper (_ :: symbsq) (wordq ++ _) ->
             pt_dot
    with
    | Nil_ptl => fun _ ptlz =>
      build_pt_dot_from_pt pt (Cons_ptl_ptz Nil_ptl ptlz)
    | _ => fun ptl' ptlz =>
      build_pt_dot_from_pt_rec ptl' I (Cons_ptl_ptlz pt ptlz)
    end ptl'
  end Hsymbs ptlz.

Definition build_pt_dot_from_ptl {symbs word}
         (ptl : parse_tree_list symbs word)
         (ptlz : ptl_zipper symbs word)
  : pt_dot :=
  match ptlz in ptl_zipper symbs word
    return parse_tree_list symbs word -> pt_dot
  with
  | Non_terminal_pt_ptlz ptz => fun ptl =>
    Reduce_ptd ptl ptz
  | Cons_ptl_ptlz pt ptlz => fun ptl =>
    build_pt_dot_from_pt pt (Cons_ptl_ptz ptl ptlz)
  end ptl.

Definition next_ptd (ptd:pt_dot) : option pt_dot :=
  match ptd with
  | Shift_ptd tok ptl ptlz =>
    Some (build_pt_dot_from_ptl (Cons_ptl ptl (Terminal_pt tok)) ptlz)
  | Reduce_ptd ptl ptz =>
    match ptz in pt_zipper symb word
      return parse_tree symb word -> _
    with
    | Top_ptz => fun _ => None
    | Cons_ptl_ptz ptl' ptlz => fun pt =>
      Some (build_pt_dot_from_ptl (Cons_ptl ptl' pt) ptlz)
    end (Non_terminal_pt _ ptl)
  end.

Fixpoint next_ptd_iter (ptd:pt_dot) (log_n_steps:nat) : option pt_dot :=
  match log_n_steps with
  | O => next_ptd ptd
  | S log_n_steps =>
    match next_ptd_iter ptd log_n_steps with
    | None => None
    | Some ptd => next_ptd_iter ptd log_n_steps
    end
  end.

(** We prove that these functions behave well w.r.t. semantic values. *)
Lemma sem_build_from_pt {symb word}
      (pt : parse_tree symb word) (ptz : pt_zipper symb word)  :
  ptz_sem ptz (pt_sem pt)
  = ptd_sem (build_pt_dot_from_pt pt ptz)
with sem_build_from_pt_rec {symbs word}
     (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word)
     Hsymbs :
  ptlz_sem ptlz (fun _ f => ptl_sem ptl f)
  = ptd_sem (build_pt_dot_from_pt_rec ptl Hsymbs ptlz).
Proof.
  - destruct pt as [tok|prod word ptl]=>/=.
    + revert ptz. generalize [tok].
      generalize (token_sem tok). generalize I.
      change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1.
      generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz.
    + match goal with
      | |- context [match ?X with Some H => _ | None => _ end] => destruct X=>//
      end.
      by rewrite -sem_build_from_pt_rec.
  - destruct ptl; [contradiction|].
    specialize (sem_build_from_pt_rec _ _ ptl)=>/=. destruct ptl.
    + by rewrite -sem_build_from_pt.
    + by rewrite -sem_build_from_pt_rec.
Qed.

Lemma sem_build_from_ptl {symbs word}
      (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) :
  ptlz_sem ptlz (fun _ f => ptl_sem ptl f)
  = ptd_sem (build_pt_dot_from_ptl ptl ptlz).
Proof. destruct ptlz=>//=. by rewrite -sem_build_from_pt. Qed.

Lemma sem_next_ptd (ptd : pt_dot) :
  match next_ptd ptd with
  | None => True
  | Some ptd' => ptd_sem ptd = ptd_sem ptd'
  end.
Proof.
  destruct ptd as [prod word ptl ptz|tok symbs word ptl ptlz] =>/=.
  - change (ptl_sem ptl (prod_action prod))
      with (pt_sem (Non_terminal_pt prod ptl)).
    generalize (Non_terminal_pt prod ptl). clear ptl.
    destruct ptz as [|?? ptl ?? ptlz]=>// pt. by rewrite -sem_build_from_ptl.
  - by rewrite -sem_build_from_ptl.
Qed.

Lemma sem_next_ptd_iter (ptd : pt_dot) (log_n_steps : nat) :
  match next_ptd_iter ptd log_n_steps with
  | None => True
  | Some ptd' => ptd_sem ptd = ptd_sem ptd'
  end.
Proof.
  revert ptd.
  induction log_n_steps as [|log_n_steps IH]; [by apply sem_next_ptd|]=>/= ptd.
  assert (IH1 := IH ptd). destruct next_ptd_iter as [ptd'|]=>//.
  specialize (IH ptd'). destruct next_ptd_iter=>//. congruence.
Qed.

(** We prove that these functions behave well w.r.t. xxx_buffer. *)
Lemma ptd_buffer_build_from_pt {symb word}
      (pt : parse_tree symb word) (ptz : pt_zipper symb word) :
  (word ++ ptz_buffer ptz)%buf = ptd_buffer (build_pt_dot_from_pt pt ptz)
with ptd_buffer_build_from_pt_rec {symbs word}
     (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word)
     Hsymbs :
  (word ++ ptlz_buffer ptlz)%buf = ptd_buffer (build_pt_dot_from_pt_rec ptl Hsymbs ptlz).
Proof.
  - destruct pt as [tok|prod word ptl]=>/=.
    + f_equal. revert ptz. generalize [tok].
      generalize (token_sem tok). generalize I.
      change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1.
      generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz.
    + match goal with
      | |- context [match ?X with Some H => _ | None => _ end] => destruct X eqn:EQ
      end.
      * by rewrite -ptd_buffer_build_from_pt_rec.
      * rewrite [X in (X ++ _)%buf](_ : word = []) //. clear -EQ. by destruct ptl.
  - destruct ptl as [|?? ptl ?? pt]; [contradiction|].
    specialize (ptd_buffer_build_from_pt_rec _ _ ptl).
    destruct ptl.
    + by rewrite /= -ptd_buffer_build_from_pt.
    + by rewrite -ptd_buffer_build_from_pt_rec //= app_buf_assoc.
Qed.

Lemma ptd_buffer_build_from_ptl {symbs word}
      (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) :
  ptlz_buffer ptlz = ptd_buffer (build_pt_dot_from_ptl ptl ptlz).
Proof.
  destruct ptlz as [|???? pt]=>//=. by rewrite -ptd_buffer_build_from_pt.
Qed.

(** We prove that these functions behave well w.r.t. xxx_stack_compat. *)
Lemma ptd_stack_compat_build_from_pt {symb word}
      (pt : parse_tree symb word) (ptz : pt_zipper symb word)
      (stk: stack) :
  ptz_stack_compat stk ptz ->
  ptd_stack_compat (build_pt_dot_from_pt pt ptz) stk
with ptd_stack_compat_build_from_pt_rec {symbs word}
     (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word)
     (stk : stack) Hsymbs :
  ptlz_stack_compat stk ptlz ->
  state_has_future (state_of_stack init stk) (ptlz_prod ptlz)
                   (rev' (prod_rhs_rev (ptlz_prod ptlz))) (ptlz_lookahead ptlz) ->
  ptd_stack_compat (build_pt_dot_from_pt_rec ptl Hsymbs ptlz) stk.
Proof.
  - intros Hstk. destruct pt as [tok|prod word ptl]=>/=.
    + revert ptz Hstk. generalize [tok]. generalize (token_sem tok). generalize I.
      change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1.
      generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz.
    + assert (state_has_future (state_of_stack init stk) prod
               (rev' (prod_rhs_rev prod)) (token_term (buf_head (ptz_buffer ptz)))).
      { revert ptz Hstk. remember (NT (prod_lhs prod)) eqn:EQ=>ptz.
        destruct ptz as [|?? ptl0 ?? ptlz0].
        - intros ->. apply start_future. congruence.
        - subst. intros (stk0 & Hfut & _). apply non_terminal_closed in Hfut.
          specialize (Hfut prod eq_refl).
          destruct (ptlz_future_first ptlz0) as [Hfirst|[Hfirst Hnull]].
          + destruct Hfut as [_ Hfut]. auto.
          + destruct Hfut as [Hfut _]. by rewrite Hnull -Hfirst in Hfut. }
      match goal with
      | |- context [match ?X with Some H => _ | None => _ end] => destruct X eqn:EQ
      end.
      * by apply ptd_stack_compat_build_from_pt_rec.
      * exists stk. destruct ptl=>//.
  - intros Hstk Hfut. destruct ptl as [|?? ptl ?? pt]; [contradiction|].
    specialize (ptd_stack_compat_build_from_pt_rec _ _ ptl). destruct ptl.
    + eapply ptd_stack_compat_build_from_pt=>//. exists stk.
      split; [|split]=>//; [].
      by rewrite -ptlz_future_ptlz_prod rev_append_rev /rev' -rev_alt
                 rev_app_distr rev_involutive in Hfut.
    + by apply ptd_stack_compat_build_from_pt_rec.
Qed.

Lemma ptd_stack_compat_build_from_ptl {symbs word}
      (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word)
      (stk stk0: stack) :
  ptlz_stack_compat stk0 ptlz ->
  ptl_stack_compat stk0 ptl stk ->
  state_has_future (state_of_stack init stk) (ptlz_prod ptlz)
                   (ptlz_future ptlz) (ptlz_lookahead ptlz) ->
  ptd_stack_compat (build_pt_dot_from_ptl ptl ptlz) stk.
Proof.
  intros Hstk0 Hstk Hfut. destruct ptlz=>/=.
  - eauto.
  - apply ptd_stack_compat_build_from_pt=>/=. eauto.
Qed.

(** We can now proceed by proving that the invariant is preserved by
  each step of parsing. We also prove that each step of parsing
  follows next_ptd.

  We start with reduce steps: *)
Lemma reduce_step_next_ptd (prod : production) (word : list token)
      (ptl : parse_tree_list (prod_rhs_rev prod) word)
      (ptz : pt_zipper (NT (prod_lhs prod)) word)
      (stk : stack)
      Hval Hi :
  ptd_stack_compat (Reduce_ptd ptl ptz) stk ->
  match next_ptd (Reduce_ptd ptl ptz) with
  | None =>
    reduce_step init stk prod (ptz_buffer ptz) Hval Hi =
      Accept_sr (ptd_sem (Reduce_ptd ptl ptz)) buffer_end
  | Some ptd =>
    exists stk',
      reduce_step init stk prod (ptz_buffer ptz) Hval Hi =
        Progress_sr stk' (ptd_buffer ptd) /\
      ptd_stack_compat ptd stk'
  end.
Proof.
  intros (stk0 & _ & Hstk & Hstk0).
  apply pop_stack_compat_pop_spec with (action := prod_action prod) in Hstk.
  rewrite <-pop_spec_ok with (Hp := reduce_step_subproof init stk prod Hval Hi) in Hstk.
  unfold reduce_step.
  match goal with
  | |- context [pop_state_valid init ?A stk ?B ?C ?D ?E ?F] =>
    generalize (pop_state_valid init A stk B C D E F)
  end.
  rewrite Hstk /=. intros Hv.
  generalize (reduce_step_subproof1 init stk prod Hval stk0 (fun _ : True => Hv)).
  clear Hval Hstk Hi Hv stk.
  assert (Hgoto := fun fut prod' =>
    non_terminal_goto (state_of_stack init stk0) prod' (NT (prod_lhs prod)::fut)).
  simpl in Hgoto.
  destruct goto_table as [[st Hst]|] eqn:Hgoto'.
  - intros _.
    assert (match ptz with Top_ptz => False | _ => True end).
    { revert ptz Hst Hstk0 Hgoto'.
      generalize (eq_refl (NT (prod_lhs prod))).
      generalize (NT (prod_lhs prod)) at 1 3 5.
      intros nt Hnt ptz. destruct ptz=>//. injection Hnt=> <- /= Hst -> /= Hg.
      assert (Hsg := start_goto init). by rewrite Hg in Hsg. }
    clear Hgoto'.

    change (ptl_sem ptl (prod_action prod))
      with (pt_sem (Non_terminal_pt prod ptl)).
    generalize (Non_terminal_pt prod ptl). clear ptl.
    destruct ptz as [|?? ptl ? ? ptlz]=>// pt.

    subst=>/=. eexists _. split.
    + f_equal. apply ptd_buffer_build_from_ptl.
    + destruct Hstk0 as (stk0' & Hfut & Hstk0' & Hstk0).
      apply (ptd_stack_compat_build_from_ptl _ _ _ stk0'); auto; [].
      split=>//. by exists eq_refl.
  - intros Hv. generalize (reduce_step_subproof0 _ prod _ (fun _ => Hv)).
    intros EQnt. clear Hv Hgoto'.

    change (ptl_sem ptl (prod_action prod))
      with (pt_sem (Non_terminal_pt prod ptl)).
    generalize (Non_terminal_pt prod ptl). clear ptl. destruct ptz.
    + intros pt. f_equal. by rewrite cast_eq.
    + edestruct Hgoto. eapply ptz_stack_compat_cons_state_has_future, Hstk0.
Qed.

Lemma step_next_ptd (ptd : pt_dot) (stk : stack) Hi :
  ptd_stack_compat ptd stk ->
  match next_ptd ptd with
  | None =>
    step safe init stk (ptd_buffer ptd) Hi =
      Accept_sr (ptd_sem ptd) buffer_end
  | Some ptd' =>
    exists stk',
      step safe init stk (ptd_buffer ptd) Hi =
        Progress_sr stk' (ptd_buffer ptd') /\
      ptd_stack_compat ptd' stk'
  end.
Proof.
  intros Hstk. unfold step.
  generalize (reduce_ok safe (state_of_stack init stk)).
  destruct ptd as [prod word ptl ptz|tok symbs word ptl ptlz].
  - assert (Hfut : state_has_future (state_of_stack init stk) prod []
                                    (token_term (buf_head (ptz_buffer ptz)))).
    { destruct Hstk as (? & ? & ?)=>//. }
    assert (Hact := end_reduce _ _ _ _ Hfut).
    destruct action_table as [?|awt]=>Hval /=.
    + subst. by apply reduce_step_next_ptd.
    + set (term := token_term (buf_head (ptz_buffer ptz))) in *.
      generalize (Hval term). clear Hval. destruct (awt term)=>//. subst.
      intros Hval. by apply reduce_step_next_ptd.
  - destruct Hstk as (stk0 & Hfut & Hstk & Hstk0).
    assert (Hact := terminal_shift _ _ _ _ Hfut). simpl in Hact. clear Hfut.
    destruct action_table as [?|awt]=>//= /(_ (token_term tok)).
    destruct awt as [st' EQ| |]=>// _. eexists. split.
    + f_equal. rewrite -ptd_buffer_build_from_ptl //.
    + apply (ptd_stack_compat_build_from_ptl _ _ _ stk0); simpl; eauto.
Qed.

(** We prove the completeness of the parser main loop. *)
Lemma parse_fix_next_ptd_iter (ptd : pt_dot) (stk : stack) (log_n_steps : nat) Hi :
  ptd_stack_compat ptd stk ->
  match next_ptd_iter ptd log_n_steps with
  | None =>
    proj1_sig (parse_fix safe init stk (ptd_buffer ptd) log_n_steps Hi) =
      Accept_sr (ptd_sem ptd) buffer_end
  | Some ptd' =>
    exists stk',
      proj1_sig (parse_fix safe init stk (ptd_buffer ptd) log_n_steps Hi) =
        Progress_sr stk' (ptd_buffer ptd') /\
      ptd_stack_compat ptd' stk'
  end.
Proof.
  revert ptd stk Hi.
  induction log_n_steps as [|log_n_steps IH]; [by apply step_next_ptd|].
  move => /= ptd stk Hi Hstk. assert (IH1 := IH ptd stk Hi Hstk).
  assert (EQsem := sem_next_ptd_iter ptd log_n_steps).
  destruct parse_fix as [sr Hi']. simpl in IH1.
  destruct next_ptd_iter as [ptd'|].
  - rewrite EQsem. destruct IH1 as (stk' & -> & Hstk'). by apply IH.
  - by subst.
Qed.

(** The parser is defined by recursion over a fuel parameter. In the
  completeness proof, we need to predict how much fuel is going to be
  needed in order to prove that enough fuel gives rise to a successful
  parsing.

  To do so, of a dotted parse tree, which is the number of actions
  left to be executed before complete parsing when the current state
  is represented by the dotted parse tree. *)
Fixpoint ptlz_cost {hole_symbs hole_word}
         (ptlz:ptl_zipper hole_symbs hole_word) :=
  match ptlz with
  | Non_terminal_pt_ptlz ptz => ptz_cost ptz
  | Cons_ptl_ptlz pt ptlz' => pt_size pt + ptlz_cost ptlz'
  end
with ptz_cost {hole_symb hole_word} (ptz:pt_zipper hole_symb hole_word) :=
  match ptz with
  | Top_ptz => 0
  | Cons_ptl_ptz ptl ptlz' => 1 + ptlz_cost ptlz'
  end.

Definition ptd_cost (ptd:pt_dot) :=
  match ptd with
  | Reduce_ptd ptl ptz => ptz_cost ptz
  | Shift_ptd _ ptl ptlz => 1 + ptlz_cost ptlz
  end.

Lemma ptd_cost_build_from_pt {symb word}
      (pt : parse_tree symb word) (ptz : pt_zipper symb word) :
  pt_size pt + ptz_cost ptz = S (ptd_cost (build_pt_dot_from_pt pt ptz))
with ptd_cost_build_from_pt_rec {symbs word}
     (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word)
     Hsymbs :
  ptl_size ptl + ptlz_cost ptlz = ptd_cost (build_pt_dot_from_pt_rec ptl Hsymbs ptlz).
Proof.
  - destruct pt as [tok|prod word ptl']=>/=.
    + revert ptz. generalize [tok]. generalize (token_sem tok). generalize I.
      change True with (match T (token_term tok) with T _ => True | NT _ => False end) at 1.
      generalize (T (token_term tok)) => symb HT sem word ptz. by destruct ptz.
    + match goal with
      | |- context [match ?X with Some H => _ | None => _ end] => destruct X eqn:EQ
      end.
      * rewrite -ptd_cost_build_from_pt_rec /= plus_n_Sm //.
      * simpl. by destruct ptl'.
  - destruct ptl as [|?? ptl ?? pt]; [contradiction|].
    specialize (ptd_cost_build_from_pt_rec _ _ ptl). destruct ptl.
    + apply eq_add_S. rewrite -ptd_cost_build_from_pt /=. ring.
    + rewrite -ptd_cost_build_from_pt_rec //=. ring.
Qed.

Lemma ptd_cost_build_from_ptl {symbs word}
      (ptl : parse_tree_list symbs word) (ptlz : ptl_zipper symbs word) :
  ptlz_cost ptlz = ptd_cost (build_pt_dot_from_ptl ptl ptlz).
Proof.
  destruct ptlz=>//. apply eq_add_S. rewrite -ptd_cost_build_from_pt /=. ring.
Qed.

Lemma next_ptd_cost ptd:
  match next_ptd ptd with
  | None => ptd_cost ptd = 0
  | Some ptd' => ptd_cost ptd = S (ptd_cost ptd')
  end.
Proof.
  destruct ptd as [prod word ptl ptz|tok symbq wordq ptl ptlz] =>/=.
  - generalize (Non_terminal_pt prod ptl). clear ptl.
    destruct ptz as [|?? ptl ?? ptlz]=>// pt. by rewrite -ptd_cost_build_from_ptl.
  - by rewrite -ptd_cost_build_from_ptl.
Qed.

Lemma next_ptd_iter_cost ptd log_n_steps :
  match next_ptd_iter ptd log_n_steps with
  | None => ptd_cost ptd < 2^log_n_steps
  | Some ptd' => ptd_cost ptd = 2^log_n_steps + ptd_cost ptd'
  end.
Proof.
  revert ptd. induction log_n_steps as [|log_n_steps IH]=>ptd /=.
  - assert (Hptd := next_ptd_cost ptd). destruct next_ptd=>//. by rewrite Hptd.
  - rewrite Nat.add_0_r. assert (IH1 := IH ptd). destruct next_ptd_iter as [ptd'|].
    + specialize (IH ptd'). destruct next_ptd_iter as [ptd''|].
      * by rewrite IH1 IH -!plus_assoc.
      * rewrite IH1. by apply plus_lt_compat_l.
    + by apply lt_plus_trans.
Qed.

(** We now prove the top-level parsing function. The only thing that
  is left to be done is the initialization. To do so, we define the
  initial dotted parse tree, depending on a full (top-level) parse tree. *)

Variable full_pt : parse_tree (NT (start_nt init)) full_word.

Theorem parse_complete log_n_steps:
  match parse safe init (full_word ++ buffer_end) log_n_steps with
  | Parsed_pr sem buff =>
    sem = pt_sem full_pt /\ buff = buffer_end /\ pt_size full_pt <= 2^log_n_steps
  | Timeout_pr => 2^log_n_steps < pt_size full_pt
  | Fail_pr => False
  end.
Proof.
  assert (Hstk : ptd_stack_compat (build_pt_dot_from_pt full_pt Top_ptz) []) by
      by apply ptd_stack_compat_build_from_pt.
  unfold parse.
  assert (Hparse := parse_fix_next_ptd_iter _ _ log_n_steps (parse_subproof init) Hstk).
  rewrite -ptd_buffer_build_from_pt -sem_build_from_pt /= in Hparse.
  assert (Hcost := next_ptd_iter_cost (build_pt_dot_from_pt full_pt Top_ptz) log_n_steps).
  destruct next_ptd_iter.
  - destruct Hparse as (? & -> & ?). apply (f_equal S) in Hcost.
    rewrite -ptd_cost_build_from_pt Nat.add_0_r in Hcost. rewrite Hcost.
    apply le_lt_n_Sm, le_plus_l.
  - rewrite Hparse. split; [|split]=>//. apply lt_le_S in Hcost.
    by rewrite -ptd_cost_build_from_pt Nat.add_0_r in Hcost.
Qed.

End Completeness_Proof.

End Make.