summaryrefslogtreecommitdiff
path: root/src/model/eval.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-25 10:36:31 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-25 12:16:13 +0100
commitbf5edbbbbb75120d065d1c9587ccfa4eed4fdca1 (patch)
tree956af910ab27a8cec0db83171cd3f0b6d0570a60 /src/model/eval.rs
parent96f72eee6c6b595164c7a0576c407d7a590661db (diff)
Tidy up
Diffstat (limited to 'src/model/eval.rs')
-rw-r--r--src/model/eval.rs114
1 files changed, 103 insertions, 11 deletions
diff --git a/src/model/eval.rs b/src/model/eval.rs
index e68f6e7a..6a93ca13 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -2,19 +2,22 @@
use std::collections::BTreeMap;
use std::mem;
+use std::path::PathBuf;
use comemo::{Track, Tracked};
use unicode_segmentation::UnicodeSegmentation;
use super::{
- methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func,
- Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value, Vm,
+ methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Func,
+ LangItems, Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value,
+};
+use crate::diag::{
+ bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
-use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint};
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
use crate::syntax::ast::AstNode;
use crate::syntax::{ast, Source, SourceId, Span, Spanned, Unit};
-use crate::util::{format_eco, EcoString};
+use crate::util::{format_eco, EcoString, PathExt};
use crate::World;
/// Evaluate a source file and return the resulting module.
@@ -54,6 +57,94 @@ pub fn eval(
Ok(Module { scope: vm.scopes.top, content: result? })
}
+/// A virtual machine.
+///
+/// Holds the state needed to [evaluate](super::eval()) Typst sources. A new
+/// virtual machine is created for each module evaluation and function call.
+pub struct Vm<'a> {
+ /// The compilation environment.
+ pub(super) world: Tracked<'a, dyn World>,
+ /// The language items.
+ pub(super) items: LangItems,
+ /// The route of source ids the VM took to reach its current location.
+ pub(super) route: Tracked<'a, Route>,
+ /// The current location.
+ pub(super) location: SourceId,
+ /// A control flow event that is currently happening.
+ pub(super) flow: Option<Flow>,
+ /// The stack of scopes.
+ pub(super) scopes: Scopes<'a>,
+}
+
+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,
+ items: world.library().items.clone(),
+ route,
+ location,
+ flow: None,
+ scopes,
+ }
+ }
+
+ /// 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")
+ }
+ }
+ }
+}
+
/// A route of source ids.
#[derive(Default)]
pub struct Route {
@@ -87,7 +178,7 @@ impl Route {
}
}
-/// An evaluated module, ready for importing or layouting.
+/// An evaluated module, ready for importing or typesetting.
#[derive(Debug, Clone)]
pub struct Module {
/// The top-level definitions that were bound in this module.
@@ -700,18 +791,19 @@ impl Eval for ast::MethodCall {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let span = self.span();
let method = self.method().take();
- let point = || Tracepoint::Call(Some(method.clone()));
- Ok(if methods::is_mutating(&method) {
+ let result = if methods::is_mutating(&method) {
let args = self.args().eval(vm)?;
let value = self.target().access(vm)?;
- methods::call_mut(value, &method, args, span).trace(vm.world, point, span)?;
- Value::None
+ methods::call_mut(value, &method, args, span).map(|()| Value::None)
} else {
let value = self.target().eval(vm)?;
let args = self.args().eval(vm)?;
- methods::call(vm, value, &method, args, span).trace(vm.world, point, span)?
- })
+ methods::call(vm, value, &method, args, span)
+ };
+
+ let point = || Tracepoint::Call(Some(method.clone()));
+ result.trace(vm.world, point, span)
}
}