From a90c9960ba1ba13703cd306c06f646109632882f Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Wed, 16 Nov 2022 23:19:30 +0900 Subject: [PATCH] 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 --- lib/src/revset.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 8f600c6e1..3aa3e55e7 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -23,7 +23,9 @@ use std::sync::Arc; use std::{error, fmt}; use itertools::Itertools; +use once_cell::sync::Lazy; use pest::iterators::{Pair, Pairs}; +use pest::pratt_parser::{Assoc, Op, PrattParser}; use pest::Parser; use pest_derive::Parser; use thiserror::Error; @@ -490,27 +492,23 @@ fn parse_expression_rule( } fn parse_infix_expression_rule( - mut pairs: Pairs, + pairs: Pairs, workspace_ctx: Option<&RevsetWorkspaceContext>, ) -> Result, RevsetParseError> { - let mut expression1 = - parse_range_expression_rule(pairs.next().unwrap().into_inner(), workspace_ctx)?; - while let Some(operator) = pairs.next() { - let expression2 = - parse_range_expression_rule(pairs.next().unwrap().into_inner(), workspace_ctx)?; - expression1 = match operator.as_rule() { - Rule::union_op => expression1.union(&expression2), - Rule::intersection_op => expression1.intersection(&expression2), - Rule::difference_op => expression1.minus(&expression2), - _ => { - panic!( - "unxpected revset infix operator rule {:?}", - operator.as_rule() - ); - } - } - } - Ok(expression1) + static PRATT: Lazy> = Lazy::new(|| { + PrattParser::new().op(Op::infix(Rule::union_op, Assoc::Left) + | Op::infix(Rule::intersection_op, Assoc::Left) + | Op::infix(Rule::difference_op, Assoc::Left)) + }); + PRATT + .map_primary(|primary| parse_range_expression_rule(primary.into_inner(), workspace_ctx)) + .map_infix(|lhs, op, rhs| match op.as_rule() { + Rule::union_op => Ok(lhs?.union(&rhs?)), + Rule::intersection_op => Ok(lhs?.intersection(&rhs?)), + Rule::difference_op => Ok(lhs?.minus(&rhs?)), + r => panic!("unexpected infix operator rule {r:?}"), + }) + .parse(pairs) } fn parse_range_expression_rule(