summaryrefslogtreecommitdiff
path: root/src/eval/template.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-27 15:05:18 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-27 15:05:18 +0100
commit2036663ed25b5885a87eb3a80caec3fa2e258d77 (patch)
tree110ca98e4d76dc887b41c91685bb202c49730236 /src/eval/template.rs
parent2641c2d20ef5ddaf8e1dc91f4a69abfe2c170e4d (diff)
Capture variables in templates 🔍
Diffstat (limited to 'src/eval/template.rs')
-rw-r--r--src/eval/template.rs56
1 files changed, 56 insertions, 0 deletions
diff --git a/src/eval/template.rs b/src/eval/template.rs
new file mode 100644
index 00000000..040685f8
--- /dev/null
+++ b/src/eval/template.rs
@@ -0,0 +1,56 @@
+use super::*;
+use crate::syntax::visit::*;
+
+impl Eval for Spanned<&ExprTemplate> {
+ type Output = Value;
+
+ fn eval(self, ctx: &mut EvalContext) -> Self::Output {
+ let mut template = self.v.clone();
+ let mut visitor = CapturesVisitor::new(ctx);
+ visitor.visit_template(&mut template);
+ Value::Template(template)
+ }
+}
+
+/// A visitor that replaces all captured variables with their values.
+struct CapturesVisitor<'a> {
+ external: &'a Scopes<'a>,
+ internal: Scopes<'a>,
+}
+
+impl<'a> CapturesVisitor<'a> {
+ fn new(ctx: &'a EvalContext) -> Self {
+ Self {
+ external: &ctx.scopes,
+ 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 it's 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);
+ }
+ }
+}