d += null.
d += 1.
+
+
+a(X) := f(X,Y).
+
+f(1,1) := 1.
+f(1,2) := 2.
+
+f(2,1) := 1.
+f(2,2) := 2.
import operator
from collections import Counter
-from utils import drepr, _repr
+from utils import drepr, _repr, user_vars
from errors import AggregatorError
"""
class BAggregator(Counter, Aggregator):
# def __init__(self):
# super(BAggregator, self).__init__()
- def inc(self, val, ruleix, variables):
+ def inc(self, val, _ruleix, _variables):
self[val] += 1
- def dec(self, val, ruleix, variables):
+ def dec(self, val, _ruleix, _variables):
self[val] -= 1
def fromkeys(self, *_):
assert False, "This method should never be called."
-class PlusEquals(object):
- __slots__ = 'pos', 'neg'
- def __init__(self):
- self.pos = 0
- self.neg = 0
- def inc(self, val, ruleix, variables):
- self.pos += val
- def dec(self, val, ruleix, variables):
- self.neg += val
- def fold(self):
- return self.pos - self.neg
+#class PlusEquals(object):
+# __slots__ = 'pos', 'neg'
+# def __init__(self):
+# self.pos = 0
+# self.neg = 0
+# def inc(self, val, ruleix, variables):
+# self.pos += val
+# def dec(self, val, ruleix, variables):
+# self.neg += val
+# def fold(self):
+# return self.pos - self.neg
class ColonEquals(BAggregator):
- def inc(self, val, ruleix, variables):
+ def inc(self, val, ruleix, _variables):
self[ruleix, val] += 1
- def dec(self, val, ruleix, variables):
+ def dec(self, val, ruleix, _variables):
self[ruleix, val] -= 1
def fold(self):
- vs = [v for v, cnt in self.iteritems() if cnt > 0]
+ vs = [v for v, m in self.iteritems() if m > 0]
if vs:
- return max(vs)[1]
+ [i, v] = max(vs)
+ vs = {v for (r, v) in vs if r == i} # filter down to max rule index
+ if len(vs) == 1:
+ return v
+ else:
+ vs = list(vs) # for stability
+ vs.sort()
+ raise AggregatorError('`:=` got conflicting values %s for rule index %s' % (vs, i))
class Equals(BAggregator):
self[val] -= 1
def fold(self):
vs = [v for v, cnt in self.iteritems() if cnt > 0]
- if len(vs) != 1:
+ if len(vs) == 0:
+ return
+ if len(vs) == 1:
+ return vs[0]
+ else:
vs.sort() # for stability
raise AggregatorError('`=` got conflicting values %s' % (vs,))
- return vs[0]
-def user_vars(variables):
- "Post process the variables past to emit (which passes them to aggregator)."
- # remove the 'u' prefix on user variables 'uX'
- # Note: We also ignore user variables with an underscore prefix
- return tuple((name[1:], val) for name, val in variables
- if name.startswith('u') and not name.startswith('u_'))
-
from collections import namedtuple
class Result(namedtuple('Result', 'value variables')):
class DictEquals(BAggregator):
- def inc(self, val, ruleix, variables):
- # I think we only want user variables -- XXX: are we guaranteed to have
- # all of the user variables?
+ def inc(self, val, _ruleix, variables):
+ # I think we only want user variables
vs = user_vars(variables)
self[val, vs] += 1
- def dec(self, val, ruleix, variables):
+ def dec(self, val, _ruleix, variables):
vs = user_vars(variables)
self[val, vs] -= 1
class set_equals(BAggregator):
def fold(self):
- from stdlib import todynalist
+ from stdlib import todyna
s = {x for x, m in self.iteritems() if m > 0}
if len(s):
- return todynalist(s)
+ return todyna(s)
class bag_equals(BAggregator):
def fold(self):
- from stdlib import todynalist
- return todynalist(Counter(self).elements())
+ from stdlib import todyna
+ return todyna(list(Counter(self).elements()))
# map names to functions
from repl import REPL
from cStringIO import StringIO
-from utils import red, green, strip_comments
+from utils import red, green, yellow, strip_comments
def extract(code):
- for block in re.compile('^:- ', re.MULTILINE).split(code):
+ for block in re.compile('^> ', re.MULTILINE).split(code):
for cmd, expect in re.findall('(.*?)\n([\w\W]*)$', block):
yield cmd, expect
def run(code):
interp = Interpreter()
repl = REPL(interp)
-
+ errors = []
for cmd, expect in extract(code):
- if not cmd.strip():
+ if not strip_comments(cmd).strip():
print
continue
- print ':-', cmd
+ print yellow % '> %s' % cmd
sys.stdout = x = StringIO()
try:
repl.onecmd(cmd)
if strip_comments(expect) != strip_comments(got):
print green % expect
print red % got
+ errors.append(cmd, expect, got)
else:
print x.getvalue().rstrip()
print
+ if not errors:
+ print green % 'PASS!'
+ else:
+ print red % '%s errors' % len(errors)
+ print
if __name__ == '__main__':
for filename in sys.argv[1:]:
from prioritydict import prioritydict
from config import dotdynadir
from errors import crash_handler, DynaInitializerException, AggregatorError, DynaCompilerError
-
+from stdlib import todyna
class Rule(object):
def __init__(self, idx):
if nullary:
print >> out
for x in others:
+
+ if x.startswith('$rule/'):
+ continue
+
y = str(self.chart[x]) # skip empty chart
if y:
print >> out, y
print >> out
print >> out, 'Errors'
print >> out, '======'
+
+ I = defaultdict(lambda: defaultdict(list))
+ E = defaultdict(lambda: defaultdict(list))
for item, (val, es) in self.error.items():
- print >> out, 'because %r is %s:' % (item, _repr(val))
for e, h in es:
- if h is not None:
- r = h.rule
- print >> out, ' %s\n in rule %s\n %s' % (e, r.span, r.src)
+ if h is None:
+ I[item.fn][type(e)].append((e, item, val))
+ else:
+ E[h.rule][type(e)].append((e, item, val))
+
+ # aggregation errors
+ for r in I:
+ print >> out, 'Error(s) aggregating %s:' % r
+ for etype in I[r]:
+ print >> out, ' %s:' % etype.__name__
+ for i, (e, item, value) in enumerate(sorted(I[r][etype])): # todo: probably don't want to show ten million errors
+ if i >= 5:
+ print >> out, ' %s more ...' % (len(I[r][etype]) - i)
+ break
+ print >> out, ' when `%s` = %s' % (item, _repr(value))
+ print >> out, ' %s' % (e)
+ print >> out
+
+ # errors pertaining to rules
+ for r in E:
+ print >> out, 'Error(s) in rule:', r.span
+ print >> out
+ for line in r.src.split('\n'):
+ print >> out, ' ', line
+ print >> out
+ for etype in E[r]:
+ print >> out, ' %s:' % etype.__name__
+ for i, (e, item, value) in enumerate(sorted(E[r][etype])): # todo: probably don't want to show ten million errors
+ if i >= 5:
+ print >> out, ' %s more ...' % (len(E[r][etype]) - i)
+ break
+ print >> out, ' when `%s` = %s' % (item, _repr(value))
+ print >> out, ' %s' % (e)
+ print >> out
+
+# for item, (val, es) in self.error.items():
+# print >> out, 'because %r is %s:' % (item, _repr(val))
+# for e, h in es:
+# if h is not None:
+# r = h.rule
+# print >> out, ' %s\n in rule %s\n %s' % (e, r.span, r.src)
print >> out
def dump_rules(self):
except KeyError:
print 'Rule %s not found.' % idx
return
+
+ # remove $rule
+ if hasattr(rule, 'item'):
+ self.delete_emit(rule.item, True, ruleix=None, variables=None)
+
# Step 1: remove update handlers
for u in rule.updaters:
for xs in self.updaters.values():
# Step 2: run initializer in delete mode
if rule.init is not None:
rule.init(emit=self.delete_emit)
- # Step 3; go!
- return self.go()
# TODO: probably have to blast any memos from BC computations
+ # Step 3; go!
+ return self.go()
+
+
def go(self):
try:
return self._go()
now = item.aggregator.fold()
except AggregatorError as e:
- error[item] = ('failed to aggregate item `%r` because %s' % (item, e), [(e, None)])
+ error[item] = (None, [(e, None)])
now = self.build('$error/0') # XXX: should go an agenda or run delete?
changed[item] = now
continue
except (ZeroDivisionError, TypeError, KeyboardInterrupt, NotImplementedError) as e:
- error[item] = ('failed to aggregate %r' % item.aggregator, [(e, None)])
+ error[item] = (None, [(e, None)])
now = self.build('$error/0') # XXX: should go an agenda or run delete?
changed[item] = now
"""
assert os.path.exists(filename)
-
-
env = imp.load_source('dynamically_loaded_module', filename)
if path(filename + '.anf').exists(): # XXX: should have codegen provide this in plan.py
for e in emits:
self.emit(*e, delete=False)
+ # ------ $rule for fun and profit -------
+ interp = self
+ def rule(ix, *a):
+ fn = '$rule/%s' % (len(a) + 1)
+ if interp.agg_name[fn] is None:
+ interp.new_fn(fn, ':=')
+ item = interp.build(fn, ix, *a)
+ interp.emit(item, True, ruleix=None, variables=None, delete=False)
+ return item
+ for i in new_rules:
+ r = self.rules[i]
+ agg, head, evals, unifs, result = r.anf[2:]
+ r.item = rule(i, r.src, todyna([head, agg, result, evals, unifs]), r.init, r.query)
+ #-----------------------------------------
+
return self.go()
def dynac(self, filename):
from cStringIO import StringIO
from utils import parse_sexpr
-from stdlib import todynalist
+from stdlib import todyna
class sexpr(object):
def obj(*a):
fn = '%s/%s' % (name, len(a))
if interp.agg_name[fn] is None:
- interp.new_fn(fn, ':=')
+ interp.new_fn(fn, '=')
return interp.build(fn, *a)
- def t(xs):
- if isinstance(xs, basestring):
- return xs
- else:
- return todynalist([t(x) for x in xs])
-
contents = file(filename).read()
for i, x in enumerate(parse_sexpr(contents)):
interp.emit(obj(i),
- t(x),
+ todyna(x),
ruleix=None,
variables=None,
delete=False)
for r in interp.rules.values():
if r.init is not None:
- r.init(emit=_emit)
+ try:
+ r.init(emit=_emit)
+ except (TypeError, ValueError, AssertionError, ZeroDivisionError):
+ pass
else:
assert r.query is not None
return ['%s = %s' % (yellow % head, cyan % _repr(head.value))] \
+ ['|'] \
- + branch(contribs) #\
-# + ['']
+ + branch(contribs)
def branch(xs):
def format(self):
rule = self.rule
#src = rule.src.replace('\n',' ').strip()
- graph = self.graph
#user_vars = dict(defn.user_vars(self.vs.items()))
+ graph = self.graph
side = [self.get_function(x) for x in graph.outputs if x != rule.anf.result and x != rule.anf.head]
explode = ('%s %s %s' % (self.get_function(rule.anf.head)[1:], # drop quote on head
return lines
-
def get_function(self, x):
"""
String of symbolic representation of ``x``, a variable or function, in
print 'Changes'
print '======='
for x, v in sorted(changed.items()):
+ if x.fn.startswith('$rule/'):
+ continue
print '%s = %s.' % (x, _repr(v))
print
x = eval(name)(*args)
return todyna(x)
-def todyna(x):
- if isinstance(x, (list, tuple, set, Counter)):
- return todynalist(x)
- return x
-
def topython(x):
if isinstance(x, Cons) or x is Nil:
return x.aslist
return x
-def todynalist(x):
+
+def todynalist(x): # TODO: get rid of this.
+ return todyna(x)
+
+def todyna(x):
if isinstance(x, (set, Counter)):
x = list(x)
x.sort()
- return todynalist(x)
- return _todynalist(list(x))
+ return todyna(x)
+ elif isinstance(x, (list, tuple)):
+ c = Nil
+ for y in reversed(x):
+ c = Cons(todyna(y), c)
+ return c
+ else:
+ return x
-def _todynalist(x):
-# if not x:
-# return Nil
-# return Cons(x[0], _todynalist(x[1:]))
- c = Nil
- for y in reversed(x):
- c = Cons(y, c)
- return c
def get(x, i):
return x[i]
return '{%s}' % ', '.join('%s=%s' % (k, _repr(v)) for k,v in vs.iteritems())
+def user_vars(variables):
+ "Post process the variables past to emit (which passes them to aggregator)."
+ # remove the 'u' prefix on user variables 'uX'
+ # Note: We also ignore user variables with an underscore prefix
+ return tuple((name[1:], val) for name, val in variables
+ if name.startswith('u') and not name.startswith('u_'))
+
+
# interactive IPython shell
ip = InteractiveShellEmbed(banner1 = 'Dropping into IPython\n')
-:- a += 1.
-=============
-a := 1
+> a += 1.
-:- b += 1.
-=============
-b := 1
+Changes
+=======
+a = 1.
-:- a += 1.
-=============
-a := 2
+> b += 1.
-:- rules
+Changes
+=======
+b = 1.
-0: a += 1.
+> a += 1.
+
+Changes
+=======
+a = 2.
+
+> rules
+
+Rules
+=====
+ 0: a += 1.
1: b += 1.
2: a += 1.
-:- sol
+> sol
Solution
========
-a := 2
-b := 1
+a = 2.
+b = 1.
+
+> retract_rule 0
+
+Changes
+=======
+a = 1.
+
+> retract_rule 1
+
+Changes
+=======
+b = null.
-:- retract_rule 0
-:- retract_rule 1
-:- sol
+> sol
Solution
========
-a := 1
+a = 1.