diff options
Diffstat (limited to 'src/eval/mod.rs')
| -rw-r--r-- | src/eval/mod.rs | 593 |
1 files changed, 269 insertions, 324 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 682a3855..decd4281 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -25,22 +25,21 @@ use std::mem; use std::path::Path; use std::rc::Rc; -use crate::diag::{Diag, DiagSet, Pass}; -use crate::util::EcoString; +use crate::diag::{Error, StrResult, TypResult}; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::image::ImageCache; use crate::loading::{FileId, Loader}; use crate::parse::parse; use crate::syntax::visit::Visit; use crate::syntax::*; +use crate::util::EcoString; use crate::Context; /// Evaluate a parsed source file into a module. -pub fn eval(ctx: &mut Context, file: FileId, ast: Rc<SyntaxTree>) -> Pass<Module> { +pub fn eval(ctx: &mut Context, file: FileId, ast: Rc<SyntaxTree>) -> TypResult<Module> { let mut ctx = EvalContext::new(ctx, file); - let template = ast.eval(&mut ctx); - let module = Module { scope: ctx.scopes.top, template }; - Pass::new(module, ctx.diags) + let template = ast.eval(&mut ctx)?; + Ok(Module { scope: ctx.scopes.top, template }) } /// Caches evaluated modules. @@ -61,7 +60,7 @@ pub trait Eval { type Output; /// Evaluate the expression to the output value. - fn eval(&self, ctx: &mut EvalContext) -> Self::Output; + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output>; } /// The context for evaluation. @@ -74,10 +73,12 @@ pub struct EvalContext<'a> { pub modules: &'a mut ModuleCache, /// The active scopes. pub scopes: Scopes<'a>, - /// Evaluation diagnostics. - pub diags: DiagSet, + /// The currently evaluated file. + pub file: FileId, /// The stack of imported files that led to evaluation of the current file. pub route: Vec<FileId>, + /// The expression map for the currently built template. + pub map: ExprMap, } impl<'a> EvalContext<'a> { @@ -88,141 +89,123 @@ impl<'a> EvalContext<'a> { images: &mut ctx.images, modules: &mut ctx.modules, scopes: Scopes::new(Some(&ctx.std)), - diags: DiagSet::new(), - route: vec![file], + file, + route: vec![], + map: ExprMap::new(), } } /// Resolve a path relative to the current file. /// - /// Generates an error if the file is not found. - pub fn resolve(&mut self, path: &str, span: Span) -> Option<FileId> { - let base = *self.route.last()?; - self.loader.resolve_from(base, Path::new(path)).ok().or_else(|| { - self.diag(error!(span, "file not found")); - None - }) + /// Returns an error if the file is not found. + pub fn resolve(&mut self, path: &str, span: Span) -> TypResult<FileId> { + self.loader + .resolve_from(self.file, Path::new(path)) + .map_err(|_| Error::boxed(self.file, span, "file not found")) } /// Process an import of a module relative to the current location. - pub fn import(&mut self, path: &str, span: Span) -> Option<FileId> { + pub fn import(&mut self, path: &str, span: Span) -> TypResult<FileId> { let id = self.resolve(path, span)?; // Prevent cyclic importing. - if self.route.contains(&id) { - self.diag(error!(span, "cyclic import")); - return None; + if self.file == id || self.route.contains(&id) { + bail!(self.file, span, "cyclic import"); } // Check whether the module was already loaded. if self.modules.get(&id).is_some() { - return Some(id); + return Ok(id); } - let buffer = self.loader.load_file(id).ok().or_else(|| { - self.diag(error!(span, "failed to load file")); - None - })?; + // Load the source file. + let buffer = self + .loader + .load_file(id) + .map_err(|_| Error::boxed(self.file, span, "failed to load file"))?; - let string = std::str::from_utf8(&buffer).ok().or_else(|| { - self.diag(error!(span, "file is not valid utf-8")); - None - })?; + // Decode UTF-8. + let string = std::str::from_utf8(&buffer) + .map_err(|_| Error::boxed(self.file, span, "file is not valid utf-8"))?; // Parse the file. - let parsed = parse(string); + let ast = parse(id, string)?; // Prepare the new context. let new_scopes = Scopes::new(self.scopes.base); let old_scopes = mem::replace(&mut self.scopes, new_scopes); - let old_diags = mem::replace(&mut self.diags, parsed.diags); - self.route.push(id); + self.route.push(self.file); + self.file = id; // Evaluate the module. - let ast = Rc::new(parsed.output); - let template = ast.eval(self); + let template = Rc::new(ast).eval(self)?; // Restore the old context. let new_scopes = mem::replace(&mut self.scopes, old_scopes); - let new_diags = mem::replace(&mut self.diags, old_diags); - self.route.pop(); - - // Put all diagnostics from the module on the import. - for mut diag in new_diags { - diag.span = span; - self.diag(diag); - } + self.file = self.route.pop().unwrap(); // Save the evaluated module. let module = Module { scope: new_scopes.top, template }; self.modules.insert(id, module); - Some(id) - } - - /// Add a diagnostic. - pub fn diag(&mut self, diag: Diag) { - self.diags.insert(diag); - } - - /// Cast a value to a type and diagnose a possible error / warning. - pub fn cast<T>(&mut self, value: Value, span: Span) -> Option<T> - where - T: Cast<Value>, - { - if value == Value::Error { - return None; - } - - match T::cast(value) { - Ok(value) => Some(value), - Err(msg) => { - self.diag(error!(span, "{}", msg)); - None - } - } - } - - /// Join with another value. - pub fn join(&mut self, lhs: Value, rhs: Value, span: Span) -> Value { - let (a, b) = (lhs.type_name(), rhs.type_name()); - match ops::join(lhs, rhs) { - Ok(joined) => joined, - Err(prev) => { - self.diag(error!(span, "cannot join {} with {}", a, b)); - prev - } - } + Ok(id) } } impl Eval for Rc<SyntaxTree> { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - struct ExprVisitor<'a, 'b> { - ctx: &'a mut EvalContext<'b>, - map: ExprMap, + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + trait Walk { + fn walk(&self, ctx: &mut EvalContext) -> TypResult<()>; } - impl<'ast> Visit<'ast> for ExprVisitor<'_, '_> { - fn visit_expr(&mut self, node: &'ast Expr) { - self.map.insert(node as *const _, node.eval(self.ctx)); + impl Walk for SyntaxTree { + fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> { + for node in self.iter() { + node.walk(ctx)?; + } + Ok(()) } } - let mut visitor = ExprVisitor { ctx, map: ExprMap::new() }; - visitor.visit_tree(self); + 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)?; + mem::replace(&mut ctx.map, prev) + }; - TemplateTree { tree: Rc::clone(self), map: visitor.map }.into() + Ok(TemplateTree { tree: Rc::clone(self), map }.into()) } } impl Eval for Expr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - match *self { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + Ok(match *self { Self::None(_) => Value::None, Self::Auto(_) => Value::Auto, Self::Bool(_, v) => Value::Bool(v), @@ -235,35 +218,32 @@ impl Eval for Expr { Self::Str(_, ref v) => Value::Str(v.clone()), Self::Ident(ref v) => match ctx.scopes.get(&v) { Some(slot) => slot.borrow().clone(), - None => { - ctx.diag(error!(v.span, "unknown variable")); - Value::Error - } + None => bail!(ctx.file, v.span, "unknown variable"), }, - Self::Array(ref v) => Value::Array(v.eval(ctx)), - Self::Dict(ref v) => Value::Dict(v.eval(ctx)), - Self::Template(ref v) => Value::Template(v.eval(ctx)), - 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) => 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), - Self::If(ref v) => v.eval(ctx), - Self::While(ref v) => v.eval(ctx), - Self::For(ref v) => v.eval(ctx), - Self::Import(ref v) => v.eval(ctx), - Self::Include(ref v) => v.eval(ctx), - } + Self::Array(ref v) => Value::Array(v.eval(ctx)?), + Self::Dict(ref v) => Value::Dict(v.eval(ctx)?), + Self::Template(ref v) => Value::Template(v.eval(ctx)?), + 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) => 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)?, + Self::If(ref v) => v.eval(ctx)?, + Self::While(ref v) => v.eval(ctx)?, + Self::For(ref v) => v.eval(ctx)?, + Self::Import(ref v) => v.eval(ctx)?, + Self::Include(ref v) => v.eval(ctx)?, + }) } } impl Eval for ArrayExpr { type Output = Array; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { self.items.iter().map(|expr| expr.eval(ctx)).collect() } } @@ -271,10 +251,10 @@ impl Eval for ArrayExpr { impl Eval for DictExpr { type Output = Dict; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { self.items .iter() - .map(|Named { name, expr }| (name.string.clone(), expr.eval(ctx))) + .map(|Named { name, expr }| Ok((name.string.clone(), expr.eval(ctx)?))) .collect() } } @@ -282,7 +262,7 @@ impl Eval for DictExpr { impl Eval for TemplateExpr { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { self.tree.eval(ctx) } } @@ -290,7 +270,7 @@ impl Eval for TemplateExpr { impl Eval for GroupExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { self.expr.eval(ctx) } } @@ -298,58 +278,44 @@ impl Eval for GroupExpr { impl Eval for BlockExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { if self.scoping { ctx.scopes.enter(); } let mut output = Value::None; for expr in &self.exprs { - let value = expr.eval(ctx); - output = ctx.join(output, value, expr.span()); + let value = expr.eval(ctx)?; + output = ops::join(output, value) + .map_err(Error::partial(ctx.file, expr.span()))?; } if self.scoping { ctx.scopes.exit(); } - output + Ok(output) } } impl Eval for UnaryExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let value = self.expr.eval(ctx); - if value == Value::Error { - return Value::Error; - } - - let ty = value.type_name(); - let out = match self.op { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let value = self.expr.eval(ctx)?; + let result = match self.op { UnOp::Pos => ops::pos(value), UnOp::Neg => ops::neg(value), UnOp::Not => ops::not(value), }; - - if out == Value::Error { - ctx.diag(error!( - self.span, - "cannot apply '{}' to {}", - self.op.as_str(), - ty, - )); - } - - out + result.map_err(Error::partial(ctx.file, self.span)) } } impl Eval for BinaryExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { match self.op { BinOp::Add => self.apply(ctx, ops::add), BinOp::Sub => self.apply(ctx, ops::sub), @@ -363,7 +329,7 @@ impl Eval for BinaryExpr { BinOp::Leq => self.apply(ctx, ops::leq), BinOp::Gt => self.apply(ctx, ops::gt), BinOp::Geq => self.apply(ctx, ops::geq), - BinOp::Assign => self.assign(ctx, |_, b| b), + BinOp::Assign => self.assign(ctx, |_, b| Ok(b)), BinOp::AddAssign => self.assign(ctx, ops::add), BinOp::SubAssign => self.assign(ctx, ops::sub), BinOp::MulAssign => self.assign(ctx, ops::mul), @@ -375,131 +341,110 @@ impl Eval for BinaryExpr { impl BinaryExpr { /// Apply a basic binary operation. - fn apply<F>(&self, ctx: &mut EvalContext, op: F) -> Value + fn apply<F>(&self, ctx: &mut EvalContext, op: F) -> TypResult<Value> where - F: FnOnce(Value, Value) -> Value, + F: FnOnce(Value, Value) -> StrResult<Value>, { - // Short-circuit boolean operations. - let lhs = self.lhs.eval(ctx); - match (self.op, &lhs) { - (BinOp::And, Value::Bool(false)) => return lhs, - (BinOp::Or, Value::Bool(true)) => return lhs, - _ => {} - } + let lhs = self.lhs.eval(ctx)?; - let rhs = self.rhs.eval(ctx); - if lhs == Value::Error || rhs == Value::Error { - return Value::Error; - } - - // Save type names before we consume the values in case of error. - let types = (lhs.type_name(), rhs.type_name()); - let out = op(lhs, rhs); - if out == Value::Error { - self.error(ctx, types); + // Short-circuit boolean operations. + if (self.op == BinOp::And && lhs == Value::Bool(false)) + || (self.op == BinOp::Or && lhs == Value::Bool(true)) + { + return Ok(lhs); } - out + let rhs = self.rhs.eval(ctx)?; + op(lhs, rhs).map_err(Error::partial(ctx.file, self.span)) } /// Apply an assignment operation. - fn assign<F>(&self, ctx: &mut EvalContext, op: F) -> Value + fn assign<F>(&self, ctx: &mut EvalContext, op: F) -> TypResult<Value> where - F: FnOnce(Value, Value) -> Value, + 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 => { - ctx.diag(error!(self.lhs.span(), "unknown variable")); - return Value::Error; - } + None => bail!(ctx.file, lspan, "unknown variable"), } } else { - ctx.diag(error!(self.lhs.span(), "cannot assign to this expression")); - return Value::Error; + bail!(ctx.file, lspan, "cannot assign to this expression",); }; - let rhs = self.rhs.eval(ctx); + let rhs = self.rhs.eval(ctx)?; let mut mutable = match slot.try_borrow_mut() { Ok(mutable) => mutable, Err(_) => { - ctx.diag(error!(self.lhs.span(), "cannot assign to a constant")); - return Value::Error; + bail!(ctx.file, lspan, "cannot assign to a constant",); } }; let lhs = mem::take(&mut *mutable); - let types = (lhs.type_name(), rhs.type_name()); - *mutable = op(lhs, rhs); + *mutable = op(lhs, rhs).map_err(Error::partial(ctx.file, self.span))?; - if *mutable == Value::Error { - self.error(ctx, types); - return Value::Error; - } - - Value::None - } - - fn error(&self, ctx: &mut EvalContext, (a, b): (&str, &str)) { - ctx.diag(error!(self.span, "{}", match self.op { - BinOp::Add => format!("cannot add {} and {}", a, b), - BinOp::Sub => format!("cannot subtract {1} from {0}", a, b), - BinOp::Mul => format!("cannot multiply {} with {}", a, b), - BinOp::Div => format!("cannot divide {} by {}", a, b), - _ => format!("cannot apply '{}' to {} and {}", self.op.as_str(), a, b), - })); + Ok(Value::None) } } impl Eval for CallExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let callee = self.callee.eval(ctx); - if let Some(func) = ctx.cast::<Function>(callee, self.callee.span()) { - let mut args = self.args.eval(ctx); - let returned = func(ctx, &mut args); - args.finish(ctx); - returned - } else { - Value::Error - } + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let callee = self + .callee + .eval(ctx)? + .cast::<Function>() + .map_err(Error::partial(ctx.file, self.callee.span()))?; + + let mut args = self.args.eval(ctx)?; + let returned = callee(ctx, &mut args)?; + args.finish()?; + + Ok(returned) } } impl Eval for CallArgs { type Output = FuncArgs; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let items = self.items.iter().map(|arg| arg.eval(ctx)).collect(); - FuncArgs { span: self.span, items } + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + Ok(FuncArgs { + file: ctx.file, + span: self.span, + items: self + .items + .iter() + .map(|arg| arg.eval(ctx)) + .collect::<TypResult<Vec<_>>>()?, + }) } } impl Eval for CallArg { type Output = FuncArg; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - match self { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + Ok(match self { Self::Pos(expr) => FuncArg { span: self.span(), name: None, - value: Spanned::new(expr.eval(ctx), expr.span()), + value: Spanned::new(expr.eval(ctx)?, expr.span()), }, Self::Named(Named { name, expr }) => FuncArg { span: self.span(), name: Some(name.string.clone()), - value: Spanned::new(expr.eval(ctx), expr.span()), + value: Spanned::new(expr.eval(ctx)?, expr.span()), }, - } + }) } } impl Eval for ClosureExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { let params = Rc::clone(&self.params); let body = Rc::clone(&self.body); @@ -511,86 +456,92 @@ impl Eval for ClosureExpr { }; let name = self.name.as_ref().map(|name| name.string.clone()); - Value::Func(Function::new(name, move |ctx, args| { + let func = Function::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); ctx.scopes.top = captured.clone(); for param in params.iter() { - // Set the parameter to `none` if the argument is missing. - let value = args.expect::<Value>(ctx, param.as_str()).unwrap_or_default(); + let value = args.expect::<Value>(param.as_str())?; ctx.scopes.def_mut(param.as_str(), value); } - let value = body.eval(ctx); + let result = body.eval(ctx); ctx.scopes = prev; - value - })) + result + }); + + Ok(Value::Func(func)) } } 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::<Function>(callee, self.callee.span()) { - let applied = self.args.eval(ctx); - let name = func.name().cloned(); - Value::Func(Function::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 - } + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let callee = self + .callee + .eval(ctx)? + .cast::<Function>() + .map_err(Error::partial(ctx.file, self.callee.span()))?; + + let applied = self.args.eval(ctx)?; + + let name = callee.name().cloned(); + let func = Function::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. + callee(ctx, args) + }); + + Ok(Value::Func(func)) } } impl Eval for LetExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { let value = match &self.init { - Some(expr) => expr.eval(ctx), + Some(expr) => expr.eval(ctx)?, None => Value::None, }; ctx.scopes.def_mut(self.binding.as_str(), value); - Value::None + Ok(Value::None) } } impl Eval for IfExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let condition = self.condition.eval(ctx); - if let Some(condition) = ctx.cast(condition, self.condition.span()) { - if condition { - self.if_body.eval(ctx) - } else if let Some(else_body) = &self.else_body { - else_body.eval(ctx) - } else { - Value::None - } + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let condition = self + .condition + .eval(ctx)? + .cast::<bool>() + .map_err(Error::partial(ctx.file, self.condition.span()))?; + + if condition { + self.if_body.eval(ctx) + } else if let Some(else_body) = &self.else_body { + else_body.eval(ctx) } else { - Value::Error + Ok(Value::None) } } } @@ -598,28 +549,28 @@ impl Eval for IfExpr { impl Eval for WhileExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { let mut output = Value::None; - loop { - let condition = self.condition.eval(ctx); - if let Some(condition) = ctx.cast(condition, self.condition.span()) { - if condition { - let value = self.body.eval(ctx); - output = ctx.join(output, value, self.body.span()); - } else { - return output; - } - } else { - return Value::Error; - } + + while self + .condition + .eval(ctx)? + .cast::<bool>() + .map_err(Error::partial(ctx.file, self.condition.span()))? + { + let value = self.body.eval(ctx)?; + output = ops::join(output, value) + .map_err(Error::partial(ctx.file, self.body.span()))?; } + + Ok(output) } } impl Eval for ForExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { macro_rules! iter { (for ($($binding:ident => $value:ident),*) in $iter:expr) => {{ let mut output = Value::None; @@ -629,17 +580,18 @@ impl Eval for ForExpr { for ($($value),*) in $iter { $(ctx.scopes.def_mut($binding.as_str(), $value);)* - let value = self.body.eval(ctx); - output = ctx.join(output, value, self.body.span()); + let value = self.body.eval(ctx)?; + output = ops::join(output, value) + .map_err(Error::partial(ctx.file, self.body.span()))?; } ctx.scopes.exit(); - output + Ok(output) }}; } - let iter = self.iter.eval(ctx); - match (self.pattern.clone(), iter) { + let iter = self.iter.eval(ctx)?; + match (&self.pattern, iter) { (ForPattern::Value(v), Value::Str(string)) => { iter!(for (v => value) in string.chars().map(|c| Value::Str(c.into()))) } @@ -655,22 +607,15 @@ impl Eval for ForExpr { (ForPattern::KeyValue(k, v), Value::Dict(dict)) => { iter!(for (k => key, v => value) in dict.into_iter()) } - (ForPattern::KeyValue(_, _), Value::Str(_)) => { - ctx.diag(error!(self.pattern.span(), "mismatched pattern")); - Value::Error - } - - (_, iter) => { - if iter != Value::Error { - ctx.diag(error!( - self.iter.span(), - "cannot loop over {}", - iter.type_name(), - )); - } - Value::Error + bail!(ctx.file, self.pattern.span(), "mismatched pattern"); } + (_, iter) => bail!( + ctx.file, + self.iter.span(), + "cannot loop over {}", + iter.type_name(), + ), } } } @@ -678,50 +623,50 @@ impl Eval for ForExpr { impl Eval for ImportExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let path = self.path.eval(ctx); - if let Some(path) = ctx.cast::<EcoString>(path, self.path.span()) { - if let Some(hash) = ctx.import(&path, self.path.span()) { - let mut module = &ctx.modules[&hash]; - match &self.imports { - Imports::Wildcard => { - for (var, slot) in module.scope.iter() { - let value = slot.borrow().clone(); - ctx.scopes.def_mut(var, value); - } - } - Imports::Idents(idents) => { - for ident in idents { - if let Some(slot) = module.scope.get(&ident) { - let value = slot.borrow().clone(); - ctx.scopes.def_mut(ident.as_str(), value); - } else { - ctx.diag(error!(ident.span, "unresolved import")); - module = &ctx.modules[&hash]; - } - } + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let path = self + .path + .eval(ctx)? + .cast::<EcoString>() + .map_err(Error::partial(ctx.file, self.path.span()))?; + + let id = ctx.import(&path, self.path.span())?; + let module = &ctx.modules[&id]; + + match &self.imports { + Imports::Wildcard => { + for (var, slot) in module.scope.iter() { + ctx.scopes.def_mut(var, slot.borrow().clone()); + } + } + Imports::Idents(idents) => { + for ident in idents { + if let Some(slot) = module.scope.get(&ident) { + ctx.scopes.def_mut(ident.as_str(), slot.borrow().clone()); + } else { + bail!(ctx.file, ident.span, "unresolved import"); } } - - return Value::None; } } - Value::Error + Ok(Value::None) } } impl Eval for IncludeExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let path = self.path.eval(ctx); - if let Some(path) = ctx.cast::<EcoString>(path, self.path.span()) { - if let Some(hash) = ctx.import(&path, self.path.span()) { - return Value::Template(ctx.modules[&hash].template.clone()); - } - } + fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let path = self + .path + .eval(ctx)? + .cast::<EcoString>() + .map_err(Error::partial(ctx.file, self.path.span()))?; + + let id = ctx.import(&path, self.path.span())?; + let module = &ctx.modules[&id]; - Value::Error + Ok(Value::Template(module.template.clone())) } } |
