From 4b8f45909de842e529bfea457c3c835cf34d8618 Mon Sep 17 00:00:00 2001 From: Tim Vieira Date: Sun, 30 Jun 2013 00:49:45 -0400 Subject: [PATCH] trace available at repl supports viewing derivations each item matching query MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit :- a :- b. :- b:- c. :- c. ============= a := true b := true c := true :- trace a a = true | └─ :- true # a :- b. # a :- b. where {} head: &a result: b=true | └─ b = true | └─ :- true # b:- c. # b:- c. where {} head: &b result: c=true | └─ c = true | └─ |= true # c. # c. where {} head: &c result: &true --- src/Dyna/Backend/Python/interpreter.py | 2 +- src/Dyna/Backend/Python/post/trace.py | 32 ++++++++++++++--------- src/Dyna/Backend/Python/repl.py | 31 ++++++++++++++++++++++ test/repl/equals-errors.doctest | 36 ++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 test/repl/equals-errors.doctest diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index ee04af9..dace1f1 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -260,7 +260,7 @@ class Interpreter(object): assert c.agg_name is None c.agg_name = agg for item in c.intern.itervalues(): - assert c.aggregator is None + assert item.aggregator is None item.aggregator = c.new_aggregator() assert self.agg_name[fn] == agg, (fn, self.agg_name[fn], agg) diff --git a/src/Dyna/Backend/Python/post/trace.py b/src/Dyna/Backend/Python/post/trace.py index fe7a590..2bcdcfc 100644 --- a/src/Dyna/Backend/Python/post/trace.py +++ b/src/Dyna/Backend/Python/post/trace.py @@ -2,7 +2,8 @@ """ Examine solution as an outline of computation. -TODO: backchained stuff +TODO: shared substructure. + """ import re @@ -24,15 +25,28 @@ class trace(object): self.interp = interp def main(self): - es = infer_edges(self.interp) + tracer = Tracer(self.interp) + for item in tracer.items: + print + print tracer(item) + + +class Tracer(object): + def __init__(self, interp): + self.interp = interp + self.edges = infer_edges(self.interp) # group edges by head then ruleindex - groups = groupby(lambda x: x[0], es) + groups = groupby(lambda x: x[0], self.edges) for a in groups: groups[a] = groupby(lambda x: x[1], groups[a]) + self.items = groups - for head in groups: - print '\n'.join(dig(head, set(), groups, self.interp)) + def __call__(self, item): + if item not in self.items: + print + print 'no trace for', item + print '\n'.join(dig(item, set(), self.items, self.interp)) def groupby(key, data): @@ -42,7 +56,6 @@ def groupby(key, data): return dict(g) - def dig(head, visited, groups, interp): if head in visited: @@ -72,7 +85,6 @@ def dig(head, visited, groups, interp): def branch(xs): - ys = [] for i, x in enumerate(xs): first = i == 0 @@ -85,16 +97,12 @@ def branch(xs): h = '├─ ' else: h = '├─ ' - if not x: continue - ys.append(h + x[0]) - indent = '│ ' if not last else ' ' for a in x[1:]: ys.append(indent + a) - return ys @@ -155,7 +163,7 @@ class Crux(object): if not g.incoming[x]: # input variable return self.values(x) if len(g.incoming[x]) > 1: - return 'UNIF ' + ' === '.join(self.get_function(e) + (red % '=%s' % self.values(e.head)) for e in g.incoming[x]) + return 'UNIFICATION ' + ' === '.join(self.get_function(e) + (red % '=%s' % self.values(e.head)) for e in g.incoming[x]) [e] = g.incoming[x] if e.label == '=': diff --git a/src/Dyna/Backend/Python/repl.py b/src/Dyna/Backend/Python/repl.py index 0cd8c62..0083b62 100644 --- a/src/Dyna/Backend/Python/repl.py +++ b/src/Dyna/Backend/Python/repl.py @@ -380,6 +380,37 @@ class REPL(cmd.Cmd, object): show_traceback() readline.write_history_file(self.hist) + def do_trace(self, q): + + if q.endswith('.'): + print "Queries don't end with a dot." + return + + self.interp.new_rules = set() + + try: + query = "$trace dict= _ is %s, &(%s)." % (q,q) + self.default(query, show_changed=False) + try: + [(_, _, results)] = self.interp.chart['$trace/0'][:,] + except ValueError: + print 'no items matching `%s`.' % q + return + + from post.trace import Tracer + tracer = Tracer(self.interp) + for item, _ in results: + print + tracer(item) + + 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['$trace/0'] + do_load.__doc__ = do_load.__doc__.format(load=', '.join(load.available)) do_post.__doc__ = do_post.__doc__.format(post=', '.join(post.available)) diff --git a/test/repl/equals-errors.doctest b/test/repl/equals-errors.doctest new file mode 100644 index 0000000..adaa7e8 --- /dev/null +++ b/test/repl/equals-errors.doctest @@ -0,0 +1,36 @@ + +:- a = 2. +============= +a := 2 + + +% It's ok to assign 2 again. + +:- a = 2. + + +% but you can't set it to a different value, such as 1. + +:- a = 1. +============= +a := $error + +:- sol + +Solution +======== +a => $error. + + +Errors +====== +because a is "failed to aggregate item `a` because `=` got conflicting values [1, 2]": + + +:- retract_rule 2 + +:- sol + +Solution +======== +a => 2. -- 2.50.1