From c80e13579f3e6ca8fb1aac5a6d423d902747368d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 7 Feb 2021 13:14:28 +0100 Subject: [PATCH] =?UTF-8?q?Dry-clean=20visitor=20with=20a=20macro=20?= =?UTF-8?q?=F0=9F=8F=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/capture.rs | 60 +++++--- src/eval/mod.rs | 1 + src/export/pdf.rs | 10 +- src/library/layout.rs | 12 +- src/syntax/mod.rs | 1 - src/syntax/visit.rs | 311 +++++++++++++++++++----------------------- tests/typeset.rs | 1 - 7 files changed, 196 insertions(+), 200 deletions(-) diff --git a/src/eval/capture.rs b/src/eval/capture.rs index 054b64ab3..42b617a72 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -15,32 +15,60 @@ impl<'a> CapturesVisitor<'a> { pub fn new(external: &'a Scopes) -> Self { Self { external, internal: Scopes::default() } } + + /// Define an internal variable. + fn define(&mut self, ident: &Ident) { + self.internal.def_mut(ident.as_str(), Value::None); + } } -impl<'a> Visitor<'a> for CapturesVisitor<'a> { - fn visit_scope_pre(&mut self) { - self.internal.push(); +impl<'ast> Visit<'ast> for CapturesVisitor<'_> { + fn visit_expr(&mut self, item: &'ast mut Expr) { + match item { + Expr::Ident(ident) => { + // Find out whether the identifier is not locally defined, but + // captured, and if so, replace it with its value. + if self.internal.get(ident).is_none() { + if let Some(value) = self.external.get(ident) { + *item = Expr::Captured(Rc::clone(&value)); + } + } + } + expr => visit_expr(self, expr), + } } - fn visit_scope_post(&mut self) { + fn visit_block(&mut self, item: &'ast mut ExprBlock) { + // Blocks create a scope except if directly in a template. + if item.scopes { + self.internal.push(); + } + visit_block(self, item); + if item.scopes { + self.internal.pop(); + } + } + + fn visit_template(&mut self, item: &'ast mut ExprTemplate) { + // Templates always create a scope. + self.internal.push(); + visit_template(self, item); self.internal.pop(); } - fn visit_def(&mut self, id: &mut Ident) { - self.internal.def_mut(id.as_str(), Value::None); + fn visit_let(&mut self, item: &'ast mut ExprLet) { + self.define(&item.pat.v); + visit_let(self, item); } - fn visit_expr(&mut self, expr: &'a mut Expr) { - if let Expr::Ident(ident) = expr { - // Find out whether the identifier is not locally defined, but - // captured, and if so, replace it with its value. - if self.internal.get(ident).is_none() { - if let Some(value) = self.external.get(ident) { - *expr = Expr::Captured(Rc::clone(&value)); - } + fn visit_for(&mut self, item: &'ast mut ExprFor) { + match &mut item.pat.v { + ForPattern::Value(value) => self.define(value), + ForPattern::KeyValue(key, value) => { + self.define(key); + self.define(value); } - } else { - walk_expr(self, expr); } + visit_for(self, item); } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 4f8961cc0..cfd1c8081 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -23,6 +23,7 @@ use crate::diag::Pass; use crate::env::Env; use crate::geom::{Angle, Length, Relative, Spec}; use crate::layout::{self, Expansion, NodeSpacing, NodeStack}; +use crate::syntax::visit::Visit; use crate::syntax::*; /// Evaluate a syntax tree into a layout tree. diff --git a/src/export/pdf.rs b/src/export/pdf.rs index e5124c21f..b2a6bbfd0 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -130,11 +130,11 @@ impl<'a> PdfExporter<'a> { let mut content = Content::new(); for (pos, element) in &page.elements { + let x = pos.x.to_pt() as f32; match element { Element::Image(image) => { let name = format!("Im{}", self.images.map(image.res)); let size = image.size; - let x = pos.x.to_pt() as f32; let y = (page.size.height - pos.y - size.height).to_pt() as f32; let w = size.width.to_pt() as f32; let h = size.height.to_pt() as f32; @@ -151,16 +151,14 @@ impl<'a> PdfExporter<'a> { match geometry.fill { Fill::Color(Color::Rgba(c)) => { content.fill_rgb( - c.r as f32 / 255., - c.g as f32 / 255., - c.b as f32 / 255., + c.r as f32 / 255.0, + c.g as f32 / 255.0, + c.b as f32 / 255.0, ); } Fill::Image(_) => todo!(), } - let x = pos.x.to_pt() as f32; - match &geometry.shape { Shape::Rect(r) => { let w = r.width.to_pt() as f32; diff --git a/src/library/layout.rs b/src/library/layout.rs index 2812a48f4..e48399539 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -175,7 +175,16 @@ impl Display for Alignment { /// # Named arguments /// - Width of the box: `width`, of type `linear` relative to parent width. /// - Height of the box: `height`, of type `linear` relative to parent height. +/// - Main layouting direction: `main-dir`, of type `direction`. +/// - Cross layouting direction: `cross-dir`, of type `direction`. /// - Background color of the box: `color`, of type `color`. +/// +/// # Relevant types and constants +/// - Type `direction` +/// - `ltr` (left to right) +/// - `rtl` (right to left) +/// - `ttb` (top to bottom) +/// - `btt` (bottom to top) pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { let snapshot = ctx.state.clone(); @@ -210,13 +219,12 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { if let Some(color) = color { ctx.push(NodeBackground { fill: Fill::Color(color), - child: Node::Any(fixed_node.into()), + child: fixed_node.into(), }) } else { ctx.push(fixed_node); } - ctx.state = snapshot; Value::None } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index af6715c6d..8bb6931ab 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -12,7 +12,6 @@ pub use ident::*; pub use node::*; pub use span::*; pub use token::*; -pub use visit::Visitor; use crate::pretty::{Pretty, Printer}; diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 6f0e7ef31..2e4391ac0 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -2,184 +2,147 @@ use super::*; -/// Visits syntax tree nodes in a depth-first manner. -pub trait Visitor<'a>: Sized { - /// Visit a variable definition. - fn visit_def(&mut self, _ident: &'a mut Ident) {} +macro_rules! visit { + ($(fn $name:ident($v:ident, $item:ident: &mut $ty:ty) $body:block)*) => { + /// Traverses the syntax tree. + pub trait Visit<'ast> { + $(fn $name(&mut self, $item: &'ast mut $ty) { + $name(self, $item); + })* + } - /// Visit the start of a scope. - fn visit_scope_pre(&mut self) {} + $(visit! { + @concat!("Walk a node of type [`", stringify!($ty), "`]."), + pub fn $name<'ast, V>($v: &mut V, $item: &'ast mut $ty) + where + V: Visit<'ast> + ?Sized + $body + })* + }; + (@$doc:expr, $($tts:tt)*) => { + #[doc = $doc] + $($tts)* + } - /// Visit the end of a scope. - fn visit_scope_post(&mut self) {} - - fn visit_node(&mut self, node: &'a mut Node) { - walk_node(self, node) - } - fn visit_expr(&mut self, expr: &'a mut Expr) { - walk_expr(self, expr) - } - fn visit_array(&mut self, array: &'a mut ExprArray) { - walk_array(self, array) - } - fn visit_dict(&mut self, dict: &'a mut ExprDict) { - walk_dict(self, dict) - } - fn visit_template(&mut self, template: &'a mut ExprTemplate) { - walk_template(self, template) - } - fn visit_group(&mut self, group: &'a mut ExprGroup) { - walk_group(self, group) - } - fn visit_block(&mut self, block: &'a mut ExprBlock) { - walk_block(self, block) - } - fn visit_binary(&mut self, binary: &'a mut ExprBinary) { - walk_binary(self, binary) - } - fn visit_unary(&mut self, unary: &'a mut ExprUnary) { - walk_unary(self, unary) - } - fn visit_call(&mut self, call: &'a mut ExprCall) { - walk_call(self, call) - } - fn visit_arg(&mut self, arg: &'a mut Argument) { - walk_arg(self, arg) - } - fn visit_let(&mut self, expr_let: &'a mut ExprLet) { - walk_let(self, expr_let) - } - fn visit_if(&mut self, expr_if: &'a mut ExprIf) { - walk_if(self, expr_if) - } - fn visit_for(&mut self, expr_for: &'a mut ExprFor) { - walk_for(self, expr_for) - } } -pub fn walk_node<'a, V: Visitor<'a>>(v: &mut V, node: &'a mut Node) { - match node { - Node::Strong => {} - Node::Emph => {} - Node::Space => {} - Node::Linebreak => {} - Node::Parbreak => {} - Node::Text(_) => {} - Node::Heading(_) => {} - Node::Raw(_) => {} - Node::Expr(expr) => v.visit_expr(expr), - } -} - -pub fn walk_expr<'a, V: Visitor<'a>>(v: &mut V, expr: &'a mut Expr) { - match expr { - Expr::None => {} - Expr::Ident(_) => {} - Expr::Bool(_) => {} - Expr::Int(_) => {} - Expr::Float(_) => {} - Expr::Length(_, _) => {} - Expr::Angle(_, _) => {} - Expr::Percent(_) => {} - Expr::Color(_) => {} - Expr::Str(_) => {} - Expr::Array(e) => v.visit_array(e), - Expr::Dict(e) => v.visit_dict(e), - Expr::Template(e) => v.visit_template(e), - Expr::Group(e) => v.visit_group(e), - Expr::Block(e) => v.visit_block(e), - Expr::Unary(e) => v.visit_unary(e), - Expr::Binary(e) => v.visit_binary(e), - Expr::Call(e) => v.visit_call(e), - Expr::Let(e) => v.visit_let(e), - Expr::If(e) => v.visit_if(e), - Expr::For(e) => v.visit_for(e), - Expr::Captured(_) => {} - } -} - -pub fn walk_array<'a, V: Visitor<'a>>(v: &mut V, array: &'a mut ExprArray) { - for expr in array { - v.visit_expr(&mut expr.v); - } -} - -pub fn walk_dict<'a, V: Visitor<'a>>(v: &mut V, dict: &'a mut ExprDict) { - for named in dict { - v.visit_expr(&mut named.expr.v); - } -} - -pub fn walk_template<'a, V: Visitor<'a>>(v: &mut V, template: &'a mut ExprTemplate) { - v.visit_scope_pre(); - for node in template { - v.visit_node(&mut node.v); - } - v.visit_scope_post(); -} - -pub fn walk_group<'a, V: Visitor<'a>>(v: &mut V, group: &'a mut ExprGroup) { - v.visit_expr(&mut group.v); -} - -pub fn walk_block<'a, V: Visitor<'a>>(v: &mut V, block: &'a mut ExprBlock) { - if block.scopes { - v.visit_scope_pre(); - } - for expr in &mut block.exprs { - v.visit_expr(&mut expr.v); - } - if block.scopes { - v.visit_scope_post(); - } -} - -pub fn walk_binary<'a, V: Visitor<'a>>(v: &mut V, binary: &'a mut ExprBinary) { - v.visit_expr(&mut binary.lhs.v); - v.visit_expr(&mut binary.rhs.v); -} - -pub fn walk_unary<'a, V: Visitor<'a>>(v: &mut V, unary: &'a mut ExprUnary) { - v.visit_expr(&mut unary.expr.v); -} - -pub fn walk_call<'a, V: Visitor<'a>>(v: &mut V, call: &'a mut ExprCall) { - v.visit_expr(&mut call.callee.v); - for arg in &mut call.args.v { - v.visit_arg(arg); - } -} - -pub fn walk_arg<'a, V: Visitor<'a>>(v: &mut V, arg: &'a mut Argument) { - match arg { - Argument::Pos(expr) => v.visit_expr(&mut expr.v), - Argument::Named(named) => v.visit_expr(&mut named.expr.v), - } -} - -pub fn walk_let<'a, V: Visitor<'a>>(v: &mut V, expr_let: &'a mut ExprLet) { - v.visit_def(&mut expr_let.pat.v); - if let Some(init) = &mut expr_let.init { - v.visit_expr(&mut init.v); - } -} - -pub fn walk_if<'a, V: Visitor<'a>>(v: &mut V, expr_if: &'a mut ExprIf) { - v.visit_expr(&mut expr_if.condition.v); - v.visit_expr(&mut expr_if.if_body.v); - if let Some(body) = &mut expr_if.else_body { - v.visit_expr(&mut body.v); - } -} - -pub fn walk_for<'a, V: Visitor<'a>>(v: &mut V, expr_for: &'a mut ExprFor) { - match &mut expr_for.pat.v { - ForPattern::Value(value) => v.visit_def(value), - ForPattern::KeyValue(key, value) => { - v.visit_def(key); - v.visit_def(value); +visit! { + fn visit_tree(v, item: &mut Tree) { + for node in item { + v.visit_node(&mut node.v); } } - v.visit_expr(&mut expr_for.iter.v); - v.visit_expr(&mut expr_for.body.v); + + fn visit_node(v, item: &mut Node) { + match item { + Node::Strong => {} + Node::Emph => {} + Node::Space => {} + Node::Linebreak => {} + Node::Parbreak => {} + Node::Text(_) => {} + Node::Heading(n) => v.visit_tree(&mut n.contents), + Node::Raw(_) => {} + Node::Expr(expr) => v.visit_expr(expr), + } + } + + fn visit_expr(v, item: &mut Expr) { + match item { + Expr::None => {} + Expr::Ident(_) => {} + Expr::Bool(_) => {} + Expr::Int(_) => {} + Expr::Float(_) => {} + Expr::Length(_, _) => {} + Expr::Angle(_, _) => {} + Expr::Percent(_) => {} + Expr::Color(_) => {} + Expr::Str(_) => {} + Expr::Array(e) => v.visit_array(e), + Expr::Dict(e) => v.visit_dict(e), + Expr::Template(e) => v.visit_template(e), + Expr::Group(e) => v.visit_group(e), + Expr::Block(e) => v.visit_block(e), + Expr::Unary(e) => v.visit_unary(e), + Expr::Binary(e) => v.visit_binary(e), + Expr::Call(e) => v.visit_call(e), + Expr::Let(e) => v.visit_let(e), + Expr::If(e) => v.visit_if(e), + Expr::For(e) => v.visit_for(e), + Expr::Captured(_) => {} + } + } + + fn visit_array(v, item: &mut ExprArray) { + for expr in item { + v.visit_expr(&mut expr.v); + } + } + + fn visit_dict(v, item: &mut ExprDict) { + for named in item { + v.visit_expr(&mut named.expr.v); + } + } + + fn visit_template(v, item: &mut ExprTemplate) { + v.visit_tree(item); + } + + fn visit_group(v, item: &mut ExprGroup) { + v.visit_expr(&mut item.v); + } + + fn visit_block(v, item: &mut ExprBlock) { + for expr in &mut item.exprs { + v.visit_expr(&mut expr.v); + } + } + + fn visit_binary(v, item: &mut ExprBinary) { + v.visit_expr(&mut item.lhs.v); + v.visit_expr(&mut item.rhs.v); + } + + fn visit_unary(v, item: &mut ExprUnary) { + v.visit_expr(&mut item.expr.v); + } + + fn visit_call(v, item: &mut ExprCall) { + v.visit_expr(&mut item.callee.v); + v.visit_args(&mut item.args.v); + } + + fn visit_args(v, item: &mut ExprArgs) { + for arg in item { + v.visit_arg(arg); + } + } + + fn visit_arg(v, item: &mut Argument) { + match item { + Argument::Pos(expr) => v.visit_expr(&mut expr.v), + Argument::Named(named) => v.visit_expr(&mut named.expr.v), + } + } + + fn visit_let(v, item: &mut ExprLet) { + if let Some(init) = &mut item.init { + v.visit_expr(&mut init.v); + } + } + + fn visit_if(v, item: &mut ExprIf) { + v.visit_expr(&mut item.condition.v); + v.visit_expr(&mut item.if_body.v); + if let Some(body) = &mut item.else_body { + v.visit_expr(&mut body.v); + } + } + + fn visit_for(v, item: &mut ExprFor) { + v.visit_expr(&mut item.iter.v); + v.visit_expr(&mut item.body.v); + } } diff --git a/tests/typeset.rs b/tests/typeset.rs index d431abfec..7e7fb4633 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -452,7 +452,6 @@ fn draw_geometry(canvas: &mut Canvas, pos: Point, _: &Env, element: &Geometry) { let y = pos.y.to_pt() as f32; let mut paint = Paint::default(); - match &element.fill { Fill::Color(c) => match c { typst::color::Color::Rgba(c) => paint.set_color_rgba8(c.r, c.g, c.b, c.a),