mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-01-14 07:57:57 -05:00
Reorganize using workspaces
This commit is contained in:
23
valuescript_vm/src/array_higher_functions/array_every.rs
Normal file
23
valuescript_vm/src/array_higher_functions/array_every.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
31
valuescript_vm/src/array_higher_functions/array_filter.rs
Normal file
31
valuescript_vm/src/array_higher_functions/array_filter.rs
Normal 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)));
|
||||
}
|
||||
}
|
||||
23
valuescript_vm/src/array_higher_functions/array_find.rs
Normal file
23
valuescript_vm/src/array_higher_functions/array_find.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
36
valuescript_vm/src/array_higher_functions/array_flat_map.rs
Normal file
36
valuescript_vm/src/array_higher_functions/array_flat_map.rs
Normal 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)));
|
||||
}
|
||||
}
|
||||
28
valuescript_vm/src/array_higher_functions/array_map.rs
Normal file
28
valuescript_vm/src/array_higher_functions/array_map.rs
Normal 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)));
|
||||
}
|
||||
}
|
||||
130
valuescript_vm/src/array_higher_functions/array_mapping_frame.rs
Normal file
130
valuescript_vm/src/array_higher_functions/array_mapping_frame.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
109
valuescript_vm/src/array_higher_functions/array_reduce.rs
Normal file
109
valuescript_vm/src/array_higher_functions/array_reduce.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
119
valuescript_vm/src/array_higher_functions/array_reduce_right.rs
Normal file
119
valuescript_vm/src/array_higher_functions/array_reduce_right.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
23
valuescript_vm/src/array_higher_functions/array_some.rs
Normal file
23
valuescript_vm/src/array_higher_functions/array_some.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
290
valuescript_vm/src/array_higher_functions/array_sort.rs
Normal file
290
valuescript_vm/src/array_higher_functions/array_sort.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
12
valuescript_vm/src/array_higher_functions/mod.rs
Normal file
12
valuescript_vm/src/array_higher_functions/mod.rs
Normal 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;
|
||||
13
valuescript_vm/src/builtins.rs
Normal file
13
valuescript_vm/src/builtins.rs
Normal 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!(""),
|
||||
}
|
||||
}
|
||||
237
valuescript_vm/src/bytecode_decoder.rs
Normal file
237
valuescript_vm/src/bytecode_decoder.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
439
valuescript_vm/src/bytecode_stack_frame.rs
Normal file
439
valuescript_vm/src/bytecode_stack_frame.rs
Normal 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),
|
||||
}
|
||||
62
valuescript_vm/src/debug.rs
Normal file
62
valuescript_vm/src/debug.rs
Normal 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;
|
||||
}
|
||||
};
|
||||
39
valuescript_vm/src/first_stack_frame.rs
Normal file
39
valuescript_vm/src/first_stack_frame.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
105
valuescript_vm/src/instruction.rs
Normal file
105
valuescript_vm/src/instruction.rs
Normal 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
22
valuescript_vm/src/lib.rs
Normal 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
350
valuescript_vm/src/math.rs
Normal 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());
|
||||
}
|
||||
};
|
||||
55
valuescript_vm/src/native_frame_function.rs
Normal file
55
valuescript_vm/src/native_frame_function.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
54
valuescript_vm/src/native_function.rs
Normal file
54
valuescript_vm/src/native_function.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
352
valuescript_vm/src/operations.rs
Normal file
352
valuescript_vm/src/operations.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
23
valuescript_vm/src/stack_frame.rs
Normal file
23
valuescript_vm/src/stack_frame.rs
Normal 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;
|
||||
}
|
||||
74
valuescript_vm/src/virtual_machine.rs
Normal file
74
valuescript_vm/src/virtual_machine.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
696
valuescript_vm/src/vs_array.rs
Normal file
696
valuescript_vm/src/vs_array.rs
Normal 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(©_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"),
|
||||
};
|
||||
}
|
||||
};
|
||||
8
valuescript_vm/src/vs_class.rs
Normal file
8
valuescript_vm/src/vs_class.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use super::vs_value::Val;
|
||||
|
||||
pub struct VsClass {
|
||||
pub constructor: Val,
|
||||
pub instance_prototype: Val,
|
||||
}
|
||||
|
||||
impl VsClass {}
|
||||
59
valuescript_vm/src/vs_function.rs
Normal file
59
valuescript_vm/src/vs_function.rs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
22
valuescript_vm/src/vs_object.rs
Normal file
22
valuescript_vm/src/vs_object.rs
Normal 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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
147
valuescript_vm/src/vs_pointer.rs
Normal file
147
valuescript_vm/src/vs_pointer.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
470
valuescript_vm/src/vs_value.rs
Normal file
470
valuescript_vm/src/vs_value.rs
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user