diff --git a/crates/oxide_compiler/src/passes/class/core.rs b/crates/oxide_compiler/src/passes/class/core.rs index 9ecd4a2..1a80d6a 100644 --- a/crates/oxide_compiler/src/passes/class/core.rs +++ b/crates/oxide_compiler/src/passes/class/core.rs @@ -1,4 +1,5 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::{ClassElement, MethodDefinitionKind}; impl Compiler { pub(crate) fn count_class(&self, class: &oxide_parser::Class, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/class/mod.rs b/crates/oxide_compiler/src/passes/class/mod.rs index 90cf258..5a7ca06 100644 --- a/crates/oxide_compiler/src/passes/class/mod.rs +++ b/crates/oxide_compiler/src/passes/class/mod.rs @@ -1,2 +1 @@ -pub use super::*; pub mod core; diff --git a/crates/oxide_compiler/src/passes/expr/assignment.rs b/crates/oxide_compiler/src/passes/expr/assignment.rs index b46cfe5..36ba499 100644 --- a/crates/oxide_compiler/src/passes/expr/assignment.rs +++ b/crates/oxide_compiler/src/passes/expr/assignment.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{AssignmentOperator, Expression, LogicalOperator}; impl Compiler { fn count_assignment_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/call.rs b/crates/oxide_compiler/src/passes/expr/call.rs index 05822cf..ac93351 100644 --- a/crates/oxide_compiler/src/passes/expr/call.rs +++ b/crates/oxide_compiler/src/passes/expr/call.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Expression; impl Compiler { fn count_call_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/function/mod.rs b/crates/oxide_compiler/src/passes/expr/function/mod.rs index 6a595bb..4f7937b 100644 --- a/crates/oxide_compiler/src/passes/expr/function/mod.rs +++ b/crates/oxide_compiler/src/passes/expr/function/mod.rs @@ -1,4 +1,6 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler, ParamSpec}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{Class, Expression, Statement}; impl Compiler { fn count_arrow_function_expression(&self, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/identifier.rs b/crates/oxide_compiler/src/passes/expr/identifier.rs index 77a4d58..1cc4158 100644 --- a/crates/oxide_compiler/src/passes/expr/identifier.rs +++ b/crates/oxide_compiler/src/passes/expr/identifier.rs @@ -1,4 +1,6 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Expression; impl Compiler { pub(crate) fn count_identifier_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/literal.rs b/crates/oxide_compiler/src/passes/expr/literal.rs index 4765105..6a12131 100644 --- a/crates/oxide_compiler/src/passes/expr/literal.rs +++ b/crates/oxide_compiler/src/passes/expr/literal.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::compiler::{is_int_literal, CompileCtx, Compiler}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Expression; impl Compiler { pub(crate) fn count_literal(&self, expr: &Expression, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/member/mod.rs b/crates/oxide_compiler/src/passes/expr/member/mod.rs index 72e5201..b9d92bf 100644 --- a/crates/oxide_compiler/src/passes/expr/member/mod.rs +++ b/crates/oxide_compiler/src/passes/expr/member/mod.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Expression; impl Compiler { fn count_static_member_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/mod.rs b/crates/oxide_compiler/src/passes/expr/mod.rs index 60e8a6b..02ecbdd 100644 --- a/crates/oxide_compiler/src/passes/expr/mod.rs +++ b/crates/oxide_compiler/src/passes/expr/mod.rs @@ -1,4 +1,5 @@ -pub use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Expression; pub mod assignment; pub mod call; diff --git a/crates/oxide_compiler/src/passes/expr/object/array.rs b/crates/oxide_compiler/src/passes/expr/object/array.rs new file mode 100644 index 0000000..2e5d840 --- /dev/null +++ b/crates/oxide_compiler/src/passes/expr/object/array.rs @@ -0,0 +1,47 @@ +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::{ + module::Constant, + opcode::{self, OpCode}, +}; +use oxide_parser::Expression; + +impl Compiler { + pub(crate) fn count_array_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { + let Expression::ArrayExpression(arr) = expr else { + return; + }; + ctx.alloc_reg(); + ctx.projected_pc += 1; + let elem_checkpoint = ctx.reg_checkpoint(); + for elem in &arr.elements { + if let Some(e) = elem.as_expression() { + self.count_expression(e, ctx); + ctx.alloc_reg(); + ctx.projected_pc += 1; + ctx.projected_pc += 1; + ctx.restore_reg_checkpoint(elem_checkpoint); + } + } + } + + pub(crate) fn emit_array_expression( + &self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx, + ) -> Result { + let arr_reg = ctx.alloc_reg(); + let n = arr.elements.len() as u16; + ctx.emit(opcode::encode(OpCode::NEW_ARRAY, arr_reg, (n & 0xFF) as u8, ((n >> 8) & 0xFF) as u8)); + let elem_checkpoint = ctx.reg_checkpoint(); + for (i, elem) in arr.elements.iter().enumerate() { + let Some(e) = elem.as_expression() else { + return Err("spread not supported".into()); + }; + let val_reg = self.emit_expression(e, ctx)?; + let idx_reg = ctx.alloc_reg(); + let idx = ctx.add_constant(Constant::Int(i as i32)); + ctx.emit_load_const(idx_reg, idx); + ctx.emit(opcode::encode(OpCode::SET_ELEM, arr_reg, idx_reg, val_reg)); + ctx.restore_reg_checkpoint(elem_checkpoint); + } + Ok(arr_reg) + } +} diff --git a/crates/oxide_compiler/src/passes/expr/object/count.rs b/crates/oxide_compiler/src/passes/expr/object/count.rs deleted file mode 100644 index f62e670..0000000 --- a/crates/oxide_compiler/src/passes/expr/object/count.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::*; -impl Compiler { - fn count_object_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { - let Expression::ObjectExpression(obj) = expr else { - return; - }; - ctx.alloc_reg(); - ctx.projected_pc += 1; // NEW_OBJECT - let prop_checkpoint = ctx.reg_checkpoint(); - for prop in &obj.properties { - if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { - if matches!(p.kind, oxide_parser::PropertyKind::Get | oxide_parser::PropertyKind::Set) { - self.count_expression(&p.value, ctx); - ctx.count_load_const(); // undefined getter/setter placeholder - ctx.count_define_accessor(); // DEFINE_ACCESSOR + ext - } else { - ctx.alloc_reg(); - ctx.projected_pc += 1; // LOAD_CONST key - self.count_expression(&p.value, ctx); - ctx.projected_pc += 1; // SET_PROP - } - ctx.restore_reg_checkpoint(prop_checkpoint); - } - } - } - - fn count_array_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { - let Expression::ArrayExpression(arr) = expr else { - return; - }; - ctx.alloc_reg(); - ctx.projected_pc += 1; // NEW_ARRAY - let elem_checkpoint = ctx.reg_checkpoint(); - for elem in &arr.elements { - if let Some(e) = elem.as_expression() { - self.count_expression(e, ctx); - ctx.alloc_reg(); - ctx.projected_pc += 1; // LOAD_CONST index - ctx.projected_pc += 1; // SET_ELEM - ctx.restore_reg_checkpoint(elem_checkpoint); - } - } - } - - pub(crate) fn count_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) { - match expr { - Expression::ObjectExpression(_) => self.count_object_expression(expr, ctx), - Expression::ArrayExpression(_) => self.count_array_expression(expr, ctx), - _ => {} - } - } -} diff --git a/crates/oxide_compiler/src/passes/expr/object/mod.rs b/crates/oxide_compiler/src/passes/expr/object/mod.rs index 502b47a..3ce1958 100644 --- a/crates/oxide_compiler/src/passes/expr/object/mod.rs +++ b/crates/oxide_compiler/src/passes/expr/object/mod.rs @@ -1,3 +1,23 @@ -pub use super::*; -pub mod count; -pub mod emit; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Expression; + +mod array; +mod object_literal; + +impl Compiler { + pub(crate) fn count_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) { + match expr { + Expression::ObjectExpression(_) => self.count_object_expression(expr, ctx), + Expression::ArrayExpression(_) => self.count_array_expression(expr, ctx), + _ => {} + } + } + + pub(crate) fn emit_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { + match expr { + Expression::ObjectExpression(obj) => self.emit_object_expression(obj, ctx), + Expression::ArrayExpression(arr) => self.emit_array_expression(arr, ctx), + _ => self.emit_unsupported_expression(expr, ctx), + } + } +} diff --git a/crates/oxide_compiler/src/passes/expr/object/emit.rs b/crates/oxide_compiler/src/passes/expr/object/object_literal.rs similarity index 54% rename from crates/oxide_compiler/src/passes/expr/object/emit.rs rename to crates/oxide_compiler/src/passes/expr/object/object_literal.rs index 106a528..1d8b6d6 100644 --- a/crates/oxide_compiler/src/passes/expr/object/emit.rs +++ b/crates/oxide_compiler/src/passes/expr/object/object_literal.rs @@ -1,16 +1,48 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::{ + module::Constant, + opcode::{self, OpCode}, +}; +use oxide_parser::{Expression, ObjectPropertyKind, PropertyKey, PropertyKind}; + impl Compiler { - fn emit_object_expression(&self, obj: &oxide_parser::ObjectExpression, ctx: &mut CompileCtx) -> Result { + pub(crate) fn count_object_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { + let Expression::ObjectExpression(obj) = expr else { + return; + }; + ctx.alloc_reg(); + ctx.projected_pc += 1; + let prop_checkpoint = ctx.reg_checkpoint(); + for prop in &obj.properties { + if let ObjectPropertyKind::ObjectProperty(p) = prop { + if matches!(p.kind, PropertyKind::Get | PropertyKind::Set) { + self.count_expression(&p.value, ctx); + ctx.count_load_const(); + ctx.count_define_accessor(); + } else { + ctx.alloc_reg(); + ctx.projected_pc += 1; + self.count_expression(&p.value, ctx); + ctx.projected_pc += 1; + } + ctx.restore_reg_checkpoint(prop_checkpoint); + } + } + } + + pub(crate) fn emit_object_expression( + &self, obj: &oxide_parser::ObjectExpression, ctx: &mut CompileCtx, + ) -> Result { let obj_reg = ctx.alloc_reg(); ctx.emit(opcode::encode(OpCode::NEW_OBJECT, obj_reg, 0, 0)); let prop_checkpoint = ctx.reg_checkpoint(); for prop in &obj.properties { - let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop else { + let ObjectPropertyKind::ObjectProperty(p) = prop else { return Err("spread properties not yet supported".into()); }; let prop_name = match &p.key { - oxide_parser::PropertyKey::StaticIdentifier(ident) => ident.name.as_str().to_string(), - oxide_parser::PropertyKey::StringLiteral(s) => s.value.to_string(), + PropertyKey::StaticIdentifier(ident) => ident.name.as_str().to_string(), + PropertyKey::StringLiteral(s) => s.value.to_string(), _ => return Err("unsupported object property key type".into()), }; match p.kind { @@ -35,8 +67,6 @@ impl Compiler { let key_reg = ctx.alloc_reg(); ctx.emit_load_const(key_reg, idx); let val_reg = self.emit_expression(&p.value, ctx)?; - // Name inference: if property value is an arrow function, - // set the compiled sub_module's function_name to the property key. if matches!(&p.value, Expression::ArrowFunctionExpression(_)) { if let Some(sub_mod) = ctx.sub_modules.last_mut() { sub_mod.function_name = Some(prop_name.to_string()); @@ -49,31 +79,4 @@ impl Compiler { } Ok(obj_reg) } - - fn emit_array_expression(&self, arr: &oxide_parser::ArrayExpression, ctx: &mut CompileCtx) -> Result { - let arr_reg = ctx.alloc_reg(); - let n = arr.elements.len() as u16; - ctx.emit(opcode::encode(OpCode::NEW_ARRAY, arr_reg, (n & 0xFF) as u8, ((n >> 8) & 0xFF) as u8)); - let elem_checkpoint = ctx.reg_checkpoint(); - for (i, elem) in arr.elements.iter().enumerate() { - let Some(e) = elem.as_expression() else { - return Err("spread not supported".into()); - }; - let val_reg = self.emit_expression(e, ctx)?; - let idx_reg = ctx.alloc_reg(); - let idx = ctx.add_constant(Constant::Int(i as i32)); - ctx.emit_load_const(idx_reg, idx); - ctx.emit(opcode::encode(OpCode::SET_ELEM, arr_reg, idx_reg, val_reg)); - ctx.restore_reg_checkpoint(elem_checkpoint); - } - Ok(arr_reg) - } - - pub(crate) fn emit_object_domain(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { - match expr { - Expression::ObjectExpression(obj) => self.emit_object_expression(obj, ctx), - Expression::ArrayExpression(arr) => self.emit_array_expression(arr, ctx), - _ => self.emit_unsupported_expression(expr, ctx), - } - } } diff --git a/crates/oxide_compiler/src/passes/expr/operator/mod.rs b/crates/oxide_compiler/src/passes/expr/operator/mod.rs index ca0bfed..fdf59ae 100644 --- a/crates/oxide_compiler/src/passes/expr/operator/mod.rs +++ b/crates/oxide_compiler/src/passes/expr/operator/mod.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::compiler::{is_side_effect_free, BinaryOperator, CompileCtx, Compiler, Label}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator}; impl Compiler { fn count_binary_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { @@ -387,7 +390,6 @@ impl Compiler { fn emit_logical_expression( &self, log: &oxide_parser::LogicalExpression, ctx: &mut CompileCtx, ) -> Result { - use oxide_parser::LogicalOperator; let left_reg = self.emit_expression(&log.left, ctx)?; if is_side_effect_free(&log.left) && is_side_effect_free(&log.right) { diff --git a/crates/oxide_compiler/src/passes/expr/parenthesized.rs b/crates/oxide_compiler/src/passes/expr/parenthesized.rs index 09eca6f..a3744e7 100644 --- a/crates/oxide_compiler/src/passes/expr/parenthesized.rs +++ b/crates/oxide_compiler/src/passes/expr/parenthesized.rs @@ -1,4 +1,5 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Expression; impl Compiler { pub(crate) fn count_parenthesized_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/expr/template/count.rs b/crates/oxide_compiler/src/passes/expr/template/count.rs deleted file mode 100644 index 8a00a4a..0000000 --- a/crates/oxide_compiler/src/passes/expr/template/count.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::*; -impl Compiler { - fn count_template_literal(&self, expr: &Expression, ctx: &mut CompileCtx) { - let Expression::TemplateLiteral(tl) = expr else { - return; - }; - for expr in &tl.expressions { - self.count_expression(expr, ctx); - } - let segment_count = tl.quasis.len() + tl.expressions.len(); - ctx.count_template_str(segment_count); - } - - fn count_tagged_template_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { - let Expression::TaggedTemplateExpression(tt) = expr else { - return; - }; - self.count_expression(&tt.tag, ctx); - let quasi_count = tt.quasi.quasis.len(); - - for _ in 0..quasi_count { - ctx.count_load_const(); // string - ctx.count_load_const(); // index - ctx.count_instr(); // SET_ELEM (cooked) - } - ctx.alloc_reg(); // cooked_temp - ctx.count_instr(); // NEW_ARRAY - - for _ in 0..quasi_count { - ctx.count_load_const(); // string - ctx.count_load_const(); // index - ctx.count_instr(); // SET_ELEM (raw) - } - ctx.alloc_reg(); // raw_temp - ctx.count_instr(); // NEW_ARRAY - - for expr in &tt.quasi.expressions { - self.count_expression(expr, ctx); - } - - ctx.alloc_reg(); // cooked_slot - ctx.alloc_reg(); // raw_slot - for _ in &tt.quasi.expressions { - ctx.alloc_reg(); // expr_slot - ctx.count_instr(); // LOAD_VAR - } - ctx.count_words(2); // LOAD_VAR for cooked and raw - ctx.count_load_const(); // undefined this arg - ctx.count_call_instr_with_arg_ext(); - ctx.count_load_var(); // result <- regs[0] - } - - pub(crate) fn count_template_domain(&self, expr: &Expression, ctx: &mut CompileCtx) { - match expr { - Expression::TemplateLiteral(_) => self.count_template_literal(expr, ctx), - Expression::TaggedTemplateExpression(_) => self.count_tagged_template_expression(expr, ctx), - _ => {} - } - } -} diff --git a/crates/oxide_compiler/src/passes/expr/template/emit.rs b/crates/oxide_compiler/src/passes/expr/template/emit.rs deleted file mode 100644 index ff1b60c..0000000 --- a/crates/oxide_compiler/src/passes/expr/template/emit.rs +++ /dev/null @@ -1,135 +0,0 @@ -use super::*; -impl Compiler { - fn emit_template_literal_expression( - &self, tl: &oxide_parser::TemplateLiteral, ctx: &mut CompileCtx, - ) -> Result { - let r = ctx.alloc_reg(); - let quasis = &tl.quasis; - let expressions = &tl.expressions; - let segment_count = quasis.len() + expressions.len(); - - // Evaluate each expression, collecting registers - let expr_regs: Vec = expressions - .iter() - .map(|e| self.emit_expression(e, ctx)) - .collect::, _>>()?; - - // Add each quasi string to constant pool - let quasi_const_idxs: Vec = quasis - .iter() - .map(|q| { - let s = q.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default(); - ctx.add_constant(Constant::String(s)) - }) - .collect(); - - // Compute total length hint - let total_len_hint: usize = quasis - .iter() - .map(|q| q.value.cooked.as_ref().map(|c| c.len()).unwrap_or(0)) - .sum(); - - // Emit TEMPLATE_STR rd, 0, 0 - ctx.emit(opcode::encode(OpCode::TEMPLATE_STR, r, 0, 0)); - - // Ext word 0: (segment_count << 16) | (total_len_hint & 0xFFFF) - ctx.emit(((segment_count as u32) << 16) | (total_len_hint as u32 & 0xFFFF)); - - // Interleave segments: quasi[0], expr[0], quasi[1], expr[1], ... - let mut expr_iter = expr_regs.iter(); - for const_idx in quasi_const_idxs.iter() { - // Quasi: is_expression=0, bits 0-15 = const_idx - ctx.emit(*const_idx as u32 & 0x7FFF_FFFF); - - // Expression (if any remaining) - if let Some(expr_reg) = expr_iter.next() { - // Expression: is_expression=1, bits 0-15 = reg - ctx.emit(0x8000_0000u32 | (*expr_reg as u32)); - } - } - - Ok(r) - } - - fn emit_tagged_template_expression( - &self, tt: &oxide_parser::TaggedTemplateExpression, ctx: &mut CompileCtx, - ) -> Result { - let quasis = &tt.quasi.quasis; - let expressions = &tt.quasi.expressions; - - // 1. Evaluate tag expression - let tag_reg = self.emit_expression(&tt.tag, ctx)?; - - // 2. Build cooked strings array (into a temp register) - let cooked_temp = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::NEW_ARRAY, cooked_temp, quasis.len() as u8, 0)); - for (i, quasi) in quasis.iter().enumerate() { - let s = quasi.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default(); - let const_idx = ctx.add_constant(Constant::String(s)); - let str_reg = ctx.alloc_reg(); - ctx.emit_load_const(str_reg, const_idx); - let idx_const = ctx.add_constant(Constant::Int(i as i32)); - let idx_reg = ctx.alloc_reg(); - ctx.emit_load_const(idx_reg, idx_const); - ctx.emit(opcode::encode(OpCode::SET_ELEM, cooked_temp, idx_reg, str_reg)); - } - - // 3. Build raw strings array (into a temp register) - let raw_temp = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::NEW_ARRAY, raw_temp, quasis.len() as u8, 0)); - for (i, quasi) in quasis.iter().enumerate() { - let raw = quasi.value.raw.to_string(); - let const_idx = ctx.add_constant(Constant::String(raw)); - let str_reg = ctx.alloc_reg(); - ctx.emit_load_const(str_reg, const_idx); - let idx_const = ctx.add_constant(Constant::Int(i as i32)); - let idx_reg = ctx.alloc_reg(); - ctx.emit_load_const(idx_reg, idx_const); - ctx.emit(opcode::encode(OpCode::SET_ELEM, raw_temp, idx_reg, str_reg)); - } - - // 4. Evaluate expression arguments (into temp registers) - let mut expr_temps = Vec::new(); - for expr in expressions { - expr_temps.push(self.emit_expression(expr, ctx)?); - } - - // 5. Allocate consecutive argument slots: cooked, raw, expr[0], expr[1], ... - let cooked_slot = ctx.alloc_reg(); - let raw_slot = ctx.alloc_reg(); - let mut expr_slots = Vec::new(); - for _ in expressions { - expr_slots.push(ctx.alloc_reg()); - } - - // Copy temps to consecutive slots using LOAD_VAR (register-to-register move) - ctx.emit(opcode::encode(OpCode::LOAD_VAR, cooked_slot, cooked_temp, 0)); - ctx.emit(opcode::encode(OpCode::LOAD_VAR, raw_slot, raw_temp, 0)); - for (slot, temp) in expr_slots.iter().zip(expr_temps.iter()) { - ctx.emit(opcode::encode(OpCode::LOAD_VAR, *slot, *temp, 0)); - } - - // 6. Emit undefined as this_arg - let undef_idx = ctx.add_constant(Constant::Undefined); - let undef_reg = ctx.alloc_reg(); - ctx.emit_load_const(undef_reg, undef_idx); - - // 7. Emit CALL(tag, undefined, cooked_slot) - let arg_count = 2 + expressions.len(); - ctx.emit(opcode::encode(OpCode::CALL, tag_reg, undef_reg, cooked_slot)); - ctx.emit(arg_count as u32); - - // 8. Result from regs[0] - let result_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, 0, 0)); - Ok(result_reg) - } - - 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), - Expression::TaggedTemplateExpression(tt) => self.emit_tagged_template_expression(tt, ctx), - _ => self.emit_unsupported_expression(expr, ctx), - } - } -} diff --git a/crates/oxide_compiler/src/passes/expr/template/mod.rs b/crates/oxide_compiler/src/passes/expr/template/mod.rs index 502b47a..7a6735d 100644 --- a/crates/oxide_compiler/src/passes/expr/template/mod.rs +++ b/crates/oxide_compiler/src/passes/expr/template/mod.rs @@ -1,3 +1,23 @@ -pub use super::*; -pub mod count; -pub mod emit; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Expression; + +mod tagged; +mod template_literal; + +impl Compiler { + pub(crate) fn count_template_domain(&self, expr: &Expression, ctx: &mut CompileCtx) { + match expr { + Expression::TemplateLiteral(_) => self.count_template_literal(expr, ctx), + Expression::TaggedTemplateExpression(_) => self.count_tagged_template_expression(expr, ctx), + _ => {} + } + } + + 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), + Expression::TaggedTemplateExpression(tt) => self.emit_tagged_template_expression(tt, ctx), + _ => self.emit_unsupported_expression(expr, ctx), + } + } +} diff --git a/crates/oxide_compiler/src/passes/expr/template/tagged.rs b/crates/oxide_compiler/src/passes/expr/template/tagged.rs new file mode 100644 index 0000000..1263ed9 --- /dev/null +++ b/crates/oxide_compiler/src/passes/expr/template/tagged.rs @@ -0,0 +1,112 @@ +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::{ + module::Constant, + opcode::{self, OpCode}, +}; +use oxide_parser::Expression; + +impl Compiler { + pub(crate) fn count_tagged_template_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { + let Expression::TaggedTemplateExpression(tt) = expr else { + return; + }; + self.count_expression(&tt.tag, ctx); + let quasi_count = tt.quasi.quasis.len(); + + for _ in 0..quasi_count { + ctx.count_load_const(); + ctx.count_load_const(); + ctx.count_instr(); + } + ctx.alloc_reg(); + ctx.count_instr(); + + for _ in 0..quasi_count { + ctx.count_load_const(); + ctx.count_load_const(); + ctx.count_instr(); + } + ctx.alloc_reg(); + ctx.count_instr(); + + for expr in &tt.quasi.expressions { + self.count_expression(expr, ctx); + } + + ctx.alloc_reg(); + ctx.alloc_reg(); + for _ in &tt.quasi.expressions { + ctx.alloc_reg(); + ctx.count_instr(); + } + ctx.count_words(2); + ctx.count_load_const(); + ctx.count_call_instr_with_arg_ext(); + ctx.count_load_var(); + } + + pub(crate) fn emit_tagged_template_expression( + &self, tt: &oxide_parser::TaggedTemplateExpression, ctx: &mut CompileCtx, + ) -> Result { + let quasis = &tt.quasi.quasis; + let expressions = &tt.quasi.expressions; + + let tag_reg = self.emit_expression(&tt.tag, ctx)?; + + let cooked_temp = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::NEW_ARRAY, cooked_temp, quasis.len() as u8, 0)); + for (i, quasi) in quasis.iter().enumerate() { + let s = quasi.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default(); + let const_idx = ctx.add_constant(Constant::String(s)); + let str_reg = ctx.alloc_reg(); + ctx.emit_load_const(str_reg, const_idx); + let idx_const = ctx.add_constant(Constant::Int(i as i32)); + let idx_reg = ctx.alloc_reg(); + ctx.emit_load_const(idx_reg, idx_const); + ctx.emit(opcode::encode(OpCode::SET_ELEM, cooked_temp, idx_reg, str_reg)); + } + + let raw_temp = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::NEW_ARRAY, raw_temp, quasis.len() as u8, 0)); + for (i, quasi) in quasis.iter().enumerate() { + let raw = quasi.value.raw.to_string(); + let const_idx = ctx.add_constant(Constant::String(raw)); + let str_reg = ctx.alloc_reg(); + ctx.emit_load_const(str_reg, const_idx); + let idx_const = ctx.add_constant(Constant::Int(i as i32)); + let idx_reg = ctx.alloc_reg(); + ctx.emit_load_const(idx_reg, idx_const); + ctx.emit(opcode::encode(OpCode::SET_ELEM, raw_temp, idx_reg, str_reg)); + } + + let mut expr_temps = Vec::new(); + for expr in expressions { + expr_temps.push(self.emit_expression(expr, ctx)?); + } + + let cooked_slot = ctx.alloc_reg(); + let raw_slot = ctx.alloc_reg(); + let mut expr_slots = Vec::new(); + for _ in expressions { + expr_slots.push(ctx.alloc_reg()); + } + + ctx.emit(opcode::encode(OpCode::LOAD_VAR, cooked_slot, cooked_temp, 0)); + ctx.emit(opcode::encode(OpCode::LOAD_VAR, raw_slot, raw_temp, 0)); + for (slot, temp) in expr_slots.iter().zip(expr_temps.iter()) { + ctx.emit(opcode::encode(OpCode::LOAD_VAR, *slot, *temp, 0)); + } + + let undef_idx = ctx.add_constant(Constant::Undefined); + let undef_reg = ctx.alloc_reg(); + ctx.emit_load_const(undef_reg, undef_idx); + + let arg_count = 2 + expressions.len(); + ctx.emit(opcode::encode(OpCode::CALL, tag_reg, undef_reg, cooked_slot)); + ctx.emit(arg_count as u32); + + let result_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, 0, 0)); + Ok(result_reg) + } +} diff --git a/crates/oxide_compiler/src/passes/expr/template/template_literal.rs b/crates/oxide_compiler/src/passes/expr/template/template_literal.rs new file mode 100644 index 0000000..f0c7cbd --- /dev/null +++ b/crates/oxide_compiler/src/passes/expr/template/template_literal.rs @@ -0,0 +1,59 @@ +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::{ + module::Constant, + opcode::{self, OpCode}, +}; +use oxide_parser::Expression; + +impl Compiler { + pub(crate) fn count_template_literal(&self, expr: &Expression, ctx: &mut CompileCtx) { + let Expression::TemplateLiteral(tl) = expr else { + return; + }; + for expr in &tl.expressions { + self.count_expression(expr, ctx); + } + let segment_count = tl.quasis.len() + tl.expressions.len(); + ctx.count_template_str(segment_count); + } + + pub(crate) fn emit_template_literal_expression( + &self, tl: &oxide_parser::TemplateLiteral, ctx: &mut CompileCtx, + ) -> Result { + let r = ctx.alloc_reg(); + let quasis = &tl.quasis; + let expressions = &tl.expressions; + let segment_count = quasis.len() + expressions.len(); + + let expr_regs: Vec = expressions + .iter() + .map(|e| self.emit_expression(e, ctx)) + .collect::, _>>()?; + + let quasi_const_idxs: Vec = quasis + .iter() + .map(|q| { + let s = q.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default(); + ctx.add_constant(Constant::String(s)) + }) + .collect(); + + let total_len_hint: usize = quasis + .iter() + .map(|q| q.value.cooked.as_ref().map(|c| c.len()).unwrap_or(0)) + .sum(); + + ctx.emit(opcode::encode(OpCode::TEMPLATE_STR, r, 0, 0)); + ctx.emit(((segment_count as u32) << 16) | (total_len_hint as u32 & 0xFFFF)); + + let mut expr_iter = expr_regs.iter(); + for const_idx in quasi_const_idxs.iter() { + ctx.emit(*const_idx as u32 & 0x7FFF_FFFF); + if let Some(expr_reg) = expr_iter.next() { + ctx.emit(0x8000_0000u32 | (*expr_reg as u32)); + } + } + + Ok(r) + } +} diff --git a/crates/oxide_compiler/src/passes/expr/this.rs b/crates/oxide_compiler/src/passes/expr/this.rs index 6d8ba58..b575fb2 100644 --- a/crates/oxide_compiler/src/passes/expr/this.rs +++ b/crates/oxide_compiler/src/passes/expr/this.rs @@ -1,4 +1,5 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::opcode::{self, OpCode}; impl Compiler { pub(crate) fn count_this_expression(&self, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/stmt/basic/break_continue.rs b/crates/oxide_compiler/src/passes/stmt/basic/break_continue.rs index ca7ac06..e33fd5c 100644 --- a/crates/oxide_compiler/src/passes/stmt/basic/break_continue.rs +++ b/crates/oxide_compiler/src/passes/stmt/basic/break_continue.rs @@ -1,4 +1,5 @@ -use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::opcode; impl Compiler { pub(super) fn count_break_statement(&self, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/stmt/basic/expression.rs b/crates/oxide_compiler/src/passes/stmt/basic/expression.rs index e4ac2e5..f176763 100644 --- a/crates/oxide_compiler/src/passes/stmt/basic/expression.rs +++ b/crates/oxide_compiler/src/passes/stmt/basic/expression.rs @@ -1,4 +1,5 @@ -use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Statement; impl Compiler { pub(super) fn count_expression_statement( diff --git a/crates/oxide_compiler/src/passes/stmt/basic/labeled.rs b/crates/oxide_compiler/src/passes/stmt/basic/labeled.rs index 8740335..57c5f1c 100644 --- a/crates/oxide_compiler/src/passes/stmt/basic/labeled.rs +++ b/crates/oxide_compiler/src/passes/stmt/basic/labeled.rs @@ -1,4 +1,5 @@ -use super::super::*; +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_parser::Statement; impl Compiler { pub(super) fn count_labeled_statement(&self, stmt: &oxide_parser::LabeledStatement<'_>, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/stmt/basic/mod.rs b/crates/oxide_compiler/src/passes/stmt/basic/mod.rs index dfccce2..a413c02 100644 --- a/crates/oxide_compiler/src/passes/stmt/basic/mod.rs +++ b/crates/oxide_compiler/src/passes/stmt/basic/mod.rs @@ -1,4 +1,5 @@ -pub use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Statement; mod break_continue; mod expression; diff --git a/crates/oxide_compiler/src/passes/stmt/basic/return_stmt.rs b/crates/oxide_compiler/src/passes/stmt/basic/return_stmt.rs index ef1d9ed..4dd890d 100644 --- a/crates/oxide_compiler/src/passes/stmt/basic/return_stmt.rs +++ b/crates/oxide_compiler/src/passes/stmt/basic/return_stmt.rs @@ -1,4 +1,6 @@ -use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Statement; impl Compiler { pub(super) fn count_return_statement(&self, stmt: &oxide_parser::ReturnStatement<'_>, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/stmt/block.rs b/crates/oxide_compiler/src/passes/stmt/block.rs index 6239f8d..ddc4695 100644 --- a/crates/oxide_compiler/src/passes/stmt/block.rs +++ b/crates/oxide_compiler/src/passes/stmt/block.rs @@ -1,4 +1,5 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Statement; impl Compiler { fn count_block_statement(&self, block: &oxide_parser::BlockStatement<'_>, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/stmt/control.rs b/crates/oxide_compiler/src/passes/stmt/control.rs index 73c9b1e..f402f5f 100644 --- a/crates/oxide_compiler/src/passes/stmt/control.rs +++ b/crates/oxide_compiler/src/passes/stmt/control.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Statement; impl Compiler { fn count_if_statement(&self, stmt: &oxide_parser::IfStatement<'_>, ctx: &mut CompileCtx) { diff --git a/crates/oxide_compiler/src/passes/stmt/declaration/class_decl.rs b/crates/oxide_compiler/src/passes/stmt/declaration/class_decl.rs new file mode 100644 index 0000000..5a6cfae --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/declaration/class_decl.rs @@ -0,0 +1,30 @@ +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{Statement, VariableDeclarationKind}; + +impl Compiler { + pub(crate) fn count_class_declaration(&self, class: &oxide_parser::Class<'_>, ctx: &mut CompileCtx) { + ctx.alloc_reg(); + self.count_class(class, ctx); + ctx.projected_pc += 1; + } + + pub(crate) fn emit_class_declaration_statement( + &self, stmt: &Statement, ctx: &mut CompileCtx, + ) -> Result, String> { + let Statement::ClassDeclaration(class) = stmt else { + return Err("ClassDeclaration without name".into()); + }; + let name = class + .id + .as_ref() + .map(|id| id.name.to_string()) + .ok_or_else(|| "ClassDeclaration without name".to_string())?; + let var_reg = ctx.alloc_reg(); + ctx.declare(&name, var_reg, VariableDeclarationKind::Let, false)?; + ctx.init_var(&name); + let ctor_reg = self.emit_class(class, ctx)?; + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, ctor_reg, 0)); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/declaration/function_decl.rs b/crates/oxide_compiler/src/passes/stmt/declaration/function_decl.rs new file mode 100644 index 0000000..f284373 --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/declaration/function_decl.rs @@ -0,0 +1,51 @@ +use crate::compiler::{CompileCtx, Compiler, ParamSpec}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{Statement, VariableDeclarationKind}; + +impl Compiler { + pub(crate) fn count_function_declaration(&self, decl: &oxide_parser::Function<'_>, ctx: &mut CompileCtx) { + let name = if let Some(id) = &decl.id { + id.name.to_string() + } else { + return; + }; + let func_reg = ctx.alloc_reg(); + let _ = ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false); + ctx.count_words(2); + } + + pub(crate) fn emit_function_declaration_statement( + &self, stmt: &Statement, ctx: &mut CompileCtx, + ) -> Result, String> { + let Statement::FunctionDeclaration(fd) = stmt else { + return Err("FunctionDeclaration without name".into()); + }; + let name = if let Some(id) = &fd.id { + id.name.to_string() + } else { + return Err("FunctionDeclaration without name".into()); + }; + let mut param_names = Vec::new(); + for (idx, param) in fd.params.items.iter().enumerate() { + match ¶m.pattern { + oxide_parser::BindingPattern::BindingIdentifier(bi) => { + param_names.push(ParamSpec::Identifier(bi.name.to_string())) + } + pattern => param_names.push(ParamSpec::Pattern { + synthetic_name: format!("@@param_{idx}"), + pattern, + }), + } + } + let body_stmts: &[Statement] = if let Some(body) = &fd.body { &body.statements } else { &[] }; + let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, false, false)?; + sub_module.function_name = Some(name.clone()); + ctx.sub_modules.push(sub_module); + let sub_idx = ctx.sub_modules.len() as u32; + let var_reg = ctx.lookup(&name)?; + ctx.reserve_reg(var_reg); + ctx.emit_create_closure(var_reg, sub_idx); + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, var_reg, 0)); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/declaration/mod.rs b/crates/oxide_compiler/src/passes/stmt/declaration/mod.rs index 9c74770..5dcdeb6 100644 --- a/crates/oxide_compiler/src/passes/stmt/declaration/mod.rs +++ b/crates/oxide_compiler/src/passes/stmt/declaration/mod.rs @@ -1,3 +1,26 @@ -pub use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Statement; -pub mod var; +mod class_decl; +mod function_decl; +mod var; + +impl Compiler { + pub(crate) fn count_declaration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { + match stmt { + Statement::VariableDeclaration(decl) => self.count_variable_declaration(decl, ctx), + Statement::FunctionDeclaration(fd) => self.count_function_declaration(fd, ctx), + Statement::ClassDeclaration(class) => self.count_class_declaration(class, ctx), + _ => {} + } + } + + 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), + Statement::FunctionDeclaration(_) => self.emit_function_declaration_statement(stmt, ctx), + Statement::ClassDeclaration(_) => self.emit_class_declaration_statement(stmt, ctx), + _ => Ok(None), + } + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/declaration/var.rs b/crates/oxide_compiler/src/passes/stmt/declaration/var.rs index 1c224a0..258d41c 100644 --- a/crates/oxide_compiler/src/passes/stmt/declaration/var.rs +++ b/crates/oxide_compiler/src/passes/stmt/declaration/var.rs @@ -1,7 +1,14 @@ -use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::{ + module::Constant, + opcode::{self, OpCode}, +}; +use oxide_parser::{BindingPattern, Expression, Statement, VariableDeclarationKind}; impl Compiler { - fn count_variable_declaration(&self, decl: &oxide_parser::VariableDeclaration<'_>, ctx: &mut CompileCtx) { + pub(crate) fn count_variable_declaration( + &self, decl: &oxide_parser::VariableDeclaration<'_>, ctx: &mut CompileCtx, + ) { let is_var = matches!(decl.kind, VariableDeclarationKind::Var); for d in &decl.declarations { if let Some(init) = &d.init { @@ -22,34 +29,6 @@ impl Compiler { } } - fn count_function_declaration(&self, decl: &oxide_parser::Function<'_>, ctx: &mut CompileCtx) { - let name = if let Some(id) = &decl.id { - id.name.to_string() - } else { - return; - }; - let func_reg = ctx.alloc_reg(); - let _ = ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false); - ctx.count_words(2); - } - - fn count_class_declaration(&self, class: &oxide_parser::Class<'_>, ctx: &mut CompileCtx) { - ctx.alloc_reg(); - self.count_class(class, ctx); - ctx.projected_pc += 1; - } - - pub(crate) fn count_declaration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { - match stmt { - Statement::VariableDeclaration(decl) => self.count_variable_declaration(decl, ctx), - Statement::FunctionDeclaration(fd) => self.count_function_declaration(fd, ctx), - Statement::ClassDeclaration(class) => self.count_class_declaration(class, ctx), - _ => {} - } - } -} - -impl Compiler { pub(crate) fn emit_variable_declaration_statement( &self, stmt: &Statement, ctx: &mut CompileCtx, ) -> Result, String> { @@ -96,67 +75,4 @@ impl Compiler { } Ok(r) } - - pub(crate) fn emit_function_declaration_statement( - &self, stmt: &Statement, ctx: &mut CompileCtx, - ) -> Result, String> { - let Statement::FunctionDeclaration(fd) = stmt else { - return Err("FunctionDeclaration without name".into()); - }; - let name = if let Some(id) = &fd.id { - id.name.to_string() - } else { - return Err("FunctionDeclaration without name".into()); - }; - let mut param_names = Vec::new(); - for (idx, param) in fd.params.items.iter().enumerate() { - match ¶m.pattern { - oxide_parser::BindingPattern::BindingIdentifier(bi) => { - param_names.push(ParamSpec::Identifier(bi.name.to_string())) - } - pattern => param_names.push(ParamSpec::Pattern { - synthetic_name: format!("@@param_{idx}"), - pattern, - }), - } - } - let body_stmts: &[Statement] = if let Some(body) = &fd.body { &body.statements } else { &[] }; - let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, false, false)?; - sub_module.function_name = Some(name.clone()); - ctx.sub_modules.push(sub_module); - let sub_idx = ctx.sub_modules.len() as u32; - let var_reg = ctx.lookup(&name)?; - ctx.reserve_reg(var_reg); - ctx.emit_create_closure(var_reg, sub_idx); - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, var_reg, 0)); - Ok(None) - } - - pub(crate) fn emit_class_declaration_statement( - &self, stmt: &Statement, ctx: &mut CompileCtx, - ) -> Result, String> { - let Statement::ClassDeclaration(class) = stmt else { - return Err("ClassDeclaration without name".into()); - }; - let name = class - .id - .as_ref() - .map(|id| id.name.to_string()) - .ok_or_else(|| "ClassDeclaration without name".to_string())?; - let var_reg = ctx.alloc_reg(); - ctx.declare(&name, var_reg, VariableDeclarationKind::Let, false)?; - ctx.init_var(&name); - let ctor_reg = self.emit_class(class, ctx)?; - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, ctor_reg, 0)); - Ok(None) - } - - 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), - Statement::FunctionDeclaration(_) => self.emit_function_declaration_statement(stmt, ctx), - Statement::ClassDeclaration(_) => self.emit_class_declaration_statement(stmt, ctx), - _ => Ok(None), - } - } } diff --git a/crates/oxide_compiler/src/passes/stmt/exception/count.rs b/crates/oxide_compiler/src/passes/stmt/exception/count.rs deleted file mode 100644 index 31db082..0000000 --- a/crates/oxide_compiler/src/passes/stmt/exception/count.rs +++ /dev/null @@ -1,67 +0,0 @@ -use super::super::*; - -impl Compiler { - fn count_throw_statement(&self, stmt: &oxide_parser::ThrowStatement<'_>, ctx: &mut CompileCtx) { - self.count_expression(&stmt.argument, ctx); - ctx.projected_pc += 1; - } - - fn count_try_statement(&self, stmt: &oxide_parser::TryStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let catch_label = Label::CatchBody(id); - let try_end_label = Label::TryEnd(id); - let has_catch = stmt.handler.is_some(); - let has_finally = stmt.finalizer.is_some(); - ctx.alloc_reg(); - if has_finally { - ctx.projected_pc += 1; - } - if has_catch { - ctx.projected_pc += 1; - } - for s in &stmt.block.body { - self.count_statement(s, ctx); - } - ctx.projected_pc += 1; - if has_catch { - ctx.projected_pc += 1; - } - let jmp_needed = has_catch || has_finally; - if jmp_needed { - ctx.projected_pc += 1; - } - ctx.labels.label_map.insert(catch_label, ctx.projected_pc); - if let Some(catch) = &stmt.handler { - ctx.push_scope(); - if let Some(param) = &catch.param { - ctx.alloc_reg(); - if let oxide_parser::BindingPattern::BindingIdentifier(_) = ¶m.pattern { - ctx.projected_pc += 1; - } - } - for cs in &catch.body.body { - self.count_statement(cs, ctx); - } - ctx.projected_pc += 1; - ctx.pop_scope(); - } - if let Some(finally) = &stmt.finalizer { - let finally_label = Label::FinallyBody(id); - ctx.labels.label_map.insert(finally_label, ctx.projected_pc); - for fs in &finally.body { - self.count_statement(fs, ctx); - } - ctx.projected_pc += 1; - ctx.projected_pc += 1; - } - ctx.labels.label_map.insert(try_end_label, ctx.projected_pc); - } - - pub(crate) fn count_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { - match stmt { - Statement::ThrowStatement(ts) => self.count_throw_statement(ts, ctx), - Statement::TryStatement(ts) => self.count_try_statement(ts, ctx), - _ => {} - } - } -} diff --git a/crates/oxide_compiler/src/passes/stmt/exception/mod.rs b/crates/oxide_compiler/src/passes/stmt/exception/mod.rs index e1311e8..8186a8e 100644 --- a/crates/oxide_compiler/src/passes/stmt/exception/mod.rs +++ b/crates/oxide_compiler/src/passes/stmt/exception/mod.rs @@ -1,4 +1,23 @@ -pub use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Statement; -pub mod count; -pub mod emit; +mod throw; +mod try_catch; + +impl Compiler { + pub(crate) fn count_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { + match stmt { + Statement::ThrowStatement(ts) => self.count_throw_statement(ts, ctx), + Statement::TryStatement(ts) => self.count_try_statement(ts, ctx), + _ => {} + } + } + + pub(crate) fn emit_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + match stmt { + Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx), + Statement::TryStatement(_) => self.emit_try_statement(stmt, ctx), + _ => Ok(None), + } + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/exception/throw.rs b/crates/oxide_compiler/src/passes/stmt/exception/throw.rs new file mode 100644 index 0000000..0d52d36 --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/exception/throw.rs @@ -0,0 +1,19 @@ +use crate::compiler::{CompileCtx, Compiler}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Statement; + +impl Compiler { + pub(crate) fn count_throw_statement(&self, stmt: &oxide_parser::ThrowStatement<'_>, ctx: &mut CompileCtx) { + self.count_expression(&stmt.argument, ctx); + ctx.projected_pc += 1; + } + + pub(crate) fn emit_throw_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::ThrowStatement(ts) = stmt else { + return Ok(None); + }; + let exc_reg = self.emit_expression(&ts.argument, ctx)?; + ctx.emit(opcode::encode(OpCode::THROW, exc_reg, 0, 0)); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/exception/emit.rs b/crates/oxide_compiler/src/passes/stmt/exception/try_catch.rs similarity index 69% rename from crates/oxide_compiler/src/passes/stmt/exception/emit.rs rename to crates/oxide_compiler/src/passes/stmt/exception/try_catch.rs index 25746ed..d987fe7 100644 --- a/crates/oxide_compiler/src/passes/stmt/exception/emit.rs +++ b/crates/oxide_compiler/src/passes/stmt/exception/try_catch.rs @@ -1,13 +1,57 @@ -use super::super::*; +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{Statement, VariableDeclarationKind}; impl Compiler { - pub(crate) fn emit_throw_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - let Statement::ThrowStatement(ts) = stmt else { - return Ok(None); - }; - let exc_reg = self.emit_expression(&ts.argument, ctx)?; - ctx.emit(opcode::encode(OpCode::THROW, exc_reg, 0, 0)); - Ok(None) + pub(crate) fn count_try_statement(&self, stmt: &oxide_parser::TryStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let catch_label = Label::CatchBody(id); + let try_end_label = Label::TryEnd(id); + let has_catch = stmt.handler.is_some(); + let has_finally = stmt.finalizer.is_some(); + ctx.alloc_reg(); + if has_finally { + ctx.projected_pc += 1; + } + if has_catch { + ctx.projected_pc += 1; + } + for s in &stmt.block.body { + self.count_statement(s, ctx); + } + ctx.projected_pc += 1; + if has_catch { + ctx.projected_pc += 1; + } + let jmp_needed = has_catch || has_finally; + if jmp_needed { + ctx.projected_pc += 1; + } + ctx.labels.label_map.insert(catch_label, ctx.projected_pc); + if let Some(catch) = &stmt.handler { + ctx.push_scope(); + if let Some(param) = &catch.param { + ctx.alloc_reg(); + if let oxide_parser::BindingPattern::BindingIdentifier(_) = ¶m.pattern { + ctx.projected_pc += 1; + } + } + for cs in &catch.body.body { + self.count_statement(cs, ctx); + } + ctx.projected_pc += 1; + ctx.pop_scope(); + } + if let Some(finally) = &stmt.finalizer { + let finally_label = Label::FinallyBody(id); + ctx.labels.label_map.insert(finally_label, ctx.projected_pc); + for fs in &finally.body { + self.count_statement(fs, ctx); + } + ctx.projected_pc += 1; + ctx.projected_pc += 1; + } + ctx.labels.label_map.insert(try_end_label, ctx.projected_pc); } pub(crate) fn emit_try_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { @@ -104,12 +148,4 @@ impl Compiler { ctx.labels.label_map.insert(try_end_label, try_end_pc); Ok(Some(result_reg)) } - - pub(crate) fn emit_exception_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - match stmt { - Statement::ThrowStatement(_) => self.emit_throw_statement(stmt, ctx), - Statement::TryStatement(_) => self.emit_try_statement(stmt, ctx), - _ => Ok(None), - } - } } diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/count.rs b/crates/oxide_compiler/src/passes/stmt/iteration/count.rs deleted file mode 100644 index 689c056..0000000 --- a/crates/oxide_compiler/src/passes/stmt/iteration/count.rs +++ /dev/null @@ -1,138 +0,0 @@ -use super::super::*; - -impl Compiler { - fn count_while_statement(&self, stmt: &oxide_parser::WhileStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let start_label = Label::WhileStart(id); - let end_label = Label::WhileEnd(id); - ctx.labels.label_map.insert(start_label, ctx.projected_pc); - self.count_expression(&stmt.test, ctx); - ctx.projected_pc += 1; - self.count_statement(&stmt.body, ctx); - ctx.projected_pc += 1; - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - } - - fn count_do_while_statement(&self, stmt: &oxide_parser::DoWhileStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let start_label = Label::DoWhileStart(id); - let end_label = Label::DoWhileEnd(id); - ctx.labels.label_map.insert(start_label, ctx.projected_pc); - self.count_statement(&stmt.body, ctx); - self.count_expression(&stmt.test, ctx); - ctx.projected_pc += 1; - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - } - - fn count_for_statement(&self, stmt: &oxide_parser::ForStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let start_label = Label::ForStart(id); - let update_label = Label::ForUpdate(id); - let end_label = Label::ForEnd(id); - if let Some(init) = &stmt.init { - if let Some(expr) = init.as_expression() { - self.count_expression(expr, ctx); - } else if let ForStatementInit::VariableDeclaration(decl) = init { - for d in &decl.declarations { - if let Some(init_expr) = &d.init { - self.count_expression(init_expr, ctx); - self.count_binding_pattern(&d.id, ctx); - } else { - ctx.alloc_reg(); - ctx.alloc_reg(); - ctx.count_words(2); - } - } - } - } - ctx.labels.label_map.insert(start_label, ctx.projected_pc); - if let Some(test) = &stmt.test { - self.count_expression(test, ctx); - ctx.projected_pc += 1; - } - self.count_statement(&stmt.body, ctx); - ctx.labels.label_map.insert(update_label, ctx.projected_pc); - if let Some(update) = &stmt.update { - self.count_expression(update, ctx); - } - ctx.projected_pc += 1; - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - } - - fn count_for_in_statement(&self, stmt: &oxide_parser::ForInStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let start_label = Label::ForInStart(id); - let end_label = Label::ForInEnd(id); - self.count_expression(&stmt.right, ctx); - ctx.count_instr(); - ctx.labels.label_map.insert(start_label, ctx.projected_pc); - ctx.count_instr(); - ctx.count_jump(); - ctx.count_jump(); - ctx.count_instr(); - match &stmt.left { - oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { - for _d in &decl.declarations { - ctx.alloc_reg(); - ctx.count_instr(); - } - } - oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { - ctx.alloc_reg(); - ctx.count_instr(); - } - _ => {} - } - self.count_statement(&stmt.body, ctx); - ctx.count_jump(); - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - ctx.count_instr(); - } - - fn count_for_of_statement(&self, stmt: &oxide_parser::ForOfStatement<'_>, ctx: &mut CompileCtx) { - let id = ctx.next_label_id(); - let start_label = Label::ForOfStart(id); - let end_label = Label::ForOfEnd(id); - self.count_expression(&stmt.right, ctx); - ctx.count_instr(); - ctx.labels.label_map.insert(start_label, ctx.projected_pc); - ctx.alloc_reg(); - ctx.count_instr(); - ctx.count_jump(); - ctx.alloc_reg(); - ctx.count_instr(); - match &stmt.left { - oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { - for d in &decl.declarations { - self.count_binding_pattern(&d.id, ctx); - } - } - oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { - ctx.alloc_reg(); - ctx.count_instr(); - } - oxide_parser::ForStatementLeft::ArrayAssignmentTarget(ap) => { - self.count_array_assignment(ap, ctx); - } - oxide_parser::ForStatementLeft::ObjectAssignmentTarget(op) => { - self.count_object_assignment(op, ctx); - } - _ => {} - } - self.count_statement(&stmt.body, ctx); - ctx.count_jump(); - ctx.labels.label_map.insert(end_label, ctx.projected_pc); - ctx.count_instr(); - } - - pub(crate) fn count_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { - match stmt { - Statement::WhileStatement(wh) => self.count_while_statement(wh, ctx), - Statement::DoWhileStatement(dw) => self.count_do_while_statement(dw, ctx), - Statement::ForStatement(fr) => self.count_for_statement(fr, ctx), - Statement::ForInStatement(fi) => self.count_for_in_statement(fi, ctx), - Statement::ForOfStatement(fo) => self.count_for_of_statement(fo, ctx), - _ => {} - } - } -} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/do_while.rs b/crates/oxide_compiler/src/passes/stmt/iteration/do_while.rs new file mode 100644 index 0000000..6390477 --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/iteration/do_while.rs @@ -0,0 +1,36 @@ +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::opcode; +use oxide_parser::Statement; + +impl Compiler { + pub(crate) fn count_do_while_statement(&self, stmt: &oxide_parser::DoWhileStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let start_label = Label::DoWhileStart(id); + let end_label = Label::DoWhileEnd(id); + ctx.labels.label_map.insert(start_label, ctx.projected_pc); + self.count_statement(&stmt.body, ctx); + self.count_expression(&stmt.test, ctx); + ctx.projected_pc += 1; + ctx.labels.label_map.insert(end_label, ctx.projected_pc); + } + + pub(crate) fn emit_do_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::DoWhileStatement(dw) = stmt else { + return Ok(None); + }; + let id = ctx.next_label_id(); + let start_label = Label::DoWhileStart(id); + let end_label = Label::DoWhileEnd(id); + ctx.push_loop(end_label, start_label); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + self.emit_statement(&dw.body, ctx)?; + let test_reg = self.emit_expression(&dw.test, ctx)?; + let start_pos = ctx.resolve_label(start_label)?; + let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp_if_true(test_reg, offset)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/emit.rs b/crates/oxide_compiler/src/passes/stmt/iteration/emit.rs deleted file mode 100644 index e7bc74e..0000000 --- a/crates/oxide_compiler/src/passes/stmt/iteration/emit.rs +++ /dev/null @@ -1,209 +0,0 @@ -use super::super::*; - -impl Compiler { - pub(crate) fn emit_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - match stmt { - Statement::WhileStatement(_) => self.emit_while_statement(stmt, ctx), - Statement::DoWhileStatement(_) => self.emit_do_while_statement(stmt, ctx), - Statement::ForStatement(_) => self.emit_for_statement(stmt, ctx), - Statement::ForInStatement(_) => self.emit_for_in_statement(stmt, ctx), - Statement::ForOfStatement(_) => self.emit_for_of_statement(stmt, ctx), - _ => Ok(None), - } - } - - pub(crate) fn emit_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - let Statement::WhileStatement(wh) = stmt else { - return Ok(None); - }; - let id = ctx.next_label_id(); - let start_label = Label::WhileStart(id); - let end_label = Label::WhileEnd(id); - ctx.push_loop(end_label, start_label); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); - let test_reg = self.emit_expression(&wh.test, ctx)?; - let end_pos = ctx.resolve_label(end_label)?; - let offset = (end_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp_if_false(test_reg, offset)); - self.emit_statement(&wh.body, ctx)?; - let start_pos = ctx.resolve_label(start_label)?; - let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp(offset)); - ctx.pop_label_scopes(n_labeled); - ctx.pop_loop(); - Ok(None) - } - - pub(crate) fn emit_do_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - let Statement::DoWhileStatement(dw) = stmt else { - return Ok(None); - }; - let id = ctx.next_label_id(); - let start_label = Label::DoWhileStart(id); - let end_label = Label::DoWhileEnd(id); - ctx.push_loop(end_label, start_label); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); - self.emit_statement(&dw.body, ctx)?; - let test_reg = self.emit_expression(&dw.test, ctx)?; - let start_pos = ctx.resolve_label(start_label)?; - let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp_if_true(test_reg, offset)); - ctx.pop_label_scopes(n_labeled); - ctx.pop_loop(); - Ok(None) - } - - pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - let Statement::ForStatement(fr) = stmt else { - return Ok(None); - }; - let id = ctx.next_label_id(); - let start_label = Label::ForStart(id); - let update_label = Label::ForUpdate(id); - let end_label = Label::ForEnd(id); - ctx.push_loop(end_label, update_label); - let n_labeled = ctx.take_pending_loop_labels(end_label, update_label); - if let Some(init) = &fr.init { - if let Some(expr) = init.as_expression() { - self.emit_expression(expr, ctx)?; - } else if let ForStatementInit::VariableDeclaration(decl) = init { - for d in &decl.declarations { - let is_const = matches!(decl.kind, VariableDeclarationKind::Const); - if let Some(init_expr) = &d.init { - let val_reg = self.emit_expression(init_expr, ctx)?; - self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?; - } else if let BindingPattern::BindingIdentifier(bi) = &d.id { - let idx = ctx.add_constant(Constant::Undefined); - let tmp = ctx.alloc_reg(); - ctx.emit_load_const(tmp, idx); - let var_reg = ctx.alloc_reg(); - ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?; - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0)); - ctx.init_var(bi.name.as_str()); - } - } - } - } - if let Some(test) = &fr.test { - let test_reg = self.emit_expression(test, ctx)?; - let end_pos = ctx.resolve_label(end_label)?; - let offset = (end_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp_if_false(test_reg, offset)); - } - self.emit_statement(&fr.body, ctx)?; - if let Some(update) = &fr.update { - self.emit_expression(update, ctx)?; - } - let start_pos = ctx.resolve_label(start_label)?; - let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp(offset)); - ctx.pop_label_scopes(n_labeled); - ctx.pop_loop(); - Ok(None) - } - - pub(crate) fn emit_for_in_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - let Statement::ForInStatement(fi) = stmt else { - return Ok(None); - }; - let id = ctx.next_label_id(); - let start_label = Label::ForInStart(id); - let end_label = Label::ForInEnd(id); - let obj_reg = self.emit_expression(&fi.right, ctx)?; - ctx.emit(opcode::encode(OpCode::FOR_IN_INIT, 0, obj_reg, 0)); - ctx.push_loop(end_label, start_label); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); - let done_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::FOR_IN_DONE, done_reg, 0, 0)); - let end_pos = ctx.resolve_label(end_label)?; - let cleanup_jmp_offset = (end_pos as isize) - (ctx.bytecode.len() as isize); - ctx.emit(opcode::encode_jmp_if_false(done_reg, 2)); - let cleanup_jmp_offset = ctx.checked_jump_offset(cleanup_jmp_offset); - ctx.emit(opcode::encode_jmp(cleanup_jmp_offset)); - let key_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::FOR_IN_NEXT, key_reg, 0, 0)); - match &fi.left { - ForStatementLeft::VariableDeclaration(decl) => { - for d in &decl.declarations { - let name = match &d.id { - oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(), - _ => return Err("destructuring not supported".into()), - }; - let var_reg = ctx.alloc_reg(); - ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?; - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0)); - ctx.init_var(name); - } - } - ForStatementLeft::AssignmentTargetIdentifier(id_ref) => { - let name = id_ref.name.as_str(); - let var_reg = ctx.lookup_or_global(name); - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0)); - } - _ => return Err("unsupported for-in left-hand side".into()), - } - self.emit_statement(&fi.body, ctx)?; - let start_pos = ctx.resolve_label(start_label)?; - let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp(offset)); - ctx.emit(opcode::encode(OpCode::FOR_IN_CLEANUP, 0, 0, 0)); - ctx.pop_label_scopes(n_labeled); - ctx.pop_loop(); - Ok(None) - } - - pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - let Statement::ForOfStatement(fo) = stmt else { - return Ok(None); - }; - let id = ctx.next_label_id(); - let start_label = Label::ForOfStart(id); - let end_label = Label::ForOfEnd(id); - let iter_src_reg = self.emit_expression(&fo.right, ctx)?; - ctx.emit(opcode::encode(OpCode::FOR_OF_INIT, 0, iter_src_reg, 0)); - ctx.push_loop(end_label, start_label); - let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); - let has_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::FOR_OF_DONE, has_reg, 0, 0)); - let end_pos = ctx.resolve_label(end_label)?; - let offset = (end_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp_if_false(has_reg, offset)); - let val_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::FOR_OF_NEXT, val_reg, 0, 0)); - match &fo.left { - ForStatementLeft::VariableDeclaration(decl) => { - for d in &decl.declarations { - self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, ctx)?; - } - } - ForStatementLeft::AssignmentTargetIdentifier(id_ref) => { - let name = id_ref.name.as_str(); - let var_reg = ctx.lookup_or_global(name); - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0)); - } - ForStatementLeft::ArrayAssignmentTarget(ap) => { - self.emit_array_assignment(ap, val_reg, ctx)?; - } - ForStatementLeft::ObjectAssignmentTarget(op) => { - self.emit_object_assignment(op, val_reg, ctx)?; - } - _ => return Err("unsupported for-of left-hand side".into()), - } - self.emit_statement(&fo.body, ctx)?; - let start_pos = ctx.resolve_label(start_label)?; - let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); - let offset = ctx.checked_jump_offset(offset); - ctx.emit(opcode::encode_jmp(offset)); - ctx.emit(opcode::encode(OpCode::FOR_OF_CLOSE, 0, 0, 0)); - ctx.pop_label_scopes(n_labeled); - ctx.pop_loop(); - Ok(None) - } -} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/for_.rs b/crates/oxide_compiler/src/passes/stmt/iteration/for_.rs new file mode 100644 index 0000000..e3e27c0 --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/iteration/for_.rs @@ -0,0 +1,94 @@ +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::{ + module::Constant, + opcode::{self, OpCode}, +}; +use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind}; + +impl Compiler { + pub(crate) fn count_for_statement(&self, stmt: &oxide_parser::ForStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let start_label = Label::ForStart(id); + let update_label = Label::ForUpdate(id); + let end_label = Label::ForEnd(id); + if let Some(init) = &stmt.init { + if let Some(expr) = init.as_expression() { + self.count_expression(expr, ctx); + } else if let ForStatementInit::VariableDeclaration(decl) = init { + for d in &decl.declarations { + if let Some(init_expr) = &d.init { + self.count_expression(init_expr, ctx); + self.count_binding_pattern(&d.id, ctx); + } else { + ctx.alloc_reg(); + ctx.alloc_reg(); + ctx.count_words(2); + } + } + } + } + ctx.labels.label_map.insert(start_label, ctx.projected_pc); + if let Some(test) = &stmt.test { + self.count_expression(test, ctx); + ctx.projected_pc += 1; + } + self.count_statement(&stmt.body, ctx); + ctx.labels.label_map.insert(update_label, ctx.projected_pc); + if let Some(update) = &stmt.update { + self.count_expression(update, ctx); + } + ctx.projected_pc += 1; + ctx.labels.label_map.insert(end_label, ctx.projected_pc); + } + + pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::ForStatement(fr) = stmt else { + return Ok(None); + }; + let id = ctx.next_label_id(); + let start_label = Label::ForStart(id); + let update_label = Label::ForUpdate(id); + let end_label = Label::ForEnd(id); + ctx.push_loop(end_label, update_label); + let n_labeled = ctx.take_pending_loop_labels(end_label, update_label); + if let Some(init) = &fr.init { + if let Some(expr) = init.as_expression() { + self.emit_expression(expr, ctx)?; + } else if let ForStatementInit::VariableDeclaration(decl) = init { + for d in &decl.declarations { + let is_const = matches!(decl.kind, VariableDeclarationKind::Const); + if let Some(init_expr) = &d.init { + let val_reg = self.emit_expression(init_expr, ctx)?; + self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?; + } else if let BindingPattern::BindingIdentifier(bi) = &d.id { + let idx = ctx.add_constant(Constant::Undefined); + let tmp = ctx.alloc_reg(); + ctx.emit_load_const(tmp, idx); + let var_reg = ctx.alloc_reg(); + ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?; + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0)); + ctx.init_var(bi.name.as_str()); + } + } + } + } + if let Some(test) = &fr.test { + let test_reg = self.emit_expression(test, ctx)?; + let end_pos = ctx.resolve_label(end_label)?; + let offset = (end_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp_if_false(test_reg, offset)); + } + self.emit_statement(&fr.body, ctx)?; + if let Some(update) = &fr.update { + self.emit_expression(update, ctx)?; + } + let start_pos = ctx.resolve_label(start_label)?; + let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp(offset)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/for_in.rs b/crates/oxide_compiler/src/passes/stmt/iteration/for_in.rs new file mode 100644 index 0000000..241d94b --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/iteration/for_in.rs @@ -0,0 +1,86 @@ +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind}; + +impl Compiler { + pub(crate) fn count_for_in_statement(&self, stmt: &oxide_parser::ForInStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let start_label = Label::ForInStart(id); + let end_label = Label::ForInEnd(id); + self.count_expression(&stmt.right, ctx); + ctx.count_instr(); + ctx.labels.label_map.insert(start_label, ctx.projected_pc); + ctx.count_instr(); + ctx.count_jump(); + ctx.count_jump(); + ctx.count_instr(); + match &stmt.left { + oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { + for _d in &decl.declarations { + ctx.alloc_reg(); + ctx.count_instr(); + } + } + oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { + ctx.alloc_reg(); + ctx.count_instr(); + } + _ => {} + } + self.count_statement(&stmt.body, ctx); + ctx.count_jump(); + ctx.labels.label_map.insert(end_label, ctx.projected_pc); + ctx.count_instr(); + } + + pub(crate) fn emit_for_in_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::ForInStatement(fi) = stmt else { + return Ok(None); + }; + let id = ctx.next_label_id(); + let start_label = Label::ForInStart(id); + let end_label = Label::ForInEnd(id); + let obj_reg = self.emit_expression(&fi.right, ctx)?; + ctx.emit(opcode::encode(OpCode::FOR_IN_INIT, 0, obj_reg, 0)); + ctx.push_loop(end_label, start_label); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let done_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::FOR_IN_DONE, done_reg, 0, 0)); + let end_pos = ctx.resolve_label(end_label)?; + let cleanup_jmp_offset = (end_pos as isize) - (ctx.bytecode.len() as isize); + ctx.emit(opcode::encode_jmp_if_false(done_reg, 2)); + let cleanup_jmp_offset = ctx.checked_jump_offset(cleanup_jmp_offset); + ctx.emit(opcode::encode_jmp(cleanup_jmp_offset)); + let key_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::FOR_IN_NEXT, key_reg, 0, 0)); + match &fi.left { + ForStatementLeft::VariableDeclaration(decl) => { + for d in &decl.declarations { + let name = match &d.id { + oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(), + _ => return Err("destructuring not supported".into()), + }; + let var_reg = ctx.alloc_reg(); + ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?; + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0)); + ctx.init_var(name); + } + } + ForStatementLeft::AssignmentTargetIdentifier(id_ref) => { + let name = id_ref.name.as_str(); + let var_reg = ctx.lookup_or_global(name); + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0)); + } + _ => return Err("unsupported for-in left-hand side".into()), + } + self.emit_statement(&fi.body, ctx)?; + let start_pos = ctx.resolve_label(start_label)?; + let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp(offset)); + ctx.emit(opcode::encode(OpCode::FOR_IN_CLEANUP, 0, 0, 0)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/for_of.rs b/crates/oxide_compiler/src/passes/stmt/iteration/for_of.rs new file mode 100644 index 0000000..353d9d1 --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/iteration/for_of.rs @@ -0,0 +1,90 @@ +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::{ForStatementLeft, Statement}; + +impl Compiler { + pub(crate) fn count_for_of_statement(&self, stmt: &oxide_parser::ForOfStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let start_label = Label::ForOfStart(id); + let end_label = Label::ForOfEnd(id); + self.count_expression(&stmt.right, ctx); + ctx.count_instr(); + ctx.labels.label_map.insert(start_label, ctx.projected_pc); + ctx.alloc_reg(); + ctx.count_instr(); + ctx.count_jump(); + ctx.alloc_reg(); + ctx.count_instr(); + match &stmt.left { + oxide_parser::ForStatementLeft::VariableDeclaration(decl) => { + for d in &decl.declarations { + self.count_binding_pattern(&d.id, ctx); + } + } + oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(_) => { + ctx.alloc_reg(); + ctx.count_instr(); + } + oxide_parser::ForStatementLeft::ArrayAssignmentTarget(ap) => { + self.count_array_assignment(ap, ctx); + } + oxide_parser::ForStatementLeft::ObjectAssignmentTarget(op) => { + self.count_object_assignment(op, ctx); + } + _ => {} + } + self.count_statement(&stmt.body, ctx); + ctx.count_jump(); + ctx.labels.label_map.insert(end_label, ctx.projected_pc); + ctx.count_instr(); + } + + pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::ForOfStatement(fo) = stmt else { + return Ok(None); + }; + let id = ctx.next_label_id(); + let start_label = Label::ForOfStart(id); + let end_label = Label::ForOfEnd(id); + let iter_src_reg = self.emit_expression(&fo.right, ctx)?; + ctx.emit(opcode::encode(OpCode::FOR_OF_INIT, 0, iter_src_reg, 0)); + ctx.push_loop(end_label, start_label); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let has_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::FOR_OF_DONE, has_reg, 0, 0)); + let end_pos = ctx.resolve_label(end_label)?; + let offset = (end_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp_if_false(has_reg, offset)); + let val_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::FOR_OF_NEXT, val_reg, 0, 0)); + match &fo.left { + ForStatementLeft::VariableDeclaration(decl) => { + for d in &decl.declarations { + self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, ctx)?; + } + } + ForStatementLeft::AssignmentTargetIdentifier(id_ref) => { + let name = id_ref.name.as_str(); + let var_reg = ctx.lookup_or_global(name); + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0)); + } + ForStatementLeft::ArrayAssignmentTarget(ap) => { + self.emit_array_assignment(ap, val_reg, ctx)?; + } + ForStatementLeft::ObjectAssignmentTarget(op) => { + self.emit_object_assignment(op, val_reg, ctx)?; + } + _ => return Err("unsupported for-of left-hand side".into()), + } + self.emit_statement(&fo.body, ctx)?; + let start_pos = ctx.resolve_label(start_label)?; + let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp(offset)); + ctx.emit(opcode::encode(OpCode::FOR_OF_CLOSE, 0, 0, 0)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/mod.rs b/crates/oxide_compiler/src/passes/stmt/iteration/mod.rs index e1311e8..81cd0b1 100644 --- a/crates/oxide_compiler/src/passes/stmt/iteration/mod.rs +++ b/crates/oxide_compiler/src/passes/stmt/iteration/mod.rs @@ -1,4 +1,32 @@ -pub use super::super::*; +use crate::compiler::{CompileCtx, Compiler}; +use oxide_parser::Statement; -pub mod count; -pub mod emit; +mod do_while; +mod for_; +mod for_in; +mod for_of; +mod while_loop; + +impl Compiler { + pub(crate) fn count_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { + match stmt { + Statement::WhileStatement(wh) => self.count_while_statement(wh, ctx), + Statement::DoWhileStatement(dw) => self.count_do_while_statement(dw, ctx), + Statement::ForStatement(fr) => self.count_for_statement(fr, ctx), + Statement::ForInStatement(fi) => self.count_for_in_statement(fi, ctx), + Statement::ForOfStatement(fo) => self.count_for_of_statement(fo, ctx), + _ => {} + } + } + + pub(crate) fn emit_iteration_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + match stmt { + Statement::WhileStatement(_) => self.emit_while_statement(stmt, ctx), + Statement::DoWhileStatement(_) => self.emit_do_while_statement(stmt, ctx), + Statement::ForStatement(_) => self.emit_for_statement(stmt, ctx), + Statement::ForInStatement(_) => self.emit_for_in_statement(stmt, ctx), + Statement::ForOfStatement(_) => self.emit_for_of_statement(stmt, ctx), + _ => Ok(None), + } + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/iteration/while_loop.rs b/crates/oxide_compiler/src/passes/stmt/iteration/while_loop.rs new file mode 100644 index 0000000..68ba0e5 --- /dev/null +++ b/crates/oxide_compiler/src/passes/stmt/iteration/while_loop.rs @@ -0,0 +1,41 @@ +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::opcode; +use oxide_parser::Statement; + +impl Compiler { + pub(crate) fn count_while_statement(&self, stmt: &oxide_parser::WhileStatement<'_>, ctx: &mut CompileCtx) { + let id = ctx.next_label_id(); + let start_label = Label::WhileStart(id); + let end_label = Label::WhileEnd(id); + ctx.labels.label_map.insert(start_label, ctx.projected_pc); + self.count_expression(&stmt.test, ctx); + ctx.projected_pc += 1; + self.count_statement(&stmt.body, ctx); + ctx.projected_pc += 1; + ctx.labels.label_map.insert(end_label, ctx.projected_pc); + } + + pub(crate) fn emit_while_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { + let Statement::WhileStatement(wh) = stmt else { + return Ok(None); + }; + let id = ctx.next_label_id(); + let start_label = Label::WhileStart(id); + let end_label = Label::WhileEnd(id); + ctx.push_loop(end_label, start_label); + let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); + let test_reg = self.emit_expression(&wh.test, ctx)?; + let end_pos = ctx.resolve_label(end_label)?; + let offset = (end_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp_if_false(test_reg, offset)); + self.emit_statement(&wh.body, ctx)?; + let start_pos = ctx.resolve_label(start_label)?; + let offset = (start_pos as isize) - (ctx.bytecode.len() as isize); + let offset = ctx.checked_jump_offset(offset); + ctx.emit(opcode::encode_jmp(offset)); + ctx.pop_label_scopes(n_labeled); + ctx.pop_loop(); + Ok(None) + } +} diff --git a/crates/oxide_compiler/src/passes/stmt/mod.rs b/crates/oxide_compiler/src/passes/stmt/mod.rs index 465d305..d5e36fc 100644 --- a/crates/oxide_compiler/src/passes/stmt/mod.rs +++ b/crates/oxide_compiler/src/passes/stmt/mod.rs @@ -1,5 +1,3 @@ -pub use super::*; - pub mod basic; pub mod block; pub mod control; diff --git a/crates/oxide_compiler/src/passes/stmt/switch.rs b/crates/oxide_compiler/src/passes/stmt/switch.rs index 40dd218..3e32ed2 100644 --- a/crates/oxide_compiler/src/passes/stmt/switch.rs +++ b/crates/oxide_compiler/src/passes/stmt/switch.rs @@ -1,4 +1,6 @@ -use super::*; +use crate::compiler::{CompileCtx, Compiler, Label}; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_parser::Statement; impl Compiler { fn count_switch_statement(&self, stmt: &oxide_parser::SwitchStatement<'_>, ctx: &mut CompileCtx) {