mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Dry-clean visitor with a macro 🏜
This commit is contained in:
parent
bfc2f5aefc
commit
c80e13579f
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user