ask-dracula-rs/src/main.rs

192 lines
5.6 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 find_conjecture<'a>(project: &'a ask_dracula::Project,
proof_direction: ask_dracula::project::ProofDirection)
-> Option<&'a ask_dracula::project::Statement>
{
for block in &project.blocks
{
if let ask_dracula::project::Block::Statement(ref statement) = block
{
if ask_dracula::format_tptp::is_statement_kind_conjecture(&statement.kind, proof_direction)
{
return Some(statement)
}
}
}
None
}
fn find_conjecture_mut<'a>(project: &'a mut ask_dracula::Project,
proof_direction: ask_dracula::project::ProofDirection)
-> Option<&'a mut ask_dracula::project::Statement>
{
for block in &mut project.blocks
{
if let ask_dracula::project::Block::Statement(ref mut statement) = block
{
if ask_dracula::format_tptp::is_statement_kind_conjecture(&statement.kind, proof_direction)
{
return Some(statement)
}
}
}
None
}
enum ProofResult
{
ProofNotFound,
Refutation,
Satisfiable,
}
fn run_vampire<I, S>(input: &str, arguments: Option<I>)
-> Result<(ProofResult, Option<f32>), 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, None));
}
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 proof_time_regex = regex::Regex::new(r"% \(\d+\)Success in time (\d+(?:\.\d+)?) s").unwrap();
let proof_time = proof_time_regex.captures(stdout)
.map(|captures| captures.get(1).unwrap().as_str().parse::<f32>().ok())
.unwrap_or(None);
let refutation_regex = regex::Regex::new(r"% \(\d+\)Termination reason: Refutation").unwrap();
if refutation_regex.is_match(stdout)
{
return Ok((ProofResult::Refutation, proof_time));
}
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("proof-direction").long("proof-direction").takes_value(true).default_value("forward"))
.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 proof_directions = match matches.value_of("proof-direction").unwrap()
{
"forward" => vec![ask_dracula::project::ProofDirection::Forward],
"backward" => vec![ask_dracula::project::ProofDirection::Backward],
"both" => vec![ask_dracula::project::ProofDirection::Forward, ask_dracula::project::ProofDirection::Backward],
proof_direction => panic!("unrecognized proof direction “{}", proof_direction),
};
for proof_direction in proof_directions
{
loop
{
let conjecture = find_conjecture(&project, proof_direction);
let (vampire_result, proof_time) = match conjecture
{
None =>
{
eprintln!("nothing to prove, exiting");
return Ok(());
},
Some(ref conjecture) =>
{
eprintln!("verifying {}: {}", &conjecture.kind, conjecture.formula);
let tptp_content = format!("{}", ask_dracula::format_tptp::display_project_with_conjecture_tptp(&project, conjecture, proof_direction));
run_vampire(&tptp_content, matches.values_of("vampire_arguments").map(|value| value))?
},
};
let conjecture = find_conjecture_mut(&mut project, proof_direction).unwrap();
match vampire_result
{
ProofResult::ProofNotFound =>
{
println!("proof not found");
return Ok(());
},
ProofResult::Refutation =>
{
if let Some(proof_time) = proof_time
{
println!("{} proven in {} seconds", &conjecture.kind, proof_time);
}
else
{
println!("assertion proven");
}
conjecture.kind = ask_dracula::project::StatementKind::Axiom;
conjecture.original_text = replace_statement_kind_regex.replace(&conjecture.original_text, "axiom").to_string();
},
ProofResult::Satisfiable =>
{
println!("assertion disproven");
return Ok(());
},
}
}
}
Ok(())
}