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, 'input>(project: &'a ask_dracula::Project<'input>) -> Option<&'a ask_dracula::project::Statement<'input>> { for block in &project.blocks { if let ask_dracula::project::Block::Statement(ref statement) = block { if statement.kind == ask_dracula::project::StatementKind::Lemma || statement.kind == ask_dracula::project::StatementKind::Conjecture { return Some(statement) } } } None } fn find_conjecture_mut<'a, 'input>(project: &'a mut ask_dracula::Project<'input>) -> Option<&'a mut ask_dracula::project::Statement<'input>> { for block in &mut project.blocks { if let ask_dracula::project::Block::Statement(ref mut statement) = block { if statement.kind == ask_dracula::project::StatementKind::Lemma || statement.kind == ask_dracula::project::StatementKind::Conjecture { return Some(statement) } } } None } enum ProofResult { 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, }; //eprintln!("{}", input); 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(ProofResult::ProofNotFound); } return Err(ask_dracula::Error::new_run_vampire( format!("Vampire returned unsuccessful exit code ({})\n\n======== stdout ========\n{}\n\n======== stderr ========\n{}", output.status, std::str::from_utf8(&output.stdout).unwrap_or("(not valid UTF-8)").trim(), std::str::from_utf8(&output.stderr).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(ProofResult::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")?; let conjecture = find_conjecture(&project); let vampire_result = match conjecture { None => { eprintln!("no lemma or conjecture found, nothing to do"); return Ok(()); }, Some(ref conjecture) => { eprintln!("verifying conjecture: {}", conjecture.formula); let tptp_content = format!("{}", ask_dracula::format_tptp::display_project_with_conjecture_tptp(&project, conjecture)); run_vampire(&tptp_content, matches.values_of("vampire_arguments").map(|value| value))? }, }; let conjecture = find_conjecture_mut(&mut project).unwrap(); match vampire_result { ProofResult::ProofNotFound => { println!("proof not found"); Ok(()) }, ProofResult::Refutation => { println!("conjecture proven"); conjecture.kind = ask_dracula::project::StatementKind::Axiom; let replace_statement_kind_regex = regex::Regex::new(r"(conjecture|lemma)").unwrap(); let new_text = replace_statement_kind_regex.replace(conjecture.original_text, "axiom"); conjecture.original_text = &new_text; 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(()) }, ProofResult::Satisfiable => { println!("conjecture disproven"); Ok(()) }, } }