Skip to content

Commit

Permalink
Added support for {% set %} (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Jul 1, 2022
1 parent a201af3 commit 0f2759e
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 114 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@ All notable changes to MiniJinja are documented here.

- Added support for `{% raw %}`. (#67)
- Minimum Rust version moved up to 1.45.
- Added support for `{% set %}`. (#70)

# 0.16.0

Expand Down
9 changes: 9 additions & 0 deletions minijinja/src/ast.rs
Expand Up @@ -56,6 +56,7 @@ pub enum Stmt<'a> {
ForLoop(Spanned<ForLoop<'a>>),
IfCond(Spanned<IfCond<'a>>),
WithBlock(Spanned<WithBlock<'a>>),
Set(Spanned<Set<'a>>),
Block(Spanned<Block<'a>>),
Extends(Spanned<Extends<'a>>),
Include(Spanned<Include<'a>>),
Expand All @@ -73,6 +74,7 @@ impl<'a> fmt::Debug for Stmt<'a> {
Stmt::ForLoop(s) => fmt::Debug::fmt(s, f),
Stmt::IfCond(s) => fmt::Debug::fmt(s, f),
Stmt::WithBlock(s) => fmt::Debug::fmt(s, f),
Stmt::Set(s) => fmt::Debug::fmt(s, f),
Stmt::Block(s) => fmt::Debug::fmt(s, f),
Stmt::Extends(s) => fmt::Debug::fmt(s, f),
Stmt::Include(s) => fmt::Debug::fmt(s, f),
Expand Down Expand Up @@ -151,6 +153,13 @@ pub struct WithBlock<'a> {
pub body: Vec<Stmt<'a>>,
}

/// A set statement.
#[cfg_attr(feature = "internal_debug", derive(Debug))]
pub struct Set<'a> {
pub target: Expr<'a>,
pub expr: Expr<'a>,
}

/// A block for inheritance elements.
#[cfg_attr(feature = "internal_debug", derive(Debug))]
pub struct Block<'a> {
Expand Down
5 changes: 5 additions & 0 deletions minijinja/src/compiler.rs
Expand Up @@ -261,6 +261,11 @@ impl<'source> Compiler<'source> {
}
self.add(Instruction::PopFrame);
}
ast::Stmt::Set(set) => {
self.set_location_from_span(set.span());
self.compile_expr(&set.expr)?;
self.compile_assignment(&set.target)?;
}
ast::Stmt::Block(block) => {
self.set_location_from_span(block.span());
let mut sub_compiler =
Expand Down
6 changes: 5 additions & 1 deletion minijinja/src/meta.rs
Expand Up @@ -172,6 +172,10 @@ pub fn find_undeclared_variables(source: &str) -> Result<HashSet<String>, Error>
stmt.body.iter().for_each(|x| walk(x, state));
state.pop();
}
ast::Stmt::Set(stmt) => {
assign_nested(&stmt.target, state);
visit_expr(&stmt.expr, state);
}
ast::Stmt::Block(stmt) => {
state.push();
state.assign("super");
Expand Down Expand Up @@ -233,7 +237,7 @@ pub fn find_referenced_templates(source: &str) -> Result<HashSet<String>, Error>
fn walk(node: &ast::Stmt, out: &mut HashSet<String>) {
match node {
ast::Stmt::Template(stmt) => stmt.children.iter().for_each(|x| walk(x, out)),
ast::Stmt::EmitExpr(_) | ast::Stmt::EmitRaw(_) => {}
ast::Stmt::EmitExpr(_) | ast::Stmt::EmitRaw(_) | ast::Stmt::Set(_) => {}
ast::Stmt::ForLoop(stmt) => stmt
.body
.iter()
Expand Down
19 changes: 19 additions & 0 deletions minijinja/src/parser.rs
Expand Up @@ -547,6 +547,10 @@ impl<'a> Parser<'a> {
self.parse_with_block()?,
self.stream.expand_span(span),
))),
Token::Ident("set") => Ok(ast::Stmt::Set(Spanned::new(
self.parse_set()?,
self.stream.expand_span(span),
))),
Token::Ident("block") => Ok(ast::Stmt::Block(Spanned::new(
self.parse_block()?,
self.stream.expand_span(span),
Expand Down Expand Up @@ -718,6 +722,21 @@ impl<'a> Parser<'a> {
Ok(ast::WithBlock { assignments, body })
}

fn parse_set(&mut self) -> Result<ast::Set<'a>, Error> {
let target = if matches!(self.stream.current()?, Some((Token::ParenOpen, _))) {
self.stream.next()?;
let assign = self.parse_assignment()?;
expect_token!(self, Token::ParenClose, "`)`")?;
assign
} else {
self.parse_assign_name()?
};
expect_token!(self, Token::Assign, "assignment operator")?;
let expr = self.parse_expr()?;

Ok(ast::Set { target, expr })
}

fn parse_block(&mut self) -> Result<ast::Block<'a>, Error> {
let (name, _) = expect_token!(self, Token::Ident(name) => name, "identifier")?;
expect_token!(self, Token::BlockEnd(..), "end of block")?;
Expand Down
14 changes: 14 additions & 0 deletions minijinja/src/syntax.rs
Expand Up @@ -18,6 +18,7 @@
//! - [`{% block %}`](#-block-)
//! - [`{% include %}`](#-include-)
//! - [`{% with %}`](#-with-)
//! - [`{% set %}`](#-set-)
//! - [`{% filter %}`](#-filter-)
//! - [`{% autoescape %}`](#-autoescape-)
//! - [`{% raw %}`](#-raw-)
Expand Down Expand Up @@ -405,6 +406,19 @@
//! {% endwith %}
//! ```
//!
//! ## `{% set %}`
//!
//! The `set` statement can be used to assign to variables on the same scope. This is
//! similar to how `with` works but it won't introduce a new scope.
//!
//! ```jinja
//! {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
//! ```
//!
//! Please keep in mind that it is not possible to set variables inside a block
//! and have them show up outside of it. This also applies to loops. The only
//! exception to that rule are if statements which do not introduce a scope.
//!
//! ## `{% filter %}`
//!
//! Filter sections allow you to apply regular [filters](crate::filters) on a
Expand Down
2 changes: 1 addition & 1 deletion minijinja/src/value.rs
Expand Up @@ -1601,7 +1601,7 @@ impl Iterator for ValueIterator {

impl ExactSizeIterator for ValueIterator {}

impl<'a> fmt::Debug for ValueIterator {
impl fmt::Debug for ValueIterator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ValueIterator").finish()
}
Expand Down

0 comments on commit 0f2759e

Please sign in to comment.