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 if optional && self.check(&TokenKind::LBracket) {
378 self.advance();
379 let index = self.parse_nested_expression("optional subscript index")?;
380 self.consume(&TokenKind::RBracket, "]")?;
381 expr = spanned(
382 Node::OptionalSubscriptAccess {
383 object: Box::new(expr),
384 index: Box::new(index),
385 },
386 Span::merge(start, self.prev_span()),
387 );
388 continue;
389 }
390 let member = self.consume_identifier_or_keyword("member name")?;
391 if self.check(&TokenKind::LParen) {
392 self.advance();
393 let args = self.parse_arg_list()?;
394 self.consume(&TokenKind::RParen, ")")?;
395 if optional {
396 expr = spanned(
397 Node::OptionalMethodCall {
398 object: Box::new(expr),
399 method: member,
400 args,
401 },
402 Span::merge(start, self.prev_span()),
403 );
404 } else {
405 expr = spanned(
406 Node::MethodCall {
407 object: Box::new(expr),
408 method: member,
409 args,
410 },
411 Span::merge(start, self.prev_span()),
412 );
413 }
414 } else if optional {
415 expr = spanned(
416 Node::OptionalPropertyAccess {
417 object: Box::new(expr),
418 property: member,
419 },
420 Span::merge(start, self.prev_span()),
421 );
422 } else {
423 expr = spanned(
424 Node::PropertyAccess {
425 object: Box::new(expr),
426 property: member,
427 },
428 Span::merge(start, self.prev_span()),
429 );
430 }
431 } else if self.check(&TokenKind::LBracket) {
432 let start = expr.span;
433 self.advance();
434
435 if self.check(&TokenKind::Colon) {
438 self.advance();
439 let end_expr = if self.check(&TokenKind::RBracket) {
440 None
441 } else {
442 Some(Box::new(self.parse_nested_expression("slice bound")?))
443 };
444 self.consume(&TokenKind::RBracket, "]")?;
445 expr = spanned(
446 Node::SliceAccess {
447 object: Box::new(expr),
448 start: None,
449 end: end_expr,
450 },
451 Span::merge(start, self.prev_span()),
452 );
453 } else {
454 let index = self.parse_nested_expression("subscript index")?;
455 if self.check(&TokenKind::Colon) {
456 self.advance();
457 let end_expr = if self.check(&TokenKind::RBracket) {
458 None
459 } else {
460 Some(Box::new(self.parse_nested_expression("slice bound")?))
461 };
462 self.consume(&TokenKind::RBracket, "]")?;
463 expr = spanned(
464 Node::SliceAccess {
465 object: Box::new(expr),
466 start: Some(Box::new(index)),
467 end: end_expr,
468 },
469 Span::merge(start, self.prev_span()),
470 );
471 } else {
472 self.consume(&TokenKind::RBracket, "]")?;
473 expr = spanned(
474 Node::SubscriptAccess {
475 object: Box::new(expr),
476 index: Box::new(index),
477 },
478 Span::merge(start, self.prev_span()),
479 );
480 }
481 }
482 } else if self.check(&TokenKind::LBrace) {
483 let struct_name = match &expr.node {
484 Node::Identifier(name) if self.is_struct_construct_lookahead(name) => {
485 Some(name.clone())
486 }
487 _ => None,
488 };
489 let Some(struct_name) = struct_name else {
490 break;
491 };
492 let start = expr.span;
493 self.advance();
494 let dict = self.parse_dict_literal(start)?;
495 let fields = match dict.node {
496 Node::DictLiteral(fields) => fields,
497 _ => unreachable!("dict parser must return a dict literal"),
498 };
499 expr = spanned(
500 Node::StructConstruct {
501 struct_name,
502 fields,
503 },
504 dict.span,
505 );
506 } else if self.check(&TokenKind::Lt) && matches!(expr.node, Node::Identifier(_)) {
507 let saved_pos = self.pos;
508 let start = expr.span;
509 self.advance();
510 let parsed_type_args = self.parse_type_arg_list();
511 if let Ok(type_args) = parsed_type_args {
512 if self.check(&TokenKind::LParen) {
513 self.advance();
514 let args = self.parse_arg_list()?;
515 self.consume(&TokenKind::RParen, ")")?;
516 if let Node::Identifier(name) = expr.node {
517 expr = spanned(
518 Node::FunctionCall {
519 name,
520 type_args,
521 args,
522 },
523 Span::merge(start, self.prev_span()),
524 );
525 }
526 } else {
527 self.pos = saved_pos;
528 break;
529 }
530 } else {
531 self.pos = saved_pos;
532 break;
533 }
534 } else if self.check(&TokenKind::LParen) {
535 let start = expr.span;
536 self.advance();
537 let args = self.parse_arg_list()?;
538 self.consume(&TokenKind::RParen, ")")?;
539 if let Node::Identifier(name) = &expr.node {
540 expr = spanned(
541 Node::FunctionCall {
542 name: name.clone(),
543 type_args: Vec::new(),
544 args,
545 },
546 Span::merge(start, self.prev_span()),
547 );
548 } else {
549 expr = spanned(
550 Node::ValueCall {
551 callee: Box::new(expr),
552 args,
553 },
554 Span::merge(start, self.prev_span()),
555 );
556 }
557 } else if self.check(&TokenKind::Question) {
558 if self.question_starts_ternary_branch() {
561 break;
562 }
563 if matches!(self.peek_kind_at(1), Some(TokenKind::LBracket)) {
564 let start = expr.span;
565 self.advance(); self.advance(); let index = self.parse_nested_expression("optional subscript index")?;
568 self.consume(&TokenKind::RBracket, "]")?;
569 expr = spanned(
570 Node::OptionalSubscriptAccess {
571 object: Box::new(expr),
572 index: Box::new(index),
573 },
574 Span::merge(start, self.prev_span()),
575 );
576 continue;
577 }
578 let start = expr.span;
579 self.advance();
580 expr = spanned(
581 Node::TryOperator {
582 operand: Box::new(expr),
583 },
584 Span::merge(start, self.prev_span()),
585 );
586 } else if self.check(&TokenKind::Not) {
587 let start = expr.span;
592 self.advance();
593 expr = spanned(
594 Node::NonNullAssert {
595 operand: Box::new(expr),
596 },
597 Span::merge(start, self.prev_span()),
598 );
599 } else {
600 break;
601 }
602 }
603
604 Ok(expr)
605 }
606
607 fn question_starts_ternary_branch(&self) -> bool {
608 let next = self
613 .tokens
614 .iter()
615 .skip(self.pos + 1)
616 .find(|t| t.kind != TokenKind::Newline)
617 .map(|t| &t.kind);
618 next.is_some_and(Self::token_starts_ternary_branch)
619 && self.question_has_top_level_ternary_colon()
620 }
621
622 fn token_starts_ternary_branch(kind: &TokenKind) -> bool {
623 matches!(
624 kind,
625 TokenKind::Identifier(_)
626 | TokenKind::IntLiteral(_)
627 | TokenKind::FloatLiteral(_)
628 | TokenKind::StringLiteral(_)
629 | TokenKind::RawStringLiteral(_)
630 | TokenKind::InterpolatedString(_)
631 | TokenKind::True
632 | TokenKind::False
633 | TokenKind::Nil
634 | TokenKind::LParen
635 | TokenKind::LBracket
636 | TokenKind::LBrace
637 | TokenKind::Not
638 | TokenKind::Minus
639 | TokenKind::Fn
640 | TokenKind::If
641 | TokenKind::Match
642 | TokenKind::Try
643 | TokenKind::Spawn
644 | TokenKind::Parallel
645 | TokenKind::Retry
646 | TokenKind::Deadline
647 | TokenKind::RequestApproval
648 | TokenKind::DualControl
649 | TokenKind::AskUser
650 | TokenKind::EscalateTo
651 | TokenKind::DurationLiteral(_)
652 )
653 }
654
655 fn question_has_top_level_ternary_colon(&self) -> bool {
656 let mut delimiter_depth = 0usize;
657 let mut at_branch_start = true;
661 for (pos, token) in self.tokens.iter().enumerate().skip(self.pos + 1) {
662 if delimiter_depth == 0 {
663 match token.kind {
664 TokenKind::Colon => return true,
665 TokenKind::Newline => {
666 if at_branch_start {
667 continue;
670 }
671 if self.next_non_newline_continues_ternary_branch(pos + 1) {
672 continue;
673 }
674 return false;
675 }
676 TokenKind::RParen
677 | TokenKind::RBracket
678 | TokenKind::RBrace
679 | TokenKind::Eof => {
680 return false;
681 }
682 _ => {
683 at_branch_start = false;
684 }
685 }
686 }
687
688 match token.kind {
689 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => {
690 delimiter_depth += 1;
691 }
692 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
693 delimiter_depth = delimiter_depth.saturating_sub(1);
694 }
695 TokenKind::Eof => return false,
696 _ => {}
697 }
698 }
699 false
700 }
701
702 fn next_non_newline_continues_ternary_branch(&self, start_pos: usize) -> bool {
703 let Some(kind) = self
704 .tokens
705 .iter()
706 .skip(start_pos)
707 .find(|token| token.kind != TokenKind::Newline)
708 .map(|token| &token.kind)
709 else {
710 return false;
711 };
712 matches!(
713 kind,
714 TokenKind::Colon
715 | TokenKind::Plus
716 | TokenKind::Star
717 | TokenKind::Slash
718 | TokenKind::Percent
719 | TokenKind::Pow
720 | TokenKind::And
721 | TokenKind::Or
722 | TokenKind::Eq
723 | TokenKind::Neq
724 | TokenKind::Lt
725 | TokenKind::Gt
726 | TokenKind::Lte
727 | TokenKind::Gte
728 | TokenKind::NilCoal
729 | TokenKind::Pipe
730 | TokenKind::Dot
731 | TokenKind::QuestionDot
732 )
733 }
734
735 pub(super) fn parse_primary(&mut self) -> Result<SNode, ParserError> {
736 let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
737 expected: "expression".into(),
738 span: self.prev_span(),
739 })?;
740 let start = self.current_span();
741
742 match &tok.kind {
743 TokenKind::StringLiteral(s) => {
744 let s = s.clone();
745 self.advance();
746 Ok(spanned(
747 Node::StringLiteral(s),
748 Span::merge(start, self.prev_span()),
749 ))
750 }
751 TokenKind::RawStringLiteral(s) => {
752 let s = s.clone();
753 self.advance();
754 Ok(spanned(
755 Node::RawStringLiteral(s),
756 Span::merge(start, self.prev_span()),
757 ))
758 }
759 TokenKind::InterpolatedString(segments) => {
760 let segments = segments.clone();
761 self.advance();
762 Ok(spanned(
763 Node::InterpolatedString(segments),
764 Span::merge(start, self.prev_span()),
765 ))
766 }
767 TokenKind::IntLiteral(n) => {
768 let n = *n;
769 self.advance();
770 Ok(spanned(
771 Node::IntLiteral(n),
772 Span::merge(start, self.prev_span()),
773 ))
774 }
775 TokenKind::FloatLiteral(n) => {
776 let n = *n;
777 self.advance();
778 Ok(spanned(
779 Node::FloatLiteral(n),
780 Span::merge(start, self.prev_span()),
781 ))
782 }
783 TokenKind::True => {
784 self.advance();
785 Ok(spanned(
786 Node::BoolLiteral(true),
787 Span::merge(start, self.prev_span()),
788 ))
789 }
790 TokenKind::False => {
791 self.advance();
792 Ok(spanned(
793 Node::BoolLiteral(false),
794 Span::merge(start, self.prev_span()),
795 ))
796 }
797 TokenKind::Nil => {
798 self.advance();
799 Ok(spanned(
800 Node::NilLiteral,
801 Span::merge(start, self.prev_span()),
802 ))
803 }
804 TokenKind::Identifier(name)
805 if name == "cost_route" && self.peek_kind() == Some(&TokenKind::LBrace) =>
806 {
807 self.parse_cost_route()
808 }
809 TokenKind::Identifier(name) => {
810 let name = name.clone();
811 self.advance();
812 Ok(spanned(
813 Node::Identifier(name),
814 Span::merge(start, self.prev_span()),
815 ))
816 }
817 TokenKind::LParen => {
818 self.advance();
819 let expr = self.with_nesting("parenthesized expression", |parser| {
820 let expr = parser.parse_expression()?;
821 parser.consume(&TokenKind::RParen, ")")?;
822 Ok(expr)
823 })?;
824 Ok(expr)
825 }
826 TokenKind::LBracket => self.parse_list_literal(),
827 TokenKind::LBrace => self.parse_dict_or_closure(),
828 TokenKind::Parallel => self.parse_parallel(),
829 TokenKind::Retry => self.parse_retry(),
830 TokenKind::If => self.parse_if_else(),
831 TokenKind::Spawn => self.parse_spawn_expr(),
832 TokenKind::RequestApproval => self.parse_hitl_expr(HitlKind::RequestApproval),
833 TokenKind::DualControl => self.parse_hitl_expr(HitlKind::DualControl),
834 TokenKind::AskUser => self.parse_hitl_expr(HitlKind::AskUser),
835 TokenKind::EscalateTo => self.parse_hitl_expr(HitlKind::EscalateTo),
836 TokenKind::DurationLiteral(ms) => {
837 let ms = *ms;
838 self.advance();
839 Ok(spanned(
840 Node::DurationLiteral(ms),
841 Span::merge(start, self.prev_span()),
842 ))
843 }
844 TokenKind::Deadline => self.parse_deadline(),
845 TokenKind::Try => self.parse_try_catch(),
846 TokenKind::Match => self.parse_match(),
847 TokenKind::Fn => self.parse_fn_expr(),
848 TokenKind::Lt
851 if matches!(self.peek_kind(), Some(&TokenKind::Lt))
852 && matches!(self.peek_kind_at(2), Some(TokenKind::Identifier(_))) =>
853 {
854 Err(ParserError::Unexpected {
855 got: "`<<` heredoc-like syntax".to_string(),
856 expected: "an expression — heredocs are only valid \
857 inside LLM tool-call argument JSON; \
858 for multiline strings in source code use \
859 triple-quoted `\"\"\"...\"\"\"`"
860 .to_string(),
861 span: start,
862 })
863 }
864 _ => Err(self.error("expression")),
865 }
866 }
867
868 pub(super) fn parse_fn_expr(&mut self) -> Result<SNode, ParserError> {
871 let start = self.current_span();
872 self.consume(&TokenKind::Fn, "fn")?;
873 self.consume(&TokenKind::LParen, "(")?;
874 let params = self.parse_typed_param_list()?;
875 self.consume(&TokenKind::RParen, ")")?;
876 let return_type = if self.check(&TokenKind::Arrow) {
877 self.advance();
878 Some(self.parse_nested_type_expr("closure return type")?)
879 } else {
880 None
881 };
882 let throws = if self.check(&TokenKind::Throws) {
885 self.advance();
886 Some(self.parse_nested_type_expr("closure throws type")?)
887 } else {
888 None
889 };
890 self.consume(&TokenKind::LBrace, "{")?;
891 let body = self.parse_block()?;
892 self.consume(&TokenKind::RBrace, "}")?;
893 Ok(spanned(
894 Node::Closure {
895 params,
896 return_type,
897 throws,
898 body,
899 fn_syntax: true,
900 },
901 Span::merge(start, self.prev_span()),
902 ))
903 }
904
905 pub(super) fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
906 let start = self.current_span();
907 self.consume(&TokenKind::Spawn, "spawn")?;
908 self.consume(&TokenKind::LBrace, "{")?;
909 let body = self.parse_block()?;
910 self.consume(&TokenKind::RBrace, "}")?;
911 Ok(spanned(
912 Node::SpawnExpr { body },
913 Span::merge(start, self.prev_span()),
914 ))
915 }
916
917 pub(super) fn parse_hitl_expr(&mut self, kind: HitlKind) -> Result<SNode, ParserError> {
929 let start = self.current_span();
930 let kw_token = match kind {
931 HitlKind::RequestApproval => TokenKind::RequestApproval,
932 HitlKind::DualControl => TokenKind::DualControl,
933 HitlKind::AskUser => TokenKind::AskUser,
934 HitlKind::EscalateTo => TokenKind::EscalateTo,
935 };
936 self.consume(&kw_token, kind.as_keyword())?;
937 self.consume(&TokenKind::LParen, "(")?;
938 self.skip_newlines();
939
940 let mut args: Vec<HitlArg> = Vec::new();
941 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
942 let arg_start = self.current_span();
943 let is_named = matches!(
950 (self.peek_kind_at(0), self.peek_kind_at(1)),
951 (Some(TokenKind::Identifier(_)), Some(TokenKind::Colon))
952 );
953 let (name, value) = if is_named {
954 let Some(TokenKind::Identifier(raw)) = self.peek_kind_at(0).cloned() else {
955 unreachable!("named arg dispatch already matched Identifier token")
956 };
957 self.advance();
958 self.consume(&TokenKind::Colon, ":")?;
959 self.skip_newlines();
960 let value = self.parse_nested_expression("HITL argument")?;
961 (Some(raw), value)
962 } else {
963 (None, self.parse_nested_expression("HITL argument")?)
964 };
965 let arg_span = Span::merge(arg_start, self.prev_span());
966 args.push(HitlArg {
967 name,
968 value,
969 span: arg_span,
970 });
971 self.skip_newlines();
972 if self.check(&TokenKind::Comma) {
973 self.advance();
974 self.skip_newlines();
975 } else {
976 break;
977 }
978 }
979
980 self.skip_newlines();
981 self.consume(&TokenKind::RParen, ")")?;
982 Ok(spanned(
983 Node::HitlExpr { kind, args },
984 Span::merge(start, self.prev_span()),
985 ))
986 }
987
988 pub(super) fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
989 let start = self.current_span();
990 self.consume(&TokenKind::LBracket, "[")?;
991 let mut elements = Vec::new();
992 self.skip_newlines();
993
994 while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
995 if self.check(&TokenKind::Dot) {
996 let saved_pos = self.pos;
997 self.advance();
998 if self.check(&TokenKind::Dot) {
999 self.advance();
1000 self.consume(&TokenKind::Dot, ".")?;
1001 let spread_start = self.tokens[saved_pos].span;
1002 let expr = self.parse_nested_expression("list spread")?;
1003 elements.push(spanned(
1004 Node::Spread(Box::new(expr)),
1005 Span::merge(spread_start, self.prev_span()),
1006 ));
1007 } else {
1008 self.pos = saved_pos;
1009 elements.push(self.parse_nested_expression("list element")?);
1010 }
1011 } else {
1012 elements.push(self.parse_nested_expression("list element")?);
1013 }
1014 self.skip_newlines();
1015 if self.check(&TokenKind::Comma) {
1016 self.advance();
1017 self.skip_newlines();
1018 }
1019 }
1020
1021 self.consume(&TokenKind::RBracket, "]")?;
1022 Ok(spanned(
1023 Node::ListLiteral(elements),
1024 Span::merge(start, self.prev_span()),
1025 ))
1026 }
1027
1028 pub(super) fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
1029 let start = self.current_span();
1030 self.consume(&TokenKind::LBrace, "{")?;
1031 self.skip_newlines();
1032
1033 if self.check(&TokenKind::RBrace) {
1034 self.advance();
1035 return Ok(spanned(
1036 Node::DictLiteral(Vec::new()),
1037 Span::merge(start, self.prev_span()),
1038 ));
1039 }
1040
1041 let saved = self.pos;
1043 if self.is_closure_lookahead() {
1044 self.pos = saved;
1045 return self.parse_closure_body(start);
1046 }
1047 self.pos = saved;
1048 self.parse_dict_literal(start)
1049 }
1050
1051 pub(super) fn is_struct_construct_lookahead(&self, struct_name: &str) -> bool {
1055 if !struct_name
1056 .chars()
1057 .next()
1058 .is_some_and(|ch| ch.is_uppercase())
1059 {
1060 return false;
1061 }
1062
1063 let mut offset = 1;
1064 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
1065 offset += 1;
1066 }
1067
1068 match self.peek_kind_at(offset) {
1069 Some(TokenKind::RBrace) => true,
1070 Some(TokenKind::Identifier(_)) | Some(TokenKind::StringLiteral(_)) => {
1071 offset += 1;
1072 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
1073 offset += 1;
1074 }
1075 matches!(self.peek_kind_at(offset), Some(TokenKind::Colon))
1076 }
1077 _ => false,
1078 }
1079 }
1080
1081 pub(super) fn is_closure_lookahead(&mut self) -> bool {
1083 let mut depth = 0;
1084 while !self.is_at_end() {
1085 if let Some(tok) = self.current() {
1086 match &tok.kind {
1087 TokenKind::Arrow if depth == 0 => return true,
1088 TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
1089 TokenKind::RBrace if depth == 0 => return false,
1090 TokenKind::RBrace => depth -= 1,
1091 TokenKind::RParen | TokenKind::RBracket if depth > 0 => depth -= 1,
1092 _ => {}
1093 }
1094 self.advance();
1095 } else {
1096 return false;
1097 }
1098 }
1099 false
1100 }
1101
1102 pub(super) fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
1104 let params = self.parse_typed_param_list_until_arrow()?;
1105 self.consume(&TokenKind::Arrow, "->")?;
1106 let body = self.parse_block()?;
1107 self.consume(&TokenKind::RBrace, "}")?;
1108 Ok(spanned(
1109 Node::Closure {
1110 params,
1111 return_type: None,
1112 throws: None,
1115 body,
1116 fn_syntax: false,
1117 },
1118 Span::merge(start, self.prev_span()),
1119 ))
1120 }
1121
1122 pub(super) fn parse_typed_param_list_until_arrow(
1124 &mut self,
1125 ) -> Result<Vec<TypedParam>, ParserError> {
1126 self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1127 }
1128
1129 pub(super) fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1130 let entries = self.parse_dict_entries()?;
1131 Ok(spanned(
1132 Node::DictLiteral(entries),
1133 Span::merge(start, self.prev_span()),
1134 ))
1135 }
1136
1137 pub(super) fn parse_dict_entries(&mut self) -> Result<Vec<DictEntry>, ParserError> {
1138 let mut entries = Vec::new();
1139 self.skip_newlines();
1140
1141 while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1142 if self.check(&TokenKind::Dot) {
1143 let saved_pos = self.pos;
1144 self.advance();
1145 if self.check(&TokenKind::Dot) {
1146 self.advance();
1147 if self.check(&TokenKind::Dot) {
1148 self.advance();
1149 let spread_start = self.tokens[saved_pos].span;
1150 let expr = self.parse_nested_expression("dict spread")?;
1151 entries.push(DictEntry {
1152 key: spanned(Node::NilLiteral, spread_start),
1153 value: spanned(
1154 Node::Spread(Box::new(expr)),
1155 Span::merge(spread_start, self.prev_span()),
1156 ),
1157 });
1158 self.skip_newlines();
1159 if self.check(&TokenKind::Comma) {
1160 self.advance();
1161 self.skip_newlines();
1162 }
1163 continue;
1164 }
1165 self.pos = saved_pos;
1166 } else {
1167 self.pos = saved_pos;
1168 }
1169 }
1170 let key = if self.check(&TokenKind::LBracket) {
1171 self.advance();
1172 let k = self.parse_nested_expression("computed dict key")?;
1173 self.consume(&TokenKind::RBracket, "]")?;
1174 k
1175 } else if matches!(
1176 self.current().map(|t| &t.kind),
1177 Some(TokenKind::StringLiteral(_))
1178 ) {
1179 let key_span = self.current_span();
1180 let name =
1181 if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1182 s.clone()
1183 } else {
1184 unreachable!()
1185 };
1186 self.advance();
1187 spanned(Node::StringLiteral(name), key_span)
1188 } else {
1189 let key_span = self.current_span();
1190 let name = self.consume_identifier_or_keyword("dict key")?;
1191 spanned(Node::StringLiteral(name), key_span)
1192 };
1193 self.consume(&TokenKind::Colon, ":")?;
1194 let value = self.parse_nested_expression("dict value")?;
1195 entries.push(DictEntry { key, value });
1196 self.skip_newlines();
1197 if self.check(&TokenKind::Comma) {
1198 self.advance();
1199 self.skip_newlines();
1200 }
1201 }
1202
1203 self.consume(&TokenKind::RBrace, "}")?;
1204 Ok(entries)
1205 }
1206
1207 pub(super) fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1209 let mut params = Vec::new();
1210 self.skip_newlines();
1211
1212 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1213 params.push(self.consume_identifier("parameter name")?);
1214 if self.check(&TokenKind::Comma) {
1215 self.advance();
1216 self.skip_newlines();
1217 }
1218 }
1219 Ok(params)
1220 }
1221
1222 pub(super) fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1224 self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1225 }
1226
1227 pub(super) fn parse_typed_params_until(
1230 &mut self,
1231 is_terminator: impl Fn(&TokenKind) -> bool,
1232 ) -> Result<Vec<TypedParam>, ParserError> {
1233 let mut params = Vec::new();
1234 let mut seen_default = false;
1235 self.skip_newlines();
1236
1237 while !self.is_at_end() {
1238 if let Some(tok) = self.current() {
1239 if is_terminator(&tok.kind) {
1240 break;
1241 }
1242 } else {
1243 break;
1244 }
1245 let is_rest = if self.check(&TokenKind::Dot) {
1246 let p1 = self.pos + 1;
1247 let p2 = self.pos + 2;
1248 let is_ellipsis = p1 < self.tokens.len()
1249 && p2 < self.tokens.len()
1250 && self.tokens[p1].kind == TokenKind::Dot
1251 && self.tokens[p2].kind == TokenKind::Dot;
1252 if is_ellipsis {
1253 self.advance();
1254 self.advance();
1255 self.advance();
1256 true
1257 } else {
1258 false
1259 }
1260 } else {
1261 false
1262 };
1263 let name = self.consume_identifier("parameter name")?;
1264 let type_expr = self.try_parse_type_annotation()?;
1265 let default_value = if self.check(&TokenKind::Assign) {
1266 self.advance();
1267 seen_default = true;
1268 Some(Box::new(self.parse_nested_expression("parameter default")?))
1269 } else {
1270 if seen_default && !is_rest {
1271 return Err(self.error(
1272 "Required parameter cannot follow a parameter with a default value",
1273 ));
1274 }
1275 None
1276 };
1277 if is_rest
1278 && !is_terminator(
1279 &self
1280 .current()
1281 .map(|t| t.kind.clone())
1282 .unwrap_or(TokenKind::Eof),
1283 )
1284 {
1285 return Err(self.error("Rest parameter must be the last parameter"));
1286 }
1287 params.push(TypedParam {
1288 name,
1289 type_expr,
1290 default_value,
1291 rest: is_rest,
1292 });
1293 if self.check(&TokenKind::Comma) {
1294 self.advance();
1295 self.skip_newlines();
1296 }
1297 }
1298 Ok(params)
1299 }
1300
1301 pub(super) fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
1302 let mut args = Vec::new();
1303 self.skip_newlines();
1304
1305 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1306 if self.check(&TokenKind::Dot) {
1307 let saved_pos = self.pos;
1308 self.advance();
1309 if self.check(&TokenKind::Dot) {
1310 self.advance();
1311 self.consume(&TokenKind::Dot, ".")?;
1312 let spread_start = self.tokens[saved_pos].span;
1313 let expr = self.parse_nested_expression("spread argument")?;
1314 args.push(spanned(
1315 Node::Spread(Box::new(expr)),
1316 Span::merge(spread_start, self.prev_span()),
1317 ));
1318 } else {
1319 self.pos = saved_pos;
1320 args.push(self.parse_nested_expression("argument")?);
1321 }
1322 } else {
1323 args.push(self.parse_nested_expression("argument")?);
1324 }
1325 self.skip_newlines();
1326 if self.check(&TokenKind::Comma) {
1327 self.advance();
1328 self.skip_newlines();
1329 }
1330 }
1331 Ok(args)
1332 }
1333}