Skip to main content

aver/vm/compiler/
mod.rs

1mod calls;
2mod classify;
3mod expr;
4mod patterns;
5
6use std::collections::{HashMap, HashSet};
7
8use crate::ast::{FnBody, FnDef, Stmt, TopLevel, TypeDef};
9use crate::nan_value::{Arena, NanValue};
10use crate::source::find_module_file;
11use crate::types::{option, result};
12
13use super::builtin::VmBuiltin;
14use super::opcode::*;
15use super::symbol::{VmSymbolTable, VmVariantCtor};
16use super::types::{CodeStore, FnChunk};
17
18/// Compile a parsed + TCO-transformed + resolved program into bytecode.
19/// Also loads dependent modules if a `module` declaration with `depends` is present.
20pub fn compile_program(
21    items: &[TopLevel],
22    arena: &mut Arena,
23) -> Result<(CodeStore, Vec<NanValue>), CompileError> {
24    compile_program_with_modules(items, arena, None)
25}
26
27/// Compile with explicit module root for `depends` resolution.
28pub fn compile_program_with_modules(
29    items: &[TopLevel],
30    arena: &mut Arena,
31    module_root: Option<&str>,
32) -> Result<(CodeStore, Vec<NanValue>), CompileError> {
33    let mut compiler = ProgramCompiler::new();
34    compiler.sync_record_field_symbols(arena);
35
36    if let Some(module_root) = module_root {
37        compiler.load_modules(items, module_root, arena)?;
38    }
39
40    for item in items {
41        match item {
42            TopLevel::FnDef(fndef) => {
43                compiler.ensure_global(&fndef.name);
44                let effect_ids: Vec<u32> = fndef
45                    .effects
46                    .iter()
47                    .map(|effect| compiler.symbols.intern_name(effect))
48                    .collect();
49                let fn_id = compiler.code.add_function(FnChunk {
50                    name: fndef.name.clone(),
51                    arity: fndef.params.len() as u8,
52                    local_count: 0,
53                    code: Vec::new(),
54                    constants: Vec::new(),
55                    effects: effect_ids,
56                    thin: false,
57                    parent_thin: false,
58                    leaf: false,
59                });
60                let symbol_id =
61                    compiler
62                        .symbols
63                        .intern_function(&fndef.name, fn_id, &fndef.effects);
64                let global_idx = compiler.global_names[&fndef.name];
65                compiler.globals[global_idx as usize] = VmSymbolTable::symbol_ref(symbol_id);
66            }
67            TopLevel::TypeDef(td) => {
68                compiler.register_type_def(td, arena);
69            }
70            _ => {}
71        }
72    }
73
74    compiler.register_current_module_namespace(items);
75
76    for item in items {
77        if let TopLevel::FnDef(fndef) = item {
78            let fn_id = compiler.code.find(&fndef.name).unwrap();
79            let chunk = compiler.compile_fn(fndef, arena)?;
80            compiler.code.functions[fn_id as usize] = chunk;
81        }
82    }
83
84    compiler.compile_top_level(items, arena)?;
85    compiler.code.symbols = compiler.symbols.clone();
86    classify::classify_thin_functions(&mut compiler.code, arena)?;
87
88    Ok((compiler.code, compiler.globals))
89}
90
91#[derive(Debug)]
92pub struct CompileError {
93    pub msg: String,
94}
95
96impl std::fmt::Display for CompileError {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        write!(f, "Compile error: {}", self.msg)
99    }
100}
101
102struct ProgramCompiler {
103    code: CodeStore,
104    symbols: VmSymbolTable,
105    globals: Vec<NanValue>,
106    global_names: HashMap<String, u16>,
107}
108
109impl ProgramCompiler {
110    fn new() -> Self {
111        let mut compiler = ProgramCompiler {
112            code: CodeStore::new(),
113            symbols: VmSymbolTable::default(),
114            globals: Vec::new(),
115            global_names: HashMap::new(),
116        };
117        compiler.bootstrap_core_symbols();
118        compiler
119    }
120
121    fn sync_record_field_symbols(&mut self, arena: &Arena) {
122        for type_id in 0..arena.type_count() {
123            let type_name = arena.get_type_name(type_id);
124            self.symbols.intern_namespace_path(type_name);
125            let field_names = arena.get_field_names(type_id);
126            if field_names.is_empty() {
127                continue;
128            }
129            let field_symbol_ids: Vec<u32> = field_names
130                .iter()
131                .map(|field_name| self.symbols.intern_name(field_name))
132                .collect();
133            self.code.register_record_fields(type_id, &field_symbol_ids);
134        }
135    }
136
137    /// Load all modules from `depends [...]` declarations.
138    /// Compiles each module's functions into the shared CodeStore and builds
139    /// namespace globals so that `Data.Fibonacci.buildFibStats` resolves to
140    /// a CALL_KNOWN with the correct fn_id.
141    fn load_modules(
142        &mut self,
143        items: &[TopLevel],
144        module_root: &str,
145        arena: &mut Arena,
146    ) -> Result<(), CompileError> {
147        let module = items.iter().find_map(|i| {
148            if let TopLevel::Module(m) = i {
149                Some(m)
150            } else {
151                None
152            }
153        });
154        let module = match module {
155            Some(m) => m,
156            None => return Ok(()),
157        };
158
159        let mut loaded = HashSet::new();
160        for dep_name in &module.depends {
161            self.load_module_recursive(dep_name, module_root, arena, &mut loaded)?;
162        }
163        Ok(())
164    }
165
166    fn load_module_recursive(
167        &mut self,
168        dep_name: &str,
169        module_root: &str,
170        arena: &mut Arena,
171        loaded: &mut HashSet<String>,
172    ) -> Result<(), CompileError> {
173        if loaded.contains(dep_name) {
174            return Ok(());
175        }
176        loaded.insert(dep_name.to_string());
177
178        let file_path = find_module_file(dep_name, module_root).ok_or_else(|| CompileError {
179            msg: format!("module '{}' not found (root: {})", dep_name, module_root),
180        })?;
181
182        let source = std::fs::read_to_string(&file_path).map_err(|e| CompileError {
183            msg: format!("cannot read module '{}': {}", dep_name, e),
184        })?;
185
186        let mut mod_items = crate::source::parse_source(&source).map_err(|e| CompileError {
187            msg: format!("parse error in module '{}': {}", dep_name, e),
188        })?;
189
190        crate::tco::transform_program(&mut mod_items);
191        crate::resolver::resolve_program(&mut mod_items);
192
193        if let Some(sub_module) = mod_items.iter().find_map(|i| {
194            if let TopLevel::Module(m) = i {
195                Some(m)
196            } else {
197                None
198            }
199        }) {
200            for sub_dep in &sub_module.depends {
201                self.load_module_recursive(sub_dep, module_root, arena, loaded)?;
202            }
203        }
204
205        for item in &mod_items {
206            if let TopLevel::TypeDef(td) = item {
207                self.register_type_def(td, arena);
208            }
209        }
210
211        let exposes: Option<Vec<String>> = mod_items.iter().find_map(|i| {
212            if let TopLevel::Module(m) = i {
213                if m.exposes.is_empty() {
214                    None
215                } else {
216                    Some(m.exposes.clone())
217                }
218            } else {
219                None
220            }
221        });
222
223        let mut module_fn_ids: Vec<(String, u32)> = Vec::new();
224        for item in &mod_items {
225            if let TopLevel::FnDef(fndef) = item {
226                let qualified_name = format!("{}.{}", dep_name, fndef.name);
227                let effect_ids: Vec<u32> = fndef
228                    .effects
229                    .iter()
230                    .map(|effect| self.symbols.intern_name(effect))
231                    .collect();
232                let fn_id = self.code.add_function(FnChunk {
233                    name: qualified_name.clone(),
234                    arity: fndef.params.len() as u8,
235                    local_count: 0,
236                    code: Vec::new(),
237                    constants: Vec::new(),
238                    effects: effect_ids,
239                    thin: false,
240                    parent_thin: false,
241                    leaf: false,
242                });
243                self.symbols
244                    .intern_function(&qualified_name, fn_id, &fndef.effects);
245                module_fn_ids.push((fndef.name.clone(), fn_id));
246            }
247        }
248
249        let module_scope: HashMap<String, u32> = module_fn_ids.iter().cloned().collect();
250
251        let mut fn_idx = 0;
252        for item in &mod_items {
253            if let TopLevel::FnDef(fndef) = item {
254                let (_, fn_id) = module_fn_ids[fn_idx];
255                let chunk = self.compile_fn_with_scope(fndef, arena, &module_scope)?;
256                self.code.functions[fn_id as usize] = chunk;
257                fn_idx += 1;
258            }
259        }
260
261        for (fn_name, _fn_id) in &module_fn_ids {
262            let exposed = match &exposes {
263                Some(list) => list.iter().any(|e| e == fn_name),
264                None => !fn_name.starts_with('_'),
265            };
266            if exposed {
267                let qualified = format!("{}.{}", dep_name, fn_name);
268                let global_idx = self.ensure_global(&qualified);
269                let symbol_id = self.symbols.find(&qualified).ok_or_else(|| CompileError {
270                    msg: format!("missing VM symbol for exposed function {}", qualified),
271                })?;
272                self.globals[global_idx as usize] = VmSymbolTable::symbol_ref(symbol_id);
273            }
274        }
275
276        let module_symbol_id = self.symbols.intern_namespace_path(dep_name);
277        for item in &mod_items {
278            if let TopLevel::TypeDef(td) = item {
279                let type_name = match td {
280                    TypeDef::Sum { name, .. } | TypeDef::Product { name, .. } => name,
281                };
282                let exposed = match &exposes {
283                    Some(list) => list.iter().any(|e| e == type_name),
284                    None => !type_name.starts_with('_'),
285                };
286                if exposed && let Some(type_symbol_id) = self.symbols.find(type_name) {
287                    let member_symbol_id = self.symbols.intern_name(type_name);
288                    self.symbols.add_namespace_member_by_id(
289                        module_symbol_id,
290                        member_symbol_id,
291                        VmSymbolTable::symbol_ref(type_symbol_id),
292                    );
293                }
294            }
295        }
296
297        for (fn_name, _fn_id) in &module_fn_ids {
298            let exposed = match &exposes {
299                Some(list) => list.iter().any(|e| e == fn_name),
300                None => !fn_name.starts_with('_'),
301            };
302            if exposed {
303                let qualified = format!("{}.{}", dep_name, fn_name);
304                if let Some(fn_symbol_id) = self.symbols.find(&qualified) {
305                    let member_symbol_id = self.symbols.intern_name(fn_name);
306                    self.symbols.add_namespace_member_by_id(
307                        module_symbol_id,
308                        member_symbol_id,
309                        VmSymbolTable::symbol_ref(fn_symbol_id),
310                    );
311                }
312            }
313        }
314
315        Ok(())
316    }
317
318    fn ensure_global(&mut self, name: &str) -> u16 {
319        if let Some(&idx) = self.global_names.get(name) {
320            return idx;
321        }
322        let idx = self.globals.len() as u16;
323        self.global_names.insert(name.to_string(), idx);
324        self.globals.push(NanValue::UNIT);
325        idx
326    }
327
328    fn register_type_def(&mut self, td: &TypeDef, arena: &mut Arena) {
329        match td {
330            TypeDef::Product { name, fields, .. } => {
331                self.symbols.intern_namespace_path(name);
332                let field_names: Vec<String> = fields.iter().map(|(n, _)| n.clone()).collect();
333                let field_symbol_ids: Vec<u32> = field_names
334                    .iter()
335                    .map(|field_name| self.symbols.intern_name(field_name))
336                    .collect();
337                let type_id = arena.register_record_type(name, field_names);
338                self.code.register_record_fields(type_id, &field_symbol_ids);
339            }
340            TypeDef::Sum { name, variants, .. } => {
341                let type_symbol_id = self.symbols.intern_namespace_path(name);
342                let variant_names: Vec<String> = variants.iter().map(|v| v.name.clone()).collect();
343                let type_id = arena.register_sum_type(name, variant_names);
344                for (variant_id, variant) in variants.iter().enumerate() {
345                    let ctor_id = arena
346                        .find_ctor_id(type_id, variant_id as u16)
347                        .expect("ctor id");
348                    let qualified_name = format!("{}.{}", name, variant.name);
349                    let ctor_symbol_id = self.symbols.intern_variant_ctor(
350                        &qualified_name,
351                        VmVariantCtor {
352                            type_id,
353                            variant_id: variant_id as u16,
354                            ctor_id,
355                            field_count: variant.fields.len() as u8,
356                        },
357                    );
358                    let member_symbol_id = self.symbols.intern_name(&variant.name);
359                    self.symbols.add_namespace_member_by_id(
360                        type_symbol_id,
361                        member_symbol_id,
362                        VmSymbolTable::symbol_ref(ctor_symbol_id),
363                    );
364                }
365            }
366        }
367    }
368
369    fn bootstrap_core_symbols(&mut self) {
370        for builtin in VmBuiltin::ALL.iter().copied() {
371            let builtin_symbol_id = self.symbols.intern_builtin(builtin);
372            if let Some((namespace, member)) = builtin.name().split_once('.') {
373                let namespace_symbol_id = self.symbols.intern_namespace_path(namespace);
374                let member_symbol_id = self.symbols.intern_name(member);
375                self.symbols.add_namespace_member_by_id(
376                    namespace_symbol_id,
377                    member_symbol_id,
378                    VmSymbolTable::symbol_ref(builtin_symbol_id),
379                );
380            }
381        }
382
383        let result_symbol_id = self.symbols.intern_namespace_path("Result");
384        let ok_symbol_id = self.symbols.intern_wrapper("Result.Ok", 0);
385        let err_symbol_id = self.symbols.intern_wrapper("Result.Err", 1);
386        let ok_member_symbol_id = self.symbols.intern_name("Ok");
387        self.symbols.add_namespace_member_by_id(
388            result_symbol_id,
389            ok_member_symbol_id,
390            VmSymbolTable::symbol_ref(ok_symbol_id),
391        );
392        let err_member_symbol_id = self.symbols.intern_name("Err");
393        self.symbols.add_namespace_member_by_id(
394            result_symbol_id,
395            err_member_symbol_id,
396            VmSymbolTable::symbol_ref(err_symbol_id),
397        );
398        for (member, builtin_name) in result::extra_members() {
399            if let Some(symbol_id) = self.symbols.find(&builtin_name) {
400                let member_symbol_id = self.symbols.intern_name(member);
401                self.symbols.add_namespace_member_by_id(
402                    result_symbol_id,
403                    member_symbol_id,
404                    VmSymbolTable::symbol_ref(symbol_id),
405                );
406            }
407        }
408
409        let option_symbol_id = self.symbols.intern_namespace_path("Option");
410        let some_symbol_id = self.symbols.intern_wrapper("Option.Some", 2);
411        self.symbols.intern_constant("Option.None", NanValue::NONE);
412        let some_member_symbol_id = self.symbols.intern_name("Some");
413        self.symbols.add_namespace_member_by_id(
414            option_symbol_id,
415            some_member_symbol_id,
416            VmSymbolTable::symbol_ref(some_symbol_id),
417        );
418        let none_member_symbol_id = self.symbols.intern_name("None");
419        self.symbols.add_namespace_member_by_id(
420            option_symbol_id,
421            none_member_symbol_id,
422            NanValue::NONE,
423        );
424        for (member, builtin_name) in option::extra_members() {
425            if let Some(symbol_id) = self.symbols.find(&builtin_name) {
426                let member_symbol_id = self.symbols.intern_name(member);
427                self.symbols.add_namespace_member_by_id(
428                    option_symbol_id,
429                    member_symbol_id,
430                    VmSymbolTable::symbol_ref(symbol_id),
431                );
432            }
433        }
434    }
435
436    fn compile_fn(&mut self, fndef: &FnDef, arena: &mut Arena) -> Result<FnChunk, CompileError> {
437        let empty_scope = HashMap::new();
438        self.compile_fn_with_scope(fndef, arena, &empty_scope)
439    }
440
441    fn compile_fn_with_scope(
442        &mut self,
443        fndef: &FnDef,
444        arena: &mut Arena,
445        module_scope: &HashMap<String, u32>,
446    ) -> Result<FnChunk, CompileError> {
447        let resolution = fndef.resolution.as_ref();
448        let local_count = resolution.map_or(fndef.params.len() as u16, |r| r.local_count);
449        let local_slots: HashMap<String, u16> = resolution
450            .map(|r| r.local_slots.as_ref().clone())
451            .unwrap_or_else(|| {
452                fndef
453                    .params
454                    .iter()
455                    .enumerate()
456                    .map(|(i, (name, _))| (name.clone(), i as u16))
457                    .collect()
458            });
459
460        let mut fc = FnCompiler::new(
461            &fndef.name,
462            fndef.params.len() as u8,
463            local_count,
464            fndef
465                .effects
466                .iter()
467                .map(|effect| self.symbols.intern_name(effect))
468                .collect(),
469            local_slots,
470            &self.global_names,
471            module_scope,
472            &self.code,
473            &mut self.symbols,
474            arena,
475        );
476
477        match fndef.body.as_ref() {
478            FnBody::Block(stmts) => fc.compile_body(stmts)?,
479        }
480
481        Ok(fc.finish())
482    }
483
484    fn compile_top_level(
485        &mut self,
486        items: &[TopLevel],
487        arena: &mut Arena,
488    ) -> Result<(), CompileError> {
489        let has_stmts = items.iter().any(|i| matches!(i, TopLevel::Stmt(_)));
490        if !has_stmts {
491            return Ok(());
492        }
493
494        for item in items {
495            if let TopLevel::Stmt(Stmt::Binding(name, _, _)) = item {
496                self.ensure_global(name);
497            }
498        }
499
500        let empty_mod_scope = HashMap::new();
501        let mut fc = FnCompiler::new(
502            "__top_level__",
503            0,
504            0,
505            Vec::new(),
506            HashMap::new(),
507            &self.global_names,
508            &empty_mod_scope,
509            &self.code,
510            &mut self.symbols,
511            arena,
512        );
513
514        for item in items {
515            if let TopLevel::Stmt(stmt) = item {
516                match stmt {
517                    Stmt::Binding(name, _type_ann, expr) => {
518                        fc.compile_expr(expr)?;
519                        let idx = self.global_names[name.as_str()];
520                        fc.emit_op(STORE_GLOBAL);
521                        fc.emit_u16(idx);
522                    }
523                    Stmt::Expr(expr) => {
524                        fc.compile_expr(expr)?;
525                        fc.emit_op(POP);
526                    }
527                }
528            }
529        }
530
531        fc.emit_op(LOAD_UNIT);
532        fc.emit_op(RETURN);
533
534        let chunk = fc.finish();
535        self.code.add_function(chunk);
536        Ok(())
537    }
538
539    fn register_current_module_namespace(&mut self, items: &[TopLevel]) {
540        let Some(module) = items.iter().find_map(|item| {
541            if let TopLevel::Module(module) = item {
542                Some(module)
543            } else {
544                None
545            }
546        }) else {
547            return;
548        };
549
550        let module_symbol_id = self.symbols.intern_namespace_path(&module.name);
551
552        for item in items {
553            match item {
554                TopLevel::FnDef(fndef) => {
555                    let exposed = if module.exposes.is_empty() {
556                        !fndef.name.starts_with('_')
557                    } else {
558                        module.exposes.iter().any(|name| name == &fndef.name)
559                    };
560                    if exposed && let Some(symbol_id) = self.symbols.find(&fndef.name) {
561                        let member_symbol_id = self.symbols.intern_name(&fndef.name);
562                        self.symbols.add_namespace_member_by_id(
563                            module_symbol_id,
564                            member_symbol_id,
565                            VmSymbolTable::symbol_ref(symbol_id),
566                        );
567                    }
568                }
569                TopLevel::TypeDef(TypeDef::Product { name, .. } | TypeDef::Sum { name, .. }) => {
570                    let exposed = if module.exposes.is_empty() {
571                        !name.starts_with('_')
572                    } else {
573                        module.exposes.iter().any(|member| member == name)
574                    };
575                    if exposed && let Some(symbol_id) = self.symbols.find(name) {
576                        let member_symbol_id = self.symbols.intern_name(name);
577                        self.symbols.add_namespace_member_by_id(
578                            module_symbol_id,
579                            member_symbol_id,
580                            VmSymbolTable::symbol_ref(symbol_id),
581                        );
582                    }
583                }
584                _ => {}
585            }
586        }
587    }
588}
589
590/// What a function expression resolves to at compile time.
591enum CallTarget {
592    /// Known function id (local or qualified module function).
593    KnownFn(u32),
594    /// Result.Ok / Result.Err / Option.Some → WRAP opcode. kind: 0=Ok, 1=Err, 2=Some.
595    Wrapper(u8),
596    /// Option.None → load constant.
597    None_,
598    /// User-defined variant constructor: Shape.Circle → VARIANT_NEW (or inline nullary at runtime).
599    Variant(u32, u16),
600    /// Known VM builtin/service resolved by name and interned into the VM symbol table.
601    Builtin(VmBuiltin),
602    /// Unknown capitalized dotted path that did not resolve to a function, variant, or builtin.
603    UnknownQualified(String),
604}
605
606struct FnCompiler<'a> {
607    name: String,
608    arity: u8,
609    local_count: u16,
610    effects: Vec<u32>,
611    local_slots: HashMap<String, u16>,
612    global_names: &'a HashMap<String, u16>,
613    /// Module-local function scope: simple_name → fn_id.
614    /// Used for intra-module calls (e.g. `placeStairs` inside map.av).
615    module_scope: &'a HashMap<String, u32>,
616    code_store: &'a CodeStore,
617    symbols: &'a mut VmSymbolTable,
618    arena: &'a mut Arena,
619    code: Vec<u8>,
620    constants: Vec<NanValue>,
621    /// Byte offset of the last emitted opcode (for superinstruction fusion).
622    last_op_pos: usize,
623}
624
625impl<'a> FnCompiler<'a> {
626    #[allow(clippy::too_many_arguments)]
627    fn new(
628        name: &str,
629        arity: u8,
630        local_count: u16,
631        effects: Vec<u32>,
632        local_slots: HashMap<String, u16>,
633        global_names: &'a HashMap<String, u16>,
634        module_scope: &'a HashMap<String, u32>,
635        code_store: &'a CodeStore,
636        symbols: &'a mut VmSymbolTable,
637        arena: &'a mut Arena,
638    ) -> Self {
639        FnCompiler {
640            name: name.to_string(),
641            arity,
642            local_count,
643            effects,
644            local_slots,
645            global_names,
646            module_scope,
647            code_store,
648            symbols,
649            arena,
650            code: Vec::new(),
651            constants: Vec::new(),
652            last_op_pos: usize::MAX,
653        }
654    }
655
656    fn finish(self) -> FnChunk {
657        FnChunk {
658            name: self.name,
659            arity: self.arity,
660            local_count: self.local_count,
661            code: self.code,
662            constants: self.constants,
663            effects: self.effects,
664            thin: false,
665            parent_thin: false,
666            leaf: false,
667        }
668    }
669
670    fn emit_op(&mut self, op: u8) {
671        let prev_pos = self.last_op_pos;
672        let prev_op = if prev_pos < self.code.len() {
673            self.code[prev_pos]
674        } else {
675            0xFF
676        };
677
678        // LOAD_LOCAL + LOAD_LOCAL → LOAD_LOCAL_2
679        if op == LOAD_LOCAL && prev_op == LOAD_LOCAL && prev_pos + 2 == self.code.len() {
680            self.code[prev_pos] = LOAD_LOCAL_2;
681            // slot_a already at prev_pos+1, slot_b emitted next via emit_u8
682            return;
683        }
684        // LOAD_LOCAL + LOAD_CONST → LOAD_LOCAL_CONST
685        if op == LOAD_CONST && prev_op == LOAD_LOCAL && prev_pos + 2 == self.code.len() {
686            self.code[prev_pos] = LOAD_LOCAL_CONST;
687            // slot at prev_pos+1, const_idx (u16) emitted next via emit_u16
688            return;
689        }
690        // VECTOR_GET + LOAD_CONST(hi,lo) + UNWRAP_OR → VECTOR_GET_OR(hi,lo)
691        // Before: [..., VECTOR_GET, LOAD_CONST, hi, lo] + about to emit UNWRAP_OR
692        // After:  [..., VECTOR_GET_OR, hi, lo]
693        if op == UNWRAP_OR && self.code.len() >= 4 {
694            let len = self.code.len();
695            if self.code[len - 4] == VECTOR_GET && self.code[len - 3] == LOAD_CONST {
696                let hi = self.code[len - 2];
697                let lo = self.code[len - 1];
698                self.code[len - 4] = VECTOR_GET_OR;
699                self.code[len - 3] = hi;
700                self.code[len - 2] = lo;
701                self.code.pop(); // remove extra byte
702                self.last_op_pos = len - 4;
703                return;
704            }
705        }
706        self.last_op_pos = self.code.len();
707        self.code.push(op);
708    }
709
710    fn emit_u8(&mut self, val: u8) {
711        self.code.push(val);
712    }
713
714    fn emit_u16(&mut self, val: u16) {
715        self.code.push((val >> 8) as u8);
716        self.code.push((val & 0xFF) as u8);
717    }
718
719    fn emit_i16(&mut self, val: i16) {
720        self.emit_u16(val as u16);
721    }
722
723    fn emit_u32(&mut self, val: u32) {
724        self.code.push((val >> 24) as u8);
725        self.code.push(((val >> 16) & 0xFF) as u8);
726        self.code.push(((val >> 8) & 0xFF) as u8);
727        self.code.push((val & 0xFF) as u8);
728    }
729
730    fn emit_u64(&mut self, val: u64) {
731        self.code.extend_from_slice(&val.to_be_bytes());
732    }
733
734    fn add_constant(&mut self, val: NanValue) -> u16 {
735        for (i, c) in self.constants.iter().enumerate() {
736            if c.bits() == val.bits() {
737                return i as u16;
738            }
739        }
740        let idx = self.constants.len() as u16;
741        self.constants.push(val);
742        idx
743    }
744
745    fn offset(&self) -> usize {
746        self.code.len()
747    }
748
749    fn emit_jump(&mut self, op: u8) -> usize {
750        self.emit_op(op);
751        let patch_pos = self.code.len();
752        self.emit_i16(0);
753        patch_pos
754    }
755
756    fn patch_jump(&mut self, patch_pos: usize) {
757        let target = self.code.len();
758        let offset = (target as isize - patch_pos as isize - 2) as i16;
759        let bytes = (offset as u16).to_be_bytes();
760        self.code[patch_pos] = bytes[0];
761        self.code[patch_pos + 1] = bytes[1];
762    }
763
764    fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
765        let offset = (target as isize - patch_pos as isize - 2) as i16;
766        let bytes = (offset as u16).to_be_bytes();
767        self.code[patch_pos] = bytes[0];
768        self.code[patch_pos + 1] = bytes[1];
769    }
770
771    fn bind_top_to_local(&mut self, name: &str) {
772        if let Some(&slot) = self.local_slots.get(name) {
773            self.emit_op(STORE_LOCAL);
774            self.emit_u8(slot as u8);
775        } else {
776            self.emit_op(POP);
777        }
778    }
779
780    fn dup_and_bind_top_to_local(&mut self, name: &str) {
781        self.emit_op(DUP);
782        self.bind_top_to_local(name);
783    }
784}