use cure_engine::kind::{BinOp, LitRef, NodeKind, RegionEvent, UnOp};
use cure_engine::{Effect, Lang};
use cure_tree::{rebuild_effects, ChildList, EventStore, NameTable};
type ToyId = u32;
#[derive(Clone, Debug)]
struct ToyNode {
kind: ToyKind,
name: Option<String>,
children: ChildList<ToyId>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum ToyKind {
Block,
VarDecl,
VarRef,
Assign,
Inc,
ForEach,
Catch,
Raw, Lit,
}
struct Toy {
nodes: Vec<ToyNode>,
names: NameTable,
events: EventStore<ToyId, u32>,
effects: Vec<Option<Effect>>,
}
impl Toy {
fn new() -> Self {
Toy {
nodes: Vec::new(),
names: NameTable::new(),
events: EventStore::new(),
effects: Vec::new(),
}
}
fn push(&mut self, kind: ToyKind, name: Option<&str>, children: Vec<ToyId>) -> ToyId {
let id = self.nodes.len() as ToyId;
self.nodes.push(ToyNode {
kind,
name: name.map(|s| s.to_string()),
children: ChildList::from_vec(children),
});
id
}
fn k(&self, id: ToyId) -> ToyKind {
self.nodes[id as usize].kind
}
}
impl Lang for Toy {
type Id = ToyId;
type NameKey = u32;
fn node_index(&self, id: ToyId) -> usize {
id as usize
}
fn id_of_index(&self, idx: usize) -> ToyId {
idx as u32
}
fn kind(&self, id: ToyId) -> NodeKind {
match self.k(id) {
ToyKind::Block => NodeKind::Block,
ToyKind::VarDecl => NodeKind::VarDecl,
ToyKind::VarRef => NodeKind::VarRef,
ToyKind::Assign => NodeKind::Assign,
ToyKind::Inc => NodeKind::Unary,
ToyKind::ForEach => NodeKind::ForEach,
ToyKind::Catch => NodeKind::Catch,
ToyKind::Raw => NodeKind::Raw,
ToyKind::Lit => NodeKind::Literal,
}
}
fn children(&self, id: ToyId) -> &[ToyId] {
self.nodes[id as usize].children.as_slice()
}
fn set_child(&mut self, parent: ToyId, index: usize, new: ToyId) {
self.nodes[parent as usize].children.set(index, new);
}
fn remove_child(&mut self, parent: ToyId, index: usize) {
self.nodes[parent as usize].children.remove(index);
}
fn splice(&mut self, node: ToyId, index: usize, remove: usize, insert: Vec<ToyId>) {
self.nodes[node as usize].children.splice(index, remove, insert);
}
fn build_return(&mut self, _v: Option<ToyId>) -> ToyId {
unreachable!()
}
fn build_block(&mut self, stmts: Vec<ToyId>) -> ToyId {
self.push(ToyKind::Block, None, stmts)
}
fn build_unary(&mut self, _op: UnOp, _operand: ToyId) -> ToyId {
unreachable!()
}
fn build_bool(&mut self, _v: bool) -> ToyId {
self.push(ToyKind::Lit, None, vec![])
}
fn build_int(&mut self, _v: i64, _wide: bool) -> ToyId {
self.push(ToyKind::Lit, None, vec![])
}
fn build_bin(&mut self, _op: BinOp, _l: ToyId, _r: ToyId) -> ToyId {
unreachable!()
}
fn build_ternary(&mut self, _c: ToyId, _a: ToyId, _b: ToyId) -> ToyId {
unreachable!()
}
fn build_assign(&mut self, target: ToyId, value: ToyId) -> ToyId {
self.push(ToyKind::Assign, None, vec![target, value])
}
fn build_str(&mut self, _s: &str) -> ToyId {
self.push(ToyKind::Lit, None, vec![])
}
fn build_char(&mut self, _c: char) -> ToyId {
self.push(ToyKind::Lit, None, vec![])
}
fn copy_subtree(&mut self, _id: ToyId) -> ToyId {
unreachable!()
}
fn effect(&self, id: ToyId) -> Effect {
self.effects[id as usize].unwrap_or(Effect::Unknown)
}
fn invalidate_effect(&mut self, id: ToyId) {
if let Some(slot) = self.effects.get_mut(id as usize) {
*slot = None;
}
self.events.invalidate(id as usize);
}
fn region_events(&self, id: ToyId) -> Option<&[RegionEvent<ToyId, u32>]> {
self.events.slot(id as usize)
}
fn var_key(&self, id: ToyId) -> Option<u32> {
self.names.var_key(id as usize)
}
fn is_opaque(&self, id: ToyId) -> bool {
self.k(id) == ToyKind::Raw
}
fn own_effect(&self, id: ToyId) -> Effect {
match self.k(id) {
ToyKind::Lit | ToyKind::Block | ToyKind::VarDecl => Effect::Pure,
ToyKind::VarRef => Effect::MayRead,
ToyKind::Assign | ToyKind::Inc => Effect::MayWrite,
ToyKind::Raw => Effect::Unknown,
_ => Effect::Pure,
}
}
fn is_bool(&self, _id: ToyId) -> bool {
false
}
fn is_exact_int(&self, _id: ToyId) -> bool {
false
}
fn bin_op(&self, _id: ToyId) -> Option<BinOp> {
None
}
fn un_op(&self, id: ToyId) -> Option<UnOp> {
if self.k(id) == ToyKind::Inc {
Some(UnOp::PreInc)
} else {
None
}
}
fn literal(&self, _id: ToyId) -> Option<LitRef<'_>> {
None
}
fn var_name(&self, id: ToyId) -> Option<&str> {
self.nodes[id as usize].name.as_deref()
}
fn prepare(&mut self, root: ToyId) {
let n = self.nodes.len();
let slots = self.names.key_slots();
if slots < n {
let names: Vec<Option<String>> = (slots..n)
.map(|i| self.var_name(i as u32).map(|s| s.to_string()))
.collect();
self.names.extend_owned(slots, &names);
}
self.events.resize(n);
if !self.events.is_clean() {
self.events.mark_clean();
for sr in cure_tree::stmt_roots(self, root) {
let idx = sr as usize;
if self.events.slot(idx).is_none() {
if let Some(ev) = cure_tree::collect_region_events(self, sr) {
self.events.put(idx, ev);
}
}
}
}
let effects = rebuild_effects(self, n);
self.effects = effects;
}
}
fn sample() -> (Toy, ToyId) {
let mut t = Toy::new();
let x_decl = t.push(ToyKind::VarDecl, Some("x"), vec![]); let x_ref = t.push(ToyKind::VarRef, Some("x"), vec![]);
let foo = t.push(ToyKind::Lit, None, vec![]);
let assign = t.push(ToyKind::Assign, None, vec![x_ref, foo]); let y_ref = t.push(ToyKind::VarRef, Some("y"), vec![]);
let inc = t.push(ToyKind::Inc, None, vec![y_ref]); let s_decl = t.push(ToyKind::ForEach, Some("s"), vec![]); let s_ref = t.push(ToyKind::VarRef, Some("s"), vec![]);
let use_stmt = t.push(ToyKind::Assign, None, vec![s_ref, foo]); let _ = use_stmt;
let raw = t.push(ToyKind::Raw, None, vec![]);
let block = t.push(
ToyKind::Block,
None,
vec![x_decl, assign, inc, s_decl, raw],
);
(t, block)
}
#[test]
fn toy_events_match_semantics() {
let (mut t, root) = sample();
t.prepare(root);
let roots = cure_tree::stmt_roots(&t, root);
assert_eq!(roots.len(), 5);
let x_key = t.names.intern("x");
let ev = t.region_events(roots[0]).unwrap();
assert_eq!(ev.len(), 2);
assert_eq!(ev[0].key, x_key);
assert!(matches!(ev[0].kind, cure_engine::kind::EventKind::Write));
assert!(matches!(ev[1].kind, cure_engine::kind::EventKind::Shadow));
let ev = t.region_events(roots[1]).unwrap();
assert_eq!(ev.len(), 1);
assert!(matches!(ev[0].kind, cure_engine::kind::EventKind::Write));
let y_key = t.names.intern("y");
let ev = t.region_events(roots[2]).unwrap();
assert_eq!(ev.len(), 2);
assert_eq!(ev[0].key, y_key);
assert!(matches!(ev[0].kind, cure_engine::kind::EventKind::Use));
assert!(matches!(ev[1].kind, cure_engine::kind::EventKind::Write));
let ev = t.region_events(roots[3]).unwrap();
assert_eq!(ev.len(), 2);
assert!(t.region_events(roots[4]).is_none());
}
#[test]
fn toy_effect_rebuild() {
let (mut t, root) = sample();
t.prepare(root);
assert_eq!(t.effect(root), Effect::Unknown);
let roots = cure_tree::stmt_roots(&t, root);
assert_eq!(t.effect(roots[0]), Effect::Pure);
assert_eq!(t.effect(roots[1]), Effect::MayWrite);
}
#[test]
fn toy_invalidate_and_lazy_rebuild() {
let (mut t, root) = sample();
t.prepare(root);
let roots = cure_tree::stmt_roots(&t, root);
let before = t.region_events(roots[1]).unwrap().len();
t.invalidate_effect(roots[1]);
assert!(t.region_events(roots[1]).is_none());
t.prepare(root);
let after = t.region_events(roots[1]).unwrap().len();
assert_eq!(before, after);
}
#[test]
fn child_list_inline_and_spill() {
let mut cl: ChildList<u32> = ChildList::new();
assert!(cl.is_empty());
cl = ChildList::from_vec(vec![1, 2, 3]);
assert_eq!(cl.as_slice(), &[1, 2, 3]); cl = ChildList::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(cl.as_slice(), &[1, 2, 3, 4, 5]);
cl.remove(4); assert_eq!(cl.as_slice(), &[1, 2, 3, 4]);
cl.remove(3);
assert_eq!(cl.as_slice(), &[1, 2, 3]);
cl.splice(1, 1, vec![9, 9]);
assert_eq!(cl.as_slice(), &[1, 9, 9, 3]);
}
#[test]
fn name_table_intern_and_keys() {
let mut nt = NameTable::new();
let a = nt.intern("foo");
let b = nt.intern("foo");
let c = nt.intern("bar");
assert_eq!(a, b);
assert_ne!(a, c);
assert_eq!(nt.name(a), "foo");
assert_eq!(nt.name(c), "bar");
nt.extend_owned(0, &[Some("x".into()), None, Some("y".into())]);
assert_eq!(nt.var_key(0), Some(a.max(c) + 1)); assert_eq!(nt.var_key(1), None);
assert!(nt.var_key(2).is_some());
}