1use crate::ast::*;
8use crate::layout::resolve_layout;
9use crate::lexer::{lex, Pos, Token, TokenKind};
10use std::collections::HashMap;
11
12pub const MAX_RECURSION_DEPTH: u32 = 128;
13
14#[derive(Debug, Clone)]
15pub struct ParseModuleResult {
16 pub module: Module,
17 pub diagnostics: Vec<ParseDiagnostic>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct ParseModuleError {
28 diagnostics: Vec<ParseDiagnostic>,
29 module: Box<Module>,
30}
31
32impl ParseModuleError {
33 #[must_use]
34 pub fn diagnostics(&self) -> &[ParseDiagnostic] {
35 &self.diagnostics
36 }
37
38 #[must_use]
39 pub fn module(&self) -> &Module {
40 &self.module
41 }
42
43 #[must_use]
44 pub fn into_parts(self) -> (Vec<ParseDiagnostic>, Module) {
45 (self.diagnostics, *self.module)
46 }
47}
48
49impl std::fmt::Display for ParseModuleError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(
52 f,
53 "parse failed with {} diagnostic(s)",
54 self.diagnostics.len()
55 )?;
56 if let Some(first) = self.diagnostics.first() {
57 write!(f, ": {}", first.message)?;
58 }
59 Ok(())
60 }
61}
62
63impl std::error::Error for ParseModuleError {}
64
65#[derive(Clone, Copy, Debug)]
66enum DoExpressionMode {
67 Allow,
68 Disallow,
69}
70
71impl DoExpressionMode {
72 const fn allows_do(&self) -> bool {
73 matches!(self, Self::Allow)
74 }
75}
76
77impl ParseModuleResult {
78 #[must_use]
79 pub const fn has_errors(&self) -> bool {
80 !self.diagnostics.is_empty()
81 }
82
83 #[must_use]
84 pub fn into_parts(self) -> (Module, Vec<ParseDiagnostic>) {
85 (self.module, self.diagnostics)
86 }
87
88 pub fn into_result(self) -> Result<Module, ParseModuleError> {
94 if self.diagnostics.is_empty() {
95 Ok(self.module)
96 } else {
97 Err(ParseModuleError {
98 diagnostics: self.diagnostics,
99 module: Box::new(self.module),
100 })
101 }
102 }
103}
104
105#[must_use]
120pub fn parse_module(source: &str) -> ParseModuleResult {
121 let lexed = lex(source);
122 let tokens = lexed.tokens;
123 let lex_errors = lexed.errors;
124 let tokens = resolve_layout(tokens);
125 let mut p = Parser {
126 toks: tokens,
127 src_len: source.len(),
128 i: 0,
129 depth: 0,
130 diags: lex_errors
131 .into_iter()
132 .map(|e| {
133 let range = e.byte_range_in(source);
134 ParseDiagnostic {
135 message: e.to_string(),
136 pos: e.pos,
137 span: crate::ast::Span::new(range.start, range.end),
138 category: DiagnosticCategory::Lex,
139 }
140 })
141 .collect(),
142 };
143 let mut module = p.module();
144 module.span = crate::ast::Span::new(0, source.len());
145 ParseModuleResult {
146 module,
147 diagnostics: p.diags,
148 }
149}
150
151pub fn parse_module_strict(source: &str) -> Result<Module, ParseModuleError> {
169 parse_module(source).into_result()
170}
171
172struct Parser {
173 toks: Vec<Token>,
174 src_len: usize,
176 i: usize,
177 diags: Vec<ParseDiagnostic>,
178 depth: u32,
181}
182
183impl Parser {
184 fn node_span(&self, from: usize) -> crate::ast::Span {
190 let mut a = from;
191 while a < self.i && self.toks[a].is_virtual() {
192 a += 1;
193 }
194 let mut b = self.i;
195 while b > a && self.toks[b - 1].is_virtual() {
196 b -= 1;
197 }
198 if a >= b {
199 let p = self.byte_at(a);
204 return crate::ast::Span::new(p, p);
205 }
206 crate::ast::Span::new(self.toks[a].start, self.toks[b - 1].end)
207 }
208
209 fn byte_at(&self, i: usize) -> usize {
211 self.toks.get(i).map(|t| t.start).unwrap_or(self.src_len)
212 }
213
214 fn end_byte(&self) -> usize {
218 let mut b = self.i;
219 while b > 0 && self.toks[b - 1].is_virtual() {
220 b -= 1;
221 }
222 if b == 0 {
223 0
224 } else {
225 self.toks[b - 1].end
226 }
227 }
228}
229
230impl Parser {
231 fn peek(&self) -> Option<&TokenKind> {
234 self.toks.get(self.i).map(|t| &t.kind)
235 }
236
237 fn peek_at(&self, n: usize) -> Option<&TokenKind> {
238 self.toks.get(self.i + n).map(|t| &t.kind)
239 }
240
241 fn pos(&self) -> Pos {
242 self.toks
243 .get(self.i)
244 .or_else(|| self.toks.last())
245 .map_or(Pos { line: 1, column: 1 }, |t| t.pos)
246 }
247
248 fn bump(&mut self) -> Option<Token> {
249 let t = self.toks.get(self.i).cloned();
250 if t.is_some() {
251 self.i += 1;
252 }
253 t
254 }
255
256 fn at_keyword(&self, kw: &str) -> bool {
257 self.peek().is_some_and(|t| t.is_keyword(kw))
258 }
259
260 fn eat_keyword(&mut self, kw: &str) -> bool {
261 if self.at_keyword(kw) {
262 self.i += 1;
263 true
264 } else {
265 false
266 }
267 }
268
269 fn at_op(&self, op: &str) -> bool {
270 self.peek().is_some_and(|t| t.is_op(op))
271 }
272
273 fn eat_op(&mut self, op: &str) -> bool {
274 if self.at_op(op) {
275 self.i += 1;
276 true
277 } else {
278 false
279 }
280 }
281
282 fn at(&self, tok: &TokenKind) -> bool {
283 self.peek() == Some(tok)
284 }
285
286 fn eat(&mut self, tok: &TokenKind) -> bool {
287 if self.at(tok) {
288 self.i += 1;
289 true
290 } else {
291 false
292 }
293 }
294
295 fn diag(&mut self, message: impl Into<String>) {
297 self.diag_cat(DiagnosticCategory::Malformed, message);
298 }
299
300 fn diag_cat(&mut self, category: DiagnosticCategory, message: impl Into<String>) {
304 let pos = self.pos();
305 let span = self.cur_span();
306 self.diags.push(ParseDiagnostic {
307 message: message.into(),
308 pos,
309 span,
310 category,
311 });
312 }
313
314 fn parse_type_annotation(
315 &mut self,
316 type_start: usize,
317 type_end: usize,
318 context: &'static str,
319 ) -> Option<Type> {
320 let type_start = type_start.min(self.toks.len());
321 let type_end = type_end.min(self.toks.len());
322 let tokens = &self.toks[type_start..type_end];
323 let ty = parse_type_from_tokens(tokens).or_else(|| {
324 let trimmed = Self::trim_type_tokens_for_parse(tokens);
325 if trimmed < tokens.len() {
326 parse_type_from_tokens(&tokens[..trimmed])
327 } else {
328 None
329 }
330 });
331 if ty.is_none() {
332 self.diags.push(ParseDiagnostic {
333 message: format!("malformed {context} type annotation"),
334 pos: self.pos_of_token(type_start),
335 span: self.span_of_token_range(type_start, type_end),
336 category: DiagnosticCategory::Malformed,
337 });
338 }
339 ty
340 }
341
342 fn trim_type_tokens_for_parse(tokens: &[Token]) -> usize {
347 let mut depth = 0usize;
348 let mut bracket_depth = 0usize;
349 let mut i = 0usize;
350 while i < tokens.len() {
351 match &tokens[i].kind {
352 TokenKind::LParen | TokenKind::LBracket => {
353 depth += 1;
354 i += 1;
355 }
356 TokenKind::RParen | TokenKind::RBracket => {
357 depth = depth.saturating_sub(1);
358 i += 1;
359 }
360 TokenKind::LBrace => {
361 bracket_depth += 1;
362 i += 1;
363 }
364 TokenKind::RBrace => {
365 bracket_depth = bracket_depth.saturating_sub(1);
366 i += 1;
367 }
368 TokenKind::Op(o) if o.as_str() == "=" && depth == 0 && bracket_depth == 0 => {
369 return i;
370 }
371 TokenKind::Semi | TokenKind::VSemi if depth == 0 && bracket_depth == 0 => {
372 return i;
373 }
374 TokenKind::Comma
375 if depth == 0
376 && bracket_depth == 0
377 && matches!(
378 tokens.get(i + 1),
379 Some(Token {
380 kind: TokenKind::LowerId {
381 qualifier: None,
382 ..
383 },
384 ..
385 })
386 )
387 && matches!(
388 tokens.get(i + 2),
389 Some(Token { kind: TokenKind::Op(o), .. }) if o.as_str() == ":"
390 ) =>
391 {
392 return i;
393 }
394 _ => {
395 i += 1;
396 }
397 }
398 }
399 tokens.len()
400 }
401
402 fn pos_of_token(&self, idx: usize) -> Pos {
403 self.toks.get(idx).map_or_else(|| self.pos(), |tok| tok.pos)
404 }
405
406 fn span_of_token_range(&self, start: usize, end: usize) -> Span {
407 let start = start.min(self.toks.len());
408 let end = end.min(self.toks.len());
409 let span_start = self.byte_at(start);
410 if end <= start {
411 return Span::new(span_start, span_start);
412 }
413
414 let mut cursor = end;
415 while cursor > start {
416 cursor -= 1;
417 let token = &self.toks[cursor];
418 if !token.is_virtual() {
419 return Span::new(span_start, token.end);
420 }
421 }
422
423 Span::new(span_start, span_start)
424 }
425
426 fn cur_span(&self) -> crate::ast::Span {
429 let mut j = self.i;
430 while self.toks.get(j).is_some_and(|t| t.is_virtual()) {
431 j += 1;
432 }
433 self.toks.get(j).map_or_else(
434 || crate::ast::Span::new(self.src_len, self.src_len),
435 |t| crate::ast::Span::new(t.start, t.end),
436 )
437 }
438
439 fn skip_to_item_end(&mut self) {
442 let mut depth = 0usize;
443 let mut brackets = 0usize;
444 while let Some(t) = self.peek() {
445 match t {
446 TokenKind::VLBrace => depth += 1,
447 TokenKind::VRBrace => {
448 if depth == 0 {
449 return;
450 }
451 depth -= 1;
452 }
453 TokenKind::VSemi if depth == 0 && brackets == 0 => return,
454 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => brackets += 1,
455 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
456 if brackets == 0 {
457 return;
460 }
461 brackets -= 1;
462 }
463 _ => {}
464 }
465 self.i += 1;
466 }
467 }
468
469 fn slice_text(&self, start: usize) -> String {
471 render_token_slice(&self.toks[start..self.i])
472 }
473
474 fn module(&mut self) -> Module {
477 let pos = self.pos();
478 let header_start = self.i;
479 let mut header = crate::ast::Span::new(0, 0);
480 let mut name = ModuleName::from("Unknown");
481
482 if self.eat_keyword("module") {
483 if let Some(TokenKind::UpperId { qualifier, name: n }) = self.peek().cloned() {
484 self.bump();
485 name = match qualifier {
486 Some(q) => format!("{q}.{n}").into(),
487 None => n.into(),
488 };
489 }
490 if self.at(&TokenKind::LParen) {
492 self.skip_balanced_parens();
493 }
494 if !self.eat_keyword("where") {
495 self.diag("expected 'where' after module header");
496 }
497 header = self.node_span(header_start);
498 }
499
500 let mut imports = Vec::new();
501 let mut decls: Vec<Decl> = Vec::new();
502
503 let _ = self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace);
507 loop {
508 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
509 match self.peek() {
510 None => break,
511 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
512 self.bump();
513 break;
514 }
515 Some(TokenKind::RParen | TokenKind::RBracket) => {
520 let cpos = self.pos();
521 let cstart = self.i;
522 self.bump();
523 decls.push(Decl::Unknown {
524 raw: self.slice_text(cstart),
525 pos: cpos,
526 span: self.node_span(cstart),
527 });
528 continue;
529 }
530 _ => {}
531 }
532 let before = self.i;
533 self.declaration(&mut imports, &mut decls);
534 if self.i == before {
535 self.bump();
537 }
538 }
539
540 merge_functions(&mut decls);
541
542 Module {
543 name,
544 pos,
545 header,
546 imports,
547 decls,
548 span: crate::ast::Span::new(0, self.src_len),
549 }
550 }
551
552 fn skip_balanced_parens(&mut self) {
553 let mut depth = 0usize;
554 while let Some(t) = self.peek() {
555 match t {
556 TokenKind::LParen => depth += 1,
557 TokenKind::RParen => {
558 if depth == 0 {
559 return;
560 }
561 depth -= 1;
562 if depth == 0 {
563 self.i += 1;
564 return;
565 }
566 }
567 _ => {}
568 }
569 self.i += 1;
570 }
571 }
572
573 fn try_infix_operator_decl(&mut self) -> bool {
577 let snap = self.i;
578 let saved_diags = self.diags.len();
579 if self.pattern().is_some()
580 && matches!(self.peek(), Some(TokenKind::Op(o)) if !is_reserved_op(o))
581 {
582 self.skip_to_item_end();
583 return true;
584 }
585 self.i = snap;
586 self.diags.truncate(saved_diags);
587 false
588 }
589
590 fn declaration(&mut self, imports: &mut Vec<ImportDecl>, decls: &mut Vec<Decl>) {
591 let pos = self.pos();
592 let start = self.i;
593 if matches!(
594 self.peek(),
595 Some(TokenKind::UpperId { .. } | TokenKind::LBracket | TokenKind::LParen)
596 ) && self.try_infix_operator_decl()
597 {
598 decls.push(Decl::Unknown {
599 raw: self.slice_text(start),
600 pos,
601 span: self.node_span(start),
602 });
603 return;
604 }
605 match self.peek() {
606 Some(t) if t.is_keyword("import") => {
607 let imp = self.import_decl();
608 self.skip_to_item_end();
612 if let Some(mut imp) = imp {
613 imp.span = self.node_span(start);
614 imports.push(imp);
615 }
616 }
617 Some(t) if t.is_keyword("template") => {
618 match self.template_decl() {
621 Some(t) => decls.push(Decl::Template(t)),
622 None => {
623 self.skip_to_item_end();
624 decls.push(Decl::Unknown {
625 raw: self.slice_text(start),
626 span: self.node_span(start),
627 pos,
628 });
629 }
630 }
631 }
632 Some(t) if t.is_keyword("interface") => match self.interface_decl() {
633 Some(i) => decls.push(Decl::Interface(i)),
634 None => {
635 self.skip_to_item_end();
636 decls.push(Decl::Unknown {
637 raw: self.slice_text(start),
638 span: self.node_span(start),
639 pos,
640 });
641 }
642 },
643 Some(t)
644 if matches!(
645 t.keyword(),
646 Some("infix" | "infixl" | "infixr" | "default" | "pattern")
649 ) =>
650 {
651 self.skip_to_item_end();
652 decls.push(Decl::Unknown {
653 raw: self.slice_text(start),
654 pos,
655 span: self.node_span(start),
656 });
657 }
658 Some(t)
659 if matches!(
660 t.keyword(),
661 Some(
662 "data"
663 | "type"
664 | "newtype"
665 | "class"
666 | "instance"
667 | "exception"
668 | "deriving"
669 )
670 ) =>
671 {
672 let keyword = t
673 .keyword()
674 .expect("declaration-head keyword token")
675 .to_string();
676 self.bump();
677 let name = match self.peek() {
678 Some(TokenKind::UpperId { qualifier, name }) => {
679 let n = qualifier
680 .as_ref()
681 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}"));
682 self.bump();
683 n
684 }
685 _ => String::new(),
686 };
687 self.skip_to_item_end();
688 decls.push(Decl::TypeDef {
689 keyword,
690 name: name.into(),
691 pos,
692 span: self.node_span(start),
693 });
694 }
695 Some(TokenKind::LowerId { .. }) => match self.function_item() {
696 Some(d) => decls.push(d),
697 None => {
698 self.skip_to_item_end();
699 decls.push(Decl::Unknown {
700 raw: self.slice_text(start),
701 span: self.node_span(start),
702 pos,
703 });
704 }
705 },
706 Some(TokenKind::LParen)
708 if matches!(self.peek_at(1), Some(TokenKind::Op(_)))
709 && self.peek_at(2) == Some(&TokenKind::RParen) =>
710 {
711 self.skip_to_item_end();
712 decls.push(Decl::Unknown {
713 raw: self.slice_text(start),
714 span: self.node_span(start),
715 pos,
716 });
717 }
718 Some(TokenKind::LParen | TokenKind::LBracket) => {
720 if self.binding().is_none() {
721 self.diag_cat(
722 DiagnosticCategory::SkippedDecl,
723 "unparseable top-level pattern binding",
724 );
725 }
726 self.skip_to_item_end();
727 decls.push(Decl::Unknown {
728 raw: self.slice_text(start),
729 span: self.node_span(start),
730 pos,
731 });
732 }
733 _ => {
734 self.diag_cat(
735 DiagnosticCategory::SkippedDecl,
736 format!("unrecognized declaration: {:?}", self.peek()),
737 );
738 self.skip_to_item_end();
739 decls.push(Decl::Unknown {
740 raw: self.slice_text(start),
741 span: self.node_span(start),
742 pos,
743 });
744 }
745 }
746 }
747
748 fn import_decl(&mut self) -> Option<ImportDecl> {
751 let pos = self.pos();
752 let start_i = self.i;
753 self.bump(); let mut style = if self.eat_keyword("qualified") {
755 ImportStyle::Qualified
756 } else {
757 ImportStyle::Unqualified
758 };
759 if matches!(self.peek(), Some(TokenKind::StringLit(_))) {
761 self.bump();
762 }
763 let module_name = match self.peek().cloned() {
764 Some(TokenKind::UpperId { qualifier, name }) => {
765 self.bump();
766 match qualifier {
767 Some(q) => format!("{q}.{name}").into(),
768 None => name.into(),
769 }
770 }
771 _ => {
772 self.diag("expected module name after 'import'");
773 return None;
774 }
775 };
776 if self.eat_keyword("qualified") {
778 style = ImportStyle::Qualified;
779 }
780 let mut alias = None;
781 if self.eat_keyword("as") {
782 if let Some(TokenKind::UpperId { qualifier, name }) = self.peek().cloned() {
783 self.bump();
784 alias = Some(match qualifier {
785 Some(q) => format!("{q}.{name}").into(),
786 None => name.into(),
787 });
788 }
789 }
790 Some(ImportDecl {
792 module_name,
793 style,
794 alias,
795 pos,
796 span: self.node_span(start_i),
797 })
798 }
799
800 fn upper_name(&mut self) -> Option<ModuleName> {
803 match self.peek().cloned() {
804 Some(TokenKind::UpperId { qualifier, name }) => {
805 self.bump();
806 Some(match qualifier {
807 Some(q) => format!("{q}.{name}").into(),
808 None => name.into(),
809 })
810 }
811 _ => None,
812 }
813 }
814
815 fn template_decl(&mut self) -> Option<TemplateDecl> {
816 let pos = self.pos();
817 let start_i = self.i;
818 self.bump(); if self.at_keyword("instance") {
820 return None; }
822 let name = self.upper_name()?.to_string().into();
823
824 let fields = self
825 .eat_keyword("with")
826 .then(|| self.field_block())
827 .map(|parsed| parsed.fields)
828 .unwrap_or_default();
829 let body = if self.eat_keyword("where") {
830 self.template_body()
831 } else {
832 Vec::new()
833 };
834 Some(TemplateDecl {
835 name,
836 fields,
837 body,
838 pos,
839 span: self.node_span(start_i),
840 })
841 }
842
843 fn field_block(&mut self) -> FieldBlock {
850 let mut fields = Vec::new();
851 if !(self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace)) {
852 return FieldBlock {
853 fields,
854 dangling: false,
855 };
856 }
857 loop {
858 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
859 match self.peek() {
860 None => break,
861 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
862 self.bump();
863 break;
864 }
865 Some(TokenKind::RParen | TokenKind::RBracket) => {
870 self.bump();
871 continue;
872 }
873 _ => {}
874 }
875 {
880 let mut j = self.i;
881 while let Some(TokenKind::LowerId {
882 qualifier: None, ..
883 }) = self.toks.get(j).map(|t| &t.kind)
884 {
885 j += 1;
886 match self.toks.get(j).map(|t| &t.kind) {
887 Some(TokenKind::Comma) => j += 1,
888 _ => break,
889 }
890 }
891 let is_field = j > self.i
892 && self
893 .toks
894 .get(j)
895 .map(|t| &t.kind)
896 .is_some_and(|t| t.is_op(":"));
897 if !is_field {
898 return FieldBlock {
899 fields,
900 dangling: true,
901 };
902 }
903 }
904 let mut names: Vec<(Identifier, Pos, Span)> = Vec::new();
906 while let Some(TokenKind::LowerId {
907 qualifier: None,
908 name,
909 }) = self.peek().cloned()
910 {
911 let p = self.pos();
912 let nspan = Span::new(self.toks[self.i].start, self.toks[self.i].end);
913 self.bump();
914 names.push((name, p, nspan));
915 if !self.eat(&TokenKind::Comma) {
916 break;
917 }
918 }
919 if names.is_empty() || !self.eat_op(":") {
920 self.diag("expected 'name : Type' field");
921 self.skip_to_item_end();
922 continue;
923 }
924 let ty_start = self.i;
925 self.skip_to_item_end();
926 let ty = self.parse_type_annotation(ty_start, self.i, "field");
927 let type_end = self.end_byte();
932 let last = names.len() - 1;
933 for (idx, (name, p, nspan)) in names.into_iter().enumerate() {
934 let span = if idx == last {
935 Span::new(nspan.start, type_end.max(nspan.end))
936 } else {
937 nspan
938 };
939 fields.push(FieldDecl {
940 name: name.to_string().into(),
941 ty: ty.clone(),
942 pos: p,
943 span,
944 });
945 }
946 }
947 FieldBlock {
948 fields,
949 dangling: false,
950 }
951 }
952
953 fn template_body(&mut self) -> Vec<TemplateBodyDecl> {
956 let mut body = Vec::new();
957 if !(self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace)) {
958 return body;
959 }
960 loop {
961 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
962 match self.peek() {
963 None => break,
964 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
965 self.bump();
966 break;
967 }
968 Some(TokenKind::RParen | TokenKind::RBracket) => {
973 self.bump();
974 continue;
975 }
976 _ => {}
977 }
978 let pos = self.pos();
979 let start = self.i;
980 let decl = self.template_body_item(pos, start);
981 body.push(decl);
982 }
983 body
984 }
985
986 fn template_body_item(&mut self, pos: Pos, start: usize) -> TemplateBodyDecl {
987 match self.peek().and_then(|t| t.keyword()) {
988 Some("signatory") => {
989 self.bump();
990 let parties = self.expr_comma_list();
991 self.skip_to_item_end();
992 TemplateBodyDecl::Signatory {
993 parties,
994 pos,
995 span: self.node_span(start),
996 }
997 }
998 Some("observer") => {
999 self.bump();
1000 let parties = self.expr_comma_list();
1001 self.skip_to_item_end();
1002 TemplateBodyDecl::Observer {
1003 parties,
1004 pos,
1005 span: self.node_span(start),
1006 }
1007 }
1008 Some("ensure") => {
1009 self.bump();
1010 let expr = self.expr();
1011 self.skip_to_item_end();
1012 TemplateBodyDecl::Ensure {
1013 expr,
1014 pos,
1015 span: self.node_span(start),
1016 }
1017 }
1018 Some("key") => {
1019 self.bump();
1020 let expr_start = self.i;
1021 let expr = self.expr();
1022 let ty = if self.eat_op(":") {
1023 let ty_start = self.i;
1024 self.skip_to_item_end();
1025 self.parse_type_annotation(ty_start, self.i, "key")
1026 } else {
1027 let mut depth = 0i32;
1030 let mut colon = None;
1031 for j in expr_start..self.i {
1032 match &self.toks[j].kind {
1033 TokenKind::LParen | TokenKind::LBracket => depth += 1,
1034 TokenKind::RParen | TokenKind::RBracket if depth > 0 => depth -= 1,
1035 TokenKind::Op(o) if o.as_str() == ":" && depth == 0 => colon = Some(j),
1036 _ => {}
1037 }
1038 }
1039 let ty = colon.and_then(|j| self.parse_type_annotation(j + 1, self.i, "key"));
1040 self.skip_to_item_end();
1041 ty
1042 };
1043 TemplateBodyDecl::Key {
1044 expr,
1045 ty,
1046 pos,
1047 span: self.node_span(start),
1048 }
1049 }
1050 Some("maintainer") => {
1051 self.bump();
1052 let expr = self.expr();
1053 self.skip_to_item_end();
1054 TemplateBodyDecl::Maintainer {
1055 expr,
1056 pos,
1057 span: self.node_span(start),
1058 }
1059 }
1060 Some("choice" | "nonconsuming" | "preconsuming" | "postconsuming") => {
1061 self.choice_decl().map_or_else(
1062 || {
1063 self.skip_to_item_end();
1064 TemplateBodyDecl::Other {
1065 raw: self.slice_text(start),
1066 span: self.node_span(start),
1067 pos,
1068 }
1069 },
1070 TemplateBodyDecl::Choice,
1071 )
1072 }
1073 Some("interface") => self.interface_instance_decl().map_or_else(
1074 || {
1075 self.skip_to_item_end();
1076 TemplateBodyDecl::Other {
1077 raw: self.slice_text(start),
1078 span: self.node_span(start),
1079 pos,
1080 }
1081 },
1082 TemplateBodyDecl::InterfaceInstance,
1083 ),
1084 Some("controller") => {
1085 self.diag_cat(
1089 DiagnosticCategory::UnsupportedSyntax,
1090 "legacy 'controller ... can' syntax is not supported; \
1091 choices inside this block are not analyzed",
1092 );
1093 self.skip_to_item_end();
1094 TemplateBodyDecl::Other {
1095 raw: self.slice_text(start),
1096 span: self.node_span(start),
1097 pos,
1098 }
1099 }
1100 _ => {
1101 self.skip_to_item_end();
1102 TemplateBodyDecl::Other {
1103 raw: self.slice_text(start),
1104 span: self.node_span(start),
1105 pos,
1106 }
1107 }
1108 }
1109 }
1110
1111 fn choice_decl(&mut self) -> Option<ChoiceDecl> {
1112 let pos = self.pos();
1113 let start_i = self.i;
1114 let consuming = match self.peek().and_then(|t| t.keyword()) {
1115 Some("nonconsuming") => {
1116 self.bump();
1117 Consuming::NonConsuming
1118 }
1119 Some("preconsuming") => {
1120 self.bump();
1121 Consuming::PreConsuming
1122 }
1123 Some("postconsuming") => {
1124 self.bump();
1125 Consuming::PostConsuming
1126 }
1127 _ => Consuming::Consuming,
1128 };
1129 if !self.eat_keyword("choice") {
1130 return None;
1131 }
1132 let name = self.upper_name()?.to_string().into();
1133 let return_ty = if self.eat_op(":") {
1134 let ty_start = self.i;
1135 self.skip_type_tokens();
1136 self.parse_type_annotation(ty_start, self.i, "choice")
1137 } else {
1138 None
1139 };
1140 let (params, dangling) = if self.eat_keyword("with") {
1141 let parsed = self.field_block();
1142 (parsed.fields, parsed.dangling)
1143 } else {
1144 (Vec::new(), false)
1145 };
1146 let mut observers = Vec::new();
1147 let mut controllers = Vec::new();
1148 loop {
1149 if dangling {
1153 while self.eat(&TokenKind::VSemi) {}
1154 }
1155 if self.eat_keyword("observer") {
1156 observers = self.expr_comma_list_no_do();
1157 } else if self.eat_keyword("controller") {
1158 controllers = self.expr_comma_list_no_do();
1159 } else {
1160 break;
1161 }
1162 }
1163 if dangling {
1164 while self.eat(&TokenKind::VSemi) {}
1165 }
1166 let body = if self.peek().is_some_and(|t| {
1167 !matches!(
1168 t,
1169 TokenKind::VSemi | TokenKind::VRBrace | TokenKind::Semi | TokenKind::RBrace
1170 )
1171 }) {
1172 Some(self.expr())
1173 } else {
1174 None
1175 };
1176 self.skip_to_item_end();
1177 if dangling {
1178 self.eat(&TokenKind::VRBrace);
1181 self.skip_to_item_end();
1182 }
1183 Some(ChoiceDecl {
1184 name,
1185 consuming,
1186 return_ty,
1187 params,
1188 controllers,
1189 observers,
1190 body,
1191 pos,
1192 span: self.node_span(start_i),
1193 })
1194 }
1195
1196 fn skip_type_tokens(&mut self) {
1199 let mut brackets = 0usize;
1200 while let Some(t) = self.peek() {
1201 match t {
1202 TokenKind::VSemi | TokenKind::VRBrace | TokenKind::VLBrace | TokenKind::Semi => {
1203 return
1204 }
1205 TokenKind::LParen | TokenKind::LBracket => brackets += 1,
1206 TokenKind::RParen | TokenKind::RBracket => {
1207 if brackets == 0 {
1208 return;
1209 }
1210 brackets -= 1;
1211 }
1212 _ if brackets == 0
1213 && matches!(
1214 t.keyword(),
1215 Some("with" | "controller" | "observer" | "do" | "where")
1216 ) =>
1217 {
1218 return
1219 }
1220 _ => {}
1221 }
1222 self.i += 1;
1223 }
1224 }
1225
1226 fn interface_decl(&mut self) -> Option<InterfaceDecl> {
1229 let pos = self.pos();
1230 let start_i = self.i;
1231 self.bump(); if self.at_keyword("instance") {
1233 return None;
1235 }
1236 let name = self.upper_name()?.to_string().into();
1237 let mut requires = Vec::new();
1238 if self.eat_keyword("requires") {
1239 while let Some(r) = self.upper_name() {
1240 requires.push(r);
1241 if !self.eat(&TokenKind::Comma) {
1242 break;
1243 }
1244 }
1245 }
1246 if !self.eat_keyword("where") {
1247 return None;
1248 }
1249 let mut viewtype = None;
1250 let mut methods = Vec::new();
1251 let mut choices = Vec::new();
1252 if !(self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace)) {
1253 return Some(InterfaceDecl {
1254 name,
1255 requires,
1256 viewtype,
1257 methods,
1258 choices,
1259 pos,
1260 span: self.node_span(start_i),
1261 });
1262 }
1263 loop {
1264 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
1265 match self.peek() {
1266 None => break,
1267 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
1268 self.bump();
1269 break;
1270 }
1271 Some(TokenKind::RParen | TokenKind::RBracket) => {
1276 self.bump();
1277 continue;
1278 }
1279 _ => {}
1280 }
1281 match self.peek().and_then(|t| t.keyword()) {
1282 Some("viewtype") => {
1283 self.bump();
1284 viewtype = self.upper_name();
1285 self.skip_to_item_end();
1286 }
1287 Some("choice" | "nonconsuming" | "preconsuming" | "postconsuming") => {
1288 if let Some(c) = self.choice_decl() {
1289 choices.push(c);
1290 } else {
1291 self.skip_to_item_end();
1292 }
1293 }
1294 _ => {
1295 let mpos = self.pos();
1298 if let Some(TokenKind::LowerId {
1299 qualifier: None,
1300 name: mname,
1301 }) = self.peek().cloned()
1302 {
1303 if self.peek_at(1).is_some_and(|t| t.is_op(":")) {
1304 let mstart = self.toks[self.i].start;
1305 self.bump();
1306 self.bump();
1307 let ty_start = self.i;
1308 self.skip_to_item_end();
1309 methods.push(FieldDecl {
1311 name: mname,
1312 ty: self.parse_type_annotation(
1313 ty_start,
1314 self.i,
1315 "interface method",
1316 ),
1317 pos: mpos,
1318 span: Span::new(mstart, self.end_byte().max(mstart)),
1319 });
1320 continue;
1321 }
1322 }
1323 self.skip_to_item_end();
1324 }
1325 }
1326 }
1327 Some(InterfaceDecl {
1328 name,
1329 requires,
1330 viewtype,
1331 methods,
1332 choices,
1333 pos,
1334 span: self.node_span(start_i),
1335 })
1336 }
1337
1338 fn interface_instance_decl(&mut self) -> Option<InterfaceInstanceDecl> {
1340 let pos = self.pos();
1341 let start_i = self.i;
1342 self.bump(); if !self.eat_keyword("instance") {
1344 return None;
1345 }
1346 let interface_name = self.upper_name()?;
1347 let for_template = if self.eat_keyword("for") {
1348 self.upper_name().unwrap_or_default()
1349 } else {
1350 ModuleName::default()
1351 };
1352 let mut methods = Vec::new();
1353 if self.eat_keyword("where")
1354 && (self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace))
1355 {
1356 loop {
1357 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
1358 match self.peek() {
1359 None => break,
1360 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
1361 self.bump();
1362 break;
1363 }
1364 Some(TokenKind::RParen | TokenKind::RBracket) => {
1366 self.bump();
1367 continue;
1368 }
1369 _ => {}
1370 }
1371 if let Some(b) = self.binding() {
1372 methods.push(b);
1373 } else {
1374 self.skip_to_item_end();
1375 }
1376 }
1377 }
1378 Some(InterfaceInstanceDecl {
1379 interface_name,
1380 for_template,
1381 methods,
1382 pos,
1383 span: self.node_span(start_i),
1384 })
1385 }
1386
1387 fn function_item(&mut self) -> Option<Decl> {
1393 let pos = self.pos();
1394 let start_i = self.i;
1395 let name = match self.peek().cloned() {
1396 Some(TokenKind::LowerId {
1397 qualifier: None,
1398 name,
1399 }) => name,
1400 _ => return None,
1401 };
1402
1403 let mut j = self.i + 1;
1405 let mut is_sig = false;
1406 loop {
1407 match self.toks.get(j).map(|t| &t.kind) {
1408 Some(TokenKind::Comma) => {
1409 j += 1;
1410 if matches!(
1411 self.toks.get(j).map(|t| &t.kind),
1412 Some(TokenKind::LowerId {
1413 qualifier: None,
1414 ..
1415 })
1416 ) {
1417 j += 1;
1418 continue;
1419 }
1420 break;
1421 }
1422 Some(TokenKind::Op(o)) if o.as_str() == ":" => {
1423 is_sig = true;
1424 break;
1425 }
1426 _ => break,
1427 }
1428 }
1429 if is_sig {
1430 self.bump(); while self.eat(&TokenKind::Comma) {
1432 self.bump(); }
1434 self.eat_op(":");
1435 let ty_start = self.i;
1436 self.skip_to_item_end();
1437 let ty = self.parse_type_annotation(ty_start, self.i, "function");
1438 return Some(Decl::Function(FunctionDecl {
1439 name,
1440 ty,
1441 equations: Vec::new(),
1442 pos,
1443 sig_span: Some(self.node_span(start_i)),
1444 span: self.node_span(start_i),
1445 }));
1446 }
1447
1448 self.bump(); let mut params = Vec::new();
1451 while !self.at_op("=") && !self.at_op("|") {
1452 if self.at_op(":") {
1455 self.bump();
1456 let mut brackets = 0usize;
1457 while let Some(t) = self.peek() {
1458 match t {
1459 TokenKind::Op(o) if o.as_str() == "=" && brackets == 0 => break,
1460 TokenKind::VSemi
1461 | TokenKind::VRBrace
1462 | TokenKind::Semi
1463 | TokenKind::RBrace => break,
1464 TokenKind::LParen | TokenKind::LBracket => brackets += 1,
1465 TokenKind::RParen | TokenKind::RBracket => {
1466 brackets = brackets.saturating_sub(1)
1467 }
1468 _ => {}
1469 }
1470 self.i += 1;
1471 }
1472 continue;
1473 }
1474 if matches!(self.peek(), Some(TokenKind::Op(o)) if !is_reserved_op(o)) {
1477 self.skip_to_item_end();
1478 return None;
1479 }
1480 match self.peek() {
1481 None
1482 | Some(
1483 TokenKind::VSemi | TokenKind::VRBrace | TokenKind::Semi | TokenKind::RBrace,
1484 ) => {
1485 self.diag(format!("could not parse equation for '{name}'"));
1486 return None;
1487 }
1488 _ => {}
1489 }
1490 match self.pattern_atom() {
1491 Some(p) => params.push(p),
1492 None => {
1493 self.diag(format!("bad parameter pattern in '{name}'"));
1494 return None;
1495 }
1496 }
1497 }
1498 let (body, guards) = self.equation_rhs()?;
1499 let where_bindings = if self.eat_keyword("where") {
1500 self.binding_block()
1501 } else {
1502 Vec::new()
1503 };
1504 self.skip_to_item_end();
1505 Some(Decl::Function(FunctionDecl {
1506 name,
1507 ty: None,
1508 equations: vec![Equation {
1509 params,
1510 body,
1511 guards,
1512 where_bindings,
1513 pos,
1514 span: self.node_span(start_i),
1515 }],
1516 pos,
1517 sig_span: None,
1518 span: self.node_span(start_i),
1519 }))
1520 }
1521
1522 fn equation_rhs(&mut self) -> Option<(Expr, Vec<(Expr, Expr)>)> {
1524 if self.eat_op("=") {
1525 return Some((self.expr(), Vec::new()));
1526 }
1527 let mut guards = Vec::new();
1528 while self.eat_op("|") {
1529 let g = loop {
1532 let g = self.expr();
1533 if self.eat_op("<-") {
1534 let _ = self.expr(); }
1536 if !self.eat(&TokenKind::Comma) {
1537 break g;
1538 }
1539 };
1540 if !self.eat_op("=") {
1541 self.diag("expected '=' after guard");
1542 return None;
1543 }
1544 let e = self.expr();
1545 guards.push((g, e));
1546 }
1547 if guards.is_empty() {
1548 self.diag("expected '=' or guarded right-hand side in equation");
1549 None
1550 } else {
1551 let first = guards[0].1.clone();
1552 Some((first, guards))
1553 }
1554 }
1555
1556 fn binding_block(&mut self) -> Vec<Binding> {
1558 let mut bindings = Vec::new();
1559 if !(self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace)) {
1560 return bindings;
1561 }
1562 loop {
1563 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
1564 match self.peek() {
1565 None => break,
1566 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
1567 self.bump();
1568 break;
1569 }
1570 Some(TokenKind::RParen | TokenKind::RBracket) => {
1575 self.bump();
1576 continue;
1577 }
1578 _ => {}
1579 }
1580 match self.binding() {
1581 Some(b) => bindings.push(b),
1582 None => self.skip_to_item_end(),
1583 }
1584 }
1585 bindings
1586 }
1587
1588 fn binding(&mut self) -> Option<Binding> {
1591 let pos = self.pos();
1592 let start_i = self.i;
1593 if self.at(&TokenKind::LParen)
1596 && matches!(self.peek_at(1), Some(TokenKind::Op(_)))
1597 && self.peek_at(2) == Some(&TokenKind::RParen)
1598 {
1599 self.skip_to_item_end();
1600 return None;
1601 }
1602 let pat = self.pattern_atom()?;
1603 let mut params = Vec::new();
1604 loop {
1605 if self.at_op("=") {
1606 self.bump();
1607 let expr = self.expr();
1608 if self.eat_keyword("where") {
1610 let _ = self.binding_block();
1611 }
1612 return Some(Binding {
1613 pat,
1614 params,
1615 expr,
1616 pos,
1617 span: self.node_span(start_i),
1618 });
1619 }
1620 if self.at_op("|") {
1621 let (body, _) = self.equation_rhs()?;
1622 if self.eat_keyword("where") {
1623 let _ = self.binding_block();
1624 }
1625 return Some(Binding {
1626 pat,
1627 params,
1628 expr: body,
1629 pos,
1630 span: self.node_span(start_i),
1631 });
1632 }
1633 if self.at_op(":") {
1634 if params.is_empty() {
1635 self.skip_to_item_end();
1637 return None;
1638 }
1639 self.bump();
1642 let mut brackets = 0usize;
1643 while let Some(t) = self.peek() {
1644 match t {
1645 TokenKind::Op(o) if o.as_str() == "=" && brackets == 0 => break,
1646 TokenKind::VSemi
1647 | TokenKind::VRBrace
1648 | TokenKind::Semi
1649 | TokenKind::RBrace => break,
1650 TokenKind::LParen | TokenKind::LBracket => brackets += 1,
1651 TokenKind::RParen | TokenKind::RBracket => {
1652 brackets = brackets.saturating_sub(1)
1653 }
1654 _ => {}
1655 }
1656 self.i += 1;
1657 }
1658 continue;
1659 }
1660 if matches!(self.peek(), Some(TokenKind::Op(o)) if !is_reserved_op(o)) {
1663 self.skip_to_item_end();
1664 return None;
1665 }
1666 match self.peek() {
1667 None
1668 | Some(
1669 TokenKind::VSemi | TokenKind::VRBrace | TokenKind::Semi | TokenKind::RBrace,
1670 ) => return None,
1671 _ => {}
1672 }
1673 params.push(self.pattern_atom()?);
1674 }
1675 }
1676
1677 fn pattern_atom(&mut self) -> Option<Pat> {
1680 if self.depth >= MAX_RECURSION_DEPTH {
1681 return None;
1682 }
1683 self.depth += 1;
1684 let result = self.pattern_atom_inner();
1685 self.depth -= 1;
1686 result
1687 }
1688
1689 fn pattern_atom_inner(&mut self) -> Option<Pat> {
1690 let pos = self.pos();
1691 let start_i = self.i;
1692 if self.at_op("~") || self.at_op("!") {
1694 self.bump();
1695 return self.pattern_atom();
1696 }
1697 match self.peek().cloned() {
1698 Some(TokenKind::LowerId {
1699 qualifier: None,
1700 name,
1701 }) => {
1702 self.bump();
1703 if name == "_" {
1704 return Some(Pat::Wild {
1705 pos,
1706 span: self.node_span(start_i),
1707 });
1708 }
1709 if self.at_op("@") {
1710 self.bump();
1711 let inner = self.pattern_atom()?;
1712 return Some(Pat::As {
1713 name,
1714 pat: Box::new(inner),
1715 pos,
1716 span: self.node_span(start_i),
1717 });
1718 }
1719 Some(Pat::Var {
1720 name,
1721 pos,
1722 span: self.node_span(start_i),
1723 })
1724 }
1725 Some(TokenKind::Op(o)) if o.as_str() == "_" => {
1726 self.bump();
1727 Some(Pat::Wild {
1728 pos,
1729 span: self.node_span(start_i),
1730 })
1731 }
1732 Some(TokenKind::UpperId { qualifier, name }) => {
1733 self.bump();
1734 if self.at(&TokenKind::LBrace) {
1737 self.skip_balanced_braces();
1738 } else if self.eat_keyword("with") {
1739 let _ = self.record_fields();
1740 }
1741 Some(Pat::Con {
1742 qualifier,
1743 name,
1744 args: Vec::new(),
1745 pos,
1746 span: self.node_span(start_i),
1747 })
1748 }
1749 Some(TokenKind::IntLit(text)) => {
1750 self.bump();
1751 Some(Pat::Lit {
1752 kind: LitKind::Int,
1753 text,
1754 pos,
1755 span: self.node_span(start_i),
1756 })
1757 }
1758 Some(TokenKind::DecimalLit(text)) => {
1759 self.bump();
1760 Some(Pat::Lit {
1761 kind: LitKind::Decimal,
1762 text,
1763 pos,
1764 span: self.node_span(start_i),
1765 })
1766 }
1767 Some(TokenKind::StringLit(text)) => {
1768 self.bump();
1769 Some(Pat::Lit {
1770 kind: LitKind::Text,
1771 text,
1772 pos,
1773 span: self.node_span(start_i),
1774 })
1775 }
1776 Some(TokenKind::CharLit(text)) => {
1777 self.bump();
1778 Some(Pat::Lit {
1779 kind: LitKind::Char,
1780 text,
1781 pos,
1782 span: self.node_span(start_i),
1783 })
1784 }
1785 Some(TokenKind::LParen) => {
1786 self.bump();
1787 if self.eat(&TokenKind::RParen) {
1788 return Some(Pat::Con {
1789 qualifier: None,
1790 name: "()".into(),
1791 args: Vec::new(),
1792 pos,
1793 span: self.node_span(start_i),
1794 });
1795 }
1796 {
1802 let mut depth = 0usize;
1803 let mut j = self.i;
1804 let mut arrow = None;
1805 while let Some(t) = self.toks.get(j).map(|t| &t.kind) {
1806 match t {
1807 TokenKind::LParen | TokenKind::LBracket => depth += 1,
1808 TokenKind::RParen | TokenKind::RBracket => {
1809 if depth == 0 {
1810 break;
1811 }
1812 depth -= 1;
1813 }
1814 TokenKind::Op(o) if o.as_str() == ":" && depth == 0 => break,
1815 TokenKind::Op(o) if o.as_str() == "->" && depth == 0 => {
1816 arrow = Some(j);
1817 break;
1818 }
1819 TokenKind::VSemi | TokenKind::VRBrace => break,
1820 TokenKind::Op(o) if o.as_str() == "\\" => break,
1822 _ => {}
1823 }
1824 j += 1;
1825 }
1826 if let Some(j) = arrow {
1827 self.i = j + 1; let inner = self.pattern()?;
1829 self.eat(&TokenKind::RParen);
1830 return Some(inner);
1831 }
1832 }
1833 let first = self.pattern()?;
1834 if self.at_op(":") {
1836 let mut depth = 0usize;
1837 while let Some(t) = self.peek() {
1838 match t {
1839 TokenKind::LParen | TokenKind::LBracket => depth += 1,
1840 TokenKind::RParen if depth == 0 => break,
1841 TokenKind::RParen | TokenKind::RBracket => {
1842 depth = depth.saturating_sub(1)
1843 }
1844 TokenKind::VSemi | TokenKind::VRBrace => break,
1845 _ => {}
1846 }
1847 self.i += 1;
1848 }
1849 }
1850 if self.at(&TokenKind::Comma) {
1851 let mut items = vec![first];
1852 while self.eat(&TokenKind::Comma) {
1853 items.push(self.pattern()?);
1854 }
1855 self.eat(&TokenKind::RParen);
1856 return Some(Pat::Tuple {
1857 items,
1858 pos,
1859 span: self.node_span(start_i),
1860 });
1861 }
1862 self.eat(&TokenKind::RParen);
1863 Some(first)
1864 }
1865 Some(TokenKind::LBracket) => {
1866 self.bump();
1867 let mut items = Vec::new();
1868 if !self.eat(&TokenKind::RBracket) {
1869 loop {
1870 items.push(self.pattern()?);
1871 if !self.eat(&TokenKind::Comma) {
1872 break;
1873 }
1874 }
1875 self.eat(&TokenKind::RBracket);
1876 }
1877 Some(Pat::List {
1878 items,
1879 pos,
1880 span: self.node_span(start_i),
1881 })
1882 }
1883 _ => None,
1884 }
1885 }
1886
1887 fn pattern(&mut self) -> Option<Pat> {
1889 if self.depth >= MAX_RECURSION_DEPTH {
1890 return None;
1891 }
1892 self.depth += 1;
1893 let result = self.pattern_inner();
1894 self.depth -= 1;
1895 result
1896 }
1897
1898 fn pattern_inner(&mut self) -> Option<Pat> {
1899 let pos = self.pos();
1900 let start_i = self.i;
1901 let first = match self.peek().cloned() {
1902 Some(TokenKind::UpperId { qualifier, name }) => {
1903 self.bump();
1904 if self.at(&TokenKind::LBrace) || self.at_keyword("with") {
1905 if self.eat_keyword("with") {
1906 let _ = self.record_fields();
1907 } else {
1908 self.skip_balanced_braces();
1909 }
1910 Pat::Con {
1911 qualifier,
1912 name,
1913 args: Vec::new(),
1914 pos,
1915 span: self.node_span(start_i),
1916 }
1917 } else {
1918 let mut args = Vec::new();
1919 while let Some(a) = self.try_pattern_atom() {
1920 args.push(a);
1921 }
1922 Pat::Con {
1923 qualifier,
1924 name,
1925 args,
1926 pos,
1927 span: self.node_span(start_i),
1928 }
1929 }
1930 }
1931 _ => self.pattern_atom()?,
1932 };
1933 if self.at_op("::") {
1934 self.bump();
1935 let rest = self.pattern()?;
1936 return Some(Pat::Con {
1937 qualifier: None,
1938 name: "::".into(),
1939 args: vec![first, rest],
1940 pos,
1941 span: self.node_span(start_i),
1942 });
1943 }
1944 Some(first)
1945 }
1946
1947 fn try_pattern_atom(&mut self) -> Option<Pat> {
1948 match self.peek() {
1949 Some(
1950 TokenKind::LowerId {
1951 qualifier: None, ..
1952 }
1953 | TokenKind::UpperId { .. }
1954 | TokenKind::IntLit(_)
1955 | TokenKind::DecimalLit(_)
1956 | TokenKind::StringLit(_)
1957 | TokenKind::CharLit(_)
1958 | TokenKind::LParen
1959 | TokenKind::LBracket,
1960 ) => self.pattern_atom(),
1961 _ => None,
1962 }
1963 }
1964
1965 fn skip_balanced_braces(&mut self) {
1966 let mut depth = 0usize;
1967 while let Some(t) = self.peek() {
1968 match t {
1969 TokenKind::LBrace => depth += 1,
1970 TokenKind::RBrace => {
1971 if depth == 0 {
1972 return;
1973 }
1974 depth -= 1;
1975 if depth == 0 {
1976 self.i += 1;
1977 return;
1978 }
1979 }
1980 _ => {}
1981 }
1982 self.i += 1;
1983 }
1984 }
1985
1986 fn expr(&mut self) -> Expr {
1989 self.expr_prec(0, DoExpressionMode::Allow)
1990 }
1991
1992 fn expr_no_do(&mut self) -> Expr {
1993 self.expr_prec(0, DoExpressionMode::Disallow)
1994 }
1995
1996 fn expr_comma_list(&mut self) -> Vec<Expr> {
1998 let mut out = vec![self.expr()];
1999 while self.eat(&TokenKind::Comma) {
2000 out.push(self.expr());
2001 }
2002 out
2003 }
2004
2005 fn expr_comma_list_no_do(&mut self) -> Vec<Expr> {
2006 let mut out = vec![self.expr_no_do()];
2007 while self.eat(&TokenKind::Comma) {
2008 out.push(self.expr_no_do());
2009 }
2010 out
2011 }
2012
2013 fn expr_prec(&mut self, min_prec: u8, do_mode: DoExpressionMode) -> Expr {
2014 let pos = self.pos();
2015 let start_i = self.i;
2016 if self.depth >= MAX_RECURSION_DEPTH {
2017 self.diag_cat(
2022 DiagnosticCategory::RecursionLimit,
2023 "expression nesting too deep; truncated to raw text",
2024 );
2025 let start = self.i;
2026 self.skip_to_item_end();
2027 if self.i == start {
2028 self.bump();
2029 }
2030 return Expr::Error {
2031 raw: self.slice_text(start),
2032 span: self.node_span(start),
2033 pos,
2034 };
2035 }
2036 self.depth += 1;
2037 let result = self.expr_prec_inner(min_prec, do_mode, pos, start_i);
2038 self.depth -= 1;
2039 result
2040 }
2041
2042 fn expr_prec_inner(
2043 &mut self,
2044 min_prec: u8,
2045 do_mode: DoExpressionMode,
2046 pos: Pos,
2047 start_i: usize,
2048 ) -> Expr {
2049 let mut lhs = match self.unary(do_mode) {
2050 Some(e) => e,
2051 None => {
2052 let start = self.i;
2054 self.skip_to_item_end();
2055 if self.i == start {
2056 self.bump();
2057 }
2058 return Expr::Error {
2059 raw: self.slice_text(start),
2060 span: self.node_span(start),
2061 pos,
2062 };
2063 }
2064 };
2065 loop {
2066 let (op, prec, right_assoc) = match self.peek() {
2067 Some(TokenKind::Op(o)) => {
2068 let o = o.clone();
2069 if is_reserved_op(&o) {
2070 if o == ":" {
2072 self.bump();
2073 self.skip_type_tokens();
2074 continue;
2075 }
2076 break;
2077 }
2078 let (p, r) = fixity(&o);
2079 (o, p, r)
2080 }
2081 Some(TokenKind::Backtick) => {
2082 let name = match self.peek_at(1) {
2084 Some(
2085 TokenKind::LowerId { qualifier, name }
2086 | TokenKind::UpperId { qualifier, name },
2087 ) => qualifier
2088 .as_ref()
2089 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}")),
2090 _ => break,
2091 };
2092 if self.peek_at(2) != Some(&TokenKind::Backtick) {
2093 break;
2094 }
2095 (format!("`{name}`").into(), 9, false)
2096 }
2097 _ => break,
2098 };
2099 if prec < min_prec {
2100 break;
2101 }
2102 self.bump();
2103 if op.starts_with('`') {
2104 self.bump();
2105 self.bump();
2106 }
2107 let next_min = if right_assoc { prec } else { prec + 1 };
2108 let rhs = self.expr_prec(next_min, do_mode);
2109 lhs = Expr::BinOp {
2110 op,
2111 lhs: Box::new(lhs),
2112 rhs: Box::new(rhs),
2113 pos,
2114 span: self.node_span(start_i),
2115 };
2116 }
2117 lhs
2118 }
2119
2120 fn unary(&mut self, do_mode: DoExpressionMode) -> Option<Expr> {
2121 let pos = self.pos();
2122 let start_i = self.i;
2123 if self.at_op("-") {
2124 self.bump();
2125 let e = self.unary(do_mode)?;
2126 return Some(Expr::Neg {
2127 expr: Box::new(e),
2128 pos,
2129 span: self.node_span(start_i),
2130 });
2131 }
2132 self.application(do_mode)
2133 }
2134
2135 fn application(&mut self, do_mode: DoExpressionMode) -> Option<Expr> {
2136 let pos = self.pos();
2137 let start_i = self.i;
2138 let head0 = self.atom(do_mode)?;
2139 let mut head = self.projection_tail(head0);
2140 let mut args = Vec::new();
2141 loop {
2142 if self.at_keyword("with") {
2145 let target = args.pop().unwrap_or_else(|| {
2146 std::mem::replace(
2147 &mut head,
2148 Expr::Error {
2149 raw: String::new(),
2150 pos,
2151 span: Span::default(),
2152 },
2153 )
2154 });
2155 self.bump(); let fields = self.record_fields();
2157 let tpos = target.pos();
2158 let sp = Span::new(target.span().start, self.end_byte());
2159 let rec = Expr::Record {
2160 base: Box::new(target),
2161 fields,
2162 pos: tpos,
2163 span: sp,
2164 };
2165 if matches!(head, Expr::Error { ref raw, .. } if raw.is_empty()) {
2166 head = rec;
2167 } else {
2168 args.push(rec);
2169 }
2170 continue;
2171 }
2172 if !do_mode.allows_do() && self.at_keyword("do") {
2173 break;
2174 }
2175 if self.at_op("@") {
2177 self.bump();
2178 match self.peek() {
2179 Some(TokenKind::UpperId { .. } | TokenKind::LowerId { .. }) => {
2180 self.bump();
2181 }
2182 Some(TokenKind::LParen) => self.skip_balanced_parens(),
2183 Some(TokenKind::LBracket) => {
2184 let mut depth = 0usize;
2185 while let Some(t) = self.peek() {
2186 match t {
2187 TokenKind::LBracket => depth += 1,
2188 TokenKind::RBracket => {
2189 if depth == 0 {
2190 break;
2191 }
2192 depth -= 1;
2193 if depth == 0 {
2194 self.i += 1;
2195 break;
2196 }
2197 }
2198 _ => {}
2199 }
2200 self.i += 1;
2201 }
2202 }
2203 _ => {}
2204 }
2205 continue;
2206 }
2207 match self.try_atom(do_mode) {
2208 Some(a) => args.push(self.projection_tail(a)),
2209 None => break,
2210 }
2211 }
2212 if args.is_empty() {
2213 Some(head)
2214 } else {
2215 Some(Expr::App {
2216 func: Box::new(head),
2217 args,
2218 pos,
2219 span: self.node_span(start_i),
2220 })
2221 }
2222 }
2223
2224 fn record_fields(&mut self) -> Vec<FieldAssign> {
2226 let mut fields = Vec::new();
2227 let explicit = self.at(&TokenKind::LBrace);
2228 if !(self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace)) {
2229 return fields;
2230 }
2231 loop {
2232 while self.eat(&TokenKind::VSemi)
2233 || self.eat(&TokenKind::Semi)
2234 || self.eat(&TokenKind::Comma)
2235 {}
2236 match self.peek() {
2237 None => break,
2238 Some(TokenKind::VRBrace) if !explicit => {
2239 self.bump();
2240 break;
2241 }
2242 Some(TokenKind::RBrace) => {
2243 self.bump();
2244 break;
2245 }
2246 Some(TokenKind::RParen | TokenKind::RBracket) => {
2248 self.bump();
2249 continue;
2250 }
2251 _ => {}
2252 }
2253 let pos = self.pos();
2254 let start_i = self.i;
2255 if self.at_op("..") {
2256 self.bump();
2257 fields.push(FieldAssign {
2258 name: "..".into(),
2259 value: None,
2260 pos,
2261 span: self.node_span(start_i),
2262 });
2263 continue;
2264 }
2265 let name = match self.peek().cloned() {
2266 Some(TokenKind::LowerId {
2267 qualifier: None,
2268 name,
2269 }) => {
2270 self.bump();
2271 name
2272 }
2273 _ => {
2274 self.skip_to_item_end();
2275 continue;
2276 }
2277 };
2278 if self.eat_op("=") {
2279 let value = self.expr_prec(1, DoExpressionMode::Allow);
2280 fields.push(FieldAssign {
2281 name,
2282 value: Some(value),
2283 pos,
2284 span: self.node_span(start_i),
2285 });
2286 } else {
2287 fields.push(FieldAssign {
2289 name,
2290 value: None,
2291 pos,
2292 span: self.node_span(start_i),
2293 });
2294 }
2295 }
2296 fields
2297 }
2298
2299 fn try_atom(&mut self, do_mode: DoExpressionMode) -> Option<Expr> {
2300 match self.peek() {
2301 Some(TokenKind::LowerId { .. }) => {
2302 let kw = self.peek().and_then(|t| t.keyword());
2303 match kw {
2304 Some("do") if do_mode.allows_do() => self.atom(do_mode),
2306 Some(
2309 "if" | "case" | "do" | "let" | "try" | "where" | "then" | "else" | "of"
2310 | "in" | "controller" | "with" | "catch",
2311 ) => None,
2312 _ => self.atom(do_mode),
2313 }
2314 }
2315 Some(
2316 TokenKind::UpperId { .. }
2317 | TokenKind::IntLit(_)
2318 | TokenKind::DecimalLit(_)
2319 | TokenKind::StringLit(_)
2320 | TokenKind::CharLit(_)
2321 | TokenKind::LParen
2322 | TokenKind::LBracket,
2323 ) => self.atom(do_mode),
2324 Some(TokenKind::Op(o)) if o.as_str() == "\\" => self.atom(do_mode),
2326 _ => None,
2327 }
2328 }
2329
2330 fn projection_tail(&mut self, mut base: Expr) -> Expr {
2337 while self.at_tight_projection() {
2338 let start = base.span().start;
2339 let pos = base.pos();
2340 self.bump(); let Some(field_tok) = self.bump() else {
2342 self.diag("expected projection field after '.'");
2343 return base;
2344 };
2345 let TokenKind::LowerId { qualifier, name } = field_tok.kind else {
2346 self.diag("expected projection field after '.'");
2347 return base;
2348 };
2349 let field = Expr::Var {
2350 qualifier,
2351 name,
2352 pos: field_tok.pos,
2353 span: Span::new(field_tok.start, field_tok.end),
2354 };
2355 base = Expr::BinOp {
2356 op: ".".into(),
2357 lhs: Box::new(base),
2358 rhs: Box::new(field),
2359 pos,
2360 span: Span::new(start, self.end_byte()),
2361 };
2362 }
2363 base
2364 }
2365
2366 fn at_tight_projection(&self) -> bool {
2370 if self.i == 0 {
2371 return false;
2372 }
2373 let dot = match self.toks.get(self.i) {
2374 Some(t) => t,
2375 None => return false,
2376 };
2377 if !matches!(&dot.kind, TokenKind::Op(o) if o.as_str() == ".") {
2378 return false;
2379 }
2380 let prev = &self.toks[self.i - 1];
2384 if prev.is_virtual() || prev.end != dot.start {
2385 return false;
2386 }
2387 self.toks.get(self.i + 1).is_some_and(|t| {
2389 matches!(
2390 &t.kind,
2391 TokenKind::LowerId {
2392 qualifier: None,
2393 ..
2394 }
2395 ) && t.start == dot.end
2396 })
2397 }
2398
2399 fn atom(&mut self, do_mode: DoExpressionMode) -> Option<Expr> {
2400 let pos = self.pos();
2401 let start_i = self.i;
2402 match self.peek().cloned() {
2403 Some(TokenKind::LowerId { qualifier, name }) => {
2404 match name.as_str() {
2405 "if" if qualifier.is_none() => return self.if_expr(),
2406 "case" if qualifier.is_none() => return self.case_expr(),
2407 "do" if qualifier.is_none() => {
2408 if !do_mode.allows_do() {
2409 return None;
2410 }
2411 return self.do_expr();
2412 }
2413 "let" if qualifier.is_none() => return self.let_expr(),
2414 "try" if qualifier.is_none() => return self.try_expr(),
2415 _ => {}
2416 }
2417 self.bump();
2418 Some(Expr::Var {
2419 qualifier,
2420 name,
2421 pos,
2422 span: self.node_span(start_i),
2423 })
2424 }
2425 Some(TokenKind::UpperId { qualifier, name }) => {
2426 self.bump();
2427 let base = Expr::Con {
2428 qualifier,
2429 name,
2430 pos,
2431 span: self.node_span(start_i),
2432 };
2433 if self.at(&TokenKind::LBrace) {
2435 let fields = self.record_fields();
2436 return Some(Expr::Record {
2437 base: Box::new(base),
2438 fields,
2439 pos,
2440 span: self.node_span(start_i),
2441 });
2442 }
2443 Some(base)
2444 }
2445 Some(TokenKind::IntLit(text)) => {
2446 self.bump();
2447 Some(Expr::Lit {
2448 kind: LitKind::Int,
2449 text,
2450 pos,
2451 span: self.node_span(start_i),
2452 })
2453 }
2454 Some(TokenKind::DecimalLit(text)) => {
2455 self.bump();
2456 Some(Expr::Lit {
2457 kind: LitKind::Decimal,
2458 text,
2459 pos,
2460 span: self.node_span(start_i),
2461 })
2462 }
2463 Some(TokenKind::StringLit(text)) => {
2464 self.bump();
2465 Some(Expr::Lit {
2466 kind: LitKind::Text,
2467 text,
2468 pos,
2469 span: self.node_span(start_i),
2470 })
2471 }
2472 Some(TokenKind::CharLit(text)) => {
2473 self.bump();
2474 Some(Expr::Lit {
2475 kind: LitKind::Char,
2476 text,
2477 pos,
2478 span: self.node_span(start_i),
2479 })
2480 }
2481 Some(TokenKind::Op(o)) if o.as_str() == "\\" => self.lambda_expr(),
2482 Some(TokenKind::LParen) => self.paren_expr(),
2483 Some(TokenKind::LBracket) => self.list_expr(),
2484 _ => None,
2485 }
2486 }
2487
2488 fn if_expr(&mut self) -> Option<Expr> {
2489 let pos = self.pos();
2490 let start_i = self.i;
2491 self.bump(); let cond = self.expr();
2493 self.eat(&TokenKind::VSemi); if !self.eat_keyword("then") {
2495 self.diag("expected 'then'");
2496 return Some(Expr::Error {
2497 raw: format!("if {}", cond.render()),
2498 pos,
2499 span: self.node_span(start_i),
2500 });
2501 }
2502 let then_branch = self.expr();
2503 self.eat(&TokenKind::VSemi);
2504 if !self.eat_keyword("else") {
2505 self.diag("expected 'else'");
2506 return Some(Expr::Error {
2507 raw: format!("if {} then {}", cond.render(), then_branch.render()),
2508 pos,
2509 span: self.node_span(start_i),
2510 });
2511 }
2512 let else_branch = self.expr();
2513 Some(Expr::If {
2514 cond: Box::new(cond),
2515 then_branch: Box::new(then_branch),
2516 else_branch: Box::new(else_branch),
2517 pos,
2518 span: self.node_span(start_i),
2519 })
2520 }
2521
2522 fn case_expr(&mut self) -> Option<Expr> {
2523 let pos = self.pos();
2524 let start_i = self.i;
2525 self.bump(); let scrutinee = self.expr_no_do();
2527 if !self.eat_keyword("of") {
2528 self.diag("expected 'of' in case expression");
2529 return Some(Expr::Error {
2530 raw: format!("case {}", scrutinee.render()),
2531 pos,
2532 span: self.node_span(start_i),
2533 });
2534 }
2535 let mut alts = Vec::new();
2536 if self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace) {
2537 loop {
2538 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
2539 match self.peek() {
2540 None => break,
2541 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
2542 self.bump();
2543 break;
2544 }
2545 Some(TokenKind::RParen | TokenKind::RBracket) => {
2547 self.bump();
2548 continue;
2549 }
2550 _ => {}
2551 }
2552 if self.eat_keyword("where") {
2554 let _ = self.binding_block();
2555 continue;
2556 }
2557 match self.case_alt() {
2558 Some(a) => alts.push(a),
2559 None => self.skip_to_item_end(),
2560 }
2561 }
2562 }
2563 Some(Expr::Case {
2564 scrutinee: Box::new(scrutinee),
2565 alts,
2566 pos,
2567 span: self.node_span(start_i),
2568 })
2569 }
2570
2571 fn case_alt(&mut self) -> Option<Alt> {
2572 let pos = self.pos();
2573 let start_i = self.i;
2574 let pat = self.pattern()?;
2575 if self.at_op("|") {
2576 let mut first: Option<Expr> = None;
2580 while self.eat_op("|") {
2581 loop {
2582 let _guard = self.expr();
2583 if self.eat_op("<-") {
2584 let _ = self.expr();
2585 }
2586 if !self.eat(&TokenKind::Comma) {
2587 break;
2588 }
2589 }
2590 if !self.eat_op("->") {
2591 self.diag("expected '->' in guarded case alternative");
2592 return None;
2593 }
2594 let body = self.expr();
2595 if first.is_none() {
2596 first = Some(body);
2597 }
2598 }
2599 return Some(Alt {
2600 pat,
2601 body: first?,
2602 pos,
2603 span: self.node_span(start_i),
2604 });
2605 }
2606 if !self.eat_op("->") {
2607 self.diag("expected '->' in case alternative");
2608 return None;
2609 }
2610 let body = self.expr();
2611 Some(Alt {
2612 pat,
2613 body,
2614 pos,
2615 span: self.node_span(start_i),
2616 })
2617 }
2618
2619 fn do_expr(&mut self) -> Option<Expr> {
2620 let pos = self.pos();
2621 let start_i = self.i;
2622 self.bump(); let mut stmts = Vec::new();
2624 if self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace) {
2625 loop {
2626 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
2627 match self.peek() {
2628 None => break,
2629 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
2630 self.bump();
2631 break;
2632 }
2633 Some(TokenKind::RParen | TokenKind::RBracket) => {
2635 self.bump();
2636 continue;
2637 }
2638 _ => {}
2639 }
2640 stmts.push(self.do_stmt());
2641 }
2642 }
2643 Some(Expr::Do {
2644 stmts,
2645 pos,
2646 span: self.node_span(start_i),
2647 })
2648 }
2649
2650 fn do_stmt(&mut self) -> DoStmt {
2651 let pos = self.pos();
2652 let start_i = self.i;
2653 if self.at_keyword("let") {
2654 self.bump();
2655 let bindings = self.binding_block();
2656 if self.eat_keyword("in") {
2658 let body = self.expr();
2659 return DoStmt::Expr {
2660 expr: Expr::LetIn {
2661 bindings,
2662 body: Box::new(body),
2663 pos,
2664 span: self.node_span(start_i),
2665 },
2666 pos,
2667 span: self.node_span(start_i),
2668 };
2669 }
2670 return DoStmt::Let {
2671 bindings,
2672 pos,
2673 span: self.node_span(start_i),
2674 };
2675 }
2676 let snapshot = self.i;
2678 if let Some(pat) = self.try_bind_pattern() {
2679 if self.at_op("<-") {
2680 self.bump();
2681 let expr = self.expr();
2682 return DoStmt::Bind {
2683 pat,
2684 expr,
2685 pos,
2686 span: self.node_span(start_i),
2687 };
2688 }
2689 }
2690 self.i = snapshot;
2691 let expr = self.expr();
2692 DoStmt::Expr {
2693 expr,
2694 pos,
2695 span: self.node_span(start_i),
2696 }
2697 }
2698
2699 fn try_bind_pattern(&mut self) -> Option<Pat> {
2702 self.pattern()
2703 }
2704
2705 fn let_expr(&mut self) -> Option<Expr> {
2706 let pos = self.pos();
2707 let start_i = self.i;
2708 self.bump(); let bindings = self.binding_block();
2710 if self.eat_keyword("in") {
2711 let body = self.expr();
2712 return Some(Expr::LetIn {
2713 bindings,
2714 body: Box::new(body),
2715 pos,
2716 span: self.node_span(start_i),
2717 });
2718 }
2719 Some(Expr::LetIn {
2721 bindings,
2722 body: Box::new(Expr::Error {
2723 raw: String::new(),
2724 pos,
2725 span: self.node_span(start_i),
2726 }),
2727 pos,
2728 span: self.node_span(start_i),
2729 })
2730 }
2731
2732 fn try_expr(&mut self) -> Option<Expr> {
2733 let pos = self.pos();
2734 let start_i = self.i;
2735 self.bump(); let body = self.expr();
2737 let mut handlers = Vec::new();
2738 self.eat(&TokenKind::VSemi);
2739 if self.eat_keyword("catch") {
2740 if self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace) {
2741 loop {
2742 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
2743 match self.peek() {
2744 None => break,
2745 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
2746 self.bump();
2747 break;
2748 }
2749 Some(TokenKind::RParen | TokenKind::RBracket) => {
2751 self.bump();
2752 continue;
2753 }
2754 _ => {}
2755 }
2756 match self.case_alt() {
2757 Some(a) => handlers.push(a),
2758 None => self.skip_to_item_end(),
2759 }
2760 }
2761 } else if let Some(a) = self.case_alt() {
2762 handlers.push(a);
2764 }
2765 }
2766 Some(Expr::Try {
2767 body: Box::new(body),
2768 handlers,
2769 pos,
2770 span: self.node_span(start_i),
2771 })
2772 }
2773
2774 fn lambda_expr(&mut self) -> Option<Expr> {
2775 let pos = self.pos();
2776 let start_i = self.i;
2777 self.bump(); if self.eat_keyword("case") {
2780 let mut alts = Vec::new();
2781 if self.eat(&TokenKind::VLBrace) || self.eat(&TokenKind::LBrace) {
2782 loop {
2783 while self.eat(&TokenKind::VSemi) || self.eat(&TokenKind::Semi) {}
2784 match self.peek() {
2785 None => break,
2786 Some(TokenKind::VRBrace | TokenKind::RBrace) => {
2787 self.bump();
2788 break;
2789 }
2790 Some(TokenKind::RParen | TokenKind::RBracket) => {
2791 self.bump();
2792 continue;
2793 }
2794 _ => {}
2795 }
2796 match self.case_alt() {
2797 Some(a) => alts.push(a),
2798 None => self.skip_to_item_end(),
2799 }
2800 }
2801 }
2802 return Some(Expr::Lambda {
2803 params: vec![Pat::Var {
2804 name: "_".into(),
2805 pos,
2806 span: Span::new(self.byte_at(start_i), self.byte_at(start_i)),
2807 }],
2808 body: Box::new(Expr::Case {
2809 scrutinee: Box::new(Expr::Var {
2810 qualifier: None,
2811 name: "_".into(),
2812 pos,
2813 span: Span::new(self.byte_at(start_i), self.byte_at(start_i)),
2814 }),
2815 alts,
2816 pos,
2817 span: self.node_span(start_i),
2818 }),
2819 pos,
2820 span: self.node_span(start_i),
2821 });
2822 }
2823 let mut params = Vec::new();
2824 while !self.at_op("->") {
2825 match self.pattern_atom() {
2826 Some(p) => params.push(p),
2827 None => {
2828 self.diag("bad lambda parameter");
2829 let start = self.i;
2830 self.skip_to_item_end();
2831 return Some(Expr::Error {
2832 raw: format!("\\{}", self.slice_text(start)),
2833 pos,
2834 span: self.node_span(start_i),
2835 });
2836 }
2837 }
2838 }
2839 self.bump(); let body = self.expr();
2841 Some(Expr::Lambda {
2842 params,
2843 body: Box::new(body),
2844 pos,
2845 span: self.node_span(start_i),
2846 })
2847 }
2848
2849 fn paren_expr(&mut self) -> Option<Expr> {
2850 let pos = self.pos();
2851 let start_i = self.i;
2852 self.bump(); if self.eat(&TokenKind::RParen) {
2854 return Some(Expr::Con {
2855 qualifier: None,
2856 name: "()".into(),
2857 pos,
2858 span: self.node_span(start_i),
2859 });
2860 }
2861 if let Some(TokenKind::Op(o)) = self.peek().cloned() {
2863 if !is_reserved_op(&o) && o != "\\" && o != "-" {
2864 self.bump();
2865 if self.eat(&TokenKind::RParen) {
2866 return Some(Expr::Section {
2867 op: o,
2868 operand: None,
2869 side: SectionSide::Right,
2870 pos,
2871 span: self.node_span(start_i),
2872 });
2873 }
2874 let operand = self.expr();
2875 self.eat(&TokenKind::RParen);
2876 return Some(Expr::Section {
2877 op: o,
2878 operand: Some(Box::new(operand)),
2879 side: SectionSide::Right,
2880 pos,
2881 span: self.node_span(start_i),
2882 });
2883 }
2884 }
2885 let first = self.expr();
2886 if self.at(&TokenKind::Comma) {
2887 let mut items = vec![first];
2888 while self.eat(&TokenKind::Comma) {
2889 items.push(self.expr());
2890 }
2891 self.eat(&TokenKind::RParen);
2892 return Some(Expr::Tuple {
2893 items,
2894 pos,
2895 span: self.node_span(start_i),
2896 });
2897 }
2898 if let Some(TokenKind::Op(o)) = self.peek().cloned() {
2900 if !is_reserved_op(&o) && self.peek_at(1) == Some(&TokenKind::RParen) {
2901 self.bump();
2902 self.bump();
2903 return Some(Expr::Section {
2904 op: o,
2905 operand: Some(Box::new(first)),
2906 side: SectionSide::Left,
2907 pos,
2908 span: self.node_span(start_i),
2909 });
2910 }
2911 }
2912 self.eat(&TokenKind::RParen);
2913 Some(first)
2914 }
2915
2916 fn list_expr(&mut self) -> Option<Expr> {
2917 let pos = self.pos();
2918 let start_i = self.i;
2919 self.bump(); let mut items = Vec::new();
2921 if self.eat(&TokenKind::RBracket) {
2922 return Some(Expr::List {
2923 items,
2924 pos,
2925 span: self.node_span(start_i),
2926 });
2927 }
2928 loop {
2929 let e = self.expr();
2930 if self.at_op("..") {
2932 self.bump();
2933 let hi = if self.at(&TokenKind::RBracket) {
2934 Expr::Error {
2935 raw: String::new(),
2936 pos,
2937 span: self.node_span(start_i),
2938 }
2939 } else {
2940 self.expr()
2941 };
2942 self.eat(&TokenKind::RBracket);
2943 return Some(Expr::BinOp {
2944 op: "..".into(),
2945 lhs: Box::new(e),
2946 rhs: Box::new(hi),
2947 pos,
2948 span: self.node_span(start_i),
2949 });
2950 }
2951 if self.at_op("|") {
2953 let start = self.i;
2954 let mut brackets = 1usize;
2955 while let Some(t) = self.peek() {
2956 match t {
2957 TokenKind::LBracket => brackets += 1,
2958 TokenKind::RBracket => {
2959 brackets -= 1;
2960 if brackets == 0 {
2961 break;
2962 }
2963 }
2964 TokenKind::VSemi | TokenKind::VRBrace => break,
2965 _ => {}
2966 }
2967 self.i += 1;
2968 }
2969 let raw = self.slice_text(start);
2970 self.eat(&TokenKind::RBracket);
2971 return Some(Expr::App {
2972 func: Box::new(e),
2973 args: vec![Expr::Error {
2974 raw,
2975 pos,
2976 span: self.node_span(start_i),
2977 }],
2978 pos,
2979 span: self.node_span(start_i),
2980 });
2981 }
2982 items.push(e);
2983 if !self.eat(&TokenKind::Comma) {
2984 break;
2985 }
2986 }
2987 self.eat(&TokenKind::RBracket);
2988 Some(Expr::List {
2989 items,
2990 pos,
2991 span: self.node_span(start_i),
2992 })
2993 }
2994}
2995
2996fn is_reserved_op(op: &str) -> bool {
2999 matches!(op, "=" | "<-" | "->" | "|" | ":" | "=>" | "@" | "\\" | "..")
3000}
3001
3002fn fixity(op: &str) -> (u8, bool) {
3005 match op {
3006 "$" | "$!" => (1, true),
3007 ">>=" | ">>" | "=<<" | "<&>" => (2, false),
3008 "||" => (3, true),
3009 "&&" => (4, true),
3010 "==" | "/=" | "<" | "<=" | ">" | ">=" => (5, false),
3011 "::" | "++" | "<>" => (6, true),
3012 "+" | "-" => (7, false),
3013 "*" | "/" => (8, false),
3014 "^" | "**" => (9, true),
3015 "." | "!!" => (10, true),
3016 _ => (9, false),
3017 }
3018}
3019
3020fn equations_extent(eqs: &[Equation]) -> Option<Span> {
3025 let mut it = eqs.iter();
3026 let first = it.next()?;
3027 let mut s = first.span;
3028 for e in it {
3029 s.start = s.start.min(e.span.start);
3030 s.end = s.end.max(e.span.end);
3031 }
3032 Some(s)
3033}
3034
3035fn merge_functions(decls: &mut Vec<Decl>) {
3036 let mut out: Vec<Decl> = Vec::with_capacity(decls.len());
3037 let mut function_index_by_name: HashMap<Identifier, usize> = HashMap::new();
3038 for decl in decls.drain(..) {
3039 match decl {
3040 Decl::Function(f) => {
3041 if let Some(existing_index) = function_index_by_name.get(&f.name).copied() {
3042 let Decl::Function(g) = &mut out[existing_index] else {
3043 out.push(Decl::Function(f));
3044 continue;
3045 };
3046 if g.ty.is_none() {
3047 g.ty = f.ty.clone();
3048 }
3049 if g.sig_span.is_none() {
3050 g.sig_span = f.sig_span;
3051 }
3052 if g.equations.is_empty() && !f.equations.is_empty() {
3055 g.pos = f.pos;
3056 }
3057 g.equations.extend(f.equations);
3058 g.span = equations_extent(&g.equations)
3065 .or(g.sig_span)
3066 .unwrap_or(g.span);
3067 } else {
3068 function_index_by_name.insert(f.name.clone(), out.len());
3069 out.push(Decl::Function(f));
3070 }
3071 }
3072 other => out.push(other),
3073 }
3074 }
3075 *decls = out;
3076}
3077
3078pub(crate) fn parse_type_from_tokens(tokens: &[Token]) -> Option<Type> {
3087 let real_tokens: Vec<&Token> = tokens.iter().filter(|t| !t.is_virtual()).collect();
3090 if real_tokens.is_empty() {
3091 return None;
3092 }
3093 let mut parser = TypeTokenParser {
3094 tokens: &real_tokens,
3095 cursor: 0,
3096 };
3097 let ty = parser.parse_type()?;
3098 if parser.cursor == real_tokens.len() {
3101 Some(ty)
3102 } else {
3103 None
3104 }
3105}
3106
3107struct TypeTokenParser<'a> {
3108 tokens: &'a [&'a Token],
3109 cursor: usize,
3110}
3111
3112#[derive(Debug)]
3113struct FieldBlock {
3114 fields: Vec<FieldDecl>,
3115 dangling: bool,
3116}
3117
3118enum TypeAtom {
3121 ParsedType(Type),
3122 DroppedLiteral(Span),
3123}
3124
3125impl<'a> TypeTokenParser<'a> {
3126 fn peek(&self) -> Option<&'a Token> {
3127 self.tokens.get(self.cursor).copied()
3128 }
3129
3130 fn eat_op(&mut self, op: &str) -> bool {
3131 if self.peek().is_some_and(|t| t.kind.is_op(op)) {
3132 self.cursor += 1;
3133 true
3134 } else {
3135 false
3136 }
3137 }
3138
3139 fn parse_type(&mut self) -> Option<Type> {
3142 let lhs = if self.eat_keyword("forall") {
3143 self.parse_forall_type()?
3144 } else {
3145 self.parse_application_type()?
3146 };
3147 if self.eat_op("=>") {
3148 let body = self.parse_type()?;
3150 let span = Span::new(lhs.span().start, body.span().end);
3151 return Some(Type::Constrained(Box::new(body), span));
3152 }
3153 if self.eat_op("->") {
3154 let rhs = self.parse_type()?;
3155 let span = Span::new(lhs.span().start, rhs.span().end);
3156 return Some(Type::Fun(Box::new(lhs), Box::new(rhs), span));
3157 }
3158 Some(lhs)
3159 }
3160
3161 fn parse_application_type(&mut self) -> Option<Type> {
3163 let head = match self.parse_atom()? {
3164 TypeAtom::ParsedType(t) => t,
3165 TypeAtom::DroppedLiteral(_) => return None,
3167 };
3168 let mut args = Vec::new();
3169 let start = head.span().start;
3170 let mut end = head.span().end;
3171 loop {
3172 if !self.is_at_type_atom_start() {
3175 break;
3176 }
3177 match self.parse_atom()? {
3178 TypeAtom::ParsedType(t) => {
3179 end = t.span().end;
3180 args.push(t);
3181 }
3182 TypeAtom::DroppedLiteral(span) => {
3183 end = span.end;
3186 }
3187 }
3188 }
3189 let span = Span::new(start, end);
3190 if args.is_empty() {
3191 Some(head.with_span(span))
3192 } else {
3193 Some(Type::App(Box::new(head), args, span))
3194 }
3195 }
3196
3197 fn is_at_type_atom_start(&self) -> bool {
3200 matches!(
3201 self.peek().map(|t| &t.kind),
3202 Some(
3203 TokenKind::UpperId { .. }
3204 | TokenKind::LowerId { .. }
3205 | TokenKind::IntLit(_)
3206 | TokenKind::DecimalLit(_)
3207 | TokenKind::StringLit(_)
3208 | TokenKind::CharLit(_)
3209 | TokenKind::LBracket
3210 | TokenKind::LParen
3211 )
3212 )
3213 }
3214
3215 fn parse_atom(&mut self) -> Option<TypeAtom> {
3216 let tok = self.peek()?;
3217 match &tok.kind {
3218 TokenKind::UpperId { qualifier, name } => {
3219 let con = Type::Con {
3220 qualifier: qualifier.clone(),
3221 name: name.clone(),
3222 span: Span::new(tok.start, tok.end),
3223 };
3224 self.cursor += 1;
3225 Some(TypeAtom::ParsedType(con))
3226 }
3227 TokenKind::LowerId { name, .. } => {
3228 let var = Type::Var(name.clone(), Span::new(tok.start, tok.end));
3231 self.cursor += 1;
3232 Some(TypeAtom::ParsedType(var))
3233 }
3234 TokenKind::IntLit(_) | TokenKind::DecimalLit(_) => {
3235 self.cursor += 1;
3237 Some(TypeAtom::DroppedLiteral(Span::new(tok.start, tok.end)))
3238 }
3239 TokenKind::StringLit(text) => {
3240 self.cursor += 1;
3241 Some(TypeAtom::ParsedType(Type::Lit {
3242 kind: LitKind::Text,
3243 text: text.clone(),
3244 span: Span::new(tok.start, tok.end),
3245 }))
3246 }
3247 TokenKind::CharLit(text) => {
3248 self.cursor += 1;
3249 Some(TypeAtom::ParsedType(Type::Lit {
3250 kind: LitKind::Char,
3251 text: text.clone(),
3252 span: Span::new(tok.start, tok.end),
3253 }))
3254 }
3255 TokenKind::LBracket => {
3256 let start = tok.start;
3257 self.cursor += 1;
3258 let inner = self.parse_type()?;
3259 self.eat_token(&TokenKind::RBracket).map(|end| {
3260 TypeAtom::ParsedType(Type::List(Box::new(inner), Span::new(start, end.end)))
3261 })
3262 }
3263 TokenKind::LParen => {
3264 let start = tok.start;
3265 self.cursor += 1;
3266 if let Some(op) = self.eat_token_if_operator() {
3267 let mut name = op.as_str().to_string();
3268 while matches!(
3269 self.tokens.get(self.cursor).map(|t| &t.kind),
3270 Some(TokenKind::Op(_))
3271 ) {
3272 self.cursor += 1;
3273 if let TokenKind::Op(o) = &self.tokens[self.cursor - 1].kind {
3274 name.push_str(o.as_str());
3275 }
3276 }
3277 if self.eat_token(&TokenKind::RParen).is_some()
3278 && self
3279 .tokens
3280 .get(self.cursor)
3281 .is_some_and(|t| Self::is_type_atom_start(&t.kind))
3282 {
3283 let end = self.tokens[self.cursor - 1];
3284 return Some(TypeAtom::ParsedType(Type::Con {
3285 qualifier: None,
3286 name: name.into(),
3287 span: Span::new(start, end.end),
3288 }));
3289 }
3290 return None;
3291 }
3292 if matches!(
3293 self.tokens.get(self.cursor).map(|t| &t.kind),
3294 Some(TokenKind::Comma)
3295 ) {
3296 let mut name = String::from(",");
3297 self.cursor += 1;
3298 while matches!(
3299 self.tokens.get(self.cursor).map(|t| &t.kind),
3300 Some(TokenKind::Comma)
3301 ) {
3302 self.cursor += 1;
3303 name.push(',');
3304 }
3305 if self.eat_token(&TokenKind::RParen).is_some()
3306 && self
3307 .tokens
3308 .get(self.cursor)
3309 .is_some_and(|t| Self::is_type_atom_start(&t.kind))
3310 {
3311 let end = self.tokens[self.cursor - 1];
3312 return Some(TypeAtom::ParsedType(Type::Con {
3313 qualifier: None,
3314 name: name.into(),
3315 span: Span::new(start, end.end),
3316 }));
3317 }
3318 return None;
3319 }
3320 if let Some(end) = self.eat_token(&TokenKind::RParen) {
3321 return Some(TypeAtom::ParsedType(Type::Unit(Span::new(start, end.end))));
3323 }
3324 let first = self.parse_type()?;
3325 if self.peek().map(|t| &t.kind) == Some(&TokenKind::Comma) {
3326 let mut items = vec![first];
3327 while self.eat_token(&TokenKind::Comma).is_some() {
3328 items.push(self.parse_type()?);
3329 }
3330 self.eat_token(&TokenKind::RParen).map(|end| {
3331 TypeAtom::ParsedType(Type::Tuple(items, Span::new(start, end.end)))
3332 })
3333 } else {
3334 self.eat_token(&TokenKind::RParen).map(|end| {
3335 TypeAtom::ParsedType(first.with_span(Span::new(start, end.end)))
3337 })
3338 }
3339 }
3340 _ => None,
3341 }
3342 }
3343
3344 fn eat_keyword(&mut self, kw: &str) -> bool {
3345 if self.peek().is_some_and(|t| t.kind.is_keyword(kw)) {
3346 self.cursor += 1;
3347 true
3348 } else {
3349 false
3350 }
3351 }
3352
3353 fn parse_forall_type(&mut self) -> Option<Type> {
3354 let start = self
3355 .tokens
3356 .get(self.cursor.wrapping_sub(1))
3357 .map(|t| t.start)
3358 .unwrap_or_default();
3359 while self.cursor < self.tokens.len() {
3360 if self.peek().is_some_and(|t| t.kind.is_op(".")) {
3361 self.cursor += 1;
3362 let body = self.parse_type()?;
3363 let body_span = body.span();
3364 return Some(body.with_span(Span::new(start, body_span.end)));
3365 }
3366 self.cursor += 1;
3367 }
3368 None
3369 }
3370
3371 fn eat_token(&mut self, tok: &TokenKind) -> Option<&'a Token> {
3372 if self.peek().is_some_and(|t| t.kind == *tok) {
3373 let t = self.peek();
3374 self.cursor += 1;
3375 t
3376 } else {
3377 None
3378 }
3379 }
3380
3381 fn eat_token_if_operator(&mut self) -> Option<&'a Operator> {
3382 match self.peek() {
3383 Some(Token {
3384 kind: TokenKind::Op(op),
3385 ..
3386 }) => {
3387 self.cursor += 1;
3388 Some(op)
3389 }
3390 _ => None,
3391 }
3392 }
3393
3394 const fn is_type_atom_start(kind: &TokenKind) -> bool {
3395 matches!(
3396 kind,
3397 TokenKind::UpperId { .. }
3398 | TokenKind::LowerId { .. }
3399 | TokenKind::IntLit(_)
3400 | TokenKind::DecimalLit(_)
3401 | TokenKind::StringLit(_)
3402 | TokenKind::CharLit(_)
3403 | TokenKind::LParen
3404 | TokenKind::LBracket
3405 )
3406 }
3407}
3408
3409fn render_token_slice(tokens: &[Token]) -> String {
3410 let mut s = String::new();
3411 let mut prev_no_space_after = true;
3412 for t in tokens {
3413 let (text, no_space_before, no_space_after): (String, bool, bool) = match &t.kind {
3414 TokenKind::LowerId { qualifier, name } | TokenKind::UpperId { qualifier, name } => (
3415 qualifier
3416 .as_ref()
3417 .map_or_else(|| name.to_string(), |q| format!("{q}.{name}")),
3418 false,
3419 false,
3420 ),
3421 TokenKind::Op(o) => (o.to_string(), false, false),
3422 TokenKind::IntLit(n) | TokenKind::DecimalLit(n) => (n.clone(), false, false),
3423 TokenKind::StringLit(v) => (format!("{v:?}"), false, false),
3424 TokenKind::CharLit(v) => (format!("'{v}'"), false, false),
3425 TokenKind::LParen => ("(".to_string(), false, true),
3426 TokenKind::RParen => (")".to_string(), true, false),
3427 TokenKind::LBracket => ("[".to_string(), false, true),
3428 TokenKind::RBracket => ("]".to_string(), true, false),
3429 TokenKind::LBrace => ("{".to_string(), false, true),
3430 TokenKind::RBrace => ("}".to_string(), true, false),
3431 TokenKind::Comma => (",".to_string(), true, false),
3432 TokenKind::Semi | TokenKind::VSemi => (";".to_string(), true, false),
3433 TokenKind::Backtick => ("`".to_string(), false, false),
3434 TokenKind::VLBrace | TokenKind::VRBrace => continue,
3435 };
3436 if !s.is_empty() && !no_space_before && !prev_no_space_after {
3437 s.push(' ');
3438 }
3439 s.push_str(&text);
3440 prev_no_space_after = no_space_after;
3441 }
3442 s
3443}
3444
3445#[cfg(test)]
3446mod type_tests {
3447 use super::*;
3448 use crate::lexer::lex;
3449
3450 fn ty(s: &str) -> Option<Type> {
3454 let (toks, errs) = lex(s).into_parts();
3455 assert!(errs.is_empty(), "lex errors for {s:?}: {errs:?}");
3456 parse_type_from_tokens(&toks)
3457 }
3458
3459 fn con(name: &str) -> Type {
3460 Type::Con {
3461 qualifier: None,
3462 name: name.into(),
3463 span: Span::default(),
3464 }
3465 }
3466
3467 fn qualified_con(qualifier: &str, name: &str) -> Type {
3468 Type::Con {
3469 qualifier: Some(qualifier.into()),
3470 name: name.into(),
3471 span: Span::default(),
3472 }
3473 }
3474
3475 fn app(head: Type, args: Vec<Type>) -> Type {
3476 Type::App(Box::new(head), args, Span::default())
3477 }
3478
3479 fn list(inner: Type) -> Type {
3480 Type::List(Box::new(inner), Span::default())
3481 }
3482
3483 fn tuple(items: Vec<Type>) -> Type {
3484 Type::Tuple(items, Span::default())
3485 }
3486
3487 fn fun(param: Type, result: Type) -> Type {
3488 Type::Fun(Box::new(param), Box::new(result), Span::default())
3489 }
3490
3491 fn var(name: &str) -> Type {
3492 Type::Var(name.into(), Span::default())
3493 }
3494
3495 fn unit() -> Type {
3496 Type::Unit(Span::default())
3497 }
3498
3499 fn constrained(body: Type) -> Type {
3500 Type::Constrained(Box::new(body), Span::default())
3501 }
3502
3503 fn text_lit(value: &str) -> Type {
3504 Type::Lit {
3505 kind: LitKind::Text,
3506 text: value.to_string(),
3507 span: Span::default(),
3508 }
3509 }
3510
3511 fn char_lit(value: &str) -> Type {
3512 Type::Lit {
3513 kind: LitKind::Char,
3514 text: value.to_string(),
3515 span: Span::default(),
3516 }
3517 }
3518
3519 #[test]
3520 fn atoms() {
3521 assert_eq!(ty("Party"), Some(con("Party")));
3522 assert_eq!(ty("Decimal"), Some(con("Decimal")));
3523 assert_eq!(ty("a"), Some(var("a")));
3524 assert_eq!(ty("()"), Some(unit()));
3525 }
3526
3527 #[test]
3528 fn application_vs_constructor() {
3529 assert_eq!(
3532 ty("ContractId Foo"),
3533 Some(app(con("ContractId"), vec![con("Foo")]))
3534 );
3535 assert_eq!(
3536 ty("Optional (ContractId Foo)"),
3537 Some(app(
3538 con("Optional"),
3539 vec![app(con("ContractId"), vec![con("Foo")])]
3540 ))
3541 );
3542 assert_eq!(
3543 ty("Map Text Int"),
3544 Some(app(con("Map"), vec![con("Text"), con("Int")]))
3545 );
3546 }
3547
3548 #[test]
3549 fn qualified_constructor_keeps_qualifier() {
3550 assert_eq!(
3551 ty("DA.Map.Map Text Int"),
3552 Some(app(
3553 qualified_con("DA.Map", "Map"),
3554 vec![con("Text"), con("Int")]
3555 ))
3556 );
3557 }
3558
3559 #[test]
3560 fn list_and_tuple() {
3561 assert_eq!(ty("[Text]"), Some(list(con("Text"))));
3562 assert_eq!(
3563 ty("(Int, Text)"),
3564 Some(tuple(vec![con("Int"), con("Text")]))
3565 );
3566 assert_eq!(
3568 ty("(a, b, c)"),
3569 Some(tuple(vec![var("a"), var("b"), var("c")]))
3570 );
3571 assert_eq!(ty("(Text)"), Some(con("Text")));
3573 }
3574
3575 #[test]
3576 fn function_types_are_arrows_not_names() {
3577 assert_eq!(ty("Int -> Int"), Some(fun(con("Int"), con("Int"))));
3580 assert_eq!(
3582 ty("Int -> Text -> Bool"),
3583 Some(fun(con("Int"), fun(con("Text"), con("Bool"))))
3584 );
3585 assert_eq!(
3586 ty("Party -> Script ()"),
3587 Some(fun(con("Party"), app(con("Script"), vec![unit()])))
3588 );
3589 }
3590
3591 #[test]
3592 fn script_application() {
3593 assert_eq!(ty("Script ()"), Some(app(con("Script"), vec![unit()])));
3596 }
3597
3598 #[test]
3599 fn numeric_nat_literal_is_dropped() {
3600 assert_eq!(ty("Numeric 10"), Some(con("Numeric")));
3603 assert_eq!(ty("Numeric n"), Some(app(con("Numeric"), vec![var("n")])));
3604 }
3605
3606 #[test]
3607 fn string_and_char_type_literals_are_structured() {
3608 assert_eq!(
3611 ty(r#"HasField "observers""#),
3612 Some(app(con("HasField"), vec![text_lit("observers")]))
3613 );
3614 assert_eq!(
3615 ty(r#"HasField "observers" t PartiesMap"#),
3616 Some(app(
3617 con("HasField"),
3618 vec![text_lit("observers"), var("t"), con("PartiesMap")]
3619 ))
3620 );
3621 assert_eq!(
3622 ty(r"HasField 'x'"),
3623 Some(app(con("HasField"), vec![char_lit("x")]))
3624 );
3625 }
3626
3627 #[test]
3628 fn type_literal_in_function_signature_is_not_malformed() {
3629 let src = r#"module M where
3630f : HasField "observers" t PartiesMap => ()
3631 = ()
3632"#;
3633 let (module, diagnostics) = parse_module(src).into_parts();
3634 assert!(
3635 !diagnostics
3636 .iter()
3637 .any(|d| d.message.contains("malformed function type annotation")),
3638 "constraint with type string literal must parse cleanly: {diagnostics:#?}"
3639 );
3640 let function = match &module.decls[0] {
3641 Decl::Function(f) => f,
3642 other => panic!("expected function, got {other:?}"),
3643 };
3644 let ty = function.ty.as_ref().expect("function signature type");
3645 assert!(matches!(
3646 ty,
3647 Type::Constrained(body, _)
3648 if matches!(
3649 &**body,
3650 Type::Unit(_)
3651 )
3652 ));
3653 }
3654
3655 #[test]
3656 fn constraint_context_is_dropped_body_kept() {
3657 assert_eq!(
3660 ty("NumericScale n => Numeric 37 -> Numeric n"),
3661 Some(constrained(fun(
3662 con("Numeric"),
3663 app(con("Numeric"), vec![var("n")])
3664 )))
3665 );
3666 assert_eq!(ty("(Eq a, Show a) => a"), Some(constrained(var("a"))));
3668 }
3669
3670 #[test]
3671 fn unparseable_is_none() {
3672 assert_eq!(ty("Int ->"), None);
3675 assert_eq!(ty("-> Int"), None);
3676 }
3677
3678 #[test]
3679 fn ty_is_populated_through_real_parse() {
3680 let src = r#"module M where
3683template T
3684 with
3685 owner : Party
3686 held : ContractId Asset
3687 where
3688 signatory owner
3689 choice Go : Optional (ContractId Asset)
3690 controller owner
3691 do
3692 pure None
3693"#;
3694 let (m, _) = parse_module(src).into_parts();
3695 let t = match &m.decls[0] {
3696 Decl::Template(t) => t,
3697 other => panic!("expected template, got {other:?}"),
3698 };
3699 assert_eq!(t.fields[0].ty, Some(con("Party")));
3700 assert_eq!(
3701 t.fields[1].ty,
3702 Some(app(con("ContractId"), vec![con("Asset")]))
3703 );
3704 let choice = match &t
3705 .body
3706 .iter()
3707 .find(|d| matches!(d, TemplateBodyDecl::Choice(_)))
3708 {
3709 Some(TemplateBodyDecl::Choice(c)) => (*c).clone(),
3710 _ => panic!("expected choice"),
3711 };
3712 assert_eq!(
3713 choice.return_ty,
3714 Some(app(
3715 con("Optional"),
3716 vec![app(con("ContractId"), vec![con("Asset")])]
3717 ))
3718 );
3719 }
3720
3721 #[test]
3722 fn ty_is_populated_on_key_and_interface_method() {
3723 let src = r#"module M where
3726template T
3727 with
3728 owner : Party
3729 where
3730 signatory owner
3731 key owner : Party
3732 maintainer owner
3733
3734interface I where
3735 getAmount : Numeric 10
3736"#;
3737 let (m, _) = parse_module(src).into_parts();
3738 let t = match &m.decls[0] {
3739 Decl::Template(t) => t,
3740 other => panic!("expected template, got {other:?}"),
3741 };
3742 let key_ty = t.body.iter().find_map(|d| match d {
3743 TemplateBodyDecl::Key { ty, .. } => Some(ty.clone()),
3744 _ => None,
3745 });
3746 assert_eq!(key_ty, Some(Some(con("Party"))));
3747
3748 let iface = match &m.decls[1] {
3749 Decl::Interface(i) => i,
3750 other => panic!("expected interface, got {other:?}"),
3751 };
3752 assert_eq!(iface.methods[0].ty, Some(con("Numeric")));
3754 }
3755
3756 #[test]
3757 fn malformed_guarded_equation_reports_missing_equals_and_continues() {
3758 let src = "module M where\nf x | x > 0\ng = 1\n";
3759 let (module, diagnostics) = parse_module(src).into_parts();
3760
3761 assert!(
3762 diagnostics.iter().any(
3763 |diagnostic| diagnostic.message == "expected '=' after guard"
3764 && diagnostic.category == DiagnosticCategory::Malformed
3765 ),
3766 "expected guard diagnostic, got {diagnostics:?}"
3767 );
3768 assert!(
3769 module
3770 .decls
3771 .iter()
3772 .any(|decl| matches!(decl, Decl::Function(function) if function.name == "g")),
3773 "parser should recover to the following declaration: {:?}",
3774 module.decls
3775 );
3776 }
3777
3778 #[test]
3779 fn malformed_brackets_do_not_underflow_recovery_scans() {
3780 let src = "module M where\ntemplate T\n with\n owner : Party\n where\n key owner ) : Party\n maintainer owner\n\nf = (]\ng = 1\n";
3781 let (module, _diagnostics) = parse_module(src).into_parts();
3782
3783 assert_eq!(module.name, "M");
3784 assert!(
3785 module
3786 .decls
3787 .iter()
3788 .any(|decl| matches!(decl, Decl::Function(function) if function.name == "g")),
3789 "parser should recover to the following declaration: {:?}",
3790 module.decls
3791 );
3792 }
3793
3794 #[test]
3795 fn headerless_file_keeps_legacy_unknown_name_fallback() {
3796 let (module, _diagnostics) = parse_module("f = 1\n").into_parts();
3797
3798 assert_eq!(module.name, "Unknown");
3799 assert!(
3800 module
3801 .decls
3802 .iter()
3803 .any(|decl| matches!(decl, Decl::Function(function) if function.name == "f")),
3804 "expected function declaration to be parsed: {:?}",
3805 module.decls
3806 );
3807 }
3808}
3809
3810#[cfg(test)]
3811mod parser_tests {
3812 use super::*;
3813
3814 fn parse(src: &str) -> (Module, Vec<ParseDiagnostic>) {
3815 parse_module(src).into_parts()
3816 }
3817
3818 fn section_side_for_fn(module: &Module, name: &str) -> SectionSide {
3819 let body = get_first_equation_body(module, name);
3820 match body {
3821 Expr::Section { side, .. } => *side,
3822 other => panic!("expected section body for {name}, got {other:?}"),
3823 }
3824 }
3825
3826 #[test]
3827 fn import_style_distinguishes_qualified_prefix_and_postfix() {
3828 let (module, diagnostics) = parse(
3829 "module M where
3830import qualified Foo.Bar as FB
3831import DA.Map qualified as Map
3832import Baz as B",
3833 );
3834
3835 assert!(diagnostics.is_empty());
3836 assert_eq!(
3837 module.imports.iter().map(|i| i.style).collect::<Vec<_>>(),
3838 vec![
3839 ImportStyle::Qualified,
3840 ImportStyle::Qualified,
3841 ImportStyle::Unqualified,
3842 ]
3843 );
3844 }
3845
3846 #[test]
3847 fn expression_sections_encode_side_in_ast() {
3848 let (module, diagnostics) = parse(
3849 "module M where
3850f = (+ 1)
3851g = (+)
3852",
3853 );
3854
3855 assert!(diagnostics.is_empty());
3856 assert!(matches!(
3857 get_first_equation_body(&module, "f"),
3858 Expr::Section {
3859 operand: Some(_),
3860 ..
3861 }
3862 ));
3863 assert!(matches!(
3864 get_first_equation_body(&module, "g"),
3865 Expr::Section { operand: None, .. }
3866 ));
3867 assert_eq!(section_side_for_fn(&module, "f"), SectionSide::Right);
3868 assert_eq!(section_side_for_fn(&module, "g"), SectionSide::Right);
3869 }
3870
3871 #[test]
3872 fn do_expr_is_allowed_for_top_level_expression_parsing() {
3873 let (module, diagnostics) = parse(
3874 "module M where
3875f = do
3876 pure True
3877",
3878 );
3879
3880 assert!(diagnostics.is_empty());
3881 assert!(matches!(
3882 get_first_equation_body(&module, "f"),
3883 Expr::Do { .. }
3884 ));
3885 }
3886
3887 #[test]
3888 fn do_expr_is_disallowed_for_case_scrutinee_parsing() {
3889 let (module, diagnostics) = parse(
3890 "module M where
3891f = case do 1 of
3892 x -> x
3893",
3894 );
3895
3896 assert!(diagnostics
3897 .iter()
3898 .any(|d| d.message == "expected 'of' in case expression"));
3899 assert!(matches!(
3900 get_first_equation_body(&module, "f"),
3901 Expr::Error { .. }
3902 ));
3903 }
3904
3905 fn get_first_equation_body<'a>(module: &'a Module, name: &str) -> &'a Expr {
3906 let function = module
3907 .decls
3908 .iter()
3909 .find_map(|d| match d {
3910 Decl::Function(f) if f.name == name => Some(f),
3911 _ => None,
3912 })
3913 .unwrap_or_else(|| panic!("missing function declaration {name}"));
3914 let first_equation = function
3915 .equations
3916 .first()
3917 .unwrap_or_else(|| panic!("missing equation for function {name}"));
3918 &first_equation.body
3919 }
3920}
3921
3922#[cfg(test)]
3923mod strict_parse_tests {
3924 use super::*;
3925 use crate::ast::DiagnosticCategory;
3926
3927 #[test]
3928 fn strict_parse_accepts_clean_module() {
3929 let src = "module M where\nfoo : Int\nfoo = 1\n";
3930 let module = parse_module_strict(src).expect("clean source must parse strictly");
3931 assert_eq!(module.name, "M");
3932 }
3933
3934 #[test]
3935 fn strict_parse_rejects_malformed_source_while_tolerant_parse_keeps_diagnostics() {
3936 let src = "module M where\nf x | x > 0\ng = 1\n";
3937 let tolerant = parse_module(src);
3938 assert!(
3939 tolerant
3940 .diagnostics
3941 .iter()
3942 .any(|d| d.category == DiagnosticCategory::Malformed),
3943 "tolerant parse must record malformed guard without aborting"
3944 );
3945
3946 let err = parse_module_strict(src).expect_err("malformed guard must fail strict parse");
3947 assert_eq!(err.diagnostics(), &tolerant.diagnostics);
3948 assert!(
3949 err.module()
3950 .decls
3951 .iter()
3952 .any(|decl| matches!(decl, Decl::Function(f) if f.name == "g")),
3953 "strict error should still carry the partial module tolerant parsing produced"
3954 );
3955 }
3956
3957 #[test]
3958 fn into_result_matches_parse_module_strict() {
3959 let src = "module M where\n%%% junk\n";
3960 let tolerant = parse_module(src);
3961 assert!(!tolerant.diagnostics.is_empty());
3962
3963 let strict = parse_module_strict(src).expect_err("junk decl must fail strict parse");
3964 let converted = tolerant
3965 .into_result()
3966 .expect_err("same diagnostics via into_result");
3967 assert_eq!(strict.diagnostics(), converted.diagnostics());
3968 }
3969}