, "+=" , "*="
, "and=" , "or=" , "&=" , "|="
, ":-"
+ , "="
, "majority=" , "set=" , "bag="
, ":="
, "dict="
go ("split", _) = Just $ PDBS $ call "split" []
go ("float", _) = Just $ PDBS $ call "float" []
go ("int", _) = Just $ PDBS $ call "int" []
- go ("getattr", _) = Just $ PDBS $ call "getattr" []
go ("pycall", _) = Just $ PDBS $ call "pycall" []
go ("<=",2) = Just $ PDBS $ infixOp "<="
return aggregator(self.agg_name)
def __repr__(self):
-
rows = [term for term in self.intern.values() if term.value is not None]
-
if not rows:
return ''
-
if self.arity == 0:
+ [term] = rows
return '%s := %s' % (term, _repr(term.value))
-
- x = '\n'.join('%-30s := %s' % (term, _repr(term.value)) for term in sorted(rows))
- return '%s\n%s\n%s\n' % (self.name, '='*len(self.name), x)
+ p = [(_repr(term), _repr(term.value)) for term in sorted(rows)]
+
+ lines = [self.name, '='*len(self.name)] # heading
+
+ for term, value in p:
+ lines.append('%-30s := %s' % (term, value))
+
+# terms, values = zip(*p)
+# widths = map(len, terms)
+# fmt = '%%-%ds => %%s.' % max(widths)
+# if max(widths) > 50:
+# for term, value in zip(terms, values):
+# lines.append(term)
+# lines.append(' => %s.' % value)
+# else:
+# for term, value in zip(terms, values):
+# lines.append(fmt % (term, value))
+
+ lines.append('')
+ return '\n'.join(lines)
def __getitem__(self, s):
assert len(s) == self.arity + 1, \
# TODO: codegen should produce specialized Term with inc/dec methods baked
# in. This seems nicer than having a separate aggregator object.
+# TODO: aggregators might want a reference to the item they are associated with.
+
import operator
from collections import Counter
from utils import drepr, _repr
+from errors import AggregatorError
+"""
class Aggregator(object):
def fold(self):
raise NotImplementedError
raise NotImplementedError
def clear(self):
raise NotImplementedError
+"""
+
+class NoAggregatorError(Exception):
+ """
+ raised when an item doesn't have an aggregator.
+ """
+ pass
+
+
+class Aggregator(object):
+ def fold(self):
+ raise AggregatorError("item doesn't have an aggregator.")
+ def inc(self, _val, _ruleix, _variables):
+ pass
+ def dec(self, _val, _ruleix, _variables):
+ pass
+ def clear(self):
+ pass
class BAggregator(Counter, Aggregator):
return max(vs)[1]
+class Equals(BAggregator):
+ def inc(self, val, _ruleix, _variables):
+ self[val] += 1
+ def dec(self, val, _ruleix, _variables):
+ self[val] -= 1
+ def fold(self):
+ vs = [v for v, cnt in self.iteritems() if cnt > 0]
+ if len(vs) != 1:
+ 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'
if name == ':=':
return ColonEquals()
+ elif name == '=':
+ return Equals()
+
elif name == 'dict=':
return DictEquals()
from repl import REPL
from cStringIO import StringIO
-from utils import red, green
+from utils import red, green, strip_comments
def extract(code):
repl = REPL(interp)
for cmd, expect in extract(code):
+ if not cmd.strip():
+ print
+ continue
print ':-', cmd
sys.stdout = x = StringIO()
try:
sys.stdout = sys.__stdout__
got = x.getvalue().strip()
expect = expect.strip()
- if expect != got:
+ if strip_comments(expect) != strip_comments(got):
print green % expect
print red % got
else:
pass
+class AggregatorError(Exception):
+ pass
+
class DynaInitializerException(Exception):
def __init__(self, exception, init):
rule = parse_attrs(init)['rule']
- sheebang?
-
- - TODO: @nwf remove comments from rule source
-
-
- vbench: a script which tracks performace over time (= git commits).
- profiler workflow
from prioritydict import prioritydict
from config import dotdynadir
-from errors import crash_handler, DynaInitializerException
+from errors import crash_handler, DynaInitializerException, AggregatorError
class Rule(object):
self.agg_name = agg_name
super(foo, self).__init__()
def __missing__(self, fn):
+
+ if fn == 'contains/2':
+ return Contains()
+
arity = int(fn.split('/')[-1])
self[fn] = c = Chart(fn, arity, self.agg_name[fn])
return c
return None
-import os
+class Contains(object):
+
+ def __init__(self):
+ self.name = 'contains'
+ self.arity = 2
+
+ def __repr__(self):
+ return 'contains/2'
+
+ def __getitem__(self, s):
+ assert len(s) == self.arity + 1, \
+ 'Chart %r: item width mismatch: arity %s, item %s' % (self.name, self.arity, len(s))
+ [x, xs], val = s[:-1], s[-1]
+ #assert val is True
+ if isinstance(x, slice):
+ assert not isinstance(xs, slice)
+ for a in xs.tolist():
+ term = Term('contains/2', (a, xs))
+ term.value = True
+ yield term, term.args, term.value
+
+ else:
+ # all bound membership test
+ assert not isinstance(x, slice) and not isinstance(xs, slice)
+ term = Term('contains/2', (x, xs))
+ term.value = (x in xs.tolist())
+ yield term, term.args, term.value
+
+ def insert(self, args):
+ assert False
+
class Interpreter(object):
print >> out
def dump_rules(self):
+ if not self.rules:
+ return
+ print
+ print 'Rules'
+ print '====='
for i in sorted(self.rules):
print '%3s: %s' % (i, self.rules[i].src)
+ print
def build(self, fn, *args):
# TODO: codegen should handle true/0 is True and false/0 is False
was = item.value
try:
now = item.aggregator.fold()
+ except AggregatorError as e:
+ error[item] = ('failed to aggregate item `%r` because %s' % (item, e), [(e, None)])
+
+ now = self.build('$error/0')
+ changed[item] = now
+ item.value = now
+ continue
+
except (ZeroDivisionError, TypeError, KeyboardInterrupt, NotImplementedError) as e:
error[item] = ('failed to aggregate %r' % item.aggregator, [(e, None)])
+
+ now = self.build('$error/0')
+ changed[item] = now
+ item.value = now
continue
+
if was == now:
continue
was_error = False
if args.plan:
# copy plan to tmp directory
- plan = tmp / args.source.read_hexhash('sha1') + '.plan.py'
+ plan = interp.tmp / args.source.read_hexhash('sha1') + '.plan.py'
args.source.copy(plan)
else:
:- c += a*b.
:- rules
+
+ Rules
+ =====
0: a += 1.
1: b += 1.
2: c += a * b.
from errors import notimplemented
from utils import _repr
+from defn import Aggregator
+
# TODO: codegen should output a derived Term instance for each functor
class Term(object):
self.tail = tail
assert isinstance(tail, (Cons, _Nil)), tail
Term.__init__(self, 'cons/2', (head, tail))
+ self.aggregator = Aggregator()
def tolist(self):
return [self.head] + self.tail.tolist()
def __repr__(self):
class _Nil(Term):
def __init__(self):
Term.__init__(self, 'nil/0', ())
+ self.aggregator = Aggregator()
+
def tolist(self):
return []
def __repr__(self):
a += 1.
a." |./dyna > $0.out
-diff <(sed -e 's/[ -][^ -]*\/\.dyna/ /g' $0.expect) \
- <(sed -e 's/[ -][^ -]*\/\.dyna/ /g' $0.out) \
+diff <(sed -e 's/[ -][^ -]*\.dyna/ /g' $0.expect) \
+ <(sed -e 's/[ -][^ -]*\.dyna/ /g' $0.out) \
&& echo pass
-:- :- :- 0: a += b * c.
+:- :- :-
+Rules
+=====
+ 0: a += b * c.
+
:- =============
b := 2
-:- 0: a += b * c.
+:-
+Rules
+=====
+ 0: a += b * c.
1: b := 2.
+
:- =============
a := 6
c := 3
-:- 0: a += b * c.
+:-
+Rules
+=====
+ 0: a += b * c.
1: b := 2.
2: c := 3.
+
:- exit
b := 1
:- =============
a := 2
-:- 0: a += 1.
+:-
+Rules
+=====
+ 0: a += 1.
1: b += 1.
2: a += 1.
+
:-
Solution
========