diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 51a220b..00cb2da 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -487,25 +487,11 @@ postfix = { // PREFIX EXPRESSIONS // ------------------------------------------------------ -deref_px = { - "*" -} - -mut_ref_px = { - "&" ~ mutable -} - -ref_px = { - "&" -} - -new_px = { - "new" -} - -not_px = { - "!" -} +deref_px = { "*" } +mut_ref_px = { "&" ~ mutable } +ref_px = { "&" } +new_px = { "new" } +not_px = { "!" } prefix = { deref_px @@ -516,30 +502,45 @@ prefix = { } // ------------------------------------------------------ -// OPERATORS +// OPERATORS (Named individually for Pratt Parser configuration) // ------------------------------------------------------ -bin_op = { - "<=" - | ">=" - | "==" - | "!=" - | "&&" - | "||" - | "+" - | "-" - | "*" - | "/" - | "%" - | "<" - | ">" +lte = { "<=" } +gte = { ">=" } +eq = { "==" } +neq = { "!=" } +and = { "&&" } +or = { "||" } +add = { "+" } +sub = { "-" } +mul = { "*" } +div = { "/" } +rem = { "%" } +lt = { "<" } +gt = { ">" } + +bin_op = _{ + lte + | gte + | eq + | neq + | and + | or + | add + | sub + | mul + | div + | rem + | lt + | gt } // ------------------------------------------------------ // EXPRESSIONS // ------------------------------------------------------ +// Top-level entry point for expressions expr = { term ~ (bin_op ~ term)* } -// A term is anything that acts as a single unit in an operation -term = _{ prefix* ~ primary ~ postfix* } +// A term handles your Prefix and Postfix combinations around a core Primary +term = { prefix* ~ primary ~ postfix* } diff --git a/parser/src/parser/common/expr.rs b/parser/src/parser/common/expr.rs index e49680d..a6d7545 100644 --- a/parser/src/parser/common/expr.rs +++ b/parser/src/parser/common/expr.rs @@ -2,37 +2,107 @@ use crate::{ Rule, ast::*, ast_expr, - error::{AstError, GetLength, IntoErr, collect_recovered, collect_recovered_map}, + error::{AstError, AstResult, GetLength, IntoErr, collect_recovered, collect_recovered_map}, }; +use pest::pratt_parser::PrattParser; +use std::sync::OnceLock; impl<'a> TryFrom> for Expression { type Error = AstError<'a, Self>; fn try_from(pair: pest::iterators::Pair<'a, Rule>) -> Result { let rule = pair.as_rule(); - let mut inner = pair.clone().into_inner(); + let inner = pair.clone().into_inner(); match rule { + // 1. The top level expressions are now processed via the Pratt Parser Rule::expr => { - let prefixes = inner - .next() - .map(|p| collect_recovered::(p.into_inner())) - .unwrap_or_else(|| Ok(Vec::new())); + static PRATT_PARSER: OnceLock> = OnceLock::new(); + let pratt = PRATT_PARSER.get_or_init(|| { + use Rule::*; + use pest::pratt_parser::{Assoc::*, Op}; - let exp = Expression::try_from(inner.next().unwrap()); + // Precedence defined from lowest to highest + PrattParser::new() + .op(Op::infix(or, Left)) + .op(Op::infix(and, Left)) + .op(Op::infix(eq, Left) + | Op::infix(neq, Left) + | Op::infix(lt, Left) + | Op::infix(gt, Left) + | Op::infix(lte, Left) + | Op::infix(gte, Left)) + .op(Op::infix(add, Left) | Op::infix(sub, Left)) + .op(Op::infix(mul, Left) | Op::infix(div, Left) | Op::infix(rem, Left)) + }); - if inner.len() > 0 || prefixes.len() > 0 { + pratt + .map_primary(|primary_pair| { + // Elements handled by map_primary are either sub-expressions or 'term' rules + Expression::try_from(primary_pair) + }) + .map_infix(|lhs, op, rhs| { + let bin_op = match op.as_rule() { + Rule::lte => BinaryOp::LessThanOrEqual, + Rule::gte => BinaryOp::GreaterThanOrEqual, + Rule::eq => BinaryOp::Equal, + Rule::neq => BinaryOp::NotEqual, + Rule::and => BinaryOp::And, + Rule::or => BinaryOp::Or, + Rule::add => BinaryOp::Plus, + Rule::sub => BinaryOp::Minus, + Rule::mul => BinaryOp::Multiply, + Rule::div => BinaryOp::Divide, + Rule::rem => BinaryOp::Modulo, + Rule::lt => BinaryOp::LessThan, + Rule::gt => BinaryOp::GreaterThan, + _ => return AstError::bug_unimplemented(op), + }; + + ast_expr!(Expression::Binary { + lhs: lhs.map(Box::new), + op: Ok(bin_op) as AstResult<'_, BinaryOp>, + rhs: rhs.map(Box::new), + }) + }) + .parse(inner) + } + + // 2. The single unit 'term' replaces the old flat 'expr' layout + Rule::term => { + let mut prefix_pairs = Vec::new(); + let mut primary_pair = None; + let mut postfix_pairs = Vec::new(); + + // Sort out flat layout components + for p in inner { + match p.as_rule() { + Rule::prefix => prefix_pairs.push(p), + Rule::primary => primary_pair = Some(p), + Rule::postfix => postfix_pairs.push(p), + _ => {} + } + } + + let prefixes = collect_recovered::(prefix_pairs.into_iter()); + let exp = Expression::try_from( + primary_pair.expect("Term must contain a primary expression"), + ); + let postfixes = collect_recovered::(postfix_pairs.into_iter()); + + // Employs your exact original logic using the GetLength trait + if postfixes.len() > 0 || prefixes.len() > 0 { ast_expr!(Expression::Fix { initial: exp.map(Box::new), prefixes: prefixes, - postfixes: collect_recovered(inner), + postfixes: postfixes, }) } else { exp } } - Rule::primary => inner.next().unwrap().try_into(), + Rule::primary => pair.into_inner().next().unwrap().try_into(), Rule::static_path => ast_expr!(Expression::Path(pair.try_into())), Rule::literal => ast_expr!(Expression::Literal(pair.try_into())), @@ -86,31 +156,10 @@ impl<'a> TryFrom> for Postfix { ast_expr!(Postfix::Index(Expression::try_from(inner.next().unwrap()))) } - Rule::binary_px => { - let op_pair = inner.next().unwrap(); - let op = match op_pair.as_str() { - "+" => BinaryOp::Plus, - "-" => BinaryOp::Minus, - "*" => BinaryOp::Multiply, - "/" => BinaryOp::Divide, - "%" => BinaryOp::Modulo, - "==" => BinaryOp::Equal, - "!=" => BinaryOp::NotEqual, - "<" => BinaryOp::LessThan, - ">" => BinaryOp::GreaterThan, - "<=" => BinaryOp::LessThanOrEqual, - ">=" => BinaryOp::GreaterThanOrEqual, - "&&" => BinaryOp::And, - "||" => BinaryOp::Or, - - _ => return AstError::bug_unimplemented(op_pair), - }; - - Ok(Postfix::Binary(op, inner.next().unwrap().try_into().get()?)) - } - Rule::macro_call_px => Ok(Postfix::MacroCall(inner.as_str().to_string())), + // Note: Rule::binary_px has been completely decoupled from postfix rules + // as it is now safely managed inside the top-level Pratt execution above. _ => AstError::bug_unimplemented(pair), } }