2Module : Idris.Package.Parser
3Description : `iPKG` file parser and package description information.
6Maintainer : The Idris Community.
8{-# LANGUAGE FlexibleContexts #-}
9module Idris.Package.Parser where
11import Idris.CmdOptions
13import Idris.Options (Opt)
14import Idris.Package.Common
15import Idris.Parser (moduleName)
16import Idris.Parser.Helpers (Parser, Parsing, eol, iName, identifier,
17 identifierWithExtraChars, isEol, lchar,
18 packageName, parseErrorDoc, reserved, runparser,
19 someSpace, stringLiteral)
21import Control.Applicative
22import Control.Monad.State.Strict
23import Data.List (union)
24import qualified Options.Applicative as Opts
25import System.Directory (doesFileExist)
27import System.FilePath (isValid, takeExtension, takeFileName)
28import Text.Megaparsec ((<?>))
29import qualified Text.Megaparsec as P
30import qualified Text.PrettyPrint.ANSI.Leijen as PP
32type PParser = Parser PkgDesc
34parseDesc :: FilePath -> IO PkgDesc
36 unless (takeExtension fp == ".ipkg") $ do
37 putStrLn $ unwords ["The presented iPKG file does not have a '.ipkg' extension:", show fp]
38 exitWith (ExitFailure 1)
39 res <- doesFileExist fp
43 case runparser pPkg defaultPkg fp p of
44 Left err -> fail (show $ PP.plain $ parseErrorDoc err)
47 putStrLn $ unwords [ "The presented iPKG file does not exist:", show fp]
48 exitWith (ExitFailure 1)
50pPkg :: PParser PkgDesc
55 modify $ \st -> st { pkgname = p }
61pPkgName :: PParser PkgName
62pPkgName = (either fail pure . pkgName =<< packageName) <?> "PkgName"
64-- | Parses a filename.
66-- | Treated for now as an identifier or a double-quoted string.
67filename :: Parsing m => m String
69 -- Treat a double-quoted string as a filename to support spaces.
70 -- This also moves away from tying filenames to identifiers, so
71 -- it will also accept hyphens
72 -- (https://github.com/idris-lang/Idris-dev/issues/2721)
73 filename <- stringLiteral
74 -- Through at least version 0.9.19.1, IPKG executable values were
75 -- possibly namespaced identifiers, like foo.bar.baz.
77 case filenameErrorMessage filename of
78 Just errorMessage -> fail errorMessage
79 Nothing -> return filename)
82 -- TODO: Report failing span better! We could lookAhead,
83 -- or do something with DeltaParsing?
84 filenameErrorMessage :: FilePath -> Maybe String
85 filenameErrorMessage path = either Just (const Nothing) $ do
88 checkNoDirectoryComponent path
90 checkThat ok message =
91 if ok then Right () else Left message
94 checkThat (path /= "") "filename must not be empty"
97 checkThat (System.FilePath.isValid path)
98 "filename must contain only valid characters"
100 checkNoDirectoryComponent path =
101 checkThat (path == takeFileName path)
102 "filename must contain no directory component"
104textUntilEol :: Parsing m => m String
105textUntilEol = many (P.satisfy (not . isEol)) <* eol <* someSpace
107clause :: String -> PParser a -> (PkgDesc -> a -> PkgDesc) -> PParser ()
108clause name p f = do value <- reserved name *> lchar '=' *> p <* someSpace
109 modify $ \st -> f st value
111commaSep :: Parsing m => m a -> m [a]
112commaSep p = P.sepBy1 p (lchar ',')
114pOptions :: PParser [Opt]
117 case execArgParserPure (words str) of
118 Opts.Success a -> return a
119 Opts.Failure e -> fail $ fst $ Opts.renderFailure e ""
120 _ -> fail "Unexpected error"
122libIdentifier :: Parsing m => m String
123libIdentifier = identifierWithExtraChars "_'-."
126pClause = clause "executable" filename (\st v -> st { execout = Just v })
127 <|> clause "main" (iName []) (\st v -> st { idris_main = Just v })
128 <|> clause "sourcedir" (identifier <|> stringLiteral) (\st v -> st { sourcedir = v })
129 <|> clause "opts" pOptions (\st v -> st { idris_opts = v ++ idris_opts st })
130 <|> clause "pkgs" (commaSep (pPkgName <* someSpace)) (\st ps ->
131 let pkgs = pureArgParser $ concatMap (\x -> ["-p", show x]) ps
132 in st { pkgdeps = ps `union` pkgdeps st
133 , idris_opts = pkgs ++ idris_opts st })
134 <|> clause "modules" (commaSep moduleName) (\st v -> st { modules = modules st ++ v })
135 <|> clause "libs" (commaSep libIdentifier) (\st v -> st { libdeps = libdeps st ++ v })
136 <|> clause "objs" (commaSep identifier) (\st v -> st { objs = objs st ++ v })
137 <|> clause "makefile" (iName []) (\st v -> st { makefile = Just (show v) })
138 <|> clause "tests" (commaSep (iName [])) (\st v -> st { idris_tests = idris_tests st ++ v })
139 <|> clause "version" textUntilEol (\st v -> st { pkgversion = Just v })
140 <|> clause "readme" textUntilEol (\st v -> st { pkgreadme = Just v })
141 <|> clause "license" textUntilEol (\st v -> st { pkglicense = Just v })
142 <|> clause "homepage" textUntilEol (\st v -> st { pkghomepage = Just v })
143 <|> clause "sourceloc" textUntilEol (\st v -> st { pkgsourceloc = Just v })
144 <|> clause "bugtracker" textUntilEol (\st v -> st { pkgbugtracker = Just v })
145 <|> clause "brief" stringLiteral (\st v -> st { pkgbrief = Just v })
146 <|> clause "author" textUntilEol (\st v -> st { pkgauthor = Just v })
147 <|> clause "maintainer" textUntilEol (\st v -> st { pkgmaintainer = Just v })
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list