Work in progress
This commit is contained in:
parent
07322f041c
commit
e118442e16
@ -5,7 +5,7 @@ authors = ["Patrick Lühne <patrick@luehne.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
foliage = {git = "https://github.com/pluehne/foliage", branch = "parser"}
|
||||
foliage = {git = "https://github.com/pluehne/foliage", branch = "parser-new"}
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
structopt = "0.3"
|
||||
|
1
src/commands.rs
Normal file
1
src/commands.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod verify_specification;
|
49
src/commands/verify_specification.rs
Normal file
49
src/commands/verify_specification.rs
Normal file
@ -0,0 +1,49 @@
|
||||
pub fn run<P>(program_path: P, specification_path: P, output_format: crate::output::Format)
|
||||
where
|
||||
P: AsRef<std::path::Path>
|
||||
{
|
||||
//let context = crate::translate::verify_properties::Context::new();
|
||||
let mut problem = crate::Problem::new();
|
||||
|
||||
log::info!("reading specification “{}”", specification_path.as_ref().display());
|
||||
|
||||
let specification_content = match std::fs::read_to_string(specification_path.as_ref())
|
||||
{
|
||||
Ok(specification_content) => specification_content,
|
||||
Err(error) =>
|
||||
{
|
||||
log::error!("could not access specification file: {}", error);
|
||||
std::process::exit(1)
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: rename to read_specification
|
||||
match crate::input::parse_specification(&specification_content, &mut problem)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(error) =>
|
||||
{
|
||||
log::error!("could not read specification: {}", error);
|
||||
std::process::exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("read specification “{}”", specification_path.as_ref().display());
|
||||
|
||||
log::info!("reading input program “{}”", program_path.as_ref().display());
|
||||
|
||||
// TODO: make consistent with specification call (path vs. content)
|
||||
match crate::translate::verify_properties::Translator::new(&mut problem).translate(program_path)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(error) =>
|
||||
{
|
||||
log::error!("could not translate input program: {}", error);
|
||||
std::process::exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
problem.prove(crate::ProofDirection::Both);
|
||||
|
||||
log::info!("done");
|
||||
}
|
83
src/error.rs
83
src/error.rs
@ -8,8 +8,18 @@ pub enum Kind
|
||||
DecodeIdentifier,
|
||||
Translate,
|
||||
ReadFile(std::path::PathBuf),
|
||||
ExpectedStatement,
|
||||
ExpectedColon,
|
||||
UnknownStatement(String),
|
||||
UnmatchedParenthesis,
|
||||
MissingStatementTerminator,
|
||||
ParseFormula,
|
||||
ExpectedIdentifier,
|
||||
ParsePredicateDeclaration,
|
||||
ParseConstantDeclaration,
|
||||
//ParseConstantDeclaration,
|
||||
UnknownProofDirection(String),
|
||||
UnknownDomainIdentifier(String),
|
||||
VariableNameNotAllowed(String),
|
||||
}
|
||||
|
||||
pub struct Error
|
||||
@ -65,14 +75,59 @@ impl Error
|
||||
Self::new(Kind::ReadFile(path)).with(source)
|
||||
}
|
||||
|
||||
pub(crate) fn new_expected_statement() -> Self
|
||||
{
|
||||
Self::new(Kind::ExpectedStatement)
|
||||
}
|
||||
|
||||
pub(crate) fn new_expected_colon() -> Self
|
||||
{
|
||||
Self::new(Kind::ExpectedColon)
|
||||
}
|
||||
|
||||
pub(crate) fn new_unknown_statement(statement_name: String) -> Self
|
||||
{
|
||||
Self::new(Kind::UnknownStatement(statement_name))
|
||||
}
|
||||
|
||||
pub(crate) fn new_unmatched_parenthesis() -> Self
|
||||
{
|
||||
Self::new(Kind::UnmatchedParenthesis)
|
||||
}
|
||||
|
||||
pub(crate) fn new_missing_statement_terminator() -> Self
|
||||
{
|
||||
Self::new(Kind::MissingStatementTerminator)
|
||||
}
|
||||
|
||||
pub(crate) fn new_parse_formula<S: Into<Source>>(source: S) -> Self
|
||||
{
|
||||
Self::new(Kind::ParseFormula).with(source)
|
||||
}
|
||||
|
||||
pub(crate) fn new_expected_identifier() -> Self
|
||||
{
|
||||
Self::new(Kind::ExpectedIdentifier)
|
||||
}
|
||||
|
||||
pub(crate) fn new_parse_predicate_declaration() -> Self
|
||||
{
|
||||
Self::new(Kind::ParsePredicateDeclaration)
|
||||
}
|
||||
|
||||
pub(crate) fn new_parse_constant_declaration() -> Self
|
||||
pub(crate) fn new_unknown_proof_direction(proof_direction: String) -> Self
|
||||
{
|
||||
Self::new(Kind::ParseConstantDeclaration)
|
||||
Self::new(Kind::UnknownProofDirection(proof_direction))
|
||||
}
|
||||
|
||||
pub(crate) fn new_unknown_domain_identifier(domain_identifier: String) -> Self
|
||||
{
|
||||
Self::new(Kind::UnknownDomainIdentifier(domain_identifier))
|
||||
}
|
||||
|
||||
pub(crate) fn new_variable_name_not_allowed(variable_name: String) -> Self
|
||||
{
|
||||
Self::new(Kind::VariableNameNotAllowed(variable_name))
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,10 +146,28 @@ impl std::fmt::Debug for Error
|
||||
Kind::DecodeIdentifier => write!(formatter, "could not decode identifier"),
|
||||
Kind::Translate => write!(formatter, "could not translate input program"),
|
||||
Kind::ReadFile(path) => write!(formatter, "could not read file “{}”", path.display()),
|
||||
Kind::ExpectedStatement => write!(formatter,
|
||||
"expected statement (axiom, assert, assume, input, lemma)"),
|
||||
Kind::ExpectedColon => write!(formatter, "expected ‘:’"),
|
||||
Kind::UnknownStatement(ref statement_name) => write!(formatter,
|
||||
"unknown statement “{}” (allowed: axiom, assert, assume, input, lemma)",
|
||||
statement_name),
|
||||
Kind::UnmatchedParenthesis => write!(formatter, "unmatched parenthesis"),
|
||||
Kind::ParsePredicateDeclaration => write!(formatter,
|
||||
"could not parse predicate declaration"),
|
||||
Kind::ParseConstantDeclaration => write!(formatter,
|
||||
"could not parse constant declaration"),
|
||||
Kind::ParseFormula => write!(formatter, "could not parse formula"),
|
||||
Kind::ExpectedIdentifier => write!(formatter, "expected constant or predicate name"),
|
||||
Kind::ParsePredicateDeclaration => write!(formatter,
|
||||
"could not parse predicate declaration"),
|
||||
Kind::MissingStatementTerminator => write!(formatter,
|
||||
"statement not terminated with ‘.’ character"),
|
||||
Kind::UnknownProofDirection(ref proof_direction) => write!(formatter,
|
||||
"unknown proof direction “{}” (allowed: integer, program)", proof_direction),
|
||||
Kind::UnknownDomainIdentifier(ref domain_identifier) => write!(formatter,
|
||||
"unknown domain identifier “{}” (allowed: int, program)", domain_identifier),
|
||||
Kind::VariableNameNotAllowed(ref variable_name) => write!(formatter,
|
||||
"variable name “{}” not allowed (program variables must start with X, Y, or Z and integer variables with I, J, K, L, M, or N)",
|
||||
variable_name),
|
||||
}?;
|
||||
|
||||
if let Some(source) = &self.source
|
||||
|
3
src/input.rs
Normal file
3
src/input.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub(crate) mod specification;
|
||||
|
||||
pub(crate) use specification::parse_specification;
|
404
src/input/specification.rs
Normal file
404
src/input/specification.rs
Normal file
@ -0,0 +1,404 @@
|
||||
// TODO: refactor
|
||||
fn term_assign_variable_declaration_domains<D>(term: &foliage::Term, declarations: &D)
|
||||
-> Result<(), crate::Error>
|
||||
where
|
||||
D: crate::traits::AssignVariableDeclarationDomain,
|
||||
{
|
||||
match term
|
||||
{
|
||||
foliage::Term::BinaryOperation(binary_operation) =>
|
||||
{
|
||||
term_assign_variable_declaration_domains(&binary_operation.left, declarations)?;
|
||||
term_assign_variable_declaration_domains(&binary_operation.right, declarations)?;
|
||||
},
|
||||
foliage::Term::Function(function) =>
|
||||
for argument in &function.arguments
|
||||
{
|
||||
term_assign_variable_declaration_domains(&argument, declarations)?;
|
||||
},
|
||||
foliage::Term::UnaryOperation(unary_operation) =>
|
||||
term_assign_variable_declaration_domains(&unary_operation.argument, declarations)?,
|
||||
foliage::Term::Variable(variable) =>
|
||||
{
|
||||
let domain = match variable.declaration.name.chars().next()
|
||||
{
|
||||
Some('X')
|
||||
| Some('Y')
|
||||
| Some('Z') => crate::Domain::Program,
|
||||
Some('I')
|
||||
| Some('J')
|
||||
| Some('K')
|
||||
| Some('L')
|
||||
| Some('M')
|
||||
| Some('N') => crate::Domain::Integer,
|
||||
// TODO: improve error handling
|
||||
Some(other) => return Err(
|
||||
crate::Error::new_variable_name_not_allowed(variable.declaration.name.clone())),
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
||||
declarations.assign_variable_declaration_domain(&variable.declaration, domain);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn formula_assign_variable_declaration_domains<D>(formula: &foliage::Formula, declarations: &D)
|
||||
-> Result<(), crate::Error>
|
||||
where
|
||||
D: crate::traits::AssignVariableDeclarationDomain,
|
||||
{
|
||||
match formula
|
||||
{
|
||||
foliage::Formula::And(arguments)
|
||||
| foliage::Formula::Or(arguments)
|
||||
| foliage::Formula::IfAndOnlyIf(arguments) =>
|
||||
for argument in arguments
|
||||
{
|
||||
formula_assign_variable_declaration_domains(&argument, declarations)?;
|
||||
},
|
||||
foliage::Formula::Compare(compare) =>
|
||||
{
|
||||
term_assign_variable_declaration_domains(&compare.left, declarations)?;
|
||||
term_assign_variable_declaration_domains(&compare.right, declarations)?;
|
||||
},
|
||||
foliage::Formula::Exists(quantified_formula)
|
||||
| foliage::Formula::ForAll(quantified_formula) =>
|
||||
formula_assign_variable_declaration_domains(&quantified_formula.argument,
|
||||
declarations)?,
|
||||
foliage::Formula::Implies(implies) =>
|
||||
{
|
||||
formula_assign_variable_declaration_domains(&implies.antecedent, declarations)?;
|
||||
formula_assign_variable_declaration_domains(&implies.implication, declarations)?;
|
||||
}
|
||||
foliage::Formula::Not(argument) =>
|
||||
formula_assign_variable_declaration_domains(&argument, declarations)?,
|
||||
foliage::Formula::Predicate(predicate) =>
|
||||
for argument in &predicate.arguments
|
||||
{
|
||||
term_assign_variable_declaration_domains(&argument, declarations)?;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn closed_formula<'i, D>(input: &'i str, declarations: &D)
|
||||
-> Result<(crate::ScopedFormula, &'i str), crate::Error>
|
||||
where
|
||||
D: foliage::FindOrCreateFunctionDeclaration
|
||||
+ foliage::FindOrCreatePredicateDeclaration
|
||||
+ crate::traits::AssignVariableDeclarationDomain,
|
||||
{
|
||||
let terminator_position = match input.find('.')
|
||||
{
|
||||
None => return Err(crate::Error::new_missing_statement_terminator()),
|
||||
Some(terminator_position) => terminator_position,
|
||||
};
|
||||
|
||||
let (formula_input, remaining_input) = input.split_at(terminator_position);
|
||||
let mut remaining_input_characters = remaining_input.chars();
|
||||
remaining_input_characters.next();
|
||||
let remaining_input = remaining_input_characters.as_str();
|
||||
|
||||
let closed_formula = foliage::parse::formula(formula_input, declarations)
|
||||
.map_err(|error| crate::Error::new_parse_formula(error))?;
|
||||
|
||||
formula_assign_variable_declaration_domains(&closed_formula.formula, declarations)?;
|
||||
|
||||
// TODO: get rid of ScopedFormula
|
||||
let scoped_formula = crate::ScopedFormula
|
||||
{
|
||||
free_variable_declarations: closed_formula.free_variable_declarations,
|
||||
formula: closed_formula.formula,
|
||||
};
|
||||
|
||||
Ok((scoped_formula, remaining_input))
|
||||
}
|
||||
|
||||
fn variable_free_formula<'i, D>(input: &'i str, declarations: &D)
|
||||
-> Result<(foliage::Formula, &'i str), crate::Error>
|
||||
where
|
||||
D: foliage::FindOrCreateFunctionDeclaration
|
||||
+ foliage::FindOrCreatePredicateDeclaration
|
||||
+ crate::traits::AssignVariableDeclarationDomain,
|
||||
{
|
||||
let (closed_formula, input) = closed_formula(input, declarations)?;
|
||||
|
||||
if !closed_formula.free_variable_declarations.is_empty()
|
||||
{
|
||||
// TODO: improve
|
||||
panic!("formula may not contain free variables");
|
||||
}
|
||||
|
||||
Ok((closed_formula.formula, input))
|
||||
}
|
||||
|
||||
fn formula_statement_body<'i>(input: &'i str, problem: &crate::Problem)
|
||||
-> Result<(foliage::Formula, &'i str), crate::Error>
|
||||
{
|
||||
let input = input.trim_start();
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
let remaining_input = match input_characters.next()
|
||||
{
|
||||
Some(':') => input_characters.as_str(),
|
||||
_ => return Err(crate::Error::new_expected_colon()),
|
||||
};
|
||||
|
||||
let input = remaining_input;
|
||||
|
||||
variable_free_formula(input, problem)
|
||||
}
|
||||
|
||||
fn input_statement_body<'i>(mut input: &'i str, problem: &crate::Problem)
|
||||
-> Result<&'i str, crate::Error>
|
||||
{
|
||||
input = input.trim_start();
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
let remaining_input = match input_characters.next()
|
||||
{
|
||||
Some(':') => input_characters.as_str(),
|
||||
_ => return Err(crate::Error::new_expected_colon()),
|
||||
};
|
||||
|
||||
input = remaining_input;
|
||||
|
||||
loop
|
||||
{
|
||||
input = input.trim_start();
|
||||
|
||||
let (constant_or_predicate_name, remaining_input) =
|
||||
foliage::parse::tokens::identifier(input)
|
||||
.ok_or_else(|| crate::Error::new_expected_identifier())?;
|
||||
|
||||
input = remaining_input.trim_start();
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
match input_characters.next()
|
||||
{
|
||||
// Parse input predicate specifiers
|
||||
Some('/') =>
|
||||
{
|
||||
input = input_characters.as_str().trim_start();
|
||||
|
||||
let (arity, remaining_input) = foliage::parse::tokens::number(input)
|
||||
.map_err(|error| crate::Error::new_parse_predicate_declaration().with(error))?
|
||||
.ok_or_else(|| crate::Error::new_parse_predicate_declaration())?;
|
||||
|
||||
input = remaining_input.trim_start();
|
||||
|
||||
let mut input_predicate_declarations =
|
||||
problem.input_predicate_declarations.borrow_mut();
|
||||
|
||||
use foliage::FindOrCreatePredicateDeclaration;
|
||||
|
||||
let predicate_declaration =
|
||||
problem.find_or_create_predicate_declaration(constant_or_predicate_name, arity);
|
||||
|
||||
input_predicate_declarations.insert(predicate_declaration);
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
match input_characters.next()
|
||||
{
|
||||
Some(',') => input = input_characters.as_str(),
|
||||
_ => break,
|
||||
}
|
||||
},
|
||||
// Parse input constant specifiers
|
||||
Some(_)
|
||||
| None =>
|
||||
{
|
||||
let domain =
|
||||
if input.starts_with("->")
|
||||
{
|
||||
let mut input_characters = input.chars();
|
||||
input_characters.next();
|
||||
input_characters.next();
|
||||
|
||||
input = input_characters.as_str().trim_start();
|
||||
|
||||
let (identifier, remaining_input) =
|
||||
foliage::parse::tokens::identifier(input)
|
||||
.ok_or_else(|| crate::Error::new_expected_identifier())?;
|
||||
|
||||
input = remaining_input;
|
||||
|
||||
match identifier
|
||||
{
|
||||
"integer" => crate::Domain::Integer,
|
||||
"program" => crate::Domain::Program,
|
||||
_ => return Err(crate::Error::new_unknown_domain_identifier(
|
||||
identifier.to_string())),
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
crate::Domain::Program
|
||||
};
|
||||
|
||||
let mut input_constant_declarations =
|
||||
problem.input_constant_declarations.borrow_mut();
|
||||
|
||||
use foliage::FindOrCreateFunctionDeclaration;
|
||||
|
||||
let constant_declaration =
|
||||
problem.find_or_create_function_declaration(constant_or_predicate_name, 0);
|
||||
|
||||
input_constant_declarations.insert(constant_declaration);
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
match input_characters.next()
|
||||
{
|
||||
Some(',') => input = input_characters.as_str(),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input = input.trim_start();
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
if input_characters.next() != Some('.')
|
||||
{
|
||||
return Err(crate::Error::new_missing_statement_terminator())
|
||||
}
|
||||
|
||||
input = input_characters.as_str();
|
||||
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_specification(mut input: &str, problem: &crate::Problem)
|
||||
-> Result<(), crate::Error>
|
||||
{
|
||||
loop
|
||||
{
|
||||
input = input.trim_start();
|
||||
|
||||
if input.is_empty()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (identifier, remaining_input) = match foliage::parse::tokens::identifier(input)
|
||||
{
|
||||
Some(identifier) => identifier,
|
||||
None => return Err(crate::Error::new_expected_statement()),
|
||||
};
|
||||
|
||||
input = remaining_input;
|
||||
|
||||
match identifier
|
||||
{
|
||||
"axiom" =>
|
||||
{
|
||||
let (formula, remaining_input) = formula_statement_body(input, problem)?;
|
||||
input = remaining_input;
|
||||
|
||||
let statement = crate::problem::Statement::new(
|
||||
crate::problem::StatementKind::Axiom, formula);
|
||||
|
||||
problem.add_statement(crate::problem::SectionKind::Axioms, statement);
|
||||
|
||||
continue;
|
||||
},
|
||||
"assume" =>
|
||||
{
|
||||
let (formula, remaining_input) = formula_statement_body(input, problem)?;
|
||||
input = remaining_input;
|
||||
|
||||
let statement = crate::problem::Statement::new(
|
||||
crate::problem::StatementKind::Assumption, formula);
|
||||
|
||||
problem.add_statement(crate::problem::SectionKind::Assumptions, statement);
|
||||
|
||||
continue;
|
||||
},
|
||||
"lemma" =>
|
||||
{
|
||||
input = input.trim_start();
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
let (proof_direction, remaining_input) = match input_characters.next()
|
||||
{
|
||||
Some('(') =>
|
||||
{
|
||||
// TODO: refactor
|
||||
input = input_characters.as_str().trim_start();
|
||||
|
||||
let (proof_direction, remaining_input) = match
|
||||
foliage::parse::tokens::identifier(input)
|
||||
{
|
||||
Some(("forward", remaining_input)) =>
|
||||
(crate::ProofDirection::Forward, remaining_input),
|
||||
Some(("backward", remaining_input)) =>
|
||||
(crate::ProofDirection::Backward, remaining_input),
|
||||
Some(("both", remaining_input)) =>
|
||||
(crate::ProofDirection::Both, remaining_input),
|
||||
Some((identifier, _)) =>
|
||||
return Err(crate::Error::new_unknown_proof_direction(
|
||||
identifier.to_string())),
|
||||
None => (crate::ProofDirection::Both, input),
|
||||
};
|
||||
|
||||
input = remaining_input.trim_start();
|
||||
|
||||
let mut input_characters = input.chars();
|
||||
|
||||
if input_characters.next() != Some(')')
|
||||
{
|
||||
return Err(crate::Error::new_unmatched_parenthesis());
|
||||
}
|
||||
|
||||
input = input_characters.as_str();
|
||||
|
||||
(proof_direction, input)
|
||||
},
|
||||
Some(_)
|
||||
| None => (crate::ProofDirection::Both, remaining_input),
|
||||
};
|
||||
|
||||
input = remaining_input;
|
||||
|
||||
let (formula, remaining_input) = formula_statement_body(input, problem)?;
|
||||
|
||||
input = remaining_input;
|
||||
|
||||
let statement = crate::problem::Statement::new(
|
||||
crate::problem::StatementKind::Lemma(proof_direction), formula);
|
||||
|
||||
problem.add_statement(crate::problem::SectionKind::Lemmas, statement);
|
||||
|
||||
continue;
|
||||
},
|
||||
"assert" =>
|
||||
{
|
||||
let (formula, remaining_input) = formula_statement_body(input, problem)?;
|
||||
|
||||
input = remaining_input;
|
||||
|
||||
let statement = crate::problem::Statement::new(
|
||||
crate::problem::StatementKind::Assertion, formula);
|
||||
|
||||
problem.add_statement(crate::problem::SectionKind::Assertions, statement);
|
||||
|
||||
continue;
|
||||
},
|
||||
"input" => input = input_statement_body(input, problem)?,
|
||||
identifier => return Err(crate::Error::new_unknown_statement(identifier.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
#![feature(trait_alias)]
|
||||
|
||||
pub mod commands;
|
||||
pub mod error;
|
||||
pub mod input;
|
||||
pub mod output;
|
||||
pub mod problem;
|
||||
pub(crate) mod traits;
|
||||
pub mod translate;
|
||||
mod utils;
|
||||
|
||||
pub use error::Error;
|
||||
pub use problem::Problem;
|
||||
pub(crate) use utils::*;
|
||||
pub use utils::{Domain, InputConstantDeclarationDomains, parse_predicate_declaration,
|
||||
parse_constant_declaration};
|
||||
pub use utils::{Domain, InputConstantDeclarationDomains};
|
||||
|
39
src/main.rs
39
src/main.rs
@ -5,25 +5,20 @@ use structopt::StructOpt as _;
|
||||
enum Command
|
||||
{
|
||||
#[structopt(about = "Verifies a logic program against a specification")]
|
||||
#[structopt(aliases = &["verify-specification", "verify-spec", "vspec"])]
|
||||
#[structopt(aliases = &["vprog"])]
|
||||
VerifyProgram
|
||||
{
|
||||
/// ASP input program (one or multiple files)
|
||||
#[structopt(parse(from_os_str), required(true))]
|
||||
input: Vec<std::path::PathBuf>,
|
||||
/// ASP input program file path
|
||||
#[structopt(name = "program", parse(from_os_str), required(true))]
|
||||
program_path: std::path::PathBuf,
|
||||
|
||||
#[structopt(name = "specification", parse(from_os_str), required(true))]
|
||||
/// Specification file path
|
||||
specification_path: std::path::PathBuf,
|
||||
|
||||
/// Output format (human-readable, tptp)
|
||||
#[structopt(long, default_value = "human-readable")]
|
||||
output_format: anthem::output::Format,
|
||||
|
||||
/// Input predicates (examples: p, q/2)
|
||||
#[structopt(long, parse(try_from_str = anthem::parse_predicate_declaration))]
|
||||
input_predicates: Vec<std::rc::Rc<foliage::PredicateDeclaration>>,
|
||||
|
||||
/// Input constants (example: c, integer(n))
|
||||
#[structopt(long, parse(try_from_str = anthem::parse_constant_declaration))]
|
||||
input_constants: Vec<
|
||||
(std::rc::Rc<foliage::FunctionDeclaration>, anthem::Domain)>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,21 +32,11 @@ fn main()
|
||||
{
|
||||
Command::VerifyProgram
|
||||
{
|
||||
input,
|
||||
program_path,
|
||||
specification_path,
|
||||
output_format,
|
||||
input_predicates,
|
||||
input_constants,
|
||||
}
|
||||
=>
|
||||
{
|
||||
if let Err(error) = anthem::translate::verify_properties::translate(&input,
|
||||
input_predicates.into_iter().collect::<foliage::PredicateDeclarations>(),
|
||||
input_constants.into_iter().collect::<std::collections::BTreeMap<_, _>>(),
|
||||
output_format)
|
||||
{
|
||||
log::error!("could not translate input program: {}", error);
|
||||
std::process::exit(1)
|
||||
}
|
||||
},
|
||||
=> anthem::commands::verify_specification::run(&program_path, &specification_path,
|
||||
output_format),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub(crate) fn display_variable_declaration<C>(context: &C, formatter: &mut std::fmt::Formatter,
|
||||
/*pub(crate) fn display_variable_declaration<C>(context: &C, formatter: &mut std::fmt::Formatter,
|
||||
variable_declaration: &std::rc::Rc<foliage::VariableDeclaration>)
|
||||
-> std::fmt::Result
|
||||
where C:
|
||||
@ -15,4 +15,4 @@ where C:
|
||||
};
|
||||
|
||||
write!(formatter, "{}{}", prefix, id + 1)
|
||||
}
|
||||
}*/
|
||||
|
502
src/problem.rs
Normal file
502
src/problem.rs
Normal file
@ -0,0 +1,502 @@
|
||||
pub enum StatementKind
|
||||
{
|
||||
Axiom,
|
||||
Assumption,
|
||||
Lemma(crate::ProofDirection),
|
||||
Assertion,
|
||||
}
|
||||
|
||||
enum ProofStatus
|
||||
{
|
||||
AssumedProven,
|
||||
ToProve,
|
||||
}
|
||||
|
||||
pub enum ProofResult
|
||||
{
|
||||
Proven,
|
||||
NotProven,
|
||||
Disproven,
|
||||
}
|
||||
|
||||
pub struct Statement
|
||||
{
|
||||
kind: StatementKind,
|
||||
name: Option<String>,
|
||||
description: Option<String>,
|
||||
formula: foliage::Formula,
|
||||
proof_status: ProofStatus,
|
||||
}
|
||||
|
||||
type VariableDeclarationIDs
|
||||
= std::collections::BTreeMap::<std::rc::Rc<foliage::VariableDeclaration>, usize>;
|
||||
|
||||
struct FormatContext<'a, 'b>
|
||||
{
|
||||
pub program_variable_declaration_ids: std::cell::RefCell<VariableDeclarationIDs>,
|
||||
pub integer_variable_declaration_ids: std::cell::RefCell<VariableDeclarationIDs>,
|
||||
pub input_constant_declaration_domains: &'a crate::InputConstantDeclarationDomains,
|
||||
pub variable_declaration_domains: &'b VariableDeclarationDomains,
|
||||
}
|
||||
|
||||
impl Statement
|
||||
{
|
||||
pub fn new(kind: StatementKind, formula: foliage::Formula) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
kind,
|
||||
name: None,
|
||||
description: None,
|
||||
formula,
|
||||
proof_status: ProofStatus::ToProve,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_name(mut self, name: String) -> Self
|
||||
{
|
||||
self.name = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_description(mut self, description: String) -> Self
|
||||
{
|
||||
self.description = Some(description);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub enum SectionKind
|
||||
{
|
||||
CompletedDefinitions,
|
||||
IntegrityConstraints,
|
||||
Axioms,
|
||||
Assumptions,
|
||||
Lemmas,
|
||||
Assertions,
|
||||
}
|
||||
|
||||
type VariableDeclarationDomains
|
||||
= std::collections::BTreeMap<std::rc::Rc<foliage::VariableDeclaration>, crate::Domain>;
|
||||
|
||||
pub struct Problem
|
||||
{
|
||||
function_declarations: std::cell::RefCell<foliage::FunctionDeclarations>,
|
||||
pub predicate_declarations: std::cell::RefCell<foliage::PredicateDeclarations>,
|
||||
|
||||
statements: std::cell::RefCell<std::collections::BTreeMap<SectionKind, Vec<Statement>>>,
|
||||
|
||||
pub input_constant_declarations: std::cell::RefCell<foliage::FunctionDeclarations>,
|
||||
pub input_constant_declaration_domains:
|
||||
std::cell::RefCell<crate::InputConstantDeclarationDomains>,
|
||||
pub input_predicate_declarations: std::cell::RefCell<foliage::PredicateDeclarations>,
|
||||
// TODO: clean up as variable declarations are dropped
|
||||
variable_declaration_domains: std::cell::RefCell<VariableDeclarationDomains>,
|
||||
}
|
||||
|
||||
impl Problem
|
||||
{
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
function_declarations: std::cell::RefCell::new(foliage::FunctionDeclarations::new()),
|
||||
predicate_declarations: std::cell::RefCell::new(foliage::PredicateDeclarations::new()),
|
||||
|
||||
statements: std::cell::RefCell::new(std::collections::BTreeMap::new()),
|
||||
|
||||
input_constant_declarations:
|
||||
std::cell::RefCell::new(foliage::FunctionDeclarations::new()),
|
||||
input_constant_declaration_domains:
|
||||
std::cell::RefCell::new(crate::InputConstantDeclarationDomains::new()),
|
||||
input_predicate_declarations:
|
||||
std::cell::RefCell::new(foliage::PredicateDeclarations::new()),
|
||||
variable_declaration_domains:
|
||||
std::cell::RefCell::new(VariableDeclarationDomains::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_statement(&self, section_kind: SectionKind, statement: Statement)
|
||||
{
|
||||
let mut statements = self.statements.borrow_mut();
|
||||
let section = statements.entry(section_kind).or_insert(vec![]);
|
||||
|
||||
section.push(statement);
|
||||
}
|
||||
|
||||
pub fn prove(&self, proof_direction: crate::ProofDirection)
|
||||
{
|
||||
if proof_direction == crate::ProofDirection::Forward
|
||||
|| proof_direction == crate::ProofDirection::Both
|
||||
{
|
||||
log::info!("performing forward proof");
|
||||
|
||||
let mut statements = self.statements.borrow_mut();
|
||||
|
||||
// Initially reset all proof statuses
|
||||
for (_, statements) in statements.iter_mut()
|
||||
{
|
||||
for statement in statements.iter_mut()
|
||||
{
|
||||
match statement.kind
|
||||
{
|
||||
StatementKind::Axiom
|
||||
| StatementKind::Assumption
|
||||
=> statement.proof_status = ProofStatus::AssumedProven,
|
||||
_ => statement.proof_status = ProofStatus::ToProve,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(statements);
|
||||
|
||||
self.display(crate::output::Format::HumanReadable);
|
||||
|
||||
log::info!("finished forward proof");
|
||||
}
|
||||
}
|
||||
|
||||
fn display(&self, output_format: crate::output::Format)
|
||||
{
|
||||
let format_context = FormatContext
|
||||
{
|
||||
program_variable_declaration_ids:
|
||||
std::cell::RefCell::new(VariableDeclarationIDs::new()),
|
||||
integer_variable_declaration_ids:
|
||||
std::cell::RefCell::new(VariableDeclarationIDs::new()),
|
||||
input_constant_declaration_domains: &self.input_constant_declaration_domains.borrow(),
|
||||
variable_declaration_domains: &self.variable_declaration_domains.borrow(),
|
||||
};
|
||||
|
||||
let print_title = |title, section_separator|
|
||||
{
|
||||
print!("{}{}", section_separator, "%".repeat(72));
|
||||
print!("\n% {}", title);
|
||||
println!("\n{}", "%".repeat(72));
|
||||
};
|
||||
|
||||
let print_formula = |formula: &foliage::Formula|
|
||||
{
|
||||
match output_format
|
||||
{
|
||||
crate::output::Format::HumanReadable => print!("{}",
|
||||
foliage::format::display_formula(formula, &format_context)),
|
||||
crate::output::Format::TPTP => print!("{}",
|
||||
crate::output::tptp::display_formula(formula, &format_context)),
|
||||
}
|
||||
};
|
||||
|
||||
let mut section_separator = "";
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print_title("anthem types", section_separator);
|
||||
section_separator = "\n";
|
||||
|
||||
let tptp_preamble_anthem_types
|
||||
= include_str!("output/tptp/preamble_types.tptp").trim_end();
|
||||
println!("{}", tptp_preamble_anthem_types);
|
||||
|
||||
print_title("anthem axioms", section_separator);
|
||||
|
||||
let tptp_preamble_anthem_types
|
||||
= include_str!("output/tptp/preamble_axioms.tptp").trim_end();
|
||||
println!("{}", tptp_preamble_anthem_types);
|
||||
|
||||
if !self.predicate_declarations.borrow().is_empty()
|
||||
|| !self.function_declarations.borrow().is_empty()
|
||||
{
|
||||
print_title("types", section_separator);
|
||||
|
||||
if !self.predicate_declarations.borrow().is_empty()
|
||||
{
|
||||
println!("% predicate types")
|
||||
}
|
||||
|
||||
for predicate_declaration in self.predicate_declarations.borrow().iter()
|
||||
{
|
||||
println!("tff(type, type, {}).",
|
||||
crate::output::tptp::display_predicate_declaration(predicate_declaration));
|
||||
}
|
||||
|
||||
if !self.function_declarations.borrow().is_empty()
|
||||
{
|
||||
println!("% function types")
|
||||
}
|
||||
|
||||
for function_declaration in self.function_declarations.borrow().iter()
|
||||
{
|
||||
println!("tff(type, type, {}).",
|
||||
crate::output::tptp::display_function_declaration(function_declaration,
|
||||
&format_context));
|
||||
}
|
||||
}
|
||||
|
||||
let function_declarations = self.function_declarations.borrow();
|
||||
let symbolic_constants = function_declarations.iter().filter(
|
||||
|x| !self.input_constant_declaration_domains.borrow().contains_key(*x));
|
||||
|
||||
let mut last_symbolic_constant: Option<std::rc::Rc<foliage::FunctionDeclaration>> =
|
||||
None;
|
||||
|
||||
for (i, symbolic_constant) in symbolic_constants.enumerate()
|
||||
{
|
||||
// Order axioms are only necessary with two or more symbolic constants
|
||||
if i == 1
|
||||
{
|
||||
println!("% axioms for order of symbolic constants")
|
||||
}
|
||||
|
||||
if symbolic_constant.arity > 0
|
||||
{
|
||||
// TODO: refactor
|
||||
panic!("unexpected n-ary function declaration");
|
||||
}
|
||||
|
||||
if let Some(last_symbolic_constant) = last_symbolic_constant
|
||||
{
|
||||
println!("tff(symbolic_constant_order, axiom, p__less__({}, {})).",
|
||||
last_symbolic_constant.name, symbolic_constant.name);
|
||||
}
|
||||
|
||||
last_symbolic_constant = Some(std::rc::Rc::clone(symbolic_constant));
|
||||
}
|
||||
}
|
||||
|
||||
for (section_kind, statements) in self.statements.borrow().iter()
|
||||
{
|
||||
if statements.is_empty()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let title = match section_kind
|
||||
{
|
||||
SectionKind::CompletedDefinitions => "completed definitions",
|
||||
SectionKind::IntegrityConstraints => "integrity constraints",
|
||||
SectionKind::Axioms => "axioms",
|
||||
SectionKind::Assumptions => "assumptions",
|
||||
SectionKind::Lemmas => "lemmas",
|
||||
SectionKind::Assertions => "assertions",
|
||||
};
|
||||
|
||||
print_title(title, section_separator);
|
||||
section_separator = "\n";
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
for statement in statements.iter()
|
||||
{
|
||||
if let Some(ref description) = statement.description
|
||||
{
|
||||
println!("% {}", description);
|
||||
}
|
||||
|
||||
let name = match &statement.name
|
||||
{
|
||||
// TODO: refactor
|
||||
Some(name) => name.clone(),
|
||||
None =>
|
||||
{
|
||||
i += 1;
|
||||
format!("statement_{}", i)
|
||||
},
|
||||
};
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print!("tff({}, <TODO>, ", name);
|
||||
}
|
||||
|
||||
print_formula(&statement.formula);
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print!(").");
|
||||
}
|
||||
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl foliage::FindOrCreateFunctionDeclaration for Problem
|
||||
{
|
||||
fn find_or_create_function_declaration(&self, name: &str, arity: usize)
|
||||
-> std::rc::Rc<foliage::FunctionDeclaration>
|
||||
{
|
||||
let mut function_declarations = self.function_declarations.borrow_mut();
|
||||
|
||||
match function_declarations.iter().find(|x| x.name == name && x.arity == arity)
|
||||
{
|
||||
Some(declaration) => std::rc::Rc::clone(&declaration),
|
||||
None =>
|
||||
{
|
||||
let declaration = foliage::FunctionDeclaration
|
||||
{
|
||||
name: name.to_string(),
|
||||
arity,
|
||||
};
|
||||
let declaration = std::rc::Rc::new(declaration);
|
||||
|
||||
function_declarations.insert(std::rc::Rc::clone(&declaration));
|
||||
|
||||
log::debug!("new function declaration: {}/{}", name, arity);
|
||||
|
||||
declaration
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl foliage::FindOrCreatePredicateDeclaration for Problem
|
||||
{
|
||||
fn find_or_create_predicate_declaration(&self, name: &str, arity: usize)
|
||||
-> std::rc::Rc<foliage::PredicateDeclaration>
|
||||
{
|
||||
let mut predicate_declarations = self.predicate_declarations.borrow_mut();
|
||||
|
||||
match predicate_declarations.iter().find(|x| x.name == name && x.arity == arity)
|
||||
{
|
||||
Some(declaration) => std::rc::Rc::clone(&declaration),
|
||||
None =>
|
||||
{
|
||||
let declaration = foliage::PredicateDeclaration
|
||||
{
|
||||
name: name.to_string(),
|
||||
arity,
|
||||
};
|
||||
let declaration = std::rc::Rc::new(declaration);
|
||||
|
||||
predicate_declarations.insert(std::rc::Rc::clone(&declaration));
|
||||
|
||||
log::debug!("new predicate declaration: {}/{}", name, arity);
|
||||
|
||||
declaration
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::traits::AssignVariableDeclarationDomain for Problem
|
||||
{
|
||||
fn assign_variable_declaration_domain(&self,
|
||||
variable_declaration: &std::rc::Rc<foliage::VariableDeclaration>, domain: crate::Domain)
|
||||
{
|
||||
let mut variable_declaration_domains = self.variable_declaration_domains.borrow_mut();
|
||||
|
||||
match variable_declaration_domains.get(variable_declaration)
|
||||
{
|
||||
Some(current_domain) => assert_eq!(*current_domain, domain,
|
||||
"inconsistent variable declaration domain"),
|
||||
None =>
|
||||
{
|
||||
variable_declaration_domains
|
||||
.insert(std::rc::Rc::clone(variable_declaration).into(), domain);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> FormatContext<'a, 'b>
|
||||
{
|
||||
fn variable_declaration_id(&self,
|
||||
variable_declaration: &std::rc::Rc<foliage::VariableDeclaration>)
|
||||
-> usize
|
||||
{
|
||||
let mut variable_declaration_ids = match self.variable_declaration_domains
|
||||
.get(variable_declaration)
|
||||
{
|
||||
Some(crate::Domain::Program) => self.program_variable_declaration_ids.borrow_mut(),
|
||||
Some(crate::Domain::Integer) => self.integer_variable_declaration_ids.borrow_mut(),
|
||||
None => unreachable!("all variables should be declared at this point"),
|
||||
};
|
||||
|
||||
match variable_declaration_ids.get(variable_declaration)
|
||||
{
|
||||
Some(id) => *id,
|
||||
None =>
|
||||
{
|
||||
let id = variable_declaration_ids.len();
|
||||
variable_declaration_ids.insert(std::rc::Rc::clone(variable_declaration).into(), id);
|
||||
id
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> crate::traits::InputConstantDeclarationDomain for FormatContext<'a, 'b>
|
||||
{
|
||||
fn input_constant_declaration_domain(&self,
|
||||
declaration: &std::rc::Rc<foliage::FunctionDeclaration>) -> crate::Domain
|
||||
{
|
||||
// Assume the program domain if not specified otherwise
|
||||
self.input_constant_declaration_domains.get(declaration).map(|x| *x)
|
||||
.unwrap_or(crate::Domain::Program)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> crate::traits::VariableDeclarationDomain for FormatContext<'a, 'b>
|
||||
{
|
||||
fn variable_declaration_domain(&self,
|
||||
variable_declaration: &std::rc::Rc<foliage::VariableDeclaration>)
|
||||
-> Option<crate::Domain>
|
||||
{
|
||||
self.variable_declaration_domains.get(variable_declaration)
|
||||
.map(|x| *x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> crate::traits::VariableDeclarationID for FormatContext<'a, 'b>
|
||||
{
|
||||
fn variable_declaration_id(&self,
|
||||
variable_declaration: &std::rc::Rc<foliage::VariableDeclaration>)
|
||||
-> usize
|
||||
{
|
||||
use crate::traits::VariableDeclarationDomain;
|
||||
|
||||
let mut variable_declaration_ids = match self.variable_declaration_domain(
|
||||
variable_declaration)
|
||||
{
|
||||
Some(crate::Domain::Program) => self.program_variable_declaration_ids.borrow_mut(),
|
||||
Some(crate::Domain::Integer) => self.integer_variable_declaration_ids.borrow_mut(),
|
||||
None => panic!("all variables should be declared at this point"),
|
||||
};
|
||||
|
||||
match variable_declaration_ids.get(variable_declaration)
|
||||
{
|
||||
Some(id) =>
|
||||
{
|
||||
*id
|
||||
}
|
||||
None =>
|
||||
{
|
||||
let id = variable_declaration_ids.len();
|
||||
variable_declaration_ids.insert(std::rc::Rc::clone(variable_declaration).into(), id);
|
||||
id
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> foliage::format::Format for FormatContext<'a, 'b>
|
||||
{
|
||||
fn display_variable_declaration(&self, formatter: &mut std::fmt::Formatter,
|
||||
variable_declaration: &std::rc::Rc<foliage::VariableDeclaration>)
|
||||
-> std::fmt::Result
|
||||
{
|
||||
let id = self.variable_declaration_id(variable_declaration);
|
||||
let domain = self.variable_declaration_domains.get(variable_declaration)
|
||||
.expect("unspecified variable domain");
|
||||
|
||||
let prefix = match domain
|
||||
{
|
||||
crate::Domain::Integer => "N",
|
||||
crate::Domain::Program => "X",
|
||||
};
|
||||
|
||||
write!(formatter, "{}{}", prefix, id + 1)
|
||||
}
|
||||
}
|
36
src/specification.rs
Normal file
36
src/specification.rs
Normal file
@ -0,0 +1,36 @@
|
||||
/*#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum ProofDirection
|
||||
{
|
||||
Forward,
|
||||
Backward,
|
||||
}
|
||||
|
||||
#[derive(Eq, Hash, PartialEq)]
|
||||
pub enum CompletionTarget
|
||||
{
|
||||
Predicate(std::rc::Rc<foliage::PredicateDeclaration>),
|
||||
Constraint,
|
||||
}
|
||||
|
||||
pub struct CompletionFormula
|
||||
{
|
||||
pub target: CompletionTarget,
|
||||
pub formula: foliage::Formula,
|
||||
}
|
||||
|
||||
pub struct Lemma
|
||||
{
|
||||
pub direction: Option<ProofDirection>,
|
||||
pub formula: foliage::Formula,
|
||||
}
|
||||
|
||||
pub struct Specification
|
||||
{
|
||||
pub axioms: foliage::Formulas,
|
||||
pub lemmas: Vec<Lemma>,
|
||||
pub assumptions: foliage::Formulas,
|
||||
pub assertions: foliage::Formulas,
|
||||
|
||||
pub input_constants: foliage::FunctionDeclarations,
|
||||
pub input_predicates: foliage::PredicateDeclarations,
|
||||
}*/
|
@ -1,47 +1,23 @@
|
||||
mod context;
|
||||
mod head_type;
|
||||
mod translate_body;
|
||||
|
||||
use context::*;
|
||||
use head_type::*;
|
||||
use translate_body::*;
|
||||
use crate::traits::AssignVariableDeclarationDomain as _;
|
||||
|
||||
struct StatementHandler
|
||||
struct PredicateDefinitions
|
||||
{
|
||||
context: Context,
|
||||
pub parameters: std::rc::Rc<foliage::VariableDeclarations>,
|
||||
pub definitions: Vec<crate::ScopedFormula>,
|
||||
}
|
||||
|
||||
impl StatementHandler
|
||||
{
|
||||
fn new() -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
context: Context::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
type Definitions =
|
||||
std::collections::BTreeMap::<std::rc::Rc<foliage::PredicateDeclaration>, PredicateDefinitions>;
|
||||
|
||||
impl clingo::StatementHandler for StatementHandler
|
||||
pub(crate) struct Translator<'p>
|
||||
{
|
||||
fn on_statement(&mut self, statement: &clingo::ast::Statement) -> bool
|
||||
{
|
||||
match statement.statement_type()
|
||||
{
|
||||
clingo::ast::StatementType::Rule(ref rule) =>
|
||||
{
|
||||
if let Err(error) = read_rule(rule, &self.context)
|
||||
{
|
||||
log::error!("could not translate input program: {}", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
_ => log::debug!("read statement (other kind)"),
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
problem: &'p mut crate::Problem,// TODO: refactor
|
||||
definitions: Definitions,
|
||||
}
|
||||
|
||||
struct Logger;
|
||||
@ -54,74 +30,78 @@ impl clingo::Logger for Logger
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate<P>(program_paths: &[P],
|
||||
input_predicate_declarations: foliage::PredicateDeclarations,
|
||||
input_constant_declaration_domains: crate::InputConstantDeclarationDomains,
|
||||
output_format: crate::output::Format)
|
||||
-> Result<(), crate::Error>
|
||||
where
|
||||
P: AsRef<std::path::Path>
|
||||
impl<'p> clingo::StatementHandler for Translator<'p>
|
||||
{
|
||||
let mut statement_handler = StatementHandler::new();
|
||||
|
||||
*statement_handler.context.input_predicate_declarations.borrow_mut()
|
||||
= input_predicate_declarations.clone();
|
||||
*statement_handler.context.predicate_declarations.borrow_mut()
|
||||
= input_predicate_declarations;
|
||||
*statement_handler.context.function_declarations.borrow_mut()
|
||||
= input_constant_declaration_domains.keys().map(std::rc::Rc::clone).collect();
|
||||
*statement_handler.context.input_constant_declaration_domains.borrow_mut()
|
||||
= input_constant_declaration_domains;
|
||||
|
||||
for input_predicate_declaration in statement_handler.context.input_predicate_declarations
|
||||
.borrow().iter()
|
||||
fn on_statement(&mut self, statement: &clingo::ast::Statement) -> bool
|
||||
{
|
||||
log::info!("input predicate: {}/{}", input_predicate_declaration.name,
|
||||
input_predicate_declaration.arity);
|
||||
match statement.statement_type()
|
||||
{
|
||||
clingo::ast::StatementType::Rule(ref rule) =>
|
||||
{
|
||||
if let Err(error) = self.read_rule(rule)
|
||||
{
|
||||
log::error!("could not translate input program: {}", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
_ => log::debug!("read statement (other kind)"),
|
||||
}
|
||||
|
||||
for (input_constant_declaration, domain) in statement_handler.context
|
||||
.input_constant_declaration_domains.borrow().iter()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p> Translator<'p>
|
||||
{
|
||||
pub fn new(problem: &'p mut crate::Problem) -> Self
|
||||
{
|
||||
log::info!("input constant: {} (domain: {:?})", input_constant_declaration.name, domain);
|
||||
Self
|
||||
{
|
||||
problem,
|
||||
definitions: Definitions::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Read all input programs
|
||||
for program_path in program_paths
|
||||
pub fn translate<P>(&mut self, program_path: P) -> Result<(), crate::Error>
|
||||
where
|
||||
P: AsRef<std::path::Path>
|
||||
{
|
||||
// Read input program
|
||||
let program = std::fs::read_to_string(program_path.as_ref())
|
||||
.map_err(|error| crate::Error::new_read_file(program_path.as_ref().to_path_buf(), error))?;
|
||||
.map_err(
|
||||
|error| crate::Error::new_read_file(program_path.as_ref().to_path_buf(), error))?;
|
||||
|
||||
clingo::parse_program_with_logger(&program, &mut statement_handler, &mut Logger, std::u32::MAX)
|
||||
clingo::parse_program_with_logger(&program, self, &mut Logger, std::u32::MAX)
|
||||
.map_err(|error| crate::Error::new_translate(error))?;
|
||||
|
||||
log::info!("read input program “{}”", program_path.as_ref().display());
|
||||
}
|
||||
|
||||
let context = statement_handler.context;
|
||||
|
||||
for (predicate_declaration, definitions) in context.definitions.borrow().iter()
|
||||
// TODO: reimplement
|
||||
/*
|
||||
for (predicate_declaration, predicate_definitions) in self.definitions.iter()
|
||||
{
|
||||
for definition in definitions.definitions.iter()
|
||||
for definition in predicate_definitions.definitions.iter()
|
||||
{
|
||||
log::debug!("definition({}/{}): {}.", predicate_declaration.name, predicate_declaration.arity,
|
||||
foliage::format::display_formula(&definition.formula, &context));
|
||||
}
|
||||
log::debug!("definition({}/{}): {}.", predicate_declaration.name,
|
||||
predicate_declaration.arity,
|
||||
foliage::format::display_formula(&definition.formula, self.problem));
|
||||
}
|
||||
}*/
|
||||
|
||||
let completed_definition = |predicate_declaration|
|
||||
let completed_definition = |predicate_declaration, definitions: &mut Definitions,
|
||||
problem: &crate::Problem|
|
||||
{
|
||||
match context.definitions.borrow_mut().remove(predicate_declaration)
|
||||
match definitions.remove(predicate_declaration)
|
||||
{
|
||||
// This predicate symbol has at least one definition, so build the disjunction of those
|
||||
Some(definitions) =>
|
||||
Some(predicate_definitions) =>
|
||||
{
|
||||
let or_arguments = definitions.definitions.into_iter()
|
||||
let or_arguments = predicate_definitions.definitions.into_iter()
|
||||
.map(|x| crate::existential_closure(x))
|
||||
.collect::<Vec<_>>();
|
||||
let or = foliage::Formula::or(or_arguments);
|
||||
|
||||
let head_arguments = definitions.head_atom_parameters.iter()
|
||||
let head_arguments = predicate_definitions.parameters.iter()
|
||||
.map(|x| foliage::Term::variable(std::rc::Rc::clone(x)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -133,7 +113,7 @@ where
|
||||
|
||||
let scoped_formula = crate::ScopedFormula
|
||||
{
|
||||
free_variable_declarations: definitions.head_atom_parameters,
|
||||
free_variable_declarations: predicate_definitions.parameters,
|
||||
formula: completed_definition,
|
||||
};
|
||||
|
||||
@ -142,18 +122,18 @@ where
|
||||
// This predicate has no definitions, so universally falsify it
|
||||
None =>
|
||||
{
|
||||
let head_atom_parameters = std::rc::Rc::new((0..predicate_declaration.arity)
|
||||
let parameters = std::rc::Rc::new((0..predicate_declaration.arity)
|
||||
.map(|_|
|
||||
{
|
||||
let variable_declaration = std::rc::Rc::new(
|
||||
foliage::VariableDeclaration::new("<anonymous>".to_string()));
|
||||
context.assign_variable_declaration_domain(&variable_declaration,
|
||||
problem.assign_variable_declaration_domain(&variable_declaration,
|
||||
crate::Domain::Program);
|
||||
variable_declaration
|
||||
})
|
||||
.collect::<Vec<_>>());
|
||||
|
||||
let head_arguments = head_atom_parameters.iter()
|
||||
let head_arguments = parameters.iter()
|
||||
.map(|x| foliage::Term::variable(std::rc::Rc::clone(x)))
|
||||
.collect();
|
||||
|
||||
@ -164,7 +144,7 @@ where
|
||||
|
||||
let scoped_formula = crate::ScopedFormula
|
||||
{
|
||||
free_variable_declarations: head_atom_parameters,
|
||||
free_variable_declarations: parameters,
|
||||
formula: not,
|
||||
};
|
||||
|
||||
@ -173,213 +153,77 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let predicate_declarations = context.predicate_declarations.borrow();
|
||||
let function_declarations = context.function_declarations.borrow();
|
||||
let input_constant_declaration_domains = context.input_constant_declaration_domains.borrow();
|
||||
let completed_definitions = predicate_declarations.iter()
|
||||
// Don’t perform completion for any input predicate
|
||||
.filter(|x| !context.input_predicate_declarations.borrow().contains(*x))
|
||||
.map(|x| (std::rc::Rc::clone(x), completed_definition(x)));
|
||||
|
||||
// Earlier log messages may have assigned IDs to the variable declarations, so reset them
|
||||
context.program_variable_declaration_ids.borrow_mut().clear();
|
||||
context.integer_variable_declaration_ids.borrow_mut().clear();
|
||||
|
||||
let print_title = |title, section_separator|
|
||||
for predicate_declaration in self.problem.predicate_declarations.borrow().iter()
|
||||
{
|
||||
print!("{}{}", section_separator, "%".repeat(80));
|
||||
print!("\n% {}", title);
|
||||
println!("\n{}", "%".repeat(80));
|
||||
};
|
||||
let completed_definition =
|
||||
completed_definition(predicate_declaration, &mut self.definitions, self.problem);
|
||||
|
||||
let print_formula = |formula: &foliage::Formula|
|
||||
{
|
||||
match output_format
|
||||
{
|
||||
crate::output::Format::HumanReadable => print!("{}",
|
||||
foliage::format::display_formula(formula, &context)),
|
||||
crate::output::Format::TPTP => print!("{}",
|
||||
crate::output::tptp::display_formula(formula, &context)),
|
||||
}
|
||||
};
|
||||
let statement = crate::problem::Statement::new(
|
||||
crate::problem::StatementKind::Assumption, completed_definition)
|
||||
.with_name(format!("completed_definition_{}_{}", predicate_declaration.name,
|
||||
predicate_declaration.arity))
|
||||
.with_description(format!("completed definition of {}/{}",
|
||||
predicate_declaration.name, predicate_declaration.arity));
|
||||
|
||||
let mut section_separator = "";
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print_title("anthem types", section_separator);
|
||||
section_separator = "\n";
|
||||
|
||||
let tptp_preamble_anthem_types
|
||||
= include_str!("../output/tptp/preamble_types.tptp").trim_end();
|
||||
println!("{}", tptp_preamble_anthem_types);
|
||||
|
||||
print_title("anthem axioms", section_separator);
|
||||
|
||||
let tptp_preamble_anthem_types
|
||||
= include_str!("../output/tptp/preamble_axioms.tptp").trim_end();
|
||||
println!("{}", tptp_preamble_anthem_types);
|
||||
|
||||
if !predicate_declarations.is_empty() || !function_declarations.is_empty()
|
||||
{
|
||||
print_title("types", section_separator);
|
||||
|
||||
if !predicate_declarations.is_empty()
|
||||
{
|
||||
println!("% predicate types")
|
||||
}
|
||||
|
||||
for predicate_declaration in &*predicate_declarations
|
||||
{
|
||||
println!("tff(type, type, {}).",
|
||||
crate::output::tptp::display_predicate_declaration(predicate_declaration));
|
||||
}
|
||||
|
||||
if !function_declarations.is_empty()
|
||||
{
|
||||
println!("% function types")
|
||||
}
|
||||
|
||||
for function_declaration in &*function_declarations
|
||||
{
|
||||
println!("tff(type, type, {}).",
|
||||
crate::output::tptp::display_function_declaration(function_declaration,
|
||||
&context));
|
||||
}
|
||||
}
|
||||
|
||||
let symbolic_constants = function_declarations.iter().filter(
|
||||
|x| !input_constant_declaration_domains.contains_key(*x));
|
||||
|
||||
let mut last_symbolic_constant: Option<std::rc::Rc<foliage::FunctionDeclaration>> = None;
|
||||
|
||||
for (i, symbolic_constant) in symbolic_constants.enumerate()
|
||||
{
|
||||
// Order axioms are only necessary with two or more symbolic constants
|
||||
if i == 1
|
||||
{
|
||||
println!("% axioms for order of symbolic constants")
|
||||
}
|
||||
|
||||
if symbolic_constant.arity > 0
|
||||
{
|
||||
return Err(crate::Error::new_logic("unexpected n-ary function declaration"));
|
||||
}
|
||||
|
||||
if let Some(last_symbolic_constant) = last_symbolic_constant
|
||||
{
|
||||
println!("tff(symbolic_constant_order, axiom, p__less__({}, {})).",
|
||||
last_symbolic_constant.name, symbolic_constant.name);
|
||||
}
|
||||
|
||||
last_symbolic_constant = Some(std::rc::Rc::clone(symbolic_constant));
|
||||
}
|
||||
}
|
||||
|
||||
for (i, (predicate_declaration, completed_definition)) in completed_definitions.enumerate()
|
||||
{
|
||||
if i == 0
|
||||
{
|
||||
print_title("completed definitions", section_separator);
|
||||
}
|
||||
|
||||
section_separator = "\n";
|
||||
|
||||
println!("% completed definition of {}/{}", predicate_declaration.name,
|
||||
predicate_declaration.arity);
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print!("tff(completion_{}_{}, axiom, ", predicate_declaration.name,
|
||||
predicate_declaration.arity);
|
||||
}
|
||||
|
||||
print_formula(&completed_definition);
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print!(").");
|
||||
}
|
||||
|
||||
println!("");
|
||||
}
|
||||
|
||||
for (i, integrity_constraint) in context.integrity_constraints.borrow().iter().enumerate()
|
||||
{
|
||||
if i == 0
|
||||
{
|
||||
print_title("integrity constraints", section_separator);
|
||||
}
|
||||
|
||||
section_separator = "\n";
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print!("tff(integrity_constraint, axiom, ");
|
||||
}
|
||||
|
||||
print_formula(&integrity_constraint);
|
||||
|
||||
if output_format == crate::output::Format::TPTP
|
||||
{
|
||||
print!(").");
|
||||
}
|
||||
|
||||
println!("");
|
||||
self.problem.add_statement(crate::problem::SectionKind::CompletedDefinitions,
|
||||
statement);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_rule(rule: &clingo::ast::Rule, context: &Context) -> Result<(), crate::Error>
|
||||
{
|
||||
let head_type = determine_head_type(rule.head(), context)?;
|
||||
fn read_rule(&mut self, rule: &clingo::ast::Rule)
|
||||
-> Result<(), crate::Error>
|
||||
{
|
||||
let head_type = determine_head_type(rule.head(), self.problem)?;
|
||||
|
||||
match &head_type
|
||||
{
|
||||
HeadType::SingleAtom(head_atom)
|
||||
| HeadType::ChoiceWithSingleAtom(head_atom) =>
|
||||
{
|
||||
if !context.definitions.borrow().contains_key(&head_atom.predicate_declaration)
|
||||
if !self.definitions.contains_key(&head_atom.predicate_declaration)
|
||||
{
|
||||
let head_atom_parameters = std::rc::Rc::new((0..head_atom.predicate_declaration.arity)
|
||||
let parameters = std::rc::Rc::new((0..head_atom.predicate_declaration.arity)
|
||||
.map(|_|
|
||||
{
|
||||
let variable_declaration = std::rc::Rc::new(
|
||||
foliage::VariableDeclaration::new("<anonymous>".to_string()));
|
||||
context.assign_variable_declaration_domain(&variable_declaration,
|
||||
self.problem.assign_variable_declaration_domain(&variable_declaration,
|
||||
crate::Domain::Program);
|
||||
variable_declaration
|
||||
})
|
||||
.collect());
|
||||
|
||||
context.definitions.borrow_mut().insert(
|
||||
self.definitions.insert(
|
||||
std::rc::Rc::clone(&head_atom.predicate_declaration),
|
||||
Definitions
|
||||
PredicateDefinitions
|
||||
{
|
||||
head_atom_parameters,
|
||||
parameters,
|
||||
definitions: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let mut definitions = context.definitions.borrow_mut();
|
||||
let definitions = definitions.get_mut(&head_atom.predicate_declaration).unwrap();
|
||||
// TODO: refactor
|
||||
let predicate_definitions =
|
||||
self.definitions.get_mut(&head_atom.predicate_declaration).unwrap();
|
||||
|
||||
let head_atom_parameters = std::rc::Rc::clone(&definitions.head_atom_parameters);
|
||||
let parameters = std::rc::Rc::clone(&predicate_definitions.parameters);
|
||||
let free_variable_declarations = std::cell::RefCell::new(vec![]);
|
||||
let free_layer =
|
||||
foliage::VariableDeclarationStackLayer::Free(free_variable_declarations);
|
||||
let head_atom_parameters_layer = foliage::VariableDeclarationStackLayer::bound(
|
||||
&free_layer, head_atom_parameters);
|
||||
let parameters_layer =
|
||||
foliage::VariableDeclarationStackLayer::bound(&free_layer, parameters);
|
||||
|
||||
let mut definition_arguments = translate_body(rule.body(), context,
|
||||
&head_atom_parameters_layer)?;
|
||||
let mut definition_arguments =
|
||||
translate_body(rule.body(), self.problem, ¶meters_layer)?;
|
||||
|
||||
assert_eq!(definitions.head_atom_parameters.len(), head_atom.arguments.len());
|
||||
// TODO: refactor
|
||||
assert_eq!(predicate_definitions.parameters.len(), head_atom.arguments.len());
|
||||
|
||||
if let HeadType::ChoiceWithSingleAtom(_) = head_type
|
||||
{
|
||||
let head_arguments = definitions.head_atom_parameters.iter()
|
||||
let head_arguments = predicate_definitions.parameters.iter()
|
||||
.map(|x| foliage::Term::variable(std::rc::Rc::clone(x)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -391,13 +235,13 @@ fn read_rule(rule: &clingo::ast::Rule, context: &Context) -> Result<(), crate::E
|
||||
|
||||
let mut head_atom_arguments_iterator = head_atom.arguments.iter();
|
||||
|
||||
for head_atom_parameter in definitions.head_atom_parameters.iter()
|
||||
for parameter in predicate_definitions.parameters.iter()
|
||||
{
|
||||
let head_atom_argument = head_atom_arguments_iterator.next().unwrap();
|
||||
|
||||
let translated_head_term = crate::translate::common::choose_value_in_term(
|
||||
head_atom_argument, std::rc::Rc::clone(head_atom_parameter), context,
|
||||
&head_atom_parameters_layer)?;
|
||||
head_atom_argument, std::rc::Rc::clone(parameter), self.problem,
|
||||
¶meters_layer)?;
|
||||
|
||||
definition_arguments.push(translated_head_term);
|
||||
}
|
||||
@ -423,10 +267,14 @@ fn read_rule(rule: &clingo::ast::Rule, context: &Context) -> Result<(), crate::E
|
||||
formula: definition,
|
||||
};
|
||||
|
||||
// TODO: refactor
|
||||
// TODO: reimplement
|
||||
/*
|
||||
log::debug!("translated rule with single atom in head: {}",
|
||||
foliage::format::display_formula(&definition.formula, context));
|
||||
foliage::format::display_formula(&definition.formula, self.problem));
|
||||
*/
|
||||
|
||||
definitions.definitions.push(definition);
|
||||
predicate_definitions.definitions.push(definition);
|
||||
},
|
||||
HeadType::IntegrityConstraint =>
|
||||
{
|
||||
@ -434,7 +282,7 @@ fn read_rule(rule: &clingo::ast::Rule, context: &Context) -> Result<(), crate::E
|
||||
let free_layer =
|
||||
foliage::VariableDeclarationStackLayer::Free(free_variable_declarations);
|
||||
|
||||
let mut arguments = translate_body(rule.body(), context, &free_layer)?;
|
||||
let mut arguments = translate_body(rule.body(), self.problem, &free_layer)?;
|
||||
|
||||
// TODO: refactor
|
||||
let free_variable_declarations = match free_layer
|
||||
@ -459,13 +307,24 @@ fn read_rule(rule: &clingo::ast::Rule, context: &Context) -> Result<(), crate::E
|
||||
|
||||
let integrity_constraint = crate::universal_closure(scoped_formula);
|
||||
|
||||
// TODO: refactor
|
||||
// TODO: reimplement
|
||||
/*
|
||||
log::debug!("translated integrity constraint: {}",
|
||||
foliage::format::display_formula(&integrity_constraint, context));
|
||||
foliage::format::display_formula(&integrity_constraint, self.problem));
|
||||
*/
|
||||
|
||||
context.integrity_constraints.borrow_mut().push(integrity_constraint);
|
||||
let statement = crate::problem::Statement::new(
|
||||
crate::problem::StatementKind::Assumption, integrity_constraint)
|
||||
.with_name("integrity_constraint".to_string())
|
||||
.with_description("integrity constraint".to_string());
|
||||
|
||||
self.problem.add_statement(crate::problem::SectionKind::IntegrityConstraints,
|
||||
statement);
|
||||
},
|
||||
HeadType::Trivial => log::info!("skipping trivial rule"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub(crate) struct Definitions
|
||||
/*pub(crate) struct Definitions
|
||||
{
|
||||
pub head_atom_parameters: std::rc::Rc<foliage::VariableDeclarations>,
|
||||
pub definitions: Vec<crate::ScopedFormula>,
|
||||
@ -192,4 +192,4 @@ impl foliage::format::Format for Context
|
||||
crate::output::human_readable::display_variable_declaration(self, formatter,
|
||||
variable_declaration)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
@ -1,3 +1,4 @@
|
||||
// TODO: rename context
|
||||
pub(crate) fn translate_body_term<C>(body_term: &clingo::ast::Term, sign: clingo::ast::Sign,
|
||||
context: &C, variable_declaration_stack: &foliage::VariableDeclarationStackLayer)
|
||||
-> Result<foliage::Formula, crate::Error>
|
||||
|
16
src/utils.rs
16
src/utils.rs
@ -36,6 +36,14 @@ impl std::fmt::Display for Domain
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum ProofDirection
|
||||
{
|
||||
Forward,
|
||||
Backward,
|
||||
Both,
|
||||
}
|
||||
|
||||
pub(crate) struct ScopedFormula
|
||||
{
|
||||
pub free_variable_declarations: std::rc::Rc<foliage::VariableDeclarations>,
|
||||
@ -62,7 +70,7 @@ pub(crate) fn universal_closure(scoped_formula: crate::ScopedFormula) -> foliage
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_predicate_declaration(input: &str)
|
||||
/*pub fn parse_predicate_declaration(input: &str)
|
||||
-> Result<std::rc::Rc<foliage::PredicateDeclaration>, crate::Error>
|
||||
{
|
||||
let mut parts = input.split("/");
|
||||
@ -88,12 +96,12 @@ pub fn parse_predicate_declaration(input: &str)
|
||||
name: name.to_string(),
|
||||
arity,
|
||||
}))
|
||||
}
|
||||
}*/
|
||||
|
||||
pub type InputConstantDeclarationDomains
|
||||
= std::collections::BTreeMap<std::rc::Rc<foliage::FunctionDeclaration>, Domain>;
|
||||
|
||||
pub fn parse_constant_declaration(input: &str)
|
||||
/*pub fn parse_constant_declaration(input: &str)
|
||||
-> Result<(std::rc::Rc<foliage::FunctionDeclaration>, crate::Domain), crate::Error>
|
||||
{
|
||||
let mut parts = input.split(":");
|
||||
@ -123,4 +131,4 @@ pub fn parse_constant_declaration(input: &str)
|
||||
});
|
||||
|
||||
Ok((constant_declaration, domain))
|
||||
}
|
||||
}*/
|
||||
|
Loading…
Reference in New Issue
Block a user