/src-extra/tosort/examples/wrappers/GenerateTupleWrapper.lhs

http://github.com/JakeWheat/hssqlppp · Haskell · 220 lines · 185 code · 35 blank · 0 comment · 7 complexity · 4f58c1881226a3b05dc827c6ad6af402 MD5 · raw file

  1. Code isn't finished, shouldn't be too difficult to sort it out, but
  2. seems a bit pointless when the th version works nice.
  3. > {-# LANGUAGE FlexibleContexts #-}
  4. > module Database.HsSqlPpp.Examples.Wrappers.GenerateTupleWrapper
  5. > (wrapperGen) where
  6. > import Language.Haskell.Exts hiding (String)
  7. > import qualified Language.Haskell.Exts as Exts
  8. > import Data.Generics.PlateData
  9. > import Data.Generics hiding (Prefix,Infix)
  10. > import Control.Monad.Error
  11. > import Data.Maybe
  12. > import Database.HsSqlPpp.SqlTypes as Sql
  13. > import Database.HsSqlPpp.Catalog
  14. > import Database.HsSqlPpp.TypeChecker
  15. > import Database.HsSqlPpp.Parser
  16. > import Database.HsSqlPpp.Annotation
  17. > wrapperGen :: String -- ^ name of database to typecheck against
  18. > -> FilePath -- ^ haskell source filename to process
  19. > -> IO String -- ^ generated wrapper source code
  20. > wrapperGen db fn = do
  21. > p <- parseFile fn
  22. > case p of
  23. > ParseOk ast -> do
  24. > catU <- readCatalogFromDatabase db
  25. > case updateCatalog defaultCatalog catU of
  26. > Left er -> return $ show er
  27. > Right cat ->
  28. > return $ {-ppShow ast ++ "\n\n" ++ -} prettyPrint (processTree cat (addImports ast))
  29. > x -> return $ show x
  30. This is the function which finds the statements which look like
  31. ident = "string"
  32. and converts them into hdbc wrappers with the correct types
  33. > processTree :: Data a => Catalog -> a -> a
  34. > processTree cat =
  35. > transformBi $ \x ->
  36. > case x of
  37. > (PatBind _
  38. > (PVar (Ident fnName))
  39. > Nothing
  40. > (UnGuardedRhs(Lit (Exts.String sqlSrc)))
  41. > (BDecls [])) : tl
  42. > -> createWrapper cat fnName sqlSrc ++ tl
  43. > x1 -> x1
  44. for each bind to convert, lookup the haskell types needed, then
  45. create a type sig and a function to use hdbc to run the sql
  46. > createWrapper :: Catalog
  47. > -> String
  48. > -> String
  49. > -> [Decl]
  50. > createWrapper cat fnName sql =
  51. > let rt = getStatementType cat sql
  52. > in case rt of
  53. > Left e -> error e
  54. > Right (StatementType pt ts) ->
  55. > let pts = map sqlTypeToHaskellTypeName pt
  56. > tns = map (sqlTypeToHaskellTypeName . snd) ts
  57. > in [makeTypeSig fnName pts tns
  58. > ,makeFn fnName sql pts tns]
  59. ================================================================================
  60. create the type signature for a wrapper, produces something like
  61. (IConnection conn) => conn -> inarg1 -> inarg2 -> ... ->
  62. IO [(outarg1, outarg2, ...)]
  63. > makeTypeSig :: String -> [String] -> [String] -> Decl
  64. > makeTypeSig fnName argTypes typeNames =
  65. > TypeSig noSrcLoc [Ident fnName] $
  66. > TyForall Nothing [ClassA (UnQual (Ident "IConnection")) [TyVar(Ident "conn")]] $
  67. > foldr TyFun lastArg args
  68. > where
  69. > tc = TyCon . UnQual . Ident
  70. > tntt = (TyApp (tc "Maybe")) . tc
  71. > args = ((TyVar (Ident "conn")) : map tntt argTypes)
  72. > lastArg = (TyApp (tc "IO") (TyList (TyTuple Boxed $ map tntt typeNames)))
  73. ================================================================================
  74. create the function which calls hdbc
  75. takes something like:
  76. pieces_at_pos = "select * from pieces where x = ? and y = ?;"
  77. and produces:
  78. pieces_at_pos conn b0 b1
  79. = do r <- selectRelation conn
  80. "select * from pieces where x = ? and y = ?;"
  81. [toSql b0, toSql b1]
  82. return $
  83. flip map r $
  84. \ [a0, a1, a2, a3, a4] ->
  85. (fromSql a0, fromSql a1, fromSql a2, fromSql a3, fromSql a4)
  86. doesn't really need to know the types, just the number of inargs and outargs,
  87. since the work is done by hdbc's toSql and fromSql, and by the type signature
  88. that is generated in the function above
  89. > makeFn :: String -> String -> [String] -> [String] -> Decl
  90. > makeFn fnName sql pts typeNames = FunBind
  91. > [ Match noSrcLoc(
  92. > Ident fnName )
  93. > (PVar (Ident "conn") : map (PVar . Ident) pNames)
  94. > Nothing (
  95. > UnGuardedRhs (
  96. > Do
  97. > [ Generator noSrcLoc (
  98. > PVar ( Ident "r" ) ) (
  99. > App (
  100. > App (
  101. > App (
  102. > Var ( UnQual ( Ident "selectRelation" ) ) ) (
  103. > Var ( UnQual ( Ident "conn" ) ) ) ) (
  104. > Lit ( Exts.String sql ) ) ) (
  105. > List $ map (\l -> App (
  106. > Var ( UnQual ( Ident "toSql" ) ) ) (
  107. > Var ( UnQual ( Ident l ) ) )) pNames
  108. > ))
  109. > , Qualifier (
  110. > InfixApp (
  111. > Var ( UnQual ( Ident "return" ) ) ) (
  112. > QVarOp ( UnQual ( Symbol "$" ) ) ) (
  113. > InfixApp (
  114. > App (
  115. > App (
  116. > Var ( UnQual ( Ident "flip" ) ) ) (
  117. > Var ( UnQual ( Ident "map" ) ) ) ) (
  118. > Var ( UnQual ( Ident "r" ) ) ) ) (
  119. > QVarOp ( UnQual ( Symbol "$" ) ) ) (
  120. > Lambda noSrcLoc
  121. > [ PList (map (PVar . Ident) vns)
  122. > ] (
  123. > Tuple (map (\n -> App (vui "fromSql") (vui n)) vns)
  124. > ) ) ) )
  125. > ] ) ) (
  126. > BDecls [] )
  127. > ]
  128. > where
  129. > varName n = "a" ++ show n
  130. > vns = map varName [0..length typeNames - 1]
  131. > vui = Var . UnQual . Ident
  132. > pName n = "b" ++ show n
  133. > pNames = map pName [0..length pts - 1]
  134. ================================================================================
  135. > addImports :: Data a => a -> a
  136. > addImports =
  137. > transformBi $ \x ->
  138. > case x of
  139. > Module sl mn o wt es im d -> Module sl mn o wt es (imports ++ im) d
  140. > imports :: [ImportDecl]
  141. > imports = [ImportDecl {importLoc = noSrcLoc
  142. > ,importModule = ModuleName "Database.HDBC"
  143. > ,importQualified = False
  144. > ,importSrc = False
  145. > ,importPkg = Nothing
  146. > ,importAs = Nothing
  147. > ,importSpecs = Nothing
  148. > }
  149. > ,ImportDecl {importLoc = noSrcLoc
  150. > ,importModule = ModuleName "Database.HsSqlPpp.Dbms.WrapLib"
  151. > ,importQualified = False
  152. > ,importSrc = False
  153. > ,importPkg = Nothing
  154. > ,importAs = Nothing
  155. > ,importSpecs = Nothing
  156. > }]
  157. ================================================================================
  158. parsing and typechecking
  159. get the input and output types for a parameterized sql statement:
  160. > getStatementType :: Catalog -> String -> Either String StatementType
  161. > getStatementType cat sql = do
  162. > ast <- tsl $ parseSql "" sql
  163. > let (_,aast) = typeCheck cat ast
  164. > let a = getTopLevelInfos aast
  165. > return $ fromJust $ head a
  166. return the equivalent haskell type for a sql type as a string
  167. > sqlTypeToHaskellTypeName :: Sql.Type -> String
  168. > sqlTypeToHaskellTypeName t =
  169. > case t of
  170. > ScalarType "text" -> "String"
  171. > ScalarType "int4" -> "Int"
  172. > ScalarType "int8" -> "Int"
  173. > ScalarType "bool" -> "Bool"
  174. > DomainType _ -> "String"
  175. > x -> show x
  176. ================================================================================
  177. simple ast shortcuts
  178. > noSrcLoc :: SrcLoc
  179. > noSrcLoc = (SrcLoc "" 0 0)
  180. ================================================================================
  181. error utility - convert either to ErrorT String
  182. > tsl :: (MonadError String m, Show t) => Either t a -> m a
  183. > tsl x = case x of
  184. > Left s -> throwError $ show s
  185. > Right b -> return b