packages feed

lambdabot-4.0: scripts/hoogle/data/hadhtml/Main.hs

module Main where

import System
import Directory
import List

import Lexer
import TextUtil
import Char


copyright = ["-- Generated by Hoogle, from Haddock HTML", "-- (C) Neil Mitchell 2005",""]


-- example, for full GHC do C:\ghc\ghc-6.4\doc\html\libraries
main = do xs <- getArgs
          let res = case xs of
                (a:_) -> a
                [] -> "C:\\ghc\\ghc-6.4.1\\doc\\html\\libraries"
          hoogledoc res


test = hoogledoc "examples"





hoogledoc :: FilePath -> IO ()
hoogledoc x = do filelist <- docFiles x
                 textlist <- mapM readFile filelist
                 
                 excludeExists <- doesFileExist "exclude.txt"
                 excludeSrc <- if excludeExists then readFile "exclude.txt" else return ""
                 h98 <- loadH98
                 
                 let filetext = zip filelist textlist
                     exclude = lines excludeSrc
                     results = onlyOnce $ h98 ++ concatMap (uncurry (document exclude)) filetext
                 writeFile "hoogle.txt" $ unlines (copyright ++ results)


-- load up the libraries that GHC shows distain for...
loadH98 :: IO [(String, [String])]
loadH98 =  do exist <- doesFileExist "haskell98.txt"
              if exist then
                  do x <- readFile "haskell98.txt"
                     return $ f $ filter (not . null) $ lines x
               else
                  do putStrLn "Warning: could not find haskell98.txt"
                     return []
    where
        f [] = []
        f (x:xs) = (drop 7 x,a) : f b
            where (a,b) = break ("module" `isPrefixOf`) xs


onlyOnce :: [(String, [String])] -> [String] 
onlyOnce xs = concatMap g ordered
    where
        ordered = groupBy eqFst $ sortBy cmpFst items
        items = map f $ groupBy eqSnd $ sortBy cmpSnd $ concatMap (\(a,b) -> map ((,) a) b) xs
        
        eqSnd  (_,a) (_,b) = a == b
        cmpSnd (_,a) (_,b) = a `compare` b
        eqFst  (a,_) (b,_) = a == b
        cmpFst (a,_) (b,_) = a `compare` b
        
        modFst (a,_) (b,_) = length (filter (== '.') a) `compare` length (filter (== '.') b)
        
        f xs = head $ sortBy modFst xs
        g xs@((name,_):_) = ["", "module " ++ name] ++ map snd xs
        
        

{-
doctest x = do let file = "C:/ghc/ghc-6.4/doc/html/libraries/parsec/Text.ParserCombinators.Parsec" ++ x ++ ".html"
               src <- readFile file
               let y = unlines $ document [] file src
               writeFile "result.txt" y
-}


-- the entries to output
document :: [String] -> FilePath -> String -> [(String, [String])]
document exclude file contents = 
        if hide then []
        else if any isSpace name then []
        else [(name, rewrite lexs)]
    where
        hide = any (`isPrefixOf` name) (map init partial) || any (== name) full
        (partial, full) = partition (\x -> last x == '.') exclude
    
        lexs = lexer contents
        name = modName lexs



data Flags = IsDir | IsHtml | IsNone
             deriving (Show, Eq)


docFiles :: FilePath -> IO [FilePath]
docFiles x = do xFlag <- getFlag x
                if xFlag == IsHtml then return [x] else do
                dir <- getDirectoryContents x
                let qdir = map (\y -> x ++ "/" ++ y) (filter (\x -> head x /= '.') dir)
                flags <- mapM getFlag qdir
                let flag_dir = zip flags qdir
                    resdirs = map snd $ filter (\(a,b) -> a == IsDir ) flag_dir
                    reshtml = map snd $ filter (\(a,b) -> a == IsHtml) flag_dir
                children <- mapM docFiles resdirs
                return $ reshtml ++ concat children
    where
        getFlag ('.':_) = return IsNone
        getFlag xs | ".html" `isSuffixOf` xs = return IsHtml
        getFlag x = do y <- doesDirectoryExist x
                       return $ if y then IsDir else IsNone



---- REWRITER

getAttr :: Lexeme -> String -> String
getAttr (Tag _ attr) name =
    case lookup name attr of
        Nothing -> ""
        Just x -> x
        
        
rewrite = concatMap rejoin . bundle . map deForall . extract
        

rebundle = bundle . map tail


deForall :: String -> String
deForall x = g x
    where
        g x | "forall " `isPrefixOf` x = g $ noImp $ tail $ tail $ dropWhile (/= '.') x
        g (x:xs) = x : g xs
        g [] = []
        
        noImp x = f x
            where
                f ('=':'>':' ':xs) = xs
                f (x:xs) = f xs
                f [] = x
        


bundle :: [String] -> [[String]]
bundle (x:xs) = (x:a) : bundle b
    where (a,b) = break (not . isSpace . head) xs
bundle [] = []

        
rejoin :: [String] -> [String]
rejoin [x] = [x]
rejoin (x:xs) | "data " `isPrefixOf` x || "newtype " `isPrefixOf` x = rejoinData (x:xs)
rejoin (x:xs) | "class " `isPrefixOf` trim x = rejoinClass (x:xs)
rejoin (x:xs) = [concat (x:map ((++) " " . tail) xs)]


