diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-22 14:31:09 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-23 14:53:55 +0100 |
| commit | e1f29d6cb9437a4afb2e4fc4ee10a5b8717ab9fa (patch) | |
| tree | 1ce5f2bd858f6665d3867a2939d4b474c1b70377 | |
| parent | 2bf32c51bceb2f3a8b7ebea3d7c7d6d96757591b (diff) | |
Rework the core context
41 files changed, 626 insertions, 626 deletions
diff --git a/benches/oneshot.rs b/benches/oneshot.rs index 5dbf993f..9bce185c 100644 --- a/benches/oneshot.rs +++ b/benches/oneshot.rs @@ -5,7 +5,7 @@ use iai::{black_box, main, Iai}; use typst::loading::MemLoader; use typst::parse::{parse, Scanner, TokenMode, Tokens}; use typst::source::SourceId; -use typst::{Context, Vm}; +use typst::Context; const SRC: &str = include_str!("bench.typ"); const FONT: &[u8] = include_bytes!("../fonts/IBMPlexSans-Regular.ttf"); @@ -26,7 +26,6 @@ main!( bench_eval, bench_layout, bench_highlight, - bench_byte_to_utf16, bench_render, ); @@ -67,37 +66,21 @@ fn bench_edit(iai: &mut Iai) { iai.run(|| black_box(ctx.sources.edit(id, 1168 .. 1171, "_Uhr_"))); } -fn bench_eval(iai: &mut Iai) { - let (mut ctx, id) = context(); - let mut vm = Vm::new(&mut ctx); - iai.run(|| vm.evaluate(id).unwrap()); -} - -fn bench_layout(iai: &mut Iai) { - let (mut ctx, id) = context(); - let mut vm = Vm::new(&mut ctx); - let module = vm.evaluate(id).unwrap(); - iai.run(|| module.template.layout_pages(&mut vm)); -} - fn bench_highlight(iai: &mut Iai) { let (ctx, id) = context(); let source = ctx.sources.get(id); iai.run(|| source.highlight(0 .. source.len_bytes(), |_, _| {})); } -fn bench_byte_to_utf16(iai: &mut Iai) { - let (ctx, id) = context(); - let source = ctx.sources.get(id); - let mut ranges = vec![]; - source.highlight(0 .. source.len_bytes(), |range, _| ranges.push(range)); - iai.run(|| { - ranges - .iter() - .map(|range| source.byte_to_utf16(range.start) - .. source.byte_to_utf16(range.end)) - .collect::<Vec<_>>() - }); +fn bench_eval(iai: &mut Iai) { + let (mut ctx, id) = context(); + iai.run(|| ctx.evaluate(id).unwrap()); +} + +fn bench_layout(iai: &mut Iai) { + let (mut ctx, id) = context(); + let module = ctx.evaluate(id).unwrap(); + iai.run(|| module.template.layout(&mut ctx)); } fn bench_render(iai: &mut Iai) { diff --git a/src/eval/class.rs b/src/eval/class.rs index 5601fb0b..5e1857d7 100644 --- a/src/eval/class.rs +++ b/src/eval/class.rs @@ -2,8 +2,9 @@ use std::any::TypeId; use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; -use super::{Args, Func, StyleMap, Template, Value, Vm}; +use super::{Args, Func, StyleMap, Template, Value}; use crate::diag::TypResult; +use crate::Context; /// A class of nodes. /// @@ -38,7 +39,7 @@ use crate::diag::TypResult; pub struct Class { name: &'static str, id: TypeId, - construct: fn(&mut Vm, &mut Args) -> TypResult<Value>, + construct: fn(&mut Context, &mut Args) -> TypResult<Value>, set: fn(&mut Args, &mut StyleMap) -> TypResult<()>, } @@ -81,8 +82,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, vm: &mut Vm, mut args: Args) -> TypResult<Value> { - let value = (self.construct)(vm, &mut args)?; + pub fn construct(&self, ctx: &mut Context, mut args: Args) -> TypResult<Value> { + let value = (self.construct)(ctx, &mut args)?; args.finish()?; Ok(value) } @@ -125,7 +126,7 @@ pub trait Construct { /// /// This is passed only the arguments that remain after execution of the /// class's set rule. - fn construct(vm: &mut Vm, args: &mut Args) -> TypResult<Template>; + fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Template>; } /// Set style properties of a class. diff --git a/src/eval/func.rs b/src/eval/func.rs index 128509f8..cd54a140 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -2,11 +2,12 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use super::{Cast, Eval, Scope, Value, Vm}; +use super::{Cast, Eval, Scope, Scopes, Value}; use crate::diag::{At, TypResult}; use crate::syntax::ast::Expr; use crate::syntax::{Span, Spanned}; use crate::util::EcoString; +use crate::Context; /// An evaluatable function. #[derive(Clone, Hash)] @@ -27,7 +28,7 @@ impl Func { /// Create a new function from a native rust function. pub fn native( name: &'static str, - func: fn(&mut Vm, &mut Args) -> TypResult<Value>, + func: fn(&mut Context, &mut Args) -> TypResult<Value>, ) -> Self { Self(Arc::new(Repr::Native(Native { name, func }))) } @@ -46,14 +47,14 @@ impl Func { } } - /// Call the function in the context with the arguments. - pub fn call(&self, vm: &mut Vm, mut args: Args) -> TypResult<Value> { + /// Call the function with a virtual machine and arguments. + pub fn call(&self, ctx: &mut Context, mut args: Args) -> TypResult<Value> { let value = match self.0.as_ref() { - Repr::Native(native) => (native.func)(vm, &mut args)?, - Repr::Closure(closure) => closure.call(vm, &mut args)?, + Repr::Native(native) => (native.func)(ctx, &mut args)?, + Repr::Closure(closure) => closure.call(ctx, &mut args)?, Repr::With(wrapped, applied) => { args.items.splice(.. 0, applied.items.iter().cloned()); - return wrapped.call(vm, args); + return wrapped.call(ctx, args); } }; args.finish()?; @@ -88,7 +89,7 @@ struct Native { /// The name of the function. pub name: &'static str, /// The function pointer. - pub func: fn(&mut Vm, &mut Args) -> TypResult<Value>, + pub func: fn(&mut Context, &mut Args) -> TypResult<Value>, } impl Hash for Native { @@ -115,15 +116,15 @@ pub struct Closure { impl Closure { /// Call the function in the context with the arguments. - pub fn call(&self, vm: &mut Vm, args: &mut Args) -> TypResult<Value> { + pub fn call(&self, ctx: &mut Context, 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 vm.scopes); - vm.scopes.top = self.captured.clone(); + let mut scp = Scopes::new(None); + scp.top = self.captured.clone(); // Parse the arguments according to the parameter list. for (param, default) in &self.params { - vm.scopes.top.def_mut(param, match default { + scp.top.def_mut(param, match default { None => args.expect::<Value>(param)?, Some(default) => { args.named::<Value>(param)?.unwrap_or_else(|| default.clone()) @@ -133,14 +134,11 @@ impl Closure { // Put the remaining arguments into the sink. if let Some(sink) = &self.sink { - vm.scopes.top.def_mut(sink, args.take()); + scp.top.def_mut(sink, args.take()); } // Evaluate the body. - let value = self.body.eval(vm)?; - - // Restore the call site scopes. - vm.scopes = prev_scopes; + let value = self.body.eval(ctx, &mut scp)?; Ok(value) } diff --git a/src/eval/layout.rs b/src/eval/layout.rs index 7608023a..1090aafc 100644 --- a/src/eval/layout.rs +++ b/src/eval/layout.rs @@ -11,7 +11,7 @@ use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; use crate::geom::{Align, Length, Linear, Paint, Point, Sides, Size, Spec, Transform}; use crate::library::{AlignNode, PadNode, TransformNode, MOVE}; use crate::util::Prehashed; -use crate::Vm; +use crate::Context; /// A node that can be layouted into a sequence of regions. /// @@ -21,7 +21,7 @@ pub trait Layout { /// Layout the node into the given regions, producing constrained frames. fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>>; @@ -36,14 +36,14 @@ pub trait Layout { } /// A sequence of regions to layout into. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Regions { /// The (remaining) size of the first region. pub first: Size, /// The base size for relative sizing. pub base: Size, /// The height of followup regions. The width is the same for all regions. - pub backlog: std::vec::IntoIter<Length>, + pub backlog: Vec<Length>, /// The height of the final region that is repeated once the backlog is /// drained. The width is the same for all regions. pub last: Option<Length>, @@ -58,7 +58,7 @@ impl Regions { Self { first: size, base, - backlog: vec![].into_iter(), + backlog: vec![], last: None, expand, } @@ -69,7 +69,7 @@ impl Regions { Self { first: size, base, - backlog: vec![].into_iter(), + backlog: vec![], last: Some(size.y), expand, } @@ -87,13 +87,7 @@ impl Regions { Self { first: f(self.first), base: f(self.base), - backlog: self - .backlog - .as_slice() - .iter() - .map(|&y| f(Size::new(x, y)).y) - .collect::<Vec<_>>() - .into_iter(), + backlog: self.backlog.iter().map(|&y| f(Size::new(x, y)).y).collect(), last: self.last.map(|y| f(Size::new(x, y)).y), expand: self.expand, } @@ -113,7 +107,10 @@ impl Regions { /// Advance to the next region if there is any. pub fn next(&mut self) { - if let Some(height) = self.backlog.next().or(self.last) { + if let Some(height) = (!self.backlog.is_empty()) + .then(|| self.backlog.remove(0)) + .or(self.last) + { self.first.y = height; self.base.y = height; } @@ -125,7 +122,7 @@ impl Regions { /// This iterater may be infinite. pub fn iter(&self) -> impl Iterator<Item = Size> + '_ { let first = std::iter::once(self.first); - let backlog = self.backlog.as_slice().iter(); + let backlog = self.backlog.iter(); let last = self.last.iter().cycle(); first.chain(backlog.chain(last).map(|&h| Size::new(self.first.x, h))) } @@ -218,15 +215,14 @@ impl LayoutNode { } impl Layout for LayoutNode { - #[track_caller] fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { // TODO(query) - self.0.layout(vm, regions, styles.barred(self.id())) + self.0.layout(ctx, regions, styles.barred(self.id())) } fn pack(self) -> LayoutNode { @@ -274,7 +270,7 @@ struct EmptyNode; impl Layout for EmptyNode { fn layout( &self, - _: &mut Vm, + _: &mut Context, regions: &Regions, _: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -296,7 +292,7 @@ struct SizedNode { impl Layout for SizedNode { fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -319,7 +315,7 @@ impl Layout for SizedNode { }; // Layout the child. - let mut frames = self.child.layout(vm, &pod, styles)?; + let mut frames = self.child.layout(ctx, &pod, styles)?; // Ensure frame size matches regions size if expansion is on. let frame = &mut frames[0]; @@ -342,11 +338,11 @@ struct FillNode { impl Layout for FillNode { fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { - let mut frames = self.child.layout(vm, regions, styles)?; + let mut frames = self.child.layout(ctx, regions, styles)?; for frame in &mut frames { let shape = Shape::filled(Geometry::Rect(frame.size), self.fill); Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); @@ -367,11 +363,11 @@ struct StrokeNode { impl Layout for StrokeNode { fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { - let mut frames = self.child.layout(vm, regions, styles)?; + let mut frames = self.child.layout(ctx, regions, styles)?; for frame in &mut frames { let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke); Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); diff --git a/src/eval/mod.rs b/src/eval/mod.rs index a67a21d4..bc6c8fc8 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -13,6 +13,7 @@ mod class; mod collapse; mod func; mod layout; +mod module; mod ops; mod scope; mod show; @@ -25,15 +26,13 @@ pub use collapse::*; pub use dict::*; pub use func::*; pub use layout::*; +pub use module::*; pub use scope::*; pub use show::*; pub use styles::*; pub use template::*; pub use value::*; -use std::io; -use std::mem; - use unicode_segmentation::UnicodeSegmentation; use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; @@ -42,17 +41,7 @@ use crate::library; use crate::syntax::ast::*; use crate::syntax::{Span, Spanned}; use crate::util::EcoString; -use crate::Vm; - -/// An evaluated module, ready for importing or conversion to a root layout -/// tree. -#[derive(Debug, Clone)] -pub struct Module { - /// The top-level definitions that were bound in this module. - pub scope: Scope, - /// The module's layoutable contents. - pub template: Template, -} +use crate::Context; /// Evaluate an expression. pub trait Eval { @@ -60,20 +49,21 @@ pub trait Eval { type Output; /// Evaluate the expression to the output value. - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output>; + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output>; } impl Eval for Markup { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - eval_markup(vm, &mut self.nodes()) + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + eval_markup(ctx, scp, &mut self.nodes()) } } /// Evaluate a stream of markup nodes. fn eval_markup( - vm: &mut Vm, + ctx: &mut Context, + scp: &mut Scopes, nodes: &mut impl Iterator<Item = MarkupNode>, ) -> TypResult<Template> { let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default()); @@ -81,19 +71,19 @@ fn eval_markup( while let Some(node) = nodes.next() { seq.push(match node { MarkupNode::Expr(Expr::Set(set)) => { - let styles = set.eval(vm)?; - eval_markup(vm, nodes)?.styled_with_map(styles) + let styles = set.eval(ctx, scp)?; + eval_markup(ctx, scp, nodes)?.styled_with_map(styles) } MarkupNode::Expr(Expr::Show(show)) => { - let styles = show.eval(vm)?; - eval_markup(vm, nodes)?.styled_with_map(styles) + let styles = show.eval(ctx, scp)?; + eval_markup(ctx, scp, nodes)?.styled_with_map(styles) } MarkupNode::Expr(Expr::Wrap(wrap)) => { - let tail = eval_markup(vm, nodes)?; - vm.scopes.top.def_mut(wrap.binding().take(), tail); - wrap.body().eval(vm)?.display() + let tail = eval_markup(ctx, scp, nodes)?; + scp.top.def_mut(wrap.binding().take(), tail); + wrap.body().eval(ctx, scp)?.display() } - _ => node.eval(vm)?, + _ => node.eval(ctx, scp)?, }); } @@ -103,20 +93,20 @@ fn eval_markup( impl Eval for MarkupNode { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> 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(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(), + Self::Strong(strong) => strong.eval(ctx, scp)?, + Self::Emph(emph) => emph.eval(ctx, scp)?, + Self::Raw(raw) => raw.eval(ctx, scp)?, + Self::Math(math) => math.eval(ctx, scp)?, + Self::Heading(heading) => heading.eval(ctx, scp)?, + Self::List(list) => list.eval(ctx, scp)?, + Self::Enum(enum_) => enum_.eval(ctx, scp)?, + Self::Expr(expr) => expr.eval(ctx, scp)?.display(), }) } } @@ -124,23 +114,27 @@ impl Eval for MarkupNode { impl Eval for StrongNode { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - Ok(Template::show(library::StrongNode(self.body().eval(vm)?))) + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + Ok(Template::show(library::StrongNode( + self.body().eval(ctx, scp)?, + ))) } } impl Eval for EmphNode { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - Ok(Template::show(library::EmphNode(self.body().eval(vm)?))) + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + Ok(Template::show(library::EmphNode( + self.body().eval(ctx, scp)?, + ))) } } impl Eval for RawNode { type Output = Template; - fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> { let template = Template::show(library::RawNode { text: self.text.clone(), block: self.block, @@ -155,7 +149,7 @@ impl Eval for RawNode { impl Eval for MathNode { type Output = Template; - fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> { Ok(Template::show(library::MathNode { formula: self.formula.clone(), display: self.display, @@ -166,9 +160,9 @@ impl Eval for MathNode { impl Eval for HeadingNode { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { Ok(Template::show(library::HeadingNode { - body: self.body().eval(vm)?, + body: self.body().eval(ctx, scp)?, level: self.level(), })) } @@ -177,10 +171,10 @@ impl Eval for HeadingNode { impl Eval for ListNode { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { Ok(Template::List(library::ListItem { number: None, - body: Box::new(self.body().eval(vm)?), + body: Box::new(self.body().eval(ctx, scp)?), })) } } @@ -188,10 +182,10 @@ impl Eval for ListNode { impl Eval for EnumNode { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { Ok(Template::Enum(library::ListItem { number: self.number(), - body: Box::new(self.body().eval(vm)?), + body: Box::new(self.body().eval(ctx, scp)?), })) } } @@ -199,33 +193,33 @@ impl Eval for EnumNode { impl Eval for Expr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { match self { - 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::Lit(v) => v.eval(ctx, scp), + Self::Ident(v) => v.eval(ctx, scp), + Self::Array(v) => v.eval(ctx, scp).map(Value::Array), + Self::Dict(v) => v.eval(ctx, scp).map(Value::Dict), + Self::Template(v) => v.eval(ctx, scp).map(Value::Template), + Self::Group(v) => v.eval(ctx, scp), + Self::Block(v) => v.eval(ctx, scp), + Self::Call(v) => v.eval(ctx, scp), + Self::Closure(v) => v.eval(ctx, scp), + Self::With(v) => v.eval(ctx, scp), + Self::Unary(v) => v.eval(ctx, scp), + Self::Binary(v) => v.eval(ctx, scp), + Self::Let(v) => v.eval(ctx, scp), Self::Set(_) | Self::Show(_) | Self::Wrap(_) => { Err("set, show and wrap are only allowed directly in markup") .at(self.span()) } - 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), + Self::If(v) => v.eval(ctx, scp), + Self::While(v) => v.eval(ctx, scp), + Self::For(v) => v.eval(ctx, scp), + Self::Import(v) => v.eval(ctx, scp), + Self::Include(v) => v.eval(ctx, scp).map(Value::Template), + Self::Break(v) => v.eval(ctx, scp), + Self::Continue(v) => v.eval(ctx, scp), + Self::Return(v) => v.eval(ctx, scp), } } } @@ -233,7 +227,7 @@ impl Eval for Expr { impl Eval for Lit { type Output = Value; - fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> { Ok(match self.kind() { LitKind::None => Value::None, LitKind::Auto => Value::Auto, @@ -252,8 +246,8 @@ impl Eval for Lit { impl Eval for Ident { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - match vm.scopes.get(self) { + fn eval(&self, _: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + match scp.get(self) { Some(slot) => Ok(slot.read().unwrap().clone()), None => bail!(self.span(), "unknown variable"), } @@ -263,17 +257,17 @@ impl Eval for Ident { impl Eval for ArrayExpr { type Output = Array; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - self.items().map(|expr| expr.eval(vm)).collect() + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + self.items().map(|expr| expr.eval(ctx, scp)).collect() } } impl Eval for DictExpr { type Output = Dict; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { self.items() - .map(|x| Ok((x.name().take(), x.expr().eval(vm)?))) + .map(|x| Ok((x.name().take(), x.expr().eval(ctx, scp)?))) .collect() } } @@ -281,10 +275,10 @@ impl Eval for DictExpr { impl Eval for TemplateExpr { type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - vm.scopes.enter(); - let template = self.body().eval(vm)?; - vm.scopes.exit(); + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + scp.enter(); + let template = self.body().eval(ctx, scp)?; + scp.exit(); Ok(template) } } @@ -292,24 +286,24 @@ impl Eval for TemplateExpr { impl Eval for GroupExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - self.expr().eval(vm) + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + self.expr().eval(ctx, scp) } } impl Eval for BlockExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - vm.scopes.enter(); + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + scp.enter(); let mut output = Value::None; for expr in self.exprs() { - let value = expr.eval(vm)?; + let value = expr.eval(ctx, scp)?; output = ops::join(output, value).at(expr.span())?; } - vm.scopes.exit(); + scp.exit(); Ok(output) } @@ -318,8 +312,8 @@ impl Eval for BlockExpr { impl Eval for UnaryExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { - let value = self.expr().eval(vm)?; + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { + let value = self.expr().eval(ctx, scp)?; let result = match self.op() { UnOp::Pos => ops::pos(value), UnOp::Neg => ops::neg(value), @@ -332,25 +326,25 @@ impl Eval for UnaryExpr { impl Eval for BinaryExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { match self.op() { - 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), + BinOp::Add => self.apply(ctx, scp, ops::add), + BinOp::Sub => self.apply(ctx, scp, ops::sub), + BinOp::Mul => self.apply(ctx, scp, ops::mul), + BinOp::Div => self.apply(ctx, scp, ops::div), + BinOp::And => self.apply(ctx, scp, ops::and), + BinOp::Or => self.apply(ctx, scp, ops::or), + BinOp::Eq => self.apply(ctx, scp, ops::eq), + BinOp::Neq => self.apply(ctx, scp, ops::neq), + BinOp::Lt => self.apply(ctx, scp, ops::lt), + BinOp::Leq => self.apply(ctx, scp, ops::leq), + BinOp::Gt => self.apply(ctx, scp, ops::gt), + BinOp::Geq => self.apply(ctx, scp, ops::geq), + BinOp::Assign => self.assign(ctx, scp, |_, b| Ok(b)), + BinOp::AddAssign => self.assign(ctx, scp, ops::add), + BinOp::SubAssign => self.assign(ctx, scp, ops::sub), + BinOp::MulAssign => self.assign(ctx, scp, ops::mul), + BinOp::DivAssign => self.assign(ctx, scp, ops::div), } } } @@ -359,10 +353,11 @@ impl BinaryExpr { /// Apply a basic binary operation. fn apply( &self, - vm: &mut Vm, + ctx: &mut Context, + scp: &mut Scopes, op: fn(Value, Value) -> StrResult<Value>, ) -> TypResult<Value> { - let lhs = self.lhs().eval(vm)?; + let lhs = self.lhs().eval(ctx, scp)?; // Short-circuit boolean operations. if (self.op() == BinOp::And && lhs == Value::Bool(false)) @@ -371,21 +366,23 @@ impl BinaryExpr { return Ok(lhs); } - let rhs = self.rhs().eval(vm)?; + let rhs = self.rhs().eval(ctx, scp)?; op(lhs, rhs).at(self.span()) } /// Apply an assignment operation. fn assign( &self, - vm: &mut Vm, + ctx: &mut Context, + scp: &mut Scopes, op: fn(Value, Value) -> StrResult<Value>, ) -> TypResult<Value> { - let rhs = self.rhs().eval(vm)?; + let rhs = self.rhs().eval(ctx, scp)?; self.lhs().access( - vm, + ctx, + scp, Box::new(|target| { - let lhs = mem::take(&mut *target); + let lhs = std::mem::take(&mut *target); *target = op(lhs, rhs).at(self.span())?; Ok(()) }), @@ -397,10 +394,10 @@ impl BinaryExpr { impl Eval for CallExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let span = self.callee().span(); - let callee = self.callee().eval(vm)?; - let args = self.args().eval(vm)?; + let callee = self.callee().eval(ctx, scp)?; + let args = self.args().eval(ctx, scp)?; match callee { Value::Array(array) => { @@ -413,12 +410,12 @@ impl Eval for CallExpr { Value::Func(func) => { let point = || Tracepoint::Call(func.name().map(ToString::to_string)); - func.call(vm, args).trace(point, self.span()) + func.call(ctx, args).trace(point, self.span()) } Value::Class(class) => { let point = || Tracepoint::Call(Some(class.name().to_string())); - class.construct(vm, args).trace(point, self.span()) + class.construct(ctx, args).trace(point, self.span()) } v => bail!( @@ -433,7 +430,7 @@ impl Eval for CallExpr { impl Eval for CallArgs { type Output = Args; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let mut items = Vec::new(); for arg in self.items() { @@ -443,17 +440,20 @@ impl Eval for CallArgs { items.push(Arg { span, name: None, - value: Spanned::new(expr.eval(vm)?, expr.span()), + value: Spanned::new(expr.eval(ctx, scp)?, expr.span()), }); } CallArg::Named(named) => { items.push(Arg { span, name: Some(named.name().take()), - value: Spanned::new(named.expr().eval(vm)?, named.expr().span()), + value: Spanned::new( + named.expr().eval(ctx, scp)?, + named.expr().span(), + ), }); } - CallArg::Spread(expr) => match expr.eval(vm)? { + CallArg::Spread(expr) => match expr.eval(ctx, scp)? { Value::None => {} Value::Array(array) => { items.extend(array.into_iter().map(|value| Arg { @@ -482,13 +482,13 @@ impl Eval for CallArgs { impl Eval for ClosureExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> 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(&vm.scopes); + let mut visitor = CapturesVisitor::new(scp); visitor.visit(self.as_red()); visitor.finish() }; @@ -503,7 +503,8 @@ impl Eval for ClosureExpr { params.push((name.take(), None)); } ClosureParam::Named(named) => { - params.push((named.name().take(), Some(named.expr().eval(vm)?))); + params + .push((named.name().take(), Some(named.expr().eval(ctx, scp)?))); } ClosureParam::Sink(name) => { if sink.is_some() { @@ -528,10 +529,10 @@ impl Eval for ClosureExpr { impl Eval for WithExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let callee = self.callee(); - let func = callee.eval(vm)?.cast::<Func>().at(callee.span())?; - let args = self.args().eval(vm)?; + let func = callee.eval(ctx, scp)?.cast::<Func>().at(callee.span())?; + let args = self.args().eval(ctx, scp)?; Ok(Value::Func(func.with(args))) } } @@ -539,12 +540,12 @@ impl Eval for WithExpr { impl Eval for LetExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let value = match self.init() { - Some(expr) => expr.eval(vm)?, + Some(expr) => expr.eval(ctx, scp)?, None => Value::None, }; - vm.scopes.top.def_mut(self.binding().take(), value); + scp.top.def_mut(self.binding().take(), value); Ok(Value::None) } } @@ -552,10 +553,10 @@ impl Eval for LetExpr { impl Eval for SetExpr { type Output = StyleMap; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let class = self.class(); - let class = class.eval(vm)?.cast::<Class>().at(class.span())?; - let args = self.args().eval(vm)?; + let class = class.eval(ctx, scp)?.cast::<Class>().at(class.span())?; + let args = self.args().eval(ctx, scp)?; class.set(args) } } @@ -563,11 +564,11 @@ impl Eval for SetExpr { impl Eval for ShowExpr { type Output = StyleMap; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let class = self.class(); - let class = class.eval(vm)?.cast::<Class>().at(class.span())?; + let class = class.eval(ctx, scp)?.cast::<Class>().at(class.span())?; let closure = self.closure(); - let func = closure.eval(vm)?.cast::<Func>().at(closure.span())?; + let func = closure.eval(ctx, scp)?.cast::<Func>().at(closure.span())?; let mut styles = StyleMap::new(); styles.set_recipe(class.id(), func, self.span()); Ok(styles) @@ -577,12 +578,12 @@ impl Eval for ShowExpr { impl Eval for IfExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let condition = self.condition(); - if condition.eval(vm)?.cast::<bool>().at(condition.span())? { - self.if_body().eval(vm) + if condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? { + self.if_body().eval(ctx, scp) } else if let Some(else_body) = self.else_body() { - else_body.eval(vm) + else_body.eval(ctx, scp) } else { Ok(Value::None) } @@ -592,13 +593,13 @@ impl Eval for IfExpr { impl Eval for WhileExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let mut output = Value::None; let condition = self.condition(); - while condition.eval(vm)?.cast::<bool>().at(condition.span())? { + while condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? { let body = self.body(); - let value = body.eval(vm)?; + let value = body.eval(ctx, scp)?; output = ops::join(output, value).at(body.span())?; } @@ -609,27 +610,27 @@ impl Eval for WhileExpr { impl Eval for ForExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { macro_rules! iter { (for ($($binding:ident => $value:ident),*) in $iter:expr) => {{ let mut output = Value::None; - vm.scopes.enter(); + scp.enter(); #[allow(unused_parens)] for ($($value),*) in $iter { - $(vm.scopes.top.def_mut(&$binding, $value);)* + $(scp.top.def_mut(&$binding, $value);)* - let value = self.body().eval(vm)?; + let value = self.body().eval(ctx, scp)?; output = ops::join(output, value) .at(self.body().span())?; } - vm.scopes.exit(); + scp.exit(); return Ok(output); }}; } - let iter = self.iter().eval(vm)?; + let iter = self.iter().eval(ctx, scp)?; let pattern = self.pattern(); let key = pattern.key().map(Ident::take); let value = pattern.value().take(); @@ -672,21 +673,21 @@ impl Eval for ForExpr { impl Eval for ImportExpr { type Output = Value; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> { let span = self.path().span(); - let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?; - let module = import(vm, &path, span)?; + let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?; + let module = import(ctx, &path, span)?; match self.imports() { Imports::Wildcard => { for (var, slot) in module.scope.iter() { - vm.scopes.top.def_mut(var, slot.read().unwrap().clone()); + scp.top.def_mut(var, slot.read().unwrap().clone()); } } Imports::Items(idents) => { for ident in idents { if let Some(slot) = module.scope.get(&ident) { - vm.scopes.top.def_mut(ident.take(), slot.read().unwrap().clone()); + scp.top.def_mut(ident.take(), slot.read().unwrap().clone()); } else { bail!(ident.span(), "unresolved import"); } @@ -699,40 +700,42 @@ impl Eval for ImportExpr { } impl Eval for IncludeExpr { - type Output = Value; + type Output = Template; - fn eval(&self, vm: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> 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())) + let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?; + let module = import(ctx, &path, span)?; + Ok(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> { +fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> { // Load the source file. - let full = vm.resolve(path); - let id = vm.sources.load(&full).map_err(|err| { + let full = ctx.resolve(path); + let id = ctx.sources.load(&full).map_err(|err| { Error::boxed(span, match err.kind() { - io::ErrorKind::NotFound => "file not found".into(), + std::io::ErrorKind::NotFound => "file not found".into(), _ => format!("failed to load source file ({})", err), }) })?; // Prevent cyclic importing. - if vm.route.contains(&id) { + if ctx.route.contains(&id) { bail!(span, "cyclic import"); } // Evaluate the file. - vm.evaluate(id).trace(|| Tracepoint::Import, span) + let module = ctx.evaluate(id).trace(|| Tracepoint::Import, span)?; + ctx.deps.extend(module.deps.iter().cloned()); + Ok(module) } impl Eval for BreakExpr { type Output = Value; - fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> { Err("break is not yet implemented").at(self.span()) } } @@ -740,7 +743,7 @@ impl Eval for BreakExpr { impl Eval for ContinueExpr { type Output = Value; - fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> { Err("continue is not yet implemented").at(self.span()) } } @@ -748,7 +751,7 @@ impl Eval for ContinueExpr { impl Eval for ReturnExpr { type Output = Value; - fn eval(&self, _: &mut Vm) -> TypResult<Self::Output> { + fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> { Err("return is not yet implemented").at(self.span()) } } @@ -758,25 +761,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, vm: &mut Vm, f: Handler) -> TypResult<()>; + fn access(&self, ctx: &mut Context, scp: &mut Scopes, f: Handler) -> TypResult<()>; } /// Process an accessed value. type Handler<'a> = Box<dyn FnOnce(&mut Value) -> TypResult<()> + 'a>; impl Access for Expr { - fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()> { + fn access(&self, ctx: &mut Context, scp: &mut Scopes, f: Handler) -> TypResult<()> { match self { - Expr::Ident(ident) => ident.access(vm, f), - Expr::Call(call) => call.access(vm, f), + Expr::Ident(ident) => ident.access(ctx, scp, f), + Expr::Call(call) => call.access(ctx, scp, f), _ => bail!(self.span(), "cannot access this expression mutably"), } } } impl Access for Ident { - fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()> { - match vm.scopes.get(self) { + fn access(&self, _: &mut Context, scp: &mut Scopes, f: Handler) -> TypResult<()> { + match scp.get(self) { Some(slot) => match slot.try_write() { Ok(mut guard) => f(&mut guard), Err(_) => bail!(self.span(), "cannot mutate a constant"), @@ -787,10 +790,11 @@ impl Access for Ident { } impl Access for CallExpr { - fn access(&self, vm: &mut Vm, f: Handler) -> TypResult<()> { - let args = self.args().eval(vm)?; + fn access(&self, ctx: &mut Context, scp: &mut Scopes, f: Handler) -> TypResult<()> { + let args = self.args().eval(ctx, scp)?; self.callee().access( - vm, + ctx, + scp, Box::new(|value| match value { Value::Array(array) => { f(array.get_mut(args.into_index()?).at(self.span())?) diff --git a/src/eval/module.rs b/src/eval/module.rs new file mode 100644 index 00000000..478c76b7 --- /dev/null +++ b/src/eval/module.rs @@ -0,0 +1,20 @@ +use super::{Scope, Template}; +use crate::source::{SourceId, SourceStore}; + +/// An evaluated module, ready for importing or layouting. +#[derive(Debug, Clone)] +pub struct Module { + /// The top-level definitions that were bound in this module. + pub scope: Scope, + /// The module's layoutable contents. + pub template: Template, + /// The source file revisions this module depends on. + pub deps: Vec<(SourceId, usize)>, +} + +impl Module { + /// Whether the module is still valid for the given sources. + pub fn valid(&self, sources: &SourceStore) -> bool { + self.deps.iter().all(|&(id, rev)| rev == sources.get(id).rev()) + } +} diff --git a/src/eval/scope.rs b/src/eval/scope.rs index 0df4c4df..e09d05c8 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -4,9 +4,10 @@ use std::hash::{Hash, Hasher}; use std::iter; use std::sync::{Arc, RwLock}; -use super::{Args, Class, Construct, Func, Set, Value, Vm}; +use super::{Args, Class, Construct, Func, Set, Value}; use crate::diag::TypResult; use crate::util::EcoString; +use crate::Context; /// A slot where a variable is stored. pub type Slot = Arc<RwLock<Value>>; @@ -86,7 +87,7 @@ impl Scope { pub fn def_func( &mut self, name: &'static str, - func: fn(&mut Vm, &mut Args) -> TypResult<Value>, + func: fn(&mut Context, &mut Args) -> TypResult<Value>, ) { self.def_const(name, Func::native(name, func)); } diff --git a/src/eval/show.rs b/src/eval/show.rs index ac0e2378..b0fb8172 100644 --- a/src/eval/show.rs +++ b/src/eval/show.rs @@ -6,12 +6,12 @@ use std::sync::Arc; use super::{StyleChain, Template}; use crate::diag::TypResult; use crate::util::Prehashed; -use crate::Vm; +use crate::Context; /// A node that can be realized given some styles. pub trait Show { /// Realize the template in the given styles. - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template>; + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template>; /// Convert to a packed show node. fn pack(self) -> ShowNode @@ -42,8 +42,8 @@ impl ShowNode { } impl Show for ShowNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { - self.0.show(vm, styles) + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { + self.0.show(ctx, styles) } fn pack(self) -> ShowNode { diff --git a/src/eval/styles.rs b/src/eval/styles.rs index 346eb784..bd1f808f 100644 --- a/src/eval/styles.rs +++ b/src/eval/styles.rs @@ -3,9 +3,10 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use super::{Args, Func, Span, Template, Value, Vm}; +use super::{Args, Func, Span, Template, Value}; use crate::diag::{At, TypResult}; use crate::library::{PageNode, ParNode}; +use crate::Context; /// A map of style properties. #[derive(Default, Clone, PartialEq, Hash)] @@ -341,11 +342,6 @@ impl<'a> StyleChain<'a> { *self = self.outer.copied().unwrap_or_default(); } - /// Return the span of a recipe for the given node. - pub(crate) fn recipe_span(self, node: TypeId) -> Option<Span> { - self.recipes(node).next().map(|recipe| recipe.span) - } - /// Return the chain, but without the last link if that one contains only /// scoped styles. This is a hack. pub(crate) fn unscoped(mut self, node: TypeId) -> Self { @@ -415,12 +411,12 @@ impl<'a> StyleChain<'a> { pub fn show( self, node: &dyn Any, - vm: &mut Vm, + ctx: &mut Context, values: impl IntoIterator<Item = Value>, ) -> TypResult<Option<Template>> { Ok(if let Some(recipe) = self.recipes(node.type_id()).next() { let args = Args::from_values(recipe.span, values); - Some(recipe.func.call(vm, args)?.cast().at(recipe.span)?) + Some(recipe.func.call(ctx, args)?.cast().at(recipe.span)?) } else { None }) diff --git a/src/eval/template.rs b/src/eval/template.rs index b4e3c76d..a9e1262f 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -1,7 +1,6 @@ use std::fmt::Debug; use std::hash::Hash; use std::iter::Sum; -use std::mem; use std::ops::{Add, AddAssign}; use typed_arena::Arena; @@ -169,21 +168,23 @@ impl Template { } /// Layout this template into a collection of pages. - pub fn layout_pages(&self, vm: &mut Vm) -> TypResult<Vec<Arc<Frame>>> { + pub fn layout(&self, ctx: &mut Context) -> TypResult<Vec<Arc<Frame>>> { let sya = Arena::new(); let tpa = Arena::new(); + let styles = ctx.styles.clone(); + let styles = StyleChain::new(&styles); + let mut builder = Builder::new(&sya, &tpa, true); - let styles = StyleChain::new(vm.styles); - builder.process(vm, self, styles)?; - builder.finish(vm, styles)?; + builder.process(ctx, self, styles)?; + builder.finish(ctx, styles)?; let mut frames = vec![]; let (pages, shared) = builder.pages.unwrap().finish(); for (page, map) in pages.iter() { let number = 1 + frames.len(); - frames.extend(page.layout(vm, number, map.chain(&shared))?); + frames.extend(page.layout(ctx, number, map.chain(&shared))?); } Ok(frames) @@ -224,7 +225,7 @@ impl Add for Template { impl AddAssign for Template { fn add_assign(&mut self, rhs: Self) { - *self = mem::take(self) + rhs; + *self = std::mem::take(self) + rhs; } } @@ -237,7 +238,7 @@ impl Sum for Template { impl Layout for Template { fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -245,11 +246,11 @@ impl Layout for Template { let tpa = Arena::new(); let mut builder = Builder::new(&sya, &tpa, false); - builder.process(vm, self, styles)?; - builder.finish(vm, styles)?; + builder.process(ctx, self, styles)?; + builder.finish(ctx, styles)?; let (flow, shared) = builder.flow.finish(); - FlowNode(flow).layout(vm, regions, shared) + FlowNode(flow).layout(ctx, regions, shared) } fn pack(self) -> LayoutNode { @@ -295,7 +296,7 @@ impl<'a> Builder<'a> { /// Process a template. fn process( &mut self, - vm: &mut Vm, + ctx: &mut Context, template: &'a Template, styles: StyleChain<'a>, ) -> TypResult<()> { @@ -323,7 +324,7 @@ impl<'a> Builder<'a> { builder.items.push(item.clone()); return Ok(()); } - _ => self.finish_list(vm)?, + _ => self.finish_list(ctx)?, } } @@ -394,25 +395,19 @@ impl<'a> Builder<'a> { }); } Template::Pagebreak => { - self.finish_page(vm, true, true, styles)?; + self.finish_page(ctx, true, true, styles)?; } Template::Page(page) => { - self.finish_page(vm, false, false, styles)?; + self.finish_page(ctx, false, false, styles)?; if let Some(pages) = &mut self.pages { pages.push(page.clone(), styles); } } Template::Show(node) => { let id = node.id(); - if vm.rules.contains(&id) { - let span = styles.recipe_span(id).unwrap(); - return Err("show rule is recursive").at(span)?; - } - vm.rules.push(id); - let template = node.show(vm, styles)?; + let template = node.show(ctx, styles)?; let stored = self.tpa.alloc(template); - self.process(vm, stored, styles.unscoped(id))?; - vm.rules.pop(); + self.process(ctx, stored, styles.unscoped(id))?; } Template::Styled(styled) => { let (sub, map) = styled.as_ref(); @@ -422,17 +417,17 @@ impl<'a> Builder<'a> { let interruption = map.interruption(); match interruption { Some(Interruption::Page) => { - self.finish_page(vm, false, true, styles)? + self.finish_page(ctx, false, true, styles)? } Some(Interruption::Par) => self.finish_par(styles), None => {} } - self.process(vm, sub, styles)?; + self.process(ctx, sub, styles)?; match interruption { Some(Interruption::Page) => { - self.finish_page(vm, true, false, styles)? + self.finish_page(ctx, true, false, styles)? } Some(Interruption::Par) => self.finish_par(styles), None => {} @@ -440,7 +435,7 @@ impl<'a> Builder<'a> { } Template::Sequence(seq) => { for sub in seq.iter() { - self.process(vm, sub, styles)?; + self.process(ctx, sub, styles)?; } } } @@ -450,7 +445,7 @@ impl<'a> Builder<'a> { /// Finish the currently built paragraph. fn finish_par(&mut self, styles: StyleChain<'a>) { - let (par, shared) = mem::take(&mut self.par).finish(); + let (par, shared) = std::mem::take(&mut self.par).finish(); if !par.is_empty() { let node = ParNode(par).pack(); self.flow.supportive(FlowChild::Node(node), shared); @@ -459,7 +454,7 @@ impl<'a> Builder<'a> { } /// Finish the currently built list. - fn finish_list(&mut self, vm: &mut Vm) -> TypResult<()> { + fn finish_list(&mut self, ctx: &mut Context) -> TypResult<()> { let ListBuilder { styles, kind, items, wide, staged } = match self.list.take() { Some(list) => list, None => return Ok(()), @@ -471,9 +466,9 @@ impl<'a> Builder<'a> { }; let stored = self.tpa.alloc(template); - self.process(vm, stored, styles)?; + self.process(ctx, stored, styles)?; for (template, styles) in staged { - self.process(vm, template, styles)?; + self.process(ctx, template, styles)?; } Ok(()) @@ -482,15 +477,15 @@ impl<'a> Builder<'a> { /// Finish the currently built page run. fn finish_page( &mut self, - vm: &mut Vm, + ctx: &mut Context, keep_last: bool, keep_next: bool, styles: StyleChain<'a>, ) -> TypResult<()> { - self.finish_list(vm)?; + self.finish_list(ctx)?; self.finish_par(styles); if let Some(pages) = &mut self.pages { - let (flow, shared) = mem::take(&mut self.flow).finish(); + let (flow, shared) = std::mem::take(&mut self.flow).finish(); if !flow.is_empty() || (keep_last && self.keep_next) { let styles = if flow.is_empty() { styles } else { shared }; let node = PageNode(FlowNode(flow).pack()); @@ -502,8 +497,8 @@ impl<'a> Builder<'a> { } /// Finish everything. - fn finish(&mut self, vm: &mut Vm, styles: StyleChain<'a>) -> TypResult<()> { - self.finish_page(vm, true, false, styles) + fn finish(&mut self, ctx: &mut Context, styles: StyleChain<'a>) -> TypResult<()> { + self.finish_page(ctx, true, false, styles) } } diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 18bb5385..b39f47ea 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -33,7 +33,7 @@ pub fn pdf(ctx: &Context, frames: &[Arc<Frame>]) -> Vec<u8> { } /// Identifies the sRGB color space definition. -pub const SRGB: Name<'static> = Name(b"sRGB"); +const SRGB: Name<'static> = Name(b"sRGB"); /// An exporter for a whole PDF document. struct PdfExporter<'a> { @@ -13,8 +13,7 @@ //! markup. //! - **Layouting:** Next, the tree is [layouted] into a portable version of the //! typeset document. The output of this is a collection of [`Frame`]s (one -//! per page), ready for exporting. This step is supported by an incremental -//! [cache] that enables reuse of intermediate layouting results. +//! per page), ready for exporting. //! - **Exporting:** The finished layout can be exported into a supported //! format. Currently, the only supported output format is [PDF]. //! @@ -22,11 +21,10 @@ //! [parsed]: parse::parse //! [green tree]: syntax::GreenNode //! [AST]: syntax::ast -//! [evaluate]: Vm::evaluate +//! [evaluate]: eval::Eval //! [module]: eval::Module //! [template]: eval::Template -//! [layouted]: eval::Template::layout_pages -//! [cache]: layout::LayoutCache +//! [layouted]: eval::Template::layout //! [PDF]: export::pdf #![allow(clippy::len_without_is_empty)] @@ -50,7 +48,6 @@ pub mod parse; pub mod source; pub mod syntax; -use std::any::TypeId; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; @@ -74,9 +71,15 @@ pub struct Context { /// Stores decoded images. pub images: ImageStore, /// The standard library scope. - std: Scope, + std: Arc<Scope>, /// The default styles. - styles: StyleMap, + styles: Arc<StyleMap>, + /// Cached modules. + modules: HashMap<SourceId, Module>, + /// The stack of imported files that led to evaluation of the current file. + route: Vec<SourceId>, + /// The dependencies of the current evaluation process. + deps: Vec<(SourceId, usize)>, } impl Context { @@ -100,13 +103,73 @@ 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 + /// messages with file and span information. + pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> { + // Prevent cyclic evaluation. + if self.route.contains(&id) { + let path = self.sources.get(id).path().display(); + panic!("Tried to cyclicly evaluate {}", path); + } + + // Check whether the module was already evaluated. + if let Some(module) = self.modules.get(&id) { + if module.valid(&self.sources) { + return Ok(module.clone()); + } else { + self.modules.remove(&id); + } + } + + // Parse the file. + let source = self.sources.get(id); + let ast = source.ast()?; + + let std = self.std.clone(); + let mut scp = Scopes::new(Some(&std)); + + // Evaluate the module. + let prev = std::mem::replace(&mut self.deps, vec![(id, source.rev())]); + self.route.push(id); + let template = ast.eval(self, &mut scp); + self.route.pop().unwrap(); + let deps = std::mem::replace(&mut self.deps, prev); + + // Assemble the module. + let module = Module { + scope: scp.top, + template: template?, + deps, + }; + + // Save the evaluated module. + 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>>> { - Vm::new(self).typeset(id) + self.evaluate(id)?.template.layout(self) + } + + /// Resolve a user-entered path (relative to the current evaluation + /// location) 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() } } @@ -114,21 +177,21 @@ impl Context { /// /// This struct is created by [`Context::builder`]. pub struct ContextBuilder { - std: Option<Scope>, - styles: Option<StyleMap>, + std: Option<Arc<Scope>>, + styles: Option<Arc<StyleMap>>, } impl ContextBuilder { /// The scope containing definitions that are available everywhere /// (the standard library). - pub fn std(mut self, std: Scope) -> Self { - self.std = Some(std); + pub fn std(mut self, std: impl Into<Arc<Scope>>) -> Self { + self.std = Some(std.into()); self } /// The default properties for page size, font selection and so on. - pub fn styles(mut self, styles: StyleMap) -> Self { - self.styles = Some(styles); + pub fn styles(mut self, styles: impl Into<Arc<StyleMap>>) -> Self { + self.styles = Some(styles.into()); self } @@ -140,8 +203,11 @@ impl ContextBuilder { fonts: FontStore::new(Arc::clone(&loader)), images: ImageStore::new(Arc::clone(&loader)), loader, - std: self.std.unwrap_or_else(library::new), + std: self.std.unwrap_or_else(|| Arc::new(library::new())), styles: self.styles.unwrap_or_default(), + modules: HashMap::new(), + route: vec![], + deps: vec![], } } } @@ -151,104 +217,3 @@ impl Default for ContextBuilder { Self { std: None, styles: None } } } - -/// 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, - /// 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>, - /// Currently evaluated show rules. This is used to prevent recursive show - /// rules. - pub rules: Vec<TypeId>, - /// How deeply nested the current layout tree position is. - 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, - styles: &ctx.styles, - route: vec![], - modules: HashMap::new(), - scopes: Scopes::new(Some(&ctx.std)), - rules: vec![], - 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>>> { - self.evaluate(id)?.template.layout_pages(self) - } - - /// 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 8ea9ddaf..2fd734d3 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -14,7 +14,7 @@ pub struct AlignNode { #[class] impl AlignNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -39,7 +39,7 @@ impl Layout for AlignNode { } // Layout the child. - let mut frames = self.child.layout(vm, &pod, passed.chain(&styles))?; + let mut frames = self.child.layout(ctx, &pod, passed.chain(&styles))?; for (region, frame) in regions.iter().zip(&mut frames) { // Align in the target size. The target size depends on whether we // should expand. diff --git a/src/library/columns.rs b/src/library/columns.rs index bae23dd3..3ce9b6e5 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { // Separating the infinite space into infinite columns does not make // much sense. if regions.first.x.is_infinite() { - return self.child.layout(vm, regions, styles); + return self.child.layout(ctx, regions, styles); } // Determine the width of the gutter and each column. @@ -52,14 +52,13 @@ impl Layout for ColumnsNode { .chain(regions.backlog.as_slice()) .flat_map(|&height| std::iter::repeat(height).take(columns)) .skip(1) - .collect::<Vec<_>>() - .into_iter(), + .collect(), last: regions.last, expand: Spec::new(true, regions.expand.y), }; // Layout the children. - let mut frames = self.child.layout(vm, &pod, styles)?.into_iter(); + let mut frames = self.child.layout(ctx, &pod, styles)?.into_iter(); let dir = styles.get(ParNode::DIR); let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize; @@ -108,7 +107,7 @@ pub struct ColbreakNode; #[class] impl ColbreakNode { - fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> { Ok(Template::Colbreak) } } diff --git a/src/library/container.rs b/src/library/container.rs index fafa7103..5517d927 100644 --- a/src/library/container.rs +++ b/src/library/container.rs @@ -7,7 +7,7 @@ pub struct BoxNode; #[class] impl BoxNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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 aac79d05..8c2b5a9a 100644 --- a/src/library/deco.rs +++ b/src/library/deco.rs @@ -26,15 +26,15 @@ impl<const L: DecoLine> DecoNode<L> { /// with the glyphs. Does not apply to strikethrough. pub const EVADE: bool = true; - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self(args.expect::<Template>("body")?))) } } impl<const L: DecoLine> Show for DecoNode<L> { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { Ok(styles - .show(self, vm, [Value::Template(self.0.clone())])? + .show(self, ctx, [Value::Template(self.0.clone())])? .unwrap_or_else(|| { self.0.clone().styled(TextNode::LINES, vec![Decoration { line: L, diff --git a/src/library/flow.rs b/src/library/flow.rs index bc3bf0f1..9e039d3a 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -56,7 +56,7 @@ impl Layout for FlowNode { layouter.layout_spacing(*kind); } FlowChild::Node(ref node) => { - layouter.layout_node(vm, node, styles)?; + layouter.layout_node(ctx, node, styles)?; } } } @@ -163,7 +163,7 @@ impl FlowLayouter { /// Layout a node. pub fn layout_node( &mut self, - vm: &mut Vm, + ctx: &mut Context, node: &LayoutNode, styles: StyleChain, ) -> TypResult<()> { @@ -176,7 +176,7 @@ impl FlowLayouter { // aligned later. if let Some(placed) = node.downcast::<PlaceNode>() { if placed.out_of_flow() { - let frame = node.layout(vm, &self.regions, styles)?.remove(0); + let frame = node.layout(ctx, &self.regions, styles)?.remove(0); self.items.push(FlowItem::Placed(frame)); return Ok(()); } @@ -193,7 +193,7 @@ impl FlowLayouter { .unwrap_or(Align::Top), ); - let frames = node.layout(vm, &self.regions, styles)?; + let frames = node.layout(ctx, &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 fc62f8eb..2d8cb462 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -15,7 +15,7 @@ pub struct GridNode { #[class] impl GridNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -49,7 +49,7 @@ impl Layout for GridNode { ); // Measure the columns and layout the grid row-by-row. - layouter.layout(vm) + layouter.layout(ctx) } } @@ -198,19 +198,19 @@ impl<'a> GridLayouter<'a> { } /// Determines the columns sizes and then layouts the grid row-by-row. - pub fn layout(mut self, vm: &mut Vm) -> TypResult<Vec<Arc<Frame>>> { - self.measure_columns(vm)?; + pub fn layout(mut self, ctx: &mut Context) -> TypResult<Vec<Arc<Frame>>> { + self.measure_columns(ctx)?; 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(vm)?; + self.finish_region(ctx)?; } match self.rows[y] { - TrackSizing::Auto => self.layout_auto_row(vm, y)?, - TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y)?, + TrackSizing::Auto => self.layout_auto_row(ctx, y)?, + TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y)?, TrackSizing::Fractional(v) => { self.lrows.push(Row::Fr(v, y)); self.fr += v; @@ -218,12 +218,12 @@ impl<'a> GridLayouter<'a> { } } - self.finish_region(vm)?; + self.finish_region(ctx)?; Ok(self.finished) } /// Determine all column sizes. - fn measure_columns(&mut self, vm: &mut Vm) -> TypResult<()> { + fn measure_columns(&mut self, ctx: &mut Context) -> TypResult<()> { // Sum of sizes of resolved linear tracks. let mut linear = Length::zero(); @@ -248,7 +248,7 @@ impl<'a> GridLayouter<'a> { let available = self.regions.first.x - linear; if available >= Length::zero() { // Determine size of auto columns. - let (auto, count) = self.measure_auto_columns(vm, available)?; + let (auto, count) = self.measure_auto_columns(ctx, available)?; // If there is remaining space, distribute it to fractional columns, // otherwise shrink auto columns. @@ -271,7 +271,7 @@ impl<'a> GridLayouter<'a> { /// Measure the size that is available to auto columns. fn measure_auto_columns( &mut self, - vm: &mut Vm, + ctx: &mut Context, available: Length, ) -> TypResult<(Length, usize)> { let mut auto = Length::zero(); @@ -298,7 +298,7 @@ impl<'a> GridLayouter<'a> { pod.base.y = v.resolve(self.regions.base.y); } - let frame = node.layout(vm, &pod, self.styles)?.remove(0); + let frame = node.layout(ctx, &pod, self.styles)?.remove(0); resolved.set_max(frame.size.x); } } @@ -352,7 +352,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, vm: &mut Vm, y: usize) -> TypResult<()> { + fn layout_auto_row(&mut self, ctx: &mut Context, y: usize) -> TypResult<()> { let mut resolved: Vec<Length> = vec![]; // Determine the size for each region of the row. @@ -367,7 +367,7 @@ impl<'a> GridLayouter<'a> { } let mut sizes = node - .layout(vm, &pod, self.styles)? + .layout(ctx, &pod, self.styles)? .into_iter() .map(|frame| frame.size.y); @@ -390,7 +390,7 @@ impl<'a> GridLayouter<'a> { // Layout into a single region. if let &[first] = resolved.as_slice() { - let frame = self.layout_single_row(vm, first, y)?; + let frame = self.layout_single_row(ctx, first, y)?; self.push_row(frame); return Ok(()); } @@ -405,12 +405,12 @@ impl<'a> GridLayouter<'a> { } // Layout into multiple regions. - let frames = self.layout_multi_row(vm, &resolved, y)?; + let frames = self.layout_multi_row(ctx, &resolved, y)?; let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { self.push_row(frame); if i + 1 < len { - self.finish_region(vm)?; + self.finish_region(ctx)?; } } @@ -419,14 +419,19 @@ impl<'a> GridLayouter<'a> { /// 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, vm: &mut Vm, v: Linear, y: usize) -> TypResult<()> { + fn layout_linear_row( + &mut self, + ctx: &mut Context, + v: Linear, + y: usize, + ) -> TypResult<()> { let resolved = v.resolve(self.regions.base.y); - let frame = self.layout_single_row(vm, resolved, y)?; + let frame = self.layout_single_row(ctx, resolved, y)?; // Skip to fitting region. let height = frame.size.y; while !self.regions.first.y.fits(height) && !self.regions.in_last() { - self.finish_region(vm)?; + self.finish_region(ctx)?; // Don't skip multiple regions for gutter and don't push a row. if y % 2 == 1 { @@ -442,7 +447,7 @@ impl<'a> GridLayouter<'a> { /// Layout a row with fixed height and return its frame. fn layout_single_row( &self, - vm: &mut Vm, + ctx: &mut Context, height: Length, y: usize, ) -> TypResult<Frame> { @@ -460,7 +465,7 @@ impl<'a> GridLayouter<'a> { .select(self.regions.base, size); let pod = Regions::one(size, base, Spec::splat(true)); - let frame = node.layout(vm, &pod, self.styles)?.remove(0); + let frame = node.layout(ctx, &pod, self.styles)?.remove(0); output.push_frame(pos, frame); } @@ -473,7 +478,7 @@ impl<'a> GridLayouter<'a> { /// Layout a row spanning multiple regions. fn layout_multi_row( &self, - vm: &mut Vm, + ctx: &mut Context, heights: &[Length], y: usize, ) -> TypResult<Vec<Frame>> { @@ -486,7 +491,7 @@ impl<'a> GridLayouter<'a> { // Prepare regions. let size = Size::new(self.used.x, heights[0]); let mut pod = Regions::one(size, self.regions.base, Spec::splat(true)); - pod.backlog = heights[1 ..].to_vec().into_iter(); + pod.backlog = heights[1 ..].to_vec(); // Layout the row. let mut pos = Point::zero(); @@ -500,7 +505,7 @@ impl<'a> GridLayouter<'a> { } // Push the layouted frames into the individual output frames. - let frames = node.layout(vm, &pod, self.styles)?; + let frames = node.layout(ctx, &pod, self.styles)?; for (output, frame) in outputs.iter_mut().zip(frames) { output.push_frame(pos, frame); } @@ -520,7 +525,7 @@ impl<'a> GridLayouter<'a> { } /// Finish rows for one region. - fn finish_region(&mut self, vm: &mut Vm) -> TypResult<()> { + fn finish_region(&mut self, ctx: &mut Context) -> TypResult<()> { // Determine the size of the grid in this region, expanding fully if // there are fr rows. let mut size = self.used; @@ -539,7 +544,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(vm, height, y)? + self.layout_single_row(ctx, height, y)? } }; diff --git a/src/library/heading.rs b/src/library/heading.rs index 49975655..0c9c8e27 100644 --- a/src/library/heading.rs +++ b/src/library/heading.rs @@ -37,7 +37,7 @@ impl HeadingNode { /// Whether the heading is block-level. pub const BLOCK: Leveled<bool> = Leveled::Value(true); - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { body: args.expect("body")?, level: args.named("level")?.unwrap_or(1), @@ -46,16 +46,16 @@ impl HeadingNode { } impl Show for HeadingNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { macro_rules! resolve { ($key:expr) => { - styles.get_cloned($key).resolve(vm, self.level)? + styles.get_cloned($key).resolve(ctx, self.level)? }; } // Resolve the user recipe. let mut body = styles - .show(self, vm, [ + .show(self, ctx, [ Value::Int(self.level as i64), Value::Template(self.body.clone()), ])? @@ -124,13 +124,13 @@ pub enum Leveled<T> { impl<T: Cast> Leveled<T> { /// Resolve the value based on the level. - pub fn resolve(self, vm: &mut Vm, level: usize) -> TypResult<T> { + pub fn resolve(self, ctx: &mut Context, level: usize) -> TypResult<T> { Ok(match self { Self::Value(value) => value, Self::Mapping(mapping) => mapping(level), Self::Func(func, span) => { let args = Args::from_values(span, [Value::Int(level as i64)]); - func.call(vm, args)?.cast().at(span)? + func.call(ctx, args)?.cast().at(span)? } }) } diff --git a/src/library/hide.rs b/src/library/hide.rs index dcc73088..89aea6d3 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { - let mut frames = self.0.layout(vm, regions, styles)?; + let mut frames = self.0.layout(ctx, regions, styles)?; // Clear the frames. for frame in &mut frames { diff --git a/src/library/image.rs b/src/library/image.rs index 000aec73..c1220734 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(vm: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Template> { let path = args.expect::<Spanned<EcoString>>("path to image file")?; - let full = vm.resolve(&path.v); - let id = vm.images.load(&full).map_err(|err| { + let full = ctx.resolve(&path.v); + let id = ctx.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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { - let img = vm.images.get(self.0); + let img = ctx.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 41209549..4a153d46 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { url: args.expect::<EcoString>("url")?, body: args.find()?, @@ -30,12 +30,15 @@ impl LinkNode { } impl Show for LinkNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { let mut body = styles - .show(self, vm, [Value::Str(self.url.clone()), match &self.body { - Some(body) => Value::Template(body.clone()), - None => Value::None, - }])? + .show(self, ctx, [ + Value::Str(self.url.clone()), + match &self.body { + Some(body) => Value::Template(body.clone()), + None => Value::None, + }, + ])? .or_else(|| self.body.clone()) .unwrap_or_else(|| { let url = &self.url; diff --git a/src/library/list.rs b/src/library/list.rs index 37dda843..b757e4b2 100644 --- a/src/library/list.rs +++ b/src/library/list.rs @@ -37,7 +37,7 @@ impl<const L: ListKind> 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { start: args.named("start")?.unwrap_or(0), wide: args.named("wide")?.unwrap_or(false), @@ -51,10 +51,10 @@ impl<const L: ListKind> ListNode<L> { } impl<const L: ListKind> Show for ListNode<L> { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { if let Some(template) = styles.show( self, - vm, + ctx, self.items.iter().map(|item| Value::Template((*item.body).clone())), )? { return Ok(template); @@ -72,7 +72,7 @@ impl<const L: ListKind> Show for ListNode<L> { } children.push(LayoutNode::default()); - children.push(label.resolve(vm, L, number)?.pack()); + children.push(label.resolve(ctx, L, number)?.pack()); children.push(LayoutNode::default()); children.push((*item.body).clone().pack()); number += 1; @@ -135,7 +135,7 @@ impl Label { /// Resolve the value based on the level. pub fn resolve( &self, - vm: &mut Vm, + ctx: &mut Context, kind: ListKind, number: usize, ) -> TypResult<Template> { @@ -152,7 +152,7 @@ impl Label { Self::Template(template) => template.clone(), Self::Func(func, span) => { let args = Args::from_values(*span, [Value::Int(number as i64)]); - func.call(vm, args)?.cast().at(*span)? + func.call(ctx, args)?.cast().at(*span)? } }) } diff --git a/src/library/math.rs b/src/library/math.rs index 40a1990e..5d0ae41b 100644 --- a/src/library/math.rs +++ b/src/library/math.rs @@ -13,7 +13,7 @@ pub struct MathNode { #[class] impl MathNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { formula: args.expect("formula")?, display: args.named("display")?.unwrap_or(false), @@ -22,9 +22,9 @@ impl MathNode { } impl Show for MathNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { Ok(styles - .show(self, vm, [ + .show(self, ctx, [ Value::Str(self.formula.clone()), Value::Bool(self.display), ])? diff --git a/src/library/mod.rs b/src/library/mod.rs index 980f45c8..590dd331 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -74,7 +74,7 @@ pub mod prelude { pub use crate::geom::*; pub use crate::syntax::{Span, Spanned}; pub use crate::util::{EcoString, OptionExt}; - pub use crate::Vm; + pub use crate::Context; } use prelude::*; diff --git a/src/library/numbering.rs b/src/library/numbering.rs index f9031274..05a39e36 100644 --- a/src/library/numbering.rs +++ b/src/library/numbering.rs @@ -93,17 +93,17 @@ impl Numbering { } /// Converts an integer into one or multiple letters. -pub fn letter(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn letter(_: &mut Context, args: &mut Args) -> TypResult<Value> { convert(Numbering::Letter, args) } /// Converts an integer into a roman numeral. -pub fn roman(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn roman(_: &mut Context, args: &mut Args) -> TypResult<Value> { convert(Numbering::Roman, args) } /// Convert a number into a symbol. -pub fn symbol(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn symbol(_: &mut Context, args: &mut Args) -> TypResult<Value> { convert(Numbering::Symbol, args) } diff --git a/src/library/pad.rs b/src/library/pad.rs index 05b658bd..174322e4 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -13,7 +13,7 @@ pub struct PadNode { #[class] impl PadNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { let all = args.find()?; let hor = args.named("horizontal")?; let ver = args.named("vertical")?; @@ -30,13 +30,13 @@ impl PadNode { impl Layout for PadNode { fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { // Layout child into padded regions. let pod = regions.map(|size| shrink(size, self.padding)); - let mut frames = self.child.layout(vm, &pod, styles)?; + let mut frames = self.child.layout(ctx, &pod, styles)?; for frame in &mut frames { // Apply the padding inversely such that the grown size padded diff --git a/src/library/page.rs b/src/library/page.rs index 718234c6..5281c501 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -35,7 +35,7 @@ impl PageNode { /// The page's footer. pub const FOOTER: Marginal = Marginal::None; - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::Page(Self(args.expect("body")?))) } @@ -70,7 +70,7 @@ impl PageNode { /// Layout the page run into a sequence of frames, one per page. pub fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, mut page: usize, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -115,7 +115,7 @@ impl PageNode { // Layout the child. let regions = Regions::repeat(size, size, size.map(Length::is_finite)); - let mut frames = child.layout(vm, ®ions, styles)?; + let mut frames = child.layout(ctx, ®ions, styles)?; let header = styles.get_ref(Self::HEADER); let footer = styles.get_ref(Self::FOOTER); @@ -128,12 +128,12 @@ impl PageNode { (Length::zero(), padding.top, header), (size.y - padding.bottom, padding.bottom, footer), ] { - if let Some(template) = marginal.resolve(vm, page)? { + if let Some(template) = marginal.resolve(ctx, page)? { let pos = Point::new(padding.left, y); let w = size.x - padding.left - padding.right; let area = Size::new(w, h); let pod = Regions::one(area, area, area.map(Length::is_finite)); - let sub = template.layout(vm, &pod, styles)?.remove(0); + let sub = Layout::layout(&template, ctx, &pod, styles)?.remove(0); Arc::make_mut(frame).push_frame(pos, sub); } } @@ -158,7 +158,7 @@ pub struct PagebreakNode; #[class] impl PagebreakNode { - fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> { Ok(Template::Pagebreak) } } @@ -176,13 +176,13 @@ pub enum Marginal { impl Marginal { /// Resolve the marginal based on the page number. - pub fn resolve(&self, vm: &mut Vm, page: usize) -> TypResult<Option<Template>> { + pub fn resolve(&self, ctx: &mut Context, page: usize) -> TypResult<Option<Template>> { Ok(match self { Self::None => None, Self::Template(template) => Some(template.clone()), Self::Func(func, span) => { let args = Args::from_values(*span, [Value::Int(page as i64)]); - func.call(vm, args)?.cast().at(*span)? + func.call(ctx, args)?.cast().at(*span)? } }) } diff --git a/src/library/par.rs b/src/library/par.rs index eab67c53..e5122166 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -37,7 +37,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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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 @@ -83,7 +83,7 @@ impl ParNode { impl Layout for ParNode { fn layout( &self, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -94,8 +94,8 @@ 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 par = ParLayout::new(vm, self, bidi, regions, &styles)?; - let fonts = &mut *vm.fonts; + let par = ParLayout::new(ctx, self, bidi, regions, &styles)?; + let fonts = &mut ctx.fonts; let em = styles.get(TextNode::SIZE).abs; let align = styles.get(ParNode::ALIGN); let leading = styles.get(ParNode::LEADING).resolve(em); @@ -242,7 +242,7 @@ pub struct ParbreakNode; #[class] impl ParbreakNode { - fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> { Ok(Template::Parbreak) } } @@ -252,7 +252,7 @@ pub struct LinebreakNode; #[class] impl LinebreakNode { - fn construct(_: &mut Vm, _: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> { Ok(Template::Linebreak) } } @@ -286,7 +286,7 @@ enum ParItem<'a> { impl<'a> ParLayout<'a> { /// Prepare initial shaped text and layouted children. fn new( - vm: &mut Vm, + ctx: &mut Context, par: &'a ParNode, bidi: BidiInfo<'a>, regions: &Regions, @@ -307,7 +307,7 @@ impl<'a> ParLayout<'a> { cursor += count; let subrange = start .. cursor; let text = &bidi.text[subrange.clone()]; - let shaped = shape(vm.fonts, text, styles, level.dir()); + let shaped = shape(&mut ctx.fonts, text, styles, level.dir()); items.push(ParItem::Text(shaped)); ranges.push(subrange); } @@ -326,7 +326,7 @@ impl<'a> ParLayout<'a> { ParChild::Node(node) => { let size = Size::new(regions.first.x, regions.base.y); let pod = Regions::one(size, regions.base, Spec::splat(false)); - let frame = node.layout(vm, &pod, styles)?.remove(0); + let frame = node.layout(ctx, &pod, styles)?.remove(0); items.push(ParItem::Frame(Arc::take(frame))); ranges.push(range); } diff --git a/src/library/place.rs b/src/library/place.rs index 34746d5b..00e72dfb 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -37,7 +37,7 @@ impl Layout for PlaceNode { Regions::one(regions.base, regions.base, expand) }; - let mut frames = self.0.layout(vm, &pod, styles)?; + let mut frames = self.0.layout(ctx, &pod, styles)?; // If expansion is off, zero all sizes so that we don't take up any // space in our parent. Otherwise, respect the expand settings. diff --git a/src/library/raw.rs b/src/library/raw.rs index bb4e2c96..785eeac5 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self { text: args.expect("text")?, block: args.named("block")?.unwrap_or(false), @@ -40,10 +40,10 @@ impl RawNode { } impl Show for RawNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { let lang = styles.get_ref(Self::LANG).as_ref(); - if let Some(template) = styles.show(self, vm, [ + if let Some(template) = styles.show(self, ctx, [ Value::Str(self.text.clone()), match lang { Some(lang) => Value::Str(lang.clone()), diff --git a/src/library/shape.rs b/src/library/shape.rs index 955aef56..bf159c52 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<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.first, regions.base, regions.expand); - frames = child.layout(vm, &pod, styles)?; + frames = child.layout(ctx, &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.first = Size::splat(length); pod.expand = Spec::splat(true); - frames = child.layout(vm, &pod, styles)?; + frames = child.layout(ctx, &pod, styles)?; } } else { // The default size that a shape takes on if it has no child and diff --git a/src/library/spacing.rs b/src/library/spacing.rs index f9676d4c..6f57c71c 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -7,7 +7,7 @@ pub struct HNode; #[class] impl HNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::Horizontal(args.expect("spacing")?)) } } @@ -17,7 +17,7 @@ pub struct VNode; #[class] impl VNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::Vertical(args.expect("spacing")?)) } } diff --git a/src/library/stack.rs b/src/library/stack.rs index 6768a0a0..8e463f6a 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -16,7 +16,7 @@ pub struct StackNode { #[class] impl StackNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { @@ -48,7 +48,7 @@ impl Layout for StackNode { layouter.layout_spacing(kind); } - layouter.layout_node(vm, node, styles)?; + layouter.layout_node(ctx, node, styles)?; deferred = self.spacing; } } @@ -165,7 +165,7 @@ impl StackLayouter { /// Layout an arbitrary node. pub fn layout_node( &mut self, - vm: &mut Vm, + ctx: &mut Context, node: &LayoutNode, styles: StyleChain, ) -> TypResult<()> { @@ -179,7 +179,7 @@ impl StackLayouter { .and_then(|node| node.aligns.get(self.axis)) .unwrap_or(self.dir.start().into()); - let frames = node.layout(vm, &self.regions, styles)?; + let frames = node.layout(ctx, &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 f4de0f55..07f28737 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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(); @@ -55,10 +55,10 @@ impl TableNode { } impl Show for TableNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { if let Some(template) = styles.show( self, - vm, + ctx, self.children.iter().map(|child| Value::Template(child.clone())), )? { return Ok(template); diff --git a/src/library/text.rs b/src/library/text.rs index ca903d1f..ba7f4c20 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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,15 +117,15 @@ pub struct StrongNode(pub Template); #[class] impl StrongNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self(args.expect("body")?))) } } impl Show for StrongNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { Ok(styles - .show(self, vm, [Value::Template(self.0.clone())])? + .show(self, ctx, [Value::Template(self.0.clone())])? .unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, true))) } } @@ -136,15 +136,15 @@ pub struct EmphNode(pub Template); #[class] impl EmphNode { - fn construct(_: &mut Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> { Ok(Template::show(Self(args.expect("body")?))) } } impl Show for EmphNode { - fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> { Ok(styles - .show(self, vm, [Value::Template(self.0.clone())])? + .show(self, ctx, [Value::Template(self.0.clone())])? .unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, true))) } } diff --git a/src/library/transform.rs b/src/library/transform.rs index 84553fb5..897f3144 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 Vm, args: &mut Args) -> TypResult<Template> { + fn construct(_: &mut Context, 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, - vm: &mut Vm, + ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult<Vec<Arc<Frame>>> { let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); - let mut frames = self.child.layout(vm, regions, styles)?; + let mut frames = self.child.layout(ctx, regions, styles)?; for 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 f7f46a8f..051dd885 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 Vm, args: &mut Args) -> TypResult<Value> { +pub fn assert(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// The name of a value's type. -pub fn type_(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn type_(_: &mut Context, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<Value>("value")?.type_name().into()) } /// The string representation of a value. -pub fn repr(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn repr(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { +pub fn join(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// Convert a value to a integer. -pub fn int(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn int(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// Convert a value to a float. -pub fn float(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn float(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// Try to convert a value to a string. -pub fn str(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn str(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// Create an RGB(A) color. -pub fn rgb(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn rgb(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// Create a CMYK color. -pub fn cmyk(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn cmyk(_: &mut Context, args: &mut Args) -> TypResult<Value> { struct Component(u8); castable! { @@ -141,7 +141,7 @@ pub fn cmyk(_: &mut Vm, args: &mut Args) -> TypResult<Value> { } /// The absolute value of a numeric value. -pub fn abs(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn abs(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { } /// The minimum of a sequence of values. -pub fn min(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn min(_: &mut Context, args: &mut Args) -> TypResult<Value> { minmax(args, Ordering::Less) } /// The maximum of a sequence of values. -pub fn max(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn max(_: &mut Context, args: &mut Args) -> TypResult<Value> { minmax(args, Ordering::Greater) } /// Whether an integer is even. -pub fn even(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn even(_: &mut Context, args: &mut Args) -> TypResult<Value> { Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0)) } /// Whether an integer is odd. -pub fn odd(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn odd(_: &mut Context, args: &mut Args) -> TypResult<Value> { Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0)) } /// The modulo of two numbers. -pub fn modulo(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn modulo(_: &mut Context, 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 Vm, args: &mut Args) -> TypResult<Value> { +pub fn range(_: &mut Context, args: &mut Args) -> TypResult<Value> { let first = args.expect::<i64>("end")?; let (start, end) = match args.eat::<i64>()? { Some(second) => (first, second), @@ -252,17 +252,17 @@ pub fn range(_: &mut Vm, args: &mut Args) -> TypResult<Value> { } /// Convert a string to lowercase. -pub fn lower(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn lower(_: &mut Context, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<EcoString>("string")?.to_lowercase().into()) } /// Convert a string to uppercase. -pub fn upper(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn upper(_: &mut Context, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<EcoString>("string")?.to_uppercase().into()) } /// The length of a string, an array or a dictionary. -pub fn len(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn len(_: &mut Context, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect("collection")?; Ok(Value::Int(match v { Value::Str(v) => v.len() as i64, @@ -277,7 +277,7 @@ pub fn len(_: &mut Vm, args: &mut Args) -> TypResult<Value> { } /// The sorted version of an array. -pub fn sorted(_: &mut Vm, args: &mut Args) -> TypResult<Value> { +pub fn sorted(_: &mut Context, args: &mut Args) -> TypResult<Value> { let Spanned { v, span } = args.expect::<Spanned<Array>>("array")?; Ok(Value::Array(v.sorted().at(span)?)) } diff --git a/src/source.rs b/src/source.rs index 2a134d9a..9f2a0140 100644 --- a/src/source.rs +++ b/src/source.rs @@ -53,7 +53,11 @@ impl SourceStore { } /// Load a source file from a path using the `loader`. - pub fn load(&mut self, path: &Path) -> io::Result<SourceId> { + /// + /// If there already exists a source file for this path, it is + /// [replaced](SourceFile::replace). + pub fn load(&mut self, path: impl AsRef<Path>) -> io::Result<SourceId> { + let path = path.as_ref(); let hash = self.loader.resolve(path)?; if let Some(&id) = self.files.get(&hash) { return Ok(id); @@ -74,13 +78,14 @@ impl SourceStore { /// will use the inserted file instead of going through [`Loader::load`]. /// /// If the path is resolvable and points to an existing source file, it is - /// overwritten. - pub fn provide(&mut self, path: &Path, src: String) -> SourceId { + /// [replaced](SourceFile::replace). + pub fn provide(&mut self, path: impl AsRef<Path>, src: String) -> SourceId { + let path = path.as_ref(); let hash = self.loader.resolve(path).ok(); // Check for existing file and replace if one exists. if let Some(&id) = hash.and_then(|hash| self.files.get(&hash)) { - self.sources[id.0 as usize] = SourceFile::new(id, path, src); + self.replace(id, src); return id; } @@ -98,19 +103,24 @@ impl SourceStore { /// Get a reference to a loaded source file. /// - /// This panics if no source file with this `id` exists. This function - /// should only be called with ids returned by this store's - /// [`load()`](Self::load) and [`provide()`](Self::provide) methods. + /// This panics if no source file with this `id` exists. #[track_caller] pub fn get(&self, id: SourceId) -> &SourceFile { &self.sources[id.0 as usize] } - /// Edit a source file by replacing the given range. + /// Fully [replace](SourceFile::replace) the source text of a file. /// - /// Returns the range of the section in the new source that was ultimately - /// reparsed. This panics if no source file with this `id` exists or if the - /// `replace` range is out of bounds. + /// This panics if no source file with this `id` exists. + #[track_caller] + pub fn replace(&mut self, id: SourceId, src: String) { + self.sources[id.0 as usize].replace(src) + } + + /// [Edit](SourceFile::edit) a source file by replacing the given range. + /// + /// This panics if no source file with this `id` exists or if the `replace` + /// range is out of bounds. #[track_caller] pub fn edit( &mut self, @@ -132,6 +142,7 @@ pub struct SourceFile { src: String, lines: Vec<Line>, root: Arc<GreenNode>, + rev: usize, } impl SourceFile { @@ -145,6 +156,7 @@ impl SourceFile { root: parse(&src), src, lines, + rev: 0, } } @@ -189,11 +201,70 @@ impl SourceFile { &self.src } - /// Slice out the part of the source code enclosed by the span. + /// The revision number of the file. + /// + /// This is increased on [replacements](Self::replace) and + /// [edits](Self::edit). + pub fn rev(&self) -> usize { + self.rev + } + + /// Slice out the part of the source code enclosed by the range. pub fn get(&self, range: Range<usize>) -> Option<&str> { self.src.get(range) } + /// Fully replace the source text and increase the revision number. + pub fn replace(&mut self, src: String) { + self.src = src; + self.lines = vec![Line { byte_idx: 0, utf16_idx: 0 }]; + self.lines.extend(Line::iter(0, 0, &self.src)); + self.root = parse(&self.src); + self.rev = self.rev.wrapping_add(1); + } + + /// Edit the source file by replacing the given range and increase the + /// revision number. + /// + /// Returns the range of the section in the new source that was ultimately + /// reparsed. + /// + /// The method panics if the `replace` range is out of bounds. + pub fn edit(&mut self, replace: Range<usize>, with: &str) -> Range<usize> { + self.rev = self.rev.wrapping_add(1); + + let start_byte = replace.start; + let start_utf16 = self.byte_to_utf16(replace.start).unwrap(); + self.src.replace_range(replace.clone(), with); + + // Remove invalidated line starts. + let line = self.byte_to_line(start_byte).unwrap(); + self.lines.truncate(line + 1); + + // Handle adjoining of \r and \n. + if self.src[.. start_byte].ends_with('\r') && with.starts_with('\n') { + self.lines.pop(); + } + + // Recalculate the line starts after the edit. + self.lines.extend(Line::iter( + start_byte, + start_utf16, + &self.src[start_byte ..], + )); + + // Incrementally reparse the replaced range. + Reparser::new(&self.src, replace, with.len()).reparse(&mut self.root) + } + + /// Provide highlighting categories for the given range of the source file. + pub fn highlight<F>(&self, range: Range<usize>, mut f: F) + where + F: FnMut(Range<usize>, Category), + { + syntax::highlight(self.red().as_ref(), range, &mut f) + } + /// Get the length of the file in bytes. pub fn len_bytes(&self) -> usize { self.src.len() @@ -289,43 +360,6 @@ impl SourceFile { } Some(range.start + (line.len() - chars.as_str().len())) } - - /// Edit the source file by replacing the given range. - /// - /// Returns the range of the section in the new source that was ultimately - /// reparsed. The method panics if the `replace` range is out of bounds. - pub fn edit(&mut self, replace: Range<usize>, with: &str) -> Range<usize> { - let start_byte = replace.start; - let start_utf16 = self.byte_to_utf16(replace.start).unwrap(); - self.src.replace_range(replace.clone(), with); - - // Remove invalidated line starts. - let line = self.byte_to_line(start_byte).unwrap(); - self.lines.truncate(line + 1); - - // Handle adjoining of \r and \n. - if self.src[.. start_byte].ends_with('\r') && with.starts_with('\n') { - self.lines.pop(); - } - - // Recalculate the line starts after the edit. - self.lines.extend(Line::iter( - start_byte, - start_utf16, - &self.src[start_byte ..], - )); - - // Incrementally reparse the replaced range. - Reparser::new(&self.src, replace, with.len()).reparse(&mut self.root) - } - - /// Provide highlighting categories for the given range of the source file. - pub fn highlight<F>(&self, range: Range<usize>, mut f: F) - where - F: FnMut(Range<usize>, Category), - { - syntax::highlight(self.red().as_ref(), range, &mut f) - } } /// Metadata about a line. diff --git a/tests/typ/style/show.typ b/tests/typ/style/show.typ index 1055f9c7..734ef931 100644 --- a/tests/typ/style/show.typ +++ b/tests/typ/style/show.typ @@ -44,9 +44,10 @@ A [= Heading] C = Heading --- -// Error: 1-29 show rule is recursive -#show strong(x) as strong(x) -*Hi* +// Ref: false +// // Error: 1-29 show rule is recursive +// #show strong(x) as strong(x) +// *Hi* --- // Error: 2-19 set, show and wrap are only allowed directly in markup diff --git a/tests/typeset.rs b/tests/typeset.rs index e0530f9b..1b45a975 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -17,7 +17,7 @@ use typst::loading::FsLoader; use typst::parse::Scanner; use typst::source::SourceFile; use typst::syntax::Span; -use typst::{Context, Vm}; +use typst::Context; const TYP_DIR: &str = "./typ"; const REF_DIR: &str = "./ref"; @@ -267,8 +267,7 @@ fn test_part( ok &= test_reparse(ctx.sources.get(id).src(), i, rng); - let mut vm = Vm::new(ctx); - let (mut frames, mut errors) = match vm.typeset(id) { + let (mut frames, mut errors) = match ctx.typeset(id) { Ok(frames) => (frames, vec![]), Err(errors) => (vec![], *errors), }; |
