1use crate::ast::{
2 AssignPathStep, AssignTarget, AstString, BinaryOp, Declaration, Expr, ExpressionSourceSpan,
3 LabelMetadata, ListComprehensionClause, ProcessDecl, ProcessParam, ProcessSignalDecl,
4 ProcessStartExpr, Program, TypeDecl, TypeExpr, TypeField, UnaryOp,
5};
6use crate::lexer::{LexError, Span, Token, TokenKind, lex};
7use thiserror::Error;
8
9#[derive(Debug, Error, PartialEq)]
10pub enum ParseError {
11 #[error(transparent)]
12 Lex(#[from] LexError),
13 #[error("expected {expected}, found {found}")]
14 Expected {
15 expected: &'static str,
16 found: String,
17 span: Span,
18 },
19 #[error("unexpected {found}")]
20 Unexpected { found: String, span: Span },
21 #[error("`{keyword}` can only be used inside a loop")]
22 LoopControlOutsideLoop { keyword: &'static str, span: Span },
23 #[error("`{keyword}` can only be used inside a `process` body")]
24 SessionProcessAdminOutsideBlock { keyword: &'static str, span: Span },
25 #[error("`{keyword}` can't be used inside a `process` body")]
26 ForegroundControlInsideProcess { keyword: &'static str, span: Span },
27 #[error(
28 "declarative trigger syntax has been removed; construct a source value and call the trigger registry register operation"
29 )]
30 DeclarativeTriggerRemoved { span: Span },
31 #[error("invalid @label annotation: {message}")]
32 InvalidLabelAnnotation { message: String, span: Span },
33 #[error(
34 "@label can annotate statements or process declarations, but not other declarations or another @label"
35 )]
36 InvalidLabelTarget { span: Span },
37 #[error("expression nesting too deep (limit {limit}); flatten the program")]
38 NestingTooDeep { limit: usize, span: Span },
39}
40
41impl ParseError {
42 pub fn span(&self) -> Option<Span> {
43 match self {
44 Self::Lex(_) => None,
45 Self::Expected { span, .. }
46 | Self::Unexpected { span, .. }
47 | Self::LoopControlOutsideLoop { span, .. }
48 | Self::SessionProcessAdminOutsideBlock { span, .. }
49 | Self::ForegroundControlInsideProcess { span, .. }
50 | Self::DeclarativeTriggerRemoved { span }
51 | Self::InvalidLabelAnnotation { span, .. }
52 | Self::InvalidLabelTarget { span }
53 | Self::NestingTooDeep { span, .. } => Some(*span),
54 }
55 }
56
57 pub fn offset(&self) -> usize {
58 match self {
59 Self::Lex(err) => err.offset(),
60 Self::Expected { span, .. }
61 | Self::Unexpected { span, .. }
62 | Self::LoopControlOutsideLoop { span, .. }
63 | Self::SessionProcessAdminOutsideBlock { span, .. }
64 | Self::ForegroundControlInsideProcess { span, .. }
65 | Self::DeclarativeTriggerRemoved { span }
66 | Self::InvalidLabelAnnotation { span, .. }
67 | Self::InvalidLabelTarget { span }
68 | Self::NestingTooDeep { span, .. } => span.start,
69 }
70 }
71}
72
73pub fn parse(source: &str) -> Result<Program, ParseError> {
74 let tokens = lex(source)?;
75 Parser {
76 tokens,
77 index: 0,
78 loop_depth: 0,
79 process_depth: 0,
80 nesting_depth: 0,
81 }
82 .parse_program()
83}
84
85const MAX_NESTING_DEPTH: usize = 40;
104
105struct Parser {
106 tokens: Vec<Token>,
107 index: usize,
108 loop_depth: usize,
109 process_depth: usize,
110 nesting_depth: usize,
111}
112
113#[derive(Clone)]
114struct ParsedExpr {
115 expr: Expr,
116 span: Span,
117 source_spans: Vec<ExpressionSourceSpan>,
118}
119
120impl ParsedExpr {
121 fn leaf(expr: Expr, span: Span) -> Self {
122 Self {
123 expr,
124 span,
125 source_spans: vec![ExpressionSourceSpan {
126 path: Vec::new(),
127 span,
128 }],
129 }
130 }
131
132 fn node(expr: Expr, span: Span, children: impl IntoIterator<Item = (u32, ParsedExpr)>) -> Self {
133 let mut source_spans = vec![ExpressionSourceSpan {
134 path: Vec::new(),
135 span,
136 }];
137 for (child_index, child) in children {
138 source_spans.extend(child.source_spans.into_iter().map(|mut source_span| {
139 source_span.path.insert(0, child_index);
140 source_span
141 }));
142 }
143 Self {
144 expr,
145 span,
146 source_spans,
147 }
148 }
149
150 fn into_expr(self) -> Expr {
151 self.expr
152 }
153}
154
155struct ParsedAssignTarget {
156 target: AssignTarget,
157 index_spans: Vec<ParsedExpr>,
158}
159
160struct ParsedListComprehensionClause {
161 clause: ListComprehensionClause,
162 expr_span: ParsedExpr,
163}
164
165fn push_root_expression(
166 expressions: &mut Vec<Expr>,
167 source_spans: &mut Vec<ExpressionSourceSpan>,
168 parsed: ParsedExpr,
169) {
170 let root = expressions.len() as u32;
171 let ParsedExpr {
172 expr,
173 source_spans: parsed_source_spans,
174 ..
175 } = parsed;
176 source_spans.extend(parsed_source_spans.into_iter().map(|mut source_span| {
177 source_span.path.insert(0, root);
178 source_span
179 }));
180 expressions.push(expr);
181}
182
183impl Parser {
184 fn parse_program(&mut self) -> Result<Program, ParseError> {
185 let capacity = (self.tokens.len() / 20).max(1);
186 let mut declarations = Vec::new();
187 let mut declaration_spans = Vec::new();
188 let mut expressions = Vec::with_capacity(capacity);
189 let mut expression_spans = Vec::with_capacity(capacity);
190 let mut expression_source_spans = Vec::new();
191 while !self.at_eof() {
192 if matches!(self.peek_kind(), TokenKind::At) {
193 let start = self.peek().span.start;
194 let label = self.parse_label_annotation()?;
195 if self.peek_contextual("process") && !self.peek_assignment_target() {
196 declarations.push(Declaration::Process(self.parse_process_decl(Some(label))?));
197 declaration_spans.push(self.span_from(start));
198 continue;
199 }
200 if self.peek_contextual("type")
201 || self.peek_contextual("trigger")
202 || matches!(self.peek_kind(), TokenKind::At)
203 {
204 return Err(ParseError::InvalidLabelTarget {
205 span: self.peek().span,
206 });
207 }
208 let inner = self.parse_statement_expr()?;
209 let end = self
210 .tokens
211 .get(self.index.saturating_sub(1))
212 .map(|token| token.span.end)
213 .unwrap_or(start);
214 let span = Span { start, end };
215 let expr = ParsedExpr::node(
216 Expr::LabelAnnotated {
217 label,
218 expr: Box::new(inner.expr.clone()),
219 },
220 span,
221 [(0, inner)],
222 );
223 expression_spans.push(span);
224 push_root_expression(&mut expressions, &mut expression_source_spans, expr);
225 continue;
226 }
227 if self.peek_contextual("type") && !self.peek_assignment_target() {
228 let start = self.peek().span.start;
229 declarations.push(Declaration::Type(self.parse_type_decl()?));
230 declaration_spans.push(self.span_from(start));
231 continue;
232 }
233 if self.peek_contextual("process") && !self.peek_assignment_target() {
234 let start = self.peek().span.start;
235 declarations.push(Declaration::Process(self.parse_process_decl(None)?));
236 declaration_spans.push(self.span_from(start));
237 continue;
238 }
239 if self.peek_contextual("trigger") && !self.peek_assignment_target() {
240 return Err(ParseError::DeclarativeTriggerRemoved {
241 span: self.peek().span,
242 });
243 }
244 let start = self.peek().span.start;
245 let expr = self.parse_statement_expr()?;
246 let end = self
247 .tokens
248 .get(self.index.saturating_sub(1))
249 .map(|token| token.span.end)
250 .unwrap_or(start);
251 let span = Span { start, end };
252 expression_spans.push(span);
253 push_root_expression(&mut expressions, &mut expression_source_spans, expr);
254 }
255 Ok(Program::module_with_spans(
256 declarations,
257 declaration_spans,
258 expressions,
259 expression_spans,
260 expression_source_spans,
261 ))
262 }
263
264 fn span_from(&self, start: usize) -> Span {
265 let end = self
266 .tokens
267 .get(self.index.saturating_sub(1))
268 .map(|token| token.span.end)
269 .unwrap_or(start);
270 Span { start, end }
271 }
272
273 fn parse_type_decl(&mut self) -> Result<TypeDecl, ParseError> {
274 self.expect_contextual("type")?;
275 let name = self.expect_ident()?;
276 self.expect_exact(TokenKind::Equal, "`=`")?;
277 let ty = if matches!(self.peek_kind(), TokenKind::LBrace) {
278 self.parse_type_object_body()?
279 } else {
280 self.parse_type_expr()?
281 };
282 Ok(TypeDecl { name, ty })
283 }
284
285 fn parse_process_decl(
286 &mut self,
287 label: Option<LabelMetadata>,
288 ) -> Result<ProcessDecl, ParseError> {
289 self.expect_contextual("process")?;
290 let name = self.expect_ident()?;
291 self.expect_exact(TokenKind::LParen, "`(`")?;
292 let mut params = Vec::new();
293 while !matches!(self.peek_kind(), TokenKind::RParen | TokenKind::Eof) {
294 let param_name = self.expect_ident()?;
295 self.expect_exact(TokenKind::Colon, "`:`")?;
296 let ty = self.parse_type_expr()?;
297 params.push(ProcessParam {
298 name: param_name,
299 ty,
300 });
301 if matches!(self.peek_kind(), TokenKind::Comma) {
302 self.bump();
303 continue;
304 }
305 break;
306 }
307 self.expect_exact(TokenKind::RParen, "`)`")?;
308 let signals = if self.peek_contextual("signals") && !self.peek_assignment_target() {
309 self.parse_process_signal_decls()?
310 } else {
311 Vec::new()
312 };
313 let return_ty = if matches!(self.peek_kind(), TokenKind::Minus)
314 && self
315 .tokens
316 .get(self.index + 1)
317 .is_some_and(|token| matches!(token.kind, TokenKind::Greater))
318 {
319 self.bump();
320 self.bump();
321 Some(self.parse_type_expr()?)
322 } else {
323 None
324 };
325 self.process_depth += 1;
326 let body = self.parse_block()?;
327 self.process_depth -= 1;
328 Ok(ProcessDecl {
329 name,
330 params,
331 signals,
332 return_ty,
333 label,
334 body: body.into_expr(),
335 })
336 }
337
338 fn parse_process_signal_decls(&mut self) -> Result<Vec<ProcessSignalDecl>, ParseError> {
339 self.expect_contextual("signals")?;
340 self.expect_exact(TokenKind::LBrace, "`{`")?;
341 let mut signals = Vec::new();
342 while !matches!(self.peek_kind(), TokenKind::RBrace | TokenKind::Eof) {
343 let name = self.expect_ident()?;
344 self.expect_exact(TokenKind::Colon, "`:`")?;
345 let ty = self.parse_type_expr()?;
346 signals.push(ProcessSignalDecl { name, ty });
347 if matches!(self.peek_kind(), TokenKind::Comma) {
348 self.bump();
349 if matches!(self.peek_kind(), TokenKind::RBrace) {
350 break;
351 }
352 continue;
353 }
354 break;
355 }
356 self.expect_exact(TokenKind::RBrace, "`}`")?;
357 Ok(signals)
358 }
359
360 fn parse_statement_expr(&mut self) -> Result<ParsedExpr, ParseError> {
361 match self.peek_kind() {
362 TokenKind::If => self.parse_if(),
363 TokenKind::For => self.parse_for(),
364 TokenKind::Submit => self.parse_submit(),
365 TokenKind::Cancel => self.parse_cancel(),
366 TokenKind::Print => self.parse_print(),
367 TokenKind::Call => Err(ParseError::Unexpected {
368 found: "`call`".to_string(),
369 span: self.peek().span,
370 }),
371 TokenKind::Ident(name) if name == "let" && !self.peek_assignment_target() => {
372 self.parse_let_assign()
373 }
374 TokenKind::Ident(name)
375 if matches!(name.as_str(), "yield" | "wake" | "finish" | "fail")
376 && !self.peek_assignment_target() =>
377 {
378 self.parse_processes()
379 }
380 TokenKind::Ident(name) if name == "break" && !self.peek_assignment_target() => {
381 self.parse_loop_control("break")
382 }
383 TokenKind::Ident(name) if name == "continue" && !self.peek_assignment_target() => {
384 self.parse_loop_control("continue")
385 }
386 TokenKind::Ident(name) if name == "while" && !self.peek_assignment_target() => {
387 self.parse_while()
388 }
389 TokenKind::Ident(_) if self.peek_assignment_target() => self.parse_assign(),
390 _ => self.parse_expr(),
391 }
392 }
393
394 fn parse_let_assign(&mut self) -> Result<ParsedExpr, ParseError> {
395 self.bump();
396 self.parse_assign()
397 }
398
399 fn parse_assign(&mut self) -> Result<ParsedExpr, ParseError> {
400 let start = self.peek().span.start;
401 let target = self.parse_assignment_target()?;
402 self.expect_exact(TokenKind::Equal, "`=`")?;
403 let expr = self.parse_expr()?;
404 let value_child_index = target.index_spans.len() as u32;
405 let span = Span {
406 start,
407 end: expr.span.end,
408 };
409 let mut children = target
410 .index_spans
411 .into_iter()
412 .enumerate()
413 .map(|(index, expr)| (index as u32, expr))
414 .collect::<Vec<_>>();
415 children.push((value_child_index, expr));
416 let value_expr = children
417 .last()
418 .expect("assignment value child")
419 .1
420 .expr
421 .clone();
422 Ok(ParsedExpr::node(
423 Expr::Assign {
424 target: target.target,
425 expr: Box::new(value_expr),
426 },
427 span,
428 children,
429 ))
430 }
431
432 fn parse_assignment_target(&mut self) -> Result<ParsedAssignTarget, ParseError> {
433 let root = self.expect_ident()?;
434 let mut steps = Vec::new();
435 let mut index_spans = Vec::new();
436 loop {
437 match self.peek_kind() {
438 TokenKind::Dot => {
439 self.bump();
440 steps.push(AssignPathStep::Field(self.expect_key_name()?));
441 }
442 TokenKind::LBracket => {
443 self.bump();
444 let index = self.parse_expr()?;
445 self.expect_exact(TokenKind::RBracket, "`]`")?;
446 steps.push(AssignPathStep::Index(index.expr.clone()));
447 index_spans.push(index);
448 }
449 _ => break,
450 }
451 }
452 Ok(ParsedAssignTarget {
453 target: AssignTarget { root, steps },
454 index_spans,
455 })
456 }
457
458 fn parse_if(&mut self) -> Result<ParsedExpr, ParseError> {
459 let start = self.bump().span.start;
460 let condition = self.parse_expr()?;
461 let then_block = self.parse_block()?;
462 let else_block = if matches!(self.peek_kind(), TokenKind::Else) {
463 self.bump();
464 if matches!(self.peek_kind(), TokenKind::If) {
465 self.parse_if()?
466 } else {
467 self.parse_block()?
468 }
469 } else {
470 ParsedExpr::leaf(
471 Expr::Block(Vec::new()),
472 Span {
473 start: then_block.span.end,
474 end: then_block.span.end,
475 },
476 )
477 };
478 let span = Span {
479 start,
480 end: else_block.span.end,
481 };
482 Ok(ParsedExpr::node(
483 Expr::If {
484 condition: Box::new(condition.expr.clone()),
485 then_block: Box::new(then_block.expr.clone()),
486 else_block: Box::new(else_block.expr.clone()),
487 },
488 span,
489 [(0, condition), (1, then_block), (2, else_block)],
490 ))
491 }
492
493 fn parse_for(&mut self) -> Result<ParsedExpr, ParseError> {
494 let start = self.bump().span.start;
495 let binding = self.expect_ident()?;
496 self.expect_exact(TokenKind::In, "`in`")?;
497 let iterable = self.parse_expr()?;
498 self.loop_depth += 1;
499 let body = self.parse_block()?;
500 self.loop_depth -= 1;
501 let span = Span {
502 start,
503 end: body.span.end,
504 };
505 Ok(ParsedExpr::node(
506 Expr::For {
507 binding,
508 iterable: Box::new(iterable.expr.clone()),
509 body: Box::new(body.expr.clone()),
510 },
511 span,
512 [(0, iterable), (1, body)],
513 ))
514 }
515
516 fn parse_while(&mut self) -> Result<ParsedExpr, ParseError> {
517 let start = self.bump().span.start;
518 let condition = self.parse_expr()?;
519 self.loop_depth += 1;
520 let body = self.parse_block()?;
521 self.loop_depth -= 1;
522 let span = Span {
523 start,
524 end: body.span.end,
525 };
526 Ok(ParsedExpr::node(
527 Expr::While {
528 condition: Box::new(condition.expr.clone()),
529 body: Box::new(body.expr.clone()),
530 },
531 span,
532 [(0, condition), (1, body)],
533 ))
534 }
535
536 fn parse_loop_control(&mut self, keyword: &'static str) -> Result<ParsedExpr, ParseError> {
537 let span = self.bump().span;
538 if self.loop_depth == 0 {
539 return Err(ParseError::LoopControlOutsideLoop { keyword, span });
540 }
541 let expr = match keyword {
542 "break" => Expr::Break,
543 "continue" => Expr::Continue,
544 _ => unreachable!("unknown loop control keyword"),
545 };
546 Ok(ParsedExpr::leaf(expr, span))
547 }
548
549 fn parse_submit(&mut self) -> Result<ParsedExpr, ParseError> {
550 let span = self.bump().span;
551 if self.process_depth > 0 {
552 return Err(ParseError::ForegroundControlInsideProcess {
553 keyword: "submit",
554 span,
555 });
556 }
557 let expr = if matches!(self.peek_kind(), TokenKind::RBrace | TokenKind::Eof) {
558 None
559 } else {
560 Some(self.parse_expr()?)
561 };
562 let end = expr.as_ref().map(|expr| expr.span.end).unwrap_or(span.end);
563 let span = Span {
564 start: span.start,
565 end,
566 };
567 let value_expr = expr.as_ref().map(|expr| Box::new(expr.expr.clone()));
568 Ok(match expr {
569 Some(expr) => ParsedExpr::node(Expr::Submit(value_expr), span, [(0, expr)]),
570 None => ParsedExpr::leaf(Expr::Submit(None), span),
571 })
572 }
573
574 fn parse_print(&mut self) -> Result<ParsedExpr, ParseError> {
575 let span = self.bump().span;
576 if self.process_depth > 0 {
577 return Err(ParseError::ForegroundControlInsideProcess {
578 keyword: "print",
579 span,
580 });
581 }
582 let expr = self.parse_expr()?;
583 let span = Span {
584 start: span.start,
585 end: expr.span.end,
586 };
587 Ok(ParsedExpr::node(
588 Expr::Print(Box::new(expr.expr.clone())),
589 span,
590 [(0, expr)],
591 ))
592 }
593
594 fn parse_processes(&mut self) -> Result<ParsedExpr, ParseError> {
595 let token = self.bump().clone();
596 let TokenKind::Ident(keyword) = token.kind else {
597 unreachable!("process admins are contextual identifiers");
598 };
599 let keyword_static = match keyword.as_str() {
600 "yield" => "yield",
601 "wake" => "wake",
602 "finish" => "finish",
603 "fail" => "fail",
604 _ => unreachable!("unknown process admin keyword"),
605 };
606 if self.process_depth == 0 {
607 return Err(ParseError::SessionProcessAdminOutsideBlock {
608 keyword: keyword_static,
609 span: token.span,
610 });
611 }
612 match keyword_static {
613 "yield" => {
614 let expr = self.parse_expr()?;
615 let span = Span {
616 start: token.span.start,
617 end: expr.span.end,
618 };
619 Ok(ParsedExpr::node(
620 Expr::Yield(Box::new(expr.expr.clone())),
621 span,
622 [(0, expr)],
623 ))
624 }
625 "wake" => {
626 let expr = self.parse_expr()?;
627 let span = Span {
628 start: token.span.start,
629 end: expr.span.end,
630 };
631 Ok(ParsedExpr::node(
632 Expr::Wake(Box::new(expr.expr.clone())),
633 span,
634 [(0, expr)],
635 ))
636 }
637 "finish" => {
638 let expr = if matches!(self.peek_kind(), TokenKind::RBrace | TokenKind::Eof) {
639 None
640 } else {
641 Some(self.parse_expr()?)
642 };
643 let end = expr
644 .as_ref()
645 .map(|expr| expr.span.end)
646 .unwrap_or(token.span.end);
647 let span = Span {
648 start: token.span.start,
649 end,
650 };
651 let value_expr = expr.as_ref().map(|expr| Box::new(expr.expr.clone()));
652 Ok(match expr {
653 Some(expr) => ParsedExpr::node(Expr::Finish(value_expr), span, [(0, expr)]),
654 None => ParsedExpr::leaf(Expr::Finish(None), span),
655 })
656 }
657 "fail" => {
658 let expr = self.parse_expr()?;
659 let span = Span {
660 start: token.span.start,
661 end: expr.span.end,
662 };
663 Ok(ParsedExpr::node(
664 Expr::Fail(Box::new(expr.expr.clone())),
665 span,
666 [(0, expr)],
667 ))
668 }
669 _ => unreachable!("unknown process admin keyword"),
670 }
671 }
672
673 fn parse_cancel(&mut self) -> Result<ParsedExpr, ParseError> {
674 let start = self.bump().span.start;
675 let expr = self.parse_expr()?;
676 let span = Span {
677 start,
678 end: expr.span.end,
679 };
680 Ok(ParsedExpr::node(
681 Expr::Cancel(Box::new(expr.expr.clone())),
682 span,
683 [(0, expr)],
684 ))
685 }
686
687 fn enter_nesting(&mut self) -> Result<(), ParseError> {
691 if self.nesting_depth >= MAX_NESTING_DEPTH {
692 return Err(ParseError::NestingTooDeep {
693 limit: MAX_NESTING_DEPTH,
694 span: self.peek().span,
695 });
696 }
697 self.nesting_depth += 1;
698 Ok(())
699 }
700
701 fn leave_nesting(&mut self) {
702 self.nesting_depth -= 1;
703 }
704
705 fn parse_block(&mut self) -> Result<ParsedExpr, ParseError> {
706 self.enter_nesting()?;
709 let result = self.parse_block_inner();
710 self.leave_nesting();
711 result
712 }
713
714 fn parse_block_inner(&mut self) -> Result<ParsedExpr, ParseError> {
715 let start = self.peek().span.start;
716 self.expect_exact(TokenKind::LBrace, "`{`")?;
717 let mut expressions = Vec::new();
718 let mut children = Vec::new();
719 while !matches!(self.peek_kind(), TokenKind::RBrace | TokenKind::Eof) {
720 let expr = if matches!(self.peek_kind(), TokenKind::At) {
721 self.parse_annotated_statement()?
722 } else {
723 self.parse_statement_expr()?
724 };
725 let child_index = expressions.len() as u32;
726 expressions.push(expr.expr.clone());
727 children.push((child_index, expr));
728 }
729 self.expect_exact(TokenKind::RBrace, "`}`")?;
730 Ok(ParsedExpr::node(
731 Expr::Block(expressions),
732 self.span_from(start),
733 children,
734 ))
735 }
736
737 fn parse_annotated_statement(&mut self) -> Result<ParsedExpr, ParseError> {
738 let start = self.peek().span.start;
739 let label = self.parse_label_annotation()?;
740 if matches!(self.peek_kind(), TokenKind::At)
741 || self.peek_contextual("type")
742 || self.peek_contextual("process")
743 || self.peek_contextual("trigger")
744 {
745 return Err(ParseError::InvalidLabelTarget {
746 span: self.peek().span,
747 });
748 }
749 let expr = self.parse_statement_expr()?;
750 let span = Span {
751 start,
752 end: expr.span.end,
753 };
754 Ok(ParsedExpr::node(
755 Expr::LabelAnnotated {
756 label,
757 expr: Box::new(expr.expr.clone()),
758 },
759 span,
760 [(0, expr)],
761 ))
762 }
763
764 fn parse_label_annotation(&mut self) -> Result<LabelMetadata, ParseError> {
765 let span = self.peek().span;
766 self.expect_exact(TokenKind::At, "`@`")?;
767 self.expect_contextual("label")?;
768 self.expect_exact(TokenKind::LParen, "`(`")?;
769
770 let mut title = None;
771 let mut description = None;
772 while !matches!(self.peek_kind(), TokenKind::RParen | TokenKind::Eof) {
773 let key_span = self.peek().span;
774 let key = self.expect_ident()?;
775 self.expect_exact(TokenKind::Colon, "`:`")?;
776 let value = self.expect_string_literal()?;
777 match key.as_str() {
778 "title" => {
779 if title.replace(value).is_some() {
780 return Err(ParseError::InvalidLabelAnnotation {
781 message: "duplicate `title` field".to_string(),
782 span: key_span,
783 });
784 }
785 }
786 "description" => {
787 if description.replace(value).is_some() {
788 return Err(ParseError::InvalidLabelAnnotation {
789 message: "duplicate `description` field".to_string(),
790 span: key_span,
791 });
792 }
793 }
794 _ => {
795 return Err(ParseError::InvalidLabelAnnotation {
796 message: format!("unknown field `{key}`"),
797 span: key_span,
798 });
799 }
800 }
801 if matches!(self.peek_kind(), TokenKind::Comma) {
802 self.bump();
803 if matches!(self.peek_kind(), TokenKind::RParen) {
804 break;
805 }
806 continue;
807 }
808 break;
809 }
810 self.expect_exact(TokenKind::RParen, "`)`")?;
811 let Some(title) = title else {
812 return Err(ParseError::InvalidLabelAnnotation {
813 message: "`title` is required".to_string(),
814 span,
815 });
816 };
817 Ok(LabelMetadata { title, description })
818 }
819
820 fn parse_expr(&mut self) -> Result<ParsedExpr, ParseError> {
821 self.enter_nesting()?;
825 let result = self.parse_ternary();
826 self.leave_nesting();
827 result
828 }
829
830 fn parse_ternary(&mut self) -> Result<ParsedExpr, ParseError> {
831 let condition = self.parse_or()?;
832 if !matches!(self.peek_kind(), TokenKind::Question) {
833 return Ok(condition);
834 }
835 self.bump();
836 let then_expr = self.parse_expr()?;
837 self.expect_exact(TokenKind::Colon, "`:`")?;
838 let else_expr = self.parse_expr()?;
839 let span = Span {
840 start: condition.span.start,
841 end: else_expr.span.end,
842 };
843 Ok(ParsedExpr::node(
844 Expr::If {
845 condition: Box::new(condition.expr.clone()),
846 then_block: Box::new(then_expr.expr.clone()),
847 else_block: Box::new(else_expr.expr.clone()),
848 },
849 span,
850 [(0, condition), (1, then_expr), (2, else_expr)],
851 ))
852 }
853
854 fn parse_or(&mut self) -> Result<ParsedExpr, ParseError> {
855 let mut expr = self.parse_and()?;
856 while matches!(self.peek_kind(), TokenKind::Or | TokenKind::OrOr) {
857 self.bump();
858 let right = self.parse_and()?;
859 let span = Span {
860 start: expr.span.start,
861 end: right.span.end,
862 };
863 expr = ParsedExpr::node(
864 Expr::Binary {
865 left: Box::new(expr.expr.clone()),
866 op: BinaryOp::Or,
867 right: Box::new(right.expr.clone()),
868 },
869 span,
870 [(0, expr), (1, right)],
871 );
872 }
873 Ok(expr)
874 }
875
876 fn parse_and(&mut self) -> Result<ParsedExpr, ParseError> {
877 let mut expr = self.parse_compare()?;
878 while matches!(self.peek_kind(), TokenKind::And | TokenKind::AndAnd) {
879 self.bump();
880 let right = self.parse_compare()?;
881 let span = Span {
882 start: expr.span.start,
883 end: right.span.end,
884 };
885 expr = ParsedExpr::node(
886 Expr::Binary {
887 left: Box::new(expr.expr.clone()),
888 op: BinaryOp::And,
889 right: Box::new(right.expr.clone()),
890 },
891 span,
892 [(0, expr), (1, right)],
893 );
894 }
895 Ok(expr)
896 }
897
898 fn parse_compare(&mut self) -> Result<ParsedExpr, ParseError> {
899 let mut expr = self.parse_add()?;
900 loop {
901 let op = match self.peek_kind() {
902 TokenKind::DoubleEqual => BinaryOp::Equal,
903 TokenKind::BangEqual => BinaryOp::NotEqual,
904 TokenKind::Less => BinaryOp::Less,
905 TokenKind::LessEqual => BinaryOp::LessEqual,
906 TokenKind::Greater => BinaryOp::Greater,
907 TokenKind::GreaterEqual => BinaryOp::GreaterEqual,
908 _ => break,
909 };
910 self.bump();
911 let right = self.parse_add()?;
912 let span = Span {
913 start: expr.span.start,
914 end: right.span.end,
915 };
916 expr = ParsedExpr::node(
917 Expr::Binary {
918 left: Box::new(expr.expr.clone()),
919 op,
920 right: Box::new(right.expr.clone()),
921 },
922 span,
923 [(0, expr), (1, right)],
924 );
925 }
926 Ok(expr)
927 }
928
929 fn parse_add(&mut self) -> Result<ParsedExpr, ParseError> {
930 let mut expr = self.parse_mul()?;
931 loop {
932 let op = match self.peek_kind() {
933 TokenKind::Plus => BinaryOp::Add,
934 TokenKind::Minus => BinaryOp::Subtract,
935 _ => break,
936 };
937 self.bump();
938 let right = self.parse_mul()?;
939 let span = Span {
940 start: expr.span.start,
941 end: right.span.end,
942 };
943 expr = ParsedExpr::node(
944 Expr::Binary {
945 left: Box::new(expr.expr.clone()),
946 op,
947 right: Box::new(right.expr.clone()),
948 },
949 span,
950 [(0, expr), (1, right)],
951 );
952 }
953 Ok(expr)
954 }
955
956 fn parse_mul(&mut self) -> Result<ParsedExpr, ParseError> {
957 let mut expr = self.parse_unary()?;
958 loop {
959 let op = match self.peek_kind() {
960 TokenKind::Star => BinaryOp::Multiply,
961 TokenKind::Slash => BinaryOp::Divide,
962 TokenKind::Percent => BinaryOp::Modulo,
963 _ => break,
964 };
965 self.bump();
966 let right = self.parse_unary()?;
967 let span = Span {
968 start: expr.span.start,
969 end: right.span.end,
970 };
971 expr = ParsedExpr::node(
972 Expr::Binary {
973 left: Box::new(expr.expr.clone()),
974 op,
975 right: Box::new(right.expr.clone()),
976 },
977 span,
978 [(0, expr), (1, right)],
979 );
980 }
981 Ok(expr)
982 }
983
984 fn parse_unary(&mut self) -> Result<ParsedExpr, ParseError> {
985 match self.peek_kind() {
986 TokenKind::Minus => {
987 let start = self.bump().span.start;
988 let expr = self.parse_unary()?;
989 Ok(ParsedExpr::node(
990 Expr::Unary {
991 op: UnaryOp::Negate,
992 expr: Box::new(expr.expr.clone()),
993 },
994 Span {
995 start,
996 end: expr.span.end,
997 },
998 [(0, expr)],
999 ))
1000 }
1001 TokenKind::Not => {
1002 let start = self.bump().span.start;
1003 let expr = self.parse_unary()?;
1004 Ok(ParsedExpr::node(
1005 Expr::Unary {
1006 op: UnaryOp::Not,
1007 expr: Box::new(expr.expr.clone()),
1008 },
1009 Span {
1010 start,
1011 end: expr.span.end,
1012 },
1013 [(0, expr)],
1014 ))
1015 }
1016 TokenKind::Bang => {
1017 let start = self.bump().span.start;
1018 let expr = self.parse_unary()?;
1019 Ok(ParsedExpr::node(
1020 Expr::Unary {
1021 op: UnaryOp::Not,
1022 expr: Box::new(expr.expr.clone()),
1023 },
1024 Span {
1025 start,
1026 end: expr.span.end,
1027 },
1028 [(0, expr)],
1029 ))
1030 }
1031 TokenKind::Await => {
1032 let start = self.bump().span.start;
1033 let expr = self.parse_unary()?;
1034 Ok(ParsedExpr::node(
1035 Expr::Await(Box::new(expr.expr.clone())),
1036 Span {
1037 start,
1038 end: expr.span.end,
1039 },
1040 [(0, expr)],
1041 ))
1042 }
1043 _ => self.parse_postfix(),
1044 }
1045 }
1046
1047 fn parse_postfix(&mut self) -> Result<ParsedExpr, ParseError> {
1048 let mut expr = self.parse_primary()?;
1049 loop {
1050 match self.peek_kind() {
1051 TokenKind::Dot => {
1052 self.bump();
1053 let field = self.expect_key_name()?;
1054 if matches!(self.peek_kind(), TokenKind::LParen) {
1055 let args = self.parse_call_arguments()?;
1056 let span = self.span_from(expr.span.start);
1057 let mut children = Vec::with_capacity(args.len() + 1);
1058 children.push((0, expr));
1059 let arg_exprs = args.iter().map(|arg| arg.expr.clone()).collect();
1060 children.extend(
1061 args.into_iter()
1062 .enumerate()
1063 .map(|(index, arg)| ((index + 1) as u32, arg)),
1064 );
1065 let receiver = children[0].1.expr.clone();
1066 expr = ParsedExpr::node(
1067 Expr::ReceiverCall {
1068 receiver: Box::new(receiver),
1069 operation: field,
1070 args: arg_exprs,
1071 },
1072 span,
1073 children,
1074 );
1075 } else {
1076 let span = self.span_from(expr.span.start);
1077 expr = ParsedExpr::node(
1078 Expr::Field {
1079 target: Box::new(expr.expr.clone()),
1080 field,
1081 },
1082 span,
1083 [(0, expr)],
1084 );
1085 }
1086 }
1087 TokenKind::LBracket => {
1088 self.bump();
1089 let index = self.parse_expr()?;
1090 self.expect_exact(TokenKind::RBracket, "`]`")?;
1091 let span = self.span_from(expr.span.start);
1092 expr = ParsedExpr::node(
1093 Expr::Index {
1094 target: Box::new(expr.expr.clone()),
1095 index: Box::new(index.expr.clone()),
1096 },
1097 span,
1098 [(0, expr), (1, index)],
1099 );
1100 }
1101 TokenKind::Question if !self.question_starts_ternary() => {
1102 self.bump();
1103 let span = self.span_from(expr.span.start);
1104 expr = ParsedExpr::node(
1105 Expr::ResultUnwrap(Box::new(expr.expr.clone())),
1106 span,
1107 [(0, expr)],
1108 );
1109 }
1110 _ => break,
1111 }
1112 }
1113 Ok(expr)
1114 }
1115
1116 fn parse_primary(&mut self) -> Result<ParsedExpr, ParseError> {
1117 match self.peek_kind() {
1118 TokenKind::Null => {
1119 let span = self.bump().span;
1120 Ok(ParsedExpr::leaf(Expr::Null, span))
1121 }
1122 TokenKind::True => {
1123 let span = self.bump().span;
1124 Ok(ParsedExpr::leaf(Expr::Bool(true), span))
1125 }
1126 TokenKind::False => {
1127 let span = self.bump().span;
1128 Ok(ParsedExpr::leaf(Expr::Bool(false), span))
1129 }
1130 TokenKind::Number(value) => {
1131 let value = *value;
1132 let span = self.bump().span;
1133 Ok(ParsedExpr::leaf(Expr::Number(value), span))
1134 }
1135 TokenKind::String(value) => {
1136 let value = value.clone();
1137 let span = self.bump().span;
1138 Ok(ParsedExpr::leaf(Expr::String(value), span))
1139 }
1140 TokenKind::Ident(name) => {
1141 let token_span = self.peek().span;
1142 if name == "parallel"
1143 && self
1144 .tokens
1145 .get(self.index + 1)
1146 .is_some_and(|token| matches!(token.kind, TokenKind::LBrace))
1147 {
1148 return Err(ParseError::Unexpected {
1149 found: "`parallel`".to_string(),
1150 span: self.peek().span,
1151 });
1152 }
1153 let name = name.clone();
1154 self.bump();
1155 if name == "sleep" {
1156 return self.parse_sleep_expr(token_span.start);
1157 }
1158 if name == "start" && matches!(self.peek_kind(), TokenKind::Call) {
1159 return Err(ParseError::Unexpected {
1160 found: "`start call`".to_string(),
1161 span: self.tokens[self.index.saturating_sub(1)].span,
1162 });
1163 }
1164 if name == "start"
1165 && (matches!(self.peek_kind(), TokenKind::Ident(_))
1166 || matches!(self.peek_kind(), TokenKind::LBrace)
1167 || self.paren_group_followed_by_lbrace())
1168 {
1169 return self.parse_process_start_expr(token_span.start);
1170 }
1171 if name == "Type" && matches!(self.peek_kind(), TokenKind::LBrace) {
1172 let ty = self.parse_type_object()?;
1173 return Ok(ParsedExpr::leaf(
1174 Expr::TypeLiteral(Box::new(ty)),
1175 self.span_from(token_span.start),
1176 ));
1177 }
1178 if matches!(self.peek_kind(), TokenKind::LParen) {
1179 let args = self.parse_call_arguments()?;
1180 if name == "wait_signal" {
1181 if args.len() != 1 {
1182 return Err(ParseError::Expected {
1183 expected: "one signal name argument",
1184 found: format!("{} arguments", args.len()),
1185 span: self.tokens[self.index.saturating_sub(1)].span,
1186 });
1187 }
1188 return Ok(ParsedExpr::leaf(
1189 Expr::WaitSignal {
1190 name: static_signal_name_arg(&args[0].expr, "wait_signal")?,
1191 },
1192 self.span_from(token_span.start),
1193 ));
1194 }
1195 if name == "signal_run" {
1196 if args.len() != 3 {
1197 return Err(ParseError::Expected {
1198 expected: "run handle, signal name, and payload arguments",
1199 found: format!("{} arguments", args.len()),
1200 span: self.tokens[self.index.saturating_sub(1)].span,
1201 });
1202 }
1203 let run = args[0].expr.clone();
1204 let payload = args[2].expr.clone();
1205 return Ok(ParsedExpr::node(
1206 Expr::SignalRun {
1207 run: Box::new(run),
1208 name: static_signal_name_arg(&args[1].expr, "signal_run")?,
1209 payload: Box::new(payload),
1210 },
1211 self.span_from(token_span.start),
1212 [(0, args[0].clone()), (1, args[2].clone())],
1213 ));
1214 }
1215 let arg_exprs = args.iter().map(|arg| arg.expr.clone()).collect();
1216 Ok(ParsedExpr::node(
1217 Expr::BuiltinCall {
1218 name,
1219 args: arg_exprs,
1220 },
1221 self.span_from(token_span.start),
1222 args.into_iter()
1223 .enumerate()
1224 .map(|(index, arg)| (index as u32, arg)),
1225 ))
1226 } else {
1227 Ok(ParsedExpr::leaf(Expr::Variable(name), token_span))
1228 }
1229 }
1230 TokenKind::LParen => {
1231 self.bump();
1232 let expr = self.parse_expr()?;
1233 self.expect_exact(TokenKind::RParen, "`)`")?;
1234 Ok(expr)
1235 }
1236 TokenKind::LBracket => self.parse_list(),
1237 TokenKind::LBrace => self.parse_record(),
1238 TokenKind::Call => Err(ParseError::Unexpected {
1239 found: "`call`".to_string(),
1240 span: self.peek().span,
1241 }),
1242 _ => Err(self.unexpected()),
1243 }
1244 }
1245
1246 fn parse_list(&mut self) -> Result<ParsedExpr, ParseError> {
1247 let start = self.peek().span.start;
1248 self.expect_exact(TokenKind::LBracket, "`[`")?;
1249 if matches!(self.peek_kind(), TokenKind::RBracket) {
1250 self.expect_exact(TokenKind::RBracket, "`]`")?;
1251 return Ok(ParsedExpr::node(
1252 Expr::List(Vec::new()),
1253 self.span_from(start),
1254 [],
1255 ));
1256 }
1257
1258 let first = self.parse_expr()?;
1259 if matches!(self.peek_kind(), TokenKind::For) {
1260 let parsed_clauses = self.parse_list_comprehension_clauses()?;
1261 self.expect_exact(TokenKind::RBracket, "`]`")?;
1262 let clauses = parsed_clauses
1263 .iter()
1264 .map(|parsed| parsed.clause.clone())
1265 .collect::<Vec<_>>();
1266 let mut children = parsed_clauses
1267 .into_iter()
1268 .enumerate()
1269 .map(|(index, parsed)| (index as u32, parsed.expr_span))
1270 .collect::<Vec<_>>();
1271 children.push((children.len() as u32, first.clone()));
1272 return Ok(ParsedExpr::node(
1273 Expr::ListComprehension {
1274 element: Box::new(first.expr.clone()),
1275 clauses,
1276 },
1277 self.span_from(start),
1278 children,
1279 ));
1280 }
1281
1282 let mut items = Vec::new();
1283 let mut children = Vec::new();
1284 children.push((0, first));
1285 items.push(children.last().expect("item").1.expr.clone());
1286 if matches!(self.peek_kind(), TokenKind::Comma) {
1287 self.bump();
1288 } else {
1289 self.expect_exact(TokenKind::RBracket, "`]`")?;
1290 return Ok(ParsedExpr::node(
1291 Expr::List(items),
1292 self.span_from(start),
1293 children,
1294 ));
1295 }
1296 while !matches!(self.peek_kind(), TokenKind::RBracket) {
1297 let item = self.parse_expr()?;
1298 children.push((items.len() as u32, item));
1299 items.push(children.last().expect("item").1.expr.clone());
1300 if matches!(self.peek_kind(), TokenKind::Comma) {
1301 self.bump();
1302 continue;
1303 }
1304 break;
1305 }
1306 self.expect_exact(TokenKind::RBracket, "`]`")?;
1307 Ok(ParsedExpr::node(
1308 Expr::List(items),
1309 self.span_from(start),
1310 children,
1311 ))
1312 }
1313
1314 fn parse_list_comprehension_clauses(
1315 &mut self,
1316 ) -> Result<Vec<ParsedListComprehensionClause>, ParseError> {
1317 let mut clauses = Vec::new();
1318 while matches!(self.peek_kind(), TokenKind::For) {
1319 self.bump();
1320 let binding = self.expect_ident()?;
1321 self.expect_exact(TokenKind::In, "`in`")?;
1322 let iterable = self.parse_expr()?;
1323 clauses.push(ParsedListComprehensionClause {
1324 clause: ListComprehensionClause::For {
1325 binding,
1326 iterable: iterable.expr.clone(),
1327 },
1328 expr_span: iterable,
1329 });
1330 while matches!(self.peek_kind(), TokenKind::If) {
1331 self.bump();
1332 let condition = self.parse_expr()?;
1333 clauses.push(ParsedListComprehensionClause {
1334 clause: ListComprehensionClause::If {
1335 condition: condition.expr.clone(),
1336 },
1337 expr_span: condition,
1338 });
1339 }
1340 }
1341 Ok(clauses)
1342 }
1343
1344 fn parse_record(&mut self) -> Result<ParsedExpr, ParseError> {
1345 let start = self.peek().span.start;
1346 self.expect_exact(TokenKind::LBrace, "`{`")?;
1347 let entries = self.parse_record_entries()?;
1348 self.expect_exact(TokenKind::RBrace, "`}`")?;
1349 let expr_entries = entries
1350 .iter()
1351 .map(|(key, value)| (key.clone(), value.expr.clone()))
1352 .collect();
1353 Ok(ParsedExpr::node(
1354 Expr::Record(expr_entries),
1355 self.span_from(start),
1356 entries
1357 .into_iter()
1358 .enumerate()
1359 .map(|(index, (_, value))| (index as u32, value)),
1360 ))
1361 }
1362
1363 fn parse_record_entries(&mut self) -> Result<Vec<(AstString, ParsedExpr)>, ParseError> {
1364 let mut entries = Vec::new();
1365 while !matches!(self.peek_kind(), TokenKind::RBrace) {
1366 let key = self.expect_key_name()?;
1367 self.expect_exact(TokenKind::Colon, "`:`")?;
1368 let value = self.parse_expr()?;
1369 entries.push((key, value));
1370 if matches!(self.peek_kind(), TokenKind::Comma) {
1371 self.bump();
1372 continue;
1373 }
1374 break;
1375 }
1376 Ok(entries)
1377 }
1378
1379 fn parse_call_arguments(&mut self) -> Result<Vec<ParsedExpr>, ParseError> {
1380 self.expect_exact(TokenKind::LParen, "`(`")?;
1381 let mut args = Vec::new();
1382 if !matches!(self.peek_kind(), TokenKind::RParen) {
1383 loop {
1384 args.push(self.parse_expr()?);
1385 if matches!(self.peek_kind(), TokenKind::Comma) {
1386 self.bump();
1387 if matches!(self.peek_kind(), TokenKind::RParen) {
1388 break;
1389 }
1390 continue;
1391 }
1392 break;
1393 }
1394 }
1395 self.expect_exact(TokenKind::RParen, "`)`")?;
1396 Ok(args)
1397 }
1398
1399 fn parse_named_arguments(&mut self) -> Result<Vec<(AstString, ParsedExpr)>, ParseError> {
1400 let mut entries = Vec::new();
1401 while !matches!(self.peek_kind(), TokenKind::RParen | TokenKind::Eof) {
1402 let key = self.expect_key_name()?;
1403 self.expect_exact(TokenKind::Colon, "`:`")?;
1404 let value = self.parse_expr()?;
1405 entries.push((key, value));
1406 if matches!(self.peek_kind(), TokenKind::Comma) {
1407 self.bump();
1408 continue;
1409 }
1410 break;
1411 }
1412 Ok(entries)
1413 }
1414
1415 fn parse_process_start_expr(&mut self, start: usize) -> Result<ParsedExpr, ParseError> {
1416 if matches!(self.peek_kind(), TokenKind::LBrace) || self.paren_group_followed_by_lbrace() {
1417 return Err(ParseError::Unexpected {
1418 found: "inline `start` process body".to_string(),
1419 span: self.peek().span,
1420 });
1421 }
1422 let process = self.expect_ident()?;
1423 self.expect_exact(TokenKind::LParen, "`(`")?;
1424 let args = self.parse_named_arguments()?;
1425 self.expect_exact(TokenKind::RParen, "`)`")?;
1426 let expr_args = args
1427 .iter()
1428 .map(|(name, value)| (name.clone(), value.expr.clone()))
1429 .collect();
1430 Ok(ParsedExpr::node(
1431 Expr::StartProcess(ProcessStartExpr {
1432 process,
1433 args: expr_args,
1434 }),
1435 self.span_from(start),
1436 args.into_iter()
1437 .enumerate()
1438 .map(|(index, (_, value))| (index as u32, value)),
1439 ))
1440 }
1441
1442 fn parse_sleep_expr(&mut self, start: usize) -> Result<ParsedExpr, ParseError> {
1443 if matches!(self.peek_kind(), TokenKind::For) {
1444 self.bump();
1445 let expr = self.parse_expr()?;
1446 return Ok(ParsedExpr::node(
1447 Expr::SleepFor(Box::new(expr.expr.clone())),
1448 Span {
1449 start,
1450 end: expr.span.end,
1451 },
1452 [(0, expr)],
1453 ));
1454 }
1455 if self.peek_contextual("until") {
1456 self.bump();
1457 let expr = self.parse_expr()?;
1458 return Ok(ParsedExpr::node(
1459 Expr::SleepUntil(Box::new(expr.expr.clone())),
1460 Span {
1461 start,
1462 end: expr.span.end,
1463 },
1464 [(0, expr)],
1465 ));
1466 }
1467 Err(ParseError::Expected {
1468 expected: "`for` or `until`",
1469 found: render_kind(self.peek_kind()),
1470 span: self.peek().span,
1471 })
1472 }
1473
1474 fn parse_type_object(&mut self) -> Result<TypeExpr, ParseError> {
1475 self.expect_exact(TokenKind::LBrace, "`{`")?;
1476 self.parse_type_object_body_after_lbrace()
1477 }
1478
1479 fn parse_type_object_body(&mut self) -> Result<TypeExpr, ParseError> {
1480 self.expect_exact(TokenKind::LBrace, "`{`")?;
1481 self.parse_type_object_body_after_lbrace()
1482 }
1483
1484 fn parse_type_object_body_after_lbrace(&mut self) -> Result<TypeExpr, ParseError> {
1485 let mut fields = Vec::new();
1486 let mut seen = std::collections::HashSet::new();
1487 while !matches!(self.peek_kind(), TokenKind::RBrace) {
1488 let name_token_span = self.peek().span;
1489 let name = self.expect_key_name()?;
1490 if !seen.insert(name.clone()) {
1491 return Err(ParseError::Expected {
1492 expected: "unique field name",
1493 found: format!("duplicate field `{name}`"),
1494 span: name_token_span,
1495 });
1496 }
1497 self.expect_exact(TokenKind::Colon, "`:`")?;
1498 let ty = self.parse_type_expr()?;
1499 let optional = if matches!(self.peek_kind(), TokenKind::Question) {
1500 self.bump();
1501 true
1502 } else {
1503 false
1504 };
1505 fields.push(TypeField { name, ty, optional });
1506 if matches!(self.peek_kind(), TokenKind::Comma) {
1507 self.bump();
1508 continue;
1509 }
1510 break;
1511 }
1512 self.expect_exact(TokenKind::RBrace, "`}`")?;
1513 Ok(TypeExpr::Object(fields))
1514 }
1515
1516 fn parse_type_expr(&mut self) -> Result<TypeExpr, ParseError> {
1517 let first = self.parse_type_term()?;
1518 if !matches!(self.peek_kind(), TokenKind::Pipe) {
1519 return Ok(first);
1520 }
1521 let mut variants = vec![first];
1525 while matches!(self.peek_kind(), TokenKind::Pipe) {
1526 self.bump();
1527 variants.push(self.parse_type_term()?);
1528 }
1529 Ok(TypeExpr::Union(variants))
1530 }
1531
1532 fn parse_type_term(&mut self) -> Result<TypeExpr, ParseError> {
1533 let token = self.peek().clone();
1534 match token.kind {
1535 TokenKind::Null => {
1536 self.bump();
1537 Ok(TypeExpr::Null)
1538 }
1539 TokenKind::String(value) => {
1540 self.bump();
1541 Ok(TypeExpr::Enum(vec![value]))
1542 }
1543 TokenKind::LBrace => self.parse_type_object_body(),
1548 TokenKind::Ident(_name) => {
1549 let name = self.parse_type_name()?;
1550 match name.as_str() {
1551 "str" | "string" => Ok(TypeExpr::Str),
1552 "int" | "integer" => Ok(TypeExpr::Int),
1553 "float" | "number" => Ok(TypeExpr::Float),
1554 "bool" | "boolean" => Ok(TypeExpr::Bool),
1555 "dict" | "object" => Ok(TypeExpr::Dict),
1556 "any" => Ok(TypeExpr::Any),
1557 "enum" => {
1558 self.expect_exact(TokenKind::LBracket, "`[`")?;
1559 let mut values = Vec::new();
1560 if !matches!(self.peek_kind(), TokenKind::RBracket) {
1561 loop {
1562 let value = self.expect_string_literal()?;
1563 values.push(value);
1564 if matches!(self.peek_kind(), TokenKind::Comma) {
1565 self.bump();
1566 continue;
1567 }
1568 break;
1569 }
1570 }
1571 if values.is_empty() {
1572 return Err(ParseError::Expected {
1573 expected: "at least one enum string literal",
1574 found: "empty enum".to_string(),
1575 span: token.span,
1576 });
1577 }
1578 self.expect_exact(TokenKind::RBracket, "`]`")?;
1579 Ok(TypeExpr::Enum(values))
1580 }
1581 "list" => {
1582 self.expect_exact(TokenKind::LBracket, "`[`")?;
1583 let inner = self.parse_type_expr()?;
1584 self.expect_exact(TokenKind::RBracket, "`]`")?;
1585 Ok(TypeExpr::List(Box::new(inner)))
1586 }
1587 "Process" => {
1588 self.expect_exact(TokenKind::Less, "`<`")?;
1589 let input = self.parse_type_expr()?;
1590 self.expect_exact(TokenKind::Comma, "`,`")?;
1591 let output = self.parse_type_expr()?;
1592 self.expect_exact(TokenKind::Greater, "`>`")?;
1593 Ok(TypeExpr::Process {
1594 input: Box::new(input),
1595 output: Box::new(output),
1596 input_count: 1,
1597 })
1598 }
1599 "TriggerHandle" => {
1600 self.expect_exact(TokenKind::Less, "`<`")?;
1601 let event = self.parse_type_expr()?;
1602 self.expect_exact(TokenKind::Greater, "`>`")?;
1603 Ok(TypeExpr::TriggerHandle(Box::new(event)))
1604 }
1605 "Type" => self.parse_type_object(),
1606 _ => Ok(TypeExpr::Ref(name)),
1607 }
1608 }
1609 _ => Err(ParseError::Expected {
1610 expected: "type expression",
1611 found: render_kind(&token.kind),
1612 span: token.span,
1613 }),
1614 }
1615 }
1616
1617 fn expect_string_literal(&mut self) -> Result<AstString, ParseError> {
1618 let token = self.bump().clone();
1619 match token.kind {
1620 TokenKind::String(value) => Ok(value),
1621 other => Err(ParseError::Expected {
1622 expected: "string literal",
1623 found: render_kind(&other),
1624 span: token.span,
1625 }),
1626 }
1627 }
1628
1629 fn expect_ident(&mut self) -> Result<AstString, ParseError> {
1630 let token = self.bump();
1631 match &token.kind {
1632 TokenKind::Ident(name) => Ok(name.clone()),
1633 other => Err(ParseError::Expected {
1634 expected: "identifier",
1635 found: render_kind(other),
1636 span: token.span,
1637 }),
1638 }
1639 }
1640
1641 fn parse_type_name(&mut self) -> Result<AstString, ParseError> {
1642 let mut path = vec![self.expect_ident()?];
1643 while matches!(self.peek_kind(), TokenKind::Dot) {
1644 self.bump();
1645 path.push(self.expect_ident()?);
1646 }
1647 Ok(path
1648 .iter()
1649 .map(AstString::as_str)
1650 .collect::<Vec<_>>()
1651 .join(".")
1652 .into())
1653 }
1654
1655 fn expect_key_name(&mut self) -> Result<AstString, ParseError> {
1656 let token = self.bump();
1657 match &token.kind {
1658 TokenKind::Ident(name) | TokenKind::String(name) => Ok(name.clone()),
1659 other => keyword_key_name(other)
1660 .map(Into::into)
1661 .ok_or_else(|| ParseError::Expected {
1662 expected: "identifier, string key, or keyword key",
1663 found: render_kind(other),
1664 span: token.span,
1665 }),
1666 }
1667 }
1668
1669 fn expect_exact(
1670 &mut self,
1671 expected_kind: TokenKind,
1672 expected: &'static str,
1673 ) -> Result<(), ParseError> {
1674 let token = self.bump();
1675 if std::mem::discriminant(&token.kind) == std::mem::discriminant(&expected_kind) {
1676 Ok(())
1677 } else {
1678 Err(ParseError::Expected {
1679 expected,
1680 found: render_kind(&token.kind),
1681 span: token.span,
1682 })
1683 }
1684 }
1685
1686 fn unexpected(&mut self) -> ParseError {
1687 let token = self.peek();
1688 ParseError::Unexpected {
1689 found: render_kind(&token.kind),
1690 span: token.span,
1691 }
1692 }
1693
1694 fn peek_assignment_target(&self) -> bool {
1695 if !matches!(self.peek_kind(), TokenKind::Ident(_)) {
1696 return false;
1697 }
1698
1699 let mut index = self.index + 1;
1700 loop {
1701 match self.tokens.get(index).map(|token| &token.kind) {
1702 Some(TokenKind::Dot) => {
1703 if !self
1704 .tokens
1705 .get(index + 1)
1706 .is_some_and(|token| token_can_be_key(&token.kind))
1707 {
1708 return false;
1709 }
1710 index += 2;
1711 }
1712 Some(TokenKind::LBracket) => {
1713 let Some(after_index) = self.skip_bracketed_index(index) else {
1714 return false;
1715 };
1716 index = after_index;
1717 }
1718 Some(TokenKind::Equal) => return true,
1719 _ => return false,
1720 }
1721 }
1722 }
1723
1724 fn skip_bracketed_index(&self, start: usize) -> Option<usize> {
1725 debug_assert!(matches!(
1726 self.tokens.get(start).map(|token| &token.kind),
1727 Some(TokenKind::LBracket)
1728 ));
1729 let mut parens = 0usize;
1730 let mut brackets = 1usize;
1731 let mut braces = 0usize;
1732 for (offset, token) in self.tokens.iter().enumerate().skip(start + 1) {
1733 match &token.kind {
1734 TokenKind::LParen => parens += 1,
1735 TokenKind::RParen => parens = parens.checked_sub(1)?,
1736 TokenKind::LBracket => brackets += 1,
1737 TokenKind::RBracket => {
1738 brackets = brackets.checked_sub(1)?;
1739 if brackets == 0 && parens == 0 && braces == 0 {
1740 return Some(offset + 1);
1741 }
1742 }
1743 TokenKind::LBrace => braces += 1,
1744 TokenKind::RBrace => braces = braces.checked_sub(1)?,
1745 TokenKind::Eof => return None,
1746 _ => {}
1747 }
1748 }
1749 None
1750 }
1751
1752 fn question_starts_ternary(&self) -> bool {
1753 debug_assert!(matches!(self.peek_kind(), TokenKind::Question));
1754 let Some(next) = self.tokens.get(self.index + 1) else {
1755 return false;
1756 };
1757 if !token_can_start_expr(&next.kind) {
1758 return false;
1759 }
1760
1761 let mut parens = 0usize;
1762 let mut brackets = 0usize;
1763 let mut braces = 0usize;
1764 for token in self.tokens.iter().skip(self.index + 1) {
1765 match &token.kind {
1766 TokenKind::Colon if parens == 0 && brackets == 0 && braces == 0 => return true,
1767 TokenKind::Equal if parens == 0 && brackets == 0 && braces == 0 => return false,
1768 TokenKind::Comma | TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace
1769 if parens == 0 && brackets == 0 && braces == 0 =>
1770 {
1771 return false;
1772 }
1773 TokenKind::Eof => return false,
1774 TokenKind::LParen => parens += 1,
1775 TokenKind::RParen => {
1776 if parens == 0 {
1777 return false;
1778 }
1779 parens -= 1;
1780 }
1781 TokenKind::LBracket => brackets += 1,
1782 TokenKind::RBracket => {
1783 if brackets == 0 {
1784 return false;
1785 }
1786 brackets -= 1;
1787 }
1788 TokenKind::LBrace => braces += 1,
1789 TokenKind::RBrace => {
1790 if braces == 0 {
1791 return false;
1792 }
1793 braces -= 1;
1794 }
1795 _ => {}
1796 }
1797 }
1798 false
1799 }
1800
1801 fn paren_group_followed_by_lbrace(&self) -> bool {
1802 if !matches!(self.peek_kind(), TokenKind::LParen) {
1803 return false;
1804 }
1805 let mut depth = 0usize;
1806 for (index, token) in self.tokens.iter().enumerate().skip(self.index) {
1807 match &token.kind {
1808 TokenKind::LParen => depth += 1,
1809 TokenKind::RParen => {
1810 depth = depth.saturating_sub(1);
1811 if depth == 0 {
1812 return self
1813 .tokens
1814 .get(index + 1)
1815 .is_some_and(|next| matches!(next.kind, TokenKind::LBrace));
1816 }
1817 }
1818 TokenKind::Eof => return false,
1819 _ => {}
1820 }
1821 }
1822 false
1823 }
1824
1825 fn peek_contextual(&self, keyword: &str) -> bool {
1826 matches!(self.peek_kind(), TokenKind::Ident(name) if name.as_str() == keyword)
1827 }
1828
1829 fn expect_contextual(&mut self, keyword: &'static str) -> Result<(), ParseError> {
1830 let token = self.bump();
1831 match &token.kind {
1832 TokenKind::Ident(name) if name.as_str() == keyword => Ok(()),
1833 other => Err(ParseError::Expected {
1834 expected: keyword,
1835 found: render_kind(other),
1836 span: token.span,
1837 }),
1838 }
1839 }
1840
1841 fn at_eof(&self) -> bool {
1842 matches!(self.peek_kind(), TokenKind::Eof)
1843 }
1844
1845 fn peek_kind(&self) -> &TokenKind {
1846 &self.tokens[self.index].kind
1847 }
1848
1849 fn peek(&self) -> &Token {
1850 &self.tokens[self.index]
1851 }
1852
1853 fn bump(&mut self) -> &Token {
1854 let token = &self.tokens[self.index];
1855 self.index += 1;
1856 token
1857 }
1858}
1859
1860fn static_signal_name_arg(expr: &Expr, call: &'static str) -> Result<AstString, ParseError> {
1861 if let Expr::String(name) = expr {
1862 return Ok(name.clone());
1863 }
1864 Err(ParseError::Unexpected {
1865 found: format!("non-literal signal name in `{call}`"),
1866 span: Span { start: 0, end: 0 },
1867 })
1868}
1869
1870fn token_can_be_key(kind: &TokenKind) -> bool {
1871 matches!(kind, TokenKind::Ident(_) | TokenKind::String(_)) || keyword_key_name(kind).is_some()
1872}
1873
1874fn keyword_key_name(kind: &TokenKind) -> Option<&'static str> {
1875 Some(match kind {
1876 TokenKind::If => "if",
1877 TokenKind::Else => "else",
1878 TokenKind::For => "for",
1879 TokenKind::In => "in",
1880 TokenKind::Await => "await",
1881 TokenKind::Cancel => "cancel",
1882 TokenKind::Submit => "submit",
1883 TokenKind::Print => "print",
1884 TokenKind::Call => "call",
1885 TokenKind::Ident(name) if matches!(name.as_str(), "yield" | "wake" | "finish" | "fail") => {
1886 return Some(match name.as_str() {
1887 "yield" => "yield",
1888 "wake" => "wake",
1889 "finish" => "finish",
1890 "fail" => "fail",
1891 _ => unreachable!(),
1892 });
1893 }
1894 TokenKind::And => "and",
1895 TokenKind::Or => "or",
1896 TokenKind::Not => "not",
1897 TokenKind::True => "true",
1898 TokenKind::False => "false",
1899 TokenKind::Null => "null",
1900 _ => return None,
1901 })
1902}
1903
1904fn token_can_start_expr(kind: &TokenKind) -> bool {
1905 matches!(
1906 kind,
1907 TokenKind::Null
1908 | TokenKind::True
1909 | TokenKind::False
1910 | TokenKind::Number(_)
1911 | TokenKind::String(_)
1912 | TokenKind::Ident(_)
1913 | TokenKind::LParen
1914 | TokenKind::LBracket
1915 | TokenKind::LBrace
1916 | TokenKind::Await
1917 | TokenKind::Minus
1918 | TokenKind::Bang
1919 | TokenKind::Not
1920 )
1921}
1922
1923fn render_kind(kind: &TokenKind) -> String {
1924 match kind {
1925 TokenKind::Ident(name) => format!("identifier `{name}`"),
1926 TokenKind::String(value) => format!("string {:?}", value),
1927 TokenKind::Number(value) => format!("number {value}"),
1928 TokenKind::LBrace => "`{`".to_string(),
1929 TokenKind::RBrace => "`}`".to_string(),
1930 TokenKind::LParen => "`(`".to_string(),
1931 TokenKind::RParen => "`)`".to_string(),
1932 TokenKind::LBracket => "`[`".to_string(),
1933 TokenKind::RBracket => "`]`".to_string(),
1934 TokenKind::Comma => "`,`".to_string(),
1935 TokenKind::Colon => "`:`".to_string(),
1936 TokenKind::At => "`@`".to_string(),
1937 TokenKind::Question => "`?`".to_string(),
1938 TokenKind::Dot => "`.`".to_string(),
1939 TokenKind::Bang => "`!`".to_string(),
1940 TokenKind::Equal => "`=`".to_string(),
1941 TokenKind::DoubleEqual => "`==`".to_string(),
1942 TokenKind::BangEqual => "`!=`".to_string(),
1943 TokenKind::AndAnd => "`&&`".to_string(),
1944 TokenKind::OrOr => "`||`".to_string(),
1945 TokenKind::Pipe => "`|`".to_string(),
1946 TokenKind::Less => "`<`".to_string(),
1947 TokenKind::LessEqual => "`<=`".to_string(),
1948 TokenKind::Greater => "`>`".to_string(),
1949 TokenKind::GreaterEqual => "`>=`".to_string(),
1950 TokenKind::Plus => "`+`".to_string(),
1951 TokenKind::Minus => "`-`".to_string(),
1952 TokenKind::Star => "`*`".to_string(),
1953 TokenKind::Slash => "`/`".to_string(),
1954 TokenKind::Percent => "`%`".to_string(),
1955 TokenKind::If => "`if`".to_string(),
1956 TokenKind::Else => "`else`".to_string(),
1957 TokenKind::For => "`for`".to_string(),
1958 TokenKind::In => "`in`".to_string(),
1959 TokenKind::Await => "`await`".to_string(),
1960 TokenKind::Cancel => "`cancel`".to_string(),
1961 TokenKind::Submit => "`submit`".to_string(),
1962 TokenKind::Print => "`print`".to_string(),
1963 TokenKind::Call => "`call`".to_string(),
1964 TokenKind::And => "`and`".to_string(),
1965 TokenKind::Or => "`or`".to_string(),
1966 TokenKind::Not => "`not`".to_string(),
1967 TokenKind::True => "`true`".to_string(),
1968 TokenKind::False => "`false`".to_string(),
1969 TokenKind::Null => "`null`".to_string(),
1970 TokenKind::Eof => "end of input".to_string(),
1971 }
1972}
1973
1974include!("parser/tests.rs");