summaryrefslogtreecommitdiff
path: root/src/eval/capture.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-30 10:24:51 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-30 10:24:51 +0100
commitac24075469f171fe83a976b9a97b9b1ea078a7e3 (patch)
treee89155de4670efa636b71f4a9a5e5f422c76214d /src/eval/capture.rs
parent2036663ed25b5885a87eb3a80caec3fa2e258d77 (diff)
Moves captures visitor into separate file 🚚
Diffstat (limited to 'src/eval/capture.rs')
-rw-r--r--src/eval/capture.rs44
1 files changed, 44 insertions, 0 deletions
diff --git a/src/eval/capture.rs b/src/eval/capture.rs
new file mode 100644
index 00000000..b7052c70
--- /dev/null
+++ b/src/eval/capture.rs
@@ -0,0 +1,44 @@
+use super::*;
+use crate::syntax::visit::*;
+
+/// A visitor that replaces all captured variables with their values.
+#[derive(Debug)]
+pub struct CapturesVisitor<'a> {
+ external: &'a Scopes<'a>,
+ internal: Scopes<'a>,
+}
+
+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() }
+ }
+}
+
+impl<'a> Visitor<'a> for CapturesVisitor<'a> {
+ fn visit_scope_pre(&mut self) {
+ self.internal.push();
+ }
+
+ fn visit_scope_post(&mut self) {
+ self.internal.pop();
+ }
+
+ fn visit_def(&mut self, id: &mut Ident) {
+ self.internal.define(id.as_str(), Value::None);
+ }
+
+ 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::CapturedValue(value.clone());
+ }
+ }
+ } else {
+ walk_expr(self, expr);
+ }
+ }
+}