module Text.GRead.Derive (deriveGrammar, deriveSimpleGrammar) where
import Text.GRead.Grammar
import Language.AbstractSyntax.TTTAS
import Text.GRead.Derive.BindingGroup
import Data.List (nub, foldl', foldl1')
import Data.Foldable (foldr')
import Data.Map (Map)
import qualified Data.Map as Map (insertWith, empty, toList)
import Control.Monad (foldM)
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
deriveGrammar :: Name -> Q [Dec]
deriveGrammar name = do
bindingGroup <- getBindingGroup name
deriveGrammar' name bindingGroup
deriveSimpleGrammar :: Name -> Q [Dec]
deriveSimpleGrammar name = deriveGrammar' name []
deriveGrammar' :: Name -> BindingGroup -> Q [Dec]
deriveGrammar' name bindingGroup = do
(UserD _ args cs) <- getUserType name
body <- mkBody name cs bindingGroup
return [InstanceD (mkContext cs) (mkInstanceType name args) body]
mkContext :: [Con] -> Cxt
mkContext = map (ClassP ''Gram) . map (: []) . uniqueVars . consArgsTypes
mkInstanceType :: Name -> [Name] -> Type
mkInstanceType name = AppT (ConT ''Gram) . foldl1' AppT . (:) (ConT name) . map VarT
consArgsTypes :: [Con] -> [Type]
consArgsTypes = concatMap consArgs
where
consArgs :: Con -> [Type]
consArgs (NormalC _ args) = map snd args
consArgs (InfixC argl _ argr) = [snd argl, snd argr]
consArgs _ = error "Error, unsupported type."
uniqueVars :: [Type] -> [Type]
uniqueVars = nub . filter isVarT . unrollApps
where
unrollApps :: [Type] -> [Type]
unrollApps [] = []
unrollApps (a@(AppT _ _):ts) = unrollApp a ++ unrollApps ts
unrollApps (other:ts) = other : unrollApps ts
isVarT :: Type -> Bool
isVarT (VarT _) = True
isVarT _ = False
mkBody :: Name -> [Con] -> BindingGroup -> Q [Dec]
mkBody top cs bindingGroup = do
let bindingGroup' | bindingGroup == [] = [(top, [])]
| otherwise = bindingGroup
neededInstances = concatMap snd bindingGroup'
strongEdges <- mapM (calculateStrongEdges neededInstances) bindingGroup'
instances <- mapM (createInstances neededInstances strongEdges) bindingGroup'
let instances' = concat instances
nontsInstance = mkNontsInstance strongEdges bindingGroup' (instanceExps instances')
topPat = map varP (instanceNames instances')
env = appsE $ (lamE topPat nontsInstance) : (linkRefs (length instances'))
[d| grammar = DGrammar Zero $(sigE env (envSignature cs (instanceTypes instances'))) |]
where
instanceNames = map (fst . fst)
instanceTypes = map (snd . fst)
instanceExps = map snd
calculateStrongEdges :: [(Name, [[Type]])] -> (Name, [(Name, [[Type]])]) -> Q (Name, [Type])
calculateStrongEdges needed (typeName, _) = do
(UserD _ _ cs) <- getUserType typeName
return $ (typeName, mkNonBGEdges typeName (map fst needed) (bindingGroupEdges typeName needed) cs)
where
bindingGroupEdges tName nd = maybe [] concat $ Prelude.lookup tName nd
mkNonBGEdges self done before = filter (not . already done before self) . consArgsTypes
already :: [Name] -> [Type] -> Name -> Type -> Bool
already _ _ _ (VarT _) = True
already done before self c@(ConT name) = elem c before
|| elem name done
|| name == self
already done before self a@(AppT _ _) = elem a before
|| elem (conName a) done
|| (conName a) == self
where conName = (\(ConT name) -> name) . head . unrollApp
already _ _ _ _ = error "Error, unsupported type."
getEdges :: Name -> [(Name, [Type])] -> [Type]
getEdges name = maybe [] id . Prelude.lookup name
mkNontsInstance :: [(Name, [Type])] -> BindingGroup -> [ExpQ] -> Q Exp
mkNontsInstance strongEdges bindingGroup instances = do
nontsTypes <- mapM (mkNontsType strongEdges) bindingGroup
appsE $ (lamE (mkNontsPat nontsTypes bindingGroup) (foldr' appE [|Empty|] instances))
: (mkNonts strongEdges bindingGroup)
where
mkNonts edges = map (mkGrammarPart edges)
mkNontsPat types = map (\(t, v) -> sigP v (do return t)) . zip types . nontsPatVars
nontsPatVars = map (varP . type2Nonts . fst)
envSignature :: [Con] -> [Type] -> Q Type
envSignature cs types = if null (consVars cs)
then envSignature'
else forallT (consVars cs) (return $ mkContext cs) envSignature'
where
consVars :: [Con] -> [TyVarBndr]
consVars = map (\(VarT n) -> (PlainTV n)) . uniqueVars . consArgsTypes
envSignature' :: Q Type
envSignature' = foldl1' appT [conT ''Env, conT ''DGram, tupleTypes types, tupleTypes types]
tupleTypes :: [Type] -> Q Type
tupleTypes = foldr' ((\x xs -> appT (appT (tupleT 2) xs) x)) (conT ''()) . map return
createInstances :: [(Name, [[Type]])] -> [(Name, [Type])] -> (Name, [(Name, [[Type]])]) -> Q [((Name, Type), ExpQ)]
createInstances needed strongEdges (typeName, edges) = do
(UserD _ args _) <- getUserType typeName
let instancesNeeded = maybe [map VarT args] id $ Prelude.lookup typeName needed
return $ mkInstances instancesNeeded
++ mkArgInstances instancesNeeded
++ mkNonBGInstances typeName strongEdges
where
mkConsG :: Type -> ((Name, Type), ExpQ)
mkConsG typ = ((instName typeName typ, typ), [|consG grammar|])
mkInstances = map (createInstance (typeName, edges) (getEdges typeName strongEdges))
mkArgInstances = map mkConsG . filter isVarT . concat
mkNonBGInstances tName = map mkConsG . getEdges tName
createInstance :: (Name, [(Name, [[Type]])]) -> [Type] -> [Type] -> ((Name, Type), ExpQ)
createInstance (typeName, edges) strongEdges inst = ((iName, iType), [|consD $(appsE $ (varE $ type2Nonts typeName) : (selfArgs ++ strongEdgeArgs ++ nonBGStrongEdges)) |])
where iName = nameArgs (type2TopRef typeName) inst
iType = foldl1' AppT (ConT typeName : inst)
selfArgs = (varE iName) : (map (varE . instName typeName) inst)
strongEdgeArgs = concatMap refEdge edges
nonBGStrongEdges = map (varE . instName typeName) strongEdges
instName :: Name -> Type -> Name
instName top (VarT n) = var2TopRef top n
instName _ (ConT n) = type2TopRef n
instName _ app@(AppT _ _) = app2TopRef $ unrollApp app
instName _ _ = error "Error, unsupported type."
mkNontsType :: [(Name, [Type])] -> (Name, [(Name, [[Type]])]) -> Q Type
mkNontsType strongEdges (typeName, edges) = do
(UserD _ args _) <- getUserType typeName
let envName = mkName "env"
env = VarT envName
ref = ConT ''Ref
argsType = map VarT args
topType = foldl' AppT (ConT typeName) argsType
resultType = AppT (AppT (ConT ''DLNontDefs) topType) env
refTo = (topType : argsType) ++ concatMap edgeType edges ++ getEdges typeName strongEdges
refs = map (\r -> AppT (AppT ref r) env) refTo
nontsType = foldr' (\r rs -> AppT (AppT ArrowT r) rs) resultType refs
return $ ForallT ((PlainTV envName):(map PlainTV args)) [] nontsType
edgeType :: (Name, [[Type]]) -> [Type]
edgeType (con, argss) = map (foldl' AppT (ConT con)) argss
mkGrammarPart :: [(Name, [Type])] -> (Name, [(Name, [[Type]])]) -> Q Exp
mkGrammarPart strongEdges (typeName, edges) = do
(UserD _ args cons) <- getUserType typeName
let selfArgsNames = (type2Ref typeName) : (map (var2Ref typeName) args)
strongEdgeNames = concatMap nameEdge edges
nonBGStrongEdgeNames = map getTypeName $ getEdges typeName strongEdges
lamE (map varP (selfArgsNames ++ strongEdgeNames ++ nonBGStrongEdgeNames)) (conProds cons typeName)
where
getTypeName (ConT name) = type2Ref name
getTypeName a@(AppT _ _) = app2Ref (unrollApp a)
getTypeName _ = error "Error, unsupported type."
refEdge :: (Name, [[Type]]) -> [ExpQ]
refEdge (con, argss) = map (varE . nameArgs baseName) argss
where baseName = type2TopRef con
nameEdge :: (Name, [[Type]]) -> [Name]
nameEdge (con, argss) = map (nameArgs baseName) argss
where baseName = type2Ref con
nameArgs :: Name -> [Type] -> Name
nameArgs baseName [] = baseName
nameArgs baseName ((ConT name):types) = nameArgs (mkName $ nameBase baseName ++ "'" ++ nameBase name) types
nameArgs baseName ((VarT name):types) = nameArgs (mkName $ nameBase baseName ++ "'" ++ nameBase name) types
nameArgs _ _ = error "Error, unsupported type."
getNeededInstances :: (Name, [[Type]]) -> [(Name, [Type])]
getNeededInstances (top, argss) = concatMap (\args -> (top,args): map (\arg -> (typeName arg, [])) args) argss
where typeName (ConT n) = n
typeName _ = error "Error, unsupported type."
linkRefs :: Int -> [ExpQ]
linkRefs x = linkRefs' (x1) [[|Zero|]]
where linkRefs' 0 done = reverse done
linkRefs' x' l@(lst:_) = linkRefs' (x' 1) ((appE [|Suc|] lst):l)
linkRefs' _ _ = error "Impossible Error!"
type PrecProd = Map Int [ExpQ]
conProds :: [Con] -> Name -> Q Exp
conProds cs top = do
prods <- foldM (insertCon top) Map.empty cs
prods' <- insertCon' 10 (parensProd top) prods
let prodList = map (\(prec, nonts) ->
tupE [ [|DRef ($(varE $ type2Ref top), prec)|]
, appE [|DPS|] (listE nonts)
]
)
(Map.toList prods')
appE [|DLNontDefs|] $ listE prodList
parensProd :: Name -> Q Exp
parensProd top = [| dTerm "(" .#. (dNont ($(varE $ type2Ref top), 0)) .#. dTerm ")" .#. dEnd parenT |]
getTypeRef :: Name -> Int -> StrictType -> Q Exp
getTypeRef top p (_,t) = [| dNont ($(varE (refTo top t)), p) |]
where refTo top' (VarT n) = var2Ref top' n
refTo _ (ConT n) = type2Ref n
refTo top' app@(AppT _ _) = appOrType2Ref top' $ unrollApp app
refTo _ _ = error "Error, unsupported type."
appOrType2Ref cur app@((ConT con):_) | cur == con = type2Ref cur
| otherwise = app2Ref app
appOrType2Ref _ _ = error "Error, unsupported type."
app2Ref :: [Type] -> Name
app2Ref ((ConT con):args) = nameArgs (type2Ref con) args
app2Ref _ = error "Error, unsupported type."
app2TopRef :: [Type] -> Name
app2TopRef ((ConT con):args) = nameArgs (type2TopRef con) args
app2TopRef _ = error "Error, unsupported type."
type2Ref :: Name -> Name
type2Ref = type2Ref' "_r_"
type2Ref' :: String -> Name -> Name
type2Ref' prefix t = mkName $ prefix ++ nameBase t
var2Ref :: Name -> Name -> Name
var2Ref = var2Ref' "_r_"
var2Ref' :: String -> Name -> Name -> Name
var2Ref' prefix t v = mkName $ prefix ++ nameBase t ++ "_" ++ nameBase v
type2Nonts :: Name -> Name
type2Nonts = type2Ref' "_nonts_"
var2TopRef :: Name -> Name -> Name
var2TopRef = var2Ref' "_t_"
type2TopRef :: Name -> Name
type2TopRef = type2Ref' "_t_"
nameStringE :: Name -> Q Exp
nameStringE = stringE . nameBase
insertCon :: Name -> PrecProd -> Con -> Q PrecProd
insertCon top pp (NormalC name args) = do
insertCon' 10 (foldr1 appE (
[ [| (.#.) $ dTerm $(nameStringE name) |] ] ++
( map (appE [|(.#.)|] . (getTypeRef top 0)) args ) ++
[ [| dEnd $(consExp name (length args)) |] ]
)) pp
insertCon top pp (InfixC argl name argr) = do
(prec, precl, precr) <- getPrec name
let refl = getTypeRef top precl argl
refr = getTypeRef top precr argr
insertCon' prec (infixProd refl (nameBase name) refr (conE name)) pp
insertCon _ _ _ = undefined
infixProd :: ExpQ -> String -> ExpQ -> ExpQ -> Q Exp
infixProd argl term argr op =
[| $argl .#. dTerm term .#. $argr .#.
dEnd (\e1 _ e2 -> $(appsE [op, [|e2|], [|e1|]]))
|]
getPrec :: Name -> Q (Int, Int, Int)
getPrec name = do
(DataConI _ _ _ (Fixity f fd)) <- reify name
return (f, (f + fLeft fd), (f + fRight fd))
where
fLeft InfixL = 0
fLeft InfixR = 1
fLeft _ = error "Error, unsupported fixity."
fRight InfixR = 0
fRight InfixL = 1
fRight _ = error "Error, unsupported fixity."
insertCon' :: Int -> ExpQ -> PrecProd -> Q PrecProd
insertCon' i e pp = return $ Map.insertWith (flip (++)) i [e] pp
consExp :: Name -> Int -> Q Exp
consExp name times = do
let names = map (\x -> mkName $ "arg" ++ show x) [1..times]
lamE (map varP names ++ [wildP]) (appsE $ (conE name):(map varE (reverse names)))