2Module : Idris.Package.Parser
3Description : `iPKG` file parser and package description information.
6Maintainer : The Idris Community.
8{-# LANGUAGE CPP, ConstraintKinds, FlexibleInstances, TypeSynonymInstances #-}
9module Idris.Package.Parser where
11import Idris.CmdOptions
13import Idris.Package.Common
14import Idris.Parser.Helpers (HasLastTokenSpan, IdrisInnerParser, MonadicParsing,
15 eol, getLastTokenSpan, iName, identifier, isEol,
16 lchar, multiLineComment, packageName,
17 parseErrorDoc, reserved, runparser,
18 simpleWhiteSpace, singleLineComment)
20import Control.Applicative
21import Control.Monad.State.Strict
22import Data.List (union)
23import System.Directory (doesFileExist)
25import System.FilePath (isValid, takeExtension, takeFileName)
26import qualified Text.PrettyPrint.ANSI.Leijen as PP
27import Text.Trifecta ((<?>))
28import qualified Text.Trifecta as P
30type PParser = StateT PkgDesc IdrisInnerParser
32instance HasLastTokenSpan PParser where
33 getLastTokenSpan = return Nothing
35#if MIN_VERSION_base(4,9,0)
36instance {-# OVERLAPPING #-} P.DeltaParsing PParser where
39 position = lift P.position
40 {-# INLINE position #-}
41 slicedWith f (StateT m) = StateT $ \s -> P.slicedWith (\(a,s') b -> (f a b, s')) $ m s
42 {-# INLINE slicedWith #-}
45 restOfLine = lift P.restOfLine
46 {-# INLINE restOfLine #-}
49instance {-# OVERLAPPING #-} P.TokenParsing PParser where
50 someSpace = many (simpleWhiteSpace <|> singleLineComment <|> multiLineComment) *> pure ()
53parseDesc :: FilePath -> IO PkgDesc
55 when (not $ takeExtension fp == ".ipkg") $ do
56 putStrLn $ unwords ["The presented iPKG file does not have a '.ipkg' extension:", show fp]
57 exitWith (ExitFailure 1)
58 res <- doesFileExist fp
62 case runparser pPkg defaultPkg fp p of
63 Left err -> fail (show $ PP.plain $ parseErrorDoc err)
66 putStrLn $ unwords [ "The presented iPKG file does not exist:", show fp]
67 exitWith (ExitFailure 1)
69pPkg :: PParser PkgDesc
75 put (st { pkgname = p })
81pPkgName :: PParser PkgName
82pPkgName = (either fail pure . pkgName =<< packageName) <?> "PkgName"
84-- | Parses a filename.
86-- | Treated for now as an identifier or a double-quoted string.
87filename :: (MonadicParsing m, HasLastTokenSpan m) => m String
90 -- Treat a double-quoted string as a filename to support spaces.
91 -- This also moves away from tying filenames to identifiers, so
92 -- it will also accept hyphens
93 -- (https://github.com/idris-lang/Idris-dev/issues/2721)
96 -- Through at least version 0.9.19.1, IPKG executable values were
97 -- possibly namespaced identifiers, like foo.bar.baz.
98 show <$> fst <$> iName []
99 case filenameErrorMessage filename of
100 Just errorMessage -> fail errorMessage
101 Nothing -> return filename)
104 -- TODO: Report failing span better! We could lookAhead,
105 -- or do something with DeltaParsing?
106 filenameErrorMessage :: FilePath -> Maybe String
107 filenameErrorMessage path = either Just (const Nothing) $ do
110 checkNoDirectoryComponent path
112 checkThat ok message =
113 if ok then Right () else Left message
116 checkThat (path /= "") "filename must not be empty"
119 checkThat (System.FilePath.isValid path)
120 "filename must contain only valid characters"
122 checkNoDirectoryComponent path =
123 checkThat (path == takeFileName path)
124 "filename must contain no directory component"
128pClause = do reserved "executable"; lchar '=';
131 put (st { execout = Just exec })
133 <|> do reserved "main"; lchar '=';
134 main <- fst <$> iName []
136 put (st { idris_main = Just main })
138 <|> do reserved "sourcedir"; lchar '=';
139 src <- fst <$> identifier
141 put (st { sourcedir = src })
143 <|> do reserved "opts"; lchar '=';
144 opts <- P.stringLiteral
146 let args = pureArgParser (words opts)
147 put (st { idris_opts = args ++ idris_opts st })
149 <|> do reserved "pkgs"; lchar '=';
150 ps <- P.sepBy1 (pPkgName <* P.someSpace) (lchar ',')
152 let pkgs = pureArgParser $ concatMap (\x -> ["-p", show x]) ps
154 put (st { pkgdeps = ps `union` (pkgdeps st)
155 , idris_opts = pkgs ++ idris_opts st})
157 <|> do reserved "modules"; lchar '=';
158 ms <- P.sepBy1 (fst <$> iName []) (lchar ',')
160 put (st { modules = modules st ++ ms })
162 <|> do reserved "libs"; lchar '=';
163 ls <- P.sepBy1 (fst <$> identifier) (lchar ',')
165 put (st { libdeps = libdeps st ++ ls })
167 <|> do reserved "objs"; lchar '=';
168 ls <- P.sepBy1 (fst <$> identifier) (lchar ',')
170 put (st { objs = objs st ++ ls })
172 <|> do reserved "makefile"; lchar '=';
173 mk <- fst <$> iName []
175 put (st { makefile = Just (show mk) })
177 <|> do reserved "tests"; lchar '=';
178 ts <- P.sepBy1 (fst <$> iName []) (lchar ',')
180 put st { idris_tests = idris_tests st ++ ts }
182 <|> do reserved "version"
184 vStr <- many (P.satisfy (not . isEol))
188 put st {pkgversion = Just vStr}
190 <|> do reserved "readme"
192 rme <- many (P.satisfy (not . isEol))
196 put (st { pkgreadme = Just rme })
198 <|> do reserved "license"
200 lStr <- many (P.satisfy (not . isEol))
203 put st {pkglicense = Just lStr}
205 <|> do reserved "homepage"
207 www <- many (P.satisfy (not . isEol))
211 put st {pkghomepage = Just www}
213 <|> do reserved "sourceloc"
215 srcpage <- many (P.satisfy (not . isEol))
219 put st {pkgsourceloc = Just srcpage}
221 <|> do reserved "bugtracker"
223 src <- many (P.satisfy (not . isEol))
227 put st {pkgbugtracker = Just src}
229 <|> do reserved "brief"
231 brief <- P.stringLiteral
234 put st {pkgbrief = Just brief}
236 <|> do reserved "author"; lchar '=';
237 author <- many (P.satisfy (not . isEol))
241 put st {pkgauthor = Just author}
243 <|> do reserved "maintainer"; lchar '=';
244 maintainer <- many (P.satisfy (not . isEol))
248 put st {pkgmaintainer = Just maintainer}
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