tock-mirror/flow/FlowUtils.hs
Neil Brown 2a15f4ef5f Filled in all the gaps such that the CheckTest tests now run (including some debug output, for now)
One of the tests fails at the moment because the specification node has two entries associated with it in the flowgraph.  One is the scope-in and one is the scope-out.  I think the analysis is currently picking the scope-out node and looking beyond that, where -- surprise, surprise -- the variable is not used again.  So I need some easy way of telling the flow analyses which of the two nodes I want to start from, in this case and other ones where I also add two nodes related to the same point in the AST.
2008-11-12 16:54:00 +00:00

266 lines
12 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007-2008 University of Kent
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
-}
module FlowUtils where
import Control.Monad.Error
import Control.Monad.Reader
import Control.Monad.State
import Data.Generics
import Data.Graph.Inductive hiding (run)
import qualified AST as A
import GenericUtils
import Metadata
import TreeUtils
import Utils
-- | A node will either have:
-- * zero links out,
-- * one or more Seq links out,
-- * ot one or more Par links out.
-- Zero links means it is a terminal node.
-- One Seq link means a normal sequential progression.
-- Multiple Seq links means choice.
-- Multiple Par links means a parallel branch. All outgoing par links should have the same identifier,
-- and this identifier is unique and matches a later endpar link
data EdgeLabel = ESeq (Maybe Bool) | EStartPar Int | EEndPar Int deriving (Show, Eq, Ord)
-- | A type used to build up tree-modifying functions. When given an inner modification function,
-- it returns a modification function for the whole tree. The functions are monadic, to
-- provide flexibility; you can always use the Identity monad. The type parameter
-- m is left-over from when the monad used to be specific (now it can be any monad,
-- using the mechanisms of Route) but it helps with code clarity
type ASTModifier m inner structType = Route inner (A.Structured structType)
-- | A choice of AST altering functions built on ASTModifier.
data AlterAST m structType =
AlterProcess (ASTModifier m A.Process structType)
|AlterAlternative (ASTModifier m A.Alternative structType)
|AlterArguments (ASTModifier m [A.Formal] structType)
|AlterExpression (ASTModifier m A.Expression structType)
|AlterExpressionList (ASTModifier m A.ExpressionList structType)
|AlterReplicator (ASTModifier m A.Replicator structType)
|AlterSpec (ASTModifier m A.Specification structType)
|AlterNothing [Int]
deriving (Show)
data Monad mAlter => FNode' structType mAlter label
= Node (Meta, label, AlterAST mAlter structType)
instance Monad m => Functor (FNode' s m) where
fmap f (Node (m, l, a)) = Node (m, f l, a)
-- | The label for a node. A Meta tag, a custom label, and a function
-- for altering the part of the AST that this node came from
type FNode mAlter label = FNode' () mAlter label
--type FEdge = (Node, EdgeLabel, Node)
instance (Monad m, Show a) => Show (FNode' b m a) where
show (Node (m,x,r)) = (filter ((/=) '\"')) $ show m ++ ":" ++ show x ++ "<" ++ show r
type FlowGraph' mAlter label structType = Gr (FNode' structType mAlter label) EdgeLabel
-- | The main FlowGraph type. The mAlter parameter is the monad
-- in which alterations to the AST (based on the FlowGraph)
-- must occur. The label parameter is the type of the node labels.
type FlowGraph mAlter label = FlowGraph' mAlter label ()
-- | A list of nodes and edges. Used for building up the graph.
type NodesEdges m a b = ([LNode (FNode' b m a)],[LEdge EdgeLabel])
-- | The state carried around when building up the graph. In order they are:
-- * The next node identifier
-- * The next identifier for a PAR item (for the EStartPar\/EEndPar edges)
-- * The list of nodes and edges to put into the graph
-- * The list of root nodes thus far (those with no links to them)
-- * The list of terminator nodes thus far (those with no links from them)
type GraphMakerState mAlter a b = (Node, Int, NodesEdges mAlter a b, [Node], [Node])
type GraphMaker mLabel mAlter a b c = ErrorT String (ReaderT (GraphLabelFuncs mLabel a) (StateT (GraphMakerState mAlter a b) mLabel)) c
-- | The GraphLabelFuncs type. These are a group of functions
-- used to provide labels for different elements of AST.
-- The m parameter is the monad the labelling must take place in,
-- and the label parameter is of course the label type.
-- The primary reason for having the blank (dummy) generator take a
-- Meta as an argument is actually for testing. But other uses
-- can simply ignore it if they want.
data Monad m => GraphLabelFuncs m label = GLF {
labelDummy :: Meta -> m label
,labelStartNode :: (Meta, [A.Formal]) -> m label
,labelProcess :: A.Process -> m label
,labelAlternative :: A.Alternative -> m label
,labelExpression :: A.Expression -> m label
, labelConditionalExpression :: A.Expression -> m label
,labelExpressionList :: A.ExpressionList -> m label
,labelReplicator :: (A.Name, A.Replicator) -> m label
,labelScopeIn :: A.Specification -> m label
,labelScopeOut :: A.Specification -> m label
}
getNodeMeta :: Monad m => FNode' b m a -> Meta
getNodeMeta (Node (m,_,_)) = m
getNodeData :: Monad m => FNode' b m a -> a
getNodeData (Node (_,d,_)) = d
getNodeFunc :: Monad m => FNode' b m a -> AlterAST m b
getNodeFunc (Node (_,_,f)) = f
getNodeRouteId :: Monad m => FNode' b m a -> [Int]
getNodeRouteId = get . getNodeFunc
where
get (AlterProcess f) = routeId f
get (AlterAlternative f) = routeId f
get (AlterArguments f) = routeId f
get (AlterExpression f) = routeId f
get (AlterExpressionList f) = routeId f
get (AlterReplicator f) = routeId f
get (AlterSpec f) = routeId f
get (AlterNothing r) = r
makeTestNode :: Monad m => Meta -> a -> FNode m a
makeTestNode m d = Node (m,d,undefined)
-- | Builds the instructions to send to GraphViz
makeFlowGraphInstr :: (Monad m, Show a, Data b) => FlowGraph' m a b -> String
makeFlowGraphInstr = graphviz'
-- | Joins two labelling functions together. They must use the same monad.
joinLabelFuncs :: forall a b m. Monad m => GraphLabelFuncs m a -> GraphLabelFuncs m b -> GraphLabelFuncs m (a,b)
joinLabelFuncs fx fy = GLF
{
labelDummy = joinItem labelDummy,
labelStartNode = joinItem labelStartNode,
labelProcess = joinItem labelProcess,
labelAlternative = joinItem labelAlternative,
labelExpression = joinItem labelExpression,
labelConditionalExpression = joinItem labelConditionalExpression,
labelExpressionList = joinItem labelExpressionList,
labelReplicator = joinItem labelReplicator,
labelScopeIn = joinItem labelScopeIn,
labelScopeOut = joinItem labelScopeOut
}
where
joinItem :: (forall l. GraphLabelFuncs m l -> (k -> m l)) -> (k -> m (a,b))
joinItem item = joinTwo (item fx) (item fy)
joinTwo :: (a' -> m b') -> (a' -> m c') -> (a' -> m (b',c'))
joinTwo f0 f1 x = do x0 <- f0 x
x1 <- f1 x
return (x0,x1)
mkLabelFuncsConst :: Monad m => m label -> GraphLabelFuncs m label
mkLabelFuncsConst v = GLF (const v) (const v) (const v) (const v) (const v) (const v) (const v) (const v) (const v) (const v)
mkLabelFuncsGeneric :: Monad m => (forall t. Data t => t -> m label) -> GraphLabelFuncs m label
mkLabelFuncsGeneric f = GLF f f f f f f f f f f
run :: forall mLabel mAlter label structType b. (Monad mLabel, Monad mAlter) => (GraphLabelFuncs mLabel label -> (b -> mLabel label)) -> b -> GraphMaker mLabel mAlter label structType label
run func x = do f <- asks func
lift . lift .lift $ f x
addNode :: (Monad mLabel, Monad mAlter) => (Meta, label, AlterAST mAlter structType) -> GraphMaker mLabel mAlter label structType Node
addNode x = do (n,pi,(nodes, edges), rs, ts) <- get
put (n+1, pi,((n, Node x):nodes, edges), rs, ts)
return n
denoteRootNode :: (Monad mLabel, Monad mAlter) => Node -> GraphMaker mLabel mAlter label structType ()
denoteRootNode root = do (n, pi, nes, roots, ts) <- get
put (n, pi, nes, root : roots, ts)
denoteTerminatorNode :: (Monad mLabel, Monad mAlter) => Node -> GraphMaker mLabel mAlter label structType ()
denoteTerminatorNode t = do (n, pi, nes, roots, ts) <- get
put (n, pi, nes, roots, t : ts)
addEdge :: (Monad mLabel, Monad mAlter) => EdgeLabel -> Node -> Node -> GraphMaker mLabel mAlter label structType ()
addEdge label start end = do (n, pi, (nodes, edges), rs, ts) <- get
-- Edges should only be added after the nodes, so
-- for safety here we can check that the nodes exist:
if (notElem start $ map fst nodes) || (notElem end $ map fst nodes)
then throwError "Could not add edge between non-existent nodes"
else put (n + 1, pi, (nodes,(start, end, label):edges), rs,
ts)
-- It is important for the flow-graph tests that the Meta tag passed in is the same as the
-- result of calling findMeta on the third parameter
addNode' :: (Monad mLabel, Monad mAlter) => Meta -> (GraphLabelFuncs mLabel label -> (b -> mLabel label)) -> b -> AlterAST mAlter structType -> GraphMaker mLabel mAlter label structType Node
addNode' m f t r = do val <- run f t
addNode (m, val, r)
addNodeExpression :: (Monad mLabel, Monad mAlter) => Meta -> A.Expression -> (ASTModifier mAlter A.Expression structType) -> GraphMaker mLabel mAlter label structType Node
addNodeExpression m e r = addNode' m labelExpression e (AlterExpression r)
addNodeExpressionList :: (Monad mLabel, Monad mAlter) => Meta -> A.ExpressionList -> (ASTModifier mAlter A.ExpressionList structType) -> GraphMaker mLabel mAlter label structType Node
addNodeExpressionList m e r = addNode' m labelExpressionList e (AlterExpressionList r)
addDummyNode :: (Monad mLabel, Monad mAlter) => Meta -> ASTModifier mAlter a structType
-> GraphMaker mLabel mAlter label structType Node
addDummyNode m mod = addNode' m labelDummy m (AlterNothing $ routeId mod)
getNextParEdgeId :: (Monad mLabel, Monad mAlter) => GraphMaker mLabel mAlter label structType Int
getNextParEdgeId = do (a, pi, b, c, d) <- get
put (a, pi + 1, b, c, d)
return pi
addParEdges :: (Monad mLabel, Monad mAlter) => Int -> (Node,Node) -> [(Node,Node)] -> GraphMaker mLabel mAlter label structType ()
addParEdges usePI (s,e) pairs
= do (n,pi,(nodes,edges),rs,ts) <- get
put (n,pi,(nodes,edges ++ (concatMap (parEdge usePI) pairs)),rs,ts)
where
parEdge :: Int -> (Node, Node) -> [LEdge EdgeLabel]
parEdge id (a,z) = [(s,a,(EStartPar id)),(z,e,(EEndPar id))]
mapMR :: forall inner mAlter mLabel label retType structType. (Monad mLabel, Monad mAlter) =>
ASTModifier mAlter [inner] structType ->
(inner -> ASTModifier mAlter inner structType -> GraphMaker mLabel mAlter label structType retType) ->
[inner] -> GraphMaker mLabel mAlter label structType [retType]
mapMR outerRoute func xs = mapM funcAndRoute (zip [0..] xs)
where
funcAndRoute :: (Int, inner) -> GraphMaker mLabel mAlter label structType retType
funcAndRoute (ind,x) = func x (outerRoute @-> routeList ind)
mapMRE :: forall inner mAlter mLabel label structType. (Monad mLabel, Monad mAlter) => ASTModifier mAlter [inner] structType -> (inner -> ASTModifier mAlter inner structType -> GraphMaker mLabel mAlter label structType (Either Bool (Node,Node))) -> [inner] -> GraphMaker mLabel mAlter label structType (Either Bool [(Node,Node)])
mapMRE outerRoute func xs = mapM funcAndRoute (zip [0..] xs) >>* foldl foldEither (Left False)
where
foldEither :: Either Bool [(Node,Node)] -> Either Bool (Node,Node) -> Either Bool [(Node,Node)]
foldEither (Left _) (Right n) = Right [n]
foldEither (Right ns) (Left _) = Right ns
foldEither (Left hadNode) (Left hadNode') = Left $ hadNode || hadNode'
foldEither (Right ns) (Right n) = Right (ns ++ [n])
funcAndRoute :: (Int, inner) -> GraphMaker mLabel mAlter label structType (Either Bool (Node,Node))
funcAndRoute (ind,x) = func x (outerRoute @-> routeList ind)
nonEmpty :: Either Bool [(Node,Node)] -> Bool
nonEmpty (Left hadNodes) = hadNodes
nonEmpty (Right nodes) = not (null nodes)
joinPairs :: (Monad mLabel, Monad mAlter) => Meta -> ASTModifier mAlter a structType
-> [(Node, Node)] -> GraphMaker mLabel mAlter label structType (Node, Node)
joinPairs m mod [] = addDummyNode m mod >>* mkPair
joinPairs m mod nodes
= do sequence_ $ mapPairs (\(_,s) (e,_) -> addEdge (ESeq Nothing) s e) nodes
return (fst (head nodes), snd (last nodes))