packages feed

eflint-3.1.0.0: src/Language/EFLINT/Explorer.hs

{-# LANGUAGE TupleSections #-}

module Language.EFLINT.Explorer where

import Language.EFLINT.Spec (Spec, Phrase(PSkip), ppTagged)
import Language.EFLINT.State (State, emptyInput, InputMap, TransInfo(..), Assignment(..), trans_is_action)
import Language.EFLINT.Interpreter (Program(..), Config(..), interpreter, initialConfig, Output, getOutput)
import Language.EFLINT.Print()

import qualified Language.Explorer.Pure as EI
import Language.Explorer.Monadic (revert, jump)

import Data.Tree (drawTree, Tree(..))

type Explorer = EI.Explorer Label Config [Output]

type Label = (InputMap, Program)

data Instruction = Execute [Program] InputMap
                 | ExecuteOnce Program InputMap
                 | Revert Ref Bool {- whether the revert is destructive or not -} 
                 | Display Ref  -- `last' edge leading to given ref
                 | DisplayFull Ref -- full `history', see `Path' below, for given ref
                 | ExplorationHeads -- nodes in the execution graph without outgoing edges
                 | CreateExportExploration -- create export data to recreate execution graph
                 | LoadExportExploration ExecutionGraph -- load execution graph
data Response    = ResultTrans Explorer [Output] (Config, Ref) (Config, Ref)
                 | Path Path
                 | Nodes [Node]
                 | InvalidRevert
                 | ExportExploration ExecutionGraph
                 | LoadExploration Explorer

type Ref = Int -- state identifier
type Path = [(Node, (Label, [Output]), Node)] 
type Node = (Ref, Config)

data N = N {
           ref :: Ref
         , config :: Config
         }  
         deriving (Eq)

data Edge = Edge {
            source :: Ref
          , target :: Ref
          , po     :: PO
          }  
          deriving (Eq)

data PO = PO {
          label  :: Label 
        , output :: [Output]
        }  
        deriving (Eq)

data ExecutionGraph = ExecutionGraph {
                      current :: Ref
                    , nodes   :: [N]
                    , edges   :: [Edge]
                    }

showTree :: Explorer -> String
showTree = drawTree . fmap (("#"++) . show . fst) . EI.toTree

showTriggerTree :: TransInfo -> String
showTriggerTree = drawTree . fmap report . triggerTree
  where report info = ppTagged (trans_tagged info) ++ " " ++ whether_enabled 
--                      ++ "\n" ++ unlines (map (uncurry mod) (M.assocs (trans_assignments info)))
         where mod te ass = case ass of  HoldsTrue -> "+" ++ ppTagged te
                                         HoldsFalse -> "-" ++ ppTagged te
                                         Unknown -> "~" ++ ppTagged te
               whether_enabled | not (trans_is_action info) = ""
                               | trans_forced info          = "(DISABLED)"
                               | otherwise                  = "(ENABLED)"

triggerTree :: TransInfo -> Tree TransInfo 
triggerTree t = Node t (map triggerTree (trans_syncs t))

get_last_edge :: Explorer -> Ref -> ((Ref, Config), (Label, [Output]), (Ref, Config))
get_last_edge exp cr = case reverse (EI.getPathFromTo exp 1 cr) of
  (edge:_) -> edge 
  _ -> maybe (error ("ASSERT: get_last_edge1")) (\cfg -> ((cr,cfg), ((emptyInput, Program PSkip),[]), (cr,cfg))) (EI.deref exp cr) 
        
init_tree_explorer, init_graph_explorer :: Maybe (Spec,State) -> Explorer
init_tree_explorer = EI.mkExplorer False (const . const $ False) defInterpreter . initialConfig
init_graph_explorer = EI.mkExplorer True (==) defInterpreter . initialConfig

defInterpreter p c = getOutput $ interpreter p c

run_ :: Explorer -> Instruction -> Response
run_ exp instr = case instr of 
  Execute ps inpm -> 
    let (exp',outs) = EI.executeAll (map (inpm,) ps) exp
        cfg'        = EI.config exp'
        ref'        = EI.currRef exp'
    in ResultTrans exp' outs (EI.config exp, EI.currRef exp) (cfg', ref')
  ExecuteOnce ps inpm -> 
    let (exp',outs) = EI.execute (inpm,ps) exp
        cfg'        = EI.config exp'
        ref'        = EI.currRef exp'
    in ResultTrans exp' outs (EI.config exp, EI.currRef exp) (cfg', ref')
  Revert r d -> case (if d then revert else jump) r exp of 
    Nothing   -> InvalidRevert
    Just exp'  -> ResultTrans exp' [] (EI.config exp, EI.currRef exp) (EI.config exp', EI.currRef exp')
  Display id -> ResultTrans exp out (from, pr) (to, cr)
    where ((pr,from), (_,out), (cr, to)) = get_last_edge exp id
  DisplayFull id -> Path $ EI.getPathFromTo exp 1 id
  ExplorationHeads -> Nodes $ EI.leaves exp
  CreateExportExploration -> ExportExploration $ convertToGraph (EI.toExport exp)
  LoadExportExploration graph -> LoadExploration $ EI.fromExport exp (convertFromGraph graph)

convertToGraph :: (Ref, [(Ref, Config)], [(Ref, Ref, (Label, [Output]))]) -> ExecutionGraph
convertToGraph (cid, nodes, edges) = ExecutionGraph{current=cid, nodes=(map convertToN nodes), edges=(map convertToEdges edges)}

convertFromGraph :: ExecutionGraph -> (Ref, [(Ref, Config)], [(Ref, Ref, (Label, [Output]))])
convertFromGraph graph = (current graph, map convertFromN (nodes graph), map convertFromEdges (edges graph))

convertToN :: (Ref, Config) -> N
convertToN (r, c) = N{ref=r, config=c}

convertFromN :: N -> (Ref, Config) 
convertFromN n = (ref n, config n)

convertToEdges :: (Ref, Ref, (Label, [Output])) -> Edge
convertToEdges (sid, tid, (p, o)) = Edge{source=sid, target=tid, po=PO{label=p, output=o}}

convertFromEdges :: Edge -> (Ref, Ref, (Label, [Output]))
convertFromEdges edge = (source edge, target edge, convertFromPO (po edge))

convertFromPO :: PO -> (Label, [Output])
convertFromPO po = (label po, output po)