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 self.consume(&TokenKind::LBrace, "{")?;
843 let body = self.parse_block()?;
844 self.consume(&TokenKind::RBrace, "}")?;
845 Ok(spanned(
846 Node::Closure {
847 params,
848 body,
849 fn_syntax: true,
850 },
851 Span::merge(start, self.prev_span()),
852 ))
853 }
854
855 pub(super) fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
856 let start = self.current_span();
857 self.consume(&TokenKind::Spawn, "spawn")?;
858 self.consume(&TokenKind::LBrace, "{")?;
859 let body = self.parse_block()?;
860 self.consume(&TokenKind::RBrace, "}")?;
861 Ok(spanned(
862 Node::SpawnExpr { body },
863 Span::merge(start, self.prev_span()),
864 ))
865 }
866
867 pub(super) fn parse_hitl_expr(&mut self, kind: HitlKind) -> Result<SNode, ParserError> {
879 let start = self.current_span();
880 let kw_token = match kind {
881 HitlKind::RequestApproval => TokenKind::RequestApproval,
882 HitlKind::DualControl => TokenKind::DualControl,
883 HitlKind::AskUser => TokenKind::AskUser,
884 HitlKind::EscalateTo => TokenKind::EscalateTo,
885 };
886 self.consume(&kw_token, kind.as_keyword())?;
887 self.consume(&TokenKind::LParen, "(")?;
888 self.skip_newlines();
889
890 let mut args: Vec<HitlArg> = Vec::new();
891 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
892 let arg_start = self.current_span();
893 let is_named = matches!(
900 (self.peek_kind_at(0), self.peek_kind_at(1)),
901 (Some(TokenKind::Identifier(_)), Some(TokenKind::Colon))
902 );
903 let (name, value) = if is_named {
904 let Some(TokenKind::Identifier(raw)) = self.peek_kind_at(0).cloned() else {
905 unreachable!("named arg dispatch already matched Identifier token")
906 };
907 self.advance();
908 self.consume(&TokenKind::Colon, ":")?;
909 self.skip_newlines();
910 let value = self.parse_nested_expression("HITL argument")?;
911 (Some(raw), value)
912 } else {
913 (None, self.parse_nested_expression("HITL argument")?)
914 };
915 let arg_span = Span::merge(arg_start, self.prev_span());
916 args.push(HitlArg {
917 name,
918 value,
919 span: arg_span,
920 });
921 self.skip_newlines();
922 if self.check(&TokenKind::Comma) {
923 self.advance();
924 self.skip_newlines();
925 } else {
926 break;
927 }
928 }
929
930 self.skip_newlines();
931 self.consume(&TokenKind::RParen, ")")?;
932 Ok(spanned(
933 Node::HitlExpr { kind, args },
934 Span::merge(start, self.prev_span()),
935 ))
936 }
937
938 pub(super) fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
939 let start = self.current_span();
940 self.consume(&TokenKind::LBracket, "[")?;
941 let mut elements = Vec::new();
942 self.skip_newlines();
943
944 while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
945 if self.check(&TokenKind::Dot) {
946 let saved_pos = self.pos;
947 self.advance();
948 if self.check(&TokenKind::Dot) {
949 self.advance();
950 self.consume(&TokenKind::Dot, ".")?;
951 let spread_start = self.tokens[saved_pos].span;
952 let expr = self.parse_nested_expression("list spread")?;
953 elements.push(spanned(
954 Node::Spread(Box::new(expr)),
955 Span::merge(spread_start, self.prev_span()),
956 ));
957 } else {
958 self.pos = saved_pos;
959 elements.push(self.parse_nested_expression("list element")?);
960 }
961 } else {
962 elements.push(self.parse_nested_expression("list element")?);
963 }
964 self.skip_newlines();
965 if self.check(&TokenKind::Comma) {
966 self.advance();
967 self.skip_newlines();
968 }
969 }
970
971 self.consume(&TokenKind::RBracket, "]")?;
972 Ok(spanned(
973 Node::ListLiteral(elements),
974 Span::merge(start, self.prev_span()),
975 ))
976 }
977
978 pub(super) fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
979 let start = self.current_span();
980 self.consume(&TokenKind::LBrace, "{")?;
981 self.skip_newlines();
982
983 if self.check(&TokenKind::RBrace) {
984 self.advance();
985 return Ok(spanned(
986 Node::DictLiteral(Vec::new()),
987 Span::merge(start, self.prev_span()),
988 ));
989 }
990
991 let saved = self.pos;
993 if self.is_closure_lookahead() {
994 self.pos = saved;
995 return self.parse_closure_body(start);
996 }
997 self.pos = saved;
998 self.parse_dict_literal(start)
999 }
1000
1001 pub(super) fn is_struct_construct_lookahead(&self, struct_name: &str) -> bool {
1005 if !struct_name
1006 .chars()
1007 .next()
1008 .is_some_and(|ch| ch.is_uppercase())
1009 {
1010 return false;
1011 }
1012
1013 let mut offset = 1;
1014 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
1015 offset += 1;
1016 }
1017
1018 match self.peek_kind_at(offset) {
1019 Some(TokenKind::RBrace) => true,
1020 Some(TokenKind::Identifier(_)) | Some(TokenKind::StringLiteral(_)) => {
1021 offset += 1;
1022 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
1023 offset += 1;
1024 }
1025 matches!(self.peek_kind_at(offset), Some(TokenKind::Colon))
1026 }
1027 _ => false,
1028 }
1029 }
1030
1031 pub(super) fn is_closure_lookahead(&mut self) -> bool {
1033 let mut depth = 0;
1034 while !self.is_at_end() {
1035 if let Some(tok) = self.current() {
1036 match &tok.kind {
1037 TokenKind::Arrow if depth == 0 => return true,
1038 TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
1039 TokenKind::RBrace if depth == 0 => return false,
1040 TokenKind::RBrace => depth -= 1,
1041 TokenKind::RParen | TokenKind::RBracket if depth > 0 => depth -= 1,
1042 _ => {}
1043 }
1044 self.advance();
1045 } else {
1046 return false;
1047 }
1048 }
1049 false
1050 }
1051
1052 pub(super) fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
1054 let params = self.parse_typed_param_list_until_arrow()?;
1055 self.consume(&TokenKind::Arrow, "->")?;
1056 let body = self.parse_block()?;
1057 self.consume(&TokenKind::RBrace, "}")?;
1058 Ok(spanned(
1059 Node::Closure {
1060 params,
1061 body,
1062 fn_syntax: false,
1063 },
1064 Span::merge(start, self.prev_span()),
1065 ))
1066 }
1067
1068 pub(super) fn parse_typed_param_list_until_arrow(
1070 &mut self,
1071 ) -> Result<Vec<TypedParam>, ParserError> {
1072 self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1073 }
1074
1075 pub(super) fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1076 let entries = self.parse_dict_entries()?;
1077 Ok(spanned(
1078 Node::DictLiteral(entries),
1079 Span::merge(start, self.prev_span()),
1080 ))
1081 }
1082
1083 pub(super) fn parse_dict_entries(&mut self) -> Result<Vec<DictEntry>, ParserError> {
1084 let mut entries = Vec::new();
1085 self.skip_newlines();
1086
1087 while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1088 if self.check(&TokenKind::Dot) {
1089 let saved_pos = self.pos;
1090 self.advance();
1091 if self.check(&TokenKind::Dot) {
1092 self.advance();
1093 if self.check(&TokenKind::Dot) {
1094 self.advance();
1095 let spread_start = self.tokens[saved_pos].span;
1096 let expr = self.parse_nested_expression("dict spread")?;
1097 entries.push(DictEntry {
1098 key: spanned(Node::NilLiteral, spread_start),
1099 value: spanned(
1100 Node::Spread(Box::new(expr)),
1101 Span::merge(spread_start, self.prev_span()),
1102 ),
1103 });
1104 self.skip_newlines();
1105 if self.check(&TokenKind::Comma) {
1106 self.advance();
1107 self.skip_newlines();
1108 }
1109 continue;
1110 }
1111 self.pos = saved_pos;
1112 } else {
1113 self.pos = saved_pos;
1114 }
1115 }
1116 let key = if self.check(&TokenKind::LBracket) {
1117 self.advance();
1118 let k = self.parse_nested_expression("computed dict key")?;
1119 self.consume(&TokenKind::RBracket, "]")?;
1120 k
1121 } else if matches!(
1122 self.current().map(|t| &t.kind),
1123 Some(TokenKind::StringLiteral(_))
1124 ) {
1125 let key_span = self.current_span();
1126 let name =
1127 if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1128 s.clone()
1129 } else {
1130 unreachable!()
1131 };
1132 self.advance();
1133 spanned(Node::StringLiteral(name), key_span)
1134 } else {
1135 let key_span = self.current_span();
1136 let name = self.consume_identifier_or_keyword("dict key")?;
1137 spanned(Node::StringLiteral(name), key_span)
1138 };
1139 self.consume(&TokenKind::Colon, ":")?;
1140 let value = self.parse_nested_expression("dict value")?;
1141 entries.push(DictEntry { key, value });
1142 self.skip_newlines();
1143 if self.check(&TokenKind::Comma) {
1144 self.advance();
1145 self.skip_newlines();
1146 }
1147 }
1148
1149 self.consume(&TokenKind::RBrace, "}")?;
1150 Ok(entries)
1151 }
1152
1153 pub(super) fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1155 let mut params = Vec::new();
1156 self.skip_newlines();
1157
1158 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1159 params.push(self.consume_identifier("parameter name")?);
1160 if self.check(&TokenKind::Comma) {
1161 self.advance();
1162 self.skip_newlines();
1163 }
1164 }
1165 Ok(params)
1166 }
1167
1168 pub(super) fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1170 self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1171 }
1172
1173 pub(super) fn parse_typed_params_until(
1176 &mut self,
1177 is_terminator: impl Fn(&TokenKind) -> bool,
1178 ) -> Result<Vec<TypedParam>, ParserError> {
1179 let mut params = Vec::new();
1180 let mut seen_default = false;
1181 self.skip_newlines();
1182
1183 while !self.is_at_end() {
1184 if let Some(tok) = self.current() {
1185 if is_terminator(&tok.kind) {
1186 break;
1187 }
1188 } else {
1189 break;
1190 }
1191 let is_rest = if self.check(&TokenKind::Dot) {
1192 let p1 = self.pos + 1;
1193 let p2 = self.pos + 2;
1194 let is_ellipsis = p1 < self.tokens.len()
1195 && p2 < self.tokens.len()
1196 && self.tokens[p1].kind == TokenKind::Dot
1197 && self.tokens[p2].kind == TokenKind::Dot;
1198 if is_ellipsis {
1199 self.advance();
1200 self.advance();
1201 self.advance();
1202 true
1203 } else {
1204 false
1205 }
1206 } else {
1207 false
1208 };
1209 let name = self.consume_identifier("parameter name")?;
1210 let type_expr = self.try_parse_type_annotation()?;
1211 let default_value = if self.check(&TokenKind::Assign) {
1212 self.advance();
1213 seen_default = true;
1214 Some(Box::new(self.parse_nested_expression("parameter default")?))
1215 } else {
1216 if seen_default && !is_rest {
1217 return Err(self.error(
1218 "Required parameter cannot follow a parameter with a default value",
1219 ));
1220 }
1221 None
1222 };
1223 if is_rest
1224 && !is_terminator(
1225 &self
1226 .current()
1227 .map(|t| t.kind.clone())
1228 .unwrap_or(TokenKind::Eof),
1229 )
1230 {
1231 return Err(self.error("Rest parameter must be the last parameter"));
1232 }
1233 params.push(TypedParam {
1234 name,
1235 type_expr,
1236 default_value,
1237 rest: is_rest,
1238 });
1239 if self.check(&TokenKind::Comma) {
1240 self.advance();
1241 self.skip_newlines();
1242 }
1243 }
1244 Ok(params)
1245 }
1246
1247 pub(super) fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
1248 let mut args = Vec::new();
1249 self.skip_newlines();
1250
1251 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1252 if self.check(&TokenKind::Dot) {
1253 let saved_pos = self.pos;
1254 self.advance();
1255 if self.check(&TokenKind::Dot) {
1256 self.advance();
1257 self.consume(&TokenKind::Dot, ".")?;
1258 let spread_start = self.tokens[saved_pos].span;
1259 let expr = self.parse_nested_expression("spread argument")?;
1260 args.push(spanned(
1261 Node::Spread(Box::new(expr)),
1262 Span::merge(spread_start, self.prev_span()),
1263 ));
1264 } else {
1265 self.pos = saved_pos;
1266 args.push(self.parse_nested_expression("argument")?);
1267 }
1268 } else {
1269 args.push(self.parse_nested_expression("argument")?);
1270 }
1271 self.skip_newlines();
1272 if self.check(&TokenKind::Comma) {
1273 self.advance();
1274 self.skip_newlines();
1275 }
1276 }
1277 Ok(args)
1278 }
1279}