From af552ee3744016ac1db741888318649a64dfee50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Tue, 4 Jan 2022 20:55:18 +0100 Subject: [PATCH] Optimize parsing of ranges Right now almost every expression needs to be parsed twice: `expr_any()` first parses the left-hand side of a range expression, and if no `..` or `..=` was found the left-hand expression is parsed again, this time as the result of the function. This diff removes the second parsing step by first looking for `.. (opt rhs)`, then for `lhs .. (opt rhs)`. --- askama_shared/src/parser.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index da9a6a044..f5685b9fd 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -640,24 +640,20 @@ expr_prec_layer!(expr_compare, expr_bor, "==", "!=", ">=", ">", "<=", "<"); expr_prec_layer!(expr_and, expr_compare, "&&"); expr_prec_layer!(expr_or, expr_and, "||"); -fn range_right(i: &str) -> IResult<&str, Expr<'_>> { - let (i, (_, incl, right)) = tuple((ws(tag("..")), opt(ws(char('='))), opt(expr_or)))(i)?; - Ok(( - i, - Expr::Range( - if incl.is_some() { "..=" } else { ".." }, - None, - right.map(Box::new), - ), - )) -} - fn expr_any(i: &str) -> IResult<&str, Expr<'_>> { - let compound = map(tuple((expr_or, range_right)), |(left, rest)| match rest { - Expr::Range(op, _, right) => Expr::Range(op, Some(Box::new(left)), right), - _ => unreachable!(), - }); - alt((range_right, compound, expr_or))(i) + let range_right = |i| pair(ws(alt((tag("..="), tag("..")))), opt(expr_or))(i); + alt(( + map(range_right, |(op, right)| { + Expr::Range(op, None, right.map(Box::new)) + }), + map( + pair(expr_or, opt(range_right)), + |(left, right)| match right { + Some((op, right)) => Expr::Range(op, Some(Box::new(left)), right.map(Box::new)), + None => left, + }, + ), + ))(i) } fn expr_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {