typst/src/eval/capture.rs

75 lines
2.0 KiB
Rust

use std::rc::Rc;
use super::{Scope, Scopes, Value};
use crate::syntax::visit::{visit_expr, Visit};
use crate::syntax::{Expr, Ident, Node};
/// A visitor that captures variable slots.
#[derive(Debug)]
pub struct CapturesVisitor<'a> {
external: &'a Scopes<'a>,
internal: Scopes<'a>,
captures: Scope,
}
impl<'a> CapturesVisitor<'a> {
/// Create a new visitor for the given external scopes.
pub fn new(external: &'a Scopes) -> Self {
Self {
external,
internal: Scopes::new(None),
captures: Scope::new(),
}
}
/// Return the scope of captured variables.
pub fn finish(self) -> Scope {
self.captures
}
/// Find out whether the name is not locally defined and if so if it can be
/// captured.
fn process(&mut self, name: &str) {
if self.internal.get(name).is_none() {
if let Some(slot) = self.external.get(name) {
self.captures.def_slot(name, Rc::clone(slot));
}
}
}
}
impl<'ast> Visit<'ast> for CapturesVisitor<'_> {
fn visit_node(&mut self, node: &'ast Node) {
match node {
Node::Text(_) => {}
Node::Space => {}
Node::Linebreak(_) => self.process(Node::LINEBREAK),
Node::Parbreak(_) => self.process(Node::PARBREAK),
Node::Strong(_) => self.process(Node::STRONG),
Node::Emph(_) => self.process(Node::EMPH),
Node::Heading(_) => self.process(Node::HEADING),
Node::Raw(_) => self.process(Node::RAW),
Node::Expr(expr) => self.visit_expr(expr),
}
}
fn visit_expr(&mut self, node: &'ast Expr) {
match node {
Expr::Ident(ident) => self.process(ident),
expr => visit_expr(self, expr),
}
}
fn visit_binding(&mut self, id: &'ast Ident) {
self.internal.def_mut(id.as_str(), Value::None);
}
fn visit_enter(&mut self) {
self.internal.enter();
}
fn visit_exit(&mut self) {
self.internal.exit();
}
}