1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DataKinds #-}
3 {-# LANGUAGE DefaultSignatures #-}
4 {-# LANGUAGE GADTs #-} -- for Router
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE InstanceSigs #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE Rank2Types #-} -- for hoistParserPerm (which is no longer used)
9 module Symantic.CLI.Parser where
11 import Control.Applicative (Applicative(..), Alternative(..), optional, many, some)
12 import Control.Monad (Monad(..), join, sequence, forM_, void)
13 import Control.Monad.Trans.Class (MonadTrans(..))
14 import Control.Monad.Trans.State (StateT(..),evalState,get,put)
16 import Data.Char (Char)
17 import Data.Either (Either(..))
18 import Data.Eq (Eq(..))
19 import Data.Foldable (null, toList)
20 import Data.Function (($), (.), id, const)
21 import Data.Functor (Functor(..), (<$>), ($>))
22 import Data.Functor.Identity (Identity(..))
24 import Data.List.NonEmpty (NonEmpty(..))
25 import Data.Map.Strict (Map)
26 import Data.Maybe (Maybe(..), maybe, isNothing)
27 import Data.Ord (Ord(..))
28 import Data.Proxy (Proxy(..))
29 import Data.Semigroup (Semigroup(..))
30 import Data.String (String)
31 import Numeric.Natural (Natural)
32 import Prelude (Integer, Num(..), error)
33 import System.Environment (lookupEnv)
35 import Text.Read (Read, readEither)
36 import Text.Show (Show(..), ShowS, showString, showParen)
37 import Type.Reflection as Reflection
38 import qualified Data.ByteString as BS
39 import qualified Data.ByteString.Lazy as BSL
40 import qualified Data.List as List
41 import qualified Data.List.NonEmpty as NonEmpty
42 import qualified Data.Map.Merge.Strict as Map
43 import qualified Data.Map.Strict as Map
44 import qualified Data.Set as Set
45 import qualified Data.Text as Text
46 import qualified Data.Text.IO as Text
47 import qualified Data.Text.Lazy as TL
48 import qualified Data.Text.Lazy.Builder as TLB
49 import qualified Data.Text.Lazy.IO as TL
50 import qualified Symantic.Document as Doc
51 import qualified System.IO as IO
52 import qualified Text.Megaparsec as P
54 import Symantic.CLI.API
57 newtype Parser e d f k = Parser
58 { unParser :: P.ParsecT e [Arg] IO (f->k) -- Reader f k
62 P.ShowErrorComponent e =>
63 Router (Parser e d) handlers (Response (Router (Parser e d))) ->
66 parser api handlers args = do
68 (unParser $ unTrans $ router api)
71 forM_ (P.bundleErrors err) $ \e -> do
73 "Error parsing the command at argument #" <>
74 show (P.errorOffset e + 1) <> ":\n" <>
75 parseErrorTextPretty e
76 Right app -> unResponseParser $ app handlers
78 -- | Rewrite 'P.parseErrorTextPretty' to keep 'Ord' of 'Arg'.
79 parseErrorTextPretty ::
81 (P.Stream s, P.ShowErrorComponent e) =>
82 P.ParseError s e -> String
83 parseErrorTextPretty (P.TrivialError _ us ps) =
84 if isNothing us && Set.null ps
85 then "unknown parse error\n"
87 messageItemsPretty "unexpected "
88 (showErrorItem pxy <$> Set.toAscList (maybe Set.empty Set.singleton us)) <>
89 messageItemsPretty "expecting "
90 (showErrorItem pxy <$> Set.toAscList ps)
91 where pxy = Proxy :: Proxy s
92 parseErrorTextPretty err = P.parseErrorTextPretty err
94 messageItemsPretty :: String -> [String] -> String
95 messageItemsPretty prefix ts
97 | otherwise = prefix <> (orList . NonEmpty.fromList) ts <> "\n"
99 orList :: NonEmpty String -> String
101 orList (x:|[y]) = x <> " or " <> y
102 orList xs = List.intercalate ", " (NonEmpty.init xs) <> ", or " <> NonEmpty.last xs
104 showErrorItem :: P.Stream s => Proxy s -> P.ErrorItem (P.Token s) -> String
105 showErrorItem pxy = \case
106 P.Tokens ts -> P.showTokens pxy ts
107 P.Label label -> NonEmpty.toList label
108 P.EndOfInput -> "end of input"
110 instance Functor (Parser e d f) where
111 a2b`fmap`Parser x = Parser $ (a2b <$>) <$> x
112 instance Applicative (Parser e d f) where
113 pure = Parser . pure . const
114 Parser f <*> Parser x = Parser $ (<*>) <$> f <*> x
115 instance Ord e => Alternative (Parser e d f) where
117 Parser x <|> Parser y = Parser $ x <|> y
118 instance Ord e => Permutable (Parser e d) where
119 type Permutation (Parser e d) = ParserPerm e d (Parser e d)
120 runPermutation (ParserPerm ma p) = Parser $ do
121 u2p <- unParser $ optional p
124 Just perm -> runPermutation perm
127 (Parser $ P.token (const Nothing) Set.empty)
128 -- NOTE: not 'empty' so that 'P.TrivialError' has the unexpected token.
130 toPermutation (Parser x) =
132 (Parser $ (\a () -> ParserPerm (Just a) empty) <$> x)
133 toPermDefault a (Parser x) =
134 ParserPerm (Just ($ a))
135 (Parser $ (\d () -> ParserPerm (Just d) empty) <$> x)
136 instance App (Parser e d) where
137 Parser x <.> Parser y = Parser $
138 x >>= \a2b -> (. a2b) <$> y
139 instance Ord e => Alt (Parser e d) where
140 Parser x <!> Parser y = Parser $
141 (\a2k (a:!:_b) -> a2k a) <$> P.try x <|>
142 (\b2k (_a:!:b) -> b2k b) <$> y
143 Parser x `alt` Parser y = Parser $ P.try x <|> y
144 opt (Parser x) = Parser $
145 mapCont Just <$> P.try x
146 instance Ord e => AltApp (Parser e d) where
147 many0 (Parser x) = Parser $ concatCont <$> many x
148 many1 (Parser x) = Parser $ concatCont <$> some x
149 instance Pro (Parser e d) where
150 dimap a2b _b2a (Parser r) = Parser $ (\k b2k -> k (b2k . a2b)) <$> r
151 instance Ord e => CLI_Command (Parser e d) where
152 -- type CommandConstraint (Parser e d) a = ()
154 command n x = commands Map.empty (Map.singleton n x)
155 instance Ord e => CLI_Tag (Parser e d) where
156 type TagConstraint (Parser e d) a = ()
157 tagged name p = Parser $ P.try $ do
158 void $ (`P.token` exp) $ \tok ->
159 if lookupTag tok name
166 TagShort t -> Set.singleton $ P.Tokens $ pure $ ArgTagShort t
167 TagLong t -> Set.singleton $ P.Tokens $ pure $ ArgTagLong t
168 Tag s l -> Set.fromList
169 [ P.Tokens $ pure $ ArgTagShort s
170 , P.Tokens $ pure $ ArgTagLong l
172 lookupTag (ArgTagShort x) (TagShort y) = x == y
173 lookupTag (ArgTagShort x) (Tag y _) = x == y
174 lookupTag (ArgTagLong x) (TagLong y) = x == y
175 lookupTag (ArgTagLong x) (Tag _ y) = x == y
176 lookupTag _ _ = False
177 endOpts = Parser $ do
178 (`P.token` exp) $ \case
179 ArgTagLong "" -> Just id
182 exp = Set.singleton $ P.Tokens $ pure $ ArgTagLong ""
183 instance Ord e => CLI_Var (Parser e d) where
184 type VarConstraint (Parser e d) a = (IOType a, FromSegment a)
185 var' :: forall a k. VarConstraint (Parser e d) a => Name -> Parser e d (a->k) k
186 var' name = Parser $ do
187 seg <- (`P.token` expName) $ \case
188 ArgSegment seg -> Just seg
190 lift (fromSegment seg) >>= \case
191 Left err -> P.failure got expType
193 got = Just $ P.Tokens $ pure $ ArgSegment seg
194 expType = Set.singleton $ P.Label $ NonEmpty.fromList $
195 "<"<>name<>"> to be of type "<>ioType @a
197 "Prelude.read: no parse" -> ""
200 Right a -> return ($ a)
202 expName = Set.singleton $ P.Label $ NonEmpty.fromList $ "<"<>name<>">"
203 just a = Parser $ return ($ a)
204 nothing = Parser $ return id
205 instance Ord e => CLI_Env (Parser e d) where
206 type EnvConstraint (Parser e d) a = (IOType a, FromSegment a)
207 env' :: forall a k. EnvConstraint (Parser e d) a => Name -> Parser e d (a->k) k
209 lift (lookupEnv name) >>= \case
210 Nothing -> P.failure got exp
213 exp = Set.singleton $ P.Label $ NonEmpty.fromList $ "${"<>name<>"}"
215 lift (fromSegment val) >>= \case
216 Right a -> return ($ a)
217 Left err -> P.failure got exp
219 got = Just $ P.Tokens $ pure $ ArgEnv name val
220 exp = Set.singleton $ P.Label $ NonEmpty.fromList $
221 "${"<>name<>"} to be of type "<>ioType @a
223 "Prelude.read: no parse" -> ""
226 instance Ord e => CLI_Response (Parser e d) where
227 type ResponseConstraint (Parser e d) a = Outputable a
228 type ResponseArgs (Parser e d) a = ParserResponseArgs a
229 type Response (Parser e d) = ParserResponse
231 P.eof $> \({-ParserResponseArgs-} io) ->
232 ParserResponse $ io >>= output
233 instance Ord e => CLI_Help (Parser e d) where
234 type HelpConstraint (Parser e d) d' = d ~ d'
236 program n = Parser . P.label n . unParser
237 rule n = Parser . P.label n . unParser
239 concatCont :: [(a->k)->k] -> ([a]->k)->k
240 concatCont = List.foldr (consCont (:)) ($ [])
242 consCont :: (a->b->c) -> ((a->k)->k) -> ((b->k)->k) -> (c->k)->k
243 consCont ab2c a2k2k b2k2k = \c2k -> a2k2k $ \a -> b2k2k $ \b -> c2k (ab2c a b)
245 mapCont :: (a->b) -> ((a->k)->k) -> ((b->k)->k)
246 mapCont a2b a2k2k = \b2k -> a2k2k (b2k . a2b)
248 -- ** Type 'ParserResponse'
249 newtype ParserResponse = ParserResponse { unResponseParser :: IO () }
250 -- ** Type 'ParserResponseArgs'
251 type ParserResponseArgs = IO
253 -- * Class 'Outputable'
254 -- | Output of a CLI.
255 class IOType a => Outputable a where
257 default output :: Show a => a -> IO ()
260 instance Outputable () where
262 instance Outputable Bool
263 instance Outputable Int
264 instance Outputable Integer
265 instance Outputable Natural
266 instance Outputable Char where
267 output c = IO.putStr [c]
268 instance Outputable String where
270 instance Outputable Text.Text where
272 instance Outputable TL.Text where
274 instance Outputable BS.ByteString where
276 instance Outputable BSL.ByteString where
278 instance Outputable (Doc.Plain TLB.Builder) where
284 -- ** Type 'OnHandle'
285 data OnHandle a = OnHandle IO.Handle a
286 instance IOType a => IOType (OnHandle a) where
288 instance Outputable (OnHandle ()) where
290 instance Outputable (OnHandle Bool) where
291 output (OnHandle h a) = IO.hPrint h a
292 instance Outputable (OnHandle Int) where
293 output (OnHandle h a) = IO.hPrint h a
294 instance Outputable (OnHandle Integer) where
295 output (OnHandle h a) = IO.hPrint h a
296 instance Outputable (OnHandle Natural) where
297 output (OnHandle h a) = IO.hPrint h a
298 instance Outputable (OnHandle Char) where
299 output (OnHandle h c) = IO.hPutStr h [c]
300 instance Outputable (OnHandle String) where
301 output (OnHandle h a) = IO.hPutStr h a
302 instance Outputable (OnHandle Text.Text) where
303 output (OnHandle h a) = Text.hPutStr h a
304 instance Outputable (OnHandle TL.Text) where
305 output (OnHandle h a) = TL.hPutStr h a
306 instance Outputable (OnHandle BS.ByteString) where
307 output (OnHandle h a) = BS.hPutStr h a
308 instance Outputable (OnHandle BSL.ByteString) where
309 output (OnHandle h a) = BSL.hPutStr h a
310 instance Outputable (OnHandle (Doc.Plain TLB.Builder)) where
311 output (OnHandle h d) =
317 , Reflection.Typeable a
318 ) => Outputable (Maybe a) where
323 ( Reflection.Typeable e
324 , Reflection.Typeable a
325 , Outputable (OnHandle e)
327 ) => Outputable (Either e a) where
329 Left e -> output $ OnHandle IO.stderr e
333 -- | Like a MIME type but for input/output of a CLI.
336 default ioType :: Reflection.Typeable a => String
337 ioType = show (Reflection.typeRep @a)
343 instance IOType Integer
344 instance IOType Natural
345 instance IOType String
346 instance IOType Text.Text
347 instance IOType TL.Text
348 instance IOType BS.ByteString
349 instance IOType BSL.ByteString
350 instance IOType (Doc.Plain TLB.Builder)
351 instance Reflection.Typeable a => IOType (Maybe a)
352 instance (Reflection.Typeable e, Reflection.Typeable a) => IOType (Either e a)
354 -- * Class 'FromSegment'
355 class FromSegment a where
356 fromSegment :: Segment -> IO (Either String a)
357 default fromSegment :: Read a => Segment -> IO (Either String a)
358 fromSegment = return . readEither
359 instance FromSegment String where
360 fromSegment = return . Right
361 instance FromSegment Text.Text where
362 fromSegment = return . Right . Text.pack
363 instance FromSegment TL.Text where
364 fromSegment = return . Right . TL.pack
365 instance FromSegment Bool
366 instance FromSegment Int
367 instance FromSegment Integer
368 instance FromSegment Natural
370 -- ** Type 'ParserPerm'
371 data ParserPerm e d repr k a = ParserPerm
372 { permutation_result :: !(Maybe ((a->k)->k))
373 , permutation_parser :: repr () (ParserPerm e d repr k a)
376 instance (App repr, Functor (repr ())) => Functor (ParserPerm e d repr k) where
377 a2b `fmap` ParserPerm a ma = ParserPerm
378 ((\a2k2k b2k -> a2k2k $ b2k . a2b) <$> a)
379 ((a2b `fmap`) `fmap` ma)
380 instance (App repr, Functor (repr ()), Alternative (repr ())) =>
381 Applicative (ParserPerm e d repr k) where
382 pure a = ParserPerm (Just ($ a)) empty
383 lhs@(ParserPerm f ma2b) <*> rhs@(ParserPerm x ma) =
384 ParserPerm a (lhsAlt <|> rhsAlt)
387 (\a2b2k2k a2k2k -> \b2k ->
388 a2b2k2k $ \a2b -> a2k2k (b2k . a2b)
390 lhsAlt = (<*> rhs) <$> ma2b
391 rhsAlt = (lhs <*>) <$> ma
392 instance CLI_Help repr => CLI_Help (ParserPerm e d repr) where
393 type HelpConstraint (ParserPerm e d repr) d' = HelpConstraint (Parser e d) d'
399 Functor (UnTrans repr ()) =>
400 ParserPerm e d (UnTrans repr) k a -> ParserPerm e d repr k a
401 noTransParserPerm (ParserPerm a ma) = ParserPerm a (noTrans $ noTransParserPerm <$> ma)
405 Functor (UnTrans repr ()) =>
406 ParserPerm e d repr k a -> ParserPerm e d (UnTrans repr) k a
407 unTransParserPerm (ParserPerm a ma) = ParserPerm a (unTransParserPerm <$> unTrans ma)
411 (forall a b. repr a b -> repr a b) ->
412 ParserPerm e d repr k c -> ParserPerm e d repr k c
413 hoistParserPerm f (ParserPerm a ma) =
414 ParserPerm a (hoistParserPerm f <$> f ma)
416 -- ** Class 'CLI_Routing'
417 class CLI_Routing repr where
418 commands :: Map Name (repr a k) -> Map Name (repr a k) -> repr a k
419 -- taggeds :: TagConstraint repr a => Map (Either Char Name) (repr (a -> k) k) -> repr (a -> k) k
420 instance Ord e => CLI_Routing (Parser e d) where
421 commands preCmds cmds = Parser $
422 P.token check exp >>= unParser
424 exp = Set.fromList $ P.Tokens . pure . ArgSegment <$> Map.keys cmds
427 Map.lookup cmd cmds <|>
428 Map.lookup cmd preCmds
432 data Router repr a b where
433 -- | Lift any @(repr)@ into 'Router', those not useful to segregate
434 -- wrt. the 'Trans'formation performed, aka. 'noTrans'.
435 Router_Any :: repr a b -> Router repr a b
436 -- | Represent 'commands'.
438 Map Name (Router repr a k) ->
439 Map Name (Router repr a k) ->
441 -- | Represent 'tagged'.
442 Router_Tagged :: Tag -> Router repr f k -> Router repr f k
443 -- | Represent ('<.>').
444 Router_App :: Router repr a b -> Router repr b c -> Router repr a c
445 -- | Represent ('<!>').
446 Router_Alt :: Router repr a k -> Router repr b k -> Router repr (a:!:b) k
447 -- | Unify 'Router's which have different 'handlers'.
448 -- Useful to put alternative 'Router's in a 'Map' as in 'Router_Commands'.
449 Router_Union :: (b->a) -> Router repr a k -> Router repr b k
451 instance Ord e => Functor (Router (Parser e d) f) where
452 a2b`fmap`x = noTrans (a2b <$> unTrans x)
453 instance Ord e => Applicative (Router (Parser e d) f) where
454 pure = noTrans . pure
455 f <*> x = noTrans (unTrans f <*> unTrans x)
456 instance Ord e => Alternative (Router (Parser e d) f) where
457 empty = noTrans empty
458 f <|> x = noTrans (unTrans f <|> unTrans x)
459 instance Ord e => Permutable (Router (Parser e d)) where
460 type Permutation (Router (Parser e d)) = ParserPerm e d (Router (Parser e d))
461 runPermutation = noTrans . runPermutation . unTransParserPerm
462 toPermutation = noTransParserPerm . toPermutation . unTrans
463 toPermDefault a = noTransParserPerm . toPermDefault a . unTrans
464 instance (repr ~ Parser e d) => Show (Router repr a b) where
466 Router_Any{} -> showString "X"
467 Router_Commands _preCmds cmds -> showParen (p>=10) $ showString "Commands [" . go (Map.toList cmds) . showString "]"
469 go :: forall h k. [(Segment, Router repr h k)] -> ShowS
472 (showParen True $ showString (show n<>", ") . showsPrec 0 r) .
475 _ -> showString ", " . go xs
476 Router_Tagged n x -> showsPrec 10 n . showString " " . showsPrec p x
477 Router_App x y -> showParen (p>=4) $ showsPrec 4 x . showString " <.> " . showsPrec 4 y
478 Router_Alt x y -> showParen (p>=3) $ showsPrec 3 x . showString " <!> " . showsPrec 3 y
479 Router_Union _u x -> showString "Union [" . showsPrec 0 x . showString "]"
481 instance Ord e => Trans (Router (Parser e d)) where
482 type UnTrans (Router (Parser e d)) = Parser e d
484 unTrans (Router_Any x) = x
485 unTrans (Router_Alt x y) = unTrans x <!> unTrans y
486 unTrans (Router_App x y) = unTrans x <.> unTrans y
487 unTrans (Router_Commands preCmds cmds) = commands (unTrans <$> preCmds) (unTrans <$> cmds)
488 unTrans (Router_Tagged n x) = tagged n (unTrans x)
489 unTrans (Router_Union u x) = Parser $ (. u) <$> unParser (unTrans x)
491 instance Ord e => App (Router (Parser e d)) where
493 instance Ord e => Alt (Router (Parser e d)) where
495 alt x y = Router_Union (\a -> a:!:a) $ Router_Alt x y
496 instance Ord e => Pro (Router (Parser e d))
497 instance (repr ~ (Parser e d)) => CLI_Command (Router repr) where
500 let is = List.tail $ List.inits n in
501 let (preCmds, cmds) = List.splitAt (List.length is - 1) is in
503 (Map.fromAscList $ (,x) <$> preCmds)
504 (Map.fromAscList $ (,x) <$> cmds)
505 instance Ord e => CLI_Var (Router (Parser e d))
506 instance Ord e => CLI_Env (Router (Parser e d))
507 instance Ord e => CLI_Tag (Router (Parser e d)) where
508 tagged = Router_Tagged
509 instance CLI_Help (Router (Parser e d)) where
510 -- NOTE: set manually (instead of the 'Trans' default 'Router_Any')
511 -- to remove them all, since they are useless for 'Parser'
512 -- and may prevent patterns to be matched in 'router'.
516 instance Ord e => CLI_Response (Router (Parser e d))
517 instance Ord e => CLI_Routing (Router (Parser e d)) where
518 -- taggeds = Router_Taggeds
519 commands = Router_Commands
523 Router repr a b -> Router repr a b
524 router = {-debug1 "router" $-} \case
526 Router_Tagged n x -> Router_Tagged n (router x)
527 Router_Alt x y -> router x`router_Alt`router y
528 Router_Commands preCmds cmds ->
535 -- Associate to the right
536 Router_App (router x) $
537 Router_App (router y) (router z)
538 _ -> router xy `Router_App` router z
539 Router_Union u x -> Router_Union u (router x)
541 -- | Merge/reorder alternatives if possible or default to a 'Router_Alt'.
546 Router repr (a:!:b) k
547 router_Alt = {-debug2 "router_Alt"-} go
549 -- Merge alternative commands together.
550 go (Router_Commands xp xs) (Router_Commands yp ys) =
552 (router_Commands False xp yp) -- NOTE: conflicting prefixes are dropped.
553 (router_Commands True xs ys)
555 -- Merge left first or right first, depending on which removes 'Router_Alt'.
556 go x (y`Router_Alt`z) =
557 case x`router_Alt`y of
559 case y'`router_Alt`z of
560 yz@(Router_Alt _y z') ->
561 case x'`router_Alt`z' of
562 Router_Alt{} -> router x'`Router_Alt`yz
563 xz -> Router_Union (\(a:!:(b:!:c)) -> (a:!:c):!:b) $ xz`router_Alt`y
564 -- NOTE: prioritize the merged router 'xz' over over the non-mergeable 'y'.
565 yz -> x'`router_Alt`yz
566 xy -> Router_Union (\(a:!:(b:!:c)) -> (a:!:b):!:c) $ xy`router_Alt`z
567 go (x`Router_Alt`y) z =
568 case y`router_Alt`z of
570 case x`router_Alt`y' of
571 xy@(Router_Alt x' _y) ->
572 case x'`router_Alt`z' of
573 Router_Alt{} -> xy`Router_Alt`router z'
574 xz -> Router_Union (\((a:!:b):!:c) -> (a:!:c):!:b) $ xz`router_Alt`y
575 -- NOTE: prioritize the merged router 'xz' over the non-mergeable 'y'.
576 xy -> xy`router_Alt`z'
577 yz -> Router_Union (\((a:!:b):!:c) -> a:!:(b:!:c)) $ x`router_Alt`yz
579 -- Merge through 'Router_Union'.
580 go (Router_Union u x) y = Router_Union (\(a:!:b) -> u a:!:b) (x`router_Alt`y)
581 go x (Router_Union u y) = Router_Union (\(a:!:b) -> a:!:u b) (x`router_Alt`y)
584 go x y = x`Router_Alt`y
589 Map Segment (Router repr a k) ->
590 Map Segment (Router repr b k) ->
591 Map Segment (Router repr (a:!:b) k)
592 router_Commands allowMerging =
593 -- NOTE: a little bit more complex than required
594 -- in order to merge 'Router_Union's instead of nesting them,
595 -- such that 'unTrans' 'Router_Union' applies them all at once.
597 (Map.mapMissing $ const keepX)
598 (Map.mapMissing $ const keepY)
599 (Map.zipWithMaybeMatched $ const $ \x y ->
600 if allowMerging then Just $ mergeFull x y else Nothing)
603 Router_Union u r -> Router_Union (\(x:!:_y) -> u x) r
604 r -> Router_Union (\(x:!:_y) -> x) r
606 Router_Union u r -> Router_Union (\(_x:!:y) -> u y) r
607 r -> Router_Union (\(_x:!:y) -> y) r
609 Router_Union xu xr -> \case
610 Router_Union yu yr -> Router_Union (\(x:!:y) -> xu x:!:yu y) $ xr`router_Alt`yr
611 yr -> Router_Union (\(a:!:b) -> xu a:!:b) $ xr`router_Alt`yr
613 Router_Union yu yr -> Router_Union (\(a:!:b) -> a:!:yu b) $ xr`router_Alt`yr
614 yr -> xr`router_Alt`yr
621 | ArgEnv Name String -- ^ Here only for error reporting.
622 deriving (Eq,Ord,Show)
624 lexer :: [String] -> [Arg]
627 (`evalState` False) $
630 f :: String -> StateT Bool Identity [Arg]
633 if skip then return [ArgSegment s]
637 return [ArgTagLong ""]
638 '-':'-':cs -> return [ArgTagLong cs]
639 '-':cs@(_:_) -> return $ ArgTagShort <$> cs
640 seg -> return [ArgSegment seg]
642 showArg :: Arg -> String
644 ArgTagShort t -> '-':[t]
645 ArgTagLong t -> '-':'-':t
646 ArgSegment seg -> seg
647 ArgEnv name val -> name<>"="<>val
649 showArgs :: [Arg] -> String
650 showArgs args = List.intercalate " " $ showArg <$> args
652 instance P.Stream [Arg] where
653 type Token [Arg] = Arg
654 type Tokens [Arg] = [Arg]
655 tokenToChunk Proxy = pure
656 tokensToChunk Proxy = id
657 chunkToTokens Proxy = id
658 chunkLength Proxy = List.length
659 chunkEmpty Proxy = List.null
661 take1_ (t:ts) = Just (t, ts)
663 | n <= 0 = Just ([], s)
664 | List.null s = Nothing
665 | otherwise = Just (List.splitAt n s)
666 takeWhile_ = List.span
667 showTokens Proxy = showArgs . toList
668 -- NOTE: those make no sense when parsing a command line,
669 -- and should not be called since 'P.errorBundlePretty' is not used in 'parser'.
670 reachOffset = error "BUG: reachOffset must not be used on [Arg]"
671 reachOffsetNoLine = error "BUG: reachOffsetNoLine must not be used on [Arg]"