summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/context.rs2
-rw-r--r--src/eval/mod.rs38
-rw-r--r--src/eval/scope.rs10
-rw-r--r--src/eval/template.rs56
-rw-r--r--src/eval/value.rs13
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)");
}