use crate::{float_to_string, vm::*};
use std::collections::VecDeque;
use std::ops::*;
type Result<T, E = VMError> = std::result::Result<T, E>;
type Handle<T> = std::rc::Rc<std::cell::RefCell<T>>;
fn unwrap_this_as_range(args: &[Value]) -> Handle<ObjRange> {
args[0].try_into_range().unwrap()
}
fn unwrap_this_as_string(args: &[Value]) -> String {
args[0].try_into_string().unwrap()
}
fn unwrap_this_as_closure(args: &[Value]) -> Handle<ObjClosure> {
args[0].try_into_closure().unwrap()
}
fn unwrap_this_as_map(args: &[Value]) -> Handle<ObjMap> {
args[0].try_into_map().unwrap()
}
fn unwrap_this_as_list(args: &[Value]) -> Handle<ObjList> {
args[0].try_into_list().unwrap()
}
fn unwrap_this_as_class(args: &[Value]) -> Handle<ObjClass> {
args[0].try_into_class().unwrap()
}
fn unwrap_this_as_fiber(args: &[Value]) -> Handle<ObjFiber> {
args[0].try_into_fiber().unwrap()
}
macro_rules! num_constant {
($func:ident, $value:expr) => {
fn $func(_vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::Num($value))
}
};
}
fn validate_num(value: &Value, arg_name: &str) -> Result<f64> {
value
.try_into_num()
.ok_or_else(|| VMError::from_string(format!("{} must be a number.", arg_name)))
}
macro_rules! infix_num_op {
($func:ident, $method:ident, $return_type:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")?;
let b = validate_num(&args[1], "Right operand")?;
Ok(Value::$return_type(a.$method(&b)))
}
};
}
macro_rules! num_binary_op {
($func:ident, $method:ident, $return_type:ident, $msg:expr) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")?;
let b = validate_num(&args[1], $msg)?;
Ok(Value::$return_type(a.$method(b)))
}
};
}
macro_rules! bitwise_num_op {
($func:ident, $method:ident, $return_type:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")? as u32;
let b = validate_num(&args[1], "Right operand")? as u32;
Ok(Value::from_u32(a.$method(&b)))
}
};
}
macro_rules! overflowing_bitwise_num_op {
($func:ident, $method:ident, $return_type:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")? as u32;
let b = validate_num(&args[1], "Right operand")? as u32;
Ok(Value::from_u32(a.$method(b).0))
}
};
}
macro_rules! num_bitwise_unary_op {
($func:ident, $method:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")? as u32;
Ok(Value::from_u32(a.$method()))
}
};
}
macro_rules! num_unary_op {
($func:ident, $method:ident, $return_type:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")?;
Ok(Value::$return_type(a.$method()))
}
};
}
num_constant!(num_infinity, f64::INFINITY);
num_constant!(num_nan, f64::NAN);
num_constant!(num_pi, std::f64::consts::PI);
num_constant!(num_tau, std::f64::consts::TAU);
num_constant!(num_largest, f64::MAX);
num_constant!(num_smallest, f64::MIN_POSITIVE);
num_constant!(num_max_safe_integer, 9007199254740991.0);
num_constant!(num_min_safe_integer, -9007199254740991.0);
infix_num_op!(num_plus, add, Num);
infix_num_op!(num_minus, sub, Num);
infix_num_op!(num_mult, mul, Num);
infix_num_op!(num_divide, div, Num);
infix_num_op!(num_lt, lt, Boolean);
infix_num_op!(num_gt, gt, Boolean);
infix_num_op!(num_lte, le, Boolean);
infix_num_op!(num_gte, ge, Boolean);
bitwise_num_op!(num_bitwise_and, bitand, Num);
bitwise_num_op!(num_bitwise_or, bitor, Num);
bitwise_num_op!(num_bitwise_xor, bitxor, Num);
overflowing_bitwise_num_op!(num_bitwise_shl, overflowing_shl, Num);
overflowing_bitwise_num_op!(num_bitwise_shr, overflowing_shr, Num);
fn num_range_inclusive(vm: &VM, args: &[Value]) -> Result<Value> {
let start = validate_num(&args[0], "Left hand side of range")?;
let end = validate_num(&args[1], "Right hand side of range")?;
Ok(Value::Range(vm.new_range(start, end, true)))
}
fn num_range_exclusive(vm: &VM, args: &[Value]) -> Result<Value> {
let start = validate_num(&args[0], "Left hand side of range")?;
let end = validate_num(&args[1], "Right hand side of range")?;
Ok(Value::Range(vm.new_range(start, end, false)))
}
num_binary_op!(num_atan2, atan2, Num, "x value");
num_binary_op!(num_pow, powf, Num, "Power value");
num_unary_op!(num_unary_minus, neg, Num);
fn num_from_string(_vm: &VM, args: &[Value]) -> Result<Value> {
let string = validate_string(&args[1], "Argument")?;
if string.is_empty() {
return Ok(Value::Null);
}
match string.trim().parse::<f64>() {
Ok(num) => {
if num.is_finite() {
Ok(Value::Num(num))
} else {
Err(VMError::from_str("Number literal is too large."))
}
}
Err(_) => Ok(Value::Null),
}
}
fn num_fraction(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")?;
let fract = a.fract();
if fract == 0.0 && a.is_sign_negative() {
Ok(Value::Num(-0.0))
} else {
Ok(Value::Num(fract))
}
}
num_unary_op!(num_is_infinity, is_infinite, Boolean);
num_unary_op!(num_is_nan, is_nan, Boolean);
fn num_sign(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = validate_num(&args[0], "this")?;
let sign = if a == 0.0 || a.is_nan() {
0.0
} else if a == -0.0 {
-0.0
} else {
a.signum()
};
Ok(Value::Num(sign))
}
num_unary_op!(num_truncate, trunc, Num);
num_unary_op!(num_abs, abs, Num);
num_unary_op!(num_acos, acos, Num);
num_unary_op!(num_asin, asin, Num);
num_unary_op!(num_atan, atan, Num);
num_unary_op!(num_cbrt, cbrt, Num);
num_unary_op!(num_ceil, ceil, Num);
num_unary_op!(num_cos, cos, Num);
num_unary_op!(num_floor, floor, Num);
num_unary_op!(num_round, round, Num);
num_binary_op!(num_min, min, Num, "Other value");
num_binary_op!(num_max, max, Num, "Other value");
fn num_clamp(_vm: &VM, args: &[Value]) -> Result<Value> {
let value = validate_num(&args[0], "this")?;
let min = validate_num(&args[1], "Min value")?;
let max = validate_num(&args[2], "Max value")?;
let result = if value < min {
min
} else {
if value > max {
max
} else {
value
}
};
Ok(Value::Num(result))
}
num_unary_op!(num_sin, sin, Num);
num_unary_op!(num_sqrt, sqrt, Num);
num_unary_op!(num_tan, tan, Num);
num_unary_op!(num_log, ln, Num);
num_unary_op!(num_log2, log2, Num);
num_unary_op!(num_exp, exp, Num);
num_binary_op!(num_mod, rem, Num, "Right operand");
num_bitwise_unary_op!(num_bitwise_not, not);
#[allow(clippy::float_cmp)]
fn f64_is_integer(x: f64) -> bool {
x.trunc() == x
}
fn num_is_integer(_vm: &VM, args: &[Value]) -> Result<Value> {
let x = validate_num(&args[0], "this")?;
Ok(Value::Boolean(
!x.is_nan() && !x.is_infinite() && f64_is_integer(x),
))
}
fn wren_num_to_string(num: f64) -> String {
if num.is_nan() {
return "nan".into();
}
if num.is_infinite() {
if num.is_sign_positive() {
return "infinity".into();
} else {
return "-infinity".into();
}
}
if num == -0.0 && num.is_sign_negative() {
return "-0".into();
}
float_to_string::float_to_shortest_string(num, 14)
}
fn num_to_string(_vm: &VM, args: &[Value]) -> Result<Value> {
let value = validate_num(&args[0], "this")?;
let string = wren_num_to_string(value);
Ok(Value::from_string(string))
}
fn class_name(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_class(&args);
let string = this.borrow().name.clone();
Ok(Value::from_string(string))
}
fn class_supertype(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_class(&args);
let maybe_superclass = &this.borrow().superclass;
match maybe_superclass {
None => Ok(Value::Null),
Some(superclass) => Ok(Value::Class(superclass.clone())),
}
}
fn class_to_string(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_class(&args);
let string = this.borrow().name.clone();
Ok(Value::from_string(string))
}
fn object_eqeq(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::Boolean(args[0].eq(&args[1])))
}
fn object_same(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::Boolean(args[1].eq(&args[2])))
}
fn object_bangeq(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::Boolean(args[0].ne(&args[1])))
}
fn object_is(vm: &VM, args: &[Value]) -> Result<Value> {
let expected_baseclass = args[1]
.try_into_class()
.ok_or_else(|| VMError::from_str("Right operand must be a class."))?;
let mut class = vm.class_for_value(&args[0]);
loop {
if *expected_baseclass.borrow().name == *class.borrow().name {
return Ok(Value::Boolean(true));
}
let superclass = match &class.borrow().superclass {
Some(superclass) => superclass.clone(),
None => {
return Ok(Value::Boolean(false));
}
};
class = superclass;
}
}
fn object_to_string(vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::from_string(format!(
"instance of {}",
vm.class_for_value(&args[0]).borrow().name
)))
}
macro_rules! range_getter {
($func:ident, $method:ident, $return_type:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let range_cell = unwrap_this_as_range(&args);
let range = range_cell.borrow();
Ok(Value::$return_type(range.$method))
}
};
}
macro_rules! range_getter_fn {
($func:ident, $method:ident, $return_type:ident) => {
fn $func(_vm: &VM, args: &[Value]) -> Result<Value> {
let range_cell = unwrap_this_as_range(&args);
let range = range_cell.borrow();
Ok(Value::$return_type(range.$method()))
}
};
}
range_getter!(range_from, from, Num);
range_getter!(range_to, to, Num);
range_getter!(range_is_inclusive, is_inclusive, Boolean);
range_getter_fn!(range_min, min, Num);
range_getter_fn!(range_max, max, Num);
fn range_iterate(_vm: &VM, args: &[Value]) -> Result<Value> {
let range_cell = unwrap_this_as_range(&args);
let range = range_cell.borrow();
if range.from == range.to && !range.is_inclusive {
return Ok(Value::Boolean(false)); }
if args[1].is_null() {
return Ok(Value::Num(range.from));
}
let mut iterator = validate_num(&args[1], "Iterator")?;
if range.from < range.to {
iterator += 1.0;
if iterator > range.to {
return Ok(Value::Boolean(false));
}
} else {
iterator -= 1.0;
if iterator < range.to {
return Ok(Value::Boolean(false));
}
}
if !range.is_inclusive && iterator == range.to {
return Ok(Value::Boolean(false));
}
Ok(Value::Num(iterator))
}
fn range_iterator_value(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(args[1].clone())
}
fn range_to_string(_vm: &VM, args: &[Value]) -> Result<Value> {
let range_ref = args[0]
.try_into_range()
.ok_or_else(|| VMError::from_str("this must be range"))?;
let range = range_ref.borrow();
let from = wren_num_to_string(range.from);
let to = wren_num_to_string(range.to);
let op = if range.is_inclusive { ".." } else { "..." };
Ok(Value::from_string(format!("{}{}{}", from, op, to)))
}
fn object_type(vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::Class(vm.class_for_value(&args[0])))
}
fn object_not(_vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::Boolean(false))
}
fn bool_not(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::Boolean(!args[0].equals_true()))
}
fn bool_to_string(_vm: &VM, args: &[Value]) -> Result<Value> {
if args[0].equals_true() {
Ok(Value::from_str("true"))
} else {
Ok(Value::from_str("false"))
}
}
fn fiber_new(vm: &VM, args: &[Value]) -> Result<Value> {
let closure = validate_fn(&args[1], "Argument")?;
if closure.borrow().fn_obj.borrow().arity.as_index() > 1 {
Err(VMError::from_str(
"Function cannot take more than one parameter.",
))
} else {
Ok(Value::Fiber(vm.new_fiber(closure)))
}
}
fn fiber_abort(_vm: &VM, args: &[Value]) -> Result<Value> {
let is_abort = !args[1].is_null();
if is_abort {
Err(VMError::FiberAbort(args[1].clone()))
} else {
Ok(Value::Boolean(args[1].is_null()))
}
}
fn fiber_current(vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::Fiber(vm.fiber.as_ref().unwrap().clone()))
}
fn fiber_suspend(_vm: &VM, _args: &[Value]) -> Result<FiberAction> {
Ok(FiberAction::Suspend)
}
fn fiber_yield(_vm: &VM, _args: &[Value]) -> Result<FiberAction> {
Ok(FiberAction::Return(Value::Null))
}
fn fiber_yield1(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
Ok(FiberAction::Return(args[1].clone()))
}
fn fiber_call(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), true, "call")?;
Ok(FiberAction::Call(this, Value::Null))
}
fn fiber_call1(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), true, "call")?;
Ok(FiberAction::Call(this, args[1].clone()))
}
fn fiber_transfer(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), false, "transfer to")?;
Ok(FiberAction::Transfer(this, Value::Null))
}
fn fiber_transfer1(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), false, "transfer to")?;
Ok(FiberAction::Transfer(this, args[1].clone()))
}
fn fiber_transfer_error(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), false, "transfer to")?;
Ok(FiberAction::TransferError(this, args[1].clone()))
}
fn fiber_try(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), true, "try")?;
Ok(FiberAction::Try(this, Value::Null))
}
fn fiber_try1(_vm: &VM, args: &[Value]) -> Result<FiberAction> {
let this = unwrap_this_as_fiber(&args);
validate_fiber_action(&this.borrow(), true, "try")?;
Ok(FiberAction::Try(this, args[1].clone()))
}
fn fiber_error(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_fiber(&args);
let error = this.borrow().error();
Ok(error)
}
fn fiber_is_done(_vm: &VM, args: &[Value]) -> Result<Value> {
let this_handle = unwrap_this_as_fiber(&args);
let this = this_handle.borrow();
let is_done = this.has_error() || this.completed_normally_cache;
Ok(Value::Boolean(is_done))
}
fn validate_fiber_action(fiber: &ObjFiber, is_call: bool, verb: &str) -> Result<()> {
if fiber.has_error() {
return Err(VMError::from_string(format!(
"Cannot {} an aborted fiber.",
verb
)));
}
if is_call {
if fiber.caller.is_some() {
return Err(VMError::from_str("Fiber has already been called."));
}
if fiber.is_root() {
return Err(VMError::from_str("Cannot call root fiber."));
}
}
if fiber.completed_normally_cache {
return Err(VMError::from_string(format!(
"Cannot {} a finished fiber.",
verb
)));
}
Ok(())
}
fn null_not(_vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::Boolean(true))
}
fn null_to_string(_vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::from_str("null"))
}
fn validate_string(arg: &Value, arg_name: &str) -> Result<String> {
arg.try_into_string()
.ok_or_else(|| VMError::from_string(format!("{} must be a string.", arg_name)))
}
fn string_from_code_point(_vm: &VM, args: &[Value]) -> Result<Value> {
let num = validate_int(&args[1], "Code point")?;
if num < 0.0 {
Err(VMError::from_str("Code point cannot be negative."))
} else if (num as u32) > 0x10ffff {
Err(VMError::from_str(
"Code point cannot be greater than 0x10ffff.",
))
} else {
let c = char::from_u32(num as u32)
.ok_or_else(|| VMError::from_str("Code point must be valid unicode"))?;
Ok(Value::from_string(c.to_string()))
}
}
fn string_from_byte(_vm: &VM, args: &[Value]) -> Result<Value> {
let num = validate_int(&args[1], "Byte")?;
if num < 0.0 {
Err(VMError::from_str("Byte cannot be negative."))
} else if (num as u32) > 0xff {
Err(VMError::from_str("Byte cannot be greater than 0xff."))
} else {
let bytes = vec![num as u8];
let string =
String::from_utf8(bytes).map_err(|_| VMError::from_str("Byte must be valid utf8"))?;
Ok(Value::from_string(string))
}
}
fn string_plus(_vm: &VM, args: &[Value]) -> Result<Value> {
let a = unwrap_this_as_string(&args);
let b = validate_string(&args[1], "Right operand")?;
Ok(Value::from_string(a + &b))
}
fn adjust_range_to_char_boundaries(
string: &str,
range: std::ops::RangeInclusive<usize>,
) -> std::ops::Range<usize> {
let mut before = *range.start();
let mut after = range.end() + 1;
while before <= after && !string.is_char_boundary(before) {
before += 1;
}
while after < string.len() && !string.is_char_boundary(after) {
after += 1;
}
before..after
}
fn string_subscript(_vm: &VM, args: &[Value]) -> Result<Value> {
let string = unwrap_this_as_string(&args);
match &args[1] {
Value::Num(_) => {
let index = validate_index(&args[1], string.len(), "Subscript")?;
Ok(wren_string_code_point_at(string, index))
}
Value::Range(r) => {
let range = calculate_range(&r.borrow(), string.len())?;
if range.range.is_empty() {
return Ok(Value::from_str(""));
}
let safe_range = adjust_range_to_char_boundaries(&string, range.range);
let slice = &string[safe_range];
if range.reverse {
Ok(Value::from_string(slice.chars().rev().collect()))
} else {
Ok(Value::from_str(slice))
}
}
_ => Err(VMError::from_str("Subscript must be a number or a range.")),
}
}
fn string_to_string(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(args[0].clone())
}
fn string_byte_count(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::from_usize(unwrap_this_as_string(&args).len()))
}
fn string_code_point_at(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_string(&args);
let index = validate_index(&args[1], this.len(), "Index")?;
if !this.is_char_boundary(index) {
return Ok(Value::Num(-1.0));
}
let c = this.split_at(index).1.chars().next().unwrap();
Ok(Value::from_usize(c as usize))
}
fn string_byte_at(_vm: &VM, args: &[Value]) -> Result<Value> {
let string = unwrap_this_as_string(&args);
let index = validate_index(&args[1], string.len(), "Index")?;
Ok(Value::from_u8(string.as_bytes()[index]))
}
fn string_contains(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_string(&args);
let search = validate_string(&args[1], "Argument")?;
Ok(Value::Boolean(this.contains(&search)))
}
fn string_ends_with(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_string(&args);
let search = validate_string(&args[1], "Argument")?;
Ok(Value::Boolean(this.ends_with(&search)))
}
fn index_or_neg_one(maybe_index: Option<usize>) -> Value {
match maybe_index {
None => Value::Num(-1.0),
Some(index) => Value::from_usize(index),
}
}
fn string_index_of1(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_string(&args);
let search = validate_string(&args[1], "Argument")?;
let maybe_index = this.find(&search);
Ok(index_or_neg_one(maybe_index))
}
fn string_index_of2(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_string(&args);
let search = validate_string(&args[1], "Argument")?;
let mut start = validate_index(&args[2], this.len(), "Start")?;
while start < this.len() && !this.is_char_boundary(start) {
start += 1;
}
let maybe_index = this[start..].find(&search);
match maybe_index {
None => Ok(Value::Num(-1.0)),
Some(index) => Ok(Value::from_usize(start + index)),
}
}
fn string_iterate(_vm: &VM, args: &[Value]) -> Result<Value> {
let string = unwrap_this_as_string(&args);
if args[1].is_null() {
return Ok(if string.is_empty() {
Value::Boolean(false)
} else {
Value::Num(0.0)
});
}
let num = validate_int(&args[1], "Iterator")?;
if num < 0.0 {
return Ok(Value::Boolean(false));
}
let mut index = num as usize;
loop {
index += 1;
if index >= string.len() {
return Ok(Value::Boolean(false));
}
if string.is_char_boundary(index) {
return Ok(Value::from_usize(index));
}
}
}
fn string_iterate_byte(_vm: &VM, args: &[Value]) -> Result<Value> {
let string = unwrap_this_as_string(&args);
if args[1].is_null() {
return Ok(if string.is_empty() {
Value::Boolean(false)
} else {
Value::Num(0.0)
});
}
let num = validate_int(&args[1], "Iterator")?;
if num < 0.0 {
return Ok(Value::Boolean(false));
}
let index = num as usize + 1;
Ok(if index >= string.len() {
Value::Boolean(false)
} else {
Value::from_usize(index)
})
}
fn wren_string_code_point_at(string: String, index: usize) -> Value {
let mut chars = string.char_indices();
let mut previous: char = char::default();
loop {
if let Some((start, c)) = chars.next() {
if start <= index {
previous = c;
continue;
}
}
return Value::from_string(previous.to_string());
}
}
fn string_iterator_value(_vm: &VM, args: &[Value]) -> Result<Value> {
let string = unwrap_this_as_string(&args);
let index = validate_index(&args[1], string.len(), "Iterator")?;
Ok(wren_string_code_point_at(string, index))
}
fn string_starts_with(_vm: &VM, args: &[Value]) -> Result<Value> {
let this = unwrap_this_as_string(&args);
let search = validate_string(&args[1], "Argument")?;
Ok(Value::Boolean(this.starts_with(&search)))
}
fn validate_fn(arg: &Value, arg_name: &str) -> Result<Handle<ObjClosure>> {
arg.try_into_closure()
.ok_or_else(|| VMError::from_string(format!("{} must be a function.", arg_name)))
}
fn fn_new(_vm: &VM, args: &[Value]) -> Result<Value> {
Ok(Value::Closure(validate_fn(&args[1], "Argument")?))
}
fn fn_arity(_vm: &VM, args: &[Value]) -> Result<Value> {
let closure = unwrap_this_as_closure(&args);
let arity = closure.borrow().fn_obj.borrow().arity;
Ok(Value::from_usize(arity.as_index()))
}
fn fn_to_string(_vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::from_str("<fn>"))
}
fn map_new(vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::Map(vm.new_map()))
}
fn map_subscript(vm: &VM, args: &[Value]) -> Result<Value> {
let key = &args[1];
validate_key(vm, key)?;
let map_cell = unwrap_this_as_map(&args);
let map = map_cell.borrow();
let maybe_value = map.data.get(key);
match maybe_value {
None => Ok(Value::Null),
Some(v) => Ok(v.clone()),
}
}
fn map_subscript_setter(vm: &VM, args: &[Value]) -> Result<Value> {
let key = &args[1];
validate_key(vm, key)?;
let map = unwrap_this_as_map(&args);
let value = &args[2];
map.borrow_mut().data.insert(key.clone(), value.clone());
Ok(value.clone())
}
fn map_is_valid_key(arg: &Value) -> bool {
matches!(
arg,
Value::Boolean(_)
| Value::Class(_)
| Value::Null
| Value::Num(_)
| Value::Range(_)
| Value::String(_)
)
}
fn validate_key(_vm: &VM, arg: &Value) -> Result<bool> {
if map_is_valid_key(arg) {
Ok(true)
} else {
Err(VMError::from_str("Key must be a value type."))
}
}
fn map_add_core(vm: &VM, args: &[Value]) -> Result<Value> {
let key = &args[1];
validate_key(vm, key)?;
let value = &args[2];
let map = unwrap_this_as_map(&args);
map.borrow_mut().data.insert(key.clone(), value.clone());
Ok(Value::Map(map))
}
fn map_clear(_vm: &VM, args: &[Value]) -> Result<Value> {
let map = unwrap_this_as_map(&args);
map.borrow_mut().data.clear();
Ok(Value::Null)
}
fn map_contains_key(vm: &VM, args: &[Value]) -> Result<Value> {
let key = &args[1];
validate_key(vm, key)?;
let map = unwrap_this_as_map(&args);
let result = map.borrow().contains_key(key);
Ok(Value::Boolean(result))
}
fn map_count(_vm: &VM, args: &[Value]) -> Result<Value> {
let map = unwrap_this_as_map(&args);
let count = map.borrow().len();
Ok(Value::from_usize(count))
}
fn map_remove(vm: &VM, args: &[Value]) -> Result<Value> {
let key = &args[1];
validate_key(vm, key)?;
let map = unwrap_this_as_map(&args);
let maybe_value = map.borrow_mut().data.remove(key);
match maybe_value {
None => Ok(Value::Null),
Some(value) => Ok(value),
}
}
fn map_iterate(_vm: &VM, args: &[Value]) -> Result<Value> {
let map = unwrap_this_as_map(&args);
if map.borrow().data.is_empty() {
return Ok(Value::Boolean(false));
}
if !args[1].is_null() {
let value = validate_int(&args[1], "Iterator")?;
if value < 0.0 {
return Ok(Value::Boolean(false));
}
let index = value as usize + 1;
if index < map.borrow().len() {
Ok(Value::from_usize(index))
} else {
Ok(Value::Boolean(false))
}
} else {
Ok(Value::from_usize(0))
}
}
fn map_key_iterator_value(_vm: &VM, args: &[Value]) -> Result<Value> {
let map_handle = unwrap_this_as_map(&args);
let map = map_handle.borrow();
let index = validate_index(&args[1], map.len(), "Iterator")?;
let mut entries = map.data.iter();
Ok(entries.nth(index).unwrap().0.clone())
}
fn map_value_iterator_value(_vm: &VM, args: &[Value]) -> Result<Value> {
let map_handle = unwrap_this_as_map(&args);
let map = map_handle.borrow();
let index = validate_index(&args[1], map.len(), "Iterator")?;
let mut entries = map.data.iter();
Ok(entries.nth(index).unwrap().1.clone())
}
fn list_filled(vm: &VM, args: &[Value]) -> Result<Value> {
let size = validate_int(&args[1], "Size")?;
if size < 0.0 {
return Err(VMError::from_str("Size cannot be negative."));
}
let contents = vec![args[2].clone(); size as usize];
Ok(Value::List(vm.new_list(contents)))
}
fn list_new(vm: &VM, _args: &[Value]) -> Result<Value> {
Ok(Value::List(vm.new_list(Vec::new())))
}
struct Range {
range: std::ops::RangeInclusive<usize>,
reverse: bool,
}
impl Range {
fn empty() -> Range {
Range {
range: 1..=0, reverse: false,
}
}
}
fn calculate_range(range: &ObjRange, length: usize) -> Result<Range> {
let len_f = length as f64;
let is_full_slice = if range.is_inclusive {
range.from == len_f && range.to == -1.0
} else {
range.from == len_f && range.to == len_f
};
if is_full_slice {
return Ok(Range::empty());
}
let from = validate_index_value(length, range.from as f64, "Range start")?;
let from_f = from as f64;
let mut value = validate_int_value(range.to as f64, "Range end")?;
if value < 0.0 {
value += len_f;
}
if !range.is_inclusive {
if value == from_f {
return Ok(Range::empty());
}
if value >= from_f {
value += -1.0;
} else {
value += 1.0;
}
}
if value < 0.0 || value >= len_f {
return Err(VMError::from_str("Range end out of bounds."));
}
let to = value as usize;
if from <= to {
Ok(Range {
range: from..=to,
reverse: false,
})
} else {
Ok(Range {
range: to..=from,
reverse: true,
})
}
}
fn list_subscript(vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
match &args[1] {
Value::Num(_) => {
let index = validate_index(&args[1], list.borrow().len(), "Subscript")?;
Ok(list.borrow().elements[index].clone())
}
Value::Range(r) => {
let range = calculate_range(&r.borrow(), list.borrow().len())?;
if range.range.is_empty() {
return Ok(Value::List(vm.new_list(Vec::new())));
}
let slice = &list.borrow().elements[range.range];
let mut vec = slice.to_vec();
if range.reverse {
vec.reverse();
}
Ok(Value::List(vm.new_list(vec)))
}
_ => Err(VMError::from_str("Subscript must be a number or a range.")),
}
}
fn list_subscript_setter(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let index = validate_index(&args[1], list.borrow().len(), "Subscript")?;
list.borrow_mut().elements[index] = args[2].clone();
Ok(args[2].clone())
}
fn list_add(_vm: &VM, args: &[Value]) -> Result<Value> {
let value = &args[1];
let list = unwrap_this_as_list(&args);
list.borrow_mut().elements.push(value.clone());
Ok(value.clone())
}
fn list_add_core(_vm: &VM, args: &[Value]) -> Result<Value> {
let value = &args[1];
let list = unwrap_this_as_list(&args);
list.borrow_mut().elements.push(value.clone());
Ok(args[0].clone())
}
fn list_clear(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
list.borrow_mut().elements.clear();
Ok(Value::Null)
}
fn list_insert(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let elements = &mut list.borrow_mut().elements;
let count = elements.len();
let index = validate_index(&args[1], count + 1, "Index")?;
if index == count {
elements.push(args[2].clone());
} else {
elements.insert(index, args[2].clone());
}
Ok(args[2].clone())
}
fn list_remove_value(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let maybe_index = list.borrow().elements.iter().position(|v| v.eq(&args[1]));
match maybe_index {
None => Ok(Value::Null),
Some(index) => {
let value = list.borrow_mut().elements.remove(index);
Ok(value)
}
}
}
fn list_index_of(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let maybe_index = list.borrow().elements.iter().position(|v| v.eq(&args[1]));
Ok(index_or_neg_one(maybe_index))
}
fn list_swap(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let index_a = validate_index(&args[1], list.borrow().len(), "Index 0")?;
let index_b = validate_index(&args[2], list.borrow().len(), "Index 1")?;
list.borrow_mut().elements.swap(index_a, index_b);
Ok(Value::Null)
}
fn validate_int_value(value: f64, arg_name: &str) -> Result<f64> {
if !f64_is_integer(value) {
Err(VMError::from_string(format!(
"{} must be an integer.",
arg_name
)))
} else {
Ok(value)
}
}
fn validate_int(value: &Value, arg_name: &str) -> Result<f64> {
let num = validate_num(value, arg_name)?;
validate_int_value(num, arg_name)
}
fn list_iterate(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let elements_len = list.borrow().len() as f64;
if args[1].is_null() {
if elements_len == 0.0 {
return Ok(Value::Boolean(false));
}
return Ok(Value::Num(0.0));
}
let index = validate_int(&args[1], "Iterator")?;
if index < 0.0 || index >= elements_len - 1.0 {
return Ok(Value::Boolean(false));
}
Ok(Value::Num(index + 1.0))
}
fn validate_index(value: &Value, count: usize, arg_name: &str) -> Result<usize> {
let num = validate_num(value, arg_name)?;
validate_index_value(count, num, arg_name)
}
fn validate_index_value(count: usize, mut value: f64, arg_name: &str) -> Result<usize> {
validate_int_value(value, arg_name)?;
if value < 0.0 {
value += count as f64;
}
if value >= 0.0 && value < count as f64 {
Ok(value as usize)
} else {
Err(VMError::from_string(format!("{} out of bounds.", arg_name)))
}
}
fn list_iterator_value(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let index = validate_index(&args[1], list.borrow().len(), "Iterator")?;
let value = list.borrow().elements[index].clone();
Ok(value)
}
fn list_remove_at(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let index = validate_index(&args[1], list.borrow().len(), "Index")?;
let value = list.borrow_mut().elements.remove(index);
Ok(value)
}
fn list_count(_vm: &VM, args: &[Value]) -> Result<Value> {
let list = unwrap_this_as_list(&args);
let count = list.borrow().len();
Ok(Value::from_usize(count))
}
fn unescape(s: &str) -> String {
let mut queue: VecDeque<_> = String::from(s).chars().collect();
let mut s = String::new();
while let Some(c) = queue.pop_front() {
if c != '\\' {
s.push(c);
continue;
}
match queue.pop_front() {
Some('n') => s.push('\n'),
Some('0') => return s,
Some(c) => {
s.push('\\');
s.push(c);
}
None => {
s.push('\\');
return s;
}
};
}
s
}
fn system_clock(vm: &VM, _args: &[Value]) -> Result<Value> {
let now = std::time::Instant::now();
Ok(Value::Num(now.duration_since(vm.start_time).as_secs_f64()))
}
fn system_write_string(vm: &VM, args: &[Value]) -> Result<Value> {
let string = args[1]
.try_into_string()
.ok_or_else(|| VMError::from_str("expected String"))?;
let result = unescape(&string);
if let Some(write_fn) = vm.config.write_fn {
write_fn(vm, &result);
}
Ok(args[1].clone())
}
macro_rules! primitive {
($vm:expr, $class:expr, $sig:expr, $func:expr) => {
let symbol = $vm.methods.ensure_symbol($sig);
$class
.borrow_mut()
.set_method(symbol, Method::ValuePrimitive($func));
};
}
macro_rules! fiber_primitive {
($vm:expr, $class:expr, $sig:expr, $func:expr) => {
let symbol = $vm.methods.ensure_symbol($sig);
$class
.borrow_mut()
.set_method(symbol, Method::FiberActionPrimitive($func));
};
}
macro_rules! fiber_primitive_static {
($vm:expr, $class:expr, $sig:expr, $func:expr) => {
let symbol = $vm.methods.ensure_symbol($sig);
$class
.borrow_mut()
.class_obj()
.borrow_mut()
.set_method(symbol, Method::FiberActionPrimitive($func));
};
}
macro_rules! primitive_static {
($vm:expr, $class:expr, $sig:expr, $func:expr) => {
let symbol = $vm.methods.ensure_symbol($sig);
$class
.borrow_mut()
.class_obj()
.borrow_mut()
.set_method(symbol, Method::ValuePrimitive($func));
};
}
pub(crate) fn init_base_classes(vm: &mut VM, core_module: &mut Module) {
let object = define_class(core_module, "Object");
primitive!(vm, object, "!", object_not);
primitive!(vm, object, "==(_)", object_eqeq);
primitive!(vm, object, "!=(_)", object_bangeq);
primitive!(vm, object, "is(_)", object_is);
primitive!(vm, object, "toString", object_to_string);
primitive!(vm, object, "type", object_type);
let class = define_class(core_module, "Class");
class.borrow_mut().bind_superclass(&object);
primitive!(vm, class, "name", class_name);
primitive!(vm, class, "supertype", class_supertype);
primitive!(vm, class, "toString", class_to_string);
let object_metaclass = define_class(core_module, "Object metaclass");
object.borrow_mut().class = Some(object_metaclass.clone());
object_metaclass.borrow_mut().class = Some(class.clone());
class.borrow_mut().class = Some(class.clone());
object_metaclass.borrow_mut().bind_superclass(&class);
primitive_static!(vm, object, "same(_,_)", object_same);
}
fn create_and_define_class(
vm: &mut VM,
module: &mut Module,
name: &str,
superclass: &Handle<ObjClass>,
) -> Handle<ObjClass> {
let class = vm
.new_class(&superclass, ClassSource::Internal, name.into())
.unwrap();
module
.define_variable(name, Value::Class(class.clone()))
.unwrap();
class
}
pub(crate) fn init_fn_and_fiber(vm: &mut VM, module: &mut Module) {
let superclass = module.expect_class("Object");
vm.fn_class = Some(create_and_define_class(vm, module, "Fn", &superclass));
vm.fiber_class = Some(create_and_define_class(vm, module, "Fiber", &superclass));
}
pub(crate) fn register_core_primitives(vm: &mut VM) {
let module = vm.core_module.as_ref().unwrap().borrow();
let core = CoreClasses {
bool_class: module.expect_class("Bool"),
num: module.expect_class("Num"),
string: module.expect_class("String"),
null: module.expect_class("Null"),
range: module.expect_class("Range"),
list: module.expect_class("List"),
map: module.expect_class("Map"),
};
primitive!(vm, core.bool_class, "!", bool_not);
primitive!(vm, core.bool_class, "toString", bool_to_string);
let fiber = vm.fiber_class.as_ref().unwrap();
primitive_static!(vm, fiber, "new(_)", fiber_new);
primitive_static!(vm, fiber, "abort(_)", fiber_abort);
primitive_static!(vm, fiber, "current", fiber_current);
fiber_primitive_static!(vm, fiber, "suspend()", fiber_suspend);
fiber_primitive_static!(vm, fiber, "yield()", fiber_yield);
fiber_primitive_static!(vm, fiber, "yield(_)", fiber_yield1);
fiber_primitive!(vm, fiber, "call()", fiber_call);
fiber_primitive!(vm, fiber, "call(_)", fiber_call1);
primitive!(vm, fiber, "error", fiber_error);
primitive!(vm, fiber, "isDone", fiber_is_done);
fiber_primitive!(vm, fiber, "transfer()", fiber_transfer);
fiber_primitive!(vm, fiber, "transfer(_)", fiber_transfer1);
fiber_primitive!(vm, fiber, "transferError(_)", fiber_transfer_error);
fiber_primitive!(vm, fiber, "try()", fiber_try);
fiber_primitive!(vm, fiber, "try(_)", fiber_try1);
let fn_class = vm.fn_class.as_ref().unwrap();
primitive_static!(vm, fn_class, "new(_)", fn_new);
primitive!(vm, fn_class, "arity", fn_arity);
for arity in 0..=crate::vm::MAX_PARAMETERS {
let name = if arity == 0 {
"call()".to_string()
} else {
format!("call({}{})", "_,".repeat(arity - 1), "_")
};
let symbol = vm.methods.ensure_symbol(&name);
fn_class
.borrow_mut()
.set_method(symbol, Method::FunctionCall);
}
primitive!(vm, fn_class, "toString", fn_to_string);
primitive!(vm, core.null, "!", null_not);
primitive!(vm, core.null, "toString", null_to_string);
primitive_static!(vm, core.num, "fromString(_)", num_from_string);
primitive_static!(vm, core.num, "infinity", num_infinity);
primitive_static!(vm, core.num, "nan", num_nan);
primitive_static!(vm, core.num, "pi", num_pi);
primitive_static!(vm, core.num, "tau", num_tau);
primitive_static!(vm, core.num, "largest", num_largest);
primitive_static!(vm, core.num, "smallest", num_smallest);
primitive_static!(vm, core.num, "maxSafeInteger", num_max_safe_integer);
primitive_static!(vm, core.num, "minSafeInteger", num_min_safe_integer);
primitive!(vm, core.num, "-(_)", num_minus);
primitive!(vm, core.num, "+(_)", num_plus);
primitive!(vm, core.num, "*(_)", num_mult);
primitive!(vm, core.num, "/(_)", num_divide);
primitive!(vm, core.num, "<(_)", num_lt);
primitive!(vm, core.num, ">(_)", num_gt);
primitive!(vm, core.num, "<=(_)", num_lte);
primitive!(vm, core.num, ">=(_)", num_gte);
primitive!(vm, core.num, "&(_)", num_bitwise_and);
primitive!(vm, core.num, "|(_)", num_bitwise_or);
primitive!(vm, core.num, "^(_)", num_bitwise_xor);
primitive!(vm, core.num, "<<(_)", num_bitwise_shl);
primitive!(vm, core.num, ">>(_)", num_bitwise_shr);
primitive!(vm, core.num, "abs", num_abs);
primitive!(vm, core.num, "acos", num_acos);
primitive!(vm, core.num, "asin", num_asin);
primitive!(vm, core.num, "atan", num_atan);
primitive!(vm, core.num, "cbrt", num_cbrt);
primitive!(vm, core.num, "ceil", num_ceil);
primitive!(vm, core.num, "cos", num_cos);
primitive!(vm, core.num, "floor", num_floor);
primitive!(vm, core.num, "-", num_unary_minus);
primitive!(vm, core.num, "round", num_round);
primitive!(vm, core.num, "min(_)", num_min);
primitive!(vm, core.num, "max(_)", num_max);
primitive!(vm, core.num, "clamp(_,_)", num_clamp);
primitive!(vm, core.num, "sin", num_sin);
primitive!(vm, core.num, "sqrt", num_sqrt);
primitive!(vm, core.num, "tan", num_tan);
primitive!(vm, core.num, "log", num_log);
primitive!(vm, core.num, "log2", num_log2);
primitive!(vm, core.num, "exp", num_exp);
primitive!(vm, core.num, "%(_)", num_mod);
primitive!(vm, core.num, "~", num_bitwise_not);
primitive!(vm, core.num, "..(_)", num_range_inclusive);
primitive!(vm, core.num, "...(_)", num_range_exclusive);
primitive!(vm, core.num, "atan(_)", num_atan2);
primitive!(vm, core.num, "pow(_)", num_pow);
primitive!(vm, core.num, "fraction", num_fraction);
primitive!(vm, core.num, "isInfinity", num_is_infinity);
primitive!(vm, core.num, "isInteger", num_is_integer);
primitive!(vm, core.num, "isNan", num_is_nan);
primitive!(vm, core.num, "sign", num_sign);
primitive!(vm, core.num, "toString", num_to_string);
primitive!(vm, core.num, "truncate", num_truncate);
primitive_static!(vm, core.string, "fromCodePoint(_)", string_from_code_point);
primitive_static!(vm, core.string, "fromByte(_)", string_from_byte);
primitive!(vm, core.string, "+(_)", string_plus);
primitive!(vm, core.string, "[_]", string_subscript);
primitive!(vm, core.string, "byteAt_(_)", string_byte_at);
primitive!(vm, core.string, "byteCount_", string_byte_count);
primitive!(vm, core.string, "codePointAt_(_)", string_code_point_at);
primitive!(vm, core.string, "contains(_)", string_contains);
primitive!(vm, core.string, "endsWith(_)", string_ends_with);
primitive!(vm, core.string, "indexOf(_)", string_index_of1);
primitive!(vm, core.string, "indexOf(_,_)", string_index_of2);
primitive!(vm, core.string, "iterate(_)", string_iterate);
primitive!(vm, core.string, "iterateByte_(_)", string_iterate_byte);
primitive!(vm, core.string, "iteratorValue(_)", string_iterator_value);
primitive!(vm, core.string, "startsWith(_)", string_starts_with);
primitive!(vm, core.string, "toString", string_to_string);
let list = module.expect_class("List");
primitive_static!(vm, list, "filled(_,_)", list_filled);
primitive_static!(vm, list, "new()", list_new);
primitive!(vm, list, "[_]", list_subscript);
primitive!(vm, list, "[_]=(_)", list_subscript_setter);
primitive!(vm, list, "add(_)", list_add);
primitive!(vm, list, "addCore_(_)", list_add_core);
primitive!(vm, list, "clear()", list_clear);
primitive!(vm, list, "count", list_count);
primitive!(vm, list, "insert(_,_)", list_insert);
primitive!(vm, list, "iterate(_)", list_iterate);
primitive!(vm, list, "iteratorValue(_)", list_iterator_value);
primitive!(vm, list, "removeAt(_)", list_remove_at);
primitive!(vm, list, "remove(_)", list_remove_value);
primitive!(vm, list, "indexOf(_)", list_index_of);
primitive!(vm, list, "swap(_,_)", list_swap);
let map = module.expect_class("Map");
primitive_static!(vm, map, "new()", map_new);
primitive!(vm, map, "[_]", map_subscript);
primitive!(vm, map, "[_]=(_)", map_subscript_setter);
primitive!(vm, map, "addCore_(_,_)", map_add_core);
primitive!(vm, map, "clear()", map_clear);
primitive!(vm, map, "containsKey(_)", map_contains_key);
primitive!(vm, map, "count", map_count);
primitive!(vm, map, "remove(_)", map_remove);
primitive!(vm, map, "iterate(_)", map_iterate);
primitive!(vm, map, "keyIteratorValue_(_)", map_key_iterator_value);
primitive!(vm, map, "valueIteratorValue_(_)", map_value_iterator_value);
primitive!(vm, core.range, "from", range_from);
primitive!(vm, core.range, "to", range_to);
primitive!(vm, core.range, "min", range_min);
primitive!(vm, core.range, "max", range_max);
primitive!(vm, core.range, "isInclusive", range_is_inclusive);
primitive!(vm, core.range, "iterate(_)", range_iterate);
primitive!(vm, core.range, "iteratorValue(_)", range_iterator_value);
primitive!(vm, core.range, "toString", range_to_string);
let system = module.expect_class("System");
primitive_static!(vm, system, "clock", system_clock);
primitive_static!(vm, system, "writeString_(_)", system_write_string);
vm.core = Some(core);
}