]> hydra-www.ietfng.org Git - dyna2/commitdiff
`draw_circuit` and `trace` work with BC computation
authorTim Vieira <tim.f.vieira@gmail.com>
Fri, 28 Jun 2013 05:31:32 +0000 (01:31 -0400)
committerTim Vieira <tim.f.vieira@gmail.com>
Fri, 28 Jun 2013 05:31:32 +0000 (01:31 -0400)
BUGFIX: looks like my previous bugs fix broke alignment with python and dopamine
  code. I've fixed the Python code. Dopamine coming soon.

REPL `query` no longer kept up-to-date with FC because we throw away the rule
once we're done with it.

added REPL `run` command to execute dyna code from a different file (NOTE: there
is no namespacing so bad things can happen.)

BUGFIX: Fixed cyclic `Term` comparsion.

BUGFIX: Fixed dictionary changed size on iteration exception. Thanks to Jason
  for reporting.

src/Dyna/Backend/Python/Backend.hs
src/Dyna/Backend/Python/chart.py
src/Dyna/Backend/Python/debug.py
src/Dyna/Backend/Python/interpreter.py
src/Dyna/Backend/Python/post/draw_circuit.py
src/Dyna/Backend/Python/post/trace.py
src/Dyna/Backend/Python/repl.py
src/Dyna/Backend/Python/term.py
src/Dyna/Backend/Python/utils.py

index 337f7436b3a2f5e94791e7b64f823f606c6a3d5a..0b297b5db16076a106bb60a8cf5c414e392aca01 100644 (file)
@@ -253,17 +253,25 @@ pdope_ _ (OPIter v vs f d   (Just (PDBS c))) = dynacPanic $
     <+> parens (pretty $ c v vs)
 
 -- XXX This works only for the special case at hand (thus the asserts)
-pdope_ bc (OPIter o m f DetSemi Nothing) | (f,length m) `S.member` bc =
+pdope_ bc (OPIter o m f DetSemi Nothing) | (f,length m) `S.member` bc = do
+  dookie <- incState
   return $
-  assert (iIsFree $ nExpose $ o^.mv_mi) $
-  assert (all (not . iIsFree . nExpose . _mv_mi) m) $
-  vcat
-  [     pretty (o^.mv_var)
-    <+> equals
-    <+> "gbc"
-    <> tupled (pfas f m : map (pretty . _mv_var) m)
-  , "if" <+> pretty (o^.mv_var) <+> "is not None" <> colon
-  ]
+   assert (iIsFree $ nExpose $ o^.mv_mi) $
+   assert (all (not . iIsFree . nExpose . _mv_mi) m) $
+   vcat
+   [     pretty (o^.mv_var)
+     <+> equals
+     <+> "gbc"
+     <> tupled (pfas f m : map (pretty . _mv_var) m)
+
+--- needs an opbuild
+   , ("d" <> pretty dookie)
+     <+> equals
+     <+> ("build" <> tupled (pfas f m : map (pretty . _mv_var) m))
+
+   , "if" <+> pretty (o^.mv_var) <+> "is not None" <> colon
+
+   ]
 
 pdope_ bc (OPIter o m f _ Nothing) =
   assert (not $ (f,length m) `S.member` bc) $ do
index 7bd2f0716786a01c094e6db57770d892d5e5f457..7b90ad6f680a9191e096ee91e9839b23a60f9d01 100644 (file)
@@ -41,10 +41,10 @@ class Chart(object):
 
         if len(b) == 0:
             # all arguments are free.
-            candidates = self.intern.itervalues()
+            candidates = self.intern.values()
 
         elif len(b) == 1:
-            candidates = iter(b[0])
+            candidates = list(b[0])
 
         else:
             b.sort(key=len)           # start with smaller ones
