Reorganize using workspaces

This commit is contained in:
Andrew Morris
2023-02-27 12:35:37 +11:00
parent aafe1c1168
commit 44759d16a8
59 changed files with 515 additions and 412 deletions

View File

@@ -0,0 +1,23 @@
use super::super::vs_value::{Val, ValTrait};
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static EVERY: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(EveryState::default()))),
};
#[derive(Default)]
struct EveryState {}
impl ArrayMappingState for EveryState {
fn process(&mut self, _i: usize, _element: &Val, mapped: Val) -> Option<Val> {
match mapped.is_truthy() {
true => None,
false => Some(Val::Bool(false)),
}
}
fn finish(&mut self) -> Val {
Val::Bool(true)
}
}

View File

@@ -0,0 +1,31 @@
use std::rc::Rc;
use super::super::vs_value::{Val, ValTrait};
use super::super::vs_array::VsArray;
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static FILTER: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(FilterState::default()))),
};
#[derive(Default)]
struct FilterState {
filter_results: Vec<Val>,
}
impl ArrayMappingState for FilterState {
fn process(&mut self, _i: usize, element: &Val, mapped: Val) -> Option<Val> {
if mapped.is_truthy() {
self.filter_results.push(element.clone());
}
return None;
}
fn finish(&mut self) -> Val {
let mut filter_results = Vec::new();
std::mem::swap(&mut self.filter_results, &mut filter_results);
return Val::Array(Rc::new(VsArray::from(filter_results)));
}
}

View File

@@ -0,0 +1,23 @@
use super::super::vs_value::{Val, ValTrait};
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static FIND: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(FindState::default()))),
};
#[derive(Default)]
struct FindState {}
impl ArrayMappingState for FindState {
fn process(&mut self, _i: usize, element: &Val, mapped: Val) -> Option<Val> {
match mapped.is_truthy() {
true => Some(element.clone()),
false => None,
}
}
fn finish(&mut self) -> Val {
Val::Undefined
}
}

View File

@@ -0,0 +1,23 @@
use super::super::vs_value::{Val, ValTrait};
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static FIND_INDEX: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(FindIndexState::default()))),
};
#[derive(Default)]
struct FindIndexState {}
impl ArrayMappingState for FindIndexState {
fn process(&mut self, i: usize, _element: &Val, mapped: Val) -> Option<Val> {
match mapped.is_truthy() {
true => Some(Val::Number(i as f64)),
false => None,
}
}
fn finish(&mut self) -> Val {
Val::Number(-1f64)
}
}

View File

@@ -0,0 +1,36 @@
use std::rc::Rc;
use super::super::vs_value::{Val, ValTrait};
use super::super::vs_array::VsArray;
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static FLAT_MAP: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(FlatMapState::default()))),
};
#[derive(Default)]
struct FlatMapState {
flat_map_results: Vec<Val>,
}
impl ArrayMappingState for FlatMapState {
fn process(&mut self, _i: usize, _element: &Val, mapped: Val) -> Option<Val> {
match mapped.as_array_data() {
None => self.flat_map_results.push(mapped),
Some(array_data) => {
for el in &array_data.elements {
self.flat_map_results.push(el.clone());
}
}
}
return None;
}
fn finish(&mut self) -> Val {
let mut flat_map_results = Vec::new();
std::mem::swap(&mut self.flat_map_results, &mut flat_map_results);
return Val::Array(Rc::new(VsArray::from(flat_map_results)));
}
}

View File

@@ -0,0 +1,28 @@
use std::rc::Rc;
use super::super::vs_value::{Val};
use super::super::vs_array::VsArray;
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static MAP: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(MapState::default()))),
};
#[derive(Default)]
struct MapState {
map_results: Vec<Val>,
}
impl ArrayMappingState for MapState {
fn process(&mut self, _i: usize, _element: &Val, mapped: Val) -> Option<Val> {
self.map_results.push(mapped);
return None;
}
fn finish(&mut self) -> Val {
let mut map_results = Vec::new();
std::mem::swap(&mut self.map_results, &mut map_results);
return Val::Array(Rc::new(VsArray::from(map_results)));
}
}

View File

@@ -0,0 +1,130 @@
use std::rc::Rc;
use super::super::vs_value::{Val, ValTrait, LoadFunctionResult};
use super::super::vs_array::VsArray;
use super::super::stack_frame::{StackFrameTrait, FrameStepResult, CallResult};
pub trait ArrayMappingState {
fn process(&mut self, i: usize, element: &Val, mapped: Val) -> Option<Val>;
fn finish(&mut self) -> Val;
}
pub struct ArrayMappingFrame {
state: Box<dyn ArrayMappingState>,
early_exit: Option<Val>,
this: Option<Rc<VsArray>>,
array_i: usize,
mapper: Val,
this_arg: Val,
param_i: usize,
}
impl ArrayMappingFrame {
pub fn new(state: Box<dyn ArrayMappingState>) -> ArrayMappingFrame {
return ArrayMappingFrame {
state: state,
early_exit: None,
this: None,
array_i: 0,
mapper: Val::Void,
this_arg: Val::Undefined,
param_i: 0,
};
}
}
impl StackFrameTrait for ArrayMappingFrame {
fn write_this(&mut self, this: Val) {
self.this = this.as_array_data();
}
fn write_param(&mut self, param: Val) {
match self.param_i {
0 => { self.mapper = param; }
1 => { self.this_arg = param; }
_ => {},
};
self.param_i += 1;
}
fn step(&mut self) -> FrameStepResult {
let array_data = match &self.this {
None => std::panic!("Not implemented: exception: array fn called on non-array"),
Some(ad) => ad,
};
for early_exit in &self.early_exit {
return FrameStepResult::Pop(CallResult {
return_: early_exit.clone(),
this: Val::Array(array_data.clone()),
})
}
let array_i = self.array_i;
self.array_i += 1;
match array_data.elements.get(array_i) {
Some(el) => match el {
Val::Void => {
return FrameStepResult::Continue;
},
_ => match self.mapper.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: exception: map fn is not a function")
,
LoadFunctionResult::NativeFunction(native_fn) => {
return match self.state.process(
array_i,
el,
native_fn(
&mut self.this_arg.clone(),
vec![
el.clone(),
Val::Number(array_i as f64),
Val::Array(array_data.clone()),
],
),
) {
None => FrameStepResult::Continue,
Some(val) => FrameStepResult::Pop(CallResult {
return_: val,
this: Val::Array(array_data.clone()),
}),
};
},
LoadFunctionResult::StackFrame(mut new_frame) => {
new_frame.write_this(self.this_arg.clone());
new_frame.write_param(el.clone());
new_frame.write_param(Val::Number(array_i as f64));
new_frame.write_param(Val::Array(array_data.clone()));
return FrameStepResult::Push(new_frame);
},
},
},
None => {
return FrameStepResult::Pop(CallResult {
return_: self.state.finish(),
this: Val::Array(array_data.clone()),
});
},
};
}
fn apply_call_result(&mut self, call_result: CallResult) {
let array_i = self.array_i - 1;
let element = match &self.this {
None => std::panic!("Not implemented: exception: array fn called on non-array"),
Some(ad) => &ad.elements[array_i],
};
self.early_exit = self.state.process(array_i, element, call_result.return_);
}
fn get_call_result(&mut self) -> CallResult {
std::panic!("Not appropriate for MapFrame")
}
}

View File

@@ -0,0 +1,109 @@
use std::rc::Rc;
use super::super::vs_value::{Val, ValTrait, LoadFunctionResult};
use super::super::vs_array::VsArray;
use super::super::native_frame_function::NativeFrameFunction;
use super::super::stack_frame::{StackFrameTrait, FrameStepResult, CallResult};
pub static REDUCE: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ReduceFrame {
this: None,
array_i: 0,
reducer: Val::Void,
param_i: 0,
value: None,
}),
};
struct ReduceFrame {
this: Option<Rc<VsArray>>,
array_i: usize,
reducer: Val,
param_i: usize,
value: Option<Val>,
}
impl StackFrameTrait for ReduceFrame {
fn write_this(&mut self, this: Val) {
self.this = this.as_array_data();
}
fn write_param(&mut self, param: Val) {
match self.param_i {
0 => { self.reducer = param; }
1 => { self.value = Some(param); }
_ => {},
};
self.param_i += 1;
}
fn step(&mut self) -> FrameStepResult {
let array_data = match &self.this {
None => std::panic!("Not implemented: exception: reduce called on non-array"),
Some(ad) => ad,
};
let array_i = self.array_i;
self.array_i += 1;
match array_data.elements.get(array_i) {
Some(el) => match el {
Val::Void => {
return FrameStepResult::Continue;
},
_ => match &self.value {
None => {
self.value = Some(el.clone());
return FrameStepResult::Continue;
},
Some(value) => match self.reducer.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: exception: reduce fn is not a function")
,
LoadFunctionResult::NativeFunction(native_fn) => {
self.value = Some(native_fn(
&mut Val::Undefined,
vec![
value.clone(),
el.clone(),
Val::Number(array_i as f64),
Val::Array(array_data.clone()),
],
));
return FrameStepResult::Continue;
},
LoadFunctionResult::StackFrame(mut new_frame) => {
new_frame.write_param(value.clone());
new_frame.write_param(el.clone());
new_frame.write_param(Val::Number(array_i as f64));
new_frame.write_param(Val::Array(array_data.clone()));
return FrameStepResult::Push(new_frame);
},
},
},
},
None => match &self.value {
None => {
std::panic!("Not implemented: exception: reduce of empty array with no initial value");
},
Some(value) => {
return FrameStepResult::Pop(CallResult {
return_: value.clone(),
this: Val::Array(array_data.clone()),
});
},
},
};
}
fn apply_call_result(&mut self, call_result: CallResult) {
self.value = Some(call_result.return_);
}
fn get_call_result(&mut self) -> CallResult {
std::panic!("Not appropriate for ReduceFrame")
}
}

View File

@@ -0,0 +1,119 @@
use std::rc::Rc;
use super::super::vs_value::{Val, ValTrait, LoadFunctionResult};
use super::super::vs_array::VsArray;
use super::super::native_frame_function::NativeFrameFunction;
use super::super::stack_frame::{StackFrameTrait, FrameStepResult, CallResult};
pub static REDUCE_RIGHT: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ReduceRightFrame {
this: None,
array_i: 0,
reducer: Val::Void,
param_i: 0,
value: None,
}),
};
struct ReduceRightFrame {
this: Option<Rc<VsArray>>,
array_i: usize,
reducer: Val,
param_i: usize,
value: Option<Val>,
}
impl StackFrameTrait for ReduceRightFrame {
fn write_this(&mut self, this: Val) {
self.this = this.as_array_data();
match &self.this {
None => {},
Some(ad) => {
self.array_i = ad.elements.len()
},
};
}
fn write_param(&mut self, param: Val) {
match self.param_i {
0 => { self.reducer = param; }
1 => { self.value = Some(param); }
_ => {},
};
self.param_i += 1;
}
fn step(&mut self) -> FrameStepResult {
let array_data = match &self.this {
None => std::panic!("Not implemented: exception: reduceRight called on non-array"),
Some(ad) => ad,
};
if self.array_i == 0 {
match &self.value {
None => {
std::panic!("Not implemented: exception: reduceRight of empty array with no initial value");
},
Some(value) => {
return FrameStepResult::Pop(CallResult {
return_: value.clone(),
this: Val::Array(array_data.clone()),
});
},
}
}
self.array_i -= 1;
let array_i = self.array_i;
let el = &array_data.elements[array_i];
match el {
Val::Void => {
return FrameStepResult::Continue;
},
_ => match &self.value {
None => {
self.value = Some(el.clone());
return FrameStepResult::Continue;
},
Some(value) => match self.reducer.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: exception: reduceRight fn is not a function")
,
LoadFunctionResult::NativeFunction(native_fn) => {
self.value = Some(native_fn(
&mut Val::Undefined,
vec![
value.clone(),
el.clone(),
Val::Number(array_i as f64),
Val::Array(array_data.clone()),
],
));
return FrameStepResult::Continue;
},
LoadFunctionResult::StackFrame(mut new_frame) => {
new_frame.write_param(value.clone());
new_frame.write_param(el.clone());
new_frame.write_param(Val::Number(array_i as f64));
new_frame.write_param(Val::Array(array_data.clone()));
return FrameStepResult::Push(new_frame);
},
},
},
};
}
fn apply_call_result(&mut self, call_result: CallResult) {
self.value = Some(call_result.return_);
}
fn get_call_result(&mut self) -> CallResult {
std::panic!("Not appropriate for ReduceRightFrame")
}
}

