@@ -1,75 +1,140 @@
1−
1+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
2module Main where
2module Main where
4−import SourceSyntax.Declaration (Declaration(Datatype))
4+import System.Console.CmdArgs
5import Control.Applicative ((<$>), (<*>))
8import Control.Applicative ((<$>), (<*>))
6−import Data.List (intercalate)
9+import Data.Aeson
7−import Parse.Type
10+import qualified Data.List as List
11+import qualified Data.Map as Map
12+import qualified Data.ByteString.Lazy as BS
14+import SourceSyntax.Helpers (isSymbol)
15+import SourceSyntax.Declaration (Declaration(..), Assoc(..))
16+import SourceSyntax.Expression (Def(..))
18+import Text.Parsec hiding (newline,spaces)
19+import Parse.Declaration (alias,datatype,infixDecl)
20+import Parse.Expression (typeAnnotation)
8import Parse.Helpers
21import Parse.Helpers
9import Parse.Module (moduleDef)
22import Parse.Module (moduleDef)
10−import Text.Parsec hiding (newline,spaces)
23+
11−import System.Environment
24+data Flags = Flags
12−import System.Exit
25+ { files :: [FilePath] }
26+ deriving (Data,Typeable,Show,Eq)
29+ { files = def &= args &= typ "FILES"
30+ } &= help "Generate documentation for Elm"
31+ &= summary ("Generate documentation for Elm, (c) Evan Czaplicki")
15− srcs <- mapM readFile =<< getArgs
34+ flags <- cmdArgs defaultFlags
16− case mapM docParse srcs of
35+ mapM parseFile (files flags)
38+ source <- readFile path
39+ case iParse docs "" source of
41+ putStrLn $ "Documenting " ++ path
42+ BS.writeFile ("docs" </> replaceExtension path ".json") (encode json)
44+ putStrLn $ "Parse error in " ++ path ++ " at " ++ show err
17− Left err -> putStrLn err >> exitFailure
45+ exitFailure
18− Right ms -> putStrLn (toModules ms)
46+
19−
47+docs :: IParser Value
20−toModules ms = wrap (intercalate ",\n " (map toModule ms))
48+docs = do
21− where wrap s = "{ \"modules\" : [\n " ++ s ++ "\n ]\n}"
23−toModule (name, values) =
24− "{ \"name\" : " ++ show name ++ ",\n " ++
25− "\"values\" : [\n " ++ vs ++ "\n ]\n }"
26− where vs = intercalate ",\n " (map toValue values)
28−toValue (name, tipe, desc) =
29− "{ \"name\" : " ++ show name ++
30− ",\n \"type\" : " ++ show tipe ++
31− ",\n \"desc\" : " ++ show desc ++ "\n }"
33−docParse :: String -> Either String (String, [(String, String, String)])
34−docParse = setupParser $ do
35 optional freshLine
49 optional freshLine
36 (names, exports) <- option (["Main"],[]) moduleDef
50 (names, exports) <- option (["Main"],[]) moduleDef
37− info <- many (try (annotation exports) <|> try skip <|> end)
51+ chompUntilFreshLine
52+ structure <- docComment
38− return (intercalate "." names, concat info)
54+ return $ toJson (List.intercalate "." names) exports structure things
56+docComment :: IParser String
59+ contents <- closeComment
60+ return (init (init contents))
62+document :: IParser [(String, Declaration t v, String)]
40− skip = manyTill anyChar simpleNewline >> return []
65+ go things = do
41− end = many1 anyChar >> return []
66+ thing <- optionMaybe docThing
42−
67+ let things' = case thing of
43−annotation :: [String] -> IParser [(String, String, String)]
68+ Nothing -> things
44−annotation exports =
69+ Just t -> things ++ [t]
45− do description <- comment
70+ done <- chompUntilFreshLine
46− extras <- option [] (lookAhead adt)
71+ case done of
47− varName <- try typeDef <|> name
72+ True -> return things'
48− varType <- tipe
73+ False -> go things'
49− return $ extras ++ export varName varType description
51− comment = concatMap clip <$> many lineComment
52− clip str = case str of { ' ':rest -> rest ; _ -> str } ++ "\n"
54− name = do v <- lowVar <|> parens symOp
75+-- returns whether the end of file has been reached
55− whitespace ; hasType ; whitespace ; return v
76+chompUntilFreshLine :: IParser Bool
79+ [ try (simpleNewline >> notFollowedBy (string " ")) >> return False
80+ , eof >> return True ]
57− tipe = manyTill anyChar (try (simpleNewline >> notFollowedBy (string " ")))
82+docThing :: IParser (String, Declaration t v, String)
83+docThing = uncommented <|> commented
90+ comment <- option "" docComment
92+ (src,def) <- withSource $ choice [ alias, datatype, Definition <$> typeAnnotation ]
93+ return (comment, def, src)
96+toJson name exports structure things =
97+ object $ [ "name" .= name
98+ , "document" .= structure
99+ , "values" .= toList values
100+ , "aliases" .= toList aliases
101+ , "datatypes" .= toList adts
104+ (values, aliases, adts) = collect Map.empty Map.empty Map.empty Map.empty things
106+ toList dict = map object . Map.elems $ filterPublics dict
59− export name tipe desc =
108+ exportMap = Map.fromList (zip exports exports)
60− if null exports || name `elem` exports then [(name,tipe,desc)] else []
109+ filterPublics dict =
110+ case Map.null exportMap of
112+ False -> Map.filterWithKey (\k _ -> Map.member k exportMap) dict
62− typeDef = lookAhead ((string "data" <|> string "type") >> whitespace >> capVar)
114+collect infixes types aliases adts things =
116+ [] -> (Map.union customOps nonCustomOps, aliases, adts)
118+ nonCustomOps = Map.mapWithKey addDefaultInfix $ Map.difference types infixes
119+ addDefaultInfix name pairs
120+ | all isSymbol name = addInfix (L, 9 :: Int) pairs
64− adt = do undefined
123+ customOps = Map.intersectionWith addInfix infixes types
65−{-- TODO: reimplement this
124+ addInfix (assoc,prec) pairs =
66− (Datatype name tvs constructors) <- datatype
125+ [ "associativity" .= show assoc, "precedence" .= prec ] ++ pairs
67− let toType (cName, typeArgs) =
68− (cName, show $ foldr (==>) (kADT name $ map kVarT tvs) typeArgs, "")
69− return $ map toType constructors
72−setupParser p source =
127+ (comment, decl, source) : rest ->
73− case iParse p "" source of
128+ case decl of
74− Right result -> Right result
129+ Fixity assoc prec name ->
75− Left err -> Left $ "Parse error at " ++ show err
130+ collect (Map.insert name (assoc,prec) infixes) types aliases adts rest
131+ Definition (TypeAnnotation name tipe) ->
132+ collect infixes (insert name [] types) aliases adts rest
133+ TypeAlias name vars tipe ->
134+ collect infixes types (insert name ["vars" .= vars] aliases) adts rest
135+ Datatype name vars ctors ->
136+ collect infixes types aliases (insert name ["vars" .= vars] adts) rest
138+ insert name fields dict = Map.insert name (obj name fields) dict
140+ [ "name" .= name, "raw" .= source, "comment" .= comment ] ++ fields