Research source · Plain text
Grammar.lean
site/public/formal/persistent-stacks/lean/FPRDTransfer/Theorem16/Grammar.lean
899 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: 9c5de1abbe299e5e8cfbb8002578b785913cc1d85d0f207815fd8727be01ceef
import FPRDTransfer.Theorem16.Reachability
namespace FPRD
namespace Theorem16
universe u v w
/-- Observe the root reached by a path whose length is bounded by the unfolding
depth. `none`, `some none`, and `some (some label)` remain distinct. -/
def observeAt {Gamma : Type u} {d : Nat} :
(depth : Nat) → Unfold d Gamma depth →
(path : List (Fin d)) → path.length ≤ depth → Option (Option Gamma)
| _, .missing, _, _ => none
| 0, .leaf label, [], _ => some label
| 0, .leaf _, _ :: _, hlength =>
False.elim (Nat.not_succ_le_zero _ hlength)
| _depth + 1, .node label _, [], _ => some label
| depth + 1, .node _ children, port :: rest, hlength =>
observeAt depth (children port) rest (Nat.le_of_succ_le_succ hlength)
def observe {Gamma : Type u} {d k : Nat}
(neighbourhood : Unfold d Gamma k) (path : BoundedPath d k) :
Option (Option Gamma) :=
observeAt k neighbourhood path.elems path.length_le
/-- The empty bounded path, used to observe the root at every depth. -/
def rootPath (d k : Nat) : BoundedPath d k := ⟨[], by simp⟩
theorem boundedPath_elems_starts_root (d k : Nat) :
∃ rest, FiniteEnum.elems (α := BoundedPath d k) = rootPath d k :: rest := by
cases k with
| zero => exact ⟨[], rfl⟩
| succ k => exact ⟨_, rfl⟩
/-- Bounded observations separate exact-depth unfoldings. -/
theorem unfold_eq_iff_observe_all {Gamma : Type u} {d k : Nat}
(left right : Unfold d Gamma k) :
left = right ↔ ∀ path : BoundedPath d k,
observe left path = observe right path := by
constructor
· intro heq path
cases heq
rfl
· intro hall
induction k with
| zero =>
cases left with
| missing =>
cases right with
| missing => rfl
| leaf label =>
have h := hall (⟨[], by simp⟩ : BoundedPath d 0)
simp [observe, observeAt] at h
| leaf leftLabel =>
cases right with
| missing =>
have h := hall (⟨[], by simp⟩ : BoundedPath d 0)
simp [observe, observeAt] at h
| leaf rightLabel =>
have h := hall (⟨[], by simp⟩ : BoundedPath d 0)
simp [observe, observeAt] at h
cases h
rfl
| succ depth ih =>
cases left with
| missing =>
cases right with
| missing => rfl
| node label children =>
have h := hall (⟨[], by simp⟩ : BoundedPath d (depth + 1))
simp [observe, observeAt] at h
| node leftLabel leftChildren =>
cases right with
| missing =>
have h := hall (⟨[], by simp⟩ : BoundedPath d (depth + 1))
simp [observe, observeAt] at h
| node rightLabel rightChildren =>
have hlabel := hall
(⟨[], by simp⟩ : BoundedPath d (depth + 1))
simp [observe, observeAt] at hlabel
cases hlabel
have hchildren : leftChildren = rightChildren := by
funext port
apply ih (leftChildren port) (rightChildren port)
intro path
have hchild := hall
(⟨port :: path.elems,
Nat.succ_le_succ path.length_le⟩ :
BoundedPath d (depth + 1))
simpa [observe, observeAt] using hchild
cases hchildren
rfl
/-- The raw source nonterminal carrier is finite by construction; path names
carry the distance bound rather than an unrestricted list. -/
inductive NT (Q : Type) (Gamma : Type u) (d k : Nat) where
| state (control : Q)
| label (symbol : Gamma)
| neighbourhood (shape : Unfold d Gamma k)
| path (ports : BoundedPath d k)
| anyStar
| accepts
deriving DecidableEq
instance [FiniteEnum Q] [FiniteEnum Gamma] :
FiniteEnum (NT Q Gamma d k) where
elems :=
(FiniteEnum.elems (α := Q)).map NT.state ++
(FiniteEnum.elems (α := Gamma)).map NT.label ++
(FiniteEnum.elems (α := Unfold d Gamma k)).map NT.neighbourhood ++
(FiniteEnum.elems (α := BoundedPath d k)).map NT.path ++
[.anyStar, .accepts]
complete name := by
cases name <;> simp [FiniteEnum.complete]
section Grammar
variable {Sym Q : Type} {Gamma : Type u} {d k : Nat}
variable [FiniteEnum Sym] [DecidableEq Sym]
variable [FiniteEnum Q] [DecidableEq Q]
variable [FiniteEnum Gamma] [DecidableEq Gamma]
abbrev TransitionInput (Sym Q : Type) (Gamma : Type u) (d k : Nat) :=
Q × (Sym × Unfold d Gamma k)
def transitionInputs : List (TransitionInput Sym Q Gamma d k) :=
FiniteEnum.elems
/-- The source abbreviation `&(sigma State(q)) &(sigma Neighbourhood(N))`. -/
def transitionExpr (control : Q) (symbol : Sym)
(neighbourhood : Unfold d Gamma k) : Expr (NT Q Gamma d k) Sym :=
.seq
(.pos (.seq (.term symbol) (.nonterm (.state control))))
(.pos (.seq (.term symbol) (.nonterm (.neighbourhood neighbourhood))))
def stateAlternatives (A : Automaton Sym Q Gamma d k) (target : Q) :
List (Expr (NT Q Gamma d k) Sym) :=
(transitionInputs (Sym := Sym) (Q := Q) (Gamma := Gamma) (d := d) (k := k)).filterMap
fun input =>
if (A.delta input.1 input.2.1 input.2.2).1 = target then
some (transitionExpr input.1 input.2.1 input.2.2)
else none
def labelAlternatives (A : Automaton Sym Q Gamma d k) (target : Gamma) :
List (Expr (NT Q Gamma d k) Sym) :=
(transitionInputs (Sym := Sym) (Q := Q) (Gamma := Gamma) (d := d) (k := k)).filterMap
fun input =>
if (A.delta input.1 input.2.1 input.2.2).2.1 = target then
some (transitionExpr input.1 input.2.1 input.2.2)
else none
def pathAlternative (A : Automaton Sym Q Gamma d k) (hdistance : A.DistanceOk)
(tail : BoundedPath d k) (port : Fin d)
(input : TransitionInput Sym Q Gamma d k) :
Expr (NT Q Gamma d k) Sym :=
let guard := transitionExpr input.1 input.2.1 input.2.2
match htarget : (A.delta input.1 input.2.1 input.2.2).2.2 port with
| .path oldPath =>
let boundedOld : BoundedPath d k :=
⟨oldPath, hdistance input.1 input.2.1 input.2.2 port oldPath htarget⟩
.seq guard
(.seq (.term input.2.1)
(.seq (.nonterm (.path boundedOld)) (.nonterm (.path tail))))
| .missing => .seq guard .fail
| .self => .seq guard (.nonterm (.path tail))
def pathRule (A : Automaton Sym Q Gamma d k) (hdistance : A.DistanceOk)
(path : BoundedPath d k) : Expr (NT Q Gamma d k) Sym :=
match hpath : path.elems with
| [] => .eps
| port :: tail =>
let boundedTail : BoundedPath d k := ⟨tail, by
have := path.length_le
simp only [hpath, List.length_cons] at this
omega⟩
Expr.altAll
((transitionInputs (Sym := Sym) (Q := Q) (Gamma := Gamma) (d := d) (k := k)).map
(pathAlternative A hdistance boundedTail port))
def observationFact (neighbourhood : Unfold d Gamma k)
(path : BoundedPath d k) : Expr (NT Q Gamma d k) Sym :=
let pathExpr : Expr (NT Q Gamma d k) Sym := .nonterm (.path path)
match observe neighbourhood path with
| none => .notP pathExpr
| some none =>
.seq (.pos pathExpr)
(Expr.seqAll ((FiniteEnum.elems (α := Gamma)).map fun label =>
.notP (.seq pathExpr (.nonterm (.label label)))))
| some (some label) =>
.pos (.seq pathExpr (.nonterm (.label label)))
def neighbourhoodRule (neighbourhood : Unfold d Gamma k) :
Expr (NT Q Gamma d k) Sym :=
Expr.seqAll ((FiniteEnum.elems (α := BoundedPath d k)).map
(observationFact (Q := Q) (Sym := Sym) neighbourhood))
def acceptsRule (A : Automaton Sym Q Gamma d k) :
Expr (NT Q Gamma d k) Sym :=
Expr.altAll (((FiniteEnum.elems (α := Q)).filter fun control =>
A.accept control = true).map fun control =>
.seq (.nonterm (.state control)) (.nonterm .anyStar))
/-- The exact finite raw grammar from the source construction. Empty
alternative families use `fail`; `Path([])` is explicitly `eps`. -/
def rawGrammar (A : Automaton Sym Q Gamma d k) (hdistance : A.DistanceOk) :
Grammar (NT Q Gamma d k) Sym where
start := .accepts
rule
| .state control =>
Expr.altAll
((if control = A.init then [.notP .any] else []) ++
stateAlternatives A control)
| .label label => Expr.altAll (labelAlternatives A label)
| .neighbourhood neighbourhood => neighbourhoodRule (Q := Q) (Sym := Sym) neighbourhood
| .path path => pathRule A hdistance path
| .anyStar => .alt (.seq .any (.nonterm .anyStar)) .eps
| .accepts => acceptsRule A
def rawFiniteGrammar (A : Automaton Sym Q Gamma d k) (hdistance : A.DistanceOk) :
FiniteGrammar Sym (NT Q Gamma d k) where
alphabet := inferInstance
nonterminals := inferInstance
grammar := rawGrammar A hdistance
def canonicalEquiv :
FiniteEquiv (NT Q Gamma d k) (Fin (FiniteEnum.card (NT Q Gamma d k))) :=
FiniteEnum.equivFin (NT Q Gamma d k)
def canonicalGrammar (A : Automaton Sym Q Gamma d k) (hdistance : A.DistanceOk) :
Grammar (Fin (FiniteEnum.card (NT Q Gamma d k))) Sym :=
transportGrammar canonicalEquiv (rawGrammar A hdistance)
def canonicalFiniteGrammar (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) :
FiniteGrammar Sym (Fin (FiniteEnum.card (NT Q Gamma d k))) :=
transportFiniteGrammar canonicalEquiv (rawFiniteGrammar A hdistance)
def ntRank : NT Q Gamma d k → Nat
| .state _ | .label _ | .anyStar => 0
| .path path => path.elems.length
| .neighbourhood _ => k + 1
| .accepts => k + 2
omit [FiniteEnum Sym] [DecidableEq Sym] [FiniteEnum Q] [DecidableEq Q]
[FiniteEnum Gamma] [DecidableEq Gamma] in
@[simp] theorem transitionExpr_samePositionCalls (control : Q) (symbol : Sym)
(neighbourhood : Unfold d Gamma k) :
Expr.samePositionCalls (transitionExpr control symbol neighbourhood) = [] := by
simp [transitionExpr, Expr.pos, Expr.samePositionCalls,
Expr.guaranteedConsumes]
omit [FiniteEnum Sym] [DecidableEq Sym] in
theorem samePositionCalls_altAll {Name : Type v}
(expressions : List (Expr Name Sym)) :
Expr.samePositionCalls (Expr.altAll expressions) =
expressions.flatMap Expr.samePositionCalls := by
induction expressions with
| nil => rfl
| cons expression expressions ih =>
change Expr.samePositionCalls (.alt expression (Expr.altAll expressions)) = _
simp [Expr.samePositionCalls, ih]
omit [FiniteEnum Sym] [DecidableEq Sym] in
theorem samePositionCalls_seqAll {Name : Type v}
(expressions : List (Expr Name Sym))
(hzero : ∀ expression, expression ∈ expressions →
Expr.guaranteedConsumes expression = false) :
Expr.samePositionCalls (Expr.seqAll expressions) =
expressions.flatMap Expr.samePositionCalls := by
induction expressions with
| nil => rfl
| cons expression expressions ih =>
change Expr.samePositionCalls (.seq expression (Expr.seqAll expressions)) = _
rw [Expr.samePositionCalls]
simp [hzero expression (by simp), ih (fun item hitem =>
hzero item (by simp [hitem]))]
omit [FiniteEnum Sym] [DecidableEq Sym] in
theorem guaranteedConsumes_seqAll_false {Name : Type v}
(expressions : List (Expr Name Sym))
(hzero : ∀ expression, expression ∈ expressions →
Expr.guaranteedConsumes expression = false) :
Expr.guaranteedConsumes (Expr.seqAll expressions) = false := by
induction expressions with
| nil => rfl
| cons expression expressions ih =>
change Expr.guaranteedConsumes
(.seq expression (Expr.seqAll expressions)) = false
rw [Expr.guaranteedConsumes, hzero expression (by simp)]
simp only [Bool.false_or]
exact ih (fun item hitem => hzero item (by simp [hitem]))
omit [FiniteEnum Sym] [DecidableEq Sym] in
theorem guaranteedConsumes_transport {Name : Type v} {Name' : Type w}
(equiv : FiniteEquiv Name Name')
(expression : Expr Name Sym) :
Expr.guaranteedConsumes (transportExpr equiv expression) =
Expr.guaranteedConsumes expression := by
induction expression <;> simp_all [transportExpr, Expr.guaranteedConsumes]
omit [FiniteEnum Sym] [DecidableEq Sym] in
theorem samePositionCalls_transport {Name : Type v} {Name' : Type w}
(equiv : FiniteEquiv Name Name')
(expression : Expr Name Sym) :
Expr.samePositionCalls (transportExpr equiv expression) =
(Expr.samePositionCalls expression).map equiv := by
induction expression with
| eps | fail | any | term | nonterm => simp [transportExpr, Expr.samePositionCalls]
| alt left right ihLeft ihRight =>
simp [transportExpr, Expr.samePositionCalls, ihLeft, ihRight]
| notP body ih => simp [transportExpr, Expr.samePositionCalls, ih]
| seq left right ihLeft ihRight =>
simp only [transportExpr, Expr.samePositionCalls, ihLeft, ihRight,
List.map_append]
rw [guaranteedConsumes_transport]
split <;> simp
omit [DecidableEq Sym] [DecidableEq Gamma] in
theorem stateAlternatives_calls_empty (A : Automaton Sym Q Gamma d k)
(target : Q) :
(stateAlternatives A target).flatMap Expr.samePositionCalls = [] := by
unfold stateAlternatives
generalize (transitionInputs (Sym := Sym) (Q := Q) (Gamma := Gamma)
(d := d) (k := k)) = inputs
induction inputs with
| nil => rfl
| cons input inputs ih =>
by_cases htarget : (A.delta input.1 input.2.1 input.2.2).1 = target
· simp [htarget, ih]
· simp [htarget, ih]
omit [DecidableEq Sym] [DecidableEq Q] in
theorem labelAlternatives_calls_empty (A : Automaton Sym Q Gamma d k)
(target : Gamma) :
(labelAlternatives A target).flatMap Expr.samePositionCalls = [] := by
unfold labelAlternatives
generalize (transitionInputs (Sym := Sym) (Q := Q) (Gamma := Gamma)
(d := d) (k := k)) = inputs
induction inputs with
| nil => rfl
| cons input inputs ih =>
by_cases htarget : (A.delta input.1 input.2.1 input.2.2).2.1 = target
· simp [htarget, ih]
· simp [htarget, ih]
omit [FiniteEnum Sym] [DecidableEq Sym] [FiniteEnum Q] [DecidableEq Q]
[FiniteEnum Gamma] [DecidableEq Gamma] in
theorem pathAlternative_call (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) (tail : BoundedPath d k) (port : Fin d)
(input : TransitionInput Sym Q Gamma d k) {callee}
(hcall : callee ∈ Expr.samePositionCalls
(pathAlternative A hdistance tail port input)) :
callee = NT.path tail := by
unfold pathAlternative at hcall
split at hcall <;>
simp [Expr.samePositionCalls, Expr.guaranteedConsumes] at hcall
exact hcall.2
omit [FiniteEnum Sym] [DecidableEq Sym] [FiniteEnum Q] [DecidableEq Q]
[DecidableEq Gamma] in
theorem observationFact_call (neighbourhood : Unfold d Gamma k)
(path : BoundedPath d k) {callee}
(hcall : callee ∈ Expr.samePositionCalls
(observationFact (Q := Q) (Sym := Sym) neighbourhood path)) :
callee = NT.path path ∨ ∃ label, callee = NT.label label := by
unfold observationFact at hcall
cases hobs : observe neighbourhood path with
| none =>
rw [hobs] at hcall
exact Or.inl (by simpa [Expr.samePositionCalls] using hcall)
| some observation =>
cases observation with
| none =>
rw [hobs] at hcall
let facts : List (Expr (NT Q Gamma d k) Sym) :=
(FiniteEnum.elems (α := Gamma)).map fun label =>
Expr.notP (.seq (.nonterm (NT.path path)) (.nonterm (.label label)))
have hzero : ∀ expression, expression ∈ facts →
Expr.guaranteedConsumes expression = false := by
intro expression hexpression
simp [facts] at hexpression
obtain ⟨label, hlabel, rfl⟩ := hexpression
rfl
change callee ∈ [NT.path path] ++
Expr.samePositionCalls (Expr.seqAll facts) at hcall
rw [samePositionCalls_seqAll facts hzero] at hcall
rcases List.mem_append.mp hcall with hpath | hfact
· exact Or.inl (by simpa using hpath)
· rw [List.mem_flatMap] at hfact
obtain ⟨expression, hexpression, hcallee⟩ := hfact
simp [facts] at hexpression
obtain ⟨label, hlabel, rfl⟩ := hexpression
simp [Expr.samePositionCalls, Expr.guaranteedConsumes] at hcallee
rcases hcallee with hpath | hlabel
· exact Or.inl hpath
· exact Or.inr ⟨label, hlabel⟩
| some label =>
rw [hobs] at hcall
simp [Expr.pos, Expr.samePositionCalls, Expr.guaranteedConsumes] at hcall
rcases hcall with hpath | hlabel
· exact Or.inl hpath
· exact Or.inr ⟨label, hlabel⟩
omit [FiniteEnum Sym] [DecidableEq Sym] [FiniteEnum Q] [DecidableEq Q]
[DecidableEq Gamma] in
theorem observationFact_notGuaranteed (neighbourhood : Unfold d Gamma k)
(path : BoundedPath d k) :
Expr.guaranteedConsumes
(observationFact (Q := Q) (Sym := Sym) neighbourhood path) = false := by
unfold observationFact
cases hobs : observe neighbourhood path with
| none => rfl
| some observation =>
cases observation with
| none =>
let facts : List (Expr (NT Q Gamma d k) Sym) :=
(FiniteEnum.elems (α := Gamma)).map fun label =>
Expr.notP (.seq (.nonterm (NT.path path)) (.nonterm (.label label)))
have hzero : ∀ expression, expression ∈ facts →
Expr.guaranteedConsumes expression = false := by
intro expression hexpression
simp [facts] at hexpression
obtain ⟨label, hlabel, rfl⟩ := hexpression
rfl
have hseq := guaranteedConsumes_seqAll_false facts hzero
change Expr.guaranteedConsumes
(.seq (.pos (.nonterm (NT.path path))) (Expr.seqAll facts)) = false
simp [Expr.pos, Expr.guaranteedConsumes, hseq]
| some label =>
rfl
omit [DecidableEq Sym] in
theorem rawGrammar_call_rank_lt (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) {caller callee : NT Q Gamma d k}
(hcall : callee ∈ Expr.samePositionCalls
((rawGrammar A hdistance).rule caller)) :
ntRank callee < ntRank caller := by
cases caller with
| state control =>
change callee ∈ Expr.samePositionCalls
(Expr.altAll ((if control = A.init then [.notP .any] else []) ++
stateAlternatives A control)) at hcall
rw [samePositionCalls_altAll, List.flatMap_append,
stateAlternatives_calls_empty] at hcall
split at hcall <;> simp [Expr.samePositionCalls] at hcall
| label label =>
change callee ∈ Expr.samePositionCalls
(Expr.altAll (labelAlternatives A label)) at hcall
rw [samePositionCalls_altAll, labelAlternatives_calls_empty] at hcall
simp at hcall
| anyStar =>
simp [rawGrammar, Expr.samePositionCalls, Expr.guaranteedConsumes] at hcall
| path path =>
change callee ∈ Expr.samePositionCalls (pathRule A hdistance path) at hcall
unfold pathRule at hcall
split at hcall
· simp [Expr.samePositionCalls] at hcall
· rename_i hpath
rw [samePositionCalls_altAll] at hcall
simp only [List.mem_flatMap, List.mem_map] at hcall
obtain ⟨expression, ⟨input, hinput, rfl⟩, hcall⟩ := hcall
have hcallee := pathAlternative_call A hdistance _ _ input hcall
subst callee
dsimp [ntRank]
rw [hpath]
simp
| neighbourhood neighbourhood =>
change callee ∈ Expr.samePositionCalls
(neighbourhoodRule (Q := Q) (Sym := Sym) neighbourhood) at hcall
unfold neighbourhoodRule at hcall
rw [samePositionCalls_seqAll _ (by
intro expression hexpression
simp at hexpression
obtain ⟨path, hpath, rfl⟩ := hexpression
exact observationFact_notGuaranteed (Q := Q) (Sym := Sym) neighbourhood path)] at hcall
simp only [List.mem_flatMap, List.mem_map] at hcall
obtain ⟨fact, ⟨path, hpath, rfl⟩, hcall⟩ := hcall
rcases observationFact_call (Q := Q) (Sym := Sym) neighbourhood path hcall with
hpathCall | ⟨label, hlabelCall⟩
· subst callee
simp [ntRank]
exact Nat.lt_succ_of_le path.length_le
· subst callee
simp [ntRank]
| accepts =>
change callee ∈ Expr.samePositionCalls (acceptsRule A) at hcall
unfold acceptsRule at hcall
rw [samePositionCalls_altAll] at hcall
simp only [List.mem_flatMap, List.mem_map] at hcall
obtain ⟨expression, ⟨control, hcontrol, rfl⟩, hcall⟩ := hcall
simp [Expr.samePositionCalls, Expr.guaranteedConsumes] at hcall
rcases hcall with rfl | rfl <;> simp [ntRank]
end Grammar
section RelationalTotality
variable {Sym : Type} {n : Nat} [DecidableEq Sym]
def HasResult (G : Grammar (Fin n) Sym) (expr : Expr (Fin n) Sym)
(input : List Sym) : Prop :=
∃ result, Eval G expr input result
/-- Relational evaluation exists when proper-suffix calls and the explicitly
same-position calls already have results. This proof does not invoke the
executable table evaluator. -/
theorem expr_hasResult_of_calls (G : Grammar (Fin n) Sym)
{expr : Expr (Fin n) Sym} {input : List Sym}
(shorter : ∀ suffix, suffix <:+ input → suffix.length < input.length →
∀ name, HasResult G (.nonterm name) suffix)
(same : ∀ name, name ∈ Expr.samePositionCalls expr →
HasResult G (.nonterm name) input) :
HasResult G expr input := by
induction expr generalizing input with
| eps => exact ⟨.ok input, .eps⟩
| fail => exact ⟨.err, .fail⟩
| any =>
cases input with
| nil => exact ⟨.err, .anyNil⟩
| cons symbol rest => exact ⟨.ok rest, .anyOk⟩
| term expected =>
cases input with
| nil => exact ⟨.err, .termNil⟩
| cons actual rest =>
by_cases hsame : expected = actual
· subst actual
exact ⟨.ok rest, .termOk⟩
· exact ⟨.err, .termNe hsame⟩
| nonterm name => exact same name (by simp [Expr.samePositionCalls])
| seq left right ihLeft ihRight =>
obtain ⟨leftResult, hleft⟩ := ihLeft shorter (fun name hname =>
same name (by simp [Expr.samePositionCalls, hname]))
cases leftResult with
| err => exact ⟨.err, .seqErr hleft⟩
| ok middle =>
have hmiddleSuffix : middle <:+ input := eval_remainder_suffix hleft
have hrightShorter : ∀ suffix, suffix <:+ middle →
suffix.length < middle.length →
∀ name, HasResult G (.nonterm name) suffix := by
intro suffix hsuffix hlength name
exact shorter suffix (hsuffix.trans hmiddleSuffix)
(Nat.lt_of_lt_of_le hlength hmiddleSuffix.length_le) name
have hrightSame : ∀ name,
name ∈ Expr.samePositionCalls right →
HasResult G (.nonterm name) middle := by
intro name hname
rcases Nat.lt_or_eq_of_le hmiddleSuffix.length_le with hlt | heq
· exact shorter middle hmiddleSuffix hlt name
· have hm : middle = input := hmiddleSuffix.eq_of_length heq
subst middle
by_cases hconsumes : Expr.guaranteedConsumes left = true
· exact (Nat.ne_of_lt
(guaranteedConsumes_sound hleft hconsumes) rfl).elim
· exact same name (by
simp [Expr.samePositionCalls, hconsumes, hname])
obtain ⟨rightResult, hright⟩ :=
ihRight hrightShorter hrightSame
exact ⟨rightResult, .seqOk hleft hright⟩
| alt left right ihLeft ihRight =>
obtain ⟨leftResult, hleft⟩ := ihLeft shorter (fun name hname =>
same name (by simp [Expr.samePositionCalls, hname]))
cases leftResult with
| ok rest => exact ⟨.ok rest, .altOk hleft⟩
| err =>
obtain ⟨rightResult, hright⟩ := ihRight shorter (fun name hname =>
same name (by simp [Expr.samePositionCalls, hname]))
exact ⟨rightResult, .altErr hleft hright⟩
| notP body ih =>
obtain ⟨bodyResult, hbody⟩ := ih shorter (fun name hname =>
same name (by simpa [Expr.samePositionCalls] using hname))
cases bodyResult with
| ok rest => exact ⟨.err, .notOk hbody⟩
| err => exact ⟨.ok input, .notErr hbody⟩
theorem ordered_nonterm_hasResult (G : Grammar (Fin n) Sym)
(schedule : RankSchedule G) (input : List Sym)
(shorter : ∀ suffix, suffix <:+ input → suffix.length < input.length →
∀ name, HasResult G (.nonterm name) suffix)
(before after : List (Fin n))
(horder : schedule.order = before ++ after)
(hbefore : ∀ name, name ∈ before → HasResult G (.nonterm name) input) :
∀ name, name ∈ before ++ after → HasResult G (.nonterm name) input := by
induction after generalizing before with
| nil =>
intro name hname
exact hbefore name (by simpa using hname)
| cons caller after ih =>
have hrule : HasResult G (G.rule caller) input :=
expr_hasResult_of_calls G shorter (fun callee hcall =>
hbefore callee (schedule.earlier horder hcall))
have hcaller : HasResult G (.nonterm caller) input := by
obtain ⟨result, hresult⟩ := hrule
exact ⟨result, .nonterm hresult⟩
have hnewBefore : ∀ name, name ∈ before ++ [caller] →
HasResult G (.nonterm name) input := by
intro name hname
rcases List.mem_append.mp hname with hold | hnew
· exact hbefore name hold
· have : name = caller := by simpa using hnew
subst name
exact hcaller
have hnewOrder : schedule.order = (before ++ [caller]) ++ after := by
simpa [List.append_assoc] using horder
have hrec := ih (before := before ++ [caller]) hnewOrder hnewBefore
intro name hname
exact hrec name (by simpa [List.append_assoc] using hname)
theorem scheduled_suffix_nonterm_hasResult (G : Grammar (Fin n) Sym)
(schedule : RankSchedule G) (input : List Sym) :
∀ suffix, suffix <:+ input → ∀ name,
HasResult G (.nonterm name) suffix := by
induction input with
| nil =>
have hcurrent := ordered_nonterm_hasResult G schedule []
(fun suffix _ hlength => by simp at hlength) [] schedule.order
(by simp) (by simp)
intro suffix hsuffix name
have hsuffixNil : suffix = [] := hsuffix.eq_of_length
(Nat.eq_zero_of_le_zero hsuffix.length_le)
subst suffix
exact hcurrent name (by simp [schedule.complete name])
| cons symbol rest ih =>
have hshorter : ∀ suffix, suffix <:+ symbol :: rest →
suffix.length < (symbol :: rest).length →
∀ name, HasResult G (.nonterm name) suffix := by
intro suffix hsuffix hlength name
exact ih suffix (TableEval.suffix_of_proper_suffix_cons hsuffix hlength) name
have hcurrent := ordered_nonterm_hasResult G schedule (symbol :: rest)
hshorter [] schedule.order (by simp) (by simp)
intro suffix hsuffix name
by_cases heq : suffix = symbol :: rest
· subst suffix
exact hcurrent name (by simp [schedule.complete name])
· have hlength : suffix.length < (symbol :: rest).length :=
Nat.lt_of_le_of_ne hsuffix.length_le (fun hlen =>
heq (hsuffix.eq_of_length hlen))
exact ih suffix (TableEval.suffix_of_proper_suffix_cons hsuffix hlength) name
/-- Every rank-scheduled finite grammar is relationally total by an induction
independent of `evalTable`. -/
theorem total_of_rankSchedule (G : Grammar (Fin n) Sym)
(schedule : RankSchedule G) : Total G := by
intro expr input
apply expr_hasResult_of_calls G
· intro suffix hsuffix hlength name
exact scheduled_suffix_nonterm_hasResult G schedule input suffix hsuffix name
· intro name hname
exact scheduled_suffix_nonterm_hasResult G schedule input input (by simp) name
end RelationalTotality
section RankOrder
def rankBucket (rank : Fin n → Nat) (level : Nat) : List (Fin n) :=
(FiniteEnum.dedup (List.finRange n)).filter fun name => rank name = level
def rankOrder (rank : Fin n → Nat) : Nat → List (Fin n)
| 0 => rankBucket rank 0
| bound + 1 => rankOrder rank bound ++ rankBucket rank (bound + 1)
@[simp] theorem mem_rankBucket (rank : Fin n → Nat) (level : Nat)
(name : Fin n) : name ∈ rankBucket rank level ↔ rank name = level := by
simp [rankBucket]
@[simp] theorem mem_rankOrder (rank : Fin n → Nat) (bound : Nat)
(name : Fin n) : name ∈ rankOrder rank bound ↔ rank name ≤ bound := by
induction bound with
| zero => simp [rankOrder]
| succ bound ih =>
simp only [rankOrder, List.mem_append, ih, mem_rankBucket]
omega
theorem rankBucket_nodup (rank : Fin n → Nat) (level : Nat) :
(rankBucket rank level).Nodup := by
exact (FiniteEnum.nodup_dedup (List.finRange n)).filter _
theorem rankOrder_nodup (rank : Fin n → Nat) (bound : Nat) :
(rankOrder rank bound).Nodup := by
induction bound with
| zero => exact rankBucket_nodup rank 0
| succ bound ih =>
rw [rankOrder, List.nodup_append]
refine ⟨ih, rankBucket_nodup rank (bound + 1), ?_⟩
intro left hleft right hright heq
have hle : rank left ≤ bound := (mem_rankOrder rank bound left).mp hleft
have hrightRank : rank right = bound + 1 :=
(mem_rankBucket rank (bound + 1) right).mp hright
subst right
omega
theorem pairwise_of_all {α : Type} (relation : α → α → Prop)
(items : List α)
(hall : ∀ left, left ∈ items → ∀ right, right ∈ items →
relation left right) : items.Pairwise relation := by
induction items with
| nil => simp
| cons head tail ih =>
rw [List.pairwise_cons]
constructor
· intro item hitem
exact hall head (by simp) item (by simp [hitem])
· exact ih (fun left hleft right hright =>
hall left (by simp [hleft]) right (by simp [hright]))
theorem rankBucket_pairwise (rank : Fin n → Nat) (level : Nat) :
(rankBucket rank level).Pairwise fun left right => rank left ≤ rank right := by
apply pairwise_of_all
intro left hleft right hright
rw [(mem_rankBucket rank level left).mp hleft,
(mem_rankBucket rank level right).mp hright]
omega
theorem rankOrder_pairwise (rank : Fin n → Nat) (bound : Nat) :
(rankOrder rank bound).Pairwise fun left right => rank left ≤ rank right := by
induction bound with
| zero => exact rankBucket_pairwise rank 0
| succ bound ih =>
rw [rankOrder, List.pairwise_append]
refine ⟨ih, rankBucket_pairwise rank (bound + 1), ?_⟩
intro left hleft right hright
have hle : rank left ≤ bound := (mem_rankOrder rank bound left).mp hleft
have hrightRank : rank right = bound + 1 :=
(mem_rankBucket rank (bound + 1) right).mp hright
omega
theorem earlier_of_rankOrder (rank : Fin n → Nat) (bound : Nat)
{before after : List (Fin n)} {caller callee : Fin n}
(horder : rankOrder rank bound = before ++ caller :: after)
(hcallee : rank callee < rank caller)
(hcomplete : rank callee ≤ bound) : callee ∈ before := by
have hmem : callee ∈ before ++ caller :: after := by
rw [← horder]
exact (mem_rankOrder rank bound callee).2 hcomplete
rcases List.mem_append.mp hmem with hbefore | hsuffix
· exact hbefore
· rcases List.mem_cons.mp hsuffix with heq | hafter
· subst callee
omega
· have hsorted : (before ++ caller :: after).Pairwise
(fun left right => rank left ≤ rank right) := by
rw [← horder]
exact rankOrder_pairwise rank bound
have hsuffixSorted : (caller :: after).Pairwise
(fun left right => rank left ≤ rank right) :=
(List.pairwise_append.mp hsorted).2.1
have hcallerLe : rank caller ≤ rank callee :=
(List.pairwise_cons.mp hsuffixSorted).1 callee hafter
omega
end RankOrder
section GeneratedSchedule
variable {Sym Q : Type} {Gamma : Type u} {d k : Nat}
variable [FiniteEnum Sym] [DecidableEq Sym]
variable [FiniteEnum Q] [DecidableEq Q]
variable [FiniteEnum Gamma] [DecidableEq Gamma]
/-- The source rank transported to the canonical finite nonterminal carrier. -/
def canonicalRank
(name : Fin (FiniteEnum.card (NT Q Gamma d k))) : Nat :=
ntRank ((canonicalEquiv (Q := Q) (Gamma := Gamma) (d := d) (k := k)).symm name)
omit [FiniteEnum Q] [DecidableEq Q] [FiniteEnum Gamma] [DecidableEq Gamma] in
theorem ntRank_le (name : NT Q Gamma d k) : ntRank name ≤ k + 2 := by
cases name with
| state _ | label _ | anyStar => simp [ntRank]
| path path =>
dsimp [ntRank]
have := path.length_le
omega
| neighbourhood _ | accepts => simp [ntRank]
omit [DecidableEq Sym] in
theorem canonicalGrammar_call_rank_lt (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk)
{caller callee : Fin (FiniteEnum.card (NT Q Gamma d k))}
(hcall : callee ∈ Expr.samePositionCalls
((canonicalGrammar A hdistance).rule caller)) :
canonicalRank callee < canonicalRank caller := by
change callee ∈ Expr.samePositionCalls
(transportExpr canonicalEquiv
((rawGrammar A hdistance).rule (canonicalEquiv.symm caller))) at hcall
rw [samePositionCalls_transport] at hcall
obtain ⟨rawCallee, hrawCall, rfl⟩ := List.mem_map.mp hcall
simpa [canonicalRank] using
(rawGrammar_call_rank_lt A hdistance hrawCall)
/-- The generated canonical grammar is ordered by increasing same-position
rank. The certificate includes `SELF`, whose recursive path call has the
strictly shorter tail rank. -/
def canonicalGrammar_rankSchedule (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) : RankSchedule (canonicalGrammar A hdistance) where
order := rankOrder canonicalRank (k + 2)
nodup := rankOrder_nodup canonicalRank (k + 2)
complete name := (mem_rankOrder canonicalRank (k + 2) name).2 (by
exact ntRank_le (canonicalEquiv.symm name))
earlier horder hcall :=
earlier_of_rankOrder canonicalRank (k + 2) horder
(canonicalGrammar_call_rank_lt A hdistance hcall)
(ntRank_le (canonicalEquiv.symm _))
/-- Relational totality is proved by the structural existence induction above;
the executable table evaluator is not used. -/
theorem canonicalGrammar_total (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) : Total (canonicalGrammar A hdistance) :=
total_of_rankSchedule _ (canonicalGrammar_rankSchedule A hdistance)
theorem rawGrammar_total (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) : Total (rawGrammar A hdistance) :=
(total_transport_iff canonicalEquiv (rawGrammar A hdistance)).mp
(canonicalGrammar_total A hdistance)
omit [DecidableEq Sym] in
/-- `Path([])` always succeeds without consuming input. -/
theorem rootPath_ok (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) (input : List Sym) :
Eval (rawGrammar A hdistance) (.nonterm (.path (rootPath d k))) input
(.ok input) := by
apply Eval.nonterm
simpa [rawGrammar, pathRule, rootPath] using
(Eval.eps : Eval (rawGrammar A hdistance) .eps input (.ok input))
omit [DecidableEq Sym] in
/-- The root fact of a missing neighbourhood rejects because it is the
negative lookahead `!Path([])`, while `Path([])` is epsilon. -/
theorem missing_root_fact_err (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) (input : List Sym) :
Eval (rawGrammar A hdistance)
(observationFact (Q := Q) (Sym := Sym) (.missing : Unfold d Gamma k)
(rootPath d k)) input .err := by
simpa [observationFact, observe, observeAt, rootPath] using
(Eval.notOk (rootPath_ok A hdistance input))
omit [DecidableEq Sym] in
/-- A top-level missing unfolding can never be the neighbourhood of a run
scaffold. Its rule starts with the failing root fact above. -/
theorem neighbourhood_missing_err (A : Automaton Sym Q Gamma d k)
(hdistance : A.DistanceOk) (input : List Sym) :
Eval (rawGrammar A hdistance)
(.nonterm (.neighbourhood (.missing : Unfold d Gamma k))) input .err := by
apply Eval.nonterm
change Eval (rawGrammar A hdistance)
(neighbourhoodRule (Q := Q) (Sym := Sym) (.missing : Unfold d Gamma k))
input .err
unfold neighbourhoodRule
obtain ⟨rest, hrest⟩ := boundedPath_elems_starts_root d k
rw [hrest]
exact Eval.seqErr (missing_root_fact_err A hdistance input)
end GeneratedSchedule
namespace GrammarExamples
def emptyPath : BoundedPath 1 1 := ⟨[], by simp⟩
def childPath : BoundedPath 1 1 := ⟨[0], by simp⟩
def unlabelledRoot : Unfold 1 Bool 1 :=
.node none (fun _ => .missing)
def labelledRoot : Unfold 1 Bool 1 :=
.node (some true) (fun _ => .leaf none)
example : observe unlabelledRoot emptyPath = some none := by decide
example : observe unlabelledRoot childPath = none := by decide
example : observe labelledRoot emptyPath = some (some true) := by decide
example : observe labelledRoot childPath = some none := by decide
example (left right : Unfold 1 Bool 1) :
left = right ↔ ∀ path : BoundedPath 1 1,
observe left path = observe right path :=
unfold_eq_iff_observe_all left right
example : NT Unit Bool 1 1 := .accepts
example : (FiniteEnum.elems (α := NT Unit Bool 1 1)).length > 0 := by decide
def selfGrammarMachine : Automaton Bool Unit Unit 1 1 where
delta _ _ _ := ((), (), fun _ => .self)
init := ()
accept _ := true
theorem selfGrammarDistance : selfGrammarMachine.DistanceOk := by
intro control symbol neighbourhood port path hpath
simp [selfGrammarMachine] at hpath
example :
(rawGrammar selfGrammarMachine selfGrammarDistance).rule (.path emptyPath) =
.eps := by rfl
example : Total (rawGrammar selfGrammarMachine selfGrammarDistance) :=
rawGrammar_total selfGrammarMachine selfGrammarDistance
example : RankSchedule (canonicalGrammar selfGrammarMachine selfGrammarDistance) :=
canonicalGrammar_rankSchedule selfGrammarMachine selfGrammarDistance
end GrammarExamples
end Theorem16
end FPRD