1use super::types::{ConstructiveProp, ExtractedProgram, ExtractionResult, ProofTerm};
4
5pub fn extract_program(prop: &ConstructiveProp, proof: &ProofTerm) -> ExtractionResult {
16 if !is_constructive(prop) {
17 return ExtractionResult::NonConstructive(format!(
18 "Proposition contains non-constructive connectives: {:?}",
19 prop
20 ));
21 }
22 if !type_check_proof(prop, proof) {
23 return ExtractionResult::Failure(format!(
24 "Proof term does not type-check against proposition {:?}",
25 prop
26 ));
27 }
28 let (input_type, output_type) = infer_io_types(prop);
29 let simplified = simplify_proof_term(proof);
30 ExtractionResult::Success(ExtractedProgram {
31 name: format!("extracted_{}", prop_name(prop)),
32 input_type,
33 output_type,
34 body: simplified,
35 original_prop: prop.clone(),
36 })
37}
38
39pub fn simplify_proof_term(pt: &ProofTerm) -> ProofTerm {
41 match pt {
42 ProofTerm::App(f, arg) => {
44 let f2 = simplify_proof_term(f);
45 let a2 = simplify_proof_term(arg);
46 if let ProofTerm::Lambda(var, body) = f2 {
47 let substituted = subst_proof_term(&body, &var, &a2);
48 simplify_proof_term(&substituted)
49 } else {
50 ProofTerm::App(Box::new(f2), Box::new(a2))
51 }
52 }
53 ProofTerm::Fst(inner) => {
55 let inner2 = simplify_proof_term(inner);
56 if let ProofTerm::Pair(a, _) = inner2 {
57 simplify_proof_term(&a)
58 } else {
59 ProofTerm::Fst(Box::new(inner2))
60 }
61 }
62 ProofTerm::Snd(inner) => {
63 let inner2 = simplify_proof_term(inner);
64 if let ProofTerm::Pair(_, b) = inner2 {
65 simplify_proof_term(&b)
66 } else {
67 ProofTerm::Snd(Box::new(inner2))
68 }
69 }
70 ProofTerm::Lambda(x, body) => {
71 ProofTerm::Lambda(x.clone(), Box::new(simplify_proof_term(body)))
72 }
73 ProofTerm::Pair(a, b) => ProofTerm::Pair(
74 Box::new(simplify_proof_term(a)),
75 Box::new(simplify_proof_term(b)),
76 ),
77 ProofTerm::Inl(t) => ProofTerm::Inl(Box::new(simplify_proof_term(t))),
78 ProofTerm::Inr(t) => ProofTerm::Inr(Box::new(simplify_proof_term(t))),
79 ProofTerm::Pack(w, t) => ProofTerm::Pack(w.clone(), Box::new(simplify_proof_term(t))),
80 ProofTerm::Unpack {
81 witness,
82 proof_var,
83 packed,
84 body,
85 } => ProofTerm::Unpack {
86 witness: witness.clone(),
87 proof_var: proof_var.clone(),
88 packed: Box::new(simplify_proof_term(packed)),
89 body: Box::new(simplify_proof_term(body)),
90 },
91 ProofTerm::Absurd(t) => ProofTerm::Absurd(Box::new(simplify_proof_term(t))),
92 ProofTerm::Unit | ProofTerm::Var(_) => pt.clone(),
93 }
94}
95
96pub fn type_check_proof(prop: &ConstructiveProp, proof: &ProofTerm) -> bool {
103 match (prop, proof) {
104 (ConstructiveProp::True, ProofTerm::Unit) => true,
106 (_, ProofTerm::Absurd(_)) => true,
108 (_, ProofTerm::Var(_)) => true,
110 (ConstructiveProp::And(p, q), ProofTerm::Pair(a, b)) => {
112 type_check_proof(p, a) && type_check_proof(q, b)
113 }
114 (p, ProofTerm::Fst(inner)) => {
116 matches_and_left(p, inner)
118 }
119 (q, ProofTerm::Snd(inner)) => matches_and_right(q, inner),
121 (ConstructiveProp::Or(p, _), ProofTerm::Inl(t)) => type_check_proof(p, t),
123 (ConstructiveProp::Or(_, q), ProofTerm::Inr(t)) => type_check_proof(q, t),
125 (ConstructiveProp::Implies(_, q), ProofTerm::Lambda(_, body)) => {
127 type_check_proof(q, body)
129 }
130 (ConstructiveProp::Not(_), ProofTerm::Lambda(_, _)) => true,
132 (ConstructiveProp::Exists(_, _), ProofTerm::Pack(_, _)) => true,
134 (ConstructiveProp::Forall(_, _), ProofTerm::Lambda(_, _)) => true,
136 (_, ProofTerm::Unpack { .. }) => true,
138 (_, ProofTerm::App(_, _)) => true,
140 (ConstructiveProp::Eq(a, b), ProofTerm::Unit) => a == b,
142 (ConstructiveProp::Atom(_, _), ProofTerm::Unit) => true,
144 _ => false,
145 }
146}
147
148pub fn is_constructive(prop: &ConstructiveProp) -> bool {
154 match prop {
155 ConstructiveProp::True | ConstructiveProp::False => true,
156 ConstructiveProp::Atom(_, _) | ConstructiveProp::Eq(_, _) => true,
157 ConstructiveProp::And(p, q)
158 | ConstructiveProp::Or(p, q)
159 | ConstructiveProp::Implies(p, q) => is_constructive(p) && is_constructive(q),
160 ConstructiveProp::Not(p) => {
161 is_constructive(p)
163 }
164 ConstructiveProp::Exists(_, p) | ConstructiveProp::Forall(_, p) => is_constructive(p),
165 }
166}
167
168pub fn realizability_check(prop: &ConstructiveProp) -> bool {
175 match prop {
176 ConstructiveProp::True => true,
177 ConstructiveProp::False => false,
178 ConstructiveProp::Atom(_, _) => true,
179 ConstructiveProp::Eq(a, b) => a == b,
180 ConstructiveProp::And(p, q) => realizability_check(p) && realizability_check(q),
181 ConstructiveProp::Or(p, q) => realizability_check(p) || realizability_check(q),
182 ConstructiveProp::Implies(p, q) => {
183 !realizability_check(p) || realizability_check(q)
185 }
186 ConstructiveProp::Not(p) => !realizability_check(p),
187 ConstructiveProp::Exists(_, p) => realizability_check(p),
188 ConstructiveProp::Forall(_, p) => realizability_check(p),
189 }
190}
191
192pub fn proof_to_rust(prog: &ExtractedProgram) -> String {
194 let body_str = proof_term_to_rust(&prog.body, 1);
195 format!(
196 "fn {}(input: {}) -> {} {{\n{}\n}}",
197 sanitize_ident(&prog.name),
198 prog.input_type,
199 prog.output_type,
200 body_str,
201 )
202}
203
204pub fn modus_ponens_extract(
207 _p: &ConstructiveProp,
208 _q: &ConstructiveProp,
209 pf_p: &ProofTerm,
210 pf_pq: &ProofTerm,
211) -> ProofTerm {
212 ProofTerm::App(Box::new(pf_pq.clone()), Box::new(pf_p.clone()))
213}
214
215pub fn conjunction_intro(pf_p: &ProofTerm, pf_q: &ProofTerm) -> ProofTerm {
217 ProofTerm::Pair(Box::new(pf_p.clone()), Box::new(pf_q.clone()))
218}
219
220pub fn existential_witness(witness: &str, pf: &ProofTerm) -> ProofTerm {
222 ProofTerm::Pack(witness.to_string(), Box::new(pf.clone()))
223}
224
225pub(super) fn subst_proof_term(term: &ProofTerm, var: &str, val: &ProofTerm) -> ProofTerm {
229 match term {
230 ProofTerm::Var(x) if x == var => val.clone(),
231 ProofTerm::Var(_) | ProofTerm::Unit => term.clone(),
232 ProofTerm::Lambda(x, body) => {
233 if x == var {
234 term.clone()
236 } else {
237 ProofTerm::Lambda(x.clone(), Box::new(subst_proof_term(body, var, val)))
238 }
239 }
240 ProofTerm::App(f, a) => ProofTerm::App(
241 Box::new(subst_proof_term(f, var, val)),
242 Box::new(subst_proof_term(a, var, val)),
243 ),
244 ProofTerm::Pair(a, b) => ProofTerm::Pair(
245 Box::new(subst_proof_term(a, var, val)),
246 Box::new(subst_proof_term(b, var, val)),
247 ),
248 ProofTerm::Fst(t) => ProofTerm::Fst(Box::new(subst_proof_term(t, var, val))),
249 ProofTerm::Snd(t) => ProofTerm::Snd(Box::new(subst_proof_term(t, var, val))),
250 ProofTerm::Inl(t) => ProofTerm::Inl(Box::new(subst_proof_term(t, var, val))),
251 ProofTerm::Inr(t) => ProofTerm::Inr(Box::new(subst_proof_term(t, var, val))),
252 ProofTerm::Pack(w, t) => {
253 ProofTerm::Pack(w.clone(), Box::new(subst_proof_term(t, var, val)))
254 }
255 ProofTerm::Unpack {
256 witness,
257 proof_var,
258 packed,
259 body,
260 } => {
261 let packed2 = subst_proof_term(packed, var, val);
262 let body2 = if witness == var || proof_var == var {
263 *body.clone()
265 } else {
266 subst_proof_term(body, var, val)
267 };
268 ProofTerm::Unpack {
269 witness: witness.clone(),
270 proof_var: proof_var.clone(),
271 packed: Box::new(packed2),
272 body: Box::new(body2),
273 }
274 }
275 ProofTerm::Absurd(t) => ProofTerm::Absurd(Box::new(subst_proof_term(t, var, val))),
276 }
277}
278
279fn prop_name(prop: &ConstructiveProp) -> String {
281 match prop {
282 ConstructiveProp::True => "true".to_string(),
283 ConstructiveProp::False => "false".to_string(),
284 ConstructiveProp::And(_, _) => "and".to_string(),
285 ConstructiveProp::Or(_, _) => "or".to_string(),
286 ConstructiveProp::Not(_) => "not".to_string(),
287 ConstructiveProp::Implies(_, _) => "implies".to_string(),
288 ConstructiveProp::Exists(x, _) => format!("exists_{}", x),
289 ConstructiveProp::Forall(x, _) => format!("forall_{}", x),
290 ConstructiveProp::Atom(name, _) => name.clone(),
291 ConstructiveProp::Eq(a, b) => format!("eq_{}_{}", a, b),
292 }
293}
294
295fn infer_io_types(prop: &ConstructiveProp) -> (String, String) {
297 match prop {
298 ConstructiveProp::Implies(p, q) => (prop_type_str(p), prop_type_str(q)),
299 ConstructiveProp::Forall(x, p) => (x.clone(), prop_type_str(p)),
300 _ => ("()".to_string(), prop_type_str(prop)),
301 }
302}
303
304fn prop_type_str(prop: &ConstructiveProp) -> String {
306 match prop {
307 ConstructiveProp::True => "()".to_string(),
308 ConstructiveProp::False => "!".to_string(),
309 ConstructiveProp::And(p, q) => format!("({}, {})", prop_type_str(p), prop_type_str(q)),
310 ConstructiveProp::Or(p, q) => format!("Result<{}, {}>", prop_type_str(p), prop_type_str(q)),
311 ConstructiveProp::Not(p) => format!("fn({}) -> !", prop_type_str(p)),
312 ConstructiveProp::Implies(p, q) => {
313 format!("fn({}) -> {}", prop_type_str(p), prop_type_str(q))
314 }
315 ConstructiveProp::Exists(x, _) => format!("({}, _)", x),
316 ConstructiveProp::Forall(_, p) => format!("impl Fn(_) -> {}", prop_type_str(p)),
317 ConstructiveProp::Atom(name, args) => {
318 if args.is_empty() {
319 name.clone()
320 } else {
321 format!("{}<{}>", name, args.join(", "))
322 }
323 }
324 ConstructiveProp::Eq(a, b) => format!("Eq<{}, {}>", a, b),
325 }
326}
327
328fn proof_term_to_rust(pt: &ProofTerm, depth: usize) -> String {
330 let indent = " ".repeat(depth);
331 match pt {
332 ProofTerm::Unit => format!("{}()", indent),
333 ProofTerm::Var(x) => format!("{}{}", indent, x),
334 ProofTerm::Lambda(x, body) => {
335 let body_str = proof_term_to_rust(body, depth + 1);
336 format!("{}|{}| {{\n{}\n{}}}", indent, x, body_str, indent)
337 }
338 ProofTerm::App(f, a) => {
339 let f_str = proof_term_to_rust(f, 0);
340 let a_str = proof_term_to_rust(a, 0);
341 format!("{}({}).({})", indent, f_str.trim(), a_str.trim())
342 }
343 ProofTerm::Pair(a, b) => {
344 let a_str = proof_term_to_rust(a, 0).trim().to_string();
345 let b_str = proof_term_to_rust(b, 0).trim().to_string();
346 format!("{}({}, {})", indent, a_str, b_str)
347 }
348 ProofTerm::Fst(t) => {
349 let inner = proof_term_to_rust(t, 0).trim().to_string();
350 format!("{}{}.0", indent, inner)
351 }
352 ProofTerm::Snd(t) => {
353 let inner = proof_term_to_rust(t, 0).trim().to_string();
354 format!("{}{}.1", indent, inner)
355 }
356 ProofTerm::Inl(t) => {
357 let inner = proof_term_to_rust(t, 0).trim().to_string();
358 format!("{}Ok({})", indent, inner)
359 }
360 ProofTerm::Inr(t) => {
361 let inner = proof_term_to_rust(t, 0).trim().to_string();
362 format!("{}Err({})", indent, inner)
363 }
364 ProofTerm::Pack(w, t) => {
365 let inner = proof_term_to_rust(t, 0).trim().to_string();
366 format!("{}({}, {})", indent, w, inner)
367 }
368 ProofTerm::Unpack {
369 witness,
370 proof_var,
371 packed,
372 body,
373 } => {
374 let packed_str = proof_term_to_rust(packed, 0).trim().to_string();
375 let body_str = proof_term_to_rust(body, depth + 1);
376 format!(
377 "{}let ({}, {}) = {};\n{}",
378 indent, witness, proof_var, packed_str, body_str
379 )
380 }
381 ProofTerm::Absurd(t) => {
382 let inner = proof_term_to_rust(t, 0).trim().to_string();
383 format!("{}{{ let _: ! = {}; unreachable!() }}", indent, inner)
384 }
385 }
386}
387
388fn matches_and_left(p: &ConstructiveProp, inner: &ProofTerm) -> bool {
390 matches!(inner, ProofTerm::Pair(a, _) if type_check_proof(p, a))
392 || matches!(inner, ProofTerm::Var(_))
393}
394
395fn matches_and_right(q: &ConstructiveProp, inner: &ProofTerm) -> bool {
397 matches!(inner, ProofTerm::Pair(_, b) if type_check_proof(q, b))
398 || matches!(inner, ProofTerm::Var(_))
399}
400
401fn sanitize_ident(s: &str) -> String {
403 s.chars()
404 .map(|c| {
405 if c.is_alphanumeric() || c == '_' {
406 c
407 } else {
408 '_'
409 }
410 })
411 .collect()
412}
413
414#[cfg(test)]
417mod tests {
418 use super::super::types::*;
419 use super::*;
420
421 fn atom(name: &str) -> ConstructiveProp {
422 ConstructiveProp::Atom(name.to_string(), vec![])
423 }
424
425 fn var(x: &str) -> ProofTerm {
426 ProofTerm::Var(x.to_string())
427 }
428
429 #[test]
432 fn test_is_constructive_true() {
433 assert!(is_constructive(&ConstructiveProp::True));
434 }
435
436 #[test]
437 fn test_is_constructive_false() {
438 assert!(is_constructive(&ConstructiveProp::False));
439 }
440
441 #[test]
442 fn test_is_constructive_atom() {
443 assert!(is_constructive(&atom("P")));
444 }
445
446 #[test]
447 fn test_is_constructive_and() {
448 let p = ConstructiveProp::And(Box::new(atom("P")), Box::new(atom("Q")));
449 assert!(is_constructive(&p));
450 }
451
452 #[test]
453 fn test_is_constructive_or() {
454 let p = ConstructiveProp::Or(Box::new(atom("P")), Box::new(atom("Q")));
455 assert!(is_constructive(&p));
456 }
457
458 #[test]
459 fn test_is_constructive_implies() {
460 let p = ConstructiveProp::Implies(Box::new(atom("P")), Box::new(atom("Q")));
461 assert!(is_constructive(&p));
462 }
463
464 #[test]
465 fn test_is_constructive_not() {
466 let p = ConstructiveProp::Not(Box::new(atom("P")));
467 assert!(is_constructive(&p));
468 }
469
470 #[test]
471 fn test_is_constructive_exists() {
472 let p = ConstructiveProp::Exists("x".to_string(), Box::new(atom("P")));
473 assert!(is_constructive(&p));
474 }
475
476 #[test]
477 fn test_is_constructive_forall() {
478 let p = ConstructiveProp::Forall("x".to_string(), Box::new(atom("P")));
479 assert!(is_constructive(&p));
480 }
481
482 #[test]
485 fn test_type_check_unit_proves_true() {
486 assert!(type_check_proof(&ConstructiveProp::True, &ProofTerm::Unit));
487 }
488
489 #[test]
490 fn test_type_check_pair_proves_and() {
491 let prop = ConstructiveProp::And(Box::new(atom("P")), Box::new(atom("Q")));
492 let pf = ProofTerm::Pair(Box::new(var("p")), Box::new(var("q")));
493 assert!(type_check_proof(&prop, &pf));
494 }
495
496 #[test]
497 fn test_type_check_inl_proves_or_left() {
498 let prop = ConstructiveProp::Or(Box::new(atom("P")), Box::new(atom("Q")));
499 let pf = ProofTerm::Inl(Box::new(var("p")));
500 assert!(type_check_proof(&prop, &pf));
501 }
502
503 #[test]
504 fn test_type_check_inr_proves_or_right() {
505 let prop = ConstructiveProp::Or(Box::new(atom("P")), Box::new(atom("Q")));
506 let pf = ProofTerm::Inr(Box::new(var("q")));
507 assert!(type_check_proof(&prop, &pf));
508 }
509
510 #[test]
511 fn test_type_check_lambda_proves_implies() {
512 let prop = ConstructiveProp::Implies(Box::new(atom("P")), Box::new(atom("Q")));
513 let pf = ProofTerm::Lambda("x".to_string(), Box::new(var("x")));
514 assert!(type_check_proof(&prop, &pf));
515 }
516
517 #[test]
518 fn test_type_check_absurd_proves_anything() {
519 let pf = ProofTerm::Absurd(Box::new(var("contradiction")));
520 assert!(type_check_proof(&atom("Q"), &pf));
521 }
522
523 #[test]
524 fn test_type_check_var_proves_anything() {
525 assert!(type_check_proof(&atom("P"), &var("p")));
526 }
527
528 #[test]
531 fn test_realizability_true() {
532 assert!(realizability_check(&ConstructiveProp::True));
533 }
534
535 #[test]
536 fn test_realizability_false() {
537 assert!(!realizability_check(&ConstructiveProp::False));
538 }
539
540 #[test]
541 fn test_realizability_atom() {
542 assert!(realizability_check(&atom("P")));
543 }
544
545 #[test]
546 fn test_realizability_and_true_true() {
547 let p = ConstructiveProp::And(
548 Box::new(ConstructiveProp::True),
549 Box::new(ConstructiveProp::True),
550 );
551 assert!(realizability_check(&p));
552 }
553
554 #[test]
555 fn test_realizability_and_with_false() {
556 let p = ConstructiveProp::And(
557 Box::new(ConstructiveProp::True),
558 Box::new(ConstructiveProp::False),
559 );
560 assert!(!realizability_check(&p));
561 }
562
563 #[test]
564 fn test_realizability_or_true_false() {
565 let p = ConstructiveProp::Or(
566 Box::new(ConstructiveProp::True),
567 Box::new(ConstructiveProp::False),
568 );
569 assert!(realizability_check(&p));
570 }
571
572 #[test]
575 fn test_simplify_beta_redex() {
576 let lam = ProofTerm::Lambda("x".to_string(), Box::new(var("x")));
578 let app = ProofTerm::App(Box::new(lam), Box::new(var("y")));
579 assert_eq!(simplify_proof_term(&app), var("y"));
580 }
581
582 #[test]
583 fn test_simplify_fst_pair() {
584 let pair = ProofTerm::Pair(Box::new(var("a")), Box::new(var("b")));
585 let fst = ProofTerm::Fst(Box::new(pair));
586 assert_eq!(simplify_proof_term(&fst), var("a"));
587 }
588
589 #[test]
590 fn test_simplify_snd_pair() {
591 let pair = ProofTerm::Pair(Box::new(var("a")), Box::new(var("b")));
592 let snd = ProofTerm::Snd(Box::new(pair));
593 assert_eq!(simplify_proof_term(&snd), var("b"));
594 }
595
596 #[test]
597 fn test_simplify_unit() {
598 assert_eq!(simplify_proof_term(&ProofTerm::Unit), ProofTerm::Unit);
599 }
600
601 #[test]
604 fn test_extract_simple_implication() {
605 let prop = ConstructiveProp::Implies(Box::new(atom("P")), Box::new(atom("P")));
606 let pf = ProofTerm::Lambda("x".to_string(), Box::new(var("x")));
607 let result = extract_program(&prop, &pf);
608 assert!(matches!(result, ExtractionResult::Success(_)));
609 }
610
611 #[test]
612 fn test_extract_conjunction_intro() {
613 let prop = ConstructiveProp::And(Box::new(atom("P")), Box::new(atom("Q")));
614 let pf = ProofTerm::Pair(Box::new(var("p")), Box::new(var("q")));
615 let result = extract_program(&prop, &pf);
616 assert!(matches!(result, ExtractionResult::Success(_)));
617 }
618
619 #[test]
622 fn test_modus_ponens_extract() {
623 let p = atom("P");
624 let q = atom("Q");
625 let pf_p = var("p");
626 let pf_pq = ProofTerm::Lambda("x".to_string(), Box::new(var("x")));
627 let result = modus_ponens_extract(&p, &q, &pf_p, &pf_pq);
628 assert_eq!(result, ProofTerm::App(Box::new(pf_pq), Box::new(pf_p)));
629 }
630
631 #[test]
634 fn test_conjunction_intro() {
635 let pf_p = var("p");
636 let pf_q = var("q");
637 let result = conjunction_intro(&pf_p, &pf_q);
638 assert_eq!(result, ProofTerm::Pair(Box::new(pf_p), Box::new(pf_q)));
639 }
640
641 #[test]
642 fn test_existential_witness() {
643 let pf = var("body");
644 let result = existential_witness("42", &pf);
645 assert_eq!(result, ProofTerm::Pack("42".to_string(), Box::new(pf)));
646 }
647
648 #[test]
651 fn test_proof_to_rust_identity() {
652 let prog = ExtractedProgram {
653 name: "identity".to_string(),
654 input_type: "P".to_string(),
655 output_type: "P".to_string(),
656 body: ProofTerm::Lambda("x".to_string(), Box::new(var("x"))),
657 original_prop: ConstructiveProp::Implies(Box::new(atom("P")), Box::new(atom("P"))),
658 };
659 let code = proof_to_rust(&prog);
660 assert!(code.contains("fn identity"));
661 assert!(code.contains("|x|"));
662 }
663
664 #[test]
665 fn test_proof_to_rust_pair() {
666 let prog = ExtractedProgram {
667 name: "pair_prog".to_string(),
668 input_type: "()".to_string(),
669 output_type: "(P, Q)".to_string(),
670 body: ProofTerm::Pair(Box::new(var("p")), Box::new(var("q"))),
671 original_prop: ConstructiveProp::And(Box::new(atom("P")), Box::new(atom("Q"))),
672 };
673 let code = proof_to_rust(&prog);
674 assert!(code.contains("fn pair_prog"));
675 }
676}