use std::collections::HashMap;
use itertools::enumerate;
use crate::check::constrain::constraint::{Constraint, MapExp};
use crate::check::constrain::constraint::expected::Expected;
use crate::check::constrain::constraint::iterator::Constraints;
use crate::check::constrain::generate::env::Environment;
use crate::check::name::Name;
use crate::common::delimit::comma_delm;
use crate::common::position::Position;
pub type VarMapping = HashMap<String, usize>;
pub fn format_var_map(var: &str, offset: &usize) -> String {
if *offset == 0_usize {
String::from(var)
} else {
format!("{var}@{offset}")
}
}
type ConstraintLvls = Vec<(Constraint, usize)>;
#[derive(Debug)]
pub struct ConstrBuilder {
constraints: Vec<(Position, String, ConstraintLvls)>,
branch_point: usize,
joined: bool,
temp_name_offset: usize,
pub var_mapping: VarMapping,
}
impl ConstrBuilder {
pub fn new() -> ConstrBuilder {
let var_mapping = VarMapping::new();
let (pos, msg) = (Position::invisible(), String::from("Script"));
ConstrBuilder { branch_point: 0, joined: false, constraints: vec![(pos, msg, vec![])], var_mapping, temp_name_offset: 0 }
}
pub fn insert_var(&mut self, var: &str) {
let offset = self.var_mapping.get(var).map_or(0, |o| o + 1);
self.var_mapping.insert(String::from(var), offset);
let mapped_var = format_var_map(var, self.var_mapping.get(var).unwrap());
trace!("Inserted {var} in constraint builder: {var} => {mapped_var}");
}
pub fn temp_name(&mut self) -> Name {
self.temp_name_offset += 1;
Name::from(format_var_map("", &self.temp_name_offset).as_str())
}
pub fn branch_point(&mut self) {
trace!("Branch point created at level {}", self.constraints.len() - 1);
self.branch_point += 1;
self.joined = false;
}
pub fn branch(&mut self, msg: &str, pos: Position) {
trace!("Branching from level {}", self.branch_point - 1);
let inherited_constraints: ConstraintLvls = if self.joined {
self.constraints.last().expect("Is never empty").2.to_vec()
} else {
self.constraints.last().expect("Is never empty").2
.iter().filter(|(_, lvl)| *lvl < self.branch_point).cloned().collect()
};
self.constraints.push((pos, String::from(msg), inherited_constraints));
}
pub fn reset_branches(&mut self) {
trace!("Reset branches: Now adding to {} branches in parallel", self.constraints.len());
self.branch_point = self.constraints.len() - 1;
self.joined = true;
}
pub fn add(&mut self, msg: &str, parent: &Expected, child: &Expected, env: &Environment) {
self.add_constr_map(&Constraint::new(msg, parent, child), &env.var_mapping, false);
}
pub fn add_constr(&mut self, constraint: &Constraint, env: &Environment) {
self.add_constr_map(constraint, &env.var_mapping, false)
}
pub fn add_constr_map(&mut self, constraint: &Constraint, var_map: &VarMapping, ignore_map: bool) {
let (mut lvls, last_branch) = (vec![], self.constraints.len() - 1);
let constraint = if ignore_map {
constraint.clone()
} else {
constraint.map_exp(var_map, &self.var_mapping)
};
if self.joined {
for (i, (_, _, constraints)) in enumerate(&mut self.constraints) {
constraints.push((constraint.clone(), self.branch_point));
lvls.push(i);
}
} else {
self.constraints[last_branch].2.push((constraint.clone(), self.branch_point));
lvls.push(last_branch);
}
let lvls = comma_delm(lvls);
trace!("Constr[{}]: {} == {}, {}: {}", lvls, constraint.parent.pos, constraint.child.pos, constraint.msg, constraint);
}
pub fn all_constr(self) -> Vec<Constraints> {
self.constraints.into_iter()
.map(|(pos, msg, constraints)| {
(pos, msg, constraints.iter().map(|(c, _)| c.clone()).collect())
})
.map(Constraints::from)
.collect()
}
}
#[cfg(test)]
mod tests {
use crate::check::constrain::constraint::builder::ConstrBuilder;
use crate::check::constrain::constraint::Constraint;
use crate::check::constrain::constraint::expected::Expected;
use crate::check::constrain::generate::env::Environment;
use crate::common::position::Position;
macro_rules! constr {
($msg:expr) => {{
Constraint::new(format!("{}", $msg).as_str(),
&Expected::any(Position::invisible()),
&Expected::any(Position::invisible()))
}}
}
macro_rules! assert_eq_constr {
($left:expr, $right:expr) => {{
let left = $left.iter().map(|c| c.msg.clone()).collect::<Vec<_>>();
let right = $right.iter().map(|c| c.msg.clone()).collect::<Vec<_>>();
assert_eq!(left, right);
}}
}
#[test]
fn all_constr_present() {
let mut builder = ConstrBuilder::new();
let (c1, c2, c3) = (constr!(1), constr!(2), constr!(3));
builder.add_constr(&c1, &Environment::default());
builder.add_constr(&c2, &Environment::default());
builder.add_constr(&c3, &Environment::default());
let all_constr = builder.all_constr();
assert_eq!(all_constr.len(), 1);
assert_eq_constr!(all_constr[0].constraints, vec![c1, c2, c3])
}
#[test]
fn disjoint_set_if() {
let mut builder = ConstrBuilder::new();
let (c1, c2, c3, c4) = (constr!(1), constr!(2), constr!(3), constr!(4));
builder.add_constr(&c1, &Environment::default());
builder.branch_point();
builder.add_constr(&c2, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c3, &Environment::default());
builder.reset_branches();
builder.add_constr(&c4, &Environment::default());
let all_constr = builder.all_constr();
assert_eq!(all_constr.len(), 2);
assert_eq_constr!(all_constr[0].constraints, [&c1, &c2, &c4]);
assert_eq_constr!(all_constr[1].constraints, [&c1, &c3, &c4]);
}
#[test]
fn disjoint_set_match() {
let mut builder = ConstrBuilder::new();
let (c1, c2, c3, c4, c5) = (constr!(1), constr!(2), constr!(3), constr!(4), constr!(5));
builder.add_constr(&c1, &Environment::default());
builder.branch_point();
builder.add_constr(&c2, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c3, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c4, &Environment::default());
builder.reset_branches();
builder.add_constr(&c5, &Environment::default());
let all_constr = builder.all_constr();
assert_eq!(all_constr.len(), 3);
assert_eq_constr!(all_constr[0].constraints, [&c1, &c2, &c5]);
assert_eq_constr!(all_constr[1].constraints, [&c1, &c3, &c5]);
assert_eq_constr!(all_constr[2].constraints, [&c1, &c4, &c5]);
}
#[test]
fn disjoint_set_nested_match() {
let mut builder = ConstrBuilder::new();
let (c1, c2, _, c4, c5) = (constr!(1), constr!(2), constr!(3), constr!(4), constr!(5));
let (c31, c32, c33, c34, c35) = (constr!(31), constr!(32), constr!(33), constr!(34), constr!(35));
builder.add_constr(&c1, &Environment::default());
builder.branch_point();
builder.add_constr(&c2, &Environment::default());
builder.branch("", Position::invisible());
{ builder.branch_point();
builder.add_constr(&c31, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c32, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c33, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c34, &Environment::default());
builder.branch("", Position::invisible());
builder.add_constr(&c35, &Environment::default());
}
builder.branch("", Position::invisible());
builder.add_constr(&c4, &Environment::default());
builder.reset_branches();
builder.add_constr(&c5, &Environment::default());
let all_constr = builder.all_constr();
assert_eq!(all_constr.len(), 7);
assert_eq_constr!(all_constr[0].constraints, [&c1, &c2, &c5]);
assert_eq_constr!(all_constr[1].constraints, [&c1, &c31, &c5]);
assert_eq_constr!(all_constr[2].constraints, [&c1, &c32, &c5]);
assert_eq_constr!(all_constr[3].constraints, [&c1, &c33, &c5]);
assert_eq_constr!(all_constr[4].constraints, [&c1, &c34, &c5]);
assert_eq_constr!(all_constr[5].constraints, [&c1, &c35, &c5]);
assert_eq_constr!(all_constr[6].constraints, [&c1, &c4, &c5]);
}
}