diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-31 22:59:14 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-08-01 00:00:36 +0200 |
| commit | 3c92bad9a7cd6b880de197806443ffcce2cac9d8 (patch) | |
| tree | 1faf79c66e23bc37711af16ad690a9878e28d348 /src/eval/mod.rs | |
| parent | fbd3d191137aac8188ab8c6503d257d65d873972 (diff) | |
Pretty-printed diagnostics with traceback
Diffstat (limited to 'src/eval/mod.rs')
| -rw-r--r-- | src/eval/mod.rs | 69 |
1 files changed, 50 insertions, 19 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index decd4281..8f5532eb 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -25,11 +25,12 @@ use std::mem; use std::path::Path; use std::rc::Rc; -use crate::diag::{Error, StrResult, TypResult}; +use crate::diag::{Error, StrResult, Tracepoint, TypResult}; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::image::ImageCache; use crate::loading::{FileId, Loader}; use crate::parse::parse; +use crate::source::{SourceFile, SourceMap}; use crate::syntax::visit::Visit; use crate::syntax::*; use crate::util::EcoString; @@ -67,6 +68,8 @@ pub trait Eval { pub struct EvalContext<'a> { /// The loader from which resources (files and images) are loaded. pub loader: &'a dyn Loader, + /// The store for source files. + pub sources: &'a mut SourceMap, /// The cache for decoded images. pub images: &'a mut ImageCache, /// The cache for loaded modules. @@ -86,6 +89,7 @@ impl<'a> EvalContext<'a> { pub fn new(ctx: &'a mut Context, file: FileId) -> Self { Self { loader: ctx.loader.as_ref(), + sources: &mut ctx.sources, images: &mut ctx.images, modules: &mut ctx.modules, scopes: Scopes::new(Some(&ctx.std)), @@ -106,49 +110,58 @@ impl<'a> EvalContext<'a> { /// Process an import of a module relative to the current location. pub fn import(&mut self, path: &str, span: Span) -> TypResult<FileId> { - let id = self.resolve(path, span)?; + let file = self.resolve(path, span)?; // Prevent cyclic importing. - if self.file == id || self.route.contains(&id) { + if self.file == file || self.route.contains(&file) { bail!(self.file, span, "cyclic import"); } // Check whether the module was already loaded. - if self.modules.get(&id).is_some() { - return Ok(id); + if self.modules.get(&file).is_some() { + return Ok(file); } // Load the source file. let buffer = self .loader - .load_file(id) + .load_file(file) .map_err(|_| Error::boxed(self.file, span, "failed to load file"))?; // Decode UTF-8. - let string = std::str::from_utf8(&buffer) + let string = String::from_utf8(buffer) .map_err(|_| Error::boxed(self.file, span, "file is not valid utf-8"))?; // Parse the file. - let ast = parse(id, string)?; + let source = self.sources.insert(SourceFile::new(file, string)); + let ast = parse(&source)?; // 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.file); - self.file = id; + self.file = file; // Evaluate the module. - let template = Rc::new(ast).eval(self)?; + let result = Rc::new(ast).eval(self); // Restore the old context. let new_scopes = mem::replace(&mut self.scopes, old_scopes); self.file = 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.file, span, Tracepoint::Import)); + } + errors + })?; + // Save the evaluated module. let module = Module { scope: new_scopes.top, template }; - self.modules.insert(id, module); + self.modules.insert(file, module); - Ok(id) + Ok(file) } } @@ -399,7 +412,22 @@ impl Eval for CallExpr { .map_err(Error::partial(ctx.file, self.callee.span()))?; let mut args = self.args.eval(ctx)?; - let returned = callee(ctx, &mut args)?; + let returned = callee(ctx, &mut args).map_err(|mut errors| { + for error in errors.iter_mut() { + // Skip errors directly related to arguments. + if error.file == ctx.file && self.span.contains(error.span) { + continue; + } + + error.trace.push(( + ctx.file, + self.span, + Tracepoint::Call(callee.name().map(Into::into)), + )); + } + errors + })?; + args.finish()?; Ok(returned) @@ -445,6 +473,7 @@ impl Eval for ClosureExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + let file = ctx.file; let params = Rc::clone(&self.params); let body = Rc::clone(&self.body); @@ -459,7 +488,8 @@ impl Eval for ClosureExpr { 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); + let prev_scopes = mem::take(&mut ctx.scopes); + let prev_file = mem::replace(&mut ctx.file, file); ctx.scopes.top = captured.clone(); for param in params.iter() { @@ -468,7 +498,8 @@ impl Eval for ClosureExpr { } let result = body.eval(ctx); - ctx.scopes = prev; + ctx.scopes = prev_scopes; + ctx.file = prev_file; result }); @@ -630,8 +661,8 @@ impl Eval for ImportExpr { .cast::<EcoString>() .map_err(Error::partial(ctx.file, self.path.span()))?; - let id = ctx.import(&path, self.path.span())?; - let module = &ctx.modules[&id]; + let file = ctx.import(&path, self.path.span())?; + let module = &ctx.modules[&file]; match &self.imports { Imports::Wildcard => { @@ -664,8 +695,8 @@ impl Eval for IncludeExpr { .cast::<EcoString>() .map_err(Error::partial(ctx.file, self.path.span()))?; - let id = ctx.import(&path, self.path.span())?; - let module = &ctx.modules[&id]; + let file = ctx.import(&path, self.path.span())?; + let module = &ctx.modules[&file]; Ok(Value::Template(module.template.clone())) } |