index 9823e42f495e970969980b16d8e6e4bf0ea8290e..e42d132f2f5980e41b3f57346fe3dd198b28e71e 100644 (file)
@@ -349,51 +349,31 @@ Update %s
 """ % (bline, kv, block)
 
 
+        # -------------
+        # Dopamine code
         with file(d + '/dopini') as f:
             code = f.read()
 
             print >> html, '<h2>Initialization plans</h2>'
 
             for (f,bline,bcol,eline,ecol,kv,block) in \
-                re.findall(';; (.*?):(\d+):(\d+)-.*?:(\d+):(\d+) (.*)\n((?: [^\n]*\n)*)'
-                          , code) :
+                    re.findall(';; (.*?):(\d+):(\d+)-.*?:(\d+):(\d+) (.*)\n((?: [^\n]*\n)*)', code):
+                print >> html, """<div class="dopamine-%s"><pre>Initializer:\n%s</pre></div>""" % (bline, block)
 
-                print >> html, """\
-<div class="dopamine-%s">
-<pre>
-Initializer:
-%s
-</pre>
-</div>
-""" % (bline, block)
 
+        # ----------------
+        # Python code
         with file(d + '/plan') as f:
             code = f.read()
-            # print >> html, code
-
             print >> html, '<h2>Update code</h2>'
-
             for block in re.split('\n\s*\n', code):
-
-                x = re.findall('Span:\s*(.*?):(\d+):(\d+)-.*?:(\d+):(\d+)\n',
-                               block)
-
+                x = re.findall('RuleIx: (\d+)\n', block)
                 if not x:
                     continue
-
-                [(f, bline, bcol, eline, ecol)] = x
-                code = block
+                [ruleix] = x
                 lexer = get_lexer_by_name("python", stripall=True)
                 formatter = HtmlFormatter(linenos=False)
-                pretty_code = highlight(code, lexer, formatter)
-
-                print >> html, """\
-<div class="handler-%s">
-<pre>
-%s
-</pre>
-</div>
-""" % (bline, pretty_code)
+                print >> html, """<div class="handler-%s"><pre>%s</pre></div>""" % (ruleix, highlight(block, lexer, formatter))
 
         print >> html, '</pre>'
         print >> html, '</div>'
index f3998b59ee6d3c026b2f78ebe139b81a8fd7c7f2..2bf13b9b426c5259e97010584129e00c282afadd 100644 (file)
@@ -39,7 +39,6 @@ TODO
    - functor
    - ignore variable
 
-   - output formats: vquery and query
    - show diffs
 
    Maybe subscription to diff is a different beast, only available as a
@@ -200,7 +199,7 @@ import load, post
 from chart import Chart, Term, _repr
 from defn import aggregator
 from utils import ip, red, green, blue, magenta, yellow, parse_attrs, \
-    ddict, dynac, read_anf
+    ddict, dynac, read_anf, strip_comments
 
 from prioritydict import prioritydict
 from config import dotdynadir
@@ -244,7 +243,7 @@ class Rule(object):
         return parse_attrs(self.init or self.query)['Span']
     @property
     def src(self):
-        return parse_attrs(self.init or self.query)['rule']
+        return strip_comments(parse_attrs(self.init or self.query)['rule'])
     def __repr__(self):
         return 'Rule(%s, %r)' % (self.idx, self.src)
 
@@ -281,9 +280,6 @@ class Interpreter(object):
         self.rules = ddict(Rule)
         self.error = {}
 
-        # not essential, available in parser_state
-        self.backchained = set()
-
     def __getstate__(self):
         return ((self.chart,
                  self.agenda,
@@ -327,7 +323,7 @@ class Interpreter(object):
         print >> out, '========'
         fns = self.chart.keys()
         fns.sort()
-        fns = [x for x in fns if x not in self.backchained]  # don't show backchained items
+        fns = [x for x in fns if x not in self._gbc]  # don't show backchained items
         nullary = [x for x in fns if x.endswith('/0')]
         others = [x for x in fns if not x.endswith('/0')]
         # show nullary charts first
@@ -364,26 +360,6 @@ class Interpreter(object):
         for i in sorted(self.rules):
             print '%3s: %s' % (i, self.rules[i].src)
 
-#    def query(self, q):
-#        if q.endswith('.'):
-#            print "Queries don't end with a dot."
-#            return
-#
-#        query = 'out("%s") dict= %s.' % (q, q)
-#
-#        src = self.dynac_code(query)   # might raise DynaCompilerError
-#        self.do(src)
-#
-#        try:
-#            [(_, _, results)] = self.chart['out/1'][q,:]
-#        except ValueError:
-#            print 'No results.'
-#            return
-#
-#        for val, bindings in results:
-#            print '   ', val, 'when', bindings
-#        print
-
     def build(self, fn, *args):
         # TODO: codegen should handle true/0 is True and false/0 is False
         if fn == "true/0":
@@ -401,24 +377,10 @@ class Interpreter(object):
 
 #    def retract_item(self, item):
 #        """
