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