]> hydra-www.ietfng.org Git - dyna2/commitdiff
Overhaul pipeline and self-tests (╯°□°)╯︵ ┻━┻
authorNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Thu, 20 Dec 2012 05:06:55 +0000 (00:06 -0500)
committerNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Thu, 20 Dec 2012 05:35:31 +0000 (00:35 -0500)
This moves us to the much more pleasant position of having one main driver
(in Dyna.Main.Driver) which runs the (common) analysis workers and then
hands off to a user-specified backend (though the interface of
Dyna.Main.BackendDefn).  Along the way, we can dump (to stderr or to files)
things we might care about.

Move the python interpreter and debugging tool over to the new pipeline
driver.  While doing that, add a "ghcbuild" target to the Makefile that may
behave better than the cabal-istic build mechanism for older GHCs than the
one on my machine.

Add a Python backend selftest module which compares us against "golden"
files in examples/expected/; they are named ".py.out" on the chance that we
get similar golden files for other backends eventually.  Wire this in to the
main test harness on the "simple" and "papa2" examples.

While here, move the test harness out of Dyna.Test to Dyna.Main.TestsDriver,
update the README, and move it to Markdown.

┬──┬ ノ( ゜-゜ノ)

20 files changed:
.gitignore
Makefile
README [deleted file]
README.md [new file with mode: 0644]
bin/interpreter.py
bin/prototype.py
bin/utils.py
dyna.cabal
examples/expected/papa2.py.out [new file with mode: 0644]
examples/expected/simple.py.out [new file with mode: 0644]
src/Dyna/Analysis/RuleMode.hs
src/Dyna/Backend/Debugging.hs [deleted file]
src/Dyna/Backend/Python.hs
src/Dyna/Backend/Python/Selftest.hs [new file with mode: 0644]
src/Dyna/Main/BackendDefn.hs [new file with mode: 0644]
src/Dyna/Main/Driver.hs [new file with mode: 0644]
src/Dyna/Main/Exception.hs
src/Dyna/Main/TestsDriver.hs [new file with mode: 0644]
src/Dyna/Test/Main.hs [deleted file]
src/Dyna/Test/Main2.hs [deleted file]

index fca079062864ae6cde134a1d13362873dec96f4b..d0cef4988c0d143c3f02810a7fff7463bf6b8688 100644 (file)
@@ -2,6 +2,4 @@
 *.o
 *.pyc
 
