4 | module System.Console.GetOpt
21 | ReturnInOrder (String -> a) |
23 | ReturnInOrder' (String -> Either String a)
26 | Functor ArgOrder where
27 | map _ RequireOrder = RequireOrder
28 | map _ Permute = Permute
29 | map f (ReturnInOrder g) = ReturnInOrder (f . g)
30 | map f (ReturnInOrder' g) = ReturnInOrder' (map f . g)
39 | ReqArg (String -> a) String |
41 | ReqArg' (String -> Either String a) String |
43 | OptArg (Maybe String -> a) String |
45 | OptArg' (Maybe String -> Either String a) String
48 | Functor ArgDescr where
49 | map f (NoArg x) = NoArg (f x)
50 | map f (ReqArg g x) = ReqArg (f . g) x
51 | map f (ReqArg' g x) = ReqArg' (map f . g) x
52 | map f (OptArg g x) = OptArg (f . g) x
53 | map f (OptArg' g x) = OptArg' (map f . g) x
64 | record OptDescr a where
67 | shortNames : List Char
69 | longNames : List String
71 | argDescr : ArgDescr a
73 | description : String
76 | Functor OptDescr where
77 | map f = { argDescr $= map f }
87 | optOrErr : Either String a -> OptKind a
88 | optOrErr = either OptErr Opt
94 | fmtShort : ArgDescr a -> Char -> String
95 | fmtShort (NoArg _ ) so = "-" ++ singleton so
96 | fmtShort (ReqArg _ ad) so = "-" ++ singleton so ++ " " ++ ad
97 | fmtShort (ReqArg' _ ad) so = "-" ++ singleton so ++ " " ++ ad
98 | fmtShort (OptArg _ ad) so = "-" ++ singleton so ++ "[" ++ ad ++ "]"
99 | fmtShort (OptArg' _ ad) so = "-" ++ singleton so ++ "[" ++ ad ++ "]"
101 | fmtLong : ArgDescr a -> String -> String
102 | fmtLong (NoArg _ ) lo = "--" ++ lo
103 | fmtLong (ReqArg _ ad) lo = "--" ++ lo ++ "=" ++ ad
104 | fmtLong (ReqArg' _ ad) lo = "--" ++ lo ++ "=" ++ ad
105 | fmtLong (OptArg _ ad) lo = "--" ++ lo ++ "[=" ++ ad ++ "]"
106 | fmtLong (OptArg' _ ad) lo = "--" ++ lo ++ "[=" ++ ad ++ "]"
108 | fmtOpt : OptDescr a -> List (String,String,String)
109 | fmtOpt (MkOpt sos los ad descr) =
110 | let sosFmt = concat $
intersperse ", " (map (fmtShort ad) sos)
111 | losFmt = concat $
intersperse ", " (map (fmtLong ad) los)
112 | (h ::: t) = lines1 descr in
113 | (sosFmt,losFmt,h) :: map (\s => ("","",s)) t
114 | where lines1 : String -> List1 String
115 | lines1 s = case lines s of
117 | (x :: xs) => x ::: xs
123 | usageInfo : (header : String) -> List (OptDescr a) -> String
124 | usageInfo header optDescr =
125 | let (ss,ls,ds) = (unzip3 . concatMap fmtOpt) optDescr
126 | paste = \x,y,z => " " ++ x ++ " " ++ y ++ " " ++ z
127 | table = zipWith3 paste (sameLen ss) (sameLen ls) ds
128 | in unlines $
header :: table
130 | where flushLeft : Nat -> String -> String
131 | flushLeft n s = s ++ pack (replicate (n `minus` length s) ' ')
133 | sameLen : List String -> List String
134 | sameLen ss = let len = foldl (\n => max n . length) 0 ss
135 | in map (flushLeft len) ss
141 | errAmbig : List (OptDescr a) -> (optStr : String) -> OptKind a
142 | errAmbig ods s = let h = "option `" ++ s ++ "' is ambiguous; could be one of:"
143 | in OptErr (usageInfo h ods)
145 | errReq : String -> (optStr : String) -> OptKind a
146 | errReq d s = OptErr ("option `" ++ s ++ "' requires an argument " ++ d ++ "\n")
148 | errNoArg : (optStr : String) -> OptKind a
149 | errNoArg s = OptErr ("option `" ++ s ++ "' doesn't allow an argument\n")
158 | record Result a where
159 | constructor MkResult
164 | nonOptions : List String
167 | unrecognized : List String
171 | errors : List String
174 | emptyRes : Result a
175 | emptyRes = MkResult [] [] [] []
178 | Functor Result where
179 | map f = { options $= map f }
181 | OptFun : Type -> Type
182 | OptFun a = List String -> List (OptDescr a) -> (OptKind a,List String)
184 | longOpt : String -> OptFun a
185 | longOpt ls rs descs =
186 | let (opt,arg) = break ('=' ==) ls
187 | getWith = \p => filter (any (p opt) . longNames) descs
188 | exact = getWith (==)
189 | options = if null exact then getWith isPrefixOf else exact
190 | ads = map argDescr options
192 | in case (ads, unpack arg, rs) of
193 | (_ :: _ :: _ , _ , r ) => (errAmbig options os, r)
194 | ([NoArg a ], [] , r ) => (Opt a, r)
195 | ([NoArg a ], c :: _ , r ) => (errNoArg os,r)
198 | ([ReqArg _ d], [] , [] ) => (errReq d os,[])
199 | ([ReqArg f _], [] , (r::rest)) => (Opt $
f r,rest)
200 | ([ReqArg f _], c :: xs, r ) => (Opt $
f (pack xs),r)
203 | ([ReqArg' _ d], [] , [] ) => (errReq d os,[])
204 | ([ReqArg' f _], [] , (r::rest)) => (optOrErr $
f r, rest)
205 | ([ReqArg' f _], c :: xs, r ) => (optOrErr $
f (pack xs) ,r)
208 | ([OptArg f _], [] , r ) => (Opt $
f Nothing,r)
209 | ([OptArg f _], c :: xs, r ) => (Opt . f . Just $
pack xs,r)
212 | ([OptArg' f _], [] , r ) => (optOrErr $
f Nothing, r)
213 | ([OptArg' f _], c :: xs, r ) => (optOrErr . f . Just $
pack xs, r)
216 | ([] , _ , r ) => (UnreqOpt $
"--" ++ ls,r)
218 | shortOpt : Char -> String -> OptFun a
219 | shortOpt y ys rs descs =
220 | let options = filter (elem y . shortNames) descs
221 | ads = map argDescr options
223 | os = mkOs (singleton y)
224 | in case (ads,ys,rs) of
225 | (_ :: _ :: _ , _ , r ) => (errAmbig options os, r)
226 | ([NoArg a ], "", r ) => (Opt a,r)
227 | ([NoArg a ], s , r ) => (Opt a, mkOs s :: r)
228 | ([ReqArg _ d], "", [] ) => (errReq d os, [])
229 | ([ReqArg f _], "", (r::rest)) => (Opt $
f r, rest)
230 | ([ReqArg f _], s , r ) => (Opt $
f s, r)
231 | ([ReqArg' _ d], "", [] ) => (errReq d os, [])
232 | ([ReqArg' f _], "", (r::rest)) => (optOrErr $
f r, rest)
233 | ([ReqArg' f _], s , r ) => (optOrErr $
f s, r)
234 | ([OptArg f _], "", r ) => (Opt $
f Nothing, r)
235 | ([OptArg f _], s , r ) => (Opt . f $
Just s, r)
236 | ([OptArg' f _], "", r ) => (optOrErr $
f Nothing, r)
237 | ([OptArg' f _], s , r ) => (optOrErr . f $
Just s, r)
238 | ([] , "", r ) => (UnreqOpt os, r)
239 | ([] , s , r ) => (UnreqOpt os, mkOs s :: r)
243 | getNext : List Char -> OptFun a
244 | getNext ('-'::'-'::[]) r _ = (EndOfOpts,r)
245 | getNext ('-'::'-'::xs) r descs = longOpt (pack xs) r descs
246 | getNext ('-':: x ::xs) r descs = shortOpt x (pack xs) r descs
247 | getNext a r _ = (NonOpt $
pack a,r)
259 | getOpt : ArgOrder a
260 | -> List (OptDescr a)
261 | -> (args : List String)
263 | getOpt _ _ [] = emptyRes
264 | getOpt ordering descs (arg::args) =
265 | let (opt,rest) = getNext (unpack arg) args descs
266 | res = getOpt ordering descs (assert_smaller args rest)
267 | in case (opt,ordering) of
268 | (Opt x, _) => {options $= (x::)} res
269 | (UnreqOpt x, _) => {unrecognized $= (x::)} res
270 | (NonOpt x, RequireOrder) => MkResult [] (x::rest) [] []
271 | (NonOpt x, Permute) => {nonOptions $= (x::)} res
272 | (NonOpt x, ReturnInOrder f) => {options $= (f x::)} res
273 | (NonOpt x, ReturnInOrder' f) => updOptOrErr (f x) res
274 | (EndOfOpts, RequireOrder) => MkResult [] rest [] []
275 | (EndOfOpts, Permute) => MkResult [] rest [] []
276 | (EndOfOpts, ReturnInOrder f) => MkResult (map f rest) [] [] []
277 | (EndOfOpts, ReturnInOrder' f) => parseRest f rest
278 | (OptErr e, _) => {errors $= (e::)} res
280 | updOptOrErr : Either String a -> Result a -> Result a
281 | updOptOrErr (Left e) = {errors $= (e::)}
282 | updOptOrErr (Right o) = {options $= (o::)}
284 | parseRest : (String -> Either String a) -> (rest : List String) -> Result a
286 | these' [] [] (\es, os => MkResult os [] [] es) $
287 | for rest $
fromEither . mapFst pure . f
296 | getOpt' : Applicative f
298 | -> List (OptDescr $
f a)
299 | -> (args : List String)
301 | getOpt' ao os args = do
302 | let MkResult opts nonOpts unrec errs = getOpt ao os args
303 | sequence opts <&> \opts' => MkResult opts' nonOpts unrec errs