Types.hs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 1000 $
  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. typeModuleName :: Type -> String
  61. typeModuleName (StructType tn _) = userTypeModuleName tn
  62. typeModuleName (PrimType (Newtype tn _)) = userTypeModuleName tn
  63. typeModuleName (PrimType (EnumType tn _ _)) = userTypeModuleName tn
  64. typeModuleName (PrimType (AtomType _)) = error "do not take typeModuleName of an AtomType"
  65. userTypeModuleName :: String -> String
  66. userTypeModuleName = first_cap . u_to_camel
  67. where
  68. first_cap (s:ss) = (toUpper s) : ss
  69. first_cap [] = []
  70. u_to_camel ('_':'t':[]) = []
  71. u_to_camel ('_':[]) = []
  72. u_to_camel ('_':a:as) = (toUpper a) : u_to_camel as
  73. u_to_camel (a:as) = a : u_to_camel as
  74. u_to_camel [] = []
  75. serializeInstance :: TypeName -> Doc
  76. serializeInstance tname = stack
  77. [ text "instance Serialize" <+> text tname <+> text "where"
  78. , indent 2 $ stack
  79. [ text "put" <+> equals <+> text ("put" ++ tname)
  80. , text "get" <+> equals <+> text ("get" ++ tname)
  81. ]
  82. ]
  83. arbitraryInstance :: TypeName -> Doc
  84. arbitraryInstance tname = stack
  85. [ text "instance Q.Arbitrary" <+> text tname <+> text "where"
  86. , indent 2 $ stack
  87. [ text "arbitrary" <+> equals <+> text ("arbitrary" ++ tname)
  88. ]
  89. ]
  90. -- | Produce a ToJSON instance.
  91. --
  92. -- NOTE: this instance relies on a GHC that supports Generics.
  93. toJSONInstance :: TypeName -> Doc
  94. toJSONInstance tname = nest 2 (text "instance ToJSON" <+> text tname)
  95. -- | Produce a FromJSON instance.
  96. --
  97. -- NOTE: this instance relies on a GHC that supports Generics.
  98. fromJSONInstance :: TypeName -> Doc
  99. fromJSONInstance tname = nest 2 (text "instance FromJSON" <+> text tname)
  100. typeDecl :: Type -> Doc
  101. typeDecl t@(StructType _ ss) = stack
  102. [ text "data" <+> text tname <+> equals
  103. , indent 2 $ text tname
  104. , indent 4 $ encloseStack lbrace (rbrace <+> deriv) comma
  105. [ text i <+> colon <> colon <+> text (typeHaskellType st)
  106. | (i,st) <- ss ]
  107. , empty
  108. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  109. , text ("put" ++ tname) <+> text tname <> text "{..}" <+> equals <+> text "do"
  110. , indent 2 $ stack
  111. [ typePutter st <+> text i
  112. | (i,st) <- ss ]
  113. , empty
  114. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  115. , text ("get" ++ tname) <+> equals <+> text "do"
  116. , indent 2 $ stack $
  117. [ text i <+> text "<-" <+> typeGetter st
  118. | (i,st) <- ss ] ++
  119. [ text "return" <+> text tname <> text "{..}" ]
  120. , empty
  121. , serializeInstance tname
  122. , empty
  123. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  124. , text ("arbitrary" ++ tname) <+> equals <+> text "do"
  125. , indent 2 $ stack $
  126. [ text i <+> text "<- Q.arbitrary"
  127. | (i,_) <- ss ] ++
  128. [ text "return" <+> text tname <> text "{..}" ]
  129. , empty
  130. , arbitraryInstance tname
  131. ]
  132. where
  133. tname = typeModuleName t
  134. deriv = typeDeriving ["Eq", "Show", "Data", "Typeable", "Generic"]
  135. typeDecl t@(PrimType (Newtype _ n)) = stack
  136. [ text "newtype" <+> text tname <+> equals
  137. , indent 2 $ text tname <+> align
  138. (lbrace <+> text ("un" ++ tname) <+> text "::" <+>
  139. text (typeHaskellType (PrimType n)) </>
  140. rbrace <+> typeDeriving ["Eq", "Show", "Data", "Typeable", "Generic"])
  141. , empty
  142. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  143. , text ("put" ++ tname) <+> parens (text tname <+> text "a") <+> equals
  144. <+> primTypePutter n <+> text "a"
  145. , empty
  146. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  147. , text ("get" ++ tname) <+> equals <+> text "do"
  148. , indent 2 $ stack $
  149. [ text "a <-" <+> primTypeGetter n
  150. , text "return" <+> parens (text tname <+> text "a") ]
  151. , empty
  152. , serializeInstance tname
  153. , empty
  154. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  155. , text ("arbitrary" ++ tname) <+> equals <+> text "do"
  156. , indent 2 $ stack $
  157. [ text "a" <+> text "<- Q.arbitrary"
  158. , text "return" <+> parens (text tname <+> text "a") ]
  159. , empty
  160. , arbitraryInstance tname
  161. ]
  162. where
  163. tname = typeModuleName t
  164. typeDecl t@(PrimType (EnumType _ s es)) = stack
  165. [ text "data" <+> text tname
  166. , indent 2 $ encloseStack equals deriv (text "|")
  167. [ text (userTypeModuleName i)
  168. | (i, _) <- es ]
  169. , empty
  170. , text "instance Enum" <+> text tname <+> text "where"
  171. , indent 2 $ stack $
  172. [ text "toEnum" <+> ppr e <+> equals <+> text (userTypeModuleName i)
  173. | (i,e) <- es ] ++
  174. [ text ("toEnum _ = error \"toEnum: invalid value for " ++ tname ++ "\"") ] ++
  175. [ text "fromEnum" <+> text (userTypeModuleName i) <+> equals <+> ppr e
  176. | (i,e) <- es ]
  177. , empty
  178. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  179. , stack
  180. [ text ("put" ++ tname) <+> text (userTypeModuleName i) <+> equals <+>
  181. primTypePutter (sizedPrim s) <+> ppr e
  182. | (i,e) <- es ]
  183. , empty
  184. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  185. , text ("get" ++ tname) <+> equals <+> text "do"
  186. , indent 2 $ stack
  187. [ text "a <-" <+> primTypeGetter (sizedPrim s)
  188. , text "case a of"
  189. , indent 2 $ stack $
  190. [ ppr e <+> text "-> return" <+> text (userTypeModuleName i)
  191. | (i,e) <- es
  192. ] ++ [text "_ -> fail \"invalid value in get" <> text tname <> text"\"" ]
  193. ]
  194. , empty
  195. , serializeInstance tname
  196. , empty
  197. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  198. , text ("arbitrary" ++ tname) <+> equals
  199. , indent 2 $ text "Q.elements" <+> encloseStack lbracket rbracket comma
  200. [ text (userTypeModuleName i) | (i,_e) <- es ]
  201. , empty
  202. , arbitraryInstance tname
  203. ]
  204. where
  205. deriv = typeDeriving ["Eq", "Show", "Ord", "Data", "Typeable", "Generic"]
  206. tname = typeModuleName t
  207. typeDecl t = error ("typeDecl: cannot create Haskell decl for type " ++ show t)
  208. typePutter :: Type -> Doc
  209. typePutter (PrimType p) = primTypePutter p
  210. typePutter struct = text "put" <> text (typeModuleName struct)
  211. primTypePutter :: PrimType -> Doc
  212. primTypePutter (Newtype tn _) = text "put" <> text (userTypeModuleName tn)
  213. primTypePutter (EnumType "bool_t" _ _) = text "put"
  214. primTypePutter (EnumType tn _ _) = text "put" <> text (userTypeModuleName tn)
  215. primTypePutter (AtomType (AtomInt _)) = text "put"
  216. primTypePutter (AtomType (AtomWord Bits8)) = text "putWord8"
  217. primTypePutter (AtomType (AtomWord Bits16)) = text "putWord16be"
  218. primTypePutter (AtomType (AtomWord Bits32)) = text "putWord32be"
  219. primTypePutter (AtomType (AtomWord Bits64)) = text "putWord64be"
  220. primTypePutter (AtomType AtomFloat) = text "putFloat32be"
  221. primTypePutter (AtomType AtomDouble) = text "putFloat64be"
  222. typeGetter :: Type -> Doc
  223. typeGetter (PrimType p) = primTypeGetter p
  224. typeGetter struct = text "get" <> text (typeModuleName struct)
  225. primTypeGetter :: PrimType -> Doc
  226. primTypeGetter (Newtype tn _) = text "get" <> text (userTypeModuleName tn)
  227. primTypeGetter (EnumType "bool_t" _ _) = text "get"
  228. primTypeGetter (EnumType tn _ _) = text "get" <> text (userTypeModuleName tn)
  229. primTypeGetter (AtomType (AtomInt _)) = text "get"
  230. primTypeGetter (AtomType (AtomWord Bits8)) = text "getWord8"
  231. primTypeGetter (AtomType (AtomWord Bits16)) = text "getWord16be"
  232. primTypeGetter (AtomType (AtomWord Bits32)) = text "getWord32be"
  233. primTypeGetter (AtomType (AtomWord Bits64)) = text "getWord64be"
  234. primTypeGetter (AtomType AtomFloat) = text "getFloat32be"
  235. primTypeGetter (AtomType AtomDouble) = text "getFloat64be"
  236. sizedPrim :: Bits -> PrimType
  237. sizedPrim b = AtomType (AtomWord b)
  238. typeDeriving :: [String] -> Doc
  239. typeDeriving cs = text "deriving" <+> parens (commasep (map text cs))
  240. data ImportType = LibraryType String
  241. | UserType String
  242. | NoImport
  243. deriving (Eq, Show)
  244. importType :: Type -> ImportType
  245. importType (StructType n _) = UserType n
  246. importType (PrimType (EnumType "bool_t" _ _)) = NoImport
  247. importType (PrimType (EnumType n _ _)) = UserType n
  248. importType (PrimType (Newtype n _)) = UserType n
  249. importType (PrimType (AtomType a)) =
  250. case a of
  251. AtomWord _ -> LibraryType "Data.Word"
  252. AtomInt _ -> LibraryType "Data.Int"
  253. _ -> NoImport
  254. isUserDefined :: Type -> Bool
  255. isUserDefined tr = case importType tr of
  256. UserType _ -> True
  257. _ -> False
  258. importDecl :: (String -> Doc) -> ImportType -> Doc
  259. importDecl _ (LibraryType p) =
  260. text "import" <+> text p
  261. importDecl mkpath (UserType t) =
  262. text "import" <+> mkpath (userTypeModuleName t)
  263. importDecl _ NoImport = empty
  264. qualifiedImportDecl :: (String -> Doc) -> ImportType -> Doc
  265. qualifiedImportDecl _ (LibraryType p) =
  266. text "import" <+> text p
  267. qualifiedImportDecl mkpath (UserType t) =
  268. text "import qualified" <+> mkpath (userTypeModuleName t) <+> text "as"
  269. <+> text (userTypeModuleName t)
  270. qualifiedImportDecl _ NoImport = empty
  271. encloseStack :: Doc -> Doc -> Doc -> [Doc] -> Doc
  272. encloseStack l r p ds = case ds of
  273. [] -> empty -- l </> r
  274. [d] -> l <+> d </> r
  275. _ -> align (l <+> (folddoc (\a b -> a </> p <+> b) ds) </> r)