Types.hs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. module Gidl.Backend.Ivory.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 :: [String] -> TypeRepr -> Artifact
  11. typeModule modulepath tr@(TypeRepr _ td) =
  12. artifactPath (intercalate "/" modulepath) $
  13. artifactText ((typeModuleName tr) ++ ".hs") $
  14. prettyLazyText 80 $
  15. stack
  16. [ text "{-# LANGUAGE RecordWildCards #-}"
  17. , text "{-# LANGUAGE DeriveDataTypeable #-}"
  18. , empty
  19. , text "module"
  20. <+> tm (typeModuleName tr)
  21. <+> text "where"
  22. , empty
  23. , stack (imports ++
  24. [ text "import Data.Serialize"
  25. , text "import Data.Typeable"
  26. , text "import Data.Data"
  27. , text "import qualified Test.QuickCheck as Q"
  28. ])
  29. , empty
  30. , typeDecl typename td
  31. ]
  32. where
  33. imports = map (importDecl tm)
  34. $ nub
  35. $ map importType
  36. $ typeLeaves td
  37. typename = typeModuleName tr
  38. tm mname = mconcat $ punctuate dot
  39. $ map text (modulepath ++ [mname])
  40. typeHaskellType :: TypeRepr -> String
  41. typeHaskellType (TypeRepr tn (StructType _)) = userTypeModuleName tn
  42. typeHaskellType (TypeRepr tn (NewtypeType _)) = userTypeModuleName tn
  43. typeHaskellType (TypeRepr tn (EnumType _)) = userTypeModuleName tn
  44. typeHaskellType (TypeRepr _ (AtomType a)) = case a of
  45. AtomInt Bits8 -> "Int8"
  46. AtomInt Bits16 -> "Int16"
  47. AtomInt Bits32 -> "Int32"
  48. AtomInt Bits64 -> "Int64"
  49. AtomWord Bits8 -> "Word8"
  50. AtomWord Bits16 -> "Word16"
  51. AtomWord Bits32 -> "Word32"
  52. AtomWord Bits64 -> "Word64"
  53. AtomFloat -> "Float"
  54. AtomDouble -> "Double"
  55. typeHaskellType (TypeRepr _ VoidType) = "()"
  56. typeModuleName :: TypeRepr -> String
  57. typeModuleName (TypeRepr tn (StructType _)) = userTypeModuleName tn
  58. typeModuleName (TypeRepr tn (NewtypeType _)) = userTypeModuleName tn
  59. typeModuleName (TypeRepr tn (EnumType _)) = userTypeModuleName tn
  60. typeModuleName (TypeRepr _ (AtomType _)) = error "do not take typeModuleName of an AtomType"
  61. typeModuleName (TypeRepr _ VoidType) = error "do not take typeModuleName of a VoidType"
  62. userTypeModuleName :: String -> String
  63. userTypeModuleName = first_cap . u_to_camel
  64. where
  65. first_cap (s:ss) = (toUpper s) : ss
  66. first_cap [] = []
  67. u_to_camel ('_':'t':[]) = []
  68. u_to_camel ('_':[]) = []
  69. u_to_camel ('_':a:as) = (toUpper a) : u_to_camel as
  70. u_to_camel (a:as) = a : u_to_camel as
  71. u_to_camel [] = []
  72. serializeInstance :: TypeName -> Doc
  73. serializeInstance tname = stack
  74. [ text "instance Serialize" <+> text tname <+> text "where"
  75. , indent 2 $ stack
  76. [ text "put" <+> equals <+> text ("put" ++ tname)
  77. , text "get" <+> equals <+> text ("get" ++ tname)
  78. ]
  79. ]
  80. arbitraryInstance :: TypeName -> Doc
  81. arbitraryInstance tname = stack
  82. [ text "instance Q.Arbitrary" <+> text tname <+> text "where"
  83. , indent 2 $ stack
  84. [ text "arbitrary" <+> equals <+> text ("arbitrary" ++ tname)
  85. ]
  86. ]
  87. typeDecl :: TypeName -> Type TypeRepr -> Doc
  88. typeDecl tname (StructType (Struct ss)) = stack
  89. [ text "data" <+> text tname <+> equals
  90. , indent 2 $ text tname
  91. , indent 4 $ encloseStack lbrace (rbrace <+> deriv) comma
  92. [ text i <+> colon <> colon <+> text (typeHaskellType t)
  93. | (i,t) <- ss ]
  94. , empty
  95. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  96. , text ("put" ++ tname) <+> text tname <> text "{..}" <+> equals <+> text "do"
  97. , indent 2 $ stack
  98. [ text "put" <+> text i
  99. | (i,_) <- ss ]
  100. , empty
  101. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  102. , text ("get" ++ tname) <+> equals <+> text "do"
  103. , indent 2 $ stack $
  104. [ text i <+> text "<- get"
  105. | (i,_) <- ss ] ++
  106. [ text "return" <+> text tname <> text "{..}" ]
  107. , empty
  108. , serializeInstance tname
  109. , empty
  110. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  111. , text ("arbitrary" ++ tname) <+> equals <+> text "do"
  112. , indent 2 $ stack $
  113. [ text i <+> text "<- Q.arbitrary"
  114. | (i,_) <- ss ] ++
  115. [ text "return" <+> text tname <> text "{..}" ]
  116. , empty
  117. , arbitraryInstance tname
  118. ]
  119. where deriv = typeDeriving ["Eq", "Show", "Data", "Typeable"]
  120. typeDecl tname (NewtypeType (Newtype n)) = stack
  121. [ text "newtype" <+> text tname <+> equals
  122. , indent 2 $ text tname <+> align
  123. (lbrace <+> text ("un" ++ tname) <+> text "::" <+>
  124. text (typeHaskellType n) </>
  125. rbrace <+> typeDeriving ["Eq", "Show", "Data", "Typeable"])
  126. , empty
  127. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  128. , text ("put" ++ tname) <+> parens (text tname <+> text "a") <+> equals <+> text "put a"
  129. , empty
  130. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  131. , text ("get" ++ tname) <+> equals <+> text "do"
  132. , indent 2 $ stack $
  133. [ text "a" <+> text "<- get"
  134. , text "return" <+> parens (text tname <+> text "a") ]
  135. , empty
  136. , serializeInstance tname
  137. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  138. , text ("arbitrary" ++ tname) <+> equals <+> text "do"
  139. , indent 2 $ stack $
  140. [ text "a" <+> text "<- Q.arbitrary"
  141. , text "return" <+> parens (text tname <+> text "a") ]
  142. , empty
  143. , arbitraryInstance tname
  144. ]
  145. typeDecl tname (EnumType (EnumT s es)) = stack
  146. [ text "data" <+> text tname
  147. , indent 2 $ encloseStack equals deriv (text "|")
  148. [ text (userTypeModuleName i)
  149. | (i, _) <- es ]
  150. , empty
  151. , text "instance Enum" <+> text tname <+> text "where"
  152. , indent 2 $ stack $
  153. [ text "toEnum" <+> ppr e <+> equals <+> text (userTypeModuleName i)
  154. | (i,e) <- es ] ++
  155. [ text ("toEnum _ = error \"toEnum: invalid value for " ++ tname ++ "\"") ] ++
  156. [ text "fromEnum" <+> text (userTypeModuleName i) <+> equals <+> ppr e
  157. | (i,e) <- es ]
  158. , empty
  159. , text ("put" ++ tname) <+> colon <> colon <+> text "Putter" <+> text tname
  160. , stack
  161. [ text ("put" ++ tname) <+> text (userTypeModuleName i) <+> equals <+>
  162. text "put" <> text (cerealSize s) <+> ppr e
  163. | (i,e) <- es ]
  164. , empty
  165. , text ("get" ++ tname) <+> colon <> colon <+> text "Get" <+> text tname
  166. , text ("get" ++ tname) <+> equals <+> text "do"
  167. , indent 2 $ stack
  168. [ text "a" <+> text "<- get" <> text (cerealSize s)
  169. , text "case a of"
  170. , indent 2 $ stack $
  171. [ ppr e <+> text "-> return" <+> text (userTypeModuleName i)
  172. | (i,e) <- es
  173. ] ++ [text "_ -> fail \"invalid value in get" <> text tname <> text"\"" ]
  174. ]
  175. , empty
  176. , serializeInstance tname
  177. , empty
  178. , text ("arbitrary" ++ tname) <+> colon <> colon <+> text "Q.Gen" <+> text tname
  179. , text ("arbitrary" ++ tname) <+> equals
  180. , indent 2 $ text "Q.elements" <+> encloseStack lbracket rbracket comma
  181. [ text (userTypeModuleName i) | (i,_e) <- es ]
  182. , empty
  183. , arbitraryInstance tname
  184. ]
  185. where deriv = typeDeriving ["Eq", "Show", "Ord", "Data", "Typeable"]
  186. typeDecl tn _ = error ("typeDecl: cannot create decl for built in type " ++ tn)
  187. cerealSize :: Bits -> String
  188. cerealSize Bits8 = "Word8"
  189. cerealSize Bits16 = "Word16be"
  190. cerealSize Bits32 = "Word32be"
  191. cerealSize Bits64 = "Word64be"
  192. typeDeriving :: [String] -> Doc
  193. typeDeriving cs = text "deriving" <+> parens (commasep (map text cs))
  194. data ImportType = LibraryType String
  195. | UserType String
  196. | NoImport
  197. deriving (Eq, Show)
  198. importType :: TypeRepr -> ImportType
  199. importType (TypeRepr _ (AtomType a)) =
  200. case a of
  201. AtomWord _ -> LibraryType "Data.Word"
  202. AtomInt _ -> LibraryType "Data.Int"
  203. _ -> NoImport
  204. importType (TypeRepr _ VoidType) = NoImport
  205. importType (TypeRepr n _) = UserType n
  206. isUserDefined :: TypeRepr -> Bool
  207. isUserDefined tr = case importType tr of
  208. UserType _ -> True
  209. _ -> False
  210. importDecl :: (String -> Doc) -> ImportType -> Doc
  211. importDecl _ (LibraryType p) =
  212. text "import" <+> text p
  213. importDecl mkpath (UserType t) =
  214. text "import" <+> mkpath (userTypeModuleName t)
  215. importDecl _ NoImport = empty
  216. encloseStack :: Doc -> Doc -> Doc -> [Doc] -> Doc
  217. encloseStack l r p ds = case ds of
  218. [] -> empty -- l </> r
  219. [d] -> l <+> d </> r
  220. _ -> align (l <+> (folddoc (\a b -> a </> p <+> b) ds) </> r)