diff --git a/Cargo.lock b/Cargo.lock index 577782c2..f53ed215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1111,6 +1111,7 @@ version = "0.1.0" dependencies = [ "oxide_bytecode", "oxide_code_cache", + "oxide_emit", "oxide_ir", "oxide_log", "oxide_parser", @@ -1119,6 +1120,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "oxide_emit" +version = "0.1.0" +dependencies = [ + "oxide_bytecode", + "oxide_ir", + "oxide_parser", +] + [[package]] name = "oxide_ir" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 65786a48..2a8534f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "crates/oxide_code_cache", "crates/oxide_log", "crates/oxide_compiler", + "crates/oxide_emit", "crates/oxide_ir", "crates/oxide_types", "crates/oxide_vm", @@ -31,6 +32,7 @@ oxc_span = "0.134.0" oxc_diagnostics = "0.134.0" oxc_syntax = "0.134.0" oxide_ir = { path = "crates/oxide_ir" } +oxide_emit = { path = "crates/oxide_emit" } clap = { version = "4", features = ["derive"] } rustyline = "14" ansi_term = "0.12" diff --git a/crates/oxide_compiler/Cargo.toml b/crates/oxide_compiler/Cargo.toml index 677813e7..28488734 100644 --- a/crates/oxide_compiler/Cargo.toml +++ b/crates/oxide_compiler/Cargo.toml @@ -11,6 +11,7 @@ oxide_bytecode = { path = "../oxide_bytecode" } oxide_parser = { path = "../oxide_parser" } oxide_log = { path = "../oxide_log" } oxide_ir.workspace = true +oxide_emit.workspace = true serde.workspace = true serde_json.workspace = true rustc-hash.workspace = true diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 9eb4bc97..4ae70c4a 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -1,900 +1,20 @@ -use std::collections::{BTreeMap, HashMap, HashSet}; - -use crate::emit_ctx::{LabelCtx, ScopeCtx}; -use oxide_ir::inst::Inst; -use oxide_ir::lower::lower; -use oxide_ir::operand::{LabelId, Operand}; -use oxide_ir::IRFunction; -use crate::symbol_table::{Binding, SymbolTable}; use oxide_bytecode::module::CompiledModule; -use oxide_bytecode::module::UpvalueCapture; -use oxide_bytecode::opcode::OpCode; +use oxide_emit::Emitter; +use oxide_ir::lower::lower; pub use crate::hash::{compiled_module_hash, structural_hash}; -use crate::symbol_table::ScopeKind; -pub use oxide_bytecode::module::Constant; -pub use oxide_parser::VariableDeclarationKind; -pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator}; pub struct Compiler; -pub(crate) fn is_int_literal(value: f64) -> bool { - value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 -} - -pub(crate) fn is_side_effect_free(expr: &Expression) -> bool { - let mut stack = vec![expr]; - while let Some(expr) = stack.pop() { - match expr { - Expression::NumericLiteral(_) - | Expression::StringLiteral(_) - | Expression::BooleanLiteral(_) - | Expression::NullLiteral(_) - | Expression::Identifier(_) - | Expression::RegExpLiteral(_) - | Expression::ThisExpression(_) => {} - Expression::ParenthesizedExpression(p) => stack.push(&p.expression), - Expression::BinaryExpression(bin) => { - stack.push(&bin.left); - stack.push(&bin.right); - } - Expression::UnaryExpression(un) if !matches!(un.operator, UnaryOperator::Delete) => { - stack.push(&un.argument); - } - _ => return false, - } - } - true -} - -const BUILTIN_GLOBALS: &[&str] = &[ - "NaN", - "undefined", - "Infinity", - "globalThis", - "Object", - "Array", - "String", - "Number", - "Boolean", - "Function", - "Error", - "TypeError", - "ReferenceError", - "RangeError", - "SyntaxError", - "URIError", - "EvalError", - "Math", - "JSON", - "Promise", - "Date", - "Set", - "Map", - "RegExp", - "Symbol", - "parseInt", - "parseFloat", - "isNaN", - "isFinite", - "Proxy", - "WeakMap", - "WeakSet", - "WeakRef", - "FinalizationRegistry", - "Atomics", - "SharedArrayBuffer", - "ArrayBuffer", - "DataView", - "Iterator", - "BigInt", - "Int8Array", - "Uint8Array", - "Uint8ClampedArray", - "Int16Array", - "Uint16Array", - "Int32Array", - "Uint32Array", - "Float32Array", - "Float64Array", - "BigInt64Array", - "BigUint64Array", - "Reflect", - "escape", - "unescape", - "encodeURI", - "decodeURI", - "encodeURIComponent", - "decodeURIComponent", -]; - -pub(crate) struct FieldBuffer { - pub(crate) insts: Vec, - pub(crate) labels: Vec<(LabelId, usize)>, -} - -/// A labeled-statement scope active during emission. `break label` targets -/// `break_label`; `continue label` targets `continue_label` (only set when the -/// labeled statement directly wraps an iteration statement). -#[derive(Debug, Clone)] -pub(crate) struct LabelScope { - pub(crate) name: String, - pub(crate) break_label: LabelId, - pub(crate) continue_label: Option, -} - -pub(crate) struct CompileCtx { - pub(crate) insts: Vec, - pub(crate) constants: Vec, - constant_map: HashMap, - next_reg: u8, - pub(crate) max_regs: u8, - reserved_reg_start: u8, - pub(crate) labels: LabelCtx, - pub(crate) scopes: ScopeCtx, - pub(crate) nested: Vec, - /// Register holding `this` in the enclosing function context. - /// Used by arrow functions to capture lexical `this`. - /// Initialized to 254 (conventional this register) at the top level. - pub(crate) enclosing_this_reg: u8, - pub(crate) in_derived_constructor: bool, - pub(crate) in_instance_method: bool, - pub(crate) in_static_method: bool, - pub(crate) static_block_this_reg: Option, - pub(crate) field_buffer: Option, - pub(crate) current_upvalue_captures: Vec, - /// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。 - pub(crate) own_bindings: HashSet, - /// 本函数被嵌套函数捕获的绑定名 → cell_idx(名字排序分配,稳定跨 run)。 - /// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射, - /// 消除符号表时序依赖与 cell 索引错位。 - pub(crate) captured_bindings: BTreeMap, - /// Set when alloc_reg() overflows into the reserved this/new.target range (≥254). - /// Checked after each emit phase to produce a compile error rather than silent corruption. - pub(crate) reg_overflow: bool, - pub(crate) const_overflow: bool, -} - -#[derive(Clone, Copy)] -pub(crate) enum FunctionBodyContext { - Ordinary, - Arrow, - ClassElement, -} - -pub(crate) enum ParamSpec<'a> { - Identifier(String), - Pattern { - synthetic_name: String, - pattern: &'a oxide_parser::BindingPattern<'a>, - }, -} - -impl ParamSpec<'_> { - pub(crate) fn register_name(&self) -> &str { - match self { - Self::Identifier(name) => name, - Self::Pattern { synthetic_name, .. } => synthetic_name, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -enum ConstantKey { - Number(u64), - Int(i32), - String(String), - Boolean(bool), - Null, - Undefined, -} - -impl CompileCtx { - pub(crate) fn new() -> Self { - Self { - insts: Vec::new(), - constants: Vec::new(), - constant_map: HashMap::new(), - next_reg: 1, - max_regs: 1, - reserved_reg_start: 1, - labels: LabelCtx { - label_pos: Vec::new(), - loop_stack: Vec::new(), - switch_stack: Vec::new(), - label_scopes: Vec::new(), - pending_loop_labels: Vec::new(), - label_counter: 0, - }, - scopes: ScopeCtx { - symbols: SymbolTable::new(), - builtin_reg_map: Vec::new(), - private_name_map: Vec::new(), - next_private_name_id: 1, - }, - nested: Vec::new(), - enclosing_this_reg: 254, // conventional this register at top level - in_derived_constructor: false, - in_instance_method: false, - in_static_method: false, - static_block_this_reg: None, - field_buffer: None, - current_upvalue_captures: Vec::new(), - own_bindings: HashSet::new(), - captured_bindings: BTreeMap::new(), - reg_overflow: false, - const_overflow: false, - } - } - - pub(crate) fn inst(&mut self, inst: Inst) { - self.insts.push(inst); - } - - pub(crate) fn alloc_reg(&mut self) -> u8 { - let r = self.next_reg; - // Registers 254 (this) and 255 (new.target) are reserved by the VM. - // Allocating into them silently corrupts the call convention, turning - // method calls' `this` into garbage. Clamp to 253 and set a flag so - // the compiler can surface a proper error after the emit pass. - if r >= 254 { - self.reg_overflow = true; - return 253; // clamp to last safe register; emit continues but reg_overflow triggers error - } - self.next_reg = self.next_reg.wrapping_add(1); - if self.next_reg > self.max_regs { - self.max_regs = self.next_reg; - } - r - } - - pub(crate) fn reset_regs(&mut self) { - self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start); - self.labels.label_counter = 0; - } - - pub(crate) fn reg_checkpoint(&self) -> u8 { - self.next_reg - } - - pub(crate) fn restore_reg_checkpoint(&mut self, checkpoint: u8) { - self.next_reg = checkpoint; - } - - pub(crate) fn reserve_reg(&mut self, reg: u8) { - let next = reg.wrapping_add(1); - if self.next_reg <= reg { - self.next_reg = next; - } - if self.max_regs < next { - self.max_regs = next; - } - } - - pub(crate) fn add_constant(&mut self, c: Constant) -> u16 { - if let Some(key) = ConstantKey::from_constant(&c) { - if let Some(&idx) = self.constant_map.get(&key) { - return idx; - } - - if self.constants.len() >= u16::MAX as usize { - self.const_overflow = true; - return u16::MAX; - } - let idx = self.constants.len() as u16; - self.constants.push(c); - self.constant_map.insert(key, idx); - return idx; - } - - let idx = self.constants.len(); - if idx >= u16::MAX as usize { - self.const_overflow = true; - return u16::MAX; - } - self.constants.push(c); - idx as u16 - } - - pub(crate) fn push_scope(&mut self) { - self.scopes.symbols.push_scope(); - } - - pub(crate) fn pop_scope(&mut self) { - self.scopes.symbols.pop_scope(); - } - - pub(crate) fn declare( - &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool, - ) -> Result<(), String> { - self.scopes.symbols.declare(name, reg, kind, is_const) - } - - pub(crate) fn declare_initialized( - &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool, - ) -> Result<(), String> { - self.scopes.symbols.declare_initialized(name, reg, kind, is_const) - } - - pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) { - self.scopes.symbols.push_scope_with_kind(kind); - } - - pub(crate) fn lookup(&self, name: &str) -> Result { - self.scopes.symbols.lookup(name) - } - - pub(crate) fn lookup_or_builtin(&mut self, name: &str) -> Result { - match self.scopes.symbols.lookup(name) { - Ok(reg) => Ok(reg), - Err(err) if Self::is_known_builtin(name) && err.contains("is not defined") => { - let reg = self.alloc_reg(); - self.scopes.symbols.pre_register_global(name, reg); - self.scopes.builtin_reg_map.push((name.to_string(), reg)); - Ok(reg) - } - Err(err) => Err(err), - } - } - - pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 { - if let Some(reg) = self.scopes.symbols.lookup_any(name) { - return reg; - } - let reg = self.alloc_reg(); - self.scopes.symbols.lookup_or_global(name, reg) - } - - pub(crate) fn lookup_const_flag(&self, name: &str) -> bool { - self.scopes.symbols.lookup_is_const(name) - } - - pub(crate) fn init_var(&mut self, name: &str) { - self.scopes.symbols.init_var(name); - } - - pub(crate) fn next_label_id(&mut self) -> u32 { - let id = self.labels.label_counter; - self.labels.label_counter += 1; - id - } - - pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId) { - self.labels.loop_stack.push((break_label, continue_label)); - } - - pub(crate) fn pop_loop(&mut self) { - self.labels.loop_stack.pop(); - } - - pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId)> { - self.labels.loop_stack.last() - } - - pub(crate) fn push_switch(&mut self, break_label: LabelId) { - self.labels.switch_stack.push(break_label); - } - - pub(crate) fn pop_switch(&mut self) { - self.labels.switch_stack.pop(); - } - - pub(crate) fn current_switch(&self) -> Option<&LabelId> { - self.labels.switch_stack.last() - } - - pub(crate) fn push_label_scope( - &mut self, name: &str, break_label: LabelId, continue_label: Option, - ) -> Result<(), String> { - if self.labels.label_scopes.iter().any(|s| s.name == name) { - return Err(format!("SyntaxError: Label '{name}' has already been declared")); - } - self.labels.label_scopes.push(LabelScope { - name: name.to_string(), - break_label, - continue_label, - }); - Ok(()) - } - - pub(crate) fn pop_label_scope(&mut self) { - self.labels.label_scopes.pop(); - } - - pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> { - self.labels.label_scopes.iter().rev().find(|s| s.name == name) - } - - /// Queue a label name to be bound to the continue target of the next loop - /// emitted as the labeled statement's body. Rejects duplicates in the active - /// or pending sets. - pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> { - if self.labels.label_scopes.iter().any(|s| s.name == name) - || self.labels.pending_loop_labels.iter().any(|n| n == name) - { - return Err(format!("SyntaxError: Label '{name}' has already been declared")); - } - self.labels.pending_loop_labels.push(name.to_string()); - Ok(()) - } - - /// Drain queued loop labels into active scopes bound to this loop's break and - /// continue targets. Returns how many scopes were pushed (to pop after). - pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize { - let names = std::mem::take(&mut self.labels.pending_loop_labels); - let count = names.len(); - for name in names { - self.labels.label_scopes.push(LabelScope { - name, - break_label, - continue_label: Some(continue_label), - }); - } - count - } - - pub(crate) fn pop_label_scopes(&mut self, n: usize) { - for _ in 0..n { - self.labels.label_scopes.pop(); - } - } - - pub(crate) fn is_builtin(&self, name: &str) -> bool { - self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name) - } - - pub(crate) fn is_known_builtin(name: &str) -> bool { - BUILTIN_GLOBALS.contains(&name) - } - - fn builtin_reg_floor(&self) -> u8 { - self.scopes - .builtin_reg_map - .iter() - .map(|(_, reg)| reg.saturating_add(1)) - .max() - .unwrap_or(0) - } - - pub(crate) fn pre_register_builtins(&mut self) { - // Builtin globals are resolved lazily by lookup_or_builtin(). Keeping this - // hook preserves the compile pipeline without reserving ~60 registers in - // every module. - } - - /// 组装 IRFunction(两出口共用),take 走编译产物状态。 - /// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。 - fn assemble_ir(&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction { - let upvalue_captures = self - .current_upvalue_captures - .iter() - .map(|u| { - let enclosing_reg = parent_ctx - .and_then(|p| p.scopes.symbols.lookup_any(u.name.as_str())) - .unwrap_or(0); - UpvalueCapture { - name: u.name.clone(), - enclosing_reg, - cell_idx: u.cell_idx, - } - }) - .collect(); - IRFunction { - insts: std::mem::take(&mut self.insts), - label_pos: std::mem::take(&mut self.labels.label_pos), - label_count: self.labels.label_counter, - constants: std::mem::take(&mut self.constants), - param_layout, - builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map), - upvalue_captures, - cells_needed: self.captured_bindings.len() as u8, - n_registers: self.max_regs, - is_arrow: false, - is_class_constructor: false, - is_derived_constructor: false, - needs_home_object: false, - captured_this_const_idx: 0, - function_name: None, - reg_overflow: self.reg_overflow, - const_overflow: self.const_overflow, - nested: std::mem::take(&mut self.nested), - } - } -} - -impl ConstantKey { - fn from_constant(value: &Constant) -> Option { - match value { - Constant::Number(v) => Some(Self::Number(v.to_bits())), - Constant::Int(v) => Some(Self::Int(*v)), - Constant::String(v) => Some(Self::String(v.clone())), - Constant::Boolean(v) => Some(Self::Boolean(*v)), - Constant::Null => Some(Self::Null), - Constant::Undefined => Some(Self::Undefined), - } - } -} - impl Compiler { pub fn new() -> Self { Self } - // ── 闭包捕获分析(AST 级,时序无关)── - - /// 收集函数参数的绑定名(BindingIdentifier 形态)。 - - - pub(crate) fn extract_function_parts<'a>( - &self, function: &'a oxide_parser::Function<'a>, - ) -> Result<(Vec>, &'a [Statement<'a>]), String> { - let mut param_specs = Vec::new(); - for (idx, param) in function.params.items.iter().enumerate() { - match ¶m.pattern { - oxide_parser::BindingPattern::BindingIdentifier(bi) => { - param_specs.push(ParamSpec::Identifier(bi.name.to_string())); - } - pattern => { - param_specs.push(ParamSpec::Pattern { - synthetic_name: format!("@@param_{idx}"), - pattern, - }); - } - } - } - let body_stmts: &[Statement] = if let Some(body) = &function.body { &body.statements } else { &[] }; - Ok((param_specs, body_stmts)) - } - - /// Compile a function body (used for FD, FE, and arrow functions). - /// This performs both counting and emitting in one pass. - /// When `is_expression_body` is true (arrow function with expression body), - /// the last expression's value is returned instead of undefined. - /// `is_arrow` controls whether super flags are inherited from the parent scope - /// (true for arrow functions, which have lexical super) or reset to false - /// (false for regular functions, which create a new super scope). - pub(crate) fn compile_function_body<'a>( - &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, - is_expression_body: bool, is_arrow: bool, - ) -> Result { - let body_context = if is_arrow { - FunctionBodyContext::Arrow - } else { - FunctionBodyContext::Ordinary - }; - self.compile_function_body_with_bindings( - param_specs, - body_stmts, - parent_ctx, - is_expression_body, - &[], - body_context, - ) - } - - pub(crate) fn compile_function_body_with_bindings<'a>( - &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, - is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext, - ) -> Result { - self.compile_function_body_with_field_hooks( - param_specs, - body_stmts, - parent_ctx, - is_expression_body, - extra_bindings, - body_context, - None:: Result<(), String>>, - false, - ) - } - - - /// Pre-register all builtin identifiers referenced anywhere in this body - /// (expressions, member objects, call args, class fields, etc.) so their - /// register slots are reserved *before* any temporary register is emitted. - /// - /// Without this, single-pass emission lazily allocates builtin slots via - /// `lookup_or_builtin` at first use, which can collide with a temporary - /// register already reused by `restore_reg_checkpoint`. Pre-scanning moves - /// builtin slot allocation ahead of the temporary register pool. - #[allow(clippy::too_many_arguments)] - pub(crate) fn compile_function_body_with_field_hooks<'a, E>( - &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, - is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext, - mut emit_fields: Option, fields_after_super: bool, - ) -> Result - where - E: FnMut(&Compiler, &mut CompileCtx) -> Result<(), String>, - { - let mut ctx = CompileCtx::new(); - - // Inherit parent's builtin_reg_map so builtin identifiers (Math, Object, etc.) - // resolve to the correct pre-allocated registers in the sub-module's register file. - ctx.scopes.builtin_reg_map = parent_ctx.scopes.builtin_reg_map.clone(); - ctx.scopes.private_name_map = parent_ctx.scopes.private_name_map.clone(); - ctx.scopes.next_private_name_id = parent_ctx.scopes.next_private_name_id; - - // Propagate enclosing_this_reg so nested arrow functions capture the correct `this`. - ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg; - // Arrow functions inherit lexical super. Class method bodies also need the - // class-provided super context for their top-level body compilation. - if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) { - ctx.in_derived_constructor = parent_ctx.in_derived_constructor; - ctx.in_instance_method = parent_ctx.in_instance_method; - ctx.in_static_method = parent_ctx.in_static_method; - } else { - ctx.in_derived_constructor = false; - ctx.in_instance_method = false; - ctx.in_static_method = false; - } - - // Inherit parent's global scope entries so previously-declared function names - // are visible from within the body. - let mut inherited_reg_start = 1u8.max(ctx.builtin_reg_floor()); - for (name, binding) in &parent_ctx.scopes.symbols.scopes[0].bindings { - ctx.scopes.symbols.scopes[0].bindings.insert( - name.clone(), - Binding { - reg: binding.reg, - initialized: binding.initialized, - is_const: binding.is_const, - }, - ); - inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1)); - } - for (name, reg) in extra_bindings { - ctx.scopes.symbols.scopes[0].bindings.insert( - (*name).to_string(), - Binding { - reg: *reg, - initialized: true, - is_const: true, - }, - ); - inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1)); - } - ctx.reserved_reg_start = inherited_reg_start.max(1); - - // Align next_reg with builtin count so both count and emit passes start at the - // same register offset (params go after builtin slots). - ctx.reset_regs(); - - let param_base = self.emit_params_prologue(param_specs, body_stmts, parent_ctx, &mut ctx, body_context)?; - - self.predeclare_function_declarations(body_stmts, &mut ctx); - - // Pre-register builtin identifier references before emitting any temporary - // register, so builtin slots never collide with reused temporaries. - self.pre_register_builtin_references(body_stmts, &mut ctx); - - // Pre-declare `var` names so hoisted function declarations (emitted in the - // first sub-pass below) can resolve the outer vars they close over. - self.predeclare_var_declarations(body_stmts, &mut ctx); - - if let Some(emit) = emit_fields.as_mut() { - if fields_after_super { - let mut parent_insts = Vec::new(); - let mut parent_label_pos = Vec::new(); - std::mem::swap(&mut ctx.insts, &mut parent_insts); - std::mem::swap(&mut ctx.labels.label_pos, &mut parent_label_pos); - emit(self, &mut ctx)?; - let field_buffer = FieldBuffer { - insts: std::mem::take(&mut ctx.insts), - labels: std::mem::take(&mut ctx.labels.label_pos) - .into_iter() - .enumerate() - .filter_map(|(id, pos)| pos.map(|p| (id as LabelId, p))) - .collect(), - }; - ctx.insts = parent_insts; - ctx.labels.label_pos = parent_label_pos; - ctx.field_buffer = Some(field_buffer); - } else { - emit(self, &mut ctx)?; - } - } - - // Emit body statements(先函数声明 hoisting,再其余)。 - let last_result_reg = self.emit_body_stmts(body_stmts, &mut ctx)?; - - // Emit implicit RETURN: expression body returns the last expression, - // statement body returns undefined. - if is_expression_body { - if let Some(reg) = last_result_reg { - ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(reg as u32), Operand::None, Operand::None)); - } else { - let undef_idx = ctx.add_constant(Constant::Undefined); - let undef_reg = ctx.alloc_reg(); - ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx)); - ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None)); - } - } else { - let undef_idx = ctx.add_constant(Constant::Undefined); - let undef_reg = ctx.alloc_reg(); - ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx)); - ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None)); - } - - let ir = ctx.assemble_ir( - oxide_ir::ParamLayout { - base: param_base as u32, - count: param_specs.len() as u32, - }, - Some(parent_ctx), - ); - Ok(ir) - } - - /// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。 - fn emit_params_prologue<'a>( - &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, - ctx: &mut CompileCtx, body_context: FunctionBodyContext, - ) -> Result { - ctx.push_scope_with_kind(ScopeKind::FunctionScope); - let param_base = ctx.next_reg; - - // Emit parameters and destructuring prologue. - for spec in param_specs { - let name = spec.register_name(); - let reg = ctx.alloc_reg(); - ctx.declare_initialized(name, reg, VariableDeclarationKind::Var, false)?; - } - - for spec in param_specs { - if let ParamSpec::Pattern { synthetic_name, pattern } = spec { - let src_reg = ctx.lookup(synthetic_name)?; - self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, ctx)?; - } - } - - // 闭包捕获分析(AST 级,emit 前确定,时序无关) - let param_names: Vec<&str> = param_specs.iter().map(|s| s.register_name()).collect(); - ctx.own_bindings = self.collect_own_binding_names(¶m_names, body_stmts); - ctx.captured_bindings = self.collect_captured_bindings(body_stmts, &ctx.own_bindings); - - // Free variable analysis for upvalue capture (Ordinary + Arrow functions only) - if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) { - ctx.current_upvalue_captures = - self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings); - } - - Ok(param_base) - } - - /// 双 sub-pass emit body:先函数声明(hoisting),再其余语句。返回最后结果寄存器。 - fn emit_body_stmts(&self, body_stmts: &[Statement], ctx: &mut CompileCtx) -> Result, String> { - let mut last_result_reg = None; - for stmt in body_stmts { - if matches!(stmt, Statement::FunctionDeclaration(_)) { - if let Some(reg) = self.emit_statement(stmt, ctx)? { - last_result_reg = Some(reg); - } - } - } - for stmt in body_stmts { - if matches!(stmt, Statement::FunctionDeclaration(_)) { - continue; - } - if let Some(reg) = self.emit_statement(stmt, ctx)? { - last_result_reg = Some(reg); - } - } - Ok(last_result_reg) - } - - pub(crate) fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - match stmt { - Statement::ExpressionStatement(_) | Statement::ReturnStatement(_) | Statement::EmptyStatement(_) => { - self.emit_basic_domain(stmt, ctx) - } - Statement::BlockStatement(_) => self.emit_block_domain(stmt, ctx), - Statement::VariableDeclaration(_) | Statement::FunctionDeclaration(_) | Statement::ClassDeclaration(_) => { - self.emit_declaration_domain(stmt, ctx) - } - Statement::IfStatement(_) => self.emit_control_domain(stmt, ctx), - Statement::WhileStatement(_) - | Statement::DoWhileStatement(_) - | Statement::ForStatement(_) - | Statement::ForInStatement(_) - | Statement::ForOfStatement(_) => self.emit_iteration_domain(stmt, ctx), - Statement::SwitchStatement(_) => self.emit_switch_domain(stmt, ctx), - Statement::ThrowStatement(_) | Statement::TryStatement(_) => self.emit_exception_domain(stmt, ctx), - Statement::BreakStatement(b) => self.emit_break_statement(b, ctx), - Statement::ContinueStatement(c) => self.emit_continue_statement(c, ctx), - Statement::LabeledStatement(ls) => self.emit_labeled_statement(ls, ctx), - _ => Ok(None), - } - } - - pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { - match expr { - Expression::NumericLiteral(_) - | Expression::StringLiteral(_) - | Expression::BooleanLiteral(_) - | Expression::NullLiteral(_) - | Expression::RegExpLiteral(_) => self.emit_literal(expr, ctx), - Expression::BinaryExpression(_) - | Expression::PrivateInExpression(_) - | Expression::UnaryExpression(_) - | Expression::ConditionalExpression(_) - | Expression::LogicalExpression(_) - | Expression::UpdateExpression(_) => self.emit_operator(expr, ctx), - Expression::StaticMemberExpression(_) - | Expression::ComputedMemberExpression(_) - | Expression::PrivateFieldExpression(_) - | Expression::ChainExpression(_) => self.emit_member_domain(expr, ctx), - Expression::ObjectExpression(_) | Expression::ArrayExpression(_) => self.emit_object_domain(expr, ctx), - Expression::AssignmentExpression(assign) => self.emit_assignment_expression(assign, ctx), - Expression::TemplateLiteral(_) | Expression::TaggedTemplateExpression(_) => { - self.emit_template_domain(expr, ctx) - } - Expression::ArrowFunctionExpression(_) - | Expression::FunctionExpression(_) - | Expression::ClassExpression(_) - | Expression::NewExpression(_) => self.emit_function_domain(expr, ctx), - Expression::Identifier(ident) => self.emit_identifier_expression(ident, ctx), - Expression::CallExpression(_) => self.emit_call_domain(expr, ctx), - Expression::ThisExpression(_) => self.emit_this_expression(ctx), - Expression::SequenceExpression(seq) => self.emit_sequence_expression(seq, ctx), - Expression::ParenthesizedExpression(p) => self.emit_parenthesized_expression(p, ctx), - _ => self.emit_unsupported_expression(expr, ctx), - } - } - pub fn compile(&self, program: &oxide_parser::Program) -> Result { crate::compiler_debug!("compile: starting..."); - let mut ctx = CompileCtx::new(); - ctx.pre_register_builtins(); - self.predeclare_function_declarations(&program.body, &mut ctx); - - // Pre-register builtin identifier references before any temporary register - // is emitted, keeping builtin slots clear of the temporary register pool. - self.pre_register_builtin_references(&program.body, &mut ctx); - - // Pre-declare top-level `var` names so hoisted function declarations - // (emitted in the first sub-pass below) can resolve the outer vars. - self.predeclare_var_declarations(&program.body, &mut ctx); - - // 闭包捕获分析(AST 级,emit 前确定) - ctx.own_bindings = self.collect_own_binding_names(&[], &program.body); - ctx.captured_bindings = self.collect_captured_bindings(&program.body, &ctx.own_bindings); - - // First sub-pass: emit FunctionDeclarations (hoisting) - // This ensures function objects are available before any code runs. - for stmt in &program.body { - if matches!(stmt, Statement::FunctionDeclaration(_)) { - self.emit_statement(stmt, &mut ctx)?; - } - } - - // Second sub-pass: emit all other statements - let mut last_result: Option = None; - for stmt in &program.body { - if matches!(stmt, Statement::FunctionDeclaration(_)) { - continue; // Already emitted above - } - match self.emit_statement(stmt, &mut ctx)? { - Some(r) => last_result = Some(r), - None => last_result = None, - } - } - if let Some(r) = last_result { - ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::None, Operand::Reg(r as u32), Operand::None)); - } else { - let undef_idx = ctx.add_constant(Constant::Undefined); - ctx.inst(Inst::load_const(Operand::None, undef_idx)); - } - ctx.inst(Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None)); - - crate::compiler_debug!("compile: done, {} instructions, {} constants", ctx.insts.len(), ctx.constants.len()); - - let ir = ctx.assemble_ir( - oxide_ir::ParamLayout { - base: ctx.scopes.builtin_reg_map.len() as u32, - count: 0, - }, - None, - ); + let ir = Emitter::new().emit_program(program)?; + crate::compiler_debug!("compile: done, {} instructions", ir.insts.len()); lower(&ir) } } diff --git a/crates/oxide_compiler/src/lib.rs b/crates/oxide_compiler/src/lib.rs index 47be17de..a1c2a1ae 100644 --- a/crates/oxide_compiler/src/lib.rs +++ b/crates/oxide_compiler/src/lib.rs @@ -1,13 +1,5 @@ #![doc = "OxideJS - AST to bytecode compiler"] -pub mod class; -pub mod closure; pub mod compiler; pub mod compiler_log; -pub mod emit_ctx; -pub mod expr; pub mod hash; -pub mod prepass; -pub mod shared; -pub mod stmt; -pub mod symbol_table; diff --git a/crates/oxide_emit/Cargo.toml b/crates/oxide_emit/Cargo.toml new file mode 100644 index 00000000..865f846d --- /dev/null +++ b/crates/oxide_emit/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "oxide_emit" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +description = "OxideJS - AST to IR emit pass" + +[dependencies] +oxide_parser = { path = "../oxide_parser" } +oxide_bytecode = { path = "../oxide_bytecode" } +oxide_ir = { path = "../oxide_ir" } diff --git a/crates/oxide_compiler/src/class/core.rs b/crates/oxide_emit/src/class/core.rs similarity index 100% rename from crates/oxide_compiler/src/class/core.rs rename to crates/oxide_emit/src/class/core.rs diff --git a/crates/oxide_compiler/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs similarity index 96% rename from crates/oxide_compiler/src/class/emit.rs rename to crates/oxide_emit/src/class/emit.rs index c43e0c20..faafdff2 100644 --- a/crates/oxide_compiler/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext}; +use crate::{CompileCtx, Emitter, FunctionBodyContext}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey}; -impl Compiler { +impl Emitter { pub(crate) fn emit_class(&self, class: &Class, ctx: &mut CompileCtx) -> Result { let mut constructor_method = None; let mut instance_fields = Vec::new(); @@ -59,7 +59,7 @@ impl Compiler { ctx.in_derived_constructor = is_derived; ctx.scopes.private_name_map = private_names.clone(); - let emit_instance_fields = |compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> { + let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> { for field in &instance_fields { if let PropertyKey::PrivateIdentifier(private) = &field.key { compiler.emit_private_field_init(Operand::This, private.name.as_str(), field.value.as_ref(), field_ctx)?; diff --git a/crates/oxide_compiler/src/class/field.rs b/crates/oxide_emit/src/class/field.rs similarity index 96% rename from crates/oxide_compiler/src/class/field.rs rename to crates/oxide_emit/src/class/field.rs index 228d07d9..273cb4db 100644 --- a/crates/oxide_compiler/src/class/field.rs +++ b/crates/oxide_emit/src/class/field.rs @@ -1,8 +1,8 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::operand::Operand; use oxide_parser::{ClassElement, PropertyKey}; -impl Compiler { +impl Emitter { /// Emit static field initializers and static blocks in source order. /// JS class semantics interleave them (e.g. `static x = 1; static { /// this.y = 1 } static z = this.y + 1` must run in that order). Emitting diff --git a/crates/oxide_compiler/src/class/header.rs b/crates/oxide_emit/src/class/header.rs similarity index 88% rename from crates/oxide_compiler/src/class/header.rs rename to crates/oxide_emit/src/class/header.rs index eaecdc7a..d73bf492 100644 --- a/crates/oxide_compiler/src/class/header.rs +++ b/crates/oxide_emit/src/class/header.rs @@ -1,6 +1,6 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; -impl Compiler { +impl Emitter { pub(crate) fn emit_class_header( &self, class: &oxide_parser::Class, ctx: &mut CompileCtx, ) -> Result<(u8, u8, Option), String> { diff --git a/crates/oxide_compiler/src/class/method.rs b/crates/oxide_emit/src/class/method.rs similarity index 98% rename from crates/oxide_compiler/src/class/method.rs rename to crates/oxide_emit/src/class/method.rs index d031e2df..6fb5c12b 100644 --- a/crates/oxide_compiler/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext}; +use crate::{CompileCtx, Emitter, FunctionBodyContext}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey}; -impl Compiler { +impl Emitter { pub(crate) fn emit_class_methods( &self, elements: &[ClassElement], ctor_reg: u8, proto_reg: u8, self_binding: &[(&str, u8)], ctx: &mut CompileCtx, diff --git a/crates/oxide_compiler/src/class/mod.rs b/crates/oxide_emit/src/class/mod.rs similarity index 100% rename from crates/oxide_compiler/src/class/mod.rs rename to crates/oxide_emit/src/class/mod.rs diff --git a/crates/oxide_compiler/src/class/prototype.rs b/crates/oxide_emit/src/class/prototype.rs similarity index 97% rename from crates/oxide_compiler/src/class/prototype.rs rename to crates/oxide_emit/src/class/prototype.rs index afa526cc..c8fc0a2a 100644 --- a/crates/oxide_compiler/src/class/prototype.rs +++ b/crates/oxide_emit/src/class/prototype.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; -impl Compiler { +impl Emitter { pub(crate) fn emit_class_prototype( &self, ctor_reg: u8, proto_reg: u8, super_reg: Option, sub_idx: u16, ctx: &mut CompileCtx, ) -> Result<(), String> { diff --git a/crates/oxide_compiler/src/closure.rs b/crates/oxide_emit/src/closure.rs similarity index 99% rename from crates/oxide_compiler/src/closure.rs rename to crates/oxide_emit/src/closure.rs index 59d99e65..a3e5d12b 100644 --- a/crates/oxide_compiler/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -3,9 +3,9 @@ use std::collections::{BTreeMap, HashSet}; use oxide_bytecode::module::UpvalueCapture; use oxide_parser::{Expression, Statement}; -use crate::compiler::Compiler; +use crate::Emitter; -impl Compiler { +impl Emitter { fn collect_fn_param_names(&self, params: &oxide_parser::FormalParameters) -> HashSet { let mut names = HashSet::new(); @@ -515,4 +515,3 @@ impl Compiler { .collect() } } - diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs new file mode 100644 index 00000000..2860eb38 --- /dev/null +++ b/crates/oxide_emit/src/emit.rs @@ -0,0 +1,903 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; + +use oxide_bytecode::module::UpvalueCapture; +use oxide_bytecode::opcode::OpCode; +use oxide_ir::inst::Inst; +use oxide_ir::operand::{LabelId, Operand}; +use oxide_ir::IRFunction; + +use crate::emit_ctx::{LabelCtx, ScopeCtx}; +use crate::symbol_table::{Binding, ScopeKind, SymbolTable}; + +pub use oxide_bytecode::module::Constant; +pub use oxide_parser::VariableDeclarationKind; +pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator}; + +pub struct Emitter; + +pub fn is_int_literal(value: f64) -> bool { + value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 +} + +pub fn is_side_effect_free(expr: &Expression) -> bool { + let mut stack = vec![expr]; + while let Some(expr) = stack.pop() { + match expr { + Expression::NumericLiteral(_) + | Expression::StringLiteral(_) + | Expression::BooleanLiteral(_) + | Expression::NullLiteral(_) + | Expression::Identifier(_) + | Expression::RegExpLiteral(_) + | Expression::ThisExpression(_) => {} + Expression::ParenthesizedExpression(p) => stack.push(&p.expression), + Expression::BinaryExpression(bin) => { + stack.push(&bin.left); + stack.push(&bin.right); + } + Expression::UnaryExpression(un) if !matches!(un.operator, UnaryOperator::Delete) => { + stack.push(&un.argument); + } + _ => return false, + } + } + true +} + +const BUILTIN_GLOBALS: &[&str] = &[ + "NaN", + "undefined", + "Infinity", + "globalThis", + "Object", + "Array", + "String", + "Number", + "Boolean", + "Function", + "Error", + "TypeError", + "ReferenceError", + "RangeError", + "SyntaxError", + "URIError", + "EvalError", + "Math", + "JSON", + "Promise", + "Date", + "Set", + "Map", + "RegExp", + "Symbol", + "parseInt", + "parseFloat", + "isNaN", + "isFinite", + "Proxy", + "WeakMap", + "WeakSet", + "WeakRef", + "FinalizationRegistry", + "Atomics", + "SharedArrayBuffer", + "ArrayBuffer", + "DataView", + "Iterator", + "BigInt", + "Int8Array", + "Uint8Array", + "Uint8ClampedArray", + "Int16Array", + "Uint16Array", + "Int32Array", + "Uint32Array", + "Float32Array", + "Float64Array", + "BigInt64Array", + "BigUint64Array", + "Reflect", + "escape", + "unescape", + "encodeURI", + "decodeURI", + "encodeURIComponent", + "decodeURIComponent", +]; + +pub(crate) struct FieldBuffer { + pub(crate) insts: Vec, + pub(crate) labels: Vec<(LabelId, usize)>, +} + +/// A labeled-statement scope active during emission. `break label` targets +/// `break_label`; `continue label` targets `continue_label` (only set when the +/// labeled statement directly wraps an iteration statement). +#[derive(Debug, Clone)] +pub struct LabelScope { + pub(crate) name: String, + pub(crate) break_label: LabelId, + pub(crate) continue_label: Option, +} + +pub struct CompileCtx { + pub(crate) insts: Vec, + pub(crate) constants: Vec, + constant_map: HashMap, + next_reg: u8, + pub(crate) max_regs: u8, + reserved_reg_start: u8, + pub(crate) labels: LabelCtx, + pub(crate) scopes: ScopeCtx, + pub(crate) nested: Vec, + /// Register holding `this` in the enclosing function context. + /// Used by arrow functions to capture lexical `this`. + /// Initialized to 254 (conventional this register) at the top level. + pub(crate) enclosing_this_reg: u8, + pub(crate) in_derived_constructor: bool, + pub(crate) in_instance_method: bool, + pub(crate) in_static_method: bool, + pub(crate) static_block_this_reg: Option, + pub(crate) field_buffer: Option, + pub(crate) current_upvalue_captures: Vec, + /// 本函数作用域声明的绑定名(参数 + 变量/函数声明,AST 收集,emit 前确定)。 + pub(crate) own_bindings: HashSet, + /// 本函数被嵌套函数捕获的绑定名 → cell_idx(名字排序分配,稳定跨 run)。 + /// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射, + /// 消除符号表时序依赖与 cell 索引错位。 + pub(crate) captured_bindings: BTreeMap, + /// Set when alloc_reg() overflows into the reserved this/new.target range (≥254). + /// Checked after each emit phase to produce a compile error rather than silent corruption. + pub(crate) reg_overflow: bool, + pub(crate) const_overflow: bool, +} + +#[derive(Clone, Copy)] +pub enum FunctionBodyContext { + Ordinary, + Arrow, + ClassElement, +} + +pub enum ParamSpec<'a> { + Identifier(String), + Pattern { + synthetic_name: String, + pattern: &'a oxide_parser::BindingPattern<'a>, + }, +} + +impl ParamSpec<'_> { + pub(crate) fn register_name(&self) -> &str { + match self { + Self::Identifier(name) => name, + Self::Pattern { synthetic_name, .. } => synthetic_name, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum ConstantKey { + Number(u64), + Int(i32), + String(String), + Boolean(bool), + Null, + Undefined, +} + +impl CompileCtx { + pub(crate) fn new() -> Self { + Self { + insts: Vec::new(), + constants: Vec::new(), + constant_map: HashMap::new(), + next_reg: 1, + max_regs: 1, + reserved_reg_start: 1, + labels: LabelCtx { + label_pos: Vec::new(), + loop_stack: Vec::new(), + switch_stack: Vec::new(), + label_scopes: Vec::new(), + pending_loop_labels: Vec::new(), + label_counter: 0, + }, + scopes: ScopeCtx { + symbols: SymbolTable::new(), + builtin_reg_map: Vec::new(), + private_name_map: Vec::new(), + next_private_name_id: 1, + }, + nested: Vec::new(), + enclosing_this_reg: 254, // conventional this register at top level + in_derived_constructor: false, + in_instance_method: false, + in_static_method: false, + static_block_this_reg: None, + field_buffer: None, + current_upvalue_captures: Vec::new(), + own_bindings: HashSet::new(), + captured_bindings: BTreeMap::new(), + reg_overflow: false, + const_overflow: false, + } + } + + pub(crate) fn inst(&mut self, inst: Inst) { + self.insts.push(inst); + } + + pub(crate) fn alloc_reg(&mut self) -> u8 { + let r = self.next_reg; + // Registers 254 (this) and 255 (new.target) are reserved by the VM. + // Allocating into them silently corrupts the call convention, turning + // method calls' `this` into garbage. Clamp to 253 and set a flag so + // the compiler can surface a proper error after the emit pass. + if r >= 254 { + self.reg_overflow = true; + return 253; // clamp to last safe register; emit continues but reg_overflow triggers error + } + self.next_reg = self.next_reg.wrapping_add(1); + if self.next_reg > self.max_regs { + self.max_regs = self.next_reg; + } + r + } + + pub(crate) fn reset_regs(&mut self) { + self.next_reg = self.builtin_reg_floor().max(self.reserved_reg_start); + self.labels.label_counter = 0; + } + + pub(crate) fn reg_checkpoint(&self) -> u8 { + self.next_reg + } + + pub(crate) fn restore_reg_checkpoint(&mut self, checkpoint: u8) { + self.next_reg = checkpoint; + } + + pub(crate) fn reserve_reg(&mut self, reg: u8) { + let next = reg.wrapping_add(1); + if self.next_reg <= reg { + self.next_reg = next; + } + if self.max_regs < next { + self.max_regs = next; + } + } + + pub(crate) fn add_constant(&mut self, c: Constant) -> u16 { + if let Some(key) = ConstantKey::from_constant(&c) { + if let Some(&idx) = self.constant_map.get(&key) { + return idx; + } + + if self.constants.len() >= u16::MAX as usize { + self.const_overflow = true; + return u16::MAX; + } + let idx = self.constants.len() as u16; + self.constants.push(c); + self.constant_map.insert(key, idx); + return idx; + } + + let idx = self.constants.len(); + if idx >= u16::MAX as usize { + self.const_overflow = true; + return u16::MAX; + } + self.constants.push(c); + idx as u16 + } + + pub(crate) fn push_scope(&mut self) { + self.scopes.symbols.push_scope(); + } + + pub(crate) fn pop_scope(&mut self) { + self.scopes.symbols.pop_scope(); + } + + pub(crate) fn declare( + &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool, + ) -> Result<(), String> { + self.scopes.symbols.declare(name, reg, kind, is_const) + } + + pub(crate) fn declare_initialized( + &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool, + ) -> Result<(), String> { + self.scopes.symbols.declare_initialized(name, reg, kind, is_const) + } + + pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) { + self.scopes.symbols.push_scope_with_kind(kind); + } + + pub(crate) fn lookup(&self, name: &str) -> Result { + self.scopes.symbols.lookup(name) + } + + pub(crate) fn lookup_or_builtin(&mut self, name: &str) -> Result { + match self.scopes.symbols.lookup(name) { + Ok(reg) => Ok(reg), + Err(err) if Self::is_known_builtin(name) && err.contains("is not defined") => { + let reg = self.alloc_reg(); + self.scopes.symbols.pre_register_global(name, reg); + self.scopes.builtin_reg_map.push((name.to_string(), reg)); + Ok(reg) + } + Err(err) => Err(err), + } + } + + pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 { + if let Some(reg) = self.scopes.symbols.lookup_any(name) { + return reg; + } + let reg = self.alloc_reg(); + self.scopes.symbols.lookup_or_global(name, reg) + } + + pub(crate) fn lookup_const_flag(&self, name: &str) -> bool { + self.scopes.symbols.lookup_is_const(name) + } + + pub(crate) fn init_var(&mut self, name: &str) { + self.scopes.symbols.init_var(name); + } + + pub(crate) fn next_label_id(&mut self) -> u32 { + let id = self.labels.label_counter; + self.labels.label_counter += 1; + id + } + + pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId) { + self.labels.loop_stack.push((break_label, continue_label)); + } + + pub(crate) fn pop_loop(&mut self) { + self.labels.loop_stack.pop(); + } + + pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId)> { + self.labels.loop_stack.last() + } + + pub(crate) fn push_switch(&mut self, break_label: LabelId) { + self.labels.switch_stack.push(break_label); + } + + pub(crate) fn pop_switch(&mut self) { + self.labels.switch_stack.pop(); + } + + pub(crate) fn current_switch(&self) -> Option<&LabelId> { + self.labels.switch_stack.last() + } + + pub(crate) fn push_label_scope( + &mut self, name: &str, break_label: LabelId, continue_label: Option, + ) -> Result<(), String> { + if self.labels.label_scopes.iter().any(|s| s.name == name) { + return Err(format!("SyntaxError: Label '{name}' has already been declared")); + } + self.labels.label_scopes.push(LabelScope { + name: name.to_string(), + break_label, + continue_label, + }); + Ok(()) + } + + pub(crate) fn pop_label_scope(&mut self) { + self.labels.label_scopes.pop(); + } + + pub(crate) fn find_label(&self, name: &str) -> Option<&LabelScope> { + self.labels.label_scopes.iter().rev().find(|s| s.name == name) + } + + /// Queue a label name to be bound to the continue target of the next loop + /// emitted as the labeled statement's body. Rejects duplicates in the active + /// or pending sets. + pub(crate) fn queue_loop_label(&mut self, name: &str) -> Result<(), String> { + if self.labels.label_scopes.iter().any(|s| s.name == name) + || self.labels.pending_loop_labels.iter().any(|n| n == name) + { + return Err(format!("SyntaxError: Label '{name}' has already been declared")); + } + self.labels.pending_loop_labels.push(name.to_string()); + Ok(()) + } + + /// Drain queued loop labels into active scopes bound to this loop's break and + /// continue targets. Returns how many scopes were pushed (to pop after). + pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize { + let names = std::mem::take(&mut self.labels.pending_loop_labels); + let count = names.len(); + for name in names { + self.labels.label_scopes.push(LabelScope { + name, + break_label, + continue_label: Some(continue_label), + }); + } + count + } + + pub(crate) fn pop_label_scopes(&mut self, n: usize) { + for _ in 0..n { + self.labels.label_scopes.pop(); + } + } + + pub(crate) fn is_builtin(&self, name: &str) -> bool { + self.scopes.builtin_reg_map.iter().any(|(n, _)| n == name) + } + + pub(crate) fn is_known_builtin(name: &str) -> bool { + BUILTIN_GLOBALS.contains(&name) + } + + fn builtin_reg_floor(&self) -> u8 { + self.scopes + .builtin_reg_map + .iter() + .map(|(_, reg)| reg.saturating_add(1)) + .max() + .unwrap_or(0) + } + + pub(crate) fn pre_register_builtins(&mut self) { + // Builtin globals are resolved lazily by lookup_or_builtin(). Keeping this + // hook preserves the compile pipeline without reserving ~60 registers in + // every module. + } + + /// 组装 IRFunction(两出口共用),take 走编译产物状态。 + /// `parent_ctx` 用于补全 upvalue_captures 的 enclosing_reg(父符号表在父 emit 完成后完整)。 + fn assemble_ir(&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>) -> IRFunction { + let upvalue_captures = self + .current_upvalue_captures + .iter() + .map(|u| { + let enclosing_reg = parent_ctx + .and_then(|p| p.scopes.symbols.lookup_any(u.name.as_str())) + .unwrap_or(0); + UpvalueCapture { + name: u.name.clone(), + enclosing_reg, + cell_idx: u.cell_idx, + } + }) + .collect(); + IRFunction { + insts: std::mem::take(&mut self.insts), + label_pos: std::mem::take(&mut self.labels.label_pos), + label_count: self.labels.label_counter, + constants: std::mem::take(&mut self.constants), + param_layout, + builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map), + upvalue_captures, + cells_needed: self.captured_bindings.len() as u8, + n_registers: self.max_regs, + is_arrow: false, + is_class_constructor: false, + is_derived_constructor: false, + needs_home_object: false, + captured_this_const_idx: 0, + function_name: None, + reg_overflow: self.reg_overflow, + const_overflow: self.const_overflow, + nested: std::mem::take(&mut self.nested), + } + } +} + +impl ConstantKey { + fn from_constant(value: &Constant) -> Option { + match value { + Constant::Number(v) => Some(Self::Number(v.to_bits())), + Constant::Int(v) => Some(Self::Int(*v)), + Constant::String(v) => Some(Self::String(v.clone())), + Constant::Boolean(v) => Some(Self::Boolean(*v)), + Constant::Null => Some(Self::Null), + Constant::Undefined => Some(Self::Undefined), + } + } +} + +impl Emitter { + pub fn new() -> Self { + Self + } + + // ── 闭包捕获分析(AST 级,时序无关)── + + /// 收集函数参数的绑定名(BindingIdentifier 形态)。 + + + pub(crate) fn extract_function_parts<'a>( + &self, function: &'a oxide_parser::Function<'a>, + ) -> Result<(Vec>, &'a [Statement<'a>]), String> { + let mut param_specs = Vec::new(); + for (idx, param) in function.params.items.iter().enumerate() { + match ¶m.pattern { + oxide_parser::BindingPattern::BindingIdentifier(bi) => { + param_specs.push(ParamSpec::Identifier(bi.name.to_string())); + } + pattern => { + param_specs.push(ParamSpec::Pattern { + synthetic_name: format!("@@param_{idx}"), + pattern, + }); + } + } + } + let body_stmts: &[Statement] = if let Some(body) = &function.body { &body.statements } else { &[] }; + Ok((param_specs, body_stmts)) + } + + /// Compile a function body (used for FD, FE, and arrow functions). + /// This performs both counting and emitting in one pass. + /// When `is_expression_body` is true (arrow function with expression body), + /// the last expression's value is returned instead of undefined. + /// `is_arrow` controls whether super flags are inherited from the parent scope + /// (true for arrow functions, which have lexical super) or reset to false + /// (false for regular functions, which create a new super scope). + pub(crate) fn compile_function_body<'a>( + &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, + is_expression_body: bool, is_arrow: bool, + ) -> Result { + let body_context = if is_arrow { + FunctionBodyContext::Arrow + } else { + FunctionBodyContext::Ordinary + }; + self.compile_function_body_with_bindings( + param_specs, + body_stmts, + parent_ctx, + is_expression_body, + &[], + body_context, + ) + } + + pub(crate) fn compile_function_body_with_bindings<'a>( + &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, + is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext, + ) -> Result { + self.compile_function_body_with_field_hooks( + param_specs, + body_stmts, + parent_ctx, + is_expression_body, + extra_bindings, + body_context, + None:: Result<(), String>>, + false, + ) + } + + + /// Pre-register all builtin identifiers referenced anywhere in this body + /// (expressions, member objects, call args, class fields, etc.) so their + /// register slots are reserved *before* any temporary register is emitted. + /// + /// Without this, single-pass emission lazily allocates builtin slots via + /// `lookup_or_builtin` at first use, which can collide with a temporary + /// register already reused by `restore_reg_checkpoint`. Pre-scanning moves + /// builtin slot allocation ahead of the temporary register pool. + #[allow(clippy::too_many_arguments)] + pub(crate) fn compile_function_body_with_field_hooks<'a, E>( + &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, + is_expression_body: bool, extra_bindings: &[(&str, u8)], body_context: FunctionBodyContext, + mut emit_fields: Option, fields_after_super: bool, + ) -> Result + where + E: FnMut(&Emitter, &mut CompileCtx) -> Result<(), String>, + { + let mut ctx = CompileCtx::new(); + + // Inherit parent's builtin_reg_map so builtin identifiers (Math, Object, etc.) + // resolve to the correct pre-allocated registers in the sub-module's register file. + ctx.scopes.builtin_reg_map = parent_ctx.scopes.builtin_reg_map.clone(); + ctx.scopes.private_name_map = parent_ctx.scopes.private_name_map.clone(); + ctx.scopes.next_private_name_id = parent_ctx.scopes.next_private_name_id; + + // Propagate enclosing_this_reg so nested arrow functions capture the correct `this`. + ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg; + // Arrow functions inherit lexical super. Class method bodies also need the + // class-provided super context for their top-level body compilation. + if matches!(body_context, FunctionBodyContext::Arrow | FunctionBodyContext::ClassElement) { + ctx.in_derived_constructor = parent_ctx.in_derived_constructor; + ctx.in_instance_method = parent_ctx.in_instance_method; + ctx.in_static_method = parent_ctx.in_static_method; + } else { + ctx.in_derived_constructor = false; + ctx.in_instance_method = false; + ctx.in_static_method = false; + } + + // Inherit parent's global scope entries so previously-declared function names + // are visible from within the body. + let mut inherited_reg_start = 1u8.max(ctx.builtin_reg_floor()); + for (name, binding) in &parent_ctx.scopes.symbols.scopes[0].bindings { + ctx.scopes.symbols.scopes[0].bindings.insert( + name.clone(), + Binding { + reg: binding.reg, + initialized: binding.initialized, + is_const: binding.is_const, + }, + ); + inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1)); + } + for (name, reg) in extra_bindings { + ctx.scopes.symbols.scopes[0].bindings.insert( + (*name).to_string(), + Binding { + reg: *reg, + initialized: true, + is_const: true, + }, + ); + inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1)); + } + ctx.reserved_reg_start = inherited_reg_start.max(1); + + // Align next_reg with builtin count so both count and emit passes start at the + // same register offset (params go after builtin slots). + ctx.reset_regs(); + + let param_base = self.emit_params_prologue(param_specs, body_stmts, parent_ctx, &mut ctx, body_context)?; + + self.predeclare_function_declarations(body_stmts, &mut ctx); + + // Pre-register builtin identifier references before emitting any temporary + // register, so builtin slots never collide with reused temporaries. + self.pre_register_builtin_references(body_stmts, &mut ctx); + + // Pre-declare `var` names so hoisted function declarations (emitted in the + // first sub-pass below) can resolve the outer vars they close over. + self.predeclare_var_declarations(body_stmts, &mut ctx); + + if let Some(emit) = emit_fields.as_mut() { + if fields_after_super { + let mut parent_insts = Vec::new(); + let mut parent_label_pos = Vec::new(); + std::mem::swap(&mut ctx.insts, &mut parent_insts); + std::mem::swap(&mut ctx.labels.label_pos, &mut parent_label_pos); + emit(self, &mut ctx)?; + let field_buffer = FieldBuffer { + insts: std::mem::take(&mut ctx.insts), + labels: std::mem::take(&mut ctx.labels.label_pos) + .into_iter() + .enumerate() + .filter_map(|(id, pos)| pos.map(|p| (id as LabelId, p))) + .collect(), + }; + ctx.insts = parent_insts; + ctx.labels.label_pos = parent_label_pos; + ctx.field_buffer = Some(field_buffer); + } else { + emit(self, &mut ctx)?; + } + } + + // Emit body statements(先函数声明 hoisting,再其余)。 + let last_result_reg = self.emit_body_stmts(body_stmts, &mut ctx)?; + + // Emit implicit RETURN: expression body returns the last expression, + // statement body returns undefined. + if is_expression_body { + if let Some(reg) = last_result_reg { + ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(reg as u32), Operand::None, Operand::None)); + } else { + let undef_idx = ctx.add_constant(Constant::Undefined); + let undef_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx)); + ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None)); + } + } else { + let undef_idx = ctx.add_constant(Constant::Undefined); + let undef_reg = ctx.alloc_reg(); + ctx.inst(Inst::load_const(Operand::Reg(undef_reg as u32), undef_idx)); + ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(undef_reg as u32), Operand::None, Operand::None)); + } + + let ir = ctx.assemble_ir( + oxide_ir::ParamLayout { + base: param_base as u32, + count: param_specs.len() as u32, + }, + Some(parent_ctx), + ); + Ok(ir) + } + + /// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。 + fn emit_params_prologue<'a>( + &self, param_specs: &[ParamSpec<'a>], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, + ctx: &mut CompileCtx, body_context: FunctionBodyContext, + ) -> Result { + ctx.push_scope_with_kind(ScopeKind::FunctionScope); + let param_base = ctx.next_reg; + + // Emit parameters and destructuring prologue. + for spec in param_specs { + let name = spec.register_name(); + let reg = ctx.alloc_reg(); + ctx.declare_initialized(name, reg, VariableDeclarationKind::Var, false)?; + } + + for spec in param_specs { + if let ParamSpec::Pattern { synthetic_name, pattern } = spec { + let src_reg = ctx.lookup(synthetic_name)?; + self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, ctx)?; + } + } + + // 闭包捕获分析(AST 级,emit 前确定,时序无关) + let param_names: Vec<&str> = param_specs.iter().map(|s| s.register_name()).collect(); + ctx.own_bindings = self.collect_own_binding_names(¶m_names, body_stmts); + ctx.captured_bindings = self.collect_captured_bindings(body_stmts, &ctx.own_bindings); + + // Free variable analysis for upvalue capture (Ordinary + Arrow functions only) + if matches!(body_context, FunctionBodyContext::Ordinary | FunctionBodyContext::Arrow) { + ctx.current_upvalue_captures = + self.collect_upvalue_names(body_stmts, &parent_ctx.captured_bindings, &ctx.own_bindings); + } + + Ok(param_base) + } + + /// 双 sub-pass emit body:先函数声明(hoisting),再其余语句。返回最后结果寄存器。 + fn emit_body_stmts(&self, body_stmts: &[Statement], ctx: &mut CompileCtx) -> Result, String> { + let mut last_result_reg = None; + for stmt in body_stmts { + if matches!(stmt, Statement::FunctionDeclaration(_)) { + if let Some(reg) = self.emit_statement(stmt, ctx)? { + last_result_reg = Some(reg); + } + } + } + for stmt in body_stmts { + if matches!(stmt, Statement::FunctionDeclaration(_)) { + continue; + } + if let Some(reg) = self.emit_statement(stmt, ctx)? { + last_result_reg = Some(reg); + } + } + Ok(last_result_reg) + } + + pub(crate) fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + match stmt { + Statement::ExpressionStatement(_) | Statement::ReturnStatement(_) | Statement::EmptyStatement(_) => { + self.emit_basic_domain(stmt, ctx) + } + Statement::BlockStatement(_) => self.emit_block_domain(stmt, ctx), + Statement::VariableDeclaration(_) | Statement::FunctionDeclaration(_) | Statement::ClassDeclaration(_) => { + self.emit_declaration_domain(stmt, ctx) + } + Statement::IfStatement(_) => self.emit_control_domain(stmt, ctx), + Statement::WhileStatement(_) + | Statement::DoWhileStatement(_) + | Statement::ForStatement(_) + | Statement::ForInStatement(_) + | Statement::ForOfStatement(_) => self.emit_iteration_domain(stmt, ctx), + Statement::SwitchStatement(_) => self.emit_switch_domain(stmt, ctx), + Statement::ThrowStatement(_) | Statement::TryStatement(_) => self.emit_exception_domain(stmt, ctx), + Statement::BreakStatement(b) => self.emit_break_statement(b, ctx), + Statement::ContinueStatement(c) => self.emit_continue_statement(c, ctx), + Statement::LabeledStatement(ls) => self.emit_labeled_statement(ls, ctx), + _ => Ok(None), + } + } + + pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { + match expr { + Expression::NumericLiteral(_) + | Expression::StringLiteral(_) + | Expression::BooleanLiteral(_) + | Expression::NullLiteral(_) + | Expression::RegExpLiteral(_) => self.emit_literal(expr, ctx), + Expression::BinaryExpression(_) + | Expression::PrivateInExpression(_) + | Expression::UnaryExpression(_) + | Expression::ConditionalExpression(_) + | Expression::LogicalExpression(_) + | Expression::UpdateExpression(_) => self.emit_operator(expr, ctx), + Expression::StaticMemberExpression(_) + | Expression::ComputedMemberExpression(_) + | Expression::PrivateFieldExpression(_) + | Expression::ChainExpression(_) => self.emit_member_domain(expr, ctx), + Expression::ObjectExpression(_) | Expression::ArrayExpression(_) => self.emit_object_domain(expr, ctx), + Expression::AssignmentExpression(assign) => self.emit_assignment_expression(assign, ctx), + Expression::TemplateLiteral(_) | Expression::TaggedTemplateExpression(_) => { + self.emit_template_domain(expr, ctx) + } + Expression::ArrowFunctionExpression(_) + | Expression::FunctionExpression(_) + | Expression::ClassExpression(_) + | Expression::NewExpression(_) => self.emit_function_domain(expr, ctx), + Expression::Identifier(ident) => self.emit_identifier_expression(ident, ctx), + Expression::CallExpression(_) => self.emit_call_domain(expr, ctx), + Expression::ThisExpression(_) => self.emit_this_expression(ctx), + Expression::SequenceExpression(seq) => self.emit_sequence_expression(seq, ctx), + Expression::ParenthesizedExpression(p) => self.emit_parenthesized_expression(p, ctx), + _ => self.emit_unsupported_expression(expr, ctx), + } + } + + /// Emit a full program into an IRFunction (top-level module body). + /// Split from the original `Compiler::compile`: this is the emit half; + /// oxide_compiler::Compiler::compile calls this then `oxide_ir::lower::lower`. + pub fn emit_program(&self, program: &oxide_parser::Program) -> Result { + let mut ctx = CompileCtx::new(); + ctx.pre_register_builtins(); + self.predeclare_function_declarations(&program.body, &mut ctx); + + // Pre-register builtin identifier references before any temporary register + // is emitted, keeping builtin slots clear of the temporary register pool. + self.pre_register_builtin_references(&program.body, &mut ctx); + + // Pre-declare top-level `var` names so hoisted function declarations + // (emitted in the first sub-pass below) can resolve the outer vars. + self.predeclare_var_declarations(&program.body, &mut ctx); + + // 闭包捕获分析(AST 级,emit 前确定) + ctx.own_bindings = self.collect_own_binding_names(&[], &program.body); + ctx.captured_bindings = self.collect_captured_bindings(&program.body, &ctx.own_bindings); + + // First sub-pass: emit FunctionDeclarations (hoisting) + // This ensures function objects are available before any code runs. + for stmt in &program.body { + if matches!(stmt, Statement::FunctionDeclaration(_)) { + self.emit_statement(stmt, &mut ctx)?; + } + } + + // Second sub-pass: emit all other statements + let mut last_result: Option = None; + for stmt in &program.body { + if matches!(stmt, Statement::FunctionDeclaration(_)) { + continue; // Already emitted above + } + match self.emit_statement(stmt, &mut ctx)? { + Some(r) => last_result = Some(r), + None => last_result = None, + } + } + if let Some(r) = last_result { + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::None, Operand::Reg(r as u32), Operand::None)); + } else { + let undef_idx = ctx.add_constant(Constant::Undefined); + ctx.inst(Inst::load_const(Operand::None, undef_idx)); + } + ctx.inst(Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None)); + + let ir = ctx.assemble_ir( + oxide_ir::ParamLayout { + base: ctx.scopes.builtin_reg_map.len() as u32, + count: 0, + }, + None, + ); + Ok(ir) + } +} + +impl Default for Emitter { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/oxide_compiler/src/emit_ctx.rs b/crates/oxide_emit/src/emit_ctx.rs similarity index 98% rename from crates/oxide_compiler/src/emit_ctx.rs rename to crates/oxide_emit/src/emit_ctx.rs index b012dcef..03998c58 100644 --- a/crates/oxide_compiler/src/emit_ctx.rs +++ b/crates/oxide_emit/src/emit_ctx.rs @@ -6,9 +6,9 @@ //! Execution-stream fields (insts, registers, pc, …) stay flat on //! `CompileCtx`. -use crate::compiler::LabelScope; -use oxide_ir::operand::LabelId; use crate::symbol_table::SymbolTable; +use crate::LabelScope; +use oxide_ir::operand::LabelId; /// Jump-target / labeled-statement resolution state. pub(crate) struct LabelCtx { diff --git a/crates/oxide_compiler/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs similarity index 99% rename from crates/oxide_compiler/src/expr/assignment.rs rename to crates/oxide_emit/src/expr/assignment.rs index 9d2aed67..449b0593 100644 --- a/crates/oxide_compiler/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::AssignmentOperator; -impl Compiler { +impl Emitter { pub(crate) fn emit_assignment_expression( &self, assign: &oxide_parser::AssignmentExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/call.rs b/crates/oxide_emit/src/expr/call.rs similarity index 98% rename from crates/oxide_compiler/src/expr/call.rs rename to crates/oxide_emit/src/expr/call.rs index 70202878..8180719c 100644 --- a/crates/oxide_compiler/src/expr/call.rs +++ b/crates/oxide_emit/src/expr/call.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::Expression; -impl Compiler { +impl Emitter { fn emit_call_expression(&self, call: &oxide_parser::CallExpression, ctx: &mut CompileCtx) -> Result { if matches!(&call.callee, Expression::Super(_)) { if !ctx.in_derived_constructor { diff --git a/crates/oxide_compiler/src/expr/function/mod.rs b/crates/oxide_emit/src/expr/function/mod.rs similarity index 98% rename from crates/oxide_compiler/src/expr/function/mod.rs rename to crates/oxide_emit/src/expr/function/mod.rs index 01676a2e..0b1370c5 100644 --- a/crates/oxide_compiler/src/expr/function/mod.rs +++ b/crates/oxide_emit/src/expr/function/mod.rs @@ -1,9 +1,9 @@ -use crate::compiler::{CompileCtx, Compiler, ParamSpec}; +use crate::{CompileCtx, Emitter, ParamSpec}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_parser::{Class, Expression, Statement}; -impl Compiler { +impl Emitter { fn emit_arrow_function_expression( &self, arrow: &oxide_parser::ArrowFunctionExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/identifier.rs b/crates/oxide_emit/src/expr/identifier.rs similarity index 95% rename from crates/oxide_compiler/src/expr/identifier.rs rename to crates/oxide_emit/src/expr/identifier.rs index 5cc565c7..a052a953 100644 --- a/crates/oxide_compiler/src/expr/identifier.rs +++ b/crates/oxide_emit/src/expr/identifier.rs @@ -1,9 +1,9 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; -impl Compiler { +impl Emitter { pub(crate) fn emit_identifier_expression( &self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/literal.rs b/crates/oxide_emit/src/expr/literal.rs similarity index 97% rename from crates/oxide_compiler/src/expr/literal.rs rename to crates/oxide_emit/src/expr/literal.rs index 06a2a275..5b6aa758 100644 --- a/crates/oxide_compiler/src/expr/literal.rs +++ b/crates/oxide_emit/src/expr/literal.rs @@ -1,11 +1,11 @@ -use crate::compiler::{is_int_literal, CompileCtx, Compiler}; +use crate::{is_int_literal, CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::Expression; -impl Compiler { +impl Emitter { pub(crate) fn emit_literal(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { match expr { Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx), diff --git a/crates/oxide_compiler/src/expr/member/mod.rs b/crates/oxide_emit/src/expr/member/mod.rs similarity index 98% rename from crates/oxide_compiler/src/expr/member/mod.rs rename to crates/oxide_emit/src/expr/member/mod.rs index d76a21ec..2f15b326 100644 --- a/crates/oxide_compiler/src/expr/member/mod.rs +++ b/crates/oxide_emit/src/expr/member/mod.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::Expression; -impl Compiler { +impl Emitter { fn emit_static_member_expression( &self, member: &oxide_parser::StaticMemberExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/mod.rs b/crates/oxide_emit/src/expr/mod.rs similarity index 92% rename from crates/oxide_compiler/src/expr/mod.rs rename to crates/oxide_emit/src/expr/mod.rs index 02ecbdd1..82660cf1 100644 --- a/crates/oxide_compiler/src/expr/mod.rs +++ b/crates/oxide_emit/src/expr/mod.rs @@ -1,4 +1,4 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Expression; pub mod assignment; @@ -13,7 +13,7 @@ pub mod parenthesized; pub mod template; pub mod this; -impl Compiler { +impl Emitter { pub(crate) fn emit_unsupported_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { let _ = ctx; Err(format!("unsupported expression type: {:?}", expr)) diff --git a/crates/oxide_compiler/src/expr/object/array.rs b/crates/oxide_emit/src/expr/object/array.rs similarity index 95% rename from crates/oxide_compiler/src/expr/object/array.rs rename to crates/oxide_emit/src/expr/object/array.rs index 763d1bd7..17792af0 100644 --- a/crates/oxide_compiler/src/expr/object/array.rs +++ b/crates/oxide_emit/src/expr/object/array.rs @@ -1,9 +1,9 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; -impl Compiler { +impl Emitter { pub(crate) fn emit_array_expression( &self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/object/mod.rs b/crates/oxide_emit/src/expr/object/mod.rs similarity index 87% rename from crates/oxide_compiler/src/expr/object/mod.rs rename to crates/oxide_emit/src/expr/object/mod.rs index 733b77e3..8a26daf7 100644 --- a/crates/oxide_compiler/src/expr/object/mod.rs +++ b/crates/oxide_emit/src/expr/object/mod.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Expression; mod array; mod object_literal; -impl Compiler { +impl Emitter { pub(crate) fn emit_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { match expr { Expression::ObjectExpression(obj) => self.emit_object_expression(obj, ctx), diff --git a/crates/oxide_compiler/src/expr/object/object_literal.rs b/crates/oxide_emit/src/expr/object/object_literal.rs similarity index 98% rename from crates/oxide_compiler/src/expr/object/object_literal.rs rename to crates/oxide_emit/src/expr/object/object_literal.rs index 2adcb3af..3cc8b4bf 100644 --- a/crates/oxide_compiler/src/expr/object/object_literal.rs +++ b/crates/oxide_emit/src/expr/object/object_literal.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind}; -impl Compiler { +impl Emitter { pub(crate) fn emit_object_expression( &self, obj: &oxide_parser::ObjectExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs similarity index 99% rename from crates/oxide_compiler/src/expr/operator/mod.rs rename to crates/oxide_emit/src/expr/operator/mod.rs index df76d008..0525cf5e 100644 --- a/crates/oxide_compiler/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -1,11 +1,11 @@ -use crate::compiler::{is_side_effect_free, BinaryOperator, CompileCtx, Compiler}; +use crate::{is_side_effect_free, BinaryOperator, CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator}; -impl Compiler { +impl Emitter { fn emit_private_in_expression( &self, pin: &oxide_parser::PrivateInExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/parenthesized.rs b/crates/oxide_emit/src/expr/parenthesized.rs similarity index 78% rename from crates/oxide_compiler/src/expr/parenthesized.rs rename to crates/oxide_emit/src/expr/parenthesized.rs index 2f8c5981..a85ce882 100644 --- a/crates/oxide_compiler/src/expr/parenthesized.rs +++ b/crates/oxide_emit/src/expr/parenthesized.rs @@ -1,6 +1,6 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; -impl Compiler { +impl Emitter { pub(crate) fn emit_parenthesized_expression( &self, p: &oxide_parser::ParenthesizedExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/template/mod.rs b/crates/oxide_emit/src/expr/template/mod.rs similarity index 88% rename from crates/oxide_compiler/src/expr/template/mod.rs rename to crates/oxide_emit/src/expr/template/mod.rs index 70ac5585..b8d01fef 100644 --- a/crates/oxide_compiler/src/expr/template/mod.rs +++ b/crates/oxide_emit/src/expr/template/mod.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Expression; mod tagged; mod template_literal; -impl Compiler { +impl Emitter { pub(crate) fn emit_template_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { match expr { Expression::TemplateLiteral(tl) => self.emit_template_literal_expression(tl, ctx), diff --git a/crates/oxide_compiler/src/expr/template/tagged.rs b/crates/oxide_emit/src/expr/template/tagged.rs similarity index 98% rename from crates/oxide_compiler/src/expr/template/tagged.rs rename to crates/oxide_emit/src/expr/template/tagged.rs index bca50e79..8fa557bd 100644 --- a/crates/oxide_compiler/src/expr/template/tagged.rs +++ b/crates/oxide_emit/src/expr/template/tagged.rs @@ -1,9 +1,9 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; -impl Compiler { +impl Emitter { pub(crate) fn emit_tagged_template_expression( &self, tt: &oxide_parser::TaggedTemplateExpression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/template/template_literal.rs b/crates/oxide_emit/src/expr/template/template_literal.rs similarity index 96% rename from crates/oxide_compiler/src/expr/template/template_literal.rs rename to crates/oxide_emit/src/expr/template/template_literal.rs index 08c4e2b2..96c24d7e 100644 --- a/crates/oxide_compiler/src/expr/template/template_literal.rs +++ b/crates/oxide_emit/src/expr/template/template_literal.rs @@ -1,9 +1,9 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; -impl Compiler { +impl Emitter { pub(crate) fn emit_template_literal_expression( &self, tl: &oxide_parser::TemplateLiteral, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/expr/this.rs b/crates/oxide_emit/src/expr/this.rs similarity index 88% rename from crates/oxide_compiler/src/expr/this.rs rename to crates/oxide_emit/src/expr/this.rs index 5a7fa78e..1cc184e0 100644 --- a/crates/oxide_compiler/src/expr/this.rs +++ b/crates/oxide_emit/src/expr/this.rs @@ -1,9 +1,9 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; -impl Compiler { +impl Emitter { pub(crate) fn emit_this_expression(&self, ctx: &mut CompileCtx) -> Result { let r = ctx.alloc_reg(); let src = ctx.static_block_this_reg.unwrap_or(254); diff --git a/crates/oxide_emit/src/lib.rs b/crates/oxide_emit/src/lib.rs new file mode 100644 index 00000000..68f361bf --- /dev/null +++ b/crates/oxide_emit/src/lib.rs @@ -0,0 +1,14 @@ +pub mod class; +pub mod closure; +pub mod emit; +pub mod emit_ctx; +pub mod expr; +pub mod prepass; +pub mod shared; +pub mod stmt; +pub mod symbol_table; + +pub use emit::{CompileCtx, Emitter, FunctionBodyContext, LabelScope, ParamSpec}; +pub use emit::{is_int_literal, is_side_effect_free}; +pub use oxide_bytecode::module::Constant; +pub use oxide_parser::{AssignmentOperator, BinaryOperator, UnaryOperator}; diff --git a/crates/oxide_compiler/src/prepass.rs b/crates/oxide_emit/src/prepass.rs similarity index 99% rename from crates/oxide_compiler/src/prepass.rs rename to crates/oxide_emit/src/prepass.rs index 93f7bd79..b7ca86fd 100644 --- a/crates/oxide_compiler/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -1,7 +1,7 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::{Expression, Statement, VariableDeclarationKind}; -impl Compiler { +impl Emitter { /// builtin slot allocation ahead of the temporary register pool. pub(crate) fn pre_register_builtin_references(&self, stmts: &[Statement], ctx: &mut CompileCtx) { @@ -340,4 +340,3 @@ impl Compiler { } } - diff --git a/crates/oxide_compiler/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs similarity index 99% rename from crates/oxide_compiler/src/shared/binding.rs rename to crates/oxide_emit/src/shared/binding.rs index 23a3cdf7..c10835ef 100644 --- a/crates/oxide_compiler/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -1,4 +1,4 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; @@ -8,7 +8,7 @@ use oxide_parser::{ BindingPattern, Expression, ObjectAssignmentTarget, ObjectPattern, VariableDeclarationKind, }; -impl Compiler { +impl Emitter { fn emit_default_if_undefined( &self, val_reg: u8, default_expr: &Expression, ctx: &mut CompileCtx, ) -> Result { diff --git a/crates/oxide_compiler/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs similarity index 99% rename from crates/oxide_compiler/src/shared/chain.rs rename to crates/oxide_emit/src/shared/chain.rs index 3ca30a2e..3c8652c2 100644 --- a/crates/oxide_compiler/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::{LabelId, Operand}; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::{ChainElement, Expression, LogicalOperator, PropertyKey}; -impl Compiler { +impl Emitter { fn emit_optional_guard(&self, reg: u8, short_label: LabelId, ctx: &mut CompileCtx) -> Result<(), String> { let dup_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg as u32), Operand::Reg(reg as u32), Operand::None)); diff --git a/crates/oxide_compiler/src/shared/class_field.rs b/crates/oxide_emit/src/shared/class_field.rs similarity index 97% rename from crates/oxide_compiler/src/shared/class_field.rs rename to crates/oxide_emit/src/shared/class_field.rs index 5fa85aa7..6667d60c 100644 --- a/crates/oxide_compiler/src/shared/class_field.rs +++ b/crates/oxide_emit/src/shared/class_field.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::{Expression, PropertyKey}; -impl Compiler { +impl Emitter { pub(crate) fn emit_public_field_init( &self, target: Operand, key: &PropertyKey, computed: bool, value: Option<&Expression>, ctx: &mut CompileCtx, ) -> Result<(), String> { diff --git a/crates/oxide_compiler/src/shared/class_key.rs b/crates/oxide_emit/src/shared/class_key.rs similarity index 97% rename from crates/oxide_compiler/src/shared/class_key.rs rename to crates/oxide_emit/src/shared/class_key.rs index 3d07a57c..1fe27f86 100644 --- a/crates/oxide_compiler/src/shared/class_key.rs +++ b/crates/oxide_emit/src/shared/class_key.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_parser::PropertyKey; -impl Compiler { +impl Emitter { fn private_name_id(&self, name: &str, ctx: &CompileCtx) -> Result { ctx.scopes .private_name_map diff --git a/crates/oxide_compiler/src/shared/mod.rs b/crates/oxide_emit/src/shared/mod.rs similarity index 100% rename from crates/oxide_compiler/src/shared/mod.rs rename to crates/oxide_emit/src/shared/mod.rs diff --git a/crates/oxide_compiler/src/stmt/basic/break_continue.rs b/crates/oxide_emit/src/stmt/basic/break_continue.rs similarity index 96% rename from crates/oxide_compiler/src/stmt/basic/break_continue.rs rename to crates/oxide_emit/src/stmt/basic/break_continue.rs index 4eb54abd..f292b39b 100644 --- a/crates/oxide_compiler/src/stmt/basic/break_continue.rs +++ b/crates/oxide_emit/src/stmt/basic/break_continue.rs @@ -1,7 +1,7 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; -impl Compiler { +impl Emitter { pub(crate) fn emit_break_statement( &self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx, ) -> Result, String> { diff --git a/crates/oxide_compiler/src/stmt/basic/expression.rs b/crates/oxide_emit/src/stmt/basic/expression.rs similarity index 88% rename from crates/oxide_compiler/src/stmt/basic/expression.rs rename to crates/oxide_emit/src/stmt/basic/expression.rs index 00861c22..43271ae1 100644 --- a/crates/oxide_compiler/src/stmt/basic/expression.rs +++ b/crates/oxide_emit/src/stmt/basic/expression.rs @@ -1,7 +1,7 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; -impl Compiler { +impl Emitter { fn emit_expression_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ExpressionStatement(es) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/basic/labeled.rs b/crates/oxide_emit/src/stmt/basic/labeled.rs similarity index 94% rename from crates/oxide_compiler/src/stmt/basic/labeled.rs rename to crates/oxide_emit/src/stmt/basic/labeled.rs index f9969ac4..1d15ba70 100644 --- a/crates/oxide_compiler/src/stmt/basic/labeled.rs +++ b/crates/oxide_emit/src/stmt/basic/labeled.rs @@ -1,7 +1,7 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; -impl Compiler { +impl Emitter { fn is_iteration_statement(stmt: &Statement) -> bool { matches!( stmt, diff --git a/crates/oxide_compiler/src/stmt/basic/mod.rs b/crates/oxide_emit/src/stmt/basic/mod.rs similarity index 89% rename from crates/oxide_compiler/src/stmt/basic/mod.rs rename to crates/oxide_emit/src/stmt/basic/mod.rs index 06c691ba..546bf3fe 100644 --- a/crates/oxide_compiler/src/stmt/basic/mod.rs +++ b/crates/oxide_emit/src/stmt/basic/mod.rs @@ -1,4 +1,4 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; mod break_continue; @@ -6,7 +6,7 @@ mod expression; mod labeled; mod return_stmt; -impl Compiler { +impl Emitter { pub(crate) fn emit_basic_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::ExpressionStatement(_) => self.emit_basic_expression(stmt, ctx), diff --git a/crates/oxide_compiler/src/stmt/basic/return_stmt.rs b/crates/oxide_emit/src/stmt/basic/return_stmt.rs similarity index 93% rename from crates/oxide_compiler/src/stmt/basic/return_stmt.rs rename to crates/oxide_emit/src/stmt/basic/return_stmt.rs index 6a377b82..3b84d164 100644 --- a/crates/oxide_compiler/src/stmt/basic/return_stmt.rs +++ b/crates/oxide_emit/src/stmt/basic/return_stmt.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::Statement; -impl Compiler { +impl Emitter { fn emit_return_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ReturnStatement(ret) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/block.rs b/crates/oxide_emit/src/stmt/block.rs similarity index 92% rename from crates/oxide_compiler/src/stmt/block.rs rename to crates/oxide_emit/src/stmt/block.rs index ab5c64ef..96f91f75 100644 --- a/crates/oxide_compiler/src/stmt/block.rs +++ b/crates/oxide_emit/src/stmt/block.rs @@ -1,7 +1,7 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; -impl Compiler { +impl Emitter { fn emit_block_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::BlockStatement(block) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/control.rs b/crates/oxide_emit/src/stmt/control.rs similarity index 97% rename from crates/oxide_compiler/src/stmt/control.rs rename to crates/oxide_emit/src/stmt/control.rs index ef120d74..0fe05668 100644 --- a/crates/oxide_compiler/src/stmt/control.rs +++ b/crates/oxide_emit/src/stmt/control.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::Statement; -impl Compiler { +impl Emitter { fn emit_if_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::IfStatement(ifs) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/declaration/class_decl.rs b/crates/oxide_emit/src/stmt/declaration/class_decl.rs similarity index 95% rename from crates/oxide_compiler/src/stmt/declaration/class_decl.rs rename to crates/oxide_emit/src/stmt/declaration/class_decl.rs index c423a159..85f76a84 100644 --- a/crates/oxide_compiler/src/stmt/declaration/class_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/class_decl.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::{Statement, VariableDeclarationKind}; -impl Compiler { +impl Emitter { pub(crate) fn emit_class_declaration_statement( &self, stmt: &Statement, ctx: &mut CompileCtx, ) -> Result, String> { diff --git a/crates/oxide_compiler/src/stmt/declaration/function_decl.rs b/crates/oxide_emit/src/stmt/declaration/function_decl.rs similarity index 96% rename from crates/oxide_compiler/src/stmt/declaration/function_decl.rs rename to crates/oxide_emit/src/stmt/declaration/function_decl.rs index cf02625a..346aa5a1 100644 --- a/crates/oxide_compiler/src/stmt/declaration/function_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/function_decl.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler, ParamSpec}; +use crate::{CompileCtx, Emitter, ParamSpec}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::Statement; -impl Compiler { +impl Emitter { pub(crate) fn emit_function_declaration_statement( &self, stmt: &Statement, ctx: &mut CompileCtx, ) -> Result, String> { diff --git a/crates/oxide_compiler/src/stmt/declaration/mod.rs b/crates/oxide_emit/src/stmt/declaration/mod.rs similarity index 90% rename from crates/oxide_compiler/src/stmt/declaration/mod.rs rename to crates/oxide_emit/src/stmt/declaration/mod.rs index 269dfead..fdf3459f 100644 --- a/crates/oxide_compiler/src/stmt/declaration/mod.rs +++ b/crates/oxide_emit/src/stmt/declaration/mod.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; mod class_decl; mod function_decl; mod var; -impl Compiler { +impl Emitter { pub(crate) fn emit_declaration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::VariableDeclaration(_) => self.emit_variable_declaration_statement(stmt, ctx), diff --git a/crates/oxide_compiler/src/stmt/declaration/var.rs b/crates/oxide_emit/src/stmt/declaration/var.rs similarity index 97% rename from crates/oxide_compiler/src/stmt/declaration/var.rs rename to crates/oxide_emit/src/stmt/declaration/var.rs index d9fd56b1..d8393e8c 100644 --- a/crates/oxide_compiler/src/stmt/declaration/var.rs +++ b/crates/oxide_emit/src/stmt/declaration/var.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::{BindingPattern, Expression, Statement, VariableDeclarationKind}; -impl Compiler { +impl Emitter { pub(crate) fn emit_variable_declaration_statement( &self, stmt: &Statement, ctx: &mut CompileCtx, ) -> Result, String> { diff --git a/crates/oxide_compiler/src/stmt/exception/mod.rs b/crates/oxide_emit/src/stmt/exception/mod.rs similarity index 86% rename from crates/oxide_compiler/src/stmt/exception/mod.rs rename to crates/oxide_emit/src/stmt/exception/mod.rs index f9fe0f91..c5a7d08c 100644 --- a/crates/oxide_compiler/src/stmt/exception/mod.rs +++ b/crates/oxide_emit/src/stmt/exception/mod.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; mod throw; mod try_catch; -impl Compiler { +impl Emitter { pub(crate) fn emit_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx), diff --git a/crates/oxide_compiler/src/stmt/exception/throw.rs b/crates/oxide_emit/src/stmt/exception/throw.rs similarity index 89% rename from crates/oxide_compiler/src/stmt/exception/throw.rs rename to crates/oxide_emit/src/stmt/exception/throw.rs index 0c0abfd6..0642874f 100644 --- a/crates/oxide_compiler/src/stmt/exception/throw.rs +++ b/crates/oxide_emit/src/stmt/exception/throw.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::Statement; -impl Compiler { +impl Emitter { pub(crate) fn emit_throw_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ThrowStatement(ts) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/exception/try_catch.rs b/crates/oxide_emit/src/stmt/exception/try_catch.rs similarity index 98% rename from crates/oxide_compiler/src/stmt/exception/try_catch.rs rename to crates/oxide_emit/src/stmt/exception/try_catch.rs index 461af9e4..24f1756e 100644 --- a/crates/oxide_compiler/src/stmt/exception/try_catch.rs +++ b/crates/oxide_emit/src/stmt/exception/try_catch.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::{Statement, VariableDeclarationKind}; -impl Compiler { +impl Emitter { pub(crate) fn emit_try_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::TryStatement(ts) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/iteration/do_while.rs b/crates/oxide_emit/src/stmt/iteration/do_while.rs similarity index 93% rename from crates/oxide_compiler/src/stmt/iteration/do_while.rs rename to crates/oxide_emit/src/stmt/iteration/do_while.rs index e58795e3..c26dd714 100644 --- a/crates/oxide_compiler/src/stmt/iteration/do_while.rs +++ b/crates/oxide_emit/src/stmt/iteration/do_while.rs @@ -1,8 +1,8 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_parser::Statement; -impl Compiler { +impl Emitter { pub(crate) fn emit_do_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::DoWhileStatement(dw) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs similarity index 98% rename from crates/oxide_compiler/src/stmt/iteration/for_.rs rename to crates/oxide_emit/src/stmt/iteration/for_.rs index 0acda96d..ffe8016e 100644 --- a/crates/oxide_compiler/src/stmt/iteration/for_.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_.rs @@ -1,11 +1,11 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind}; -impl Compiler { +impl Emitter { pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ForStatement(fr) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs similarity index 97% rename from crates/oxide_compiler/src/stmt/iteration/for_in.rs rename to crates/oxide_emit/src/stmt/iteration/for_in.rs index 9cf01ac0..67b8f236 100644 --- a/crates/oxide_compiler/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind}; -impl Compiler { +impl Emitter { pub(crate) fn emit_for_in_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ForInStatement(fi) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs similarity index 97% rename from crates/oxide_compiler/src/stmt/iteration/for_of.rs rename to crates/oxide_emit/src/stmt/iteration/for_of.rs index c2110538..74176e6c 100644 --- a/crates/oxide_compiler/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::{ForStatementLeft, Statement}; -impl Compiler { +impl Emitter { pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::ForOfStatement(fo) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/iteration/mod.rs b/crates/oxide_emit/src/stmt/iteration/mod.rs similarity index 91% rename from crates/oxide_compiler/src/stmt/iteration/mod.rs rename to crates/oxide_emit/src/stmt/iteration/mod.rs index 1a3c5b34..46007d59 100644 --- a/crates/oxide_compiler/src/stmt/iteration/mod.rs +++ b/crates/oxide_emit/src/stmt/iteration/mod.rs @@ -1,4 +1,4 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_parser::Statement; mod do_while; @@ -7,7 +7,7 @@ mod for_in; mod for_of; mod while_loop; -impl Compiler { +impl Emitter { pub(crate) fn emit_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::WhileStatement(_) => self.emit_while_statement(stmt, ctx), diff --git a/crates/oxide_compiler/src/stmt/iteration/while_loop.rs b/crates/oxide_emit/src/stmt/iteration/while_loop.rs similarity index 93% rename from crates/oxide_compiler/src/stmt/iteration/while_loop.rs rename to crates/oxide_emit/src/stmt/iteration/while_loop.rs index 8ff13e5a..19f00c98 100644 --- a/crates/oxide_compiler/src/stmt/iteration/while_loop.rs +++ b/crates/oxide_emit/src/stmt/iteration/while_loop.rs @@ -1,8 +1,8 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_parser::Statement; -impl Compiler { +impl Emitter { pub(crate) fn emit_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::WhileStatement(wh) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/stmt/mod.rs b/crates/oxide_emit/src/stmt/mod.rs similarity index 100% rename from crates/oxide_compiler/src/stmt/mod.rs rename to crates/oxide_emit/src/stmt/mod.rs diff --git a/crates/oxide_compiler/src/stmt/switch.rs b/crates/oxide_emit/src/stmt/switch.rs similarity index 97% rename from crates/oxide_compiler/src/stmt/switch.rs rename to crates/oxide_emit/src/stmt/switch.rs index c97a24a3..6e35732d 100644 --- a/crates/oxide_compiler/src/stmt/switch.rs +++ b/crates/oxide_emit/src/stmt/switch.rs @@ -1,10 +1,10 @@ -use crate::compiler::{CompileCtx, Compiler}; +use crate::{CompileCtx, Emitter}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_bytecode::opcode::OpCode; use oxide_parser::Statement; -impl Compiler { +impl Emitter { fn emit_switch_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::SwitchStatement(sw) = stmt else { return Ok(None); diff --git a/crates/oxide_compiler/src/symbol_table.rs b/crates/oxide_emit/src/symbol_table.rs similarity index 100% rename from crates/oxide_compiler/src/symbol_table.rs rename to crates/oxide_emit/src/symbol_table.rs