use std::fmt;
use crate::{SmtRes, Solver};
use self::SmtStyle::*;
pub type ConfItem = Option<&'static str>;
#[inline]
fn unsupported() -> ConfItem {
None
}
#[inline]
fn supported(keyword: &'static str) -> ConfItem {
Some(keyword)
}
pub const Z3_ENV_VAR: &str = "RSMT2_Z3_CMD";
pub const CVC4_ENV_VAR: &str = "RSMT2_CVC4_CMD";
pub const YICES_2_ENV_VAR: &str = "RSMT2_YICES_2_CMD";
fn get_env_var(env_var: &str) -> SmtRes<Option<String>> {
match std::env::var(env_var) {
Ok(res) => Ok(Some(res)),
Err(std::env::VarError::NotPresent) => Ok(None),
Err(std::env::VarError::NotUnicode(cmd)) => bail!(
"value of environment variable `{}` is not legal unicode: `{}`",
env_var,
cmd.to_string_lossy(),
),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum SmtStyle {
Z3,
CVC4,
Yices2,
}
impl SmtStyle {
pub fn new(self, cmd: impl Into<String>) -> SmtConf {
let cmd = cmd.into();
match self {
Z3 => SmtConf {
style: self,
cmd,
options: vec!["-in".into(), "-smt2".into()],
models: true,
incremental: true,
check_success: false,
unsat_cores: false,
interpolants: true,
check_sat_assuming: supported("check-sat-assuming"),
},
CVC4 => SmtConf {
style: self,
cmd,
options: vec![
"-q".into(),
"--no-interactive".into(),
"--lang".into(),
"smt2".into(),
],
models: false,
incremental: false,
check_success: false,
unsat_cores: false,
interpolants: false,
check_sat_assuming: unsupported(),
},
Yices2 => SmtConf {
style: self,
cmd,
options: vec![],
models: false,
incremental: false,
check_success: false,
unsat_cores: false,
interpolants: false,
check_sat_assuming: supported("check-sat-assuming"),
},
}
}
pub fn default(self) -> SmtConf {
self.new(self.cmd())
}
#[allow(dead_code)]
pub fn of_str(s: &str) -> Option<Self> {
match s {
"z3" | "Z3" => Some(Z3),
"cvc4" | "CVC4" => Some(CVC4),
"Yices2" | "yices2" | "YICES2" | "Yices 2" | "yices 2" | "YICES 2" => Some(Yices2),
_ => None,
}
}
#[allow(dead_code)]
pub fn str_keys() -> Vec<&'static str> {
vec![
"z3", "Z3", "cvc4", "CVC4", "Yices2", "yices2", "YICES2", "Yices 2", "yices 2",
"YICES 2",
]
}
pub fn env_cmd(self) -> SmtRes<Option<String>> {
match self {
Z3 => get_env_var(Z3_ENV_VAR),
CVC4 => get_env_var(CVC4_ENV_VAR),
Yices2 => get_env_var(YICES_2_ENV_VAR),
}
}
#[cfg(not(windows))]
pub fn cmd(self) -> &'static str {
match self {
Z3 => "z3",
CVC4 => "cvc4",
Yices2 => "yices",
}
}
#[cfg(windows)]
pub fn cmd(self) -> &'static str {
match self {
Z3 => "z3.exe",
CVC4 => "cvc4.exe",
Yices2 => "yices.exe",
}
}
pub fn is_z3(self) -> bool {
self == Self::Z3
}
}
impl fmt::Display for SmtStyle {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Z3 => write!(fmt, "z3"),
CVC4 => write!(fmt, "cvc4"),
Yices2 => write!(fmt, "yices2"),
}
}
}
#[derive(Debug, Clone)]
pub struct SmtConf {
style: SmtStyle,
cmd: String,
options: Vec<String>,
models: bool,
incremental: bool,
check_success: bool,
unsat_cores: bool,
interpolants: bool,
check_sat_assuming: ConfItem,
}
impl SmtConf {
pub fn style(&self) -> SmtStyle {
self.style
}
#[inline]
pub fn z3(cmd: impl Into<String>) -> Self {
Z3.new(cmd)
}
#[inline]
pub fn cvc4(cmd: impl Into<String>) -> Self {
CVC4.new(cmd)
}
#[inline]
pub fn yices_2(cmd: impl Into<String>) -> Self {
Yices2.new(cmd)
}
#[inline]
pub fn z3_or_env(cmd: Option<String>) -> SmtRes<Self> {
if let Some(cmd) = cmd {
Ok(Self::z3(cmd))
} else {
let cmd = SmtStyle::Z3.env_cmd()?;
if let Some(cmd) = cmd {
Ok(Self::z3(cmd))
} else {
bail!(
"no command for z3 provided, \
and `{}` environment variable not set; cannot spawn z3 solver",
Z3_ENV_VAR,
)
}
}
}
#[inline]
pub fn cvc4_or_env(cmd: Option<String>) -> SmtRes<Self> {
if let Some(cmd) = cmd {
Ok(Self::cvc4(cmd))
} else {
let cmd = SmtStyle::CVC4.env_cmd()?;
if let Some(cmd) = cmd {
Ok(Self::cvc4(cmd))
} else {
bail!(
"no command for CVC4 provided, \
and `{}` environment variable not set; cannot spawn CVC4 solver",
CVC4_ENV_VAR,
)
}
}
}
#[inline]
pub fn yices_2_or_env(cmd: Option<String>) -> SmtRes<Self> {
if let Some(cmd) = cmd {
Ok(Self::yices_2(cmd))
} else {
let cmd = SmtStyle::Yices2.env_cmd()?;
if let Some(cmd) = cmd {
Ok(Self::yices_2(cmd))
} else {
bail!(
"no command for Yices 2 provided, \
and `{}` environment variable not set; cannot spawn Yices 2 solver",
YICES_2_ENV_VAR,
)
}
}
}
#[inline]
pub fn default_z3() -> Self {
Z3.default()
}
#[inline]
pub fn default_cvc4() -> Self {
CVC4.default()
}
#[inline]
pub fn default_yices_2() -> Self {
Yices2.default()
}
#[inline]
pub fn spawn<Parser>(self, parser: Parser) -> SmtRes<Solver<Parser>> {
Solver::new(self, parser)
}
#[inline]
pub fn desc(&self) -> &str {
match self.style {
Z3 => "z3",
CVC4 => "cvc4",
Yices2 => "yices2",
}
}
#[inline]
pub fn get_cmd(&self) -> &str {
&self.cmd
}
#[inline]
pub fn get_options(&self) -> &[String] {
&self.options
}
#[inline]
pub fn option<S: Into<String>>(&mut self, o: S) -> &mut Self {
self.options.push(o.into());
self
}
#[inline]
pub fn cmd<S: Into<String>>(&mut self, cmd: S) -> &mut Self {
let cmd = cmd.into();
let mut iter = cmd.split(' ');
'set_cmd: while let Some(cmd) = iter.next() {
if !cmd.is_empty() {
self.cmd = cmd.into();
break 'set_cmd;
}
}
for option in iter {
if !option.is_empty() {
self.options.push(option.into())
}
}
self
}
pub fn get_models(&self) -> bool {
self.models
}
pub fn models(&mut self) {
self.set_models(true)
}
pub fn set_models(&mut self, val: bool) {
self.models = val;
if val {
match self.style {
CVC4 => self.options.push("--produce-models".into()),
Yices2 => self.options.push("--smt2-model-format".into()),
Z3 => (),
}
}
}
pub fn get_incremental(&self) -> bool {
self.incremental
}
pub fn incremental(&mut self) {
self.set_incremental(true)
}
pub fn set_incremental(&mut self, val: bool) {
self.incremental = val;
if val {
match self.style {
CVC4 | Yices2 => self.options.push("--incremental".into()),
Z3 => (),
}
}
}
#[inline]
pub fn get_unsat_cores(&self) -> bool {
self.unsat_cores
}
#[inline]
pub fn unsat_cores(&mut self) {
self.set_unsat_cores(true)
}
#[inline]
pub fn set_unsat_cores(&mut self, val: bool) {
self.unsat_cores = val
}
#[inline]
pub fn get_check_sat_assuming(&self) -> ConfItem {
self.check_sat_assuming.as_ref().map(|s| *s)
}
#[inline]
pub fn get_check_success(&self) -> bool {
self.check_success
}
#[inline]
pub fn check_success(&mut self) {
self.set_check_success(true)
}
#[inline]
pub fn set_check_success(&mut self, val: bool) {
self.check_success = val
}
#[inline]
pub fn get_interpolants(&self) -> bool {
self.interpolants
}
#[inline]
pub fn interpolants(&mut self) -> SmtRes<()> {
self.set_interpolants(true)
}
#[inline]
pub fn set_interpolants(&mut self, val: bool) -> SmtRes<()> {
if self.style.is_z3() {
self.interpolants = val;
Ok(())
} else {
bail!(
"interpolant production is only supported by Z3, \
cannot activate interpolants for `{}`",
self.style,
)
}
}
}
impl SmtConf {
pub fn enrich_get_values_error(&self, e: crate::errors::Error) -> crate::errors::Error {
match self.style {
SmtStyle::CVC4 => e.chain_err(|| {
"some versions of CVC4 produce errors on `get-value` commands, \
consider using `get-model` instead"
}),
SmtStyle::Z3 | SmtStyle::Yices2 => e,
}
}
}