1#![allow(unused_imports)]
2
3#![allow(missing_docs)]
344#![warn(clippy::all)]
345#![allow(clippy::result_large_err)]
346#![allow(clippy::field_reassign_with_default)]
347#![allow(clippy::ptr_arg)]
348#![allow(clippy::derivable_impls)]
349#![allow(clippy::len_without_is_empty)]
350#![allow(clippy::should_implement_trait)]
351#![allow(clippy::type_complexity)]
352#![allow(clippy::collapsible_if)]
353#![allow(clippy::single_match)]
354#![allow(clippy::needless_ifs)]
355#![allow(clippy::useless_format)]
356#![allow(clippy::new_without_default)]
357#![allow(clippy::manual_strip)]
358#![allow(clippy::needless_borrows_for_generic_args)]
359#![allow(clippy::manual_saturating_arithmetic)]
360#![allow(clippy::manual_is_variant_and)]
361#![allow(clippy::iter_kv_map)]
362#![allow(clippy::if_same_then_else)]
363#![allow(clippy::collapsible_str_replace)]
364#![allow(clippy::bool_comparison)]
365#![allow(clippy::nonminimal_bool)]
366#![allow(clippy::manual_range_contains)]
367#![allow(clippy::len_zero)]
368#![allow(clippy::unnecessary_map_or)]
369#![allow(clippy::enum_variant_names)]
370#![allow(clippy::implicit_saturating_sub)]
371#![allow(clippy::to_string_in_format_args)]
372#![allow(clippy::incompatible_msrv)]
373#![allow(clippy::int_plus_one)]
374#![allow(clippy::manual_map)]
375#![allow(clippy::needless_bool)]
376#![allow(clippy::needless_else)]
377#![allow(clippy::clone_on_copy)]
378#![allow(clippy::inherent_to_string)]
379#![allow(clippy::manual_find)]
380#![allow(clippy::double_ended_iterator_last)]
381#![allow(clippy::for_kv_map)]
382#![allow(clippy::needless_splitn)]
383#![allow(clippy::trim_split_whitespace)]
384#![allow(clippy::useless_vec)]
385#![allow(clippy::cloned_ref_to_slice_refs)]
386#![allow(non_snake_case)]
387
388use oxilean_kernel::{Expr, Literal, Name};
389pub mod attribute;
390pub mod bench_support;
391pub mod binder;
392pub mod coercion;
393pub mod context;
394pub mod derive;
395pub mod do_notation;
397pub mod elab_decl;
398pub mod elab_expr;
399pub mod elaborate;
400pub mod equation;
401pub mod error_msg;
403pub mod implicit;
404pub mod infer;
405pub mod info_tree;
407pub mod instance;
408pub mod macro_expand;
409pub mod metaprog;
410pub mod metavar;
411pub mod mutual;
412pub mod notation;
413pub mod parallel;
414pub mod pattern_match;
415pub mod predef;
417pub mod quote;
418pub mod solver;
419pub mod structure;
420pub mod tactic;
421pub mod tactic_auto;
422pub mod trace;
423pub mod typeclass;
424pub mod unify;
425
426pub mod command_elab;
427pub mod completion_provider;
428pub mod delaborator;
430pub mod derive_adv;
431pub mod differential_test;
433pub mod elaboration_profiler;
434pub mod hover_info;
435pub mod lean4_compat;
436pub mod module_import;
437
438pub use attribute::{
439 apply_attributes, process_attributes, AttrEntry, AttrError, AttrHandler, AttributeManager,
440 ProcessedAttrs,
441};
442pub use binder::{BinderElabResult, BinderTypeResult};
443pub use coercion::{Coercion, CoercionRegistry};
444pub use context::{ElabContext, LocalEntry};
445pub use derive::{DerivableClass, DeriveRegistry, Deriver};
446pub use elab_decl::{elaborate_decl, DeclElabError, DeclElaborator, PendingDecl};
447pub use elaborate::elaborate_expr;
448pub use equation::{DecisionTree, Equation, EquationCompiler, Pattern};
449pub use implicit::{resolve_implicits, resolve_instance, ImplicitArg};
450pub use infer::{Constraint, InferResult, TypeInferencer};
451pub use instance::{InstanceDecl, InstanceResolver};
452pub use macro_expand::{MacroDef, MacroExpander};
453pub use metavar::{MetaVar, MetaVarContext};
454pub use mutual::{
455 CallGraph, MutualBlock, MutualChecker, MutualElabError, StructuralRecursion, TerminationKind,
456 WellFoundedRecursion,
457};
458pub use notation::{expand_do_notation, expand_list_literal, Notation, NotationRegistry};
459pub use pattern_match::{
460 check_exhaustive, check_redundant, elaborate_match, ElabPattern, ExhaustivenessResult,
461 MatchEquation, PatternCompiler,
462};
463pub use quote::{quote, unquote, QuoteContext};
464pub use solver::{is_unifiable, ConstraintSolver};
465pub use structure::{
466 FieldInfo, ProjectionDecl, StructElabError, StructureElaborator, StructureInfo,
467};
468pub use tactic::{
469 eval_tactic_block, tactic_apply, tactic_by_contra, tactic_cases, tactic_contrapose,
470 tactic_exact, tactic_induction, tactic_intro, tactic_push_neg, tactic_split, Goal, Tactic,
471 TacticError, TacticRegistry, TacticResult, TacticState,
472};
473pub use typeclass::{Instance, Method, TypeClass, TypeClassRegistry};
474pub use unify::unify;
475
476#[allow(dead_code)]
482#[derive(Debug, Clone)]
483pub struct ElabConfig {
484 pub max_depth: u32,
486 pub proof_irrelevance: bool,
488 pub auto_implicit: bool,
490 pub strict_instances: bool,
492 pub max_tactic_steps: u32,
494 pub trace_elaboration: bool,
496 pub kernel_check: bool,
498 pub allow_sorry: bool,
500 pub max_universe_level: u32,
502}
503
504impl Default for ElabConfig {
505 fn default() -> Self {
506 Self {
507 max_depth: 512,
508 proof_irrelevance: true,
509 auto_implicit: true,
510 strict_instances: false,
511 max_tactic_steps: 100_000,
512 trace_elaboration: false,
513 kernel_check: true,
514 allow_sorry: false,
515 max_universe_level: 100,
516 }
517 }
518}
519
520impl ElabConfig {
521 #[allow(dead_code)]
523 pub fn interactive() -> Self {
524 Self {
525 allow_sorry: true,
526 strict_instances: false,
527 ..Self::default()
528 }
529 }
530
531 #[allow(dead_code)]
533 pub fn strict() -> Self {
534 Self {
535 allow_sorry: false,
536 strict_instances: true,
537 kernel_check: true,
538 ..Self::default()
539 }
540 }
541
542 #[allow(dead_code)]
544 pub fn debug() -> Self {
545 Self {
546 trace_elaboration: true,
547 ..Self::default()
548 }
549 }
550
551 #[allow(dead_code)]
553 pub fn batch() -> Self {
554 Self {
555 allow_sorry: false,
556 strict_instances: true,
557 kernel_check: true,
558 trace_elaboration: false,
559 ..Self::default()
560 }
561 }
562}
563
564#[allow(dead_code)]
566#[derive(Debug, Clone, Default)]
567pub struct ElabStats {
568 pub num_decls: usize,
570 pub num_mvars_created: usize,
572 pub num_mvars_solved: usize,
574 pub num_unifications: usize,
576 pub num_tactic_steps: usize,
578 pub num_instance_lookups: usize,
580 pub num_sorry: usize,
582 pub num_coercions: usize,
584 pub max_depth_reached: u32,
586}
587
588impl ElabStats {
589 #[allow(dead_code)]
591 pub fn new() -> Self {
592 Self::default()
593 }
594
595 #[allow(dead_code)]
597 pub fn merge(&mut self, other: &ElabStats) {
598 self.num_decls += other.num_decls;
599 self.num_mvars_created += other.num_mvars_created;
600 self.num_mvars_solved += other.num_mvars_solved;
601 self.num_unifications += other.num_unifications;
602 self.num_tactic_steps += other.num_tactic_steps;
603 self.num_instance_lookups += other.num_instance_lookups;
604 self.num_sorry += other.num_sorry;
605 self.num_coercions += other.num_coercions;
606 self.max_depth_reached = self.max_depth_reached.max(other.max_depth_reached);
607 }
608
609 #[allow(dead_code)]
611 pub fn mvar_solve_rate(&self) -> f64 {
612 if self.num_mvars_created == 0 {
613 1.0
614 } else {
615 self.num_mvars_solved as f64 / self.num_mvars_created as f64
616 }
617 }
618
619 #[allow(dead_code)]
621 pub fn all_mvars_solved(&self) -> bool {
622 self.num_mvars_created == self.num_mvars_solved
623 }
624}
625
626#[allow(dead_code)]
628#[derive(Debug, Clone, PartialEq, Eq)]
629pub enum ElabErrorCode {
630 UnknownName,
632 TypeMismatch,
634 UnsolvedMvar,
636 AmbiguousInstance,
638 NoInstance,
640 UnificationFailed,
642 IllTyped,
644 TacticFailed,
646 NonExhaustiveMatch,
648 SyntaxError,
650 KernelRejected,
652 SorryNotAllowed,
654 RecursionLimit,
656 MutualCycle,
658 Other,
660}
661
662impl std::fmt::Display for ElabErrorCode {
663 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
664 let s = match self {
665 ElabErrorCode::UnknownName => "unknown name",
666 ElabErrorCode::TypeMismatch => "type mismatch",
667 ElabErrorCode::UnsolvedMvar => "unsolved metavariable",
668 ElabErrorCode::AmbiguousInstance => "ambiguous instance",
669 ElabErrorCode::NoInstance => "no instance found",
670 ElabErrorCode::UnificationFailed => "unification failed",
671 ElabErrorCode::IllTyped => "ill-typed expression",
672 ElabErrorCode::TacticFailed => "tactic failed",
673 ElabErrorCode::NonExhaustiveMatch => "non-exhaustive match",
674 ElabErrorCode::SyntaxError => "syntax error",
675 ElabErrorCode::KernelRejected => "kernel rejected term",
676 ElabErrorCode::SorryNotAllowed => "sorry not allowed",
677 ElabErrorCode::RecursionLimit => "recursion limit exceeded",
678 ElabErrorCode::MutualCycle => "mutual recursion cycle",
679 ElabErrorCode::Other => "elaboration error",
680 };
681 write!(f, "{}", s)
682 }
683}
684
685#[allow(dead_code)]
687#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
688pub enum ElabStage {
689 NameResolution,
691 TypeInference,
693 ImplicitArgs,
695 InstanceResolution,
697 Unification,
699 Coercions,
701 MacroExpansion,
703 TacticExecution,
705 KernelValidation,
707}
708
709impl ElabStage {
710 #[allow(dead_code)]
712 pub fn all_in_order() -> &'static [ElabStage] {
713 &[
714 ElabStage::NameResolution,
715 ElabStage::TypeInference,
716 ElabStage::ImplicitArgs,
717 ElabStage::InstanceResolution,
718 ElabStage::Unification,
719 ElabStage::Coercions,
720 ElabStage::MacroExpansion,
721 ElabStage::TacticExecution,
722 ElabStage::KernelValidation,
723 ]
724 }
725
726 #[allow(dead_code)]
728 pub fn name(&self) -> &'static str {
729 match self {
730 ElabStage::NameResolution => "name_resolution",
731 ElabStage::TypeInference => "type_inference",
732 ElabStage::ImplicitArgs => "implicit_args",
733 ElabStage::InstanceResolution => "instance_resolution",
734 ElabStage::Unification => "unification",
735 ElabStage::Coercions => "coercions",
736 ElabStage::MacroExpansion => "macro_expansion",
737 ElabStage::TacticExecution => "tactic_execution",
738 ElabStage::KernelValidation => "kernel_validation",
739 }
740 }
741}
742
743#[allow(dead_code)]
745pub mod attr_names {
746 pub const SIMP: &str = "simp";
748 pub const REDUCIBLE: &str = "reducible";
750 pub const SEMIREDUCIBLE: &str = "semireducible";
752 pub const IRREDUCIBLE: &str = "irreducible";
754 pub const INLINE: &str = "inline";
756 pub const INSTANCE: &str = "instance";
758 pub const CLASS: &str = "class";
760 pub const DERIVE: &str = "derive";
762 pub const EXT: &str = "ext";
764 pub const NORM_CAST: &str = "norm_cast";
766 pub const PROTECTED: &str = "protected";
768 pub const MACRO: &str = "macro";
770}
771
772#[cfg(test)]
773mod lib_tests {
774 use super::*;
775
776 #[test]
777 fn test_elab_config_default() {
778 let cfg = ElabConfig::default();
779 assert_eq!(cfg.max_depth, 512);
780 assert!(cfg.kernel_check);
781 assert!(!cfg.allow_sorry);
782 }
783
784 #[test]
785 fn test_elab_config_interactive() {
786 let cfg = ElabConfig::interactive();
787 assert!(cfg.allow_sorry);
788 assert!(!cfg.strict_instances);
789 }
790
791 #[test]
792 fn test_elab_config_strict() {
793 let cfg = ElabConfig::strict();
794 assert!(!cfg.allow_sorry);
795 assert!(cfg.strict_instances);
796 assert!(cfg.kernel_check);
797 }
798
799 #[test]
800 fn test_elab_config_batch() {
801 let cfg = ElabConfig::batch();
802 assert!(!cfg.allow_sorry);
803 assert!(!cfg.trace_elaboration);
804 }
805
806 #[test]
807 fn test_elab_config_debug() {
808 let cfg = ElabConfig::debug();
809 assert!(cfg.trace_elaboration);
810 }
811
812 #[test]
813 fn test_elab_stats_default() {
814 let s = ElabStats::new();
815 assert_eq!(s.num_decls, 0);
816 assert!(s.all_mvars_solved());
817 assert_eq!(s.mvar_solve_rate(), 1.0);
818 }
819
820 #[test]
821 fn test_elab_stats_merge() {
822 let mut s1 = ElabStats {
823 num_decls: 3,
824 num_mvars_created: 5,
825 num_mvars_solved: 5,
826 ..Default::default()
827 };
828 let s2 = ElabStats {
829 num_decls: 2,
830 num_mvars_created: 3,
831 num_mvars_solved: 2,
832 max_depth_reached: 100,
833 ..Default::default()
834 };
835 s1.merge(&s2);
836 assert_eq!(s1.num_decls, 5);
837 assert_eq!(s1.num_mvars_created, 8);
838 assert_eq!(s1.max_depth_reached, 100);
839 }
840
841 #[test]
842 fn test_elab_stats_mvar_rate() {
843 let s = ElabStats {
844 num_mvars_created: 10,
845 num_mvars_solved: 8,
846 ..Default::default()
847 };
848 let rate = s.mvar_solve_rate();
849 assert!((rate - 0.8).abs() < 1e-10);
850 assert!(!s.all_mvars_solved());
851 }
852
853 #[test]
854 fn test_elab_error_codes_display() {
855 assert_eq!(format!("{}", ElabErrorCode::TypeMismatch), "type mismatch");
856 assert_eq!(format!("{}", ElabErrorCode::UnknownName), "unknown name");
857 assert_eq!(format!("{}", ElabErrorCode::TacticFailed), "tactic failed");
858 }
859
860 #[test]
861 fn test_elab_stage_order() {
862 let stages = ElabStage::all_in_order();
863 assert_eq!(stages.len(), 9);
864 assert_eq!(stages[0], ElabStage::NameResolution);
865 assert_eq!(stages[8], ElabStage::KernelValidation);
866 }
867
868 #[test]
869 fn test_elab_stage_names() {
870 assert_eq!(ElabStage::Unification.name(), "unification");
871 assert_eq!(ElabStage::KernelValidation.name(), "kernel_validation");
872 }
873
874 #[test]
875 fn test_attr_names() {
876 assert_eq!(attr_names::SIMP, "simp");
877 assert_eq!(attr_names::INSTANCE, "instance");
878 assert_eq!(attr_names::DERIVE, "derive");
879 }
880
881 #[test]
882 fn test_elab_error_other() {
883 assert_eq!(format!("{}", ElabErrorCode::Other), "elaboration error");
884 }
885
886 #[test]
887 fn test_all_error_variants_display() {
888 let variants = [
889 ElabErrorCode::UnknownName,
890 ElabErrorCode::TypeMismatch,
891 ElabErrorCode::UnsolvedMvar,
892 ElabErrorCode::AmbiguousInstance,
893 ElabErrorCode::NoInstance,
894 ElabErrorCode::UnificationFailed,
895 ElabErrorCode::IllTyped,
896 ElabErrorCode::TacticFailed,
897 ElabErrorCode::NonExhaustiveMatch,
898 ElabErrorCode::SyntaxError,
899 ElabErrorCode::KernelRejected,
900 ElabErrorCode::SorryNotAllowed,
901 ElabErrorCode::RecursionLimit,
902 ElabErrorCode::MutualCycle,
903 ElabErrorCode::Other,
904 ];
905 for v in &variants {
906 assert!(!format!("{}", v).is_empty());
907 }
908 }
909}
910
911#[allow(dead_code)]
915#[derive(Default)]
916pub struct ElabPipelineRegistry {
917 pre_passes: Vec<String>,
919 post_passes: Vec<String>,
921 tactic_passes: Vec<String>,
923}
924
925impl ElabPipelineRegistry {
926 #[allow(dead_code)]
928 pub fn new() -> Self {
929 Self::default()
930 }
931
932 #[allow(dead_code)]
934 pub fn add_pre_pass(&mut self, pass_name: impl Into<String>) {
935 self.pre_passes.push(pass_name.into());
936 }
937
938 #[allow(dead_code)]
940 pub fn add_post_pass(&mut self, pass_name: impl Into<String>) {
941 self.post_passes.push(pass_name.into());
942 }
943
944 #[allow(dead_code)]
946 pub fn add_tactic_pass(&mut self, pass_name: impl Into<String>) {
947 self.tactic_passes.push(pass_name.into());
948 }
949
950 #[allow(dead_code)]
952 pub fn num_pre_passes(&self) -> usize {
953 self.pre_passes.len()
954 }
955
956 #[allow(dead_code)]
958 pub fn num_post_passes(&self) -> usize {
959 self.post_passes.len()
960 }
961
962 #[allow(dead_code)]
964 pub fn num_tactic_passes(&self) -> usize {
965 self.tactic_passes.len()
966 }
967
968 #[allow(dead_code)]
970 pub fn all_passes(&self) -> Vec<&str> {
971 self.pre_passes
972 .iter()
973 .chain(self.post_passes.iter())
974 .chain(self.tactic_passes.iter())
975 .map(|s| s.as_str())
976 .collect()
977 }
978}
979
980#[cfg(test)]
981mod pipeline_tests {
982 use super::*;
983
984 #[test]
985 fn test_pipeline_registry_empty() {
986 let reg = ElabPipelineRegistry::new();
987 assert_eq!(reg.num_pre_passes(), 0);
988 assert_eq!(reg.num_post_passes(), 0);
989 assert!(reg.all_passes().is_empty());
990 }
991
992 #[test]
993 fn test_pipeline_registry_add_passes() {
994 let mut reg = ElabPipelineRegistry::new();
995 reg.add_pre_pass("normalize");
996 reg.add_post_pass("kernel_check");
997 reg.add_tactic_pass("simp_prep");
998 assert_eq!(reg.num_pre_passes(), 1);
999 assert_eq!(reg.num_post_passes(), 1);
1000 assert_eq!(reg.num_tactic_passes(), 1);
1001 assert_eq!(reg.all_passes().len(), 3);
1002 }
1003}
1004
1005#[allow(dead_code)]
1011#[derive(Debug, Clone, PartialEq, Eq)]
1012pub enum ElabNote {
1013 Hint(String),
1015 Warning(String),
1017 Info(String),
1019 SorryUsed {
1021 declaration: String,
1023 },
1024 ImplicitUniverse(String),
1026}
1027
1028impl ElabNote {
1029 #[allow(dead_code)]
1031 pub fn prefix(&self) -> &'static str {
1032 match self {
1033 ElabNote::Hint(_) => "hint",
1034 ElabNote::Warning(_) => "warning",
1035 ElabNote::Info(_) => "info",
1036 ElabNote::SorryUsed { .. } => "sorry",
1037 ElabNote::ImplicitUniverse(_) => "universe",
1038 }
1039 }
1040
1041 #[allow(dead_code)]
1043 pub fn message(&self) -> &str {
1044 match self {
1045 ElabNote::Hint(s)
1046 | ElabNote::Warning(s)
1047 | ElabNote::Info(s)
1048 | ElabNote::ImplicitUniverse(s) => s,
1049 ElabNote::SorryUsed { declaration } => declaration,
1050 }
1051 }
1052
1053 #[allow(dead_code)]
1055 pub fn is_warning_like(&self) -> bool {
1056 matches!(self, ElabNote::Warning(_) | ElabNote::SorryUsed { .. })
1057 }
1058}
1059
1060impl std::fmt::Display for ElabNote {
1061 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1062 write!(f, "[{}] {}", self.prefix(), self.message())
1063 }
1064}
1065
1066#[allow(dead_code)]
1068#[derive(Debug, Clone, Default)]
1069pub struct ElabNoteSet {
1070 notes: Vec<ElabNote>,
1071}
1072
1073impl ElabNoteSet {
1074 #[allow(dead_code)]
1076 pub fn new() -> Self {
1077 Self::default()
1078 }
1079
1080 #[allow(dead_code)]
1082 pub fn add(&mut self, note: ElabNote) {
1083 self.notes.push(note);
1084 }
1085
1086 #[allow(dead_code)]
1088 pub fn add_hint(&mut self, msg: impl Into<String>) {
1089 self.add(ElabNote::Hint(msg.into()));
1090 }
1091
1092 #[allow(dead_code)]
1094 pub fn add_warning(&mut self, msg: impl Into<String>) {
1095 self.add(ElabNote::Warning(msg.into()));
1096 }
1097
1098 #[allow(dead_code)]
1100 pub fn add_info(&mut self, msg: impl Into<String>) {
1101 self.add(ElabNote::Info(msg.into()));
1102 }
1103
1104 #[allow(dead_code)]
1106 pub fn add_sorry(&mut self, decl: impl Into<String>) {
1107 self.add(ElabNote::SorryUsed {
1108 declaration: decl.into(),
1109 });
1110 }
1111
1112 #[allow(dead_code)]
1114 pub fn len(&self) -> usize {
1115 self.notes.len()
1116 }
1117
1118 #[allow(dead_code)]
1120 pub fn is_empty(&self) -> bool {
1121 self.notes.is_empty()
1122 }
1123
1124 #[allow(dead_code)]
1126 pub fn has_warnings(&self) -> bool {
1127 self.notes.iter().any(|n| n.is_warning_like())
1128 }
1129
1130 #[allow(dead_code)]
1132 pub fn warnings(&self) -> Vec<&ElabNote> {
1133 self.notes.iter().filter(|n| n.is_warning_like()).collect()
1134 }
1135
1136 #[allow(dead_code)]
1138 pub fn iter(&self) -> impl Iterator<Item = &ElabNote> {
1139 self.notes.iter()
1140 }
1141
1142 #[allow(dead_code)]
1144 pub fn merge(&mut self, other: ElabNoteSet) {
1145 self.notes.extend(other.notes);
1146 }
1147
1148 #[allow(dead_code)]
1150 pub fn clear(&mut self) {
1151 self.notes.clear();
1152 }
1153}
1154
1155#[allow(dead_code)]
1161pub mod tactic_names {
1162 pub const INTRO: &str = "intro";
1164 pub const INTROS: &str = "intros";
1166 pub const APPLY: &str = "apply";
1168 pub const EXACT: &str = "exact";
1170 pub const REFL: &str = "refl";
1172 pub const ASSUMPTION: &str = "assumption";
1174 pub const TRIVIAL: &str = "trivial";
1176 pub const SORRY: &str = "sorry";
1178 pub const RW: &str = "rw";
1180 pub const SIMP: &str = "simp";
1182 pub const SIMP_ALL: &str = "simp_all";
1184 pub const CASES: &str = "cases";
1186 pub const INDUCTION: &str = "induction";
1188 pub const CONSTRUCTOR: &str = "constructor";
1190 pub const LEFT: &str = "left";
1192 pub const RIGHT: &str = "right";
1194 pub const EXISTSI: &str = "existsi";
1196 pub const USE: &str = "use";
1198 pub const PUSH_NEG: &str = "push_neg";
1200 pub const BY_CONTRA: &str = "by_contra";
1202 pub const CONTRAPOSE: &str = "contrapose";
1204 pub const SPLIT: &str = "split";
1206 pub const EXFALSO: &str = "exfalso";
1208 pub const LINARITH: &str = "linarith";
1210 pub const RING: &str = "ring";
1212 pub const NORM_CAST: &str = "norm_cast";
1214 pub const CLEAR: &str = "clear";
1216 pub const HAVE: &str = "have";
1218 pub const OBTAIN: &str = "obtain";
1220 pub const SHOW: &str = "show";
1222 pub const REVERT: &str = "revert";
1224 pub const SPECIALIZE: &str = "specialize";
1226 pub const RENAME: &str = "rename";
1228}
1229
1230#[allow(dead_code)]
1232pub fn is_known_tactic(name: &str) -> bool {
1233 matches!(
1234 name,
1235 "intro"
1236 | "intros"
1237 | "apply"
1238 | "exact"
1239 | "refl"
1240 | "assumption"
1241 | "trivial"
1242 | "sorry"
1243 | "rw"
1244 | "simp"
1245 | "simp_all"
1246 | "cases"
1247 | "induction"
1248 | "constructor"
1249 | "left"
1250 | "right"
1251 | "existsi"
1252 | "use"
1253 | "push_neg"
1254 | "by_contra"
1255 | "by_contradiction"
1256 | "contrapose"
1257 | "split"
1258 | "exfalso"
1259 | "linarith"
1260 | "ring"
1261 | "norm_cast"
1262 | "clear"
1263 | "have"
1264 | "obtain"
1265 | "show"
1266 | "revert"
1267 | "specialize"
1268 | "rename"
1269 | "repeat"
1270 | "first"
1271 | "try"
1272 | "all_goals"
1273 | "any_goals"
1274 | "field_simp"
1275 | "push_cast"
1276 | "exact_mod_cast"
1277 )
1278}
1279
1280#[allow(dead_code)]
1282pub fn tactic_category(name: &str) -> &'static str {
1283 match name {
1284 "intro" | "intros" | "revert" | "clear" | "rename" | "obtain" | "have" | "show" => {
1285 "context"
1286 }
1287 "apply" | "exact" | "assumption" | "trivial" | "sorry" | "refl" => "proof-search",
1288 "rw" | "simp" | "simp_all" | "field_simp" | "ring" | "linarith" | "norm_cast"
1289 | "push_cast" | "exact_mod_cast" => "rewriting",
1290 "cases" | "induction" | "constructor" | "left" | "right" | "existsi" | "use" | "split"
1291 | "exfalso" => "structure",
1292 "push_neg" | "by_contra" | "by_contradiction" | "contrapose" => "logic",
1293 "repeat" | "first" | "try" | "all_goals" | "any_goals" => "combinator",
1294 "specialize" => "context",
1295 _ => "unknown",
1296 }
1297}
1298
1299#[allow(dead_code)]
1305#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
1306pub enum Reducibility {
1307 Reducible = 0,
1309 #[default]
1311 SemiReducible = 1,
1312 Irreducible = 2,
1314}
1315
1316impl Reducibility {
1317 #[allow(dead_code)]
1319 pub fn is_reducible(&self) -> bool {
1320 *self == Reducibility::Reducible
1321 }
1322 #[allow(dead_code)]
1324 pub fn is_irreducible(&self) -> bool {
1325 *self == Reducibility::Irreducible
1326 }
1327 #[allow(dead_code)]
1329 pub fn attr_name(&self) -> &'static str {
1330 match self {
1331 Reducibility::Reducible => "reducible",
1332 Reducibility::SemiReducible => "semireducible",
1333 Reducibility::Irreducible => "irreducible",
1334 }
1335 }
1336}
1337
1338impl std::fmt::Display for Reducibility {
1339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1340 write!(f, "{}", self.attr_name())
1341 }
1342}
1343
1344#[cfg(test)]
1345mod elab_lib_extra_tests {
1346 use super::*;
1347
1348 #[test]
1349 fn test_elab_note_hint() {
1350 let n = ElabNote::Hint("use norm_num".to_string());
1351 assert_eq!(n.prefix(), "hint");
1352 assert!(!n.is_warning_like());
1353 }
1354
1355 #[test]
1356 fn test_elab_note_warning() {
1357 let n = ElabNote::Warning("unsupported construct".to_string());
1358 assert!(n.is_warning_like());
1359 }
1360
1361 #[test]
1362 fn test_elab_note_sorry() {
1363 let n = ElabNote::SorryUsed {
1364 declaration: "myTheorem".to_string(),
1365 };
1366 assert!(n.is_warning_like());
1367 assert_eq!(n.message(), "myTheorem");
1368 }
1369
1370 #[test]
1371 fn test_elab_note_display() {
1372 let n = ElabNote::Info("no issues".to_string());
1373 let s = format!("{}", n);
1374 assert!(s.contains("info"));
1375 }
1376
1377 #[test]
1378 fn test_elab_note_set_add_warning() {
1379 let mut ns = ElabNoteSet::new();
1380 ns.add_warning("potential issue");
1381 assert!(ns.has_warnings());
1382 assert_eq!(ns.len(), 1);
1383 }
1384
1385 #[test]
1386 fn test_elab_note_set_merge() {
1387 let mut a = ElabNoteSet::new();
1388 a.add_hint("hint 1");
1389 let mut b = ElabNoteSet::new();
1390 b.add_info("info 1");
1391 a.merge(b);
1392 assert_eq!(a.len(), 2);
1393 }
1394
1395 #[test]
1396 fn test_elab_note_set_clear() {
1397 let mut ns = ElabNoteSet::new();
1398 ns.add_sorry("myThm");
1399 ns.clear();
1400 assert!(ns.is_empty());
1401 }
1402
1403 #[test]
1404 fn test_is_known_tactic() {
1405 assert!(is_known_tactic("intro"));
1406 assert!(is_known_tactic("simp"));
1407 assert!(is_known_tactic("ring"));
1408 assert!(!is_known_tactic("unknownTac"));
1409 }
1410
1411 #[test]
1412 fn test_tactic_category() {
1413 assert_eq!(tactic_category("intro"), "context");
1414 assert_eq!(tactic_category("simp"), "rewriting");
1415 assert_eq!(tactic_category("cases"), "structure");
1416 assert_eq!(tactic_category("push_neg"), "logic");
1417 assert_eq!(tactic_category("repeat"), "combinator");
1418 }
1419
1420 #[test]
1421 fn test_reducibility_ordering() {
1422 assert!(Reducibility::Reducible < Reducibility::SemiReducible);
1423 assert!(Reducibility::SemiReducible < Reducibility::Irreducible);
1424 }
1425
1426 #[test]
1427 fn test_reducibility_attr_names() {
1428 assert_eq!(Reducibility::Reducible.attr_name(), "reducible");
1429 assert_eq!(Reducibility::Irreducible.attr_name(), "irreducible");
1430 }
1431
1432 #[test]
1433 fn test_reducibility_default() {
1434 assert_eq!(Reducibility::default(), Reducibility::SemiReducible);
1435 }
1436
1437 #[test]
1438 fn test_tactic_names_intro() {
1439 assert_eq!(tactic_names::INTRO, "intro");
1440 assert_eq!(tactic_names::SORRY, "sorry");
1441 }
1442
1443 #[test]
1444 fn test_elab_note_warnings_filter() {
1445 let mut ns = ElabNoteSet::new();
1446 ns.add_hint("h1");
1447 ns.add_warning("w1");
1448 ns.add_sorry("decl");
1449 let warns = ns.warnings();
1450 assert_eq!(warns.len(), 2);
1451 }
1452}
1453
1454#[allow(dead_code)]
1460pub trait ElabPass {
1461 fn name(&self) -> &str;
1463
1464 fn run(&self, expr: oxilean_kernel::Expr) -> Result<oxilean_kernel::Expr, String>;
1466
1467 fn enabled_by_default(&self) -> bool {
1469 true
1470 }
1471}
1472
1473#[allow(dead_code)]
1475pub struct ElabPipeline {
1476 passes: Vec<Box<dyn ElabPass>>,
1477 enabled: Vec<bool>,
1478}
1479
1480impl ElabPipeline {
1481 #[allow(dead_code)]
1483 pub fn new() -> Self {
1484 Self {
1485 passes: Vec::new(),
1486 enabled: Vec::new(),
1487 }
1488 }
1489
1490 #[allow(dead_code)]
1492 pub fn add<P: ElabPass + 'static>(&mut self, pass: P) {
1493 let enabled = pass.enabled_by_default();
1494 self.passes.push(Box::new(pass));
1495 self.enabled.push(enabled);
1496 }
1497
1498 #[allow(dead_code)]
1500 pub fn set_enabled(&mut self, idx: usize, enabled: bool) {
1501 if let Some(e) = self.enabled.get_mut(idx) {
1502 *e = enabled;
1503 }
1504 }
1505
1506 #[allow(dead_code)]
1508 pub fn run_all(&self, expr: oxilean_kernel::Expr) -> Result<oxilean_kernel::Expr, String> {
1509 let mut current = expr;
1510 for (pass, &enabled) in self.passes.iter().zip(self.enabled.iter()) {
1511 if enabled {
1512 current = pass.run(current)?;
1513 }
1514 }
1515 Ok(current)
1516 }
1517
1518 #[allow(dead_code)]
1520 pub fn len(&self) -> usize {
1521 self.passes.len()
1522 }
1523
1524 #[allow(dead_code)]
1526 pub fn is_empty(&self) -> bool {
1527 self.passes.is_empty()
1528 }
1529}
1530
1531impl Default for ElabPipeline {
1532 fn default() -> Self {
1533 Self::new()
1534 }
1535}
1536
1537#[derive(Debug, Clone)]
1543#[allow(dead_code)]
1544pub struct ElabConfigExt {
1545 pub max_metavars: usize,
1547 pub max_depth: u32,
1549 pub warn_sorry: bool,
1551 pub check_unused_hyps: bool,
1553 pub allow_sorry: bool,
1555 pub resolve_coercions: bool,
1557 pub bidir_checking: bool,
1559 pub universe_checking: UniverseCheckMode,
1561}
1562
1563#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1565#[allow(dead_code)]
1566pub enum UniverseCheckMode {
1567 Full,
1569 Partial,
1571 Skip,
1573}
1574
1575impl Default for ElabConfigExt {
1576 fn default() -> Self {
1577 Self {
1578 max_metavars: 10_000,
1579 max_depth: 500,
1580 warn_sorry: true,
1581 check_unused_hyps: false,
1582 allow_sorry: true,
1583 resolve_coercions: true,
1584 bidir_checking: true,
1585 universe_checking: UniverseCheckMode::Partial,
1586 }
1587 }
1588}
1589
1590impl ElabConfigExt {
1591 #[allow(dead_code)]
1593 pub fn new() -> Self {
1594 Self::default()
1595 }
1596
1597 #[allow(dead_code)]
1599 pub fn strict() -> Self {
1600 Self {
1601 allow_sorry: false,
1602 warn_sorry: true,
1603 check_unused_hyps: true,
1604 universe_checking: UniverseCheckMode::Full,
1605 ..Self::default()
1606 }
1607 }
1608
1609 #[allow(dead_code)]
1611 pub fn permissive() -> Self {
1612 Self {
1613 allow_sorry: true,
1614 warn_sorry: false,
1615 check_unused_hyps: false,
1616 universe_checking: UniverseCheckMode::Skip,
1617 ..Self::default()
1618 }
1619 }
1620
1621 #[allow(dead_code)]
1623 pub fn sorry_warned(&self) -> bool {
1624 self.allow_sorry && self.warn_sorry
1625 }
1626}
1627
1628#[derive(Debug, Clone, Default)]
1634#[allow(dead_code)]
1635pub struct ElabMetrics {
1636 pub declarations_elaborated: u64,
1638 pub tactics_executed: u64,
1640 pub sorry_count: u64,
1642 pub unification_steps: u64,
1644 pub metavars_created: u64,
1646 pub metavars_solved: u64,
1648 pub inference_steps: u64,
1650 pub failures: u64,
1652}
1653
1654impl ElabMetrics {
1655 #[allow(dead_code)]
1657 pub fn new() -> Self {
1658 Self::default()
1659 }
1660
1661 #[allow(dead_code)]
1663 pub fn record_decl(&mut self) {
1664 self.declarations_elaborated += 1;
1665 }
1666
1667 #[allow(dead_code)]
1669 pub fn record_tactic(&mut self) {
1670 self.tactics_executed += 1;
1671 }
1672
1673 #[allow(dead_code)]
1675 pub fn record_sorry(&mut self) {
1676 self.sorry_count += 1;
1677 }
1678
1679 #[allow(dead_code)]
1681 pub fn record_failure(&mut self) {
1682 self.failures += 1;
1683 }
1684
1685 #[allow(dead_code)]
1687 pub fn solve_rate(&self) -> f64 {
1688 if self.metavars_created == 0 {
1689 1.0
1690 } else {
1691 self.metavars_solved as f64 / self.metavars_created as f64
1692 }
1693 }
1694
1695 #[allow(dead_code)]
1697 pub fn merge(&mut self, other: &ElabMetrics) {
1698 self.declarations_elaborated += other.declarations_elaborated;
1699 self.tactics_executed += other.tactics_executed;
1700 self.sorry_count += other.sorry_count;
1701 self.unification_steps += other.unification_steps;
1702 self.metavars_created += other.metavars_created;
1703 self.metavars_solved += other.metavars_solved;
1704 self.inference_steps += other.inference_steps;
1705 self.failures += other.failures;
1706 }
1707}
1708
1709#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1715#[allow(dead_code)]
1716pub enum DeclKind {
1717 Def,
1719 Theorem,
1721 Axiom,
1723 Inductive,
1725 Structure,
1727 Class,
1729 Instance,
1731 Namespace,
1733 Abbrev,
1735 Noncomputable,
1737 Opaque,
1739}
1740
1741impl DeclKind {
1742 #[allow(dead_code)]
1744 pub fn keyword(&self) -> &'static str {
1745 match self {
1746 DeclKind::Def => "def",
1747 DeclKind::Theorem => "theorem",
1748 DeclKind::Axiom => "axiom",
1749 DeclKind::Inductive => "inductive",
1750 DeclKind::Structure => "structure",
1751 DeclKind::Class => "class",
1752 DeclKind::Instance => "instance",
1753 DeclKind::Namespace => "namespace",
1754 DeclKind::Abbrev => "abbrev",
1755 DeclKind::Noncomputable => "noncomputable",
1756 DeclKind::Opaque => "opaque",
1757 }
1758 }
1759
1760 #[allow(dead_code)]
1762 pub fn produces_term(&self) -> bool {
1763 matches!(
1764 self,
1765 DeclKind::Def
1766 | DeclKind::Theorem
1767 | DeclKind::Axiom
1768 | DeclKind::Abbrev
1769 | DeclKind::Noncomputable
1770 | DeclKind::Opaque
1771 )
1772 }
1773
1774 #[allow(dead_code)]
1776 pub fn requires_proof(&self) -> bool {
1777 matches!(self, DeclKind::Theorem)
1778 }
1779
1780 #[allow(dead_code)]
1782 pub fn is_computable(&self) -> bool {
1783 !matches!(self, DeclKind::Noncomputable | DeclKind::Axiom)
1784 }
1785}
1786
1787impl std::fmt::Display for DeclKind {
1788 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1789 write!(f, "{}", self.keyword())
1790 }
1791}
1792
1793#[derive(Debug, Clone)]
1799#[allow(dead_code)]
1800pub struct ProofStateSnapshot {
1801 pub id: u64,
1803 pub description: String,
1805 pub goal_count: usize,
1807 pub hypothesis_names: Vec<oxilean_kernel::Name>,
1809}
1810
1811impl ProofStateSnapshot {
1812 #[allow(dead_code)]
1814 pub fn new(
1815 id: u64,
1816 description: impl Into<String>,
1817 goal_count: usize,
1818 hypothesis_names: Vec<oxilean_kernel::Name>,
1819 ) -> Self {
1820 Self {
1821 id,
1822 description: description.into(),
1823 goal_count,
1824 hypothesis_names,
1825 }
1826 }
1827
1828 #[allow(dead_code)]
1830 pub fn is_complete(&self) -> bool {
1831 self.goal_count == 0
1832 }
1833}
1834
1835#[derive(Debug, Default)]
1837#[allow(dead_code)]
1838pub struct ProofHistory {
1839 snapshots: Vec<ProofStateSnapshot>,
1840 current: usize,
1841}
1842
1843impl ProofHistory {
1844 #[allow(dead_code)]
1846 pub fn new() -> Self {
1847 Self::default()
1848 }
1849
1850 #[allow(dead_code)]
1852 pub fn push(&mut self, snap: ProofStateSnapshot) {
1853 self.snapshots.truncate(self.current);
1855 self.snapshots.push(snap);
1856 self.current = self.snapshots.len();
1857 }
1858
1859 #[allow(dead_code)]
1861 pub fn undo(&mut self) -> Option<&ProofStateSnapshot> {
1862 if self.current > 1 {
1863 self.current -= 1;
1864 self.snapshots.get(self.current - 1)
1865 } else {
1866 None
1867 }
1868 }
1869
1870 #[allow(dead_code)]
1872 pub fn redo(&mut self) -> Option<&ProofStateSnapshot> {
1873 if self.current < self.snapshots.len() {
1874 self.current += 1;
1875 self.snapshots.get(self.current - 1)
1876 } else {
1877 None
1878 }
1879 }
1880
1881 #[allow(dead_code)]
1883 pub fn current(&self) -> Option<&ProofStateSnapshot> {
1884 self.snapshots.get(self.current.saturating_sub(1))
1885 }
1886
1887 #[allow(dead_code)]
1889 pub fn len(&self) -> usize {
1890 self.snapshots.len()
1891 }
1892
1893 #[allow(dead_code)]
1895 pub fn is_empty(&self) -> bool {
1896 self.snapshots.is_empty()
1897 }
1898}
1899
1900#[derive(Debug, Clone)]
1906#[allow(dead_code)]
1907pub struct CoercionExt {
1908 pub from_type: oxilean_kernel::Name,
1910 pub to_type: oxilean_kernel::Name,
1912 pub coercion_fn: oxilean_kernel::Name,
1914 pub priority: u32,
1916}
1917
1918impl CoercionExt {
1919 #[allow(dead_code)]
1921 pub fn new(
1922 from_type: oxilean_kernel::Name,
1923 to_type: oxilean_kernel::Name,
1924 coercion_fn: oxilean_kernel::Name,
1925 ) -> Self {
1926 Self {
1927 from_type,
1928 to_type,
1929 coercion_fn,
1930 priority: 0,
1931 }
1932 }
1933
1934 #[allow(dead_code)]
1936 pub fn with_priority(mut self, priority: u32) -> Self {
1937 self.priority = priority;
1938 self
1939 }
1940
1941 #[allow(dead_code)]
1943 pub fn apply(&self, expr: oxilean_kernel::Expr) -> oxilean_kernel::Expr {
1944 use oxilean_kernel::Expr;
1945 Expr::App(
1946 Box::new(Expr::Const(self.coercion_fn.clone(), vec![])),
1947 Box::new(expr),
1948 )
1949 }
1950}
1951
1952#[derive(Debug, Default)]
1954#[allow(dead_code)]
1955pub struct CoercionRegistryExt {
1956 coercions: Vec<CoercionExt>,
1957}
1958
1959impl CoercionRegistryExt {
1960 #[allow(dead_code)]
1962 pub fn new() -> Self {
1963 Self::default()
1964 }
1965
1966 #[allow(dead_code)]
1968 pub fn register(&mut self, coercion: CoercionExt) {
1969 self.coercions.push(coercion);
1970 }
1971
1972 #[allow(dead_code)]
1974 pub fn find(
1975 &self,
1976 from: &oxilean_kernel::Name,
1977 to: &oxilean_kernel::Name,
1978 ) -> Option<&CoercionExt> {
1979 self.coercions
1980 .iter()
1981 .filter(|c| &c.from_type == from && &c.to_type == to)
1982 .max_by_key(|c| c.priority)
1983 }
1984
1985 #[allow(dead_code)]
1987 pub fn coercions_from(&self, from: &oxilean_kernel::Name) -> Vec<&CoercionExt> {
1988 self.coercions
1989 .iter()
1990 .filter(|c| &c.from_type == from)
1991 .collect()
1992 }
1993
1994 #[allow(dead_code)]
1996 pub fn len(&self) -> usize {
1997 self.coercions.len()
1998 }
1999
2000 #[allow(dead_code)]
2002 pub fn is_empty(&self) -> bool {
2003 self.coercions.is_empty()
2004 }
2005
2006 #[allow(dead_code)]
2008 pub fn remove_from(&mut self, from: &oxilean_kernel::Name) {
2009 self.coercions.retain(|c| &c.from_type != from);
2010 }
2011}
2012
2013#[derive(Debug, Clone)]
2019#[allow(dead_code)]
2020pub struct ClassInstance {
2021 pub class: oxilean_kernel::Name,
2023 pub instance: oxilean_kernel::Name,
2025 pub type_params: Vec<oxilean_kernel::Expr>,
2027 pub priority: u32,
2029 pub is_default: bool,
2031}
2032
2033impl ClassInstance {
2034 #[allow(dead_code)]
2036 pub fn new(class: oxilean_kernel::Name, instance: oxilean_kernel::Name) -> Self {
2037 Self {
2038 class,
2039 instance,
2040 type_params: Vec::new(),
2041 priority: 100,
2042 is_default: false,
2043 }
2044 }
2045
2046 #[allow(dead_code)]
2048 pub fn as_default(mut self) -> Self {
2049 self.is_default = true;
2050 self
2051 }
2052
2053 #[allow(dead_code)]
2055 pub fn with_priority(mut self, priority: u32) -> Self {
2056 self.priority = priority;
2057 self
2058 }
2059
2060 #[allow(dead_code)]
2062 pub fn with_type_param(mut self, param: oxilean_kernel::Expr) -> Self {
2063 self.type_params.push(param);
2064 self
2065 }
2066}
2067
2068#[derive(Debug, Default)]
2070#[allow(dead_code)]
2071pub struct InstanceRegistry {
2072 instances: Vec<ClassInstance>,
2073}
2074
2075impl InstanceRegistry {
2076 #[allow(dead_code)]
2078 pub fn new() -> Self {
2079 Self::default()
2080 }
2081
2082 #[allow(dead_code)]
2084 pub fn register(&mut self, instance: ClassInstance) {
2085 self.instances.push(instance);
2086 }
2087
2088 #[allow(dead_code)]
2090 pub fn instances_of(&self, class: &oxilean_kernel::Name) -> Vec<&ClassInstance> {
2091 let mut results: Vec<&ClassInstance> = self
2092 .instances
2093 .iter()
2094 .filter(|i| &i.class == class)
2095 .collect();
2096 results.sort_by(|a, b| b.priority.cmp(&a.priority));
2097 results
2098 }
2099
2100 #[allow(dead_code)]
2102 pub fn default_instance(&self, class: &oxilean_kernel::Name) -> Option<&ClassInstance> {
2103 self.instances_of(class).into_iter().find(|i| i.is_default)
2104 }
2105
2106 #[allow(dead_code)]
2108 pub fn len(&self) -> usize {
2109 self.instances.len()
2110 }
2111
2112 #[allow(dead_code)]
2114 pub fn is_empty(&self) -> bool {
2115 self.instances.is_empty()
2116 }
2117
2118 #[allow(dead_code)]
2120 pub fn remove_class(&mut self, class: &oxilean_kernel::Name) {
2121 self.instances.retain(|i| &i.class != class);
2122 }
2123}
2124
2125#[derive(Debug, Clone)]
2131#[allow(dead_code)]
2132pub struct DeclAttribute {
2133 pub name: String,
2135 pub arg: Option<String>,
2137 pub decl: oxilean_kernel::Name,
2139}
2140
2141impl DeclAttribute {
2142 #[allow(dead_code)]
2144 pub fn new(name: impl Into<String>, decl: oxilean_kernel::Name) -> Self {
2145 Self {
2146 name: name.into(),
2147 arg: None,
2148 decl,
2149 }
2150 }
2151
2152 #[allow(dead_code)]
2154 pub fn with_arg(mut self, arg: impl Into<String>) -> Self {
2155 self.arg = Some(arg.into());
2156 self
2157 }
2158}
2159
2160#[derive(Debug, Default)]
2162#[allow(dead_code)]
2163pub struct AttributeRegistry {
2164 attrs: Vec<DeclAttribute>,
2165}
2166
2167impl AttributeRegistry {
2168 #[allow(dead_code)]
2170 pub fn new() -> Self {
2171 Self::default()
2172 }
2173
2174 #[allow(dead_code)]
2176 pub fn register(&mut self, attr: DeclAttribute) {
2177 self.attrs.push(attr);
2178 }
2179
2180 #[allow(dead_code)]
2182 pub fn attrs_of(&self, decl: &oxilean_kernel::Name) -> Vec<&DeclAttribute> {
2183 self.attrs.iter().filter(|a| &a.decl == decl).collect()
2184 }
2185
2186 #[allow(dead_code)]
2188 pub fn decls_with(&self, attr_name: &str) -> Vec<&oxilean_kernel::Name> {
2189 self.attrs
2190 .iter()
2191 .filter(|a| a.name == attr_name)
2192 .map(|a| &a.decl)
2193 .collect()
2194 }
2195
2196 #[allow(dead_code)]
2198 pub fn len(&self) -> usize {
2199 self.attrs.len()
2200 }
2201
2202 #[allow(dead_code)]
2204 pub fn is_empty(&self) -> bool {
2205 self.attrs.is_empty()
2206 }
2207}
2208
2209#[derive(Debug, Clone)]
2215#[allow(dead_code)]
2216pub struct NamespaceEntry {
2217 pub name: oxilean_kernel::Name,
2219 pub is_open: bool,
2221 pub parent: Option<oxilean_kernel::Name>,
2223}
2224
2225impl NamespaceEntry {
2226 #[allow(dead_code)]
2228 pub fn new(name: oxilean_kernel::Name, parent: Option<oxilean_kernel::Name>) -> Self {
2229 Self {
2230 name,
2231 is_open: false,
2232 parent,
2233 }
2234 }
2235}
2236
2237#[derive(Debug, Default)]
2239#[allow(dead_code)]
2240pub struct NamespaceManager {
2241 namespaces: Vec<NamespaceEntry>,
2242 open_stack: Vec<oxilean_kernel::Name>,
2243}
2244
2245impl NamespaceManager {
2246 #[allow(dead_code)]
2248 pub fn new() -> Self {
2249 Self::default()
2250 }
2251
2252 #[allow(dead_code)]
2254 pub fn open(&mut self, name: oxilean_kernel::Name) {
2255 if !self.namespaces.iter().any(|ns| ns.name == name) {
2257 self.namespaces
2258 .push(NamespaceEntry::new(name.clone(), self.current_namespace()));
2259 }
2260 if let Some(ns) = self.namespaces.iter_mut().find(|ns| ns.name == name) {
2262 ns.is_open = true;
2263 }
2264 self.open_stack.push(name);
2265 }
2266
2267 #[allow(dead_code)]
2269 pub fn close(&mut self) -> Option<oxilean_kernel::Name> {
2270 if let Some(name) = self.open_stack.pop() {
2271 if let Some(ns) = self.namespaces.iter_mut().find(|ns| ns.name == name) {
2272 ns.is_open = !self.open_stack.contains(&name);
2273 }
2274 Some(name)
2275 } else {
2276 None
2277 }
2278 }
2279
2280 #[allow(dead_code)]
2282 pub fn current_namespace(&self) -> Option<oxilean_kernel::Name> {
2283 self.open_stack.last().cloned()
2284 }
2285
2286 #[allow(dead_code)]
2288 pub fn open_namespaces(&self) -> &[oxilean_kernel::Name] {
2289 &self.open_stack
2290 }
2291
2292 #[allow(dead_code)]
2294 pub fn qualify(&self, name: &str) -> oxilean_kernel::Name {
2295 match self.current_namespace() {
2296 Some(ns) => oxilean_kernel::Name::str(format!("{}.{}", ns, name)),
2297 None => oxilean_kernel::Name::str(name),
2298 }
2299 }
2300
2301 #[allow(dead_code)]
2303 pub fn depth(&self) -> usize {
2304 self.open_stack.len()
2305 }
2306}
2307
2308#[allow(dead_code)]
2314pub fn pretty_expr(expr: &oxilean_kernel::Expr) -> String {
2315 match expr {
2316 Expr::Sort(l) => format!("Sort({:?})", l),
2317 Expr::BVar(i) => format!("#{}", i),
2318 Expr::FVar(fv) => format!("@{}", fv.0),
2319 Expr::Const(name, _) => name.to_string(),
2320 Expr::App(f, a) => format!("({} {})", pretty_expr(f), pretty_expr(a)),
2321 Expr::Lam(_, name, _ty, body) => {
2322 format!("(fun {} => {})", name, pretty_expr(body))
2323 }
2324 Expr::Pi(_, name, ty, body) => {
2325 format!(
2326 "(({} : {}) -> {})",
2327 name,
2328 pretty_expr(ty),
2329 pretty_expr(body)
2330 )
2331 }
2332 Expr::Let(name, _ty, val, body) => {
2333 format!(
2334 "(let {} := {} in {})",
2335 name,
2336 pretty_expr(val),
2337 pretty_expr(body)
2338 )
2339 }
2340 Expr::Lit(lit) => {
2341 use oxilean_kernel::Literal;
2342 match lit {
2343 Literal::Nat(n) => format!("{}", n),
2344 Literal::Str(s) => format!("{:?}", s),
2345 }
2346 }
2347 Expr::Proj(name, idx, inner) => {
2348 format!("{}.{} ({})", name, idx, pretty_expr(inner))
2349 }
2350 }
2351}
2352
2353#[allow(dead_code)]
2355pub fn pretty_expr_list(exprs: &[oxilean_kernel::Expr]) -> String {
2356 exprs.iter().map(pretty_expr).collect::<Vec<_>>().join(", ")
2357}
2358
2359#[allow(dead_code)]
2367pub fn might_be_recursive(name: &oxilean_kernel::Name, body: &oxilean_kernel::Expr) -> bool {
2368 fn contains_name(expr: &Expr, target: &oxilean_kernel::Name) -> bool {
2369 match expr {
2370 Expr::Const(n, _) => n == target,
2371 Expr::App(f, a) => contains_name(f, target) || contains_name(a, target),
2372 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
2373 contains_name(ty, target) || contains_name(body, target)
2374 }
2375 Expr::Let(_, ty, val, b) => {
2376 contains_name(ty, target) || contains_name(val, target) || contains_name(b, target)
2377 }
2378 Expr::Proj(_, _, inner) => contains_name(inner, target),
2379 _ => false,
2380 }
2381 }
2382 contains_name(body, name)
2383}
2384
2385#[allow(dead_code)]
2391pub mod tactic_names_ext {
2392 pub const NORM_NUM: &str = "norm_num";
2394 pub const OMEGA: &str = "omega";
2396 pub const DECIDE: &str = "decide";
2398 pub const NATIVE_DECIDE: &str = "native_decide";
2400 pub const AESOP: &str = "aesop";
2402 pub const TAUTO: &str = "tauto";
2404 pub const FIN_CASES: &str = "fin_cases";
2406 pub const INTERVAL_CASES: &str = "interval_cases";
2408 pub const GCONGR: &str = "gcongr";
2410 pub const POSITIVITY: &str = "positivity";
2412 pub const POLYRITH: &str = "polyrith";
2414 pub const LINEAR_COMBINATION: &str = "linear_combination";
2416 pub const EXT: &str = "ext";
2418 pub const FUNEXT: &str = "funext";
2420 pub const CONGR: &str = "congr";
2422 pub const UNFOLD: &str = "unfold";
2424 pub const CHANGE: &str = "change";
2426 pub const SUBST: &str = "subst";
2428 pub const SYMM: &str = "symm";
2430 pub const TRANS: &str = "trans";
2432 pub const CALC: &str = "calc";
2434 pub const RCASES: &str = "rcases";
2436 pub const RINTRO: &str = "rintro";
2438 pub const REFINE: &str = "refine";
2440 pub const AC_RFL: &str = "ac_rfl";
2442}
2443
2444#[allow(dead_code)]
2446pub fn is_mathlib_tactic(name: &str) -> bool {
2447 matches!(
2448 name,
2449 "norm_num"
2450 | "omega"
2451 | "decide"
2452 | "native_decide"
2453 | "aesop"
2454 | "tauto"
2455 | "fin_cases"
2456 | "interval_cases"
2457 | "gcongr"
2458 | "positivity"
2459 | "polyrith"
2460 | "linear_combination"
2461 | "ext"
2462 | "funext"
2463 | "congr"
2464 | "unfold"
2465 | "change"
2466 | "subst"
2467 | "symm"
2468 | "trans"
2469 | "calc"
2470 | "rcases"
2471 | "rintro"
2472 | "refine"
2473 | "ac_rfl"
2474 )
2475}
2476
2477#[derive(Debug, Clone)]
2483#[allow(dead_code)]
2484pub struct EnvSnapshot {
2485 pub id: u64,
2487 pub decl_count: usize,
2489 pub description: String,
2491}
2492
2493impl EnvSnapshot {
2494 #[allow(dead_code)]
2496 pub fn new(id: u64, decl_count: usize, description: impl Into<String>) -> Self {
2497 Self {
2498 id,
2499 decl_count,
2500 description: description.into(),
2501 }
2502 }
2503}
2504
2505#[derive(Debug, Default)]
2507#[allow(dead_code)]
2508pub struct EnvSnapshotManager {
2509 snapshots: Vec<EnvSnapshot>,
2510 next_id: u64,
2511}
2512
2513impl EnvSnapshotManager {
2514 #[allow(dead_code)]
2516 pub fn new() -> Self {
2517 Self::default()
2518 }
2519
2520 #[allow(dead_code)]
2522 pub fn take(&mut self, decl_count: usize, description: impl Into<String>) -> u64 {
2523 let id = self.next_id;
2524 self.next_id += 1;
2525 self.snapshots
2526 .push(EnvSnapshot::new(id, decl_count, description));
2527 id
2528 }
2529
2530 #[allow(dead_code)]
2532 pub fn get(&self, id: u64) -> Option<&EnvSnapshot> {
2533 self.snapshots.iter().find(|s| s.id == id)
2534 }
2535
2536 #[allow(dead_code)]
2538 pub fn all(&self) -> &[EnvSnapshot] {
2539 &self.snapshots
2540 }
2541
2542 #[allow(dead_code)]
2544 pub fn latest(&self) -> Option<&EnvSnapshot> {
2545 self.snapshots.last()
2546 }
2547
2548 #[allow(dead_code)]
2550 pub fn len(&self) -> usize {
2551 self.snapshots.len()
2552 }
2553
2554 #[allow(dead_code)]
2556 pub fn is_empty(&self) -> bool {
2557 self.snapshots.is_empty()
2558 }
2559}
2560
2561#[cfg(test)]
2566mod lib_extended_tests {
2567 use super::*;
2568 use oxilean_kernel::Name;
2569
2570 #[test]
2571 fn test_elab_config_defaults() {
2572 let cfg = ElabConfig::default();
2573 assert!(!cfg.allow_sorry);
2574 assert!(cfg.kernel_check);
2575 assert!(cfg.proof_irrelevance);
2576 assert!(cfg.auto_implicit);
2577 }
2578
2579 #[test]
2580 fn test_elab_config_strict() {
2581 let cfg = ElabConfig::strict();
2582 assert!(!cfg.allow_sorry);
2583 assert!(cfg.strict_instances);
2584 assert!(cfg.kernel_check);
2585 }
2586
2587 #[test]
2588 fn test_elab_config_interactive() {
2589 let cfg = ElabConfig::interactive();
2590 assert!(cfg.allow_sorry);
2591 assert!(!cfg.strict_instances);
2592 }
2593
2594 #[test]
2595 fn test_elab_config_batch() {
2596 let cfg = ElabConfig::batch();
2597 assert!(!cfg.allow_sorry);
2598 assert!(cfg.strict_instances);
2599 assert!(!cfg.trace_elaboration);
2600 }
2601
2602 #[test]
2603 fn test_elab_metrics_solve_rate() {
2604 let mut m = ElabMetrics::new();
2605 m.metavars_created = 10;
2606 m.metavars_solved = 8;
2607 let rate = m.solve_rate();
2608 assert!((rate - 0.8).abs() < 1e-10);
2609 }
2610
2611 #[test]
2612 fn test_elab_metrics_solve_rate_zero() {
2613 let m = ElabMetrics::new();
2614 assert_eq!(m.solve_rate(), 1.0);
2615 }
2616
2617 #[test]
2618 fn test_elab_metrics_merge() {
2619 let mut a = ElabMetrics::new();
2620 a.declarations_elaborated = 5;
2621 let mut b = ElabMetrics::new();
2622 b.declarations_elaborated = 3;
2623 a.merge(&b);
2624 assert_eq!(a.declarations_elaborated, 8);
2625 }
2626
2627 #[test]
2628 fn test_decl_kind_keyword() {
2629 assert_eq!(DeclKind::Def.keyword(), "def");
2630 assert_eq!(DeclKind::Theorem.keyword(), "theorem");
2631 assert_eq!(DeclKind::Axiom.keyword(), "axiom");
2632 }
2633
2634 #[test]
2635 fn test_decl_kind_produces_term() {
2636 assert!(DeclKind::Def.produces_term());
2637 assert!(DeclKind::Theorem.produces_term());
2638 assert!(!DeclKind::Inductive.produces_term());
2639 assert!(!DeclKind::Namespace.produces_term());
2640 }
2641
2642 #[test]
2643 fn test_decl_kind_requires_proof() {
2644 assert!(DeclKind::Theorem.requires_proof());
2645 assert!(!DeclKind::Def.requires_proof());
2646 }
2647
2648 #[test]
2649 fn test_decl_kind_is_computable() {
2650 assert!(DeclKind::Def.is_computable());
2651 assert!(!DeclKind::Noncomputable.is_computable());
2652 assert!(!DeclKind::Axiom.is_computable());
2653 }
2654
2655 #[test]
2656 fn test_proof_history_undo_redo() {
2657 let mut h = ProofHistory::new();
2658 assert!(h.is_empty());
2659 h.push(ProofStateSnapshot::new(0, "start", 2, vec![]));
2660 h.push(ProofStateSnapshot::new(1, "step1", 1, vec![]));
2661 h.push(ProofStateSnapshot::new(2, "step2", 0, vec![]));
2662 assert_eq!(h.len(), 3);
2663
2664 let prev = h.undo();
2665 assert!(prev.is_some());
2666 assert_eq!(prev.expect("test operation should succeed").id, 1);
2667
2668 let next = h.redo();
2669 assert!(next.is_some());
2670 assert_eq!(next.expect("test operation should succeed").id, 2);
2671 }
2672
2673 #[test]
2674 fn test_proof_history_current() {
2675 let mut h = ProofHistory::new();
2676 h.push(ProofStateSnapshot::new(0, "start", 1, vec![]));
2677 assert!(h.current().is_some());
2678 assert_eq!(h.current().expect("test operation should succeed").id, 0);
2679 assert!(!h
2680 .current()
2681 .expect("test operation should succeed")
2682 .is_complete());
2683 }
2684
2685 #[test]
2686 fn test_coercion_registry_find() {
2687 let mut reg = CoercionRegistryExt::new();
2688 let c = CoercionExt::new(Name::str("Nat"), Name::str("Int"), Name::str("Int.ofNat"));
2689 reg.register(c);
2690 assert!(reg.find(&Name::str("Nat"), &Name::str("Int")).is_some());
2691 assert!(reg.find(&Name::str("Int"), &Name::str("Nat")).is_none());
2692 }
2693
2694 #[test]
2695 fn test_coercion_apply() {
2696 let c = CoercionExt::new(Name::str("Nat"), Name::str("Int"), Name::str("Int.ofNat"));
2697 let nat_expr = Expr::Const(Name::str("zero"), vec![]);
2698 let coerced = c.apply(nat_expr);
2699 assert!(matches!(coerced, Expr::App(_, _)));
2700 }
2701
2702 #[test]
2703 fn test_instance_registry() {
2704 let mut reg = InstanceRegistry::new();
2705 let inst = ClassInstance::new(Name::str("Add"), Name::str("instAddNat")).as_default();
2706 reg.register(inst);
2707 assert_eq!(reg.instances_of(&Name::str("Add")).len(), 1);
2708 assert!(reg.default_instance(&Name::str("Add")).is_some());
2709 }
2710
2711 #[test]
2712 fn test_attribute_registry() {
2713 let mut reg = AttributeRegistry::new();
2714 let attr = DeclAttribute::new("simp", Name::str("myLemma")).with_arg("all");
2715 reg.register(attr);
2716 assert_eq!(reg.attrs_of(&Name::str("myLemma")).len(), 1);
2717 assert_eq!(reg.decls_with("simp").len(), 1);
2718 }
2719
2720 #[test]
2721 fn test_namespace_manager() {
2722 let mut nm = NamespaceManager::new();
2723 assert_eq!(nm.depth(), 0);
2724 nm.open(Name::str("Nat"));
2725 assert_eq!(nm.depth(), 1);
2726 let q = nm.qualify("succ");
2727 assert!(q.to_string().contains("succ"));
2728 nm.close();
2729 assert_eq!(nm.depth(), 0);
2730 }
2731
2732 #[test]
2733 fn test_pretty_expr() {
2734 let nat = Expr::Const(Name::str("Nat"), vec![]);
2735 let s = pretty_expr(&nat);
2736 assert_eq!(s, "Nat");
2737
2738 let bvar = Expr::BVar(2);
2739 let s2 = pretty_expr(&bvar);
2740 assert!(s2.contains('2'));
2741 }
2742
2743 #[test]
2744 fn test_pretty_expr_list() {
2745 let exprs = vec![
2746 Expr::Const(Name::str("a"), vec![]),
2747 Expr::Const(Name::str("b"), vec![]),
2748 ];
2749 let s = pretty_expr_list(&exprs);
2750 assert!(s.contains("a"));
2751 assert!(s.contains("b"));
2752 assert!(s.contains(','));
2753 }
2754
2755 #[test]
2756 fn test_might_be_recursive_yes() {
2757 let name = Name::str("fib");
2758 let body = Expr::App(
2759 Box::new(Expr::Const(Name::str("fib"), vec![])),
2760 Box::new(Expr::BVar(0)),
2761 );
2762 assert!(might_be_recursive(&name, &body));
2763 }
2764
2765 #[test]
2766 fn test_might_be_recursive_no() {
2767 let name = Name::str("fib");
2768 let body = Expr::Const(Name::str("Nat.succ"), vec![]);
2769 assert!(!might_be_recursive(&name, &body));
2770 }
2771
2772 #[test]
2773 fn test_is_mathlib_tactic() {
2774 assert!(is_mathlib_tactic("omega"));
2775 assert!(is_mathlib_tactic("norm_num"));
2776 assert!(is_mathlib_tactic("aesop"));
2777 assert!(!is_mathlib_tactic("intro"));
2778 assert!(!is_mathlib_tactic("unknown"));
2779 }
2780
2781 #[test]
2782 fn test_tactic_names_ext_constants() {
2783 assert_eq!(tactic_names_ext::OMEGA, "omega");
2784 assert_eq!(tactic_names_ext::NORM_NUM, "norm_num");
2785 assert_eq!(tactic_names_ext::EXT, "ext");
2786 }
2787
2788 #[test]
2789 fn test_env_snapshot_manager() {
2790 let mut mgr = EnvSnapshotManager::new();
2791 assert!(mgr.is_empty());
2792 let id1 = mgr.take(10, "after module A");
2793 let _id2 = mgr.take(20, "after module B");
2794 assert_eq!(mgr.len(), 2);
2795 let snap = mgr.get(id1).expect("key should exist");
2796 assert_eq!(snap.decl_count, 10);
2797 let latest = mgr.latest().expect("test operation should succeed");
2798 assert_eq!(latest.decl_count, 20);
2799 }
2800
2801 #[test]
2802 fn test_universe_check_mode_equality() {
2803 assert_eq!(UniverseCheckMode::Full, UniverseCheckMode::Full);
2804 assert_ne!(UniverseCheckMode::Full, UniverseCheckMode::Skip);
2805 }
2806
2807 #[test]
2808 fn test_coercion_registry_remove_from() {
2809 let mut reg = CoercionRegistryExt::new();
2810 reg.register(CoercionExt::new(
2811 Name::str("Nat"),
2812 Name::str("Int"),
2813 Name::str("f"),
2814 ));
2815 reg.register(CoercionExt::new(
2816 Name::str("Nat"),
2817 Name::str("Real"),
2818 Name::str("g"),
2819 ));
2820 reg.register(CoercionExt::new(
2821 Name::str("Int"),
2822 Name::str("Real"),
2823 Name::str("h"),
2824 ));
2825 assert_eq!(reg.len(), 3);
2826 reg.remove_from(&Name::str("Nat"));
2827 assert_eq!(reg.len(), 1);
2828 }
2829
2830 #[test]
2831 fn test_instance_registry_remove_class() {
2832 let mut reg = InstanceRegistry::new();
2833 reg.register(ClassInstance::new(Name::str("Add"), Name::str("addNat")));
2834 reg.register(ClassInstance::new(Name::str("Add"), Name::str("addInt")));
2835 reg.register(ClassInstance::new(Name::str("Mul"), Name::str("mulNat")));
2836 assert_eq!(reg.len(), 3);
2837 reg.remove_class(&Name::str("Add"));
2838 assert_eq!(reg.len(), 1);
2839 }
2840
2841 #[test]
2842 fn test_decl_kind_display() {
2843 let s = format!("{}", DeclKind::Def);
2844 assert_eq!(s, "def");
2845 }
2846}