Add 'tsm-lean/' from commit '2e9061abead6f2daa464b39a79c17a949db30785'
git-subtree-dir: tsm-lean git-subtree-mainline:6592cd058dgit-subtree-split:2e9061abea
This commit is contained in:
commit
bd2e14214d
17 changed files with 943 additions and 0 deletions
1
tsm-lean/.gitignore
vendored
Normal file
1
tsm-lean/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/.lake
|
||||||
22
tsm-lean/Main.lean
Normal file
22
tsm-lean/Main.lean
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import TsmLean
|
||||||
|
|
||||||
|
open TsmLean.Core in
|
||||||
|
def main : IO UInt32 := do
|
||||||
|
-- Demo: 5 + 3, then * 2 = 16
|
||||||
|
let prog : Array Instr := #[
|
||||||
|
.push 5,
|
||||||
|
.push 3,
|
||||||
|
.add,
|
||||||
|
.push 2,
|
||||||
|
.mul,
|
||||||
|
.halt
|
||||||
|
]
|
||||||
|
let s₀ : State := { code := prog, pc := 0, stack := [] }
|
||||||
|
match run 100 s₀ with
|
||||||
|
| some s_final =>
|
||||||
|
IO.println s!"final stack: {repr s_final.stack}"
|
||||||
|
IO.println s!"final pc: {s_final.pc}"
|
||||||
|
return 0
|
||||||
|
| none =>
|
||||||
|
IO.eprintln "execution did not terminate within fuel"
|
||||||
|
return 1
|
||||||
58
tsm-lean/README.md
Normal file
58
tsm-lean/README.md
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# tsm-lean
|
||||||
|
|
||||||
|
A Lean 4 formalization of a Tiny Stack Machine — third concrete kernel parallel to `golang-lean` (TGC) and `octive-lean` (TOC).
|
||||||
|
|
||||||
|
The substrate-level asymmetry: TGC and TOC have *named variables*. TSM has values living *by position* on a stack. Forces the cross-language abstraction to factor over "operand-access mechanism" instead of baking name-lookup into the framework. Maps directly to real bytecode targets — WebAssembly, JVM, CPython, .NET CIL, SECD.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
lake build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run the demo
|
||||||
|
|
||||||
|
```sh
|
||||||
|
lake exe tsm-lean
|
||||||
|
# → final stack: [TsmLean.Core.Value.vInt 16] ((5 + 3) * 2)
|
||||||
|
# → final pc: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| Path | What's there |
|
||||||
|
| --- | --- |
|
||||||
|
| `TsmLean/Core/Syntax.lean` | `Instr`, `Value`, `Code` |
|
||||||
|
| `TsmLean/Core/Semantics.lean` | `State`, `step` (function), `MultiStep` (relation) |
|
||||||
|
| `TsmLean/Core/Determinism.lean` | `step_deterministic`, `MultiStep.deterministic` |
|
||||||
|
| `TsmLean/Core/Eval.lean` | fuel-bounded `run` + `run_sound` |
|
||||||
|
| `TsmLean/Core/Types.lean` | `Ty`, `StackTy`, `HasTypeInstr` |
|
||||||
|
| `TsmLean/Core/TypeSoundness.lean` | `HasTypeV`, `HasTypeStack` |
|
||||||
|
| `TsmLean/Core/Preservation.lean` | `stack_preservation`, `progress` |
|
||||||
|
| `Main.lean` | demo program |
|
||||||
|
|
||||||
|
## Theorems proven
|
||||||
|
|
||||||
|
- **`step_deterministic`** — single-step is functional.
|
||||||
|
- **`MultiStep.deterministic`** — multi-step paths to halted states are unique.
|
||||||
|
- **`run_sound`** — successful fuel-bounded execution corresponds to a `MultiStep` derivation ending at a halted state.
|
||||||
|
- **`stack_preservation`** — if the stack matches an instruction's input type and the step succeeds, the post-stack matches its output type.
|
||||||
|
- **`progress`** — well-typed non-halt instructions always make a step.
|
||||||
|
|
||||||
|
The first three are the operational counterparts of the big-step theorems in TGC and TOC. The last two are the small-step type-soundness theorems (Pierce-style), which TGC/TOC's big-step formulations don't have direct analogues for.
|
||||||
|
|
||||||
|
Zero sorries, axioms, or admits.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
**v0.1**: per-instruction (local) preservation. Global program-level type soundness — the JVM-style stackmap that ensures all reachable PCs have consistent stack types — is the next layer up.
|
||||||
|
|
||||||
|
## Instruction set
|
||||||
|
|
||||||
|
```
|
||||||
|
push n pushB b pop dup swap
|
||||||
|
add sub mul eq lt
|
||||||
|
jmp k jmpFalse k halt
|
||||||
|
```
|
||||||
|
|
||||||
|
Twelve instructions. No call / ret yet — direct jumps only. Adding function-call frames is a future extension.
|
||||||
10
tsm-lean/TsmLean.lean
Normal file
10
tsm-lean/TsmLean.lean
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import TsmLean.Core.Syntax
|
||||||
|
import TsmLean.Core.Semantics
|
||||||
|
import TsmLean.Core.Determinism
|
||||||
|
import TsmLean.Core.Eval
|
||||||
|
import TsmLean.Core.Types
|
||||||
|
import TsmLean.Core.TypeSoundness
|
||||||
|
import TsmLean.Core.Preservation
|
||||||
|
import TsmLean.Compile.Source
|
||||||
|
import TsmLean.Compile.Compile
|
||||||
|
import TsmLean.Compile.Correctness
|
||||||
27
tsm-lean/TsmLean/Compile/Compile.lean
Normal file
27
tsm-lean/TsmLean/Compile/Compile.lean
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import TsmLean.Compile.Source
|
||||||
|
import TsmLean.Core.Syntax
|
||||||
|
|
||||||
|
namespace TsmLean.Compile
|
||||||
|
|
||||||
|
open TsmLean.Core (Instr Code)
|
||||||
|
|
||||||
|
/-- Compile a source expression to TSM bytecode, with the output
|
||||||
|
placed at absolute address `offset` in the final assembled code.
|
||||||
|
Jumps (in `ifte`) reference absolute addresses. -/
|
||||||
|
def compile : (offset : Nat) → Source.Expr → Code
|
||||||
|
| _, .intLit n => #[.push n]
|
||||||
|
| _, .boolLit b => #[.pushB b]
|
||||||
|
| offset, .add e1 e2 =>
|
||||||
|
let c1 := compile offset e1
|
||||||
|
let c2 := compile (offset + c1.size) e2
|
||||||
|
c1 ++ c2 ++ #[.add]
|
||||||
|
| offset, .sub e1 e2 =>
|
||||||
|
let c1 := compile offset e1
|
||||||
|
let c2 := compile (offset + c1.size) e2
|
||||||
|
c1 ++ c2 ++ #[.sub]
|
||||||
|
| offset, .mul e1 e2 =>
|
||||||
|
let c1 := compile offset e1
|
||||||
|
let c2 := compile (offset + c1.size) e2
|
||||||
|
c1 ++ c2 ++ #[.mul]
|
||||||
|
|
||||||
|
end TsmLean.Compile
|
||||||
304
tsm-lean/TsmLean/Compile/Correctness.lean
Normal file
304
tsm-lean/TsmLean/Compile/Correctness.lean
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
import TsmLean.Compile.Compile
|
||||||
|
import TsmLean.Core.Semantics
|
||||||
|
|
||||||
|
namespace TsmLean.Compile
|
||||||
|
|
||||||
|
open TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Compiler-correctness theorem.
|
||||||
|
|
||||||
|
`Source.Eval e v ⟹ TSM.MultiStep (start of compile e) (end of compile e, with v on stack)`
|
||||||
|
|
||||||
|
The CompCert-flavored bridge: source-level evaluation and target-level
|
||||||
|
execution agree on the value produced. -/
|
||||||
|
|
||||||
|
/-! ## Multi-step utilities. -/
|
||||||
|
|
||||||
|
theorem MultiStep.trans
|
||||||
|
{s₁ s₂ s₃ : State}
|
||||||
|
(h₁ : MultiStep s₁ s₂) (h₂ : MultiStep s₂ s₃) :
|
||||||
|
MultiStep s₁ s₃ := by
|
||||||
|
induction h₁ with
|
||||||
|
| refl => exact h₂
|
||||||
|
| cons hs _ ih => exact .cons hs (ih h₂)
|
||||||
|
|
||||||
|
theorem MultiStep.single
|
||||||
|
{s s' : State} (h : step s = some s') :
|
||||||
|
MultiStep s s' := .cons h (.refl _)
|
||||||
|
|
||||||
|
/-! ## Single-step reduction lemmas. -/
|
||||||
|
|
||||||
|
theorem step_push
|
||||||
|
{code : Code} {pc : Nat} {stack : List Value} {n : Int}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .push n) :
|
||||||
|
step { code := code, pc := pc, stack := stack }
|
||||||
|
= some { code := code, pc := pc + 1, stack := .vInt n :: stack } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_pushB
|
||||||
|
{code : Code} {pc : Nat} {stack : List Value} {b : Bool}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .pushB b) :
|
||||||
|
step { code := code, pc := pc, stack := stack }
|
||||||
|
= some { code := code, pc := pc + 1, stack := .vBool b :: stack } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_add
|
||||||
|
{code : Code} {pc : Nat} {a b : Int} {rest : List Value}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .add) :
|
||||||
|
step { code := code, pc := pc, stack := .vInt b :: .vInt a :: rest }
|
||||||
|
= some { code := code, pc := pc + 1, stack := .vInt (a + b) :: rest } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_sub
|
||||||
|
{code : Code} {pc : Nat} {a b : Int} {rest : List Value}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .sub) :
|
||||||
|
step { code := code, pc := pc, stack := .vInt b :: .vInt a :: rest }
|
||||||
|
= some { code := code, pc := pc + 1, stack := .vInt (a - b) :: rest } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_mul
|
||||||
|
{code : Code} {pc : Nat} {a b : Int} {rest : List Value}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .mul) :
|
||||||
|
step { code := code, pc := pc, stack := .vInt b :: .vInt a :: rest }
|
||||||
|
= some { code := code, pc := pc + 1, stack := .vInt (a * b) :: rest } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_jmp
|
||||||
|
{code : Code} {pc k : Nat} {stack : List Value}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .jmp k) :
|
||||||
|
step { code := code, pc := pc, stack := stack }
|
||||||
|
= some { code := code, pc := k, stack := stack } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_jmpFalse_true
|
||||||
|
{code : Code} {pc k : Nat} {rest : List Value}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .jmpFalse k) :
|
||||||
|
step { code := code, pc := pc, stack := .vBool true :: rest }
|
||||||
|
= some { code := code, pc := pc + 1, stack := rest } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
theorem step_jmpFalse_false
|
||||||
|
{code : Code} {pc k : Nat} {rest : List Value}
|
||||||
|
(h_pc : pc < code.size) (h_get : code[pc]'h_pc = .jmpFalse k) :
|
||||||
|
step { code := code, pc := pc, stack := .vBool false :: rest }
|
||||||
|
= some { code := code, pc := k, stack := rest } := by
|
||||||
|
unfold step; rw [dif_pos h_pc, h_get]
|
||||||
|
|
||||||
|
/-! ## Generic array-lookup helpers. -/
|
||||||
|
|
||||||
|
/-- The instruction at the boundary of a `c1 ++ c2 ++ #[op]` arrangement. -/
|
||||||
|
theorem getElem_at_op_boundary
|
||||||
|
(c1 c2 : Code) (op : Instr)
|
||||||
|
(h : c1.size + c2.size < (c1 ++ c2 ++ #[op]).size) :
|
||||||
|
(c1 ++ c2 ++ #[op])[c1.size + c2.size]'h = op := by
|
||||||
|
show ((c1 ++ c2) ++ #[op])[c1.size + c2.size]'h = op
|
||||||
|
have hle : (c1 ++ c2).size ≤ c1.size + c2.size := Nat.le_of_eq Array.size_append
|
||||||
|
rw [Array.getElem_append_right hle]
|
||||||
|
simp [Array.size_append, Nat.sub_self]
|
||||||
|
|
||||||
|
/-- Lookup at offset `pre.size + i` within a `pre ++ X ++ suf` array reduces
|
||||||
|
to lookup at `i` within `X` itself, when `i < X.size`. -/
|
||||||
|
theorem getElem_at_offset
|
||||||
|
(pre X suf : Code) (i : Nat)
|
||||||
|
(h_lt : i < X.size)
|
||||||
|
(h_pc : pre.size + i < (pre ++ X ++ suf).size) :
|
||||||
|
(pre ++ X ++ suf)[pre.size + i]'h_pc = X[i]'h_lt := by
|
||||||
|
have h_pre_X : pre.size + i < (pre ++ X).size := by
|
||||||
|
rw [Array.size_append]; omega
|
||||||
|
rw [Array.getElem_append_left h_pre_X]
|
||||||
|
rw [Array.getElem_append_right (Nat.le_add_right _ _)]
|
||||||
|
congr 1; omega
|
||||||
|
|
||||||
|
/-! ## Per-construct compile-output lookup lemmas. -/
|
||||||
|
|
||||||
|
theorem compile_add_get_op (offset : Nat) (e1 e2 : Source.Expr)
|
||||||
|
(h : (compile offset e1).size + (compile (offset + (compile offset e1).size) e2).size
|
||||||
|
< (compile offset (Source.Expr.add e1 e2)).size) :
|
||||||
|
(compile offset (Source.Expr.add e1 e2))[(compile offset e1).size + (compile (offset + (compile offset e1).size) e2).size]'h = .add :=
|
||||||
|
getElem_at_op_boundary (compile offset e1) (compile (offset + (compile offset e1).size) e2) _ h
|
||||||
|
|
||||||
|
theorem compile_sub_get_op (offset : Nat) (e1 e2 : Source.Expr)
|
||||||
|
(h : (compile offset e1).size + (compile (offset + (compile offset e1).size) e2).size
|
||||||
|
< (compile offset (Source.Expr.sub e1 e2)).size) :
|
||||||
|
(compile offset (Source.Expr.sub e1 e2))[(compile offset e1).size + (compile (offset + (compile offset e1).size) e2).size]'h = .sub :=
|
||||||
|
getElem_at_op_boundary (compile offset e1) (compile (offset + (compile offset e1).size) e2) _ h
|
||||||
|
|
||||||
|
theorem compile_mul_get_op (offset : Nat) (e1 e2 : Source.Expr)
|
||||||
|
(h : (compile offset e1).size + (compile (offset + (compile offset e1).size) e2).size
|
||||||
|
< (compile offset (Source.Expr.mul e1 e2)).size) :
|
||||||
|
(compile offset (Source.Expr.mul e1 e2))[(compile offset e1).size + (compile (offset + (compile offset e1).size) e2).size]'h = .mul :=
|
||||||
|
getElem_at_op_boundary (compile offset e1) (compile (offset + (compile offset e1).size) e2) _ h
|
||||||
|
|
||||||
|
|
||||||
|
/-! ## Main theorem.
|
||||||
|
|
||||||
|
The compile is offset-aware: `compile pre.size e` produces bytecode
|
||||||
|
correctly placed at position `pre.size` in `pre ++ ... ++ suf`. -/
|
||||||
|
|
||||||
|
theorem compile_correct
|
||||||
|
{e : Source.Expr} {v : Source.Value}
|
||||||
|
(h_eval : Source.Eval e v) :
|
||||||
|
∀ (pre suf : Code) (rest : List Value),
|
||||||
|
MultiStep
|
||||||
|
{ code := pre ++ compile pre.size e ++ suf, pc := pre.size, stack := rest }
|
||||||
|
{ code := pre ++ compile pre.size e ++ suf,
|
||||||
|
pc := pre.size + (compile pre.size e).size, stack := v :: rest } := by
|
||||||
|
induction h_eval with
|
||||||
|
| intLit n =>
|
||||||
|
intros pre suf rest
|
||||||
|
apply MultiStep.single
|
||||||
|
have h_pc : pre.size < (pre ++ compile pre.size (Source.Expr.intLit n) ++ suf).size := by
|
||||||
|
simp only [compile, Array.size_append, Array.size_singleton]; omega
|
||||||
|
have h_get : (pre ++ compile pre.size (Source.Expr.intLit n) ++ suf)[pre.size]'h_pc = .push n := by
|
||||||
|
have h_pre_ce : pre.size < (pre ++ compile pre.size (Source.Expr.intLit n)).size := by
|
||||||
|
simp only [compile, Array.size_append, Array.size_singleton]; omega
|
||||||
|
rw [Array.getElem_append_left h_pre_ce]
|
||||||
|
rw [Array.getElem_append_right (Nat.le_refl _)]
|
||||||
|
simp [compile, Nat.sub_self]
|
||||||
|
have step_thm := step_push h_pc h_get (stack := rest)
|
||||||
|
have h_size : (compile pre.size (Source.Expr.intLit n)).size = 1 := by simp [compile]
|
||||||
|
rw [h_size]
|
||||||
|
exact step_thm
|
||||||
|
| boolLit b =>
|
||||||
|
intros pre suf rest
|
||||||
|
apply MultiStep.single
|
||||||
|
have h_pc : pre.size < (pre ++ compile pre.size (Source.Expr.boolLit b) ++ suf).size := by
|
||||||
|
simp only [compile, Array.size_append, Array.size_singleton]; omega
|
||||||
|
have h_get : (pre ++ compile pre.size (Source.Expr.boolLit b) ++ suf)[pre.size]'h_pc = .pushB b := by
|
||||||
|
have h_pre_ce : pre.size < (pre ++ compile pre.size (Source.Expr.boolLit b)).size := by
|
||||||
|
simp only [compile, Array.size_append, Array.size_singleton]; omega
|
||||||
|
rw [Array.getElem_append_left h_pre_ce]
|
||||||
|
rw [Array.getElem_append_right (Nat.le_refl _)]
|
||||||
|
simp [compile, Nat.sub_self]
|
||||||
|
have step_thm := step_pushB h_pc h_get (stack := rest)
|
||||||
|
have h_size : (compile pre.size (Source.Expr.boolLit b)).size = 1 := by simp [compile]
|
||||||
|
rw [h_size]
|
||||||
|
exact step_thm
|
||||||
|
| @add e1 e2 a b _ _ ih1 ih2 =>
|
||||||
|
intros pre suf rest
|
||||||
|
have stepA := ih1 pre (compile (pre.size + (compile pre.size e1).size) e2 ++ #[.add] ++ suf) rest
|
||||||
|
have stepB := ih2 (pre ++ compile pre.size e1) (#[.add] ++ suf) (.vInt a :: rest)
|
||||||
|
have h_pre_e1_size : (pre ++ compile pre.size e1).size = pre.size + (compile pre.size e1).size := by
|
||||||
|
simp [Array.size_append]
|
||||||
|
have h_code_A : pre ++ compile pre.size e1 ++ (compile (pre.size + (compile pre.size e1).size) e2 ++ #[Instr.add] ++ suf)
|
||||||
|
= pre ++ compile pre.size (Source.Expr.add e1 e2) ++ suf := by
|
||||||
|
simp only [compile, Array.append_assoc]
|
||||||
|
have h_code_B : pre ++ compile pre.size e1 ++ compile (pre.size + (compile pre.size e1).size) e2 ++ (#[Instr.add] ++ suf)
|
||||||
|
= pre ++ compile pre.size (Source.Expr.add e1 e2) ++ suf := by
|
||||||
|
simp only [compile, Array.append_assoc]
|
||||||
|
rw [h_code_A] at stepA
|
||||||
|
rw [h_pre_e1_size] at stepB
|
||||||
|
rw [h_code_B] at stepB
|
||||||
|
apply MultiStep.trans stepA
|
||||||
|
apply MultiStep.trans stepB
|
||||||
|
apply MultiStep.single
|
||||||
|
have h_total_size : (compile pre.size (Source.Expr.add e1 e2)).size
|
||||||
|
= (compile pre.size e1).size
|
||||||
|
+ (compile (pre.size + (compile pre.size e1).size) e2).size + 1 := by
|
||||||
|
show (compile pre.size e1 ++ compile (pre.size + (compile pre.size e1).size) e2 ++ #[Instr.add]).size
|
||||||
|
= (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size + 1
|
||||||
|
simp [Array.size_append]; omega
|
||||||
|
have h_in_comp : (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size
|
||||||
|
< (compile pre.size (Source.Expr.add e1 e2)).size := by
|
||||||
|
simp [compile, Array.size_append]
|
||||||
|
have h_full_pc : pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size)
|
||||||
|
< (pre ++ compile pre.size (Source.Expr.add e1 e2) ++ suf).size := by
|
||||||
|
simp only [Array.size_append, h_total_size]; omega
|
||||||
|
have h_op_get : (pre ++ compile pre.size (Source.Expr.add e1 e2) ++ suf)[pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size)]'h_full_pc = .add := by
|
||||||
|
rw [getElem_at_offset pre (compile pre.size (Source.Expr.add e1 e2)) suf
|
||||||
|
((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) h_in_comp h_full_pc]
|
||||||
|
exact compile_add_get_op pre.size e1 e2 h_in_comp
|
||||||
|
have h_step := step_add (a := a) (b := b) (rest := rest) h_full_pc h_op_get
|
||||||
|
have h_pre_pc : pre.size + (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size
|
||||||
|
= pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) := by omega
|
||||||
|
have h_post_pc : pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) + 1
|
||||||
|
= pre.size + (compile pre.size (Source.Expr.add e1 e2)).size := by
|
||||||
|
rw [h_total_size]; omega
|
||||||
|
rw [h_pre_pc, ← h_post_pc]
|
||||||
|
exact h_step
|
||||||
|
| @sub e1 e2 a b _ _ ih1 ih2 =>
|
||||||
|
intros pre suf rest
|
||||||
|
have stepA := ih1 pre (compile (pre.size + (compile pre.size e1).size) e2 ++ #[.sub] ++ suf) rest
|
||||||
|
have stepB := ih2 (pre ++ compile pre.size e1) (#[.sub] ++ suf) (.vInt a :: rest)
|
||||||
|
have h_pre_e1_size : (pre ++ compile pre.size e1).size = pre.size + (compile pre.size e1).size := by
|
||||||
|
simp [Array.size_append]
|
||||||
|
have h_code_A : pre ++ compile pre.size e1 ++ (compile (pre.size + (compile pre.size e1).size) e2 ++ #[Instr.sub] ++ suf)
|
||||||
|
= pre ++ compile pre.size (Source.Expr.sub e1 e2) ++ suf := by
|
||||||
|
simp only [compile, Array.append_assoc]
|
||||||
|
have h_code_B : pre ++ compile pre.size e1 ++ compile (pre.size + (compile pre.size e1).size) e2 ++ (#[Instr.sub] ++ suf)
|
||||||
|
= pre ++ compile pre.size (Source.Expr.sub e1 e2) ++ suf := by
|
||||||
|
simp only [compile, Array.append_assoc]
|
||||||
|
rw [h_code_A] at stepA
|
||||||
|
rw [h_pre_e1_size] at stepB
|
||||||
|
rw [h_code_B] at stepB
|
||||||
|
apply MultiStep.trans stepA
|
||||||
|
apply MultiStep.trans stepB
|
||||||
|
apply MultiStep.single
|
||||||
|
have h_total_size : (compile pre.size (Source.Expr.sub e1 e2)).size
|
||||||
|
= (compile pre.size e1).size
|
||||||
|
+ (compile (pre.size + (compile pre.size e1).size) e2).size + 1 := by
|
||||||
|
show (compile pre.size e1 ++ compile (pre.size + (compile pre.size e1).size) e2 ++ #[Instr.sub]).size
|
||||||
|
= (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size + 1
|
||||||
|
simp [Array.size_append]; omega
|
||||||
|
have h_in_comp : (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size
|
||||||
|
< (compile pre.size (Source.Expr.sub e1 e2)).size := by
|
||||||
|
simp [compile, Array.size_append]
|
||||||
|
have h_full_pc : pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size)
|
||||||
|
< (pre ++ compile pre.size (Source.Expr.sub e1 e2) ++ suf).size := by
|
||||||
|
simp only [Array.size_append, h_total_size]; omega
|
||||||
|
have h_op_get : (pre ++ compile pre.size (Source.Expr.sub e1 e2) ++ suf)[pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size)]'h_full_pc = .sub := by
|
||||||
|
rw [getElem_at_offset pre (compile pre.size (Source.Expr.sub e1 e2)) suf
|
||||||
|
((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) h_in_comp h_full_pc]
|
||||||
|
exact compile_sub_get_op pre.size e1 e2 h_in_comp
|
||||||
|
have h_step := step_sub (a := a) (b := b) (rest := rest) h_full_pc h_op_get
|
||||||
|
have h_pre_pc : pre.size + (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size
|
||||||
|
= pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) := by omega
|
||||||
|
have h_post_pc : pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) + 1
|
||||||
|
= pre.size + (compile pre.size (Source.Expr.sub e1 e2)).size := by
|
||||||
|
rw [h_total_size]; omega
|
||||||
|
rw [h_pre_pc, ← h_post_pc]
|
||||||
|
exact h_step
|
||||||
|
| @mul e1 e2 a b _ _ ih1 ih2 =>
|
||||||
|
intros pre suf rest
|
||||||
|
have stepA := ih1 pre (compile (pre.size + (compile pre.size e1).size) e2 ++ #[.mul] ++ suf) rest
|
||||||
|
have stepB := ih2 (pre ++ compile pre.size e1) (#[.mul] ++ suf) (.vInt a :: rest)
|
||||||
|
have h_pre_e1_size : (pre ++ compile pre.size e1).size = pre.size + (compile pre.size e1).size := by
|
||||||
|
simp [Array.size_append]
|
||||||
|
have h_code_A : pre ++ compile pre.size e1 ++ (compile (pre.size + (compile pre.size e1).size) e2 ++ #[Instr.mul] ++ suf)
|
||||||
|
= pre ++ compile pre.size (Source.Expr.mul e1 e2) ++ suf := by
|
||||||
|
simp only [compile, Array.append_assoc]
|
||||||
|
have h_code_B : pre ++ compile pre.size e1 ++ compile (pre.size + (compile pre.size e1).size) e2 ++ (#[Instr.mul] ++ suf)
|
||||||
|
= pre ++ compile pre.size (Source.Expr.mul e1 e2) ++ suf := by
|
||||||
|
simp only [compile, Array.append_assoc]
|
||||||
|
rw [h_code_A] at stepA
|
||||||
|
rw [h_pre_e1_size] at stepB
|
||||||
|
rw [h_code_B] at stepB
|
||||||
|
apply MultiStep.trans stepA
|
||||||
|
apply MultiStep.trans stepB
|
||||||
|
apply MultiStep.single
|
||||||
|
have h_total_size : (compile pre.size (Source.Expr.mul e1 e2)).size
|
||||||
|
= (compile pre.size e1).size
|
||||||
|
+ (compile (pre.size + (compile pre.size e1).size) e2).size + 1 := by
|
||||||
|
show (compile pre.size e1 ++ compile (pre.size + (compile pre.size e1).size) e2 ++ #[Instr.mul]).size
|
||||||
|
= (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size + 1
|
||||||
|
simp [Array.size_append]; omega
|
||||||
|
have h_in_comp : (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size
|
||||||
|
< (compile pre.size (Source.Expr.mul e1 e2)).size := by
|
||||||
|
simp [compile, Array.size_append]
|
||||||
|
have h_full_pc : pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size)
|
||||||
|
< (pre ++ compile pre.size (Source.Expr.mul e1 e2) ++ suf).size := by
|
||||||
|
simp only [Array.size_append, h_total_size]; omega
|
||||||
|
have h_op_get : (pre ++ compile pre.size (Source.Expr.mul e1 e2) ++ suf)[pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size)]'h_full_pc = .mul := by
|
||||||
|
rw [getElem_at_offset pre (compile pre.size (Source.Expr.mul e1 e2)) suf
|
||||||
|
((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) h_in_comp h_full_pc]
|
||||||
|
exact compile_mul_get_op pre.size e1 e2 h_in_comp
|
||||||
|
have h_step := step_mul (a := a) (b := b) (rest := rest) h_full_pc h_op_get
|
||||||
|
have h_pre_pc : pre.size + (compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size
|
||||||
|
= pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) := by omega
|
||||||
|
have h_post_pc : pre.size + ((compile pre.size e1).size + (compile (pre.size + (compile pre.size e1).size) e2).size) + 1
|
||||||
|
= pre.size + (compile pre.size (Source.Expr.mul e1 e2)).size := by
|
||||||
|
rw [h_total_size]; omega
|
||||||
|
rw [h_pre_pc, ← h_post_pc]
|
||||||
|
exact h_step
|
||||||
|
|
||||||
|
end TsmLean.Compile
|
||||||
35
tsm-lean/TsmLean/Compile/Source.lean
Normal file
35
tsm-lean/TsmLean/Compile/Source.lean
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import TsmLean.Core.Syntax
|
||||||
|
|
||||||
|
namespace TsmLean.Compile.Source
|
||||||
|
|
||||||
|
/-! # Source language for compilation (v0.4).
|
||||||
|
|
||||||
|
Integer/bool literals + arithmetic. The compile function takes an
|
||||||
|
offset (infrastructure for future control-flow constructs that
|
||||||
|
require absolute jump addresses). For the constructs in v0.4, the
|
||||||
|
offset doesn't change the compile output — it's just threaded. -/
|
||||||
|
|
||||||
|
inductive Expr where
|
||||||
|
| intLit : Int → Expr
|
||||||
|
| boolLit : Bool → Expr
|
||||||
|
| add : Expr → Expr → Expr
|
||||||
|
| sub : Expr → Expr → Expr
|
||||||
|
| mul : Expr → Expr → Expr
|
||||||
|
deriving Repr, Inhabited
|
||||||
|
|
||||||
|
abbrev Value := TsmLean.Core.Value
|
||||||
|
|
||||||
|
inductive Eval : Expr → Value → Prop where
|
||||||
|
| intLit (n : Int) : Eval (.intLit n) (.vInt n)
|
||||||
|
| boolLit (b : Bool) : Eval (.boolLit b) (.vBool b)
|
||||||
|
| add {e1 e2 a b}
|
||||||
|
(h1 : Eval e1 (.vInt a)) (h2 : Eval e2 (.vInt b)) :
|
||||||
|
Eval (.add e1 e2) (.vInt (a + b))
|
||||||
|
| sub {e1 e2 a b}
|
||||||
|
(h1 : Eval e1 (.vInt a)) (h2 : Eval e2 (.vInt b)) :
|
||||||
|
Eval (.sub e1 e2) (.vInt (a - b))
|
||||||
|
| mul {e1 e2 a b}
|
||||||
|
(h1 : Eval e1 (.vInt a)) (h2 : Eval e2 (.vInt b)) :
|
||||||
|
Eval (.mul e1 e2) (.vInt (a * b))
|
||||||
|
|
||||||
|
end TsmLean.Compile.Source
|
||||||
41
tsm-lean/TsmLean/Core/Determinism.lean
Normal file
41
tsm-lean/TsmLean/Core/Determinism.lean
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import TsmLean.Core.Semantics
|
||||||
|
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Determinism of TSM step.
|
||||||
|
|
||||||
|
`step` is a total function `State → Option State`, so single-step
|
||||||
|
determinism is *immediate*: two transitions from the same state yield
|
||||||
|
the same successor (or both fail).
|
||||||
|
|
||||||
|
Multi-step determinism follows by induction on the chain. We prove
|
||||||
|
that any two `MultiStep` derivations of the same length collapse to
|
||||||
|
the same trace. -/
|
||||||
|
|
||||||
|
theorem step_deterministic
|
||||||
|
{s s₁ s₂ : State}
|
||||||
|
(h₁ : step s = some s₁) (h₂ : step s = some s₂) :
|
||||||
|
s₁ = s₂ := by
|
||||||
|
rw [h₁] at h₂
|
||||||
|
exact Option.some.inj h₂
|
||||||
|
|
||||||
|
/-- Multi-step paths to halted states are deterministic. -/
|
||||||
|
theorem MultiStep.deterministic
|
||||||
|
{s s_a s_b : State}
|
||||||
|
(D_a : MultiStep s s_a) (D_b : MultiStep s s_b)
|
||||||
|
(halt_a : step s_a = none) (halt_b : step s_b = none) :
|
||||||
|
s_a = s_b := by
|
||||||
|
induction D_a generalizing s_b with
|
||||||
|
| refl =>
|
||||||
|
cases D_b with
|
||||||
|
| refl => rfl
|
||||||
|
| cons h₁ _ => rw [halt_a] at h₁; cases h₁
|
||||||
|
| cons h₁ _ ih =>
|
||||||
|
cases D_b with
|
||||||
|
| refl => rw [halt_b] at h₁; cases h₁
|
||||||
|
| cons h₁' D_b' =>
|
||||||
|
have heq := step_deterministic h₁ h₁'
|
||||||
|
subst heq
|
||||||
|
exact ih D_b' halt_a halt_b
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
44
tsm-lean/TsmLean/Core/Eval.lean
Normal file
44
tsm-lean/TsmLean/Core/Eval.lean
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import TsmLean.Core.Semantics
|
||||||
|
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Fuel-bounded executable multi-step.
|
||||||
|
|
||||||
|
`run n s₀` executes up to `n` steps from `s₀`. Returns the final state
|
||||||
|
when execution halts (step returns `none`) within fuel, or `none` when
|
||||||
|
fuel is exhausted before halting.
|
||||||
|
|
||||||
|
Soundness: any successful run corresponds to a `MultiStep` derivation
|
||||||
|
ending at a halted state — same shape as TGC/TOC's eval_sound, but
|
||||||
|
phrased over the small-step closure rather than big-step. -/
|
||||||
|
|
||||||
|
def run : Nat → State → Option State
|
||||||
|
| 0, _ => none
|
||||||
|
| n + 1, s =>
|
||||||
|
match step s with
|
||||||
|
| none => some s -- halted
|
||||||
|
| some s' => run n s'
|
||||||
|
|
||||||
|
theorem run_sound :
|
||||||
|
∀ (n : Nat) (s s' : State),
|
||||||
|
run n s = some s' → MultiStep s s' ∧ step s' = none := by
|
||||||
|
intro n
|
||||||
|
induction n with
|
||||||
|
| zero =>
|
||||||
|
intros s s' heq
|
||||||
|
simp [run] at heq
|
||||||
|
| succ n ih =>
|
||||||
|
intros s s' heq
|
||||||
|
simp only [run] at heq
|
||||||
|
cases hstep : step s with
|
||||||
|
| none =>
|
||||||
|
rw [hstep] at heq
|
||||||
|
simp at heq
|
||||||
|
subst heq
|
||||||
|
exact ⟨.refl s, hstep⟩
|
||||||
|
| some s_next =>
|
||||||
|
rw [hstep] at heq
|
||||||
|
have ⟨hMS, hHalt⟩ := ih s_next s' heq
|
||||||
|
exact ⟨.cons hstep hMS, hHalt⟩
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
203
tsm-lean/TsmLean/Core/Preservation.lean
Normal file
203
tsm-lean/TsmLean/Core/Preservation.lean
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
import TsmLean.Core.TypeSoundness
|
||||||
|
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Preservation and progress for TSM.
|
||||||
|
|
||||||
|
Local (per-instruction) preservation: if the stack matches an
|
||||||
|
instruction's input type and that instruction succeeds, the post-stack
|
||||||
|
matches its output type.
|
||||||
|
|
||||||
|
Global type soundness — that *every* reachable PC has a consistent
|
||||||
|
stackmap — requires program-wide code typing (JVM-style stackmaps).
|
||||||
|
That's a layer above; this file proves the per-instruction theorem
|
||||||
|
on which the global one is built.
|
||||||
|
|
||||||
|
Progress: well-typed non-halt instructions always make a step. -/
|
||||||
|
|
||||||
|
theorem stack_preservation
|
||||||
|
{s s' : State} {in_ty out_ty : StackTy}
|
||||||
|
(h_pc : s.pc < s.code.size)
|
||||||
|
(h_typed : HasTypeInstr (s.code[s.pc]'h_pc) in_ty out_ty)
|
||||||
|
(h_stack : HasTypeStack s.stack in_ty)
|
||||||
|
(h_step : step s = some s') :
|
||||||
|
HasTypeStack s'.stack out_ty := by
|
||||||
|
unfold step at h_step
|
||||||
|
rw [dif_pos h_pc] at h_step
|
||||||
|
generalize h_at : s.code[s.pc]'h_pc = instr at h_typed h_step
|
||||||
|
generalize h_stk : s.stack = stk at h_stack h_step
|
||||||
|
cases h_typed with
|
||||||
|
| push n =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vInt n) h_stack
|
||||||
|
| pushB b =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vBool b) h_stack
|
||||||
|
| pop =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons _ hs =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact hs
|
||||||
|
| dup =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv hs =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons hv (.cons hv hs)
|
||||||
|
| swap =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h_rest =>
|
||||||
|
cases h_rest with
|
||||||
|
| cons hv2 hs =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons hv2 (.cons hv1 hs)
|
||||||
|
| add =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt a =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 hs =>
|
||||||
|
cases hv2 with
|
||||||
|
| vInt b =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vInt _) hs
|
||||||
|
| sub =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt a =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 hs =>
|
||||||
|
cases hv2 with
|
||||||
|
| vInt b =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vInt _) hs
|
||||||
|
| mul =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt a =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 hs =>
|
||||||
|
cases hv2 with
|
||||||
|
| vInt b =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vInt _) hs
|
||||||
|
| eq_int =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt a =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 hs =>
|
||||||
|
cases hv2 with
|
||||||
|
| vInt b =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vBool _) hs
|
||||||
|
| lt_int =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt a =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 hs =>
|
||||||
|
cases hv2 with
|
||||||
|
| vInt b =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact .cons (.vBool _) hs
|
||||||
|
| jmp =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact h_stack
|
||||||
|
| jmpFalse =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv hs =>
|
||||||
|
cases hv with
|
||||||
|
| vBool b =>
|
||||||
|
cases b with
|
||||||
|
| false =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact hs
|
||||||
|
| true =>
|
||||||
|
simp at h_step
|
||||||
|
obtain ⟨_, rfl⟩ := h_step
|
||||||
|
exact hs
|
||||||
|
| halt =>
|
||||||
|
simp at h_step
|
||||||
|
|
||||||
|
theorem progress
|
||||||
|
{s : State} {in_ty out_ty : StackTy}
|
||||||
|
(h_pc : s.pc < s.code.size)
|
||||||
|
(h_typed : HasTypeInstr (s.code[s.pc]'h_pc) in_ty out_ty)
|
||||||
|
(h_stack : HasTypeStack s.stack in_ty)
|
||||||
|
(h_not_halt : s.code[s.pc]'h_pc ≠ .halt) :
|
||||||
|
∃ s', step s = some s' := by
|
||||||
|
unfold step
|
||||||
|
rw [dif_pos h_pc]
|
||||||
|
generalize h_at : s.code[s.pc]'h_pc = instr at h_typed h_not_halt
|
||||||
|
generalize h_stk : s.stack = stk at h_stack
|
||||||
|
cases h_typed with
|
||||||
|
| push n => exact ⟨_, rfl⟩
|
||||||
|
| pushB b => exact ⟨_, rfl⟩
|
||||||
|
| pop =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons _ _ => exact ⟨_, rfl⟩
|
||||||
|
| dup =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons _ _ => exact ⟨_, rfl⟩
|
||||||
|
| swap =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons _ h1 => cases h1 with | cons _ _ => exact ⟨_, rfl⟩
|
||||||
|
| add =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt _ =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 _ => cases hv2 with | vInt _ => exact ⟨_, rfl⟩
|
||||||
|
| sub =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt _ =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 _ => cases hv2 with | vInt _ => exact ⟨_, rfl⟩
|
||||||
|
| mul =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt _ =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 _ => cases hv2 with | vInt _ => exact ⟨_, rfl⟩
|
||||||
|
| eq_int =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt _ =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 _ => cases hv2 with | vInt _ => exact ⟨_, rfl⟩
|
||||||
|
| lt_int =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv1 h1 =>
|
||||||
|
cases hv1 with
|
||||||
|
| vInt _ =>
|
||||||
|
cases h1 with
|
||||||
|
| cons hv2 _ => cases hv2 with | vInt _ => exact ⟨_, rfl⟩
|
||||||
|
| jmp => exact ⟨_, rfl⟩
|
||||||
|
| jmpFalse =>
|
||||||
|
cases h_stack with
|
||||||
|
| cons hv _ => cases hv with | vBool b => cases b <;> exact ⟨_, rfl⟩
|
||||||
|
| halt => exact absurd rfl h_not_halt
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
80
tsm-lean/TsmLean/Core/Semantics.lean
Normal file
80
tsm-lean/TsmLean/Core/Semantics.lean
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import TsmLean.Core.Syntax
|
||||||
|
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Small-step operational semantics for TSM.
|
||||||
|
|
||||||
|
State = `(Code, PC, Stack)`. The stack is `List Value` (top-of-stack at
|
||||||
|
the head). Step is a *function* `State → Option State`:
|
||||||
|
* `some s'` : the next state.
|
||||||
|
* `none` : halted, OOB, or stuck (type error).
|
||||||
|
|
||||||
|
Compare with TGC/TOC's big-step `Env → Term → Value → Env → Prop`:
|
||||||
|
TSM uses small-step because instructions are atomic. The reflexive-
|
||||||
|
transitive closure (`MultiStep`) is the analogue of big-step. -/
|
||||||
|
|
||||||
|
structure State where
|
||||||
|
code : Code
|
||||||
|
pc : Nat
|
||||||
|
stack : List Value
|
||||||
|
deriving Repr, Inhabited
|
||||||
|
|
||||||
|
def step (s : State) : Option State :=
|
||||||
|
if h : s.pc < s.code.size then
|
||||||
|
match s.code[s.pc] with
|
||||||
|
| .push n => some { s with pc := s.pc + 1, stack := .vInt n :: s.stack }
|
||||||
|
| .pushB b => some { s with pc := s.pc + 1, stack := .vBool b :: s.stack }
|
||||||
|
| .pop =>
|
||||||
|
match s.stack with
|
||||||
|
| _ :: rest => some { s with pc := s.pc + 1, stack := rest }
|
||||||
|
| [] => none
|
||||||
|
| .dup =>
|
||||||
|
match s.stack with
|
||||||
|
| v :: rest => some { s with pc := s.pc + 1, stack := v :: v :: rest }
|
||||||
|
| [] => none
|
||||||
|
| .swap =>
|
||||||
|
match s.stack with
|
||||||
|
| a :: b :: rest => some { s with pc := s.pc + 1, stack := b :: a :: rest }
|
||||||
|
| _ => none
|
||||||
|
| .add =>
|
||||||
|
match s.stack with
|
||||||
|
| .vInt a :: .vInt b :: rest =>
|
||||||
|
some { s with pc := s.pc + 1, stack := .vInt (b + a) :: rest }
|
||||||
|
| _ => none
|
||||||
|
| .sub =>
|
||||||
|
match s.stack with
|
||||||
|
| .vInt a :: .vInt b :: rest =>
|
||||||
|
some { s with pc := s.pc + 1, stack := .vInt (b - a) :: rest }
|
||||||
|
| _ => none
|
||||||
|
| .mul =>
|
||||||
|
match s.stack with
|
||||||
|
| .vInt a :: .vInt b :: rest =>
|
||||||
|
some { s with pc := s.pc + 1, stack := .vInt (b * a) :: rest }
|
||||||
|
| _ => none
|
||||||
|
| .eq =>
|
||||||
|
match s.stack with
|
||||||
|
| .vInt a :: .vInt b :: rest =>
|
||||||
|
some { s with pc := s.pc + 1, stack := .vBool (b == a) :: rest }
|
||||||
|
| _ => none
|
||||||
|
| .lt =>
|
||||||
|
match s.stack with
|
||||||
|
| .vInt a :: .vInt b :: rest =>
|
||||||
|
some { s with pc := s.pc + 1, stack := .vBool (b < a) :: rest }
|
||||||
|
| _ => none
|
||||||
|
| .jmp k => some { s with pc := k }
|
||||||
|
| .jmpFalse k =>
|
||||||
|
match s.stack with
|
||||||
|
| .vBool false :: rest => some { s with pc := k, stack := rest }
|
||||||
|
| .vBool true :: rest => some { s with pc := s.pc + 1, stack := rest }
|
||||||
|
| _ => none
|
||||||
|
| .halt => none
|
||||||
|
else none
|
||||||
|
|
||||||
|
/-- Reflexive-transitive closure of `step`. -/
|
||||||
|
inductive MultiStep : State → State → Prop where
|
||||||
|
| refl (s : State) : MultiStep s s
|
||||||
|
| cons {s s' s'' : State}
|
||||||
|
(h₁ : step s = some s') (h₂ : MultiStep s' s'') :
|
||||||
|
MultiStep s s''
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
39
tsm-lean/TsmLean/Core/Syntax.lean
Normal file
39
tsm-lean/TsmLean/Core/Syntax.lean
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Tiny Stack Machine (TSM) — abstract syntax.
|
||||||
|
|
||||||
|
Third concrete kernel, parallel to golang-lean's TGC and octive-lean's
|
||||||
|
TOC. Where TGC and TOC have *named variables*, TSM has values living
|
||||||
|
*by position* on a stack — the deepest substrate-level asymmetry.
|
||||||
|
|
||||||
|
Instructions are atomic; programs are arrays of instructions. The PC
|
||||||
|
indexes into the array. Conditional/unconditional jumps use absolute
|
||||||
|
target addresses (not relative offsets — simpler to reason about).
|
||||||
|
|
||||||
|
Maps to real-world stack-based bytecodes: WebAssembly, JVM, CPython,
|
||||||
|
.NET CIL, SECD machines. Anything proved here transfers to those. -/
|
||||||
|
|
||||||
|
inductive Value where
|
||||||
|
| vInt : Int → Value
|
||||||
|
| vBool : Bool → Value
|
||||||
|
deriving Repr, BEq, Inhabited
|
||||||
|
|
||||||
|
inductive Instr where
|
||||||
|
| push : Int → Instr -- push integer literal
|
||||||
|
| pushB : Bool → Instr -- push bool literal
|
||||||
|
| pop : Instr
|
||||||
|
| dup : Instr -- duplicate top
|
||||||
|
| swap : Instr -- swap top two
|
||||||
|
| add : Instr
|
||||||
|
| sub : Instr
|
||||||
|
| mul : Instr
|
||||||
|
| eq : Instr -- pop two ints, push bool
|
||||||
|
| lt : Instr -- pop two ints, push bool
|
||||||
|
| jmp : Nat → Instr -- absolute jump
|
||||||
|
| jmpFalse : Nat → Instr -- pop bool; if false, jump
|
||||||
|
| halt : Instr
|
||||||
|
deriving Repr, BEq, Inhabited
|
||||||
|
|
||||||
|
abbrev Code := Array Instr
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
22
tsm-lean/TsmLean/Core/TypeSoundness.lean
Normal file
22
tsm-lean/TsmLean/Core/TypeSoundness.lean
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import TsmLean.Core.Types
|
||||||
|
import TsmLean.Core.Semantics
|
||||||
|
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Stack-typing infrastructure.
|
||||||
|
|
||||||
|
`HasTypeV` types individual values (int / bool). `HasTypeStack` is the
|
||||||
|
pointwise lift to a list, length-aligned with a `StackTy`. -/
|
||||||
|
|
||||||
|
inductive HasTypeV : Value → Ty → Prop where
|
||||||
|
| vInt (n : Int) : HasTypeV (.vInt n) .int
|
||||||
|
| vBool (b : Bool) : HasTypeV (.vBool b) .bool
|
||||||
|
|
||||||
|
inductive HasTypeStack : List Value → StackTy → Prop where
|
||||||
|
| nil : HasTypeStack [] []
|
||||||
|
| cons {v vs T sty}
|
||||||
|
(hv : HasTypeV v T)
|
||||||
|
(hs : HasTypeStack vs sty) :
|
||||||
|
HasTypeStack (v :: vs) (T :: sty)
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
40
tsm-lean/TsmLean/Core/Types.lean
Normal file
40
tsm-lean/TsmLean/Core/Types.lean
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import TsmLean.Core.Syntax
|
||||||
|
|
||||||
|
namespace TsmLean.Core
|
||||||
|
|
||||||
|
/-! # Static type system for TSM.
|
||||||
|
|
||||||
|
Types live on the *stack*, not on names — this is the substrate-level
|
||||||
|
asymmetry vs TGC and TOC. Each instruction transforms the *type* of
|
||||||
|
the stack it expects to its post-state.
|
||||||
|
|
||||||
|
Two base types: `int` and `bool`. A stack-type `StackTy` is a list of
|
||||||
|
types matching the stack's runtime contents top-to-tail. Per-instruction
|
||||||
|
typing `HasTypeInstr instr ty_in ty_out` is the abstract transition;
|
||||||
|
real code-typing (the JVM-style stackmap) requires that all reachable
|
||||||
|
PCs have consistent stack types — handled separately. -/
|
||||||
|
|
||||||
|
inductive Ty where
|
||||||
|
| int : Ty
|
||||||
|
| bool : Ty
|
||||||
|
deriving Repr, BEq, DecidableEq, Inhabited
|
||||||
|
|
||||||
|
abbrev StackTy := List Ty
|
||||||
|
|
||||||
|
inductive HasTypeInstr : Instr → StackTy → StackTy → Prop where
|
||||||
|
| push {sty} (n : Int) : HasTypeInstr (.push n) sty (.int :: sty)
|
||||||
|
| pushB {sty} (b : Bool) : HasTypeInstr (.pushB b) sty (.bool :: sty)
|
||||||
|
| pop {T sty} : HasTypeInstr .pop (T :: sty) sty
|
||||||
|
| dup {T sty} : HasTypeInstr .dup (T :: sty) (T :: T :: sty)
|
||||||
|
| swap {T₁ T₂ sty} : HasTypeInstr .swap (T₁ :: T₂ :: sty) (T₂ :: T₁ :: sty)
|
||||||
|
| add {sty} : HasTypeInstr .add (.int :: .int :: sty) (.int :: sty)
|
||||||
|
| sub {sty} : HasTypeInstr .sub (.int :: .int :: sty) (.int :: sty)
|
||||||
|
| mul {sty} : HasTypeInstr .mul (.int :: .int :: sty) (.int :: sty)
|
||||||
|
| eq_int {sty} : HasTypeInstr .eq (.int :: .int :: sty) (.bool :: sty)
|
||||||
|
| lt_int {sty} : HasTypeInstr .lt (.int :: .int :: sty) (.bool :: sty)
|
||||||
|
-- Jumps preserve the stack type (target's expected stack matches source's).
|
||||||
|
| jmp {k sty} : HasTypeInstr (.jmp k) sty sty
|
||||||
|
| jmpFalse {k sty} : HasTypeInstr (.jmpFalse k) (.bool :: sty) sty
|
||||||
|
| halt {sty} : HasTypeInstr .halt sty sty
|
||||||
|
|
||||||
|
end TsmLean.Core
|
||||||
6
tsm-lean/lake-manifest.json
Normal file
6
tsm-lean/lake-manifest.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{"version": "1.2.0",
|
||||||
|
"packagesDir": ".lake/packages",
|
||||||
|
"packages": [],
|
||||||
|
"name": "«tsm-lean»",
|
||||||
|
"lakeDir": ".lake",
|
||||||
|
"fixedToolchain": false}
|
||||||
10
tsm-lean/lakefile.toml
Normal file
10
tsm-lean/lakefile.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
name = "tsm-lean"
|
||||||
|
version = "0.1.0"
|
||||||
|
defaultTargets = ["tsm-lean"]
|
||||||
|
|
||||||
|
[[lean_lib]]
|
||||||
|
name = "TsmLean"
|
||||||
|
|
||||||
|
[[lean_exe]]
|
||||||
|
name = "tsm-lean"
|
||||||
|
root = "Main"
|
||||||
1
tsm-lean/lean-toolchain
Normal file
1
tsm-lean/lean-toolchain
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
leanprover/lean4:v4.30.0-rc2
|
||||||
Loading…
Add table
Reference in a new issue