From 2036663ed25b5885a87eb3a80caec3fa2e258d77 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 27 Jan 2021 15:05:18 +0100 Subject: =?UTF-8?q?Capture=20variables=20in=20templates=20=F0=9F=94=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/template.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/eval/template.rs (limited to 'src/eval/template.rs') 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); + } + } +} -- cgit v1.2.3