diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-06-26 18:07:05 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-06-26 18:07:05 +0200 |
| commit | 422b8e640f00977177a5a7250a3c56009eed10c4 (patch) | |
| tree | b1a21718e9511d776a6de47b713e3308bb1baaad /src/eval | |
| parent | d53c933e4d56e6c0484d81814779ddb1597ee032 (diff) | |
With expressions
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/mod.rs | 46 | ||||
| -rw-r--r-- | src/eval/value.rs | 20 |
2 files changed, 47 insertions, 19 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 116d3c12..a9fff56b 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -263,7 +263,8 @@ impl Eval for Expr { Self::Group(ref v) => v.eval(ctx), Self::Block(ref v) => v.eval(ctx), Self::Call(ref v) => v.eval(ctx), - Self::Closure(ref v) => Value::Func(v.eval(ctx)), + Self::Closure(ref v) => v.eval(ctx), + Self::With(ref v) => v.eval(ctx), Self::Unary(ref v) => v.eval(ctx), Self::Binary(ref v) => v.eval(ctx), Self::Let(ref v) => v.eval(ctx), @@ -498,11 +499,13 @@ impl Eval for CallArg { fn eval(&self, ctx: &mut EvalContext) -> Self::Output { match self { Self::Pos(expr) => FuncArg { + span: self.span(), name: None, value: Spanned::new(expr.eval(ctx), expr.span()), }, Self::Named(Named { name, expr }) => FuncArg { - name: Some(Spanned::new(name.string.clone(), name.span)), + span: self.span(), + name: Some(name.string.clone()), value: Spanned::new(expr.eval(ctx), expr.span()), }, } @@ -510,7 +513,7 @@ impl Eval for CallArg { } impl Eval for ClosureExpr { - type Output = FuncValue; + type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { let params = Rc::clone(&self.params); @@ -524,7 +527,7 @@ impl Eval for ClosureExpr { }; let name = self.name.as_ref().map(|id| id.to_string()); - FuncValue::new(name, move |ctx, args| { + Value::Func(FuncValue::new(name, move |ctx, args| { // Don't leak the scopes from the call site. Instead, we use the // scope of captured variables we collected earlier. let prev = mem::take(&mut ctx.scopes); @@ -539,7 +542,40 @@ impl Eval for ClosureExpr { let value = body.eval(ctx); ctx.scopes = prev; value - }) + })) + } +} + +impl Eval for WithExpr { + type Output = Value; + + fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + let callee = self.callee.eval(ctx); + if let Some(func) = ctx.cast::<FuncValue>(callee, self.callee.span()) { + let applied = self.args.eval(ctx); + let name = func.name().map(|s| s.to_string()); + Value::Func(FuncValue::new(name, move |ctx, args| { + // Remove named arguments that were overridden. + let kept: Vec<_> = applied + .items + .iter() + .filter(|arg| { + arg.name.is_none() + || args.items.iter().all(|other| arg.name != other.name) + }) + .cloned() + .collect(); + + // Preprend the applied arguments so that the positional arguments + // are in the right order. + args.items.splice(.. 0, kept); + + // Call the original function. + func(ctx, args) + })) + } else { + Value::Error + } } } diff --git a/src/eval/value.rs b/src/eval/value.rs index e8ecffa2..28c7d588 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -274,7 +274,7 @@ impl Debug for FuncValue { pub struct FuncArgs { /// The span of the whole argument list. pub span: Span, - /// The arguments. + /// The positional arguments. pub items: Vec<FuncArg>, } @@ -346,7 +346,7 @@ impl FuncArgs { let index = self .items .iter() - .position(|arg| arg.name.as_ref().map(|s| s.v.as_str()) == Some(name))?; + .position(|arg| arg.name.as_ref().map_or(false, |other| name == other))?; let value = self.items.remove(index).value; let span = value.span; @@ -373,7 +373,7 @@ impl FuncArgs { pub fn finish(self, ctx: &mut EvalContext) { for arg in &self.items { if arg.value.v != Value::Error { - ctx.diag(error!(arg.span(), "unexpected argument")); + ctx.diag(error!(arg.span, "unexpected argument")); } } } @@ -382,22 +382,14 @@ impl FuncArgs { /// An argument to a function call: `12` or `draw: false`. #[derive(Debug, Clone, PartialEq)] pub struct FuncArg { + /// The span of the whole argument. + pub span: Span, /// The name of the argument (`None` for positional arguments). - pub name: Option<Spanned<String>>, + pub name: Option<String>, /// The value of the argument. pub value: Spanned<Value>, } -impl FuncArg { - /// The source code location. - pub fn span(&self) -> Span { - match &self.name { - Some(name) => name.span.join(self.value.span), - None => self.value.span, - } - } -} - /// A wrapper around a dynamic value. pub struct AnyValue(Box<dyn Bounds>); |
