packages feed

exploring-interpreters 1.5.0.1 → 1.6.0.0

raw patch · 10 files changed

+1360/−1351 lines, 10 filesdep ~aesondep ~attoparsecdep ~bytestringsetup-changed

Dependency ranges changed: aeson, attoparsec, bytestring, containers, exceptions, fgl, haskeline, http-types, mtl, network, scientific, text, transformers

Files

CHANGELOG.md view
@@ -1,41 +1,41 @@-# Revision history for exploring-interpreters--## 0.2.0.0 -- 2021-03-15-* First official version.--## 0.3.0.0 -- 2021-03-16-* Require that definitional interpreters return configurations in the Maybe monad.-  This adds support for run-time errors by returning Nothing when an errors occurs.--## 0.3.1.0 -- 2021-04-18-* This version adds the 'leaves' function to the exploring interpreter.--## 0.3.2.0 -- 2021-06-29-* This version adds functionality to support exporting and importing of execution environments.-This functionality is provided via the 'toExport' and 'fromExport' functions.-* Furthermore, this version also exports the initial reference.--## 0.3.2.1 -- 2021-10-01-* Add the toExport and fromExport functions to the exported functions in the pure module.--## 0.4.0.0 -- 2021-10-01-* fromExport function now correctly determines the referece to use for generation of new nodes.--## 1.0.0.0 -- 2021-10-06-* Change explorer model to the new model where the exploration is always reported-by a tree and sharing is possible via the optional shadow graph.-Furthermore, the *jump* action is introduced to allow jumping to any node in the tree without the -destructive property. In addition, the *revert* action is now always destructive and can only operate -on the current trace.-* Add the Tools module.-This module includes an implementation of the exploring interpreter protocol and an implementation-of an language parametric REPL.--## 1.2.0.0 -- 2022-01-13-* Change the readline dependency to haskeline.--## 1.3.0.0 -- 2022-01-13-* Bump and freeze haskeline dependency.--## 1.5.0.0 -- 2024-01-19+# Revision history for exploring-interpreters
+
+## 0.2.0.0 -- 2021-03-15
+* First official version.
+
+## 0.3.0.0 -- 2021-03-16
+* Require that definitional interpreters return configurations in the Maybe monad.
+  This adds support for run-time errors by returning Nothing when an errors occurs.
+
+## 0.3.1.0 -- 2021-04-18
+* This version adds the 'leaves' function to the exploring interpreter.
+
+## 0.3.2.0 -- 2021-06-29
+* This version adds functionality to support exporting and importing of execution environments.
+This functionality is provided via the 'toExport' and 'fromExport' functions.
+* Furthermore, this version also exports the initial reference.
+
+## 0.3.2.1 -- 2021-10-01
+* Add the toExport and fromExport functions to the exported functions in the pure module.
+
+## 0.4.0.0 -- 2021-10-01
+* fromExport function now correctly determines the referece to use for generation of new nodes.
+
+## 1.0.0.0 -- 2021-10-06
+* Change explorer model to the new model where the exploration is always reported
+by a tree and sharing is possible via the optional shadow graph.
+Furthermore, the *jump* action is introduced to allow jumping to any node in the tree without the 
+destructive property. In addition, the *revert* action is now always destructive and can only operate 
+on the current trace.
+* Add the Tools module.
+This module includes an implementation of the exploring interpreter protocol and an implementation
+of an language parametric REPL.
+
+## 1.2.0.0 -- 2022-01-13
+* Change the readline dependency to haskeline.
+
+## 1.3.0.0 -- 2022-01-13
+* Bump and freeze haskeline dependency.
+
+## 1.5.0.0 -- 2024-01-19
 * Bump the text dependency, and bump + bound the aeson dependency.
