mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Scope variables in blocks 🏔️
This commit is contained in:
parent
710f88ccb2
commit
ce8138c685
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user