implement pattern calling
This commit is contained in:
@@ -242,6 +242,9 @@ pub enum PrimOpPhase {
|
|||||||
ForceResultShallowLoop,
|
ForceResultShallowLoop,
|
||||||
ForceResultDeepFinish,
|
ForceResultDeepFinish,
|
||||||
|
|
||||||
|
// TODO: split into separate enums
|
||||||
|
CallPattern,
|
||||||
|
|
||||||
Illegal,
|
Illegal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use fix_builtins::PrimOpPhase;
|
use fix_builtins::PrimOpPhase;
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use gc_arena::{Gc, Mutation};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
use num_enum::TryFromPrimitive as _;
|
use num_enum::TryFromPrimitive as _;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ impl<'gc> Vm<'gc> {
|
|||||||
ForceResultShallowLoop => self.primop_force_result_shallow_loop(reader, mc),
|
ForceResultShallowLoop => self.primop_force_result_shallow_loop(reader, mc),
|
||||||
ForceResultDeepFinish => self.primop_force_result_deep_finish(ctx, reader, mc),
|
ForceResultDeepFinish => self.primop_force_result_deep_finish(ctx, reader, mc),
|
||||||
|
|
||||||
|
CallPattern => self.primop_call_pattern(ctx, reader, mc),
|
||||||
|
|
||||||
phase => todo!("primop phase {phase:?}"),
|
phase => todo!("primop phase {phase:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,6 +370,56 @@ impl<'gc> Vm<'gc> {
|
|||||||
self.finish_ok(ctx.convert_value(val.relax()))
|
self.finish_ok(ctx.convert_value(val.relax()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn primop_call_pattern(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut impl VmRuntimeCtx,
|
||||||
|
reader: &mut BytecodeReader<'_>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Step {
|
||||||
|
let (func, attrset) = self.try_force::<(Gc<Closure>, Gc<AttrSet>)>(reader, mc)?;
|
||||||
|
|
||||||
|
let Closure {
|
||||||
|
ip,
|
||||||
|
n_locals,
|
||||||
|
env,
|
||||||
|
pattern,
|
||||||
|
} = *func;
|
||||||
|
let Some(pattern) = pattern else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
// TODO: get function name
|
||||||
|
// TODO: param spans
|
||||||
|
if !pattern.ellipsis {
|
||||||
|
for key in pattern.required.iter().copied() {
|
||||||
|
if attrset.lookup(key).is_none() {
|
||||||
|
let name = ctx.resolve_string(key);
|
||||||
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
"function 'anonymous lambda' called without required argument '{name}'"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for &(key, _) in attrset.iter() {
|
||||||
|
let is_expected =
|
||||||
|
pattern.required.contains(&key) || pattern.optional.contains(&key);
|
||||||
|
if !is_expected {
|
||||||
|
let name = ctx.resolve_string(key);
|
||||||
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
"function 'anonymous lambda' called with unexpected argument '{name}'"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_env = Gc::new(
|
||||||
|
mc,
|
||||||
|
RefLock::new(Env::with_arg(Value::new_gc(attrset), n_locals, env)),
|
||||||
|
);
|
||||||
|
reader.set_pc(ip as usize);
|
||||||
|
self.env = new_env;
|
||||||
|
|
||||||
|
Step::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_value_in_seen(&self, seen: Gc<'gc, List<'gc>>, val: Value<'gc>) -> bool {
|
fn is_value_in_seen(&self, seen: Gc<'gc, List<'gc>>, val: Value<'gc>) -> bool {
|
||||||
if !is_container(val) {
|
if !is_container(val) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,12 +23,24 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
}
|
}
|
||||||
self.call_depth += 1;
|
self.call_depth += 1;
|
||||||
if let Some(closure) = func.as_gc::<Closure>() {
|
if let Some(closure) = func.as_gc::<Closure>() {
|
||||||
|
if closure.pattern.is_some() {
|
||||||
|
// FIXME: better DX...
|
||||||
|
self.push(func.relax());
|
||||||
|
self.push(arg);
|
||||||
|
self.call_stack.push(CallFrame {
|
||||||
|
pc: resume_pc,
|
||||||
|
stack_depth: 0,
|
||||||
|
thunk: None,
|
||||||
|
env: self.env,
|
||||||
|
with_env: self.with_env,
|
||||||
|
});
|
||||||
|
reader.set_pc(PrimOpPhase::CallPattern.ip() as usize);
|
||||||
|
return Step::Continue(());
|
||||||
|
}
|
||||||
|
|
||||||
let ip = closure.ip;
|
let ip = closure.ip;
|
||||||
let n_locals = closure.n_locals;
|
let n_locals = closure.n_locals;
|
||||||
let env = closure.env;
|
let env = closure.env;
|
||||||
if let Some(ref _pattern) = closure.pattern {
|
|
||||||
todo!("pattern call")
|
|
||||||
} else {
|
|
||||||
let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env)));
|
let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env)));
|
||||||
self.call_stack.push(CallFrame {
|
self.call_stack.push(CallFrame {
|
||||||
pc: resume_pc,
|
pc: resume_pc,
|
||||||
@@ -39,7 +51,6 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
});
|
});
|
||||||
reader.set_pc(ip as usize);
|
reader.set_pc(ip as usize);
|
||||||
self.env = new_env;
|
self.env = new_env;
|
||||||
}
|
|
||||||
} else if let Some(primop) = func.as_inline::<PrimOp>() {
|
} else if let Some(primop) = func.as_inline::<PrimOp>() {
|
||||||
if primop.arity == 1 {
|
if primop.arity == 1 {
|
||||||
self.push(arg);
|
self.push(arg);
|
||||||
|
|||||||
Reference in New Issue
Block a user