summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-23 21:55:58 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-23 21:55:58 +0200
commit04fb8b288aa7c80607da79db7d085a4820b95a9d (patch)
tree7ca96d09d511274ebac298c329d5eef53a290d9c /src/eval
parent7a2cc3e7d29d16c5cf9b5a93a688e14da93c8662 (diff)
Show rules with type ascribed object
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/capture.rs13
-rw-r--r--src/eval/content.rs23
-rw-r--r--src/eval/mod.rs25
-rw-r--r--src/eval/raw.rs1
-rw-r--r--src/eval/show.rs28
-rw-r--r--src/eval/styles.rs21
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)