]> hydra-www.ietfng.org Git - dyna2/commitdiff
New output format
authorTim Vieira <tim.f.vieira@gmail.com>
Tue, 25 Jun 2013 01:53:03 +0000 (21:53 -0400)
committerTim Vieira <tim.f.vieira@gmail.com>
Tue, 25 Jun 2013 01:53:03 +0000 (21:53 -0400)
 - print nullary terms together at the top.

 - don't print empty charts

 - heading is "Solution" instead of "Charts"

REPL cmd 'chart' renamed to 'sol'

Added lots of help documentation for REPL

21 files changed:
examples/expected/dijkstra.py.out
examples/expected/equalities.py.out
examples/expected/fib-limit.py.out
examples/expected/matrixops.py.out
examples/expected/papa2.py.out
examples/expected/simple.py.out
src/Dyna/Backend/Python/Backend.hs
src/Dyna/Backend/Python/chart.py
src/Dyna/Backend/Python/defn.py
src/Dyna/Backend/Python/interpreter.py
src/Dyna/Backend/Python/load/__init__.py
src/Dyna/Backend/Python/post/__init__.py
src/Dyna/Backend/Python/post/draw_circuit.py
src/Dyna/Backend/Python/post/dump_solution.py [moved from src/Dyna/Backend/Python/post/dump_chart.py with 75% similarity]
src/Dyna/Backend/Python/post/graph.py
src/Dyna/Backend/Python/post/save.py
src/Dyna/Backend/Python/repl.py
test/repl/aggregator-conflict.expect
test/repl/late-aggregator-assignment.expect
test/repl/retract-rule
test/repl/retract-rule.expect

index 4fcbb540eab4831b66d466950442e72693e8ab75..033158547a5ae7ae21a833064af8b55130dd0b2e 100644 (file)
@@ -1,8 +1,12 @@
 
-Charts
-============
+Solution
+========
+end := "MyHouse"
+goal := 2410
+start := "FriendHouse"
+
 edge/2
-=================
+======
 edge("BOS","JFK")              := 187
 edge("BOS","MIA")              := 1258
 edge("DFW","LAX")              := 1235
@@ -17,16 +21,8 @@ edge("MIA","LAX")              := 2342
 edge("ORD","DFW")              := 802
 edge("ORD","MyHouse")          := 20
 
-end/0
-=================
-end                            := "MyHouse"
-
-goal/0
-=================
-goal                           := 2410
-
 path/1
-=================
+======
 path("BOS")                    := 10
 path("DFW")                    := 1588
 path("FriendHouse")            := 0
@@ -37,7 +33,3 @@ path("MyHouse")                := 2410
 path("ORD")                    := 2390
 path("SFO")                    := 2779
 
-start/0
-=================
-start                          := "FriendHouse"
-
index 637d9c7b62232e56c960f022c77e7e3b02e3e5d0..f4e53c2140219aa794b4ba50e289ef948f9d9b10 100644 (file)
@@ -1,35 +1,11 @@
 
-Charts
-============
-a/0
-=================
-a                              := 0
-
-b/0
-=================
-b                              := 0
-
-by_evl_cross/0
-=================
-by_evl_cross                   := true
-
-by_evl_refl/0
-=================
-by_evl_refl                    := true
-
-by_is_0a/0
-=================
-by_is_0a                       := true
-
-by_is_ab/0
-=================
-
-
-by_syn_cross/0
-=================
-by_syn_cross                   := false
-
-by_syn_refl/0
-=================
-by_syn_refl                    := true
+Solution
+========
+a := 0
+b := 0
+by_evl_cross := true
+by_evl_refl := true
+by_is_0a := true
+by_syn_cross := false
+by_syn_refl := true
 
index 2d35826c1792c0d14d8d59a0f575abda72e66b23..db580ece01998d30f9b27387af151d957fba0bef 100644 (file)
@@ -1,8 +1,10 @@
 
-Charts
-============
+Solution
+========
+lim := 10
+
 f/1
-=================
+===
 f(1)                           := 1
 f(2)                           := 1
 f(3)                           := 2
@@ -13,7 +15,3 @@ f(7)                           := 13
 f(8)                           := 21
 f(9)                           := 34
 
