module Text.ProtocolBuffers.ProtoCompile.Gen(protoModule,descriptorModules,enumModule,oneofModule,prettyPrint) where
import Text.ProtocolBuffers.Basic
import Text.ProtocolBuffers.Identifiers
import Text.ProtocolBuffers.Reflections(KeyInfo,HsDefault(..),SomeRealFloat(..),DescriptorInfo(..),ProtoInfo(..),OneofInfo(..),EnumInfo(..),ProtoName(..),ProtoFName(..),FieldInfo(..))
import Text.ProtocolBuffers.ProtoCompile.BreakRecursion(Result(..),VertexKind(..),pKey,pfKey,getKind,Part(..))
import qualified Data.ByteString.Lazy.Char8 as LC(unpack)
import qualified Data.Foldable as F(foldr,toList)
import Data.List(sortBy,foldl',foldl1',group,sort,union)
import Data.Function(on)
import Language.Haskell.Exts.Pretty(prettyPrint)
import Language.Haskell.Exts.Syntax hiding (Int,String)
import Language.Haskell.Exts.Syntax as Hse
import Data.Char(isLower,isUpper)
import qualified Data.Map as M
import Data.Maybe(mapMaybe)
import Data.Sequence (ViewL(..),(><))
import qualified Data.Sequence as Seq(null,length,empty,viewl)
import qualified Data.Set as S
import System.FilePath(joinPath)
ecart :: String -> a -> a
ecart _ x = x
default (Int)
imp :: String -> a
imp s = error ("Impossible? Text.ProtocolBuffers.ProtoCompile.Gen."++s)
nubSort :: Ord a => [a] -> [a]
nubSort = map head . group . sort
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
noWhere :: Maybe Binds
noWhere = Nothing
#else
noWhere :: Binds
noWhere = BDecls []
#endif
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
whereBinds :: Binds -> Maybe Binds
whereBinds = Just
#else
whereBinds :: Binds -> Binds
whereBinds = id
#endif
($$) :: Exp -> Exp -> Exp
($$) = App
infixl 1 $$
src :: SrcLoc
src = SrcLoc "No SrcLoc" 0 0
litStr :: String -> Exp
litStr = Lit . Hse.String
litIntP :: Integral x => x -> Pat
litIntP x | x<0 = PParen $ PLit Signless (Hse.Int (toInteger x))
| otherwise = PLit Signless (Hse.Int (toInteger x))
litIntP' :: Int -> Pat
litIntP' = litIntP
litInt :: Integral x => x -> Exp
litInt x | x<0 = Paren $ Lit (Hse.Int (toInteger x))
| otherwise = Lit (Hse.Int (toInteger x))
litInt' :: Int -> Exp
litInt' = litInt
typeApp :: String -> Type -> Type
typeApp s = TyApp (TyCon (private s))
private :: String -> QName
private t = Qual (ModuleName "P'") (Ident t)
prelude :: String -> QName
prelude t = Qual (ModuleName "Prelude'") (Ident t)
local :: String -> QName
local t = UnQual (Ident t)
localField :: DescriptorInfo -> String -> QName
localField di t = UnQual (fieldIdent di t)
isVar :: String -> Bool
isVar (x:_) = isLower x || x == '_'
isVar _ = False
isCon :: String -> Bool
isCon (x:_) = isUpper x
isCon _ = False
pvar :: String -> Exp
pvar t | isVar t = Var (private t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: pvar expected lower-case first letter in " ++ show t
preludevar :: String -> Exp
preludevar t | isVar t = Var (prelude t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: preludevar expected lower-case first letter in " ++ show t
lvar :: String -> Exp
lvar t | isVar t = Var (local t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: lvar expected lower-case first letter in " ++ show t
pcon :: String -> Exp
pcon t | isCon t = Con (private t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: pcon expected upper-case first letter in " ++ show t
preludecon :: String -> Exp
preludecon t | isCon t = Con (prelude t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: preludecon expected upper-case first letter in " ++ show t
lcon :: String -> Exp
lcon t | isCon t = Con (local t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: lcon expected upper-case first letter in " ++ show t
patvar :: String -> Pat
patvar t | isVar t = PVar (Ident t)
| otherwise = error $ "hprotoc Gen.hs assertion failed: patvar expected lower-case first letter in " ++ show t
match :: String -> [Pat] -> Exp -> Match
match s p r = Match src (Ident s) p Nothing (UnGuardedRhs r) noWhere
inst :: String -> [Pat] -> Exp -> InstDecl
inst s p r = InsDecl $ FunBind [match s p r]
defun :: String -> [Pat] -> Exp -> Decl
defun s p r = FunBind [match s p r]
mkOp :: String -> Exp -> Exp -> Exp
mkOp s a b = InfixApp a (QVarOp (UnQual (Symbol s))) b
compose :: Exp -> Exp -> Exp
compose = mkOp "."
fqMod :: ProtoName -> String
fqMod (ProtoName _ a b c) = joinMod $ a++b++[c]
importPN :: Result -> ModuleName -> Part -> ProtoName -> Maybe ImportDecl
importPN r selfMod@(ModuleName self) part pn =
let o = pKey pn
m1 = ModuleName (joinMod (haskellPrefix pn ++ parentModule pn ++ [baseName pn]))
m2 = ModuleName (joinMod (parentModule pn))
fromSource = S.member (FMName self,part,o) (rIBoot r)
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
iabs = IAbs NoNamespace (Ident (mName (baseName pn)))
#else
iabs = IAbs (Ident (mName (baseName pn)))
#endif
ans = if m1 == selfMod && part /= KeyFile then Nothing
else Just $ ImportDecl src m1 True fromSource False Nothing (Just m2)
(Just (False,[iabs]))
in ecart (unlines . map (\ (a,b) -> a ++ " = "++b) $
[("selfMod",show selfMod)
,("part",show part)
,("pn",show pn)
,("o",show o)
,("m1",show m1)
,("m2",show m2)
,("fromSource",show fromSource)
,("ans",show ans)]) $
ans
importPFN :: Result -> ModuleName -> ProtoFName -> Maybe ImportDecl
importPFN r m@(ModuleName self) pfn =
let o@(FMName _other) = pfKey pfn
m1@(ModuleName m1') = ModuleName (joinMod (haskellPrefix' pfn ++ parentModule' pfn))
m2 = ModuleName (joinMod (parentModule' pfn))
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
spec = Just (False,[IVar (Ident (fName (baseName' pfn)))])
#else
spec = Just (False,[IVar NoNamespace (Ident (fName (baseName' pfn)))])
#endif
kind = getKind r o
fromAlt = S.member (FMName self,FMName m1') (rIKey r)
m1key = if kind == SplitKeyTypeBoot && fromAlt
then keyFile m1
else m1
qualifiedFlag = (m1 /= m)
qualifiedName | qualifiedFlag = if m2/=m1key then Just m2 else Nothing
| otherwise = Nothing
sourceFlag = (kind == KeyTypeBoot) && fromAlt
ans = if not qualifiedFlag && kind /= SplitKeyTypeBoot then Nothing else Just $
ImportDecl src m1key qualifiedFlag sourceFlag False Nothing qualifiedName spec
in ecart (unlines . map (\ (a,b) -> a ++ " = "++b) $
[("m",show m)
,("pfn",show pfn)
,("o",show o)
,("m1",show m1)
,("m2",show m2)
,("kind",show kind)
,("ans",show ans)]) $
ans
importO :: Result -> ModuleName -> Part -> OneofInfo -> Maybe [ImportDecl]
importO r selfMod@(ModuleName self) part oi =
let pn = oneofName oi
o = pKey pn
m1 = ModuleName (joinMod (haskellPrefix pn ++ parentModule pn ++ [baseName pn]))
m2 = ModuleName (joinMod (parentModule pn))
m3 = ModuleName (joinMod (parentModule pn ++ [baseName pn]))
fromSource = S.member (FMName self,part,o) (rIBoot r)
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
iabs1 = IAbs NoNamespace (Ident (mName (baseName pn)))
iabsget = map (IAbs NoNamespace . Ident . fst . oneofGet) . F.toList . oneofFields $ oi
#else
iabs1 = IAbs (Ident (mName (baseName pn)))
iabsget = map (IAbs . Ident . fst . oneofGet) . F.toList . oneofFields $ oi
#endif
ithall = IThingAll (Ident (mName (baseName pn)))
ans1 = ImportDecl src m1 True fromSource False Nothing (Just m2)
(Just (False,[iabs1]))
ans2 = ImportDecl src m1 True fromSource False Nothing (Just m3)
(Just (False,ithall:iabsget))
in if m1 == selfMod && part /= KeyFile
then Nothing
else Just [ans1,ans2]
mergeImports :: [ImportDecl] -> [ImportDecl]
mergeImports importsIn =
let idKey ImportDecl{..} = (importModule,importQualified,importSrc,importAs,fmap fst importSpecs)
mergeImports' ImportDecl{importSpecs=Just(hiding,xs), ..} ImportDecl{importSpecs=Just(_,ys)} =
ImportDecl{importSpecs=Just (hiding, xs `union` ys), ..}
mergeImports' i _ = i
combined = M.fromListWith mergeImports' . map (\ i -> (idKey i,i)) $ importsIn
in M.elems combined
keyFile :: ModuleName -> ModuleName
keyFile (ModuleName s) = ModuleName (s++"'Key")
joinMod :: [MName String] -> String
joinMod [] = ""
joinMod ms = fmName $ foldr1 dotFM . map promoteFM $ ms
baseIdent :: ProtoName -> Name
baseIdent = Ident . mName . baseName
baseIdent' :: ProtoFName -> Name
baseIdent' pfn = Ident $ baseNamePrefix' pfn ++ fName (baseName' pfn)
fieldIdent :: DescriptorInfo -> String -> Name
fieldIdent di str | makeLenses di = Ident ('_':str)
| otherwise = Ident str
qualName :: ProtoName -> QName
qualName p@(ProtoName _ _prefix [] _base) = UnQual (baseIdent p)
qualName p@(ProtoName _ _prefix (parents) _base) = Qual (ModuleName (joinMod parents)) (baseIdent p)
qualFName :: ProtoFName -> QName
qualFName p@(ProtoFName _ _prefix [] _base _basePrefix) = UnQual (baseIdent' p)
qualFName p@(ProtoFName _ _prefix parents _base _basePrefix) = Qual (ModuleName (joinMod parents)) (baseIdent' p)
unqualName :: ProtoName -> QName
unqualName p = UnQual (baseIdent p)
unqualFName :: ProtoFName -> QName
unqualFName p = UnQual (baseIdent' p)
mayQualName :: ProtoName -> ProtoFName -> QName
mayQualName (ProtoName _ c'prefix c'parents c'base) name@(ProtoFName _ prefix parents _base _basePrefix) =
if joinMod (c'prefix++c'parents++[c'base]) == joinMod (prefix++parents)
then UnQual (baseIdent' name)
else qualFName name
oneofCon :: (ProtoName,FieldInfo) -> Exp
oneofCon (name,_) = Con (qualName name)
oneofPat :: (ProtoName,FieldInfo) -> (Pat,Pat)
oneofPat (name,fi) =
let fName@(Ident fname) = baseIdent' (fieldName fi)
in (PApp (qualName name) [PVar fName],PApp (unqualName name) [PVar fName])
oneofRec :: (ProtoName,FieldInfo) -> (Exp,Exp)
oneofRec (_,fi) =
let fName@(Ident fname) = baseIdent' (fieldName fi)
in (litStr fname,lvar fname)
oneofGet :: (ProtoName,FieldInfo) -> (String,ProtoName)
oneofGet (p,fi) =
let Ident fname = baseIdent' (fieldName fi)
unqual = "get'" ++ fname
p' = p { baseName = MName unqual }
in (unqual,p')
modulePragmas :: Bool -> [ModulePragma]
modulePragmas templateHaskell =
[ LanguagePragma src (map Ident $
thPragma ++ ["BangPatterns","DeriveDataTypeable","DeriveGeneric","FlexibleInstances","MultiParamTypeClasses"]
)
, OptionsPragma src (Just GHC) " -fno-warn-unused-imports "
]
where thPragma | templateHaskell = ["TemplateHaskell"]
| otherwise = []
oneofModule :: Result -> OneofInfo -> Module
oneofModule result oi
= Module src (ModuleName (fqMod protoName)) (modulePragmas $ oneofMakeLenses oi) Nothing
Nothing imports (oneofDecls oi)
where protoName = oneofName oi
typs = mapMaybe typeName . F.toList . fmap snd . oneofFields $ oi
imports = (standardImports False False (oneofMakeLenses oi))
++ (mergeImports (mapMaybe (importPN result (ModuleName (fqMod protoName)) Normal) typs))
oneofDecls :: OneofInfo -> [Decl]
oneofDecls oi = (oneofX oi : oneofFuncs oi) ++ lenses ++ instances
where
mkPrisms = Var (Qual (ModuleName "Control.Lens.TH") (Ident "makePrisms"))
lenses | oneofMakeLenses oi = [SpliceDecl src (mkFun $$ TypQuote (unqualName (oneofName oi))) |
mkFun <- [mkLenses, mkPrisms]]
| otherwise = []
instances = [ instanceDefaultOneof oi
, instanceMergeableOneof oi
]
oneofX :: OneofInfo -> Decl
oneofX oi = DataDecl src DataType [] (baseIdent (oneofName oi)) []
(map oneofValueX (F.toList (oneofFields oi) ))
derives
where oneofValueX (pname,fi) = QualConDecl src [] [] con
where con = RecDecl (baseIdent pname) [fieldX]
fieldX = ([baseIdent' . fieldName $ fi], TyParen (TyCon typed ))
typed = case useType (getFieldType (typeCode fi)) of
Just s -> private s
Nothing -> case typeName fi of
Just s -> qualName s
Nothing -> imp $ "No Name for Field!\n" ++ show fi
oneofFuncs :: OneofInfo -> [Decl]
oneofFuncs oi = map mkfuns (F.toList (oneofFields oi))
where mkfuns f = defun (fst (oneofGet f)) [patvar "x"] $
Case (lvar "x")
[ Alt src (snd (oneofPat f))
(UnGuardedRhs (preludecon "Just" $$ snd (oneofRec f))) noWhere
, Alt src PWildCard
(UnGuardedRhs (preludecon "Nothing")) noWhere
]
instanceDefaultOneof :: OneofInfo -> Decl
instanceDefaultOneof oi
= InstDecl src Nothing [] [] (private "Default") [TyCon (unqualName (oneofName oi))]
[ inst "defaultValue" [] firstValue ]
where firstValue :: Exp
firstValue = case Seq.viewl (oneofFields oi) of
EmptyL -> imp ("instanceDefaultOneof: empty in " ++ show oi)
(n,_) :< _ -> case (baseIdent n) of
Ident str -> App (lcon str) (pvar "defaultValue")
Symbol _ -> imp ("instanceDefaultOneof: " ++ show n)
instanceMergeableOneof :: OneofInfo -> Decl
instanceMergeableOneof oi
= InstDecl src Nothing [] [] (private "Mergeable") [TyCon (unqualName (oneofName oi))] []
enumModule :: EnumInfo -> Module
enumModule ei
= let protoName = enumName ei
in Module src (ModuleName (fqMod protoName)) (modulePragmas False) Nothing
(Just [EThingAll (unqualName protoName)])
(standardImports True False False) (enumDecls ei)
enumDecls :: EnumInfo -> [Decl]
enumDecls ei = map ($ ei) [ enumX
, instanceMergeableEnum
, instanceBounded
, instanceDefaultEnum ]
++ declToEnum ei ++
map ($ ei) [ instanceEnum
, instanceWireEnum
, instanceGPB . enumName
, instanceMessageAPI . enumName
, instanceReflectEnum
, instanceTextTypeEnum
]
enumX :: EnumInfo -> Decl
enumX ei = DataDecl src DataType [] (baseIdent (enumName ei)) [] (map enumValueX (enumValues ei)) derivesEnum
where enumValueX (_,name) = QualConDecl src [] [] (ConDecl (Ident name) [])
instanceTextTypeEnum :: EnumInfo -> Decl
instanceTextTypeEnum ei
= InstDecl src Nothing [] [] (private "TextType") [TyCon (unqualName (enumName ei))] [
inst "tellT" [] (pvar "tellShow")
, inst "getT" [] (pvar "getRead")
]
instanceMergeableEnum :: EnumInfo -> Decl
instanceMergeableEnum ei
= InstDecl src Nothing [] [] (private "Mergeable") [TyCon (unqualName (enumName ei))] []
instanceBounded :: EnumInfo -> Decl
instanceBounded ei
= InstDecl src Nothing [] [] (prelude "Bounded") [TyCon (unqualName (enumName ei))]
[set "minBound" (head values),set "maxBound" (last values)]
where values = enumValues ei
set f (_,n) = inst f [] (lcon n)
instanceDefaultEnum :: EnumInfo -> Decl
instanceDefaultEnum ei
= InstDecl src Nothing [] [] (private "Default") [TyCon (unqualName (enumName ei))]
[ inst "defaultValue" [] firstValue ]
where firstValue :: Exp
firstValue = case enumValues ei of
(:) (_,n) _ -> lcon n
[] -> error $ "Impossible? EnumDescriptorProto had empty sequence of EnumValueDescriptorProto.\n" ++ show ei
declToEnum :: EnumInfo -> [Decl]
declToEnum ei = [ TypeSig src [Ident "toMaybe'Enum"]
(TyFun (TyCon (prelude "Int"))
(typeApp "Maybe" (TyCon (unqualName (enumName ei)))))
, FunBind (map toEnum'one values ++ [final]) ]
where values = enumValues ei
toEnum'one (v,n) = match "toMaybe'Enum" [litIntP (getEnumCode v)] (preludecon "Just" $$ lcon n)
final = match "toMaybe'Enum" [PWildCard] (preludecon "Nothing")
instanceEnum :: EnumInfo -> Decl
instanceEnum ei
= InstDecl src Nothing [] [] (prelude "Enum") [TyCon (unqualName (enumName ei))]
(map (InsDecl . FunBind) [fromEnum',toEnum',succ',pred'])
where values = enumValues ei
fromEnum' = map fromEnum'one values
fromEnum'one (v,n) = match "fromEnum" [PApp (local n) []] (litInt (getEnumCode v))
toEnum' = [ match "toEnum" [] (compose mayErr (lvar "toMaybe'Enum")) ]
mayErr = pvar "fromMaybe" $$ (Paren (preludevar "error" $$ (litStr $
"hprotoc generated code: toEnum failure for type "++ fqMod (enumName ei))))
succ' = zipWith (equate "succ") values (tail values) ++
[ match "succ" [PWildCard] (preludevar "error" $$ (litStr $
"hprotoc generated code: succ failure for type "++ fqMod (enumName ei))) ]
pred' = zipWith (equate "pred") (tail values) values ++
[ match "pred" [PWildCard] (preludevar "error" $$ (litStr $
"hprotoc generated code: pred failure for type "++ fqMod (enumName ei))) ]
equate f (_,n1) (_,n2) = match f [PApp (local n1) []] (lcon n2)
instanceWireEnum :: EnumInfo -> Decl
instanceWireEnum ei
= InstDecl src Nothing [] [] (private "Wire") [TyCon (unqualName (enumName ei))]
[ withName "wireSize", withName "wirePut", withGet, withGetErr,withGetPacked,withGetPackedErr ]
where withName foo = inst foo [patvar "ft'",patvar "enum"] rhs
where rhs = pvar foo $$ lvar "ft'" $$
(Paren $ preludevar "fromEnum" $$ lvar "enum")
withGet = inst "wireGet" [litIntP' 14] rhs
where rhs = pvar "wireGetEnum" $$ lvar "toMaybe'Enum"
withGetErr = inst "wireGet" [patvar "ft'"] rhs
where rhs = pvar "wireGetErr" $$ lvar "ft'"
withGetPacked = inst "wireGetPacked" [litIntP' 14] rhs
where rhs = pvar "wireGetPackedEnum" $$ lvar "toMaybe'Enum"
withGetPackedErr = inst "wireGetPacked" [patvar "ft'"] rhs
where rhs = pvar "wireGetErr" $$ lvar "ft'"
instanceGPB :: ProtoName -> Decl
instanceGPB protoName
= InstDecl src Nothing [] [] (private "GPB") [TyCon (unqualName protoName)] []
instanceReflectEnum :: EnumInfo -> Decl
instanceReflectEnum ei
= InstDecl src Nothing [] [] (private "ReflectEnum") [TyCon (unqualName (enumName ei))]
[ inst "reflectEnum" [] ascList
, inst "reflectEnumInfo" [ PWildCard ] ei' ]
where (ProtoName xxx a b c) = enumName ei
xxx'Exp = Paren $ pvar "pack" $$ litStr (LC.unpack (utf8 (fiName xxx)))
values = enumValues ei
ascList,ei',protoNameExp :: Exp
ascList = List (map one values)
where one (v,ns) = Tuple Boxed [litInt (getEnumCode v),litStr ns,lcon ns]
ei' = foldl' App (pcon "EnumInfo") [protoNameExp
,List $ map litStr (enumFilePath ei)
,List (map two values)]
where two (v,ns) = Tuple Boxed [litInt (getEnumCode v),litStr ns]
protoNameExp = Paren $ foldl' App (pvar "makePNF")
[ xxx'Exp, mList a, mList b, litStr (mName c) ]
where mList = List . map (litStr . mName)
hasExt :: DescriptorInfo -> Bool
hasExt di = not (null (extRanges di))
protoModule :: Result -> ProtoInfo -> ByteString -> Module
protoModule result pri fdpBS
= let protoName = protoMod pri
(extendees,myKeys) = unzip $ F.toList (extensionKeys pri)
m = ModuleName (fqMod protoName)
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
exportKeys = map (EVar . unqualFName . fieldName) myKeys
exportNames = map (EVar . UnQual . Ident) ["protoInfo","fileDescriptorProto"]
#else
exportKeys = map (EVar NoNamespace . unqualFName . fieldName) myKeys
exportNames = map (EVar NoNamespace . UnQual . Ident) ["protoInfo","fileDescriptorProto"]
#endif
imports = (protoImports ++) . mergeImports $
mapMaybe (importPN result m Normal) $
extendees ++ mapMaybe typeName myKeys
in Module src m (modulePragmas False) Nothing (Just (exportKeys++exportNames)) imports
(keysXTypeVal protoName (extensionKeys pri) ++ embed'ProtoInfo pri ++ embed'fdpBS fdpBS)
where protoImports = standardImports False (not . Seq.null . extensionKeys $ pri) False ++
[ ImportDecl src (ModuleName "Text.DescriptorProtos.FileDescriptorProto") False False False Nothing Nothing
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
(Just (False,[IAbs NoNamespace (Ident "FileDescriptorProto")]))
#else
(Just (False,[IAbs (Ident "FileDescriptorProto")]))
#endif
, ImportDecl src (ModuleName "Text.ProtocolBuffers.Reflections") False False False Nothing Nothing
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
(Just (False,[IAbs NoNamespace (Ident "ProtoInfo")]))
#else
(Just (False,[IAbs (Ident "ProtoInfo")]))
#endif
, ImportDecl src (ModuleName "Text.ProtocolBuffers.WireMessage") True False False Nothing (Just (ModuleName "P'"))
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
(Just (False,[IVar (Ident "wireGet,getFromBS")]))
#else
(Just (False,[IVar NoNamespace (Ident "wireGet,getFromBS")]))
#endif
]
embed'ProtoInfo :: ProtoInfo -> [Decl]
embed'ProtoInfo pri = [ myType, myValue ]
where myType = TypeSig src [ Ident "protoInfo" ] (TyCon (local "ProtoInfo"))
myValue = PatBind src (PApp (local "protoInfo") []) (UnGuardedRhs $
preludevar "read" $$ litStr (show pri)) noWhere
embed'fdpBS :: ByteString -> [Decl]
embed'fdpBS bs = [ myType, myValue ]
where myType = TypeSig src [ Ident "fileDescriptorProto" ] (TyCon (local "FileDescriptorProto"))
myValue = PatBind src (PApp (local "fileDescriptorProto") []) (UnGuardedRhs $
pvar "getFromBS" $$
Paren (pvar "wireGet" $$ litInt' 11) $$
Paren (pvar "pack" $$ litStr (LC.unpack bs))) noWhere
descriptorModules :: Result -> DescriptorInfo -> [(FilePath,Module)]
descriptorModules result di
= let mainPath = joinPath (descFilePath di)
bootPath = joinPath (descFilePath di) ++ "-boot"
keyfilePath = take (length mainPath 3) mainPath ++ "'Key.hs"
in (mainPath,descriptorNormalModule result di) :
case getKind result (pKey (descName di)) of
TopProtoInfo -> imp $ "descriptorModules was given a TopProtoInfo kinded DescriptorInfo!"
Simple -> []
TypeBoot -> [(bootPath,descriptorBootModule di)]
KeyTypeBoot -> [(bootPath,descriptorKeyBootModule result di)]
SplitKeyTypeBoot -> [(bootPath,descriptorBootModule di)
,(keyfilePath,descriptorKeyfileModule result di)]
descriptorBootModule :: DescriptorInfo -> Module
descriptorBootModule di
= let protoName = descName di
un = unqualName protoName
classes = [prelude "Show",prelude "Eq",prelude "Ord",prelude "Typeable",prelude "Data", prelude "Generic"
,private "Mergeable",private "Default"
,private "Wire",private "GPB",private "ReflectDescriptor"
, private "TextType", private "TextMsg"
]
++ if hasExt di then [private "ExtendMessage"] else []
++ if storeUnknown di then [private "UnknownMessage"] else []
instMesAPI = InstDecl src Nothing [] [] (private "MessageAPI")
[TyVar (Ident "msg'"), TyFun (TyVar (Ident "msg'")) (TyCon un), (TyCon un)] []
dataDecl = DataDecl src DataType [] (baseIdent protoName) [] [] []
mkInst s = InstDecl src Nothing [] [] s [TyCon un] []
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
eabs = EAbs NoNamespace un
#else
eabs = EAbs un
#endif
in Module src (ModuleName (fqMod protoName)) (modulePragmas $ makeLenses di) Nothing (Just [eabs]) minimalImports
(dataDecl : instMesAPI : map mkInst classes)
descriptorKeyBootModule :: Result -> DescriptorInfo -> Module
descriptorKeyBootModule result di
= let Module p1 m@(ModuleName _self) p3 p4 (Just exports) imports decls = descriptorBootModule di
(extendees,myKeys) = unzip $ F.toList (keys di)
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
exportKeys = map (EVar . unqualFName . fieldName) myKeys
#else
exportKeys = map (EVar NoNamespace . unqualFName . fieldName) myKeys
#endif
importTypes = mergeImports . mapMaybe (importPN result m Source) . nubSort $
extendees ++ mapMaybe typeName myKeys
declKeys = keysXType (descName di) (keys di)
in Module p1 m p3 p4 (Just (exports++exportKeys)) (imports++importTypes) (decls++declKeys)
descriptorKeyfileModule :: Result -> DescriptorInfo -> Module
descriptorKeyfileModule result di
= let protoName'Key = (descName di) { baseName = MName . (++"'Key") . mName $ (baseName (descName di)) }
(extendees,myKeys) = unzip $ F.toList (keys di)
mBase = ModuleName (fqMod (descName di))
m = ModuleName (fqMod protoName'Key)
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
exportKeys = map (EVar . unqualFName . fieldName) myKeys
#else
exportKeys = map (EVar NoNamespace . unqualFName . fieldName) myKeys
#endif
importTypes = mergeImports . mapMaybe (importPN result mBase KeyFile) . nubSort $
extendees ++ mapMaybe typeName myKeys
declKeys = keysXTypeVal protoName'Key (keys di)
in Module src m (modulePragmas $ makeLenses di) Nothing (Just exportKeys) (minimalImports++importTypes) declKeys
descriptorNormalModule :: Result -> DescriptorInfo -> Module
descriptorNormalModule result di
= let protoName = descName di
un = unqualName protoName
myKind = getKind result (pKey protoName)
sepKey = myKind == SplitKeyTypeBoot
(extendees,myKeys) = unzip $ F.toList (keys di)
extendees' = if sepKey then [] else extendees
myKeys' = if sepKey then [] else myKeys
m = ModuleName (fqMod protoName)
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
exportKeys = map (EVar . unqualFName . fieldName) myKeys
#else
exportKeys = map (EVar NoNamespace . unqualFName . fieldName) myKeys
#endif
imports = (standardImports False (hasExt di) (makeLenses di) ++) . mergeImports . concat $
[ mapMaybe (importPN result m Normal) $
extendees' ++ mapMaybe typeName (myKeys' ++ (F.toList (fields di)))
, concat . mapMaybe (importO result m Normal) $ F.toList (descOneofs di)
, mapMaybe (importPFN result m) (map fieldName (myKeys ++ F.toList (knownKeys di))) ]
lenses | makeLenses di = [SpliceDecl src (mkLenses $$ TypQuote (unqualName protoName))]
| otherwise = []
declKeys | sepKey = []
| otherwise = keysXTypeVal (descName di) (keys di)
in Module src m (modulePragmas $ makeLenses di) Nothing (Just (EThingAll un : exportLenses di ++ exportKeys)) imports
(descriptorX di : lenses ++ declKeys ++ instancesDescriptor di)
mkLenses :: Exp
mkLenses = Var (Qual (ModuleName "Control.Lens.TH") (Ident "makeLenses"))
exportLenses :: DescriptorInfo -> [ExportSpec]
exportLenses di =
if makeLenses di
then
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
map (EVar . unqualFName . stripPrefix) (lensFieldNames di)
#else
map (EVar NoNamespace . unqualFName . stripPrefix) (lensFieldNames di)
#endif
else []
where stripPrefix pfn = pfn { baseNamePrefix' = "" }
lensFieldNames di = map fieldName (F.toList (fields di))
++ map oneofFName (F.toList (descOneofs di))
minimalImports :: [ImportDecl]
minimalImports =
[ ImportDecl src (ModuleName "Prelude") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "Data.Typeable") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "Data.Data") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "GHC.Generics") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "Text.ProtocolBuffers.Header") True False False Nothing (Just (ModuleName "P'")) Nothing ]
standardImports :: Bool -> Bool -> Bool -> [ImportDecl]
standardImports isEnumMod ext lenses =
[ ImportDecl src (ModuleName "Prelude") False False False Nothing Nothing (Just (False,ops))
, ImportDecl src (ModuleName "Prelude") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "Data.Typeable") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "GHC.Generics") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "Data.Data") True False False Nothing (Just (ModuleName "Prelude'")) Nothing
, ImportDecl src (ModuleName "Text.ProtocolBuffers.Header") True False False Nothing (Just (ModuleName "P'")) Nothing ] ++ lensTH
where
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
ops | ext = map (IVar . Symbol) $ base ++ ["==","<=","&&"]
| otherwise = map (IVar . Symbol) base
#else
ops | ext = map (IVar NoNamespace . Symbol) $ base ++ ["==","<=","&&"]
| otherwise = map (IVar NoNamespace . Symbol) base
#endif
base | isEnumMod = ["+","/","."]
| otherwise = ["+","/"]
lensTH | lenses = [ImportDecl src (ModuleName "Control.Lens.TH") True False False Nothing Nothing Nothing]
| otherwise = []
keysXType :: ProtoName -> Seq KeyInfo -> [Decl]
keysXType self ks = map (makeKeyType self) . F.toList $ ks
keysXTypeVal :: ProtoName -> Seq KeyInfo -> [Decl]
keysXTypeVal self ks = concatMap (\ ki -> [makeKeyType self ki,makeKeyVal self ki]) . F.toList $ ks
makeKeyType :: ProtoName -> KeyInfo -> Decl
makeKeyType self (extendee,f) = keyType
where keyType = TypeSig src [ baseIdent' . fieldName $ f ] (foldl1 TyApp . map TyCon $
[ private "Key", private labeled
, if extendee /= self then qualName extendee else unqualName extendee
, typeQName ])
labeled | isPacked f = "PackedSeq"
| canRepeat f = "Seq"
| otherwise = "Maybe"
typeNumber = getFieldType . typeCode $ f
typeQName :: QName
typeQName = case useType typeNumber of
Just s -> private s
Nothing -> case typeName f of
Just s | self /= s -> qualName s
| otherwise -> unqualName s
Nothing -> error $ "No Name for Field!\n" ++ show f
makeKeyVal :: ProtoName -> KeyInfo -> Decl
makeKeyVal _self (_extendee,f) = keyVal
where typeNumber = getFieldType . typeCode $ f
keyVal = PatBind src (PApp (unqualFName . fieldName $ f) []) (UnGuardedRhs
(pcon "Key" $$ litInt (getFieldId (fieldNumber f))
$$ litInt typeNumber
$$ maybe (preludecon "Nothing")
(Paren . (preludecon "Just" $$) . (defToSyntax (typeCode f)))
(hsDefault f)
)) noWhere
defToSyntax :: FieldType -> HsDefault -> Exp
defToSyntax tc x =
case x of
HsDef'Bool b -> preludecon (show b)
HsDef'ByteString bs -> (if tc == 9 then (\ xx -> Paren (pcon "Utf8" $$ xx)) else id) $
(Paren $ pvar "pack" $$ litStr (LC.unpack bs))
HsDef'RealFloat (SRF'Rational r) | r < 0 -> Paren $ Lit (Frac r)
| otherwise -> Lit (Frac r)
HsDef'RealFloat SRF'nan -> litInt' 0 /! litInt' 0
HsDef'RealFloat SRF'ninf -> litInt' 1 /! litInt' 0
HsDef'RealFloat SRF'inf -> litInt' (1) /! litInt' 0
HsDef'Integer i -> litInt i
HsDef'Enum s -> Paren $ preludevar "read" $$ litStr s
where (/!) a b = Paren (mkOp "/" a b)
descriptorX :: DescriptorInfo -> Decl
descriptorX di = DataDecl src DataType [] name [] [QualConDecl src [] [] con] derives
where self = descName di
name = baseIdent self
con = RecDecl name eFields
where eFields = F.foldr ((:) . fieldX) end (fields di)
end = (if hasExt di then (extfield:) else id)
. (if storeUnknown di then (unknownField:) else id)
$ eOneof
eOneof = F.foldr ((:) . fieldOneofX) [] (descOneofs di)
bangType = if lazyFields di then TyParen else TyBang BangedTy . TyParen
extfield = ([fieldIdent di "ext'field"], bangType (TyCon (private "ExtField")))
unknownField = ([fieldIdent di "unknown'field"], bangType (TyCon (private "UnknownField")))
fieldX fi = ([baseIdent' . fieldName $ fi], bangType (labeled (TyCon typed)))
where labeled | canRepeat fi = typeApp "Seq"
| isRequired fi = id
| otherwise = typeApp "Maybe"
typed :: QName
typed = case useType (getFieldType (typeCode fi)) of
Just s -> private s
Nothing -> case typeName fi of
Just s | self /= s -> qualName s
| otherwise -> unqualName s
Nothing -> error $ "No Name for Field!\n" ++ show fi
fieldOneofX :: OneofInfo -> ([Name],Type)
fieldOneofX oi = ([baseIdent' . oneofFName $ oi], typeApp "Maybe" (TyParen (TyCon typed)))
where typed = qualName (oneofName oi)
instancesDescriptor :: DescriptorInfo -> [Decl]
instancesDescriptor di = map ($ di) $
(if hasExt di then (instanceExtendMessage:) else id) $
(if storeUnknown di then (instanceUnknownMessage:) else id) $
[ instanceMergeable
, instanceDefault
, instanceWireDescriptor
, instanceMessageAPI . descName
, instanceGPB . descName
, instanceReflectDescriptor
, instanceTextType
, instanceTextMsg
]
instanceExtendMessage :: DescriptorInfo -> Decl
instanceExtendMessage di
= InstDecl src Nothing [] [] (private "ExtendMessage") [TyCon (unqualName (descName di))]
[ inst "getExtField" [] (Var (localField di "ext'field"))
, inst "putExtField" [patvar "e'f", patvar "msg"] putextfield
, inst "validExtRanges" [patvar "msg"] (pvar "extRanges" $$ (Paren $ pvar "reflectDescriptorInfo" $$ lvar "msg"))
]
where putextfield = RecUpdate (lvar "msg") [ FieldUpdate (localField di "ext'field") (lvar "e'f") ]
instanceUnknownMessage :: DescriptorInfo -> Decl
instanceUnknownMessage di
= InstDecl src Nothing [] [] (private "UnknownMessage") [TyCon (unqualName (descName di))]
[ inst "getUnknownField" [] (Var (localField di "unknown'field"))
, inst "putUnknownField" [patvar "u'f",patvar "msg"] putunknownfield
]
where putunknownfield = RecUpdate (lvar "msg") [ FieldUpdate (localField di "unknown'field") (lvar "u'f") ]
instanceTextType :: DescriptorInfo -> Decl
instanceTextType di
= InstDecl src Nothing [] [] (private "TextType") [TyCon (unqualName (descName di))] [
inst "tellT" [] (pvar "tellSubMessage")
, inst "getT" [] (pvar "getSubMessage")
]
instanceTextMsg :: DescriptorInfo -> Decl
instanceTextMsg di
= InstDecl src Nothing [] [] (private "TextMsg") [TyCon (unqualName (descName di))] [
inst "textPut" [patvar msgVar] genPrint
, InsDecl $ FunBind [Match src (Ident "textGet") [] Nothing (UnGuardedRhs parser) bdecls]
]
where
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
bdecls = Just (BDecls (subparsers ++ subparsersO))
#else
bdecls = BDecls (subparsers ++ subparsersO)
#endif
flds = F.toList (fields di)
os = F.toList (descOneofs di)
msgVar = distinctVar "msg"
distinctVar var = if var `elem` reservedVars then distinctVar (var ++ "'") else var
reservedVars = map toPrintName flds
genPrintFields = map (Qualifier . printField msgVar) flds
genPrintOneofs = map (Qualifier . printOneof msgVar) os
genPrint = if null flds && null os
then preludevar "return" $$ Hse.Tuple Boxed []
else Do $ genPrintFields ++ genPrintOneofs
parser
| null flds && null os = preludevar "return" $$ pvar "defaultValue"
| otherwise = Do [
Generator src (patvar "mods")
$ pvar "sepEndBy"
$$ Paren (pvar "choice" $$ List (map (lvar . parserName) flds ++ map (lvar . parserNameO) os))
$$ pvar "spaces",
Qualifier $ (preludevar "return")
$$ Paren (preludevar "foldl"
$$ Lambda src [patvar "v", patvar "f"] (lvar "f" $$ lvar "v")
$$ pvar "defaultValue"
$$ lvar "mods")
]
parserName f = let Ident fname = baseIdent' (fieldName f) in "parse'" ++ fname
parserNameO o = let Ident oname = baseIdent' (oneofFName o) in "parse'" ++ oname
subparsers = map (\f -> defun (parserName f) [] (getField f)) flds
getField fi = let printname = toPrintName fi
Ident funcname = baseIdent' (fieldName fi)
update = if canRepeat fi then pvar "append" $$ Paren (lvar funcname $$ lvar "o") $$ lvar "v" else lvar "v"
in pvar "try" $$ Do [
Generator src (patvar "v") $ pvar "getT" $$ litStr printname,
Qualifier $ (preludevar "return")
$$ Paren (Lambda src [patvar "o"]
(RecUpdate (lvar "o") [ FieldUpdate (local funcname) update]))
]
subparsersO = map funbind os
funbind o = FunBind [Match src (Ident (parserNameO o)) [] Nothing (UnGuardedRhs (getOneof)) whereParse]
where getOneof = pvar "try" $$
(pvar "choice" $$ List (map (Var . UnQual . Ident) parsefs))
oflds = F.toList (oneofFields o)
flds = map snd oflds
parsefs = map parserName flds
whereParse = whereBinds $ BDecls (map decl oflds)
where decl (n,f) = defun (parserName f) [] (getOneofField (n,f))
getOneofField p@(n,f) =
let Ident oname = baseIdent' (oneofFName o)
printname = toPrintName f
update = preludecon "Just" $$ Paren (oneofCon p $$ lvar "v")
in pvar "try" $$ Do [
Generator src (patvar "v") $ pvar "getT" $$ litStr printname,
Qualifier $ (preludevar "return")
$$ Paren (Lambda src [patvar "s"]
(RecUpdate (lvar "s") [ FieldUpdate (local oname) update]))
]
printField :: String -> FieldInfo -> Exp
printField msgVar fi
= let Ident funcname = baseIdent' (fieldName fi)
printname = toPrintName fi
in pvar "tellT" $$ litStr printname $$ Paren (lvar funcname $$ lvar msgVar)
toPrintName :: FieldInfo -> String
toPrintName fi = let IName uname = last $ splitFI $ protobufName' (fieldName fi) in uToString uname
printOneof :: String -> OneofInfo -> Exp
printOneof msgVar oi
= Case (Paren (lvar funcname $$ lvar msgVar)) (map caseAlt flds ++ [caseAltNothing])
where Ident funcname = baseIdent' (oneofFName oi)
IName uname = last $ splitFI $ protobufName' (oneofFName oi)
printname = uToString uname
flds = F.toList (oneofFields oi)
caseAlt :: (ProtoName,FieldInfo) -> Alt
caseAlt f = Alt src patt (UnGuardedRhs rhs) noWhere
where patt = PApp (prelude "Just") [fst (oneofPat f)]
(rstr,rvar) = oneofRec f
rhs = pvar "tellT" $$ rstr $$ rvar
caseAltNothing :: Alt
caseAltNothing = Alt src (PApp (prelude "Nothing") []) (UnGuardedRhs rhs) noWhere
where rhs = preludevar "return" $$ unit_con
instanceMergeable :: DescriptorInfo -> Decl
instanceMergeable di
= InstDecl src Nothing [] [] (private "Mergeable") [TyCon un]
[
inst "mergeAppend" [PApp un patternVars1, PApp un patternVars2]
(foldl' App (Con un) (zipWith append vars1 vars2))
]
where un = unqualName (descName di)
len = (if hasExt di then succ else id)
$ (if storeUnknown di then succ else id)
$ Seq.length (fields di) + Seq.length (descOneofs di)
patternVars1,patternVars2 :: [Pat]
patternVars1 = take len inf
where inf = map (\ n -> patvar ("x'" ++ show n)) [(1::Int)..]
patternVars2 = take len inf
where inf = map (\ n -> patvar ("y'" ++ show n)) [(1::Int)..]
vars1,vars2 :: [Exp]
vars1 = take len inf
where inf = map (\ n -> lvar ("x'" ++ show n)) [(1::Int)..]
vars2 = take len inf
where inf = map (\ n -> lvar ("y'" ++ show n)) [(1::Int)..]
append x y = Paren $ pvar "mergeAppend" $$ x $$ y
instanceDefault :: DescriptorInfo -> Decl
instanceDefault di
= InstDecl src Nothing [] [] (private "Default") [TyCon un]
[ inst "defaultValue" [] (foldl' App (Con un) deflistExt) ]
where un = unqualName (descName di)
deflistExt = F.foldr ((:) . defX) end (fields di)
end = (if hasExt di then (pvar "defaultValue":) else id)
. (if storeUnknown di then (pvar "defaultValue":) else id)
$ F.foldr ((:) . defOneof) [] (descOneofs di)
defX :: FieldInfo -> Exp
defX fi | isRequired fi = dv1
| otherwise = dv2
where dv1 = case hsDefault fi of
Nothing -> pvar "defaultValue"
Just hsdef -> defToSyntax (typeCode fi) hsdef
dv2 = case hsDefault fi of
Nothing -> pvar "defaultValue"
Just hsdef -> Paren $ preludecon "Just" $$ defToSyntax (typeCode fi) hsdef
defOneof :: OneofInfo -> Exp
defOneof oi= pvar "defaultValue"
instanceMessageAPI :: ProtoName -> Decl
instanceMessageAPI protoName
= InstDecl src Nothing [] [] (private "MessageAPI")
[TyVar (Ident "msg'"), TyFun (TyVar (Ident "msg'")) (TyCon un), (TyCon un)]
[ inst "getVal" [patvar "m'",patvar "f'"] (App (lvar "f'" ) (lvar "m'")) ]
where un = unqualName protoName
instanceWireDescriptor :: DescriptorInfo -> Decl
instanceWireDescriptor di@(DescriptorInfo { descName = protoName
, fields = fieldInfos
, descOneofs = oneofInfos
, extRanges = allowedExts
, knownKeys = fieldExts })
= let me = unqualName protoName
extensible = not (null allowedExts)
len = (if extensible then succ else id)
$ (if storeUnknown di then succ else id)
$ Seq.length fieldInfos + Seq.length oneofInfos
mine = PApp me . take len . map (\ n -> patvar ("x'" ++ show n)) $ [(1::Int)..]
vars = take len . map (\ n -> lvar ("x'" ++ show n)) $ [(1::Int)..]
mExt | extensible = Just (vars !! Seq.length fieldInfos)
| otherwise = Nothing
mUnknown | storeUnknown di = Just (last vars)
| otherwise = Nothing
cases g m e = Case (lvar "ft'") [ Alt src (litIntP' 10) (UnGuardedRhs g) noWhere
, Alt src (litIntP' 11) (UnGuardedRhs m) noWhere
, Alt src PWildCard (UnGuardedRhs e) noWhere
]
sizeCases = UnGuardedRhs $ cases (lvar "calc'Size")
(pvar "prependMessageSize" $$ lvar "calc'Size")
(pvar "wireSizeErr" $$ lvar "ft'" $$ lvar "self'")
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
whereCalcSize = Just (BDecls [defun "calc'Size" [] sizes])
#else
whereCalcSize = BDecls [defun "calc'Size" [] sizes]
#endif
sizes | null sizesList = Lit (Hse.Int 0)
| otherwise = Paren (foldl1' (+!) sizesList)
where (+!) = mkOp "+"
sizesList | Just v <- mUnknown = sizesListExt ++ [ pvar "wireSizeUnknownField" $$ v ]
| otherwise = sizesListExt
sizesListExt | Just v <- mExt = sizesListFields ++ [ pvar "wireSizeExtField" $$ v ]
| otherwise = sizesListFields
sizesListFields = concat . zipWith toSize vars . F.toList $
fmap Left fieldInfos >< fmap Right oneofInfos
toSize var (Left fi)
= let f = if isPacked fi then "wireSizePacked"
else if isRequired fi then "wireSizeReq"
else if canRepeat fi then "wireSizeRep"
else "wireSizeOpt"
in [foldl' App (pvar f) [ litInt (wireTagLength fi)
, litInt (getFieldType (typeCode fi))
, var]]
toSize var (Right oi) = map (toSize' var) . F.toList . oneofFields $ oi
where toSize' var r@(n,fi)
= let f = "wireSizeOpt"
var' = mkOp "Prelude'.=<<" (Var (qualName (snd (oneofGet r)))) var
in foldl' App (pvar f) [ litInt (wireTagLength fi)
, litInt (getFieldType (typeCode fi))
, var']
putCases = UnGuardedRhs $ cases
(lvar "put'Fields")
(Do [ Qualifier $ pvar "putSize" $$
(Paren $ foldl' App (pvar "wireSize") [ litInt' 10 , lvar "self'" ])
, Qualifier $ lvar "put'Fields" ])
(pvar "wirePutErr" $$ lvar "ft'" $$ lvar "self'")
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
wherePutFields = Just (BDecls [defun "put'Fields" [] (Do putStmts)])
#else
wherePutFields = BDecls [defun "put'Fields" [] (Do putStmts)]
#endif
putStmts = putStmtsContent
where putStmtsContent | null putStmtsAll = [Qualifier $ preludevar "return" $$ Con (Special UnitCon)]
| otherwise = putStmtsAll
putStmtsAll | Just v <- mUnknown = putStmtsListExt ++ [ Qualifier $ pvar "wirePutUnknownField" $$ v ]
| otherwise = putStmtsListExt
putStmtsListExt | Just v <- mExt = sortedPutStmtsList ++ [ Qualifier $ pvar "wirePutExtField" $$ v ]
| otherwise = sortedPutStmtsList
sortedPutStmtsList = map snd
. sortBy (compare `on` fst)
$ putStmtsList
putStmtsList = concat . zipWith toPut vars . F.toList $
fmap Left fieldInfos >< fmap Right oneofInfos
toPut var (Left fi)
= let f = if isPacked fi then "wirePutPacked"
else if isRequired fi then "wirePutReq"
else if canRepeat fi then "wirePutRep"
else "wirePutOpt"
in [(fieldNumber fi,
Qualifier $
foldl' App (pvar f) [ litInt (getWireTag (wireTag fi))
, litInt (getFieldType (typeCode fi))
, var]
)]
toPut var (Right oi) = map (toPut' var) . F.toList . oneofFields $ oi
where toPut' var r@(n,fi)
= let f = "wirePutOpt"
var' = mkOp "Prelude'.=<<" (Var (qualName (snd (oneofGet r)))) var
in (fieldNumber fi
,Qualifier $
foldl' App (pvar f) [ litInt (getWireTag (wireTag fi))
, litInt (getFieldType (typeCode fi))
, var']
)
getCases = let param = if storeUnknown di
then Paren (pvar "catch'Unknown" $$ lvar "update'Self")
else lvar "update'Self"
in UnGuardedRhs $ cases (pvar "getBareMessageWith" $$ param)
(pvar "getMessageWith" $$ param)
(pvar "wireGetErr" $$ lvar "ft'")
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
whereDecls = Just (BDecls [whereUpdateSelf])
#else
whereDecls = BDecls [whereUpdateSelf]
#endif
whereUpdateSelf = defun "update'Self" [patvar "wire'Tag", patvar "old'Self"]
(Case (lvar "wire'Tag") updateAlts)
updateAlts = concatMap toUpdate (F.toList fieldInfos)
++ (do
o <- F.toList oneofInfos
f <- F.toList (oneofFields o)
toUpdateO o f)
++ (if extensible then concatMap toUpdateExt (F.toList fieldExts) else [])
++ [Alt src PWildCard (UnGuardedRhs wildcardAlt) noWhere]
wildcardAlt = letPair extBranch
where letPair = Let (BDecls [PatBind src (PTuple Boxed [patvar "field'Number",patvar "wire'Type"])
(UnGuardedRhs (pvar "splitWireTag" $$ lvar "wire'Tag")) bdecls])
extBranch | extensible = If (isAllowedExt (lvar "field'Number"))
(argPair (pvar "loadExtension"))
unknownBranch
| otherwise = unknownBranch
unknownBranch = argPair (pvar "unknown")
argPair x = x $$ lvar "field'Number" $$ lvar "wire'Type" $$ lvar "old'Self"
#if MIN_VERSION_haskell_src_exts(1, 17, 0)
bdecls = Nothing
#else
bdecls = BDecls []
#endif
isAllowedExt x = preludevar "or" $$ List ranges where
(<=!) = mkOp "<="; (&&!) = mkOp "&&"; (==!) = mkOp "=="; (FieldId maxHi) = maxBound
ranges = map (\ (FieldId lo,FieldId hi) ->
if hi < maxHi
then if lo == hi
then (x ==! litInt lo)
else (litInt lo <=! x) &&! (x <=! litInt hi)
else litInt lo <=! x )
allowedExts
toUpdateExt fi | Just (wt1,wt2) <- packedTag fi = [toUpdateExtUnpacked wt1, toUpdateExtPacked wt2]
| otherwise = [toUpdateExtUnpacked (wireTag fi)]
where (getUnP,getP) | isPacked fi = (pvar "wireGetKeyToPacked",pvar "wireGetKey")
| otherwise = (pvar "wireGetKey",pvar "wireGetKeyToUnPacked")
toUpdateExtUnpacked wt1 =
Alt src (litIntP . getWireTag $ wt1)
(UnGuardedRhs $ getUnP $$ Var (mayQualName protoName (fieldName fi)) $$ lvar "old'Self")
noWhere
toUpdateExtPacked wt2 =
Alt src (litIntP . getWireTag $ wt2)
(UnGuardedRhs $ getP $$ Var (mayQualName protoName (fieldName fi)) $$ lvar "old'Self")
noWhere
toUpdate fi | Just (wt1,wt2) <- packedTag fi = [toUpdateUnpacked wt1 fi, toUpdatePacked wt2 fi]
| otherwise = [toUpdateUnpacked (wireTag fi) fi]
toUpdateUnpacked wt1 fi =
Alt src (litIntP . getWireTag $ wt1) (UnGuardedRhs $
preludevar "fmap" $$ (Paren $ Lambda src [PBangPat (patvar "new'Field")] $
RecUpdate (lvar "old'Self")
[FieldUpdate (unqualFName . fieldName $ fi)
(labelUpdateUnpacked fi)])
$$ (Paren (pvar "wireGet" $$ (litInt . getFieldType . typeCode $ fi)))) noWhere
labelUpdateUnpacked fi | canRepeat fi = pvar "append" $$ Paren ((Var . unqualFName . fieldName $ fi)
$$ lvar "old'Self")
$$ lvar "new'Field"
| isRequired fi = qMerge (lvar "new'Field")
| otherwise = qMerge (preludecon "Just" $$ lvar "new'Field")
where qMerge x | fromIntegral (getFieldType (typeCode fi)) `elem` [10,(11::Int)] =
pvar "mergeAppend" $$ Paren ( (Var . unqualFName . fieldName $ fi)
$$ lvar "old'Self" )
$$ Paren x
| otherwise = x
toUpdatePacked wt2 fi =
Alt src (litIntP . getWireTag $ wt2) (UnGuardedRhs $
preludevar "fmap" $$ (Paren $ Lambda src [PBangPat (patvar "new'Field")] $
RecUpdate (lvar "old'Self")
[FieldUpdate (unqualFName . fieldName $ fi)
(labelUpdatePacked fi)])
$$ (Paren (pvar "wireGetPacked" $$ (litInt . getFieldType . typeCode $ fi)))) noWhere
labelUpdatePacked fi = pvar "mergeAppend" $$ Paren ((Var . unqualFName . fieldName $ fi)
$$ lvar "old'Self")
$$ lvar "new'Field"
toUpdateO oi f@(_n,fi)
| Just (wt1,wt2) <- packedTag fi = [toUpdateUnpackedO oi wt1 f, toUpdatePackedO oi wt2 f]
| otherwise = [toUpdateUnpackedO oi (wireTag fi) f]
toUpdateUnpackedO oi wt1 f@(_,fi) =
Alt src (litIntP . getWireTag $ wt1) (UnGuardedRhs $
preludevar "fmap" $$ (Paren $ Lambda src [PBangPat (patvar "new'Field")] $
RecUpdate (lvar "old'Self")
[FieldUpdate (unqualFName . oneofFName $ oi)
(labelUpdateUnpackedO oi f)])
$$ (Paren (pvar "wireGet" $$ (litInt . getFieldType . typeCode $ fi)))) noWhere
labelUpdateUnpackedO oi f@(_,fi) = qMerge (preludecon "Just" $$
(oneofCon f $$ lvar "new'Field")
)
where qMerge x | fromIntegral (getFieldType (typeCode fi)) `elem` [10,(11::Int)] =
pvar "mergeAppend" $$ Paren ( (Var . unqualFName . oneofFName $ oi)
$$ lvar "old'Self" )
$$ Paren x
| otherwise = x
toUpdatePackedO oi wt2 f@(_,fi) =
Alt src (litIntP . getWireTag $ wt2) (UnGuardedRhs $
preludevar "fmap" $$ (Paren $ Lambda src [PBangPat (patvar "new'Field")] $
RecUpdate (lvar "old'Self")
[FieldUpdate (unqualFName . oneofFName $ oi)
(labelUpdatePackedO oi f)])
$$ (Paren (pvar "wireGetPacked" $$ (litInt . getFieldType . typeCode $ fi)))) noWhere
labelUpdatePackedO oi f@(_,fi) = pvar "mergeAppend" $$ Paren ((Var . unqualFName . oneofFName $ oi)
$$ lvar "old'Self")
$$ Paren (preludecon "Just" $$
(oneofCon f $$ lvar "new'Field"))
in InstDecl src Nothing [] [] (private "Wire") [TyCon me] . map InsDecl $
[ FunBind [Match src (Ident "wireSize") [patvar "ft'",PAsPat (Ident "self'") (PParen mine)] Nothing sizeCases whereCalcSize]
, FunBind [Match src (Ident "wirePut") [patvar "ft'",PAsPat (Ident "self'") (PParen mine)] Nothing putCases wherePutFields]
, FunBind [Match src (Ident "wireGet") [patvar "ft'"] Nothing getCases whereDecls]
]
instanceReflectDescriptor :: DescriptorInfo -> Decl
instanceReflectDescriptor di
= InstDecl src Nothing [] [] (private "ReflectDescriptor") [TyCon (unqualName (descName di))]
[ inst "getMessageInfo" [PWildCard] gmi
, inst "reflectDescriptorInfo" [ PWildCard ] rdi ]
where
rdi :: Exp
rdi = preludevar "read" $$ litStr (show di)
gmi,reqId,allId :: Exp
gmi = pcon "GetMessageInfo" $$ Paren reqId $$ Paren allId
reqId = pvar "fromDistinctAscList" $$
List (map litInt . sort . concat $ [ allowedList fi | fi <- F.toList (fields di), isRequired fi])
allId = pvar "fromDistinctAscList" $$
List (map litInt . sort . concat $ [ allowedList fi | fi <- F.toList (fields di)] ++
[ allowedList fi | fi <- F.toList (knownKeys di)])
allowedList fi | Just (wt1,wt2) <- packedTag fi = [getWireTag wt1,getWireTag wt2]
| otherwise = [getWireTag (wireTag fi)]
derives,derivesEnum :: [Deriving]
derives = map (\ x -> (prelude x,[])) ["Show","Eq","Ord","Typeable","Data","Generic"]
derivesEnum = map (\ x -> (prelude x,[])) ["Read","Show","Eq","Ord","Typeable","Data","Generic"]
useType :: Int -> Maybe String
useType 1 = Just "Double"
useType 2 = Just "Float"
useType 3 = Just "Int64"
useType 4 = Just "Word64"
useType 5 = Just "Int32"
useType 6 = Just "Word64"
useType 7 = Just "Word32"
useType 8 = Just "Bool"
useType 9 = Just "Utf8"
useType 10 = Nothing
useType 11 = Nothing
useType 12 = Just "ByteString"
useType 13 = Just "Word32"
useType 14 = Nothing
useType 15 = Just "Int32"
useType 16 = Just "Int64"
useType 17 = Just "Int32"
useType 18 = Just "Int64"
useType x = imp $ "useType: Unknown type code (expected 1 to 18) of "++show x