/src/Scion/PersistentHoogle/Parser.hs

https://github.com/JPMoresmau/scion-class-browser · Haskell · 174 lines · 158 code · 13 blank · 3 comment · 11 complexity · b962335f8f2c6c97a461798c181e8e6a MD5 · raw file

  1. {-# LANGUAGE RankNTypes, ImpredicativeTypes, ScopedTypeVariables #-}
  2. module Scion.PersistentHoogle.Parser where
  3. import Data.List (intercalate)
  4. import qualified Data.Text as T
  5. import Database.Persist
  6. import Database.Persist.Sql
  7. import Language.Haskell.Exts.Annotated.Syntax
  8. import Scion.PersistentBrowser.DbTypes
  9. import Scion.PersistentBrowser.Parser.Internal
  10. import Scion.PersistentBrowser.Query
  11. import Scion.PersistentBrowser.Types
  12. import Scion.PersistentHoogle.Types
  13. import Text.Parsec.Char
  14. import Text.Parsec.Combinator
  15. import Text.Parsec.Prim
  16. data HalfResult = HalfPackage String
  17. | HalfModule String (Documented Module)
  18. | HalfDecl String (Documented Decl)
  19. | HalfGadtDecl String (Documented GadtDecl)
  20. | HalfKeyword String
  21. | HalfWarning String -- ^ a warning
  22. hoogleElements :: BSParser (SQL [Result])
  23. hoogleElements = do elts <- hoogleElements'
  24. let results = catMaybesM $ map convertHalfToResult elts
  25. return results
  26. catMaybesM :: Monad m => [m (Maybe a)] -> m [a]
  27. catMaybesM [] = return []
  28. catMaybesM (x:xs) = do y <- x
  29. zs <- catMaybesM xs
  30. case y of
  31. Nothing -> return zs
  32. Just z -> return (z:zs)
  33. hoogleElements' :: BSParser [HalfResult]
  34. hoogleElements' = try (do spaces0
  35. optional $ try (do
  36. string "No results found"
  37. spacesOrEol0)
  38. eof
  39. return [])
  40. <|> (do first <- hoogleElement
  41. rest <- many $ try (try eol >> try hoogleElement)
  42. spaces
  43. eof
  44. return $ first:rest)
  45. hoogleElement :: BSParser HalfResult
  46. hoogleElement = try (do pname <- hooglePackageName
  47. return $ HalfPackage pname)
  48. <|> try (do pname <- hoogleKeyword
  49. return $ HalfKeyword pname)
  50. <|> try (do (mname, m) <- moduled (module_ NoDoc)
  51. return $ HalfModule mname m)
  52. <|> try (do (mname, d) <- moduled (function NoDoc)
  53. return $ HalfDecl mname d)
  54. <|> try (do (mname, d) <- moduled (dataHead NoDoc)
  55. return $ HalfDecl mname d)
  56. <|> try (do (mname, d) <- moduled (newtypeHead NoDoc)
  57. return $ HalfDecl mname d)
  58. <|> try (do (mname, d) <- moduled (type_ NoDoc)
  59. return $ HalfDecl mname d)
  60. <|> try (do (mname, d) <- moduled (class_ NoDoc)
  61. return $ HalfDecl mname d)
  62. <|> try (do (mname, d) <- moduled (constructor NoDoc)
  63. return $ HalfGadtDecl mname d)
  64. <|> try (do
  65. string "Warning:"
  66. spaces0
  67. s<-restOfLine
  68. return $ HalfWarning s)
  69. moduled :: BSParser a -> BSParser (String, a)
  70. moduled p = try (do mname <- try conid `sepBy` char '.'
  71. let name = intercalate "." (map getid mname)
  72. try spaces1
  73. rest <- p
  74. return (name, rest))
  75. hooglePackageName :: BSParser String
  76. hooglePackageName = string "package" >> restIsName
  77. -- | handle a keyword. For example searching for 'id' gives 'keyword hiding' in the results
  78. hoogleKeyword :: BSParser String
  79. hoogleKeyword = string "keyword" >> restIsName
  80. -- | Rest of the line is a name
  81. restIsName :: BSParser String
  82. restIsName = do
  83. spaces1
  84. name <- restOfLine
  85. spaces0
  86. return name
  87. convertHalfToResult :: HalfResult -> SQL (Maybe Result)
  88. convertHalfToResult (HalfKeyword kw) =
  89. return $ Just (RKeyword kw)
  90. convertHalfToResult (HalfWarning w) =
  91. return $ Just (RWarning w)
  92. convertHalfToResult (HalfPackage pname) =
  93. do pkgs <- packagesByName pname Nothing
  94. case pkgs of
  95. [] -> return Nothing
  96. p -> return $ Just (RPackage p)
  97. convertHalfToResult (HalfModule mname _) =
  98. do let sql = "SELECT DbModule.name, DbModule.doc, DbModule.packageId, DbPackage.name, DbPackage.version"
  99. ++ " FROM DbModule, DbPackage"
  100. ++ " WHERE DbModule.packageId = DbPackage.id"
  101. ++ " AND DbModule.name = ?"
  102. mods <- queryDb sql [mname] action
  103. return $ if null mods then Nothing else Just (RModule mods)
  104. where action [PersistText modName, modDoc, PersistInt64 pkgId, PersistText pkgName, PersistText pkgVersion] =
  105. ( DbPackageIdentifier (T.unpack pkgName) (T.unpack pkgVersion)
  106. , DbModule (T.unpack modName) (fromDbText modDoc) (DbPackageKey $ SqlBackendKey pkgId) )
  107. action _ = error "This should not happen"
  108. convertHalfToResult (HalfDecl mname dcl) =
  109. do let sql = "SELECT DbDecl.id, DbDecl.declType, DbDecl.name, DbDecl.doc, DbDecl.kind, DbDecl.signature, DbDecl.equals, DbDecl.moduleId"
  110. ++ ", DbPackage.name, DbPackage.version"
  111. ++ " FROM DbDecl, DbModule, DbPackage"
  112. ++ " WHERE DbDecl.moduleId = DbModule.id"
  113. ++ " AND DbModule.packageId = DbPackage.id"
  114. ++ " AND DbDecl.name = ?"
  115. ++ " AND DbModule.name = ?"
  116. decls <- queryDb sql [getName dcl, mname] action
  117. completeDecls <- mapM (\(pkgId, modName, dclKey, dclInfo) -> do complete <- getAllDeclInfo (dclKey, dclInfo)
  118. return (pkgId, modName, complete) ) decls
  119. return $ if null completeDecls then Nothing else Just (RDeclaration completeDecls)
  120. where action [ PersistInt64 declId, PersistText declType, PersistText declName
  121. , declDoc, declKind, declSignature, declEquals, PersistInt64 modId
  122. , PersistText pkgName, PersistText pkgVersion ] =
  123. let (innerDclKey :: DbDeclId) = DbDeclKey $ SqlBackendKey declId
  124. innerDcl = DbDecl (read (T.unpack declType)) (T.unpack declName) (fromDbText declDoc)
  125. (fromDbText declKind) (fromDbText declSignature) (fromDbText declEquals)
  126. (DbModuleKey $ SqlBackendKey modId)
  127. in ( DbPackageIdentifier (T.unpack pkgName) (T.unpack pkgVersion)
  128. , mname
  129. , innerDclKey
  130. , innerDcl
  131. )
  132. action _ = error "This should not happen"
  133. convertHalfToResult (HalfGadtDecl mname dcl) =
  134. do let sql = "SELECT DbConstructor.name, DbConstructor.signature"
  135. ++ ", DbDecl.id, DbDecl.declType, DbDecl.name, DbDecl.doc, DbDecl.kind, DbDecl.signature, DbDecl.equals, DbDecl.moduleId"
  136. ++ ", DbPackage.name, DbPackage.version"
  137. ++ " FROM DbConstructor, DbDecl, DbModule, DbPackage"
  138. ++ " WHERE DbConstructor.declId = DbDecl.id"
  139. ++ " AND DbDecl.moduleId = DbModule.id"
  140. ++ " AND DbModule.packageId = DbPackage.id"
  141. ++ " AND DbDecl.name = ?"
  142. ++ " AND DbModule.name = ?"
  143. decls <- queryDb sql [getName dcl, mname] action
  144. completeDecls <- mapM (\(pkgId, modName, dclKey, dclInfo, cst) -> do complete <- getAllDeclInfo (dclKey, dclInfo)
  145. return (pkgId, modName, complete, cst) ) decls
  146. return $ if null completeDecls then Nothing else Just (RConstructor completeDecls)
  147. where action [ PersistText constName, PersistText constSignature
  148. , PersistInt64 declId, PersistText declType, PersistText declName
  149. , declDoc, declKind, declSignature, declEquals, PersistInt64 modId
  150. , PersistText pkgName, PersistText pkgVersion ] =
  151. let (innerDclKey :: DbDeclId) = DbDeclKey $ SqlBackendKey declId
  152. innerDcl = DbDecl (read (T.unpack declType)) (T.unpack declName) (fromDbText declDoc)
  153. (fromDbText declKind) (fromDbText declSignature) (fromDbText declEquals)
  154. (DbModuleKey $ SqlBackendKey modId)
  155. in ( DbPackageIdentifier (T.unpack pkgName) (T.unpack pkgVersion)
  156. , mname
  157. , innerDclKey
  158. , innerDcl
  159. , DbConstructor (T.unpack constName) (T.unpack constSignature) (DbDeclKey $ SqlBackendKey declId)
  160. )
  161. action _ = error "This should not happen"