use std::process::ExitCode;
use z3rs::cmd_context::run_smt2;
use z3rs::sat::{SatResult, check_drat_text, parse_dimacs};
use z3rs::util::lbool::LBool;
fn print_version() {
println!(
"z3rs version {} (pure-Rust port of Z3 {})",
z3rs::VERSION,
z3rs::Z3_UPSTREAM_VERSION
);
}
fn print_usage() {
println!("z3rs [options] <file>");
println!();
println!("A pure-Rust port of the Z3 theorem prover (early, in progress).");
println!();
println!("Options:");
println!(" -h, --help print this message");
println!(" --version print version information");
println!(" -smt2 read input as SMT-LIB2 (default; QF_UF subset)");
println!(" -dimacs read input in DIMACS CNF format (default for *.cnf)");
println!(" -dl read input as Datalog (facts/rules/?- queries)");
println!(" -drat <cnf> <p> check a DRAT proof <p> against a DIMACS CNF <cnf>");
}
fn run_datalog_file(path: &str) -> ExitCode {
let text = match std::fs::read_to_string(path) {
Ok(t) => t,
Err(e) => {
eprintln!("z3rs: cannot read {path:?}: {e}");
return ExitCode::from(1);
}
};
let program = match z3rs::muz::parse(&text) {
Ok(p) => p,
Err(e) => {
eprintln!("z3rs: {e}");
return ExitCode::from(1);
}
};
let model = z3rs::muz::evaluate(&program);
for q in &program.queries {
let answers = model.query(q);
if answers.is_empty() {
println!("unsat");
} else {
println!("sat");
}
}
ExitCode::SUCCESS
}
fn run_drat(cnf_path: &str, proof_path: &str) -> ExitCode {
let cnf = match std::fs::read_to_string(cnf_path) {
Ok(t) => t,
Err(e) => {
eprintln!("z3rs: cannot read {cnf_path:?}: {e}");
return ExitCode::from(1);
}
};
let proof = match std::fs::read_to_string(proof_path) {
Ok(t) => t,
Err(e) => {
eprintln!("z3rs: cannot read {proof_path:?}: {e}");
return ExitCode::from(1);
}
};
match check_drat_text(&cnf, &proof) {
Ok(()) => {
println!("s VERIFIED");
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("z3rs: DRAT check failed: {e}");
ExitCode::from(1)
}
}
}
fn run_dimacs(path: &str) -> ExitCode {
let text = match std::fs::read_to_string(path) {
Ok(t) => t,
Err(e) => {
eprintln!("z3rs: cannot read {path:?}: {e}");
return ExitCode::from(1);
}
};
let mut solver = match parse_dimacs(&text) {
Ok(s) => s,
Err(e) => {
eprintln!("z3rs: {e}");
return ExitCode::from(1);
}
};
match solver.solve() {
SatResult::Sat => {
println!("s SATISFIABLE");
let mut line = String::from("v");
for v in 0..solver.num_vars() as u32 {
let signed = match solver.value(v) {
LBool::False => -((v + 1) as i64),
_ => (v + 1) as i64, };
line.push_str(&format!(" {signed}"));
}
line.push_str(" 0");
println!("{line}");
ExitCode::from(10)
}
SatResult::Unsat => {
println!("s UNSATISFIABLE");
ExitCode::from(20)
}
}
}
fn run_smt2_file(path: &str) -> ExitCode {
let text = match std::fs::read_to_string(path) {
Ok(t) => t,
Err(e) => {
eprintln!("z3rs: cannot read {path:?}: {e}");
return ExitCode::from(1);
}
};
match run_smt2(&text) {
Ok(responses) => {
for r in responses {
println!("{r}");
}
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("z3rs: {e}");
ExitCode::from(1)
}
}
}
fn main() -> ExitCode {
let args: Vec<String> = std::env::args().skip(1).collect();
if args.is_empty() {
print_usage();
return ExitCode::SUCCESS;
}
let mut force_dimacs = false;
let mut force_datalog = false;
let mut force_drat = false;
let mut files: Vec<String> = Vec::new();
for arg in &args {
match arg.as_str() {
"--version" | "-version" | "/version" => {
print_version();
return ExitCode::SUCCESS;
}
"-h" | "--help" | "-?" | "/?" => {
print_usage();
return ExitCode::SUCCESS;
}
"-dimacs" => force_dimacs = true,
"-dl" => force_datalog = true,
"-drat" => force_drat = true,
"-smt2" | "-in" => {} other if other.starts_with('-') => {
eprintln!("z3rs: unknown option {other:?}");
return ExitCode::from(1);
}
other => files.push(other.to_string()),
}
}
if force_drat {
return match files.as_slice() {
[cnf, proof] => run_drat(cnf, proof),
_ => {
eprintln!("z3rs: -drat requires exactly two files: <cnf> <proof>");
ExitCode::from(1)
}
};
}
let file = files.into_iter().next();
match file {
Some(path) if force_datalog || path.ends_with(".dl") || path.ends_with(".datalog") => {
run_datalog_file(&path)
}
Some(path) if force_dimacs || path.ends_with(".cnf") || path.ends_with(".dimacs") => {
run_dimacs(&path)
}
Some(path) => run_smt2_file(&path), None => {
print_usage();
ExitCode::SUCCESS
}
}
}