summaryrefslogtreecommitdiff
path: root/src/eval/func.rs
diff options
context:
space:
mode:
authorMarmare314 <49279081+Marmare314@users.noreply.github.com>2023-04-13 16:07:58 +0200
committerGitHub <noreply@github.com>2023-04-13 16:07:58 +0200
commit0105eb7382801b56781308ea94b3aeffa6fd867f (patch)
tree9374989fb8999f4d1ca1362c8b96679cc61be9e0 /src/eval/func.rs
parentd1cd814ef8149cbac6e59c81e074aa59c930eed3 (diff)
Fix function sinks (#638)
Diffstat (limited to 'src/eval/func.rs')
-rw-r--r--src/eval/func.rs61
1 files changed, 43 insertions, 18 deletions
diff --git a/src/eval/func.rs b/src/eval/func.rs
index d64ca732..582eabd4 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -260,15 +260,22 @@ pub(super) struct Closure {
pub name: Option<Ident>,
/// Captured values from outer scopes.
pub captured: Scope,
- /// The parameter names and default values. Parameters with default value
- /// are named parameters.
- pub params: Vec<(Ident, Option<Value>)>,
- /// The name of an argument sink where remaining arguments are placed.
- pub sink: Option<Ident>,
+ /// The list of parameters.
+ pub params: Vec<Param>,
/// The expression the closure should evaluate to.
pub body: Expr,
}
+#[derive(Hash)]
+pub enum Param {
+ /// A positional parameter: `x`.
+ Pos(Ident),
+ /// A named parameter with a default value: `draw: false`.
+ Named(Ident, Value),
+ /// An argument sink: `..args`.
+ Sink(Option<Ident>),
+}
+
impl Closure {
/// Call the function in the context with the arguments.
#[allow(clippy::too_many_arguments)]
@@ -304,21 +311,38 @@ impl Closure {
}
// Parse the arguments according to the parameter list.
- for (param, default) in &closure.params {
- vm.define(
- param.clone(),
- match default {
- Some(default) => {
- args.named::<Value>(param)?.unwrap_or_else(|| default.clone())
+ let num_pos_params =
+ closure.params.iter().filter(|p| matches!(p, Param::Pos(_))).count();
+ let num_pos_args = args.to_pos().len() as usize;
+ let sink_size = num_pos_args.checked_sub(num_pos_params);
+
+ let mut sink = None;
+ let mut sink_pos_values = None;
+ for p in &closure.params {
+ match p {
+ Param::Pos(ident) => {
+ vm.define(ident.clone(), args.expect::<Value>(ident)?);
+ }
+ Param::Sink(ident) => {
+ sink = ident.clone();
+ if let Some(sink_size) = sink_size {
+ sink_pos_values = Some(args.consume(sink_size)?);
}
- None => args.expect::<Value>(param)?,
- },
- );
+ }
+ Param::Named(ident, default) => {
+ let value =
+ args.named::<Value>(ident)?.unwrap_or_else(|| default.clone());
+ vm.define(ident.clone(), value);
+ }
+ }
}
- // Put the remaining arguments into the sink.
- if let Some(sink) = &closure.sink {
- vm.define(sink.clone(), args.take());
+ if let Some(sink) = sink {
+ let mut remaining_args = args.take();
+ if let Some(sink_pos_values) = sink_pos_values {
+ remaining_args.items.extend(sink_pos_values);
+ }
+ vm.define(sink, remaining_args);
}
// Ensure all arguments have been used.
@@ -407,7 +431,8 @@ impl<'a> CapturesVisitor<'a> {
match param {
ast::Param::Pos(ident) => self.bind(ident),
ast::Param::Named(named) => self.bind(named.name()),
- ast::Param::Sink(ident) => self.bind(ident),
+ ast::Param::Sink(Some(ident)) => self.bind(ident),
+ _ => {}
}
}