From b7f405ccb891909247ecd38c9df61bb103a0c495 Mon Sep 17 00:00:00 2001 From: Tim Vieira Date: Sat, 27 Jul 2013 14:16:13 -0400 Subject: [PATCH] Incorporate feedback from LI student crashlogs. - 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 | 44 ++++++++++++++++++------ src/Dyna/Backend/Python/load/__init__.py | 6 +++- src/Dyna/Backend/Python/load/matrix.py | 5 +++ src/Dyna/Backend/Python/load/pickled.py | 6 +++- src/Dyna/Backend/Python/load/sexpr.py | 6 +++- src/Dyna/Backend/Python/load/tsv.py | 5 +++ src/Dyna/Backend/Python/post/trace.py | 18 +++++++++- test/repl/retract-bc.dynadoc | 2 +- 8 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index cdff442..6ea4adc 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -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) diff --git a/src/Dyna/Backend/Python/load/__init__.py b/src/Dyna/Backend/Python/load/__init__.py index a8ab571..a84e1a2 100644 --- a/src/Dyna/Backend/Python/load/__init__.py +++ b/src/Dyna/Backend/Python/load/__init__.py @@ -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() diff --git a/src/Dyna/Backend/Python/load/matrix.py b/src/Dyna/Backend/Python/load/matrix.py index e1c9dea..4162804 100644 --- a/src/Dyna/Backend/Python/load/matrix.py +++ b/src/Dyna/Backend/Python/load/matrix.py @@ -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 diff --git a/src/Dyna/Backend/Python/load/pickled.py b/src/Dyna/Backend/Python/load/pickled.py index fa592c4..7104922 100644 --- a/src/Dyna/Backend/Python/load/pickled.py +++ b/src/Dyna/Backend/Python/load/pickled.py @@ -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 diff --git a/src/Dyna/Backend/Python/load/sexpr.py b/src/Dyna/Backend/Python/load/sexpr.py index c02222b..5379db2 100644 --- a/src/Dyna/Backend/Python/load/sexpr.py +++ b/src/Dyna/Backend/Python/load/sexpr.py @@ -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 diff --git a/src/Dyna/Backend/Python/load/tsv.py b/src/Dyna/Backend/Python/load/tsv.py index fd68ad3..13b59ec 100644 --- a/src/Dyna/Backend/Python/load/tsv.py +++ b/src/Dyna/Backend/Python/load/tsv.py @@ -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 diff --git a/src/Dyna/Backend/Python/post/trace.py b/src/Dyna/Backend/Python/post/trace.py index 2fea1f4..bfa1a49 100644 --- a/src/Dyna/Backend/Python/post/trace.py +++ b/src/Dyna/Backend/Python/post/trace.py @@ -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: diff --git a/test/repl/retract-bc.dynadoc b/test/repl/retract-bc.dynadoc index 91e24c0..0f5ec63 100644 --- a/test/repl/retract-bc.dynadoc +++ b/test/repl/retract-bc.dynadoc @@ -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 -- 2.50.1