mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-01-14 07:57:57 -05:00
Implement (basic) subscripting
This commit is contained in:
@@ -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(),
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Vec<u8>>,
|
||||
pos: usize,
|
||||
decoded: RefCell<Option<Val>>,
|
||||
resolved: RefCell<Option<Val>>,
|
||||
}
|
||||
|
||||
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<Rc<Vec<Val>>> {
|
||||
return self.decode().as_array();
|
||||
fn to_index(&self) -> Option<usize> {
|
||||
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<Val>) -> Option<Val> {
|
||||
return self.decode().bind(params);
|
||||
}
|
||||
|
||||
fn make_frame(&self) -> Option<StackFrame> {
|
||||
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<Rc<Vec<Val>>> {
|
||||
return self.resolve().as_array_data();
|
||||
}
|
||||
|
||||
fn as_object_data(&self) -> Option<Rc<BTreeMap<String, Val>>> {
|
||||
return self.resolve().as_object_data();
|
||||
}
|
||||
|
||||
fn make_frame(&self) -> Option<StackFrame> {
|
||||
return self.resolve().make_frame();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Rc<Vec<Val>>>;
|
||||
fn to_index(&self) -> Option<usize>;
|
||||
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<Val>) -> Option<Val>;
|
||||
|
||||
fn as_array_data(&self) -> Option<Rc<Vec<Val>>>;
|
||||
fn as_object_data(&self) -> Option<Rc<BTreeMap<String, Val>>>;
|
||||
|
||||
fn make_frame(&self) -> Option<StackFrame>;
|
||||
}
|
||||
|
||||
@@ -117,15 +123,24 @@ impl ValTrait for Val {
|
||||
};
|
||||
}
|
||||
|
||||
fn as_array(&self) -> Option<Rc<Vec<Val>>> {
|
||||
fn to_index(&self) -> Option<usize> {
|
||||
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<Val>) -> Option<Val> {
|
||||
use Val::*;
|
||||
|
||||
@@ -198,12 +217,35 @@ impl ValTrait for Val {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_array_data(&self) -> Option<Rc<Vec<Val>>> {
|
||||
use Val::*;
|
||||
|
||||
return match self {
|
||||
Array(a) => Some(a.clone()),
|
||||
Custom(val) => val.as_array_data(),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_object_data(&self) -> Option<Rc<BTreeMap<String, Val>>> {
|
||||
use Val::*;
|
||||
|
||||
return match self {
|
||||
Object(obj) => Some(obj.clone()),
|
||||
Custom(val) => val.as_object_data(),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_frame(&self) -> Option<StackFrame> {
|
||||
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<usize> {
|
||||
if x < 0_f64 || x != x.floor() {
|
||||
return None
|
||||
}
|
||||
|
||||
return Some(x as usize);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user