Simplify native_fn

This commit is contained in:
Andrew Morris
2023-05-26 16:13:23 +10:00
parent d214e27dc8
commit 1613734325
15 changed files with 1436 additions and 1648 deletions

View File

@@ -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()),
})
});

View File

@@ -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 {

View File

@@ -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)
});

View File

@@ -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)
});

View File

@@ -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()))
});

View File

@@ -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)
})
});

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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() {

View File

@@ -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

View File

@@ -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()));

View File

@@ -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()),
})
});