View File

@@ -0,0 +1,23 @@
use super::super::vs_value::{Val, ValTrait};
use super::super::native_frame_function::NativeFrameFunction;
use super::array_mapping_frame::{ArrayMappingState, ArrayMappingFrame};
pub static SOME: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(ArrayMappingFrame::new(Box::new(SomeState::default()))),
};
#[derive(Default)]
struct SomeState {}
impl ArrayMappingState for SomeState {
fn process(&mut self, _i: usize, _element: &Val, mapped: Val) -> Option<Val> {
match mapped.is_truthy() {
true => Some(Val::Bool(true)),
false => None,
}
}
fn finish(&mut self) -> Val {
Val::Bool(false)
}
}

View File

@@ -0,0 +1,290 @@
use std::rc::Rc;
use super::super::vs_value::{Val, ValTrait, LoadFunctionResult};
use super::super::vs_array::VsArray;
use super::super::native_frame_function::NativeFrameFunction;
use super::super::stack_frame::{StackFrameTrait, FrameStepResult, CallResult};
pub static SORT: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(SortFrame {
this: None,
comparator: Val::Void,
param_i: 0,
tree: SortTreeNode { data: SortTreeNodeData::Sorted(vec![]) },
started: false,
}),
};
struct SortFrame {
this: Option<Rc<VsArray>>,
comparator: Val,
param_i: usize,
tree: SortTreeNode,
started: bool,
}
struct VecPos<T> {
vec: Vec<T>,
pos: usize,
}
struct VecSlice<'a, T> {
vec: &'a Vec<T>,
start: usize,
end: usize,
}
struct SortTreeNode {
data: SortTreeNodeData,
}
impl SortTreeNode {
fn new(vals: VecSlice<Val>) -> SortTreeNode {
let len = vals.end - vals.start;
if len <= 1 {
let mut sorted = vec![];
for i in vals.start..vals.end {
sorted.push(vals.vec[i].clone());
}
return SortTreeNode { data: SortTreeNodeData::Sorted(sorted) };
}
if len == 2 {
return SortTreeNode {
data: SortTreeNodeData::Sorting(
Vec::new(),
VecPos {
vec: vec![vals.vec[vals.start].clone()],
pos: 0,
},
VecPos {
vec: vec![vals.vec[vals.start + 1].clone()],
pos: 0,
},
),
};
}
let mid = vals.start + (vals.end - vals.start) / 2;
return SortTreeNode {
data: SortTreeNodeData::Branch(
Box::new(SortTreeNode::new(VecSlice {
vec: vals.vec,
start: vals.start,
end: mid,
})),
Box::new(SortTreeNode::new(VecSlice {
vec: vals.vec,
start: mid,
end: vals.end,
})),
),
};
}
fn get_compare_elements(&self) -> Option<(Val, Val)> {
match &self.data {
SortTreeNodeData::Branch(left, right) => {
return left.get_compare_elements()
.or_else(|| right.get_compare_elements());
},
SortTreeNodeData::Sorting(_vals, left, right) => {
let lval_opt = left.vec.get(left.pos);
let rval_opt = right.vec.get(right.pos);
match (lval_opt, rval_opt) {
(Some(lval), Some(rval)) => {
return Some((lval.clone(), rval.clone()));
},
_ => {
std::panic!("Failed to get compare elements from sorting state");
},
}
},
SortTreeNodeData::Sorted(_) => {
return None;
},
};
}
fn apply_outcome(&mut self, should_swap: bool) {
match &mut self.data {
SortTreeNodeData::Branch(left, right) => {
match &mut left.data {
SortTreeNodeData::Branch(_, _) | SortTreeNodeData::Sorting(_, _, _) => {
left.apply_outcome(should_swap);
},
SortTreeNodeData::Sorted(left_vals) => {
right.apply_outcome(should_swap);
match &mut right.data {
SortTreeNodeData::Sorted(right_vals) => {
let mut owned_left_vals = vec![];
std::mem::swap(&mut owned_left_vals, left_vals);
let mut owned_right_vals = vec![];
std::mem::swap(&mut owned_right_vals, right_vals);
self.data = SortTreeNodeData::Sorting(
vec![],
VecPos { vec: owned_left_vals, pos: 0 },
VecPos { vec: owned_right_vals, pos: 0 },
);
},
_ => {},
};
}
};
},
SortTreeNodeData::Sorted(_) => {
std::panic!("Failed to apply outcome");
},
SortTreeNodeData::Sorting(vals, left, right) => {
let lval_opt = left.vec.get(left.pos);
let rval_opt = right.vec.get(right.pos);
match (lval_opt, rval_opt) {
(Some(lval), Some(rval)) => match should_swap {
false => {
vals.push(lval.clone());
left.pos += 1;
},
true => {
vals.push(rval.clone());
right.pos += 1;
},
},
_ => std::panic!("Failed to apply outcome"),
};
if left.pos == left.vec.len() || right.pos == right.vec.len() {
for i in left.pos..left.vec.len() {
vals.push(left.vec[i].clone());
}
for i in right.pos..right.vec.len() {
vals.push(right.vec[i].clone());
}
let mut owned_vals = vec![];
std::mem::swap(&mut owned_vals, vals);
self.data = SortTreeNodeData::Sorted(owned_vals);
}
},
};
}
}
enum SortTreeNodeData {
Branch(Box<SortTreeNode>, Box<SortTreeNode>),
Sorting(Vec<Val>, VecPos<Val>, VecPos<Val>),
Sorted(Vec<Val>),
}
impl StackFrameTrait for SortFrame {
fn write_this(&mut self, this: Val) {
self.this = this.as_array_data();
}
fn write_param(&mut self, param: Val) {
match self.param_i {
0 => { self.comparator = param; },
_ => {},
};
self.param_i += 1;
}
fn step(&mut self) -> FrameStepResult {
if !self.started {
let array_data = match &mut self.this {
None => std::panic!("Not implemented: exception: array fn called on non-array"),
Some(ad) => ad,
};
match self.comparator {
Val::Void => {
let array_data_mut = Rc::make_mut(array_data);
array_data_mut.elements.sort_by(|a, b|
a.val_to_string().cmp(&b.val_to_string())
);
return FrameStepResult::Pop(CallResult {
return_: Val::Array(array_data.clone()),
this: Val::Array(array_data.clone()),
});
},
_ => {
self.tree = SortTreeNode::new(VecSlice {
vec: &array_data.elements,
start: 0,
end: array_data.elements.len(),
});
self.started = true;
},
};
}
match self.tree.get_compare_elements() {
None => match &mut self.tree.data {
SortTreeNodeData::Sorted(vals) => {
let mut owned_vals = vec![];
std::mem::swap(&mut owned_vals, vals);
let res = Val::Array(Rc::new(VsArray::from(owned_vals)));
return FrameStepResult::Pop(CallResult {
return_: res.clone(),
this: res,
});
},
_ => std::panic!("This shouldn't happen"),
},
Some((left, right)) => match self.comparator.load_function() {
LoadFunctionResult::NotAFunction => {
std::panic!("Not implemented: exception: comparator is not a function");
},
LoadFunctionResult::NativeFunction(native_fn) => {
let res = native_fn(&mut Val::Undefined, vec![left, right]).to_number();
let should_swap = match res.is_nan() {
true => false,
false => res > 0_f64,
};
self.tree.apply_outcome(should_swap);
return FrameStepResult::Continue;
},
LoadFunctionResult::StackFrame(mut new_frame) => {
new_frame.write_param(left);
new_frame.write_param(right);
return FrameStepResult::Push(new_frame);
},
},
};
}
fn apply_call_result(&mut self, call_result: CallResult) {
let res = call_result.return_.to_number();
let should_swap = match res.is_nan() {
true => false,
false => res > 0_f64,
};
self.tree.apply_outcome(should_swap);
}
fn get_call_result(&mut self) -> CallResult {
std::panic!("Not appropriate for SortFrame")
}
}

View File

@@ -0,0 +1,12 @@
mod array_mapping_frame;
pub mod array_map;
pub mod array_every;
pub mod array_some;
pub mod array_filter;
pub mod array_find;
pub mod array_find_index;
pub mod array_flat_map;
pub mod array_reduce;
pub mod array_reduce_right;
pub mod array_sort;

View File

@@ -0,0 +1,13 @@
use super::vs_value::ValTrait;
use super::math::MATH;
use super::debug::DEBUG;
// TODO: Investigate whether a static array can be used for this and why rust
// seems to not like it when I try.
pub fn get_builtin(index: usize) -> &'static dyn ValTrait {
return match index {
0 => &MATH,
1 => &DEBUG,
_ => std::panic!(""),
}
}

View File

