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 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(); }
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(); self.next(); 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(); self.next(); 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}