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, Copy, PartialEq, Eq)]
61#[non_exhaustive]
62pub enum SectionSide {
63 Right,
65 Left,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[non_exhaustive]
72pub enum ImportStyle {
73 Qualified,
75 Unqualified,
77}
78
79#[derive(Debug, Clone)]
80pub struct FieldAssign {
81 pub name: String,
82 pub value: Option<Expr>,
85 pub pos: Pos,
86 pub span: Span,
87}
88
89#[derive(Debug, Clone)]
90pub struct Alt {
91 pub pat: Pat,
92 pub body: Expr,
93 pub pos: Pos,
94 pub span: Span,
95}
96
97#[derive(Debug, Clone)]
98pub struct Binding {
99 pub pat: Pat,
101 pub params: Vec<Pat>,
103 pub expr: Expr,
104 pub pos: Pos,
105 pub span: Span,
106}
107
108#[derive(Debug, Clone)]
109#[non_exhaustive]
110pub enum Pat {
111 Var {
112 name: String,
113 pos: Pos,
114 span: Span,
115 },
116 Wild {
117 pos: Pos,
118 span: Span,
119 },
120 Con {
121 qualifier: Option<String>,
122 name: String,
123 args: Vec<Self>,
124 pos: Pos,
125 span: Span,
126 },
127 Tuple {
128 items: Vec<Self>,
129 pos: Pos,
130 span: Span,
131 },
132 List {
133 items: Vec<Self>,
134 pos: Pos,
135 span: Span,
136 },
137 Lit {
138 kind: LitKind,
139 text: String,
140 pos: Pos,
141 span: Span,
142 },
143 As {
145 name: String,
146 pat: Box<Self>,
147 pos: Pos,
148 span: Span,
149 },
150 Other {
152 raw: String,
153 pos: Pos,
154 span: Span,
155 },
156}
157
158#[derive(Debug, Clone)]
159#[non_exhaustive]
160pub enum Expr {
161 Var {
163 qualifier: Option<String>,
164 name: String,
165 pos: Pos,
166 span: Span,
167 },
168 Con {
170 qualifier: Option<String>,
171 name: String,
172 pos: Pos,
173 span: Span,
174 },
175 Lit {
176 kind: LitKind,
177 text: String,
178 pos: Pos,
179 span: Span,
180 },
181 App {
183 func: Box<Self>,
184 args: Vec<Self>,
185 pos: Pos,
186 span: Span,
187 },
188 BinOp {
190 op: String,
191 lhs: Box<Self>,
192 rhs: Box<Self>,
193 pos: Pos,
194 span: Span,
195 },
196 Neg {
198 expr: Box<Self>,
199 pos: Pos,
200 span: Span,
201 },
202 Lambda {
203 params: Vec<Pat>,
204 body: Box<Self>,
205 pos: Pos,
206 span: Span,
207 },
208 If {
209 cond: Box<Self>,
210 then_branch: Box<Self>,
211 else_branch: Box<Self>,
212 pos: Pos,
213 span: Span,
214 },
215 Case {
216 scrutinee: Box<Self>,
217 alts: Vec<Alt>,
218 pos: Pos,
219 span: Span,
220 },
221 Do {
222 stmts: Vec<DoStmt>,
223 pos: Pos,
224 span: Span,
225 },
226 LetIn {
227 bindings: Vec<Binding>,
228 body: Box<Self>,
229 pos: Pos,
230 span: Span,
231 },
232 Record {
235 base: Box<Self>,
236 fields: Vec<FieldAssign>,
237 pos: Pos,
238 span: Span,
239 },
240 Tuple {
241 items: Vec<Self>,
242 pos: Pos,
243 span: Span,
244 },
245 List {
246 items: Vec<Self>,
247 pos: Pos,
248 span: Span,
249 },
250 Try {
252 body: Box<Self>,
253 handlers: Vec<Alt>,
254 pos: Pos,
255 span: Span,
256 },
257 Section {
259 op: String,
260 operand: Option<Box<Self>>,
261 side: SectionSide,
262 pos: Pos,
263 span: Span,
264 },
265 Error { raw: String, pos: Pos, span: Span },
268}
269
270#[derive(Debug, Clone)]
271#[non_exhaustive]
272pub enum DoStmt {
273 Bind {
275 pat: Pat,
276 expr: Expr,
277 pos: Pos,
278 span: Span,
279 },
280 Let {
282 bindings: Vec<Binding>,
283 pos: Pos,
284 span: Span,
285 },
286 Expr { expr: Expr, pos: Pos, span: Span },
288}
289
290#[derive(Debug, Clone)]
298#[non_exhaustive]
299pub enum Type {
300 Con {
302 qualifier: Option<String>,
303 name: String,
304 span: Span,
305 },
306 App(Box<Self>, Vec<Self>, Span),
311 List(Box<Self>, Span),
313 Tuple(Vec<Self>, Span),
315 Fun(Box<Self>, Box<Self>, Span),
317 Var(String, Span),
319 Unit(Span),
321 Constrained(Box<Self>, Span),
324}
325
326impl Type {
327 pub const fn span(&self) -> Span {
328 match self {
329 Self::Con { span, .. }
330 | Self::App(_, _, span)
331 | Self::List(_, span)
332 | Self::Tuple(_, span)
333 | Self::Fun(_, _, span)
334 | Self::Var(_, span)
335 | Self::Unit(span)
336 | Self::Constrained(_, span) => *span,
337 }
338 }
339
340 pub(crate) const fn with_span(mut self, span: Span) -> Self {
341 match &mut self {
342 Self::Con { span: s, .. }
343 | Self::App(_, _, s)
344 | Self::List(_, s)
345 | Self::Tuple(_, s)
346 | Self::Fun(_, _, s)
347 | Self::Var(_, s)
348 | Self::Unit(s)
349 | Self::Constrained(_, s) => *s = span,
350 }
351 self
352 }
353}
354
355impl PartialEq for Type {
360 fn eq(&self, other: &Self) -> bool {
361 match (self, other) {
362 (
363 Self::Con {
364 qualifier: aq,
365 name: an,
366 ..
367 },
368 Self::Con {
369 qualifier: bq,
370 name: bn,
371 ..
372 },
373 ) => aq == bq && an == bn,
374 (Self::App(ah, aa, _), Self::App(bh, ba, _)) => ah == bh && aa == ba,
375 (Self::List(a, _), Self::List(b, _))
376 | (Self::Constrained(a, _), Self::Constrained(b, _)) => a == b,
377 (Self::Tuple(a, _), Self::Tuple(b, _)) => a == b,
378 (Self::Fun(al, ar, _), Self::Fun(bl, br, _)) => al == bl && ar == br,
379 (Self::Var(a, _), Self::Var(b, _)) => a == b,
380 (Self::Unit(_), Self::Unit(_)) => true,
381 _ => false,
382 }
383 }
384}
385
386impl Eq for Type {}
387
388#[derive(Debug, Clone)]
389pub struct FieldDecl {
390 pub name: String,
391 pub ty: Option<Type>,
394 pub pos: Pos,
395 pub span: Span,
396}
397
398#[derive(Debug, Clone, Copy, PartialEq, Eq)]
399pub enum Consuming {
400 Consuming,
401 NonConsuming,
402 PreConsuming,
403 PostConsuming,
404}
405
406#[derive(Debug, Clone)]
407pub struct ChoiceDecl {
408 pub name: String,
409 pub consuming: Consuming,
410 pub return_ty: Option<Type>,
413 pub params: Vec<FieldDecl>,
414 pub controllers: Vec<Expr>,
416 pub observers: Vec<Expr>,
418 pub body: Option<Expr>,
419 pub pos: Pos,
420 pub span: Span,
421}
422
423#[derive(Debug, Clone)]
424pub enum TemplateBodyDecl {
425 Signatory {
426 parties: Vec<Expr>,
427 pos: Pos,
428 span: Span,
429 },
430 Observer {
431 parties: Vec<Expr>,
432 pos: Pos,
433 span: Span,
434 },
435 Ensure {
436 expr: Expr,
437 pos: Pos,
438 span: Span,
439 },
440 Key {
441 expr: Expr,
442 ty: Option<Type>,
444 pos: Pos,
445 span: Span,
446 },
447 Maintainer {
448 expr: Expr,
449 pos: Pos,
450 span: Span,
451 },
452 Choice(ChoiceDecl),
453 InterfaceInstance(InterfaceInstanceDecl),
454 Other {
456 raw: String,
457 pos: Pos,
458 span: Span,
459 },
460}
461
462#[derive(Debug, Clone)]
463pub struct InterfaceInstanceDecl {
464 pub interface_name: String,
466 pub for_template: String,
469 pub methods: Vec<Binding>,
471 pub pos: Pos,
472 pub span: Span,
473}
474
475#[derive(Debug, Clone)]
476pub struct TemplateDecl {
477 pub name: String,
478 pub fields: Vec<FieldDecl>,
479 pub body: Vec<TemplateBodyDecl>,
480 pub pos: Pos,
481 pub span: Span,
482}
483
484#[derive(Debug, Clone)]
485pub struct InterfaceDecl {
486 pub name: String,
487 pub requires: Vec<String>,
489 pub viewtype: Option<String>,
490 pub methods: Vec<FieldDecl>,
492 pub choices: Vec<ChoiceDecl>,
493 pub pos: Pos,
494 pub span: Span,
495}
496
497#[derive(Debug, Clone)]
498pub struct Equation {
499 pub params: Vec<Pat>,
500 pub body: Expr,
501 pub guards: Vec<(Expr, Expr)>,
504 pub where_bindings: Vec<Binding>,
506 pub pos: Pos,
507 pub span: Span,
508}
509
510#[derive(Debug, Clone)]
511pub struct FunctionDecl {
512 pub name: String,
513 pub ty: Option<Type>,
514 pub equations: Vec<Equation>,
515 pub pos: Pos,
516 pub span: Span,
520 pub sig_span: Option<Span>,
522}
523
524#[derive(Debug, Clone)]
525pub struct ImportDecl {
526 pub module_name: String,
527 pub style: ImportStyle,
528 pub alias: Option<String>,
529 pub pos: Pos,
530 pub span: Span,
531}
532
533#[derive(Debug, Clone)]
534#[non_exhaustive]
535pub enum Decl {
536 Template(TemplateDecl),
537 Interface(InterfaceDecl),
538 Function(FunctionDecl),
539 TypeDef {
541 keyword: String,
542 name: String,
543 pos: Pos,
544 span: Span,
545 },
546 Unknown {
548 raw: String,
549 pos: Pos,
550 span: Span,
551 },
552}
553
554#[derive(Debug, Clone)]
555pub struct Module {
556 pub name: String,
557 pub pos: Pos,
558 pub span: Span,
560 pub header: Span,
564 pub imports: Vec<ImportDecl>,
565 pub decls: Vec<Decl>,
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574#[non_exhaustive]
575pub enum DiagnosticCategory {
576 SkippedDecl,
578 Malformed,
581 UnsupportedSyntax,
584 RecursionLimit,
587 Lex,
589}
590
591impl DiagnosticCategory {
592 pub const fn as_str(self) -> &'static str {
594 match self {
595 Self::SkippedDecl => "skipped-declaration",
596 Self::Malformed => "malformed",
597 Self::UnsupportedSyntax => "unsupported-syntax",
598 Self::RecursionLimit => "recursion-limit",
599 Self::Lex => "lexical-error",
600 }
601 }
602}
603
604#[derive(Debug, Clone)]
606pub struct ParseDiagnostic {
607 pub message: String,
608 pub pos: Pos,
609 pub span: Span,
613 pub category: DiagnosticCategory,
614}
615
616impl Expr {
617 pub const fn pos(&self) -> Pos {
618 match self {
619 Self::Var { pos, .. }
620 | Self::Con { pos, .. }
621 | Self::Lit { pos, .. }
622 | Self::App { pos, .. }
623 | Self::BinOp { pos, .. }
624 | Self::Neg { pos, .. }
625 | Self::Lambda { pos, .. }
626 | Self::If { pos, .. }
627 | Self::Case { pos, .. }
628 | Self::Do { pos, .. }
629 | Self::LetIn { pos, .. }
630 | Self::Record { pos, .. }
631 | Self::Tuple { pos, .. }
632 | Self::List { pos, .. }
633 | Self::Try { pos, .. }
634 | Self::Section { pos, .. }
635 | Self::Error { pos, .. } => *pos,
636 }
637 }
638
639 pub const fn span(&self) -> Span {
641 match self {
642 Self::Var { span, .. }
643 | Self::Con { span, .. }
644 | Self::Lit { span, .. }
645 | Self::App { span, .. }
646 | Self::BinOp { span, .. }
647 | Self::Neg { span, .. }
648 | Self::Lambda { span, .. }
649 | Self::If { span, .. }
650 | Self::Case { span, .. }
651 | Self::Do { span, .. }
652 | Self::LetIn { span, .. }
653 | Self::Record { span, .. }
654 | Self::Tuple { span, .. }
655 | Self::List { span, .. }
656 | Self::Try { span, .. }
657 | Self::Section { span, .. }
658 | Self::Error { span, .. } => *span,
659 }
660 }
661
662 pub fn render(&self) -> String {
672 match self {
673 Self::Var {
674 qualifier, name, ..
675 }
676 | Self::Con {
677 qualifier, name, ..
678 } => qualifier
679 .as_ref()
680 .map_or_else(|| name.clone(), |q| format!("{q}.{name}")),
681 Self::Lit { kind, text, .. } => match kind {
682 LitKind::Text => format!("{text:?}"),
683 LitKind::Char => format!("'{text}'"),
684 _ => text.clone(),
685 },
686 Self::App { func, args, .. } => {
687 let mut s = func.render_atomic();
688 for a in args {
689 s.push(' ');
690 s.push_str(&a.render_atomic());
691 }
692 s
693 }
694 Self::BinOp { op, lhs, rhs, .. } => {
695 if op == "." {
696 format!("{}.{}", lhs.render_atomic(), rhs.render_atomic())
698 } else {
699 format!("{} {} {}", lhs.render_atomic(), op, rhs.render_atomic())
700 }
701 }
702 Self::Neg { expr, .. } => format!("-{}", expr.render_atomic()),
703 Self::Lambda { params, body, .. } => {
704 let ps: Vec<String> = params.iter().map(|p| p.render()).collect();
705 format!("\\{} -> {}", ps.join(" "), body.render())
706 }
707 Self::If {
708 cond,
709 then_branch,
710 else_branch,
711 ..
712 } => format!(
713 "if {} then {} else {}",
714 cond.render(),
715 then_branch.render(),
716 else_branch.render()
717 ),
718 Self::Case {
719 scrutinee, alts, ..
720 } => {
721 let arms: Vec<String> = alts
722 .iter()
723 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
724 .collect();
725 format!("case {} of {}", scrutinee.render(), arms.join("; "))
726 }
727 Self::Do { stmts, .. } => {
728 let body: Vec<String> = stmts.iter().map(render_do_stmt).collect();
729 format!("do {}", body.join("; "))
730 }
731 Self::LetIn { bindings, body, .. } => {
732 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
733 format!("let {} in {}", bs.join("; "), body.render())
734 }
735 Self::Record { base, fields, .. } => {
736 let fs: Vec<String> = fields
737 .iter()
738 .map(|f| {
739 f.value.as_ref().map_or_else(
740 || f.name.clone(),
741 |v| format!("{} = {}", f.name, v.render()),
742 )
743 })
744 .collect();
745 format!("{} with {}", base.render_atomic(), fs.join("; "))
746 }
747 Self::Tuple { items, .. } => {
748 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
749 format!("({})", xs.join(", "))
750 }
751 Self::List { items, .. } => {
752 let xs: Vec<String> = items.iter().map(|e| e.render()).collect();
753 format!("[{}]", xs.join(", "))
754 }
755 Self::Try { body, handlers, .. } => {
756 let hs: Vec<String> = handlers
757 .iter()
758 .map(|a| format!("{} -> {}", a.pat.render(), a.body.render()))
759 .collect();
760 format!("try {} catch {}", body.render(), hs.join("; "))
761 }
762 Self::Section {
763 op, operand, side, ..
764 } => match (operand, side) {
765 (Some(e), SectionSide::Left) => format!("({} {})", e.render(), op),
766 (Some(e), SectionSide::Right) => format!("({} {})", op, e.render()),
767 (None, _) => format!("({op})"),
768 },
769 Self::Error { raw, .. } => raw.clone(),
770 }
771 }
772
773 fn render_atomic(&self) -> String {
776 match self {
777 Self::Var { .. }
778 | Self::Con { .. }
779 | Self::Lit { .. }
780 | Self::Tuple { .. }
781 | Self::List { .. }
782 | Self::Section { .. }
783 | Self::Error { .. } => self.render(),
784 _ => format!("({})", self.render()),
785 }
786 }
787
788 pub fn application_head(&self) -> &Self {
791 match self {
792 Self::App { func, .. } => func.application_head(),
793 _ => self,
794 }
795 }
796
797 pub fn application_args(&self) -> &[Self] {
801 match self {
802 Self::App { args, .. } => args,
803 _ => &[],
804 }
805 }
806}
807
808fn render_do_stmt(s: &DoStmt) -> String {
809 match s {
810 DoStmt::Bind { pat, expr, .. } => format!("{} <- {}", pat.render(), expr.render()),
811 DoStmt::Let { bindings, .. } => {
812 let bs: Vec<String> = bindings.iter().map(render_binding).collect();
813 format!("let {}", bs.join("; "))
814 }
815 DoStmt::Expr { expr, .. } => expr.render(),
816 }
817}
818
819fn render_binding(b: &Binding) -> String {
820 let mut s = b.pat.render();
821 for p in &b.params {
822 s.push(' ');
823 s.push_str(&p.render());
824 }
825 format!("{} = {}", s, b.expr.render())
826}
827
828impl Pat {
829 pub const fn pos(&self) -> Pos {
830 match self {
831 Self::Var { pos, .. }
832 | Self::Wild { pos, .. }
833 | Self::Con { pos, .. }
834 | Self::Tuple { pos, .. }
835 | Self::List { pos, .. }
836 | Self::Lit { pos, .. }
837 | Self::As { pos, .. }
838 | Self::Other { pos, .. } => *pos,
839 }
840 }
841
842 pub const fn span(&self) -> Span {
844 match self {
845 Self::Var { span, .. }
846 | Self::Wild { span, .. }
847 | Self::Con { span, .. }
848 | Self::Tuple { span, .. }
849 | Self::List { span, .. }
850 | Self::Lit { span, .. }
851 | Self::As { span, .. }
852 | Self::Other { span, .. } => *span,
853 }
854 }
855
856 pub fn render(&self) -> String {
860 match self {
861 Self::Var { name, .. } => name.clone(),
862 Self::Wild { .. } => "_".to_string(),
863 Self::Con {
864 qualifier,
865 name,
866 args,
867 ..
868 } => {
869 let head = qualifier
870 .as_ref()
871 .map_or_else(|| name.clone(), |q| format!("{q}.{name}"));
872 if args.is_empty() {
873 head
874 } else {
875 let parts: Vec<String> = args.iter().map(|p| p.render()).collect();
876 format!("({} {})", head, parts.join(" "))
877 }
878 }
879 Self::Tuple { items, .. } => {
880 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
881 format!("({})", xs.join(", "))
882 }
883 Self::List { items, .. } => {
884 let xs: Vec<String> = items.iter().map(|p| p.render()).collect();
885 format!("[{}]", xs.join(", "))
886 }
887 Self::Lit { kind, text, .. } => match kind {
888 LitKind::Text => format!("{text:?}"),
889 LitKind::Char => format!("'{text}'"),
890 _ => text.clone(),
891 },
892 Self::As { name, pat, .. } => format!("{}@{}", name, pat.render()),
893 Self::Other { raw, .. } => raw.clone(),
894 }
895 }
896}
897
898impl std::fmt::Display for Expr {
899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
900 f.write_str(&self.render())
901 }
902}
903
904impl std::fmt::Display for Pat {
905 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
906 f.write_str(&self.render())
907 }
908}
909
910#[cfg(test)]
911mod tests {
912 use super::*;
913
914 fn pos() -> Pos {
915 Pos { line: 1, column: 1 }
916 }
917
918 fn span(start: usize, end: usize) -> Span {
919 Span::new(start, end)
920 }
921
922 #[test]
923 fn span_distinguishes_empty_from_invalid() {
924 assert!(span(3, 3).is_valid());
925 assert!(span(3, 3).is_empty());
926
927 assert!(!span(4, 3).is_valid());
928 assert!(!span(4, 3).is_empty());
929 }
930
931 #[test]
932 fn contains_rejects_invalid_spans() {
933 let parent = span(1, 10);
934
935 assert!(parent.contains(&span(3, 7)));
936 assert!(!parent.contains(&span(7, 3)));
937 assert!(!span(10, 1).contains(&span(3, 7)));
938 }
939
940 #[test]
941 fn expr_render_keeps_normalized_application_and_projection_shape() {
942 let projection = Expr::BinOp {
943 op: ".".to_string(),
944 lhs: Box::new(Expr::Var {
945 qualifier: None,
946 name: "this".to_string(),
947 pos: pos(),
948 span: span(0, 4),
949 }),
950 rhs: Box::new(Expr::Var {
951 qualifier: None,
952 name: "note".to_string(),
953 pos: pos(),
954 span: span(5, 9),
955 }),
956 pos: pos(),
957 span: span(0, 9),
958 };
959
960 let expr = Expr::App {
961 func: Box::new(Expr::Var {
962 qualifier: None,
963 name: "length".to_string(),
964 pos: pos(),
965 span: span(0, 6),
966 }),
967 args: vec![projection],
968 pos: pos(),
969 span: span(0, 16),
970 };
971
972 assert_eq!(expr.render(), "length (this.note)");
973 }
974
975 #[test]
976 fn section_render_depends_on_section_side() {
977 let expr_left = Expr::Section {
978 op: "+".to_string(),
979 operand: Some(Box::new(Expr::Var {
980 qualifier: None,
981 name: "x".to_string(),
982 pos: pos(),
983 span: span(0, 1),
984 })),
985 side: SectionSide::Left,
986 pos: pos(),
987 span: span(0, 4),
988 };
989 let expr_right = Expr::Section {
990 op: "+".to_string(),
991 operand: Some(Box::new(Expr::Lit {
992 kind: LitKind::Int,
993 text: "1".to_string(),
994 pos: pos(),
995 span: span(0, 1),
996 })),
997 side: SectionSide::Right,
998 pos: pos(),
999 span: span(0, 4),
1000 };
1001
1002 assert_eq!(expr_left.render(), "(x +)");
1003 assert_eq!(expr_right.render(), "(+ 1)");
1004 }
1005
1006 #[test]
1007 fn pat_render_preserves_collection_shape() {
1008 let pat = Pat::Tuple {
1009 items: vec![
1010 Pat::Var {
1011 name: "owner".to_string(),
1012 pos: pos(),
1013 span: span(1, 6),
1014 },
1015 Pat::List {
1016 items: Vec::new(),
1017 pos: pos(),
1018 span: span(8, 10),
1019 },
1020 ],
1021 pos: pos(),
1022 span: span(0, 11),
1023 };
1024
1025 assert_eq!(pat.render(), "(owner, [])");
1026 }
1027}