]> hydra-www.ietfng.org Git - dyna2/commitdiff
Incorporate feedback from LI student crashlogs.
authorTim Vieira <tim.f.vieira@gmail.com>
Sat, 27 Jul 2013 18:16:13 +0000 (14:16 -0400)
committerTim Vieira <tim.f.vieira@gmail.com>
Sat, 27 Jul 2013 18:16:13 +0000 (14:16 -0400)
 - loaders test for whether file exists

 - load cmd catches syntax errors

 - fixed clearing push-time errors when retracting rule

Documented (and add test cases) for few newly discovered bugs in BC.

Recompile relevant rules after receiving `:- backchain` pragma.

src/Dyna/Backend/Python/interpreter.py
src/Dyna/Backend/Python/load/__init__.py
src/Dyna/Backend/Python/load/matrix.py
src/Dyna/Backend/Python/load/pickled.py
src/Dyna/Backend/Python/load/sexpr.py
src/Dyna/Backend/Python/load/tsv.py
src/Dyna/Backend/Python/post/trace.py
test/repl/retract-bc.dynadoc

index cdff442d30f069422511b71e5ba67b8c85b072e8..6ea4adc95453629e443f37caa56ec436567da18c 100644 (file)
@@ -74,6 +74,13 @@ class Interpreter(object):
         lines.extend(':-%s %s.' % (k,v) for k,v in other)
         return '\n'.join(lines)
 
+    def set_parser_state(self, x):
+        self.pstate = (bc, _rix, _agg, _other) = x
+        for fn in bc:
+            if fn not in self._gbc:    # new backchain declaration
+                for r in self.rule_by_head[fn]:
+                    self.recompile.add(r)
+
     def __init__(self):
         # declarations
         self.agg_name = defaultdict(none)
@@ -240,7 +247,7 @@ class Interpreter(object):
         t_emit = lambda item, val, ruleix, variables: \
            emittiers.append((item, val, ruleix, variables, delete))
 
-        error = []
+        errors = []
 
         for handler in self.updaters[item.fn]:
 
@@ -253,10 +260,10 @@ class Interpreter(object):
             except (ZeroDivisionError, ValueError, TypeError, RuntimeError, OverflowError) as e:
                 e.exception_frame = rule_error_context()
                 e.traceback = traceback.format_exc()
-                error.append((e, handler))
+                errors.append((e, handler))
 
-        if error:
-            self.set_error(item, (val, error))
+        if errors:
+            self.set_error(item, (val, errors))
             return
 
         # no exceptions, accept emissions.
@@ -330,7 +337,7 @@ class Interpreter(object):
                 anf[x.ruleix] = x
 
         # update parser state
-        self.pstate = parse_parser_state(env.parser_state)
+        self.set_parser_state(parse_parser_state(env.parser_state))
 
         for k, v in env.agg_decl.items():
             self.new_fn(k, v)
@@ -373,6 +380,13 @@ class Interpreter(object):
                 self.recompile.clear()
                 self.load_plan(plan)
 
+                # TODO: should probably indicate that some rules were recompiled
+                # and no longer in an error state. -- there is a bit of a
+                # mismatch with when we choose not to add a rule... in the try
+                # block above we reject rules on compiler error, but if the
+                # rules existed before and it now longer compiles we just print
+                # the error and add it to the recompile list.
+
         # we we don't accumulate all changed rules, new_rules return will be new
         # rules of top-level call.
         return new_rules
@@ -411,6 +425,8 @@ class Interpreter(object):
             del self.error[x]
 
     def set_error(self, x, e):
+        if not e:
+            self.clear_error(x)
         self.error[x] = e
 
     #___________________________________________________________________________
@@ -453,6 +469,7 @@ class Interpreter(object):
             rule.query = query
             query.rule = rule
 
+            # fix dependents
             if head_fn not in self._gbc:
 
                 # quick monkey patch assertion.
@@ -469,6 +486,12 @@ class Interpreter(object):
 
             self._gbc[head_fn].append(query)
 
+            if head_fn in self.chart:
+                # if we've added a new rule we need to recompute memos for
+                # existing memos. (TODO: can do slightly better than recompute
+                # -- only need to evaluate contributions from this new rule)
+                self.recompute_gbc_memo(head_fn)
+
         else:
             assert False, "Can't add rule with out an initializer or query handler."
 
@@ -541,14 +564,12 @@ class Interpreter(object):
             # recompute memos and dependent memos
             self.recompute_gbc_memo(rule.head_fn)
 
-
         # clear push-time errors pertaining to this rule
         for item, x in self.error.items():
             if isinstance(item, Rule):
                 continue
-            (_, es) = x
-            for e, h in es:
-                self.error[item] = [(e, h) for e, h in self.error[item] if h is not None and h.rule.index == rule.index]
+            (v, es) = x
+            self.error[item] = (v, [(e, h) for e, h in es if h is None or h.rule.index == rule.index])
 
         self.recompute_coarse()
 
@@ -570,7 +591,7 @@ class Interpreter(object):
             visited = set()
 
         if fn not in self._gbc or fn in visited:
-            # don't refresh non BC computation be careful not to get stuck in an
+            # don't refresh non-BC computation be careful not to get stuck in an
             # infinite loop if there is a cycle in the dep graph
             return
 
