-- import Control.Exception
import Control.Lens ((^.))
import Control.Monad
+import Control.Monad.State
-- import qualified Data.ByteString as B
-- import qualified Data.ByteString.UTF8 as BU
-- import Data.Char
import Dyna.Term.TTerm
-- import qualified Dyna.ParserHS.Parser as P
import Dyna.XXX.PPrint
+import Dyna.XXX.MonadUtils
import Dyna.XXX.Trifecta (prettySpanLoc)
import System.IO
import Text.PrettyPrint.Free
filterGround = map (^.mv_var) . filter (not.nGround.(^.mv_mi))
-- | Render a single dopamine opcode or its surrogate
-pdope_ :: DOpAMine PyDopeBS -> Doc e
+pdope_ :: DOpAMine PyDopeBS -> State Int (Doc e)
pdope_ (OPIndr _ _) = dynacSorry "indirect evaluation not implemented"
-pdope_ (OPAsgn v val) = pretty v <+> equals <+> pretty val
-pdope_ (OPCheq v val) = "if" <+> pretty v <+> "!="
- <+> pretty val <> ": continue"
-pdope_ (OPCkne v val) = "if" <+> pretty v <+> "=="
- <+> pretty val <> ": continue"
-pdope_ (OPPeel vs i f) =
+pdope_ (OPAsgn v val) = return $ pretty v <+> equals <+> pretty val
+pdope_ (OPCheq v val) = return $ "if" <+> pretty v <+> "!="
+ <+> pretty val <> ": continue"
+pdope_ (OPCkne v val) = return $ "if" <+> pretty v <+> "=="
+ <+> pretty val <> ": continue"
+pdope_ (OPPeel vs i f) = return $
"try:" `above` (indent 4 $
tupledOrUnderscore vs
<+> equals
)
-- you'll get a "TypeError: 'NoneType' is not iterable."
`above` "except (TypeError, AssertionError): continue"
-pdope_ (OPWrap v vs f) = pretty v
+pdope_ (OPWrap v vs f) = return $ pretty v
<+> equals
<+> "build"
<> (parens $ pfas f vs <> comma
<> (sepBy "," $ map pretty vs))
-pdope_ (OPIter v vs f _ (Just (PDBS c))) = pretty (v^.mv_var)
+pdope_ (OPIter v vs f Det (Just (PDBS c))) = return $ pretty (v^.mv_var)
<+> equals
<+> c v vs
-pdope_ (OPIter o m f _ Nothing) =
- let mo = m ++ [o] in
- "for" <+> piterate mo --(tupledOrUnderscore $ filterGround mo)
+pdope_ (OPIter o m f _ Nothing) = do
+ i <- incState
+ return $ let mo = m ++ [o] in
+ "for" <+> "d" <> pretty i <> "," <> piterate mo
<+> "in" <+> functorIndirect "chart" f m <> pslice mo <> colon
-- XXX Ought to make i and vs conditional on... doing debugging or the
-- aggregator for this head caring. The latter is a good bit more
-- advanced than we are right now.
-pdope_ (OPEmit h r i vs) =
- "emit" <> tupled [ pretty h
- , pretty r
- , pretty i
- , varmap
- ]
- where
+pdope_ (OPEmit h r i vs) = do
+ ds <- get
+
-- A python map of variable name to value
- varmap = encloseSep lbrace rbrace comma
- $ map (\v -> let v' = pretty v in dquotes v' <> colon <+> v') vs
+ let varmap = encloseSep lbrace rbrace comma $
+ ("'nodes'" <> colon <> (encloseSep lbracket rbracket comma $ map (("d"<>).pretty) [0..ds-1]))
+ : (map (\v -> let v' = pretty v in dquotes v' <> colon <+> v') vs)
+
+ return $ "emit" <> tupled [ pretty h
+ , pretty r
+ , pretty i
+ , varmap
+ ]
-- | Render a dopamine sequence's checks and loops above a (indended) core.
pdope :: Actions PyDopeBS -> Doc e
pdope _d = (indent 4 $ "for _ in [None]:")
- `above` (indent 8 $ go _d)
+ `above` (indent 8 $ evalState (go _d) 0)
where
- go [] = empty
+ go [] = return empty
go (x:xs) = let indents = case x of OPIter _ _ _ d _ -> d /= Det ; _ -> False
- in above (pdope_ x)
- . (if indents then indent 4 else id)
- $ go xs
+ in do
+ x' <- pdope_ x
+ xs' <- go xs
+ return $ x' `above` ((if indents then indent 4 else id) xs')
printPlanHeader :: Rule -> Cost -> Maybe Int -> Doc e
head = re.sub('"', r'\\"', head)
body = map(lambda b: re.sub('"', r'\\"', b), body)
-
e = Edge(head, label, tuple(body))
self.edges.append(e)
def render(self, name, sty=None):
sty = sty or defaultdict(dict)
+ # TODO: misses orphaned nodes.
+
dot = '%s.dot' % name
svg = '%s.svg' % name
with file(dot, 'wb') as f:
print >> f, 'digraph rule {'
print >> f, 'rankdir=LR;' # left-to-right layout
-
print >> f, 'node [style=filled,fillcolor=white];'
-
print >> f, 'bgcolor="transparent";'
-
print >> f, 'edge [color=white];'
for e in self.edges:
# node styles
for x in self.nodes:
- sty[x].update({'shape': 'circle'})
+# sty[x].update({'shape': 'circle'})
+ sty[e].update({'shape': 'rectangle'})
print >> f, '"%s" [%s]' % (x, ','.join('%s="%s"' % (k,v) for k,v in sty[x].items()))
# edge styles
return t(root)
-def show_slice(e, M):
- return [(b if bind else ':') for b, bind in zip(e.body, M)]
+#def show_slice(e, M):
+# return [(b if bind else ':') for b, bind in zip(e.body, M)]
def isvar(x):
print >> html, '</div>'
if browser:
- #os.system('gnome-open %s 2>/dev/null >/dev/null' % html.name)
import webbrowser
webbrowser.open(html.name)
thinking because we don't yet know what things in the chart have been
touched.
- - TODO: transactions for errors.
-
- TODO: Numeric precision is an issue with BAggregators.
a[0.1] += 1
from functools import partial
from argparse import ArgumentParser
+from StringIO import StringIO
+import webbrowser
+
from utils import ip, red, green, blue, magenta, yellow, dynahome, \
notimplemented, prioritydict
from defn import aggregator
return None
-error_suppression = False
+error_suppression = True
trace = None
agenda = prioritydict()
agg_decl = aggregator_declaration()
chart = chart_indirect()
+errors = {}
def dump_charts(out=sys.stdout):
print >> out, chart[x]
print >> out
+ if errors:
+ print >> out
+ print >> out, 'Errors'
+ print >> out, '============'
+ for item, (val, es) in errors.items():
+ print >> out, 'because %r is %r:' % (item, val)
+ for e in es:
+ print >> out, ' ', e
+ print >> out
+
# TODO: codegen should output a derived Term instance for each functor
-class Term(namedtuple('Term', 'fn args'), object):
+class Term(object):
+
+ __slots__ = 'fn args value aggregator'.split()
def __init__(self, fn, args):
- self._value = None
+ self.fn = fn
+ self.args = args
+ self.value = None
self.aggregator = None
- super(Term, self).__init__(fn, args)
+
+ def __cmp__(self, other):
+ if other is None:
+ return 1
+ return cmp((self.fn, self.args), (other.fn, other.args))
+
+ # default hash and eq suffice because we intern
+ #def __hash__(self):
+ #def __eq__(self):
def __repr__(self):
"Pretty print a term. Will retrieve the complete (ground) term."
return fn
return '%s(%s)' % (fn, ','.join(map(repr, self.args)))
- __add__ \
- = __sub__ \
- = __mul__ \
- = notimplemented
+ __add__ = __sub__ = __mul__ = notimplemented
-# @property
-# def value(self):
-# return self._value
-# @value.setter
-# def value(self, val):
-# assert not isinstance(val, tuple) or isinstance(val, Term)
-# self._value = val
+_edges = defaultdict(set)
+def edges():
+ def _emit(item, val, ruleix, variables):
+ b = variables['nodes']
+ b.sort()
+ b = tuple(b)
+ _edges[item].add((ruleix, b))
+ for init in initializer.handlers:
+ init(emit=_emit)
class Chart(object):
if isinstance(val, slice):
for term in candidates:
if term.value is not None:
- yield term.args + (term.value,)
+ yield term, term.args + (term.value,)
else:
for term in candidates:
if term.value == val:
- yield term.args + (term.value,)
+ yield term, term.args + (term.value,)
def lookup(self, args):
"find index for these args"
def build(fn, *args):
- if fn == "true/0": # TODO: I'd rather have the codegen ensure true/0 is True and false/0 is False
+ # TODO: codegen should handle true/0 is True and false/0 is False
+ if fn == "true/0":
return True
if fn == "false/0":
return False
"""
Passes update to relevant handlers.
"""
+
if val is None:
return
+
for handler in register.handlers[item.fn]:
emittiers = []
_emit = lambda item, val, ruleix, variables: \
- emittiers.append(lambda: emit(item, val, ruleix, variables, delete=delete))
+ emittiers.append((item, val, ruleix, variables, delete))
try:
handler(item, val, emit=_emit)
if error_suppression:
#print >> trace,
print '%s on update %s = %s' % (e, item, val)
+
+ if item not in errors:
+ errors[item] = (val, [])
+ errors[item][1].append(e)
+
+ # TODO: store which rule.
+
else:
raise e
else:
# no exception, accept emissions.
for e in emittiers:
- e()
+ # an error could happen here, but we assume (by contract) that
+ # this is not possible.
+ emit(*e)
def emit(item, val, ruleix, variables, delete):
was = item.value
print >> trace, '(was: %s,' % (was,),
- now = item.aggregator.fold()
+ if item in errors: # clear the error
+ del errors[item]
+
+ try:
+ now = item.aggregator.fold()
+ except (ZeroDivisionError, TypeError) as e:
+ errors[item] = ('failed to aggregate %r' % item.aggregator, [e])
+ # TODO: Are we sure there is never a reason to requeue this item.
+ continue
+
print >> trace, 'now: %s)' % (now,)
if was == now:
else:
self.do_changed('')
+ def do_draw(self, _):
+ draw()
+
def cmdloop(self, _=None):
try:
super(REPL, self).cmdloop()
def repl(hist):
REPL(hist).cmdloop()
+
+def hypergraph():
+ from debug import Hypergraph
+ # collect edges
+ edges()
+ # create hypergraph object
+ g = Hypergraph()
+ for c in chart.values():
+ for x in c.intern.values():
+ for e in _edges[x]:
+ label, body = e
+ g.edge(str(x), str(label), map(str, body))
+ return g
+
+
+def draw():
+ g = hypergraph()
+ with file('/tmp/state.html', 'wb') as f:
+ print >> f, """
+ <html>
+ <head>
+ <style>
+ body {
+ background-color: black;
+ color: white;
+ }
+ </style>
+ </head>
+ <body>
+ """
+
+ x = StringIO()
+ dump_charts(x)
+
+ print >> f, '<div style="position:absolute;">%s</div>' \
+ % '<h1>Charts</h1>%s' \
+ % '<pre style="width: 500px;">%s</pre>' \
+ % x.getvalue()
+
+ print >> f, """
+<div style="width: 800px; position:absolute; left: 550px">
+<h1>Hypergraph</h1>
+%s
+</div>
+""" % g.render('/tmp/hypergraph')
+
+ print >> f, '</body></html>'
+
+ webbrowser.open(f.name)
+
def main():
# from repl import repl
parser.add_argument('--trace', default='/tmp/dyna.log')
parser.add_argument('-i', dest='interactive', action='store_true', help='Fire-up an IPython shell.')
parser.add_argument('-o', dest='output', help='Output chart.')
+ parser.add_argument('--draw', action='store_true',
+ help='Output html page with hypergraph and chart.')
argv = parser.parse_args()
else:
repl(hist = '/tmp/dyna.hist')
+ if argv.draw:
+ draw()
+
+
if __name__ == '__main__':
main()