From e1f29d6cb9437a4afb2e4fc4ee10a5b8717ab9fa Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 22 Feb 2022 14:31:09 +0100 Subject: Rework the core context --- src/lib.rs | 199 +++++++++++++++++++++++++------------------------------------ 1 file changed, 82 insertions(+), 117 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index cfbdcae7..458c8cec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,8 +13,7 @@ //! markup. //! - **Layouting:** Next, the tree is [layouted] into a portable version of the //! typeset document. The output of this is a collection of [`Frame`]s (one -//! per page), ready for exporting. This step is supported by an incremental -//! [cache] that enables reuse of intermediate layouting results. +//! per page), ready for exporting. //! - **Exporting:** The finished layout can be exported into a supported //! format. Currently, the only supported output format is [PDF]. //! @@ -22,11 +21,10 @@ //! [parsed]: parse::parse //! [green tree]: syntax::GreenNode //! [AST]: syntax::ast -//! [evaluate]: Vm::evaluate +//! [evaluate]: eval::Eval //! [module]: eval::Module //! [template]: eval::Template -//! [layouted]: eval::Template::layout_pages -//! [cache]: layout::LayoutCache +//! [layouted]: eval::Template::layout //! [PDF]: export::pdf #![allow(clippy::len_without_is_empty)] @@ -50,7 +48,6 @@ pub mod parse; pub mod source; pub mod syntax; -use std::any::TypeId; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; @@ -74,9 +71,15 @@ pub struct Context { /// Stores decoded images. pub images: ImageStore, /// The standard library scope. - std: Scope, + std: Arc, /// The default styles. - styles: StyleMap, + styles: Arc, + /// Cached modules. + modules: HashMap, + /// The stack of imported files that led to evaluation of the current file. + route: Vec, + /// The dependencies of the current evaluation process. + deps: Vec<(SourceId, usize)>, } impl Context { @@ -100,13 +103,73 @@ impl Context { &self.styles } + /// Evaluate a source file and return the resulting module. + /// + /// Returns either a module containing a scope with top-level bindings and a + /// layoutable template or diagnostics in the form of a vector of error + /// messages with file and span information. + pub fn evaluate(&mut self, id: SourceId) -> TypResult { + // Prevent cyclic evaluation. + if self.route.contains(&id) { + let path = self.sources.get(id).path().display(); + panic!("Tried to cyclicly evaluate {}", path); + } + + // Check whether the module was already evaluated. + if let Some(module) = self.modules.get(&id) { + if module.valid(&self.sources) { + return Ok(module.clone()); + } else { + self.modules.remove(&id); + } + } + + // Parse the file. + let source = self.sources.get(id); + let ast = source.ast()?; + + let std = self.std.clone(); + let mut scp = Scopes::new(Some(&std)); + + // Evaluate the module. + let prev = std::mem::replace(&mut self.deps, vec![(id, source.rev())]); + self.route.push(id); + let template = ast.eval(self, &mut scp); + self.route.pop().unwrap(); + let deps = std::mem::replace(&mut self.deps, prev); + + // Assemble the module. + let module = Module { + scope: scp.top, + template: template?, + deps, + }; + + // Save the evaluated module. + self.modules.insert(id, module.clone()); + + Ok(module) + } + /// Typeset a source file into a collection of layouted frames. /// /// Returns either a vector of frames representing individual pages or /// diagnostics in the form of a vector of error message with file and span /// information. pub fn typeset(&mut self, id: SourceId) -> TypResult>> { - Vm::new(self).typeset(id) + self.evaluate(id)?.template.layout(self) + } + + /// Resolve a user-entered path (relative to the current evaluation + /// location) to be relative to the compilation environment's root. + pub fn resolve(&self, path: &str) -> PathBuf { + if let Some(&id) = self.route.last() { + if let Some(dir) = self.sources.get(id).path().parent() { + return dir.join(path); + } + } + + path.into() } } @@ -114,21 +177,21 @@ impl Context { /// /// This struct is created by [`Context::builder`]. pub struct ContextBuilder { - std: Option, - styles: Option, + std: Option>, + styles: Option>, } impl ContextBuilder { /// The scope containing definitions that are available everywhere /// (the standard library). - pub fn std(mut self, std: Scope) -> Self { - self.std = Some(std); + pub fn std(mut self, std: impl Into>) -> Self { + self.std = Some(std.into()); self } /// The default properties for page size, font selection and so on. - pub fn styles(mut self, styles: StyleMap) -> Self { - self.styles = Some(styles); + pub fn styles(mut self, styles: impl Into>) -> Self { + self.styles = Some(styles.into()); self } @@ -140,8 +203,11 @@ impl ContextBuilder { fonts: FontStore::new(Arc::clone(&loader)), images: ImageStore::new(Arc::clone(&loader)), loader, - std: self.std.unwrap_or_else(library::new), + std: self.std.unwrap_or_else(|| Arc::new(library::new())), styles: self.styles.unwrap_or_default(), + modules: HashMap::new(), + route: vec![], + deps: vec![], } } } @@ -151,104 +217,3 @@ impl Default for ContextBuilder { Self { std: None, styles: None } } } - -/// A virtual machine for a single typesetting process. -pub struct Vm<'a> { - /// The loader the context was created with. - pub loader: &'a dyn Loader, - /// Stores loaded source files. - pub sources: &'a mut SourceStore, - /// Stores parsed font faces. - pub fonts: &'a mut FontStore, - /// Stores decoded images. - pub images: &'a mut ImageStore, - /// The default styles. - pub styles: &'a StyleMap, - /// The stack of imported files that led to evaluation of the current file. - pub route: Vec, - /// Caches imported modules. - pub modules: HashMap, - /// The active scopes. - pub scopes: Scopes<'a>, - /// Currently evaluated show rules. This is used to prevent recursive show - /// rules. - pub rules: Vec, - /// How deeply nested the current layout tree position is. - pub level: usize, -} - -impl<'a> Vm<'a> { - /// Create a new virtual machine. - pub fn new(ctx: &'a mut Context) -> Self { - Self { - loader: ctx.loader.as_ref(), - sources: &mut ctx.sources, - fonts: &mut ctx.fonts, - images: &mut ctx.images, - styles: &ctx.styles, - route: vec![], - modules: HashMap::new(), - scopes: Scopes::new(Some(&ctx.std)), - rules: vec![], - level: 0, - } - } - - /// Evaluate a source file and return the resulting module. - /// - /// Returns either a module containing a scope with top-level bindings and a - /// layoutable template or diagnostics in the form of a vector of error - /// message with file and span information. - pub fn evaluate(&mut self, id: SourceId) -> TypResult { - // Prevent cyclic evaluation. - assert!(!self.route.contains(&id)); - - // Check whether the module was already loaded. - if let Some(module) = self.modules.get(&id) { - return Ok(module.clone()); - } - - // Parse the file. - let source = self.sources.get(id); - let ast = source.ast()?; - - // Prepare the new context. - let fresh = Scopes::new(self.scopes.base); - let prev = std::mem::replace(&mut self.scopes, fresh); - self.route.push(id); - - // Evaluate the module. - let template = ast.eval(self)?; - - // Restore the old context. - let scope = std::mem::replace(&mut self.scopes, prev).top; - self.route.pop().unwrap(); - - // Save the evaluated module. - let module = Module { scope, template }; - self.modules.insert(id, module.clone()); - - Ok(module) - } - - /// Typeset a source file into a collection of layouted frames. - /// - /// Returns either a vector of frames representing individual pages or - /// diagnostics in the form of a vector of error message with file and span - /// information. - pub fn typeset(&mut self, id: SourceId) -> TypResult>> { - self.evaluate(id)?.template.layout_pages(self) - } - - /// Resolve a user-entered path (relative to the source file) to be - /// relative to the compilation environment's root. - pub fn resolve(&self, path: &str) -> PathBuf { - if let Some(&id) = self.route.last() { - if let Some(dir) = self.sources.get(id).path().parent() { - return dir.join(path); - } - } - - path.into() - } -} -- cgit v1.2.3