1use bock_ast::{
10 Annotation, AnnotationArg, Arg, AssignOp, AssociatedType, BinOp, Block, ClassDecl, ConstDecl,
11 EffectDecl, EnumDecl, EnumVariant, Expr, FnDecl, ForLoop, GenericParam, GuardStmt, HandlerPair,
12 HandlingBlock, Ident, ImplBlock, ImportDecl, ImportItems, ImportedName, InterpolationPart,
13 Item, LetStmt, Literal, MatchArm, Module, ModuleHandleDecl, ModulePath, NodeId, Param, Pattern,
14 RecordDecl, RecordDeclField, RecordField, RecordPatternField, RecordSpread, Stmt, TraitDecl,
15 TypeAliasDecl, TypeConstraint, TypeExpr, TypePath, UnaryOp, Visibility, WhileLoop,
16};
17use bock_errors::{DiagnosticBag, DiagnosticCode, Span};
18use bock_lexer::{Token, TokenKind};
19use bock_source::SourceFile;
20
21#[derive(Debug, Clone, PartialEq)]
23enum OpTag {
24 Assign(AssignOp),
25 Pipe,
26 Compose,
27 Range,
28 RangeInclusive,
29 Binary(BinOp),
30 Is,
31}
32
33pub struct Parser<'src> {
37 tokens: Vec<Token>,
38 pos: usize,
39 source: &'src SourceFile,
40 diagnostics: DiagnosticBag,
41 next_id: NodeId,
42 consecutive_errors: usize,
44}
45
46impl<'src> Parser<'src> {
47 #[must_use]
51 pub fn new(tokens: Vec<Token>, source: &'src SourceFile) -> Self {
52 assert!(
53 !tokens.is_empty(),
54 "token list must contain at least an EOF token"
55 );
56 Self {
57 tokens,
58 pos: 0,
59 source,
60 diagnostics: DiagnosticBag::new(),
61 next_id: 0,
62 consecutive_errors: 0,
63 }
64 }
65
66 pub fn parse_module(&mut self) -> Module {
68 let start_span = self.peek().span;
69
70 self.skip_newlines();
73 let mut doc = Vec::new();
74 while self.at(TokenKind::ModuleDocComment) {
75 let tok = self.advance();
76 if let Some(text) = tok.literal {
77 doc.push(text);
78 }
79 self.skip_newlines();
80 }
81 self.skip_newlines();
82
83 let path = if self.at(TokenKind::Module) {
85 Some(self.parse_module_decl())
86 } else {
87 None
88 };
89 self.skip_newlines();
90
91 while self.at(TokenKind::ModuleDocComment) {
95 let tok = self.advance();
96 if let Some(text) = tok.literal {
97 doc.push(text);
98 }
99 self.skip_newlines();
100 }
101 self.skip_newlines();
102
103 let mut imports = Vec::new();
105 loop {
106 self.skip_newlines();
107 if self.at(TokenKind::Use) {
108 imports.push(self.parse_import_decl(Visibility::Private));
109 } else if self.at_visibility() && self.peek_kind_at(1) == Some(TokenKind::Use) {
110 let vis = self.parse_visibility();
111 imports.push(self.parse_import_decl(vis));
112 } else {
113 break;
114 }
115 }
116 self.skip_newlines();
117
118 let items = self.parse_items();
120
121 let end_span = self.peek().span;
122 Module {
123 id: self.alloc_id(),
124 span: Span::merge(start_span, end_span),
125 doc,
126 path,
127 imports,
128 items,
129 }
130 }
131
132 #[must_use]
134 pub fn diagnostics(&self) -> &DiagnosticBag {
135 &self.diagnostics
136 }
137
138 pub(crate) fn peek(&self) -> &Token {
142 &self.tokens[self.pos]
143 }
144
145 pub(crate) fn advance(&mut self) -> Token {
147 let tok = self.tokens[self.pos].clone();
148 if self.pos + 1 < self.tokens.len() {
149 self.pos += 1;
150 }
151 tok
152 }
153
154 #[must_use]
156 pub(crate) fn at(&self, kind: TokenKind) -> bool {
157 self.peek().kind == kind
158 }
159
160 pub(crate) fn expect(&mut self, kind: TokenKind) -> Result<Token, ()> {
162 if self.at(kind.clone()) {
163 Ok(self.advance())
164 } else {
165 let span = self.peek().span;
166 let found = self.peek().kind.clone();
167 self.diagnostics.error(
168 DiagnosticCode {
169 prefix: 'E',
170 number: 2000,
171 },
172 format!("expected `{kind}`, found `{found}`"),
173 span,
174 );
175 Err(())
176 }
177 }
178
179 pub(crate) fn skip_newlines(&mut self) {
181 while self.at(TokenKind::Newline) {
182 let _ = self.advance();
183 }
184 }
185
186 fn peek_past_newlines_kind(&self) -> Option<TokenKind> {
189 let mut i = self.pos;
190 while i < self.tokens.len() && self.tokens[i].kind == TokenKind::Newline {
191 i += 1;
192 }
193 self.tokens.get(i).map(|t| t.kind.clone())
194 }
195
196 fn alloc_id(&mut self) -> NodeId {
199 let id = self.next_id;
200 self.next_id += 1;
201 id
202 }
203
204 fn peek_kind_at(&self, offset: usize) -> Option<TokenKind> {
206 self.tokens.get(self.pos + offset).map(|t| t.kind.clone())
207 }
208
209 fn at_visibility(&self) -> bool {
210 matches!(self.peek().kind, TokenKind::Public | TokenKind::Internal)
211 }
212
213 fn at_decl_start(&self) -> bool {
215 matches!(
216 self.peek().kind,
217 TokenKind::Fn
218 | TokenKind::Async
219 | TokenKind::Record
220 | TokenKind::Enum
221 | TokenKind::Class
222 | TokenKind::Trait
223 | TokenKind::Platform
224 | TokenKind::Impl
225 | TokenKind::Effect
226 | TokenKind::Const
227 | TokenKind::Type
228 | TokenKind::Use
229 | TokenKind::Handle
230 | TokenKind::At
231 | TokenKind::Public
232 | TokenKind::Internal
233 )
234 }
235
236 fn synchronize(&mut self) -> Span {
241 let start = self.peek().span;
242 let mut end = start;
243 while !self.at(TokenKind::Eof) {
244 let kind = self.peek().kind.clone();
245 match kind {
246 TokenKind::Semicolon | TokenKind::RBrace => {
248 let _ = self.advance(); end = self.peek().span;
250 break;
251 }
252 TokenKind::Newline => {
253 let _ = self.advance();
254 if self.at_decl_start() || self.at(TokenKind::Eof) {
256 break;
257 }
258 }
259 _ if self.at_decl_start() => break,
260 _ => {
261 end = self.peek().span;
262 let _ = self.advance();
263 }
264 }
265 }
266 Span::merge(start, end)
267 }
268
269 fn synchronize_to_top_level(&mut self) -> Span {
274 let start = self.peek().span;
275 let mut end = start;
276 while !self.at(TokenKind::Eof) {
277 self.skip_newlines();
278 if self.at(TokenKind::Eof) || self.at_decl_start() {
279 break;
280 }
281 end = self.peek().span;
282 let _ = self.advance();
283 }
284 Span::merge(start, end)
285 }
286
287 fn parse_visibility(&mut self) -> Visibility {
288 match self.peek().kind {
289 TokenKind::Public => {
290 let _ = self.advance();
291 Visibility::Public
292 }
293 TokenKind::Internal => {
294 let _ = self.advance();
295 Visibility::Internal
296 }
297 _ => Visibility::Private,
298 }
299 }
300
301 fn parse_module_decl(&mut self) -> ModulePath {
305 let _ = self.advance(); let path = self.parse_module_path();
307 if self.at(TokenKind::Newline) {
308 let _ = self.advance();
309 }
310 path
311 }
312
313 fn parse_module_path(&mut self) -> ModulePath {
315 let start = self.peek().span;
316 let mut segments = Vec::new();
317
318 if let Some(seg) = self.try_parse_path_segment() {
319 segments.push(seg);
320 }
321
322 while self.at(TokenKind::Dot) {
324 match self.peek_kind_at(1) {
325 Some(kind) if Self::is_path_segment_token(&kind) => {
326 let _ = self.advance(); if let Some(seg) = self.try_parse_path_segment() {
328 segments.push(seg);
329 }
330 }
331 _ => break,
332 }
333 }
334
335 let end = segments.last().map(|s| s.span).unwrap_or(start);
336 ModulePath {
337 span: Span::merge(start, end),
338 segments,
339 }
340 }
341
342 fn is_path_segment_token(kind: &TokenKind) -> bool {
352 matches!(
353 kind,
354 TokenKind::Ident
355 | TokenKind::TypeIdent
356 | TokenKind::Effect
357 | TokenKind::Handle
358 | TokenKind::Handling
359 )
360 }
361
362 fn try_parse_path_segment(&mut self) -> Option<Ident> {
369 if Self::is_path_segment_token(&self.peek().kind) {
370 let tok = self.advance();
371 let name = tok.literal.unwrap_or_else(|| tok.kind.to_string());
375 Some(Ident {
376 name,
377 span: tok.span,
378 })
379 } else {
380 let span = self.peek().span;
381 let found = self.peek().kind.clone();
382 self.diagnostics.error(
383 DiagnosticCode {
384 prefix: 'E',
385 number: 2001,
386 },
387 format!("expected identifier in path, found `{found}`"),
388 span,
389 );
390 None
391 }
392 }
393
394 fn parse_import_decl(&mut self, vis: Visibility) -> ImportDecl {
398 let start = self.peek().span;
399 let _ = self.advance(); let path = self.parse_import_base_path();
403
404 let items = self.parse_import_items();
406
407 if self.at(TokenKind::Newline) {
408 let _ = self.advance();
409 }
410
411 let end = self.peek().span;
412 ImportDecl {
413 id: self.alloc_id(),
414 span: Span::merge(start, end),
415 visibility: vis,
416 path,
417 items,
418 }
419 }
420
421 fn parse_import_base_path(&mut self) -> ModulePath {
425 let start = self.peek().span;
426 let mut segments = Vec::new();
427
428 if let Some(seg) = self.try_parse_path_segment() {
429 segments.push(seg);
430 }
431
432 while self.at(TokenKind::Dot) {
433 match self.peek_kind_at(1) {
434 Some(TokenKind::LBrace) | Some(TokenKind::Star) => break,
436 Some(kind) if Self::is_path_segment_token(&kind) => {
438 let _ = self.advance(); if let Some(seg) = self.try_parse_path_segment() {
440 segments.push(seg);
441 }
442 }
443 _ => break,
444 }
445 }
446
447 let end = segments.last().map(|s| s.span).unwrap_or(start);
448 ModulePath {
449 span: Span::merge(start, end),
450 segments,
451 }
452 }
453
454 fn parse_import_items(&mut self) -> ImportItems {
463 if !self.at(TokenKind::Dot) {
464 return ImportItems::Module;
465 }
466
467 match self.peek_kind_at(1) {
468 Some(TokenKind::Star) => {
469 let _ = self.advance(); let _ = self.advance(); ImportItems::Glob
472 }
473 Some(TokenKind::LBrace) => {
474 let _ = self.advance(); let _ = self.advance(); let names = self.parse_named_import_list();
477 let _ = self.expect(TokenKind::RBrace);
478 ImportItems::Named(names)
479 }
480 Some(TokenKind::Ident) | Some(TokenKind::TypeIdent) => {
481 let _ = self.advance(); let tok = self.advance(); let span = tok.span;
484 let name = Ident {
485 name: tok.literal.unwrap_or_default(),
486 span,
487 };
488 ImportItems::Named(vec![ImportedName {
489 span,
490 name,
491 alias: None,
492 }])
493 }
494 _ => ImportItems::Module,
495 }
496 }
497
498 fn parse_named_import_list(&mut self) -> Vec<ImportedName> {
500 let mut names = Vec::new();
501 self.skip_newlines();
502
503 while !self.at(TokenKind::RBrace) && !self.at(TokenKind::Eof) {
504 if !matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
505 break;
506 }
507 let tok = self.advance();
508 let start_span = tok.span;
509 let name = Ident {
510 name: tok.literal.unwrap_or_default(),
511 span: tok.span,
512 };
513
514 let alias = if self.at(TokenKind::Ident) && self.peek().literal.as_deref() == Some("as")
516 {
517 let _ = self.advance(); if matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
519 let alias_tok = self.advance();
520 Some(Ident {
521 name: alias_tok.literal.unwrap_or_default(),
522 span: alias_tok.span,
523 })
524 } else {
525 None
526 }
527 } else {
528 None
529 };
530
531 let end_span = alias.as_ref().map(|a| a.span).unwrap_or(start_span);
532 names.push(ImportedName {
533 span: Span::merge(start_span, end_span),
534 name,
535 alias,
536 });
537
538 self.skip_newlines();
539 if self.at(TokenKind::Comma) {
540 let _ = self.advance();
541 self.skip_newlines();
542 } else {
543 break;
544 }
545 }
546
547 names
548 }
549
550 fn parse_items(&mut self) -> Vec<Item> {
554 let mut items = Vec::new();
555
556 loop {
557 self.skip_newlines();
558 if self.at(TokenKind::Eof) {
559 break;
560 }
561
562 while self.at(TokenKind::DocComment) || self.at(TokenKind::ModuleDocComment) {
566 let _ = self.advance();
567 self.skip_newlines();
568 }
569 if self.at(TokenKind::Eof) {
570 break;
571 }
572
573 let mut annotations = Vec::new();
575 while self.at(TokenKind::At) {
576 annotations.push(self.parse_annotation());
577 self.skip_newlines();
578 }
579
580 let vis = if self.at_visibility() {
582 self.parse_visibility()
583 } else {
584 Visibility::Private
585 };
586
587 let error_count_before = self.diagnostics.error_count();
588
589 let item = match self.peek().kind.clone() {
591 TokenKind::Fn | TokenKind::Async => Item::Fn(self.parse_fn_decl(annotations, vis)),
592 TokenKind::Record => Item::Record(self.parse_record_decl(annotations, vis)),
593 TokenKind::Enum => Item::Enum(self.parse_enum_decl(annotations, vis)),
594 TokenKind::Class => Item::Class(self.parse_class_decl(annotations, vis)),
595 TokenKind::Trait => Item::Trait(self.parse_trait_decl(annotations, vis, false)),
596 TokenKind::Platform => {
597 Item::PlatformTrait(self.parse_platform_trait_decl(annotations, vis))
599 }
600 TokenKind::Impl => Item::Impl(self.parse_impl_block(annotations)),
601 TokenKind::Effect => Item::Effect(self.parse_effect_decl(annotations, vis)),
602 TokenKind::Handle => Item::ModuleHandle(self.parse_module_handle_decl()),
603 TokenKind::Type => Item::TypeAlias(self.parse_type_alias_decl(annotations, vis)),
604 TokenKind::Const => Item::Const(self.parse_const_decl(annotations, vis)),
605 _ => {
606 if self.at(TokenKind::Eof) {
607 break;
608 }
609 let span = self.peek().span;
611 let found = self.peek().kind.clone();
612 self.diagnostics.error(
613 DiagnosticCode {
614 prefix: 'E',
615 number: 2050,
616 },
617 format!("unexpected token `{found}` at top level"),
618 span,
619 );
620 self.consecutive_errors += 1;
621 let error_span = if self.consecutive_errors >= 3 {
622 self.consecutive_errors = 0;
624 self.synchronize_to_top_level()
625 } else {
626 self.synchronize()
627 };
628 let id = self.alloc_id();
629 items.push(Item::Error {
630 id,
631 span: error_span,
632 });
633 continue;
634 }
635 };
636
637 if self.diagnostics.error_count() == error_count_before {
639 self.consecutive_errors = 0;
640 } else {
641 self.consecutive_errors += 1;
642 }
643
644 items.push(item);
645 }
646
647 items
648 }
649
650 fn parse_annotation(&mut self) -> Annotation {
654 let start = self.peek().span;
655 let _ = self.advance(); let name_span = self.peek().span;
658 let name = if matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
659 let tok = self.advance();
660 Ident {
661 name: tok.literal.unwrap_or_default(),
662 span: tok.span,
663 }
664 } else {
665 self.diagnostics.error(
666 DiagnosticCode {
667 prefix: 'E',
668 number: 2002,
669 },
670 format!("expected annotation name, found `{}`", self.peek().kind),
671 name_span,
672 );
673 Ident {
674 name: String::new(),
675 span: name_span,
676 }
677 };
678
679 let mut args = Vec::new();
680 if self.at(TokenKind::LParen) {
681 let _ = self.advance(); self.skip_newlines();
683 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
684 let label = if self.at(TokenKind::Ident)
686 && self.peek_kind_at(1) == Some(TokenKind::Colon)
687 {
688 let lbl_tok = self.advance();
689 let _ = self.advance(); Some(Ident {
691 name: lbl_tok.literal.unwrap_or_default(),
692 span: lbl_tok.span,
693 })
694 } else {
695 None
696 };
697 args.push(AnnotationArg {
698 label,
699 value: self.parse_expr_stub(),
700 });
701 self.skip_newlines();
702 if self.at(TokenKind::Comma) {
703 let _ = self.advance();
704 self.skip_newlines();
705 } else {
706 break;
707 }
708 }
709 let _ = self.expect(TokenKind::RParen);
710 }
711
712 let end = self.peek().span;
713 Annotation {
714 id: self.alloc_id(),
715 span: Span::merge(start, end),
716 name,
717 args,
718 }
719 }
720
721 fn parse_generic_params(&mut self) -> Vec<GenericParam> {
725 if !self.at(TokenKind::LBracket) {
726 return Vec::new();
727 }
728 let _ = self.advance(); let mut params = Vec::new();
731 self.skip_newlines();
732
733 while !self.at(TokenKind::RBracket) && !self.at(TokenKind::Eof) {
734 if !matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
735 break;
736 }
737 let id = self.alloc_id();
738 let start = self.peek().span;
739 let tok = self.advance();
740 let name = Ident {
741 name: tok.literal.unwrap_or_default(),
742 span: tok.span,
743 };
744
745 let bounds = if self.at(TokenKind::Colon) {
747 let _ = self.advance();
748 vec![self.parse_type_path()]
749 } else {
750 Vec::new()
751 };
752
753 let end = bounds.last().map(|b| b.span).unwrap_or(name.span);
754 params.push(GenericParam {
755 id,
756 span: Span::merge(start, end),
757 name,
758 bounds,
759 });
760
761 self.skip_newlines();
762 if self.at(TokenKind::Comma) {
763 let _ = self.advance();
764 self.skip_newlines();
765 } else {
766 break;
767 }
768 }
769
770 let _ = self.expect(TokenKind::RBracket);
771 params
772 }
773
774 fn parse_where_clause(&mut self) -> Vec<TypeConstraint> {
776 if !self.at(TokenKind::Where) {
777 return Vec::new();
778 }
779 let _ = self.advance(); let _ = self.expect(TokenKind::LParen);
782 let mut constraints = Vec::new();
783 self.skip_newlines();
784
785 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
786 if !matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
787 break;
788 }
789 let id = self.alloc_id();
790 let start = self.peek().span;
791 let tok = self.advance();
792 let param = Ident {
793 name: tok.literal.unwrap_or_default(),
794 span: tok.span,
795 };
796
797 let _ = self.expect(TokenKind::Colon);
798 let bounds = vec![self.parse_type_path()];
799
800 let end = bounds.last().map(|b| b.span).unwrap_or(param.span);
801 constraints.push(TypeConstraint {
802 id,
803 span: Span::merge(start, end),
804 param,
805 bounds,
806 });
807
808 self.skip_newlines();
809 if self.at(TokenKind::Comma) {
810 let _ = self.advance();
811 self.skip_newlines();
812 } else {
813 break;
814 }
815 }
816
817 let _ = self.expect(TokenKind::RParen);
818 constraints
819 }
820
821 fn parse_type_expr(&mut self) -> TypeExpr {
825 let id = self.alloc_id();
826 let start = self.peek().span;
827
828 let base = match self.peek().kind.clone() {
829 TokenKind::LParen => {
830 let _ = self.advance(); self.skip_newlines();
832
833 if self.at(TokenKind::RParen) {
834 let end = self.advance().span;
836 TypeExpr::Tuple {
837 id,
838 span: Span::merge(start, end),
839 elems: vec![],
840 }
841 } else {
842 let mut elems = Vec::new();
843 elems.push(self.parse_type_expr());
844 self.skip_newlines();
845 while self.at(TokenKind::Comma) {
846 let _ = self.advance();
847 self.skip_newlines();
848 if self.at(TokenKind::RParen) {
849 break;
850 }
851 elems.push(self.parse_type_expr());
852 self.skip_newlines();
853 }
854 let end = self
855 .expect(TokenKind::RParen)
856 .map(|t| t.span)
857 .unwrap_or(start);
858
859 if self.at(TokenKind::ThinArrow) {
861 let _ = self.advance();
862 let ret = self.parse_type_expr();
863 TypeExpr::Function {
864 id,
865 span: Span::merge(start, ret.span()),
866 params: elems,
867 ret: Box::new(ret),
868 effects: vec![],
869 }
870 } else if elems.len() == 1 {
871 elems.remove(0)
873 } else {
874 TypeExpr::Tuple {
875 id,
876 span: Span::merge(start, end),
877 elems,
878 }
879 }
880 }
881 }
882
883 TokenKind::SelfLower | TokenKind::SelfUpper => {
884 let tok = self.advance();
885 TypeExpr::SelfType { id, span: tok.span }
886 }
887
888 TokenKind::Ident | TokenKind::TypeIdent => {
889 if self.peek().literal.as_deref() == Some("Fn")
891 && self.peek_kind_at(1) == Some(TokenKind::LParen)
892 {
893 let _ = self.advance(); let _ = self.advance(); self.skip_newlines();
896 let mut params = Vec::new();
897 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
898 params.push(self.parse_type_expr());
899 self.skip_newlines();
900 if self.at(TokenKind::Comma) {
901 let _ = self.advance();
902 self.skip_newlines();
903 } else {
904 break;
905 }
906 }
907 let _ = self.expect(TokenKind::RParen);
908 let _ = self.expect(TokenKind::ThinArrow);
909 let ret = self.parse_type_expr();
910 let effects = self.parse_effect_clause();
912 let end = effects
913 .last()
914 .map(|e: &TypePath| e.span)
915 .unwrap_or(ret.span());
916 TypeExpr::Function {
917 id,
918 span: Span::merge(start, end),
919 params,
920 ret: Box::new(ret),
921 effects,
922 }
923 } else {
924 let path = self.parse_type_path();
925 let args = if self.at(TokenKind::LBracket) {
927 let _ = self.advance();
928 let mut args = Vec::new();
929 self.skip_newlines();
930 while !self.at(TokenKind::RBracket) && !self.at(TokenKind::Eof) {
931 args.push(self.parse_type_expr());
932 self.skip_newlines();
933 if self.at(TokenKind::Comma) {
934 let _ = self.advance();
935 self.skip_newlines();
936 } else {
937 break;
938 }
939 }
940 let _ = self.expect(TokenKind::RBracket);
941 args
942 } else {
943 Vec::new()
944 };
945 let span = path.span;
946 TypeExpr::Named {
947 id,
948 span,
949 path,
950 args,
951 }
952 }
953 }
954
955 _ => {
956 self.diagnostics.error(
957 DiagnosticCode {
958 prefix: 'E',
959 number: 2010,
960 },
961 format!("expected type expression, found `{}`", self.peek().kind),
962 start,
963 );
964 TypeExpr::Named {
965 id,
966 span: start,
967 path: TypePath {
968 segments: vec![],
969 span: start,
970 },
971 args: vec![],
972 }
973 }
974 };
975
976 if self.at(TokenKind::Question) {
978 let q = self.advance();
979 let id2 = self.alloc_id();
980 TypeExpr::Optional {
981 id: id2,
982 span: Span::merge(base.span(), q.span),
983 inner: Box::new(base),
984 }
985 } else {
986 base
987 }
988 }
989
990 fn parse_type_path(&mut self) -> TypePath {
992 let start = self.peek().span;
993 let mut segments = Vec::new();
994
995 if matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
996 let tok = self.advance();
997 segments.push(Ident {
998 name: tok.literal.unwrap_or_default(),
999 span: tok.span,
1000 });
1001 }
1002
1003 while self.at(TokenKind::Dot) {
1004 match self.peek_kind_at(1) {
1005 Some(TokenKind::TypeIdent) | Some(TokenKind::Ident) => {
1006 let _ = self.advance(); let tok = self.advance();
1008 segments.push(Ident {
1009 name: tok.literal.unwrap_or_default(),
1010 span: tok.span,
1011 });
1012 }
1013 _ => break,
1014 }
1015 }
1016
1017 let end = segments.last().map(|s| s.span).unwrap_or(start);
1018 TypePath {
1019 segments,
1020 span: Span::merge(start, end),
1021 }
1022 }
1023
1024 pub(crate) fn parse_expr(&mut self) -> Expr {
1028 self.parse_prec(0)
1029 }
1030
1031 fn parse_expr_stub(&mut self) -> Expr {
1033 self.parse_expr()
1034 }
1035
1036 fn parse_prec(&mut self, min_prec: u8) -> Expr {
1039 let mut left = self.parse_unary();
1041
1042 let mut seen_comparison = false;
1047
1048 loop {
1049 if self.at(TokenKind::Newline) {
1053 match self.peek_past_newlines_kind() {
1054 Some(TokenKind::Pipe) => self.skip_newlines(),
1055 _ => break,
1056 }
1057 }
1058
1059 let Some((op_prec, right_prec, op_tok)) = self.binary_op_info() else {
1060 break;
1061 };
1062 if op_prec < min_prec {
1063 break;
1064 }
1065
1066 if op_prec == 7 {
1068 if seen_comparison {
1069 break;
1070 }
1071 seen_comparison = true;
1072 }
1073
1074 if op_tok == OpTag::Is {
1076 let _ = self.advance(); self.skip_newlines(); let ty = self.parse_type_expr();
1079 let id = self.alloc_id();
1080 let span = Span::merge(left.span(), ty.span());
1081 left = Expr::Is {
1082 id,
1083 span,
1084 expr: Box::new(left),
1085 type_expr: ty,
1086 };
1087 continue; }
1089
1090 if matches!(op_tok, OpTag::Range | OpTag::RangeInclusive) {
1092 let inclusive = op_tok == OpTag::RangeInclusive;
1093 let _ = self.advance(); self.skip_newlines(); let right = self.parse_prec(op_prec + 1);
1096 let id = self.alloc_id();
1097 let span = Span::merge(left.span(), right.span());
1098 left = Expr::Range {
1099 id,
1100 span,
1101 lo: Box::new(left),
1102 hi: Box::new(right),
1103 inclusive,
1104 };
1105 break; }
1107
1108 let _ = self.advance(); self.skip_newlines();
1111
1112 let right = self.parse_prec(right_prec);
1113 let id = self.alloc_id();
1114 let span = Span::merge(left.span(), right.span());
1115
1116 left = match op_tok {
1117 OpTag::Assign(op) => Expr::Assign {
1118 id,
1119 span,
1120 op,
1121 target: Box::new(left),
1122 value: Box::new(right),
1123 },
1124 OpTag::Pipe => Expr::Pipe {
1125 id,
1126 span,
1127 left: Box::new(left),
1128 right: Box::new(right),
1129 },
1130 OpTag::Compose => Expr::Compose {
1131 id,
1132 span,
1133 left: Box::new(left),
1134 right: Box::new(right),
1135 },
1136 OpTag::Binary(op) => Expr::Binary {
1137 id,
1138 span,
1139 op,
1140 left: Box::new(left),
1141 right: Box::new(right),
1142 },
1143 OpTag::Is | OpTag::Range | OpTag::RangeInclusive => unreachable!(),
1144 };
1145
1146 }
1149
1150 left
1151 }
1152
1153 fn binary_op_info(&self) -> Option<(u8, u8, OpTag)> {
1156 let kind = &self.peek().kind;
1173 let (prec, right_prec, tag) = match kind {
1174 TokenKind::Assign => (1, 1, OpTag::Assign(AssignOp::Assign)),
1176 TokenKind::PlusEq => (1, 1, OpTag::Assign(AssignOp::AddAssign)),
1177 TokenKind::MinusEq => (1, 1, OpTag::Assign(AssignOp::SubAssign)),
1178 TokenKind::StarEq => (1, 1, OpTag::Assign(AssignOp::MulAssign)),
1179 TokenKind::SlashEq => (1, 1, OpTag::Assign(AssignOp::DivAssign)),
1180 TokenKind::PercentEq => (1, 1, OpTag::Assign(AssignOp::RemAssign)),
1181 TokenKind::Pipe => (2, 3, OpTag::Pipe),
1183 TokenKind::Shr | TokenKind::Compose => (3, 4, OpTag::Compose),
1185 TokenKind::DotDot => (4, 5, OpTag::Range),
1187 TokenKind::DotDotEq => (4, 5, OpTag::RangeInclusive),
1188 TokenKind::Or => (5, 6, OpTag::Binary(BinOp::Or)),
1190 TokenKind::And => (6, 7, OpTag::Binary(BinOp::And)),
1192 TokenKind::Eq => (7, 8, OpTag::Binary(BinOp::Eq)),
1194 TokenKind::Neq => (7, 8, OpTag::Binary(BinOp::Ne)),
1195 TokenKind::Lt => (7, 8, OpTag::Binary(BinOp::Lt)),
1196 TokenKind::Gt => (7, 8, OpTag::Binary(BinOp::Gt)),
1197 TokenKind::Lte => (7, 8, OpTag::Binary(BinOp::Le)),
1198 TokenKind::Gte => (7, 8, OpTag::Binary(BinOp::Ge)),
1199 TokenKind::Is => (7, 8, OpTag::Is),
1200 TokenKind::BitOr => (8, 9, OpTag::Binary(BinOp::BitOr)),
1202 TokenKind::BitXor => (9, 10, OpTag::Binary(BinOp::BitXor)),
1203 TokenKind::BitAnd => (10, 11, OpTag::Binary(BinOp::BitAnd)),
1204 TokenKind::Plus => (11, 12, OpTag::Binary(BinOp::Add)),
1206 TokenKind::Minus => (11, 12, OpTag::Binary(BinOp::Sub)),
1207 TokenKind::Star => (12, 13, OpTag::Binary(BinOp::Mul)),
1209 TokenKind::Slash => (12, 13, OpTag::Binary(BinOp::Div)),
1210 TokenKind::Percent => (12, 13, OpTag::Binary(BinOp::Rem)),
1211 TokenKind::Power => (13, 13, OpTag::Binary(BinOp::Pow)),
1213 _ => return None,
1214 };
1215 Some((prec, right_prec, tag))
1216 }
1217
1218 fn parse_unary(&mut self) -> Expr {
1220 let id = self.alloc_id();
1221 let span = self.peek().span;
1222
1223 match self.peek().kind.clone() {
1224 TokenKind::Minus => {
1225 let _ = self.advance();
1226 let operand = self.parse_unary();
1227 let span = Span::merge(span, operand.span());
1228 Expr::Unary {
1229 id,
1230 span,
1231 op: UnaryOp::Neg,
1232 operand: Box::new(operand),
1233 }
1234 }
1235 TokenKind::Not => {
1236 let _ = self.advance();
1237 let operand = self.parse_unary();
1238 let span = Span::merge(span, operand.span());
1239 Expr::Unary {
1240 id,
1241 span,
1242 op: UnaryOp::Not,
1243 operand: Box::new(operand),
1244 }
1245 }
1246 TokenKind::BitNot => {
1247 let _ = self.advance();
1248 let operand = self.parse_unary();
1249 let span = Span::merge(span, operand.span());
1250 Expr::Unary {
1251 id,
1252 span,
1253 op: UnaryOp::BitNot,
1254 operand: Box::new(operand),
1255 }
1256 }
1257 _ => self.parse_postfix(),
1258 }
1259 }
1260
1261 fn parse_postfix(&mut self) -> Expr {
1263 let mut expr = self.parse_primary();
1264
1265 loop {
1266 if self.at(TokenKind::Newline) {
1269 match self.peek_past_newlines_kind() {
1270 Some(TokenKind::Dot) => self.skip_newlines(),
1271 _ => break,
1272 }
1273 }
1274
1275 match self.peek().kind.clone() {
1276 TokenKind::Question => {
1278 let end_span = self.advance().span;
1279 let id = self.alloc_id();
1280 let span = Span::merge(expr.span(), end_span);
1281 expr = Expr::Try {
1282 id,
1283 span,
1284 expr: Box::new(expr),
1285 };
1286 }
1287 TokenKind::Dot => {
1289 match self.peek_kind_at(1) {
1290 Some(TokenKind::Await) => {
1291 let _ = self.advance(); let end_span = self.advance().span; let id = self.alloc_id();
1294 let span = Span::merge(expr.span(), end_span);
1295 expr = Expr::Await {
1296 id,
1297 span,
1298 expr: Box::new(expr),
1299 };
1300 }
1301 Some(TokenKind::Ident) | Some(TokenKind::TypeIdent) => {
1302 let _ = self.advance(); let tok = self.advance(); let field = Ident {
1305 name: tok.literal.unwrap_or_default(),
1306 span: tok.span,
1307 };
1308 let type_args = self.parse_optional_type_args();
1310 if self.at(TokenKind::LParen) {
1311 let _ = self.advance(); let args = self.parse_arg_list();
1313 let _ = self.expect(TokenKind::RParen);
1314 let id = self.alloc_id();
1315 let span = Span::merge(expr.span(), self.peek().span);
1316 expr = Expr::MethodCall {
1317 id,
1318 span,
1319 receiver: Box::new(expr),
1320 method: field,
1321 type_args,
1322 args,
1323 };
1324 } else {
1325 let id = self.alloc_id();
1326 let span = Span::merge(expr.span(), field.span);
1327 expr = Expr::FieldAccess {
1328 id,
1329 span,
1330 object: Box::new(expr),
1331 field,
1332 };
1333 }
1334 }
1335 Some(TokenKind::IntLiteral) | Some(TokenKind::FloatLiteral) => {
1344 let dot_span = self.advance().span; let index_tok = self.advance(); let index = index_tok.literal.clone().unwrap_or_default();
1347 let span = Span::merge(dot_span, index_tok.span);
1348 self.diagnostics.error(
1349 DiagnosticCode {
1350 prefix: 'E',
1351 number: 2092,
1352 },
1353 format!(
1354 "tuple positional indexing `t.{index}` is not available in v1; \
1355 destructure with `let (a, b) = t` to bind tuple elements instead"
1356 ),
1357 span,
1358 );
1359 }
1363 _ => break,
1364 }
1365 }
1366 TokenKind::LParen => {
1368 let _ = self.advance(); let type_args = Vec::new(); let args = self.parse_arg_list();
1371 let end_span = self
1372 .expect(TokenKind::RParen)
1373 .map(|t| t.span)
1374 .unwrap_or_else(|_| self.peek().span);
1375 let id = self.alloc_id();
1376 let span = Span::merge(expr.span(), end_span);
1377 expr = Expr::Call {
1378 id,
1379 span,
1380 callee: Box::new(expr),
1381 args,
1382 type_args,
1383 };
1384 }
1385 TokenKind::LBracket => {
1387 if Self::expr_is_simple_type_name(&expr) && self.is_type_args_before_dot() {
1393 let _ = self.advance(); self.skip_newlines();
1395 while !self.at(TokenKind::RBracket) && !self.at(TokenKind::Eof) {
1396 let _ = self.parse_type_expr();
1397 self.skip_newlines();
1398 if self.at(TokenKind::Comma) {
1399 let _ = self.advance();
1400 self.skip_newlines();
1401 } else {
1402 break;
1403 }
1404 }
1405 let _ = self.expect(TokenKind::RBracket);
1406 continue;
1407 }
1408 let _ = self.advance(); let index = self.parse_expr();
1410 let end_span = self
1411 .expect(TokenKind::RBracket)
1412 .map(|t| t.span)
1413 .unwrap_or_else(|_| self.peek().span);
1414 let id = self.alloc_id();
1415 let span = Span::merge(expr.span(), end_span);
1416 expr = Expr::Index {
1417 id,
1418 span,
1419 object: Box::new(expr),
1420 index: Box::new(index),
1421 };
1422 }
1423 TokenKind::LBrace if self.expr_is_type_path(&expr) => {
1425 let path = self.expr_to_type_path(&expr);
1426 let record = self.parse_record_construct(expr.span(), path);
1427 expr = record;
1428 }
1429 _ => break,
1430 }
1431 }
1432
1433 expr
1434 }
1435
1436 fn expr_is_type_path(&self, expr: &Expr) -> bool {
1445 match expr {
1446 Expr::Identifier { name, .. } => Self::is_type_name(&name.name),
1447 Expr::FieldAccess { object, field, .. } => {
1448 Self::is_type_name(&field.name) && self.expr_is_type_path(object)
1449 }
1450 _ => false,
1451 }
1452 }
1453
1454 fn is_type_name(name: &str) -> bool {
1460 name.starts_with(|c: char| c.is_uppercase())
1461 && (name.len() == 1 || name.contains(|c: char| c.is_lowercase()))
1462 }
1463
1464 fn expr_is_simple_type_name(expr: &Expr) -> bool {
1468 matches!(expr, Expr::Identifier { name, .. } if Self::is_type_name(&name.name))
1469 }
1470
1471 fn is_type_args_before_dot(&self) -> bool {
1476 let mut offset = 1; loop {
1478 while self.peek_kind_at(offset) == Some(TokenKind::Newline) {
1479 offset += 1;
1480 }
1481 match self.peek_kind_at(offset) {
1482 Some(TokenKind::TypeIdent) => offset += 1,
1483 _ => return false,
1484 }
1485 while self.peek_kind_at(offset) == Some(TokenKind::Newline) {
1486 offset += 1;
1487 }
1488 match self.peek_kind_at(offset) {
1489 Some(TokenKind::Comma) => {
1490 offset += 1;
1491 }
1492 Some(TokenKind::RBracket) => {
1493 offset += 1;
1494 if self.peek_kind_at(offset) != Some(TokenKind::Dot) {
1495 return false;
1496 }
1497 offset += 1;
1498 return matches!(
1499 self.peek_kind_at(offset),
1500 Some(TokenKind::Ident) | Some(TokenKind::TypeIdent)
1501 );
1502 }
1503 _ => return false,
1504 }
1505 }
1506 }
1507
1508 fn expr_to_type_path(&self, expr: &Expr) -> TypePath {
1510 match expr {
1511 Expr::Identifier { name, span, .. } => TypePath {
1512 segments: vec![name.clone()],
1513 span: *span,
1514 },
1515 Expr::FieldAccess {
1516 object,
1517 field,
1518 span,
1519 ..
1520 } => {
1521 let mut path = self.expr_to_type_path(object);
1522 path.segments.push(field.clone());
1523 path.span = *span;
1524 path
1525 }
1526 _ => TypePath {
1527 segments: vec![],
1528 span: expr.span(),
1529 },
1530 }
1531 }
1532
1533 fn parse_optional_type_args(&mut self) -> Vec<TypeExpr> {
1535 if self.at(TokenKind::LBracket) && !self.is_index_bracket() {
1536 let _ = self.advance(); let mut args = Vec::new();
1538 self.skip_newlines();
1539 while !self.at(TokenKind::RBracket) && !self.at(TokenKind::Eof) {
1540 args.push(self.parse_type_expr());
1541 self.skip_newlines();
1542 if self.at(TokenKind::Comma) {
1543 let _ = self.advance();
1544 self.skip_newlines();
1545 } else {
1546 break;
1547 }
1548 }
1549 let _ = self.expect(TokenKind::RBracket);
1550 args
1551 } else {
1552 Vec::new()
1553 }
1554 }
1555
1556 fn is_index_bracket(&self) -> bool {
1562 let mut offset = 1; loop {
1565 while self.peek_kind_at(offset) == Some(TokenKind::Newline) {
1567 offset += 1;
1568 }
1569
1570 match self.peek_kind_at(offset) {
1572 Some(TokenKind::TypeIdent) => offset += 1,
1573 _ => return true, }
1575
1576 while self.peek_kind_at(offset) == Some(TokenKind::Newline) {
1578 offset += 1;
1579 }
1580
1581 match self.peek_kind_at(offset) {
1583 Some(TokenKind::Comma) => offset += 1,
1584 Some(TokenKind::RBracket) => {
1585 offset += 1;
1586 return !matches!(self.peek_kind_at(offset), Some(TokenKind::LParen));
1588 }
1589 _ => return true,
1590 }
1591 }
1592 }
1593
1594 fn parse_arg_list(&mut self) -> Vec<Arg> {
1596 let mut args = Vec::new();
1597 self.skip_newlines();
1598
1599 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
1600 let start = self.peek().span;
1601
1602 let mutable = if self.at(TokenKind::Mut) {
1604 let _ = self.advance();
1605 true
1606 } else {
1607 false
1608 };
1609
1610 let (label, value) =
1612 if self.at(TokenKind::Ident) && self.peek_kind_at(1) == Some(TokenKind::Colon) {
1613 let tok = self.advance();
1614 let label = Ident {
1615 name: tok.literal.unwrap_or_default(),
1616 span: tok.span,
1617 };
1618 let _ = self.advance(); let value = self.parse_expr();
1620 (Some(label), value)
1621 } else {
1622 let value = self.parse_expr();
1623 (None, value)
1624 };
1625
1626 let end = value.span();
1627 args.push(Arg {
1628 span: Span::merge(start, end),
1629 label,
1630 mutable,
1631 value,
1632 });
1633
1634 self.skip_newlines();
1635 if self.at(TokenKind::Comma) {
1636 let _ = self.advance();
1637 self.skip_newlines();
1638 } else {
1639 break;
1640 }
1641 }
1642
1643 args
1644 }
1645
1646 fn parse_record_construct(&mut self, start: Span, path: TypePath) -> Expr {
1648 let _ = self.advance(); let mut fields = Vec::new();
1650 let mut spread = None;
1651
1652 self.skip_newlines();
1653 while !self.at(TokenKind::RBrace) && !self.at(TokenKind::Eof) {
1654 if self.at(TokenKind::DotDot) {
1656 let spread_start = self.advance().span; let expr = self.parse_expr();
1658 let span = Span::merge(spread_start, expr.span());
1659 spread = Some(Box::new(RecordSpread { span, expr }));
1660 self.skip_newlines();
1661 break;
1662 }
1663
1664 let field_span = self.peek().span;
1666 if !self.at(TokenKind::Ident) {
1667 break;
1668 }
1669 let tok = self.advance();
1670 let name = Ident {
1671 name: tok.literal.unwrap_or_default(),
1672 span: tok.span,
1673 };
1674
1675 let value = if self.at(TokenKind::Colon) {
1676 let _ = self.advance(); Some(self.parse_expr())
1678 } else {
1679 None };
1681
1682 let field_end = value.as_ref().map(|v| v.span()).unwrap_or(field_span);
1683 fields.push(RecordField {
1684 span: Span::merge(field_span, field_end),
1685 name,
1686 value,
1687 });
1688
1689 self.skip_newlines();
1690 if self.at(TokenKind::Comma) {
1691 let _ = self.advance();
1692 self.skip_newlines();
1693 }
1694 }
1695
1696 let end_span = self
1697 .expect(TokenKind::RBrace)
1698 .map(|t| t.span)
1699 .unwrap_or(start);
1700 let id = self.alloc_id();
1701 Expr::RecordConstruct {
1702 id,
1703 span: Span::merge(start, end_span),
1704 path,
1705 fields,
1706 spread,
1707 }
1708 }
1709
1710 fn parse_primary(&mut self) -> Expr {
1713 let id = self.alloc_id();
1714 let span = self.peek().span;
1715
1716 match self.peek().kind.clone() {
1717 TokenKind::IntLiteral => {
1719 let tok = self.advance();
1720 Expr::Literal {
1721 id,
1722 span,
1723 lit: Literal::Int(tok.literal.unwrap_or_default()),
1724 }
1725 }
1726 TokenKind::FloatLiteral => {
1727 let tok = self.advance();
1728 Expr::Literal {
1729 id,
1730 span,
1731 lit: Literal::Float(tok.literal.unwrap_or_default()),
1732 }
1733 }
1734 TokenKind::StringLiteral
1735 | TokenKind::RawStringLiteral
1736 | TokenKind::MultiLineStringLiteral
1737 | TokenKind::RawMultiLineStringLiteral => {
1738 let tok = self.advance();
1739 Expr::Literal {
1740 id,
1741 span,
1742 lit: Literal::String(tok.literal.unwrap_or_default()),
1743 }
1744 }
1745 TokenKind::BoolLiteral => {
1746 let tok = self.advance();
1747 let value = self.source.slice(tok.span) == "true";
1749 Expr::Literal {
1750 id,
1751 span,
1752 lit: Literal::Bool(value),
1753 }
1754 }
1755 TokenKind::CharLiteral => {
1756 let tok = self.advance();
1757 Expr::Literal {
1758 id,
1759 span,
1760 lit: Literal::Char(tok.literal.unwrap_or_default()),
1761 }
1762 }
1763 TokenKind::StringLiteralPart | TokenKind::InterpolationStart => {
1765 self.parse_interpolation(id, span)
1766 }
1767 TokenKind::Ident => {
1769 let tok = self.advance();
1770 let name = Ident {
1771 name: tok.literal.unwrap_or_default(),
1772 span: tok.span,
1773 };
1774 Expr::Identifier {
1775 id,
1776 span: tok.span,
1777 name,
1778 }
1779 }
1780 TokenKind::TypeIdent
1781 | TokenKind::Ok_
1782 | TokenKind::Err_
1783 | TokenKind::Some_
1784 | TokenKind::None_ => {
1785 let tok = self.advance();
1786 let name = Ident {
1788 name: tok.literal.unwrap_or_else(|| tok.kind.to_string()),
1789 span: tok.span,
1790 };
1791 Expr::Identifier {
1792 id,
1793 span: tok.span,
1794 name,
1795 }
1796 }
1797 TokenKind::SelfLower => {
1798 let tok = self.advance();
1799 let name = Ident {
1800 name: "self".into(),
1801 span: tok.span,
1802 };
1803 Expr::Identifier {
1804 id,
1805 span: tok.span,
1806 name,
1807 }
1808 }
1809 TokenKind::SelfUpper => {
1810 let tok = self.advance();
1811 let name = Ident {
1812 name: "Self".into(),
1813 span: tok.span,
1814 };
1815 Expr::Identifier {
1816 id,
1817 span: tok.span,
1818 name,
1819 }
1820 }
1821 TokenKind::Underscore => {
1823 let _ = self.advance();
1824 Expr::Placeholder { id, span }
1825 }
1826 TokenKind::Unreachable => {
1828 let _ = self.advance();
1829 if self.at(TokenKind::LParen) {
1831 if let Some(next) = self.tokens.get(self.pos + 1) {
1832 if next.kind == TokenKind::RParen {
1833 let _ = self.advance(); let _ = self.advance(); }
1836 }
1837 }
1838 Expr::Unreachable { id, span }
1839 }
1840 TokenKind::Return => {
1842 let _ = self.advance();
1843 let value = if !self.at_stmt_terminator() {
1844 Some(Box::new(self.parse_expr()))
1845 } else {
1846 None
1847 };
1848 let end = value.as_ref().map(|v| v.span()).unwrap_or(span);
1849 Expr::Return {
1850 id,
1851 span: Span::merge(span, end),
1852 value,
1853 }
1854 }
1855 TokenKind::Break => {
1857 let _ = self.advance();
1858 let value = if !self.at_stmt_terminator() {
1859 Some(Box::new(self.parse_expr()))
1860 } else {
1861 None
1862 };
1863 let end = value.as_ref().map(|v| v.span()).unwrap_or(span);
1864 Expr::Break {
1865 id,
1866 span: Span::merge(span, end),
1867 value,
1868 }
1869 }
1870 TokenKind::Continue => {
1872 let _ = self.advance();
1873 Expr::Continue { id, span }
1874 }
1875 TokenKind::Await => {
1877 let _ = self.advance();
1878 let inner = self.parse_unary();
1879 let end = inner.span();
1880 Expr::Await {
1881 id,
1882 span: Span::merge(span, end),
1883 expr: Box::new(inner),
1884 }
1885 }
1886 TokenKind::If => self.parse_if_expr(),
1888 TokenKind::Match => self.parse_match_expr(),
1890 TokenKind::Loop => self.parse_loop_expr(),
1892 TokenKind::LParen => {
1894 if self.is_lambda_start() {
1895 self.parse_lambda()
1896 } else {
1897 self.parse_paren_or_tuple()
1898 }
1899 }
1900 TokenKind::LBracket => self.parse_list_literal(),
1902 TokenKind::Hash => {
1904 if self.peek_kind_at(1) == Some(TokenKind::LBrace) {
1905 self.parse_set_literal()
1906 } else {
1907 let _ = self.advance();
1909 self.diagnostics.error(
1910 DiagnosticCode {
1911 prefix: 'E',
1912 number: 2022,
1913 },
1914 "expected `{` after `#` for set literal".to_string(),
1915 span,
1916 );
1917 Expr::Literal {
1918 id,
1919 span,
1920 lit: Literal::Unit,
1921 }
1922 }
1923 }
1924 TokenKind::LBrace => {
1926 if self.is_map_literal_start() {
1927 self.parse_map_literal()
1928 } else {
1929 let block = self.parse_block();
1930 let block_span = block.span;
1931 Expr::Block {
1932 id: self.alloc_id(),
1933 span: block_span,
1934 block,
1935 }
1936 }
1937 }
1938 _ => {
1939 self.diagnostics.error(
1940 DiagnosticCode {
1941 prefix: 'E',
1942 number: 2020,
1943 },
1944 format!("expected expression, found `{}`", self.peek().kind),
1945 span,
1946 );
1947 if !self.at(TokenKind::Eof) {
1949 let _ = self.advance();
1950 }
1951 Expr::Literal {
1952 id,
1953 span,
1954 lit: Literal::Unit,
1955 }
1956 }
1957 }
1958 }
1959
1960 fn parse_interpolation(&mut self, id: NodeId, span: Span) -> Expr {
1962 let mut parts = Vec::new();
1963 let mut end = span;
1964
1965 loop {
1966 match self.peek().kind.clone() {
1967 TokenKind::StringLiteralPart => {
1968 let tok = self.advance();
1969 end = tok.span;
1970 parts.push(InterpolationPart::Literal(tok.literal.unwrap_or_default()));
1971 }
1972 TokenKind::InterpolationStart => {
1973 let _ = self.advance(); let expr = self.parse_expr();
1975 end = expr.span();
1976 parts.push(InterpolationPart::Expr(expr));
1977 if self.at(TokenKind::InterpolationEnd) || self.at(TokenKind::RBrace) {
1979 end = self.advance().span;
1980 }
1981 }
1982 _ => break,
1983 }
1984 }
1985
1986 Expr::Interpolation {
1987 id,
1988 span: Span::merge(span, end),
1989 parts,
1990 }
1991 }
1992
1993 fn is_lambda_start(&self) -> bool {
1995 let mut i = self.pos + 1; let mut depth = 1usize;
1998
1999 while i < self.tokens.len() {
2000 match &self.tokens[i].kind {
2001 TokenKind::LParen => depth += 1,
2002 TokenKind::RParen => {
2003 depth -= 1;
2004 if depth == 0 {
2005 i += 1;
2006 break;
2007 }
2008 }
2009 TokenKind::Eof => return false,
2010 _ => {}
2011 }
2012 i += 1;
2013 }
2014
2015 while i < self.tokens.len() && self.tokens[i].kind == TokenKind::Newline {
2017 i += 1;
2018 }
2019
2020 matches!(
2021 self.tokens.get(i).map(|t| &t.kind),
2022 Some(TokenKind::FatArrow)
2023 )
2024 }
2025
2026 fn parse_lambda(&mut self) -> Expr {
2028 let start = self.peek().span;
2029 let _ = self.advance(); let params = self.parse_lambda_param_list();
2031 let _ = self.expect(TokenKind::RParen);
2032 let _ = self.expect(TokenKind::FatArrow);
2033
2034 let body = if self.at(TokenKind::LBrace) {
2036 let block = self.parse_block();
2037 let bspan = block.span;
2038 Expr::Block {
2039 id: self.alloc_id(),
2040 span: bspan,
2041 block,
2042 }
2043 } else {
2044 self.parse_expr()
2045 };
2046
2047 let id = self.alloc_id();
2048 let span = Span::merge(start, body.span());
2049 Expr::Lambda {
2050 id,
2051 span,
2052 params,
2053 body: Box::new(body),
2054 }
2055 }
2056
2057 fn parse_lambda_param_list(&mut self) -> Vec<Param> {
2059 let mut params = Vec::new();
2060 self.skip_newlines();
2061
2062 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
2063 let id = self.alloc_id();
2064 let start = self.peek().span;
2065
2066 let pattern = match self.peek().kind.clone() {
2067 TokenKind::Ident => {
2068 let tok = self.advance();
2069 let span = tok.span;
2070 Pattern::Bind {
2071 id: self.alloc_id(),
2072 span,
2073 name: Ident {
2074 name: tok.literal.unwrap_or_default(),
2075 span,
2076 },
2077 }
2078 }
2079 TokenKind::Underscore => {
2080 let tok = self.advance();
2081 Pattern::Wildcard {
2082 id: self.alloc_id(),
2083 span: tok.span,
2084 }
2085 }
2086 TokenKind::Mut => {
2087 let _ = self.advance(); let tok = if self.at(TokenKind::Ident) {
2089 self.advance()
2090 } else {
2091 return params; };
2093 let span = tok.span;
2094 Pattern::MutBind {
2095 id: self.alloc_id(),
2096 span,
2097 name: Ident {
2098 name: tok.literal.unwrap_or_default(),
2099 span,
2100 },
2101 }
2102 }
2103 _ => break,
2104 };
2105
2106 let ty = if self.at(TokenKind::Colon) {
2107 let _ = self.advance();
2108 Some(self.parse_type_expr())
2109 } else {
2110 None
2111 };
2112
2113 let end = self.peek().span;
2114 params.push(Param {
2115 id,
2116 span: Span::merge(start, end),
2117 pattern,
2118 ty,
2119 default: None,
2120 });
2121
2122 self.skip_newlines();
2123 if self.at(TokenKind::Comma) {
2124 let _ = self.advance();
2125 self.skip_newlines();
2126 } else {
2127 break;
2128 }
2129 }
2130
2131 params
2132 }
2133
2134 fn parse_paren_or_tuple(&mut self) -> Expr {
2136 let start = self.peek().span;
2137 let _ = self.advance(); self.skip_newlines();
2140
2141 if self.at(TokenKind::RParen) {
2143 let end = self.advance().span;
2144 let id = self.alloc_id();
2145 return Expr::Literal {
2146 id,
2147 span: Span::merge(start, end),
2148 lit: Literal::Unit,
2149 };
2150 }
2151
2152 let first = self.parse_expr();
2153 self.skip_newlines();
2154
2155 if self.at(TokenKind::Comma) {
2156 let mut elems = vec![first];
2158 while self.at(TokenKind::Comma) {
2159 let _ = self.advance();
2160 self.skip_newlines();
2161 if self.at(TokenKind::RParen) {
2162 break;
2163 }
2164 elems.push(self.parse_expr());
2165 self.skip_newlines();
2166 }
2167 let end = self
2168 .expect(TokenKind::RParen)
2169 .map(|t| t.span)
2170 .unwrap_or(start);
2171 let id = self.alloc_id();
2172 Expr::TupleLiteral {
2173 id,
2174 span: Span::merge(start, end),
2175 elems,
2176 }
2177 } else {
2178 let end = self
2180 .expect(TokenKind::RParen)
2181 .map(|t| t.span)
2182 .unwrap_or(start);
2183 let mut e = first;
2184 if let Some(new_span) = Some(Span::merge(start, end)) {
2186 match &mut e {
2187 Expr::Literal { span, .. }
2188 | Expr::Identifier { span, .. }
2189 | Expr::Binary { span, .. }
2190 | Expr::Unary { span, .. } => *span = new_span,
2191 _ => {}
2192 }
2193 }
2194 e
2195 }
2196 }
2197
2198 fn parse_list_literal(&mut self) -> Expr {
2200 let start = self.peek().span;
2201 let _ = self.advance(); let mut elems = Vec::new();
2203 self.skip_newlines();
2204
2205 while !self.at(TokenKind::RBracket) && !self.at(TokenKind::Eof) {
2206 elems.push(self.parse_expr());
2207 self.skip_newlines();
2208 if self.at(TokenKind::Comma) {
2209 let _ = self.advance();
2210 self.skip_newlines();
2211 } else {
2212 break;
2213 }
2214 }
2215
2216 let end = self
2217 .expect(TokenKind::RBracket)
2218 .map(|t| t.span)
2219 .unwrap_or(start);
2220 let id = self.alloc_id();
2221 Expr::ListLiteral {
2222 id,
2223 span: Span::merge(start, end),
2224 elems,
2225 }
2226 }
2227
2228 fn parse_set_literal(&mut self) -> Expr {
2230 let start = self.peek().span;
2231 let _ = self.advance(); let _ = self.advance(); let mut elems = Vec::new();
2234 self.skip_newlines();
2235
2236 while !self.at(TokenKind::RBrace) && !self.at(TokenKind::Eof) {
2237 elems.push(self.parse_expr());
2238 self.skip_newlines();
2239 if self.at(TokenKind::Comma) {
2240 let _ = self.advance();
2241 self.skip_newlines();
2242 } else {
2243 break;
2244 }
2245 }
2246
2247 let end = self
2248 .expect(TokenKind::RBrace)
2249 .map(|t| t.span)
2250 .unwrap_or(start);
2251 let id = self.alloc_id();
2252 Expr::SetLiteral {
2253 id,
2254 span: Span::merge(start, end),
2255 elems,
2256 }
2257 }
2258
2259 fn is_empty_brace(&self) -> bool {
2262 if self.peek().kind != TokenKind::LBrace {
2263 return false;
2264 }
2265 let mut i = self.pos + 1;
2266 while i < self.tokens.len() && self.tokens[i].kind == TokenKind::Newline {
2267 i += 1;
2268 }
2269 i < self.tokens.len() && self.tokens[i].kind == TokenKind::RBrace
2270 }
2271
2272 fn is_map_type_annotation(ty: &Option<TypeExpr>) -> bool {
2274 matches!(ty, Some(TypeExpr::Named { path, .. })
2275 if path.segments.last().map(|s| s.name.as_str()) == Some("Map"))
2276 }
2277
2278 fn is_map_literal_start(&self) -> bool {
2280 let mut i = self.pos + 1;
2282 while i < self.tokens.len() && self.tokens[i].kind == TokenKind::Newline {
2283 i += 1;
2284 }
2285 if i >= self.tokens.len() {
2286 return false;
2287 }
2288 let is_map_key_start = matches!(
2290 &self.tokens[i].kind,
2291 TokenKind::StringLiteral
2292 | TokenKind::RawStringLiteral
2293 | TokenKind::RawMultiLineStringLiteral
2294 | TokenKind::IntLiteral
2295 | TokenKind::FloatLiteral
2296 | TokenKind::Ident
2297 | TokenKind::TypeIdent
2298 );
2299 if !is_map_key_start || i + 1 >= self.tokens.len() {
2300 return false;
2301 }
2302 self.tokens[i + 1].kind == TokenKind::Colon
2303 }
2304
2305 fn parse_map_literal(&mut self) -> Expr {
2307 let start = self.peek().span;
2308 let _ = self.advance(); let mut entries = Vec::new();
2310 self.skip_newlines();
2311
2312 while !self.at(TokenKind::RBrace) && !self.at(TokenKind::Eof) {
2313 let key = self.parse_expr();
2314 let _ = self.expect(TokenKind::Colon);
2315 let val = self.parse_expr();
2316 entries.push((key, val));
2317 self.skip_newlines();
2318 if self.at(TokenKind::Comma) {
2319 let _ = self.advance();
2320 self.skip_newlines();
2321 } else {
2322 break;
2323 }
2324 }
2325
2326 let end = self
2327 .expect(TokenKind::RBrace)
2328 .map(|t| t.span)
2329 .unwrap_or(start);
2330 let id = self.alloc_id();
2331 Expr::MapLiteral {
2332 id,
2333 span: Span::merge(start, end),
2334 entries,
2335 }
2336 }
2337
2338 fn parse_if_expr(&mut self) -> Expr {
2340 let start = self.peek().span;
2341 let _ = self.advance(); let had_lparen = if self.at(TokenKind::LParen) {
2351 let _ = self.advance();
2352 true
2353 } else {
2354 let span = self.peek().span;
2355 let found = self.peek().kind.clone();
2356 self.diagnostics
2357 .error(
2358 DiagnosticCode {
2359 prefix: 'E',
2360 number: 2030,
2361 },
2362 format!("`if` condition must be parenthesized, found `{found}`"),
2363 span,
2364 )
2365 .note("wrap the condition in parentheses: `if (cond) { ... }`");
2366 false
2367 };
2368
2369 let (let_pattern, condition) = if self.at(TokenKind::Let) {
2371 let _ = self.advance(); let pat = self.parse_pattern();
2373 let _ = self.expect(TokenKind::Assign);
2374 let cond = self.parse_expr();
2375 (Some(pat), cond)
2376 } else {
2377 (None, self.parse_expr())
2378 };
2379
2380 if had_lparen {
2384 let _ = self.expect(TokenKind::RParen);
2385 }
2386 self.skip_newlines();
2387
2388 let then_block = self.parse_block();
2389
2390 if self.at(TokenKind::Newline) && self.peek_past_newlines_kind() == Some(TokenKind::Else) {
2393 self.skip_newlines();
2394 }
2395
2396 let else_block = if self.at(TokenKind::Else) {
2398 let _ = self.advance(); self.skip_newlines();
2400 if self.at(TokenKind::If) {
2401 Some(Box::new(self.parse_if_expr()))
2403 } else {
2404 let block = self.parse_block();
2405 let bspan = block.span;
2406 Some(Box::new(Expr::Block {
2407 id: self.alloc_id(),
2408 span: bspan,
2409 block,
2410 }))
2411 }
2412 } else {
2413 None
2414 };
2415
2416 let end = else_block
2417 .as_ref()
2418 .map(|e| e.span())
2419 .unwrap_or(then_block.span);
2420 let id = self.alloc_id();
2421 Expr::If {
2422 id,
2423 span: Span::merge(start, end),
2424 let_pattern,
2425 condition: Box::new(condition),
2426 then_block,
2427 else_block,
2428 }
2429 }
2430
2431 fn parse_match_expr(&mut self) -> Expr {
2433 let start = self.peek().span;
2434 let _ = self.advance(); let scrutinee = self.parse_expr();
2437 self.skip_newlines();
2438
2439 let _ = self.expect(TokenKind::LBrace);
2440 let mut arms = Vec::new();
2441
2442 loop {
2443 self.skip_newlines();
2444 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
2445 break;
2446 }
2447 let arm_start = self.peek().span;
2449 let pattern = self.parse_pattern();
2450
2451 let guard = if self.at(TokenKind::If) {
2453 let _ = self.advance(); let _ = self.expect(TokenKind::LParen);
2455 let g = self.parse_expr();
2456 let _ = self.expect(TokenKind::RParen);
2457 Some(g)
2458 } else {
2459 None
2460 };
2461
2462 let _ = self.expect(TokenKind::FatArrow);
2463 self.skip_newlines();
2464
2465 let body = if self.at(TokenKind::LBrace) {
2467 let block = self.parse_block();
2468 let bspan = block.span;
2469 Expr::Block {
2470 id: self.alloc_id(),
2471 span: bspan,
2472 block,
2473 }
2474 } else {
2475 self.parse_expr()
2476 };
2477
2478 let arm_end = body.span();
2479 arms.push(MatchArm {
2480 id: self.alloc_id(),
2481 span: Span::merge(arm_start, arm_end),
2482 pattern,
2483 guard,
2484 body,
2485 });
2486
2487 self.skip_newlines();
2488 if self.at(TokenKind::Comma) {
2490 let _ = self.advance();
2491 }
2492 }
2493
2494 let end = self
2495 .expect(TokenKind::RBrace)
2496 .map(|t| t.span)
2497 .unwrap_or(start);
2498 let id = self.alloc_id();
2499 Expr::Match {
2500 id,
2501 span: Span::merge(start, end),
2502 scrutinee: Box::new(scrutinee),
2503 arms,
2504 }
2505 }
2506
2507 pub(crate) fn parse_pattern(&mut self) -> Pattern {
2509 let first = self.parse_simple_pattern();
2511
2512 if self.at(TokenKind::BitOr) {
2513 let start = first.span();
2514 let mut alternatives = vec![first];
2515 while self.at(TokenKind::BitOr) {
2516 let _ = self.advance(); self.skip_newlines();
2518 alternatives.push(self.parse_simple_pattern());
2519 }
2520 let end = alternatives.last().map(|p| p.span()).unwrap_or(start);
2521 Pattern::Or {
2522 id: self.alloc_id(),
2523 span: Span::merge(start, end),
2524 alternatives,
2525 }
2526 } else {
2527 first
2528 }
2529 }
2530
2531 fn parse_simple_pattern(&mut self) -> Pattern {
2533 let id = self.alloc_id();
2534 let span = self.peek().span;
2535
2536 match self.peek().kind.clone() {
2537 TokenKind::Underscore => {
2539 let _ = self.advance();
2540 Pattern::Wildcard { id, span }
2541 }
2542 TokenKind::Mut => {
2544 let _ = self.advance(); let tok = if self.at(TokenKind::Ident) {
2546 self.advance()
2547 } else {
2548 return Pattern::Wildcard { id, span };
2549 };
2550 let name = Ident {
2551 name: tok.literal.unwrap_or_default(),
2552 span: tok.span,
2553 };
2554 Pattern::MutBind {
2555 id,
2556 span: Span::merge(span, tok.span),
2557 name,
2558 }
2559 }
2560 TokenKind::DotDot => {
2562 let _ = self.advance();
2563 Pattern::Rest { id, span }
2564 }
2565 TokenKind::IntLiteral => {
2567 let tok = self.advance();
2568 let lit = Literal::Int(tok.literal.unwrap_or_default());
2569 let pat = Pattern::Literal {
2570 id,
2571 span: tok.span,
2572 lit,
2573 };
2574 self.try_parse_range_pattern(pat)
2576 }
2577 TokenKind::Minus => {
2578 let _ = self.advance();
2580 if self.at(TokenKind::IntLiteral) {
2581 let tok = self.advance();
2582 let lit = Literal::Int(format!("-{}", tok.literal.unwrap_or_default()));
2583 let pat = Pattern::Literal {
2584 id,
2585 span: Span::merge(span, tok.span),
2586 lit,
2587 };
2588 self.try_parse_range_pattern(pat)
2589 } else if self.at(TokenKind::FloatLiteral) {
2590 let tok = self.advance();
2591 let lit = Literal::Float(format!("-{}", tok.literal.unwrap_or_default()));
2592 Pattern::Literal {
2593 id,
2594 span: Span::merge(span, tok.span),
2595 lit,
2596 }
2597 } else {
2598 Pattern::Wildcard { id, span }
2599 }
2600 }
2601 TokenKind::FloatLiteral => {
2602 let tok = self.advance();
2603 Pattern::Literal {
2604 id,
2605 span: tok.span,
2606 lit: Literal::Float(tok.literal.unwrap_or_default()),
2607 }
2608 }
2609 TokenKind::StringLiteral
2610 | TokenKind::RawStringLiteral
2611 | TokenKind::MultiLineStringLiteral
2612 | TokenKind::RawMultiLineStringLiteral => {
2613 let tok = self.advance();
2614 Pattern::Literal {
2615 id,
2616 span: tok.span,
2617 lit: Literal::String(tok.literal.unwrap_or_default()),
2618 }
2619 }
2620 TokenKind::BoolLiteral => {
2621 let tok = self.advance();
2622 let val = self.source.slice(tok.span) == "true";
2623 Pattern::Literal {
2624 id,
2625 span: tok.span,
2626 lit: Literal::Bool(val),
2627 }
2628 }
2629 TokenKind::TypeIdent
2631 | TokenKind::Ok_
2632 | TokenKind::Err_
2633 | TokenKind::Some_
2634 | TokenKind::None_ => {
2635 let tok = self.advance();
2636 let name = tok.literal.unwrap_or_else(|| tok.kind.to_string());
2639 let path_name = Ident {
2640 name,
2641 span: tok.span,
2642 };
2643 let path = TypePath {
2644 segments: vec![path_name],
2645 span: tok.span,
2646 };
2647
2648 if self.at(TokenKind::LParen) {
2649 let _ = self.advance();
2651 let mut fields = Vec::new();
2652 self.skip_newlines();
2653 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
2654 fields.push(self.parse_pattern());
2655 self.skip_newlines();
2656 if self.at(TokenKind::Comma) {
2657 let _ = self.advance();
2658 self.skip_newlines();
2659 } else {
2660 break;
2661 }
2662 }
2663 let end = self
2664 .expect(TokenKind::RParen)
2665 .map(|t| t.span)
2666 .unwrap_or(span);
2667 Pattern::Constructor {
2668 id,
2669 span: Span::merge(span, end),
2670 path,
2671 fields,
2672 }
2673 } else if self.at(TokenKind::LBrace) {
2674 let _ = self.advance();
2676 let mut fields = Vec::new();
2677 let mut rest = false;
2678 self.skip_newlines();
2679 while !self.at(TokenKind::RBrace) && !self.at(TokenKind::Eof) {
2680 if self.at(TokenKind::DotDot) {
2681 let _ = self.advance(); rest = true;
2683 self.skip_newlines();
2684 break;
2685 } else if self.at(TokenKind::Ident) {
2686 let ftok = self.advance();
2687 let fname = Ident {
2688 name: ftok.literal.unwrap_or_default(),
2689 span: ftok.span,
2690 };
2691 let fpat = if self.at(TokenKind::Colon) {
2692 let _ = self.advance();
2693 Some(self.parse_pattern())
2694 } else {
2695 None };
2697 fields.push(RecordPatternField {
2698 span: fname.span,
2699 name: fname,
2700 pattern: fpat,
2701 });
2702 } else {
2703 break;
2704 }
2705 self.skip_newlines();
2706 if self.at(TokenKind::Comma) {
2707 let _ = self.advance();
2708 self.skip_newlines();
2709 } else {
2710 break;
2711 }
2712 }
2713 let end = self
2714 .expect(TokenKind::RBrace)
2715 .map(|t| t.span)
2716 .unwrap_or(span);
2717 Pattern::Record {
2718 id,
2719 span: Span::merge(span, end),
2720 path,
2721 fields,
2722 rest,
2723 }
2724 } else {
2725 Pattern::Constructor {
2727 id,
2728 span: path.span,
2729 path,
2730 fields: vec![],
2731 }
2732 }
2733 }
2734 TokenKind::Ident => {
2736 let tok = self.advance();
2737 let name = Ident {
2738 name: tok.literal.unwrap_or_default(),
2739 span: tok.span,
2740 };
2741 Pattern::Bind {
2742 id,
2743 span: tok.span,
2744 name,
2745 }
2746 }
2747 TokenKind::LParen => {
2749 let _ = self.advance();
2750 let mut elems = Vec::new();
2751 self.skip_newlines();
2752 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
2753 elems.push(self.parse_pattern());
2754 self.skip_newlines();
2755 if self.at(TokenKind::Comma) {
2756 let _ = self.advance();
2757 self.skip_newlines();
2758 } else {
2759 break;
2760 }
2761 }
2762 let end = self
2763 .expect(TokenKind::RParen)
2764 .map(|t| t.span)
2765 .unwrap_or(span);
2766 if elems.len() == 1 {
2767 elems.remove(0) } else {
2769 Pattern::Tuple {
2770 id,
2771 span: Span::merge(span, end),
2772 elems,
2773 }
2774 }
2775 }
2776 TokenKind::LBracket => {
2778 let _ = self.advance();
2779 let mut elems = Vec::new();
2780 let mut rest = None;
2781 self.skip_newlines();
2782
2783 while !self.at(TokenKind::RBracket) && !self.at(TokenKind::Eof) {
2784 if self.at(TokenKind::DotDot) {
2785 let rest_start = self.peek().span;
2786 let _ = self.advance(); if self.at(TokenKind::Ident) {
2788 let tok = self.advance();
2789 let name = Ident {
2790 name: tok.literal.unwrap_or_default(),
2791 span: tok.span,
2792 };
2793 rest = Some(Box::new(Pattern::Bind {
2794 id: self.alloc_id(),
2795 span: Span::merge(rest_start, tok.span),
2796 name,
2797 }));
2798 } else {
2799 rest = Some(Box::new(Pattern::Rest {
2800 id: self.alloc_id(),
2801 span: rest_start,
2802 }));
2803 }
2804 self.skip_newlines();
2805 break;
2806 }
2807 elems.push(self.parse_pattern());
2808 self.skip_newlines();
2809 if self.at(TokenKind::Comma) {
2810 let _ = self.advance();
2811 self.skip_newlines();
2812 } else {
2813 break;
2814 }
2815 }
2816
2817 let end = self
2818 .expect(TokenKind::RBracket)
2819 .map(|t| t.span)
2820 .unwrap_or(span);
2821 Pattern::List {
2822 id,
2823 span: Span::merge(span, end),
2824 elems,
2825 rest,
2826 }
2827 }
2828 _ => {
2829 self.diagnostics.error(
2830 DiagnosticCode {
2831 prefix: 'E',
2832 number: 2021,
2833 },
2834 format!("expected pattern, found `{}`", self.peek().kind),
2835 span,
2836 );
2837 if !self.at(TokenKind::Eof) {
2838 let _ = self.advance();
2839 }
2840 Pattern::Wildcard { id, span }
2841 }
2842 }
2843 }
2844
2845 fn try_parse_range_pattern(&mut self, lo: Pattern) -> Pattern {
2847 if self.at(TokenKind::DotDot) || self.at(TokenKind::DotDotEq) {
2848 let inclusive = self.at(TokenKind::DotDotEq);
2849 let _ = self.advance();
2850 let hi = self.parse_simple_pattern();
2851 let span = Span::merge(lo.span(), hi.span());
2852 Pattern::Range {
2853 id: self.alloc_id(),
2854 span,
2855 lo: Box::new(lo),
2856 hi: Box::new(hi),
2857 inclusive,
2858 }
2859 } else {
2860 lo
2861 }
2862 }
2863
2864 fn at_stmt_terminator(&self) -> bool {
2866 matches!(
2867 self.peek().kind,
2868 TokenKind::Newline | TokenKind::Semicolon | TokenKind::RBrace | TokenKind::Eof
2869 )
2870 }
2871
2872 fn parse_block(&mut self) -> Block {
2876 let start = self.peek().span;
2877 if self.expect(TokenKind::LBrace).is_err() {
2878 return Block {
2879 id: self.alloc_id(),
2880 span: start,
2881 stmts: vec![],
2882 tail: None,
2883 };
2884 }
2885
2886 let mut stmts = Vec::new();
2887 let mut tail: Option<Box<Expr>> = None;
2888
2889 loop {
2890 self.skip_newlines();
2891 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
2892 break;
2893 }
2894 if self.at(TokenKind::Semicolon) {
2896 let _ = self.advance();
2897 continue;
2898 }
2899
2900 let _stmt_start = self.peek().span;
2901
2902 match self.peek().kind.clone() {
2904 TokenKind::Let => {
2905 stmts.push(Stmt::Let(self.parse_let_stmt()));
2906 self.skip_newlines();
2907 continue;
2908 }
2909 TokenKind::For => {
2910 stmts.push(Stmt::For(self.parse_for_loop()));
2911 self.skip_newlines();
2912 continue;
2913 }
2914 TokenKind::While => {
2915 stmts.push(Stmt::While(self.parse_while_loop()));
2916 self.skip_newlines();
2917 continue;
2918 }
2919 TokenKind::Loop => {
2920 let expr = self.parse_loop_expr();
2921 self.skip_newlines();
2922 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
2923 tail = Some(Box::new(expr));
2924 break;
2925 }
2926 stmts.push(Stmt::Expr(expr));
2927 continue;
2928 }
2929 TokenKind::Guard => {
2930 stmts.push(Stmt::Guard(self.parse_guard_stmt()));
2931 self.skip_newlines();
2932 continue;
2933 }
2934 TokenKind::Handling => {
2935 stmts.push(Stmt::Handling(self.parse_handling_block()));
2936 self.skip_newlines();
2937 continue;
2938 }
2939 _ => {}
2940 }
2941
2942 let expr = self.parse_expr();
2944
2945 self.skip_newlines();
2948
2949 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
2950 tail = Some(Box::new(expr));
2952 break;
2953 }
2954
2955 if self.at(TokenKind::Semicolon) {
2957 let _ = self.advance();
2958 }
2959
2960 stmts.push(Stmt::Expr(expr));
2961 }
2962
2963 let fallback_end = stmts
2964 .last()
2965 .map(|s| match s {
2966 Stmt::Expr(e) => e.span(),
2967 Stmt::Let(l) => l.span,
2968 _ => start,
2969 })
2970 .or_else(|| tail.as_ref().map(|t| t.span()))
2971 .unwrap_or(start);
2972 let end = self
2973 .expect(TokenKind::RBrace)
2974 .map(|t| t.span)
2975 .unwrap_or(fallback_end);
2976 Block {
2977 id: self.alloc_id(),
2978 span: Span::merge(start, end),
2979 stmts,
2980 tail,
2981 }
2982 }
2983
2984 fn parse_let_stmt(&mut self) -> LetStmt {
2986 let id = self.alloc_id();
2987 let start = self.peek().span;
2988 let _ = self.advance(); let pattern = if self.at(TokenKind::Mut) {
2991 let mut_span = self.advance().span; if self.at(TokenKind::Ident) {
2993 let tok = self.advance();
2994 let name = Ident {
2995 name: tok.literal.unwrap_or_default(),
2996 span: tok.span,
2997 };
2998 Pattern::MutBind {
2999 id: self.alloc_id(),
3000 span: Span::merge(mut_span, tok.span),
3001 name,
3002 }
3003 } else {
3004 self.parse_pattern()
3005 }
3006 } else {
3007 self.parse_pattern()
3008 };
3009
3010 let ty = if self.at(TokenKind::Colon) {
3011 let _ = self.advance();
3012 Some(self.parse_type_expr())
3013 } else {
3014 None
3015 };
3016
3017 let _ = self.expect(TokenKind::Assign);
3018 let value = if self.is_empty_brace() && Self::is_map_type_annotation(&ty) {
3019 let open = self.advance().span; self.skip_newlines();
3022 let close_span = self
3023 .expect(TokenKind::RBrace)
3024 .map(|t| t.span)
3025 .unwrap_or(open);
3026 Expr::MapLiteral {
3027 id: self.alloc_id(),
3028 span: Span::merge(open, close_span),
3029 entries: vec![],
3030 }
3031 } else {
3032 self.parse_expr()
3033 };
3034 let end = value.span();
3035
3036 LetStmt {
3037 id,
3038 span: Span::merge(start, end),
3039 pattern,
3040 ty,
3041 value,
3042 }
3043 }
3044
3045 fn parse_for_loop(&mut self) -> ForLoop {
3047 let id = self.alloc_id();
3048 let start = self.peek().span;
3049 let _ = self.advance(); let pattern = self.parse_pattern();
3052 let _ = self.expect(TokenKind::In);
3053 let iterable = self.parse_expr();
3054 self.skip_newlines();
3055 let body = self.parse_block();
3056
3057 let end = body.span;
3058 ForLoop {
3059 id,
3060 span: Span::merge(start, end),
3061 pattern,
3062 iterable,
3063 body,
3064 }
3065 }
3066
3067 fn parse_while_loop(&mut self) -> WhileLoop {
3069 let id = self.alloc_id();
3070 let start = self.peek().span;
3071 let _ = self.advance(); let _ = self.expect(TokenKind::LParen);
3074 let condition = self.parse_expr();
3075 let _ = self.expect(TokenKind::RParen);
3076 self.skip_newlines();
3077 let body = self.parse_block();
3078
3079 let end = body.span;
3080 WhileLoop {
3081 id,
3082 span: Span::merge(start, end),
3083 condition,
3084 body,
3085 }
3086 }
3087
3088 fn parse_loop_expr(&mut self) -> Expr {
3090 let id = self.alloc_id();
3091 let start = self.peek().span;
3092 let _ = self.advance(); self.skip_newlines();
3094 let body = self.parse_block();
3095 let end = body.span;
3096 Expr::Loop {
3097 id,
3098 span: Span::merge(start, end),
3099 body,
3100 }
3101 }
3102
3103 fn parse_guard_stmt(&mut self) -> GuardStmt {
3105 let id = self.alloc_id();
3106 let start = self.peek().span;
3107 let _ = self.advance(); let _ = self.expect(TokenKind::LParen);
3109
3110 let (let_pattern, condition) = if self.at(TokenKind::Let) {
3112 let _ = self.advance(); let pat = self.parse_pattern();
3114 let _ = self.expect(TokenKind::Assign);
3115 let cond = self.parse_expr();
3116 (Some(pat), cond)
3117 } else {
3118 (None, self.parse_expr())
3119 };
3120
3121 let _ = self.expect(TokenKind::RParen);
3122 let _ = self.expect(TokenKind::Else);
3123 self.skip_newlines();
3124 let else_block = self.parse_block();
3125 let end = else_block.span;
3126 GuardStmt {
3127 id,
3128 span: Span::merge(start, end),
3129 let_pattern,
3130 condition,
3131 else_block,
3132 }
3133 }
3134
3135 fn parse_handling_block(&mut self) -> HandlingBlock {
3137 let id = self.alloc_id();
3138 let start = self.peek().span;
3139 let _ = self.advance(); let _ = self.expect(TokenKind::LParen);
3141
3142 let mut handlers = Vec::new();
3143 self.skip_newlines();
3144 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
3145 let h_start = self.peek().span;
3146 let effect = self.parse_type_path();
3147 let _ = self.expect(TokenKind::With);
3148 let handler = self.parse_expr();
3149 let h_end = handler.span();
3150 handlers.push(HandlerPair {
3151 span: Span::merge(h_start, h_end),
3152 effect,
3153 handler,
3154 });
3155 self.skip_newlines();
3156 if self.at(TokenKind::Comma) {
3157 let _ = self.advance();
3158 self.skip_newlines();
3159 } else {
3160 break;
3161 }
3162 }
3163 let _ = self.expect(TokenKind::RParen);
3164 self.skip_newlines();
3165 let body = self.parse_block();
3166 let end = body.span;
3167 HandlingBlock {
3168 id,
3169 span: Span::merge(start, end),
3170 handlers,
3171 body,
3172 }
3173 }
3174
3175 fn parse_fn_decl(&mut self, annotations: Vec<Annotation>, vis: Visibility) -> FnDecl {
3183 let start = self.peek().span;
3184
3185 let is_async = if self.at(TokenKind::Async) {
3187 let _ = self.advance();
3188 true
3189 } else {
3190 false
3191 };
3192
3193 let _ = self.expect(TokenKind::Fn); let name_span = self.peek().span;
3197 let name = if self.at(TokenKind::Ident) {
3198 let tok = self.advance();
3199 Ident {
3200 name: tok.literal.unwrap_or_default(),
3201 span: tok.span,
3202 }
3203 } else {
3204 self.diagnostics.error(
3205 DiagnosticCode {
3206 prefix: 'E',
3207 number: 2073,
3208 },
3209 format!("expected function name, found `{}`", self.peek().kind),
3210 name_span,
3211 );
3212 Ident {
3213 name: String::new(),
3214 span: name_span,
3215 }
3216 };
3217
3218 let generic_params = self.parse_generic_params();
3220
3221 let _ = self.expect(TokenKind::LParen);
3223 let params = self.parse_param_list();
3224 let _ = self.expect(TokenKind::RParen);
3225
3226 let return_type = if self.at(TokenKind::ThinArrow) {
3228 let _ = self.advance();
3229 Some(self.parse_type_expr())
3230 } else {
3231 None
3232 };
3233
3234 if self.peek_past_newlines_kind() == Some(TokenKind::With) {
3236 self.skip_newlines();
3237 }
3238 let effect_clause = self.parse_effect_clause();
3239
3240 if self.peek_past_newlines_kind() == Some(TokenKind::Where) {
3242 self.skip_newlines();
3243 }
3244 let where_clause = self.parse_where_clause();
3245
3246 self.skip_newlines();
3247
3248 let body = self.parse_block();
3250 let end = body.span;
3251
3252 FnDecl {
3253 id: self.alloc_id(),
3254 span: Span::merge(start, end),
3255 annotations,
3256 visibility: vis,
3257 is_async,
3258 name,
3259 generic_params,
3260 params,
3261 return_type,
3262 effect_clause,
3263 where_clause,
3264 body: Some(body),
3265 }
3266 }
3267
3268 fn parse_param_list(&mut self) -> Vec<Param> {
3270 let mut params = Vec::new();
3271 self.skip_newlines();
3272
3273 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
3274 params.push(self.parse_param());
3275 self.skip_newlines();
3276 if self.at(TokenKind::Comma) {
3277 let _ = self.advance();
3278 self.skip_newlines();
3279 } else {
3280 break;
3281 }
3282 }
3283
3284 params
3285 }
3286
3287 fn parse_param(&mut self) -> Param {
3289 let id = self.alloc_id();
3290 let start = self.peek().span;
3291
3292 let pattern = match self.peek().kind.clone() {
3294 TokenKind::Mut => {
3295 let _ = self.advance(); match self.peek().kind.clone() {
3297 TokenKind::Ident => {
3298 let tok = self.advance();
3299 let span = tok.span;
3300 Pattern::MutBind {
3301 id: self.alloc_id(),
3302 span,
3303 name: Ident {
3304 name: tok.literal.unwrap_or_default(),
3305 span,
3306 },
3307 }
3308 }
3309 TokenKind::SelfLower => {
3310 let tok = self.advance();
3311 Pattern::MutBind {
3312 id: self.alloc_id(),
3313 span: tok.span,
3314 name: Ident {
3315 name: "self".into(),
3316 span: tok.span,
3317 },
3318 }
3319 }
3320 _ => {
3321 self.diagnostics.error(
3322 DiagnosticCode {
3323 prefix: 'E',
3324 number: 2031,
3325 },
3326 format!(
3327 "expected parameter name after `mut`, found `{}`",
3328 self.peek().kind
3329 ),
3330 start,
3331 );
3332 Pattern::Wildcard {
3333 id: self.alloc_id(),
3334 span: start,
3335 }
3336 }
3337 }
3338 }
3339 TokenKind::Ident => {
3340 let tok = self.advance();
3341 let span = tok.span;
3342 Pattern::Bind {
3343 id: self.alloc_id(),
3344 span,
3345 name: Ident {
3346 name: tok.literal.unwrap_or_default(),
3347 span,
3348 },
3349 }
3350 }
3351 TokenKind::SelfLower => {
3352 let tok = self.advance();
3353 Pattern::Bind {
3354 id: self.alloc_id(),
3355 span: tok.span,
3356 name: Ident {
3357 name: "self".into(),
3358 span: tok.span,
3359 },
3360 }
3361 }
3362 TokenKind::Underscore => {
3363 let tok = self.advance();
3364 Pattern::Wildcard {
3365 id: self.alloc_id(),
3366 span: tok.span,
3367 }
3368 }
3369 _ => {
3370 self.diagnostics.error(
3371 DiagnosticCode {
3372 prefix: 'E',
3373 number: 2031,
3374 },
3375 format!("expected parameter name, found `{}`", self.peek().kind),
3376 start,
3377 );
3378 Pattern::Wildcard {
3379 id: self.alloc_id(),
3380 span: start,
3381 }
3382 }
3383 };
3384
3385 let ty = if self.at(TokenKind::Colon) {
3387 let _ = self.advance();
3388 Some(self.parse_type_expr())
3389 } else {
3390 None
3391 };
3392
3393 let default = if self.at(TokenKind::Assign) {
3395 let _ = self.advance();
3396 Some(self.parse_expr_stub())
3397 } else {
3398 None
3399 };
3400
3401 let end = self.peek().span;
3402 Param {
3403 id,
3404 span: Span::merge(start, end),
3405 pattern,
3406 ty,
3407 default,
3408 }
3409 }
3410
3411 fn parse_effect_clause(&mut self) -> Vec<TypePath> {
3413 if !self.at(TokenKind::With) {
3414 return Vec::new();
3415 }
3416 let _ = self.advance(); let mut effects = Vec::new();
3419 effects.push(self.parse_type_path());
3420
3421 while self.at(TokenKind::Comma) {
3422 let _ = self.advance();
3423 self.skip_newlines();
3424 effects.push(self.parse_type_path());
3425 }
3426
3427 effects
3428 }
3429
3430 fn parse_type_alias_decl(
3434 &mut self,
3435 annotations: Vec<Annotation>,
3436 vis: Visibility,
3437 ) -> TypeAliasDecl {
3438 let start = self.peek().span;
3439 let _ = self.advance(); let name_span = self.peek().span;
3443 let name = if self.at(TokenKind::TypeIdent) {
3444 let tok = self.advance();
3445 Ident {
3446 name: tok.literal.unwrap_or_default(),
3447 span: tok.span,
3448 }
3449 } else if self.at(TokenKind::Ident) {
3450 let tok = self.advance();
3452 Ident {
3453 name: tok.literal.unwrap_or_default(),
3454 span: tok.span,
3455 }
3456 } else {
3457 self.diagnostics.error(
3458 DiagnosticCode {
3459 prefix: 'E',
3460 number: 2060,
3461 },
3462 format!("expected type alias name, found `{}`", self.peek().kind),
3463 name_span,
3464 );
3465 Ident {
3466 name: String::new(),
3467 span: name_span,
3468 }
3469 };
3470
3471 let generic_params = self.parse_generic_params();
3473
3474 let _ = self.expect(TokenKind::Assign);
3476
3477 let ty = self.parse_type_expr();
3479
3480 let where_clause = self.parse_where_clause();
3482
3483 let end = if !where_clause.is_empty() {
3484 where_clause
3485 .last()
3486 .expect("where_clause confirmed non-empty")
3487 .span
3488 } else {
3489 ty.span()
3490 };
3491
3492 TypeAliasDecl {
3493 id: self.alloc_id(),
3494 span: Span::merge(start, end),
3495 annotations,
3496 visibility: vis,
3497 name,
3498 generic_params,
3499 ty,
3500 where_clause,
3501 }
3502 }
3503
3504 fn parse_const_decl(&mut self, annotations: Vec<Annotation>, vis: Visibility) -> ConstDecl {
3508 let start = self.peek().span;
3509 let _ = self.advance(); let name_span = self.peek().span;
3513 let name = if matches!(self.peek().kind, TokenKind::Ident | TokenKind::TypeIdent) {
3514 let tok = self.advance();
3515 Ident {
3516 name: tok.literal.unwrap_or_default(),
3517 span: tok.span,
3518 }
3519 } else {
3520 self.diagnostics.error(
3521 DiagnosticCode {
3522 prefix: 'E',
3523 number: 2061,
3524 },
3525 format!("expected constant name, found `{}`", self.peek().kind),
3526 name_span,
3527 );
3528 Ident {
3529 name: String::new(),
3530 span: name_span,
3531 }
3532 };
3533
3534 let _ = self.expect(TokenKind::Colon);
3536
3537 let ty = self.parse_type_expr();
3539
3540 let _ = self.expect(TokenKind::Assign);
3542
3543 let value = self.parse_expr();
3545
3546 let end = value.span();
3547
3548 ConstDecl {
3549 id: self.alloc_id(),
3550 span: Span::merge(start, end),
3551 annotations,
3552 visibility: vis,
3553 name,
3554 ty,
3555 value,
3556 }
3557 }
3558
3559 fn parse_record_decl(&mut self, annotations: Vec<Annotation>, vis: Visibility) -> RecordDecl {
3567 let start = self.peek().span;
3568 let _ = self.advance(); let name_span = self.peek().span;
3571 let name = if self.at(TokenKind::TypeIdent) {
3572 let tok = self.advance();
3573 Ident {
3574 name: tok.literal.unwrap_or_default(),
3575 span: tok.span,
3576 }
3577 } else {
3578 self.diagnostics.error(
3579 DiagnosticCode {
3580 prefix: 'E',
3581 number: 2040,
3582 },
3583 format!("expected record name, found `{}`", self.peek().kind),
3584 name_span,
3585 );
3586 Ident {
3587 name: String::new(),
3588 span: name_span,
3589 }
3590 };
3591
3592 let generic_params = self.parse_generic_params();
3593 let where_clause = self.parse_where_clause();
3594
3595 self.skip_newlines();
3596 let _ = self.expect(TokenKind::LBrace);
3597 let fields = self.parse_record_fields();
3598 let end = self
3599 .expect(TokenKind::RBrace)
3600 .map(|t| t.span)
3601 .unwrap_or(start);
3602
3603 RecordDecl {
3604 id: self.alloc_id(),
3605 span: Span::merge(start, end),
3606 annotations,
3607 visibility: vis,
3608 name,
3609 generic_params,
3610 where_clause,
3611 fields,
3612 }
3613 }
3614
3615 fn parse_record_fields(&mut self) -> Vec<RecordDeclField> {
3617 let mut fields = Vec::new();
3618
3619 loop {
3620 self.skip_newlines();
3621 while self.at(TokenKind::DocComment) {
3624 let _ = self.advance();
3625 self.skip_newlines();
3626 }
3627 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
3628 break;
3629 }
3630 if !self.at(TokenKind::Ident) {
3631 break;
3632 }
3633
3634 let id = self.alloc_id();
3635 let start = self.peek().span;
3636 let tok = self.advance();
3637 let name = Ident {
3638 name: tok.literal.unwrap_or_default(),
3639 span: tok.span,
3640 };
3641
3642 let _ = self.expect(TokenKind::Colon);
3643 let ty = self.parse_type_expr();
3644
3645 let default = if self.at(TokenKind::Assign) {
3646 let _ = self.advance();
3647 Some(self.parse_expr_stub())
3648 } else {
3649 None
3650 };
3651
3652 let end = self.peek().span;
3653 fields.push(RecordDeclField {
3654 id,
3655 span: Span::merge(start, end),
3656 name,
3657 ty,
3658 default,
3659 });
3660
3661 self.skip_newlines();
3662 if self.at(TokenKind::Comma) {
3663 let _ = self.advance();
3664 }
3665 }
3666
3667 fields
3668 }
3669
3670 fn parse_enum_decl(&mut self, annotations: Vec<Annotation>, vis: Visibility) -> EnumDecl {
3678 let start = self.peek().span;
3679 let _ = self.advance(); let name_span = self.peek().span;
3682 let name = if self.at(TokenKind::TypeIdent) {
3683 let tok = self.advance();
3684 Ident {
3685 name: tok.literal.unwrap_or_default(),
3686 span: tok.span,
3687 }
3688 } else {
3689 self.diagnostics.error(
3690 DiagnosticCode {
3691 prefix: 'E',
3692 number: 2050,
3693 },
3694 format!("expected enum name, found `{}`", self.peek().kind),
3695 name_span,
3696 );
3697 Ident {
3698 name: String::new(),
3699 span: name_span,
3700 }
3701 };
3702
3703 let generic_params = self.parse_generic_params();
3704 let where_clause = self.parse_where_clause();
3705
3706 self.skip_newlines();
3707 let _ = self.expect(TokenKind::LBrace);
3708 let variants = self.parse_enum_variants();
3709 let end = self
3710 .expect(TokenKind::RBrace)
3711 .map(|t| t.span)
3712 .unwrap_or(start);
3713
3714 EnumDecl {
3715 id: self.alloc_id(),
3716 span: Span::merge(start, end),
3717 annotations,
3718 visibility: vis,
3719 name,
3720 generic_params,
3721 where_clause,
3722 variants,
3723 }
3724 }
3725
3726 fn parse_enum_variants(&mut self) -> Vec<EnumVariant> {
3728 let mut variants = Vec::new();
3729
3730 loop {
3731 self.skip_newlines();
3732 while self.at(TokenKind::DocComment) {
3733 let _ = self.advance();
3734 self.skip_newlines();
3735 }
3736 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
3737 break;
3738 }
3739 if !matches!(
3741 self.peek().kind,
3742 TokenKind::TypeIdent
3743 | TokenKind::Ok_
3744 | TokenKind::Err_
3745 | TokenKind::Some_
3746 | TokenKind::None_
3747 ) {
3748 break;
3749 }
3750
3751 let id = self.alloc_id();
3752 let start = self.peek().span;
3753 let tok = self.advance();
3754 let variant_name = tok.literal.unwrap_or_else(|| tok.kind.to_string());
3756 let name = Ident {
3757 name: variant_name,
3758 span: tok.span,
3759 };
3760
3761 let variant = if self.at(TokenKind::LBrace) {
3762 let _ = self.advance();
3764 let fields = self.parse_record_fields();
3765 let end = self
3766 .expect(TokenKind::RBrace)
3767 .map(|t| t.span)
3768 .unwrap_or(start);
3769 EnumVariant::Struct {
3770 id,
3771 span: Span::merge(start, end),
3772 name,
3773 fields,
3774 }
3775 } else if self.at(TokenKind::LParen) {
3776 let _ = self.advance();
3778 let mut tys = Vec::new();
3779 self.skip_newlines();
3780 while !self.at(TokenKind::RParen) && !self.at(TokenKind::Eof) {
3781 tys.push(self.parse_type_expr());
3782 self.skip_newlines();
3783 if self.at(TokenKind::Comma) {
3784 let _ = self.advance();
3785 self.skip_newlines();
3786 } else {
3787 break;
3788 }
3789 }
3790 let end = self
3791 .expect(TokenKind::RParen)
3792 .map(|t| t.span)
3793 .unwrap_or(start);
3794 EnumVariant::Tuple {
3795 id,
3796 span: Span::merge(start, end),
3797 name,
3798 tys,
3799 }
3800 } else {
3801 EnumVariant::Unit {
3803 id,
3804 span: start,
3805 name,
3806 }
3807 };
3808
3809 variants.push(variant);
3810
3811 self.skip_newlines();
3812 if self.at(TokenKind::Comma) {
3813 let _ = self.advance();
3814 }
3815 }
3816
3817 variants
3818 }
3819
3820 fn parse_class_decl(&mut self, annotations: Vec<Annotation>, vis: Visibility) -> ClassDecl {
3828 let start = self.peek().span;
3829 let _ = self.advance(); let name_span = self.peek().span;
3832 let name = if self.at(TokenKind::TypeIdent) {
3833 let tok = self.advance();
3834 Ident {
3835 name: tok.literal.unwrap_or_default(),
3836 span: tok.span,
3837 }
3838 } else {
3839 self.diagnostics.error(
3840 DiagnosticCode {
3841 prefix: 'E',
3842 number: 2060,
3843 },
3844 format!("expected class name, found `{}`", self.peek().kind),
3845 name_span,
3846 );
3847 Ident {
3848 name: String::new(),
3849 span: name_span,
3850 }
3851 };
3852
3853 let generic_params = self.parse_generic_params();
3854
3855 let mut base: Option<TypePath> = None;
3857 let mut traits: Vec<TypePath> = Vec::new();
3858
3859 if self.at(TokenKind::Colon) {
3860 let _ = self.advance(); let first = self.parse_type_path();
3863 base = Some(first);
3864
3865 while self.at(TokenKind::Comma) {
3867 let _ = self.advance();
3868 self.skip_newlines();
3869 traits.push(self.parse_type_path());
3870 }
3871 }
3872
3873 let where_clause = self.parse_where_clause();
3874
3875 self.skip_newlines();
3876 let _ = self.expect(TokenKind::LBrace);
3877
3878 let (fields, methods) = self.parse_class_members();
3879
3880 let end = self
3881 .expect(TokenKind::RBrace)
3882 .map(|t| t.span)
3883 .unwrap_or(start);
3884
3885 ClassDecl {
3886 id: self.alloc_id(),
3887 span: Span::merge(start, end),
3888 annotations,
3889 visibility: vis,
3890 name,
3891 generic_params,
3892 base,
3893 traits,
3894 where_clause,
3895 fields,
3896 methods,
3897 }
3898 }
3899
3900 fn parse_class_members(&mut self) -> (Vec<RecordDeclField>, Vec<FnDecl>) {
3902 let mut fields = Vec::new();
3903 let mut methods = Vec::new();
3904
3905 loop {
3906 self.skip_newlines();
3907 while self.at(TokenKind::DocComment) {
3908 let _ = self.advance();
3909 self.skip_newlines();
3910 }
3911 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
3912 break;
3913 }
3914
3915 let mut annotations = Vec::new();
3917 while self.at(TokenKind::At) {
3918 annotations.push(self.parse_annotation());
3919 self.skip_newlines();
3920 }
3921
3922 let vis = if self.at_visibility() {
3924 self.parse_visibility()
3925 } else {
3926 Visibility::Private
3927 };
3928
3929 match self.peek().kind.clone() {
3930 TokenKind::Fn | TokenKind::Async => {
3931 methods.push(self.parse_fn_decl(annotations, vis));
3932 }
3933 TokenKind::Ident => {
3934 let id = self.alloc_id();
3936 let field_start = self.peek().span;
3937 let tok = self.advance();
3938 let field_name = Ident {
3939 name: tok.literal.unwrap_or_default(),
3940 span: tok.span,
3941 };
3942 let _ = self.expect(TokenKind::Colon);
3943 let ty = self.parse_type_expr();
3944 let default = if self.at(TokenKind::Assign) {
3945 let _ = self.advance();
3946 Some(self.parse_expr_stub())
3947 } else {
3948 None
3949 };
3950 let field_end = self.peek().span;
3951 fields.push(RecordDeclField {
3952 id,
3953 span: Span::merge(field_start, field_end),
3954 name: field_name,
3955 ty,
3956 default,
3957 });
3958 self.skip_newlines();
3959 if self.at(TokenKind::Comma) {
3960 let _ = self.advance();
3961 }
3962 }
3963 _ => {
3964 let _ = self.advance();
3966 }
3967 }
3968 }
3969
3970 (fields, methods)
3971 }
3972
3973 fn parse_trait_decl(
3981 &mut self,
3982 annotations: Vec<Annotation>,
3983 vis: Visibility,
3984 is_platform: bool,
3985 ) -> TraitDecl {
3986 let start = self.peek().span;
3987 let _ = self.expect(TokenKind::Trait); let name_span = self.peek().span;
3990 let name = if self.at(TokenKind::TypeIdent) {
3991 let tok = self.advance();
3992 Ident {
3993 name: tok.literal.unwrap_or_default(),
3994 span: tok.span,
3995 }
3996 } else {
3997 self.diagnostics.error(
3998 DiagnosticCode {
3999 prefix: 'E',
4000 number: 2070,
4001 },
4002 format!("expected trait name, found `{}`", self.peek().kind),
4003 name_span,
4004 );
4005 Ident {
4006 name: String::new(),
4007 span: name_span,
4008 }
4009 };
4010
4011 let generic_params = self.parse_generic_params();
4012
4013 let mut supertraits = Vec::new();
4015 if self.at(TokenKind::Colon) {
4016 let _ = self.advance();
4017 supertraits.push(self.parse_type_path());
4018 while self.at(TokenKind::Comma) {
4019 let _ = self.advance();
4020 self.skip_newlines();
4021 supertraits.push(self.parse_type_path());
4022 }
4023 }
4024
4025 let _where_clause = self.parse_where_clause();
4026
4027 self.skip_newlines();
4028 let _ = self.expect(TokenKind::LBrace);
4029
4030 let (associated_types, methods) = self.parse_trait_members();
4031
4032 let end = self
4033 .expect(TokenKind::RBrace)
4034 .map(|t| t.span)
4035 .unwrap_or(start);
4036
4037 TraitDecl {
4038 id: self.alloc_id(),
4039 span: Span::merge(start, end),
4040 annotations,
4041 visibility: vis,
4042 is_platform,
4043 name,
4044 generic_params,
4045 supertraits,
4046 associated_types,
4047 methods,
4048 }
4049 }
4050
4051 fn parse_platform_trait_decl(
4053 &mut self,
4054 annotations: Vec<Annotation>,
4055 vis: Visibility,
4056 ) -> TraitDecl {
4057 let _ = self.advance(); self.parse_trait_decl(annotations, vis, true)
4059 }
4060
4061 fn parse_trait_members(&mut self) -> (Vec<AssociatedType>, Vec<FnDecl>) {
4065 let mut assoc_types = Vec::new();
4066 let mut methods = Vec::new();
4067
4068 loop {
4069 self.skip_newlines();
4070 while self.at(TokenKind::DocComment) {
4071 let _ = self.advance();
4072 self.skip_newlines();
4073 }
4074 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
4075 break;
4076 }
4077
4078 let mut annotations = Vec::new();
4080 while self.at(TokenKind::At) {
4081 annotations.push(self.parse_annotation());
4082 self.skip_newlines();
4083 }
4084
4085 let vis = if self.at_visibility() {
4087 self.parse_visibility()
4088 } else {
4089 Visibility::Private
4090 };
4091
4092 match self.peek().kind.clone() {
4093 TokenKind::Type => {
4095 let at_start = self.peek().span;
4096 let _ = self.advance(); let at_id = self.alloc_id();
4098 let at_name_span = self.peek().span;
4099 let at_name = if self.at(TokenKind::TypeIdent) {
4100 let tok = self.advance();
4101 Ident {
4102 name: tok.literal.unwrap_or_default(),
4103 span: tok.span,
4104 }
4105 } else {
4106 self.diagnostics.error(
4107 DiagnosticCode {
4108 prefix: 'E',
4109 number: 2071,
4110 },
4111 format!(
4112 "expected associated type name, found `{}`",
4113 self.peek().kind
4114 ),
4115 at_name_span,
4116 );
4117 Ident {
4118 name: String::new(),
4119 span: at_name_span,
4120 }
4121 };
4122
4123 let bounds = if self.at(TokenKind::Colon) {
4124 let _ = self.advance();
4125 vec![self.parse_type_path()]
4126 } else {
4127 Vec::new()
4128 };
4129
4130 let at_end = self.peek().span;
4131 assoc_types.push(AssociatedType {
4132 id: at_id,
4133 span: Span::merge(at_start, at_end),
4134 name: at_name,
4135 bounds,
4136 });
4137 }
4138 TokenKind::Fn | TokenKind::Async => {
4139 let fn_start = self.peek().span;
4140 let is_async = if self.at(TokenKind::Async) {
4141 let _ = self.advance();
4142 true
4143 } else {
4144 false
4145 };
4146 let _ = self.expect(TokenKind::Fn);
4147
4148 let fn_name_span = self.peek().span;
4149 let fn_name = if self.at(TokenKind::Ident) {
4150 let tok = self.advance();
4151 Ident {
4152 name: tok.literal.unwrap_or_default(),
4153 span: tok.span,
4154 }
4155 } else {
4156 self.diagnostics.error(
4157 DiagnosticCode {
4158 prefix: 'E',
4159 number: 2072,
4160 },
4161 format!("expected method name, found `{}`", self.peek().kind),
4162 fn_name_span,
4163 );
4164 Ident {
4165 name: String::new(),
4166 span: fn_name_span,
4167 }
4168 };
4169
4170 let generic_params = self.parse_generic_params();
4171 let _ = self.expect(TokenKind::LParen);
4172 let params = self.parse_param_list();
4173 let _ = self.expect(TokenKind::RParen);
4174
4175 let return_type = if self.at(TokenKind::ThinArrow) {
4176 let _ = self.advance();
4177 Some(self.parse_type_expr())
4178 } else {
4179 None
4180 };
4181
4182 let effect_clause = self.parse_effect_clause();
4183 let where_clause = self.parse_where_clause();
4184
4185 self.skip_newlines();
4186
4187 let body = if self.at(TokenKind::LBrace) {
4189 Some(self.parse_block())
4190 } else {
4191 None
4192 };
4193
4194 let fn_end = body
4195 .as_ref()
4196 .map(|b| b.span)
4197 .unwrap_or_else(|| self.peek().span);
4198 methods.push(FnDecl {
4199 id: self.alloc_id(),
4200 span: Span::merge(fn_start, fn_end),
4201 annotations,
4202 visibility: vis,
4203 is_async,
4204 name: fn_name,
4205 generic_params,
4206 params,
4207 return_type,
4208 effect_clause,
4209 where_clause,
4210 body,
4211 });
4212 }
4213 _ => {
4214 let _ = self.advance();
4216 }
4217 }
4218 }
4219
4220 (assoc_types, methods)
4221 }
4222
4223 fn parse_impl_block(&mut self, annotations: Vec<Annotation>) -> ImplBlock {
4231 let start = self.peek().span;
4232 let _ = self.advance(); let generic_params = self.parse_generic_params();
4235
4236 let (trait_path, trait_args, target) = self.parse_impl_header();
4239
4240 let where_clause = self.parse_where_clause();
4241
4242 self.skip_newlines();
4243 let _ = self.expect(TokenKind::LBrace);
4244 let methods = self.parse_impl_methods();
4245 let end = self
4246 .expect(TokenKind::RBrace)
4247 .map(|t| t.span)
4248 .unwrap_or(start);
4249
4250 ImplBlock {
4251 id: self.alloc_id(),
4252 span: Span::merge(start, end),
4253 annotations,
4254 generic_params,
4255 trait_path,
4256 trait_args,
4257 target,
4258 where_clause,
4259 type_assignments: vec![],
4260 methods,
4261 }
4262 }
4263
4264 fn parse_impl_header(&mut self) -> (Option<TypePath>, Vec<TypeExpr>, TypeExpr) {
4271 let first = self.parse_type_expr();
4273
4274 if self.at(TokenKind::For) {
4275 let _ = self.advance(); let target = self.parse_type_expr();
4278 let (trait_path, trait_args) = match &first {
4281 TypeExpr::Named { path, args, .. } => (Some(path.clone()), args.clone()),
4282 _ => (None, vec![]),
4283 };
4284 (trait_path, trait_args, target)
4285 } else {
4286 (None, vec![], first)
4288 }
4289 }
4290
4291 fn parse_impl_methods(&mut self) -> Vec<FnDecl> {
4293 let mut methods = Vec::new();
4294
4295 loop {
4296 self.skip_newlines();
4297 while self.at(TokenKind::DocComment) {
4298 let _ = self.advance();
4299 self.skip_newlines();
4300 }
4301 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
4302 break;
4303 }
4304
4305 let mut annotations = Vec::new();
4307 while self.at(TokenKind::At) {
4308 annotations.push(self.parse_annotation());
4309 self.skip_newlines();
4310 }
4311
4312 let vis = if self.at_visibility() {
4314 self.parse_visibility()
4315 } else {
4316 Visibility::Private
4317 };
4318
4319 if matches!(self.peek().kind, TokenKind::Fn | TokenKind::Async) {
4320 methods.push(self.parse_fn_decl(annotations, vis));
4321 } else {
4322 if self.at(TokenKind::Eof) {
4324 break;
4325 }
4326 let _ = self.advance();
4327 }
4328 }
4329
4330 methods
4331 }
4332
4333 fn parse_effect_decl(&mut self, annotations: Vec<Annotation>, vis: Visibility) -> EffectDecl {
4342 let start = self.peek().span;
4343 let _ = self.advance(); let name_span = self.peek().span;
4346 let name = if self.at(TokenKind::TypeIdent) {
4347 let tok = self.advance();
4348 Ident {
4349 name: tok.literal.unwrap_or_default(),
4350 span: tok.span,
4351 }
4352 } else {
4353 self.diagnostics.error(
4354 DiagnosticCode {
4355 prefix: 'E',
4356 number: 2090,
4357 },
4358 format!("expected effect name, found `{}`", self.peek().kind),
4359 name_span,
4360 );
4361 Ident {
4362 name: String::new(),
4363 span: name_span,
4364 }
4365 };
4366
4367 let generic_params = self.parse_generic_params();
4368
4369 if self.at(TokenKind::Assign) {
4371 let _ = self.advance(); let mut components = vec![self.parse_type_path()];
4373 while self.at(TokenKind::Plus) {
4374 let _ = self.advance();
4375 self.skip_newlines();
4376 components.push(self.parse_type_path());
4377 }
4378 let end = self.peek().span;
4379 if self.at(TokenKind::Newline) {
4381 let _ = self.advance();
4382 }
4383 return EffectDecl {
4384 id: self.alloc_id(),
4385 span: Span::merge(start, end),
4386 annotations,
4387 visibility: vis,
4388 name,
4389 generic_params,
4390 components,
4391 operations: Vec::new(),
4392 };
4393 }
4394
4395 self.skip_newlines();
4397 let _ = self.expect(TokenKind::LBrace);
4398 let operations = self.parse_effect_operations();
4399 let end = self
4400 .expect(TokenKind::RBrace)
4401 .map(|t| t.span)
4402 .unwrap_or(start);
4403
4404 EffectDecl {
4405 id: self.alloc_id(),
4406 span: Span::merge(start, end),
4407 annotations,
4408 visibility: vis,
4409 name,
4410 generic_params,
4411 components: vec![],
4412 operations,
4413 }
4414 }
4415
4416 fn parse_effect_operations(&mut self) -> Vec<FnDecl> {
4420 let mut ops = Vec::new();
4421
4422 loop {
4423 self.skip_newlines();
4424 while self.at(TokenKind::DocComment) {
4425 let _ = self.advance();
4426 self.skip_newlines();
4427 }
4428 if self.at(TokenKind::RBrace) || self.at(TokenKind::Eof) {
4429 break;
4430 }
4431
4432 let mut annotations = Vec::new();
4434 while self.at(TokenKind::At) {
4435 annotations.push(self.parse_annotation());
4436 self.skip_newlines();
4437 }
4438
4439 let vis = if self.at_visibility() {
4440 self.parse_visibility()
4441 } else {
4442 Visibility::Private
4443 };
4444
4445 if !matches!(self.peek().kind, TokenKind::Fn | TokenKind::Async) {
4446 if self.at(TokenKind::Eof) {
4447 break;
4448 }
4449 let _ = self.advance();
4450 continue;
4451 }
4452
4453 let fn_start = self.peek().span;
4454 let is_async = if self.at(TokenKind::Async) {
4455 let _ = self.advance();
4456 true
4457 } else {
4458 false
4459 };
4460 let _ = self.expect(TokenKind::Fn);
4461
4462 let fn_name_span = self.peek().span;
4463 let fn_name = if self.at(TokenKind::Ident) {
4464 let tok = self.advance();
4465 Ident {
4466 name: tok.literal.unwrap_or_default(),
4467 span: tok.span,
4468 }
4469 } else {
4470 self.diagnostics.error(
4471 DiagnosticCode {
4472 prefix: 'E',
4473 number: 2091,
4474 },
4475 format!("expected operation name, found `{}`", self.peek().kind),
4476 fn_name_span,
4477 );
4478 Ident {
4479 name: String::new(),
4480 span: fn_name_span,
4481 }
4482 };
4483
4484 let generic_params = self.parse_generic_params();
4485 let _ = self.expect(TokenKind::LParen);
4486 let params = self.parse_param_list();
4487 let _ = self.expect(TokenKind::RParen);
4488
4489 let return_type = if self.at(TokenKind::ThinArrow) {
4490 let _ = self.advance();
4491 Some(self.parse_type_expr())
4492 } else {
4493 None
4494 };
4495
4496 let effect_clause = self.parse_effect_clause();
4497 let where_clause = self.parse_where_clause();
4498
4499 let fn_end = self.peek().span;
4501
4502 ops.push(FnDecl {
4503 id: self.alloc_id(),
4504 span: Span::merge(fn_start, fn_end),
4505 annotations,
4506 visibility: vis,
4507 is_async,
4508 name: fn_name,
4509 generic_params,
4510 params,
4511 return_type,
4512 effect_clause,
4513 where_clause,
4514 body: None,
4515 });
4516 }
4517
4518 ops
4519 }
4520
4521 fn parse_module_handle_decl(&mut self) -> ModuleHandleDecl {
4525 let start = self.peek().span;
4526 let _ = self.advance(); let effect = self.parse_type_path();
4529 let _ = self.expect(TokenKind::With);
4530 let handler = self.parse_expr();
4531 let end = handler.span();
4532
4533 if self.at(TokenKind::Newline) {
4534 let _ = self.advance();
4535 }
4536
4537 ModuleHandleDecl {
4538 id: self.alloc_id(),
4539 span: Span::merge(start, end),
4540 effect,
4541 handler,
4542 }
4543 }
4544}
4545
4546#[cfg(test)]
4549mod tests {
4550 use super::*;
4551 use bock_errors::FileId;
4552 use bock_lexer::Lexer;
4553 use std::path::PathBuf;
4554
4555 fn parse(src: &str) -> (Module, DiagnosticBag) {
4556 let file_id = FileId(0);
4557 let source = SourceFile::new(file_id, PathBuf::from("test.bock"), src.to_string());
4558 let tokens = Lexer::new(&source).tokenize();
4559 let mut parser = Parser::new(tokens, &source);
4560 let module = parser.parse_module();
4561 let diags = std::mem::replace(&mut parser.diagnostics, DiagnosticBag::new());
4562 (module, diags)
4563 }
4564
4565 #[test]
4566 fn parse_empty_file() {
4567 let (m, diags) = parse("");
4568 assert!(m.path.is_none());
4569 assert!(m.imports.is_empty());
4570 assert!(m.items.is_empty());
4571 assert!(!diags.has_errors());
4572 }
4573
4574 #[test]
4575 fn parse_module_declaration() {
4576 let (m, diags) = parse("module app.auth\n");
4577 assert!(!diags.has_errors(), "unexpected errors");
4578 let path = m.path.expect("module decl should be present");
4579 let names: Vec<&str> = path.segments.iter().map(|s| s.name.as_str()).collect();
4580 assert_eq!(names, ["app", "auth"]);
4581 }
4582
4583 #[test]
4584 fn parse_module_declaration_missing() {
4585 let (m, diags) = parse("fn foo() {}\n");
4587 assert!(m.path.is_none());
4588 assert!(!diags.has_errors());
4589 }
4590
4591 #[test]
4592 fn parse_import_glob() {
4593 let (m, diags) = parse("use app.services.*\n");
4594 assert!(!diags.has_errors(), "unexpected errors");
4595 assert_eq!(m.imports.len(), 1);
4596 let imp = &m.imports[0];
4597 let path_segs: Vec<&str> = imp.path.segments.iter().map(|s| s.name.as_str()).collect();
4598 assert_eq!(path_segs, ["app", "services"]);
4599 assert_eq!(imp.items, ImportItems::Glob);
4600 }
4601
4602 #[test]
4603 fn parse_import_named_list() {
4604 let (m, diags) = parse("use core.collections.{List, Map}\n");
4605 assert!(!diags.has_errors(), "unexpected errors");
4606 assert_eq!(m.imports.len(), 1);
4607 let imp = &m.imports[0];
4608 let path_segs: Vec<&str> = imp.path.segments.iter().map(|s| s.name.as_str()).collect();
4609 assert_eq!(path_segs, ["core", "collections"]);
4610 match &imp.items {
4611 ImportItems::Named(names) => {
4612 let ns: Vec<&str> = names.iter().map(|n| n.name.name.as_str()).collect();
4613 assert_eq!(ns, ["List", "Map"]);
4614 }
4615 other => panic!("expected Named, got {other:?}"),
4616 }
4617 }
4618
4619 #[test]
4620 fn parse_module_path_with_effect_keyword_segment() {
4621 let (m, diags) = parse("module core.effect\n");
4624 assert!(!diags.has_errors(), "unexpected errors: {diags:?}");
4625 let path = m.path.expect("module decl should be present");
4626 let names: Vec<&str> = path.segments.iter().map(|s| s.name.as_str()).collect();
4627 assert_eq!(names, ["core", "effect"]);
4628 }
4629
4630 #[test]
4631 fn parse_module_path_with_handle_and_handling_segments() {
4632 let (m, diags) = parse("module core.handle.handling\n");
4634 assert!(!diags.has_errors(), "unexpected errors: {diags:?}");
4635 let path = m.path.expect("module decl should be present");
4636 let names: Vec<&str> = path.segments.iter().map(|s| s.name.as_str()).collect();
4637 assert_eq!(names, ["core", "handle", "handling"]);
4638 }
4639
4640 #[test]
4641 fn parse_import_named_list_with_effect_keyword_segment() {
4642 let (m, diags) = parse("use core.effect.{Log, console_log}\n");
4645 assert!(!diags.has_errors(), "unexpected errors: {diags:?}");
4646 assert_eq!(m.imports.len(), 1);
4647 let imp = &m.imports[0];
4648 let path_segs: Vec<&str> = imp.path.segments.iter().map(|s| s.name.as_str()).collect();
4649 assert_eq!(path_segs, ["core", "effect"]);
4650 match &imp.items {
4651 ImportItems::Named(names) => {
4652 let ns: Vec<&str> = names.iter().map(|n| n.name.name.as_str()).collect();
4653 assert_eq!(ns, ["Log", "console_log"]);
4654 }
4655 other => panic!("expected Named, got {other:?}"),
4656 }
4657 }
4658
4659 #[test]
4660 fn effect_keyword_still_parses_as_effect_declaration_at_item_position() {
4661 let (m, diags) = parse("effect Log {\n fn log() -> Void\n}\n");
4664 assert!(!diags.has_errors(), "unexpected errors: {diags:?}");
4665 assert!(m.path.is_none(), "no module decl expected");
4666 assert_eq!(m.items.len(), 1);
4667 let Item::Effect(e) = &m.items[0] else {
4668 panic!("expected Item::Effect, got {:?}", m.items[0]);
4669 };
4670 assert_eq!(e.name.name, "Log");
4671 assert_eq!(e.operations.len(), 1);
4672 assert_eq!(e.operations[0].name.name, "log");
4673 }
4674
4675 #[test]
4676 fn parse_import_single_name() {
4677 let (m, diags) = parse("use app.models.User\n");
4678 assert!(!diags.has_errors(), "unexpected errors");
4679 assert_eq!(m.imports.len(), 1);
4680 let imp = &m.imports[0];
4681 match &imp.items {
4685 ImportItems::Named(names) if names.len() == 1 => {
4686 assert_eq!(names[0].name.name, "User");
4687 }
4688 ImportItems::Module => {
4689 let last = imp.path.segments.last().expect("non-empty path");
4691 assert_eq!(last.name, "User");
4692 }
4693 other => panic!("unexpected import items: {other:?}"),
4694 }
4695 }
4696
4697 #[test]
4698 fn parse_multi_import_file() {
4699 let src = "\
4700module myapp.core\n\
4701use core.collections.{List, Map}\n\
4702use app.models.User\n\
4703use app.services.*\n\
4704";
4705 let (m, diags) = parse(src);
4706 assert!(!diags.has_errors(), "unexpected errors");
4707
4708 let path = m.path.expect("module decl");
4710 assert_eq!(path.segments[0].name, "myapp");
4711 assert_eq!(path.segments[1].name, "core");
4712
4713 assert_eq!(m.imports.len(), 3);
4715
4716 let first = &m.imports[0];
4718 assert!(matches!(first.items, ImportItems::Named(_)));
4719
4720 let last = &m.imports[2];
4722 assert_eq!(last.items, ImportItems::Glob);
4723 }
4724
4725 #[test]
4726 fn parser_new_and_diagnostics() {
4727 let file_id = FileId(0);
4728 let source = SourceFile::new(file_id, PathBuf::from("x.bock"), String::new());
4729 let tokens = Lexer::new(&source).tokenize();
4730 let parser = Parser::new(tokens, &source);
4731 assert!(!parser.diagnostics().has_errors());
4732 }
4733
4734 #[test]
4737 fn parse_simple_fn() {
4738 let (m, diags) = parse("fn greet() {}\n");
4739 assert!(!diags.has_errors(), "{diags:?}");
4740 assert_eq!(m.items.len(), 1);
4741 let Item::Fn(f) = &m.items[0] else {
4742 panic!("expected Fn")
4743 };
4744 assert_eq!(f.name.name, "greet");
4745 assert!(!f.is_async);
4746 assert_eq!(f.visibility, Visibility::Private);
4747 assert!(f.params.is_empty());
4748 assert!(f.return_type.is_none());
4749 }
4750
4751 #[test]
4752 fn parse_fn_with_params_and_return_type() {
4753 let (m, diags) = parse("fn add(x: Int, y: Int) -> Int {}\n");
4754 assert!(!diags.has_errors(), "{diags:?}");
4755 let Item::Fn(f) = &m.items[0] else { panic!() };
4756 assert_eq!(f.name.name, "add");
4757 assert_eq!(f.params.len(), 2);
4758 assert_eq!(
4759 f.params[0].pattern,
4760 Pattern::Bind {
4761 id: f.params[0].pattern.node_id(),
4762 span: f.params[0].pattern.span(),
4763 name: Ident {
4764 name: "x".into(),
4765 span: f.params[0].pattern.span()
4766 },
4767 }
4768 );
4769 assert!(f.return_type.is_some());
4770 let TypeExpr::Named { path, .. } = f.return_type.as_ref().unwrap() else {
4771 panic!()
4772 };
4773 assert_eq!(path.segments[0].name, "Int");
4774 }
4775
4776 #[test]
4777 fn parse_async_fn() {
4778 let (m, diags) = parse("async fn fetch() -> String {}\n");
4779 assert!(!diags.has_errors(), "{diags:?}");
4780 let Item::Fn(f) = &m.items[0] else { panic!() };
4781 assert!(f.is_async);
4782 assert_eq!(f.name.name, "fetch");
4783 }
4784
4785 #[test]
4786 fn parse_fn_with_visibility() {
4787 let (m, diags) = parse("public fn exposed() {}\n");
4788 assert!(!diags.has_errors(), "{diags:?}");
4789 let Item::Fn(f) = &m.items[0] else { panic!() };
4790 assert_eq!(f.visibility, Visibility::Public);
4791 }
4792
4793 #[test]
4794 fn parse_fn_with_generic_params() {
4795 let (m, diags) = parse("fn identity[T](x: T) -> T {}\n");
4796 assert!(!diags.has_errors(), "{diags:?}");
4797 let Item::Fn(f) = &m.items[0] else { panic!() };
4798 assert_eq!(f.generic_params.len(), 1);
4799 assert_eq!(f.generic_params[0].name.name, "T");
4800 }
4801
4802 #[test]
4803 fn parse_fn_with_generic_bounds() {
4804 let (m, diags) = parse("fn compare[T: Ord](a: T, b: T) -> Bool {}\n");
4805 assert!(!diags.has_errors(), "{diags:?}");
4806 let Item::Fn(f) = &m.items[0] else { panic!() };
4807 assert_eq!(f.generic_params[0].name.name, "T");
4808 assert_eq!(f.generic_params[0].bounds[0].segments[0].name, "Ord");
4809 }
4810
4811 #[test]
4812 fn parse_fn_with_where_clause() {
4813 let (m, diags) = parse("fn sorted[T](items: List) -> List where (T: Ord) {}\n");
4814 assert!(!diags.has_errors(), "{diags:?}");
4815 let Item::Fn(f) = &m.items[0] else { panic!() };
4816 assert_eq!(f.where_clause.len(), 1);
4817 assert_eq!(f.where_clause[0].param.name, "T");
4818 assert_eq!(f.where_clause[0].bounds[0].segments[0].name, "Ord");
4819 }
4820
4821 #[test]
4822 fn parse_fn_with_where_clause_multiple_constraints() {
4823 let (m, diags) = parse("fn dual[T, U](a: T, b: U) -> Bool where (T: Eq, U: Ord) {}\n");
4824 assert!(!diags.has_errors(), "{diags:?}");
4825 let Item::Fn(f) = &m.items[0] else { panic!() };
4826 assert_eq!(f.where_clause.len(), 2);
4827 }
4828
4829 #[test]
4830 fn parse_fn_with_effect_clause() {
4831 let (m, diags) = parse("fn log_msg(msg: String) with Log {}\n");
4832 assert!(!diags.has_errors(), "{diags:?}");
4833 let Item::Fn(f) = &m.items[0] else { panic!() };
4834 assert_eq!(f.effect_clause.len(), 1);
4835 assert_eq!(f.effect_clause[0].segments[0].name, "Log");
4836 }
4837
4838 #[test]
4839 fn parse_fn_with_multiple_effects() {
4840 let (m, diags) = parse("fn do_io(path: String) with Io, Log {}\n");
4841 assert!(!diags.has_errors(), "{diags:?}");
4842 let Item::Fn(f) = &m.items[0] else { panic!() };
4843 assert_eq!(f.effect_clause.len(), 2);
4844 }
4845
4846 #[test]
4847 fn parse_fn_with_default_param() {
4848 let (m, diags) = parse("fn greet(name: String, loud: Bool = false) {}\n");
4849 assert!(!diags.has_errors(), "{diags:?}");
4850 let Item::Fn(f) = &m.items[0] else { panic!() };
4851 assert!(f.params[1].default.is_some());
4852 let Expr::Literal {
4853 lit: Literal::Bool(false),
4854 ..
4855 } = f.params[1].default.as_ref().unwrap()
4856 else {
4857 panic!("expected false literal")
4858 };
4859 }
4860
4861 #[test]
4862 fn parse_fn_with_annotation() {
4863 let (m, diags) = parse("@test\nfn it_works() {}\n");
4864 assert!(!diags.has_errors(), "{diags:?}");
4865 let Item::Fn(f) = &m.items[0] else { panic!() };
4866 assert_eq!(f.annotations.len(), 1);
4867 assert_eq!(f.annotations[0].name.name, "test");
4868 }
4869
4870 #[test]
4873 fn parse_simple_record() {
4874 let (m, diags) = parse("record Point { x: Int, y: Int }\n");
4875 assert!(!diags.has_errors(), "{diags:?}");
4876 assert_eq!(m.items.len(), 1);
4877 let Item::Record(r) = &m.items[0] else {
4878 panic!("expected Record")
4879 };
4880 assert_eq!(r.name.name, "Point");
4881 assert_eq!(r.fields.len(), 2);
4882 assert_eq!(r.fields[0].name.name, "x");
4883 assert_eq!(r.fields[1].name.name, "y");
4884 }
4885
4886 #[test]
4887 fn parse_record_with_default_field_values() {
4888 let (m, diags) = parse("record Config { retries: Int = 3, verbose: Bool = false }\n");
4889 assert!(!diags.has_errors(), "{diags:?}");
4890 let Item::Record(r) = &m.items[0] else {
4891 panic!()
4892 };
4893 assert_eq!(r.name.name, "Config");
4894 assert!(r.fields[0].default.is_some());
4895 let Expr::Literal {
4896 lit: Literal::Int(n),
4897 ..
4898 } = r.fields[0].default.as_ref().unwrap()
4899 else {
4900 panic!("expected int literal")
4901 };
4902 assert_eq!(n, "3");
4903 assert!(r.fields[1].default.is_some());
4904 }
4905
4906 #[test]
4907 fn parse_record_with_generic_params() {
4908 let (m, diags) = parse("record Pair[A, B] { first: A, second: B }\n");
4909 assert!(!diags.has_errors(), "{diags:?}");
4910 let Item::Record(r) = &m.items[0] else {
4911 panic!()
4912 };
4913 assert_eq!(r.generic_params.len(), 2);
4914 assert_eq!(r.generic_params[0].name.name, "A");
4915 assert_eq!(r.generic_params[1].name.name, "B");
4916 }
4917
4918 #[test]
4919 fn parse_record_with_annotation() {
4920 let (m, diags) = parse("@derive(Equatable)\nrecord User { id: Int, name: String }\n");
4921 assert!(!diags.has_errors(), "{diags:?}");
4922 let Item::Record(r) = &m.items[0] else {
4923 panic!()
4924 };
4925 assert_eq!(r.annotations.len(), 1);
4926 assert_eq!(r.annotations[0].name.name, "derive");
4927 }
4928
4929 #[test]
4930 fn parse_record_with_visibility() {
4931 let (m, diags) = parse("public record Token { kind: Int }\n");
4932 assert!(!diags.has_errors(), "{diags:?}");
4933 let Item::Record(r) = &m.items[0] else {
4934 panic!()
4935 };
4936 assert_eq!(r.visibility, Visibility::Public);
4937 }
4938
4939 #[test]
4940 fn parse_record_optional_field_type() {
4941 let (m, diags) = parse("record Profile { name: String, bio: String? }\n");
4942 assert!(!diags.has_errors(), "{diags:?}");
4943 let Item::Record(r) = &m.items[0] else {
4944 panic!()
4945 };
4946 assert!(matches!(r.fields[1].ty, TypeExpr::Optional { .. }));
4947 }
4948
4949 #[test]
4952 fn parse_enum_unit_variants() {
4953 let (m, diags) = parse("enum Direction {\n North\n South\n East\n West\n}\n");
4954 assert!(!diags.has_errors(), "{diags:?}");
4955 let Item::Enum(e) = &m.items[0] else {
4956 panic!("expected Enum")
4957 };
4958 assert_eq!(e.name.name, "Direction");
4959 assert_eq!(e.variants.len(), 4);
4960 for v in &e.variants {
4961 assert!(matches!(v, EnumVariant::Unit { .. }));
4962 }
4963 }
4964
4965 #[test]
4966 fn parse_enum_struct_variants() {
4967 let src = "enum Shape {\n Circle { radius: Float }\n Rect { w: Float, h: Float }\n}\n";
4968 let (m, diags) = parse(src);
4969 assert!(!diags.has_errors(), "{diags:?}");
4970 let Item::Enum(e) = &m.items[0] else { panic!() };
4971 assert_eq!(e.variants.len(), 2);
4972 assert!(matches!(&e.variants[0], EnumVariant::Struct { .. }));
4973 assert!(matches!(&e.variants[1], EnumVariant::Struct { .. }));
4974 let EnumVariant::Struct { fields, .. } = &e.variants[0] else {
4975 unreachable!()
4976 };
4977 assert_eq!(fields[0].name.name, "radius");
4978 }
4979
4980 #[test]
4981 fn parse_enum_tuple_variants() {
4982 let src = "enum Result {\n Ok(Int)\n Err(String)\n}\n";
4983 let (m, diags) = parse(src);
4984 assert!(!diags.has_errors(), "{diags:?}");
4985 let Item::Enum(e) = &m.items[0] else { panic!() };
4986 assert_eq!(e.variants.len(), 2);
4987 assert!(matches!(&e.variants[0], EnumVariant::Tuple { .. }));
4988 assert!(matches!(&e.variants[1], EnumVariant::Tuple { .. }));
4989 let EnumVariant::Tuple { tys, .. } = &e.variants[0] else {
4990 unreachable!()
4991 };
4992 assert_eq!(tys.len(), 1);
4993 }
4994
4995 #[test]
4996 fn parse_enum_mixed_variants() {
4997 let src = "enum Expr {\n Num(Int)\n Add { left: Int, right: Int }\n Unit\n}\n";
4998 let (m, diags) = parse(src);
4999 assert!(!diags.has_errors(), "{diags:?}");
5000 let Item::Enum(e) = &m.items[0] else { panic!() };
5001 assert_eq!(e.variants.len(), 3);
5002 assert!(matches!(&e.variants[0], EnumVariant::Tuple { .. }));
5003 assert!(matches!(&e.variants[1], EnumVariant::Struct { .. }));
5004 assert!(matches!(&e.variants[2], EnumVariant::Unit { .. }));
5005 }
5006
5007 #[test]
5008 fn parse_enum_with_generics() {
5009 let src = "enum Option[T] {\n Some(T)\n None\n}\n";
5010 let (m, diags) = parse(src);
5011 assert!(!diags.has_errors(), "{diags:?}");
5012 let Item::Enum(e) = &m.items[0] else { panic!() };
5013 assert_eq!(e.generic_params.len(), 1);
5014 assert_eq!(e.generic_params[0].name.name, "T");
5015 assert_eq!(e.variants.len(), 2);
5016 }
5017
5018 #[test]
5019 fn parse_enum_with_annotation() {
5020 let src = "@derive(Equatable)\nenum Status {\n Active\n Inactive\n}\n";
5021 let (m, diags) = parse(src);
5022 assert!(!diags.has_errors(), "{diags:?}");
5023 let Item::Enum(e) = &m.items[0] else { panic!() };
5024 assert_eq!(e.annotations.len(), 1);
5025 }
5026
5027 #[test]
5030 fn parse_generic_type_in_field() {
5031 let (m, diags) = parse("record Container { items: List[Int] }\n");
5032 assert!(!diags.has_errors(), "{diags:?}");
5033 let Item::Record(r) = &m.items[0] else {
5034 panic!()
5035 };
5036 let TypeExpr::Named { args, .. } = &r.fields[0].ty else {
5037 panic!()
5038 };
5039 assert_eq!(args.len(), 1);
5040 }
5041
5042 #[test]
5043 fn parse_multiple_items_in_file() {
5044 let src = "fn foo() {}\nrecord Bar { x: Int }\nenum Baz { A\n B\n}\n";
5045 let (m, diags) = parse(src);
5046 assert!(!diags.has_errors(), "{diags:?}");
5047 assert_eq!(m.items.len(), 3);
5048 assert!(matches!(m.items[0], Item::Fn(_)));
5049 assert!(matches!(m.items[1], Item::Record(_)));
5050 assert!(matches!(m.items[2], Item::Enum(_)));
5051 }
5052
5053 #[test]
5056 fn parse_empty_class() {
5057 let src = "class Animal {}\n";
5058 let (m, diags) = parse(src);
5059 assert!(!diags.has_errors(), "{diags:?}");
5060 let Item::Class(c) = &m.items[0] else {
5061 panic!("expected Class")
5062 };
5063 assert_eq!(c.name.name, "Animal");
5064 assert!(c.base.is_none());
5065 assert!(c.traits.is_empty());
5066 assert!(c.fields.is_empty());
5067 assert!(c.methods.is_empty());
5068 }
5069
5070 #[test]
5071 fn parse_class_with_fields_and_method() {
5072 let src = "class Point {\n x: Int\n y: Int\n fn distance(self) -> Float { }\n}\n";
5073 let (m, diags) = parse(src);
5074 assert!(!diags.has_errors(), "{diags:?}");
5075 let Item::Class(c) = &m.items[0] else {
5076 panic!()
5077 };
5078 assert_eq!(c.name.name, "Point");
5079 assert_eq!(c.fields.len(), 2);
5080 assert_eq!(c.fields[0].name.name, "x");
5081 assert_eq!(c.fields[1].name.name, "y");
5082 assert_eq!(c.methods.len(), 1);
5083 assert_eq!(c.methods[0].name.name, "distance");
5084 }
5085
5086 #[test]
5087 fn parse_class_with_inheritance() {
5088 let src = "class Dog : Animal, Trainable {}\n";
5089 let (m, diags) = parse(src);
5090 assert!(!diags.has_errors(), "{diags:?}");
5091 let Item::Class(c) = &m.items[0] else {
5092 panic!()
5093 };
5094 assert_eq!(c.name.name, "Dog");
5095 let base = c.base.as_ref().expect("should have base");
5096 assert_eq!(base.segments[0].name, "Animal");
5097 assert_eq!(c.traits.len(), 1);
5098 assert_eq!(c.traits[0].segments[0].name, "Trainable");
5099 }
5100
5101 #[test]
5102 fn parse_class_with_generics() {
5103 let src = "class Box[T] {\n value: T\n}\n";
5104 let (m, diags) = parse(src);
5105 assert!(!diags.has_errors(), "{diags:?}");
5106 let Item::Class(c) = &m.items[0] else {
5107 panic!()
5108 };
5109 assert_eq!(c.generic_params.len(), 1);
5110 assert_eq!(c.generic_params[0].name.name, "T");
5111 assert_eq!(c.fields.len(), 1);
5112 }
5113
5114 #[test]
5115 fn parse_class_with_annotation() {
5116 let src = "@derive(Equatable)\nclass User {\n name: String\n}\n";
5117 let (m, diags) = parse(src);
5118 assert!(!diags.has_errors(), "{diags:?}");
5119 let Item::Class(c) = &m.items[0] else {
5120 panic!()
5121 };
5122 assert_eq!(c.annotations.len(), 1);
5123 assert_eq!(c.annotations[0].name.name, "derive");
5124 }
5125
5126 #[test]
5127 fn parse_public_class() {
5128 let src = "public class Foo {}\n";
5129 let (m, diags) = parse(src);
5130 assert!(!diags.has_errors(), "{diags:?}");
5131 let Item::Class(c) = &m.items[0] else {
5132 panic!()
5133 };
5134 assert_eq!(c.visibility, Visibility::Public);
5135 }
5136
5137 #[test]
5138 fn parse_empty_trait() {
5139 let src = "trait Printable {}\n";
5140 let (m, diags) = parse(src);
5141 assert!(!diags.has_errors(), "{diags:?}");
5142 let Item::Trait(t) = &m.items[0] else {
5143 panic!("expected Trait")
5144 };
5145 assert_eq!(t.name.name, "Printable");
5146 assert!(!t.is_platform);
5147 assert!(t.methods.is_empty());
5148 assert!(t.associated_types.is_empty());
5149 }
5150
5151 #[test]
5152 fn parse_trait_with_required_and_default_methods() {
5153 let src = "trait Display {\n fn show(self) -> String\n fn debug(self) -> String { }\n}\n";
5154 let (m, diags) = parse(src);
5155 assert!(!diags.has_errors(), "{diags:?}");
5156 let Item::Trait(t) = &m.items[0] else {
5157 panic!()
5158 };
5159 assert_eq!(t.methods.len(), 2);
5160 assert_eq!(t.methods[0].name.name, "show");
5161 assert_eq!(t.methods[1].name.name, "debug");
5162 }
5163
5164 #[test]
5165 fn parse_trait_with_associated_type() {
5166 let src = "trait Collection {\n type Item\n fn len(self) -> Int\n}\n";
5167 let (m, diags) = parse(src);
5168 assert!(!diags.has_errors(), "{diags:?}");
5169 let Item::Trait(t) = &m.items[0] else {
5170 panic!()
5171 };
5172 assert_eq!(t.associated_types.len(), 1);
5173 assert_eq!(t.associated_types[0].name.name, "Item");
5174 assert_eq!(t.methods.len(), 1);
5175 }
5176
5177 #[test]
5178 fn parse_trait_associated_type_with_bound() {
5179 let src = "trait Keyed {\n type Key: Hashable\n}\n";
5180 let (m, diags) = parse(src);
5181 assert!(!diags.has_errors(), "{diags:?}");
5182 let Item::Trait(t) = &m.items[0] else {
5183 panic!()
5184 };
5185 assert_eq!(t.associated_types[0].bounds.len(), 1);
5186 assert_eq!(t.associated_types[0].bounds[0].segments[0].name, "Hashable");
5187 }
5188
5189 #[test]
5190 fn parse_trait_with_generics() {
5191 let src = "trait Functor[F] {\n fn map(self) -> F\n}\n";
5192 let (m, diags) = parse(src);
5193 assert!(!diags.has_errors(), "{diags:?}");
5194 let Item::Trait(t) = &m.items[0] else {
5195 panic!()
5196 };
5197 assert_eq!(t.generic_params.len(), 1);
5198 assert_eq!(t.generic_params[0].name.name, "F");
5199 }
5200
5201 #[test]
5202 fn parse_platform_trait() {
5203 let src = "platform trait FileSystem {\n fn read(path: String) -> String\n}\n";
5204 let (m, diags) = parse(src);
5205 assert!(!diags.has_errors(), "{diags:?}");
5206 let Item::PlatformTrait(t) = &m.items[0] else {
5207 panic!("expected PlatformTrait")
5208 };
5209 assert_eq!(t.name.name, "FileSystem");
5210 assert!(t.is_platform);
5211 assert_eq!(t.methods.len(), 1);
5212 }
5213
5214 #[test]
5215 fn parse_impl_type() {
5216 let src = "impl Dog {\n fn bark(self) -> String { }\n}\n";
5217 let (m, diags) = parse(src);
5218 assert!(!diags.has_errors(), "{diags:?}");
5219 let Item::Impl(b) = &m.items[0] else {
5220 panic!("expected Impl")
5221 };
5222 assert!(b.trait_path.is_none());
5223 assert_eq!(b.methods.len(), 1);
5224 assert_eq!(b.methods[0].name.name, "bark");
5225 }
5226
5227 #[test]
5228 fn parse_impl_trait_for_type() {
5229 let src = "impl Printable for Dog {\n fn show(self) -> String { }\n}\n";
5230 let (m, diags) = parse(src);
5231 assert!(!diags.has_errors(), "{diags:?}");
5232 let Item::Impl(b) = &m.items[0] else { panic!() };
5233 let trait_path = b.trait_path.as_ref().expect("should have trait path");
5234 assert_eq!(trait_path.segments[0].name, "Printable");
5235 assert_eq!(b.methods.len(), 1);
5236 }
5237
5238 #[test]
5239 fn parse_impl_with_generics() {
5240 let src = "impl[T] Display for Box[T] {\n fn show(self) -> String { }\n}\n";
5241 let (m, diags) = parse(src);
5242 assert!(!diags.has_errors(), "{diags:?}");
5243 let Item::Impl(b) = &m.items[0] else { panic!() };
5244 assert_eq!(b.generic_params.len(), 1);
5245 assert_eq!(b.generic_params[0].name.name, "T");
5246 let trait_path = b.trait_path.as_ref().expect("trait path");
5247 assert_eq!(trait_path.segments[0].name, "Display");
5248 }
5249
5250 #[test]
5251 fn parse_impl_empty_body() {
5252 let src = "impl Animal {}\n";
5253 let (m, diags) = parse(src);
5254 assert!(!diags.has_errors(), "{diags:?}");
5255 let Item::Impl(b) = &m.items[0] else { panic!() };
5256 assert!(b.methods.is_empty());
5257 }
5258
5259 #[test]
5260 fn parse_mixed_items_with_class_trait_impl() {
5261 let src = concat!(
5262 "trait Greet {\n fn hello(self) -> String\n}\n",
5263 "class Cat {}\n",
5264 "impl Greet for Cat {\n fn hello(self) -> String { }\n}\n",
5265 );
5266 let (m, diags) = parse(src);
5267 assert!(!diags.has_errors(), "{diags:?}");
5268 assert_eq!(m.items.len(), 3);
5269 assert!(matches!(m.items[0], Item::Trait(_)));
5270 assert!(matches!(m.items[1], Item::Class(_)));
5271 assert!(matches!(m.items[2], Item::Impl(_)));
5272 }
5273
5274 fn parse_fn_body(src: &str) -> (Expr, DiagnosticBag) {
5278 let src = format!("fn test() {{\n{src}\n}}");
5279 let (m, diags) = parse(&src);
5280 let fn_decl = match m.items.first().expect("expected fn") {
5281 Item::Fn(f) => f.clone(),
5282 _ => panic!("expected fn item"),
5283 };
5284 let body = fn_decl.body.expect("expected function body");
5285 let tail = body.tail.expect("expected tail expression");
5286 (*tail, diags)
5287 }
5288
5289 fn parse_expr_str(src: &str) -> (Expr, DiagnosticBag) {
5291 parse_fn_body(src)
5292 }
5293
5294 #[test]
5295 fn expr_integer_literal() {
5296 let (e, diags) = parse_expr_str("42");
5297 assert!(!diags.has_errors(), "{diags:?}");
5298 assert!(matches!(e, Expr::Literal { lit: Literal::Int(ref s), .. } if s == "42"));
5299 }
5300
5301 #[test]
5302 fn expr_float_literal() {
5303 let (e, diags) = parse_expr_str("3.14");
5304 assert!(!diags.has_errors(), "{diags:?}");
5305 assert!(matches!(
5306 e,
5307 Expr::Literal {
5308 lit: Literal::Float(_),
5309 ..
5310 }
5311 ));
5312 }
5313
5314 #[test]
5315 fn expr_bool_literal() {
5316 let (e, diags) = parse_expr_str("true");
5317 assert!(!diags.has_errors(), "{diags:?}");
5318 assert!(matches!(
5319 e,
5320 Expr::Literal {
5321 lit: Literal::Bool(true),
5322 ..
5323 }
5324 ));
5325
5326 let (e2, _) = parse_expr_str("false");
5327 assert!(matches!(
5328 e2,
5329 Expr::Literal {
5330 lit: Literal::Bool(false),
5331 ..
5332 }
5333 ));
5334 }
5335
5336 #[test]
5337 fn expr_string_literal() {
5338 let (e, diags) = parse_expr_str(r#""hello""#);
5339 assert!(!diags.has_errors(), "{diags:?}");
5340 assert!(matches!(
5341 e,
5342 Expr::Literal {
5343 lit: Literal::String(_),
5344 ..
5345 }
5346 ));
5347 }
5348
5349 #[test]
5350 fn expr_identifier() {
5351 let (e, diags) = parse_expr_str("foo");
5352 assert!(!diags.has_errors(), "{diags:?}");
5353 assert!(matches!(e, Expr::Identifier { ref name, .. } if name.name == "foo"));
5354 }
5355
5356 #[test]
5357 fn expr_binary_add() {
5358 let (e, diags) = parse_expr_str("1 + 2");
5359 assert!(!diags.has_errors(), "{diags:?}");
5360 assert!(matches!(e, Expr::Binary { op: BinOp::Add, .. }));
5361 }
5362
5363 #[test]
5364 fn expr_binary_precedence_mul_over_add() {
5365 let (e, diags) = parse_expr_str("1 + 2 * 3");
5367 assert!(!diags.has_errors(), "{diags:?}");
5368 match e {
5369 Expr::Binary {
5370 op: BinOp::Add,
5371 right,
5372 ..
5373 } => {
5374 assert!(matches!(*right, Expr::Binary { op: BinOp::Mul, .. }));
5375 }
5376 _ => panic!("expected Add binary expr, got {e:?}"),
5377 }
5378 }
5379
5380 #[test]
5381 fn expr_binary_left_associative() {
5382 let (e, diags) = parse_expr_str("1 - 2 - 3");
5384 assert!(!diags.has_errors(), "{diags:?}");
5385 match e {
5386 Expr::Binary {
5387 op: BinOp::Sub,
5388 left,
5389 right,
5390 ..
5391 } => {
5392 assert!(matches!(*left, Expr::Binary { op: BinOp::Sub, .. }));
5393 assert!(matches!(*right, Expr::Literal { .. }));
5394 }
5395 _ => panic!("expected Sub binary expr"),
5396 }
5397 }
5398
5399 #[test]
5400 fn expr_power_right_associative() {
5401 let (e, diags) = parse_expr_str("2 ** 3 ** 4");
5403 assert!(!diags.has_errors(), "{diags:?}");
5404 match e {
5405 Expr::Binary {
5406 op: BinOp::Pow,
5407 left,
5408 right,
5409 ..
5410 } => {
5411 assert!(matches!(*left, Expr::Literal { .. }));
5412 assert!(matches!(*right, Expr::Binary { op: BinOp::Pow, .. }));
5413 }
5414 _ => panic!("expected Pow binary expr"),
5415 }
5416 }
5417
5418 #[test]
5419 fn expr_comparison_operators() {
5420 for (src, expected_op) in [
5421 ("a == b", BinOp::Eq),
5422 ("a != b", BinOp::Ne),
5423 ("a < b", BinOp::Lt),
5424 ("a > b", BinOp::Gt),
5425 ("a <= b", BinOp::Le),
5426 ("a >= b", BinOp::Ge),
5427 ] {
5428 let (e, diags) = parse_expr_str(src);
5429 assert!(!diags.has_errors(), "errors for {src}: {diags:?}");
5430 assert!(
5431 matches!(&e, Expr::Binary { op, .. } if *op == expected_op),
5432 "{src} expected {expected_op:?}"
5433 );
5434 }
5435 }
5436
5437 #[test]
5438 fn expr_logical_and_or() {
5439 let (e, diags) = parse_expr_str("a && b");
5440 assert!(!diags.has_errors(), "{diags:?}");
5441 assert!(matches!(e, Expr::Binary { op: BinOp::And, .. }));
5442
5443 let (e2, _) = parse_expr_str("a || b");
5444 assert!(matches!(e2, Expr::Binary { op: BinOp::Or, .. }));
5445 }
5446
5447 #[test]
5448 fn expr_and_binds_tighter_than_or() {
5449 let (e, diags) = parse_expr_str("a || b && c");
5451 assert!(!diags.has_errors(), "{diags:?}");
5452 match e {
5453 Expr::Binary {
5454 op: BinOp::Or,
5455 right,
5456 ..
5457 } => {
5458 assert!(matches!(*right, Expr::Binary { op: BinOp::And, .. }));
5459 }
5460 _ => panic!("expected Or expr"),
5461 }
5462 }
5463
5464 #[test]
5465 fn expr_assignment() {
5466 let (e, diags) = parse_expr_str("x = 5");
5467 assert!(!diags.has_errors(), "{diags:?}");
5468 assert!(matches!(
5469 e,
5470 Expr::Assign {
5471 op: AssignOp::Assign,
5472 ..
5473 }
5474 ));
5475 }
5476
5477 #[test]
5478 fn expr_compound_assignment() {
5479 for (src, expected_op) in [
5480 ("x += 1", AssignOp::AddAssign),
5481 ("x -= 1", AssignOp::SubAssign),
5482 ("x *= 2", AssignOp::MulAssign),
5483 ("x /= 2", AssignOp::DivAssign),
5484 ("x %= 3", AssignOp::RemAssign),
5485 ] {
5486 let (e, diags) = parse_expr_str(src);
5487 assert!(!diags.has_errors(), "errors for {src}: {diags:?}");
5488 assert!(
5489 matches!(&e, Expr::Assign { op, .. } if *op == expected_op),
5490 "expected {expected_op:?} for {src}"
5491 );
5492 }
5493 }
5494
5495 #[test]
5496 fn expr_unary_neg() {
5497 let (e, diags) = parse_expr_str("-x");
5498 assert!(!diags.has_errors(), "{diags:?}");
5499 assert!(matches!(
5500 e,
5501 Expr::Unary {
5502 op: UnaryOp::Neg,
5503 ..
5504 }
5505 ));
5506 }
5507
5508 #[test]
5509 fn expr_unary_not() {
5510 let (e, diags) = parse_expr_str("!flag");
5511 assert!(!diags.has_errors(), "{diags:?}");
5512 assert!(matches!(
5513 e,
5514 Expr::Unary {
5515 op: UnaryOp::Not,
5516 ..
5517 }
5518 ));
5519 }
5520
5521 #[test]
5522 fn expr_unary_bitnot() {
5523 let (e, diags) = parse_expr_str("~x");
5524 assert!(!diags.has_errors(), "{diags:?}");
5525 assert!(matches!(
5526 e,
5527 Expr::Unary {
5528 op: UnaryOp::BitNot,
5529 ..
5530 }
5531 ));
5532 }
5533
5534 #[test]
5535 fn expr_try_operator() {
5536 let (e, diags) = parse_expr_str("result?");
5537 assert!(!diags.has_errors(), "{diags:?}");
5538 assert!(matches!(e, Expr::Try { .. }));
5539 }
5540
5541 #[test]
5542 fn expr_field_access() {
5543 let (e, diags) = parse_expr_str("obj.field");
5544 assert!(!diags.has_errors(), "{diags:?}");
5545 assert!(matches!(e, Expr::FieldAccess { ref field, .. } if field.name == "field"));
5546 }
5547
5548 #[test]
5549 fn expr_method_call() {
5550 let (e, diags) = parse_expr_str("obj.method(1, 2)");
5551 assert!(!diags.has_errors(), "{diags:?}");
5552 assert!(matches!(e, Expr::MethodCall { ref method, .. } if method.name == "method"));
5553 }
5554
5555 #[test]
5556 fn expr_tuple_index_diagnostic() {
5557 let (_e, diags) = parse_expr_str("t.0");
5562 assert!(diags.has_errors(), "expected an error for `t.0`");
5563 let diag = diags
5564 .iter()
5565 .find(|d| d.code.prefix == 'E' && d.code.number == 2092)
5566 .expect("expected an E2092 tuple-index diagnostic");
5567 assert!(
5568 diag.message
5569 .contains("tuple positional indexing `t.0` is not available in v1"),
5570 "unexpected message: {}",
5571 diag.message
5572 );
5573 assert!(
5574 diag.message.contains("destructure with `let (a, b) = t`"),
5575 "diagnostic should suggest destructuring: {}",
5576 diag.message
5577 );
5578 assert!(
5580 !diags
5581 .iter()
5582 .any(|d| d.code.prefix == 'E' && d.code.number == 2020),
5583 "should not emit the generic E2020 for `t.0`"
5584 );
5585 }
5586
5587 #[test]
5588 fn expr_tuple_index_recovers_single_error() {
5589 let (_e, diags) = parse_expr_str("t.0 + 1");
5592 assert_eq!(
5593 diags.error_count(),
5594 1,
5595 "expected exactly one error from `t.0 + 1`, got {diags:?}"
5596 );
5597 }
5598
5599 #[test]
5600 fn expr_tuple_index_float_chain_diagnostic() {
5601 let (_e, diags) = parse_expr_str("t.0.0");
5605 assert!(
5606 diags
5607 .iter()
5608 .any(|d| d.code.prefix == 'E' && d.code.number == 2092),
5609 "expected an E2092 tuple-index diagnostic for `t.0.0`, got {diags:?}"
5610 );
5611 }
5612
5613 #[test]
5614 fn tuple_destructuring_still_parses() {
5615 let src = "fn test() {\n let t = (1, 2)\n let (a, b) = t\n a\n}";
5618 let (_m, diags) = parse(src);
5619 assert!(!diags.has_errors(), "{diags:?}");
5620 }
5621
5622 #[test]
5623 fn expr_function_call() {
5624 let (e, diags) = parse_expr_str("foo(1, 2, 3)");
5625 assert!(!diags.has_errors(), "{diags:?}");
5626 match e {
5627 Expr::Call { args, .. } => assert_eq!(args.len(), 3),
5628 _ => panic!("expected Call"),
5629 }
5630 }
5631
5632 #[test]
5633 fn expr_labeled_arg() {
5634 let (e, diags) = parse_expr_str("foo(x: 1, y: 2)");
5635 assert!(!diags.has_errors(), "{diags:?}");
5636 match e {
5637 Expr::Call { args, .. } => {
5638 assert_eq!(args.len(), 2);
5639 assert_eq!(args[0].label.as_ref().map(|i| i.name.as_str()), Some("x"));
5640 assert_eq!(args[1].label.as_ref().map(|i| i.name.as_str()), Some("y"));
5641 }
5642 _ => panic!("expected Call"),
5643 }
5644 }
5645
5646 #[test]
5647 fn expr_index_access() {
5648 let (e, diags) = parse_expr_str("arr[0]");
5649 assert!(!diags.has_errors(), "{diags:?}");
5650 assert!(matches!(e, Expr::Index { .. }));
5651 }
5652
5653 #[test]
5654 fn expr_postfix_chain() {
5655 let (e, diags) = parse_expr_str("obj.method(arg).field");
5657 assert!(!diags.has_errors(), "{diags:?}");
5658 match e {
5660 Expr::FieldAccess {
5661 ref field,
5662 ref object,
5663 ..
5664 } => {
5665 assert_eq!(field.name, "field");
5666 assert!(matches!(**object, Expr::MethodCall { .. }));
5667 }
5668 _ => panic!("expected FieldAccess, got {e:?}"),
5669 }
5670 }
5671
5672 #[test]
5673 fn expr_deep_postfix_chain() {
5674 let (e, diags) = parse_expr_str("a.b(c).d[0]?");
5675 assert!(!diags.has_errors(), "{diags:?}");
5676 assert!(matches!(e, Expr::Try { .. }));
5677 }
5678
5679 #[test]
5680 fn expr_pipe_operator() {
5681 let (e, diags) = parse_expr_str("data |> parse");
5682 assert!(!diags.has_errors(), "{diags:?}");
5683 assert!(matches!(e, Expr::Pipe { .. }));
5684 }
5685
5686 #[test]
5687 fn expr_pipe_chain() {
5688 let (e, diags) = parse_expr_str("a |> b |> c");
5690 assert!(!diags.has_errors(), "{diags:?}");
5691 match e {
5692 Expr::Pipe { left, .. } => {
5693 assert!(matches!(*left, Expr::Pipe { .. }));
5694 }
5695 _ => panic!("expected Pipe"),
5696 }
5697 }
5698
5699 #[test]
5700 fn expr_compose_operator() {
5701 let (e, diags) = parse_expr_str("parse >> validate");
5702 assert!(!diags.has_errors(), "{diags:?}");
5703 assert!(matches!(e, Expr::Compose { .. }));
5704 }
5705
5706 #[test]
5707 fn expr_range_exclusive() {
5708 let (e, diags) = parse_expr_str("1..10");
5709 assert!(!diags.has_errors(), "{diags:?}");
5710 assert!(matches!(
5711 e,
5712 Expr::Range {
5713 inclusive: false,
5714 ..
5715 }
5716 ));
5717 }
5718
5719 #[test]
5720 fn expr_range_inclusive() {
5721 let (e, diags) = parse_expr_str("1..=10");
5722 assert!(!diags.has_errors(), "{diags:?}");
5723 assert!(matches!(
5724 e,
5725 Expr::Range {
5726 inclusive: true,
5727 ..
5728 }
5729 ));
5730 }
5731
5732 #[test]
5733 fn expr_bitwise_operators() {
5734 let cases = [
5735 ("a & b", BinOp::BitAnd),
5736 ("a | b", BinOp::BitOr),
5737 ("a ^ b", BinOp::BitXor),
5738 ];
5739 for (src, op) in cases {
5740 let (e, diags) = parse_expr_str(src);
5741 assert!(!diags.has_errors(), "errors for {src}: {diags:?}");
5742 assert!(matches!(&e, Expr::Binary { op: actual, .. } if *actual == op));
5743 }
5744 }
5745
5746 #[test]
5747 fn shl_is_parse_error() {
5748 let (e, _) = parse_expr_str("a << 2");
5752 assert!(
5753 !matches!(&e, Expr::Binary { .. }),
5754 "`<<` must not be parsed as infix binary operator"
5755 );
5756 }
5757
5758 #[test]
5759 fn compose_still_works() {
5760 let (e, diags) = parse_expr_str("f >> g");
5762 assert!(!diags.has_errors(), "{diags:?}");
5763 assert!(matches!(e, Expr::Compose { .. }));
5764 }
5765
5766 #[test]
5767 fn precedence_add_mul_after_renumber() {
5768 let (e, diags) = parse_expr_str("a + b * c");
5770 assert!(!diags.has_errors(), "{diags:?}");
5771 match &e {
5773 Expr::Binary { op, right, .. } => {
5774 assert_eq!(*op, BinOp::Add);
5775 assert!(
5776 matches!(right.as_ref(), Expr::Binary { op: inner_op, .. } if *inner_op == BinOp::Mul),
5777 "right side of Add should be Mul"
5778 );
5779 }
5780 _ => panic!("expected Binary(Add) at top level"),
5781 }
5782 }
5783
5784 #[test]
5785 fn expr_placeholder() {
5786 let (e, diags) = parse_expr_str("_");
5787 assert!(!diags.has_errors(), "{diags:?}");
5788 assert!(matches!(e, Expr::Placeholder { .. }));
5789 }
5790
5791 #[test]
5792 fn expr_list_literal() {
5793 let (e, diags) = parse_expr_str("[1, 2, 3]");
5794 assert!(!diags.has_errors(), "{diags:?}");
5795 match e {
5796 Expr::ListLiteral { elems, .. } => assert_eq!(elems.len(), 3),
5797 _ => panic!("expected ListLiteral"),
5798 }
5799 }
5800
5801 #[test]
5802 fn expr_empty_list() {
5803 let (e, diags) = parse_expr_str("[]");
5804 assert!(!diags.has_errors(), "{diags:?}");
5805 assert!(matches!(e, Expr::ListLiteral { ref elems, .. } if elems.is_empty()));
5806 }
5807
5808 #[test]
5809 fn expr_tuple_literal() {
5810 let (e, diags) = parse_expr_str("(1, 2, 3)");
5811 assert!(!diags.has_errors(), "{diags:?}");
5812 match e {
5813 Expr::TupleLiteral { elems, .. } => assert_eq!(elems.len(), 3),
5814 _ => panic!("expected TupleLiteral, got {e:?}"),
5815 }
5816 }
5817
5818 #[test]
5819 fn expr_unit_literal() {
5820 let (e, diags) = parse_expr_str("()");
5821 assert!(!diags.has_errors(), "{diags:?}");
5822 assert!(matches!(
5823 e,
5824 Expr::Literal {
5825 lit: Literal::Unit,
5826 ..
5827 }
5828 ));
5829 }
5830
5831 #[test]
5832 fn expr_set_literal() {
5833 let (e, diags) = parse_expr_str(r#"#{"a", "b"}"#);
5834 assert!(!diags.has_errors(), "{diags:?}");
5835 match e {
5836 Expr::SetLiteral { elems, .. } => assert_eq!(elems.len(), 2),
5837 _ => panic!("expected SetLiteral"),
5838 }
5839 }
5840
5841 #[test]
5842 fn expr_map_literal() {
5843 let (e, diags) = parse_expr_str(r#"{"key": "value"}"#);
5844 assert!(!diags.has_errors(), "{diags:?}");
5845 match e {
5846 Expr::MapLiteral { entries, .. } => assert_eq!(entries.len(), 1),
5847 _ => panic!("expected MapLiteral, got {e:?}"),
5848 }
5849 }
5850
5851 #[test]
5852 fn empty_map_literal_with_type_annotation() {
5853 let src = "fn test() {\nlet m: Map[String, Int] = {}\n}";
5855 let (m, diags) = parse(src);
5856 assert!(!diags.has_errors(), "{diags:?}");
5857 let f = match m.items.first().unwrap() {
5858 Item::Fn(f) => f,
5859 other => panic!("expected Fn, got {other:?}"),
5860 };
5861 let body = f.body.as_ref().expect("expected body");
5862 let stmt = body.stmts.first().expect("expected a statement");
5863 match stmt {
5864 Stmt::Let(let_stmt) => match &let_stmt.value {
5865 Expr::MapLiteral { entries, .. } => assert!(entries.is_empty()),
5866 other => panic!("expected MapLiteral, got {other:?}"),
5867 },
5868 other => panic!("expected Let, got {other:?}"),
5869 }
5870 }
5871
5872 #[test]
5873 fn empty_braces_without_map_annotation_is_block() {
5874 let src = "fn test() {\nlet x = {}\n}";
5876 let (m, diags) = parse(src);
5877 assert!(!diags.has_errors(), "{diags:?}");
5878 let f = match m.items.first().unwrap() {
5879 Item::Fn(f) => f,
5880 other => panic!("expected Fn, got {other:?}"),
5881 };
5882 let body = f.body.as_ref().expect("expected body");
5883 let stmt = body.stmts.first().expect("expected a statement");
5884 match stmt {
5885 Stmt::Let(let_stmt) => assert!(
5886 matches!(&let_stmt.value, Expr::Block { .. }),
5887 "expected Block, got {:?}",
5888 let_stmt.value
5889 ),
5890 other => panic!("expected Let, got {other:?}"),
5891 }
5892 }
5893
5894 #[test]
5895 fn if_empty_block_still_works() {
5896 let (e, diags) = parse_expr_str("if (true) {}");
5898 assert!(!diags.has_errors(), "{diags:?}");
5899 assert!(matches!(e, Expr::If { .. }));
5900 }
5901
5902 #[test]
5903 fn expr_block_is_expression() {
5904 let src = "fn test() {\n{ let x = 1\nx }\n}";
5905 let (m, diags) = parse(src);
5906 assert!(!diags.has_errors(), "{diags:?}");
5907 let fn_decl = match m.items.first().unwrap() {
5908 Item::Fn(f) => f,
5909 _ => panic!(),
5910 };
5911 let body = fn_decl.body.as_ref().expect("expected function body");
5913 let tail = body.tail.as_ref().expect("expected tail");
5914 assert!(matches!(**tail, Expr::Block { .. }));
5915 }
5916
5917 #[test]
5918 fn expr_if_expression() {
5919 let (e, diags) = parse_expr_str("if (cond) { a } else { b }");
5920 assert!(!diags.has_errors(), "{diags:?}");
5921 match e {
5922 Expr::If {
5923 let_pattern,
5924 else_block,
5925 ..
5926 } => {
5927 assert!(let_pattern.is_none());
5928 assert!(else_block.is_some());
5929 }
5930 _ => panic!("expected If expr, got {e:?}"),
5931 }
5932 }
5933
5934 #[test]
5935 fn expr_if_no_else() {
5936 let (e, diags) = parse_expr_str("if (x > 0) { foo() }");
5937 assert!(!diags.has_errors(), "{diags:?}");
5938 assert!(matches!(
5939 e,
5940 Expr::If {
5941 else_block: None,
5942 ..
5943 }
5944 ));
5945 }
5946
5947 #[test]
5948 fn expr_if_let() {
5949 let (e, diags) = parse_expr_str("if (let Some(v) = opt) { v }");
5950 assert!(!diags.has_errors(), "{diags:?}");
5951 match e {
5952 Expr::If {
5953 let_pattern: Some(p),
5954 ..
5955 } => {
5956 assert!(matches!(p, Pattern::Constructor { .. }));
5957 }
5958 _ => panic!("expected if-let, got {e:?}"),
5959 }
5960 }
5961
5962 #[test]
5963 fn expr_if_else_if_chain() {
5964 let (e, diags) = parse_expr_str("if (a) { 1 } else if (b) { 2 } else { 3 }");
5965 assert!(!diags.has_errors(), "{diags:?}");
5966 match e {
5967 Expr::If {
5968 else_block: Some(else_e),
5969 ..
5970 } => {
5971 assert!(matches!(*else_e, Expr::If { .. }));
5972 }
5973 _ => panic!("expected if-else-if chain"),
5974 }
5975 }
5976
5977 #[test]
5978 fn expr_match() {
5979 let src = "match val {\n 0 => zero\n n => other\n}";
5980 let (e, diags) = parse_expr_str(src);
5981 assert!(!diags.has_errors(), "{diags:?}");
5982 match e {
5983 Expr::Match { arms, .. } => assert_eq!(arms.len(), 2),
5984 _ => panic!("expected Match"),
5985 }
5986 }
5987
5988 #[test]
5989 fn expr_match_with_guard() {
5990 let src = "match x {\n n if (n > 0) => pos\n _ => other\n}";
5991 let (e, diags) = parse_expr_str(src);
5992 assert!(!diags.has_errors(), "{diags:?}");
5993 match e {
5994 Expr::Match { arms, .. } => {
5995 assert!(arms[0].guard.is_some());
5996 assert!(arms[1].guard.is_none());
5997 }
5998 _ => panic!("expected Match"),
5999 }
6000 }
6001
6002 #[test]
6003 fn expr_lambda_single_param() {
6004 let (e, diags) = parse_expr_str("(x) => x * 2");
6005 assert!(!diags.has_errors(), "{diags:?}");
6006 match e {
6007 Expr::Lambda { params, body, .. } => {
6008 assert_eq!(params.len(), 1);
6009 assert!(matches!(*body, Expr::Binary { op: BinOp::Mul, .. }));
6010 }
6011 _ => panic!("expected Lambda, got {e:?}"),
6012 }
6013 }
6014
6015 #[test]
6016 fn expr_lambda_no_params() {
6017 let (e, diags) = parse_expr_str("() => 42");
6018 assert!(!diags.has_errors(), "{diags:?}");
6019 match e {
6020 Expr::Lambda { params, .. } => assert_eq!(params.len(), 0),
6021 _ => panic!("expected Lambda"),
6022 }
6023 }
6024
6025 #[test]
6026 fn expr_lambda_block_body() {
6027 let (e, diags) = parse_expr_str("(a, b) => { a + b }");
6028 assert!(!diags.has_errors(), "{diags:?}");
6029 match e {
6030 Expr::Lambda { params, body, .. } => {
6031 assert_eq!(params.len(), 2);
6032 assert!(matches!(*body, Expr::Block { .. }));
6033 }
6034 _ => panic!("expected Lambda"),
6035 }
6036 }
6037
6038 #[test]
6039 fn expr_lambda_typed_params() {
6040 let (e, diags) = parse_expr_str("(x: Int) => x");
6041 assert!(!diags.has_errors(), "{diags:?}");
6042 match e {
6043 Expr::Lambda { params, .. } => {
6044 assert_eq!(params.len(), 1);
6045 assert!(params[0].ty.is_some());
6046 }
6047 _ => panic!("expected Lambda"),
6048 }
6049 }
6050
6051 #[test]
6052 fn expr_return() {
6053 let (e, diags) = parse_expr_str("return 42");
6054 assert!(!diags.has_errors(), "{diags:?}");
6055 assert!(matches!(e, Expr::Return { value: Some(_), .. }));
6056 }
6057
6058 #[test]
6059 fn expr_return_void() {
6060 let src = "fn test() {\nreturn\n}";
6062 let (m, diags) = parse(src);
6063 assert!(!diags.has_errors(), "{diags:?}");
6064 let fn_decl = match m.items.first().unwrap() {
6065 Item::Fn(f) => f,
6066 _ => panic!(),
6067 };
6068 let body = fn_decl.body.as_ref().expect("expected function body");
6070 let has_return = body
6071 .tail
6072 .as_ref()
6073 .map(|t| matches!(**t, Expr::Return { .. }))
6074 .or_else(|| {
6075 body.stmts
6076 .last()
6077 .map(|s| matches!(s, Stmt::Expr(Expr::Return { .. })))
6078 })
6079 .unwrap_or(false);
6080 assert!(has_return, "expected return expr in body");
6081 }
6082
6083 #[test]
6084 fn expr_await() {
6085 let (e, diags) = parse_expr_str("await foo()");
6086 assert!(!diags.has_errors(), "{diags:?}");
6087 assert!(matches!(e, Expr::Await { .. }));
6088 }
6089
6090 #[test]
6091 fn expr_await_postfix() {
6092 let (e, diags) = parse_expr_str("foo().await");
6093 assert!(!diags.has_errors(), "{diags:?}");
6094 assert!(matches!(e, Expr::Await { .. }));
6095 }
6096
6097 #[test]
6098 fn expr_unreachable() {
6099 let (e, diags) = parse_expr_str("unreachable");
6100 assert!(!diags.has_errors(), "{diags:?}");
6101 assert!(matches!(e, Expr::Unreachable { .. }));
6102 }
6103
6104 #[test]
6105 fn expr_is_simple_type() {
6106 let (e, diags) = parse_expr_str("value is Int");
6107 assert!(!diags.has_errors(), "{diags:?}");
6108 match &e {
6109 Expr::Is { type_expr, .. } => {
6110 if let TypeExpr::Named { path, args, .. } = type_expr {
6111 assert_eq!(path.segments[0].name, "Int");
6112 assert!(args.is_empty());
6113 } else {
6114 panic!("expected Named type_expr, got {type_expr:?}");
6115 }
6116 }
6117 _ => panic!("expected Expr::Is, got {e:?}"),
6118 }
6119 }
6120
6121 #[test]
6122 fn expr_is_generic_args_preserved() {
6123 let (e, diags) = parse_expr_str("x is Option[Int]");
6124 assert!(!diags.has_errors(), "{diags:?}");
6125 match &e {
6126 Expr::Is { type_expr, .. } => {
6127 if let TypeExpr::Named { path, args, .. } = type_expr {
6128 assert_eq!(path.segments[0].name, "Option");
6129 assert_eq!(args.len(), 1);
6130 } else {
6131 panic!("expected Named type_expr, got {type_expr:?}");
6132 }
6133 }
6134 _ => panic!("expected Expr::Is, got {e:?}"),
6135 }
6136 }
6137
6138 #[test]
6139 fn expr_is_multi_arg_generic() {
6140 let (e, diags) = parse_expr_str("x is Result[String, Error]");
6141 assert!(!diags.has_errors(), "{diags:?}");
6142 match &e {
6143 Expr::Is { type_expr, .. } => {
6144 if let TypeExpr::Named { path, args, .. } = type_expr {
6145 assert_eq!(path.segments[0].name, "Result");
6146 assert_eq!(args.len(), 2);
6147 } else {
6148 panic!("expected Named type_expr, got {type_expr:?}");
6149 }
6150 }
6151 _ => panic!("expected Expr::Is, got {e:?}"),
6152 }
6153 }
6154
6155 #[test]
6156 fn expr_is_nested_generic() {
6157 let (e, diags) = parse_expr_str("x is List[List[Int]]");
6158 assert!(!diags.has_errors(), "{diags:?}");
6159 match &e {
6160 Expr::Is { type_expr, .. } => {
6161 if let TypeExpr::Named { path, args, .. } = type_expr {
6162 assert_eq!(path.segments[0].name, "List");
6163 assert_eq!(args.len(), 1);
6164 if let TypeExpr::Named {
6166 path: inner,
6167 args: inner_args,
6168 ..
6169 } = &args[0]
6170 {
6171 assert_eq!(inner.segments[0].name, "List");
6172 assert_eq!(inner_args.len(), 1);
6173 } else {
6174 panic!("expected nested Named type_expr");
6175 }
6176 } else {
6177 panic!("expected Named type_expr, got {type_expr:?}");
6178 }
6179 }
6180 _ => panic!("expected Expr::Is, got {e:?}"),
6181 }
6182 }
6183
6184 #[test]
6185 fn expr_record_construct() {
6186 let (e, diags) = parse_expr_str("User { id: 1, name }");
6187 assert!(!diags.has_errors(), "{diags:?}");
6188 match e {
6189 Expr::RecordConstruct {
6190 path,
6191 fields,
6192 spread,
6193 ..
6194 } => {
6195 assert_eq!(path.segments[0].name, "User");
6196 assert_eq!(fields.len(), 2);
6197 assert_eq!(fields[0].name.name, "id");
6198 assert!(fields[0].value.is_some()); assert_eq!(fields[1].name.name, "name");
6200 assert!(fields[1].value.is_none()); assert!(spread.is_none());
6202 }
6203 _ => panic!("expected RecordConstruct, got {e:?}"),
6204 }
6205 }
6206
6207 #[test]
6208 fn expr_record_construct_with_spread() {
6209 let (e, diags) = parse_expr_str("User { name, ..defaults }");
6210 assert!(!diags.has_errors(), "{diags:?}");
6211 match e {
6212 Expr::RecordConstruct { spread, .. } => {
6213 assert!(spread.is_some());
6214 }
6215 _ => panic!("expected RecordConstruct"),
6216 }
6217 }
6218
6219 #[test]
6220 fn expr_let_statement_in_block() {
6221 let src = "fn test() {\nlet x = 42\nx\n}";
6222 let (m, diags) = parse(src);
6223 assert!(!diags.has_errors(), "{diags:?}");
6224 let fn_decl = match m.items.first().unwrap() {
6225 Item::Fn(f) => f,
6226 _ => panic!(),
6227 };
6228 let body = fn_decl.body.as_ref().expect("expected function body");
6229 assert_eq!(body.stmts.len(), 1);
6230 assert!(matches!(body.stmts[0], Stmt::Let(_)));
6231 assert!(body.tail.is_some());
6232 }
6233
6234 #[test]
6235 fn expr_let_mut_in_block() {
6236 let src = "fn test() {\nlet mut x = 0\nx\n}";
6237 let (m, diags) = parse(src);
6238 assert!(!diags.has_errors(), "{diags:?}");
6239 let fn_decl = match m.items.first().unwrap() {
6240 Item::Fn(f) => f,
6241 _ => panic!(),
6242 };
6243 let body = fn_decl.body.as_ref().expect("expected function body");
6244 assert!(
6245 matches!(&body.stmts[0], Stmt::Let(l) if matches!(l.pattern, Pattern::MutBind { .. }))
6246 );
6247 }
6248
6249 #[test]
6250 fn expr_for_loop_in_block() {
6251 let src = "fn test() {\nfor x in items { foo(x) }\n}";
6252 let (m, diags) = parse(src);
6253 assert!(!diags.has_errors(), "{diags:?}");
6254 let fn_decl = match m.items.first().unwrap() {
6255 Item::Fn(f) => f,
6256 _ => panic!(),
6257 };
6258 assert!(matches!(
6259 &fn_decl.body.as_ref().unwrap().stmts[0],
6260 Stmt::For(_)
6261 ));
6262 }
6263
6264 #[test]
6265 fn expr_while_loop_in_block() {
6266 let src = "fn test() {\nwhile (x > 0) { x -= 1 }\n}";
6267 let (m, diags) = parse(src);
6268 assert!(!diags.has_errors(), "{diags:?}");
6269 let fn_decl = match m.items.first().unwrap() {
6270 Item::Fn(f) => f,
6271 _ => panic!(),
6272 };
6273 assert!(matches!(
6274 &fn_decl.body.as_ref().unwrap().stmts[0],
6275 Stmt::While(_)
6276 ));
6277 }
6278
6279 #[test]
6280 fn expr_loop_in_block() {
6281 let src = "fn test() {\nloop { break }\n}";
6282 let (m, diags) = parse(src);
6283 assert!(!diags.has_errors(), "{diags:?}");
6284 let fn_decl = match m.items.first().unwrap() {
6285 Item::Fn(f) => f,
6286 _ => panic!(),
6287 };
6288 assert!(matches!(
6290 fn_decl.body.as_ref().unwrap().tail.as_deref(),
6291 Some(Expr::Loop { .. })
6292 ));
6293 }
6294
6295 #[test]
6296 fn expr_complex_expression() {
6297 let (e, diags) = parse_expr_str("data |> parse |> validate");
6299 assert!(!diags.has_errors(), "{diags:?}");
6300 match e {
6302 Expr::Pipe { left, .. } => {
6303 assert!(matches!(*left, Expr::Pipe { .. }));
6304 }
6305 _ => panic!("expected Pipe chain"),
6306 }
6307 }
6308
6309 #[test]
6310 fn expr_nested_binary() {
6311 let (e, diags) = parse_expr_str("(a + b) * c");
6313 assert!(!diags.has_errors(), "{diags:?}");
6314 match e {
6315 Expr::Binary {
6316 op: BinOp::Mul,
6317 left,
6318 ..
6319 } => {
6320 assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
6321 }
6322 _ => panic!("expected Mul expr"),
6323 }
6324 }
6325
6326 #[test]
6327 fn expr_pattern_constructor_in_match() {
6328 let src = "match opt {\n Some(x) => x\n None => 0\n}";
6329 let (e, diags) = parse_expr_str(src);
6330 assert!(!diags.has_errors(), "{diags:?}");
6331 match e {
6332 Expr::Match { arms, .. } => {
6333 assert!(matches!(&arms[0].pattern, Pattern::Constructor { .. }));
6334 assert!(matches!(&arms[1].pattern, Pattern::Constructor { .. }));
6335 }
6336 _ => panic!("expected Match"),
6337 }
6338 }
6339
6340 #[test]
6341 fn expr_pattern_wildcard_in_match() {
6342 let src = "match x {\n _ => 0\n}";
6343 let (e, diags) = parse_expr_str(src);
6344 assert!(!diags.has_errors(), "{diags:?}");
6345 match e {
6346 Expr::Match { arms, .. } => {
6347 assert!(matches!(&arms[0].pattern, Pattern::Wildcard { .. }));
6348 }
6349 _ => panic!("expected Match"),
6350 }
6351 }
6352
6353 #[test]
6354 fn expr_pattern_or() {
6355 let src = "match x {\n 1 | 2 => few\n _ => many\n}";
6356 let (e, diags) = parse_expr_str(src);
6357 assert!(!diags.has_errors(), "{diags:?}");
6358 match e {
6359 Expr::Match { arms, .. } => {
6360 assert!(matches!(&arms[0].pattern, Pattern::Or { .. }));
6361 }
6362 _ => panic!("expected Match"),
6363 }
6364 }
6365
6366 #[test]
6367 fn expr_if_as_primary_expression() {
6368 let src = "fn test() {\nlet x = if (cond) { a } else { b }\nx\n}";
6370 let (m, diags) = parse(src);
6371 assert!(!diags.has_errors(), "{diags:?}");
6372 let fn_decl = match m.items.first().unwrap() {
6373 Item::Fn(f) => f,
6374 _ => panic!(),
6375 };
6376 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6378 Stmt::Let(l) => {
6379 assert!(
6380 matches!(l.value, Expr::If { .. }),
6381 "expected If as let value"
6382 );
6383 }
6384 _ => panic!("expected Let stmt"),
6385 }
6386 }
6387
6388 #[test]
6389 fn expr_match_as_primary_expression() {
6390 let src = "fn test() {\nlet x = match val {\n1 => a\n_ => b\n}\nx\n}";
6392 let (m, diags) = parse(src);
6393 assert!(!diags.has_errors(), "{diags:?}");
6394 let fn_decl = match m.items.first().unwrap() {
6395 Item::Fn(f) => f,
6396 _ => panic!(),
6397 };
6398 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6399 Stmt::Let(l) => {
6400 assert!(
6401 matches!(l.value, Expr::Match { .. }),
6402 "expected Match as let value"
6403 );
6404 }
6405 _ => panic!("expected Let stmt"),
6406 }
6407 }
6408
6409 #[test]
6410 fn expr_loop_as_primary_expression() {
6411 let src = "fn test() {\nlet x = loop {\nbreak 42\n}\nx\n}";
6413 let (m, diags) = parse(src);
6414 assert!(!diags.has_errors(), "{diags:?}");
6415 let fn_decl = match m.items.first().unwrap() {
6416 Item::Fn(f) => f,
6417 _ => panic!(),
6418 };
6419 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6420 Stmt::Let(l) => {
6421 assert!(
6422 matches!(l.value, Expr::Loop { .. }),
6423 "expected Loop as let value"
6424 );
6425 }
6426 _ => panic!("expected Let stmt"),
6427 }
6428 }
6429
6430 #[test]
6431 fn expr_loop_as_function_arg() {
6432 let src = "fn test() {\nfoo(loop { break 1 })\n}";
6433 let (m, diags) = parse(src);
6434 assert!(!diags.has_errors(), "{diags:?}");
6435 let fn_decl = match m.items.first().unwrap() {
6436 Item::Fn(f) => f,
6437 _ => panic!(),
6438 };
6439 let tail = fn_decl
6441 .body
6442 .as_ref()
6443 .unwrap()
6444 .tail
6445 .as_ref()
6446 .expect("expected tail");
6447 match tail.as_ref() {
6448 Expr::Call { args, .. } => {
6449 assert!(
6450 matches!(args[0].value, Expr::Loop { .. }),
6451 "expected Loop as arg"
6452 );
6453 }
6454 _ => panic!("expected Call expr"),
6455 }
6456 }
6457
6458 #[test]
6459 fn stmt_let_with_type_annotation() {
6460 let src = "fn test() {\nlet x: Int = 42\nx\n}";
6461 let (m, diags) = parse(src);
6462 assert!(!diags.has_errors(), "{diags:?}");
6463 let fn_decl = match m.items.first().unwrap() {
6464 Item::Fn(f) => f,
6465 _ => panic!(),
6466 };
6467 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6468 Stmt::Let(l) => {
6469 assert!(l.ty.is_some(), "expected type annotation on let");
6470 }
6471 _ => panic!("expected Let stmt"),
6472 }
6473 }
6474
6475 #[test]
6476 fn stmt_let_with_tuple_destructuring() {
6477 let src = "fn test() {\nlet (x, y) = get_point()\nx\n}";
6478 let (m, diags) = parse(src);
6479 assert!(!diags.has_errors(), "{diags:?}");
6480 let fn_decl = match m.items.first().unwrap() {
6481 Item::Fn(f) => f,
6482 _ => panic!(),
6483 };
6484 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6485 Stmt::Let(l) => {
6486 assert!(
6487 matches!(l.pattern, Pattern::Tuple { .. }),
6488 "expected tuple pattern, got {:?}",
6489 l.pattern
6490 );
6491 }
6492 _ => panic!("expected Let stmt"),
6493 }
6494 }
6495
6496 #[test]
6497 fn stmt_guard_in_block() {
6498 let src = "fn test() {\nguard (x > 0) else { return }\nx\n}";
6499 let (m, diags) = parse(src);
6500 assert!(!diags.has_errors(), "{diags:?}");
6501 let fn_decl = match m.items.first().unwrap() {
6502 Item::Fn(f) => f,
6503 _ => panic!(),
6504 };
6505 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6506 Stmt::Guard(g) => {
6507 assert!(
6508 !g.else_block.stmts.is_empty()
6509 || g.else_block.tail.is_some()
6510 || !g.else_block.stmts.is_empty(),
6511 "expected non-empty else block"
6512 );
6513 }
6514 _ => panic!(
6515 "expected Guard stmt, got {:?}",
6516 fn_decl.body.as_ref().unwrap().stmts[0]
6517 ),
6518 }
6519 }
6520
6521 #[test]
6522 fn stmt_handling_block_with_multiple_bindings() {
6523 let src = "fn test() {\nhandling (Log with logger, Clock with mock) {\ndo_work()\n}\n}";
6524 let (m, diags) = parse(src);
6525 assert!(!diags.has_errors(), "{diags:?}");
6526 let fn_decl = match m.items.first().unwrap() {
6527 Item::Fn(f) => f,
6528 _ => panic!(),
6529 };
6530 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6531 Stmt::Handling(h) => {
6532 assert_eq!(h.handlers.len(), 2, "expected 2 handler bindings");
6533 }
6534 _ => panic!(
6535 "expected Handling stmt, got {:?}",
6536 fn_decl.body.as_ref().unwrap().stmts[0]
6537 ),
6538 }
6539 }
6540
6541 #[test]
6542 fn stmt_handling_block_single_binding() {
6543 let src = "fn test() {\nhandling (Log with handler) {\ndo_work()\n}\n}";
6544 let (m, diags) = parse(src);
6545 assert!(!diags.has_errors(), "{diags:?}");
6546 let fn_decl = match m.items.first().unwrap() {
6547 Item::Fn(f) => f,
6548 _ => panic!(),
6549 };
6550 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6551 Stmt::Handling(h) => {
6552 assert_eq!(h.handlers.len(), 1);
6553 }
6554 _ => panic!("expected Handling stmt"),
6555 }
6556 }
6557
6558 #[test]
6559 fn stmt_continuation_operator_at_end_of_line() {
6560 let src = "fn test() {\nlet x = a +\n b\nx\n}";
6562 let (m, diags) = parse(src);
6563 assert!(!diags.has_errors(), "{diags:?}");
6564 let fn_decl = match m.items.first().unwrap() {
6565 Item::Fn(f) => f,
6566 _ => panic!(),
6567 };
6568 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6569 Stmt::Let(l) => {
6570 assert!(
6571 matches!(l.value, Expr::Binary { op: BinOp::Add, .. }),
6572 "expected binary Add across lines"
6573 );
6574 }
6575 _ => panic!("expected Let stmt"),
6576 }
6577 }
6578
6579 #[test]
6580 fn stmt_continuation_pipe_at_start_of_next_line() {
6581 let src = "fn test() {\nlet x = data\n |> transform\nx\n}";
6583 let (m, diags) = parse(src);
6584 assert!(!diags.has_errors(), "{diags:?}");
6585 let fn_decl = match m.items.first().unwrap() {
6586 Item::Fn(f) => f,
6587 _ => panic!(),
6588 };
6589 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6590 Stmt::Let(l) => {
6591 assert!(
6592 matches!(l.value, Expr::Pipe { .. }),
6593 "expected Pipe across lines"
6594 );
6595 }
6596 _ => panic!("expected Let stmt"),
6597 }
6598 }
6599
6600 #[test]
6601 fn stmt_continuation_dot_at_start_of_next_line() {
6602 let src = "fn test() {\nlet x = obj\n .field\nx\n}";
6604 let (m, diags) = parse(src);
6605 assert!(!diags.has_errors(), "{diags:?}");
6606 let fn_decl = match m.items.first().unwrap() {
6607 Item::Fn(f) => f,
6608 _ => panic!(),
6609 };
6610 match &fn_decl.body.as_ref().unwrap().stmts[0] {
6611 Stmt::Let(l) => {
6612 assert!(
6613 matches!(l.value, Expr::FieldAccess { .. }),
6614 "expected FieldAccess across lines"
6615 );
6616 }
6617 _ => panic!("expected Let stmt"),
6618 }
6619 }
6620
6621 #[test]
6622 fn stmt_continuation_else_on_next_line() {
6623 let src = "fn test() -> Int {\nif (true) { 1 }\nelse { 2 }\n}";
6625 let (m, diags) = parse(src);
6626 assert!(!diags.has_errors(), "{diags:?}");
6627 let fn_decl = match m.items.first().unwrap() {
6628 Item::Fn(f) => f,
6629 _ => panic!(),
6630 };
6631 let tail = fn_decl
6633 .body
6634 .as_ref()
6635 .unwrap()
6636 .tail
6637 .as_ref()
6638 .expect("expected tail expr");
6639 match tail.as_ref() {
6640 Expr::If { else_block, .. } => {
6641 assert!(else_block.is_some(), "expected else block across lines");
6642 }
6643 other => panic!("expected If expr, got {other:?}"),
6644 }
6645 }
6646
6647 #[test]
6648 fn stmt_continuation_else_if_on_next_line() {
6649 let src = "fn test() -> Int {\nif (true) { 1 }\nelse if (false) { 2 }\nelse { 3 }\n}";
6651 let (m, diags) = parse(src);
6652 assert!(!diags.has_errors(), "{diags:?}");
6653 let fn_decl = match m.items.first().unwrap() {
6654 Item::Fn(f) => f,
6655 _ => panic!(),
6656 };
6657 let tail = fn_decl
6658 .body
6659 .as_ref()
6660 .unwrap()
6661 .tail
6662 .as_ref()
6663 .expect("expected tail expr");
6664 match tail.as_ref() {
6665 Expr::If { else_block, .. } => {
6666 let inner = else_block.as_ref().expect("expected else-if chain");
6667 assert!(
6668 matches!(inner.as_ref(), Expr::If { .. }),
6669 "expected nested If in else-if chain"
6670 );
6671 }
6672 other => panic!("expected If expr, got {other:?}"),
6673 }
6674 }
6675
6676 #[test]
6677 fn stmt_semicolon_separates_statements() {
6678 let src = "fn test() {\nlet x = 1; let y = 2\nx\n}";
6680 let (m, diags) = parse(src);
6681 assert!(!diags.has_errors(), "{diags:?}");
6682 let fn_decl = match m.items.first().unwrap() {
6683 Item::Fn(f) => f,
6684 _ => panic!(),
6685 };
6686 assert_eq!(
6687 fn_decl.body.as_ref().unwrap().stmts.len(),
6688 2,
6689 "expected 2 stmts from semicolon-separated line"
6690 );
6691 }
6692
6693 #[test]
6694 fn stmt_loop_break_with_value() {
6695 let src = "fn test() {\nloop { break 42 }\n}";
6697 let (m, diags) = parse(src);
6698 assert!(!diags.has_errors(), "{diags:?}");
6699 let fn_decl = match m.items.first().unwrap() {
6700 Item::Fn(f) => f,
6701 _ => panic!(),
6702 };
6703 match fn_decl.body.as_ref().unwrap().tail.as_deref() {
6705 Some(Expr::Loop { body, .. }) => {
6706 let has_break = body
6708 .stmts
6709 .iter()
6710 .any(|s| matches!(s, Stmt::Expr(Expr::Break { value: Some(_), .. })))
6711 || body
6712 .tail
6713 .as_ref()
6714 .is_some_and(|t| matches!(**t, Expr::Break { value: Some(_), .. }));
6715 assert!(has_break, "expected break with value in loop body");
6716 }
6717 _ => panic!("expected Loop tail expression"),
6718 }
6719 }
6720
6721 fn parse_pat(src: &str) -> (Pattern, DiagnosticBag) {
6724 let wrapped = format!("fn f() {{ match x {{\n{src}\n}} }}");
6726 let (m, diags) = parse(&wrapped);
6727 let fn_decl = match m.items.first().unwrap() {
6728 Item::Fn(f) => f,
6729 _ => panic!("expected fn"),
6730 };
6731 let mat = match fn_decl.body.as_ref().unwrap().tail.as_deref() {
6732 Some(Expr::Match { arms, .. }) => arms.clone(),
6733 _ => match fn_decl.body.as_ref().unwrap().stmts.first() {
6734 Some(Stmt::Expr(Expr::Match { arms, .. })) => arms.clone(),
6735 _ => panic!("expected match expression"),
6736 },
6737 };
6738 let pat = mat
6739 .into_iter()
6740 .next()
6741 .expect("expected at least one arm")
6742 .pattern;
6743 (pat, diags)
6744 }
6745
6746 #[test]
6747 fn pattern_wildcard() {
6748 let (pat, diags) = parse_pat("_ => 1");
6749 assert!(!diags.has_errors(), "{diags:?}");
6750 assert!(matches!(pat, Pattern::Wildcard { .. }));
6751 }
6752
6753 #[test]
6754 fn pattern_bind() {
6755 let (pat, diags) = parse_pat("name => 1");
6756 assert!(!diags.has_errors(), "{diags:?}");
6757 assert!(matches!(pat, Pattern::Bind { .. }));
6758 if let Pattern::Bind { name, .. } = pat {
6759 assert_eq!(name.name, "name");
6760 }
6761 }
6762
6763 #[test]
6764 fn pattern_mut_bind() {
6765 let (pat, diags) = parse_pat("mut x => 1");
6766 assert!(!diags.has_errors(), "{diags:?}");
6767 assert!(matches!(pat, Pattern::MutBind { .. }));
6768 if let Pattern::MutBind { name, .. } = pat {
6769 assert_eq!(name.name, "x");
6770 }
6771 }
6772
6773 #[test]
6774 fn pattern_literal_int() {
6775 let (pat, diags) = parse_pat("42 => 1");
6776 assert!(!diags.has_errors(), "{diags:?}");
6777 assert!(matches!(
6778 pat,
6779 Pattern::Literal {
6780 lit: Literal::Int(_),
6781 ..
6782 }
6783 ));
6784 }
6785
6786 #[test]
6787 fn pattern_literal_string() {
6788 let (pat, diags) = parse_pat(r#""hello" => 1"#);
6789 assert!(!diags.has_errors(), "{diags:?}");
6790 assert!(matches!(
6791 pat,
6792 Pattern::Literal {
6793 lit: Literal::String(_),
6794 ..
6795 }
6796 ));
6797 }
6798
6799 #[test]
6800 fn pattern_literal_bool_true() {
6801 let (pat, diags) = parse_pat("true => 1");
6802 assert!(!diags.has_errors(), "{diags:?}");
6803 assert!(matches!(
6804 pat,
6805 Pattern::Literal {
6806 lit: Literal::Bool(true),
6807 ..
6808 }
6809 ));
6810 }
6811
6812 #[test]
6813 fn pattern_literal_bool_false() {
6814 let (pat, diags) = parse_pat("false => 1");
6815 assert!(!diags.has_errors(), "{diags:?}");
6816 assert!(matches!(
6817 pat,
6818 Pattern::Literal {
6819 lit: Literal::Bool(false),
6820 ..
6821 }
6822 ));
6823 }
6824
6825 #[test]
6826 fn pattern_constructor_some() {
6827 let (pat, diags) = parse_pat("Some(x) => 1");
6828 assert!(!diags.has_errors(), "{diags:?}");
6829 if let Pattern::Constructor { fields, .. } = pat {
6830 assert_eq!(fields.len(), 1);
6831 assert!(matches!(fields[0], Pattern::Bind { .. }));
6832 } else {
6833 panic!("expected Constructor pattern");
6834 }
6835 }
6836
6837 #[test]
6838 fn pattern_constructor_err() {
6839 let (pat, diags) = parse_pat("Err(e) => 1");
6840 assert!(!diags.has_errors(), "{diags:?}");
6841 assert!(matches!(pat, Pattern::Constructor { .. }));
6842 }
6843
6844 #[test]
6845 fn pattern_record_shorthand() {
6846 let (pat, diags) = parse_pat("Point { x, y } => 1");
6847 assert!(!diags.has_errors(), "{diags:?}");
6848 if let Pattern::Record { fields, rest, .. } = pat {
6849 assert_eq!(fields.len(), 2);
6850 assert_eq!(fields[0].name.name, "x");
6851 assert!(
6852 fields[0].pattern.is_none(),
6853 "shorthand should have no sub-pattern"
6854 );
6855 assert_eq!(fields[1].name.name, "y");
6856 assert!(!rest, "no rest expected");
6857 } else {
6858 panic!("expected Record pattern");
6859 }
6860 }
6861
6862 #[test]
6863 fn pattern_record_with_rename() {
6864 let (pat, diags) = parse_pat("User { name: n, age: a } => 1");
6865 assert!(!diags.has_errors(), "{diags:?}");
6866 if let Pattern::Record { fields, rest, .. } = pat {
6867 assert_eq!(fields.len(), 2);
6868 assert_eq!(fields[0].name.name, "name");
6869 assert!(fields[0].pattern.is_some());
6870 assert!(!rest);
6871 } else {
6872 panic!("expected Record pattern");
6873 }
6874 }
6875
6876 #[test]
6877 fn pattern_record_with_rest() {
6878 let (pat, diags) = parse_pat("User { name: n, .. } => 1");
6879 assert!(!diags.has_errors(), "{diags:?}");
6880 if let Pattern::Record { fields, rest, .. } = pat {
6881 assert_eq!(fields.len(), 1);
6882 assert_eq!(fields[0].name.name, "name");
6883 assert!(rest, "rest flag should be set for `..`");
6884 } else {
6885 panic!("expected Record pattern");
6886 }
6887 }
6888
6889 #[test]
6890 fn pattern_tuple() {
6891 let (pat, diags) = parse_pat("(a, b, c) => 1");
6892 assert!(!diags.has_errors(), "{diags:?}");
6893 if let Pattern::Tuple { elems, .. } = pat {
6894 assert_eq!(elems.len(), 3);
6895 } else {
6896 panic!("expected Tuple pattern");
6897 }
6898 }
6899
6900 #[test]
6901 fn pattern_list_with_rest() {
6902 let (pat, diags) = parse_pat("[first, ..rest] => 1");
6903 assert!(!diags.has_errors(), "{diags:?}");
6904 if let Pattern::List { elems, rest, .. } = pat {
6905 assert_eq!(elems.len(), 1);
6906 assert!(matches!(elems[0], Pattern::Bind { .. }));
6907 let r = rest.expect("rest pattern expected");
6908 assert!(matches!(*r, Pattern::Bind { .. }));
6909 } else {
6910 panic!("expected List pattern");
6911 }
6912 }
6913
6914 #[test]
6915 fn pattern_list_rest_only() {
6916 let (pat, diags) = parse_pat("[..] => 1");
6917 assert!(!diags.has_errors(), "{diags:?}");
6918 if let Pattern::List { elems, rest, .. } = pat {
6919 assert!(elems.is_empty());
6920 assert!(rest.is_some());
6921 } else {
6922 panic!("expected List pattern");
6923 }
6924 }
6925
6926 #[test]
6927 fn pattern_or() {
6928 let (pat, diags) = parse_pat("A | B | C => 1");
6929 assert!(!diags.has_errors(), "{diags:?}");
6930 if let Pattern::Or { alternatives, .. } = pat {
6931 assert_eq!(alternatives.len(), 3);
6932 } else {
6933 panic!("expected Or pattern");
6934 }
6935 }
6936
6937 #[test]
6938 fn pattern_range() {
6939 let (pat, diags) = parse_pat("1..10 => 1");
6940 assert!(!diags.has_errors(), "{diags:?}");
6941 if let Pattern::Range { inclusive, .. } = pat {
6942 assert!(!inclusive);
6943 } else {
6944 panic!("expected Range pattern");
6945 }
6946 }
6947
6948 #[test]
6949 fn pattern_nested_constructor() {
6950 let (pat, diags) = parse_pat("Some(Ok((a, b))) => 1");
6952 assert!(!diags.has_errors(), "{diags:?}");
6953 if let Pattern::Constructor { fields, .. } = pat {
6954 assert_eq!(fields.len(), 1, "Some should have 1 field");
6955 if let Pattern::Constructor { fields: inner, .. } = &fields[0] {
6956 assert_eq!(inner.len(), 1, "Ok should have 1 field");
6957 assert!(matches!(inner[0], Pattern::Tuple { .. }));
6958 } else {
6959 panic!("expected inner Constructor (Ok)");
6960 }
6961 } else {
6962 panic!("expected outer Constructor (Some)");
6963 }
6964 }
6965
6966 #[test]
6967 fn pattern_or_in_match_arm() {
6968 let src = "fn f() { match x {\n1 | 2 => \"small\"\n_ => \"other\"\n} }";
6969 let (m, diags) = parse(src);
6970 assert!(!diags.has_errors(), "{diags:?}");
6971 let fn_decl = match m.items.first().unwrap() {
6972 Item::Fn(f) => f,
6973 _ => panic!(),
6974 };
6975 let arms = match fn_decl.body.as_ref().unwrap().tail.as_deref() {
6976 Some(Expr::Match { arms, .. }) => arms.clone(),
6977 _ => match fn_decl.body.as_ref().unwrap().stmts.first() {
6978 Some(Stmt::Expr(Expr::Match { arms, .. })) => arms.clone(),
6979 _ => panic!("expected match"),
6980 },
6981 };
6982 assert_eq!(arms.len(), 2);
6983 assert!(matches!(arms[0].pattern, Pattern::Or { .. }));
6984 }
6985
6986 #[test]
6987 fn pattern_guard_in_match_arm() {
6988 let src = "fn f() { match x {\nn if (n > 100) => \"large\"\n_ => \"other\"\n} }";
6989 let (m, diags) = parse(src);
6990 assert!(!diags.has_errors(), "{diags:?}");
6991 let fn_decl = match m.items.first().unwrap() {
6992 Item::Fn(f) => f,
6993 _ => panic!(),
6994 };
6995 let arms = match fn_decl.body.as_ref().unwrap().tail.as_deref() {
6996 Some(Expr::Match { arms, .. }) => arms.clone(),
6997 _ => match fn_decl.body.as_ref().unwrap().stmts.first() {
6998 Some(Stmt::Expr(Expr::Match { arms, .. })) => arms.clone(),
6999 _ => panic!("expected match"),
7000 },
7001 };
7002 assert_eq!(arms.len(), 2);
7003 assert!(arms[0].guard.is_some(), "first arm should have guard");
7004 assert!(arms[1].guard.is_none());
7005 }
7006
7007 #[test]
7008 fn pattern_full_match_example() {
7009 let src = r#"fn f() {
7020match value {
7021 0 => "zero"
7022 1 | 2 => "small"
7023 n if (n > 100) => "large"
7024 Point { x: 0, y } => "on y-axis"
7025 Some(Ok(v)) => "got it"
7026 [first, ..rest] => "head"
7027 _ => "other"
7028}
7029}"#;
7030 let (m, diags) = parse(src);
7031 assert!(!diags.has_errors(), "{diags:?}");
7032 let fn_decl = match m.items.first().unwrap() {
7033 Item::Fn(f) => f,
7034 _ => panic!(),
7035 };
7036 let arms = match fn_decl.body.as_ref().unwrap().tail.as_deref() {
7037 Some(Expr::Match { arms, .. }) => arms.clone(),
7038 _ => match fn_decl.body.as_ref().unwrap().stmts.first() {
7039 Some(Stmt::Expr(Expr::Match { arms, .. })) => arms.clone(),
7040 _ => panic!("expected match"),
7041 },
7042 };
7043 assert_eq!(arms.len(), 7);
7044 assert!(matches!(
7045 arms[0].pattern,
7046 Pattern::Literal {
7047 lit: Literal::Int(_),
7048 ..
7049 }
7050 ));
7051 assert!(matches!(arms[1].pattern, Pattern::Or { .. }));
7052 assert!(arms[2].guard.is_some());
7053 assert!(matches!(arms[3].pattern, Pattern::Record { .. }));
7054 assert!(matches!(arms[4].pattern, Pattern::Constructor { .. }));
7055 assert!(matches!(arms[5].pattern, Pattern::List { .. }));
7056 assert!(matches!(arms[6].pattern, Pattern::Wildcard { .. }));
7057 }
7058
7059 #[test]
7060 fn pattern_if_let() {
7061 let src = "fn f() { if (let Some(user) = find(id)) { consume(user) } }";
7062 let (m, diags) = parse(src);
7063 assert!(!diags.has_errors(), "{diags:?}");
7064 let fn_decl = match m.items.first().unwrap() {
7065 Item::Fn(f) => f,
7066 _ => panic!(),
7067 };
7068 let has_if_let = fn_decl.body.as_ref().unwrap().stmts.iter().any(|s| {
7070 matches!(
7071 s,
7072 Stmt::Expr(Expr::If {
7073 let_pattern: Some(_),
7074 ..
7075 })
7076 )
7077 }) || fn_decl
7078 .body
7079 .as_ref()
7080 .unwrap()
7081 .tail
7082 .as_ref()
7083 .is_some_and(|t| {
7084 matches!(
7085 t.as_ref(),
7086 Expr::If {
7087 let_pattern: Some(_),
7088 ..
7089 }
7090 )
7091 });
7092 assert!(has_if_let, "expected if-let expression with let_pattern");
7093 }
7094
7095 fn parse_type_str(ty: &str) -> (TypeExpr, DiagnosticBag) {
7099 let src = format!("fn f(x: {ty}) {{}}\n");
7100 let (m, diags) = parse(&src);
7101 let Item::Fn(f) = &m.items[0] else {
7102 panic!("expected fn")
7103 };
7104 let ty = f.params[0].ty.clone().expect("param should have type");
7105 (ty, diags)
7106 }
7107
7108 #[test]
7109 fn type_named_simple() {
7110 let (ty, diags) = parse_type_str("Int");
7111 assert!(!diags.has_errors(), "{diags:?}");
7112 let TypeExpr::Named { path, args, .. } = ty else {
7113 panic!("expected Named")
7114 };
7115 assert_eq!(path.segments[0].name, "Int");
7116 assert!(args.is_empty());
7117 }
7118
7119 #[test]
7120 fn type_named_module_path() {
7121 let (ty, diags) = parse_type_str("app.models.User");
7122 assert!(!diags.has_errors(), "{diags:?}");
7123 let TypeExpr::Named { path, .. } = ty else {
7124 panic!("expected Named")
7125 };
7126 let names: Vec<&str> = path.segments.iter().map(|s| s.name.as_str()).collect();
7127 assert_eq!(names, ["app", "models", "User"]);
7128 }
7129
7130 #[test]
7131 fn type_generic_single() {
7132 let (ty, diags) = parse_type_str("List[Int]");
7133 assert!(!diags.has_errors(), "{diags:?}");
7134 let TypeExpr::Named { path, args, .. } = ty else {
7135 panic!("expected Named")
7136 };
7137 assert_eq!(path.segments[0].name, "List");
7138 assert_eq!(args.len(), 1);
7139 assert!(matches!(&args[0], TypeExpr::Named { path, .. } if path.segments[0].name == "Int"));
7140 }
7141
7142 #[test]
7143 fn type_generic_two_params() {
7144 let (ty, diags) = parse_type_str("Map[String, Int]");
7145 assert!(!diags.has_errors(), "{diags:?}");
7146 let TypeExpr::Named { path, args, .. } = ty else {
7147 panic!("expected Named")
7148 };
7149 assert_eq!(path.segments[0].name, "Map");
7150 assert_eq!(args.len(), 2);
7151 }
7152
7153 #[test]
7154 fn type_generic_nested() {
7155 let (ty, diags) = parse_type_str("Map[String, List[User]]");
7156 assert!(!diags.has_errors(), "{diags:?}");
7157 let TypeExpr::Named { args, .. } = ty else {
7158 panic!("expected Named")
7159 };
7160 assert_eq!(args.len(), 2);
7161 let TypeExpr::Named {
7162 path: inner_path,
7163 args: inner_args,
7164 ..
7165 } = &args[1]
7166 else {
7167 panic!("expected inner Named")
7168 };
7169 assert_eq!(inner_path.segments[0].name, "List");
7170 assert_eq!(inner_args.len(), 1);
7171 }
7172
7173 #[test]
7174 fn type_tuple_unit() {
7175 let (ty, diags) = parse_type_str("()");
7176 assert!(!diags.has_errors(), "{diags:?}");
7177 let TypeExpr::Tuple { elems, .. } = ty else {
7178 panic!("expected Tuple")
7179 };
7180 assert!(elems.is_empty());
7181 }
7182
7183 #[test]
7184 fn type_tuple_two_elems() {
7185 let (ty, diags) = parse_type_str("(Int, String)");
7186 assert!(!diags.has_errors(), "{diags:?}");
7187 let TypeExpr::Tuple { elems, .. } = ty else {
7188 panic!("expected Tuple")
7189 };
7190 assert_eq!(elems.len(), 2);
7191 }
7192
7193 #[test]
7194 fn type_tuple_three_elems() {
7195 let (ty, diags) = parse_type_str("(Int, String, Bool)");
7196 assert!(!diags.has_errors(), "{diags:?}");
7197 let TypeExpr::Tuple { elems, .. } = ty else {
7198 panic!("expected Tuple")
7199 };
7200 assert_eq!(elems.len(), 3);
7201 }
7202
7203 #[test]
7204 fn type_fn_no_params() {
7205 let (ty, diags) = parse_type_str("Fn() -> Void");
7206 assert!(!diags.has_errors(), "{diags:?}");
7207 let TypeExpr::Function {
7208 params,
7209 ret,
7210 effects,
7211 ..
7212 } = ty
7213 else {
7214 panic!("expected Function")
7215 };
7216 assert!(params.is_empty());
7217 assert!(effects.is_empty());
7218 assert!(
7219 matches!(ret.as_ref(), TypeExpr::Named { path, .. } if path.segments[0].name == "Void")
7220 );
7221 }
7222
7223 #[test]
7224 fn type_fn_with_params_and_return() {
7225 let (ty, diags) = parse_type_str("Fn(Int, Int) -> Int");
7226 assert!(!diags.has_errors(), "{diags:?}");
7227 let TypeExpr::Function {
7228 params,
7229 ret,
7230 effects,
7231 ..
7232 } = ty
7233 else {
7234 panic!("expected Function")
7235 };
7236 assert_eq!(params.len(), 2);
7237 assert!(effects.is_empty());
7238 assert!(
7239 matches!(ret.as_ref(), TypeExpr::Named { path, .. } if path.segments[0].name == "Int")
7240 );
7241 }
7242
7243 #[test]
7244 fn type_fn_with_effect_clause() {
7245 let (ty, diags) = parse_type_str("Fn(String) -> Void with Log");
7246 assert!(!diags.has_errors(), "{diags:?}");
7247 let TypeExpr::Function {
7248 params, effects, ..
7249 } = ty
7250 else {
7251 panic!("expected Function")
7252 };
7253 assert_eq!(params.len(), 1);
7254 assert_eq!(effects.len(), 1);
7255 assert_eq!(effects[0].segments[0].name, "Log");
7256 }
7257
7258 #[test]
7259 fn type_fn_with_multiple_effects() {
7260 let (ty, diags) = parse_type_str("Fn() -> Void with Log, Io");
7261 assert!(!diags.has_errors(), "{diags:?}");
7262 let TypeExpr::Function { effects, .. } = ty else {
7263 panic!("expected Function")
7264 };
7265 assert_eq!(effects.len(), 2);
7266 assert_eq!(effects[0].segments[0].name, "Log");
7267 assert_eq!(effects[1].segments[0].name, "Io");
7268 }
7269
7270 #[test]
7271 fn type_optional_shorthand() {
7272 let (ty, diags) = parse_type_str("User?");
7273 assert!(!diags.has_errors(), "{diags:?}");
7274 let TypeExpr::Optional { inner, .. } = ty else {
7275 panic!("expected Optional")
7276 };
7277 let TypeExpr::Named { path, .. } = inner.as_ref() else {
7278 panic!("expected Named inner")
7279 };
7280 assert_eq!(path.segments[0].name, "User");
7281 }
7282
7283 #[test]
7284 fn type_optional_generic() {
7285 let (ty, diags) = parse_type_str("List[Int]?");
7286 assert!(!diags.has_errors(), "{diags:?}");
7287 let TypeExpr::Optional { inner, .. } = ty else {
7288 panic!("expected Optional")
7289 };
7290 assert!(matches!(inner.as_ref(), TypeExpr::Named { .. }));
7291 }
7292
7293 #[test]
7294 fn type_self_in_impl() {
7295 let src = "record Wrap { inner: Self }\n";
7297 let (m, diags) = parse(src);
7298 assert!(!diags.has_errors(), "{diags:?}");
7299 let Item::Record(r) = &m.items[0] else {
7300 panic!()
7301 };
7302 assert!(matches!(r.fields[0].ty, TypeExpr::SelfType { .. }));
7303 }
7304
7305 #[test]
7306 fn type_deeply_nested_generics() {
7307 let (ty, diags) = parse_type_str("Map[String, List[Result[User, String]]]");
7309 assert!(!diags.has_errors(), "{diags:?}");
7310 let TypeExpr::Named { path, args, .. } = ty else {
7311 panic!("expected Named")
7312 };
7313 assert_eq!(path.segments[0].name, "Map");
7314 assert_eq!(args.len(), 2);
7315 let TypeExpr::Named {
7316 path: list_path,
7317 args: list_args,
7318 ..
7319 } = &args[1]
7320 else {
7321 panic!("expected List")
7322 };
7323 assert_eq!(list_path.segments[0].name, "List");
7324 assert_eq!(list_args.len(), 1);
7325 let TypeExpr::Named {
7326 path: result_path,
7327 args: result_args,
7328 ..
7329 } = &list_args[0]
7330 else {
7331 panic!("expected Result")
7332 };
7333 assert_eq!(result_path.segments[0].name, "Result");
7334 assert_eq!(result_args.len(), 2);
7335 }
7336
7337 #[test]
7340 fn effect_empty_body() {
7341 let src = "effect Log {\n}\n";
7342 let (m, diags) = parse(src);
7343 assert!(!diags.has_errors(), "{diags:?}");
7344 assert_eq!(m.items.len(), 1);
7345 let Item::Effect(e) = &m.items[0] else {
7346 panic!("expected Effect")
7347 };
7348 assert_eq!(e.name.name, "Log");
7349 assert!(e.operations.is_empty());
7350 }
7351
7352 #[test]
7353 fn effect_with_operations() {
7354 let src = "effect Log {\n fn log(level: Level, message: String) -> Void\n}\n";
7355 let (m, diags) = parse(src);
7356 assert!(!diags.has_errors(), "{diags:?}");
7357 let Item::Effect(e) = &m.items[0] else {
7358 panic!("expected Effect")
7359 };
7360 assert_eq!(e.name.name, "Log");
7361 assert_eq!(e.operations.len(), 1);
7362 assert_eq!(e.operations[0].name.name, "log");
7363 assert_eq!(e.operations[0].params.len(), 2);
7364 }
7365
7366 #[test]
7367 fn effect_multiple_operations() {
7368 let src = "effect Storage {\n fn read(key: String) -> Option[Bytes]\n fn write(key: String, val: Bytes) -> Void\n}\n";
7369 let (m, diags) = parse(src);
7370 assert!(!diags.has_errors(), "{diags:?}");
7371 let Item::Effect(e) = &m.items[0] else {
7372 panic!("expected Effect")
7373 };
7374 assert_eq!(e.operations.len(), 2);
7375 assert_eq!(e.operations[0].name.name, "read");
7376 assert_eq!(e.operations[1].name.name, "write");
7377 }
7378
7379 #[test]
7380 fn effect_composite() {
7381 let src = "effect Observable = Log + Trace + Metrics\n";
7382 let (m, diags) = parse(src);
7383 assert!(!diags.has_errors(), "{diags:?}");
7384 assert_eq!(m.items.len(), 1);
7385 let Item::Effect(e) = &m.items[0] else {
7386 panic!("expected Effect")
7387 };
7388 assert_eq!(e.name.name, "Observable");
7389 assert!(
7390 e.operations.is_empty(),
7391 "composite effects have no operations"
7392 );
7393 }
7394
7395 #[test]
7396 fn effect_with_visibility() {
7397 let src = "public effect Log {\n fn log(msg: String) -> Void\n}\n";
7398 let (m, diags) = parse(src);
7399 assert!(!diags.has_errors(), "{diags:?}");
7400 let Item::Effect(e) = &m.items[0] else {
7401 panic!("expected Effect")
7402 };
7403 assert_eq!(e.visibility, Visibility::Public);
7404 }
7405
7406 #[test]
7409 fn annotation_no_args() {
7410 let src = "@deprecated\nfn old() {}\n";
7411 let (m, diags) = parse(src);
7412 assert!(!diags.has_errors(), "{diags:?}");
7413 let Item::Fn(f) = &m.items[0] else { panic!() };
7414 assert_eq!(f.annotations.len(), 1);
7415 assert_eq!(f.annotations[0].name.name, "deprecated");
7416 assert!(f.annotations[0].args.is_empty());
7417 }
7418
7419 #[test]
7420 fn annotation_positional_arg() {
7421 let src = "@domain(\"e-commerce\")\nfn process() {}\n";
7422 let (m, diags) = parse(src);
7423 assert!(!diags.has_errors(), "{diags:?}");
7424 let Item::Fn(f) = &m.items[0] else { panic!() };
7425 assert_eq!(f.annotations[0].name.name, "domain");
7426 assert_eq!(f.annotations[0].args.len(), 1);
7427 }
7428
7429 #[test]
7430 fn annotation_named_arg() {
7431 let src = "@performance(max_latency: 100)\nfn fast() {}\n";
7432 let (m, diags) = parse(src);
7433 assert!(!diags.has_errors(), "{diags:?}");
7434 let Item::Fn(f) = &m.items[0] else { panic!() };
7435 assert_eq!(f.annotations[0].name.name, "performance");
7436 assert_eq!(f.annotations[0].args.len(), 1);
7437 }
7438
7439 #[test]
7440 fn annotation_multiline_string() {
7441 let src = "@context(\"\"\"\n Payment module.\n\"\"\")\nfn pay() {}\n";
7442 let (m, diags) = parse(src);
7443 assert!(!diags.has_errors(), "{diags:?}");
7444 let Item::Fn(f) = &m.items[0] else { panic!() };
7445 assert_eq!(f.annotations[0].name.name, "context");
7446 assert_eq!(f.annotations[0].args.len(), 1);
7447 }
7448
7449 #[test]
7450 fn multiple_annotations_stack() {
7451 let src = "@deprecated\n@domain(\"old\")\nfn legacy() {}\n";
7452 let (m, diags) = parse(src);
7453 assert!(!diags.has_errors(), "{diags:?}");
7454 let Item::Fn(f) = &m.items[0] else { panic!() };
7455 assert_eq!(f.annotations.len(), 2);
7456 assert_eq!(f.annotations[0].name.name, "deprecated");
7457 assert_eq!(f.annotations[1].name.name, "domain");
7458 }
7459
7460 #[test]
7463 fn module_handle_decl() {
7464 let src = "handle Log with ConsoleLog\n";
7465 let (m, diags) = parse(src);
7466 assert!(!diags.has_errors(), "{diags:?}");
7467 assert_eq!(m.items.len(), 1);
7468 let Item::ModuleHandle(h) = &m.items[0] else {
7469 panic!("expected ModuleHandle")
7470 };
7471 assert_eq!(h.effect.segments[0].name, "Log");
7472 }
7473
7474 #[test]
7475 fn module_handle_decl_qualified_effect() {
7476 let src = "handle Std.Io with FileIo\n";
7477 let (m, diags) = parse(src);
7478 assert!(!diags.has_errors(), "{diags:?}");
7479 let Item::ModuleHandle(h) = &m.items[0] else {
7480 panic!("expected ModuleHandle")
7481 };
7482 assert_eq!(h.effect.segments.len(), 2);
7483 assert_eq!(h.effect.segments[0].name, "Std");
7484 assert_eq!(h.effect.segments[1].name, "Io");
7485 }
7486
7487 #[test]
7490 fn fn_with_effect_clause_integration() {
7491 let src = "fn process(data: Data) -> Result\n with Log, Clock\n{\n data\n}\n";
7492 let (m, diags) = parse(src);
7493 assert!(!diags.has_errors(), "{diags:?}");
7494 let Item::Fn(f) = &m.items[0] else { panic!() };
7495 assert_eq!(f.effect_clause.len(), 2);
7496 assert_eq!(f.effect_clause[0].segments[0].name, "Log");
7497 assert_eq!(f.effect_clause[1].segments[0].name, "Clock");
7498 }
7499
7500 #[test]
7504 fn disambig_brace_after_type_ident_is_record_construct() {
7505 let src = "fn f() { Point { x: 1, y: 2 } }\n";
7506 let (m, diags) = parse(src);
7507 assert!(!diags.has_errors(), "{diags:?}");
7508 let Item::Fn(f) = &m.items[0] else { panic!() };
7509 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7510 assert!(
7511 matches!(tail, Expr::RecordConstruct { .. }),
7512 "expected RecordConstruct, got {tail:?}"
7513 );
7514 }
7515
7516 #[test]
7518 fn disambig_brace_with_colon_is_map() {
7519 let src = "fn f() { { \"key\": 42 } }\n";
7520 let (m, diags) = parse(src);
7521 assert!(!diags.has_errors(), "{diags:?}");
7522 let Item::Fn(f) = &m.items[0] else { panic!() };
7523 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7524 assert!(
7525 matches!(tail, Expr::MapLiteral { .. }),
7526 "expected MapLiteral, got {tail:?}"
7527 );
7528 }
7529
7530 #[test]
7532 fn disambig_brace_without_colon_is_block() {
7533 let src = "fn f() { { 42 } }\n";
7534 let (m, diags) = parse(src);
7535 assert!(!diags.has_errors(), "{diags:?}");
7536 let Item::Fn(f) = &m.items[0] else { panic!() };
7537 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7538 assert!(
7539 matches!(tail, Expr::Block { .. }),
7540 "expected Block, got {tail:?}"
7541 );
7542 }
7543
7544 #[test]
7546 fn disambig_single_paren_is_grouping() {
7547 let src = "fn f() { (42) }\n";
7548 let (m, diags) = parse(src);
7549 assert!(!diags.has_errors(), "{diags:?}");
7550 let Item::Fn(f) = &m.items[0] else { panic!() };
7551 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7552 assert!(
7554 !matches!(tail, Expr::TupleLiteral { .. }),
7555 "should not be TupleLiteral"
7556 );
7557 }
7558
7559 #[test]
7561 fn disambig_multi_paren_is_tuple() {
7562 let src = "fn f() { (1, 2) }\n";
7563 let (m, diags) = parse(src);
7564 assert!(!diags.has_errors(), "{diags:?}");
7565 let Item::Fn(f) = &m.items[0] else { panic!() };
7566 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7567 assert!(matches!(tail, Expr::TupleLiteral { elems, .. } if elems.len() == 2));
7568 }
7569
7570 #[test]
7572 fn disambig_trailing_comma_is_single_elem_tuple() {
7573 let src = "fn f() { (1,) }\n";
7574 let (m, diags) = parse(src);
7575 assert!(!diags.has_errors(), "{diags:?}");
7576 let Item::Fn(f) = &m.items[0] else { panic!() };
7577 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7578 assert!(matches!(tail, Expr::TupleLiteral { elems, .. } if elems.len() == 1));
7579 }
7580
7581 #[test]
7583 fn disambig_map_with_ident_key() {
7584 let src = "fn f() { { name: \"Alice\" } }\n";
7585 let (m, diags) = parse(src);
7586 assert!(!diags.has_errors(), "{diags:?}");
7587 let Item::Fn(f) = &m.items[0] else { panic!() };
7588 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7589 assert!(matches!(tail, Expr::MapLiteral { entries, .. } if entries.len() == 1));
7590 }
7591
7592 #[test]
7594 fn disambig_empty_braces_is_block() {
7595 let src = "fn f() { {} }\n";
7596 let (m, diags) = parse(src);
7597 assert!(!diags.has_errors(), "{diags:?}");
7598 let Item::Fn(f) = &m.items[0] else { panic!() };
7599 let tail = f.body.as_ref().unwrap().tail.as_deref().expect("tail expr");
7600 assert!(
7601 matches!(tail, Expr::Block { .. }),
7602 "expected Block, got {tail:?}"
7603 );
7604 }
7605
7606 #[test]
7611 fn recovery_unexpected_token_at_top_level() {
7612 let src = "fn before() {}\n???\nfn after() {}\n";
7614 let (m, diags) = parse(src);
7615 assert!(diags.has_errors(), "should have errors");
7616 let fns: Vec<_> = m
7618 .items
7619 .iter()
7620 .filter(|i| matches!(i, Item::Fn(_)))
7621 .collect();
7622 assert_eq!(fns.len(), 2, "both fns should be in the AST");
7623 assert!(m.items.iter().any(|i| matches!(i, Item::Error { .. })));
7625 }
7626
7627 #[test]
7629 fn recovery_multiple_errors_reported() {
7630 let src = "???\nfn mid() {}\n!!!\nfn ok() {}\n";
7632 let (m, diags) = parse(src);
7633 assert!(diags.has_errors());
7634 assert!(
7635 diags.error_count() >= 2,
7636 "should report multiple errors, got: {diags:?}"
7637 );
7638 let fns: Vec<_> = m
7640 .items
7641 .iter()
7642 .filter(|i| matches!(i, Item::Fn(_)))
7643 .collect();
7644 assert_eq!(fns.len(), 2);
7645 }
7646
7647 #[test]
7649 fn recovery_after_malformed_fn_body() {
7650 let src = "fn bad( {}\nfn good() {}\n";
7652 let (m, diags) = parse(src);
7653 assert!(diags.has_errors());
7654 let has_good = m.items.iter().any(|i| {
7656 if let Item::Fn(f) = i {
7657 f.name.name == "good"
7658 } else {
7659 false
7660 }
7661 });
7662 assert!(
7663 has_good,
7664 "fn good should be recovered; items: {:#?}",
7665 m.items
7666 );
7667 }
7668
7669 #[test]
7672 fn integration_complete_source_file() {
7673 let src = "\
7674module app.core\n\
7675use std.io.*\n\
7676use std.collections.{List, Map}\n\
7677\n\
7678public record User {\n\
7679 name: String\n\
7680 age: Int\n\
7681}\n\
7682\n\
7683public enum Color {\n\
7684 Red\n\
7685 Green\n\
7686 Blue\n\
7687}\n\
7688\n\
7689public fn greet(user: User) -> String {\n\
7690 \"Hello\"\n\
7691}\n\
7692\n\
7693public fn add(x: Int, y: Int) -> Int {\n\
7694 x + y\n\
7695}\n\
7696";
7697 let (m, diags) = parse(src);
7698 assert!(!diags.has_errors(), "errors: {diags:?}");
7699
7700 let path = m.path.as_ref().expect("module path");
7702 assert_eq!(path.segments[0].name, "app");
7703 assert_eq!(path.segments[1].name, "core");
7704
7705 assert_eq!(m.imports.len(), 2);
7707
7708 assert_eq!(m.items.len(), 4);
7710 assert!(matches!(m.items[0], Item::Record(_)));
7711 assert!(matches!(m.items[1], Item::Enum(_)));
7712 assert!(matches!(m.items[2], Item::Fn(_)));
7713 assert!(matches!(m.items[3], Item::Fn(_)));
7714
7715 let Item::Record(r) = &m.items[0] else {
7716 panic!()
7717 };
7718 assert_eq!(r.name.name, "User");
7719 assert_eq!(r.fields.len(), 2);
7720
7721 let Item::Enum(e) = &m.items[1] else { panic!() };
7722 assert_eq!(e.name.name, "Color");
7723 assert_eq!(e.variants.len(), 3);
7724 }
7725
7726 #[test]
7729 fn parse_type_alias_simple() {
7730 let (m, diags) = parse("type Email = String\n");
7731 assert!(!diags.has_errors(), "errors: {diags:?}");
7732 assert_eq!(m.items.len(), 1);
7733 let Item::TypeAlias(ta) = &m.items[0] else {
7734 panic!("expected TypeAlias")
7735 };
7736 assert_eq!(ta.name.name, "Email");
7737 assert!(ta.generic_params.is_empty());
7738 assert!(ta.where_clause.is_empty());
7739 assert_eq!(ta.visibility, Visibility::Private);
7740 }
7741
7742 #[test]
7743 fn parse_type_alias_generic() {
7744 let (m, diags) = parse("type NonEmpty[T] = List[T]\n");
7745 assert!(!diags.has_errors(), "errors: {diags:?}");
7746 assert_eq!(m.items.len(), 1);
7747 let Item::TypeAlias(ta) = &m.items[0] else {
7748 panic!("expected TypeAlias")
7749 };
7750 assert_eq!(ta.name.name, "NonEmpty");
7751 assert_eq!(ta.generic_params.len(), 1);
7752 assert_eq!(ta.generic_params[0].name.name, "T");
7753 }
7754
7755 #[test]
7756 fn parse_type_alias_plain() {
7757 let (m, diags) = parse("type Port = Int\n");
7758 assert!(!diags.has_errors(), "errors: {diags:?}");
7759 assert_eq!(m.items.len(), 1);
7760 let Item::TypeAlias(ta) = &m.items[0] else {
7761 panic!("expected TypeAlias")
7762 };
7763 assert_eq!(ta.name.name, "Port");
7764 assert!(ta.generic_params.is_empty());
7765 }
7766
7767 #[test]
7768 fn parse_type_alias_with_where_clause() {
7769 let (m, diags) = parse("type Sortable[T] = List[T] where (T: Comparable)\n");
7770 assert!(!diags.has_errors(), "errors: {diags:?}");
7771 assert_eq!(m.items.len(), 1);
7772 let Item::TypeAlias(ta) = &m.items[0] else {
7773 panic!("expected TypeAlias")
7774 };
7775 assert_eq!(ta.name.name, "Sortable");
7776 assert_eq!(ta.generic_params.len(), 1);
7777 assert_eq!(ta.where_clause.len(), 1);
7778 assert_eq!(ta.where_clause[0].param.name, "T");
7779 }
7780
7781 #[test]
7784 fn parse_const_int() {
7785 let (m, diags) = parse("const MAX_SIZE: Int = 1024\n");
7786 assert!(!diags.has_errors(), "errors: {diags:?}");
7787 assert_eq!(m.items.len(), 1);
7788 let Item::Const(cd) = &m.items[0] else {
7789 panic!("expected Const")
7790 };
7791 assert_eq!(cd.name.name, "MAX_SIZE");
7792 assert_eq!(cd.visibility, Visibility::Private);
7793 }
7794
7795 #[test]
7796 fn parse_const_float() {
7797 let (m, diags) = parse("const PI: Float = 3.14159\n");
7798 assert!(!diags.has_errors(), "errors: {diags:?}");
7799 assert_eq!(m.items.len(), 1);
7800 let Item::Const(cd) = &m.items[0] else {
7801 panic!("expected Const")
7802 };
7803 assert_eq!(cd.name.name, "PI");
7804 }
7805
7806 #[test]
7807 fn parse_const_with_visibility() {
7808 let (m, diags) = parse("public const VERSION: String = \"1.0.0\"\n");
7809 assert!(!diags.has_errors(), "errors: {diags:?}");
7810 assert_eq!(m.items.len(), 1);
7811 let Item::Const(cd) = &m.items[0] else {
7812 panic!("expected Const")
7813 };
7814 assert_eq!(cd.name.name, "VERSION");
7815 assert_eq!(cd.visibility, Visibility::Public);
7816 }
7817
7818 #[test]
7819 fn parse_type_alias_with_visibility() {
7820 let (m, diags) = parse("public type UserId = Int\n");
7821 assert!(!diags.has_errors(), "errors: {diags:?}");
7822 assert_eq!(m.items.len(), 1);
7823 let Item::TypeAlias(ta) = &m.items[0] else {
7824 panic!("expected TypeAlias")
7825 };
7826 assert_eq!(ta.name.name, "UserId");
7827 assert_eq!(ta.visibility, Visibility::Public);
7828 }
7829
7830 #[test]
7833 fn import_visibility_stored() {
7834 let (m, diags) = parse("public use app.models.User\n");
7835 assert!(!diags.has_errors(), "errors: {diags:?}");
7836 assert_eq!(m.imports.len(), 1);
7837 assert_eq!(m.imports[0].visibility, Visibility::Public);
7838 }
7839
7840 #[test]
7841 fn import_private_visibility_default() {
7842 let (m, diags) = parse("use app.models.User\n");
7843 assert!(!diags.has_errors(), "errors: {diags:?}");
7844 assert_eq!(m.imports.len(), 1);
7845 assert_eq!(m.imports[0].visibility, Visibility::Private);
7846 }
7847
7848 #[test]
7849 fn composite_effect_components_stored() {
7850 let (m, diags) = parse("effect IO = Log + Clock + Storage\n");
7851 assert!(!diags.has_errors(), "errors: {diags:?}");
7852 assert_eq!(m.items.len(), 1);
7853 let Item::Effect(eff) = &m.items[0] else {
7854 panic!("expected Effect")
7855 };
7856 assert_eq!(eff.name.name, "IO");
7857 let component_names: Vec<&str> = eff
7858 .components
7859 .iter()
7860 .map(|c| c.segments[0].name.as_str())
7861 .collect();
7862 assert_eq!(component_names, ["Log", "Clock", "Storage"]);
7863 }
7864
7865 #[test]
7866 fn trait_supertraits_stored() {
7867 let (m, diags) = parse("trait Ordered: Comparable, Equatable {\n}\n");
7868 assert!(!diags.has_errors(), "errors: {diags:?}");
7869 assert_eq!(m.items.len(), 1);
7870 let Item::Trait(tr) = &m.items[0] else {
7871 panic!("expected Trait")
7872 };
7873 assert_eq!(tr.name.name, "Ordered");
7874 let supertrait_names: Vec<&str> = tr
7875 .supertraits
7876 .iter()
7877 .map(|s| s.segments[0].name.as_str())
7878 .collect();
7879 assert_eq!(supertrait_names, ["Comparable", "Equatable"]);
7880 }
7881
7882 #[test]
7883 fn import_alias_in_list() {
7884 let (m, diags) = parse("use json.{Value as JsonValue}\n");
7885 assert!(!diags.has_errors(), "errors: {diags:?}");
7886 assert_eq!(m.imports.len(), 1);
7887 match &m.imports[0].items {
7888 ImportItems::Named(names) => {
7889 assert_eq!(names.len(), 1);
7890 assert_eq!(names[0].name.name, "Value");
7891 assert_eq!(names[0].alias.as_ref().unwrap().name, "JsonValue");
7892 }
7893 other => panic!("expected Named import, got {other:?}"),
7894 }
7895 }
7896
7897 #[test]
7898 fn annotation_named_args_preserve_labels() {
7899 let (m, diags) = parse("@performance(max_latency: 100, max_memory: 50)\nfn fast() {}\n");
7900 assert!(!diags.has_errors(), "errors: {diags:?}");
7901 assert_eq!(m.items.len(), 1);
7902 let Item::Fn(f) = &m.items[0] else {
7903 panic!("expected Fn")
7904 };
7905 assert_eq!(f.annotations.len(), 1);
7906 let ann = &f.annotations[0];
7907 assert_eq!(ann.name.name, "performance");
7908 assert_eq!(ann.args.len(), 2);
7909 assert_eq!(ann.args[0].label.as_ref().unwrap().name, "max_latency");
7910 assert_eq!(ann.args[1].label.as_ref().unwrap().name, "max_memory");
7911 }
7912
7913 #[test]
7914 fn trait_required_method_body_is_none() {
7915 let (m, diags) = parse("trait Foo {\n fn bar(self) -> Int\n}\n");
7916 assert!(!diags.has_errors(), "errors: {diags:?}");
7917 assert_eq!(m.items.len(), 1);
7918 let Item::Trait(tr) = &m.items[0] else {
7919 panic!("expected Trait")
7920 };
7921 assert_eq!(tr.methods.len(), 1);
7922 assert_eq!(tr.methods[0].name.name, "bar");
7923 assert!(
7924 tr.methods[0].body.is_none(),
7925 "required method should have body: None"
7926 );
7927 }
7928
7929 #[test]
7934 fn comparison_chained_eq_is_error() {
7935 let (_, diags) = parse_expr_str("a == b == c");
7937 assert!(diags.has_errors(), "chained == must produce a parse error");
7938 }
7939
7940 #[test]
7941 fn comparison_chained_ne_is_error() {
7942 let (_, diags) = parse_expr_str("a != b != c");
7943 assert!(diags.has_errors(), "chained != must produce a parse error");
7944 }
7945
7946 #[test]
7947 fn comparison_chained_lt_gt_is_error() {
7948 let (_, diags) = parse_expr_str("a < b > c");
7949 assert!(diags.has_errors(), "chained < > must produce a parse error");
7950 }
7951
7952 #[test]
7953 fn comparison_chained_le_ge_is_error() {
7954 let (_, diags) = parse_expr_str("a <= b >= c");
7955 assert!(
7956 diags.has_errors(),
7957 "chained <= >= must produce a parse error"
7958 );
7959 }
7960
7961 #[test]
7962 fn comparison_single_eq_still_works() {
7963 let (e, diags) = parse_expr_str("a == b");
7964 assert!(!diags.has_errors(), "{diags:?}");
7965 assert!(matches!(e, Expr::Binary { op: BinOp::Eq, .. }));
7966 }
7967
7968 #[test]
7969 fn comparison_single_lt_still_works() {
7970 let (e, diags) = parse_expr_str("a < b");
7971 assert!(!diags.has_errors(), "{diags:?}");
7972 assert!(matches!(e, Expr::Binary { op: BinOp::Lt, .. }));
7973 }
7974
7975 #[test]
7979 fn prec_01_02_assignment_wraps_pipe() {
7980 let (e, diags) = parse_expr_str("a = b |> c");
7982 assert!(!diags.has_errors(), "{diags:?}");
7983 match &e {
7984 Expr::Assign { value, .. } => {
7985 assert!(
7986 matches!(value.as_ref(), Expr::Pipe { .. }),
7987 "assignment RHS should be Pipe, got {value:?}"
7988 );
7989 }
7990 _ => panic!("expected Assign, got {e:?}"),
7991 }
7992 }
7993
7994 #[test]
7995 fn prec_02_03_pipe_wraps_compose() {
7996 let (e, diags) = parse_expr_str("a |> b >> c");
7998 assert!(!diags.has_errors(), "{diags:?}");
7999 match &e {
8000 Expr::Pipe { right, .. } => {
8001 assert!(
8002 matches!(right.as_ref(), Expr::Compose { .. }),
8003 "pipe RHS should be Compose, got {right:?}"
8004 );
8005 }
8006 _ => panic!("expected Pipe, got {e:?}"),
8007 }
8008 }
8009
8010 #[test]
8011 fn prec_03_04_compose_wraps_range() {
8012 let (e, diags) = parse_expr_str("a >> b .. c");
8014 assert!(!diags.has_errors(), "{diags:?}");
8015 match &e {
8016 Expr::Compose { right, .. } => {
8017 assert!(
8018 matches!(right.as_ref(), Expr::Range { .. }),
8019 "compose RHS should be Range, got {right:?}"
8020 );
8021 }
8022 _ => panic!("expected Compose, got {e:?}"),
8023 }
8024 }
8025
8026 #[test]
8027 fn prec_04_05_range_wraps_logical_or() {
8028 let (e, diags) = parse_expr_str("a .. b || c");
8030 assert!(!diags.has_errors(), "{diags:?}");
8031 match &e {
8032 Expr::Range { hi, .. } => {
8033 assert!(
8034 matches!(hi.as_ref(), Expr::Binary { op: BinOp::Or, .. }),
8035 "range hi should be Or, got {hi:?}"
8036 );
8037 }
8038 _ => panic!("expected Range, got {e:?}"),
8039 }
8040 }
8041
8042 #[test]
8043 fn prec_05_06_or_wraps_and() {
8044 let (e, diags) = parse_expr_str("a || b && c");
8046 assert!(!diags.has_errors(), "{diags:?}");
8047 match &e {
8048 Expr::Binary {
8049 op: BinOp::Or,
8050 right,
8051 ..
8052 } => {
8053 assert!(
8054 matches!(right.as_ref(), Expr::Binary { op: BinOp::And, .. }),
8055 "Or RHS should be And, got {right:?}"
8056 );
8057 }
8058 _ => panic!("expected Or, got {e:?}"),
8059 }
8060 }
8061
8062 #[test]
8063 fn prec_06_07_and_wraps_comparison() {
8064 let (e, diags) = parse_expr_str("a && b == c");
8066 assert!(!diags.has_errors(), "{diags:?}");
8067 match &e {
8068 Expr::Binary {
8069 op: BinOp::And,
8070 right,
8071 ..
8072 } => {
8073 assert!(
8074 matches!(right.as_ref(), Expr::Binary { op: BinOp::Eq, .. }),
8075 "And RHS should be Eq, got {right:?}"
8076 );
8077 }
8078 _ => panic!("expected And, got {e:?}"),
8079 }
8080 }
8081
8082 #[test]
8083 fn prec_07_08_comparison_wraps_bitor() {
8084 let (e, diags) = parse_expr_str("a == b | c");
8086 assert!(!diags.has_errors(), "{diags:?}");
8087 match &e {
8088 Expr::Binary {
8089 op: BinOp::Eq,
8090 right,
8091 ..
8092 } => {
8093 assert!(
8094 matches!(
8095 right.as_ref(),
8096 Expr::Binary {
8097 op: BinOp::BitOr,
8098 ..
8099 }
8100 ),
8101 "Eq RHS should be BitOr, got {right:?}"
8102 );
8103 }
8104 _ => panic!("expected Eq, got {e:?}"),
8105 }
8106 }
8107
8108 #[test]
8109 fn prec_08_09_bitor_wraps_bitxor() {
8110 let (e, diags) = parse_expr_str("a | b ^ c");
8112 assert!(!diags.has_errors(), "{diags:?}");
8113 match &e {
8114 Expr::Binary {
8115 op: BinOp::BitOr,
8116 right,
8117 ..
8118 } => {
8119 assert!(
8120 matches!(
8121 right.as_ref(),
8122 Expr::Binary {
8123 op: BinOp::BitXor,
8124 ..
8125 }
8126 ),
8127 "BitOr RHS should be BitXor, got {right:?}"
8128 );
8129 }
8130 _ => panic!("expected BitOr, got {e:?}"),
8131 }
8132 }
8133
8134 #[test]
8135 fn prec_09_10_bitxor_wraps_bitand() {
8136 let (e, diags) = parse_expr_str("a ^ b & c");
8138 assert!(!diags.has_errors(), "{diags:?}");
8139 match &e {
8140 Expr::Binary {
8141 op: BinOp::BitXor,
8142 right,
8143 ..
8144 } => {
8145 assert!(
8146 matches!(
8147 right.as_ref(),
8148 Expr::Binary {
8149 op: BinOp::BitAnd,
8150 ..
8151 }
8152 ),
8153 "BitXor RHS should be BitAnd, got {right:?}"
8154 );
8155 }
8156 _ => panic!("expected BitXor, got {e:?}"),
8157 }
8158 }
8159
8160 #[test]
8161 fn prec_10_11_bitand_wraps_add() {
8162 let (e, diags) = parse_expr_str("a & b + c");
8164 assert!(!diags.has_errors(), "{diags:?}");
8165 match &e {
8166 Expr::Binary {
8167 op: BinOp::BitAnd,
8168 right,
8169 ..
8170 } => {
8171 assert!(
8172 matches!(right.as_ref(), Expr::Binary { op: BinOp::Add, .. }),
8173 "BitAnd RHS should be Add, got {right:?}"
8174 );
8175 }
8176 _ => panic!("expected BitAnd, got {e:?}"),
8177 }
8178 }
8179
8180 #[test]
8181 fn prec_11_12_add_wraps_mul() {
8182 let (e, diags) = parse_expr_str("a + b * c");
8184 assert!(!diags.has_errors(), "{diags:?}");
8185 match &e {
8186 Expr::Binary {
8187 op: BinOp::Add,
8188 right,
8189 ..
8190 } => {
8191 assert!(
8192 matches!(right.as_ref(), Expr::Binary { op: BinOp::Mul, .. }),
8193 "Add RHS should be Mul, got {right:?}"
8194 );
8195 }
8196 _ => panic!("expected Add, got {e:?}"),
8197 }
8198 }
8199
8200 #[test]
8201 fn prec_12_13_mul_wraps_power() {
8202 let (e, diags) = parse_expr_str("a * b ** c");
8204 assert!(!diags.has_errors(), "{diags:?}");
8205 match &e {
8206 Expr::Binary {
8207 op: BinOp::Mul,
8208 right,
8209 ..
8210 } => {
8211 assert!(
8212 matches!(right.as_ref(), Expr::Binary { op: BinOp::Pow, .. }),
8213 "Mul RHS should be Pow, got {right:?}"
8214 );
8215 }
8216 _ => panic!("expected Mul, got {e:?}"),
8217 }
8218 }
8219
8220 #[test]
8221 fn prec_13_14_power_wraps_unary() {
8222 let (e, diags) = parse_expr_str("-a ** b");
8224 assert!(!diags.has_errors(), "{diags:?}");
8225 match &e {
8226 Expr::Binary {
8227 op: BinOp::Pow,
8228 left,
8229 ..
8230 } => {
8231 assert!(
8232 matches!(
8233 left.as_ref(),
8234 Expr::Unary {
8235 op: UnaryOp::Neg,
8236 ..
8237 }
8238 ),
8239 "Pow LHS should be Neg, got {left:?}"
8240 );
8241 }
8242 _ => panic!("expected Pow, got {e:?}"),
8243 }
8244 }
8245
8246 #[test]
8247 fn prec_14_15_unary_wraps_postfix() {
8248 let (e, diags) = parse_expr_str("!a.b");
8250 assert!(!diags.has_errors(), "{diags:?}");
8251 match &e {
8252 Expr::Unary {
8253 op: UnaryOp::Not,
8254 operand,
8255 ..
8256 } => {
8257 assert!(
8258 matches!(operand.as_ref(), Expr::FieldAccess { .. }),
8259 "Not operand should be FieldAccess, got {operand:?}"
8260 );
8261 }
8262 _ => panic!("expected Unary(Not), got {e:?}"),
8263 }
8264 }
8265
8266 #[test]
8269 fn assoc_01_assignment_right() {
8270 let (e, diags) = parse_expr_str("a = b = c");
8272 assert!(!diags.has_errors(), "{diags:?}");
8273 match &e {
8274 Expr::Assign { value, .. } => {
8275 assert!(
8276 matches!(value.as_ref(), Expr::Assign { .. }),
8277 "assignment should be right-assoc, got {value:?}"
8278 );
8279 }
8280 _ => panic!("expected Assign, got {e:?}"),
8281 }
8282 }
8283
8284 #[test]
8285 fn assoc_02_pipe_left() {
8286 let (e, diags) = parse_expr_str("a |> b |> c");
8288 assert!(!diags.has_errors(), "{diags:?}");
8289 match &e {
8290 Expr::Pipe { left, .. } => {
8291 assert!(
8292 matches!(left.as_ref(), Expr::Pipe { .. }),
8293 "pipe should be left-assoc, got {left:?}"
8294 );
8295 }
8296 _ => panic!("expected Pipe, got {e:?}"),
8297 }
8298 }
8299
8300 #[test]
8301 fn assoc_03_compose_left() {
8302 let (e, diags) = parse_expr_str("a >> b >> c");
8304 assert!(!diags.has_errors(), "{diags:?}");
8305 match &e {
8306 Expr::Compose { left, .. } => {
8307 assert!(
8308 matches!(left.as_ref(), Expr::Compose { .. }),
8309 "compose should be left-assoc, got {left:?}"
8310 );
8311 }
8312 _ => panic!("expected Compose, got {e:?}"),
8313 }
8314 }
8315
8316 #[test]
8317 fn assoc_04_range_non_assoc() {
8318 let (_, diags) = parse_expr_str("a .. b .. c");
8320 assert!(diags.has_errors(), "chained .. must produce a parse error");
8321 }
8322
8323 #[test]
8324 fn assoc_05_or_left() {
8325 let (e, diags) = parse_expr_str("a || b || c");
8327 assert!(!diags.has_errors(), "{diags:?}");
8328 match &e {
8329 Expr::Binary {
8330 op: BinOp::Or,
8331 left,
8332 ..
8333 } => {
8334 assert!(
8335 matches!(left.as_ref(), Expr::Binary { op: BinOp::Or, .. }),
8336 "|| should be left-assoc, got {left:?}"
8337 );
8338 }
8339 _ => panic!("expected Or, got {e:?}"),
8340 }
8341 }
8342
8343 #[test]
8344 fn assoc_06_and_left() {
8345 let (e, diags) = parse_expr_str("a && b && c");
8347 assert!(!diags.has_errors(), "{diags:?}");
8348 match &e {
8349 Expr::Binary {
8350 op: BinOp::And,
8351 left,
8352 ..
8353 } => {
8354 assert!(
8355 matches!(left.as_ref(), Expr::Binary { op: BinOp::And, .. }),
8356 "&& should be left-assoc, got {left:?}"
8357 );
8358 }
8359 _ => panic!("expected And, got {e:?}"),
8360 }
8361 }
8362
8363 #[test]
8364 fn assoc_07_comparison_non_assoc() {
8365 let (_, diags) = parse_expr_str("a == b == c");
8367 assert!(diags.has_errors(), "chained == must produce a parse error");
8368 }
8369
8370 #[test]
8371 fn assoc_08_bitor_left() {
8372 let (e, diags) = parse_expr_str("a | b | c");
8374 assert!(!diags.has_errors(), "{diags:?}");
8375 match &e {
8376 Expr::Binary {
8377 op: BinOp::BitOr,
8378 left,
8379 ..
8380 } => {
8381 assert!(
8382 matches!(
8383 left.as_ref(),
8384 Expr::Binary {
8385 op: BinOp::BitOr,
8386 ..
8387 }
8388 ),
8389 "| should be left-assoc, got {left:?}"
8390 );
8391 }
8392 _ => panic!("expected BitOr, got {e:?}"),
8393 }
8394 }
8395
8396 #[test]
8397 fn assoc_09_bitxor_left() {
8398 let (e, diags) = parse_expr_str("a ^ b ^ c");
8400 assert!(!diags.has_errors(), "{diags:?}");
8401 match &e {
8402 Expr::Binary {
8403 op: BinOp::BitXor,
8404 left,
8405 ..
8406 } => {
8407 assert!(
8408 matches!(
8409 left.as_ref(),
8410 Expr::Binary {
8411 op: BinOp::BitXor,
8412 ..
8413 }
8414 ),
8415 "^ should be left-assoc, got {left:?}"
8416 );
8417 }
8418 _ => panic!("expected BitXor, got {e:?}"),
8419 }
8420 }
8421
8422 #[test]
8423 fn assoc_10_bitand_left() {
8424 let (e, diags) = parse_expr_str("a & b & c");
8426 assert!(!diags.has_errors(), "{diags:?}");
8427 match &e {
8428 Expr::Binary {
8429 op: BinOp::BitAnd,
8430 left,
8431 ..
8432 } => {
8433 assert!(
8434 matches!(
8435 left.as_ref(),
8436 Expr::Binary {
8437 op: BinOp::BitAnd,
8438 ..
8439 }
8440 ),
8441 "& should be left-assoc, got {left:?}"
8442 );
8443 }
8444 _ => panic!("expected BitAnd, got {e:?}"),
8445 }
8446 }
8447
8448 #[test]
8449 fn assoc_11_add_left() {
8450 let (e, diags) = parse_expr_str("a - b - c");
8452 assert!(!diags.has_errors(), "{diags:?}");
8453 match &e {
8454 Expr::Binary {
8455 op: BinOp::Sub,
8456 left,
8457 ..
8458 } => {
8459 assert!(
8460 matches!(left.as_ref(), Expr::Binary { op: BinOp::Sub, .. }),
8461 "- should be left-assoc, got {left:?}"
8462 );
8463 }
8464 _ => panic!("expected Sub, got {e:?}"),
8465 }
8466 }
8467
8468 #[test]
8469 fn assoc_12_mul_left() {
8470 let (e, diags) = parse_expr_str("a / b / c");
8472 assert!(!diags.has_errors(), "{diags:?}");
8473 match &e {
8474 Expr::Binary {
8475 op: BinOp::Div,
8476 left,
8477 ..
8478 } => {
8479 assert!(
8480 matches!(left.as_ref(), Expr::Binary { op: BinOp::Div, .. }),
8481 "/ should be left-assoc, got {left:?}"
8482 );
8483 }
8484 _ => panic!("expected Div, got {e:?}"),
8485 }
8486 }
8487
8488 #[test]
8489 fn assoc_13_power_right() {
8490 let (e, diags) = parse_expr_str("a ** b ** c");
8492 assert!(!diags.has_errors(), "{diags:?}");
8493 match &e {
8494 Expr::Binary {
8495 op: BinOp::Pow,
8496 right,
8497 ..
8498 } => {
8499 assert!(
8500 matches!(right.as_ref(), Expr::Binary { op: BinOp::Pow, .. }),
8501 "** should be right-assoc, got {right:?}"
8502 );
8503 }
8504 _ => panic!("expected Pow, got {e:?}"),
8505 }
8506 }
8507
8508 #[test]
8509 fn assoc_14_unary_chains() {
8510 let (e, diags) = parse_expr_str("--a");
8512 assert!(!diags.has_errors(), "{diags:?}");
8513 match &e {
8514 Expr::Unary {
8515 op: UnaryOp::Neg,
8516 operand,
8517 ..
8518 } => {
8519 assert!(
8520 matches!(
8521 operand.as_ref(),
8522 Expr::Unary {
8523 op: UnaryOp::Neg,
8524 ..
8525 }
8526 ),
8527 "unary should chain, got {operand:?}"
8528 );
8529 }
8530 _ => panic!("expected Neg(Neg), got {e:?}"),
8531 }
8532 }
8533
8534 #[test]
8535 fn assoc_15_postfix_chains_left() {
8536 let (e, diags) = parse_expr_str("a.b.c");
8538 assert!(!diags.has_errors(), "{diags:?}");
8539 match &e {
8540 Expr::FieldAccess { field, object, .. } => {
8541 assert_eq!(field.name, "c");
8542 assert!(
8543 matches!(object.as_ref(), Expr::FieldAccess { .. }),
8544 "postfix should chain left, got {object:?}"
8545 );
8546 }
8547 _ => panic!("expected FieldAccess, got {e:?}"),
8548 }
8549 }
8550
8551 #[test]
8554 fn module_qualified_record_construct() {
8555 let (e, diags) = parse_expr_str("Mod.Type { field: val }");
8557 assert!(!diags.has_errors(), "{diags:?}");
8558 match e {
8559 Expr::RecordConstruct { path, fields, .. } => {
8560 assert_eq!(path.segments.len(), 2);
8561 assert_eq!(path.segments[0].name, "Mod");
8562 assert_eq!(path.segments[1].name, "Type");
8563 assert_eq!(fields.len(), 1);
8564 assert_eq!(fields[0].name.name, "field");
8565 }
8566 _ => panic!("expected RecordConstruct, got {e:?}"),
8567 }
8568 }
8569
8570 #[test]
8571 fn deeply_qualified_record_construct() {
8572 let (e, diags) = parse_expr_str("A.B.C { x: 1 }");
8574 assert!(!diags.has_errors(), "{diags:?}");
8575 match e {
8576 Expr::RecordConstruct { path, fields, .. } => {
8577 assert_eq!(path.segments.len(), 3);
8578 assert_eq!(path.segments[0].name, "A");
8579 assert_eq!(path.segments[1].name, "B");
8580 assert_eq!(path.segments[2].name, "C");
8581 assert_eq!(fields.len(), 1);
8582 }
8583 _ => panic!("expected RecordConstruct, got {e:?}"),
8584 }
8585 }
8586
8587 #[test]
8588 fn lowercase_field_access_not_record() {
8589 let (e, diags) = parse_expr_str("Mod.field");
8592 assert!(!diags.has_errors(), "{diags:?}");
8593 assert!(
8594 matches!(e, Expr::FieldAccess { .. }),
8595 "expected FieldAccess, got {e:?}"
8596 );
8597 }
8598
8599 #[test]
8602 fn method_type_args_single() {
8603 let (e, diags) = parse_expr_str("obj.method[T]()");
8605 assert!(!diags.has_errors(), "{diags:?}");
8606 match e {
8607 Expr::MethodCall {
8608 method,
8609 type_args,
8610 args,
8611 ..
8612 } => {
8613 assert_eq!(method.name, "method");
8614 assert_eq!(type_args.len(), 1);
8615 assert!(args.is_empty());
8616 }
8617 _ => panic!("expected MethodCall, got {e:?}"),
8618 }
8619 }
8620
8621 #[test]
8622 fn method_type_args_multiple() {
8623 let (e, diags) = parse_expr_str("obj.convert[From, To](x)");
8625 assert!(!diags.has_errors(), "{diags:?}");
8626 match e {
8627 Expr::MethodCall {
8628 method,
8629 type_args,
8630 args,
8631 ..
8632 } => {
8633 assert_eq!(method.name, "convert");
8634 assert_eq!(type_args.len(), 2);
8635 assert_eq!(args.len(), 1);
8636 }
8637 _ => panic!("expected MethodCall, got {e:?}"),
8638 }
8639 }
8640
8641 #[test]
8642 fn index_access_not_type_args() {
8643 let (e, diags) = parse_expr_str("obj.data[0]");
8645 assert!(!diags.has_errors(), "{diags:?}");
8646 assert!(matches!(e, Expr::Index { .. }), "expected Index, got {e:?}");
8648 }
8649
8650 #[test]
8653 fn doc_comment_before_fn_no_error() {
8654 let (m, diags) = parse("/// Adds two numbers\nfn add(a: Int, b: Int) -> Int { a + b }\n");
8655 assert!(
8656 !diags.has_errors(),
8657 "doc comment before fn caused errors: {diags:?}"
8658 );
8659 assert_eq!(m.items.len(), 1);
8660 assert!(matches!(m.items[0], Item::Fn(_)));
8661 }
8662
8663 #[test]
8664 fn doc_comment_before_record_no_error() {
8665 let (m, diags) = parse("/// A user\nrecord User {\n name: String\n}\n");
8666 assert!(
8667 !diags.has_errors(),
8668 "doc comment before record caused errors: {diags:?}"
8669 );
8670 assert_eq!(m.items.len(), 1);
8671 assert!(matches!(m.items[0], Item::Record(_)));
8672 }
8673
8674 #[test]
8675 fn doc_comment_before_enum_no_error() {
8676 let (m, diags) = parse("/// Colors\nenum Color {\n Red\n Green\n}\n");
8677 assert!(
8678 !diags.has_errors(),
8679 "doc comment before enum caused errors: {diags:?}"
8680 );
8681 assert_eq!(m.items.len(), 1);
8682 assert!(matches!(m.items[0], Item::Enum(_)));
8683 }
8684
8685 #[test]
8686 fn doc_comment_before_trait_no_error() {
8687 let (m, diags) =
8688 parse("/// Greetable things\ntrait Greetable {\n fn greet() -> String\n}\n");
8689 assert!(
8690 !diags.has_errors(),
8691 "doc comment before trait caused errors: {diags:?}"
8692 );
8693 assert_eq!(m.items.len(), 1);
8694 assert!(matches!(m.items[0], Item::Trait(_)));
8695 }
8696
8697 #[test]
8698 fn multiple_consecutive_doc_comments() {
8699 let (m, diags) = parse("/// Line 1\n/// Line 2\n/// Line 3\nfn foo() {}\n");
8700 assert!(
8701 !diags.has_errors(),
8702 "multiple doc comments caused errors: {diags:?}"
8703 );
8704 assert_eq!(m.items.len(), 1);
8705 assert!(matches!(m.items[0], Item::Fn(_)));
8706 }
8707
8708 #[test]
8709 fn module_doc_comment_no_error() {
8710 let (m, diags) = parse("//! Module docs\n\nfn foo() {}\n");
8711 assert!(
8712 !diags.has_errors(),
8713 "module doc comment caused errors: {diags:?}"
8714 );
8715 assert_eq!(m.doc.len(), 1);
8716 assert_eq!(m.doc[0], "Module docs");
8717 }
8718
8719 #[test]
8720 fn module_doc_comment_after_module_decl() {
8721 let (m, diags) = parse("module foo\n\n//! After module\n//! More docs\n\nfn bar() {}\n");
8723 assert!(
8724 !diags.has_errors(),
8725 "//! after module decl caused errors: {diags:?}"
8726 );
8727 assert_eq!(m.doc.len(), 2);
8728 assert_eq!(m.doc[0], "After module");
8729 assert_eq!(m.doc[1], "More docs");
8730 assert_eq!(m.items.len(), 1);
8731 }
8732
8733 #[test]
8734 fn module_doc_comment_before_and_after_module_decl() {
8735 let (m, diags) = parse("//! Before\nmodule foo\n//! After\nfn bar() {}\n");
8737 assert!(
8738 !diags.has_errors(),
8739 "//! before+after module decl caused errors: {diags:?}"
8740 );
8741 assert_eq!(m.doc.len(), 2);
8742 assert_eq!(m.doc[0], "Before");
8743 assert_eq!(m.doc[1], "After");
8744 }
8745
8746 #[test]
8747 fn module_doc_comment_after_module_with_use_no_hang() {
8748 let (m, diags) = parse("module foo\n//! Docs\nuse bar.{baz}\nfn f() {}\n");
8750 assert!(
8751 !diags.has_errors(),
8752 "//! after module with use caused errors: {diags:?}"
8753 );
8754 assert_eq!(m.doc.len(), 1);
8755 assert_eq!(m.imports.len(), 1);
8756 }
8757
8758 #[test]
8759 fn doc_comment_trailing_no_error() {
8760 let (_m, diags) = parse("/// orphan doc\n");
8762 assert!(
8763 !diags.has_errors(),
8764 "trailing doc comment caused errors: {diags:?}"
8765 );
8766 }
8767}