use crate::driver::{Config, Driver, launch};
use crate::parser::{parse, Response};
use crate::term::{LogicError, Sort, Term};
use std::io::{BufRead, Write};
pub struct Solver {
pub config: Config,
pub driver: Driver,
pub trace_file: Option<std::fs::File>,
}
impl Solver {
#[cfg(not(feature = "tokio"))]
pub fn new(config: Config) -> Result<Self, LogicError> {
let trace_file = if config.trace {
let path = format!("trace_{}.smt2", std::process::id());
Some(std::fs::File::create(&path)?)
} else {
None
};
let driver = launch(&config)?;
let mut solver = Solver { config, driver, trace_file };
solver.send("(set-option :print-success true)")?;
solver.send("(set-logic ALL)")?;
Ok(solver)
}
#[cfg(feature = "tokio")]
pub async fn new(config: Config) -> Result<Self, LogicError> {
let trace_file = if config.trace {
let path = format!("trace_{}.smt2", std::process::id());
Some(std::fs::File::create(&path)?)
} else {
None
};
let driver = launch(&config)?;
let mut solver = Solver { config, driver, trace_file };
solver.send("(set-option :print-success true)").await?;
solver.send("(set-logic ALL)").await?;
Ok(solver)
}
#[cfg(not(feature = "tokio"))]
fn send(&mut self, cmd: &str) -> Result<(), LogicError> {
if let Some(ref mut f) = self.trace_file {
writeln!(f, "{}", cmd)?;
}
writeln!(self.driver.stdin, "{}", cmd)?;
self.driver.stdin.flush()?;
let mut line = String::new();
self.driver.stdout.read_line(&mut line)?;
if !line.trim().is_empty() && line.trim() != "success" {
if line.contains("error") {
return Err(LogicError::Solver(line.trim().to_string()));
}
}
Ok(())
}
#[cfg(feature = "tokio")]
async fn send(&mut self, cmd: &str) -> Result<(), LogicError> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
if let Some(ref mut f) = self.trace_file {
writeln!(f, "{}", cmd)?;
}
self.driver.stdin.write_all(cmd.as_bytes()).await.map_err(|e| LogicError::Io(e))?;
self.driver.stdin.write_all(b"\n").await.map_err(|e| LogicError::Io(e))?;
self.driver.stdin.flush().await.map_err(|e| LogicError::Io(e))?;
let mut line = String::new();
self.driver.stdout.read_line(&mut line).await.map_err(|e| LogicError::Io(e))?;
if !line.trim().is_empty() && line.trim() != "success" {
if line.contains("error") {
return Err(LogicError::Solver(line.trim().to_string()));
}
}
Ok(())
}
#[cfg(not(feature = "tokio"))]
fn query(&mut self, cmd: &str) -> Result<String, LogicError> {
if let Some(ref mut f) = self.trace_file {
writeln!(f, "{}", cmd)?;
}
writeln!(self.driver.stdin, "{}", cmd)?;
self.driver.stdin.flush()?;
let mut result = String::new();
self.driver.stdout.read_line(&mut result)?;
if result.trim().starts_with('(') {
let mut depth = 0i32;
for c in result.chars() {
match c {
'(' => depth += 1,
')' => depth -= 1,
_ => {}
}
}
while depth > 0 {
let mut line = String::new();
self.driver.stdout.read_line(&mut line)?;
for c in line.chars() {
match c {
'(' => depth += 1,
')' => depth -= 1,
_ => {}
}
}
result.push_str(&line);
}
}
Ok(result)
}
#[cfg(feature = "tokio")]
async fn query(&mut self, cmd: &str) -> Result<String, LogicError> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
if let Some(ref mut f) = self.trace_file {
writeln!(f, "{}", cmd)?;
}
self.driver.stdin.write_all(cmd.as_bytes()).await.map_err(|e| LogicError::Io(e))?;
self.driver.stdin.write_all(b"\n").await.map_err(|e| LogicError::Io(e))?;
self.driver.stdin.flush().await.map_err(|e| LogicError::Io(e))?;
let mut result = String::new();
self.driver.stdout.read_line(&mut result).await.map_err(|e| LogicError::Io(e))?;
if result.trim().starts_with('(') {
let mut depth = 0i32;
for c in result.chars() {
match c {
'(' => depth += 1,
')' => depth -= 1,
_ => {}
}
}
while depth > 0 {
let mut line = String::new();
self.driver.stdout.read_line(&mut line).await.map_err(|e| LogicError::Io(e))?;
for c in line.chars() {
match c {
'(' => depth += 1,
')' => depth -= 1,
_ => {}
}
}
result.push_str(&line);
}
}
Ok(result)
}
#[cfg(not(feature = "tokio"))]
pub fn assert(&mut self, term: &Term) -> Result<(), LogicError> {
let cmd = format!("(assert {})", term);
self.send(&cmd)
}
#[cfg(feature = "tokio")]
pub async fn assert(&mut self, term: &Term) -> Result<(), LogicError> {
let cmd = format!("(assert {})", term);
self.send(&cmd).await
}
#[cfg(not(feature = "tokio"))]
pub fn declare(&mut self, name: &str, sort: &Sort) -> Result<(), LogicError> {
let sort_str = match sort {
Sort::Bool => "Bool",
Sort::Int => "Int",
};
let cmd = format!("(declare-const {} {})", name, sort_str);
self.send(&cmd)
}
#[cfg(feature = "tokio")]
pub async fn declare(&mut self, name: &str, sort: &Sort) -> Result<(), LogicError> {
let sort_str = match sort {
Sort::Bool => "Bool",
Sort::Int => "Int",
};
let cmd = format!("(declare-const {} {})", name, sort_str);
self.send(&cmd).await
}
#[cfg(not(feature = "tokio"))]
pub fn check(&mut self) -> Result<Response, LogicError> {
let result = self.query("(check-sat)")?;
parse(&result)
}
#[cfg(feature = "tokio")]
pub async fn check(&mut self) -> Result<Response, LogicError> {
let result = self.query("(check-sat)").await?;
parse(&result)
}
#[cfg(not(feature = "tokio"))]
pub fn get_model(&mut self) -> Result<Response, LogicError> {
let result = self.query("(get-model)")?;
parse(&result)
}
#[cfg(feature = "tokio")]
pub async fn get_model(&mut self) -> Result<Response, LogicError> {
let result = self.query("(get-model)").await?;
parse(&result)
}
#[cfg(not(feature = "tokio"))]
pub fn push(&mut self, n: usize) -> Result<(), LogicError> {
let cmd = format!("(push {})", n);
self.send(&cmd)
}
#[cfg(feature = "tokio")]
pub async fn push(&mut self, n: usize) -> Result<(), LogicError> {
let cmd = format!("(push {})", n);
self.send(&cmd).await
}
#[cfg(not(feature = "tokio"))]
pub fn pop(&mut self, n: usize) -> Result<(), LogicError> {
let cmd = format!("(pop {})", n);
self.send(&cmd)
}
#[cfg(feature = "tokio")]
pub async fn pop(&mut self, n: usize) -> Result<(), LogicError> {
let cmd = format!("(pop {})", n);
self.send(&cmd).await
}
}