-
Notifications
You must be signed in to change notification settings - Fork 139
/
Everythings.hs
124 lines (108 loc) · 5.23 KB
/
Everythings.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import System.Environment ( getArgs )
import System.Directory ( getDirectoryContents )
import System.Exit ( exitFailure )
import Control.Monad ( forM, forM_ )
import Data.Maybe ( listToMaybe, maybeToList, mapMaybe )
import Data.List ( (\\), delete, find, intercalate, sort, stripPrefix )
stripSuffix :: (Eq a) => [a] -> [a] -> Maybe [a]
stripSuffix sfx = fmap reverse . stripPrefix (reverse sfx) . reverse
splitOn :: Char -> String -> [String]
splitOn d = foldr go []
where go :: Char -> [String] -> [String]
go x acc | d == x = []:acc
go x (a:acc) = (x:a):acc
go x [] = [[x]]
type SplitFilePath = [String]
showFP :: Char -> SplitFilePath -> String
showFP c fp = intercalate [c] (reverse fp)
addToFP :: SplitFilePath -> String -> SplitFilePath
addToFP fp dir = dir : fp
-- Given a path to a directory, returns a pair containing the list of all its
-- subdirectories and the list of all agda files it contains
getSubDirsFiles :: SplitFilePath -> IO ([String], [String])
getSubDirsFiles fp = do
ls <- getDirectoryContents ("./" ++ showFP '/' fp)
let sub_dirs = filter ('.' `notElem`) ls
files = mapMaybe (stripSuffix ".agda") ls
pure (sub_dirs, files)
-- Given a path to an agda file, returns the list of all files it imports
getImported :: SplitFilePath -> IO [SplitFilePath]
getImported fp = do
ls <- fmap words . lines <$> readFile ("./" ++ showFP '/' fp ++ ".agda")
pure $ fmap (reverse . splitOn '.') (mapMaybe f ls)
where f :: [String] -> Maybe String
f ("open":"import":x:_) = Just x
f ("import":x:_) = Just x
f _ = Nothing
-- Given a path to a directory $fp and a path to an agda file $fileToCheck.agda,
-- returns the list of all files in* $fp not imported in $fileToCheck.agda
-- * recursively
getMissingFiles :: SplitFilePath -> Maybe SplitFilePath -> IO [SplitFilePath]
getMissingFiles fp fpToCheck = do
(sub_dirs, sub_files) <- getSubDirsFiles fp
-- recursively get all files in $fp/X not imported in $fp/X.agda (if it exists)
missing_files <- concat <$> forM sub_dirs (\sub_dir ->
getMissingFiles (addToFP fp sub_dir)
(addToFP fp <$> find (== sub_dir) sub_files))
-- return all of these files, plus all agda files in the current directory,
-- except for those which are imported in $fpToCheck.agda (if it exists) or
-- which are $fpToCheck.agda itself
imported <- maybe (pure []) getImported fpToCheck
pure $ ((addToFP fp <$> sub_files) ++ missing_files)
\\ (maybeToList fpToCheck ++ imported)
checkEverythings :: [String] -> IO ()
checkEverythings dirs = do
missing_files <- concat <$> forM dirs (\dir ->
getMissingFiles [dir,"Cubical"] (Just ["Everything",dir,"Cubical"]))
if null missing_files then pure ()
else do putStrLn "Found some files which are not imported in any Everything.agda:"
forM_ missing_files (putStrLn . (" " ++) . showFP '.')
exitFailure
checkREADME :: IO ()
checkREADME = do
(sub_dirs, _) <- getSubDirsFiles ["Cubical"]
imported <- getImported ["README","Cubical"]
let missing_files = fmap (\dir -> ["Everything",dir,"Cubical"]) sub_dirs \\ imported
if null missing_files then pure ()
else do putStrLn "Found some Everything.agda's which are not imported in README.agda:"
forM_ missing_files (putStrLn . (" " ++) . showFP '.')
exitFailure
genEverythings :: [String] -> IO ()
genEverythings =
mapM_ $ \ dir -> do
let fp = addToFP ["Cubical"] dir
files <- getMissingFiles fp Nothing
let ls = ["{-# OPTIONS --safe #-}",
"module " ++ showFP '.' (addToFP fp "Everything") ++ " where",[]]
++ sort (fmap (\file -> "import " ++ showFP '.' file)
(delete (addToFP fp "Everything") files))
writeFile ("./" ++ showFP '/' (addToFP fp "Everything") ++ ".agda")
(unlines ls)
helpText :: String
helpText = unlines [
"Accepted arguments: ",
" check d1 d2 ... dn checks imports in the Everything files in the",
" given directories",
" check-except d1 d2 ... dn checks imports in all Everything files except those",
" in the given directories",
" gen d1 d2 ... dn generates Everything files in the given directories",
" gen-except d1 d2 ... dn generates Everything files in all directories",
" except in those given",
" check-README checks all Everything files are imported in README",
" get-imports-README gets the list of all Everything files imported in README"]
main :: IO ()
main = do
all_dirs <- filter ('.' `notElem`) <$> getDirectoryContents "./Cubical"
args <- getArgs
case args of
"check":dirs -> checkEverythings dirs
"gen" :dirs -> genEverythings dirs
"check-except":ex_dirs -> checkEverythings (all_dirs \\ ex_dirs)
"gen-except" :ex_dirs -> genEverythings (all_dirs \\ ex_dirs)
["check-README"] -> checkREADME
["get-imports-README"] -> do
imported <- filter (\fp -> listToMaybe fp == Just "Everything")
<$> getImported ["README","Cubical"]
putStrLn . unwords $ map (\fp -> showFP '/' fp ++ ".agda") imported
"help":_ -> putStrLn helpText
_ -> putStrLn "argument(s) not recognized, try 'help'"