(****************************************************************************)
(* *)
(* 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 Syntax Derive. From Coq.ssr Require Import ssreflect. Require Automaton. Require Import Alphabet Validator_classes. Module Make(Import A:Automaton.T). (** We instantiate some sets/map. **)
Module TerminalComparableM <: ComparableM. Definition t := terminal. Instance tComparable : Comparable t := _. End TerminalComparableM. Module TerminalOrderedType := OrderedType_from_ComparableM TerminalComparableM. Module StateProdPosComparableM <: ComparableM. Definition t := (state*production*nat)%type. Instance tComparable : Comparable t := _. End StateProdPosComparableM. Module StateProdPosOrderedType :=
OrderedType_from_ComparableM StateProdPosComparableM. Module TerminalSet := FSetAVL.Make TerminalOrderedType. Module StateProdPosMap := FMapAVL.Make StateProdPosOrderedType. (** Nullable predicate for symbols and list of symbols. **)
Definition nullable_symb (symbol:symbol) :=
match symbol with
| NT nt => nullable_nterm nt
| _ => false
end. Definition nullable_word (word:list symbol) :=
forallb nullable_symb word. (** First predicate for non terminal, symbols and list of symbols, given as FSets. **)
Definition first_nterm_set (nterm:nonterminal) :=
fold_left (fun acc t => TerminalSet.add t acc)
(first_nterm nterm) TerminalSet.empty. Definition first_symb_set (symbol:symbol) :=
match symbol with
| NT nt => first_nterm_set nt
| T t => TerminalSet.singleton t
end. Fixpoint first_word_set (word:list symbol) :=
match word with
| [] => TerminalSet.empty
| t::q =>
if nullable_symb t then
TerminalSet.union (first_symb_set t) (first_word_set q)
else
first_symb_set t
end. (** Small helper for finding the part of an item that is after the dot. **)
Definition future_of_prod prod dot_pos : list symbol :=
(fix loop n lst :=
match n with
| O => lst
| S x => match loop x lst with [] => [] | _::q => q end
end)
dot_pos (rev' (prod_rhs_rev prod)). (** We build a fast map to store all the items of all the states. **)
Definition items_map (_:unit): StateProdPosMap.t TerminalSet.t :=
fold_left (fun acc state =>
fold_left (fun acc item =>
let key := (state, prod_item item, dot_pos_item item) in
let data := fold_left (fun acc t => TerminalSet.add t acc)
(lookaheads_item item) TerminalSet.empty
in
let old :=
match StateProdPosMap.find key acc with
| Some x => x | None => TerminalSet.empty
end
in
StateProdPosMap.add key (TerminalSet.union data old) acc
) (items_of_state state) acc
) all_list (StateProdPosMap.empty TerminalSet.t). (** We need to avoid computing items_map each time we need it. To that
purpose, we declare a typeclass specifying that some map is equal to
items_map. *)
Class IsItemsMap m := is_items_map : m = items_map (). (** Accessor. **)
Definition find_items_map items_map state prod dot_pos : TerminalSet.t :=
match StateProdPosMap.find (state, prod, dot_pos) items_map with
| None => TerminalSet.empty
| Some x => x
end. Definition state_has_future state prod (fut:list symbol) (lookahead:terminal) :=
exists dot_pos:nat,
fut = future_of_prod prod dot_pos /\
TerminalSet.In lookahead (find_items_map (items_map ()) state prod dot_pos). (** Iterator over items. **)
Definition forallb_items items_map (P:state -> production -> nat -> TerminalSet.t -> bool): bool:=
StateProdPosMap.fold (fun key set acc =>
match key with (st, p, pos) => (acc && P st p pos set)%bool end
) items_map true.
Proof. Admitted.
Proof. intros ?. by apply TerminalSet.subset_2. Qed.
(* We do not declare this lemma as an instance, and use [Hint Extern]
instead, because the typeclass mechanism has trouble instantiating
some evars if we do not explicitely call [eassumption]. *)
Hint Extern 2 (IsValidator (state_has_future _ _ _ _) _) =>
eapply is_validator_state_has_future_subset; [eassumption|eassumption || reflexivity|]
: typeclass_instances.
Proof. intros Hlset%TerminalSet.elements_1 Hval Val. apply Hval. revert Val. rewrite TerminalSet.fold_1. generalize true at 1. clear -Hlset. induction Hlset as [? l <-%compare_eq|? l ? IH]=> /= b' Val. - destruct (b lookahead). by destruct b'. exfalso. by induction l; destruct b'. - eauto. Qed.
Hint Extern 100 (IsValidator _ _) =>
match goal with
| H : TerminalSet.In ?lookahead ?lset |- _ =>
eapply (is_validator_iterate_lset _ (fun lookahead => _) _ _ H); clear H
end
: typeclass_instances.
Proof. Admitted.
Proof. intros -> Hval1 Hval2 Val st prod fut lookahead (pos & -> & Hlookahead). rewrite /forallb_items StateProdPosMap.fold_1 in Val. assert (match future_of_prod prod pos with
| [] => b1 st prod pos (find_items_map (items_map ()) st prod pos)
| s :: fut' => b2 st prod pos (find_items_map (items_map ()) st prod pos) s fut'
end = true). - unfold find_items_map in *. assert (Hfind := @StateProdPosMap.find_2 _ (items_map ()) (st, prod, pos)). destruct StateProdPosMap.find as [lset|]; [|by edestruct (TerminalSet.empty_1); eauto]. specialize (Hfind _ eq_refl). apply StateProdPosMap.elements_1 in Hfind. revert Val. generalize true at 1. induction Hfind as [[? ?] l [?%compare_eq ?]|??? IH]=>?. + simpl in *; subst. match goal with |- _ -> ?X = true => destruct X end; [done|]. rewrite Bool.andb_false_r. clear. induction l as [|[[[??]?]?] l IH]=>//. + apply IH. - destruct future_of_prod eqn:EQ. by eapply Hval1; eauto. eapply Hval2 with (pos := pos); eauto; []. revert EQ. unfold future_of_prod=>-> //. Qed.
(* We need a hint for expplicitely instantiating b1 and b2 with lambdas. *)
Hint Extern 0 (IsValidator
(forall st prod fut lookahead,
state_has_future st prod fut lookahead -> _)
_) =>
eapply (is_validator_forall_items _ (fun st prod pos lset => _)
_ (fun st prod pos lset s fut' => _))
: typeclass_instances.
Proof. Admitted.
Proof. move=> -> /forallb_forall Val look. specialize (Val look (all_list_forall _)). exists 0. split=>//. by apply TerminalSet.mem_2. Qed.
(** * Validation for completeness **)
(** The nullable predicate is a fixpoint : it is correct. **)
Definition nullable_stable :=
forall p:production,
if nullable_word (prod_rhs_rev p) then
nullable_nterm (prod_lhs p) = true
else True. (** The first predicate is a fixpoint : it is correct. **)
Definition first_stable:=
forall (p:production),
TerminalSet.Subset (first_word_set (rev' (prod_rhs_rev p)))
(first_nterm_set (prod_lhs p)). (** The initial state has all the S=>.u items, where S is the start non-terminal **)
Definition start_future :=
forall (init:initstate) (p:production),
prod_lhs p = start_nt init ->
forall (t:terminal),
state_has_future init p (rev' (prod_rhs_rev p)) t. (** If a state contains an item of the form A->_.av[[b]], where a is a
terminal, then reading an a does a [Shift_act], to a state containing
an item of the form A->_.v[[b]]. **)
Definition terminal_shift :=
forall (s1:state) prod fut lookahead,
state_has_future s1 prod fut lookahead ->
match fut with
| T t::q =>
match action_table s1 with
| Lookahead_act awp =>
match awp t with
| Shift_act s2 _ =>
state_has_future s2 prod q lookahead
| _ => False
end
| _ => False
end
| _ => True
end. (** If a state contains an item of the form A->_.[[a]], then either we do a
[Default_reduce_act] of the corresponding production, either a is a
terminal (ie. there is a lookahead terminal), and reading a does a
[Reduce_act] of the corresponding production. **)
Definition end_reduce :=
forall (s:state) prod fut lookahead,
state_has_future s prod fut lookahead ->
match fut with
| [] =>
match action_table s with
| Default_reduce_act p => p = prod
| Lookahead_act awt =>
match awt lookahead with
| Reduce_act p => p = prod
| _ => False
end
end
| _ => True
end. Definition is_end_reduce items_map :=
forallb_items items_map (fun s prod pos lset =>
match future_of_prod prod pos with
| [] =>
match action_table s with
| Default_reduce_act p => compare_eqb p prod
| Lookahead_act awt =>
TerminalSet.fold (fun lookahead acc =>
match awt lookahead with
| Reduce_act p => (acc && compare_eqb p prod)%bool
| _ => false
end) lset true
end
| _ => true
end). (** If a state contains an item of the form A->_.Bv[[b]], where B is a
non terminal, then the goto table says we have to go to a state containing
an item of the form A->_.v[[b]]. **)
Definition non_terminal_goto :=
forall (s1:state) prod fut lookahead,
state_has_future s1 prod fut lookahead ->
match fut with
| NT nt::q =>
match goto_table s1 nt with
| Some (exist _ s2 _) =>
state_has_future s2 prod q lookahead
| None => False
end
| _ => True
end. Definition start_goto :=
forall (init:initstate),
match goto_table init (start_nt init) with
| None => True
| Some _ => False
end. (** Closure property of item sets : if a state contains an item of the form
A->_.Bv[[b]], then for each production B->u and each terminal a of
first(vb), the state contains an item of the form B->_.u[[a]] **)
Definition non_terminal_closed :=
forall s1 prod fut lookahead,
state_has_future s1 prod fut lookahead ->
match fut with
| NT nt::q =>
forall p, prod_lhs p = nt ->
(if nullable_word q then
state_has_future s1 p (future_of_prod p 0) lookahead
else True) /\
(forall lookahead2,
TerminalSet.In lookahead2 (first_word_set q) ->
state_has_future s1 p (future_of_prod p 0) lookahead2)
| _ => True
end. (** The automaton is complete **)
Definition complete :=
nullable_stable /\ first_stable /\ start_future /\ terminal_shift
/\ end_reduce /\ non_terminal_goto /\ start_goto /\ non_terminal_closed.
Proof. intros im. subst is_complete_0. instantiate (1:=fun im => _). apply _. Qed.