#![cfg_attr(feature = "const-bit-vec", allow(incomplete_features))]
#![cfg_attr(feature = "const-bit-vec", feature(generic_const_exprs))]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
use std::collections::HashMap;
pub use backend::Backend;
use itertools::Itertools;
pub use logics::Logic;
use smtlib_lowlevel::ast;
pub use smtlib_lowlevel::{self as lowlevel, backend, Logger};
pub use terms::Sorted;
use terms::{Const, STerm};
#[cfg(feature = "tokio")]
mod tokio_solver;
#[rustfmt::skip]
#[allow(clippy::all)]
mod logics;
pub mod funs;
mod solver;
pub mod sorts;
pub mod terms;
#[cfg(test)]
mod tests;
pub mod theories;
pub use smtlib_lowlevel::Storage;
pub use solver::Solver;
pub use theories::{core::*, fixed_size_bit_vectors::*, ints::*, reals::*};
#[cfg(feature = "tokio")]
pub use tokio_solver::TokioSolver;
pub mod prelude {
pub use crate::terms::{Sorted, StaticSorted};
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SatResult {
Unsat,
Sat,
Unknown,
}
impl std::fmt::Display for SatResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SatResult::Unsat => write!(f, "unsat"),
SatResult::Sat => write!(f, "sat"),
SatResult::Unknown => write!(f, "unknown"),
}
}
}
#[derive(Debug)]
pub enum SatResultWithModel<'st> {
Unsat,
Sat(Model<'st>),
Unknown,
}
impl<'st> SatResultWithModel<'st> {
pub fn expect_sat(self) -> Result<Model<'st>, Error> {
match self {
SatResultWithModel::Sat(m) => Ok(m),
SatResultWithModel::Unsat => Err(Error::UnexpectedSatResult {
expected: SatResult::Sat,
actual: SatResult::Unsat,
}),
SatResultWithModel::Unknown => Err(Error::UnexpectedSatResult {
expected: SatResult::Sat,
actual: SatResult::Unknown,
}),
}
}
}
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[non_exhaustive]
pub enum Error {
#[error(transparent)]
#[diagnostic(transparent)]
Lowlevel(
#[from]
#[diagnostic_source]
smtlib_lowlevel::Error,
),
#[error("SMT error {0} after running {1}")]
Smt(String, String),
#[error("Expected the model to be {expected} but was {actual}")]
UnexpectedSatResult {
expected: SatResult,
actual: SatResult,
},
#[error("tried to cast a dynamic of sort {expected} to {actual}")]
DynamicCastSortMismatch {
expected: String,
actual: String,
},
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Model<'st> {
values: HashMap<String, STerm<'st>>,
}
impl std::fmt::Debug for Model<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.values.fmt(f)
}
}
impl std::fmt::Display for Model<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{{ {} }}",
self.values
.iter()
.sorted_by_key(|(n, _)| n.as_str())
.map(|(n, t)| format!("{n}: {}", t.term()))
.format(", ")
)
}
}
impl<'st> Model<'st> {
fn new(st: &'st Storage, model: ast::GetModelResponse<'st>) -> Self {
Self {
values: model
.0
.iter()
.map(|res| match res {
ast::ModelResponse::DefineFun(f) => (f.0 .0.trim_matches('|').into(), f.3),
ast::ModelResponse::DefineFunRec(_) => todo!(),
ast::ModelResponse::DefineFunsRec(_, _) => todo!(),
})
.map(|(name, term)| (name, STerm::new_from_ref(st, term)))
.collect(),
}
}
pub fn eval<T: Sorted<'st>>(&self, x: Const<'st, T>) -> Option<T::Inner>
where
T::Inner: From<STerm<'st>>,
{
Some((*self.values.get(x.name().trim_matches('|'))?).into())
}
}