aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared/src/parser.rs
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/parser.rs
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/parser.rs')
-rw-r--r--askama_shared/src/parser.rs48
1 files changed, 42 insertions, 6 deletions
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<'_>> {