(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Jacques-Henri Jourdan, INRIA Paris-Rocquencourt            *)
(*                                                                     *)
(*  Copyright Institut National de Recherche en Informatique et en     *)
(*  Automatique.  All rights reserved.  This file is distributed       *)
(*  under the terms of the GNU General Public License as published by  *)
(*  the Free Software Foundation, either version 2 of the License, or  *)
(*  (at your option) any later version.  This file is also distributed *)
(*  under the terms of the INRIA Non-Commercial License Agreement.     *)
(*                                                                     *)
(* *********************************************************************)

Require Import Streams.
Require Import Equality.
Require Import List.
Require Import Syntax.
Require Import Alphabet.
Require Grammar.
Require Automaton.
Require Validator_safe.
Require Interpreter.

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

(** * A correct automaton does not crash **)

Section Safety_proof.

Hypothesis safe: safe.

Proposition shift_head_symbs: shift_head_symbs.
(* Goal: reduce_ok *)
Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed.
Proposition goto_head_symbs: goto_head_symbs.
(* Goal: reduce_ok *)
Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed.
Proposition shift_past_state: shift_past_state.
(* Goal: reduce_ok *)
Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed.
Proposition goto_past_state: goto_past_state.
(* Goal: reduce_ok *)
Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed.
Proposition reduce_ok: reduce_ok.
(* Goal: reduce_ok *)
Proof. pose proof safe; unfold Valid.safe in H; intuition. Qed.

(** We prove that a correct automaton won't crash : the interpreter will
    not return [Err] **)

Variable init : initstate.

(** The stack of states of an automaton stack **)
Definition state_stack_of_stack (stack:stack) :=
  (List.map
    (fun cell:sigT noninitstate_type => singleton_state_pred (projT1 cell))
    stack ++ [singleton_state_pred init])%list.

(** The stack of symbols of an automaton stack **)
Definition symb_stack_of_stack (stack:stack) :=
  List.map
    (fun cell:sigT noninitstate_type => last_symb_of_non_init_state (projT1 cell))
    stack.

(** The stack invariant : it basically states that the assumptions on the
    states are true. **)
Inductive stack_invariant: stack -> Prop :=
  | stack_invariant_constr:
    forall stack,
      prefix      (head_symbs_of_state (state_of_stack init stack))
                  (symb_stack_of_stack stack) ->
      prefix_pred (head_states_of_state (state_of_stack init stack))
                  (state_stack_of_stack stack) ->
      stack_invariant_next stack ->
      stack_invariant stack
with stack_invariant_next: stack -> Prop :=
  | stack_invariant_next_nil:
      stack_invariant_next []
  | stack_invariant_next_cons:
    forall state_cur st stack_rec,
      stack_invariant stack_rec ->
      stack_invariant_next (existT _ state_cur st::stack_rec).

(** [pop] conserves the stack invariant and does not crash
     under the assumption that we can pop at this place.
     Moreover, after a pop, the top state of the stack is allowed. **)
Lemma pop_stack_invariant_conserved
  (symbols_to_pop:list symbol) (stack_cur:stack) A action:
  stack_invariant stack_cur ->
  prefix symbols_to_pop (head_symbs_of_state (state_of_stack init stack_cur)) ->
  match pop symbols_to_pop stack_cur (A:=A) action with
    | OK (stack_new, _) =>
      stack_invariant stack_new /\
      state_valid_after_pop
        (state_of_stack init stack_new) symbols_to_pop
        (head_states_of_state (state_of_stack init stack_cur))
    | Err => False
  end.
Proof with eauto.
  intros.
  pose proof H.
  destruct H.
  revert H H0 H1 H2 H3.
  generalize (head_symbs_of_state (state_of_stack init stack0)).
  generalize (head_states_of_state (state_of_stack init stack0)).
  revert stack0 A action.
  induction symbols_to_pop; intros.
  - split...
    destruct l; constructor.
    inversion H2; subst.
    specialize (H7 (state_of_stack init stack0)).
    destruct (f2 (state_of_stack init stack0)) as [] eqn:? ...
    destruct stack0 as [|[]]; simpl in *.
    + inversion H6; subst.
      unfold singleton_state_pred in Heqb0.
      now rewrite compare_refl in Heqb0; discriminate.
    + inversion H6; subst.
      unfold singleton_state_pred in Heqb0.
      now rewrite compare_refl in Heqb0; discriminate.
  - destruct stack0 as [|[]]; unfold pop.
    + inversion H0; subst.
      now inversion H.
    + fold pop.
      destruct (compare_eqdec (last_symb_of_non_init_state x) a).
(* Goal: None *)
      * inversion H0; subst. clear H0.
(* Goal: None *)
        inversion H; subst. clear H.
        dependent destruction H3; simpl.
        assert (prefix_pred (List.tl l) (state_stack_of_stack stack0)).
        unfold tl; destruct l; [constructor | inversion H2]...
        pose proof H. destruct H3.
        specialize (IHsymbols_to_pop stack0 A (action0 n) _ _ H4 H7 H H0 H6).
        revert IHsymbols_to_pop.
        fold (noninitstate_type x); generalize (pop symbols_to_pop stack0 (action0 n)).
        destruct r as [|[]]; intuition...
        destruct l; constructor...
      * apply n0.
(* Goal: None *)
        inversion H0; subst.
(* Goal: None *)
        inversion H; subst...
Qed.

(** [prefix] is associative **)
Lemma prefix_ass:
  forall (l1 l2 l3:list symbol), prefix l1 l2 -> prefix l2 l3 -> prefix l1 l3.
