summaryrefslogtreecommitdiff
path: root/src/eval/capture.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-02-07 13:14:28 +0100
committerLaurenz <laurmaedje@gmail.com>2021-02-07 13:14:28 +0100
commitc80e13579f3e6ca8fb1aac5a6d423d902747368d (patch)
treeee62a7b7517acd16e9b2b5133c7ad1bdceff0d3c /src/eval/capture.rs
parentbfc2f5aefc6c407de0b699b31dafd835fc2c9be3 (diff)
Dry-clean visitor with a macro 🏜
Diffstat (limited to 'src/eval/capture.rs')
-rw-r--r--src/eval/capture.rs60
1 files changed, 44 insertions, 16 deletions
diff --git a/src/eval/capture.rs b/src/eval/capture.rs
index 054b64ab..42b617a7 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);
}
}