ask-dracula-rs/src/main.rs

196 lines
5.5 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, '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<I, S>(input: &str, arguments: Option<I>)
-> Result<ProofResult, 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,
};
//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<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")?;
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(())
},
}
}