summaryrefslogtreecommitdiff
path: root/src/eval/capture.rs
blob: e46103c8b5c08afef9cffbfe33b5c56c620072b3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use std::rc::Rc;

use super::{Scope, Scopes, Value};
use crate::syntax::ast::{ClosureParam, Expr, Imports};
use crate::syntax::RedRef;

/// A visitor that captures variable slots.
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(),
        }
    }

    pub fn visit(&mut self, node: RedRef) {
        let expr: Option<Expr> = node.cast();

        match expr.as_ref() {
            Some(Expr::Let(expr)) => {
                self.visit(expr.init_ref());
                let ident = expr.binding();
                self.internal.def_mut(ident.as_str(), Value::None);
            }
            Some(Expr::Closure(closure)) => {
                for arg in closure.params() {
                    match arg {
                        ClosureParam::Pos(ident) | ClosureParam::Sink(ident) => {
                            self.internal.def_mut(ident.as_str(), Value::None);
                        }
                        ClosureParam::Named(name) => {
                            self.internal.def_mut(name.name().as_str(), Value::None);
                        }
                    }
                }
                self.visit(closure.body_ref());
            }
            Some(Expr::For(forloop)) => {
                let pattern = forloop.pattern();
                self.internal.def_mut(pattern.value().as_str(), Value::None);

                if let Some(key) = pattern.key() {
                    self.internal.def_mut(key.as_str(), Value::None);
                }
                self.visit(forloop.body_ref());
            }
            Some(Expr::Import(import)) => {
                if let Imports::Idents(idents) = import.imports() {
                    for ident in idents {
                        self.internal.def_mut(ident.as_str(), Value::None);
                    }
                }
            }
            Some(Expr::Ident(ident)) => {
                if self.internal.get(ident.as_str()).is_none() {
                    if let Some(slot) = self.external.get(ident.as_str()) {
                        self.captures.def_slot(ident.as_str(), Rc::clone(slot));
                    }
                }
            }
            _ => {}
        }

        match expr.as_ref() {
            Some(Expr::Let(_)) | Some(Expr::For(_)) | Some(Expr::Closure(_)) => {}

            Some(Expr::Block(_)) => {
                self.internal.enter();
                for child in node.children() {
                    self.visit(child);
                }
                self.internal.exit();
            }

            Some(Expr::Template(_)) => {
                self.internal.enter();
                for child in node.children() {
                    self.visit(child);
                }
                self.internal.exit();
            }

            _ => {
                for child in node.children() {
                    self.visit(child);
                }
            }
        }
    }

    /// Return the scope of captured variables.
    pub fn finish(self) -> Scope {
        self.captures
    }
}