1use crate::{
12 evaluate_with_env, format_diagnostic, is_covered, is_terminating, is_total,
13 CoverageDiagnostic, Diagnostic, Env, TerminationDiagnostic, TotalityDiagnostic,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct MetaDiagnostic {
22 pub code: String,
23 pub message: String,
24}
25
26impl From<TotalityDiagnostic> for MetaDiagnostic {
27 fn from(d: TotalityDiagnostic) -> Self {
28 MetaDiagnostic {
29 code: d.code,
30 message: d.message,
31 }
32 }
33}
34
35impl From<CoverageDiagnostic> for MetaDiagnostic {
36 fn from(d: CoverageDiagnostic) -> Self {
37 MetaDiagnostic {
38 code: d.code,
39 message: d.message,
40 }
41 }
42}
43
44impl From<TerminationDiagnostic> for MetaDiagnostic {
45 fn from(d: TerminationDiagnostic) -> Self {
46 MetaDiagnostic {
47 code: d.code,
48 message: d.message,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum CheckKind {
57 Totality,
58 Coverage,
59 Termination,
60}
61
62impl CheckKind {
63 pub fn as_str(&self) -> &'static str {
64 match self {
65 CheckKind::Totality => "totality",
66 CheckKind::Coverage => "coverage",
67 CheckKind::Termination => "termination",
68 }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct MetatheoremCheck {
76 pub kind: CheckKind,
77 pub ok: bool,
78 pub diagnostics: Vec<MetaDiagnostic>,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct MetatheoremResult {
85 pub name: String,
86 pub ok: bool,
87 pub checks: Vec<MetatheoremCheck>,
88}
89
90#[derive(Debug, Clone, PartialEq)]
94pub struct MetatheoremReport {
95 pub ok: bool,
96 pub evaluation_diagnostics: Vec<Diagnostic>,
97 pub relations: Vec<MetatheoremResult>,
98 pub definitions: Vec<MetatheoremResult>,
99}
100
101fn check_relation(env: &Env, name: &str) -> MetatheoremResult {
102 let totality = is_total(env, name);
103 let coverage = is_covered(env, name);
104 let totality_check = MetatheoremCheck {
105 kind: CheckKind::Totality,
106 ok: totality.ok,
107 diagnostics: totality.diagnostics.into_iter().map(Into::into).collect(),
108 };
109 let coverage_check = MetatheoremCheck {
110 kind: CheckKind::Coverage,
111 ok: coverage.ok,
112 diagnostics: coverage.diagnostics.into_iter().map(Into::into).collect(),
113 };
114 let ok = totality_check.ok && coverage_check.ok;
115 MetatheoremResult {
116 name: name.to_string(),
117 ok,
118 checks: vec![totality_check, coverage_check],
119 }
120}
121
122fn check_definition(env: &Env, name: &str) -> MetatheoremResult {
123 let term = is_terminating(env, name);
124 let check = MetatheoremCheck {
125 kind: CheckKind::Termination,
126 ok: term.ok,
127 diagnostics: term.diagnostics.into_iter().map(Into::into).collect(),
128 };
129 let ok = check.ok;
130 MetatheoremResult {
131 name: name.to_string(),
132 ok,
133 checks: vec![check],
134 }
135}
136
137pub fn check_metatheorems(text: &str, file: Option<&str>) -> MetatheoremReport {
145 let mut env = Env::new(None);
146 let evaluation = evaluate_with_env(text, file, &mut env);
147
148 let mut relation_names: Vec<String> = env.modes.keys().cloned().collect();
149 relation_names.sort();
150 let mut relations: Vec<MetatheoremResult> = Vec::new();
151 for name in &relation_names {
152 let clauses = env.relations.get(name);
153 if clauses.map_or(true, |c| c.is_empty()) {
157 continue;
158 }
159 relations.push(check_relation(&env, name));
160 }
161
162 let mut definition_names: Vec<String> = env.definitions.keys().cloned().collect();
163 definition_names.sort();
164 let mut definitions: Vec<MetatheoremResult> = Vec::new();
165 for name in &definition_names {
166 definitions.push(check_definition(&env, name));
167 }
168
169 let ok = evaluation.diagnostics.is_empty()
170 && relations.iter().all(|r| r.ok)
171 && definitions.iter().all(|d| d.ok);
172 MetatheoremReport {
173 ok,
174 evaluation_diagnostics: evaluation.diagnostics,
175 relations,
176 definitions,
177 }
178}
179
180fn format_check(check: &MetatheoremCheck) -> Vec<String> {
181 let status = if check.ok { "pass" } else { "fail" };
182 let mut lines = vec![format!(" - {}: {}", check.kind.as_str(), status)];
183 for diag in &check.diagnostics {
184 lines.push(format!(" {} {}", diag.code, diag.message).trim_end().to_string());
185 }
186 lines
187}
188
189pub fn format_report(report: &MetatheoremReport) -> String {
193 let mut lines: Vec<String> = Vec::new();
194 for diag in &report.evaluation_diagnostics {
195 lines.push(format_diagnostic(diag, None));
196 }
197 if report.relations.is_empty() && report.definitions.is_empty() {
198 lines.push(
199 "No metatheorem candidates found (no `(mode ...)` or `(define ...)` declarations)."
200 .to_string(),
201 );
202 return lines.join("\n");
203 }
204 if !report.relations.is_empty() {
205 lines.push("Relations:".to_string());
206 for rel in &report.relations {
207 let status = if rel.ok { "OK" } else { "FAIL" };
208 lines.push(format!(" {}: {}", status, rel.name));
209 for check in &rel.checks {
210 lines.extend(format_check(check));
211 }
212 }
213 }
214 if !report.definitions.is_empty() {
215 lines.push("Definitions:".to_string());
216 for def in &report.definitions {
217 let status = if def.ok { "OK" } else { "FAIL" };
218 lines.push(format!(" {}: {}", status, def.name));
219 for check in &def.checks {
220 lines.extend(format_check(check));
221 }
222 }
223 }
224 lines.push(
225 if report.ok {
226 "All metatheorems hold."
227 } else {
228 "One or more metatheorems failed."
229 }
230 .to_string(),
231 );
232 lines.join("\n")
233}