Skip to main content

zapcode_core/compiler/
mod.rs

1pub mod instruction;
2
3use std::collections::{HashMap, HashSet};
4
5use crate::error::{Result, ZapcodeError};
6use crate::parser::ir::*;
7use instruction::*;
8
9/// Compiled program ready for VM execution.
10#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
11pub struct CompiledProgram {
12    pub instructions: Vec<Instruction>,
13    pub functions: Vec<CompiledFunction>,
14    pub local_names: Vec<String>,
15}
16
17#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
18pub struct CompiledFunction {
19    pub name: Option<String>,
20    pub params: Vec<ParamPattern>,
21    pub instructions: Vec<Instruction>,
22    pub local_count: usize,
23    pub local_names: Vec<String>,
24    pub is_async: bool,
25    pub is_generator: bool,
26}
27
28struct Compiler {
29    instructions: Vec<Instruction>,
30    locals: Vec<String>,
31    local_indices: HashMap<String, usize>,
32    functions: Vec<CompiledFunction>,
33    loop_stack: Vec<LoopInfo>,
34    external_functions: HashSet<String>,
35}
36
37struct LoopInfo {
38    break_patches: Vec<usize>,
39    continue_patches: Vec<usize>,
40}
41
42impl Compiler {
43    fn new(external_functions: HashSet<String>) -> Self {
44        Self {
45            instructions: Vec::new(),
46            locals: Vec::new(),
47            local_indices: HashMap::new(),
48            functions: Vec::new(),
49            loop_stack: Vec::new(),
50            external_functions,
51        }
52    }
53
54    fn emit(&mut self, instr: Instruction) -> usize {
55        let idx = self.instructions.len();
56        self.instructions.push(instr);
57        idx
58    }
59
60    fn current_offset(&self) -> usize {
61        self.instructions.len()
62    }
63
64    fn patch_jump(&mut self, instr_idx: usize, target: usize) {
65        match &mut self.instructions[instr_idx] {
66            Instruction::Jump(t)
67            | Instruction::JumpIfFalse(t)
68            | Instruction::JumpIfTrue(t)
69            | Instruction::JumpIfNullish(t) => {
70                *t = target;
71            }
72            Instruction::SetupTry(catch_target, _) => {
73                *catch_target = target;
74            }
75            _ => {}
76        }
77    }
78
79    fn declare_local(&mut self, name: &str) -> usize {
80        if let Some(&idx) = self.local_indices.get(name) {
81            return idx;
82        }
83        let idx = self.locals.len();
84        self.locals.push(name.to_string());
85        self.local_indices.insert(name.to_string(), idx);
86        idx
87    }
88
89    fn resolve_local(&self, name: &str) -> Option<usize> {
90        self.local_indices.get(name).copied()
91    }
92
93    fn compile_program(&mut self, program: &Program) -> Result<()> {
94        // First pass: compile all function definitions
95        for func_def in &program.functions {
96            let compiled = self.compile_function_def(func_def)?;
97            self.functions.push(compiled);
98        }
99
100        // Second pass: compile body
101        // For the last statement, if it's an expression, keep the value on the stack
102        let len = program.body.len();
103        for (i, stmt) in program.body.iter().enumerate() {
104            let is_last = i == len - 1;
105            if is_last {
106                if let Statement::Expression { expr, .. } = stmt {
107                    self.compile_expr(expr)?;
108                    // Don't pop — leave value on stack as program result
109                } else {
110                    self.compile_statement(stmt)?;
111                }
112            } else {
113                self.compile_statement(stmt)?;
114            }
115        }
116
117        Ok(())
118    }
119
120    fn compile_function_def(&mut self, func: &FunctionDef) -> Result<CompiledFunction> {
121        let mut func_compiler = Compiler::new(self.external_functions.clone());
122
123        // Set up parameters as locals
124        for param in &func.params {
125            match param {
126                ParamPattern::Ident(name) => {
127                    func_compiler.declare_local(name);
128                }
129                ParamPattern::Rest(name) => {
130                    func_compiler.declare_local(name);
131                }
132                ParamPattern::DefaultValue { pattern, .. } => {
133                    if let ParamPattern::Ident(name) = pattern.as_ref() {
134                        func_compiler.declare_local(name);
135                    }
136                }
137                ParamPattern::ObjectDestructure(fields) => {
138                    for field in fields {
139                        let name = field.alias.as_ref().unwrap_or(&field.key);
140                        func_compiler.declare_local(name);
141                    }
142                }
143                ParamPattern::ArrayDestructure(elems) => {
144                    for elem in elems.iter().flatten() {
145                        if let ParamPattern::Ident(name) = elem {
146                            func_compiler.declare_local(name);
147                        }
148                    }
149                }
150            }
151        }
152
153        for stmt in &func.body {
154            func_compiler.compile_statement(stmt)?;
155        }
156
157        // Implicit return undefined
158        func_compiler.emit(Instruction::Push(Constant::Undefined));
159        func_compiler.emit(Instruction::Return);
160
161        Ok(CompiledFunction {
162            name: func.name.clone(),
163            params: func.params.clone(),
164            instructions: func_compiler.instructions,
165            local_count: func_compiler.locals.len(),
166            local_names: func_compiler.locals,
167            is_async: func.is_async,
168            is_generator: func.is_generator,
169        })
170    }
171
172    fn compile_statement(&mut self, stmt: &Statement) -> Result<()> {
173        match stmt {
174            Statement::VariableDecl { declarations, .. } => {
175                for decl in declarations {
176                    self.compile_var_declarator(decl)?;
177                }
178            }
179            Statement::Expression { expr, .. } => {
180                self.compile_expr(expr)?;
181                self.emit(Instruction::Pop);
182            }
183            Statement::Return { value, .. } => {
184                match value {
185                    Some(expr) => self.compile_expr(expr)?,
186                    None => {
187                        self.emit(Instruction::Push(Constant::Undefined));
188                    }
189                }
190                self.emit(Instruction::Return);
191            }
192            Statement::If {
193                test,
194                consequent,
195                alternate,
196                ..
197            } => {
198                self.compile_expr(test)?;
199                let jump_else = self.emit(Instruction::JumpIfFalse(0));
200
201                for s in consequent {
202                    self.compile_statement(s)?;
203                }
204
205                if let Some(alt) = alternate {
206                    let jump_end = self.emit(Instruction::Jump(0));
207                    let else_target = self.current_offset();
208                    self.patch_jump(jump_else, else_target);
209
210                    for s in alt {
211                        self.compile_statement(s)?;
212                    }
213                    let end_target = self.current_offset();
214                    self.patch_jump(jump_end, end_target);
215                } else {
216                    let else_target = self.current_offset();
217                    self.patch_jump(jump_else, else_target);
218                }
219            }
220            Statement::While { test, body, .. } => {
221                let loop_start = self.current_offset();
222                self.loop_stack.push(LoopInfo {
223                    break_patches: Vec::new(),
224                    continue_patches: Vec::new(),
225                });
226
227                self.compile_expr(test)?;
228                let exit_jump = self.emit(Instruction::JumpIfFalse(0));
229
230                for s in body {
231                    self.compile_statement(s)?;
232                }
233
234                self.emit(Instruction::Jump(loop_start));
235                let loop_end = self.current_offset();
236                self.patch_jump(exit_jump, loop_end);
237
238                let loop_info = self.loop_stack.pop().unwrap();
239                for patch in loop_info.break_patches {
240                    self.patch_jump(patch, loop_end);
241                }
242                for patch in loop_info.continue_patches {
243                    self.patch_jump(patch, loop_start);
244                }
245            }
246            Statement::DoWhile { body, test, .. } => {
247                let loop_start = self.current_offset();
248                self.loop_stack.push(LoopInfo {
249                    break_patches: Vec::new(),
250                    continue_patches: Vec::new(),
251                });
252
253                for s in body {
254                    self.compile_statement(s)?;
255                }
256
257                let continue_target = self.current_offset();
258                self.compile_expr(test)?;
259                self.emit(Instruction::JumpIfTrue(loop_start));
260
261                let loop_end = self.current_offset();
262                let loop_info = self.loop_stack.pop().unwrap();
263                for patch in loop_info.break_patches {
264                    self.patch_jump(patch, loop_end);
265                }
266                for patch in loop_info.continue_patches {
267                    self.patch_jump(patch, continue_target);
268                }
269            }
270            Statement::For {
271                init,
272                test,
273                update,
274                body,
275                ..
276            } => {
277                if let Some(init) = init {
278                    self.compile_statement(init)?;
279                }
280
281                let loop_start = self.current_offset();
282                self.loop_stack.push(LoopInfo {
283                    break_patches: Vec::new(),
284                    continue_patches: Vec::new(),
285                });
286
287                let exit_jump = if let Some(test) = test {
288                    self.compile_expr(test)?;
289                    Some(self.emit(Instruction::JumpIfFalse(0)))
290                } else {
291                    None
292                };
293
294                for s in body {
295                    self.compile_statement(s)?;
296                }
297
298                let continue_target = self.current_offset();
299                if let Some(update) = update {
300                    self.compile_expr(update)?;
301                    self.emit(Instruction::Pop);
302                }
303
304                self.emit(Instruction::Jump(loop_start));
305                let loop_end = self.current_offset();
306
307                if let Some(exit) = exit_jump {
308                    self.patch_jump(exit, loop_end);
309                }
310
311                let loop_info = self.loop_stack.pop().unwrap();
312                for patch in loop_info.break_patches {
313                    self.patch_jump(patch, loop_end);
314                }
315                for patch in loop_info.continue_patches {
316                    self.patch_jump(patch, continue_target);
317                }
318            }
319            Statement::ForOf {
320                binding,
321                iterable,
322                body,
323                ..
324            } => {
325                self.compile_expr(iterable)?;
326                self.emit(Instruction::GetIterator);
327
328                let loop_start = self.current_offset();
329                self.loop_stack.push(LoopInfo {
330                    break_patches: Vec::new(),
331                    continue_patches: Vec::new(),
332                });
333
334                self.emit(Instruction::Dup);
335                self.emit(Instruction::IteratorNext);
336                self.emit(Instruction::IteratorDone);
337                let exit_jump = self.emit(Instruction::JumpIfTrue(0));
338
339                // Bind the value
340                match binding {
341                    ForBinding::Ident(name) => {
342                        let idx = self.declare_local(name);
343                        self.emit(Instruction::StoreLocal(idx));
344                    }
345                    ForBinding::Destructure(_) => {
346                        self.emit(Instruction::Pop); // TODO: destructure
347                    }
348                }
349
350                for s in body {
351                    self.compile_statement(s)?;
352                }
353
354                self.emit(Instruction::Jump(loop_start));
355                let loop_end = self.current_offset();
356                self.patch_jump(exit_jump, loop_end);
357                self.emit(Instruction::Pop); // pop iterator
358
359                let loop_info = self.loop_stack.pop().unwrap();
360                for patch in loop_info.break_patches {
361                    self.patch_jump(patch, loop_end);
362                }
363                for patch in loop_info.continue_patches {
364                    self.patch_jump(patch, loop_start);
365                }
366            }
367            Statement::Block { body, .. } => {
368                for s in body {
369                    self.compile_statement(s)?;
370                }
371            }
372            Statement::Throw { value, .. } => {
373                self.compile_expr(value)?;
374                self.emit(Instruction::Throw);
375            }
376            Statement::TryCatch {
377                try_body,
378                catch_param,
379                catch_body,
380                finally_body,
381                ..
382            } => {
383                let setup = self.emit(Instruction::SetupTry(0, None));
384
385                for s in try_body {
386                    self.compile_statement(s)?;
387                }
388                self.emit(Instruction::EndTry);
389                let jump_past_catch = self.emit(Instruction::Jump(0));
390
391                // Catch block
392                let catch_start = self.current_offset();
393                self.patch_jump(setup, catch_start);
394
395                if let Some(param) = catch_param {
396                    let idx = self.declare_local(param);
397                    self.emit(Instruction::StoreLocal(idx));
398                } else {
399                    self.emit(Instruction::Pop); // discard error
400                }
401
402                for s in catch_body {
403                    self.compile_statement(s)?;
404                }
405
406                let after_catch = self.current_offset();
407                self.patch_jump(jump_past_catch, after_catch);
408
409                if let Some(finally) = finally_body {
410                    for s in finally {
411                        self.compile_statement(s)?;
412                    }
413                }
414            }
415            Statement::Break { .. } => {
416                let idx = self.emit(Instruction::Jump(0));
417                if let Some(loop_info) = self.loop_stack.last_mut() {
418                    loop_info.break_patches.push(idx);
419                }
420            }
421            Statement::Continue { .. } => {
422                let idx = self.emit(Instruction::Jump(0));
423                if let Some(loop_info) = self.loop_stack.last_mut() {
424                    loop_info.continue_patches.push(idx);
425                }
426            }
427            Statement::FunctionDecl { func_index, .. } => {
428                self.emit(Instruction::CreateClosure(*func_index));
429                let name = if *func_index < self.functions.len() {
430                    self.functions[*func_index].name.clone()
431                } else {
432                    None
433                };
434                if let Some(name) = name {
435                    // Store as both local and global so recursion works
436                    self.emit(Instruction::Dup);
437                    let idx = self.declare_local(&name);
438                    self.emit(Instruction::StoreLocal(idx));
439                    self.emit(Instruction::StoreGlobal(name));
440                } else {
441                    self.emit(Instruction::Pop);
442                }
443            }
444            Statement::ClassDecl {
445                name,
446                super_class,
447                constructor,
448                methods,
449                static_methods,
450                ..
451            } => {
452                self.compile_class(
453                    Some(name),
454                    super_class.as_deref(),
455                    constructor.as_deref(),
456                    methods,
457                    static_methods,
458                )?;
459                // Store the class as both local and global
460                self.emit(Instruction::Dup);
461                let idx = self.declare_local(name);
462                self.emit(Instruction::StoreLocal(idx));
463                self.emit(Instruction::StoreGlobal(name.clone()));
464            }
465            Statement::Switch {
466                discriminant,
467                cases,
468                ..
469            } => {
470                self.compile_expr(discriminant)?;
471                let mut case_jumps = Vec::new();
472                let mut default_jump = None;
473
474                // Compile test expressions and jumps
475                for case in cases {
476                    if let Some(test) = &case.test {
477                        self.emit(Instruction::Dup);
478                        self.compile_expr(test)?;
479                        self.emit(Instruction::StrictEq);
480                        let jump = self.emit(Instruction::JumpIfTrue(0));
481                        case_jumps.push(jump);
482                    } else {
483                        default_jump = Some(case_jumps.len());
484                        case_jumps.push(0); // placeholder
485                    }
486                }
487
488                let jump_end = self.emit(Instruction::Jump(0));
489
490                // Compile case bodies
491                let mut body_starts = Vec::new();
492                for case in cases {
493                    body_starts.push(self.current_offset());
494                    for s in &case.consequent {
495                        self.compile_statement(s)?;
496                    }
497                }
498
499                let end = self.current_offset();
500                self.emit(Instruction::Pop); // pop discriminant
501
502                // Patch jumps
503                for (i, &jump) in case_jumps.iter().enumerate() {
504                    if jump != 0 {
505                        self.patch_jump(jump, body_starts[i]);
506                    }
507                }
508                if let Some(default_idx) = default_jump {
509                    // Jump to default case
510                    self.patch_jump(jump_end, body_starts[default_idx]);
511                } else {
512                    self.patch_jump(jump_end, end);
513                }
514            }
515        }
516        Ok(())
517    }
518
519    fn compile_var_declarator(&mut self, decl: &VarDeclarator) -> Result<()> {
520        match &decl.pattern {
521            AssignTarget::Ident(name) => {
522                let idx = self.declare_local(name);
523                match &decl.init {
524                    Some(expr) => {
525                        self.compile_expr(expr)?;
526                        self.emit(Instruction::StoreLocal(idx));
527                    }
528                    None => {
529                        self.emit(Instruction::Push(Constant::Undefined));
530                        self.emit(Instruction::StoreLocal(idx));
531                    }
532                }
533            }
534            AssignTarget::ObjectDestructure(fields) => {
535                if let Some(expr) = &decl.init {
536                    self.compile_expr(expr)?;
537                } else {
538                    self.emit(Instruction::Push(Constant::Undefined));
539                }
540                for field in fields {
541                    self.emit(Instruction::Dup);
542                    self.emit(Instruction::GetProperty(field.key.clone()));
543                    let name = field.alias.as_ref().unwrap_or(&field.key);
544                    let idx = self.declare_local(name);
545                    self.emit(Instruction::StoreLocal(idx));
546                }
547                self.emit(Instruction::Pop); // pop source object
548            }
549            AssignTarget::ArrayDestructure(elems) => {
550                if let Some(expr) = &decl.init {
551                    self.compile_expr(expr)?;
552                } else {
553                    self.emit(Instruction::Push(Constant::Undefined));
554                }
555                for (i, elem) in elems.iter().enumerate() {
556                    if let Some(target) = elem {
557                        self.emit(Instruction::Dup);
558                        self.emit(Instruction::Push(Constant::Int(i as i64)));
559                        self.emit(Instruction::GetIndex);
560                        match target {
561                            AssignTarget::Ident(name) => {
562                                let idx = self.declare_local(name);
563                                self.emit(Instruction::StoreLocal(idx));
564                            }
565                            _ => {
566                                self.emit(Instruction::Pop); // TODO: nested destructure
567                            }
568                        }
569                    }
570                }
571                self.emit(Instruction::Pop); // pop source array
572            }
573        }
574        Ok(())
575    }
576
577    fn compile_expr(&mut self, expr: &Expr) -> Result<()> {
578        match expr {
579            Expr::NumberLit(n) => {
580                if *n == (*n as i64) as f64 && !n.is_nan() && n.is_finite() {
581                    self.emit(Instruction::Push(Constant::Int(*n as i64)));
582                } else {
583                    self.emit(Instruction::Push(Constant::Float(*n)));
584                }
585            }
586            Expr::StringLit(s) => {
587                self.emit(Instruction::Push(Constant::String(s.clone())));
588            }
589            Expr::BoolLit(b) => {
590                self.emit(Instruction::Push(Constant::Bool(*b)));
591            }
592            Expr::NullLit => {
593                self.emit(Instruction::Push(Constant::Null));
594            }
595            Expr::UndefinedLit => {
596                self.emit(Instruction::Push(Constant::Undefined));
597            }
598            Expr::TemplateLit { quasis, exprs } => {
599                let mut parts = 0;
600                for (i, quasi) in quasis.iter().enumerate() {
601                    if !quasi.is_empty() {
602                        self.emit(Instruction::Push(Constant::String(quasi.clone())));
603                        parts += 1;
604                    }
605                    if i < exprs.len() {
606                        self.compile_expr(&exprs[i])?;
607                        parts += 1;
608                    }
609                }
610                if parts == 0 {
611                    self.emit(Instruction::Push(Constant::String(String::new())));
612                } else if parts > 1 {
613                    self.emit(Instruction::ConcatStrings(parts));
614                }
615            }
616            Expr::RegExpLit { .. } => {
617                // RegExp not fully supported — push as string for now
618                self.emit(Instruction::Push(Constant::Undefined));
619            }
620            Expr::Ident(name) => {
621                if name == "this" {
622                    self.emit(Instruction::LoadThis);
623                } else if let Some(idx) = self.resolve_local(name) {
624                    self.emit(Instruction::LoadLocal(idx));
625                } else {
626                    self.emit(Instruction::LoadGlobal(name.clone()));
627                }
628            }
629            Expr::Array(elements) => {
630                let mut count = 0;
631                for elem in elements {
632                    match elem {
633                        Some(e) => {
634                            self.compile_expr(e)?;
635                            count += 1;
636                        }
637                        None => {
638                            self.emit(Instruction::Push(Constant::Undefined));
639                            count += 1;
640                        }
641                    }
642                }
643                self.emit(Instruction::CreateArray(count));
644            }
645            Expr::Object(props) => {
646                let mut count = 0;
647                for prop in props {
648                    match prop.kind {
649                        PropKind::Spread => {
650                            self.compile_expr(&prop.value)?;
651                            self.emit(Instruction::Spread);
652                            count += 1;
653                        }
654                        _ => {
655                            self.emit(Instruction::Push(Constant::String(prop.key.clone())));
656                            self.compile_expr(&prop.value)?;
657                            count += 1;
658                        }
659                    }
660                }
661                self.emit(Instruction::CreateObject(count));
662            }
663            Expr::Spread(expr) => {
664                self.compile_expr(expr)?;
665                self.emit(Instruction::Spread);
666            }
667            Expr::Binary { op, left, right } => {
668                self.compile_expr(left)?;
669                self.compile_expr(right)?;
670                let instr = match op {
671                    BinOp::Add => Instruction::Add,
672                    BinOp::Sub => Instruction::Sub,
673                    BinOp::Mul => Instruction::Mul,
674                    BinOp::Div => Instruction::Div,
675                    BinOp::Rem => Instruction::Rem,
676                    BinOp::Pow => Instruction::Pow,
677                    BinOp::Eq => Instruction::Eq,
678                    BinOp::Neq => Instruction::Neq,
679                    BinOp::StrictEq => Instruction::StrictEq,
680                    BinOp::StrictNeq => Instruction::StrictNeq,
681                    BinOp::Lt => Instruction::Lt,
682                    BinOp::Lte => Instruction::Lte,
683                    BinOp::Gt => Instruction::Gt,
684                    BinOp::Gte => Instruction::Gte,
685                    BinOp::BitAnd => Instruction::BitAnd,
686                    BinOp::BitOr => Instruction::BitOr,
687                    BinOp::BitXor => Instruction::BitXor,
688                    BinOp::Shl => Instruction::Shl,
689                    BinOp::Shr => Instruction::Shr,
690                    BinOp::Ushr => Instruction::Ushr,
691                    BinOp::In => Instruction::In,
692                    BinOp::InstanceOf => Instruction::InstanceOf,
693                };
694                self.emit(instr);
695            }
696            Expr::Unary { op, operand } => {
697                self.compile_expr(operand)?;
698                match op {
699                    UnaryOp::Neg => {
700                        self.emit(Instruction::Neg);
701                    }
702                    UnaryOp::Not => {
703                        self.emit(Instruction::Not);
704                    }
705                    UnaryOp::BitNot => {
706                        self.emit(Instruction::BitNot);
707                    }
708                    UnaryOp::Void => {
709                        self.emit(Instruction::Void);
710                    }
711                }
712            }
713            Expr::Update {
714                op,
715                prefix,
716                operand,
717            } => {
718                // Load current value
719                self.compile_expr(operand)?;
720
721                if !prefix {
722                    self.emit(Instruction::Dup); // keep pre-value
723                }
724
725                match op {
726                    UpdateOp::Increment => {
727                        self.emit(Instruction::Increment);
728                    }
729                    UpdateOp::Decrement => {
730                        self.emit(Instruction::Decrement);
731                    }
732                }
733
734                if *prefix {
735                    self.emit(Instruction::Dup); // keep post-value
736                }
737
738                // Store back
739                self.compile_store(operand)?;
740
741                if !prefix {
742                    // Swap to get pre-value on top
743                    // Actually the dup before increment already has it
744                }
745            }
746            Expr::Logical { op, left, right } => match op {
747                LogicalOp::And => {
748                    self.compile_expr(left)?;
749                    self.emit(Instruction::Dup);
750                    let skip = self.emit(Instruction::JumpIfFalse(0));
751                    self.emit(Instruction::Pop);
752                    self.compile_expr(right)?;
753                    let end = self.current_offset();
754                    self.patch_jump(skip, end);
755                }
756                LogicalOp::Or => {
757                    self.compile_expr(left)?;
758                    self.emit(Instruction::Dup);
759                    let skip = self.emit(Instruction::JumpIfTrue(0));
760                    self.emit(Instruction::Pop);
761                    self.compile_expr(right)?;
762                    let end = self.current_offset();
763                    self.patch_jump(skip, end);
764                }
765                LogicalOp::NullishCoalescing => {
766                    self.compile_expr(left)?;
767                    self.emit(Instruction::Dup);
768                    let skip = self.emit(Instruction::JumpIfNullish(0));
769                    let jump_end = self.emit(Instruction::Jump(0));
770                    let nullish_target = self.current_offset();
771                    self.patch_jump(skip, nullish_target);
772                    self.emit(Instruction::Pop);
773                    self.compile_expr(right)?;
774                    let end = self.current_offset();
775                    self.patch_jump(jump_end, end);
776                }
777            },
778            Expr::Conditional {
779                test,
780                consequent,
781                alternate,
782            } => {
783                self.compile_expr(test)?;
784                let jump_else = self.emit(Instruction::JumpIfFalse(0));
785                self.compile_expr(consequent)?;
786                let jump_end = self.emit(Instruction::Jump(0));
787                let else_target = self.current_offset();
788                self.patch_jump(jump_else, else_target);
789                self.compile_expr(alternate)?;
790                let end = self.current_offset();
791                self.patch_jump(jump_end, end);
792            }
793            Expr::Assignment { op, target, value } => {
794                match op {
795                    AssignOp::Assign => {
796                        self.compile_expr(value)?;
797                        self.emit(Instruction::Dup);
798                        self.compile_store(target)?;
799                    }
800                    _ => {
801                        // Compound assignment: load, operate, store
802                        self.compile_expr(target)?;
803                        self.compile_expr(value)?;
804                        match op {
805                            AssignOp::AddAssign => {
806                                self.emit(Instruction::Add);
807                            }
808                            AssignOp::SubAssign => {
809                                self.emit(Instruction::Sub);
810                            }
811                            AssignOp::MulAssign => {
812                                self.emit(Instruction::Mul);
813                            }
814                            AssignOp::DivAssign => {
815                                self.emit(Instruction::Div);
816                            }
817                            AssignOp::RemAssign => {
818                                self.emit(Instruction::Rem);
819                            }
820                            AssignOp::PowAssign => {
821                                self.emit(Instruction::Pow);
822                            }
823                            AssignOp::BitAndAssign => {
824                                self.emit(Instruction::BitAnd);
825                            }
826                            AssignOp::BitOrAssign => {
827                                self.emit(Instruction::BitOr);
828                            }
829                            AssignOp::BitXorAssign => {
830                                self.emit(Instruction::BitXor);
831                            }
832                            AssignOp::ShlAssign => {
833                                self.emit(Instruction::Shl);
834                            }
835                            AssignOp::ShrAssign => {
836                                self.emit(Instruction::Shr);
837                            }
838                            AssignOp::UshrAssign => {
839                                self.emit(Instruction::Ushr);
840                            }
841                            _ => {}
842                        }
843                        self.emit(Instruction::Dup);
844                        self.compile_store(target)?;
845                    }
846                }
847            }
848            Expr::Sequence(exprs) => {
849                for (i, e) in exprs.iter().enumerate() {
850                    self.compile_expr(e)?;
851                    if i < exprs.len() - 1 {
852                        self.emit(Instruction::Pop);
853                    }
854                }
855            }
856            Expr::Member {
857                object,
858                property,
859                optional,
860            } => {
861                self.compile_expr(object)?;
862                if *optional {
863                    self.emit(Instruction::Dup);
864                    let skip = self.emit(Instruction::JumpIfNullish(0));
865                    self.emit(Instruction::GetProperty(property.clone()));
866                    let end = self.emit(Instruction::Jump(0));
867                    let nullish = self.current_offset();
868                    self.patch_jump(skip, nullish);
869                    self.emit(Instruction::Pop);
870                    self.emit(Instruction::Push(Constant::Undefined));
871                    let after = self.current_offset();
872                    self.patch_jump(end, after);
873                } else {
874                    self.emit(Instruction::GetProperty(property.clone()));
875                }
876            }
877            Expr::ComputedMember {
878                object,
879                property,
880                optional,
881            } => {
882                self.compile_expr(object)?;
883                if *optional {
884                    self.emit(Instruction::Dup);
885                    let skip = self.emit(Instruction::JumpIfNullish(0));
886                    self.compile_expr(property)?;
887                    self.emit(Instruction::GetIndex);
888                    let end = self.emit(Instruction::Jump(0));
889                    let nullish = self.current_offset();
890                    self.patch_jump(skip, nullish);
891                    self.emit(Instruction::Pop);
892                    self.emit(Instruction::Push(Constant::Undefined));
893                    let after = self.current_offset();
894                    self.patch_jump(end, after);
895                } else {
896                    self.compile_expr(property)?;
897                    self.emit(Instruction::GetIndex);
898                }
899            }
900            Expr::Call { callee, args, .. } => {
901                // Check if this is a super() call
902                if let Expr::Ident(name) = callee.as_ref() {
903                    if name == "super" {
904                        for arg in args {
905                            self.compile_expr(arg)?;
906                        }
907                        self.emit(Instruction::CallSuper(args.len()));
908                        return Ok(());
909                    }
910                }
911                // Check if this is a direct call to an external function
912                if let Expr::Ident(name) = callee.as_ref() {
913                    if self.external_functions.contains(name) {
914                        // Emit args then CallExternal (no callee push needed)
915                        for arg in args {
916                            self.compile_expr(arg)?;
917                        }
918                        self.emit(Instruction::CallExternal(name.clone(), args.len()));
919                        return Ok(());
920                    }
921                }
922                self.compile_expr(callee)?;
923                for arg in args {
924                    self.compile_expr(arg)?;
925                }
926                self.emit(Instruction::Call(args.len()));
927            }
928            Expr::New { callee, args } => {
929                self.compile_expr(callee)?;
930                for arg in args {
931                    self.compile_expr(arg)?;
932                }
933                self.emit(Instruction::Construct(args.len()));
934            }
935            Expr::ArrowFunction { func_index } | Expr::FunctionExpr { func_index } => {
936                self.emit(Instruction::CreateClosure(*func_index));
937            }
938            Expr::Await(expr) => {
939                self.compile_expr(expr)?;
940                // Emit Await instruction to unwrap Promise objects.
941                // External call suspension is already handled by CallExternal
942                // before this point — Await only handles internal promise values.
943                self.emit(Instruction::Await);
944            }
945            Expr::Yield { value, delegate: _ } => {
946                // Compile the yielded value (or undefined if none)
947                match value {
948                    Some(expr) => self.compile_expr(expr)?,
949                    None => {
950                        self.emit(Instruction::Push(Constant::Undefined));
951                    }
952                }
953                // Yield instruction: suspends the generator, pops value, pushes received value on resume
954                self.emit(Instruction::Yield);
955            }
956            Expr::TypeOf(operand) => {
957                self.compile_expr(operand)?;
958                self.emit(Instruction::TypeOf);
959            }
960            Expr::ClassExpr {
961                name,
962                super_class,
963                constructor,
964                methods,
965                static_methods,
966            } => {
967                self.compile_class(
968                    name.as_deref(),
969                    super_class.as_deref(),
970                    constructor.as_deref(),
971                    methods,
972                    static_methods,
973                )?;
974            }
975        }
976        Ok(())
977    }
978
979    fn compile_class(
980        &mut self,
981        name: Option<&str>,
982        super_class: Option<&str>,
983        constructor: Option<&FunctionDef>,
984        methods: &[ClassMethod],
985        static_methods: &[ClassMethod],
986    ) -> Result<()> {
987        let class_name = name.unwrap_or("AnonymousClass").to_string();
988
989        // Push super class if present
990        if let Some(sc) = super_class {
991            if let Some(idx) = self.resolve_local(sc) {
992                self.emit(Instruction::LoadLocal(idx));
993            } else {
994                self.emit(Instruction::LoadGlobal(sc.to_string()));
995            }
996        }
997
998        // Push static methods: name, closure pairs
999        for sm in static_methods {
1000            self.emit(Instruction::Push(Constant::String(sm.name.clone())));
1001            let compiled = self.compile_function_def(&sm.func)?;
1002            let func_idx = self.functions.len();
1003            self.functions.push(compiled);
1004            self.emit(Instruction::CreateClosure(func_idx));
1005        }
1006
1007        // Push instance methods: name, closure pairs
1008        for m in methods {
1009            self.emit(Instruction::Push(Constant::String(m.name.clone())));
1010            let compiled = self.compile_function_def(&m.func)?;
1011            let func_idx = self.functions.len();
1012            self.functions.push(compiled);
1013            self.emit(Instruction::CreateClosure(func_idx));
1014        }
1015
1016        // Push constructor closure (or undefined if none)
1017        if let Some(ctor) = constructor {
1018            let compiled = self.compile_function_def(ctor)?;
1019            let func_idx = self.functions.len();
1020            self.functions.push(compiled);
1021            self.emit(Instruction::CreateClosure(func_idx));
1022        } else {
1023            self.emit(Instruction::Push(Constant::Undefined));
1024        }
1025
1026        self.emit(Instruction::CreateClass {
1027            name: class_name,
1028            n_methods: methods.len(),
1029            n_statics: static_methods.len(),
1030            has_super: super_class.is_some(),
1031        });
1032
1033        Ok(())
1034    }
1035
1036    fn compile_store(&mut self, target: &Expr) -> Result<()> {
1037        match target {
1038            Expr::Ident(name) if name == "this" => {
1039                self.emit(Instruction::StoreThis);
1040            }
1041            Expr::Ident(name) => {
1042                if let Some(idx) = self.resolve_local(name) {
1043                    self.emit(Instruction::StoreLocal(idx));
1044                } else {
1045                    self.emit(Instruction::StoreGlobal(name.clone()));
1046                }
1047            }
1048            Expr::Member {
1049                object, property, ..
1050            } => {
1051                self.compile_expr(object)?;
1052                self.emit(Instruction::SetProperty(property.clone()));
1053                // SetProperty pushes the modified object back — store it to the parent
1054                self.compile_store(object)?;
1055            }
1056            Expr::ComputedMember {
1057                object, property, ..
1058            } => {
1059                self.compile_expr(object)?;
1060                self.compile_expr(property)?;
1061                self.emit(Instruction::SetIndex);
1062                // SetIndex pushes the modified object back — store it to the parent
1063                self.compile_store(object)?;
1064            }
1065            _ => {
1066                return Err(ZapcodeError::CompileError(
1067                    "invalid assignment target".to_string(),
1068                ));
1069            }
1070        }
1071        Ok(())
1072    }
1073}
1074
1075pub fn compile(program: &Program) -> Result<CompiledProgram> {
1076    compile_with_externals(program, HashSet::new())
1077}
1078
1079pub fn compile_with_externals(
1080    program: &Program,
1081    external_functions: HashSet<String>,
1082) -> Result<CompiledProgram> {
1083    let mut compiler = Compiler::new(external_functions);
1084    compiler.compile_program(program)?;
1085
1086    Ok(CompiledProgram {
1087        instructions: compiler.instructions,
1088        functions: compiler.functions,
1089        local_names: compiler.locals,
1090    })
1091}