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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
|
module CurryDoc.Main where
import Control.Monad ( unless, when )
import Data.Function
import Data.List
import Data.Maybe ( fromJust )
import System.Environment
import Data.Time
import HTML.Base
import System.Directory
import System.FilePath
import System.Process
import AbstractCurry.Files
import FlatCurry.Types
import FlatCurry.Files
import FlatCurry.Read ( readFlatCurryWithImports )
import Analysis.Deterministic
import Analysis.TotallyDefined
import Analysis.Indeterministic
import Analysis.SolutionCompleteness
import Analysis.Types ( analysisName )
import CASS.Server ( initializeAnalysisSystem, analyzeInterface )
import System.CurryPath ( stripCurrySuffix, lookupModuleSourceInLoadPath
, getLoadPathForModule )
import System.FrontendExec ( FrontendTarget (..), callFrontend )
import CurryDoc.AnaInfo
import CurryDoc.Files ( generateModuleDocMapping )
import CurryDoc.Options
import CurryDoc.Read
import CurryDoc.Html
import CurryDoc.TeX
import CurryDoc.CDoc
import CurryDoc.Config
import CurryDoc.PackageConfig ( packagePath )
banner :: String
banner = unlines [bannerLine,bannerText,bannerLine]
where
bannerText =
"CurryDoc (" ++ currydocVersion ++ ") - the Curry Documentation Tool"
bannerLine = take (length bannerText) (repeat '-')
includeDir :: String
includeDir = packagePath </> "include"
main :: IO ()
main = do
args <- getArgs
putStrLn banner
processArgs defaultCurryDocOptions args
processArgs :: DocOptions -> [String] -> IO ()
processArgs opts args = do
case args of
("--nomarkdown" : margs) -> processArgs opts { withMarkdown = False } margs
("--title" : t : margs) -> processArgs opts { mainTitle = t } margs
("--use" : t : margs) ->
let (src,url) = break (=='@') t
in if null url
then error "URL missing in --use option!"
else processArgs opts { useDirURL = useDirURL opts ++
[(src,tail url)] } margs
("--html" : margs) -> processArgs opts { docType = HtmlDoc } margs
("--tex" : margs) ->
processArgs opts { docType = TexDoc, withIndex = False } margs
("--cdoc" : margs) ->
processArgs opts { docType = CDoc, withIndex = False } margs
["--noindexhtml",docdir,modname] -> do
opts' <- processOpts opts { withIndex = False, docType = HtmlDoc }
makeCompleteDoc opts' True docdir (stripCurrySuffix modname)
("--onlyindexhtml":docdir:modnames) -> do
opts' <- processOpts opts
makeIndexPages opts' docdir (map stripCurrySuffix modnames)
("--libsindexhtml":docdir:modnames) -> do
opts' <- processOpts opts
makeSystemLibsIndex opts' docdir modnames
(('-':_):_) -> printUsageMessage
[modname] -> do
opts' <- processOpts opts
makeCompleteDoc opts' (docType opts == HtmlDoc)
("DOC_" ++ stripCurrySuffix (takeFileName modname))
(stripCurrySuffix modname)
[docdir,modname] -> do
opts' <- processOpts opts
makeCompleteDoc opts' (docType opts == HtmlDoc) docdir
(stripCurrySuffix modname)
_ -> printUsageMessage
processOpts :: DocOptions -> IO DocOptions
processOpts opts = do
modurls <- generateModuleDocMapping (useDirURL opts)
return $ opts { docMods = map fst modurls
, docURL = \m -> maybe m (\b -> b </> m) (lookup m modurls) }
printUsageMessage :: IO ()
printUsageMessage = do
args <- getArgs
putStrLn $ unlines
[ "ERROR: Illegal arguments for CurryDoc: " ++ unwords args
, ""
, "Usage:"
, "curry-doc <options> [--html|--tex|--cdoc] [<doc_dir>] <module>"
, "curry-doc <options> --noindexhtml <doc_dir> <module>"
, "curry-doc <options> --onlyindexhtml <doc_dir> <modules>"
, "curry-doc <options> --libsindexhtml <doc_dir> <modules>"
, ""
, "where <options> can be:"
, " --title s : Title of the main HTML documentation page"
, " --use dir@url: use for all Curry programs in <dir> the documentation"
, " already stored at <url>"
, " --nomarkdown : do not process markdown code in comments"
]
createDir :: String -> IO ()
createDir dir = do
exdir <- doesDirectoryExist dir
unless exdir $ system ("mkdir -p " ++ dir) >> return ()
copyDirectory :: String -> String -> IO ()
copyDirectory src dst = do
retCode <- system $ "cp -pR \"" ++ src ++ "\" \"" ++ dst ++ "\""
when (retCode /= 0) $
error $ "Copy failed with return code " ++ show retCode
makeCompleteDoc :: DocOptions -> Bool -> String -> String -> IO ()
makeCompleteDoc docopts recursive reldocdir modpath = do
docdir <- makeAbsolute reldocdir
prepareDocDir (docType docopts) docdir
lookupModuleSourceInLoadPath modpath >>=
maybe (error $ "Source code of module '" ++ modpath ++ "' not found!")
(\ (moddir,_) -> do
let modname = takeFileName modpath
homeref = ("index.html", [htxt "Program", nbsp, code [htxt modname]])
setCurrentDirectory moddir
callFrontend FCY modname
callFrontend ACY modname
(alltypes,allfuns) <- getProg modname $ docType docopts
makeDocIfNecessary docopts recursive docdir modname
when (withIndex docopts) $ do
genMainIndexPage docopts docdir [modname]
genFunctionIndexPage homeref docopts docdir allfuns
genConsIndexPage homeref docopts docdir alltypes
system ("chmod -R go+rX "++docdir)
putStrLn ("Documentation files written into directory "++docdir) )
where
getProg modname HtmlDoc = readTypesFuncsWithImports modname
getProg modname TexDoc = readTypesFuncsWithImports modname
getProg modname CDoc = do (Prog _ _ types funs _) <- readFlatCurry modname
return (types,funs)
makeAbsolute :: String -> IO String
makeAbsolute f =
if isAbsolute f
then return f
else do curdir <- getCurrentDirectory
return (curdir </> f)
makeIndexPages :: DocOptions -> String -> [String] -> IO ()
makeIndexPages docopts docdir modnames = do
prepareDocDir HtmlDoc docdir
(alltypes,allfuns) <- mapM readTypesFuncs modnames >>= return . unzip
genMainIndexPage docopts docdir modnames
genFunctionIndexPage homeref docopts docdir (concat allfuns)
genConsIndexPage homeref docopts docdir (concat alltypes)
system $ "chmod -R go+rX " ++ docdir
return ()
where
hometitle = if null (mainTitle docopts)
then "Curry Documentation"
else mainTitle docopts
homeref = ("index.html", [htxt hometitle])
readTypesFuncs modname = do
fcyfile <- getFlatCurryFileInLoadPath modname
(Prog _ _ types funs _) <- readFlatCurryFile fcyfile
return (types,funs)
makeSystemLibsIndex :: DocOptions -> String -> [String] -> IO ()
makeSystemLibsIndex docopts docdir modnames = do
makeIndexPages docopts docdir modnames
putStrLn ("Categorizing modules ...")
modInfos <- mapM getModInfo modnames
putStrLn ("Grouping modules by categories ...")
let grpMods = map sortByName $ groupByCategory $ sortByCategory modInfos
cats = sortBy (<=) $ nub $ map fst3 modInfos
genSystemLibsPage docdir cats grpMods
where
fst3 (x,_,_) = x
snd3 (_,y,_) = y
sortByCategory = sortBy ((<=) `on` fst3)
groupByCategory = groupBy ((==) `on` fst3)
sortByName = sortBy ((<=) `on` snd3)
getModInfo :: String -> IO (Category,String,String)
getModInfo modname = do
mmodsrc <- lookupModuleSourceInLoadPath modname
case mmodsrc of
Nothing -> error $ "Source code of module '"++modname++"' not found!"
Just (_,progname) -> do
modcmts <- readModuleComment progname
let (modcmt,catcmts) = splitComment modcmts
category = readCategory $ getCommentType "category" catcmts
return (category,modname,firstPassage modcmt)
prepareDocDir :: DocType -> String -> IO ()
prepareDocDir HtmlDoc docdir = do
createDir docdir
let docstyledir = docdir </> "bt4"
exdir <- doesDirectoryExist docstyledir
unless exdir $ copyDirectory (includeDir </> "bt4") docstyledir
prepareDocDir TexDoc docdir = do
createDir docdir
putStrLn $ "Copy macros into documentation directory '"++docdir++"'..."
copyIncludeIfPresent docdir "currydoc.tex"
prepareDocDir CDoc docdir = do
createDir docdir
putStrLn "Directory was succesfully created"
copyIncludeIfPresent :: String -> String -> IO ()
copyIncludeIfPresent docdir inclfile = do
existIDir <- doesDirectoryExist includeDir
when existIDir $
system (unwords ["cp", includeDir </> inclfile, docdir]) >> return ()
readAnaInfo :: String -> IO AnaInfo
readAnaInfo modname = do
initializeAnalysisSystem
nondet <- analyzeAndCheck nondetAnalysis
complete <- analyzeAndCheck patCompAnalysis
indet <- analyzeAndCheck indetAnalysis
solcomp <- analyzeAndCheck solcompAnalysis
return (AnaInfo (\qn -> nondet qn == NDet) complete indet solcomp)
where
analyzeAndCheck ana =
analyzeInterface ana modname >>= either
(\results ->
return (\qn -> maybe (error $ "No '" ++ analysisName ana ++
"' analysis result for function " ++ show qn)
id
(lookup qn results)))
(\err -> error $ "Analysis error: " ++ err)
makeDoc :: DocOptions -> Bool -> String -> String -> IO ()
makeDoc docopts recursive docdir modname = do
Just (_,progname) <- lookupModuleSourceInLoadPath modname
putStrLn $ "Reading comments from file '" ++ progname ++ "'..."
(modcmts,progcmts) <- readComments progname
putStrLn $ "Reading analysis information for module \"" ++ modname ++ "\"..."
anainfo <- readAnaInfo modname
makeDocWithComments (docType docopts) docopts recursive docdir
anainfo modname modcmts progcmts
makeDocWithComments :: DocType -> DocOptions -> Bool -> String -> AnaInfo
-> String -> String -> [(SourceLine,String)] -> IO ()
HtmlDoc docopts recursive docdir anainfo modname
modcmts progcmts = do
Just (dir,_) <- lookupModuleSourceInLoadPath modname
let acyfile = dir </> abstractCurryFileName modname
exacy <- doesFileExist acyfile
unless exacy $ callFrontend ACY modname
writeOutfile docopts recursive docdir modname
(generateHtmlDocs docopts anainfo modname modcmts progcmts)
translateSource2ColoredHtml docdir modname
writeOutfile docopts { docType = CDoc, withIndex = False
, withMarkdown = False }
False docdir modname
(generateCDoc modname modcmts progcmts anainfo)
makeDocWithComments TexDoc docopts recursive docdir anainfo modname
modcmts progcmts = do
writeOutfile docopts recursive docdir modname
(generateTexDocs docopts anainfo modname modcmts progcmts)
makeDocWithComments CDoc docopts recursive docdir anainfo modname
modcmts progcmts = do
writeOutfile docopts recursive docdir modname
(generateCDoc modname modcmts progcmts anainfo)
makeDocIfNecessary :: DocOptions -> Bool -> String -> String -> IO ()
makeDocIfNecessary docopts recursive docdir modname =
when (modname `notElem` docMods docopts) $ do
let docfile = docdir </> modname ++
(if docType docopts == HtmlDoc then ".html" else ".tex")
docexists <- doesFileExist docfile
if not docexists
then copyOrMakeDoc docopts recursive docdir modname
else do
ctime <- getFlatCurryFileInLoadPath modname >>= getModificationTime
dftime <- getModificationTime docfile
if compareClockTime ctime dftime == GT
then copyOrMakeDoc docopts recursive docdir modname
else when recursive $ do
imports <- getImports modname
mapM_ (makeDocIfNecessary docopts recursive docdir) imports
getImports :: String -> IO [String]
getImports modname = do
mbfintfile <- getLoadPathForModule modname >>=
findFileWithSuffix (flatCurryIntName modname) [""]
(Prog _ imports _ _ _) <- maybe
(getFlatCurryFileInLoadPath modname >>=
readFlatCurryFile)
readFlatCurryFile
mbfintfile
return imports
copyOrMakeDoc :: DocOptions -> Bool -> String -> String -> IO ()
copyOrMakeDoc docopts recursive docdir modname = do
hasCopied <- copyDocIfPossible docopts docdir modname
unless hasCopied $ makeDoc docopts recursive docdir modname
copyDocIfPossible :: DocOptions -> String -> String -> IO Bool
copyDocIfPossible docopts docdir modname =
if docType docopts == TexDoc
then return False
else do
mdir <- lookupModuleSourceInLoadPath modname >>= return . fst . fromJust
let docprogname = mdir </> "CDOC" </> modname
docHtmlFile = docprogname <.> "html"
docexists <- doesFileExist docHtmlFile
if not docexists
then return False
else do
ctime <- getModificationTime (mdir </> flatCurryFileName modname)
htime <- getModificationTime docHtmlFile
if compareClockTime ctime htime == GT
then return False
else do
putStrLn ("Copying doc file from " ++ docHtmlFile)
system ("cp " ++ docHtmlFile ++ ' ':docdir)
system ("cp " ++ docprogname ++ "_curry.html "++docdir)
return True
readTypesFuncsWithImports :: String -> IO ([TypeDecl],[FuncDecl])
readTypesFuncsWithImports modname = do
allprogs <- readFlatCurryWithImports modname
let (ts,fs) = unzip (map (\ (Prog _ _ types funs _) -> (types,funs)) allprogs)
return (concat ts, concat fs)
fileExtension :: DocType -> String
fileExtension HtmlDoc = "html"
fileExtension TexDoc = "tex"
fileExtension CDoc = "cdoc"
writeOutfile :: DocOptions -> Bool -> String -> String -> IO String -> IO ()
writeOutfile docopts recursive docdir modname generate = do
doc <- generate
imports <- getImports modname
let outfile = docdir </> modname <.> fileExtension (docType docopts)
putStrLn ("Writing documentation to \"" ++ outfile ++ "\"...")
writeFile outfile doc
when recursive $
mapM_ (makeDocIfNecessary docopts recursive docdir) imports
|