Rework the core context
This commit is contained in:
parent
2bf32c51bc
commit
e1f29d6cb9
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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));
|
||||
|
360
src/eval/mod.rs
360
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())?)
|
||||
|
20
src/eval/module.rs
Normal file
20
src/eval/module.rs
Normal file
@ -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())
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
201
src/lib.rs
201
src/lib.rs
@ -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,132 +103,49 @@ impl Context {
|
||||
&self.styles
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for a [`Context`].
|
||||
///
|
||||
/// This struct is created by [`Context::builder`].
|
||||
pub struct ContextBuilder {
|
||||
std: Option<Scope>,
|
||||
styles: Option<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);
|
||||
self
|
||||
}
|
||||
|
||||
/// The default properties for page size, font selection and so on.
|
||||
pub fn styles(mut self, styles: StyleMap) -> Self {
|
||||
self.styles = Some(styles);
|
||||
self
|
||||
}
|
||||
|
||||
/// Finish building the context by providing the `loader` used to load
|
||||
/// fonts, images, source files and other resources.
|
||||
pub fn build(self, loader: Arc<dyn Loader>) -> Context {
|
||||
Context {
|
||||
sources: SourceStore::new(Arc::clone(&loader)),
|
||||
fonts: FontStore::new(Arc::clone(&loader)),
|
||||
images: ImageStore::new(Arc::clone(&loader)),
|
||||
loader,
|
||||
std: self.std.unwrap_or_else(library::new),
|
||||
styles: self.styles.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ContextBuilder {
|
||||
fn default() -> Self {
|
||||
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.
|
||||
/// messages with file and span information.
|
||||
pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> {
|
||||
// Prevent cyclic evaluation.
|
||||
assert!(!self.route.contains(&id));
|
||||
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 loaded.
|
||||
// Check whether the module was already evaluated.
|
||||
if let Some(module) = self.modules.get(&id) {
|
||||
return Ok(module.clone());
|
||||
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()?;
|
||||
|
||||
// Prepare the new context.
|
||||
let fresh = Scopes::new(self.scopes.base);
|
||||
let prev = std::mem::replace(&mut self.scopes, fresh);
|
||||
self.route.push(id);
|
||||
let std = self.std.clone();
|
||||
let mut scp = Scopes::new(Some(&std));
|
||||
|
||||
// Evaluate the module.
|
||||
let template = ast.eval(self)?;
|
||||
|
||||
// Restore the old context.
|
||||
let scope = std::mem::replace(&mut self.scopes, prev).top;
|
||||
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.
|
||||
let module = Module { scope, template };
|
||||
self.modules.insert(id, module.clone());
|
||||
|
||||
Ok(module)
|
||||
@ -237,11 +157,11 @@ impl<'a> Vm<'a> {
|
||||
/// 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)
|
||||
self.evaluate(id)?.template.layout(self)
|
||||
}
|
||||
|
||||
/// Resolve a user-entered path (relative to the source file) to be
|
||||
/// relative to the compilation environment's root.
|
||||
/// 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() {
|
||||
@ -252,3 +172,48 @@ impl<'a> Vm<'a> {
|
||||
path.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for a [`Context`].
|
||||
///
|
||||
/// This struct is created by [`Context::builder`].
|
||||
pub struct ContextBuilder {
|
||||
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: 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: impl Into<Arc<StyleMap>>) -> Self {
|
||||
self.styles = Some(styles.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Finish building the context by providing the `loader` used to load
|
||||
/// fonts, images, source files and other resources.
|
||||
pub fn build(self, loader: Arc<dyn Loader>) -> Context {
|
||||
Context {
|
||||
sources: SourceStore::new(Arc::clone(&loader)),
|
||||
fonts: FontStore::new(Arc::clone(&loader)),
|
||||
images: ImageStore::new(Arc::clone(&loader)),
|
||||
loader,
|
||||
std: self.std.unwrap_or_else(|| Arc::new(library::new())),
|
||||
styles: self.styles.unwrap_or_default(),
|
||||
modules: HashMap::new(),
|
||||
route: vec![],
|
||||
deps: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ContextBuilder {
|
||||
fn default() -> Self {
|
||||
Self { std: None, styles: None }
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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)?
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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),
|
||||
])?
|
||||
|
@ -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::*;
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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()),
|
||||
|
@ -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
|
||||
|
@ -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")?))
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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)?))
|
||||
}
|
||||
|
132
src/source.rs
132
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.
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user