{-# LANGUAGE RankNTypes #-} module Scion.Hoogle.Parser where import Data.List (find, intercalate) import qualified Data.Map as M import Data.Maybe (fromJust, catMaybes) import Distribution.Package hiding (Package) import Language.Haskell.Exts.Annotated.Syntax import Scion.Browser import Scion.Browser.Parser.Internal import Scion.Browser.Query import Scion.Hoogle.Types import Text.Parsec.Char import Text.Parsec.Combinator import Text.Parsec.Prim data HalfResult = HalfPackage String | HalfModule String (Documented Module) | HalfDecl String (Documented Decl) | HalfGadtDecl String (Documented GadtDecl) hoogleElements :: Database -> BSParser [Result] hoogleElements db = do elts <- hoogleElements' return $ catMaybes $ map (convertHalfToResult db) elts hoogleElements' :: BSParser [HalfResult] hoogleElements' = try (do spaces0 eof return []) <|> (do first <- hoogleElement rest <- many $ try (try eol >> try hoogleElement) spaces eof return $ first:rest) hoogleElement :: BSParser HalfResult hoogleElement = try (do pname <- hooglePackageName return $ HalfPackage pname) <|> try (do (mname, m) <- moduled (module_ NoDoc) return $ HalfModule mname m) <|> try (do (mname, d) <- moduled (function NoDoc) return $ HalfDecl mname d) <|> try (do (mname, d) <- moduled (dataHead NoDoc) return $ HalfDecl mname d) <|> try (do (mname, d) <- moduled (newtypeHead NoDoc) return $ HalfDecl mname d) <|> try (do (mname, d) <- moduled (type_ NoDoc) return $ HalfDecl mname d) <|> try (do (mname, d) <- moduled (class_ NoDoc) return $ HalfDecl mname d) <|> try (do (mname, d) <- moduled (constructor NoDoc) return $ HalfGadtDecl mname d) moduled :: BSParser a -> BSParser (String, a) moduled p = try (do mname <- try conid `sepBy` char '.' let name = intercalate "." (map getid mname) try spaces1 rest <- p return (name, rest)) hooglePackageName :: BSParser String hooglePackageName = do string "package" spaces1 name <- restOfLine spaces0 return name convertHalfToResult :: Database -> HalfResult -> Maybe Result convertHalfToResult db (HalfPackage pname) = case packagesByName pname db of [] -> Nothing pkgs -> Just $ RPackage pkgs convertHalfToResult db (HalfModule mname _) = case findPackagesForModule db mname of [] -> Nothing mds -> Just $ RModule mds convertHalfToResult db (HalfDecl mname dcl) = let pidMods = findPackagesForModule db mname in case findDeclsInModules pidMods dcl of [] -> Nothing decls -> Just $ RDeclaration decls convertHalfToResult db (HalfGadtDecl mname dcl) = let pidMods = findPackagesForModule db mname gadts = concatMap filterGadtStyleItems pidMods in case findConstructorsInGadts gadts (getName dcl) of [] -> Nothing decls -> Just $ RConstructor decls findPackagesForModule :: Database -> String -> [(PackageIdentifier, Documented Module)] findPackagesForModule db md = let pkgs = M.filter (\(Package _ _ mds) -> M.member md mds) db in M.toAscList $ M.map (\(Package _ _ mds) -> fromJust $ M.lookup md mds) pkgs findDeclsInModules :: [(PackageIdentifier, Documented Module)] -> Documented Decl -> [(PackageIdentifier, String, Documented Decl)] findDeclsInModules pidMods declName = foldr (\pidMod lst -> case findDeclInModule pidMod declName of Nothing -> lst Just d -> d:lst) [] pidMods findDeclInModule :: (PackageIdentifier, Documented Module) -> Documented Decl -> Maybe (PackageIdentifier, String, Documented Decl) findDeclInModule (pid, md@(Module _ _ _ _ decls)) dname = case find (\d -> (fmap (const "") d) == (fmap (const "") dname)) decls of Nothing -> Nothing Just d -> Just (pid, getName md, d) findDeclInModule _ _ = error "The impossible happened" filterGadtStyleItems :: (PackageIdentifier, Documented Module) -> [(PackageIdentifier, String, Documented Decl)] filterGadtStyleItems (pid, md@(Module _ _ _ _ decls)) = map (\d -> (pid, getName md, d)) $ filter (\x -> case x of (GDataDecl _ _ _ _ _ _ _) -> True _ -> False) decls filterGadtStyleItems _ = error "The impossible happened" findConstructorsInGadts :: [(PackageIdentifier, String, Documented Decl)] -> String -> [(PackageIdentifier, String, Documented Decl, Documented GadtDecl)] findConstructorsInGadts gadts conName = foldr (\gadt lst -> case findConstructorInGadt gadt conName of Nothing -> lst Just c -> c:lst) [] gadts findConstructorInGadt :: (PackageIdentifier, String, Documented Decl) -> String -> Maybe (PackageIdentifier, String, Documented Decl, Documented GadtDecl) findConstructorInGadt (pid, mdName, gadt@(GDataDecl _ _ _ _ _ decls _)) cname = case find (\c -> (getName c) == cname) decls of Nothing -> Nothing Just c -> Just (pid, mdName, gadt, c) findConstructorInGadt _ _ = error "The impossible happened"