implement pattern calling
This commit is contained in:
@@ -242,6 +242,9 @@ pub enum PrimOpPhase {
|
||||
ForceResultShallowLoop,
|
||||
ForceResultDeepFinish,
|
||||
|
||||
// TODO: split into separate enums
|
||||
CallPattern,
|
||||
|
||||
Illegal,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use fix_builtins::PrimOpPhase;
|
||||
use fix_error::Error;
|
||||
use gc_arena::{Gc, Mutation};
|
||||
use gc_arena::{Gc, Mutation, RefLock};
|
||||
use num_enum::TryFromPrimitive as _;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
@@ -37,6 +37,8 @@ impl<'gc> Vm<'gc> {
|
||||
ForceResultShallowLoop => self.primop_force_result_shallow_loop(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:?}"),
|
||||
}
|
||||
}
|
||||
@@ -368,6 +370,56 @@ impl<'gc> Vm<'gc> {
|
||||
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 {
|
||||
if !is_container(val) {
|
||||
return false;
|
||||
|
||||
@@ -23,12 +23,24 @@ impl<'gc> crate::Vm<'gc> {
|
||||
}
|
||||
self.call_depth += 1;
|
||||
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 n_locals = closure.n_locals;
|
||||
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)));
|
||||
self.call_stack.push(CallFrame {
|
||||
pc: resume_pc,
|
||||
@@ -39,7 +51,6 @@ impl<'gc> crate::Vm<'gc> {
|
||||
});
|
||||
reader.set_pc(ip as usize);
|
||||
self.env = new_env;
|
||||
}
|
||||
} else if let Some(primop) = func.as_inline::<PrimOp>() {
|
||||
if primop.arity == 1 {
|
||||
self.push(arg);
|
||||
|
||||
Reference in New Issue
Block a user