oak_core/parser/pratt_parser.rs
1use crate::{
2 Language,
3 parser::ParserState,
4 source::Source,
5 tree::{GreenBuilder, GreenNode, GreenTree},
6};
7use core::marker::PhantomData;
8use triomphe::Arc;
9
10/// Pratt parser: supports prefix/infix/postfix (postfix left for language layer special handling)
11pub struct PrattParser<L: Language> {
12 token: PhantomData<L::SyntaxKind>,
13 /// unary prefix
14 prefix_ops: Vec<PrefixEntry<L::SyntaxKind>>,
15 /// binary infix
16 infix_ops: Vec<InfixEntry<L::SyntaxKind>>,
17}
18
19/// Operator precedence
20pub type Precedence = u8;
21
22/// Operator associativity defines how operators of the same precedence are grouped.
23///
24/// Associativity determines whether an expression like `a + b + c` is interpreted as
25/// `(a + b) + c` (left-associative) or `a + (b + c)` (right-associative).
26/// Non-associative operators cannot be chained together without explicit parentheses.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Associativity {
29 /// Left-associative operators group from left to right.
30 /// For example, subtraction is left-associative: `a - b - c` means `(a - b) - c`
31 Left,
32 /// Right-associative operators group from right to left.
33 /// For example, assignment is right-associative: `a = b = c` means `a = (b = c)`
34 Right,
35 /// Non-associative operators cannot be chained without parentheses.
36 /// For example, comparison operators are typically non-associative
37 None,
38}
39
40/// Operator information containing precedence and associativity.
41///
42/// This structure encapsulates the essential properties needed for operator
43/// precedence parsing, including the precedence level and how operators of the
44/// same precedence should be grouped.
45#[derive(Debug, Clone)]
46pub struct OperatorInfo {
47 /// The precedence level of the operator (higher values have higher precedence)
48 pub precedence: Precedence,
49 /// The associativity of the operator (Left, Right, or None)
50 pub associativity: Associativity,
51}
52
53impl OperatorInfo {
54 /// Creates a new operator with the specified precedence and associativity.
55 ///
56 /// # Arguments
57 ///
58 /// * `precedence` - The precedence level of the operator (higher values have higher precedence)
59 /// * `associativity` - The associativity of the operator (Left, Right, or None)
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// let op = OperatorInfo::new(10, Associativity::Left);
65 /// ```
66 pub fn new(precedence: Precedence, associativity: Associativity) -> Self {
67 Self { precedence, associativity }
68 }
69
70 /// Creates a left-associative operator with the specified precedence.
71 ///
72 /// # Arguments
73 ///
74 /// * `precedence` - The precedence level of the operator
75 ///
76 /// # Examples
77 ///
78 /// ```
79 /// let left_op = OperatorInfo::left(5);
80 /// ```
81 pub fn left(precedence: Precedence) -> Self {
82 Self::new(precedence, Associativity::Left)
83 }
84
85 /// Creates a right-associative operator with the specified precedence.
86 ///
87 /// # Arguments
88 ///
89 /// * `precedence` - The precedence level of the operator
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// let right_op = OperatorInfo::right(8);
95 /// ```
96 pub fn right(precedence: Precedence) -> Self {
97 Self::new(precedence, Associativity::Right)
98 }
99
100 /// Creates a non-associative operator with the specified precedence.
101 ///
102 /// # Arguments
103 ///
104 /// * `precedence` - The precedence level of the operator
105 ///
106 /// # Examples
107 ///
108 /// ```
109 /// let non_assoc_op = OperatorInfo::none(3);
110 /// ```
111 pub fn none(precedence: Precedence) -> Self {
112 Self::new(precedence, Associativity::None)
113 }
114}
115
116#[derive(Clone)]
117struct PrefixEntry<K> {
118 op: K,
119 info: OperatorInfo,
120 node_kind: K,
121}
122
123#[derive(Clone)]
124struct InfixEntry<K> {
125 op: K,
126 info: OperatorInfo,
127 node_kind: K,
128}
129
130impl<L: Language> PrattParser<L>
131where
132 L::SyntaxKind: Copy + PartialEq,
133{
134 /// Creates a new Pratt parser
135 pub fn new() -> Self {
136 Self { token: PhantomData, prefix_ops: Vec::new(), infix_ops: Vec::new() }
137 }
138
139 /// Registers a prefix operator
140 pub fn prefix(&mut self, op: L::SyntaxKind, info: OperatorInfo, node_kind: L::SyntaxKind) -> &mut Self {
141 self.prefix_ops.push(PrefixEntry { op, info, node_kind });
142 self
143 }
144
145 /// Registers an infix operator
146 pub fn infix(&mut self, op: L::SyntaxKind, info: OperatorInfo, node_kind: L::SyntaxKind) -> &mut Self {
147 self.infix_ops.push(InfixEntry { op, info, node_kind });
148 self
149 }
150
151 fn find_prefix(&self, k: L::SyntaxKind) -> Option<&PrefixEntry<L::SyntaxKind>> {
152 self.prefix_ops.iter().find(|e| e.op == k)
153 }
154
155 fn find_infix(&self, k: L::SyntaxKind) -> Option<&InfixEntry<L::SyntaxKind>> {
156 self.infix_ops.iter().find(|e| e.op == k)
157 }
158
159 /// Parses expressions using the Pratt algorithm
160 /// - `min_bp`: Minimum binding strength (precedence)
161 /// - `primary`: Primary expression parser provided by language layer (includes parentheses, literals, identifiers, and high-binding suffixes like calls/fields)
162 pub fn parse<S, F>(
163 &self,
164 state: &mut ParserState<'_, S, L>,
165 min_bp: Precedence,
166 primary: &F,
167 ) -> Arc<GreenNode<L::SyntaxKind>>
168 where
169 S: Source,
170 F: Fn(&mut ParserState<'_, S, L>) -> Arc<GreenNode<L::SyntaxKind>>,
171 {
172 // Handle prefix or primary expression
173 let mut left = if let Some(k) = state.peek_kind() {
174 if let Some(prefix) = self.find_prefix(k) {
175 // consume op and copy needed fields immediately to drop borrow
176 let (op_kind, op_len) = match state.advance() {
177 Some(t) => (t.kind, t.span.end - t.span.start),
178 None => return primary(state),
179 };
180 let rhs = self.parse(state, prefix.info.precedence, primary);
181 let mut builder = GreenBuilder::<L>::new(128);
182 builder = builder.token(op_kind, op_len);
183 builder = builder.push(GreenTree::Node(rhs));
184 builder.finish(prefix.node_kind)
185 }
186 else {
187 primary(state)
188 }
189 }
190 else {
191 primary(state)
192 };
193
194 // Handle infix chain
195 loop {
196 if !state.not_at_end() {
197 break;
198 }
199 let op_kind = match state.peek_kind() {
200 Some(k) => k,
201 None => break,
202 };
203 let infix = match self.find_infix(op_kind) {
204 Some(i) => i,
205 None => break,
206 };
207
208 let p = infix.info.precedence;
209 let lbp = p;
210 let rbp = match infix.info.associativity {
211 Associativity::Left | Associativity::None => p + 1,
212 Associativity::Right => p,
213 };
214
215 if lbp < min_bp {
216 break;
217 }
218
219 // consume op and copy needed fields immediately to drop borrow
220 let (op_kind2, op_len2) = match state.advance() {
221 Some(t) => (t.kind, t.span.end - t.span.start),
222 None => break,
223 };
224 let right = self.parse(state, rbp, primary);
225
226 let mut builder = GreenBuilder::<L>::new(128);
227 builder = builder.push(GreenTree::Node(left));
228 builder = builder.token(op_kind2, op_len2);
229 builder = builder.push(GreenTree::Node(right));
230 left = builder.finish(infix.node_kind);
231 }
232
233 left
234 }
235}