summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-01-07 21:24:36 +0100
committerLaurenz <laurmaedje@gmail.com>2022-01-08 00:20:48 +0100
commite74ae6ce70d4c6ca006613eadf07f920951789e3 (patch)
tree0b9b2ddabf79dad8d55631780ee5d70afe7362d7 /src/eval
parent0b624390906e911bde325b487b2710b67c8205c8 (diff)
Make all nodes into classes
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/mod.rs10
-rw-r--r--src/eval/node.rs77
-rw-r--r--src/eval/value.rs8
3 files changed, 62 insertions, 33 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index e8c8fcd2..c16c2208 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -248,7 +248,7 @@ impl Eval for ListNode {
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
Ok(Node::block(library::ListNode {
child: self.body().eval(ctx)?.into_block(),
- labelling: library::Unordered,
+ kind: library::Unordered,
}))
}
}
@@ -259,7 +259,7 @@ impl Eval for EnumNode {
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
Ok(Node::block(library::ListNode {
child: self.body().eval(ctx)?.into_block(),
- labelling: library::Ordered(self.number()),
+ kind: library::Ordered(self.number()),
}))
}
}
@@ -450,6 +450,7 @@ impl Eval for CallExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
+ let span = self.callee().span();
let callee = self.callee().eval(ctx)?;
let mut args = self.args().eval(ctx)?;
@@ -470,13 +471,14 @@ impl Eval for CallExpr {
}
Value::Class(class) => {
- let node = class.construct(ctx, &mut args)?;
+ let point = || Tracepoint::Call(Some(class.name().to_string()));
+ let node = class.construct(ctx, &mut args).trace(point, self.span())?;
args.finish()?;
Ok(Value::Node(node))
}
v => bail!(
- self.callee().span(),
+ span,
"expected callable or collection, found {}",
v.type_name(),
),
diff --git a/src/eval/node.rs b/src/eval/node.rs
index ecbee8ee..d909fc7d 100644
--- a/src/eval/node.rs
+++ b/src/eval/node.rs
@@ -10,7 +10,7 @@ use crate::diag::StrResult;
use crate::geom::SpecAxis;
use crate::layout::{Layout, PackedNode, RootNode};
use crate::library::{
- FlowChild, FlowNode, PageNode, ParChild, ParNode, PlacedNode, SpacingKind, TextNode,
+ FlowChild, FlowNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, TextNode,
};
use crate::util::EcoString;
@@ -98,6 +98,10 @@ impl Node {
/// Style this node with a full style map.
pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
+ if styles.is_empty() {
+ return self;
+ }
+
if let Self::Sequence(vec) = &mut self {
if let [styled] = vec.as_mut_slice() {
styled.map.apply(&styles);
@@ -193,7 +197,7 @@ impl Packer {
/// Finish up and return the resulting flow.
fn into_block(mut self) -> PackedNode {
- self.parbreak(None);
+ self.parbreak(None, false);
FlowNode(self.flow.children).pack()
}
@@ -209,7 +213,7 @@ impl Packer {
Node::Space => {
// A text space is "soft", meaning that it can be eaten up by
// adjacent line breaks or explicit spacings.
- self.par.last.soft(Styled::new(ParChild::text(' '), styles));
+ self.par.last.soft(Styled::new(ParChild::text(' '), styles), false);
}
Node::Linebreak => {
// A line break eats up surrounding text spaces.
@@ -222,12 +226,12 @@ impl Packer {
// styles (`Some(_)`) whereas paragraph breaks forced by
// incompatibility take their styles from the preceding
// paragraph.
- self.parbreak(Some(styles));
+ self.parbreak(Some(styles), true);
}
Node::Colbreak => {
// Explicit column breaks end the current paragraph and then
// discards the paragraph break.
- self.parbreak(None);
+ self.parbreak(None, false);
self.make_flow_compatible(&styles);
self.flow.children.push(Styled::new(FlowChild::Skip, styles));
self.flow.last.hard();
@@ -252,7 +256,7 @@ impl Packer {
Node::Spacing(SpecAxis::Vertical, kind) => {
// Explicit vertical spacing ends the current paragraph and then
// discards the paragraph break.
- self.parbreak(None);
+ self.parbreak(None, false);
self.make_flow_compatible(&styles);
self.flow.children.push(Styled::new(FlowChild::Spacing(kind), styles));
self.flow.last.hard();
@@ -284,14 +288,15 @@ impl Packer {
/// Insert an inline-level element into the current paragraph.
fn push_inline(&mut self, child: Styled<ParChild>) {
- if let Some(styled) = self.par.last.any() {
- self.push_coalescing(styled);
- }
-
// The node must be both compatible with the current page and the
// current paragraph.
self.make_flow_compatible(&child.map);
self.make_par_compatible(&child.map);
+
+ if let Some(styled) = self.par.last.any() {
+ self.push_coalescing(styled);
+ }
+
self.push_coalescing(child);
self.par.last.any();
}
@@ -314,13 +319,13 @@ impl Packer {
/// Insert a block-level element into the current flow.
fn push_block(&mut self, node: Styled<PackedNode>) {
- let placed = node.item.is::<PlacedNode>();
+ let placed = node.item.is::<PlaceNode>();
- self.parbreak(None);
+ self.parbreak(Some(node.map.clone()), false);
self.make_flow_compatible(&node.map);
self.flow.children.extend(self.flow.last.any());
self.flow.children.push(node.map(FlowChild::Node));
- self.parbreak(None);
+ self.parbreak(None, false);
// Prevent paragraph spacing between the placed node and the paragraph
// below it.
@@ -330,18 +335,13 @@ impl Packer {
}
/// Advance to the next paragraph.
- fn parbreak(&mut self, break_styles: Option<StyleMap>) {
+ fn parbreak(&mut self, break_styles: Option<StyleMap>, important: bool) {
// Erase any styles that will be inherited anyway.
let Builder { mut children, styles, .. } = mem::take(&mut self.par);
for Styled { map, .. } in &mut children {
map.erase(&styles);
}
- // For explicit paragraph breaks, `break_styles` is already `Some(_)`.
- // For page breaks due to incompatibility, we fall back to the styles
- // of the preceding paragraph.
- let break_styles = break_styles.unwrap_or_else(|| styles.clone());
-
// We don't want empty paragraphs.
if !children.is_empty() {
// The paragraph's children are all compatible with the page, so the
@@ -352,14 +352,30 @@ impl Packer {
self.flow.children.push(Styled::new(FlowChild::Node(par), styles));
}
+ // Actually styled breaks have precedence over whatever was before.
+ if break_styles.is_some() {
+ if let Last::Soft(_, false) = self.flow.last {
+ self.flow.last = Last::Any;
+ }
+ }
+
+ // For explicit paragraph breaks, `break_styles` is already `Some(_)`.
+ // For page breaks due to incompatibility, we fall back to the styles
+ // of the preceding thing.
+ let break_styles = break_styles
+ .or_else(|| self.flow.children.last().map(|styled| styled.map.clone()))
+ .unwrap_or_default();
+
// Insert paragraph spacing.
- self.flow.last.soft(Styled::new(FlowChild::Break, break_styles));
+ self.flow
+ .last
+ .soft(Styled::new(FlowChild::Break, break_styles), important);
}
/// Advance to the next page.
fn pagebreak(&mut self) {
if self.top {
- self.parbreak(None);
+ self.parbreak(None, false);
// Take the flow and erase any styles that will be inherited anyway.
let Builder { mut children, styles, .. } = mem::take(&mut self.flow);
@@ -381,7 +397,7 @@ impl Packer {
}
if !self.par.styles.compatible::<ParNode>(styles) {
- self.parbreak(None);
+ self.parbreak(Some(styles.clone()), false);
self.par.styles = styles.clone();
return;
}
@@ -441,8 +457,10 @@ enum Last<N> {
/// Hard nodes: Linebreaks and explicit spacing.
Hard,
/// Soft nodes: Word spaces and paragraph breaks. These are saved here
- /// temporarily and then applied once an `Any` node appears.
- Soft(N),
+ /// temporarily and then applied once an `Any` node appears. The boolean
+ /// says whether this soft node is "important" and preferrable to other soft
+ /// nodes (that is the case for explicit paragraph breaks).
+ Soft(N, bool),
}
impl<N> Last<N> {
@@ -450,16 +468,19 @@ impl<N> Last<N> {
/// now if currently in `Soft` state.
fn any(&mut self) -> Option<N> {
match mem::replace(self, Self::Any) {
- Self::Soft(soft) => Some(soft),
+ Self::Soft(soft, _) => Some(soft),
_ => None,
}
}
/// Transition into the `Soft` state, but only if in `Any`. Otherwise, the
/// soft node is discarded.
- fn soft(&mut self, soft: N) {
- if let Self::Any = self {
- *self = Self::Soft(soft);
+ fn soft(&mut self, soft: N, important: bool) {
+ if matches!(
+ (&self, important),
+ (Self::Any, _) | (Self::Soft(_, false), true)
+ ) {
+ *self = Self::Soft(soft, important);
}
}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 0995ab75..3b1ef3f7 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -397,7 +397,13 @@ primitive! { EcoString: "string", Str }
primitive! { Array: "array", Array }
primitive! { Dict: "dictionary", Dict }
primitive! { Node: "template", Node }
-primitive! { Function: "function", Func }
+primitive! { Function: "function",
+ Func,
+ Class(v) => Function::new(
+ Some(v.name().clone()),
+ move |ctx, args| v.construct(ctx, args).map(Value::Node)
+ )
+}
primitive! { Class: "class", Class }
impl Cast<Value> for Value {