1use crate::ast::BinaryOp;
2use crate::parser::Rule;
3use pest::pratt_parser::{Op, PrattParser};
4use std::sync::LazyLock;
5
6pub use pest::pratt_parser::Assoc;
8
9macro_rules! define_precedence {
12 (
13 $(
14 precedence $prec:expr, $assoc:ident => {
15 $( $binop:ident : $rule:ident ),* $(,)?
16 }
17 )*
18 ) => {
19 static PRECEDENCE_TABLE: &[(u8, Assoc, BinaryOp, Rule)] = &[
23 $(
24 $(
25 ($prec, Assoc::$assoc, BinaryOp::$binop, Rule::$rule),
26 )*
27 )*
28 ];
29 };
30}
31
32define_precedence! {
34 precedence 1, Left => {
36 And: and,
37 NaturalAnd: natural_and,
38 Or: or,
39 NaturalOr: natural_or,
40 Via: via,
41 Into: into,
42 Where: where_,
43 }
44
45 precedence 2, Left => {
47 Equal: equal,
48 NotEqual: not_equal,
49 Less: less,
50 LessEq: less_eq,
51 Greater: greater,
52 GreaterEq: greater_eq,
53 DotEqual: dot_equal,
54 DotNotEqual: dot_not_equal,
55 DotLess: dot_less,
56 DotLessEq: dot_less_eq,
57 DotGreater: dot_greater,
58 DotGreaterEq: dot_greater_eq,
59 }
60
61 precedence 3, Left => {
63 Add: add,
64 Subtract: subtract,
65 }
66
67 precedence 4, Left => {
69 Multiply: multiply,
70 Divide: divide,
71 Modulo: modulo,
72 }
73
74 precedence 5, Right => {
76 Power: power,
77 }
78 precedence 5, Left => {
79 Coalesce: coalesce,
80 }
81}
82
83pub fn build_pratt_parser() -> PrattParser<Rule> {
85 let mut parser = PrattParser::new();
86
87 let mut precedence_groups: Vec<(u8, Assoc, Vec<Rule>)> = Vec::new();
89
90 for &(prec, assoc, _binop, rule) in PRECEDENCE_TABLE {
91 if let Some(group) = precedence_groups
93 .iter_mut()
94 .find(|(p, a, _)| *p == prec && *a == assoc)
95 {
96 group.2.push(rule);
97 } else {
98 precedence_groups.push((prec, assoc, vec![rule]));
99 }
100 }
101
102 precedence_groups.sort_by_key(|(prec, _, _)| *prec);
104
105 for (_prec, assoc, rules) in precedence_groups {
107 let mut op_chain = Op::infix(rules[0], assoc);
108 for &rule in &rules[1..] {
109 op_chain = op_chain | Op::infix(rule, assoc);
110 }
111 parser = parser.op(op_chain);
112 }
113
114 parser = parser.op(Op::prefix(Rule::negation)
116 | Op::prefix(Rule::spread_operator)
117 | Op::prefix(Rule::invert)
118 | Op::prefix(Rule::natural_not));
119
120 parser = parser.op(Op::postfix(Rule::factorial));
122 parser = parser.op(Op::postfix(Rule::access)
123 | Op::postfix(Rule::dot_access)
124 | Op::postfix(Rule::call_list));
125
126 parser
127}
128
129pub static PRATT: LazyLock<PrattParser<Rule>> = LazyLock::new(build_pratt_parser);
131
132pub fn operator_info(op: &BinaryOp) -> (u8, Assoc) {
135 PRECEDENCE_TABLE
136 .iter()
137 .find(|(_, _, binop, _)| binop == op)
138 .map(|(prec, assoc, _, _)| (*prec, *assoc))
139 .expect("All BinaryOp variants must be in PRECEDENCE_TABLE")
140}