packages feed

fregel-1.2.0: compiler/Analysis.hs

{-# Language TypeSynonymInstances,FlexibleInstances,MultiParamTypeClasses,FunctionalDependencies,RankNTypes,FlexibleContexts,KindSignatures,ScopedTypeVariables #-}


{-

Depencency check, reordering of let-bindings, and alpha renaming with unique names. (1st step)

-}

module Analysis where

import Spec

import Debug.Trace

import Control.Monad.State
import Data.Maybe
import Data.List
import ASTData

type DNewName = DVarName
type DDepth = Int
type DDepVar = (DVarName, (DDepth, DNewName))
type DDeps = [DDepVar]
type DEnvDC a = ([(DDeps, DDepth)], DUnique, a, Bool)

-- extracted aggregations, in-aggregator flag, 
type NormalizeData1 a = ([DSmplDef (DDeps, a)], Bool, [Bool])
class DepCheckable a b c | a -> b c where
    depCheck :: a -> State (DEnvDC c) b  -- assigns [(name to which the given term depends, depth)] to the given term


-- to flattern definitions (within a vertex-compute/vertex-init function)
collectDef :: DSmplDef a -> [DSmplDef a]
collectDef (DDefFun f vs defs e a) = concatMap collectDef defs++[(DDefFun f vs [] e a)]
collectDef (DDefVar v defs e a) = concatMap collectDef defs++[(DDefVar v [] e a)]
collectDef (DDefTuple vs defs e a) = concatMap collectDef defs++[(DDefTuple vs [] e a)]

-- getter/setter on the environment

getEnvDepth' :: DEnvDC a -> DDepth
getEnvDepth' (bds, _, _, _) = snd (head bds)

getEnvDepth :: State (DEnvDC a) DDepth
getEnvDepth = 
    do env <- get 
       return (getEnvDepth' env)

getEnvBinds' :: DEnvDC a -> DDeps
getEnvBinds' (bds, _, _, _) = fst (head bds)

getEnvBinds :: State (DEnvDC a) DDeps
getEnvBinds = 
    do env <- get 
       return (getEnvBinds' env)

setEnvBindsDepth :: (DDeps, DDepth) -> State (DEnvDC a) ()
setEnvBindsDepth bd =
    do (bds, i, a, b) <- get
       put (bd:bds, i, a, b)

popBindsDepth :: State (DEnvDC a) ()
popBindsDepth =
    do (bds, i, a, b) <- get
       put (tail bds, i, a, b)

getNewName :: String -> State (DEnvDC a) DNewName
getNewName n = 
    do (bds, i, a, b) <- get 
       let (nn, i') = genNewName i n
       put (bds, i', a, b)
       return nn

doingAlpha :: State (DEnvDC a) Bool
doingAlpha =
    do (bds, i, a, b) <- get
       return b

replaceApostrophe n = map (\c -> if c == '\'' then 'X' else c) n
              
-- orgName = true => new name = original name  (for predefined identifiers)
getNewName' :: Bool -> (DVarName, DDepth) -> State (DEnvDC a) DDepVar
getNewName' orgName (n, d) = 
    do b <- doingAlpha
       if b then do let n' = replaceApostrophe n
                    nn <- getNewName n'
                    return (n, (d, if orgName then n else nn))
            else return (n, (d, n))

getDVDepth :: DDepVar -> DDepth
getDVDepth (_, (d, _)) = d

getDVVar :: DDepVar -> DVarName
getDVVar (v, (_, _)) = v

getDVNewName :: DDepVar -> DNewName
getDVNewName (_, (_, n)) = n

-- entry point for analysis chain (accepts the uniq ID)
dependencyC :: forall (t :: * -> *) a . 
               (DepCheckable (t a) (t (DDeps, a)) (NormalizeData1 a),
                DAdditionalData2 (t (DDeps, a)) (DDeps, a) ([String], a) (t ([String], a))) 
               => DUnique -> t a -> (t ([DVarName], a), DUnique)
dependencyC uid p = 
    let (ast, uid') = dependencyC' uid p
    in (mapData (\(ds::DDeps, a::a) -> (map (snd.snd) ds, a)) ast, uid')

dependencyC' :: forall (t :: * -> *) a . 
               (DepCheckable (t a) (t (DDeps, a)) (NormalizeData1 a),
                DAdditionalData2 (t (DDeps, a)) (DDeps, a) ([String], a) (t ([String], a))) 
               => DUnique -> t a -> (t (DDeps, a), DUnique)
dependencyC' uid p = 
    let (ast, (_,uid',_,_)) = runState (depCheck0 p) ([([], -2)], uid, ([], False, [True]), True)
    in (ast, uid')

-- no alpha-renaming
topoSortOnly :: forall (t :: * -> *) a . 
               (DepCheckable (t a) (t (DDeps, a)) (NormalizeData1 a),
                DAdditionalData2 (t (DDeps, a)) (DDeps, a) a (t a)) 
               => t a -> t a
topoSortOnly p = 
  let (ast, _) = runState (depCheck0 p) ([([], -2)], -100, ([], False, [True]), False)
  in mapData (snd::(DDeps,a)->a) ast

-- entry point for single use
dependency :: forall (t :: * -> *) a . 
               (DepCheckable (t a) (t (DDeps, a)) (NormalizeData1 a),
                DAdditionalData2 (t (DDeps, a)) (DDeps, a) ([String], a) (t ([String], a))
               ) => t a -> t (DDeps, a)
dependency p = fst $ dependencyC' 0 p

-- cleaned version
dependency' :: forall (t :: * -> *) a . 
               (DepCheckable (t a) (t (DDeps, a)) (NormalizeData1 a),
                DAdditionalData2 (t (DDeps, a)) (DDeps, a) ([String], a) (t ([String], a))
               ) => t a -> t ([String], a)
dependency' p = fst $ dependencyC 0 p


-- cleaned data

depCheck0 p = 
    do addNames' True initDCnames  -- names at level -1
       depCheck p
  
initDCnames = map fst initEnv

-- misc functions for dependencies
getDep :: forall (t :: * -> *) b . (DAdditionalData (t (DDeps, b)) (DDeps, b)) => (t (DDeps, b)) -> DDeps
getDep = fst . getData

getDepL :: forall (t :: * -> *) b . (DAdditionalData (t (DDeps, b)) (DDeps, b)) => Int -> (t (DDeps, b)) -> DDeps
getDepL depth = filter ((< depth) . getDVDepth ) . getDep    -- names in lower (outer) depth levels
getDepE depth = filter ((== depth) . getDVDepth ) . getDep   -- names at the depth level

getDeps a b = normalizeDepsX [getDep a, getDep b]
getDeps3 a b c = normalizeDepsX [getDep a, getDep b, getDep c]
getDeps4 a b c d = normalizeDepsX [getDep a, getDep b, getDep c, getDep d]
getDepsX es = normalizeDepsX $ map getDep es

normalizeDeps :: DDeps -> DDeps
normalizeDeps = nub  -- remove duplicated dependency entries

normalizeDepsX ::[DDeps] -> DDeps
normalizeDepsX = foldr (\a r -> normalizeDeps (a++r)) []

-- reordering the let bindings (or any named things) according to their dependencies among them
-- NOTE: run after the alpha-conversion
depTopoSort ::  forall (t :: * -> *) b . (DNamed (t (DDeps, b)), DAdditionalData (t (DDeps, b)) (DDeps, b)) =>
     DDepth -> [(t (DDeps, b)) ] -> [(t (DDeps, b)) ]
depTopoSort depth ts = depTopoSort' True depth ts

-- withNew == True => using new alpha-converted names 
depTopoSort' ::  forall (t :: * -> *) b . (DNamed (t (DDeps, b)), DAdditionalData (t (DDeps, b)) (DDeps, b)) =>
     Bool -> DDepth -> [(t (DDeps, b)) ] -> [(t (DDeps, b)) ]
depTopoSort' withNew depth ts = ds'
 where
  ts' = zip [0..] ts
  vs = map findDepIds ts'
  findDepIds (i, t) = (i, map findId $ getDepE depth t, t)
  findId :: DDepVar -> Int  -- this dv is defined in the returned position
  findId dv = let tt = findIndices (elem (if withNew then getDVNewName dv else getDVVar dv) . getNames) ts in if tt == [] then error ("Something wrong: " ++ show (map getNames ts) ++ " -- " ++ (show dv)) else head tt
  (ds, ok) = topologicalSort vs 
  ds' = if ok then map snd ds else error ("circular dependency among " ++ show (map getNames ts))

-- topological sort algorithm: https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%9D%E3%83%AD%E3%82%B8%E3%82%AB%E3%83%AB%E3%82%BD%E3%83%BC%E3%83%88
-- (id, outNeighbors, data)
-- id starts from 0, contiguous =>  assumption: vs!!i = (i, _, _)
topologicalSort :: [(Int, [Int], a)] -> ([(Int, a)], Bool)
topologicalSort vs =
  let vs' = map (findIncoming vs) vs
      findIncoming us (v,ns,d) = (v, ns, d, length $ filter (\(_, os, _) -> elem v os) us)
      ins (_,_,_,i) = i
      decIns (x,ns,d,i) = (x, ns, d, i-1)
      noIncomings = filter ((==0) . ins)
      s = noIncomings vs'
      (l, g) = rec s [] vs'
      ok = all ((==0). ins)g
      rec [] l g = (l, g)
      rec (((x, ns, d, _)):s) l g 
          = let l' = (x, d):l
                (g', s') = foldr step (g, s) ns
                step j (g, s) = let (g1, y:g2) = splitAt j g
                                    g' = g1++((decIns y):g2)
                                in (g', if ins y == 1 then y:s else s)
            in rec s' l' g'
  in (l, ok)


-- adds names to the envitonment, returns the old env with the current depth
addNames :: [String] -> State (DEnvDC a) (Int, [DNewName])
addNames ns = addNames' False ns

addNames' :: Bool -> [String] -> State (DEnvDC a) (Int, [DNewName])
addNames' orgName ns = 
    do depth <- getEnvDepth
       binds <- getEnvBinds
       let ns' = if ns == nub ns then ns else error ("multiple definitions in " ++ show ns)
           depth' = depth + 1
           nds = zip ns' (repeat depth')
       binds' <- mapM (getNewName' orgName) nds
       setEnvBindsDepth (binds'++ binds, depth')
       return (depth', map getDVNewName binds')

-- removed added names by addNames
popNames :: State (DEnvDC a) ()
popNames = popBindsDepth

{- dependency check of expression e with simple-let-bindings defs -}
depCheckLet
    :: forall (u :: * -> *) b a . 
       (DepCheckable (u b) (u (DDeps, b)) (NormalizeData1 b),
        DAdditionalData (u (DDeps, b)) (DDeps, b)) 
       => [(DSmplDef b)] -> (u b) -> [DVarName] -> State (DEnvDC (NormalizeData1 b)) ([(DSmplDef (DDeps, b))], (u (DDeps, b)), DDeps, [DNewName])
depCheckLet defs e vs = depCheckLetG' False defs e vs

-- general version 
depCheckLetG' 
    :: forall (t :: * -> *) (u :: * -> *) b . 
       (DNamed (t b), 
        DNamed (t (DDeps, b)), 
        DepCheckable (t b) (t (DDeps, b)) (NormalizeData1 b),
        DepCheckable (u b) (u (DDeps, b)) (NormalizeData1 b),
        DAdditionalData (t (DDeps, b)) (DDeps, b),
        DAdditionalData (u (DDeps, b)) (DDeps, b)) 
       => Bool -> [(t b)] -> (u b) -> [DVarName] -> State (DEnvDC (NormalizeData1 b)) ([(t (DDeps, b))], (u (DDeps, b)), DDeps, [DNewName])
depCheckLetG' orgName defs e vs =
    do (_, nvs) <- addNames' orgName vs  -- add the argument variables first,
       let ons = concatMap getNames defs
       (depth, nns) <- addNames ons -- then add the let-bindings
       defs' <- mapM depCheck defs
       e' <- depCheck e
       let defs'' = replaceNames nns defs'  
           dss = map (getDepL depth) defs'' ++ [getDepL depth e'] 
           ds = normalizeDepsX dss
           defs''' = depTopoSort depth $ defs''  -- sort the bindings according to their dependency
       popNames
       popNames
       return (defs''', e', ds, nvs)

{- replaces old names with newnames at the depth -}
replaceNames nns dfs = dfs'
  where dfs' = evalState (mapM setNames dfs) nns

instance (Show a) => DepCheckable (DProgramSpec a) (DProgramSpec (DDeps, a)) (NormalizeData1 a) where
    depCheck (DProgramSpec rs p a) = 
        do (rs', p', ds, _) <- depCheckLetG' False rs p []
           return (DProgramSpec rs' p' (ds, a))

{- newly introduced names have no dependency -}
nullDep :: forall (k :: * -> *) a.
                 DAdditionalData2 (k a) a (DDeps, a) (k (DDeps, a)) =>
                 (k a) -> (k (DDeps, a))
nullDep f = mapData null f
  where null :: a -> (DDeps, a)
        null a = ([], a)

instance Show a => DepCheckable (DRecordSpec a) (DRecordSpec (DDeps, a)) (NormalizeData1 a) where
    depCheck (DRecordSpec c fts a) =
        do dst <- mapM depCheck (map snd fts)
           let dsf = map nullDep (map fst fts)
               c' = nullDep c
               fts' = zip dsf dst
               ds = normalizeDeps $ concatMap getDep dst
               ret = (DRecordSpec c' fts' (ds, a))
           return ret

instance DepCheckable (DType a) (DType (DDeps, a)) (NormalizeData1 a) where
    depCheck (DTInt a) = return (DTInt ([],a))
    depCheck (DTBool a) = return (DTBool ([],a))
    depCheck (DTString a) = return (DTString ([],a))
    depCheck (DTDouble a) = return (DTDouble ([],a))
    depCheck (DTTuple ts a) = 
        do ts' <- mapM depCheck ts
           let ds = normalizeDeps $ concatMap getDep ts'
           return (DTTuple ts' (ds, a))
    depCheck (DTRecord c ts a) = 
        do c' <- depCheck c
           ts' <- mapM depCheck ts
           let ds = normalizeDeps $ concatMap getDep ts' ++ getDep c'
           return (DTRecord c' ts' (ds,a))

instance DepCheckable (DConst a) (DConst (DDeps, a)) (NormalizeData1 a) where
    depCheck (DCInt c a) = return (DCInt c ([], a))
    depCheck (DCBool c a) = return (DCBool c ([], a))
    depCheck (DCString c a) = return (DCString c ([], a))
    depCheck (DCDouble c a) = return (DCDouble c ([], a))

instance DepCheckable (DProg a) (DProg (DDeps, a)) (NormalizeData1 a) where
    depCheck (DProg f defs e a) = 
        do (defs', e', ds, _) <- depCheckLetG' True defs e ["g"]
           let f' = nullDep f
           return (DProg f' defs' e' (ds, a))

instance DepCheckable (DGroundDef a) (DGroundDef (DDeps, a)) (NormalizeData1 a) where
    depCheck (DGDefVI d a) = do d' <- depCheck d
                                return (DGDefVI d' (getDep d', a))
    depCheck (DGDefVC d a) = do d' <- depCheck d
                                return (DGDefVC d' (getDep d', a))
    depCheck (DGDefGV d a) = do d' <- depCheck d
                                return (DGDefGV d' (getDep d', a))
    depCheck (DGDefGF d a) = do d' <- depCheck d
                                return (DGDefGF d' (getDep d', a))
    depCheck (DGDefSmpl d a) = do d' <- depCheck d
                                  return (DGDefSmpl d' (getDep d', a))

instance DepCheckable (DDefVertComp a) (DDefVertComp (DDeps, a)) (NormalizeData1 a) where
    depCheck (DDefVertComp f defs e a) = 
        do (defs', e', ds, _) <- depCheckLetG' True defs e ["v", "prev", "curr"]
           let f' = nullDep f
               defs'' = concatMap collectDef defs'
           return (DDefVertComp f' defs'' e' (ds, a))

instance DepCheckable (DDefVertInit a) (DDefVertInit (DDeps, a)) (NormalizeData1 a) where
    depCheck (DDefVertInit f defs e a) = 
        do (defs', e', ds, _) <- depCheckLetG' True defs e ["v"]
           let f' = nullDep f
               defs'' = concatMap collectDef defs'
           return (DDefVertInit f' defs'' e' (ds, a))

instance DepCheckable (DDefGraphVar a) (DDefGraphVar (DDeps, a)) (NormalizeData1 a) where
    depCheck (DDefGraphVar v e a) = 
        do e' <- depCheck e
           let v' = nullDep v
           return (DDefGraphVar v' e' (getDep e', a))

instance DepCheckable (DDefGraphFun a) (DDefGraphFun (DDeps, a)) (NormalizeData1 a) where
    depCheck (DDefGraphFun f v defs e a) = 
        do (defs', e', ds, vns) <- depCheckLetG' True defs e (getNames v)
           let f' = nullDep f
               [v'] = replaceNames vns [nullDep v]
           return (DDefGraphFun f' v' defs' e' (ds, a))

instance DepCheckable (DSmplDef a) (DSmplDef (DDeps, a)) (NormalizeData1 a) where
    depCheck (DDefFun f vs defs e a) = 
        do let vns = concatMap getNames vs
               f' = nullDep f
               vs' = map nullDep vs
           (defs', e', ds, vns') <- depCheckLet defs e vns
           let vs'' = replaceNames vns' vs'
           return (DDefFun f' vs'' defs' e' (ds, a))
    depCheck (DDefVar v defs e a) = 
        do (defs', e', ds, _) <- depCheckLet defs e []
           let v' = nullDep v
           return (DDefVar v' defs' e' (ds, a))
    depCheck (DDefTuple vs defs e a) = 
        do (defs', e', ds, _) <- depCheckLet defs e []
           let vs' = map nullDep vs
           return (DDefTuple vs' defs' e' (ds, a))

instance DepCheckable (DTermination a) (DTermination (DDeps, a)) (NormalizeData1 a) where
    depCheck (DTermF a) = return (DTermF ([], a)) 
    depCheck (DTermI e a) = 
        do e' <- depCheck e
           return (DTermI e' (getDep e', a))
    depCheck (DTermU e a) = 
        do addNames' True ["g"]
           e' <- depCheck e
           popNames
           return (DTermU e' (getDep e', a))

instance DepCheckable (DGraphExpr a) (DGraphExpr (DDeps, a)) (NormalizeData1 a) where
    depCheck (DPregel f0 ft x g a) = 
        do f0' <- depCheck f0
           ft' <- depCheck ft
           x' <- depCheck x
           g' <- depCheck g
           let ds = getDeps4 f0' ft' x' g'
           return (DPregel f0' ft' x' g' (ds, a))
    depCheck (DGMap f g a) = 
        do f' <- depCheck f
           g' <- depCheck g
           let ds = getDeps f' g'
           return (DGMap f' g' (ds, a))
    depCheck (DGZip g1 g2 a) = 
        do g1' <- depCheck g1
           g2' <- depCheck g2
           let ds = getDeps g1' g2'
           return (DGZip g1' g2' (ds, a))
    depCheck (DGIter f0 ft x g a) = 
        do f0' <- depCheck f0
           ft' <- depCheck ft
           x' <- depCheck x
           g' <- depCheck g
           let ds = getDeps4 f0' ft' x' g'
           return (DGIter f0' ft' x' g' (ds, a))
    depCheck (DGVar v a) = 
        do v' <- depCheck v 
           return (DGVar v' (getDep v', a))


instance DepCheckable (DExpr a) (DExpr (DDeps, a)) (NormalizeData1 a) where
    depCheck (DIf p t e a) = 
        do p' <- depCheck p
           t' <- depCheck t
           e' <- depCheck e
           let ds = getDeps3 p' t' e'
           return (DIf p' t' e' (ds, a))
    depCheck (DTuple es a) =
        do es' <- mapM depCheck es 
           let ds = getDepsX es'
           return (DTuple es' (ds, a))
    depCheck (DFunAp f es a) =
        do f' <- depCheck f
           es' <- mapM depCheck es
           let ds = normalizeDepsX (getDep f' : map getDep es')
           return (DFunAp f' es' (ds, a))
    depCheck (DConsAp c es a) =
        do c' <- depCheck c
           es' <- mapM depCheck es
           let ds = normalizeDepsX (getDep c' : map getDep es')
           return (DConsAp c' es' (ds, a))
    depCheck (DFieldAcc t fs a) =
        do t' <- depCheck t
           fs' <- mapM depCheck fs
           let ds = normalizeDepsX (getDep t' : map getDep fs')
           return (DFieldAcc t' fs' (ds, a))
    depCheck (DFieldAccE e fs a) =
        do e' <- depCheck e
           fs' <- mapM depCheck fs
           let ds = normalizeDepsX (getDep e' : map getDep fs')
           return (DFieldAccE e' fs' (ds, a))
    depCheck (DAggr a' e g es a) =
        do let a'' = nullDep a'
           g' <- depCheck g
           (depth, _) <- addNames' True (getNames g)
           e' <- depCheck e
           es' <- mapM depCheck es
           popNames
           let ds = normalizeDepsX (getDep g' : map (getDepL depth) (e':es'))
           return (DAggr a'' e' g' es' (ds, a))
    depCheck (DVExp v a) =
        do v' <- depCheck v
           return (DVExp v' (getDep v', a))
    depCheck (DCExp c a) = 
        do c' <- depCheck c 
           return (DCExp c' (getDep c', a))

instance DepCheckable (DGen a) (DGen (DDeps, a)) (NormalizeData1 a) where
    depCheck (DGenI a) = 
        do d <- checkDefined "v"           -- hard-coded...
           return (DGenI ([d], a))
    depCheck (DGenO a) = 
        do d <- checkDefined "v"
           return (DGenO ([d], a))
    depCheck (DGenG a) = 
        do return (DGenG ([], a))
    depCheck (DGenTermG a) = 
        do return (DGenTermG ([], a))

instance DepCheckable (DEdge a) (DEdge (DDeps, a)) (NormalizeData1 a) where
    depCheck (DEdge a) = 
        do d <- checkDefined "e"
           return (DEdge ([d], a))

instance DepCheckable (DTableExpr a) (DTableExpr (DDeps, a)) (NormalizeData1 a) where
    depCheck (DPrev v a) = 
        do v' <- depCheck v 
           d <- checkDefined "prev"
           return (DPrev v' (d:getDep v', a))
    depCheck (DCurr v a) = 
        do v' <- depCheck v 
           d <- checkDefined "curr"
           return (DCurr v' (d:getDep v', a))
    depCheck (DVal v a) = 
        do v' <- depCheck v 
           return (DVal v' (getDep v', a))

checkDefined n = 
  do binds <- getEnvBinds
     case (lookup n binds) of
       Just d -> return (n, d)
       Nothing -> error ("\nUndefined in dependency check " ++ n ++ " in " ++ show binds)

-- do not use these depChecks in field/fun/var/constructor definitions
instance DepCheckable (DField a) (DField (DDeps, a)) (NormalizeData1 a) where
    depCheck (DField f a) = 
        do d <- checkDefined f
           return (DField (getDVNewName d) ([d], a))
--    depCheck (DFfst a) = 
--        do return (DFfst ([], a))
--    depCheck (DFsnd a) = 
--        do return (DFsnd ([], a))

instance DepCheckable (DFun a) (DFun (DDeps, a)) (NormalizeData1 a) where
    depCheck (DFun f a) = 
        do d <- checkDefined f
           return (DFun (getDVNewName d) ([d], a))
    depCheck (DBinOp f a) = 
        do d <- checkDefined f
           return (DBinOp (getDVNewName d) ([d], a))

instance DepCheckable (DVar a) (DVar (DDeps, a)) (NormalizeData1 a) where
    depCheck (DVar v a) = 
        do d <- checkDefined v
           return (DVar (getDVNewName d) ([d], a))

instance DepCheckable (DConstructor a) (DConstructor (DDeps, a)) (NormalizeData1 a) where
    depCheck (DConstructor c a) = 
        do d <- checkDefined c
           return (DConstructor (getDVNewName d) ([d], a))