Research source · Plain text
StackMachine.lean
site/public/formal/persistent-stacks/lean/FPRDTransfer/StackMachine.lean
92 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: dce34cd921bc8e9a40b475d3d4c8bacc9e07af08256573402c7802136b59ee14
/-
Letter-synchronous multi-stack machines.
A machine has a fixed control carrier and a fixed number `s` of stacks. On each
arriving input symbol it performs at most one of `push`/`keep`/`pop` on *each*
stack, choosing on the basis of the control state, the symbol, and the top
symbol of every stack. There is no length advice and no post-input step.
Finiteness is imposed when this operational model is specialized for the
published transfer theorem.
-/
namespace FPRD
/-- The three stack actions available in one synchronous step. -/
inductive StackOp (A : Type) where
| push : A → StackOp A
| keep : StackOp A
| pop : StackOp A
namespace StackOp
variable {A : Type}
/-- Action on a stack held top-first. `pop` of the empty stack is a no-op,
via `List.tail [] = []`; this is the "empty pop" case the encoding must also
get right. -/
def apply : StackOp A → List A → List A
| .push a, l => a :: l
| .keep, l => l
| .pop, l => l.tail
@[simp] theorem apply_push (a : A) (l : List A) : (StackOp.push a).apply l = a :: l := rfl
@[simp] theorem apply_keep (l : List A) : (StackOp.keep : StackOp A).apply l = l := rfl
@[simp] theorem apply_pop (l : List A) : (StackOp.pop : StackOp A).apply l = l.tail := rfl
end StackOp
/-- A deterministic letter-synchronous `s`-stack machine.
`step q x tops` sees the top symbol of each stack (`none` for an empty stack)
and returns the next control state together with one action per stack. -/
structure StackMachine (Sym : Type) (Q : Type) (A : Type) (s : Nat) where
step : Q → Sym → (Fin s → Option A) → Q × (Fin s → StackOp A)
init : Q
accept : Q → Bool
/-- A machine configuration: control state plus the contents of every stack,
each held top-first. -/
structure Config (Q : Type) (A : Type) (s : Nat) where
ctrl : Q
stacks : Fin s → List A
namespace StackMachine
variable {Sym Q A : Type} {s : Nat}
/-- One synchronous step on a configuration. -/
def stepConfig (M : StackMachine Sym Q A s) (c : Config Q A s) (x : Sym) :
Config Q A s :=
let r := M.step c.ctrl x (fun j => (c.stacks j).head?)
{ ctrl := r.1, stacks := fun j => (r.2 j).apply (c.stacks j) }
@[simp] theorem stepConfig_ctrl (M : StackMachine Sym Q A s) (c : Config Q A s) (x : Sym) :
(M.stepConfig c x).ctrl = (M.step c.ctrl x (fun j => (c.stacks j).head?)).1 := rfl
@[simp] theorem stepConfig_stacks (M : StackMachine Sym Q A s) (c : Config Q A s)
(x : Sym) (j : Fin s) :
(M.stepConfig c x).stacks j =
((M.step c.ctrl x (fun j => (c.stacks j).head?)).2 j).apply (c.stacks j) := rfl
/-- The initial configuration: every stack empty. -/
def initConfig (M : StackMachine Sym Q A s) : Config Q A s where
ctrl := M.init
stacks := fun _ => []
/-- The configuration reached on a word. -/
def run (M : StackMachine Sym Q A s) (w : List Sym) : Config Q A s :=
w.foldl M.stepConfig M.initConfig
/-- Acceptance: final control state only. -/
def decides (M : StackMachine Sym Q A s) (w : List Sym) : Bool :=
M.accept (M.run w).ctrl
@[simp] theorem run_nil (M : StackMachine Sym Q A s) : M.run [] = M.initConfig := rfl
theorem run_append (M : StackMachine Sym Q A s) (w : List Sym) (x : Sym) :
M.run (w ++ [x]) = M.stepConfig (M.run w) x := by
simp [run, List.foldl_append]
end StackMachine
end FPRD