module Language.Clafer.Intermediate.Analysis where
import Language.Clafer.Front.Absclafer hiding (Path)
import qualified Language.Clafer.Intermediate.Intclafer as I
import Language.Clafer.Intermediate.Desugarer
import Language.Clafer.Front.Printclafer
import Control.Applicative
import Control.Monad.LPMonad.Supply
import Control.Monad.Error
import Control.Monad.Identity
import Control.Monad.List
import Control.Monad.Maybe
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Data.Either
import Data.List
import Data.Maybe
newtype AnalysisT m a = AnalysisT (ReaderT Info m a)
deriving (Monad, Functor, MonadReader Info, MonadState s, MonadTrans, MonadPlus, MonadError e, Applicative, Alternative)
type Analysis = AnalysisT Identity
class (Monad m, Functor m) => MonadAnalysis m where
clafers :: m [SClafer]
withClafers :: [SClafer] -> m a -> m a
withExtraClafers :: MonadAnalysis m => [SClafer] -> m a -> m a
withExtraClafers cs a =
do
c <- clafers
withClafers (cs ++ c) a
instance (Monad m, Functor m) => MonadAnalysis (AnalysisT m) where
clafers = AnalysisT $ asks sclafers
withClafers cs = local (const $ Info cs)
instance (Error e, MonadAnalysis m) => MonadAnalysis (ErrorT e m) where
clafers = lift clafers
withClafers = mapErrorT . withClafers
instance MonadAnalysis m => MonadAnalysis (ListT m) where
clafers = lift clafers
withClafers = mapListT . withClafers
instance MonadAnalysis m => MonadAnalysis (MaybeT m) where
clafers = lift clafers
withClafers = mapMaybeT . withClafers
instance MonadAnalysis m => MonadAnalysis (ReaderT r m) where
clafers = lift clafers
withClafers = mapReaderT . withClafers
instance (Monoid w, MonadAnalysis m) => MonadAnalysis (WriterT w m) where
clafers = lift clafers
withClafers = mapWriterT . withClafers
instance MonadAnalysis m => MonadAnalysis (VSupplyT m) where
clafers = lift clafers
withClafers = mapVSupplyT . withClafers
isConcrete :: SClafer -> Bool
isConcrete = not . isAbstract
isBase :: SClafer -> Bool
isBase = (`elem` ["clafer", "string", "real", "int", "integer", "boolean"]) . uid
isDerived :: SClafer -> Bool
isDerived = not . isBase
data SSuper = Ref String | Colon String deriving Show
data SClafer = SClafer {uid::String, origUid::String, isAbstract::Bool, low::Integer, high::Integer, groupLow::Integer, groupHigh::Integer, parent::Maybe String, super::Maybe SSuper, constraints::[I.PExp]} deriving Show
data Info = Info{sclafers :: [SClafer]} deriving Show
runAnalysis :: Analysis a -> Info -> a
runAnalysis r info = runIdentity $ runAnalysisT r info
runAnalysisT :: AnalysisT m a -> Info -> m a
runAnalysisT (AnalysisT r) info = runReaderT r info
claferWithUid :: MonadAnalysis m => String -> m SClafer
claferWithUid u =
do
c <- clafers
case find ((==) u . uid) c of
Just c' -> return c'
Nothing -> error $ "claferWithUid: Unknown uid " ++ u
parentUid :: Monad m => SClafer -> m String
parentUid clafer =
case parent clafer of
Just p -> return p
Nothing -> fail $ "No parent uid for " ++ show clafer
parentOf :: (Uidable c, MonadAnalysis m) => c -> m c
parentOf clafer = fromUid =<< parentUid =<< toClafer clafer
parentsOf :: (Uidable c, MonadAnalysis m) => c -> m [c]
parentsOf clafer =
do
r <- runMaybeT $ parentOf clafer
case r of
Just r' -> (r' :) <$> parentsOf r'
Nothing -> return []
ancestorsOf :: (Uidable c, MonadAnalysis m) => c -> m [c]
ancestorsOf clafer = (clafer :) <$> parentsOf clafer
directChildrenOf :: (Uidable c, MonadAnalysis m) => c -> m [c]
directChildrenOf c =
do
cs <- (anything |^ c) `select` fst
mapM fromClafer cs
directDescendantsOf :: (Uidable c, MonadAnalysis m) => c -> m [c]
directDescendantsOf c =
do
cs <- (anything |^ c) `select` fst
css <- mapM directDescendantsOf cs
mapM fromClafer $ cs ++ concat css
topNonRootAncestor :: (Uidable c, MonadAnalysis m) => c -> m c
topNonRootAncestor clafer =
do
uid' <- toUid clafer
when (uid' == rootUid) $ error "Root does not have a non root ancestor."
(head . tail . reverse) <$> ancestorsOf clafer
refUid :: Monad m => SClafer -> m String
refUid clafer =
case super clafer of
Just (Ref u) -> return u
_ -> fail $ "No ref uid for " ++ show clafer
refOf :: (Uidable c, MonadAnalysis m) => c -> m c
refOf clafer = fromUid =<< refUid =<< toClafer clafer
refsOf :: (Uidable c, MonadAnalysis m) => c -> m [c]
refsOf clafer =
do
r <- runMaybeT $ refOf clafer
case r of
Just r' -> (r' :) <$> refsOf r'
Nothing -> return []
colonUid :: (Uidable c, MonadAnalysis m) => c -> m String
colonUid c =
do
clafer <- toClafer c
case super clafer of
Just (Colon u) -> return u
_ -> fail $ "No colon uid for " ++ show clafer
colonOf :: (Uidable c, MonadAnalysis m) => c -> m c
colonOf clafer = fromUid =<< colonUid =<< toClafer clafer
colonsOf :: (Uidable c, MonadAnalysis m) => c -> m [c]
colonsOf clafer =
do
r <- runMaybeT $ colonOf clafer
case r of
Just r' -> (r' :) <$> colonsOf r'
Nothing -> return []
colonsTo :: (Uidable c, MonadAnalysis m) => c -> m [c]
colonsTo clafer =
runListT $ do
(sub, _) <- foreach $ anything |: clafer
fromClafer =<< (return sub `mplus` foreach ( colonsTo sub))
hierarchy :: (Uidable c, MonadAnalysis m) => c -> m [c]
hierarchy t = (t :) <$> colonsOf t
isDirectChild :: (Uidable c, MonadAnalysis m) => c -> c -> m Bool
isDirectChild c p = (not . null) <$> (c |^ p)
isIndirectChild :: (Uidable c, MonadAnalysis m) => c -> c -> m Bool
isIndirectChild c p =
fromMaybeT False $ do
child <- toClafer c
parent <- toClafer p
s <- colonOf parent
when (uid s == "clafer") mzero
isChild child s
isChild :: (Uidable c, MonadAnalysis m) => c -> c -> m Bool
isChild child parent =
liftM2 (||) (isDirectChild child parent) (isIndirectChild child parent)
class Matchable c => Uidable c where
toClafer :: MonadAnalysis m => c -> m SClafer
fromClafer :: MonadAnalysis m => SClafer -> m c
toUid :: MonadAnalysis m => c -> m String
fromUid :: MonadAnalysis m => String -> m c
instance Uidable SClafer where
toClafer = return
fromClafer = return
toUid = return . uid
fromUid = claferWithUid
instance Uidable String where
toClafer = claferWithUid
fromClafer = return . uid
toUid = return
fromUid = return
data Anything = Anything
class Matchable u where
matches :: u -> SClafer -> Bool
instance Matchable String where
matches s c = s == uid c
instance Matchable Anything where
matches _ _ = True
instance Matchable SClafer where
matches c1 c2 = uid c1 == uid c2
anything :: Anything
anything = Anything
(|^) :: (MonadAnalysis m, Matchable a, Matchable b) => a -> b -> m [(SClafer, SClafer)]
lower |^ upper = runListT $ do
clafer <- foreach clafers
guard $ matches lower clafer
parent <- parentOf clafer
guard $ matches upper parent
return (clafer , parent)
(|->) :: (MonadAnalysis m, Matchable a, Matchable b) => a -> b -> m [(SClafer, SClafer)]
lower |-> upper = runListT $ do
clafer <- foreach clafers
guard $ matches lower clafer
super <- refOf clafer
guard $ matches upper super
return (clafer, super)
(|:) :: (MonadAnalysis m, Matchable a, Matchable b) => a -> b -> m [(SClafer, SClafer)]
lower |: upper = runListT $ do
clafer <- foreach clafers
guard $ matches lower clafer
super <- colonOf clafer
guard $ matches upper super
return (clafer, super)
constraintsUnder :: (MonadAnalysis m, Matchable a) => a -> m [(SClafer, I.PExp)]
constraintsUnder under =
do
clafers' <- filter (matches under) <$> clafers
return [(clafer, constraint) | clafer <- clafers', constraint <- constraints clafer]
rootUid :: String
rootUid = "_root"
convertClafer :: I.IClafer -> [SClafer]
convertClafer =
convertClafer' Nothing
where
convertElement' parent (I.IEClafer clafer) = Just $ Left $ convertClafer' parent clafer
convertElement' _ (I.IEConstraint _ pexp) = Just $ Right $ pexp
convertElement' _ _ = Nothing
convertClafer' parent clafer =
sclafer : concat children
where
sclafer
| maybe 1 groupLow parent == 0 && maybe 1 groupHigh parent /= 1 =
SClafer (I._uid clafer) (I._uid clafer) (I._isAbstract clafer) 1 high gLow gHigh (uid <$> parent) super constraints
| otherwise =
SClafer (I._uid clafer) (I._uid clafer) (I._isAbstract clafer) low high gLow gHigh (uid <$> parent) super constraints
(children, constraints) = partitionEithers $ mapMaybe (convertElement' $ Just $ sclafer) (I._elements clafer)
Just (low, high) = I._card clafer
(gLow, gHigh) =
case I._gcard clafer of
Nothing -> (0, 1)
Just (I.IGCard True _) -> (0, 1)
Just (I.IGCard _ i) -> i
super =
case I._super clafer of
I.ISuper True [I.PExp{I._exp = I.IClaferId{I._sident = superUid}}] -> Just $ Ref superUid
I.ISuper False [I.PExp{I._exp = I.IClaferId{I._sident = superUid}}] ->
if superUid `elem` ["string", "real", "int", "integer", "boolean"]
then Just $ Ref superUid
else Just $ Colon superUid
_ -> Nothing
gatherInfo :: I.IModule -> Info
gatherInfo imodule =
Info $ sClafer : sInteger : sInt : sReal : sString : sBoolean : convertClafer root
where
sClafer = SClafer "clafer" "clafer" False 0 (1) 0 (1) Nothing Nothing []
sInteger = SClafer "integer" "integer" False 0 (1) 0 (1) Nothing Nothing []
sInt = SClafer "int" "int" False 0 (1) 0 (1) Nothing Nothing []
sReal = SClafer "real" "real" False 0 (1) 0 (1) Nothing Nothing []
sString = SClafer "string" "string" False 0 (1) 0 (1) Nothing Nothing []
sBoolean = SClafer "boolean" "boolean" False 0 (1) 0 (1) Nothing Nothing []
root = I.IClafer noSpan False Nothing rootUid rootUid (I.ISuper False [I.PExp Nothing "" noSpan $ I.IClaferId "clafer" "clafer" True]) (Just (1, 1)) (0, 0) $ I._mDecls imodule
liftMaybe :: Monad m => Maybe a -> MaybeT m a
liftMaybe = MaybeT . return
liftList :: Monad m => [a] -> ListT m a
liftList = ListT . return
runListT_ :: Monad m => ListT m a -> m ()
runListT_ l = runListT l >> return ()
foreach :: m[a] -> ListT m a
foreach = ListT
foreachM :: Monad m => [a] -> ListT m a
foreachM = ListT . return
subClafers :: (a, b) -> a
subClafers = fst
superClafers :: (a, b) -> b
superClafers = snd
findAll :: Monad m => m a -> ListT m a
findAll = lift
select :: Monad m => m [a] -> (a -> b) -> m [b]
select from f = from >>= return . map f
suchThat :: Monad m => m [a] -> (a -> Bool) -> m [a]
suchThat = flip $ liftM . filter
concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
concatMapM f l = concat `liftM` mapM f l
whenM :: Monad m => m Bool -> m () -> m ()
whenM a b = a >>= (`when` b)
unlessM :: Monad m => m Bool -> m() -> m()
unlessM a b = a >>= (`unless` b)
fromMaybeT :: Monad m => a -> MaybeT m a -> m a
fromMaybeT def m = fromMaybe def `liftM` runMaybeT m
mapMaybeT :: (m1 (Maybe a1) -> m (Maybe a)) -> MaybeT m1 a1 -> MaybeT m a
mapMaybeT f = MaybeT . f . runMaybeT
mapVSupplyT :: (Monad m, Monad m1) => (m1 a1 -> m a) -> VSupplyT m1 a1 -> VSupplyT m a
mapVSupplyT f = lift . f . runVSupplyT
mapLeft :: (t -> a) -> Either t b -> Either a b
mapLeft f (Left l) = Left $ f l
mapLeft _ (Right r) = Right r
mapRight :: (t -> b) -> Either a t -> Either a b
mapRight _ (Left l) = Left l
mapRight f (Right r) = Right $ f r
(<:>) :: Monad m => m a -> m [a] -> m [a]
(<:>) = liftM2 (:)
testing :: Eq b => (a -> b) -> a -> a -> Bool
testing f a b = f a == f b
comparing :: Ord b => (a -> b) -> a -> a -> Ordering
comparing f a b = f a `compare` f b
syntaxOf :: I.PExp -> String
syntaxOf = printTree . sugarExp
combine :: Ord a => [(a, b)] -> [(a, [b])]
combine =
map mergeGroup . groupBy (testing fst) . sortBy (comparing fst)
where
mergeGroup ((a, b):xs) = (a, b : map snd xs)
mergeGroup [] = error "Function mergeGroup from Analysis expected a non empty list, but was given an empty one"
sameAs :: I.PExp -> I.PExp -> Bool
sameAs e1 e2 = syntaxOf e1 == syntaxOf e2