forked from agda/agda-stdlib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
GenerateEverything.hs
101 lines (81 loc) · 2.82 KB
/
GenerateEverything.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
{-# LANGUAGE PatternGuards #-}
import qualified Data.List as List
import Control.Applicative
import System.Environment
import System.IO
import System.Exit
import System.FilePath
import System.FilePath.Find
headerFile = "Header"
outputFile = "Everything.agda"
srcDir = "src"
main = do
args <- getArgs
case args of
[] -> return ()
_ -> hPutStr stderr usage >> exitFailure
header <- readFileUTF8 headerFile
modules <- filter isLibraryModule . List.sort <$>
find always
(extension ==? ".agda" ||? extension ==? ".lagda")
srcDir
headers <- mapM extractHeader modules
writeFileUTF8 outputFile $
header ++ format (zip modules headers)
-- | Usage info.
usage :: String
usage = unlines
[ "GenerateEverything: A utility program for Agda's standard library."
, ""
, "Usage: GenerateEverything"
, ""
, "This program should be run in the base directory of a clean checkout of"
, "the library."
, ""
, "The program generates documentation for the library by extracting"
, "headers from library modules. The output is written to " ++ outputFile
, "with the file " ++ headerFile ++ " inserted verbatim at the beginning."
]
-- | Returns 'True' for all Agda files except for core modules.
isLibraryModule :: FilePath -> Bool
isLibraryModule f =
takeExtension f `elem` [".agda", ".lagda"] &&
dropExtension (takeFileName f) /= "Core"
-- | Reads a module and extracts the header.
extractHeader :: FilePath -> IO [String]
extractHeader mod = fmap (extract . lines) $ readFileUTF8 mod
where
delimiter = all (== '-')
extract (d1 : "-- The Agda standard library" : "--" : ss)
| delimiter d1
, (info, d2 : rest) <- span ("-- " `List.isPrefixOf`) ss
, delimiter d2
= info
extract _ = error $ mod ++ " is malformed."
-- | Formats the extracted module information.
format :: [(FilePath, [String])]
-- ^ Pairs of module names and headers. All lines in the
-- headers are already prefixed with \"-- \".
-> String
format = unlines . concat . map fmt
where
fmt (mod, header) = "" : header ++ ["import " ++ fileToMod mod]
-- | Translates a file name to the corresponding module name. It is
-- assumed that the file name corresponds to an Agda module under
-- 'srcDir'.
fileToMod :: FilePath -> String
fileToMod = map slashToDot . dropExtension . makeRelative srcDir
where
slashToDot c | isPathSeparator c = '.'
| otherwise = c
-- | A variant of 'readFile' which uses the 'utf8' encoding.
readFileUTF8 :: FilePath -> IO String
readFileUTF8 f = do
h <- openFile f ReadMode
hSetEncoding h utf8
hGetContents h
-- | A variant of 'writeFile' which uses the 'utf8' encoding.
writeFileUTF8 :: FilePath -> String -> IO ()
writeFileUTF8 f s = withFile f WriteMode $ \h -> do
hSetEncoding h utf8
hPutStr h s