2019-11-07 07:53:50 +01:00
|
|
|
|
enum ProofResult
|
2019-11-02 07:23:34 +01:00
|
|
|
|
{
|
2019-11-07 07:53:50 +01:00
|
|
|
|
ProofNotFound,
|
|
|
|
|
Refutation,
|
|
|
|
|
Satisfiable,
|
2019-11-05 19:44:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
fn reset_proof_results<'a>(project: &'a mut ask_dracula::Project)
|
2019-11-05 19:44:28 +01:00
|
|
|
|
{
|
|
|
|
|
for block in &mut project.blocks
|
2019-11-02 07:23:34 +01:00
|
|
|
|
{
|
2019-11-07 07:53:50 +01:00
|
|
|
|
match block
|
2019-11-02 07:23:34 +01:00
|
|
|
|
{
|
2019-11-07 07:53:50 +01:00
|
|
|
|
ask_dracula::project::Block::Whitespace(_) => (),
|
2019-11-07 08:12:26 +01:00
|
|
|
|
ask_dracula::project::Block::FormulaStatement(ref mut formula_statement) => formula_statement.proven = false,
|
2019-11-02 07:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn run_vampire<I, S>(input: &str, arguments: Option<I>)
|
2019-11-07 02:05:15 +01:00
|
|
|
|
-> Result<(ProofResult, Option<f32>), ask_dracula::Error>
|
2019-11-02 07:23:34 +01:00
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2019-11-05 19:44:28 +01:00
|
|
|
|
//eprintln!("{}", input);
|
|
|
|
|
|
2019-11-02 07:23:34 +01:00
|
|
|
|
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)
|
|
|
|
|
{
|
2019-11-07 02:05:15 +01:00
|
|
|
|
return Ok((ProofResult::ProofNotFound, None));
|
2019-11-02 07:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Err(ask_dracula::Error::new_run_vampire(
|
2019-11-02 07:54:39 +01:00
|
|
|
|
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())));
|
2019-11-02 07:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-07 02:05:15 +01:00
|
|
|
|
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);
|
|
|
|
|
|
2019-11-02 07:23:34 +01:00
|
|
|
|
let refutation_regex = regex::Regex::new(r"% \(\d+\)Termination reason: Refutation").unwrap();
|
|
|
|
|
|
|
|
|
|
if refutation_regex.is_match(stdout)
|
|
|
|
|
{
|
2019-11-07 02:05:15 +01:00
|
|
|
|
return Ok((ProofResult::Refutation, proof_time));
|
2019-11-02 07:23:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
2019-11-06 19:52:08 +01:00
|
|
|
|
.arg(clap::Arg::with_name("proof-direction").long("proof-direction").takes_value(true).default_value("forward"))
|
2019-11-02 07:23:34 +01:00
|
|
|
|
.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-06 19:52:08 +01:00
|
|
|
|
let proof_directions = match matches.value_of("proof-direction").unwrap()
|
2019-11-02 07:23:34 +01:00
|
|
|
|
{
|
2019-11-07 05:42:42 +01:00
|
|
|
|
"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],
|
2019-11-06 19:52:08 +01:00
|
|
|
|
proof_direction => panic!("unrecognized proof direction “{}”", proof_direction),
|
|
|
|
|
};
|
2019-11-06 11:36:51 +01:00
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
let mut section_separator = "";
|
|
|
|
|
let mut was_proof_attempted = false;
|
|
|
|
|
|
2019-11-06 19:52:08 +01:00
|
|
|
|
for proof_direction in proof_directions
|
|
|
|
|
{
|
2019-11-07 07:53:50 +01:00
|
|
|
|
println!("{}performing {} proof", section_separator, proof_direction);
|
|
|
|
|
|
|
|
|
|
section_separator = "\n";
|
|
|
|
|
|
|
|
|
|
reset_proof_results(&mut project);
|
|
|
|
|
|
2019-11-06 19:52:08 +01:00
|
|
|
|
loop
|
2019-11-05 19:44:28 +01:00
|
|
|
|
{
|
2019-11-07 07:53:50 +01:00
|
|
|
|
let conjecture = project.blocks.iter()
|
|
|
|
|
.filter_map
|
|
|
|
|
(
|
|
|
|
|
|block|
|
|
|
|
|
match block
|
|
|
|
|
{
|
|
|
|
|
ask_dracula::project::Block::Whitespace(_) => None,
|
2019-11-07 08:12:26 +01:00
|
|
|
|
ask_dracula::project::Block::FormulaStatement(ref formula_statement) => Some(formula_statement),
|
2019-11-07 07:53:50 +01:00
|
|
|
|
}
|
|
|
|
|
)
|
2019-11-07 08:12:26 +01:00
|
|
|
|
.find(|formula_statement| ask_dracula::format_tptp::is_formula_statement_lemma(&formula_statement, proof_direction));
|
2019-11-05 19:44:28 +01:00
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
let conjecture = match conjecture
|
2019-11-06 11:36:51 +01:00
|
|
|
|
{
|
2019-11-07 07:53:50 +01:00
|
|
|
|
Some(conjecture) => Some(conjecture),
|
|
|
|
|
None => project.blocks.iter()
|
|
|
|
|
.filter_map
|
|
|
|
|
(
|
|
|
|
|
|block|
|
|
|
|
|
match block
|
|
|
|
|
{
|
|
|
|
|
ask_dracula::project::Block::Whitespace(_) => None,
|
2019-11-07 08:12:26 +01:00
|
|
|
|
ask_dracula::project::Block::FormulaStatement(ref formula_statement) => Some(formula_statement),
|
2019-11-07 07:53:50 +01:00
|
|
|
|
}
|
|
|
|
|
)
|
2019-11-07 08:12:26 +01:00
|
|
|
|
.find(|formula_statement| ask_dracula::format_tptp::is_formula_statement_theorem(&formula_statement, proof_direction)),
|
2019-11-07 07:53:50 +01:00
|
|
|
|
};
|
2019-11-05 19:44:28 +01:00
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
let conjecture = match conjecture
|
|
|
|
|
{
|
|
|
|
|
Some(ref conjecture) => conjecture,
|
|
|
|
|
None => break,
|
2019-11-06 19:52:08 +01:00
|
|
|
|
};
|
2019-11-05 19:44:28 +01:00
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
was_proof_attempted = true;
|
|
|
|
|
|
|
|
|
|
eprintln!("verifying {}: {}", &conjecture.kind, conjecture.formula);
|
|
|
|
|
|
|
|
|
|
let tptp_content = format!("{}", ask_dracula::format_tptp::display_project_with_conjecture_tptp(&project, conjecture, proof_direction));
|
|
|
|
|
|
|
|
|
|
let (vampire_result, proof_time)
|
|
|
|
|
= run_vampire(&tptp_content, matches.values_of("vampire_arguments").map(|value| value))?;
|
2019-11-05 19:44:28 +01:00
|
|
|
|
|
2019-11-06 19:52:08 +01:00
|
|
|
|
match vampire_result
|
2019-11-06 11:36:51 +01:00
|
|
|
|
{
|
2019-11-06 19:52:08 +01:00
|
|
|
|
ProofResult::ProofNotFound =>
|
|
|
|
|
{
|
|
|
|
|
println!("proof not found");
|
|
|
|
|
return Ok(());
|
|
|
|
|
},
|
|
|
|
|
ProofResult::Refutation =>
|
|
|
|
|
{
|
2019-11-07 02:05:15 +01:00
|
|
|
|
if let Some(proof_time) = proof_time
|
|
|
|
|
{
|
2019-11-07 05:43:22 +01:00
|
|
|
|
println!("{} proven in {} seconds", &conjecture.kind, proof_time);
|
2019-11-07 02:05:15 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
println!("assertion proven");
|
|
|
|
|
}
|
2019-11-06 19:52:08 +01:00
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
// TODO: refactor
|
|
|
|
|
let conjecture = project.blocks.iter_mut()
|
|
|
|
|
.filter_map
|
|
|
|
|
(
|
|
|
|
|
|block|
|
|
|
|
|
match block
|
|
|
|
|
{
|
|
|
|
|
ask_dracula::project::Block::Whitespace(_) => None,
|
2019-11-07 08:12:26 +01:00
|
|
|
|
ask_dracula::project::Block::FormulaStatement(ref mut formula_statement) => Some(formula_statement),
|
2019-11-07 07:53:50 +01:00
|
|
|
|
}
|
|
|
|
|
)
|
2019-11-07 08:12:26 +01:00
|
|
|
|
.find(|formula_statement| ask_dracula::format_tptp::is_formula_statement_lemma(&formula_statement, proof_direction));
|
2019-11-07 07:53:50 +01:00
|
|
|
|
|
|
|
|
|
let mut conjecture = match conjecture
|
|
|
|
|
{
|
|
|
|
|
Some(conjecture) => Some(conjecture),
|
|
|
|
|
None => project.blocks.iter_mut()
|
|
|
|
|
.filter_map
|
|
|
|
|
(
|
|
|
|
|
|block|
|
|
|
|
|
match block
|
|
|
|
|
{
|
|
|
|
|
ask_dracula::project::Block::Whitespace(_) => None,
|
2019-11-07 08:12:26 +01:00
|
|
|
|
ask_dracula::project::Block::FormulaStatement(ref mut formula_statement) => Some(formula_statement),
|
2019-11-07 07:53:50 +01:00
|
|
|
|
}
|
|
|
|
|
)
|
2019-11-07 08:12:26 +01:00
|
|
|
|
.find(|formula_statement| ask_dracula::format_tptp::is_formula_statement_theorem(&formula_statement, proof_direction)),
|
2019-11-07 07:53:50 +01:00
|
|
|
|
}.unwrap();
|
|
|
|
|
|
|
|
|
|
conjecture.proven = true;
|
2019-11-06 19:52:08 +01:00
|
|
|
|
},
|
|
|
|
|
ProofResult::Satisfiable =>
|
|
|
|
|
{
|
|
|
|
|
println!("assertion disproven");
|
|
|
|
|
return Ok(());
|
|
|
|
|
},
|
|
|
|
|
}
|
2019-11-06 11:36:51 +01:00
|
|
|
|
}
|
2019-11-02 07:23:34 +01:00
|
|
|
|
}
|
2019-11-06 19:52:08 +01:00
|
|
|
|
|
2019-11-07 07:53:50 +01:00
|
|
|
|
match was_proof_attempted
|
|
|
|
|
{
|
|
|
|
|
true => println!("finished all proofs"),
|
|
|
|
|
false => println!("nothing to verify, exiting"),
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-06 19:52:08 +01:00
|
|
|
|
Ok(())
|
2019-11-02 02:13:45 +01:00
|
|
|
|
}
|