Skip to main content

ternlang_core/codegen/
betbc.rs

1use crate::ast::*;
2use crate::vm::bet::pack_trits;
3use crate::trit::Trit;
4
5pub struct BytecodeEmitter {
6    code: Vec<u8>,
7    symbols: std::collections::HashMap<String, u8>,
8    func_addrs: std::collections::HashMap<String, u16>,
9    function_patches: std::collections::HashMap<String, Vec<usize>>,
10    break_patches: Vec<usize>,
11    continue_patches: Vec<usize>,
12    next_reg: u8,
13    struct_layouts: std::collections::HashMap<String, Vec<String>>,
14    agent_type_ids: std::collections::HashMap<String, u16>,
15    agent_handlers: Vec<(u16, u16)>,
16    /// Snapshots of the local symbol table for each function, keyed by function name.
17    /// Captured just before scope is restored so callers can map reg→varname after execution.
18    function_symbols: std::collections::HashMap<String, std::collections::HashMap<String, u8>>,
19}
20
21impl BytecodeEmitter {
22    pub fn new() -> Self {
23        Self {
24            code: Vec::new(),
25            symbols: std::collections::HashMap::new(),
26            func_addrs: std::collections::HashMap::new(),
27            function_patches: std::collections::HashMap::new(),
28            break_patches: Vec::new(),
29            continue_patches: Vec::new(),
30            next_reg: 0,
31            struct_layouts: std::collections::HashMap::new(),
32            agent_type_ids: std::collections::HashMap::new(),
33            agent_handlers: Vec::new(),
34            function_symbols: std::collections::HashMap::new(),
35        }
36    }
37
38    /// Returns the top-level variable-name → register-number map.
39    pub fn get_symbols(&self) -> &std::collections::HashMap<String, u8> {
40        &self.symbols
41    }
42
43    /// Returns the local symbol snapshot for a specific function (e.g. "main").
44    /// Used by `ternlang-cli --emit-symbols` to correlate VM register dumps with source variable names.
45    pub fn get_function_symbols(&self, name: &str) -> Option<&std::collections::HashMap<String, u8>> {
46        self.function_symbols.get(name)
47    }
48
49    pub fn register_agents(&self, vm: &mut crate::vm::BetVm) {
50        for &(type_id, addr) in &self.agent_handlers {
51            vm.register_agent_type(type_id, addr as usize);
52        }
53    }
54
55    pub fn emit_header_jump(&mut self) -> usize {
56        let patch_pos = self.code.len() + 1;
57        self.code.push(0x0b); // TJMP
58        self.code.extend_from_slice(&[0u8, 0u8]);
59        patch_pos
60    }
61
62    pub fn patch_header_jump(&mut self, patch_pos: usize) {
63        let addr = self.code.len() as u16;
64        self.patch_u16(patch_pos, addr);
65    }
66
67    pub fn emit_program(&mut self, program: &Program) {
68        let parent_next_reg = self.next_reg;
69        for s in &program.structs {
70            let names: Vec<String> = s.fields.iter().map(|(n, _)| n.clone()).collect();
71            self.struct_layouts.insert(s.name.clone(), names);
72        }
73        for (idx, agent) in program.agents.iter().enumerate() {
74            self.agent_type_ids.insert(agent.name.clone(), idx as u16);
75        }
76
77        // PASS 1: Addresses
78        let real_code = std::mem::take(&mut self.code);
79        let real_func_addrs = std::mem::take(&mut self.func_addrs);
80        let real_agent_handlers = std::mem::take(&mut self.agent_handlers);
81        let base_addr = real_code.len() as u16;
82
83        for agent in &program.agents {
84            let type_id = self.agent_type_ids[&agent.name];
85            let mut handler_addr = None;
86            for method in &agent.methods {
87                let addr = base_addr + self.code.len() as u16;
88                if handler_addr.is_none() { handler_addr = Some(addr); }
89                self.emit_function(method);
90                self.func_addrs.insert(format!("{}::{}", agent.name, method.name), addr);
91            }
92            if let Some(addr) = handler_addr { self.agent_handlers.push((type_id, addr)); }
93        }
94        for func in &program.functions {
95            let addr = base_addr + self.code.len() as u16;
96            self.func_addrs.insert(func.name.clone(), addr);
97            // Ensure any global symbols or previous definitions are visible
98            self.emit_function(func);
99        }
100
101        let final_func_addrs = std::mem::replace(&mut self.func_addrs, real_func_addrs);
102        let final_agent_handlers = std::mem::replace(&mut self.agent_handlers, real_agent_handlers);
103        self.code = real_code;
104        self.func_addrs = final_func_addrs;
105        self.agent_handlers = final_agent_handlers;
106        self.next_reg = parent_next_reg;
107
108        // PASS 2: Real
109        for agent in &program.agents {
110            for method in &agent.methods { self.emit_function(method); }
111        }
112        for func in &program.functions { self.emit_function(func); }
113    }
114
115    pub fn emit_function(&mut self, func: &Function) {
116        let func_addr = self.code.len() as u16;
117        self.func_addrs.insert(func.name.clone(), func_addr);
118        if let Some(patches) = self.function_patches.remove(&func.name) {
119            for p in patches {
120                self.code[p..p + 2].copy_from_slice(&func_addr.to_le_bytes());
121            }
122        }
123        let parent_symbols = self.symbols.clone();
124        let parent_next_reg = self.next_reg;
125        self.next_reg = 0;
126
127        // If function has @sparseskip, we could emit a special header here.
128        // For now, it's just a marker in the AST.
129
130        for (name, ty) in func.params.iter().rev() {
131            if let Type::Named(s_name) = ty {
132                if let Some(fields) = self.struct_layouts.get(s_name).cloned() {
133                    // Structs are passed as a bundle: [field1, field2, ..., root_dummy]
134                    // We must pop root dummy first, then fields.
135                    
136                    // Pop root dummy
137                    let root_reg = self.next_reg;
138                    self.symbols.insert(name.clone(), root_reg);
139                    self.next_reg += 1;
140                    self.code.push(0x08); self.code.push(root_reg);
141
142                    // Pop fields in reverse order of how they were pushed
143                    for f_name in fields.iter().rev() {
144                        let f_reg = self.next_reg;
145                        let key = format!("{}.{}", name, f_name);
146                        self.symbols.insert(key, f_reg);
147                        self.next_reg += 1;
148                        self.code.push(0x08); self.code.push(f_reg);
149                    }
150                    continue;
151                }
152            }
153            let reg = self.next_reg;
154            self.symbols.insert(name.clone(), reg);
155            self.next_reg += 1;
156            self.code.push(0x08); self.code.push(reg);
157        }
158        for stmt in &func.body { self.emit_stmt(stmt); }
159        // Snapshot local symbols before scope is restored — used by --emit-symbols
160        self.function_symbols.insert(func.name.clone(), self.symbols.clone());
161        self.symbols = parent_symbols;
162        self.next_reg = parent_next_reg;
163        self.code.push(0x11); // TRET
164    }
165
166    pub fn emit_stmt(&mut self, stmt: &Stmt) {
167        match stmt {
168            Stmt::Let { name, ty, value } => {
169                let mut handled = false;
170                if let Type::TritTensor { dims } = ty {
171                    // Only auto-allocate if size is fixed (>0) and no literal is provided
172                    if !dims.is_empty() && !dims.contains(&0) && !matches!(value, Expr::TritTensorLiteral(_)) {
173                        let rows = dims[0];
174                        let cols = if dims.len() > 1 { dims[1] } else { 1 };
175                        self.code.push(0x0f);
176                        self.code.extend_from_slice(&(rows as u16).to_le_bytes());
177                        self.code.extend_from_slice(&(cols as u16).to_le_bytes());
178                        handled = true;
179                    }
180                } else if let Type::Named(_) = ty {
181                    if let Expr::StructLiteral { fields, .. } = value {
182                        // Flatten struct fields into mangled registers
183                        for (f_name, f_val) in fields {
184                            self.emit_expr(f_val);
185                            let reg = self.next_reg;
186                            let key = format!("{}.{}", name, f_name);
187                            self.symbols.insert(key, reg);
188                            self.next_reg += 1;
189                            self.code.push(0x08); self.code.push(reg);
190                        }
191                        // Now we let the normal path emit the root variable's dummy value
192                    }
193                }
194                if !handled {
195                    self.emit_expr(value);
196                }
197                let reg = self.next_reg;
198                self.symbols.insert(name.clone(), reg);
199                self.next_reg += 1;
200                self.code.push(0x08); self.code.push(reg); // TSTORE
201            }
202            Stmt::Set { name, value } => {
203                self.emit_expr(value);
204                if let Some(&reg) = self.symbols.get(name) {
205                    self.code.push(0x08); self.code.push(reg);
206                }
207            }
208            Stmt::FieldSet { object, field, value } => {
209                let key = format!("{}.{}", object, field);
210                self.emit_expr(value);
211                if let Some(&reg) = self.symbols.get(&key) {
212                    self.code.push(0x08); self.code.push(reg);
213                }
214            }
215            Stmt::IndexSet { object, row, col, value } => {
216                if let Some(&reg) = self.symbols.get(object) {
217                    self.code.push(0x09); self.code.push(reg);
218                    self.emit_expr(row);
219                    self.emit_expr(col);
220                    self.emit_expr(value);
221                    self.code.push(0x23);
222                }
223            }
224            Stmt::IfTernary { condition, on_pos, on_zero, on_neg } => {
225                self.emit_expr(condition);
226                self.code.push(0x0a);
227                let pos_patch = self.code.len() + 1;
228                self.code.push(0x05); self.code.extend_from_slice(&[0, 0]);
229                self.code.push(0x0a);
230                let zero_patch = self.code.len() + 1;
231                self.code.push(0x06); self.code.extend_from_slice(&[0, 0]);
232                self.code.push(0x0c);
233                self.emit_stmt(on_neg);
234                let exit_patch = self.code.len() + 1;
235                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
236                let pos_addr = self.code.len() as u16;
237                self.patch_u16(pos_patch, pos_addr);
238                self.code.push(0x0c);
239                self.emit_stmt(on_pos);
240                let exit_pos = self.code.len() + 1;
241                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
242                let zero_addr = self.code.len() as u16;
243                self.patch_u16(zero_patch, zero_addr);
244                self.code.push(0x0c);
245                self.emit_stmt(on_zero);
246                let end = self.code.len() as u16;
247                self.patch_u16(exit_patch, end);
248                self.patch_u16(exit_pos, end);
249            }
250            Stmt::Match { condition, arms } => {
251                self.emit_expr(condition);
252                let cond_reg = self.next_reg; self.next_reg += 1;
253                self.code.push(0x08); self.code.push(cond_reg); // Tstore
254
255                let mut end_patches = Vec::new();
256                let mut next_arm_patch = None;
257
258                for (val, stmt) in arms {
259                    if let Some(p) = next_arm_patch {
260                        let addr = self.code.len() as u16;
261                        self.patch_u16(p, addr);
262                    }
263
264                    // Load condition for this arm
265                    self.code.push(0x09); self.code.push(cond_reg); // Tload
266
267                    let match_patch;
268                    match val {
269                        1 => {
270                            self.code.push(0x05); // TjmpPos (peeks)
271                            match_patch = self.code.len();
272                            self.code.extend_from_slice(&[0, 0]);
273                        }
274                        0 => {
275                            self.code.push(0x06); // TjmpZero (peeks)
276                            match_patch = self.code.len();
277                            self.code.extend_from_slice(&[0, 0]);
278                        }
279                        -1 => {
280                            self.code.push(0x07); // TjmpNeg (peeks)
281                            match_patch = self.code.len();
282                            self.code.extend_from_slice(&[0, 0]);
283                        }
284                        v => {
285                            self.code.push(0x25); // TjmpEqInt (peeks)
286                            self.code.extend_from_slice(&v.to_le_bytes());
287                            match_patch = self.code.len();
288                            self.code.extend_from_slice(&[0, 0]);
289                        }
290                    }
291
292                    // Mismatch: Jump past body to the next arm's check
293                    let skip_patch = self.code.len() + 1;
294                    self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
295                    next_arm_patch = Some(skip_patch);
296
297                    // Match found: execute body
298                    let body_addr = self.code.len() as u16;
299                    self.patch_u16(match_patch, body_addr);
300                    
301                    // Body: first pop the condition we were peeking at
302                    self.code.push(0x0c); // Tpop
303                    self.emit_stmt(stmt);
304                    
305                    // After body, jump to end of match
306                    let end_patch = self.code.len() + 1;
307                    self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
308                    end_patches.push(end_patch);
309                }
310
311                if let Some(p) = next_arm_patch {
312                    let addr = self.code.len() as u16;
313                    self.patch_u16(p, addr);
314                }
315                
316                // If no arms matched, we still have one Tload on stack from the last failed arm check
317                // unless arms was empty (but semantic enforces it isn't for Trit, and for Int it might be)
318                if !arms.is_empty() {
319                    self.code.push(0x0c); // Tpop
320                }
321
322                let end_addr = self.code.len() as u16;
323                for p in end_patches { self.patch_u16(p, end_addr); }
324                self.next_reg -= 1;
325            }
326            Stmt::ForIn { var, iter, body } => {
327                self.emit_expr(iter);
328                let it_reg = self.next_reg; self.next_reg += 1;
329                self.code.push(0x08); self.code.push(it_reg);
330                self.code.push(0x09); self.code.push(it_reg);
331                self.code.push(0x24); // TSHAPE: pushes rows then cols (cols on top)
332                self.code.push(0x0c); // pop cols — iterate over rows, not cols
333                let r_reg = self.next_reg; self.next_reg += 1;
334                self.code.push(0x08); self.code.push(r_reg); // store rows as loop bound
335                let i_reg = self.next_reg; self.next_reg += 1;
336                self.code.push(0x17); self.code.extend_from_slice(&0i64.to_le_bytes());
337                self.code.push(0x08); self.code.push(i_reg);
338                
339                let top = self.code.len() as u16;
340                let pre_break = self.break_patches.len();
341                let pre_cont = self.continue_patches.len();
342
343                self.code.push(0x09); self.code.push(i_reg);
344                self.code.push(0x09); self.code.push(r_reg);
345                self.code.push(0x14);
346                self.code.push(0x0a);
347                let neg = self.code.len() + 1;
348                self.code.push(0x07); self.code.extend_from_slice(&[0, 0]);
349                self.code.push(0x0a);
350                let zero = self.code.len() + 1;
351                self.code.push(0x06); self.code.extend_from_slice(&[0, 0]);
352                self.code.push(0x0c);
353                self.code.push(0x09); self.code.push(it_reg);
354                self.code.push(0x09); self.code.push(i_reg);
355                self.code.push(0x17); self.code.extend_from_slice(&0i64.to_le_bytes());
356                self.code.push(0x22);
357                let v_reg = self.next_reg; self.next_reg += 1;
358                self.symbols.insert(var.clone(), v_reg);
359                self.code.push(0x08); self.code.push(v_reg);
360                self.emit_stmt(body);
361                
362                let cont_addr = self.code.len() as u16;
363                let cs: Vec<usize> = self.continue_patches.drain(pre_cont..).collect();
364                for p in cs { self.patch_u16(p, cont_addr); }
365
366                self.code.push(0x09); self.code.push(i_reg);
367                self.code.push(0x17); self.code.extend_from_slice(&1i64.to_le_bytes());
368                self.code.push(0x18);
369                self.code.push(0x08); self.code.push(i_reg);
370                let back = self.code.len() + 1;
371                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
372                self.patch_u16(back, top);
373                let end = self.code.len() as u16;
374                self.patch_u16(neg, end); self.patch_u16(zero, end);
375                let bs: Vec<usize> = self.break_patches.drain(pre_break..).collect();
376                for p in bs { self.patch_u16(p, end); }
377            }
378            Stmt::WhileTernary { condition, on_pos, on_zero, on_neg } => {
379                let top = self.code.len() as u16;
380                let pre_break = self.break_patches.len();
381                let pre_cont = self.continue_patches.len();
382
383                self.emit_expr(condition);
384                self.code.push(0x0a); // TDUP
385                let pos_patch = self.code.len() + 1;
386                self.code.push(0x05); self.code.extend_from_slice(&[0, 0]); // TJMP_POS
387                self.code.push(0x0a); // TDUP
388                let zero_patch = self.code.len() + 1;
389                self.code.push(0x06); self.code.extend_from_slice(&[0, 0]); // TJMP_ZERO
390                
391                // NEG ARM: execute and EXIT (don't loop back)
392                self.code.push(0x0c); // TPOP
393                self.emit_stmt(on_neg);
394                let exit_neg = self.code.len() + 1;
395                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]); // TJMP to end
396
397                // POS ARM: execute and LOOP BACK
398                let pos_addr = self.code.len() as u16;
399                self.patch_u16(pos_patch, pos_addr);
400                self.code.push(0x0c); // TPOP
401                self.emit_stmt(on_pos);
402                let back_pos = self.code.len() + 1;
403                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
404                self.patch_u16(back_pos, top);
405
406                // ZERO ARM: execute and EXIT (don't loop back)
407                let zero_addr = self.code.len() as u16;
408                self.patch_u16(zero_patch, zero_addr);
409                self.code.push(0x0c); // TPOP
410                self.emit_stmt(on_zero);
411                
412                let end = self.code.len() as u16;
413                self.patch_u16(exit_neg, end);
414
415                let cs: Vec<usize> = self.continue_patches.drain(pre_cont..).collect();
416                for p in cs { self.patch_u16(p, top); }
417                let bs: Vec<usize> = self.break_patches.drain(pre_break..).collect();
418                for p in bs { self.patch_u16(p, end); }
419            }
420            Stmt::Loop { body } => {
421                let top = self.code.len() as u16;
422                let pre_break = self.break_patches.len();
423                let pre_cont = self.continue_patches.len();
424                self.emit_stmt(body);
425                let back = self.code.len() + 1;
426                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
427                self.patch_u16(back, top);
428                let end = self.code.len() as u16;
429                let cs: Vec<usize> = self.continue_patches.drain(pre_cont..).collect();
430                for p in cs { self.patch_u16(p, top); }
431                let bs: Vec<usize> = self.break_patches.drain(pre_break..).collect();
432                for p in bs { self.patch_u16(p, end); }
433            }
434            Stmt::Break => {
435                let p = self.code.len() + 1;
436                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
437                self.break_patches.push(p);
438            }
439            Stmt::Continue => {
440                let p = self.code.len() + 1;
441                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]);
442                self.continue_patches.push(p);
443            }
444            Stmt::Send { target, message } => {
445                self.emit_expr(target);
446                self.emit_expr(message);
447                self.code.push(0x31); // TSEND
448            }
449            Stmt::Return(e) => { self.emit_expr(e); self.code.push(0x11); }
450            Stmt::Block(ss) => { for s in ss { self.emit_stmt(s); } }
451            Stmt::Expr(e) => { self.emit_expr(e); self.code.push(0x0c); }
452            Stmt::Decorated { directive: _, stmt } => { self.emit_stmt(stmt); }
453            _ => {}
454        }
455    }
456
457    fn emit_expr(&mut self, expr: &Expr) {
458        match expr {
459            Expr::TritLiteral(v) => {
460                self.code.push(0x01);
461                self.code.extend(pack_trits(&[Trit::from(*v)]));
462            }
463            Expr::IntLiteral(v) => {
464                self.code.push(0x17);
465                self.code.extend_from_slice(&v.to_le_bytes());
466            }
467            Expr::FloatLiteral(val) => {
468                self.code.push(0x19);
469                self.code.extend_from_slice(&val.to_le_bytes());
470            }
471            Expr::StringLiteral(val) => {
472                self.code.push(0x21); // TPUSH_STRING
473                let bytes = val.as_bytes();
474                self.code.extend_from_slice(&(bytes.len() as u16).to_le_bytes());
475                self.code.extend_from_slice(bytes);
476            }
477            Expr::Ident(name) => {
478                if let Some(&r) = self.symbols.get(name) {
479                    self.code.push(0x09); self.code.push(r);
480                }
481            }
482            Expr::BinaryOp { op, lhs, rhs } => {
483                self.emit_expr(lhs); self.emit_expr(rhs);
484                match op {
485                    BinOp::Add => self.code.push(0x02),
486                    BinOp::Mul => self.code.push(0x03),
487                    BinOp::Div => self.code.push(0x1e),
488                    BinOp::Mod => self.code.push(0x1f),
489                    BinOp::Sub => { self.code.push(0x04); self.code.push(0x02); }
490                    BinOp::Equal => self.code.push(0x16),
491                    BinOp::NotEqual => { self.code.push(0x16); self.code.push(0x04); }
492                    BinOp::And => self.code.push(0x28), // TAND = min(a,b)
493                    BinOp::Or  => self.code.push(0x29), // TOR  = max(a,b)
494                    BinOp::Less => self.code.push(0x14),
495                    BinOp::Greater => self.code.push(0x15),
496                    BinOp::LessEqual => self.code.push(0x26),
497                    BinOp::GreaterEqual => self.code.push(0x27),
498                }
499            }
500            Expr::UnaryOp { op, expr } => {
501                self.emit_expr(expr);
502                match op { UnOp::Neg => self.code.push(0x04) }
503            }
504            Expr::Call { callee, args } => {
505                match callee.as_str() {
506                    "print" | "println" => {
507                        for a in args {
508                            self.emit_expr(a);
509                            self.code.push(0x20); // TPRINT
510                        }
511                        self.code.push(0x01); self.code.extend(pack_trits(&[Trit::Tend])); // return hold()
512                    }
513                    "consensus" => {
514                        for a in args { self.emit_expr(a); }
515                        if args.len() == 2 { self.code.push(0x0e); }
516                    }
517                    "length" => {
518                        if args.len() == 1 {
519                            self.emit_expr(&args[0]);
520                            self.code.push(0x24); // TSHAPE
521                            self.code.push(0x0c); // TPOP (cols)
522                        }
523                    }
524                    "mul" => {
525                        for a in args { self.emit_expr(a); }
526                        if args.len() == 2 { self.code.push(0x03); }
527                    }
528                    "truth" => { self.code.push(0x01); self.code.extend(pack_trits(&[Trit::Affirm])); }
529                    "hold" => { self.code.push(0x01); self.code.extend(pack_trits(&[Trit::Tend])); }
530                    "conflict" => { self.code.push(0x01); self.code.extend(pack_trits(&[Trit::Reject])); }
531                    _ => {
532                        for a in args {
533                            // If argument is a struct, we need to push all its flattened fields + root dummy
534                            let mut pushed_as_struct = false;
535                            if let Expr::Ident(name) = a {
536                                // We don't have the variable type here, but we can try to find if it's a struct
537                                // by looking for any mangled keys starting with "name.".
538                                // To get the correct field order, we'd need the struct name.
539                                // Let's try to find which struct layout matches the existing mangled keys.
540                                let mut fields_found = Vec::new();
541                                for (_s_name, s_fields) in &self.struct_layouts {
542                                    let mut all_present = true;
543                                    let mut current_regs = Vec::new();
544                                    for f in s_fields {
545                                        let key = format!("{}.{}", name, f);
546                                        if let Some(&r) = self.symbols.get(&key) {
547                                            current_regs.push(r);
548                                        } else {
549                                            all_present = false;
550                                            break;
551                                        }
552                                    }
553                                    if all_present && !s_fields.is_empty() {
554                                        fields_found = current_regs;
555                                        break;
556                                    }
557                                }
558
559                                if !fields_found.is_empty() {
560                                    for reg in fields_found {
561                                        self.code.push(0x09); self.code.push(reg); // TLOAD field
562                                    }
563                                    // Push root dummy
564                                    if let Some(&reg) = self.symbols.get(name) {
565                                        self.code.push(0x09); self.code.push(reg); // TLOAD root
566                                    }
567                                    pushed_as_struct = true;
568                                }
569                            }
570                            
571                            if !pushed_as_struct {
572                                self.emit_expr(a);
573                            }
574                        }
575                        self.code.push(0x10); // TCALL
576                        if let Some(&addr) = self.func_addrs.get(callee) {
577                            self.code.extend_from_slice(&addr.to_le_bytes());
578                        } else {
579                            let patch = self.code.len();
580                            self.code.extend_from_slice(&[0, 0]);
581                            self.function_patches.entry(callee.to_string()).or_default().push(patch);
582                        }
583                    }
584                }
585            }
586            Expr::Spawn { agent_name, .. } => {
587                if let Some(&type_id) = self.agent_type_ids.get(agent_name) {
588                    self.code.push(0x30); // TSPAWN
589                    self.code.extend_from_slice(&type_id.to_le_bytes());
590                } else {
591                    self.code.push(0x01); self.code.extend(pack_trits(&[Trit::Tend]));
592                }
593            }
594            Expr::Await { target } => {
595                self.emit_expr(target);
596                self.code.push(0x32); // TAWAIT
597            }
598            Expr::TritTensorLiteral(vs) => {
599                let rows = vs.len();
600                let cols = 1;
601                self.code.push(0x0f);
602                self.code.extend_from_slice(&(rows as u16).to_le_bytes());
603                self.code.extend_from_slice(&(cols as u16).to_le_bytes());
604                let tr = self.next_reg; self.next_reg += 1;
605                self.code.push(0x08); self.code.push(tr);
606                for (idx, &v) in vs.iter().enumerate() {
607                    self.code.push(0x09); self.code.push(tr);
608                    self.code.push(0x17); self.code.extend_from_slice(&(idx as i64).to_le_bytes());
609                    self.code.push(0x17); self.code.extend_from_slice(&0i64.to_le_bytes());
610                    self.code.push(0x01); self.code.extend(pack_trits(&[Trit::from(v)]));
611                    self.code.push(0x23);
612                }
613                self.code.push(0x09); self.code.push(tr);
614            }
615            Expr::StructLiteral { .. } => {
616                // Struct literals are largely handled by Stmt::Let by flattening.
617                // We push a dummy hold so Return(struct_lit) or Let works consistently.
618                self.code.push(0x01); self.code.extend(pack_trits(&[Trit::Tend]));
619            }
620            Expr::Propagate { expr } => {
621                self.emit_expr(expr);
622                self.code.push(0x0a); // TDUP
623                let patch = self.code.len() + 1;
624                self.code.push(0x07); self.code.extend_from_slice(&[0, 0]); // TJMP_NEG
625                let skip = self.code.len() + 1;
626                self.code.push(0x0b); self.code.extend_from_slice(&[0, 0]); // TJMP
627                let early_ret = self.code.len() as u16;
628                self.patch_u16(patch, early_ret);
629                self.code.push(0x11); // TRET
630                let next = self.code.len() as u16;
631                self.patch_u16(skip, next);
632            }
633            Expr::Index { object, row, col } => {
634                self.emit_expr(object); self.emit_expr(row); self.emit_expr(col);
635                self.code.push(0x22);
636            }
637            Expr::FieldAccess { object, field } => {
638                if let Expr::Ident(obj_name) = object.as_ref() {
639                    let key = format!("{}.{}", obj_name, field);
640                    if let Some(&r) = self.symbols.get(&key) {
641                        self.code.push(0x09); self.code.push(r); // TLOAD
642                    }
643                }
644            }
645            Expr::Cast { expr, .. } => {
646                // cast() is a type annotation hint only — pass inner expression through
647                self.emit_expr(expr);
648            }
649            Expr::NodeId => {
650                let bytes = b"127.0.0.1:7373";
651                self.code.push(0x21);
652                self.code.extend_from_slice(&(bytes.len() as u16).to_le_bytes());
653                self.code.extend_from_slice(bytes);
654            }
655        }
656    }
657
658    pub fn emit_entry_call(&mut self, name: &str) {
659        if let Some(&addr) = self.func_addrs.get(name) {
660            self.code.push(0x10); self.code.extend_from_slice(&addr.to_le_bytes());
661        }
662    }
663
664    pub fn get_agent_handlers(&self) -> Vec<(u16, usize)> {
665        self.agent_handlers.iter().map(|&(id, addr)| (id, addr as usize)).collect()
666    }
667
668    pub fn finalize(&mut self) -> Vec<u8> { std::mem::take(&mut self.code) }
669
670    fn patch_u16(&mut self, pos: usize, val: u16) {
671        let b = val.to_le_bytes();
672        self.code[pos] = b[0]; self.code[pos + 1] = b[1];
673    }
674}