@@ -0,0 +1,237 @@
use std::rc::Rc;
use std::collections::BTreeMap;
use super::vs_value::Val;
use super::vs_value::ValTrait;
use super::vs_pointer::VsPointer;
use super::vs_function::VsFunction;
use super::instruction::Instruction;
use super::vs_object::VsObject;
use super::vs_array::VsArray;
use super::vs_class::VsClass;
use super::builtins::get_builtin;
pub struct BytecodeDecoder {
// TODO: Enable borrow usage to avoid the rc overhead
pub data: Rc<Vec<u8>>,
pub pos: usize,
}
#[repr(u8)]
#[derive(PartialEq)]
pub enum BytecodeType {
End = 0x00,
Void = 0x01,
Undefined = 0x02,
Null = 0x03,
False = 0x04,
True = 0x05,
SignedByte = 0x06,
Number = 0x07,
String = 0x08,
Array = 0x09,
Object = 0x0a,
Function = 0x0b,
Pointer = 0x0d,
Register = 0x0e,
Builtin = 0x10,
Class = 0x11,
}
impl BytecodeType {
fn from_byte(byte: u8) -> BytecodeType {
use BytecodeType::*;
return match byte {
0x00 => End,
0x01 => Void,
0x02 => Undefined,
0x03 => Null,
0x04 => False,
0x05 => True,
0x06 => SignedByte,
0x07 => Number,
0x08 => String,
0x09 => Array,
0x0a => Object,
0x0b => Function,
0x0d => Pointer,
0x0e => Register,
0x10 => Builtin,
0x11 => Class,
_ => std::panic!("Unrecognized BytecodeType"),
};
}
}
impl BytecodeDecoder {
pub fn decode_byte(&mut self) -> u8 {
let byte = self.data[self.pos];
self.pos += 1;
return byte;
}
pub fn peek_byte(&self) -> u8 {
return self.data[self.pos];
}
pub fn decode_type(&mut self) -> BytecodeType {
return BytecodeType::from_byte(self.decode_byte());
}
pub fn peek_type(&self) -> BytecodeType {
return BytecodeType::from_byte(self.peek_byte());
}
pub fn decode_val(&mut self, registers: &Vec<Val>) -> Val {
return match self.decode_type() {
BytecodeType::End => std::panic!("Cannot decode end"),
BytecodeType::Void => Val::Void,
BytecodeType::Undefined => Val::Undefined,
BytecodeType::Null => Val::Null,
BytecodeType::False => Val::Bool(false),
BytecodeType::True => Val::Bool(true),
BytecodeType::SignedByte => Val::Number(
self.decode_signed_byte() as f64
),
BytecodeType::Number => Val::Number(
self.decode_number()
),
BytecodeType::String => Val::String(Rc::new(
self.decode_string()
)),
BytecodeType::Array => {
let mut vals: Vec<Val> = Vec::new();
while self.peek_type() != BytecodeType::End {
vals.push(self.decode_val(registers));
}
self.decode_type(); // End (TODO: assert)
Val::Array(Rc::new(VsArray::from(vals)))
},
BytecodeType::Object => {
let mut obj: BTreeMap<String, Val> = BTreeMap::new();
while self.peek_type() != BytecodeType::End {
obj.insert(
self.decode_val(registers).val_to_string(),
self.decode_val(registers),
);
}
self.decode_type(); // End (TODO: assert)
Val::Object(Rc::new(VsObject { string_map: obj, prototype: None }))
},
BytecodeType::Function => self.decode_function_header(),
BytecodeType::Pointer => self.decode_pointer(),
BytecodeType::Register => registers[self.decode_register_index().unwrap()].clone(),
BytecodeType::Builtin => Val::Static(get_builtin(self.decode_varsize_uint())),
BytecodeType::Class => Val::Class(Rc::new(VsClass {
constructor: self.decode_val(registers),
instance_prototype: self.decode_val(registers),
}))
}
}
pub fn decode_signed_byte(&mut self) -> i8 {
let res = self.data[self.pos] as i8;
self.pos += 1;
return res;
}
pub fn decode_number(&mut self) -> f64 {
let mut buf = [0u8; 8];
let next_pos = self.pos + 8;
buf.clone_from_slice(&self.data[self.pos..next_pos]);
self.pos = next_pos;
return f64::from_le_bytes(buf);
}
pub fn decode_string(&mut self) -> String {
let len = self.decode_varsize_uint();
let start = self.pos; // Start after decoding varsize
let end = self.pos + len;
let res = String::from_utf8_lossy(&self.data[start..end]).into_owned();
self.pos = end;
return res;
}
pub fn decode_varsize_uint(&mut self) -> usize {
let mut res = 0_usize;
let mut mul = 1_usize;
loop {
let byte = self.decode_byte();
res += mul * ((byte % 128) as usize);
if byte & 128 == 0 {
return res;
}
mul *= 128;
}
}
pub fn decode_pos(&mut self) -> usize {
// TODO: the number of bytes to represent a position should be based on the
// size of the bytecode
return self.decode_byte() as usize + 256 * self.decode_byte() as usize;
}
pub fn decode_register_index(&mut self) -> Option<usize> {
// TODO: Handle multi-byte registers
let byte = self.decode_byte();
if byte == 0xff {
return None;
}
return Some(byte as usize);
}
pub fn clone_at(&self, pos: usize) -> BytecodeDecoder {
return BytecodeDecoder { data: self.data.clone(), pos: pos };
}
pub fn decode_pointer(&mut self) -> Val {
let from_pos = self.pos;
let pos = self.decode_pos();
if pos < from_pos {
if
self.clone_at(pos).decode_type() != BytecodeType::Function &&
self.clone_at(pos).decode_type() != BytecodeType::Class
{
std::panic!("Invalid: non-function pointer that points backwards");
}
}
return VsPointer::new(
&self.data,
pos,
);
}
pub fn decode_function_header(&mut self) -> Val {
// TODO: Support >256
let register_count = self.decode_byte() as usize;
let parameter_count = self.decode_byte() as usize;
return Val::Function(Rc::new(VsFunction {
bytecode: self.data.clone(),
register_count: register_count,
parameter_count: parameter_count,
start: self.pos,
binds: Vec::new(),
}));
}
pub fn decode_instruction(&mut self) -> Instruction {
return Instruction::from_byte(self.decode_byte());
}
}

View File

@@ -0,0 +1,439 @@
use std::rc::Rc;
use super::vs_value::{Val, ValTrait, LoadFunctionResult};
use super::vs_object::VsObject;
use super::operations;
use super::bytecode_decoder::BytecodeDecoder;
use super::bytecode_decoder::BytecodeType;
use super::instruction::Instruction;
use super::stack_frame::{StackFrame, StackFrameTrait, FrameStepResult, CallResult};
pub struct BytecodeStackFrame {
pub decoder: BytecodeDecoder,
pub registers: Vec<Val>,
pub param_start: usize,
pub param_end: usize,
pub this_target: Option<usize>,
pub return_target: Option<usize>,
}
impl BytecodeStackFrame {
pub fn apply_unary_op(
&mut self,
op: fn(input: Val) -> Val,
) {
let input = self.decoder.decode_val(&self.registers);
let register_index = self.decoder.decode_register_index();
if register_index.is_some() {
self.registers[register_index.unwrap()] = op(input);
}
}
pub fn apply_binary_op(
&mut self,
op: fn(left: Val, right: Val) -> Val,
) {
let left = self.decoder.decode_val(&self.registers);
let right = self.decoder.decode_val(&self.registers);
let register_index = self.decoder.decode_register_index();
if register_index.is_some() {
self.registers[register_index.unwrap()] = op(left, right);
}
}
pub fn transfer_parameters(
&mut self,
new_frame: &mut StackFrame,
) {
let bytecode_type = self.decoder.decode_type();
if bytecode_type != BytecodeType::Array {
std::panic!("Not implemented: call instruction not using inline array");
}
while self.decoder.peek_type() != BytecodeType::End {
let p = self.decoder.decode_val(&self.registers);
new_frame.write_param(p);
}
self.decoder.decode_type(); // End (TODO: assert)
}
pub fn decode_parameters(
&mut self,
) -> Vec<Val> {
let mut res = Vec::<Val>::new();
let bytecode_type = self.decoder.decode_type();
if bytecode_type != BytecodeType::Array {
std::panic!("Not implemented: call instruction not using inline array");
}
while self.decoder.peek_type() != BytecodeType::End {
res.push(self.decoder.decode_val(&self.registers));
}
self.decoder.decode_type(); // End (TODO: assert)
return res;
}
}
impl StackFrameTrait for BytecodeStackFrame {
fn write_this(&mut self, this: Val) {
self.registers[1] = this;
}
fn write_param(&mut self, param: Val) {
if self.param_start < self.param_end {
self.registers[self.param_start] = param;
self.param_start += 1;
}
}
fn step(&mut self) -> FrameStepResult {
use Instruction::*;
match self.decoder.decode_instruction() {
End => {
return FrameStepResult::Pop(CallResult {
return_: self.registers[0].clone(),
this: self.registers[1].clone(),
});
},
Mov => {
let val = self.decoder.decode_val(&self.registers);
let register_index = self.decoder.decode_register_index();
if register_index.is_some() {
self.registers[register_index.unwrap()] = val;
}
},
OpInc => {
let register_index = self.decoder.decode_register_index().unwrap();
let mut val = self.registers[register_index].clone();
val = operations::op_plus(val, Val::Number(1_f64));
self.registers[register_index] = val;
},
OpDec => {
let register_index = self.decoder.decode_register_index().unwrap();
let mut val = self.registers[register_index].clone();
val = operations::op_minus(val, Val::Number(1_f64));
self.registers[register_index] = val;
},
OpPlus => self.apply_binary_op(operations::op_plus),
OpMinus => self.apply_binary_op(operations::op_minus),
OpMul => self.apply_binary_op(operations::op_mul),
OpDiv => self.apply_binary_op(operations::op_div),
OpMod => self.apply_binary_op(operations::op_mod),
OpExp => self.apply_binary_op(operations::op_exp),
OpEq => self.apply_binary_op(operations::op_eq),
OpNe => self.apply_binary_op(operations::op_ne),
OpTripleEq => self.apply_binary_op(operations::op_triple_eq),
OpTripleNe => self.apply_binary_op(operations::op_triple_ne),
OpAnd => self.apply_binary_op(operations::op_and),
OpOr => self.apply_binary_op(operations::op_or),
OpNot => self.apply_unary_op(operations::op_not),
OpLess => self.apply_binary_op(operations::op_less),
OpLessEq => self.apply_binary_op(operations::op_less_eq),
OpGreater => self.apply_binary_op(operations::op_greater),
OpGreaterEq => self.apply_binary_op(operations::op_greater_eq),
OpNullishCoalesce => self.apply_binary_op(operations::op_nullish_coalesce),
OpOptionalChain => self.apply_binary_op(operations::op_optional_chain),
OpBitAnd => self.apply_binary_op(operations::op_bit_and),
OpBitOr => self.apply_binary_op(operations::op_bit_or),
OpBitNot => self.apply_unary_op(operations::op_bit_not),
OpBitXor => self.apply_binary_op(operations::op_bit_xor),
OpLeftShift => self.apply_binary_op(operations::op_left_shift),
OpRightShift => self.apply_binary_op(operations::op_right_shift),
OpRightShiftUnsigned => self.apply_binary_op(operations::op_right_shift_unsigned),
TypeOf => self.apply_unary_op(operations::op_typeof),
InstanceOf => self.apply_binary_op(operations::op_instance_of),
In => self.apply_binary_op(operations::op_in),
Call => {
let fn_ = self.decoder.decode_val(&self.registers);
match fn_.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: throw exception (fn_ is not a function)")
,
LoadFunctionResult::StackFrame(mut new_frame) => {
self.transfer_parameters(&mut new_frame);
self.return_target = self.decoder.decode_register_index();
self.this_target = None;
return FrameStepResult::Push(new_frame);
},
LoadFunctionResult::NativeFunction(native_fn) => {
let res = native_fn(
&mut Val::Undefined,
self.decode_parameters(),
);
match self.decoder.decode_register_index() {
Some(return_target) => {
self.registers[return_target] = res;
},
None => {},
};
},
};
}
Apply => {
let fn_ = self.decoder.decode_val(&self.registers);
match fn_.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: throw exception (fn_ is not a function)")
,
LoadFunctionResult::StackFrame(mut new_frame) => {
if self.decoder.peek_type() == BytecodeType::Register {
self.decoder.decode_type();
let this_target = self.decoder.decode_register_index();
self.this_target = this_target;
if this_target.is_some() {
new_frame.write_this(self.registers[this_target.unwrap()].clone());
}
} else {
self.this_target = None;
new_frame.write_this(self.decoder.decode_val(&self.registers));
}
self.transfer_parameters(&mut new_frame);
self.return_target = self.decoder.decode_register_index();
return FrameStepResult::Push(new_frame);
},
LoadFunctionResult::NativeFunction(_native_fn) => {
std::panic!("Not implemented");
},
}
}
Bind => {
let fn_val = self.decoder.decode_val(&self.registers);
let params = self.decoder.decode_val(&self.registers);
let register_index = self.decoder.decode_register_index();
let params_array = params.as_array_data();
if params_array.is_none() {
// Not sure this needs to be an exception in future since compiled
// code should never violate this
std::panic!("bind params should always be array")
}
let bound_fn = fn_val.bind((*params_array.unwrap()).elements.clone());
if bound_fn.is_none() {
// Not sure this needs to be an exception in future since compiled
// code should never violate this
std::panic!("fn parameter of bind should always be bindable");
}
if register_index.is_some() {
self.registers[register_index.unwrap()] = bound_fn.unwrap();
}
},
Sub => self.apply_binary_op(operations::op_sub),
SubMov => {
let subscript = self.decoder.decode_val(&self.registers);
let value = self.decoder.decode_val(&self.registers);
let register_index = self.decoder.decode_register_index().unwrap();
let mut target = self.registers[register_index].clone(); // TODO: Lift
operations::op_submov(&mut target, subscript, value);
self.registers[register_index] = target;
},
SubCall => {
let mut obj = match self.decoder.peek_type() {
BytecodeType::Register => {
self.decoder.decode_type();
ThisArg::Register(
self.decoder.decode_register_index().unwrap()
)
},
_ => ThisArg::Val(self.decoder.decode_val(&self.registers)),
};
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,
);
match fn_.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: throw exception (fn_ is not a function)")
,
LoadFunctionResult::StackFrame(mut new_frame) => {
self.transfer_parameters(&mut new_frame);
new_frame.write_this(match &obj {
ThisArg::Register(reg_i) => self.registers[reg_i.clone()].clone(),
ThisArg::Val(val) => val.clone(),
});
self.return_target = self.decoder.decode_register_index();
self.this_target = match obj {
ThisArg::Register(reg_i) => Some(reg_i),
ThisArg::Val(_) => None,
};
return FrameStepResult::Push(new_frame);
},
LoadFunctionResult::NativeFunction(native_fn) => {
let params = self.decode_parameters();
let res = match &mut obj {
ThisArg::Register(reg_i) => {
native_fn(
self.registers.get_mut(reg_i.clone()).unwrap(),
params,
)
},
ThisArg::Val(val) => {
native_fn(
val,
params,
)
},
};
match self.decoder.decode_register_index() {
Some(return_target) => {
self.registers[return_target] = res;
},
None => {},
};
},
};
},
Jmp => {
let dst = self.decoder.decode_pos();
self.decoder.pos = dst;
}
JmpIf => {
let cond = self.decoder.decode_val(&self.registers);
let dst = self.decoder.decode_pos();
if cond.is_truthy() {
self.decoder.pos = dst;
}
}
UnaryPlus => self.apply_unary_op(operations::op_unary_plus),
UnaryMinus => self.apply_unary_op(operations::op_unary_minus),
New => {
let class = self.decoder.decode_val(&self.registers)
.as_class_data()
.expect("Not implemented: throw exception (not constructible)");
let mut instance = Val::Object(Rc::new(VsObject {
string_map: Default::default(),
prototype: Some(class.instance_prototype.clone()),
}));
match class.constructor {
Val::Void => {
// Ignore parameters
self.decoder.decode_val(&self.registers);
let target_register = self.decoder.decode_register_index();
match target_register {
None => {},
Some(tr) => self.registers[tr] = instance,
};
},
_ => match class.constructor.load_function() {
LoadFunctionResult::NotAFunction =>
std::panic!("Not implemented: throw exception (class.constructor is not a function)")
,
LoadFunctionResult::StackFrame(mut new_frame) => {
self.transfer_parameters(&mut new_frame);
new_frame.write_this(instance);
self.return_target = None;
self.this_target = self.decoder.decode_register_index();
return FrameStepResult::Push(new_frame);
},
LoadFunctionResult::NativeFunction(native_fn) => {
native_fn(
&mut instance,
self.decode_parameters(),
);
match self.decoder.decode_register_index() {
Some(target) => {
self.registers[target] = instance;
},
None => {},
};
},
},
};
}
};
return FrameStepResult::Continue;
}
fn apply_call_result(&mut self, call_result: CallResult) {
match self.this_target {
None => {},
Some(tt) => {
self.registers[tt] = call_result.this;
},
};
match self.return_target {
None => {},
Some(rt) => {
self.registers[rt] = call_result.return_;
},
};
}
fn get_call_result(&mut self) -> CallResult {
std::panic!("Not appropriate for BytecodeStackFrame")
}
}
enum ThisArg {
Register(usize),
Val(Val),
}