LICENSE view
@@ -1,30 +1,30 @@-Copyright (c) 2021, Damian Frolich--All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--    * Redistributions of source code must retain the above copyright-      notice, this list of conditions and the following disclaimer.--    * Redistributions in binary form must reproduce the above-      copyright notice, this list of conditions and the following-      disclaimer in the documentation and/or other materials provided-      with the distribution.--    * Neither the name of Damian Frolich nor the names of other-      contributors may be used to endorse or promote products derived-      from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2021, Damian Frolich
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Damian Frolich nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Language/Explorer/Basic.hs view
@@ -1,103 +1,108 @@-{-# LANGUAGE ConstraintKinds #-}--module Language.Explorer.Basic-    ( Explorer-    , mkExplorer-    , mkExplorerNoSharing-    , execute-    , executeAll-    , revert-    , ExplorerM.toTree-    , config-    , currRef-    , Ref-    , deref-    , leaves-    , getTrace-    , getTraces-    , getPathsFromTo-    , getPathFromTo-    , executionGraph-    , fromExport-    , toExport-    ) where--import qualified Language.Explorer.Monadic as ExplorerM-import Control.Monad.Identity--import qualified Data.IntMap as IntMap-import Data.List-import Data.Functor-import Data.Foldable-import Data.Monoid ()-import Data.Graph.Inductive.Graph (emap)---- We shadow instead of exporting directly to make the user interaction--- the same.-type Ref = ExplorerM.Ref-type Explorer a b = ExplorerM.Explorer a Identity b ()-type BasicLanguage p c = Eq p--mkExplorer :: BasicLanguage p c => Bool -> (c -> c -> Bool) -> (p -> c -> Maybe c) -> c -> Explorer p c-mkExplorer shadow eqfunc definterp initialConf = ExplorerM.mkExplorer shadow eqfunc (wrap definterp) initialConf--mkExplorerNoSharing :: BasicLanguage p c => (p -> c -> Maybe c) -> c -> Explorer p c-mkExplorerNoSharing = mkExplorer False (const . const $ False)--currRef :: Explorer a b -> Ref-currRef = ExplorerM.currRef--config :: Explorer a b -> b-config = ExplorerM.config--deref :: Explorer p c -> Ref -> Maybe c-deref = ExplorerM.deref---- This should be able with func composition.-wrap :: Monad m => (a -> b -> Maybe b) -> a -> b -> m (Maybe b, ())-wrap def p e = return $ (def p e, ())--execute :: BasicLanguage p c => p -> Explorer p c -> Explorer p c-execute p e = fst $ runIdentity $ ExplorerM.execute p e--executeAll :: BasicLanguage p c => [p] -> Explorer p c -> Explorer p c-executeAll p e = fst $ runIdentity $ ExplorerM.executeAll p e--revert :: ExplorerM.Ref -> Explorer p c -> Maybe (Explorer p c)-revert = ExplorerM.revert--removeOutput :: ((Ref, c), (p, o), (Ref, c)) -> ((Ref, c), p, (Ref, c))-removeOutput (s, (p, _), t) = (s, p, t)--incomingEdges :: Ref -> Explorer p c -> [((Ref, c), p, (Ref, c))]-incomingEdges r e = map removeOutput $ ExplorerM.incomingEdges r e--getTrace :: Explorer p c -> [((Ref, c), p, (Ref, c))]-getTrace e = map removeOutput $ ExplorerM.getTrace e--getTraces :: Explorer p c -> [[((Ref, c), p, (Ref, c))]]-getTraces e = map (map removeOutput) $ ExplorerM.getTraces e--getPathsFromTo :: Explorer p c -> Ref -> Ref -> [[((Ref, c), p, (Ref, c))]]-getPathsFromTo e s t = map (map removeOutput) $ ExplorerM.getPathsFromTo e s t--getPathFromTo :: Explorer p c -> Ref -> Ref -> [((Ref, c), p, (Ref, c))]-getPathFromTo e s t = map removeOutput $ ExplorerM.getPathFromTo e s t--executionGraph :: Explorer p c -> ((Ref, c), [(Ref, c)], [((Ref, c), p, (Ref, c))])-executionGraph e = (curr, nodes, map removeOutput graph)-  where-    (curr, nodes, graph) = ExplorerM.executionGraph e--leaves :: Explorer p c -> [(Ref, c)]-leaves = ExplorerM.leaves--toExport :: Explorer p c -> (Ref, [(Ref, c)], [(Ref, Ref, p)])-toExport = removeOut . ExplorerM.toExport-  where-    removeOut (c, nodes, edges) = (c, nodes, map (\(s, t, (p, _)) -> (s, t, p)) edges)--fromExport :: Explorer p c -> (Ref, [(Ref, c)], [(Ref, Ref, p)]) -> Explorer p c-fromExport e exported = ExplorerM.fromExport e (addOut exported)-  where+{-# LANGUAGE ConstraintKinds #-}
+
+module Language.Explorer.Basic
+    ( Explorer
+    , mkExplorer
+    , mkExplorerNoSharing
+    , execute
+    , executeAll
+    , revert
+    , jump
+    , ExplorerM.toTree
+    , incomingEdges
+    , config
+    , currRef
+    , Ref
+    , deref
+    , leaves
+    , getTrace
+    , getTraces
+    , getPathsFromTo
+    , getPathFromTo
+    , executionGraph
+    , fromExport
+    , toExport
+    ) where
+
+import qualified Language.Explorer.Monadic as ExplorerM
+import Control.Monad.Identity
+
+import qualified Data.IntMap as IntMap
+import Data.List
+import Data.Functor
+import Data.Foldable
+import Data.Monoid ()
+import Data.Graph.Inductive.Graph (emap)
+
+-- We shadow instead of exporting directly to make the user interaction
+-- the same.
+type Ref = ExplorerM.Ref
+type Explorer a b = ExplorerM.Explorer a Identity b ()
+type BasicLanguage p c = Eq p
+
+mkExplorer :: BasicLanguage p c => Bool -> (c -> c -> Bool) -> (p -> c -> Maybe c) -> c -> Explorer p c
+mkExplorer shadow eqfunc definterp initialConf = ExplorerM.mkExplorer shadow eqfunc (wrap definterp) initialConf
+
+mkExplorerNoSharing :: BasicLanguage p c => (p -> c -> Maybe c) -> c -> Explorer p c
+mkExplorerNoSharing = mkExplorer False (const . const $ False)
+
+currRef :: Explorer a b -> Ref
+currRef = ExplorerM.currRef
+
+config :: Explorer a b -> b
+config = ExplorerM.config
+
+deref :: Explorer p c -> Ref -> Maybe c
+deref = ExplorerM.deref
+
+-- This should be able with func composition.
+wrap :: Monad m => (a -> b -> Maybe b) -> a -> b -> m (Maybe b, ())
+wrap def p e = return $ (def p e, ())
+
+execute :: BasicLanguage p c => p -> Explorer p c -> Explorer p c
+execute p e = fst $ runIdentity $ ExplorerM.execute p e
+
+executeAll :: BasicLanguage p c => [p] -> Explorer p c -> Explorer p c
+executeAll p e = fst $ runIdentity $ ExplorerM.executeAll p e
+
+revert :: ExplorerM.Ref -> Explorer p c -> Maybe (Explorer p c)
+revert = ExplorerM.revert
+
+jump :: ExplorerM.Ref -> Explorer p c -> Maybe (Explorer p c)
+jump = ExplorerM.jump
+
+removeOutput :: ((Ref, c), (p, o), (Ref, c)) -> ((Ref, c), p, (Ref, c))
+removeOutput (s, (p, _), t) = (s, p, t)
+
+incomingEdges :: Ref -> Explorer p c -> [((Ref, c), p, (Ref, c))]
+incomingEdges r e = map removeOutput $ ExplorerM.incomingEdges r e
+
+getTrace :: Explorer p c -> [((Ref, c), p, (Ref, c))]
+getTrace e = map removeOutput $ ExplorerM.getTrace e
+
+getTraces :: Explorer p c -> [[((Ref, c), p, (Ref, c))]]
+getTraces e = map (map removeOutput) $ ExplorerM.getTraces e
+
+getPathsFromTo :: Explorer p c -> Ref -> Ref -> [[((Ref, c), p, (Ref, c))]]
+getPathsFromTo e s t = map (map removeOutput) $ ExplorerM.getPathsFromTo e s t
+
+getPathFromTo :: Explorer p c -> Ref -> Ref -> [((Ref, c), p, (Ref, c))]
+getPathFromTo e s t = map removeOutput $ ExplorerM.getPathFromTo e s t
+
+executionGraph :: Explorer p c -> ((Ref, c), [(Ref, c)], [((Ref, c), p, (Ref, c))])
+executionGraph e = (curr, nodes, map removeOutput graph)
+  where
+    (curr, nodes, graph) = ExplorerM.executionGraph e
+
+leaves :: Explorer p c -> [(Ref, c)]
+leaves = ExplorerM.leaves
+
+toExport :: Explorer p c -> (Ref, [(Ref, c)], [(Ref, Ref, p)])
+toExport = removeOut . ExplorerM.toExport
+  where
+    removeOut (c, nodes, edges) = (c, nodes, map (\(s, t, (p, _)) -> (s, t, p)) edges)
+
+fromExport :: Explorer p c -> (Ref, [(Ref, c)], [(Ref, Ref, p)]) -> Explorer p c
+fromExport e exported = ExplorerM.fromExport e (addOut exported)
+  where
     addOut (c, nodes, edges) = (c, nodes, map (\(s, t, p) -> (s, t, (p, ()))) edges)
Language/Explorer/Monadic.hs view
@@ -1,268 +1,268 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE ConstraintKinds #-}---module Language.Explorer.Monadic-    ( Explorer-    , mkExplorer-    , mkExplorerNoSharing-    , execute-    , executeAll-    , revert-    , jump-    , toTree-    , incomingEdges-    , config-    , execEnv-    , currRef-    , leaves-    , Ref-    , Language-    , deref-    , getTrace-    , getTraces-    , getPathsFromTo-    , getPathFromTo-    , executionGraph-    , shadowExecEnv-    , eqClasses-    , initialRef-    , fromExport-    , toExport-    ) where--import Data.Graph.Inductive.Graph-import Data.Graph.Inductive.PatriciaTree-import Data.Graph.Inductive.Query-import Data.Graph.Inductive.Query.SP-import Data.Tree (Tree(..))--import qualified Data.IntMap as IntMap-import Data.List-import Data.Foldable-import Data.Maybe--type Ref = Int-type Language p m c o = (Eq p, Eq o, Monad m, Monoid o)--data Explorer programs m configs output where-    Explorer :: Language programs m configs output =>-        { shadowing :: Bool -- Shadow the exploration tree in a shadow graph.-        , defInterp :: programs -> configs -> m (Maybe configs, output)-        , config :: configs -- Cache the config-        , currRef :: Ref-        , genRef :: Ref-        , cmap :: IntMap.IntMap configs-        , execEnv :: Gr Ref (programs, output)-        , shadowExecEnv :: Gr [Ref] (programs, output)-        , configEq :: configs -> configs -> Bool-        } -> Explorer programs m configs output---mkExplorer :: Language p m c o => Bool -> (c -> c -> Bool) -> (p -> c -> m (Maybe c, o)) -> c -> Explorer p m c o-mkExplorer shadow shadowEq definterp conf = Explorer-    { defInterp = definterp-    , config = conf-    , genRef = 1 -- Currently generate references by increasing a counter.-    , currRef = initialRef-    , cmap = IntMap.fromList [(initialRef, conf)]-    , execEnv = mkGraph [(initialRef, initialRef)] []-    , shadowExecEnv = mkGraph [(initialRef, [initialRef])] []-    , shadowing = shadow-    , configEq = shadowEq-}--initialRef :: Int-initialRef = 1--mkExplorerNoSharing :: Language p m c o  => (p -> c -> m (Maybe c, o)) -> c -> Explorer p m c o-mkExplorerNoSharing = mkExplorer False (\_ -> \_ -> False)--deref :: Explorer p m c o -> Ref -> Maybe c-deref e r = IntMap.lookup r (cmap e)--findRef :: Explorer p m c o -> c -> (c -> Bool) -> Maybe (Ref, c)-findRef e c eq = find (\(r, c') -> eq c') (IntMap.toList (cmap e))--addNewPath :: Explorer p m c o -> p -> o -> c -> Explorer p m c o-addNewPath e p o c = e { config = c, currRef = newref, genRef = newref, cmap = IntMap.insert newref c (cmap e),-     execEnv = insNode (newref, newref) $ insEdge (currRef e, newref, (p,o)) (execEnv e)}-     where newref = genRef e + 1---findNodeRef :: Gr [Ref] (p, o) -> Ref -> Ref-findNodeRef g r = fst $ fromJust $ find (\(_, rs) -> r `elem` rs) $ labNodes g---updateShadowEnv :: Language p m c o => Explorer p m c o -> (p, c, o, Ref, Ref) -> Explorer p m c o-updateShadowEnv e (p, newconf, output, newref, oldref) =-  case findRef e newconf (configEq e newconf) of-    Just (r', _) ->-      if hasLEdge shadowEnv (nref, findNodeRef shadowEnv r', (p, output))-        then e-        else e {-          shadowExecEnv = updateLabel (findNodeRef shadowEnv r', newref, p, output) $ insEdge (nref, findNodeRef shadowEnv r', (p, output)) shadowEnv-        }-    Nothing -> e {-      shadowExecEnv = insNode (newref, [newref]) $ insEdge (nref, newref, (p, output)) $ shadowExecEnv e-    }-    where-      shadowEnv = shadowExecEnv e-      nref = findNodeRef shadowEnv oldref-      updateLabel (target, add_to_label, p, output) gr =-        case match target gr of-          (Just (toadj, node, label, fromadj), decomgr) -> (toadj, node, add_to_label : label, fromadj) & decomgr-          _ -> error "Shadow execution environment is inconsistent."---updateExecEnvs :: Language p m c o => Explorer p m c o -> (p, c, o) -> Explorer p m c o-updateExecEnvs e (p, newconf, output)-  | shadowing e = addNewPath (updateShadowEnv e (p, newconf, output, (currRef newexplorer), (currRef e))) p output newconf-  | otherwise = newexplorer-  where-    newexplorer = addNewPath e p output newconf--execute :: Language p m c o =>  p -> Explorer p m c o -> m (Explorer p m c o, o)-execute p e =-  do (mcfg, o) <- defInterp e p (config e)-     case mcfg of-       Just cfg -> return (updateExecEnvs e (p, cfg, o), o)-       Nothing  -> return (e, o)--executeAll :: Language p m c o => [p] -> Explorer p m c o -> m (Explorer p m c o, o)-executeAll ps exp = foldlM executeCollect (exp, mempty) ps-  where executeCollect (exp, out) p = do (res, out') <- execute p exp-                                         return (res, out `mappend` out')--deleteMap :: [Ref] -> IntMap.IntMap a -> IntMap.IntMap a-deleteMap xs m = foldl (flip IntMap.delete) m xs---deleteFromShadowEnv :: [(Ref, Ref)] -> Gr [Ref] po -> Gr [Ref] po-deleteFromShadowEnv [(l, r)] gr = case match r gr of-  (Just (toadj, node, label, fromadj), decomgr) -> (toadj, node, label \\ [l], fromadj) & decomgr-  _ -> error "Inconsistent shadow env."-deleteFromShadowEnv (x:xs) gr = deleteFromShadowEnv xs (deleteFromShadowEnv [x] gr)--cleanEdges :: [Ref] -> [(Ref, Ref, (p, o))] -> [(Ref, Ref, (p, o))]-cleanEdges ref edg = filter (\(s, t, l) -> not $ t `elem` ref) edg--cleanShadowEnv :: Bool -> [Ref] -> Gr [Ref] (p, o) -> Gr [Ref] (p, o)-cleanShadowEnv False _ g = g-cleanShadowEnv True nds g = shadowEnv''-  where-    shadowEnv' = deleteFromShadowEnv (map (\r -> (r, findNodeRef g r)) nds) g-    nodesToDel' = map fst (filter (\(r, labels) -> labels == []) $ labNodes (shadowEnv'))-    edgesToDel' = filter (\(s, t) -> s `elem` nodesToDel' || t `elem` nodesToDel') (edges shadowEnv')-    shadowEnv'' = (delEdges edgesToDel' . delNodes nodesToDel') shadowEnv'--data RevertableStatus = ContinueRevert | StopRevert deriving Show--findRevertableNodes gr source target =-  case findNextNodeInPath gr source target of-    (Just node) -> fst $ findRevertableNodes' gr node target-    Nothing     -> []-  where-    findNextNodeInPath gr source target = find (\n -> target `elem` (reachable n gr)) (suc gr source)-    findRevertableNodes' gr source target-      | source == target = if outdeg gr source > 1 then ([], StopRevert) else ([source], ContinueRevert)-      | otherwise = case findNextNodeInPath gr source target of-        (Just node) -> case findRevertableNodes' gr node target of-          (res, StopRevert) -> (res, StopRevert)-          (res, ContinueRevert) -> if outdeg gr source > 1 then (res, StopRevert) else (source : res, ContinueRevert)-        Nothing -> ([], ContinueRevert)--revert :: Ref -> Explorer p m c o -> Maybe (Explorer p m c o)-revert r e-  | currRef e `elem` reachNodes =-      jump r e >>= \e' -> return $ e' { execEnv = mkGraph (zip remainNodes remainNodes) $ cleanEdges reachNodes (labEdges $ execEnv e')-                                      ,  cmap = deleteMap reachNodes (cmap e')-                                      , shadowExecEnv = cleanShadowEnv (shadowing e') reachNodes (shadowExecEnv e')}-  | otherwise = Nothing-  where-    reachNodes = findRevertableNodes gr r (currRef e)-    remainNodes = nodes gr \\ reachNodes-    gr = execEnv e--jump :: Ref -> Explorer p m c o -> Maybe (Explorer p m c o)-jump r e = case deref e r of-             (Just c) -> return $ e { config = c, currRef = r }-             Nothing -> Nothing--toTree :: Explorer p m c o -> Tree (Ref, c)-toTree exp = mkTree initialRef-  where graph = execEnv exp-        target (_, r, _) = r-        mkTree r = Node (r, cmap exp IntMap.! r) (map (mkTree . target) (out graph r))---incomingEdges :: Ref -> Explorer p m c o -> [((Ref, c), (p, o), (Ref, c))]-incomingEdges ref e = foldr (\(s, t, l) acc ->  [((s, unpack s), l, (t, unpack t))] ++ acc) [] (filter (\(_, t, _) -> t == ref) (labEdges (execEnv e)))-  where-    unpack ref = fromJust $ deref e ref--getTrace :: Explorer p m c o -> [((Ref, c), (p, o), (Ref, c))]-getTrace e = getPathFromTo e initialRef (currRef e)--getTraces :: Explorer p m c o -> [[((Ref, c), (p, o), (Ref, c))]]-getTraces e = getPathsFromTo e initialRef (currRef e)---mapOut :: Explorer p m c o -> Gr Ref (p, o) -> [Ref] -> Ref -> (Ref, Ref, (p,o)) -> Maybe [[((Ref, c), (p, o), (Ref, c))]]-mapOut exp gr visited goal (s, t, (l, o))-  | goal == t = Just $ [[((s, unpack s), (l, o), (t, unpack t))]] ++ explore-  | otherwise = case t `elem` visited of-                  True -> Nothing-                  False -> Just explore-  where-    explore = map ((:)((s, unpack s), (l, o), (t, unpack t))) (concat $ catMaybes $ map (mapOut exp gr (t : visited) goal) (out gr t))-    unpack ref = fromJust $ deref exp ref---getPathsFromTo :: Explorer p m c o -> Ref -> Ref -> [[((Ref, c), (p, o), (Ref, c))]]-getPathsFromTo exp from to = concat $ catMaybes $ map (mapOut exp (execEnv exp) [from] to) (out (execEnv exp) from)--getPathFromTo :: Explorer p m c o -> Ref -> Ref -> [((Ref, c), (p, o), (Ref, c))]-getPathFromTo exp from to =-  case getPathsFromTo exp from to of-    [] -> []-    (x:_) -> x----executionGraph :: Explorer p m c o -> ((Ref, c), [(Ref, c)], [((Ref, c), (p, o), (Ref, c))])-executionGraph exp =-  (curr, nodes, edges)-  where-    curr = (currRef exp, config exp)-    nodes = IntMap.toList $ cmap exp-    edges = map (\(s, t, p) -> ((s, fromJust $ deref exp s), p, (t, fromJust $ deref exp t)) ) (labEdges (execEnv exp))--{-|-  Returns all configurations that have not been the source for an execute action.-  This corresponds to leaves in a tree or nodes without an outbound-edge in a graph.--}-leaves :: Explorer p m c o -> [(Ref, c)]-leaves exp = map refToPair leaf_nodes-  where-    env = execEnv exp-    refToPair = \r -> (r, fromJust $ deref exp r)-    leaf_nodes = nodes $ nfilter (\n -> (==0) $ outdeg env n) env---toExport :: Explorer p m c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))])-toExport exp = (currRef exp, IntMap.toList $ cmap exp, labEdges $ execEnv exp)--fromExport :: Explorer p m c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))]) -> Explorer p m c o-fromExport exp (curr, nds, edgs) = exp { genRef = findMax nds,-                                         config = findCurrentConf curr nds,-                                         currRef = curr,-                                         cmap = IntMap.fromList nds,-                                         execEnv = mkGraph (map (\(x, _) -> (x, x)) nds) edgs }-  where findMax l = maximum $ map fst l-        findCurrentConf curr nds = case lookup curr nds of-                                     Just conf -> conf-                                     Nothing   -> error "no config found"--eqClasses :: Explorer p m c o -> [[Ref]]-eqClasses expl = map snd $ labNodes $ shadowExecEnv expl+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE ConstraintKinds #-}
+
+
+module Language.Explorer.Monadic
+    ( Explorer
+    , mkExplorer
+    , mkExplorerNoSharing
+    , execute
+    , executeAll
+    , revert
+    , jump
+    , toTree
+    , incomingEdges
+    , config
+    , execEnv
+    , currRef
+    , leaves
+    , Ref
+    , Language
+    , deref
+    , getTrace
+    , getTraces
+    , getPathsFromTo
+    , getPathFromTo
+    , executionGraph
+    , shadowExecEnv
+    , eqClasses
+    , initialRef
+    , fromExport
+    , toExport
+    ) where
+
+import Data.Graph.Inductive.Graph
+import Data.Graph.Inductive.PatriciaTree
+import Data.Graph.Inductive.Query
+import Data.Graph.Inductive.Query.SP
+import Data.Tree (Tree(..))
+
+import qualified Data.IntMap as IntMap
+import Data.List
+import Data.Foldable
+import Data.Maybe
+
+type Ref = Int
+type Language p m c o = (Eq p, Eq o, Monad m, Monoid o)
+
+data Explorer programs m configs output where
+    Explorer :: Language programs m configs output =>
+        { shadowing :: Bool -- Shadow the exploration tree in a shadow graph.
+        , defInterp :: programs -> configs -> m (Maybe configs, output)
+        , config :: configs -- Cache the config
+        , currRef :: Ref
+        , genRef :: Ref
+        , cmap :: IntMap.IntMap configs
+        , execEnv :: Gr Ref (programs, output)
+        , shadowExecEnv :: Gr [Ref] (programs, output)
+        , configEq :: configs -> configs -> Bool
+        } -> Explorer programs m configs output
+
+
+mkExplorer :: Language p m c o => Bool -> (c -> c -> Bool) -> (p -> c -> m (Maybe c, o)) -> c -> Explorer p m c o
+mkExplorer shadow shadowEq definterp conf = Explorer
+    { defInterp = definterp
+    , config = conf
+    , genRef = 1 -- Currently generate references by increasing a counter.
+    , currRef = initialRef
+    , cmap = IntMap.fromList [(initialRef, conf)]
+    , execEnv = mkGraph [(initialRef, initialRef)] []
+    , shadowExecEnv = mkGraph [(initialRef, [initialRef])] []
+    , shadowing = shadow
+    , configEq = shadowEq
+}
+
+initialRef :: Int
+initialRef = 1
+
+mkExplorerNoSharing :: Language p m c o  => (p -> c -> m (Maybe c, o)) -> c -> Explorer p m c o
+mkExplorerNoSharing = mkExplorer False (\_ -> \_ -> False)
+
+deref :: Explorer p m c o -> Ref -> Maybe c
+deref e r = IntMap.lookup r (cmap e)
+
+findRef :: Explorer p m c o -> c -> (c -> Bool) -> Maybe (Ref, c)
+findRef e c eq = find (\(r, c') -> eq c') (IntMap.toList (cmap e))
+
+addNewPath :: Explorer p m c o -> p -> o -> c -> Explorer p m c o
+addNewPath e p o c = e { config = c, currRef = newref, genRef = newref, cmap = IntMap.insert newref c (cmap e),
+     execEnv = insNode (newref, newref) $ insEdge (currRef e, newref, (p,o)) (execEnv e)}
+     where newref = genRef e + 1
+
+
+findNodeRef :: Gr [Ref] (p, o) -> Ref -> Ref
+findNodeRef g r = fst $ fromJust $ find (\(_, rs) -> r `elem` rs) $ labNodes g
+
+
+updateShadowEnv :: Language p m c o => Explorer p m c o -> (p, c, o, Ref, Ref) -> Explorer p m c o
+updateShadowEnv e (p, newconf, output, newref, oldref) =
+  case findRef e newconf (configEq e newconf) of
+    Just (r', _) ->
+      if hasLEdge shadowEnv (nref, findNodeRef shadowEnv r', (p, output))
+        then e
+        else e {
+          shadowExecEnv = updateLabel (findNodeRef shadowEnv r', newref, p, output) $ insEdge (nref, findNodeRef shadowEnv r', (p, output)) shadowEnv
+        }
+    Nothing -> e {
+      shadowExecEnv = insNode (newref, [newref]) $ insEdge (nref, newref, (p, output)) $ shadowExecEnv e
+    }
+    where
+      shadowEnv = shadowExecEnv e
+      nref = findNodeRef shadowEnv oldref
+      updateLabel (target, add_to_label, p, output) gr =
+        case match target gr of
+          (Just (toadj, node, label, fromadj), decomgr) -> (toadj, node, add_to_label : label, fromadj) & decomgr
+          _ -> error "Shadow execution environment is inconsistent."
+
+
+updateExecEnvs :: Language p m c o => Explorer p m c o -> (p, c, o) -> Explorer p m c o
+updateExecEnvs e (p, newconf, output)
+  | shadowing e = addNewPath (updateShadowEnv e (p, newconf, output, (currRef newexplorer), (currRef e))) p output newconf
+  | otherwise = newexplorer
+  where
+    newexplorer = addNewPath e p output newconf
+
+execute :: Language p m c o =>  p -> Explorer p m c o -> m (Explorer p m c o, o)
+execute p e =
+  do (mcfg, o) <- defInterp e p (config e)
+     case mcfg of
+       Just cfg -> return (updateExecEnvs e (p, cfg, o), o)
+       Nothing  -> return (e, o)
+
+executeAll :: Language p m c o => [p] -> Explorer p m c o -> m (Explorer p m c o, o)
+executeAll ps exp = foldlM executeCollect (exp, mempty) ps
+  where executeCollect (exp, out) p = do (res, out') <- execute p exp
+                                         return (res, out `mappend` out')
+
+deleteMap :: [Ref] -> IntMap.IntMap a -> IntMap.IntMap a
+deleteMap xs m = foldl (flip IntMap.delete) m xs
+
+
+deleteFromShadowEnv :: [(Ref, Ref)] -> Gr [Ref] po -> Gr [Ref] po
+deleteFromShadowEnv [(l, r)] gr = case match r gr of
+  (Just (toadj, node, label, fromadj), decomgr) -> (toadj, node, label \\ [l], fromadj) & decomgr
+  _ -> error "Inconsistent shadow env."
+deleteFromShadowEnv (x:xs) gr = deleteFromShadowEnv xs (deleteFromShadowEnv [x] gr)
+
+cleanEdges :: [Ref] -> [(Ref, Ref, (p, o))] -> [(Ref, Ref, (p, o))]
+cleanEdges ref edg = filter (\(s, t, l) -> not $ t `elem` ref) edg
+
+cleanShadowEnv :: Bool -> [Ref] -> Gr [Ref] (p, o) -> Gr [Ref] (p, o)
+cleanShadowEnv False _ g = g
+cleanShadowEnv True nds g = shadowEnv''
+  where
+    shadowEnv' = deleteFromShadowEnv (map (\r -> (r, findNodeRef g r)) nds) g
+    nodesToDel' = map fst (filter (\(r, labels) -> labels == []) $ labNodes (shadowEnv'))
+    edgesToDel' = filter (\(s, t) -> s `elem` nodesToDel' || t `elem` nodesToDel') (edges shadowEnv')
+    shadowEnv'' = (delEdges edgesToDel' . delNodes nodesToDel') shadowEnv'
+
+data RevertableStatus = ContinueRevert | StopRevert deriving Show
+
+findRevertableNodes gr source target =
+  case findNextNodeInPath gr source target of
+    (Just node) -> fst $ findRevertableNodes' gr node target
+    Nothing     -> []
+  where
+    findNextNodeInPath gr source target = find (\n -> target `elem` (reachable n gr)) (suc gr source)
+    findRevertableNodes' gr source target
+      | source == target = if outdeg gr source > 1 then ([], StopRevert) else ([source], ContinueRevert)
+      | otherwise = case findNextNodeInPath gr source target of
+        (Just node) -> case findRevertableNodes' gr node target of
+          (res, StopRevert) -> (res, StopRevert)
+          (res, ContinueRevert) -> if outdeg gr source > 1 then (res, StopRevert) else (source : res, ContinueRevert)
+        Nothing -> ([], ContinueRevert)
+
+revert :: Ref -> Explorer p m c o -> Maybe (Explorer p m c o)
+revert r e
+  | currRef e `elem` reachNodes =
+      jump r e >>= \e' -> return $ e' { execEnv = mkGraph (zip remainNodes remainNodes) $ cleanEdges reachNodes (labEdges $ execEnv e')
+                                      ,  cmap = deleteMap reachNodes (cmap e')
+                                      , shadowExecEnv = cleanShadowEnv (shadowing e') reachNodes (shadowExecEnv e')}
+  | otherwise = Nothing
+  where
+    reachNodes = findRevertableNodes gr r (currRef e)
+    remainNodes = nodes gr \\ reachNodes
+    gr = execEnv e
+
+jump :: Ref -> Explorer p m c o -> Maybe (Explorer p m c o)
+jump r e = case deref e r of
+             (Just c) -> return $ e { config = c, currRef = r }
+             Nothing -> Nothing
+
+toTree :: Explorer p m c o -> Tree (Ref, c)
+toTree exp = mkTree initialRef
+  where graph = execEnv exp
+        target (_, r, _) = r
+        mkTree r = Node (r, cmap exp IntMap.! r) (map (mkTree . target) (out graph r))
+
+
+incomingEdges :: Ref -> Explorer p m c o -> [((Ref, c), (p, o), (Ref, c))]
+incomingEdges ref e = foldr (\(s, t, l) acc ->  [((s, unpack s), l, (t, unpack t))] ++ acc) [] (filter (\(_, t, _) -> t == ref) (labEdges (execEnv e)))
+  where
+    unpack ref = fromJust $ deref e ref
+
+getTrace :: Explorer p m c o -> [((Ref, c), (p, o), (Ref, c))]
+getTrace e = getPathFromTo e initialRef (currRef e)
+
+getTraces :: Explorer p m c o -> [[((Ref, c), (p, o), (Ref, c))]]
+getTraces e = getPathsFromTo e initialRef (currRef e)
+
+
+mapOut :: Explorer p m c o -> Gr Ref (p, o) -> [Ref] -> Ref -> (Ref, Ref, (p,o)) -> Maybe [[((Ref, c), (p, o), (Ref, c))]]
+mapOut exp gr visited goal (s, t, (l, o))
+  | goal == t = Just $ [[((s, unpack s), (l, o), (t, unpack t))]] ++ explore
+  | otherwise = case t `elem` visited of
+                  True -> Nothing
+                  False -> Just explore
+  where
+    explore = map ((:)((s, unpack s), (l, o), (t, unpack t))) (concat $ catMaybes $ map (mapOut exp gr (t : visited) goal) (out gr t))
+    unpack ref = fromJust $ deref exp ref
+
+
+getPathsFromTo :: Explorer p m c o -> Ref -> Ref -> [[((Ref, c), (p, o), (Ref, c))]]
+getPathsFromTo exp from to = concat $ catMaybes $ map (mapOut exp (execEnv exp) [from] to) (out (execEnv exp) from)
+
+getPathFromTo :: Explorer p m c o -> Ref -> Ref -> [((Ref, c), (p, o), (Ref, c))]
+getPathFromTo exp from to =
+  case getPathsFromTo exp from to of
+    [] -> []
+    (x:_) -> x
+
+
+
+executionGraph :: Explorer p m c o -> ((Ref, c), [(Ref, c)], [((Ref, c), (p, o), (Ref, c))])
+executionGraph exp =
+  (curr, nodes, edges)
+  where
+    curr = (currRef exp, config exp)
+    nodes = IntMap.toList $ cmap exp
+    edges = map (\(s, t, p) -> ((s, fromJust $ deref exp s), p, (t, fromJust $ deref exp t)) ) (labEdges (execEnv exp))
+
+{-|
+  Returns all configurations that have not been the source for an execute action.
+  This corresponds to leaves in a tree or nodes without an outbound-edge in a graph.
+-}
+leaves :: Explorer p m c o -> [(Ref, c)]
+leaves exp = map refToPair leaf_nodes
+  where
+    env = execEnv exp
+    refToPair = \r -> (r, fromJust $ deref exp r)
+    leaf_nodes = nodes $ nfilter (\n -> (==0) $ outdeg env n) env
+
+
+toExport :: Explorer p m c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))])
+toExport exp = (currRef exp, IntMap.toList $ cmap exp, labEdges $ execEnv exp)
+
+fromExport :: Explorer p m c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))]) -> Explorer p m c o
+fromExport exp (curr, nds, edgs) = exp { genRef = findMax nds,
+                                         config = findCurrentConf curr nds,
+                                         currRef = curr,
+                                         cmap = IntMap.fromList nds,
+                                         execEnv = mkGraph (map (\(x, _) -> (x, x)) nds) edgs }
+  where findMax l = maximum $ map fst l
+        findCurrentConf curr nds = case lookup curr nds of
+                                     Just conf -> conf
+                                     Nothing   -> error "no config found"
+
+eqClasses :: Explorer p m c o -> [[Ref]]
+eqClasses expl = map snd $ labNodes $ shadowExecEnv expl
Language/Explorer/Pure.hs view
@@ -1,91 +1,95 @@-{-# LANGUAGE ConstraintKinds #-}--module Language.Explorer.Pure-    ( Explorer-    , mkExplorer-    , mkExplorerNoSharing-    , execute-    , executeAll-    , revert-    , ExplorerM.toTree-    , incomingEdges-    , config-    , currRef-    , Ref-    , deref-    , leaves-    , getTrace-    , getTraces-    , getPathsFromTo-    , getPathFromTo-    , executionGraph-    , fromExport-    , toExport-    ) where--import qualified Language.Explorer.Monadic as ExplorerM-import Control.Monad.Identity--import qualified Data.IntMap as IntMap-import Data.List-import Data.Foldable---- We shadow instead of exporting directly to make the user interaction--- the same.-type Ref = ExplorerM.Ref-type Explorer a b o = ExplorerM.Explorer a Identity b o-type PureLanguage p c o = (Eq p, Eq o, Monoid o)--mkExplorer :: PureLanguage p c o => Bool -> (c -> c -> Bool) -> (p -> c -> (Maybe c, o)) -> c -> Explorer p c o-mkExplorer shadowing eqfunc definterp initialConf = ExplorerM.mkExplorer shadowing eqfunc (wrap definterp) initialConf--mkExplorerNoSharing :: PureLanguage p c o => (p -> c -> (Maybe c, o)) -> c -> Explorer p c o-mkExplorerNoSharing = mkExplorer False (const . const $ False)--currRef :: Explorer a b o -> Ref-currRef = ExplorerM.currRef--config :: Explorer a b o -> b-config = ExplorerM.config--deref :: Explorer p c o -> Ref -> Maybe c-deref = ExplorerM.deref--wrap :: (Monad m, Monoid o) => (a -> b -> (Maybe b, o)) -> a -> b -> m (Maybe b, o)-wrap def p e = return $ def p e--execute :: PureLanguage p c o =>  p -> Explorer p c o -> (Explorer p c o, o)-execute p e = runIdentity $ ExplorerM.execute p e--executeAll :: PureLanguage p c o => [p] -> Explorer p c o -> (Explorer p c o, o)-executeAll p e = runIdentity $ ExplorerM.executeAll p e--revert :: ExplorerM.Ref -> Explorer p c o -> Maybe (Explorer p c o)-revert = ExplorerM.revert--incomingEdges :: Ref -> Explorer p c o -> [((Ref, c), (p, o), (Ref, c))]-incomingEdges = ExplorerM.incomingEdges--getTrace :: Explorer p c o -> [((Ref, c), (p, o), (Ref, c))]-getTrace = ExplorerM.getTrace--getTraces :: Explorer p c o -> [[((Ref, c), (p, o), (Ref, c))]]-getTraces = ExplorerM.getTraces--getPathsFromTo :: Explorer p c o -> Ref -> Ref -> [[((Ref, c), (p, o), (Ref, c))]]-getPathsFromTo = ExplorerM.getPathsFromTo--getPathFromTo :: Explorer p c o -> Ref -> Ref -> [((Ref, c), (p, o), (Ref, c))]-getPathFromTo = ExplorerM.getPathFromTo--executionGraph :: Explorer p c o -> ((Ref, c), [(Ref, c)], [((Ref, c), (p, o), (Ref, c))])-executionGraph = ExplorerM.executionGraph--leaves :: Explorer p c o -> [(Ref, c)]-leaves = ExplorerM.leaves--toExport :: Explorer p c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))])-toExport = ExplorerM.toExport--fromExport :: Explorer p c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))]) -> Explorer p c o+{-# LANGUAGE ConstraintKinds #-}
+
+module Language.Explorer.Pure
+    ( Explorer
+    , mkExplorer
+    , mkExplorerNoSharing
+    , execute
+    , executeAll
+    , revert
+    , jump
+    , ExplorerM.toTree
+    , incomingEdges
+    , config
+    , currRef
+    , Ref
+    , deref
+    , leaves
+    , getTrace
+    , getTraces
+    , getPathsFromTo
+    , getPathFromTo
+    , executionGraph
+    , fromExport
+    , toExport
+    ) where
+
+import qualified Language.Explorer.Monadic as ExplorerM
+import Control.Monad.Identity
+
+import qualified Data.IntMap as IntMap
+import Data.List
+import Data.Foldable
+
+-- We shadow instead of exporting directly to make the user interaction
+-- the same.
+type Ref = ExplorerM.Ref
+type Explorer a b o = ExplorerM.Explorer a Identity b o
+type PureLanguage p c o = (Eq p, Eq o, Monoid o)
+
+mkExplorer :: PureLanguage p c o => Bool -> (c -> c -> Bool) -> (p -> c -> (Maybe c, o)) -> c -> Explorer p c o
+mkExplorer shadowing eqfunc definterp initialConf = ExplorerM.mkExplorer shadowing eqfunc (wrap definterp) initialConf
+
+mkExplorerNoSharing :: PureLanguage p c o => (p -> c -> (Maybe c, o)) -> c -> Explorer p c o
+mkExplorerNoSharing = mkExplorer False (const . const $ False)
+
+currRef :: Explorer a b o -> Ref
+currRef = ExplorerM.currRef
+
+config :: Explorer a b o -> b
+config = ExplorerM.config
+
+deref :: Explorer p c o -> Ref -> Maybe c
+deref = ExplorerM.deref
+
+wrap :: (Monad m, Monoid o) => (a -> b -> (Maybe b, o)) -> a -> b -> m (Maybe b, o)
+wrap def p e = return $ def p e
+
+execute :: PureLanguage p c o =>  p -> Explorer p c o -> (Explorer p c o, o)
+execute p e = runIdentity $ ExplorerM.execute p e
+
+executeAll :: PureLanguage p c o => [p] -> Explorer p c o -> (Explorer p c o, o)
+executeAll p e = runIdentity $ ExplorerM.executeAll p e
+
+revert :: ExplorerM.Ref -> Explorer p c o -> Maybe (Explorer p c o)
+revert = ExplorerM.revert
+
+jump :: ExplorerM.Ref -> Explorer p c o -> Maybe (Explorer p c o)
+jump = ExplorerM.jump
+
+incomingEdges :: Ref -> Explorer p c o -> [((Ref, c), (p, o), (Ref, c))]
+incomingEdges = ExplorerM.incomingEdges
+
+getTrace :: Explorer p c o -> [((Ref, c), (p, o), (Ref, c))]
+getTrace = ExplorerM.getTrace
+
+getTraces :: Explorer p c o -> [[((Ref, c), (p, o), (Ref, c))]]
+getTraces = ExplorerM.getTraces
+
+getPathsFromTo :: Explorer p c o -> Ref -> Ref -> [[((Ref, c), (p, o), (Ref, c))]]
+getPathsFromTo = ExplorerM.getPathsFromTo
+
+getPathFromTo :: Explorer p c o -> Ref -> Ref -> [((Ref, c), (p, o), (Ref, c))]
+getPathFromTo = ExplorerM.getPathFromTo
+
+executionGraph :: Explorer p c o -> ((Ref, c), [(Ref, c)], [((Ref, c), (p, o), (Ref, c))])
+executionGraph = ExplorerM.executionGraph
+
+leaves :: Explorer p c o -> [(Ref, c)]
+leaves = ExplorerM.leaves
+
+toExport :: Explorer p c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))])
+toExport = ExplorerM.toExport
+
+fromExport :: Explorer p c o -> (Ref, [(Ref, c)], [(Ref, Ref, (p, o))]) -> Explorer p c o
 fromExport = ExplorerM.fromExport
