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