1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DataKinds #-}
3 {-# LANGUAGE DefaultSignatures #-}
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE InstanceSigs #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE Rank2Types #-}
9 module Symantic.CLI.Parser where
11 import Control.Applicative (Applicative(..), Alternative(..), optional, many, some)
12 import Control.Arrow (first)
13 import Control.Monad (Monad(..), join, sequence, unless)
14 import Control.Monad.Trans.Class (MonadTrans(..))
15 import Control.Monad.Trans.Except (ExceptT(..),throwE,runExceptT)
16 import Control.Monad.Trans.State (StateT(..),evalState,get,put)
18 import Data.Char (Char)
19 import Data.Either (Either(..))
20 import Data.Eq (Eq(..))
21 import Data.Function (($), (.), id, const)
22 import Data.Functor (Functor(..), (<$>))
23 import Data.Functor.Identity (Identity(..))
25 import Data.Map.Strict (Map)
26 import Data.Maybe (Maybe(..), maybe)
27 import Data.Monoid (Monoid(..))
28 import Data.Ord (Ord(..))
29 import Data.Semigroup (Semigroup(..))
30 import Data.String (String)
31 import Prelude (Integer)
32 import Numeric.Natural (Natural)
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.List as List
39 import qualified Data.Text as Text
40 import qualified Data.Text.Lazy as TL
41 import qualified Data.Text.Lazy.IO as TL
42 import qualified Data.Text.Lazy.Builder as TLB
43 import qualified Data.Map.Merge.Strict as Map
44 import qualified Data.Map.Strict as Map
45 import qualified Symantic.Document as Doc
46 import qualified System.IO as IO
47 -- import qualified Debug.Trace as Debug
49 import Symantic.CLI.API
52 newtype Parser d f k = Parser
53 { unParser :: StateT ParserState
54 (ParserCheckT [ParserError] IO)
55 (f -> k) -- Reader f k
57 instance Functor (Parser d f) where
58 a2b`fmap`Parser x = Parser $ (a2b <$>) <$> x
59 instance Applicative (Parser d f) where
60 pure = Parser . pure . const
61 Parser f <*> Parser x = Parser $ (<*>) <$> f <*> x
62 instance Alternative (Parser d f) where
65 throwE $ Fail st [ParserError_Alt]
66 Parser x <|> Parser y = Parser $
68 lift (runExceptT (runStateT x st)) >>= \case
69 Left xe | FailFatal{} <- xe -> throwE xe
71 lift (runExceptT (runStateT y st)) >>= \case
72 Left ye -> throwE (xe<>ye)
73 Right yr -> ExceptT $ return $ Right yr
76 instance Permutable (Parser d) where
77 type Permutation (Parser d) = ParserPerm d (Parser d)
78 runPermutation (ParserPerm ma p) = Parser $ do
79 u2p <- unParser $ optional p
82 Nothing -> maybe empty (Parser . return) ma
83 Just perm -> runPermutation perm
84 toPermutation (Parser x) =
86 (Parser $ (\a () -> ParserPerm (Just a) empty) <$> x)
87 toPermDefault a (Parser x) =
88 ParserPerm (Just ($ a))
89 (Parser $ (\d () -> ParserPerm (Just d) empty) <$> x)
92 -- d ~ String => -- dummy d
93 Router (Parser d) handlers (Response (Router (Parser d))) ->
96 parser api handlers args = do
98 runExceptT $ runStateT
99 (unParser $ unTrans $ router api)
101 { parserState_args = args
104 Left err -> IO.print err
105 Right (app, _st) -> unResponseParser $ app handlers
107 -- | Helper to parse the current argument.
111 StateT ParserState (ParserCheckT [ParserError] IO) a) ->
112 StateT ParserState (ParserCheckT [ParserError] IO) a
115 case parserState_args st of
116 [] -> lift $ throwE $ Fail st [errEnd]
118 lift (lift (runExceptT (runStateT (f curr) (ParserState next)))) >>= \case
119 Left err -> lift $ throwE err
131 parseArgs :: [String] -> [Arg]
134 (`evalState` False) $
137 f :: String -> StateT Bool Identity [Arg]
140 if skip then return [ArgSegment s]
144 return [ArgTagLong ""]
145 '-':'-':cs -> return [ArgTagLong cs]
146 '-':cs@(_:_) -> return $ ArgTagShort <$> cs
147 seg -> return [ArgSegment seg]
149 -- ** Type 'ParserState'
150 newtype ParserState = ParserState
151 { parserState_args :: [Arg]
155 type ParserCheckT e = ExceptT (Fail e)
157 -- ** Type 'ParserError'
159 = ParserError_Alt -- ^ When there is no alternative.
160 | ParserError_Arg { expectedArg :: Name, gotArg :: Maybe Arg }
161 | ParserError_Env { expectedEnv :: Name, gotEnv :: Maybe String, errorEnv :: Maybe String }
162 | ParserError_Tag { expectedTag :: Tag, gotArg :: Maybe Arg }
163 | ParserError_Cmd { expectedCmd :: [Name], gotCmd :: Maybe Arg }
164 | ParserError_Var { expectedVar :: Name, gotVar :: Maybe Arg, errorVar :: Maybe String }
165 | ParserError_End { gotEnd :: Arg }
168 -- *** Type 'RouteResult'
169 type RouteResult e = Either (Fail e)
173 = Fail ParserState e -- ^ Keep trying other paths.
174 | FailFatal !ParserState !e -- ^ Don't try other paths.
176 failState :: Fail e -> ParserState
177 failState (Fail st _) = st
178 failState (FailFatal st _) = st
179 failError :: Fail e -> e
180 failError (Fail _st e) = e
181 failError (FailFatal _st e) = e
182 instance Semigroup e => Semigroup (Fail e) where
183 Fail _ x <> Fail st y = Fail st (x<>y)
184 FailFatal _ x <> Fail st _y = FailFatal st (x{-<>y-})
185 Fail _ _x <> FailFatal st y = FailFatal st ({-x<>-}y)
186 FailFatal _ x <> FailFatal st y = FailFatal st (x<>y)
187 instance Monoid e => Monoid (Fail e) where
188 mempty = Fail (ParserState []) mempty
191 -- * Class 'FromSegment'
192 class FromSegment a where
193 fromSegment :: Segment -> IO (Either String a)
194 default fromSegment :: Read a => Segment -> IO (Either String a)
195 fromSegment = return . readEither
196 instance FromSegment String where
197 fromSegment = return . Right
198 instance FromSegment Text.Text where
199 fromSegment = return . Right . Text.pack
200 instance FromSegment TL.Text where
201 fromSegment = return . Right . TL.pack
202 instance FromSegment Bool
203 instance FromSegment Int
204 instance FromSegment Integer
205 instance FromSegment Natural
207 -- ** Type 'ParserPerm'
208 data ParserPerm d repr k a = ParserPerm
209 { permutation_result :: !(Maybe ((a->k)->k))
210 , permutation_parser :: repr () (ParserPerm d repr k a)
213 instance (App repr, Functor (repr ())) => Functor (ParserPerm d repr k) where
214 a2b `fmap` ParserPerm a ma = ParserPerm
215 ((\a2k2k b2k -> a2k2k $ b2k . a2b) <$> a)
216 ((a2b `fmap`) `fmap` ma)
217 instance (App repr, Functor (repr ()), Alternative (repr ())) => Applicative (ParserPerm d repr k) where
218 pure a = ParserPerm (Just ($ a)) empty
219 lhs@(ParserPerm f ma2b) <*> rhs@(ParserPerm x ma) =
220 ParserPerm a (lhsAlt <|> rhsAlt)
223 (\a2b2k2k a2k2k -> \b2k ->
224 a2b2k2k $ \a2b -> a2k2k (b2k . a2b)
226 lhsAlt = (<*> rhs) <$> ma2b
227 rhsAlt = (lhs <*>) <$> ma
228 instance CLI_Help repr => CLI_Help (ParserPerm d repr) where
229 type HelpConstraint (ParserPerm d repr) d' = HelpConstraint (Parser d) d'
235 Functor (UnTrans repr ()) =>
236 ParserPerm d (UnTrans repr) k a -> ParserPerm d repr k a
237 noTransParserPerm (ParserPerm a ma) = ParserPerm a (noTrans $ noTransParserPerm <$> ma)
241 Functor (UnTrans repr ()) =>
242 ParserPerm d repr k a -> ParserPerm d (UnTrans repr) k a
243 unTransParserPerm (ParserPerm a ma) = ParserPerm a (unTransParserPerm <$> unTrans ma)
247 (forall a b. repr a b -> repr a b) ->
248 ParserPerm d repr k c -> ParserPerm d repr k c
249 hoistParserPerm f (ParserPerm a ma) =
250 ParserPerm a (hoistParserPerm f <$> f ma)
252 instance App (Parser d) where
253 Parser x <.> Parser y = Parser $
254 x >>= \a2b -> (. a2b) <$> y
255 instance Alt (Parser d) where
256 Parser x <!> Parser y = Parser $
258 lift (runExceptT (runStateT x st)) >>= \case
259 Left xe | FailFatal{} <- xe -> throwE xe
261 lift (runExceptT (runStateT y st)) >>= \case
262 Left ye -> throwE (xe<>ye)
263 Right yr -> ExceptT $ return $ Right $
264 first (\b2k (_a:!:b) -> b2k b) yr
266 return $ first (\a2k (a:!:_b) -> a2k a) xr
267 opt (Parser x) = Parser $ do
269 lift (lift (runExceptT $ runStateT x st)) >>= \case
270 Left _err -> return ($ Nothing)
273 return (mapCont Just a)
274 instance AltApp (Parser d) where
275 many0 (Parser x) = Parser $ concatCont <$> many x
276 many1 (Parser x) = Parser $ concatCont <$> some x
277 instance Pro (Parser d) where
278 dimap a2b _b2a (Parser r) = Parser $ (\k b2k -> k (b2k . a2b)) <$> r
279 instance CLI_Command (Parser d) where
280 -- type CommandConstraint (Parser d) a = ()
282 command n x = commands $ Map.singleton n x
283 instance CLI_Var (Parser d) where
284 type VarConstraint (Parser d) a = FromSegment a
285 var' :: forall a k. VarConstraint (Parser d) a => Name -> Parser d (a->k) k
286 var' name = Parser $ do
287 popArg (ParserError_Var name Nothing Nothing) $ \curr -> do
288 st@ParserState{..} <- get
291 lift (lift (fromSegment seg)) >>= \case
292 Left err -> lift $ throwE $ FailFatal st [ParserError_Var name (Just curr) (Just err)]
293 Right a -> return ($ a)
294 _ -> lift $ throwE $ Fail st [ParserError_Var name (Just curr) Nothing]
295 just a = Parser $ return ($ a)
296 nothing = Parser $ return id
297 instance CLI_Env (Parser d) where
298 type EnvConstraint (Parser d) a = FromSegment a
299 env' :: forall a k. EnvConstraint (Parser d) a => Name -> Parser d (a->k) k
300 env' name = Parser $ do
302 lift (lift (lookupEnv name)) >>= \case
303 Nothing -> lift $ throwE $ Fail st [ParserError_Env name Nothing Nothing]
305 lift (lift (fromSegment raw)) >>= \case
306 Left err -> lift $ throwE $ FailFatal st [ParserError_Env name (Just raw) (Just err)]
307 Right a -> return ($ a)
309 concatCont :: [(a->k)->k] -> ([a]->k)->k
310 concatCont = List.foldr (consCont (:)) ($ [])
312 consCont :: (a->b->c) -> ((a->k)->k) -> ((b->k)->k) -> (c->k)->k
313 consCont ab2c a2k2k b2k2k = \c2k -> a2k2k $ \a -> b2k2k $ \b -> c2k (ab2c a b)
315 mapCont :: (a->b) -> ((a->k)->k) -> ((b->k)->k)
316 mapCont a2b a2k2k = \b2k -> a2k2k (b2k . a2b)
318 instance CLI_Tag (Parser d) where
319 type TagConstraint (Parser d) a = ()
320 tagged name p = Parser $ do
321 popArg (ParserError_Tag name Nothing) $ \curr -> do
323 case lookupTag curr name of
324 False -> lift $ throwE $ Fail st [ParserError_Tag name (Just curr)]
326 lift (lift (runExceptT (runStateT (unParser p) st))) >>= \case
327 Left (Fail st' e) -> lift $ throwE $ FailFatal st' e
328 Left e -> lift $ throwE e
333 lookupTag (ArgTagShort x) (TagShort y) = x == y
334 lookupTag (ArgTagShort x) (Tag y _) = x == y
335 lookupTag (ArgTagLong x) (TagLong y) = x == y
336 lookupTag (ArgTagLong x) (Tag _ y) = x == y
337 lookupTag _ _ = False
338 endOpts = Parser $ do
339 popArg (ParserError_Tag (TagLong "") Nothing) $ \curr -> do
340 ParserState{..} <- get
342 ArgTagLong "" -> return id
343 _ -> return id -- TODO: raise an error and use option?
345 -- ** Type 'ParserResponse'
346 newtype ParserResponse = ParserResponse { unResponseParser :: IO () }
347 -- ** Type 'ParserResponseArgs'
348 newtype ParserResponseArgs a = ParserResponseArgs (IO a)
349 deriving (Functor,Applicative,Monad)
351 instance CLI_Response (Parser d) where
352 type ResponseConstraint (Parser d) a = Outputable a
353 type ResponseArgs (Parser d) a = ParserResponseArgs a
354 type Response (Parser d) = ParserResponse
355 response' = Parser $ do
357 unless (List.null $ parserState_args st) $ do
358 lift $ throwE $ Fail st [ParserError_End $
359 List.head $ parserState_args st]
360 return $ \(ParserResponseArgs io) ->
361 ParserResponse $ io >>= output
363 -- * Class 'Outputable'
364 -- | Output of a CLI.
365 class IOType a => Outputable a where
367 default output :: Show a => a -> IO ()
369 instance Outputable String where
372 instance Outputable (Doc.AnsiText (Doc.Plain TLB.Builder)) where
380 instance Outputable (Doc.Reorg Doc.Term) where
381 output = TL.hPutStrLn IO.stdout . Doc.textTerm
382 instance Outputable (Doc.Reorg DocIO.TermIO) where
383 output = DocIO.runTermIO IO.stdout
384 instance Outputable (IO.Handle, (Doc.Reorg DocIO.TermIO)) where
385 output = uncurry DocIO.runTermIO
389 -- | Like a MIME type but for input/output of a CLI.
392 default ioType :: Reflection.Typeable a => String
393 ioType = show (Reflection.typeRep @a)
395 instance IOType String
396 instance IOType (Doc.AnsiText (Doc.Plain TLB.Builder))
398 instance IOType (Doc.Reorg Doc.Term) where
399 instance IOType (Doc.Reorg DocIO.TermIO) where
400 instance IOType (IO.Handle, Doc.Reorg DocIO.TermIO)
403 instance CLI_Help (Parser d) where
404 type HelpConstraint (Parser d) d' = d ~ d'
410 -- ** Class 'CLI_Routing'
411 class CLI_Routing repr where
412 commands :: Map Name (repr a k) -> repr a k
413 -- taggeds :: TagConstraint repr a => Map (Either Char Name) (repr (a -> k) k) -> repr (a -> k) k
414 instance CLI_Routing (Parser d) where
415 commands cmds = Parser $ do
416 st@ParserState{..} <- get
417 let exp = Map.keys cmds
418 popArg (ParserError_Cmd exp Nothing) $ \curr ->
421 case Map.lookup cmd cmds of
422 Nothing -> lift $ throwE $ Fail st [ParserError_Cmd exp (Just curr)]
424 _ -> lift $ throwE $ Fail st [ParserError_Cmd exp (Just curr)]
426 taggeds ms = Parser $ do
427 st@ParserState{..} <- get
428 case parserState_args of
429 [] -> lift $ throwE $ Fail st [ParserError "empty path segment"]
431 case lookupTag curr of
432 Nothing -> lift $ throwE $ Fail st [ParserError $ "expected: "<>fromString (show (Map.keys ms))<>" but got: "<>fromString (show curr)]
434 put st{parserState_args=next}
437 lookupTag (ArgTagShort x) = Map.lookup (Left x) ms
438 lookupTag (ArgTagLong x) = Map.lookup (Right x) ms
439 lookupTag _ = Nothing
442 data Router repr a b where
443 -- | Lift any @(repr)@ into 'Router', those not useful to segregate
444 -- wrt. the 'Trans'formation performed, aka. 'noTrans'.
445 Router_Any :: repr a b -> Router repr a b
446 -- | Represent 'commands'.
447 Router_Commands :: Map Name (Router repr a k) -> Router repr a k
448 -- | Represent 'tagged'.
449 Router_Tagged :: Tag -> Router repr f k -> Router repr f k
450 -- | Represent 'taggeds'.
452 Router_Taggeds :: TagConstraint repr a =>
453 Map (Either Char Name) (Router repr (a -> k) k) ->
454 Router repr (a -> k) k
456 -- | Represent ('<.>').
457 Router_App :: Router repr a b -> Router repr b c -> Router repr a c
458 -- | Represent ('<!>').
459 Router_Alt :: Router repr a k -> Router repr b k -> Router repr (a:!:b) k
460 -- | Unify 'Router's which have different 'handlers'.
461 -- Useful to put alternative 'Router's in a 'Map' as in 'Router_Commands'.
462 Router_Union :: (b->a) -> Router repr a k -> Router repr b k
464 instance Functor (Router (Parser d) f) where
465 a2b`fmap`x = noTrans (a2b <$> unTrans x)
466 instance Applicative (Router (Parser d) f) where
467 pure = noTrans . pure
468 f <*> x = noTrans (unTrans f <*> unTrans x)
469 instance Alternative (Router (Parser d) f) where
470 empty = noTrans empty
471 f <|> x = noTrans (unTrans f <|> unTrans x)
472 instance Permutable (Router (Parser d)) where
473 type Permutation (Router (Parser d)) = ParserPerm d (Router (Parser d))
474 runPermutation = noTrans . runPermutation . unTransParserPerm
475 toPermutation = noTransParserPerm . toPermutation . unTrans
476 toPermDefault a = noTransParserPerm . toPermDefault a . unTrans
477 instance (repr ~ Parser d) => Show (Router repr a b) where
479 Router_Any{} -> showString "X"
480 Router_Commands ms -> showParen (p>=10) $ showString "Commands [" . go (Map.toList ms) . showString "]"
482 go :: forall h k. [(Segment, Router repr h k)] -> ShowS
485 (showParen True $ showString (show n<>", ") . showsPrec 0 r) .
488 _ -> showString ", " . go xs
489 -- Router_Command n os x -> showString n . showString " " . showsPrec 10 (permutation_parser os) . showString " " . showsPrec p x
490 Router_Tagged n x -> showsPrec 10 n . showString " " . showsPrec p x
492 Router_Taggeds ms -> showParen (p>=10) $
493 showString "taggeds [" . go (Map.toList ms) . showString "]"
495 go :: forall h k. [(Either Char Name, Router repr h k)] -> ShowS
498 (showParen True $ showString (show n<>", ") . showsPrec 0 r) .
501 _ -> showString ", " . go xs
503 Router_App x y -> showParen (p>=4) $ showsPrec 4 x . showString " <.> " . showsPrec 4 y
504 Router_Alt x y -> showParen (p>=3) $ showsPrec 3 x . showString " <!> " . showsPrec 3 y
505 Router_Union _u x -> showString "Union [" . showsPrec 0 x . showString "]"
507 instance Trans (Router (Parser d)) where
508 type UnTrans (Router (Parser d)) = Parser d
510 unTrans (Router_Any x) = x
511 unTrans (Router_Alt x y) = unTrans x <!> unTrans y
512 unTrans (Router_App x y) = unTrans x <.> unTrans y
513 -- unTrans (Router_Command n os x) = command n (unTransParserPerm os) (unTrans x)
514 unTrans (Router_Commands ms) = commands (unTrans <$> ms)
515 unTrans (Router_Tagged n x) = tagged n (unTrans x)
516 -- unTrans (Router_Taggeds ms) = taggeds (unTrans <$> ms)
517 unTrans (Router_Union u x) = Parser $ (. u) <$> unParser (unTrans x)
519 instance App (Router (Parser d)) where
521 instance Alt (Router (Parser d)) where
523 instance Pro (Router (Parser d))
524 instance repr ~ (Parser d) => CLI_Command (Router repr) where
525 -- command = Router_Command
527 command n x = Router_Commands $ Map.singleton n x
528 instance CLI_Var (Router (Parser d))
529 instance CLI_Env (Router (Parser d))
530 instance CLI_Tag (Router (Parser d)) where
531 tagged = Router_Tagged
532 instance CLI_Help (Router (Parser d)) where
533 -- NOTE: set manually (instead of the 'Trans' default 'Router_Any')
534 -- to remove them all, since they are useless for 'Parser'
535 -- and may prevent patterns to be matched in 'router'.
539 instance CLI_Response (Router (Parser d))
540 instance CLI_Routing (Router (Parser d)) where
541 -- taggeds = Router_Taggeds
542 commands = Router_Commands
546 Router repr a b -> Router repr a b
547 router = {-debug1 "router" $-} \case
549 -- Router_Command n os x -> Router_Command n (hoistParserPerm router os) (router x)
550 Router_Tagged n x -> Router_Tagged n (router x)
552 Router_Tagged n x -> Router_Taggeds $
554 Tag c s -> Map.fromList [(Left c, r), (Right s, r)]
555 TagShort c -> Map.singleton (Left c) r
556 TagLong s -> Map.singleton (Right s) r
560 Router_Taggeds xs `Router_App` Router_Taggeds ys ->
561 Router_Taggeds $ router <$> (xs <> ys)
563 Router_Alt x y -> router x`router_Alt`router y
564 Router_Commands xs -> Router_Commands $ router <$> xs
565 -- Router_Taggeds xs -> Router_Taggeds $ router <$> xs
569 -- Associate to the right
570 Router_App (router x) $
571 Router_App (router y) (router z)
572 _ -> router xy `Router_App` router z
573 Router_Union u x -> Router_Union u (router x)
574 -- Router_Merge x -> Router_Merge (router x)
576 -- | Merge/reorder alternatives if possible or default to a 'Router_Alt'.
581 Router repr (a:!:b) k
582 router_Alt = {-debug2 "router_Alt"-} go
584 -- Merge alternative commands together.
585 {- NOTE: useless because 'command' is already a 'Router_Commands'.
586 go (Router_Command x xo xt) (Router_Command y yo yt) =
587 Map.singleton x (router (runPermutation xo <.> xt))
589 Map.singleton y (router (runPermutation yo <.> yt))
590 go (Router_Command x xo xt) (Router_Commands ys) =
591 Map.singleton x (router (runPermutation xo <.> xt))
593 go (Router_Commands xs) (Router_Command y yo yt) =
595 Map.singleton y (router (runPermutation yo <.> yt))
597 go (Router_Commands xs) (Router_Commands ys) =
598 xs`router_Commands`ys
600 -- Merge left first or right first, depending on which removes 'Router_Alt'.
601 go x (y`Router_Alt`z) =
602 case x`router_Alt`y of
604 case y'`router_Alt`z of
605 yz@(Router_Alt _y z') ->
606 case x'`router_Alt`z' of
607 Router_Alt{} -> router x'`Router_Alt`yz
608 xz -> Router_Union (\(a:!:(b:!:c)) -> (a:!:c):!:b) $ xz`router_Alt`y
609 -- NOTE: prioritize the merged router 'xz' over over the non-mergeable 'y'.
610 yz -> x'`router_Alt`yz
611 xy -> Router_Union (\(a:!:(b:!:c)) -> (a:!:b):!:c) $ xy`router_Alt`z
612 go (x`Router_Alt`y) z =
613 case y`router_Alt`z of
615 case x`router_Alt`y' of
616 xy@(Router_Alt x' _y) ->
617 case x'`router_Alt`z' of
618 Router_Alt{} -> xy`Router_Alt`router z'
619 xz -> Router_Union (\((a:!:b):!:c) -> (a:!:c):!:b) $ xz`router_Alt`y
620 -- NOTE: prioritize the merged router 'xz' over the non-mergeable 'y'.
621 xy -> xy`router_Alt`z'
622 yz -> Router_Union (\((a:!:b):!:c) -> a:!:(b:!:c)) $ x`router_Alt`yz
624 -- Merge through 'Router_Union'.
625 go (Router_Union u x) y = Router_Union (\(a:!:b) -> u a:!:b) (x`router_Alt`y)
626 go x (Router_Union u y) = Router_Union (\(a:!:b) -> a:!:u b) (x`router_Alt`y)
629 go x y = x`Router_Alt`y
633 Map Segment (Router repr a k) ->
634 Map Segment (Router repr b k) ->
635 Router repr (a:!:b) k
636 router_Commands xs ys =
637 -- NOTE: a little bit more complex than required
638 -- in order to merge 'Router_Union's instead of nesting them,
639 -- such that 'unTrans' 'Router_Union' applies them all at once.
642 (Map.traverseMissing $ const $ \case
644 return $ Router_Union (\(x:!:_y) -> u x) r
645 r -> return $ Router_Union (\(x:!:_y) -> x) r)
646 (Map.traverseMissing $ const $ \case
648 return $ Router_Union (\(_x:!:y) -> u y) r
649 r -> return $ Router_Union (\(_x:!:y) -> y) r)
650 (Map.zipWithAMatched $ const $ \case
651 Router_Union xu xr -> \case
652 Router_Union yu yr ->
653 return $ Router_Union (\(x:!:y) -> xu x:!:yu y) $ xr`router_Alt`yr
655 return $ Router_Union (\(a:!:b) -> xu a:!:b) $ xr`router_Alt`yr
657 Router_Union yu yr ->
658 return $ Router_Union (\(a:!:b) -> a:!:yu b) $ xr`router_Alt`yr
659 yr -> return $ xr`router_Alt`yr)
663 debug0 :: Show a => String -> a -> a
664 debug0 n a = Debug.trace (" {"<>n<>": "<>show a) a
665 debug1 :: Show a => Show b => String -> (a->b) -> (a->b)
666 debug1 n a2b a = Debug.trace ("} "<>n<>": r: "<>show b) b
667 where b = a2b $ Debug.trace ("{ "<>n<>": a: "<>show a) a
668 debug2 :: Show a => Show b => Show c => String -> (a->b->c) -> (a->b->c)
669 debug2 n a2b2c a b = Debug.trace ("} "<>n<>": r: "<>show c) c
671 b2c = a2b2c $ Debug.trace ("{ "<>n<>": a: "<>show a) a
672 c = b2c $ Debug.trace (n<>": b: "<>show b) b