summaryrefslogtreecommitdiff
path: root/src/eval/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval/mod.rs')
-rw-r--r--src/eval/mod.rs105
1 files changed, 59 insertions, 46 deletions
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)
}