View File

@@ -0,0 +1,62 @@
use std::rc::Rc;
use super::vs_value::{Val, VsType, ValTrait, LoadFunctionResult};
use super::vs_array::VsArray;
use super::vs_object::VsObject;
use super::vs_class::VsClass;
use super::native_function::NativeFunction;
pub struct Debug {}
pub static DEBUG: Debug = Debug {};
impl ValTrait for Debug {
fn typeof_(&self) -> VsType { VsType::Object }
fn val_to_string(&self) -> String { "[object Debug]".to_string() }
fn to_number(&self) -> f64 { f64::NAN }
fn to_index(&self) -> Option<usize> { None }
fn is_primitive(&self) -> bool { false }
fn to_primitive(&self) -> Val { Val::String(Rc::new(self.val_to_string())) }
fn is_truthy(&self) -> bool { true }
fn is_nullish(&self) -> bool { false }
fn bind(&self, _params: Vec<Val>) -> Option<Val> { None }
fn as_array_data(&self) -> Option<Rc<VsArray>> { None }
fn as_object_data(&self) -> Option<Rc<VsObject>> { None }
fn as_class_data(&self) -> Option<Rc<VsClass>> { None }
fn load_function(&self) -> LoadFunctionResult {
LoadFunctionResult::NotAFunction
}
fn sub(&self, key: Val) -> Val {
match key.val_to_string().as_str() {
"log" => Val::Static(&LOG),
_ => Val::Undefined,
}
}
fn submov(&mut self, _key: Val, _value: Val) {
std::panic!("Not implemented: exceptions");
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\x1b[36m[Debug]\x1b[39m")
}
fn codify(&self) -> String {
"Debug".into()
}
}
static LOG: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
for p in params {
println!("Debug.log: {}", p);
}
return Val::Undefined;
}
};

View File

@@ -0,0 +1,39 @@
use super::stack_frame::{StackFrameTrait, FrameStepResult, CallResult};
use super::vs_value::Val;
pub struct FirstStackFrame {
call_result: CallResult,
}
impl FirstStackFrame {
pub fn new() -> FirstStackFrame {
return FirstStackFrame {
call_result: CallResult {
return_: Val::Void,
this: Val::Void,
},
};
}
}
impl StackFrameTrait for FirstStackFrame {
fn write_this(&mut self, _this: Val) {
std::panic!("Not appropriate for FirstStackFrame");
}
fn write_param(&mut self, _param: Val) {
std::panic!("Not appropriate for FirstStackFrame");
}
fn step(&mut self) -> FrameStepResult {
std::panic!("Not appropriate for FirstStackFrame");
}
fn apply_call_result(&mut self, call_result: CallResult) {
self.call_result = call_result;
}
fn get_call_result(&mut self) -> CallResult {
return self.call_result.clone();
}
}

View File

@@ -0,0 +1,105 @@
// TODO: Fix duplication with assembler (requires internal crate?)
#[derive(Debug)]
#[derive(Clone)]
pub enum Instruction {
End = 0x00,
Mov = 0x01,
OpInc = 0x02,
OpDec = 0x03,
OpPlus = 0x04,
OpMinus = 0x05,
OpMul = 0x06,
OpDiv = 0x07,
OpMod = 0x08,
OpExp = 0x09,
OpEq = 0x0a,
OpNe = 0x0b,
OpTripleEq = 0x0c,
OpTripleNe = 0x0d,
OpAnd = 0x0e,
OpOr = 0x0f,
OpNot = 0x10,
OpLess = 0x11,
OpLessEq = 0x12,
OpGreater = 0x13,
OpGreaterEq = 0x14,
OpNullishCoalesce = 0x15,
OpOptionalChain = 0x16,
OpBitAnd = 0x17,
OpBitOr = 0x18,
OpBitNot = 0x19,
OpBitXor = 0x1a,
OpLeftShift = 0x1b,
OpRightShift = 0x1c,
OpRightShiftUnsigned = 0x1d,
TypeOf = 0x1e,
InstanceOf = 0x1f,
In = 0x20,
Call = 0x21,
Apply = 0x22,
Bind = 0x23,
Sub = 0x24,
SubMov = 0x25,
SubCall = 0x26,
Jmp = 0x27,
JmpIf = 0x28,
UnaryPlus = 0x29,
UnaryMinus = 0x2a,
New = 0x2b,
}
impl Instruction {
pub fn from_byte(byte: u8) -> Instruction {
use Instruction::*;
return match byte {
0x00 => End,
0x01 => Mov,
0x02 => OpInc,
0x03 => OpDec,
0x04 => OpPlus,
0x05 => OpMinus,
0x06 => OpMul,
0x07 => OpDiv,
0x08 => OpMod,
0x09 => OpExp,
0x0a => OpEq,
0x0b => OpNe,
0x0c => OpTripleEq,
0x0d => OpTripleNe,
0x0e => OpAnd,
0x0f => OpOr,
0x10 => OpNot,
0x11 => OpLess,
0x12 => OpLessEq,
0x13 => OpGreater,
0x14 => OpGreaterEq,
0x15 => OpNullishCoalesce,
0x16 => OpOptionalChain,
0x17 => OpBitAnd,
0x18 => OpBitOr,
0x19 => OpBitNot,
0x1a => OpBitXor,
0x1b => OpLeftShift,
0x1c => OpRightShift,
0x1d => OpRightShiftUnsigned,
0x1e => TypeOf,
0x1f => InstanceOf,
0x20 => In,
0x21 => Call,
0x22 => Apply,
0x23 => Bind,
0x24 => Sub,
0x25 => SubMov,
0x26 => SubCall,
0x27 => Jmp,
0x28 => JmpIf,
0x29 => UnaryPlus,
0x2a => UnaryMinus,
0x2b => New,
_ => std::panic!("Unrecognized instruction: {}", byte),
};
}
}

22
valuescript_vm/src/lib.rs Normal file
View File

@@ -0,0 +1,22 @@
mod array_higher_functions;
mod builtins;
mod bytecode_decoder;
mod bytecode_stack_frame;
mod debug;
mod first_stack_frame;
mod instruction;
mod math;
mod native_frame_function;
mod native_function;
mod operations;
mod stack_frame;
mod virtual_machine;
mod vs_array;
mod vs_class;
mod vs_function;
mod vs_object;
mod vs_pointer;
mod vs_value;
pub use virtual_machine::VirtualMachine;
pub use vs_value::ValTrait;

350
valuescript_vm/src/math.rs Normal file
View File