-#        For the moment we only correctly retract leaves.
-#
-#        If you retract a non-leaf item, you run the risk of it being
-#        rederived. In the case of cyclic programs the derivation might be the
-#        same or different.
+#        For the moment we only correctly retract leaves. If you retract a
+#        non-leaf item, you run the risk of it being rederived. In the case of
+#        cyclic programs the derivation might be the same or different.
 #        """
-#        # and now, for something truely horrendous -- look up an item by it's
-#        # string value! This could fail because of whitespace or trivial
-#        # formatting differences.
-#        items = {}
-#        for c in self.chart.values():
-#            for i in c.intern.values():
-#                items[str(i)] = i
-#        try:
-#            item = items[item]
-#        except KeyError:
-#            print 'item not found. This could be because of a trivial formatting differences...'
-#            return
 #        self.emit(item, item.value, None, sys.maxint, delete=True)
 #        return self.go()
 
@@ -571,7 +533,6 @@ class Interpreter(object):
         A rule is bad if the compiler rejects it or it's initializer fails.
         """
         assert os.path.exists(filename)
-#        assert os.path.exists(filename + '.anf')
 
         env = imp.load_source('dynamically_loaded_module', filename)
 
@@ -591,6 +552,13 @@ class Interpreter(object):
         for k, v in env.agg_decl.items():
             self.new_fn(k, v)
 
+        new_rules = set()    
+        for _, r, _ in env.queries:
+            new_rules.add(r)
+        for r, _ in env.initializers:
+            new_rules.add(r)
+        self.new_rules = new_rules
+
         for fn, r, h in env.queries:
             self.new_query(fn, r, h)
 
@@ -619,8 +587,6 @@ class Interpreter(object):
             # accept the new parser state
             self.parser_state = env.parser_state
 
-            self.backchained = {f + '/' + a for f, a in re.findall(":-backchain '([^']+)'/(\d+).", env.parser_state)}
-
             # process emits
             for e in emits:
                 self.emit(*e, delete=False)
@@ -709,7 +675,6 @@ def main():
         if args.plan:
             plan = args.source
         else:
-#            plan = dotdynadir / 'tmp' / args.source.read_hexhash('sha1') + '.plan.py'
             plan = args.source + '.plan.py'
             dynac(args.source, plan)
 
index e697a3e550a3fc77f93648cbaed2cbbfe9d9764c..b01a5e78c6e7c1b848a8bb09515b586325fcc683 100644 (file)
@@ -1,7 +1,4 @@
 # -*- coding: utf-8 -*-
-"""
-TODO: nwf remove comments from rule source
-"""
 
 import webbrowser
 from debug import Hypergraph
@@ -29,7 +26,17 @@ def infer_edges(interp):
         edges.add((item, ruleix, tuple(b), variables))
 
     for r in interp.rules.values():
-        r.init(emit=_emit)
+        if r.init is not None:
+            r.init(emit=_emit)
+        else:
+            assert r.query is not None
+
+    # todo: this might pick nodes that aren't used
+    for fn, hs in interp._gbc.items():
+        for x in interp.chart[fn].intern.values():
+            if x.value is not None:
+                for h in hs:
+                    h(*x.args, emit=_emit)
 
     return edges
 
@@ -42,8 +49,7 @@ class draw_circuit(object):
     def __init__(self, interp):
         self.interp = interp
 
-    def main(self, outfile):
-        global interp
+    def main(self, outfile, open=True):
         interp = self.interp
 
         es = infer_edges(interp)
@@ -80,4 +86,5 @@ class draw_circuit(object):
 
             print >> f, '</body></html>'
 
-        webbrowser.open(f.name)
+        if open:
+            webbrowser.open(f.name)
index ba54ca2e12fb3301a23edca2c801ff5b748160b6..c28b8f270a46ad8b216bd1e05ff380f307f0d3b1 100644 (file)
@@ -64,7 +64,7 @@ def dig(head, visited, groups, interp):
             block = branch([dig(x, visited, groups, interp) for x in body])
 
             if block:
-                contribs.append(crux.format() + [''] + block)
+                contribs.append(crux.format() + ['|'] + block)
             else:
                 contribs.append(crux.format())
 
index 3af412010cb6e1480b7ff195357e99efafd9d3c6..aaa1cccc33ca87a7eeee5f007c14bed731d92c79 100644 (file)
@@ -1,7 +1,7 @@
 """
 TODO: unsubscribe
 
