ptera-th-0.3.0.0: src/Language/Parser/Ptera/TH/Util.hs
{-# LANGUAGE TemplateHaskell #-}
module Language.Parser.Ptera.TH.Util (
genGrammarToken,
GenRulesTypes (..),
genRules,
genParsePoints,
module Language.Parser.Ptera.Data.HEnum,
unsafeMembership,
) where
import Language.Parser.Ptera.Prelude
import qualified Language.Haskell.TH as TH
import qualified Language.Haskell.TH.Syntax as TH
import Language.Parser.Ptera.Data.HEnum (HEnum (..))
import Language.Parser.Ptera.TH.Syntax
import Prelude (String)
import qualified Type.Membership as Membership
import qualified Unsafe.Coerce as Unsafe
genGrammarToken :: TH.Name -> TH.Q TH.Type -> [(String, TH.Q TH.Pat)] -> TH.Q [TH.Dec]
genGrammarToken tyName tokenTy tokens = do
grammarTokenInstD <- grammarTokenInstDQ
tokensTagInstD <- tokensTagInstDQ
tokensMemberInstDs <- tokensMemberInstDsQ
pure do [tokensTyD, grammarTokenInstD, tokensTagInstD] ++ tokensMemberInstDs
where
tokensTyD = TH.DataD [] tyName [] Nothing [] []
grammarTokenInstDQ = TH.InstanceD Nothing []
<$> [t|GrammarToken $(pure do TH.ConT tyName) $(tokenTy)|]
<*> sequence
[ TH.FunD
do TH.mkName "tokenToTerminal"
<$> sequence [tokenToTerminalClause]
]
tokensTagInstDQ = TH.TySynInstD
<$> do
TH.TySynEqn Nothing
<$> [t|TokensTag $(tokensTy)|]
<*> foldr
do \(tokenName, _) ty -> do
let tokenLitTy = TH.LitT do TH.StrTyLit tokenName
[t|$(pure tokenLitTy) ': $(ty)|]
do [t|'[]|]
do tokens
tokensMemberInstDsQ = buildTokensMemberInstDsQ
do 0 :: Int
do []
do tokens
tokenToTerminalClause = do
paramTokenName <- TH.newName "token"
TH.Clause
<$> sequence [[p|Proxy|], pure do TH.VarP paramTokenName]
<*> do TH.NormalB <$> TH.CaseE
do TH.VarE paramTokenName
<$> buildTokenToTerminalMatchesQ
do 0 :: Int
do []
do tokens
<*> pure []
buildTokensMemberInstDsQ n ds = \case
[] ->
pure ds
(tokenName, _):ts -> do
let tokenLitTy = TH.LitT do TH.StrTyLit tokenName
tokenDs <- [d|
instance TokensMember $(tokensTy) $(pure tokenLitTy) where
tokensMembership _ = unsafeMembership $(TH.lift n)
|]
buildTokensMemberInstDsQ
do n + 1
do tokenDs ++ ds
do ts
buildTokenToTerminalMatchesQ n ms = \case
[] ->
pure ms
(_, tokenPat):ts -> do
m <- TH.Match
<$> tokenPat
<*> do TH.NormalB <$>
[e|UnsafeHEnum $(TH.lift n)|]
<*> pure []
buildTokenToTerminalMatchesQ
do n + 1
do m:ms
do ts
tokensTy = pure do TH.ConT tyName
data GenRulesTypes = GenRulesTypes
{ genRulesCtxTy :: TH.Q TH.Type
, genRulesTokensTy :: TH.Q TH.Type
, genRulesTokenTy :: TH.Q TH.Type
}
genRules :: TH.Name -> GenRulesTypes -> [(TH.Name, String, TH.Q TH.Type)] -> TH.Q [TH.Dec]
genRules rulesTyName genRulesTypes ruleDefs
= (:) <$> rulesTyD <*>
do (:) <$> rulesTagInstD <*>
do (:) <$> ruleExprTypeTyD <*>
do (++) <$> rulesInstD <*> hasFieldDs
where
rulesTyD = TH.DataD [] rulesTyName [] Nothing
<$> sequence
[ TH.RecC rulesTyName
<$> buildRuleFields [] ruleDefs
]
<*> pure []
rulesTagInstD = TH.TySynInstD
<$> do
TH.TySynEqn Nothing
<$> [t|RulesTag $(rulesTy)|]
<*> buildNonTerminalSymList [t|'[]|] ruleDefs
rulesInstD =
[d|
instance Rules $(rulesTy) where
generateRules =
$(foldl'
do \l _ -> [|HFCons DictF $(l)|]
do [|HFNil|]
ruleDefs
)
|]
ruleExprTypeTyD = TH.TySynInstD
<$> do
TH.TySynEqn Nothing
<$> [t|RuleExprType $(rulesTy)|]
<*> ruleExprTy
hasFieldDs = buildHasFieldInstances [] ruleDefs
buildRuleFields acc = \case
[] ->
pure acc
(fieldName, _, ty):rs -> do
fieldTy <- [t|$(ruleExprTy) $(ty)|]
buildRuleFields
do (fieldName, fieldBang, fieldTy):acc
do rs
buildNonTerminalSymList acc = \case
[] ->
acc
(_, name, _):rs -> do
let nameTy = TH.LitT do TH.StrTyLit name
buildNonTerminalSymList
[t|$(pure nameTy) ': $(acc)|] rs
buildHasFieldInstances acc = \case
[] ->
pure acc
(fieldName, name, ty):rs -> do
let nameTy = pure do TH.LitT do TH.StrTyLit name
insts <-
[d|
instance HasRuleExprField $(rulesTy) $(nameTy) where
type RuleExprReturnType $(rulesTy) $(nameTy) = $(ty)
getExprField x _ = $(pure do TH.VarE fieldName) x
|]
buildHasFieldInstances
do insts ++ acc
do rs
fieldBang = TH.Bang
TH.NoSourceUnpackedness
TH.SourceStrict
rulesTy = pure do TH.ConT rulesTyName
ruleExprTy =
[t|RuleExprM
$(genRulesCtxTy genRulesTypes)
$(rulesTy)
$(genRulesTokensTy genRulesTypes)
$(genRulesTokenTy genRulesTypes)
|]
genParsePoints :: TH.Name -> TH.Name -> [String] -> TH.Q [TH.Dec]
genParsePoints tyName rulesTyName initials = (:) <$> parsePointsTyD <*> memberInitialsInstD where
parsePointsTyD = TH.TySynD tyName [] <$> buildParsePointsSymList initials
memberInitialsInstD =
[d|
instance MemberInitials $(rulesTy) $(parsePointsTy) where
memberInitials =
$(foldl'
do \l _ -> [|HFCons DictF $(l)|]
do [|HFNil|]
do initials
)
|]
buildParsePointsSymList = \case
[] ->
[t|'[]|]
n:ns -> do
let nameTy = TH.LitT do TH.StrTyLit n
[t|$(pure nameTy) ': $(buildParsePointsSymList ns)|]
parsePointsTy = pure do TH.ConT tyName
rulesTy = pure do TH.ConT rulesTyName
unsafeMembership :: Int -> Membership.Membership xs x
unsafeMembership = Unsafe.unsafeCoerce