typst/src/model/vm.rs

98 lines
2.8 KiB
Rust

use std::path::PathBuf;
use comemo::Tracked;
use super::{LangItems, Route, Scopes, Value};
use crate::diag::{error, SourceError, StrResult};
use crate::syntax::{SourceId, Span};
use crate::util::PathExt;
use crate::World;
/// A virtual machine.
///
/// Holds the state needed to evaluate Typst sources. A new virtual machine is
/// created for each module evaluation and function call.
pub struct Vm<'a> {
/// The core context.
pub(crate) world: Tracked<'a, dyn World>,
/// The route of source ids the VM took to reach its current location.
pub(crate) route: Tracked<'a, Route>,
/// The current location.
pub(crate) location: SourceId,
/// The stack of scopes.
pub(crate) scopes: Scopes<'a>,
/// A control flow event that is currently happening.
pub(crate) flow: Option<Flow>,
/// The language items.
pub(crate) items: LangItems,
}
impl<'a> Vm<'a> {
/// Create a new virtual machine.
pub fn new(
world: Tracked<'a, dyn World>,
route: Tracked<'a, Route>,
location: SourceId,
scopes: Scopes<'a>,
) -> Self {
Self {
world,
route,
location,
scopes,
flow: None,
items: world.library().items.clone(),
}
}
/// Access the underlying world.
pub fn world(&self) -> Tracked<dyn World> {
self.world
}
/// Resolve a user-entered path to be relative to the compilation
/// environment's root.
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());
}
}
Err("cannot access file system from here".into())
}
}
/// A control flow event that occurred during evaluation.
#[derive(Debug, Clone, PartialEq)]
pub enum Flow {
/// Stop iteration in a loop.
Break(Span),
/// Skip the remainder of the current iteration in a loop.
Continue(Span),
/// Stop execution of a function early, optionally returning an explicit
/// value.
Return(Span, Option<Value>),
}
impl Flow {
/// Return an error stating that this control flow is forbidden.
pub fn forbidden(&self) -> SourceError {
match *self {
Self::Break(span) => {
error!(span, "cannot break outside of loop")
}
Self::Continue(span) => {
error!(span, "cannot continue outside of loop")
}
Self::Return(span, _) => {
error!(span, "cannot return outside of function")
}
}
}
}