revset: leverage PrattParser to parse infix (or set) expression

Apparently, this is new feature introduced in pest 2.4.0. It allows us to
easily enforce operator precedence. I think the whole expression post-parsing
can be migrated to PrattParser, but let's start small. We might want to
add a weird rule to the range_expression layer in future.

https://github.com/pest-parser/pest/releases/tag/v2.4.0
https://docs.rs/pest/latest/pest/pratt_parser/struct.PrattParser.html#example
This commit is contained in:
Yuya Nishihara 2022-11-16 23:19:30 +09:00
parent 1717690a64
commit a90c9960ba

View file

@ -23,7 +23,9 @@ use std::sync::Arc;
use std::{error, fmt}; use std::{error, fmt};
use itertools::Itertools; use itertools::Itertools;
use once_cell::sync::Lazy;
use pest::iterators::{Pair, Pairs}; use pest::iterators::{Pair, Pairs};
use pest::pratt_parser::{Assoc, Op, PrattParser};
use pest::Parser; use pest::Parser;
use pest_derive::Parser; use pest_derive::Parser;
use thiserror::Error; use thiserror::Error;
@ -490,27 +492,23 @@ fn parse_expression_rule(
} }
fn parse_infix_expression_rule( fn parse_infix_expression_rule(
mut pairs: Pairs<Rule>, pairs: Pairs<Rule>,
workspace_ctx: Option<&RevsetWorkspaceContext>, workspace_ctx: Option<&RevsetWorkspaceContext>,
) -> Result<Rc<RevsetExpression>, RevsetParseError> { ) -> Result<Rc<RevsetExpression>, RevsetParseError> {
let mut expression1 = static PRATT: Lazy<PrattParser<Rule>> = Lazy::new(|| {
parse_range_expression_rule(pairs.next().unwrap().into_inner(), workspace_ctx)?; PrattParser::new().op(Op::infix(Rule::union_op, Assoc::Left)
while let Some(operator) = pairs.next() { | Op::infix(Rule::intersection_op, Assoc::Left)
let expression2 = | Op::infix(Rule::difference_op, Assoc::Left))
parse_range_expression_rule(pairs.next().unwrap().into_inner(), workspace_ctx)?; });
expression1 = match operator.as_rule() { PRATT
Rule::union_op => expression1.union(&expression2), .map_primary(|primary| parse_range_expression_rule(primary.into_inner(), workspace_ctx))
Rule::intersection_op => expression1.intersection(&expression2), .map_infix(|lhs, op, rhs| match op.as_rule() {
Rule::difference_op => expression1.minus(&expression2), Rule::union_op => Ok(lhs?.union(&rhs?)),
_ => { Rule::intersection_op => Ok(lhs?.intersection(&rhs?)),
panic!( Rule::difference_op => Ok(lhs?.minus(&rhs?)),
"unxpected revset infix operator rule {:?}", r => panic!("unexpected infix operator rule {r:?}"),
operator.as_rule() })
); .parse(pairs)
}
}
}
Ok(expression1)
} }
fn parse_range_expression_rule( fn parse_range_expression_rule(