1use crate::CodegenBackend;
2use ling_ast::ast::BinOp;
3use ling_ast::ast::UnOp;
4use ling_mir::ir::*;
5use std::collections::HashMap;
6
7const OP_NOP: u8 = 0;
10const OP_PUSHI64: u8 = 1;
11const OP_PUSHF64: u8 = 2;
12const OP_PUSHSTR: u8 = 3;
13const OP_PUSHBOOL: u8 = 4;
14const OP_PUSHNONE: u8 = 5;
15const OP_POP: u8 = 6;
16const OP_LOADLOCAL: u8 = 7;
17const OP_STORELOCAL: u8 = 8;
18const OP_ADD: u8 = 9;
19const OP_SUB: u8 = 10;
20const OP_MUL: u8 = 11;
21const OP_DIV: u8 = 12;
22const OP_REM: u8 = 13;
23const OP_EQ: u8 = 14;
24const OP_NE: u8 = 15;
25const OP_LT: u8 = 16;
26const OP_LE: u8 = 17;
27const OP_GT: u8 = 18;
28const OP_GE: u8 = 19;
29const OP_AND: u8 = 20;
30const OP_OR: u8 = 21;
31const OP_NOT: u8 = 22;
32const OP_NEG: u8 = 23;
33const OP_CALL: u8 = 24;
34const OP_CALLBUILTIN: u8 = 25;
35const OP_RET: u8 = 26;
36const OP_JUMP: u8 = 27;
37const OP_JUMPIFFALSE: u8 = 28;
38const OP_MAKELIST: u8 = 29;
39const OP_GETINDEX: u8 = 30;
40const OP_HALT: u8 = 0xFF;
41
42#[derive(Debug, Clone)]
45pub enum Value {
46 Number(f64),
47 Bool(bool),
48 Str(String),
49 None,
50 List(Vec<Value>),
51}
52
53impl Value {
54 fn is_truthy(&self) -> bool {
55 match self {
56 Value::Bool(b) => *b,
57 Value::Number(n) => *n != 0.0,
58 Value::Str(s) => !s.is_empty(),
59 Value::List(l) => !l.is_empty(),
60 Value::None => false,
61 }
62 }
63
64 fn display(&self) -> String {
65 match self {
66 Value::Number(n) => {
67 if n.fract() == 0.0 && n.is_finite() && n.abs() < 1e15 {
68 format!("{:.0}", n)
69 } else {
70 format!("{}", n)
71 }
72 }
73 Value::Bool(b) => b.to_string(),
74 Value::Str(s) => s.clone(),
75 Value::None => "()".to_string(),
76 Value::List(l) => {
77 let items: Vec<String> = l.iter().map(|v| v.display()).collect();
78 format!("[{}]", items.join(", "))
79 }
80 }
81 }
82}
83
84fn values_equal(a: &Value, b: &Value) -> bool {
85 match (a, b) {
86 (Value::Number(a), Value::Number(b)) => a.to_bits() == b.to_bits(),
87 (Value::Bool(a), Value::Bool(b)) => a == b,
88 (Value::Str(a), Value::Str(b)) => a == b,
89 (Value::None, Value::None) => true,
90 (Value::List(a), Value::List(b)) => {
91 a.len() == b.len() && a.iter().zip(b).all(|(x, y)| values_equal(x, y))
92 }
93 _ => false,
94 }
95}
96
97fn num_cmp<F: Fn(f64, f64) -> bool>(a: &Value, b: &Value, cmp: F) -> bool {
98 match (a, b) {
99 (Value::Number(a), Value::Number(b)) => cmp(*a, *b),
100 _ => false,
101 }
102}
103
104fn resolve_builtin(name: &str) -> Result<(&'static str, usize), String> {
105 match name {
106 "print" | "println" | "พิมพ์" | "印" | "打印" | "印刷" => Ok(("print", 1)),
107 "format" | "รูปแบบ" | "格式" | "フォーマット" | "서식" => Ok(("format", 0)),
108 "len" | "str_len" | "ความยาว" | "长度" | "長さ" | "길이" => Ok(("len", 1)),
109 "to_str" | "str" | "แปลงสตริง" => Ok(("to_str", 1)),
110 "sin" => Ok(("sin", 1)),
111 "cos" => Ok(("cos", 1)),
112 _ => Err(format!("unknown builtin: {}", name)),
113 }
114}
115
116#[derive(Debug, Default)]
119pub struct Chunk {
120 pub code: Vec<u8>,
121 pub floats: Vec<f64>,
122 pub strings: Vec<String>,
123 pub local_count: u16,
124}
125
126impl Chunk {
127 fn w(&mut self, b: u8) { self.code.push(b); }
128 fn w2(&mut self, v: u16) { self.code.extend_from_slice(&v.to_le_bytes()); }
129 fn w8(&mut self, v: i64) { self.code.extend_from_slice(&v.to_le_bytes()); }
130 fn add_float(&mut self, v: f64) -> u16 { let i = self.floats.len(); self.floats.push(v); i as u16 }
131 fn add_str(&mut self, s: &str) -> u16 { let i = self.strings.len(); self.strings.push(s.to_string()); i as u16 }
132}
133
134#[derive(Debug)]
137pub struct CompiledFunction {
138 pub name: String,
139 pub chunk: Chunk,
140 pub arg_count: u16,
141}
142
143#[derive(Debug)]
144pub struct VmProgram {
145 pub functions: Vec<CompiledFunction>,
146 pub main_index: usize,
147}
148
149struct Ctx {
152 chunk: Chunk,
153 local_map: HashMap<Local, u16>,
154 local_next: u16,
155 bb_start: HashMap<usize, usize>,
156 patches: Vec<(usize, usize)>,
157}
158
159pub fn compile_mir_program(mir: &MirProgram) -> VmProgram {
160 let fn_names: Vec<String> = mir.functions.iter().map(|f| f.name.clone()).collect();
161 let mut functions = Vec::new();
162 let mut main_index = 0;
163
164 for (i, mir_fn) in mir.functions.iter().enumerate() {
165 let chunk = compile_fn(mir_fn, &fn_names);
166 if mir_fn.name == "__main__" || mir_fn.name == "start" || mir_fn.name == "เริ่ม" {
167 main_index = i;
168 }
169 functions.push(CompiledFunction {
170 name: mir_fn.name.clone(),
171 chunk,
172 arg_count: mir_fn.arg_count as u16,
173 });
174 }
175
176 if functions.is_empty() {
177 functions.push(CompiledFunction {
178 name: "__main__".into(),
179 chunk: Chunk { code: vec![OP_PUSHNONE, OP_RET], ..Default::default() },
180 arg_count: 0,
181 });
182 }
183
184 VmProgram { functions, main_index }
185}
186
187fn compile_fn(mir_fn: &MirFunction, fn_names: &[String]) -> Chunk {
188 let mut ctx = Ctx {
189 chunk: Chunk::default(),
190 local_map: HashMap::new(),
191 local_next: 0,
192 bb_start: HashMap::new(),
193 patches: Vec::new(),
194 };
195
196 ctx.local_map.insert(Local(0), 0);
198
199 for i in 0..mir_fn.arg_count {
201 let local = Local(i + 1);
202 ctx.local_map.entry(local).or_insert_with(|| {
203 let s = ctx.local_next; ctx.local_next += 1; s
204 });
205 }
206
207 for li in 0..mir_fn.locals.len() {
209 let local = Local(mir_fn.arg_count + 1 + li);
210 ctx.local_map.entry(local).or_insert_with(|| {
211 let s = ctx.local_next; ctx.local_next += 1; s
212 });
213 }
214
215 ctx.chunk.local_count = ctx.local_next;
216
217 for bb_idx in 0..mir_fn.basic_blocks.len() {
218 ctx.bb_start.insert(bb_idx, ctx.chunk.code.len());
219 let bb = &mir_fn.basic_blocks[bb_idx];
220 for stmt in &bb.statements {
221 compile_stmt(stmt, fn_names, &mut ctx);
222 }
223 if let Some(term) = &bb.terminator {
224 compile_term(term, fn_names, &mut ctx);
225 }
226 }
227
228 for &(pos, target) in &ctx.patches {
229 let target_offset = ctx.bb_start.get(&target).copied().unwrap_or(0);
230 ctx.chunk.code[pos..pos + 2].copy_from_slice(&(target_offset as u16).to_le_bytes());
231 }
232
233 ctx.chunk
234}
235
236fn compile_stmt(stmt: &Statement, fn_names: &[String], ctx: &mut Ctx) {
237 match &stmt.kind {
238 StatementKind::Assign(local, rvalue) => {
239 compile_rval(rvalue, fn_names, ctx);
240 let slot = *ctx.local_map.entry(*local).or_insert_with(|| {
241 let s = ctx.local_next; ctx.local_next += 1; s
242 });
243 ctx.chunk.w(OP_STORELOCAL);
244 ctx.chunk.w2(slot);
245 }
246 _ => {}
247 }
248}
249
250fn compile_rval(rv: &Rvalue, fn_names: &[String], ctx: &mut Ctx) {
251 match rv {
252 Rvalue::Use(op) => compile_op(op, fn_names, ctx),
253 Rvalue::BinaryOp(op, l, r) => {
254 compile_op(l, fn_names, ctx);
255 compile_op(r, fn_names, ctx);
256 ctx.chunk.w(match op {
257 BinOp::Add => OP_ADD, BinOp::Sub => OP_SUB,
258 BinOp::Mul => OP_MUL, BinOp::Div => OP_DIV,
259 BinOp::Rem => OP_REM, BinOp::Eq => OP_EQ,
260 BinOp::Ne => OP_NE, BinOp::Lt => OP_LT,
261 BinOp::Le => OP_LE, BinOp::Gt => OP_GT,
262 BinOp::Ge => OP_GE, BinOp::And => OP_AND,
263 BinOp::Or => OP_OR,
264 _ => OP_NOP, });
266 }
267 Rvalue::UnaryOp(op, o) => {
268 compile_op(o, fn_names, ctx);
269 ctx.chunk.w(match op { UnOp::Not => OP_NOT, UnOp::Neg => OP_NEG, _ => OP_NOP });
270 }
271 Rvalue::Call { func, args } => {
272 for arg in args {
273 compile_op(arg, fn_names, ctx);
274 }
275 match func {
276 Operand::Constant(Constant::Function(name)) => {
277 if let Some(idx) = fn_names.iter().position(|n| n == name) {
278 ctx.chunk.w(OP_CALL);
279 ctx.chunk.w2(idx as u16);
280 } else {
281 let sidx = ctx.chunk.add_str(name);
282 ctx.chunk.w(OP_CALLBUILTIN);
283 ctx.chunk.w2(sidx);
284 ctx.chunk.w2(args.len() as u16);
285 }
286 }
287 _ => {
288 compile_op(func, fn_names, ctx);
289 let sidx = ctx.chunk.add_str("__indirect");
290 ctx.chunk.w(OP_CALLBUILTIN);
291 ctx.chunk.w2(sidx);
292 ctx.chunk.w2(args.len() as u16);
293 }
294 }
295 }
296 Rvalue::Aggregate(_kind, ops) => {
297 for op in ops {
298 compile_op(op, fn_names, ctx);
299 }
300 ctx.chunk.w(OP_MAKELIST);
301 ctx.chunk.w2(ops.len() as u16);
302 }
303 Rvalue::GetIndex(obj, idx) => {
304 compile_op(obj, fn_names, ctx);
305 compile_op(idx, fn_names, ctx);
306 ctx.chunk.w(OP_GETINDEX);
307 }
308 _ => { ctx.chunk.w(OP_PUSHNONE); }
309 }
310}
311
312fn compile_op(op: &Operand, _fn_names: &[String], ctx: &mut Ctx) {
313 match op {
314 Operand::Copy(l) | Operand::Move(l) => {
315 let slot = ctx.local_map.get(l).copied().unwrap_or(0);
316 ctx.chunk.w(OP_LOADLOCAL);
317 ctx.chunk.w2(slot);
318 }
319 Operand::Constant(c) => match c {
320 Constant::I64(v) => {
321 ctx.chunk.w(OP_PUSHI64);
322 ctx.chunk.w8(*v);
323 }
324 Constant::F64(bits) => {
325 let idx = ctx.chunk.add_float(f64::from_bits(*bits));
326 ctx.chunk.w(OP_PUSHF64);
327 ctx.chunk.w2(idx);
328 }
329 Constant::Str(s) => {
330 let idx = ctx.chunk.add_str(s);
331 ctx.chunk.w(OP_PUSHSTR);
332 ctx.chunk.w2(idx);
333 }
334 Constant::Bool(b) => {
335 ctx.chunk.w(OP_PUSHBOOL);
336 ctx.chunk.w(if *b { 1 } else { 0 });
337 }
338 _ => { ctx.chunk.w(OP_PUSHNONE); }
339 },
340 }
341}
342
343fn compile_term(term: &Terminator, fn_names: &[String], ctx: &mut Ctx) {
344 match &term.kind {
345 TerminatorKind::Return => {
346 ctx.chunk.w(OP_LOADLOCAL);
347 ctx.chunk.w2(0);
348 ctx.chunk.w(OP_RET);
349 }
350 TerminatorKind::Goto { target } => {
351 ctx.chunk.w(OP_JUMP);
352 let pos = ctx.chunk.code.len();
353 ctx.chunk.w2(0);
354 ctx.patches.push((pos, target.0));
355 }
356 TerminatorKind::SwitchInt { discr, targets, otherwise } => {
357 compile_op(discr, fn_names, ctx);
358 if let Some((val, target)) = targets.first() {
359 ctx.chunk.w(OP_PUSHI64);
360 ctx.chunk.w8(*val);
361 ctx.chunk.w(OP_EQ);
362 ctx.chunk.w(OP_JUMPIFFALSE);
363 let pos = ctx.chunk.code.len();
364 ctx.chunk.w2(0);
365 ctx.patches.push((pos, target.0));
366 }
367 ctx.chunk.w(OP_JUMP);
368 let pos = ctx.chunk.code.len();
369 ctx.chunk.w2(0);
370 ctx.patches.push((pos, otherwise.0));
371 }
372 TerminatorKind::Unreachable => { ctx.chunk.w(OP_HALT); }
373 }
374}
375
376fn read_u8(code: &[u8], ip: usize) -> u8 {
377 code[ip]
378}
379
380fn read_u16(code: &[u8], ip: usize) -> u16 {
381 let mut bytes = [0u8; 2];
382 bytes.copy_from_slice(&code[ip..ip + 2]);
383 u16::from_le_bytes(bytes)
384}
385
386fn read_i64(code: &[u8], ip: usize) -> i64 {
387 let mut bytes = [0u8; 8];
388 bytes.copy_from_slice(&code[ip..ip + 8]);
389 i64::from_le_bytes(bytes)
390}
391
392struct Frame {
395 fn_index: usize,
396 ip: usize,
397 base: usize,
398 local_count: usize,
399}
400
401pub struct Vm {
402 stack: Vec<Value>,
403 program: Option<VmProgram>,
404 call_stack: Vec<Frame>,
405}
406
407impl Vm {
408 pub fn new() -> Self {
409 Self { stack: Vec::new(), program: None, call_stack: Vec::new() }
410 }
411
412 pub fn load(&mut self, program: VmProgram) {
413 self.program = Some(program);
414 }
415
416 pub fn run_main(&mut self) -> Result<(), String> {
417 let p = self.program.as_ref().ok_or("no program loaded")?;
418 if p.functions.is_empty() {
419 return Ok(());
420 }
421 let main_idx = p.main_index;
422 let func = &p.functions[main_idx];
423 let lc = func.chunk.local_count as usize;
424 for _ in 0..lc {
425 self.stack.push(Value::None);
426 }
427 self.call_stack.push(Frame { fn_index: main_idx, ip: 0, base: 0, local_count: lc });
428 self.exec()
429 }
430
431 fn exec(&mut self) -> Result<(), String> {
432 loop {
433 if self.call_stack.is_empty() {
434 return Ok(());
435 }
436
437 let frame_ip = self.call_stack.last().unwrap().ip;
438
439 let fn_index = self.call_stack.last().unwrap().fn_index;
441 let (code, floats, strings) = {
442 let p = self.program.as_ref().unwrap();
443 let chunk = &p.functions[fn_index].chunk;
444 (chunk.code.clone(), chunk.floats.clone(), chunk.strings.clone())
446 };
447 let code_len = code.len();
448
449 if frame_ip >= code_len {
450 self.call_stack.pop();
451 return Err("unexpected end of code".into());
452 }
453
454 let op = read_u8(&code, frame_ip);
455 let mut ip = frame_ip + 1;
456
457 match op {
458 OP_PUSHI64 => {
459 let v = read_i64(&code, ip);
460 ip += 8;
461 self.stack.push(Value::Number(v as f64));
462 }
463 OP_PUSHF64 => {
464 let idx = read_u16(&code, ip) as usize;
465 ip += 2;
466 self.stack.push(Value::Number(floats[idx]));
467 }
468 OP_PUSHSTR => {
469 let idx = read_u16(&code, ip) as usize;
470 ip += 2;
471 self.stack.push(Value::Str(strings[idx].clone()));
472 }
473 OP_PUSHBOOL => {
474 let b = read_u8(&code, ip) != 0;
475 ip += 1;
476 self.stack.push(Value::Bool(b));
477 }
478 OP_PUSHNONE => self.stack.push(Value::None),
479 OP_POP => { self.stack.pop(); }
480 OP_LOADLOCAL => {
481 let idx = read_u16(&code, ip) as usize;
482 ip += 2;
483 let base = self.call_stack.last().unwrap().base;
484 self.stack.push(self.stack[base + idx].clone());
485 }
486 OP_STORELOCAL => {
487 let idx = read_u16(&code, ip) as usize;
488 ip += 2;
489 let val = self.stack.pop().unwrap();
490 let base = self.call_stack.last().unwrap().base;
491 self.stack[base + idx] = val;
492 }
493 OP_ADD | OP_SUB | OP_MUL | OP_DIV | OP_REM => {
494 let b = self.stack.pop().unwrap();
495 let a = self.stack.pop().unwrap();
496 let r = match (op, &a, &b) {
497 (OP_ADD, Value::Str(s), v) => Value::Str(format!("{}{}", s, v.display())),
498 (OP_ADD, v, Value::Str(s)) => Value::Str(format!("{}{}", v.display(), s)),
499 (OP_ADD, Value::Number(a), Value::Number(b)) => Value::Number(a + b),
500 (OP_SUB, Value::Number(a), Value::Number(b)) => Value::Number(a - b),
501 (OP_MUL, Value::Number(a), Value::Number(b)) => Value::Number(a * b),
502 (OP_DIV, Value::Number(a), Value::Number(b)) => Value::Number(a / b),
503 (OP_REM, Value::Number(a), Value::Number(b)) => Value::Number(a % b),
504 _ => return Err("type error in arithmetic".into()),
505 };
506 self.stack.push(r);
507 }
508 OP_EQ | OP_NE | OP_LT | OP_LE | OP_GT | OP_GE => {
509 let b = self.stack.pop().unwrap();
510 let a = self.stack.pop().unwrap();
511 let r = match op {
512 OP_EQ => values_equal(&a, &b),
513 OP_NE => !values_equal(&a, &b),
514 OP_LT => num_cmp(&a, &b, |a, b| a < b),
515 OP_LE => num_cmp(&a, &b, |a, b| a <= b),
516 OP_GT => num_cmp(&a, &b, |a, b| a > b),
517 OP_GE => num_cmp(&a, &b, |a, b| a >= b),
518 _ => false,
519 };
520 self.stack.push(Value::Bool(r));
521 }
522 OP_AND => {
523 let b = self.stack.pop().unwrap();
524 let a = self.stack.pop().unwrap();
525 self.stack.push(Value::Bool(a.is_truthy() && b.is_truthy()));
526 }
527 OP_OR => {
528 let b = self.stack.pop().unwrap();
529 let a = self.stack.pop().unwrap();
530 self.stack.push(Value::Bool(a.is_truthy() || b.is_truthy()));
531 }
532 OP_NOT => {
533 let v = self.stack.pop().unwrap();
534 self.stack.push(Value::Bool(!v.is_truthy()));
535 }
536 OP_NEG => {
537 let v = self.stack.pop().unwrap();
538 if let Value::Number(n) = v { self.stack.push(Value::Number(-n)); }
539 else { return Err("type error: negate non-number".into()); }
540 }
541 OP_CALL => {
542 let idx = read_u16(&code, ip) as usize;
543 ip += 2;
544 let (ac, lc) = {
545 let p = self.program.as_ref().unwrap();
546 (p.functions[idx].arg_count as usize, p.functions[idx].chunk.local_count as usize)
547 };
548 let extra = lc.saturating_sub(ac);
549 for _ in 0..extra { self.stack.push(Value::None); }
550 let new_base = self.stack.len() - lc;
551 self.call_stack.push(Frame { fn_index: idx, ip: 0, base: new_base, local_count: lc });
552 }
553 OP_CALLBUILTIN => {
554 let si = read_u16(&code, ip) as usize;
555 ip += 2;
556 let count = read_u16(&code, ip) as usize;
557 ip += 2;
558 let name = strings[si].clone();
559 let (canon, _) = resolve_builtin(&name).map_err(|e| format!("{} in {}", e, name))?;
560 self.call_builtin(canon, count)?;
561 }
562 OP_RET => {
563 let val = self.stack.pop().unwrap_or(Value::None);
564 let _frame = self.call_stack.pop().unwrap();
565 if let Some(caller) = self.call_stack.last() {
566 self.stack.truncate(caller.base + caller.local_count);
567 self.stack.push(val);
568 } else {
569 self.stack.push(val);
570 return Ok(());
571 }
572 }
573 OP_JUMP => {
574 ip = read_u16(&code, ip) as usize;
575 }
576 OP_JUMPIFFALSE => {
577 let offset = read_u16(&code, ip) as usize;
578 ip += 2;
579 let cond = self.stack.pop().unwrap();
580 if !cond.is_truthy() { ip = offset; }
581 }
582 OP_MAKELIST => {
583 let count = read_u16(&code, ip) as usize;
584 ip += 2;
585 let mut items = Vec::with_capacity(count);
586 for _ in 0..count { items.push(self.stack.pop().unwrap()); }
587 items.reverse();
588 self.stack.push(Value::List(items));
589 }
590 OP_GETINDEX => {
591 let idx_v = self.stack.pop().unwrap();
592 let obj = self.stack.pop().unwrap();
593 match (&obj, &idx_v) {
594 (Value::List(list), Value::Number(n)) => {
595 let i = *n as usize;
596 self.stack.push(if i < list.len() { list[i].clone() } else { Value::None });
597 }
598 _ => self.stack.push(Value::None),
599 }
600 }
601 OP_HALT => return Ok(()),
602 _ => return Err(format!("unknown opcode {} at ip {}", op, ip - 1)),
603 }
604
605 self.call_stack.last_mut().unwrap().ip = ip;
606 }
607 }
608
609 fn call_builtin(&mut self, name: &str, arg_count: usize) -> Result<(), String> {
610 match name {
611 "print" => {
612 if let Some(val) = self.stack.pop() {
613 println!("{}", val.display());
614 }
615 self.stack.push(Value::None);
616 }
617 "format" => {
618 let mut parts: Vec<String> = Vec::with_capacity(arg_count);
619 for _ in 0..arg_count {
620 if let Some(val) = self.stack.pop() {
621 parts.push(val.display());
622 }
623 }
624 parts.reverse();
625 if parts.is_empty() {
626 self.stack.push(Value::Str(String::new()));
627 return Ok(());
628 }
629 let fmt_str = parts.remove(0);
630 let mut result = fmt_str;
631 for part in parts {
632 result = result.replacen("{}", &part, 1);
633 }
634 self.stack.push(Value::Str(result));
635 }
636 "len" => {
637 let val = self.stack.pop().unwrap_or(Value::None);
638 let n = match &val {
639 Value::Str(s) => s.len() as f64,
640 Value::List(l) => l.len() as f64,
641 _ => 0.0,
642 };
643 self.stack.push(Value::Number(n));
644 }
645 "to_str" => {
646 let val = self.stack.pop().unwrap_or(Value::None);
647 self.stack.push(Value::Str(val.display()));
648 }
649 "sin" => {
650 let val = self.stack.pop().unwrap_or(Value::None);
651 if let Value::Number(n) = val {
652 self.stack.push(Value::Number(n.sin()));
653 } else {
654 self.stack.push(Value::Number(0.0));
655 }
656 }
657 "cos" => {
658 let val = self.stack.pop().unwrap_or(Value::None);
659 if let Value::Number(n) = val {
660 self.stack.push(Value::Number(n.cos()));
661 } else {
662 self.stack.push(Value::Number(0.0));
663 }
664 }
665 _ => return Err(format!("unimplemented builtin: {}", name)),
666 }
667 Ok(())
668 }
669}
670
671pub struct BytecodeBackend;
674
675impl CodegenBackend for BytecodeBackend {
676 fn emit(&mut self, program: &crate::MirProgram, out: &std::path::Path) -> anyhow::Result<()> {
677 let vm_prog = compile_mir_program(&program.mir);
678 let mut bytes = Vec::new();
679 bytes.extend_from_slice(b"LINGBC");
680 let fn_count = vm_prog.functions.len() as u32;
681 bytes.extend_from_slice(&fn_count.to_le_bytes());
682 for func in &vm_prog.functions {
683 let name_bytes = func.name.as_bytes();
684 bytes.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
685 bytes.extend_from_slice(name_bytes);
686 bytes.extend_from_slice(&func.arg_count.to_le_bytes());
687 bytes.extend_from_slice(&func.chunk.local_count.to_le_bytes());
688 bytes.extend_from_slice(&(func.chunk.code.len() as u32).to_le_bytes());
689 bytes.extend_from_slice(&func.chunk.code);
690 bytes.extend_from_slice(&(func.chunk.floats.len() as u32).to_le_bytes());
691 for &f in &func.chunk.floats {
692 bytes.extend_from_slice(&f.to_bits().to_le_bytes());
693 }
694 bytes.extend_from_slice(&(func.chunk.strings.len() as u32).to_le_bytes());
695 for s in &func.chunk.strings {
696 let sb = s.as_bytes();
697 bytes.extend_from_slice(&(sb.len() as u32).to_le_bytes());
698 bytes.extend_from_slice(sb);
699 }
700 }
701 std::fs::write(out, &bytes)?;
702 Ok(())
703 }
704}