diff --git a/valuescript_common/src/builtins.rs b/valuescript_common/src/builtins.rs index b6131d8..a1c4452 100644 --- a/valuescript_common/src/builtins.rs +++ b/valuescript_common/src/builtins.rs @@ -23,6 +23,8 @@ pub enum BuiltinName { parseInt, Error, + TypeError, + RangeError, } pub const BUILTIN_NAMES: [&str; BuiltinName::COUNT] = [ @@ -37,6 +39,8 @@ pub const BUILTIN_NAMES: [&str; BuiltinName::COUNT] = [ "parseFloat", "parseInt", "Error", + "TypeError", + "RangeError", ]; pub const BUILTIN_COUNT: usize = BuiltinName::COUNT; diff --git a/valuescript_vm/src/array_higher_functions/array_mapping_frame.rs b/valuescript_vm/src/array_higher_functions/array_mapping_frame.rs index 8b4ff4a..0735a84 100644 --- a/valuescript_vm/src/array_higher_functions/array_mapping_frame.rs +++ b/valuescript_vm/src/array_higher_functions/array_mapping_frame.rs @@ -1,10 +1,10 @@ use std::rc::Rc; -use crate::format_err; use crate::stack_frame::FrameStepResult; use crate::stack_frame::{CallResult, FrameStepOk, StackFrameTrait}; use crate::vs_array::VsArray; use crate::vs_value::{LoadFunctionResult, Val, ValTrait}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub trait ArrayMappingState { fn process(&mut self, i: usize, element: &Val, mapped: Val) -> Option; @@ -58,7 +58,7 @@ impl StackFrameTrait for ArrayMappingFrame { fn step(&mut self) -> FrameStepResult { let array_data = match &self.this { - None => return format_err!("TypeError: Array fn called on non-array"), + None => return type_error!("Array fn called on non-array"), Some(ad) => ad, }; @@ -79,7 +79,7 @@ impl StackFrameTrait for ArrayMappingFrame { Val::Void => Ok(FrameStepOk::Continue), _ => match self.mapper.load_function() { LoadFunctionResult::NotAFunction => { - format_err!("TypeError: map fn is not a function") + type_error!("map fn is not a function") } LoadFunctionResult::NativeFunction(native_fn) => { match self.state.process( @@ -122,7 +122,7 @@ impl StackFrameTrait for ArrayMappingFrame { let element = match &self.this { None => { - self.early_exit = Some(format_err!("TypeError: Array fn called on non-array")); + self.early_exit = Some(type_error!("Array fn called on non-array")); return; } Some(ad) => &ad.elements[array_i], diff --git a/valuescript_vm/src/array_higher_functions/array_reduce.rs b/valuescript_vm/src/array_higher_functions/array_reduce.rs index 954ba45..1032ecd 100644 --- a/valuescript_vm/src/array_higher_functions/array_reduce.rs +++ b/valuescript_vm/src/array_higher_functions/array_reduce.rs @@ -1,10 +1,10 @@ use std::rc::Rc; -use crate::format_err; use crate::native_frame_function::NativeFrameFunction; use crate::stack_frame::{CallResult, FrameStepOk, FrameStepResult, StackFrameTrait}; use crate::vs_array::VsArray; use crate::vs_value::{LoadFunctionResult, Val, ValTrait}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub static REDUCE: NativeFrameFunction = NativeFrameFunction { make_frame: || { @@ -48,7 +48,7 @@ impl StackFrameTrait for ReduceFrame { fn step(&mut self) -> FrameStepResult { let array_data = match &self.this { - None => return format_err!("TypeError: reduce called on non-array"), + None => return type_error!("reduce called on non-array"), Some(ad) => ad, }; @@ -64,9 +64,7 @@ impl StackFrameTrait for ReduceFrame { FrameStepOk::Continue } Some(value) => match self.reducer.load_function() { - LoadFunctionResult::NotAFunction => { - return format_err!("TypeError: reduce fn is not a function") - } + LoadFunctionResult::NotAFunction => return type_error!("reduce fn is not a function"), LoadFunctionResult::NativeFunction(native_fn) => { self.value = Some(native_fn( &mut Val::Undefined, @@ -91,7 +89,7 @@ impl StackFrameTrait for ReduceFrame { }, }, None => match &self.value { - None => return format_err!("TypeError: reduce of empty array with no initial value"), + None => return type_error!("reduce of empty array with no initial value"), Some(value) => FrameStepOk::Pop(CallResult { return_: value.clone(), this: Val::Array(array_data.clone()), diff --git a/valuescript_vm/src/array_higher_functions/array_reduce_right.rs b/valuescript_vm/src/array_higher_functions/array_reduce_right.rs index 734cbf9..6da6f69 100644 --- a/valuescript_vm/src/array_higher_functions/array_reduce_right.rs +++ b/valuescript_vm/src/array_higher_functions/array_reduce_right.rs @@ -1,10 +1,10 @@ use std::rc::Rc; -use crate::format_err; use crate::native_frame_function::NativeFrameFunction; use crate::stack_frame::{CallResult, FrameStepOk, FrameStepResult, StackFrameTrait}; use crate::vs_array::VsArray; use crate::vs_value::{LoadFunctionResult, Val, ValTrait}; +use crate::{builtins::type_error_builtin::to_type_error, format_err, type_error}; pub static REDUCE_RIGHT: NativeFrameFunction = NativeFrameFunction { make_frame: || { @@ -53,14 +53,14 @@ impl StackFrameTrait for ReduceRightFrame { fn step(&mut self) -> FrameStepResult { let array_data = match &self.this { - None => return format_err!("TypeError: reduceRight called on non-array"), + None => return type_error!("reduceRight called on non-array"), Some(ad) => ad, }; if self.array_i == 0 { match &self.value { None => { - return format_err!("TypeError: reduceRight of empty array with no initial value"); + return type_error!("reduceRight of empty array with no initial value"); } Some(value) => { return Ok(FrameStepOk::Pop(CallResult { diff --git a/valuescript_vm/src/array_higher_functions/array_sort.rs b/valuescript_vm/src/array_higher_functions/array_sort.rs index d527877..c83657e 100644 --- a/valuescript_vm/src/array_higher_functions/array_sort.rs +++ b/valuescript_vm/src/array_higher_functions/array_sort.rs @@ -1,11 +1,11 @@ use std::rc::Rc; -use crate::format_err; use crate::native_frame_function::NativeFrameFunction; use crate::stack_frame::FrameStepResult; use crate::stack_frame::{CallResult, FrameStepOk, StackFrameTrait}; use crate::vs_array::VsArray; use crate::vs_value::{LoadFunctionResult, Val, ValTrait}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub static SORT: NativeFrameFunction = NativeFrameFunction { make_frame: || { @@ -223,7 +223,7 @@ impl StackFrameTrait for SortFrame { fn step(&mut self) -> FrameStepResult { if !self.started { let array_data = match &mut self.this { - None => return format_err!("TypeError: array fn called on non-array"), + None => return type_error!("array fn called on non-array"), Some(ad) => ad, }; @@ -268,7 +268,7 @@ impl StackFrameTrait for SortFrame { }, Some((left, right)) => match self.comparator.load_function() { LoadFunctionResult::NotAFunction => { - return format_err!("TypeError: comparator is not a function"); + return type_error!("comparator is not a function"); } LoadFunctionResult::NativeFunction(native_fn) => { let res = native_fn(&mut Val::Undefined, vec![left, right])?.to_number(); diff --git a/valuescript_vm/src/builtins/array_builtin.rs b/valuescript_vm/src/builtins/array_builtin.rs index 57da0b1..a3e7f68 100644 --- a/valuescript_vm/src/builtins/array_builtin.rs +++ b/valuescript_vm/src/builtins/array_builtin.rs @@ -3,9 +3,12 @@ use std::rc::Rc; use num_bigint::BigInt; use crate::{ + builtins::range_error_builtin::to_range_error, + builtins::type_error_builtin::to_type_error, format_err, native_function::NativeFunction, operations::op_sub, + range_error, type_error, vs_array::VsArray, vs_class::VsClass, vs_object::VsObject, @@ -72,7 +75,7 @@ impl ValTrait for ArrayBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Array builtin") + type_error!("Cannot assign to subscript of Array builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -99,7 +102,7 @@ static IS_ARRAY: NativeFunction = NativeFunction { static FROM: NativeFunction = NativeFunction { fn_: |_this: &mut Val, params: Vec| -> Result { let first_param = match params.get(0) { - None => return format_err!("TypeError: undefined is not iterable"), + None => return type_error!("undefined is not iterable"), Some(p) => p, }; @@ -114,9 +117,7 @@ static FROM: NativeFunction = NativeFunction { .map(|c| Val::String(Rc::new(c.to_string()))) .collect(), ))), - Val::Void | Val::Undefined | Val::Null => { - return format_err!("TypeError: items is not iterable") - } + Val::Void | Val::Undefined | Val::Null => return type_error!("items is not iterable"), Val::Bool(..) | Val::Number(..) | Val::BigInt(..) => Val::Array(Rc::new(VsArray::new())), Val::Object(..) | Val::Function(..) | Val::Class(..) | Val::Static(..) | Val::Custom(..) => { let len = op_sub( @@ -132,7 +133,7 @@ static FROM: NativeFunction = NativeFunction { } if len.is_infinite() { - return format_err!("RangeError: Invalid array length"); + return range_error!("Invalid array length"); } let len = len as usize; @@ -169,7 +170,7 @@ fn to_array(_: &mut Val, params: Vec) -> Result { Ok(match params[0] { Val::Number(number) => { if number.is_sign_negative() || number != number.floor() { - return format_err!("RangeError: Invalid array length"); + return range_error!("Invalid array length"); } let len = number as usize; diff --git a/valuescript_vm/src/builtins/boolean_builtin.rs b/valuescript_vm/src/builtins/boolean_builtin.rs index 4934fe7..6f47a12 100644 --- a/valuescript_vm/src/builtins/boolean_builtin.rs +++ b/valuescript_vm/src/builtins/boolean_builtin.rs @@ -3,7 +3,8 @@ use std::rc::Rc; use num_bigint::BigInt; use crate::{ - format_err, + builtins::type_error_builtin::to_type_error, + type_error, vs_array::VsArray, vs_class::VsClass, vs_object::VsObject, @@ -65,7 +66,7 @@ impl ValTrait for BooleanBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Boolean builtin") + type_error!("Cannot assign to subscript of Boolean builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/debug_builtin.rs b/valuescript_vm/src/builtins/debug_builtin.rs index 70c59bb..e9fa8bc 100644 --- a/valuescript_vm/src/builtins/debug_builtin.rs +++ b/valuescript_vm/src/builtins/debug_builtin.rs @@ -2,12 +2,12 @@ use std::rc::Rc; use num_bigint::BigInt; -use crate::format_err; use crate::native_function::NativeFunction; use crate::vs_array::VsArray; use crate::vs_class::VsClass; use crate::vs_object::VsObject; use crate::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub struct DebugBuiltin {} @@ -69,7 +69,7 @@ impl ValTrait for DebugBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Debug builtin") + type_error!("Cannot assign to subscript of Debug builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/error_builtin.rs b/valuescript_vm/src/builtins/error_builtin.rs index cc51dc0..7213b7b 100644 --- a/valuescript_vm/src/builtins/error_builtin.rs +++ b/valuescript_vm/src/builtins/error_builtin.rs @@ -2,8 +2,9 @@ use std::{collections::BTreeMap, rc::Rc}; use num_bigint::BigInt; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; use crate::{ - format_err, format_val, + format_val, native_function::NativeFunction, operations::{op_sub, op_submov}, vs_array::VsArray, @@ -70,7 +71,7 @@ impl ValTrait for ErrorBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Error builtin") + type_error!("Cannot assign to subscript of Error builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -125,6 +126,6 @@ static SET_MESSAGE: NativeFunction = NativeFunction { static ERROR_TO_STRING: NativeFunction = NativeFunction { fn_: |this: &mut Val, _params: Vec| -> Result { let message = op_sub(this.clone(), format_val!("message"))?; - Ok(format_val!("Error({})", message)) + Ok(format_val!("Error({})", message)) // TODO: Fixes needed here (and other errors) }, }; diff --git a/valuescript_vm/src/builtins/math_builtin.rs b/valuescript_vm/src/builtins/math_builtin.rs index 758dd69..726ed79 100644 --- a/valuescript_vm/src/builtins/math_builtin.rs +++ b/valuescript_vm/src/builtins/math_builtin.rs @@ -2,13 +2,13 @@ use std::rc::Rc; use num_bigint::BigInt; -use crate::format_err; use crate::native_function::NativeFunction; use crate::operations::to_u32; use crate::vs_array::VsArray; use crate::vs_class::VsClass; use crate::vs_object::VsObject; use crate::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub struct MathBuiltin {} @@ -114,7 +114,7 @@ impl ValTrait for MathBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Math builtin") + type_error!("Cannot assign to subscript of Math builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/mod.rs b/valuescript_vm/src/builtins/mod.rs index 55afb02..b3528ed 100644 --- a/valuescript_vm/src/builtins/mod.rs +++ b/valuescript_vm/src/builtins/mod.rs @@ -4,7 +4,9 @@ mod debug_builtin; mod error_builtin; mod math_builtin; mod number_builtin; +pub mod range_error_builtin; mod string_builtin; +pub mod type_error_builtin; use valuescript_common::BUILTIN_COUNT; @@ -22,4 +24,6 @@ pub static BUILTIN_VALS: [&'static (dyn ValTrait + Sync); BUILTIN_COUNT] = [ &number_builtin::PARSE_FLOAT, &number_builtin::PARSE_INT, &error_builtin::ERROR_BUILTIN, + &type_error_builtin::TYPE_ERROR_BUILTIN, + &range_error_builtin::RANGE_ERROR_BUILTIN, ]; diff --git a/valuescript_vm/src/builtins/number_builtin.rs b/valuescript_vm/src/builtins/number_builtin.rs index 2673d3d..5de4c59 100644 --- a/valuescript_vm/src/builtins/number_builtin.rs +++ b/valuescript_vm/src/builtins/number_builtin.rs @@ -2,8 +2,8 @@ use std::rc::Rc; use num_bigint::BigInt; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; use crate::{ - format_err, native_function::NativeFunction, vs_array::VsArray, vs_class::VsClass, @@ -82,7 +82,7 @@ impl ValTrait for NumberBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Number builtin") + type_error!("Cannot assign to subscript of Number builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/builtins/range_error_builtin.rs b/valuescript_vm/src/builtins/range_error_builtin.rs new file mode 100644 index 0000000..45b01d6 --- /dev/null +++ b/valuescript_vm/src/builtins/range_error_builtin.rs @@ -0,0 +1,144 @@ +use std::{collections::BTreeMap, rc::Rc}; + +use num_bigint::BigInt; + +use crate::{builtins::type_error_builtin::to_type_error, type_error}; +use crate::{ + format_val, + native_function::NativeFunction, + operations::{op_sub, op_submov}, + vs_array::VsArray, + vs_class::VsClass, + vs_object::VsObject, + vs_value::{LoadFunctionResult, Val, VsType}, + ValTrait, +}; + +pub struct RangeErrorBuiltin {} + +pub static RANGE_ERROR_BUILTIN: RangeErrorBuiltin = RangeErrorBuiltin {}; + +impl ValTrait for RangeErrorBuiltin { + fn typeof_(&self) -> VsType { + VsType::Object + } + fn val_to_string(&self) -> String { + "function RangeError() { [native code] }".to_string() + } + fn to_number(&self) -> f64 { + core::f64::NAN + } + fn to_index(&self) -> Option { + None + } + fn is_primitive(&self) -> bool { + false + } + fn to_primitive(&self) -> Val { + Val::String(Rc::new( + "function RangeError() { [native code] }".to_string(), + )) + } + fn is_truthy(&self) -> bool { + true + } + fn is_nullish(&self) -> bool { + false + } + fn bind(&self, _params: Vec) -> Option { + None + } + fn as_bigint_data(&self) -> Option { + None + } + fn as_array_data(&self) -> Option> { + None + } + fn as_object_data(&self) -> Option> { + None + } + fn as_class_data(&self) -> Option> { + Some(Rc::new(VsClass { + constructor: Val::Static(&SET_MESSAGE), + instance_prototype: make_range_error_prototype(), + })) + } + + fn load_function(&self) -> LoadFunctionResult { + LoadFunctionResult::NativeFunction(to_range_error) + } + + fn sub(&self, _key: Val) -> Result { + Ok(Val::Undefined) + } + + fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { + type_error!("Cannot assign to subscript of RangeError builtin") + } + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\x1b[36m[RangeError]\x1b[39m") + } + + fn codify(&self) -> String { + "RangeError".into() + } +} + +pub fn to_range_error(_: &mut Val, params: Vec) -> Result { + Ok(Val::Object(Rc::new(VsObject { + string_map: BTreeMap::from([( + "message".to_string(), + Val::String(Rc::new(match params.get(0) { + Some(param) => param.val_to_string(), + None => "".to_string(), + })), + )]), + prototype: Some(make_range_error_prototype()), + }))) +} + +// TODO: Static? (Rc -> Arc?) +fn make_range_error_prototype() -> Val { + Val::Object(Rc::new(VsObject { + string_map: BTreeMap::from([ + ( + "name".to_string(), + Val::String(Rc::new("RangeError".to_string())), + ), + ("toString".to_string(), Val::Static(&RANGE_ERROR_TO_STRING)), + ]), + prototype: None, + })) +} + +static SET_MESSAGE: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Result { + let message = match params.get(0) { + Some(param) => param.val_to_string(), + None => "".to_string(), + }; + + op_submov(this, format_val!("message"), format_val!("{}", message))?; + + Ok(Val::Undefined) + }, +}; + +static RANGE_ERROR_TO_STRING: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Result { + let message = op_sub(this.clone(), format_val!("message"))?; + Ok(format_val!("RangeError({})", message)) + }, +}; + +#[macro_export] +macro_rules! range_error { + ($fmt:expr $(, $($arg:expr),*)?) => {{ + let mut this = Val::Undefined; + let formatted_string = format!($fmt $(, $($arg),*)?); + Err(to_range_error( + &mut this, vec![Val::String(Rc::new(formatted_string))] + ).unwrap()) + }}; +} diff --git a/valuescript_vm/src/builtins/string_builtin.rs b/valuescript_vm/src/builtins/string_builtin.rs index f1a0c5f..f5376b0 100644 --- a/valuescript_vm/src/builtins/string_builtin.rs +++ b/valuescript_vm/src/builtins/string_builtin.rs @@ -2,8 +2,9 @@ use std::rc::Rc; use num_bigint::BigInt; +use crate::{builtins::range_error_builtin::to_range_error, range_error}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; use crate::{ - format_err, native_function::NativeFunction, vs_array::VsArray, vs_class::VsClass, @@ -74,7 +75,7 @@ impl ValTrait for StringBuiltin { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of String builtin") + type_error!("Cannot assign to subscript of String builtin") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -95,7 +96,7 @@ static FROM_CODE_POINT: NativeFunction = NativeFunction { let char = match std::char::from_u32(code_point) { Some(c) => c, - None => return format_err!("RangeError: Invalid code point"), + None => return range_error!("Invalid code point"), }; result.push(char); diff --git a/valuescript_vm/src/builtins/type_error_builtin.rs b/valuescript_vm/src/builtins/type_error_builtin.rs new file mode 100644 index 0000000..e59b8b7 --- /dev/null +++ b/valuescript_vm/src/builtins/type_error_builtin.rs @@ -0,0 +1,144 @@ +use std::{collections::BTreeMap, rc::Rc}; + +use num_bigint::BigInt; + +use crate::type_error; +use crate::{ + format_val, + native_function::NativeFunction, + operations::{op_sub, op_submov}, + vs_array::VsArray, + vs_class::VsClass, + vs_object::VsObject, + vs_value::{LoadFunctionResult, Val, VsType}, + ValTrait, +}; + +pub struct TypeErrorBuiltin {} + +pub static TYPE_ERROR_BUILTIN: TypeErrorBuiltin = TypeErrorBuiltin {}; + +impl ValTrait for TypeErrorBuiltin { + fn typeof_(&self) -> VsType { + VsType::Object + } + fn val_to_string(&self) -> String { + "function TypeError() { [native code] }".to_string() + } + fn to_number(&self) -> f64 { + core::f64::NAN + } + fn to_index(&self) -> Option { + None + } + fn is_primitive(&self) -> bool { + false + } + fn to_primitive(&self) -> Val { + Val::String(Rc::new( + "function TypeError() { [native code] }".to_string(), + )) + } + fn is_truthy(&self) -> bool { + true + } + fn is_nullish(&self) -> bool { + false + } + fn bind(&self, _params: Vec) -> Option { + None + } + fn as_bigint_data(&self) -> Option { + None + } + fn as_array_data(&self) -> Option> { + None + } + fn as_object_data(&self) -> Option> { + None + } + fn as_class_data(&self) -> Option> { + Some(Rc::new(VsClass { + constructor: Val::Static(&SET_MESSAGE), + instance_prototype: make_type_error_prototype(), + })) + } + + fn load_function(&self) -> LoadFunctionResult { + LoadFunctionResult::NativeFunction(to_type_error) + } + + fn sub(&self, _key: Val) -> Result { + Ok(Val::Undefined) + } + + fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { + type_error!("Cannot assign to subscript of TypeError builtin") + } + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\x1b[36m[TypeError]\x1b[39m") + } + + fn codify(&self) -> String { + "TypeError".into() + } +} + +pub fn to_type_error(_: &mut Val, params: Vec) -> Result { + Ok(Val::Object(Rc::new(VsObject { + string_map: BTreeMap::from([( + "message".to_string(), + Val::String(Rc::new(match params.get(0) { + Some(param) => param.val_to_string(), + None => "".to_string(), + })), + )]), + prototype: Some(make_type_error_prototype()), + }))) +} + +// TODO: Static? (Rc -> Arc?) +fn make_type_error_prototype() -> Val { + Val::Object(Rc::new(VsObject { + string_map: BTreeMap::from([ + ( + "name".to_string(), + Val::String(Rc::new("TypeError".to_string())), + ), + ("toString".to_string(), Val::Static(&TYPE_ERROR_TO_STRING)), + ]), + prototype: None, + })) +} + +static SET_MESSAGE: NativeFunction = NativeFunction { + fn_: |this: &mut Val, params: Vec| -> Result { + let message = match params.get(0) { + Some(param) => param.val_to_string(), + None => "".to_string(), + }; + + op_submov(this, format_val!("message"), format_val!("{}", message))?; + + Ok(Val::Undefined) + }, +}; + +static TYPE_ERROR_TO_STRING: NativeFunction = NativeFunction { + fn_: |this: &mut Val, _params: Vec| -> Result { + let message = op_sub(this.clone(), format_val!("message"))?; + Ok(format_val!("TypeError({})", message)) + }, +}; + +#[macro_export] +macro_rules! type_error { + ($fmt:expr $(, $($arg:expr),*)?) => {{ + let mut this = Val::Undefined; + let formatted_string = format!($fmt $(, $($arg),*)?); + Err(to_type_error( + &mut this, vec![Val::String(Rc::new(formatted_string))] + ).unwrap()) + }}; +} diff --git a/valuescript_vm/src/bytecode_stack_frame.rs b/valuescript_vm/src/bytecode_stack_frame.rs index 5c8ca86..ecf45c1 100644 --- a/valuescript_vm/src/bytecode_stack_frame.rs +++ b/valuescript_vm/src/bytecode_stack_frame.rs @@ -2,13 +2,14 @@ use std::rc::Rc; use valuescript_common::InstructionByte; +use crate::builtins::type_error_builtin::to_type_error; use crate::bytecode_decoder::BytecodeDecoder; use crate::bytecode_decoder::BytecodeType; -use crate::format_val; use crate::operations; use crate::stack_frame::FrameStepOk; use crate::stack_frame::FrameStepResult; use crate::stack_frame::{CallResult, StackFrame, StackFrameTrait}; +use crate::type_error; use crate::vs_object::VsObject; use crate::vs_value::{LoadFunctionResult, Val, ValTrait}; @@ -345,11 +346,12 @@ impl StackFrameTrait for BytecodeStackFrame { New => { // TODO: new Array - let class = self - .decoder - .decode_val(&self.registers) - .as_class_data() - .ok_or(format_val!("TypeError: value is not a constructor"))?; + let class = match self.decoder.decode_val(&self.registers).as_class_data() { + Some(class) => class, + None => { + return type_error!("value is not a constructor"); + } + }; let mut instance = Val::Object(Rc::new(VsObject { string_map: Default::default(), diff --git a/valuescript_vm/src/native_frame_function.rs b/valuescript_vm/src/native_frame_function.rs index f0ddd57..c30e936 100644 --- a/valuescript_vm/src/native_frame_function.rs +++ b/valuescript_vm/src/native_frame_function.rs @@ -3,12 +3,12 @@ use std::rc::Rc; use num_bigint::BigInt; use crate::format_err; - -use super::stack_frame::StackFrame; -use super::vs_array::VsArray; -use super::vs_class::VsClass; -use super::vs_object::VsObject; -use super::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::stack_frame::StackFrame; +use crate::vs_array::VsArray; +use crate::vs_class::VsClass; +use crate::vs_object::VsObject; +use crate::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub struct NativeFrameFunction { pub make_frame: fn() -> StackFrame, @@ -66,7 +66,7 @@ impl ValTrait for NativeFrameFunction { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of native function") + type_error!("Cannot assign to subscript of native function") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/native_function.rs b/valuescript_vm/src/native_function.rs index e644914..b4cf259 100644 --- a/valuescript_vm/src/native_function.rs +++ b/valuescript_vm/src/native_function.rs @@ -3,11 +3,11 @@ use std::rc::Rc; use num_bigint::BigInt; use crate::format_err; - -use super::vs_array::VsArray; -use super::vs_class::VsClass; -use super::vs_object::VsObject; -use super::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::vs_array::VsArray; +use crate::vs_class::VsClass; +use crate::vs_object::VsObject; +use crate::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub struct NativeFunction { pub fn_: fn(this: &mut Val, params: Vec) -> Result, @@ -65,7 +65,7 @@ impl ValTrait for NativeFunction { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of native function") + type_error!("Cannot assign to subscript of native function") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/number_methods.rs b/valuescript_vm/src/number_methods.rs index 9e6c1c3..8a41fcb 100644 --- a/valuescript_vm/src/number_methods.rs +++ b/valuescript_vm/src/number_methods.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +use crate::{builtins::range_error_builtin::to_range_error, range_error}; use crate::{ format_err, format_val, native_function::NativeFunction, @@ -39,7 +40,7 @@ static TO_FIXED: NativeFunction = NativeFunction { precision = f64::floor(precision); if precision < 1.0 || precision > 100.0 { - return format_err!("RangeError: precision must be between 1 and 100"); + return range_error!("precision must be between 1 and 100"); } format_val!("{:.*}", precision as usize, number) @@ -58,7 +59,7 @@ static TO_EXPONENTIAL: NativeFunction = NativeFunction { precision = f64::floor(precision); if precision < 0.0 || precision > 100.0 { - return format_err!("RangeError: precision must be between 0 and 100"); + return range_error!("precision must be between 0 and 100"); } format_exponential(*number, Some(precision as usize)) diff --git a/valuescript_vm/src/operations.rs b/valuescript_vm/src/operations.rs index 328d323..c079a03 100644 --- a/valuescript_vm/src/operations.rs +++ b/valuescript_vm/src/operations.rs @@ -9,10 +9,11 @@ use crate::format_val; use crate::native_function::NativeFunction; use crate::number_methods::op_sub_number; use crate::string_methods::op_sub_string; - -use super::vs_value::Val; -use super::vs_value::ValTrait; -use super::vs_value::VsType; +use crate::vs_value::Val; +use crate::vs_value::ValTrait; +use crate::vs_value::VsType; +use crate::{builtins::range_error_builtin::to_range_error, range_error}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; pub fn op_plus(left: Val, right: Val) -> Result { let left_prim = left.to_primitive(); @@ -73,7 +74,7 @@ pub fn op_mul(left: Val, right: Val) -> Result { 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(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => Ok(Val::Number(left.to_number() * right.to_number())), } @@ -83,7 +84,7 @@ pub fn op_div(left: Val, right: Val) -> Result { 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(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => Ok(Val::Number(left.to_number() / right.to_number())), } @@ -93,7 +94,7 @@ pub fn op_mod(left: Val, right: Val) -> Result { 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(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => Ok(Val::Number(left.to_number() % right.to_number())), } @@ -103,18 +104,18 @@ pub fn op_exp(left: Val, right: Val) -> Result { match (left.as_bigint_data(), right.as_bigint_data()) { (Some(left_bigint), Some(right_bigint)) => { if right_bigint.sign() == Sign::Minus { - return format_err!("RangeError: Exponent must be non-negative"); + return range_error!("Exponent must be non-negative"); } let exp = match right_bigint.to_u32() { Some(exp) => exp, - None => return format_err!("RangeError: Exponent must be less than 2^32"), + None => return range_error!("Exponent must be less than 2^32"), }; Ok(Val::BigInt(left_bigint.pow(exp))) } (Some(_), None) | (None, Some(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => Ok(Val::Number(left.to_number().powf(right.to_number()))), } @@ -274,7 +275,7 @@ pub fn op_bit_and(left: Val, right: Val) -> Result { 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(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => { let res_i32 = to_i32(left.to_number()) & to_i32(right.to_number()); @@ -287,7 +288,7 @@ pub fn op_bit_or(left: Val, right: Val) -> Result { 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(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => { let res_i32 = to_i32(left.to_number()) | to_i32(right.to_number()); @@ -310,7 +311,7 @@ pub fn op_bit_xor(left: Val, right: Val) -> Result { 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(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => { let res_i32 = to_i32(left.to_number()) ^ to_i32(right.to_number()); @@ -325,7 +326,7 @@ pub fn op_left_shift(left: Val, right: Val) -> Result { left_bigint << right_bigint.to_i64().expect("TODO"), )), (Some(_), None) | (None, Some(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => { let res_i32 = to_i32(left.to_number()) << (to_u32(right.to_number()) & 0x1f); @@ -341,7 +342,7 @@ pub fn op_right_shift(left: Val, right: Val) -> Result { Ok(Val::BigInt(left_bigint >> right_i64)) } (Some(_), None) | (None, Some(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => { let res_i32 = to_i32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f); @@ -353,10 +354,10 @@ pub fn op_right_shift(left: Val, right: Val) -> Result { pub fn op_right_shift_unsigned(left: Val, right: Val) -> Result { match (left.as_bigint_data(), right.as_bigint_data()) { (Some(_), Some(_)) => { - format_err!("TypeError: BigInts don't support unsigned right shift") + type_error!("BigInts don't support unsigned right shift") } (Some(_), None) | (None, Some(_)) => { - format_err!("TypeError: Cannot mix BigInt with other types") + type_error!("Cannot mix BigInt with other types") } _ => { let res_u32 = to_u32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f); @@ -393,8 +394,8 @@ pub fn op_in(_left: Val, _right: Val) -> Result { pub fn op_sub(left: Val, right: Val) -> Result { return match left { 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::Undefined => type_error!("Cannot subscript undefined"), + Val::Null => type_error!("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), diff --git a/valuescript_vm/src/vs_array.rs b/valuescript_vm/src/vs_array.rs index 46c4249..0d4ec35 100644 --- a/valuescript_vm/src/vs_array.rs +++ b/valuescript_vm/src/vs_array.rs @@ -15,6 +15,7 @@ use crate::operations::op_triple_eq_impl; use crate::vs_class::VsClass; use crate::vs_object::VsObject; use crate::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; +use crate::{builtins::type_error_builtin::to_type_error, type_error}; #[derive(Clone, Debug)] pub struct VsArray { @@ -135,7 +136,7 @@ impl ValTrait for ArrayPrototype { } fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { - format_err!("TypeError: Cannot assign to subscript of Array.prototype") + type_error!("Cannot assign to subscript of Array.prototype") } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/valuescript_vm/src/vs_value.rs b/valuescript_vm/src/vs_value.rs index 59e3f84..f32fc59 100644 --- a/valuescript_vm/src/vs_value.rs +++ b/valuescript_vm/src/vs_value.rs @@ -391,7 +391,7 @@ impl ValTrait for Val { match op_sub(proto.clone(), format_val!("name")) { Ok(name) => { if name.typeof_() == VsType::String { - res += format!("{} ", name.val_to_string()).as_str(); + res += format!("{}", name.val_to_string()).as_str(); } } Err(_) => {}