Skip to main content

ternlang_core/vm/
mod.rs

1pub mod bet;
2
3use crate::trit::Trit;
4use crate::vm::bet::{unpack_trits, BetFault};
5
6use std::fmt;
7use std::sync::Arc;
8
9// ─── Remote transport trait ───────────────────────────────────────────────────
10
11pub trait RemoteTransport: Send + Sync {
12    fn remote_send(&self, node_addr: &str, agent_id: usize, trit: i8) -> std::io::Result<()>;
13    fn remote_await(&self, node_addr: &str, agent_id: usize) -> std::io::Result<i8>;
14}
15
16/// Maximum call depth before the VM returns a `CallStackOverflow` error.
17/// Prevents OOM/freeze when programs contain unbounded recursion or
18/// mutual recursion across imported modules.
19const MAX_CALL_DEPTH: usize = 4096;
20
21#[derive(Debug, PartialEq, Eq)]
22pub enum VmError {
23    StackUnderflow,
24    BetFault(BetFault),
25    Halt,
26    InvalidOpcode(u8),
27    InvalidRegister(u8),
28    PcOutOfBounds(usize),
29    TypeMismatch { expected: String, found: String },
30    // ── Tensor errors ────────────────────────────────────────────────────────
31    TensorIndexOutOfBounds { tensor_id: usize, index: usize, size: usize },
32    TensorNotAllocated(usize),
33    // ── Agent errors ─────────────────────────────────────────────────────────
34    AgentTypeNotRegistered(u16),
35    AgentIdInvalid(usize),
36    RuntimeError(String),
37    CallStackOverflow,
38    // ── File I/O errors ──────────────────────────────────────────────────────
39    FileOpenError(String),
40    FileReadError(String),
41    FileWriteError(String),
42    FileNotOpen(usize),
43}
44
45impl fmt::Display for VmError {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        match self {
48            VmError::StackUnderflow =>
49                write!(f, "[BET-001] Stack underflow — you tried to pop a truth that wasn't there.\n          → details: stdlib/errors/BET-001.tern  |  ternlang errors BET-001"),
50            VmError::BetFault(fault) =>
51                write!(f, "[BET-002] BET encoding fault: {fault:?}. The 0b00 state is forbidden — only 01/10/11 are valid trit bits.\n          → details: stdlib/errors/BET-002.tern  |  ternlang errors BET-002"),
52            VmError::Halt =>
53                write!(f, "[BET-003] VM halted cleanly. Execution reached the end. This is not an error — this is peace.\n          → details: stdlib/errors/BET-003.tern  |  ternlang errors BET-003"),
54            VmError::InvalidOpcode(op) =>
55                write!(f, "[BET-004] Unknown opcode 0x{op:02x} — the machine has never seen this instruction. Delete cached .ternbc files and recompile.\n          → details: stdlib/errors/BET-004.tern  |  ternlang errors BET-004"),
56            VmError::InvalidRegister(reg) =>
57                write!(f, "[BET-005] Register {reg} is out of range. The BET has exactly 27 registers (0–26). That's 3³. No more.\n          → details: stdlib/errors/BET-005.tern  |  ternlang errors BET-005"),
58            VmError::PcOutOfBounds(pc) =>
59                write!(f, "[BET-006] PC {pc} is out of bounds — you jumped outside the known universe. Recompile from source.\n          → details: stdlib/errors/BET-006.tern  |  ternlang errors BET-006"),
60            VmError::TypeMismatch { expected, found } =>
61                write!(f, "[BET-007] Runtime type mismatch — expected {expected} but found {found}. Square peg, round hole.\n          → details: stdlib/errors/BET-007.tern  |  ternlang errors BET-007"),
62            VmError::TensorIndexOutOfBounds { tensor_id, index, size } =>
63                write!(f, "[BET-008] Tensor[{tensor_id}]: index {index} is out of bounds — tensor only has {size} element(s). Trittensors don't grow on access.\n          → details: stdlib/errors/BET-008.tern  |  ternlang errors BET-008"),
64            VmError::TensorNotAllocated(idx) =>
65                write!(f, "[BET-009] TensorRef({idx}) doesn't exist — you never allocated it. TALLOC first, then TIDX.\n          → details: stdlib/errors/BET-009.tern  |  ternlang errors BET-009"),
66            VmError::AgentTypeNotRegistered(type_id) =>
67                write!(f, "[BET-010] Agent type_id 0x{type_id:04x} was never registered. You can't spawn what was never declared.\n          → details: stdlib/errors/BET-010.tern  |  ternlang errors BET-010"),
68            VmError::AgentIdInvalid(id) =>
69                write!(f, "[BET-011] Agent #{id} doesn't exist — no agent was spawned at this ID. TSEND and TAWAIT require a live agent.\n          → details: stdlib/errors/BET-011.tern  |  ternlang errors BET-011"),
70            VmError::RuntimeError(msg) =>
71                write!(f, "[BET-012] Runtime error: {msg}"),
72            VmError::CallStackOverflow =>
73                write!(f, "[BET-013] Call stack overflow — max depth ({MAX_CALL_DEPTH}) exceeded. Infinite recursion or unbounded cross-module mutual calls detected.\n          → details: stdlib/errors/BET-013.tern  |  ternlang errors BET-013"),
74            VmError::FileOpenError(e) =>
75                write!(f, "[IO-001] File open error: {e}"),
76            VmError::FileReadError(e) =>
77                write!(f, "[IO-002] File read error: {e}"),
78            VmError::FileWriteError(e) =>
79                write!(f, "[IO-003] File write error: {e}"),
80            VmError::FileNotOpen(id) =>
81                write!(f, "[IO-004] File handle {id} is not open or was closed."),
82        }
83    }
84}
85
86#[derive(Debug, Clone, PartialEq)]
87pub enum Value {
88    Trit(Trit),
89    Int(i64),
90    Float(f64),
91    String(String),
92    TensorRef(usize),
93    AgentRef(usize, Option<String>),
94}
95
96impl Default for Value {
97    fn default() -> Self {
98        Value::Trit(Trit::Tend)
99    }
100}
101
102struct TensorInstance {
103    data: Vec<Trit>,
104    rows: usize,
105    cols: usize,
106}
107
108struct AgentInstance {
109    handler_addr: usize,
110    mailbox: std::collections::VecDeque<Value>,
111}
112
113pub struct BetVm {
114    /// Dynamic register file — grows on demand so programs with > 27 locals work correctly
115    /// instead of silently dropping stores and returning zero on reads.
116    registers: Vec<Value>,
117    register_stack: Vec<Vec<Value>>,
118    carry_reg: Trit,
119    stack: Vec<Value>,
120    call_stack: Vec<usize>,
121    tensors: Vec<TensorInstance>,
122    agents: Vec<AgentInstance>,
123    agent_types: std::collections::HashMap<u16, usize>,
124    pc: usize,
125    code: Vec<u8>,
126    node_id: String,
127    remote: Option<Arc<dyn RemoteTransport>>,
128    open_files: Vec<Option<std::fs::File>>,
129    _instructions_count: u64,
130    pub print_log: Vec<String>,
131}
132
133impl BetVm {
134    pub fn new(code: Vec<u8>) -> Self {
135        Self {
136            registers: vec![Value::default(); 27],
137            register_stack: Vec::new(),
138            carry_reg: Trit::Tend,
139            stack: Vec::new(),
140            call_stack: Vec::new(),
141            tensors: Vec::new(),
142            agents: Vec::new(),
143            agent_types: std::collections::HashMap::new(),
144            pc: 0,
145            code,
146            node_id: "127.0.0.1".into(),
147            remote: None,
148            open_files: Vec::new(),
149            _instructions_count: 0,
150            print_log: Vec::new(),
151        }
152    }
153
154
155    /// Drain all lines printed by `print()`/`println()` during execution.
156    pub fn take_output(&mut self) -> Vec<String> {
157        std::mem::take(&mut self.print_log)
158    }
159
160    pub fn set_node_id(&mut self, node_id: String) {
161        self.node_id = node_id;
162    }
163
164    pub fn set_remote(&mut self, transport: Arc<dyn RemoteTransport>) {
165        self.remote = Some(transport);
166    }
167
168    pub fn register_agent_type(&mut self, type_id: u16, handler_addr: usize) {
169        self.agent_types.insert(type_id, handler_addr);
170    }
171
172    pub fn peek_stack(&self) -> Option<Value> {
173        self.stack.last().cloned()
174    }
175
176    pub fn get_register(&self, reg: u8) -> Value {
177        self.registers.get(reg as usize).cloned().unwrap_or_default()
178    }
179
180    pub fn run(&mut self) -> Result<(), VmError> {
181        loop {
182            if self.pc >= self.code.len() { break; }
183            let opcode = self.code[self.pc];
184            self.pc += 1;
185
186            match opcode {
187                0x01 => { // Tpush
188                    let packed = self.read_u8()?;
189                    let trits = unpack_trits(&[packed], 1).map_err(VmError::BetFault)?;
190                    self.stack.push(Value::Trit(trits[0]));
191                }
192                0x02 => { // Tadd
193                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
194                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
195                    match (a.clone(), b.clone()) {
196                        (Value::Trit(av), Value::Trit(bv)) => {
197                            let (sum, carry) = av + bv;
198                            self.stack.push(Value::Trit(sum));
199                            self.carry_reg = carry;
200                        }
201                        (Value::Int(av), Value::Int(bv)) => self.stack.push(Value::Int(av + bv)),
202                        (Value::Float(av), Value::Float(bv)) => self.stack.push(Value::Float(av + bv)),
203                        (Value::Int(av), Value::Trit(bv)) => self.stack.push(Value::Int(av + bv as i64)),
204                        (Value::Trit(av), Value::Int(bv)) => self.stack.push(Value::Int(av as i64 + bv)),
205                        (Value::Float(av), Value::Trit(bv)) => self.stack.push(Value::Float(av + (bv as i8 as f64))),
206                        (Value::Trit(av), Value::Float(bv)) => self.stack.push(Value::Float((av as i8 as f64) + bv)),
207                        (Value::Float(av), Value::Int(bv)) => self.stack.push(Value::Float(av + (bv as f64))),
208                        (Value::Int(av), Value::Float(bv)) => self.stack.push(Value::Float((av as f64) + bv)),
209                        // PARSER-STR-001: string concatenation via + operator
210                        (Value::String(av), Value::String(bv)) => self.stack.push(Value::String(av + &bv)),
211                        _ => return Err(VmError::TypeMismatch { expected: "Numeric".into(), found: format!("{:?}", (a, b)) }),
212                    }
213                }
214                0x03 => { // Tmul
215                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
216                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
217                    match (a.clone(), b.clone()) {
218                        (Value::Trit(av), Value::Trit(bv)) => self.stack.push(Value::Trit(av * bv)),
219                        (Value::Int(av), Value::Int(bv)) => self.stack.push(Value::Int(av * bv)),
220                        (Value::Float(av), Value::Float(bv)) => self.stack.push(Value::Float(av * bv)),
221                        (Value::Int(av), Value::Trit(bv)) => self.stack.push(Value::Int(av * bv as i64)),
222                        (Value::Trit(av), Value::Int(bv)) => self.stack.push(Value::Int(av as i64 * bv)),
223                        (Value::Float(av), Value::Trit(bv)) => self.stack.push(Value::Float(av * (bv as i8 as f64))),
224                        (Value::Trit(av), Value::Float(bv)) => self.stack.push(Value::Float((av as i8 as f64) * bv)),
225                        (Value::Float(av), Value::Int(bv)) => self.stack.push(Value::Float(av * (bv as f64))),
226                        (Value::Int(av), Value::Float(bv)) => self.stack.push(Value::Float((av as f64) * bv)),
227                        _ => return Err(VmError::TypeMismatch { expected: "Numeric".into(), found: format!("{:?}", (a, b)) }),
228                    }
229                }
230                0x04 => { // Tneg
231                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
232                    match a.clone() {
233                        Value::Trit(av) => self.stack.push(Value::Trit(-av)),
234                        Value::Int(av) => self.stack.push(Value::Int(-av)),
235                        Value::Float(av) => self.stack.push(Value::Float(-av)),
236                        _ => return Err(VmError::TypeMismatch { expected: "Numeric".into(), found: format!("{:?}", a) }),
237                    }
238                }
239                0x05 => { // TjmpPos — jumps if top is +1
240                    let addr = self.read_u16()?;
241                    let val = self.stack.last().ok_or(VmError::StackUnderflow)?;
242                    let is_pos = match val {
243                        Value::Trit(Trit::Affirm) => true,
244                        Value::Int(1) => true,
245                        _ => false,
246                    };
247                    if is_pos { self.pc = addr as usize; }
248                }
249                0x06 => { // TjmpZero — jumps if top is 0
250                    let addr = self.read_u16()?;
251                    let val = self.stack.last().ok_or(VmError::StackUnderflow)?;
252                    let is_zero = match val {
253                        Value::Trit(Trit::Tend) => true,
254                        Value::Int(0) => true,
255                        _ => false,
256                    };
257                    if is_zero { self.pc = addr as usize; }
258                }
259                0x07 => { // TjmpNeg — jumps if top is -1
260                    let addr = self.read_u16()?;
261                    let val = self.stack.last().ok_or(VmError::StackUnderflow)?;
262                    let is_neg = match val {
263                        Value::Trit(Trit::Reject) => true,
264                        Value::Int(-1) => true,
265                        _ => false,
266                    };
267                    if is_neg { self.pc = addr as usize; }
268                }
269                0x08 => { // Tstore
270                    let reg = self.read_u8()? as usize;
271                    let val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
272                    if reg >= self.registers.len() { self.registers.resize(reg + 1, Value::default()); }
273                    self.registers[reg] = val;
274                }
275                0x09 => { // Tload
276                    let reg = self.read_u8()? as usize;
277                    if reg >= self.registers.len() { self.registers.resize(reg + 1, Value::default()); }
278                    self.stack.push(self.registers[reg].clone());
279                }
280                0x0a => { // Tdup
281                    let val = self.stack.last().ok_or(VmError::StackUnderflow)?;
282                    self.stack.push(val.clone());
283                }
284                0x0b => { // Tjmp
285                    let addr = self.read_u16()?;
286                    self.pc = addr as usize;
287                }
288                0x0c => { // Tpop
289                    self.stack.pop().ok_or(VmError::StackUnderflow)?;
290                }
291                0x0e => { // Tcons
292                    let b_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
293                    let a_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
294                    
295                    let a = match a_val {
296                        Value::Trit(t) => t,
297                        Value::Int(v) if v == 1 => Trit::Affirm,
298                        Value::Int(v) if v == 0 => Trit::Tend,
299                        Value::Int(v) if v == -1 => Trit::Reject,
300                        _ => return Err(VmError::TypeMismatch { expected: "Trit or Int(-1..1)".into(), found: format!("{:?}", a_val) }),
301                    };
302                    let b = match b_val {
303                        Value::Trit(t) => t,
304                        Value::Int(v) if v == 1 => Trit::Affirm,
305                        Value::Int(v) if v == 0 => Trit::Tend,
306                        Value::Int(v) if v == -1 => Trit::Reject,
307                        _ => return Err(VmError::TypeMismatch { expected: "Trit or Int(-1..1)".into(), found: format!("{:?}", b_val) }),
308                    };
309
310                    let result = match (a, b) {
311                        (Trit::Affirm, Trit::Affirm) => Trit::Affirm,
312                        (Trit::Reject, Trit::Reject) => Trit::Reject,
313                        (Trit::Tend, x) => x,
314                        (x, Trit::Tend) => x,
315                        _ => Trit::Tend,
316                    };
317                    self.stack.push(Value::Trit(result));
318                }
319                0x0f => { // Talloc
320                    let rows = self.read_u16()? as usize;
321                    let cols = self.read_u16()? as usize;
322                    let size = rows * cols;
323                    let idx = self.tensors.len();
324                    self.tensors.push(TensorInstance {
325                        data: vec![Trit::Tend; size],
326                        rows,
327                        cols,
328                    });
329                    self.stack.push(Value::TensorRef(idx));
330                }
331                0x10 => { // Tcall
332                    if self.call_stack.len() >= MAX_CALL_DEPTH {
333                        return Err(VmError::CallStackOverflow);
334                    }
335                    let addr = self.read_u16()? as usize;
336                    self.register_stack.push(self.registers.clone());
337                    self.call_stack.push(self.pc);
338                    self.pc = addr;
339                }
340                0x11 => { // Tret
341                    if let Some(prev) = self.register_stack.pop() {
342                        self.registers = prev;
343                    }
344                    match self.call_stack.pop() {
345                        Some(ret) => self.pc = ret,
346                        None => return Ok(()),
347                    }
348                }
349                0x14 => { // Tless
350                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
351                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
352                    match (a.clone(), b.clone()) {
353                        (Value::Int(x), Value::Int(y)) => {
354                            let r = if x < y { Trit::Affirm } else if x == y { Trit::Tend } else { Trit::Reject };
355                            self.stack.push(Value::Trit(r));
356                        }
357                        (Value::Float(x), Value::Float(y)) => {
358                            let r = if x < y { Trit::Affirm } else if (x - y).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
359                            self.stack.push(Value::Trit(r));
360                        }
361                        (Value::Int(x), Value::Trit(y)) => {
362                            let bv = y as i64;
363                            let r = if x < bv { Trit::Affirm } else if x == bv { Trit::Tend } else { Trit::Reject };
364                            self.stack.push(Value::Trit(r));
365                        }
366                        (Value::Trit(x), Value::Int(y)) => {
367                            let av = x as i64;
368                            let r = if av < y { Trit::Affirm } else if av == y { Trit::Tend } else { Trit::Reject };
369                            self.stack.push(Value::Trit(r));
370                        }
371                        (Value::Int(av), Value::Float(bv)) => {
372                            let a_val = av as f64;
373                            let r = if a_val < bv { Trit::Affirm } else if (a_val - bv).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
374                            self.stack.push(Value::Trit(r));
375                        }
376                        (Value::Float(av), Value::Int(bv)) => {
377                            let b_val = bv as f64;
378                            let r = if av < b_val { Trit::Affirm } else if (av - b_val).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
379                            self.stack.push(Value::Trit(r));
380                        }
381                        (Value::Trit(av), Value::Float(bv)) => {
382                            let a_val = av as i8 as f64;
383                            let r = if a_val < bv { Trit::Affirm } else if (a_val - bv).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
384                            self.stack.push(Value::Trit(r));
385                        }
386                        (Value::Float(av), Value::Trit(bv)) => {
387                            let b_val = bv as i8 as f64;
388                            let r = if av < b_val { Trit::Affirm } else if (av - b_val).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
389                            self.stack.push(Value::Trit(r));
390                        }
391                        (Value::Trit(x), Value::Trit(y)) => {
392                            let av = x as i64;
393                            let bv = y as i64;
394                            let r = if av < bv { Trit::Affirm } else if av == bv { Trit::Tend } else { Trit::Reject };
395                            self.stack.push(Value::Trit(r));
396                        }
397                        _ => return Err(VmError::TypeMismatch { expected: "Numeric".into(), found: format!("{:?}", (a, b)) }),
398                    }
399                }
400                0x15 => { // Tgreater
401                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
402                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
403                    match (a.clone(), b.clone()) {
404                        (Value::Int(x), Value::Int(y)) => {
405                            let r = if x > y { Trit::Affirm } else if x == y { Trit::Tend } else { Trit::Reject };
406                            self.stack.push(Value::Trit(r));
407                        }
408                        (Value::Float(x), Value::Float(y)) => {
409                            let r = if x > y { Trit::Affirm } else if (x - y).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
410                            self.stack.push(Value::Trit(r));
411                        }
412                        (Value::Int(x), Value::Trit(y)) => {
413                            let bv = y as i64;
414                            let r = if x > bv { Trit::Affirm } else if x == bv { Trit::Tend } else { Trit::Reject };
415                            self.stack.push(Value::Trit(r));
416                        }
417                        (Value::Trit(x), Value::Int(y)) => {
418                            let av = x as i64;
419                            let r = if av > y { Trit::Affirm } else if av == y { Trit::Tend } else { Trit::Reject };
420                            self.stack.push(Value::Trit(r));
421                        }
422                        (Value::Int(av), Value::Float(bv)) => {
423                            let a_val = av as f64;
424                            let r = if a_val > bv { Trit::Affirm } else if (a_val - bv).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
425                            self.stack.push(Value::Trit(r));
426                        }
427                        (Value::Float(av), Value::Int(bv)) => {
428                            let b_val = bv as f64;
429                            let r = if av > b_val { Trit::Affirm } else if (av - b_val).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
430                            self.stack.push(Value::Trit(r));
431                        }
432                        (Value::Trit(av), Value::Float(bv)) => {
433                            let a_val = av as i8 as f64;
434                            let r = if a_val > bv { Trit::Affirm } else if (a_val - bv).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
435                            self.stack.push(Value::Trit(r));
436                        }
437                        (Value::Float(av), Value::Trit(bv)) => {
438                            let b_val = bv as i8 as f64;
439                            let r = if av > b_val { Trit::Affirm } else if (av - b_val).abs() < f64::EPSILON { Trit::Tend } else { Trit::Reject };
440                            self.stack.push(Value::Trit(r));
441                        }
442                        (Value::Trit(x), Value::Trit(y)) => {
443                            let av = x as i64;
444                            let bv = y as i64;
445                            let r = if av > bv { Trit::Affirm } else if av == bv { Trit::Tend } else { Trit::Reject };
446                            self.stack.push(Value::Trit(r));
447                        }
448                        _ => return Err(VmError::TypeMismatch { expected: "Numeric".into(), found: format!("{:?}", (a, b)) }),
449                    }
450                }
451                0x16 => { // Teq
452                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
453                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
454                    let is_eq = match (a.clone(), b.clone()) {
455                        (Value::Int(av), Value::Trit(bv)) => av == bv as i64,
456                        (Value::Trit(av), Value::Int(bv)) => av as i64 == bv,
457                        (Value::Float(av), Value::Float(bv)) => (av - bv).abs() < f64::EPSILON,
458                        (Value::Float(av), Value::Trit(bv)) => (av - (bv as i8 as f64)).abs() < f64::EPSILON,
459                        (Value::Trit(av), Value::Float(bv)) => ((av as i8 as f64) - bv).abs() < f64::EPSILON,
460                        (Value::Float(av), Value::Int(bv)) => (av - (bv as f64)).abs() < f64::EPSILON,
461                        (Value::Int(av), Value::Float(bv)) => ((av as f64) - bv).abs() < f64::EPSILON,
462                        _ => a == b,
463                    };
464                    let r = if is_eq { Trit::Affirm } else { Trit::Reject };
465                    self.stack.push(Value::Trit(r));
466                }
467                0x17 => { // TpushInt
468                    let mut b = [0u8; 8];
469                    for i in 0..8 { b[i] = self.read_u8()?; }
470                    self.stack.push(Value::Int(i64::from_le_bytes(b)));
471                }
472                0x18 => { // TaddInt
473                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
474                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
475                    match (a.clone(), b.clone()) {
476                        (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x + y)),
477                        _ => return Err(VmError::TypeMismatch { expected: "Int".into(), found: format!("{:?}", (a, b)) }),
478                    }
479                }
480                0x19 => { // TpushFloat
481                    let mut b = [0u8; 8];
482                    for i in 0..8 { b[i] = self.read_u8()?; }
483                    self.stack.push(Value::Float(f64::from_le_bytes(b)));
484                }
485                0x1e => { // Tdiv
486                    let b_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
487                    let a_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
488                    match (a_val.clone(), b_val.clone()) {
489                        (Value::Int(av), Value::Int(bv)) => {
490                            if bv == 0 { return Err(VmError::RuntimeError("Division by zero".into())); }
491                            self.stack.push(Value::Int(av / bv));
492                        }
493                        (Value::Float(av), Value::Float(bv)) => {
494                            if bv == 0.0 { return Err(VmError::RuntimeError("Division by zero".into())); }
495                            self.stack.push(Value::Float(av / bv));
496                        }
497                        (Value::Int(av), Value::Trit(bv)) => {
498                            let b = bv as i64;
499                            if b == 0 { return Err(VmError::RuntimeError("Division by zero".into())); }
500                            self.stack.push(Value::Int(av / b));
501                        }
502                        (Value::Trit(av), Value::Int(bv)) => {
503                            if bv == 0 { return Err(VmError::RuntimeError("Division by zero".into())); }
504                            self.stack.push(Value::Int(av as i64 / bv));
505                        }
506                        (Value::Float(av), Value::Trit(bv)) => {
507                            let b = bv as i8 as f64;
508                            if b == 0.0 { return Err(VmError::RuntimeError("Division by zero".into())); }
509                            self.stack.push(Value::Float(av / b));
510                        }
511                        (Value::Trit(av), Value::Float(bv)) => {
512                            if bv == 0.0 { return Err(VmError::RuntimeError("Division by zero".into())); }
513                            self.stack.push(Value::Float(av as i8 as f64 / bv));
514                        }
515                        (Value::Float(av), Value::Int(bv)) => {
516                            let b = bv as f64;
517                            if b == 0.0 { return Err(VmError::RuntimeError("Division by zero".into())); }
518                            self.stack.push(Value::Float(av / b));
519                        }
520                        (Value::Int(av), Value::Float(bv)) => {
521                            if bv == 0.0 { return Err(VmError::RuntimeError("Division by zero".into())); }
522                            self.stack.push(Value::Float(av as f64 / bv));
523                        }
524                        _ => return Err(VmError::TypeMismatch { expected: "Numeric".into(), found: format!("{:?}", (a_val, b_val)) }),
525                    }
526                }
527                0x1f => { // Tmod
528                    let b_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
529                    let a_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
530                    match (a_val.clone(), b_val.clone()) {
531                        (Value::Int(av), Value::Int(bv)) => {
532                            if bv == 0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
533                            self.stack.push(Value::Int(av % bv));
534                        }
535                        (Value::Int(av), Value::Trit(bv)) => {
536                            let b = bv as i64;
537                            if b == 0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
538                            self.stack.push(Value::Int(av % b));
539                        }
540                        (Value::Trit(av), Value::Int(bv)) => {
541                            if bv == 0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
542                            self.stack.push(Value::Int(av as i64 % bv));
543                        }
544                        (Value::Float(av), Value::Float(bv)) => {
545                             if bv == 0.0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
546                             self.stack.push(Value::Float(av % bv));
547                        }
548                        (Value::Float(av), Value::Trit(bv)) => {
549                             let b = bv as i8 as f64;
550                             if b == 0.0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
551                             self.stack.push(Value::Float(av % b));
552                        }
553                        (Value::Trit(av), Value::Float(bv)) => {
554                             if bv == 0.0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
555                             self.stack.push(Value::Float(av as i8 as f64 % bv));
556                        }
557                        (Value::Float(av), Value::Int(bv)) => {
558                             let b = bv as f64;
559                             if b == 0.0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
560                             self.stack.push(Value::Float(av % b));
561                        }
562                        (Value::Int(av), Value::Float(bv)) => {
563                             if bv == 0.0 { return Err(VmError::RuntimeError("Modulo by zero".into())); }
564                             self.stack.push(Value::Float(av as f64 % bv));
565                        }
566                        _ => return Err(VmError::TypeMismatch { expected: "Int or Trit".into(), found: format!("{:?}", (a_val, b_val)) }),
567                    }
568                }
569                0x20 => { // Tprint
570                    let val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
571                    let line = match &val {
572                        Value::Trit(t) => format!("{:?}", t),
573                        Value::Int(i) => format!("{}", i),
574                        Value::Float(f) => format!("{}", f),
575                        Value::String(s) => s.clone(),
576                        Value::TensorRef(idx) => format!("TensorRef({})", idx),
577                        Value::AgentRef(idx, addr) => format!("AgentRef({}, {:?})", idx, addr),
578                    };
579                    println!("{}", line);
580                    self.print_log.push(line);
581                }
582                0x21 => { // TpushString
583                    let len = self.read_u16()? as usize;
584                    let mut bytes = vec![0u8; len];
585                    for i in 0..len { bytes[i] = self.read_u8()?; }
586                    let s = String::from_utf8(bytes).map_err(|_| VmError::RuntimeError("Invalid UTF-8 string".into()))?;
587                    self.stack.push(Value::String(s));
588                }
589                0x22 => { // Tidx
590                    let col = self.stack.pop().ok_or(VmError::StackUnderflow)?;
591                    let row = self.stack.pop().ok_or(VmError::StackUnderflow)?;
592                    let rf = self.stack.pop().ok_or(VmError::StackUnderflow)?;
593                    let r = match row { Value::Int(v) => v, Value::Trit(t) => t as i64, _ => return Err(VmError::TypeMismatch { expected: "Int or Trit".into(), found: format!("{:?}", row) }) };
594                    let c = match col { Value::Int(v) => v, Value::Trit(t) => t as i64, _ => return Err(VmError::TypeMismatch { expected: "Int or Trit".into(), found: format!("{:?}", col) }) };
595                    match rf {
596                        Value::TensorRef(idx) => {
597                            if idx >= self.tensors.len() {
598                                return Err(VmError::TensorNotAllocated(idx));
599                            }
600                            let tensor = &self.tensors[idx];
601                            let pos = if tensor.cols > 1 { r as usize * tensor.cols + c as usize } else { r as usize };
602                            if pos >= tensor.data.len() {
603                                return Err(VmError::TensorIndexOutOfBounds { tensor_id: idx, index: pos, size: tensor.data.len() });
604                            }
605                            self.stack.push(Value::Trit(tensor.data[pos]));
606                        }
607                        _ => return Err(VmError::TypeMismatch { expected: "TensorRef".into(), found: format!("{:?}", rf) }),
608                    }
609                }
610                0x23 => { // Tset
611                    let val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
612                    let col = self.stack.pop().ok_or(VmError::StackUnderflow)?;
613                    let row = self.stack.pop().ok_or(VmError::StackUnderflow)?;
614                    let rf = self.stack.pop().ok_or(VmError::StackUnderflow)?;
615                    let r = match row { Value::Int(v) => v, Value::Trit(t) => t as i64, _ => return Err(VmError::TypeMismatch { expected: "Int or Trit".into(), found: format!("{:?}", row) }) };
616                    let c = match col { Value::Int(v) => v, Value::Trit(t) => t as i64, _ => return Err(VmError::TypeMismatch { expected: "Int or Trit".into(), found: format!("{:?}", col) }) };
617                    match (rf.clone(), val.clone()) {
618                        (Value::TensorRef(idx), Value::Trit(t)) => {
619                            if idx >= self.tensors.len() { return Err(VmError::TensorNotAllocated(idx)); }
620                            let tensor = &mut self.tensors[idx];
621                            let pos = if tensor.cols > 1 { r as usize * tensor.cols + c as usize } else { r as usize };
622                            if pos >= tensor.data.len() { return Err(VmError::TensorIndexOutOfBounds { tensor_id: idx, index: pos, size: tensor.data.len() }); }
623                            tensor.data[pos] = t;
624                        }
625                        (Value::TensorRef(idx), Value::Int(v)) => {
626                            if idx >= self.tensors.len() { return Err(VmError::TensorNotAllocated(idx)); }
627                            let tensor = &mut self.tensors[idx];
628                            let pos = if tensor.cols > 1 { r as usize * tensor.cols + c as usize } else { r as usize };
629                            if pos >= tensor.data.len() { return Err(VmError::TensorIndexOutOfBounds { tensor_id: idx, index: pos, size: tensor.data.len() }); }
630                            tensor.data[pos] = if v > 0 { Trit::Affirm } else if v < 0 { Trit::Reject } else { Trit::Tend };
631                        }
632                        _ => return Err(VmError::TypeMismatch { expected: "TensorRef, Trit".into(), found: format!("{:?}", (rf, val)) }),
633                    }
634                }
635                0x24 => { // Tshape
636                    let rf = self.stack.pop().ok_or(VmError::StackUnderflow)?;
637                    if let Value::TensorRef(idx) = rf {
638                        if idx >= self.tensors.len() {
639                            return Err(VmError::TensorNotAllocated(idx));
640                        }
641                        let tensor = &self.tensors[idx];
642                        self.stack.push(Value::Int(tensor.rows as i64));
643                        self.stack.push(Value::Int(tensor.cols as i64));
644                    } else { return Err(VmError::TypeMismatch { expected: "TensorRef".into(), found: format!("{:?}", rf) }); }
645                }
646                0x30 => { // Tspawn — (type_id) → AgentRef
647                    let type_id = self.read_u16()?;
648                    if let Some(&handler_addr) = self.agent_types.get(&type_id) {
649                        let id = self.agents.len();
650                        self.agents.push(AgentInstance { handler_addr, mailbox: Default::default() });
651                        self.stack.push(Value::AgentRef(id, None));
652                    } else {
653                        return Err(VmError::AgentTypeNotRegistered(type_id));
654                    }
655                }
656                0x31 => { // Tsend — msg, target → void
657                    let msg = self.stack.pop().ok_or(VmError::StackUnderflow)?;
658                    let target = self.stack.pop().ok_or(VmError::StackUnderflow)?;
659                    if let Value::AgentRef(id, None) = target {
660                        if id < self.agents.len() {
661                            self.agents[id].mailbox.push_back(msg);
662                        } else {
663                            return Err(VmError::AgentIdInvalid(id));
664                        }
665                    } else {
666                        return Err(VmError::TypeMismatch { expected: "Local AgentRef".into(), found: format!("{:?}", target) });
667                    }
668                }
669                0x32 => { // Tawait — target → result
670                    let target = self.stack.pop().ok_or(VmError::StackUnderflow)?;
671                    if let Value::AgentRef(id, None) = target {
672                        if id < self.agents.len() {
673                            if self.call_stack.len() >= MAX_CALL_DEPTH {
674                                return Err(VmError::CallStackOverflow);
675                            }
676                            let handler_addr = self.agents[id].handler_addr;
677                            let msg = self.agents[id].mailbox.pop_front().unwrap_or(Value::default());
678                            // Synchronous handler dispatch — identical to TCALL
679                            self.register_stack.push(self.registers.clone());
680                            self.call_stack.push(self.pc);
681                            self.pc = handler_addr;
682                            self.stack.push(msg);
683                        } else {
684                            return Err(VmError::AgentIdInvalid(id));
685                        }
686                    } else {
687                        return Err(VmError::TypeMismatch { expected: "Local AgentRef".into(), found: format!("{:?}", target) });
688                    }
689                }
690                0x25 => { // TjmpEqInt — imm_int, imm_addr → peek, jumps if eq
691                    let mut b = [0u8; 8];
692                    for i in 0..8 { b[i] = self.read_u8()?; }
693                    let target_val = i64::from_le_bytes(b);
694                    let addr = self.read_u16()?;
695                    let val = self.stack.last().ok_or(VmError::StackUnderflow)?;
696                    let is_eq = match val {
697                        Value::Int(v) => *v == target_val,
698                        Value::Trit(t) => (*t as i8) as i64 == target_val,
699                        _ => false,
700                    };
701                    if is_eq { self.pc = addr as usize; }
702                }
703                0x26 => { // TlessEqual
704                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
705                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
706                    let is_le = match (a.clone(), b.clone()) {
707                        (Value::Int(x), Value::Int(y)) => x <= y,
708                        (Value::Float(x), Value::Float(y)) => x <= y || (x - y).abs() < f64::EPSILON,
709                        (Value::Int(x), Value::Trit(y)) => x <= y as i64,
710                        (Value::Trit(x), Value::Int(y)) => (x as i64) <= y,
711                        (Value::Trit(x), Value::Trit(y)) => (x as i64) <= (y as i64),
712                        _ => false,
713                    };
714                    self.stack.push(Value::Trit(if is_le { Trit::Affirm } else { Trit::Reject }));
715                }
716                0x27 => { // TgreaterEqual
717                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
718                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
719                    let is_ge = match (a.clone(), b.clone()) {
720                        (Value::Int(x), Value::Int(y)) => x >= y,
721                        (Value::Float(x), Value::Float(y)) => x >= y || (x - y).abs() < f64::EPSILON,
722                        (Value::Int(x), Value::Trit(y)) => x >= y as i64,
723                        (Value::Trit(x), Value::Int(y)) => (x as i64) >= y,
724                        (Value::Trit(x), Value::Trit(y)) => (x as i64) >= (y as i64),
725                        _ => false,
726                    };
727                    self.stack.push(Value::Trit(if is_ge { Trit::Affirm } else { Trit::Reject }));
728                }
729                0x28 => { // Tand — min(a, b) in balanced ternary (logical AND)
730                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
731                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
732                    let to_trit = |v: Value| -> Result<Trit, VmError> {
733                        match v {
734                            Value::Trit(t) => Ok(t),
735                            Value::Int(n) if n > 0 => Ok(Trit::Affirm),
736                            Value::Int(0) => Ok(Trit::Tend),
737                            Value::Int(_) => Ok(Trit::Reject),
738                            other => Err(VmError::TypeMismatch { expected: "Trit or Int".into(), found: format!("{:?}", other) }),
739                        }
740                    };
741                    let ta = to_trit(a)?;
742                    let tb = to_trit(b)?;
743                    let result = if (ta as i8) <= (tb as i8) { ta } else { tb };
744                    self.stack.push(Value::Trit(result));
745                }
746                0x29 => { // Tor — max(a, b) in balanced ternary (logical OR)
747                    let b = self.stack.pop().ok_or(VmError::StackUnderflow)?;
748                    let a = self.stack.pop().ok_or(VmError::StackUnderflow)?;
749                    let to_trit = |v: Value| -> Result<Trit, VmError> {
750                        match v {
751                            Value::Trit(t) => Ok(t),
752                            Value::Int(n) if n > 0 => Ok(Trit::Affirm),
753                            Value::Int(0) => Ok(Trit::Tend),
754                            Value::Int(_) => Ok(Trit::Reject),
755                            other => Err(VmError::TypeMismatch { expected: "Trit or Int".into(), found: format!("{:?}", other) }),
756                        }
757                    };
758                    let ta = to_trit(a)?;
759                    let tb = to_trit(b)?;
760                    let result = if (ta as i8) >= (tb as i8) { ta } else { tb };
761                    self.stack.push(Value::Trit(result));
762                }
763                0x2a => { // TjmpEqFloat — peek stack, jump if top Float equals embedded f64 literal
764                    // Emitted by betbc.rs for float match-arm patterns (PARSER-002 fix).
765                    // Layout: [opcode: u8] [target_f64: 8 bytes LE] [jump_addr: u16 LE]
766                    // Peeks the top of stack (does NOT consume it), jumps if value matches
767                    // within machine epsilon.
768                    let mut fb = [0u8; 8];
769                    for i in 0..8 { fb[i] = self.read_u8()?; }
770                    let target_f = f64::from_le_bytes(fb);
771                    let addr = self.read_u16()?;
772                    let val = self.stack.last().ok_or(VmError::StackUnderflow)?;
773                    if let Value::Float(f) = val {
774                        if (f - target_f).abs() < 1e-9 {
775                            self.pc = addr as usize;
776                        }
777                    }
778                }
779                0x33 => { // Topent — path_str, mode_int → handle_int
780                    let mode = self.stack.pop().ok_or(VmError::StackUnderflow)?;
781                    let path = self.stack.pop().ok_or(VmError::StackUnderflow)?;
782                    if let (Value::String(p), Value::Int(m)) = (path, mode) {
783                        use std::fs::OpenOptions;
784                        let mut options = OpenOptions::new();
785                        match m {
786                            0 => { options.read(true); } // Read
787                            1 => { options.write(true).create(true).truncate(true); } // Write
788                            2 => { options.append(true).create(true); } // Append
789                            _ => return Err(VmError::RuntimeError(format!("Invalid file mode: {m}"))),
790                        }
791                        let file = options.open(&p).map_err(|e| VmError::FileOpenError(e.to_string()))?;
792                        let handle = self.open_files.len();
793                        self.open_files.push(Some(file));
794                        self.stack.push(Value::Int(handle as i64));
795                    } else {
796                        return Err(VmError::TypeMismatch { expected: "String, Int".into(), found: "Unknown".into() });
797                    }
798                }
799                0x34 => { // Treadt — handle_int → trit
800                    let handle_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
801                    if let Value::Int(h) = handle_val {
802                        let h = h as usize;
803                        if h >= self.open_files.len() || self.open_files[h].is_none() {
804                            return Err(VmError::FileNotOpen(h));
805                        }
806                        let file = self.open_files[h].as_mut().unwrap();
807                        let mut buf = [0u8; 1];
808                        use std::io::Read;
809                        match file.read_exact(&mut buf) {
810                            Ok(_) => {
811                                let t = match buf[0] {
812                                    b'+' | b'1' => Trit::Affirm,
813                                    b'-' => Trit::Reject,
814                                    _ => Trit::Tend,
815                                };
816                                self.stack.push(Value::Trit(t));
817                            }
818                            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
819                                self.stack.push(Value::Trit(Trit::Tend)); // EOF as Tend
820                            }
821                            Err(e) => return Err(VmError::FileReadError(e.to_string())),
822                        }
823                    } else {
824                        return Err(VmError::TypeMismatch { expected: "Int".into(), found: format!("{:?}", handle_val) });
825                    }
826                }
827                0x35 => { // Twritet — handle_int, trit → void
828                    let t_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
829                    let h_val = self.stack.pop().ok_or(VmError::StackUnderflow)?;
830                    if let (Value::Int(h), Value::Trit(t)) = (h_val, t_val) {
831                        let h = h as usize;
832                        if h >= self.open_files.len() || self.open_files[h].is_none() {
833                            return Err(VmError::FileNotOpen(h));
834                        }
835                        let file = self.open_files[h].as_mut().unwrap();
836                        let out = match t {
837                            Trit::Affirm => b'+',
838                            Trit::Reject => b'-',
839                            Trit::Tend   => b'0',
840                        };
841                        use std::io::Write;
842                        file.write_all(&[out]).map_err(|e| VmError::FileWriteError(e.to_string()))?;
843                    } else {
844                        return Err(VmError::TypeMismatch { expected: "Int, Trit".into(), found: "Unknown".into() });
845                    }
846                }
847                0x36 => { // Tnodeid — push this node's runtime address as a String
848                    // Defers the binding to runtime so --node-addr is respected.
849                    // Previously, Expr::NodeId emitted a hardcoded "127.0.0.1:7373"
850                    // string at compile time, ignoring vm.set_node_id().
851                    self.stack.push(Value::String(self.node_id.clone()));
852                }
853                0x00 => return Ok(()),
854                _ => return Err(VmError::InvalidOpcode(opcode)),
855            }
856        }
857        Ok(())
858    }
859
860    fn read_u8(&mut self) -> Result<u8, VmError> {
861        if self.pc >= self.code.len() { return Err(VmError::PcOutOfBounds(self.pc)); }
862        let val = self.code[self.pc];
863        self.pc += 1;
864        Ok(val)
865    }
866
867    fn read_u16(&mut self) -> Result<u16, VmError> {
868        if self.pc + 1 >= self.code.len() { return Err(VmError::PcOutOfBounds(self.pc)); }
869        let val = u16::from_le_bytes([self.code[self.pc], self.code[self.pc + 1]]);
870        self.pc += 2;
871        Ok(val)
872    }
873}