FormalGrammars-0.0.0.2: FormalLanguage/CFG/TH.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternGuards #-}
-- |
--
-- TODO we should check if it is possible to go a bit ``lower'' to the more raw
-- stuff, instead of trying to rebuild the top-level ADPfusion syntax. Thats
-- mostly for the RHS of rules.
--
-- TODO we should build the algebra product automatically (but that piece of TH
-- should go into ADPfusion)
module FormalLanguage.CFG.TH where
import Data.Char (toUpper,toLower)
import Control.Applicative
import Control.Arrow ((&&&))
import Control.Lens hiding (Strict)
import Control.Monad
import Control.Monad.Trans.Class
import Data.Array.Repa.Index
import Data.Function (on)
import Data.List (intersperse,nub,nubBy,groupBy)
import Data.Maybe
import Data.Vector.Fusion.Stream.Monadic (Stream)
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import qualified Data.Map as M
import qualified Data.Set as S
import ADP.Fusion ( (%), (|||), (...), (<<<) )
import qualified ADP.Fusion.Multi as ADP
import FormalLanguage.CFG.Grammar
-- * Local data ctors we use to build up signature and grammar
data TheTT = TheTT
{ _ttType :: TyVarBndr
, _ttName :: Name
, _ttPat :: Pat
}
deriving (Show)
makeLenses ''TheTT
data TheF = TheF
{ _fName :: Name
, _fVar :: Exp
, _fStrict :: Strict
, _fType :: Type
}
deriving (Show)
makeLenses ''TheF
fVarStrictType :: Lens' TheF VarStrictType
fVarStrictType = lens get set where
get :: TheF -> VarStrictType
get f = (f^.fName, f^.fStrict, f^.fType)
set :: TheF -> VarStrictType -> TheF
set f (v,s,t) = f { _fName = v, _fStrict = s, _fType = t }
data TheN = TheN
{ _nName :: Name
, _nVar :: Exp
, _nPat :: Pat
}
deriving (Show)
makeLenses ''TheN
data TheT = TheT
{ _tNames :: [Name]
, _tVar :: Exp
, _tType :: Type
}
deriving (Show)
makeLenses ''TheT
data TheS = TheS
{ _sString :: String
, _sName :: Name
, _sVarP :: Pat
, _sConT :: Type
}
makeLenses ''TheS
-- * Builder functions
-- | Build the signature type and data constructor
genTheS s = do
let n = "Sig" ++ s
return $ TheS n (mkName n) (VarP . mkName . headLower $ n) (ConT . mkName $ n)
-- | the new generator
newGen :: Grammar -> Q [Dec]
newGen g = do
m <- newName "m"
x <- newName "x"
r <- newName "r"
ix <- newName "ix"
ns <- M.fromList <$> (mapM genN $ collectSymbN g)
tt <- M.fromList <$> (mapM genTT . nub $ g^..tsyms.folded.symb.folded.tnName)
ts <- M.fromList <$> (mapM (genT tt) $ collectSymbT g)
fs <- M.fromList <$> (mapM (genF x ts) . nubBy ((==) `on` _fun) $ g^..rules.folded)
h <- genHfun m x r
sg <- genTheS $ g^.name
runIO $ print fs
sig <- dataD (cxt [])
(sg^.sName)
(PlainTV m:PlainTV x:PlainTV r:(tt^..folded.ttType))
[recC (sg^.sName) ((map return $ fs^..folded.fVarStrictType) ++ [return h])
]
[]
let graArgs = (recP (sg^.sName) ((return (h^._1, VarP $ h^._1)):[return (n, VarP n) | n <- fs^..folded.fName]))
: (map (return . view nPat) $ ns^..folded)
++ (map (return . view ttPat) $ tt^..folded)
let graBody = normalB . tupE . map (genBodyPair h ix ns ts fs) . groupBy ((==)`on`_lhs) $ g^..rules.folded
gra <- funD (mkName $ "g" ++ g^.name) [clause graArgs graBody []]
inl <- pragInlD (mkName $ "g" ++ g^.name) Inline FunLike AllPhases
return [sig,gra,inl]
-- | The body is a series of pairs, built here
genBodyPair h ix ns ts fs rs = do
let r = head rs
let rhs = lamE [varP ix]
$ appE ( uInfixE (foldl1 (\acc z -> uInfixE acc (varE '(|||)) z) . map (genBodyRhs ns ts fs) $ rs)
(varE '(...))
(varE $ h^._1) )
(varE ix)
tupE [return . view nVar $ ns M.! (r^.lhs), rhs]
-- | the right-hand sides involved in each rule
genBodyRhs ns ts fs (Rule _ f rs) = appE (appE (varE '(<<<)) (return . view fVar $ fs M.! f))
. foldl1 (\acc z -> uInfixE acc (varE '(%)) z) . map genS $ rs
where genS s
| isSymbT s = return . view tVar $ ts M.! s
| isSymbN s = return . view nVar $ ns M.! s
-- | the objective function @h@ is always of the same type, we need to make
-- sure that stream payload and return here are different for things like
-- classified DP.
genHfun :: Name -> Name -> Name -> Q VarStrictType
genHfun m x r = do
let n = "h"
let strm = ConT ''Stream
let args = AppT ArrowT . AppT (AppT strm (VarT m)) $ VarT x
let rtrn = AppT (VarT m) (VarT r)
return (mkName n, NotStrict, AppT args rtrn)
-- | Generate all the information for single terminals
genTT :: String -> Q (String,TheTT)
genTT t = do
nn <- newName t
return (t, TheTT (PlainTV nn) nn (VarP nn))
-- | Generate all the function information. Note that we do not create a new
-- name here, because users need to be able to easily identify all the
-- signature functions.
genF :: Name -> M.Map Symb TheT -> Rule -> Q ([String],TheF)
genF tyN theT r = do
let nn = mkName . headLower . concat . map headUpper $ r^.fun
let args = map (AppT ArrowT . genFArg tyN theT) $ r^.rhs
return (r^.fun, TheF nn (VarE nn) NotStrict (foldr AppT (VarT tyN) args))
-- | builds up a function argument
genFArg :: Name -> M.Map Symb TheT -> Symb -> Type
genFArg tyN theT s
| isSymbT s = view tType $ theT M.! s
| isSymbN s = VarT tyN
| otherwise = error $ "incompatible symbol: " ++ show s
-- | associate each non-terminal with a new name for the variable in the grammar
genN :: Symb -> Q (Symb,TheN)
genN s = do
nn <- newName "n"
return (s, TheN nn (VarE nn) (VarP nn))
-- | builds up a terminal symbol, in 1-dim stuff we just have the terminal
-- symbol; in multi-dim cases we build up using ADPfusion stuff.
genT :: M.Map String TheTT -> Symb -> Q (Symb,TheT)
genT tt s@(Symb [z]) = do
let n = view ttName $ tt M.! (z^.tnName)
return $ (s, TheT [n] (VarE n) (VarT n))
genT tt s@(Symb zs) = do
let ns = map (view ttName . (tt M.!) . view tnName) zs
k <- foldl (\acc z -> uInfixE acc (varE '(ADP.:!)) z) (varE 'T) . map varE $ ns
let t = foldl (\l r -> AppT (AppT (ConT '(:.)) l) r) (ConT 'Z) (map VarT ns)
return $ (s, TheT ns k t)
-- * helper functions
headUpper [] = []
headUpper (x:xs) = toUpper x : xs
headLower [] = []
headLower (x:xs) = toLower x : xs