- fixed argument disposition of tupling operators: `->` and `:`.
- Compare rules by their index (i.e. equality, hash,q and sort order).
- Recompilation works like rule initialization
def diff(expect, got):
with file('/tmp/expect','wb') as A:
A.write(expect)
+ A.write('\n')
with file('/tmp/got','wb') as B:
B.write(got)
+ B.write('\n')
from subprocess import Popen, PIPE
p = Popen(['colordiff', A.name, B.name], stdout=PIPE, stderr=PIPE)
return p.communicate()[0]
def extract(code):
for block in re.compile('^> ', re.MULTILINE).split(code):
- cmd = []
- expect = []
-
- reading = True
-
+ cmd, expect, reading = [], [], True
for i, line in enumerate(block.split('\n')):
if (line.startswith('|') or i == 0) and reading:
if line.startswith('|'):
else:
reading = False
expect.append(line)
-
yield '\n'.join(cmd).strip(), '\n'.join(expect).strip()
from term import Term, Cons, Nil, MapsTo, Error
from chart import Chart
from utils import red, parse_attrs, ddict, dynac, read_anf, strip_comments, \
- _repr, hide_ugly_filename, true, false, parse_parser_state
+ _repr, hide_ugly_filename, true, false, parse_parser_state, magenta, indent
from prioritydict import prioritydict
from config import dotdynadir
self.anf = None
self.head_fn = None
+ def __eq__(self, other):
+ return self.index == other.index
+
+ def hash(self):
+ return self.index
+
+ def __cmp__(self, other):
+ try:
+ return cmp(self.index, other.index)
+ except AttributeError:
+ return 1
+
def __repr__(self):
return 'Rule(%s, %r)' % (self.index, self.src)
for fn in bc:
if fn not in self._gbc: # new backchain declaration
for r in self.rule_by_head[fn]:
- self.recompile.add(r)
+ self.needs_recompile(r)
def __init__(self):
# declarations
while agenda:
item = self.agenda.pop_smallest()
self.pop(item)
- # after draining the agenda, try to initialize pending rules.
+ self.run_recompile()
self.run_uninitialized()
if self.agenda:
self._agenda()
self.emit(*e)
def gbc(self, fn, args):
-
item = self.build(fn, *args)
-
- # TODO: we will need to distinguish `unknown` from `null` when we move
- # to mixed chaining.
if item.value is not None:
return item.value
-
return self.force_gbc(item)
def force_gbc(self, item):
return self.pop(item)
- def load_plan(self, filename, recurse=True):
+ def load_plan(self, filename):
"""
Compile, load, and execute new dyna rules.
for fn, r, h in env.updaters:
self.new_updater(fn, r, h)
- if recurse:
- self.run_recompile()
-
- # we we don't accumulate all changed rules, new_rules return will be new
- # rules of top-level call.
return new_rules
def recompile_rule(self, r):
f.write(code)
return self.dynac(dyna)
+ def needs_recompile(self, r):
+ self.retract_rule(r.index) # clears errors
+ self.recompile.add(r)
+
def run_recompile(self):
- # TODO: it's a bit strange to ignore the error and just print
- # it. However, since the rules in the recompile list are
- # syntactically valid (well, they at least they were valid) -- this
- # means that errors must be planning errors... probably all to do
- # with missing BC declarations.
- #
- # TODO: we probably have to worry about infinite loops -- at the
- # moment this results in an interpreter crash due to max recursion
- # limit
- #
- # TODO: maybe we should handle recompilation more like we do
- # uninitialized rules.
# run to fixed point.
while self.recompile:
+ success = set()
failed = set()
for r in list(self.recompile):
try:
- plan = self.recompile_rule(r)
+ r.plan = self.recompile_rule(r)
except DynaCompilerError as e:
failed.add(r)
self.set_error(r, e)
else:
- self.retract_rule(r.index)
- self.load_plan(plan, recurse=False)
- if failed == self.recompile: # no progress
- break
+ success.add(r)
+ self.clear_error(r)
self.recompile = failed
+ if not success:
+ break
+ for r in success:
+ self.load_plan(r.plan)
def run_uninitialized(self):
q = set(self.uninitialized_rules)
# 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.needs_recompile(d)
self._gbc[head_fn].append(query)
visited = set()
if fn not in self._gbc or fn in visited:
- # don't refresh non-BC computation be careful not to get stuck in an
- # infinite loop if there is a cycle in the dep graph
+ # don't refresh non-BC computation. Also, be careful not to get
+ # stuck in an infinite loop if there is a cycle in the dep graph
return
visited.add(fn)
else:
print >> out, ' %s' % (e)
+ #print >> out
+ #print >> out, magenta % indent(e.traceback.rstrip(), indent=' ')
+
print >> out
print >> out, r.render_ctx(e.exception_frame, indent=' ')
print >> out
# infix
if (not label[0].isalpha() and label[0] not in ('$','&') and len(fn_args) == 2) \
- or label in ('in', 'and', 'or', 'with_key', '->', 'is'):
+ or label in ('in', 'with_key', '&with_key', '->', '&->', 'is'):
+
+ if label in {'&with_key', '->', '&->'}:
+ label = label[1:]
+
[a,b] = fn_args
return '(%s %s %s)' % (a, label, b)
return '%s(%s)' % (label, ', '.join(fn_args))
_e = e
a = []
while e.label == '& cons':
-
- # TODO: crashlogs/abarany:2013-07-24:11:47:31:17823.log
- x, xs = e.body # e = _a3 = & cons(uV, uX)
-
- # g.incoming = {
- # '_a5': [_a5 = edge@1(uX, uU, uV)],
- # '_a4': [_a4 = pathto@0(uU)],
- # '_a3': [_a3 = & cons(uV, uX)],
- # '_a2': [_a2 = +@2(_a4, _a5)],
- # 'uU': [],
- # '_t1': [_t1 = & with_key(_a2, _a3)],
- # 'uV': [],
- # '_t0': [_t0 = & pathto(uV)], 'uX': []
- # }
- # xs = 'uX'
-
- [e] = g.incoming[xs]
+ x, xs = e.body
a.append(self._get_function(x))
+ if not g.incoming[xs]:
+ a.append(self._get_function(xs))
+ break
+ [e] = g.incoming[xs]
if e.label == '& nil':
return '[%s]' % ', '.join(a)
from utils import ip, lexer, subst, drepr, _repr, get_module, yellow, \
green, bold
from stdlib import topython, todyna
-from errors import DynaCompilerError
+from errors import DynaCompilerError, show_traceback
from config import dotdynadir
-from errors import show_traceback
import load, post
from interpreter import Rule
errors_before = self.interp.error.copy()
yield
if errors_before.items() != self.interp.error.items():
+
e = set(errors_before) | set(self.interp.error)
new_errors = 0
cleared_errors = 0
#print 'cleared error at `%s`.' % k
cleared_errors += 1
elif not was and now:
-# print 'new error at `%s`.' % k, es
+ #print 'new error at `%s`.' % k, es
new_errors += 1
if new_errors and cleared_errors:
print yellow % '>>>', '%s new errors, %s errors cleared. Type `sol` for details.\n' \
# return todyna([x+a for a in self.aslist])
+def lookup(k, alist):
+ a = dict(alist)
+ return a.get(k)
+
def or_(x, y):
if not (isbool(x) and isbool(y)):
raise TypeError('`|` expected Boolean arguments, got `%s` and `%s`' \
def __init__(self, k, v):
super(MapsTo, self).__init__('->/2', (k, v))
def __repr__(self):
- return '%s -> %s' % self.args
+ return '%s -> %s' % tuple(map(_repr, self.args))
return backchain, ruleix, iaggr, other
+def indent(x, indent=''):
+ if isinstance(x, basestring):
+ return re.compile('^(.*)$', flags=re.MULTILINE).sub(indent + r'\1', x)
+ else:
+ return [indent + y for y in x]
+
class _true(object):
def __nonzero__(self):
, (("false",0),(SDQuote,[]))
-- key
, (("$key" ,1),(SDEval,[ADQuote]))
- , (("with_key",2),(SDQuote,[ADEval, ADEval]))
+ , (("with_key",2),(SDQuote,[ADEval,ADEval]))
-- lists
, (("nil", 0),(SDQuote,[]))
, (("cons", 2),(SDQuote,[ADEval,ADEval]))
- , (("->",2),(SDQuote,[ADQuote, ADQuote]))
- , ((":",2),(SDQuote,[ADQuote, ADQuote]))
+ , (("->", 2),(SDQuote,[ADEval,ADEval]))
+ , ((":", 2),(SDQuote,[ADEval,ADEval]))
]
-- | Make the default surface syntax more functional. Here, all functors
-- key
, (("$key" ,1),(SDEval,[ADQuote]))
, (("with_key",2),(SDQuote,[ADEval, ADEval]))
- , (("->",2),(SDQuote,[ADQuote, ADQuote]))
- , ((":",2),(SDQuote,[ADQuote, ADQuote]))
-
+ -- tuples
+ , (("->", 2),(SDQuote,[ADEval,ADEval]))
+ , ((":", 2),(SDQuote,[ADEval,ADEval]))
]
------------------------------------------------------------------------}}}
> query foo(3)
foo(3) = 12.
+
+> sol
+
+Solution
+========
+a/1
+===
+a(1) = 1.
+a(2) = 2.
+a(3) = 3.
+a(4) = 4.