Research source · Plain text
Semantics.lean
site/public/formal/persistent-stacks/lean/FPRDTransfer/PEG/Semantics.lean
285 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: 65ce84396fc42118cfcb05f71f109079f79264cb498e3b9e7ef88c268fe7710e
import FPRDTransfer.PEG.Syntax
namespace FPRD
universe uNT uNT' uSym
/-- A PEG either rejects or succeeds with one exact unconsumed suffix. -/
inductive Result (Sym : Type uSym) where
| ok (rest : List Sym)
| err
deriving DecidableEq
/-- The theorem-bearing relational semantics of ordinary ordered-choice PEGs. -/
inductive Eval (G : Grammar NT Sym) :
Expr NT Sym → List Sym → Result Sym → Prop where
| eps : Eval G .eps input (.ok input)
| fail : Eval G .fail input .err
| anyOk : Eval G .any (symbol :: rest) (.ok rest)
| anyNil : Eval G .any [] .err
| termOk : Eval G (.term symbol) (symbol :: rest) (.ok rest)
| termNe : left ≠ right → Eval G (.term left) (right :: rest) .err
| termNil : Eval G (.term symbol) [] .err
| nonterm : Eval G (G.rule name) input result →
Eval G (.nonterm name) input result
| seqOk : Eval G left input (.ok middle) → Eval G right middle result →
Eval G (.seq left right) input result
| seqErr : Eval G left input .err → Eval G (.seq left right) input .err
| altOk : Eval G left input (.ok rest) →
Eval G (.alt left right) input (.ok rest)
| altErr : Eval G left input .err → Eval G right input result →
Eval G (.alt left right) input result
| notOk : Eval G body input (.ok rest) → Eval G (.notP body) input .err
| notErr : Eval G body input .err → Eval G (.notP body) input (.ok input)
/-- Relational evaluation, including recursive nonterminals, has one result. -/
theorem eval_deterministic {G : Grammar NT Sym} {expr : Expr NT Sym}
{input : List Sym} {left right : Result Sym}
(hleft : Eval G expr input left) (hright : Eval G expr input right) :
left = right := by
induction hleft generalizing right with
| eps => cases hright; rfl
| fail => cases hright; rfl
| anyOk => cases hright; rfl
| anyNil => cases hright; rfl
| termOk =>
cases hright with
| termOk => rfl
| termNe hne => exact (hne rfl).elim
| termNe hne =>
cases hright with
| termOk => exact (hne rfl).elim
| termNe _ => rfl
| termNil => cases hright; rfl
| nonterm _ ih =>
cases hright with
| nonterm hright => exact ih hright
| seqOk _ _ ihLeft ihRight =>
cases hright with
| seqOk hLeft hRight =>
have middle_eq := ihLeft hLeft
cases middle_eq
exact ihRight hRight
| seqErr hLeft =>
have impossible := ihLeft hLeft
cases impossible
| seqErr _ ihLeft =>
cases hright with
| seqOk hLeft _ =>
have impossible := ihLeft hLeft
cases impossible
| seqErr _ => rfl
| altOk _ ihLeft =>
cases hright with
| altOk hLeft => exact ihLeft hLeft
| altErr hLeft _ =>
have impossible := ihLeft hLeft
cases impossible
| altErr _ _ ihLeft ihRight =>
cases hright with
| altOk hLeft =>
have impossible := ihLeft hLeft
cases impossible
| altErr _ hRight => exact ihRight hRight
| notOk _ ihBody =>
cases hright with
| notOk _ => rfl
| notErr hBody =>
have impossible := ihBody hBody
cases impossible
| notErr _ ihBody =>
cases hright with
| notOk hBody =>
have impossible := ihBody hBody
cases impossible
| notErr _ => rfl
theorem eval_nonterm_iff (G : Grammar NT Sym) (name : NT)
(input : List Sym) (result : Result Sym) :
Eval G (.nonterm name) input result ↔ Eval G (G.rule name) input result := by
constructor
· intro h
cases h with
| nonterm h => exact h
· exact Eval.nonterm
theorem term_ok_iff (G : Grammar NT Sym) (symbol : Sym)
(input rest : List Sym) :
Eval G (.term symbol) input (.ok rest) ↔ input = symbol :: rest := by
constructor
· intro h
cases h
rfl
· intro h
subst input
exact .termOk
theorem term_ok_nil_iff (G : Grammar NT Sym) (symbol : Sym)
(input : List Sym) :
Eval G (.term symbol) input (.ok []) ↔ input = [symbol] :=
term_ok_iff G symbol input []
/-- Every expression and input has a relational result. This is a property of
a grammar, not an assumption built into `Eval`. -/
def Total (G : Grammar NT Sym) : Prop :=
∀ expr input, ∃ result, Eval G expr input result
/-- Complete-match recognition requires the start rule to consume all input. -/
def Recognizes (G : Grammar NT Sym) (language : List Sym → Prop) : Prop :=
∀ input, language input ↔ Eval G (.nonterm G.start) input (.ok [])
/-- Membership in PEG uses a literal finite `Fin n` nonterminal carrier. -/
def InPEG [DecidableEq Sym] (language : List Sym → Prop) : Prop :=
∃ (n : Nat) (G : FiniteGrammar Sym (Fin n)),
Total G.grammar ∧ Recognizes G.grammar language
/-- Rename an evaluation derivation along a nonterminal equivalence. -/
theorem eval_transport (e : FiniteEquiv NT NT') (G : Grammar NT Sym)
{expr : Expr NT Sym} {input : List Sym} {result : Result Sym}
(h : Eval G expr input result) :
Eval (transportGrammar e G) (transportExpr e expr) input result := by
induction h with
| eps => exact .eps
| fail => exact .fail
| anyOk => exact .anyOk
| anyNil => exact .anyNil
| termOk => exact .termOk
| termNe hne => exact .termNe hne
| termNil => exact .termNil
| nonterm _ ih =>
apply Eval.nonterm
simpa using ih
| seqOk _ _ ihLeft ihRight => exact .seqOk ihLeft ihRight
| seqErr _ ih => exact .seqErr ih
| altOk _ ih => exact .altOk ih
| altErr _ _ ihLeft ihRight => exact .altErr ihLeft ihRight
| notOk _ ih => exact .notOk ih
| notErr _ ih => exact .notErr ih
theorem eval_transport_iff (e : FiniteEquiv NT NT') (G : Grammar NT Sym)
(expr : Expr NT Sym) (input : List Sym) (result : Result Sym) :
Eval (transportGrammar e G) (transportExpr e expr) input result ↔
Eval G expr input result := by
constructor
· intro h
simpa using eval_transport e.symm (transportGrammar e G) h
· exact eval_transport e G
theorem total_transport_iff (e : FiniteEquiv NT NT') (G : Grammar NT Sym) :
Total (transportGrammar e G) ↔ Total G := by
constructor
· intro h expr input
obtain ⟨result, hresult⟩ := h (transportExpr e expr) input
exact ⟨result, (eval_transport_iff e G expr input result).mp hresult⟩
· intro h expr input
obtain ⟨result, hresult⟩ := h (transportExpr e.symm expr) input
refine ⟨result, ?_⟩
simpa using eval_transport e G hresult
theorem recognizes_transport_iff (e : FiniteEquiv NT NT')
(G : Grammar NT Sym) (language : List Sym → Prop) :
Recognizes (transportGrammar e G) language ↔ Recognizes G language := by
constructor
· intro h input
exact (h input).trans
(eval_transport_iff e G (.nonterm G.start) input (.ok []))
· intro h input
exact (h input).trans
(eval_transport_iff e G (.nonterm G.start) input (.ok [])).symm
/-- Two expressions with the same canonical result accept exactly the same
result at that input, by determinism. -/
theorem eval_iff_of_shared_result {G : Grammar NT Sym}
{left right : Expr NT Sym} {input : List Sym} {canonical result : Result Sym}
(hleft : Eval G left input canonical) (hright : Eval G right input canonical) :
Eval G left input result ↔ Eval G right input result := by
constructor
· intro h
have result_eq := eval_deterministic h hleft
cases result_eq
exact hright
· intro h
have result_eq := eval_deterministic h hright
cases result_eq
exact hleft
/-- Negative lookahead for `any` succeeds exactly at end of input. -/
theorem notAny_iff_nil (G : Grammar NT Sym) (input rest : List Sym) :
Eval G (.notP .any) input (.ok rest) ↔ input = [] ∧ rest = [] := by
constructor
· intro h
cases input with
| nil =>
cases h with
| notErr _ => exact ⟨rfl, rfl⟩
| cons symbol tail =>
cases h with
| notErr hAny => cases hAny
· rintro ⟨rfl, rfl⟩
exact .notErr .anyNil
theorem termAlt_ok_of_mem (G : Grammar NT Sym) {symbol : Sym}
{symbols : List Sym} {rest : List Sym} (hmem : symbol ∈ symbols) :
Eval G (Expr.termAlt symbols) (symbol :: rest) (.ok rest) := by
induction symbols with
| nil => simp at hmem
| cons head tail ih =>
change Eval G (.alt (.term head) (Expr.termAlt tail))
(symbol :: rest) (.ok rest)
by_cases hhead : head = symbol
· subst head
exact .altOk .termOk
· have htail : symbol ∈ tail :=
(List.mem_cons.mp hmem).resolve_left (Ne.symm hhead)
exact .altErr (.termNe hhead) (ih htail)
theorem termAlt_nil_err (G : Grammar NT Sym) (symbols : List Sym) :
Eval G (Expr.termAlt symbols) [] .err := by
induction symbols with
| nil => exact .fail
| cons head tail ih =>
exact .altErr .termNil ih
/-- For an exhaustive symbol list, looking past every terminal is exactly the
ordinary end-of-input predicate `!any`. -/
theorem notAlt_enum_iff_notAny (G : Grammar NT Sym) (symbols : List Sym)
(complete : ∀ symbol, symbol ∈ symbols) (input : List Sym)
(result : Result Sym) :
Eval G (.notP (Expr.termAlt symbols)) input result ↔
Eval G (.notP .any) input result := by
cases input with
| nil =>
exact eval_iff_of_shared_result
(.notErr (termAlt_nil_err G symbols)) (.notErr .anyNil)
| cons symbol rest =>
exact eval_iff_of_shared_result
(.notOk (termAlt_ok_of_mem G (complete symbol))) (.notOk .anyOk)
/-- The guarded rule `any A / eps` consumes every finite input. -/
theorem anyStar_ok (G : Grammar NT Sym) (name : NT)
(rule_eq : G.rule name = .alt (.seq .any (.nonterm name)) .eps)
(input : List Sym) : Eval G (.nonterm name) input (.ok []) := by
induction input with
| nil =>
apply Eval.nonterm
rw [rule_eq]
exact .altErr (.seqErr .anyNil) .eps
| cons symbol rest ih =>
apply Eval.nonterm
rw [rule_eq]
exact .altOk (.seqOk .anyOk ih)
theorem anyStar_ok_iff (G : Grammar NT Sym) (name : NT)
(rule_eq : G.rule name = .alt (.seq .any (.nonterm name)) .eps)
(input rest : List Sym) :
Eval G (.nonterm name) input (.ok rest) ↔ rest = [] := by
constructor
· intro h
have result_eq := eval_deterministic h (anyStar_ok G name rule_eq input)
cases result_eq
rfl
· intro h
subst rest
exact anyStar_ok G name rule_eq input
end FPRD