From: Tim Vieira Date: Sun, 21 Jul 2013 22:36:05 +0000 (-0400) Subject: recompile rules on late backchain declaration (Issue #31); requires running X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=63f3b9d52bb8352b3d1a8017068dd0042e820fce;p=dyna2 recompile rules on late backchain declaration (Issue #31); requires running compiler to fixed point; maintaining a set of failed rules. small tweak (hacky) to make compiler error messages friendlier -- show rule source instead of `` (which was the mask for a big sha1+linenumbers). --- diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index 83e2707..374df71 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -86,6 +86,10 @@ class Interpreter(object): # (an over estimate -- high recall, low precision). self.coarse_deps = defaultdict(set) self.rule_by_head = defaultdict(list) + self.rule_dep = defaultdict(set) # rules which depend on a predicate + + # rules which need to be recompiled against new program state. + self.recompile = set() def new_fn(self, fn, agg): if self.agg_name[fn] is None: @@ -291,18 +295,22 @@ class Interpreter(object): env = imp.load_source('dynamically_loaded_module', filename) - anf = {} - assert path(filename + '.anf').exists() # XXX: codegen should put this is plan.py - with file(filename + '.anf') as f: - for x in read_anf(f.read()): - anf[x.ruleix] = x - for k,v in [('chart', self.chart), ('build', self.build), ('gbc', self.gbc), ('peel', peel)]: setattr(env, k, v) + anf = {} + assert path(filename + '.anf').exists() # XXX: codegen should put this is plan.py + with file(filename + '.anf') as f: + contents = f.read().strip() + '\n' + for x in read_anf(contents): + anf[x.ruleix] = x + + # accept the new parser state + self.parser_state = env.parser_state + for k, v in env.agg_decl.items(): self.new_fn(k, v) @@ -312,19 +320,37 @@ class Interpreter(object): self.add_rule(index, query=h, head_fn=fn, anf=anf[index]) for index, h in env.initializers: + assert index not in self.rules new_rules.add(index) self.add_rule(index, init=h, anf=anf[index]) for fn, r, h in env.updaters: self.new_updater(fn, r, h) - # accept the new parser state - self.parser_state = env.parser_state - + # run to fixed point. + if self.recompile: + try: + plan = self.dynac_code('\n'.join(r.src for r in self.recompile)) + except DynaCompilerError as e: + # TODO: it is a little bit strange to ignore the error and + # simply print something. However, since the rules in the + # recompile list are syntactically valid (well, they at least + # were valid) -- this means that errors must be planning + # errors... probably all to do with missing BC declarations. + print e + else: + # TODO: reuse old rule index when we recompile. + for r in self.recompile: + self.retract_rule(r.index) + self.recompile.clear() + self.load_plan(plan) + + # we we don't accumulate all changed rules, new_rules return will be new + # rules of top-level call. return new_rules def run_uninitialized(self): - q = list(self.uninitialized_rules) + q = set(self.uninitialized_rules) failed = [] while q: rule = q.pop() @@ -396,10 +422,25 @@ class Interpreter(object): self.uninitialized_rules.append(rule) elif query: - self._gbc[head_fn].append(query) rule.query = query query.rule = rule + if head_fn not in self._gbc: + + # quick monkey patch assertion. + def monkey(_, _args): + assert False, '__getitem__ should never be called because' \ + ' `%s` should be backchained' % head_fn + self.chart.__getitem__ = monkey + + # retract and replan rules dependent on this predicate which is + # now backchained. + for d in self.rule_dep[head_fn]: + if rule != d and d.head_fn not in self._gbc: + self.recompile.add(d) + + self._gbc[head_fn].append(query) + else: assert False, "Can't add rule with out an initializer or query handler." @@ -421,10 +462,12 @@ class Interpreter(object): [(label, _evalix)] = re.findall('^(.*)@(\d+)$', label) # remove evaluation index b = '%s/%d' % (label, len(ys)) self.coarse_deps[b].add(rule.head_fn) + self.rule_dep[b].add(rule) def recompute_coarse(self): self.rule_by_head.clear() self.coarse_deps.clear() + self.rule_dep.clear() for rule in self.rules.values(): self.update_coarse(rule) diff --git a/src/Dyna/Backend/Python/repl.py b/src/Dyna/Backend/Python/repl.py index 217fe1f..e2bf78c 100644 --- a/src/Dyna/Backend/Python/repl.py +++ b/src/Dyna/Backend/Python/repl.py @@ -150,7 +150,7 @@ class REPL(cmd.Cmd, object): def do_dynac(self, line): try: - src = self.interp.dynac_code(line) # might raise DynaCompilerError + src = self.interp.dynac_code(line) except DynaCompilerError as e: src = e.filename print e diff --git a/src/Dyna/Backend/Python/utils.py b/src/Dyna/Backend/Python/utils.py index 87824dc..d90d354 100644 --- a/src/Dyna/Backend/Python/utils.py +++ b/src/Dyna/Backend/Python/utils.py @@ -111,7 +111,7 @@ def dynac(f, out, anf=None, compiler_args=()): stdout, stderr = p.communicate() if p.returncode: assert not stdout.strip(), [stdout, stderr] - stderr = hide_ugly_filename(stderr) + stderr = hide_ugly_filename(stderr, lambda m: '\n %s\n' % rule_source(m.group(0))) raise DynaCompilerError(stderr, f) diff --git a/test/repl/aggregator-conflict.dynadoc b/test/repl/aggregator-conflict.dynadoc index 9fd31c5..2970372 100644 --- a/test/repl/aggregator-conflict.dynadoc +++ b/test/repl/aggregator-conflict.dynadoc @@ -8,7 +8,8 @@ a = 1. > a. DynaCompilerError: Encountered error in input program: - Conflicting aggregators; rule + Conflicting aggregators; rule + a. uses ':-' for a/0 but I had been lead to expect '+='. Everything was syntactically valid, but we could not see it through.