From 144f20882136ef81b79d77bd8a68f42b76c66676 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 13 Aug 2021 12:21:14 +0200 Subject: Add file information to spans --- src/eval/function.rs | 21 ++---- src/eval/mod.rs | 209 ++++++++++++++++++--------------------------------- 2 files changed, 83 insertions(+), 147 deletions(-) (limited to 'src/eval') diff --git a/src/eval/function.rs b/src/eval/function.rs index be7894f4..d2ec1bb9 100644 --- a/src/eval/function.rs +++ b/src/eval/function.rs @@ -3,8 +3,7 @@ use std::ops::Deref; use std::rc::Rc; use super::{Cast, EvalContext, Value}; -use crate::diag::{Error, TypResult}; -use crate::source::SourceId; +use crate::diag::{At, TypResult}; use crate::syntax::{Span, Spanned}; use crate::util::EcoString; @@ -59,8 +58,6 @@ impl PartialEq for Function { /// Evaluated arguments to a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncArgs { - /// The id of the source file in which the function was called. - pub source: SourceId, /// The span of the whole argument list. pub span: Span, /// The positional arguments. @@ -103,7 +100,7 @@ impl FuncArgs { { match self.eat() { Some(found) => Ok(found), - None => bail!(self.source, self.span, "missing argument: {}", what), + None => bail!(self.span, "missing argument: {}", what), } } @@ -134,14 +131,14 @@ impl FuncArgs { let value = self.items.remove(index).value; let span = value.span; - T::cast(value).map(Some).map_err(Error::at(self.source, span)) + T::cast(value).map(Some).at(span) } /// Return an "unexpected argument" error if there is any remaining /// argument. pub fn finish(self) -> TypResult<()> { if let Some(arg) = self.items.first() { - bail!(self.source, arg.span, "unexpected argument"); + bail!(arg.span, "unexpected argument"); } Ok(()) } @@ -165,19 +162,17 @@ impl FuncArgs { { let mut iter = self.items.into_iter(); let value = match iter.next() { + Some(FuncArg { name: None, value, .. }) => value.v.cast().at(value.span)?, None => { - bail!(self.source, self.span, "missing {}", what); + bail!(self.span, "missing {}", what); } Some(FuncArg { name: Some(_), span, .. }) => { - bail!(self.source, span, "named pair is not allowed here"); - } - Some(FuncArg { name: None, value, .. }) => { - value.v.cast().map_err(Error::at(self.source, value.span))? + bail!(span, "named pair is not allowed here"); } }; if let Some(arg) = iter.next() { - bail!(self.source, arg.span, "only one {} is allowed", what); + bail!(arg.span, "only one {} is allowed", what); } Ok(value) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 1b851a9b..0932dc71 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -27,7 +27,7 @@ use std::mem; use std::path::PathBuf; use std::rc::Rc; -use crate::diag::{Error, StrResult, Tracepoint, TypResult}; +use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::image::ImageStore; use crate::loading::Loader; @@ -71,8 +71,6 @@ pub struct EvalContext<'a> { pub images: &'a mut ImageStore, /// Caches evaluated modules. pub modules: &'a mut ModuleCache, - /// The id of the currently evaluated source file. - pub source: SourceId, /// The stack of imported files that led to evaluation of the current file. pub route: Vec, /// The active scopes. @@ -89,8 +87,7 @@ impl<'a> EvalContext<'a> { sources: &mut ctx.sources, images: &mut ctx.images, modules: &mut ctx.modules, - source, - route: vec![], + route: vec![source], scopes: Scopes::new(Some(&ctx.std)), map: ExprMap::new(), } @@ -101,15 +98,15 @@ impl<'a> EvalContext<'a> { // Load the source file. let full = self.make_path(path); let id = self.sources.load(&full).map_err(|err| { - Error::boxed(self.source, span, match err.kind() { + Error::boxed(span, match err.kind() { io::ErrorKind::NotFound => "file not found".into(), _ => format!("failed to load source file ({})", err), }) })?; // Prevent cyclic importing. - if self.source == id || self.route.contains(&id) { - bail!(self.source, span, "cyclic import"); + if self.route.contains(&id) { + bail!(span, "cyclic import"); } // Check whether the module was already loaded. @@ -124,23 +121,14 @@ impl<'a> EvalContext<'a> { // Prepare the new context. let new_scopes = Scopes::new(self.scopes.base); let old_scopes = mem::replace(&mut self.scopes, new_scopes); - self.route.push(self.source); - self.source = id; + self.route.push(id); // Evaluate the module. - let result = Rc::new(ast).eval(self); + let template = Rc::new(ast).eval(self).trace(|| Tracepoint::Import, span)?; // Restore the old context. let new_scopes = mem::replace(&mut self.scopes, old_scopes); - self.source = self.route.pop().unwrap(); - - // Add a tracepoint to the errors. - let template = result.map_err(|mut errors| { - for error in errors.iter_mut() { - error.trace.push((self.source, span, Tracepoint::Import)); - } - errors - })?; + self.route.pop().unwrap(); // Save the evaluated module. let module = Module { scope: new_scopes.top, template }; @@ -152,11 +140,13 @@ impl<'a> EvalContext<'a> { /// Complete a user-entered path (relative to the source file) to be /// relative to the compilation environment's root. pub fn make_path(&self, path: &str) -> PathBuf { - if let Some(dir) = self.sources.get(self.source).path().parent() { - dir.join(path) - } else { - path.into() + if let Some(&id) = self.route.last() { + if let Some(dir) = self.sources.get(id).path().parent() { + return dir.join(path); + } } + + path.into() } } @@ -198,10 +188,7 @@ impl Eval for Expr { Self::Percent(_, v) => Value::Relative(Relative::new(v / 100.0)), Self::Fractional(_, v) => Value::Fractional(Fractional::new(v)), Self::Str(_, ref v) => Value::Str(v.clone()), - Self::Ident(ref v) => match ctx.scopes.get(&v) { - Some(slot) => slot.borrow().clone(), - None => bail!(ctx.source, v.span, "unknown variable"), - }, + Self::Ident(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)?), @@ -222,6 +209,17 @@ impl Eval for Expr { } } +impl Eval for Ident { + type Output = Value; + + fn eval(&self, ctx: &mut EvalContext) -> TypResult { + match ctx.scopes.get(self) { + Some(slot) => Ok(slot.borrow().clone()), + None => bail!(self.span, "unknown variable"), + } + } +} + impl Eval for ArrayExpr { type Output = Array; @@ -268,8 +266,7 @@ 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::at(ctx.source, expr.span()))?; + output = ops::join(output, value).at(expr.span())?; } if self.scoping { @@ -290,7 +287,7 @@ impl Eval for UnaryExpr { UnOp::Neg => ops::neg(value), UnOp::Not => ops::not(value), }; - result.map_err(Error::at(ctx.source, self.span)) + result.at(self.span) } } @@ -337,7 +334,7 @@ impl BinaryExpr { } let rhs = self.rhs.eval(ctx)?; - op(lhs, rhs).map_err(Error::at(ctx.source, self.span)) + op(lhs, rhs).at(self.span) } /// Apply an assignment operation. @@ -345,11 +342,10 @@ impl BinaryExpr { where F: FnOnce(Value, Value) -> StrResult, { - let source = ctx.source; let rhs = self.rhs.eval(ctx)?; let mut target = self.lhs.access(ctx)?; let lhs = mem::take(&mut *target); - *target = op(lhs, rhs).map_err(Error::at(source, self.span))?; + *target = op(lhs, rhs).at(self.span)?; Ok(Value::None) } } @@ -362,36 +358,22 @@ impl Eval for CallExpr { let mut args = self.args.eval(ctx)?; match callee { - Value::Array(array) => array - .get(args.into_index()?) - .map(Value::clone) - .map_err(Error::at(ctx.source, self.span)), + Value::Array(array) => { + array.get(args.into_index()?).map(Value::clone).at(self.span) + } - Value::Dict(dict) => dict - .get(&args.into_key()?) - .map(Value::clone) - .map_err(Error::at(ctx.source, self.span)), + Value::Dict(dict) => { + dict.get(&args.into_key()?).map(Value::clone).at(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 - })?; + let point = || Tracepoint::Call(func.name().map(Into::into)); + let value = func(ctx, &mut args).trace(point, self.span)?; args.finish()?; - Ok(returned) + Ok(value) } v => bail!( - ctx.source, self.callee.span(), "expected function or collection, found {}", v.type_name(), @@ -405,7 +387,6 @@ impl Eval for CallArgs { fn eval(&self, ctx: &mut EvalContext) -> TypResult { Ok(FuncArgs { - source: ctx.source, span: self.span, items: self .items @@ -444,9 +425,6 @@ impl Eval for ClosureExpr { default: Option, } - let file = ctx.source; - let name = self.name.as_ref().map(|name| name.string.clone()); - // Evaluate default values for named parameters. let params: Vec<_> = self .params @@ -472,36 +450,31 @@ impl Eval for ClosureExpr { // Clone the body expression so that we don't have a lifetime // dependence on the AST. let body = Rc::clone(&self.body); + let name = self.name.as_ref().map(|name| name.string.clone()); // Define the actual function. let func = Function::new(name, move |ctx, args| { - let prev_file = mem::replace(&mut ctx.source, file); - // Don't leak the scopes from the call site. Instead, we use the // scope of captured variables we collected earlier. let prev_scopes = mem::take(&mut ctx.scopes); ctx.scopes.top = captured.clone(); - let mut try_eval = || { - // Parse the arguments according to the parameter list. - for param in ¶ms { - let value = match ¶m.default { - None => args.expect::(¶m.name)?, - Some(default) => args - .named::(¶m.name)? - .unwrap_or_else(|| default.clone()), - }; - - ctx.scopes.def_mut(¶m.name, value); - } + // Parse the arguments according to the parameter list. + for param in ¶ms { + let value = match ¶m.default { + None => args.expect::(¶m.name)?, + Some(default) => args + .named::(¶m.name)? + .unwrap_or_else(|| default.clone()), + }; + + ctx.scopes.def_mut(¶m.name, value); + } - body.eval(ctx) - }; + let value = body.eval(ctx)?; - let result = try_eval(); ctx.scopes = prev_scopes; - ctx.source = prev_file; - result + Ok(value) }); Ok(Value::Func(func)) @@ -512,11 +485,7 @@ impl Eval for WithExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - let callee = self - .callee - .eval(ctx)? - .cast::() - .map_err(Error::at(ctx.source, self.callee.span()))?; + let callee = self.callee.eval(ctx)?.cast::().at(self.callee.span())?; let name = callee.name().cloned(); let applied = self.args.eval(ctx)?; @@ -562,11 +531,8 @@ impl Eval for IfExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - let condition = self - .condition - .eval(ctx)? - .cast::() - .map_err(Error::at(ctx.source, self.condition.span()))?; + let condition = + self.condition.eval(ctx)?.cast::().at(self.condition.span())?; if condition { self.if_body.eval(ctx) @@ -584,15 +550,9 @@ impl Eval for WhileExpr { fn eval(&self, ctx: &mut EvalContext) -> TypResult { let mut output = Value::None; - while self - .condition - .eval(ctx)? - .cast::() - .map_err(Error::at(ctx.source, self.condition.span()))? - { + while self.condition.eval(ctx)?.cast::().at(self.condition.span())? { let value = self.body.eval(ctx)?; - output = ops::join(output, value) - .map_err(Error::at(ctx.source, self.body.span()))?; + output = ops::join(output, value).at(self.body.span())?; } Ok(output) @@ -614,7 +574,7 @@ impl Eval for ForExpr { let value = self.body.eval(ctx)?; output = ops::join(output, value) - .map_err(Error::at(ctx.source, self.body.span()))?; + .at(self.body.span())?; } ctx.scopes.exit(); @@ -640,14 +600,11 @@ impl Eval for ForExpr { iter!(for (k => key, v => value) in dict.into_iter()) } (ForPattern::KeyValue(_, _), Value::Str(_)) => { - bail!(ctx.source, self.pattern.span(), "mismatched pattern"); + bail!(self.pattern.span(), "mismatched pattern"); + } + (_, iter) => { + bail!(self.iter.span(), "cannot loop over {}", iter.type_name()); } - (_, iter) => bail!( - ctx.source, - self.iter.span(), - "cannot loop over {}", - iter.type_name(), - ), } } } @@ -656,11 +613,7 @@ impl Eval for ImportExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - let path = self - .path - .eval(ctx)? - .cast::() - .map_err(Error::at(ctx.source, self.path.span()))?; + let path = self.path.eval(ctx)?.cast::().at(self.path.span())?; let file = ctx.import(&path, self.path.span())?; let module = &ctx.modules[&file]; @@ -676,7 +629,7 @@ impl Eval for ImportExpr { if let Some(slot) = module.scope.get(&ident) { ctx.scopes.def_mut(ident.as_str(), slot.borrow().clone()); } else { - bail!(ctx.source, ident.span, "unresolved import"); + bail!(ident.span, "unresolved import"); } } } @@ -690,11 +643,7 @@ impl Eval for IncludeExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - let path = self - .path - .eval(ctx)? - .cast::() - .map_err(Error::at(ctx.source, self.path.span()))?; + let path = self.path.eval(ctx)?.cast::().at(self.path.span())?; let file = ctx.import(&path, self.path.span())?; let module = &ctx.modules[&file]; @@ -753,42 +702,34 @@ impl Access for Expr { match self { Expr::Ident(ident) => ident.access(ctx), Expr::Call(call) => call.access(ctx), - _ => bail!( - ctx.source, - self.span(), - "cannot access this expression mutably", - ), + _ => bail!(self.span(), "cannot access this expression mutably"), } } } impl Access for Ident { fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult> { - 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)) + match ctx.scopes.get(self) { + Some(slot) => match slot.try_borrow_mut() { + Ok(guard) => Ok(guard), + Err(_) => bail!(self.span, "cannot mutate a constant"), + }, + None => bail!(self.span, "unknown variable"), + } } } impl Access for CallExpr { fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult> { - 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::Array(array) => array.get_mut(args.into_index()?).at(self.span), Value::Dict(dict) => Ok(dict.get_mut(args.into_key()?)), v => bail!( - source, self.callee.span(), "expected collection, found {}", v.type_name(), -- cgit v1.2.3