fusabi_frontend/
compiler.rs

1//! Bytecode Compiler for Fusabi Mini-F#
2//!
3//! This module implements compilation from AST to bytecode chunks for the Fusabi VM.
4//! The compiler performs constant pooling, variable scoping, and generates efficient
5//! bytecode instruction sequences.
6//!
7//! # Architecture
8//!
9//! The compiler uses:
10//! - **Constant Pool**: Deduplicates literal values across the bytecode
11//! - **Local Variables**: Stack-allocated variables tracked by index
12//! - **Jump Patching**: Forward jump resolution for control flow
13//! - **Optional Type Checking**: Can run type inference before compilation
14//! - **Module System**: Supports module-aware compilation and qualified names
15//! - **Auto-Recursive Detection**: Automatically handles recursive lambdas (issue #126)
16//!
17//! # Example
18//!
19//! ```rust
20//! use fusabi_frontend::ast::{Expr, Literal, BinOp};
21//! use fusabi_frontend::compiler::{Compiler, CompileOptions};
22//!
23//! // Compile: 42 + 1
24//! let expr = Expr::BinOp {
25//!     op: BinOp::Add,
26//!     left: Box::new(Expr::Lit(Literal::Int(42))),
27//!     right: Box::new(Expr::Lit(Literal::Int(1))),
28//! };
29//!
30//! // Compile without type checking (backward compatible)
31//! let chunk = Compiler::compile(&expr).unwrap();
32//!
33//! // Compile with type checking enabled
34//! let options = CompileOptions {
35//!     enable_type_checking: true,
36//!     ..Default::default()
37//! };
38//! let chunk = Compiler::compile_with_options(&expr, options).unwrap();
39//! ```
40
41use crate::ast::{BinOp, Expr, Import, Literal, MatchArm, ModuleDef, ModuleItem, Pattern, Program};
42use crate::modules::ModuleRegistry;
43use crate::types::{Type, TypeEnv};
44use fusabi_vm::chunk::Chunk;
45use fusabi_vm::closure::Closure;
46use fusabi_vm::instruction::Instruction;
47use fusabi_vm::value::Value;
48use std::collections::HashMap;
49use std::fmt;
50use std::sync::Arc;
51
52/// Compilation errors
53#[derive(Debug, Clone, PartialEq)]
54pub enum CompileError {
55    /// Undefined variable reference
56    UndefinedVariable(String),
57    /// Too many constants in constant pool (max u16::MAX)
58    TooManyConstants,
59    /// Too many local variables (max u8::MAX)
60    TooManyLocals,
61    /// Invalid jump offset (beyond i16 range)
62    InvalidJumpOffset,
63    /// Unsupported float operations in Phase 1
64    UnsupportedFloat,
65    /// Tuple too large (max u16::MAX elements)
66    TupleTooLarge,
67    /// Type error during type checking
68    TypeError(String),
69    /// Code generation error
70    CodeGenError(String),
71    /// Module not found
72    ModuleNotFound(String),
73    /// No module context available
74    NoModuleContext,
75    /// Break statement used outside of loop
76    BreakOutsideLoop,
77    /// Continue statement used outside of loop
78    ContinueOutsideLoop,
79}
80
81impl fmt::Display for CompileError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            CompileError::UndefinedVariable(name) => {
85                write!(f, "Undefined variable: {}", name)
86            }
87            CompileError::TooManyConstants => {
88                write!(f, "Too many constants (max {})", u16::MAX)
89            }
90            CompileError::TooManyLocals => {
91                write!(f, "Too many local variables (max {})", u8::MAX)
92            }
93            CompileError::InvalidJumpOffset => {
94                write!(f, "Jump offset too large")
95            }
96            CompileError::UnsupportedFloat => {
97                write!(f, "Float operations not supported in Phase 1")
98            }
99            CompileError::TupleTooLarge => {
100                write!(f, "Tuple too large (max {} elements)", u16::MAX)
101            }
102            CompileError::TypeError(msg) => {
103                write!(f, "Type error: {}", msg)
104            }
105            CompileError::CodeGenError(msg) => {
106                write!(f, "Code generation error: {}", msg)
107            }
108            CompileError::ModuleNotFound(name) => {
109                write!(f, "Module not found: {}", name)
110            }
111            CompileError::NoModuleContext => {
112                write!(f, "No module context available for qualified name lookup")
113            }
114            CompileError::BreakOutsideLoop => {
115                write!(f, "Break statement used outside of loop")
116            }
117            CompileError::ContinueOutsideLoop => {
118                write!(f, "Continue statement used outside of loop")
119            }
120        }
121    }
122}
123
124impl std::error::Error for CompileError {}
125
126/// Compilation result type
127pub type CompileResult<T> = Result<T, CompileError>;
128
129/// Compilation options
130///
131/// Controls various aspects of the compilation process, including
132/// optional type checking and strictness levels.
133#[derive(Debug, Clone)]
134pub struct CompileOptions {
135    /// Enable type checking before compilation
136    pub enable_type_checking: bool,
137    /// Strict mode - treat warnings as errors
138    pub strict_mode: bool,
139    /// Allow type warnings (only relevant if enable_type_checking is true)
140    pub allow_warnings: bool,
141}
142
143impl Default for CompileOptions {
144    fn default() -> Self {
145        CompileOptions {
146            enable_type_checking: false, // Backward compatible - type checking is opt-in
147            strict_mode: false,
148            allow_warnings: true,
149        }
150    }
151}
152
153/// Local variable information
154#[derive(Debug, Clone)]
155struct Local {
156    name: String,
157    depth: usize,
158}
159
160/// Loop state for tracking break/continue targets
161#[derive(Debug, Clone)]
162struct LoopState {
163    /// Offset of the loop start (for continue)
164    start_offset: usize,
165    /// Offsets of break jumps to be patched to loop end
166    break_jumps: Vec<usize>,
167    /// Offsets of continue jumps to be patched to loop start
168    continue_jumps: Vec<usize>,
169}
170
171/// Bytecode compiler state
172pub struct Compiler {
173    chunk: Chunk,
174    locals: Vec<Local>,
175    scope_depth: usize,
176    options: CompileOptions,
177    type_env: Option<TypeEnv>,
178
179    // Module support
180    module_registry: Option<ModuleRegistry>,
181    imported_bindings: HashMap<String, Expr>,
182
183    // Loop support
184    loop_stack: Vec<LoopState>,
185}
186
187impl Compiler {
188    /// Create a new compiler with default options
189    fn new() -> Self {
190        Compiler {
191            chunk: Chunk::new(),
192            locals: Vec::new(),
193            scope_depth: 0,
194            options: CompileOptions::default(),
195            type_env: None,
196            module_registry: None,
197            imported_bindings: HashMap::new(),
198            loop_stack: Vec::new(),
199        }
200    }
201
202    /// Create a new compiler with custom options
203    fn new_with_options(options: CompileOptions) -> Self {
204        Compiler {
205            chunk: Chunk::new(),
206            locals: Vec::new(),
207            scope_depth: 0,
208            options,
209            type_env: None,
210            module_registry: None,
211            imported_bindings: HashMap::new(),
212            loop_stack: Vec::new(),
213        }
214    }
215
216    /// Check if an expression references a variable (for auto-recursion detection).
217    ///
218    /// This performs a simple free variable analysis to detect if `name` appears
219    /// anywhere in the expression. Used to automatically treat `let x = fun ... x ...`
220    /// as recursive without requiring explicit `let rec`.
221    ///
222    /// This is the same logic as in inference.rs to ensure consistency.
223    fn expr_references_var(expr: &Expr, name: &str) -> bool {
224        match expr {
225            Expr::Var(var_name) => var_name == name,
226            Expr::Lambda { param, body } => {
227                // If the lambda parameter shadows the name, don't look inside
228                if param == name {
229                    false
230                } else {
231                    Self::expr_references_var(body, name)
232                }
233            }
234            Expr::App { func, arg } => {
235                Self::expr_references_var(func, name) || Self::expr_references_var(arg, name)
236            }
237            Expr::Let {
238                name: let_name,
239                value,
240                body,
241            } => {
242                // Check value, but if let shadows the name, don't check body
243                Self::expr_references_var(value, name)
244                    || (let_name != name && Self::expr_references_var(body, name))
245            }
246            Expr::LetRec {
247                name: rec_name,
248                value,
249                body,
250            } => {
251                // Similar to Let
252                Self::expr_references_var(value, name)
253                    || (rec_name != name && Self::expr_references_var(body, name))
254            }
255            Expr::LetRecMutual { bindings, body } => {
256                // Check all binding values
257                bindings
258                    .iter()
259                    .any(|(_, expr)| Self::expr_references_var(expr, name))
260                    // Check body unless one of the bindings shadows the name
261                    || (!bindings.iter().any(|(n, _)| n == name)
262                        && Self::expr_references_var(body, name))
263            }
264            Expr::If {
265                cond,
266                then_branch,
267                else_branch,
268            } => {
269                Self::expr_references_var(cond, name)
270                    || Self::expr_references_var(then_branch, name)
271                    || Self::expr_references_var(else_branch, name)
272            }
273            Expr::BinOp { left, right, .. } => {
274                Self::expr_references_var(left, name) || Self::expr_references_var(right, name)
275            }
276            Expr::Tuple(elements) | Expr::List(elements) | Expr::Array(elements) => {
277                elements.iter().any(|e| Self::expr_references_var(e, name))
278            }
279            Expr::Cons { head, tail } => {
280                Self::expr_references_var(head, name) || Self::expr_references_var(tail, name)
281            }
282            Expr::ArrayIndex { array, index } => {
283                Self::expr_references_var(array, name) || Self::expr_references_var(index, name)
284            }
285            Expr::ArrayUpdate {
286                array,
287                index,
288                value,
289            } => {
290                Self::expr_references_var(array, name)
291                    || Self::expr_references_var(index, name)
292                    || Self::expr_references_var(value, name)
293            }
294            Expr::ArrayLength(array) => Self::expr_references_var(array, name),
295            Expr::RecordLiteral { fields, .. } => fields
296                .iter()
297                .any(|(_, expr)| Self::expr_references_var(expr, name)),
298            Expr::RecordAccess { record, .. } => Self::expr_references_var(record, name),
299            Expr::RecordUpdate { record, fields } => {
300                Self::expr_references_var(record, name)
301                    || fields
302                        .iter()
303                        .any(|(_, expr)| Self::expr_references_var(expr, name))
304            }
305            Expr::VariantConstruct { fields, .. } => fields
306                .iter()
307                .any(|expr| Self::expr_references_var(expr, name)),
308            Expr::Match { scrutinee, arms } => {
309                Self::expr_references_var(scrutinee, name)
310                    || arms.iter().any(|arm| {
311                        // For simplicity, we don't check if pattern shadows the name
312                        // This is a conservative approach - may detect false recursion
313                        // but won't miss actual recursion
314                        Self::expr_references_var(&arm.body, name)
315                    })
316            }
317            Expr::MethodCall { receiver, args, .. } => {
318                Self::expr_references_var(receiver, name)
319                    || args.iter().any(|e| Self::expr_references_var(e, name))
320            }
321            Expr::While { cond, body } => {
322                Self::expr_references_var(cond, name) || Self::expr_references_var(body, name)
323            }
324            Expr::ComputationExpr { body, .. } => {
325                // Check if any statement in the CE body references the variable
326                body.iter().any(|stmt| {
327                    use crate::ast::CEStatement;
328                    match stmt {
329                        CEStatement::Let { value, .. }
330                        | CEStatement::LetBang { value, .. }
331                        | CEStatement::DoBang { value }
332                        | CEStatement::Return { value }
333                        | CEStatement::ReturnBang { value }
334                        | CEStatement::Yield { value }
335                        | CEStatement::YieldBang { value }
336                        | CEStatement::Expr { value } => Self::expr_references_var(value, name),
337                    }
338                })
339            }
340            // Literals and control flow don't reference variables
341            Expr::Lit(_) | Expr::Break | Expr::Continue => false,
342        }
343    }
344
345    /// Main entry point: compile an expression to a chunk (backward compatible)
346    pub fn compile(expr: &Expr) -> CompileResult<Chunk> {
347        Self::compile_with_options(expr, CompileOptions::default())
348    }
349
350    /// Compile an expression with type checking enabled
351    pub fn compile_checked(expr: &Expr) -> CompileResult<Chunk> {
352        let options = CompileOptions {
353            enable_type_checking: true,
354            ..Default::default()
355        };
356        Self::compile_with_options(expr, options)
357    }
358
359    /// Compile an expression with custom options
360    pub fn compile_with_options(expr: &Expr, options: CompileOptions) -> CompileResult<Chunk> {
361        let mut compiler = Compiler::new_with_options(options);
362
363        // Optional type checking phase
364        if compiler.options.enable_type_checking {
365            compiler.type_check(expr)?;
366        }
367
368        // Compilation phase
369        compiler.compile_expr(expr)?;
370        compiler.emit(Instruction::Return);
371        Ok(compiler.chunk)
372    }
373
374    /// Compile a complete program with modules
375    ///
376    /// This is the main entry point for compiling programs with module definitions.
377    /// It performs three phases:
378    /// 1. Register all modules and their bindings
379    /// 2. Apply imports to the current environment
380    /// 3. Compile the main expression (if present)
381    pub fn compile_program(program: &Program) -> CompileResult<Chunk> {
382        let mut compiler = Compiler::new();
383        let mut registry = ModuleRegistry::with_stdlib();
384
385        // Phase 1: Register all modules
386        for module in &program.modules {
387            compiler.register_module(&mut registry, module)?;
388        }
389
390        // Store registry for qualified name lookups
391        compiler.module_registry = Some(registry);
392
393        // Phase 2: Apply imports to environment
394        for import in &program.imports {
395            compiler.apply_import(import)?;
396        }
397
398        // Phase 3: Compile top-level items and main expression
399        compiler.compile_top_level_items(&program.items, &program.main_expr)?;
400
401        compiler.emit(Instruction::Return);
402        Ok(compiler.chunk)
403    }
404
405    /// Register a module and compile its bindings
406    ///
407    /// This processes all items in a module definition and registers them
408    /// in the module registry for later lookup.
409    #[allow(clippy::only_used_in_recursion)]
410    fn register_module(
411        &mut self,
412        registry: &mut ModuleRegistry,
413        module: &ModuleDef,
414    ) -> CompileResult<()> {
415        let mut bindings = HashMap::new();
416        let mut types = HashMap::new();
417
418        for item in &module.items {
419            match item {
420                ModuleItem::Let(name, expr) => {
421                    // Store binding for later compilation (skip discard bindings)
422                    if let Some(name) = name {
423                        bindings.insert(name.clone(), expr.clone());
424                    }
425                }
426                ModuleItem::LetRec(rec_bindings) => {
427                    // Handle recursive bindings
428                    for (name, expr) in rec_bindings {
429                        bindings.insert(name.clone(), expr.clone());
430                    }
431                }
432                ModuleItem::TypeDef(type_def) => {
433                    // Convert AST TypeDefinition to modules TypeDefinition
434                    let module_type_def = match type_def {
435                        crate::ast::TypeDefinition::Record(r) => {
436                            crate::modules::TypeDefinition::Record(r.clone())
437                        }
438                        crate::ast::TypeDefinition::Du(du) => {
439                            crate::modules::TypeDefinition::Du(du.clone())
440                        }
441                    };
442
443                    // Extract type name based on definition
444                    let type_name = match type_def {
445                        crate::ast::TypeDefinition::Record(r) => r.name.clone(),
446                        crate::ast::TypeDefinition::Du(du) => du.name.clone(),
447                    };
448
449                    types.insert(type_name, module_type_def);
450                }
451                ModuleItem::Module(nested) => {
452                    // Recursively register nested module
453                    self.register_module(registry, nested)?;
454                }
455            }
456        }
457
458        // Register module in registry
459        registry.register_module(module.name.clone(), bindings, types);
460
461        Ok(())
462    }
463
464    /// Apply an import to the current environment
465    ///
466    /// This brings all bindings from the imported module into the current scope,
467    /// allowing them to be accessed without qualification.
468    fn apply_import(&mut self, import: &Import) -> CompileResult<()> {
469        // Get module name (for simple imports, it's the first component)
470        let module_name = import
471            .module_path
472            .first()
473            .ok_or_else(|| CompileError::ModuleNotFound("empty module path".to_string()))?;
474
475        let registry = self
476            .module_registry
477            .as_ref()
478            .ok_or(CompileError::NoModuleContext)?;
479
480        let module_bindings = registry
481            .get_module_bindings(module_name)
482            .ok_or_else(|| CompileError::ModuleNotFound(module_name.clone()))?;
483
484        // Add all bindings from imported module to current environment
485        for (name, expr) in module_bindings {
486            self.imported_bindings.insert(name.clone(), expr.clone());
487        }
488
489        Ok(())
490    }
491
492    /// Type check expression
493    ///
494    /// This is a placeholder for the actual type inference implementation.
495    /// Once the type inference module is complete, this will perform full
496    /// Hindley-Milner type inference and constraint solving.
497    fn type_check(&mut self, _expr: &Expr) -> CompileResult<Type> {
498        // Placeholder implementation
499        // TODO: Replace with actual type inference when available
500        //
501        // Expected implementation:
502        // let mut inference = TypeInference::new();
503        // let env = TypeEnv::new();
504        // let ty = inference.infer(expr, &env)
505        //     .map_err(|e| CompileError::TypeError(format!("{}", e)))?;
506        // let subst = inference.solve_constraints()
507        //     .map_err(|e| CompileError::TypeError(format!("{}", e)))?;
508        // let final_ty = subst.apply_type(&ty);
509        // self.type_env = Some(env);
510        // Ok(final_ty)
511
512        // For now, just initialize empty type environment
513        self.type_env = Some(TypeEnv::new());
514        Ok(Type::Unit)
515    }
516
517    /// Compile an expression and emit instructions
518    fn compile_expr(&mut self, expr: &Expr) -> CompileResult<()> {
519        match expr {
520            Expr::Lit(lit) => self.compile_literal(lit),
521            Expr::Var(name) => self.compile_var(name),
522            Expr::BinOp { op, left, right } => self.compile_binop(*op, left, right),
523            Expr::Let { name, value, body } => self.compile_let(name, value, body),
524            Expr::LetRec { name, value, body } => self.compile_let_rec(name, value, body),
525            Expr::LetRecMutual { bindings, body } => self.compile_let_rec_mutual(bindings, body),
526            Expr::Lambda { param, body } => self.compile_lambda(param, body),
527            Expr::App { func, arg } => self.compile_app(func, arg),
528            Expr::If {
529                cond,
530                then_branch,
531                else_branch,
532            } => self.compile_if(cond, then_branch, else_branch),
533            Expr::Tuple(elements) => self.compile_tuple(elements),
534            Expr::List(elements) => self.compile_list(elements),
535            Expr::Cons { head, tail } => self.compile_cons(head, tail),
536            Expr::Array(elements) => self.compile_array(elements),
537            Expr::ArrayIndex { array, index } => self.compile_array_index(array, index),
538            Expr::ArrayUpdate {
539                array,
540                index,
541                value,
542            } => self.compile_array_update(array, index, value),
543            Expr::ArrayLength(array) => self.compile_array_length(array),
544            Expr::RecordLiteral { fields, .. } => self.compile_record_literal(fields),
545            Expr::RecordAccess { record, field } => self.compile_record_access(record, field),
546            Expr::RecordUpdate { record, fields } => self.compile_record_update(record, fields),
547            Expr::Match { scrutinee, arms } => self.compile_match(scrutinee, arms),
548            Expr::VariantConstruct {
549                type_name,
550                variant,
551                fields,
552            } => self.compile_variant_construct(type_name, variant, fields),
553            Expr::MethodCall {
554                receiver,
555                method_name,
556                args,
557            } => self.compile_method_call(receiver, method_name, args),
558            Expr::While { cond, body } => self.compile_while(cond, body),
559            Expr::Break => self.compile_break(),
560            Expr::Continue => self.compile_continue(),
561            Expr::ComputationExpr { builder, body } => self.compile_computation_expr(builder, body),
562        }
563    }
564
565    /// Compile a literal value
566    fn compile_literal(&mut self, lit: &Literal) -> CompileResult<()> {
567        let value = match lit {
568            Literal::Int(n) => Value::Int(*n),
569            Literal::Bool(b) => Value::Bool(*b),
570            Literal::Str(s) => Value::Str(s.clone()),
571            Literal::Unit => Value::Unit,
572            Literal::Float(f) => Value::Float(*f),
573        };
574
575        let idx = self.add_constant(value)?;
576        self.emit(Instruction::LoadConst(idx));
577        Ok(())
578    }
579    /// Compile a variable reference with module support
580    ///
581    /// This handles:
582    /// - Qualified names (e.g., Math.add)
583    /// - Imported bindings (from open statements)
584    /// - Local variables
585    fn compile_var(&mut self, name: &str) -> CompileResult<()> {
586        // Check if it's a qualified name (e.g., "Math.add")
587        if let Some((module_path, binding_name)) = parse_qualified_name(name) {
588            return self.compile_qualified_var(&module_path, &binding_name);
589        }
590
591        // Check local scope first
592        for (i, local) in self.locals.iter().enumerate().rev() {
593            if local.name == name {
594                let idx = i as u8;
595                self.emit(Instruction::LoadLocal(idx));
596                return Ok(());
597            }
598        }
599
600        // Check imported bindings
601        if let Some(expr) = self.imported_bindings.get(name) {
602            // For imported bindings, check if the expression is a simple variable reference
603            // (which indicates a global/stdlib reference). If so, compile it directly as
604            // a global lookup to avoid infinite recursion.
605            if let Expr::Var(ref global_name) = expr {
606                let idx = self.add_constant(Value::Str(global_name.clone()))?;
607                self.emit(Instruction::LoadGlobal(idx));
608                return Ok(());
609            }
610            // For other expressions (e.g., user-defined module bindings), compile normally
611            return self.compile_expr(&expr.clone());
612        }
613
614        // If not found locally or imported, assume it's a global variable
615        let idx = self.add_constant(Value::Str(name.to_string()))?;
616        self.emit(Instruction::LoadGlobal(idx));
617        Ok(())
618    }
619
620    /// Compile a qualified variable reference (e.g., Math.add)
621    fn compile_qualified_var(&mut self, module_path: &[String], name: &str) -> CompileResult<()> {
622        // For now, only support single-level qualification (Module.binding)
623        if module_path.len() != 1 {
624            return Err(CompileError::CodeGenError(
625                "Nested module paths not yet supported in compilation".to_string(),
626            ));
627        }
628
629        let module_name = &module_path[0];
630
631        // Try to look up the qualified name in module registry if available
632        if let Some(registry) = self.module_registry.as_ref() {
633            if let Some(expr) = registry.resolve_qualified(module_name, name) {
634                // For stdlib modules, the expression will be Expr::Var("Module.function")
635                // which represents a global reference. Compile it directly as global lookup
636                // to avoid infinite recursion.
637                if let Expr::Var(ref global_name) = expr {
638                    let idx = self.add_constant(Value::Str(global_name.clone()))?;
639                    self.emit(Instruction::LoadGlobal(idx));
640                    return Ok(());
641                }
642
643                // For user-defined modules, compile the expression normally
644                return self.compile_expr(&expr.clone());
645            }
646        }
647
648        // Fall back to treating qualified name as a runtime-resolved global
649        // This allows host functions registered via register_module() to work in eval()
650        let qualified_name = format!("{}.{}", module_name, name);
651        let idx = self.add_constant(Value::Str(qualified_name))?;
652        self.emit(Instruction::LoadGlobal(idx));
653        Ok(())
654    }
655    /// Compile a binary operation
656    fn compile_binop(&mut self, op: BinOp, left: &Expr, right: &Expr) -> CompileResult<()> {
657        // Compile operands
658        self.compile_expr(left)?;
659        self.compile_expr(right)?;
660
661        // Emit operation instruction
662        let instr = match op {
663            BinOp::Add => Instruction::Add,
664            BinOp::Sub => Instruction::Sub,
665            BinOp::Mul => Instruction::Mul,
666            BinOp::Div => Instruction::Div,
667            BinOp::Concat => Instruction::Concat,
668            BinOp::Eq => Instruction::Eq,
669            BinOp::Neq => Instruction::Neq,
670            BinOp::Lt => Instruction::Lt,
671            BinOp::Lte => Instruction::Lte,
672            BinOp::Gt => Instruction::Gt,
673            BinOp::Gte => Instruction::Gte,
674            BinOp::And => Instruction::And,
675            BinOp::Or => Instruction::Or,
676        };
677        self.emit(instr);
678        Ok(())
679    }
680
681    /// Compile a tuple expression
682    fn compile_tuple(&mut self, elements: &[Expr]) -> CompileResult<()> {
683        // Check if tuple size fits in u16
684        if elements.len() > u16::MAX as usize {
685            return Err(CompileError::TupleTooLarge);
686        }
687
688        // Compile each element (left to right)
689        for element in elements {
690            self.compile_expr(element)?;
691        }
692
693        // Emit MakeTuple instruction with element count
694        let element_count = elements.len() as u16;
695        self.emit(Instruction::MakeTuple(element_count));
696
697        Ok(())
698    }
699
700    /// Compile a let-binding with automatic recursion detection
701    ///
702    /// Implements issue #126: Detects if the value expression references the binding name
703    /// and automatically uses recursive compilation strategy if needed.
704    fn compile_list(&mut self, elements: &[Expr]) -> CompileResult<()> {
705        // Handle empty list: [] -> emit LoadConst with Value::Nil
706        if elements.is_empty() {
707            let idx = self.add_constant(Value::Nil)?;
708            self.emit(Instruction::LoadConst(idx));
709            return Ok(());
710        }
711
712        // Check if list size fits in u16
713        if elements.len() > u16::MAX as usize {
714            return Err(CompileError::TupleTooLarge); // Reuse error, or add ListTooLarge
715        }
716
717        // Compile each element (left to right)
718        for element in elements {
719            self.compile_expr(element)?;
720        }
721
722        // Emit MakeList instruction with element count
723        let element_count = elements.len() as u16;
724        self.emit(Instruction::MakeList(element_count));
725
726        Ok(())
727    }
728
729    /// Compile a cons expression
730    fn compile_cons(&mut self, head: &Expr, tail: &Expr) -> CompileResult<()> {
731        // Compile head expression
732        self.compile_expr(head)?;
733        // Compile tail expression
734        self.compile_expr(tail)?;
735        // Emit Cons instruction
736        self.emit(Instruction::Cons);
737        Ok(())
738    }
739
740    /// Compile an array expression
741    fn compile_array(&mut self, elements: &[Expr]) -> CompileResult<()> {
742        // Check if array size fits in u16
743        if elements.len() > u16::MAX as usize {
744            return Err(CompileError::TupleTooLarge); // Reuse error for now
745        }
746
747        // Compile each element (left to right)
748        for element in elements {
749            self.compile_expr(element)?;
750        }
751
752        // Emit MakeArray instruction with element count
753        let element_count = elements.len() as u16;
754        self.emit(Instruction::MakeArray(element_count));
755
756        Ok(())
757    }
758
759    /// Compile an array index expression
760    fn compile_array_index(&mut self, array: &Expr, index: &Expr) -> CompileResult<()> {
761        // Compile array expression
762        self.compile_expr(array)?;
763        // Compile index expression
764        self.compile_expr(index)?;
765        // Emit ArrayGet instruction
766        self.emit(Instruction::ArrayGet);
767        Ok(())
768    }
769
770    /// Compile an array update expression (immutable)
771    fn compile_array_update(
772        &mut self,
773        array: &Expr,
774        index: &Expr,
775        value: &Expr,
776    ) -> CompileResult<()> {
777        // Compile array expression
778        self.compile_expr(array)?;
779        // Compile index expression
780        self.compile_expr(index)?;
781        // Compile new value expression
782        self.compile_expr(value)?;
783        // Emit ArrayUpdate instruction (creates new array)
784        self.emit(Instruction::ArrayUpdate);
785        Ok(())
786    }
787
788    /// Compile an array length expression
789    fn compile_array_length(&mut self, array: &Expr) -> CompileResult<()> {
790        // Compile array expression
791        self.compile_expr(array)?;
792        // Emit ArrayLength instruction
793        self.emit(Instruction::ArrayLength);
794        Ok(())
795    }
796
797    fn compile_top_level_items(
798        &mut self,
799        items: &[ModuleItem],
800        main_expr: &Option<Expr>,
801    ) -> CompileResult<()> {
802        if let Some((first, rest)) = items.split_first() {
803            match first {
804                ModuleItem::Let(name, value) => {
805                    // Compile value
806                    self.compile_expr(value)?;
807
808                    if let Some(name) = name {
809                        // Enter scope
810                        self.begin_scope();
811                        self.add_local(name.to_string())?;
812
813                        // Store local
814                        let local_idx = (self.locals.len() - 1) as u8;
815                        self.emit(Instruction::StoreLocal(local_idx));
816
817                        // Recurse
818                        self.compile_top_level_items(rest, main_expr)?;
819
820                        // Clean up scope
821                        let locals_to_remove = self.end_scope_count();
822                        for _ in 0..locals_to_remove {
823                            self.locals.pop();
824                        }
825                        self.scope_depth -= 1;
826                    } else {
827                        // Discard pattern: just pop the value and continue
828                        self.emit(Instruction::Pop);
829                        self.compile_top_level_items(rest, main_expr)?;
830                    }
831                }
832                ModuleItem::LetRec(bindings) => {
833                    if bindings.len() == 1 {
834                        // Single recursive binding
835                        let (name, value) = &bindings[0];
836                        let placeholder_idx = self.add_constant(Value::Unit)?;
837                        self.emit(Instruction::LoadConst(placeholder_idx));
838
839                        self.begin_scope();
840                        self.add_local(name.to_string())?;
841                        let local_idx = (self.locals.len() - 1) as u8;
842                        self.emit(Instruction::StoreLocal(local_idx));
843
844                        self.compile_expr(value)?;
845                        self.emit(Instruction::StoreLocal(local_idx));
846
847                        self.compile_top_level_items(rest, main_expr)?;
848
849                        let locals_to_remove = self.end_scope_count();
850                        for _ in 0..locals_to_remove {
851                            self.locals.pop();
852                        }
853                        self.scope_depth -= 1;
854                    } else {
855                        // Mutual recursion
856                        let placeholder_idx = self.add_constant(Value::Unit)?;
857                        let mut local_indices = Vec::new();
858
859                        self.begin_scope();
860
861                        for (name, _) in bindings {
862                            self.emit(Instruction::LoadConst(placeholder_idx));
863                            self.add_local(name.to_string())?;
864                            local_indices.push((self.locals.len() - 1) as u8);
865                            self.emit(Instruction::StoreLocal(*local_indices.last().unwrap()));
866                        }
867
868                        for ((_, value), local_idx) in bindings.iter().zip(local_indices.iter()) {
869                            self.compile_expr(value)?;
870                            self.emit(Instruction::StoreLocal(*local_idx));
871                        }
872
873                        self.compile_top_level_items(rest, main_expr)?;
874
875                        let locals_to_remove = self.end_scope_count();
876                        for _ in 0..locals_to_remove {
877                            self.locals.pop();
878                        }
879                        self.scope_depth -= 1;
880                    }
881                }
882                ModuleItem::TypeDef(_) | ModuleItem::Module(_) => {
883                    // Skip non-executable items and recurse
884                    self.compile_top_level_items(rest, main_expr)?;
885                }
886            }
887        } else {
888            // Base case: compile main expression
889            if let Some(expr) = main_expr {
890                self.compile_expr(expr)?;
891            } else {
892                let unit_idx = self.add_constant(Value::Unit)?;
893                self.emit(Instruction::LoadConst(unit_idx));
894            }
895        }
896        Ok(())
897    }
898
899    fn compile_let(&mut self, name: &str, value: &Expr, body: &Expr) -> CompileResult<()> {
900        // Auto-detect recursion: check if value references name
901        let auto_recursive = Self::expr_references_var(value, name);
902
903        if auto_recursive {
904            // Use recursive compilation strategy
905            self.compile_let_rec(name, value, body)
906        } else {
907            // Use standard let compilation
908            // Compile the value expression
909            self.compile_expr(value)?;
910
911            // Enter new scope
912            self.begin_scope();
913
914            // Add local variable
915            self.add_local(name.to_string())?;
916
917            // Store the value in the local slot
918            let local_idx = (self.locals.len() - 1) as u8;
919            self.emit(Instruction::StoreLocal(local_idx));
920
921            // Compile the body expression
922            self.compile_expr(body)?;
923
924            // Exit scope - note: we don't emit POP for the body result
925            // The result stays on top of the stack for the caller
926            let locals_to_remove = self.end_scope_count();
927
928            // Only pop if we have multiple locals and need to clean up intermediates
929            // For Phase 1 MVP, we simplify by not emitting POPs
930            // The locals stay in their stack slots until function return
931            for _ in 0..locals_to_remove {
932                self.locals.pop();
933            }
934            self.scope_depth -= 1;
935
936            Ok(())
937        }
938    }
939
940    /// Compile a lambda function
941    fn compile_lambda(&mut self, param: &str, body: &Expr) -> CompileResult<()> {
942        // Create a nested chunk for the lambda body
943        let mut lambda_compiler = Compiler::new();
944
945        // Lambda parameter becomes local 0
946        lambda_compiler.begin_scope();
947        lambda_compiler.add_local(param.to_string())?;
948
949        // Compile the lambda body
950        lambda_compiler.compile_expr(body)?;
951        lambda_compiler.emit(Instruction::Return);
952
953        // Clean up scope (not strictly needed as we discard compiler, but good practice)
954        let _locals_to_remove = lambda_compiler.end_scope_count();
955        lambda_compiler.scope_depth -= 1;
956
957        // Create a closure prototype (chunk + arity)
958        // For now, we don't support upvalue capture in the compiler (Phase 2 extension)
959        // We assume no upvalues.
960        let closure = Closure::with_arity(lambda_compiler.chunk, 1);
961        let closure_val = Value::Closure(Arc::new(closure));
962
963        // Store prototype in constants
964        let const_idx = self.add_constant(closure_val)?;
965
966        // Emit MakeClosure instruction (0 upvalues for now)
967        self.emit(Instruction::MakeClosure(const_idx, 0));
968        Ok(())
969    }
970
971    /// Compile a recursive let-binding using placeholder strategy
972    fn compile_let_rec(&mut self, name: &str, value: &Expr, body: &Expr) -> CompileResult<()> {
973        // Strategy: Create a placeholder, compile the function with
974        // the name in scope, then update the binding
975
976        // 1. Push placeholder (will be replaced by the actual value)
977        let placeholder_idx = self.add_constant(Value::Unit)?;
978        self.emit(Instruction::LoadConst(placeholder_idx));
979
980        // 2. Enter scope and add local for the recursive binding
981        self.begin_scope();
982        self.add_local(name.to_string())?;
983        let local_idx = (self.locals.len() - 1) as u8;
984
985        // 3. Store placeholder in local slot
986        self.emit(Instruction::StoreLocal(local_idx));
987
988        // 4. Compile the value (usually a lambda) with name in scope
989        // The value can now reference itself via the local
990        self.compile_expr(value)?;
991
992        // 5. Update the local slot with the actual value
993        self.emit(Instruction::StoreLocal(local_idx));
994
995        // 6. Compile body (the local is still in scope)
996        self.compile_expr(body)?;
997
998        // 7. Clean up scope
999        let locals_to_remove = self.end_scope_count();
1000        for _ in 0..locals_to_remove {
1001            self.locals.pop();
1002        }
1003        self.scope_depth -= 1;
1004
1005        Ok(())
1006    }
1007
1008    /// Compile mutually recursive bindings
1009    fn compile_let_rec_mutual(
1010        &mut self,
1011        bindings: &[(String, Expr)],
1012        body: &Expr,
1013    ) -> CompileResult<()> {
1014        // Strategy: Create placeholders for all bindings, then fill them in
1015
1016        // 1. Enter scope
1017        self.begin_scope();
1018
1019        // 2. Push placeholders and create locals for all bindings
1020        let placeholder_idx = self.add_constant(Value::Unit)?;
1021        let mut local_indices = Vec::new();
1022
1023        for (name, _) in bindings {
1024            // Push placeholder
1025            self.emit(Instruction::LoadConst(placeholder_idx));
1026
1027            // Add local
1028            self.add_local(name.clone())?;
1029            let local_idx = (self.locals.len() - 1) as u8;
1030            local_indices.push(local_idx);
1031
1032            // Store placeholder
1033            self.emit(Instruction::StoreLocal(local_idx));
1034        }
1035
1036        // 3. Compile each value (with all names in scope)
1037        for (i, (_name, value)) in bindings.iter().enumerate() {
1038            self.compile_expr(value)?;
1039            self.emit(Instruction::StoreLocal(local_indices[i]));
1040        }
1041
1042        // 4. Compile body
1043        self.compile_expr(body)?;
1044
1045        // 5. Clean up scope
1046        let locals_to_remove = self.end_scope_count();
1047        for _ in 0..locals_to_remove {
1048            self.locals.pop();
1049        }
1050        self.scope_depth -= 1;
1051
1052        Ok(())
1053    }
1054
1055    /// Compile a function application
1056    fn compile_app(&mut self, func: &Expr, arg: &Expr) -> CompileResult<()> {
1057        // Compile the function expression
1058        self.compile_expr(func)?;
1059
1060        // Compile the argument expression
1061        self.compile_expr(arg)?;
1062
1063        // Emit call instruction with 1 argument
1064        self.emit(Instruction::Call(1));
1065
1066        Ok(())
1067    }
1068
1069    /// Compile an if-then-else expression
1070    fn compile_if(
1071        &mut self,
1072        cond: &Expr,
1073        then_branch: &Expr,
1074        else_branch: &Expr,
1075    ) -> CompileResult<()> {
1076        // Compile condition
1077        self.compile_expr(cond)?;
1078
1079        // Emit JumpIfFalse with placeholder offset
1080        let jump_to_else = self.emit_jump(Instruction::JumpIfFalse(0));
1081
1082        // Note: JumpIfFalse pops the condition value, so no manual POP needed
1083
1084        // Compile then branch
1085        self.compile_expr(then_branch)?;
1086
1087        // Emit Jump to skip else branch with placeholder offset
1088        let jump_to_end = self.emit_jump(Instruction::Jump(0));
1089
1090        // Patch the JumpIfFalse to point here
1091        self.patch_jump(jump_to_else)?;
1092
1093        // Compile else branch
1094        self.compile_expr(else_branch)?;
1095
1096        // Patch the Jump to point here
1097        self.patch_jump(jump_to_end)?;
1098
1099        Ok(())
1100    }
1101
1102    /// Compile a while loop: while <cond> do <body>
1103    fn compile_while(&mut self, cond: &Expr, body: &Expr) -> CompileResult<()> {
1104        // Record the loop start offset
1105        let start_offset = self.chunk.current_offset();
1106
1107        // Compile condition
1108        self.compile_expr(cond)?;
1109
1110        // Emit JumpIfFalse to loop end (placeholder offset)
1111        let jump_to_end = self.emit_jump(Instruction::JumpIfFalse(0));
1112
1113        // Push loop state on stack
1114        self.loop_stack.push(LoopState {
1115            start_offset,
1116            break_jumps: Vec::new(),
1117            continue_jumps: Vec::new(),
1118        });
1119
1120        // Compile body
1121        self.compile_expr(body)?;
1122
1123        // Pop body result (loops return unit)
1124        self.emit(Instruction::Pop);
1125
1126        // Jump back to loop start
1127        let offset_to_start = self.chunk.current_offset() as i32 - start_offset as i32 + 1;
1128        if offset_to_start > i16::MAX as i32 || -offset_to_start > i16::MAX as i32 {
1129            return Err(CompileError::InvalidJumpOffset);
1130        }
1131        self.emit(Instruction::Jump(-offset_to_start as i16));
1132
1133        // Patch jump to end
1134        self.patch_jump(jump_to_end)?;
1135
1136        // Pop loop state and patch all break/continue jumps
1137        let loop_state = self.loop_stack.pop().unwrap();
1138
1139        // Patch all break jumps to point to current offset (after loop)
1140        for break_jump in loop_state.break_jumps {
1141            self.patch_jump(break_jump)?;
1142        }
1143
1144        // Patch all continue jumps to point to loop start
1145        for continue_jump in loop_state.continue_jumps {
1146            let target = start_offset;
1147            let offset = target as i32 - continue_jump as i32 - 1;
1148            if offset > i16::MAX as i32 || offset < i16::MIN as i32 {
1149                return Err(CompileError::InvalidJumpOffset);
1150            }
1151            match self.chunk.instructions[continue_jump] {
1152                Instruction::Jump(_) => {
1153                    self.chunk.instructions[continue_jump] = Instruction::Jump(offset as i16);
1154                }
1155                _ => unreachable!("Expected Jump instruction for continue"),
1156            }
1157        }
1158
1159        // Push unit (result of while loop)
1160        let unit_idx = self.add_constant(Value::Unit)?;
1161        self.emit(Instruction::LoadConst(unit_idx));
1162
1163        Ok(())
1164    }
1165
1166    /// Compile a break statement
1167    fn compile_break(&mut self) -> CompileResult<()> {
1168        if self.loop_stack.is_empty() {
1169            return Err(CompileError::BreakOutsideLoop);
1170        }
1171
1172        // Emit a jump to loop end (placeholder offset)
1173        let jump_idx = self.emit_jump(Instruction::Jump(0));
1174
1175        // Record this jump to be patched later
1176        self.loop_stack
1177            .last_mut()
1178            .unwrap()
1179            .break_jumps
1180            .push(jump_idx);
1181
1182        Ok(())
1183    }
1184
1185    /// Compile a continue statement
1186    fn compile_continue(&mut self) -> CompileResult<()> {
1187        if self.loop_stack.is_empty() {
1188            return Err(CompileError::ContinueOutsideLoop);
1189        }
1190
1191        // Emit a jump to loop start (placeholder offset)
1192        let jump_idx = self.emit_jump(Instruction::Jump(0));
1193
1194        // Record this jump to be patched later
1195        self.loop_stack
1196            .last_mut()
1197            .unwrap()
1198            .continue_jumps
1199            .push(jump_idx);
1200
1201        Ok(())
1202    }
1203
1204    /// Compile a computation expression
1205    fn compile_computation_expr(
1206        &mut self,
1207        builder: &str,
1208        body: &[crate::ast::CEStatement],
1209    ) -> CompileResult<()> {
1210        // Desugar the computation expression
1211        let desugared = self.desugar_ce_statements(builder, body)?;
1212
1213        // Compile the desugared expression
1214        self.compile_expr(&desugared)
1215    }
1216
1217    /// Desugar computation expression statements into builder calls
1218    fn desugar_ce_statements(
1219        &self,
1220        builder: &str,
1221        statements: &[crate::ast::CEStatement],
1222    ) -> CompileResult<Expr> {
1223        if statements.is_empty() {
1224            // Empty block: return unit
1225            // builder.Return(())
1226            let builder_expr = Box::new(Expr::Var(builder.to_string()));
1227            let unit_expr = Expr::Lit(Literal::Unit);
1228
1229            return Ok(Expr::MethodCall {
1230                receiver: builder_expr,
1231                method_name: "Return".to_string(),
1232                args: vec![unit_expr],
1233            });
1234        }
1235
1236        let first = &statements[0];
1237        let rest = &statements[1..];
1238        let builder_expr = Box::new(Expr::Var(builder.to_string()));
1239
1240        match first {
1241            crate::ast::CEStatement::Let { name, value } => {
1242                // let x = expr; rest...
1243                // -> let x = expr in desugar(rest)
1244                // Implicitly handle rest being empty (though unlikely to be useful without return)
1245                let rest_expr = self.desugar_ce_statements(builder, rest)?;
1246                Ok(Expr::Let {
1247                    name: name.clone(),
1248                    value: value.clone(),
1249                    body: Box::new(rest_expr),
1250                })
1251            }
1252            crate::ast::CEStatement::LetBang { name, value } => {
1253                // let! x = expr; rest...
1254                // -> builder.Bind(expr, fun x -> desugar(rest))
1255
1256                // If rest is empty, Bind needs a continuation (implicit Return(()))
1257                let rest_expr = self.desugar_ce_statements(builder, rest)?;
1258
1259                let continuation = Expr::Lambda {
1260                    param: name.clone(),
1261                    body: Box::new(rest_expr),
1262                };
1263
1264                Ok(Expr::MethodCall {
1265                    receiver: builder_expr,
1266                    method_name: "Bind".to_string(),
1267                    args: vec![*value.clone(), continuation],
1268                })
1269            }
1270            crate::ast::CEStatement::DoBang { value } => {
1271                // do! expr; rest...
1272                // -> builder.Bind(expr, fun () -> desugar(rest))
1273
1274                let rest_expr = self.desugar_ce_statements(builder, rest)?;
1275
1276                // Use wildcard/dummy param for unit
1277                let continuation = Expr::Lambda {
1278                    param: "_".to_string(),
1279                    body: Box::new(rest_expr),
1280                };
1281
1282                Ok(Expr::MethodCall {
1283                    receiver: builder_expr,
1284                    method_name: "Bind".to_string(),
1285                    args: vec![*value.clone(), continuation],
1286                })
1287            }
1288            crate::ast::CEStatement::Return { value } => {
1289                // return expr
1290                // -> builder.Return(expr)
1291                // Ignores rest (dead code)
1292                Ok(Expr::MethodCall {
1293                    receiver: builder_expr,
1294                    method_name: "Return".to_string(),
1295                    args: vec![*value.clone()],
1296                })
1297            }
1298            crate::ast::CEStatement::ReturnBang { value } => {
1299                // return! expr
1300                // -> builder.ReturnFrom(expr)
1301                Ok(Expr::MethodCall {
1302                    receiver: builder_expr,
1303                    method_name: "ReturnFrom".to_string(),
1304                    args: vec![*value.clone()],
1305                })
1306            }
1307            crate::ast::CEStatement::Yield { value } => {
1308                // yield expr
1309                // -> builder.Yield(expr)
1310                let yield_expr = Expr::MethodCall {
1311                    receiver: builder_expr.clone(),
1312                    method_name: "Yield".to_string(),
1313                    args: vec![*value.clone()],
1314                };
1315
1316                if rest.is_empty() {
1317                    Ok(yield_expr)
1318                } else {
1319                    // builder.Combine(yield_expr, builder.Delay(fun () -> desugar(rest)))
1320                    let rest_expr = self.desugar_ce_statements(builder, rest)?;
1321
1322                    let delay_lambda = Expr::Lambda {
1323                        param: "_".to_string(),
1324                        body: Box::new(rest_expr),
1325                    };
1326
1327                    let delay_expr = Expr::MethodCall {
1328                        receiver: builder_expr.clone(),
1329                        method_name: "Delay".to_string(),
1330                        args: vec![delay_lambda],
1331                    };
1332
1333                    Ok(Expr::MethodCall {
1334                        receiver: builder_expr,
1335                        method_name: "Combine".to_string(),
1336                        args: vec![yield_expr, delay_expr],
1337                    })
1338                }
1339            }
1340            crate::ast::CEStatement::YieldBang { value } => {
1341                // yield! expr
1342                // -> builder.YieldFrom(expr)
1343                let yield_from_expr = Expr::MethodCall {
1344                    receiver: builder_expr.clone(),
1345                    method_name: "YieldFrom".to_string(),
1346                    args: vec![*value.clone()],
1347                };
1348
1349                if rest.is_empty() {
1350                    Ok(yield_from_expr)
1351                } else {
1352                    let rest_expr = self.desugar_ce_statements(builder, rest)?;
1353
1354                    let delay_lambda = Expr::Lambda {
1355                        param: "_".to_string(),
1356                        body: Box::new(rest_expr),
1357                    };
1358
1359                    let delay_expr = Expr::MethodCall {
1360                        receiver: builder_expr.clone(),
1361                        method_name: "Delay".to_string(),
1362                        args: vec![delay_lambda],
1363                    };
1364
1365                    Ok(Expr::MethodCall {
1366                        receiver: builder_expr,
1367                        method_name: "Combine".to_string(),
1368                        args: vec![yield_from_expr, delay_expr],
1369                    })
1370                }
1371            }
1372            crate::ast::CEStatement::Expr { value } => {
1373                // expr; rest...
1374                if rest.is_empty() {
1375                    // Last expression in block - just return it
1376                    Ok(*value.clone())
1377                } else {
1378                    // expr; rest...
1379                    // -> let _ = expr in desugar(rest)
1380                    // Treat as synchronous sequence
1381                    let rest_expr = self.desugar_ce_statements(builder, rest)?;
1382
1383                    Ok(Expr::Let {
1384                        name: "_".to_string(),
1385                        value: value.clone(),
1386                        body: Box::new(rest_expr),
1387                    })
1388                }
1389            }
1390        }
1391    }
1392
1393    /// Compile a match expression with full pattern matching support
1394    fn compile_match(&mut self, scrutinee: &Expr, arms: &[MatchArm]) -> CompileResult<()> {
1395        // Compile scrutinee once and keep it on the stack
1396        self.compile_expr(scrutinee)?;
1397
1398        let _end_label = self.chunk.current_offset();
1399        let mut end_jumps = Vec::new();
1400
1401        for (i, arm) in arms.iter().enumerate() {
1402            let is_last_arm = i == arms.len() - 1;
1403
1404            // Duplicate scrutinee for pattern test (except we'll clean it up)
1405            if !is_last_arm {
1406                self.emit(Instruction::Dup);
1407            }
1408
1409            // Compile pattern test - this will push a boolean result
1410            let _next_arm_offset = if !is_last_arm {
1411                self.chunk.current_offset()
1412            } else {
1413                0 // Last arm doesn't need a jump
1414            };
1415
1416            // Test the pattern and get a boolean result
1417            self.compile_pattern_test(&arm.pattern)?;
1418
1419            // Jump to next arm if pattern didn't match
1420            let jump_to_next = if !is_last_arm {
1421                self.emit_jump(Instruction::JumpIfFalse(0))
1422            } else {
1423                // Pop the boolean since it's not consumed by jump
1424                self.emit(Instruction::Pop);
1425                0
1426            };
1427
1428            // Pattern matched - now bind variables from the pattern
1429            // We still have the scrutinee on the stack
1430            if !is_last_arm {
1431                self.emit(Instruction::Dup); // Keep scrutinee for binding
1432            }
1433
1434            // Enter a new scope for pattern bindings
1435            self.begin_scope();
1436
1437            // Bind variables from the pattern
1438            self.compile_pattern_bindings(&arm.pattern)?;
1439
1440            // Compile arm body
1441            self.compile_expr(&arm.body)?;
1442
1443            // Exit scope for pattern bindings
1444            let locals_to_remove = self.end_scope_count();
1445            for _ in 0..locals_to_remove {
1446                self.locals.pop();
1447            }
1448            self.scope_depth -= 1;
1449
1450            // Jump to end of match expression
1451            let jump_to_end = self.emit_jump(Instruction::Jump(0));
1452            end_jumps.push(jump_to_end);
1453
1454            // Patch jump to next arm (if not last)
1455            if !is_last_arm {
1456                self.patch_jump(jump_to_next)?;
1457                // Pop the scrutinee since this arm didn't match
1458                self.emit(Instruction::Pop);
1459            }
1460        }
1461
1462        // Patch all end jumps to point here
1463        for jump_idx in end_jumps {
1464            self.patch_jump(jump_idx)?;
1465        }
1466
1467        Ok(())
1468    }
1469
1470    /// Compile a pattern test - checks if scrutinee matches pattern
1471    /// Expects scrutinee on top of stack, pushes boolean result
1472    fn compile_pattern_test(&mut self, pattern: &Pattern) -> CompileResult<()> {
1473        match pattern {
1474            Pattern::Wildcard | Pattern::Var(_) => {
1475                // Always matches - push true
1476                let true_idx = self.add_constant(Value::Bool(true))?;
1477                self.emit(Instruction::LoadConst(true_idx));
1478                Ok(())
1479            }
1480            Pattern::Literal(Literal::Int(n)) => {
1481                self.emit(Instruction::CheckInt(*n));
1482                Ok(())
1483            }
1484            Pattern::Literal(Literal::Bool(b)) => {
1485                self.emit(Instruction::CheckBool(*b));
1486                Ok(())
1487            }
1488            Pattern::Literal(Literal::Str(s)) => {
1489                self.emit(Instruction::CheckString(s.clone()));
1490                Ok(())
1491            }
1492            Pattern::Literal(Literal::Unit) => {
1493                // Check if value equals Unit
1494                let unit_idx = self.add_constant(Value::Unit)?;
1495                self.emit(Instruction::LoadConst(unit_idx));
1496                self.emit(Instruction::Eq);
1497                Ok(())
1498            }
1499            Pattern::Literal(Literal::Float(f)) => {
1500                // Check if value equals the float
1501                let float_idx = self.add_constant(Value::Float(*f))?;
1502                self.emit(Instruction::LoadConst(float_idx));
1503                self.emit(Instruction::Eq);
1504                Ok(())
1505            }
1506            Pattern::Tuple(patterns) => {
1507                // Check tuple length first
1508                self.emit(Instruction::Dup);
1509                self.emit(Instruction::CheckTupleLen(patterns.len() as u8));
1510
1511                // If not a tuple of right length, we're done (false on stack)
1512                // If it is, we need to check each element
1513                if !patterns.is_empty() {
1514                    // We have length check result on stack
1515                    // For now, simplified: just check length
1516                    // Full nested pattern checking would require more complex control flow
1517                }
1518
1519                Ok(())
1520            }
1521            Pattern::Variant {
1522                variant,
1523                patterns: _,
1524            } => {
1525                // Check variant tag
1526                self.emit(Instruction::Dup);
1527                self.emit(Instruction::CheckVariantTag(variant.clone()));
1528                Ok(())
1529            }
1530        }
1531    }
1532
1533    /// Compile pattern bindings - extracts values from scrutinee and stores in locals
1534    /// Expects scrutinee on top of stack, consumes it
1535    fn compile_pattern_bindings(&mut self, pattern: &Pattern) -> CompileResult<()> {
1536        match pattern {
1537            Pattern::Wildcard | Pattern::Literal(_) => {
1538                // No bindings, just pop the scrutinee
1539                self.emit(Instruction::Pop);
1540                Ok(())
1541            }
1542            Pattern::Var(name) => {
1543                // Bind the scrutinee to this variable
1544                self.add_local(name.clone())?;
1545                let local_idx = (self.locals.len() - 1) as u8;
1546                self.emit(Instruction::StoreLocal(local_idx));
1547                Ok(())
1548            }
1549            Pattern::Tuple(patterns) => {
1550                // Extract each element and bind recursively
1551                for (i, pat) in patterns.iter().enumerate() {
1552                    self.emit(Instruction::Dup); // Dup the tuple
1553                    self.emit(Instruction::GetTupleElem(i as u8)); // Get element
1554                    self.compile_pattern_bindings(pat)?; // Bind it
1555                }
1556                // Pop the original tuple
1557                self.emit(Instruction::Pop);
1558                Ok(())
1559            }
1560            Pattern::Variant {
1561                variant: _,
1562                patterns,
1563            } => {
1564                // Extract each field from the variant and bind recursively
1565                for (i, pat) in patterns.iter().enumerate() {
1566                    self.emit(Instruction::Dup); // Dup the variant
1567                    self.emit(Instruction::GetVariantField(i as u8)); // Get field
1568                    self.compile_pattern_bindings(pat)?; // Bind it
1569                }
1570                // Pop the original variant
1571                self.emit(Instruction::Pop);
1572                Ok(())
1573            }
1574        }
1575    }
1576
1577    /// Emit an instruction
1578    /// Compile a variant construction expression
1579    /// Stack effect: pushes a variant value
1580    fn compile_variant_construct(
1581        &mut self,
1582        type_name: &str,
1583        variant_name: &str,
1584        fields: &[Box<Expr>],
1585    ) -> CompileResult<()> {
1586        // Check if field count fits in u16
1587        if fields.len() > u16::MAX as usize {
1588            return Err(CompileError::TupleTooLarge); // Reuse error for now
1589        }
1590
1591        // Push type name as a string constant
1592        let type_name_idx = self.add_constant(Value::Str(type_name.to_string()))?;
1593        self.emit(Instruction::LoadConst(type_name_idx));
1594
1595        // Push variant name as a string constant
1596        let variant_name_idx = self.add_constant(Value::Str(variant_name.to_string()))?;
1597        self.emit(Instruction::LoadConst(variant_name_idx));
1598
1599        // Compile each field expression
1600        for field in fields {
1601            self.compile_expr(field)?;
1602        }
1603
1604        // Emit MakeVariant instruction with field count
1605        let field_count = fields.len() as u16;
1606        self.emit(Instruction::MakeVariant(field_count));
1607
1608        Ok(())
1609    }
1610
1611    fn emit(&mut self, instruction: Instruction) {
1612        self.chunk.emit(instruction);
1613    }
1614
1615    /// Emit a jump instruction and return its index for later patching
1616    fn emit_jump(&mut self, instruction: Instruction) -> usize {
1617        self.emit(instruction);
1618        self.chunk.current_offset() - 1
1619    }
1620
1621    /// Patch a jump instruction with the correct offset
1622    fn patch_jump(&mut self, jump_index: usize) -> CompileResult<()> {
1623        // Calculate the offset from the jump instruction to the current position
1624        let jump_offset = self.chunk.current_offset() - jump_index - 1;
1625
1626        // Check if offset fits in i16
1627        if jump_offset > i16::MAX as usize {
1628            return Err(CompileError::InvalidJumpOffset);
1629        }
1630
1631        // Get the current instruction and patch it
1632        match self.chunk.instructions[jump_index] {
1633            Instruction::Jump(_) => {
1634                self.chunk.instructions[jump_index] = Instruction::Jump(jump_offset as i16);
1635            }
1636            Instruction::JumpIfFalse(_) => {
1637                self.chunk.instructions[jump_index] = Instruction::JumpIfFalse(jump_offset as i16);
1638            }
1639            _ => unreachable!("patch_jump called on non-jump instruction"),
1640        }
1641
1642        Ok(())
1643    }
1644
1645    /// Add a constant to the constant pool and return its index
1646    fn add_constant(&mut self, value: Value) -> CompileResult<u16> {
1647        let count = self.chunk.constants.len();
1648        if count >= u16::MAX as usize {
1649            return Err(CompileError::TooManyConstants);
1650        }
1651        let idx = self.chunk.add_constant(value);
1652        Ok(idx)
1653    }
1654    /// Begin a new scope
1655    fn begin_scope(&mut self) {
1656        self.scope_depth += 1;
1657    }
1658
1659    /// Count locals to be removed when ending current scope
1660    fn end_scope_count(&self) -> usize {
1661        self.locals
1662            .iter()
1663            .rev()
1664            .take_while(|local| local.depth > self.scope_depth - 1)
1665            .count()
1666    }
1667
1668    /// Add a local variable
1669    fn add_local(&mut self, name: String) -> CompileResult<()> {
1670        if self.locals.len() >= u8::MAX as usize {
1671            return Err(CompileError::TooManyLocals);
1672        }
1673
1674        self.locals.push(Local {
1675            name,
1676            depth: self.scope_depth,
1677        });
1678
1679        Ok(())
1680    }
1681    /// Compile a record literal expression
1682    /// Stack effect: pushes a record value
1683    fn compile_record_literal(&mut self, fields: &[(String, Box<Expr>)]) -> CompileResult<()> {
1684        // Check if record size fits in u16
1685        if fields.len() > u16::MAX as usize {
1686            return Err(CompileError::TupleTooLarge); // Reuse error for now, or add RecordTooLarge
1687        }
1688
1689        // Compile each field (field_name, field_value) pair
1690        // Stack layout: [field_name_1, field_value_1, field_name_2, field_value_2, ...]
1691        for (field_name, field_value) in fields {
1692            // Push field name as a string constant
1693            let field_name_idx = self.add_constant(Value::Str(field_name.clone()))?;
1694            self.emit(Instruction::LoadConst(field_name_idx));
1695
1696            // Compile and push field value
1697            self.compile_expr(field_value)?;
1698        }
1699
1700        // Emit MakeRecord instruction with field count
1701        let field_count = fields.len() as u16;
1702        self.emit(Instruction::MakeRecord(field_count));
1703
1704        Ok(())
1705    }
1706
1707    /// Compile a record field access expression
1708    /// Stack effect: pushes the field value
1709    fn compile_record_access(&mut self, record: &Expr, field: &str) -> CompileResult<()> {
1710        // Compile the record expression
1711        self.compile_expr(record)?;
1712
1713        // Push field name as a string constant
1714        let field_name_idx = self.add_constant(Value::Str(field.to_string()))?;
1715        self.emit(Instruction::LoadConst(field_name_idx));
1716
1717        // Emit GetRecordField instruction
1718        self.emit(Instruction::GetRecordField);
1719
1720        Ok(())
1721    }
1722
1723    /// Compile a record update expression (immutable)
1724    /// Stack effect: pushes a new record with updated fields
1725    fn compile_record_update(
1726        &mut self,
1727        record: &Expr,
1728        fields: &[(String, Box<Expr>)],
1729    ) -> CompileResult<()> {
1730        // Check if update size fits in u16
1731        if fields.len() > u16::MAX as usize {
1732            return Err(CompileError::TupleTooLarge); // Reuse error for now
1733        }
1734
1735        // Compile the base record expression
1736        self.compile_expr(record)?;
1737
1738        // Compile each update (field_name, new_value) pair
1739        // Stack layout: [record, field_name_1, new_value_1, field_name_2, new_value_2, ...]
1740        for (field_name, field_value) in fields {
1741            // Push field name as a string constant
1742            let field_name_idx = self.add_constant(Value::Str(field_name.clone()))?;
1743            self.emit(Instruction::LoadConst(field_name_idx));
1744
1745            // Compile and push new field value
1746            self.compile_expr(field_value)?;
1747        }
1748
1749        // Emit UpdateRecord instruction with update count
1750        let update_count = fields.len() as u16;
1751        self.emit(Instruction::UpdateRecord(update_count));
1752
1753        Ok(())
1754    }
1755
1756    /// Compile a method call expression
1757    /// Stack effect: pushes the result of the method call
1758    fn compile_method_call(
1759        &mut self,
1760        receiver: &Expr,
1761        method_name: &str,
1762        args: &[Expr],
1763    ) -> CompileResult<()> {
1764        // Check if arg count fits in u8
1765        if args.len() > u8::MAX as usize {
1766            return Err(CompileError::CodeGenError(
1767                "Too many method arguments (max 255)".to_string(),
1768            ));
1769        }
1770
1771        // Special case: if receiver is a simple variable, check if "receiver.method_name"
1772        // could be a module function (e.g., db.append where db is a module namespace)
1773        // In that case, compile it as a regular function call instead of a method call
1774        if let Expr::Var(module_name) = receiver {
1775            let qualified_name = format!("{}.{}", module_name, method_name);
1776
1777            // Try to resolve it in the module registry first
1778            let has_module_binding = self
1779                .module_registry
1780                .as_ref()
1781                .and_then(|r| r.resolve_qualified(module_name, method_name))
1782                .is_some();
1783
1784            // If not in module registry, assume it could be a runtime-registered host function
1785            // and compile as a function call instead of a method call
1786            if !has_module_binding {
1787                // Load the function by qualified name
1788                let name_idx = self.add_constant(Value::Str(qualified_name))?;
1789                self.emit(Instruction::LoadGlobal(name_idx));
1790
1791                // Compile argument expressions
1792                for arg in args {
1793                    self.compile_expr(arg)?;
1794                }
1795
1796                // Emit function call instruction
1797                let arg_count = args.len() as u8;
1798                self.emit(Instruction::Call(arg_count));
1799
1800                return Ok(());
1801            }
1802        }
1803
1804        // Standard method call compilation
1805        // Compile receiver expression
1806        self.compile_expr(receiver)?;
1807
1808        // Compile argument expressions
1809        for arg in args {
1810            self.compile_expr(arg)?;
1811        }
1812
1813        // Store method name in constants pool
1814        let method_name_idx = self.add_constant(Value::Str(method_name.to_string()))?;
1815
1816        // Emit CallMethod instruction
1817        let arg_count = args.len() as u8;
1818        self.emit(Instruction::CallMethod(method_name_idx, arg_count));
1819
1820        Ok(())
1821    }
1822}
1823
1824/// Parse a qualified name into module path and binding name
1825///
1826/// Examples:
1827/// - "Math.add" -> (["Math"], "add")
1828/// - "Geometry.Point.x" -> (["Geometry", "Point"], "x")
1829fn parse_qualified_name(name: &str) -> Option<(Vec<String>, String)> {
1830    if name.contains('.') {
1831        let parts: Vec<&str> = name.split('.').collect();
1832        if parts.len() >= 2 {
1833            let module_path: Vec<String> = parts[..parts.len() - 1]
1834                .iter()
1835                .map(|s| s.to_string())
1836                .collect();
1837            let binding_name = parts[parts.len() - 1].to_string();
1838            return Some((module_path, binding_name));
1839        }
1840    }
1841    None
1842}
1843
1844// Note: Tests remain unchanged from original file
1845// They validate the compiler works with default options (no type checking)
1846#[cfg(test)]
1847mod tests {
1848    use super::*;
1849
1850    // Existing tests validate backward compatibility
1851    #[test]
1852    fn test_compile_options_default() {
1853        let options = CompileOptions::default();
1854        assert!(!options.enable_type_checking);
1855        assert!(!options.strict_mode);
1856        assert!(options.allow_warnings);
1857    }
1858
1859    #[test]
1860    fn test_compile_with_type_checking() {
1861        let expr = Expr::Lit(Literal::Int(42));
1862        let result = Compiler::compile_checked(&expr);
1863        assert!(result.is_ok());
1864    }
1865
1866    #[test]
1867    fn test_compile_backwards_compatible() {
1868        let expr = Expr::Lit(Literal::Int(42));
1869        let chunk = Compiler::compile(&expr).unwrap();
1870        assert_eq!(chunk.constants.len(), 1);
1871        assert_eq!(chunk.constants[0], Value::Int(42));
1872    }
1873
1874    #[test]
1875    fn test_parse_qualified_name_simple() {
1876        let result = parse_qualified_name("Math.add");
1877        assert!(result.is_some());
1878        let (path, name) = result.unwrap();
1879        assert_eq!(path, vec!["Math".to_string()]);
1880        assert_eq!(name, "add");
1881    }
1882
1883    #[test]
1884    fn test_parse_qualified_name_nested() {
1885        let result = parse_qualified_name("Geometry.Point.x");
1886        assert!(result.is_some());
1887        let (path, name) = result.unwrap();
1888        assert_eq!(path, vec!["Geometry".to_string(), "Point".to_string()]);
1889        assert_eq!(name, "x");
1890    }
1891
1892    #[test]
1893    fn test_parse_qualified_name_unqualified() {
1894        let result = parse_qualified_name("add");
1895        assert!(result.is_none());
1896    }
1897}