mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-04-18 03:00:27 -04:00
Exceptions for binary ops
This commit is contained in:
@@ -122,6 +122,8 @@ static FROM: NativeFunction = NativeFunction {
|
||||
first_param.clone(),
|
||||
Val::String(Rc::new("length".to_string())),
|
||||
)
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap() // TODO: Exception
|
||||
.to_number();
|
||||
|
||||
if len.is_sign_negative() || len.is_nan() {
|
||||
@@ -139,7 +141,11 @@ static FROM: NativeFunction = NativeFunction {
|
||||
// TODO: We should probably use a frame and step through this
|
||||
// Also using op_sub is slow. Should write specialized stuff instead.
|
||||
for i in 0..len {
|
||||
arr.push(op_sub(first_param.clone(), Val::Number(i as f64)));
|
||||
arr.push(
|
||||
op_sub(first_param.clone(), Val::Number(i as f64))
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap(), // TODO: Exception
|
||||
);
|
||||
}
|
||||
|
||||
Val::Array(Rc::new(VsArray::from(arr)))
|
||||
|
||||
@@ -30,15 +30,20 @@ impl BytecodeStackFrame {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_binary_op(&mut self, op: fn(left: Val, right: Val) -> Val) {
|
||||
pub fn apply_binary_op(
|
||||
&mut self,
|
||||
op: fn(left: Val, right: Val) -> Result<Val, Val>,
|
||||
) -> Result<(), Val> {
|
||||
let left = self.decoder.decode_val(&self.registers);
|
||||
let right = self.decoder.decode_val(&self.registers);
|
||||
|
||||
let register_index = self.decoder.decode_register_index();
|
||||
|
||||
if register_index.is_some() {
|
||||
self.registers[register_index.unwrap()] = op(left, right);
|
||||
self.registers[register_index.unwrap()] = op(left, right)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn transfer_parameters(&mut self, new_frame: &mut StackFrame) {
|
||||
@@ -110,52 +115,52 @@ impl StackFrameTrait for BytecodeStackFrame {
|
||||
OpInc => {
|
||||
let register_index = self.decoder.decode_register_index().unwrap();
|
||||
let mut val = self.registers[register_index].clone();
|
||||
val = operations::op_plus(val, Val::Number(1_f64));
|
||||
val = operations::op_plus(val, Val::Number(1_f64))?; // TODO: BigInt
|
||||
self.registers[register_index] = val;
|
||||
}
|
||||
|
||||
OpDec => {
|
||||
let register_index = self.decoder.decode_register_index().unwrap();
|
||||
let mut val = self.registers[register_index].clone();
|
||||
val = operations::op_minus(val, Val::Number(1_f64));
|
||||
val = operations::op_minus(val, Val::Number(1_f64))?; // TODO: BigInt
|
||||
self.registers[register_index] = val;
|
||||
}
|
||||
|
||||
OpPlus => self.apply_binary_op(operations::op_plus),
|
||||
OpMinus => self.apply_binary_op(operations::op_minus),
|
||||
OpMul => self.apply_binary_op(operations::op_mul),
|
||||
OpDiv => self.apply_binary_op(operations::op_div),
|
||||
OpMod => self.apply_binary_op(operations::op_mod),
|
||||
OpExp => self.apply_binary_op(operations::op_exp),
|
||||
OpEq => self.apply_binary_op(operations::op_eq),
|
||||
OpNe => self.apply_binary_op(operations::op_ne),
|
||||
OpTripleEq => self.apply_binary_op(operations::op_triple_eq),
|
||||
OpTripleNe => self.apply_binary_op(operations::op_triple_ne),
|
||||
OpAnd => self.apply_binary_op(operations::op_and),
|
||||
OpOr => self.apply_binary_op(operations::op_or),
|
||||
OpPlus => self.apply_binary_op(operations::op_plus)?,
|
||||
OpMinus => self.apply_binary_op(operations::op_minus)?,
|
||||
OpMul => self.apply_binary_op(operations::op_mul)?,
|
||||
OpDiv => self.apply_binary_op(operations::op_div)?,
|
||||
OpMod => self.apply_binary_op(operations::op_mod)?,
|
||||
OpExp => self.apply_binary_op(operations::op_exp)?,
|
||||
OpEq => self.apply_binary_op(operations::op_eq)?,
|
||||
OpNe => self.apply_binary_op(operations::op_ne)?,
|
||||
OpTripleEq => self.apply_binary_op(operations::op_triple_eq)?,
|
||||
OpTripleNe => self.apply_binary_op(operations::op_triple_ne)?,
|
||||
OpAnd => self.apply_binary_op(operations::op_and)?,
|
||||
OpOr => self.apply_binary_op(operations::op_or)?,
|
||||
|
||||
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),
|
||||
OpGreater => self.apply_binary_op(operations::op_greater),
|
||||
OpGreaterEq => self.apply_binary_op(operations::op_greater_eq),
|
||||
OpNullishCoalesce => self.apply_binary_op(operations::op_nullish_coalesce),
|
||||
OpOptionalChain => self.apply_binary_op(operations::op_optional_chain),
|
||||
OpBitAnd => self.apply_binary_op(operations::op_bit_and),
|
||||
OpBitOr => self.apply_binary_op(operations::op_bit_or),
|
||||
OpLess => self.apply_binary_op(operations::op_less)?,
|
||||
OpLessEq => self.apply_binary_op(operations::op_less_eq)?,
|
||||
OpGreater => self.apply_binary_op(operations::op_greater)?,
|
||||
OpGreaterEq => self.apply_binary_op(operations::op_greater_eq)?,
|
||||
OpNullishCoalesce => self.apply_binary_op(operations::op_nullish_coalesce)?,
|
||||
OpOptionalChain => self.apply_binary_op(operations::op_optional_chain)?,
|
||||
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),
|
||||
|
||||
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),
|
||||
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),
|
||||
|
||||
InstanceOf => self.apply_binary_op(operations::op_instance_of),
|
||||
In => self.apply_binary_op(operations::op_in),
|
||||
InstanceOf => self.apply_binary_op(operations::op_instance_of)?,
|
||||
In => self.apply_binary_op(operations::op_in)?,
|
||||
|
||||
Call => {
|
||||
let fn_ = self.decoder.decode_val(&self.registers);
|
||||
@@ -244,7 +249,7 @@ impl StackFrameTrait for BytecodeStackFrame {
|
||||
}
|
||||
}
|
||||
|
||||
Sub => self.apply_binary_op(operations::op_sub),
|
||||
Sub => self.apply_binary_op(operations::op_sub)?,
|
||||
|
||||
SubMov => {
|
||||
let subscript = self.decoder.decode_val(&self.registers);
|
||||
@@ -253,7 +258,7 @@ impl StackFrameTrait for BytecodeStackFrame {
|
||||
let register_index = self.decoder.decode_register_index().unwrap();
|
||||
let mut target = self.registers[register_index].clone(); // TODO: Lift
|
||||
|
||||
operations::op_submov(&mut target, subscript, value);
|
||||
operations::op_submov(&mut target, subscript, value)?;
|
||||
self.registers[register_index] = target;
|
||||
}
|
||||
|
||||
@@ -275,7 +280,7 @@ impl StackFrameTrait for BytecodeStackFrame {
|
||||
ThisArg::Val(val) => val.clone(),
|
||||
},
|
||||
subscript,
|
||||
);
|
||||
)?;
|
||||
|
||||
match fn_.load_function() {
|
||||
LoadFunctionResult::NotAFunction => {
|
||||
|
||||
@@ -12,7 +12,23 @@ use super::vs_value::Val;
|
||||
use super::vs_value::ValTrait;
|
||||
use super::vs_value::VsType;
|
||||
|
||||
pub fn op_plus(left: Val, right: Val) -> Val {
|
||||
macro_rules! format_err {
|
||||
($fmt:expr $(, $($arg:expr),*)?) => {{
|
||||
let formatted_string = format!($fmt $(, $($arg),*)?);
|
||||
|
||||
// TODO: This should be a proper error type
|
||||
Err(Val::String(Rc::new(formatted_string)))
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! format_string {
|
||||
($fmt:expr $(, $($arg:expr),*)?) => {{
|
||||
let formatted_string = format!($fmt $(, $($arg),*)?);
|
||||
Val::String(Rc::new(formatted_string))
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn op_plus(left: Val, right: Val) -> Result<Val, Val> {
|
||||
let left_prim = left.to_primitive();
|
||||
let right_prim = right.to_primitive();
|
||||
|
||||
@@ -20,25 +36,27 @@ pub fn op_plus(left: Val, right: Val) -> Val {
|
||||
let right_type = right_prim.typeof_();
|
||||
|
||||
if left_type == VsType::String || right_type == VsType::String {
|
||||
return Val::String(Rc::new(
|
||||
return Ok(Val::String(Rc::new(
|
||||
left_prim.val_to_string() + &right_prim.val_to_string(),
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
if left_type == VsType::BigInt || right_type == VsType::BigInt {
|
||||
if left_type != right_type {
|
||||
std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)");
|
||||
return Err(Val::String(Rc::new(
|
||||
"TypeError: Cannot mix BigInt and other types".to_string(),
|
||||
)));
|
||||
}
|
||||
|
||||
match (left_prim.as_bigint_data(), right_prim.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => {
|
||||
return Val::BigInt(left_bigint + right_bigint);
|
||||
return Ok(Val::BigInt(left_bigint + right_bigint));
|
||||
}
|
||||
_ => std::panic!("Not implemented"),
|
||||
_ => return format_err!("TODO"),
|
||||
}
|
||||
}
|
||||
|
||||
return Val::Number(left_prim.to_number() + right_prim.to_number());
|
||||
return Ok(Val::Number(left_prim.to_number() + right_prim.to_number()));
|
||||
}
|
||||
|
||||
pub fn op_unary_plus(input: Val) -> Val {
|
||||
@@ -48,13 +66,13 @@ pub fn op_unary_plus(input: Val) -> Val {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_minus(left: Val, right: Val) -> Val {
|
||||
pub fn op_minus(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint - right_bigint),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
}
|
||||
_ => Val::Number(left.to_number() - right.to_number()),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint - right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => Err(Val::String(Rc::new(
|
||||
"TypeError: Cannot mix BigInt with other types".to_string(),
|
||||
))),
|
||||
_ => Ok(Val::Number(left.to_number() - right.to_number())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,85 +83,83 @@ pub fn op_unary_minus(input: Val) -> Val {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_mul(left: Val, right: Val) -> Val {
|
||||
pub fn op_mul(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint * right_bigint),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint * right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => Val::Number(left.to_number() * right.to_number()),
|
||||
_ => Ok(Val::Number(left.to_number() * right.to_number())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_div(left: Val, right: Val) -> Val {
|
||||
pub fn op_div(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint / right_bigint),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint / right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => Val::Number(left.to_number() / right.to_number()),
|
||||
_ => Ok(Val::Number(left.to_number() / right.to_number())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_mod(left: Val, right: Val) -> Val {
|
||||
pub fn op_mod(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint % right_bigint),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint % right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => Val::Number(left.to_number() % right.to_number()),
|
||||
_ => Ok(Val::Number(left.to_number() % right.to_number())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_exp(left: Val, right: Val) -> Val {
|
||||
pub fn op_exp(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => {
|
||||
if right_bigint.sign() == Sign::Minus {
|
||||
std::panic!("TODO: Exceptions (RangeError: Exponent must be non-negative)");
|
||||
return format_err!("RangeError: Exponent must be non-negative");
|
||||
}
|
||||
|
||||
let exp = match right_bigint.to_u32() {
|
||||
Some(exp) => exp,
|
||||
None => {
|
||||
std::panic!("TODO: Exceptions (RangeError: Exponent must be less than 2^32)")
|
||||
}
|
||||
None => return format_err!("RangeError: Exponent must be less than 2^32"),
|
||||
};
|
||||
|
||||
Val::BigInt(left_bigint.pow(exp))
|
||||
Ok(Val::BigInt(left_bigint.pow(exp)))
|
||||
}
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
std::panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => Val::Number(left.to_number().powf(right.to_number())),
|
||||
_ => Ok(Val::Number(left.to_number().powf(right.to_number()))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_eq(left: Val, right: Val) -> Val {
|
||||
Val::Bool(match (left, right) {
|
||||
pub fn op_eq(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(Val::Bool(match (left, right) {
|
||||
(Val::Undefined, Val::Undefined) => true,
|
||||
(Val::Null, Val::Null) => true,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool == right_bool,
|
||||
(Val::Number(left_number), Val::Number(right_number)) => left_number == right_number,
|
||||
(Val::String(left_string), Val::String(right_string)) => left_string == right_string,
|
||||
(Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint == right_bigint,
|
||||
_ => panic!("TODO"),
|
||||
})
|
||||
_ => return format_err!("TODO"),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn op_ne(left: Val, right: Val) -> Val {
|
||||
Val::Bool(match (left, right) {
|
||||
pub fn op_ne(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(Val::Bool(match (left, right) {
|
||||
(Val::Undefined, Val::Undefined) => false,
|
||||
(Val::Null, Val::Null) => false,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool != right_bool,
|
||||
(Val::Number(left_number), Val::Number(right_number)) => left_number != right_number,
|
||||
(Val::String(left_string), Val::String(right_string)) => left_string != right_string,
|
||||
(Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint != right_bigint,
|
||||
_ => panic!("TODO"),
|
||||
})
|
||||
_ => return format_err!("TODO"),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn op_triple_eq_impl(left: Val, right: Val) -> bool {
|
||||
match (&left, &right) {
|
||||
pub fn op_triple_eq_impl(left: Val, right: Val) -> Result<bool, Val> {
|
||||
Ok(match (&left, &right) {
|
||||
(Val::Undefined, Val::Undefined) => true,
|
||||
(Val::Null, Val::Null) => true,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool == right_bool,
|
||||
@@ -154,34 +170,36 @@ pub fn op_triple_eq_impl(left: Val, right: Val) -> bool {
|
||||
if left.typeof_() != right.typeof_() {
|
||||
false
|
||||
} else {
|
||||
panic!("TODO")
|
||||
return format_err!("TODO");
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn op_triple_eq(left: Val, right: Val) -> Val {
|
||||
Val::Bool(op_triple_eq_impl(left, right))
|
||||
pub fn op_triple_eq(left: Val, right: Val) -> Result<Val, Val> {
|
||||
let is_eq = op_triple_eq_impl(left, right)?;
|
||||
Ok(Val::Bool(is_eq))
|
||||
}
|
||||
|
||||
pub fn op_triple_ne(left: Val, right: Val) -> Val {
|
||||
Val::Bool(!op_triple_eq_impl(left, right))
|
||||
pub fn op_triple_ne(left: Val, right: Val) -> Result<Val, Val> {
|
||||
let is_eq = op_triple_eq_impl(left, right)?;
|
||||
Ok(Val::Bool(!is_eq))
|
||||
}
|
||||
|
||||
pub fn op_and(left: Val, right: Val) -> Val {
|
||||
return if left.is_truthy() { right } else { left };
|
||||
pub fn op_and(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(if left.is_truthy() { right } else { left })
|
||||
}
|
||||
|
||||
pub fn op_or(left: Val, right: Val) -> Val {
|
||||
return if left.is_truthy() { left } else { right };
|
||||
pub fn op_or(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(if left.is_truthy() { left } else { right })
|
||||
}
|
||||
|
||||
pub fn op_not(input: Val) -> Val {
|
||||
return Val::Bool(!input.is_truthy());
|
||||
}
|
||||
|
||||
pub fn op_less(left: Val, right: Val) -> Val {
|
||||
Val::Bool(match (&left, &right) {
|
||||
pub fn op_less(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(Val::Bool(match (&left, &right) {
|
||||
(Val::Undefined, Val::Undefined) => false,
|
||||
(Val::Null, Val::Null) => false,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool < right_bool,
|
||||
@@ -192,56 +210,55 @@ pub fn op_less(left: Val, right: Val) -> Val {
|
||||
if left.typeof_() == VsType::Undefined || right.typeof_() == VsType::Undefined {
|
||||
false
|
||||
} else {
|
||||
panic!("TODO")
|
||||
return format_err!("TODO");
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn op_less_eq(left: Val, right: Val) -> Val {
|
||||
Val::Bool(match (left, right) {
|
||||
pub fn op_less_eq(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(Val::Bool(match (left, right) {
|
||||
(Val::Undefined, Val::Undefined) => false,
|
||||
(Val::Null, Val::Null) => true,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool <= right_bool,
|
||||
(Val::Number(left_number), Val::Number(right_number)) => left_number <= right_number,
|
||||
(Val::String(left_string), Val::String(right_string)) => left_string <= right_string,
|
||||
(Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint <= right_bigint,
|
||||
_ => panic!("TODO"),
|
||||
})
|
||||
_ => return format_err!("TODO"),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn op_greater(left: Val, right: Val) -> Val {
|
||||
Val::Bool(match (left, right) {
|
||||
pub fn op_greater(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(Val::Bool(match (left, right) {
|
||||
(Val::Undefined, Val::Undefined) => false,
|
||||
(Val::Null, Val::Null) => false,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool > right_bool,
|
||||
(Val::Number(left_number), Val::Number(right_number)) => left_number > right_number,
|
||||
(Val::String(left_string), Val::String(right_string)) => left_string > right_string,
|
||||
(Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint > right_bigint,
|
||||
_ => panic!("TODO"),
|
||||
})
|
||||
_ => return format_err!("TODO"),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn op_greater_eq(left: Val, right: Val) -> Val {
|
||||
Val::Bool(match (left, right) {
|
||||
pub fn op_greater_eq(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(Val::Bool(match (left, right) {
|
||||
(Val::Undefined, Val::Undefined) => false,
|
||||
(Val::Null, Val::Null) => true,
|
||||
(Val::Bool(left_bool), Val::Bool(right_bool)) => left_bool >= right_bool,
|
||||
(Val::Number(left_number), Val::Number(right_number)) => left_number >= right_number,
|
||||
(Val::String(left_string), Val::String(right_string)) => left_string >= right_string,
|
||||
(Val::BigInt(left_bigint), Val::BigInt(right_bigint)) => left_bigint >= right_bigint,
|
||||
_ => panic!("TODO"),
|
||||
})
|
||||
_ => return format_err!("TODO"),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn op_nullish_coalesce(left: Val, right: Val) -> Val {
|
||||
return if left.is_nullish() { right } else { left };
|
||||
pub fn op_nullish_coalesce(left: Val, right: Val) -> Result<Val, Val> {
|
||||
Ok(if left.is_nullish() { right } else { left })
|
||||
}
|
||||
|
||||
pub fn op_optional_chain(left: Val, right: Val) -> Val {
|
||||
pub fn op_optional_chain(left: Val, right: Val) -> Result<Val, Val> {
|
||||
return match left {
|
||||
Val::Undefined => Val::Undefined,
|
||||
Val::Null => Val::Undefined,
|
||||
Val::Undefined | Val::Null => Ok(Val::Undefined),
|
||||
|
||||
_ => op_sub(left, right),
|
||||
};
|
||||
@@ -267,28 +284,28 @@ pub fn to_u32(x: f64) -> u32 {
|
||||
return int1 as u32;
|
||||
}
|
||||
|
||||
pub fn op_bit_and(left: Val, right: Val) -> Val {
|
||||
pub fn op_bit_and(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint & right_bigint),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint & right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => {
|
||||
let res_i32 = to_i32(left.to_number()) & to_i32(right.to_number());
|
||||
Val::Number(res_i32 as f64)
|
||||
Ok(Val::Number(res_i32 as f64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_bit_or(left: Val, right: Val) -> Val {
|
||||
pub fn op_bit_or(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint | right_bigint),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint | right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => {
|
||||
let res_i32 = to_i32(left.to_number()) | to_i32(right.to_number());
|
||||
Val::Number(res_i32 as f64)
|
||||
Ok(Val::Number(res_i32 as f64))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,60 +320,61 @@ pub fn op_bit_not(input: Val) -> Val {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_bit_xor(left: Val, right: Val) -> Val {
|
||||
pub fn op_bit_xor(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => Val::BigInt(left_bigint ^ right_bigint),
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(left_bigint ^ right_bigint)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => {
|
||||
let res_i32 = to_i32(left.to_number()) ^ to_i32(right.to_number());
|
||||
Val::Number(res_i32 as f64)
|
||||
Ok(Val::Number(res_i32 as f64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_left_shift(left: Val, right: Val) -> Val {
|
||||
pub fn op_left_shift(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => {
|
||||
Val::BigInt(left_bigint << right_bigint.to_i64().expect("TODO"))
|
||||
}
|
||||
(Some(left_bigint), Some(right_bigint)) => Ok(Val::BigInt(
|
||||
left_bigint << right_bigint.to_i64().expect("TODO"),
|
||||
)),
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => {
|
||||
let res_i32 = to_i32(left.to_number()) << (to_u32(right.to_number()) & 0x1f);
|
||||
Val::Number(res_i32 as f64)
|
||||
Ok(Val::Number(res_i32 as f64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_right_shift(left: Val, right: Val) -> Val {
|
||||
pub fn op_right_shift(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(left_bigint), Some(right_bigint)) => {
|
||||
Val::BigInt(left_bigint >> right_bigint.to_i64().expect("TODO"))
|
||||
let right_i64 = right_bigint.to_i64().ok_or(format_string!("TODO"))?;
|
||||
Ok(Val::BigInt(left_bigint >> right_i64))
|
||||
}
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => {
|
||||
let res_i32 = to_i32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f);
|
||||
Val::Number(res_i32 as f64)
|
||||
Ok(Val::Number(res_i32 as f64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_right_shift_unsigned(left: Val, right: Val) -> Val {
|
||||
pub fn op_right_shift_unsigned(left: Val, right: Val) -> Result<Val, Val> {
|
||||
match (left.as_bigint_data(), right.as_bigint_data()) {
|
||||
(Some(_), Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: BigInts don't support unsigned right shift)")
|
||||
format_err!("TypeError: BigInts don't support unsigned right shift")
|
||||
}
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
panic!("TODO: Exceptions (TypeError: Cannot mix BigInt with other types)")
|
||||
format_err!("TypeError: Cannot mix BigInt with other types")
|
||||
}
|
||||
_ => {
|
||||
let res_u32 = to_u32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f);
|
||||
Val::Number(res_u32 as f64)
|
||||
Ok(Val::Number(res_u32 as f64))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,74 +396,71 @@ pub fn op_typeof(input: Val) -> Val {
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn op_instance_of(_left: Val, _right: Val) -> Val {
|
||||
pub fn op_instance_of(_left: Val, _right: Val) -> Result<Val, Val> {
|
||||
std::panic!("Not implemented: op_instance_of");
|
||||
}
|
||||
|
||||
pub fn op_in(_left: Val, _right: Val) -> Val {
|
||||
pub fn op_in(_left: Val, _right: Val) -> Result<Val, Val> {
|
||||
std::panic!("Not implemented: op_in");
|
||||
}
|
||||
|
||||
pub fn op_sub(left: Val, right: Val) -> Val {
|
||||
pub fn op_sub(left: Val, right: Val) -> Result<Val, Val> {
|
||||
return match left {
|
||||
Val::Void => std::panic!("Shouldn't happen"),
|
||||
Val::Undefined => std::panic!("TODO: Exceptions"),
|
||||
Val::Null => std::panic!("TODO: Exceptions"),
|
||||
Val::Bool(_) => match right.val_to_string().as_str() {
|
||||
Val::Void => format_err!("Internal: Shouldn't happen"), // TODO: Internal errors
|
||||
Val::Undefined => format_err!("TypeError: Cannot subscript undefined"),
|
||||
Val::Null => format_err!("TypeError: Cannot subscript null"),
|
||||
Val::Bool(_) => Ok(match right.val_to_string().as_str() {
|
||||
"toString" => Val::Static(&BOOL_TO_STRING),
|
||||
"valueOf" => Val::Static(&BOOL_VALUE_OF),
|
||||
_ => Val::Undefined,
|
||||
},
|
||||
Val::Number(number) => op_sub_number(number, &right),
|
||||
Val::BigInt(bigint) => op_sub_bigint(&bigint, &right),
|
||||
Val::String(string_data) => op_sub_string(&string_data, &right),
|
||||
}),
|
||||
Val::Number(number) => Ok(op_sub_number(number, &right)),
|
||||
Val::BigInt(bigint) => Ok(op_sub_bigint(&bigint, &right)),
|
||||
Val::String(string_data) => Ok(op_sub_string(&string_data, &right)),
|
||||
Val::Array(array_data) => {
|
||||
let right_index = match right.to_index() {
|
||||
None => {
|
||||
// FIXME: Inefficient val_to_string() that gets duplicated
|
||||
// when subscripting the object
|
||||
if right.val_to_string() == "length" {
|
||||
return Val::Number(array_data.elements.len() as f64);
|
||||
return Ok(Val::Number(array_data.elements.len() as f64));
|
||||
}
|
||||
|
||||
return array_data.object.sub(right);
|
||||
return Ok(array_data.object.sub(right));
|
||||
}
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
if right_index >= array_data.elements.len() {
|
||||
return Val::Undefined;
|
||||
return Ok(Val::Undefined);
|
||||
}
|
||||
|
||||
let res = array_data.elements[right_index].clone();
|
||||
|
||||
return match res {
|
||||
return Ok(match res {
|
||||
Val::Void => Val::Undefined,
|
||||
_ => res,
|
||||
};
|
||||
});
|
||||
}
|
||||
Val::Object(object_data) => {
|
||||
return object_data.sub(right);
|
||||
}
|
||||
Val::Function(_) => Val::Undefined,
|
||||
Val::Class(_) => Val::Undefined,
|
||||
Val::Static(s) => s.sub(right),
|
||||
Val::Custom(custom_data) => custom_data.sub(right),
|
||||
Val::Object(object_data) => Ok(object_data.sub(right)),
|
||||
Val::Function(_) | Val::Class(_) => Ok(Val::Undefined),
|
||||
Val::Static(s) => Ok(s.sub(right)),
|
||||
Val::Custom(custom_data) => Ok(custom_data.sub(right)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn op_submov(target: &mut Val, subscript: Val, value: Val) {
|
||||
pub fn op_submov(target: &mut Val, subscript: Val, value: Val) -> Result<(), Val> {
|
||||
match target {
|
||||
Val::Void => std::panic!("Shouldn't happen"),
|
||||
Val::Undefined => std::panic!("TODO: Exceptions"),
|
||||
Val::Null => std::panic!("TODO: Exceptions"),
|
||||
Val::Bool(_) => std::panic!("TODO: Exceptions"),
|
||||
Val::Number(_) => std::panic!("TODO: Exceptions"),
|
||||
Val::BigInt(_) => std::panic!("TODO: Exceptions"),
|
||||
Val::String(_) => std::panic!("TODO: Exceptions"),
|
||||
Val::Void => format_err!("Internal: Shouldn't happen"), // TODO: Internal errors
|
||||
Val::Undefined => format_err!("Cannot assign to subscript of undefined"),
|
||||
Val::Null => format_err!("Cannot assign to subscript of null"),
|
||||
Val::Bool(_) => format_err!("Cannot assign to subscript of bool"),
|
||||
Val::Number(_) => format_err!("Cannot assign to subscript of number"),
|
||||
Val::BigInt(_) => format_err!("Cannot assign to subscript of bigint"),
|
||||
Val::String(_) => format_err!("Cannot assign to subscript of string"),
|
||||
Val::Array(array_data) => {
|
||||
let subscript_index = match subscript.to_index() {
|
||||
None => std::panic!("Not implemented: non-uint array subscript assignment"),
|
||||
None => return format_err!("TODO: non-uint array subscript assignment"),
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
@@ -455,7 +470,7 @@ pub fn op_submov(target: &mut Val, subscript: Val, value: Val) {
|
||||
array_data_mut.elements[subscript_index] = value;
|
||||
} else {
|
||||
if subscript_index - array_data_mut.elements.len() > 100 {
|
||||
std::panic!("Not implemented: Sparse arrays");
|
||||
return format_err!("TODO: Sparse arrays");
|
||||
}
|
||||
|
||||
while subscript_index > array_data_mut.elements.len() {
|
||||
@@ -464,16 +479,20 @@ pub fn op_submov(target: &mut Val, subscript: Val, value: Val) {
|
||||
|
||||
array_data_mut.elements.push(value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Val::Object(object_data) => {
|
||||
Rc::make_mut(object_data)
|
||||
.string_map
|
||||
.insert(subscript.val_to_string(), value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Val::Function(_) => std::panic!("Not implemented: function subscript assignment"),
|
||||
Val::Class(_) => std::panic!("Not implemented: class subscript assignment"),
|
||||
Val::Static(_) => std::panic!("TODO: Exceptions"),
|
||||
Val::Custom(_) => std::panic!("Not implemented"),
|
||||
Val::Function(_) => format_err!("TODO: function subscript assignment"),
|
||||
Val::Class(_) => format_err!("Cannot assign to subscript of class"),
|
||||
Val::Static(_) => format_err!("Cannot assign to subscript of static value"),
|
||||
Val::Custom(_) => format_err!("TODO: Assign to subscript of custom value"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -340,7 +340,11 @@ static INCLUDES: NativeFunction = NativeFunction {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
for elem in &array_data.elements {
|
||||
if op_triple_eq_impl(elem.clone(), search_param.clone()) {
|
||||
let is_eq = op_triple_eq_impl(elem.clone(), search_param.clone())
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Val::Bool(true);
|
||||
}
|
||||
}
|
||||
@@ -359,7 +363,11 @@ static INDEX_OF: NativeFunction = NativeFunction {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
for i in 0..array_data.elements.len() {
|
||||
if op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone()) {
|
||||
let is_eq = op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone())
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Val::Number(i as f64);
|
||||
}
|
||||
}
|
||||
@@ -429,7 +437,11 @@ static LAST_INDEX_OF: NativeFunction = NativeFunction {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
for i in (0..array_data.elements.len()).rev() {
|
||||
if op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone()) {
|
||||
let is_eq = op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone())
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Val::Number(i as f64);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::vs_value::{Val, ValTrait};
|
||||
use super::operations::op_sub;
|
||||
use super::vs_value::{Val, ValTrait};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct VsObject {
|
||||
@@ -14,7 +14,9 @@ impl VsObject {
|
||||
return match self.string_map.get(&key.val_to_string()) {
|
||||
Some(val) => val.clone(),
|
||||
None => match &self.prototype {
|
||||
Some(prototype) => op_sub(prototype.clone(), key),
|
||||
Some(prototype) => op_sub(prototype.clone(), key)
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap(), // TODO: Exception
|
||||
None => Val::Undefined,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -333,11 +333,15 @@ impl ValTrait for Val {
|
||||
|
||||
fn sub(&self, key: Val) -> Val {
|
||||
// TODO: Avoid cloning?
|
||||
return op_sub(self.clone(), key);
|
||||
op_sub(self.clone(), key)
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap() // TODO: Exception
|
||||
}
|
||||
|
||||
fn submov(&mut self, key: Val, value: Val) {
|
||||
op_submov(self, key, value);
|
||||
op_submov(self, key, value)
|
||||
.map_err(|e| e.val_to_string())
|
||||
.unwrap() // TODO: Exception
|
||||
}
|
||||
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
||||
Reference in New Issue
Block a user