implement pattern calling

This commit is contained in:
2026-04-27 18:17:19 +08:00
parent a28dfada30
commit fe96f6d9c5
4 changed files with 77 additions and 10 deletions
+3
View File
@@ -242,6 +242,9 @@ pub enum PrimOpPhase {
ForceResultShallowLoop, ForceResultShallowLoop,
ForceResultDeepFinish, ForceResultDeepFinish,
// TODO: split into separate enums
CallPattern,
Illegal, Illegal,
} }
+53 -1
View File
@@ -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;
+20 -9
View File
@@ -23,13 +23,10 @@ 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>() {
let ip = closure.ip; if closure.pattern.is_some() {
let n_locals = closure.n_locals; // FIXME: better DX...
let env = closure.env; self.push(func.relax());
if let Some(ref _pattern) = closure.pattern { self.push(arg);
todo!("pattern call")
} else {
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,
stack_depth: 0, stack_depth: 0,
@@ -37,9 +34,23 @@ impl<'gc> crate::Vm<'gc> {
env: self.env, env: self.env,
with_env: self.with_env, with_env: self.with_env,
}); });
reader.set_pc(ip as usize); reader.set_pc(PrimOpPhase::CallPattern.ip() as usize);
self.env = new_env; return Step::Continue(());
} }
let ip = closure.ip;
let n_locals = closure.n_locals;
let env = closure.env;
let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env)));
self.call_stack.push(CallFrame {
pc: resume_pc,
stack_depth: 0,
thunk: None,
env: self.env,
with_env: self.with_env,
});
reader.set_pc(ip as usize);
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);
+1
View File
@@ -50,6 +50,7 @@
llm-agents.claude-code llm-agents.claude-code
llm-agents.opencode llm-agents.opencode
llm-agents.forge
]; ];
}; };
} }