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