ai-free / forth /Forth /Eval.lean
SNAPKITTYWEST's picture
October 2026 main drop: sync from GitHub at 8d0f04c
9ad95e5 verified
Raw History Blame Contribute Delete
10.4 kB
import Forth.Semantics
/-!
# Forth.Eval
A fuel-bounded executable interpreter for the Forth reference semantics, proved sound
against `Run`: if it returns a result, `Run` derives that result. It lets concrete facts about
the reference semantics (including recursive words) be established by evaluation.
-/
namespace WordDialect
namespace Forth
/-- Run `b` from `st` with at most `fuel` nested evaluation steps; `none` means out of fuel. -/
def eval {n : Nat} (defs : List Block) : Nat → Block → FState n → Option (Res n)
| 0, _, _ => none
| _ + 1, .nil, st => some (.ok st)
| _ + 1, .exit _, st => some (.exit st)
| _ + 1, .leave _, ⟨d, m, _ :: _ :: rs⟩ => some (.leave ⟨d, m, rs⟩)
| _ + 1, .leave _, _ => some (.error .returnUnderflow)
| f + 1, .op o rest, st =>
match o.sem st with
| .ok st' => eval defs f rest st'
| .error t => some (.error t)
| _ + 1, .ite _ _ _, ⟨[], _, _⟩ => some (.error .stackUnderflow)
| f + 1, .ite t e rest, ⟨c :: d, m, rs⟩ =>
match eval defs f (if Word.isTrue c then t else e) ⟨d, m, rs⟩ with
| none => none
| some (.error x) => some (.error x)
| some (.exit st1) => some (.exit st1)
| some (.leave st1) => some (.leave st1)
| some (.ok st1) => eval defs f rest st1
| f + 1, .untilL body rest, st =>
match eval defs f body st with
| none => none
| some (.error x) => some (.error x)
| some (.exit st1) => some (.exit st1)
| some (.leave st1) => some (.leave st1)
| some (.ok ⟨[], _, _⟩) => some (.error .stackUnderflow)
| some (.ok ⟨c :: d, m, rs⟩) =>
if Word.isTrue c then eval defs f rest ⟨d, m, rs⟩
else eval defs f (.untilL body rest) ⟨d, m, rs⟩
| f + 1, .whileL cond body rest, st =>
match eval defs f cond st with
| none => none
| some (.error x) => some (.error x)
| some (.exit st1) => some (.exit st1)
| some (.leave st1) => some (.leave st1)
| some (.ok ⟨[], _, _⟩) => some (.error .stackUnderflow)
| some (.ok ⟨c :: d, m, rs⟩) =>
if Word.isTrue c then
match eval defs f body ⟨d, m, rs⟩ with
| none => none
| some (.error x) => some (.error x)
| some (.exit st2) => some (.exit st2)
| some (.leave st2) => some (.leave st2)
| some (.ok st2) => eval defs f (.whileL cond body rest) st2
else eval defs f rest ⟨d, m, rs⟩
| f + 1, .call i rest, st =>
match defs[i]? with
| none => some (.error .badPc)
| some body =>
match eval defs f body st with
| none => none
| some (.error x) => some (.error x)
| some (.exit st1) => eval defs f rest st1
| some (.leave st1) => eval defs f rest st1
| some (.ok st1) => eval defs f rest st1
| f + 1, .doLoop body rest, ⟨i :: l :: d, m, rs⟩ =>
match eval defs f body ⟨d, m, i :: l :: rs⟩ with
| none => none
| some (.error x) => some (.error x)
| some (.exit st1) => some (.exit st1)
| some (.leave st1) => eval defs f rest st1
| some (.ok ⟨d1, m1, i1 :: l1 :: rs1⟩) =>
if i1 + 1#n = l1 then eval defs f rest ⟨d1, m1, rs1⟩
else eval defs f (.doLoop body rest) ⟨(i1 + 1#n) :: l1 :: d1, m1, rs1⟩
| some (.ok _) => some (.error .returnUnderflow)
| _ + 1, .doLoop _ _, _ => some (.error .stackUnderflow)
| f + 1, .plusLoop body rest, ⟨i :: l :: d, m, rs⟩ =>
match eval defs f body ⟨d, m, i :: l :: rs⟩ with
| none => none
| some (.error x) => some (.error x)
| some (.exit st1) => some (.exit st1)
| some (.leave st1) => eval defs f rest st1
| some (.ok ⟨k :: d1, m1, i1 :: l1 :: rs1⟩) =>
if loopCrossed i1 l1 k then eval defs f rest ⟨d1, m1, rs1⟩
else eval defs f (.plusLoop body rest) ⟨(i1 + k) :: l1 :: d1, m1, rs1⟩
| some (.ok ⟨[], _, _ :: _ :: _⟩) => some (.error .stackUnderflow)
| some (.ok _) => some (.error .returnUnderflow)
| _ + 1, .plusLoop _ _, _ => some (.error .stackUnderflow)
theorem eval_sound {n : Nat} {defs : List Block} :
∀ (fuel : Nat) (b : Block) (st : FState n) (r : Res n),
eval defs fuel b st = some r → Run defs b st r := by
intro fuel
induction fuel with
| zero => intro b st r h; simp [eval] at h
| succ f ih =>
intro b st r h
cases b with
| nil => simp only [eval, Option.some.injEq] at h; subst h; exact .nil
| exit rest => simp only [eval, Option.some.injEq] at h; subst h; exact .exit
| leave rest =>
obtain ⟨d, m, rs⟩ := st
rcases rs with _ | ⟨a, _ | ⟨b, rs⟩⟩
· simp only [eval, Option.some.injEq] at h; subst h; exact .leaveUnder (by simp)
· simp only [eval, Option.some.injEq] at h; subst h; exact .leaveUnder (by simp)
· simp only [eval, Option.some.injEq] at h; subst h; exact .leaveOk
| op o rest =>
simp only [eval] at h
split at h
· exact .opOk ‹_› (ih _ _ _ h)
· cases h; exact .opErr ‹_›
| ite t e rest =>
obtain ⟨d, m, rs⟩ := st
cases d with
| nil => simp only [eval, Option.some.injEq] at h; subst h; exact .iteUnder
| cons c d =>
simp only [eval] at h
split at h
· cases h
· rename_i x hx
cases h
cases hc : Word.isTrue c
· rw [hc] at hx; exact .iteFalseStop hc (ih _ _ _ hx) rfl
· rw [hc] at hx; exact .iteTrueStop hc (ih _ _ _ hx) rfl
· rename_i x hx
cases h
cases hc : Word.isTrue c
· rw [hc] at hx; exact .iteFalseStop hc (ih _ _ _ hx) rfl
· rw [hc] at hx; exact .iteTrueStop hc (ih _ _ _ hx) rfl
· rename_i x hx
cases h
cases hc : Word.isTrue c
· rw [hc] at hx; exact .iteFalseStop hc (ih _ _ _ hx) rfl
· rw [hc] at hx; exact .iteTrueStop hc (ih _ _ _ hx) rfl
· rename_i st1 hx
cases hc : Word.isTrue c
· rw [hc] at hx; exact .iteFalse hc (ih _ _ _ hx) (ih _ _ _ h)
· rw [hc] at hx; exact .iteTrue hc (ih _ _ _ hx) (ih _ _ _ h)
| untilL body rest =>
simp only [eval] at h
split at h
· cases h
· rename_i x hx; cases h; exact .untilBodyStop (ih _ _ _ hx) rfl
· rename_i x hx; cases h; exact .untilBodyStop (ih _ _ _ hx) rfl
· rename_i x hx; cases h; exact .untilBodyStop (ih _ _ _ hx) rfl
· rename_i m rs hx; cases h; exact .untilUnder (ih _ _ _ hx)
· rename_i c d m rs hx
cases hc : Word.isTrue c
· rw [hc] at h; exact .untilAgain (ih _ _ _ hx) hc (ih _ _ _ h)
· rw [hc] at h; exact .untilDone (ih _ _ _ hx) hc (ih _ _ _ h)
| whileL cond body rest =>
simp only [eval] at h
split at h
· cases h
· rename_i x hx; cases h; exact .whileCondStop (ih _ _ _ hx) rfl
· rename_i x hx; cases h; exact .whileCondStop (ih _ _ _ hx) rfl
· rename_i x hx; cases h; exact .whileCondStop (ih _ _ _ hx) rfl
· rename_i m rs hx; cases h; exact .whileUnder (ih _ _ _ hx)
· rename_i c d m rs hx
split at h
· rename_i hc
split at h
· cases h
· rename_i x hb; cases h; exact .whileBodyStop (ih _ _ _ hx) hc (ih _ _ _ hb) rfl
· rename_i x hb; cases h; exact .whileBodyStop (ih _ _ _ hx) hc (ih _ _ _ hb) rfl
· rename_i x hb; cases h; exact .whileBodyStop (ih _ _ _ hx) hc (ih _ _ _ hb) rfl
· rename_i st2 hb; exact .whileAgain (ih _ _ _ hx) hc (ih _ _ _ hb) (ih _ _ _ h)
· rename_i hc
exact .whileExit (ih _ _ _ hx) (by simpa using hc) (ih _ _ _ h)
| call i rest =>
simp only [eval] at h
split at h
· cases h; exact .callUndef ‹_›
· rename_i body hi
split at h
· cases h
· rename_i x hx; cases h; exact .callErr hi (ih _ _ _ hx)
· rename_i st1 hx; exact .callOk hi (ih _ _ _ hx) rfl (ih _ _ _ h)
· rename_i st1 hx; exact .callOk hi (ih _ _ _ hx) rfl (ih _ _ _ h)
· rename_i st1 hx; exact .callOk hi (ih _ _ _ hx) rfl (ih _ _ _ h)
| doLoop body rest =>
obtain ⟨d, m, rs⟩ := st
rcases d with _ | ⟨i, _ | ⟨l, d⟩⟩
· simp only [eval, Option.some.injEq] at h; subst h; exact .doUnder (by simp)
· simp only [eval, Option.some.injEq] at h; subst h; exact .doUnder (by simp)
simp only [eval] at h
split at h
· cases h
· rename_i x hx; cases h; exact .doBodyStop (ih _ _ _ hx) rfl
· rename_i x hx; cases h; exact .doBodyStop (ih _ _ _ hx) rfl
· rename_i st1 hx; exact .doLeave (ih _ _ _ hx) (ih _ _ _ h)
· rename_i d1 m1 i1 l1 rs1 hx
split at h
· exact .doDone (ih _ _ _ hx) ‹_› (ih _ _ _ h)
· exact .doAgain (ih _ _ _ hx) ‹_› (ih _ _ _ h)
· rename_i st1 hne hx
cases h
obtain ⟨d1, m1, rs1⟩ := st1
rcases rs1 with _ | ⟨a, _ | ⟨b, rs1⟩⟩
· exact .doTestUnder (ih _ _ _ hx) (by simp)
· exact .doTestUnder (ih _ _ _ hx) (by simp)
· exact absurd rfl (hne d1 m1 a b rs1)
| plusLoop body rest =>
obtain ⟨d, m, rs⟩ := st
rcases d with _ | ⟨i, _ | ⟨l, d⟩⟩
· simp only [eval, Option.some.injEq] at h; subst h; exact .plusUnder (by simp)
· simp only [eval, Option.some.injEq] at h; subst h; exact .plusUnder (by simp)
simp only [eval] at h
split at h
· cases h
· rename_i x hx; cases h; exact .plusBodyStop (ih _ _ _ hx) rfl
· rename_i x hx; cases h; exact .plusBodyStop (ih _ _ _ hx) rfl
· rename_i st1 hx; exact .plusLeave (ih _ _ _ hx) (ih _ _ _ h)
· rename_i k d1 m1 i1 l1 rs1 hx
split at h
· exact .plusDone (ih _ _ _ hx) ‹_› (ih _ _ _ h)
· rename_i hc; exact .plusAgain (ih _ _ _ hx) (by simpa using hc) (ih _ _ _ h)
· rename_i m1 i1 l1 rs1 hx; cases h; exact .plusStepUnder (ih _ _ _ hx)
· rename_i st1 hne1 hne2 hx
cases h
obtain ⟨d1, m1, rs1⟩ := st1
rcases rs1 with _ | ⟨a, _ | ⟨b, rs1⟩⟩
· exact .plusTestUnder (ih _ _ _ hx) (by simp)
· exact .plusTestUnder (ih _ _ _ hx) (by simp)
· cases d1 with
| nil => exact absurd rfl (hne2 m1 a b rs1)
| cons k d1 => exact absurd rfl (hne1 k d1 m1 a b rs1)
end Forth
end WordDialect