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) && matches!(expr.node, Node::Identifier(_)) {
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,
543 type_args: Vec::new(),
544 args,
545 },
546 Span::merge(start, self.prev_span()),
547 );
548 }
549 } else if self.check(&TokenKind::Question) {
550 if self.question_starts_ternary_branch() {
553 break;
554 }
555 if matches!(self.peek_kind_at(1), Some(TokenKind::LBracket)) {
556 let start = expr.span;
557 self.advance(); self.advance(); let index = self.parse_nested_expression("optional subscript index")?;
560 self.consume(&TokenKind::RBracket, "]")?;
561 expr = spanned(
562 Node::OptionalSubscriptAccess {
563 object: Box::new(expr),
564 index: Box::new(index),
565 },
566 Span::merge(start, self.prev_span()),
567 );
568 continue;
569 }
570 let start = expr.span;
571 self.advance();
572 expr = spanned(
573 Node::TryOperator {
574 operand: Box::new(expr),
575 },
576 Span::merge(start, self.prev_span()),
577 );
578 } else {
579 break;
580 }
581 }
582
583 Ok(expr)
584 }
585
586 fn question_starts_ternary_branch(&self) -> bool {
587 let next = self
592 .tokens
593 .iter()
594 .skip(self.pos + 1)
595 .find(|t| t.kind != TokenKind::Newline)
596 .map(|t| &t.kind);
597 next.is_some_and(Self::token_starts_ternary_branch)
598 && self.question_has_top_level_ternary_colon()
599 }
600
601 fn token_starts_ternary_branch(kind: &TokenKind) -> bool {
602 matches!(
603 kind,
604 TokenKind::Identifier(_)
605 | TokenKind::IntLiteral(_)
606 | TokenKind::FloatLiteral(_)
607 | TokenKind::StringLiteral(_)
608 | TokenKind::RawStringLiteral(_)
609 | TokenKind::InterpolatedString(_)
610 | TokenKind::True
611 | TokenKind::False
612 | TokenKind::Nil
613 | TokenKind::LParen
614 | TokenKind::LBracket
615 | TokenKind::LBrace
616 | TokenKind::Not
617 | TokenKind::Minus
618 | TokenKind::Fn
619 | TokenKind::If
620 | TokenKind::Match
621 | TokenKind::Try
622 | TokenKind::Spawn
623 | TokenKind::Parallel
624 | TokenKind::Retry
625 | TokenKind::Deadline
626 | TokenKind::RequestApproval
627 | TokenKind::DualControl
628 | TokenKind::AskUser
629 | TokenKind::EscalateTo
630 | TokenKind::DurationLiteral(_)
631 )
632 }
633
634 fn question_has_top_level_ternary_colon(&self) -> bool {
635 let mut delimiter_depth = 0usize;
636 let mut at_branch_start = true;
640 for (pos, token) in self.tokens.iter().enumerate().skip(self.pos + 1) {
641 if delimiter_depth == 0 {
642 match token.kind {
643 TokenKind::Colon => return true,
644 TokenKind::Newline => {
645 if at_branch_start {
646 continue;
649 }
650 if self.next_non_newline_continues_ternary_branch(pos + 1) {
651 continue;
652 }
653 return false;
654 }
655 TokenKind::RParen
656 | TokenKind::RBracket
657 | TokenKind::RBrace
658 | TokenKind::Eof => {
659 return false;
660 }
661 _ => {
662 at_branch_start = false;
663 }
664 }
665 }
666
667 match token.kind {
668 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => {
669 delimiter_depth += 1;
670 }
671 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
672 delimiter_depth = delimiter_depth.saturating_sub(1);
673 }
674 TokenKind::Eof => return false,
675 _ => {}
676 }
677 }
678 false
679 }
680
681 fn next_non_newline_continues_ternary_branch(&self, start_pos: usize) -> bool {
682 let Some(kind) = self
683 .tokens
684 .iter()
685 .skip(start_pos)
686 .find(|token| token.kind != TokenKind::Newline)
687 .map(|token| &token.kind)
688 else {
689 return false;
690 };
691 matches!(
692 kind,
693 TokenKind::Colon
694 | TokenKind::Plus
695 | TokenKind::Star
696 | TokenKind::Slash
697 | TokenKind::Percent
698 | TokenKind::Pow
699 | TokenKind::And
700 | TokenKind::Or
701 | TokenKind::Eq
702 | TokenKind::Neq
703 | TokenKind::Lt
704 | TokenKind::Gt
705 | TokenKind::Lte
706 | TokenKind::Gte
707 | TokenKind::NilCoal
708 | TokenKind::Pipe
709 | TokenKind::Dot
710 | TokenKind::QuestionDot
711 )
712 }
713
714 pub(super) fn parse_primary(&mut self) -> Result<SNode, ParserError> {
715 let tok = self.current().ok_or_else(|| ParserError::UnexpectedEof {
716 expected: "expression".into(),
717 span: self.prev_span(),
718 })?;
719 let start = self.current_span();
720
721 match &tok.kind {
722 TokenKind::StringLiteral(s) => {
723 let s = s.clone();
724 self.advance();
725 Ok(spanned(
726 Node::StringLiteral(s),
727 Span::merge(start, self.prev_span()),
728 ))
729 }
730 TokenKind::RawStringLiteral(s) => {
731 let s = s.clone();
732 self.advance();
733 Ok(spanned(
734 Node::RawStringLiteral(s),
735 Span::merge(start, self.prev_span()),
736 ))
737 }
738 TokenKind::InterpolatedString(segments) => {
739 let segments = segments.clone();
740 self.advance();
741 Ok(spanned(
742 Node::InterpolatedString(segments),
743 Span::merge(start, self.prev_span()),
744 ))
745 }
746 TokenKind::IntLiteral(n) => {
747 let n = *n;
748 self.advance();
749 Ok(spanned(
750 Node::IntLiteral(n),
751 Span::merge(start, self.prev_span()),
752 ))
753 }
754 TokenKind::FloatLiteral(n) => {
755 let n = *n;
756 self.advance();
757 Ok(spanned(
758 Node::FloatLiteral(n),
759 Span::merge(start, self.prev_span()),
760 ))
761 }
762 TokenKind::True => {
763 self.advance();
764 Ok(spanned(
765 Node::BoolLiteral(true),
766 Span::merge(start, self.prev_span()),
767 ))
768 }
769 TokenKind::False => {
770 self.advance();
771 Ok(spanned(
772 Node::BoolLiteral(false),
773 Span::merge(start, self.prev_span()),
774 ))
775 }
776 TokenKind::Nil => {
777 self.advance();
778 Ok(spanned(
779 Node::NilLiteral,
780 Span::merge(start, self.prev_span()),
781 ))
782 }
783 TokenKind::Identifier(name)
784 if name == "cost_route" && self.peek_kind() == Some(&TokenKind::LBrace) =>
785 {
786 self.parse_cost_route()
787 }
788 TokenKind::Identifier(name) => {
789 let name = name.clone();
790 self.advance();
791 Ok(spanned(
792 Node::Identifier(name),
793 Span::merge(start, self.prev_span()),
794 ))
795 }
796 TokenKind::LParen => {
797 self.advance();
798 let expr = self.with_nesting("parenthesized expression", |parser| {
799 let expr = parser.parse_expression()?;
800 parser.consume(&TokenKind::RParen, ")")?;
801 Ok(expr)
802 })?;
803 Ok(expr)
804 }
805 TokenKind::LBracket => self.parse_list_literal(),
806 TokenKind::LBrace => self.parse_dict_or_closure(),
807 TokenKind::Parallel => self.parse_parallel(),
808 TokenKind::Retry => self.parse_retry(),
809 TokenKind::If => self.parse_if_else(),
810 TokenKind::Spawn => self.parse_spawn_expr(),
811 TokenKind::RequestApproval => self.parse_hitl_expr(HitlKind::RequestApproval),
812 TokenKind::DualControl => self.parse_hitl_expr(HitlKind::DualControl),
813 TokenKind::AskUser => self.parse_hitl_expr(HitlKind::AskUser),
814 TokenKind::EscalateTo => self.parse_hitl_expr(HitlKind::EscalateTo),
815 TokenKind::DurationLiteral(ms) => {
816 let ms = *ms;
817 self.advance();
818 Ok(spanned(
819 Node::DurationLiteral(ms),
820 Span::merge(start, self.prev_span()),
821 ))
822 }
823 TokenKind::Deadline => self.parse_deadline(),
824 TokenKind::Try => self.parse_try_catch(),
825 TokenKind::Match => self.parse_match(),
826 TokenKind::Fn => self.parse_fn_expr(),
827 TokenKind::Lt
830 if matches!(self.peek_kind(), Some(&TokenKind::Lt))
831 && matches!(self.peek_kind_at(2), Some(TokenKind::Identifier(_))) =>
832 {
833 Err(ParserError::Unexpected {
834 got: "`<<` heredoc-like syntax".to_string(),
835 expected: "an expression — heredocs are only valid \
836 inside LLM tool-call argument JSON; \
837 for multiline strings in source code use \
838 triple-quoted `\"\"\"...\"\"\"`"
839 .to_string(),
840 span: start,
841 })
842 }
843 _ => Err(self.error("expression")),
844 }
845 }
846
847 pub(super) fn parse_fn_expr(&mut self) -> Result<SNode, ParserError> {
850 let start = self.current_span();
851 self.consume(&TokenKind::Fn, "fn")?;
852 self.consume(&TokenKind::LParen, "(")?;
853 let params = self.parse_typed_param_list()?;
854 self.consume(&TokenKind::RParen, ")")?;
855 let return_type = if self.check(&TokenKind::Arrow) {
856 self.advance();
857 Some(self.parse_nested_type_expr("closure return type")?)
858 } else {
859 None
860 };
861 self.consume(&TokenKind::LBrace, "{")?;
862 let body = self.parse_block()?;
863 self.consume(&TokenKind::RBrace, "}")?;
864 Ok(spanned(
865 Node::Closure {
866 params,
867 return_type,
868 body,
869 fn_syntax: true,
870 },
871 Span::merge(start, self.prev_span()),
872 ))
873 }
874
875 pub(super) fn parse_spawn_expr(&mut self) -> Result<SNode, ParserError> {
876 let start = self.current_span();
877 self.consume(&TokenKind::Spawn, "spawn")?;
878 self.consume(&TokenKind::LBrace, "{")?;
879 let body = self.parse_block()?;
880 self.consume(&TokenKind::RBrace, "}")?;
881 Ok(spanned(
882 Node::SpawnExpr { body },
883 Span::merge(start, self.prev_span()),
884 ))
885 }
886
887 pub(super) fn parse_hitl_expr(&mut self, kind: HitlKind) -> Result<SNode, ParserError> {
899 let start = self.current_span();
900 let kw_token = match kind {
901 HitlKind::RequestApproval => TokenKind::RequestApproval,
902 HitlKind::DualControl => TokenKind::DualControl,
903 HitlKind::AskUser => TokenKind::AskUser,
904 HitlKind::EscalateTo => TokenKind::EscalateTo,
905 };
906 self.consume(&kw_token, kind.as_keyword())?;
907 self.consume(&TokenKind::LParen, "(")?;
908 self.skip_newlines();
909
910 let mut args: Vec<HitlArg> = Vec::new();
911 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
912 let arg_start = self.current_span();
913 let is_named = matches!(
920 (self.peek_kind_at(0), self.peek_kind_at(1)),
921 (Some(TokenKind::Identifier(_)), Some(TokenKind::Colon))
922 );
923 let (name, value) = if is_named {
924 let Some(TokenKind::Identifier(raw)) = self.peek_kind_at(0).cloned() else {
925 unreachable!("named arg dispatch already matched Identifier token")
926 };
927 self.advance();
928 self.consume(&TokenKind::Colon, ":")?;
929 self.skip_newlines();
930 let value = self.parse_nested_expression("HITL argument")?;
931 (Some(raw), value)
932 } else {
933 (None, self.parse_nested_expression("HITL argument")?)
934 };
935 let arg_span = Span::merge(arg_start, self.prev_span());
936 args.push(HitlArg {
937 name,
938 value,
939 span: arg_span,
940 });
941 self.skip_newlines();
942 if self.check(&TokenKind::Comma) {
943 self.advance();
944 self.skip_newlines();
945 } else {
946 break;
947 }
948 }
949
950 self.skip_newlines();
951 self.consume(&TokenKind::RParen, ")")?;
952 Ok(spanned(
953 Node::HitlExpr { kind, args },
954 Span::merge(start, self.prev_span()),
955 ))
956 }
957
958 pub(super) fn parse_list_literal(&mut self) -> Result<SNode, ParserError> {
959 let start = self.current_span();
960 self.consume(&TokenKind::LBracket, "[")?;
961 let mut elements = Vec::new();
962 self.skip_newlines();
963
964 while !self.is_at_end() && !self.check(&TokenKind::RBracket) {
965 if self.check(&TokenKind::Dot) {
966 let saved_pos = self.pos;
967 self.advance();
968 if self.check(&TokenKind::Dot) {
969 self.advance();
970 self.consume(&TokenKind::Dot, ".")?;
971 let spread_start = self.tokens[saved_pos].span;
972 let expr = self.parse_nested_expression("list spread")?;
973 elements.push(spanned(
974 Node::Spread(Box::new(expr)),
975 Span::merge(spread_start, self.prev_span()),
976 ));
977 } else {
978 self.pos = saved_pos;
979 elements.push(self.parse_nested_expression("list element")?);
980 }
981 } else {
982 elements.push(self.parse_nested_expression("list element")?);
983 }
984 self.skip_newlines();
985 if self.check(&TokenKind::Comma) {
986 self.advance();
987 self.skip_newlines();
988 }
989 }
990
991 self.consume(&TokenKind::RBracket, "]")?;
992 Ok(spanned(
993 Node::ListLiteral(elements),
994 Span::merge(start, self.prev_span()),
995 ))
996 }
997
998 pub(super) fn parse_dict_or_closure(&mut self) -> Result<SNode, ParserError> {
999 let start = self.current_span();
1000 self.consume(&TokenKind::LBrace, "{")?;
1001 self.skip_newlines();
1002
1003 if self.check(&TokenKind::RBrace) {
1004 self.advance();
1005 return Ok(spanned(
1006 Node::DictLiteral(Vec::new()),
1007 Span::merge(start, self.prev_span()),
1008 ));
1009 }
1010
1011 let saved = self.pos;
1013 if self.is_closure_lookahead() {
1014 self.pos = saved;
1015 return self.parse_closure_body(start);
1016 }
1017 self.pos = saved;
1018 self.parse_dict_literal(start)
1019 }
1020
1021 pub(super) fn is_struct_construct_lookahead(&self, struct_name: &str) -> bool {
1025 if !struct_name
1026 .chars()
1027 .next()
1028 .is_some_and(|ch| ch.is_uppercase())
1029 {
1030 return false;
1031 }
1032
1033 let mut offset = 1;
1034 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
1035 offset += 1;
1036 }
1037
1038 match self.peek_kind_at(offset) {
1039 Some(TokenKind::RBrace) => true,
1040 Some(TokenKind::Identifier(_)) | Some(TokenKind::StringLiteral(_)) => {
1041 offset += 1;
1042 while matches!(self.peek_kind_at(offset), Some(TokenKind::Newline)) {
1043 offset += 1;
1044 }
1045 matches!(self.peek_kind_at(offset), Some(TokenKind::Colon))
1046 }
1047 _ => false,
1048 }
1049 }
1050
1051 pub(super) fn is_closure_lookahead(&mut self) -> bool {
1053 let mut depth = 0;
1054 while !self.is_at_end() {
1055 if let Some(tok) = self.current() {
1056 match &tok.kind {
1057 TokenKind::Arrow if depth == 0 => return true,
1058 TokenKind::LBrace | TokenKind::LParen | TokenKind::LBracket => depth += 1,
1059 TokenKind::RBrace if depth == 0 => return false,
1060 TokenKind::RBrace => depth -= 1,
1061 TokenKind::RParen | TokenKind::RBracket if depth > 0 => depth -= 1,
1062 _ => {}
1063 }
1064 self.advance();
1065 } else {
1066 return false;
1067 }
1068 }
1069 false
1070 }
1071
1072 pub(super) fn parse_closure_body(&mut self, start: Span) -> Result<SNode, ParserError> {
1074 let params = self.parse_typed_param_list_until_arrow()?;
1075 self.consume(&TokenKind::Arrow, "->")?;
1076 let body = self.parse_block()?;
1077 self.consume(&TokenKind::RBrace, "}")?;
1078 Ok(spanned(
1079 Node::Closure {
1080 params,
1081 return_type: None,
1082 body,
1083 fn_syntax: false,
1084 },
1085 Span::merge(start, self.prev_span()),
1086 ))
1087 }
1088
1089 pub(super) fn parse_typed_param_list_until_arrow(
1091 &mut self,
1092 ) -> Result<Vec<TypedParam>, ParserError> {
1093 self.parse_typed_params_until(|tok| tok == &TokenKind::Arrow)
1094 }
1095
1096 pub(super) fn parse_dict_literal(&mut self, start: Span) -> Result<SNode, ParserError> {
1097 let entries = self.parse_dict_entries()?;
1098 Ok(spanned(
1099 Node::DictLiteral(entries),
1100 Span::merge(start, self.prev_span()),
1101 ))
1102 }
1103
1104 pub(super) fn parse_dict_entries(&mut self) -> Result<Vec<DictEntry>, ParserError> {
1105 let mut entries = Vec::new();
1106 self.skip_newlines();
1107
1108 while !self.is_at_end() && !self.check(&TokenKind::RBrace) {
1109 if self.check(&TokenKind::Dot) {
1110 let saved_pos = self.pos;
1111 self.advance();
1112 if self.check(&TokenKind::Dot) {
1113 self.advance();
1114 if self.check(&TokenKind::Dot) {
1115 self.advance();
1116 let spread_start = self.tokens[saved_pos].span;
1117 let expr = self.parse_nested_expression("dict spread")?;
1118 entries.push(DictEntry {
1119 key: spanned(Node::NilLiteral, spread_start),
1120 value: spanned(
1121 Node::Spread(Box::new(expr)),
1122 Span::merge(spread_start, self.prev_span()),
1123 ),
1124 });
1125 self.skip_newlines();
1126 if self.check(&TokenKind::Comma) {
1127 self.advance();
1128 self.skip_newlines();
1129 }
1130 continue;
1131 }
1132 self.pos = saved_pos;
1133 } else {
1134 self.pos = saved_pos;
1135 }
1136 }
1137 let key = if self.check(&TokenKind::LBracket) {
1138 self.advance();
1139 let k = self.parse_nested_expression("computed dict key")?;
1140 self.consume(&TokenKind::RBracket, "]")?;
1141 k
1142 } else if matches!(
1143 self.current().map(|t| &t.kind),
1144 Some(TokenKind::StringLiteral(_))
1145 ) {
1146 let key_span = self.current_span();
1147 let name =
1148 if let Some(TokenKind::StringLiteral(s)) = self.current().map(|t| &t.kind) {
1149 s.clone()
1150 } else {
1151 unreachable!()
1152 };
1153 self.advance();
1154 spanned(Node::StringLiteral(name), key_span)
1155 } else {
1156 let key_span = self.current_span();
1157 let name = self.consume_identifier_or_keyword("dict key")?;
1158 spanned(Node::StringLiteral(name), key_span)
1159 };
1160 self.consume(&TokenKind::Colon, ":")?;
1161 let value = self.parse_nested_expression("dict value")?;
1162 entries.push(DictEntry { key, value });
1163 self.skip_newlines();
1164 if self.check(&TokenKind::Comma) {
1165 self.advance();
1166 self.skip_newlines();
1167 }
1168 }
1169
1170 self.consume(&TokenKind::RBrace, "}")?;
1171 Ok(entries)
1172 }
1173
1174 pub(super) fn parse_param_list(&mut self) -> Result<Vec<String>, ParserError> {
1176 let mut params = Vec::new();
1177 self.skip_newlines();
1178
1179 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1180 params.push(self.consume_identifier("parameter name")?);
1181 if self.check(&TokenKind::Comma) {
1182 self.advance();
1183 self.skip_newlines();
1184 }
1185 }
1186 Ok(params)
1187 }
1188
1189 pub(super) fn parse_typed_param_list(&mut self) -> Result<Vec<TypedParam>, ParserError> {
1191 self.parse_typed_params_until(|tok| tok == &TokenKind::RParen)
1192 }
1193
1194 pub(super) fn parse_typed_params_until(
1197 &mut self,
1198 is_terminator: impl Fn(&TokenKind) -> bool,
1199 ) -> Result<Vec<TypedParam>, ParserError> {
1200 let mut params = Vec::new();
1201 let mut seen_default = false;
1202 self.skip_newlines();
1203
1204 while !self.is_at_end() {
1205 if let Some(tok) = self.current() {
1206 if is_terminator(&tok.kind) {
1207 break;
1208 }
1209 } else {
1210 break;
1211 }
1212 let is_rest = if self.check(&TokenKind::Dot) {
1213 let p1 = self.pos + 1;
1214 let p2 = self.pos + 2;
1215 let is_ellipsis = p1 < self.tokens.len()
1216 && p2 < self.tokens.len()
1217 && self.tokens[p1].kind == TokenKind::Dot
1218 && self.tokens[p2].kind == TokenKind::Dot;
1219 if is_ellipsis {
1220 self.advance();
1221 self.advance();
1222 self.advance();
1223 true
1224 } else {
1225 false
1226 }
1227 } else {
1228 false
1229 };
1230 let name = self.consume_identifier("parameter name")?;
1231 let type_expr = self.try_parse_type_annotation()?;
1232 let default_value = if self.check(&TokenKind::Assign) {
1233 self.advance();
1234 seen_default = true;
1235 Some(Box::new(self.parse_nested_expression("parameter default")?))
1236 } else {
1237 if seen_default && !is_rest {
1238 return Err(self.error(
1239 "Required parameter cannot follow a parameter with a default value",
1240 ));
1241 }
1242 None
1243 };
1244 if is_rest
1245 && !is_terminator(
1246 &self
1247 .current()
1248 .map(|t| t.kind.clone())
1249 .unwrap_or(TokenKind::Eof),
1250 )
1251 {
1252 return Err(self.error("Rest parameter must be the last parameter"));
1253 }
1254 params.push(TypedParam {
1255 name,
1256 type_expr,
1257 default_value,
1258 rest: is_rest,
1259 });
1260 if self.check(&TokenKind::Comma) {
1261 self.advance();
1262 self.skip_newlines();
1263 }
1264 }
1265 Ok(params)
1266 }
1267
1268 pub(super) fn parse_arg_list(&mut self) -> Result<Vec<SNode>, ParserError> {
1269 let mut args = Vec::new();
1270 self.skip_newlines();
1271
1272 while !self.is_at_end() && !self.check(&TokenKind::RParen) {
1273 if self.check(&TokenKind::Dot) {
1274 let saved_pos = self.pos;
1275 self.advance();
1276 if self.check(&TokenKind::Dot) {
1277 self.advance();
1278 self.consume(&TokenKind::Dot, ".")?;
1279 let spread_start = self.tokens[saved_pos].span;
1280 let expr = self.parse_nested_expression("spread argument")?;
1281 args.push(spanned(
1282 Node::Spread(Box::new(expr)),
1283 Span::merge(spread_start, self.prev_span()),
1284 ));
1285 } else {
1286 self.pos = saved_pos;
1287 args.push(self.parse_nested_expression("argument")?);
1288 }
1289 } else {
1290 args.push(self.parse_nested_expression("argument")?);
1291 }
1292 self.skip_newlines();
1293 if self.check(&TokenKind::Comma) {
1294 self.advance();
1295 self.skip_newlines();
1296 }
1297 }
1298 Ok(args)
1299 }
1300}