summaryrefslogtreecommitdiff
path: root/src/eval/walk.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-08-21 16:38:51 +0200
committerLaurenz <laurmaedje@gmail.com>2021-08-21 17:30:21 +0200
commit0dd4ae0a7ac0c247078df492469ff20b8a90c886 (patch)
tree07a55343b9ccab3fe76b0f1b0de9d1be310d8b14 /src/eval/walk.rs
parentf38eb10c2b54bd13ccef119454839f6a66448462 (diff)
Prune derives
Diffstat (limited to 'src/eval/walk.rs')
-rw-r--r--src/eval/walk.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/src/eval/walk.rs b/src/eval/walk.rs
new file mode 100644
index 00000000..4c6a1605
--- /dev/null
+++ b/src/eval/walk.rs
@@ -0,0 +1,129 @@
+use std::fmt::Write;
+use std::rc::Rc;
+
+use super::{Eval, EvalContext, Template, Value};
+use crate::diag::TypResult;
+use crate::geom::Gen;
+use crate::layout::{ParChild, ParNode, StackChild, StackNode};
+use crate::syntax::*;
+use crate::util::EcoString;
+
+/// Walk a syntax node and fill the currently built template.
+pub trait Walk {
+ /// Walk the node.
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()>;
+}
+
+impl Walk for SyntaxTree {
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
+ for node in self.iter() {
+ node.walk(ctx)?;
+ }
+ Ok(())
+ }
+}
+
+impl Walk for SyntaxNode {
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
+ match self {
+ Self::Space => ctx.template.space(),
+ Self::Linebreak(_) => ctx.template.linebreak(),
+ Self::Parbreak(_) => ctx.template.parbreak(),
+ Self::Strong(_) => ctx.template.modify(|s| s.font_mut().strong ^= true),
+ Self::Emph(_) => ctx.template.modify(|s| s.font_mut().emph ^= true),
+ Self::Text(text) => ctx.template.text(text),
+ Self::Raw(raw) => raw.walk(ctx)?,
+ Self::Heading(heading) => heading.walk(ctx)?,
+ Self::List(list) => list.walk(ctx)?,
+ Self::Enum(enum_) => enum_.walk(ctx)?,
+ Self::Expr(expr) => match expr.eval(ctx)? {
+ Value::None => {}
+ Value::Int(v) => ctx.template.text(v.to_string()),
+ Value::Float(v) => ctx.template.text(v.to_string()),
+ Value::Str(v) => ctx.template.text(v),
+ Value::Template(v) => ctx.template += v,
+ // For values which can't be shown "naturally", we print the
+ // representation in monospace.
+ other => ctx.template.monospace(other.to_string()),
+ },
+ }
+ Ok(())
+ }
+}
+
+impl Walk for RawNode {
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
+ if self.block {
+ ctx.template.parbreak();
+ }
+
+ ctx.template.monospace(&self.text);
+
+ if self.block {
+ ctx.template.parbreak();
+ }
+
+ Ok(())
+ }
+}
+
+impl Walk for HeadingNode {
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
+ let level = self.level;
+ let body = self.body.eval(ctx)?;
+
+ ctx.template.parbreak();
+ ctx.template.save();
+ ctx.template.modify(move |state| {
+ let font = state.font_mut();
+ let upscale = 1.6 - 0.1 * level as f64;
+ font.size *= upscale;
+ font.strong = true;
+ });
+ ctx.template += body;
+ ctx.template.restore();
+ ctx.template.parbreak();
+
+ Ok(())
+ }
+}
+
+impl Walk for ListNode {
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
+ let body = self.body.eval(ctx)?;
+ walk_item(ctx, '•'.into(), body);
+ Ok(())
+ }
+}
+
+impl Walk for EnumNode {
+ fn walk(&self, ctx: &mut EvalContext) -> TypResult<()> {
+ let body = self.body.eval(ctx)?;
+ let mut label = EcoString::new();
+ write!(&mut label, "{}.", self.number.unwrap_or(1)).unwrap();
+ walk_item(ctx, label, body);
+ Ok(())
+ }
+}
+
+fn walk_item(ctx: &mut EvalContext, label: EcoString, body: Template) {
+ ctx.template += Template::from_block(move |state| {
+ let label = ParNode {
+ dir: state.dirs.cross,
+ line_spacing: state.line_spacing(),
+ children: vec![ParChild::Text(
+ label.clone(),
+ state.aligns.cross,
+ Rc::clone(&state.font),
+ )],
+ };
+ StackNode {
+ dirs: Gen::new(state.dirs.main, state.dirs.cross),
+ children: vec![
+ StackChild::Any(label.into(), Gen::default()),
+ StackChild::Spacing((state.font.size / 2.0).into()),
+ StackChild::Any(body.to_stack(&state).into(), Gen::default()),
+ ],
+ }
+ });
+}