use super::Context;
use super::InternalCompilerTag;
use crate::contract::actions::Guard;
use crate::contract::actions::SimpGen;
use crate::contract::CompilationError;
use sapio_base::effects::PathFragment;
use sapio_base::simp::GuardLT;
use sapio_base::simp::SIMPAttachableAt;
use sapio_base::Clause;
use std::collections::BTreeMap;
use std::sync::Arc;
pub type GuardSimps = Vec<Arc<dyn SIMPAttachableAt<GuardLT>>>;
pub(crate) enum CacheEntry<T> {
Cached(Clause, GuardSimps),
Fresh(fn(&T, Context) -> Clause, Option<SimpGen<T>>),
}
pub(crate) struct GuardCache<T> {
cache: BTreeMap<usize, Option<CacheEntry<T>>>,
}
impl<T> GuardCache<T> {
pub fn new() -> Self {
GuardCache {
cache: BTreeMap::new(),
}
}
pub(crate) fn create_entry(
g: Option<Guard<T>>,
t: &T,
ctx: Context,
simp_ctx: Context,
) -> Result<Option<CacheEntry<T>>, CompilationError> {
match g {
Some(Guard::Cache(f, Some(simp_gen))) => {
Ok(Some(CacheEntry::Cached(f(t, ctx), simp_gen(t, simp_ctx)?)))
}
Some(Guard::Cache(f, None)) => Ok(Some(CacheEntry::Cached(f(t, ctx), vec![]))),
Some(Guard::Fresh(f, simp_gen)) => Ok(Some(CacheEntry::Fresh(f, simp_gen))),
None => Ok(None),
}
}
pub(crate) fn get(
&mut self,
t: &T,
f: fn() -> Option<Guard<T>>,
ctx: Context,
simp_ctx: Context,
) -> Result<Option<(Clause, GuardSimps)>, CompilationError> {
let mut entry = self.cache.entry(f as usize);
let r = match entry {
std::collections::btree_map::Entry::Vacant(v) => {
let ent = Self::create_entry(
f(),
t,
ctx.internal_clone(InternalCompilerTag { _secret: () }),
simp_ctx.internal_clone(InternalCompilerTag { _secret: () }),
)?;
v.insert(ent)
}
std::collections::btree_map::Entry::Occupied(ref mut o) => o.get_mut(),
};
match r {
Some(CacheEntry::Cached(s, v)) => Ok(Some((s.clone(), v.to_vec()))),
Some(CacheEntry::Fresh(f, s)) => Ok(Some((
f(t, ctx),
match s {
Some(f2) => f2(t, simp_ctx)?,
None => vec![],
},
))),
None => Ok(None),
}
}
}
pub(crate) fn create_guards<T>(
self_ref: &T,
mut ctx: Context,
guards: &[fn() -> Option<Guard<T>>],
gc: &mut GuardCache<T>,
) -> Result<(Clause, Vec<(Clause, GuardSimps)>), CompilationError> {
let v = guards
.iter()
.zip((0..).flat_map(|i| {
let new = ctx.derive(PathFragment::Branch(i)).ok()?;
let simp = ctx.derive(PathFragment::Metadata).ok()?;
Some((new, simp))
}))
.filter_map(|(x, (c, simp_c))| gc.get(self_ref, *x, c, simp_c).transpose())
.collect::<Result<Vec<_>, _>>()?;
let mut clauses: Vec<_> = v
.iter()
.map(|x| &x.0)
.filter(|x| **x != Clause::Trivial)
.cloned()
.collect(); if clauses.is_empty() {
Ok((Clause::Trivial, v))
} else if clauses.len() == 1 {
Ok((clauses.pop().unwrap(), v))
} else {
Ok((Clause::And(clauses), v))
}
}