tock-mirror/transformations/Unnest.hs
Neil Brown acd57d74de Changed the A.Structured type to be parameterised
This patch is actually an amalgam of multiple (already large) patches.  Those patches conflicted (parameterised Structured vs. changes to usage checking and FlowGraph) and encountered a nasty bug in darcs 1 involving exponential time (see http://wiki.darcs.net/DarcsWiki/ConflictsFAQ for more details).  Reasoning that half an hour (of 100% CPU use) was too long to apply patches, I opted to re-record the parameterised Structured changes as this new large patch.  Here are the commit messages originally used for the patches (which, as mentioned, were already large patches):

A gigantic patch switching all the non-test modules over to using parameterised A.Structured
Changed the FlowGraph module again to handle any sort of Structured you want to pass to it (mainly for testing)
A further gigantic patch changing all the tests to work with the new parameterised Structured
Fixed a nasty bug involving functions being named incorrectly inside transformInputCase
Added a hand-written instance of Data for Structured that allows us to use ext1M properly
Fixed a few warnings in the code
2008-02-05 19:40:27 +00:00

206 lines
7.9 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007 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/>.
-}
-- | Flatten nested declarations.
module Unnest (unnest) where
import Control.Monad.State
import Data.Generics
import qualified Data.Map as Map
import Data.Maybe
import qualified AST as A
import CompState
import Errors
import EvalConstants
import Metadata
import Pass
import Types
unnest :: Pass
unnest = runPasses passes
where
passes =
[ ("Convert free names to arguments", removeFreeNames)
, ("Pull nested definitions to top level", removeNesting)
]
type NameMap = Map.Map String A.Name
-- | Get the set of free names within a block of code.
freeNamesIn :: Data t => t -> NameMap
freeNamesIn = doGeneric
`extQ` (ignore :: String -> NameMap)
`extQ` (ignore :: Meta -> NameMap)
`extQ` doName `ext1Q` doStructured `extQ` doSpecType
where
doGeneric :: Data t => t -> NameMap
doGeneric n = Map.unions $ gmapQ freeNamesIn n
ignore :: t -> NameMap
ignore s = Map.empty
doName :: A.Name -> NameMap
doName n = Map.singleton (A.nameName n) n
doStructured :: Data a => A.Structured a -> NameMap
doStructured (A.Rep _ rep s) = doRep rep s
doStructured (A.Spec _ spec s) = doSpec spec s
doStructured s = doGeneric s
doSpec :: Data t => A.Specification -> t -> NameMap
doSpec (A.Specification _ n st) child
= Map.union fns $ Map.delete (A.nameName n) $ freeNamesIn child
where
fns = freeNamesIn st
doRep :: Data t => A.Replicator -> t -> NameMap
doRep rep child
= Map.union fns $ Map.delete (A.nameName repName) $ freeNamesIn child
where
(repName, fns) = case rep of
A.For _ n b c -> (n, Map.union (freeNamesIn b) (freeNamesIn c))
doSpecType :: A.SpecType -> NameMap
doSpecType (A.Proc _ _ fs p) = Map.difference (freeNamesIn p) (freeNamesIn fs)
doSpecType (A.Function _ _ _ fs vp) = Map.difference (freeNamesIn vp) (freeNamesIn fs)
doSpecType st = doGeneric st
-- | Replace names.
replaceNames :: Data t => [(A.Name, A.Name)] -> t -> t
replaceNames map p = everywhere (mkT doName
`extT` (id :: String -> String)
`extT` (id :: Meta -> Meta)
) p
where
smap = [(A.nameName f, t) | (f, t) <- map]
doName :: A.Name -> A.Name
doName n
= case lookup (A.nameName n) smap of
Just n' -> n'
Nothing -> n
-- | Turn free names in PROCs into arguments.
removeFreeNames :: Data t => t -> PassM t
removeFreeNames = doGeneric `extM` doSpecification `extM` doProcess
where
doGeneric :: Data t => t -> PassM t
doGeneric = makeGeneric removeFreeNames
doSpecification :: A.Specification -> PassM A.Specification
doSpecification spec = case spec of
A.Specification m n st@(A.Proc _ _ _ _) ->
do st'@(A.Proc mp sm fs p) <- removeFreeNames st
-- If this is the top-level process, we shouldn't add new args --
-- we know it's not going to be moved by removeNesting, so anything
-- that it had in scope originally will still be in scope.
ps <- get
when (null $ csMainLocals ps) (dieReport (Nothing,"No main process found"))
let isTLP = (snd $ head $ csMainLocals ps) == n
-- Figure out the free names.
let freeNames' = if isTLP then [] else Map.elems $ freeNamesIn st'
let freeNames'' = [n | n <- freeNames',
case A.nameType n of
A.ChannelName -> True
A.PortName -> True
A.TimerName -> True
A.VariableName -> True
_ -> False]
-- Don't bother with constants -- they get pulled up anyway.
freeNames <- filterM (liftM not . isConstantName) freeNames''
types <- mapM typeOfName freeNames
origAMs <- mapM abbrevModeOfName freeNames
let ams = map makeAbbrevAM origAMs
-- Generate and define new names to replace them with
newNamesS <- sequence [makeNonce (A.nameName n) | n <- freeNames]
let newNames = [on { A.nameName = nn } | (on, nn) <- zip freeNames newNamesS]
onds <- mapM (\n -> lookupNameOrError n $ dieP mp $ "Could not find recorded type for free name: " ++ (show $ A.nameName n)) freeNames
sequence_ [defineName nn (ond { A.ndName = A.nameName nn,
A.ndAbbrevMode = am })
| (ond, nn, am) <- zip3 onds newNames ams]
-- Add formals for each of the free names
let newFs = [A.Formal am t n | (am, t, n) <- zip3 ams types newNames]
let st'' = A.Proc mp sm (fs ++ newFs) $ replaceNames (zip freeNames newNames) p
let spec' = A.Specification m n st''
-- Update the definition of the proc
nameDef <- lookupName n
defineName n (nameDef { A.ndType = st'' })
-- Note that we should add extra arguments to calls of this proc
-- when we find them
let newAs = [case am of
A.Abbrev -> A.ActualVariable am t (A.Variable m n)
_ -> A.ActualExpression t (A.ExprVariable m (A.Variable m n))
| (am, n, t) <- zip3 ams freeNames types]
debug $ "removeFreeNames: " ++ show n ++ " has new args " ++ show newAs
when (newAs /= []) $
modify $ (\ps -> ps { csAdditionalArgs = Map.insert (A.nameName n) newAs (csAdditionalArgs ps) })
return spec'
_ -> doGeneric spec
-- | Add the extra arguments we recorded when we saw the definition.
doProcess :: A.Process -> PassM A.Process
doProcess p@(A.ProcCall m n as)
= do st <- get
case Map.lookup (A.nameName n) (csAdditionalArgs st) of
Just add -> doGeneric $ A.ProcCall m n (as ++ add)
Nothing -> doGeneric p
doProcess p = doGeneric p
-- | Pull nested declarations to the top level.
removeNesting :: forall a. Data a => A.Structured a -> PassM (A.Structured a)
removeNesting p
= do pushPullContext
p' <- pullSpecs p
s <- applyPulled p'
popPullContext
return s
where
pullSpecs :: Data t => t -> PassM t
pullSpecs = doGeneric `extM` doStructured
doGeneric :: Data t => t -> PassM t
doGeneric = makeGeneric pullSpecs
doStructured :: A.Structured a -> PassM (A.Structured a)
doStructured s@(A.Spec m spec@(A.Specification _ n st) subS)
= do isConst <- isConstantName n
if isConst || canPull st then
do debug $ "removeNesting: pulling up " ++ show n
spec' <- doGeneric spec
addPulled $ (m, Left spec')
doStructured subS
else doGeneric s
doStructured s = doGeneric s
canPull :: A.SpecType -> Bool
canPull (A.Proc _ _ _ _) = True
canPull (A.RecordType _ _ _) = True
canPull (A.Protocol _ _) = True
canPull (A.ProtocolCase _ _) = True
canPull _ = False