-lim/0
-=================
-lim                            := 10
-
index 76ff74304137826cd6254b16fd2cb52b76872386..cc48229dd3972cdbf5ab83aa55ef62c4b140a458 100644 (file)
@@ -1,26 +1,15 @@
 
-Charts
-============
-a/0
-=================
-
-
-b/0
-=================
-
-
-c/0
-=================
-
+Solution
+========
 
 cols/1
-=================
+======
 cols(a)                        := 2
 cols(b)                        := 3
 cols(c)                        := 3
 
 m/3
-=================
+===
 m(a,1,1)                       := 1
 m(a,1,2)                       := 0
 m(a,2,1)                       := 0
@@ -39,23 +28,23 @@ m(c,2,2)                       := 2
 m(c,2,3)                       := 0
 
 product/2
-=================
+=========
 product(a,b)                   := c
 
 rows/1
-=================
+======
 rows(a)                        := 2
 rows(b)                        := 2
 rows(c)                        := 2
 
 shape/3
-=================
+=======
 shape(a,2,2)                   := true
 shape(b,2,3)                   := true
 shape(c,2,3)                   := true
 
 times/4
-=================
+=======
 times(a,b,1,1)                 := 3
 times(a,b,1,2)                 := 0
 times(a,b,1,3)                 := 1
index 3ec66f33fa8155a23a43c1940013bee1e385df58..aac98f009813333f1ec8ae01f9164eb8b5933faa 100644 (file)
@@ -1,33 +1,18 @@
 
-Charts
-============
-best/0
-=================
-best                           := pair(1,t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),"."))
-
-bestParse/0
-=================
-bestParse                      := t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),".")
-
-bestScore/0
-=================
-bestScore                      := 1
+Solution
+========
+best := pair(1,t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),"."))
+bestParse := t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),".")
+bestScore := 1
+length := 8
 
 goal/1
