use sim_kernel::{Cx, Expr, OpKey, Result, Symbol};
use crate::{LanguageProfile, standard_diff_capability};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProfileDiffStatus {
Same,
Different,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProfileDifference {
pub field: Symbol,
pub left: Expr,
pub right: Expr,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProfileDiff {
pub left: Symbol,
pub right: Symbol,
pub status: ProfileDiffStatus,
pub shared_organs: Vec<Symbol>,
pub left_only_organs: Vec<Symbol>,
pub right_only_organs: Vec<Symbol>,
pub differences: Vec<ProfileDifference>,
}
impl ProfileDiff {
pub fn is_same(&self) -> bool {
self.status == ProfileDiffStatus::Same
}
}
pub fn standard_diff_op_key() -> OpKey {
OpKey::new(Symbol::new("standard"), Symbol::new("diff"), 1)
}
pub fn profile_diff_symbol() -> Symbol {
Symbol::qualified("profile", "diff")
}
pub fn standard_diff_stub(
cx: &Cx,
left: &LanguageProfile,
right: &LanguageProfile,
) -> Result<ProfileDiff> {
cx.require(&standard_diff_capability())?;
let left_organs = left
.organs
.iter()
.map(|organ| organ.organ.clone())
.collect::<Vec<_>>();
let right_organs = right
.organs
.iter()
.map(|organ| organ.organ.clone())
.collect::<Vec<_>>();
let mut differences = Vec::new();
push_difference(
&mut differences,
"reader",
Expr::Symbol(left.reader.clone()),
Expr::Symbol(right.reader.clone()),
);
push_difference(
&mut differences,
"lowering",
Expr::Symbol(left.lowering.clone()),
Expr::Symbol(right.lowering.clone()),
);
push_difference(
&mut differences,
"eval-policy",
Expr::Symbol(left.eval_policy.clone()),
Expr::Symbol(right.eval_policy.clone()),
);
push_difference(
&mut differences,
"organs",
symbols_expr(&left_organs),
symbols_expr(&right_organs),
);
push_difference(
&mut differences,
"numeric",
optional_symbol_expr(left.numeric_tower.as_ref()),
optional_symbol_expr(right.numeric_tower.as_ref()),
);
push_difference(
&mut differences,
"capabilities",
capability_expr(left),
capability_expr(right),
);
push_difference(
&mut differences,
"unsupported",
symbols_expr(&left.unsupported_forms),
symbols_expr(&right.unsupported_forms),
);
push_difference(
&mut differences,
"conformance-tests",
symbols_expr(&left.conformance_tests),
symbols_expr(&right.conformance_tests),
);
Ok(ProfileDiff {
left: left.symbol.clone(),
right: right.symbol.clone(),
status: if differences.is_empty() {
ProfileDiffStatus::Same
} else {
ProfileDiffStatus::Different
},
shared_organs: left_organs
.iter()
.filter(|organ| right_organs.contains(organ))
.cloned()
.collect(),
left_only_organs: left_organs
.iter()
.filter(|organ| !right_organs.contains(organ))
.cloned()
.collect(),
right_only_organs: right_organs
.iter()
.filter(|organ| !left_organs.contains(organ))
.cloned()
.collect(),
differences,
})
}
fn push_difference(differences: &mut Vec<ProfileDifference>, field: &str, left: Expr, right: Expr) {
if left != right {
differences.push(ProfileDifference {
field: Symbol::qualified("profile/diff", field.to_owned()),
left,
right,
});
}
}
fn symbols_expr(symbols: &[Symbol]) -> Expr {
Expr::List(symbols.iter().cloned().map(Expr::Symbol).collect())
}
fn optional_symbol_expr(symbol: Option<&Symbol>) -> Expr {
symbol.cloned().map(Expr::Symbol).unwrap_or(Expr::Nil)
}
fn capability_expr(profile: &LanguageProfile) -> Expr {
Expr::List(
profile
.capabilities
.iter()
.map(|capability| Expr::Symbol(capability.as_symbol()))
.collect(),
)
}