-TODO: should probably remove the new rule after we get the results.
+TODO: query should probably remove the new rule after we get the results.
 
 TODO: subscriptions probably should only show "changes"
 
@@ -17,7 +17,7 @@ TODO: $include load rules from a file.
 import re, os, cmd, readline
 
 import debug, interpreter
-from utils import ip, lexer, subst
+from utils import dynac, ip, lexer, subst
 from errors import DynaCompilerError, DynaInitializerException
 from chart import _repr
 from config import dotdynadir
@@ -136,17 +136,43 @@ class REPL(cmd.Cmd, object):
             f.write(line)
         debug.main(f.name)
 
+    def do_run(self, filename):
+        """
+        Load dyna rules from `filename`.
+
+        :- run examples/papa.dyna
+
+        """
+        try:
+            changed = self.interp.do(dynac(filename))
+        except DynaCompilerError as e:
+            print e
+        else:
+            self._changed(changed)
+
     def _query(self, q):
         if q.endswith('.'):
             print "Queries don't end with a dot."
             return
-        query = '$out(%s) dict= %s.' % (self.lineno, q)
-        self.default(query, show_changed=False)
+
+        self.interp.new_rules = set()
+
         try:
-            [(_, _, results)] = self.interp.chart['$out/1'][self.lineno,:]
-        except ValueError:
-            return []
-        return results
+            query = "$out(%s) dict= %s." % (self.lineno, q)
+            self.default(query, show_changed=False)
+            try:
+                [(_, _, results)] = self.interp.chart['$out/1'][self.lineno,:]
+                return results
+            except ValueError:
+                return []
+        finally:
+            # cleanup:
+            # retract newly added rules.
+            for r in self.interp.new_rules:
+                self.interp.retract_rule(r)
+            # drop $out chart
+            del self.interp.chart['$out/1']
+
 
     def do_vquery(self, q):
         """
index f03d600135e0eac84aa169a5123027ac3dde09ef..79b96b26e97ac61f4608a808076dfed97cc9e3ac 100644 (file)
@@ -12,17 +12,24 @@ class Term(object):
         self.value = None
         self.aggregator = None
 
+    def __eq__(self, other):
+        if other is None:
+            return False
+        if not isinstance(other, Term):
+            return False
+        return self.fn == other.fn and self.args == other.args
+
     def __cmp__(self, other):
+#        if self is other:
+#            return 0
         if other is None:
             return 1
         if not isinstance(other, Term):
             return 1
+#        if self == other:
+#            return 0
         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."
         fn = '/'.join(self.fn.split('/')[:-1])  # drop arity from name.
@@ -226,4 +233,3 @@ def symbol(name):
 #    X.value = Y
 #    print [X,Y,Z]
 #    assert X.value == Y.value == Z.value == 3
-
index 7e6d0cd96187efdfdb9fa0e73a562d31372425ff..92162e405c9c184d469efc1f0f27af458b21a7ea 100644 (file)
@@ -29,13 +29,25 @@ black, red, green, yellow, blue, magenta, cyan, white = \
     map('\033[3%sm%%s\033[0m'.__mod__, range(8))
 
 
-def dynac(f, out):
+_comments = re.compile('%.*$', re.MULTILINE)
+def strip_comments(src):
+    return _comments.sub('', src).strip()
+
+
+def dynac(f, out=None):
     """
     Run compiler on file, ``f``, write results to ``out``. Raises
     ``DynaCompilerError`` on failure.
     """
     from errors import DynaCompilerError
 
+    f = path(f)
+    if not f.exists():
+        raise DynaCompilerError("File '%s' does not exist." % f)
+
+    if out is None:
+        out = dotdynadir / 'tmp' / f.read_hexhash('sha1') + '.plan.py'
+
     p = Popen(['%s/dist/build/dyna/dyna' % dynahome,
                '--dump-anf=' + out + '.anf',                                 # timv: don't like this filename...
                '-B', 'python', '-o', out, f], stdout=PIPE, stderr=PIPE)
@@ -44,6 +56,8 @@ def dynac(f, out):
         assert not stdout.strip(), [stdout, stderr]
         raise DynaCompilerError(stderr)
 
+    return out
+
 
 def lexer(term):
     return re.findall('"[^"]*"'               # string