Skip to main content

lisette_syntax/parse/
pratt.rs

1use ecow::EcoString;
2
3use super::{ParseError, Parser};
4use crate::ast;
5use crate::lex::TokenKind::{self, *};
6use crate::types::Type;
7
8const RANGE_PREC: u8 = 6;
9const CAST_PREC: u8 = 9;
10
11impl<'source> Parser<'source> {
12    /// Parses by grouping together operations in expressions based on precedence.
13    ///
14    /// 1. Parse a left-hand side expression (primary, unary, or prefix).
15    /// 2. Look for binary or postfix operators.
16    /// 3. For binary operators: If the operator's precedence is higher than `min_prec`,
17    ///    parse the right-hand side recursively with the operator's precedence.
18    /// 4. For postfix operators: Transform the current expression into a larger one.
19    ///
20    /// The `min_prec` param sets the minimum precedence level for this parsing context.
21    pub fn pratt_parse(&mut self, min_prec: u8) -> ast::Expression {
22        if !self.enter_recursion() {
23            let span = self.span_from_token(self.current_token());
24            self.resync_on_error();
25            return ast::Expression::Unit {
26                ty: Type::uninferred(),
27                span,
28            };
29        }
30
31        let start = self.current_token();
32        let mut lhs = self.parse_left_hand_side();
33        let depth_before_loop = self.depth;
34
35        while !self.at_eof() && !self.too_many_errors() {
36            if self.check_go_channel_send() {
37                self.depth = depth_before_loop;
38                self.leave_recursion();
39                return lhs;
40            }
41
42            if self.at_range() && RANGE_PREC > min_prec {
43                lhs = self.parse_range(Some(lhs.into()), start);
44                continue;
45            }
46
47            if self.current_token().kind == As && CAST_PREC > min_prec {
48                self.next();
49                let target_type = self.parse_annotation();
50                lhs = ast::Expression::Cast {
51                    expression: lhs.into(),
52                    target_type,
53                    ty: Type::uninferred(),
54                    span: self.span_from_tokens(start),
55                };
56                continue;
57            }
58
59            if min_prec == 0
60                && self.current_token().kind == PipeDouble
61                && self.newline_before_current()
62            {
63                break;
64            }
65
66            if let Some(prec) = self.binary_operator_precedence(self.current_token().kind)
67                && prec > min_prec
68            {
69                let operator = self.parse_binary_operator();
70                let rhs = self.pratt_parse(prec);
71                lhs = ast::Expression::Binary {
72                    operator,
73                    left: lhs.into(),
74                    right: rhs.into(),
75                    ty: Type::uninferred(),
76                    span: self.span_from_tokens(start),
77                };
78                continue;
79            }
80
81            if self.is_postfix_operator(&lhs) {
82                if !self.enter_recursion() {
83                    break;
84                }
85                if self.is_format_string(&lhs)
86                    && (self.current_token().kind == LeftParen
87                        || self.current_token().kind == LeftSquareBracket)
88                    && self.newline_before_current()
89                {
90                    break;
91                }
92                lhs = self.include_in_larger_expression(lhs);
93                continue;
94            }
95
96            if matches!(self.current_token().kind, Ampersand | Pipe | Caret)
97                && !self.newline_before_current()
98            {
99                let op_token = self.current_token();
100                let span = self.span_from_token(op_token);
101                let error = ParseError::new(
102                    "Unsupported operator",
103                    span,
104                    format!("`{}` is not a supported binary operator", op_token.text),
105                )
106                .with_help("Lisette does not support bitwise operators")
107                .with_parse_code("unsupported_operator");
108                self.errors.push(error);
109                self.next();
110                let _rhs = self.pratt_parse(min_prec);
111                continue;
112            }
113
114            break;
115        }
116
117        self.depth = depth_before_loop;
118        self.leave_recursion();
119
120        lhs
121    }
122
123    fn prefix_operator_precedence(&self, kind: TokenKind) -> u8 {
124        match kind {
125            Minus | Bang | Ampersand => 15,
126            _ => {
127                debug_assert!(false, "unexpected prefix operator: {:?}", kind);
128                15
129            }
130        }
131    }
132
133    fn binary_operator_precedence(&self, kind: TokenKind) -> Option<u8> {
134        match kind {
135            LeftAngleBracket if self.is_type_args_call() => None,
136            Pipeline => Some(1),
137            PipeDouble if self.stream.peek_ahead(1).kind == Arrow => None,
138            PipeDouble => Some(3),
139            AmpersandDouble => Some(4),
140            EqualDouble | NotEqual | LeftAngleBracket | RightAngleBracket | LessThanOrEqual
141            | GreaterThanOrEqual => Some(5),
142            Plus | Minus => Some(7),
143            Star | Slash | Percent => Some(8),
144            _ => None,
145        }
146    }
147
148    fn is_postfix_operator(&self, lhs: &ast::Expression) -> bool {
149        match self.current_token().kind {
150            LeftParen | LeftSquareBracket | QuestionMark | Dot => true,
151            LeftCurlyBrace => match lhs {
152                ast::Expression::Identifier { .. } | ast::Expression::DotAccess { .. } => {
153                    self.is_struct_instantiation()
154                }
155                _ => false,
156            },
157            LeftAngleBracket => self.is_type_args_call(),
158            Colon if self.stream.peek_ahead(1).kind == Colon => true,
159            _ => false,
160        }
161    }
162
163    fn is_format_string(&self, expression: &ast::Expression) -> bool {
164        matches!(
165            expression,
166            ast::Expression::Literal {
167                literal: ast::Literal::FormatString(_),
168                ..
169            }
170        )
171    }
172
173    fn parse_left_hand_side(&mut self) -> ast::Expression {
174        let start = self.current_token();
175
176        match start.kind {
177            Bang | Minus => {
178                self.next();
179
180                let operator = if start.kind == Bang {
181                    ast::UnaryOperator::Not
182                } else {
183                    ast::UnaryOperator::Negative
184                };
185
186                let prec = self.prefix_operator_precedence(start.kind);
187
188                ast::Expression::Unary {
189                    operator,
190                    expression: self.pratt_parse(prec).into(),
191                    ty: Type::uninferred(),
192                    span: self.span_from_tokens(start),
193                }
194            }
195
196            Ampersand => {
197                self.next();
198                if self.current_token().kind == Mut {
199                    let span = ast::Span::new(
200                        self.file_id,
201                        start.byte_offset,
202                        self.current_token().byte_offset + self.current_token().byte_length
203                            - start.byte_offset,
204                    );
205                    self.track_error_at(
206                        span,
207                        "invalid syntax",
208                        "Lisette has no mutable references. Use `&x` instead",
209                    );
210                    self.next(); // consume `mut`
211                }
212                let prec = self.prefix_operator_precedence(start.kind);
213                ast::Expression::Reference {
214                    expression: self.pratt_parse(prec).into(),
215                    ty: Type::uninferred(),
216                    span: self.span_from_tokens(start),
217                }
218            }
219
220            _ => self.parse_atomic_expression(),
221        }
222    }
223
224    pub fn include_in_larger_expression(&mut self, lhs: ast::Expression) -> ast::Expression {
225        match self.current_token().kind {
226            LeftParen => self.parse_function_call(lhs, vec![]),
227            LeftSquareBracket => self.parse_index_expression(lhs),
228            LeftCurlyBrace => self.parse_struct_call(lhs),
229            QuestionMark => self.parse_try(lhs),
230            Dot => self.parse_field_access(lhs),
231            LeftAngleBracket => {
232                let type_args = self.parse_type_args();
233
234                if self.current_token().kind == Dot && self.stream.peek_ahead(1).kind == Identifier
235                {
236                    let type_name = match &lhs {
237                        ast::Expression::Identifier { value, .. } => value.as_str(),
238                        ast::Expression::DotAccess { member, .. } => member.as_str(),
239                        _ => "",
240                    };
241                    let method = self.stream.peek_ahead(1).text;
242                    let args_str = type_args
243                        .iter()
244                        .map(format_annotation)
245                        .collect::<Vec<_>>()
246                        .join(", ");
247                    let plural = type_args.len() != 1;
248                    let title = if plural {
249                        "Misplaced type arguments"
250                    } else {
251                        "Misplaced type argument"
252                    };
253                    let help = if !type_name.is_empty() {
254                        format!(
255                            "Set the type {} on the method: `{}.{}<{}>()`",
256                            if plural { "arguments" } else { "argument" },
257                            type_name,
258                            method,
259                            args_str,
260                        )
261                    } else {
262                        format!(
263                            "Set the type {} on the method: `.{}<{}>()`",
264                            if plural { "arguments" } else { "argument" },
265                            method,
266                            args_str,
267                        )
268                    };
269                    let Some(first) = type_args.first() else {
270                        return self.parse_function_call(lhs, type_args);
271                    };
272                    let first_span = first.get_span();
273                    let last_span = type_args.last().expect("non-empty").get_span();
274                    let span = ast::Span::new(
275                        self.file_id,
276                        first_span.byte_offset,
277                        (last_span.byte_offset + last_span.byte_length)
278                            .saturating_sub(first_span.byte_offset),
279                    );
280                    let error = ParseError::new(title, span, "misplaced")
281                        .with_parse_code("syntax_error")
282                        .with_help(help);
283                    self.errors.push(error);
284
285                    let dot_access = self.parse_field_access(lhs);
286                    return self.parse_function_call(dot_access, type_args);
287                }
288
289                self.parse_function_call(lhs, type_args)
290            }
291
292            Colon => {
293                let lhs_name = match &lhs {
294                    ast::Expression::Identifier { value, .. } => value.to_string(),
295                    ast::Expression::DotAccess { member, .. } => member.to_string(),
296                    _ => std::string::String::new(),
297                };
298                let colon_token = self.current_token();
299                let span = ast::Span::new(self.file_id, colon_token.byte_offset, 2);
300                let after = self.stream.peek_ahead(2);
301
302                if after.kind == LeftAngleBracket {
303                    let help = if !lhs_name.is_empty() {
304                        format!(
305                            "Lisette does not use turbofish syntax. Use `{}<T>(...)` instead",
306                            lhs_name
307                        )
308                    } else {
309                        "Lisette does not use turbofish syntax. Use `func<T>(...)` instead"
310                            .to_string()
311                    };
312                    self.track_error_at(span, "invalid syntax", help);
313                    self.next(); // consume first `:`
314                    self.next(); // consume second `:`
315                    let type_args = self.parse_type_args();
316                    self.parse_function_call(lhs, type_args)
317                } else {
318                    let help = if !lhs_name.is_empty() && after.kind == Identifier {
319                        format!(
320                            "Use `.` instead of `::` for enum variant access, e.g. `{}.{}`",
321                            lhs_name, after.text
322                        )
323                    } else {
324                        "Use `.` instead of `::` for enum variant access".to_string()
325                    };
326                    self.track_error_at(span, "invalid syntax", help);
327                    self.next(); // consume first `:`
328                    self.next(); // consume second `:`
329                    let field_start = self.current_token();
330                    let field: EcoString = self.current_token().text.into();
331                    self.ensure(Identifier);
332                    ast::Expression::DotAccess {
333                        ty: Type::uninferred(),
334                        expression: lhs.into(),
335                        member: field,
336                        span: self.span_from_tokens(field_start),
337                    }
338                }
339            }
340
341            _ => {
342                debug_assert!(
343                    false,
344                    "is_postfix_operator and include_in_larger_expression are out of sync"
345                );
346                self.track_error("internal error", "Unexpected token in postfix position");
347                self.resync_on_error();
348                lhs
349            }
350        }
351    }
352
353    pub fn parse_range_end(&mut self) -> ast::Expression {
354        self.pratt_parse(RANGE_PREC)
355    }
356
357    fn check_go_channel_send(&mut self) -> bool {
358        if self.current_token().kind != LeftAngleBracket {
359            return false;
360        }
361        let next = self.stream.peek_ahead(1);
362        if next.kind != Minus {
363            return false;
364        }
365        let current = self.current_token();
366        if current.byte_offset + current.byte_length != next.byte_offset {
367            return false;
368        }
369
370        let span = ast::Span::new(
371            self.file_id,
372            self.current_token().byte_offset,
373            self.current_token().byte_length + 1,
374        );
375        self.track_error_at(
376            span,
377            "invalid syntax",
378            "Use `ch.Send(value)` inside a `select` expression",
379        );
380        self.resync_on_error();
381        true
382    }
383}
384
385fn format_annotation(ann: &ast::Annotation) -> std::string::String {
386    match ann {
387        ast::Annotation::Constructor { name, params, .. } => {
388            if params.is_empty() {
389                name.to_string()
390            } else {
391                format!(
392                    "{}<{}>",
393                    name,
394                    params
395                        .iter()
396                        .map(format_annotation)
397                        .collect::<Vec<_>>()
398                        .join(", ")
399                )
400            }
401        }
402        ast::Annotation::Tuple { elements, .. } => {
403            format!(
404                "({})",
405                elements
406                    .iter()
407                    .map(format_annotation)
408                    .collect::<Vec<_>>()
409                    .join(", ")
410            )
411        }
412        ast::Annotation::Function {
413            params,
414            return_type,
415            ..
416        } => {
417            format!(
418                "fn({}) -> {}",
419                params
420                    .iter()
421                    .map(format_annotation)
422                    .collect::<Vec<_>>()
423                    .join(", "),
424                format_annotation(return_type)
425            )
426        }
427        ast::Annotation::Unknown | ast::Annotation::Opaque { .. } => "_".to_string(),
428    }
429}