1use super::error::ExtractError;
28use crate::kernel::{Context, Literal, Term};
29use std::collections::{HashMap, HashSet};
30
31struct TermGenCtx<'a> {
33 def_name: &'a str,
35 rec_name: &'a str,
37 deref_vars: &'a HashSet<String>,
39 multi_use: &'a HashSet<String>,
43}
44
45pub struct CodeGen<'a> {
47 ctx: &'a Context,
48 output: String,
49 emitted: HashSet<String>,
50}
51
52impl<'a> CodeGen<'a> {
53 pub fn new(ctx: &'a Context) -> Self {
55 Self {
56 ctx,
57 output: String::new(),
58 emitted: HashSet::new(),
59 }
60 }
61
62 pub fn finish(self) -> String {
64 self.output
65 }
66
67 pub fn emit_inductive(&mut self, name: &str) -> Result<(), ExtractError> {
69 if self.emitted.contains(name) {
70 return Ok(());
71 }
72 self.emitted.insert(name.to_string());
73
74 let ctors = distinct_constructors(self.ctx, name);
75 if ctors.is_empty() {
76 return Err(ExtractError::NotFound(name.to_string()));
77 }
78
79 let params = type_params(self.ctx, name);
83 let generics = if params.is_empty() {
84 String::new()
85 } else {
86 format!("<{}>", params.join(", "))
87 };
88
89 self.output.push_str("#[derive(Clone, Debug, PartialEq)]\n");
93 self.output.push_str(&format!("pub enum {}{} {{\n", name, generics));
94 for (ctor_name, ctor_ty) in &ctors {
95 let fields = extract_ctor_fields(self.ctx, ctor_ty, name, params.len());
96 if fields.is_empty() {
97 self.output.push_str(&format!(" {},\n", ctor_name));
98 } else {
99 self.output
100 .push_str(&format!(" {}({}),\n", ctor_name, fields.join(", ")));
101 }
102 }
103 self.output.push_str("}\n\n");
104
105 if params.is_empty() {
115 self.output.push_str(&format!(
116 "impl std::fmt::Display for {n} {{\n \
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ write!(f, \"{{:?}}\", self) }}\n}}\n\n",
118 n = name,
119 ));
120 for (ctor_name, ctor_ty) in &ctors {
121 if matches!(*ctor_name, "Some" | "None" | "Ok" | "Err") {
125 continue;
126 }
127 let cps = ctor_params(self.ctx, ctor_ty, 0, name);
128 if cps.is_empty() {
129 self.output.push_str(&format!(
130 "pub const {c}: {n} = {n}::{c};\n",
131 c = ctor_name, n = name,
132 ));
133 } else {
134 let sig: Vec<String> =
135 cps.iter().enumerate().map(|(i, (t, _))| format!("a{i}: {t}")).collect();
136 let args: Vec<String> = cps
137 .iter()
138 .enumerate()
139 .map(|(i, (_, rec))| if *rec { format!("Box::new(a{i})") } else { format!("a{i}") })
140 .collect();
141 self.output.push_str(&format!(
142 "pub fn {c}({sig}) -> {n} {{ {n}::{c}({args}) }}\n",
143 c = ctor_name, n = name, sig = sig.join(", "), args = args.join(", "),
144 ));
145 }
146 }
147 self.output.push('\n');
148 }
149 Ok(())
150 }
151
152 pub fn emit_definition(&mut self, name: &str) -> Result<(), ExtractError> {
154 if self.emitted.contains(name) {
155 return Ok(());
156 }
157 self.emitted.insert(name.to_string());
158
159 let body = self
160 .ctx
161 .get_definition_body(name)
162 .ok_or_else(|| ExtractError::NotFound(name.to_string()))?;
163 let ty = self
164 .ctx
165 .get_definition_type(name)
166 .ok_or_else(|| ExtractError::NotFound(name.to_string()))?;
167
168 if let Term::Fix {
170 name: rec_name,
171 body: fix_body,
172 } = body
173 {
174 self.emit_fix_as_fn(name, rec_name, fix_body, ty)?;
175 } else if is_lambda(body) {
176 self.emit_lambda_as_fn(name, body, ty)?;
177 } else {
178 self.emit_value_fn(name, body, ty)?;
182 }
183 Ok(())
184 }
185
186 fn emit_fix_as_fn(
188 &mut self,
189 def_name: &str,
190 rec_name: &str,
191 fix_body: &Term,
192 ty: &Term,
193 ) -> Result<(), ExtractError> {
194 let (params, inner_body) = extract_lambda_params(fix_body);
196
197 let param_types = extract_pi_params(ty);
199
200 let ret_ty = extract_return_type(ty);
202
203 self.output.push_str(&format!("pub fn {}(", def_name));
205 for (i, (param_name, _)) in params.iter().enumerate() {
206 if i > 0 {
207 self.output.push_str(", ");
208 }
209 let param_ty = param_types
210 .get(i)
211 .map(|(_, t)| type_to_rust(t))
212 .unwrap_or_else(|| "()".to_string());
213 self.output.push_str(&format!("{}: {}", param_name, param_ty));
214 }
215 self.output
216 .push_str(&format!(") -> {} {{\n", type_to_rust(&ret_ty)));
217
218 let body_code = self.term_to_rust(inner_body, def_name, rec_name);
220 self.output.push_str(&format!(" {}\n", body_code));
221 self.output.push_str("}\n\n");
222
223 Ok(())
224 }
225
226 fn emit_lambda_as_fn(
228 &mut self,
229 def_name: &str,
230 body: &Term,
231 ty: &Term,
232 ) -> Result<(), ExtractError> {
233 let (params, inner_body) = extract_lambda_params(body);
235
236 let param_types = extract_pi_params(ty);
238
239 let ret_ty = extract_return_type(ty);
241
242 self.output.push_str(&format!("pub fn {}(", def_name));
244 for (i, (param_name, _)) in params.iter().enumerate() {
245 if i > 0 {
246 self.output.push_str(", ");
247 }
248 let param_ty = param_types
249 .get(i)
250 .map(|(_, t)| type_to_rust(t))
251 .unwrap_or_else(|| "()".to_string());
252 self.output.push_str(&format!("{}: {}", param_name, param_ty));
253 }
254 self.output
255 .push_str(&format!(") -> {} {{\n", type_to_rust(&ret_ty)));
256
257 let body_code = self.term_to_rust(inner_body, def_name, "");
259 self.output.push_str(&format!(" {}\n", body_code));
260 self.output.push_str("}\n\n");
261
262 Ok(())
263 }
264
265 fn emit_value_fn(&mut self, name: &str, body: &Term, ty: &Term) -> Result<(), ExtractError> {
270 let ty_str = type_to_rust(ty);
271 let body_str = self.term_to_rust(body, name, "");
272 self.output
273 .push_str(&format!("pub fn {}() -> {} {{\n {}\n}}\n\n", name, ty_str, body_str));
274 Ok(())
275 }
276
277 fn term_to_rust(&self, term: &Term, def_name: &str, rec_name: &str) -> String {
279 self.term_to_rust_forced(term, def_name, rec_name, &HashSet::new())
280 }
281
282 fn term_to_rust_forced(
286 &self,
287 term: &Term,
288 def_name: &str,
289 rec_name: &str,
290 force_clone: &HashSet<String>,
291 ) -> String {
292 let empty_deref = HashSet::new();
293 let mut counts: HashMap<String, usize> = HashMap::new();
296 count_vars(term, &mut counts);
297 let mut multi_use: HashSet<String> = counts
298 .into_iter()
299 .filter(|(_, c)| *c > 1)
300 .map(|(name, _)| name)
301 .collect();
302 multi_use.extend(force_clone.iter().cloned());
303 let ctx = TermGenCtx {
304 def_name,
305 rec_name,
306 deref_vars: &empty_deref,
307 multi_use: &multi_use,
308 };
309 self.term_to_rust_ctx(term, &ctx)
310 }
311
312 fn term_to_rust_ctx(&self, term: &Term, tctx: &TermGenCtx) -> String {
314 match term {
315 Term::Const { name, .. } => name.clone(),
318 Term::Var(name) => {
319 if name == tctx.rec_name && !tctx.rec_name.is_empty() {
321 tctx.def_name.to_string()
322 } else {
323 let base = if tctx.deref_vars.contains(name) {
325 format!("(*{})", name)
326 } else {
327 name.clone()
328 };
329 if tctx.multi_use.contains(name) {
331 format!("{}.clone()", base)
332 } else {
333 base
334 }
335 }
336 }
337 Term::Global(name) => {
338 if self.ctx.is_constructor(name) {
340 if let Some(ind) = self.ctx.constructor_inductive(name) {
341 return format!("{}::{}", ind, name);
342 }
343 }
344 if name == tctx.rec_name && !tctx.rec_name.is_empty() {
346 return tctx.def_name.to_string();
347 }
348 if is_value_def(self.ctx, name) {
351 return format!("{}()", name);
352 }
353 name.clone()
354 }
355 Term::App(_, _) => {
356 let (head, args) = collect_app_chain(term);
358 let args_strs: Vec<String> = args
359 .iter()
360 .map(|a| self.term_to_rust_ctx(a, tctx))
361 .collect();
362
363 if let Term::Global(name) = head {
365 if self.ctx.is_constructor(name) {
366 if let Some(ind) = self.ctx.constructor_inductive(name) {
367 let n_params = type_params(self.ctx, ind).len();
371 let value_args: &[String] = if args_strs.len() >= n_params {
372 &args_strs[n_params..]
373 } else {
374 &args_strs
375 };
376 let rec = ctor_field_recursion(self.ctx, self.ctx.get_global(name), ind, n_params);
377 let fields: Vec<String> = value_args
378 .iter()
379 .enumerate()
380 .map(|(i, a)| {
381 if rec.get(i).copied().unwrap_or(false) {
382 format!("Box::new({})", a)
383 } else {
384 a.clone()
385 }
386 })
387 .collect();
388 if fields.is_empty() {
389 return format!("{}::{}", ind, name);
390 } else {
391 return format!("{}::{}({})", ind, name, fields.join(", "));
392 }
393 }
394 }
395 }
396
397 if let Term::Global(name) = head {
407 let is_builtin_op = self.ctx.get_definition_body(name).is_none()
408 && !self.ctx.is_constructor(name)
409 && !self.ctx.is_inductive(name);
410 if is_builtin_op {
411 if let Some(op) = arith_operator(name) {
412 if args_strs.len() == 2 {
413 return format!("({} {} {})", args_strs[0], op, args_strs[1]);
414 }
415 }
416 }
417 }
418
419 let head_str = if let Term::Var(name) = head {
421 if name == tctx.rec_name && !tctx.rec_name.is_empty() {
422 tctx.def_name.to_string()
423 } else {
424 name.clone()
425 }
426 } else {
427 self.term_to_rust_ctx(head, tctx)
428 };
429
430 format!("{}({})", head_str, args_strs.join(", "))
432 }
433 Term::Lambda {
434 param,
435 param_type,
436 body,
437 } => {
438 let param_ty = type_to_rust(param_type);
439 let body_str = self.term_to_rust_ctx(body, tctx);
440 format!("|{}: {}| {}", param, param_ty, body_str)
441 }
442 Term::Let {
443 name, value, body, ..
444 } => {
445 let value_str = self.term_to_rust_ctx(value, tctx);
446 let body_str = self.term_to_rust_ctx(body, tctx);
447 format!("{{ let {} = {}; {} }}", name, value_str, body_str)
448 }
449 Term::MutualFix { defs, index } => {
450 match defs.get(*index) {
453 Some((_, body)) => self.term_to_rust_ctx(body, tctx),
454 None => "()".to_string(),
455 }
456 }
457 Term::Match {
458 discriminant,
459 motive,
460 cases,
461 } => {
462 let disc_str = self.term_to_rust_ctx(discriminant, tctx);
463
464 let ind_name = self.infer_inductive_from_motive(motive)
467 .or_else(|| self.infer_inductive_type(discriminant));
468
469 let mut result = format!("match {} {{\n", disc_str);
470 if let Some(ind) = &ind_name {
471 let ctors = distinct_constructors(self.ctx, ind);
472 let n_params = type_params(self.ctx, ind).len();
473
474 for (i, (ctor_name, ctor_ty)) in ctors.iter().enumerate() {
475 if i < cases.len() {
476 let case = &cases[i];
477 let ctor_arity = count_ctor_args(ctor_ty).saturating_sub(n_params);
479
480 result.push_str(&format!(" {}::{}", ind, ctor_name));
481 if ctor_arity > 0 {
482 let (bindings, case_body) = extract_case_bindings(case, ctor_arity);
484 result.push_str("(");
485 for (j, binding) in bindings.iter().enumerate() {
486 if j > 0 {
487 result.push_str(", ");
488 }
489 result.push_str(binding);
490 }
491 result.push_str(")");
492
493 let box_mask = ctor_field_recursion(self.ctx, Some(ctor_ty), ind, n_params);
499 let case_deref_vars: HashSet<String> = bindings
500 .iter()
501 .enumerate()
502 .filter(|(j, _)| box_mask.get(*j).copied().unwrap_or(false))
503 .map(|(_, b)| b.clone())
504 .collect();
505 let case_tctx = TermGenCtx {
506 def_name: tctx.def_name,
507 rec_name: tctx.rec_name,
508 deref_vars: &case_deref_vars,
509 multi_use: tctx.multi_use,
510 };
511 let body_str = self.term_to_rust_ctx(&case_body, &case_tctx);
512 result.push_str(&format!(" => {},\n", body_str));
513 } else {
514 let case_str = self.term_to_rust_ctx(case, tctx);
515 result.push_str(&format!(" => {},\n", case_str));
516 }
517 }
518 }
519 }
520 result.push_str(" }");
521 result
522 }
523 Term::Fix { name, body } => {
524 let fix_tctx = TermGenCtx {
526 def_name: tctx.def_name,
527 rec_name: name,
528 deref_vars: tctx.deref_vars,
529 multi_use: tctx.multi_use,
530 };
531 self.term_to_rust_ctx(body, &fix_tctx)
532 }
533 Term::Lit(lit) => match lit {
534 Literal::Int(n) => format!("{}i64", n),
535 Literal::BigInt(n) => format!("{}i128 /* bigint */", n),
536 Literal::Nat(n) => format!("{}u64 /* nat */", n),
537 Literal::Float(f) => format!("{}f64", f),
538 Literal::Text(s) => format!("{:?}", s),
539 Literal::Duration(nanos) => format!("{}i64 /* nanos */", nanos),
540 Literal::Date(days) => format!("{}i32 /* days since epoch */", days),
541 Literal::Moment(nanos) => format!("{}i64 /* nanos since epoch */", nanos),
542 },
543 Term::Pi { .. } => "/* type */".to_string(),
544 Term::Sort(_) => "/* sort */".to_string(),
545 Term::Hole => "_".to_string(), }
547 }
548
549 fn infer_inductive_from_motive(&self, motive: &Term) -> Option<String> {
553 if let Term::Lambda { param_type, .. } = motive {
554 if let Term::Global(name) = param_type.as_ref() {
555 if self.ctx.is_inductive(name) {
556 return Some(name.clone());
557 }
558 }
559 }
560 None
561 }
562
563 fn infer_inductive_type(&self, term: &Term) -> Option<String> {
565 match term {
566 Term::Var(_) => {
567 None
569 }
570 Term::Global(name) => {
571 if self.ctx.is_constructor(name) {
572 self.ctx.constructor_inductive(name).map(|s| s.to_string())
573 } else if self.ctx.is_inductive(name) {
574 Some(name.clone())
575 } else {
576 None
577 }
578 }
579 Term::App(f, _) => self.infer_inductive_type(f),
580 Term::Hole => None, _ => None,
582 }
583 }
584}
585
586fn count_vars(term: &Term, counts: &mut HashMap<String, usize>) {
591 match term {
592 Term::Const { .. } => {}
593 Term::Var(name) => {
594 *counts.entry(name.clone()).or_insert(0) += 1;
595 }
596 Term::App(f, a) => {
597 count_vars(f, counts);
598 count_vars(a, counts);
599 }
600 Term::Lambda {
601 param_type, body, ..
602 } => {
603 count_vars(param_type, counts);
604 count_vars(body, counts);
605 }
606 Term::Let {
607 ty, value, body, ..
608 } => {
609 count_vars(ty, counts);
610 count_vars(value, counts);
611 count_vars(body, counts);
612 }
613 Term::MutualFix { defs, .. } => {
614 for (_, body) in defs {
615 count_vars(body, counts);
616 }
617 }
618 Term::Pi {
619 param_type,
620 body_type,
621 ..
622 } => {
623 count_vars(param_type, counts);
624 count_vars(body_type, counts);
625 }
626 Term::Fix { body, .. } => count_vars(body, counts),
627 Term::Match {
628 discriminant,
629 motive,
630 cases,
631 } => {
632 count_vars(discriminant, counts);
633 count_vars(motive, counts);
634 for case in cases {
635 count_vars(case, counts);
636 }
637 }
638 Term::Sort(_) | Term::Global(_) | Term::Lit(_) | Term::Hole => {}
639 }
640}
641
642pub fn emit_value(ctx: &Context, term: &Term) -> String {
645 CodeGen::new(ctx).term_to_rust(term, "", "")
646}
647
648pub fn emit_property_check(ctx: &Context, name: &str, prop: &Term) -> Option<String> {
653 let mut params: Vec<(String, String)> = Vec::new();
655 let mut cur = prop;
656 while let Term::Pi { param, param_type, body_type } = cur {
657 if !prop_type_is_data(ctx, param_type) {
658 return None; }
660 params.push((param.clone(), type_to_rust(param_type)));
661 cur = body_type;
662 }
663 if params.is_empty() {
664 }
666 let param_names: HashSet<String> = params.iter().map(|(n, _)| n.clone()).collect();
667 let body = emit_prop_body(ctx, cur, ¶m_names)?;
668 let sig: Vec<String> = params.iter().map(|(n, t)| format!("{}: {}", n, t)).collect();
669 Some(format!(
670 "/// Runnable property from the proven theorem `{name}`.\n\
671 pub fn check_{name}({}) -> bool {{\n {}\n}}\n\n",
672 sig.join(", "),
673 body
674 ))
675}
676
677fn prop_type_is_data(ctx: &Context, ty: &Term) -> bool {
679 match ty {
680 Term::Global(n) => {
681 matches!(n.as_str(), "Int" | "Float" | "Text" | "Bool" | "Duration" | "Date" | "Moment")
682 || (ctx.is_inductive(n) && !ctx.get_constructors(n).is_empty())
683 }
684 Term::App(_, _) => {
685 let (h, args) = collect_app_chain(ty);
686 prop_type_is_data(ctx, h) && args.iter().all(|a| prop_type_is_data(ctx, a))
687 }
688 _ => false,
689 }
690}
691
692fn emit_prop_body(ctx: &Context, p: &Term, params: &HashSet<String>) -> Option<String> {
695 let (head, args) = collect_app_chain(p);
696 if let Term::Global(h) = head {
697 match (h.as_str(), args.len()) {
698 ("Eq", 3) => {
699 let lhs = emit_checkable_term(ctx, args[1], params)?;
700 let rhs = emit_checkable_term(ctx, args[2], params)?;
701 return Some(format!("({} == {})", lhs, rhs));
702 }
703 ("And", 2) => {
704 return Some(format!(
705 "({} && {})",
706 emit_prop_body(ctx, args[0], params)?,
707 emit_prop_body(ctx, args[1], params)?
708 ))
709 }
710 ("Or", 2) => {
711 return Some(format!(
712 "({} || {})",
713 emit_prop_body(ctx, args[0], params)?,
714 emit_prop_body(ctx, args[1], params)?
715 ))
716 }
717 ("Not", 1) => return Some(format!("(!{})", emit_prop_body(ctx, args[0], params)?)),
718 _ => {}
719 }
720 }
721 None
722}
723
724fn emit_checkable_term(ctx: &Context, term: &Term, params: &HashSet<String>) -> Option<String> {
727 let mut refs = Vec::new();
728 super::collector::collect_globals(term, &mut refs);
729 if !refs.iter().all(|g| g == "_" || crate::extraction::is_extractable(ctx, g)) {
730 return None;
731 }
732 Some(CodeGen::new(ctx).term_to_rust_forced(term, "", "", params))
733}
734
735
736fn distinct_constructors<'a>(ctx: &'a Context, inductive: &str) -> Vec<(&'a str, &'a Term)> {
743 let mut seen = HashSet::new();
744 ctx.get_constructors(inductive)
745 .into_iter()
746 .filter(|(n, _)| seen.insert(n.to_string()))
747 .collect()
748}
749
750fn type_params(ctx: &Context, inductive: &str) -> Vec<String> {
755 let mut names = Vec::new();
756 let mut current = match ctx.get_global(inductive) {
757 Some(ty) => ty,
758 None => return names,
759 };
760 while let Term::Pi {
761 param,
762 param_type,
763 body_type,
764 } = current
765 {
766 if matches!(param_type.as_ref(), Term::Sort(_)) {
768 names.push(param.clone());
769 current = body_type;
770 } else {
771 break;
772 }
773 }
774 names
775}
776
777fn collect_inductive_globals(ctx: &Context, term: &Term, out: &mut Vec<String>) {
782 match term {
783 Term::Global(n) => {
784 if ctx.is_inductive(n) {
785 out.push(n.clone());
786 }
787 }
788 Term::App(f, a) => {
789 collect_inductive_globals(ctx, f, out);
790 collect_inductive_globals(ctx, a, out);
791 }
792 Term::Pi { param_type, body_type, .. } => {
793 collect_inductive_globals(ctx, param_type, out);
794 collect_inductive_globals(ctx, body_type, out);
795 }
796 _ => {}
797 }
798}
799
800fn ctor_field_inductives(ctx: &Context, ctor_ty: &Term, skip: usize, out: &mut Vec<String>) {
804 let mut cur = ctor_ty;
805 for _ in 0..skip {
806 if let Term::Pi { body_type, .. } = cur {
807 cur = body_type;
808 } else {
809 break;
810 }
811 }
812 while let Term::Pi { param_type, body_type, .. } = cur {
813 collect_inductive_globals(ctx, param_type, out);
814 cur = body_type;
815 }
816}
817
818fn inductive_reaches(ctx: &Context, from: &str, to: &str) -> bool {
822 let mut visited: HashSet<String> = HashSet::new();
823 let mut stack = vec![from.to_string()];
824 while let Some(x) = stack.pop() {
825 let skip = type_params(ctx, &x).len();
826 for (_, cty) in distinct_constructors(ctx, &x) {
827 let mut refs = Vec::new();
828 ctor_field_inductives(ctx, cty, skip, &mut refs);
829 for r in refs {
830 if r == to {
831 return true;
832 }
833 if visited.insert(r.clone()) {
834 stack.push(r);
835 }
836 }
837 }
838 }
839 false
840}
841
842fn field_boxed(ctx: &Context, field_type: &Term, enclosing: &str) -> bool {
848 let mut refs = Vec::new();
849 collect_inductive_globals(ctx, field_type, &mut refs);
850 refs.iter().any(|t| inductive_reaches(ctx, t, enclosing))
851}
852
853fn extract_ctor_fields(ctx: &Context, ty: &Term, inductive: &str, skip: usize) -> Vec<String> {
854 let mut current = ty;
855 for _ in 0..skip {
856 if let Term::Pi { body_type, .. } = current {
857 current = body_type;
858 } else {
859 break;
860 }
861 }
862
863 let mut fields = Vec::new();
864 while let Term::Pi {
865 param_type,
866 body_type,
867 ..
868 } = current
869 {
870 let field_ty = type_to_rust(param_type);
871 if field_boxed(ctx, param_type, inductive) {
872 fields.push(format!("Box<{}>", field_ty));
873 } else {
874 fields.push(field_ty);
875 }
876 current = body_type;
877 }
878 fields
879}
880
881fn ctor_params(ctx: &Context, ty: &Term, skip: usize, inductive: &str) -> Vec<(String, bool)> {
888 let mut current = ty;
889 for _ in 0..skip {
890 if let Term::Pi { body_type, .. } = current {
891 current = body_type;
892 } else {
893 break;
894 }
895 }
896 let mut out = Vec::new();
897 while let Term::Pi { param_type, body_type, .. } = current {
898 out.push((type_to_rust(param_type), field_boxed(ctx, param_type, inductive)));
899 current = body_type;
900 }
901 out
902}
903
904fn ctor_field_recursion(ctx: &Context, ty: Option<&Term>, inductive: &str, skip: usize) -> Vec<bool> {
905 let mut flags = Vec::new();
906 let mut current = match ty {
907 Some(t) => t,
908 None => return flags,
909 };
910 for _ in 0..skip {
911 if let Term::Pi { body_type, .. } = current {
912 current = body_type;
913 } else {
914 break;
915 }
916 }
917 while let Term::Pi {
918 param_type,
919 body_type,
920 ..
921 } = current
922 {
923 flags.push(field_boxed(ctx, param_type, inductive));
924 current = body_type;
925 }
926 flags
927}
928
929fn is_value_def(ctx: &Context, name: &str) -> bool {
932 ctx.get_definition_body(name)
933 .map(|b| !matches!(b, Term::Lambda { .. } | Term::Fix { .. }))
934 .unwrap_or(false)
935}
936
937pub fn primitive_rust_type(name: &str) -> Option<&'static str> {
946 Some(match name {
947 "Int" => "i64",
948 "Float" => "f64",
949 "Text" => "String",
950 "Bool" => "bool",
951 "Duration" => "i64",
952 "Date" => "i32",
953 "Moment" => "i64",
954 _ => return None,
955 })
956}
957
958pub(crate) fn arith_uses_well_formed(term: &Term) -> bool {
964 match term {
965 Term::Global(name) => arith_operator(name).is_none(),
967 Term::App(_, _) => {
968 let (head, args) = collect_app_chain(term);
969 if let Term::Global(name) = head {
970 if arith_operator(name).is_some() {
971 return args.len() == 2 && args.iter().all(|a| arith_uses_well_formed(a));
973 }
974 }
975 arith_uses_well_formed(head) && args.iter().all(|a| arith_uses_well_formed(a))
977 }
978 Term::Lambda { param_type, body, .. } => {
979 arith_uses_well_formed(param_type) && arith_uses_well_formed(body)
980 }
981 Term::Pi { param_type, body_type, .. } => {
982 arith_uses_well_formed(param_type) && arith_uses_well_formed(body_type)
983 }
984 Term::Fix { body, .. } => arith_uses_well_formed(body),
985 Term::Match { discriminant, motive, cases } => {
986 arith_uses_well_formed(discriminant)
987 && arith_uses_well_formed(motive)
988 && cases.iter().all(arith_uses_well_formed)
989 }
990 _ => true,
991 }
992}
993
994pub(crate) fn arith_operator(name: &str) -> Option<&'static str> {
1000 Some(match name {
1001 "add" => "+",
1002 "sub" => "-",
1003 "mul" => "*",
1004 "div" => "/",
1005 "mod" => "%",
1006 _ => return None,
1007 })
1008}
1009
1010fn type_to_rust(ty: &Term) -> String {
1012 match ty {
1013 Term::Var(name) => name.clone(),
1015 Term::Global(name) => {
1016 primitive_rust_type(name)
1017 .map(str::to_string)
1018 .unwrap_or_else(|| name.clone())
1019 }
1020 Term::Pi {
1021 param_type,
1022 body_type,
1023 ..
1024 } => {
1025 let arg = type_to_rust(param_type);
1027 let ret = type_to_rust(body_type);
1028 format!("fn({}) -> {}", arg, ret)
1029 }
1030 Term::App(_, _) => {
1031 let (head, args) = collect_app_chain(ty);
1034 let head_str = type_to_rust(head);
1035 let arg_strs: Vec<String> = args.iter().map(|a| type_to_rust(a)).collect();
1036 format!("{}<{}>", head_str, arg_strs.join(", "))
1037 }
1038 Term::Sort(_) => "()".to_string(),
1039 Term::Lit(_) => "()".to_string(), _ => "()".to_string(),
1041 }
1042}
1043
1044fn is_lambda(term: &Term) -> bool {
1046 matches!(term, Term::Lambda { .. })
1047}
1048
1049fn extract_lambda_params(term: &Term) -> (Vec<(String, Term)>, &Term) {
1051 let mut params = Vec::new();
1052 let mut current = term;
1053
1054 while let Term::Lambda {
1055 param,
1056 param_type,
1057 body,
1058 } = current
1059 {
1060 params.push((param.clone(), (**param_type).clone()));
1061 current = body;
1062 }
1063
1064 (params, current)
1065}
1066
1067fn extract_pi_params(ty: &Term) -> Vec<(String, Term)> {
1069 let mut params = Vec::new();
1070 let mut current = ty;
1071
1072 while let Term::Pi {
1073 param,
1074 param_type,
1075 body_type,
1076 } = current
1077 {
1078 params.push((param.clone(), (**param_type).clone()));
1079 current = body_type;
1080 }
1081
1082 params
1083}
1084
1085fn extract_return_type(ty: &Term) -> Term {
1087 let mut current = ty;
1088 while let Term::Pi { body_type, .. } = current {
1089 current = body_type;
1090 }
1091 current.clone()
1092}
1093
1094fn count_ctor_args(ty: &Term) -> usize {
1096 let mut count = 0;
1097 let mut current = ty;
1098 while let Term::Pi { body_type, .. } = current {
1099 count += 1;
1100 current = body_type;
1101 }
1102 count
1103}
1104
1105fn extract_case_bindings(case: &Term, arity: usize) -> (Vec<String>, Term) {
1107 let mut bindings = Vec::new();
1108 let mut current = case;
1109
1110 for _ in 0..arity {
1111 if let Term::Lambda { param, body, .. } = current {
1112 bindings.push(param.clone());
1113 current = body;
1114 } else {
1115 break;
1116 }
1117 }
1118
1119 (bindings, current.clone())
1120}
1121
1122fn collect_app_chain(term: &Term) -> (&Term, Vec<&Term>) {
1126 let mut args = Vec::new();
1127 let mut current = term;
1128
1129 while let Term::App(f, a) = current {
1130 args.push(a.as_ref());
1131 current = f.as_ref();
1132 }
1133
1134 args.reverse();
1136 (current, args)
1137}
1138
1139#[allow(dead_code)]
1141fn is_constructor_app(term: &Term, ctx: &Context) -> bool {
1142 match term {
1143 Term::Global(name) => ctx.is_constructor(name),
1144 Term::App(f, _) => is_constructor_app(f, ctx),
1145 _ => false,
1146 }
1147}