Scope variables in blocks 🏔️

This commit is contained in:
Laurenz 2021-01-27 11:50:51 +01:00
parent 710f88ccb2
commit ce8138c685
4 changed files with 32 additions and 8 deletions

View File

@ -210,10 +210,19 @@ impl Eval for Spanned<&ExprBlock> {
type Output = Value; type Output = Value;
fn eval(self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
if self.v.scopes {
ctx.scopes.push();
}
let mut output = Value::None; let mut output = Value::None;
for expr in &self.v.exprs { for expr in &self.v.exprs {
output = expr.eval(ctx); output = expr.eval(ctx);
} }
if self.v.scopes {
ctx.scopes.pop();
}
output output
} }
} }

View File

@ -4,7 +4,7 @@ use std::iter;
use super::Value; use super::Value;
/// A hierarchy of scopes. /// A stack of scopes.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Scopes<'a> { pub struct Scopes<'a> {
/// The active scope. /// The active scope.
@ -21,6 +21,19 @@ impl<'a> Scopes<'a> {
Self { top: Scope::new(), scopes: vec![], base } 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. /// Define a variable in the active scope.
pub fn define(&mut self, var: impl Into<String>, value: impl Into<Value>) { pub fn define(&mut self, var: impl Into<String>, value: impl Into<Value>) {
self.top.define(var, value); self.top.define(var, value);
@ -29,7 +42,7 @@ impl<'a> Scopes<'a> {
/// Look up the value of a variable. /// Look up the value of a variable.
pub fn get(&self, var: &str) -> Option<&Value> { pub fn get(&self, var: &str) -> Option<&Value> {
iter::once(&self.top) iter::once(&self.top)
.chain(&self.scopes) .chain(self.scopes.iter().rev())
.chain(iter::once(self.base)) .chain(iter::once(self.base))
.find_map(|scope| scope.get(var)) .find_map(|scope| scope.get(var))
} }
@ -37,7 +50,7 @@ impl<'a> Scopes<'a> {
/// Get a mutable reference to a variable. /// Get a mutable reference to a variable.
pub fn get_mut(&mut self, var: &str) -> Option<&mut Value> { pub fn get_mut(&mut self, var: &str) -> Option<&mut Value> {
iter::once(&mut self.top) iter::once(&mut self.top)
.chain(&mut self.scopes) .chain(self.scopes.iter_mut().rev())
.find_map(|scope| scope.get_mut(var)) .find_map(|scope| scope.get_mut(var))
} }

View File

@ -52,7 +52,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
// Code block. // Code block.
Token::LeftBrace => { Token::LeftBrace => {
return Some(Node::Expr(block(p)?)); return Some(Node::Expr(block(p, false)?));
} }
// Markup. // Markup.
@ -285,7 +285,7 @@ fn primary(p: &mut Parser) -> Option<Expr> {
// Nested block. // Nested block.
Some(Token::LeftBrace) => { Some(Token::LeftBrace) => {
return block(p); return block(p, true);
} }
// Dictionary or just a parenthesized expression. // Dictionary or just a parenthesized expression.
@ -340,7 +340,7 @@ fn template(p: &mut Parser) -> Expr {
} }
/// Parse a block expression: `{...}`. /// Parse a block expression: `{...}`.
fn block(p: &mut Parser) -> Option<Expr> { fn block(p: &mut Parser, scopes: bool) -> Option<Expr> {
p.start_group(Group::Brace, TokenMode::Code); p.start_group(Group::Brace, TokenMode::Code);
let mut exprs = vec![]; let mut exprs = vec![];
while !p.eof() { while !p.eof() {
@ -355,7 +355,7 @@ fn block(p: &mut Parser) -> Option<Expr> {
p.skip_white(); p.skip_white();
} }
p.end_group(); p.end_group();
Some(Expr::Block(ExprBlock { exprs })) Some(Expr::Block(ExprBlock { exprs, scopes }))
} }
/// Parse a parenthesized function call. /// Parse a parenthesized function call.
@ -469,7 +469,7 @@ fn ident(p: &mut Parser) -> Option<Ident> {
fn body(p: &mut Parser) -> Option<Expr> { fn body(p: &mut Parser) -> Option<Expr> {
match p.peek() { match p.peek() {
Some(Token::LeftBracket) => Some(template(p)), Some(Token::LeftBracket) => Some(template(p)),
Some(Token::LeftBrace) => block(p), Some(Token::LeftBrace) => block(p, true),
_ => { _ => {
p.expected_at("body", p.last_end()); p.expected_at("body", p.last_end());
None None

View File

@ -147,6 +147,8 @@ pub type ExprGroup = SpanBox<Expr>;
pub struct ExprBlock { pub struct ExprBlock {
/// The list of expressions contained in the block. /// The list of expressions contained in the block.
pub exprs: SpanVec<Expr>, pub exprs: SpanVec<Expr>,
/// Whether the block should create a scope.
pub scopes: bool,
} }
impl Pretty for ExprBlock { impl Pretty for ExprBlock {