diff options
Diffstat (limited to 'src/eval/mod.rs')
| -rw-r--r-- | src/eval/mod.rs | 253 |
1 files changed, 155 insertions, 98 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index e911596d..abb7cab7 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -20,6 +20,7 @@ pub use scope::*; pub use template::*; pub use value::*; +use std::cell::RefMut; use std::collections::HashMap; use std::io; use std::mem; @@ -34,7 +35,7 @@ use crate::parse::parse; use crate::source::{SourceId, SourceStore}; use crate::syntax::visit::Visit; use crate::syntax::*; -use crate::util::EcoString; +use crate::util::{EcoString, RefMutExt}; use crate::Context; /// Evaluate a parsed source file into a module. @@ -60,15 +61,6 @@ pub struct Module { pub template: Template, } -/// Evaluate an expression. -pub trait Eval { - /// The output of evaluating the expression. - type Output; - - /// Evaluate the expression to the output value. - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output>; -} - /// The context for evaluation. pub struct EvalContext<'a> { /// The loader from which resources (files and images) are loaded. @@ -168,45 +160,19 @@ impl<'a> EvalContext<'a> { } } +/// Evaluate an expression. +pub trait Eval { + /// The output of evaluating the expression. + type Output; + + /// Evaluate the expression to the output value. + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output>; +} + impl Eval for Rc<SyntaxTree> { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - trait Walk { - fn walk(&self, ctx: &mut EvalContext) -> TypResult<()>; - } - - impl Walk for SyntaxTree { - fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> { - for node in self.iter() { - node.walk(ctx)?; - } - Ok(()) - } - } - - impl Walk for SyntaxNode { - fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> { - match self { - Self::Text(_) => {} - Self::Space => {} - Self::Linebreak(_) => {} - Self::Parbreak(_) => {} - Self::Strong(_) => {} - Self::Emph(_) => {} - Self::Raw(_) => {} - Self::Heading(n) => n.body.walk(ctx)?, - Self::List(n) => n.body.walk(ctx)?, - Self::Enum(n) => n.body.walk(ctx)?, - Self::Expr(n) => { - let value = n.eval(ctx)?; - ctx.map.insert(n as *const _, value); - } - } - Ok(()) - } - } - let map = { let prev = mem::take(&mut ctx.map); self.walk(ctx)?; @@ -302,8 +268,8 @@ impl Eval for BlockExpr { let mut output = Value::None; for expr in &self.exprs { let value = expr.eval(ctx)?; - output = ops::join(output, value) - .map_err(Error::partial(ctx.source, expr.span()))?; + output = + ops::join(output, value).map_err(Error::at(ctx.source, expr.span()))?; } if self.scoping { @@ -324,7 +290,7 @@ impl Eval for UnaryExpr { UnOp::Neg => ops::neg(value), UnOp::Not => ops::not(value), }; - result.map_err(Error::partial(ctx.source, self.span)) + result.map_err(Error::at(ctx.source, self.span)) } } @@ -371,7 +337,7 @@ impl BinaryExpr { } let rhs = self.rhs.eval(ctx)?; - op(lhs, rhs).map_err(Error::partial(ctx.source, self.span)) + op(lhs, rhs).map_err(Error::at(ctx.source, self.span)) } /// Apply an assignment operation. @@ -379,27 +345,11 @@ impl BinaryExpr { where F: FnOnce(Value, Value) -> StrResult<Value>, { - let lspan = self.lhs.span(); - let slot = if let Expr::Ident(id) = self.lhs.as_ref() { - match ctx.scopes.get(id) { - Some(slot) => Rc::clone(slot), - None => bail!(ctx.source, lspan, "unknown variable"), - } - } else { - bail!(ctx.source, lspan, "cannot assign to this expression",); - }; - + let source = ctx.source; let rhs = self.rhs.eval(ctx)?; - let mut mutable = match slot.try_borrow_mut() { - Ok(mutable) => mutable, - Err(_) => { - bail!(ctx.source, lspan, "cannot assign to a constant",); - } - }; - - let lhs = mem::take(&mut *mutable); - *mutable = op(lhs, rhs).map_err(Error::partial(ctx.source, self.span))?; - + let mut target = self.lhs.access(ctx)?; + let lhs = mem::take(&mut *target); + *target = op(lhs, rhs).map_err(Error::at(source, self.span))?; Ok(Value::None) } } @@ -408,32 +358,45 @@ impl Eval for CallExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - let callee = self - .callee - .eval(ctx)? - .cast::<Function>() - .map_err(Error::partial(ctx.source, self.callee.span()))?; - + let callee = self.callee.eval(ctx)?; let mut args = self.args.eval(ctx)?; - let returned = callee(ctx, &mut args).map_err(|mut errors| { - for error in errors.iter_mut() { - // Skip errors directly related to arguments. - if error.source == ctx.source && self.span.contains(error.span) { - continue; - } - error.trace.push(( - ctx.source, - self.span, - Tracepoint::Call(callee.name().map(Into::into)), - )); + match callee { + Value::Array(array) => array + .get(args.into_index()?) + .map(Value::clone) + .map_err(Error::at(ctx.source, self.span)), + + Value::Dict(dict) => dict + .get(&args.into_key()?) + .map(Value::clone) + .map_err(Error::at(ctx.source, self.span)), + + Value::Func(func) => { + let returned = func(ctx, &mut args).map_err(|mut errors| { + for error in errors.iter_mut() { + // Skip errors directly related to arguments. + if error.source != ctx.source || !self.span.contains(error.span) { + error.trace.push(( + ctx.source, + self.span, + Tracepoint::Call(func.name().map(Into::into)), + )); + } + } + errors + })?; + args.finish()?; + Ok(returned) } - errors - })?; - args.finish()?; - - Ok(returned) + v => bail!( + ctx.source, + self.callee.span(), + "expected function or collection, found {}", + v.type_name(), + ), + } } } @@ -518,7 +481,7 @@ impl Eval for WithExpr { .callee .eval(ctx)? .cast::<Function>() - .map_err(Error::partial(ctx.source, self.callee.span()))?; + .map_err(Error::at(ctx.source, self.callee.span()))?; let applied = self.args.eval(ctx)?; @@ -568,7 +531,7 @@ impl Eval for IfExpr { .condition .eval(ctx)? .cast::<bool>() - .map_err(Error::partial(ctx.source, self.condition.span()))?; + .map_err(Error::at(ctx.source, self.condition.span()))?; if condition { self.if_body.eval(ctx) @@ -590,11 +553,11 @@ impl Eval for WhileExpr { .condition .eval(ctx)? .cast::<bool>() - .map_err(Error::partial(ctx.source, self.condition.span()))? + .map_err(Error::at(ctx.source, self.condition.span()))? { let value = self.body.eval(ctx)?; output = ops::join(output, value) - .map_err(Error::partial(ctx.source, self.body.span()))?; + .map_err(Error::at(ctx.source, self.body.span()))?; } Ok(output) @@ -616,7 +579,7 @@ impl Eval for ForExpr { let value = self.body.eval(ctx)?; output = ops::join(output, value) - .map_err(Error::partial(ctx.source, self.body.span()))?; + .map_err(Error::at(ctx.source, self.body.span()))?; } ctx.scopes.exit(); @@ -662,7 +625,7 @@ impl Eval for ImportExpr { .path .eval(ctx)? .cast::<EcoString>() - .map_err(Error::partial(ctx.source, self.path.span()))?; + .map_err(Error::at(ctx.source, self.path.span()))?; let file = ctx.import(&path, self.path.span())?; let module = &ctx.modules[&file]; @@ -696,7 +659,7 @@ impl Eval for IncludeExpr { .path .eval(ctx)? .cast::<EcoString>() - .map_err(Error::partial(ctx.source, self.path.span()))?; + .map_err(Error::at(ctx.source, self.path.span()))?; let file = ctx.import(&path, self.path.span())?; let module = &ctx.modules[&file]; @@ -704,3 +667,97 @@ impl Eval for IncludeExpr { Ok(Value::Template(module.template.clone())) } } + +/// Walk a node in a template, filling the context's expression map. +pub trait Walk { + /// Walk the node. + fn walk(&self, ctx: &mut EvalContext) -> TypResult<()>; +} + +impl Walk for SyntaxTree { + fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> { + for node in self.iter() { + node.walk(ctx)?; + } + Ok(()) + } +} + +impl Walk for SyntaxNode { + fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> { + match self { + Self::Text(_) => {} + Self::Space => {} + Self::Linebreak(_) => {} + Self::Parbreak(_) => {} + Self::Strong(_) => {} + Self::Emph(_) => {} + Self::Raw(_) => {} + Self::Heading(n) => n.body.walk(ctx)?, + Self::List(n) => n.body.walk(ctx)?, + Self::Enum(n) => n.body.walk(ctx)?, + Self::Expr(n) => { + let value = n.eval(ctx)?; + ctx.map.insert(n as *const _, value); + } + } + Ok(()) + } +} + +/// Try to mutably access the value an expression points to. +/// +/// This only works if the expression is a valid lvalue. +pub trait Access { + /// Try to access the value. + fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>>; +} + +impl Access for Expr { + fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> { + match self { + Expr::Ident(ident) => ident.access(ctx), + Expr::Call(call) => call.access(ctx), + _ => bail!( + ctx.source, + self.span(), + "cannot access this expression mutably", + ), + } + } +} + +impl Access for Ident { + fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> { + ctx.scopes + .get(self) + .ok_or("unknown variable") + .and_then(|slot| { + slot.try_borrow_mut().map_err(|_| "cannot mutate a constant") + }) + .map_err(Error::at(ctx.source, self.span)) + } +} + +impl Access for CallExpr { + fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> { + let source = ctx.source; + let args = self.args.eval(ctx)?; + let guard = self.callee.access(ctx)?; + + RefMut::try_map(guard, |value| match value { + Value::Array(array) => array + .get_mut(args.into_index()?) + .map_err(Error::at(source, self.span)), + + Value::Dict(dict) => Ok(dict.get_mut(args.into_key()?)), + + v => bail!( + source, + self.callee.span(), + "expected collection, found {}", + v.type_name(), + ), + }) + } +} |
