use crate::{
known_atoms::KnownAtoms,
sexpr::{Arena, DisplayExpr, SExpr, SExprData},
solver::Solver,
};
use std::{ffi, io};
macro_rules! variadic {
($name:ident, $op:ident) => {
pub fn $name<I: IntoIterator<Item = SExpr>>(&self, items: I) -> SExpr {
let mut iter = items.into_iter().peekable();
let first = iter.next().expect("At least one argument is expected");
if iter.peek().is_some() {
let args = std::iter::once(self.atoms.$op)
.chain(std::iter::once(first))
.chain(iter)
.collect();
self.list(args)
} else {
first
}
}
};
}
macro_rules! unary {
($name:ident, $op:ident) => {
pub fn $name(&self, val: SExpr) -> SExpr {
self.list(vec![self.atoms.$op, val])
}
};
}
macro_rules! binop {
($name:ident, $op:ident) => {
pub fn $name(&self, lhs: SExpr, rhs: SExpr) -> SExpr {
self.list(vec![self.atoms.$op, lhs, rhs])
}
};
}
macro_rules! right_assoc {
($name:ident, $many:ident, $op:ident) => {
binop!($name, $op);
variadic!($many, $op);
};
}
macro_rules! left_assoc {
($name:ident, $many:ident, $op:ident) => {
binop!($name, $op);
variadic!($many, $op);
};
}
macro_rules! chainable {
($name:ident, $many:ident, $op:ident) => {
binop!($name, $op);
variadic!($many, $op);
};
}
macro_rules! pairwise {
($name:ident, $many:ident, $op:ident) => {
binop!($name, $op);
variadic!($many, $op);
};
}
#[derive(Default)]
pub struct ContextBuilder {
solver_program_and_args: Option<(ffi::OsString, Vec<ffi::OsString>)>,
replay_file: Option<Box<dyn io::Write>>,
}
impl ContextBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn solver<P, A>(&mut self, program: P, args: A) -> &mut Self
where
P: Into<ffi::OsString>,
A: IntoIterator,
A::Item: Into<ffi::OsString>,
{
self.solver_program_and_args =
Some((program.into(), args.into_iter().map(|a| a.into()).collect()));
self
}
pub fn without_solver(&mut self) -> &mut Self {
self.solver_program_and_args = None;
self
}
pub fn replay_file<W>(&mut self, replay_file: Option<W>) -> &mut Self
where
W: 'static + io::Write,
{
self.replay_file = replay_file.map(|w| Box::new(w) as _);
self
}
pub fn build(&mut self) -> io::Result<Context> {
let arena = Arena::new();
let atoms = KnownAtoms::new(&arena);
let solver = if let Some((program, args)) = self.solver_program_and_args.take() {
Some(Solver::new(
program,
args,
self.replay_file
.take()
.unwrap_or_else(|| Box::new(io::sink())),
)?)
} else {
None
};
let mut ctx = Context {
solver,
arena,
atoms,
};
if ctx.solver.is_some() {
ctx.set_option(":print-success", ctx.true_())?;
ctx.set_option(":produce-models", ctx.true_())?;
}
Ok(ctx)
}
}
pub struct Context {
solver: Option<Solver>,
arena: Arena,
atoms: KnownAtoms,
}
impl Context {
pub fn set_option<K>(&mut self, name: K, value: SExpr) -> io::Result<()>
where
K: Into<String> + AsRef<str>,
{
let solver = self
.solver
.as_mut()
.expect("set_option requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena
.list(vec![self.atoms.set_option, self.arena.atom(name), value]),
)
}
pub fn check_assuming(
&mut self,
props: impl IntoIterator<Item = SExpr>,
) -> io::Result<Response> {
let solver = self
.solver
.as_mut()
.expect("check requires a running solver");
let args = self.arena.list(props.into_iter().collect());
solver.send(
&self.arena,
self.arena.list(vec![self.atoms.check_sat_assuming, args]),
)?;
let resp = solver.recv(&self.arena)?;
if resp == self.atoms.sat {
Ok(Response::Sat)
} else if resp == self.atoms.unsat {
Ok(Response::Unsat)
} else if resp == self.atoms.unknown {
Ok(Response::Unknown)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Unexpected result from solver: {}", self.display(resp)),
))
}
}
pub fn check(&mut self) -> io::Result<Response> {
let solver = self
.solver
.as_mut()
.expect("check requires a running solver");
solver.send(&self.arena, self.arena.list(vec![self.atoms.check_sat]))?;
let resp = solver.recv(&self.arena)?;
if resp == self.atoms.sat {
Ok(Response::Sat)
} else if resp == self.atoms.unsat {
Ok(Response::Unsat)
} else if resp == self.atoms.unknown {
Ok(Response::Unknown)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Unexpected result from solver: {}", self.display(resp)),
))
}
}
pub fn declare_const<S: Into<String> + AsRef<str>>(
&mut self,
name: S,
sort: SExpr,
) -> io::Result<SExpr> {
let name = self.atom(name);
let solver = self
.solver
.as_mut()
.expect("declare_const requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![self.atoms.declare_const, name, sort]),
)?;
Ok(name)
}
pub fn declare_datatype<S: Into<String> + AsRef<str>>(
&mut self,
name: S,
decl: SExpr,
) -> io::Result<SExpr> {
let name = self.atom(name);
let solver = self
.solver
.as_mut()
.expect("declare_datatype requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena
.list(vec![self.atoms.declare_datatype, name, decl]),
)?;
Ok(name)
}
pub fn declare_datatypes<N, S, D>(&mut self, sorts: S, decls: D) -> io::Result<()>
where
N: Into<String> + AsRef<str>,
S: IntoIterator<Item = (N, i32)>,
D: IntoIterator<Item = SExpr>,
{
let sorts = sorts
.into_iter()
.map(|(n, i)| self.list(vec![self.atom(n), self.numeral(i)]))
.collect();
let decls = decls.into_iter().collect();
let solver = self
.solver
.as_mut()
.expect("declare_datatype requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![
self.atoms.declare_datatypes,
self.arena.list(sorts),
self.arena.list(decls),
]),
)
}
pub fn declare_fun<S: Into<String> + AsRef<str>>(
&mut self,
name: S,
args: Vec<SExpr>,
out: SExpr,
) -> io::Result<SExpr> {
let name = self.atom(name);
let solver = self
.solver
.as_mut()
.expect("declare_fun requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![
self.atoms.declare_fun,
name,
self.arena.list(args),
out,
]),
)?;
Ok(name)
}
pub fn define_fun<S: Into<String> + AsRef<str>>(
&mut self,
name: S,
args: Vec<(S, SExpr)>,
out: SExpr,
body: SExpr,
) -> io::Result<SExpr> {
let name = self.atom(name);
let args = args
.into_iter()
.map(|(n, s)| self.list(vec![self.atom(n), s]))
.collect();
let solver = self
.solver
.as_mut()
.expect("define_fun requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![
self.atoms.define_fun,
name,
self.arena.list(args),
out,
body,
]),
)?;
Ok(name)
}
pub fn define_const<S: Into<String> + AsRef<str>>(
&mut self,
name: S,
out: SExpr,
body: SExpr,
) -> io::Result<SExpr> {
self.define_fun(name, vec![], out, body)
}
pub fn declare_sort<S: Into<String> + AsRef<str>>(
&mut self,
symbol: S,
arity: i32,
) -> io::Result<SExpr> {
let symbol = self.atom(symbol);
let arity = self.numeral(arity);
let solver = self
.solver
.as_mut()
.expect("declare_sort requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena
.list(vec![self.atoms.declare_sort, symbol, arity]),
)?;
Ok(symbol)
}
pub fn assert(&mut self, expr: SExpr) -> io::Result<()> {
let solver = self
.solver
.as_mut()
.expect("assert requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![self.atoms.assert, expr]),
)
}
pub fn get_model(&mut self) -> io::Result<SExpr> {
let solver = self
.solver
.as_mut()
.expect("get_model requires a running solver");
solver.send(&self.arena, self.arena.list(vec![self.atoms.get_model]))?;
solver.recv(&self.arena)
}
pub fn get_value(&mut self, vals: Vec<SExpr>) -> io::Result<Vec<(SExpr, SExpr)>> {
if vals.is_empty() {
return Ok(vec![]);
}
let solver = self
.solver
.as_mut()
.expect("get_value requires a running solver");
solver.send(
&self.arena,
self.arena
.list(vec![self.atoms.get_value, self.arena.list(vals)]),
)?;
let resp = solver.recv(&self.arena)?;
match self.arena.get(resp) {
SExprData::List(pairs) => {
let mut res = Vec::with_capacity(pairs.len());
for expr in pairs {
match self.arena.get(*expr) {
SExprData::List(pair) => {
assert_eq!(2, pair.len());
res.push((pair[0], pair[1]));
}
_ => unreachable!(),
}
}
Ok(res)
}
_ => Err(io::Error::new(
io::ErrorKind::Other,
"Failed to parse solver output",
)),
}
}
pub fn get_unsat_core(&mut self) -> io::Result<SExpr> {
let solver = self
.solver
.as_mut()
.expect("get_unsat_core requires a running solver");
solver.send(
&self.arena,
self.arena.list(vec![self.atoms.get_unsat_core]),
)?;
solver.recv(&self.arena)
}
pub fn set_logic<L: Into<String> + AsRef<str>>(&mut self, logic: L) -> io::Result<()> {
let solver = self
.solver
.as_mut()
.expect("set_logic requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena
.list(vec![self.atoms.set_logic, self.arena.atom(logic)]),
)
}
pub fn push(&mut self) -> io::Result<()> {
let solver = self
.solver
.as_mut()
.expect("push requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![self.atoms.push]),
)
}
pub fn push_many(&mut self, n: usize) -> io::Result<()> {
let solver = self
.solver
.as_mut()
.expect("push_many requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena
.list(vec![self.atoms.push, self.arena.atom(n.to_string())]),
)
}
pub fn pop(&mut self) -> io::Result<()> {
let solver = self.solver.as_mut().expect("pop requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena.list(vec![self.atoms.pop]),
)
}
pub fn pop_many(&mut self, n: usize) -> io::Result<()> {
let solver = self
.solver
.as_mut()
.expect("pop_many requires a running solver");
solver.ack_command(
&self.arena,
self.atoms.success,
self.arena
.list(vec![self.atoms.pop, self.arena.atom(n.to_string())]),
)
}
}
impl Context {
pub fn atom(&self, name: impl Into<String> + AsRef<str>) -> SExpr {
self.arena.atom(name)
}
pub fn list(&self, list: Vec<SExpr>) -> SExpr {
self.arena.list(list)
}
pub fn numeral(&self, val: impl IntoNumeral) -> SExpr {
self.arena.atom(val.to_string())
}
pub fn decimal(&self, val: impl IntoDecimal) -> SExpr {
self.arena.atom(val.to_string())
}
pub fn binary(&self, bit_width: usize, val: impl IntoBinary) -> SExpr {
let val = format!("#b{val:0>bit_width$b}");
self.arena.atom(val)
}
pub fn display(&self, expr: SExpr) -> DisplayExpr {
self.arena.display(expr)
}
pub fn get(&self, expr: SExpr) -> SExprData {
self.arena.get(expr)
}
pub fn atoms(&self) -> &KnownAtoms {
&self.atoms
}
}
impl Context {
pub fn par<N, S, D>(&self, symbols: S, decls: D) -> SExpr
where
N: Into<String> + AsRef<str>,
S: IntoIterator<Item = N>,
D: IntoIterator<Item = SExpr>,
{
self.list(vec![
self.atoms.par,
self.list(symbols.into_iter().map(|n| self.atom(n)).collect()),
self.list(decls.into_iter().collect()),
])
}
pub fn attr(&self, expr: SExpr, name: impl Into<String> + AsRef<str>, val: SExpr) -> SExpr {
self.list(vec![self.atoms.bang, expr, self.atom(name), val])
}
pub fn named(&self, name: impl Into<String> + AsRef<str>, expr: SExpr) -> SExpr {
self.attr(expr, ":named", self.atom(name))
}
pub fn let_<N: Into<String> + AsRef<str>>(&self, name: N, e: SExpr, body: SExpr) -> SExpr {
self.list(vec![
self.atoms.let_,
self.list(vec![self.atom(name), e]),
body,
])
}
pub fn let_many<N, I>(&self, bindings: I, body: SExpr) -> SExpr
where
I: IntoIterator<Item = (N, SExpr)>,
N: Into<String> + AsRef<str>,
{
let args: Vec<_> = std::iter::once(self.atoms.let_)
.chain(
bindings
.into_iter()
.map(|(n, v)| self.list(vec![self.atom(n), v])),
)
.chain(std::iter::once(body))
.collect();
assert!(args.len() >= 3);
self.list(args)
}
pub fn forall<N, I>(&self, vars: I, body: SExpr) -> SExpr
where
I: IntoIterator<Item = (N, SExpr)>,
N: Into<String> + AsRef<str>,
{
let args: Vec<_> = std::iter::once(self.atoms.forall)
.chain(
vars.into_iter()
.map(|(n, s)| self.list(vec![self.atom(n), s])),
)
.chain(std::iter::once(body))
.collect();
assert!(args.len() >= 3);
self.list(args)
}
pub fn exists<N, I>(&self, vars: I, body: SExpr) -> SExpr
where
I: IntoIterator<Item = (N, SExpr)>,
N: Into<String> + AsRef<str>,
{
let args: Vec<_> = std::iter::once(self.atoms.exists)
.chain(
vars.into_iter()
.map(|(n, s)| self.list(vec![self.atom(n), s])),
)
.chain(std::iter::once(body))
.collect();
assert!(args.len() >= 3);
self.list(args)
}
pub fn match_<I: IntoIterator<Item = (SExpr, SExpr)>>(&self, term: SExpr, arms: I) -> SExpr {
let args: Vec<_> = std::iter::once(self.atoms.match_)
.chain(std::iter::once(term))
.chain(arms.into_iter().map(|(p, v)| self.list(vec![p, v])))
.collect();
assert!(args.len() >= 3);
self.list(args)
}
pub fn ite(&self, c: SExpr, t: SExpr, e: SExpr) -> SExpr {
self.list(vec![self.atoms.ite, c, t, e])
}
}
impl Context {
pub fn bool_sort(&self) -> SExpr {
self.atoms.bool
}
pub fn true_(&self) -> SExpr {
self.atoms.t
}
pub fn false_(&self) -> SExpr {
self.atoms.f
}
unary!(not, not);
right_assoc!(imp, imp_many, imp);
left_assoc!(and, and_many, and);
left_assoc!(or, or_many, or);
left_assoc!(xor, xor_many, xor);
chainable!(eq, eq_many, eq);
pairwise!(distinct, distinct_many, distinct);
}
impl Context {
pub fn int_sort(&self) -> SExpr {
self.atoms.int
}
unary!(negate, minus);
left_assoc!(sub, sub_many, minus);
left_assoc!(plus, plus_many, plus);
left_assoc!(times, times_many, times);
left_assoc!(div, div_many, div);
left_assoc!(modulo, modulo_many, modulo);
left_assoc!(rem, rem_many, rem);
chainable!(lte, lte_many, lte);
chainable!(lt, lt_many, lt);
chainable!(gt, gt_many, gt);
chainable!(gte, gte_many, gte);
}
impl Context {
pub fn array_sort(&self, index: SExpr, element: SExpr) -> SExpr {
self.list(vec![self.atoms.array, index, element])
}
pub fn select(&self, ary: SExpr, index: SExpr) -> SExpr {
self.list(vec![self.atoms.select, ary, index])
}
pub fn store(&self, ary: SExpr, index: SExpr, value: SExpr) -> SExpr {
self.list(vec![self.atoms.store, ary, index, value])
}
}
impl Context {
pub fn bit_vec_sort(&self, width: SExpr) -> SExpr {
self.list(vec![self.atoms.und, self.atoms.bit_vec, width])
}
pub fn concat(&self, lhs: SExpr, rhs: SExpr) -> SExpr {
self.list(vec![self.atoms.concat, lhs, rhs])
}
pub fn extract(&self, i: i32, j: i32, bv: SExpr) -> SExpr {
self.list(vec![
self.list(vec![
self.atoms.und,
self.atoms.extract,
self.numeral(i),
self.numeral(j),
]),
bv,
])
}
unary!(bvnot, bvnot);
left_assoc!(bvor, bvor_many, bvor);
left_assoc!(bvand, bvand_many, bvand);
left_assoc!(bvnand, bvnand_many, bvnand);
left_assoc!(bvxor, bvxor_many, bvxor);
left_assoc!(bvxnor, bvxnor_many, bvxnor);
unary!(bvneg, bvneg);
left_assoc!(bvadd, bvadd_many, bvadd);
binop!(bvsub, bvsub);
left_assoc!(bvmul, bvmul_many, bvmul);
binop!(bvudiv, bvudiv);
binop!(bvurem, bvurem);
binop!(bvsrem, bvsrem);
binop!(bvshl, bvshl);
binop!(bvlshr, bvlshr);
binop!(bvashr, bvashr);
binop!(bvule, bvule);
binop!(bvult, bvult);
binop!(bvuge, bvuge);
binop!(bvugt, bvugt);
binop!(bvsle, bvsle);
binop!(bvslt, bvslt);
binop!(bvsge, bvsge);
binop!(bvsgt, bvsgt);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Response {
Sat,
Unsat,
Unknown,
}
mod private {
pub trait IntoNumeral: IntoNumeralSealed {}
pub trait IntoNumeralSealed: std::fmt::Display {}
pub trait IntoBinary: IntoBinarySealed {}
pub trait IntoBinarySealed: std::fmt::Binary {}
impl IntoNumeralSealed for i8 {}
impl IntoNumeral for i8 {}
impl IntoBinarySealed for i8 {}
impl IntoBinary for i8 {}
impl IntoNumeralSealed for i16 {}
impl IntoNumeral for i16 {}
impl IntoBinarySealed for i16 {}
impl IntoBinary for i16 {}
impl IntoNumeralSealed for i32 {}
impl IntoNumeral for i32 {}
impl IntoBinarySealed for i32 {}
impl IntoBinary for i32 {}
impl IntoNumeralSealed for i64 {}
impl IntoNumeral for i64 {}
impl IntoBinarySealed for i64 {}
impl IntoBinary for i64 {}
impl IntoNumeralSealed for i128 {}
impl IntoNumeral for i128 {}
impl IntoBinarySealed for i128 {}
impl IntoBinary for i128 {}
impl IntoNumeralSealed for isize {}
impl IntoNumeral for isize {}
impl IntoBinarySealed for isize {}
impl IntoBinary for isize {}
impl IntoNumeralSealed for u8 {}
impl IntoNumeral for u8 {}
impl IntoBinarySealed for u8 {}
impl IntoBinary for u8 {}
impl IntoNumeralSealed for u16 {}
impl IntoNumeral for u16 {}
impl IntoBinarySealed for u16 {}
impl IntoBinary for u16 {}
impl IntoNumeralSealed for u32 {}
impl IntoNumeral for u32 {}
impl IntoBinarySealed for u32 {}
impl IntoBinary for u32 {}
impl IntoNumeralSealed for u64 {}
impl IntoNumeral for u64 {}
impl IntoBinarySealed for u64 {}
impl IntoBinary for u64 {}
impl IntoNumeralSealed for u128 {}
impl IntoNumeral for u128 {}
impl IntoBinarySealed for u128 {}
impl IntoBinary for u128 {}
impl IntoNumeralSealed for usize {}
impl IntoNumeral for usize {}
impl IntoBinarySealed for usize {}
impl IntoBinary for usize {}
pub trait IntoDecimal: IntoDecimalSealed {}
pub trait IntoDecimalSealed: std::fmt::Display {}
impl IntoDecimal for f32 {}
impl IntoDecimalSealed for f32 {}
impl IntoDecimal for f64 {}
impl IntoDecimalSealed for f64 {}
}
pub use private::{IntoBinary, IntoDecimal, IntoNumeral};
#[cfg(test)]
mod tests {
use super::*;
macro_rules! check_expr {
($ctx:expr, $expr:expr, $expected:expr) => {
let actual = $ctx.display($expr).to_string();
assert_eq!(actual, $expected);
};
}
#[test]
fn test_variadic_ops() {
let ctx = ContextBuilder::new().build().unwrap();
check_expr!(ctx, ctx.imp_many([ctx.true_()]), "true");
check_expr!(
ctx,
ctx.imp_many([ctx.true_(), ctx.false_()]),
"(=> true false)"
);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn sexpr_with_wrong_context() {
let ctx1 = ContextBuilder::new().build().unwrap();
let ctx2 = ContextBuilder::new().build().unwrap();
let pizza1 = ctx1.atom("pizza");
let _pizza2 = ctx2.atom("pizza");
let _ = ctx2.get(pizza1);
}
}