mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-08-11 19:40:34 +00:00
feat(avm): add Lean-only strict-typed ISA skeleton (v1)
This commit is contained in:
parent
6d73a1b251
commit
a84a704dbf
6 changed files with 323 additions and 0 deletions
42
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Instr.lean
Normal file
42
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Instr.lean
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
-- AVM ISA v1 (Lean-only): Instructions
|
||||||
|
-- Closed-world opcodes. No CALL/IMPORT. No string dispatch.
|
||||||
|
|
||||||
|
import Semantics.AVMIsa.Value
|
||||||
|
|
||||||
|
namespace Semantics.AVMIsa
|
||||||
|
|
||||||
|
/-- Finite primitive set (closed-world).
|
||||||
|
|
||||||
|
If extensibility is needed, add a constructor here and define its semantics in Lean.
|
||||||
|
Backends must implement the same semantics.
|
||||||
|
-/
|
||||||
|
inductive Prim : Type where
|
||||||
|
| addSatQ0
|
||||||
|
| subSatQ0
|
||||||
|
| addSatQ16
|
||||||
|
| subSatQ16
|
||||||
|
| and
|
||||||
|
| or
|
||||||
|
| not
|
||||||
|
deriving DecidableEq, BEq, Inhabited
|
||||||
|
|
||||||
|
/-- Core instruction set.
|
||||||
|
|
||||||
|
`load`/`store` use `Nat` indices in this v1 skeleton.
|
||||||
|
Strict implementations SHOULD replace them with `Fin n` once the local-frame
|
||||||
|
size is part of `Program`.
|
||||||
|
-/
|
||||||
|
inductive Instr : Type where
|
||||||
|
| push : AnyVal → Instr
|
||||||
|
| pop : Instr
|
||||||
|
| dup : Instr
|
||||||
|
| swap : Instr
|
||||||
|
| load : Nat → Instr
|
||||||
|
| store : Nat → Instr
|
||||||
|
| jump : Nat → Instr
|
||||||
|
| jumpIf : Nat → Instr
|
||||||
|
| prim : Prim → Instr
|
||||||
|
| halt : Instr
|
||||||
|
deriving Inhabited
|
||||||
|
|
||||||
|
end Semantics.AVMIsa
|
||||||
44
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Run.lean
Normal file
44
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Run.lean
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
-- AVM ISA v1 (Lean-only): Run semantics (fuel-bounded)
|
||||||
|
|
||||||
|
import Semantics.AVMIsa.Step
|
||||||
|
|
||||||
|
namespace Semantics.AVMIsa
|
||||||
|
|
||||||
|
/-- Fuel for total execution. -/
|
||||||
|
abbrev Fuel := Nat
|
||||||
|
|
||||||
|
/-- Fuel-bounded run.
|
||||||
|
|
||||||
|
Stops when:
|
||||||
|
- fuel exhausted
|
||||||
|
- machine halted
|
||||||
|
- an error occurs
|
||||||
|
-/
|
||||||
|
def run (fuel : Fuel) (program : List Instr) (s : State) : Outcome State :=
|
||||||
|
match fuel with
|
||||||
|
| 0 => Outcome.ok s
|
||||||
|
| Nat.succ f =>
|
||||||
|
if s.halted then Outcome.ok s
|
||||||
|
else
|
||||||
|
match step program s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok s1 => run f program s1
|
||||||
|
|
||||||
|
/-- Canary: boolean not.
|
||||||
|
|
||||||
|
#eval should produce `true` on top of stack.
|
||||||
|
-/
|
||||||
|
def canaryNot : List Instr :=
|
||||||
|
[
|
||||||
|
Instr.push ⟨AvmTy.bool, AvmVal.b false⟩,
|
||||||
|
Instr.prim Prim.not,
|
||||||
|
Instr.halt
|
||||||
|
]
|
||||||
|
|
||||||
|
/-- Canary initial state. -/
|
||||||
|
def canaryState : State :=
|
||||||
|
{ pc := 0, stack := [], locals := [], halted := false }
|
||||||
|
|
||||||
|
#eval run 8 canaryNot canaryState
|
||||||
|
|
||||||
|
end Semantics.AVMIsa
|
||||||
31
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/State.lean
Normal file
31
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/State.lean
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
-- AVM ISA v1 (Lean-only): State
|
||||||
|
|
||||||
|
import Semantics.AVMIsa.Instr
|
||||||
|
|
||||||
|
namespace Semantics.AVMIsa
|
||||||
|
|
||||||
|
/-- Machine state.
|
||||||
|
|
||||||
|
This is intentionally minimal in v1. It is sufficient to define a total `run`
|
||||||
|
with fuel.
|
||||||
|
-/
|
||||||
|
structure State where
|
||||||
|
pc : Nat
|
||||||
|
stack : List AnyVal
|
||||||
|
locals : List (Option AnyVal)
|
||||||
|
halted : Bool
|
||||||
|
|
||||||
|
deriving Inhabited
|
||||||
|
|
||||||
|
/-- Safe locals lookup (returns `none` when out of bounds). -/
|
||||||
|
def getLocal? (s : State) (i : Nat) : Option AnyVal :=
|
||||||
|
s.locals.getD i none
|
||||||
|
|
||||||
|
/-- Safe locals set (no-op when out of bounds). -/
|
||||||
|
def setLocal (s : State) (i : Nat) (v : AnyVal) : State :=
|
||||||
|
if h : i < s.locals.length then
|
||||||
|
{ s with locals := s.locals.set ⟨i, h⟩ (some v) }
|
||||||
|
else
|
||||||
|
s
|
||||||
|
|
||||||
|
end Semantics.AVMIsa
|
||||||
162
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Step.lean
Normal file
162
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Step.lean
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
-- AVM ISA v1 (Lean-only): Step semantics
|
||||||
|
|
||||||
|
import Semantics.AVMIsa.State
|
||||||
|
|
||||||
|
namespace Semantics.AVMIsa
|
||||||
|
|
||||||
|
/-- Error tags for rejected states (ill-typed, underflow, etc.). -/
|
||||||
|
inductive StepError : Type where
|
||||||
|
| stackUnderflow
|
||||||
|
| typeMismatch
|
||||||
|
| invalidJump
|
||||||
|
| missingLocal
|
||||||
|
deriving Inhabited, DecidableEq, BEq
|
||||||
|
|
||||||
|
/-- Outcome type for AVM execution.
|
||||||
|
|
||||||
|
We avoid Float and avoid exceptions. Backends should mirror this boundary.
|
||||||
|
-/
|
||||||
|
inductive Outcome (α : Type) : Type where
|
||||||
|
| ok : α → Outcome α
|
||||||
|
| err : StepError → Outcome α
|
||||||
|
deriving Inhabited
|
||||||
|
|
||||||
|
/-- Pop one element from stack. -/
|
||||||
|
def pop1 (s : State) : Outcome (AnyVal × State) :=
|
||||||
|
match s.stack with
|
||||||
|
| [] => Outcome.err StepError.stackUnderflow
|
||||||
|
| x :: xs => Outcome.ok (x, { s with stack := xs })
|
||||||
|
|
||||||
|
/-- Push one element onto stack. -/
|
||||||
|
def push1 (s : State) (v : AnyVal) : State :=
|
||||||
|
{ s with stack := v :: s.stack }
|
||||||
|
|
||||||
|
/-- Evaluate a primitive.
|
||||||
|
|
||||||
|
NOTE: This v1 skeleton implements only a small subset. Extend strictly by
|
||||||
|
adding Lean semantics; do not delegate meaning to backends.
|
||||||
|
-/
|
||||||
|
def evalPrim (p : Prim) (s : State) : Outcome State :=
|
||||||
|
match p with
|
||||||
|
| Prim.not =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v, s1) =>
|
||||||
|
match v.ty with
|
||||||
|
| AvmTy.bool =>
|
||||||
|
let b := match v.val with | AvmVal.b x => x
|
||||||
|
Outcome.ok (push1 s1 ⟨AvmTy.bool, AvmVal.b (!b)⟩)
|
||||||
|
| _ => Outcome.err StepError.typeMismatch
|
||||||
|
| Prim.and =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v1, s1) =>
|
||||||
|
match pop1 s1 with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v2, s2) =>
|
||||||
|
if v1.ty = AvmTy.bool ∧ v2.ty = AvmTy.bool then
|
||||||
|
let b1 := match v1.val with | AvmVal.b x => x
|
||||||
|
let b2 := match v2.val with | AvmVal.b x => x
|
||||||
|
Outcome.ok (push1 s2 ⟨AvmTy.bool, AvmVal.b (b2 && b1)⟩)
|
||||||
|
else
|
||||||
|
Outcome.err StepError.typeMismatch
|
||||||
|
| Prim.or =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v1, s1) =>
|
||||||
|
match pop1 s1 with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v2, s2) =>
|
||||||
|
if v1.ty = AvmTy.bool ∧ v2.ty = AvmTy.bool then
|
||||||
|
let b1 := match v1.val with | AvmVal.b x => x
|
||||||
|
let b2 := match v2.val with | AvmVal.b x => x
|
||||||
|
Outcome.ok (push1 s2 ⟨AvmTy.bool, AvmVal.b (b2 || b1)⟩)
|
||||||
|
else
|
||||||
|
Outcome.err StepError.typeMismatch
|
||||||
|
| Prim.addSatQ0 =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v1, s1) =>
|
||||||
|
match pop1 s1 with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v2, s2) =>
|
||||||
|
if v1.ty = AvmTy.q0_16 ∧ v2.ty = AvmTy.q0_16 then
|
||||||
|
let x := match v1.val with | AvmVal.q0 q => q
|
||||||
|
let y := match v2.val with | AvmVal.q0 q => q
|
||||||
|
Outcome.ok (push1 s2 ⟨AvmTy.q0_16, AvmVal.q0 (Semantics.Q0_16.addSat y x)⟩)
|
||||||
|
else
|
||||||
|
Outcome.err StepError.typeMismatch
|
||||||
|
| Prim.subSatQ0 =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v1, s1) =>
|
||||||
|
match pop1 s1 with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v2, s2) =>
|
||||||
|
if v1.ty = AvmTy.q0_16 ∧ v2.ty = AvmTy.q0_16 then
|
||||||
|
let x := match v1.val with | AvmVal.q0 q => q
|
||||||
|
let y := match v2.val with | AvmVal.q0 q => q
|
||||||
|
Outcome.ok (push1 s2 ⟨AvmTy.q0_16, AvmVal.q0 (Semantics.Q0_16.subSat y x)⟩)
|
||||||
|
else
|
||||||
|
Outcome.err StepError.typeMismatch
|
||||||
|
| _ =>
|
||||||
|
-- Remaining primitives are not yet implemented in v1.
|
||||||
|
Outcome.err StepError.typeMismatch
|
||||||
|
|
||||||
|
/-- One-step execution.
|
||||||
|
|
||||||
|
`Program` is modeled as a list for v1.
|
||||||
|
-/
|
||||||
|
def step (program : List Instr) (s : State) : Outcome State :=
|
||||||
|
if s.halted then
|
||||||
|
Outcome.ok s
|
||||||
|
else
|
||||||
|
match program.get? s.pc with
|
||||||
|
| none => Outcome.err StepError.invalidJump
|
||||||
|
| some instr =>
|
||||||
|
match instr with
|
||||||
|
| Instr.halt => Outcome.ok { s with halted := true }
|
||||||
|
| Instr.push v => Outcome.ok { s with pc := s.pc + 1, stack := v :: s.stack }
|
||||||
|
| Instr.pop =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (_, s1) => Outcome.ok { s1 with pc := s.pc + 1 }
|
||||||
|
| Instr.dup =>
|
||||||
|
match s.stack with
|
||||||
|
| [] => Outcome.err StepError.stackUnderflow
|
||||||
|
| x :: xs => Outcome.ok { s with pc := s.pc + 1, stack := x :: x :: xs }
|
||||||
|
| Instr.swap =>
|
||||||
|
match s.stack with
|
||||||
|
| a :: b :: xs => Outcome.ok { s with pc := s.pc + 1, stack := b :: a :: xs }
|
||||||
|
| _ => Outcome.err StepError.stackUnderflow
|
||||||
|
| Instr.load i =>
|
||||||
|
match getLocal? s i with
|
||||||
|
| none => Outcome.err StepError.missingLocal
|
||||||
|
| some v => Outcome.ok { s with pc := s.pc + 1, stack := v :: s.stack }
|
||||||
|
| Instr.store i =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v, s1) => Outcome.ok { (setLocal s1 i v) with pc := s.pc + 1 }
|
||||||
|
| Instr.jump target =>
|
||||||
|
if target < program.length then
|
||||||
|
Outcome.ok { s with pc := target }
|
||||||
|
else
|
||||||
|
Outcome.err StepError.invalidJump
|
||||||
|
| Instr.jumpIf target =>
|
||||||
|
match pop1 s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok (v, s1) =>
|
||||||
|
if v.ty = AvmTy.bool then
|
||||||
|
let b := match v.val with | AvmVal.b x => x
|
||||||
|
if b then
|
||||||
|
if target < program.length then Outcome.ok { s1 with pc := target } else Outcome.err StepError.invalidJump
|
||||||
|
else
|
||||||
|
Outcome.ok { s1 with pc := s.pc + 1 }
|
||||||
|
else
|
||||||
|
Outcome.err StepError.typeMismatch
|
||||||
|
| Instr.prim p =>
|
||||||
|
match evalPrim p s with
|
||||||
|
| Outcome.err e => Outcome.err e
|
||||||
|
| Outcome.ok s1 => Outcome.ok { s1 with pc := s.pc + 1 }
|
||||||
|
|
||||||
|
end Semantics.AVMIsa
|
||||||
15
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Types.lean
Normal file
15
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Types.lean
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
-- AVM ISA v1 (Lean-only): Types
|
||||||
|
-- Core rule: closed-world finite type universe; no Float.
|
||||||
|
|
||||||
|
import Semantics.FixedPoint
|
||||||
|
|
||||||
|
namespace Semantics.AVMIsa
|
||||||
|
|
||||||
|
/-- Finite AVM type universe. Closed-world; extend only by adding constructors. -/
|
||||||
|
inductive AvmTy : Type where
|
||||||
|
| q0_16 : AvmTy
|
||||||
|
| q16_16 : AvmTy
|
||||||
|
| bool : AvmTy
|
||||||
|
deriving DecidableEq, BEq, Inhabited
|
||||||
|
|
||||||
|
end Semantics.AVMIsa
|
||||||
29
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Value.lean
Normal file
29
0-Core-Formalism/lean/Semantics/Semantics/AVMIsa/Value.lean
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
-- AVM ISA v1 (Lean-only): Values
|
||||||
|
-- Values are strictly typed; no dynamic Any.
|
||||||
|
|
||||||
|
import Semantics.AVMIsa.Types
|
||||||
|
import Semantics.FixedPoint
|
||||||
|
|
||||||
|
namespace Semantics.AVMIsa
|
||||||
|
|
||||||
|
/-- Typed value payload. -/
|
||||||
|
inductive AvmVal : AvmTy → Type where
|
||||||
|
| q0 : Semantics.Q0_16 → AvmVal AvmTy.q0_16
|
||||||
|
| q16 : Semantics.Q16_16 → AvmVal AvmTy.q16_16
|
||||||
|
| b : Bool → AvmVal AvmTy.bool
|
||||||
|
|
||||||
|
/-- Existential wrapper for storing values in an untyped container.
|
||||||
|
|
||||||
|
We use this in the ISA skeleton to keep the state representation simple.
|
||||||
|
Typing is enforced by an explicit type-check pass and by instruction semantics
|
||||||
|
that can reject ill-typed stacks.
|
||||||
|
|
||||||
|
Later upgrade path: replace with a fully typed stack representation.
|
||||||
|
-/
|
||||||
|
structure AnyVal where
|
||||||
|
ty : AvmTy
|
||||||
|
val : AvmVal ty
|
||||||
|
|
||||||
|
deriving Inhabited
|
||||||
|
|
||||||
|
end Semantics.AVMIsa
|
||||||
Loading…
Add table
Reference in a new issue