diff --git a/src/eval/capture.rs b/src/eval/capture.rs index 42b617a72..9ef55fb24 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -3,17 +3,27 @@ use std::rc::Rc; use super::*; use crate::syntax::visit::*; -/// A visitor that replaces all captured variables with their values. +/// 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::default() } + Self { + external, + internal: Scopes::default(), + captures: Scope::new(), + } + } + + /// Return the scope of capture variables. + pub fn finish(self) -> Scope { + self.captures } /// Define an internal variable. @@ -23,14 +33,14 @@ impl<'a> CapturesVisitor<'a> { } impl<'ast> Visit<'ast> for CapturesVisitor<'_> { - fn visit_expr(&mut self, item: &'ast mut Expr) { + fn visit_expr(&mut self, item: &'ast 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)); + if let Some(slot) = self.external.get(ident) { + self.captures.def_slot(ident.as_str(), Rc::clone(slot)); } } } @@ -38,7 +48,7 @@ impl<'ast> Visit<'ast> for CapturesVisitor<'_> { } } - fn visit_block(&mut self, item: &'ast mut ExprBlock) { + fn visit_block(&mut self, item: &'ast ExprBlock) { // Blocks create a scope except if directly in a template. if item.scopes { self.internal.push(); @@ -49,20 +59,20 @@ impl<'ast> Visit<'ast> for CapturesVisitor<'_> { } } - fn visit_template(&mut self, item: &'ast mut ExprTemplate) { + fn visit_template(&mut self, item: &'ast ExprTemplate) { // Templates always create a scope. self.internal.push(); visit_template(self, item); self.internal.pop(); } - fn visit_let(&mut self, item: &'ast mut ExprLet) { + fn visit_let(&mut self, item: &'ast ExprLet) { self.define(&item.pat.v); visit_let(self, item); } - fn visit_for(&mut self, item: &'ast mut ExprFor) { - match &mut item.pat.v { + fn visit_for(&mut self, item: &'ast ExprFor) { + match &item.pat.v { ForPattern::Value(value) => self.define(value), ForPattern::KeyValue(key, value) => { self.define(key); diff --git a/src/eval/mod.rs b/src/eval/mod.rs index cfd1c8081..e49f7779c 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -194,7 +194,6 @@ impl Eval for Spanned<&Expr> { Expr::Let(v) => v.with_span(self.span).eval(ctx), Expr::If(v) => v.with_span(self.span).eval(ctx), Expr::For(v) => v.with_span(self.span).eval(ctx), - Expr::Captured(v) => v.borrow().clone(), } } } @@ -344,21 +343,17 @@ impl Spanned<&ExprBinary> { let rhs = self.v.rhs.eval(ctx); let span = self.v.lhs.span; - let slot = match &self.v.lhs.v { - Expr::Ident(id) => match ctx.scopes.get(id) { + let slot = if let Expr::Ident(id) = &self.v.lhs.v { + match ctx.scopes.get(id) { Some(slot) => slot, None => { ctx.diag(error!(span, "unknown variable")); return Value::Error; } - }, - - Expr::Captured(slot) => slot, - - _ => { - ctx.diag(error!(span, "cannot assign to this expression")); - return Value::Error; } + } else { + ctx.diag(error!(span, "cannot assign to this expression")); + return Value::Error; }; let (constant, err, value) = if let Ok(mut inner) = slot.try_borrow_mut() { diff --git a/src/eval/scope.rs b/src/eval/scope.rs index 70338041b..8f2bd1d52 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -86,6 +86,11 @@ impl Scope { self.values.insert(var.into(), Rc::new(RefCell::new(value.into()))); } + /// Define a variable with a slot. + pub fn def_slot(&mut self, var: impl Into, slot: Slot) { + self.values.insert(var.into(), slot); + } + /// Look up the value of a variable. pub fn get(&self, var: &str) -> Option<&Slot> { self.values.get(var) diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index a681aa32a..3160e0e40 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,6 +1,5 @@ use super::*; use crate::color::RgbaColor; -use crate::eval::Slot; use crate::geom::{AngularUnit, LengthUnit}; /// An expression. @@ -51,11 +50,6 @@ pub enum Expr { If(ExprIf), /// A for expression: `#for x #in y { z }`. For(ExprFor), - /// A captured variable slot. - /// - /// This node is never created by parsing. It only results from an in-place - /// transformation of an identifier to a captured variable. - Captured(Slot), } impl Pretty for Expr { @@ -92,7 +86,6 @@ impl Pretty for Expr { Self::Let(v) => v.pretty(p), Self::If(v) => v.pretty(p), Self::For(v) => v.pretty(p), - Self::Captured(v) => v.borrow().pretty(p), } } } diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 2e4391ac0..3a289eb16 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -3,17 +3,17 @@ use super::*; macro_rules! visit { - ($(fn $name:ident($v:ident, $item:ident: &mut $ty:ty) $body:block)*) => { + ($(fn $name:ident($v:ident, $item:ident: &$ty:ty) $body:block)*) => { /// Traverses the syntax tree. pub trait Visit<'ast> { - $(fn $name(&mut self, $item: &'ast mut $ty) { + $(fn $name(&mut self, $item: &'ast $ty) { $name(self, $item); })* } $(visit! { @concat!("Walk a node of type [`", stringify!($ty), "`]."), - pub fn $name<'ast, V>($v: &mut V, $item: &'ast mut $ty) + pub fn $name<'ast, V>($v: &mut V, $item: &'ast $ty) where V: Visit<'ast> + ?Sized $body @@ -27,13 +27,13 @@ macro_rules! visit { } visit! { - fn visit_tree(v, item: &mut Tree) { + fn visit_tree(v, item: &Tree) { for node in item { - v.visit_node(&mut node.v); + v.visit_node(&node.v); } } - fn visit_node(v, item: &mut Node) { + fn visit_node(v, item: &Node) { match item { Node::Strong => {} Node::Emph => {} @@ -41,13 +41,13 @@ visit! { Node::Linebreak => {} Node::Parbreak => {} Node::Text(_) => {} - Node::Heading(n) => v.visit_tree(&mut n.contents), + Node::Heading(n) => v.visit_tree(&n.contents), Node::Raw(_) => {} Node::Expr(expr) => v.visit_expr(expr), } } - fn visit_expr(v, item: &mut Expr) { + fn visit_expr(v, item: &Expr) { match item { Expr::None => {} Expr::Ident(_) => {} @@ -70,79 +70,78 @@ visit! { 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) { + fn visit_array(v, item: &ExprArray) { for expr in item { - v.visit_expr(&mut expr.v); + v.visit_expr(&expr.v); } } - fn visit_dict(v, item: &mut ExprDict) { + fn visit_dict(v, item: &ExprDict) { for named in item { - v.visit_expr(&mut named.expr.v); + v.visit_expr(&named.expr.v); } } - fn visit_template(v, item: &mut ExprTemplate) { + fn visit_template(v, item: &ExprTemplate) { v.visit_tree(item); } - fn visit_group(v, item: &mut ExprGroup) { - v.visit_expr(&mut item.v); + fn visit_group(v, item: &ExprGroup) { + v.visit_expr(&item.v); } - fn visit_block(v, item: &mut ExprBlock) { - for expr in &mut item.exprs { - v.visit_expr(&mut expr.v); + fn visit_block(v, item: &ExprBlock) { + for expr in &item.exprs { + v.visit_expr(&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_binary(v, item: &ExprBinary) { + v.visit_expr(&item.lhs.v); + v.visit_expr(&item.rhs.v); } - fn visit_unary(v, item: &mut ExprUnary) { - v.visit_expr(&mut item.expr.v); + fn visit_unary(v, item: &ExprUnary) { + v.visit_expr(&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_call(v, item: &ExprCall) { + v.visit_expr(&item.callee.v); + v.visit_args(&item.args.v); } - fn visit_args(v, item: &mut ExprArgs) { + fn visit_args(v, item: &ExprArgs) { for arg in item { v.visit_arg(arg); } } - fn visit_arg(v, item: &mut Argument) { + fn visit_arg(v, item: &Argument) { match item { - Argument::Pos(expr) => v.visit_expr(&mut expr.v), - Argument::Named(named) => v.visit_expr(&mut named.expr.v), + Argument::Pos(expr) => v.visit_expr(&expr.v), + Argument::Named(named) => v.visit_expr(&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_let(v, item: &ExprLet) { + if let Some(init) = &item.init { + v.visit_expr(&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_if(v, item: &ExprIf) { + v.visit_expr(&item.condition.v); + v.visit_expr(&item.if_body.v); + if let Some(body) = &item.else_body { + v.visit_expr(&body.v); } } - fn visit_for(v, item: &mut ExprFor) { - v.visit_expr(&mut item.iter.v); - v.visit_expr(&mut item.body.v); + fn visit_for(v, item: &ExprFor) { + v.visit_expr(&item.iter.v); + v.visit_expr(&item.body.v); } }