additional list functions (range, map, filter, reduce, flatten) (#74)
also make the VM cope with reentrancy
This commit is contained in:
parent
2b924c3efb
commit
65645f410f
4 changed files with 341 additions and 47 deletions
|
@ -11,7 +11,7 @@ use crate::{
|
|||
vm::{Exception, FnArgs, Vm},
|
||||
};
|
||||
|
||||
pub type SystemFn = fn(&mut Vm, FnArgs) -> Result<Value, Exception>;
|
||||
pub type SystemFn = fn(&System, &mut Vm, FnArgs) -> Result<Value, Exception>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ChunkId(u32);
|
||||
|
@ -249,6 +249,11 @@ pub mod fns {
|
|||
|
||||
0x90 Nary "len" => len,
|
||||
0x91 Nary "index" => index,
|
||||
0x92 Nary "range" => range,
|
||||
0x93 Nary "map" => map,
|
||||
0x94 Nary "filter" => filter,
|
||||
0x95 Nary "reduce" => reduce,
|
||||
0x96 Nary "flatten" => flatten,
|
||||
|
||||
0xc0 Nary "toShape" => to_shape_f,
|
||||
0xc1 Nary "line" => line,
|
||||
|
@ -282,23 +287,23 @@ pub mod fns {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn add(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
binary_math_op(vm, args, <f32 as Add>::add, <Vec4 as Add>::add)
|
||||
}
|
||||
|
||||
pub fn sub(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn sub(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
binary_math_op(vm, args, <f32 as Sub>::sub, <Vec4 as Sub>::sub)
|
||||
}
|
||||
|
||||
pub fn mul(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn mul(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
binary_math_op(vm, args, <f32 as Mul>::mul, <Vec4 as Mul>::mul)
|
||||
}
|
||||
|
||||
pub fn div(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn div(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
binary_math_op(vm, args, <f32 as Div>::div, <Vec4 as Div>::div)
|
||||
}
|
||||
|
||||
pub fn neg(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn neg(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
match args.get(vm, 0) {
|
||||
Value::Number(x) => Ok(Value::Number(-x)),
|
||||
Value::Vec4(x) => Ok(Value::Vec4(-x)),
|
||||
|
@ -346,7 +351,7 @@ pub mod fns {
|
|||
macro_rules! math_fns {
|
||||
($($arity:tt $sysname:tt $name:tt),* $(,)?) => {
|
||||
$(
|
||||
pub fn $name(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn $name(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
$arity(vm, args, $sysname, libm::$name)
|
||||
}
|
||||
)*
|
||||
|
@ -385,48 +390,48 @@ pub mod fns {
|
|||
math1 "atanh" atanhf,
|
||||
}
|
||||
|
||||
pub fn not(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn not(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let value = args.get(vm, 0);
|
||||
Ok(Value::from(value.is_falsy()))
|
||||
}
|
||||
|
||||
pub fn eq(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn eq(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let a = args.get(vm, 0);
|
||||
let b = args.get(vm, 1);
|
||||
Ok(Value::from(a == b))
|
||||
}
|
||||
|
||||
pub fn neq(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn neq(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let a = args.get(vm, 0);
|
||||
let b = args.get(vm, 1);
|
||||
Ok(Value::from(a != b))
|
||||
}
|
||||
|
||||
pub fn lt(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn lt(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let a = args.get(vm, 0);
|
||||
let b = args.get(vm, 1);
|
||||
Ok(Value::from(a < b))
|
||||
}
|
||||
|
||||
pub fn leq(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn leq(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let a = args.get(vm, 0);
|
||||
let b = args.get(vm, 1);
|
||||
Ok(Value::from(a <= b))
|
||||
}
|
||||
|
||||
pub fn gt(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn gt(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let a = args.get(vm, 0);
|
||||
let b = args.get(vm, 1);
|
||||
Ok(Value::from(a > b))
|
||||
}
|
||||
|
||||
pub fn geq(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn geq(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
let a = args.get(vm, 0);
|
||||
let b = args.get(vm, 1);
|
||||
Ok(Value::from(a >= b))
|
||||
}
|
||||
|
||||
pub fn vec(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn vec(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
static ERROR: &str = "arguments to `vec` must be numbers (vec x y z w)";
|
||||
match args.num() {
|
||||
1 => {
|
||||
|
@ -465,7 +470,7 @@ pub mod fns {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn vec_x(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn vec_x(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`vecX` expects a single argument (vecX vec)"));
|
||||
}
|
||||
|
@ -474,7 +479,7 @@ pub mod fns {
|
|||
Ok(Value::Number(vec.x))
|
||||
}
|
||||
|
||||
pub fn vec_y(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn vec_y(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`vecY` expects a single argument (vecY vec)"));
|
||||
}
|
||||
|
@ -483,7 +488,7 @@ pub mod fns {
|
|||
Ok(Value::Number(vec.y))
|
||||
}
|
||||
|
||||
pub fn vec_z(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn vec_z(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`vecZ` expects a single argument (vecZ vec)"));
|
||||
}
|
||||
|
@ -492,7 +497,7 @@ pub mod fns {
|
|||
Ok(Value::Number(vec.z))
|
||||
}
|
||||
|
||||
pub fn vec_w(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn vec_w(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`vecW` expects a single argument (vecW vec)"));
|
||||
}
|
||||
|
@ -501,7 +506,7 @@ pub mod fns {
|
|||
Ok(Value::Number(vec.w))
|
||||
}
|
||||
|
||||
pub fn rgba(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn rgba(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 4 {
|
||||
return Err(vm.create_exception("`rgba` expects four arguments (rgba r g b a)"));
|
||||
}
|
||||
|
@ -515,7 +520,7 @@ pub mod fns {
|
|||
Ok(Value::Rgba(Rgba { r, g, b, a }))
|
||||
}
|
||||
|
||||
pub fn rgba_r(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn rgba_r(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`rgbaR` expects a single argument (rgbaR rgba)"));
|
||||
}
|
||||
|
@ -524,7 +529,7 @@ pub mod fns {
|
|||
Ok(Value::Number(rgba.r))
|
||||
}
|
||||
|
||||
pub fn rgba_g(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn rgba_g(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`rgbaG` expects a single argument (rgbaG rgba)"));
|
||||
}
|
||||
|
@ -533,7 +538,7 @@ pub mod fns {
|
|||
Ok(Value::Number(rgba.r))
|
||||
}
|
||||
|
||||
pub fn rgba_b(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn rgba_b(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`rgbaB` expects a single argument (rgbaB rgba)"));
|
||||
}
|
||||
|
@ -542,7 +547,7 @@ pub mod fns {
|
|||
Ok(Value::Number(rgba.r))
|
||||
}
|
||||
|
||||
pub fn rgba_a(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn rgba_a(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`rgbaA` expects a single argument (rgbaA rgba)"));
|
||||
}
|
||||
|
@ -551,7 +556,7 @@ pub mod fns {
|
|||
Ok(Value::Number(rgba.r))
|
||||
}
|
||||
|
||||
pub fn len(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn len(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`len` expects a single argument (len list)"));
|
||||
}
|
||||
|
@ -560,7 +565,7 @@ pub mod fns {
|
|||
Ok(Value::Number(list.elements.len() as f32))
|
||||
}
|
||||
|
||||
pub fn index(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn index(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 2 {
|
||||
return Err(vm.create_exception("`index` expects two arguments (index list i)"));
|
||||
}
|
||||
|
@ -580,6 +585,174 @@ pub mod fns {
|
|||
)))
|
||||
}
|
||||
|
||||
pub fn range(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 2 {
|
||||
return Err(vm.create_exception("`range` expects two arguments (range min max)"));
|
||||
}
|
||||
|
||||
let min =
|
||||
args.get_number(vm, 0, "1st argument to (range min max) must be a number")? as i64;
|
||||
let max =
|
||||
args.get_number(vm, 1, "2nd argument to (range min max) must be a number")? as i64;
|
||||
|
||||
// Careful here. We don't want someone to generate a list that's so long it blows up the server.
|
||||
// Therefore generating a list consumes fuel, in addition to bulk memory.
|
||||
// An important thing here is to perform any checks ahead of time, before the list is allocated.
|
||||
let elements: Vec<_> = if min < max {
|
||||
let len = (min..=max).count();
|
||||
vm.consume_fuel(len)?;
|
||||
vm.track_array::<Value>(len)?;
|
||||
|
||||
(min..=max).map(|v| Value::Number(v as f32)).collect()
|
||||
} else {
|
||||
let len = (max..=min).count();
|
||||
vm.consume_fuel(len)?;
|
||||
vm.track_array::<Value>(len)?;
|
||||
|
||||
(max..=min).rev().map(|v| Value::Number(v as f32)).collect()
|
||||
};
|
||||
Ok(Value::Ref(vm.create_ref(Ref::List(List { elements }))?))
|
||||
}
|
||||
|
||||
pub fn map(system: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 2 {
|
||||
return Err(vm.create_exception("`map` expects two arguments (map list function)"));
|
||||
}
|
||||
|
||||
let list = args.get_list(vm, 0, "1st argument to (map list function) must be a list")?;
|
||||
let (function_id, _) = args.get_closure(
|
||||
vm,
|
||||
1,
|
||||
1,
|
||||
"2nd argument to (map list function) must be a single-argument function",
|
||||
)?;
|
||||
|
||||
// Unlike with (range), we don't consume fuel---because running the function itself is enough.
|
||||
let len = list.elements.len();
|
||||
vm.track_array::<Value>(len)?;
|
||||
|
||||
// Weird reborrow required due to track_array consuming a mutable Vm reference.
|
||||
let (_, Ref::List(list)) = vm.get_ref_value(args.get(vm, 0)).unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut result = list.clone();
|
||||
for i in 0..result.elements.len() {
|
||||
let value = result.elements[i];
|
||||
let mapped = vm.run(system, function_id, &[value])?;
|
||||
result.elements[i] = mapped;
|
||||
}
|
||||
|
||||
Ok(Value::Ref(vm.create_ref(Ref::List(result))?))
|
||||
}
|
||||
|
||||
pub fn filter(system: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 2 {
|
||||
return Err(
|
||||
vm.create_exception("`filter` expects two arguments (filter list function)")
|
||||
);
|
||||
}
|
||||
|
||||
let list = args.get_list(
|
||||
vm,
|
||||
0,
|
||||
"1st argument to (filter list function) must be a list",
|
||||
)?;
|
||||
let (function_id, _) = args.get_closure(
|
||||
vm,
|
||||
1,
|
||||
1,
|
||||
"2nd argument to (filter list function) must be a single-argument function",
|
||||
)?;
|
||||
|
||||
let len = list.elements.len();
|
||||
vm.track_array::<Value>(len)?;
|
||||
|
||||
let (_, Ref::List(list)) = vm.get_ref_value(args.get(vm, 0)).unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut result = list.clone();
|
||||
let mut error = Ok(());
|
||||
result.elements.retain(|&value| {
|
||||
if error.is_err() {
|
||||
return true;
|
||||
}
|
||||
match vm.run(system, function_id, &[value]) {
|
||||
Ok(value) => value.is_truthy(),
|
||||
Err(err) => {
|
||||
error = Err(err);
|
||||
true
|
||||
}
|
||||
}
|
||||
});
|
||||
error?;
|
||||
|
||||
Ok(Value::Ref(vm.create_ref(Ref::List(result))?))
|
||||
}
|
||||
|
||||
pub fn reduce(system: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 3 {
|
||||
return Err(
|
||||
vm.create_exception("`reduce` expects three arguments (reduce list init function)")
|
||||
);
|
||||
}
|
||||
|
||||
let num = args
|
||||
.get_list(
|
||||
vm,
|
||||
0,
|
||||
"1st argument to (reduce list init function) must be a list",
|
||||
)?
|
||||
.elements
|
||||
.len();
|
||||
let (function_id, _) = args.get_closure(
|
||||
vm,
|
||||
2,
|
||||
2,
|
||||
"3rd argument to (reduce list init function) must be a two-argument function",
|
||||
)?;
|
||||
|
||||
let mut result = args.get(vm, 1);
|
||||
|
||||
for i in 0..num {
|
||||
let (_, Ref::List(list)) = vm.get_ref_value(args.get(vm, 0)).unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
let value = list.elements[i];
|
||||
result = vm.run(system, function_id, &[result, value])?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn flatten(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`flatten` expects a single argument"));
|
||||
}
|
||||
|
||||
let list = args.get_list(vm, 0, "1st argument to (map list function) must be a list")?;
|
||||
let len = list.elements.len();
|
||||
|
||||
let mut elements = Vec::with_capacity(list.elements.len());
|
||||
for i in 0..len {
|
||||
let value = args.get_list(vm, 0, "unreachable").unwrap().elements[i];
|
||||
if let Some((_, Ref::List(inner))) = vm.get_ref_value(value) {
|
||||
let len = inner.elements.len();
|
||||
vm.consume_fuel(len)?;
|
||||
vm.track_array::<Value>(len)?;
|
||||
let Some((_, Ref::List(inner))) = vm.get_ref_value(value) else {
|
||||
unreachable!()
|
||||
};
|
||||
elements.extend_from_slice(&inner.elements);
|
||||
} else {
|
||||
vm.consume_fuel(1)?;
|
||||
vm.track_array::<Value>(1)?;
|
||||
elements.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Ref(vm.create_ref(Ref::List(List { elements }))?))
|
||||
}
|
||||
|
||||
fn to_shape(value: Value, vm: &Vm) -> Option<Shape> {
|
||||
match value {
|
||||
Value::Nil
|
||||
|
@ -601,7 +774,7 @@ pub mod fns {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_shape_f(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn to_shape_f(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception("`toShape` expects 1 argument (toShape value)"));
|
||||
}
|
||||
|
@ -614,7 +787,7 @@ pub mod fns {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn line(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn line(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 2 {
|
||||
return Err(vm.create_exception("`line` expects 2 arguments (line start end)"));
|
||||
}
|
||||
|
@ -627,7 +800,7 @@ pub mod fns {
|
|||
Ok(Value::Ref(id))
|
||||
}
|
||||
|
||||
pub fn rect(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn rect(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
static ARGS2: &str = "arguments to 2-argument `rect` must be `vec`";
|
||||
static ARGS4: &str = "arguments to 4-argument `rect` must be numbers";
|
||||
|
||||
|
@ -650,7 +823,7 @@ pub mod fns {
|
|||
Ok(Value::Ref(id))
|
||||
}
|
||||
|
||||
pub fn circle(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn circle(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
static ARGS2: &str = "arguments to 2-argument `circle` must be `vec` and a number";
|
||||
static ARGS3: &str = "arguments to 3-argument `circle` must be numbers";
|
||||
|
||||
|
@ -670,7 +843,7 @@ pub mod fns {
|
|||
Ok(Value::Ref(id))
|
||||
}
|
||||
|
||||
pub fn stroke(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn stroke(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 3 {
|
||||
return Err(
|
||||
vm.create_exception("`stroke` expects 3 arguments (stroke thickness color shape)")
|
||||
|
@ -695,7 +868,7 @@ pub mod fns {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fill(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn fill(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 2 {
|
||||
return Err(vm.create_exception("`fill` expects 2 arguments (fill color shape)"));
|
||||
}
|
||||
|
@ -709,17 +882,19 @@ pub mod fns {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_dotter(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
pub fn with_dotter(_: &System, vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
|
||||
if args.num() != 1 {
|
||||
return Err(vm.create_exception(
|
||||
"`withDotter` expects a single argument (withDotter \\d -> [])",
|
||||
));
|
||||
}
|
||||
|
||||
let draw = args.get_closure(vm, 0, "argument to `withDotter` must be a closure")?;
|
||||
if draw.param_count != 1 {
|
||||
return Err(vm.create_exception("function passed to `withDotter` must take in a single parameter (withDotter \\d -> [])"));
|
||||
}
|
||||
_ = args.get_closure(
|
||||
vm,
|
||||
0,
|
||||
1,
|
||||
"argument to `withDotter` must be a single-parameter function (withDotter \\d -> [])",
|
||||
)?;
|
||||
|
||||
let id = vm.create_ref(Ref::Reticle(Reticle::Dotter {
|
||||
draw: args.get(vm, 0),
|
||||
|
|
|
@ -47,6 +47,13 @@ impl Value {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ref(&self) -> Option<RefId> {
|
||||
match self {
|
||||
Self::Ref(v) => Some(*v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Value {
|
||||
|
|
|
@ -89,6 +89,15 @@ impl Vm {
|
|||
self.fuel = fuel;
|
||||
}
|
||||
|
||||
pub fn consume_fuel(&mut self, amount: usize) -> Result<(), Exception> {
|
||||
let fuel = self
|
||||
.remaining_fuel()
|
||||
.checked_sub(amount)
|
||||
.ok_or_else(|| self.create_exception("code ran for too long"))?;
|
||||
self.set_fuel(fuel);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn image(&self) -> VmImage {
|
||||
assert!(
|
||||
self.stack.is_empty() && self.call_stack.is_empty(),
|
||||
|
@ -197,6 +206,8 @@ impl Vm {
|
|||
.as_closure()
|
||||
.expect("a Closure-type Ref must be passed to `run`");
|
||||
|
||||
vmtrace!("ENTER ref = {closure_id:?}, closure = {closure:?}");
|
||||
|
||||
let mut chunk_id = closure.start.chunk_id;
|
||||
let mut chunk = system.chunk(chunk_id);
|
||||
let mut pc = closure.start.offset as usize;
|
||||
|
@ -216,6 +227,7 @@ impl Vm {
|
|||
#[allow(unused)]
|
||||
let closure = (); // Do not use `closure` after this! Use `get_ref` on `closure_id` instead.
|
||||
|
||||
let call_bottom = self.call_stack.len();
|
||||
self.push_call(CallFrame {
|
||||
closure_id,
|
||||
chunk_id,
|
||||
|
@ -223,6 +235,8 @@ impl Vm {
|
|||
bottom,
|
||||
})?;
|
||||
|
||||
vmtrace!("LOOP");
|
||||
|
||||
loop {
|
||||
fuel = fuel
|
||||
.checked_sub(1)
|
||||
|
@ -231,7 +245,7 @@ impl Vm {
|
|||
#[allow(unused)]
|
||||
let pc2 = pc;
|
||||
let opcode = chunk.read_opcode(&mut pc)?;
|
||||
vmtrace!("{pc2:2} {opcode:?}");
|
||||
vmtrace!("insn {pc2:2} {opcode:?}");
|
||||
match opcode {
|
||||
Opcode::Nil => self.push(Value::Nil)?,
|
||||
Opcode::False => self.push(Value::False)?,
|
||||
|
@ -300,6 +314,7 @@ impl Vm {
|
|||
|
||||
Opcode::List => {
|
||||
let len = chunk.read_u16(&mut pc)? as usize;
|
||||
self.track_array::<Value>(len)?;
|
||||
let bottom = self.stack.len().checked_sub(len).ok_or_else(|| {
|
||||
self.create_exception(
|
||||
"corrupted bytecode (list has more elements than stack)",
|
||||
|
@ -307,7 +322,6 @@ impl Vm {
|
|||
})?;
|
||||
let elements = self.stack[bottom..].to_vec();
|
||||
self.stack.resize_with(bottom, || unreachable!());
|
||||
self.track_array(&elements)?;
|
||||
let id = self.create_ref(Ref::List(List { elements }))?;
|
||||
self.push(Value::Ref(id))?;
|
||||
}
|
||||
|
@ -349,7 +363,7 @@ impl Vm {
|
|||
local_count,
|
||||
captures,
|
||||
};
|
||||
vmtrace!("{closure:?}");
|
||||
vmtrace!(" : {closure:?}");
|
||||
let id = self.create_ref(Ref::Closure(closure))?;
|
||||
self.push(Value::Ref(id))?;
|
||||
}
|
||||
|
@ -449,6 +463,7 @@ impl Vm {
|
|||
|
||||
self.store_context(Context { fuel });
|
||||
let result = system_fn(
|
||||
system,
|
||||
self,
|
||||
FnArgs {
|
||||
base: self
|
||||
|
@ -475,7 +490,8 @@ impl Vm {
|
|||
self.push(value)?;
|
||||
|
||||
// Once the initial frame is popped, halt the VM.
|
||||
if self.call_stack.is_empty() {
|
||||
debug_assert!(self.call_stack.len() >= call_bottom);
|
||||
if self.call_stack.len() == call_bottom {
|
||||
self.store_context(Context { fuel });
|
||||
break;
|
||||
}
|
||||
|
@ -491,12 +507,15 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
vmtrace!("EXIT");
|
||||
|
||||
let result = self
|
||||
.stack
|
||||
.pop()
|
||||
.expect("there should be a result at the top of the stack");
|
||||
self.stack.resize_with(init_bottom, || unreachable!());
|
||||
|
||||
vmtrace!("result = {result:?}");
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
@ -535,10 +554,12 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn track_array<T>(&mut self, array: &[T]) -> Result<(), Exception> {
|
||||
pub fn track_array<T>(&mut self, len: usize) -> Result<(), Exception> {
|
||||
let layout = core::alloc::Layout::array::<T>(len)
|
||||
.map_err(|_| self.create_exception("out of heap memory"))?;
|
||||
self.memory = self
|
||||
.memory
|
||||
.checked_sub(core::mem::size_of_val(array))
|
||||
.checked_sub(layout.size())
|
||||
.ok_or_else(|| self.create_exception("out of heap memory"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -611,16 +632,20 @@ impl FnArgs {
|
|||
&self,
|
||||
vm: &'vm Vm,
|
||||
index: usize,
|
||||
param_count: u8,
|
||||
message: &'static str,
|
||||
) -> Result<&'vm Closure, Exception> {
|
||||
) -> Result<(RefId, &'vm Closure), Exception> {
|
||||
let value = self.get(vm, index);
|
||||
let (_, any_ref) = vm
|
||||
let (ref_id, any_ref) = vm
|
||||
.get_ref_value(value)
|
||||
.ok_or_else(|| vm.create_exception(message))?;
|
||||
let Ref::Closure(closure) = any_ref else {
|
||||
return Err(vm.create_exception(message));
|
||||
};
|
||||
Ok(closure)
|
||||
if closure.param_count != param_count {
|
||||
return Err(vm.create_exception(message));
|
||||
}
|
||||
Ok((ref_id, closure))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue