diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-17 15:47:54 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-17 17:32:56 +0100 |
| commit | c5e67af22bd6242366819879be84c10c4dd135be (patch) | |
| tree | d857b99b26401d1b3b74c4cebacbf086c25bef40 /src | |
| parent | 3d965ae6a479636a13b2e2f2344e8d97bedece1f (diff) | |
Merge eval and layout contexts into `Vm`
Diffstat (limited to 'src')
33 files changed, 465 insertions, 506 deletions
diff --git a/src/eval/capture.rs b/src/eval/capture.rs index 8585776a..d62ec55c 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -28,7 +28,7 @@ impl<'a> CapturesVisitor<'a> { /// Bind a new internal variable. pub fn bind(&mut self, ident: Ident) { - self.internal.def_mut(ident.take(), Value::None); + self.internal.top.def_mut(ident.take(), Value::None); } /// Capture a variable if it isn't internal. @@ -135,9 +135,9 @@ mod tests { let red = RedNode::from_root(green, SourceId::from_raw(0)); let mut scopes = Scopes::new(None); - scopes.def_const("x", 0); - scopes.def_const("y", 0); - scopes.def_const("z", 0); + scopes.top.def_const("x", 0); + scopes.top.def_const("y", 0); + scopes.top.def_const("z", 0); let mut visitor = CapturesVisitor::new(&scopes); visitor.visit(red.as_ref()); diff --git a/src/eval/class.rs b/src/eval/class.rs index c4880e9a..28e49a99 100644 --- a/src/eval/class.rs +++ b/src/eval/class.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; -use super::{Args, EvalContext, Func, StyleMap, Template, Value}; +use super::{Args, Func, StyleMap, Template, Value, Vm}; use crate::diag::TypResult; /// A class of nodes. @@ -36,7 +36,7 @@ use crate::diag::TypResult; #[derive(Clone)] pub struct Class { name: &'static str, - construct: fn(&mut EvalContext, &mut Args) -> TypResult<Value>, + construct: fn(&mut Vm, &mut Args) -> TypResult<Value>, set: fn(&mut Args, &mut StyleMap) -> TypResult<()>, } @@ -73,8 +73,8 @@ impl Class { /// This parses both property and data arguments (in this order), styles the /// template constructed from the data with the style properties and wraps /// it in a value. - pub fn construct(&self, ctx: &mut EvalContext, mut args: Args) -> TypResult<Value> { - let value = (self.construct)(ctx, &mut args)?; + pub fn construct(&self, vm: &mut Vm, mut args: Args) -> TypResult<Value> { + let value = (self.construct)(vm, &mut args)?; args.finish()?; Ok(value) } @@ -117,7 +117,7 @@ pub trait Construct { /// /// This is passed only the arguments that remain after execution of the /// class's set rule. - fn construct(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Template>; + fn construct(vm: &mut Vm, args: &mut Args) -> TypResult<Template>; } /// Set style properties of a class. diff --git a/src/eval/func.rs b/src/eval/func.rs index a4a733b3..9a8a9c94 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use super::{Cast, Eval, EvalContext, Scope, Value}; +use super::{Cast, Eval, Scope, Value, Vm}; use crate::diag::{At, TypResult}; use crate::syntax::ast::Expr; use crate::syntax::{Span, Spanned}; @@ -27,7 +27,7 @@ impl Func { /// Create a new function from a native rust function. pub fn native( name: &'static str, - func: fn(&mut EvalContext, &mut Args) -> TypResult<Value>, + func: fn(&mut Vm, &mut Args) -> TypResult<Value>, ) -> Self { Self(Arc::new(Repr::Native(Native { name, func }))) } @@ -47,13 +47,13 @@ impl Func { } /// Call the function in the context with the arguments. - pub fn call(&self, ctx: &mut EvalContext, mut args: Args) -> TypResult<Value> { + pub fn call(&self, vm: &mut Vm, mut args: Args) -> TypResult<Value> { let value = match self.0.as_ref() { - Repr::Native(native) => (native.func)(ctx, &mut args)?, - Repr::Closure(closure) => closure.call(ctx, &mut args)?, + Repr::Native(native) => (native.func)(vm, &mut args)?, + Repr::Closure(closure) => closure.call(vm, &mut args)?, Repr::With(wrapped, applied) => { args.items.splice(.. 0, applied.items.iter().cloned()); - return wrapped.call(ctx, args); + return wrapped.call(vm, args); } }; args.finish()?; @@ -88,7 +88,7 @@ struct Native { /// The name of the function. pub name: &'static str, /// The function pointer. - pub func: fn(&mut EvalContext, &mut Args) -> TypResult<Value>, + pub func: fn(&mut Vm, &mut Args) -> TypResult<Value>, } impl Hash for Native { @@ -115,15 +115,15 @@ pub struct Closure { impl Closure { /// Call the function in the context with the arguments. - pub fn call(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + pub fn call(&self, vm: &mut Vm, args: &mut Args) -> TypResult<Value> { // Don't leak the scopes from the call site. Instead, we use the // scope of captured variables we collected earlier. - let prev_scopes = std::mem::take(&mut ctx.scopes); - ctx.scopes.top = self.captured.clone(); + let prev_scopes = std::mem::take(&mut vm.scopes); + vm.scopes.top = self.captured.clone(); // Parse the arguments according to the parameter list. for (param, default) in &self.params { - ctx.scopes.def_mut(param, match default { + vm.scopes.top.def_mut(param, match default { None => args.expect::<Value>(param)?, Some(default) => { args.named::<Value>(param)?.unwrap_or_else(|| default.clone()) @@ -133,14 +133,14 @@ impl Closure { // Put the remaining arguments into the sink. if let Some(sink) = &self.sink { - ctx.scopes.def_mut(sink, args.take()); + vm.scopes.top.def_mut(sink, args.take()); } // Evaluate the body. - let value = self.body.eval(ctx)?; + let value = self.body.eval(vm)?; // Restore the call site scopes. - ctx.scopes = prev_scopes; + vm.scopes = prev_scopes; Ok(value) } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 5f339bc8..98f84082 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -29,24 +29,19 @@ pub use styles::*; pub use template::*; pub use value::*; -use std::collections::HashMap; use std::io; use std::mem; -use std::path::PathBuf; use unicode_segmentation::UnicodeSegmentation; use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::geom::{Angle, Fractional, Length, Relative}; -use crate::image::ImageStore; use crate::layout::Layout; use crate::library::{self, ORDERED, UNORDERED}; -use crate::loading::Loader; -use crate::source::{SourceId, SourceStore}; use crate::syntax::ast::*; use crate::syntax::{Span, Spanned}; use crate::util::EcoString; -use crate::Context; +use crate::Vm; /// An evaluated module, ready for importing or conversion to a root layout /// tree. @@ -64,106 +59,20 @@ pub trait Eval { type Output; /// Evaluate the expression to the output value. - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output>; -} - -/// The context for evaluation. -pub struct EvalContext<'a> { - /// The loader from which resources (files and images) are loaded. - pub loader: &'a dyn Loader, - /// Stores loaded source files. - pub sources: &'a mut SourceStore, - /// Stores decoded images. - pub images: &'a mut ImageStore, - /// The stack of imported files that led to evaluation of the current file. - pub route: Vec<SourceId>, - /// Caches imported modules. - pub modules: HashMap<SourceId, Module>, - /// The active scopes. - pub scopes: Scopes<'a>, -} - -impl<'a> EvalContext<'a> { - /// Create a new evaluation context. - pub fn new(ctx: &'a mut Context, source: SourceId) -> Self { - Self { - loader: ctx.loader.as_ref(), - sources: &mut ctx.sources, - images: &mut ctx.images, - route: vec![source], - modules: HashMap::new(), - scopes: Scopes::new(Some(&ctx.std)), - } - } - - /// Process an import of a module relative to the current location. - pub fn import(&mut self, path: &str, span: Span) -> TypResult<SourceId> { - // Load the source file. - let full = self.make_path(path); - let id = self.sources.load(&full).map_err(|err| { - Error::boxed(span, match err.kind() { - io::ErrorKind::NotFound => "file not found".into(), - _ => format!("failed to load source file ({})", err), - }) - })?; - - // Prevent cyclic importing. - if self.route.contains(&id) { - bail!(span, "cyclic import"); - } - - // Check whether the module was already loaded. - if self.modules.get(&id).is_some() { - return Ok(id); - } - - // Parse the file. - let source = self.sources.get(id); - let ast = source.ast()?; - - // Prepare the new context. - let new_scopes = Scopes::new(self.scopes.base); - let prev_scopes = mem::replace(&mut self.scopes, new_scopes); - self.route.push(id); - - // Evaluate the module. - let template = ast.eval(self).trace(|| Tracepoint::Import, span)?; - - // Restore the old context. - let new_scopes = mem::replace(&mut self.scopes, prev_scopes); - self.route.pop().unwrap(); - - // Save the evaluated module. - let module = Module { scope: new_scopes.top, template }; - self.modules.insert(id, module); - - Ok(id) - } - - /// Complete a user-entered path (relative to the source file) to be - /// relative to the compilation environment's root. - pub fn make_path(&self, path: &str) -> PathBuf { - if let Some(&id) = self.route.last() { - if let Some(dir) = self.sources.get(id).path().parent() { - return dir.join(path); - } - } - - path.into() - } + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output>; } impl Eval for Markup { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - eval_markup(ctx, &mut self.nodes()) + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + eval_markup(vm, &mut self.nodes()) } } /// Evaluate a stream of markup nodes. fn eval_markup( - ctx: &mut EvalContext, + vm: &mut Vm, nodes: &mut impl Iterator<Item = MarkupNode>, ) -> TypResult<Template> { let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default()); @@ -172,21 +81,21 @@ fn eval_markup( seq.push(match node { MarkupNode::Expr(Expr::Set(set)) => { let class = set.class(); - let class = class.eval(ctx)?.cast::<Class>().at(class.span())?; - let args = set.args().eval(ctx)?; + let class = class.eval(vm)?.cast::<Class>().at(class.span())?; + let args = set.args().eval(vm)?; let styles = class.set(args)?; - let tail = eval_markup(ctx, nodes)?; + let tail = eval_markup(vm, nodes)?; tail.styled_with_map(styles) } MarkupNode::Expr(Expr::Show(show)) => { return Err("show rules are not yet implemented").at(show.span()); } MarkupNode::Expr(Expr::Wrap(wrap)) => { - let tail = eval_markup(ctx, nodes)?; - ctx.scopes.def_mut(wrap.binding().take(), tail); - wrap.body().eval(ctx)?.display() + let tail = eval_markup(vm, nodes)?; + vm.scopes.top.def_mut(wrap.binding().take(), tail); + wrap.body().eval(vm)?.display() } - _ => node.eval(ctx)?, + _ => node.eval(vm)?, }); } @@ -196,20 +105,20 @@ fn eval_markup( impl Eval for MarkupNode { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { Ok(match self { Self::Space => Template::Space, Self::Linebreak => Template::Linebreak, Self::Parbreak => Template::Parbreak, Self::Text(text) => Template::Text(text.clone()), - Self::Strong(strong) => strong.eval(ctx)?, - Self::Emph(emph) => emph.eval(ctx)?, - Self::Raw(raw) => raw.eval(ctx)?, - Self::Math(math) => math.eval(ctx)?, - Self::Heading(heading) => heading.eval(ctx)?, - Self::List(list) => list.eval(ctx)?, - Self::Enum(enum_) => enum_.eval(ctx)?, - Self::Expr(expr) => expr.eval(ctx)?.display(), + Self::Strong(strong) => strong.eval(vm)?, + Self::Emph(emph) => emph.eval(vm)?, + Self::Raw(raw) => raw.eval(vm)?, + Self::Math(math) => math.eval(vm)?, + Self::Heading(heading) => heading.eval(vm)?, + Self::List(list) => list.eval(vm)?, + Self::Enum(enum_) => enum_.eval(vm)?, + Self::Expr(expr) => expr.eval(vm)?.display(), }) } } @@ -217,23 +126,23 @@ impl Eval for MarkupNode { impl Eval for StrongNode { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - Ok(Template::show(library::StrongNode(self.body().eval(ctx)?))) + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + Ok(Template::show(library::StrongNode(self.body().eval(vm)?))) } } impl Eval for EmphNode { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - Ok(Template::show(library::EmphNode(self.body().eval(ctx)?))) + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + Ok(Template::show(library::EmphNode(self.body().eval(vm)?))) } } impl Eval for RawNode { type Output = Template; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { let template = Template::show(library::RawNode { text: self.text.clone(), block: self.block, @@ -248,7 +157,7 @@ impl Eval for RawNode { impl Eval for MathNode { type Output = Template; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Ok(Template::show(library::MathNode { formula: self.formula.clone(), display: self.display, @@ -259,9 +168,9 @@ impl Eval for MathNode { impl Eval for HeadingNode { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { Ok(Template::show(library::HeadingNode { - body: self.body().eval(ctx)?, + body: self.body().eval(vm)?, level: self.level(), })) } @@ -270,10 +179,10 @@ impl Eval for HeadingNode { impl Eval for ListNode { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { Ok(Template::show(library::ListNode::<UNORDERED> { number: None, - child: self.body().eval(ctx)?.pack(), + child: self.body().eval(vm)?.pack(), })) } } @@ -281,10 +190,10 @@ impl Eval for ListNode { impl Eval for EnumNode { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { Ok(Template::show(library::ListNode::<ORDERED> { number: self.number(), - child: self.body().eval(ctx)?.pack(), + child: self.body().eval(vm)?.pack(), })) } } @@ -292,32 +201,32 @@ impl Eval for EnumNode { impl Eval for Expr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { match self { - Self::Lit(v) => v.eval(ctx), - Self::Ident(v) => v.eval(ctx), - Self::Array(v) => v.eval(ctx).map(Value::Array), - Self::Dict(v) => v.eval(ctx).map(Value::Dict), - Self::Template(v) => v.eval(ctx).map(Value::Template), - Self::Group(v) => v.eval(ctx), - Self::Block(v) => v.eval(ctx), - Self::Call(v) => v.eval(ctx), - Self::Closure(v) => v.eval(ctx), - Self::With(v) => v.eval(ctx), - Self::Unary(v) => v.eval(ctx), - Self::Binary(v) => v.eval(ctx), - Self::Let(v) => v.eval(ctx), - Self::Set(v) => v.eval(ctx), - Self::Show(v) => v.eval(ctx), - Self::Wrap(v) => v.eval(ctx), - Self::If(v) => v.eval(ctx), - Self::While(v) => v.eval(ctx), - Self::For(v) => v.eval(ctx), - Self::Import(v) => v.eval(ctx), - Self::Include(v) => v.eval(ctx), - Self::Break(v) => v.eval(ctx), - Self::Continue(v) => v.eval(ctx), - Self::Return(v) => v.eval(ctx), + Self::Lit(v) => v.eval(vm), + Self::Ident(v) => v.eval(vm), + Self::Array(v) => v.eval(vm).map(Value::Array), + Self::Dict(v) => v.eval(vm).map(Value::Dict), + Self::Template(v) => v.eval(vm).map(Value::Template), + Self::Group(v) => v.eval(vm), + Self::Block(v) => v.eval(vm), + Self::Call(v) => v.eval(vm), + Self::Closure(v) => v.eval(vm), + Self::With(v) => v.eval(vm), + Self::Unary(v) => v.eval(vm), + Self::Binary(v) => v.eval(vm), + Self::Let(v) => v.eval(vm), + Self::Set(v) => v.eval(vm), + Self::Show(v) => v.eval(vm), + Self::Wrap(v) => v.eval(vm), + Self::If(v) => v.eval(vm), + Self::While(v) => v.eval(vm), + Self::For(v) => v.eval(vm), + Self::Import(v) => v.eval(vm), + Self::Include(v) => v.eval(vm), + Self::Break(v) => v.eval(vm), + Self::Continue(v) => v.eval(vm), + Self::Return(v) => v.eval(vm), } } } @@ -325,7 +234,7 @@ impl Eval for Expr { impl Eval for Lit { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Ok(match self.kind() { LitKind::None => Value::None, LitKind::Auto => Value::Auto, @@ -344,8 +253,8 @@ impl Eval for Lit { impl Eval for Ident { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - match ctx.scopes.get(self) { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + match vm.scopes.get(self) { Some(slot) => Ok(slot.read().unwrap().clone()), None => bail!(self.span(), "unknown variable"), } @@ -355,17 +264,17 @@ impl Eval for Ident { impl Eval for ArrayExpr { type Output = Array; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - self.items().map(|expr| expr.eval(ctx)).collect() + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + self.items().map(|expr| expr.eval(vm)).collect() } } impl Eval for DictExpr { type Output = Dict; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { self.items() - .map(|x| Ok((x.name().take(), x.expr().eval(ctx)?))) + .map(|x| Ok((x.name().take(), x.expr().eval(vm)?))) .collect() } } @@ -373,10 +282,10 @@ impl Eval for DictExpr { impl Eval for TemplateExpr { type Output = Template; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - ctx.scopes.enter(); - let template = self.body().eval(ctx)?; - ctx.scopes.exit(); + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + vm.scopes.enter(); + let template = self.body().eval(vm)?; + vm.scopes.exit(); Ok(template) } } @@ -384,24 +293,24 @@ impl Eval for TemplateExpr { impl Eval for GroupExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - self.expr().eval(ctx) + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + self.expr().eval(vm) } } impl Eval for BlockExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - ctx.scopes.enter(); + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + vm.scopes.enter(); let mut output = Value::None; for expr in self.exprs() { - let value = expr.eval(ctx)?; + let value = expr.eval(vm)?; output = ops::join(output, value).at(expr.span())?; } - ctx.scopes.exit(); + vm.scopes.exit(); Ok(output) } @@ -410,8 +319,8 @@ impl Eval for BlockExpr { impl Eval for UnaryExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - let value = self.expr().eval(ctx)?; + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + let value = self.expr().eval(vm)?; let result = match self.op() { UnOp::Pos => ops::pos(value), UnOp::Neg => ops::neg(value), @@ -424,25 +333,25 @@ impl Eval for UnaryExpr { impl Eval for BinaryExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { match self.op() { - BinOp::Add => self.apply(ctx, ops::add), - BinOp::Sub => self.apply(ctx, ops::sub), - BinOp::Mul => self.apply(ctx, ops::mul), - BinOp::Div => self.apply(ctx, ops::div), - BinOp::And => self.apply(ctx, ops::and), - BinOp::Or => self.apply(ctx, ops::or), - BinOp::Eq => self.apply(ctx, ops::eq), - BinOp::Neq => self.apply(ctx, ops::neq), - BinOp::Lt => self.apply(ctx, ops::lt), - BinOp::Leq => self.apply(ctx, ops::leq), - BinOp::Gt => self.apply(ctx, ops::gt), - BinOp::Geq => self.apply(ctx, ops::geq), - BinOp::Assign => self.assign(ctx, |_, b| Ok(b)), - BinOp::AddAssign => self.assign(ctx, ops::add), - BinOp::SubAssign => self.assign(ctx, ops::sub), - BinOp::MulAssign => self.assign(ctx, ops::mul), - BinOp::DivAssign => self.assign(ctx, ops::div), + BinOp::Add => self.apply(vm, ops::add), + BinOp::Sub => self.apply(vm, ops::sub), + BinOp::Mul => self.apply(vm, ops::mul), + BinOp::Div => self.apply(vm, ops::div), + BinOp::And => self.apply(vm, ops::and), + BinOp::Or => self.apply(vm, ops::or), + BinOp::Eq => self.apply(vm, ops::eq), + BinOp::Neq => self.apply(vm, ops::neq), + BinOp::Lt => self.apply(vm, ops::lt), + BinOp::Leq => self.apply(vm, ops::leq), + BinOp::Gt => self.apply(vm, ops::gt), + BinOp::Geq => self.apply(vm, ops::geq), + BinOp::Assign => self.assign(vm, |_, b| Ok(b)), + BinOp::AddAssign => self.assign(vm, ops::add), + BinOp::SubAssign => self.assign(vm, ops::sub), + BinOp::MulAssign => self.assign(vm, ops::mul), + BinOp::DivAssign => self.assign(vm, ops::div), } } } @@ -451,10 +360,10 @@ impl BinaryExpr { /// Apply a basic binary operation. fn apply( &self, - ctx: &mut EvalContext, + vm: &mut Vm, op: fn(Value, Value) -> StrResult<Value>, ) -> TypResult<Value> { - let lhs = self.lhs().eval(ctx)?; + let lhs = self.lhs().eval(vm)?; // Short-circuit boolean operations. if (self.op() == BinOp::And && lhs == Value::Bool(false)) @@ -463,19 +372,19 @@ impl BinaryExpr { return Ok(lhs); } - let rhs = self.rhs().eval(ctx)?; + let rhs = self.rhs().eval(vm)?; op(lhs, rhs).at(self.span()) } /// Apply an assignment operation. fn assign( &self, - ctx: &mut EvalContext, + vm: &mut Vm, op: fn(Value, Value) -> StrResult<Value>, ) -> TypResult<Value> { - let rhs = self.rhs().eval(ctx)?; + let rhs = self.rhs().eval(vm)?; self.lhs().access( - ctx, + vm, Box::new(|target| { let lhs = mem::take(&mut *target); *target = op(lhs, rhs).at(self.span())?; @@ -489,10 +398,10 @@ impl BinaryExpr { impl Eval for CallExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { let span = self.callee().span(); - let callee = self.callee().eval(ctx)?; - let args = self.args().eval(ctx)?; + let callee = self.callee().eval(vm)?; + let args = self.args().eval(vm)?; match callee { Value::Array(array) => { @@ -505,12 +414,12 @@ impl Eval for CallExpr { Value::Func(func) => { let point = || Tracepoint::Call(func.name().map(ToString::to_string)); - func.call(ctx, args).trace(point, self.span()) + func.call(vm, args).trace(point, self.span()) } Value::Class(class) => { let point = || Tracepoint::Call(Some(class.name().to_string())); - class.construct(ctx, args).trace(point, self.span()) + class.construct(vm, args).trace(point, self.span()) } v => bail!( @@ -525,7 +434,7 @@ impl Eval for CallExpr { impl Eval for CallArgs { type Output = Args; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { let mut items = Vec::new(); for arg in self.items() { @@ -535,17 +444,17 @@ impl Eval for CallArgs { items.push(Arg { span, name: None, - value: Spanned::new(expr.eval(ctx)?, expr.span()), + value: Spanned::new(expr.eval(vm)?, expr.span()), }); } CallArg::Named(named) => { items.push(Arg { span, name: Some(named.name().take()), - value: Spanned::new(named.expr().eval(ctx)?, named.expr().span()), + value: Spanned::new(named.expr().eval(vm)?, named.expr().span()), }); } - CallArg::Spread(expr) => match expr.eval(ctx)? { + CallArg::Spread(expr) => match expr.eval(vm)? { Value::None => {} Value::Array(array) => { items.extend(array.into_iter().map(|value| Arg { @@ -574,13 +483,13 @@ impl Eval for CallArgs { impl Eval for ClosureExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { // The closure's name is defined by its let binding if there's one. let name = self.name().map(Ident::take); // Collect captured variables. let captured = { - let mut visitor = CapturesVisitor::new(&ctx.scopes); + let mut visitor = CapturesVisitor::new(&vm.scopes); visitor.visit(self.as_red()); visitor.finish() }; @@ -595,7 +504,7 @@ impl Eval for ClosureExpr { params.push((name.take(), None)); } ClosureParam::Named(named) => { - params.push((named.name().take(), Some(named.expr().eval(ctx)?))); + params.push((named.name().take(), Some(named.expr().eval(vm)?))); } ClosureParam::Sink(name) => { if sink.is_some() { @@ -620,10 +529,10 @@ impl Eval for ClosureExpr { impl Eval for WithExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { let callee = self.callee(); - let func = callee.eval(ctx)?.cast::<Func>().at(callee.span())?; - let args = self.args().eval(ctx)?; + let func = callee.eval(vm)?.cast::<Func>().at(callee.span())?; + let args = self.args().eval(vm)?; Ok(Value::Func(func.with(args))) } } @@ -631,12 +540,12 @@ impl Eval for WithExpr { impl Eval for LetExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { let value = match self.init() { - Some(expr) => expr.eval(ctx)?, + Some(expr) => expr.eval(vm)?, None => Value::None, }; - ctx.scopes.def_mut(self.binding().take(), value); + vm.scopes.top.def_mut(self.binding().take(), value); Ok(Value::None) } } @@ -644,7 +553,7 @@ impl Eval for LetExpr { impl Eval for SetExpr { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Err("set is only allowed directly in markup").at(self.span()) } } @@ -652,7 +561,7 @@ impl Eval for SetExpr { impl Eval for ShowExpr { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Err("show is only allowed directly in markup").at(self.span()) } } @@ -660,7 +569,7 @@ impl Eval for ShowExpr { impl Eval for WrapExpr { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Err("wrap is only allowed directly in markup").at(self.span()) } } @@ -668,12 +577,12 @@ impl Eval for WrapExpr { impl Eval for IfExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { let condition = self.condition(); - if condition.eval(ctx)?.cast::<bool>().at(condition.span())? { - self.if_body().eval(ctx) + if condition.eval(vm)?.cast::<bool>().at(condition.span())? { + self.if_body().eval(vm) } else if let Some(else_body) = self.else_body() { - else_body.eval(ctx) + else_body.eval(vm) } else { Ok(Value::None) } @@ -683,13 +592,13 @@ impl Eval for IfExpr { impl Eval for WhileExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { let mut output = Value::None; let condition = self.condition(); - while condition.eval(ctx)?.cast::<bool>().at(condition.span())? { + while condition.eval(vm)?.cast::<bool>().at(condition.span())? { let body = self.body(); - let value = body.eval(ctx)?; + let value = body.eval(vm)?; output = ops::join(output, value).at(body.span())?; } @@ -700,27 +609,27 @@ impl Eval for WhileExpr { impl Eval for ForExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { macro_rules! iter { (for ($($binding:ident => $value:ident),*) in $iter:expr) => {{ let mut output = Value::None; - ctx.scopes.enter(); + vm.scopes.enter(); #[allow(unused_parens)] for ($($value),*) in $iter { - $(ctx.scopes.def_mut(&$binding, $value);)* + $(vm.scopes.top.def_mut(&$binding, $value);)* - let value = self.body().eval(ctx)?; + let value = self.body().eval(vm)?; output = ops::join(output, value) .at(self.body().span())?; } - ctx.scopes.exit(); + vm.scopes.exit(); return Ok(output); }}; } - let iter = self.iter().eval(ctx)?; + let iter = self.iter().eval(vm)?; let pattern = self.pattern(); let key = pattern.key().map(Ident::take); let value = pattern.value().take(); @@ -763,22 +672,21 @@ impl Eval for ForExpr { impl Eval for ImportExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - let path = self.path(); - let resolved = path.eval(ctx)?.cast::<EcoString>().at(path.span())?; - let file = ctx.import(&resolved, path.span())?; - let module = &ctx.modules[&file]; + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + let span = self.path().span(); + let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?; + let module = import(vm, &path, span)?; match self.imports() { Imports::Wildcard => { for (var, slot) in module.scope.iter() { - ctx.scopes.def_mut(var, slot.read().unwrap().clone()); + vm.scopes.top.def_mut(var, slot.read().unwrap().clone()); } } Imports::Items(idents) => { for ident in idents { if let Some(slot) = module.scope.get(&ident) { - ctx.scopes.def_mut(ident.take(), slot.read().unwrap().clone()); + vm.scopes.top.def_mut(ident.take(), slot.read().unwrap().clone()); } else { bail!(ident.span(), "unresolved import"); } @@ -793,19 +701,38 @@ impl Eval for ImportExpr { impl Eval for IncludeExpr { type Output = Value; - fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { - let path = self.path(); - let resolved = path.eval(ctx)?.cast::<EcoString>().at(path.span())?; - let file = ctx.import(&resolved, path.span())?; - let module = &ctx.modules[&file]; + fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + let span = self.path().span(); + let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?; + let module = import(vm, &path, span)?; Ok(Value::Template(module.template.clone())) } } +/// Process an import of a module relative to the current location. +fn import(vm: &mut Vm, path: &str, span: Span) -> TypResult<Module> { + // Load the source file. + let full = vm.resolve(path); + let id = vm.sources.load(&full).map_err(|err| { + Error::boxed(span, match err.kind() { + io::ErrorKind::NotFound => "file not found".into(), + _ => format!("failed to load source file ({})", err), + }) + })?; + + // Prevent cyclic importing. + if vm.route.contains(&id) { + bail!(span, "cyclic import"); + } + + // Evaluate the file. + vm.evaluate(id).trace(|| Tracepoint::Import, span) +} + impl Eval for BreakExpr { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Err("break is not yet implemented").at(self.span()) } } @@ -813,7 +740,7 @@ impl Eval for BreakExpr { impl Eval for ContinueExpr { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Err("continue is not yet implemented").at(self.span()) } } @@ -821,7 +748,7 @@ impl Eval for ContinueExpr { impl Eval for ReturnExpr { type Output = Value; - fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { Err("return is not yet implemented").at(self.span()) } } @@ -831,25 +758,25 @@ impl Eval for ReturnExpr { /// This only works if the expression is a valid lvalue. pub trait Access { /// Try to access the value. - fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()>; + fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()>; } /// Process an accessed value. type Handler<'a> = Box<dyn FnOnce(&mut Value) -> TypResult<()> + 'a>; impl Access for Expr { - fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()> { + fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()> { match self { - Expr::Ident(ident) => ident.access(ctx, f), - Expr::Call(call) => call.access(ctx, f), + Expr::Ident(ident) => ident.access(vm, f), + Expr::Call(call) => call.access(vm, f), _ => bail!(self.span(), "cannot access this expression mutably"), } } } impl Access for Ident { - fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()> { - match ctx.scopes.get(self) { + fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()> { + match vm.scopes.get(self) { Some(slot) => match slot.try_write() { Ok(mut guard) => f(&mut guard), Err(_) => bail!(self.span(), "cannot mutate a constant"), @@ -860,10 +787,10 @@ impl Access for Ident { } impl Access for CallExpr { - fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()> { - let args = self.args().eval(ctx)?; + fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()> { + let args = self.args().eval(vm)?; self.callee().access( - ctx, + vm, Box::new(|value| match value { Value::Array(array) => { f(array.get_mut(args.into_index()?).at(self.span())?) diff --git a/src/eval/scope.rs b/src/eval/scope.rs index ed4ff70d..0df4c4df 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher}; use std::iter; use std::sync::{Arc, RwLock}; -use super::{Args, Class, Construct, EvalContext, Func, Set, Value}; +use super::{Args, Class, Construct, Func, Set, Value, Vm}; use crate::diag::TypResult; use crate::util::EcoString; @@ -40,21 +40,6 @@ impl<'a> Scopes<'a> { self.top = self.scopes.pop().expect("no pushed scope"); } - /// Define a constant variable with a value in the active scope. - pub fn def_const(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) { - self.top.def_const(var, value); - } - - /// Define a mutable variable with a value in the active scope. - pub fn def_mut(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) { - self.top.def_mut(var, value); - } - - /// Define a variable with a slot in the active scope. - pub fn def_slot(&mut self, var: impl Into<EcoString>, slot: Slot) { - self.top.def_slot(var, slot); - } - /// Look up the slot of a variable. pub fn get(&self, var: &str) -> Option<&Slot> { iter::once(&self.top) @@ -101,7 +86,7 @@ impl Scope { pub fn def_func( &mut self, name: &'static str, - func: fn(&mut EvalContext, &mut Args) -> TypResult<Value>, + func: fn(&mut Vm, &mut Args) -> TypResult<Value>, ) { self.def_const(name, Func::native(name, func)); } diff --git a/src/eval/template.rs b/src/eval/template.rs index f953287d..2cd6797a 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -17,7 +17,6 @@ use crate::library::{ TextNode, UNDERLINE, }; use crate::util::EcoString; -use crate::Context; /// Composable representation of styled content. /// @@ -166,19 +165,19 @@ impl Template { } /// Layout this template into a collection of pages. - pub fn layout(&self, ctx: &mut Context) -> Vec<Arc<Frame>> { + pub fn layout(&self, vm: &mut Vm) -> Vec<Arc<Frame>> { let style_arena = Arena::new(); let template_arena = Arena::new(); - let (mut ctx, styles) = LayoutContext::new(ctx); let mut builder = Builder::new(&style_arena, &template_arena, true); - builder.process(self, styles); - builder.finish_page(true, false, styles); + let chain = StyleChain::new(vm.styles); + builder.process(self, chain); + builder.finish_page(true, false, chain); let (pages, shared) = builder.pages.unwrap().finish(); pages .iter() - .flat_map(|(page, map)| page.layout(&mut ctx, map.chain(&shared))) + .flat_map(|(page, map)| page.layout(vm, map.chain(&shared))) .collect() } } @@ -267,7 +266,7 @@ impl Sum for Template { impl Layout for Template { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -279,7 +278,7 @@ impl Layout for Template { builder.finish_par(styles); let (flow, shared) = builder.flow.finish(); - FlowNode(flow).layout(ctx, regions, shared) + FlowNode(flow).layout(vm, regions, shared) } fn pack(self) -> LayoutNode { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index c334433e..d47e37bb 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -16,13 +16,11 @@ use std::hash::{Hash, Hasher}; use std::sync::Arc; use crate::eval::StyleChain; -use crate::font::FontStore; use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform}; -use crate::image::ImageStore; use crate::library::{AlignNode, PadNode, TransformNode, MOVE}; use crate::util::Prehashed; -use crate::Context; +use crate::Vm; /// A node that can be layouted into a sequence of regions. /// @@ -32,7 +30,7 @@ pub trait Layout { /// Layout the node into the given regions, producing constrained frames. fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>>; @@ -46,35 +44,6 @@ pub trait Layout { } } -/// The context for layouting. -pub struct LayoutContext<'a> { - /// Stores parsed font faces. - pub fonts: &'a mut FontStore, - /// Stores decoded images. - pub images: &'a mut ImageStore, - /// Caches layouting artifacts. - #[cfg(feature = "layout-cache")] - pub layout_cache: &'a mut LayoutCache, - /// How deeply nested the current layout tree position is. - #[cfg(feature = "layout-cache")] - level: usize, -} - -impl<'a> LayoutContext<'a> { - /// Create a new layout context and style chain. - pub fn new(ctx: &'a mut Context) -> (Self, StyleChain<'a>) { - let this = Self { - fonts: &mut ctx.fonts, - images: &mut ctx.images, - #[cfg(feature = "layout-cache")] - layout_cache: &mut ctx.layout_cache, - #[cfg(feature = "layout-cache")] - level: 0, - }; - (this, StyleChain::new(&ctx.styles)) - } -} - /// A type-erased layouting node with a precomputed hash. #[derive(Clone, Hash)] pub struct LayoutNode(Arc<Prehashed<dyn Bounds>>); @@ -165,7 +134,7 @@ impl Layout for LayoutNode { #[track_caller] fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -185,14 +154,14 @@ impl Layout for LayoutNode { // This is not written with `unwrap_or_else`, because then the // #[track_caller] annotation doesn't work. #[cfg(feature = "layout-cache")] - if let Some(frames) = ctx.layout_cache.get(hash, regions) { + if let Some(frames) = vm.layout_cache.get(hash, regions) { frames } else { - ctx.level += 1; - let frames = self.0.layout(ctx, regions, styles); - ctx.level -= 1; + vm.level += 1; + let frames = self.0.layout(vm, regions, styles); + vm.level -= 1; - let entry = FramesEntry::new(frames.clone(), ctx.level); + let entry = FramesEntry::new(frames.clone(), vm.level); #[cfg(debug_assertions)] if !entry.check(regions) { @@ -205,7 +174,7 @@ impl Layout for LayoutNode { panic!("constraints did not match regions they were created for"); } - ctx.layout_cache.insert(hash, entry); + vm.layout_cache.insert(hash, entry); frames } } @@ -276,7 +245,7 @@ struct EmptyNode; impl Layout for EmptyNode { fn layout( &self, - _: &mut LayoutContext, + _: &mut Vm, regions: &Regions, _: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -299,7 +268,7 @@ struct SizedNode { impl Layout for SizedNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -323,7 +292,7 @@ impl Layout for SizedNode { Regions::one(size, base, expand) }; - let mut frames = self.child.layout(ctx, &pod, styles); + let mut frames = self.child.layout(vm, &pod, styles); let Constrained { item: frame, cts } = &mut frames[0]; // Ensure frame size matches regions size if expansion is on. @@ -353,11 +322,11 @@ struct FillNode { impl Layout for FillNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { - let mut frames = self.child.layout(ctx, regions, styles); + let mut frames = self.child.layout(vm, regions, styles); for Constrained { item: frame, .. } in &mut frames { let shape = Shape::filled(Geometry::Rect(frame.size), self.fill); Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); @@ -378,11 +347,11 @@ struct StrokeNode { impl Layout for StrokeNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { - let mut frames = self.child.layout(ctx, regions, styles); + let mut frames = self.child.layout(vm, regions, styles); for Constrained { item: frame, .. } in &mut frames { let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke); Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); @@ -22,7 +22,7 @@ //! [parsed]: parse::parse //! [green tree]: syntax::GreenNode //! [AST]: syntax::ast -//! [evaluate]: Context::evaluate +//! [evaluate]: Vm::evaluate //! [module]: eval::Module //! [template]: eval::Template //! [layouted]: eval::Template::layout @@ -51,10 +51,12 @@ pub mod parse; pub mod source; pub mod syntax; +use std::collections::HashMap; +use std::path::PathBuf; use std::sync::Arc; use crate::diag::TypResult; -use crate::eval::{Eval, EvalContext, Module, Scope, StyleMap}; +use crate::eval::{Eval, Module, Scope, Scopes, StyleMap}; use crate::export::RenderCache; use crate::font::FontStore; use crate::frame::Frame; @@ -106,27 +108,13 @@ impl Context { &self.styles } - /// Evaluate a source file and return the resulting module. - /// - /// Returns either a module containing a scope with top-level bindings and a - /// layoutable template or diagnostics in the form of a vector of error - /// message with file and span information. - pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> { - let markup = self.sources.get(id).ast()?; - let mut ctx = EvalContext::new(self, id); - let template = markup.eval(&mut ctx)?; - Ok(Module { scope: ctx.scopes.top, template }) - } - /// Typeset a source file into a collection of layouted frames. /// /// Returns either a vector of frames representing individual pages or /// diagnostics in the form of a vector of error message with file and span /// information. pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> { - let module = self.evaluate(id)?; - let frames = module.template.layout(self); - Ok(frames) + Vm::new(self).typeset(id) } /// Garbage-collect caches. @@ -208,3 +196,107 @@ impl Default for ContextBuilder { } } } + +/// A virtual machine for a single typesetting process. +pub struct Vm<'a> { + /// The loader the context was created with. + pub loader: &'a dyn Loader, + /// Stores loaded source files. + pub sources: &'a mut SourceStore, + /// Stores parsed font faces. + pub fonts: &'a mut FontStore, + /// Stores decoded images. + pub images: &'a mut ImageStore, + /// Caches layouting artifacts. + #[cfg(feature = "layout-cache")] + pub layout_cache: &'a mut LayoutCache, + /// The default styles. + pub styles: &'a StyleMap, + /// The stack of imported files that led to evaluation of the current file. + pub route: Vec<SourceId>, + /// Caches imported modules. + pub modules: HashMap<SourceId, Module>, + /// The active scopes. + pub scopes: Scopes<'a>, + /// How deeply nested the current layout tree position is. + #[cfg(feature = "layout-cache")] + pub level: usize, +} + +impl<'a> Vm<'a> { + /// Create a new virtual machine. + pub fn new(ctx: &'a mut Context) -> Self { + Self { + loader: ctx.loader.as_ref(), + sources: &mut ctx.sources, + fonts: &mut ctx.fonts, + images: &mut ctx.images, + layout_cache: &mut ctx.layout_cache, + styles: &ctx.styles, + route: vec![], + modules: HashMap::new(), + scopes: Scopes::new(Some(&ctx.std)), + level: 0, + } + } + + /// Evaluate a source file and return the resulting module. + /// + /// Returns either a module containing a scope with top-level bindings and a + /// layoutable template or diagnostics in the form of a vector of error + /// message with file and span information. + pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> { + // Prevent cyclic evaluation. + assert!(!self.route.contains(&id)); + + // Check whether the module was already loaded. + if let Some(module) = self.modules.get(&id) { + return Ok(module.clone()); + } + + // Parse the file. + let source = self.sources.get(id); + let ast = source.ast()?; + + // Prepare the new context. + let fresh = Scopes::new(self.scopes.base); + let prev = std::mem::replace(&mut self.scopes, fresh); + self.route.push(id); + + // Evaluate the module. + let template = ast.eval(self)?; + + // Restore the old context. + let scope = std::mem::replace(&mut self.scopes, prev).top; + self.route.pop().unwrap(); + + // Save the evaluated module. + let module = Module { scope, template }; + self.modules.insert(id, module.clone()); + + Ok(module) + } + + /// Typeset a source file into a collection of layouted frames. + /// + /// Returns either a vector of frames representing individual pages or + /// diagnostics in the form of a vector of error message with file and span + /// information. + pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> { + let module = self.evaluate(id)?; + let frames = module.template.layout(self); + Ok(frames) + } + + /// Resolve a user-entered path (relative to the source file) to be + /// relative to the compilation environment's root. + pub fn resolve(&self, path: &str) -> PathBuf { + if let Some(&id) = self.route.last() { + if let Some(dir) = self.sources.get(id).path().parent() { + return dir.join(path); + } + } + + path.into() + } +} diff --git a/src/library/align.rs b/src/library/align.rs index 1fb91138..5b50937e 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -14,7 +14,7 @@ pub struct AlignNode { #[class] impl AlignNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let aligns: Spec<_> = args.find()?.unwrap_or_default(); let body: LayoutNode = args.expect("body")?; Ok(Template::block(body.aligned(aligns))) @@ -24,7 +24,7 @@ impl AlignNode { impl Layout for AlignNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -39,7 +39,7 @@ impl Layout for AlignNode { } // Layout the child. - let mut frames = self.child.layout(ctx, &pod, passed.chain(&styles)); + let mut frames = self.child.layout(vm, &pod, passed.chain(&styles)); for ((current, base), Constrained { item: frame, cts }) in regions.iter().zip(&mut frames) diff --git a/src/library/columns.rs b/src/library/columns.rs index 98989c5a..61da6177 100644 --- a/src/library/columns.rs +++ b/src/library/columns.rs @@ -18,7 +18,7 @@ impl ColumnsNode { /// The size of the gutter space between each column. pub const GUTTER: Linear = Relative::new(0.04).into(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::block(Self { columns: args.expect("column count")?, child: args.expect("body")?, @@ -29,14 +29,14 @@ impl ColumnsNode { impl Layout for ColumnsNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { // Separating the infinite space into infinite columns does not make // much sense. if regions.current.x.is_infinite() { - return self.child.layout(ctx, regions, styles); + return self.child.layout(vm, regions, styles); } // Determine the width of the gutter and each column. @@ -59,7 +59,7 @@ impl Layout for ColumnsNode { }; // Layout the children. - let mut frames = self.child.layout(ctx, &pod, styles).into_iter(); + let mut frames = self.child.layout(vm, &pod, styles).into_iter(); let dir = styles.get(ParNode::DIR); let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize; @@ -111,7 +111,7 @@ pub struct ColbreakNode; #[class] impl ColbreakNode { - fn construct(_: &mut EvalContext, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { Ok(Template::Colbreak) } } diff --git a/src/library/container.rs b/src/library/container.rs index 382be262..fafa7103 100644 --- a/src/library/container.rs +++ b/src/library/container.rs @@ -7,7 +7,7 @@ pub struct BoxNode; #[class] impl BoxNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let width = args.named("width")?; let height = args.named("height")?; let body: LayoutNode = args.find()?.unwrap_or_default(); @@ -20,7 +20,7 @@ pub struct BlockNode; #[class] impl BlockNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::Block(args.find()?.unwrap_or_default())) } } diff --git a/src/library/deco.rs b/src/library/deco.rs index 5e438d54..92fcf09f 100644 --- a/src/library/deco.rs +++ b/src/library/deco.rs @@ -26,7 +26,7 @@ impl<const L: DecoLine> DecoNode<L> { /// with the glyphs. Does not apply to strikethrough. pub const EVADE: bool = true; - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self(args.expect::<Template>("body")?))) } } diff --git a/src/library/flow.rs b/src/library/flow.rs index ca554033..95469b65 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -28,7 +28,7 @@ pub enum FlowChild { impl Layout for FlowNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -56,7 +56,7 @@ impl Layout for FlowNode { layouter.layout_spacing(*kind); } FlowChild::Node(ref node) => { - layouter.layout_node(ctx, node, styles); + layouter.layout_node(vm, node, styles); } } } @@ -161,12 +161,7 @@ impl FlowLayouter { } /// Layout a node. - pub fn layout_node( - &mut self, - ctx: &mut LayoutContext, - node: &LayoutNode, - styles: StyleChain, - ) { + pub fn layout_node(&mut self, vm: &mut Vm, node: &LayoutNode, styles: StyleChain) { // Don't even try layouting into a full region. if self.regions.is_full() { self.finish_region(); @@ -176,7 +171,7 @@ impl FlowLayouter { // aligned later. if let Some(placed) = node.downcast::<PlaceNode>() { if placed.out_of_flow() { - let frame = node.layout(ctx, &self.regions, styles).remove(0); + let frame = node.layout(vm, &self.regions, styles).remove(0); self.items.push(FlowItem::Placed(frame.item)); return; } @@ -193,7 +188,7 @@ impl FlowLayouter { .unwrap_or(Align::Top), ); - let frames = node.layout(ctx, &self.regions, styles); + let frames = node.layout(vm, &self.regions, styles); let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. diff --git a/src/library/grid.rs b/src/library/grid.rs index c9904a5f..78ac375f 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -15,7 +15,7 @@ pub struct GridNode { #[class] impl GridNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let columns = args.named("columns")?.unwrap_or_default(); let rows = args.named("rows")?.unwrap_or_default(); let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default(); @@ -35,7 +35,7 @@ impl GridNode { impl Layout for GridNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -49,7 +49,7 @@ impl Layout for GridNode { ); // Measure the columsna nd layout the grid row-by-row. - layouter.layout(ctx) + layouter.layout(vm) } } @@ -205,19 +205,19 @@ impl<'a> GridLayouter<'a> { } /// Determines the columns sizes and then layouts the grid row-by-row. - pub fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Arc<Frame>>> { - self.measure_columns(ctx); + pub fn layout(mut self, vm: &mut Vm) -> Vec<Constrained<Arc<Frame>>> { + self.measure_columns(vm); for y in 0 .. self.rows.len() { // Skip to next region if current one is full, but only for content // rows, not for gutter rows. if y % 2 == 0 && self.regions.is_full() { - self.finish_region(ctx); + self.finish_region(vm); } match self.rows[y] { - TrackSizing::Auto => self.layout_auto_row(ctx, y), - TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y), + TrackSizing::Auto => self.layout_auto_row(vm, y), + TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y), TrackSizing::Fractional(v) => { self.cts.exact.y = Some(self.full); self.lrows.push(Row::Fr(v, y)); @@ -226,12 +226,12 @@ impl<'a> GridLayouter<'a> { } } - self.finish_region(ctx); + self.finish_region(vm); self.finished } /// Determine all column sizes. - fn measure_columns(&mut self, ctx: &mut LayoutContext) { + fn measure_columns(&mut self, vm: &mut Vm) { enum Case { /// The column sizing is only determined by specified linear sizes. PurelyLinear, @@ -277,7 +277,7 @@ impl<'a> GridLayouter<'a> { let available = self.regions.current.x - linear; if available >= Length::zero() { // Determine size of auto columns. - let (auto, count) = self.measure_auto_columns(ctx, available); + let (auto, count) = self.measure_auto_columns(vm, available); // If there is remaining space, distribute it to fractional columns, // otherwise shrink auto columns. @@ -313,7 +313,7 @@ impl<'a> GridLayouter<'a> { /// Measure the size that is available to auto columns. fn measure_auto_columns( &mut self, - ctx: &mut LayoutContext, + vm: &mut Vm, available: Length, ) -> (Length, usize) { let mut auto = Length::zero(); @@ -340,7 +340,7 @@ impl<'a> GridLayouter<'a> { pod.base.y = v.resolve(self.regions.base.y); } - let frame = node.layout(ctx, &pod, self.styles).remove(0).item; + let frame = node.layout(vm, &pod, self.styles).remove(0).item; resolved.set_max(frame.size.x); } } @@ -394,7 +394,7 @@ impl<'a> GridLayouter<'a> { /// Layout a row with automatic height. Such a row may break across multiple /// regions. - fn layout_auto_row(&mut self, ctx: &mut LayoutContext, y: usize) { + fn layout_auto_row(&mut self, vm: &mut Vm, y: usize) { let mut resolved: Vec<Length> = vec![]; // Determine the size for each region of the row. @@ -409,7 +409,7 @@ impl<'a> GridLayouter<'a> { } let mut sizes = node - .layout(ctx, &pod, self.styles) + .layout(vm, &pod, self.styles) .into_iter() .map(|frame| frame.item.size.y); @@ -432,7 +432,7 @@ impl<'a> GridLayouter<'a> { // Layout into a single region. if let &[first] = resolved.as_slice() { - let frame = self.layout_single_row(ctx, first, y); + let frame = self.layout_single_row(vm, first, y); self.push_row(frame); return; } @@ -449,28 +449,28 @@ impl<'a> GridLayouter<'a> { } // Layout into multiple regions. - let frames = self.layout_multi_row(ctx, &resolved, y); + let frames = self.layout_multi_row(vm, &resolved, y); let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { self.push_row(frame); if i + 1 < len { self.cts.exact.y = Some(self.full); - self.finish_region(ctx); + self.finish_region(vm); } } } /// Layout a row with linear height. Such a row cannot break across multiple /// regions, but it may force a region break. - fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) { + fn layout_linear_row(&mut self, vm: &mut Vm, v: Linear, y: usize) { let resolved = v.resolve(self.regions.base.y); - let frame = self.layout_single_row(ctx, resolved, y); + let frame = self.layout_single_row(vm, resolved, y); // Skip to fitting region. let height = frame.size.y; while !self.regions.current.y.fits(height) && !self.regions.in_last() { self.cts.max.y = Some(self.used.y + height); - self.finish_region(ctx); + self.finish_region(vm); // Don't skip multiple regions for gutter and don't push a row. if y % 2 == 1 { @@ -484,7 +484,7 @@ impl<'a> GridLayouter<'a> { /// Layout a row with fixed height and return its frame. fn layout_single_row( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, height: Length, y: usize, ) -> Frame { @@ -502,7 +502,7 @@ impl<'a> GridLayouter<'a> { .select(self.regions.base, size); let pod = Regions::one(size, base, Spec::splat(true)); - let frame = node.layout(ctx, &pod, self.styles).remove(0); + let frame = node.layout(vm, &pod, self.styles).remove(0); output.push_frame(pos, frame.item); } @@ -515,7 +515,7 @@ impl<'a> GridLayouter<'a> { /// Layout a row spanning multiple regions. fn layout_multi_row( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, heights: &[Length], y: usize, ) -> Vec<Frame> { @@ -542,7 +542,7 @@ impl<'a> GridLayouter<'a> { } // Push the layouted frames into the individual output frames. - let frames = node.layout(ctx, &pod, self.styles); + let frames = node.layout(vm, &pod, self.styles); for (output, frame) in outputs.iter_mut().zip(frames) { output.push_frame(pos, frame.item); } @@ -562,7 +562,7 @@ impl<'a> GridLayouter<'a> { } /// Finish rows for one region. - fn finish_region(&mut self, ctx: &mut LayoutContext) { + fn finish_region(&mut self, vm: &mut Vm) { // Determine the size of the grid in this region, expanding fully if // there are fr rows. let mut size = self.used; @@ -584,7 +584,7 @@ impl<'a> GridLayouter<'a> { Row::Fr(v, y) => { let remaining = self.full - self.used.y; let height = v.resolve(self.fr, remaining); - self.layout_single_row(ctx, height, y) + self.layout_single_row(vm, height, y) } }; diff --git a/src/library/heading.rs b/src/library/heading.rs index 00a48021..2123116b 100644 --- a/src/library/heading.rs +++ b/src/library/heading.rs @@ -34,7 +34,7 @@ impl HeadingNode { /// The extra padding below the heading. pub const BELOW: Length = Length::zero(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { body: args.expect("body")?, level: args.named("level")?.unwrap_or(1), diff --git a/src/library/hide.rs b/src/library/hide.rs index 72d8e52b..230300d2 100644 --- a/src/library/hide.rs +++ b/src/library/hide.rs @@ -8,7 +8,7 @@ pub struct HideNode(pub LayoutNode); #[class] impl HideNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::inline(Self(args.expect("body")?))) } } @@ -16,11 +16,11 @@ impl HideNode { impl Layout for HideNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { - let mut frames = self.0.layout(ctx, regions, styles); + let mut frames = self.0.layout(vm, regions, styles); // Clear the frames. for Constrained { item: frame, .. } in &mut frames { diff --git a/src/library/image.rs b/src/library/image.rs index 59e30364..5527fbdc 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -14,10 +14,10 @@ impl ImageNode { /// How the image should adjust itself to a given area. pub const FIT: ImageFit = ImageFit::Cover; - fn construct(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(vm: &mut Vm, args: &mut Args) -> TypResult<Template> { let path = args.expect::<Spanned<EcoString>>("path to image file")?; - let full = ctx.make_path(&path.v); - let id = ctx.images.load(&full).map_err(|err| { + let full = vm.resolve(&path.v); + let id = vm.images.load(&full).map_err(|err| { Error::boxed(path.span, match err.kind() { std::io::ErrorKind::NotFound => "file not found".into(), _ => format!("failed to load image ({})", err), @@ -36,11 +36,11 @@ impl ImageNode { impl Layout for ImageNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { - let img = ctx.images.get(self.0); + let img = vm.images.get(self.0); let pxw = img.width() as f64; let pxh = img.height() as f64; let px_ratio = pxw / pxh; diff --git a/src/library/link.rs b/src/library/link.rs index 42c630f6..58c7e550 100644 --- a/src/library/link.rs +++ b/src/library/link.rs @@ -21,7 +21,7 @@ impl LinkNode { /// Whether to underline link. pub const UNDERLINE: bool = true; - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let url = args.expect::<EcoString>("url")?; let body = args.find()?.unwrap_or_else(|| { let mut text = url.as_str(); diff --git a/src/library/list.rs b/src/library/list.rs index e07177f4..7f76a178 100644 --- a/src/library/list.rs +++ b/src/library/list.rs @@ -19,7 +19,7 @@ impl<const L: Labelling> ListNode<L> { /// The space between the label and the body of each item. pub const BODY_INDENT: Linear = Relative::new(0.5).into(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(args .all()? .into_iter() diff --git a/src/library/math.rs b/src/library/math.rs index c75cdea8..29367cd5 100644 --- a/src/library/math.rs +++ b/src/library/math.rs @@ -13,7 +13,7 @@ pub struct MathNode { #[class] impl MathNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { formula: args.expect("formula")?, display: args.named("display")?.unwrap_or(false), diff --git a/src/library/mod.rs b/src/library/mod.rs index db173daf..d90c96da 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -64,16 +64,17 @@ pub mod prelude { pub use crate::diag::{At, TypResult}; pub use crate::eval::{ - Args, Construct, EvalContext, Merge, Property, Scope, Set, Show, ShowNode, Smart, - StyleChain, StyleMap, StyleVec, Template, Value, + Args, Construct, Merge, Property, Scope, Set, Show, ShowNode, Smart, StyleChain, + StyleMap, StyleVec, Template, Value, }; pub use crate::frame::*; pub use crate::geom::*; pub use crate::layout::{ - Constrain, Constrained, Constraints, Layout, LayoutContext, LayoutNode, Regions, + Constrain, Constrained, Constraints, Layout, LayoutNode, Regions, }; pub use crate::syntax::{Span, Spanned}; pub use crate::util::{EcoString, OptionExt}; + pub use crate::Vm; } use prelude::*; diff --git a/src/library/pad.rs b/src/library/pad.rs index 684e1172..84e83939 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -13,7 +13,7 @@ pub struct PadNode { #[class] impl PadNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let all = args.find()?; let left = args.named("left")?; let top = args.named("top")?; @@ -34,13 +34,13 @@ impl PadNode { impl Layout for PadNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { // Layout child into padded regions. let pod = regions.map(|size| shrink(size, self.padding)); - let mut frames = self.child.layout(ctx, &pod, styles); + let mut frames = self.child.layout(vm, &pod, styles); for ((current, base), Constrained { item: frame, cts }) in regions.iter().zip(&mut frames) diff --git a/src/library/page.rs b/src/library/page.rs index 7f0f7332..155f60dd 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -31,7 +31,7 @@ impl PageNode { /// How many columns the page has. pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::Page(Self(args.expect("body")?))) } @@ -60,7 +60,7 @@ impl PageNode { impl PageNode { /// Layout the page run into a sequence of frames, one per page. - pub fn layout(&self, ctx: &mut LayoutContext, styles: StyleChain) -> Vec<Arc<Frame>> { + pub fn layout(&self, vm: &mut Vm, styles: StyleChain) -> Vec<Arc<Frame>> { // When one of the lengths is infinite the page fits its content along // that axis. let width = styles.get(Self::WIDTH).unwrap_or(Length::inf()); @@ -104,7 +104,7 @@ impl PageNode { let expand = size.map(Length::is_finite); let regions = Regions::repeat(size, size, expand); child - .layout(ctx, ®ions, styles) + .layout(vm, ®ions, styles) .into_iter() .map(|c| c.item) .collect() @@ -124,7 +124,7 @@ pub struct PagebreakNode; #[class] impl PagebreakNode { - fn construct(_: &mut EvalContext, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { Ok(Template::Pagebreak) } } diff --git a/src/library/par.rs b/src/library/par.rs index 2a4b4d08..02633612 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -36,7 +36,7 @@ impl ParNode { /// The extra spacing between paragraphs (dependent on scaled font size). pub const SPACING: Linear = Relative::new(0.55).into(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { // The paragraph constructor is special: It doesn't create a paragraph // since that happens automatically through markup. Instead, it just // lifts the passed body to the block level so that it won't merge with @@ -82,7 +82,7 @@ impl ParNode { impl Layout for ParNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -95,10 +95,10 @@ impl Layout for ParNode { // Prepare paragraph layout by building a representation on which we can // do line breaking without layouting each and every line from scratch. - let layouter = ParLayouter::new(self, ctx, regions, &styles, bidi); + let layouter = ParLayouter::new(self, vm, regions, &styles, bidi); // Find suitable linebreaks. - layouter.layout(ctx, regions.clone()) + layouter.layout(vm, regions.clone()) } } @@ -167,7 +167,7 @@ pub struct ParbreakNode; #[class] impl ParbreakNode { - fn construct(_: &mut EvalContext, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { Ok(Template::Parbreak) } } @@ -177,7 +177,7 @@ pub struct LinebreakNode; #[class] impl LinebreakNode { - fn construct(_: &mut EvalContext, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { Ok(Template::Linebreak) } } @@ -216,7 +216,7 @@ impl<'a> ParLayouter<'a> { /// Prepare initial shaped text and layouted children. fn new( par: &'a ParNode, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: &'a StyleChain<'a>, bidi: BidiInfo<'a>, @@ -236,7 +236,7 @@ impl<'a> ParLayouter<'a> { cursor += count; let subrange = start .. cursor; let text = &bidi.text[subrange.clone()]; - let shaped = shape(ctx.fonts, text, styles, level.dir()); + let shaped = shape(vm.fonts, text, styles, level.dir()); items.push(ParItem::Text(shaped)); ranges.push(subrange); } @@ -255,7 +255,7 @@ impl<'a> ParLayouter<'a> { ParChild::Node(node) => { let size = Size::new(regions.current.x, regions.base.y); let pod = Regions::one(size, regions.base, Spec::splat(false)); - let frame = node.layout(ctx, &pod, styles).remove(0); + let frame = node.layout(vm, &pod, styles).remove(0); items.push(ParItem::Frame(Arc::take(frame.item))); ranges.push(range); } @@ -270,11 +270,7 @@ impl<'a> ParLayouter<'a> { } /// Find first-fit line breaks and build the paragraph. - fn layout( - self, - ctx: &mut LayoutContext, - regions: Regions, - ) -> Vec<Constrained<Arc<Frame>>> { + fn layout(self, vm: &mut Vm, regions: Regions) -> Vec<Constrained<Arc<Frame>>> { let mut stack = LineStack::new(self.leading, regions); // The current line attempt. @@ -288,7 +284,7 @@ impl<'a> ParLayouter<'a> { // TODO: Provide line break opportunities on alignment changes. for (end, mandatory) in LineBreakIterator::new(self.bidi.text) { // Compute the line and its size. - let mut line = LineLayout::new(ctx, &self, start .. end); + let mut line = LineLayout::new(vm, &self, start .. end); // If the line doesn't fit anymore, we push the last fitting attempt // into the stack and rebuild the line from its end. The resulting @@ -318,7 +314,7 @@ impl<'a> ParLayouter<'a> { stack.push(last_line); stack.cts.min.y = Some(stack.size.y); start = last_end; - line = LineLayout::new(ctx, &self, start .. end); + line = LineLayout::new(vm, &self, start .. end); } } } @@ -335,7 +331,7 @@ impl<'a> ParLayouter<'a> { // below. let too_large = stack.size.y + self.leading + line.size.y; stack.cts.max.y.set_min(too_large); - stack.finish_region(ctx); + stack.finish_region(vm); } // If the line does not fit horizontally or we have a mandatory @@ -350,7 +346,7 @@ impl<'a> ParLayouter<'a> { // If there is a trailing line break at the end of the // paragraph, we want to force an empty line. if mandatory && end == self.bidi.text.len() { - let line = LineLayout::new(ctx, &self, end .. end); + let line = LineLayout::new(vm, &self, end .. end); if stack.regions.current.y.fits(line.size.y) { stack.push(line); } @@ -370,7 +366,7 @@ impl<'a> ParLayouter<'a> { stack.cts.min.y = Some(stack.size.y); } - stack.finish(ctx) + stack.finish(vm) } /// Find the index of the item whose range contains the `text_offset`. @@ -408,7 +404,7 @@ struct LineLayout<'a> { impl<'a> LineLayout<'a> { /// Create a line which spans the given range. - fn new(ctx: &mut LayoutContext, par: &'a ParLayouter<'a>, mut line: Range) -> Self { + fn new(vm: &mut Vm, par: &'a ParLayouter<'a>, mut line: Range) -> Self { // Find the items which bound the text range. let last_idx = par.find(line.end.saturating_sub(1)).unwrap(); let first_idx = if line.is_empty() { @@ -438,7 +434,7 @@ impl<'a> LineLayout<'a> { // empty string. if !range.is_empty() || rest.is_empty() { // Reshape that part. - let reshaped = shaped.reshape(ctx.fonts, range); + let reshaped = shaped.reshape(vm.fonts, range); last = Some(ParItem::Text(reshaped)); } @@ -459,7 +455,7 @@ impl<'a> LineLayout<'a> { // Reshape if necessary. if range.len() < shaped.text.len() { if !range.is_empty() { - let reshaped = shaped.reshape(ctx.fonts, range); + let reshaped = shaped.reshape(vm.fonts, range); first = Some(ParItem::Text(reshaped)); } @@ -504,7 +500,7 @@ impl<'a> LineLayout<'a> { } /// Build the line's frame. - fn build(&self, ctx: &LayoutContext, width: Length) -> Frame { + fn build(&self, ctx: &Vm, width: Length) -> Frame { let size = Size::new(self.size.x.max(width), self.size.y); let remaining = size.x - self.size.x; @@ -622,7 +618,7 @@ impl<'a> LineStack<'a> { } /// Finish the frame for one region. - fn finish_region(&mut self, ctx: &LayoutContext) { + fn finish_region(&mut self, ctx: &Vm) { if self.regions.expand.x || self.fractional { self.size.x = self.regions.current.x; self.cts.exact.x = Some(self.regions.current.x); @@ -653,7 +649,7 @@ impl<'a> LineStack<'a> { } /// Finish the last region and return the built frames. - fn finish(mut self, ctx: &LayoutContext) -> Vec<Constrained<Arc<Frame>>> { + fn finish(mut self, ctx: &Vm) -> Vec<Constrained<Arc<Frame>>> { self.finish_region(ctx); self.finished } diff --git a/src/library/place.rs b/src/library/place.rs index 52bbeb21..c2c9ac25 100644 --- a/src/library/place.rs +++ b/src/library/place.rs @@ -9,7 +9,7 @@ pub struct PlaceNode(pub LayoutNode); #[class] impl PlaceNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let aligns = args.find()?.unwrap_or(Spec::with_x(Some(Align::Left))); let tx = args.named("dx")?.unwrap_or_default(); let ty = args.named("dy")?.unwrap_or_default(); @@ -23,7 +23,7 @@ impl PlaceNode { impl Layout for PlaceNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -37,7 +37,7 @@ impl Layout for PlaceNode { Regions::one(regions.base, regions.base, expand) }; - let mut frames = self.0.layout(ctx, &pod, styles); + let mut frames = self.0.layout(vm, &pod, styles); let Constrained { item: frame, cts } = &mut frames[0]; // If expansion is off, zero all sizes so that we don't take up any diff --git a/src/library/raw.rs b/src/library/raw.rs index e2ae259b..45733fd0 100644 --- a/src/library/raw.rs +++ b/src/library/raw.rs @@ -31,7 +31,7 @@ impl RawNode { /// The language to syntax-highlight in. pub const LANG: Option<EcoString> = None; - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { text: args.expect("text")?, block: args.named("block")?.unwrap_or(false), diff --git a/src/library/shape.rs b/src/library/shape.rs index dd306f91..acb011ea 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -20,7 +20,7 @@ impl<const S: ShapeKind> ShapeNode<S> { /// How much to pad the shape's content. pub const PADDING: Linear = Linear::zero(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let size = match S { SQUARE => args.named::<Length>("size")?.map(Linear::from), CIRCLE => args.named::<Length>("radius")?.map(|r| 2.0 * Linear::from(r)), @@ -46,7 +46,7 @@ impl<const S: ShapeKind> ShapeNode<S> { impl<const S: ShapeKind> Layout for ShapeNode<S> { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -61,7 +61,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> { let child = child.clone().padded(Sides::splat(padding)); let mut pod = Regions::one(regions.current, regions.base, regions.expand); - frames = child.layout(ctx, &pod, styles); + frames = child.layout(vm, &pod, styles); // Relayout with full expansion into square region to make sure // the result is really a square or circle. @@ -77,7 +77,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> { pod.current = Size::splat(length); pod.expand = Spec::splat(true); - frames = child.layout(ctx, &pod, styles); + frames = child.layout(vm, &pod, styles); frames[0].cts = Constraints::tight(regions); } } else { diff --git a/src/library/spacing.rs b/src/library/spacing.rs index 3e26cb3c..f9676d4c 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -7,7 +7,7 @@ pub struct HNode; #[class] impl HNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::Horizontal(args.expect("spacing")?)) } } @@ -17,7 +17,7 @@ pub struct VNode; #[class] impl VNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::Vertical(args.expect("spacing")?)) } } diff --git a/src/library/stack.rs b/src/library/stack.rs index 88605a30..287731b8 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -16,7 +16,7 @@ pub struct StackNode { #[class] impl StackNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::block(Self { dir: args.named("dir")?.unwrap_or(Dir::TTB), spacing: args.named("spacing")?, @@ -28,7 +28,7 @@ impl StackNode { impl Layout for StackNode { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { @@ -48,7 +48,7 @@ impl Layout for StackNode { layouter.layout_spacing(kind); } - layouter.layout_node(ctx, node, styles); + layouter.layout_node(vm, node, styles); deferred = self.spacing; } } @@ -163,12 +163,7 @@ impl StackLayouter { } /// Layout an arbitrary node. - pub fn layout_node( - &mut self, - ctx: &mut LayoutContext, - node: &LayoutNode, - styles: StyleChain, - ) { + pub fn layout_node(&mut self, vm: &mut Vm, node: &LayoutNode, styles: StyleChain) { if self.regions.is_full() { self.finish_region(); } @@ -179,7 +174,7 @@ impl StackLayouter { .and_then(|node| node.aligns.get(self.axis)) .unwrap_or(self.dir.start().into()); - let frames = node.layout(ctx, &self.regions, styles); + let frames = node.layout(vm, &self.regions, styles); let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. diff --git a/src/library/table.rs b/src/library/table.rs index 3f30dd9e..dfefa21d 100644 --- a/src/library/table.rs +++ b/src/library/table.rs @@ -27,7 +27,7 @@ impl TableNode { /// How much to pad the cells's content. pub const PADDING: Linear = Length::pt(5.0).into(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let columns = args.named("columns")?.unwrap_or_default(); let rows = args.named("rows")?.unwrap_or_default(); let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default(); diff --git a/src/library/text.rs b/src/library/text.rs index 4ee64e7a..9fbc8605 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -103,7 +103,7 @@ impl TextNode { #[skip] pub const LINK: Option<EcoString> = None; - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { // The text constructor is special: It doesn't create a text node. // Instead, it leaves the passed argument structurally unchanged, but // styles all text in it. @@ -117,7 +117,7 @@ pub struct StrongNode(pub Template); #[class] impl StrongNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self(args.expect("body")?))) } } @@ -134,7 +134,7 @@ pub struct EmphNode(pub Template); #[class] impl EmphNode { - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self(args.expect("body")?))) } } diff --git a/src/library/transform.rs b/src/library/transform.rs index cc40921b..91d4b574 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -17,7 +17,7 @@ impl<const T: TransformKind> TransformNode<T> { /// The origin of the transformation. pub const ORIGIN: Spec<Option<Align>> = Spec::default(); - fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { let transform = match T { MOVE => { let tx = args.named("x")?.unwrap_or_default(); @@ -46,12 +46,12 @@ impl<const T: TransformKind> TransformNode<T> { impl<const T: TransformKind> Layout for TransformNode<T> { fn layout( &self, - ctx: &mut LayoutContext, + vm: &mut Vm, regions: &Regions, styles: StyleChain, ) -> Vec<Constrained<Arc<Frame>>> { let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); - let mut frames = self.child.layout(ctx, regions, styles); + let mut frames = self.child.layout(vm, regions, styles); for Constrained { item: frame, .. } in &mut frames { let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s)); diff --git a/src/library/utility.rs b/src/library/utility.rs index 3b9b8b04..91f0e3ad 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -7,7 +7,7 @@ use super::prelude::*; use crate::eval::Array; /// Ensure that a condition is fulfilled. -pub fn assert(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn assert(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?; if !v { bail!(span, "assertion failed"); @@ -16,17 +16,17 @@ pub fn assert(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// The name of a value's type. -pub fn type_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn type_(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<Value>("value")?.type_name().into()) } /// The string representation of a value. -pub fn repr(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn repr(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<Value>("value")?.repr().into()) } /// Join a sequence of values, optionally interspersing it with another value. -pub fn join(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn join(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let span = args.span; let sep = args.named::<Value>("sep")?.unwrap_or(Value::None); @@ -46,7 +46,7 @@ pub fn join(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Convert a value to a integer. -pub fn int(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn int(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect("value")?; Ok(Value::Int(match v { Value::Bool(v) => v as i64, @@ -61,7 +61,7 @@ pub fn int(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Convert a value to a float. -pub fn float(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn float(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect("value")?; Ok(Value::Float(match v { Value::Int(v) => v as f64, @@ -75,7 +75,7 @@ pub fn float(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Try to convert a value to a string. -pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn str(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect("value")?; Ok(Value::Str(match v { Value::Int(v) => format_eco!("{}", v), @@ -86,7 +86,7 @@ pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Create an RGB(A) color. -pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn rgb(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(Value::from( if let Some(string) = args.find::<Spanned<EcoString>>()? { match RgbaColor::from_str(&string.v) { @@ -120,7 +120,7 @@ pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Create a CMYK color. -pub fn cmyk(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn cmyk(_: &mut Vm, args: &mut Args) -> TypResult<Value> { struct Component(u8); castable! { @@ -141,7 +141,7 @@ pub fn cmyk(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// The absolute value of a numeric value. -pub fn abs(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn abs(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect("numeric value")?; Ok(match v { Value::Int(v) => Value::Int(v.abs()), @@ -156,27 +156,27 @@ pub fn abs(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// The minimum of a sequence of values. -pub fn min(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn min(_: &mut Vm, args: &mut Args) -> TypResult<Value> { minmax(args, Ordering::Less) } /// The maximum of a sequence of values. -pub fn max(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn max(_: &mut Vm, args: &mut Args) -> TypResult<Value> { minmax(args, Ordering::Greater) } /// Whether an integer is even. -pub fn even(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn even(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0)) } /// Whether an integer is odd. -pub fn odd(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn odd(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0)) } /// The modulo of two numbers. -pub fn modulo(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn modulo(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v: v1, span: span1 } = args.expect("integer or float")?; let Spanned { v: v2, span: span2 } = args.expect("integer or float")?; @@ -227,7 +227,7 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> { } /// Create a sequence of numbers. -pub fn range(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn range(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let first = args.expect::<i64>("end")?; let (start, end) = match args.eat::<i64>()? { Some(second) => (first, second), @@ -252,19 +252,19 @@ pub fn range(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Convert a string to lowercase. -pub fn lower(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn lower(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<EcoString>("string")?.to_lowercase().into()) } /// Convert a string to uppercase. -pub fn upper(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn upper(_: &mut Vm, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<EcoString>("string")?.to_uppercase().into()) } /// Converts an integer into a roman numeral. /// /// Works for integer between 0 and 3,999,999. -pub fn roman(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn roman(_: &mut Vm, args: &mut Args) -> TypResult<Value> { // Adapted from Yann Villessuzanne's roman.rs under the Unlicense, at // https://github.com/linfir/roman.rs/ static PAIRS: &'static [(&'static str, usize)] = &[ @@ -314,7 +314,7 @@ pub fn roman(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// Convert a number into a roman numeral. -pub fn symbol(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn symbol(_: &mut Vm, args: &mut Args) -> TypResult<Value> { static SYMBOLS: &'static [char] = &['*', '†', '‡', '§', '‖', '¶']; let n = args.expect::<usize>("non-negative integer")?; @@ -327,7 +327,7 @@ pub fn symbol(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// The length of a string, an array or a dictionary. -pub fn len(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn len(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect("collection")?; Ok(Value::Int(match v { Value::Str(v) => v.len() as i64, @@ -342,7 +342,7 @@ pub fn len(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } /// The sorted version of an array. -pub fn sorted(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { +pub fn sorted(_: &mut Vm, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect::<Spanned<Array>>("array")?; Ok(Value::Array(v.sorted().at(span)?)) } |
