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                        dot_access_kind: None,
338                        receiver_coercion: None,
339                    }
340                }
341            }
342
343            _ => {
344                debug_assert!(
345                    false,
346                    "is_postfix_operator and include_in_larger_expression are out of sync"
347                );
348                self.track_error("internal error", "Unexpected token in postfix position");
349                self.resync_on_error();
350                lhs
351            }
352        }
353    }
354
355    pub fn parse_range_end(&mut self) -> ast::Expression {
356        self.pratt_parse(RANGE_PREC)
357    }
358
359    fn check_go_channel_send(&mut self) -> bool {
360        if self.current_token().kind != LeftAngleBracket {
361            return false;
362        }
363        let next = self.stream.peek_ahead(1);
364        if next.kind != Minus {
365            return false;
366        }
367        let current = self.current_token();
368        if current.byte_offset + current.byte_length != next.byte_offset {
369            return false;
370        }
371
372        let span = ast::Span::new(
373            self.file_id,
374            self.current_token().byte_offset,
375            self.current_token().byte_length + 1,
376        );
377        self.track_error_at(
378            span,
379            "invalid syntax",
380            "Use `ch.Send(value)` inside a `select` expression",
381        );
382        self.resync_on_error();
383        true
384    }
385}
386
387fn format_annotation(ann: &ast::Annotation) -> std::string::String {
388    match ann {
389        ast::Annotation::Constructor { name, params, .. } => {
390            if params.is_empty() {
391                name.to_string()
392            } else {
393                format!(
394                    "{}<{}>",
395                    name,
396                    params
397                        .iter()
398                        .map(format_annotation)
399                        .collect::<Vec<_>>()
400                        .join(", ")
401                )
402            }
403        }
404        ast::Annotation::Tuple { elements, .. } => {
405            format!(
406                "({})",
407                elements
408                    .iter()
409                    .map(format_annotation)
410                    .collect::<Vec<_>>()
411                    .join(", ")
412            )
413        }
414        ast::Annotation::Function {
415            params,
416            return_type,
417            ..
418        } => {
419            format!(
420                "fn({}) -> {}",
421                params
422                    .iter()
423                    .map(format_annotation)
424                    .collect::<Vec<_>>()
425                    .join(", "),
426                format_annotation(return_type)
427            )
428        }
429        ast::Annotation::Unknown | ast::Annotation::Opaque { .. } => "_".to_string(),
430    }
431}