1use std::collections::HashSet;
8use std::sync::LazyLock;
9
10use super::prelude::*;
11use crate::{
12 ast::identifiers::global_id::view::{ConstructorKind, PathSegment, TypeDefKind},
13 resugarings::BinOp,
14};
15
16mod binops {
17 pub use crate::names::rust_primitives::hax::machine_int::{add, div, mul, rem, shr, sub};
18 pub use crate::names::rust_primitives::hax::{logical_op_and, logical_op_or};
19}
20
21#[derive(Default)]
23pub struct LeanPrinter;
24impl_doc_allocator_for!(LeanPrinter);
25
26const INDENT: isize = 2;
27
28static RESERVED_KEYWORDS: LazyLock<HashSet<String>> = LazyLock::new(|| {
29 HashSet::from_iter(
30 [
31 "end",
32 "def",
33 "abbrev",
34 "theorem",
35 "example",
36 "inductive",
37 "structure",
38 "from",
39 ]
40 .iter()
41 .map(|s| s.to_string()),
42 )
43});
44
45impl RenderView for LeanPrinter {
46 fn separator(&self) -> &str {
47 "."
48 }
49 fn render_path_segment(&self, chunk: &PathSegment) -> Vec<String> {
50 fn uppercase_first(s: &str) -> String {
51 let mut c = s.chars();
52 match c.next() {
53 None => String::new(),
54 Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
55 }
56 }
57 (match chunk.kind() {
59 AnyKind::Mod => {
60 let mut chunks = default::render_path_segment(self, chunk);
61 for c in &mut chunks {
62 *c = uppercase_first(c);
63 }
64 Some(chunks)
65 }
66 AnyKind::Constructor(ConstructorKind::Constructor { ty })
67 if matches!(ty.kind(), TypeDefKind::Struct) =>
68 {
69 Some(vec![
70 self.render_path_segment_payload(chunk.payload())
71 .to_string(),
72 "mk".to_string(),
73 ])
74 }
75 AnyKind::Field { named: _, parent } => match parent.kind() {
76 ConstructorKind::Constructor { ty }
77 if matches!(&ty.kind(), TypeDefKind::Struct) =>
78 {
79 chunk.parent().map(|parent| {
80 vec![
81 self.escape(
82 self.render_path_segment_payload(parent.payload())
83 .to_string(),
84 ),
85 self.escape(
86 self.render_path_segment_payload(chunk.payload())
87 .to_string(),
88 ),
89 ]
90 })
91 }
92 _ => None,
93 },
94 _ => None,
95 })
96 .unwrap_or(default::render_path_segment(self, chunk))
97 }
98}
99
100impl Printer for LeanPrinter {
101 fn resugaring_phases() -> Vec<Box<dyn Resugaring>> {
102 vec![Box::new(BinOp::new(&[
103 binops::add,
104 binops::sub,
105 binops::mul,
106 binops::rem,
107 binops::div,
108 binops::shr,
109 binops::logical_op_and,
110 binops::logical_op_or,
111 ]))]
112 }
113}
114
115pub struct LeanBackend;
117
118impl Backend for LeanBackend {
119 type Printer = LeanPrinter;
120
121 fn module_path(&self, module: &Module) -> camino::Utf8PathBuf {
122 camino::Utf8PathBuf::from_iter(self.printer().render_strings(&module.ident.view()))
123 .with_extension("lean")
124 }
125}
126
127impl LeanPrinter {
128 pub fn printable_item(item: &Item) -> bool {
132 match &item.kind {
133 ItemKind::Fn {
135 name,
136 generics: _,
137 body: _,
138 params: _,
139 safety: _,
140 } if name.is_anonymous_const() => false,
141 ItemKind::Error(_) | ItemKind::NotImplementedYet | ItemKind::Use { .. } => false,
143 ItemKind::Fn { .. }
145 | ItemKind::TyAlias { .. }
146 | ItemKind::Type { .. }
147 | ItemKind::Trait { .. }
148 | ItemKind::Impl { .. }
149 | ItemKind::Alias { .. }
150 | ItemKind::Resugared(_)
151 | ItemKind::Quote { .. } => true,
152 }
153 }
154
155 pub fn render_id(&self, id: &GlobalId) -> String {
158 self.render_string(&id.view())
159 }
160
161 pub fn escape(&self, id: String) -> String {
165 let id = id.replace([' ', '<', '>'], "_");
166 if id.is_empty() {
167 "_ERROR_EMPTY_ID_".to_string()
168 } else if RESERVED_KEYWORDS.contains(&id) || id.starts_with(|c: char| c.is_ascii_digit()) {
169 format!("_{id}")
170 } else {
171 id
172 }
173 }
174
175 pub fn render_symbol(&self, symbol: Symbol) -> String {
177 self.escape(symbol.to_string())
178 }
179
180 pub fn render_last(&self, id: &GlobalId) -> String {
182 let id = self
183 .render(&id.view())
184 .path
185 .last()
186 .expect("Segments should always be non-empty")
189 .clone();
190 self.escape(id)
191 }
192}
193
194impl<'a, A: 'a + Clone> Pretty<'a, LeanPrinter, A> for &Vec<Param> {
196 fn pretty(self, allocator: &'a LeanPrinter) -> DocBuilder<'a, LeanPrinter, A> {
197 allocator.params(self)
198 }
199}
200
201#[prepend_associated_functions_with(install_pretty_helpers!(self: Self))]
202const _: () = {
203 #[allow(unused)]
205 macro_rules! todo {($($tt:tt)*) => {disambiguated_todo!($($tt)*)};}
206
207 macro_rules! line {($($tt:tt)*) => {disambiguated_line!($($tt)*)};}
209
210 macro_rules! concat {($($tt:tt)*) => {disambiguated_concat!($($tt)*)};}
212
213 macro_rules! zip_right {
215 ($a:expr, $sep:expr) => {
216 docs![concat!($a.into_iter().map(|a| docs![a, $sep]))]
217 };
218 }
219
220 macro_rules! zip_left {
222 ($sep:expr, $a:expr) => {
223 docs![concat!($a.into_iter().map(|a| docs![$sep, a]))]
224 };
225 }
226
227 impl LeanPrinter {
229 pub fn arguments<'a, 'b, A: 'a + Clone, D>(
232 &'a self,
233 fields: &'b [(GlobalId, D)],
234 is_record: &bool,
235 ) -> DocBuilder<'a, Self, A>
236 where
237 &'b D: Pretty<'a, Self, A>,
238 {
239 if *is_record {
240 self.named_arguments(fields)
241 } else {
242 self.positional_arguments(fields)
243 }
244 }
245
246 fn named_arguments<'a, 'b, A: 'a + Clone, D>(
248 &'a self,
249 fields: &'b [(GlobalId, D)],
250 ) -> DocBuilder<'a, Self, A>
251 where
252 &'b D: Pretty<'a, Self, A>,
253 {
254 macro_rules! line {($($tt:tt)*) => {disambiguated_line!($($tt)*)};}
255 docs![intersperse!(
256 fields.iter().map(|(id, e)| {
257 docs![self.render_last(id), reflow!(" := "), e]
258 .parens()
259 .group()
260 }),
261 line!()
262 )]
263 .group()
264 }
265
266 fn positional_arguments<'a, 'b, A: 'a + Clone, D>(
268 &'a self,
269 fields: &'b [(GlobalId, D)],
270 ) -> DocBuilder<'a, Self, A>
271 where
272 &'b D: Pretty<'a, Self, A>,
273 {
274 macro_rules! line {($($tt:tt)*) => {disambiguated_line!($($tt)*)};}
275 docs![intersperse!(fields.iter().map(|(_, e)| e), line!())].group()
276 }
277
278 fn params<'a, 'b, A: 'a + Clone>(
280 &'a self,
281 params: &'b Vec<Param>,
282 ) -> DocBuilder<'a, Self, A> {
283 zip_right!(params, line!())
284 }
285
286 fn expr_typed_result<'a, 'b, A: 'a + Clone>(
289 &'a self,
290 expr: &'b Expr,
291 ) -> DocBuilder<'a, Self, A> {
292 docs![
293 expr,
294 reflow!(" : "),
295 docs!["Result", line!(), &expr.ty].group()
296 ]
297 .group()
298 }
299
300 fn pat_typed<'a, 'b, A: 'a + Clone>(&'a self, pat: &'b Pat) -> DocBuilder<'a, Self, A> {
301 docs![pat.kind(), reflow!(" :"), line!(), &pat.ty]
302 .parens()
303 .group()
304 }
305 }
306
307 impl<'a, 'b, A: 'a + Clone> PrettyAst<'a, 'b, A> for LeanPrinter {
308 const NAME: &'static str = "Lean";
309
310 fn module(&'a self, module: &'b Module) -> DocBuilder<'a, Self, A> {
311 let items = &module.items;
312 docs![
313 intersperse!(
314 "
315-- Experimental lean backend for Hax
316-- The Hax prelude library can be found in hax/proof-libs/lean
317import Hax
318import Std.Tactic.Do
319import Std.Do.Triple
320import Std.Tactic.Do.Syntax
321open Std.Do
322open Std.Tactic
323
324set_option mvcgen.warning false
325set_option linter.unusedVariables false
326
327
328"
329 .lines(),
330 hardline!(),
331 ),
332 intersperse!(
333 items
334 .iter()
335 .filter(|item| LeanPrinter::printable_item(item)),
336 docs![hardline!(), hardline!()]
337 )
338 ]
339 }
340
341 fn global_id(&'a self, global_id: &'b GlobalId) -> DocBuilder<'a, Self, A> {
342 docs![self.render_id(global_id)]
343 }
344
345 fn generics(
347 &'a self,
348 Generics {
349 params,
350 constraints,
351 }: &'b Generics,
352 ) -> DocBuilder<'a, Self, A> {
353 docs![
356 zip_right!(params, line!()),
357 zip_right!(
358 constraints
359 .iter()
360 .map(|constraint| docs![constraint].brackets()),
361 line!()
362 ),
363 ]
364 .group()
365 }
366
367 fn generic_constraint(
368 &'a self,
369 generic_constraint: &'b GenericConstraint,
370 ) -> DocBuilder<'a, Self, A> {
371 match generic_constraint {
372 GenericConstraint::Type(impl_ident) => docs![impl_ident],
373 _ => todo!("-- unsupported constraint"),
374 }
375 }
376
377 fn generic_param(&'a self, generic_param: &'b GenericParam) -> DocBuilder<'a, Self, A> {
378 match generic_param.kind() {
379 GenericParamKind::Type => docs![&generic_param.ident, reflow!(" : Type")]
380 .parens()
381 .group(),
382 GenericParamKind::Lifetime => unreachable!(),
383 GenericParamKind::Const { .. } => {
384 todo!("-- Unsupported const param")
385 }
386 }
387 }
388
389 fn expr(&'a self, Expr { kind, ty, meta: _ }: &'b Expr) -> DocBuilder<'a, Self, A> {
390 match &**kind {
391 ExprKind::Literal(int_lit @ Literal::Int { .. }) => {
392 docs![int_lit, reflow!(" : "), ty].parens().group()
393 }
394 ExprKind::If {
395 condition,
396 then,
397 else_,
398 } => {
399 if let Some(else_branch) = else_ {
400 docs![
403 docs!["← if", line!(), condition, reflow!(" then do")].group(),
404 docs![line!(), then].nest(INDENT),
405 line!(),
406 reflow!("else do"),
407 docs![line!(), else_branch].nest(INDENT)
408 ]
409 .parens()
410 .group()
411 } else {
412 unreachable!()
414 }
415 }
416 ExprKind::App {
417 head,
418 args,
419 generic_args,
420 bounds_impls: _,
421 trait_: _,
422 } => {
423 let monadic_lift = if let ExprKind::GlobalId(head_id) = head.kind()
425 && (head_id.is_constructor() || head_id.is_projector())
426 {
427 None
428 } else {
429 Some("← ")
430 };
431 let generic_args = (!generic_args.is_empty()).then_some(
432 docs![line!(), self.intersperse(generic_args, line!())]
433 .nest(INDENT)
434 .group(),
435 );
436 let args = (!args.is_empty()).then_some(
437 docs![line!(), intersperse!(args, line!())]
438 .nest(INDENT)
439 .group(),
440 );
441 docs![monadic_lift, head, generic_args, args]
442 .nest(INDENT)
443 .parens()
444 .group()
445 }
446 ExprKind::Literal(literal) => docs![literal],
447 ExprKind::Array(exprs) => docs![
448 "#v[",
449 intersperse!(exprs, docs![",", line!()])
450 .nest(INDENT)
451 .group()
452 .align(),
453 "]"
454 ]
455 .group(),
456 ExprKind::Construct {
457 constructor,
458 is_record,
459 is_struct: _,
460 fields,
461 base,
462 } => {
463 if fields.is_empty() && base.is_none() {
464 docs![constructor]
465 } else if base.is_some() {
466 todo!("-- Unsupported base expressions for structs.")
468 } else {
469 docs![constructor, line!(), self.arguments(fields, is_record)]
470 .nest(INDENT)
471 .parens()
472 .group()
473 }
474 }
475 ExprKind::Let { lhs, rhs, body } => {
476 docs![
477 docs![
478 docs![
479 "let",
480 line!(),
481 match *lhs.kind.clone() {
484 PatKind::Binding {
485 mutable: false,
486 var,
487 mode: BindingMode::ByValue,
488 sub_pat: None,
489 } => docs![&var, reflow!(" : "), &lhs.ty],
490 _ => docs![lhs],
491 },
492 ]
493 .group(),
494 " ←",
495 softline!(),
496 docs!["pure", line!(), rhs].parens().group(),
497 ";"
498 ]
499 .nest(INDENT)
500 .group(),
501 line!(),
502 body,
503 ]
504 }
505 ExprKind::GlobalId(global_id) => docs![global_id],
506 ExprKind::LocalId(local_id) => docs![local_id],
507 ExprKind::Ascription { e, ty } => docs![
508 match *e.kind {
511 ExprKind::Literal(_) | ExprKind::Construct { .. } => None,
512 _ => Some("← "),
513 },
514 e,
515 reflow!(" : "),
516 ty
517 ]
518 .parens()
519 .group(),
520 ExprKind::Closure {
521 params,
522 body,
523 captures: _,
524 } => docs![
525 reflow!("fun "),
526 intersperse!(params, line!()).group(),
527 reflow!(" => "),
528 docs!["do", line!(), self.expr_typed_result(body)]
530 .nest(INDENT)
531 .parens()
532 .group()
533 ]
534 .parens()
535 .group()
536 .nest(INDENT),
537 ExprKind::Resugared(resugared_expr_kind) => match resugared_expr_kind {
538 ResugaredExprKind::BinOp {
539 op,
540 lhs,
541 rhs,
542 generic_args: _,
543 bounds_impls: _,
544 trait_: _,
545 } => {
546 let symbol = match *op {
547 binops::add => "+?",
548 binops::sub => "-?",
549 binops::mul => "*?",
550 binops::div => "/?",
551 binops::rem => "%?",
552 binops::shr => ">>>?",
553 binops::logical_op_and => "&&?",
554 binops::logical_op_or => "||?",
555 _ => unreachable!(),
556 };
557
558 docs!["← ", lhs, line!(), docs![symbol, softline!(), rhs].group()]
561 .group()
562 .nest(INDENT)
563 .parens()
564 }
565 ResugaredExprKind::Tuple { .. } => {
566 unreachable!("This printer doesn't use the tuple resugaring")
567 }
568 },
569 ExprKind::Match { scrutinee, arms } => docs![
570 docs![
571 "match",
572 docs![line!(), scrutinee].nest(INDENT),
573 line!(),
574 "with"
575 ]
576 .group(),
577 docs![line!(), intersperse!(arms, line!())]
578 .group()
579 .nest(INDENT),
580 ]
581 .parens()
582 .group(),
583 _ => todo!(),
584 }
585 }
586
587 fn arm(&'a self, arm: &'b Arm) -> DocBuilder<'a, Self, A> {
588 if let Some(_guard) = &arm.guard {
589 todo!()
590 } else {
591 docs![
592 reflow!("| "),
593 &*arm.pat.kind,
594 line!(),
595 docs!["=> do", line!(), &arm.body].nest(INDENT).group()
596 ]
597 .nest(INDENT)
598 .group()
599 }
600 }
601
602 fn pat(&'a self, pat: &'b Pat) -> DocBuilder<'a, Self, A> {
603 docs![pat.kind()]
604 }
605
606 fn pat_kind(&'a self, pat_kind: &'b PatKind) -> DocBuilder<'a, Self, A> {
607 match pat_kind {
608 PatKind::Wild => docs!["_"],
609 PatKind::Ascription { pat, ty: _ } => docs![&*pat.kind],
610 PatKind::Binding {
611 mutable,
612 var,
613 mode,
614 sub_pat,
615 } => match (mutable, mode, sub_pat) {
616 (false, BindingMode::ByValue, None) => docs![var],
617 _ => panic!(),
618 },
619 PatKind::Or { sub_pats } => docs![intersperse!(sub_pats, reflow!(" | "))].group(),
620 PatKind::Array { args: _ } => todo!(),
621 PatKind::Deref { sub_pat: _ } => todo!(),
622 PatKind::Constant { lit: _ } => todo!(),
623 PatKind::Construct {
624 constructor,
625 is_record,
626 is_struct,
627 fields,
628 } => {
629 if *is_struct {
630 if !*is_record {
631 docs![
633 "⟨",
634 intersperse!(
635 fields.iter().map(|field| { docs![&field.1] }),
636 docs![",", line!()]
637 )
638 .align()
639 .group(),
640 "⟩"
641 ]
642 .align()
643 .group()
644 } else {
645 docs![intersperse!(
647 fields.iter().map(|(id, pat)| {
648 docs![self.render_last(id), reflow!(" := "), pat].group()
649 }),
650 docs![",", line!()]
651 )]
652 .align()
653 .braces()
654 .group()
655 }
656 } else {
657 docs![
659 constructor,
660 line!(),
661 self.arguments(fields, is_record).align()
662 ]
663 .parens()
664 .group()
665 .nest(INDENT)
666 }
667 }
668 PatKind::Resugared(_resugared_pat_kind) => todo!(),
669 PatKind::Error(_error_node) => todo!(),
670 }
671 }
672
673 fn ty(&'a self, ty: &'b Ty) -> DocBuilder<'a, Self, A> {
674 match ty.kind() {
675 TyKind::Primitive(primitive_ty) => docs![primitive_ty],
676 TyKind::App { head, args } => {
677 if args.is_empty() {
678 docs![head]
679 } else {
680 docs![head, zip_left!(line!(), args)]
681 .parens()
682 .group()
683 .nest(INDENT)
684 }
685 }
686 TyKind::Arrow { inputs, output } => docs![
687 zip_right!(inputs, docs![line!(), reflow!("-> ")]),
688 "Result ",
689 output
690 ]
691 .group(),
692 TyKind::Param(local_id) => docs![local_id],
693 TyKind::Slice(ty) => docs!["RustSlice", line!(), ty].parens().group(),
694 TyKind::Array { ty, length } => docs!["RustArray", line!(), ty, line!(), &**length]
695 .parens()
696 .group(),
697 TyKind::AssociatedType { impl_, item } => {
698 let kind = impl_.kind();
699 match &kind {
700 ImplExprKind::Self_ => docs![self.render_last(item)],
701 _ => todo!(), }
703 }
704 _ => todo!("sorry \n-- unsupported type\n"),
705 }
706 }
707
708 fn literal(&'a self, literal: &'b Literal) -> DocBuilder<'a, Self, A> {
709 docs![match literal {
710 Literal::String(symbol) => format!("\"{symbol}\""),
711 Literal::Char(c) => format!("'{c}'"),
712 Literal::Bool(b) => format!("{b}"),
713 Literal::Int {
714 value,
715 negative,
716 kind: _,
717 } => format!("{}{value}", if *negative { "-" } else { "" }),
718 Literal::Float {
719 value: _,
720 negative: _,
721 kind: _,
722 } => todo!(),
723 }]
724 }
725
726 fn local_id(&'a self, local_id: &'b LocalId) -> DocBuilder<'a, Self, A> {
727 docs![self.escape(local_id.0.to_string())]
729 }
730
731 fn spanned_ty(&'a self, spanned_ty: &'b SpannedTy) -> DocBuilder<'a, Self, A> {
732 docs![&spanned_ty.ty]
733 }
734
735 fn primitive_ty(&'a self, primitive_ty: &'b PrimitiveTy) -> DocBuilder<'a, Self, A> {
736 match primitive_ty {
737 PrimitiveTy::Bool => docs!["Bool"],
738 PrimitiveTy::Int(int_kind) => docs![int_kind],
739 PrimitiveTy::Float(_float_kind) => todo!(),
740 PrimitiveTy::Char => docs!["Char"],
741 PrimitiveTy::Str => docs!["String"],
742 }
743 }
744
745 fn int_kind(&'a self, int_kind: &'b IntKind) -> DocBuilder<'a, Self, A> {
746 docs![match (&int_kind.signedness, &int_kind.size) {
747 (Signedness::Signed, IntSize::S8) => "i8",
748 (Signedness::Signed, IntSize::S16) => "i16",
749 (Signedness::Signed, IntSize::S32) => "i32",
750 (Signedness::Signed, IntSize::S64) => "i64",
751 (Signedness::Signed, IntSize::S128) => "i128",
752 (Signedness::Signed, IntSize::SSize) => "isize",
753 (Signedness::Unsigned, IntSize::S8) => "u8",
754 (Signedness::Unsigned, IntSize::S16) => "u16",
755 (Signedness::Unsigned, IntSize::S32) => "u32",
756 (Signedness::Unsigned, IntSize::S64) => "u64",
757 (Signedness::Unsigned, IntSize::S128) => "u128",
758 (Signedness::Unsigned, IntSize::SSize) => "usize",
759 }]
760 }
761
762 fn generic_value(&'a self, generic_value: &'b GenericValue) -> DocBuilder<'a, Self, A> {
763 match generic_value {
764 GenericValue::Ty(ty) => docs![ty],
765 GenericValue::Expr(expr) => docs![expr],
766 GenericValue::Lifetime => todo!(),
767 }
768 }
769
770 fn quote_content(&'a self, quote_content: &'b QuoteContent) -> DocBuilder<'a, Self, A> {
771 match quote_content {
772 QuoteContent::Verbatim(s) => {
773 intersperse!(s.lines().map(|x| x.to_string()), hardline!())
774 }
775 QuoteContent::Expr(expr) => docs![expr],
776 QuoteContent::Pattern(pat) => docs![pat],
777 QuoteContent::Ty(ty) => docs![ty],
778 }
779 }
780
781 fn quote(&'a self, quote: &'b Quote) -> DocBuilder<'a, Self, A> {
782 concat!["e.0]
783 }
784
785 fn param(&'a self, param: &'b Param) -> DocBuilder<'a, Self, A> {
786 self.pat_typed(¶m.pat)
787 }
788
789 fn item(
790 &'a self,
791 item @ Item {
792 ident,
793 kind,
794 meta: _,
795 }: &'b Item,
796 ) -> DocBuilder<'a, Self, A> {
797 if !LeanPrinter::printable_item(item) {
798 return nil!();
799 };
800 match kind {
801 ItemKind::Fn {
802 name,
803 generics,
804 body,
805 params,
806 safety: _,
807 } => match &*body.kind {
808 ExprKind::Literal(l) if params.is_empty() => {
811 docs!["def ", name, reflow!(" : "), &body.ty, reflow!(" := "), l].group()
812 }
813 _ => docs![
814 docs![
815 docs!["def", line!(), name].group(),
816 line!(),
817 generics,
818 params,
819 docs![": Result", line!(), &body.ty].group(),
820 line!(),
821 ":= do"
822 ]
823 .group(),
824 line!(),
825 body
826 ]
827 .group()
828 .nest(INDENT),
829 },
830 ItemKind::TyAlias {
831 name,
832 generics: _,
833 ty,
834 } => docs!["abbrev ", name, reflow!(" := "), ty].group(),
835 ItemKind::Use {
836 path: _,
837 is_external: _,
838 rename: _,
839 } => nil!(),
840 ItemKind::Quote { quote, origin: _ } => docs![quote],
841 ItemKind::NotImplementedYet => {
842 docs!["example : Unit := sorry /- unsupported by the Hax engine -/"]
843 }
844 ItemKind::Type {
845 name,
846 generics,
847 variants,
848 is_struct,
849 } => {
850 if *is_struct {
852 let Some(variant) = variants.first() else {
854 unreachable!()
856 };
857 let args = if !variant.is_record {
858 intersperse!(
860 variant.arguments.iter().enumerate().map(|(i, (_, ty, _))| {
861 docs![format!("_{i} :"), line!(), ty].group().nest(INDENT)
862 }),
863 hardline!()
864 )
865 } else {
866 intersperse!(
868 variant.arguments.iter().map(|(id, ty, _)| {
869 docs![self.render_last(id), reflow!(" : "), ty]
870 .group()
871 .nest(INDENT)
872 }),
873 hardline!()
874 )
875 };
876 docs![
877 docs![reflow!("structure "), name, line!(), generics, "where"].group(),
878 docs![hardline!(), args],
879 ]
880 .nest(INDENT)
881 .group()
882 } else {
883 let applied_name: DocBuilder<'a, Self, A> =
885 docs![name, line!(), generics].group();
886 docs![
887 docs!["inductive ", name, line!(), generics, ": Type"].group(),
888 hardline!(),
889 concat!(variants.iter().map(|variant| docs![
890 "| ",
891 docs![variant, applied_name.clone()].group().nest(INDENT),
892 hardline!()
893 ])),
894 ]
895 }
896 }
897 ItemKind::Trait {
898 name,
899 generics,
900 items,
901 } => {
902 docs![
904 docs![
905 docs![reflow!("class "), name],
906 (!generics.params.is_empty()).then_some(docs![
907 line!(),
908 intersperse!(&generics.params, line!()).group()
909 ]),
910 line!(),
911 "where"
912 ]
913 .group(),
914 hardline!(),
915 (!generics.constraints.is_empty()).then_some(docs![zip_right!(
916 generics
917 .constraints
918 .iter()
919 .map(|constraint: &GenericConstraint| {
920 match constraint {
921 GenericConstraint::Type(tc_constraint) => docs![
922 format!("_constr_{}", tc_constraint.name),
923 " :",
924 line!(),
925 constraint
926 ]
927 .group()
928 .brackets(),
929 _ => {
930 todo!("unsupported type constraint in trait definition")
931 }
932 }
933 }),
934 hardline!()
935 )]),
936 intersperse!(
937 items.iter().filter(|item| {
938 !(item.ident.is_precondition() || item.ident.is_postcondition())
941 }),
942 hardline!()
943 )
944 ]
945 .nest(INDENT)
946 }
947 ItemKind::Impl {
948 generics,
949 self_ty: _,
950 of_trait: (trait_, args),
951 items,
952 parent_bounds: _,
953 safety: _,
954 } => docs![
955 docs![
956 docs![reflow!("instance "), ident, line!(), generics, ":"].group(),
957 line!(),
958 docs![trait_, concat!(args.iter().map(|gv| docs![line!(), gv]))].group(),
959 line!(),
960 "where",
961 ]
962 .group()
963 .nest(INDENT),
964 docs![
965 hardline!(),
966 intersperse!(
967 items.iter().filter(|item| {
968 !(item.ident.is_precondition() || item.ident.is_postcondition())
971 }),
972 hardline!()
973 )
974 ]
975 .nest(INDENT),
976 ],
977 _ => todo!("-- unsupported item"),
978 }
979 }
980
981 fn trait_item(
982 &'a self,
983 TraitItem {
984 meta: _,
985 kind,
986 generics,
987 ident,
988 }: &'b TraitItem,
989 ) -> DocBuilder<'a, Self, A> {
990 let name = self.render_last(ident);
991 docs![match kind {
992 TraitItemKind::Fn(ty) => {
993 docs![name, softline!(), generics, ":", line!(), ty]
994 .group()
995 .nest(INDENT)
996 }
997 TraitItemKind::Type(constraints) => {
998 docs![
999 name.clone(),
1000 reflow!(" : Type"),
1001 concat!(constraints.iter().map(|c| docs![
1002 hardline!(),
1003 docs![format!("_constr_{}", c.name),
1004 reflow!(" :"),
1005 line!(),
1006 &c.goal
1007 ]
1008 .group()
1009 .nest(INDENT)
1010 .brackets()]))
1011 ]
1012 }
1013 _ => todo!("-- unsupported trait item"),
1014 }]
1015 }
1016
1017 fn impl_item(
1018 &'a self,
1019 ImplItem {
1020 meta: _,
1021 generics,
1022 kind,
1023 ident,
1024 }: &'b ImplItem,
1025 ) -> DocBuilder<'a, Self, A> {
1026 let name = self.render_last(ident);
1027 match kind {
1028 ImplItemKind::Type {
1029 ty,
1030 parent_bounds: _,
1031 } => docs![name, reflow!(" := "), ty],
1032 ImplItemKind::Fn { body, params } => docs![
1033 docs![
1034 name,
1035 softline!(),
1036 generics,
1037 zip_right!(params, line!()).group(),
1038 ":= do",
1039 ]
1040 .group(),
1041 line!(),
1042 body
1043 ]
1044 .group()
1045 .nest(INDENT),
1046 ImplItemKind::Resugared(_) => todo!(),
1047 }
1048 }
1049
1050 fn impl_ident(
1051 &'a self,
1052 ImplIdent { goal, name: _ }: &'b ImplIdent,
1053 ) -> DocBuilder<'a, Self, A> {
1054 docs![goal]
1055 }
1056
1057 fn trait_goal(
1058 &'a self,
1059 TraitGoal { trait_, args }: &'b TraitGoal,
1060 ) -> DocBuilder<'a, Self, A> {
1061 docs![trait_, concat!(args.iter().map(|arg| docs![line!(), arg]))]
1062 .parens()
1063 .nest(INDENT)
1064 .group()
1065 }
1066
1067 fn variant(
1068 &'a self,
1069 Variant {
1070 name,
1071 arguments,
1072 is_record,
1073 attributes: _,
1074 }: &'b Variant,
1075 ) -> DocBuilder<'a, Self, A> {
1076 docs![
1077 self.render_last(name),
1078 softline!(),
1079 if *is_record {
1081 docs![
1083 intersperse!(
1084 arguments.iter().map(|(id, ty, _)| {
1085 docs![self.render_last(id), reflow!(" : "), ty]
1086 .parens()
1087 .group()
1088 }),
1089 line!()
1090 )
1091 .align()
1092 .nest(INDENT),
1093 line!(),
1094 reflow!(": "),
1095 ]
1096 .group()
1097 } else {
1098 docs![
1100 reflow!(": "),
1101 concat!(
1102 arguments
1103 .iter()
1104 .map(|(_, ty, _)| { docs![ty, reflow!(" -> ")] })
1105 )
1106 ]
1107 }
1108 ]
1109 .group()
1110 .nest(INDENT)
1111 }
1112 }
1113};