2Module : Idris.Package.Parser
3Description : `iPKG` file parser and package description information.
6Maintainer : The Idris Community.
8{-# LANGUAGE CPP, ConstraintKinds, FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}
9module Idris.Package.Parser where
11import Idris.CmdOptions
13import Idris.Package.Common
14import Idris.Parser.Helpers (IdrisInnerParser, MonadicParsing, eol, iName, identifier, isEol,
15 lchar, packageName, parseErrorDoc, reserved,
16 runparser, someSpace')
18import Control.Applicative
19import Control.Monad.State.Strict
20import Data.List (union)
21import System.Directory (doesFileExist)
23import System.FilePath (isValid, takeExtension, takeFileName)
24import Text.Parser.Token (TokenParsing(..), stringLiteral)
25import qualified Text.PrettyPrint.ANSI.Leijen as PP
26import Text.Megaparsec ((<?>))
27import qualified Text.Megaparsec as P
28import qualified Text.Megaparsec.Char as P
30type PParser = StateT PkgDesc IdrisInnerParser
32instance {-# OVERLAPPING #-} TokenParsing PParser where
33 someSpace = someSpace'
36parseDesc :: FilePath -> IO PkgDesc
38 when (not $ takeExtension fp == ".ipkg") $ do
39 putStrLn $ unwords ["The presented iPKG file does not have a '.ipkg' extension:", show fp]
40 exitWith (ExitFailure 1)
41 res <- doesFileExist fp
45 case runparser pPkg defaultPkg fp p of
46 Left err -> fail (show $ PP.plain $ parseErrorDoc err)
49 putStrLn $ unwords [ "The presented iPKG file does not exist:", show fp]
50 exitWith (ExitFailure 1)
52pPkg :: PParser PkgDesc
57 modify $ \st -> st { pkgname = p }
63pPkgName :: PParser PkgName
64pPkgName = (either fail pure . pkgName =<< packageName) <?> "PkgName"
66-- | Parses a filename.
68-- | Treated for now as an identifier or a double-quoted string.
69filename :: (MonadicParsing m) => m String
71 -- Treat a double-quoted string as a filename to support spaces.
72 -- This also moves away from tying filenames to identifiers, so
73 -- it will also accept hyphens
74 -- (https://github.com/idris-lang/Idris-dev/issues/2721)
75 filename <- stringLiteral <* someSpace'
76 -- Through at least version 0.9.19.1, IPKG executable values were
77 -- possibly namespaced identifiers, like foo.bar.baz.
78 <|> show . fst <$> iName []
79 case filenameErrorMessage filename of
80 Just errorMessage -> fail errorMessage
81 Nothing -> return filename)
84 -- TODO: Report failing span better! We could lookAhead,
85 -- or do something with DeltaParsing?
86 filenameErrorMessage :: FilePath -> Maybe String
87 filenameErrorMessage path = either Just (const Nothing) $ do
90 checkNoDirectoryComponent path
92 checkThat ok message =
93 if ok then Right () else Left message
96 checkThat (path /= "") "filename must not be empty"
99 checkThat (System.FilePath.isValid path)
100 "filename must contain only valid characters"
102 checkNoDirectoryComponent path =
103 checkThat (path == takeFileName path)
104 "filename must contain no directory component"
106textUntilEol :: MonadicParsing m => m String
107textUntilEol = many (P.satisfy (not . isEol)) <* eol <* someSpace'
109clause :: String -> PParser a -> (PkgDesc -> a -> PkgDesc) -> PParser ()
110clause name p f = do value <- reserved name *> lchar '=' *> p <* someSpace'
111 modify $ \st -> f st value
113commaSep :: MonadicParsing m => m a -> m [a]
114commaSep p = P.sepBy1 p (lchar ',')
117pClause = clause "executable" filename (\st v -> st { execout = Just v })
118 <|> clause "main" (fst <$> iName []) (\st v -> st { idris_main = Just v })
119 <|> clause "sourcedir" (fst <$> identifier) (\st v -> st { sourcedir = v })
120 <|> clause "opts" (pureArgParser . words <$> stringLiteral) (\st v -> st { idris_opts = v ++ idris_opts st })
121 <|> clause "pkgs" (commaSep (pPkgName <* someSpace')) (\st ps ->
122 let pkgs = pureArgParser $ concatMap (\x -> ["-p", show x]) ps
123 in st { pkgdeps = ps `union` pkgdeps st
124 , idris_opts = pkgs ++ idris_opts st })
125 <|> clause "modules" (commaSep (fst <$> iName [])) (\st v -> st { modules = modules st ++ v })
126 <|> clause "libs" (commaSep (fst <$> identifier)) (\st v -> st { libdeps = libdeps st ++ v })
127 <|> clause "objs" (commaSep (fst <$> identifier)) (\st v -> st { objs = objs st ++ v })
128 <|> clause "makefile" (fst <$> iName []) (\st v -> st { makefile = Just (show v) })
129 <|> clause "tests" (commaSep (fst <$> iName [])) (\st v -> st { idris_tests = idris_tests st ++ v })
130 <|> clause "version" textUntilEol (\st v -> st { pkgversion = Just v })
131 <|> clause "readme" textUntilEol (\st v -> st { pkgreadme = Just v })
132 <|> clause "license" textUntilEol (\st v -> st { pkglicense = Just v })
133 <|> clause "homepage" textUntilEol (\st v -> st { pkghomepage = Just v })
134 <|> clause "sourceloc" textUntilEol (\st v -> st { pkgsourceloc = Just v })
135 <|> clause "bugtracker" textUntilEol (\st v -> st { pkgbugtracker = Just v })
136 <|> clause "brief" stringLiteral (\st v -> st { pkgbrief = Just v })
137 <|> clause "author" textUntilEol (\st v -> st { pkgauthor = Just v })
138 <|> 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