diff options
author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2022-04-11 17:19:30 +0200 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2022-04-21 10:08:42 +0200 |
commit | edfff8d863db0a1e5c1368404714ab9b78a917c7 (patch) | |
tree | fe189550e5725d567a07634123a8b24501d59f79 /askama_shared/src | |
parent | f447a2a8d20b5cd6995b5c33260db23d04716f17 (diff) | |
download | askama-edfff8d863db0a1e5c1368404714ab9b78a917c7.tar.gz askama-edfff8d863db0a1e5c1368404714ab9b78a917c7.tar.bz2 askama-edfff8d863db0a1e5c1368404714ab9b78a917c7.zip |
Update parser to allow "+" sign
Diffstat (limited to '')
-rw-r--r-- | askama_shared/src/generator.rs | 16 | ||||
-rw-r--r-- | askama_shared/src/parser.rs | 368 |
2 files changed, 218 insertions, 166 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index cae6389..7eb5fbe 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -1,6 +1,6 @@ use crate::heritage::{Context, Heritage}; use crate::input::{Print, Source, TemplateInput}; -use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Ws}; +use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Whitespace, Ws}; use crate::{filters, get_template_source, read_config_file, CompileError, Config}; use proc_macro2::TokenStream; @@ -238,7 +238,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { self.handle(ctx, ctx.nodes, buf, AstLevel::Top) }?; - self.flush_ws(Ws(false, false)); + self.flush_ws(Ws(None, None)); buf.writeln("::askama::Result::Ok(())")?; buf.writeln("}")?; @@ -1700,6 +1700,14 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { self.prepare_ws(ws); } + fn should_trim_ws(&self, ws: Option<Whitespace>) -> bool { + match ws { + Some(Whitespace::Trim) => true, + Some(Whitespace::Preserve) => false, + None => self.suppress_whitespace, + } + } + // If the previous literal left some trailing whitespace in `next_ws` and the // prefix whitespace suppressor from the given argument, flush that whitespace. // In either case, `next_ws` is reset to `None` (no trailing whitespace). @@ -1710,7 +1718,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { // If `suppress_whitespace` is enabled, we keep the whitespace characters only if there is // a `+` character. - if self.suppress_whitespace == ws.0 { + if !self.should_trim_ws(ws.0) { let val = self.next_ws.unwrap(); if !val.is_empty() { self.buf_writable.push(Writable::Lit(val)); @@ -1723,7 +1731,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { // argument, to determine whether to suppress leading whitespace from the // next literal. fn prepare_ws(&mut self, ws: Ws) { - self.skip_ws = self.suppress_whitespace != ws.1; + self.skip_ws = self.should_trim_ws(ws.1); } } diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index eeca326..4654e82 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -129,11 +129,27 @@ pub(crate) enum Target<'a> { Path(Vec<&'a str>), } -/// First field is "minus sign was used on the left part of the item". +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) enum Whitespace { + Preserve, + Trim, +} + +impl From<char> for Whitespace { + fn from(c: char) -> Self { + match c { + '+' => Self::Preserve, + '-' => Self::Trim, + _ => panic!("unsupported `Whitespace` conversion"), + } + } +} + +/// First field is "minus/plus sign was used on the left part of the item". /// /// Second field is "minus/plus sign was used on the right part of the item". #[derive(Clone, Copy, Debug, PartialEq)] -pub(crate) struct Ws(pub(crate) bool, pub(crate) bool); +pub(crate) struct Ws(pub(crate) Option<Whitespace>, pub(crate) Option<Whitespace>); pub(crate) type Cond<'a> = (Ws, Option<CondTest<'a>>, Vec<Node<'a>>); @@ -660,6 +676,10 @@ expr_prec_layer!(expr_compare, expr_bor, "==", "!=", ">=", ">", "<=", "<"); expr_prec_layer!(expr_and, expr_compare, "&&"); expr_prec_layer!(expr_or, expr_and, "||"); +fn expr_handle_ws(i: &str) -> IResult<&str, Whitespace> { + alt((char('-'), char('+')))(i).map(|(s, r)| (s, Whitespace::from(r))) +} + fn expr_any(i: &str) -> IResult<&str, Expr<'_>> { let range_right = |i| pair(ws(alt((tag("..="), tag("..")))), opt(expr_or))(i); alt(( @@ -679,31 +699,31 @@ fn expr_any(i: &str) -> IResult<&str, Expr<'_>> { fn expr_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( |i| tag_expr_start(i, s), - cut(tuple((opt(char('-')), ws(expr_any), opt(char('-')), |i| { - tag_expr_end(i, s) - }))), + cut(tuple(( + opt(expr_handle_ws), + ws(expr_any), + opt(expr_handle_ws), + |i| tag_expr_end(i, s), + ))), )); let (i, (_, (pws, expr, nws, _))) = p(i)?; - Ok((i, Node::Expr(Ws(pws.is_some(), nws.is_some()), expr))) + Ok((i, Node::Expr(Ws(pws, nws), expr))) } fn block_call(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("call")), cut(tuple(( opt(tuple((ws(identifier), ws(tag("::"))))), ws(identifier), ws(arguments), - opt(char('-')), + opt(expr_handle_ws), ))), )); let (i, (pws, _, (scope, name, args, nws))) = p(i)?; let scope = scope.map(|(scope, _)| scope); - Ok(( - i, - Node::Call(Ws(pws.is_some(), nws.is_some()), scope, name, args), - )) + Ok((i, Node::Call(Ws(pws, nws), scope, name, args))) } fn cond_if(i: &str) -> IResult<&str, CondTest<'_>> { @@ -725,86 +745,83 @@ fn cond_if(i: &str) -> IResult<&str, CondTest<'_>> { fn cond_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Cond<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("else")), cut(tuple(( opt(cond_if), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(|i| parse_template(i, s)), ))), )); let (i, (_, pws, _, (cond, nws, _, block))) = p(i)?; - Ok((i, (Ws(pws.is_some(), nws.is_some()), cond, block))) + Ok((i, (Ws(pws, nws), cond, block))) } fn block_if<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), cond_if, cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( |i| parse_template(i, s), many0(|i| cond_block(i, s)), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endif")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), )); let (i, (pws1, cond, (nws1, _, (block, elifs, (_, pws2, _, nws2))))) = p(i)?; - let mut res = vec![(Ws(pws1.is_some(), nws1.is_some()), Some(cond), block)]; + let mut res = vec![(Ws(pws1, nws1), Some(cond), block)]; res.extend(elifs); - Ok((i, Node::Cond(res, Ws(pws2.is_some(), nws2.is_some())))) + Ok((i, Node::Cond(res, Ws(pws2, nws2)))) } fn match_else_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("else")), cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(|i| parse_template(i, s)), ))), )); let (i, (_, pws, _, (nws, _, block))) = p(i)?; - Ok(( - i, - (Ws(pws.is_some(), nws.is_some()), Target::Name("_"), block), - )) + Ok((i, (Ws(pws, nws), Target::Name("_"), block))) } fn when_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("when")), cut(tuple(( ws(target), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(|i| parse_template(i, s)), ))), )); let (i, (_, pws, _, (target, nws, _, block))) = p(i)?; - Ok((i, (Ws(pws.is_some(), nws.is_some()), target, block))) + Ok((i, (Ws(pws, nws), target, block))) } fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("match")), cut(tuple(( ws(expr_any), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( ws(many0(ws(value((), |i| block_comment(i, s))))), @@ -813,9 +830,9 @@ fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { opt(|i| match_else_block(i, s)), cut(tuple(( ws(|i| tag_block_start(i, s)), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endmatch")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), @@ -828,25 +845,17 @@ fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { arms.push(arm); } - Ok(( - i, - Node::Match( - Ws(pws1.is_some(), nws1.is_some()), - expr, - arms, - Ws(pws2.is_some(), nws2.is_some()), - ), - )) + Ok((i, Node::Match(Ws(pws1, nws1), expr, arms, Ws(pws2, nws2)))) } fn block_let(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(alt((tag("let"), tag("set")))), cut(tuple(( ws(target), opt(tuple((ws(char('=')), ws(expr_any)))), - opt(char('-')), + opt(expr_handle_ws), ))), )); let (i, (pws, _, (var, val, nws))) = p(i)?; @@ -854,9 +863,9 @@ fn block_let(i: &str) -> IResult<&str, Node<'_>> { Ok(( i, if let Some((_, val)) = val { - Node::Let(Ws(pws.is_some(), nws.is_some()), var, val) + Node::Let(Ws(pws, nws), var, val) } else { - Node::LetDecl(Ws(pws.is_some(), nws.is_some()), var) + Node::LetDecl(Ws(pws, nws), var) }, )) } @@ -874,20 +883,20 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = preceded( ws(tag("else")), cut(tuple(( - opt(tag("-")), + opt(expr_handle_ws), delimited( |i| tag_block_end(i, s), |i| parse_template(i, s), |i| tag_block_start(i, s), ), - opt(tag("-")), + opt(expr_handle_ws), ))), ); let (i, (pws, nodes, nws)) = p(i)?; - Ok((i, (pws.is_some(), nodes, nws.is_some()))) + Ok((i, (pws, nodes, nws))) }; let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("for")), cut(tuple(( ws(target), @@ -895,16 +904,16 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { cut(tuple(( ws(expr_any), opt(if_cond), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( |i| parse_loop_content(i, s), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), opt(else_block), ws(tag("endfor")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), @@ -916,14 +925,14 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { Ok(( i, Node::Loop(Loop { - ws1: Ws(pws1.is_some(), nws1.is_some()), + ws1: Ws(pws1, nws1), var, iter, cond, body, - ws2: Ws(pws2.is_some(), nws3), + ws2: Ws(pws2, nws3), else_block, - ws3: Ws(pws3, nws2.is_some()), + ws3: Ws(pws3, nws2), }), )) } @@ -935,9 +944,9 @@ fn block_extends(i: &str) -> IResult<&str, Node<'_>> { fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut start = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("block")), - cut(tuple((ws(identifier), opt(char('-')), |i| { + cut(tuple((ws(identifier), opt(expr_handle_ws), |i| { tag_block_end(i, s) }))), )); @@ -947,67 +956,59 @@ fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { |i| parse_template(i, s), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endblock")), - cut(tuple((opt(ws(tag(name))), opt(char('-'))))), + cut(tuple((opt(ws(tag(name))), opt(expr_handle_ws)))), ))), ))); let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?; Ok(( i, - Node::BlockDef( - Ws(pws1.is_some(), nws1.is_some()), - name, - contents, - Ws(pws2.is_some(), nws2.is_some()), - ), + Node::BlockDef(Ws(pws1, nws1), name, contents, Ws(pws2, nws2)), )) } fn block_include(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("include")), - cut(pair(ws(str_lit), opt(char('-')))), + cut(pair(ws(str_lit), opt(expr_handle_ws))), )); let (i, (pws, _, (name, nws))) = p(i)?; - Ok((i, Node::Include(Ws(pws.is_some(), nws.is_some()), name))) + Ok((i, Node::Include(Ws(pws, nws), name))) } fn block_import(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("import")), cut(tuple(( ws(str_lit), ws(tag("as")), - cut(pair(ws(identifier), opt(char('-')))), + cut(pair(ws(identifier), opt(expr_handle_ws))), ))), )); let (i, (pws, _, (name, _, (scope, nws)))) = p(i)?; - Ok(( - i, - Node::Import(Ws(pws.is_some(), nws.is_some()), name, scope), - )) + Ok((i, Node::Import(Ws(pws, nws), name, scope))) } fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("macro")), cut(tuple(( ws(identifier), ws(parameters), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( |i| parse_template(i, s), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endmacro")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), @@ -1021,10 +1022,10 @@ fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { Node::Macro( name, Macro { - ws1: Ws(pws1.is_some(), nws1.is_some()), + ws1: Ws(pws1, nws1), args: params, nodes: contents, - ws2: Ws(pws2.is_some(), nws2.is_some()), + ws2: Ws(pws2, nws2), }, ), )) @@ -1033,17 +1034,17 @@ fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let endraw = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endraw")), - opt(char('-')), + opt(expr_handle_ws), peek(|i| tag_block_end(i, s)), )); let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("raw")), cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), consumed(skip_till(endraw)), ))), @@ -1054,27 +1055,31 @@ fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { Node::Lit(lws, val, rws) => (lws, val, rws), _ => unreachable!(), }; - let ws1 = Ws(pws1.is_some(), nws1.is_some()); - let ws2 = Ws(pws2.is_some(), nws2.is_some()); + let ws1 = Ws(pws1, nws1); + let ws2 = Ws(pws2, nws2); Ok((i, Node::Raw(ws1, lws, val, rws, ws2))) } fn break_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { - let mut p = tuple((opt(char('-')), ws(tag("break")), opt(char('-')))); + let mut p = tuple((opt(expr_handle_ws), ws(tag("break")), opt(expr_handle_ws))); let (j, (pws, _, nws)) = p(i)?; if s.loop_depth.get() == 0 { return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); } - Ok((j, Node::Break(Ws(pws.is_some(), nws.is_some())))) + Ok((j, Node::Break(Ws(pws, nws)))) } fn continue_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { - let mut p = tuple((opt(char('-')), ws(tag("continue")), opt(char('-')))); + let mut p = tuple(( + opt(expr_handle_ws), + ws(tag("continue")), + opt(expr_handle_ws), + )); let (j, (pws, _, nws)) = p(i)?; if s.loop_depth.get() == 0 { return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); } - Ok((j, Node::Continue(Ws(pws.is_some(), nws.is_some())))) + Ok((j, Node::Continue(Ws(pws, nws)))) } fn block_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { @@ -1123,13 +1128,20 @@ fn block_comment<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( |i| tag_comment_start(i, s), cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| block_comment_body(i, s), |i| tag_comment_end(i, s), ))), )); let (i, (_, (pws, tail, _))) = p(i)?; - Ok((i, Node::Comment(Ws(pws.is_some(), tail.ends_with('-'))))) + let nws = if tail.ends_with('-') { + Some(Whitespace::Trim) + } else if tail.ends_with('+') { + Some(Whitespace::Preserve) + } else { + None + }; + Ok((i, Node::Comment(Ws(pws, nws)))) } fn parse_template<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Vec<Node<'a>>> { @@ -1205,7 +1217,7 @@ pub(crate) fn parse<'a>( #[cfg(test)] mod tests { - use super::{Expr, Node, Ws}; + use super::{Expr, Node, Whitespace, Ws}; use crate::Syntax; fn check_ws_split(s: &str, res: &(&str, &str, &str)) { @@ -1242,29 +1254,23 @@ mod tests { let syntax = Syntax::default(); assert_eq!( super::parse("{{ strvar|e }}", &syntax).unwrap(), - vec![Node::Expr( - Ws(false, false), - Filter("e", vec![Var("strvar")]), - )], + vec![Node::Expr(Ws(None, None), Filter("e", vec![Var("strvar")]),)], ); assert_eq!( super::parse("{{ 2|abs }}", &syntax).unwrap(), - vec![Node::Expr( - Ws(false, false), - Filter("abs", vec![NumLit("2")]), - )], + vec![Node::Expr(Ws(None, None), Filter("abs", vec![NumLit("2")]),)], ); assert_eq!( super::parse("{{ -2|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Unary("-", NumLit("2").into())]), )], ); assert_eq!( super::parse("{{ (1 - 2)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter( "abs", vec![Group( @@ -1280,11 +1286,11 @@ mod tests { let syntax = Syntax::default(); assert_eq!( super::parse("{{ 2 }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::NumLit("2"),)], + vec![Node::Expr(Ws(None, None), Expr::NumLit("2"),)], ); assert_eq!( super::parse("{{ 2.5 }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::NumLit("2.5"),)], + vec![Node::Expr(Ws(None, None), Expr::NumLit("2.5"),)], ); } @@ -1294,16 +1300,16 @@ mod tests { assert_eq!( super::parse("{{ foo }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Var("foo"))], + vec![Node::Expr(Ws(None, None), Expr::Var("foo"))], ); assert_eq!( super::parse("{{ foo_bar }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Var("foo_bar"))], + vec![Node::Expr(Ws(None, None), Expr::Var("foo_bar"))], ); assert_eq!( super::parse("{{ none }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Var("none"))], + vec![Node::Expr(Ws(None, None), Expr::Var("none"))], ); } @@ -1313,16 +1319,16 @@ mod tests { assert_eq!( super::parse("{{ FOO }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["FOO"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO"]))], ); assert_eq!( super::parse("{{ FOO_BAR }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["FOO_BAR"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO_BAR"]))], ); assert_eq!( super::parse("{{ NONE }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["NONE"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["NONE"]))], ); } @@ -1332,12 +1338,12 @@ mod tests { assert_eq!( super::parse("{{ None }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["None"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["None"]))], ); assert_eq!( super::parse("{{ Some(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["Some"])), vec![Expr::NumLit("123")] @@ -1348,14 +1354,14 @@ mod tests { assert_eq!( super::parse("{{ Ok(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call(Box::new(Expr::Path(vec!["Ok"])), vec![Expr::NumLit("123")]), )], ); assert_eq!( super::parse("{{ Err(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call(Box::new(Expr::Path(vec!["Err"])), vec![Expr::NumLit("123")]), )], ); @@ -1366,7 +1372,7 @@ mod tests { assert_eq!( super::parse("{{ function(\"123\", 3) }}", &Syntax::default()).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Var("function")), vec![Expr::StrLit("123"), Expr::NumLit("3")] @@ -1382,14 +1388,14 @@ mod tests { assert_eq!( super::parse("{{ Option::None }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Path(vec!["Option", "None"]) )], ); assert_eq!( super::parse("{{ Option::Some(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["Option", "Some"])), vec![Expr::NumLit("123")], @@ -1400,7 +1406,7 @@ mod tests { assert_eq!( super::parse("{{ self::function(\"123\", 3) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["self", "function"])), vec![Expr::StrLit("123"), Expr::NumLit("3")], @@ -1415,7 +1421,7 @@ mod tests { assert_eq!( super::parse("{{ std::string::String::new() }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["std", "string", "String", "new"])), vec![] @@ -1425,7 +1431,7 @@ mod tests { assert_eq!( super::parse("{{ ::std::string::String::new() }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["", "std", "string", "String", "new"])), vec![] @@ -1452,7 +1458,7 @@ mod tests { assert_eq!( super::parse("{{ a + b == c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "==", BinOp("+", Var("a").into(), Var("b").into()).into(), @@ -1463,7 +1469,7 @@ mod tests { assert_eq!( super::parse("{{ a + b * c - d / e }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "-", BinOp( @@ -1479,7 +1485,7 @@ mod tests { assert_eq!( super::parse("{{ a * (b + c) / -d }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "/", BinOp( @@ -1495,7 +1501,7 @@ mod tests { assert_eq!( super::parse("{{ a || b && c || d && e }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "||", BinOp( @@ -1517,7 +1523,7 @@ mod tests { assert_eq!( super::parse("{{ a + b + c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "+", BinOp("+", Var("a").into(), Var("b").into()).into(), @@ -1528,7 +1534,7 @@ mod tests { assert_eq!( super::parse("{{ a * b * c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "*", BinOp("*", Var("a").into(), Var("b").into()).into(), @@ -1539,7 +1545,7 @@ mod tests { assert_eq!( super::parse("{{ a && b && c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "&&", BinOp("&&", Var("a").into(), Var("b").into()).into(), @@ -1550,7 +1556,7 @@ mod tests { assert_eq!( super::parse("{{ a + b - c + d }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "+", BinOp( @@ -1566,7 +1572,7 @@ mod tests { assert_eq!( super::parse("{{ a == b != c > d > e == f }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "==", BinOp( @@ -1598,7 +1604,7 @@ mod tests { assert_eq!( super::parse("{{ a[b](c) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Call( Box::new(Index(Box::new(Var("a")), Box::new(Var("b")))), vec![Var("c")], @@ -1608,7 +1614,7 @@ mod tests { assert_eq!( super::parse("{{ (a + b)(c) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Call( Box::new(Group(Box::new(BinOp( "+", @@ -1622,7 +1628,7 @@ mod tests { assert_eq!( super::parse("{{ a + b(c) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "+", Box::new(Var("a")), @@ -1633,7 +1639,7 @@ mod tests { assert_eq!( super::parse("{{ (-a)(b) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Call( Box::new(Group(Box::new(Unary("-", Box::new(Var("a")))))), vec![Var("b")], @@ -1643,7 +1649,7 @@ mod tests { assert_eq!( super::parse("{{ -a(b) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")])),), )], ); @@ -1655,32 +1661,70 @@ mod tests { assert_eq!( super::parse("{##}", s).unwrap(), - vec![Node::Comment(Ws(false, false))], + vec![Node::Comment(Ws(None, None))], ); assert_eq!( super::parse("{#- #}", s).unwrap(), - vec![Node::Comment(Ws(true, false))], + vec![Node::Comment(Ws(Some(Whitespace::Trim), None))], ); assert_eq!( super::parse("{# -#}", s).unwrap(), - vec![Node::Comment(Ws(false, true))], + vec![Node::Comment(Ws(None, Some(Whitespace::Trim)))], ); assert_eq!( super::parse("{#--#}", s).unwrap(), - vec![Node::Comment(Ws(true, true))], + vec![Node::Comment(Ws( + Some(Whitespace::Trim), + Some(Whitespace::Trim) + ))], ); - assert_eq!( super::parse("{#- foo\n bar -#}", s).unwrap(), - vec![Node::Comment(Ws(true, true))], + vec![Node::Comment(Ws( + Some(Whitespace::Trim), + Some(Whitespace::Trim) + ))], ); assert_eq!( super::parse("{#- foo\n {#- bar\n -#} baz -#}", s).unwrap(), - vec![Node::Comment(Ws(true, true))], + vec![Node::Comment(Ws( + Some(Whitespace::Trim), + Some(Whitespace::Trim) + ))], + ); + assert_eq!( + super::parse("{#+ #}", s).unwrap(), + vec![Node::Comment(Ws(Some(Whitespace::Preserve), None))], + ); + assert_eq!( + super::parse("{# +#}", s).unwrap(), + vec![Node::Comment(Ws(None, Some(Whitespace::Preserve)))], + ); + assert_eq!( + super::parse("{#++#}", s).unwrap(), + vec![Node::Comment(Ws( + Some(Whitespace::Preserve), + Some(Whitespace::Preserve) + ))], + ); + assert_eq!( + super::parse("{#+ foo\n bar +#}", s).unwrap(), + vec![Node::Comment(Ws( + Some(Whitespace::Preserve), + Some(Whitespace::Preserve) + ))], ); assert_eq!( + super::parse("{#+ foo\n {#+ bar\n +#} baz -+#}", s).unwrap(), + vec![Node::Comment(Ws( + Some(Whitespace::Preserve), + Some(Whitespace::Preserve) + ))], + ); + + assert_eq!( super::parse("{# foo {# bar #} {# {# baz #} qux #} #}", s).unwrap(), - vec![Node::Comment(Ws(false, false))], + vec![Node::Comment(Ws(None, None))], ); } @@ -1690,74 +1734,74 @@ mod tests { let syntax = Syntax::default(); assert_eq!( super::parse("{{ () }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![]),)], ); assert_eq!( super::parse("{{ (1) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Group(Box::new(NumLit("1"))),)], + vec![Node::Expr(Ws(None, None), Group(Box::new(NumLit("1"))),)], ); assert_eq!( super::parse("{{ (1,) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1, ) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1 ,) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1 , ) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1, 2) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2")]), )], ); assert_eq!( super::parse("{{ (1, 2,) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2")]), )], ); assert_eq!( super::parse("{{ (1, 2, 3) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]), )], ); assert_eq!( super::parse("{{ ()|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Tuple(vec![])]), )], ); assert_eq!( super::parse("{{ () | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))), )], ); assert_eq!( super::parse("{{ (1)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Group(Box::new(NumLit("1")))]), )], ); assert_eq!( super::parse("{{ (1) | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "|", Box::new(Group(Box::new(NumLit("1")))), @@ -1768,14 +1812,14 @@ mod tests { assert_eq!( super::parse("{{ (1,)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Tuple(vec![NumLit("1")])]), )], ); assert_eq!( super::parse("{{ (1,) | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "|", Box::new(Tuple(vec![NumLit("1")])), @@ -1786,14 +1830,14 @@ mod tests { assert_eq!( super::parse("{{ (1, 2)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]), )], ); assert_eq!( super::parse("{{ (1, 2) | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "|", Box::new(Tuple(vec![NumLit("1"), NumLit("2")])), |