Library compcert.cparser.validator.Automaton
Require Grammar.
Require Import Orders.
Require Export Alphabet.
Require Export List.
Require Export Syntax.
Module Type AutInit.
The grammar of the automaton.
The set of non initial state is considered as an alphabet.
Parameter noninitstate : Type.
Declare Instance NonInitStateAlph : Alphabet noninitstate.
Parameter initstate : Type.
Declare Instance InitStateAlph : Alphabet initstate.
Declare Instance NonInitStateAlph : Alphabet noninitstate.
Parameter initstate : Type.
Declare Instance InitStateAlph : Alphabet initstate.
When we are at this state, we know that this symbol is the top of the
stack.
Parameter last_symb_of_non_init_state: noninitstate -> symbol.
End AutInit.
Module Types(Import Init:AutInit).
End AutInit.
Module Types(Import Init:AutInit).
In many ways, the behaviour of the initial state is different from the
behaviour of the other states. So we have chosen to explicitaly separate
them: the user has to provide the type of non initial states.
Inductive state :=
| Init: initstate -> state
| Ninit: noninitstate -> state.
Program Instance StateAlph : Alphabet state :=
{ AlphabetComparable := {| compare := fun x y =>
match x, y return comparison with
| Init _, Ninit _ => Lt
| Init x, Init y => compare x y
| Ninit _, Init _ => Gt
| Ninit x, Ninit y => compare x y
end |};
AlphabetFinite := {| all_list := map Init all_list ++ map Ninit all_list |} }.
Local Obligation Tactic := intros.
Next Obligation.
destruct x, y; intuition; apply compare_antisym.
Qed.
Next Obligation.
destruct x, y, z; intuition.
apply (compare_trans _ i0); intuition.
congruence.
congruence.
apply (compare_trans _ n0); intuition.
Qed.
Next Obligation.
intros x y.
destruct x, y; intuition; try discriminate.
rewrite (compare_eq i i0); intuition.
rewrite (compare_eq n n0); intuition.
Qed.
Next Obligation.
apply in_or_app; destruct x; intuition;
[left|right]; apply in_map; apply all_list_forall.
Qed.
Coercion Ninit : noninitstate >-> state.
Coercion Init : initstate >-> state.
| Init: initstate -> state
| Ninit: noninitstate -> state.
Program Instance StateAlph : Alphabet state :=
{ AlphabetComparable := {| compare := fun x y =>
match x, y return comparison with
| Init _, Ninit _ => Lt
| Init x, Init y => compare x y
| Ninit _, Init _ => Gt
| Ninit x, Ninit y => compare x y
end |};
AlphabetFinite := {| all_list := map Init all_list ++ map Ninit all_list |} }.
Local Obligation Tactic := intros.
Next Obligation.
destruct x, y; intuition; apply compare_antisym.
Qed.
Next Obligation.
destruct x, y, z; intuition.
apply (compare_trans _ i0); intuition.
congruence.
congruence.
apply (compare_trans _ n0); intuition.
Qed.
Next Obligation.
intros x y.
destruct x, y; intuition; try discriminate.
rewrite (compare_eq i i0); intuition.
rewrite (compare_eq n n0); intuition.
Qed.
Next Obligation.
apply in_or_app; destruct x; intuition;
[left|right]; apply in_map; apply all_list_forall.
Qed.
Coercion Ninit : noninitstate >-> state.
Coercion Init : initstate >-> state.
For an LR automaton, there are four kind of actions that can be done at a
given state:
As in the menhir parser generator, we do not want our parser to read after
the end of stream. That means that once the parser has read a word in the
grammar language, it should stop without peeking the input stream. So, for
the automaton to be complete, the grammar must be particular: if a word is
in its language, then it is not a prefix of an other word of the language
(otherwise, menhir reports an end of stream conflict).
As a consequence of that, there is two notions of action: the first one is
an action performed before having read the stream, the second one is after
- Shifting, that is reading a token and putting it into the stack,
- Reducing a production, that is popping the right hand side of the production from the stack, and pushing the left hand side,
- Failing
- Accepting the word (special case of reduction)
Inductive lookahead_action (term:terminal) :=
| Shift_act: forall s:noninitstate,
T term = last_symb_of_non_init_state s -> lookahead_action term
| Reduce_act: production -> lookahead_action term
| Fail_act: lookahead_action term.
Implicit Arguments Shift_act [term].
Implicit Arguments Reduce_act [term].
Implicit Arguments Fail_act [term].
Inductive action :=
| Default_reduce_act: production -> action
| Lookahead_act : (forall term:terminal, lookahead_action term) -> action.
Types used for the annotations of the automaton.
An item is a part of the annotations given to the validator.
It is acually a set of LR(1) items sharing the same core. It is needed
to validate completeness.
The pseudo-production of the item.
The position of the dot.
The lookahead symbol of the item. We are using a list, so we can store
together multiple LR(1) items sharing the same core.
lookaheads_item: list terminal
}.
End Types.
Module Type T.
Include AutInit <+ Types.
Module Export GramDefs := Grammar.Defs Gram.
}.
End Types.
Module Type T.
Include AutInit <+ Types.
Module Export GramDefs := Grammar.Defs Gram.
For each initial state, the non terminal it recognizes.
The action table maps a state to either a map terminal -> action.
The goto table of an LR(1) automaton.
Parameter goto_table: state -> forall nt:nonterminal,
option { s:noninitstate | NT nt = last_symb_of_non_init_state s }.
option { s:noninitstate | NT nt = last_symb_of_non_init_state s }.
Some annotations on the automaton to help the validation.
When we are at this state, we know that these symbols are just below
the top of the stack. The list is ordered such that the head correspond
to the (almost) top of the stack.
When we are at this state, the (strictly) previous states verify these
predicates.
The items of the state.
The nullable predicate for non terminals :
true if and only if the symbol produces the empty string
The first predicates for non terminals, symbols or words of symbols. A
terminal is in the returned list if, and only if the parameter produces a
word that begins with the given terminal