From e657604cbecc478f37b1b11732147d85bda7d6f0 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Tue, 21 Mar 2023 16:06:53 +1100 Subject: [PATCH] Error builtin --- valuescript_common/src/builtins.rs | 3 + valuescript_vm/src/builtins/error_builtin.rs | 130 +++++++++++++++++++ valuescript_vm/src/builtins/mod.rs | 2 + valuescript_vm/src/bytecode_stack_frame.rs | 3 +- valuescript_vm/src/vs_array.rs | 2 +- valuescript_vm/src/vs_class.rs | 1 + valuescript_vm/src/vs_function.rs | 5 +- valuescript_vm/src/vs_object.rs | 2 +- valuescript_vm/src/vs_value.rs | 57 ++++++-- 9 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 valuescript_vm/src/builtins/error_builtin.rs diff --git a/valuescript_common/src/builtins.rs b/valuescript_common/src/builtins.rs index 3a2e827..b6131d8 100644 --- a/valuescript_common/src/builtins.rs +++ b/valuescript_common/src/builtins.rs @@ -21,6 +21,8 @@ pub enum BuiltinName { #[allow(non_camel_case_types)] parseInt, + + Error, } pub const BUILTIN_NAMES: [&str; BuiltinName::COUNT] = [ @@ -34,6 +36,7 @@ pub const BUILTIN_NAMES: [&str; BuiltinName::COUNT] = [ "isNaN", "parseFloat", "parseInt", + "Error", ]; pub const BUILTIN_COUNT: usize = BuiltinName::COUNT; diff --git a/valuescript_vm/src/builtins/error_builtin.rs b/valuescript_vm/src/builtins/error_builtin.rs new file mode 100644 index 0000000..cc51dc0 --- /dev/null +++ b/valuescript_vm/src/builtins/error_builtin.rs @@ -0,0 +1,130 @@ +use std::{collections::BTreeMap, rc::Rc}; + +use num_bigint::BigInt; + +use crate::{ + format_err, 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 ErrorBuiltin {} + +pub static ERROR_BUILTIN: ErrorBuiltin = ErrorBuiltin {}; + +impl ValTrait for ErrorBuiltin { + fn typeof_(&self) -> VsType { + VsType::Object + } + fn val_to_string(&self) -> String { + "function Error() { [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 Error() { [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_error_prototype(), + })) + } + + fn load_function(&self) -> LoadFunctionResult { + LoadFunctionResult::NativeFunction(to_error) + } + + fn sub(&self, _key: Val) -> Result { + Ok(Val::Undefined) + } + + fn submov(&mut self, _key: Val, _value: Val) -> Result<(), Val> { + format_err!("TypeError: Cannot assign to subscript of Error builtin") + } + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\x1b[36m[Error]\x1b[39m") + } + + fn codify(&self) -> String { + "Error".into() + } +} + +fn to_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_error_prototype()), + }))) +} + +// TODO: Static? (Rc -> Arc?) +fn make_error_prototype() -> Val { + Val::Object(Rc::new(VsObject { + string_map: BTreeMap::from([ + ( + "name".to_string(), + Val::String(Rc::new("Error".to_string())), + ), + ("toString".to_string(), Val::Static(&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 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)) + }, +}; diff --git a/valuescript_vm/src/builtins/mod.rs b/valuescript_vm/src/builtins/mod.rs index 041ecd7..55afb02 100644 --- a/valuescript_vm/src/builtins/mod.rs +++ b/valuescript_vm/src/builtins/mod.rs @@ -1,6 +1,7 @@ mod array_builtin; mod boolean_builtin; mod debug_builtin; +mod error_builtin; mod math_builtin; mod number_builtin; mod string_builtin; @@ -20,4 +21,5 @@ pub static BUILTIN_VALS: [&'static (dyn ValTrait + Sync); BUILTIN_COUNT] = [ &number_builtin::IS_NAN, &number_builtin::PARSE_FLOAT, &number_builtin::PARSE_INT, + &error_builtin::ERROR_BUILTIN, ]; diff --git a/valuescript_vm/src/bytecode_stack_frame.rs b/valuescript_vm/src/bytecode_stack_frame.rs index 0f74473..5c8ca86 100644 --- a/valuescript_vm/src/bytecode_stack_frame.rs +++ b/valuescript_vm/src/bytecode_stack_frame.rs @@ -4,6 +4,7 @@ use valuescript_common::InstructionByte; 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; @@ -348,7 +349,7 @@ impl StackFrameTrait for BytecodeStackFrame { .decoder .decode_val(&self.registers) .as_class_data() - .expect("Not implemented: throw exception (not constructible)"); + .ok_or(format_val!("TypeError: value is not a constructor"))?; let mut instance = Val::Object(Rc::new(VsObject { string_map: Default::default(), diff --git a/valuescript_vm/src/vs_array.rs b/valuescript_vm/src/vs_array.rs index 54adb2a..46c4249 100644 --- a/valuescript_vm/src/vs_array.rs +++ b/valuescript_vm/src/vs_array.rs @@ -16,7 +16,7 @@ use crate::vs_class::VsClass; use crate::vs_object::VsObject; use crate::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VsArray { pub elements: Vec, pub object: VsObject, diff --git a/valuescript_vm/src/vs_class.rs b/valuescript_vm/src/vs_class.rs index 80178ec..6d11e7f 100644 --- a/valuescript_vm/src/vs_class.rs +++ b/valuescript_vm/src/vs_class.rs @@ -1,5 +1,6 @@ use super::vs_value::Val; +#[derive(Debug)] pub struct VsClass { pub constructor: Val, pub instance_prototype: Val, diff --git a/valuescript_vm/src/vs_function.rs b/valuescript_vm/src/vs_function.rs index 6bf9e0d..7f6c3d2 100644 --- a/valuescript_vm/src/vs_function.rs +++ b/valuescript_vm/src/vs_function.rs @@ -1,10 +1,11 @@ use std::rc::Rc; -use super::vs_value::Val; -use super::bytecode_stack_frame::BytecodeStackFrame; use super::bytecode_decoder::BytecodeDecoder; +use super::bytecode_stack_frame::BytecodeStackFrame; use super::stack_frame::StackFrame; +use super::vs_value::Val; +#[derive(Debug)] pub struct VsFunction { pub bytecode: Rc>, pub register_count: usize, diff --git a/valuescript_vm/src/vs_object.rs b/valuescript_vm/src/vs_object.rs index cbdec17..76611eb 100644 --- a/valuescript_vm/src/vs_object.rs +++ b/valuescript_vm/src/vs_object.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use super::operations::op_sub; use super::vs_value::{Val, ValTrait}; -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct VsObject { pub string_map: BTreeMap, pub prototype: Option, diff --git a/valuescript_vm/src/vs_value.rs b/valuescript_vm/src/vs_value.rs index ef37d05..59e3f84 100644 --- a/valuescript_vm/src/vs_value.rs +++ b/valuescript_vm/src/vs_value.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::rc::Rc; use std::str::FromStr; @@ -5,14 +6,15 @@ use num_bigint::BigInt; use num_traits::cast::ToPrimitive; use num_traits::Zero; -use super::operations::{op_sub, op_submov}; -use super::stack_frame::StackFrame; -use super::vs_array::VsArray; -use super::vs_class::VsClass; -use super::vs_function::VsFunction; -use super::vs_object::VsObject; +use crate::format_val; +use crate::operations::{op_sub, op_submov}; +use crate::stack_frame::StackFrame; +use crate::vs_array::VsArray; +use crate::vs_class::VsClass; +use crate::vs_function::VsFunction; +use crate::vs_object::VsObject; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Val { Void, Undefined, @@ -75,6 +77,14 @@ pub trait ValTrait { fn codify(&self) -> String; } +impl fmt::Debug for dyn ValTrait { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(dyn ValTrait)(")?; + self.fmt(f)?; + write!(f, ")") + } +} + impl ValTrait for Val { fn typeof_(&self) -> VsType { use Val::*; @@ -313,6 +323,7 @@ impl ValTrait for Val { return match self { Class(class) => Some(class.clone()), + Static(s) => s.as_class_data(), Custom(val) => val.as_class_data(), _ => None, @@ -374,11 +385,26 @@ impl ValTrait for Val { } } Val::Object(object) => { - if object.string_map.len() == 0 { - return "{}".into(); + let mut res = String::new(); + + if let Some(proto) = &object.prototype { + match op_sub(proto.clone(), format_val!("name")) { + Ok(name) => { + if name.typeof_() == VsType::String { + res += format!("{} ", name.val_to_string()).as_str(); + } + } + Err(_) => {} + } } - let mut res = "{".into(); + if object.string_map.len() == 0 { + res += "{}"; + return res; + } + + res += "{"; + let mut first = true; for (k, v) in &object.string_map { @@ -437,6 +463,17 @@ impl std::fmt::Display for Val { write!(f, " ]") } Val::Object(object) => { + if let Some(proto) = &object.prototype { + match op_sub(proto.clone(), format_val!("name")) { + Ok(name) => { + if name.typeof_() == VsType::String { + write!(f, "{} ", name.val_to_string())?; + } + } + Err(_) => {} + } + } + if object.string_map.len() == 0 { return f.write_str("{}"); }