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