@@ -604,6 +625,9 @@ class Interpreter(object):
         """
         filename = path(filename)
         self.files.append(filename)
+
+        # TODO: crashlogs/amareshj:2013-07-01:22:26:54:13412.log -- file doesn't exist.
+
         out = self.tmp / filename.read_hexhash('sha1') + '.plan.py'
         #out = filename + '.plan.py'
         self.files.append(out)
index a8ab57151830666a5d2805bbe7f26c280ea7990e..a84e1a2d39da6d94ac9c0eb5b4c61c5d27cede8f 100644 (file)
@@ -17,5 +17,9 @@ def run(interp, line):
         return
 
     m = get_module('load', module)(interp, name)
-    exec 'm.main(%s)' % args
+    try:
+        exec 'm.main(%s)' % args
+    except SyntaxError as e:
+        print 'Syntax error: %s' % e
+        return
     return interp.run_agenda()
index e1c9dea141e447ff7203ca82aa6aa25b281270d7..4162804d24da14b4dd1593a8eba7bf513bdf1a99 100644 (file)
@@ -1,4 +1,5 @@
 import re
+from path import path
 
 class matrix(object):
     """
@@ -55,6 +56,10 @@ class matrix(object):
     # TODO: option for strict width
     # TODO: option for stripping comments
     def main(self, filename, astype=float, delim='\s+'):
+        filename = path(filename)
+        if not filename.exists():
+            print 'file `%s` does not exist.' % filename
+            return
 
         interp = self.interp
 
index fa592c455a21d781053a95af309109e3c328b2ad..7104922966c63e8cc93397dafcb78128e4fdf634 100644 (file)
@@ -7,7 +7,7 @@ TODO: Can we merge a pickled interpreter into an existing one?
 """
 
 import cPickle
-
+from path import path
 
 class pickled(object):
 
@@ -16,6 +16,10 @@ class pickled(object):
         self.name = name
 
     def main(self, filename):
+        filename = path(filename)
+        if not filename.exists():
+            print 'file `%s` does not exist.' % filename
+            return
         with file(filename, 'r') as f:
             interp = cPickle.load(f)
         return interp
index c02222b3957ee8329593df4eb224255342f19e72..5379db2e4dae31b65492450010f0bc1ee14c0ff1 100644 (file)
@@ -1,7 +1,7 @@
 from cStringIO import StringIO
 from utils import parse_sexpr
 from stdlib import todyna
-
+from path import path
 
 class sexpr(object):
     """
@@ -26,6 +26,10 @@ class sexpr(object):
         self.name = name
 
     def main(self, filename):
+        filename = path(filename)
+        if not filename.exists():
+            print 'file `%s` does not exist.' % filename
+            return
 
         interp = self.interp
         name = self.name
index fd68ad3104008d89e49c3da180e572d34eea3f68..13b59ecffc98bab4a068975ba3380b1c010a3eaa 100644 (file)
@@ -5,6 +5,7 @@ TODO: option for strict number of columns.
 
 import re
 from utils import true
+from path import path
 
 class tsv(object):
     """
@@ -30,6 +31,10 @@ class tsv(object):
         self.name = name
 
     def main(self, filename, delim='\t'):
+        filename = path(filename)
+        if not filename.exists():
+            print 'file `%s` does not exist.' % filename
+            return
 
         interp = self.interp
         name = self.name
index 2fea1f4f4ab0dd247dbcf854aaa5ce15ddcb5e4c..bfa1a49cdd3afad7a04298ddc8c485d26c545f1b 100644 (file)
@@ -219,9 +219,25 @@ class Crux(object):
                 _e = e
                 a = []
                 while e.label == '& cons':
-                    x, xs = e.body
+
+                    # TODO: crashlogs/abarany:2013-07-24:11:47:31:17823.log
+                    x, xs = e.body           # e = _a3 = & cons(uV, uX)
+
+                    # g.incoming = {
+                    #  '_a5': [_a5 = edge@1(uX, uU, uV)],
+                    #  '_a4': [_a4 = pathto@0(uU)],
+                    #  '_a3': [_a3 = & cons(uV, uX)],
+                    #  '_a2': [_a2 = +@2(_a4, _a5)],
+                    #  'uU': [],
+                    #  '_t1': [_t1 = & with_key(_a2, _a3)],
+                    #  'uV': [],
+                    #  '_t0': [_t0 = & pathto(uV)], 'uX': []
+                    # }
+                    # xs = 'uX'
+
                     [e] = g.incoming[xs]
                     a.append(self.get_function(x))
+
                 if e.label == '& nil':
                     return '[%s]' % ', '.join(a)
                 else:
index 91e24c01cc373424ce1221a1c7506bf18b4e4e6e..0f5ec6304e0f73e0be9d0436cbdfc5be49e15bd6 100644 (file)
@@ -1,5 +1,4 @@
 > :- backchain f/1.
-| :- backchain g/1.
 | f(0) := 0.
 | f(1) := 1.
 | f(X) := f(X-1) + f(X-2) for X > 1.
@@ -55,6 +54,7 @@ a(1) = 1.
 > :- backchain f/1.
 | f(X) := f(X-1) * X for X > 1.
 | f(0) := 1.
+| f(0) := 5.
 | b(X) = f(X) for X in range(6).
 
 % Check that `a(x)` and `s`, know that we have a new definition! for `f`, just