diff --git a/src/vstc/virtual_machine/bytecode_decoder.rs b/src/vstc/virtual_machine/bytecode_decoder.rs index c6f17c8..09d3e33 100644 --- a/src/vstc/virtual_machine/bytecode_decoder.rs +++ b/src/vstc/virtual_machine/bytecode_decoder.rs @@ -97,7 +97,7 @@ impl BytecodeDecoder { vals.push(self.decode_val(registers)); } - self.decode_byte(); // End (TODO: assert) + self.decode_type(); // End (TODO: assert) Val::Array(Rc::new(vals)) }, @@ -111,6 +111,8 @@ impl BytecodeDecoder { ); } + self.decode_type(); // End (TODO: assert) + Val::Object(Rc::new(obj)) }, BytecodeType::Function => self.decode_function_header(), diff --git a/src/vstc/virtual_machine/operations.rs b/src/vstc/virtual_machine/operations.rs index da7936f..3508eec 100644 --- a/src/vstc/virtual_machine/operations.rs +++ b/src/vstc/virtual_machine/operations.rs @@ -182,6 +182,52 @@ pub fn op_in(_left: Val, _right: Val) -> Val { std::panic!("Not implemented: op_in"); } -pub fn op_sub(_left: Val, _right: Val) -> Val { - std::panic!("Not implemented: op_sub"); +pub fn op_sub(left: Val, right: Val) -> Val { + return match left { + Val::Void => std::panic!("Shouldn't happen"), + Val::Undefined => std::panic!("Not implemented: exceptions"), + Val::Null => std::panic!("Not implemented: exceptions"), + Val::Bool(_) => Val::Undefined, // TODO: toString etc + Val::Number(_) => Val::Undefined, // TODO: toString etc + Val::String(string_data) => { + let right_index = match right.to_index() { + None => { return Val::Undefined } + Some(i) => i, + }; + + let string_bytes = string_data.as_bytes(); + + if right_index >= string_bytes.len() { + return Val::Undefined; + } + + let byte = string_bytes[right_index]; + + // TODO: Val::Strings need to change to not use rust's string type, + // because they need to represent an actual byte array underneath. This + // occurs for invalid utf8 sequences which are getting converted to U+FFFD + // here. To be analogous to js, the information of the actual byte needs + // to be preserved, but that can't be represented in rust's string type. + return Val::String(Rc::new(String::from_utf8_lossy(&[byte]).into_owned())); + }, + Val::Array(array_data) => { + let right_index = match right.to_index() { + None => { return Val::Undefined } + Some(i) => i, + }; + + if right_index >= array_data.len() { + return Val::Undefined; + } + + return array_data[right_index].clone(); + }, + Val::Object(object_data) => { + return object_data + .get(&right.val_to_string()) + .unwrap_or(&Val::Undefined).clone(); + }, + Val::Function(_) => Val::Undefined, + Val::Custom(custom_data) => op_sub(custom_data.resolve(), right), + } } diff --git a/src/vstc/virtual_machine/virtual_machine.rs b/src/vstc/virtual_machine/virtual_machine.rs index d572b67..14e9b54 100644 --- a/src/vstc/virtual_machine/virtual_machine.rs +++ b/src/vstc/virtual_machine/virtual_machine.rs @@ -217,7 +217,7 @@ impl VirtualMachine { let params = frame.decoder.decode_val(&frame.registers); let register_index = frame.decoder.decode_register_index(); - let params_array = params.as_array(); + let params_array = params.as_array_data(); if params_array.is_none() { // Not sure this needs to be an exception in future since compiled diff --git a/src/vstc/virtual_machine/vs_pointer.rs b/src/vstc/virtual_machine/vs_pointer.rs index 33f6e1b..68de7c9 100644 --- a/src/vstc/virtual_machine/vs_pointer.rs +++ b/src/vstc/virtual_machine/vs_pointer.rs @@ -1,5 +1,6 @@ use std::rc::Rc; use std::cell::RefCell; +use std::collections::BTreeMap; use super::vs_value::Val; use super::vs_value::ValTrait; @@ -11,7 +12,7 @@ use super::bytecode_decoder::BytecodeType; pub struct VsPointer { bytecode: Rc>, pos: usize, - decoded: RefCell>, + resolved: RefCell>, } impl VsPointer { @@ -19,30 +20,9 @@ impl VsPointer { return Val::Custom(Rc::new(VsPointer { bytecode: bytecode.clone(), pos: pos, - decoded: RefCell::new(None), + resolved: RefCell::new(None), })); } - - pub fn decode(&self) -> Val { - let mut decoded = self.decoded.borrow_mut(); - - if decoded.is_some() { - return decoded.clone().unwrap(); - } - - let mut bd = BytecodeDecoder { - data: self.bytecode.clone(), - pos: self.pos, - }; - - let val = bd.decode_val(&Vec::new()); - - // TODO: Check that this actually inserts into the cell and not just a copy - // somehow - *decoded = Some(val.clone()); - - return val; - } } impl ValTrait for VsPointer { @@ -70,15 +50,15 @@ impl ValTrait for VsPointer { } fn val_to_string(&self) -> String { - return self.decode().val_to_string(); + return self.resolve().val_to_string(); } fn to_number(&self) -> f64 { - return self.decode().to_number(); + return self.resolve().to_number(); } - fn as_array(&self) -> Option>> { - return self.decode().as_array(); + fn to_index(&self) -> Option { + return self.resolve().to_index(); } fn is_primitive(&self) -> bool { @@ -95,22 +75,51 @@ impl ValTrait for VsPointer { } fn to_primitive(&self) -> Val { - return self.decode().to_primitive(); + return self.resolve().to_primitive(); + } + + fn resolve(&self) -> Val { + let mut resolved = self.resolved.borrow_mut(); + + if resolved.is_some() { + return resolved.clone().unwrap(); + } + + let mut bd = BytecodeDecoder { + data: self.bytecode.clone(), + pos: self.pos, + }; + + let val = bd.decode_val(&Vec::new()); + + // TODO: Check that this actually inserts into the cell and not just a copy + // somehow + *resolved = Some(val.clone()); + + return val; } fn bind(&self, params: Vec) -> Option { - return self.decode().bind(params); - } - - fn make_frame(&self) -> Option { - return self.decode().make_frame(); + return self.resolve().bind(params); } fn is_truthy(&self) -> bool { - return self.decode().is_truthy(); + return self.resolve().is_truthy(); } fn is_nullish(&self) -> bool { - return self.decode().is_nullish(); + return self.resolve().is_nullish(); + } + + fn as_array_data(&self) -> Option>> { + return self.resolve().as_array_data(); + } + + fn as_object_data(&self) -> Option>> { + return self.resolve().as_object_data(); + } + + fn make_frame(&self) -> Option { + return self.resolve().make_frame(); } } diff --git a/src/vstc/virtual_machine/vs_value.rs b/src/vstc/virtual_machine/vs_value.rs index 8eef12c..8288263 100644 --- a/src/vstc/virtual_machine/vs_value.rs +++ b/src/vstc/virtual_machine/vs_value.rs @@ -35,13 +35,19 @@ pub trait ValTrait { fn typeof_(&self) -> VsType; fn val_to_string(&self) -> String; fn to_number(&self) -> f64; - fn as_array(&self) -> Option>>; + fn to_index(&self) -> Option; fn is_primitive(&self) -> bool; fn to_primitive(&self) -> Val; fn is_truthy(&self) -> bool; fn is_nullish(&self) -> bool; + + fn resolve(&self) -> Val; + fn bind(&self, params: Vec) -> Option; + fn as_array_data(&self) -> Option>>; + fn as_object_data(&self) -> Option>>; + fn make_frame(&self) -> Option; } @@ -117,15 +123,24 @@ impl ValTrait for Val { }; } - fn as_array(&self) -> Option>> { + fn to_index(&self) -> Option { use Val::*; return match self { - Array(a) => Some(a.clone()), - Custom(val) => val.as_array(), - - _ => None, - } + Void => std::panic!("Shouldn't happen"), + Undefined => None, + Null => None, + Bool(_) => None, + Number(x) => number_to_index(*x), + String(s) => match f64::from_str(s) { + Ok(x) => number_to_index(x), + Err(_) => None, + }, + Array(vals) => None, + Object(_) => None, + Function(_) => None, + Custom(val) => val.to_index(), + }; } fn is_primitive(&self) -> bool { @@ -167,7 +182,7 @@ impl ValTrait for Val { Object(_) => true, Function(_) => true, Custom(val) => val.is_truthy(), - } + }; } fn is_nullish(&self) -> bool { @@ -187,6 +202,10 @@ impl ValTrait for Val { }; } + fn resolve(&self) -> Val { + std::panic!("Unexpected resolve call on plain Val") + } + fn bind(&self, params: Vec) -> Option { use Val::*; @@ -198,12 +217,35 @@ impl ValTrait for Val { } } + fn as_array_data(&self) -> Option>> { + use Val::*; + + return match self { + Array(a) => Some(a.clone()), + Custom(val) => val.as_array_data(), + + _ => None, + } + } + + fn as_object_data(&self) -> Option>> { + use Val::*; + + return match self { + Object(obj) => Some(obj.clone()), + Custom(val) => val.as_object_data(), + + _ => None, + } + } + fn make_frame(&self) -> Option { use Val::*; return match self { Function(f) => f.make_frame(), Custom(val) => val.make_frame(), + _ => None, } } @@ -214,3 +256,11 @@ impl std::fmt::Display for Val { write!(f, "{}", self.val_to_string()) } } + +fn number_to_index(x: f64) -> Option { + if x < 0_f64 || x != x.floor() { + return None + } + + return Some(x as usize); +}