@@ -0,0 +1,350 @@
use std::rc::Rc;
use super::vs_value::{Val, VsType, ValTrait, LoadFunctionResult};
use super::vs_array::VsArray;
use super::vs_object::VsObject;
use super::vs_class::VsClass;
use super::native_function::NativeFunction;
use super::operations::to_u32;
pub struct Math {}
pub static MATH: Math = Math {};
impl ValTrait for Math {
fn typeof_(&self) -> VsType { VsType::Object }
fn val_to_string(&self) -> String { "[object Math]".to_string() }
fn to_number(&self) -> f64 { f64::NAN }
fn to_index(&self) -> Option<usize> { None }
fn is_primitive(&self) -> bool { false }
fn to_primitive(&self) -> Val { Val::String(Rc::new(self.val_to_string())) }
fn is_truthy(&self) -> bool { true }
fn is_nullish(&self) -> bool { false }
fn bind(&self, _params: Vec<Val>) -> Option<Val> { None }
fn as_array_data(&self) -> Option<Rc<VsArray>> { None }
fn as_object_data(&self) -> Option<Rc<VsObject>> { None }
fn as_class_data(&self) -> Option<Rc<VsClass>> { None }
fn load_function(&self) -> LoadFunctionResult {
LoadFunctionResult::NotAFunction
}
fn sub(&self, key: Val) -> Val {
match key.val_to_string().as_str() {
"E" => Val::Number(std::f64::consts::E),
"LN10" => Val::Number(std::f64::consts::LN_10),
"LN2" => Val::Number(std::f64::consts::LN_2),
"LOG10E" => Val::Number(std::f64::consts::LOG10_E),
"LOG2E" => Val::Number(std::f64::consts::LOG2_E),
"PI" => Val::Number(std::f64::consts::PI),
"SQRT1_2" => Val::Number(std::f64::consts::FRAC_1_SQRT_2),
"SQRT2" => Val::Number(std::f64::consts::SQRT_2),
"abs" => Val::Static(&ABS),
"acos" => Val::Static(&ACOS),
"acosh" => Val::Static(&ACOSH),
"asin" => Val::Static(&ASIN),
"asinh" => Val::Static(&ASINH),
"atan" => Val::Static(&ATAN),
"atan2" => Val::Static(&ATAN2),
"atanh" => Val::Static(&ATANH),
"cbrt" => Val::Static(&CBRT),
"ceil" => Val::Static(&CEIL),
"clz32" => Val::Static(&CLZ32),
"cos" => Val::Static(&COS),
"cosh" => Val::Static(&COSH),
"exp" => Val::Static(&EXP),
"expm1" => Val::Static(&EXPM1),
"floor" => Val::Static(&FLOOR),
"fround" => Val::Static(&FROUND),
"hypot" => Val::Static(&HYPOT),
"imul" => Val::Static(&IMUL),
"log" => Val::Static(&LOG),
"log10" => Val::Static(&LOG10),
"log1p" => Val::Static(&LOG1P),
"log2" => Val::Static(&LOG2),
"max" => Val::Static(&MAX),
"min" => Val::Static(&MIN),
"pow" => Val::Static(&POW),
// random: Not included because it cannot work as expected in ValueScript
"round" => Val::Static(&ROUND),
"sign" => Val::Static(&SIGN),
"sin" => Val::Static(&SIN),
"sinh" => Val::Static(&SINH),
"sqrt" => Val::Static(&SQRT),
"tan" => Val::Static(&TAN),
"tanh" => Val::Static(&TANH),
"trunc" => Val::Static(&TRUNC),
_ => Val::Undefined,
}
}
fn submov(&mut self, _key: Val, _value: Val) {
std::panic!("Not implemented: exceptions");
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\x1b[36m[Math]\x1b[39m")
}
fn codify(&self) -> String {
"Math".into()
}
}
fn param_to_number(param: Option<&Val>) -> f64 {
match param {
None => f64::NAN,
Some(p) => p.to_number(),
}
}
static ABS: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.abs());
}
};
static ACOS: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.acos());
}
};
static ACOSH: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.acosh());
}
};
static ASIN: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.asin());
}
};
static ASINH: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.sinh());
}
};
static ATAN: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.atan());
}
};
static ATAN2: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
let y = param_to_number(params.get(1));
return Val::Number(x.atan2(y));
}
};
static ATANH: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.atanh());
}
};
static CBRT: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.cbrt());
}
};
static CEIL: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.ceil());
}
};
static CLZ32: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(to_u32(x).leading_zeros() as f64);
}
};
static COS: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.cos());
}
};
static COSH: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.cosh());
}
};
static EXP: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.exp());
}
};
static EXPM1: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.exp_m1());
}
};
static FLOOR: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.floor());
}
};
static FROUND: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x as f32 as f64);
}
};
static HYPOT: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
let y = param_to_number(params.get(1));
return Val::Number(x.hypot(y));
}
};
static IMUL: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
let y = param_to_number(params.get(1));
return Val::Number((to_u32(x) * to_u32(y)) as i32 as f64);
}
};
static LOG: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.ln());
}
};
static LOG10: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.log10());
}
};
static LOG1P: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.ln_1p());
}
};
static LOG2: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.log2());
}
};
static MAX: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
let y = param_to_number(params.get(1));
return Val::Number(x.max(y));
}
};
static MIN: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
let y = param_to_number(params.get(1));
return Val::Number(x.min(y));
}
};
static POW: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
let y = param_to_number(params.get(1));
return Val::Number(x.powf(y));
}
};
static ROUND: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.round());
}
};
static SIGN: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.signum());
}
};
static SIN: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.sin());
}
};
static SINH: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.sinh());
}
};
static SQRT: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.sqrt());
}
};
static TAN: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.tan());
}
};
static TANH: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.tanh());
}
};
static TRUNC: NativeFunction = NativeFunction {
fn_: |_this: &mut Val, params: Vec<Val>| -> Val {
let x = param_to_number(params.get(0));
return Val::Number(x.trunc());
}
};

View File

@@ -0,0 +1,55 @@
use std::rc::Rc;
use super::vs_value::{
Val,
VsType,
ValTrait,
LoadFunctionResult,
};
use super::vs_object::VsObject;
use super::vs_array::VsArray;
use super::vs_class::VsClass;
use super::stack_frame::StackFrame;
pub struct NativeFrameFunction {
pub make_frame: fn() -> StackFrame,
}
impl ValTrait for NativeFrameFunction {
fn typeof_(&self) -> VsType { VsType::Function }
fn val_to_string(&self) -> String { "function() { [native code] }".to_string() }
fn to_number(&self) -> f64 { f64::NAN }
fn to_index(&self) -> Option<usize> { None }
fn is_primitive(&self) -> bool { false }
fn to_primitive(&self) -> Val { Val::String(Rc::new(self.val_to_string())) }
fn is_truthy(&self) -> bool { true }
fn is_nullish(&self) -> bool { false }
fn bind(&self, _params: Vec<Val>) -> Option<Val> {
std::panic!("Not implemented");
}
fn as_array_data(&self) -> Option<Rc<VsArray>> { None }
fn as_object_data(&self) -> Option<Rc<VsObject>> { None }
fn as_class_data(&self) -> Option<Rc<VsClass>> { None }
fn load_function(&self) -> LoadFunctionResult {
LoadFunctionResult::StackFrame((self.make_frame)())
}
fn sub(&self, _key: Val) -> Val {
std::panic!("Not implemented");
}
fn submov(&mut self, _key: Val, _value: Val) {
std::panic!("Not implemented: exceptions");
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\x1b[36m[Function]\x1b[39m")
}
fn codify(&self) -> String {
"function() { [native code] }".into()
}
}

View File

@@ -0,0 +1,54 @@
use std::rc::Rc;
use super::vs_value::{
Val,
VsType,
ValTrait,
LoadFunctionResult,
};
use super::vs_object::VsObject;
use super::vs_array::VsArray;
use super::vs_class::VsClass;
pub struct NativeFunction {
pub fn_: fn(this: &mut Val, params: Vec<Val>) -> Val,
}
impl ValTrait for NativeFunction {
fn typeof_(&self) -> VsType { VsType::Function }
fn val_to_string(&self) -> String { "function() { [native code] }".to_string() }
fn to_number(&self) -> f64 { f64::NAN }
fn to_index(&self) -> Option<usize> { None }
fn is_primitive(&self) -> bool { false }
fn to_primitive(&self) -> Val { Val::String(Rc::new(self.val_to_string())) }
fn is_truthy(&self) -> bool { true }
fn is_nullish(&self) -> bool { false }
fn bind(&self, _params: Vec<Val>) -> Option<Val> {
std::panic!("Not implemented");
}
fn as_array_data(&self) -> Option<Rc<VsArray>> { None }
fn as_object_data(&self) -> Option<Rc<VsObject>> { None }
fn as_class_data(&self) -> Option<Rc<VsClass>> { None }
fn load_function(&self) -> LoadFunctionResult {
LoadFunctionResult::NativeFunction(self.fn_)
}
fn sub(&self, _key: Val) -> Val {
std::panic!("Not implemented");
}
fn submov(&mut self, _key: Val, _value: Val) {
std::panic!("Not implemented: exceptions");
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\x1b[36m[Function]\x1b[39m")
}
fn codify(&self) -> String {
"function() { [native code] }".into()
}
}

View File

@@ -0,0 +1,352 @@
use std::rc::Rc;
use super::vs_value::Val;
use super::vs_value::ValTrait;
use super::vs_value::VsType;
pub fn op_plus(left: Val, right: Val) -> Val {
let left_prim = left.to_primitive();
let right_prim = right.to_primitive();
if left_prim.typeof_() == VsType::String || right_prim.typeof_() == VsType::String {
return Val::String(Rc::new(left_prim.val_to_string() + &right_prim.val_to_string()));
}
return Val::Number(left_prim.to_number() + right_prim.to_number());
}
pub fn op_unary_plus(input: Val) -> Val {
return Val::Number(input.to_number());
}
pub fn op_minus(left: Val, right: Val) -> Val {
return Val::Number(left.to_number() - right.to_number());
}
pub fn op_unary_minus(input: Val) -> Val {
return Val::Number(-input.to_number());
}
pub fn op_mul(left: Val, right: Val) -> Val {
return Val::Number(left.to_number() * right.to_number());
}
pub fn op_div(left: Val, right: Val) -> Val {
return Val::Number(left.to_number() / right.to_number());
}
pub fn op_mod(left: Val, right: Val) -> Val {
return Val::Number(left.to_number() % right.to_number());
}
pub fn op_exp(left: Val, right: Val) -> Val {
return Val::Number(left.to_number().powf(right.to_number()));
}
pub fn op_eq(left: Val, right: Val) -> Val {
if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number {
std::panic!("Not implemented");
}
return Val::Bool(left.to_number() == right.to_number());
}
pub fn op_ne(left: Val, right: Val) -> Val {
if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number {
std::panic!("Not implemented");
}
return Val::Bool(left.to_number() != right.to_number());
}
pub fn op_triple_eq_impl(left: Val, right: Val) -> bool {
let type_ = left.typeof_();
if right.typeof_() != type_ {
return false;
}
match type_ {
VsType::Undefined | VsType::Null => {
return true;
}
_ => {},
};
return match (left, right) {
(Val::Number(lnum), Val::Number(rnum)) => lnum == rnum,
(Val::String(lstr), Val::String(rstr)) => lstr == rstr,
_ => std::panic!("Not implemented"),
};
}
pub fn op_triple_eq(left: Val, right: Val) -> Val {
return Val::Bool(op_triple_eq_impl(left, right));
}
pub fn op_triple_ne(left: Val, right: Val) -> Val {
return Val::Bool(!op_triple_eq_impl(left, right));
}
pub fn op_and(left: Val, right: Val) -> Val {
return if left.is_truthy() {
right
} else {
left
};
}
pub fn op_or(left: Val, right: Val) -> Val {
return if left.is_truthy() {
left
} else {
right
};
}
pub fn op_not(input: Val) -> Val {
return Val::Bool(!input.is_truthy());
}
pub fn op_less(left: Val, right: Val) -> Val {
let left_type = left.typeof_();
let right_type = right.typeof_();
if left_type == VsType::Undefined || right_type == VsType::Undefined {
return Val::Bool(false);
}
if left_type != VsType::Number || right_type != VsType::Number {
std::panic!("Not implemented");
}
return Val::Bool(left.to_number() < right.to_number());
}
pub fn op_less_eq(left: Val, right: Val) -> Val {
if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number {
std::panic!("Not implemented");
}
return Val::Bool(left.to_number() <= right.to_number());
}
pub fn op_greater(left: Val, right: Val) -> Val {
if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number {
std::panic!("Not implemented");
}
return Val::Bool(left.to_number() > right.to_number());
}
pub fn op_greater_eq(left: Val, right: Val) -> Val {
if left.typeof_() != VsType::Number || right.typeof_() != VsType::Number {
std::panic!("Not implemented");
}
return Val::Bool(left.to_number() >= right.to_number());
}
pub fn op_nullish_coalesce(left: Val, right: Val) -> Val {
return if left.is_nullish() {
right
} else {
left
};
}
pub fn op_optional_chain(left: Val, right: Val) -> Val {
return match left {
Val::Undefined => Val::Undefined,
Val::Null => Val::Undefined,
_ => op_sub(left, right),
};
}
fn to_i32(x: f64) -> i32 {
if x == f64::INFINITY {
return 0;
}
let int1 = (x.trunc() as i64) & 0xffffffff;
return int1 as i32;
}
pub fn to_u32(x: f64) -> u32 {
if x == f64::INFINITY {
return 0;
}
let int1 = (x.trunc() as i64) & 0xffffffff;
return int1 as u32;
}
pub fn op_bit_and(left: Val, right: Val) -> Val {
let res_i32 = to_i32(left.to_number()) & to_i32(right.to_number());
return Val::Number(res_i32 as f64);
}
pub fn op_bit_or(left: Val, right: Val) -> Val {
let res_i32 = to_i32(left.to_number()) | to_i32(right.to_number());
return Val::Number(res_i32 as f64);
}
pub fn op_bit_not(input: Val) -> Val {
let res_i32 = !to_i32(input.to_number());
return Val::Number(res_i32 as f64);
}
pub fn op_bit_xor(left: Val, right: Val) -> Val {
let res_i32 = to_i32(left.to_number()) ^ to_i32(right.to_number());
return Val::Number(res_i32 as f64);
}
pub fn op_left_shift(left: Val, right: Val) -> Val {
let res_i32 = to_i32(left.to_number()) << (to_u32(right.to_number()) & 0x1f);
return Val::Number(res_i32 as f64);
}
pub fn op_right_shift(left: Val, right: Val) -> Val {
let res_i32 = to_i32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f);
return Val::Number(res_i32 as f64);
}
pub fn op_right_shift_unsigned(left: Val, right: Val) -> Val {
let res_u32 = to_u32(left.to_number()) >> (to_u32(right.to_number()) & 0x1f);
return Val::Number(res_u32 as f64);
}
pub fn op_typeof(input: Val) -> Val {
use VsType::*;
return Val::String(Rc::new(match input.typeof_() {
Undefined => "undefined".to_string(),
Null => "object".to_string(),
Bool => "boolean".to_string(),
Number => "number".to_string(),
String => "string".to_string(),
Array => "object".to_string(),
Object => "object".to_string(),
Function => "function".to_string(),
Class => "function".to_string(),
}));
}
pub fn op_instance_of(_left: Val, _right: Val) -> Val {
std::panic!("Not implemented: op_instance_of");
}
pub fn op_in(_left: Val, _right: Val) -> Val {
std::panic!("Not implemented: op_in");
}
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 match right.val_to_string().as_str() == "length" {
true => Val::Number(string_data.len() as f64),
false => 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 => {
// FIXME: Inefficient val_to_string() that gets duplicated
// when subscripting the object
if right.val_to_string() == "length" {
return Val::Number(array_data.elements.len() as f64);
}
return array_data.object.sub(right);
}
Some(i) => i,
};
if right_index >= array_data.elements.len() {
return Val::Undefined;
}
let res = array_data.elements[right_index].clone();
return match res {
Val::Void => Val::Undefined,
_ => res,
};
},
Val::Object(object_data) => {
return object_data.sub(right);
},
Val::Function(_) => Val::Undefined,
Val::Class(_) => Val::Undefined,
Val::Static(s) => s.sub(right),
Val::Custom(custom_data) => custom_data.sub(right),
}
}
pub fn op_submov(target: &mut Val, subscript: Val, value: Val) {
match target {
Val::Void => std::panic!("Shouldn't happen"),
Val::Undefined => std::panic!("Not implemented: exceptions"),
Val::Null => std::panic!("Not implemented: exceptions"),
Val::Bool(_) => std::panic!("Not implemented: exceptions"),
Val::Number(_) => std::panic!("Not implemented: exceptions"),
Val::String(_) => std::panic!("Not implemented: exceptions"),
Val::Array(array_data) => {
let subscript_index = match subscript.to_index() {
None => std::panic!("Not implemented: non-uint array subscript assignment"),
Some(i) => i,
};
let array_data_mut = Rc::make_mut(array_data);
if subscript_index < array_data_mut.elements.len() {
array_data_mut.elements[subscript_index] = value;
} else {
if subscript_index - array_data_mut.elements.len() > 100 {
std::panic!("Not implemented: Sparse arrays");
}
while subscript_index > array_data_mut.elements.len() {
array_data_mut.elements.push(Val::Void);
}
array_data_mut.elements.push(value);
}
},
Val::Object(object_data) => {
Rc::make_mut(object_data).string_map.insert(subscript.val_to_string(), value);
},
Val::Function(_) => std::panic!("Not implemented: function subscript assignment"),
Val::Class(_) => std::panic!("Not implemented: class subscript assignment"),
Val::Static(_) => std::panic!("Not implemented: exceptions"),
Val::Custom(_) => std::panic!("Not implemented"),
}
}

