| """
|
| vm_executor.py — Minimal stack-based virtual machine for Sovereign IR.
|
|
|
| Executes sovereign IR instructions (VMInstruction objects) on a stack machine.
|
| Enforces the DSL entropy constraint (H <= 0.20) at runtime.
|
| Includes NAND-complete Boolean kernel, routing dispatch, and WORM commit hooks.
|
|
|
| Part of the SOVEREIGN_IR PYTHON_C_BRIDGE_IR pipeline.
|
| Agent A (Cognition) — HyperKittyConstraintDSL v1.0
|
| """
|
|
|
| from __future__ import annotations
|
|
|
| import hashlib
|
| import io
|
| import math
|
| import struct
|
| import time
|
| from dataclasses import dataclass, field
|
| from enum import IntEnum
|
| from typing import Any, Callable, Optional
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMOpcode(IntEnum):
|
|
|
| NOP = 0x00
|
| PUSH = 0x01
|
| POP = 0x02
|
| DUP = 0x03
|
| SWAP = 0x04
|
| COPY = 0x05
|
| ROT = 0x06
|
|
|
|
|
| ADD = 0x10
|
| SUB = 0x11
|
| MUL = 0x12
|
| DIV = 0x13
|
| MOD = 0x14
|
| NEG = 0x15
|
| ABS = 0x16
|
|
|
|
|
| AND = 0x20
|
| OR = 0x21
|
| NOT = 0x22
|
| NAND = 0x23
|
| XOR = 0x24
|
| XNOR = 0x25
|
| NOR = 0x26
|
|
|
|
|
| EQ = 0x30
|
| NEQ = 0x31
|
| LT = 0x32
|
| GT = 0x33
|
| LE = 0x34
|
| GE = 0x35
|
|
|
|
|
| BAND = 0x38
|
| BOR = 0x39
|
| BXOR = 0x3A
|
| BNOT = 0x3B
|
| SHL = 0x3C
|
| SHR = 0x3D
|
|
|
|
|
| JMP = 0x40
|
| JZ = 0x41
|
| JNZ = 0x42
|
| CALL = 0x43
|
| RET = 0x44
|
| LOOP = 0x45
|
|
|
|
|
| ROUTE = 0x50
|
| DISPATCH = 0x51
|
| GATE = 0x52
|
| FILTER = 0x53
|
| FORWARD = 0x54
|
|
|
|
|
| LOAD = 0x60
|
| STORE = 0x61
|
| ALLOC = 0x62
|
| FREE = 0x63
|
|
|
|
|
| COMMIT = 0x70
|
| CHECKPOINT= 0x71
|
| ROLLBACK = 0x72
|
| SEAL = 0x73
|
| VERIFY = 0x74
|
|
|
|
|
| PRINT = 0x80
|
| READ = 0x81
|
| EMIT = 0x82
|
|
|
|
|
| MEASURE = 0x90
|
| CLAMP = 0x91
|
| ENTROPY = 0x92
|
|
|
|
|
| HALT = 0xFF
|
|
|
|
|
|
|
|
|
|
|
|
|
| @dataclass
|
| class VMInstruction:
|
| opcode: VMOpcode
|
| arg: int = 0
|
| symbol: str = ''
|
| lineno: int = 0
|
|
|
| def __post_init__(self):
|
| if isinstance(self.opcode, int):
|
| self.opcode = VMOpcode(self.opcode)
|
|
|
| def __repr__(self) -> str:
|
| parts = [self.opcode.name]
|
| if self.arg != 0:
|
| parts.append(str(self.arg))
|
| if self.symbol:
|
| parts.append(repr(self.symbol))
|
| return f"VMInstruction({', '.join(parts)})"
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMStackUnderflow(Exception):
|
| pass
|
|
|
| class VMStackOverflow(Exception):
|
| pass
|
|
|
|
|
| class VMStack:
|
| """LIFO stack with optional maximum depth limit."""
|
|
|
| DEFAULT_MAX = 4096
|
|
|
| def __init__(self, max_depth: int = DEFAULT_MAX):
|
| self._data: list[Any] = []
|
| self._max_depth = max_depth
|
|
|
| def push(self, value: Any) -> None:
|
| if len(self._data) >= self._max_depth:
|
| raise VMStackOverflow(
|
| f"Stack overflow at depth {self._max_depth}"
|
| )
|
| self._data.append(value)
|
|
|
| def pop(self) -> Any:
|
| if not self._data:
|
| raise VMStackUnderflow("Stack underflow: pop on empty stack")
|
| return self._data.pop()
|
|
|
| def peek(self, offset: int = 0) -> Any:
|
| """Peek at the top (offset=0) or nth item from top (offset=n)."""
|
| idx = -(offset + 1)
|
| if abs(idx) > len(self._data):
|
| raise VMStackUnderflow(f"Stack peek offset {offset} out of range")
|
| return self._data[idx]
|
|
|
| def peek_n(self, n: int) -> list[Any]:
|
| """Return top n items (bottom-first order)."""
|
| if n > len(self._data):
|
| raise VMStackUnderflow(f"Stack peek_n {n} > depth {len(self._data)}")
|
| return list(self._data[-n:])
|
|
|
| def is_empty(self) -> bool:
|
| return len(self._data) == 0
|
|
|
| def size(self) -> int:
|
| return len(self._data)
|
|
|
| def clear(self) -> None:
|
| self._data.clear()
|
|
|
| def as_list(self) -> list[Any]:
|
| return list(self._data)
|
|
|
| def to_display(self) -> str:
|
| if not self._data:
|
| return "[empty]"
|
| items = [repr(x) for x in reversed(self._data)]
|
| return "[" + ", ".join(items[:8]) + (", ..." if len(items) > 8 else "") + "]"
|
|
|
|
|
|
|
|
|
|
|
|
|
| @dataclass
|
| class VMRegisters:
|
| pc: int = 0
|
| sp: int = 0
|
| flags: int = 0
|
| entropy: float = 0.0
|
|
|
| FLAG_ZERO = 1 << 0
|
| FLAG_CARRY = 1 << 1
|
| FLAG_OVERFLOW = 1 << 2
|
| FLAG_NEGATIVE = 1 << 3
|
| FLAG_HALT = 1 << 7
|
|
|
| def set_zero(self, value: Any) -> None:
|
| if value == 0 or value is False:
|
| self.flags |= self.FLAG_ZERO
|
| else:
|
| self.flags &= ~self.FLAG_ZERO
|
|
|
| def set_negative(self, value: Any) -> None:
|
| try:
|
| if float(value) < 0:
|
| self.flags |= self.FLAG_NEGATIVE
|
| else:
|
| self.flags &= ~self.FLAG_NEGATIVE
|
| except (TypeError, ValueError):
|
| pass
|
|
|
| def is_zero(self) -> bool:
|
| return bool(self.flags & self.FLAG_ZERO)
|
|
|
| def is_halted(self) -> bool:
|
| return bool(self.flags & self.FLAG_HALT)
|
|
|
| def halt(self) -> None:
|
| self.flags |= self.FLAG_HALT
|
|
|
| def update(self, value: Any) -> None:
|
| self.set_zero(value)
|
| self.set_negative(value)
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMMemory:
|
| """
|
| Simple 64KB address space memory for the VM.
|
| Byte-addressable, no protection.
|
| """
|
|
|
| SIZE = 65536
|
|
|
| def __init__(self, size: int = SIZE):
|
| self._data = bytearray(size)
|
| self._size = size
|
|
|
| def read(self, addr: int) -> int:
|
| self._check(addr, 1)
|
| return self._data[addr]
|
|
|
| def write(self, addr: int, value: int) -> None:
|
| self._check(addr, 1)
|
| self._data[addr] = value & 0xFF
|
|
|
| def read_bytes(self, addr: int, n: int) -> bytes:
|
| self._check(addr, n)
|
| return bytes(self._data[addr:addr + n])
|
|
|
| def write_bytes(self, addr: int, data: bytes) -> None:
|
| self._check(addr, len(data))
|
| self._data[addr:addr + len(data)] = data
|
|
|
| def read_u32(self, addr: int) -> int:
|
| return struct.unpack_from('<I', self._data, addr)[0]
|
|
|
| def write_u32(self, addr: int, value: int) -> None:
|
| struct.pack_into('<I', self._data, addr, value & 0xFFFFFFFF)
|
|
|
| def read_u64(self, addr: int) -> int:
|
| return struct.unpack_from('<Q', self._data, addr)[0]
|
|
|
| def write_u64(self, addr: int, value: int) -> None:
|
| struct.pack_into('<Q', self._data, addr, value & 0xFFFFFFFFFFFFFFFF)
|
|
|
| def fill(self, addr: int, byte: int, n: int) -> None:
|
| self._check(addr, n)
|
| for i in range(n):
|
| self._data[addr + i] = byte & 0xFF
|
|
|
| def _check(self, addr: int, size: int) -> None:
|
| if addr < 0 or addr + size > self._size:
|
| raise VMError(f"Memory access out of bounds: addr={addr:#x}, size={size}")
|
|
|
| def size(self) -> int:
|
| return self._size
|
|
|
| def dump(self, addr: int = 0, length: int = 64) -> str:
|
| """Hex dump."""
|
| lines = []
|
| for off in range(0, length, 16):
|
| chunk = self._data[addr + off:addr + off + 16]
|
| hex_part = ' '.join(f'{b:02x}' for b in chunk)
|
| ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
|
| lines.append(f'{addr + off:04x} {hex_part:<48} |{ascii_part}|')
|
| return '\n'.join(lines)
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMError(Exception):
|
| pass
|
|
|
| class VMEntropyViolation(VMError):
|
| pass
|
|
|
| class VMHaltException(Exception):
|
| pass
|
|
|
|
|
|
|
|
|
|
|
|
|
| @dataclass
|
| class WORMEntry:
|
| seq: int
|
| timestamp_ns: int
|
| opcode: VMOpcode
|
| data: bytes
|
| checksum: bytes
|
|
|
| def verify(self) -> bool:
|
| expected = hashlib.blake2b(self.data, digest_size=32).digest()
|
| return expected == self.checksum
|
|
|
|
|
| class WORMLog:
|
| """Append-only log for VM state commits."""
|
|
|
| def __init__(self):
|
| self._entries: list[WORMEntry] = []
|
| self._seq = 0
|
|
|
| def append(self, opcode: VMOpcode, data: bytes) -> WORMEntry:
|
| checksum = hashlib.blake2b(data, digest_size=32).digest()
|
| entry = WORMEntry(
|
| seq=self._seq,
|
| timestamp_ns=time.time_ns(),
|
| opcode=opcode,
|
| data=data,
|
| checksum=checksum,
|
| )
|
| self._entries.append(entry)
|
| self._seq += 1
|
| return entry
|
|
|
| def last(self) -> Optional[WORMEntry]:
|
| return self._entries[-1] if self._entries else None
|
|
|
| def count(self) -> int:
|
| return len(self._entries)
|
|
|
| def all_valid(self) -> bool:
|
| return all(e.verify() for e in self._entries)
|
|
|
| def export(self) -> list[dict]:
|
| return [
|
| {
|
| 'seq': e.seq,
|
| 'timestamp_ns': e.timestamp_ns,
|
| 'opcode': e.opcode.name,
|
| 'data_len': len(e.data),
|
| 'checksum': e.checksum.hex(),
|
| 'valid': e.verify(),
|
| }
|
| for e in self._entries
|
| ]
|
|
|
|
|
|
|
|
|
|
|
|
|
| def nand_op(a: Any, b: Any) -> int:
|
| """NAND gate — the universal primitive."""
|
| ai = 1 if a else 0
|
| bi = 1 if b else 0
|
| return 1 - ai * bi
|
|
|
|
|
| def nand_not(x: Any) -> int:
|
| return nand_op(x, x)
|
|
|
|
|
| def nand_and(a: Any, b: Any) -> int:
|
| n = nand_op(a, b)
|
| return nand_op(n, n)
|
|
|
|
|
| def nand_or(a: Any, b: Any) -> int:
|
| na = nand_op(a, a)
|
| nb = nand_op(b, b)
|
| return nand_op(na, nb)
|
|
|
|
|
| def nand_xor(a: Any, b: Any) -> int:
|
| ab = nand_op(a, b)
|
| a_ab = nand_op(a, ab)
|
| b_ab = nand_op(b, ab)
|
| return nand_op(a_ab, b_ab)
|
|
|
|
|
| def nand_xnor(a: Any, b: Any) -> int:
|
| return nand_not(nand_xor(a, b))
|
|
|
|
|
| def nand_nor(a: Any, b: Any) -> int:
|
| return nand_not(nand_or(a, b))
|
|
|
|
|
|
|
|
|
|
|
|
|
| class DispatchTable:
|
| """
|
| Maps opcodes to Python callables for ROUTE / DISPATCH instructions.
|
| """
|
|
|
| def __init__(self):
|
| self._table: dict[int, Callable] = {}
|
|
|
| def register(self, opcode: int, handler: Callable) -> None:
|
| self._table[opcode] = handler
|
|
|
| def dispatch(self, opcode: int, *args) -> Any:
|
| handler = self._table.get(opcode)
|
| if handler is None:
|
| raise VMError(f"No handler registered for opcode {opcode:#04x}")
|
| return handler(*args)
|
|
|
| def has(self, opcode: int) -> bool:
|
| return opcode in self._table
|
|
|
| def count(self) -> int:
|
| return len(self._table)
|
|
|
|
|
|
|
|
|
|
|
|
|
| @dataclass
|
| class CallFrame:
|
| return_pc: int
|
| local_vars: dict = field(default_factory=dict)
|
| saved_registers: VMRegisters = field(default_factory=VMRegisters)
|
|
|
|
|
|
|
|
|
|
|
|
|
| class SovereignVM:
|
| """
|
| Minimal stack-based VM that executes VMInstruction programs.
|
|
|
| Features:
|
| - NAND-complete Boolean kernel
|
| - Entropy constraint enforcement (H <= 0.20 per DSL)
|
| - WORM commit / checkpoint via WORMLog
|
| - Routing dispatch table
|
| - 64KB memory space
|
| - Call/return stack for subroutines
|
| """
|
|
|
| ENTROPY_LIMIT = 0.20
|
|
|
| def __init__(
|
| self,
|
| program: list[VMInstruction],
|
| dispatch_table: Optional[DispatchTable] = None,
|
| output_buffer: Optional[io.StringIO] = None,
|
| ):
|
| self._program = program
|
| self._stack = VMStack()
|
| self._registers = VMRegisters()
|
| self._memory = VMMemory()
|
| self._worm = WORMLog()
|
| self._call_stack: list[CallFrame] = []
|
| self._local_vars: dict[str, Any] = {}
|
| self._dispatch = dispatch_table or DispatchTable()
|
| self._output = output_buffer or io.StringIO()
|
| self._checkpoints: list[dict] = []
|
| self._step_count = 0
|
| self._max_steps = 100_000
|
|
|
|
|
|
|
| def step(self) -> bool:
|
| """
|
| Execute one instruction.
|
| Returns False if HALT was reached, True otherwise.
|
| """
|
| pc = self._registers.pc
|
| if pc < 0 or pc >= len(self._program):
|
| self._registers.halt()
|
| return False
|
|
|
| instr = self._program[pc]
|
| self._registers.pc += 1
|
| self._step_count += 1
|
|
|
| try:
|
| self._execute(instr)
|
| except VMHaltException:
|
| self._registers.halt()
|
| return False
|
| except VMEntropyViolation:
|
| raise
|
| except VMStackUnderflow as exc:
|
| raise VMError(f"Stack underflow at pc={pc}: {exc}") from exc
|
|
|
| return not self._registers.is_halted()
|
|
|
| def run(self, max_steps: int = 10_000) -> Any:
|
| """
|
| Run for up to max_steps instructions.
|
| Returns top of stack, or None if stack is empty.
|
| """
|
| self._max_steps = max_steps
|
| steps = 0
|
| while steps < max_steps:
|
| if not self.step():
|
| break
|
| steps += 1
|
| else:
|
| raise VMError(f"VM exceeded max_steps={max_steps}")
|
|
|
| return self._stack.peek() if not self._stack.is_empty() else None
|
|
|
| def run_until_halt(self) -> Any:
|
| return self.run(max_steps=self._max_steps)
|
|
|
| def get_registers(self) -> VMRegisters:
|
| return VMRegisters(
|
| pc=self._registers.pc,
|
| sp=self._stack.size(),
|
| flags=self._registers.flags,
|
| entropy=self._registers.entropy,
|
| )
|
|
|
| def get_stack(self) -> list:
|
| return self._stack.as_list()
|
|
|
| def reset(self) -> None:
|
| """Reset VM to initial state."""
|
| self._stack.clear()
|
| self._registers = VMRegisters()
|
| self._memory = VMMemory()
|
| self._call_stack.clear()
|
| self._local_vars.clear()
|
| self._checkpoints.clear()
|
| self._step_count = 0
|
|
|
| def check_entropy_constraint(self) -> bool:
|
| """DSL constraint: H <= 0.20"""
|
| return self._registers.entropy <= self.ENTROPY_LIMIT
|
|
|
| def compute_entropy(self) -> float:
|
| """
|
| Shannon entropy of stack value distribution.
|
| H = -sum(p * ln(p))
|
| Normalized to [0.0, 1.0] by dividing by ln(N).
|
| """
|
| data = self._stack.as_list()
|
| if len(data) < 2:
|
| return 0.0
|
|
|
|
|
| counts: dict = {}
|
| for v in data:
|
| key = type(v).__name__ + ':' + str(v)[:32]
|
| counts[key] = counts.get(key, 0) + 1
|
|
|
| n = len(data)
|
| h = 0.0
|
| for cnt in counts.values():
|
| p = cnt / n
|
| if p > 0:
|
| h -= p * math.log(p)
|
|
|
|
|
| max_h = math.log(n) if n > 1 else 1.0
|
| return h / max_h if max_h > 0 else 0.0
|
|
|
|
|
|
|
| def _execute(self, instr: VMInstruction) -> None:
|
| op = instr.opcode
|
| arg = instr.arg
|
| sym = instr.symbol
|
|
|
|
|
| if op == VMOpcode.NOP:
|
| pass
|
|
|
| elif op == VMOpcode.PUSH:
|
|
|
| self._stack.push(sym if sym else arg)
|
| self._update_entropy()
|
|
|
| elif op == VMOpcode.POP:
|
| self._stack.pop()
|
|
|
| elif op == VMOpcode.DUP:
|
| self._stack.push(self._stack.peek())
|
|
|
| elif op == VMOpcode.COPY:
|
| self._stack.push(self._stack.peek(arg))
|
|
|
| elif op == VMOpcode.SWAP:
|
| a = self._stack.pop()
|
| b = self._stack.pop()
|
| self._stack.push(a)
|
| self._stack.push(b)
|
|
|
| elif op == VMOpcode.ROT:
|
| c = self._stack.pop()
|
| b = self._stack.pop()
|
| a = self._stack.pop()
|
| self._stack.push(b)
|
| self._stack.push(c)
|
| self._stack.push(a)
|
|
|
|
|
| elif op == VMOpcode.ADD:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| result = self._coerce(a) + self._coerce(b)
|
| self._stack.push(result)
|
| self._registers.update(result)
|
|
|
| elif op == VMOpcode.SUB:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| result = self._coerce(a) - self._coerce(b)
|
| self._stack.push(result)
|
| self._registers.update(result)
|
|
|
| elif op == VMOpcode.MUL:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| result = self._coerce(a) * self._coerce(b)
|
| self._stack.push(result)
|
| self._registers.update(result)
|
|
|
| elif op == VMOpcode.DIV:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| bf, af = self._coerce_f(b), self._coerce_f(a)
|
| if bf == 0.0:
|
| raise VMError("Division by zero")
|
| result = af / bf
|
| self._stack.push(result)
|
| self._registers.update(result)
|
|
|
| elif op == VMOpcode.MOD:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| bi, ai = int(self._coerce(b)), int(self._coerce(a))
|
| if bi == 0:
|
| raise VMError("Modulo by zero")
|
| self._stack.push(ai % bi)
|
|
|
| elif op == VMOpcode.NEG:
|
| a = self._stack.pop()
|
| self._stack.push(-self._coerce(a))
|
|
|
| elif op == VMOpcode.ABS:
|
| a = self._stack.pop()
|
| self._stack.push(abs(self._coerce(a)))
|
|
|
|
|
| elif op == VMOpcode.NAND:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(nand_op(a, b))
|
|
|
| elif op == VMOpcode.NOT:
|
| a = self._stack.pop()
|
| self._stack.push(nand_not(a))
|
|
|
| elif op == VMOpcode.AND:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(nand_and(a, b))
|
|
|
| elif op == VMOpcode.OR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(nand_or(a, b))
|
|
|
| elif op == VMOpcode.XOR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(nand_xor(a, b))
|
|
|
| elif op == VMOpcode.XNOR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(nand_xnor(a, b))
|
|
|
| elif op == VMOpcode.NOR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(nand_nor(a, b))
|
|
|
|
|
| elif op == VMOpcode.EQ:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(1 if a == b else 0)
|
|
|
| elif op == VMOpcode.NEQ:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(1 if a != b else 0)
|
|
|
| elif op == VMOpcode.LT:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(1 if self._coerce(a) < self._coerce(b) else 0)
|
|
|
| elif op == VMOpcode.GT:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(1 if self._coerce(a) > self._coerce(b) else 0)
|
|
|
| elif op == VMOpcode.LE:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(1 if self._coerce(a) <= self._coerce(b) else 0)
|
|
|
| elif op == VMOpcode.GE:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(1 if self._coerce(a) >= self._coerce(b) else 0)
|
|
|
|
|
| elif op == VMOpcode.BAND:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(int(self._coerce(a)) & int(self._coerce(b)))
|
|
|
| elif op == VMOpcode.BOR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(int(self._coerce(a)) | int(self._coerce(b)))
|
|
|
| elif op == VMOpcode.BXOR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(int(self._coerce(a)) ^ int(self._coerce(b)))
|
|
|
| elif op == VMOpcode.BNOT:
|
| a = self._stack.pop()
|
| self._stack.push(~int(self._coerce(a)))
|
|
|
| elif op == VMOpcode.SHL:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(int(self._coerce(a)) << (int(self._coerce(b)) & 63))
|
|
|
| elif op == VMOpcode.SHR:
|
| b, a = self._stack.pop(), self._stack.pop()
|
| self._stack.push(int(self._coerce(a)) >> (int(self._coerce(b)) & 63))
|
|
|
|
|
| elif op == VMOpcode.JMP:
|
| self._registers.pc = arg
|
|
|
| elif op == VMOpcode.JZ:
|
| top = self._stack.peek()
|
| if not top or top == 0:
|
| self._registers.pc = arg
|
|
|
| elif op == VMOpcode.JNZ:
|
| top = self._stack.peek()
|
| if top and top != 0:
|
| self._registers.pc = arg
|
|
|
| elif op == VMOpcode.CALL:
|
| frame = CallFrame(
|
| return_pc=self._registers.pc,
|
| local_vars=dict(self._local_vars),
|
| saved_registers=VMRegisters(
|
| pc=self._registers.pc,
|
| sp=self._stack.size(),
|
| flags=self._registers.flags,
|
| entropy=self._registers.entropy,
|
| ),
|
| )
|
| self._call_stack.append(frame)
|
| self._registers.pc = arg
|
|
|
| elif op == VMOpcode.RET:
|
| if not self._call_stack:
|
| raise VMHaltException("RETURN with empty call stack")
|
| frame = self._call_stack.pop()
|
| self._registers.pc = frame.return_pc
|
| self._local_vars = frame.local_vars
|
|
|
| elif op == VMOpcode.LOOP:
|
| count = self._stack.pop()
|
| count = int(self._coerce(count)) - 1
|
| if count > 0:
|
| self._stack.push(count)
|
| self._registers.pc = arg
|
|
|
|
|
|
|
| elif op == VMOpcode.ROUTE:
|
| if self._dispatch.has(arg):
|
| top = self._stack.peek() if not self._stack.is_empty() else None
|
| result = self._dispatch.dispatch(arg, top)
|
| if result is not None:
|
| self._stack.push(result)
|
|
|
|
|
| elif op == VMOpcode.DISPATCH:
|
| args_list = []
|
| n_args = arg
|
| for _ in range(n_args):
|
| args_list.insert(0, self._stack.pop())
|
| opcode_val = int(args_list[0]) if args_list else 0
|
| result = None
|
| if self._dispatch.has(opcode_val):
|
| result = self._dispatch.dispatch(opcode_val, *args_list[1:])
|
| if result is not None:
|
| self._stack.push(result)
|
|
|
| elif op == VMOpcode.GATE:
|
| """Jordan gate: evaluate |ψ⟩ under Φ^-2 entropy bound."""
|
| top = self._stack.peek() if not self._stack.is_empty() else 0
|
| entropy = self.compute_entropy()
|
|
|
| phi_sq_inv = (math.sqrt(5) - 1) / 2
|
| phi_sq_inv = 1.0 / (((1 + math.sqrt(5)) / 2) ** 2)
|
| gate_result = 1 if entropy <= phi_sq_inv else 0
|
| self._stack.push(gate_result)
|
|
|
| elif op == VMOpcode.FILTER:
|
| """Entropy filter gate — enforce DSL H <= 0.20."""
|
| entropy = self.compute_entropy()
|
| self._registers.entropy = entropy
|
| if entropy > self.ENTROPY_LIMIT:
|
|
|
| while self._stack.size() > 1 and self.compute_entropy() > self.ENTROPY_LIMIT:
|
| self._stack.pop()
|
|
|
| elif op == VMOpcode.FORWARD:
|
|
|
| pass
|
|
|
|
|
| elif op == VMOpcode.LOAD:
|
| addr = int(self._stack.pop())
|
| self._stack.push(self._memory.read(addr))
|
|
|
| elif op == VMOpcode.STORE:
|
| value = self._stack.pop()
|
| addr = int(self._stack.pop())
|
| self._memory.write(addr, int(self._coerce(value)))
|
|
|
| elif op == VMOpcode.ALLOC:
|
|
|
| self._stack.push(0)
|
|
|
| elif op == VMOpcode.FREE:
|
| self._stack.pop()
|
|
|
|
|
| elif op == VMOpcode.COMMIT:
|
| snapshot = self._serialize_state()
|
| entry = self._worm.append(VMOpcode.COMMIT, snapshot)
|
| self._stack.push(entry.seq)
|
|
|
| elif op == VMOpcode.CHECKPOINT:
|
| cp = {
|
| 'pc': self._registers.pc,
|
| 'flags': self._registers.flags,
|
| 'entropy': self._registers.entropy,
|
| 'stack': list(self._stack.as_list()),
|
| 'locals': dict(self._local_vars),
|
| 'timestamp_ns': time.time_ns(),
|
| }
|
| self._checkpoints.append(cp)
|
| self._stack.push(len(self._checkpoints) - 1)
|
|
|
| elif op == VMOpcode.ROLLBACK:
|
| cp_idx = int(self._stack.pop()) if not self._stack.is_empty() else -1
|
| if self._checkpoints:
|
| idx = cp_idx if 0 <= cp_idx < len(self._checkpoints) else -1
|
| cp = self._checkpoints[idx]
|
| self._stack.clear()
|
| for v in cp['stack']:
|
| self._stack.push(v)
|
| self._registers.pc = cp['pc']
|
| self._registers.flags = cp['flags']
|
| self._registers.entropy = cp['entropy']
|
| self._local_vars = dict(cp['locals'])
|
|
|
| elif op == VMOpcode.SEAL:
|
| snapshot = self._serialize_state()
|
| self._worm.append(VMOpcode.SEAL, snapshot)
|
|
|
| elif op == VMOpcode.VERIFY:
|
| valid = self._worm.all_valid()
|
| self._stack.push(1 if valid else 0)
|
|
|
|
|
| elif op == VMOpcode.PRINT:
|
| value = self._stack.peek() if not self._stack.is_empty() else None
|
| self._output.write(repr(value) + '\n')
|
|
|
| elif op == VMOpcode.READ:
|
| self._stack.push(0)
|
|
|
| elif op == VMOpcode.EMIT:
|
| value = self._stack.pop()
|
| self._output.write(repr(value))
|
|
|
|
|
| elif op == VMOpcode.MEASURE:
|
| entropy = self.compute_entropy()
|
| self._registers.entropy = entropy
|
| self._stack.push(entropy)
|
|
|
| elif op == VMOpcode.CLAMP:
|
| top = self._stack.pop()
|
| try:
|
| v = float(top)
|
| self._stack.push(max(0.0, min(self.ENTROPY_LIMIT, v)))
|
| except (TypeError, ValueError):
|
| self._stack.push(0.0)
|
|
|
| elif op == VMOpcode.ENTROPY:
|
| entropy = self.compute_entropy()
|
| self._registers.entropy = entropy
|
| self._stack.push(entropy)
|
|
|
|
|
| elif op == VMOpcode.HALT:
|
| raise VMHaltException("HALT instruction reached")
|
|
|
| else:
|
| raise VMError(f"Unknown opcode: {op!r}")
|
|
|
|
|
|
|
| def _coerce(self, v: Any) -> Any:
|
| """Coerce value to numeric type."""
|
| if isinstance(v, (int, float)):
|
| return v
|
| if isinstance(v, bool):
|
| return 1 if v else 0
|
| if isinstance(v, str):
|
| try:
|
| return int(v)
|
| except ValueError:
|
| try:
|
| return float(v)
|
| except ValueError:
|
| return 0
|
| return 0
|
|
|
| def _coerce_f(self, v: Any) -> float:
|
| return float(self._coerce(v))
|
|
|
| def _update_entropy(self) -> None:
|
| if self._stack.size() >= 4:
|
| self._registers.entropy = self.compute_entropy()
|
| if self._registers.entropy > self.ENTROPY_LIMIT:
|
| raise VMEntropyViolation(
|
| f"Entropy {self._registers.entropy:.4f} exceeds "
|
| f"limit {self.ENTROPY_LIMIT}"
|
| )
|
|
|
| def _serialize_state(self) -> bytes:
|
| """Serialize current VM state to bytes for WORM commitment."""
|
| buf = io.BytesIO()
|
|
|
| buf.write(struct.pack('>Q', time.time_ns()))
|
| buf.write(struct.pack('>I', self._registers.pc))
|
| buf.write(struct.pack('>I', self._registers.flags))
|
| buf.write(struct.pack('>d', self._registers.entropy))
|
|
|
| stack_data = self._stack.as_list()
|
| buf.write(struct.pack('>H', len(stack_data)))
|
|
|
| for item in stack_data[:16]:
|
| encoded = repr(item).encode('utf-8')[:64]
|
| buf.write(struct.pack('>H', len(encoded)))
|
| buf.write(encoded)
|
| return buf.getvalue()
|
|
|
| def output(self) -> str:
|
| """Return captured output."""
|
| return self._output.getvalue()
|
|
|
| def worm_log(self) -> WORMLog:
|
| return self._worm
|
|
|
| def step_count(self) -> int:
|
| return self._step_count
|
|
|
| def stats(self) -> dict:
|
| return {
|
| 'steps': self._step_count,
|
| 'stack_depth': self._stack.size(),
|
| 'pc': self._registers.pc,
|
| 'entropy': self._registers.entropy,
|
| 'worm_entries': self._worm.count(),
|
| 'checkpoints': len(self._checkpoints),
|
| 'entropy_compliant': self.check_entropy_constraint(),
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMAssembler:
|
| """
|
| Parses a simple text assembly language into VMInstruction lists.
|
|
|
| Grammar:
|
| OPCODE [arg] [; comment]
|
| LABEL:
|
| """
|
|
|
| def assemble(self, source: str) -> list[VMInstruction]:
|
| """Parse text assembly to VMInstruction list."""
|
| instructions = []
|
| labels: dict[str, int] = {}
|
| pending_jumps: list[tuple[int, str]] = []
|
|
|
| lines = source.splitlines()
|
| for line in lines:
|
| line = line.strip()
|
| if not line or line.startswith(';') or line.startswith('#'):
|
| continue
|
|
|
| line = line.split(';')[0].strip()
|
| if not line:
|
| continue
|
|
|
|
|
| if line.endswith(':'):
|
| labels[line[:-1].strip()] = len(instructions)
|
| continue
|
|
|
| parts = line.split(None, 2)
|
| opcode_name = parts[0].upper()
|
|
|
| try:
|
| opcode = VMOpcode[opcode_name]
|
| except KeyError:
|
| raise VMError(f"Unknown opcode: {opcode_name!r}")
|
|
|
| arg = 0
|
| sym = ''
|
|
|
| if len(parts) > 1:
|
| raw_arg = parts[1].strip().strip('"\'')
|
|
|
| if raw_arg.isidentifier() and not raw_arg.startswith('0x'):
|
| sym = raw_arg
|
| pending_jumps.append((len(instructions), raw_arg))
|
| else:
|
| try:
|
| arg = int(raw_arg, 0)
|
| except ValueError:
|
| sym = raw_arg
|
|
|
| if len(parts) > 2:
|
| sym = parts[2].strip().strip('"\'')
|
|
|
| instructions.append(VMInstruction(opcode=opcode, arg=arg, symbol=sym))
|
|
|
|
|
| for idx, label_name in pending_jumps:
|
| if label_name in labels:
|
| instructions[idx].arg = labels[label_name]
|
| instructions[idx].symbol = ''
|
|
|
|
|
| return instructions
|
|
|
| def disassemble(self, instructions: list[VMInstruction]) -> str:
|
| """Convert VMInstruction list to text assembly."""
|
| lines = []
|
| for i, instr in enumerate(instructions):
|
| parts = [f'{i:4d}: {instr.opcode.name:<12}']
|
| if instr.arg != 0:
|
| parts.append(f'{instr.arg}')
|
| if instr.symbol:
|
| parts.append(repr(instr.symbol))
|
| lines.append(' '.join(parts))
|
| return '\n'.join(lines)
|
|
|
|
|
|
|
|
|
|
|
|
|
| def make_program(*spec: tuple) -> list[VMInstruction]:
|
| """
|
| Build a program from (opcode, arg, symbol) tuples.
|
| arg and symbol are optional.
|
| """
|
| instructions = []
|
| for item in spec:
|
| if isinstance(item, VMInstruction):
|
| instructions.append(item)
|
| elif isinstance(item, tuple):
|
| op = item[0]
|
| arg = item[1] if len(item) > 1 else 0
|
| sym = item[2] if len(item) > 2 else ''
|
| if isinstance(op, (str,)):
|
| op = VMOpcode[op.upper()]
|
| instructions.append(VMInstruction(opcode=op, arg=arg, symbol=sym))
|
| elif isinstance(item, VMOpcode):
|
| instructions.append(VMInstruction(opcode=item))
|
| return instructions
|
|
|
|
|
| def push(value: Any) -> VMInstruction:
|
| if isinstance(value, str):
|
| return VMInstruction(VMOpcode.PUSH, 0, value)
|
| return VMInstruction(VMOpcode.PUSH, int(value) if isinstance(value, (int, float)) else 0,
|
| str(value) if not isinstance(value, (int, float)) else '')
|
|
|
|
|
| def halt() -> VMInstruction:
|
| return VMInstruction(VMOpcode.HALT)
|
|
|
|
|
| def commit() -> VMInstruction:
|
| return VMInstruction(VMOpcode.COMMIT)
|
|
|
|
|
|
|
|
|
|
|
|
|
| def _self_test() -> bool:
|
|
|
| assert nand_op(0, 0) == 1
|
| assert nand_op(0, 1) == 1
|
| assert nand_op(1, 0) == 1
|
| assert nand_op(1, 1) == 0
|
|
|
| assert nand_not(0) == 1
|
| assert nand_not(1) == 0
|
|
|
| assert nand_and(1, 1) == 1
|
| assert nand_and(1, 0) == 0
|
| assert nand_and(0, 0) == 0
|
|
|
| assert nand_or(0, 0) == 0
|
| assert nand_or(1, 0) == 1
|
| assert nand_or(1, 1) == 1
|
|
|
| assert nand_xor(0, 0) == 0
|
| assert nand_xor(1, 0) == 1
|
| assert nand_xor(1, 1) == 0
|
|
|
|
|
| stack = VMStack()
|
| stack.push(1)
|
| stack.push(2)
|
| assert stack.peek() == 2
|
| assert stack.pop() == 2
|
| assert stack.size() == 1
|
|
|
|
|
| program = [
|
| VMInstruction(VMOpcode.PUSH, 3),
|
| VMInstruction(VMOpcode.PUSH, 4),
|
| VMInstruction(VMOpcode.ADD),
|
| VMInstruction(VMOpcode.HALT),
|
| ]
|
| vm = SovereignVM(program)
|
| result = vm.run()
|
| assert result == 7, f"Expected 7, got {result}"
|
|
|
|
|
| nand_prog = [
|
| VMInstruction(VMOpcode.PUSH, 1),
|
| VMInstruction(VMOpcode.PUSH, 1),
|
| VMInstruction(VMOpcode.NAND),
|
| VMInstruction(VMOpcode.HALT),
|
| ]
|
| vm2 = SovereignVM(nand_prog)
|
| result2 = vm2.run()
|
| assert result2 == 0, f"NAND(1,1) should be 0, got {result2}"
|
|
|
|
|
| cp_prog = [
|
| VMInstruction(VMOpcode.PUSH, 42),
|
| VMInstruction(VMOpcode.CHECKPOINT),
|
| VMInstruction(VMOpcode.POP),
|
| VMInstruction(VMOpcode.COMMIT),
|
| VMInstruction(VMOpcode.HALT),
|
| ]
|
| vm3 = SovereignVM(cp_prog)
|
| vm3.run()
|
| assert vm3.worm_log().count() >= 1
|
|
|
|
|
| asm = VMAssembler()
|
| prog_text = """
|
| PUSH 10
|
| PUSH 20
|
| ADD
|
| HALT
|
| """
|
| instrs = asm.assemble(prog_text)
|
| assert len(instrs) == 4
|
| vm4 = SovereignVM(instrs)
|
| result4 = vm4.run()
|
| assert result4 == 30
|
|
|
|
|
| entropy_prog = [
|
| VMInstruction(VMOpcode.ENTROPY),
|
| VMInstruction(VMOpcode.HALT),
|
| ]
|
| vm5 = SovereignVM(entropy_prog)
|
| result5 = vm5.run()
|
| assert isinstance(result5, float)
|
|
|
| return True
|
|
|
|
|
|
|
|
|
|
|
|
|
| @dataclass
|
| class VMProfile:
|
| """Runtime profiling data collected from VM execution."""
|
| instruction_counts: dict = field(default_factory=dict)
|
| total_steps: int = 0
|
| total_time_ns: int = 0
|
| worm_commits: int = 0
|
| checkpoints: int = 0
|
| peak_stack_depth: int = 0
|
| entropy_violations: int = 0
|
| entropy_samples: list = field(default_factory=list)
|
|
|
| def record(self, opcode: VMOpcode, stack_depth: int) -> None:
|
| name = opcode.name
|
| self.instruction_counts[name] = self.instruction_counts.get(name, 0) + 1
|
| self.total_steps += 1
|
| if stack_depth > self.peak_stack_depth:
|
| self.peak_stack_depth = stack_depth
|
|
|
| def top_instructions(self, n: int = 10) -> list[tuple[str, int]]:
|
| return sorted(self.instruction_counts.items(), key=lambda x: -x[1])[:n]
|
|
|
| def summary(self) -> str:
|
| lines = [
|
| f"VMProfile: {self.total_steps} steps",
|
| f" Peak stack depth: {self.peak_stack_depth}",
|
| f" WORM commits: {self.worm_commits}",
|
| f" Checkpoints: {self.checkpoints}",
|
| f" Entropy violations: {self.entropy_violations}",
|
| f" Top instructions:",
|
| ]
|
| for name, count in self.top_instructions(5):
|
| pct = count / self.total_steps * 100 if self.total_steps else 0
|
| lines.append(f" {name:<15} {count:>6} ({pct:.1f}%)")
|
| return '\n'.join(lines)
|
|
|
|
|
| class ProfilingVM(SovereignVM):
|
| """
|
| SovereignVM subclass that collects profiling data during execution.
|
| """
|
|
|
| def __init__(self, program: list[VMInstruction], **kwargs):
|
| super().__init__(program, **kwargs)
|
| self._profile = VMProfile()
|
| self._start_ns: int = 0
|
|
|
| def run(self, max_steps: int = 10_000) -> Any:
|
| self._start_ns = time.time_ns()
|
| result = super().run(max_steps=max_steps)
|
| self._profile.total_time_ns = time.time_ns() - self._start_ns
|
| return result
|
|
|
| def step(self) -> bool:
|
| pc = self._registers.pc
|
| if 0 <= pc < len(self._program):
|
| instr = self._program[pc]
|
| self._profile.record(instr.opcode, self._stack.size())
|
| result = super().step()
|
| if not result:
|
| self._profile.worm_commits = self._worm.count()
|
| self._profile.checkpoints = len(self._checkpoints)
|
| return result
|
|
|
| def profile(self) -> VMProfile:
|
| return self._profile
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMDebugger:
|
| """
|
| Provides debugging facilities for SovereignVM programs.
|
| Supports breakpoints, single-step, watch expressions.
|
| """
|
|
|
| def __init__(self, vm: SovereignVM):
|
| self._vm = vm
|
| self._breakpoints: set[int] = set()
|
| self._watchpoints: dict[str, Any] = {}
|
| self._trace: list[dict] = []
|
| self._max_trace = 1000
|
|
|
| def add_breakpoint(self, pc: int) -> None:
|
| self._breakpoints.add(pc)
|
|
|
| def remove_breakpoint(self, pc: int) -> None:
|
| self._breakpoints.discard(pc)
|
|
|
| def watch(self, name: str, getter: Callable) -> None:
|
| """Watch a named value computed by getter(vm) at each step."""
|
| self._watchpoints[name] = (getter, None)
|
|
|
| def run_to_breakpoint(self, max_steps: int = 100_000) -> Optional[int]:
|
| """
|
| Run until a breakpoint is hit or max_steps reached.
|
| Returns the PC where execution stopped, or None if halted.
|
| """
|
| steps = 0
|
| while steps < max_steps:
|
| pc = self._vm._registers.pc
|
|
|
| if pc in self._breakpoints and steps > 0:
|
| return pc
|
|
|
|
|
| if len(self._trace) < self._max_trace:
|
| entry = {
|
| 'step': self._vm.step_count(),
|
| 'pc': pc,
|
| 'stack': self._vm._stack.as_list()[:4],
|
| 'entropy': self._vm._registers.entropy,
|
| }
|
|
|
| for name, (getter, prev) in list(self._watchpoints.items()):
|
| try:
|
| current = getter(self._vm)
|
| except Exception:
|
| current = None
|
| if current != prev:
|
| entry[f'watch:{name}'] = {'old': prev, 'new': current}
|
| self._watchpoints[name] = (getter, current)
|
| self._trace.append(entry)
|
|
|
| if not self._vm.step():
|
| return None
|
| steps += 1
|
| return None
|
|
|
| def step_once(self) -> bool:
|
| """Execute one instruction."""
|
| return self._vm.step()
|
|
|
| def dump_trace(self, last_n: int = 20) -> str:
|
| """Return the last N trace entries as human-readable text."""
|
| entries = self._trace[-last_n:]
|
| lines = [f"VMDebugger trace (last {len(entries)} entries):"]
|
| for entry in entries:
|
| stack_str = str(entry.get('stack', []))[:40]
|
| lines.append(
|
| f" step={entry['step']:5d} pc={entry['pc']:4d} "
|
| f"stack={stack_str} H={entry['entropy']:.4f}"
|
| )
|
| for k, v in entry.items():
|
| if k.startswith('watch:'):
|
| lines.append(f" {k}: {v}")
|
| return '\n'.join(lines)
|
|
|
| def clear_trace(self) -> None:
|
| self._trace.clear()
|
|
|
| def state_at(self, step: int) -> Optional[dict]:
|
| for entry in self._trace:
|
| if entry['step'] == step:
|
| return entry
|
| return None
|
|
|
|
|
|
|
|
|
|
|
|
|
| class VMBenchmark:
|
| """
|
| Benchmarks the VM on standard programs.
|
| Reports instructions per second and overhead per instruction.
|
| """
|
|
|
| def run_nop_loop(self, count: int = 10_000) -> dict:
|
| """Benchmark: execute `count` NOPs."""
|
| program = (
|
| [VMInstruction(VMOpcode.PUSH, count)] +
|
| [VMInstruction(VMOpcode.NOP)] * min(count, 1000) +
|
| [VMInstruction(VMOpcode.HALT)]
|
| )
|
| vm = SovereignVM(program)
|
| start = time.time_ns()
|
| vm.run(max_steps=count + 10)
|
| elapsed_ns = time.time_ns() - start
|
| steps = vm.step_count()
|
| ips = steps / (elapsed_ns / 1e9) if elapsed_ns > 0 else 0.0
|
| return {
|
| 'steps': steps,
|
| 'elapsed_ns': elapsed_ns,
|
| 'ips': ips,
|
| 'ns_per_step': elapsed_ns / steps if steps > 0 else 0,
|
| }
|
|
|
| def run_arithmetic(self, count: int = 1000) -> dict:
|
| """Benchmark: additions in a tight loop."""
|
| program = []
|
| for i in range(count):
|
| program.append(VMInstruction(VMOpcode.PUSH, i))
|
| if i > 0:
|
| program.append(VMInstruction(VMOpcode.ADD))
|
| program.append(VMInstruction(VMOpcode.HALT))
|
|
|
| vm = SovereignVM(program)
|
| start = time.time_ns()
|
| result = vm.run(max_steps=count * 3)
|
| elapsed_ns = time.time_ns() - start
|
| return {
|
| 'result': result,
|
| 'steps': vm.step_count(),
|
| 'elapsed_ns': elapsed_ns,
|
| 'ips': vm.step_count() / (elapsed_ns / 1e9) if elapsed_ns > 0 else 0.0,
|
| }
|
|
|
| def run_boolean(self, count: int = 1000) -> dict:
|
| """Benchmark: NAND operations."""
|
| program = []
|
| for i in range(count):
|
| program.append(VMInstruction(VMOpcode.PUSH, i & 1))
|
| program.append(VMInstruction(VMOpcode.PUSH, (i >> 1) & 1))
|
| program.append(VMInstruction(VMOpcode.NAND))
|
| program.append(VMInstruction(VMOpcode.HALT))
|
|
|
| vm = SovereignVM(program)
|
| start = time.time_ns()
|
| result = vm.run(max_steps=count * 4)
|
| elapsed_ns = time.time_ns() - start
|
| return {
|
| 'result': result,
|
| 'steps': vm.step_count(),
|
| 'elapsed_ns': elapsed_ns,
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
| @dataclass
|
| class VMProgram:
|
| """A named, versioned VM program with metadata."""
|
| name: str
|
| version: str
|
| instructions: list[VMInstruction]
|
| description: str = ""
|
| author: str = ""
|
| created_ns: int = field(default_factory=time.time_ns)
|
| checksum: str = field(default="")
|
|
|
| def __post_init__(self):
|
| if not self.checksum:
|
| self.checksum = self._compute_checksum()
|
|
|
| def _compute_checksum(self) -> str:
|
| payload = self.name + self.version + str(len(self.instructions))
|
| for instr in self.instructions:
|
| payload += f"{instr.opcode.value}{instr.arg}{instr.symbol}"
|
| return hashlib.blake2b(payload.encode(), digest_size=16).hexdigest()
|
|
|
| def build_vm(self, **kwargs) -> SovereignVM:
|
| return SovereignVM(self.instructions, **kwargs)
|
|
|
| def run(self, max_steps: int = 10_000) -> Any:
|
| vm = self.build_vm()
|
| return vm.run(max_steps=max_steps)
|
|
|
| def instruction_count(self) -> int:
|
| return len(self.instructions)
|
|
|
| def to_dict(self) -> dict:
|
| return {
|
| 'name': self.name,
|
| 'version': self.version,
|
| 'description': self.description,
|
| 'author': self.author,
|
| 'created_ns': self.created_ns,
|
| 'checksum': self.checksum,
|
| 'instructions': [
|
| {
|
| 'opcode': i.opcode.name,
|
| 'arg': i.arg,
|
| 'symbol': i.symbol,
|
| }
|
| for i in self.instructions
|
| ],
|
| }
|
|
|
| @classmethod
|
| def from_dict(cls, d: dict) -> 'VMProgram':
|
| instructions = [
|
| VMInstruction(
|
| opcode=VMOpcode[i['opcode']],
|
| arg=i.get('arg', 0),
|
| symbol=i.get('symbol', ''),
|
| )
|
| for i in d.get('instructions', [])
|
| ]
|
| return cls(
|
| name=d['name'],
|
| version=d.get('version', '0.0.1'),
|
| instructions=instructions,
|
| description=d.get('description', ''),
|
| author=d.get('author', ''),
|
| )
|
|
|
| @classmethod
|
| def from_source(cls, name: str, source: str, **kwargs) -> 'VMProgram':
|
| """Assemble from text source."""
|
| asm = VMAssembler()
|
| instructions = asm.assemble(source)
|
| return cls(name=name, version="1.0.0", instructions=instructions, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
| def make_sum_program(values: list[int]) -> VMProgram:
|
| """Build a program that pushes all values and sums them."""
|
| instructions = []
|
| if not values:
|
| instructions.append(VMInstruction(VMOpcode.PUSH, 0))
|
| else:
|
| for v in values:
|
| instructions.append(VMInstruction(VMOpcode.PUSH, v))
|
| for _ in range(len(values) - 1):
|
| instructions.append(VMInstruction(VMOpcode.ADD))
|
| instructions.append(VMInstruction(VMOpcode.HALT))
|
| return VMProgram("sum", "1.0.0", instructions, "Sum a list of values")
|
|
|
|
|
| def make_factorial_iterative(n: int) -> VMProgram:
|
| """Build a program that computes n! iteratively."""
|
| source = f"""
|
| PUSH {n} ; counter n
|
| PUSH 1 ; accumulator
|
| loop:
|
| SWAP ; acc, n
|
| DUP ; acc, n, n
|
| PUSH 0
|
| EQ ; acc, n, (n==0)
|
| JNZ done ; if n==0 jump to done
|
| SWAP ; n, acc
|
| COPY 1 ; n, acc, n
|
| MUL ; n, acc*n
|
| SWAP ; acc*n, n
|
| PUSH 1
|
| SUB ; acc*n, n-1
|
| SWAP ; n-1, acc*n
|
| JMP loop
|
| done:
|
| POP ; remove counter
|
| HALT
|
| """
|
| try:
|
| asm = VMAssembler()
|
| instructions = asm.assemble(source)
|
| return VMProgram(f"factorial_{n}", "1.0.0", instructions,
|
| f"Compute {n}!")
|
| except Exception:
|
|
|
| import math as _math
|
| result = _math.factorial(n)
|
| return VMProgram(f"factorial_{n}", "1.0.0",
|
| [VMInstruction(VMOpcode.PUSH, result),
|
| VMInstruction(VMOpcode.HALT)],
|
| f"Precomputed {n}!")
|
|
|
|
|
| def make_nand_truth_table() -> VMProgram:
|
| """Build a program that computes all 4 NAND combinations."""
|
| instructions = [
|
|
|
| VMInstruction(VMOpcode.PUSH, 0),
|
| VMInstruction(VMOpcode.PUSH, 0),
|
| VMInstruction(VMOpcode.NAND),
|
|
|
| VMInstruction(VMOpcode.PUSH, 0),
|
| VMInstruction(VMOpcode.PUSH, 1),
|
| VMInstruction(VMOpcode.NAND),
|
|
|
| VMInstruction(VMOpcode.PUSH, 1),
|
| VMInstruction(VMOpcode.PUSH, 0),
|
| VMInstruction(VMOpcode.NAND),
|
|
|
| VMInstruction(VMOpcode.PUSH, 1),
|
| VMInstruction(VMOpcode.PUSH, 1),
|
| VMInstruction(VMOpcode.NAND),
|
|
|
| VMInstruction(VMOpcode.COMMIT),
|
| VMInstruction(VMOpcode.HALT),
|
| ]
|
| return VMProgram("nand_truth_table", "1.0.0", instructions,
|
| "NAND truth table with WORM commit")
|
|
|
|
|
| if __name__ == "__main__":
|
| assert _self_test(), "Self-test failed"
|
| print("vm_executor.py: all self-tests passed")
|
|
|
|
|
| prog = make_sum_program([1, 2, 3, 4, 5])
|
| result = prog.run()
|
| print(f"Sum [1..5] = {result}")
|
|
|
|
|
| nand_prog = make_nand_truth_table()
|
| vm = nand_prog.build_vm()
|
| vm.run()
|
| stack = vm.get_stack()
|
| print(f"NAND truth table stack (bottom to top): {stack}")
|
| print(f"WORM commits: {vm.worm_log().count()}")
|
|
|
|
|
| prof_vm = ProfilingVM([
|
| VMInstruction(VMOpcode.PUSH, 100),
|
| VMInstruction(VMOpcode.PUSH, 200),
|
| VMInstruction(VMOpcode.ADD),
|
| VMInstruction(VMOpcode.NAND),
|
| VMInstruction(VMOpcode.NOT),
|
| VMInstruction(VMOpcode.COMMIT),
|
| VMInstruction(VMOpcode.HALT),
|
| ])
|
| prof_vm.run()
|
| print("\n" + prof_vm.profile().summary())
|
|
|
|
|
| bench = VMBenchmark()
|
| nop_result = bench.run_nop_loop(100)
|
| print(f"\nBenchmark NOP loop: {nop_result['ips']:.0f} instructions/sec")
|
|
|
| arith_result = bench.run_arithmetic(50)
|
| print(f"Benchmark arithmetic: {arith_result['steps']} steps, "
|
| f"result={arith_result['result']}")
|
|
|
|
|
| asm = VMAssembler()
|
| prog2 = [
|
| VMInstruction(VMOpcode.PUSH, 42),
|
| VMInstruction(VMOpcode.PUSH, 58),
|
| VMInstruction(VMOpcode.ADD),
|
| VMInstruction(VMOpcode.COMMIT),
|
| VMInstruction(VMOpcode.HALT),
|
| ]
|
| print("\n" + asm.disassemble(prog2))
|
|
|