Types.hs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. module Gidl.Backend.Haskell.Types where
  2. import Data.Monoid
  3. import Data.List (intercalate, nub)
  4. import Data.Char (toUpper)
  5. import Gidl.Types
  6. import Ivory.Artifact
  7. import Text.PrettyPrint.Mainland
  8. -- invariant: only make a typeModule from a StructType, NewtypeType, or EnumType
  9. -- i.e. when isUserDefined is true.
  10. typeModule :: Bool -> [String] -> Type -> Artifact
  11. typeModule useAeson modulepath t =
  12. artifactPath (intercalate "/" modulepath) $
  13. artifactText ((typeModuleName t) ++ ".hs") $
  14. prettyLazyText 80 $
  15. stack $
  16. [ text "{-# LANGUAGE RecordWildCards #-}"
  17. , text "{-# LANGUAGE DeriveDataTypeable #-}"
  18. , text "{-# LANGUAGE DeriveGeneric #-}"
  19. , empty
  20. , text "module"
  21. <+> tm (typeModuleName t)
  22. <+> text "where"
  23. , empty
  24. , stack (imports ++
  25. [ text "import Data.Aeson (ToJSON,FromJSON)" | useAeson ] ++
  26. [ text "import Data.Serialize"
  27. , text "import Data.Typeable"
  28. , text "import Data.Data"
  29. , text "import GHC.Generics (Generic)"
  30. , text "import qualified Test.QuickCheck as Q"
  31. ])
  32. , empty
  33. , typeDecl t
  34. ] ++
  35. [ toJSONInstance (typeModuleName t) | useAeson ] ++
  36. [ fromJSONInstance (typeModuleName t) | useAeson ]
  37. where
  38. imports = map (importDecl tm)
  39. $ nub
  40. $ map importType
  41. $ typeLeaves t
  42. tm mname = mconcat $ punctuate dot
  43. $ map text (modulepath ++ [mname])
  44. --typename = typeModuleName t
  45. typeHaskellType :: Type -> String
  46. typeHaskellType (StructType tn _) = userTypeModuleName tn
  47. typeHaskellType (PrimType (Newtype tn _)) = userTypeModuleName tn
  48. typeHaskellType (PrimType (EnumType tn _ _)) = userTypeModuleName tn
  49. typeHaskellType (PrimType (AtomType a)) = case a of
  50. AtomInt Bits8 -> "Int8"
  51. AtomInt Bits16 -> "Int16"
  52. AtomInt Bits32 -> "Int32"
  53. AtomInt Bits64 -> "Int64"
  54. AtomWord Bits8 -> "Word8"
  55. AtomWord Bits16 -> "Word16"
  56. AtomWord Bits32 -> "Word32"
  57. AtomWord Bits64 -> "Word64"
  58. AtomFloat -> "Float"
  59. AtomDouble -> "Double"
  60. typeHaskellType (PrimType VoidType) = "()"
  61. typeModuleName :: Type -> String
  62. typeModuleName (StructType tn _) = userTypeModuleName tn
  63. typeModuleName (PrimType (Newtype tn _)) = userTypeModuleName tn
  64. typeModuleName (PrimType (EnumType tn _ _)) = userTypeModuleName tn
  65. typeModuleName (PrimType (AtomType _)) = error "do not take typeModuleName of an AtomType"
  66. typeModuleName (PrimType VoidType) = error "do not take typeModuleName of a VoidType"
  67. userTypeModuleName :: String -> String
  68. userTypeModuleName = first_cap . u_to_camel
  69. where
  70. first_cap (s:ss) = (toUpper s) : ss
  71. first_cap [] = []
  72. u_to_camel ('_':'t':[]) = []
  73. u_to_camel ('_':[]) = []
  74. u_to_camel ('_':a:as) = (toUpper a) : u_to_camel as
  75. u_to_camel (a:as) = a : u_to_camel as
  76. u_to_camel [] = []
  77. serializeInstance :: TypeName -> Doc
  78. serializeInstance tname = stack
  79. [ text "instance Serialize" <+> text tname <+> text "where"
  80. , indent 2 $ stack
  81. [ text "put" <+> equals <+> text ("put" ++ tname)
  82. , text "get" <+> equals <+> text ("get" ++ tname)
  83. ]
  84. ]
  85. arbitraryInstance :: TypeName -> Doc
  86. arbitraryInstance tname = stack
  87. [ text "instance Q.Arbitrary" <+> text tname <+> text "where"
  88. , indent 2 $ stack
  89. [ text "arbitrary" <+> equals <+> text ("arbitrary" ++ tname)
  90. ]
  91. ]
  92. -- | Produce a ToJSON instance.
  93. --
  94. -- NOTE: this instance relies on a GHC that supports Generics.
  95. toJSONInstance :: TypeName -> Doc
  96. toJSONInstance tname = nest 2 (text "instance ToJSON" <+> text tname)
  97. -- | Produce a FromJSON instance.
  98. --
  99. -- NOTE: this instance relies on a GHC that supports Generics.
  100. fromJSONInstance :: TypeName -> Doc
  101. fromJSONInstance tname = nest 2 (text "instance FromJSON" <+> text tname)
  102. typeDecl :: Type -> Doc
  103. typeDecl t@(StructType _ ss) = stack
  104. [ text "data" <+> text tname <+> equals
  105. , indent 2 $ text tname
  106. , indent 4 $ encloseStack lbrace (rbrace <+> deriv) comma
  107. [ text i <+> colon <> colon <+> text (typeHaskellType st)
  108. | (i,st) <- ss ]
  109. , empty
  110. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  111. , text ("put" ++ tname) <+> text tname <> text "{..}" <+> equals <+> text "do"
  112. , indent 2 $ stack
  113. [ typePutter st <+> text i
  114. | (i,st) <- ss ]
  115. , empty
  116. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  117. , text ("get" ++ tname) <+> equals <+> text "do"
  118. , indent 2 $ stack $
  119. [ text i <+> text "<-" <+> typeGetter st
  120. | (i,st) <- ss ] ++
  121. [ text "return" <+> text tname <> text "{..}" ]
  122. , empty
  123. , serializeInstance tname
  124. , empty
  125. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  126. , text ("arbitrary" ++ tname) <+> equals <+> text "do"
  127. , indent 2 $ stack $
  128. [ text i <+> text "<- Q.arbitrary"
  129. | (i,_) <- ss ] ++
  130. [ text "return" <+> text tname <> text "{..}" ]
  131. , empty
  132. , arbitraryInstance tname
  133. ]
  134. where
  135. tname = typeModuleName t
  136. deriv = typeDeriving ["Eq", "Show", "Data", "Typeable", "Generic"]
  137. typeDecl t@(PrimType (Newtype _ n)) = stack
  138. [ text "newtype" <+> text tname <+> equals
  139. , indent 2 $ text tname <+> align
  140. (lbrace <+> text ("un" ++ tname) <+> text "::" <+>
  141. text (typeHaskellType (PrimType n)) </>
  142. rbrace <+> typeDeriving ["Eq", "Show", "Data", "Typeable", "Generic"])
  143. , empty
  144. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  145. , text ("put" ++ tname) <+> parens (text tname <+> text "a") <+> equals
  146. <+> primTypePutter n <+> text "a"
  147. , empty
  148. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  149. , text ("get" ++ tname) <+> equals <+> text "do"
  150. , indent 2 $ stack $
  151. [ text "a <-" <+> primTypeGetter n
  152. , text "return" <+> parens (text tname <+> text "a") ]
  153. , empty
  154. , serializeInstance tname
  155. , empty
  156. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  157. , text ("arbitrary" ++ tname) <+> equals <+> text "do"
  158. , indent 2 $ stack $
  159. [ text "a" <+> text "<- Q.arbitrary"
  160. , text "return" <+> parens (text tname <+> text "a") ]
  161. , empty
  162. , arbitraryInstance tname
  163. ]
  164. where
  165. tname = typeModuleName t
  166. typeDecl t@(PrimType (EnumType _ s es)) = stack
  167. [ text "data" <+> text tname
  168. , indent 2 $ encloseStack equals deriv (text "|")
  169. [ text (userTypeModuleName i)
  170. | (i, _) <- es ]
  171. , empty
  172. , text "instance Enum" <+> text tname <+> text "where"
  173. , indent 2 $ stack $
  174. [ text "toEnum" <+> ppr e <+> equals <+> text (userTypeModuleName i)
  175. | (i,e) <- es ] ++
  176. [ text ("toEnum _ = error \"toEnum: invalid value for " ++ tname ++ "\"") ] ++
  177. [ text "fromEnum" <+> text (userTypeModuleName i) <+> equals <+> ppr e
  178. | (i,e) <- es ]
  179. , empty
  180. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  181. , stack
  182. [ text ("put" ++ tname) <+> text (userTypeModuleName i) <+> equals <+>
  183. primTypePutter (sizedPrim s) <+> ppr e
  184. | (i,e) <- es ]
  185. , empty
  186. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  187. , text ("get" ++ tname) <+> equals <+> text "do"
  188. , indent 2 $ stack
  189. [ text "a <-" <+> primTypeGetter (sizedPrim s)
  190. , text "case a of"
  191. , indent 2 $ stack $
  192. [ ppr e <+> text "-> return" <+> text (userTypeModuleName i)
  193. | (i,e) <- es
  194. ] ++ [text "_ -> fail \"invalid value in get" <> text tname <> text"\"" ]
  195. ]
  196. , empty
  197. , serializeInstance tname
  198. , empty
  199. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  200. , text ("arbitrary" ++ tname) <+> equals
  201. , indent 2 $ text "Q.elements" <+> encloseStack lbracket rbracket comma
  202. [ text (userTypeModuleName i) | (i,_e) <- es ]
  203. , empty
  204. , arbitraryInstance tname
  205. ]
  206. where
  207. deriv = typeDeriving ["Eq", "Show", "Ord", "Data", "Typeable", "Generic"]
  208. tname = typeModuleName t
  209. typeDecl t = error ("typeDecl: cannot create Haskell decl for type " ++ show t)
  210. typePutter :: Type -> Doc
  211. typePutter (PrimType p) = primTypePutter p
  212. typePutter struct = text "put" <> text (typeModuleName struct)
  213. primTypePutter :: PrimType -> Doc
  214. primTypePutter (Newtype tn _) = text "put" <> text (userTypeModuleName tn)
  215. primTypePutter (EnumType tn _ _) = text "put" <> text (userTypeModuleName tn)
  216. primTypePutter (AtomType (AtomInt _)) = text "put"
  217. primTypePutter (AtomType (AtomWord Bits8)) = text "putWord8"
  218. primTypePutter (AtomType (AtomWord Bits16)) = text "putWord16be"
  219. primTypePutter (AtomType (AtomWord Bits32)) = text "putWord32be"
  220. primTypePutter (AtomType (AtomWord Bits64)) = text "putWord64be"
  221. primTypePutter (AtomType AtomFloat) = text "putFloat32be"
  222. primTypePutter (AtomType AtomDouble) = text "putFloat64be"
  223. primTypePutter VoidType = text "put"
  224. typeGetter :: Type -> Doc
  225. typeGetter (PrimType p) = primTypeGetter p
  226. typeGetter struct = text "get" <> text (typeModuleName struct)
  227. primTypeGetter :: PrimType -> Doc
  228. primTypeGetter (Newtype tn _) = text "get" <> text (userTypeModuleName tn)
  229. primTypeGetter (EnumType tn _ _) = text "get" <> text (userTypeModuleName tn)
  230. primTypeGetter (AtomType (AtomInt _)) = text "get"
  231. primTypeGetter (AtomType (AtomWord Bits8)) = text "getWord8"
  232. primTypeGetter (AtomType (AtomWord Bits16)) = text "getWord16be"
  233. primTypeGetter (AtomType (AtomWord Bits32)) = text "getWord32be"
  234. primTypeGetter (AtomType (AtomWord Bits64)) = text "getWord64be"
  235. primTypeGetter (AtomType AtomFloat) = text "getFloat32be"
  236. primTypeGetter (AtomType AtomDouble) = text "getFloat64be"
  237. primTypeGetter VoidType = text "get"
  238. sizedPrim :: Bits -> PrimType
  239. sizedPrim b = AtomType (AtomWord b)
  240. typeDeriving :: [String] -> Doc
  241. typeDeriving cs = text "deriving" <+> parens (commasep (map text cs))
  242. data ImportType = LibraryType String
  243. | UserType String
  244. | NoImport
  245. deriving (Eq, Show)
  246. importType :: Type -> ImportType
  247. importType (StructType n _) = UserType n
  248. importType (PrimType (EnumType "bool_t" _ _)) = NoImport
  249. importType (PrimType (EnumType n _ _)) = UserType n
  250. importType (PrimType (Newtype n _)) = UserType n
  251. importType (PrimType (AtomType a)) =
  252. case a of
  253. AtomWord _ -> LibraryType "Data.Word"
  254. AtomInt _ -> LibraryType "Data.Int"
  255. _ -> NoImport
  256. importType (PrimType VoidType) = NoImport
  257. isUserDefined :: Type -> Bool
  258. isUserDefined tr = case importType tr of
  259. UserType _ -> True
  260. _ -> False
  261. importDecl :: (String -> Doc) -> ImportType -> Doc
  262. importDecl _ (LibraryType p) =
  263. text "import" <+> text p
  264. importDecl mkpath (UserType t) =
  265. text "import" <+> mkpath (userTypeModuleName t)
  266. importDecl _ NoImport = empty
  267. encloseStack :: Doc -> Doc -> Doc -> [Doc] -> Doc
  268. encloseStack l r p ds = case ds of
  269. [] -> empty -- l </> r
  270. [d] -> l <+> d </> r
  271. _ -> align (l <+> (folddoc (\a b -> a </> p <+> b) ds) </> r)