fn backup_file_path(file_path: &std::path::Path) -> Result { let file_name = file_path.file_name() .ok_or(ask_dracula::Error::new_not_a_file(file_path.to_owned()))?; let mut i = 0; loop { i += 1; let backup_file_name = format!("{}.{}~", file_name.to_string_lossy(), i); let backup_file_path = file_path.with_file_name(backup_file_name); if !backup_file_path.exists() { return Ok(backup_file_path); } } } fn find_conjecture<'a>(project: &'a ask_dracula::Project) -> Option<(ask_dracula::project::StatementKind, &'a foliage::Formula)> { if let Some(lemmas) = project.statements.get(&ask_dracula::project::StatementKind::Lemma) { if let Some(lemma) = lemmas.first() { return Some((ask_dracula::project::StatementKind::Lemma, lemma)); } } if let Some(conjectures) = project.statements.get(&ask_dracula::project::StatementKind::Conjecture) { if let Some(conjecture) = conjectures.first() { return Some((ask_dracula::project::StatementKind::Conjecture, conjecture)); } } None } enum VampireResult { ProofNotFound, Refutation, Satisfiable, } fn run_vampire(input: &str, arguments: Option) -> Result where I: IntoIterator, S: AsRef { let mut vampire = std::process::Command::new("vampire"); let vampire = match arguments { Some(arguments) => vampire.args(arguments), None => &mut vampire, }; let mut vampire = vampire .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .spawn() .map_err(|error| ask_dracula::Error::new_run_vampire(error))?; { use std::io::Write; let vampire_stdin = vampire.stdin.as_mut().unwrap(); vampire_stdin.write_all(input.as_bytes()) .map_err(|error| ask_dracula::Error::new_run_vampire(error))?; } let output = vampire.wait_with_output() .map_err(|error| ask_dracula::Error::new_run_vampire(error))?; let stdout = std::str::from_utf8(&output.stdout) .map_err(|error| ask_dracula::Error::new_run_vampire(error))?; let stderr = std::str::from_utf8(&output.stderr) .map_err(|error| ask_dracula::Error::new_run_vampire(error))?; if !output.status.success() { let proof_not_found_regex = regex::Regex::new(r"% \(\d+\)Proof not found in time").unwrap(); if proof_not_found_regex.is_match(stdout) { return Ok(VampireResult::ProofNotFound); } return Err(ask_dracula::Error::new_run_vampire( format!("Vampire returned unsuccessful exit code ({})\n\n======== stderr ========\n{}", output.status, std::str::from_utf8(&output.stdout).unwrap_or("(not valid UTF-8)").trim()))); } let refutation_regex = regex::Regex::new(r"% \(\d+\)Termination reason: Refutation").unwrap(); if refutation_regex.is_match(stdout) { return Ok(VampireResult::Refutation); } Err(ask_dracula::Error::new_interpret_vampire_output(stdout.to_string(), stderr.to_string())) } fn main() -> Result<(), Box> { let matches = clap::App::new("Ask Dracula: Using Vampire as an interactive theorem prover") .arg(clap::Arg::with_name("file").takes_value(true).required(true)) .arg(clap::Arg::with_name("vampire_arguments").multiple(true)) .after_help("example: ask_dracula -- --mode casc --cores 8") .get_matches(); let file = matches.value_of("file").unwrap().to_string(); let file_path = std::path::Path::new(&file); let file_content = std::fs::read_to_string(&file_path)?; let (_, mut project) = ask_dracula::parse::project(&file_content) .map_err(|_| "couldn’t parse input file")?; match find_conjecture(&project) { None => { eprintln!("no lemma or conjecture found, nothing to do"); Ok(()) }, Some((ref statement_kind, ref conjecture)) => { eprintln!("verifying conjecture: {}", conjecture); let tptp_content = format!("{}", ask_dracula::format_tptp::display_project_with_conjecture_tptp(&project, (statement_kind.clone(), conjecture))); match run_vampire(&tptp_content, matches.values_of("vampire_arguments").map(|value| value))? { VampireResult::ProofNotFound => { println!("proof not found"); Ok(()) }, VampireResult::Refutation => { println!("conjecture proven"); let axiom = project.statements.get_mut(statement_kind).unwrap().remove(0); project.statements.entry(ask_dracula::project::StatementKind::Axiom).or_insert_with(Vec::new).push(axiom); let backup_file_path = backup_file_path(file_path)?; // Make backup of old file std::fs::rename(file_path, backup_file_path) .map_err(|error| ask_dracula::Error::new_write_file(file_path.to_owned(), error))?; // Write updated version of the file let file_content = format!("{}", project); std::fs::write(file_path, &file_content) .map_err(|error| ask_dracula::Error::new_write_file(file_path.to_owned(), error))?; Ok(()) }, VampireResult::Satisfiable => { println!("conjecture disproven"); Ok(()) }, } }, } }