diff options
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/context.rs | 2 | ||||
| -rw-r--r-- | src/eval/mod.rs | 38 | ||||
| -rw-r--r-- | src/eval/scope.rs | 10 | ||||
| -rw-r--r-- | src/eval/template.rs | 56 | ||||
| -rw-r--r-- | src/eval/value.rs | 13 |
5 files changed, 97 insertions, 22 deletions
diff --git a/src/eval/context.rs b/src/eval/context.rs index a998bbdc..fd7e264f 100644 --- a/src/eval/context.rs +++ b/src/eval/context.rs @@ -40,7 +40,7 @@ impl<'a> EvalContext<'a> { pub fn new(env: &'a mut Env, scope: &'a Scope, state: State) -> Self { Self { env, - scopes: Scopes::new(scope), + scopes: Scopes::new(Some(scope)), state, groups: vec![], inner: vec![], diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 13d242f8..9e45a67b 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -7,6 +7,7 @@ mod context; mod ops; mod scope; mod state; +mod template; pub use call::*; pub use context::*; @@ -174,7 +175,7 @@ impl Eval for Spanned<&Expr> { Expr::Str(v) => Value::Str(v.clone()), Expr::Array(v) => Value::Array(v.with_span(self.span).eval(ctx)), Expr::Dict(v) => Value::Dict(v.with_span(self.span).eval(ctx)), - Expr::Template(v) => Value::Template(v.clone()), + Expr::Template(v) => v.with_span(self.span).eval(ctx), Expr::Group(v) => v.eval(ctx), Expr::Block(v) => v.with_span(self.span).eval(ctx), Expr::Call(v) => v.with_span(self.span).eval(ctx), @@ -183,6 +184,7 @@ impl Eval for Spanned<&Expr> { Expr::Let(v) => v.with_span(self.span).eval(ctx), Expr::If(v) => v.with_span(self.span).eval(ctx), Expr::For(v) => v.with_span(self.span).eval(ctx), + Expr::CapturedValue(v) => v.clone(), } } } @@ -327,18 +329,28 @@ impl Spanned<&ExprBinary> { let rhs = self.v.rhs.eval(ctx); let span = self.v.lhs.span; - if let Expr::Ident(id) = &self.v.lhs.v { - if let Some(slot) = ctx.scopes.get_mut(id) { - let lhs = std::mem::replace(slot, Value::None); - *slot = op(lhs, rhs); - return Value::None; - } else if ctx.scopes.is_const(id) { - ctx.diag(error!(span, "cannot assign to constant")); - } else { - ctx.diag(error!(span, "unknown variable")); + match &self.v.lhs.v { + Expr::Ident(id) => { + if let Some(slot) = ctx.scopes.get_mut(id) { + *slot = op(std::mem::take(slot), rhs); + return Value::None; + } else if ctx.scopes.is_const(id) { + ctx.diag(error!(span, "cannot assign to a constant")); + } else { + ctx.diag(error!(span, "unknown variable")); + } + } + + Expr::CapturedValue(_) => { + ctx.diag(error!( + span, + "cannot assign to captured expression in a template", + )); + } + + _ => { + ctx.diag(error!(span, "cannot assign to this expression")); } - } else { - ctx.diag(error!(span, "cannot assign to this expression")); } Value::Error @@ -421,7 +433,7 @@ impl Eval for Spanned<&ExprFor> { (ForPattern::KeyValue(..), Value::Str(_)) | (ForPattern::KeyValue(..), Value::Array(_)) => { - ctx.diag(error!(self.v.pat.span, "mismatched pattern",)); + ctx.diag(error!(self.v.pat.span, "mismatched pattern")); } (_, Value::Error) => {} diff --git a/src/eval/scope.rs b/src/eval/scope.rs index 1ed34f86..9c966a24 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -5,19 +5,19 @@ use std::iter; use super::Value; /// A stack of scopes. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Scopes<'a> { /// The active scope. top: Scope, /// The stack of lower scopes. scopes: Vec<Scope>, /// The base scope. - base: &'a Scope, + base: Option<&'a Scope>, } impl<'a> Scopes<'a> { /// Create a new hierarchy of scopes. - pub fn new(base: &'a Scope) -> Self { + pub fn new(base: Option<&'a Scope>) -> Self { Self { top: Scope::new(), scopes: vec![], base } } @@ -43,7 +43,7 @@ impl<'a> Scopes<'a> { pub fn get(&self, var: &str) -> Option<&Value> { iter::once(&self.top) .chain(self.scopes.iter().rev()) - .chain(iter::once(self.base)) + .chain(self.base.into_iter()) .find_map(|scope| scope.get(var)) } @@ -58,7 +58,7 @@ impl<'a> Scopes<'a> { /// /// Defaults to `false` if the variable does not exist. pub fn is_const(&self, var: &str) -> bool { - self.base.get(var).is_some() + self.base.map_or(false, |base| base.get(var).is_some()) } } 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); + } + } +} diff --git a/src/eval/value.rs b/src/eval/value.rs index 6fa70206..6e838f6c 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -87,7 +87,14 @@ impl Eval for &Value { ctx.push(ctx.make_text_node(match self { Value::None => return, Value::Str(s) => s.clone(), - Value::Template(tree) => return tree.eval(ctx), + Value::Template(tree) => { + // We do not want to allow the template access to the current + // scopes. + let prev = std::mem::take(&mut ctx.scopes); + tree.eval(ctx); + ctx.scopes = prev; + return; + } other => pretty(other), })); } @@ -195,7 +202,7 @@ impl Deref for ValueFunc { impl Pretty for ValueFunc { fn pretty(&self, p: &mut Printer) { - write!(p, "(function {})", self.name).unwrap(); + p.push_str(&self.name); } } @@ -515,7 +522,7 @@ mod tests { test_pretty(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101"); test_pretty("hello", r#""hello""#); test_pretty(vec![Spanned::zero(Node::Strong)], "[*]"); - test_pretty(ValueFunc::new("nil", |_, _| Value::None), "(function nil)"); + test_pretty(ValueFunc::new("nil", |_, _| Value::None), "nil"); test_pretty(ValueAny::new(1), "1"); test_pretty(Value::Error, "(error)"); } |
