From: Tim Vieira Date: Thu, 27 Jun 2013 20:45:14 +0000 (-0400) Subject: Experiment version of `trace` now available. X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=1f4b2a21c7b6bca249da88d666340fa800cd68ee;p=dyna2 Experiment version of `trace` now available. $ ./dyna :- a += 1. :- a += a/2. :- post trace() --- diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index 5eb2aec..1a0fb53 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -4,6 +4,8 @@ TODO ==== + - TODO: @nwf remove comments from rule source + - More info in crash handler. (stack trace, repl transcript, cmd-line args, version control info, and dyna source is enough) @@ -139,10 +141,10 @@ USERS NOTES ===== - - TODO: `None` does not propagate, eventually it will because of the `?` prefix + - `None` does not propagate, eventually it will because of the `?` prefix operator. - - TODO: Term values should only be aggregated with ``=`` or ``:=`` maybe even + - Term values should only be aggregated with ``=`` or ``:=`` maybe even ``set=``. We should disallow ``a += &b.`` Equals aggregation only one value allowed, mult. >0 on single value. The @@ -154,7 +156,7 @@ NOTES This might not be the case during computation -- this is the same as the error problem. - - TODO: Numeric precision is an issue with BAggregators. + - Numeric precision is an issue with BAggregators. timv: Are we sure we have this bug? diff --git a/src/Dyna/Backend/Python/post/__init__.py b/src/Dyna/Backend/Python/post/__init__.py index 68121c7..77c9085 100644 --- a/src/Dyna/Backend/Python/post/__init__.py +++ b/src/Dyna/Backend/Python/post/__init__.py @@ -3,7 +3,7 @@ from save import save from graph import graph from draw_circuit import draw_circuit from dump_solution import dump_solution - +from trace import trace def run(interp, line): [(name, args)] = _re.findall('([a-z][a-zA-Z_0-9]*)\((.*)\)$', line.strip()) diff --git a/src/Dyna/Backend/Python/post/draw_circuit.py b/src/Dyna/Backend/Python/post/draw_circuit.py index d249fc3..e697a3e 100644 --- a/src/Dyna/Backend/Python/post/draw_circuit.py +++ b/src/Dyna/Backend/Python/post/draw_circuit.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +""" +TODO: nwf remove comments from rule source +""" import webbrowser from debug import Hypergraph @@ -77,166 +80,4 @@ class draw_circuit(object): print >> f, '' -# webbrowser.open(f.name) - - # group edges by head then ruleindex - global groups - groups = groupby(lambda x: x[0], es) - for a in groups: - groups[a] = groupby(lambda x: x[1], groups[a]) - - for head in groups: - dig(head, set()) - - -from collections import defaultdict -def groupby(key, data): - g = defaultdict(list) - for x in data: - g[key(x)].append(x) - return dict(g) - - -groups = None - - - -def dig(head, visited, indent='', first=False, last=False): - - if last and first: - xxx = '└─ ' - elif last: - xxx = '└─ ' - elif first: - xxx = '├─ ' - else: - xxx = '├─ ' - - if head in visited: - print indent[:-4] + xxx + red % '*CYCLE*' - return - - if head not in groups: - return - visited.add(head) - - print indent[:-4] + xxx + '%s = %s' % (yellow % head, _repr(head.value)) - - if last: - indent = indent[:-4] + ' ' - else: - indent = indent + ' ' - - for ruleix in groups[head]: - - contrib = groups[head][ruleix] - for (i, (_, _, body, vs)) in enumerate(contrib): - - rule = interp.rules[ruleix] - - # TODO: nwf remove comments from rule source - crux = Crux(head, rule, body, dict(vs)) - - for line in crux.format(): - print indent + line - - print indent - - for i, x in enumerate(body): - dig(x, visited, indent=indent + ' │ ', first=i==0, last=i==len(body)-1) - - print indent[:-2] - - -""" -def branch(*xs): - - for i, x in enumerate(xs): - first = i == 0 - last = i == len(contrib)-1 - if last and first: - xxx = '└─ ' - elif last: - xxx = '└─ ' - elif first: - xxx = '├─ ' - else: - xxx = '├─ ' -""" - - - -class Crux(object): - - def __init__(self, head, rule, body, vs): - self.head = head - self.rule = rule - self.body = body - self.vs = vs - self.graph = debug.circuit(rule.anf) - - def values(self, x): - if x in self.vs: - return _repr(self.vs[x]) - try: - return _repr(eval(x.replace('\\"', '"'))) - except (SyntaxError, NameError): - return x - - def format(self): - rule = self.rule - src = rule.src.replace('\n',' ').strip() - graph = self.graph - user_vars = dict(defn.user_vars(self.vs.items())) - side = [' side: ' + self.get_function(x) for x in graph.outputs if x != rule.anf.result and x != rule.anf.head] - return [('%s %s' % (red % rule.anf.agg, self.values(rule.anf.result))), - (green % (' # %s' % src)), - (green % (' # %s' % subst(src, user_vars))), - (green % (' # %s' % drepr(user_vars))), - (' head: %s' % self.get_function(rule.anf.head)), - (' result: %s' % self.get_function(rule.anf.result))] \ - + side - - def get_function(self, x): - """ - String of symbolic representation of ``x``, a variable or function, in - this expresion graph. - """ - g = self.graph - if isinstance(x, debug.Edge): - label = re.sub('@\d+$', '', x.label) - label = re.sub('^& ', '&', label) - - if label == '=': - [b] = x.body - return self.get_function(b) - - if not x.body: # arity 0 - return label - - fn_args = [self.get_function(y) for y in x.body] - if not label.isalpha() and not label.startswith('& ') and len(fn_args) == 2: # infix - [a,b] = fn_args - return '(%s %s %s)' % (a, label, b) - return '%s(%s)' % (label, ', '.join(fn_args)) - else: - 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]) - [e] = g.incoming[x] - - if e.label == '=': - return self.get_function(e) - - if e.label.startswith('&'): - return self.get_function(e) - - return self.get_function(e) + (red % '=%s' % self.values(x)) - - -import re -from utils import yellow, green, red -from defn import drepr -from term import _repr -import debug, defn + webbrowser.open(f.name) diff --git a/src/Dyna/Backend/Python/post/trace.py b/src/Dyna/Backend/Python/post/trace.py new file mode 100644 index 0000000..0fe076b --- /dev/null +++ b/src/Dyna/Backend/Python/post/trace.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +""" +Examine solution as an outline of computation. +""" + +import re +from utils import yellow, green, red +from defn import drepr +from term import _repr +import debug, defn + +import webbrowser +from debug import Hypergraph +from cStringIO import StringIO +from utils import lexer, subst + +from draw_circuit import infer_edges +from collections import defaultdict + + +class trace(object): + """ + Crude visualization of circuit pertaining to state of the interpreter. + """ + + def __init__(self, interp): + self.interp = interp + + def main(self): + es = infer_edges(self.interp) + + # group edges by head then ruleindex + groups = groupby(lambda x: x[0], es) + for a in groups: + groups[a] = groupby(lambda x: x[1], groups[a]) + + for head in groups: + print '\n'.join(dig(head, set(), groups, self.interp)) + + +def groupby(key, data): + g = defaultdict(list) + for x in data: + g[key(x)].append(x) + return dict(g) + + + +def dig(head, visited, groups, interp): + + if head in visited: + return [red % '*CYCLE*'] + + if head not in groups: + return [] + + visited.add(head) + + contribs = [] + + for ruleix in groups[head]: + for (_, _, body, vs) in groups[head][ruleix]: + + crux = Crux(head, interp.rules[ruleix], body, dict(vs)) + block = branch([dig(x, visited, groups, interp) for x in body]) + + if block: + contribs.append(crux.format() + [''] + block) + else: + contribs.append(crux.format()) + + return ['%s = %s' % (yellow % head, _repr(head.value))] \ + + ['|'] \ + + branch(contribs) + [''] + + +def branch(xs): + + ys = [] + for i, x in enumerate(xs): + first = i == 0 + last = i == len(xs)-1 + if last and first: + h = '└─ ' + elif last: + h = '└─ ' + elif first: + 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 + + +class Crux(object): + + def __init__(self, head, rule, body, vs): + self.head = head + self.rule = rule + self.body = body + self.vs = vs + self.graph = debug.circuit(self.rule.anf) + + def values(self, x): + if x in self.vs: + return _repr(self.vs[x]) + try: + return _repr(eval(x.replace('\\"', '"'))) + except (SyntaxError, NameError): + return x + + def format(self): + rule = self.rule + src = rule.src.replace('\n',' ').strip() + graph = self.graph + user_vars = dict(defn.user_vars(self.vs.items())) + side = ['side: ' + self.get_function(x) for x in graph.outputs if x != rule.anf.result and x != rule.anf.head] + return [('%s %s' % (red % rule.anf.agg, self.values(rule.anf.result))), + (green % ('# %s' % src)), + (green % ('# %s' % subst(src, user_vars))), + (green % ('# %s' % drepr(user_vars))), + ('head: %s' % self.get_function(rule.anf.head)), + ('result: %s' % self.get_function(rule.anf.result))] \ + + side + + def get_function(self, x): + """ + String of symbolic representation of ``x``, a variable or function, in + this expresion graph. + """ + g = self.graph + if isinstance(x, debug.Edge): + label = re.sub('@\d+$', '', x.label) + label = re.sub('^& ', '&', label) + + if label == '=': + [b] = x.body + return self.get_function(b) + + if not x.body: # arity 0 + return label + + fn_args = [self.get_function(y) for y in x.body] + if not label.isalpha() and not label.startswith('& ') and len(fn_args) == 2: # infix + [a,b] = fn_args + return '(%s %s %s)' % (a, label, b) + return '%s(%s)' % (label, ', '.join(fn_args)) + + else: + 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]) + [e] = g.incoming[x] + + if e.label == '=': + return self.get_function(e) + + if e.label.startswith('&'): + return self.get_function(e) + + return self.get_function(e) + (red % '=%s' % self.values(x))