View File

@@ -0,0 +1,23 @@
use super::vs_value::Val;
pub type StackFrame = Box<dyn StackFrameTrait>;
#[derive(Clone)]
pub struct CallResult {
pub return_: Val,
pub this: Val,
}
pub enum FrameStepResult {
Continue,
Pop(CallResult),
Push(StackFrame),
}
pub trait StackFrameTrait {
fn write_this(&mut self, this: Val);
fn write_param(&mut self, param: Val);
fn step(&mut self) -> FrameStepResult;
fn apply_call_result(&mut self, call_result: CallResult);
fn get_call_result(&mut self) -> CallResult;
}

View File

@@ -0,0 +1,74 @@
use std::rc::Rc;
use super::bytecode_decoder::BytecodeDecoder;
use super::first_stack_frame::FirstStackFrame;
use super::stack_frame::{FrameStepResult, StackFrame};
use super::vs_value::{LoadFunctionResult, Val, ValTrait};
pub struct VirtualMachine {
pub frame: StackFrame,
pub stack: Vec<StackFrame>,
}
impl VirtualMachine {
pub fn run(&mut self, bytecode: &Rc<Vec<u8>>, params: &[String]) -> Val {
let mut bd = BytecodeDecoder {
data: bytecode.clone(),
pos: 0,
};
let main_fn = bd.decode_val(&Vec::new());
let mut frame = match main_fn.load_function() {
LoadFunctionResult::StackFrame(f) => f,
_ => std::panic!("bytecode does start with function"),
};
for p in params {
frame.write_param(Val::String(Rc::new(p.clone())));
}
self.push(frame);
while self.stack.len() > 0 {
self.step();
}
return self.frame.get_call_result().return_;
}
pub fn new() -> VirtualMachine {
let mut registers: Vec<Val> = Vec::with_capacity(2);
registers.push(Val::Undefined);
registers.push(Val::Undefined);
return VirtualMachine {
frame: Box::new(FirstStackFrame::new()),
stack: Default::default(),
};
}
pub fn step(&mut self) {
match self.frame.step() {
FrameStepResult::Continue => {}
FrameStepResult::Pop(call_result) => {
self.pop();
self.frame.apply_call_result(call_result);
}
FrameStepResult::Push(new_frame) => {
self.push(new_frame);
}
};
}
pub fn push(&mut self, mut frame: StackFrame) {
std::mem::swap(&mut self.frame, &mut frame);
self.stack.push(frame);
}
pub fn pop(&mut self) {
// This name is accurate after the swap
let mut old_frame = self.stack.pop().unwrap();
std::mem::swap(&mut self.frame, &mut old_frame);
}
}

View File

