1use crate::ast::*;
2use harn_lexer::{Span, TokenKind};
3
4use super::error::ParserError;
5use super::state::Parser;
6
7impl Parser {
8 pub fn parse_single_expression(&mut self) -> Result<SNode, ParserError> {
10 self.skip_newlines();
11 self.parse_expression()
12 }
13
14 pub(super) fn parse_expression(&mut self) -> Result<SNode, ParserError> {
15 self.skip_newlines();
16 self.parse_pipe()
17 }
18
19 pub(super) fn parse_pipe(&mut self) -> Result<SNode, ParserError> {
20 let mut left = self.parse_range()?;
21 while self.check_skip_newlines(&TokenKind::Pipe) {
22 let start = left.span;
23 self.advance();
24 self.skip_newlines();
25 let right = self.parse_range()?;
26 left = spanned(
27 Node::BinaryOp {
28 op: "|>".into(),
29 left: Box::new(left),
30 right: Box::new(right),
31 },
32 Span::merge(start, self.prev_span()),
33 );
34 }
35 Ok(left)
36 }
37
38 pub(super) fn parse_range(&mut self) -> Result<SNode, ParserError> {
39 let left = self.parse_ternary()?;
40 if self.check(&TokenKind::To) {
41 let start = left.span;
42 self.advance();
43 let right = self.parse_ternary()?;
44 let inclusive = if self.check(&TokenKind::Exclusive) {
45 self.advance();
46 false
47 } else {
48 true
49 };
50 return Ok(spanned(
51 Node::RangeExpr {
52 start: Box::new(left),
53 end: Box::new(right),
54 inclusive,
55 },
56 Span::merge(start, self.prev_span()),
57 ));
58 }
59 Ok(left)
60 }
61
62 pub(super) fn parse_ternary(&mut self) -> Result<SNode, ParserError> {
63 let condition = self.parse_logical_or()?;
64 if !self.check(&TokenKind::Question) {
65 return Ok(condition);
66 }
67 let start = condition.span;
68 self.advance(); let true_val = self.parse_ternary()?;
70 self.consume(&TokenKind::Colon, ":")?;
71 let false_val = self.parse_ternary()?;
72 Ok(spanned(
73 Node::Ternary {
74 condition: Box::new(condition),
75 true_expr: Box::new(true_val),
76 false_expr: Box::new(false_val),
77 },
78 Span::merge(start, self.prev_span()),
79 ))
80 }
81
82 pub(super) fn parse_nil_coalescing(&mut self) -> Result<SNode, ParserError> {
85 let mut left = self.parse_multiplicative()?;
86 while self.check_skip_newlines(&TokenKind::NilCoal) {
87 let start = left.span;
88 self.advance();
89 self.skip_newlines();
90 let right = self.parse_multiplicative()?;
91 left = spanned(
92 Node::BinaryOp {
93 op: "??".into(),
94 left: Box::new(left),
95 right: Box::new(right),
96 },
97 Span::merge(start, self.prev_span()),
98 );
99 }
100 Ok(left)
101 }
102
103 pub(super) fn parse_logical_or(&mut self) -> Result<SNode, ParserError> {
104 let mut left = self.parse_logical_and()?;
105 while self.check_skip_newlines(&TokenKind::Or) {
106 let start = left.span;
107 self.advance();
108 self.skip_newlines();
109 let right = self.parse_logical_and()?;
110 left = spanned(
111 Node::BinaryOp {
112 op: "||".into(),
113 left: Box::new(left),
114 right: Box::new(right),
115 },
116 Span::merge(start, self.prev_span()),
117 );
118 }
119 Ok(left)
120 }
121
122 pub(super) fn parse_logical_and(&mut self) -> Result<SNode, ParserError> {
123 let mut left = self.parse_equality()?;
124 while self.check_skip_newlines(&TokenKind::And) {
125 let start = left.span;
126 self.advance();
127 self.skip_newlines();
128 let right = self.parse_equality()?;
129 left = spanned(
130 Node::BinaryOp {
131 op: "&&".into(),
132 left: Box::new(left),
133 right: Box::new(right),
134 },
135 Span::merge(start, self.prev_span()),
136 );
137 }
138 Ok(left)
139 }
140
141 pub(super) fn parse_equality(&mut self) -> Result<SNode, ParserError> {
142 let mut left = self.parse_comparison()?;
143 while self.check_skip_newlines(&TokenKind::Eq) || self.check_skip_newlines(&TokenKind::Neq)
144 {
145 let start = left.span;
146 let op = if self.check(&TokenKind::Eq) {
147 "=="
148 } else {
149 "!="
150 };
151 self.advance();
152 self.skip_newlines();
153 let right = self.parse_comparison()?;
154 left = spanned(
155 Node::BinaryOp {
156 op: op.into(),
157 left: Box::new(left),
158 right: Box::new(right),
159 },
160 Span::merge(start, self.prev_span()),
161 );
162 }
163 Ok(left)
164 }
165
166 pub(super) fn parse_comparison(&mut self) -> Result<SNode, ParserError> {
167 let mut left = self.parse_additive()?;
168 loop {
169 if self.check_skip_newlines(&TokenKind::Lt)
170 || self.check_skip_newlines(&TokenKind::Gt)
171 || self.check_skip_newlines(&TokenKind::Lte)
172 || self.check_skip_newlines(&TokenKind::Gte)
173 {
174 let start = left.span;
175 let op = match self.current().map(|t| &t.kind) {
176 Some(TokenKind::Lt) => "<",
177 Some(TokenKind::Gt) => ">",
178 Some(TokenKind::Lte) => "<=",
179 Some(TokenKind::Gte) => ">=",
180 _ => "<",
181 };
182 self.advance();
183 self.skip_newlines();
184 let right = self.parse_additive()?;
185 left = spanned(
186 Node::BinaryOp {
187 op: op.into(),
188 left: Box::new(left),
189 right: Box::new(right),
190 },
191 Span::merge(start, self.prev_span()),
192 );
193 } else if self.check(&TokenKind::In) {
194 let start = left.span;
195 self.advance();
196 self.skip_newlines();
197 let right = self.parse_additive()?;
198 left = spanned(
199 Node::BinaryOp {
200 op: "in".into(),
201 left: Box::new(left),
202 right: Box::new(right),
203 },
204 Span::merge(start, self.prev_span()),
205 );
206 } else if self.check_identifier("not") {
207 let saved = self.pos;
208 self.advance();
209 if self.check(&TokenKind::In) {
210 let start = left.span;
211 self.advance();
212 self.skip_newlines();
213 let right = self.parse_additive()?;
214 left = spanned(
215 Node::BinaryOp {
216 op: "not_in".into(),
217 left: Box::new(left),
218 right: Box::new(right),
219 },
220 Span::merge(start, self.prev_span()),
221 );
222 } else {
223 self.pos = saved;
224 break;
225 }
226 } else {
227 break;
228 }
229 }
230 Ok(left)
231 }
232
233 pub(super) fn parse_additive(&mut self) -> Result<SNode, ParserError> {
234 let mut left = self.parse_nil_coalescing()?;
235 while self.check_skip_newlines(&TokenKind::Plus) || self.check(&TokenKind::Minus) {
236 let start = left.span;
237 let op = if self.check(&TokenKind::Plus) {
238 "+"
239 } else {
240 "-"
241 };
242 self.advance();
243 self.skip_newlines();
244 let right = self.parse_nil_coalescing()?;
245 left = spanned(
246 Node::BinaryOp {
247 op: op.into(),
248 left: Box::new(left),
249 right: Box::new(right),
250 },
251 Span::merge(start, self.prev_span()),
252 );
253 }
254 Ok(left)
255 }
256
257 pub(super) fn parse_multiplicative(&mut self) -> Result<SNode, ParserError> {
258 let mut left = self.parse_exponent()?;
259 while self.check_skip_newlines(&TokenKind::Star)
260 || self.check_skip_newlines(&TokenKind::Slash)
261 || self.check_skip_newlines(&TokenKind::Percent)
262 {
263 let start = left.span;
264 let op = if self.check(&TokenKind::Star) {
265 "*"
266 } else if self.check(&TokenKind::Slash) {
267 "/"
268 } else {
269 "%"
270 };
271 self.advance();
272 self.skip_newlines();
273 let right = self.parse_exponent()?;
274 left = spanned(
275 Node::BinaryOp {
276 op: op.into(),
277 left: Box::new(left),
278 right: Box::new(right),
279 },
280 Span::merge(start, self.prev_span()),
281 );
282 }
283 Ok(left)
284 }
285
286 pub(super) fn parse_exponent(&mut self) -> Result<SNode, ParserError> {
287 let left = self.parse_unary()?;
288 if !self.check_skip_newlines(&TokenKind::Pow) {
289 return Ok(left);
290 }
291
292 let start = left.span;
293 self.advance();
294 self.skip_newlines();
295 let right = self.parse_exponent()?;
296 Ok(spanned(
297 Node::BinaryOp {
298 op: "**".into(),
299 left: Box::new(left),
300 right: Box::new(right),
301 },
302 Span::merge(start, self.prev_span()),
303 ))
304 }
305
306 pub(super) fn parse_unary(&mut self) -> Result<SNode, ParserError> {
307 if self.check(&TokenKind::Not) {
308 let start = self.current_span();
309 self.advance();
310 let operand = self.parse_unary()?;
311 return Ok(spanned(
312 Node::UnaryOp {
313 op: "!".into(),
314 operand: Box::new(operand),
315 },
316 Span::merge(start, self.prev_span()),
317 ));
318 }
319 if self.check(&TokenKind::Minus) {
320 let start = self.current_span();
321 self.advance();
322 let operand = self.parse_unary()?;
323 return Ok(spanned(
324 Node::UnaryOp {
325 op: "-".into(),
326 operand: Box::new(operand),
327 },
328 Span::merge(start, self.prev_span()),
329 ));
330 }
331 self.parse_postfix()
332 }
333
334 pub(super) fn parse_postfix(&mut self) -> Result<SNode, ParserError> {
335 let mut expr = self.parse_primary()?;
336
337 loop {
338 if self.check_skip_newlines(&TokenKind::Dot)
339 || self.check_skip_newlines(&TokenKind::QuestionDot)
340 {
341 let optional = self.check(&TokenKind::QuestionDot);
342 let start = expr.span;
343 self.advance();
344 let member = self.consume_identifier_or_keyword("member name")?;
345 if self.check(&TokenKind::LParen) {
346 self.advance();
347 let args = self.parse_arg_list()?;
348 self.consume(&TokenKind::RParen, ")")?;
349 if optional {
350 expr = spanned(
351 Node::OptionalMethodCall {
352 object: Box::new(expr),
353 method: member,
354 args,
355 },
356 Span::merge(start, self.prev_span()),
357 );
358 } else {
359 expr = spanned(
360 Node::MethodCall {
361 object: Box::new(expr),
362 method: member,
363 args,
364 },
365 Span::merge(start, self.prev_span()),
366 );
367 }
368 } else if optional {
369 expr = spanned(
370 Node::OptionalPropertyAccess {
371 object: Box::new(expr),
372 property: member,
373 },
374 Span::merge(start, self.prev_span()),
375 );
376 } else {
377 expr = spanned(
378 Node::PropertyAccess {
379 object: Box::new(expr),
380 property: member,
381 },
382 Span::merge(start, self.prev_span()),
383 );
384 }
385 } else if self.check(&TokenKind::LBracket) {
386 let start = expr.span;
387 self.advance();
388
389 if self.check(&TokenKind::Colon) {
392 self.advance();
393 let end_expr = if self.check(&TokenKind::RBracket) {
394 None
395 } else {
396 Some(Box::new(self.parse_expression()?))
397 };
398 self.consume(&TokenKind::RBracket, "]")?;
399 expr = spanned(
400 Node::SliceAccess {
401 object: Box::new(expr),
402 start: None,
403 end: end_expr,
404 },
405 Span::merge(start, self.prev_span()),
406 );
407 } else {
408 let index = self.parse_expression()?;
409 if self.check(&TokenKind::Colon) {
410 self.advance();
411 let end_expr = if self.check(&TokenKind::RBracket) {
412 None
413 } else {
414 Some(Box::new(self.parse_expression()?))
415 };
416 self.consume(&TokenKind::RBracket, "]")?;
417 expr = spanned(
418 Node::SliceAccess {
419 object: Box::new(expr),
420 start: Some(Box::new(index)),
421 end: end_expr,
422 },
423 Span::merge(start, self.prev_span()),
424 );
425 } else {
426 self.consume(&TokenKind::RBracket, "]")?;
427 expr = spanned(
428 Node::SubscriptAccess {
429 object: Box::new(expr),
430 index: Box::new(index),
431 },
432 Span::merge(start, self.prev_span()),
433 );
434 }
435 }
436 } else if self.check(&TokenKind::LBrace) {
437 let struct_name = match &expr.node {
438 Node::Identifier(name) if self.is_struct_construct_lookahead(name) => {
439 Some(name.clone())
440 }
441 _ => None,
442 };
443 let Some(struct_name) = struct_name else {
444 break;
445 };
446 let start = expr.span;
447 self.advance();
448 let dict = self.parse_dict_literal(start)?;
449 let fields = match dict.node {
450 Node::DictLiteral(fields) => fields,
451 _ => unreachable!("dict parser must return a dict literal"),
452 };
453 expr = spanned(
454 Node::StructConstruct {
455 struct_name,
456 fields,
457 },
458 dict.span,
459 );
460 } else if self.check(&TokenKind::Lt) && matches!(expr.node, Node::Identifier(_)) {
461 let saved_pos = self.pos;
462 let start = expr.span;
463 self.advance();
464 let parsed_type_args = self.parse_type_arg_list();
465 if let Ok(type_args) = parsed_type_args {
466 if self.check(&TokenKind::LParen) {
467 self.advance();
468 let args = self.parse_arg_list()?;
469 self.consume(&TokenKind::RParen, ")")?;
470 if let Node::Identifier(name) = expr.node {
471 expr = spanned(
472 Node::FunctionCall {
473 name,
474 type_args,
475 args,
476 },
477 Span::merge(start, self.prev_span()),
478 );
479 }
480 } else {
481 self.pos = saved_pos;
482 break;
483 }
484 } else {
485 self.pos = saved_pos;
486 break;
487 }
488 } else if self.check(&TokenKind::LParen) && matches!(expr.node, Node::Identifier(_)) {
489 let start = expr.span;
490 self.advance();
491 let args = self.parse_arg_list()?;
492 self.consume(&TokenKind::RParen, ")")?;
493 if let Node::Identifier(name) = expr.node {
494 expr = spanned(
495 Node::FunctionCall {
496 name,
497 type_args: Vec::new(),
498 args,
499 },
500 Span::merge(start, self.prev_span()),
501 );
502 }
503 } else if self.check(&TokenKind::Question) {
504 if self.question_starts_ternary_branch() {
507 break;
508 }
509 if matches!(self.peek_kind_at(1), Some(TokenKind::LBracket)) {
510 let start = expr.span;
511 self.advance(); self.advance(); let index = self.parse_expression()?;
514 self.consume(&TokenKind::RBracket, "]")?;
515 expr = spanned(
516 Node::OptionalSubscriptAccess {
517 object: Box::new(expr),
518 index: Box::new(index),
519 },
520 Span::merge(start, self.prev_span()),
521 );
522 continue;
523 }
524 let start = expr.span;
525 self.advance();
526 expr = spanned(
527 Node::TryOperator {
528 operand: Box::new(expr),
529 },
530 Span::merge(start, self.prev_span()),
531 );
532 } else {
533 break;
534 }
535 }
536
537 Ok(expr)
538 }
539
540 fn question_starts_ternary_branch(&self) -> bool {
541 self.peek_kind_at(1)
542 .is_some_and(Self::token_starts_ternary_branch)
543 && self.question_has_top_level_ternary_colon()
544 }
545
546 fn token_starts_ternary_branch(kind: &TokenKind) -> bool {
547 matches!(
548 kind,
549 TokenKind::Identifier(_)
550 | TokenKind::IntLiteral(_)
551 | TokenKind::FloatLiteral(_)
552 | TokenKind::StringLiteral(_)
553 | TokenKind::RawStringLiteral(_)
554 | TokenKind::InterpolatedString(_)
555 | TokenKind::True
556 | TokenKind::False
557 | TokenKind::Nil
558 | TokenKind::LParen
559 | TokenKind::LBracket
560 | TokenKind::LBrace
561 | TokenKind::Not
562 | TokenKind::Minus
563 | TokenKind::Fn
564 | TokenKind::If
565 | TokenKind::Match
566 | TokenKind::Try
567 | TokenKind::Spawn
568 | TokenKind::Parallel
569 | TokenKind::Retry
570 | TokenKind::Deadline
571 | TokenKind::RequestApproval
572 | TokenKind::DualControl
573 | TokenKind::AskUser
574 | TokenKind::EscalateTo
575 | TokenKind::DurationLiteral(_)
576 )
577 }
578
579 fn question_has_top_level_ternary_colon(&self) -> bool {
580 let mut delimiter_depth = 0usize;
581 for (pos, token) in self.tokens.iter().enumerate().skip(self.pos + 1) {
582 if delimiter_depth == 0 {
583 match token.kind {
584 TokenKind::Colon => return true,
585 TokenKind::Newline => {
586 if self.next_non_newline_continues_ternary_branch(pos + 1) {
587 continue;
588 }
589 return false;
590 }
591 TokenKind::RParen
592 | TokenKind::RBracket
593 | TokenKind::RBrace
594 | TokenKind::Eof => {
595 return false;
596 }
597 _ => {}
598 }
599 }
600
601 match token.kind {
602 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => {
603 delimiter_depth += 1;
604 }
605 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
606 delimiter_depth = delimiter_depth.saturating_sub(1);
607 }
608 TokenKind::Eof => return false,
609 _ => {}
610 }
611 }
612 false
613 }
614
615 fn next_non_newline_continues_ternary_branch(&self, start_pos: usize) -> bool {
616 let Some(kind) = self
617 .tokens
618 .iter()
619 .skip(start_pos)
620 .find(|token| token.kind != TokenKind::Newline)
621 .map(|token| &token.kind)
622 else {
623 return false;
624 };
625 matches!(
626 kind,
627 TokenKind::Colon
628 | TokenKind::Plus
629 | TokenKind::Star
630 | TokenKind::Slash
631 | TokenKind::Percent
632 | TokenKind::Pow
633 | TokenKind::And
634 | TokenKind::Or
635 | TokenKind::Eq
636 | TokenKind::Neq
637 | TokenKind::Lt
638 | TokenKind::Gt
639 | TokenKind::Lte
640 | TokenKind::Gte
641 | TokenKind::NilCoal
642 | TokenKind::Pipe
643 | TokenKind::Dot
644 | TokenKind::QuestionDot
645 )
646 }
647
648 pub(super) fn parse_primary(&mut self) -> Result<SNode, ParserError> {
649 let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
650 expected: "expression".into(),
651 span: self.prev_span(),
652 })?;
653 let start = self.current_span();
654
655 match &tok.kind {
656 TokenKind::StringLiteral(s) => {
657 let s = s.clone();
658 self.advance();
659 Ok(spanned(
660 Node::StringLiteral(s),
661 Span::merge(start, self.prev_span()),
662 ))
663 }
664 TokenKind::RawStringLiteral(s) => {
665 let s = s.clone();
666 self.advance();
667 Ok(spanned(
668 Node::RawStringLiteral(s),
669 Span::merge(start, self.prev_span()),
670 ))
671 }
672 TokenKind::InterpolatedString(segments) => {
673 let segments = segments.clone();
674 self.advance();
675 Ok(spanned(
676 Node::InterpolatedString(segments),
677 Span::merge(start, self.prev_span()),
678 ))
679 }
680 TokenKind::IntLiteral(n) => {
681 let n = *n;
682 self.advance();
683 Ok(spanned(
684 Node::IntLiteral(n),
685 Span::merge(start, self.prev_span()),
686 ))
687 }
688 TokenKind::FloatLiteral(n) => {
689 let n = *n;
690 self.advance();
691 Ok(spanned(
692 Node::FloatLiteral(n),
693 Span::merge(start, self.prev_span()),
694 ))
695 }
696 TokenKind::True => {
697 self.advance();
698 Ok(spanned(
699 Node::BoolLiteral(true),
700 Span::merge(start, self.prev_span()),
701 ))
702 }
703 TokenKind::False => {
704 self.advance();
705 Ok(spanned(
706 Node::BoolLiteral(false),
707 Span::merge(start, self.prev_span()),
708 ))
709 }
710 TokenKind::Nil => {
711 self.advance();
712 Ok(spanned(
713 Node::NilLiteral,
714 Span::merge(start, self.prev_span()),
715 ))
716 }
717 TokenKind::Identifier(name)
718 if name == "cost_route" && self.peek_kind() == Some(&TokenKind::LBrace) =>
719 {
720 self.parse_cost_route()
721 }
722 TokenKind::Identifier(name) => {
723 let name = name.clone();
724 self.advance();
725 Ok(spanned(
726 Node::Identifier(name),
727 Span::merge(start, self.prev_span()),
728 ))
729 }
730 TokenKind::LParen => {
731 self.advance();
732 let expr = self.parse_expression()?;
733 self.consume(&TokenKind::RParen, ")")?;
734 Ok(expr)
735 }
736 TokenKind::LBracket => self.parse_list_literal(),
737 TokenKind::LBrace => self.parse_dict_or_closure(),
738 TokenKind::Parallel => self.parse_parallel(),
739 TokenKind::Retry => self.parse_retry(),
740 TokenKind::If => self.parse_if_else(),
741 TokenKind::Spawn => self.parse_spawn_expr(),
742 TokenKind::RequestApproval => self.parse_hitl_expr(HitlKind::RequestApproval),
743 TokenKind::DualControl => self.parse_hitl_expr(HitlKind::DualControl),
744 TokenKind::AskUser => self.parse_hitl_expr(HitlKind::AskUser),
745 TokenKind::EscalateTo => self.parse_hitl_expr(HitlKind::EscalateTo),
746 TokenKind::DurationLiteral(ms) => {
747 let ms = *ms;
748 self.advance();
749 Ok(spanned(
750 Node::DurationLiteral(ms),
751 Span::merge(start, self.prev_span()),
752 ))
753 }
754 TokenKind::Deadline => self.parse_deadline(),
755 TokenKind::Try => self.parse_try_catch(),
756 TokenKind::Match => self.parse_match(),
757 TokenKind::Fn => self.parse_fn_expr(),
758 TokenKind::Lt
761 if matches!(self.peek_kind(), Some(&TokenKind::Lt))
762 && matches!(self.peek_kind_at(2), Some(TokenKind::Identifier(_))) =>
763 {
764 Err(ParserError::Unexpected {
765 got: "`<<` heredoc-like syntax".to_string(),
766 expected: "an expression — heredocs are only valid \
767 inside LLM tool-call argument JSON; \
768 for multiline strings in source code use \
769 triple-quoted `\"\"\"...\"\"\"`"
770 .to_string(),
771 span: start,
772 })
773 }
774 _ => Err(self.error("expression")),
775 }
776 }
777
778 pub(super) fn parse_fn_expr(&mut self) -> Result<SNode, ParserError> {
781 let start = self.current_span();
782 self.consume(&TokenKind::Fn, "fn")?;
783 self.consume(&TokenKind::LParen, "(")?;
784 let params = self.parse_typed_param_list()?;
785 self.consume(&TokenKind::RParen, ")")?;
786 self.consume(&TokenKind::LBrace, "{")?;
787 let body = self.parse_block()?;
788 self.consume(&TokenKind::RBrace, "}")?;
789 Ok(spanned(
790 Node::Closure {
791 params,
792 body,
793 fn_syntax: true,
794 },
795 Span::merge(start, self.prev_span()),
796 ))
797 }
798
799 pub(super) fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
800 let start = self.current_span();
801 self.consume(&TokenKind::Spawn, "spawn")?;
802 self.consume(&TokenKind::LBrace, "{")?;
803 let body = self.parse_block()?;
804 self.consume(&TokenKind::RBrace, "}")?;
805 Ok(spanned(
806 Node::SpawnExpr { body },
807 Span::merge(start, self.prev_span()),
808 ))
809 }
810
811 pub(super) fn parse_hitl_expr(&mut self, kind: HitlKind) -> Result<SNode, ParserError> {
823 let start = self.current_span();
824 let kw_token = match kind {
825 HitlKind::RequestApproval => TokenKind::RequestApproval,
826 HitlKind::DualControl => TokenKind::DualControl,
827 HitlKind::AskUser => TokenKind::AskUser,
828 HitlKind::EscalateTo => TokenKind::EscalateTo,
829 };
830 self.consume(&kw_token, kind.as_keyword())?;
831 self.consume(&TokenKind::LParen, "(")?;
832 self.skip_newlines();
833
834 let mut args: Vec<HitlArg> = Vec::new();
835 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
836 let arg_start = self.current_span();
837 let is_named = matches!(
844 (self.peek_kind_at(0), self.peek_kind_at(1)),
845 (Some(TokenKind::Identifier(_)), Some(TokenKind::Colon))
846 );
847 let (name, value) = if is_named {
848 let Some(TokenKind::Identifier(raw)) = self.peek_kind_at(0).cloned() else {
849 unreachable!("named arg dispatch already matched Identifier token")
850 };
851 self.advance();
852 self.consume(&TokenKind::Colon, ":")?;
853 self.skip_newlines();
854 let value = self.parse_expression()?;
855 (Some(raw), value)
856 } else {
857 (None, self.parse_expression()?)
858 };
859 let arg_span = Span::merge(arg_start, self.prev_span());
860 args.push(HitlArg {
861 name,
862 value,
863 span: arg_span,
864 });
865 self.skip_newlines();
866 if self.check(&TokenKind::Comma) {
867 self.advance();
868 self.skip_newlines();
869 } else {
870 break;
871 }
872 }
873
874 self.skip_newlines();
875 self.consume(&TokenKind::RParen, ")")?;
876 Ok(spanned(
877 Node::HitlExpr { kind, args },
878 Span::merge(start, self.prev_span()),
879 ))
880 }
881
882 pub(super) fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
883 let start = self.current_span();
884 self.consume(&TokenKind::LBracket, "[")?;
885 let mut elements = Vec::new();
886 self.skip_newlines();
887
888 while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
889 if self.check(&TokenKind::Dot) {
890 let saved_pos = self.pos;
891 self.advance();
892 if self.check(&TokenKind::Dot) {
893 self.advance();
894 self.consume(&TokenKind::Dot, ".")?;
895 let spread_start = self.tokens[saved_pos].span;
896 let expr = self.parse_expression()?;
897 elements.push(spanned(
898 Node::Spread(Box::new(expr)),
899 Span::merge(spread_start, self.prev_span()),
900 ));
901 } else {
902 self.pos = saved_pos;
903 elements.push(self.parse_expression()?);
904 }
905 } else {
906 elements.push(self.parse_expression()?);
907 }
908 self.skip_newlines();
909 if self.check(&TokenKind::Comma) {
910 self.advance();
911 self.skip_newlines();
912 }
913 }
914
915 self.consume(&TokenKind::RBracket, "]")?;
916 Ok(spanned(
917 Node::ListLiteral(elements),
918 Span::merge(start, self.prev_span()),
919 ))
920 }
921
922 pub(super) fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
923 let start = self.current_span();
924 self.consume(&TokenKind::LBrace, "{")?;
925 self.skip_newlines();
926
927 if self.check(&TokenKind::RBrace) {
928 self.advance();
929 return Ok(spanned(
930 Node::DictLiteral(Vec::new()),
931 Span::merge(start, self.prev_span()),
932 ));
933 }
934
935 let saved = self.pos;
937 if self.is_closure_lookahead() {
938 self.pos = saved;
939 return self.parse_closure_body(start);
940 }
941 self.pos = saved;
942 self.parse_dict_literal(start)
943 }
944
945 pub(super) fn is_struct_construct_lookahead(&self, struct_name: &str) -> bool {
949 if !struct_name
950 .chars()
951 .next()
952 .is_some_and(|ch| ch.is_uppercase())
953 {
954 return false;
955 }
956
957 let mut offset = 1;
958 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
959 offset += 1;
960 }
961
962 match self.peek_kind_at(offset) {
963 Some(TokenKind::RBrace) => true,
964 Some(TokenKind::Identifier(_)) | Some(TokenKind::StringLiteral(_)) => {
965 offset += 1;
966 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
967 offset += 1;
968 }
969 matches!(self.peek_kind_at(offset), Some(TokenKind::Colon))
970 }
971 _ => false,
972 }
973 }
974
975 pub(super) fn is_closure_lookahead(&mut self) -> bool {
977 let mut depth = 0;
978 while !self.is_at_end() {
979 if let Some(tok) = self.current() {
980 match &tok.kind {
981 TokenKind::Arrow if depth == 0 => return true,
982 TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
983 TokenKind::RBrace if depth == 0 => return false,
984 TokenKind::RBrace => depth -= 1,
985 TokenKind::RParen | TokenKind::RBracket if depth > 0 => depth -= 1,
986 _ => {}
987 }
988 self.advance();
989 } else {
990 return false;
991 }
992 }
993 false
994 }
995
996 pub(super) fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
998 let params = self.parse_typed_param_list_until_arrow()?;
999 self.consume(&TokenKind::Arrow, "->")?;
1000 let body = self.parse_block()?;
1001 self.consume(&TokenKind::RBrace, "}")?;
1002 Ok(spanned(
1003 Node::Closure {
1004 params,
1005 body,
1006 fn_syntax: false,
1007 },
1008 Span::merge(start, self.prev_span()),
1009 ))
1010 }
1011
1012 pub(super) fn parse_typed_param_list_until_arrow(
1014 &mut self,
1015 ) -> Result<Vec<TypedParam>, ParserError> {
1016 self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1017 }
1018
1019 pub(super) fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1020 let entries = self.parse_dict_entries()?;
1021 Ok(spanned(
1022 Node::DictLiteral(entries),
1023 Span::merge(start, self.prev_span()),
1024 ))
1025 }
1026
1027 pub(super) fn parse_dict_entries(&mut self) -> Result<Vec<DictEntry>, ParserError> {
1028 let mut entries = Vec::new();
1029 self.skip_newlines();
1030
1031 while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1032 if self.check(&TokenKind::Dot) {
1033 let saved_pos = self.pos;
1034 self.advance();
1035 if self.check(&TokenKind::Dot) {
1036 self.advance();
1037 if self.check(&TokenKind::Dot) {
1038 self.advance();
1039 let spread_start = self.tokens[saved_pos].span;
1040 let expr = self.parse_expression()?;
1041 entries.push(DictEntry {
1042 key: spanned(Node::NilLiteral, spread_start),
1043 value: spanned(
1044 Node::Spread(Box::new(expr)),
1045 Span::merge(spread_start, self.prev_span()),
1046 ),
1047 });
1048 self.skip_newlines();
1049 if self.check(&TokenKind::Comma) {
1050 self.advance();
1051 self.skip_newlines();
1052 }
1053 continue;
1054 }
1055 self.pos = saved_pos;
1056 } else {
1057 self.pos = saved_pos;
1058 }
1059 }
1060 let key = if self.check(&TokenKind::LBracket) {
1061 self.advance();
1062 let k = self.parse_expression()?;
1063 self.consume(&TokenKind::RBracket, "]")?;
1064 k
1065 } else if matches!(
1066 self.current().map(|t| &t.kind),
1067 Some(TokenKind::StringLiteral(_))
1068 ) {
1069 let key_span = self.current_span();
1070 let name =
1071 if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1072 s.clone()
1073 } else {
1074 unreachable!()
1075 };
1076 self.advance();
1077 spanned(Node::StringLiteral(name), key_span)
1078 } else {
1079 let key_span = self.current_span();
1080 let name = self.consume_identifier_or_keyword("dict key")?;
1081 spanned(Node::StringLiteral(name), key_span)
1082 };
1083 self.consume(&TokenKind::Colon, ":")?;
1084 let value = self.parse_expression()?;
1085 entries.push(DictEntry { key, value });
1086 self.skip_newlines();
1087 if self.check(&TokenKind::Comma) {
1088 self.advance();
1089 self.skip_newlines();
1090 }
1091 }
1092
1093 self.consume(&TokenKind::RBrace, "}")?;
1094 Ok(entries)
1095 }
1096
1097 pub(super) fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1099 let mut params = Vec::new();
1100 self.skip_newlines();
1101
1102 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1103 params.push(self.consume_identifier("parameter name")?);
1104 if self.check(&TokenKind::Comma) {
1105 self.advance();
1106 self.skip_newlines();
1107 }
1108 }
1109 Ok(params)
1110 }
1111
1112 pub(super) fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1114 self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1115 }
1116
1117 pub(super) fn parse_typed_params_until(
1120 &mut self,
1121 is_terminator: impl Fn(&TokenKind) -> bool,
1122 ) -> Result<Vec<TypedParam>, ParserError> {
1123 let mut params = Vec::new();
1124 let mut seen_default = false;
1125 self.skip_newlines();
1126
1127 while !self.is_at_end() {
1128 if let Some(tok) = self.current() {
1129 if is_terminator(&tok.kind) {
1130 break;
1131 }
1132 } else {
1133 break;
1134 }
1135 let is_rest = if self.check(&TokenKind::Dot) {
1136 let p1 = self.pos + 1;
1137 let p2 = self.pos + 2;
1138 let is_ellipsis = p1 < self.tokens.len()
1139 && p2 < self.tokens.len()
1140 && self.tokens[p1].kind == TokenKind::Dot
1141 && self.tokens[p2].kind == TokenKind::Dot;
1142 if is_ellipsis {
1143 self.advance();
1144 self.advance();
1145 self.advance();
1146 true
1147 } else {
1148 false
1149 }
1150 } else {
1151 false
1152 };
1153 let name = self.consume_identifier("parameter name")?;
1154 let type_expr = self.try_parse_type_annotation()?;
1155 let default_value = if self.check(&TokenKind::Assign) {
1156 self.advance();
1157 seen_default = true;
1158 Some(Box::new(self.parse_expression()?))
1159 } else {
1160 if seen_default && !is_rest {
1161 return Err(self.error(
1162 "Required parameter cannot follow a parameter with a default value",
1163 ));
1164 }
1165 None
1166 };
1167 if is_rest
1168 && !is_terminator(
1169 &self
1170 .current()
1171 .map(|t| t.kind.clone())
1172 .unwrap_or(TokenKind::Eof),
1173 )
1174 {
1175 return Err(self.error("Rest parameter must be the last parameter"));
1176 }
1177 params.push(TypedParam {
1178 name,
1179 type_expr,
1180 default_value,
1181 rest: is_rest,
1182 });
1183 if self.check(&TokenKind::Comma) {
1184 self.advance();
1185 self.skip_newlines();
1186 }
1187 }
1188 Ok(params)
1189 }
1190
1191 pub(super) fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
1192 let mut args = Vec::new();
1193 self.skip_newlines();
1194
1195 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1196 if self.check(&TokenKind::Dot) {
1197 let saved_pos = self.pos;
1198 self.advance();
1199 if self.check(&TokenKind::Dot) {
1200 self.advance();
1201 self.consume(&TokenKind::Dot, ".")?;
1202 let spread_start = self.tokens[saved_pos].span;
1203 let expr = self.parse_expression()?;
1204 args.push(spanned(
1205 Node::Spread(Box::new(expr)),
1206 Span::merge(spread_start, self.prev_span()),
1207 ));
1208 } else {
1209 self.pos = saved_pos;
1210 args.push(self.parse_expression()?);
1211 }
1212 } else {
1213 args.push(self.parse_expression()?);
1214 }
1215 self.skip_newlines();
1216 if self.check(&TokenKind::Comma) {
1217 self.advance();
1218 self.skip_newlines();
1219 }
1220 }
1221 Ok(args)
1222 }
1223}