rejoinData (dat:xs) = nub $ (keyword ++ " " ++ pre) : (concatMap f $ rebundle xs)
    where
        (keyword, _:pre) = break (== ' ') dat
        
        f ("Instances":xs) = map ((++) "instance " . tail) xs
        f ("Constructors":xs) = concatMap g $ rebundle xs
        
        g [x] = [y ++ " :: " ++ concatMap (++ " -> ") ys ++ pre]
            where (y:ys) = chunks x
            
        g (x:xs) = (dechunk $ x : "::" : concatMap h xs ++ [pre]) : concatMap t xs
        
        h x = res
            where
                res = concatMap (++ ["->"]) (replicate (reps+1) typ2)
            
                reps = length $ filter (== ',') names
                (names:_:typ) = chunks x
                typ2 = bracketStrip typ
        
        t x = map res names
            where
                names = splitList "," a
                res name = dechunk $ name : "::" : clls ++ pre : "->" : imp
                
                (a:_:b) = chunks x
                bb = bracketStrip b
                (cls,rest) = break (== "=>") bb
                
                clls = if null rest then [] else cls ++ ["=>"]
                imp = if null rest then cls else tail rest


bracketStrip ['(':xs] | not ('(' `elem` xs) = chunks $ init xs
bracketStrip x = x
                


rejoinClass (dat:xs) = ("class " ++ pre) : (concatMap f $ rebundle xs)
    where
        pre2 = drop 6 $ reverse $ drop 7 $ reverse dat
        pre = if '|' `elem` pre2 then takeWhile (/= '|') pre2 else pre2
        
        cpre = chunks pre
        body = dechunk $
            if "=>" `elem` cpre then tail (dropWhile (/= "=>") cpre) else cpre
        
        f ("Instances":xs) = []
        f ("Methods":xs) = map g $ rebundle xs
        f (x:xs) = error $ "rejoinClass: " ++ x
        
        g [x] = dechunk $ a ++ "::" : cls : "=>" : imp
            where
                (a,b2) = break (== "::") (chunks x)
                b = if null b2 then error pre else tail b2
                (c,d) = break (== "=>") b
                
                cls = if null d then body else concat ["(", body, ", ", nobrackets (dechunk c), ")"]
                imp = if null d then b else tail d
                
                nobrackets ('(':xs) = init xs
                nobrackets x = x
        
        g xs = g [dechunk xs]


dechunk = concat . intersperse " "

-- divide up into lexemes, respecting brackets
chunks :: String -> [String]
chunks x = filter (not . null) $ f "" 0 x
    where
        f a n (',':' ':xs) = f a n (',':xs)
        f a n (x:xs) | x `elem` "[({" = f (x:a) (n+1) xs
                     | x `elem` "])}" = f (x:a) (n-1) xs
                     | isSpace x && n == 0 = reverse a : f "" n xs
                     | otherwise = f (x:a) n xs
        f a n [] = [reverse a]



extract :: [Lexeme] -> [String]
extract xs =
        filter (not . isPrefixOf "module") $ -- remove modules
        dropWhile (isSpace . head) $     -- remove synopsis
        f (-1) xs
    where
        f n (Tag "TABLE" _:xs) = f (n+1) xs
        f n (ShutTag "TABLE":xs) = f (n-1) xs
        f n [] = []
        
        f n (t@(Tag "TD" attr):xs) | att `elem` ["decl","arg","section4"] = g n "" xs
            where att = getAttr t "CLASS"
        f n (_:xs) = f n xs
        
        g n a (Tag "TABLE" _:xs) = h n 1 a xs
        g n a (ShutTag "TD":xs) = (replicate n '\t' ++ trim a) : f n xs
        g n a (Text x:xs) = g n (a ++ x) xs
        g n a (_:xs) = g n a xs
        
        h n m a (Text x:xs) = h n m (a ++ x) xs
        h n m a (ShutTag "TABLE":xs) = if m == 1 then g n a xs else h n (m-1) a xs
        h n m a (Tag "TABLE" _:xs) = h n (m+1) a xs
        h n m a (_:xs) = h n m a xs


deescape ('&':'g':'t':';':xs) = '>' : deescape xs
deescape ('&':'l':'t':';':xs) = '<' : deescape xs
deescape ('&':'a':'m':'p':';':xs) = '&' : deescape xs
deescape (x:xs) = x : deescape xs
deescape [] = []



modName :: [Lexeme] -> String
modName x = a
    where Text a = head $ tail $ dropWhile (isntTag $ Tag "TITLE" []) $ x


-- first one is the pattern, second is the actual
isTag :: Lexeme -> Lexeme -> Bool
isTag (Tag a c) (Tag b d) = eqEmpty a b && all contain c
    where contain (key, val) = Just val == lookup key d

isTag (ShutTag a) (ShutTag b) = eqEmpty a b
isTag (Text a) (Text b) = eqEmpty a b
isTag _ _ = False

isntTag a b = not (isTag a b)

eqEmpty a b = a == "" || a == b