diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-06-26 13:57:21 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-06-27 18:40:17 +0200 |
| commit | 7b92bd7c340d9f9c094ed2fa57912049317d9b20 (patch) | |
| tree | b91399526ba94d87309d09d864df2935dd7a4d0a /src/eval | |
| parent | 9c7f31870b4e1bf37df79ebbe1df9a56df83d878 (diff) | |
Basic package management
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/func.rs | 8 | ||||
| -rw-r--r-- | src/eval/library.rs | 7 | ||||
| -rw-r--r-- | src/eval/mod.rs | 105 | ||||
| -rw-r--r-- | src/eval/module.rs | 38 | ||||
| -rw-r--r-- | src/eval/value.rs | 9 |
5 files changed, 97 insertions, 70 deletions
diff --git a/src/eval/func.rs b/src/eval/func.rs index 28d4a40d..22f948ce 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -11,9 +11,10 @@ use super::{ Value, Vm, }; use crate::diag::{bail, SourceResult, StrResult}; +use crate::file::FileId; use crate::model::{DelayedErrors, ElemFunc, Introspector, Locator, Vt}; use crate::syntax::ast::{self, AstNode, Expr, Ident}; -use crate::syntax::{SourceId, Span, SyntaxNode}; +use crate::syntax::{Span, SyntaxNode}; use crate::World; /// An evaluatable function. @@ -125,7 +126,6 @@ impl Func { args: impl IntoIterator<Item = T>, ) -> SourceResult<Value> { let route = Route::default(); - let id = SourceId::detached(); let scopes = Scopes::new(None); let mut locator = Locator::chained(vt.locator.track()); let vt = Vt { @@ -135,7 +135,7 @@ impl Func { delayed: TrackedMut::reborrow_mut(&mut vt.delayed), tracer: TrackedMut::reborrow_mut(&mut vt.tracer), }; - let mut vm = Vm::new(vt, route.track(), id, scopes); + let mut vm = Vm::new(vt, route.track(), FileId::detached(), scopes); let args = Args::new(self.span(), args); self.call_vm(&mut vm, args) } @@ -297,7 +297,7 @@ pub struct ParamInfo { #[derive(Hash)] pub(super) struct Closure { /// The source file where the closure was defined. - pub location: SourceId, + pub location: FileId, /// The name of the closure. pub name: Option<Ident>, /// Captured values from outer scopes. diff --git a/src/eval/library.rs b/src/eval/library.rs index 4978ada2..1b05de83 100644 --- a/src/eval/library.rs +++ b/src/eval/library.rs @@ -13,7 +13,6 @@ use crate::geom::{Abs, Dir}; use crate::model::{Content, ElemFunc, Introspector, Label, StyleChain, Styles, Vt}; use crate::syntax::Span; use crate::util::hash128; -use crate::World; /// Definition of Typst's standard library. #[derive(Debug, Clone, Hash)] @@ -66,10 +65,8 @@ pub struct LangItems { pub reference: fn(target: Label, supplement: Option<Content>) -> Content, /// The keys contained in the bibliography and short descriptions of them. #[allow(clippy::type_complexity)] - pub bibliography_keys: fn( - world: Tracked<dyn World + '_>, - introspector: Tracked<Introspector>, - ) -> Vec<(EcoString, Option<EcoString>)>, + pub bibliography_keys: + fn(introspector: Tracked<Introspector>) -> Vec<(EcoString, Option<EcoString>)>, /// A section heading: `= Introduction`. pub heading: fn(level: NonZeroUsize, body: Content) -> Content, /// The heading function. diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 93a73ea4..0805f9cc 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -55,27 +55,24 @@ pub use self::value::{Dynamic, Type, Value}; use std::collections::HashSet; use std::mem; -use std::path::{Path, PathBuf}; +use std::path::Path; use comemo::{Track, Tracked, TrackedMut, Validate}; use ecow::{EcoString, EcoVec}; use unicode_segmentation::UnicodeSegmentation; use self::func::{CapturesVisitor, Closure}; -use crate::model::{ - Content, Introspector, Label, Locator, Recipe, ShowableSelector, Styles, Transform, - Unlabellable, Vt, +use crate::diag::{ + bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint, }; -use crate::syntax::ast::AstNode; -use crate::syntax::{ - ast, parse_code, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode, +use crate::file::{FileId, PackageManifest, PackageSpec}; +use crate::model::{ + Content, DelayedErrors, Introspector, Label, Locator, Recipe, ShowableSelector, + Styles, Transform, Unlabellable, Vt, }; -use crate::util::PathExt; +use crate::syntax::ast::{self, AstNode}; +use crate::syntax::{parse_code, Source, Span, Spanned, SyntaxKind, SyntaxNode}; use crate::World; -use crate::{ - diag::{bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint}, - model::DelayedErrors, -}; const MAX_ITERATIONS: usize = 10_000; const MAX_CALL_DEPTH: usize = 64; @@ -91,9 +88,8 @@ pub fn eval( ) -> SourceResult<Module> { // Prevent cyclic evaluation. let id = source.id(); - let path = if id.is_detached() { Path::new("") } else { world.source(id).path() }; if route.contains(id) { - panic!("Tried to cyclicly evaluate {}", path.display()); + panic!("Tried to cyclicly evaluate {}", id.path().display()); } // Hook up the lang items. @@ -130,7 +126,7 @@ pub fn eval( } // Assemble the module. - let name = path.file_stem().unwrap_or_default().to_string_lossy(); + let name = id.path().file_stem().unwrap_or_default().to_string_lossy(); Ok(Module::new(name).with_scope(vm.scopes.top).with_content(result?)) } @@ -166,7 +162,7 @@ pub fn eval_string( // Prepare VM. let route = Route::default(); - let id = SourceId::detached(); + let id = FileId::detached(); let scopes = Scopes::new(Some(world.library())); let mut vm = Vm::new(vt, route.track(), id, scopes); @@ -194,7 +190,7 @@ pub struct Vm<'a> { /// The route of source ids the VM took to reach its current location. route: Tracked<'a, Route<'a>>, /// The current location. - location: SourceId, + location: FileId, /// A control flow event that is currently happening. flow: Option<FlowEvent>, /// The stack of scopes. @@ -210,7 +206,7 @@ impl<'a> Vm<'a> { fn new( vt: Vt<'a>, route: Tracked<'a, Route>, - location: SourceId, + location: FileId, scopes: Scopes<'a>, ) -> Self { let traced = vt.tracer.span(location); @@ -232,6 +228,11 @@ impl<'a> Vm<'a> { self.vt.world } + /// The location to which paths are relative currently. + pub fn location(&self) -> FileId { + self.location + } + /// Define a variable in the current scope. #[tracing::instrument(skip_all)] pub fn define(&mut self, var: ast::Ident, value: impl IntoValue) { @@ -241,23 +242,6 @@ impl<'a> Vm<'a> { } self.scopes.top.define(var.take(), value); } - - /// Resolve a user-entered path to be relative to the compilation - /// environment's root. - #[tracing::instrument(skip_all)] - pub fn locate(&self, path: &str) -> StrResult<PathBuf> { - if !self.location.is_detached() { - if let Some(path) = path.strip_prefix('/') { - return Ok(self.world().root().join(path).normalize()); - } - - if let Some(dir) = self.world().source(self.location).path().parent() { - return Ok(dir.join(path).normalize()); - } - } - - bail!("cannot access file system from here") - } } /// A control flow event that occurred during evaluation. @@ -296,12 +280,12 @@ pub struct Route<'a> { // covariant over the constraint. If it becomes invariant, we're in for a // world of lifetime pain. outer: Option<Tracked<'a, Self, <Route<'static> as Validate>::Constraint>>, - id: Option<SourceId>, + id: Option<FileId>, } impl<'a> Route<'a> { /// Create a new route with just one entry. - pub fn new(id: SourceId) -> Self { + pub fn new(id: FileId) -> Self { Self { id: Some(id), outer: None } } @@ -309,7 +293,7 @@ impl<'a> Route<'a> { /// /// You must guarantee that `outer` lives longer than the resulting /// route is ever used. - pub fn insert(outer: Tracked<'a, Self>, id: SourceId) -> Self { + pub fn insert(outer: Tracked<'a, Self>, id: FileId) -> Self { Route { outer: Some(outer), id: Some(id) } } @@ -328,7 +312,7 @@ impl<'a> Route<'a> { #[comemo::track] impl<'a> Route<'a> { /// Whether the given id is part of the route. - fn contains(&self, id: SourceId) -> bool { + fn contains(&self, id: FileId) -> bool { self.id == Some(id) || self.outer.map_or(false, |outer| outer.contains(id)) } } @@ -358,8 +342,8 @@ impl Tracer { #[comemo::track] impl Tracer { /// The traced span if it is part of the given source file. - fn span(&self, id: SourceId) -> Option<Span> { - if self.span.map(Span::source) == Some(id) { + fn span(&self, id: FileId) -> Option<Span> { + if self.span.map(Span::id) == Some(id) { self.span } else { None @@ -1764,20 +1748,49 @@ fn import( } }; + // Handle package and file imports. + let path = path.as_str(); + if path.starts_with('@') { + let spec = path.parse::<PackageSpec>().at(span)?; + import_package(vm, spec, span) + } else { + import_file(vm, path, span) + } +} + +/// Import an external package. +fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Module> { + // Evaluate the manifest. + let manifest_id = FileId::new(Some(spec.clone()), Path::new("/typst.toml")); + let bytes = vm.world().file(manifest_id).at(span)?; + let manifest = PackageManifest::parse(&bytes).at(span)?; + manifest.validate(&spec).at(span)?; + + // Evaluate the entry point. + let entrypoint = Path::new("/").join(manifest.package.entrypoint.as_str()); + let entrypoint_id = FileId::new(Some(spec), &entrypoint); + let source = vm.world().source(entrypoint_id).at(span)?; + let point = || Tracepoint::Import; + Ok(eval(vm.world(), vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source) + .trace(vm.world(), point, span)? + .with_name(manifest.package.name)) +} + +/// Import a file from a path. +fn import_file(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> { // Load the source file. let world = vm.world(); - let full = vm.locate(&path).at(span)?; - let id = world.resolve(&full).at(span)?; + let id = vm.location().join(path).at(span)?; + let source = world.source(id).at(span)?; // Prevent cyclic importing. - if vm.route.contains(id) { + if vm.route.contains(source.id()) { bail!(span, "cyclic import"); } // Evaluate the file. - let source = world.source(id); let point = || Tracepoint::Import; - eval(world, vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), source) + eval(world, vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source) .trace(world, point, span) } diff --git a/src/eval/module.rs b/src/eval/module.rs index fbfdd4e6..0bc6bf38 100644 --- a/src/eval/module.rs +++ b/src/eval/module.rs @@ -7,15 +7,20 @@ use super::{Content, Scope, Value}; use crate::diag::StrResult; /// An evaluated module, ready for importing or typesetting. +/// +/// Values of this type are cheap to clone and hash. #[derive(Clone, Hash)] #[allow(clippy::derived_hash_with_manual_eq)] -pub struct Module(Arc<Repr>); +pub struct Module { + /// The module's name. + name: EcoString, + /// The reference-counted inner fields. + inner: Arc<Repr>, +} /// The internal representation. #[derive(Clone, Hash)] struct Repr { - /// The module's name. - name: EcoString, /// The top-level definitions that were bound in this module. scope: Scope, /// The module's layoutable contents. @@ -25,38 +30,43 @@ struct Repr { impl Module { /// Create a new module. pub fn new(name: impl Into<EcoString>) -> Self { - Self(Arc::new(Repr { + Self { name: name.into(), - scope: Scope::new(), - content: Content::empty(), - })) + inner: Arc::new(Repr { scope: Scope::new(), content: Content::empty() }), + } + } + + /// Update the module's name. + pub fn with_name(mut self, name: impl Into<EcoString>) -> Self { + self.name = name.into(); + self } /// Update the module's scope. pub fn with_scope(mut self, scope: Scope) -> Self { - Arc::make_mut(&mut self.0).scope = scope; + Arc::make_mut(&mut self.inner).scope = scope; self } /// Update the module's content. pub fn with_content(mut self, content: Content) -> Self { - Arc::make_mut(&mut self.0).content = content; + Arc::make_mut(&mut self.inner).content = content; self } /// Get the module's name. pub fn name(&self) -> &EcoString { - &self.0.name + &self.name } /// Access the module's scope. pub fn scope(&self) -> &Scope { - &self.0.scope + &self.inner.scope } /// Access the module's scope, mutably. pub fn scope_mut(&mut self) -> &mut Scope { - &mut Arc::make_mut(&mut self.0).scope + &mut Arc::make_mut(&mut self.inner).scope } /// Try to access a definition in the module. @@ -68,7 +78,7 @@ impl Module { /// Extract the module's content. pub fn content(self) -> Content { - match Arc::try_unwrap(self.0) { + match Arc::try_unwrap(self.inner) { Ok(repr) => repr.content, Err(arc) => arc.content.clone(), } @@ -83,6 +93,6 @@ impl Debug for Module { impl PartialEq for Module { fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.0, &other.0) + self.name == other.name && Arc::ptr_eq(&self.inner, &other.inner) } } diff --git a/src/eval/value.rs b/src/eval/value.rs index 91fdadbe..b1782cab 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -15,9 +15,10 @@ use crate::diag::StrResult; use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel}; use crate::model::{Label, Styles}; use crate::syntax::{ast, Span}; +use crate::util::Bytes; /// A computational value. -#[derive(Clone, Default)] +#[derive(Default, Clone)] pub enum Value { /// The value that indicates the absence of a meaningful value. #[default] @@ -46,6 +47,8 @@ pub enum Value { Symbol(Symbol), /// A string: `"string"`. Str(Str), + /// Raw bytes. + Bytes(Bytes), /// A label: `<intro>`. Label(Label), /// A content value: `[*Hi* there]`. @@ -103,6 +106,7 @@ impl Value { Self::Color(_) => Color::TYPE_NAME, Self::Symbol(_) => Symbol::TYPE_NAME, Self::Str(_) => Str::TYPE_NAME, + Self::Bytes(_) => Bytes::TYPE_NAME, Self::Label(_) => Label::TYPE_NAME, Self::Content(_) => Content::TYPE_NAME, Self::Styles(_) => Styles::TYPE_NAME, @@ -186,6 +190,7 @@ impl Debug for Value { Self::Color(v) => Debug::fmt(v, f), Self::Symbol(v) => Debug::fmt(v, f), Self::Str(v) => Debug::fmt(v, f), + Self::Bytes(v) => Debug::fmt(v, f), Self::Label(v) => Debug::fmt(v, f), Self::Content(v) => Debug::fmt(v, f), Self::Styles(v) => Debug::fmt(v, f), @@ -228,6 +233,7 @@ impl Hash for Value { Self::Color(v) => v.hash(state), Self::Symbol(v) => v.hash(state), Self::Str(v) => v.hash(state), + Self::Bytes(v) => v.hash(state), Self::Label(v) => v.hash(state), Self::Content(v) => v.hash(state), Self::Styles(v) => v.hash(state), @@ -400,6 +406,7 @@ primitive! { Str, Symbol(symbol) => symbol.get().into() } +primitive! { Bytes: "bytes", Bytes } primitive! { Label: "label", Label } primitive! { Content: "content", Content, |
