summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-06-26 18:07:05 +0200
committerLaurenz <laurmaedje@gmail.com>2021-06-26 18:07:05 +0200
commit422b8e640f00977177a5a7250a3c56009eed10c4 (patch)
treeb1a21718e9511d776a6de47b713e3308bb1baaad /src/eval
parentd53c933e4d56e6c0484d81814779ddb1597ee032 (diff)
With expressions
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/mod.rs46
-rw-r--r--src/eval/value.rs20
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>);