diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs index bdc6652d..e1dfc378 100644 --- a/askama_parser/src/expr.rs +++ b/askama_parser/src/expr.rs @@ -8,35 +8,30 @@ use winnow::token::{any, one_of, take, take_until}; use crate::node::CondTest; use crate::{ - CharLit, ErrorContext, HashSet, InputStream, Level, Num, ParseResult, PathOrIdentifier, StrLit, - StrPrefix, WithSpan, can_be_variable_name, char_lit, cut_error, filter, identifier, keyword, - not_suffix_with_hash, num_lit, path_or_identifier, skip_ws0, skip_ws1, str_lit, ws, + CharLit, ErrorContext, HashSet, InputStream, Num, ParseResult, PathOrIdentifier, StrLit, + StrPrefix, WithSpan, can_be_variable_name, char_lit, cut_error, filter, identifier, + is_rust_keyword, keyword, not_suffix_with_hash, num_lit, path_or_identifier, skip_ws0, + skip_ws1, str_lit, ws, }; macro_rules! expr_prec_layer { ( $name:ident, $inner:ident, $op:expr ) => { - fn $name( - i: &mut InputStream<'a>, - level: Level<'_>, - ) -> ParseResult<'a, WithSpan>> { - expr_prec_layer(i, level, Expr::$inner, |i: &mut _| $op.parse_next(i)) + fn $name(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { + expr_prec_layer(i, Expr::$inner, |i: &mut _| $op.parse_next(i)) } }; } -fn expr_prec_layer<'a>( - i: &mut InputStream<'a>, - level: Level<'_>, - inner: fn(&mut InputStream<'a>, Level<'_>) -> ParseResult<'a, WithSpan>>>, - op: fn(&mut InputStream<'a>) -> ParseResult<'a>, +fn expr_prec_layer<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, + inner: fn(&mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>>>, + op: fn(&mut InputStream<'a, 'l>) -> ParseResult<'a>, ) -> ParseResult<'a, WithSpan>>> { - let mut expr = inner(i, level)?; + let mut expr = inner(i)?; let mut i_before = *i; - let mut level_guard = level.guard(); - while let Some(((op, span), rhs)) = - opt((ws(op.with_span()), |i: &mut _| inner(i, level))).parse_next(i)? - { + let mut level_guard = i.state.level.guard(); + while let Some(((op, span), rhs)) = opt((ws(op.with_span()), inner)).parse_next(i)? { level_guard.nest(&i_before)?; expr = WithSpan::new(Box::new(Expr::BinOp(BinOp { op, lhs: expr, rhs })), span); i_before = *i; @@ -165,7 +160,7 @@ pub struct PathComponent<'a> { pub generics: Option>>>>, } -impl<'a> PathComponent<'a> { +impl<'a: 'l, 'l> PathComponent<'a> { #[inline] pub fn new_with_name(name: WithSpan<&'a str>) -> Self { Self { @@ -174,10 +169,10 @@ impl<'a> PathComponent<'a> { } } - pub(crate) fn parse(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Self> { + pub(crate) fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { let mut p = ( identifier.with_span(), - opt(preceded(ws("::"), |i: &mut _| TyGenerics::args(i, level))), + opt(preceded(ws("::"), TyGenerics::args)), ); let ((name, name_span), generics) = p.parse_next(i)?; Ok(Self { @@ -242,12 +237,11 @@ pub struct BinOp<'a> { pub rhs: WithSpan>>, } -impl<'a> Expr<'a> { +impl<'a: 'l, 'l> Expr<'a> { pub(super) fn arguments( - i: &mut InputStream<'a>, - level: Level<'_>, + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, WithSpan>>>> { - let _level_guard = level.nest(i)?; + let _level_guard = i.state.level.nest(i)?; let mut named_arguments = HashSet::default(); let mut p = ( ws('('.span()), @@ -261,8 +255,8 @@ impl<'a> Expr<'a> { let has_named_arguments = !named_arguments.is_empty(); let mut p = alt(( - move |i: &mut _| Self::named_argument(i, level, named_arguments), - move |i: &mut _| Self::parse(i, level, false), + move |i: &mut _| Self::named_argument(i, named_arguments), + move |i: &mut _| Self::parse(i, false), )); let expr = p.parse_next(i)?; if has_named_arguments && !matches!(**expr, Self::NamedArgument(..)) { @@ -287,13 +281,12 @@ impl<'a> Expr<'a> { } fn named_argument( - i: &mut InputStream<'a>, - level: Level<'_>, + i: &mut InputStream<'a, 'l>, named_arguments: &mut HashSet<&'a str>, ) -> ParseResult<'a, WithSpan>> { let (((argument, arg_span), _, value), span) = (identifier.with_span(), ws('='), move |i: &mut _| { - Self::parse(i, level, false) + Self::parse(i, false) }) .with_span() .parse_next(i)?; @@ -317,25 +310,20 @@ impl<'a> Expr<'a> { } pub(super) fn parse( - i: &mut InputStream<'a>, - level: Level<'_>, + i: &mut InputStream<'a, 'l>, allow_underscore: bool, ) -> ParseResult<'a, WithSpan>> { - let _level_guard = level.nest(i)?; - Self::range(i, level, allow_underscore) + let _level_guard = i.state.level.nest(i)?; + Self::range(i, allow_underscore) } fn range( - i: &mut InputStream<'a>, - level: Level<'_>, + i: &mut InputStream<'a, 'l>, allow_underscore: bool, ) -> ParseResult<'a, WithSpan>> { - let range_right = move |i: &mut InputStream<'a>| { - let ((op, span), rhs) = ( - ws(alt(("..=", "..")).with_span()), - opt(move |i: &mut _| Self::or(i, level)), - ) - .parse_next(i)?; + let range_right = move |i: &mut InputStream<'a, 'l>| { + let ((op, span), rhs) = + (ws(alt(("..=", "..")).with_span()), opt(Self::or)).parse_next(i)?; Ok((op, rhs, span)) }; @@ -345,19 +333,17 @@ impl<'a> Expr<'a> { }); // `expr..expr` or `expr..` - let range_from = (move |i: &mut _| Self::or(i, level), opt(range_right)).map( - move |(lhs, rhs)| match rhs { - Some((op, rhs, span)) => WithSpan::new( - Box::new(Self::Range(Range { - op, - lhs: Some(lhs), - rhs, - })), - span, - ), - None => lhs, - }, - ); + let range_from = (Self::or, opt(range_right)).map(move |(lhs, rhs)| match rhs { + Some((op, rhs, span)) => WithSpan::new( + Box::new(Self::Range(Range { + op, + lhs: Some(lhs), + rhs, + })), + span, + ), + None => lhs, + }); let expr = alt((range_to, range_from)).parse_next(i)?; check_expr( @@ -373,14 +359,10 @@ impl<'a> Expr<'a> { expr_prec_layer!(or, and, "||"); expr_prec_layer!(and, compare, "&&"); - fn compare(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { + fn compare(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { let mut parse_op = ws(alt(("==", "!=", ">=", ">", "<=", "<")).with_span()); - let (expr, rhs) = ( - |i: &mut _| Self::bor(i, level), - opt((parse_op.by_ref(), |i: &mut _| Self::bor(i, level))), - ) - .parse_next(i)?; + let (expr, rhs) = (Self::bor, opt((parse_op.by_ref(), Self::bor))).parse_next(i)?; let Some(((op, span), rhs)) = rhs else { return Ok(expr); }; @@ -405,15 +387,14 @@ impl<'a> Expr<'a> { expr_prec_layer!(shifts, addsub, alt((">>", "<<"))); expr_prec_layer!(addsub, concat, alt(("+", "-"))); - fn concat(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { + fn concat(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { #[allow(clippy::type_complexity)] - fn concat_expr<'a>( - i: &mut InputStream<'a>, - level: Level<'_>, + fn concat_expr<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, Option<(WithSpan>>, std::ops::Range)>> { let ws1 = |i: &mut _| opt(skip_ws1).parse_next(i); let tilde = (ws1, '~', ws1).with_span(); - let data = opt((tilde, |i: &mut _| Expr::muldivmod(i, level))).parse_next(i)?; + let data = opt((tilde, Expr::muldivmod)).parse_next(i)?; let Some((((t1, _, t2), span), expr)) = data else { return Ok(None); @@ -425,11 +406,11 @@ impl<'a> Expr<'a> { Ok(Some((expr, span))) } - let expr = Self::muldivmod(i, level)?; - let expr2 = concat_expr(i, level)?; + let expr = Self::muldivmod(i)?; + let expr2 = concat_expr(i)?; if let Some((expr2, span)) = expr2 { let mut exprs = vec![expr, expr2]; - while let Some((expr, _)) = concat_expr(i, level)? { + while let Some((expr, _)) = concat_expr(i)? { exprs.push(expr); } Ok(WithSpan::new(Box::new(Self::Concat(exprs)), span)) @@ -440,13 +421,13 @@ impl<'a> Expr<'a> { expr_prec_layer!(muldivmod, is_as, alt(("*", "/", "%"))); - fn is_as(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { - let lhs = Self::filtered(i, level)?; + fn is_as(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { + let lhs = Self::filtered(i)?; let checkpoint = i.checkpoint(); let rhs = opt(ws(identifier.with_span())).parse_next(i)?; match rhs { Some(("is", span)) => Self::is_as_handle_is(i, lhs, span), - Some(("as", span)) => Self::is_as_handle_as(i, level, lhs, span), + Some(("as", span)) => Self::is_as_handle_as(i, lhs, span), _ => { i.reset(&checkpoint); Ok(lhs) @@ -455,7 +436,7 @@ impl<'a> Expr<'a> { } fn is_as_handle_is( - i: &mut InputStream<'a>, + i: &mut InputStream<'a, 'l>, lhs: WithSpan>>, span: std::ops::Range, ) -> ParseResult<'a, WithSpan>> { @@ -483,12 +464,11 @@ impl<'a> Expr<'a> { } fn is_as_handle_as( - i: &mut InputStream<'a>, - level: Level<'_>, + i: &mut InputStream<'a, 'l>, lhs: WithSpan>>, span: std::ops::Range, ) -> ParseResult<'a, WithSpan>> { - let target = opt(|i: &mut _| path_or_identifier(i, level)).parse_next(i)?; + let target = opt(path_or_identifier).parse_next(i)?; let Some(PathOrIdentifier::Identifier(target)) = target else { return cut_error!( "`as` operator expects the name of a primitive type on its right-hand side, \ @@ -516,14 +496,12 @@ impl<'a> Expr<'a> { } } - fn filtered(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { - let mut res = Self::prefix(i, level)?; + fn filtered(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { + let mut res = Self::prefix(i)?; - let mut level_guard = level.guard(); + let mut level_guard = i.state.level.guard(); let mut i_before = *i; - while let Some((mut filter, span)) = - opt(ws((|i: &mut _| filter(i, level)).with_span())).parse_next(i)? - { + while let Some((mut filter, span)) = opt(ws(filter.with_span())).parse_next(i)? { level_guard.nest(&i_before)?; filter.arguments.insert(0, res); res = WithSpan::new(Box::new(Self::Filter(filter)), span); @@ -532,11 +510,11 @@ impl<'a> Expr<'a> { Ok(res) } - fn prefix(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { + fn prefix(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { // This is a rare place where we create recursion in the parsed AST // without recursing the parser call stack. However, this can lead // to stack overflows in drop glue when the AST is very deep. - let mut level_guard = level.guard(); + let mut level_guard = i.state.level.guard(); let mut i_before = *i; let mut ops = vec![]; while let Some(op) = opt(ws(alt(("!", "-", "*", "&")).with_span())).parse_next(i)? { @@ -545,7 +523,7 @@ impl<'a> Expr<'a> { i_before = *i; } - let mut expr = Suffix::parse(i, level)?; + let mut expr = Suffix::parse(i)?; for (op, span) in ops.into_iter().rev() { expr = WithSpan::new(Box::new(Self::Unary(op, expr)), span); } @@ -553,44 +531,36 @@ impl<'a> Expr<'a> { Ok(expr) } - fn single(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { + fn single(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { alt(( Self::num, Self::str, Self::char, - move |i: &mut _| Self::path_var_bool(i, level), - move |i: &mut _| Self::array(i, level), - move |i: &mut _| Self::group(i, level), + Self::path_var_bool, + Self::array, + Self::group, )) .parse_next(i) } - fn group(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { + fn group(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { (skip_ws0, peek('(')).parse_next(i)?; - Self::group_actually(i, level) + Self::group_actually(i) } // `Self::group()` is quite big. Let's only put it on the stack if needed. #[inline(never)] - fn group_actually( - i: &mut InputStream<'a>, - level: Level<'_>, - ) -> ParseResult<'a, WithSpan>> { - let (expr, span) = cut_err(preceded('(', |i: &mut _| { - Self::group_actually_inner(i, level) - })) - .with_span() - .parse_next(i)?; + fn group_actually(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { + let (expr, span) = cut_err(preceded('(', Self::group_actually_inner)) + .with_span() + .parse_next(i)?; Ok(WithSpan::new(expr, span)) } #[inline] - fn group_actually_inner( - i: &mut InputStream<'a>, - level: Level<'_>, - ) -> ParseResult<'a, Box> { + fn group_actually_inner(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box> { let (expr, comma, closing) = ( - ws(opt(|i: &mut _| Self::parse(i, level, true))), + ws(opt(|i: &mut _| Self::parse(i, true))), opt(terminated(','.span(), skip_ws0)), opt(')'), ) @@ -617,7 +587,7 @@ impl<'a> Expr<'a> { let collect_items = opt(separated( 1.., |i: &mut _| { - exprs.push(Self::parse(i, level, true)?); + exprs.push(Self::parse(i, true)?); Ok(()) }, ws(','), @@ -639,12 +609,12 @@ impl<'a> Expr<'a> { cut_error!(msg, span) } - fn array(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan>> { + fn array(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { let (array, span) = preceded( ws('['), cut_err(terminated( opt(terminated( - separated(1.., ws(move |i: &mut _| Self::parse(i, level, true)), ','), + separated(1.., ws(move |i: &mut _| Self::parse(i, true)), ','), ws(opt(',')), )), ']', @@ -658,13 +628,8 @@ impl<'a> Expr<'a> { )) } - fn path_var_bool( - i: &mut InputStream<'a>, - level: Level<'_>, - ) -> ParseResult<'a, WithSpan>> { - let (ret, span) = (|i: &mut _| path_or_identifier(i, level)) - .with_span() - .parse_next(i)?; + fn path_var_bool(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { + let (ret, span) = path_or_identifier.with_span().parse_next(i)?; let ret = match ret { PathOrIdentifier::Path(v) => Box::new(Self::Path(v)), PathOrIdentifier::Identifier(v) if *v == "true" => Box::new(Self::BoolLit(true)), @@ -674,17 +639,17 @@ impl<'a> Expr<'a> { Ok(WithSpan::new(ret, span)) } - fn str(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan>> { + fn str(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { let (s, span) = str_lit.with_span().parse_next(i)?; Ok(WithSpan::new(Box::new(Self::StrLit(s)), span)) } - fn num(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan>> { + fn num(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { let ((num, full), span) = num_lit.with_taken().with_span().parse_next(i)?; Ok(WithSpan::new(Box::new(Expr::NumLit(full, num)), span)) } - fn char(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan>> { + fn char(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>> { let (c, span) = char_lit.with_span().parse_next(i)?; Ok(WithSpan::new(Box::new(Self::CharLit(c)), span)) } @@ -722,7 +687,7 @@ impl<'a> Expr<'a> { } } -fn token_xor<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> { +fn token_xor<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> { let (good, span) = alt((keyword("xor").value(true), '^'.value(false))) .with_span() .parse_next(i)?; @@ -733,7 +698,7 @@ fn token_xor<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> { } } -fn token_bitand<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> { +fn token_bitand<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> { let (good, span) = alt((keyword("bitand").value(true), ('&', not('&')).value(false))) .with_span() .parse_next(i)?; @@ -750,12 +715,9 @@ pub struct Filter<'a> { pub arguments: Vec>>>, } -impl<'a> Filter<'a> { - pub(crate) fn parse(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Self> { - let mut p = ( - ws(|i: &mut _| path_or_identifier(i, level)), - opt(|i: &mut _| Expr::arguments(i, level)), - ); +impl<'a: 'l, 'l> Filter<'a> { + pub(crate) fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { + let mut p = (ws(path_or_identifier), opt(Expr::arguments)); let (name, arguments) = p.parse_next(i)?; Ok(Self { name, @@ -782,17 +744,14 @@ enum Suffix<'a> { Try, } -impl<'a> Suffix<'a> { - fn parse( - i: &mut InputStream<'a>, - level: Level<'_>, - ) -> ParseResult<'a, WithSpan>>> { - let mut level_guard = level.guard(); - let mut expr = Expr::single(i, level)?; +impl<'a: 'l, 'l> Suffix<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan>>> { + let mut level_guard = i.state.level.guard(); + let mut expr = Expr::single(i)?; let mut right = opt(alt(( - |i: &mut _| Self::associated_item(i, level), - |i: &mut _| Self::index(i, level), - |i: &mut _| Self::call(i, level), + Self::associated_item, + Self::index, + Self::call, Self::r#try, Self::r#macro, ))); @@ -852,7 +811,7 @@ impl<'a> Suffix<'a> { Ok(expr) } - fn r#macro(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan> { + fn r#macro(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Token { SomeOther, @@ -877,14 +836,14 @@ impl<'a> Suffix<'a> { } } - fn macro_arguments<'a>( - i: &mut InputStream<'a>, + fn macro_arguments<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, open_token: Group, ) -> ParseResult<'a, Suffix<'a>> { - fn inner<'a>( - i: &mut InputStream<'a>, + fn inner<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, open_token: Group, - ) -> ParseResult<'a, as Stream>::Checkpoint> { + ) -> ParseResult<'a, as Stream>::Checkpoint> { let mut open_list = vec![open_token]; loop { let before = i.checkpoint(); @@ -925,36 +884,50 @@ impl<'a> Suffix<'a> { Ok(Suffix::MacroCall(inner)) } - fn lifetime<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { - // Before the 2021 edition, we can have whitespace characters between "r#" and the - // identifier so we allow it here. - let start = *i; - '\''.parse_next(i)?; - let Some((is_raw, identifier)) = opt(alt(( - ('r', '#', identifier).map(|(_, _, ident)| (true, ident)), - (identifier, not(peek('#'))).map(|(ident, _)| (false, ident)), - ))) - .parse_next(i)? - else { - return cut_error!("wrong lifetime format", **start); - }; - if !is_raw { - if crate::is_rust_keyword(identifier) { - return cut_error!( - "a non-raw lifetime cannot be named like an existing keyword", - **start, - ); + fn lifetime<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + // this code assumes that we tried to match char literals before calling this function + let p = ( + '\''.void(), + identifier, + opt((repeat(1.., '#'), opt(identifier))), + opt('\'').map(|o| o.is_some()), + ); + let ((_, front, back, quot), span) = p.with_span().parse_next(i)?; + match (front, back, quot) { + // this case should never be encountered + (_, _, true) => cut_error!( + "cannot have multiple characters in a character literal, \ + use `\"...\"` to write a string", + span + ), + // a normal lifetime + (identifier, None, _) => { + if !is_rust_keyword(identifier) { + Ok(()) + } else { + cut_error!( + "a non-raw lifetime cannot be named like an existing keyword", + span, + ) + } } - } else if ["Self", "self", "crate", "super", "_"].contains(&identifier) { - return cut_error!(format!("`{identifier}` cannot be a raw lifetime"), **start,); + // a raw lifetime + ("r", Some((1, Some(identifier))), _) => { + if matches!(identifier, "Self" | "self" | "crate" | "super" | "_") { + cut_error!( + format!("`{}` cannot be a raw lifetime", identifier.escape_debug()), + span, + ) + } else { + Ok(()) + } + } + // an illegal prefix (not `'r#..`, multiple `#` or no identifier after `#`) + (_, Some(_), _) => cut_error!("wrong lifetime format", span), } - if opt(peek('\'')).parse_next(i)?.is_some() { - return cut_error!("unexpected `'` after lifetime", **start); - } - Ok(()) } - fn token<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Token> { + fn token<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Token> { // let some_other = alt(( // literals @@ -974,8 +947,8 @@ impl<'a> Suffix<'a> { alt((open.map(Token::Open), close.map(Token::Close), some_other)).parse_next(i) } - fn line_comment<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { - fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, bool> { + fn line_comment<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + fn inner<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, bool> { let mut p = ( "//".span(), alt(( @@ -1000,8 +973,8 @@ impl<'a> Suffix<'a> { doc_comment_no_bare_cr(i, inner) } - fn block_comment<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { - fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, bool> { + fn block_comment<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + fn inner<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, bool> { let is_doc_comment = alt(( ('*', not(peek(one_of(['*', '/'])))).value(true), '!'.value(true), @@ -1034,7 +1007,9 @@ impl<'a> Suffix<'a> { doc_comment_no_bare_cr(i, inner) } - fn identifier_or_prefixed_string<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { + fn identifier_or_prefixed_string<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, + ) -> ParseResult<'a, ()> { // let ((prefix, hashes, quot), prefix_span): ((_, usize, _), _) = @@ -1145,7 +1120,7 @@ impl<'a> Suffix<'a> { } } - fn hash<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Token> { + fn hash<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Token> { let (quot, span) = preceded('#', opt('"')).with_span().parse_next(i)?; if quot.is_some() { return cut_error!( @@ -1156,7 +1131,7 @@ impl<'a> Suffix<'a> { Ok(Token::SomeOther) } - fn punctuation<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { + fn punctuation<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { // // hash '#' omitted @@ -1200,7 +1175,7 @@ impl<'a> Suffix<'a> { alt((three_chars, two_chars, one_char)).parse_next(i) } - fn open<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Group> { + fn open<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Group> { alt(( '('.value(Group::Paren), '{'.value(Group::Brace), @@ -1209,7 +1184,7 @@ impl<'a> Suffix<'a> { .parse_next(i) } - fn close<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Group> { + fn close<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Group> { alt(( ')'.value(Group::Paren), '}'.value(Group::Brace), @@ -1223,10 +1198,7 @@ impl<'a> Suffix<'a> { Ok(WithSpan::new(inner, span)) } - fn associated_item( - i: &mut InputStream<'a>, - level: Level<'_>, - ) -> ParseResult<'a, WithSpan> { + fn associated_item(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let mut p = ( ws(terminated('.'.span(), not('.'))), cut_err(( @@ -1240,7 +1212,7 @@ impl<'a> Suffix<'a> { } Ok(WithSpan::new(name, span)) }, - opt(|i: &mut _| call_generics(i, level)), + opt(call_generics), )), ); let (span, (name, generics)) = p.parse_next(i)?; @@ -1250,10 +1222,10 @@ impl<'a> Suffix<'a> { )) } - fn index(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan> { + fn index(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let mut p = ( ws('['.span()), - cut_err((ws(move |i: &mut _| Expr::parse(i, level, true)), opt(']'))), + cut_err((ws(move |i: &mut _| Expr::parse(i, true)), opt(']'))), ); let (span, (expr, closed)) = p.parse_next(i)?; if closed.is_none() { @@ -1262,24 +1234,22 @@ impl<'a> Suffix<'a> { Ok(WithSpan::new(Self::Index(expr), span)) } - fn call(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, WithSpan> { - let mut p = (opt(|i: &mut _| call_generics(i, level)), |i: &mut _| { - Expr::arguments(i, level) - }); + fn call(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { + let mut p = (opt(call_generics), Expr::arguments); let (generics, args) = p.parse_next(i)?; let (args, span) = args.deconstruct(); Ok(WithSpan::new(Self::Call { generics, args }, span)) } - fn r#try(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan> { + fn r#try(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let span = preceded(skip_ws0, '?'.span()).parse_next(i)?; Ok(WithSpan::new(Self::Try, span)) } } -fn doc_comment_no_bare_cr<'a>( - i: &mut InputStream<'a>, - inner: fn(i: &mut InputStream<'a>) -> ParseResult<'a, bool>, +fn doc_comment_no_bare_cr<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, + inner: fn(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, bool>, ) -> ParseResult<'a, ()> { let ((is_doc_comment, comment), span) = inner.with_taken().with_span().parse_next(i)?; if is_doc_comment && comment.split('\r').skip(1).any(|s| !s.starts_with('\n')) { @@ -1307,8 +1277,8 @@ pub struct TyGenerics<'a> { pub args: Option>>>>, } -impl<'i> TyGenerics<'i> { - fn parse(i: &mut InputStream<'i>, level: Level<'_>) -> ParseResult<'i, WithSpan> { +impl<'a: 'l, 'l> TyGenerics<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let path = separated( 1.., ws(identifier @@ -1318,11 +1288,7 @@ impl<'i> TyGenerics<'i> { ) .map(|v: Vec<_>| v); - let p = ws(( - repeat(0.., ws('&')), - path, - opt(|i: &mut _| Self::args(i, level)), - )); + let p = ws((repeat(0.., ws('&')), path, opt(Self::args))); let ((refs, path, args), span) = p.with_span().parse_next(i)?; if let [name] = path.as_slice() { @@ -1347,27 +1313,25 @@ impl<'i> TyGenerics<'i> { } fn args( - i: &mut InputStream<'i>, - level: Level<'_>, - ) -> ParseResult<'i, WithSpan>>>> { + i: &mut InputStream<'a, 'l>, + ) -> ParseResult<'a, WithSpan>>>> { let mut p = cut_err(terminated( opt(terminated( - separated(1.., |i: &mut _| TyGenerics::parse(i, level), ws(',')), + separated(1.., TyGenerics::parse, ws(',')), ws(opt(',')), )), '>', )); let span = ws('<'.span()).parse_next(i)?; - let _level_guard = level.nest(i)?; + let _level_guard = i.state.level.nest(i)?; let args = p.parse_next(i)?; Ok(WithSpan::new(args.unwrap_or_default(), span)) } } -pub(crate) fn call_generics<'i>( - i: &mut InputStream<'i>, - level: Level<'_>, -) -> ParseResult<'i, WithSpan>>>> { - preceded(ws("::"), cut_err(|i: &mut _| TyGenerics::args(i, level))).parse_next(i) +pub(crate) fn call_generics<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, +) -> ParseResult<'a, WithSpan>>>> { + preceded(ws("::"), cut_err(TyGenerics::args)).parse_next(i) } diff --git a/askama_parser/src/lib.rs b/askama_parser/src/lib.rs index c93e8cb7..1851ff1a 100644 --- a/askama_parser/src/lib.rs +++ b/askama_parser/src/lib.rs @@ -103,7 +103,7 @@ mod _parsed { pub use _parsed::Parsed; -type InputStream<'a> = Stateful, ()>; +type InputStream<'a, 'l> = Stateful, &'l State<'l>>; #[derive(Debug, Default)] pub struct Ast<'a> { @@ -117,18 +117,16 @@ impl<'a> Ast<'a> { src: &'a str, file_path: Option>, syntax: &Syntax<'_>, - ) -> Result { - let level = Cell::new(Level::MAX_DEPTH); + ) -> Result, ParseError> { let state = State { - syntax, - loop_depth: Cell::new(0), - level: Level(&level), + syntax: *syntax, + ..State::default() }; let mut src = InputStream { input: LocatingSlice::new(src), - state: (), + state: &state, }; - match Node::parse_template(&mut src, &state) { + match Node::parse_template(&mut src) { Ok(nodes) if src.is_empty() => Ok(Self { nodes }), Ok(_) | Err(ErrMode::Incomplete(_)) => unreachable!(), Err( @@ -164,13 +162,6 @@ pub struct Span { end: usize, } -impl From<&InputStream<'_>> for Span { - #[inline] - fn from(i: &InputStream<'_>) -> Self { - (*i).into() - } -} - impl Default for Span { #[inline] fn default() -> Self { @@ -178,9 +169,16 @@ impl Default for Span { } } -impl From> for Span { +impl From<&InputStream<'_, '_>> for Span { #[inline] - fn from(mut i: InputStream<'_>) -> Self { + fn from(i: &InputStream<'_, '_>) -> Self { + (*i).into() + } +} + +impl From> for Span { + #[inline] + fn from(mut i: InputStream<'_, '_>) -> Self { let start = i.current_token_start(); i.finish(); Self { @@ -471,11 +469,11 @@ impl ErrorContext { } } -impl<'a> winnow::error::ParserError> for ErrorContext { +impl<'a: 'l, 'l> winnow::error::ParserError> for ErrorContext { type Inner = Self; #[inline] - fn from_input(input: &InputStream<'a>) -> Self { + fn from_input(input: &InputStream<'a, 'l>) -> Self { Self { span: input.into(), message: None, @@ -488,35 +486,35 @@ impl<'a> winnow::error::ParserError> for ErrorContext { } } -fn skip_ws0<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { +fn skip_ws0<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { take_while(0.., |c: char| c.is_ascii_whitespace()) .void() .parse_next(i) } -fn skip_ws1<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { +fn skip_ws1<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { take_while(1.., |c: char| c.is_ascii_whitespace()) .void() .parse_next(i) } -fn ws<'a, O>( - inner: impl ModalParser, O, ErrorContext>, -) -> impl ModalParser, O, ErrorContext> { +fn ws<'a: 'l, 'l, O>( + inner: impl ModalParser, O, ErrorContext>, +) -> impl ModalParser, O, ErrorContext> { delimited(skip_ws0, inner, skip_ws0) } -fn keyword<'a>(k: &str) -> impl ModalParser, &'a str, ErrorContext> { +fn keyword<'a: 'l, 'l>(k: &str) -> impl ModalParser, &'a str, ErrorContext> { identifier.verify(move |v: &str| v == k) } -fn identifier<'i>(input: &mut InputStream<'i>) -> ParseResult<'i> { +fn identifier<'a: 'l, 'l>(input: &mut InputStream<'a, 'l>) -> ParseResult<'a> { let head = any.verify(|&c| c == '_' || unicode_ident::is_xid_start(c)); let tail = take_while(.., unicode_ident::is_xid_continue); (head, tail).take().parse_next(input) } -fn bool_lit<'i>(i: &mut InputStream<'i>) -> ParseResult<'i> { +fn bool_lit<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> { alt((keyword("false"), keyword("true"))).parse_next(i) } @@ -526,11 +524,11 @@ pub enum Num<'a> { Float(&'a str, Option), } -fn num_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Num<'a>> { - fn num_lit_suffix<'a, T: Copy>( +fn num_lit<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Num<'a>> { + fn num_lit_suffix<'a: 'l, 'l, T: Copy>( kind: &'a str, list: &[(&str, T)], - i: &mut InputStream<'a>, + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, T> { let (suffix, span) = identifier.with_span().parse_next(i)?; if let Some(value) = list @@ -558,7 +556,7 @@ fn num_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Num<'a>> { // Equivalent to : // no `_` directly after the decimal point `.`, or between `e` and `+/-`. - let float = |i: &mut InputStream<'a>| -> ParseResult<'a, ()> { + let float = |i: &mut InputStream<'a, 'l>| -> ParseResult<'a, ()> { let has_dot = opt(('.', separated_digits(10, true))).parse_next(i)?; let has_exp = opt(|i: &mut _| { let ((kind, op), span) = (one_of(['e', 'E']), opt(one_of(['+', '-']))) @@ -605,10 +603,10 @@ fn num_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Num<'a>> { /// Underscore separated digits of the given base, unless `start` is true this may start /// with an underscore. -fn separated_digits<'a>( +fn separated_digits<'a: 'l, 'l>( radix: u32, start: bool, -) -> impl ModalParser, &'a str, ErrorContext> { +) -> impl ModalParser, &'a str, ErrorContext> { ( cond(!start, repeat(0.., '_').map(|()| ())), one_of(move |ch: char| ch.is_digit(radix)), @@ -661,10 +659,10 @@ pub struct StrLit<'a> { pub contains_high_ascii: bool, } -fn str_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, StrLit<'a>> { +fn str_lit<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, StrLit<'a>> { // - fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, StrLit<'a>> { + fn inner<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, StrLit<'a>> { #[derive(Debug, Clone, PartialEq, Eq)] enum Sequence<'a> { Text(&'a str), @@ -800,7 +798,7 @@ fn str_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, StrLit<'a>> { Ok(lit) } -fn not_suffix_with_hash<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { +fn not_suffix_with_hash<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { if let Some(span) = opt(identifier.span()).parse_next(i)? { return cut_error!( "you are missing a space to separate two string literals", @@ -810,7 +808,7 @@ fn not_suffix_with_hash<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> { Ok(()) } -fn str_lit_without_prefix<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> { +fn str_lit_without_prefix<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> { let (lit, span) = str_lit.with_span().parse_next(i)?; let kind = match lit.prefix { @@ -841,7 +839,7 @@ pub struct CharLit<'a> { // Information about allowed character escapes is available at: // . -fn char_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, CharLit<'a>> { +fn char_lit<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, CharLit<'a>> { let ((prefix, _, content, is_closed), span) = ( alt(('b'.value(Some(CharPrefix::Binary)), empty.value(None))), '\'', @@ -985,18 +983,13 @@ pub enum PathOrIdentifier<'a> { Identifier(WithSpan<&'a str>), } -fn path_or_identifier<'a>( - i: &mut InputStream<'a>, - level: Level<'_>, +fn path_or_identifier<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, PathOrIdentifier<'a>> { let mut p = |i: &mut _| { let root = ws(opt(terminated(empty.span(), "::"))); - let start = move |i: &mut _| PathComponent::parse(i, level); - let tail = opt(repeat( - 1.., - preceded(ws("::"), move |i: &mut _| PathComponent::parse(i, level)), - ) - .map(|v: Vec<_>| v)); + let start = PathComponent::parse; + let tail = opt(repeat(1.., preceded(ws("::"), PathComponent::parse)).map(|v: Vec<_>| v)); let (root, start, rest) = (root, start, tail).parse_next(i)?; Ok((root, start, rest.unwrap_or_default())) @@ -1037,41 +1030,50 @@ fn path_or_identifier<'a>( } } -struct State<'a, 'l> { - syntax: &'l Syntax<'a>, +#[derive(Debug, Clone, Default)] +struct State<'a> { + syntax: Syntax<'a>, loop_depth: Cell, - level: Level<'l>, + level: Level, } -impl State<'_, '_> { - fn tag_block_start<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> { - self.syntax.block_start.value(()).parse_next(i) - } +fn block_start<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + i.state.syntax.block_start.void().parse_next(i) +} - fn tag_block_end<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> { - let (control, span) = alt(( - self.syntax.block_end.value(None), - peek(delimited('%', alt(('-', '~', '+')).map(Some), '}')), - fail, // rollback on partial matches in the previous line - )) - .with_span() - .parse_next(i)?; +fn block_end<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + let (control, span) = alt(( + i.state.syntax.block_end.value(None), + peek(delimited('%', alt(('-', '~', '+')).map(Some), '}')), + fail, // rollback on partial matches in the previous line + )) + .with_span() + .parse_next(i)?; - let Some(control) = control else { - return Ok(()); - }; + let Some(control) = control else { + return Ok(()); + }; - let err = ErrorContext::new( - format!( - "unclosed block, you likely meant to apply whitespace control: \"{}{}\"", - control.escape_default(), - self.syntax.block_end.escape_default(), - ), - span, - ); - Err(err.backtrack()) - } + let err = ErrorContext::new( + format!( + "unclosed block, you likely meant to apply whitespace control: \"{}{}\"", + control.escape_default(), + i.state.syntax.block_end.escape_default(), + ), + span, + ); + Err(err.backtrack()) +} +fn expr_start<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + i.state.syntax.expr_start.void().parse_next(i) +} + +fn expr_end<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + i.state.syntax.expr_end.void().parse_next(i) +} + +impl State<'_> { fn enter_loop(&self) { self.loop_depth.set(self.loop_depth.get() + 1); } @@ -1244,27 +1246,34 @@ impl<'a> SyntaxBuilder<'a> { /// [`Level::nest()`] / [`LevelGuard::nest()`] will return an error. The same [`Level`] instance is /// shared across all usages in a [`Parsed::new()`] / [`Ast::from_str()`] call, using a reference /// to an interior mutable counter. -#[derive(Debug, Clone, Copy)] -struct Level<'l>(&'l Cell); +#[derive(Debug, Clone)] +struct Level(Cell); -impl Level<'_> { +impl Default for Level { + #[inline] + fn default() -> Self { + Self(Cell::new(Level::MAX_DEPTH)) + } +} + +impl Level { const MAX_DEPTH: usize = 128; /// Acquire a [`LevelGuard`] without decrementing the counter, to be used with loops. fn guard(&self) -> LevelGuard<'_> { LevelGuard { - level: *self, + level: self, count: 0, } } /// Decrement the remaining level counter, and return a [`LevelGuard`] that increments it again /// when it's dropped. - fn nest<'a>(&self, i: &InputStream<'a>) -> ParseResult<'a, LevelGuard<'_>> { + fn nest<'a: 'l, 'l>(&self, i: &InputStream<'a, 'l>) -> ParseResult<'a, LevelGuard<'_>> { if let Some(new_level) = self.0.get().checked_sub(1) { self.0.set(new_level); Ok(LevelGuard { - level: *self, + level: self, count: 1, }) } else { @@ -1274,7 +1283,7 @@ impl Level<'_> { #[cold] #[inline(never)] - fn _fail<'a, T>(i: &InputStream<'a>) -> ParseResult<'a, T> { + fn _fail<'a: 'l, 'l, T>(i: &InputStream<'a, 'l>) -> ParseResult<'a, T> { let msg = "your template code is too deeply nested, or the last expression is too complex"; Err(ErrorContext::new(msg, i).cut()) } @@ -1284,7 +1293,7 @@ impl Level<'_> { /// remaining level counter when it is dropped / falls out of scope. #[must_use] struct LevelGuard<'l> { - level: Level<'l>, + level: &'l Level, count: usize, } @@ -1296,7 +1305,7 @@ impl Drop for LevelGuard<'_> { impl LevelGuard<'_> { /// Used to decrement the level multiple times, e.g. for every iteration of a loop. - fn nest<'a>(&mut self, i: &InputStream<'a>) -> ParseResult<'a, ()> { + fn nest<'a: 'l, 'l>(&mut self, i: &InputStream<'a, 'l>) -> ParseResult<'a, ()> { if let Some(new_level) = self.level.0.get().checked_sub(1) { self.level.0.set(new_level); self.count += 1; @@ -1307,12 +1316,8 @@ impl LevelGuard<'_> { } } -fn filter<'a>(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Filter<'a>> { - preceded( - ('|', not('|')), - cut_err(|i: &mut _| Filter::parse(i, level)), - ) - .parse_next(i) +fn filter<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Filter<'a>> { + preceded(('|', not('|')), cut_err(Filter::parse)).parse_next(i) } /// Returns the common parts of two paths. @@ -1602,78 +1607,82 @@ mod test { assert_eq!(strip_common(&cwd, Path::new("/a/b/c")), "/a/b/c"); } - fn parse_peek<'a, T>( - mut parser: impl ModalParser, T, ErrorContext>, + #[track_caller] + fn parse_peek<'a: 'l, 'l, T>( + state: &'l State<'l>, + parser: impl FnOnce(&mut InputStream<'a, 'l>) -> ParseResult<'a, T>, input: &'a str, ) -> ParseResult<'a, (&'a str, T)> { let mut i = InputStream { input: LocatingSlice::new(input), - state: (), + state, }; - let value = parser.parse_next(&mut i)?; + let value = parser(&mut i)?; Ok((**i, value)) } #[test] fn test_num_lit() { + let s = State::default(); + // Should fail. - assert!(parse_peek(num_lit, ".").is_err()); + assert!(parse_peek(&s, num_lit, ".").is_err()); // Should succeed. assert_eq!( - parse_peek(num_lit, "1.2E-02").unwrap(), + parse_peek(&s, num_lit, "1.2E-02").unwrap(), ("", Num::Float("1.2E-02", None)) ); assert_eq!( - parse_peek(num_lit, "4e3").unwrap(), + parse_peek(&s, num_lit, "4e3").unwrap(), ("", Num::Float("4e3", None)), ); assert_eq!( - parse_peek(num_lit, "4e+_3").unwrap(), + parse_peek(&s, num_lit, "4e+_3").unwrap(), ("", Num::Float("4e+_3", None)), ); // Not supported because Rust wants a number before the `.`. - assert!(parse_peek(num_lit, ".1").is_err()); - assert!(parse_peek(num_lit, ".1E-02").is_err()); + assert!(parse_peek(&s, num_lit, ".1").is_err()); + assert!(parse_peek(&s, num_lit, ".1E-02").is_err()); // A `_` directly after the `.` denotes a field. assert_eq!( - parse_peek(num_lit, "1._0").unwrap(), + parse_peek(&s, num_lit, "1._0").unwrap(), ("._0", Num::Int("1", None)) ); assert_eq!( - parse_peek(num_lit, "1_.0").unwrap(), + parse_peek(&s, num_lit, "1_.0").unwrap(), ("", Num::Float("1_.0", None)) ); // Not supported (voluntarily because of `1..` syntax). assert_eq!( - parse_peek(num_lit, "1.").unwrap(), + parse_peek(&s, num_lit, "1.").unwrap(), (".", Num::Int("1", None)) ); assert_eq!( - parse_peek(num_lit, "1_.").unwrap(), + parse_peek(&s, num_lit, "1_.").unwrap(), (".", Num::Int("1_", None)) ); assert_eq!( - parse_peek(num_lit, "1_2.").unwrap(), + parse_peek(&s, num_lit, "1_2.").unwrap(), (".", Num::Int("1_2", None)) ); // Numbers with suffixes assert_eq!( - parse_peek(num_lit, "-1usize").unwrap(), + parse_peek(&s, num_lit, "-1usize").unwrap(), ("", Num::Int("-1", Some(IntKind::Usize))) ); assert_eq!( - parse_peek(num_lit, "123_f32").unwrap(), + parse_peek(&s, num_lit, "123_f32").unwrap(), ("", Num::Float("123_", Some(FloatKind::F32))) ); assert_eq!( - parse_peek(num_lit, "1_.2_e+_3_f64|into_isize").unwrap(), + parse_peek(&s, num_lit, "1_.2_e+_3_f64|into_isize").unwrap(), ( "|into_isize", Num::Float("1_.2_e+_3_", Some(FloatKind::F64)) ) ); assert_eq!( - parse_peek(num_lit, "4e3f128").unwrap(), + parse_peek(&s, num_lit, "4e3f128").unwrap(), ("", Num::Float("4e3", Some(FloatKind::F128))), ); } @@ -1684,43 +1693,59 @@ mod test { prefix: None, content: s, }; + let s = State::default(); - assert_eq!(parse_peek(char_lit, "'a'").unwrap(), ("", lit("a"))); - assert_eq!(parse_peek(char_lit, "'字'").unwrap(), ("", lit("字"))); + assert_eq!(parse_peek(&s, char_lit, "'a'").unwrap(), ("", lit("a"))); + assert_eq!(parse_peek(&s, char_lit, "'字'").unwrap(), ("", lit("字"))); // Escaped single characters. - assert_eq!(parse_peek(char_lit, "'\\\"'").unwrap(), ("", lit("\\\""))); - assert_eq!(parse_peek(char_lit, "'\\''").unwrap(), ("", lit("\\'"))); - assert_eq!(parse_peek(char_lit, "'\\t'").unwrap(), ("", lit("\\t"))); - assert_eq!(parse_peek(char_lit, "'\\n'").unwrap(), ("", lit("\\n"))); - assert_eq!(parse_peek(char_lit, "'\\r'").unwrap(), ("", lit("\\r"))); - assert_eq!(parse_peek(char_lit, "'\\0'").unwrap(), ("", lit("\\0"))); + assert_eq!( + parse_peek(&s, char_lit, "'\\\"'").unwrap(), + ("", lit("\\\"")) + ); + assert_eq!(parse_peek(&s, char_lit, "'\\''").unwrap(), ("", lit("\\'"))); + assert_eq!(parse_peek(&s, char_lit, "'\\t'").unwrap(), ("", lit("\\t"))); + assert_eq!(parse_peek(&s, char_lit, "'\\n'").unwrap(), ("", lit("\\n"))); + assert_eq!(parse_peek(&s, char_lit, "'\\r'").unwrap(), ("", lit("\\r"))); + assert_eq!(parse_peek(&s, char_lit, "'\\0'").unwrap(), ("", lit("\\0"))); // Escaped ascii characters (up to `0x7F`). - assert_eq!(parse_peek(char_lit, "'\\x12'").unwrap(), ("", lit("\\x12"))); - assert_eq!(parse_peek(char_lit, "'\\x02'").unwrap(), ("", lit("\\x02"))); - assert_eq!(parse_peek(char_lit, "'\\x6a'").unwrap(), ("", lit("\\x6a"))); - assert_eq!(parse_peek(char_lit, "'\\x7F'").unwrap(), ("", lit("\\x7F"))); + assert_eq!( + parse_peek(&s, char_lit, "'\\x12'").unwrap(), + ("", lit("\\x12")) + ); + assert_eq!( + parse_peek(&s, char_lit, "'\\x02'").unwrap(), + ("", lit("\\x02")) + ); + assert_eq!( + parse_peek(&s, char_lit, "'\\x6a'").unwrap(), + ("", lit("\\x6a")) + ); + assert_eq!( + parse_peek(&s, char_lit, "'\\x7F'").unwrap(), + ("", lit("\\x7F")) + ); // Escaped unicode characters (up to `0x10FFFF`). assert_eq!( - parse_peek(char_lit, "'\\u{A}'").unwrap(), + parse_peek(&s, char_lit, "'\\u{A}'").unwrap(), ("", lit("\\u{A}")) ); assert_eq!( - parse_peek(char_lit, "'\\u{10}'").unwrap(), + parse_peek(&s, char_lit, "'\\u{10}'").unwrap(), ("", lit("\\u{10}")) ); assert_eq!( - parse_peek(char_lit, "'\\u{aa}'").unwrap(), + parse_peek(&s, char_lit, "'\\u{aa}'").unwrap(), ("", lit("\\u{aa}")) ); assert_eq!( - parse_peek(char_lit, "'\\u{10FFFF}'").unwrap(), + parse_peek(&s, char_lit, "'\\u{10FFFF}'").unwrap(), ("", lit("\\u{10FFFF}")) ); // Check with `b` prefix. assert_eq!( - parse_peek(char_lit, "b'a'").unwrap(), + parse_peek(&s, char_lit, "b'a'").unwrap(), ( "", crate::CharLit { @@ -1731,20 +1756,21 @@ mod test { ); // Should fail. - assert!(parse_peek(char_lit, "''").is_err()); - assert!(parse_peek(char_lit, "'\\o'").is_err()); - assert!(parse_peek(char_lit, "'\\x'").is_err()); - assert!(parse_peek(char_lit, "'\\x1'").is_err()); - assert!(parse_peek(char_lit, "'\\x80'").is_err()); - assert!(parse_peek(char_lit, "'\\u'").is_err()); - assert!(parse_peek(char_lit, "'\\u{}'").is_err()); - assert!(parse_peek(char_lit, "'\\u{110000}'").is_err()); + assert!(parse_peek(&s, char_lit, "''").is_err()); + assert!(parse_peek(&s, char_lit, "'\\o'").is_err()); + assert!(parse_peek(&s, char_lit, "'\\x'").is_err()); + assert!(parse_peek(&s, char_lit, "'\\x1'").is_err()); + assert!(parse_peek(&s, char_lit, "'\\x80'").is_err()); + assert!(parse_peek(&s, char_lit, "'\\u'").is_err()); + assert!(parse_peek(&s, char_lit, "'\\u{}'").is_err()); + assert!(parse_peek(&s, char_lit, "'\\u{110000}'").is_err()); } #[test] fn test_str_lit() { + let s = State::default(); assert_eq!( - parse_peek(str_lit, r#"b"hello""#).unwrap(), + parse_peek(&s, str_lit, r#"b"hello""#).unwrap(), ( "", StrLit { @@ -1758,7 +1784,7 @@ mod test { ) ); assert_eq!( - parse_peek(str_lit, r#"c"hello""#).unwrap(), + parse_peek(&s, str_lit, r#"c"hello""#).unwrap(), ( "", StrLit { @@ -1771,7 +1797,7 @@ mod test { } ) ); - assert!(parse_peek(str_lit, r#"d"hello""#).is_err()); + assert!(parse_peek(&s, str_lit, r#"d"hello""#).is_err()); } #[test] diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs index a0821ed8..3b09cfc9 100644 --- a/askama_parser/src/node.rs +++ b/askama_parser/src/node.rs @@ -10,9 +10,9 @@ use winnow::token::{any, literal, rest, take, take_until}; use winnow::{ModalParser, Parser}; use crate::{ - ErrorContext, Expr, Filter, HashSet, InputStream, ParseErr, ParseResult, Span, State, Target, - WithSpan, cut_error, filter, identifier, is_rust_keyword, keyword, skip_ws0, - str_lit_without_prefix, ws, + ErrorContext, Expr, Filter, HashSet, InputStream, ParseErr, ParseResult, Span, Target, + WithSpan, block_end, block_start, cut_error, expr_end, expr_start, filter, identifier, + is_rust_keyword, keyword, skip_ws0, str_lit_without_prefix, ws, }; #[derive(Debug, PartialEq)] @@ -36,18 +36,12 @@ pub enum Node<'a> { FilterBlock(WithSpan>), } -impl<'a> Node<'a> { - pub(super) fn parse_template( - i: &mut InputStream<'a>, - s: &State<'_, '_>, - ) -> ParseResult<'a, Vec>> { - let mut p = parse_with_unexpected_fallback( - |i: &mut _| Self::many(i, s), - |i: &mut _| unexpected_tag(i, s), - ); +impl<'a: 'l, 'l> Node<'a> { + pub(super) fn parse_template(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec>> { + let mut p = parse_with_unexpected_fallback(Self::many, unexpected_tag); let nodes = p.parse_next(i)?; if !i.is_empty() { - opt(|i: &mut _| unexpected_tag(i, s)).parse_next(i)?; + opt(unexpected_tag).parse_next(i)?; return cut_error!( "cannot parse entire template\n\ you should never encounter this error\n\ @@ -58,24 +52,19 @@ impl<'a> Node<'a> { Ok(nodes) } - fn many(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Vec>> { + fn many(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec>> { repeat( 0.., - alt(( - |i: &mut _| Lit::parse(i, s), - |i: &mut _| Comment::parse(i, s), - |i: &mut _| Self::expr(i, s), - |i: &mut _| Self::parse(i, s), - )), + alt((Lit::parse, Comment::parse, Self::expr, Self::parse)), ) .map(|v: Vec<_>| v) .parse_next(i) } - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box> { let start = i.checkpoint(); let (span, tag) = ( - s.syntax.block_start.span(), + block_start.span(), peek(preceded((opt(Whitespace::parse), skip_ws0), identifier)), ) .parse_next(i)?; @@ -86,9 +75,9 @@ impl<'a> Node<'a> { "if" => If::parse, "for" => Loop::parse, "match" => Match::parse, - "extends" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Extends::parse(i), - "include" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Include::parse(i), - "import" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Import::parse(i), + "extends" => Extends::parse, + "include" => Include::parse, + "import" => Import::parse, "block" => BlockDef::parse, "macro" => Macro::parse, "raw" => Raw::parse, @@ -101,23 +90,22 @@ impl<'a> Node<'a> { } }; - let _level_guard = s.level.nest(i)?; - let node = func(i, s)?; - let closed = cut_node( - None, - alt(( - ws(eof).value(false), - (|i: &mut _| s.tag_block_end(i)).value(true), - )), - ) - .parse_next(i)?; + let _level_guard = i.state.level.nest(i)?; + let node = func(i)?; + let closed = + cut_node(None, alt((ws(eof).value(false), block_end.value(true)))).parse_next(i)?; match closed { true => Ok(node), - false => Err(ErrorContext::unclosed("block", s.syntax.block_end, span).cut()), + false => { + Err( + ErrorContext::unclosed("block", i.state.syntax.block_end, Span::new(span)) + .cut(), + ) + } } } - fn r#break(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { + fn r#break(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let mut p = ( opt(Whitespace::parse), ws(keyword("break").span()), @@ -125,13 +113,13 @@ impl<'a> Node<'a> { ); let (pws, span, nws) = p.parse_next(i)?; - if !s.is_in_loop() { + if !i.state.is_in_loop() { return cut_error!("you can only `break` inside a `for` loop", span); } Ok(Box::new(Self::Break(WithSpan::new(Ws(pws, nws), span)))) } - fn r#continue(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { + fn r#continue(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let mut p = ( opt(Whitespace::parse), ws(keyword("continue").span()), @@ -139,27 +127,27 @@ impl<'a> Node<'a> { ); let (pws, span, nws) = p.parse_next(i)?; - if !s.is_in_loop() { + if !i.state.is_in_loop() { return cut_error!("you can only `continue` inside a `for` loop", span); } Ok(Box::new(Self::Continue(WithSpan::new(Ws(pws, nws), span)))) } - fn expr(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box> { + fn expr(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box> { let mut p = ( - s.syntax.expr_start.span(), + expr_start.span(), cut_node( None, ( opt(Whitespace::parse), - ws(|i: &mut _| Expr::parse(i, s.level, false)), + ws(|i: &mut _| Expr::parse(i, false)), ), ), cut_node( None, ( opt(Whitespace::parse), - alt((s.syntax.expr_end.value(true), ws(eof).value(false))), + alt((expr_end.value(true), ws(eof).value(false))), ), ), ); @@ -168,7 +156,7 @@ impl<'a> Node<'a> { if closed { Ok(Box::new(Self::Expr(Ws(pws, nws), expr))) } else { - Err(ErrorContext::unclosed("expression", s.syntax.expr_end, start).cut()) + Err(ErrorContext::unclosed("expression", i.state.syntax.expr_end, start).cut()) } } @@ -197,15 +185,15 @@ impl<'a> Node<'a> { } #[inline] -fn parse_with_unexpected_fallback<'a, O>( - mut parser: impl ModalParser, O, ErrorContext>, - mut unexpected_parser: impl FnMut(&mut InputStream<'a>) -> ParseResult<'a, ()>, -) -> impl ModalParser, O, ErrorContext> { +fn parse_with_unexpected_fallback<'a: 'l, 'l, O>( + mut parser: impl ModalParser, O, ErrorContext>, + mut unexpected_parser: impl FnMut(&mut InputStream<'a, 'l>) -> ParseResult<'a, ()>, +) -> impl ModalParser, O, ErrorContext> { #[cold] #[inline(never)] - fn try_assign_fallback_error<'a>( - i: &mut InputStream<'a>, - unexpected_parser: &mut dyn FnMut(&mut InputStream<'a>) -> ParseResult<'a, ()>, + fn try_assign_fallback_error<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, + unexpected_parser: &mut dyn FnMut(&mut InputStream<'a, 'l>) -> ParseResult<'a, ()>, err: &mut ErrMode, ) { let (ErrMode::Backtrack(err_ctx) | ErrMode::Cut(err_ctx)) = &err else { @@ -227,7 +215,7 @@ fn parse_with_unexpected_fallback<'a, O>( i.reset(&checkpoint); } - move |i: &mut InputStream<'a>| { + move |i: &mut InputStream<'a, 'l>| { let mut result = parser.parse_next(i); if let Err(err) = &mut result { try_assign_fallback_error(i, &mut unexpected_parser, err); @@ -237,26 +225,24 @@ fn parse_with_unexpected_fallback<'a, O>( } #[inline] -fn cut_node<'a, O>( +fn cut_node<'a: 'l, 'l, O>( kind: Option<&'static str>, - inner: impl ModalParser, O, ErrorContext>, -) -> impl ModalParser, O, ErrorContext> { + inner: impl ModalParser, O, ErrorContext>, +) -> impl ModalParser, O, ErrorContext> { parse_with_unexpected_fallback(cut_err(inner), move |i: &mut _| unexpected_raw_tag(kind, i)) } -fn unexpected_tag<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, ()> { - ( - |i: &mut _| s.tag_block_start(i), - opt(Whitespace::parse), - |i: &mut _| unexpected_raw_tag(None, i), - ) +fn unexpected_tag<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { + (block_start, opt(Whitespace::parse), |i: &mut _| { + unexpected_raw_tag(None, i) + }) .void() .parse_next(i) } -fn unexpected_raw_tag<'a>( +fn unexpected_raw_tag<'a: 'l, 'l>( kind: Option<&'static str>, - i: &mut InputStream<'a>, + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, ()> { let (tag, span) = peek(ws(identifier.with_span())).parse_next(i)?; cut_error!( @@ -281,18 +267,18 @@ pub struct When<'a> { pub nodes: Vec>>, } -impl<'a> When<'a> { - fn r#else(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan> { +impl<'a: 'l, 'l> When<'a> { + fn r#else(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let mut p = ( - |i: &mut _| s.tag_block_start(i), + block_start, opt(Whitespace::parse), ws(keyword("else").span()), cut_node( Some("match-else"), ( opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), - cut_node(Some("match-else"), |i: &mut _| Node::many(i, s)), + block_end, + cut_node(Some("match-else"), Node::many), ), ), ); @@ -308,19 +294,19 @@ impl<'a> When<'a> { } #[allow(clippy::self_named_constructors)] - fn when(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan> { + fn when(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let mut p = ( - |i: &mut _| s.tag_block_start(i), + block_start, opt(Whitespace::parse), ws(keyword("when").span()), cut_node( Some("match-when"), ( - separated(1.., ws(|i: &mut _| Target::parse(i, s)), '|'), + separated(1.., ws(Target::parse), '|'), opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), - cut_node(Some("match-when"), |i: &mut _| Node::many(i, s)), - opt(|i: &mut _| Self::endwhen(i, s)), + block_end, + cut_node(Some("match-when"), Node::many), + opt(Self::endwhen), ), ), ); @@ -338,10 +324,10 @@ impl<'a> When<'a> { )) } - fn endwhen(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { + fn endwhen(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let mut p = ws(terminated( ( - |i: &mut _| s.tag_block_start(i), + block_start, opt(Whitespace::parse), ws(keyword("endwhen").span()), ), @@ -349,8 +335,8 @@ impl<'a> When<'a> { Some("match-endwhen"), ( opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), - repeat(0.., ws(|i: &mut _| Comment::parse(i, s)).void()).map(|()| ()), + block_end, + repeat(0.., ws(Comment::parse).void()).map(|()| ()), ), ), )); @@ -372,28 +358,25 @@ pub struct Cond<'a> { pub nodes: Vec>>, } -impl<'a> Cond<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan> { - let alt_else = ( - ws(keyword("else").span()), - opt(|i: &mut _| CondTest::parse(i, s)), - ); +impl<'a: 'l, 'l> Cond<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { + let alt_else = (ws(keyword("else").span()), opt(CondTest::parse)); let alt_elif = |i: &mut _| { let mut p = ( ws(keyword("elif").span()), - cut_node(Some("if-elif"), |i: &mut _| CondTest::parse_cond(i, s)), + cut_node(Some("if-elif"), CondTest::parse_cond), ); let (span, cond) = p.parse_next(i)?; Ok((span.clone(), Some(WithSpan::new(cond, span)))) }; let (_, pws, (span, cond), nws, _, nodes) = ( - |i: &mut _| s.tag_block_start(i), + block_start, opt(Whitespace::parse), alt((alt_else, alt_elif)), opt(Whitespace::parse), - cut_node(Some("if"), |i: &mut _| s.tag_block_end(i)), - cut_node(Some("if"), |i: &mut _| Node::many(i, s)), + cut_node(Some("if"), block_end), + cut_node(Some("if"), Node::many), ) .parse_next(i)?; @@ -415,39 +398,37 @@ pub struct CondTest<'a> { pub contains_bool_lit_or_is_defined: bool, } -impl<'a> CondTest<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan> { +impl<'a: 'l, 'l> CondTest<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan> { let mut p = ( ws(keyword("if").span()), - cut_node(Some("if"), |i: &mut _| Self::parse_cond(i, s)), + cut_node(Some("if"), Self::parse_cond), ); let (span, cond) = p.parse_next(i)?; Ok(WithSpan::new(cond, span)) } - fn parse_cond(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> { + fn parse_cond(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { let (target, expr) = ( opt(delimited( ws(alt((keyword("let"), keyword("set")))), - ws(|i: &mut _| Target::parse(i, s)), + ws(Target::parse), ws('='), )), - ws(|i: &mut InputStream<'a>| { + ws(|i: &mut InputStream<'a, 'l>| { let start_checkpoint = i.checkpoint(); let start_offset = i.current_token_start(); - let mut expr = Expr::parse(i, s.level, false)?; + let mut expr = Expr::parse(i, false)?; if let Expr::BinOp(v) = &mut *expr.inner && matches!(*v.rhs.inner, Expr::Var("set" | "let")) { - let _level_guard = s.level.nest(i)?; + let _level_guard = i.state.level.nest(i)?; i.reset(&start_checkpoint); i.next_slice(v.rhs.span.start - start_offset); - let (new_right, span) = (|i: &mut _| Self::parse_cond(i, s)) - .with_span() - .parse_next(i)?; + let (new_right, span) = Self::parse_cond.with_span().parse_next(i)?; v.rhs.inner = Box::new(Expr::LetCond(WithSpan::new(new_right, span))); } Ok(expr) @@ -474,7 +455,7 @@ pub enum Whitespace { } impl Whitespace { - fn parse<'i>(i: &mut InputStream<'i>) -> ParseResult<'i, Self> { + fn parse<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { any.verify_map(Self::parse_char).parse_next(i) } @@ -509,10 +490,9 @@ impl FromStr for Whitespace { } } -fn check_block_start<'a>( - i: &mut InputStream<'a>, +fn check_block_start<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, start: Span, - s: &State<'_, '_>, node: &str, expected: &str, ) -> ParseResult<'a, ()> { @@ -522,7 +502,7 @@ fn check_block_start<'a>( start, ); } - (|i: &mut _| s.tag_block_start(i)).parse_next(i) + i.state.syntax.block_start.void().parse_next(i) } #[derive(Debug, PartialEq)] @@ -537,15 +517,12 @@ pub struct Loop<'a> { pub ws3: Ws, } -impl<'a> Loop<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { - fn content<'a>( - i: &mut InputStream<'a>, - s: &State<'_, '_>, - ) -> ParseResult<'a, Vec>>> { - s.enter_loop(); - let result = (|i: &mut _| Node::many(i, s)).parse_next(i); - s.leave_loop(); +impl<'a: 'l, 'l> Loop<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { + fn content<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec>>> { + i.state.enter_loop(); + let result = Node::many(i); + i.state.leave_loop(); result } @@ -554,24 +531,17 @@ impl<'a> Loop<'a> { let if_cond = preceded( ws(keyword("if")), - cut_node( - Some("for-if"), - ws(|i: &mut _| Expr::parse(i, s.level, true)), - ), + cut_node(Some("for-if"), ws(|i: &mut _| Expr::parse(i, true))), ); - let else_block = |i: &mut _| { + let else_block = |i: &mut InputStream<'a, 'l>| { let mut p = preceded( ws(keyword("else")), cut_node( Some("for-else"), ( opt(Whitespace::parse), - delimited( - |i: &mut _| s.tag_block_end(i), - |i: &mut _| Node::many(i, s), - |i: &mut _| s.tag_block_start(i), - ), + delimited(block_end, Node::many, block_start), opt(Whitespace::parse), ), ), @@ -584,11 +554,11 @@ impl<'a> Loop<'a> { let (body, (_, pws, else_block, _, nws)) = cut_node( Some("for"), ( - |i: &mut _| content(i, s), + content, cut_node( Some("for"), ( - |i: &mut _| check_block_start(i, span, s, "for", "endfor"), + |i: &mut _| check_block_start(i, span, "for", "endfor"), opt(Whitespace::parse), opt(else_block), end_node("for", "endfor"), @@ -604,15 +574,15 @@ impl<'a> Loop<'a> { let mut p = cut_node( Some("for"), ( - ws(|i: &mut _| Target::parse(i, s)), + ws(Target::parse), ws(keyword("in")), cut_node( Some("for"), ( - ws(|i: &mut _| Expr::parse(i, s.level, true)), + ws(|i: &mut _| Expr::parse(i, true)), opt(if_cond), opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), + block_end, body_and_end, ), ), @@ -664,8 +634,8 @@ fn check_duplicated_name<'a>( Ok(()) } -impl<'a> Macro<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Macro<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let (pws1, keyword_span, (name, name_span)) = ( opt(Whitespace::parse), ws(keyword("macro").span()), @@ -683,10 +653,7 @@ impl<'a> Macro<'a> { let macro_arg = |i: &mut _| { let mut p = ( ws(identifier.with_span()), - opt(preceded( - '=', - ws(|i: &mut _| Expr::parse(i, s.level, false)), - )), + opt(preceded('=', ws(|i: &mut _| Expr::parse(i, false)))), ); let ((name, name_span), default) = p.parse_next(i)?; Ok(MacroArg { @@ -709,7 +676,7 @@ impl<'a> Macro<'a> { let (params, nws1, _) = cut_node( Some("macro"), - (parameters, opt(Whitespace::parse), s.syntax.block_end), + (parameters, opt(Whitespace::parse), block_end), ) .parse_next(i)?; @@ -745,11 +712,11 @@ impl<'a> Macro<'a> { let mut end = cut_node( Some("macro"), ( - |i: &mut _| Node::many(i, s), + Node::many, cut_node( Some("macro"), ( - |i: &mut _| check_block_start(i, keyword_span, s, "macro", "endmacro"), + |i: &mut _| check_block_start(i, keyword_span, "macro", "endmacro"), opt(Whitespace::parse), end_node("macro", "endmacro"), check_end_name(name, "macro"), @@ -780,21 +747,19 @@ pub struct FilterBlock<'a> { pub ws2: Ws, } -impl<'a> FilterBlock<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> FilterBlock<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let (pws1, span) = (opt(Whitespace::parse), ws(keyword("filter").span())).parse_next(i)?; let span = Span::new(span); - fn filters<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Filter<'a>> { - let mut filter = opt(ws(|i: &mut InputStream<'a>| filter(i, s.level)).with_span()); + fn filters<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Filter<'a>> { + let mut filter = opt(ws(filter.with_span())); - let (mut res, span) = (|i: &mut _| Filter::parse(i, s.level)) - .with_span() - .parse_next(i)?; + let (mut res, span) = Filter::parse.with_span().parse_next(i)?; res.arguments .insert(0, WithSpan::new(Box::new(Expr::FilterSource), span)); - let mut level_guard = s.level.guard(); + let mut level_guard = i.state.level.guard(); let mut i_before = *i; while let Some((mut filter, span)) = filter.parse_next(i)? { level_guard.nest(&i_before)?; @@ -810,17 +775,13 @@ impl<'a> FilterBlock<'a> { let mut p = ( cut_node( Some("filter"), - ( - ws(|i: &mut _| filters(i, s)), - opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), - ), + (ws(filters), opt(Whitespace::parse), block_end), ), - cut_node(Some("filter"), |i: &mut _| Node::many(i, s)), + cut_node(Some("filter"), Node::many), cut_node( Some("filter"), ( - |i: &mut _| check_block_start(i, span, s, "filter", "endfilter"), + |i: &mut _| check_block_start(i, span, "filter", "endfilter"), opt(Whitespace::parse), end_node("filter", "endfilter"), opt(Whitespace::parse), @@ -848,8 +809,8 @@ pub struct Import<'a> { pub scope: &'a str, } -impl<'a> Import<'a> { - fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Import<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let mut p = ( opt(Whitespace::parse), ws(keyword("import").span()), @@ -885,8 +846,8 @@ pub struct Call<'a> { pub ws2: Ws, } -impl<'a> Call<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Call<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let (pws, span) = (opt(Whitespace::parse), ws(keyword("call").span())).parse_next(i)?; let keyword_span = Span::new(span); @@ -915,9 +876,9 @@ impl<'a> Call<'a> { Ok(WithSpan::new(scope, span)) }), ws(identifier.with_span()), - opt(ws(|nested: &mut _| Expr::arguments(nested, s.level))), + opt(ws(Expr::arguments)), opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), + block_end, ), ), ); @@ -926,11 +887,11 @@ impl<'a> Call<'a> { let mut end = cut_node( Some("call"), ( - |i: &mut _| Node::many(i, s), + Node::many, cut_node( Some("call"), ( - |i: &mut _| check_block_start(i, keyword_span, s, "call", "endcall"), + |i: &mut _| check_block_start(i, keyword_span, "call", "endcall"), opt(Whitespace::parse), end_node("call", "endcall"), opt(Whitespace::parse), @@ -963,28 +924,28 @@ pub struct Match<'a> { pub ws2: Ws, } -impl<'a> Match<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Match<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let (pws1, span) = (opt(Whitespace::parse), ws(keyword("match").span())).parse_next(i)?; let span = Span::new(span); let mut p = cut_node( Some("match"), ( - ws(|i: &mut _| Expr::parse(i, s.level, false)), + ws(|i: &mut _| Expr::parse(i, false)), opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), + block_end, cut_node( Some("match"), ( - ws(repeat(0.., ws(|i: &mut _| Comment::parse(i, s)))).map(|()| ()), - repeat(0.., |i: &mut _| When::when(i, s)).map(|v: Vec<_>| v), + ws(repeat(0.., ws(Comment::parse))).map(|()| ()), + repeat(0.., When::when).map(|v: Vec<_>| v), ), ), - cut_node(Some("match"), opt(|i: &mut _| When::r#else(i, s))), + cut_node(Some("match"), opt(When::r#else)), cut_node( Some("match"), ( - ws(|i: &mut _| check_block_start(i, span, s, "match", "endmatch")), + ws(|i: &mut _| check_block_start(i, span, "match", "endmatch")), opt(Whitespace::parse), end_node("match", "endmatch"), opt(Whitespace::parse), @@ -1024,8 +985,8 @@ pub struct BlockDef<'a> { pub ws2: Ws, } -impl<'a> BlockDef<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> BlockDef<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let mut start = ( opt(Whitespace::parse), ws(keyword("block").span()), @@ -1034,7 +995,7 @@ impl<'a> BlockDef<'a> { ( ws(identifier.with_span()), opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), + block_end, ), ), ); @@ -1044,11 +1005,11 @@ impl<'a> BlockDef<'a> { let mut end = cut_node( Some("block"), ( - |i: &mut _| Node::many(i, s), + Node::many, cut_node( Some("block"), ( - |i: &mut _| check_block_start(i, keyword_span, s, "block", "endblock"), + |i: &mut _| check_block_start(i, keyword_span, "block", "endblock"), opt(Whitespace::parse), end_node("block", "endblock"), check_end_name(name, "block"), @@ -1070,11 +1031,11 @@ impl<'a> BlockDef<'a> { } } -fn check_end_name<'a>( +fn check_end_name<'a: 'l, 'l>( name: &'a str, kind: &'static str, -) -> impl ModalParser, Option, ErrorContext> { - let name = move |i: &mut InputStream<'a>| { +) -> impl ModalParser, Option, ErrorContext> { + let name = move |i: &mut InputStream<'a, 'l>| { let Some((end_name, span)) = ws(opt(identifier.with_span())).parse_next(i)? else { return Ok(()); }; @@ -1101,14 +1062,14 @@ pub struct Lit<'a> { pub rws: WithSpan<&'a str>, } -impl<'a> Lit<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Lit<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let content = take_until( .., ( - s.syntax.block_start, - s.syntax.comment_start, - s.syntax.expr_start, + i.state.syntax.block_start, + i.state.syntax.comment_start, + i.state.syntax.expr_start, ), ); let (content, span) = preceded(not(eof), alt((content, rest))) @@ -1153,11 +1114,10 @@ pub struct Raw<'a> { pub ws2: Ws, } -impl<'a> Raw<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { - fn endraw<'a>( - i: &mut InputStream<'a>, - s: &State<'_, '_>, +impl<'a: 'l, 'l> Raw<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { + fn endraw<'a: 'l, 'l>( + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, (Ws, WithSpan>)> { let start_i = ***i; let start_idx = i.current_token_start(); @@ -1178,7 +1138,7 @@ impl<'a> Raw<'a> { (inner, None) }; - let Some(inner) = inner.strip_suffix(s.syntax.block_start) else { + let Some(inner) = inner.strip_suffix(i.state.syntax.block_start) else { continue; }; let span = start_idx..start_idx + inner.len(); @@ -1187,7 +1147,7 @@ impl<'a> Raw<'a> { skip_ws0(i)?; let i_before_nws = i.checkpoint(); let nws = opt(Whitespace::parse).parse_next(i)?; - if opt(peek(s.syntax.block_end)).parse_next(i)?.is_none() { + if opt(peek(block_end)).parse_next(i)?.is_none() { i.reset(&i_before_nws); // `block_start` might start with the `nws` character continue; } @@ -1204,11 +1164,7 @@ impl<'a> Raw<'a> { ws(keyword("raw").span()), cut_node( Some("raw"), - separated_pair( - opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), - |i: &mut _| endraw(i, s), - ), + separated_pair(opt(Whitespace::parse), block_end, endraw), ), ); let (pws, span, (nws, (ws2, lit))) = p.parse_next(i)?; @@ -1228,8 +1184,8 @@ pub struct Let<'a> { pub is_mutable: bool, } -impl<'a> Let<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Let<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let mut p = ( opt(Whitespace::parse), ws(alt((keyword("let"), keyword("set"))).span()), @@ -1237,11 +1193,8 @@ impl<'a> Let<'a> { cut_node( Some("let"), ( - ws((|i: &mut _| Target::parse(i, s)).with_span()), - opt(preceded( - ws('='), - ws(|i: &mut _| Expr::parse(i, s.level, false)), - )), + ws(Target::parse.with_span()), + opt(preceded(ws('='), ws(|i: &mut _| Expr::parse(i, false)))), opt(Whitespace::parse), ), ), @@ -1300,15 +1253,14 @@ pub struct If<'a> { pub branches: Vec>>, } -impl<'a> If<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { - let (pws1, cond) = - (opt(Whitespace::parse), (|i: &mut _| CondTest::parse(i, s))).parse_next(i)?; +impl<'a: 'l, 'l> If<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { + let (pws1, cond) = (opt(Whitespace::parse), CondTest::parse).parse_next(i)?; let cond_span = cond.span; let end_if = cut_node( Some("if"), ( - |i: &mut _| check_block_start(i, cond_span, s, "if", "endif"), + |i: &mut _| check_block_start(i, cond_span, "if", "endif"), opt(Whitespace::parse), end_node("if", "endif"), opt(Whitespace::parse), @@ -1318,12 +1270,12 @@ impl<'a> If<'a> { Some("if"), ( opt(Whitespace::parse), - |i: &mut _| s.tag_block_end(i), + block_end, cut_node( Some("if"), ( - |i: &mut _| Node::many(i, s), - repeat(0.., |i: &mut _| Cond::parse(i, s)).map(|v: Vec<_>| v), + Node::many, + repeat(0.., Cond::parse).map(|v: Vec<_>| v), end_if, ), ), @@ -1357,8 +1309,8 @@ pub struct Include<'a> { pub path: &'a str, } -impl<'a> Include<'a> { - fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Include<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let p = ( opt(Whitespace::parse), ws(keyword("include")), @@ -1383,8 +1335,8 @@ pub struct Extends<'a> { pub path: &'a str, } -impl<'a> Extends<'a> { - fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box>> { +impl<'a: 'l, 'l> Extends<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { let p = preceded( (opt(Whitespace::parse), ws(keyword("extends"))), cut_node( @@ -1403,18 +1355,22 @@ pub struct Comment<'a> { pub content: &'a str, } -impl<'a> Comment<'a> { - fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box>> { - fn content<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, ()> { +impl<'a: 'l, 'l> Comment<'a> { + fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box>> { + fn content<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> { let mut depth = 0usize; loop { - take_until(.., (s.syntax.comment_start, s.syntax.comment_end)).parse_next(i)?; - let is_open = opt(s.syntax.comment_start).parse_next(i)?.is_some(); + take_until( + .., + (i.state.syntax.comment_start, i.state.syntax.comment_end), + ) + .parse_next(i)?; + let is_open = opt(i.state.syntax.comment_start).parse_next(i)?.is_some(); if is_open { // cannot overflow: `i` cannot be longer than `isize::MAX`, cf. [std::alloc::Layout] depth += 1; } else if let Some(new_depth) = depth.checked_sub(1) { - literal(s.syntax.comment_end).parse_next(i)?; + literal(i.state.syntax.comment_end).parse_next(i)?; depth = new_depth; } else { return Ok(()); @@ -1422,19 +1378,18 @@ impl<'a> Comment<'a> { } } - fn comment<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a> { - let start = s.syntax.comment_start.span().parse_next(i)?; - let mut content = opt(terminated( - (|i: &mut _| content(i, s)).take(), - s.syntax.comment_end, - )); + fn comment<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> { + let start = i.state.syntax.comment_start.span().parse_next(i)?; + let mut content = opt(terminated(content.take(), i.state.syntax.comment_end)); let Some(content) = content.parse_next(i)? else { - return Err(ErrorContext::unclosed("comment", s.syntax.comment_end, start).cut()); + return Err( + ErrorContext::unclosed("comment", i.state.syntax.comment_end, start).cut(), + ); }; Ok(content) } - let (content, span) = (|i: &mut _| comment(i, s)).with_span().parse_next(i)?; + let (content, span) = comment.with_span().parse_next(i)?; let ws = match *content.as_bytes() { [b'-' | b'+' | b'~'] => { return cut_error!( @@ -1442,7 +1397,7 @@ impl<'a> Comment<'a> { "ambiguous whitespace stripping\n\ use `{}{content} {content}{}` to apply the same whitespace stripping on \ both sides", - s.syntax.comment_start, s.syntax.comment_end, + i.state.syntax.comment_start, i.state.syntax.comment_end, ), span, ); @@ -1463,11 +1418,11 @@ impl<'a> Comment<'a> { #[derive(Clone, Copy, Debug, PartialEq)] pub struct Ws(pub Option, pub Option); -fn end_node<'a, 'g: 'a>( +fn end_node<'a: 'l, 'g: 'a, 'l>( node: &'g str, expected: &'g str, -) -> impl ModalParser, &'a str, ErrorContext> + 'g { - move |i: &mut InputStream<'a>| { +) -> impl ModalParser, &'a str, ErrorContext> + 'g { + move |i: &mut InputStream<'a, 'l>| { let start = i.checkpoint(); let (actual, span) = ws(identifier.with_span()).parse_next(i)?; if actual == expected { diff --git a/askama_parser/src/target.rs b/askama_parser/src/target.rs index b701dce0..530b53f7 100644 --- a/askama_parser/src/target.rs +++ b/askama_parser/src/target.rs @@ -6,8 +6,8 @@ use winnow::{ModalParser, Parser}; use crate::{ CharLit, ErrorContext, InputStream, Num, ParseErr, ParseResult, PathComponent, - PathOrIdentifier, Span, State, StrLit, WithSpan, bool_lit, can_be_variable_name, char_lit, - cut_error, identifier, is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit, ws, + PathOrIdentifier, Span, StrLit, WithSpan, bool_lit, can_be_variable_name, char_lit, cut_error, + identifier, is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit, ws, }; #[derive(Clone, Debug, PartialEq)] @@ -40,7 +40,7 @@ impl<'a> From<(WithSpan<&'a str>, Target<'a>)> for NamedTarget<'a> { } } -impl<'a> Target<'a> { +impl<'a: 'l, 'l> Target<'a> { pub fn span(&self) -> Span { match self { Target::Name(v) => v.span(), @@ -59,17 +59,15 @@ impl<'a> Target<'a> { } /// Parses multiple targets with `or` separating them - pub(super) fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> { + pub(super) fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { enum OneOrMany<'a> { One(Target<'a>), Many(Vec>), } - let mut or_more = opt(preceded(ws(keyword("or")), |i: &mut _| { - Self::parse_one(i, s) - })); + let mut or_more = opt(preceded(ws(keyword("or")), Self::parse_one)); let one_or_many = |i: &mut _| { - let target = Self::parse_one(i, s)?; + let target = Self::parse_one(i)?; let Some(snd_target) = or_more.parse_next(i)? else { return Ok(OneOrMany::One(target)); }; @@ -81,7 +79,7 @@ impl<'a> Target<'a> { Ok(OneOrMany::Many(targets)) }; - let _level_guard = s.level.nest(i)?; + let _level_guard = i.state.level.nest(i)?; let (inner, span) = one_or_many.with_span().parse_next(i)?; match inner { OneOrMany::One(target) => Ok(target), @@ -90,7 +88,7 @@ impl<'a> Target<'a> { } /// Parses a single target without an `or`, unless it is wrapped in parentheses. - fn parse_one(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> { + fn parse_one(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { let mut opt_opening_paren = opt(ws('(').span()).map(|o| o.is_some()); let mut opt_opening_brace = opt(ws('{').span()).map(|o| o.is_some()); let mut opt_opening_bracket = opt(ws('[').span()).map(|o| o.is_some()); @@ -105,8 +103,7 @@ impl<'a> Target<'a> { // match tuples let target_is_tuple = opt_opening_paren.parse_next(i)?; if target_is_tuple { - let (is_singleton, mut targets) = - collect_targets(i, ')', |i: &mut _| Self::unnamed(i, s))?; + let (is_singleton, mut targets) = collect_targets(i, ')', Self::unnamed)?; if is_singleton && let Some(target) = targets.pop() { return Ok(target); } @@ -119,14 +116,14 @@ impl<'a> Target<'a> { // match array let target_is_array = opt_opening_bracket.parse_next(i)?; if target_is_array { - let targets = collect_targets(i, ']', |i: &mut _| Self::unnamed(i, s))?.1; + let targets = collect_targets(i, ']', Self::unnamed)?.1; let inner = only_one_rest_pattern(targets, true, "array")?; let range = start..i.current_token_start(); return Ok(Self::Array(WithSpan::new(inner, range))); } // match structs - let path = (|i: &mut _| path_or_identifier(i, s.level)).verify_map(|r| match r { + let path = path_or_identifier.verify_map(|r| match r { PathOrIdentifier::Path(v) => Some(v), PathOrIdentifier::Identifier(_) => None, }); @@ -137,14 +134,14 @@ impl<'a> Target<'a> { let is_unnamed_struct = opt_opening_paren.parse_next(i)?; if is_unnamed_struct { - let targets = collect_targets(i, ')', |i: &mut _| Self::unnamed(i, s))?.1; + let targets = collect_targets(i, ')', Self::unnamed)?.1; let inner = only_one_rest_pattern(targets, false, "struct")?; return Ok(Self::Tuple(WithSpan::new((path, inner), path_span))); } let is_named_struct = opt_opening_brace.parse_next(i)?; if is_named_struct { - let targets = collect_targets(i, '}', |i: &mut _| Self::named(i, s))?.1; + let targets = collect_targets(i, '}', Self::named)?.1; return Ok(Self::Struct(WithSpan::new((path, targets), path_span))); } @@ -184,7 +181,7 @@ impl<'a> Target<'a> { } } - fn lit(i: &mut InputStream<'a>) -> ParseResult<'a, Self> { + fn lit(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { enum Lit<'a> { Str(StrLit<'a>), Bool(&'a str), @@ -208,13 +205,12 @@ impl<'a> Target<'a> { } } - fn unnamed(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> { - alt((Self::rest, |i: &mut _| Self::parse(i, s))).parse_next(i) + fn unnamed(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { + alt((Self::rest, Self::parse)).parse_next(i) } fn named, Self)>>( - i: &mut InputStream<'a>, - s: &State<'_, '_>, + i: &mut InputStream<'a, 'l>, ) -> ParseResult<'a, O> { if let Some(rest) = opt(Self::rest_inner).parse_next(i)? { let chr = peek(ws(opt(one_of([',', ':']).with_span()))).parse_next(i)?; @@ -233,11 +229,8 @@ impl<'a> Target<'a> { } Ok((WithSpan::new("..", rest.span), Target::Rest(rest)).into()) } else { - let ((src, span), target) = ( - identifier.with_span(), - opt(preceded(ws(':'), |i: &mut _| Self::parse(i, s))), - ) - .parse_next(i)?; + let ((src, span), target) = + (identifier.with_span(), opt(preceded(ws(':'), Self::parse))).parse_next(i)?; let src = WithSpan::new(src, span); if *src == "_" { @@ -255,11 +248,13 @@ impl<'a> Target<'a> { } } - fn rest(i: &mut InputStream<'a>) -> ParseResult<'a, Self> { + fn rest(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> { Self::rest_inner.map(Self::Rest).parse_next(i) } - fn rest_inner(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan>>> { + fn rest_inner( + i: &mut InputStream<'a, 'l>, + ) -> ParseResult<'a, WithSpan>>> { let p = |i: &mut _| { let id = terminated(opt(terminated(identifier.with_span(), ws('@'))), "..").parse_next(i)?; @@ -297,10 +292,10 @@ fn verify_name<'a>(name: WithSpan<&'a str>) -> Result, ErrMode( - i: &mut InputStream<'a>, +fn collect_targets<'a: 'l, 'l, T>( + i: &mut InputStream<'a, 'l>, delim: char, - one: impl ModalParser, T, ErrorContext>, + one: impl ModalParser, T, ErrorContext>, ) -> ParseResult<'a, (bool, Vec)> { let opt_comma = ws(opt(',')).map(|o| o.is_some()); let mut opt_end = ws(opt(one_of(delim))).map(|o| o.is_some()); diff --git a/askama_parser/src/tests.rs b/askama_parser/src/tests.rs index d61c367c..f57fee7c 100644 --- a/askama_parser/src/tests.rs +++ b/askama_parser/src/tests.rs @@ -1,10 +1,12 @@ +use std::cell::Cell; + use winnow::{LocatingSlice, Parser}; use crate::expr::BinOp; use crate::node::{Let, Lit, Raw, Whitespace, Ws}; use crate::{ - Ast, Expr, Filter, InnerSyntax, InputStream, Node, Num, PathComponent, PathOrIdentifier, - StrLit, Syntax, SyntaxBuilder, Target, WithSpan, + Ast, Expr, Filter, InnerSyntax, InputStream, Level, Node, Num, PathComponent, PathOrIdentifier, + State, StrLit, Syntax, SyntaxBuilder, Target, WithSpan, }; fn as_path<'a>(path: &'a [&'a str]) -> Vec> { @@ -36,7 +38,8 @@ fn test_ws_splitter() { #[test] #[should_panic] fn test_invalid_block() { - Ast::from_str("{% extend \"blah\" %}", None, &Syntax::default()).unwrap(); + let syntax = Syntax::default(); + Ast::from_str("{% extend \"blah\" %}", None, &syntax).unwrap(); } fn int_lit<'a>(i: &'a str) -> WithSpan>> { @@ -133,17 +136,17 @@ fn test_parse_numbers() { #[test] fn test_parse_var() { - let s = Syntax::default(); + let syntax = Syntax::default(); assert_eq!( - Ast::from_str("{{ foo }}", None, &s).unwrap().nodes, + Ast::from_str("{{ foo }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Var("foo"))) ))] ); assert_eq!( - Ast::from_str("{{ foo_bar }}", None, &s).unwrap().nodes, + Ast::from_str("{{ foo_bar }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Var("foo_bar"))) @@ -151,7 +154,7 @@ fn test_parse_var() { ); assert_eq!( - Ast::from_str("{{ none }}", None, &s).unwrap().nodes, + Ast::from_str("{{ none }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Var("none"))) @@ -161,17 +164,17 @@ fn test_parse_var() { #[test] fn test_parse_const() { - let s = Syntax::default(); + let syntax = Syntax::default(); assert_eq!( - Ast::from_str("{{ FOO }}", None, &s).unwrap().nodes, + Ast::from_str("{{ FOO }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Path(as_path(&["FOO"])))) ))] ); assert_eq!( - Ast::from_str("{{ FOO_BAR }}", None, &s).unwrap().nodes, + Ast::from_str("{{ FOO_BAR }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Path(as_path(&["FOO_BAR"])))) @@ -179,7 +182,7 @@ fn test_parse_const() { ); assert_eq!( - Ast::from_str("{{ NONE }}", None, &s).unwrap().nodes, + Ast::from_str("{{ NONE }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Path(as_path(&["NONE"])))) @@ -189,17 +192,19 @@ fn test_parse_const() { #[test] fn test_parse_path() { - let s = Syntax::default(); + let syntax = Syntax::default(); assert_eq!( - Ast::from_str("{{ None }}", None, &s).unwrap().nodes, + Ast::from_str("{{ None }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Path(as_path(&["None"])))), ))] ); assert_eq!( - Ast::from_str("{{ Some(123) }}", None, &s).unwrap().nodes, + Ast::from_str("{{ Some(123) }}", None, &syntax) + .unwrap() + .nodes, [Box::new(Node::Expr( Ws(None, None), call( @@ -210,7 +215,7 @@ fn test_parse_path() { ); assert_eq!( - Ast::from_str("{{ Ok(123) }}", None, &s).unwrap().nodes, + Ast::from_str("{{ Ok(123) }}", None, &syntax).unwrap().nodes, [Box::new(Node::Expr( Ws(None, None), call( @@ -220,7 +225,9 @@ fn test_parse_path() { ))], ); assert_eq!( - Ast::from_str("{{ Err(123) }}", None, &s).unwrap().nodes, + Ast::from_str("{{ Err(123) }}", None, &syntax) + .unwrap() + .nodes, [Box::new(Node::Expr( Ws(None, None), call( @@ -233,8 +240,10 @@ fn test_parse_path() { #[test] fn test_parse_var_call() { + let syntax = Syntax::default(); + assert_eq!( - Ast::from_str("{{ function(\"123\", 3) }}", None, &Syntax::default()) + Ast::from_str("{{ function(\"123\", 3) }}", None, &syntax) .unwrap() .nodes, [Box::new(Node::Expr( @@ -259,17 +268,19 @@ fn test_parse_var_call() { #[test] fn test_parse_path_call() { - let s = Syntax::default(); + let syntax = Syntax::default(); assert_eq!( - Ast::from_str("{{ Option::None }}", None, &s).unwrap().nodes, + Ast::from_str("{{ Option::None }}", None, &syntax) + .unwrap() + .nodes, [Box::new(Node::Expr( Ws(None, None), WithSpan::no_span(Box::new(Expr::Path(as_path(&["Option", "None"])))), ))], ); assert_eq!( - Ast::from_str("{{ Option::Some(123) }}", None, &s) + Ast::from_str("{{ Option::Some(123) }}", None, &syntax) .unwrap() .nodes, [Box::new(Node::Expr( @@ -282,7 +293,7 @@ fn test_parse_path_call() { ); assert_eq!( - Ast::from_str("{{ self::function(\"123\", 3) }}", None, &s) + Ast::from_str("{{ self::function(\"123\", 3) }}", None, &syntax) .unwrap() .nodes, [Box::new(Node::Expr( @@ -741,8 +752,8 @@ fn test_odd_calls() { fn test_parse_comments() { #[track_caller] fn one_comment_ws(source: &str, ws: Ws) { - let s = &Syntax::default(); - let mut nodes = Ast::from_str(source, None, s).unwrap().nodes; + let syntax = Syntax::default(); + let mut nodes = Ast::from_str(source, None, &syntax).unwrap().nodes; assert_eq!(nodes.len(), 1, "expected to parse one node"); match *nodes.pop().unwrap() { Node::Comment(comment) => assert_eq!(comment.ws, ws), @@ -1055,10 +1066,12 @@ fn fuzzed_unary_recursion() { #[test] fn fuzzed_comment_depth() { + let syntax = Syntax::default(); + let (sender, receiver) = std::sync::mpsc::channel(); let test = std::thread::spawn(move || { const TEMPLATE: &str = include_str!("../tests/comment-depth.txt"); - assert!(Ast::from_str(TEMPLATE, None, &Syntax::default()).is_ok()); + assert!(Ast::from_str(TEMPLATE, None, &syntax).is_ok()); sender.send(()).unwrap(); }); receiver @@ -1069,13 +1082,10 @@ fn fuzzed_comment_depth() { #[test] fn let_set() { + let syntax = Syntax::default(); assert_eq!( - Ast::from_str("{% let a %}", None, &Syntax::default()) - .unwrap() - .nodes(), - Ast::from_str("{% set a %}", None, &Syntax::default()) - .unwrap() - .nodes(), + Ast::from_str("{% let a %}", None, &syntax).unwrap().nodes(), + Ast::from_str("{% set a %}", None, &syntax).unwrap().nodes(), ); } @@ -1181,110 +1191,35 @@ fn fuzzed_excessive_filter_block() { #[test] fn test_generics_parsing() { + let syntax = Syntax::default(); + // Method call. - Ast::from_str("{{ a.b::<&str, H>>() }}", None, &Syntax::default()).unwrap(); - Ast::from_str( - "{{ a.b::<&str, H , &u32>>() }}", - None, - &Syntax::default(), - ) - .unwrap(); + Ast::from_str("{{ a.b::<&str, H>>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&str, H , &u32>>() }}", None, &syntax).unwrap(); // Call. - Ast::from_str( - "{{ a::<&str, H , &u32>>() }}", - None, - &Syntax::default(), - ) - .unwrap(); + Ast::from_str("{{ a::<&str, H , &u32>>() }}", None, &syntax).unwrap(); // Filter. - Ast::from_str("{{ 12 | a::<&str> }}", None, &Syntax::default()).unwrap(); - Ast::from_str("{{ 12 | a::<&str, u32>('a') }}", None, &Syntax::default()).unwrap(); + Ast::from_str("{{ 12 | a::<&str> }}", None, &syntax).unwrap(); + Ast::from_str("{{ 12 | a::<&str, u32>('a') }}", None, &syntax).unwrap(); // Unclosed `<`. - assert!( - Ast::from_str( - "{{ a.b::<&str, H , &u32>() }}", - None, - &Syntax::default() - ) - .is_err() - ); + assert!(Ast::from_str("{{ a.b::<&str, H , &u32>() }}", None, &syntax).is_err()); // With path and spaces - Ast::from_str( - "{{ a.b::<&&core::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b ::<&&core::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b:: <&&core::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::< &&core::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<& &core::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&& core::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&&core ::primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&&core:: primitive::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&&core::primitive ::str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&&core::primitive:: str>() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&&core::primitive::str >() }}", - None, - &Syntax::default(), - ) - .unwrap(); - Ast::from_str( - "{{ a.b::<&&core::primitive::str> () }}", - None, - &Syntax::default(), - ) - .unwrap(); + Ast::from_str("{{ a.b::<&&core::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b ::<&&core::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b:: <&&core::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::< &&core::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<& &core::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&& core::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&&core ::primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&&core:: primitive::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&&core::primitive ::str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&&core::primitive:: str>() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&&core::primitive::str >() }}", None, &syntax).unwrap(); + Ast::from_str("{{ a.b::<&&core::primitive::str> () }}", None, &syntax).unwrap(); } #[test] @@ -1365,9 +1300,14 @@ fn test_filter_with_path() { #[test] fn underscore_is_an_identifier() { + let state = State { + syntax: Syntax::default(), + loop_depth: Cell::new(0), + level: Level::default(), + }; let mut input = InputStream { input: LocatingSlice::new("_"), - state: (), + state: &state, }; let result = crate::identifier.parse_next(&mut input); assert_eq!(result.unwrap(), "_"); @@ -1512,6 +1452,7 @@ fn macro_comments_in_macro_calls() { #[test] fn test_raw() { let syntax = Syntax::default(); + let val = "hello {{ endraw %} my {%* endraw %} green {% endraw }} world"; assert_eq!( Ast::from_str(