Proof.
induction l1; intros.
(* Goal: None *)
(* Goal: None *)
constructor.
(* Goal: None *)
inversion H; subst.
(* Goal: None *)
inversion H0; subst.
(* Goal: None *)
constructor; eauto.
Qed.

(** [prefix_pred] is associative **)
Lemma prefix_pred_ass:
  forall (l1 l2 l3:list (state->bool)),
  prefix_pred l1 l2 -> prefix_pred l2 l3 -> prefix_pred l1 l3.
Proof.
induction l1; intros.
(* Goal: None *)
(* Goal: None *)
constructor.
(* Goal: None *)
inversion H; subst.
(* Goal: None *)
inversion H0; subst.
(* Goal: None *)
constructor; eauto.
(* Goal: None *)
intro.
(* Goal: @eq bool (implb (f0 x) (a x)) true *)
specialize (H3 x).
(* Goal: @eq bool (implb (f0 x) (a x)) true *)
specialize (H4 x).
destruct (f0 x); simpl in *; intuition.
(* Goal: @eq bool (a x) true *)
rewrite H4 in H3; intuition.
Qed.

(** If we have the right to reduce at this state, then the stack invariant
   is conserved by [reduce_step] and [reduce_step] does not crash. **)
Lemma reduce_step_stack_invariant_conserved stack prod buff:
  stack_invariant stack ->
  valid_for_reduce (state_of_stack init stack) prod ->
  match reduce_step init stack prod buff with
    | OK (Fail_sr | Accept_sr _ _) => True
    | OK (Progress_sr stack_new _) => stack_invariant stack_new
    | Err => False
  end.
Proof with eauto.
unfold valid_for_reduce.
intuition.
unfold reduce_step.
pose proof (pop_stack_invariant_conserved (prod_rhs_rev prod) stack _ (prod_action' prod)).
destruct (pop (prod_rhs_rev prod) stack (prod_action' prod)) as [|[]].
apply H0...
destruct H0...
pose proof (goto_head_symbs (state_of_stack init s) (prod_lhs prod)).
pose proof (goto_past_state (state_of_stack init s) (prod_lhs prod)).
unfold bind2.
destruct H0.
specialize (H2 _ H3)...
destruct (goto_table (state_of_stack init stack0) (prod_lhs prod)) as [[]|].
(* Goal: None *)
(* Goal: None *)
constructor.
simpl.
(* Goal: None *)
(* Goal: None *)
constructor.
eapply prefix_ass...
(* Goal: None *)
(* Goal: None *)
unfold state_stack_of_stack; simpl; constructor.
intro; destruct (singleton_state_pred x x0); reflexivity.
eapply prefix_pred_ass...
(* Goal: None *)
(* Goal: None *)
constructor...
(* Goal: None *)
(* Goal: None *)
constructor...
destruct stack0 as [|[]]...
destruct (compare_eqdec (prod_lhs prod) (start_nt init))...
apply n, H2, eq_refl.
apply H2, eq_refl.
Qed.

(** If the automaton is safe, then the stack invariant is conserved by
    [step] and [step] does not crash. **)
Lemma step_stack_invariant_conserved (stack:stack) buffer:
  stack_invariant stack ->
  match step init stack buffer with
    | OK (Fail_sr | Accept_sr _ _) => True
    | OK (Progress_sr stack_new _) => stack_invariant stack_new
    | Err => False
  end.
Proof with eauto.
(* Goal: None *)
intro.
unfold step.
pose proof (shift_head_symbs (state_of_stack init stack)).
pose proof (shift_past_state (state_of_stack init stack)).
pose proof (reduce_ok (state_of_stack init stack)).
destruct (action_table (state_of_stack init stack)).
apply reduce_step_stack_invariant_conserved...
destruct buffer as [[]]; simpl.
specialize (H0 x); specialize (H1 x); specialize (H2 x).
destruct (l x)...
destruct H.
(* Goal: None *)
(* Goal: None *)
constructor.
unfold state_of_stack.
(* Goal: None *)
(* Goal: None *)
constructor.
eapply prefix_ass...
(* Goal: None *)
(* Goal: None *)
unfold state_stack_of_stack; simpl; constructor.
intro; destruct (singleton_state_pred s0 x0)...
eapply prefix_pred_ass...
(* Goal: None *)
(* Goal: None *)
constructor...
(* Goal: None *)
(* Goal: None *)
constructor...
apply reduce_step_stack_invariant_conserved...
Qed.

(** If the automaton is safe, then it does not crash **)
Theorem parse_no_err buffer n_steps:
  parse init buffer n_steps <> Err.
Proof with eauto.
unfold parse.
assert (stack_invariant []).
(* Goal: None *)
(* Goal: None *)
constructor.
(* Goal: None *)
(* Goal: None *)
constructor.
(* Goal: None *)
(* Goal: None *)
unfold state_stack_of_stack; simpl; constructor.
intro; destruct (singleton_state_pred init x)...
(* Goal: None *)
(* Goal: None *)
constructor.
(* Goal: None *)
(* Goal: None *)
constructor.
revert H.
generalize buffer ([]:stack).
induction n_steps; simpl.
now discriminate.
intros.
pose proof (step_stack_invariant_conserved s buffer0 H).
destruct (step init s buffer0) as [|[]]; simpl...
discriminate.
discriminate.
Qed.

(** A version of [parse] that uses safeness in order to return a
   [parse_result], and not a [result parse_result] : we have proven that
   parsing does not return [Err]. **)
Definition parse_with_safe (buffer:Stream token) (n_steps:nat):
  parse_result init.
Proof with eauto.
pose proof (parse_no_err buffer n_steps).
destruct (parse init buffer n_steps)...
congruence.
Defined.

End Safety_proof.

End Make.
