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}