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