mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-04-18 03:00:27 -04:00
Simplify native_fn
This commit is contained in:
@@ -2,7 +2,7 @@ use num_bigint::BigInt;
|
||||
|
||||
use crate::{
|
||||
builtins::error_builtin::ToError,
|
||||
native_function::{NativeFunction, ThisWrapper},
|
||||
native_function::{native_fn, NativeFunction},
|
||||
todo_fn::TODO,
|
||||
vs_value::{ToVal, Val},
|
||||
};
|
||||
@@ -17,26 +17,22 @@ pub fn op_sub_bigint(_bigint: &BigInt, subscript: &Val) -> Val {
|
||||
.to_val()
|
||||
}
|
||||
|
||||
static TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::BigInt(bigint) => match params.get(0) {
|
||||
Some(_) => {
|
||||
return Err("TODO: toString with radix".to_error());
|
||||
}
|
||||
static TO_STRING: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::BigInt(bigint) => match params.get(0) {
|
||||
Some(_) => {
|
||||
return Err("TODO: toString with radix".to_error());
|
||||
}
|
||||
|
||||
None => bigint.clone().to_val().to_string().to_val(),
|
||||
},
|
||||
_ => return Err("TODO: bigint indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
None => bigint.clone().to_val().to_string().to_val(),
|
||||
},
|
||||
_ => return Err("TODO: bigint indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static VALUE_OF: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::BigInt(bigint) => Val::BigInt(bigint.clone()),
|
||||
_ => return Err("TODO: bigint indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
static VALUE_OF: NativeFunction = native_fn(|this, _params| {
|
||||
Ok(match this.get() {
|
||||
Val::BigInt(bigint) => Val::BigInt(bigint.clone()),
|
||||
_ => return Err("TODO: bigint indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{fmt, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
builtins::range_error_builtin::to_range_error,
|
||||
native_function::{NativeFunction, ThisWrapper},
|
||||
native_function::{native_fn, NativeFunction, ThisWrapper},
|
||||
operations::op_sub,
|
||||
range_error,
|
||||
vs_array::VsArray,
|
||||
@@ -23,12 +23,12 @@ impl BuiltinObject for ArrayBuiltin {
|
||||
}
|
||||
|
||||
fn bo_sub(key: &str) -> Val {
|
||||
Val::Static(match key {
|
||||
"isArray" => &IS_ARRAY,
|
||||
"from" => &FROM,
|
||||
"of" => &OF,
|
||||
match key {
|
||||
"isArray" => IS_ARRAY.to_val(),
|
||||
"from" => FROM.to_val(),
|
||||
"of" => OF.to_val(),
|
||||
_ => return Val::Undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn bo_load_function() -> LoadFunctionResult {
|
||||
@@ -46,77 +46,65 @@ impl fmt::Display for ArrayBuiltin {
|
||||
}
|
||||
}
|
||||
|
||||
static IS_ARRAY: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match params.get(0) {
|
||||
static IS_ARRAY: NativeFunction = native_fn(|_this, params| {
|
||||
Ok(match params.get(0) {
|
||||
None => Val::Bool(false),
|
||||
Some(p) => match p.as_array_data() {
|
||||
None => Val::Bool(false),
|
||||
Some(p) => match p.as_array_data() {
|
||||
None => Val::Bool(false),
|
||||
Some(_) => Val::Bool(true),
|
||||
},
|
||||
})
|
||||
},
|
||||
};
|
||||
Some(_) => Val::Bool(true),
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
static FROM: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let first_param = match params.get(0) {
|
||||
None => return Err("undefined is not iterable".to_type_error()),
|
||||
Some(p) => p,
|
||||
};
|
||||
static FROM: NativeFunction = native_fn(|_this, params| {
|
||||
let first_param = match params.get(0) {
|
||||
None => return Err("undefined is not iterable".to_type_error()),
|
||||
Some(p) => p,
|
||||
};
|
||||
|
||||
if params.len() > 1 {
|
||||
return Err(format!("TODO: Using Array.from with a map function").to_val());
|
||||
if params.len() > 1 {
|
||||
return Err(format!("TODO: Using Array.from with a map function").to_val());
|
||||
}
|
||||
|
||||
Ok(match first_param {
|
||||
Val::Array(arr) => Val::Array(arr.clone()),
|
||||
Val::String(s) => s.chars().map(|c| c.to_val()).collect::<Vec<Val>>().to_val(),
|
||||
Val::Void | Val::Undefined | Val::Null => return Err("items is not iterable".to_type_error()),
|
||||
Val::Bool(..) | Val::Number(..) | Val::BigInt(..) | Val::Symbol(..) => VsArray::new().to_val(),
|
||||
Val::Object(..) | Val::Function(..) | Val::Class(..) | Val::Static(..) | Val::Custom(..) => {
|
||||
let len = op_sub(first_param.clone(), "length".to_val())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap() // TODO: Exception
|
||||
.to_number();
|
||||
|
||||
if len.is_sign_negative() || len.is_nan() {
|
||||
return Ok(VsArray::new().to_val());
|
||||
}
|
||||
|
||||
if len.is_infinite() {
|
||||
return range_error!("Invalid array length");
|
||||
}
|
||||
|
||||
let len = len as usize;
|
||||
|
||||
let mut arr = Vec::with_capacity(len);
|
||||
|
||||
// TODO: We should probably use a frame and step through this
|
||||
// Also using op_sub is slow. Should write specialized stuff instead.
|
||||
for i in 0..len {
|
||||
arr.push(
|
||||
op_sub(first_param.clone(), Val::Number(i as f64))
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(), // TODO: Exception
|
||||
);
|
||||
}
|
||||
|
||||
VsArray::from(arr).to_val()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Ok(match first_param {
|
||||
Val::Array(arr) => Val::Array(arr.clone()),
|
||||
Val::String(s) => s.chars().map(|c| c.to_val()).collect::<Vec<Val>>().to_val(),
|
||||
Val::Void | Val::Undefined | Val::Null => {
|
||||
return Err("items is not iterable".to_type_error())
|
||||
}
|
||||
Val::Bool(..) | Val::Number(..) | Val::BigInt(..) | Val::Symbol(..) => {
|
||||
VsArray::new().to_val()
|
||||
}
|
||||
Val::Object(..) | Val::Function(..) | Val::Class(..) | Val::Static(..) | Val::Custom(..) => {
|
||||
let len = op_sub(first_param.clone(), "length".to_val())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap() // TODO: Exception
|
||||
.to_number();
|
||||
|
||||
if len.is_sign_negative() || len.is_nan() {
|
||||
return Ok(VsArray::new().to_val());
|
||||
}
|
||||
|
||||
if len.is_infinite() {
|
||||
return range_error!("Invalid array length");
|
||||
}
|
||||
|
||||
let len = len as usize;
|
||||
|
||||
let mut arr = Vec::with_capacity(len);
|
||||
|
||||
// TODO: We should probably use a frame and step through this
|
||||
// Also using op_sub is slow. Should write specialized stuff instead.
|
||||
for i in 0..len {
|
||||
arr.push(
|
||||
op_sub(first_param.clone(), Val::Number(i as f64))
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(), // TODO: Exception
|
||||
);
|
||||
}
|
||||
|
||||
VsArray::from(arr).to_val()
|
||||
}
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static OF: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(VsArray::from(params).to_val())
|
||||
},
|
||||
};
|
||||
static OF: NativeFunction = native_fn(|_this, params| Ok(VsArray::from(params).to_val()));
|
||||
|
||||
fn to_array(_: ThisWrapper, params: Vec<Val>) -> Result<Val, Val> {
|
||||
if params.len() != 1 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::native_function::{NativeFunction, ThisWrapper};
|
||||
use crate::native_function::{native_fn, NativeFunction};
|
||||
use crate::vs_class::VsClass;
|
||||
use crate::vs_value::{LoadFunctionResult, Val};
|
||||
|
||||
@@ -38,12 +38,10 @@ impl fmt::Display for DebugBuiltin {
|
||||
}
|
||||
}
|
||||
|
||||
static LOG: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
for p in params {
|
||||
println!("Debug.log: {}", p.pretty());
|
||||
}
|
||||
static LOG: NativeFunction = native_fn(|_this, params| {
|
||||
for p in params {
|
||||
println!("Debug.log: {}", p.pretty());
|
||||
}
|
||||
|
||||
Ok(Val::Undefined)
|
||||
},
|
||||
};
|
||||
Ok(Val::Undefined)
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::native_function::{native_fn, ThisWrapper};
|
||||
use crate::vs_class::VsClass;
|
||||
use crate::vs_value::ToVal;
|
||||
use crate::{
|
||||
@@ -93,22 +93,18 @@ fn make_error_prototype() -> Val {
|
||||
.to_val()
|
||||
}
|
||||
|
||||
static SET_MESSAGE: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let message = match params.get(0) {
|
||||
Some(param) => param.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
static SET_MESSAGE: NativeFunction = native_fn(|mut this, params| {
|
||||
let message = match params.get(0) {
|
||||
Some(param) => param.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
op_submov(this.get_mut()?, "message".to_val(), message.to_val())?;
|
||||
op_submov(this.get_mut()?, "message".to_val(), message.to_val())?;
|
||||
|
||||
Ok(Val::Undefined)
|
||||
},
|
||||
};
|
||||
Ok(Val::Undefined)
|
||||
});
|
||||
|
||||
static ERROR_TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
let message = op_sub(this.get().clone(), "message".to_val())?;
|
||||
Ok(format!("Error({})", message).to_val()) // TODO: Fixes needed here (and other errors)
|
||||
},
|
||||
};
|
||||
static ERROR_TO_STRING: NativeFunction = native_fn(|this, _params| {
|
||||
let message = op_sub(this.get().clone(), "message".to_val())?;
|
||||
Ok(format!("Error({})", message).to_val()) // TODO: Fixes needed here (and other errors)
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::native_function::{NativeFunction, ThisWrapper};
|
||||
use crate::native_function::{native_fn, NativeFunction};
|
||||
use crate::operations::to_u32;
|
||||
use crate::vs_class::VsClass;
|
||||
use crate::vs_value::{LoadFunctionResult, Val, ValTrait};
|
||||
use crate::vs_value::{LoadFunctionResult, ToVal, Val, ValTrait};
|
||||
|
||||
use super::builtin_object::BuiltinObject;
|
||||
|
||||
@@ -19,51 +19,50 @@ impl BuiltinObject for MathBuiltin {
|
||||
|
||||
fn bo_sub(key: &str) -> Val {
|
||||
match key {
|
||||
"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),
|
||||
"E" => std::f64::consts::E.to_val(),
|
||||
"LN10" => std::f64::consts::LN_10.to_val(),
|
||||
"LN2" => std::f64::consts::LN_2.to_val(),
|
||||
"LOG10E" => std::f64::consts::LOG10_E.to_val(),
|
||||
"LOG2E" => std::f64::consts::LOG2_E.to_val(),
|
||||
"PI" => std::f64::consts::PI.to_val(),
|
||||
"SQRT1_2" => std::f64::consts::FRAC_1_SQRT_2.to_val(),
|
||||
"SQRT2" => std::f64::consts::SQRT_2.to_val(),
|
||||
|
||||
"abs" => ABS.to_val(),
|
||||
"acos" => ACOS.to_val(),
|
||||
"acosh" => ACOSH.to_val(),
|
||||
"asin" => ASIN.to_val(),
|
||||
"asinh" => ASINH.to_val(),
|
||||
"atan" => ATAN.to_val(),
|
||||
"atan2" => ATAN2.to_val(),
|
||||
"atanh" => ATANH.to_val(),
|
||||
"cbrt" => CBRT.to_val(),
|
||||
"ceil" => CEIL.to_val(),
|
||||
"clz32" => CLZ32.to_val(),
|
||||
"cos" => COS.to_val(),
|
||||
"cosh" => COSH.to_val(),
|
||||
"exp" => EXP.to_val(),
|
||||
"expm1" => EXPM1.to_val(),
|
||||
"floor" => FLOOR.to_val(),
|
||||
"fround" => FROUND.to_val(),
|
||||
"hypot" => HYPOT.to_val(),
|
||||
"imul" => IMUL.to_val(),
|
||||
"log" => LOG.to_val(),
|
||||
"log10" => LOG10.to_val(),
|
||||
"log1p" => LOG1P.to_val(),
|
||||
"log2" => LOG2.to_val(),
|
||||
"max" => MAX.to_val(),
|
||||
"min" => MIN.to_val(),
|
||||
"pow" => POW.to_val(),
|
||||
// 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),
|
||||
"round" => ROUND.to_val(),
|
||||
"sign" => SIGN.to_val(),
|
||||
"sin" => SIN.to_val(),
|
||||
"sinh" => SINH.to_val(),
|
||||
"sqrt" => SQRT.to_val(),
|
||||
"tan" => TAN.to_val(),
|
||||
"tanh" => TANH.to_val(),
|
||||
"trunc" => TRUNC.to_val(),
|
||||
|
||||
_ => Val::Undefined,
|
||||
}
|
||||
@@ -91,247 +90,179 @@ fn param_to_number(param: Option<&Val>) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
static ABS: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.abs()))
|
||||
},
|
||||
};
|
||||
static ABS: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.abs()))
|
||||
});
|
||||
|
||||
static ACOS: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.acos()))
|
||||
},
|
||||
};
|
||||
static ACOS: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.acos()))
|
||||
});
|
||||
|
||||
static ACOSH: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.acosh()))
|
||||
},
|
||||
};
|
||||
static ACOSH: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.acosh()))
|
||||
});
|
||||
|
||||
static ASIN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.asin()))
|
||||
},
|
||||
};
|
||||
static ASIN: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.asin()))
|
||||
});
|
||||
|
||||
static ASINH: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sinh()))
|
||||
},
|
||||
};
|
||||
static ASINH: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sinh()))
|
||||
});
|
||||
|
||||
static ATAN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.atan()))
|
||||
},
|
||||
};
|
||||
static ATAN: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.atan()))
|
||||
});
|
||||
|
||||
static ATAN2: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
static ATAN2: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
|
||||
Ok(Val::Number(x.atan2(y)))
|
||||
},
|
||||
};
|
||||
Ok(Val::Number(x.atan2(y)))
|
||||
});
|
||||
|
||||
static ATANH: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.atanh()))
|
||||
},
|
||||
};
|
||||
static ATANH: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.atanh()))
|
||||
});
|
||||
|
||||
static CBRT: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.cbrt()))
|
||||
},
|
||||
};
|
||||
static CBRT: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.cbrt()))
|
||||
});
|
||||
|
||||
static CEIL: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.ceil()))
|
||||
},
|
||||
};
|
||||
static CEIL: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.ceil()))
|
||||
});
|
||||
|
||||
static CLZ32: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(to_u32(x).leading_zeros() as f64))
|
||||
},
|
||||
};
|
||||
static CLZ32: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(to_u32(x).leading_zeros() as f64))
|
||||
});
|
||||
|
||||
static COS: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.cos()))
|
||||
},
|
||||
};
|
||||
static COS: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.cos()))
|
||||
});
|
||||
|
||||
static COSH: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.cosh()))
|
||||
},
|
||||
};
|
||||
static COSH: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.cosh()))
|
||||
});
|
||||
|
||||
static EXP: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.exp()))
|
||||
},
|
||||
};
|
||||
static EXP: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.exp()))
|
||||
});
|
||||
|
||||
static EXPM1: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.exp_m1()))
|
||||
},
|
||||
};
|
||||
static EXPM1: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.exp_m1()))
|
||||
});
|
||||
|
||||
static FLOOR: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.floor()))
|
||||
},
|
||||
};
|
||||
static FLOOR: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.floor()))
|
||||
});
|
||||
|
||||
static FROUND: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x as f32 as f64))
|
||||
},
|
||||
};
|
||||
static FROUND: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x as f32 as f64))
|
||||
});
|
||||
|
||||
static HYPOT: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.hypot(y)))
|
||||
},
|
||||
};
|
||||
static HYPOT: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.hypot(y)))
|
||||
});
|
||||
|
||||
static IMUL: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number((to_u32(x) * to_u32(y)) as i32 as f64))
|
||||
},
|
||||
};
|
||||
static IMUL: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number((to_u32(x) * to_u32(y)) as i32 as f64))
|
||||
});
|
||||
|
||||
static LOG: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.ln()))
|
||||
},
|
||||
};
|
||||
static LOG: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.ln()))
|
||||
});
|
||||
|
||||
static LOG10: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.log10()))
|
||||
},
|
||||
};
|
||||
static LOG10: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.log10()))
|
||||
});
|
||||
|
||||
static LOG1P: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.ln_1p()))
|
||||
},
|
||||
};
|
||||
static LOG1P: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.ln_1p()))
|
||||
});
|
||||
|
||||
static LOG2: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.log2()))
|
||||
},
|
||||
};
|
||||
static LOG2: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.log2()))
|
||||
});
|
||||
|
||||
static MAX: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.max(y)))
|
||||
},
|
||||
};
|
||||
static MAX: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.max(y)))
|
||||
});
|
||||
|
||||
static MIN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.min(y)))
|
||||
},
|
||||
};
|
||||
static MIN: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.min(y)))
|
||||
});
|
||||
|
||||
static POW: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.powf(y)))
|
||||
},
|
||||
};
|
||||
static POW: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
let y = param_to_number(params.get(1));
|
||||
Ok(Val::Number(x.powf(y)))
|
||||
});
|
||||
|
||||
static ROUND: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.round()))
|
||||
},
|
||||
};
|
||||
static ROUND: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.round()))
|
||||
});
|
||||
|
||||
static SIGN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.signum()))
|
||||
},
|
||||
};
|
||||
static SIGN: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.signum()))
|
||||
});
|
||||
|
||||
static SIN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sin()))
|
||||
},
|
||||
};
|
||||
static SIN: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sin()))
|
||||
});
|
||||
|
||||
static SINH: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sinh()))
|
||||
},
|
||||
};
|
||||
static SINH: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sinh()))
|
||||
});
|
||||
|
||||
static SQRT: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sqrt()))
|
||||
},
|
||||
};
|
||||
static SQRT: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.sqrt()))
|
||||
});
|
||||
|
||||
static TAN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.tan()))
|
||||
},
|
||||
};
|
||||
static TAN: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.tan()))
|
||||
});
|
||||
|
||||
static TANH: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.tanh()))
|
||||
},
|
||||
};
|
||||
static TANH: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.tanh()))
|
||||
});
|
||||
|
||||
static TRUNC: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.trunc()))
|
||||
},
|
||||
};
|
||||
static TRUNC: NativeFunction = native_fn(|_this, params| {
|
||||
let x = param_to_number(params.get(0));
|
||||
Ok(Val::Number(x.trunc()))
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::native_function::{native_fn, ThisWrapper};
|
||||
use crate::vs_value::ToVal;
|
||||
use crate::{
|
||||
native_function::NativeFunction,
|
||||
@@ -62,104 +62,92 @@ impl fmt::Display for NumberBuiltin {
|
||||
}
|
||||
}
|
||||
|
||||
pub static IS_FINITE: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let number = value.to_number();
|
||||
Val::Bool(number.is_finite())
|
||||
} else {
|
||||
Val::Bool(false)
|
||||
})
|
||||
},
|
||||
};
|
||||
pub static IS_FINITE: NativeFunction = native_fn(|_this, params| {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let number = value.to_number();
|
||||
Val::Bool(number.is_finite())
|
||||
} else {
|
||||
Val::Bool(false)
|
||||
})
|
||||
});
|
||||
|
||||
static IS_INTEGER: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let num = match params.get(0) {
|
||||
Some(n) => n.to_number(),
|
||||
None => return Ok(Val::Bool(false)),
|
||||
static IS_INTEGER: NativeFunction = native_fn(|_this, params| {
|
||||
let num = match params.get(0) {
|
||||
Some(n) => n.to_number(),
|
||||
None => return Ok(Val::Bool(false)),
|
||||
};
|
||||
|
||||
let is_finite = num.is_finite();
|
||||
let is_integer = num.floor() == num;
|
||||
|
||||
Ok(Val::Bool(is_finite && is_integer))
|
||||
});
|
||||
|
||||
pub static IS_NAN: NativeFunction = native_fn(|_this, params| {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let number = value.to_number();
|
||||
Val::Bool(number.is_nan())
|
||||
} else {
|
||||
Val::Bool(false)
|
||||
})
|
||||
});
|
||||
|
||||
static IS_SAFE_INTEGER: NativeFunction = native_fn(|_this, params| {
|
||||
let num = match params.get(0) {
|
||||
Some(n) => n.to_number(),
|
||||
None => return Ok(Val::Bool(false)),
|
||||
};
|
||||
|
||||
let is_finite = num.is_finite();
|
||||
let is_integer = num.floor() == num;
|
||||
let min_safe_integer = -(2f64.powi(53) - 1f64);
|
||||
let max_safe_integer = 2f64.powi(53) - 1f64;
|
||||
let in_safe_range = min_safe_integer <= num && num <= max_safe_integer;
|
||||
|
||||
Ok(Val::Bool(is_finite && is_integer && in_safe_range))
|
||||
});
|
||||
|
||||
pub static PARSE_FLOAT: NativeFunction = native_fn(|_this, params| {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let string_value = value.to_string().trim().to_string();
|
||||
|
||||
match string_value.parse::<f64>() {
|
||||
Ok(number) => Val::Number(number),
|
||||
Err(_) => Val::Number(core::f64::NAN),
|
||||
}
|
||||
} else {
|
||||
Val::Number(core::f64::NAN)
|
||||
})
|
||||
});
|
||||
|
||||
pub static PARSE_INT: NativeFunction = native_fn(|_this, params| {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let string_value = value.to_string().trim_start().to_string();
|
||||
let radix = params.get(1).and_then(|v| v.to_index()).unwrap_or(10);
|
||||
|
||||
if radix < 2 || radix > 36 {
|
||||
return Ok(Val::Number(core::f64::NAN));
|
||||
}
|
||||
|
||||
let (is_negative, string_value) = if string_value.starts_with('-') {
|
||||
(true, &string_value[1..])
|
||||
} else {
|
||||
(false, string_value.as_str())
|
||||
};
|
||||
|
||||
let is_finite = num.is_finite();
|
||||
let is_integer = num.floor() == num;
|
||||
|
||||
Ok(Val::Bool(is_finite && is_integer))
|
||||
},
|
||||
};
|
||||
|
||||
pub static IS_NAN: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let number = value.to_number();
|
||||
Val::Bool(number.is_nan())
|
||||
} else {
|
||||
Val::Bool(false)
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static IS_SAFE_INTEGER: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let num = match params.get(0) {
|
||||
Some(n) => n.to_number(),
|
||||
None => return Ok(Val::Bool(false)),
|
||||
let string_value = match string_value.find(|c: char| !c.is_digit(radix as u32)) {
|
||||
Some(pos) => &string_value[..pos],
|
||||
None => &string_value,
|
||||
};
|
||||
|
||||
let is_finite = num.is_finite();
|
||||
let is_integer = num.floor() == num;
|
||||
let min_safe_integer = -(2f64.powi(53) - 1f64);
|
||||
let max_safe_integer = 2f64.powi(53) - 1f64;
|
||||
let in_safe_range = min_safe_integer <= num && num <= max_safe_integer;
|
||||
|
||||
Ok(Val::Bool(is_finite && is_integer && in_safe_range))
|
||||
},
|
||||
};
|
||||
|
||||
pub static PARSE_FLOAT: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let string_value = value.to_string().trim().to_string();
|
||||
|
||||
match string_value.parse::<f64>() {
|
||||
Ok(number) => Val::Number(number),
|
||||
Err(_) => Val::Number(core::f64::NAN),
|
||||
match i64::from_str_radix(string_value, radix as u32) {
|
||||
Ok(number) => {
|
||||
let number = if is_negative { -number } else { number };
|
||||
Val::Number(number as f64)
|
||||
}
|
||||
} else {
|
||||
Val::Number(core::f64::NAN)
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
pub static PARSE_INT: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
let string_value = value.to_string().trim_start().to_string();
|
||||
let radix = params.get(1).and_then(|v| v.to_index()).unwrap_or(10);
|
||||
|
||||
if radix < 2 || radix > 36 {
|
||||
return Ok(Val::Number(core::f64::NAN));
|
||||
}
|
||||
|
||||
let (is_negative, string_value) = if string_value.starts_with('-') {
|
||||
(true, &string_value[1..])
|
||||
} else {
|
||||
(false, string_value.as_str())
|
||||
};
|
||||
|
||||
let string_value = match string_value.find(|c: char| !c.is_digit(radix as u32)) {
|
||||
Some(pos) => &string_value[..pos],
|
||||
None => &string_value,
|
||||
};
|
||||
|
||||
match i64::from_str_radix(string_value, radix as u32) {
|
||||
Ok(number) => {
|
||||
let number = if is_negative { -number } else { number };
|
||||
Val::Number(number as f64)
|
||||
}
|
||||
Err(_) => Val::Number(core::f64::NAN),
|
||||
}
|
||||
} else {
|
||||
Val::Number(core::f64::NAN)
|
||||
})
|
||||
},
|
||||
};
|
||||
Err(_) => Val::Number(core::f64::NAN),
|
||||
}
|
||||
} else {
|
||||
Val::Number(core::f64::NAN)
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::{collections::BTreeMap, rc::Rc};
|
||||
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::native_function::{native_fn, ThisWrapper};
|
||||
use crate::vs_value::ToVal;
|
||||
use crate::{
|
||||
native_function::NativeFunction,
|
||||
@@ -57,18 +57,16 @@ fn make_range_error_prototype() -> Val {
|
||||
.to_val()
|
||||
}
|
||||
|
||||
static SET_MESSAGE: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let message = match params.get(0) {
|
||||
Some(param) => param.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
static SET_MESSAGE: NativeFunction = native_fn(|mut this, params| {
|
||||
let message = match params.get(0) {
|
||||
Some(param) => param.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
op_submov(this.get_mut()?, "message".to_val(), message.to_val())?;
|
||||
op_submov(this.get_mut()?, "message".to_val(), message.to_val())?;
|
||||
|
||||
Ok(Val::Undefined)
|
||||
},
|
||||
};
|
||||
Ok(Val::Undefined)
|
||||
});
|
||||
|
||||
pub fn to_range_error(_: ThisWrapper, params: Vec<Val>) -> Result<Val, Val> {
|
||||
Ok(
|
||||
@@ -87,12 +85,10 @@ pub fn to_range_error(_: ThisWrapper, params: Vec<Val>) -> Result<Val, Val> {
|
||||
)
|
||||
}
|
||||
|
||||
static RANGE_ERROR_TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
let message = op_sub(this.get().clone(), "message".to_val())?;
|
||||
Ok(format!("RangeError({})", message).to_val())
|
||||
},
|
||||
};
|
||||
static RANGE_ERROR_TO_STRING: NativeFunction = native_fn(|this, _params| {
|
||||
let message = op_sub(this.get().clone(), "message".to_val())?;
|
||||
Ok(format!("RangeError({})", message).to_val())
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! range_error {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::native_function::{native_fn, ThisWrapper};
|
||||
use crate::vs_value::ToVal;
|
||||
use crate::{builtins::range_error_builtin::to_range_error, range_error};
|
||||
use crate::{
|
||||
@@ -49,24 +49,22 @@ impl fmt::Display for StringBuiltin {
|
||||
}
|
||||
}
|
||||
|
||||
static FROM_CODE_POINT: NativeFunction = NativeFunction {
|
||||
fn_: |_this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let mut result = String::new();
|
||||
static FROM_CODE_POINT: NativeFunction = native_fn(|_this, params| {
|
||||
let mut result = String::new();
|
||||
|
||||
for param in params {
|
||||
let code_point = param.to_number() as u32; // TODO: Check overflow behavior
|
||||
for param in params {
|
||||
let code_point = param.to_number() as u32; // TODO: Check overflow behavior
|
||||
|
||||
let char = match std::char::from_u32(code_point) {
|
||||
Some(c) => c,
|
||||
None => return range_error!("Invalid code point"),
|
||||
};
|
||||
let char = match std::char::from_u32(code_point) {
|
||||
Some(c) => c,
|
||||
None => return range_error!("Invalid code point"),
|
||||
};
|
||||
|
||||
result.push(char);
|
||||
}
|
||||
result.push(char);
|
||||
}
|
||||
|
||||
Ok(result.to_val())
|
||||
},
|
||||
};
|
||||
Ok(result.to_val())
|
||||
});
|
||||
|
||||
fn to_string(_: ThisWrapper, params: Vec<Val>) -> Result<Val, Val> {
|
||||
Ok(if let Some(value) = params.get(0) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::{collections::BTreeMap, rc::Rc};
|
||||
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::native_function::{native_fn, ThisWrapper};
|
||||
use crate::vs_value::ToVal;
|
||||
use crate::{
|
||||
native_function::NativeFunction,
|
||||
@@ -65,25 +65,21 @@ fn make_type_error_prototype() -> Val {
|
||||
.to_val()
|
||||
}
|
||||
|
||||
static SET_MESSAGE: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let message = match params.get(0) {
|
||||
Some(param) => param.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
static SET_MESSAGE: NativeFunction = native_fn(|mut this, params| {
|
||||
let message = match params.get(0) {
|
||||
Some(param) => param.to_string(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
op_submov(this.get_mut()?, "message".to_val(), message.to_val())?;
|
||||
op_submov(this.get_mut()?, "message".to_val(), message.to_val())?;
|
||||
|
||||
Ok(Val::Undefined)
|
||||
},
|
||||
};
|
||||
Ok(Val::Undefined)
|
||||
});
|
||||
|
||||
static TYPE_ERROR_TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
let message = op_sub(this.get().clone(), "message".to_val())?;
|
||||
Ok(format!("TypeError({})", message).to_val())
|
||||
},
|
||||
};
|
||||
static TYPE_ERROR_TO_STRING: NativeFunction = native_fn(|this, _params| {
|
||||
let message = op_sub(this.get().clone(), "message".to_val())?;
|
||||
Ok(format!("TypeError({})", message).to_val())
|
||||
});
|
||||
|
||||
pub trait ToTypeError {
|
||||
fn to_type_error(self) -> Val;
|
||||
|
||||
@@ -37,6 +37,12 @@ pub struct NativeFunction {
|
||||
pub fn_: fn(this: ThisWrapper, params: Vec<Val>) -> Result<Val, Val>,
|
||||
}
|
||||
|
||||
pub const fn native_fn(
|
||||
fn_: fn(this: ThisWrapper, params: Vec<Val>) -> Result<Val, Val>,
|
||||
) -> NativeFunction {
|
||||
NativeFunction { fn_ }
|
||||
}
|
||||
|
||||
impl ValTrait for NativeFunction {
|
||||
fn typeof_(&self) -> VsType {
|
||||
VsType::Function
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::builtins::error_builtin::ToError;
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::native_function::{native_fn, ThisWrapper};
|
||||
use crate::vs_value::ToVal;
|
||||
use crate::{builtins::range_error_builtin::to_range_error, range_error};
|
||||
use crate::{
|
||||
@@ -21,92 +21,80 @@ pub fn op_sub_number(_number: f64, subscript: &Val) -> Val {
|
||||
.to_val()
|
||||
}
|
||||
|
||||
static TO_FIXED: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => {
|
||||
if number.is_infinite() {
|
||||
return Ok(
|
||||
if number.is_sign_positive() {
|
||||
"Infinity"
|
||||
} else {
|
||||
"-Infinity"
|
||||
}
|
||||
.to_val(),
|
||||
);
|
||||
}
|
||||
static TO_FIXED: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => {
|
||||
if number.is_infinite() {
|
||||
return Ok(
|
||||
if number.is_sign_positive() {
|
||||
"Infinity"
|
||||
} else {
|
||||
"-Infinity"
|
||||
}
|
||||
.to_val(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut precision = match params.get(0) {
|
||||
Some(p) => p.to_number(),
|
||||
_ => return Ok(number.to_val().to_string().to_val()),
|
||||
};
|
||||
let mut precision = match params.get(0) {
|
||||
Some(p) => p.to_number(),
|
||||
_ => return Ok(number.to_val().to_string().to_val()),
|
||||
};
|
||||
|
||||
precision = f64::floor(precision);
|
||||
|
||||
if precision < 1.0 || precision > 100.0 {
|
||||
return range_error!("precision must be between 1 and 100");
|
||||
}
|
||||
|
||||
format!("{:.*}", precision as usize, number).to_val()
|
||||
}
|
||||
_ => return Err(format!("TODO: number indirection").to_val()),
|
||||
})
|
||||
});
|
||||
|
||||
static TO_EXPONENTIAL: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => match params.get(0) {
|
||||
Some(p) => {
|
||||
let mut precision = p.to_number();
|
||||
precision = f64::floor(precision);
|
||||
|
||||
if precision < 1.0 || precision > 100.0 {
|
||||
return range_error!("precision must be between 1 and 100");
|
||||
if precision < 0.0 || precision > 100.0 {
|
||||
return range_error!("precision must be between 0 and 100");
|
||||
}
|
||||
|
||||
format!("{:.*}", precision as usize, number).to_val()
|
||||
format_exponential(*number, Some(precision as usize))
|
||||
}
|
||||
_ => return Err(format!("TODO: number indirection").to_val()),
|
||||
})
|
||||
},
|
||||
};
|
||||
None => format_exponential(*number, None),
|
||||
},
|
||||
_ => return Err("number indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static TO_EXPONENTIAL: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => match params.get(0) {
|
||||
Some(p) => {
|
||||
let mut precision = p.to_number();
|
||||
precision = f64::floor(precision);
|
||||
static TODO_LOCALE: NativeFunction = native_fn(|this, _params| match this.get() {
|
||||
Val::Number(_number) => return Err("TODO: locale".to_error()),
|
||||
_ => return Err("number indirection".to_error()),
|
||||
});
|
||||
|
||||
if precision < 0.0 || precision > 100.0 {
|
||||
return range_error!("precision must be between 0 and 100");
|
||||
}
|
||||
static TO_STRING: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => match params.get(0) {
|
||||
Some(_) => {
|
||||
return Err("TODO: toString with radix".to_error());
|
||||
}
|
||||
|
||||
format_exponential(*number, Some(precision as usize))
|
||||
}
|
||||
None => format_exponential(*number, None),
|
||||
},
|
||||
_ => return Err("number indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
None => number.to_val().to_string().to_val(),
|
||||
},
|
||||
_ => return Err("number indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static TODO_LOCALE: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
match this.get() {
|
||||
Val::Number(_number) => return Err("TODO: locale".to_error()),
|
||||
_ => return Err("number indirection".to_error()),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => match params.get(0) {
|
||||
Some(_) => {
|
||||
return Err("TODO: toString with radix".to_error());
|
||||
}
|
||||
|
||||
None => number.to_val().to_string().to_val(),
|
||||
},
|
||||
_ => return Err("number indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static VALUE_OF: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => Val::Number(*number),
|
||||
_ => return Err("number indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
static VALUE_OF: NativeFunction = native_fn(|this, _params| {
|
||||
Ok(match this.get() {
|
||||
Val::Number(number) => Val::Number(*number),
|
||||
_ => return Err("number indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
fn format_exponential(number: f64, precision: Option<usize>) -> Val {
|
||||
if number.is_infinite() {
|
||||
|
||||
@@ -6,6 +6,7 @@ use num_traits::ToPrimitive;
|
||||
use crate::bigint_methods::op_sub_bigint;
|
||||
use crate::builtins::error_builtin::ToError;
|
||||
use crate::builtins::type_error_builtin::ToTypeError;
|
||||
use crate::native_function::native_fn;
|
||||
use crate::native_function::NativeFunction;
|
||||
use crate::native_function::ThisWrapper;
|
||||
use crate::number_methods::op_sub_number;
|
||||
@@ -469,20 +470,16 @@ pub fn op_submov(target: &mut Val, subscript: Val, value: Val) -> Result<(), Val
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL_TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Bool(b) => b.to_string().to_val(),
|
||||
_ => return Err("bool indirection".to_type_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
static BOOL_TO_STRING: NativeFunction = native_fn(|this, _params| {
|
||||
Ok(match this.get() {
|
||||
Val::Bool(b) => b.to_string().to_val(),
|
||||
_ => return Err("bool indirection".to_type_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static BOOL_VALUE_OF: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Bool(b) => Val::Bool(*b),
|
||||
_ => return Err("bool indirection".to_type_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
static BOOL_VALUE_OF: NativeFunction = native_fn(|this, _params| {
|
||||
Ok(match this.get() {
|
||||
Val::Bool(b) => Val::Bool(*b),
|
||||
_ => return Err("bool indirection".to_type_error()),
|
||||
})
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,6 @@
|
||||
use crate::{
|
||||
builtins::error_builtin::ToError,
|
||||
native_function::{NativeFunction, ThisWrapper},
|
||||
vs_value::Val,
|
||||
native_function::{native_fn, NativeFunction},
|
||||
};
|
||||
|
||||
pub static TODO: NativeFunction = NativeFunction {
|
||||
fn_: |_: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> { Err("TODO".to_error()) },
|
||||
};
|
||||
pub static TODO: NativeFunction = native_fn(|_, _| Err("TODO".to_error()));
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::array_higher_functions::{
|
||||
use crate::builtins::error_builtin::ToError;
|
||||
use crate::builtins::type_error_builtin::ToTypeError;
|
||||
use crate::helpers::{to_wrapping_index, to_wrapping_index_clamped};
|
||||
use crate::native_function::{NativeFunction, ThisWrapper};
|
||||
use crate::native_function::{native_fn, NativeFunction};
|
||||
use crate::operations::op_triple_eq_impl;
|
||||
use crate::todo_fn::TODO;
|
||||
use crate::vs_class::VsClass;
|
||||
@@ -162,520 +162,481 @@ impl fmt::Display for ArrayPrototype {
|
||||
}
|
||||
}
|
||||
|
||||
static AT: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
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(),
|
||||
},
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
static AT: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
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(),
|
||||
},
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static CONCAT: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let mut new_array = array_data.as_ref().clone();
|
||||
static CONCAT: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_array.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static COPY_WITHIN: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
new_array.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
let array_data_mut = Rc::make_mut(array_data);
|
||||
let ulen = array_data_mut.elements.len();
|
||||
static COPY_WITHIN: NativeFunction = native_fn(|mut this, params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
if ulen > isize::MAX as usize {
|
||||
return Err("TODO: array len exceeds isize".to_error());
|
||||
Ok(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 {
|
||||
return Err("TODO: array len exceeds isize".to_error());
|
||||
}
|
||||
|
||||
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 Ok(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;
|
||||
}
|
||||
|
||||
let mut target = match params.get(0) {
|
||||
None => 0,
|
||||
Some(p) => to_wrapping_index_clamped(p, ulen),
|
||||
};
|
||||
while target >= 0 && end >= start {
|
||||
array_data_mut.elements[target as usize] = array_data_mut.elements[end as usize].clone();
|
||||
|
||||
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 Ok(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;
|
||||
target -= 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;
|
||||
}
|
||||
}
|
||||
|
||||
this.clone()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static ENTRIES: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
match this.get() {
|
||||
Val::Array(_array_data) => return Err("TODO: iterators".to_error()),
|
||||
_ => return Err("array indirection".to_error()),
|
||||
};
|
||||
},
|
||||
};
|
||||
this.clone()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static FILL: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
static ENTRIES: NativeFunction = native_fn(|this, _params| {
|
||||
match this.get() {
|
||||
Val::Array(_array_data) => return Err("TODO: iterators".to_error()),
|
||||
_ => return Err("array indirection".to_error()),
|
||||
};
|
||||
});
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
let array_data_mut = Rc::make_mut(array_data);
|
||||
let len = array_data_mut.elements.len();
|
||||
static FILL: NativeFunction = native_fn(|mut this, params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
let fill_val = params.get(0).unwrap_or(&Val::Undefined);
|
||||
Ok(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(1) {
|
||||
None => 0,
|
||||
Some(v) => to_wrapping_index_clamped(v, len),
|
||||
};
|
||||
let fill_val = params.get(0).unwrap_or(&Val::Undefined);
|
||||
|
||||
let end = match params.get(2) {
|
||||
None => len as isize,
|
||||
Some(v) => to_wrapping_index_clamped(v, len),
|
||||
};
|
||||
let start = match params.get(1) {
|
||||
None => 0,
|
||||
Some(v) => to_wrapping_index_clamped(v, len),
|
||||
};
|
||||
|
||||
for i in start..end {
|
||||
array_data_mut.elements[i as usize] = fill_val.clone();
|
||||
}
|
||||
let end = match params.get(2) {
|
||||
None => len as isize,
|
||||
Some(v) => to_wrapping_index_clamped(v, len),
|
||||
};
|
||||
|
||||
this.clone()
|
||||
for i in start..end {
|
||||
array_data_mut.elements[i as usize] = fill_val.clone();
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static FLAT: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
if params.len() > 0 {
|
||||
return Err("TODO: .flat depth parameter".to_error());
|
||||
}
|
||||
this.clone()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
let mut new_elems = Vec::<Val>::new();
|
||||
static FLAT: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
if params.len() > 0 {
|
||||
return Err("TODO: .flat depth parameter".to_error());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_elems.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static INCLUDES: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
new_elems.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
for elem in &array_data.elements {
|
||||
let is_eq = op_triple_eq_impl(elem.clone(), search_param.clone())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
static INCLUDES: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
if is_eq {
|
||||
return Ok(Val::Bool(true));
|
||||
for elem in &array_data.elements {
|
||||
let is_eq = op_triple_eq_impl(elem.clone(), search_param.clone())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Ok(Val::Bool(true));
|
||||
}
|
||||
}
|
||||
|
||||
Val::Bool(false)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static INDEX_OF: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
for i in 0..array_data.elements.len() {
|
||||
let is_eq = op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Ok(Val::Number(i as f64));
|
||||
}
|
||||
}
|
||||
|
||||
Val::Number(-1.0)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static JOIN: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Array(vals) => {
|
||||
if vals.elements.len() == 0 {
|
||||
return Ok("".to_val());
|
||||
}
|
||||
|
||||
if vals.elements.len() == 1 {
|
||||
return Ok(vals.elements[0].clone().to_val_string());
|
||||
}
|
||||
|
||||
let separator = match params.get(0) {
|
||||
None => ",".to_string(),
|
||||
Some(v) => v.to_string(),
|
||||
};
|
||||
|
||||
let mut iter = vals.elements.iter();
|
||||
let mut res = iter.next().unwrap().to_string();
|
||||
|
||||
for val in iter {
|
||||
res += &separator;
|
||||
|
||||
match val.typeof_() {
|
||||
VsType::Undefined => {}
|
||||
_ => {
|
||||
res += &val.to_string();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
res.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static LAST_INDEX_OF: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
for i in (0..array_data.elements.len()).rev() {
|
||||
let is_eq = op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Ok(Val::Number(i as f64));
|
||||
}
|
||||
}
|
||||
|
||||
Val::Number(-1_f64)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static POP: NativeFunction = native_fn(|mut this, _params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
if array_data.elements.len() == 0 {
|
||||
return Ok(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);
|
||||
|
||||
match removed_el {
|
||||
Val::Void => Val::Undefined,
|
||||
_ => removed_el,
|
||||
}
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static PUSH: NativeFunction = native_fn(|mut this, params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(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);
|
||||
}
|
||||
|
||||
Val::Number(array_data_mut.elements.len() as f64)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static REVERSE: NativeFunction = native_fn(|mut this, _params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(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 Ok(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;
|
||||
}
|
||||
|
||||
this.clone()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static SHIFT: NativeFunction = native_fn(|mut this, _params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
if array_data.elements.len() == 0 {
|
||||
return Ok(Val::Undefined);
|
||||
}
|
||||
|
||||
let array_data_mut = Rc::make_mut(array_data);
|
||||
|
||||
array_data_mut.elements.remove(0)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static SLICE: NativeFunction = native_fn(|this, params| {
|
||||
Ok(match this.get() {
|
||||
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());
|
||||
}
|
||||
|
||||
new_elems.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
static SPLICE: NativeFunction = native_fn(|mut this, params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(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();
|
||||
}
|
||||
|
||||
Val::Bool(false)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
let gap = insert_len - replace_len;
|
||||
|
||||
static INDEX_OF: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
for _ in 0..gap {
|
||||
array_data_mut.elements.push(Val::Void);
|
||||
}
|
||||
|
||||
for i in 0..array_data.elements.len() {
|
||||
let is_eq = op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
for i in ((start + replace_len)..len).rev() {
|
||||
array_data_mut.elements[i + gap] = array_data_mut.elements[i].clone();
|
||||
}
|
||||
|
||||
if is_eq {
|
||||
return Ok(Val::Number(i as f64));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Val::Number(-1.0)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static JOIN: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Array(vals) => {
|
||||
if vals.elements.len() == 0 {
|
||||
return Ok("".to_val());
|
||||
}
|
||||
|
||||
if vals.elements.len() == 1 {
|
||||
return Ok(vals.elements[0].clone().to_val_string());
|
||||
}
|
||||
|
||||
let separator = match params.get(0) {
|
||||
None => ",".to_string(),
|
||||
Some(v) => v.to_string(),
|
||||
};
|
||||
|
||||
let mut iter = vals.elements.iter();
|
||||
let mut res = iter.next().unwrap().to_string();
|
||||
|
||||
for val in iter {
|
||||
res += &separator;
|
||||
|
||||
match val.typeof_() {
|
||||
VsType::Undefined => {}
|
||||
_ => {
|
||||
res += &val.to_string();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
res.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static LAST_INDEX_OF: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
Val::Array(array_data) => {
|
||||
let search_param = params.get(0).unwrap_or(&Val::Undefined).clone();
|
||||
|
||||
for i in (0..array_data.elements.len()).rev() {
|
||||
let is_eq = op_triple_eq_impl(array_data.elements[i].clone(), search_param.clone())
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap(); // TODO: Exception
|
||||
|
||||
if is_eq {
|
||||
return Ok(Val::Number(i as f64));
|
||||
}
|
||||
}
|
||||
|
||||
Val::Number(-1_f64)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static POP: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
if array_data.elements.len() == 0 {
|
||||
return Ok(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);
|
||||
|
||||
match removed_el {
|
||||
Val::Void => Val::Undefined,
|
||||
_ => removed_el,
|
||||
}
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static PUSH: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(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);
|
||||
}
|
||||
|
||||
Val::Number(array_data_mut.elements.len() as f64)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static REVERSE: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(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 Ok(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;
|
||||
}
|
||||
|
||||
this.clone()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static SHIFT: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
if array_data.elements.len() == 0 {
|
||||
return Ok(Val::Undefined);
|
||||
}
|
||||
|
||||
let array_data_mut = Rc::make_mut(array_data);
|
||||
|
||||
array_data_mut.elements.remove(0)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static SLICE: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(match this.get() {
|
||||
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());
|
||||
}
|
||||
|
||||
new_elems.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
static SPLICE: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(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();
|
||||
}
|
||||
array_data_mut.elements.pop();
|
||||
}
|
||||
}
|
||||
|
||||
deleted_elements.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
deleted_elements.to_val()
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
// TODO: Share this? (JS doesn't?)
|
||||
static TO_STRING: NativeFunction = NativeFunction {
|
||||
fn_: |this: ThisWrapper, _params: Vec<Val>| -> Result<Val, Val> {
|
||||
Ok(this.get().to_string().to_val())
|
||||
},
|
||||
};
|
||||
static TO_STRING: NativeFunction = native_fn(|this, _params| Ok(this.get().to_string().to_val()));
|
||||
|
||||
static UNSHIFT: NativeFunction = NativeFunction {
|
||||
fn_: |mut this: ThisWrapper, params: Vec<Val>| -> Result<Val, Val> {
|
||||
let this = this.get_mut()?;
|
||||
static UNSHIFT: NativeFunction = native_fn(|mut this, params| {
|
||||
let this = this.get_mut()?;
|
||||
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
let array_data_mut = Rc::make_mut(array_data);
|
||||
Ok(match this {
|
||||
Val::Array(array_data) => {
|
||||
let array_data_mut = Rc::make_mut(array_data);
|
||||
|
||||
let mut i = 0;
|
||||
let mut i = 0;
|
||||
|
||||
for p in params {
|
||||
array_data_mut.elements.insert(i, p);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Val::Number(array_data_mut.elements.len() as f64)
|
||||
for p in params {
|
||||
array_data_mut.elements.insert(i, p);
|
||||
i += 1;
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
Val::Number(array_data_mut.elements.len() as f64)
|
||||
}
|
||||
_ => return Err("array indirection".to_error()),
|
||||
})
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user