Skip to content

Commit

Permalink
Optimize parsing of ranges
Browse files Browse the repository at this point in the history
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)`.
  • Loading branch information
Kijewski authored and djc committed Jan 6, 2022
1 parent 803d32c commit af552ee
Showing 1 changed file with 13 additions and 17 deletions.
30 changes: 13 additions & 17 deletions askama_shared/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>> {
Expand Down

0 comments on commit af552ee

Please sign in to comment.