BUGFIX: looks like my previous bugs fix broke alignment with python and dopamine
code. I've fixed the Python code. Dopamine coming soon.
REPL `query` no longer kept up-to-date with FC because we throw away the rule
once we're done with it.
added REPL `run` command to execute dyna code from a different file (NOTE: there
is no namespacing so bad things can happen.)
BUGFIX: Fixed cyclic `Term` comparsion.
BUGFIX: Fixed dictionary changed size on iteration exception. Thanks to Jason
for reporting.
<+> parens (pretty $ c v vs)
-- XXX This works only for the special case at hand (thus the asserts)
-pdope_ bc (OPIter o m f DetSemi Nothing) | (f,length m) `S.member` bc =
+pdope_ bc (OPIter o m f DetSemi Nothing) | (f,length m) `S.member` bc = do
+ dookie <- incState
return $
- assert (iIsFree $ nExpose $ o^.mv_mi) $
- assert (all (not . iIsFree . nExpose . _mv_mi) m) $
- vcat
- [ pretty (o^.mv_var)
- <+> equals
- <+> "gbc"
- <> tupled (pfas f m : map (pretty . _mv_var) m)
- , "if" <+> pretty (o^.mv_var) <+> "is not None" <> colon
- ]
+ assert (iIsFree $ nExpose $ o^.mv_mi) $
+ assert (all (not . iIsFree . nExpose . _mv_mi) m) $
+ vcat
+ [ pretty (o^.mv_var)
+ <+> equals
+ <+> "gbc"
+ <> tupled (pfas f m : map (pretty . _mv_var) m)
+
+--- needs an opbuild
+ , ("d" <> pretty dookie)
+ <+> equals
+ <+> ("build" <> tupled (pfas f m : map (pretty . _mv_var) m))
+
+ , "if" <+> pretty (o^.mv_var) <+> "is not None" <> colon
+
+ ]
pdope_ bc (OPIter o m f _ Nothing) =
assert (not $ (f,length m) `S.member` bc) $ do
if len(b) == 0:
# all arguments are free.
- candidates = self.intern.itervalues()
+ candidates = self.intern.values()
elif len(b) == 1:
- candidates = iter(b[0])
+ candidates = list(b[0])
else:
b.sort(key=len) # start with smaller ones
""" % (bline, kv, block)
+ # -------------
+ # Dopamine code
with file(d + '/dopini') as f:
code = f.read()
print >> html, '<h2>Initialization plans</h2>'
for (f,bline,bcol,eline,ecol,kv,block) in \
- re.findall(';; (.*?):(\d+):(\d+)-.*?:(\d+):(\d+) (.*)\n((?: [^\n]*\n)*)'
- , code) :
+ re.findall(';; (.*?):(\d+):(\d+)-.*?:(\d+):(\d+) (.*)\n((?: [^\n]*\n)*)', code):
+ print >> html, """<div class="dopamine-%s"><pre>Initializer:\n%s</pre></div>""" % (bline, block)
- print >> html, """\
-<div class="dopamine-%s">
-<pre>
-Initializer:
-%s
-</pre>
-</div>
-""" % (bline, block)
+ # ----------------
+ # Python code
with file(d + '/plan') as f:
code = f.read()
- # print >> html, code
-
print >> html, '<h2>Update code</h2>'
-
for block in re.split('\n\s*\n', code):
-
- x = re.findall('Span:\s*(.*?):(\d+):(\d+)-.*?:(\d+):(\d+)\n',
- block)
-
+ x = re.findall('RuleIx: (\d+)\n', block)
if not x:
continue
-
- [(f, bline, bcol, eline, ecol)] = x
- code = block
+ [ruleix] = x
lexer = get_lexer_by_name("python", stripall=True)
formatter = HtmlFormatter(linenos=False)
- pretty_code = highlight(code, lexer, formatter)
-
- print >> html, """\
-<div class="handler-%s">
-<pre>
-%s
-</pre>
-</div>
-""" % (bline, pretty_code)
+ print >> html, """<div class="handler-%s"><pre>%s</pre></div>""" % (ruleix, highlight(block, lexer, formatter))
print >> html, '</pre>'
print >> html, '</div>'
- functor
- ignore variable
- - output formats: vquery and query
- show diffs
Maybe subscription to diff is a different beast, only available as a
from chart import Chart, Term, _repr
from defn import aggregator
from utils import ip, red, green, blue, magenta, yellow, parse_attrs, \
- ddict, dynac, read_anf
+ ddict, dynac, read_anf, strip_comments
from prioritydict import prioritydict
from config import dotdynadir
return parse_attrs(self.init or self.query)['Span']
@property
def src(self):
- return parse_attrs(self.init or self.query)['rule']
+ return strip_comments(parse_attrs(self.init or self.query)['rule'])
def __repr__(self):
return 'Rule(%s, %r)' % (self.idx, self.src)
self.rules = ddict(Rule)
self.error = {}
- # not essential, available in parser_state
- self.backchained = set()
-
def __getstate__(self):
return ((self.chart,
self.agenda,
print >> out, '========'
fns = self.chart.keys()
fns.sort()
- fns = [x for x in fns if x not in self.backchained] # don't show backchained items
+ fns = [x for x in fns if x not in self._gbc] # don't show backchained items
nullary = [x for x in fns if x.endswith('/0')]
others = [x for x in fns if not x.endswith('/0')]
# show nullary charts first
for i in sorted(self.rules):
print '%3s: %s' % (i, self.rules[i].src)
-# def query(self, q):
-# if q.endswith('.'):
-# print "Queries don't end with a dot."
-# return
-#
-# query = 'out("%s") dict= %s.' % (q, q)
-#
-# src = self.dynac_code(query) # might raise DynaCompilerError
-# self.do(src)
-#
-# try:
-# [(_, _, results)] = self.chart['out/1'][q,:]
-# except ValueError:
-# print 'No results.'
-# return
-#
-# for val, bindings in results:
-# print ' ', val, 'when', bindings
-# print
-
def build(self, fn, *args):
# TODO: codegen should handle true/0 is True and false/0 is False
if fn == "true/0":
# def retract_item(self, item):
# """
-# For the moment we only correctly retract leaves.
-#
-# If you retract a non-leaf item, you run the risk of it being
-# rederived. In the case of cyclic programs the derivation might be the
-# same or different.
+# For the moment we only correctly retract leaves. If you retract a
+# non-leaf item, you run the risk of it being rederived. In the case of
+# cyclic programs the derivation might be the same or different.
# """
-# # and now, for something truely horrendous -- look up an item by it's
-# # string value! This could fail because of whitespace or trivial
-# # formatting differences.
-# items = {}
-# for c in self.chart.values():
-# for i in c.intern.values():
-# items[str(i)] = i
-# try:
-# item = items[item]
-# except KeyError:
-# print 'item not found. This could be because of a trivial formatting differences...'
-# return
# self.emit(item, item.value, None, sys.maxint, delete=True)
# return self.go()
A rule is bad if the compiler rejects it or it's initializer fails.
"""
assert os.path.exists(filename)
-# assert os.path.exists(filename + '.anf')
env = imp.load_source('dynamically_loaded_module', filename)
for k, v in env.agg_decl.items():
self.new_fn(k, v)
+ new_rules = set()
+ for _, r, _ in env.queries:
+ new_rules.add(r)
+ for r, _ in env.initializers:
+ new_rules.add(r)
+ self.new_rules = new_rules
+
for fn, r, h in env.queries:
self.new_query(fn, r, h)
# accept the new parser state
self.parser_state = env.parser_state
- self.backchained = {f + '/' + a for f, a in re.findall(":-backchain '([^']+)'/(\d+).", env.parser_state)}
-
# process emits
for e in emits:
self.emit(*e, delete=False)
if args.plan:
plan = args.source
else:
-# plan = dotdynadir / 'tmp' / args.source.read_hexhash('sha1') + '.plan.py'
plan = args.source + '.plan.py'
dynac(args.source, plan)
# -*- coding: utf-8 -*-
-"""
-TODO: nwf remove comments from rule source
-"""
import webbrowser
from debug import Hypergraph
edges.add((item, ruleix, tuple(b), variables))
for r in interp.rules.values():
- r.init(emit=_emit)
+ if r.init is not None:
+ r.init(emit=_emit)
+ else:
+ assert r.query is not None
+
+ # todo: this might pick nodes that aren't used
+ for fn, hs in interp._gbc.items():
+ for x in interp.chart[fn].intern.values():
+ if x.value is not None:
+ for h in hs:
+ h(*x.args, emit=_emit)
return edges
def __init__(self, interp):
self.interp = interp
- def main(self, outfile):
- global interp
+ def main(self, outfile, open=True):
interp = self.interp
es = infer_edges(interp)
print >> f, '</body></html>'
- webbrowser.open(f.name)
+ if open:
+ webbrowser.open(f.name)
block = branch([dig(x, visited, groups, interp) for x in body])
if block:
- contribs.append(crux.format() + [''] + block)
+ contribs.append(crux.format() + ['|'] + block)
else:
contribs.append(crux.format())
"""
TODO: unsubscribe
-TODO: should probably remove the new rule after we get the results.
+TODO: query should probably remove the new rule after we get the results.
TODO: subscriptions probably should only show "changes"
import re, os, cmd, readline
import debug, interpreter
-from utils import ip, lexer, subst
+from utils import dynac, ip, lexer, subst
from errors import DynaCompilerError, DynaInitializerException
from chart import _repr
from config import dotdynadir
f.write(line)
debug.main(f.name)
+ def do_run(self, filename):
+ """
+ Load dyna rules from `filename`.
+
+ :- run examples/papa.dyna
+
+ """
+ try:
+ changed = self.interp.do(dynac(filename))
+ except DynaCompilerError as e:
+ print e
+ else:
+ self._changed(changed)
+
def _query(self, q):
if q.endswith('.'):
print "Queries don't end with a dot."
return
- query = '$out(%s) dict= %s.' % (self.lineno, q)
- self.default(query, show_changed=False)
+
+ self.interp.new_rules = set()
+
try:
- [(_, _, results)] = self.interp.chart['$out/1'][self.lineno,:]
- except ValueError:
- return []
- return results
+ query = "$out(%s) dict= %s." % (self.lineno, q)
+ self.default(query, show_changed=False)
+ try:
+ [(_, _, results)] = self.interp.chart['$out/1'][self.lineno,:]
+ return results
+ except ValueError:
+ return []
+ 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['$out/1']
+
def do_vquery(self, q):
"""
self.value = None
self.aggregator = None
+ def __eq__(self, other):
+ if other is None:
+ return False
+ if not isinstance(other, Term):
+ return False
+ return self.fn == other.fn and self.args == other.args
+
def __cmp__(self, other):
+# if self is other:
+# return 0
if other is None:
return 1
if not isinstance(other, Term):
return 1
+# if self == other:
+# return 0
return cmp((self.fn, self.args), (other.fn, other.args))
- # default hash and eq suffice because we intern
- #def __hash__(self):
- #def __eq__(self):
-
def __repr__(self):
"Pretty print a term. Will retrieve the complete (ground) term."
fn = '/'.join(self.fn.split('/')[:-1]) # drop arity from name.
# X.value = Y
# print [X,Y,Z]
# assert X.value == Y.value == Z.value == 3
-
map('\033[3%sm%%s\033[0m'.__mod__, range(8))
-def dynac(f, out):
+_comments = re.compile('%.*$', re.MULTILINE)
+def strip_comments(src):
+ return _comments.sub('', src).strip()
+
+
+def dynac(f, out=None):
"""
Run compiler on file, ``f``, write results to ``out``. Raises
``DynaCompilerError`` on failure.
"""
from errors import DynaCompilerError
+ f = path(f)
+ if not f.exists():
+ raise DynaCompilerError("File '%s' does not exist." % f)
+
+ if out is None:
+ out = dotdynadir / 'tmp' / f.read_hexhash('sha1') + '.plan.py'
+
p = Popen(['%s/dist/build/dyna/dyna' % dynahome,
'--dump-anf=' + out + '.anf', # timv: don't like this filename...
'-B', 'python', '-o', out, f], stdout=PIPE, stderr=PIPE)
assert not stdout.strip(), [stdout, stderr]
raise DynaCompilerError(stderr)
+ return out
+
def lexer(term):
return re.findall('"[^"]*"' # string