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