-=================
+======
 goal(t("S",t("S",t("NP","Papa"),t("VP",t("V","ate"),t("NP",t("NP",t("Det","the"),t("N","caviar")),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon")))))),".")) := 1
 goal(t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),".")) := 1
 
-length/0
-=================
-length                         := 8
-
-pair/2
-=================
-
-
 phrase/4
-=================
+========
 phrase(".",7,8,".")            := 1
 phrase("Det",2,3,t("Det","the")) := 1
 phrase("Det",5,6,t("Det","a")) := 1
@@ -57,7 +42,7 @@ phrase("the",2,3,"the")        := 1
 phrase("with",4,5,"with")      := 1
 
 rewrite/2
-=================
+=========
 rewrite("Det","a")             := 1
 rewrite("Det","the")           := 1
 rewrite("N","caviar")          := 1
@@ -67,7 +52,7 @@ rewrite("P","with")            := 1
 rewrite("V","ate")             := 1
 
 rewrite/3
-=================
+=========
 rewrite("NP","Det","N")        := 1
 rewrite("NP","NP","PP")        := 1
 rewrite("PP","P","NP")         := 1
@@ -76,16 +61,8 @@ rewrite("S","S",".")           := 1
 rewrite("VP","V","NP")         := 1
 rewrite("VP","VP","PP")        := 1
 
-t/2
-=================
-
-
-t/3
-=================
-
-
 word/2
-=================
+======
 word(".",7)                    := true
 word("Papa",0)                 := true
 word("a",5)                    := true
index bd2f72ce9659b4ef6f45c4df2375b2813ba666d1..e43e6513d88df8f7a9693c7ea343b91a4711bb80 100644 (file)
@@ -1,15 +1,7 @@
 
-Charts
-============
-a/0
-=================
-a                              := true
-
-b/0
-=================
-b                              := true
-
-c/0
-=================
-c                              := true
+Solution
+========
+a := true
+b := true
+c := true
 
index 033315b8fb573ded98bca31f7c029662a405cff6..d6ea0f2927d3d747c212b94940eb538c1ec9fcd2 100644 (file)
@@ -279,14 +279,14 @@ pdope_ _ (OPEmit h r i vs) = do
   ds <- get
 
   -- A python map of variable name to value
-  let varmap = braces $ align $ fillPunct (comma <> space) $
-         ("'nodes'" <> colon <> (encloseSep lbracket rbracket comma $ map (("d"<>).pretty) [0..ds-1]))
-         : (map (\v -> let v' = pretty v in dquotes v' <> colon <+> v') vs)
+  let varmap = brackets $ align $ fillPunct (comma <> space) $
+         parens ("'nodes'" <> comma <> (encloseSep lbracket rbracket comma $ map (("d"<>).pretty) [0..ds-1]))
+         : (map (\v -> let v' = pretty v in parens (dquotes v' <> comma <+> v')) vs)
 
   return $ "emit" <> tupled [ pretty h
                             , pretty r
                             , pretty i
-                            , varmap
+                            , "tuple" <> (parens $ varmap)
                             ]
 
 -- | Render a dopamine sequence's checks and loops above a (indended) core.
index a6209d79a72194eed43b097b5c176fc6284fbc3e..7bd2f0716786a01c094e6db57770d892d5e5f457 100644 (file)
@@ -16,9 +16,17 @@ class Chart(object):
         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:
+            return '%s := %s' % (term, _repr(term.value))
+
         x = '\n'.join('%-30s := %s' % (term, _repr(term.value)) for term in sorted(rows))
-        return '%s\n=================\n%s' % (self.name, x)
+        return '%s\n%s\n%s\n' % (self.name, '='*len(self.name), x)
 
     def __getitem__(self, s):
         assert len(s) == self.arity + 1, \
index e48d28f597be157c99efd9ea18f60abe6b6fe5a7..cbcd7c59a3eb6c66b6bac17db35a6f2299811ee2 100644 (file)
@@ -17,7 +17,7 @@ class Aggregator(object):
 
 
 class BAggregator(Counter, Aggregator):
-#    def __init__(self):   
+#    def __init__(self):
 #        super(BAggregator, self).__init__()
     def inc(self, val, ruleix, variables):
         self[val] += 1
@@ -25,7 +25,7 @@ class BAggregator(Counter, Aggregator):
         self[val] -= 1
     def fromkeys(self, *_):
         assert False, "This method should never be called."
-        
+
 
 class PlusEquals(object):
     __slots__ = 'pos', 'neg'
@@ -55,10 +55,20 @@ 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.items()
+    return tuple((name[1:], val) for name, val in variables
                  if name.startswith('u') and not name.startswith('u_'))
 
 
+from term import _repr
+def drepr(vs):
+    return '{%s}' %  ', '.join('%s=%s' % (k, _repr(v)) for k,v in vs.iteritems())
+
+from collections import namedtuple
+class Result(namedtuple('Result', 'value variables')):
+    def __repr__(self):
+        return 'Result(value=%s, variables=%s)' % (_repr(self.value), drepr(dict(self.variables)))
+
+
 class DictEquals(BAggregator):
 
     def inc(self, val, ruleix, variables):
@@ -72,7 +82,7 @@ class DictEquals(BAggregator):
         self[val, vs] -= 1
 
     def fold(self):
-        return list((v, dict(b)) for (v, b), cnt in self.iteritems())
+        return tuple(Result(v, b) for (v, b), cnt in self.iteritems() if cnt > 0)
 
 
 class majority_equals(BAggregator):
index 6d5d6652a51cc8b4e49b1b904f79d5975d9245bd..2d7d5a31d02ef5975ec9ebb338ff88d581ece6a4 100644 (file)
@@ -43,6 +43,8 @@ TODO
    Maybe subscription to diff is a different beast, only available as a
    procedural world.
 
+ - TODO: True and 1 are equivalent. This sometimes leads to strange behavior.
+
  - New syntax for doing repl stuff (@nwf): load, subscribe, post-process
 
    - sheebang
@@ -55,7 +57,7 @@ TODO
 
  - crash handler
 
-   - where to errors go?
+   - where do errors go?
 
      - @nwf suggests temporary measure for LSA students: time-stamped file
        sitting in the users home directory,
@@ -95,9 +97,6 @@ FASTER
  - Collect all query modes used by the planner. Consider indexing value column
    if plans need it.
 
- - dynac should provide routines for building terms. We can hack something
-   together with anf output, but this will be prety kludgy and inefficient.
-
  - better default prioritization (currently FIFO)
 
  - BAggregators aren't very efficient.
@@ -190,6 +189,7 @@ import os, sys, imp, argparse
 from collections import defaultdict
 from hashlib import sha1
 from time import time
+from path import path
 
 import load, post
 
@@ -314,13 +314,23 @@ class Interpreter(object):
 
     def dump_charts(self, out=sys.stdout):
         print >> out
-        print >> out, 'Charts'
-        print >> out, '============'
+        print >> out, 'Solution'
+        print >> out, '========'
         fns = self.chart.keys()
         fns.sort()
-        for x in fns:
-            print >> out, self.chart[x]
+        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 x in nullary:
+            y = str(self.chart[x])   # skip empty chart
+            if y:
+                print >> out, y
+        if nullary:
             print >> out
+        for x in others:
+            y = str(self.chart[x])   # skip empty chart
+            if y:
+                print >> out, y
         self.dump_errors(out)
 
     def dump_errors(self, out=sys.stdout):
@@ -329,7 +339,7 @@ class Interpreter(object):
             return
         print >> out
         print >> out, 'Errors'
-        print >> out, '============'
+        print >> out, '======'
         for item, (val, es) in self.error.items():
             print >> out,  'because %r is %s:' % (item, _repr(val))
             for e, h in es:
@@ -491,7 +501,6 @@ class Interpreter(object):
         rule.updaters.append(handler)
         handler.rule = rule
 
-
     def gbc(self, fn, *args):
         # TODO: need to distinguish `unknown` from `null`
 
@@ -533,12 +542,11 @@ class Interpreter(object):
             item.aggregator.dec(val, ruleix, variables)
         else:
             item.aggregator.inc(val, ruleix, variables)
-#        self.agenda[item] = 0   # everything is high priority
         self.agenda[item] = time()  # FIFO
 
     def repl(self):
         import repl
-        repl.REPL(self, dotdynadir / 'dyna.hist').cmdloop()
+        repl.REPL(self).cmdloop()
 
     def do(self, filename, initialize=True):
         """
@@ -646,7 +654,7 @@ def peel(fn, item):
     assert item.fn == fn
     return item.args
 
-from path import path
+
 def main():
     parser = argparse.ArgumentParser(description="The dyna interpreter!")
     parser.add_argument('source', nargs='?', type=path,
index eca4929d79d75a6d6313cd08b0758ed9d2323c45..dea5dffd9203302f299d42d381bfe65b3f800137 100644 (file)
@@ -3,10 +3,10 @@ from tsv import tsv
 from matrix import matrix
 from pickled import pickled
 
-import re
+import re as _re
 
 def run(interp, line):
-    [(name, module, args)] = re.findall('^([a-z][a-zA-Z_0-9]*) = ([a-z][a-zA-Z_0-9]*)\((.*)\)', line)
+    [(name, module, args)] = _re.findall('^([a-z][a-zA-Z_0-9]*) = ([a-z][a-zA-Z_0-9]*)\((.*)\)', line)
     m = getattr(__import__('load'), module)(interp, name)
     exec 'm.main(%s)' % args
     interp.go()
index 30aa68e01f738267f5656d0789fd3d0e7450f1fc..31684738531597567a521b26e92f8b7392a89a0c 100644 (file)
@@ -1,11 +1,11 @@
-import re
+import re as _re
 from save import save
 from graph import graph
 from draw_circuit import draw_circuit
-from dump_chart import dump_chart
+from dump_solution import dump_solution
 
 
 def run(interp, line):
-    [(name, args)] = re.findall('([a-z][a-zA-Z_0-9]*)\((.*)\)$', line)
+    [(name, args)] = _re.findall('([a-z][a-zA-Z_0-9]*)\((.*)\)$', line)
     m = globals()[name](interp)
     eval('m.main(%s)' % args)
index 3d4ed5d29634f6de3264e48866b15f80ed186284..a6884c4debee976ff20ff446217299ba957e535d 100644 (file)
@@ -1,7 +1,3 @@
-"""
-Crude visualization of circuit pertaining to state of the interpreter.
-"""
-
 import webbrowser
 from debug import Hypergraph
 from cStringIO import StringIO
@@ -21,7 +17,7 @@ def infer_edges(interp):
 
     # Use rule initializers to find all active hyperedges in the current Chart.
     def _emit(item, _, ruleix, variables):
-        b = variables['nodes']
+        b = dict(variables)['nodes']
         b.sort()
         b = tuple(b)
         edges.add((item, ruleix, b))
@@ -33,6 +29,9 @@ def infer_edges(interp):
 
 
 class draw_circuit(object):
+    """
+    Crude visualization of circuit pertaining to state of the interpreter.
+    """
 
     def __init__(self, interp):
         self.interp = interp
similarity index 75%
rename from src/Dyna/Backend/Python/post/dump_chart.py
rename to src/Dyna/Backend/Python/post/dump_solution.py
index 438d3d99a77acb1a9053db5a767fa8c75f40dd98..9d911c89d4994ef276d61ff7b7707fd8d8b7db87 100644 (file)
@@ -1,14 +1,9 @@
-
-
-"""
-Save interpreter state using python's pickle protocol.
-"""
-
 import sys
 
-
-class dump_chart(object):
-
+class dump_solution(object):
+    """
+    Print solution
+    """
     def __init__(self, interp):
         self.interp = interp
 
index f06b460262c4464e6f260e105a9b6b6f80367215..79b59ad6b9059d8564b49ef86d93e644b7cc19c1 100644 (file)
@@ -1,38 +1,37 @@
-"""
-Postprocessor for animated visualization of basic elements such as lines and
-text.
-
-We look for the following patterns in the dynabase
-
-         visual element
-               v
-    frame(T, &text(String, tuple(X, Y))).
-          ^
-       time index
-
-Frames should have value true. The example above places a text element reading
-`String` at position `(X,Y)` in a frame at time `T`. This element can be
-specified by dyna rule.
-"""
-
 import pylab as pl
 from matplotlib.animation import FuncAnimation
 from collections import defaultdict
 
 class graph(object):
+    """
+    Postprocessor for animated visualization of basic elements such as lines and
+    text.
+
+    We look for the following patterns in the dynabase
+
+             visual element
+                   v
+        frame(T, &text(String, tuple(X, Y))).
+              ^
+           time index
+
+    Frames should have value true. The example above places a text element reading
+    `String` at position `(X,Y)` in a frame at time `T`. This element can be
+    specified by dyna rule.
+    """
 
     def __init__(self, interp):
         self.interp = interp
 
-    def main(self, outfile):
-        
+    def main(self, outfile, fps=30):
+
         frame = defaultdict(list)
         for _, [t, item], val in self.interp.chart['frame/2'][:,:,:]:
             if val:
                 frame[t].append(item)
-    
+
         nframes = max(frame)
-    
+
         def draw_frame(t):
             ax.cla()
             ax.set_title(t)
@@ -49,12 +48,8 @@ class graph(object):
                     ax.text(x,y,s)
                 else:
                     print 'dont know how to render', item
-    
+
         fig = pl.figure()
         ax = pl.axes()
-    
-        print 'creating animation..'
         anim = FuncAnimation(fig, draw_frame, frames=nframes)
-        print 'saving...'
-        anim.save(outfile, fps=30, extra_args=['-vcodec', 'libx264'])
-        print 'wrote examples/force.dyna.mp4'
+        anim.save(outfile, fps=fps, extra_args=['-vcodec', 'libx264'])
index 9e3ba8c2a07f8bb244bc1f9fbc85d5d6813d740a..78e570164abdc8d883248b07b8417ef9bdda08d0 100644 (file)
@@ -1,11 +1,10 @@
-"""
-Save interpreter state using python's pickle protocol.
-"""
 
 import cPickle
 
-
 class save(object):
+    """
+    Save interpreter state using python's pickle protocol.
+    """
 
     def __init__(self, interp):
         self.interp = interp
index 72fb9c8cd253b736c7299168d1a6efd6931e1c77..d222eedd68870953c0689122039311c9f9319c82 100644 (file)
@@ -1,4 +1,19 @@
-import os, cmd, readline
+"""
+TODO: unsubscribe
+
+TODO: should probably remove the new rule after we get the results.
+
+TODO: probably should show "changed"
+
+TODO: queries are all maintained... should probably toss out the query rule. If
+users want queries to be kept up-to-date user should subscribe instead.
+
+TODO: help should print call signature of loads and post-processors in addition
+to help.
+
+"""
+
+import re, os, cmd, readline
 
 import debug, interpreter
 from utils import ip
@@ -11,10 +26,37 @@ import load, post
 
 from interpreter import Interpreter, foo, none
 
+from term import _repr
+from defn import drepr
+
+
+def lexer(term):
+    return re.findall('"[^"]*"'               # string
+                      '|[a-z][a-zA-Z_0-9]*'   # functor
+                      '|[A-Z][a-zA-Z0-9_]*'   # variable
+                      '|[(), ]'               # parens and comma
+                      '|[^(), ]+', term)      # everything else
+
+
+def subst(term, v):
+    """
+    >>> subst('f("asdf",*g(1,X, Y), X+1)', {'X': 1234})
+    'f("asdf",*g(1,1234, Y), 1234+1)'
+
+    >>> subst('f("asdf",*g(1,X, Y), XX+1)', {'X': 1234})
+    'f("asdf",*g(1,1234, Y), XX+1)'
+
+    >>> subst('f("asdf",*g(1,uX, Y), X_+1)', {'X': 1234})
+    'f("asdf",*g(1,uX, Y), X_+1)'
+
+    """
+    assert isinstance(v, dict)
+    return ''.join((_repr(v[x]) if x in v else x) for x in lexer(term))
+
 
 class REPL(cmd.Cmd, object):
 
-    def __init__(self, interp, hist):
+    def __init__(self, interp, hist=dotdynadir / 'dyna.hist'):
         self.interp = interp
         cmd.Cmd.__init__(self)
         self.hist = hist
@@ -25,17 +67,54 @@ class REPL(cmd.Cmd, object):
         readline.read_history_file(hist)
         self.lineno = 0
 
+        # create help routines based on doc string.
+        for x, v in REPL.__dict__.iteritems():
+            if x.startswith('do_') and hasattr(v, '__doc__'):
+                def show_doc(d=v.__doc__):
+                    print d
+                setattr(self, 'help_' + x[3:], show_doc)
+
     @property
     def prompt(self):
-        return ':- ' #% self.lineno
+        return ':- '
 
     def do_rules(self, _):
+        """
+        List rules in the program.
+        """
         self.interp.dump_rules()
 
     def do_retract_rule(self, idx):
+        """
+        Retract rule from program by rule index.
+
+        :- a += 1.
+        :- b += 1.
+        :- c += a*b.
+
+        :- rules
+          0: a += 1.
+          1: b += 1.
+          2: c += a * b.
+
+        :- retract_rule 0
+
+        This removes rule 0 from the program. Now, let's inspect the changes to
+        the solution.
+
+        :- sol
+
+        Solution
+        ========
+        b := 1.
+
+        """
         self.interp.retract_rule(int(idx))
 
     def do_exit(self, _):
+        """
+        Exit REPL by typing exit or control-d. See also EOF.
+        """
         readline.write_history_file(self.hist)
         return -1
 
@@ -56,7 +135,10 @@ class REPL(cmd.Cmd, object):
         self.lineno += 1
         return stop
 
-    def do_chart(self, _):
+    def do_sol(self, _):
+        """
+        Show solution.
+        """
         self.interp.dump_charts()
 
     def emptyline(self):
@@ -64,34 +146,80 @@ class REPL(cmd.Cmd, object):
         pass
 
     def do_ip(self, _):
+        """
+        Development tool. Jump into an interactive python shell.
+        """
         ip()
 
     def do_debug(self, line):
+        """
+        Development tool. Used for view Dyna's intermediate representations.
+        """
         with file(dotdynadir / 'repl-debug-line.dyna', 'wb') as f:
             f.write(line)
         debug.main(f.name)
 
-    def do_query(self, line):
-
-        if line.endswith('.'):
+    def _query(self, q):
+        if q.endswith('.'):
             print "Queries don't end with a dot."
             return
-
-        query = 'out(%s) dict= %s.' % (self.lineno, line)
-
-        self.default(query)
-
+        query = '$out(%s) dict= %s.' % (self.lineno, q)
+        self.default(query, show_changed=False)
         try:
-            [(_, _, results)] = self.interp.chart['out/1'][self.lineno,:]
+            [(_, _, results)] = self.interp.chart['$out/1'][self.lineno,:]
         except ValueError:
+            return []
+        return results
+
+    def do_vquery(self, q):
+        """
+        See query.
+        """
+        results = self._query(q)
+        if results is None:
+            return
+        if len(results) == 0:
             print 'No results.'
             return
-
         for val, bindings in results:
-            print '   ', val, 'when', bindings
+            print '   ', _repr(val), 'when', drepr(dict(bindings))
+        print
+
+    def do_query(self, q):
+        """
+        Query solution.
+
+        Consider the following example;
+
+          :- f(1) := 1.
+          :- f(2) := 4.
+
+        There a few versions of query:
+
+         - `vquery` shows variable bindings
+
+            :- vquery f(X)
+                1 when {X=1}
+                4 when {X=1}
+
+         - `query` shows variable bindings applied to query
+
+            :- query f(X)
+                1 is f(1)
+                4 is f(2)
+
+        """
+        results = self._query(q)
+        if results is None:
+            return
+        if len(results) == 0:
+            print 'No results.'
+            return
+        for term, result in sorted((subst(q, dict(result.variables)), result) for result in results):
+            print '   ', _repr(result.value), 'is', term
         print
 
-    def default(self, line):
+    def default(self, line, show_changed=True):
         """
         Called on an input line when the command prefix is not recognized.  In
         that case we execute the line as Python code.
@@ -110,7 +238,8 @@ class REPL(cmd.Cmd, object):
             print '> new rule(s) were not added to program.'
             print
         else:
-            self._changed(changed)
+            if show_changed:
+                self._changed(changed)
 
     def _changed(self, changed):
         if not changed:
@@ -118,6 +247,20 @@ class REPL(cmd.Cmd, object):
         print '============='
         for x, v in sorted(changed.items()):
             print '%s := %s' % (x, _repr(v))
+
+    def _changed_subscriptions(self, changed):
+
+        # TODO: this doesn't show changes - it redumps everything.
+
+        if not changed:
+            return
+        for x, _ in sorted(changed.items()):
+            if x.fn == '$subscribed/2':
+                [i, q] = x.args
+                if x.value:
+                    print '%s: %s' % (i, q)
+                    for result in x.value:
+                        print ' ', _repr(result.value), 'when', drepr(dict(result.variables))
         print
         self.interp.dump_errors()
 
@@ -133,33 +276,114 @@ class REPL(cmd.Cmd, object):
             readline.write_history_file(self.hist)
 
     def do_subscribe(self, line):
+        """
+        Establish a subscription to the results of a query.
+
+        For example,
+
+            :- subscribe f(X,X)
+            :- f(1,1) := 1. f(1,2) := 2. f(2,2) := 3.
+            Changes
+            =======
+            f(X,X):
+                1 when {X=1}
+
+        To view all subscriptions:
+
+            :- subscriptions
+            f(X):
+               1 when {X=1}
+               2 when {X=2}
+
+        """
         if line.endswith('.'):
             print "Queries don't end with a dot."
             return
         # subscriptions are maintained via forward chaining.
-        query = 'subscribed(%s, %s) dict= %s.' % (self.lineno, _repr(line), line)
+        query = '$subscribed(%s, %s) dict= %s.' % (self.lineno, _repr(line), line)
         self.default(query)
 
     def do_subscriptions(self, _):
-        for (_, [_, q], answers) in self.interp.chart['subscribed/2'][:,:,:]:
-            print
-            print q
-            for [value, vs] in answers:
-                print '  %s when {%s}' \
-                    % (value, ', '.join('%s=%s' % (k, _repr(v)) for k,v in vs.items()))
+        "List subscriptions. See subscribe."
+        for (_, [_, q], results) in self.interp.chart['$subscribed/2'][:,:,:]:
+            if results:
+                print q
+                for result in results:
+                    print ' ', _repr(result.value), 'when', drepr(dict(result.variables))
         print
 
+    def do_help(self, line):
+        mod = line.split()
+        if len(mod) <= 1:
+            return super(REPL, self).do_help(line)
+        else:
+            if len(mod) == 2:
+                [cmd, sub] = mod
+                if cmd in ('load', 'post'):
+                    try:
+                        print getattr(globals()[cmd], sub).__doc__
+                    except (KeyError, AttributeError):
+                        print 'No help available for "%s %s"' % (cmd, sub)
+                        return
+                    else:
+                        return
+        print 'Error: Did not understand help command.'
+
     def do_load(self, line):
+        """
+        Execute load command.
+
+        Available loaders:
+
+            {loaders}
+
+        For more information about a particular loader type the following (in
+        this case we get help for the `tsv` loader):
+
+            :- help load tsv
+
+        Examples:
+
+        :- load data = tsv("examples/data/data.csv", delim=',')
+        :- sol
+        Solution
+        ========
+        data/3
+        ======
+        data(2,"cow","boy")            := true
+
+        data/4
+        ======
+        data(0,"a","b","3.0")          := true
+        data(1,"c","d","4.0")          := true
+
+        """
         try:
             load.run(self.interp, line)
-#            self.interp.dump_charts()
         except:
             show_traceback()
             readline.write_history_file(self.hist)
 
+    do_load.__doc__ = do_load.__doc__.format(loaders=', '.join(x for x in dir(load) if not x.startswith('_')))
+
     def do_post(self, line):
+        """
+        Execute post-processor.
+
+        Available post-processors:
+
+            {post}
+
+        For more information about a particular post processor (in this case
+        `save`)
+
+            :- help post save
+
+        """
         try:
             post.run(self.interp, line)
         except:
             show_traceback()
             readline.write_history_file(self.hist)
+
+    do_post.__doc__ = do_post.__doc__.format(post=', '.join(x for x in dir(post) if not x.startswith('_')))
index 143f9ffc5661630e19732a4f91064f6178b2cbb5..a57130160ef2df6e5b08116ddcfc14c7580b3b03 100644 (file)
@@ -1,6 +1,5 @@
 :- :- =============
 a := 1
-
 :- DynaCompilerError:
 FATAL: Encountered error in input program:
  Conflicting aggregators; rule /home/timv/.dyna/tmp/966093dc38b755a6f17b02774b5c656931163a3a.dyna:5:1-/home/timv/.dyna/tmp/966093dc38b755a6f17b02774b5c656931163a3a.dyna:5:3
index ae5380e51846067f7dbfba9ce2cb63b26224d4d6..0599ae37959647b3a9d83d922c9ed1595fa3c22d 100644 (file)
@@ -1,13 +1,11 @@
 :- :- :-   0: a += b * c.
 :- =============
 b := 2
-
 :-   0: a += b * c.
   1: b := 2.
 :- =============
 a := 6
 c := 3
-
 :-   0: a += b * c.
   1: b := 2.
   2: c := 3.
index cacff9abb2afc0c9f8786ed978a4e94bf21a8694..25e039a453e8675df586d1906724390fc35864e2 100755 (executable)
@@ -5,10 +5,10 @@ a += 1.
 b += 1.
 a += 1.
 rules
-chart
+sol
 retract_rule 0
 retract_rule 1
-chart" |./dyna > $0.out
+sol" |./dyna > $0.out
 
 diff $0.expect $0.out && echo pass
 
index c4c5012036a72535a80d8f1c1abeb62ff71ff9f6..bfc109b63a47014ee17c452e7d242ba3719dcaed 100644 (file)
@@ -1,35 +1,21 @@
 :- :- =============
 a := 1
-
 :- =============
 b := 1
-
 :- =============
 a := 2
-
 :-   0: a += 1.
   1: b += 1.
   2: a += 1.
 :- 
-Charts
-============
-a/0
-=================
-a                              := 2
-
-b/0
-=================
-b                              := 1
+Solution
+========
+a := 2
+b := 1
 
 :- :- :- 
-Charts
-============
-a/0
-=================
-a                              := 1
-
-b/0
-=================
-
+Solution
+========
+a := 1
 
 :- exit