packages feed

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