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