Dry-clean visitor with a macro 🏜

This commit is contained in:
Laurenz 2021-02-07 13:14:28 +01:00
parent bfc2f5aefc
commit c80e13579f
7 changed files with 196 additions and 200 deletions

View File

@ -15,32 +15,60 @@ impl<'a> CapturesVisitor<'a> {
pub fn new(external: &'a Scopes) -> Self { pub fn new(external: &'a Scopes) -> Self {
Self { external, internal: Scopes::default() } 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> { impl<'ast> Visit<'ast> for CapturesVisitor<'_> {
fn visit_scope_pre(&mut self) { fn visit_expr(&mut self, item: &'ast mut Expr) {
self.internal.push(); 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(); self.internal.pop();
} }
fn visit_def(&mut self, id: &mut Ident) { fn visit_let(&mut self, item: &'ast mut ExprLet) {
self.internal.def_mut(id.as_str(), Value::None); self.define(&item.pat.v);
visit_let(self, item);
} }
fn visit_expr(&mut self, expr: &'a mut Expr) { fn visit_for(&mut self, item: &'ast mut ExprFor) {
if let Expr::Ident(ident) = expr { match &mut item.pat.v {
// Find out whether the identifier is not locally defined, but ForPattern::Value(value) => self.define(value),
// captured, and if so, replace it with its value. ForPattern::KeyValue(key, value) => {
if self.internal.get(ident).is_none() { self.define(key);
if let Some(value) = self.external.get(ident) { self.define(value);
*expr = Expr::Captured(Rc::clone(&value));
}
} }
} else {
walk_expr(self, expr);
} }
visit_for(self, item);
} }
} }

View File

@ -23,6 +23,7 @@ use crate::diag::Pass;
use crate::env::Env; use crate::env::Env;
use crate::geom::{Angle, Length, Relative, Spec}; use crate::geom::{Angle, Length, Relative, Spec};
use crate::layout::{self, Expansion, NodeSpacing, NodeStack}; use crate::layout::{self, Expansion, NodeSpacing, NodeStack};
use crate::syntax::visit::Visit;
use crate::syntax::*; use crate::syntax::*;
/// Evaluate a syntax tree into a layout tree. /// Evaluate a syntax tree into a layout tree.

View File

@ -130,11 +130,11 @@ impl<'a> PdfExporter<'a> {
let mut content = Content::new(); let mut content = Content::new();
for (pos, element) in &page.elements { for (pos, element) in &page.elements {
let x = pos.x.to_pt() as f32;
match element { match element {
Element::Image(image) => { Element::Image(image) => {
let name = format!("Im{}", self.images.map(image.res)); let name = format!("Im{}", self.images.map(image.res));
let size = image.size; 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 y = (page.size.height - pos.y - size.height).to_pt() as f32;
let w = size.width.to_pt() as f32; let w = size.width.to_pt() as f32;
let h = size.height.to_pt() as f32; let h = size.height.to_pt() as f32;
@ -151,16 +151,14 @@ impl<'a> PdfExporter<'a> {
match geometry.fill { match geometry.fill {
Fill::Color(Color::Rgba(c)) => { Fill::Color(Color::Rgba(c)) => {
content.fill_rgb( content.fill_rgb(
c.r as f32 / 255., c.r as f32 / 255.0,
c.g as f32 / 255., c.g as f32 / 255.0,
c.b as f32 / 255., c.b as f32 / 255.0,
); );
} }
Fill::Image(_) => todo!(), Fill::Image(_) => todo!(),
} }
let x = pos.x.to_pt() as f32;
match &geometry.shape { match &geometry.shape {
Shape::Rect(r) => { Shape::Rect(r) => {
let w = r.width.to_pt() as f32; let w = r.width.to_pt() as f32;

View File

@ -175,7 +175,16 @@ impl Display for Alignment {
/// # Named arguments /// # Named arguments
/// - Width of the box: `width`, of type `linear` relative to parent width. /// - Width of the box: `width`, of type `linear` relative to parent width.
/// - Height of the box: `height`, of type `linear` relative to parent height. /// - 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`. /// - 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 { pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();
@ -210,13 +219,12 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
if let Some(color) = color { if let Some(color) = color {
ctx.push(NodeBackground { ctx.push(NodeBackground {
fill: Fill::Color(color), fill: Fill::Color(color),
child: Node::Any(fixed_node.into()), child: fixed_node.into(),
}) })
} else { } else {
ctx.push(fixed_node); ctx.push(fixed_node);
} }
ctx.state = snapshot; ctx.state = snapshot;
Value::None Value::None
} }

View File

@ -12,7 +12,6 @@ pub use ident::*;
pub use node::*; pub use node::*;
pub use span::*; pub use span::*;
pub use token::*; pub use token::*;
pub use visit::Visitor;
use crate::pretty::{Pretty, Printer}; use crate::pretty::{Pretty, Printer};

View File

@ -2,184 +2,147 @@
use super::*; use super::*;
/// Visits syntax tree nodes in a depth-first manner. macro_rules! visit {
pub trait Visitor<'a>: Sized { ($(fn $name:ident($v:ident, $item:ident: &mut $ty:ty) $body:block)*) => {
/// Visit a variable definition. /// Traverses the syntax tree.
fn visit_def(&mut self, _ident: &'a mut Ident) {} pub trait Visit<'ast> {
$(fn $name(&mut self, $item: &'ast mut $ty) {
$name(self, $item);
})*
}
/// Visit the start of a scope. $(visit! {
fn visit_scope_pre(&mut self) {} @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) { visit! {
match node { fn visit_tree(v, item: &mut Tree) {
Node::Strong => {} for node in item {
Node::Emph => {} v.visit_node(&mut node.v);
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);
} }
} }
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);
}
} }

View File

@ -452,7 +452,6 @@ fn draw_geometry(canvas: &mut Canvas, pos: Point, _: &Env, element: &Geometry) {
let y = pos.y.to_pt() as f32; let y = pos.y.to_pt() as f32;
let mut paint = Paint::default(); let mut paint = Paint::default();
match &element.fill { match &element.fill {
Fill::Color(c) => match c { Fill::Color(c) => match c {
typst::color::Color::Rgba(c) => paint.set_color_rgba8(c.r, c.g, c.b, c.a), typst::color::Color::Rgba(c) => paint.set_color_rgba8(c.r, c.g, c.b, c.a),