1pub use crate::lexer::Pos;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub struct Span {
22 pub start: usize,
23 pub end: usize,
24}
25
26impl Span {
27 pub const fn new(start: usize, end: usize) -> Self {
28 Self { start, end }
29 }
30
31 pub const fn is_valid(&self) -> bool {
33 self.start <= self.end
34 }
35
36 pub const fn is_empty(&self) -> bool {
38 self.start == self.end
39 }
40
41 pub const fn contains(&self, other: &Self) -> bool {
43 self.is_valid() && other.is_valid() && self.start <= other.start && other.end <= self.end
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48#[non_exhaustive]
49pub enum LitKind {
50 Int,
51 Decimal,
52 Text,
53 Char,
54}
55
56#[derive(Debug, Clone)]
57pub struct FieldAssign {
58 pub name: String,
59 pub value: Option<Expr>,
62 pub pos: Pos,
63 pub span: Span,
64}
65
66#[derive(Debug, Clone)]
67pub struct Alt {
68 pub pat: Pat,
69 pub body: Expr,
70 pub pos: Pos,
71 pub span: Span,
72}
73
74#[derive(Debug, Clone)]
75pub struct Binding {
76 pub pat: Pat,
78 pub params: Vec<Pat>,
80 pub expr: Expr,
81 pub pos: Pos,
82 pub span: Span,
83}
84
85#[derive(Debug, Clone)]
86#[non_exhaustive]
87pub enum Pat {
88 Var {
89 name: String,
90 pos: Pos,
91 span: Span,
92 },
93 Wild {
94 pos: Pos,
95 span: Span,
96 },
97 Con {
98 qualifier: Option<String>,
99 name: String,
100 args: Vec<Self>,
101 pos: Pos,
102 span: Span,
103 },
104 Tuple {
105 items: Vec<Self>,
106 pos: Pos,
107 span: Span,
108 },
109 List {
110 items: Vec<Self>,
111 pos: Pos,
112 span: Span,
113 },
114 Lit {
115 kind: LitKind,
116 text: String,
117 pos: Pos,
118 span: Span,
119 },
120 As {
122 name: String,
123 pat: Box<Self>,
124 pos: Pos,
125 span: Span,
126 },
127 Other {
129 raw: String,
130 pos: Pos,
131 span: Span,
132 },
133}
134
135#[derive(Debug, Clone)]
136#[non_exhaustive]
137pub enum Expr {
138 Var {
140 qualifier: Option<String>,
141 name: String,
142 pos: Pos,
143 span: Span,
144 },
145 Con {
147 qualifier: Option<String>,
148 name: String,
149 pos: Pos,
150 span: Span,
151 },
152 Lit {
153 kind: LitKind,
154 text: String,
155 pos: Pos,
156 span: Span,
157 },
158 App {
160 func: Box<Self>,
161 args: Vec<Self>,
162 pos: Pos,
163 span: Span,
164 },
165 BinOp {
167 op: String,
168 lhs: Box<Self>,
169 rhs: Box<Self>,
170 pos: Pos,
171 span: Span,
172 },
173 Neg {
175 expr: Box<Self>,
176 pos: Pos,
177 span: Span,
178 },
179 Lambda {
180 params: Vec<Pat>,
181 body: Box<Self>,
182 pos: Pos,
183 span: Span,
184 },
185 If {
186 cond: Box<Self>,
187 then_branch: Box<Self>,
188 else_branch: Box<Self>,
189 pos: Pos,
190 span: Span,
191 },
192 Case {
193 scrutinee: Box<Self>,
194 alts: Vec<Alt>,
195 pos: Pos,
196 span: Span,
197 },
198 Do {
199 stmts: Vec<DoStmt>,
200 pos: Pos,
201 span: Span,
202 },
203 LetIn {
204 bindings: Vec<Binding>,
205 body: Box<Self>,
206 pos: Pos,
207 span: Span,
208 },
209 Record {
212 base: Box<Self>,
213 fields: Vec<FieldAssign>,
214 pos: Pos,
215 span: Span,
216 },
217 Tuple {
218 items: Vec<Self>,
219 pos: Pos,
220 span: Span,
221 },
222 List {
223 items: Vec<Self>,
224 pos: Pos,
225 span: Span,
226 },
227 Try {
229 body: Box<Self>,
230 handlers: Vec<Alt>,
231 pos: Pos,
232 span: Span,
233 },
234 Section {
236 op: String,
237 operand: Option<Box<Self>>,
238 left: bool,
239 pos: Pos,
240 span: Span,
241 },
242 Error { raw: String, pos: Pos, span: Span },
245}
246
247#[derive(Debug, Clone)]
248#[non_exhaustive]
249pub enum DoStmt {
250 Bind {
252 pat: Pat,
253 expr: Expr,
254 pos: Pos,
255 span: Span,
256 },
257 Let {
259 bindings: Vec<Binding>,
260 pos: Pos,
261 span: Span,
262 },
263 Expr { expr: Expr, pos: Pos, span: Span },
265}
266
267#[derive(Debug, Clone)]
275#[non_exhaustive]
276pub enum Type {
277 Con {
279 qualifier: Option<String>,
280 name: String,
281 span: Span,
282 },
283 App(Box<Self>, Vec<Self>, Span),
288 List(Box<Self>, Span),
290 Tuple(Vec<Self>, Span),
292 Fun(Box<Self>, Box<Self>, Span),
294 Var(String, Span),
296 Unit(Span),
298 Constrained(Box<Self>, Span),
301}
302
303impl Type {
304 pub const fn span(&self) -> Span {
305 match self {
306 Self::Con { span, .. }
307 | Self::App(_, _, span)
308 | Self::List(_, span)
309 | Self::Tuple(_, span)
310 | Self::Fun(_, _, span)
311 | Self::Var(_, span)
312 | Self::Unit(span)
313 | Self::Constrained(_, span) => *span,
314 }
315 }
316
317 pub(crate) const fn with_span(mut self, span: Span) -> Self {
318 match &mut self {
319 Self::Con { span: s, .. }
320 | Self::App(_, _, s)
321 | Self::List(_, s)
322 | Self::Tuple(_, s)
323 | Self::Fun(_, _, s)
324 | Self::Var(_, s)
325 | Self::Unit(s)
326 | Self::Constrained(_, s) => *s = span,
327 }
328 self
329 }
330}
331
332impl PartialEq for Type {
337 fn eq(&self, other: &Self) -> bool {
338 match (self, other) {
339 (
340 Self::Con {
341 qualifier: aq,
342 name: an,
343 ..
344 },
345 Self::Con {
346 qualifier: bq,
347 name: bn,
348 ..
349 },
350 ) => aq == bq && an == bn,
351 (Self::App(ah, aa, _), Self::App(bh, ba, _)) => ah == bh && aa == ba,
352 (Self::List(a, _), Self::List(b, _))
353 | (Self::Constrained(a, _), Self::Constrained(b, _)) => a == b,
354 (Self::Tuple(a, _), Self::Tuple(b, _)) => a == b,
355 (Self::Fun(al, ar, _), Self::Fun(bl, br, _)) => al == bl && ar == br,
356 (Self::Var(a, _), Self::Var(b, _)) => a == b,
357 (Self::Unit(_), Self::Unit(_)) => true,
358 _ => false,
359 }
360 }
361}
362
363impl Eq for Type {}
364
365#[derive(Debug, Clone)]
366pub struct FieldDecl {
367 pub name: String,
368 pub ty: Option<Type>,
371 pub pos: Pos,
372 pub span: Span,
373}
374
375#[derive(Debug, Clone, Copy, PartialEq, Eq)]
376pub enum Consuming {
377 Consuming,
378 NonConsuming,
379 PreConsuming,
380 PostConsuming,
381}
382
383#[derive(Debug, Clone)]
384pub struct ChoiceDecl {
385 pub name: String,
386 pub consuming: Consuming,
387 pub return_ty: Option<Type>,
390 pub params: Vec<FieldDecl>,
391 pub controllers: Vec<Expr>,
393 pub observers: Vec<Expr>,
395 pub body: Option<Expr>,
396 pub pos: Pos,
397 pub span: Span,
398}
399
400#[derive(Debug, Clone)]
401pub enum TemplateBodyDecl {
402 Signatory {
403 parties: Vec<Expr>,
404 pos: Pos,
405 span: Span,
406 },
407 Observer {
408 parties: Vec<Expr>,
409 pos: Pos,
410 span: Span,
411 },
412 Ensure {
413 expr: Expr,
414 pos: Pos,
415 span: Span,
416 },
417 Key {
418 expr: Expr,
419 ty: Option<Type>,
421 pos: Pos,
422 span: Span,
423 },
424 Maintainer {
425 expr: Expr,
426 pos: Pos,
427 span: Span,
428 },
429 Choice(ChoiceDecl),
430 InterfaceInstance(InterfaceInstanceDecl),
431 Other {
433 raw: String,
434 pos: Pos,
435 span: Span,
436 },
437}
438
439#[derive(Debug, Clone)]
440pub struct InterfaceInstanceDecl {
441 pub interface_name: String,
443 pub for_template: String,
446 pub methods: Vec<Binding>,
448 pub pos: Pos,
449 pub span: Span,
450}
451
452#[derive(Debug, Clone)]
453pub struct TemplateDecl {
454 pub name: String,
455 pub fields: Vec<FieldDecl>,
456 pub body: Vec<TemplateBodyDecl>,
457 pub pos: Pos,
458 pub span: Span,
459}
460
461#[derive(Debug, Clone)]
462pub struct InterfaceDecl {
463 pub name: String,
464 pub requires: Vec<String>,
466 pub viewtype: Option<String>,
467 pub methods: Vec<FieldDecl>,
469 pub choices: Vec<ChoiceDecl>,
470 pub pos: Pos,
471 pub span: Span,
472}
473
474#[derive(Debug, Clone)]
475pub struct Equation {
476 pub params: Vec<Pat>,
477 pub body: Expr,
478 pub guards: Vec<(Expr, Expr)>,
481 pub where_bindings: Vec<Binding>,
483 pub pos: Pos,
484 pub span: Span,
485}
486
487#[derive(Debug, Clone)]
488pub struct FunctionDecl {
489 pub name: String,
490 pub ty: Option<Type>,
491 pub equations: Vec<Equation>,
492 pub pos: Pos,
493 pub span: Span,
497 pub sig_span: Option<Span>,
499}
500
501#[derive(Debug, Clone)]
502pub struct ImportDecl {
503 pub module_name: String,
504 pub qualified: bool,
505 pub alias: Option<String>,
506 pub pos: Pos,
507 pub span: Span,
508}
509
510#[derive(Debug, Clone)]
511#[non_exhaustive]
512pub enum Decl {
513 Template(TemplateDecl),
514 Interface(InterfaceDecl),
515 Function(FunctionDecl),
516 TypeDef {
518 keyword: String,
519 name: String,
520 pos: Pos,
521 span: Span,
522 },
523 Unknown {
525 raw: String,
526 pos: Pos,
527 span: Span,
528 },
529}
530
531#[derive(Debug, Clone)]
532pub struct Module {
533 pub name: String,
534 pub pos: Pos,
535 pub span: Span,
537 pub header: Span,
541 pub imports: Vec<ImportDecl>,
542 pub decls: Vec<Decl>,
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq)]
551#[non_exhaustive]
552pub enum DiagnosticCategory {
553 SkippedDecl,
555 Malformed,
558 UnsupportedSyntax,
561 RecursionLimit,
564 Lex,
566}
567
568impl DiagnosticCategory {
569 pub const fn as_str(self) -> &'static str {
571 match self {
572 Self::SkippedDecl => "skipped-declaration",
573 Self::Malformed => "malformed",
574 Self::UnsupportedSyntax => "unsupported-syntax",
575 Self::RecursionLimit => "recursion-limit",
576 Self::Lex => "lexical-error",
577 }
578 }
579}
580
581#[derive(Debug, Clone)]
583pub struct ParseDiagnostic {
584 pub message: String,
585 pub pos: Pos,
586 pub span: Span,
590 pub category: DiagnosticCategory,
591}
592
593impl Expr {
594 pub const fn pos(&self) -> Pos {
595 match self {
596 Self::Var { pos, .. }
597 | Self::Con { pos, .. }
598 | Self::Lit { pos, .. }
599 | Self::App { pos, .. }
600 | Self::BinOp { pos, .. }
601 | Self::Neg { pos, .. }
602 | Self::Lambda { pos, .. }
603 | Self::If { pos, .. }
604 | Self::Case { pos, .. }
605 | Self::Do { pos, .. }
606 | Self::LetIn { pos, .. }
607 | Self::Record { pos, .. }
608 | Self::Tuple { pos, .. }
609 | Self::List { pos, .. }
610 | Self::Try { pos, .. }
611 | Self::Section { pos, .. }
612 | Self::Error { pos, .. } => *pos,
613 }
614 }
615
616 pub const fn span(&self) -> Span {
618 match self {
619 Self::Var { span, .. }
620 | Self::Con { span, .. }
621 | Self::Lit { span, .. }
622 | Self::App { span, .. }
623 | Self::BinOp { span, .. }
624 | Self::Neg { span, .. }
625 | Self::Lambda { span, .. }
626 | Self::If { span, .. }
627 | Self::Case { span, .. }
628 | Self::Do { span, .. }
629 | Self::LetIn { span, .. }
630 | Self::Record { span, .. }
631 | Self::Tuple { span, .. }
632 | Self::List { span, .. }
633 | Self::Try { span, .. }
634 | Self::Section { span, .. }
635 | Self::Error { span, .. } => *span,
636 }
637 }
638
639 pub fn render(&self) -> String {
649 match self {
650 Self::Var {
651 qualifier, name, ..
652 }
653 | Self::Con {
654 qualifier, name, ..
655 } => qualifier
656 .as_ref()
657 .map_or_else(|| name.clone(), |q| format!("{q}.{name}")),
658 Self::Lit { kind, text, .. } => match kind {
659 LitKind::Text => format!("{text:?}"),
660 LitKind::Char => format!("'{text}'"),
661 _ => text.clone(),
662 },
663 Self::App { func, args, .. } => {
664 let mut s = func.render_atomic();
665 for a in args {
666 s.push(' ');
667 s.push_str(&a.render_atomic());
668 }
669 s
670 }
671 Self::BinOp { op, lhs, rhs, .. } => {
672 if op == "." {
673 format!("{}.{}", lhs.render_atomic(), rhs.render_atomic())
675 } else {
676 format!("{} {} {}", lhs.render_atomic(), op, rhs.render_atomic())
677 }
678 }
679 Self::Neg { expr, .. } => format!("-{}", expr.render_atomic()),
680 Self::Lambda { params, body, .. } => {
681 let ps: Vec<String> = params.iter().map(|p| p.render()).collect();
682 format!("\\{} -> {}", ps.join(" "), body.render())
683 }
684 Self::If {
685 cond,
686 then_branch,
687 else_branch,
688 ..
689 } => format!(
690 "if {} then {} else {}",
691 cond.render(),
692 then_branch.render(),
693 else_branch.render()
694 ),
695 Self::Case {
696 scrutinee, alts, ..
697 } => {
698 let arms: Vec<String> = alts
699 .iter()
700 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
701 .collect();
702 format!("case {} of {}", scrutinee.render(), arms.join("; "))
703 }
704 Self::Do { stmts, .. } => {
705 let body: Vec<String> = stmts.iter().map(render_do_stmt).collect();
706 format!("do {}", body.join("; "))
707 }
708 Self::LetIn { bindings, body, .. } => {
709 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
710 format!("let {} in {}", bs.join("; "), body.render())
711 }
712 Self::Record { base, fields, .. } => {
713 let fs: Vec<String> = fields
714 .iter()
715 .map(|f| {
716 f.value.as_ref().map_or_else(
717 || f.name.clone(),
718 |v| format!("{} = {}", f.name, v.render()),
719 )
720 })
721 .collect();
722 format!("{} with {}", base.render_atomic(), fs.join("; "))
723 }
724 Self::Tuple { items, .. } => {
725 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
726 format!("({})", xs.join(", "))
727 }
728 Self::List { items, .. } => {
729 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
730 format!("[{}]", xs.join(", "))
731 }
732 Self::Try { body, handlers, .. } => {
733 let hs: Vec<String> = handlers
734 .iter()
735 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
736 .collect();
737 format!("try {} catch {}", body.render(), hs.join("; "))
738 }
739 Self::Section {
740 op, operand, left, ..
741 } => match (operand, left) {
742 (Some(e), true) => format!("({} {})", e.render(), op),
743 (Some(e), false) => format!("({} {})", op, e.render()),
744 (None, _) => format!("({op})"),
745 },
746 Self::Error { raw, .. } => raw.clone(),
747 }
748 }
749
750 fn render_atomic(&self) -> String {
753 match self {
754 Self::Var { .. }
755 | Self::Con { .. }
756 | Self::Lit { .. }
757 | Self::Tuple { .. }
758 | Self::List { .. }
759 | Self::Section { .. }
760 | Self::Error { .. } => self.render(),
761 _ => format!("({})", self.render()),
762 }
763 }
764
765 pub fn application_head(&self) -> &Self {
768 match self {
769 Self::App { func, .. } => func.application_head(),
770 _ => self,
771 }
772 }
773
774 pub fn application_args(&self) -> &[Self] {
778 match self {
779 Self::App { args, .. } => args,
780 _ => &[],
781 }
782 }
783}
784
785fn render_do_stmt(s: &DoStmt) -> String {
786 match s {
787 DoStmt::Bind { pat, expr, .. } => format!("{} <- {}", pat.render(), expr.render()),
788 DoStmt::Let { bindings, .. } => {
789 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
790 format!("let {}", bs.join("; "))
791 }
792 DoStmt::Expr { expr, .. } => expr.render(),
793 }
794}
795
796fn render_binding(b: &Binding) -> String {
797 let mut s = b.pat.render();
798 for p in &b.params {
799 s.push(' ');
800 s.push_str(&p.render());
801 }
802 format!("{} = {}", s, b.expr.render())
803}
804
805impl Pat {
806 pub const fn pos(&self) -> Pos {
807 match self {
808 Self::Var { pos, .. }
809 | Self::Wild { pos, .. }
810 | Self::Con { pos, .. }
811 | Self::Tuple { pos, .. }
812 | Self::List { pos, .. }
813 | Self::Lit { pos, .. }
814 | Self::As { pos, .. }
815 | Self::Other { pos, .. } => *pos,
816 }
817 }
818
819 pub const fn span(&self) -> Span {
821 match self {
822 Self::Var { span, .. }
823 | Self::Wild { span, .. }
824 | Self::Con { span, .. }
825 | Self::Tuple { span, .. }
826 | Self::List { span, .. }
827 | Self::Lit { span, .. }
828 | Self::As { span, .. }
829 | Self::Other { span, .. } => *span,
830 }
831 }
832
833 pub fn render(&self) -> String {
837 match self {
838 Self::Var { name, .. } => name.clone(),
839 Self::Wild { .. } => "_".to_string(),
840 Self::Con {
841 qualifier,
842 name,
843 args,
844 ..
845 } => {
846 let head = qualifier
847 .as_ref()
848 .map_or_else(|| name.clone(), |q| format!("{q}.{name}"));
849 if args.is_empty() {
850 head
851 } else {
852 let parts: Vec<String> = args.iter().map(|p| p.render()).collect();
853 format!("({} {})", head, parts.join(" "))
854 }
855 }
856 Self::Tuple { items, .. } => {
857 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
858 format!("({})", xs.join(", "))
859 }
860 Self::List { items, .. } => {
861 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
862 format!("[{}]", xs.join(", "))
863 }
864 Self::Lit { kind, text, .. } => match kind {
865 LitKind::Text => format!("{text:?}"),
866 LitKind::Char => format!("'{text}'"),
867 _ => text.clone(),
868 },
869 Self::As { name, pat, .. } => format!("{}@{}", name, pat.render()),
870 Self::Other { raw, .. } => raw.clone(),
871 }
872 }
873}
874
875impl std::fmt::Display for Expr {
876 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
877 f.write_str(&self.render())
878 }
879}
880
881impl std::fmt::Display for Pat {
882 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
883 f.write_str(&self.render())
884 }
885}
886
887#[cfg(test)]
888mod tests {
889 use super::*;
890
891 fn pos() -> Pos {
892 Pos { line: 1, column: 1 }
893 }
894
895 fn span(start: usize, end: usize) -> Span {
896 Span::new(start, end)
897 }
898
899 #[test]
900 fn span_distinguishes_empty_from_invalid() {
901 assert!(span(3, 3).is_valid());
902 assert!(span(3, 3).is_empty());
903
904 assert!(!span(4, 3).is_valid());
905 assert!(!span(4, 3).is_empty());
906 }
907
908 #[test]
909 fn contains_rejects_invalid_spans() {
910 let parent = span(1, 10);
911
912 assert!(parent.contains(&span(3, 7)));
913 assert!(!parent.contains(&span(7, 3)));
914 assert!(!span(10, 1).contains(&span(3, 7)));
915 }
916
917 #[test]
918 fn expr_render_keeps_normalized_application_and_projection_shape() {
919 let projection = Expr::BinOp {
920 op: ".".to_string(),
921 lhs: Box::new(Expr::Var {
922 qualifier: None,
923 name: "this".to_string(),
924 pos: pos(),
925 span: span(0, 4),
926 }),
927 rhs: Box::new(Expr::Var {
928 qualifier: None,
929 name: "note".to_string(),
930 pos: pos(),
931 span: span(5, 9),
932 }),
933 pos: pos(),
934 span: span(0, 9),
935 };
936
937 let expr = Expr::App {
938 func: Box::new(Expr::Var {
939 qualifier: None,
940 name: "length".to_string(),
941 pos: pos(),
942 span: span(0, 6),
943 }),
944 args: vec![projection],
945 pos: pos(),
946 span: span(0, 16),
947 };
948
949 assert_eq!(expr.render(), "length (this.note)");
950 }
951
952 #[test]
953 fn pat_render_preserves_collection_shape() {
954 let pat = Pat::Tuple {
955 items: vec![
956 Pat::Var {
957 name: "owner".to_string(),
958 pos: pos(),
959 span: span(1, 6),
960 },
961 Pat::List {
962 items: Vec::new(),
963 pos: pos(),
964 span: span(8, 10),
965 },
966 ],
967 pos: pos(),
968 span: span(0, 11),
969 };
970
971 assert_eq!(pat.render(), "(owner, [])");
972 }
973}