use std::io::Error;
use crate::{error::VeriPBError, prelude::Elaborator, Rule};
use colored::Colorize;
use logos::Logos;
use veripb_formula::prelude::*;
use veripb_parser::parser::get_lines;
use crate::{
context::{Context, CORE},
database::Database,
error::CheckingError,
rules::{line_to_rule, RuleToken},
};
pub struct Verifier {
pub context: Context,
pub database: Database,
returned_constraint_id: Option<isize>,
}
impl Verifier {
pub fn new(mut context: Context, formula: Formula) -> Result<Self, Error> {
if let Some(output_file) = context.args.elaborate.clone() {
let mut elaborator = Elaborator::new(output_file)?;
elaborator.load_formula(formula.len());
context.elaborator = Some(elaborator);
}
let mut database = Database::from_formula(formula.constraints);
context.objective = formula.objective.clone();
if let Some(elaborator) = context.elaborator.as_mut() {
for (orig_id, constraint) in database.entries.iter_mut().flatten().enumerate() {
constraint.set_out_id(orig_id + 1, elaborator.inc_id());
}
}
context.original_constraints = database.unique_constraints.iter().cloned().collect();
context.original_objective = formula.objective;
let verifier = Verifier {
context,
database,
returned_constraint_id: None,
};
if verifier.context.args.trace {
verifier.print_formula();
}
Ok(verifier)
}
pub fn initialize(&mut self) -> anyhow::Result<()> {
for unique_constraint in self.database.unique_constraints.iter() {
self.context
.propagation_engine
.attach(CORE, unique_constraint)?;
}
Ok(())
}
pub fn verify_file_version_2(&mut self) -> anyhow::Result<()> {
let proof_lines = get_lines(&self.context.args.derivation)?;
for (line_number, line) in proof_lines.map_while(Result::ok).enumerate() {
let lex = RuleToken::lexer(&line);
if self.context.args.trace {
println!(
"{}",
format!("line {:>4}: {}", line_number + 1, lex.source()).bright_black(),
);
}
let rule = anyhow::Context::with_context(line_to_rule(lex, &mut self.context), || {
format!(
"Parsing error at {}:{}",
self.context.args.derivation.to_string_lossy(),
line_number + 1
)
})?;
self.execute_rule(line_number, rule)?;
}
if !self.context.has_end_proof {
return Err(CheckingError::EndProofMissing.into());
}
Ok(())
}
pub fn get_returned_constraint_id(&mut self) -> Option<isize> {
let result = self.returned_constraint_id;
self.returned_constraint_id = None;
result
}
pub fn execute_rule(
&mut self,
line: usize,
mut rule: Box<dyn Rule>,
) -> Result<(), VeriPBError> {
let last_subcontext = self.context.subcontexts.last();
if last_subcontext.is_some()
&& last_subcontext.unwrap().is_subproof()
&& !rule.is_subproof_friendly()
{
return Err(CheckingError::RuleNotSubproofFriendly(
std::any::type_name_of_val(&rule).to_string(),
)
.add_context(self.context.args.derivation.clone(), line));
}
let new_constraints = rule
.compute(&mut self.context, &mut self.database)
.map_err(|e| e.add_context(self.context.args.derivation.clone(), line))?;
let num_new_constraints = new_constraints.len();
for constraint in new_constraints {
if self.context.args.trace {
println!(
" ConstraintId {}: {}",
self.database.len().to_string().bright_green(),
&constraint
.constraint
.to_pretty_string(&self.context.var_names)
.blue()
)
}
if let Some(level) = self.context.current_level {
unsafe {
self.context
.level_ids
.get_unchecked_mut(level)
.push(self.database.len())
};
}
self.database.add_constraint(
constraint,
self.context.only_core || rule.add_constraints_to_core(&self.context),
);
}
self.returned_constraint_id = rule
.get_returned_id()
.or_else(|| Some(std::cmp::max(self.database.len(), 1) as isize - 1));
if let Some(elaborator) = self.context.elaborator.as_mut() {
let mut start_id_new_constraints = self.database.len() - num_new_constraints;
for constraint in self.database.entries[start_id_new_constraints..]
.iter()
.flatten()
{
constraint.set_out_id(start_id_new_constraints, elaborator.inc_id());
start_id_new_constraints += 1;
}
rule.elaborate(&mut self.context, &self.database)?;
}
if let Some(deletion_sequence) = rule
.get_deleted(&self.database)
.map_err(|e| e.add_context(self.context.args.derivation.clone(), line))?
{
if self.context.args.trace {
if let Some(str) = deletion_sequence.trace_deletions() {
println!(" Deleting IDs: {str}");
}
}
deletion_sequence
.delete_constraints(&mut self.database, &mut self.context)
.map_err(|e| e.add_context(self.context.args.derivation.clone(), line))?
}
if rule.swaps_database() {
let (database, prop_engine) = rule.get_new_database();
let old_database = std::mem::replace(&mut self.database, database);
let old_prop_engine =
std::mem::replace(&mut self.context.propagation_engine, prop_engine);
rule.handle_old_database(&mut self.context, old_database, old_prop_engine);
}
Ok(())
}
fn print_formula(&self) {
if let Some(objective) = &self.context.objective {
println!(
" {}: {}",
"Objective".bright_green(),
("min ".to_owned() + objective.to_pretty_string(&self.context.var_names).as_str())
.green()
)
}
for (idx, constraint) in self.database.entries.iter().enumerate() {
if let Some(constraint) = constraint {
println!(
" ConstraintId {}: {}",
idx.to_string().bright_green(),
constraint
.constraint
.to_pretty_string(&self.context.var_names)
.blue()
)
}
}
}
}