hgom-0.5.1: Gom/CodeGen/Common/GenMonad.hs
------------------------------------------------------------------
-- |
-- Module : Gom.CodeGen.Common.GenMonad
-- Copyright : (c) Paul Brauner 2009
-- (c) Emilie Balland 2009
-- (c) INRIA 2009
-- Licence : GPL (see COPYING)
--
-- Maintainer : paul.brauner@inria.fr
-- Stability : provisional
-- Portability : non-portable (requires generalized newtype deriving)
--
-- Defines the 'Gen' monad, a context containing a read-only symbol table.
--------------------------------------------------------------------
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Gom.CodeGen.Common.GenMonad (
-- * Definition
Gen(),
runGen,
-- * Basic access
askSt,
askConf,
ifConf,
ifConfM,
iterOverFields,
iterOverSortFields,
-- * High-Level access
packagePrefix,
qualifiedSort,
qualifiedCtor,
abstractType,
qualifiedAbstractType,
qualifiedStratPrefix
) where
import Gom.Sig
import Gom.SymbolTable
import Gom.CodeGen.Common.Builtins
import Gom.Config
import Text.PrettyPrint.Leijen
import Control.Monad.Reader
import Control.Applicative
import Data.Char(toLower)
import Data.List(nub, intersperse)
-- | A computation inside a context containing a read-only symbol table.
newtype Gen a = Gen (Reader (SymbolTable,Config) a)
deriving (Functor, Monad, MonadReader (SymbolTable,Config))
instance Applicative Gen where
(<*>) = ap
pure = return
-- | Run the Gen monad
runGen :: Gen a -> SymbolTable -> Config -> a
runGen (Gen comp) st c = runReader comp (st,c)
-- | Query symbol table.
askSt :: (SymbolTable -> a) -> Gen a
askSt f = asks (f . fst)
-- | Query configuration.
askConf :: (Config -> a) -> Gen a
askConf f = asks (f . snd)
-- | @ifConf f e d@ is @return e@ if @f config@ holds, else @return d@ .
--
-- Example usage:
--
-- > do hs <- ifConf haskell [rMethod ...] []
-- > return rClass ... (vcat $ defaultMethods ++ hs)
ifConf :: (Config -> Bool) -> a -> a -> Gen a
ifConf f e d = ifConfM f (return e) (return d)
-- | @ifConfM f e d@ is @e@ if @f config@ holds, else @d@ .
--
-- Example usage:
--
-- > do hs <- ifConfM haskell compSomeMethods (return [])
-- > return rClass ... (vcat $ defaultMethods ++ hs)
ifConfM :: (Config -> Bool) -> Gen a -> Gen a -> Gen a
ifConfM f e d = do cond <- askConf f
if cond then e else d
-- | Helper fonction that iters over the fields of
-- a constructor and combines them.
iterOverFields
:: (FieldId -> SortId -> Gen a) -- ^ the function to iter
-> ([a] -> b) -- ^ the combinator
-> CtorId -- ^ the constructor
-> Gen b
iterOverFields f g c = do fis <- askSt (fieldsOf c)
fis' <- mapM (uncurry f) fis
return $ g fis'
-- | Helper fonction for 'compEmptyGettersOfSort' and
-- 'compEmptySettersOfSort'. Iters the first argument on the fields of the
-- subject sort and combines them with the second one. Duplicate fields are
-- merged.
iterOverSortFields
:: (SortId -> FieldId -> SortId -> Gen a)
-- ^ gets codomain, field name and field sort
-> ([a] -> b) -- ^ combinator of results
-> SortId -- ^ subject sort
-> Gen b
iterOverSortFields f g s = do cs <- askSt (sCtorsOf s)
fs <- (nub . concat) `fmap` mapM combine cs
ms <- mapM (\(co,(fi,ty)) -> f co fi ty) fs
return $ g ms
where combine c = do fis <- askSt (fieldsOf c)
co <- askSt (codomainOf c)
return $ map ((,) co) fis
-- | Returns the package prefix ended by a dot.
--
-- As an example, returns @aa.bb.cc.foo@ for the module @Foo@,
-- provided the user toggled @-p aa.bb.cc@.
packagePrefix :: Gen Doc
packagePrefix = do m <- map toLower `fmap` askSt modName
go (pretty m) `fmap` askConf package
where go dm Nothing = dm
go dm (Just l) = hcat . intersperse dot $ map text l ++ [dm]
-- | Given a sort @S@, returns @module.types.S@
qualifiedSort :: SortId -> Gen Doc
qualifiedSort s
| isBuiltin s = return $ pretty s
| otherwise = do p <- packagePrefix
return $ p <> dot <> text "types" <> dot <> pretty s
-- | Given a constructor @C@ of codomain @S@, returns
-- @module.types.s.C@
qualifiedCtor :: CtorId -> Gen Doc
qualifiedCtor c =
do p <- packagePrefix
lows <- lowerId `fmap` askSt (codomainOf c)
return $ p <> dot <> text "types" <> dot <>
pretty lows <> dot <> pretty c
-- | Generates @mAbstractType@ for the current module @m@.
abstractType :: Gen String
abstractType = do mn <- askSt modName
return $ mn ++ "AbstractType"
-- | Generates @m.mAbstractType@ for the current module @m@.
qualifiedAbstractType :: Gen Doc
qualifiedAbstractType = do at <- abstractType
pr <- packagePrefix
return $ pr <> dot <> text at
-- | Given a sort @S@ in the module @m@, generates @m.strategy.s@
qualifiedStratPrefix :: SortId -> Gen Doc
qualifiedStratPrefix s = do
m <- packagePrefix
return $ m <> dot <> text "strategy" <> dot <> pretty (lowerId s)