@@ -0,0 +1,696 @@
use std::rc::Rc;
use std::cmp::{min, max};
use super::vs_value::{
Val,
VsType,
ValTrait,
LoadFunctionResult,
};
use super::vs_object::VsObject;
use super::vs_class::VsClass;
use super::native_function::NativeFunction;
use super::operations::op_triple_eq_impl;
use super::array_higher_functions::array_map::MAP;
use super::array_higher_functions::array_every::EVERY;
use super::array_higher_functions::array_some::SOME;
use super::array_higher_functions::array_filter::FILTER;
use super::array_higher_functions::array_find::FIND;
use super::array_higher_functions::array_find_index::FIND_INDEX;
use super::array_higher_functions::array_flat_map::FLAT_MAP;
use super::array_higher_functions::array_reduce::REDUCE;
use super::array_higher_functions::array_reduce_right::REDUCE_RIGHT;
use super::array_higher_functions::array_sort::SORT;
#[derive(Clone)]
pub struct VsArray {
pub elements: Vec<Val>,
pub object: VsObject,
}
impl VsArray {
pub fn from(vals: Vec<Val>) -> VsArray {
return VsArray {
elements: vals,
object: VsObject {
string_map: Default::default(),
prototype: Some(Val::Static(&ARRAY_PROTOTYPE)),
},
};
}
}
pub struct ArrayPrototype {}
static ARRAY_PROTOTYPE: ArrayPrototype = ArrayPrototype {};
impl ValTrait for ArrayPrototype {
fn typeof_(&self) -> VsType { VsType::Object }
fn val_to_string(&self) -> String { "".to_string() }
fn to_number(&self) -> f64 { 0_f64 }
fn to_index(&self) -> Option<usize> { None }
fn is_primitive(&self) -> bool { false }
fn to_primitive(&self) -> Val { Val::String(Rc::new("".to_string())) }
fn is_truthy(&self) -> bool { true }
fn is_nullish(&self) -> bool { false }
fn bind(&self, _params: Vec<Val>) -> Option<Val> { None }
fn as_array_data(&self) -> Option<Rc<VsArray>> { None }
fn as_object_data(&self) -> Option<Rc<VsObject>> { None }
fn as_class_data(&self) -> Option<Rc<VsClass>> { None }
fn load_function(&self) -> LoadFunctionResult {
LoadFunctionResult::NotAFunction
}
fn sub(&self, key: Val) -> Val {
match key.val_to_string().as_str() {
"at" => Val::Static(&AT),
"concat" => Val::Static(&CONCAT),
"copyWithin" => Val::Static(&COPY_WITHIN),
"entries" => Val::Static(&ENTRIES),
"every" => Val::Static(&EVERY),
"fill" => Val::Static(&FILL),
"filter" => Val::Static(&FILTER),
"find" => Val::Static(&FIND),
"findIndex" => Val::Static(&FIND_INDEX),
"flat" => Val::Static(&FLAT),
"flatMap" => Val::Static(&FLAT_MAP),
// forEach: Not included because it cannot work as expected in ValueScript
// (Use a for..of loop)
"includes" => Val::Static(&INCLUDES),
"indexOf" => Val::Static(&INDEX_OF),
"join" => Val::Static(&JOIN),
"keys" => Val::Static(&KEYS),
"lastIndexOf" => Val::Static(&LAST_INDEX_OF),
"map" => Val::Static(&MAP),
"pop" => Val::Static(&POP),
"push" => Val::Static(&PUSH),
"reduce" => Val::Static(&REDUCE),
"reduceRight" => Val::Static(&REDUCE_RIGHT),
"reverse" => Val::Static(&REVERSE),
"shift" => Val::Static(&SHIFT),
"slice" => Val::Static(&SLICE),
"some" => Val::Static(&SOME),
"sort" => Val::Static(&SORT),
"splice" => Val::Static(&SPLICE),
"toLocaleString" => Val::Static(&TO_LOCALE_STRING),
"toString" => Val::Static(&TO_STRING),
"unshift" => Val::Static(&UNSHIFT),
"values" => Val::Static(&VALUES),
_ => Val::Undefined,
}
}
fn submov(&mut self, _key: Val, _value: Val) {
std::panic!("Not implemented: exceptions");
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\x1b[36m[Array Prototype]\x1b[39m")
}
fn codify(&self) -> String {
"Array.prototype".into()
}
}
fn to_unchecked_wrapping_index(index: &Val, len: usize) -> isize {
let index_num = index.to_number();
let mut floored_index = index_num.trunc();
let f64_len = len as f64;
if floored_index < 0_f64 {
floored_index += f64_len;
}
// TODO: Investigate potential pitfalls for arrays with length exceeding max
// isize.
return floored_index as isize;
}
fn to_wrapping_index_clamped(index: &Val, len: usize) -> isize {
let wrapping_index = to_unchecked_wrapping_index(index, len);
if wrapping_index < 0 {
return 0;
}
if wrapping_index > len as isize {
// len-1 would be a mistake. The end of the array is a meaningful index even
// though there is no data there.
return len as isize;
}
return wrapping_index;
}
fn to_wrapping_index(index: Option<&Val>, len: usize) -> Option<usize> {
let unchecked = match index {
None => { return None; }
Some(i) => to_unchecked_wrapping_index(i, len),
};
if unchecked < 0 || unchecked as usize >= len {
return None;
}
return Some(unchecked as usize);
}
static AT: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => match to_wrapping_index(
params.get(0),
array_data.elements.len(),
) {
None => Val::Undefined,
Some(i) => array_data.elements[i].clone(),
},
_ => std::panic!("Not implemented: exceptions/array indirection")
}
}
};
static CONCAT: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let mut new_array = array_data.as_ref().clone();
for p in params {
match &p.as_array_data() {
None => {
new_array.elements.push(p);
},
Some(p_array_data) => {
for elem in &p_array_data.elements {
new_array.elements.push(elem.clone());
}
},
}
}
return Val::Array(Rc::new(new_array));
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static COPY_WITHIN: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let array_data_mut = Rc::make_mut(array_data);
let ulen = array_data_mut.elements.len();
if ulen > isize::MAX as usize {
std::panic!("Not implemented: array len exceeds isize");
}
let mut target = match params.get(0) {
None => 0,
Some(p) => to_wrapping_index_clamped(p, ulen),
};
let mut start = match params.get(1) {
None => 0,
Some(p) => to_wrapping_index_clamped(p, ulen),
};
let ilen = ulen as isize;
let mut end = match params.get(2) {
None => ilen,
// FIXME: undefined -> len (and others in this file)
Some(p) => to_wrapping_index_clamped(p, ulen),
};
let copy_len = end - start;
if copy_len <= 0 {
return this.clone();
}
if target <= start || target >= end {
while target < ilen && start < end {
array_data_mut.elements[target as usize] =
array_data_mut.elements[start as usize].clone()
;
target += 1;
start += 1;
}
} else {
// The target is after the start. If we copied from start to target
// and worked forwards we'd overwrite the values we needed later.
// Instead we simply do the copies in the reverse order.
target += copy_len - 1;
end -= 1;
if target >= ilen {
end -= target - ilen + 1;
target = ilen - 1;
}
while target >= 0 && end >= start {
array_data_mut.elements[target as usize] =
array_data_mut.elements[end as usize].clone()
;
target -= 1;
end -= 1;
}
}
return this.clone();
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static ENTRIES: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(_array_data) => {
std::panic!("Not implemented: ENTRIES");
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static FILL: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let array_data_mut = Rc::make_mut(array_data);
let len = array_data_mut.elements.len();
let fill_val = params.get(0).unwrap_or(&Val::Undefined);
let start = match params.get(1) {
None => 0,
Some(v) => to_wrapping_index_clamped(v, len),
};
let end = match params.get(2) {
None => len as isize,
Some(v) => to_wrapping_index_clamped(v, len),
};
for i in start..end {
array_data_mut.elements[i as usize] = fill_val.clone();
}
return this.clone();
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static FLAT: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
if params.len() > 0 {
std::panic!("Not implemented: .flat depth parameter");
}
let mut new_elems = Vec::<Val>::new();
for el in &array_data.elements {
match &el.as_array_data() {
None => {
new_elems.push(el.clone());
},
Some(p_array_data) => {
for elem in &p_array_data.elements {
new_elems.push(elem.clone());
}
},
}
}
return Val::Array(Rc::new(VsArray::from(new_elems)));
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static INCLUDES: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
for elem in &array_data.elements {
if op_triple_eq_impl(elem.clone(), search_param.clone()) {
return Val::Bool(true);
}
}
return Val::Bool(false);
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static INDEX_OF: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
for i in 0..array_data.elements.len() {
if op_triple_eq_impl(
array_data.elements[i].clone(),
search_param.clone(),
) {
return Val::Number(i as f64);
}
}
return Val::Number(-1_f64);
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static JOIN: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(vals) => {
if vals.elements.len() == 0 {
return Val::String(Rc::new("".to_string()));
}
if vals.elements.len() == 1 {
return Val::String(Rc::new(vals.elements[0].val_to_string()));
}
let separator = params.get(0).unwrap_or(&Val::Undefined);
let separator_str = match separator.typeof_() {
VsType::Undefined => ",".to_string(),
_ => separator.val_to_string(),
};
let mut iter = vals.elements.iter();
let mut res = iter.next().unwrap().val_to_string();
for val in iter {
res += &separator_str;
match val.typeof_() {
VsType::Undefined => {},
_ => { res += &val.val_to_string(); },
};
}
return Val::String(Rc::new(res));
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static KEYS: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(_array_data) => {
std::panic!("Not implemented: KEYS");
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static LAST_INDEX_OF: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
for i in (0..array_data.elements.len()).rev() {
if op_triple_eq_impl(
array_data.elements[i].clone(),
search_param.clone(),
) {
return Val::Number(i as f64);
}
}
return Val::Number(-1_f64);
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static POP: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
if array_data.elements.len() == 0 {
return Val::Undefined;
}
let array_data_mut = Rc::make_mut(array_data);
let removed_el = array_data_mut.elements.remove(
array_data_mut.elements.len() - 1,
);
return match removed_el {
Val::Void => Val::Undefined,
_ => removed_el,
};
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static PUSH: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let array_data_mut = Rc::make_mut(array_data);
for p in params {
array_data_mut.elements.push(p);
}
return Val::Number(array_data_mut.elements.len() as f64);
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static REVERSE: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
if array_data.elements.len() == 0 {
// Treating this as an edge case because rust protects us from
// underflow when computing last below.
return this.clone();
}
let array_data_mut = Rc::make_mut(array_data);
let last = array_data_mut.elements.len() - 1;
for i in 0..(array_data_mut.elements.len() / 2) {
let tmp = array_data_mut.elements[i].clone();
array_data_mut.elements[i] = array_data_mut.elements[last - i].clone();
array_data_mut.elements[last - i] = tmp;
}
return this.clone();
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static SHIFT: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
if array_data.elements.len() == 0 {
return Val::Undefined;
}
let array_data_mut = Rc::make_mut(array_data);
return array_data_mut.elements.remove(0);
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static SLICE: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let mut new_elems = Vec::<Val>::new();
let start = match params.get(0) {
None => 0,
Some(v) => to_wrapping_index_clamped(v, array_data.elements.len()),
};
let end = match params.get(1) {
None => array_data.elements.len() as isize,
Some(v) => to_wrapping_index_clamped(v, array_data.elements.len()),
};
for i in start..end {
new_elems.push(array_data.elements[i as usize].clone());
}
return Val::Array(Rc::new(VsArray::from(new_elems)));
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static SPLICE: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let array_data_mut = Rc::make_mut(array_data);
let len = array_data_mut.elements.len();
let start = match params.get(0) {
None => 0,
Some(v) => to_wrapping_index_clamped(v, len),
} as usize;
let delete_count_f64 = match params.get(1) {
None => len as f64,
Some(v) => match v.typeof_() {
VsType::Undefined => len as f64,
_ => v.to_number(),
},
};
let delete_count = match delete_count_f64 < 0_f64 {
true => 0,
false => min(delete_count_f64.floor() as usize, len - start),
};
let mut deleted_elements = Vec::<Val>::new();
for i in 0..delete_count {
deleted_elements.push(array_data_mut.elements[start + i].clone());
}
let insert_len = max(2, params.len()) - 2;
let replace_len = min(insert_len, delete_count);
if insert_len > replace_len {
for i in 0..replace_len {
array_data_mut.elements[start + i] = params[i + 2].clone();
}
let gap = insert_len - replace_len;
for _ in 0..gap {
array_data_mut.elements.push(Val::Void);
}
for i in ((start + replace_len)..len).rev() {
array_data_mut.elements[i + gap] = array_data_mut.elements[i].clone();
}
for i in replace_len..insert_len {
array_data_mut.elements[start + i] = params[i + 2].clone();
}
} else {
for i in 0..insert_len {
array_data_mut.elements[start + i] = params[i + 2].clone();
}
let gap = delete_count - insert_len;
if gap != 0 {
for i in (start + insert_len)..(len - gap) {
array_data_mut.elements[i] = array_data_mut.elements[i + gap].clone();
}
for _ in 0..gap {
array_data_mut.elements.pop();
}
}
}
return Val::Array(Rc::new(VsArray::from(deleted_elements)));
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static TO_LOCALE_STRING: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(_array_data) => {
std::panic!("Not implemented: TO_LOCALE_STRING");
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};
static TO_STRING: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
return Val::String(Rc::new(this.val_to_string()));
}
};
static UNSHIFT: NativeFunction = NativeFunction {
fn_: |this: &mut Val, params: Vec<Val>| -> Val {
match this {
Val::Array(array_data) => {
let array_data_mut = Rc::make_mut(array_data);
let mut i = 0;
for p in params {
array_data_mut.elements.insert(i, p);
i += 1;
}
return Val::Number(array_data_mut.elements.len() as f64);
},
_ => std::panic!("Not implemented: exceptions/array indirection")
};
}
};
static VALUES: NativeFunction = NativeFunction {
fn_: |this: &mut Val, _params: Vec<Val>| -> Val {
match this {
Val::Array(_array_data) => {
std::panic!("Not implemented: VALUES");
},
_ => std::panic!("Not implemented: exceptions/array indirection"),
};
}
};

View File

@@ -0,0 +1,8 @@
use super::vs_value::Val;
pub struct VsClass {
pub constructor: Val,
pub instance_prototype: Val,
}
impl VsClass {}

View File

@@ -0,0 +1,59 @@
use std::rc::Rc;
use super::vs_value::Val;
use super::bytecode_stack_frame::BytecodeStackFrame;
use super::bytecode_decoder::BytecodeDecoder;
use super::stack_frame::StackFrame;
pub struct VsFunction {
pub bytecode: Rc<Vec<u8>>,
pub register_count: usize,
pub parameter_count: usize,
pub start: usize,
pub binds: Vec<Val>,
}
impl VsFunction {
pub fn bind(&self, params: Vec<Val>) -> VsFunction {
let mut new_binds = self.binds.clone();
for p in params {
new_binds.push(p);
}
return VsFunction {
bytecode: self.bytecode.clone(),
register_count: self.register_count,
parameter_count: self.parameter_count,
start: self.start,
binds: new_binds,
};
}
pub fn make_frame(&self) -> StackFrame {
let mut registers: Vec<Val> = Vec::with_capacity(self.register_count - 1);
registers.push(Val::Undefined);
registers.push(Val::Undefined);
for bind_val in &self.binds {
registers.push(bind_val.clone());
}
while registers.len() < registers.capacity() {
registers.push(Val::Undefined);
}
return Box::new(BytecodeStackFrame {
decoder: BytecodeDecoder {
data: self.bytecode.clone(),
pos: self.start,
},
registers: registers,
param_start: self.binds.len() + 2,
param_end: self.parameter_count + 2,
this_target: None,
return_target: None,
});
}
}

View File

@@ -0,0 +1,22 @@
use std::collections::BTreeMap;
use super::vs_value::{Val, ValTrait};
use super::operations::op_sub;
#[derive(Clone, Default)]
pub struct VsObject {
pub string_map: BTreeMap<String, Val>,
pub prototype: Option<Val>,
}
impl VsObject {
pub fn sub(&self, key: Val) -> Val {
return match self.string_map.get(&key.val_to_string()) {
Some(val) => val.clone(),
None => match &self.prototype {
Some(prototype) => op_sub(prototype.clone(), key),
None => Val::Undefined,
},
};
}
}

View File

@@ -0,0 +1,147 @@
use std::rc::Rc;
use std::cell::RefCell;
use super::vs_value::{Val, ValTrait, VsType, LoadFunctionResult};
use super::bytecode_decoder::{BytecodeDecoder, BytecodeType};
use super::vs_object::VsObject;
use super::vs_array::VsArray;
use super::vs_class::VsClass;
pub struct VsPointer {
bytecode: Rc<Vec<u8>>,
pos: usize,
resolved: RefCell<Option<Val>>,
}
impl VsPointer {
pub fn new(bytecode: &Rc<Vec<u8>>, pos: usize) -> Val {
return Val::Custom(Rc::new(VsPointer {
bytecode: bytecode.clone(),
pos: pos,
resolved: RefCell::new(None),
}));
}
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;
}
}
impl ValTrait for VsPointer {
fn typeof_(&self) -> VsType {
let mut bd = BytecodeDecoder {
data: self.bytecode.clone(),
pos: self.pos,
};
return match bd.decode_type() {
BytecodeType::End => std::panic!("Invalid: pointer to end"),
BytecodeType::Void => std::panic!("Invalid: pointer to void"),
BytecodeType::Undefined => VsType::Undefined,
BytecodeType::Null => VsType::Null,
BytecodeType::False => VsType::Bool,
BytecodeType::True => VsType::Bool,
BytecodeType::SignedByte => VsType::Number,
BytecodeType::Number => VsType::Number,
BytecodeType::String => VsType::String,
BytecodeType::Array => VsType::Array,
BytecodeType::Object => VsType::Object,
BytecodeType::Function => VsType::Function,
BytecodeType::Pointer => std::panic!("Invalid: pointer to pointer"),
BytecodeType::Register => std::panic!("Invalid: pointer to register"),
BytecodeType::Builtin => std::panic!("Invalid: pointer to builtin"),
BytecodeType::Class => VsType::Class,
}
}
fn val_to_string(&self) -> String {
return self.resolve().val_to_string();
}
fn to_number(&self) -> f64 {
return self.resolve().to_number();
}
fn to_index(&self) -> Option<usize> {
return self.resolve().to_index();
}
fn is_primitive(&self) -> bool {
return match self.typeof_() {
VsType::Undefined => true,
VsType::Null => true,
VsType::Bool => true,
VsType::Number => true,
VsType::String => true,
VsType::Array => false,
VsType::Object => false,
VsType::Function => false,
VsType::Class => false,
}
}
fn to_primitive(&self) -> Val {
return self.resolve().to_primitive();
}
fn bind(&self, params: Vec<Val>) -> Option<Val> {
return self.resolve().bind(params);
}
fn is_truthy(&self) -> bool {
return self.resolve().is_truthy();
}
fn is_nullish(&self) -> bool {
return self.resolve().is_nullish();
}
fn as_array_data(&self) -> Option<Rc<VsArray>> {
return self.resolve().as_array_data();
}
fn as_object_data(&self) -> Option<Rc<VsObject>> {
return self.resolve().as_object_data();
}
fn as_class_data(&self) -> Option<Rc<VsClass>> {
return self.resolve().as_class_data();
}
fn load_function(&self) -> LoadFunctionResult {
return self.resolve().load_function();
}
fn sub(&self, subscript: Val) -> Val {
return self.resolve().sub(subscript);
}
fn submov(&mut self, _subscript: Val, _value: Val) {
std::panic!("Not implemented");
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.resolve().fmt(f)
}
fn codify(&self) -> String {
self.resolve().codify()
}
}

