SilverSight/scala/AVMIsa/avm.scala
allaun f6cbddcbf2 feat(tests): Python AVM port rewrite + test harness, Go test harness
Python port rewritten to match spec:
- Added Q0_16, PUSH_Q0, PUSH_BOOL as separate opcodes
- Added V6 comparison (lt_q16_v6)
- Added floor division (Lean Int.ediv)
- Added stack depth limit (AVM_MAX_STACK = 1024)
- Added type checking in exec_prim
- All 10 tests passing

Go AVM port: added test_avm_test.go with 8 test cases

Milestone: Python → , Go → 🔄
2026-06-30 17:56:09 -05:00

101 lines
4.5 KiB
Scala

// AVM ISA v1 — Scala Port (Strict Functional Execution)
package avm
object AVM {
val Q16Scale: Int = 65536
def q16Mul(a: Int, b: Int): Int = ((a.toLong * b.toLong) / Q16Scale).toInt
def q16Div(a: Int, b: Int): Int = {
if (b == 0) throw new ArithmeticException("div by zero")
((a.toLong * Q16Scale) / b).toInt
}
// ── Types ───────────────────────────────────────
sealed trait ValType
case object Q0 extends ValType
case object Q16 extends ValType
case object BoolT extends ValType
case class AvmVal(ty: ValType, raw: Int)
object AvmVal {
def q16(x: Int): AvmVal = AvmVal(Q16, x)
def bool(x: Boolean): AvmVal = AvmVal(BoolT, if (x) 1 else 0)
}
// ── Primitives ──────────────────────────────────
sealed trait Prim
case object AddQ16 extends Prim; case object SubQ16 extends Prim
case object MulQ16 extends Prim; case object DivQ16 extends Prim
case object LtQ16 extends Prim; case object EqQ16 extends Prim
case object And extends Prim; case object Or extends Prim; case object Not extends Prim
def execPrim(p: Prim, a: AvmVal, b: Option[AvmVal]): AvmVal = (p, b) match {
case (AddQ16, Some(bv)) => AvmVal.q16(a.raw + bv.raw)
case (SubQ16, Some(bv)) => AvmVal.q16(a.raw - bv.raw)
case (MulQ16, Some(bv)) => AvmVal.q16(q16Mul(a.raw, bv.raw))
case (DivQ16, Some(bv)) => AvmVal.q16(q16Div(a.raw, bv.raw))
case (LtQ16, Some(bv)) => AvmVal.bool(a.raw < bv.raw)
case (EqQ16, Some(bv)) => AvmVal.bool(a.raw == bv.raw)
case (And, Some(bv)) => AvmVal.bool(a.raw != 0 && bv.raw != 0)
case (Or, Some(bv)) => AvmVal.bool(a.raw != 0 || bv.raw != 0)
case (Not, _) => AvmVal.bool(a.raw == 0)
case _ => throw new RuntimeException("type mismatch")
}
// ── Instructions ────────────────────────────────
sealed trait Instr
case class PushQ16(x: Int) extends Instr
case class PushBool(x: Boolean) extends Instr
case object Pop extends Instr; case object Dup extends Instr
case object Swap extends Instr
case class Load(i: Int) extends Instr; case class Store(i: Int) extends Instr
case class Jump(t: Int) extends Instr; case class JumpIf(t: Int) extends Instr
case class PrimOp(p: Prim) extends Instr; case object Halt extends Instr
// ── State ───────────────────────────────────────
case class State(pc: Int, stack: List[AvmVal], locals: Vector[Option[AvmVal]],
halted: Boolean)
object State { def apply(nLocals: Int = 0): State =
State(0, Nil, Vector.fill(nLocals)(None), false)
}
// ── Step ────────────────────────────────────────
def step(s: State, prog: Vector[Instr]): State = {
if (s.halted) throw new RuntimeException("halted")
if (s.pc < 0 || s.pc >= prog.length) return s.copy(halted = true)
prog(s.pc) match {
case PushQ16(x) => s.copy(pc = s.pc + 1, stack = AvmVal.q16(x) :: s.stack)
case PushBool(x) => s.copy(pc = s.pc + 1, stack = AvmVal.bool(x) :: s.stack)
case Pop => s.copy(pc = s.pc + 1, stack = s.stack.tail)
case Dup => s.copy(pc = s.pc + 1, stack = s.stack.head :: s.stack)
case Swap =>
val a :: b :: rest = s.stack
s.copy(pc = s.pc + 1, stack = b :: a :: rest)
case Load(i) =>
val v = s.locals(i).getOrElse(throw new RuntimeException("missing local"))
s.copy(pc = s.pc + 1, stack = v :: s.stack)
case Store(i) =>
s.copy(pc = s.pc + 1, locals = s.locals.updated(i, Some(s.stack.head)),
stack = s.stack.tail)
case Jump(t) => s.copy(pc = t)
case JumpIf(t) =>
if (s.stack.head.raw != 0) s.copy(pc = t, stack = s.stack.tail)
else s.copy(pc = s.pc + 1, stack = s.stack.tail)
case PrimOp(p) =>
val arity = if (p == Not) 1 else 2
val b = if (arity >= 2) Some(s.stack.head) else None
val rest = if (arity >= 2) s.stack.tail else s.stack
val a = rest.head
val result = execPrim(p, a, b)
s.copy(pc = s.pc + 1, stack = result :: rest.tail)
case Halt => s.copy(halted = true)
}
}
def run(init: State, prog: Vector[Instr], fuel: Int): State = {
var s = init
for (_ <- 0 until fuel if !s.halted) s = step(s, prog)
s
}
}