use super::*;
use std::collections::BTreeMap;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct MapContainer {
do_rebuild_keys: bool,
do_rebuild_vals: bool,
pub data: BTreeMap<Value, Value>,
}
impl ContainerValue for MapContainer {
fn rebuild_contents(&mut self, rebuilder: &dyn ValueRebuilder) -> bool {
let mut changed = false;
if self.do_rebuild_keys {
self.data = self
.data
.iter()
.map(|(old, v)| {
let new = rebuilder.rebuild_val(*old);
changed |= *old != new;
(new, *v)
})
.collect();
}
if self.do_rebuild_vals {
for old in self.data.values_mut() {
let new = rebuilder.rebuild_val(*old);
changed |= *old != new;
*old = new;
}
}
changed
}
fn iter(&self) -> impl Iterator<Item = Value> + '_ {
self.data.iter().flat_map(|(k, v)| [k, v]).copied()
}
}
fn map_term_to_btreemap<'a>(
termdag: &'a TermDag,
term_id: TermId,
) -> Option<BTreeMap<OrdTerm<'a>, TermId>> {
match termdag.get(term_id) {
Term::App(head, args) if head == "map-of" => map_of_args_to_btreemap(termdag, args),
_ => None,
}
}
fn map_of_args_to_btreemap<'a>(
termdag: &'a TermDag,
args: &[TermId],
) -> Option<BTreeMap<OrdTerm<'a>, TermId>> {
if !args.len().is_multiple_of(2) {
return None;
}
Some(
args.chunks_exact(2)
.map(|kv| (termdag.ord_term(kv[0]), kv[1]))
.collect(),
)
}
fn map_term_args(map: BTreeMap<OrdTerm<'_>, TermId>) -> Vec<TermId> {
map.into_iter().flat_map(|(k, v)| [k.id(), v]).collect()
}
fn normalize_map_term(termdag: &mut TermDag, args: &[TermId]) -> Option<TermId> {
let flat = map_term_args(map_of_args_to_btreemap(termdag, args)?);
Some(termdag.app("map-of".to_string(), flat))
}
#[derive(Clone, Debug)]
pub struct MapSort {
name: String,
key: ArcSort,
value: ArcSort,
}
impl MapSort {
pub fn key(&self) -> ArcSort {
self.key.clone()
}
pub fn value(&self) -> ArcSort {
self.value.clone()
}
}
impl Presort for MapSort {
fn presort_name() -> &'static str {
"Map"
}
fn reserved_primitives() -> Vec<&'static str> {
vec![
"map-empty",
"map-of",
"map-insert",
"map-get",
"map-not-contains",
"map-contains",
"map-remove",
"map-length",
]
}
fn make_sort(
typeinfo: &mut TypeInfo,
name: String,
args: &[Expr],
span: Span,
) -> Result<ArcSort, TypeError> {
if let [Expr::Var(k_span, k), Expr::Var(v_span, v)] = args {
let k = typeinfo
.get_sort_by_name(k)
.ok_or(TypeError::UndefinedSort(k.clone(), k_span.clone()))?;
let v = typeinfo
.get_sort_by_name(v)
.ok_or(TypeError::UndefinedSort(v.clone(), v_span.clone()))?;
let out = Self {
name,
key: k.clone(),
value: v.clone(),
};
Ok(out.to_arcsort())
} else {
Err(TypeError::BadPresortArguments(
Self::presort_name().to_owned(),
span,
))
}
}
}
impl ContainerSort for MapSort {
type Container = MapContainer;
fn name(&self) -> &str {
&self.name
}
fn inner_sorts(&self) -> Vec<ArcSort> {
vec![self.key.clone(), self.value.clone()]
}
fn is_eq_container_sort(&self) -> bool {
self.key.is_eq_sort()
|| self.value.is_eq_sort()
|| self.key.is_eq_container_sort()
|| self.value.is_eq_container_sort()
}
fn inner_values(
&self,
container_values: &ContainerValues,
value: Value,
) -> Vec<(ArcSort, Value)> {
let val = container_values
.get_val::<MapContainer>(value)
.unwrap()
.clone();
val.data
.iter()
.flat_map(|(k, v)| [(self.key.clone(), *k), (self.value.clone(), *v)])
.collect()
}
fn register_primitives(&self, eg: &mut EGraph) {
let arc = self.clone().to_arcsort();
let map_empty_validator = |termdag: &mut TermDag, _args: &[TermId]| -> Option<TermId> {
Some(termdag.app("map-of".into(), vec![]))
};
let map_insert_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [map, key, value] = args else {
return None;
};
let mut map = map_term_to_btreemap(termdag, *map)?;
map.insert(termdag.ord_term(*key), *value);
let flat = map_term_args(map);
Some(termdag.app("map-of".into(), flat))
};
let map_get_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [map, key] = args else { return None };
map_term_to_btreemap(termdag, *map)?
.get(&termdag.ord_term(*key))
.copied()
};
let map_length_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [map] = args else { return None };
let len = map_term_to_btreemap(termdag, *map)?.len() as i64;
Some(termdag.lit(Literal::Int(len)))
};
let map_contains_validator = |termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [map, key] = args else { return None };
let contains =
map_term_to_btreemap(termdag, *map)?.contains_key(&termdag.ord_term(*key));
contains.then(|| termdag.lit(Literal::Unit))
};
let map_not_contains_validator =
|termdag: &mut TermDag, args: &[TermId]| -> Option<TermId> {
let [map, key] = args else { return None };
let contains =
map_term_to_btreemap(termdag, *map)?.contains_key(&termdag.ord_term(*key));
(!contains).then(|| termdag.lit(Literal::Unit))
};
add_primitive_with_validator!(eg, "map-empty" = {self.clone(): MapSort} || -> @MapContainer (arc) { MapContainer {
do_rebuild_keys: self.ctx.key.is_eq_sort() || self.ctx.key.is_eq_container_sort(),
do_rebuild_vals: self.ctx.value.is_eq_sort() || self.ctx.value.is_eq_container_sort(),
data: BTreeMap::new()
} }, map_empty_validator);
eg.add_pure_primitive(
MapOf {
name: "map-of".to_string(),
map: arc.clone(),
key: self.key.clone(),
value: self.value.clone(),
},
Some(std::sync::Arc::new(normalize_map_term)),
);
add_primitive_with_validator!(eg, "map-get" = | xs: @MapContainer (arc), x: # (self.key()) | -?> # (self.value()) { xs.data.get(&x).copied() }, map_get_validator);
add_primitive_with_validator!(eg, "map-insert" = |mut xs: @MapContainer (arc), x: # (self.key()), y: # (self.value())| -> @MapContainer (arc) {{ xs.data.insert(x, y); xs }}, map_insert_validator);
add_primitive!(eg, "map-remove" = |mut xs: @MapContainer (arc), x: # (self.key()) | -> @MapContainer (arc) {{ xs.data.remove(&x); xs }});
add_primitive_with_validator!(eg, "map-length" = |xs: @MapContainer (arc)| -> i64 { xs.data.len() as i64 }, map_length_validator);
add_primitive_with_validator!(eg, "map-contains" = |xs: @MapContainer (arc), x: # (self.key())| -?> () { ( xs.data.contains_key(&x)).then_some(()) }, map_contains_validator);
add_primitive_with_validator!(eg, "map-not-contains" = |xs: @MapContainer (arc), x: # (self.key())| -?> () { (!xs.data.contains_key(&x)).then_some(()) }, map_not_contains_validator);
}
fn reconstruct_termdag(
&self,
_container_values: &ContainerValues,
_value: Value,
termdag: &mut TermDag,
element_terms: Vec<TermId>,
) -> TermId {
normalize_map_term(termdag, &element_terms).expect("map elements come in key/value pairs")
}
fn rebuild_container_normalizer(&self) -> Option<(String, PrimitiveValidator)> {
Some(("map-of".to_owned(), Arc::new(normalize_map_term)))
}
fn serialized_name(&self, _container_values: &ContainerValues, _: Value) -> String {
"map-of".to_owned()
}
}
#[derive(Clone)]
struct MapOf {
name: String,
map: ArcSort,
key: ArcSort,
value: ArcSort,
}
impl Primitive for MapOf {
fn name(&self) -> &str {
&self.name
}
fn get_type_constraints(&self, span: &Span) -> Box<dyn TypeConstraint> {
Box::new(MapOfTypeConstraint {
name: self.name.clone(),
key: self.key.clone(),
value: self.value.clone(),
map: self.map.clone(),
span: span.clone(),
})
}
}
impl PurePrim for MapOf {
fn apply<'a, 'db>(&self, mut state: PureState<'a, 'db>, args: &[Value]) -> Option<Value> {
let mut data = BTreeMap::new();
for chunk in args.chunks(2) {
if let [k, v] = chunk {
data.insert(*k, *v);
}
}
let mc = MapContainer {
do_rebuild_keys: self.key.is_eq_sort() || self.key.is_eq_container_sort(),
do_rebuild_vals: self.value.is_eq_sort() || self.value.is_eq_container_sort(),
data,
};
Some(state.register_container(mc))
}
}
struct MapOfTypeConstraint {
name: String,
key: ArcSort,
value: ArcSort,
map: ArcSort,
span: Span,
}
impl TypeConstraint for MapOfTypeConstraint {
fn get(
&self,
arguments: &[AtomTerm],
_typeinfo: &TypeInfo,
) -> Vec<Box<dyn Constraint<AtomTerm, ArcSort>>> {
let arity_mismatch = |expected: usize| {
vec![constraint::impossible(
constraint::ImpossibleConstraint::ArityMismatch {
atom: Atom {
span: self.span.clone(),
head: self.name.clone(),
args: arguments.to_vec(),
},
expected,
},
)]
};
let Some((out, inputs)) = arguments.split_last() else {
return arity_mismatch(1);
};
if inputs.len() % 2 != 0 {
return arity_mismatch(inputs.len() + 2);
}
let mut cs: Vec<Box<dyn Constraint<AtomTerm, ArcSort>>> =
vec![constraint::assign(out.clone(), self.map.clone())];
for (i, arg) in inputs.iter().enumerate() {
let sort = if i % 2 == 0 {
self.key.clone()
} else {
self.value.clone()
};
cs.push(constraint::assign(arg.clone(), sort));
}
cs
}
}