From ce8138c68557a2d158424a8aa6056d73ff9cb1ba Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 27 Jan 2021 11:50:51 +0100 Subject: [PATCH] =?UTF-8?q?Scope=20variables=20in=20blocks=20=F0=9F=8F=94?= =?UTF-8?q?=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/mod.rs | 9 +++++++++ src/eval/scope.rs | 19 ++++++++++++++++--- src/parse/mod.rs | 10 +++++----- src/syntax/expr.rs | 2 ++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index e04879d28..cadffef5e 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -210,10 +210,19 @@ impl Eval for Spanned<&ExprBlock> { type Output = Value; fn eval(self, ctx: &mut EvalContext) -> Self::Output { + if self.v.scopes { + ctx.scopes.push(); + } + let mut output = Value::None; for expr in &self.v.exprs { output = expr.eval(ctx); } + + if self.v.scopes { + ctx.scopes.pop(); + } + output } } diff --git a/src/eval/scope.rs b/src/eval/scope.rs index ed4edbb96..1ed34f866 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -4,7 +4,7 @@ use std::iter; use super::Value; -/// A hierarchy of scopes. +/// A stack of scopes. #[derive(Debug, Clone, PartialEq)] pub struct Scopes<'a> { /// The active scope. @@ -21,6 +21,19 @@ impl<'a> Scopes<'a> { Self { top: Scope::new(), scopes: vec![], base } } + /// Push a new scope. + pub fn push(&mut self) { + self.scopes.push(std::mem::take(&mut self.top)); + } + + /// Pop the topmost scope. + /// + /// # Panics + /// Panics if no scope was pushed. + pub fn pop(&mut self) { + self.top = self.scopes.pop().expect("no pushed scope"); + } + /// Define a variable in the active scope. pub fn define(&mut self, var: impl Into, value: impl Into) { self.top.define(var, value); @@ -29,7 +42,7 @@ impl<'a> Scopes<'a> { /// Look up the value of a variable. pub fn get(&self, var: &str) -> Option<&Value> { iter::once(&self.top) - .chain(&self.scopes) + .chain(self.scopes.iter().rev()) .chain(iter::once(self.base)) .find_map(|scope| scope.get(var)) } @@ -37,7 +50,7 @@ impl<'a> Scopes<'a> { /// Get a mutable reference to a variable. pub fn get_mut(&mut self, var: &str) -> Option<&mut Value> { iter::once(&mut self.top) - .chain(&mut self.scopes) + .chain(self.scopes.iter_mut().rev()) .find_map(|scope| scope.get_mut(var)) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 8687ac246..ff75a563d 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -52,7 +52,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option { // Code block. Token::LeftBrace => { - return Some(Node::Expr(block(p)?)); + return Some(Node::Expr(block(p, false)?)); } // Markup. @@ -285,7 +285,7 @@ fn primary(p: &mut Parser) -> Option { // Nested block. Some(Token::LeftBrace) => { - return block(p); + return block(p, true); } // Dictionary or just a parenthesized expression. @@ -340,7 +340,7 @@ fn template(p: &mut Parser) -> Expr { } /// Parse a block expression: `{...}`. -fn block(p: &mut Parser) -> Option { +fn block(p: &mut Parser, scopes: bool) -> Option { p.start_group(Group::Brace, TokenMode::Code); let mut exprs = vec![]; while !p.eof() { @@ -355,7 +355,7 @@ fn block(p: &mut Parser) -> Option { p.skip_white(); } p.end_group(); - Some(Expr::Block(ExprBlock { exprs })) + Some(Expr::Block(ExprBlock { exprs, scopes })) } /// Parse a parenthesized function call. @@ -469,7 +469,7 @@ fn ident(p: &mut Parser) -> Option { fn body(p: &mut Parser) -> Option { match p.peek() { Some(Token::LeftBracket) => Some(template(p)), - Some(Token::LeftBrace) => block(p), + Some(Token::LeftBrace) => block(p, true), _ => { p.expected_at("body", p.last_end()); None diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 9f3bd9ad2..15c53cc0e 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -147,6 +147,8 @@ pub type ExprGroup = SpanBox; pub struct ExprBlock { /// The list of expressions contained in the block. pub exprs: SpanVec, + /// Whether the block should create a scope. + pub scopes: bool, } impl Pretty for ExprBlock {