diff --git a/valuescript_compiler/src/function_compiler.rs b/valuescript_compiler/src/function_compiler.rs index 2285ecb..ef11ccc 100644 --- a/valuescript_compiler/src/function_compiler.rs +++ b/valuescript_compiler/src/function_compiler.rs @@ -1020,7 +1020,7 @@ impl FunctionCompiler { ec.compile(&for_of.right, Some(iter_reg.clone())); - ec.fnc.push(Instruction::SubCall( + ec.fnc.push(Instruction::ConstSubCall( Value::Register(iter_reg.clone()), Value::Builtin(Builtin { name: "SymbolIterator".to_string(), diff --git a/valuescript_vm/src/builtins/array_builtin.rs b/valuescript_vm/src/builtins/array_builtin.rs index db34061..e1b1a6c 100644 --- a/valuescript_vm/src/builtins/array_builtin.rs +++ b/valuescript_vm/src/builtins/array_builtin.rs @@ -2,7 +2,6 @@ use std::{fmt, rc::Rc}; use crate::{ native_function::{native_fn, NativeFunction, ThisWrapper}, - operations::op_sub, vs_array::VsArray, vs_class::VsClass, vs_value::{LoadFunctionResult, ToVal, Val}, @@ -70,7 +69,8 @@ static FROM: NativeFunction = native_fn(|_this, params| { Val::Void | Val::Undefined | Val::Null => return Err("items is not iterable".to_type_error()), Val::Bool(..) | Val::Number(..) | Val::BigInt(..) | Val::Symbol(..) => VsArray::new().to_val(), Val::Object(..) | Val::Function(..) | Val::Class(..) | Val::Static(..) | Val::Dynamic(..) => { - let len = op_sub(first_param.clone(), "length".to_val()) + let len = first_param + .sub("length".to_val()) .map_err(|e| e.to_string()) .unwrap() // TODO: Exception .to_number(); @@ -88,10 +88,10 @@ static FROM: NativeFunction = native_fn(|_this, params| { let mut arr = Vec::with_capacity(len); // 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)) + first_param + .sub((i as f64).to_val()) .map_err(|e| e.to_string()) .unwrap(), // TODO: Exception ); diff --git a/valuescript_vm/src/builtins/error_builtin.rs b/valuescript_vm/src/builtins/error_builtin.rs index 23c1739..496691a 100644 --- a/valuescript_vm/src/builtins/error_builtin.rs +++ b/valuescript_vm/src/builtins/error_builtin.rs @@ -5,9 +5,10 @@ use std::rc::Rc; use crate::native_function::{native_fn, ThisWrapper}; use crate::vs_class::VsClass; use crate::vs_value::ToVal; +use crate::ValTrait; use crate::{ native_function::NativeFunction, - operations::{op_sub, op_submov}, + operations::op_submov, vs_object::VsObject, vs_value::{LoadFunctionResult, Val}, }; @@ -103,6 +104,6 @@ static SET_MESSAGE: NativeFunction = native_fn(|mut this, params| { }); static ERROR_TO_STRING: NativeFunction = native_fn(|this, _params| { - let message = op_sub(this.get().clone(), "message".to_val())?; + let message = this.get().sub("message".to_val())?; Ok(format!("Error({})", message).to_val()) // TODO: Fixes needed here (and other errors) }); diff --git a/valuescript_vm/src/builtins/mod.rs b/valuescript_vm/src/builtins/mod.rs index a439c0d..b6ff663 100644 --- a/valuescript_vm/src/builtins/mod.rs +++ b/valuescript_vm/src/builtins/mod.rs @@ -39,5 +39,5 @@ pub static BUILTIN_VALS: [fn() -> Val; BUILTIN_COUNT] = [ || TypeErrorBuiltin {}.to_val(), || RangeErrorBuiltin {}.to_val(), || SymbolBuiltin {}.to_val(), - || Val::Symbol(VsSymbol::ITERATOR), + || VsSymbol::ITERATOR.to_val(), ]; diff --git a/valuescript_vm/src/builtins/range_error_builtin.rs b/valuescript_vm/src/builtins/range_error_builtin.rs index dd1f042..4e4205c 100644 --- a/valuescript_vm/src/builtins/range_error_builtin.rs +++ b/valuescript_vm/src/builtins/range_error_builtin.rs @@ -3,9 +3,10 @@ use std::{collections::BTreeMap, rc::Rc}; use crate::native_function::{native_fn, ThisWrapper}; use crate::vs_value::ToVal; +use crate::ValTrait; use crate::{ native_function::NativeFunction, - operations::{op_sub, op_submov}, + operations::op_submov, vs_class::VsClass, vs_object::VsObject, vs_value::{LoadFunctionResult, Val}, @@ -84,7 +85,7 @@ pub fn to_range_error(_: ThisWrapper, params: Vec) -> Result { } static RANGE_ERROR_TO_STRING: NativeFunction = native_fn(|this, _params| { - let message = op_sub(this.get().clone(), "message".to_val())?; + let message = this.get().sub("message".to_val())?; Ok(format!("RangeError({})", message).to_val()) }); diff --git a/valuescript_vm/src/builtins/symbol_builtin.rs b/valuescript_vm/src/builtins/symbol_builtin.rs index a730e4a..8774141 100644 --- a/valuescript_vm/src/builtins/symbol_builtin.rs +++ b/valuescript_vm/src/builtins/symbol_builtin.rs @@ -3,7 +3,7 @@ use std::{fmt, rc::Rc}; use crate::{ vs_class::VsClass, vs_symbol::VsSymbol, - vs_value::{LoadFunctionResult, Val}, + vs_value::{LoadFunctionResult, ToVal, Val}, }; use super::builtin_object::BuiltinObject; @@ -17,7 +17,7 @@ impl BuiltinObject for SymbolBuiltin { fn bo_sub(key: &str) -> Val { match key { - "iterator" => Val::Symbol(VsSymbol::ITERATOR), + "iterator" => VsSymbol::ITERATOR.to_val(), _ => Val::Undefined, } } diff --git a/valuescript_vm/src/builtins/type_error_builtin.rs b/valuescript_vm/src/builtins/type_error_builtin.rs index 7551402..902371d 100644 --- a/valuescript_vm/src/builtins/type_error_builtin.rs +++ b/valuescript_vm/src/builtins/type_error_builtin.rs @@ -3,9 +3,10 @@ use std::{collections::BTreeMap, rc::Rc}; use crate::native_function::{native_fn, ThisWrapper}; use crate::vs_value::ToVal; +use crate::ValTrait; use crate::{ native_function::NativeFunction, - operations::{op_sub, op_submov}, + operations::op_submov, vs_class::VsClass, vs_object::VsObject, vs_value::{LoadFunctionResult, Val}, @@ -75,7 +76,7 @@ static SET_MESSAGE: NativeFunction = native_fn(|mut this, params| { }); static TYPE_ERROR_TO_STRING: NativeFunction = native_fn(|this, _params| { - let message = op_sub(this.get().clone(), "message".to_val())?; + let message = this.get().sub("message".to_val())?; Ok(format!("TypeError({})", message).to_val()) }); diff --git a/valuescript_vm/src/bytecode_stack_frame.rs b/valuescript_vm/src/bytecode_stack_frame.rs index abddfac..a61897f 100644 --- a/valuescript_vm/src/bytecode_stack_frame.rs +++ b/valuescript_vm/src/bytecode_stack_frame.rs @@ -306,13 +306,10 @@ impl StackFrameTrait for BytecodeStackFrame { let subscript = self.decoder.decode_val(&self.registers); - let fn_ = operations::op_sub( - match &obj { - ThisArg::Register(reg_i) => self.registers[reg_i.clone()].clone(), - ThisArg::Val(val) => val.clone(), - }, - subscript, - )?; + let fn_ = match &obj { + ThisArg::Register(reg_i) => self.registers[reg_i.clone()].sub(subscript)?, + ThisArg::Val(val) => val.sub(subscript)?, + }; match fn_.load_function() { LoadFunctionResult::NotAFunction => { @@ -480,7 +477,7 @@ impl StackFrameTrait for BytecodeStackFrame { let res_i = self.decoder.decode_register_index(); - let next_fn = operations::op_sub(self.registers[iter_i].clone(), "next".to_val())?; + let next_fn = self.registers[iter_i].sub("next".to_val())?; match next_fn.load_function() { LoadFunctionResult::NotAFunction => { @@ -511,13 +508,11 @@ impl StackFrameTrait for BytecodeStackFrame { }; if let Some(value_i) = self.decoder.decode_register_index() { - self.registers[value_i] = - operations::op_sub(self.registers[iter_res_i].clone(), "value".to_val())?; + self.registers[value_i] = self.registers[iter_res_i].sub("value".to_val())?; } if let Some(done_i) = self.decoder.decode_register_index() { - self.registers[done_i] = - operations::op_sub(self.registers[iter_res_i].clone(), "done".to_val())?; + self.registers[done_i] = self.registers[iter_res_i].sub("done".to_val())?; } } @@ -527,11 +522,7 @@ impl StackFrameTrait for BytecodeStackFrame { "TODO: cat non-inline arrays" ); - let cat_frame = CatStackFrame { - args: self.decoder.decode_vec_val(&self.registers), - i: 0, - res: vec![], - }; + let cat_frame = CatStackFrame::from_args(self.decoder.decode_vec_val(&self.registers)); self.return_target = self.decoder.decode_register_index(); diff --git a/valuescript_vm/src/cat_stack_frame.rs b/valuescript_vm/src/cat_stack_frame.rs index 58e58f7..0914b24 100644 --- a/valuescript_vm/src/cat_stack_frame.rs +++ b/valuescript_vm/src/cat_stack_frame.rs @@ -1,29 +1,44 @@ use std::{mem::take, rc::Rc}; use crate::{ - builtins::error_builtin::ToError, + builtins::type_error_builtin::ToTypeError, + native_function::ThisWrapper, + operations::op_sub, stack_frame::{CallResult, FrameStepOk, FrameStepResult, StackFrameTrait}, + vs_symbol::VsSymbol, vs_value::{ToVal, Val}, + LoadFunctionResult, ValTrait, }; #[derive(Debug)] pub struct CatStackFrame { + pub state: CatFrameState, + pub iter_result: Option, pub args: Vec, pub i: usize, pub res: Vec, } -impl StackFrameTrait for CatStackFrame { - fn write_this(&mut self, _const: bool, _this: Val) -> Result<(), Val> { - Ok(()) +#[derive(Debug)] +pub enum CatFrameState { + ReadNext, + MakingIterator, + Iterating(Val), +} + +impl CatStackFrame { + pub fn from_args(args: Vec) -> Self { + Self { + state: CatFrameState::ReadNext, + iter_result: None, + args, + i: 0, + res: vec![], + } } - fn write_param(&mut self, param: Val) { - self.args.push(param); - } - - fn step(&mut self) -> FrameStepResult { - let arg = match self.args.get_mut(self.i) { + fn read_next(&mut self) -> FrameStepResult { + let mut arg = match self.args.get_mut(self.i) { None => { return Ok(FrameStepOk::Pop(CallResult { return_: take(&mut self.res).to_val(), @@ -48,11 +63,80 @@ impl StackFrameTrait for CatStackFrame { return Ok(FrameStepOk::Continue); } - Err("TODO: Cat: Non-array iterables".to_error()) + let make_iter = op_sub(arg.clone(), VsSymbol::ITERATOR.to_val())?; + + match make_iter.load_function() { + LoadFunctionResult::NotAFunction => Err("Non-iterable cat argument".to_type_error()), + LoadFunctionResult::NativeFunction(fn_) => { + self.state = CatFrameState::Iterating(fn_(ThisWrapper::new(true, &mut arg), vec![])?); + Ok(FrameStepOk::Continue) + } + LoadFunctionResult::StackFrame(mut new_frame) => { + self.state = CatFrameState::MakingIterator; + new_frame.write_this(true, arg)?; + Ok(FrameStepOk::Push(new_frame)) + } + } } - fn apply_call_result(&mut self, _call_result: CallResult) { - panic!("Not expected (yet)"); + fn apply_iter_result( + state: &mut CatFrameState, + res: &mut Vec, + iter_result: Val, + ) -> Result<(), Val> { + let done = iter_result.sub("done".to_val())?.is_truthy(); + + if done { + *state = CatFrameState::ReadNext; + } else { + res.push(iter_result.sub("value".to_val())?); + } + + Ok(()) + } +} + +impl StackFrameTrait for CatStackFrame { + fn write_this(&mut self, _const: bool, _this: Val) -> Result<(), Val> { + Ok(()) + } + + fn write_param(&mut self, param: Val) { + self.args.push(param); + } + + fn step(&mut self) -> FrameStepResult { + if let Some(iter_result) = take(&mut self.iter_result) { + Self::apply_iter_result(&mut self.state, &mut self.res, iter_result)?; + } + + match &mut self.state { + CatFrameState::ReadNext => self.read_next(), + CatFrameState::MakingIterator => panic!("Unexpected step during MakingIterator"), + CatFrameState::Iterating(iter) => match iter.sub("next".to_val())?.load_function() { + LoadFunctionResult::NotAFunction => Err(".next was not a function".to_type_error()), + LoadFunctionResult::NativeFunction(fn_) => { + let iter_result = fn_(ThisWrapper::new(false, iter), vec![])?; + Self::apply_iter_result(&mut self.state, &mut self.res, iter_result)?; + Ok(FrameStepOk::Continue) + } + LoadFunctionResult::StackFrame(mut new_frame) => { + new_frame.write_this(false, iter.clone())?; + Ok(FrameStepOk::Push(new_frame)) + } + }, + } + } + + fn apply_call_result(&mut self, call_result: CallResult) { + match &mut self.state { + CatFrameState::ReadNext => panic!("Unexpected call result during ReadNext"), + CatFrameState::MakingIterator => self.state = CatFrameState::Iterating(call_result.return_), + CatFrameState::Iterating(iter) => { + *iter = call_result.this; + self.iter_result = Some(call_result.return_); + } + } } fn get_call_result(&mut self) -> CallResult { diff --git a/valuescript_vm/src/vs_object.rs b/valuescript_vm/src/vs_object.rs index 0258168..68c7297 100644 --- a/valuescript_vm/src/vs_object.rs +++ b/valuescript_vm/src/vs_object.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use crate::vs_symbol::VsSymbol; use crate::vs_value::ToVal; +use crate::ValTrait; -use super::operations::op_sub; use super::vs_value::Val; #[derive(Clone, Default, Debug)] @@ -27,9 +27,7 @@ impl VsObject { } match &self.prototype { - Some(prototype) => op_sub(prototype.clone(), key) - .map_err(|e| e.to_string()) - .unwrap(), // TODO: Exception + Some(prototype) => prototype.sub(key).map_err(|e| e.to_string()).unwrap(), // TODO: Exception None => Val::Undefined, } } diff --git a/valuescript_vm/src/vs_symbol.rs b/valuescript_vm/src/vs_symbol.rs index fc29b5f..f4e7980 100644 --- a/valuescript_vm/src/vs_symbol.rs +++ b/valuescript_vm/src/vs_symbol.rs @@ -1,3 +1,5 @@ +use crate::vs_value::{ToVal, Val}; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum VsSymbol { ITERATOR, @@ -8,3 +10,9 @@ pub fn symbol_to_name(symbol: VsSymbol) -> &'static str { VsSymbol::ITERATOR => "iterator", } } + +impl ToVal for VsSymbol { + fn to_val(self) -> Val { + Val::Symbol(self) + } +} diff --git a/valuescript_vm/src/vs_value.rs b/valuescript_vm/src/vs_value.rs index 60f694c..bfe6a15 100644 --- a/valuescript_vm/src/vs_value.rs +++ b/valuescript_vm/src/vs_value.rs @@ -388,7 +388,7 @@ impl ValTrait for Val { let mut res = String::new(); if let Some(proto) = &object.prototype { - match op_sub(proto.clone(), "name".to_val()) { + match proto.sub("name".to_val()) { Ok(name) => { if name.typeof_() == VsType::String { res += &name.to_string(); @@ -607,7 +607,7 @@ impl<'a> std::fmt::Display for PrettyVal<'a> { } Val::Object(object) => { if let Some(proto) = &object.prototype { - match op_sub(proto.clone(), "name".to_val()) { + match proto.sub("name".to_val()) { Ok(name) => { if name.typeof_() == VsType::String { write!(f, "{} ", name)?;