summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/func.rs8
-rw-r--r--src/eval/library.rs7
-rw-r--r--src/eval/mod.rs105
-rw-r--r--src/eval/module.rs38
-rw-r--r--src/eval/value.rs9
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,