use crate::formally;
use formally::{
smt::{backends::Backend, logics::Logic, *},
support::*,
};
use std::{
cell::RefCell,
collections::HashSet,
fmt::{Debug, Formatter},
rc::Rc,
sync::Arc,
};
#[derive(Debug, Clone, Default)]
pub struct Config {
pub logic: Option<Identifier<'static>>,
pub produce_models: bool,
}
impl Config {
pub fn new() -> Config {
Config::default()
}
pub fn logic<'a>(self, logic: impl Into<Identifier<'a>>) -> Config {
Config {
logic: Some(logic.into().into_owned()),
..self
}
}
pub fn produce_models(self, produce_models: bool) -> Config {
Config {
produce_models,
..self
}
}
}
#[derive(Default, Clone)]
pub struct Env {
pub functions: Scope<Function>,
pub sorts: Scope<Function>,
}
impl Env {
pub fn new() -> Self {
Env::default()
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub enum Role {
Function,
Sort,
}
impl Env {
pub fn lookup<'i>(&self, name: Identifier<'i>, role: Role) -> LookupSet<'_, 'i, Function> {
match role {
Role::Function => self.functions.lookup(name),
Role::Sort => self.sorts.lookup(name),
}
}
pub fn with_parent(self, parent: Env) -> Env {
Env {
functions: self.functions.with_parent(parent.functions),
sorts: self.sorts.with_parent(parent.sorts),
}
}
}
#[derive(Clone)]
pub struct TermManager {
backend_manager: Rc<dyn backends::Manager>,
pool: Rc<dyn TermPool>,
decls: RefCell<HashSet<Arc<Declaration<Sort, Sort>>>>,
defs: RefCell<HashSet<Arc<Definition<Sort, Sort, Term>>>>,
}
impl TermManager {
fn decl(&self, decl: Declaration<Sort, Sort>) -> Declared {
if let Some(decl) = self.decls.borrow().get(&decl) {
return Declared(Nominal(decl.clone()));
}
let arc = Arc::new(decl);
self.decls.borrow_mut().insert(arc.clone());
Declared(Nominal(arc))
}
fn def(&self, def: Definition<Sort, Sort, Term>) -> Defined {
if let Some(def) = self.defs.borrow().get(&def) {
return Defined(Nominal(def.clone()));
}
let arc = Arc::new(def);
self.defs.borrow_mut().insert(arc.clone());
Defined(Nominal(arc))
}
}
impl Debug for TermManager {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "TermManager {{ }}")
}
}
impl TermManager {
pub fn new() -> Result<TermManager> {
TermManager::with_backend(backends::Register::default_backend()?)
}
pub fn with_backend_name<'a>(backend: impl Into<Identifier<'a>>) -> Result<TermManager> {
let backend = backends::Register::backend(backend)?;
TermManager::with_backend(backend)
}
pub fn with_backend(backend: impl Backend) -> Result<TermManager> {
Ok(TermManager {
backend_manager: Rc::from(backend.manager()?),
pool: Rc::new(HashPool::new()),
decls: RefCell::default(),
defs: RefCell::default(),
})
}
pub fn with_pool(backend: impl Backend, pool: Rc<dyn TermPool>) -> Result<TermManager> {
Ok(TermManager {
backend_manager: Rc::from(backend.manager()?),
pool,
decls: RefCell::default(),
defs: RefCell::default(),
})
}
pub fn pool(&self) -> &dyn TermPool {
&*self.pool
}
}
pub struct Solver {
backend_solver: Box<dyn backends::Solver>,
env: Env,
manager: Rc<TermManager>,
}
impl Solver {
pub fn with_manager(config: &Config, manager: impl Into<Rc<TermManager>>) -> Result<Solver> {
let manager = manager.into();
let backend_solver = manager
.backend_manager
.backend()
.solver(config, manager.backend_manager.clone())?;
Ok(Solver {
env: Env::new().with_parent(backend_solver.logic().theory().env()),
backend_solver,
manager,
})
}
pub fn with_backend(config: &Config, backend: impl Backend) -> Result<Solver> {
Solver::with_manager(config, TermManager::with_backend(backend)?)
}
pub fn with_backend_name<'a>(
config: &Config,
backend: impl Into<Identifier<'a>>,
) -> Result<Solver> {
Solver::with_manager(config, TermManager::with_backend_name(backend)?)
}
pub fn new(config: &Config) -> Result<Solver> {
Solver::with_manager(config, TermManager::new()?)
}
pub fn logic(&self) -> &dyn Logic {
self.backend_solver.logic()
}
pub fn config(&self, config: &Config) -> Result<()> {
Ok(self.backend_solver.config(config)?)
}
pub fn env(&self) -> &Env {
&self.env
}
pub fn pool(&self) -> &dyn TermPool {
self.manager.pool()
}
pub fn lookup<T: ToTerm>(&self, term: T, role: Role) -> Result<Term> {
let interned = term.into_term_in(self.manager.pool());
let resolved = interned.resolve(self.env(), self.manager.pool(), role)?;
resolved.type_check()?;
Ok(resolved)
}
pub fn lookup_sort<S: ToSort>(&self, sort: &S) -> Result<Sort> {
let resolved = sort.resolve(self.env(), self.manager.pool(), Role::Sort)?;
resolved.type_check()?;
Ok(resolved.try_into()?)
}
pub fn lookup_var<S: ToSort>(&self, variable: &Variable<S>) -> Result<Variable> {
Ok(
Variable::new(variable.name().clone(), self.lookup_sort(variable.sort())?)
.over(variable.span()),
)
}
pub fn lookup_binding<T: ToTerm>(&self, binding: Binding<Infer, T>) -> Result<Binding> {
let def = self.lookup(&binding.def, Role::Function)?;
let sort = Sort::of(&def)?;
let variable =
Variable::new(binding.variable.name().clone(), sort).over(binding.variable.span());
Ok(Binding {
variable,
def,
span: binding.span,
})
}
pub fn declare<R: ToSort, B: ToSort>(&mut self, decl: Declaration<R, B>) -> Result<Declared> {
let mut domain = Vec::with_capacity(decl.domain.len());
for d in &decl.domain {
domain.push(self.lookup_sort(d)?);
}
let decl = Declaration {
name: decl.name,
domain,
range: self.lookup_sort(&decl.range)?,
span: decl.span,
};
let decl = self.manager.decl(decl);
self.backend_solver
.logic()
.check_function(&decl.clone().into())?;
if decl.range == Sort::sort() {
self.env.sorts.add(&decl.name, decl.clone().into());
} else {
self.env.functions.add(&decl.name, decl.clone().into());
}
self.backend_solver.declare(decl.clone())?;
Ok(decl)
}
pub fn define<V: ToSort, R: ToSort, B: ToTerm>(
&mut self,
def: Definition<V, R, B>,
) -> Result<Defined> {
let mut nested = Env::new().with_parent(self.env().clone());
let mut domain = Vec::with_capacity(def.domain.len());
for var in &def.domain {
let var = self.lookup_var(var)?;
domain.push(var.clone());
nested
.functions
.add(var.name(), Function::Variable(var.clone()));
}
let range = self.lookup_sort(&def.range)?;
let body = def.body.into_term_in(self.manager.pool()).resolve(
&nested,
self.manager.pool(),
Role::Function,
)?;
let bodysort = body.type_check()?;
if range == Sort::sort() {
if bodysort != Sort::sort() {
error!(
body.span(),
"expected sort in definition of `{}`, found `{}`", def.name, bodysort
);
return Err(DiagnosticEmitted);
}
} else {
if bodysort != range {
error!(
body.span(),
"expected term of sort `{}` in definition of `{}`, found `{}`",
range,
def.name,
bodysort
);
return Err(DiagnosticEmitted);
}
}
let def = Definition {
name: def.name,
domain,
range,
body,
span: None,
};
let def = self.manager.def(def);
self.backend_solver
.logic()
.check_function(&def.clone().into())?;
if def.range == Sort::sort() {
self.env.sorts.add(&def.name, def.clone().into());
} else {
self.env.functions.add(&def.name, def.clone().into());
}
self.backend_solver.define(def.clone())?;
Ok(def)
}
pub fn require<T: ToTerm>(&mut self, term: T) -> Result<()> {
let term = self.lookup(term, Role::Function)?;
self.backend_solver.logic().check_term(&term)?;
let sort = Sort::of(&term)?;
if sort != theories::Core::Bool() {
error!(term.span(), "can only assert Boolean terms");
note!(term.span(), "asserted term is of sort `{}`", sort);
return Err(DiagnosticEmitted);
}
self.backend_solver.require(&term)?;
Ok(())
}
pub fn check(&mut self) -> Result<Answer> {
match self.backend_solver.check()? {
Some(true) => Ok(Answer::Yes),
Some(false) => Ok(Answer::No),
None => Ok(Answer::Unknown),
}
}
pub fn model(&self) -> Result<Option<Model<'_>>> {
match self.backend_solver.model()? {
Some(provider) => Ok(Some(Model {
solver: self,
provider,
})),
None => Ok(None),
}
}
}
impl Stack for Solver {
fn push(&mut self) -> Result<()> {
Ok(self.backend_solver.push()?)
}
fn pop_n(&mut self, n: usize) -> Result<()> {
Ok(self.backend_solver.pop_n(n)?)
}
}
pub trait ModelProvider {
fn value(&self, term: &Term, pool: &dyn TermPool) -> Option<Term>;
}
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub enum Answer {
Yes,
No,
#[default]
Unknown,
}
impl Debug for Answer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Answer::Yes => write!(f, "sat"),
Answer::No => write!(f, "unsat"),
Answer::Unknown => write!(f, "unknown"),
}
}
}
pub struct Model<'s> {
solver: &'s Solver,
provider: Box<dyn 's + ModelProvider>,
}
impl Model<'_> {
pub fn value(&self, term: impl ToTerm) -> Result<Option<Term>> {
Ok(self.provider.value(
&self.solver.lookup(term, Role::Function)?,
self.solver.pool(),
))
}
}