use core::any::TypeId;
use core::marker::PhantomData;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::fmt::Debug;
use std::fmt::{Formatter,Result};
use std::fmt;
use std::hash::{Hash,Hasher};
use std::collections::hash_map::DefaultHasher;
use std::mem::replace;
use std::mem::transmute;
use std::rc::Rc;
use std::fmt::Write;
use crate::macros::{ProgPt};
use crate::reflect;
thread_local!(static GLOBALS: RefCell<Globals> = RefCell::new(Globals{engine:Engine::Naive}));
thread_local!(static UNIT_NAME: Name = Name{ hash:0, symbol: Rc::new(NameSym::Unit) });
struct TraceSt { stack:Vec<Box<Vec<reflect::trace::Trace>>>, }
thread_local!(static TRACES: RefCell<Option<TraceSt>> = RefCell::new( None ));
fn my_hash<T>(obj: T) -> u64
where T: Hash
{
let mut hasher = DefaultHasher::new();
obj.hash(&mut hasher);
hasher.finish()
}
pub mod reflect_dcg {
use crate::reflect::*;
pub use crate::parse_val;
use std::fmt::{Write};
use super::{TraceSt,TRACES,GLOBALS,Engine};
use crate::adapton::engine::Name;
pub fn debug_begin(n:Option<Name>, msg:Option<String>) {
super::debug_begin(n, msg)
}
pub fn debug_end() {
super::debug_end()
}
pub fn debug_effect(n:Option<Name>, msg:Option<String>) {
super::debug_effect(n, msg)
}
pub fn string_of_name (n:&Name) -> String {
let mut output = String::from(""); write_name(&mut output, n); output
}
pub fn string_of_path (p:&Path) -> String {
let mut output = String::from(""); write_path(&mut output, &p); output
}
pub fn string_of_loc (l:&Loc) -> String {
let mut output = String::from(""); write_loc (&mut output, &l); output
}
pub fn write_name<W:Write> (w:&mut W, n:&Name) {
super::write_namesym(w, &n.symbol).unwrap();
}
pub fn write_path<W:Write> (w:&mut W, p:&Path) {
write!(w, "__").unwrap();
for n in p.iter() {
write_name(w, n);
write!(w, "__").unwrap();
}
}
pub fn write_loc<W:Write> (w:&mut W, l:&Loc) {
write_path(w, &l.path);
write!(w, "_").unwrap();
write_name(w, &l.name);
}
pub fn dcg_reflect_now() -> Option<DCG> {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg) => Some((*dcg.borrow()).reflect()),
Engine::Naive => None,
}
})
}
pub fn dcg_reflect_begin() {
TRACES.with(|tr| {
let check = match *tr.borrow() {
None => true,
Some(_) => false };
if check {
*tr.borrow_mut() = Some(TraceSt{stack:vec![Box::new(vec![])]})
} else {
panic!("cannot currently nest calls to dcg_reflect_begin().")
}
})
}
pub fn dcg_reflect_end() -> Vec<trace::Trace> {
TRACES.with(|tr| {
let traces = match *tr.borrow_mut() {
None => panic!("dcg_reflect_end() without a corresponding dcg_reflect_begin()."),
Some(ref mut tr) => {
assert_eq!(tr.stack.len(), 1);
tr.stack.pop()
}
};
match traces {
None => unreachable!(),
Some(traces) => {
*tr.borrow_mut() = None;
*traces
}
}
})
}
}
use crate::reflect::Reflect;
macro_rules! dcg_effect_begin {
( $eff:expr, $loc:expr, $succ:expr, $has_extent:expr ) => {{
TRACES.with(|tr| {
match *tr.borrow_mut() {
None => (),
Some(ref mut ts) => {
match ts.stack.last_mut() {
None => unreachable!(),
Some(ref mut ts) => {
ts.push( reflect::trace::Trace{
extent: Box::new(vec![]),
effect:$eff,
edge: reflect::trace::EffectEdge::Fwd(
reflect::trace::Edge{
loc: $loc.reflect(),
succ: $succ.reflect(),
})})}};
if $has_extent {
ts.stack.push(Box::new(vec![]))
} else { }
}
}})
}}
;
( $eff:expr, $has_extent:expr ) => {{
TRACES.with(|tr| {
match *tr.borrow_mut() {
None => (),
Some(ref mut ts) => {
match ts.stack.last_mut() {
None => unreachable!(),
Some(ref mut ts) => {
ts.push( reflect::trace::Trace{
extent: Box::new(vec![]),
effect:$eff,
edge: reflect::trace::EffectEdge::None,
})}};
if $has_extent {
ts.stack.push(Box::new(vec![]))
} else { }
}
}})
}}
;
( $eff:expr, $loc:expr, $succ:expr ) => {{
dcg_effect_begin!($eff, $loc, $succ, true)
}}
}
macro_rules! dcg_effect_end {
() => {{
TRACES.with(|tr| {
match *tr.borrow_mut() {
None => (),
Some(ref mut tr) => {
let trs = tr.stack.pop();
match (trs, tr.stack.last_mut()) {
(None, _) => unreachable!(),
(_, None) => unreachable!(),
(Some(parent_extent), Some(trs)) =>
match trs.last_mut() {
None => unreachable!(),
Some(parent) => { assert_eq!(parent.extent.len(), 0);
parent.extent = parent_extent }
}
}
}
}
})
}}
}
macro_rules! dcg_effect {
( $eff:expr, $loc:expr, $succ:expr ) => {{
dcg_effect_begin!($eff, $loc, $succ, false)
}}
}
macro_rules! current_loc {
( $st:expr ) => {{
match ($st).stack.last() {
None => None,
Some(frame) => Some(&frame.loc),
}
}}
}
fn debug_begin(n:Option<Name>, msg:Option<String>) {
dcg_effect_begin!(reflect::trace::Effect::Debug(n, msg), true);
}
fn debug_end() {
dcg_effect_end!();
}
fn debug_effect(n:Option<Name>, msg:Option<String>) {
dcg_effect_begin!(reflect::trace::Effect::Debug(n, msg), false);
}
#[derive(PartialEq,Eq,Clone)]
pub struct Name {
hash : u64,
symbol : Rc<NameSym>,
}
impl Debug for Name {
fn fmt(&self, f:&mut Formatter) -> Result { self.symbol.fmt(f) }
}
impl Hash for Name {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.hash.hash(state)
}
}
#[derive(PartialEq,Eq,Clone)]
struct Loc {
hash : u64,
path : Rc<Path>,
id : Rc<ArtId>,
}
impl Debug for Loc {
fn fmt(&self, f:&mut Formatter) -> Result {
write!(f,"Loc {{ path:[{:?}], id:{:?} }}", self.path, self.id)
}
}
impl Hash for Loc {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.hash.hash(state)
}
}
impl reflect::Reflect<reflect::Loc> for Loc {
fn reflect(&self) -> reflect::Loc {
reflect::Loc {
path:self.path.reflect(),
name:match *self.id {
ArtId::Structural(ref hash) => name_of_hash64(*hash),
ArtId::Nominal(ref name) => name.clone(),
}
}
}
}
#[derive(Hash,PartialEq,Eq,Clone)]
enum ArtId {
Structural(u64),
Nominal(Name),
}
impl Debug for ArtId {
fn fmt(&self, f:&mut Formatter) -> Result {
match *self {
ArtId::Structural(ref hash) => write!(f, "{}", hash),
ArtId::Nominal(ref name) => write!(f, "{:?}", name),
}
}
}
#[derive(Debug)]
pub struct Flags {
pub use_purity_optimization : bool,
pub ignore_nominal_use_structural : bool,
pub check_dcg_is_wf : bool,
pub write_dcg : bool,
pub gmlog_dcg : bool,
}
struct Globals {
engine: Engine,
}
#[derive(Debug,Clone)]
pub enum Engine {
DCG(RefCell<DCG>),
Naive
}
#[derive(Debug)]
pub struct DCG {
pub flags : Flags,
table : HashMap<Rc<Loc>, Box<dyn GraphNode>>,
stack : Vec<Frame>,
path : Rc<Path>,
dcg_count : usize,
dcg_hash : u64,
}
impl reflect::Reflect<reflect::DCG> for DCG {
fn reflect(&self) -> reflect::DCG {
reflect::DCG{
table:{
let mut table = HashMap::new();
for (loc, gn) in self.table.iter() {
let _ = table.insert(loc.reflect(), gn.reflect());
}; table
},
stack:self.stack.iter()
.map(|ref frame| frame.reflect() )
.collect::<Vec<_>>(),
path:self.path.reflect(),
}
}
}
impl Hash for DCG { fn hash<H>(&self, _state: &mut H) where H: Hasher { unimplemented!() }}
impl Eq for DCG { }
impl PartialEq for DCG { fn eq(&self, _other:&Self) -> bool { unimplemented!() } }
impl Clone for DCG { fn clone(&self) -> Self { unimplemented!() } }
#[derive(Hash,PartialEq,Eq,Clone,Debug)]
enum NameSym {
Unit,
Hash64,
String(String),
Usize(usize),
Isize(isize),
Pair(Rc<NameSym>,Rc<NameSym>),
ForkL(Rc<NameSym>),
ForkR(Rc<NameSym>),
}
fn write_namesym<W:Write>(w:&mut W, n:&NameSym) -> Result {
match *n {
NameSym::Unit => write!(w, "▲"),
NameSym::Hash64 => write!(w, "(Hash64)"),
NameSym::String(ref s) => write!(w, "{}", s),
NameSym::Usize(ref n) => write!(w, "{}", n),
NameSym::Isize(ref n) => write!(w, "{}", n),
NameSym::Pair(ref l, ref r) => { write_namesym(w, l).unwrap(); write!(w, "-").unwrap(); write_namesym(w, r) },
NameSym::ForkL(ref s) => { write_namesym(w, s).unwrap(); write!(w, "-l") },
NameSym::ForkR(ref s) => { write_namesym(w, s).unwrap(); write!(w, "-r") },
}
}
#[derive(Hash,PartialEq,Eq,Clone)]
enum Path {
Empty,
Child(Rc<Path>,Name),
}
impl reflect::Reflect<reflect::Path> for Path {
fn reflect(&self) -> reflect::Path {
match *self {
Path::Empty => vec![],
Path::Child(ref path, ref name) => {
let mut p = path.reflect();
p.push(name.clone());
p
}
}
}
}
impl Debug for Path {
fn fmt(&self, f:&mut Formatter) -> Result {
match *self {
Path::Empty => write!(f, ""),
Path::Child(ref p, ref n) => write!(f, "{:?},{:?}", p, n),
}
}
}
trait GraphNode : Debug + reflect::Reflect<reflect::Node> {
fn res_typeid (self:&Self) -> TypeId ;
fn preds_alloc<'r> (self:&Self) -> Vec<Rc<Loc>> ;
fn preds_obs<'r> (self:&Self) -> Vec<(Rc<Loc>, Option<Rc<Box<dyn DCGDep>>>)> ;
fn preds_insert<'r>(self:&'r mut Self, _: Effect, _: &Rc<Loc>, _: Option<Rc<Box<dyn DCGDep>>>) -> () ;
fn preds_remove<'r>(self:&'r mut Self, _: &Rc<Loc>) -> () ;
fn succs_def<'r> (self:&Self) -> bool ;
fn succs_mut<'r> (self:&'r mut Self) -> &'r mut Vec<Succ> ;
fn succs<'r> (self:&'r Self) -> &'r Vec<Succ> ;
fn hash_seeded (self:&Self, _: u64) -> u64 ;
}
#[derive(Debug,Clone)]
struct Frame {
loc : Rc<Loc>,
succs : Vec<(Succ, Option<Rc<Box<dyn DCGDep>>>)>,
}
impl reflect::Reflect<reflect::Frame> for Frame {
fn reflect(&self) -> reflect::Frame {
reflect::Frame{
loc:self.loc.reflect(),
succs:self.succs.reflect(),
}
}
}
#[derive(Debug,Clone)]
struct Succ {
dirty : bool,
loc : Rc<Loc>,
effect : Effect,
dep : Rc<Box<dyn DCGDep>>,
}
#[derive(Debug,Clone)]
struct Pred {
loc : Rc<Loc>,
effect : Effect,
dep : Option<Rc<Box<dyn DCGDep>>>,
}
impl reflect::Reflect<reflect::Succ> for Succ {
fn reflect(&self) -> reflect::Succ {
reflect::Succ {
dirty:self.dirty,
loc:self.loc.reflect(),
effect:self.effect.reflect(),
value:reflect::Val::ValTODO,
is_dup:false,
}
}
}
impl reflect::Reflect<Vec<reflect::Succ>> for Vec<Succ> {
fn reflect(&self) -> Vec<reflect::Succ> {
self.iter().map(|ref x| x.reflect()).collect::<Vec<_>>()
}
}
impl reflect::Reflect<Vec<reflect::Succ>> for Vec<(Succ, Option<Rc<Box<dyn DCGDep>>>)> {
fn reflect(&self) -> Vec<reflect::Succ> {
self.iter().map(|ref x| x.0.reflect()).collect::<Vec<_>>()
}
}
#[derive(PartialEq,Eq,Debug,Clone,Hash)]
enum Effect {
Observe,
Allocate,
}
impl reflect::Reflect<reflect::Effect> for Effect {
fn reflect(&self) -> reflect::Effect {
match *self {
Effect::Observe => reflect::Effect::Force,
Effect::Allocate => reflect::Effect::Alloc,
}
}
}
struct DCGRes {
changed : bool,
}
trait DCGDep : Debug {
fn is_absmap(self:&Self) -> Option<TypeId> ;
fn dirty (self:&Self, g:&mut DCG, loc:&Rc<Loc>) -> DCGRes ;
fn clean (self:&Self, g:&RefCell<DCG>, loc:&Rc<Loc>) -> DCGRes ;
}
impl Hash for Succ {
fn hash<H>(&self, hasher: &mut H) where H: Hasher {
self.dirty.hash( hasher );
self.loc.hash( hasher );
self.effect.hash( hasher );
}
}
impl Hash for Pred {
fn hash<H>(&self, hasher: &mut H) where H: Hasher {
self.loc.hash( hasher );
self.effect.hash( hasher );
}
}
#[allow(dead_code)]
#[derive(Debug,Hash)]
enum Node<Res> {
Comp(CompNode<Res>),
Pure(PureNode<Res>),
Mut(MutNode<Res>),
}
impl<X:Debug> reflect::Reflect<reflect::Node> for Node<X> {
fn reflect(&self) -> reflect::Node {
use crate::parse_val::parse_val;
match *self {
Node::Comp(ref n) => {
reflect::Node::Comp(
reflect::CompNode{
preds:n.preds.reflect(),
succs:n.succs.reflect(),
prog_pt:n.producer.prog_pt().clone(),
value:match n.res {
Some(ref v) => Some( parse_val(v) ),
None => None
}
})
},
Node::Pure(ref n) => {
reflect::Node::Pure(
reflect::PureNode {
value:parse_val( &n.val ),
})
},
Node::Mut(ref n) => {
reflect::Node::Ref(
reflect::RefNode {
preds:n.preds.reflect(),
value:parse_val( &n.val ),
})
},
}
}
}
#[derive(Debug,Hash)]
struct PureNode<T> {
val : T,
}
#[derive(Debug,Hash)]
struct MutNode<T> {
preds : Vec<Pred>,
val : T,
}
struct CompNode<Res> {
preds : Vec<Pred>,
succs : Vec<Succ>,
producer : Box<dyn Producer<Res>>,
res : Option<Res>,
}
impl reflect::Reflect<Vec<reflect::Pred>> for Vec<Pred> {
fn reflect(&self) -> Vec<reflect::Pred> {
self.iter().map(|pred|
reflect::Pred{
effect:pred.effect.reflect(),
loc:pred.loc.reflect()
}).collect::<Vec<_>>()
}
}
impl reflect::Reflect<Vec<reflect::Loc>> for Vec<Rc<Loc>> {
fn reflect(&self) -> Vec<reflect::Loc> {
self.iter().map(|loc| loc.reflect()).collect::<Vec<_>>()
}
}
#[derive(Hash,Debug,PartialEq,Eq,Clone)]
pub enum NameChoice {
Naive,
Eager,
Structural,
Nominal(Name),
}
trait Producer<Res> : Debug {
fn produce(self:&Self) -> Res;
fn copy(self:&Self) -> Box<dyn Producer<Res>>;
fn eq(self:&Self, other:&dyn Producer<Res>) -> bool;
fn prog_pt<'r>(self:&'r Self) -> &'r ProgPt;
}
trait Consumer<Arg> : Debug {
fn consume(self:&mut Self, _: Arg);
fn get_arg(self:&mut Self) -> Arg;
}
#[derive(Clone)]
struct App<Arg:Debug,Spurious,Res> {
prog_pt: ProgPt,
fn_box: Rc<Box<dyn Fn(Arg, Spurious) -> Res>>,
arg: Arg,
spurious: Spurious,
}
impl<Arg:Debug,Spurious,Res>
Debug for
App<Arg,Spurious,Res>
{
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f,"App({:?} {:?})", self.prog_pt, self.arg)
}
}
impl<Arg:Hash+Debug,Spurious,Res>
Hash for
App<Arg,Spurious,Res>
{
fn hash<H>(&self, state: &mut H) where H: Hasher { (&self.prog_pt,&self.arg).hash(state) }
}
impl<Arg:'static+PartialEq+Eq+Clone+Debug,Spurious:'static+Clone,Res:'static+Debug+Hash>
Producer<Res> for
App<Arg,Spurious,Res>
{
fn produce(self:&Self) -> Res {
let f = self.fn_box.clone() ;
let res = f (self.arg.clone(),self.spurious.clone()) ;
res
}
fn copy(self:&Self) -> Box<dyn Producer<Res>> {
Box::new(App{
prog_pt:self.prog_pt.clone(),
fn_box:self.fn_box.clone(),
arg:self.arg.clone(),
spurious:self.spurious.clone(),
})
}
fn prog_pt<'r>(self:&'r Self) -> &'r ProgPt {
& self.prog_pt
}
fn eq (&self, other:&dyn Producer<Res>) -> bool {
if &self.prog_pt == other.prog_pt() {
let other = Box::new(other) ;
let other : &Box<App<Arg,Spurious,Res>> = unsafe { transmute::<_,_>( other ) } ;
self.arg == other.arg
} else {
false
}
}
}
impl<Arg:Clone+PartialEq+Eq+Debug,Spurious,Res>
Consumer<Arg> for
App<Arg,Spurious,Res>
{
fn consume(self:&mut Self, arg:Arg) { self.arg = arg; }
fn get_arg(self:&mut Self) -> Arg { self.arg.clone() }
}
fn lookup_abs<'r>(st:&'r mut DCG, loc:&Rc<Loc>) -> &'r mut Box<dyn GraphNode> {
match st.table.get_mut( loc ) {
None => panic!("dangling pointer: {:?}", loc),
Some(node) => node.be_node()
}
}
fn get_top_stack_loc(st:&DCG) -> Option<Rc<Loc>> {
if st.stack.len() > 0 {
Some(st.stack.get(st.stack.len() - 1).unwrap().loc.clone())
} else {
None
}
}
fn assert_graphnode_res_type<Res:'static> (loc:&Loc, node:&Box<dyn GraphNode>, top_stack:Option<Rc<Loc>>) {
let res_typeid = TypeId::of::<Res>();
let node_res_typeid = node.res_typeid();
if node_res_typeid != res_typeid {
let alloc_preds = node.preds_alloc().reflect();
panic!("\
Adapton engine: Detected a dynamic type error, possibly due to an ambiguous name:
\t at location: {:?}
\t existing allocator(s): {:?}
\tcontext/current allocator: {:?}
\t location has result type: {:?}
\tbut context expected type: {:?}",
loc, alloc_preds, top_stack.reflect(), node_res_typeid, res_typeid
);
}
}
fn res_node_of_loc<'r,Res:'static> (st:&'r mut DCG, loc:&Rc<Loc>) -> &'r mut Box<Node<Res>> {
let top_loc = get_top_stack_loc(st) ;
let abs_node = lookup_abs(st, loc) ;
assert_graphnode_res_type::<Res>(&*loc, abs_node, top_loc);
unsafe { transmute::<_,_>(abs_node) }
}
impl <Res:'static+Debug+Hash> GraphNode for Node<Res> {
fn res_typeid(self:&Self) -> TypeId {
return TypeId::of::<Res>()
}
fn preds_alloc(self:&Self) -> Vec<Rc<Loc>> {
match *self { Node::Mut(ref nd) => nd.preds.iter().filter_map(|pred| if pred.effect == Effect::Allocate { Some(pred.loc.clone()) } else { None } ).collect::<Vec<_>>(),
Node::Comp(ref nd) => nd.preds.iter().filter_map(|pred| if pred.effect == Effect::Allocate { Some(pred.loc.clone()) } else { None } ).collect::<Vec<_>>(),
Node::Pure(_) => unreachable!(),
}}
fn preds_obs(self:&Self) -> Vec<(Rc<Loc>, Option<Rc<Box<dyn DCGDep>>>)> {
match *self {
Node::Mut(ref nd) =>
nd.preds.iter().filter_map(
|pred|
if pred.effect == Effect::Observe { Some((pred.loc.clone(), pred.dep.clone())) }
else { None }
).collect::<Vec<_>>(),
Node::Comp(ref nd) =>
nd.preds.iter().filter_map(
|pred|
if pred.effect == Effect::Observe { Some((pred.loc.clone(), None)) }
else { None }
).collect::<Vec<_>>(),
Node::Pure(_) => unreachable!(),
}}
fn preds_insert (self:&mut Self, eff:Effect, loc:&Rc<Loc>, dep:Option<Rc<Box<dyn DCGDep>>>) -> () {
match *self { Node::Mut(ref mut nd) => nd.preds.push (Pred{effect:eff,loc:loc.clone(),dep:dep.clone()}),
Node::Comp(ref mut nd) => nd.preds.push (Pred{effect:eff,loc:loc.clone(),dep:dep.clone()}),
Node::Pure(_) => unreachable!(),
}}
fn preds_remove (self:&mut Self, loc:&Rc<Loc>) -> () {
match *self { Node::Mut(ref mut nd) => nd.preds.retain (|pred|{ &pred.loc != loc}),
Node::Comp(ref mut nd) => nd.preds.retain (|pred|{ &pred.loc != loc}),
Node::Pure(_) => unreachable!(),
}}
fn succs_def(self:&Self) -> bool {
match *self { Node::Comp(_) => true, _ => false
}}
fn succs_mut<'r>(self:&'r mut Self) -> &'r mut Vec<Succ> {
match *self { Node::Comp(ref mut n) => &mut n.succs,
_ => panic!("undefined"),
}
}
fn succs<'r>(self:&'r Self) -> &'r Vec<Succ> {
match *self { Node::Comp(ref n) => &n.succs,
_ => panic!("undefined"),
}
}
fn hash_seeded(self:&Self, seed:u64) -> u64 {
let mut hasher = DefaultHasher::new();
seed.hash(&mut hasher);
self.hash(&mut hasher);
hasher.finish()
}
}
trait ShapeShifter {
fn be_node<'r> (self:&'r mut Self) -> &'r mut Box<dyn GraphNode> ;
}
impl <Res> ShapeShifter for Box<Node<Res>> {
fn be_node<'r>(self:&'r mut Self) -> &'r mut Box<dyn GraphNode> {
unsafe { transmute::<_,_>(self) }
}
}
impl ShapeShifter for Box<dyn GraphNode> {
fn be_node<'r>(self:&'r mut Self) -> &'r mut Box<dyn GraphNode> {
unsafe { transmute::<_,_>(self) }
}
}
impl<Res> fmt::Debug for CompNode<Res> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.producer)
}
}
impl<Res:Hash> Hash for CompNode<Res> {
fn hash<H:Hasher>(&self, h: &mut H) {
self.preds.hash(h);
self.succs.hash(h);
self.res.hash(h);
(format!("{:?}",self.producer)).hash(h);
}
}
fn loc_produce<Res:'static+Debug+PartialEq+Eq+Clone+Hash>(g:&RefCell<DCG>, loc:&Rc<Loc>) -> Res
{
let (producer, prev_path) = {
let st : &mut DCG = &mut *g.borrow_mut() ;
let succs : Vec<Succ> = {
let succs : Vec<Succ> = Vec::new();
let node : &mut Node<Res> = res_node_of_loc( st, loc ) ;
replace(node.succs_mut(), succs)
} ;
revoke_succs( st, loc, &succs );
st.stack.push ( Frame{loc:loc.clone(), succs:Vec::new(), } );
let prev_path = st.path.clone () ;
st.path = loc.path.clone() ;
let producer : Box<dyn Producer<Res>> = {
let node : &mut Node<Res> = res_node_of_loc( st, loc ) ;
match *node {
Node::Comp(ref nd) => nd.producer.copy(),
_ => panic!("internal error"),
}
} ;
drop(st);
(producer, prev_path)
};
let res = producer.produce() ;
let st = &mut * g.borrow_mut() ;
st.path = prev_path ;
let frame = match st.stack.pop() {
None => panic!("expected Some _: stack invariants are broken"),
Some(frame) => frame
} ;
assert!( &frame.loc == loc );
for succ in &frame.succs {
if succ.0.dirty {
panic!("invariants broken: newly-built DCG edge should be clean, but is dirty.")
} ;
let succ_node = lookup_abs( st, &succ.0.loc );
succ_node.preds_insert( succ.0.effect.clone(), loc, succ.1.clone() );
} ;
{
let node : &mut Node<Res> = res_node_of_loc( st, loc ) ;
match *node {
Node::Comp(ref mut node) => {
replace(&mut node.succs, frame.succs.into_iter().map(|(succ,_)|succ).collect() ) ;
replace(&mut node.res, Some(res.clone()))
},
_ => panic!("internal error"),
}
} ;
res
}
fn clean_comp<Res:'static+Sized+Debug+PartialEq+Clone+Eq+Hash>
(g:&RefCell<DCG>,
this_dep:&ForceDep<Res>,
loc:&Rc<Loc>, cache:Res, succs:Vec<Succ>) -> DCGRes
{
for succ in succs.iter() {
let dirty = {
let st = &mut *g.borrow_mut();
get_succ_mut(st, loc, succ.effect.clone(), &succ.loc).dirty
} ;
if dirty {
dcg_effect_begin!(reflect::trace::Effect::CleanRec, Some(loc), succ);
let succ_dep = & succ.dep ;
let res = succ_dep.clean(g, &succ.loc) ;
if res.changed {
dcg_effect_begin!(reflect::trace::Effect::CleanEval, Some(loc), succ);
let result : Res = loc_produce( g, loc ) ;
dcg_effect_end!();
let changed = result != this_dep.res ;
dcg_effect_end!();
return DCGRes{changed:changed}
}
else {
let st : &mut DCG = &mut *g.borrow_mut();
get_succ_mut(st, loc, succ.effect.clone(), &succ.loc).dirty = false ;
dcg_effect!(reflect::trace::Effect::CleanEdge, Some(loc), succ);
}
dcg_effect_end!();
}
} ;
let changed = this_dep.res != cache ;
DCGRes{changed:changed}
}
#[derive(Debug)]
struct AllocStructuralThunk;
impl DCGDep for AllocStructuralThunk {
fn is_absmap (&self) -> Option<TypeId> { None }
fn dirty (self:&Self, _g:&mut DCG, _loc:&Rc<Loc>) -> DCGRes { DCGRes{changed:true} }
fn clean (self:&Self, _g:&RefCell<DCG>, _loc:&Rc<Loc>) -> DCGRes { DCGRes{changed:false} }
}
#[derive(Debug)]
struct AllocNominalThunk<T> { val:T }
impl<T:Debug> DCGDep for AllocNominalThunk<T> {
fn is_absmap (&self) -> Option<TypeId> { None }
fn dirty (self:&Self, _g:&mut DCG, _loc:&Rc<Loc>) -> DCGRes { DCGRes{changed:true} }
fn clean (self:&Self, _g:&RefCell<DCG>, _loc:&Rc<Loc>) -> DCGRes { DCGRes{changed:true} }
}
#[derive(Debug)]
struct AllocCell<T> { val:T }
impl<T:Debug> DCGDep for AllocCell<T> {
fn is_absmap (&self) -> Option<TypeId> { None }
fn dirty (self:&Self, _g:&mut DCG, _loc:&Rc<Loc>) -> DCGRes { DCGRes{changed:true} }
fn clean (self:&Self, _g:&RefCell<DCG>, _loc:&Rc<Loc>) -> DCGRes { DCGRes{changed:true} }
}
#[derive(Debug)]
struct ForceDep<T:Debug> { res:T }
struct ForceMapDep<T,S,F:Fn(&Art<T>, T)->S> { raw:PhantomData<T>, mapf:F, res:S }
pub trait AbsMapFam<Arg,Abs,T,DiffT,S> {
fn map (&self, arg:Arg, inp:T) -> S;
fn abs (&self, arg:Arg) -> Abs;
fn join (&self, fst:Abs, snd:Abs) -> Abs;
fn diff(&self, fst:&T, snd:&T) -> DiffT;
fn is_dirty(&self, diff:DiffT, abs:&Abs) -> bool;
}
trait AbsDiff<T> {
fn is_diff_dirty(&self, fst:&T, snd:&T) -> bool ;
}
struct ForceAbsDep<Arg,Abs,T,DiffT,S> {
phm:PhantomData<(Arg,T,DiffT,S)>,
map:(Abs, Box<dyn AbsMapFam<Arg,Abs,T,DiffT,S>>),
}
impl<Arg,Abs,T:'static,DiffT,S> AbsDiff<T> for ForceAbsDep<Arg,Abs,T,DiffT,S> {
fn is_diff_dirty(self:&Self, fst:&T, snd:&T) -> bool {
let diff = self.map.1.diff(fst, snd);
self.map.1.is_dirty(diff, &self.map.0)
}
}
impl<Arg:'static,
Abs:'static,
T:'static,
DiffT:'static,
S:'static
>
DCGDep for
ForceAbsDep<Arg,Abs,T,DiffT,S>
{
fn is_absmap(self:&Self) -> Option<TypeId> {
Some(TypeId::of::<Self>())
}
fn dirty (self:&Self, _g:&mut DCG, _loc:&Rc<Loc>) -> DCGRes {
unimplemented!()
}
fn clean (self:&Self, _g:&RefCell<DCG>, _loc:&Rc<Loc>) -> DCGRes {
DCGRes{ changed: true }
}
}
impl <Arg,Abs,T,DiffT,S> Debug for ForceAbsDep<Arg,Abs,T,DiffT,S>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ForceAbsDep(?)")
}
}
fn check_force_map_dep
<T:'static+Sized+Debug+PartialEq+Eq+Clone+Hash,
S:'static+Sized+Debug+PartialEq+Eq+Clone+Hash,
F:Fn(&Art<T>, T)->S>
(st:&mut DCG, dep:&ForceMapDep<T,S,F>, loc:&Rc<Loc>) -> DCGRes
{
let node : &mut Node<T> = res_node_of_loc(st, loc) ;
match *node {
Node::Mut(ref nd) =>
DCGRes{changed:dep.res != (dep.mapf)
(&Art{art:EnumArt::Loc(loc.clone())},
nd.val.clone())},
Node::Comp(_) | Node::Pure(_) =>
unreachable!()
}
}
impl <T:'static+Sized+Debug+PartialEq+Eq+Clone+Hash,
S:'static+Sized+Debug+PartialEq+Eq+Clone+Hash, F:Fn(&Art<T>, T)->S>
DCGDep for ForceMapDep<T,S,F>
{
fn is_absmap(self:&Self) -> Option<TypeId> {
None
}
fn dirty(self:&Self, g:&mut DCG, loc:&Rc<Loc>) -> DCGRes {
check_force_map_dep(g, self, loc)
}
fn clean(self:&Self, g:&RefCell<DCG>, loc:&Rc<Loc>) -> DCGRes {
check_force_map_dep(&mut *g.borrow_mut(), self, loc)
}
}
impl <T:'static+Sized+Debug+PartialEq+Eq+Clone+Hash,
S:'static+Sized+Debug+PartialEq+Eq+Clone+Hash, F:Fn(&Art<T>, T)->S>
Debug for ForceMapDep<T,S,F>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ForceMapDep({:?})", self.res)
}
}
impl <Res:'static+Sized+Debug+PartialEq+Eq+Clone+Hash>
DCGDep for ForceDep<Res>
{
fn is_absmap(self:&Self) -> Option<TypeId> {
None
}
fn dirty(self:&Self, _g:&mut DCG, _loc:&Rc<Loc>) -> DCGRes {
DCGRes{changed:true}
}
fn clean(self:&Self, g:&RefCell<DCG>, loc:&Rc<Loc>) -> DCGRes {
let res_succs = {
let st = &mut *g.borrow_mut();
let node : &mut Node<Res> = res_node_of_loc(st, loc) ;
match *node {
Node::Comp(ref nd) => {
match nd.res {
Some(ref res) => Some((res.clone(), nd.succs.clone ())),
None => None
}},
Node::Pure(_) => {
return DCGRes{changed:false}
},
Node::Mut(ref nd) => {
return DCGRes{changed:nd.val != self.res}
},
}
} ;
let none : Option<Loc> = None ;
match res_succs {
Some((res,succs)) => clean_comp(g, self, loc, res, succs),
None => {
dcg_effect_begin!(
reflect::trace::Effect::CleanEval,
none,
reflect::Succ{
loc:loc.reflect(),
dirty:true,
effect:reflect::Effect::Force,
value:reflect::Val::ValTODO,
is_dup:false,
}
);
let res = loc_produce( g, loc );
let changed = self.res != res ;
dcg_effect_end!();
DCGRes{changed:changed}
}
}
}
}
fn revoke_succs<'x> (st:&mut DCG, src:&Rc<Loc>, succs:&Vec<Succ>) {
for succ in succs.iter() {
dcg_effect!(reflect::trace::Effect::Remove, Some(src), succ);
let succ_node : &mut Box<dyn GraphNode> = lookup_abs(st, &succ.loc) ;
succ_node.preds_remove(src)
}
}
fn loc_of_id(path:Rc<Path>,id:Rc<ArtId>) -> Rc<Loc> {
let hash = my_hash(&(&path,&id));
Rc::new(Loc{path:path,id:id,hash:hash})
}
fn get_succ<'r>(st:&'r DCG, src_loc:&Rc<Loc>, eff:Effect, tgt_loc:&Rc<Loc>) -> &'r Succ {
let nd = st.table.get(src_loc);
let nd = match nd {
None => panic!(""),
Some(nd) => nd
} ;
for succ in nd.succs() {
if (succ.effect == eff) && (&succ.loc == tgt_loc) {
return succ
} else {}
} ;
panic!("tgt_loc is dangling in src_node.dem_succs")
}
fn get_succ_mut<'r>(st:&'r mut DCG, src_loc:&Rc<Loc>, eff:Effect, tgt_loc:&Rc<Loc>) -> &'r mut Succ {
let nd = lookup_abs( st, src_loc );
for succ in nd.succs_mut().iter_mut() {
if (succ.effect == eff) && (&succ.loc == tgt_loc) {
return succ
} else {}
} ;
panic!("tgt_loc is dangling in src_node.dem_succs")
}
fn dirty_pred_observers(st:&mut DCG, loc:&Rc<Loc>) {
let pred_locs : Vec<(Rc<Loc>, Option<Rc<Box<dyn DCGDep>>>)> = lookup_abs( st, loc ).preds_obs() ;
for (pred_loc, dep) in pred_locs {
let stop : bool = match dep {
None => false,
Some(dep) => dep.is_absmap() != None || dep.dirty(st, loc).changed == false
};
let stop : bool = if stop { true } else {
let succ = get_succ_mut(st, &pred_loc, Effect::Observe, &loc) ;
if succ.dirty { true } else {
assert!(&pred_loc != loc);
dcg_effect_begin!(reflect::trace::Effect::Dirty, Some(&pred_loc), succ);
replace(&mut succ.dirty, true);
false
}}
;
if !stop {
dirty_pred_observers(st,&pred_loc);
dcg_effect_end!();
} else { }
}
}
fn dirty_alloc(st:&mut DCG, loc:&Rc<Loc>) {
dirty_pred_observers(st, loc);
let pred_locs : Vec<Rc<Loc>> = lookup_abs(st, loc).preds_alloc() ;
for pred_loc in pred_locs {
let stop : bool = {
let succ = get_succ_mut(st, &pred_loc, Effect::Allocate, &loc) ;
if succ.dirty { true } else {
replace(&mut succ.dirty, true);
assert!(&pred_loc != loc);
dcg_effect_begin!(reflect::trace::Effect::Dirty, Some(&pred_loc), succ);
false
}} ;
if !stop {
dirty_pred_observers(st,&pred_loc);
dcg_effect_end!();
} else { }
}
if false {
wf::check_stack_is_clean(st)
}
}
fn check_cell_change<T:'static+Eq+Debug> (st:&mut DCG, cell:AbsArt<T,Loc>, val:&T) -> bool {
if let AbsArt::Loc(ref loc) = cell {
let node = res_node_of_loc::<T>( st, loc ) ;
match **node {
Node::Mut(ref mut nd) => { &nd.val != val }
_ => { true }
}
}
else { panic!("{:?} is not a cell", cell) }
}
fn set_<T:'static+Eq+Debug> (st:&mut DCG, cell:AbsArt<T,Loc>, val:T) {
if let AbsArt::Loc(ref loc) = cell {
let changed : bool = {
let node = res_node_of_loc( st, loc ) ;
match **node {
Node::Mut(ref mut nd) => {
if nd.val == val {
false
} else {
replace(&mut nd.val, val) ;
true
}},
_ => unreachable!(),
}
};
if changed {
dirty_alloc(st, loc);
}
}
else { panic!("{:?} is not a cell", cell) }
}
fn current_path (st:&DCG) -> Rc<Path> {
st.path.clone()
}
#[derive(Hash,Debug,PartialEq,Eq,Clone)]
enum AbsArt<T,Loc> {
Rc(Rc<T>),
Loc(Rc<Loc>),
}
trait Adapton : Debug+PartialEq+Eq+Hash+Clone {
type Loc : Debug+PartialEq+Eq+Hash+Clone;
fn new () -> Self ;
fn ns<T,F> (g: &RefCell<DCG>, _: Name, body:F) -> T where F:FnOnce() -> T;
fn structural<T,F> (g: &RefCell<DCG>, body:F) -> T where F:FnOnce() -> T;
fn put<T:Eq+Debug+Clone> (self:&mut Self, _: T) -> AbsArt<T,Self::Loc> ;
fn cell<T:Eq+Debug+Clone+Hash+'static> (self:&mut Self, _: Name, _: T) -> AbsArt<T,Self::Loc> ;
fn set<T:'static+Eq+Debug+Clone> (self:&mut Self, _: AbsArt<T,Self::Loc>, _: T) ;
fn thunk <Arg:Eq+Hash+Debug+Clone+'static,
Spurious:Clone+'static,
Res:Eq+Debug+Clone+Hash+'static
>
(self:&mut Self,
id:NameChoice,
prog_pt:ProgPt,
fn_box:Rc<Box< dyn Fn(Arg, Spurious) -> Res >>,
arg:Arg, spurious:Spurious)
-> AbsArt<Res,Self::Loc> ;
fn force<T:Eq+Debug+Clone+Hash+'static> (g:&RefCell<DCG>, _: &AbsArt<T,Self::Loc>, _: Option<T>) -> T ;
fn force_map<T:Eq+Debug+Clone+Hash+'static,
S:Eq+Debug+Clone+Hash+'static,
F:'static>
(g:&RefCell<DCG>, _: &AbsArt<T,Self::Loc>, _: F) -> S
where F:Fn(&Art<T>, T) -> S
;
fn force_abs
<Arg:'static+Eq+Debug+Clone+Hash,
Abs:'static+Eq+Debug+Clone+Hash,
T:'static+Eq+Debug+Clone+Hash,
DiffT:'static+Eq+Debug+Clone+Hash,
S:'static+Eq+Debug+Clone+Hash>
(g:&RefCell<DCG>,
absmapfam:Box<dyn AbsMapFam<Arg,Abs,T,DiffT,S>>,
arg:Arg, art:&AbsArt<T,Self::Loc>) -> S ;
}
impl Adapton for DCG {
type Loc = Loc;
fn new () -> DCG {
let path = Rc::new(Path::Empty);
let stack = Vec::new() ;
let table = HashMap::new ();
DCG {
flags : Flags {
use_purity_optimization : { match env::var("ADAPTON_NO_PURITY") { Ok(_) => false, _ => true } },
ignore_nominal_use_structural : { match env::var("ADAPTON_STRUCTURAL") { Ok(_) => true, _ => false } },
check_dcg_is_wf : { match env::var("ADAPTON_CHECK_DCG") { Ok(_) => true, _ => false } },
write_dcg : { match env::var("ADAPTON_WRITE_DCG") { Ok(_) => true, _ => false } },
gmlog_dcg : { match env::var("ADAPTON_GMLOG_DCG") { Ok(_) => true, _ => false } },
},
table : table,
stack : stack,
path : path,
dcg_count : 0,
dcg_hash : 0,
}
}
fn structural<T,F> (g: &RefCell<DCG>, body:F) -> T
where F:FnOnce() -> T
{
let saved = {
let st = &mut *g.borrow_mut();
let saved = st.flags.ignore_nominal_use_structural ;
st.flags.ignore_nominal_use_structural = true ;
saved
} ;
let x = body() ;
g.borrow_mut().flags.ignore_nominal_use_structural = saved;
x
}
fn ns<T,F> (g: &RefCell<DCG>, nm:Name, body:F) -> T
where F:FnOnce() -> T
{
let saved = {
let st = &mut *g.borrow_mut();
let saved = st.path.clone();
st.path = Rc::new(Path::Child(st.path.clone(), nm)) ;
saved
};
let x = body() ;
g.borrow_mut().path = saved ;
x
}
fn put<T:Eq> (self:&mut DCG, x:T) -> AbsArt<T,Self::Loc> { AbsArt::Rc(Rc::new(x)) }
fn cell<T:Eq+Debug+Clone+Hash
+'static
>
(self:&mut DCG, nm:Name, val:T) -> AbsArt<T,Self::Loc> {
wf::check_dcg(self);
let path = current_path(self) ;
let (id, is_pure) = {
if ! self.flags.ignore_nominal_use_structural {
(Rc::new(ArtId::Nominal(nm)), false)
} else {
let hash = my_hash (&val) ;
(Rc::new(ArtId::Structural(hash)), self.flags.use_purity_optimization)
}
};
let hash = my_hash(&(&path,&id));
let loc = Rc::new(Loc{path:path,id:id,hash:hash})
;
let (do_dirty, do_set, succs, do_insert, is_fresh) =
if self.table.contains_key(&loc) {
let node : &Box<Node<T>> = res_node_of_loc(self, &loc) ;
match **node {
Node::Mut(_) => { (false, true, None, false, false) }
Node::Comp(ref nd) => { (true, false, Some(nd.succs.clone()), false, false ) }
Node::Pure(_) => { (false, false, None, false, false) }
}} else { (false, false, None, true, true ) }
;
dcg_effect_begin!(
reflect::trace::Effect::Alloc(
if is_fresh { reflect::trace::AllocCase::LocFresh }
else {
let changed =
if check_cell_change(self, AbsArt::Loc(loc.clone()), &val) {
reflect::trace::ChangeFlag::ContentDiff
} else {
reflect::trace::ChangeFlag::ContentSame
};
reflect::trace::AllocCase::LocExists(changed)
},
reflect::trace::AllocKind::RefCell,
),
current_loc!(self),
reflect::Succ{
loc:loc.reflect(),
effect:reflect::Effect::Alloc,
value:reflect::Val::ValTODO,
dirty:false,
is_dup:false,
}
);
if do_set { set_(self, AbsArt::Loc(loc.clone()), val.clone()) };
if do_dirty { dirty_alloc(self, &loc) } ;
match succs { Some(succs) => revoke_succs(self, &loc, &succs), None => () } ;
dcg_effect_end!();
if do_insert {
let node = if is_pure { Node::Pure(PureNode{val:val.clone()}) } else {
Node::Mut(MutNode{
preds:Vec::new(),
val:val.clone(),
})} ;
self.table.insert(loc.clone(), Box::new(node));
} ;
if ! is_pure { match self.stack.last_mut() {
None => (),
Some(frame) => {
let succ =
Succ{loc:loc.clone(),
dep:Rc::new(Box::new(AllocCell{val:val})),
effect:Effect::Allocate,
dirty:false};
frame.succs.push((succ, None))
}}} ;
wf::check_dcg(self);
AbsArt::Loc(loc)
}
fn set<T:'static+Eq+Debug> (self:&mut Self, cell:AbsArt<T,Self::Loc>, val:T) {
wf::check_dcg(self);
assert!( self.stack.is_empty() );
set_(self, cell, val);
wf::check_dcg(self);
}
fn thunk<Arg:Eq+Hash+Debug+Clone+'static,Spurious:'static+Clone,Res:Eq+Debug+Clone+Hash+'static>
(self:&mut DCG,
id:NameChoice,
prog_pt:ProgPt,
fn_box:Rc<Box<dyn Fn(Arg, Spurious) -> Res>>,
arg:Arg, spurious:Spurious)
-> AbsArt<Res,Self::Loc>
{
wf::check_dcg(self);
let id =
match id { NameChoice::Nominal(_)
if self.flags.ignore_nominal_use_structural
=> NameChoice::Structural,
id => id } ;
match id {
NameChoice::Eager => {
AbsArt::Rc(Rc::new(fn_box(arg,spurious)))
},
NameChoice::Naive => {
unimplemented!()
}
NameChoice::Structural => {
wf::check_dcg(self);
let hash = my_hash (&(&prog_pt, &arg)) ;
let loc = loc_of_id(current_path(self),
Rc::new(ArtId::Structural(hash)));
{
let node = self.table.get_mut(&loc);
match node { None => { },
Some(_) => { return AbsArt::Loc(loc) },
}
} ;
match self.stack.last_mut() {
None => (),
Some(frame) => {
let succ =
Succ{loc:loc.clone(),
dep:Rc::new(Box::new(AllocStructuralThunk)),
effect:Effect::Allocate,
dirty:false};
frame.succs.push((succ, None))
}};
let producer : Box<dyn Producer<Res>> =
Box::new(App{prog_pt:prog_pt,
fn_box:fn_box,
arg:arg.clone(),
spurious:spurious.clone()})
;
let node : CompNode<Res> = CompNode{
preds:Vec::new(),
succs:Vec::new(),
producer:producer,
res:None,
} ;
self.table.insert(loc.clone(),
Box::new(Node::Comp(node)));
wf::check_dcg(self);
AbsArt::Loc(loc)
},
NameChoice::Nominal(nm) => {
wf::check_dcg(self);
let loc = loc_of_id(current_path(self),
Rc::new(ArtId::Nominal(nm)));
let producer : App<Arg,Spurious,Res> =
App{prog_pt:prog_pt.clone(),
fn_box:fn_box,
arg:arg.clone(),
spurious:spurious.clone(),
}
;
let top_loc = get_top_stack_loc( self );
let (do_dirty, do_insert, is_fresh) = { match self.table.get_mut( &loc ) {
None => {
(false, true, true)
},
Some(node) => {
let node: &mut Box<dyn GraphNode> = node ;
assert_graphnode_res_type::<Res>(&loc, node, top_loc);
let res_nd: &mut Box<Node<Res>> = unsafe { transmute::<_,_>( node ) } ;
match ** res_nd {
Node::Pure(_)=> unreachable!(),
Node::Mut(_) => {
(true, true, false)
},
Node::Comp(ref mut comp_nd) => {
let equal_producer_prog_pts : bool =
comp_nd.producer.prog_pt().eq( producer.prog_pt() ) ;
if equal_producer_prog_pts {
let app: &mut Box<App<Arg,Spurious,Res>> =
unsafe { transmute::<_,_>( &mut comp_nd.producer ) }
;
if app.get_arg() == arg {
(false, false, false)
}
else {
app.consume(arg.clone());
comp_nd.res = None ;
(true, false, false)
}}
else {
panic!("Memozied functions not equal!
Function was: {:?}
with Producer: {:?}
Function now: {:?}
with Producer: {:?}
Common location: {:?}
** Hint: Consider using distinct namespaces, via `Adapton::ns`
(See: https://docs.rs/adapton/0/adapton/engine/fn.ns.html)
",
comp_nd.producer.prog_pt(), &comp_nd.producer,
producer.prog_pt(), &producer,
&loc,
)
}
},
}
}
} } ;
dcg_effect_begin!(
reflect::trace::Effect::Alloc(
if is_fresh { reflect::trace::AllocCase::LocFresh }
else {
let cf =
if do_dirty { reflect::trace::ChangeFlag::ContentDiff }
else { reflect::trace::ChangeFlag::ContentSame };
reflect::trace::AllocCase::LocExists(cf) },
reflect::trace::AllocKind::Thunk
),
current_loc!(self),
reflect::Succ{
loc:loc.reflect(),
effect:reflect::Effect::Alloc,
value:reflect::Val::ValTODO,
dirty:false,
is_dup:false,
});
if do_dirty {dirty_alloc(self, &loc) };
dcg_effect_end!();
match self.stack.last_mut() { None => (), Some(frame) => {
let succ =
Succ{loc:loc.clone(),
dep:Rc::new(Box::new(AllocNominalThunk{val:arg.clone()})),
effect:Effect::Allocate,
dirty:false};
frame.succs.push((succ, None))
}};
if do_insert {
let node : CompNode<Res> = CompNode{
preds:Vec::new(),
succs:Vec::new(),
producer:Box::new(producer),
res:None,
} ;
self.table.insert(loc.clone(), Box::new(Node::Comp(node)));
wf::check_dcg(self);
AbsArt::Loc(loc)
}
else {
wf::check_dcg(self);
AbsArt::Loc(loc)
}
}
}
}
fn force_map<T:'static+Eq+Debug+Clone+Hash,
S:'static+Eq+Debug+Clone+Hash,
F:'static>
(g:&RefCell<DCG>,
art:&AbsArt<T,Self::Loc>, mapf:F) -> S
where F:Fn(&Art<T>, T) -> S
{
match *art {
AbsArt::Rc(ref v) => mapf(&Art{art:EnumArt::Rc(v.clone())},
(**v).clone()),
AbsArt::Loc(ref loc) => {
let cell_val : Option<T> = {
let st : &mut DCG = &mut *g.borrow_mut();
let node : &mut Node<T> = res_node_of_loc(st, &loc) ;
match *node {
Node::Comp(_) => { None }
Node::Pure(_) => { None }
Node::Mut(ref nd) => { Some(nd.val.clone()) }
}
} ;
match cell_val {
None => {
mapf(&Art{art:EnumArt::Loc(loc.clone())},
<DCG as Adapton>::force(g, art, None))
},
Some(val) => {
dcg_effect!(
reflect::trace::Effect::Force(reflect::trace::ForceCase::RefGet),
current_loc!(*g.borrow()),
reflect::Succ{
loc:loc.reflect(),
value:reflect::Val::ValTODO,
effect:reflect::Effect::Force,
dirty:false,
is_dup:false,
});
let st : &mut DCG = &mut *g.borrow_mut() ;
let res = mapf(&Art{art:EnumArt::Loc(loc.clone())}, val.clone());
match st.stack.last_mut() { None => (), Some(frame) => {
let dep : Rc<Box<dyn DCGDep>> = Rc::new(Box::new(ForceMapDep{
raw:PhantomData,
mapf:mapf,
res:res.clone()}));
let succ =
Succ{loc:loc.clone(),
dep:dep.clone(),
effect:Effect::Observe,
dirty:false};
frame.succs.push((succ, Some(dep.clone())));
}};
res
}
}
}
}
}
fn force_abs
<Arg:'static+Eq+Debug+Clone+Hash,
Abs:'static+Eq+Debug+Clone+Hash,
T:'static+Eq+Debug+Clone+Hash,
DiffT:'static+Eq+Debug+Clone+Hash,
S:'static+Eq+Debug+Clone+Hash>
(g:&RefCell<DCG>, absmapfam:Box<dyn AbsMapFam<Arg,Abs,T,DiffT,S>>,
arg:Arg, art:&AbsArt<T,Self::Loc>) -> S
{
match *art {
AbsArt::Rc(ref v) => absmapfam.map(arg, (**v).clone()),
AbsArt::Loc(ref loc) => {
let cell_val : Option<T> = {
let st : &mut DCG = &mut *g.borrow_mut();
let node : &mut Node<T> = res_node_of_loc(st, &loc) ;
match *node {
Node::Comp(_) => { None }
Node::Pure(_) => { None }
Node::Mut(ref nd) => { Some(nd.val.clone()) }
}
} ;
match cell_val {
None => {
absmapfam.map(arg,
<DCG as Adapton>::force(g, art, None))
},
Some(val) => {
dcg_effect!(
reflect::trace::Effect::Force(reflect::trace::ForceCase::RefGet),
current_loc!(*g.borrow()),
reflect::Succ{
loc:loc.reflect(),
value:reflect::Val::ValTODO,
effect:reflect::Effect::Force,
dirty:false,
is_dup:false,
});
let st : &mut DCG = &mut *g.borrow_mut() ;
let res = absmapfam.map(arg.clone(),val.clone());
match st.stack.last_mut() { None => (), Some(frame) => {
let dep : Rc<Box<dyn DCGDep>> = Rc::new(Box::new(ForceAbsDep{
phm:PhantomData,
map:(absmapfam.abs(arg), absmapfam),
}));
let succ =
Succ{loc:loc.clone(),
dep:dep.clone(),
effect:Effect::Observe,
dirty:false};
frame.succs.push((succ, Some(dep.clone())));
}};
res
}
}
}
}
}
fn force<T:'static+Eq+Debug+Clone+Hash> (g:&RefCell<DCG>,
art:&AbsArt<T,Self::Loc>, cycle_out:Option<T>) -> T
{
{
let st : &mut DCG = &mut *g.borrow_mut();
wf::check_dcg(st);
drop(st)
}
match *art {
AbsArt::Rc(ref v) => (**v).clone(),
AbsArt::Loc(ref loc) => {
let (is_comp, is_dup, is_pure, is_cycle, cached_result) : (bool, bool, bool, bool, Option<T>) = {
let st : &mut DCG = &mut *g.borrow_mut();
let is_pure_opt : bool = st.flags.use_purity_optimization ;
let is_cycle = { let mut is_cycle = false;
for frame in st.stack.iter() {
if &frame.loc == loc {
is_cycle = true ; break
} else { } }
is_cycle
};
let is_dup : bool = match st.stack.last_mut() { None => false, Some(frame) => {
let mut is_dup = false;
for &(ref succ, ref _pred_dep) in frame.succs.iter() {
if &succ.loc == loc && succ.effect == Effect::Observe
{ is_dup = true }
};
is_dup
}};
let node : &mut Node<T> = res_node_of_loc(st, &loc) ;
match *node {
Node::Pure(ref mut nd) => (false, is_dup, true, false, Some(nd.val.clone())),
Node::Mut(ref mut nd) => (false, is_dup, false, false, Some(nd.val.clone())),
Node::Comp(ref mut nd) => {
let is_pure = match *loc.id {
ArtId::Structural(_) => nd.succs.len() == 0 && is_pure_opt,
ArtId::Nominal(_) => false } ;
if is_cycle {
match cycle_out {
None => { panic!("unexpected cycle detected in DCG") }
Some(out) => (true, is_dup, is_pure, true, Some(out)),
}
}
else {
(true, is_dup, is_pure, false, nd.res.clone())
}
}
}
};
let result = match cached_result {
None => {
assert!(is_comp);
assert!(!is_cycle);
dcg_effect_begin!(
reflect::trace::Effect::Force(reflect::trace::ForceCase::CompCacheMiss),
current_loc!(*g.borrow()),
reflect::Succ{
loc:loc.reflect(),
value:reflect::Val::ValTODO,
effect:reflect::Effect::Force,
dirty:false,
is_dup:is_dup,
}
);
assert_eq!(is_dup, false);
let res = loc_produce(g, &loc);
dcg_effect_end!();
res
},
Some(res) => {
if is_comp {
dcg_effect_begin!(
reflect::trace::Effect::Force(reflect::trace::ForceCase::CompCacheHit),
current_loc!(*g.borrow()),
reflect::Succ{
loc:loc.reflect(),
value:reflect::Val::ValTODO,
effect:reflect::Effect::Force,
dirty:false,
is_dup:is_dup,
}
);
if is_cycle {
dcg_effect_end!();
res
}
else {
let _ = ForceDep{res:res.clone()}.clean(g, &loc) ;
dcg_effect_end!();
let st : &mut DCG = &mut *g.borrow_mut();
let node : &mut Node<T> = res_node_of_loc(st, &loc) ;
match *node {
Node::Comp(ref nd) => match nd.res {
None => unreachable!(),
Some(ref res) =>
res.clone()
},
_ => unreachable!(),
}}
}
else {
dcg_effect!(
reflect::trace::Effect::Force(reflect::trace::ForceCase::RefGet),
current_loc!(*g.borrow()),
reflect::Succ{
loc:loc.reflect(),
value:reflect::Val::ValTODO,
effect:reflect::Effect::Force,
dirty:false,
is_dup:is_dup,
});
res.clone()
}
}
} ;
let st : &mut DCG = &mut *g.borrow_mut() ;
if !is_dup && !is_pure { match st.stack.last_mut() { None => (), Some(frame) => {
let succ =
Succ{loc:loc.clone(),
dep:Rc::new(Box::new(ForceDep{res:result.clone()})),
effect:Effect::Observe,
dirty:false};
frame.succs.push((succ, None));
}}} ;
wf::check_dcg(st);
result
}
}}
}
#[derive(Clone,PartialEq,Eq,Hash,Debug)]
pub struct Art<T> {
art:EnumArt<T>,
}
#[derive(Clone)]
enum EnumArt<T> {
Rc(Rc<T>),
Loc(Rc<Loc>),
Force(Rc<dyn Force<T>>),
}
impl<T:Hash> Hash for EnumArt<T> {
fn hash<H>(&self, hasher:&mut H) where H:Hasher {
match *self {
EnumArt::Rc(ref rc) => rc.hash( hasher ),
EnumArt::Loc(ref loc) => loc.hash( hasher ),
EnumArt::Force(ref f) => f.hash_u64().hash( hasher ),
}
}
}
impl<T:PartialEq> PartialEq for EnumArt<T> {
fn eq(&self, other:&Self) -> bool {
match *self {
EnumArt::Rc(ref rc) =>
if let EnumArt::Rc(ref rc2) = *other { rc == rc2 } else { false },
EnumArt::Loc(ref loc) =>
if let EnumArt::Loc(ref loc2) = *other { loc == loc2 } else { false },
EnumArt::Force(ref f) =>
if let EnumArt::Force(ref f2) = *other { f.eq(&**f2) } else { false },
}
}
}
impl<T:Debug> Debug for EnumArt<T> {
fn fmt(&self, f:&mut Formatter) -> Result {
match *self {
EnumArt::Rc(ref rc) => rc.fmt(f),
EnumArt::Loc(ref loc) => loc.fmt(f),
EnumArt::Force(ref frc) => frc.fmt(f),
}
}
}
impl<T:Eq> Eq for EnumArt<T> { }
trait Force<T> {
fn force(&self) -> T;
fn copy(self:&Self) -> Box<dyn Force<T>>;
fn eq(self:&Self, other:&dyn Force<T>) -> bool;
fn id<'r>(self:&'r Self) -> &'r NameChoice;
fn prog_pt<'r>(self:&'r Self) -> &'r ProgPt;
fn hash_u64(self:&Self) -> u64;
fn fmt(&self, f:&mut Formatter) -> fmt::Result;
}
#[derive(Clone)]
struct NaiveThunk<Arg, Spurious, Res> {
id:NameChoice,
prog_pt:ProgPt,
fn_box:Rc<Box< dyn Fn(Arg, Spurious) -> Res >>,
arg:Arg,
spurious:Spurious
}
impl<A:Hash+Clone+Eq+Debug+'static,S:Clone+'static,T:'static>
Force<T>
for NaiveThunk<A,S,T>
{
fn force(&self) -> T {
(*self.fn_box)(self.arg.clone(), self.spurious.clone())
}
fn copy(self:&Self) -> Box<dyn Force<T>> {
Box::new(NaiveThunk{id:self.id.clone(),
prog_pt:self.prog_pt.clone(),
fn_box:self.fn_box.clone(),
arg:self.arg.clone(),
spurious:self.spurious.clone()})
}
fn prog_pt<'r>(self:&'r Self) -> &'r ProgPt {
& self.prog_pt
}
fn id<'r>(self:&'r Self) -> &'r NameChoice {
& self.id
}
fn hash_u64(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.id.hash( &mut hasher );
self.prog_pt.hash( &mut hasher );
self.arg.hash( &mut hasher );
hasher.finish()
}
fn eq (&self, other:&dyn Force<T>) -> bool {
if &self.id == other.id()
&& &self.prog_pt == other.prog_pt()
{
let other = Box::new(other) ;
let other : &Box<NaiveThunk<A,S,T>> = unsafe { transmute::<_,_>( other ) } ;
self.arg == other.arg
} else {
false
}
}
fn fmt(&self, f:&mut Formatter) -> Result {
write!(f,"NaiveThunk{{id:{:?},prog_pt:{:?},arg:{:?}}}",
self.id, self.prog_pt, self.arg)
}
}
impl<A:Hash,S,T> Hash for NaiveThunk<A,S,T> {
fn hash<H:Hasher>(&self, hasher: &mut H) {
self.prog_pt.hash( hasher );
self.arg.hash( hasher );
}
}
impl<A:Debug,S,T> Debug for NaiveThunk<A,S,T> {
fn fmt(&self, f:&mut Formatter) -> fmt::Result {
write!(f, "NaiveThunk({:?},{:?})", self.prog_pt, self.arg)
}
}
impl<A:PartialEq,S,T> PartialEq for NaiveThunk<A,S,T> {
fn eq(&self, other:&Self) -> bool {
self.prog_pt == other.prog_pt &&
self.arg == other.arg
}
}
pub fn name_unit () -> Name {
UNIT_NAME.with(|r|r.clone())
}
pub fn name_pair (n1:Name, n2:Name) -> Name {
let h = my_hash( &(n1.hash,n2.hash) ) ;
let p = NameSym::Pair(n1.symbol, n2.symbol) ;
Name{ hash:h, symbol:Rc::new(p) }
}
pub fn name_of_hash64(h:u64) -> Name {
Name{ hash:h, symbol:Rc::new(NameSym::Hash64) }
}
pub fn name_of_usize (u:usize) -> Name {
let h = my_hash(&u) ;
let s = NameSym::Usize(u) ;
Name{ hash:h, symbol:Rc::new(s) }
}
pub fn name_of_isize (i:isize) -> Name {
let h = my_hash(&i) ;
let s = NameSym::Isize(i) ;
Name{ hash:h, symbol:Rc::new(s) }
}
pub fn name_of_string (s:String) -> Name {
let h = my_hash(&s);
let s = NameSym::String(s) ;
Name{ hash:h, symbol:Rc::new(s) }
}
pub fn name_of_str (s:&'static str) -> Name {
let h = my_hash(&s);
let s = NameSym::String(s.to_string()) ;
Name{ hash:h, symbol:Rc::new(s) }
}
pub fn name_fork (n:Name) -> (Name, Name) {
let h1 = my_hash( &(&n, 11111111) ) ;
let h2 = my_hash( &(&n, 22222222) ) ;
( Name{ hash:h1,
symbol:Rc::new(NameSym::ForkL(n.symbol.clone())) } ,
Name{ hash:h2,
symbol:Rc::new(NameSym::ForkR(n.symbol)) } )
}
pub fn name_fork3 (n:Name)
-> (Name,Name,Name)
{
let (n1,n) = name_fork(n);
let (n2,n3) = name_fork(n);
(n1,n2,n3)
}
pub fn name_fork4 (n:Name)
-> (Name,Name,Name,Name)
{
let (n1,n) = name_fork(n);
let (n2,n) = name_fork(n);
let (n3,n4) = name_fork(n);
(n1,n2,n3,n4)
}
pub fn ns<T,F> (n:Name, body:F) -> T
where F:FnOnce() -> T {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg) => <DCG as Adapton>::ns(dcg, n, body),
Engine::Naive => (body)()
}
})
}
pub fn structural<T,F> (body:F) -> T
where F:FnOnce() -> T {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg) => <DCG as Adapton>::structural(dcg,body),
Engine::Naive => (body)()
}
})
}
pub fn put<T:Eq+Debug+Clone> (val:T) -> Art<T> {
Art{art:EnumArt::Rc(Rc::new(val))}
}
pub fn cell<T:Hash+Eq+Debug+Clone+'static> (n:Name, val:T) -> Art<T> {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg) => {
if
let AbsArt::Loc(loc) = (dcg.borrow_mut()).cell(n,val) {
Art{art:EnumArt::Loc(loc)} }
else { unreachable!() } }
Engine::Naive => Art{art:EnumArt::Rc(Rc::new(val))}
}
})
}
pub fn set<T:'static+Eq+Debug+Clone> (a:&Art<T>, val:T) {
match (*a).art {
EnumArt::Rc(_) => { panic!("set: Cannot mutate immutable Rc articulation; use an DCG cell instead") },
EnumArt::Force(_) => { panic!("set: Cannot mutate immutable Force articulation; use an DCG cell instead") },
EnumArt::Loc(ref l) => {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::Naive => unimplemented!(),
Engine::DCG(ref dcg) => {
(dcg.borrow_mut()).set(AbsArt::Loc(l.clone()), val)
}
}
})
}
}
}
pub fn thunk<Arg:Hash+Eq+Debug+Clone+'static,Spurious:Clone+'static,Res:Hash+Eq+Debug+Clone+'static>
(id:NameChoice,
prog_pt:ProgPt,
fn_box:Rc<Box< dyn Fn(Arg, Spurious) -> Res >>,
arg:Arg, spurious:Spurious)
-> Art<Res>
{
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg) => {
Art{art:EnumArt::Loc({
if let AbsArt::Loc(loc) =
(dcg.borrow_mut()).thunk(id, prog_pt, fn_box, arg, spurious)
{ loc } else { unreachable!() }})}
},
Engine::Naive => {
Art{art:EnumArt::Force(
Rc::new(NaiveThunk{
id:id,prog_pt:prog_pt,
fn_box:fn_box,arg:arg,
spurious:spurious} ))}}}
})
}
pub fn thunk_map<Res1:Hash+Eq+Debug+Clone+'static,
Res2:Hash+Eq+Debug+Clone+'static>
(thunk:Art<Res1>, map_fn:Rc<dyn Fn(Res1) -> Res2>) -> Art<Res2>
{
Art{art:EnumArt::Force(
Rc::new(NaiveThunk{
id:NameChoice::Naive,
prog_pt:ProgPt{symbol:"engine::thunk_map"},
fn_box:{Rc::new(Box::new(move |_,_| {
let res1 = force(&thunk);
let res2 = map_fn(res1);
res2
}))},
arg:(),
spurious:()
}))
}
}
pub fn force<T:Hash+Eq+Debug+Clone+'static> (a:&Art<T>) -> T {
match a.art {
EnumArt::Force(ref f) => f.force(),
EnumArt::Rc(ref rc) => (&**rc).clone(),
EnumArt::Loc(ref loc) => {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg_refcell) =>
<DCG as Adapton>::force(dcg_refcell, &AbsArt::Loc(loc.clone()), None),
Engine::Naive => panic!("cannot force a non-naive location with the naive engine")
}})
}
}
}
pub fn force_cycle<T:Hash+Eq+Debug+Clone+'static> (a:&Art<T>, cycle_out:Option<T>) -> T {
match a.art {
EnumArt::Force(ref f) => f.force(),
EnumArt::Rc(ref rc) => (&**rc).clone(),
EnumArt::Loc(ref loc) => {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg_refcell) =>
<DCG as Adapton>::force(dcg_refcell, &AbsArt::Loc(loc.clone()), cycle_out),
Engine::Naive => panic!("cannot force a non-naive location with the naive engine")
}})
}
}
}
pub fn force_map<T:Hash+Eq+Debug+Clone+'static,
S:Hash+Eq+Debug+Clone+'static,
MapF:'static>
(a:&Art<T>, mapf:MapF) -> S
where MapF:Fn(&Art<T>, T) -> S
{
match a.art {
EnumArt::Force(ref f) => mapf(a, f.force()),
EnumArt::Rc(ref rc) => mapf(a, (&**rc).clone()),
EnumArt::Loc(ref loc) => {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg_refcell) =>
<DCG as Adapton>::force_map(dcg_refcell, &AbsArt::Loc(loc.clone()), mapf),
Engine::Naive => panic!("cannot force a non-naive location with the naive engine")
}
})
}
}
}
pub fn force_abs
<Arg:'static+Eq+Debug+Clone+Hash,
Abs:'static+Eq+Debug+Clone+Hash,
T:'static+Eq+Debug+Clone+Hash,
DiffT:'static+Eq+Debug+Clone+Hash,
S:'static+Eq+Debug+Clone+Hash>
(absmapfam:Box<dyn AbsMapFam<Arg,Abs,T,DiffT,S>>, arg:Arg, a:Art<T>) -> S
{
match a.art {
EnumArt::Force(ref f) => absmapfam.map(arg, f.force()),
EnumArt::Rc(ref rc) => absmapfam.map(arg, (&**rc).clone()),
EnumArt::Loc(ref loc) => {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(ref dcg_refcell) =>
<DCG as Adapton>::force_abs(dcg_refcell, absmapfam, arg, &AbsArt::Loc(loc.clone())),
Engine::Naive => panic!("cannot force a non-naive location with the naive engine")
}
})
}
}
}
pub mod manage {
use super::*;
pub fn init_dcg () -> Engine { init_engine(Engine::DCG(RefCell::new(DCG::new()))) }
pub fn init_naive () -> Engine { init_engine(Engine::Naive) }
pub fn use_engine (engine: Engine) -> Engine {
use std::mem;
let mut engine = engine;
GLOBALS.with(|g| {
mem::swap(&mut g.borrow_mut().engine, &mut engine);
});
return engine
}
pub fn init_engine (engine: Engine) -> Engine {
use_engine(engine)
}
pub fn engine_is_naive () -> bool {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(_) => false,
Engine::Naive => true
}})
}
pub fn engine_is_dcg () -> bool {
GLOBALS.with(|g| {
match g.borrow().engine {
Engine::DCG(_) => true,
Engine::Naive => false
}})
}
}
mod wf {
use std::collections::HashMap;
use std::rc::Rc;
use std::io::BufWriter;
use std::io::Write;
use std::fs::File;
use super::*;
#[derive(Eq,PartialEq,Clone)]
enum NodeStatus {
Dirty, Clean, Unknown
}
type Cs = HashMap<Rc<Loc>, NodeStatus> ;
fn add_constraint (cs:&mut Cs, loc:&Rc<Loc>, new_status: NodeStatus)
{
let old_status = match
cs.get(loc) { None => NodeStatus::Unknown,
Some(x) => (*x).clone() } ;
match (old_status, new_status) {
(NodeStatus::Clean, NodeStatus::Dirty) |
(NodeStatus::Dirty, NodeStatus::Clean) => {
panic!("{:?}: Constrained to be both clean and dirty: Inconsistent status => DCG is not well-formed.")
},
(NodeStatus::Unknown, new_status) => { cs.insert(loc.clone(), new_status); () },
(old_status, NodeStatus::Unknown) => { cs.insert(loc.clone(), old_status); () },
(ref old_status, ref new_status) if old_status == new_status => { },
_ => unreachable!(),
}
}
fn dirty (st:&DCG, cs:&mut Cs, loc:&Rc<Loc>) {
add_constraint(cs, loc, NodeStatus::Dirty) ;
let node = match st.table.get(loc) { Some(x) => x, None => panic!("") } ;
for (pred,_) in node.preds_obs () {
let succ = super::get_succ(st, &pred, super::Effect::Observe, loc) ;
if succ.dirty {} else {
debug_dcg(st);
write_next_dcg(st, None);
panic!("Expected dirty edge, but found clean edge: {:?} --Observe--dirty:!--> {:?}", &pred, loc);
} ;
dirty(st, cs, &pred)
}
}
fn clean (st:&DCG, cs:&mut Cs, loc:&Rc<Loc>) {
add_constraint(cs, loc, NodeStatus::Clean) ;
let node = match st.table.get(loc) {
Some(x) => x,
None => { panic!("dangling: {:?}", loc) }
} ;
if ! node.succs_def () { return } ;
for succ in node.succs () {
let succ = super::get_succ(st, loc, super::Effect::Observe, &succ.loc) ;
assert!( ! succ.dirty );
clean(st, cs, &succ.loc)
}
}
pub fn check_dcg (st:&mut DCG) {
if st.flags.write_dcg {
let dcg_hash = my_hash(format!("{:?}",st.table));
if dcg_hash != st.dcg_hash {
println!("adapton: dcg #{} hash: {:?}", st.dcg_count, dcg_hash);
st.dcg_hash = dcg_hash;
let dcg_count = st.dcg_count;
st.dcg_count += 1;
write_next_dcg(st, Some(dcg_count));
}
} ;
if st.flags.check_dcg_is_wf {
let mut cs = HashMap::new() ;
for frame in st.stack.iter() {
clean(st, &mut cs, &frame.loc)
}
for (loc, node) in &st.table {
if ! node.succs_def () { continue } ;
for succ in node.succs () {
if succ.dirty {
dirty(st, &mut cs, loc)
}
}
}
}}
pub fn write_next_dcg (st:&DCG, num:Option<usize>) {
let name = match num {
None => format!("adapton-dcg.dot"),
Some(n) => format!("adapton-dcg-{:08}.dot", n),
} ;
let mut file = File::create(name).unwrap() ;
write_dcg_file(st, &mut file);
}
pub fn write_dcg_file (st:&DCG, file:&mut File) {
let mut writer = BufWriter::new(file);
writeln!(&mut writer, "digraph {{\n").unwrap();
writeln!(&mut writer, "ordering=out;").unwrap();
for frame in st.stack.iter() {
writeln!(&mut writer, "\"{:?}\" [color=blue,penwidth=10];", frame.loc).unwrap();
for succ in frame.succs.iter() {
writeln!(&mut writer, "\"{:?}\" -> \"{:?}\" [color=blue,weight=10,penwidth=10];", &frame.loc, &succ.0.loc).unwrap();
}
};
for (loc, node) in &st.table {
if ! node.succs_def () {
writeln!(&mut writer, "\"{:?}\" [shape=box];", loc).unwrap();
continue;
} ;
for succ in node.succs () {
if succ.dirty {
writeln!(&mut writer, "\"{:?}\" -> \"{:?}\" [color=red,weight=5,penwidth=5];", &loc, &succ.loc).unwrap();
} else {
let (weight, penwidth, color) =
match succ.effect {
super::Effect::Observe => (0.1, 1, "grey"),
super::Effect::Allocate => (2.0, 3, "darkgreen") } ;
writeln!(&mut writer, "\"{:?}\" -> \"{:?}\" [weight={},penwidth={},color={}];",
&loc, &succ.loc, weight, penwidth, color).unwrap();
}
}
}
writeln!(&mut writer, "}}\n").unwrap();
}
pub fn debug_dcg (st:&DCG) {
let prefix = "debug_dcg::stack: " ;
let mut frame_num = 0;
for frame in st.stack.iter() {
println!("{} frame {}: {:?}", prefix, frame_num, frame.loc);
for succ in frame.succs.iter() {
println!("{} frame {}: \t\t {:?}", prefix, frame_num, &succ);
}
frame_num += 1;
}
let prefix = "debug_dcg::table: " ;
for (loc, node) in &st.table {
println!("{} {:?} ==> {:?}", prefix, loc, node);
if ! node.succs_def () { continue } ;
for succ in node.succs () {
println!("{}\t\t{:?}", prefix, succ);
}
}
}
pub fn check_stack_is_clean (st:&DCG) {
let stack = st.stack.clone() ;
for frame in stack.iter() {
let node = match st.table.get(&frame.loc) {
Some(x) => x,
None => { panic!("dangling: {:?}", &frame.loc) }
} ;
if ! node.succs_def () { return } ;
for succ in node.succs () {
let succ = super::get_succ(st, &frame.loc, succ.effect.clone(), &succ.loc) ;
assert!( succ.dirty );
}
}
}
}
#[test]
fn test_cycles () -> () {
fn adjs (n:usize) -> (usize, usize) {
match n {
0 => (1, 0),
1 => (2, 3),
2 => (3, 0),
3 => (3, 1),
_ => unimplemented!()
}
}
#[warn(unconditional_recursion)]
fn explore_rec(cur_n:usize) -> Vec<usize> {
println!("explore {}", cur_n);
let (a,b) = adjs(cur_n);
let mut av = explore_rec(a);
let mut bv = explore_rec(b);
let mut res = vec![cur_n];
res.append(&mut av);
res.append(&mut bv);
res
}
fn explore_thunk(cur_n:usize) -> Art<Vec<usize>> {
thunk!([Some(name_of_usize(cur_n))]? explore ; n:cur_n)
}
fn explore(cur_n:usize) -> Vec<usize> {
println!("explore {}", cur_n);
let (a,b) = adjs(cur_n);
let at = explore_thunk(a);
let bt = explore_thunk(b);
println!(" explore {} goes to {:?} and {:?}", cur_n, at, bt);
let mut av = get!(at, vec![]);
let mut bv = get!(bt, vec![]);
let mut res = vec![cur_n];
res.append(&mut av);
res.append(&mut bv);
println!(" res={:?}", res);
res
}
super::engine::manage::init_dcg();
assert_eq!(get!(explore_thunk(0)), vec![0,1,2,3,3])
}