1#[derive(Debug, Clone, PartialEq)]
7pub struct IdrisData {
8 pub name: String,
10 pub params: Vec<(String, IdrisType)>,
12 pub kind: IdrisType,
14 pub constructors: Vec<IdrisConstructor>,
16 pub visibility: Visibility,
18 pub doc: Option<String>,
20}
21impl IdrisData {
22 pub fn emit(&self, indent: usize) -> String {
24 let pad = " ".repeat(indent);
25 let mut out = String::new();
26 if let Some(doc) = &self.doc {
27 for line in doc.lines() {
28 out.push_str(&format!("{}||| {}\n", pad, line));
29 }
30 }
31 let params_str: String = self
32 .params
33 .iter()
34 .map(|(n, t)| format!(" ({} : {})", n, t))
35 .collect();
36 out.push_str(&format!(
37 "{}{}data {}{} : {} where\n",
38 pad, self.visibility, self.name, params_str, self.kind
39 ));
40 for ctor in &self.constructors {
41 if let Some(doc) = &ctor.doc {
42 out.push_str(&format!("{} ||| {}\n", pad, doc));
43 }
44 out.push_str(&format!("{} {} : {}\n", pad, ctor.name, ctor.ty));
45 }
46 out
47 }
48}
49#[derive(Debug, Clone, PartialEq)]
51pub struct IdrisRecord {
52 pub name: String,
54 pub params: Vec<(String, IdrisType)>,
56 pub kind: IdrisType,
58 pub constructor: String,
60 pub fields: Vec<(String, IdrisType)>,
62 pub visibility: Visibility,
64 pub doc: Option<String>,
66}
67impl IdrisRecord {
68 pub fn emit(&self, indent: usize) -> String {
70 let pad = " ".repeat(indent);
71 let mut out = String::new();
72 if let Some(doc) = &self.doc {
73 for line in doc.lines() {
74 out.push_str(&format!("{}||| {}\n", pad, line));
75 }
76 }
77 let params_str: String = self
78 .params
79 .iter()
80 .map(|(n, t)| format!(" ({} : {})", n, t))
81 .collect();
82 out.push_str(&format!(
83 "{}{}record {}{} : {} where\n",
84 pad, self.visibility, self.name, params_str, self.kind
85 ));
86 out.push_str(&format!("{} constructor {}\n", pad, self.constructor));
87 for (fname, ftype) in &self.fields {
88 out.push_str(&format!("{} {} : {}\n", pad, fname, ftype));
89 }
90 out
91 }
92}
93#[derive(Debug, Clone)]
95#[allow(dead_code)]
96pub struct IdrisNamespaceBlock {
97 pub name: String,
99 pub decls: Vec<IdrisDecl>,
101}
102impl IdrisNamespaceBlock {
103 #[allow(dead_code)]
105 pub fn new(name: impl Into<String>) -> Self {
106 IdrisNamespaceBlock {
107 name: name.into(),
108 decls: Vec::new(),
109 }
110 }
111 #[allow(dead_code)]
113 pub fn add(&mut self, decl: IdrisDecl) {
114 self.decls.push(decl);
115 }
116 #[allow(dead_code)]
118 pub fn emit(&self, backend: &IdrisBackend) -> String {
119 let mut out = format!("namespace {}\n", self.name);
120 for decl in &self.decls {
121 for line in backend.emit_decl(decl).lines() {
122 out.push_str(" ");
123 out.push_str(line);
124 out.push('\n');
125 }
126 }
127 out
128 }
129}
130#[derive(Debug, Clone)]
132#[allow(dead_code)]
133pub enum IdrisTactic {
134 Intro(String),
136 Intros,
138 Exact(IdrisExpr),
140 Refl,
142 Trivial,
144 Decide,
146 Rewrite(String),
148 RewriteBack(String),
150 Apply(String),
152 Cases(String),
154 Induction(String),
156 Search,
158 Auto,
160 With(String),
162 Let(String, IdrisExpr),
164 Have(String, IdrisType),
166 Focus(usize),
168 Claim(String, IdrisType),
170 Unfold(String),
172 Compute,
174 Normals,
176 Fail(String),
178 Seq(Vec<IdrisTactic>),
180}
181#[derive(Debug, Clone, PartialEq)]
183pub struct IdrisFunction {
184 pub name: String,
186 pub type_sig: IdrisType,
188 pub clauses: Vec<(Vec<IdrisPattern>, IdrisExpr)>,
190 pub totality: Totality,
192 pub visibility: Visibility,
194 pub pragmas: Vec<String>,
196 pub doc: Option<String>,
198}
199impl IdrisFunction {
200 pub fn new(name: impl Into<String>, ty: IdrisType, body: IdrisExpr) -> Self {
202 IdrisFunction {
203 name: name.into(),
204 type_sig: ty,
205 clauses: vec![(vec![], body)],
206 totality: Totality::Default,
207 visibility: Visibility::Default,
208 pragmas: vec![],
209 doc: None,
210 }
211 }
212 pub fn with_clauses(
214 name: impl Into<String>,
215 ty: IdrisType,
216 clauses: Vec<(Vec<IdrisPattern>, IdrisExpr)>,
217 ) -> Self {
218 IdrisFunction {
219 name: name.into(),
220 type_sig: ty,
221 clauses,
222 totality: Totality::Default,
223 visibility: Visibility::Default,
224 pragmas: vec![],
225 doc: None,
226 }
227 }
228 pub fn emit(&self, indent: usize) -> String {
230 let pad = " ".repeat(indent);
231 let mut out = String::new();
232 if let Some(doc) = &self.doc {
233 for line in doc.lines() {
234 out.push_str(&format!("{}||| {}\n", pad, line));
235 }
236 }
237 for pragma in &self.pragmas {
238 out.push_str(&format!("{}%{}\n", pad, pragma));
239 }
240 let tot = format!("{}", self.totality);
241 if !tot.is_empty() {
242 out.push_str(&format!("{}{}", pad, tot));
243 }
244 out.push_str(&format!(
245 "{}{}{} : {}\n",
246 pad, self.visibility, self.name, self.type_sig
247 ));
248 for (pats, rhs) in &self.clauses {
249 if pats.is_empty() {
250 out.push_str(&format!("{}{} = {}\n", pad, self.name, rhs));
251 } else {
252 let pat_str = pats
253 .iter()
254 .map(|p| format!("{}", p))
255 .collect::<Vec<_>>()
256 .join(" ");
257 out.push_str(&format!("{}{} {} = {}\n", pad, self.name, pat_str, rhs));
258 }
259 }
260 out
261 }
262}
263#[derive(Debug, Clone, PartialEq)]
265pub struct IdrisImport {
266 pub path: Vec<String>,
268 pub alias: Option<String>,
270 pub public: bool,
272}
273impl IdrisImport {
274 pub fn new(path: Vec<String>) -> Self {
276 IdrisImport {
277 path,
278 alias: None,
279 public: false,
280 }
281 }
282 pub fn public_import(path: Vec<String>) -> Self {
284 IdrisImport {
285 path,
286 alias: None,
287 public: true,
288 }
289 }
290 pub fn emit(&self) -> String {
292 let path_str = self.path.join(".");
293 let prefix = if self.public {
294 "public import "
295 } else {
296 "import "
297 };
298 if let Some(alias) = &self.alias {
299 format!("{}{} as {}", prefix, path_str, alias)
300 } else {
301 format!("{}{}", prefix, path_str)
302 }
303 }
304}
305#[allow(dead_code)]
307pub struct IdrisPatternBuilder;
308impl IdrisPatternBuilder {
309 #[allow(dead_code)]
311 pub fn con(name: impl Into<String>, args: Vec<IdrisPattern>) -> IdrisPattern {
312 IdrisPattern::Con(name.into(), args)
313 }
314 #[allow(dead_code)]
316 pub fn var(name: impl Into<String>) -> IdrisPattern {
317 IdrisPattern::Var(name.into())
318 }
319 #[allow(dead_code)]
321 pub fn wildcard() -> IdrisPattern {
322 IdrisPattern::Wildcard
323 }
324 #[allow(dead_code)]
326 pub fn lit(l: IdrisLiteral) -> IdrisPattern {
327 IdrisPattern::Lit(l)
328 }
329 #[allow(dead_code)]
331 pub fn tuple(pats: Vec<IdrisPattern>) -> IdrisPattern {
332 IdrisPattern::Tuple(pats)
333 }
334 #[allow(dead_code)]
336 pub fn as_pat(name: impl Into<String>, pat: IdrisPattern) -> IdrisPattern {
337 IdrisPattern::As(name.into(), Box::new(pat))
338 }
339}
340#[derive(Debug, Clone, PartialEq)]
342pub enum IdrisType {
343 Type,
345 Integer,
347 Nat,
349 Bool,
351 String,
353 Char,
355 Double,
357 List(Box<IdrisType>),
359 Vect(Box<IdrisExpr>, Box<IdrisType>),
361 Pair(Box<IdrisType>, Box<IdrisType>),
363 Unit,
365 Function(Box<IdrisType>, Box<IdrisType>),
367 Linear(Box<IdrisType>, Box<IdrisType>),
369 Erased(Box<IdrisType>, Box<IdrisType>),
371 Pi(String, Box<IdrisType>, Box<IdrisType>),
373 Data(String, Vec<IdrisType>),
375 Interface(String, Vec<IdrisType>),
377 Var(String),
379 IO(Box<IdrisType>),
381 Maybe(Box<IdrisType>),
383 Either(Box<IdrisType>, Box<IdrisType>),
385}
386impl IdrisType {
387 pub(super) fn needs_parens(&self) -> bool {
389 matches!(
390 self,
391 IdrisType::Function(_, _)
392 | IdrisType::Linear(_, _)
393 | IdrisType::Erased(_, _)
394 | IdrisType::Pi(_, _, _)
395 | IdrisType::Interface(_, _)
396 ) || matches!(self, IdrisType::Data(_, args) if ! args.is_empty())
397 }
398 pub(super) fn fmt_parens(&self) -> String {
400 if self.needs_parens() {
401 format!("({})", self)
402 } else {
403 format!("{}", self)
404 }
405 }
406}
407#[derive(Debug, Clone, PartialEq)]
409pub struct IdrisConstructor {
410 pub name: String,
412 pub ty: IdrisType,
414 pub doc: Option<String>,
416}
417#[derive(Debug, Clone, PartialEq)]
419pub struct IdrisInterface {
420 pub name: String,
422 pub constraints: Vec<(String, Vec<IdrisType>)>,
424 pub params: Vec<(String, IdrisType)>,
426 pub methods: Vec<(String, IdrisType)>,
428 pub defaults: Vec<IdrisFunction>,
430 pub visibility: Visibility,
432 pub doc: Option<String>,
434}
435impl IdrisInterface {
436 pub fn emit(&self, indent: usize) -> String {
438 let pad = " ".repeat(indent);
439 let mut out = String::new();
440 if let Some(doc) = &self.doc {
441 for line in doc.lines() {
442 out.push_str(&format!("{}||| {}\n", pad, line));
443 }
444 }
445 let constraints_str: String = self
446 .constraints
447 .iter()
448 .map(|(c, args)| {
449 if args.is_empty() {
450 c.clone()
451 } else {
452 format!(
453 "{} {}",
454 c,
455 args.iter()
456 .map(|a| format!("{}", a))
457 .collect::<Vec<_>>()
458 .join(" ")
459 )
460 }
461 })
462 .collect::<Vec<_>>()
463 .join(", ");
464 let params_str: String = self
465 .params
466 .iter()
467 .map(|(n, t)| format!(" ({} : {})", n, t))
468 .collect();
469 if constraints_str.is_empty() {
470 out.push_str(&format!(
471 "{}{}interface {}{} where\n",
472 pad, self.visibility, self.name, params_str
473 ));
474 } else {
475 out.push_str(&format!(
476 "{}{}interface {} => {}{} where\n",
477 pad, self.visibility, constraints_str, self.name, params_str
478 ));
479 }
480 for (mname, mtype) in &self.methods {
481 out.push_str(&format!("{} {} : {}\n", pad, mname, mtype));
482 }
483 for default in &self.defaults {
484 out.push_str(&default.emit(indent + 2));
485 }
486 out
487 }
488}
489#[derive(Debug, Clone)]
491#[allow(dead_code)]
492pub struct IdrisParametersBlock {
493 pub params: Vec<(String, IdrisType)>,
495 pub decls: Vec<IdrisDecl>,
497}
498impl IdrisParametersBlock {
499 #[allow(dead_code)]
501 pub fn new(params: Vec<(String, IdrisType)>) -> Self {
502 IdrisParametersBlock {
503 params,
504 decls: Vec::new(),
505 }
506 }
507 #[allow(dead_code)]
509 pub fn add(&mut self, decl: IdrisDecl) {
510 self.decls.push(decl);
511 }
512 #[allow(dead_code)]
514 pub fn emit(&self, backend: &IdrisBackend) -> String {
515 let params_str: Vec<String> = self
516 .params
517 .iter()
518 .map(|(n, t)| format!("({} : {})", n, t))
519 .collect();
520 let mut out = format!("parameters {}\n", params_str.join(" "));
521 for decl in &self.decls {
522 for line in backend.emit_decl(decl).lines() {
523 out.push_str(" ");
524 out.push_str(line);
525 out.push('\n');
526 }
527 }
528 out
529 }
530}
531#[derive(Debug, Clone)]
533#[allow(dead_code)]
534pub struct IdrisImplementation {
535 pub name: Option<String>,
537 pub interface: String,
539 pub type_args: Vec<IdrisType>,
541 pub constraints: Vec<IdrisType>,
543 pub clauses: Vec<IdrisFunction>,
545 pub visibility: Visibility,
547}
548impl IdrisImplementation {
549 #[allow(dead_code)]
551 pub fn new(interface: impl Into<String>, type_args: Vec<IdrisType>) -> Self {
552 IdrisImplementation {
553 name: None,
554 interface: interface.into(),
555 type_args,
556 constraints: Vec::new(),
557 clauses: Vec::new(),
558 visibility: Visibility::Default,
559 }
560 }
561 #[allow(dead_code)]
563 pub fn add_method(&mut self, func: IdrisFunction) {
564 self.clauses.push(func);
565 }
566 #[allow(dead_code)]
568 pub fn emit(&self, backend: &IdrisBackend) -> String {
569 let name_part = self
570 .name
571 .as_ref()
572 .map(|n| format!("[{}] ", n))
573 .unwrap_or_default();
574 let constraints_str = if self.constraints.is_empty() {
575 String::new()
576 } else {
577 let cs: Vec<String> = self.constraints.iter().map(|c| format!("{}", c)).collect();
578 format!("{} => ", cs.join(", "))
579 };
580 let type_args_str: Vec<String> = self.type_args.iter().map(|t| format!("{}", t)).collect();
581 let mut out = format!(
582 "{}implementation {}{}{} {} where\n",
583 self.visibility,
584 name_part,
585 constraints_str,
586 self.interface,
587 type_args_str.join(" "),
588 );
589 for clause in &self.clauses {
590 let decl = IdrisDecl::Func(clause.clone());
591 for line in backend.emit_decl(&decl).lines() {
592 out.push_str(" ");
593 out.push_str(line);
594 out.push('\n');
595 }
596 }
597 out
598 }
599}
600#[derive(Debug, Clone, PartialEq, Eq)]
602#[allow(dead_code)]
603pub enum IdrisPragma {
604 Name(String, Vec<String>),
606 AutoImplicit,
608 DefaultTotal,
610 DefaultPartial,
612 DefaultCovering,
614 Inline,
616 NoInline,
618 Hint,
620 Extern,
622 Builtin(String),
624 Foreign { backend: String, impl_str: String },
626 Transform(String),
628 Deprecate(Option<String>),
630 Hide(String),
632 UnboundImplicitsOff,
634 AmbiguityDepth(u32),
636 SearchTimeout(u32),
638 Logging { topic: String, level: u32 },
640 Language(String),
642 RunElab(String),
644}
645#[derive(Debug, Clone, PartialEq)]
647pub enum IdrisPattern {
648 Wildcard,
650 Var(String),
652 Con(String, Vec<IdrisPattern>),
654 Lit(IdrisLiteral),
656 Tuple(Vec<IdrisPattern>),
658 Nil,
660 Cons(Box<IdrisPattern>, Box<IdrisPattern>),
662 As(String, Box<IdrisPattern>),
664 Implicit(Box<IdrisPattern>),
666 Dot(Box<IdrisExpr>),
668}
669#[derive(Debug, Clone, PartialEq)]
671pub enum IdrisDecl {
672 Func(IdrisFunction),
674 Data(IdrisData),
676 Record(IdrisRecord),
678 Interface(IdrisInterface),
680 Implementation(IdrisImpl),
682 Namespace(String, Vec<IdrisDecl>),
684 Mutual(Vec<IdrisDecl>),
686 Parameters(Vec<(String, IdrisType)>, Vec<IdrisDecl>),
688 Pragma(String),
690 Postulate(String, IdrisType, Visibility),
692 Comment(String),
694 Blank,
696}
697impl IdrisDecl {
698 pub fn emit(&self, indent: usize) -> String {
700 let pad = " ".repeat(indent);
701 match self {
702 IdrisDecl::Func(f) => f.emit(indent),
703 IdrisDecl::Data(d) => d.emit(indent),
704 IdrisDecl::Record(r) => r.emit(indent),
705 IdrisDecl::Interface(i) => i.emit(indent),
706 IdrisDecl::Implementation(im) => im.emit(indent),
707 IdrisDecl::Namespace(name, decls) => {
708 let mut out = format!("{}namespace {} where\n", pad, name);
709 for d in decls {
710 out.push_str(&d.emit(indent + 2));
711 }
712 out
713 }
714 IdrisDecl::Mutual(decls) => {
715 let mut out = format!("{}mutual\n", pad);
716 for d in decls {
717 out.push_str(&d.emit(indent + 2));
718 }
719 out
720 }
721 IdrisDecl::Parameters(params, decls) => {
722 let params_str: String = params
723 .iter()
724 .map(|(n, t)| format!("({} : {})", n, t))
725 .collect::<Vec<_>>()
726 .join(" ");
727 let mut out = format!("{}parameters {}\n", pad, params_str);
728 for d in decls {
729 out.push_str(&d.emit(indent + 2));
730 }
731 out
732 }
733 IdrisDecl::Pragma(s) => format!("{}%{}\n", pad, s),
734 IdrisDecl::Postulate(name, ty, vis) => {
735 format!("{}{}postulate {} : {}\n", pad, vis, name, ty)
736 }
737 IdrisDecl::Comment(s) => format!("{}-- {}\n", pad, s),
738 IdrisDecl::Blank => String::from("\n"),
739 }
740 }
741}
742#[allow(dead_code)]
744pub struct IdrisStdlibSnippets;
745impl IdrisStdlibSnippets {
746 #[allow(dead_code)]
748 pub fn map_maybe_fn() -> IdrisFunction {
749 IdrisFunction::with_clauses(
750 "mapMaybe",
751 IdrisTypeBuilder::arrow(vec![
752 IdrisTypeBuilder::arrow(vec![
753 IdrisType::Var("a".to_string()),
754 IdrisTypeBuilder::maybe(IdrisType::Var("b".to_string())),
755 ]),
756 IdrisTypeBuilder::list(IdrisType::Var("a".to_string())),
757 IdrisTypeBuilder::list(IdrisType::Var("b".to_string())),
758 ]),
759 vec![
760 (
761 vec![
762 IdrisPatternBuilder::wildcard(),
763 IdrisPatternBuilder::con("Nil", vec![]),
764 ],
765 IdrisExpr::Var("Nil".to_string()),
766 ),
767 (
768 vec![
769 IdrisPatternBuilder::var("f"),
770 IdrisPatternBuilder::con(
771 "::",
772 vec![
773 IdrisPatternBuilder::var("x"),
774 IdrisPatternBuilder::var("xs"),
775 ],
776 ),
777 ],
778 IdrisExprBuilder::case_of(
779 IdrisExprBuilder::app_chain(
780 IdrisExpr::Var("f".to_string()),
781 vec![IdrisExpr::Var("x".to_string())],
782 ),
783 vec![
784 (
785 IdrisPatternBuilder::con("Nothing", vec![]),
786 IdrisExprBuilder::app_chain(
787 IdrisExpr::Var("mapMaybe".to_string()),
788 vec![
789 IdrisExpr::Var("f".to_string()),
790 IdrisExpr::Var("xs".to_string()),
791 ],
792 ),
793 ),
794 (
795 IdrisPatternBuilder::con(
796 "Just",
797 vec![IdrisPatternBuilder::var("y")],
798 ),
799 IdrisExprBuilder::app_chain(
800 IdrisExpr::Var("::".to_string()),
801 vec![
802 IdrisExpr::Var("y".to_string()),
803 IdrisExprBuilder::app_chain(
804 IdrisExpr::Var("mapMaybe".to_string()),
805 vec![
806 IdrisExpr::Var("f".to_string()),
807 IdrisExpr::Var("xs".to_string()),
808 ],
809 ),
810 ],
811 ),
812 ),
813 ],
814 ),
815 ),
816 ],
817 )
818 }
819 #[allow(dead_code)]
821 pub fn foldr_fn() -> String {
822 "foldr : (a -> b -> b) -> b -> List a -> b\nfoldr f z [] = z\nfoldr f z (x :: xs) = f x (foldr f z xs)\n"
823 .to_string()
824 }
825 #[allow(dead_code)]
827 pub fn zip_fn() -> String {
828 "zip : List a -> List b -> List (a, b)\nzip [] _ = []\nzip _ [] = []\nzip (x :: xs) (y :: ys) = (x, y) :: zip xs ys\n"
829 .to_string()
830 }
831 #[allow(dead_code)]
833 pub fn nub_fn() -> String {
834 "nub : DecEq a => List a -> List a\nnub [] = []\nnub (x :: xs) = x :: nub (filter (/= x) xs)\n"
835 .to_string()
836 }
837 #[allow(dead_code)]
839 pub fn show_nat_instance(type_name: &str) -> String {
840 format!("Show {} where\n show x = show (toNat x)\n", type_name)
841 }
842}
843#[derive(Debug, Clone, PartialEq)]
845pub enum IdrisLiteral {
846 Int(i64),
848 Nat(u64),
850 Float(f64),
852 Char(char),
854 Str(String),
856 True,
858 False,
860 Unit,
862}
863#[derive(Debug, Clone, PartialEq)]
865pub enum IdrisExpr {
866 Lit(IdrisLiteral),
868 Var(String),
870 Qualified(Vec<String>),
872 App(Box<IdrisExpr>, Box<IdrisExpr>),
874 Lam(Vec<String>, Box<IdrisExpr>),
876 Let(String, Box<IdrisExpr>, Box<IdrisExpr>),
878 CaseOf(Box<IdrisExpr>, Vec<(IdrisPattern, IdrisExpr)>),
880 IfThenElse(Box<IdrisExpr>, Box<IdrisExpr>, Box<IdrisExpr>),
882 Do(Vec<IdrisDoStmt>),
884 Tuple(Vec<IdrisExpr>),
886 ListLit(Vec<IdrisExpr>),
888 Annot(Box<IdrisExpr>, Box<IdrisType>),
890 Idiom(Box<IdrisExpr>),
892 ProofTerm(Box<IdrisExpr>),
894 WithPattern(Box<IdrisExpr>, Vec<(IdrisPattern, IdrisExpr)>),
896 Infix(String, Box<IdrisExpr>, Box<IdrisExpr>),
898 Hole(String),
900 Refl,
902 AnonHole,
904 RecordUpdate(Box<IdrisExpr>, Vec<(String, IdrisExpr)>),
906 Neg(Box<IdrisExpr>),
908}
909impl IdrisExpr {
910 pub(super) fn needs_parens(&self) -> bool {
912 matches!(
913 self,
914 IdrisExpr::App(_, _)
915 | IdrisExpr::Lam(_, _)
916 | IdrisExpr::Let(_, _, _)
917 | IdrisExpr::CaseOf(_, _)
918 | IdrisExpr::IfThenElse(_, _, _)
919 | IdrisExpr::Do(_)
920 | IdrisExpr::Infix(_, _, _)
921 | IdrisExpr::Annot(_, _)
922 | IdrisExpr::Neg(_)
923 )
924 }
925 pub(super) fn fmt_arg(&self) -> String {
927 if self.needs_parens() {
928 format!("({})", self)
929 } else {
930 format!("{}", self)
931 }
932 }
933}
934#[derive(Debug, Clone, PartialEq)]
936pub struct IdrisImpl {
937 pub impl_name: Option<String>,
939 pub constraints: Vec<(String, Vec<IdrisType>)>,
941 pub interface: String,
943 pub args: Vec<IdrisType>,
945 pub methods: Vec<IdrisFunction>,
947 pub visibility: Visibility,
949}
950impl IdrisImpl {
951 pub fn emit(&self, indent: usize) -> String {
953 let pad = " ".repeat(indent);
954 let mut out = String::new();
955 let constraints_str: String = self
956 .constraints
957 .iter()
958 .map(|(c, args)| {
959 if args.is_empty() {
960 c.clone()
961 } else {
962 format!(
963 "{} {}",
964 c,
965 args.iter()
966 .map(|a| format!("{}", a))
967 .collect::<Vec<_>>()
968 .join(" ")
969 )
970 }
971 })
972 .collect::<Vec<_>>()
973 .join(", ");
974 let args_str: String = self.args.iter().map(|a| format!(" {}", a)).collect();
975 let name_str = self
976 .impl_name
977 .as_ref()
978 .map(|n| format!(" [{}]", n))
979 .unwrap_or_default();
980 if constraints_str.is_empty() {
981 out.push_str(&format!(
982 "{}{}implementation{} {}{} where\n",
983 pad, self.visibility, name_str, self.interface, args_str
984 ));
985 } else {
986 out.push_str(&format!(
987 "{}{}implementation{} {} => {}{} where\n",
988 pad, self.visibility, name_str, constraints_str, self.interface, args_str
989 ));
990 }
991 for method in &self.methods {
992 out.push_str(&method.emit(indent + 2));
993 }
994 out
995 }
996}
997#[derive(Debug, Default)]
1001pub struct IdrisBackend {
1002 pub add_header: bool,
1004 pub default_total: bool,
1006 pub auto_implicit: bool,
1008 pub options: IdrisBackendOptions,
1010}
1011impl IdrisBackend {
1012 pub fn new() -> Self {
1014 IdrisBackend {
1015 add_header: false,
1016 default_total: false,
1017 auto_implicit: false,
1018 options: IdrisBackendOptions {
1019 blank_between_decls: true,
1020 emit_docs: true,
1021 },
1022 }
1023 }
1024 pub fn proof_mode() -> Self {
1026 IdrisBackend {
1027 add_header: true,
1028 default_total: true,
1029 auto_implicit: true,
1030 options: IdrisBackendOptions {
1031 blank_between_decls: true,
1032 emit_docs: true,
1033 },
1034 }
1035 }
1036 pub fn emit_module(&self, module: &IdrisModule) -> String {
1038 let mut out = String::new();
1039 if self.add_header {
1040 out.push_str("-- AUTO-GENERATED by OxiLean\n\n");
1041 }
1042 if let Some(doc) = &module.doc {
1043 for line in doc.lines() {
1044 out.push_str(&format!("||| {}\n", line));
1045 }
1046 }
1047 let module_str = module.module_name.join(".");
1048 out.push_str(&format!("module {}\n\n", module_str));
1049 if self.default_total {
1050 out.push_str("%default total\n\n");
1051 }
1052 if self.auto_implicit {
1053 out.push_str("%auto_implicit_depth 50\n\n");
1054 }
1055 for imp in &module.imports {
1056 out.push_str(&imp.emit());
1057 out.push('\n');
1058 }
1059 if !module.imports.is_empty() {
1060 out.push('\n');
1061 }
1062 for decl in &module.declarations {
1063 let s = decl.emit(0);
1064 out.push_str(&s);
1065 if self.options.blank_between_decls
1066 && !matches!(decl, IdrisDecl::Blank | IdrisDecl::Comment(_))
1067 {
1068 out.push('\n');
1069 }
1070 }
1071 out
1072 }
1073 pub fn emit_decl(&self, decl: &IdrisDecl) -> String {
1075 decl.emit(0)
1076 }
1077 pub fn emit_function(&self, func: &IdrisFunction) -> String {
1079 func.emit(0)
1080 }
1081 pub fn emit_data(&self, data: &IdrisData) -> String {
1083 data.emit(0)
1084 }
1085 pub fn emit_record(&self, rec: &IdrisRecord) -> String {
1087 rec.emit(0)
1088 }
1089 pub fn emit_interface(&self, iface: &IdrisInterface) -> String {
1091 iface.emit(0)
1092 }
1093 pub fn emit_impl(&self, im: &IdrisImpl) -> String {
1095 im.emit(0)
1096 }
1097}
1098#[derive(Debug, Clone, PartialEq, Eq)]
1100pub enum Visibility {
1101 PublicExport,
1103 Export,
1105 Private,
1107 Default,
1109}
1110#[derive(Debug, Clone, PartialEq, Eq)]
1112pub enum Totality {
1113 Total,
1115 Partial,
1117 Covering,
1119 Default,
1121}
1122#[derive(Debug, Clone, PartialEq)]
1124pub enum IdrisDoStmt {
1125 Bind(String, IdrisExpr),
1127 Let(String, IdrisExpr),
1129 Expr(IdrisExpr),
1131 LetTyped(String, IdrisType, IdrisExpr),
1133 Ignore(IdrisExpr),
1135}
1136#[derive(Debug, Default, Clone)]
1138pub struct IdrisBackendOptions {
1139 pub blank_between_decls: bool,
1141 pub emit_docs: bool,
1143}
1144#[derive(Debug, Clone)]
1146#[allow(dead_code)]
1147pub struct IdrisInterfaceExt {
1148 pub name: String,
1150 pub params: Vec<(String, IdrisType)>,
1152 pub constraints: Vec<IdrisType>,
1154 pub methods: Vec<(String, IdrisType, Option<IdrisExpr>)>,
1156 pub visibility: Visibility,
1158 pub doc: Option<String>,
1160}
1161impl IdrisInterfaceExt {
1162 #[allow(dead_code)]
1164 pub fn new(name: impl Into<String>, params: Vec<(String, IdrisType)>) -> Self {
1165 IdrisInterfaceExt {
1166 name: name.into(),
1167 params,
1168 constraints: Vec::new(),
1169 methods: Vec::new(),
1170 visibility: Visibility::PublicExport,
1171 doc: None,
1172 }
1173 }
1174 #[allow(dead_code)]
1176 pub fn add_method(&mut self, name: impl Into<String>, ty: IdrisType) {
1177 self.methods.push((name.into(), ty, None));
1178 }
1179 #[allow(dead_code)]
1181 pub fn add_method_with_default(
1182 &mut self,
1183 name: impl Into<String>,
1184 ty: IdrisType,
1185 default: IdrisExpr,
1186 ) {
1187 self.methods.push((name.into(), ty, Some(default)));
1188 }
1189 #[allow(dead_code)]
1191 pub fn emit(&self) -> String {
1192 let mut out = String::new();
1193 if let Some(doc) = &self.doc {
1194 for line in doc.lines() {
1195 out.push_str(&format!("||| {}\n", line));
1196 }
1197 }
1198 let params_str: Vec<String> = self
1199 .params
1200 .iter()
1201 .map(|(n, t)| format!("({} : {})", n, t))
1202 .collect();
1203 let constraints_str = if self.constraints.is_empty() {
1204 String::new()
1205 } else {
1206 let cs: Vec<String> = self.constraints.iter().map(|c| format!("{}", c)).collect();
1207 format!("{} => ", cs.join(", "))
1208 };
1209 out.push_str(&format!(
1210 "{}interface {}{} {} where\n",
1211 self.visibility,
1212 constraints_str,
1213 self.name,
1214 params_str.join(" ")
1215 ));
1216 for (mname, mty, mdefault) in &self.methods {
1217 out.push_str(&format!(" {} : {}\n", mname, mty));
1218 if let Some(def) = mdefault {
1219 out.push_str(&format!(" {} _ = {}\n", mname, def));
1220 }
1221 }
1222 out
1223 }
1224}
1225#[allow(dead_code)]
1227pub struct IdrisTypeBuilder;
1228impl IdrisTypeBuilder {
1229 #[allow(dead_code)]
1231 pub fn arrow(types: Vec<IdrisType>) -> IdrisType {
1232 assert!(!types.is_empty(), "arrow type must have at least one type");
1233 let mut it = types.into_iter().rev();
1234 let mut result = it
1235 .next()
1236 .expect("types is non-empty; guaranteed by assert above");
1237 for ty in it {
1238 result = IdrisType::Function(Box::new(ty), Box::new(result));
1239 }
1240 result
1241 }
1242 #[allow(dead_code)]
1244 pub fn app(head: impl Into<String>, args: Vec<IdrisType>) -> IdrisType {
1245 IdrisType::Data(head.into(), args)
1246 }
1247 #[allow(dead_code)]
1249 pub fn vect(n_expr: impl Into<String>, elem_ty: IdrisType) -> IdrisType {
1250 IdrisType::Data(
1251 "Vect".to_string(),
1252 vec![IdrisType::Var(n_expr.into()), elem_ty],
1253 )
1254 }
1255 #[allow(dead_code)]
1257 pub fn list(elem_ty: IdrisType) -> IdrisType {
1258 IdrisType::List(Box::new(elem_ty))
1259 }
1260 #[allow(dead_code)]
1262 pub fn maybe(ty: IdrisType) -> IdrisType {
1263 IdrisType::Data("Maybe".to_string(), vec![ty])
1264 }
1265 #[allow(dead_code)]
1267 pub fn either(left: IdrisType, right: IdrisType) -> IdrisType {
1268 IdrisType::Data("Either".to_string(), vec![left, right])
1269 }
1270 #[allow(dead_code)]
1272 pub fn pair(a: IdrisType, b: IdrisType) -> IdrisType {
1273 IdrisType::Data("Pair".to_string(), vec![a, b])
1274 }
1275 #[allow(dead_code)]
1277 pub fn io(ty: IdrisType) -> IdrisType {
1278 IdrisType::Data("IO".to_string(), vec![ty])
1279 }
1280 #[allow(dead_code)]
1282 pub fn nat() -> IdrisType {
1283 IdrisType::Nat
1284 }
1285 #[allow(dead_code)]
1287 pub fn bool() -> IdrisType {
1288 IdrisType::Bool
1289 }
1290 #[allow(dead_code)]
1292 pub fn string() -> IdrisType {
1293 IdrisType::String
1294 }
1295 #[allow(dead_code)]
1297 pub fn integer() -> IdrisType {
1298 IdrisType::Integer
1299 }
1300 #[allow(dead_code)]
1302 pub fn pi(param: impl Into<String>, domain: IdrisType, codomain: IdrisType) -> IdrisType {
1303 IdrisType::Pi(param.into(), Box::new(domain), Box::new(codomain))
1304 }
1305}
1306#[derive(Debug)]
1308#[allow(dead_code)]
1309pub struct IdrisModuleBuilder {
1310 pub(super) module: IdrisModule,
1311}
1312impl IdrisModuleBuilder {
1313 #[allow(dead_code)]
1315 pub fn new(parts: Vec<String>) -> Self {
1316 IdrisModuleBuilder {
1317 module: IdrisModule::new(parts),
1318 }
1319 }
1320 #[allow(dead_code)]
1322 pub fn import(mut self, imp: IdrisImport) -> Self {
1323 self.module.import(imp);
1324 self
1325 }
1326 #[allow(dead_code)]
1328 pub fn public_import(mut self, parts: Vec<String>) -> Self {
1329 self.module.import(IdrisImport::public_import(parts));
1330 self
1331 }
1332 #[allow(dead_code)]
1334 pub fn decl(mut self, decl: IdrisDecl) -> Self {
1335 self.module.add(decl);
1336 self
1337 }
1338 #[allow(dead_code)]
1340 pub fn pragma(mut self, pragma: IdrisPragma) -> Self {
1341 self.module.add(IdrisDecl::Pragma(format!("{}", pragma)));
1342 self
1343 }
1344 #[allow(dead_code)]
1346 pub fn comment(mut self, text: impl Into<String>) -> Self {
1347 self.module.add(IdrisDecl::Comment(text.into()));
1348 self
1349 }
1350 #[allow(dead_code)]
1352 pub fn build(self) -> IdrisModule {
1353 self.module
1354 }
1355}
1356#[allow(dead_code)]
1358pub struct IdrisExprBuilder;
1359impl IdrisExprBuilder {
1360 #[allow(dead_code)]
1362 pub fn app_chain(func: IdrisExpr, args: Vec<IdrisExpr>) -> IdrisExpr {
1363 args.into_iter().fold(func, |acc, arg| {
1364 IdrisExpr::App(Box::new(acc), Box::new(arg))
1365 })
1366 }
1367 #[allow(dead_code)]
1369 pub fn case_of(scrutinee: IdrisExpr, alts: Vec<(IdrisPattern, IdrisExpr)>) -> IdrisExpr {
1370 IdrisExpr::CaseOf(Box::new(scrutinee), alts)
1371 }
1372 #[allow(dead_code)]
1374 pub fn let_chain(bindings: Vec<(String, IdrisExpr)>, body: IdrisExpr) -> IdrisExpr {
1375 bindings.into_iter().rev().fold(body, |acc, (name, val)| {
1376 IdrisExpr::Let(name, Box::new(val), Box::new(acc))
1377 })
1378 }
1379 #[allow(dead_code)]
1381 pub fn lam(params: Vec<impl Into<String>>, body: IdrisExpr) -> IdrisExpr {
1382 IdrisExpr::Lam(
1383 params.into_iter().map(|p| p.into()).collect(),
1384 Box::new(body),
1385 )
1386 }
1387 #[allow(dead_code)]
1389 pub fn do_block(stmts: Vec<IdrisDoStmt>) -> IdrisExpr {
1390 IdrisExpr::Do(stmts)
1391 }
1392 #[allow(dead_code)]
1394 pub fn if_then_else(cond: IdrisExpr, then_e: IdrisExpr, else_e: IdrisExpr) -> IdrisExpr {
1395 IdrisExpr::IfThenElse(Box::new(cond), Box::new(then_e), Box::new(else_e))
1396 }
1397 #[allow(dead_code)]
1399 pub fn tuple(elems: Vec<IdrisExpr>) -> IdrisExpr {
1400 IdrisExpr::Tuple(elems)
1401 }
1402 #[allow(dead_code)]
1404 pub fn list_lit(elems: Vec<IdrisExpr>) -> IdrisExpr {
1405 IdrisExpr::ListLit(elems)
1406 }
1407 #[allow(dead_code)]
1409 pub fn annot(expr: IdrisExpr, ty: IdrisType) -> IdrisExpr {
1410 IdrisExpr::Annot(Box::new(expr), Box::new(ty))
1411 }
1412 #[allow(dead_code)]
1414 pub fn infix(op: impl Into<String>, l: IdrisExpr, r: IdrisExpr) -> IdrisExpr {
1415 IdrisExpr::Infix(op.into(), Box::new(l), Box::new(r))
1416 }
1417}
1418#[derive(Debug, Clone, PartialEq, Eq)]
1420#[allow(dead_code)]
1421pub enum IdrisAttribute {
1422 Auto,
1424 Interface,
1426 Search,
1428 Totality(Totality),
1430 Inline,
1432 Static,
1434}
1435#[derive(Debug, Clone)]
1437#[allow(dead_code)]
1438pub struct IdrisCodegenConfig {
1439 pub emit_docs: bool,
1441 pub emit_logging: bool,
1443 pub default_totality: Totality,
1445 pub auto_inline: bool,
1447 pub auto_inline_limit: usize,
1449 pub emit_name_pragmas: bool,
1451 pub emit_header_comment: bool,
1453 pub target_backend: String,
1455}
1456#[derive(Debug, Clone, Default)]
1458#[allow(dead_code)]
1459pub struct IdrisModuleMetrics {
1460 pub num_functions: usize,
1462 pub num_data_types: usize,
1464 pub num_records: usize,
1466 pub num_imports: usize,
1468 pub total_clauses: usize,
1470 pub num_mutual_blocks: usize,
1472 pub num_pragmas: usize,
1474}
1475impl IdrisModuleMetrics {
1476 #[allow(dead_code)]
1478 pub fn compute(module: &IdrisModule) -> Self {
1479 let mut m = IdrisModuleMetrics {
1480 num_imports: module.imports.len(),
1481 ..Default::default()
1482 };
1483 for decl in &module.declarations {
1484 Self::count_decl(decl, &mut m);
1485 }
1486 m
1487 }
1488 pub(super) fn count_decl(decl: &IdrisDecl, m: &mut IdrisModuleMetrics) {
1489 match decl {
1490 IdrisDecl::Func(f) => {
1491 m.num_functions += 1;
1492 m.total_clauses += f.clauses.len();
1493 }
1494 IdrisDecl::Data(_) => {
1495 m.num_data_types += 1;
1496 }
1497 IdrisDecl::Record(_) => {
1498 m.num_records += 1;
1499 }
1500 IdrisDecl::Mutual(decls) => {
1501 m.num_mutual_blocks += 1;
1502 for d in decls {
1503 Self::count_decl(d, m);
1504 }
1505 }
1506 IdrisDecl::Pragma(_) => {
1507 m.num_pragmas += 1;
1508 }
1509 _ => {}
1510 }
1511 }
1512 #[allow(dead_code)]
1514 pub fn summary(&self) -> String {
1515 format!(
1516 "functions={} data_types={} records={} imports={} total_clauses={} mutual_blocks={} pragmas={}",
1517 self.num_functions, self.num_data_types, self.num_records, self.num_imports,
1518 self.total_clauses, self.num_mutual_blocks, self.num_pragmas,
1519 )
1520 }
1521}
1522#[derive(Debug, Clone)]
1524#[allow(dead_code)]
1525pub struct IdrisProofScript {
1526 pub theorem_name: String,
1528 pub goal_type: IdrisType,
1530 pub tactics: Vec<IdrisTactic>,
1532}
1533impl IdrisProofScript {
1534 #[allow(dead_code)]
1536 pub fn new(theorem_name: impl Into<String>, goal_type: IdrisType) -> Self {
1537 IdrisProofScript {
1538 theorem_name: theorem_name.into(),
1539 goal_type,
1540 tactics: Vec::new(),
1541 }
1542 }
1543 #[allow(dead_code)]
1545 pub fn push(&mut self, tactic: IdrisTactic) {
1546 self.tactics.push(tactic);
1547 }
1548 #[allow(dead_code)]
1550 pub fn emit(&self) -> String {
1551 let mut out = format!(
1552 "{} : {}\n{} = ?{}_proof\n",
1553 self.theorem_name, self.goal_type, self.theorem_name, self.theorem_name
1554 );
1555 out.push_str(&format!("-- Proof sketch for {}:\n", self.theorem_name));
1556 for tac in &self.tactics {
1557 out.push_str(&format!("-- {}\n", tac));
1558 }
1559 out
1560 }
1561}
1562#[derive(Debug, Clone, PartialEq, Eq)]
1564pub enum Quantity {
1565 Zero,
1567 One,
1569 Unrestricted,
1571}
1572#[derive(Debug, Clone)]
1574pub struct IdrisModule {
1575 pub module_name: Vec<String>,
1577 pub imports: Vec<IdrisImport>,
1579 pub declarations: Vec<IdrisDecl>,
1581 pub doc: Option<String>,
1583}
1584impl IdrisModule {
1585 pub fn new(name: Vec<String>) -> Self {
1587 IdrisModule {
1588 module_name: name,
1589 imports: vec![],
1590 declarations: vec![],
1591 doc: None,
1592 }
1593 }
1594 pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
1596 self.doc = Some(doc.into());
1597 self
1598 }
1599 pub fn import(&mut self, imp: IdrisImport) {
1601 self.imports.push(imp);
1602 }
1603 pub fn add(&mut self, decl: IdrisDecl) {
1605 self.declarations.push(decl);
1606 }
1607 pub fn blank(&mut self) {
1609 self.declarations.push(IdrisDecl::Blank);
1610 }
1611}