diff options
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/capture.rs | 13 | ||||
| -rw-r--r-- | src/eval/content.rs | 23 | ||||
| -rw-r--r-- | src/eval/mod.rs | 25 | ||||
| -rw-r--r-- | src/eval/raw.rs | 1 | ||||
| -rw-r--r-- | src/eval/show.rs | 28 | ||||
| -rw-r--r-- | src/eval/styles.rs | 21 |
6 files changed, 80 insertions, 31 deletions
diff --git a/src/eval/capture.rs b/src/eval/capture.rs index 4e8d7604..8e54122f 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -88,6 +88,14 @@ impl<'a> CapturesVisitor<'a> { self.bind(expr.binding()); } + // A show rule contains a binding, but that binding is only active + // after the target has been evaluated. + Some(Expr::Show(show)) => { + self.visit(show.target().as_red()); + self.bind(show.binding()); + self.visit(show.body().as_red()); + } + // A for loop contains one or two bindings in its pattern. These are // active after the iterable is evaluated but before the body is // evaluated. @@ -162,6 +170,11 @@ mod tests { test("{(..x) => x + y}", &["y"]); test("{(x, y: x + z) => x + y}", &["x", "z"]); + // Show rule. + test("#show x: y as x", &["y"]); + test("#show x: y as x + z", &["y", "z"]); + test("#show x: x as x", &["x"]); + // For loop. test("#for x in y { x + z }", &["y", "z"]); test("#for x, y in y { x + y }", &["y"]); diff --git a/src/eval/content.rs b/src/eval/content.rs index 605abe51..098bfbfc 100644 --- a/src/eval/content.rs +++ b/src/eval/content.rs @@ -320,7 +320,7 @@ struct ListBuilder<'a> { styles: StyleChain<'a>, kind: ListKind, items: Vec<ListItem>, - wide: bool, + tight: bool, staged: Vec<(&'a Content, StyleChain<'a>)>, } @@ -356,15 +356,15 @@ impl<'a> Builder<'a> { return Ok(()); } Content::List(item) if builder.kind == UNORDERED => { - builder.wide |= - builder.staged.iter().any(|&(t, _)| *t == Content::Parbreak); + builder.tight &= + builder.staged.iter().all(|&(t, _)| *t != Content::Parbreak); builder.staged.clear(); builder.items.push(item.clone()); return Ok(()); } Content::Enum(item) if builder.kind == ORDERED => { - builder.wide |= - builder.staged.iter().any(|&(t, _)| *t == Content::Parbreak); + builder.tight &= + builder.staged.iter().all(|&(t, _)| *t != Content::Parbreak); builder.staged.clear(); builder.items.push(item.clone()); return Ok(()); @@ -430,7 +430,7 @@ impl<'a> Builder<'a> { styles, kind: UNORDERED, items: vec![item.clone()], - wide: false, + tight: true, staged: vec![], }); } @@ -439,7 +439,7 @@ impl<'a> Builder<'a> { styles, kind: ORDERED, items: vec![item.clone()], - wide: false, + tight: true, staged: vec![], }); } @@ -454,7 +454,8 @@ impl<'a> Builder<'a> { } Content::Show(node) => { let id = node.id(); - let content = node.show(ctx, styles)?; + let realized = styles.realize(ctx, node)?; + let content = node.show(ctx, styles, realized)?; let stored = self.tpa.alloc(content); self.process(ctx, stored, styles.unscoped(id))?; } @@ -532,14 +533,14 @@ impl<'a> Builder<'a> { /// Finish the currently built list. fn finish_list(&mut self, ctx: &mut Context) -> TypResult<()> { - let ListBuilder { styles, kind, items, wide, staged } = match self.list.take() { + let ListBuilder { styles, kind, items, tight, staged } = match self.list.take() { Some(list) => list, None => return Ok(()), }; let content = match kind { - UNORDERED => Content::show(ListNode::<UNORDERED> { start: 1, wide, items }), - ORDERED | _ => Content::show(ListNode::<ORDERED> { start: 1, wide, items }), + UNORDERED => Content::show(ListNode::<UNORDERED> { start: 1, tight, items }), + ORDERED | _ => Content::show(ListNode::<ORDERED> { start: 1, tight, items }), }; let stored = self.tpa.alloc(content); diff --git a/src/eval/mod.rs b/src/eval/mod.rs index d9651cce..7e0d3b15 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -624,13 +624,30 @@ impl Eval for ShowExpr { type Output = StyleMap; fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { + // Evaluate the target function. let target = self.target(); let target_span = target.span(); let target = target.eval(ctx, scp)?.cast::<Func>().at(target_span)?; - let recipe = self.recipe(); - let recipe_span = recipe.span(); - let recipe = recipe.eval(ctx, scp)?.cast::<Func>().at(recipe_span)?; - Ok(target.show(recipe, recipe_span).at(target_span)?) + + // Collect captured variables. + let captured = { + let mut visitor = CapturesVisitor::new(scp); + visitor.visit(self.as_red()); + visitor.finish() + }; + + // Define the recipe function. + let body = self.body(); + let body_span = body.span(); + let recipe = Func::from_closure(Closure { + name: None, + captured, + params: vec![(self.binding().take(), None)], + sink: None, + body, + }); + + Ok(target.show(recipe, body_span).at(target_span)?) } } diff --git a/src/eval/raw.rs b/src/eval/raw.rs index a83c363f..6792c491 100644 --- a/src/eval/raw.rs +++ b/src/eval/raw.rs @@ -110,7 +110,6 @@ impl Resolve for RawStroke { } } -// This faciliates RawStroke => Stroke. impl Fold for RawStroke<Length> { type Output = Self; diff --git a/src/eval/show.rs b/src/eval/show.rs index 383497ba..f8d98d52 100644 --- a/src/eval/show.rs +++ b/src/eval/show.rs @@ -3,15 +3,24 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::sync::Arc; -use super::{Content, StyleChain}; +use super::{Content, Dict, StyleChain}; use crate::diag::TypResult; use crate::util::Prehashed; use crate::Context; /// A node that can be realized given some styles. pub trait Show: 'static { - /// Realize this node in the given styles. - fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content>; + /// Encode this node into a dictionary. + fn encode(&self) -> Dict; + + /// Show this node in the given styles and optionally given the realization + /// of a show rule. + fn show( + &self, + ctx: &mut Context, + styles: StyleChain, + realized: Option<Content>, + ) -> TypResult<Content>; /// Convert to a packed show node. fn pack(self) -> ShowNode @@ -42,8 +51,17 @@ impl ShowNode { } impl Show for ShowNode { - fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { - self.0.show(ctx, styles) + fn encode(&self) -> Dict { + self.0.encode() + } + + fn show( + &self, + ctx: &mut Context, + styles: StyleChain, + realized: Option<Content>, + ) -> TypResult<Content> { + self.0.show(ctx, styles, realized) } fn pack(self) -> ShowNode { diff --git a/src/eval/styles.rs b/src/eval/styles.rs index f147d8cf..b666c85d 100644 --- a/src/eval/styles.rs +++ b/src/eval/styles.rs @@ -4,7 +4,7 @@ use std::hash::Hash; use std::marker::PhantomData; use std::sync::Arc; -use super::{Args, Content, Func, Layout, Node, Smart, Span, Value}; +use super::{Args, Content, Func, Layout, Node, Show, ShowNode, Smart, Span, Value}; use crate::diag::{At, TypResult}; use crate::geom::{Numeric, Relative, Sides, Spec}; use crate::library::layout::PageNode; @@ -510,19 +510,20 @@ impl<'a> StyleChain<'a> { K::get(self, self.values(key)) } - /// Execute and return the result of a user recipe for a node if there is - /// any. - pub fn show<T, I>(self, ctx: &mut Context, values: I) -> TypResult<Option<Content>> - where - T: Node, - I: IntoIterator<Item = Value>, - { + /// Realize a node with a user recipe. + pub fn realize( + self, + ctx: &mut Context, + node: &ShowNode, + ) -> TypResult<Option<Content>> { + let id = node.id(); if let Some(recipe) = self .entries() .filter_map(Entry::recipe) - .find(|recipe| recipe.node == TypeId::of::<T>()) + .find(|recipe| recipe.node == id) { - let args = Args::from_values(recipe.span, values); + let dict = node.encode(); + let args = Args::from_values(recipe.span, [Value::Dict(dict)]); Ok(Some(recipe.func.call(ctx, args)?.cast().at(recipe.span)?)) } else { Ok(None) |