View File

@@ -0,0 +1,470 @@
use std::rc::Rc;
use std::str::FromStr;
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;
#[derive(Clone)]
pub enum Val {
Void,
Undefined,
Null,
Bool(bool),
Number(f64),
String(Rc<String>),
Array(Rc<VsArray>),
Object(Rc<VsObject>),
Function(Rc<VsFunction>),
Class(Rc<VsClass>),
Static(&'static dyn ValTrait),
Custom(Rc<dyn ValTrait>),
}
#[derive(PartialEq, Debug)]
pub enum VsType {
Undefined,
Null,
Bool,
Number,
String,
Array,
Object,
Function,
Class,
}
pub enum LoadFunctionResult {
NotAFunction,
StackFrame(StackFrame),
NativeFunction(fn(this: &mut Val, params: Vec<Val>) -> Val),
}
pub trait ValTrait {
fn typeof_(&self) -> VsType;
fn val_to_string(&self) -> String;
fn to_number(&self) -> f64;
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 bind(&self, params: Vec<Val>) -> Option<Val>;
fn as_array_data(&self) -> Option<Rc<VsArray>>;
fn as_object_data(&self) -> Option<Rc<VsObject>>;
fn as_class_data(&self) -> Option<Rc<VsClass>>;
fn load_function(&self) -> LoadFunctionResult;
fn sub(&self, key: Val) -> Val;
fn submov(&mut self, key: Val, value: Val);
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
fn codify(&self) -> String;
}
impl ValTrait for Val {
fn typeof_(&self) -> VsType {
use Val::*;
return match self {
Void => VsType::Undefined,
Undefined => VsType::Undefined,
Null => VsType::Null,
Bool(_) => VsType::Bool,
Number(_) => VsType::Number,
String(_) => VsType::String,
Array(_) => VsType::Array,
Object(_) => VsType::Object,
Function(_) => VsType::Function,
Class(_) => VsType::Class,
Static(val) => val.typeof_(),
Custom(val) => val.typeof_(),
};
}
fn val_to_string(&self) -> String {
use Val::*;
return match self {
Void => "".to_string(),
Undefined => "undefined".to_string(),
Null => "null".to_string(),
Bool(b) => b.to_string(),
Number(x) => x.to_string(), // TODO: Match js's number string format
String(s) => s.to_string(),
Array(vals) => {
if vals.elements.len() == 0 {
"".to_string()
} else if vals.elements.len() == 1 {
vals.elements[0].val_to_string()
} else {
let mut iter = vals.elements.iter();
let mut res = iter.next().unwrap().val_to_string();
for val in iter {
res += ",";
match val.typeof_() {
VsType::Undefined => {}
_ => {
res += &val.val_to_string();
}
};
}
res
}
}
Object(_) => "[object Object]".to_string(),
Function(_) => "[function]".to_string(),
Class(_) => "[class]".to_string(),
Static(val) => val.val_to_string(),
Custom(val) => val.val_to_string(),
};
}
fn to_number(&self) -> f64 {
use Val::*;
return match self {
Void => f64::NAN,
Undefined => f64::NAN,
Null => 0_f64,
Bool(b) => *b as u8 as f64,
Number(x) => *x,
String(s) => f64::from_str(s).unwrap_or(f64::NAN),
Array(vals) => match vals.elements.len() {
0 => 0_f64,
1 => vals.elements[0].to_number(),
_ => f64::NAN,
},
Object(_) => f64::NAN,
Function(_) => f64::NAN,
Class(_) => f64::NAN,
Static(val) => val.to_number(),
Custom(val) => val.to_number(),
};
}
fn to_index(&self) -> Option<usize> {
use Val::*;
return match self {
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(_) => None,
Object(_) => None,
Function(_) => None,
Class(_) => None,
Static(val) => val.to_index(),
Custom(val) => val.to_index(),
};
}
fn is_primitive(&self) -> bool {
use Val::*;
return match self {
Void => true,
Undefined => true,
Null => true,
Bool(_) => true,
Number(_) => true,
String(_) => true,
Array(_) => false,
Object(_) => false,
Function(_) => false,
Class(_) => false,
Static(val) => val.is_primitive(), // TODO: false?
Custom(val) => val.is_primitive(),
};
}
fn to_primitive(&self) -> Val {
if self.is_primitive() {
return self.clone();
}
return Val::String(Rc::new(self.val_to_string()));
}
fn is_truthy(&self) -> bool {
use Val::*;
return match self {
Void => false,
Undefined => false,
Null => false,
Bool(b) => *b,
Number(x) => *x != 0_f64,
String(s) => s.len() > 0,
Array(_) => true,
Object(_) => true,
Function(_) => true,
Class(_) => true,
Static(val) => val.is_truthy(), // TODO: true?
Custom(val) => val.is_truthy(),
};
}
fn is_nullish(&self) -> bool {
use Val::*;
return match self {
Void => std::panic!("Shouldn't happen"), // TODO: Or just true?
Undefined => true,
Null => true,
Bool(_) => false,
Number(_) => false,
String(_) => false,
Array(_) => false,
Object(_) => false,
Function(_) => false,
Class(_) => false,
Static(_) => false,
Custom(val) => val.is_nullish(),
};
}
fn bind(&self, params: Vec<Val>) -> Option<Val> {
use Val::*;
return match self {
Function(f) => Some(Val::Function(Rc::new(f.bind(params)))),
Custom(val) => val.bind(params),
_ => None,
};
}
fn as_array_data(&self) -> Option<Rc<VsArray>> {
use Val::*;
return match self {
Array(a) => Some(a.clone()),
Custom(val) => val.as_array_data(),
_ => None,
};
}
fn as_object_data(&self) -> Option<Rc<VsObject>> {
use Val::*;
return match self {
Object(obj) => Some(obj.clone()),
Custom(val) => val.as_object_data(),
_ => None,
};
}
fn as_class_data(&self) -> Option<Rc<VsClass>> {
use Val::*;
return match self {
Class(class) => Some(class.clone()),
Custom(val) => val.as_class_data(),
_ => None,
};
}
fn load_function(&self) -> LoadFunctionResult {
use Val::*;
return match self {
Function(f) => LoadFunctionResult::StackFrame(f.make_frame()),
Static(s) => s.load_function(),
Custom(val) => val.load_function(),
_ => LoadFunctionResult::NotAFunction,
};
}
fn sub(&self, key: Val) -> Val {
// TODO: Avoid cloning?
return op_sub(self.clone(), key);
}
fn submov(&mut self, key: Val, value: Val) {
op_submov(self, key, value);
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
fn codify(&self) -> String {
match self {
Val::Void => "".into(),
Val::Undefined => "undefined".into(),
Val::Null => "null".into(),
Val::Bool(_) => self.val_to_string(),
Val::Number(_) => self.val_to_string(),
Val::String(str) => stringify_string(str),
Val::Array(vals) => {
if vals.elements.len() == 0 {
"[]".to_string()
} else if vals.elements.len() == 1 {
"[".to_string() + vals.elements[0].codify().as_str() + "]"
} else {
let mut iter = vals.elements.iter();
let mut res: String = "[".into();
res += iter.next().unwrap().codify().as_str();
for val in iter {
res += ",";
res += &val.codify();
}
res += "]";
res
}
}
Val::Object(object) => {
if object.string_map.len() == 0 {
return "{}".into();
}
let mut res = "{".into();
let mut first = true;
for (k, v) in &object.string_map {
if first {
first = false;
} else {
res += ",";
}
res += stringify_string(k).as_str();
res += ":";
res += v.codify().as_str();
}
res += "}";
res
}
Val::Function(_) => "() => { [unavailable] }".to_string(),
Val::Class(_) => "class { [unavailable] }".to_string(),
Val::Static(val) => val.codify(),
Val::Custom(val) => val.codify(),
}
}
}
impl std::fmt::Display for Val {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Val::Void => write!(f, "void"),
Val::Undefined => write!(f, "\x1b[90mundefined\x1b[39m"),
Val::Null => write!(f, "\x1b[1mnull\x1b[22m"),
Val::Bool(_) => write!(f, "\x1b[33m{}\x1b[39m", self.val_to_string()),
Val::Number(_) => write!(f, "\x1b[33m{}\x1b[39m", self.val_to_string()),
Val::String(_) => write!(f, "\x1b[32m{}\x1b[39m", self.codify()),
Val::Array(array) => {
if array.elements.len() == 0 {
return write!(f, "[]");
}
write!(f, "[ ").expect("Failed to write");
let mut first = true;
for elem in &array.elements {
if first {
first = false;
} else {
write!(f, ", ").expect("Failed to write");
}
write!(f, "{}", elem).expect("Failed to write");
}
write!(f, " ]")
}
Val::Object(object) => {
if object.string_map.len() == 0 {
return f.write_str("{}");
}
match f.write_str("{ ") {
Ok(_) => {}
Err(e) => {
return Err(e);
}
};
let mut first = true;
for (k, v) in &object.string_map {
if first {
first = false;
} else {
write!(f, ", ").expect("Failed to write");
}
write!(f, "{}: {}", k, v).expect("Failed to write");
}
f.write_str(" }")
}
Val::Function(_) => write!(f, "\x1b[36m[Function]\x1b[39m"),
Val::Class(_) => write!(f, "\x1b[36m[Class]\x1b[39m"),
// TODO: Improve printing these
Val::Static(s) => s.fmt(f),
Val::Custom(c) => c.fmt(f),
}
}
}
fn number_to_index(x: f64) -> Option<usize> {
if x < 0_f64 || x != x.floor() {
return None;
}
return Some(x as usize);
}
fn stringify_string(str: &String) -> String {
let mut res: String = "\"".into();
for c in str.chars() {
let escape_seq = match c {
'\r' => Some("\\r"),
'\n' => Some("\\n"),
'\t' => Some("\\t"),
'"' => Some("\\\""),
_ => None,
};
match escape_seq {
Some(seq) => {
res += seq;
}
None => {
res.push(c);
}
};
}
res += "\"";
res
}