1use std::fmt;
16use std::io::{self, Write};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum LfscSort {
21 Kind,
23 Type,
25 Bool,
27 Int,
29 Real,
31 BitVec(u32),
33 Arrow(Box<LfscSort>, Box<LfscSort>),
35 Named(String),
37 App(String, Vec<LfscSort>),
39}
40
41impl fmt::Display for LfscSort {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 match self {
44 Self::Kind => write!(f, "kind"),
45 Self::Type => write!(f, "type"),
46 Self::Bool => write!(f, "bool"),
47 Self::Int => write!(f, "mpz"),
48 Self::Real => write!(f, "mpq"),
49 Self::BitVec(w) => write!(f, "(bitvec {})", w),
50 Self::Arrow(a, b) => write!(f, "(! _ {} {})", a, b),
51 Self::Named(n) => write!(f, "{}", n),
52 Self::App(n, args) => {
53 write!(f, "({}", n)?;
54 for arg in args {
55 write!(f, " {}", arg)?;
56 }
57 write!(f, ")")
58 }
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
65pub enum LfscTerm {
66 Var(String),
68 IntLit(i64),
70 RatLit(i64, i64),
72 True,
74 False,
76 App(String, Vec<LfscTerm>),
78 Lambda(String, Box<LfscSort>, Box<LfscTerm>),
80 Pi(String, Box<LfscSort>, Box<LfscTerm>),
82 SideCondition(String, Vec<LfscTerm>),
84 Hold(Box<LfscTerm>),
86 Annotate(Box<LfscTerm>, Box<LfscSort>),
88}
89
90impl fmt::Display for LfscTerm {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Self::Var(v) => write!(f, "{}", v),
94 Self::IntLit(n) => write!(f, "{}", n),
95 Self::RatLit(n, d) => write!(f, "{}/{}", n, d),
96 Self::True => write!(f, "tt"),
97 Self::False => write!(f, "ff"),
98 Self::App(func, args) => {
99 write!(f, "({}", func)?;
100 for arg in args {
101 write!(f, " {}", arg)?;
102 }
103 write!(f, ")")
104 }
105 Self::Lambda(var, sort, body) => {
106 write!(f, "(\\ {} {} {})", var, sort, body)
107 }
108 Self::Pi(var, sort, body) => {
109 write!(f, "(! {} {} {})", var, sort, body)
110 }
111 Self::SideCondition(name, args) => {
112 write!(f, "(# {} (", name)?;
113 for (i, arg) in args.iter().enumerate() {
114 if i > 0 {
115 write!(f, " ")?;
116 }
117 write!(f, "{}", arg)?;
118 }
119 write!(f, "))")
120 }
121 Self::Hold(t) => write!(f, "(holds {})", t),
122 Self::Annotate(t, s) => write!(f, "(: {} {})", t, s),
123 }
124 }
125}
126
127#[derive(Debug, Clone)]
129pub enum LfscDecl {
130 DeclareSort { name: String, arity: u32 },
132 DeclareConst { name: String, sort: LfscSort },
134 Define {
136 name: String,
137 sort: LfscSort,
138 value: LfscTerm,
139 },
140 DeclareRule {
142 name: String,
143 params: Vec<(String, LfscSort)>,
144 conclusion: LfscTerm,
145 },
146 SideCondition {
148 name: String,
149 params: Vec<(String, LfscSort)>,
150 return_sort: LfscSort,
151 body: String, },
153 Check(LfscTerm),
155}
156
157impl fmt::Display for LfscDecl {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 match self {
160 Self::DeclareSort { name, arity } => {
161 write!(f, "(declare {} ", name)?;
162 for _ in 0..*arity {
163 write!(f, "(! _ type ")?;
164 }
165 write!(f, "type")?;
166 for _ in 0..*arity {
167 write!(f, ")")?;
168 }
169 write!(f, ")")
170 }
171 Self::DeclareConst { name, sort } => {
172 write!(f, "(declare {} {})", name, sort)
173 }
174 Self::Define { name, sort, value } => {
175 write!(f, "(define {} (: {} {}))", name, value, sort)
176 }
177 Self::DeclareRule {
178 name,
179 params,
180 conclusion,
181 } => {
182 write!(f, "(declare {} ", name)?;
183 for (pname, psort) in params {
184 write!(f, "(! {} {} ", pname, psort)?;
185 }
186 write!(f, "{}", conclusion)?;
187 for _ in params {
188 write!(f, ")")?;
189 }
190 write!(f, ")")
191 }
192 Self::SideCondition {
193 name,
194 params,
195 return_sort,
196 body,
197 } => {
198 write!(f, "(program {} (", name)?;
199 for (i, (pname, psort)) in params.iter().enumerate() {
200 if i > 0 {
201 write!(f, " ")?;
202 }
203 write!(f, "({} {})", pname, psort)?;
204 }
205 write!(f, ") {} {})", return_sort, body)
206 }
207 Self::Check(term) => {
208 write!(f, "(check {})", term)
209 }
210 }
211 }
212}
213
214#[derive(Debug, Default)]
216pub struct LfscProof {
217 decls: Vec<LfscDecl>,
219}
220
221impl LfscProof {
222 #[must_use]
224 pub fn new() -> Self {
225 Self { decls: Vec::new() }
226 }
227
228 pub fn declare_sort(&mut self, name: impl Into<String>, arity: u32) {
230 self.decls.push(LfscDecl::DeclareSort {
231 name: name.into(),
232 arity,
233 });
234 }
235
236 pub fn declare_const(&mut self, name: impl Into<String>, sort: LfscSort) {
238 self.decls.push(LfscDecl::DeclareConst {
239 name: name.into(),
240 sort,
241 });
242 }
243
244 pub fn define(&mut self, name: impl Into<String>, sort: LfscSort, value: LfscTerm) {
246 self.decls.push(LfscDecl::Define {
247 name: name.into(),
248 sort,
249 value,
250 });
251 }
252
253 pub fn declare_rule(
255 &mut self,
256 name: impl Into<String>,
257 params: Vec<(String, LfscSort)>,
258 conclusion: LfscTerm,
259 ) {
260 self.decls.push(LfscDecl::DeclareRule {
261 name: name.into(),
262 params,
263 conclusion,
264 });
265 }
266
267 pub fn side_condition(
269 &mut self,
270 name: impl Into<String>,
271 params: Vec<(String, LfscSort)>,
272 return_sort: LfscSort,
273 body: impl Into<String>,
274 ) {
275 self.decls.push(LfscDecl::SideCondition {
276 name: name.into(),
277 params,
278 return_sort,
279 body: body.into(),
280 });
281 }
282
283 pub fn check(&mut self, term: LfscTerm) {
285 self.decls.push(LfscDecl::Check(term));
286 }
287
288 #[must_use]
290 pub fn len(&self) -> usize {
291 self.decls.len()
292 }
293
294 #[must_use]
296 pub fn is_empty(&self) -> bool {
297 self.decls.is_empty()
298 }
299
300 #[must_use]
302 pub fn decls(&self) -> &[LfscDecl] {
303 &self.decls
304 }
305
306 pub fn clear(&mut self) {
308 self.decls.clear();
309 }
310
311 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
313 writeln!(writer, "; LFSC proof generated by OxiZ")?;
314 writeln!(writer)?;
315
316 for decl in &self.decls {
317 writeln!(writer, "{}", decl)?;
318 }
319
320 Ok(())
321 }
322
323 #[must_use]
325 #[allow(clippy::inherent_to_string)]
326 pub fn to_string(&self) -> String {
327 let mut buf = Vec::new();
328 self.write(&mut buf)
329 .expect("writing to Vec should not fail");
330 String::from_utf8(buf).expect("LFSC output is UTF-8")
331 }
332}
333
334pub mod signatures {
336 use super::*;
337
338 pub fn boolean_theory() -> Vec<LfscDecl> {
340 vec![
341 LfscDecl::DeclareSort {
342 name: "formula".to_string(),
343 arity: 0,
344 },
345 LfscDecl::DeclareConst {
346 name: "true".to_string(),
347 sort: LfscSort::Named("formula".to_string()),
348 },
349 LfscDecl::DeclareConst {
350 name: "false".to_string(),
351 sort: LfscSort::Named("formula".to_string()),
352 },
353 LfscDecl::DeclareConst {
354 name: "not".to_string(),
355 sort: LfscSort::Arrow(
356 Box::new(LfscSort::Named("formula".to_string())),
357 Box::new(LfscSort::Named("formula".to_string())),
358 ),
359 },
360 LfscDecl::DeclareConst {
361 name: "and".to_string(),
362 sort: LfscSort::Arrow(
363 Box::new(LfscSort::Named("formula".to_string())),
364 Box::new(LfscSort::Arrow(
365 Box::new(LfscSort::Named("formula".to_string())),
366 Box::new(LfscSort::Named("formula".to_string())),
367 )),
368 ),
369 },
370 LfscDecl::DeclareConst {
371 name: "or".to_string(),
372 sort: LfscSort::Arrow(
373 Box::new(LfscSort::Named("formula".to_string())),
374 Box::new(LfscSort::Arrow(
375 Box::new(LfscSort::Named("formula".to_string())),
376 Box::new(LfscSort::Named("formula".to_string())),
377 )),
378 ),
379 },
380 LfscDecl::DeclareConst {
381 name: "impl".to_string(),
382 sort: LfscSort::Arrow(
383 Box::new(LfscSort::Named("formula".to_string())),
384 Box::new(LfscSort::Arrow(
385 Box::new(LfscSort::Named("formula".to_string())),
386 Box::new(LfscSort::Named("formula".to_string())),
387 )),
388 ),
389 },
390 ]
391 }
392
393 pub fn holds_theory() -> Vec<LfscDecl> {
395 vec![LfscDecl::DeclareConst {
396 name: "holds".to_string(),
397 sort: LfscSort::Arrow(
398 Box::new(LfscSort::Named("formula".to_string())),
399 Box::new(LfscSort::Type),
400 ),
401 }]
402 }
403}
404
405pub trait LfscProofProducer {
407 fn enable_lfsc_proof(&mut self);
409
410 fn disable_lfsc_proof(&mut self);
412
413 fn get_lfsc_proof(&self) -> Option<&LfscProof>;
415
416 fn take_lfsc_proof(&mut self) -> Option<LfscProof>;
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn test_lfsc_sort_display() {
426 assert_eq!(format!("{}", LfscSort::Bool), "bool");
427 assert_eq!(format!("{}", LfscSort::Int), "mpz");
428 assert_eq!(format!("{}", LfscSort::BitVec(32)), "(bitvec 32)");
429
430 let arrow = LfscSort::Arrow(Box::new(LfscSort::Int), Box::new(LfscSort::Bool));
431 assert_eq!(format!("{}", arrow), "(! _ mpz bool)");
432 }
433
434 #[test]
435 fn test_lfsc_term_display() {
436 assert_eq!(format!("{}", LfscTerm::Var("x".to_string())), "x");
437 assert_eq!(format!("{}", LfscTerm::IntLit(42)), "42");
438 assert_eq!(format!("{}", LfscTerm::True), "tt");
439 assert_eq!(format!("{}", LfscTerm::False), "ff");
440
441 let app = LfscTerm::App(
442 "add".to_string(),
443 vec![LfscTerm::IntLit(1), LfscTerm::IntLit(2)],
444 );
445 assert_eq!(format!("{}", app), "(add 1 2)");
446 }
447
448 #[test]
449 fn test_lfsc_declare_sort() {
450 let mut proof = LfscProof::new();
451 proof.declare_sort("mySort", 0);
452 proof.declare_sort("myParam", 1);
453
454 let output = proof.to_string();
455 assert!(output.contains("(declare mySort type)"));
456 assert!(output.contains("(declare myParam (! _ type type))"));
457 }
458
459 #[test]
460 fn test_lfsc_declare_const() {
461 let mut proof = LfscProof::new();
462 proof.declare_const("x", LfscSort::Int);
463
464 let output = proof.to_string();
465 assert!(output.contains("(declare x mpz)"));
466 }
467
468 #[test]
469 fn test_lfsc_check() {
470 let mut proof = LfscProof::new();
471 proof.check(LfscTerm::Hold(Box::new(LfscTerm::True)));
472
473 let output = proof.to_string();
474 assert!(output.contains("(check (holds tt))"));
475 }
476
477 #[test]
478 fn test_lfsc_boolean_theory() {
479 let decls = signatures::boolean_theory();
480 assert!(!decls.is_empty());
481
482 let names: Vec<_> = decls
484 .iter()
485 .filter_map(|d| match d {
486 LfscDecl::DeclareSort { name, .. } => Some(name.as_str()),
487 LfscDecl::DeclareConst { name, .. } => Some(name.as_str()),
488 _ => None,
489 })
490 .collect();
491
492 assert!(names.contains(&"formula"));
493 assert!(names.contains(&"true"));
494 assert!(names.contains(&"false"));
495 assert!(names.contains(&"not"));
496 assert!(names.contains(&"and"));
497 assert!(names.contains(&"or"));
498 }
499
500 #[test]
501 fn test_lfsc_proof_clear() {
502 let mut proof = LfscProof::new();
503 proof.declare_sort("test", 0);
504 assert!(!proof.is_empty());
505
506 proof.clear();
507 assert!(proof.is_empty());
508 }
509
510 #[test]
511 fn test_lfsc_lambda() {
512 let lambda = LfscTerm::Lambda(
513 "x".to_string(),
514 Box::new(LfscSort::Int),
515 Box::new(LfscTerm::Var("x".to_string())),
516 );
517
518 assert_eq!(format!("{}", lambda), "(\\ x mpz x)");
519 }
520}