use foliage::flavor::VariableDeclaration as _; pub type Source = Box; pub enum Kind { Logic(&'static str), UnsupportedLanguageFeature(&'static str), NotYetImplemented(&'static str), DecodeIdentifier, Translate, ReadFile(std::path::PathBuf), ExpectedStatement, ExpectedColon, UnknownStatement(String), UnmatchedParenthesis, MissingStatementTerminator, ParseFormula, ExpectedIdentifier, ExpectedPredicateSpecifier, ParsePredicateDeclaration, //ParseConstantDeclaration, UnknownProofDirection(String), UnknownDomainIdentifier(String), UnknownColorChoice(String), VariableNameNotAllowed(String), FormulaNotClosed(std::rc::Rc), PrivatePredicateCycle(std::rc::Rc), PrivatePredicateDependingOnPublicPredicate(std::rc::Rc, std::rc::Rc), RunVampire, // TODO: rename to something Vampire-specific ProveProgram(Option, String, String), ParseVampireOutput(String, String), IO, } pub struct Error { pub kind: Kind, pub source: Option, } impl Error { pub(crate) fn new(kind: Kind) -> Self { Self { kind, source: None, } } pub(crate) fn with>(mut self, source: S) -> Self { self.source = Some(source.into()); self } pub(crate) fn new_logic(description: &'static str) -> Self { Self::new(Kind::Logic(description)) } pub(crate) fn new_unsupported_language_feature(description: &'static str) -> Self { Self::new(Kind::UnsupportedLanguageFeature(description)) } pub(crate) fn new_not_yet_implemented(description: &'static str) -> Self { Self::new(Kind::NotYetImplemented(description)) } pub(crate) fn new_decode_identifier>(source: S) -> Self { Self::new(Kind::DecodeIdentifier).with(source) } pub(crate) fn new_translate>(source: S) -> Self { Self::new(Kind::Translate).with(source) } pub(crate) fn new_read_file>(path: std::path::PathBuf, source: S) -> Self { 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>(source: S) -> Self { Self::new(Kind::ParseFormula).with(source) } pub(crate) fn new_expected_identifier() -> Self { Self::new(Kind::ExpectedIdentifier) } pub(crate) fn new_expected_predicate_specifier() -> Self { Self::new(Kind::ExpectedPredicateSpecifier) } pub(crate) fn new_parse_predicate_declaration() -> Self { Self::new(Kind::ParsePredicateDeclaration) } pub(crate) fn new_unknown_proof_direction(proof_direction: String) -> Self { 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_unknown_color_choice(color_choice: String) -> Self { Self::new(Kind::UnknownColorChoice(color_choice)) } pub(crate) fn new_variable_name_not_allowed(variable_name: String) -> Self { Self::new(Kind::VariableNameNotAllowed(variable_name)) } pub(crate) fn new_formula_not_closed(free_variables: std::rc::Rc) -> Self { Self::new(Kind::FormulaNotClosed(free_variables)) } pub(crate) fn new_private_predicate_cycle( predicate_declaration: std::rc::Rc) -> Self { Self::new(Kind::PrivatePredicateCycle(predicate_declaration)) } pub(crate) fn new_private_predicate_depending_on_public_predicate( private_predicate_declaration: std::rc::Rc, public_predicate_declaration: std::rc::Rc) -> Self { Self::new(Kind::PrivatePredicateDependingOnPublicPredicate(private_predicate_declaration, public_predicate_declaration)) } pub(crate) fn new_run_vampire>(source: S) -> Self { Self::new(Kind::RunVampire).with(source) } pub(crate) fn new_prove_program(exit_code: Option, stdout: String, stderr: String) -> Self { Self::new(Kind::ProveProgram(exit_code, stdout, stderr)) } pub(crate) fn new_parse_vampire_output(stdout: String, stderr: String) -> Self { Self::new(Kind::ParseVampireOutput(stdout, stderr)) } pub(crate) fn new_io>(source: S) -> Self { Self::new(Kind::IO).with(source) } } impl std::fmt::Debug for Error { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match &self.kind { Kind::Logic(ref description) => write!(formatter, "logic error, please report to bug tracker ({})", description), Kind::UnsupportedLanguageFeature(ref description) => write!(formatter, "language feature not yet supported ({})", description), Kind::NotYetImplemented(ref description) => write!(formatter, "not yet implemented ({})", description), 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::ParseFormula => write!(formatter, "could not parse formula"), Kind::ExpectedIdentifier => write!(formatter, "expected constant or predicate name"), Kind::ExpectedPredicateSpecifier => write!(formatter, "expected predicate specifier (examples: p/0, q/2)"), Kind::ParsePredicateDeclaration => write!(formatter, "could not parse predicate declaration"), // TODO: rename to ExpectedStatementTerminator 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::UnknownColorChoice(ref color_choice) => write!(formatter, "unknown color choice “{}” (allowed: auto, always, never)", color_choice), 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), Kind::FormulaNotClosed(free_variable_declarations) => { write!(formatter, "formula may not contain free variables (free variables in this formula: ")?; let mut separator = ""; for free_variable_declaration in &**free_variable_declarations { write!(formatter, "{}", separator)?; free_variable_declaration.display_name(formatter)?; separator = ", "; } write!(formatter, ")") }, Kind::PrivatePredicateCycle(ref predicate_declaration) => write!(formatter, "program is not supertight (private predicate {} transitively depends on itself)", predicate_declaration.declaration), Kind::PrivatePredicateDependingOnPublicPredicate(ref private_predicate_declaration, ref public_predicate_declaration) => write!(formatter, "private predicate {} transitively depends on public predicate {}", private_predicate_declaration.declaration, public_predicate_declaration.declaration), Kind::RunVampire => write!(formatter, "could not run Vampire"), Kind::ProveProgram(exit_code, ref stdout, ref stderr) => { let exit_code_output = match exit_code { None => "no exit code".to_string(), Some(exit_code) => format!("exit code: {}", exit_code), }; write!(formatter, "error proving program ({})\n\ ==== stdout ===========================================================\n\ {}\ ==== stderr ===========================================================\n\ {}", exit_code_output, stdout, stderr) }, Kind::ParseVampireOutput(ref stdout, ref stderr) => write!(formatter, "could not parse Vampire output\n\ ==== stdout ===========================================================\n\ {}\ ==== stderr ===========================================================\n\ {}", stdout, stderr), Kind::IO => write!(formatter, "input/output error"), }?; if let Some(source) = &self.source { write!(formatter, "\nerror source: {}", source)?; } Ok(()) } } impl std::fmt::Display for Error { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "{:?}", self) } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self.source { Some(source) => Some(source.as_ref()), None => None, } } } impl From for Error { fn from(error: std::io::Error) -> Self { Self::new_io(error) } }