Language/Explorer/Tools/Protocol.hs view
@@ -1,450 +1,450 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE MultiParamTypeClasses #-}----module Language.Explorer.Tools.Protocol where--import GHC.Generics-import Data.Monoid-import Data.Aeson-import Network.HTTP.Types.Status-import Network.HTTP.Types.Header (hContentType)-import Data.Maybe-import Control.Concurrent (forkFinally)-import qualified Control.Exception as E-import Control.Monad (unless, forever, void)-import qualified Data.ByteString.Lazy as S-import qualified Data.Attoparsec.ByteString.Lazy as AB-import qualified Data.Attoparsec.ByteString.Char8 as ABC-import Network.Socket hiding (recv)-import Network.Socket.ByteString.Lazy (recv, sendAll)-import Data.Scientific-import qualified Language.Explorer.Monadic as Ex-import Control.Monad.RWS.Lazy hiding (listen)-import Control.Monad.Trans.Except-import Data.List--type ExplorerParser p m c o = (Ex.Explorer p m c o, String -> Maybe p)-type ProcessResult = Either ErrorMessage Value--type EIP p m c o = RWST (String -> Maybe p) S.ByteString (Ex.Explorer p m c o) m--class ExplorerPostValue p c o where-    postExecute :: Ex.Explorer p m c o -> Ex.Explorer p m c o -> o -> Value-    postExecute = \_ _ _ -> Null-    postJump :: Ex.Explorer p m c o -> Ex.Explorer p m c o -> Value-    postJump = \_ _ -> Null-    postRevert :: Ex.Explorer p m c o -> Ex.Explorer p m c o -> [Ex.Ref] -> Value-    postRevert = \ _ _ _ -> Null--data RequestMessage = RequestMessage {-    jsonrpc :: String,-    req_id :: String,-    method :: String,-    params :: Maybe Value-} deriving (Show, Generic)--instance ToJSON RequestMessage where-    toEncoding = genericToEncoding defaultOptions--instance FromJSON RequestMessage where-    parseJSON = withObject "RequestMessage" $ \v -> RequestMessage-        <$> v .: "jsonrpc"-        <*> v .: "id"-        <*> v .: "method"-        <*> v .:? "params"--data ResponseMessage = ResponseMessage {-    res_id :: String,-    body :: ProcessResult-} deriving (Show)--instance ToJSON ResponseMessage where-    toJSON (ResponseMessage res_id (Left e)) = object ["id" .= res_id, "error" .= e]-    toJSON (ResponseMessage res_id (Right res)) = object ["id" .= res_id, "result" .= res]--    toEncoding (ResponseMessage res_id (Left e)) = pairs ("id" .= res_id <> "error" .= e)-    toEncoding (ResponseMessage res_id (Right res)) = pairs ("id" .= res_id <> "result" .= res)---data ErrorMessage = ErrorMessage {-    code :: Int,-    message :: String,-    error_data :: Maybe Value-} deriving (Show, Generic)--instance ToJSON ErrorMessage where-    toEncoding = genericToEncoding defaultOptions--instance FromJSON ErrorMessage-    -- No need to provide a parseJSON implementation.---- instance Except ErrorMessage --- where---     noMsg = ErrorMessage { code = 0, message = "", error_data = Nothing}---     strMsg msg = ErrorMessage {code = 0, message = msg, error_data = Nothing }--data JumpParams = JumpParams {-    jump_ref :: Int-}--instance FromJSON JumpParams where-    parseJSON = withObject "JumpParams" $ \v -> JumpParams-        <$> v .: "reference"--data JumpResult = JumpResult {-    jump_post :: Value-}--instance ToJSON JumpResult where -    toJSON res = object ["post" .= jump_post res]--data ExecuteParams = ExecuteParams {-    paramsProgram :: String-} deriving (Show, Generic)--instance FromJSON ExecuteParams--data ExecuteResult = ExecuteResult {-    exec_ref :: Int,-    exec_out :: Value,-    exec_post :: Value-}--instance ToJSON ExecuteResult where-    toJSON (ExecuteResult ref out post) = object ["reference" .= ref, "output" .= out, "post" .= post]--    toEncoding (ExecuteResult ref out post) = pairs ("reference" .= ref <> "output" .= out <> "post" .= post)--data RevertParams = RevertParams {-    revert_ref :: Int-}--instance FromJSON RevertParams where-    parseJSON = withObject "RevertParams" $ \v -> RevertParams-        <$> v .: "reference"--data RevertResult = RevertResult {-    revert_deleted :: [Ex.Ref],-    post_revert :: Value-}--instance ToJSON RevertResult where -    toJSON res = object ["deleted" .= revert_deleted res, "post" .= post_revert res]--data DerefParams = DerefParams {-    deref_ref :: Int-}--instance FromJSON DerefParams where-    parseJSON = withObject "DerefParams" $ \v -> DerefParams-        <$> v .: "reference"---data TraceParams = TraceParams {-    reference :: Int-} deriving (Generic)--instance FromJSON TraceParams---data Edge = Edge {-    source :: Int,-    target :: Int,-    label  :: EdgeLabel-} deriving (Generic)--instance ToJSON Edge where-    toEncoding = genericToEncoding defaultOptions---data EdgeLabel = EdgeLabel {-    program :: Value,-    mval :: Value-} deriving (Generic)--instance ToJSON EdgeLabel where-    toEncoding = genericToEncoding defaultOptions---data ExecutionTree = ExecutionTree {-    current :: Int,-    references :: [Int],-    edges :: [Edge]-} deriving (Generic)--instance ToJSON ExecutionTree where-    toEncoding = genericToEncoding defaultOptions--data PathParams = PathParams {-    paramsSource :: Int,-    paramsTarget :: Int-} deriving (Generic)--instance FromJSON PathParams--parseErrorCode = -32700-invalidRequestCode = -32600-methodNotFoundCode = -32601-invalidParamsCode = -32602-internalErrorCode = -32603--referenceNotInTreeCode = 1-referenceRevertInvalidCode = 2-programParseErrorCode = 3-pathNonExistingCode = 4----parseError :: ErrorMessage-parseError = ErrorMessage {-    code = parseErrorCode,-    message = "Parse error",-    error_data = Nothing-}--methodNotFound :: ErrorMessage-methodNotFound = ErrorMessage {-    code = methodNotFoundCode,-    message = "Method not found",-    error_data = Nothing-}--invalidParams :: ErrorMessage-invalidParams = ErrorMessage {-    code = invalidParamsCode,-    message = "Invalid method parameter(s)",-    error_data = Nothing-}--ensureParameter :: Monad m => Maybe Value -> ExceptT ErrorMessage (EIP p m c o) Value-ensureParameter Nothing = throwE invalidParams-ensureParameter (Just v) = return v--fromResult :: Monad m => Result a -> Value -> ExceptT ErrorMessage (EIP p m c o) Value-fromResult res onSuccess = case res of-    (Error e) -> throwE invalidParams-    (Success v) -> return onSuccess--jump :: (Monad m, ExplorerPostValue p c o) => Value -> ExceptT ErrorMessage (EIP p m c o) Value-jump v = case (fromJSON v) :: Result JumpParams of-    (Error e) -> throwE invalidParams-    (Success v') -> do-        ex <- lift $ get-        case Ex.jump (jump_ref v') ex of-            Just ex' -> do-                lift $ put $ ex'-                return . toJSON . JumpResult $ postJump ex ex'-            Nothing -> throwE ErrorMessage { code = referenceNotInTreeCode, message = "", error_data = Nothing }--execute :: (Eq o, Monoid o, ToJSON o, Eq p, ExplorerPostValue p c o) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value-execute v = case (fromJSON v) :: Result ExecuteParams of-    (Error e) -> throwE invalidParams-    (Success v') -> do-        parser <- lift $ ask-        let pl = parser $ paramsProgram v'-        case pl of-            Just prog -> do-                ex <- lift $ get-                (ex', output) <- liftIO $ Ex.execute prog ex-                lift $ put ex'-                return $ toJSON $ ExecuteResult { exec_ref = Ex.currRef ex', exec_out = toJSON output, exec_post = postExecute ex ex' output }-            Nothing -> throwE ErrorMessage { code = programParseErrorCode, message = "", error_data = Nothing }--allRefs :: Ex.Explorer p IO c o -> [(Ex.Ref, c)]-allRefs ex = refs-    where-        (_, refs, _) = Ex.executionGraph ex---revert :: (Eq o, Monoid o, Eq p, ExplorerPostValue p c o) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value-revert v = case (fromJSON v) :: Result RevertParams of-    (Error e) -> throwE invalidParams-    (Success v) -> do-        ex <- lift $ get-        case Ex.revert (revert_ref v) ex of-            Just ex' -> do-                lift $ put ex'-                return $ toJSON $ RevertResult { revert_deleted = deleted, post_revert = postRevert ex ex' deleted}-                where -                    refs = map fst (allRefs ex)-                    refs' = map fst (allRefs ex')-                    deleted = (refs \\ refs')-            Nothing -> throwE ErrorMessage { code = referenceRevertInvalidCode, message = "", error_data = Nothing }--deref :: (Eq o, Monoid o, Eq p, ToJSON c) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value-deref v = case (fromJSON v) :: Result DerefParams of-    (Error e) -> throwE invalidParams-    (Success v) -> do-        ex <- lift $ get-        case Ex.deref ex (deref_ref v) of-            (Just conf) -> return $ toJSON conf-            Nothing -> throwE ErrorMessage { code = referenceNotInTreeCode, message = "", error_data = Nothing}--executionTree :: (ToJSON o, ToJSON p) => ExceptT ErrorMessage (EIP p IO c o) Value-executionTree = do-    ex <- lift $ get-    let (curr, nodes, edges) = Ex.executionGraph ex-    return $ toJSON $ ExecutionTree -        { current = fst curr-        , references = map fst nodes-        , edges = map (\(s, (p, o), t) -> Edge { source = fst s-        , label = EdgeLabel { program = toJSON p, mval = toJSON o}-        , target = fst t} ) edges}--getCurrentReference :: ExceptT ErrorMessage (EIP p IO c o) Value-getCurrentReference = do-    ex <- lift $ get-    return $ toJSON $ Ex.currRef ex--getAllReferences :: ExceptT ErrorMessage (EIP p IO c o) Value-getAllReferences = do-    ex <- lift $ get-    return $ toJSON $ map fst (allRefs ex)--getTrace :: (ToJSON p, ToJSON o) => Maybe Value -> ExceptT ErrorMessage (EIP p IO c o) Value-getTrace (Just r) = case (fromJSON r) :: Result TraceParams of-    (Error e) -> throwE invalidParams-    (Success v) -> do-        ex <- lift $ get-        let path = Ex.getPathFromTo ex 1 (reference (v :: TraceParams)) -- Fix hardcode 1(it's initialRef).-        return $ toJSON $ map (\(s, (p, o), t) -> Edge { source = fst s, target = fst t, label = EdgeLabel { program = toJSON p, mval = toJSON o} }) path-getTrace Nothing = do-    ex <- lift $ get-    let trace = Ex.getTrace ex-    return $ toJSON $ map (\(s, (p, o), t) -> Edge { source = fst s, target = fst t, label = EdgeLabel { program = toJSON p, mval = toJSON o} }) trace--getPath :: (ToJSON o, ToJSON p) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value-getPath val = case (fromJSON val) :: Result PathParams of-    (Error e) -> throwE ErrorMessage { code = pathNonExistingCode, message = "", error_data = Nothing}-    (Success v) -> do-        ex <- lift $ get-        let path = Ex.getPathFromTo ex (paramsSource v) (paramsTarget v)-        return $ toJSON $ map (\(s, (p, o), t) -> Edge { source = fst s, target = fst t, label = EdgeLabel { program = toJSON p, mval = toJSON o} }) path--getLeaves :: ExceptT ErrorMessage (EIP p IO c o) Value-getLeaves = do-    ex <- lift $ get-    return $ toJSON $ map fst (Ex.leaves ex)--methodDispatch :: (Eq o, Monoid o, ToJSON o, ToJSON p, Eq p, ToJSON c, ExplorerPostValue p c o) => String -> Maybe Value -> ExceptT ErrorMessage (EIP p IO c o) Value-methodDispatch "jump" mval = ensureParameter mval >>= jump-methodDispatch "execute" mval = ensureParameter mval >>= execute-methodDispatch "revert" mval = ensureParameter mval >>= revert-methodDispatch "deref" mval = ensureParameter mval >>= deref-methodDispatch "getTrace" mval = getTrace mval-methodDispatch "getPath" mval = ensureParameter mval >>= getPath-methodDispatch "getExecutionTree" _ = executionTree-methodDispatch "getCurrentReference" _ = getCurrentReference-methodDispatch "getAllReferences" _ = getAllReferences-methodDispatch "getLeaves" _ = getLeaves-methodDispatch _ _ = throwE methodNotFound--handleRequest :: (Eq o, Monoid o, ToJSON o, ToJSON o, ToJSON p, Eq p, ToJSON c, ExplorerPostValue p c o) => Maybe RequestMessage -> EIP p IO c o ResponseMessage-handleRequest (Just msg) = do-    res <- runExceptT $ methodDispatch (method msg) (params msg)-    return $ ResponseMessage { res_id = req_id msg, body = res }-handleRequest Nothing = return $ ResponseMessage { res_id = "0", body = Left parseError { message = "NOthing" }}---handleRequest' :: (Eq o, Monoid o, ToJSON o, Eq p, ToJSON p, ToJSON c, ExplorerPostValue p c o) => S.ByteString -> EIP p IO c o ResponseMessage-handleRequest' body =-    case decode body of-        (Just m) -> handleRequest m-        Nothing -> return invalidHeader--invalidHeader :: ResponseMessage-invalidHeader = ResponseMessage { res_id = "0", body = Left parseError {message = "Headeer"} }--parseHeader :: AB.Parser (Int, String)-parseHeader = do-    AB.string "Content-Length:"-    AB.skipMany (ABC.char ' ')-    res <- ABC.scientific-    AB.skipMany (ABC.char ' ')-    ABC.char '\r'-    ABC.char '\n'-    ABC.string "Content-Type:"-    AB.skipMany (ABC.char ' ')-    typ <- AB.manyTill ABC.letter_ascii (ABC.char '\r')-    AB.skipMany (ABC.char ' ')-    ABC.char '\n'-    ABC.char '\r'-    ABC.char '\n'-    return $ (fromJust $ ((toBoundedInteger res) :: Maybe Int), typ)---intProg :: Int -> Int -> IO (Maybe Int, ())-intProg x y = do-    putStrLn . show $ y-    return (Just x, ())--intParse :: String -> Maybe Int-intParse _ = Just 1---- TODO: Handle incorrect request.--- TODO: Send correct error messages.-serve :: (Eq o, Monoid o, ToJSON o, Eq p, ToJSON p, ToJSON c, ExplorerPostValue p c o) => String -> Ex.Explorer p IO c o -> (String -> Maybe p) -> IO ()-serve port ex parser = withSocketsDo $ do-    addr <- resolve port-    E.bracket (open addr) close loop-  where-    resolve port = do-        let hints = defaultHints {-                addrFlags = [AI_PASSIVE]-              , addrSocketType = Stream-              }-        addr:_ <- getAddrInfo (Just hints) Nothing (Just port)-        return addr-    open addr = do-        sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)-        setSocketOption sock ReuseAddr 1-        bind sock (addrAddress addr)-        -- If the prefork technique is not used,-        -- set CloseOnExec for the security reasons.-        fd <- fdSocket sock-        setCloseOnExecIfNeeded fd-        listen sock 10-        return sock-    loop sock = forever $ do-        (conn, peer) <- accept sock-        putStrLn $ "Connection from " ++ show peer-        forkFinally (talk ex parser conn) (\_ -> close conn)-    talk ex parser conn = do-        putStrLn "Hello receiving"-        msg <- recv conn 1024-        unless (S.null msg) $ do-            ex' <- acceptCommand ex parser conn msg-            talk ex' parser conn--acceptCommand ex parser conn command = do-    let res = AB.parse parseHeader command-    putStrLn $ show res-    (result, toParse) <- case res of-        (AB.Done rem (val, _)) -> do-            case S.length rem < (fromIntegral val) of-                True -> do-                    msg <- recv conn 1024-                    return (Nothing, S.append command msg)-                False -> do-                    let command = S.take (fromIntegral val) rem-                    putStrLn "-------------------------"-                    putStrLn $ show command-                    putStrLn "-------------------------"-                    out <- runRWST (handleRequest' command) parser ex-                    return (Just out, S.drop (fromIntegral val) rem)-        (AB.Fail _ _ "not enough input") -> do-            msg <- recv conn 1024-            return (Nothing, S.append command msg)-        _ ->  return (Just (invalidHeader, ex, ""), "")-    case result of-        Nothing -> if toParse == "" then return ex else acceptCommand ex parser conn toParse-        Just (resp, ex', log) -> do-            let encoded_resp = encode resp-            let full_resp = S.concat ["Content-Length:", encode $ S.length encoded_resp, "\r\nContent-Type: jrpcei\r\n\r\n", encoded_resp]-            sendAll conn full_resp-            putStrLn $ show full_resp-            putStrLn $ show toParse-            if toParse == "" then return ex' else acceptCommand ex' parser conn toParse+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+
+
+module Language.Explorer.Tools.Protocol where
+
+import GHC.Generics
+import Data.Monoid
+import Data.Aeson
+import Network.HTTP.Types.Status
+import Network.HTTP.Types.Header (hContentType)
+import Data.Maybe
+import Control.Concurrent (forkFinally)
+import qualified Control.Exception as E
+import Control.Monad (unless, forever, void)
+import qualified Data.ByteString.Lazy as S
+import qualified Data.Attoparsec.ByteString.Lazy as AB
+import qualified Data.Attoparsec.ByteString.Char8 as ABC
+import Network.Socket hiding (recv)
+import Network.Socket.ByteString.Lazy (recv, sendAll)
+import Data.Scientific
+import qualified Language.Explorer.Monadic as Ex
+import Control.Monad.RWS.Lazy hiding (listen)
+import Control.Monad.Trans.Except
+import Data.List
+
+type ExplorerParser p m c o = (Ex.Explorer p m c o, String -> Maybe p)
+type ProcessResult = Either ErrorMessage Value
+
+type EIP p m c o = RWST (String -> Maybe p) S.ByteString (Ex.Explorer p m c o) m
+
+class ExplorerPostValue p c o where
+    postExecute :: Ex.Explorer p m c o -> Ex.Explorer p m c o -> o -> Value
+    postExecute = \_ _ _ -> Null
+    postJump :: Ex.Explorer p m c o -> Ex.Explorer p m c o -> Value
+    postJump = \_ _ -> Null
+    postRevert :: Ex.Explorer p m c o -> Ex.Explorer p m c o -> [Ex.Ref] -> Value
+    postRevert = \ _ _ _ -> Null
+
+data RequestMessage = RequestMessage {
+    jsonrpc :: String,
+    req_id :: String,
+    method :: String,
+    params :: Maybe Value
+} deriving (Show, Generic)
+
+instance ToJSON RequestMessage where
+    toEncoding = genericToEncoding defaultOptions
+
+instance FromJSON RequestMessage where
+    parseJSON = withObject "RequestMessage" $ \v -> RequestMessage
+        <$> v .: "jsonrpc"
+        <*> v .: "id"
+        <*> v .: "method"
+        <*> v .:? "params"
+
+data ResponseMessage = ResponseMessage {
+    res_id :: String,
+    body :: ProcessResult
+} deriving (Show)
+
+instance ToJSON ResponseMessage where
+    toJSON (ResponseMessage res_id (Left e)) = object ["id" .= res_id, "error" .= e]
+    toJSON (ResponseMessage res_id (Right res)) = object ["id" .= res_id, "result" .= res]
+
+    toEncoding (ResponseMessage res_id (Left e)) = pairs ("id" .= res_id <> "error" .= e)
+    toEncoding (ResponseMessage res_id (Right res)) = pairs ("id" .= res_id <> "result" .= res)
+
+
+data ErrorMessage = ErrorMessage {
+    code :: Int,
+    message :: String,
+    error_data :: Maybe Value
+} deriving (Show, Generic)
+
+instance ToJSON ErrorMessage where
+    toEncoding = genericToEncoding defaultOptions
+
+instance FromJSON ErrorMessage
+    -- No need to provide a parseJSON implementation.
+
+-- instance Except ErrorMessage 
+-- where
+--     noMsg = ErrorMessage { code = 0, message = "", error_data = Nothing}
+--     strMsg msg = ErrorMessage {code = 0, message = msg, error_data = Nothing }
+
+data JumpParams = JumpParams {
+    jump_ref :: Int
+}
+
+instance FromJSON JumpParams where
+    parseJSON = withObject "JumpParams" $ \v -> JumpParams
+        <$> v .: "reference"
+
+data JumpResult = JumpResult {
+    jump_post :: Value
+}
+
+instance ToJSON JumpResult where 
+    toJSON res = object ["post" .= jump_post res]
+
+data ExecuteParams = ExecuteParams {
+    paramsProgram :: String
+} deriving (Show, Generic)
+
+instance FromJSON ExecuteParams
+
+data ExecuteResult = ExecuteResult {
+    exec_ref :: Int,
+    exec_out :: Value,
+    exec_post :: Value
+}
+
+instance ToJSON ExecuteResult where
+    toJSON (ExecuteResult ref out post) = object ["reference" .= ref, "output" .= out, "post" .= post]
+
+    toEncoding (ExecuteResult ref out post) = pairs ("reference" .= ref <> "output" .= out <> "post" .= post)
+
+data RevertParams = RevertParams {
+    revert_ref :: Int
+}
+
+instance FromJSON RevertParams where
+    parseJSON = withObject "RevertParams" $ \v -> RevertParams
+        <$> v .: "reference"
+
+data RevertResult = RevertResult {
+    revert_deleted :: [Ex.Ref],
+    post_revert :: Value
+}
+
+instance ToJSON RevertResult where 
+    toJSON res = object ["deleted" .= revert_deleted res, "post" .= post_revert res]
+
+data DerefParams = DerefParams {
+    deref_ref :: Int
+}
+
+instance FromJSON DerefParams where
+    parseJSON = withObject "DerefParams" $ \v -> DerefParams
+        <$> v .: "reference"
+
+
+data TraceParams = TraceParams {
+    reference :: Int
+} deriving (Generic)
+
+instance FromJSON TraceParams
+
+
+data Edge = Edge {
+    source :: Int,
+    target :: Int,
+    label  :: EdgeLabel
+} deriving (Generic)
+
+instance ToJSON Edge where
+    toEncoding = genericToEncoding defaultOptions
+
+
+data EdgeLabel = EdgeLabel {
+    program :: Value,
+    mval :: Value
+} deriving (Generic)
+
+instance ToJSON EdgeLabel where
+    toEncoding = genericToEncoding defaultOptions
+
+
+data ExecutionTree = ExecutionTree {
+    current :: Int,
+    references :: [Int],
+    edges :: [Edge]
+} deriving (Generic)
+
+instance ToJSON ExecutionTree where
+    toEncoding = genericToEncoding defaultOptions
+
+data PathParams = PathParams {
+    paramsSource :: Int,
+    paramsTarget :: Int
+} deriving (Generic)
+
+instance FromJSON PathParams
+
+parseErrorCode = -32700
+invalidRequestCode = -32600
+methodNotFoundCode = -32601
+invalidParamsCode = -32602
+internalErrorCode = -32603
+
+referenceNotInTreeCode = 1
+referenceRevertInvalidCode = 2
+programParseErrorCode = 3
+pathNonExistingCode = 4
+
+
+
+parseError :: ErrorMessage
+parseError = ErrorMessage {
+    code = parseErrorCode,
+    message = "Parse error",
+    error_data = Nothing
+}
+
+methodNotFound :: ErrorMessage
+methodNotFound = ErrorMessage {
+    code = methodNotFoundCode,
+    message = "Method not found",
+    error_data = Nothing
+}
+
+invalidParams :: ErrorMessage
+invalidParams = ErrorMessage {
+    code = invalidParamsCode,
+    message = "Invalid method parameter(s)",
+    error_data = Nothing
+}
+
+ensureParameter :: Monad m => Maybe Value -> ExceptT ErrorMessage (EIP p m c o) Value
+ensureParameter Nothing = throwE invalidParams
+ensureParameter (Just v) = return v
+
+fromResult :: Monad m => Result a -> Value -> ExceptT ErrorMessage (EIP p m c o) Value
+fromResult res onSuccess = case res of
+    (Error e) -> throwE invalidParams
+    (Success v) -> return onSuccess
+
+jump :: (Monad m, ExplorerPostValue p c o) => Value -> ExceptT ErrorMessage (EIP p m c o) Value
+jump v = case (fromJSON v) :: Result JumpParams of
+    (Error e) -> throwE invalidParams
+    (Success v') -> do
+        ex <- lift $ get
+        case Ex.jump (jump_ref v') ex of
+            Just ex' -> do
+                lift $ put $ ex'
+                return . toJSON . JumpResult $ postJump ex ex'
+            Nothing -> throwE ErrorMessage { code = referenceNotInTreeCode, message = "", error_data = Nothing }
+
+execute :: (Eq o, Monoid o, ToJSON o, Eq p, ExplorerPostValue p c o) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value
+execute v = case (fromJSON v) :: Result ExecuteParams of
+    (Error e) -> throwE invalidParams
+    (Success v') -> do
+        parser <- lift $ ask
+        let pl = parser $ paramsProgram v'
+        case pl of
+            Just prog -> do
+                ex <- lift $ get
+                (ex', output) <- liftIO $ Ex.execute prog ex
+                lift $ put ex'
+                return $ toJSON $ ExecuteResult { exec_ref = Ex.currRef ex', exec_out = toJSON output, exec_post = postExecute ex ex' output }
+            Nothing -> throwE ErrorMessage { code = programParseErrorCode, message = "", error_data = Nothing }
+
+allRefs :: Ex.Explorer p IO c o -> [(Ex.Ref, c)]
+allRefs ex = refs
+    where
+        (_, refs, _) = Ex.executionGraph ex
+
+
+revert :: (Eq o, Monoid o, Eq p, ExplorerPostValue p c o) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value
+revert v = case (fromJSON v) :: Result RevertParams of
+    (Error e) -> throwE invalidParams
+    (Success v) -> do
+        ex <- lift $ get
+        case Ex.revert (revert_ref v) ex of
+            Just ex' -> do
+                lift $ put ex'
+                return $ toJSON $ RevertResult { revert_deleted = deleted, post_revert = postRevert ex ex' deleted}
+                where 
+                    refs = map fst (allRefs ex)
+                    refs' = map fst (allRefs ex')
+                    deleted = (refs \\ refs')
+            Nothing -> throwE ErrorMessage { code = referenceRevertInvalidCode, message = "", error_data = Nothing }
+
+deref :: (Eq o, Monoid o, Eq p, ToJSON c) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value
+deref v = case (fromJSON v) :: Result DerefParams of
+    (Error e) -> throwE invalidParams
+    (Success v) -> do
+        ex <- lift $ get
+        case Ex.deref ex (deref_ref v) of
+            (Just conf) -> return $ toJSON conf
+            Nothing -> throwE ErrorMessage { code = referenceNotInTreeCode, message = "", error_data = Nothing}
+
+executionTree :: (ToJSON o, ToJSON p) => ExceptT ErrorMessage (EIP p IO c o) Value
+executionTree = do
+    ex <- lift $ get
+    let (curr, nodes, edges) = Ex.executionGraph ex
+    return $ toJSON $ ExecutionTree 
+        { current = fst curr
+        , references = map fst nodes
+        , edges = map (\(s, (p, o), t) -> Edge { source = fst s
+        , label = EdgeLabel { program = toJSON p, mval = toJSON o}
+        , target = fst t} ) edges}
+
+getCurrentReference :: ExceptT ErrorMessage (EIP p IO c o) Value
+getCurrentReference = do
+    ex <- lift $ get
+    return $ toJSON $ Ex.currRef ex
+
+getAllReferences :: ExceptT ErrorMessage (EIP p IO c o) Value
+getAllReferences = do
+    ex <- lift $ get
+    return $ toJSON $ map fst (allRefs ex)
+
+getTrace :: (ToJSON p, ToJSON o) => Maybe Value -> ExceptT ErrorMessage (EIP p IO c o) Value
+getTrace (Just r) = case (fromJSON r) :: Result TraceParams of
+    (Error e) -> throwE invalidParams
+    (Success v) -> do
+        ex <- lift $ get
+        let path = Ex.getPathFromTo ex 1 (reference (v :: TraceParams)) -- Fix hardcode 1(it's initialRef).
+        return $ toJSON $ map (\(s, (p, o), t) -> Edge { source = fst s, target = fst t, label = EdgeLabel { program = toJSON p, mval = toJSON o} }) path
+getTrace Nothing = do
+    ex <- lift $ get
+    let trace = Ex.getTrace ex
+    return $ toJSON $ map (\(s, (p, o), t) -> Edge { source = fst s, target = fst t, label = EdgeLabel { program = toJSON p, mval = toJSON o} }) trace
+
+getPath :: (ToJSON o, ToJSON p) => Value -> ExceptT ErrorMessage (EIP p IO c o) Value
+getPath val = case (fromJSON val) :: Result PathParams of
+    (Error e) -> throwE ErrorMessage { code = pathNonExistingCode, message = "", error_data = Nothing}
+    (Success v) -> do
+        ex <- lift $ get
+        let path = Ex.getPathFromTo ex (paramsSource v) (paramsTarget v)
+        return $ toJSON $ map (\(s, (p, o), t) -> Edge { source = fst s, target = fst t, label = EdgeLabel { program = toJSON p, mval = toJSON o} }) path
+
+getLeaves :: ExceptT ErrorMessage (EIP p IO c o) Value
+getLeaves = do
+    ex <- lift $ get
+    return $ toJSON $ map fst (Ex.leaves ex)
+
+methodDispatch :: (Eq o, Monoid o, ToJSON o, ToJSON p, Eq p, ToJSON c, ExplorerPostValue p c o) => String -> Maybe Value -> ExceptT ErrorMessage (EIP p IO c o) Value
+methodDispatch "jump" mval = ensureParameter mval >>= jump
+methodDispatch "execute" mval = ensureParameter mval >>= execute
+methodDispatch "revert" mval = ensureParameter mval >>= revert
+methodDispatch "deref" mval = ensureParameter mval >>= deref
+methodDispatch "getTrace" mval = getTrace mval
+methodDispatch "getPath" mval = ensureParameter mval >>= getPath
+methodDispatch "getExecutionTree" _ = executionTree
+methodDispatch "getCurrentReference" _ = getCurrentReference
+methodDispatch "getAllReferences" _ = getAllReferences
+methodDispatch "getLeaves" _ = getLeaves
+methodDispatch _ _ = throwE methodNotFound
+
+handleRequest :: (Eq o, Monoid o, ToJSON o, ToJSON o, ToJSON p, Eq p, ToJSON c, ExplorerPostValue p c o) => Maybe RequestMessage -> EIP p IO c o ResponseMessage
+handleRequest (Just msg) = do
+    res <- runExceptT $ methodDispatch (method msg) (params msg)
+    return $ ResponseMessage { res_id = req_id msg, body = res }
+handleRequest Nothing = return $ ResponseMessage { res_id = "0", body = Left parseError { message = "NOthing" }}
+
+
+handleRequest' :: (Eq o, Monoid o, ToJSON o, Eq p, ToJSON p, ToJSON c, ExplorerPostValue p c o) => S.ByteString -> EIP p IO c o ResponseMessage
+handleRequest' body =
+    case decode body of
+        (Just m) -> handleRequest m
+        Nothing -> return invalidHeader
+
+invalidHeader :: ResponseMessage
+invalidHeader = ResponseMessage { res_id = "0", body = Left parseError {message = "Headeer"} }
+
+parseHeader :: AB.Parser (Int, String)
+parseHeader = do
+    AB.string "Content-Length:"
+    AB.skipMany (ABC.char ' ')
+    res <- ABC.scientific
+    AB.skipMany (ABC.char ' ')
+    ABC.char '\r'
+    ABC.char '\n'
+    ABC.string "Content-Type:"
+    AB.skipMany (ABC.char ' ')
+    typ <- AB.manyTill ABC.letter_ascii (ABC.char '\r')
+    AB.skipMany (ABC.char ' ')
+    ABC.char '\n'
+    ABC.char '\r'
+    ABC.char '\n'
+    return $ (fromJust $ ((toBoundedInteger res) :: Maybe Int), typ)
+
+
+intProg :: Int -> Int -> IO (Maybe Int, ())
+intProg x y = do
+    putStrLn . show $ y
+    return (Just x, ())
+
+intParse :: String -> Maybe Int
+intParse _ = Just 1
+
+-- TODO: Handle incorrect request.
+-- TODO: Send correct error messages.
+serve :: (Eq o, Monoid o, ToJSON o, Eq p, ToJSON p, ToJSON c, ExplorerPostValue p c o) => String -> Ex.Explorer p IO c o -> (String -> Maybe p) -> IO ()
+serve port ex parser = withSocketsDo $ do
+    addr <- resolve port
+    E.bracket (open addr) close loop
+  where
+    resolve port = do
+        let hints = defaultHints {
+                addrFlags = [AI_PASSIVE]
+              , addrSocketType = Stream
+              }
+        addr:_ <- getAddrInfo (Just hints) Nothing (Just port)
+        return addr
+    open addr = do
+        sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
+        setSocketOption sock ReuseAddr 1
+        bind sock (addrAddress addr)
+        -- If the prefork technique is not used,
+        -- set CloseOnExec for the security reasons.
+        fd <- fdSocket sock
+        setCloseOnExecIfNeeded fd
+        listen sock 10
+        return sock
+    loop sock = forever $ do
+        (conn, peer) <- accept sock
+        putStrLn $ "Connection from " ++ show peer
+        forkFinally (talk ex parser conn) (\_ -> close conn)
+    talk ex parser conn = do
+        putStrLn "Hello receiving"
+        msg <- recv conn 1024
+        unless (S.null msg) $ do
+            ex' <- acceptCommand ex parser conn msg
+            talk ex' parser conn
+
+acceptCommand ex parser conn command = do
+    let res = AB.parse parseHeader command
+    putStrLn $ show res
+    (result, toParse) <- case res of
+        (AB.Done rem (val, _)) -> do
+            case S.length rem < (fromIntegral val) of
+                True -> do
+                    msg <- recv conn 1024
+                    return (Nothing, S.append command msg)
+                False -> do
+                    let command = S.take (fromIntegral val) rem
+                    putStrLn "-------------------------"
+                    putStrLn $ show command
+                    putStrLn "-------------------------"
+                    out <- runRWST (handleRequest' command) parser ex
+                    return (Just out, S.drop (fromIntegral val) rem)
+        (AB.Fail _ _ "not enough input") -> do
+            msg <- recv conn 1024
+            return (Nothing, S.append command msg)
+        _ ->  return (Just (invalidHeader, ex, ""), "")
+    case result of
+        Nothing -> if toParse == "" then return ex else acceptCommand ex parser conn toParse
+        Just (resp, ex', log) -> do
+            let encoded_resp = encode resp
+            let full_resp = S.concat ["Content-Length:", encode $ S.length encoded_resp, "\r\nContent-Type: jrpcei\r\n\r\n", encoded_resp]
+            sendAll conn full_resp
+            putStrLn $ show full_resp
+            putStrLn $ show toParse
+            if toParse == "" then return ex' else acceptCommand ex' parser conn toParse
Language/Explorer/Tools/REPL.hs view
@@ -1,73 +1,73 @@-module Language.Explorer.Tools.REPL where--import Control.Monad.IO.Class (MonadIO(..))-import Language.Explorer.Monadic-    (Explorer(config), execute, revert, jump, toTree)-import qualified System.Console.Haskeline as Hl-import Data.Tree (drawTree)-import Data.Maybe (fromJust, isNothing)-import Data.Char (isSpace)-import Data.List (find, isPrefixOf)-import Text.Read (readMaybe)-import Control.Arrow (Arrow(first))-import Control.Monad.Trans-import Control.Monad.Catch---type MetaTable p m c o = [(String, String -> Explorer p m c o -> m (Explorer p m c o))]-type RParser p c = String -> c -> Maybe p-type Prompt p m c o = Explorer p m c o -> String-type MetaHandler p m c o = String -> Explorer p m c o -> m (Explorer p m c o)-type OutputHandler m o = o -> m ()-type Repl p m c o = Prompt p m c o -> RParser p c -> String -> MetaTable p m c o -> MetaHandler p m c o -> OutputHandler m o -> Explorer p m c o -> m ()---handleJump :: MonadIO m => String -> Explorer p m c o -> m (Explorer p m c o)-handleJump input ex = case readMaybe (dropWhile isSpace input) of-  (Just ref_id) -> case jump ref_id ex of-    (Just ex') -> return ex'-    Nothing -> liftIO $ putStrLn "Given reference is not in the exploration tree." >> return ex-  Nothing -> liftIO $ putStrLn "the jump command requires an integer argument." >> return ex--handleRevert :: MonadIO m => MetaHandler p m c o-handleRevert input ex = case readMaybe (dropWhile isSpace input) of-  (Just ref_id) -> case revert ref_id ex of-    (Just ex') -> do-      liftIO $ putStrLn "reverting"-      return ex'-    Nothing -> liftIO $ putStrLn "Given reference is not valid for reverting." >> return ex-  Nothing -> liftIO $ putStrLn "the jump command requires an integer argument." >> return ex--handleTree :: MonadIO m => MetaHandler p m c o-handleTree input ex = do-  liftIO $ putStrLn . drawTree $ fmap (show . fst) (toTree ex)-  return ex--metaTable :: MonadIO m => [(String, String -> Explorer p m c o -> m (Explorer p m c o))]-metaTable = [-  ("jump", handleJump),-  ("revert", handleRevert),-  ("tree", handleTree)]--constructMetaTable :: MonadIO m => String -> [(String, String -> Explorer p m c o -> m (Explorer p m c o))]-constructMetaTable prefix = map (first (prefix ++ )) metaTable--repl :: (Eq p, Eq o, Monoid o, MonadIO m, MonadMask m) => Repl p m c o-repl prompt parser metaPrefix metaTable metaHandler outputHandler ex =-  Hl.runInputT Hl.defaultSettings (loop ex)-    where-    loop ex = do-      minput <- Hl.getInputLine . prompt $ ex-      case minput of-        (Just input) -> lift (if metaPrefix `isPrefixOf` input then runMeta input else runExec input) >>= loop-        Nothing -> return ()-      where-        runMeta input =-          let (pcmd, args) = break isSpace input in-            case find (\(cmd, _) -> (metaPrefix ++ cmd) == pcmd) metaTable of-              Just (_, f) -> f args ex-              Nothing -> metaHandler input ex-        runExec input =-          case parser input (config ex) of-            (Just program) -> execute program ex >>= \(newEx, out) -> outputHandler out >> return newEx-            Nothing -> return ex+module Language.Explorer.Tools.REPL where
+
+import Control.Monad.IO.Class (MonadIO(..))
+import Language.Explorer.Monadic
+    (Explorer(config), execute, revert, jump, toTree)
+import qualified System.Console.Haskeline as Hl
+import Data.Tree (drawTree)
+import Data.Maybe (fromJust, isNothing)
+import Data.Char (isSpace)
+import Data.List (find, isPrefixOf)
+import Text.Read (readMaybe)
+import Control.Arrow (Arrow(first))
+import Control.Monad.Trans
+import Control.Monad.Catch
+
+
+type MetaTable p m c o = [(String, String -> Explorer p m c o -> m (Explorer p m c o))]
+type RParser p c = String -> c -> Maybe p
+type Prompt p m c o = Explorer p m c o -> String
+type MetaHandler p m c o = String -> Explorer p m c o -> m (Explorer p m c o)
+type OutputHandler m o = o -> m ()
+type Repl p m c o = Prompt p m c o -> RParser p c -> String -> MetaTable p m c o -> MetaHandler p m c o -> OutputHandler m o -> Explorer p m c o -> m ()
+
+
+handleJump :: MonadIO m => String -> Explorer p m c o -> m (Explorer p m c o)
+handleJump input ex = case readMaybe (dropWhile isSpace input) of
+  (Just ref_id) -> case jump ref_id ex of
+    (Just ex') -> return ex'
+    Nothing -> liftIO $ putStrLn "Given reference is not in the exploration tree." >> return ex
+  Nothing -> liftIO $ putStrLn "the jump command requires an integer argument." >> return ex
+
+handleRevert :: MonadIO m => MetaHandler p m c o
+handleRevert input ex = case readMaybe (dropWhile isSpace input) of
+  (Just ref_id) -> case revert ref_id ex of
+    (Just ex') -> do
+      liftIO $ putStrLn "reverting"
+      return ex'
+    Nothing -> liftIO $ putStrLn "Given reference is not valid for reverting." >> return ex
+  Nothing -> liftIO $ putStrLn "the jump command requires an integer argument." >> return ex
+
+handleTree :: MonadIO m => MetaHandler p m c o
+handleTree input ex = do
+  liftIO $ putStrLn . drawTree $ fmap (show . fst) (toTree ex)
+  return ex
+
+metaTable :: MonadIO m => [(String, String -> Explorer p m c o -> m (Explorer p m c o))]
+metaTable = [
+  ("jump", handleJump),
+  ("revert", handleRevert),
+  ("tree", handleTree)]
+
+constructMetaTable :: MonadIO m => String -> [(String, String -> Explorer p m c o -> m (Explorer p m c o))]
+constructMetaTable prefix = map (first (prefix ++ )) metaTable
+
+repl :: (Eq p, Eq o, Monoid o, MonadIO m, MonadMask m) => Repl p m c o
+repl prompt parser metaPrefix metaTable metaHandler outputHandler ex =
+  Hl.runInputT Hl.defaultSettings (loop ex)
+    where
+    loop ex = do
+      minput <- Hl.getInputLine . prompt $ ex
+      case minput of
+        (Just input) -> lift (if metaPrefix `isPrefixOf` input then runMeta input else runExec input) >>= loop
+        Nothing -> return ()
+      where
+        runMeta input =
+          let (pcmd, args) = break isSpace input in
+            case find (\(cmd, _) -> (metaPrefix ++ cmd) == pcmd) metaTable of
+              Just (_, f) -> f args ex
+              Nothing -> metaHandler input ex
+        runExec input =
+          case parser input (config ex) of
+            (Just program) -> execute program ex >>= \(newEx, out) -> outputHandler out >> return newEx
+            Nothing -> return ex
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple
+main = defaultMain
examples/Whilelang.hs view
@@ -1,248 +1,248 @@-module Whilelang where--import qualified Data.Map as Map-import Data.List-import Data.Maybe-import Data.Graph.Inductive (emap)-import Control.Monad.Trans.Writer.Lazy-import Control.Monad.Trans.State.Lazy-import Control.Monad.Trans.Class-import Control.Monad.Identity-import qualified Language.Explorer.Basic as E-import qualified Language.Explorer.Pure as EP-import qualified Language.Explorer.Monadic as EM-import qualified Language.Explorer.Tools.REPL as R-import Language.Explorer.Basic (mkExplorerNoSharing)----data Literal = LitBool Bool | LitInt Integer deriving (Eq, Read)-instance Show Literal where-    show (LitBool b) = show b-    show (LitInt i) = show i--data Expr = Leq Expr Expr | Plus Expr Expr | LitExpr Literal | Id String deriving (Eq, Read)-instance Show Expr where-    show (Leq e1 e2) = show e1 ++ "<=" ++ show e2-    show (Plus e1 e2) = show e1 ++ "+" ++ show e2-    show (LitExpr lit) = show lit-    show (Id s) = s--data Command = Seq Command Command | Assign String Expr | Print Expr | While Expr Expr Command | Done deriving (Eq, Read)-instance Show Command where-    show (Print e1) = "print(" ++ show e1 ++ ")"-    show Done = "Done"-    show (Assign s e) = s ++ " = " ++ show e-    show (Seq c1 c2) = show c1 ++ "\n" ++ show c2-    show (While e1 e2 c) = "while(" ++ show e2 ++ ") do\n" ++ show c ++ "\nod"--type Store = Map.Map String Literal-type StoreM = State Store-type Output = [String]-data Config = Config { cfgStore :: Store, cfgOutput :: Output } deriving (Show, Eq)--type WhileExplorer = E.Explorer Command Config-type WhileExplorerM = EM.Explorer Command IO Config ()-type WhileExplorerO = EP.Explorer Command Config [String]--evalPlus :: Expr -> Expr -> StoreM Expr-evalPlus (LitExpr (LitInt l1)) (LitExpr (LitInt l2)) = return $ LitExpr $ LitInt (l1 + l2)-evalPlus (LitExpr l1) l2 = do-    l2' <- evalExpr l2-    return (Plus (LitExpr l1) l2')-evalPlus l1 l2 = do-    l1' <- evalExpr l1-    return (Plus l1' l2)--evalLeq :: Expr -> Expr -> StoreM Expr-evalLeq (LitExpr (LitInt l1)) (LitExpr (LitInt l2)) = return $ LitExpr $ LitBool (l1 <= l2)-evalLeq (LitExpr l1) e2 = do-    e2' <- evalExpr e2-    return (Leq (LitExpr l1) e2')-evalLeq e1 e2 = do-    e1' <- evalExpr e1-    return (Leq e1' e2)--evalExpr :: Expr -> StoreM Expr-evalExpr (LitExpr e) = return $ LitExpr e-evalExpr (Plus e1 e2) = evalPlus e1 e2-evalExpr (Leq e1 e2) = evalLeq e1 e2-evalExpr (Id s) = do-    m <- get-    let l = Map.lookup s m-    case l of-        Just lit -> return $ LitExpr lit-        Nothing -> error $ "Invalid Id: " ++ s--evalExpr' :: Expr -> StoreM Expr-evalExpr' (LitExpr e) = return $ LitExpr e-evalExpr' e = do-    e' <- evalExpr e-    evalExpr' e'--store :: String -> Expr -> StoreM Command-store s (LitExpr l) = do-    lut <- get-    put $ Map.insert s l lut-    return Done--evalCommand :: Command -> WriterT [String] StoreM Command-evalCommand (Print e) = do-    x <- (lift . evalExpr') e-    tell [show x]-    return Done-evalCommand (Assign id e) = do-    lit <- (lift . evalExpr') e-    lift $ store id lit-evalCommand (Seq Done c2) = return c2-evalCommand (Seq c1 c2) = do-    c1' <- evalCommand c1-    return $ Seq c1' c2-evalCommand (While (LitExpr (LitBool False)) e2 c) = return Done-evalCommand (While (LitExpr (LitBool True)) e2 c) = return $ Seq c (While e2 e2 c)-evalCommand (While e1 e2 c) = do-    e1' <- (lift . evalExpr') e1-    return $ While e1' e2 c---evalCommand' :: Command -> WriterT [String] StoreM Command-evalCommand' Done = return Done-evalCommand' c = do-    c' <- evalCommand c-    evalCommand' c'---- Initial configuration in the while language)-initialConfig :: Config-initialConfig = Config {cfgStore = Map.empty, cfgOutput = []}---- Definitial interpreter for the while language.-definterp :: Command -> Config -> Maybe Config-definterp c cfg = Just cfg {cfgStore = newstore, cfgOutput = cfgOutput cfg ++ newout}-    where ((_, newout), newstore) = runState (runWriterT (evalCommand' c)) (cfgStore cfg)---definterpO :: Command -> Config -> (Maybe Config, [String])-definterpO c cfg = (Just $ cfg {cfgStore = newstore}, newout)-    where ((_, newout), newstore) = runState (runWriterT (evalCommand' c)) (cfgStore cfg)----- Simulate doing IO in the definitional interpreter.-definterpM :: Command -> Config -> IO (Maybe Config, ())-definterpM c cfg = do-    mapM putStrLn newout-    return (Just $ cfg {cfgStore = newstore, cfgOutput = []}, ())-    where ((_, newout), newstore) = runState (runWriterT (evalCommand' c)) (cfgStore cfg)---parser :: String -> c -> Maybe Command-parser s _ = Just (read s :: Command)--repl = R.repl (const "While> ") parser ":" R.metaTable  (\_ ex -> return ex) (\_ -> return ()) (EM.mkExplorerNoSharing definterpM initialConfig)---- whileLang = (Command, Config, initialConfig, definterp)----do_ :: Command -> WhileExplorer -> IO WhileExplorer-do_ (Seq c1 c2) e = do_ c1 e >>= do_ c2-do_ p e = do-    let e' = E.execute p e-    putStr $ unlines $ cfgOutput (E.config e') \\ cfgOutput (E.config e)-    return e'--do_2 :: Command -> WhileExplorerM -> IO WhileExplorerM-do_2 (Seq c1 c2) e = do_2 c1 e >>= do_2 c2-do_2 p e = fst <$> EM.execute p e--do_3 :: Command -> (WhileExplorerO, [String]) -> (WhileExplorerO, [String])-do_3 (Seq c1 c2) e = do_3 c2 $ do_3 c1 e-do_3 p (e, o) = (e', o ++ o')-  where (e', o') = EP.execute p e--start :: IO WhileExplorer-start = return whileExplorer---- startM :: IO WhileExplorerM--- startM = return whileTree---- startO :: WhileExplorerO--- startO = whileGraphO--session1 :: IO WhileExplorer-session1 = start >>=-  do_ (assign "x" (intToExpr 1)) >>=-  do_ (assign "y" (Id "x")) >>=-  do_ (assign "x" (intToExpr 1)) >>=-  do_ (Print (Id "y")) >>=-  do_ (Print (Id "x")) . fromJust . EM.jump 2 >>=-  do_ (assign "z" (intToExpr 100)) >>=-  do_ (Print (Id "z"))---sessionS :: IO WhileExplorer-sessionS = start >>=-  do_ (assign "x" (intToExpr 1)) >>=-  do_ (assign "y" (Id "x")) >>=-  do_ (assign "x" (intToExpr 1)) >>=-  do_ (assign "z" (intToExpr 20)) >>=-  do_ (assign "y" (Id "x"))------ -- When using sharing, this results in 3 configurations and not 4,--- -- since the IO effect is hidden in the monad and not part of the--- -- configurations anymore.--- session2 :: IO WhileExplorerM--- session2 = startM >>=---   do_2 (assign "x" (intToExpr 1)) >>=---   do_2 (assign "y" (Id "x")) >>=---   do_2 (Print (Id "y"))----- session3 :: (WhileExplorerO, [String])--- session3 =---   do_3 (Print (Id "y")) $ do_3 (assign "y" (Id "x")) $ do_3 (assign "x" (intToExpr 1)) (startO, [])---- Below are some helpers to create a Command and fully evaluate it.--- Example:--- ghci> let x = wprint (intToExpr 10) `wseq` (wprint (intToExpr 100) `wseq` wprint (intToExpr 200))--- ghci> runCommand' x--- ["10","100","200"]--- ghci>-runCommand :: Command -> IO()-runCommand c = do-    let ((_, output), _) = runState (runWriterT (evalCommand' c)) Map.empty-    print output---intToExpr :: Integer -> Expr-intToExpr = LitExpr . LitInt--boolToExpr :: Bool -> Expr-boolToExpr = LitExpr . LitBool--while ::  Expr -> Command -> Command-while e = While e e--leq :: Expr -> Expr -> Expr-leq = Leq--wprint :: Expr -> Command-wprint = Print--plus :: Expr -> Expr -> Expr-plus = Plus--assign :: String -> Expr -> Command-assign = Assign--wseq :: Command -> Command -> Command-wseq = Seq--whileExplorer :: WhileExplorer-whileExplorer = E.mkExplorer True (==) definterp initialConfig--whileExample = Seq (Assign "x" (intToExpr 0)) (while (Leq (Id "x") (intToExpr 10)) (Seq (Assign "x" (Plus (Id "x") (intToExpr 1))) (Print (Id "x"))))--zero = intToExpr 0--getRef :: WhileExplorer -> E.Ref-getRef = E.currRef+module Whilelang where
+
+import qualified Data.Map as Map
+import Data.List
+import Data.Maybe
+import Data.Graph.Inductive (emap)
+import Control.Monad.Trans.Writer.Lazy
+import Control.Monad.Trans.State.Lazy
+import Control.Monad.Trans.Class
+import Control.Monad.Identity
+import qualified Language.Explorer.Basic as E
+import qualified Language.Explorer.Pure as EP
+import qualified Language.Explorer.Monadic as EM
+import qualified Language.Explorer.Tools.REPL as R
+import Language.Explorer.Basic (mkExplorerNoSharing)
+
+
+
+data Literal = LitBool Bool | LitInt Integer deriving (Eq, Read)
+instance Show Literal where
+    show (LitBool b) = show b
+    show (LitInt i) = show i
+
+data Expr = Leq Expr Expr | Plus Expr Expr | LitExpr Literal | Id String deriving (Eq, Read)
+instance Show Expr where
+    show (Leq e1 e2) = show e1 ++ "<=" ++ show e2
+    show (Plus e1 e2) = show e1 ++ "+" ++ show e2
+    show (LitExpr lit) = show lit
+    show (Id s) = s
+
+data Command = Seq Command Command | Assign String Expr | Print Expr | While Expr Expr Command | Done deriving (Eq, Read)
+instance Show Command where
+    show (Print e1) = "print(" ++ show e1 ++ ")"
+    show Done = "Done"
+    show (Assign s e) = s ++ " = " ++ show e
+    show (Seq c1 c2) = show c1 ++ "\n" ++ show c2
+    show (While e1 e2 c) = "while(" ++ show e2 ++ ") do\n" ++ show c ++ "\nod"
+
+type Store = Map.Map String Literal
+type StoreM = State Store
+type Output = [String]
+data Config = Config { cfgStore :: Store, cfgOutput :: Output } deriving (Show, Eq)
+
+type WhileExplorer = E.Explorer Command Config
+type WhileExplorerM = EM.Explorer Command IO Config ()
+type WhileExplorerO = EP.Explorer Command Config [String]
+
+evalPlus :: Expr -> Expr -> StoreM Expr
+evalPlus (LitExpr (LitInt l1)) (LitExpr (LitInt l2)) = return $ LitExpr $ LitInt (l1 + l2)
+evalPlus (LitExpr l1) l2 = do
+    l2' <- evalExpr l2
+    return (Plus (LitExpr l1) l2')
+evalPlus l1 l2 = do
+    l1' <- evalExpr l1
+    return (Plus l1' l2)
+
+evalLeq :: Expr -> Expr -> StoreM Expr
+evalLeq (LitExpr (LitInt l1)) (LitExpr (LitInt l2)) = return $ LitExpr $ LitBool (l1 <= l2)
+evalLeq (LitExpr l1) e2 = do
+    e2' <- evalExpr e2
+    return (Leq (LitExpr l1) e2')
+evalLeq e1 e2 = do
+    e1' <- evalExpr e1
+    return (Leq e1' e2)
+
+evalExpr :: Expr -> StoreM Expr
+evalExpr (LitExpr e) = return $ LitExpr e
+evalExpr (Plus e1 e2) = evalPlus e1 e2
+evalExpr (Leq e1 e2) = evalLeq e1 e2
+evalExpr (Id s) = do
+    m <- get
+    let l = Map.lookup s m
+    case l of
+        Just lit -> return $ LitExpr lit
+        Nothing -> error $ "Invalid Id: " ++ s
+
+evalExpr' :: Expr -> StoreM Expr
+evalExpr' (LitExpr e) = return $ LitExpr e
+evalExpr' e = do
+    e' <- evalExpr e
+    evalExpr' e'
+
+store :: String -> Expr -> StoreM Command
+store s (LitExpr l) = do
+    lut <- get
+    put $ Map.insert s l lut
+    return Done
+
+evalCommand :: Command -> WriterT [String] StoreM Command
+evalCommand (Print e) = do
+    x <- (lift . evalExpr') e
+    tell [show x]
+    return Done
+evalCommand (Assign id e) = do
+    lit <- (lift . evalExpr') e
+    lift $ store id lit
+evalCommand (Seq Done c2) = return c2
+evalCommand (Seq c1 c2) = do
+    c1' <- evalCommand c1
+    return $ Seq c1' c2
+evalCommand (While (LitExpr (LitBool False)) e2 c) = return Done
+evalCommand (While (LitExpr (LitBool True)) e2 c) = return $ Seq c (While e2 e2 c)
+evalCommand (While e1 e2 c) = do
+    e1' <- (lift . evalExpr') e1
+    return $ While e1' e2 c
+
+
+evalCommand' :: Command -> WriterT [String] StoreM Command
+evalCommand' Done = return Done
+evalCommand' c = do
+    c' <- evalCommand c
+    evalCommand' c'
+
+-- Initial configuration in the while language)
+initialConfig :: Config
+initialConfig = Config {cfgStore = Map.empty, cfgOutput = []}
+
+-- Definitial interpreter for the while language.
+definterp :: Command -> Config -> Maybe Config
+definterp c cfg = Just cfg {cfgStore = newstore, cfgOutput = cfgOutput cfg ++ newout}
+    where ((_, newout), newstore) = runState (runWriterT (evalCommand' c)) (cfgStore cfg)
+
+
+definterpO :: Command -> Config -> (Maybe Config, [String])
+definterpO c cfg = (Just $ cfg {cfgStore = newstore}, newout)
+    where ((_, newout), newstore) = runState (runWriterT (evalCommand' c)) (cfgStore cfg)
+
+
+-- Simulate doing IO in the definitional interpreter.
+definterpM :: Command -> Config -> IO (Maybe Config, ())
+definterpM c cfg = do
+    mapM putStrLn newout
+    return (Just $ cfg {cfgStore = newstore, cfgOutput = []}, ())
+    where ((_, newout), newstore) = runState (runWriterT (evalCommand' c)) (cfgStore cfg)
+
+
+parser :: String -> c -> Maybe Command
+parser s _ = Just (read s :: Command)
+
+repl = R.repl (const "While> ") parser ":" R.metaTable  (\_ ex -> return ex) (\_ -> return ()) (EM.mkExplorerNoSharing definterpM initialConfig)
+
+-- whileLang = (Command, Config, initialConfig, definterp)
+--
+do_ :: Command -> WhileExplorer -> IO WhileExplorer
+do_ (Seq c1 c2) e = do_ c1 e >>= do_ c2
+do_ p e = do
+    let e' = E.execute p e
+    putStr $ unlines $ cfgOutput (E.config e') \\ cfgOutput (E.config e)
+    return e'
+
+do_2 :: Command -> WhileExplorerM -> IO WhileExplorerM
+do_2 (Seq c1 c2) e = do_2 c1 e >>= do_2 c2
+do_2 p e = fst <$> EM.execute p e
+
+do_3 :: Command -> (WhileExplorerO, [String]) -> (WhileExplorerO, [String])
+do_3 (Seq c1 c2) e = do_3 c2 $ do_3 c1 e
+do_3 p (e, o) = (e', o ++ o')
+  where (e', o') = EP.execute p e
+
+start :: IO WhileExplorer
+start = return whileExplorer
+
+-- startM :: IO WhileExplorerM
+-- startM = return whileTree
+
+-- startO :: WhileExplorerO
+-- startO = whileGraphO
+
+session1 :: IO WhileExplorer
+session1 = start >>=
+  do_ (assign "x" (intToExpr 1)) >>=
+  do_ (assign "y" (Id "x")) >>=
+  do_ (assign "x" (intToExpr 1)) >>=
+  do_ (Print (Id "y")) >>=
+  do_ (Print (Id "x")) . fromJust . EM.jump 2 >>=
+  do_ (assign "z" (intToExpr 100)) >>=
+  do_ (Print (Id "z"))
+
+
+sessionS :: IO WhileExplorer
+sessionS = start >>=
+  do_ (assign "x" (intToExpr 1)) >>=
+  do_ (assign "y" (Id "x")) >>=
+  do_ (assign "x" (intToExpr 1)) >>=
+  do_ (assign "z" (intToExpr 20)) >>=
+  do_ (assign "y" (Id "x"))
+
+
+
+-- -- When using sharing, this results in 3 configurations and not 4,
+-- -- since the IO effect is hidden in the monad and not part of the
+-- -- configurations anymore.
+-- session2 :: IO WhileExplorerM
+-- session2 = startM >>=
+--   do_2 (assign "x" (intToExpr 1)) >>=
+--   do_2 (assign "y" (Id "x")) >>=
+--   do_2 (Print (Id "y"))
+
+
+-- session3 :: (WhileExplorerO, [String])
+-- session3 =
+--   do_3 (Print (Id "y")) $ do_3 (assign "y" (Id "x")) $ do_3 (assign "x" (intToExpr 1)) (startO, [])
+
+-- Below are some helpers to create a Command and fully evaluate it.
+-- Example:
+-- ghci> let x = wprint (intToExpr 10) `wseq` (wprint (intToExpr 100) `wseq` wprint (intToExpr 200))
+-- ghci> runCommand' x
+-- ["10","100","200"]
+-- ghci>
+runCommand :: Command -> IO()
+runCommand c = do
+    let ((_, output), _) = runState (runWriterT (evalCommand' c)) Map.empty
+    print output
+
+
+intToExpr :: Integer -> Expr
+intToExpr = LitExpr . LitInt
+
+boolToExpr :: Bool -> Expr
+boolToExpr = LitExpr . LitBool
+
+while ::  Expr -> Command -> Command
+while e = While e e
+
+leq :: Expr -> Expr -> Expr
+leq = Leq
+
+wprint :: Expr -> Command
+wprint = Print
+
+plus :: Expr -> Expr -> Expr
+plus = Plus
+
+assign :: String -> Expr -> Command
+assign = Assign
+
+wseq :: Command -> Command -> Command
+wseq = Seq
+
+whileExplorer :: WhileExplorer
+whileExplorer = E.mkExplorer True (==) definterp initialConfig
+
+whileExample = Seq (Assign "x" (intToExpr 0)) (while (Leq (Id "x") (intToExpr 10)) (Seq (Assign "x" (Plus (Id "x") (intToExpr 1))) (Print (Id "x"))))
+
+zero = intToExpr 0
+
+getRef :: WhileExplorer -> E.Ref
+getRef = E.currRef
exploring-interpreters.cabal view
@@ -1,48 +1,48 @@-cabal-version:       >=1.10--name:                exploring-interpreters-version:             1.5.0.1-synopsis:            A generic exploring interpreter for exploratory programming--- synopsis:--- description:--- bug-reports:-license:             BSD3-license-file:        LICENSE-author:              Damian Frolich-maintainer:          leegbestand@gmail.com--- copyright:-category:            Compilers/Interpreters-build-type:          Simple-extra-source-files:  CHANGELOG.md examples/Whilelang.hs--source-repository head-    type:         git-    location:     https://github.com/leegbestand/exploring_interpreters--library-  exposed-modules:-      Language.Explorer.Monadic,-      Language.Explorer.Pure,-      Language.Explorer.Tools.REPL,-      Language.Explorer.Tools.Protocol-  other-modules:-      Language.Explorer.Basic-  -- other-extensions:-  build-depends:-      base >=4.9 && <5,-      containers >=0.5 && <0.7,-      fgl >= 5.7.0 && < 5.9,-      transformers >= 0.5.2 && < 0.6,-      mtl          >= 2.2.1 && < 2.3,-      aeson                 >= 2.2.1 && < 2.3,-      attoparsec            >= 0.14.1 && < 0.15,-      bytestring            >= 0.10.10 && < 0.12,-      scientific            >= 0.3.7 && < 0.4,-      text                  >= 2.1 && < 2.2,-      http-types            >= 0.12.3 && < 0.13,-      network               >= 3.1.2 && < 3.2,-      haskeline             >=0.8.2 && < 0.9,-      exceptions            >=0.10.4 && < 0.11--  -- hs-source-dirs:-  default-language:    Haskell2010+cabal-version:       >=1.10
+
+name:                exploring-interpreters
+version:             1.6.0.0
+synopsis:            A generic exploring interpreter for exploratory programming
+-- synopsis:
+-- description:
+-- bug-reports:
+license:             BSD3
+license-file:        LICENSE
+author:              Damian Frolich
+maintainer:          leegbestand@gmail.com
+-- copyright:
+category:            Compilers/Interpreters
+build-type:          Simple
+extra-source-files:  CHANGELOG.md examples/Whilelang.hs
+
+source-repository head
+    type:         git
+    location:     https://github.com/leegbestand/exploring_interpreters
+
+library
+  exposed-modules:
+      Language.Explorer.Monadic,
+      Language.Explorer.Pure,
+      Language.Explorer.Tools.REPL,
+      Language.Explorer.Tools.Protocol
+  other-modules:
+      Language.Explorer.Basic
+  -- other-extensions:
+  build-depends:
+      base >=4.9 && <5,
+      containers >=0.5 && <1,
+      fgl >= 5.7.0 && < 6,
+      transformers >= 0.5.2 && < 1,
+      mtl          >= 2.2.1 && < 3,
+      aeson                 >= 2.2.1 && < 3,
+      attoparsec            >= 0.14.1 && < 1,
+      bytestring            >= 0.10.10 && < 1,
+      scientific            >= 0.3.7 && < 1,
+      text                  >= 2.1 && <3,
+      http-types            >= 0.12.3 && < 1,
+      network               >= 3.1.2 && <4,
+      haskeline             >=0.8.2 && < 1,
+      exceptions            >=0.10.4 && < 1
+
+  -- hs-source-dirs:
+  default-language:    Haskell2010