aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared/src
diff options
context:
space:
mode:
authorLibravatar René Kijewski <kijewski@library.vetmed.fu-berlin.de>2021-07-01 18:29:48 +0200
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2021-07-05 14:17:49 +0200
commit7e227907fe5d293b2d7620602d48ffba94e35dc5 (patch)
tree3dcbe0d932741fefb7a6ebf20748a9d0e2747f14 /askama_shared/src
parent3055c4b52160f729f27009eae5649e8201cbeecb (diff)
downloadaskama-7e227907fe5d293b2d7620602d48ffba94e35dc5.tar.gz
askama-7e227907fe5d293b2d7620602d48ffba94e35dc5.tar.bz2
askama-7e227907fe5d293b2d7620602d48ffba94e35dc5.zip
Implement destructoring of structs
This PR implements the destructoring of structs on the lhs of "let" and "for" statements.
Diffstat (limited to 'askama_shared/src')
-rw-r--r--askama_shared/src/generator.rs19
-rw-r--r--askama_shared/src/parser.rs48
2 files changed, 59 insertions, 8 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index a7f52ca..36d1841 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -824,9 +824,12 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
_ => false,
}
}
- Target::Tuple(targets) => targets
+ Target::Tuple(_, targets) => targets
.iter()
.any(|target| self.is_shadowing_variable(target)),
+ Target::Struct(_, named_targets) => named_targets
+ .iter()
+ .any(|(_, target)| self.is_shadowing_variable(target)),
}
}
@@ -1521,7 +1524,8 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
}
buf.write(name);
}
- Target::Tuple(targets) => {
+ Target::Tuple(path, targets) => {
+ buf.write(&path.join("::"));
buf.write("(");
for target in targets {
self.visit_target(buf, initialized, target);
@@ -1529,6 +1533,17 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
}
buf.write(")");
}
+ Target::Struct(path, targets) => {
+ buf.write(&path.join("::"));
+ buf.write(" { ");
+ for (name, target) in targets {
+ buf.write(normalize_identifier(name));
+ buf.write(": ");
+ self.visit_target(buf, initialized, target);
+ buf.write(",");
+ }
+ buf.write(" }");
+ }
}
}
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index a1aabe7..261887b 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -4,7 +4,7 @@ use nom::character::complete::{anychar, char, digit1};
use nom::combinator::{complete, map, opt, recognize, value};
use nom::error::{Error, ParseError};
use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1};
-use nom::sequence::{delimited, pair, preceded, tuple};
+use nom::sequence::{delimited, pair, preceded, terminated, tuple};
use nom::{self, error_position, Compare, IResult, InputTake};
use std::str;
@@ -138,7 +138,8 @@ pub struct Macro<'a> {
#[derive(Debug, PartialEq)]
pub enum Target<'a> {
Name(&'a str),
- Tuple(Vec<Target<'a>>),
+ Tuple(Vec<&'a str>, Vec<Target<'a>>),
+ Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>),
}
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -404,15 +405,21 @@ fn variant_path(i: &[u8]) -> IResult<&[u8], MatchVariant<'_>> {
})(i)
}
+fn named_target(i: &[u8]) -> IResult<&[u8], (&str, Target<'_>)> {
+ let (i, (src, target)) = pair(identifier, opt(preceded(ws(tag(":")), target)))(i)?;
+ Ok((i, (src, target.unwrap_or(Target::Name(src)))))
+}
+
fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
let mut opt_opening_paren = map(opt(ws(tag("("))), |o| o.is_some());
let mut opt_closing_paren = map(opt(ws(tag(")"))), |o| o.is_some());
+ // match tuples and unused parentheses
let (i, target_is_tuple) = opt_opening_paren(i)?;
if target_is_tuple {
let (i, is_empty_tuple) = opt_closing_paren(i)?;
if is_empty_tuple {
- return Ok((i, Target::Tuple(vec![])));
+ return Ok((i, Target::Tuple(Vec::new(), Vec::new())));
}
let (i, first_target) = target(i)?;
@@ -429,10 +436,39 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
opt(ws(tag(","))),
ws(tag(")")),
))(i)?;
- Ok((i, Target::Tuple(targets)))
- } else {
- map(identifier, Target::Name)(i)
+ return Ok((i, Target::Tuple(Vec::new(), targets)));
+ }
+
+ // match structs
+ let (i, path) = opt(path)(i)?;
+ if let Some(path) = path {
+ let (i, is_unnamed_struct) = opt_opening_paren(i)?;
+ if is_unnamed_struct {
+ let (i, targets) = alt((
+ map(tag(")"), |_| Vec::new()),
+ terminated(
+ separated_list1(ws(tag(",")), target),
+ pair(opt(ws(tag(","))), ws(tag(")"))),
+ ),
+ ))(i)?;
+ return Ok((i, Target::Tuple(path, targets)));
+ } else {
+ let (i, targets) = preceded(
+ ws(tag("{")),
+ alt((
+ map(tag("}"), |_| Vec::new()),
+ terminated(
+ separated_list1(ws(tag(",")), named_target),
+ pair(opt(ws(tag(","))), ws(tag("}"))),
+ ),
+ )),
+ )(i)?;
+ return Ok((i, Target::Struct(path, targets)));
+ }
}
+
+ // neither nor struct
+ map(identifier, Target::Name)(i)
}
fn variant_name(i: &[u8]) -> IResult<&[u8], MatchVariant<'_>> {