This repository has been archived on 2026-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
el-retired/crates/el-compiler/src/bytecode.rs
T
Will Anderson 977a2cd654 Add struct literals, generics, log/print stdlib registration, activate DB wiring
- Register print/println/log/print_err in TypeEnv::with_builtins() with polymorphic (Unknown) param type so any value type is accepted without spurious warnings
- Add StructLit expr to AST + parser (uppercase-IDENT { ... } syntax), BuildStruct bytecode instruction + Struct value to runtime
- Add type_params to FnDef AST node and TypeParam variant to TypeExpr; parser parses <T, E> generics; type checker treats TypeParam as Unknown (universal type)
- Rewrite interpreter to support user-defined function calls via call stack (Frame + return_ip); dispatch_builtin handles print/println/log/print_err/__build_list__
- Fix engram_activate_search to unwrap { results: [...] } response envelope from /search; use std::net for sync HTTP to avoid reqwest dependency
- Add run-file command to CLI for single-file execution without el.toml
- Fix worktree engram-crypto path dep
2026-04-28 11:36:25 -05:00

174 lines
7.6 KiB
Rust

//! Bytecode instruction set for the Engram virtual machine.
//!
//! The VM is a simple stack machine. Every instruction pops its operands
//! from the stack and pushes its result. Control flow uses relative signed
//! offsets from the instruction *after* the jump.
use serde::{Deserialize, Serialize};
/// A runtime value on the VM stack.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
Int(i64),
Float(f64),
Str(String),
Bool(bool),
Nil,
/// A list of values (used for `activate` results and array literals).
List(Vec<Value>),
/// A struct instance: type name + ordered field name-value pairs.
Struct { type_name: String, fields: Vec<(String, Value)> },
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Int(n) => write!(f, "{n}"),
Value::Float(n) => write!(f, "{n}"),
Value::Str(s) => write!(f, "{s}"),
Value::Bool(b) => write!(f, "{b}"),
Value::Nil => write!(f, "nil"),
Value::List(vs) => {
let items: Vec<_> = vs.iter().map(|v| v.to_string()).collect();
write!(f, "[{}]", items.join(", "))
}
Value::Struct { type_name, fields } => {
let fs: Vec<_> = fields.iter().map(|(k, v)| format!("{k}: {v}")).collect();
write!(f, "{type_name} {{ {} }}", fs.join(", "))
}
}
}
}
/// A single VM instruction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Bytecode {
// ── Stack ─────────────────────────────────────────────────────────────────
/// Push a constant value onto the stack.
Push(Value),
/// Discard the top of stack.
Pop,
/// Duplicate the top of stack.
Dup,
// ── Arithmetic ────────────────────────────────────────────────────────────
Add,
Sub,
Mul,
Div,
// ── Comparison ────────────────────────────────────────────────────────────
Eq,
NotEq,
Lt,
Gt,
LtEq,
GtEq,
// ── Logical ───────────────────────────────────────────────────────────────
And,
Or,
Not,
// ── Locals ───────────────────────────────────────────────────────────────
/// Load a local variable by name.
LoadLocal(String),
/// Store the top of stack into a local variable.
StoreLocal(String),
// ── Functions ─────────────────────────────────────────────────────────────
/// Call a function by name with `arity` arguments.
Call { name: String, arity: u32 },
/// Return from the current function (leaves return value on stack).
Return,
// ── Control flow ──────────────────────────────────────────────────────────
/// Unconditional jump: `ip += offset` (offset is from the *next* instruction).
Jump(i32),
/// Jump if the top of stack is truthy; pops the value.
JumpIf(i32),
/// Jump if the top of stack is falsy; pops the value.
JumpIfNot(i32),
// ── Fields & Indexing ─────────────────────────────────────────────────────
/// Load a named field from the struct on top of stack.
GetField(String),
/// Index into an array: pops index then array.
GetIndex,
// ── Special ───────────────────────────────────────────────────────────────
/// `activate TypeName "query"` — emit a semantic query stub.
/// In a full implementation this would call into the Engram runtime.
Activate { type_name: String, query: String },
/// Mark the start of a sealed section (the runtime enforces protection).
SealedBegin,
/// Mark the end of a sealed section.
SealedEnd,
/// Build a struct value: pops `fields.len()` values from the stack (in
/// order), combines them with the field names, and pushes a `Struct` value.
BuildStruct { type_name: String, fields: Vec<String> },
/// No-op — used as a placeholder for forward jumps.
Nop,
/// Halt the VM.
Halt,
}
impl std::fmt::Display for Bytecode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Bytecode::Push(v) => write!(f, "PUSH {v}"),
Bytecode::Pop => write!(f, "POP"),
Bytecode::Dup => write!(f, "DUP"),
Bytecode::Add => write!(f, "ADD"),
Bytecode::Sub => write!(f, "SUB"),
Bytecode::Mul => write!(f, "MUL"),
Bytecode::Div => write!(f, "DIV"),
Bytecode::Eq => write!(f, "EQ"),
Bytecode::NotEq => write!(f, "NEQ"),
Bytecode::Lt => write!(f, "LT"),
Bytecode::Gt => write!(f, "GT"),
Bytecode::LtEq => write!(f, "LTE"),
Bytecode::GtEq => write!(f, "GTE"),
Bytecode::And => write!(f, "AND"),
Bytecode::Or => write!(f, "OR"),
Bytecode::Not => write!(f, "NOT"),
Bytecode::LoadLocal(n) => write!(f, "LOAD {n}"),
Bytecode::StoreLocal(n) => write!(f, "STORE {n}"),
Bytecode::Call { name, arity } => write!(f, "CALL {name}/{arity}"),
Bytecode::Return => write!(f, "RETURN"),
Bytecode::Jump(off) => write!(f, "JUMP {off:+}"),
Bytecode::JumpIf(off) => write!(f, "JUMPIF {off:+}"),
Bytecode::JumpIfNot(off) => write!(f, "JUMPIFNOT {off:+}"),
Bytecode::GetField(n) => write!(f, "GETFIELD {n}"),
Bytecode::GetIndex => write!(f, "GETINDEX"),
Bytecode::Activate { type_name, query } => {
write!(f, "ACTIVATE {type_name} \"{query}\"")
}
Bytecode::SealedBegin => write!(f, "SEALED_BEGIN"),
Bytecode::SealedEnd => write!(f, "SEALED_END"),
Bytecode::BuildStruct { type_name, fields } => {
write!(f, "BUILD_STRUCT {type_name}({})", fields.join(", "))
}
Bytecode::Nop => write!(f, "NOP"),
Bytecode::Halt => write!(f, "HALT"),
}
}
}
/// Serialize bytecode instructions to bytes for storage/sealing.
pub fn serialize_bytecode(instructions: &[Bytecode]) -> Result<Vec<u8>, String> {
serde_json::to_vec(instructions).map_err(|e| e.to_string())
}
/// Deserialize bytecode instructions from bytes.
pub fn deserialize_bytecode(bytes: &[u8]) -> Result<Vec<Bytecode>, String> {
serde_json::from_slice(bytes).map_err(|e| e.to_string())
}
impl Bytecode {
/// Deserialize a bytecode slice from JSON bytes (convenience wrapper).
pub fn deserialize_all(bytes: &[u8]) -> Result<Vec<Bytecode>, String> {
deserialize_bytecode(bytes)
}
}