-examples/*.plan
-examples/*.anf
-examples/*.d
+dist/
index b7931322b546fd8a0e10d2aa56b2b1160d78399b..164707e98899591c8459e99aec46f0ed126d00a8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,5 +16,31 @@ build: deps
        cabal build
        cabal test
 
+# If the cabal file doesn't do the right thing, this tries to work through
+# it all by hand.  Blech!  But it's better than nothing.
+ghcbuild:
+       mkdir -p dist/build/dyna/
+       mkdir -p dist/build/dyna/dyna-tmp
+       ghc --make -isrc                \
+               -o         dist/build/dyna/dyna     \
+               -outputdir dist/build/dyna/dyna-tmp \
+               -main-is Dyna.Main.Driver Dyna.Main.Driver
+       
+       mkdir -p dist/build/dyna-selftests
+       mkdir -p dist/build/dyna-selftests/dyna-selftests-tmp
+       ghc --make -isrc                          \
+               -o         dist/build/dyna-selftests/dyna-selftests     \
+               -outputdir dist/build/dyna-selftests/dyna-selftests-tmp \
+               -main-is Dyna.Main.TestsDriver Dyna.Main.TestsDriver
+       
+tests:
+       dist/build/dyna-selftests/dyna-selftests
+
 run-parser:
        ghci -isrc Dyna.ParserHS.Parser
+
+.PHONY: clean veryclean
+clean:
+       rm -rf examples/*.dyna.plan examples/*.dyna.d
+veryclean: clean
+       rm -rf dist
diff --git a/README b/README
deleted file mode 100644 (file)
index dd5466a..0000000
--- a/README
+++ /dev/null
@@ -1,77 +0,0 @@
-An overview of the source tree
-------------------------------
-
-external/
-  damsl-k3         -- The DAMSL K3 tree, tracked as a git submodule.
-
-  ekmett-parsers   -- ekmett's parsers combinator library, tracked
-                   -- as a git submodule.
-
-  ekmett-trifecta  -- ekmett's trifecta parser combinator library,
-                   -- tracked as a git submodule.
-
-src/Dyna/
-
-  BackendK3        -- An AST and printer for K3,
-                   -- done in finally-tagless style.
-                   -- Includes some "Examples", even if not self-tests.
-
-  Backend          -- Compilation to target languages
-
-  Main             -- Dyna compiler main modules
-
-  ParserHS         -- the Haskell front-end parser and selftests
-
-  Term             -- Different representations of terms and
-                   -- utilities
-
-  Test             -- code used by self-tests throughout the codebase
-                   -- and the top-level test harness itself
-
-  XXX              -- code that should probably go upstream;
-                   -- modules here are named by the upstream package.
-
-
-Building
---------
-
-First, ensure that you have GHC 7.6 or later.  (Though in a pinch, if you're
-only interested in the frontend stuff, apparently as early as 7.0 continues
-to be servicable.)
-
-Then, sadly, I have to ask you to build some upstream packages out of their
-repositories.  I thought they were going to be released "soon" when I
-switched to these later versions, but it hasn't happened yet:
-
-make upstream
-
-Build K3, which requires OCaml:
-
-git submodule update external/damsl-k3
-(cd external/damsl-k3; make)
-
-Then fetch, build, and install any dependencies
-
-make deps
-
-Build Dyna:
-
-make build
-
-And then run the REPL:
-
-./dist/build/drepl/drepl
-
-OK, that last bit is probably not quite true.  At this point, the code is
-still rather "in the works" so you probably want to load some module in
-GHCi; for example:
-
-ghci -isrc Dyna.ParserHS.Parser
-
-Disclaimer
-----------
-
-This may someday be useful.  For the moment, it doesn't do much except keep
-us busy.  If you're trying to make it do something and it breaks, you get to
-keep all the pieces; see sections 15 - 17 of the AGPLv3 (available in
-LICENSE).
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..80d5a2b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,79 @@
+An overview of the source tree
+------------------------------
+
+* examples/            -- Dyna source programs
+    *  expected/       -- Expected output for self-tests.  Named by program and backend, both.
+* external/
+    * damsl-k3         -- The DAMSL K3 tree, tracked as a git submodule.
+    * ekmett-parsers   -- ekmett's parsers combinator library, tracked as a git submodule.
+    * ekmett-trifecta  -- ekmett's trifecta parser combinator library, tracked as a git submodule.
+* src/Dyna/
+    * Backend
+        * K3           -- An AST and printer for K3, done in finally-tagless style.
+        * Python       -- A Python code generator
+    * Backend          -- Compilation to target languages
+    * Main             -- Dyna compiler drivers (main and test) and definitions used throughout the pipeline
+    * ParserHS         -- the Haskell front-end parser and selftests
+    * Term             -- Different representations of terms and utilities
+    * XXX              -- code that should probably go upstream; modules here are named by the upstream package.
+
+Building
+--------
+
+First, ensure that you have GHC 7.6 or later.  (Though in a pinch, if you're
+only interested in the frontend stuff and the Python backend, apparently as
+early as 7.0 continues to be servicable.)
+
+Then, sadly, I have to ask you to build some upstream packages out of their
+repositories.  I thought they were going to be released "soon" when I
+switched to these later versions, but it hasn't happened yet:
+
+    make upstream
+
+Build K3, if that's your thing, which requires OCaml:
+
+    git submodule update external/damsl-k3
+    (cd external/damsl-k3; make)
+
+Then fetch, build, and install any dependencies
+
+    make deps
+
+Build Dyna:
+
+    make build
+
+(If that doesn't work, you might try `make ghcbuild` which ignores the Cabal
+infrastructure.)
+
+Run the test harness:
+
+    make tests
+
+And then run the REPL:
+
+    ./dist/build/drepl/drepl
+
+OK, that last bit is probably not quite true.  At this point, the code is
+still rather "in the works" so you probably want to...
+
+* load some module in GHCi; for example:
+
+        ghci -isrc Dyna.ParserHS.Parser
+
+* Run the python backend interactively (leave off the "-i" for bulk
+operation):
+
+        ./dyna -i examples/papa2.dyna
+
+* Produce visualizations of some of the internal stages of our compiler
+
+        ./debug examples/papa2.dyna
+
+Disclaimer
+----------
+
+This may someday be useful.  For the moment, it doesn't do much except keep
+us busy.  If you're trying to make it do something and it breaks, you get to
+keep all the pieces; see sections 15 - 17 of the AGPLv3 (available in
+LICENSE).
index fd92b06a6d5543f1cebe42ee75b0b137eea1e40a..72c92a0615408d9722a5b4049be830146a3cca6e 100644 (file)
@@ -305,14 +305,15 @@ def go():
     except KeyboardInterrupt:
         pass
     finally:
-        dump_charts()
         if argv.output is not None:
-            with file(argv.output, 'wb') as f:
-                dump_charts(f)
+            if argv.output == "-": dump_charts(sys.stdout)
+            else:
+                with file(argv.output, 'wb') as f: dump_charts(f)
+        else: dump_charts()
 
 
 def dynac(f):
-    cmd = """ghc -isrc Dyna.Backend.Python -e 'processFile "%s"' """ % f
+    cmd = """dist/build/dyna/dyna -B python -o "%s".plan "%s" """ % (f,f)
     assert 0 == os.system(cmd), 'command failed:\n\t' + cmd
     return f + '.plan'
 
index d8a08e166acdf7c4c27df6faaea154dc20c013e1..1580c9b69a4b2a75301c2e31ad9089e93d392f3e 100644 (file)
@@ -296,7 +296,7 @@ function selectline(lineno) {
 
         print >> html, '<h2>Update plans</h2>'
 
-        cmd = """ghc -isrc Dyna.Backend.Python -e 'processFile "%s"' """ % dynafile
+        cmd = """dist/build/dyna/dyna -B python -o "%s".plan "%s" """ % (dynafile,dynafile)
         if 0 != os.system(cmd):
             print 'command failed:\n\t' + cmd
             os.system('gnome-open %s 2>/dev/null >/dev/null' % html.name)
index 0170cd440114ce1c504c429719c34c97d543fedb..f5363cf7eeacb1b5d58de3fb101b74f1fe0eba0f 100644 (file)
@@ -12,7 +12,7 @@ def toANF(code, f='/tmp/tmp.dyna'):
     with file(f, 'wb') as tmp:
         tmp.write(code)
     os.system('rm -f %s.anf' % f)  # clean up any existing ANF output
-    assert 0 == os.system("""ghc -isrc Dyna.Backend.Debugging -e 'normalizeFile "%s"' """ % f), \
+    assert 0 == os.system("""dist/build/dyna/dyna --dump-anf="%s".anf --backend=none \"%s\" """ % (f,f)), \
         'failed to convert file.'
     with file('%s.anf' % f) as h:
         return h.read()
index 5553cb1996a949bbddbcb198d0b278ea59643ce8..231106980cbbb259039e5bc5422bf7e5c187c0bc 100644 (file)
@@ -57,11 +57,11 @@ Library
 
 Executable drepl
     Default-Language:   Haskell2010
+    Hs-Source-Dirs:     src
 
     ghc-options:        -Wall
                         -main-is Dyna.REPL
 
-    Hs-Source-Dirs:     src
     Build-Depends:      base >=4,
                         bytestring >=0.9,
                         charset >=0.3,
@@ -81,13 +81,42 @@ Executable drepl
     
     Main-Is: Dyna/REPL.hs
 
+Executable dyna
+
+    Default-Language:   Haskell2010
+    Hs-Source-Dirs:     src
+
+    ghc-options:        -Wall
+                        -main-is Dyna.Main.Driver
+
+    Build-Depends:      base >=4,
+                        bytestring >=0.9,
+                        charset >=0.3,
+                        containers >=0.4,
+                        haskeline >=0.6,
+                        HUnit >=1.2,
+                        mtl >=2.1,
+                        parsers >=0.2,
+                        process >=1.1,
+                        reducers >=3.0,
+                        semigroups >=0.8,
+                        tagged >= 0.4.4,
+                        trifecta >=0.90,
+                        unification-fd,
+                        unordered-containers>=0.2,
+                        utf8-string >=0.3,
+                        wl-pprint-extras >=3.0,
+                        wl-pprint-terminfo >=3.0
+
+    Main-Is: Dyna/Main/Driver.hs
+
 Test-suite dyna-selftests
     type:               exitcode-stdio-1.0
     Default-Language:   Haskell2010
     Hs-Source-Dirs:     src
 
     ghc-options:        -Wall
-                        -main-is Dyna.Test.Main
+                        -main-is Dyna.Main.TestsDriver
 
     Build-Depends:      base >=4,
                         bytestring >=0.9,
@@ -97,6 +126,7 @@ Test-suite dyna-selftests
                         HUnit >=1.2,
                         mtl >=2.1,
                         parsers >=0.2,
+                        process >=1.1,
                         reducers >=3.0,
                         semigroups >=0.8,
                         tagged >= 0.4.4,
@@ -104,6 +134,7 @@ Test-suite dyna-selftests
                         test-framework >=0.6,
                         test-framework-hunit >=0.2,
                         test-framework-th >=0.2,
+                        test-framework-golden >= 1.1,
                         trifecta >=0.90,
                         unification-fd,
                         unordered-containers>=0.2,
@@ -112,4 +143,4 @@ Test-suite dyna-selftests
 
     Other-Modules:      Dyna.Backend.K3.Examples
 
-    Main-Is: Dyna/Test/Main.hs
+    Main-Is: Dyna/Main/TestsDriver.hs
diff --git a/examples/expected/papa2.py.out b/examples/expected/papa2.py.out
new file mode 100644 (file)
index 0000000..424292a
--- /dev/null
@@ -0,0 +1,97 @@
+
+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
+
+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
+phrase('N',3,4,t('N','caviar')) := 1
+phrase('N',6,7,t('N','spoon')) := 1
+phrase('NP',0,1,t('NP','Papa')) := 1
+phrase('NP',2,4,t('NP',t('Det','the'),t('N','caviar'))) := 1
+phrase('NP',2,7,t('NP',t('NP',t('Det','the'),t('N','caviar')),t('PP',t('P','with'),t('NP',t('Det','a'),t('N','spoon'))))) := 1
+phrase('NP',5,7,t('NP',t('Det','a'),t('N','spoon'))) := 1
+phrase('P',4,5,t('P','with'))  := 1
+phrase('PP',4,7,t('PP',t('P','with'),t('NP',t('Det','a'),t('N','spoon')))) := 1
+phrase('Papa',0,1,'Papa')      := 1
+phrase('S',0,4,t('S',t('NP','Papa'),t('VP',t('V','ate'),t('NP',t('Det','the'),t('N','caviar'))))) := 1
+phrase('S',0,7,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
+phrase('S',0,7,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
+phrase('S',0,8,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
+phrase('S',0,8,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
+phrase('V',1,2,t('V','ate'))   := 1
+phrase('VP',1,4,t('VP',t('V','ate'),t('NP',t('Det','the'),t('N','caviar')))) := 1
+phrase('VP',1,7,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
+phrase('VP',1,7,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
+phrase('a',5,6,'a')            := 1
+phrase('ate',1,2,'ate')        := 1
+phrase('caviar',3,4,'caviar')  := 1
+phrase('spoon',6,7,'spoon')    := 1
+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
+rewrite('N','spoon')           := 1
+rewrite('NP','Papa')           := 1
+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
+rewrite('S','NP','VP')         := 1
+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
+word('ate',1)                  := True
+word('caviar',3)               := True
+word('spoon',6)                := True
+word('the',2)                  := True
+word('with',4)                 := True
+
diff --git a/examples/expected/simple.py.out b/examples/expected/simple.py.out
new file mode 100644 (file)
index 0000000..b8b99a9
--- /dev/null
@@ -0,0 +1,15 @@
+
+Charts
+============
+a/0
+=================
+a                              := True
+
+b/0
+=================
+b                              := True
+
+c/0
+=================
+c                              := True
+
index 41e083101ef7c61a57f23c1028493ba23a002247..9c0a89411f25aa3a58921261a7fe88c9f33fea3e 100644 (file)
@@ -15,7 +15,10 @@ module Dyna.Analysis.RuleMode (
 
     Crux(..),
 
-    Action, Cost, Det(..), planInitializer, planEachEval,
+    Action, Cost, Det(..), planInitializer,
+    BackendPossible, planEachEval,
+
+    EvalMap, combinePlans,
 
     adornedQueries
 ) where
@@ -33,8 +36,10 @@ import           Dyna.Analysis.Base
 import           Dyna.Term.TTerm
 import           Dyna.Main.Exception
 import qualified Dyna.ParserHS.Parser       as DP
-import           Dyna.XXX.DataUtils(argmin)
+import           Dyna.XXX.DataUtils(argmin,mapInOrApp)
+import           Dyna.XXX.Trifecta (prettySpanLoc)
 import           Dyna.XXX.TrifectaTest
+import           Text.PrettyPrint.Free
 
 ------------------------------------------------------------------------}}}
 -- Modes                                                                {{{
@@ -325,17 +330,20 @@ stepAgenda st sc mic = go [] . (\x -> [x])
   go [] []     = []
   go (r:rs) [] = go rs r
   go rs (p:ps) = case stepPartialPlan st sc mic p of
-                    Left df -> df : (go rs ps)
+                    Left df ->   (\(c,a) -> (c,fmap (\(_,x,y) -> (x,y)) mic,a)) df 
+                               : (go rs ps)
                     Right ps' -> go (ps':rs) ps
 
 -- XXX we're going to need to initially plan a unification crux as part of
 -- backward chaining, but we don't yet.
-initializeForCrux :: (Crux DVar a, DVar, DVar)
+initializeForCrux :: (Crux DVar a)
                   -> ((DFunctAr, DVar, DVar), Action fbs)
-initializeForCrux (cr, hi, v) = case cr of
-  CFCall o is f -> ( ((f,length is), hi, o)
-                   , [ OPPeel is hi f, OPAsgn o (NTVar v) ])
+initializeForCrux cr = case cr of
+  CFCall o is f -> ( ((f,length is), _hi, o)
+                   , [ OPPeel is _hi f ])
   _             -> dynacSorry "Don't know how to initially plan !CFCall"
+ where
+  _hi = "_i"
 
 -- | Given a normalized form and an initial crux, saturate the graph and
 --   get a plan for doing so.
@@ -344,17 +352,14 @@ initializeForCrux (cr, hi, v) = case cr of
 plan_ :: Possible fbs                                -- ^ Available steps
       -> (PartialPlan fbs -> Action fbs -> Cost)     -- ^ Scoring function
       -> ANFState                                    -- ^ Normal form
-      -> Maybe (Crux DVar NTV, DVar, DVar)           -- ^ Initial crux,
-                                                     --   item intern, and
-                                                     --   value, if any.
-      -> [(Cost, Action fbs)]                        -- ^ If there's a plan...
+      -> Maybe (Crux DVar NTV)                       -- ^ Initial crux
+      -> [(Cost, Maybe (DVar,DVar), Action fbs)]     -- ^ If there's a plan...
 plan_ st sc anf mi =
   let cruxes =    eval_cruxes anf
                ++ unif_cruxes anf
       (mic,ip) = maybe (Nothing, []) (first Just . initializeForCrux) mi
-      initPlan = PP { pp_cruxes = maybe id (\(c,_,_) -> S.delete c) mi
-                                  $ S.fromList cruxes
-                    , pp_binds  = maybe S.empty (\(c,_,_) -> cruxVars c) mi
+      initPlan = PP { pp_cruxes = maybe id S.delete mi $ S.fromList cruxes
+                    , pp_binds  = maybe S.empty cruxVars mi
                     , pp_restrictSearch = False
                     , pp_score  = 0
                     , pp_plan   = ip
@@ -364,29 +369,63 @@ plan_ st sc anf mi =
 plan :: Possible fbs
      -> (PartialPlan fbs -> Action fbs -> Cost)
      -> ANFState
-     -> Maybe (Crux DVar NTV, DVar, DVar)
-     -> Maybe (Cost, Action fbs)
+     -> Maybe (Crux DVar NTV)
+     -> Maybe (Cost, Maybe (DVar,DVar), Action fbs)
 plan st sc anf mi =
   (\x -> case x of
                 [] -> Nothing
-                plans -> Just $ argmin fst plans)
+                plans -> Just $ argmin (\(c,_,_) -> c) plans)
   $ plan_ st sc anf mi
 
-planInitializer :: BackendPossible fbs -> Rule -> Maybe (Cost,Action fbs)
-planInitializer bp (Rule { r_anf = anf }) = plan (possible bp)
-                                                 simpleCost anf Nothing
-
-planEachEval :: BackendPossible fbs
-             -> S.Set DFunctAr
-             -> DVar -> DVar -> Rule -> [(DFunctAr, Maybe (Cost,Action fbs))]
-planEachEval bp cs hi v (Rule { r_anf = anf })  =
-  map (\(c,fa) -> (fa, plan (possible bp) simpleCost anf $ Just (c,hi,v)))
+planInitializer :: BackendPossible fbs -> Rule -> Maybe (Cost, Action fbs)
+planInitializer bp (Rule { r_anf = anf }) =
+  fmap (\(c,m,a) -> case m of
+                      Nothing -> (c,a)
+                      Just _ -> dynacPanic "Initializer wants input variables?")
+  $ plan (possible bp) simpleCost anf Nothing
+
+planEachEval :: BackendPossible fbs     -- ^ The backend's primitive support
+             -> (DFunctAr -> Bool)      -- ^ Indicator for constant function
+             -> Rule
+             -> [(DFunctAr, Maybe (Cost, Maybe (DVar, DVar), Action fbs))]
+planEachEval bp cs (Rule { r_anf = anf })  =
+  map (\(c,fa) -> (fa, plan (possible bp) simpleCost anf $ Just c))
     $ MA.mapMaybe (\c -> case c of
-                           CFCall _ is f | S.notMember (f,length is) cs
+                           CFCall _ is f | cs (f,length is)
                                          -> Just $ (c,(f,length is))
                            _             -> Nothing )
     $ eval_cruxes anf
 
+------------------------------------------------------------------------}}}
+-- Plan combination                                                     {{{
+
+type EvalMap fbs = M.Map DFunctAr [(Rule, Cost, Maybe (DVar,DVar), Action fbs)]
+
+-- | Return all plans for each functor/arity
+--
+-- XXX This may still belong elsewhere.
+--
+-- XXX This guy wants span information; he's got it now use it.
+--
+-- timv: might want to fuse these into one circuit
+--
+combinePlans :: [(Rule,[(DFunctAr, Maybe (Cost, Maybe (DVar, DVar), Action fbs))])]
+             -> EvalMap fbs  
+combinePlans = go (M.empty)
+ where
+  go m []             = m
+  go m ((fr,cmca):xs) = go' xs fr cmca m
+
+  go' xs _  []           m = go m xs
+  go' xs fr ((fa,mca):ys) m =
+    case mca of
+      Nothing -> dynacUserErr
+                       $ "No update plan for "
+                          <+> (pretty fa)
+                          <+> "in rule at"
+                          <+> (prettySpanLoc $ r_span fr)
+      Just (c,mv,a) -> go' xs fr ys $ mapInOrApp fa (fr,c,mv,a) m
+
 ------------------------------------------------------------------------}}}
 -- Adorned Queries                                                      {{{
 
diff --git a/src/Dyna/Backend/Debugging.hs b/src/Dyna/Backend/Debugging.hs
deleted file mode 100644 (file)
index 2c4e176..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
----------------------------------------------------------------------------
--- | A variety of debugging backends.
---
--- XXX Eventually, these may want such things kicked on by flags rather
--- than invoked directly.
-
--- Header material                                                      {{{
-module Dyna.Backend.Debugging where
-
-import           Control.Applicative ((<*))
-import           Control.Exception
-import           Dyna.Analysis.ANF
-import qualified Dyna.ParserHS.Parser                as P
-import           System.IO
-import           Text.PrettyPrint.Free               as PP
-import qualified Text.Trifecta                       as T
-
-------------------------------------------------------------------------}}}
--- File to ANF                                                          {{{
-
--- | Normalize all the rules in a file and emit S-exprs for the ANF
--- normalized form.
---
--- NOTE: This is used by bin/prototype.py
-normalizeFile_ file oh = do
-  pr <- T.parseFromFileEx (P.dlines <* T.eof) file
-  case pr of
-    T.Failure td -> T.display td
-    T.Success rs -> mapM_ (PP.hPutDoc oh)
-                    $ map (\(P.LRule x T.:~ _) -> printANF $ normRule x) rs
-
-normalizeFile i = bracket
-  (openFile (i++".anf") WriteMode)
-  (hClose)
-  $ normalizeFile_ i
-
-normalizeFileStdout file = normalizeFile_ file stdout
-
-------------------------------------------------------------------------}}}
index 897a7c3a893fe101ef6638cce900c0e3aeca1cdc..caebedba4a5b0b5ff1141e910581638f3d21b178 100644 (file)
@@ -1,12 +1,12 @@
 ---------------------------------------------------------------------------
 -- | Compile to Python
 --
--- See bin/stdlib.py
+-- See bin/interpreter.py
 
 -- Header material                                                      {{{
 {-# LANGUAGE OverloadedStrings #-}
 
-module Dyna.Backend.Python where
+module Dyna.Backend.Python (pythonBackend) where
 
 import           Control.Applicative ((<*))
 import qualified Control.Arrow              as A
@@ -26,6 +26,7 @@ import           Dyna.Analysis.ANF
 import           Dyna.Analysis.Base
 import           Dyna.Analysis.Aggregation
 import           Dyna.Analysis.RuleMode
+import           Dyna.Main.BackendDefn
 import           Dyna.Main.Exception
 import           Dyna.Term.TTerm
 import qualified Dyna.ParserHS.Parser       as P
@@ -39,39 +40,10 @@ import qualified Text.Trifecta              as T
 ------------------------------------------------------------------------}}}
 -- DOpAMine Backend Information                                         {{{
 
-constants = S.fromList
-    [ ("+",2)
-    , ("-",2)
-    , ("-",1)    -- unary negation
-    , ("*",2)
-    , ("/",2)
-    , ("^",2)
-    , ("&",2)
-    , ("|",2)
-    , ("%",2)
-    , ("**",2)
-    , ("<",2)
-    , ("<=",2)
-    , (">",2)
-    , (">=",2)
-    , ("!",1)
-    , ("mod",1)
-    , ("abs",1)
-    , ("log",1)
-    , ("exp",1)
-    , ("and",2)
-    , ("or",2)
-    , ("not",1)
-    , ("eval",1)
-    , ("true",0)
-    , ("false",0)
-    , ("null",0)   -- XXX is this right?
-    ]
-
 data PyDopeBS = PDBAsIs
               | PDBRewrite   (([ModedVar],ModedVar) -> [DOpAMine PyDopeBS])
 
-builtin (f,is,o) = case () of
+builtins (f,is,o) = case () of
   _ | all (== MBound) is && S.member (f,length is) constants
     -> case o of
          MFree  -> Right (Det,PDBAsIs)
@@ -100,6 +72,37 @@ builtin (f,is,o) = case () of
   _ | S.member (f,length is) constants  -> Left True
   _ -> Left False
 
+-- XXX This and pycall ought to be merged
+constants :: S.Set (DFunct,Int)
+constants = S.fromList
+    [ ("+",2)
+    , ("-",2)
+    , ("-",1)    -- unary negation
+    , ("*",2)
+    , ("/",2)
+    , ("^",2)
+    , ("&",2)
+    , ("|",2)
+    , ("%",2)
+    , ("**",2)
+    , ("<",2)
+    , ("<=",2)
+    , (">",2)
+    , (">=",2)
+    , ("!",1)
+    , ("mod",1)
+    , ("abs",1)
+    , ("log",1)
+    , ("exp",1)
+    , ("and",2)
+    , ("or",2)
+    , ("not",1)
+    , ("eval",1)
+    , ("true",0)
+    , ("false",0)
+    , ("null",0)   -- XXX is this right?
+    ]
+
 ------------------------------------------------------------------------}}}
 -- DOpAMine Printout                                                    {{{
 
@@ -149,7 +152,6 @@ filterBound = map (\(MF v) -> pretty v) . filter (not.isBound)
 
 functorIndirect table f vs = table <> (brackets $ dquotes $ (pretty f <> "/" <> (text $ show $ length vs)))
 
-
 pycall table f vs = case (f, length vs) of
   (  "*", 2) -> infixOp " * "
   (  "+", 2) -> infixOp " + "
@@ -188,34 +190,6 @@ pycall table f vs = case (f, length vs) of
 
 pf f vs = pretty f <> (tupled $ map pretty vs)
 
-------------------------------------------------------------------------}}}
--- Experimental Detritus                                                {{{
-
--- | Return all plans for each functor/arity
---
--- XXX This belongs elsewhere.
---
--- XXX This guy wants span information; he's got it now use it.
---
--- timv: might want to fuse these into one circuit
---
-combinePlans :: [(Rule,[(DFunctAr, Maybe (Cost,Action fbs))])] ->
-                M.Map DFunctAr [(Rule, Cost, Action fbs)]
-combinePlans = go (M.empty)
- where
-  go m []             = m
-  go m ((fr,cmca):xs) = go' xs fr cmca m
-
-  go' xs _  []           m = go m xs
-  go' xs fr ((fa,mca):ys) m =
-    case mca of
-      Nothing -> throw $ UserProgramError
-                       $ "No update plan for "
-                          <+> (pretty fa)
-                          <+> "in rule at"
-                          <+> (prettySpanLoc $ r_span fr)
-      Just (c,a) -> go' xs fr ys $ mapInOrApp fa (fr,c,a) m
-
 py (f,a) mu (Rule _ h _ _ r span _) dope =
            case mu of
              Just (hv,v) ->
@@ -244,13 +218,12 @@ py (f,a) mu (Rule _ h _ _ r span _) dope =
 
    emit = "emit" <> tupled [pretty h, pretty r]
 
-
 printPlan :: Handle
           -> (DFunct,Int)                    -- ^ Functor & arity
-          -> Maybe (DVar,DVar)               -- ^ if update, input intern & value
-          -> (Rule, Cost, Action PyDopeBS)  -- ^ rule and plan
+             -- | rule, cost, input variables, and plan
+          -> (Rule, Cost, Maybe (DVar, DVar), Action PyDopeBS)
           -> IO ()
-printPlan fh fa mu (r, cost, dope) = do         -- display plan
+printPlan fh fa (r, cost, mu, dope) = do         -- display plan
   hPutStrLn fh $ "# --"
   displayIO fh $ prefixSD "# " $ renderPretty 1.0 100
                  $ (prettySpanLoc $ r_span r) <> line
@@ -259,45 +232,29 @@ printPlan fh fa mu (r, cost, dope) = do         -- display plan
                  $ py fa mu r dope <> line
   hPutStrLn fh ""
 
-processFile fileName = bracket
-  (openFile (fileName ++ ".plan") WriteMode)
-  hClose
-  $ processFile_ fileName
-
-processFileStdout fileName = do
-  processFile_ fileName stdout
-
-processFile_ fileName fh = do
-  pr <- T.parseFromFileEx (P.dlines <* T.eof) fileName
-  case pr of
-    T.Failure td -> T.display td
-    T.Success rs ->
-      let urs = map (\(P.LRule x T.:~ _) -> x) rs
-          frs = map normRule urs
-          initializers = MA.mapMaybe (\(f,mca) -> (\(c,a) -> (f,c,a)) `fmap` mca)
-                         $ map (\x -> (x, planInitializer builtin x)) frs
-      in do
-         aggm <- case buildAggMap frs of
-                   Left e -> throw $ UserProgramError (text e)
-                   Right x -> return x
-
-         hPutStrLn fh $ "agg_decl = {}"
-         forM (M.toList aggm) $ \((f,a),v) -> do {
-             hPutStrLn fh $ show $ "agg_decl" <> brackets (dquotes $ pretty f <> "/" <> pretty a)
-                <+> equals <+> (dquotes $ pretty v)
-           }
-
-         cPlans <- return $! combinePlans                  -- crux plans
-                      $ map (\x -> (x, planEachEval builtin constants headVar valVar x)) frs
-         forM_ (M.toList cPlans) $ \(fa, ps) -> do    -- plans aggregated by functor/arity
-            hPutStrLn fh ""
-            hPutStrLn fh $ "# =============="
-            hPutStrLn fh $ "# " ++ show fa
-            forM_ ps $ printPlan fh fa (Just (headVar,valVar))
-         hPutStrLn fh ""
-         hPutStrLn fh $ "# =============="
-         hPutStrLn fh $ "# Initializers"
-         forM_ initializers $ \(f,c,a) -> printPlan fh (findHeadFA f) Nothing (f,c,a)
+------------------------------------------------------------------------}}}
+-- Driver                                                               {{{
+
+driver am em is fh = do
+  -- Aggregation mapping
+  hPutStrLn fh $ "agg_decl = {}"
+  forM (M.toList am) $ \((f,a),v) -> do
+     hPutStrLn fh $ show $    "agg_decl"
+                           <> brackets (dquotes $ pretty f <> "/" <> pretty a)
+                           <+> equals <+> (dquotes $ pretty v)
+
+  -- plans aggregated by functor/arity
+  forM_ (M.toList em) $ \(fa, ps) -> do
+     hPutStrLn fh ""
+     hPutStrLn fh $ "# =============="
+     hPutStrLn fh $ "# " ++ show fa
+     forM_ ps $ printPlan fh fa
+
+  hPutStrLn fh ""
+  hPutStrLn fh $ "# =============="
+  hPutStrLn fh $ "# Initializers"
+
+  forM_ is $ \(f,c,a) -> printPlan fh (findHeadFA f) (f,c,Nothing,a)
 
  where
   findHeadFA (Rule _ h _ _ _ _ (AS { as_assgn = as })) =
@@ -306,10 +263,9 @@ processFile_ fileName fh = do
       Just (Left _)      -> error "NTVar head?"
       Just (Right (f,a)) -> (f, length a)
 
-  headVar = "_h"
-  valVar  = "_v"
-
+------------------------------------------------------------------------}}}
+-- Export                                                               {{{
 
--- TEST: processFileStdout "examples/cky.dyna"
+pythonBackend = Backend builtins constants driver
 
 ------------------------------------------------------------------------}}}
diff --git a/src/Dyna/Backend/Python/Selftest.hs b/src/Dyna/Backend/Python/Selftest.hs
new file mode 100644 (file)
index 0000000..96a4d46
--- /dev/null
@@ -0,0 +1,68 @@
+---------------------------------------------------------------------------
+-- | Self-tests for the Python backend, mostly by running the generated
+-- code through the interpreter.
+
+-- Header material                                                      {{{
+module Dyna.Backend.Python.Selftest where
+
+import           Control.Exception (throw)
+import qualified Data.ByteString.Lazy                as BL
+import           System.Exit (ExitCode(..))
+import           System.IO
+import           System.Process
+import qualified Test.Framework                      as TF
+import           Test.Golden
+
+------------------------------------------------------------------------}}}
+-- Run Backend                                                          {{{
+
+runDynaPy :: String -> IO BL.ByteString
+runDynaPy f = do
+  devnull <- openFile "/dev/null" ReadWriteMode
+
+  (Nothing,Just so,Nothing,ph) <- createProcess $ CreateProcess
+     { cmdspec = RawCommand "/usr/bin/env"
+                            ["python", "bin/interpreter.py", "-o", "-", f]
+     , cwd = Nothing
+     , env = Nothing
+     , std_in = UseHandle devnull
+     , std_out = CreatePipe
+     , std_err = UseHandle devnull
+     , close_fds = True
+     , create_group = False
+     }
+  bs <- BL.hGetContents so
+  ec <- waitForProcess ph
+  case ec of
+   ExitSuccess -> return bs
+   ExitFailure _ -> throw ec
+
+------------------------------------------------------------------------}}}
+-- Tests                                                                {{{
+
+mkExample :: String -> TF.Test
+mkExample name =
+  let (dy,ex) = names in goldenVsString dy ex (runDynaPy dy)
+ where
+  names = ( "examples/" ++ name ++ ".dyna"
+          , "examples/expected/" ++ name ++ ".py.out")
+
+goldens :: TF.Test
+goldens = TF.testGroup "Python Backend End-To-End"
+          $ map mkExample ["simple", "papa2"]
+
+------------------------------------------------------------------------}}}
+-- Harness toplevel                                                     {{{
+
+selftest :: TF.Test
+selftest = goldens
+
+main :: IO ()
+main = TF.defaultMain [selftest]
+
+-- If you're running from within GHCi and just want to do something quickly,
+-- try
+--
+-- TF.defaultMain [mkExample "simple"]
+
+------------------------------------------------------------------------}}}
diff --git a/src/Dyna/Main/BackendDefn.hs b/src/Dyna/Main/BackendDefn.hs
new file mode 100644 (file)
index 0000000..48a9047
--- /dev/null
@@ -0,0 +1,42 @@
+---------------------------------------------------------------------------
+-- | What does it mean to be a backend?
+
+-- Header material                                                      {{{
+{-# LANGUAGE ExistentialQuantification #-}
+
+module Dyna.Main.BackendDefn where
+
+import qualified Data.Set as S
+import           Dyna.Analysis.Aggregation (AggMap)
+import           Dyna.Analysis.ANF (Rule)
+import           Dyna.Analysis.RuleMode (Action, BackendPossible, Cost, EvalMap)
+import           Dyna.Term.TTerm (DFunctAr)
+import           System.IO (Handle)
+
+-- XXX The notion of be_constants is not quite right, I think?  It is used
+-- only in Dyna.Analysis.RuleMode.planEachEval to avoid generating some
+-- plans, but that's not really how we should be doing it.  The right
+-- answer, of course, is to use update mode information, once we have it.
+
+data Backend = forall bs . Backend
+             { -- | Builtin support hook for mode planning.  Options are
+               --   to return
+               -- 
+               --   * @Left False@  -- This functor is not built in
+               --
+               --   * @Left True@   -- There is no plan for this mode
+               --
+               --   * @Right (d,b)@ -- There is a plan here with determinism
+               --                      @d@ and backend-specific data @b@.
+               be_builtin :: BackendPossible bs
+
+               -- | Any constants made available by this backend.
+             , be_constants :: S.Set DFunctAr
+              
+               -- | Backend driver
+             , be_driver  :: AggMap                  -- ^ Aggregation
+                          -> EvalMap bs              -- ^ Rules
+                          -> [(Rule,Cost,Action bs)] -- ^ Initializers
+                          -> Handle                  -- ^ Output
+                          -> IO ()
+             }
diff --git a/src/Dyna/Main/Driver.hs b/src/Dyna/Main/Driver.hs
new file mode 100644 (file)
index 0000000..064974b
--- /dev/null
@@ -0,0 +1,226 @@
+---------------------------------------------------------------------------
+-- | Main driver of the pipeline
+
+-- Header material                                                      {{{
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE ImplicitParams #-} -- This is probably a terrible idea, but
+                                -- I'd never done it before and wanted to
+                                -- see what it's like.  It won't be hard to
+                                -- rip out.
+{-# LANGUAGE OverloadedStrings #-}
+
+module Dyna.Main.Driver where
+
+import           Control.Applicative ((<*))
+import           Control.Exception
+import           Control.Monad
+import           Data.Char
+import qualified Data.Map                   as M
+import qualified Data.Maybe                 as MA
+import qualified Data.Set                   as S
+import           Dyna.Analysis.Aggregation
+import           Dyna.Analysis.ANF
+import           Dyna.Analysis.RuleMode
+import           Dyna.Backend.Python
+import           Dyna.Main.BackendDefn
+import           Dyna.Main.Exception
+import qualified Dyna.ParserHS.Parser       as P
+import           System.Console.GetOpt
+import           System.Environment
+import           System.Exit
+import           System.IO
+import           Text.PrettyPrint.Free
+import qualified Text.Trifecta              as T
+
+------------------------------------------------------------------------}}}
+-- Dumping                                                              {{{
+
+data DumpType = DumpAgg
+              | DumpANF
+              | DumpParsed
+ deriving (Eq,Ord,Show)
+
+type DumpMap = M.Map DumpType (Maybe FilePath)
+
+dump :: (?dcfg :: DynacConfig) => DumpType -> Doc e -> IO ()
+dump dt doc =
+  case M.lookup dt (dcfg_dumps ?dcfg) of
+    Nothing       -> return ()
+    Just Nothing  -> go True stderr
+    Just (Just f) -> bracket (openFile f WriteMode) hClose (go False)
+ where
+  go h f = hPutDoc f $
+    if h
+     then    header `above` doc <> line <> line
+          <> hcat (replicate 4 bar) <> line
+     else doc
+
+  header = bar <+> fill 18 (text $ show dt) <+> bar
+  bar    = "=========="
+
+anyDumpStderr :: (?dcfg :: DynacConfig) => Bool
+anyDumpStderr = M.foldr (\v r -> r || MA.isNothing v)
+                        False (dcfg_dumps ?dcfg)
+
+------------------------------------------------------------------------}}}
+-- Backend                                                              {{{
+
+noBackend :: Backend
+noBackend = Backend
+          { be_builtin   = \_ -> Left False
+          , be_constants = S.empty
+          , be_driver    = \_ _ _ _ -> hPutStrLn stderr
+                                        "No backend specified; stopping"
+          }
+
+parseBackend :: String -> Backend
+parseBackend s = case map toLower s of
+                   "none" -> noBackend
+                   "python" -> pythonBackend
+                   _ -> dynacThrow $ InvocationError
+                                   $ "Unknown backend:" <+> pretty s
+
+------------------------------------------------------------------------}}}
+-- DynacConfiguration                                                   {{{
+
+data DynacConfig = DynacConfig { dcfg_backend :: Backend
+                               , dcfg_dumps   :: DumpMap
+                               , dcfg_outFile :: Maybe FilePath
+                               }
+
+
+defaultDynacConfig :: DynacConfig
+defaultDynacConfig = DynacConfig
+  { dcfg_backend = noBackend
+  , dcfg_dumps   = M.empty
+  , dcfg_outFile = Nothing
+  }
+
+------------------------------------------------------------------------}}}
+-- Options and Argument Handling                                        {{{
+
+data Opt = OptVersion
+         | OptHelp
+         | OptBackend Backend
+         | OptDump (DumpMap -> DumpMap)
+         | OptOutput  FilePath
+
+
+options :: [OptDescr Opt]
+options =
+  [ Option ['h'] ["help"]    (NoArg  OptHelp)    "display this help message"
+  , Option ['V'] ["version"] (NoArg  OptVersion) "display version and exit"
+  ]
+  ++
+  [ Option ['B'] ["backend"]      (ReqArg obe "BE")
+    "use backend BE"
+  , Option ['o'] ["out","output"] (ReqArg OptOutput "FILE")
+    "write generated output to FILE"
+  ]
+  -- XXX we'd like these to not be documented, at least not by default, but
+  -- that would require patching the getopt library
+  ++ mkDumpOpt "agg"   DumpAgg
+  ++ mkDumpOpt "anf"   DumpANF
+  ++ mkDumpOpt "parse" DumpParsed
+ where
+  obe = OptBackend . parseBackend
+
+  mkDumpOpt arg fl =
+    [ Option [] ["dump-"    ++ arg] (OptArg (OptDump . sfl) "FILE") ""
+    , Option [] ["no-dump-" ++ arg] (NoArg  (OptDump   cfl)       ) ""
+    ]
+   where
+    sfl x fs = M.insert fl x fs
+    cfl   fs = M.delete fl   fs
+
+
+procArgs :: [String] -> IO (DynacConfig, [String])
+procArgs argv = do
+  case getOpt Permute options argv of
+    (os,as,[]) -> case foldOpts os of
+                    Left x -> do
+                               putStrLn "Dyna 0.4"
+                               when x $ putStrLn (usageInfo "" options)
+                               exitSuccess
+                    Right f -> return $ (f defaultDynacConfig, as)
+    (_,_,es)   -> dynacThrow $ InvocationError
+                             $ vcat (map pretty es)
+                                `above`
+                                pretty (usageInfo "" options)
+ where
+  foldOpts :: [Opt] -> Either Bool (DynacConfig -> DynacConfig)
+  foldOpts = go id
+   where
+    go f []                = Right f
+    go _ (OptHelp:_)       = Left True
+    go _ (OptVersion:_)    = Left False
+    go f (OptBackend b:os) = go (setBackend b  . f) os
+    go f (OptDump f':os)   = go (mungeDump  f' . f) os
+    go f (OptOutput fn:os) = go (setOutput fn  . f) os
+
+    setBackend b c = c { dcfg_backend = b }
+    mungeDump  f c = c { dcfg_dumps = f $ dcfg_dumps c }
+    setOutput  o c = c { dcfg_outFile = if o == "-" then Nothing else Just o }
+
+------------------------------------------------------------------------}}}
+-- Pipeline!                                                            {{{
+
+processFile :: (?dcfg :: DynacConfig) => String -> IO ()
+processFile fileName = bracket openOut hClose go
+ where
+  openOut = maybe (return stdout) (flip openFile WriteMode)
+            $ dcfg_outFile ?dcfg
+
+  go out = do
+    rs <- parse
+
+    dump DumpParsed (vcat $ map (text.show) rs)
+   
+    let urs = map (\(P.LRule x T.:~ _) -> x) rs
+        frs = map normRule urs
+
+    dump DumpANF (vcat $ map printANF frs)
+
+    aggm <- case buildAggMap frs of
+              Left e -> dynacThrow $ UserProgramError (text e)
+              Right x -> return x
+
+    dump DumpAgg (M.foldlWithKey (\d f a -> d `above`
+                                    (pretty f <+> colon <+> pretty a))
+                                 empty aggm)
+
+    case dcfg_backend ?dcfg of
+      Backend be_b be_c be_d ->
+        let initializers = MA.mapMaybe
+                             (\(f,mca) -> (\(c,a) -> (f,c,a)) `fmap` mca)
+                           $ map (\x -> (x, planInitializer be_b x)) frs
+   
+  
+            cPlans = combinePlans
+                     $ map (\x -> (x, planEachEval be_b
+                                                   (not . flip S.member be_c) x))
+                           frs
+        in be_d aggm cPlans initializers out
+
+  parse = do
+    pr <- T.parseFromFileEx (P.dlines <* T.eof) fileName
+    case pr of
+      T.Failure td -> dynacUserErr $ align ("Parser error" `above` td)
+      T.Success rs -> return rs
+
+------------------------------------------------------------------------}}}
+-- Main                                                                 {{{
+
+main_ :: [String] -> IO ()
+main_ argv = do
+  (dcfg, fis) <- procArgs argv
+  let ?dcfg = dcfg
+  case fis of
+    []  -> dynacThrow $ InvocationError "Must specify a Dyna file"
+    [x] -> processFile x
+    _   -> dynacSorry "We can't do more than one file"
+
+main :: IO ()
+main = getArgs >>= main_
+
+------------------------------------------------------------------------}}}
index b40ad681531e3bffcea3e34efeabefc3f2866d2f..a21b7001d5f5e8a6aaa1147977605d72a60a58ad 100644 (file)
@@ -21,6 +21,9 @@ data DynacException =
     -- | The user program contains an error
     UserProgramError (PP.Doc TP.Effect)
 
+    -- | Something went wrong when trying to understand arguments
+  | InvocationError (PP.Doc TP.Effect)
+
     -- | We don't implement a feature yet.
   | Sorry            (PP.Doc TP.Effect)
 
@@ -34,9 +37,12 @@ instance Exception DynacException
 ------------------------------------------------------------------------}}}
 -- Utilities                                                            {{{
 
+dynacUserErr, dynacSorry, dynacPanic :: PP.Doc TP.Effect -> a
+dynacUserErr = throw . UserProgramError
+dynacSorry = throw . Sorry
+dynacPanic = throw . Panic
+
 dynacThrow :: DynacException -> a
 dynacThrow = throw
 
-dynacSorry = throw . Sorry
-
 ------------------------------------------------------------------------}}}
diff --git a/src/Dyna/Main/TestsDriver.hs b/src/Dyna/Main/TestsDriver.hs
new file mode 100644 (file)
index 0000000..f3fbef3
--- /dev/null
@@ -0,0 +1,20 @@
+-- Bring together all of our test suites
+
+module Dyna.Main.TestsDriver where
+
+import           Test.Framework
+-- import qualified Dyna.Backend.K3.Selftest     as DBK3S
+import qualified Dyna.Backend.Python.Selftest as DBPS
+import qualified Dyna.ParserHS.Selftest       as DPHS
+import qualified Dyna.XXX.TrifectaTests       as DXT
+
+main :: IO ()
+main = defaultMain
+           [DPHS.selftest
+           , DXT.selftest
+
+           -- XXX Until this is meaningful...
+           -- ,DBK3S.selftest
+
+           , DBPS.selftest
+           ]
diff --git a/src/Dyna/Test/Main.hs b/src/Dyna/Test/Main.hs
deleted file mode 100644 (file)
index a7be1b6..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
--- Bring together all of our test suites
-
-module Dyna.Test.Main where
-
-import           Test.Framework
-import qualified Dyna.Backend.K3.Selftest as DK3S
-import qualified Dyna.ParserHS.Selftest   as DPHS
-import qualified Dyna.XXX.TrifectaTests   as DXT
-
-main :: IO ()
-main = defaultMain
-           [DPHS.selftest
-           -- XXX Until this is meaningful...
-           -- ,DK3S.selftest
-           , DXT.selftest
-           ]
diff --git a/src/Dyna/Test/Main2.hs b/src/Dyna/Test/Main2.hs
deleted file mode 100644 (file)
index acaeb6e..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
--- Bring together all of our test suites
-
--- XXX temporary, use Dyna.Test.Main as soons as timv has upgrades ghc.
-
-module Dyna.Test.Main2 where
-
-import           Test.Framework
-import qualified Dyna.ParserHS.Selftest  as DPHS
-import qualified Dyna.XXX.TrifectaTests  as DXT
-
-main :: IO ()
-main = defaultMain
-           [ DPHS.selftest
-           , DXT.selftest
-           ]