ask-dracula-rs/src/main.rs

173 lines
4.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

fn backup_file_path(file_path: &std::path::Path) -> Result<std::path::PathBuf, ask_dracula::Error>
{
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<I, S>(input: &str, arguments: Option<I>)
-> Result<VampireResult, ask_dracula::Error>
where I: IntoIterator<Item = S>, S: AsRef<std::ffi::OsStr>
{
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<dyn std::error::Error>>
{
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 <project file> -- --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(|_| "couldnt 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(())
},
}
},
}
}