diff --git a/valuescript_compiler/src/optimization/kal.rs b/valuescript_compiler/src/optimization/kal.rs index 5123e6c..21afadb 100644 --- a/valuescript_compiler/src/optimization/kal.rs +++ b/valuescript_compiler/src/optimization/kal.rs @@ -835,7 +835,12 @@ impl FnState { } } - fn apply_unary_op(&mut self, arg: &mut Value, dst: &Register, op: fn(input: &Val) -> Val) { + fn apply_unary_op( + &mut self, + arg: &mut Value, + dst: &Register, + op: fn(input: &Val) -> Result, + ) { match self.apply_unary_op_impl(arg, dst, op) { Some(_) => {} None => { @@ -848,10 +853,14 @@ impl FnState { &mut self, arg: &mut Value, dst: &Register, - op: fn(input: &Val) -> Val, + op: fn(input: &Val) -> Result, ) -> Option<()> { let arg = self.eval_arg(arg).try_to_val()?; - let kal = op(&arg).try_to_kal()?; + + let kal = match op(&arg) { + Ok(res) => res.try_to_kal()?, + Err(_) => return None, + }; self.set(dst.name.clone(), kal); diff --git a/valuescript_vm/src/binary_op.rs b/valuescript_vm/src/binary_op.rs new file mode 100644 index 0000000..be7318e --- /dev/null +++ b/valuescript_vm/src/binary_op.rs @@ -0,0 +1,24 @@ +pub enum BinaryOp { + Plus, + Minus, + Mul, + Div, + Mod, + Exp, + LooseEq, + LooseNe, + Eq, + Ne, + And, + Or, + Less, + LessEq, + Greater, + GreaterEq, + BitAnd, + BitOr, + BitXor, + LeftShift, + RightShift, + RightShiftUnsigned, +} diff --git a/valuescript_vm/src/bytecode_stack_frame.rs b/valuescript_vm/src/bytecode_stack_frame.rs index 4b0f553..4d9a585 100644 --- a/valuescript_vm/src/bytecode_stack_frame.rs +++ b/valuescript_vm/src/bytecode_stack_frame.rs @@ -38,12 +38,14 @@ pub struct CatchSetting { } impl BytecodeStackFrame { - pub fn apply_unary_op(&mut self, op: fn(input: &Val) -> Val) { + pub fn apply_unary_op(&mut self, op: fn(input: &Val) -> Result) -> Result<(), Val> { let input = self.decoder.decode_val(&mut self.registers); if let Some(register_index) = self.decoder.decode_register_index() { - self.registers[register_index] = op(&input); + self.registers[register_index] = op(&input)?; } + + Ok(()) } pub fn apply_binary_op( @@ -184,7 +186,7 @@ impl StackFrameTrait for BytecodeStackFrame { OpAnd => self.apply_binary_op(operations::op_and)?, OpOr => self.apply_binary_op(operations::op_or)?, - OpNot => self.apply_unary_op(operations::op_not), + OpNot => self.apply_unary_op(operations::op_not)?, OpLess => self.apply_binary_op(operations::op_less)?, OpLessEq => self.apply_binary_op(operations::op_less_eq)?, @@ -202,14 +204,14 @@ impl StackFrameTrait for BytecodeStackFrame { OpBitAnd => self.apply_binary_op(operations::op_bit_and)?, OpBitOr => self.apply_binary_op(operations::op_bit_or)?, - OpBitNot => self.apply_unary_op(operations::op_bit_not), + OpBitNot => self.apply_unary_op(operations::op_bit_not)?, OpBitXor => self.apply_binary_op(operations::op_bit_xor)?, OpLeftShift => self.apply_binary_op(operations::op_left_shift)?, OpRightShift => self.apply_binary_op(operations::op_right_shift)?, OpRightShiftUnsigned => self.apply_binary_op(operations::op_right_shift_unsigned)?, - TypeOf => self.apply_unary_op(operations::op_typeof), + TypeOf => self.apply_unary_op(operations::op_typeof)?, InstanceOf => self.apply_binary_op(operations::op_instance_of)?, In => self.apply_binary_op(operations::op_in)?, @@ -415,8 +417,8 @@ impl StackFrameTrait for BytecodeStackFrame { } } - UnaryPlus => self.apply_unary_op(operations::op_unary_plus), - UnaryMinus => self.apply_unary_op(operations::op_unary_minus), + UnaryPlus => self.apply_unary_op(operations::op_unary_plus)?, + UnaryMinus => self.apply_unary_op(operations::op_unary_minus)?, New => { // TODO: new Array diff --git a/valuescript_vm/src/lib.rs b/valuescript_vm/src/lib.rs index cf35ba8..fbaa09a 100644 --- a/valuescript_vm/src/lib.rs +++ b/valuescript_vm/src/lib.rs @@ -1,6 +1,7 @@ mod array_higher_functions; mod array_methods; mod bigint_methods; +mod binary_op; mod builtins; mod bytecode; mod bytecode_decoder; @@ -20,6 +21,7 @@ pub mod operations; mod stack_frame; mod string_methods; mod todo_fn; +mod unary_op; mod val_storage; mod virtual_machine; pub mod vs_array; diff --git a/valuescript_vm/src/operations.rs b/valuescript_vm/src/operations.rs index a65c543..2542e0a 100644 --- a/valuescript_vm/src/operations.rs +++ b/valuescript_vm/src/operations.rs @@ -9,6 +9,7 @@ use num_traits::ToPrimitive; use crate::array_methods::op_sub_array; use crate::bigint_methods::op_sub_bigint; +use crate::binary_op::BinaryOp; use crate::builtins::internal_error_builtin::ToInternalError; use crate::builtins::range_error_builtin::ToRangeError; use crate::builtins::type_error_builtin::ToTypeError; @@ -16,12 +17,17 @@ use crate::native_function::native_fn; use crate::native_function::NativeFunction; use crate::number_methods::op_sub_number; use crate::string_methods::op_sub_string; +use crate::unary_op::UnaryOp; use crate::vs_value::ToVal; use crate::vs_value::Val; use crate::vs_value::ValTrait; use crate::vs_value::VsType; pub fn op_plus(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Plus, right) { + return res; + } + let left_prim = left.to_primitive(); let right_prim = right.to_primitive(); @@ -48,14 +54,22 @@ pub fn op_plus(left: &Val, right: &Val) -> Result { Ok(Val::Number(left_prim.to_number() + right_prim.to_number())) } -pub fn op_unary_plus(input: &Val) -> Val { - match input.as_bigint_data() { +pub fn op_unary_plus(input: &Val) -> Result { + if let Some(res) = input.override_unary_op(UnaryOp::Plus) { + return res; + } + + Ok(match input.as_bigint_data() { Some(bigint) => Val::BigInt(bigint), _ => Val::Number(input.to_number()), - } + }) } pub fn op_minus(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Minus, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint - right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -63,14 +77,22 @@ pub fn op_minus(left: &Val, right: &Val) -> Result { } } -pub fn op_unary_minus(input: &Val) -> Val { - match input.as_bigint_data() { +pub fn op_unary_minus(input: &Val) -> Result { + if let Some(res) = input.override_unary_op(UnaryOp::Minus) { + return res; + } + + Ok(match input.as_bigint_data() { Some(bigint) => Val::BigInt(-bigint), _ => Val::Number(-input.to_number()), - } + }) } pub fn op_mul(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Mul, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint * right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -79,6 +101,10 @@ pub fn op_mul(left: &Val, right: &Val) -> Result { } pub fn op_div(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Div, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint / right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -87,6 +113,10 @@ pub fn op_div(left: &Val, right: &Val) -> Result { } pub fn op_mod(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Mod, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint % right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -95,6 +125,10 @@ pub fn op_mod(left: &Val, right: &Val) -> Result { } pub fn op_exp(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Exp, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => { if right_bigint.sign() == Sign::Minus { @@ -247,10 +281,18 @@ where } pub fn op_eq(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::LooseEq, right) { + return res; + } + Ok(Val::Bool(op_eq_impl(left, right)?)) } pub fn op_ne(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::LooseNe, right) { + return res; + } + Ok(Val::Bool(!op_eq_impl(left, right)?)) } @@ -358,32 +400,56 @@ pub fn op_triple_eq_impl(left: &Val, right: &Val) -> Result { } pub fn op_triple_eq(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Eq, right) { + return res; + } + let is_eq = op_triple_eq_impl(left, right)?; Ok(Val::Bool(is_eq)) } pub fn op_triple_ne(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Ne, right) { + return res; + } + let is_eq = op_triple_eq_impl(left, right)?; Ok(Val::Bool(!is_eq)) } pub fn op_and(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::And, right) { + return res; + } + let truthy = left.is_truthy(); Ok((if truthy { right } else { left }).clone()) } pub fn op_or(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Or, right) { + return res; + } + let truthy = left.is_truthy(); Ok((if truthy { left } else { right }).clone()) } -pub fn op_not(input: &Val) -> Val { - Val::Bool(!input.is_truthy()) +pub fn op_not(input: &Val) -> Result { + if let Some(res) = input.override_unary_op(UnaryOp::Not) { + return res; + } + + Ok(Val::Bool(!input.is_truthy())) } pub fn op_less(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Less, right) { + return res; + } + Ok(Val::Bool(match (left, right) { (_, Val::Undefined) | (Val::Undefined, _) => false, (Val::Null, Val::Null) => false, @@ -396,6 +462,10 @@ pub fn op_less(left: &Val, right: &Val) -> Result { } pub fn op_less_eq(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::LessEq, right) { + return res; + } + Ok(Val::Bool(match (left, right) { (_, Val::Undefined) | (Val::Undefined, _) => false, (Val::Null, Val::Null) => true, @@ -411,6 +481,10 @@ pub fn op_less_eq(left: &Val, right: &Val) -> Result { } pub fn op_greater(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::Greater, right) { + return res; + } + Ok(Val::Bool(match (left, right) { (_, Val::Undefined) | (Val::Undefined, _) => false, (Val::Null, Val::Null) => false, @@ -423,6 +497,10 @@ pub fn op_greater(left: &Val, right: &Val) -> Result { } pub fn op_greater_eq(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::GreaterEq, right) { + return res; + } + Ok(Val::Bool(match (left, right) { (_, Val::Undefined) | (Val::Undefined, _) => false, (Val::Null, Val::Null) => true, @@ -472,6 +550,10 @@ pub fn to_u32(x: f64) -> u32 { } pub fn op_bit_and(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::BitAnd, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint & right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -483,6 +565,10 @@ pub fn op_bit_and(left: &Val, right: &Val) -> Result { } pub fn op_bit_or(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::BitOr, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint | right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -493,17 +579,25 @@ pub fn op_bit_or(left: &Val, right: &Val) -> Result { } } -pub fn op_bit_not(input: &Val) -> Val { - match input.as_bigint_data() { +pub fn op_bit_not(input: &Val) -> Result { + if let Some(res) = input.override_unary_op(UnaryOp::BitNot) { + return res; + } + + Ok(match input.as_bigint_data() { Some(bigint) => Val::BigInt(!bigint), None => { let res_i32 = !to_i32(input.to_number()); Val::Number(res_i32 as f64) } - } + }) } pub fn op_bit_xor(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::BitXor, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint ^ right_bigint)), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -515,6 +609,10 @@ pub fn op_bit_xor(left: &Val, right: &Val) -> Result { } pub fn op_left_shift(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::LeftShift, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt( left_bigint << right_bigint.to_i64().expect("TODO"), @@ -528,6 +626,10 @@ pub fn op_left_shift(left: &Val, right: &Val) -> Result { } pub fn op_right_shift(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::RightShift, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => { let right_i64 = right_bigint @@ -544,6 +646,10 @@ pub fn op_right_shift(left: &Val, right: &Val) -> Result { } pub fn op_right_shift_unsigned(left: &Val, right: &Val) -> Result { + if let Some(res) = left.override_binary_op(BinaryOp::RightShiftUnsigned, right) { + return res; + } + match (left.as_bigint_data(), right.as_bigint_data()) { (Some(_), Some(_)) => Err("BigInts don't support unsigned right shift".to_type_error()), (Some(_), None) | (None, Some(_)) => Err("Cannot mix BigInt with other types".to_type_error()), @@ -554,23 +660,25 @@ pub fn op_right_shift_unsigned(left: &Val, right: &Val) -> Result { } } -pub fn op_typeof(input: &Val) -> Val { +pub fn op_typeof(input: &Val) -> Result { use VsType::*; - match input.typeof_() { - Undefined => "undefined", - Null => "object", - Bool => "boolean", - Number => "number", - BigInt => "bigint", - Symbol => "symbol", - String => "string", - Array => "object", - Object => "object", - Function => "function", - Class => "function", - } - .to_val() + Ok( + match input.typeof_() { + Undefined => "undefined", + Null => "object", + Bool => "boolean", + Number => "number", + BigInt => "bigint", + Symbol => "symbol", + String => "string", + Array => "object", + Object => "object", + Function => "function", + Class => "function", + } + .to_val(), + ) } pub fn op_instance_of(left: &Val, right: &Val) -> Result { diff --git a/valuescript_vm/src/unary_op.rs b/valuescript_vm/src/unary_op.rs new file mode 100644 index 0000000..6250841 --- /dev/null +++ b/valuescript_vm/src/unary_op.rs @@ -0,0 +1,6 @@ +pub enum UnaryOp { + Plus, + Minus, + Not, + BitNot, +} diff --git a/valuescript_vm/src/vs_value.rs b/valuescript_vm/src/vs_value.rs index 6a811d8..da680ce 100644 --- a/valuescript_vm/src/vs_value.rs +++ b/valuescript_vm/src/vs_value.rs @@ -9,10 +9,12 @@ use num_bigint::BigInt; use num_traits::cast::ToPrimitive; use num_traits::Zero; +use crate::binary_op::BinaryOp; use crate::copy_counter::CopyCounter; use crate::native_function::ThisWrapper; use crate::operations::{op_sub, op_submov}; use crate::stack_frame::StackFrame; +use crate::unary_op::UnaryOp; use crate::vs_array::VsArray; use crate::vs_class::VsClass; use crate::vs_function::VsFunction; @@ -100,6 +102,14 @@ pub trait ValTrait: fmt::Display { fn has(&self, key: &Val) -> Option; fn submov(&mut self, key: &Val, value: Val) -> Result<(), Val>; + fn override_binary_op(&self, _op: BinaryOp, _right: &Val) -> Option> { + None + } + + fn override_unary_op(&self, _op: UnaryOp) -> Option> { + None + } + fn pretty_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; fn codify(&self) -> String; } @@ -454,14 +464,14 @@ impl ValTrait for Val { fn load_function(&self) -> LoadFunctionResult { use Val::*; - return match self { + match self { Function(f) => LoadFunctionResult::StackFrame(f.make_frame()), Static(s) => s.load_function(), Dynamic(val) => val.load_function(), StoragePtr(ptr) => ptr.get().load_function(), _ => LoadFunctionResult::NotAFunction, - }; + } } fn sub(&self, key: &Val) -> Result {