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