From 680b1ae264a62b6185736e311062fd0c33715406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Sat, 6 Jun 2026 10:14:33 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=B0=86=E7=BC=96=E8=AF=91=20pass?= =?UTF-8?q?=20=E6=94=BE=E5=85=A5=E7=8B=AC=E7=AB=8B=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_compiler/src/compiler.rs | 486 +------------------------- crates/oxide_compiler/src/counter.rs | 143 ++++++++ crates/oxide_compiler/src/emitter.rs | 317 +++++++++++++++++ crates/oxide_compiler/src/lib.rs | 2 + 4 files changed, 478 insertions(+), 470 deletions(-) create mode 100644 crates/oxide_compiler/src/counter.rs create mode 100644 crates/oxide_compiler/src/emitter.rs diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 041428b..88c3a9e 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -8,21 +8,20 @@ pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement pub struct Compiler; -fn is_int_literal(value: f64) -> bool { +pub(crate) fn is_int_literal(value: f64) -> bool { value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 } -struct CompileCtx { - bytecode: Vec, - constants: Vec, +pub(crate) struct CompileCtx { + pub(crate) bytecode: Vec, + pub(crate) constants: Vec, next_reg: u8, - max_regs: u8, - #[allow(dead_code)] + pub(crate) max_regs: u8, symbols: SymbolTable, } impl CompileCtx { - fn new() -> Self { + pub(crate) fn new() -> Self { Self { bytecode: Vec::new(), constants: Vec::new(), @@ -32,11 +31,11 @@ impl CompileCtx { } } - fn emit(&mut self, instr: opcode::Instr) { + pub(crate) fn emit(&mut self, instr: opcode::Instr) { self.bytecode.push(instr); } - fn alloc_reg(&mut self) -> u8 { + pub(crate) fn alloc_reg(&mut self) -> u8 { let r = self.next_reg; self.next_reg += 1; if self.next_reg > self.max_regs { @@ -45,11 +44,11 @@ impl CompileCtx { r } - fn reset_regs(&mut self) { + pub(crate) fn reset_regs(&mut self) { self.next_reg = 0; } - fn add_constant(&mut self, c: Constant) -> u16 { + pub(crate) fn add_constant(&mut self, c: Constant) -> u16 { if let Some(idx) = self.constants.iter().position(|x| x == &c) { return idx as u16; } @@ -58,33 +57,28 @@ impl CompileCtx { idx as u16 } - #[allow(dead_code)] - fn push_scope(&mut self) { + pub(crate) fn push_scope(&mut self) { self.symbols.push_scope(); } - #[allow(dead_code)] - fn pop_scope(&mut self) { + pub(crate) fn pop_scope(&mut self) { self.symbols.pop_scope(); } - #[allow(dead_code)] - fn declare(&mut self, name: &str, reg: u8) -> Result<(), String> { + pub(crate) fn declare(&mut self, name: &str, reg: u8) -> Result<(), String> { self.symbols.declare(name, reg) } - #[allow(dead_code)] - fn lookup(&self, name: &str) -> Result { + pub(crate) fn lookup(&self, name: &str) -> Result { self.symbols.lookup(name) } - fn lookup_or_global(&mut self, name: &str) -> u8 { + pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 { let reg = self.alloc_reg(); self.symbols.lookup_or_global(name, reg) } - #[allow(dead_code)] - fn init_var(&mut self, name: &str) { + pub(crate) fn init_var(&mut self, name: &str) { self.symbols.init_var(name); } } @@ -128,451 +122,3 @@ impl Default for Compiler { Self::new() } } - -// ── Pass 1: Counter ── - -impl Compiler { - fn count_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) { - match stmt { - Statement::ExpressionStatement(es) => { - self.count_expression(&es.expression, ctx); - } - Statement::VariableDeclaration(decl) => { - for d in &decl.declarations { - ctx.alloc_reg(); - if let Some(init) = &d.init { - self.count_expression(init, ctx); - } - } - } - Statement::ReturnStatement(ret) => { - if let Some(arg) = &ret.argument { - self.count_expression(arg, ctx); - } - } - Statement::IfStatement(ifs) => { - self.count_expression(&ifs.test, ctx); - self.count_statement(&ifs.consequent, ctx); - if let Some(alt) = &ifs.alternate { - self.count_statement(alt, ctx); - } - } - Statement::WhileStatement(wh) => { - self.count_expression(&wh.test, ctx); - self.count_statement(&wh.body, ctx); - } - Statement::ForStatement(fr) => { - if let Some(init) = &fr.init { - if let Some(expr) = init.as_expression() { - self.count_expression(expr, ctx); - } - } - if let Some(test) = &fr.test { - self.count_expression(test, ctx); - } - if let Some(update) = &fr.update { - self.count_expression(update, ctx); - } - self.count_statement(&fr.body, ctx); - } - Statement::BlockStatement(block) => { - for s in &block.body { - self.count_statement(s, ctx); - } - } - _ => {} - } - } - - fn count_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { - match expr { - Expression::BinaryExpression(bin) => { - self.count_expression(&bin.left, ctx); - self.count_expression(&bin.right, ctx); - ctx.alloc_reg(); - } - Expression::UnaryExpression(un) => { - self.count_expression(&un.argument, ctx); - ctx.alloc_reg(); - } - Expression::CallExpression(call) => { - self.count_expression(&call.callee, ctx); - ctx.alloc_reg(); - } - Expression::AssignmentExpression(assign) => { - if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left - { - self.count_expression(&member.object, ctx); - self.count_expression(&assign.right, ctx); - ctx.alloc_reg(); - } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = - &assign.left - { - self.count_expression(&member.object, ctx); - self.count_expression(&member.expression, ctx); - self.count_expression(&assign.right, ctx); - ctx.alloc_reg(); - } else { - self.count_expression(&assign.right, ctx); - ctx.alloc_reg(); - } - } - Expression::ConditionalExpression(cond) => { - self.count_expression(&cond.test, ctx); - self.count_expression(&cond.consequent, ctx); - self.count_expression(&cond.alternate, ctx); - ctx.alloc_reg(); - } - Expression::SequenceExpression(seq) => { - for e in &seq.expressions { - self.count_expression(e, ctx); - } - } - Expression::LogicalExpression(log) => { - self.count_expression(&log.left, ctx); - self.count_expression(&log.right, ctx); - ctx.alloc_reg(); - } - Expression::ObjectExpression(obj) => { - ctx.alloc_reg(); - for prop in &obj.properties { - if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { - ctx.alloc_reg(); - self.count_expression(&p.value, ctx); - } - } - } - Expression::ArrayExpression(arr) => { - ctx.alloc_reg(); - for elem in &arr.elements { - if let Some(e) = elem.as_expression() { - self.count_expression(e, ctx); - ctx.alloc_reg(); - } - } - } - Expression::StaticMemberExpression(member) => { - self.count_expression(&member.object, ctx); - ctx.alloc_reg(); - ctx.alloc_reg(); - } - Expression::ComputedMemberExpression(member) => { - self.count_expression(&member.object, ctx); - self.count_expression(&member.expression, ctx); - ctx.alloc_reg(); - } - Expression::ParenthesizedExpression(p) => { - self.count_expression(&p.expression, ctx); - } - _ => { - ctx.alloc_reg(); - } - } - } -} - -// ── Pass 2: Emitter ── - -impl Compiler { - fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { - match stmt { - Statement::ExpressionStatement(es) => { - Ok(Some(self.emit_expression(&es.expression, ctx)?)) - } - Statement::VariableDeclaration(decl) => { - let mut r = None; - 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)?; - if let Some(init) = &d.init { - let val_reg = self.emit_expression(init, ctx)?; - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0)); - ctx.init_var(name); - r = Some(val_reg); - } else { - let idx = ctx.add_constant(Constant::Undefined); - let tmp = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - tmp, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0)); - ctx.init_var(name); - r = Some(var_reg); - } - } - Ok(r) - } - Statement::ReturnStatement(ret) => { - match &ret.argument { - Some(expr) => { - let r = self.emit_expression(expr, ctx)?; - ctx.emit(opcode::encode(OpCode::RETURN, r, 0, 0)); - } - None => { - ctx.emit(opcode::encode(OpCode::RETURN, 0, 0, 0)); - } - } - Ok(None) - } - Statement::BlockStatement(block) => { - ctx.push_scope(); - let mut r = None; - for s in &block.body { - if let Some(rr) = self.emit_statement(s, ctx)? { - r = Some(rr); - } - } - ctx.pop_scope(); - Ok(r) - } - _ => Ok(None), - } - } - - fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { - match expr { - Expression::NumericLiteral(n) => { - let idx = if is_int_literal(n.value) { - ctx.add_constant(Constant::Int(n.value as i32)) - } else { - ctx.add_constant(Constant::Number(n.value)) - }; - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - r, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - Ok(r) - } - Expression::StringLiteral(s) => { - let idx = ctx.add_constant(Constant::String(s.value.to_string())); - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - r, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - Ok(r) - } - Expression::BooleanLiteral(b) => { - let idx = ctx.add_constant(Constant::Boolean(b.value)); - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - r, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - Ok(r) - } - Expression::NullLiteral(_) => { - let idx = ctx.add_constant(Constant::Null); - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - r, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - Ok(r) - } - Expression::BinaryExpression(bin) => { - let left = self.emit_expression(&bin.left, ctx)?; - let right = self.emit_expression(&bin.right, ctx)?; - let op = match bin.operator { - BinaryOperator::Addition => OpCode::ADD, - BinaryOperator::Subtraction => OpCode::SUB, - BinaryOperator::Multiplication => OpCode::MUL, - BinaryOperator::Division => OpCode::DIV, - BinaryOperator::Remainder => OpCode::MOD, - BinaryOperator::Equality => OpCode::EQ, - BinaryOperator::Inequality => OpCode::NEQ, - BinaryOperator::LessThan => OpCode::LT, - BinaryOperator::GreaterThan => OpCode::GT, - BinaryOperator::LessEqualThan => OpCode::LTE, - BinaryOperator::GreaterEqualThan => OpCode::GTE, - BinaryOperator::In => OpCode::IN, - _ => return Err(format!("unsupported binary operator: {:?}", bin.operator)), - }; - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(op, r, left, right)); - Ok(r) - } - Expression::UnaryExpression(un) => { - let arg = self.emit_expression(&un.argument, ctx)?; - match un.operator { - UnaryOperator::UnaryNegation => { - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::NEG, r, arg, 0)); - Ok(r) - } - UnaryOperator::Typeof => { - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::TYPEOF, r, arg, 0)); - Ok(r) - } - UnaryOperator::Void => { - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::VOID, r, arg, 0)); - Ok(r) - } - _ => Err(format!("unsupported unary operator: {:?}", un.operator)), - } - } - Expression::StaticMemberExpression(member) => { - let obj_reg = self.emit_expression(&member.object, ctx)?; - let prop_name = member.property.name.as_str(); - let idx = ctx.add_constant(Constant::String(prop_name.to_string())); - let key_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - key_reg, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - let result_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::IC_GET_PROP, - result_reg, - obj_reg, - key_reg, - )); - ctx.emit(0); - Ok(result_reg) - } - Expression::ComputedMemberExpression(member) => { - let obj_reg = self.emit_expression(&member.object, ctx)?; - let key_reg = self.emit_expression(&member.expression, ctx)?; - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::GET_PROP_DYNAMIC, - obj_reg, - key_reg, - r, - )); - Ok(r) - } - Expression::ObjectExpression(obj) => { - let obj_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::NEW_OBJECT, obj_reg, 0, 0)); - for prop in &obj.properties { - let oxide_parser::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(), - _ => return Err("unsupported object property key type".into()), - }; - let idx = ctx.add_constant(Constant::String(prop_name.to_string())); - let key_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - key_reg, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - let val_reg = self.emit_expression(&p.value, ctx)?; - ctx.emit(opcode::encode(OpCode::SET_PROP, obj_reg, val_reg, key_reg)); - } - Ok(obj_reg) - } - Expression::ArrayExpression(arr) => { - 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, - )); - 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::Number(i as f64)); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - idx_reg, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - ctx.emit(opcode::encode(OpCode::SET_ELEM, arr_reg, idx_reg, val_reg)); - } - Ok(arr_reg) - } - Expression::AssignmentExpression(assign) => { - if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left - { - let obj_reg = self.emit_expression(&member.object, ctx)?; - let val_reg = self.emit_expression(&assign.right, ctx)?; - let prop_name = member.property.name.as_str(); - let idx = ctx.add_constant(Constant::String(prop_name.to_string())); - let key_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode( - OpCode::LOAD_CONST, - key_reg, - (idx & 0xFF) as u8, - ((idx >> 8) & 0xFF) as u8, - )); - ctx.emit(opcode::encode( - OpCode::IC_SET_PROP, - obj_reg, - val_reg, - key_reg, - )); - ctx.emit(0); - Ok(val_reg) - } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = - &assign.left - { - let obj_reg = self.emit_expression(&member.object, ctx)?; - let key_reg = self.emit_expression(&member.expression, ctx)?; - let val_reg = self.emit_expression(&assign.right, ctx)?; - ctx.emit(opcode::encode( - OpCode::SET_PROP_DYNAMIC, - obj_reg, - key_reg, - val_reg, - )); - Ok(val_reg) - } else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = - &assign.left - { - if assign.operator != AssignmentOperator::Assign { - return Err("compound assignment not yet supported".into()); - } - let val_reg = self.emit_expression(&assign.right, ctx)?; - 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)); - Ok(val_reg) - } else { - Err("assignment target not supported".into()) - } - } - Expression::Identifier(ident) => { - let var_reg = ctx.lookup(ident.name.as_str())?; - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::LOAD_VAR, r, var_reg, 0)); - Ok(r) - } - Expression::ParenthesizedExpression(p) => self.emit_expression(&p.expression, ctx), - _ => Err(format!("unsupported expression type: {:?}", expr)), - } - } -} diff --git a/crates/oxide_compiler/src/counter.rs b/crates/oxide_compiler/src/counter.rs new file mode 100644 index 0000000..b9d9947 --- /dev/null +++ b/crates/oxide_compiler/src/counter.rs @@ -0,0 +1,143 @@ +use oxide_parser::{Expression, Statement}; + +use crate::compiler::{CompileCtx, Compiler}; + +impl Compiler { + pub(crate) fn count_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) { + match stmt { + Statement::ExpressionStatement(es) => { + self.count_expression(&es.expression, ctx); + } + Statement::VariableDeclaration(decl) => { + for d in &decl.declarations { + ctx.alloc_reg(); + if let Some(init) = &d.init { + self.count_expression(init, ctx); + } + } + } + Statement::ReturnStatement(ret) => { + if let Some(arg) = &ret.argument { + self.count_expression(arg, ctx); + } + } + Statement::IfStatement(ifs) => { + self.count_expression(&ifs.test, ctx); + self.count_statement(&ifs.consequent, ctx); + if let Some(alt) = &ifs.alternate { + self.count_statement(alt, ctx); + } + } + Statement::WhileStatement(wh) => { + self.count_expression(&wh.test, ctx); + self.count_statement(&wh.body, ctx); + } + Statement::ForStatement(fr) => { + if let Some(init) = &fr.init { + if let Some(expr) = init.as_expression() { + self.count_expression(expr, ctx); + } + } + if let Some(test) = &fr.test { + self.count_expression(test, ctx); + } + if let Some(update) = &fr.update { + self.count_expression(update, ctx); + } + self.count_statement(&fr.body, ctx); + } + Statement::BlockStatement(block) => { + for s in &block.body { + self.count_statement(s, ctx); + } + } + _ => {} + } + } + + pub(crate) fn count_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { + match expr { + Expression::BinaryExpression(bin) => { + self.count_expression(&bin.left, ctx); + self.count_expression(&bin.right, ctx); + ctx.alloc_reg(); + } + Expression::UnaryExpression(un) => { + self.count_expression(&un.argument, ctx); + ctx.alloc_reg(); + } + Expression::CallExpression(call) => { + self.count_expression(&call.callee, ctx); + ctx.alloc_reg(); + } + Expression::AssignmentExpression(assign) => { + if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left + { + self.count_expression(&member.object, ctx); + self.count_expression(&assign.right, ctx); + ctx.alloc_reg(); + } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = + &assign.left + { + self.count_expression(&member.object, ctx); + self.count_expression(&member.expression, ctx); + self.count_expression(&assign.right, ctx); + ctx.alloc_reg(); + } else { + self.count_expression(&assign.right, ctx); + ctx.alloc_reg(); + } + } + Expression::ConditionalExpression(cond) => { + self.count_expression(&cond.test, ctx); + self.count_expression(&cond.consequent, ctx); + self.count_expression(&cond.alternate, ctx); + ctx.alloc_reg(); + } + Expression::SequenceExpression(seq) => { + for e in &seq.expressions { + self.count_expression(e, ctx); + } + } + Expression::LogicalExpression(log) => { + self.count_expression(&log.left, ctx); + self.count_expression(&log.right, ctx); + ctx.alloc_reg(); + } + Expression::ObjectExpression(obj) => { + ctx.alloc_reg(); + for prop in &obj.properties { + if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { + ctx.alloc_reg(); + self.count_expression(&p.value, ctx); + } + } + } + Expression::ArrayExpression(arr) => { + ctx.alloc_reg(); + for elem in &arr.elements { + if let Some(e) = elem.as_expression() { + self.count_expression(e, ctx); + ctx.alloc_reg(); + } + } + } + Expression::StaticMemberExpression(member) => { + self.count_expression(&member.object, ctx); + ctx.alloc_reg(); + ctx.alloc_reg(); + } + Expression::ComputedMemberExpression(member) => { + self.count_expression(&member.object, ctx); + self.count_expression(&member.expression, ctx); + ctx.alloc_reg(); + } + Expression::ParenthesizedExpression(p) => { + self.count_expression(&p.expression, ctx); + } + _ => { + ctx.alloc_reg(); + } + } + } +} diff --git a/crates/oxide_compiler/src/emitter.rs b/crates/oxide_compiler/src/emitter.rs new file mode 100644 index 0000000..fd112ba --- /dev/null +++ b/crates/oxide_compiler/src/emitter.rs @@ -0,0 +1,317 @@ +use crate::opcode::{self, OpCode}; +use oxide_parser::{AssignmentOperator, Expression, Statement, UnaryOperator}; + +use crate::compiler::{is_int_literal, BinaryOperator, CompileCtx, Compiler}; +use crate::module::Constant; + +impl Compiler { + pub(crate) fn emit_statement( + &self, + stmt: &Statement, + ctx: &mut CompileCtx, + ) -> Result, String> { + match stmt { + Statement::ExpressionStatement(es) => { + Ok(Some(self.emit_expression(&es.expression, ctx)?)) + } + Statement::VariableDeclaration(decl) => { + let mut r = None; + 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)?; + if let Some(init) = &d.init { + let val_reg = self.emit_expression(init, ctx)?; + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0)); + ctx.init_var(name); + r = Some(val_reg); + } else { + let idx = ctx.add_constant(Constant::Undefined); + let tmp = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + tmp, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0)); + ctx.init_var(name); + r = Some(var_reg); + } + } + Ok(r) + } + Statement::ReturnStatement(ret) => { + match &ret.argument { + Some(expr) => { + let r = self.emit_expression(expr, ctx)?; + ctx.emit(opcode::encode(OpCode::RETURN, r, 0, 0)); + } + None => { + ctx.emit(opcode::encode(OpCode::RETURN, 0, 0, 0)); + } + } + Ok(None) + } + Statement::BlockStatement(block) => { + ctx.push_scope(); + let mut r = None; + for s in &block.body { + if let Some(rr) = self.emit_statement(s, ctx)? { + r = Some(rr); + } + } + ctx.pop_scope(); + Ok(r) + } + _ => Ok(None), + } + } + + pub(crate) fn emit_expression( + &self, + expr: &Expression, + ctx: &mut CompileCtx, + ) -> Result { + match expr { + Expression::NumericLiteral(n) => { + let idx = if is_int_literal(n.value) { + ctx.add_constant(Constant::Int(n.value as i32)) + } else { + ctx.add_constant(Constant::Number(n.value)) + }; + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + r, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + Ok(r) + } + Expression::StringLiteral(s) => { + let idx = ctx.add_constant(Constant::String(s.value.to_string())); + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + r, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + Ok(r) + } + Expression::BooleanLiteral(b) => { + let idx = ctx.add_constant(Constant::Boolean(b.value)); + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + r, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + Ok(r) + } + Expression::NullLiteral(_) => { + let idx = ctx.add_constant(Constant::Null); + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + r, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + Ok(r) + } + Expression::BinaryExpression(bin) => { + let left = self.emit_expression(&bin.left, ctx)?; + let right = self.emit_expression(&bin.right, ctx)?; + let op = match bin.operator { + BinaryOperator::Addition => OpCode::ADD, + BinaryOperator::Subtraction => OpCode::SUB, + BinaryOperator::Multiplication => OpCode::MUL, + BinaryOperator::Division => OpCode::DIV, + BinaryOperator::Remainder => OpCode::MOD, + BinaryOperator::Equality => OpCode::EQ, + BinaryOperator::Inequality => OpCode::NEQ, + BinaryOperator::LessThan => OpCode::LT, + BinaryOperator::GreaterThan => OpCode::GT, + BinaryOperator::LessEqualThan => OpCode::LTE, + BinaryOperator::GreaterEqualThan => OpCode::GTE, + BinaryOperator::In => OpCode::IN, + _ => return Err(format!("unsupported binary operator: {:?}", bin.operator)), + }; + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(op, r, left, right)); + Ok(r) + } + Expression::UnaryExpression(un) => { + let arg = self.emit_expression(&un.argument, ctx)?; + match un.operator { + UnaryOperator::UnaryNegation => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::NEG, r, arg, 0)); + Ok(r) + } + UnaryOperator::Typeof => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::TYPEOF, r, arg, 0)); + Ok(r) + } + UnaryOperator::Void => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::VOID, r, arg, 0)); + Ok(r) + } + _ => Err(format!("unsupported unary operator: {:?}", un.operator)), + } + } + Expression::StaticMemberExpression(member) => { + let obj_reg = self.emit_expression(&member.object, ctx)?; + let prop_name = member.property.name.as_str(); + let idx = ctx.add_constant(Constant::String(prop_name.to_string())); + let key_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + key_reg, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + let result_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::IC_GET_PROP, + result_reg, + obj_reg, + key_reg, + )); + ctx.emit(0); + Ok(result_reg) + } + Expression::ComputedMemberExpression(member) => { + let obj_reg = self.emit_expression(&member.object, ctx)?; + let key_reg = self.emit_expression(&member.expression, ctx)?; + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::GET_PROP_DYNAMIC, + obj_reg, + key_reg, + r, + )); + Ok(r) + } + Expression::ObjectExpression(obj) => { + let obj_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::NEW_OBJECT, obj_reg, 0, 0)); + for prop in &obj.properties { + let oxide_parser::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(), + _ => return Err("unsupported object property key type".into()), + }; + let idx = ctx.add_constant(Constant::String(prop_name.to_string())); + let key_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + key_reg, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + let val_reg = self.emit_expression(&p.value, ctx)?; + ctx.emit(opcode::encode(OpCode::SET_PROP, obj_reg, val_reg, key_reg)); + } + Ok(obj_reg) + } + Expression::ArrayExpression(arr) => { + 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, + )); + 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::Number(i as f64)); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + idx_reg, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + ctx.emit(opcode::encode(OpCode::SET_ELEM, arr_reg, idx_reg, val_reg)); + } + Ok(arr_reg) + } + Expression::AssignmentExpression(assign) => { + if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left + { + let obj_reg = self.emit_expression(&member.object, ctx)?; + let val_reg = self.emit_expression(&assign.right, ctx)?; + let prop_name = member.property.name.as_str(); + let idx = ctx.add_constant(Constant::String(prop_name.to_string())); + let key_reg = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::LOAD_CONST, + key_reg, + (idx & 0xFF) as u8, + ((idx >> 8) & 0xFF) as u8, + )); + ctx.emit(opcode::encode( + OpCode::IC_SET_PROP, + obj_reg, + val_reg, + key_reg, + )); + ctx.emit(0); + Ok(val_reg) + } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = + &assign.left + { + let obj_reg = self.emit_expression(&member.object, ctx)?; + let key_reg = self.emit_expression(&member.expression, ctx)?; + let val_reg = self.emit_expression(&assign.right, ctx)?; + ctx.emit(opcode::encode( + OpCode::SET_PROP_DYNAMIC, + obj_reg, + key_reg, + val_reg, + )); + Ok(val_reg) + } else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = + &assign.left + { + if assign.operator != AssignmentOperator::Assign { + return Err("compound assignment not yet supported".into()); + } + let val_reg = self.emit_expression(&assign.right, ctx)?; + 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)); + Ok(val_reg) + } else { + Err("assignment target not supported".into()) + } + } + Expression::Identifier(ident) => { + let var_reg = ctx.lookup(ident.name.as_str())?; + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::LOAD_VAR, r, var_reg, 0)); + Ok(r) + } + Expression::ParenthesizedExpression(p) => self.emit_expression(&p.expression, ctx), + _ => Err(format!("unsupported expression type: {:?}", expr)), + } + } +} diff --git a/crates/oxide_compiler/src/lib.rs b/crates/oxide_compiler/src/lib.rs index c941aba..a73f3f7 100644 --- a/crates/oxide_compiler/src/lib.rs +++ b/crates/oxide_compiler/src/lib.rs @@ -1,6 +1,8 @@ #![doc = "OxideJS - AST to bytecode compiler"] pub mod compiler; +pub mod counter; +pub mod emitter; pub mod hash; pub mod module; pub mod opcode;