csound-expression-dynamic-0.3.9.1: src/Csound/Dynamic/Tfm/UnfoldMultiOuts.hs
{-# Language TupleSections #-}
module Csound.Dynamic.Tfm.UnfoldMultiOuts(
unfoldMultiOuts, Selector(..)
) where
import Data.List(sortBy)
import Data.Ord(comparing)
import Control.Monad.Trans.State.Strict
import qualified Data.IntMap.Strict as IM
import Data.Either (partitionEithers)
import Csound.Dynamic.Tfm.InferTypes(Var(..), Stmt(..), InferenceResult(..))
import Csound.Dynamic.Types.Exp hiding (Var (..))
import Csound.Dynamic.Build(getRates, isMultiOutSignature)
type ChildrenMap = IM.IntMap [Port]
lookupChildren :: ChildrenMap -> Var -> [Port]
lookupChildren m parentVar = m IM.! varId parentVar
mkChildrenMap :: [(Var, Selector)] -> ChildrenMap
mkChildrenMap = IM.fromListWith (++) . fmap extract
where extract (var, sel) = (varId $ selectorParent sel,
return $ Port (varId var) (selectorOrder sel))
data Port = Port
{ portId :: Int
, portOrder :: Int } deriving (Show)
type SingleStmt = Stmt Var
type MultiStmt = ([Var], RatedExp Var)
data Selector = Selector
{ selectorParent :: Var
, selectorOrder :: Int
}
unfoldMultiOuts :: InferenceResult -> ([MultiStmt], Int)
unfoldMultiOuts InferenceResult{..} = runState st programLastFreshId
where
(noSelectorStmts, selectors) = partitionEithers $
fmap (\stmt@(Stmt lhs rhs) -> maybe (Left stmt) (Right . (lhs, )) $ getSelector rhs) typedProgram
st = mapM (unfoldStmt $ mkChildrenMap selectors) $ noSelectorStmts
unfoldStmt :: ChildrenMap -> SingleStmt -> State Int MultiStmt
unfoldStmt childrenMap (Stmt lhs rhs) = case getParentTypes rhs of
Nothing -> return ([lhs], rhs)
Just types -> fmap (,rhs) $ formLhs (lookupChildren childrenMap lhs) types
formLhs :: [Port] -> [Rate] -> State Int [Var]
formLhs ports types = fmap (zipWith Var types) (getPorts ports)
where getPorts ps = state $ \lastFreshId ->
let ps' = sortBy (comparing portOrder) ps
(ids, lastPortOrder) = runState (mapM (fillMissingPorts lastFreshId) ps') 0
freshIdForTail = 1 + lastFreshId + inUsePortsSize
tailIds = map (+ freshIdForTail) [0 .. outputArity - 1 - lastPortOrder]
in (concat ids ++ tailIds, lastFreshId + outputArity - inUsePortsSize)
outputArity = length types
inUsePortsSize = length ports
fillMissingPorts :: Int -> Port -> State Int [Int]
fillMissingPorts lastFreshId port = state $ \s ->
if s == order
then ([e], next)
else (fmap (+ lastFreshId) [s .. order - 1] ++ [e], next)
where e = portId port
order = portOrder port
next = order + 1
-----------------------------------------------------------------------
-- unfolds multiple rates generic functions
getSelector :: RatedExp Var -> Maybe Selector
getSelector x =
case ratedExpExp x of
Select _ order (PrimOr (Right parent)) -> Just $ Selector parent order
_ -> Nothing
getParentTypes :: RatedExp Var -> Maybe [Rate]
getParentTypes x =
case ratedExpExp x of
Tfm i _ -> if (isMultiOutSignature $ infoSignature i)
then Just (getRates $ ratedExpExp x)
else Nothing
_ -> Nothing