2019-11-02 07:23:34 +01:00
|
|
|
|
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()))
|
|
|
|
|
}
|
2019-11-02 02:13:45 +01:00
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>>
|
|
|
|
|
{
|
|
|
|
|
let matches = clap::App::new("Ask Dracula: Using Vampire as an interactive theorem prover")
|
2019-11-02 07:23:34 +01:00
|
|
|
|
.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")
|
2019-11-02 02:13:45 +01:00
|
|
|
|
.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)?;
|
2019-11-02 07:23:34 +01:00
|
|
|
|
let (_, mut project) = ask_dracula::parse::project(&file_content)
|
2019-11-02 02:13:45 +01:00
|
|
|
|
.map_err(|_| "couldn’t parse input file")?;
|
|
|
|
|
|
2019-11-02 07:23:34 +01:00
|
|
|
|
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))?;
|
2019-11-02 02:13:45 +01:00
|
|
|
|
|
2019-11-02 07:23:34 +01:00
|
|
|
|
// 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))?;
|
2019-11-02 02:13:45 +01:00
|
|
|
|
|
2019-11-02 07:23:34 +01:00
|
|
|
|
Ok(())
|
|
|
|
|
},
|
|
|
|
|
VampireResult::Satisfiable =>
|
|
|
|
|
{
|
|
|
|
|
println!("conjecture disproven");
|
|
|
|
|
Ok(())
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
2019-11-02 02:13:45 +01:00
|
|
|
|
}
|