Implement mapOut, peekInput, cmd child merging
parent
b15f1ae585
commit
c1cbc77e5b
|
@ -49,6 +49,7 @@ library
|
||||||
, nats
|
, nats
|
||||||
, void
|
, void
|
||||||
, bifunctors
|
, bifunctors
|
||||||
|
, deque
|
||||||
}
|
}
|
||||||
if flag(butcher-dev) {
|
if flag(butcher-dev) {
|
||||||
build-depends:
|
build-depends:
|
||||||
|
@ -110,6 +111,7 @@ test-suite tests
|
||||||
, transformers
|
, transformers
|
||||||
, mtl
|
, mtl
|
||||||
, extra
|
, extra
|
||||||
|
, deque
|
||||||
}
|
}
|
||||||
if flag(butcher-dev) {
|
if flag(butcher-dev) {
|
||||||
buildable: True
|
buildable: True
|
||||||
|
|
|
@ -69,6 +69,33 @@ simpleRunTest = do
|
||||||
it "flag 3" $ testRunA testCmd3 "abc -g" `shouldBe` Right 2
|
it "flag 3" $ testRunA testCmd3 "abc -g" `shouldBe` Right 2
|
||||||
it "flag 4" $ testRunA testCmd3 "abc -f -g" `shouldBe` Right 3
|
it "flag 4" $ testRunA testCmd3 "abc -f -g" `shouldBe` Right 3
|
||||||
it "flag 5" $ testRunA testCmd3 "abc -g -f" `shouldBe` Right 3
|
it "flag 5" $ testRunA testCmd3 "abc -g -f" `shouldBe` Right 3
|
||||||
|
describe "separated children" $ do
|
||||||
|
it "case 1" $ testRun testCmd4 "a aa" `shouldBe` Right (Just 1)
|
||||||
|
it "case 2" $ testRun testCmd4 "a ab" `shouldBe` Right (Just 2)
|
||||||
|
it "case 3" $ testRun testCmd4 "b ba" `shouldBe` Right (Just 3)
|
||||||
|
it "case 4" $ testRun testCmd4 "b bb" `shouldBe` Right (Just 4)
|
||||||
|
it "doc" $ show (ppHelpShallow (getDoc "" testCmd4)) `shouldBe`
|
||||||
|
List.unlines
|
||||||
|
[ "NAME"
|
||||||
|
, ""
|
||||||
|
, " test"
|
||||||
|
, ""
|
||||||
|
, "USAGE"
|
||||||
|
, ""
|
||||||
|
, " test a | b"
|
||||||
|
]
|
||||||
|
it "doc" $ show (ppHelpShallow (getDoc "a" testCmd4)) `shouldBe`
|
||||||
|
List.unlines
|
||||||
|
[ "NAME"
|
||||||
|
, ""
|
||||||
|
, " test a"
|
||||||
|
, ""
|
||||||
|
, "USAGE"
|
||||||
|
, ""
|
||||||
|
, " test a aa | ab"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
testCmd1 :: CmdParser Identity (WriterS.Writer (Sum Int) ()) ()
|
testCmd1 :: CmdParser Identity (WriterS.Writer (Sum Int) ()) ()
|
||||||
|
@ -110,6 +137,21 @@ testCmd3 = do
|
||||||
addCmd "def" $ do
|
addCmd "def" $ do
|
||||||
addCmdImpl ()
|
addCmdImpl ()
|
||||||
|
|
||||||
|
testCmd4 :: CmdParser Identity (WriterS.Writer (Sum Int) ()) ()
|
||||||
|
testCmd4 = do
|
||||||
|
addCmd "a" $ do
|
||||||
|
addCmd "aa" $ do
|
||||||
|
addCmdImpl $ WriterS.tell 1
|
||||||
|
addCmd "b" $ do
|
||||||
|
addCmd "bb" $ do
|
||||||
|
addCmdImpl $ WriterS.tell 4
|
||||||
|
addCmd "a" $ do
|
||||||
|
addCmd "ab" $ do
|
||||||
|
addCmdImpl $ WriterS.tell 2
|
||||||
|
addCmd "b" $ do
|
||||||
|
addCmd "ba" $ do
|
||||||
|
addCmdImpl $ WriterS.tell 3
|
||||||
|
|
||||||
testParse :: CmdParser Identity out () -> String -> Maybe (CommandDesc out)
|
testParse :: CmdParser Identity out () -> String -> Maybe (CommandDesc out)
|
||||||
testParse cmd s = either (const Nothing) Just
|
testParse cmd s = either (const Nothing) Just
|
||||||
$ snd
|
$ snd
|
||||||
|
@ -124,3 +166,6 @@ testRunA :: CmdParser (StateS.State Int) () () -> String -> Either ParsingError
|
||||||
testRunA cmd str = (\((_, e), s) -> e $> s)
|
testRunA cmd str = (\((_, e), s) -> e $> s)
|
||||||
$ flip StateS.runState (0::Int)
|
$ flip StateS.runState (0::Int)
|
||||||
$ runCmdParserA Nothing (InputString str) cmd
|
$ runCmdParserA Nothing (InputString str) cmd
|
||||||
|
|
||||||
|
getDoc :: String -> CmdParser Identity out () -> CommandDesc ()
|
||||||
|
getDoc s = fst . runCmdParser (Just "test") (InputString s)
|
||||||
|
|
|
@ -28,6 +28,7 @@ module UI.Butcher.Monadic
|
||||||
-- * Builtin commands
|
-- * Builtin commands
|
||||||
, addHelpCommand
|
, addHelpCommand
|
||||||
, addButcherDebugCommand
|
, addButcherDebugCommand
|
||||||
|
, mapOut
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
|
|
@ -34,12 +34,13 @@ addHelpCommand desc = addCmd "help" $ do
|
||||||
rest <- addRestOfInputStringParam "SUBCOMMAND(s)" mempty
|
rest <- addRestOfInputStringParam "SUBCOMMAND(s)" mempty
|
||||||
addCmdImpl $ do
|
addCmdImpl $ do
|
||||||
let parentDesc = maybe undefined snd (_cmd_mParent desc)
|
let parentDesc = maybe undefined snd (_cmd_mParent desc)
|
||||||
let restWords = List.words rest
|
let restWords = List.words rest
|
||||||
let descent :: [String] -> CommandDesc a -> CommandDesc a
|
let descent :: [String] -> CommandDesc a -> CommandDesc a
|
||||||
descent [] curDesc = curDesc
|
descent [] curDesc = curDesc
|
||||||
descent (w:wr) curDesc = case List.lookup w $ _cmd_children curDesc of
|
descent (w:wr) curDesc =
|
||||||
Nothing -> curDesc
|
case List.lookup w $ Data.Foldable.toList $ _cmd_children curDesc of
|
||||||
Just child -> descent wr child
|
Nothing -> curDesc
|
||||||
|
Just child -> descent wr child
|
||||||
print $ ppHelpShallow $ descent restWords parentDesc
|
print $ ppHelpShallow $ descent restWords parentDesc
|
||||||
|
|
||||||
-- | Adds a help command that prints help for the command currently in context.
|
-- | Adds a help command that prints help for the command currently in context.
|
||||||
|
|
|
@ -60,10 +60,16 @@ module UI.Butcher.Monadic.Command
|
||||||
, reorderStart
|
, reorderStart
|
||||||
, reorderStop
|
, reorderStop
|
||||||
, peekCmdDesc
|
, peekCmdDesc
|
||||||
|
, peekInput
|
||||||
-- * Building CmdParsers - myprog -v --input PATH
|
-- * Building CmdParsers - myprog -v --input PATH
|
||||||
, module UI.Butcher.Monadic.Flag
|
, module UI.Butcher.Monadic.Flag
|
||||||
-- * Building CmdParsers - myprog SOME_INT
|
-- * Building CmdParsers - myprog SOME_INT
|
||||||
, module UI.Butcher.Monadic.Param
|
, module UI.Butcher.Monadic.Param
|
||||||
|
-- * Low-level part functions
|
||||||
|
, addCmdPart
|
||||||
|
, addCmdPartMany
|
||||||
|
, addCmdPartInp
|
||||||
|
, addCmdPartManyInp
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ module UI.Butcher.Monadic.Internal.Core
|
||||||
, addCmdHelp
|
, addCmdHelp
|
||||||
, addCmdHelpStr
|
, addCmdHelpStr
|
||||||
, peekCmdDesc
|
, peekCmdDesc
|
||||||
|
, peekInput
|
||||||
, addCmdPart
|
, addCmdPart
|
||||||
, addCmdPartA
|
, addCmdPartA
|
||||||
, addCmdPartMany
|
, addCmdPartMany
|
||||||
|
@ -28,6 +29,7 @@ module UI.Butcher.Monadic.Internal.Core
|
||||||
, runCmdParserExt
|
, runCmdParserExt
|
||||||
, runCmdParserA
|
, runCmdParserA
|
||||||
, runCmdParserAExt
|
, runCmdParserAExt
|
||||||
|
, mapOut
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -114,9 +116,12 @@ addCmdHelpStr s = liftF $ CmdParserHelp (PP.text s) ()
|
||||||
-- For best results, use this "below"
|
-- For best results, use this "below"
|
||||||
-- any 'addCmd' invocations in the current context, e.g. directly before
|
-- any 'addCmd' invocations in the current context, e.g. directly before
|
||||||
-- the 'addCmdImpl' invocation.
|
-- the 'addCmdImpl' invocation.
|
||||||
peekCmdDesc :: CmdParser f out (CommandDesc out)
|
peekCmdDesc :: CmdParser f out (CommandDesc ())
|
||||||
peekCmdDesc = liftF $ CmdParserPeekDesc id
|
peekCmdDesc = liftF $ CmdParserPeekDesc id
|
||||||
|
|
||||||
|
peekInput :: CmdParser f out String
|
||||||
|
peekInput = liftF $ CmdParserPeekInput id
|
||||||
|
|
||||||
addCmdPart
|
addCmdPart
|
||||||
:: (Applicative f, Typeable p)
|
:: (Applicative f, Typeable p)
|
||||||
=> PartDesc
|
=> PartDesc
|
||||||
|
@ -236,15 +241,17 @@ data PartGatherData f
|
||||||
, _pgd_many :: Bool
|
, _pgd_many :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data ChildGather f out = ChildGather String (CmdParser f out ()) (f ())
|
||||||
|
|
||||||
type PartParsedData = Map Int [Dynamic]
|
type PartParsedData = Map Int [Dynamic]
|
||||||
|
|
||||||
data CmdDescStack = StackBottom [PartDesc]
|
data CmdDescStack = StackBottom (Deque PartDesc)
|
||||||
| StackLayer [PartDesc] String CmdDescStack
|
| StackLayer (Deque PartDesc) String CmdDescStack
|
||||||
|
|
||||||
descStackAdd :: PartDesc -> CmdDescStack -> CmdDescStack
|
descStackAdd :: PartDesc -> CmdDescStack -> CmdDescStack
|
||||||
descStackAdd d = \case
|
descStackAdd d = \case
|
||||||
StackBottom l -> StackBottom $ d:l
|
StackBottom l -> StackBottom $ Deque.snoc d l
|
||||||
StackLayer l s u -> StackLayer (d:l) s u
|
StackLayer l s u -> StackLayer (Deque.snoc d l) s u
|
||||||
|
|
||||||
|
|
||||||
-- | Because butcher is evil (i.e. has constraints not encoded in the types;
|
-- | Because butcher is evil (i.e. has constraints not encoded in the types;
|
||||||
|
@ -262,7 +269,7 @@ checkCmdParser :: forall f out
|
||||||
checkCmdParser mTopLevel cmdParser
|
checkCmdParser mTopLevel cmdParser
|
||||||
= (>>= final)
|
= (>>= final)
|
||||||
$ MultiRWSS.runMultiRWSTNil
|
$ MultiRWSS.runMultiRWSTNil
|
||||||
$ MultiRWSS.withMultiStateAS (StackBottom [])
|
$ MultiRWSS.withMultiStateAS (StackBottom mempty)
|
||||||
$ MultiRWSS.withMultiStateS emptyCommandDesc
|
$ MultiRWSS.withMultiStateS emptyCommandDesc
|
||||||
$ processMain cmdParser
|
$ processMain cmdParser
|
||||||
where
|
where
|
||||||
|
@ -273,8 +280,7 @@ checkCmdParser mTopLevel cmdParser
|
||||||
StackBottom descs -> Right
|
StackBottom descs -> Right
|
||||||
$ descFixParentsWithTopM (mTopLevel <&> \n -> (n, emptyCommandDesc))
|
$ descFixParentsWithTopM (mTopLevel <&> \n -> (n, emptyCommandDesc))
|
||||||
$ () <$ desc
|
$ () <$ desc
|
||||||
{ _cmd_parts = reverse descs
|
{ _cmd_parts = Data.Foldable.toList descs
|
||||||
, _cmd_children = reverse $ _cmd_children desc
|
|
||||||
}
|
}
|
||||||
StackLayer _ _ _ -> Left "unclosed ReorderStart or GroupStart"
|
StackLayer _ _ _ -> Left "unclosed ReorderStart or GroupStart"
|
||||||
processMain :: CmdParser f out ()
|
processMain :: CmdParser f out ()
|
||||||
|
@ -291,6 +297,8 @@ checkCmdParser mTopLevel cmdParser
|
||||||
processMain next
|
processMain next
|
||||||
Free (CmdParserPeekDesc nextF) -> do
|
Free (CmdParserPeekDesc nextF) -> do
|
||||||
processMain $ nextF monadMisuseError
|
processMain $ nextF monadMisuseError
|
||||||
|
Free (CmdParserPeekInput nextF) -> do
|
||||||
|
processMain $ nextF monadMisuseError
|
||||||
Free (CmdParserPart desc _parseF _act nextF) -> do
|
Free (CmdParserPart desc _parseF _act nextF) -> do
|
||||||
do
|
do
|
||||||
descStack <- mGet
|
descStack <- mGet
|
||||||
|
@ -312,25 +320,23 @@ checkCmdParser mTopLevel cmdParser
|
||||||
mSet $ descStackAdd (wrapBoundDesc bound desc) descStack
|
mSet $ descStackAdd (wrapBoundDesc bound desc) descStack
|
||||||
processMain $ nextF monadMisuseError
|
processMain $ nextF monadMisuseError
|
||||||
Free (CmdParserChild cmdStr sub _act next) -> do
|
Free (CmdParserChild cmdStr sub _act next) -> do
|
||||||
|
mInitialDesc <- takeCommandChild cmdStr
|
||||||
cmd :: CommandDesc out <- mGet
|
cmd :: CommandDesc out <- mGet
|
||||||
subCmd <- do
|
subCmd <- do
|
||||||
stackCur :: CmdDescStack <- mGet
|
stackCur :: CmdDescStack <- mGet
|
||||||
mSet (emptyCommandDesc :: CommandDesc out)
|
mSet $ fromMaybe (emptyCommandDesc :: CommandDesc out) mInitialDesc
|
||||||
mSet $ StackBottom []
|
mSet $ StackBottom mempty
|
||||||
processMain sub
|
processMain sub
|
||||||
c <- mGet
|
c <- mGet
|
||||||
stackBelow <- mGet
|
stackBelow <- mGet
|
||||||
mSet cmd
|
mSet cmd
|
||||||
mSet stackCur
|
mSet stackCur
|
||||||
subParts <- case stackBelow of
|
subParts <- case stackBelow of
|
||||||
StackBottom descs -> return $ reverse descs
|
StackBottom descs -> return $ Data.Foldable.toList descs
|
||||||
StackLayer _ _ _ -> lift $ Left "unclosed ReorderStart or GroupStart"
|
StackLayer _ _ _ -> lift $ Left "unclosed ReorderStart or GroupStart"
|
||||||
return c
|
return c { _cmd_parts = subParts }
|
||||||
{ _cmd_children = reverse $ _cmd_children c
|
|
||||||
, _cmd_parts = subParts
|
|
||||||
}
|
|
||||||
mSet $ cmd
|
mSet $ cmd
|
||||||
{ _cmd_children = (cmdStr, subCmd) : _cmd_children cmd
|
{ _cmd_children = (cmdStr, subCmd) `Deque.snoc` _cmd_children cmd
|
||||||
}
|
}
|
||||||
processMain next
|
processMain next
|
||||||
Free (CmdParserImpl out next) -> do
|
Free (CmdParserImpl out next) -> do
|
||||||
|
@ -338,7 +344,7 @@ checkCmdParser mTopLevel cmdParser
|
||||||
processMain $ next
|
processMain $ next
|
||||||
Free (CmdParserGrouped groupName next) -> do
|
Free (CmdParserGrouped groupName next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] groupName stackCur
|
mSet $ StackLayer mempty groupName stackCur
|
||||||
processMain $ next
|
processMain $ next
|
||||||
Free (CmdParserGroupEnd next) -> do
|
Free (CmdParserGroupEnd next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
|
@ -348,19 +354,19 @@ checkCmdParser mTopLevel cmdParser
|
||||||
StackLayer _descs "" _up -> do
|
StackLayer _descs "" _up -> do
|
||||||
lift $ Left $ "GroupEnd found, but expected ReorderStop first"
|
lift $ Left $ "GroupEnd found, but expected ReorderStop first"
|
||||||
StackLayer descs groupName up -> do
|
StackLayer descs groupName up -> do
|
||||||
mSet $ descStackAdd (PartRedirect groupName (PartSeq (reverse descs))) up
|
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
|
||||||
processMain $ next
|
processMain $ next
|
||||||
Free (CmdParserReorderStop next) -> do
|
Free (CmdParserReorderStop next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
case stackCur of
|
case stackCur of
|
||||||
StackBottom{} -> lift $ Left $ "ReorderStop without reorderStart"
|
StackBottom{} -> lift $ Left $ "ReorderStop without reorderStart"
|
||||||
StackLayer descs "" up -> do
|
StackLayer descs "" up -> do
|
||||||
mSet $ descStackAdd (PartReorder (reverse descs)) up
|
mSet $ descStackAdd (PartReorder (Data.Foldable.toList descs)) up
|
||||||
StackLayer{} -> lift $ Left $ "Found ReorderStop, but need GroupEnd first"
|
StackLayer{} -> lift $ Left $ "Found ReorderStop, but need GroupEnd first"
|
||||||
processMain next
|
processMain next
|
||||||
Free (CmdParserReorderStart next) -> do
|
Free (CmdParserReorderStart next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] "" stackCur
|
mSet $ StackLayer mempty "" stackCur
|
||||||
processMain next
|
processMain next
|
||||||
|
|
||||||
monadMisuseError :: a
|
monadMisuseError :: a
|
||||||
|
@ -425,7 +431,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
$ (<&> captureFinal)
|
$ (<&> captureFinal)
|
||||||
$ MultiRWSS.withMultiWriterWA
|
$ MultiRWSS.withMultiWriterWA
|
||||||
$ MultiRWSS.withMultiStateA cmdParser
|
$ MultiRWSS.withMultiStateA cmdParser
|
||||||
$ MultiRWSS.withMultiStateSA (StackBottom [])
|
$ MultiRWSS.withMultiStateSA (StackBottom mempty)
|
||||||
$ MultiRWSS.withMultiStateSA inputInitial
|
$ MultiRWSS.withMultiStateSA inputInitial
|
||||||
$ MultiRWSS.withMultiStateSA (PastCommandInput inputInitial)
|
$ MultiRWSS.withMultiStateSA (PastCommandInput inputInitial)
|
||||||
$ MultiRWSS.withMultiStateSA initialCommandDesc
|
$ MultiRWSS.withMultiStateSA initialCommandDesc
|
||||||
|
@ -480,9 +486,11 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
$ MultiRWSS.runMultiRWSTNil
|
$ MultiRWSS.runMultiRWSTNil
|
||||||
$ MultiRWSS.withMultiStateSA emptyCommandDesc
|
$ MultiRWSS.withMultiStateSA emptyCommandDesc
|
||||||
{ _cmd_mParent = _cmd_mParent cmdCur } -- partialDesc
|
{ _cmd_mParent = _cmd_mParent cmdCur } -- partialDesc
|
||||||
$ MultiRWSS.withMultiStateS (StackBottom []) -- partialStack
|
$ MultiRWSS.withMultiStateS (StackBottom mempty) -- partialStack
|
||||||
$ iterM processCmdShallow $ parser
|
$ iterM processCmdShallow $ parser
|
||||||
processMain $ nextF $ postProcessCmd stack cmd
|
processMain $ nextF $ () <$ postProcessCmd stack cmd
|
||||||
|
Free (CmdParserPeekInput nextF) -> do
|
||||||
|
processMain $ nextF $ inputToString inputInitial
|
||||||
Free (CmdParserPart desc parseF actF nextF) -> do
|
Free (CmdParserPart desc parseF actF nextF) -> do
|
||||||
do
|
do
|
||||||
descStack <- mGet
|
descStack <- mGet
|
||||||
|
@ -561,53 +569,81 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
r <- proc
|
r <- proc
|
||||||
let act = traverse actF r
|
let act = traverse actF r
|
||||||
(act *>) <$> processMain (nextF $ r)
|
(act *>) <$> processMain (nextF $ r)
|
||||||
f@(Free (CmdParserChild cmdStr sub act next)) -> do
|
f@(Free (CmdParserChild _ _ _ _)) -> do
|
||||||
dropSpaces
|
dropSpaces
|
||||||
input <- mGet
|
input <- mGet
|
||||||
|
(gatheredChildren :: [ChildGather f out], restCmdParser) <-
|
||||||
|
MultiRWSS.withMultiWriterWA $ childrenGather f
|
||||||
let
|
let
|
||||||
mRest = case input of
|
child_fold
|
||||||
InputString str | cmdStr == str ->
|
:: (Deque String, Map String (CmdParser f out (), f ()))
|
||||||
Just $ InputString ""
|
-> ChildGather f out
|
||||||
InputString str | (cmdStr++" ") `isPrefixOf` str ->
|
-> (Deque String, Map String (CmdParser f out (), f ()))
|
||||||
Just $ InputString $ drop (length cmdStr + 1) str
|
child_fold (c_names, c_map) (ChildGather name child act) =
|
||||||
InputArgs (str:strr) | cmdStr == str ->
|
case name `MapS.lookup` c_map of
|
||||||
Just $ InputArgs strr
|
Nothing ->
|
||||||
_ -> Nothing
|
( Deque.snoc name c_names
|
||||||
|
, MapS.insert name (child, act) c_map
|
||||||
|
)
|
||||||
|
Just (child', act') ->
|
||||||
|
( c_names
|
||||||
|
, MapS.insert name (child' >> child, act') c_map
|
||||||
|
-- we intentionally override/ignore act here.
|
||||||
|
-- TODO: it should be documented that we expect the same act
|
||||||
|
-- on different child nodes with the same name.
|
||||||
|
)
|
||||||
|
(child_name_list, child_map) =
|
||||||
|
foldl' child_fold (mempty, MapS.empty) gatheredChildren
|
||||||
|
combined_child_list = Data.Foldable.toList child_name_list <&> \n ->
|
||||||
|
(n, child_map MapS.! n)
|
||||||
|
let mRest = asum $ combined_child_list <&> \(name, (child, act)) ->
|
||||||
|
case input of
|
||||||
|
InputString str | name == str ->
|
||||||
|
Just $ (name, child, act, InputString "")
|
||||||
|
InputString str | (name++" ") `isPrefixOf` str ->
|
||||||
|
Just $ (name, child, act, InputString $ drop (length name + 1) str)
|
||||||
|
InputArgs (str:strr) | name == str ->
|
||||||
|
Just $ (name, child, act, InputArgs strr)
|
||||||
|
_ -> Nothing
|
||||||
case mRest of
|
case mRest of
|
||||||
Nothing -> do
|
Nothing -> do -- a child not matching what we have in the input
|
||||||
cmd :: CommandDesc out <- mGet
|
let initialDesc :: CommandDesc out = emptyCommandDesc
|
||||||
let (subCmd, subStack)
|
-- get the shallow desc for the child in a separate env.
|
||||||
= runIdentity
|
combined_child_list `forM_` \(child_name, (child, _)) -> do
|
||||||
$ MultiRWSS.runMultiRWSTNil
|
let (subCmd, subStack)
|
||||||
$ MultiRWSS.withMultiStateSA (emptyCommandDesc :: CommandDesc out)
|
= runIdentity
|
||||||
$ MultiRWSS.withMultiStateS (StackBottom [])
|
$ MultiRWSS.runMultiRWSTNil
|
||||||
$ iterM processCmdShallow sub
|
$ MultiRWSS.withMultiStateSA initialDesc
|
||||||
mSet $ cmd
|
$ MultiRWSS.withMultiStateS (StackBottom mempty)
|
||||||
{ _cmd_children = (cmdStr, postProcessCmd subStack subCmd)
|
$ iterM processCmdShallow child
|
||||||
: _cmd_children cmd
|
cmd_children %=+ Deque.snoc (child_name, postProcessCmd subStack subCmd)
|
||||||
}
|
-- proceed regularly on the same layer
|
||||||
processMain next
|
processMain $ restCmdParser
|
||||||
Just rest -> do
|
Just (name, child, act, rest) -> do -- matching child -> descend
|
||||||
|
-- process all remaining stuff on the same layer shallowly,
|
||||||
|
-- including the current node. This will be replaced later.
|
||||||
iterM processCmdShallow f
|
iterM processCmdShallow f
|
||||||
|
-- so the descend
|
||||||
cmd <- do
|
cmd <- do
|
||||||
c :: CommandDesc out <- mGet
|
c :: CommandDesc out <- mGet
|
||||||
prevStack :: CmdDescStack <- mGet
|
prevStack :: CmdDescStack <- mGet
|
||||||
return $ postProcessCmd prevStack c
|
return $ postProcessCmd prevStack c
|
||||||
mSet $ rest
|
mSet $ rest
|
||||||
mSet $ PastCommandInput rest
|
mSet $ PastCommandInput rest
|
||||||
mSet $ (emptyCommandDesc :: CommandDesc out)
|
mSet $ emptyCommandDesc
|
||||||
{ _cmd_mParent = Just (cmdStr, cmd)
|
{ _cmd_mParent = Just (name, cmd)
|
||||||
}
|
}
|
||||||
mSet $ sub
|
mSet $ child
|
||||||
mSet $ StackBottom []
|
mSet $ StackBottom mempty
|
||||||
subAct <- processMain sub
|
childAct <- processMain child
|
||||||
return $ act *> subAct
|
-- check that descending yielded
|
||||||
|
return $ act *> childAct
|
||||||
Free (CmdParserImpl out next) -> do
|
Free (CmdParserImpl out next) -> do
|
||||||
cmd_out .=+ Just out
|
cmd_out .=+ Just out
|
||||||
processMain $ next
|
processMain $ next
|
||||||
Free (CmdParserGrouped groupName next) -> do
|
Free (CmdParserGrouped groupName next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] groupName stackCur
|
mSet $ StackLayer mempty groupName stackCur
|
||||||
processMain $ next
|
processMain $ next
|
||||||
Free (CmdParserGroupEnd next) -> do
|
Free (CmdParserGroupEnd next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
|
@ -616,7 +652,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
mTell $ ["butcher interface error: group end without group start"]
|
mTell $ ["butcher interface error: group end without group start"]
|
||||||
return $ pure () -- hard abort should be fine for this case.
|
return $ pure () -- hard abort should be fine for this case.
|
||||||
StackLayer descs groupName up -> do
|
StackLayer descs groupName up -> do
|
||||||
mSet $ descStackAdd (PartRedirect groupName (PartSeq (reverse descs))) up
|
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
|
||||||
processMain $ next
|
processMain $ next
|
||||||
Free (CmdParserReorderStop next) -> do
|
Free (CmdParserReorderStop next) -> do
|
||||||
mTell $ ["butcher interface error: reorder stop without reorder start"]
|
mTell $ ["butcher interface error: reorder stop without reorder start"]
|
||||||
|
@ -627,7 +663,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
$ iterM reorderPartGather $ next
|
$ iterM reorderPartGather $ next
|
||||||
let
|
let
|
||||||
reorderMapInit :: Map Int (PartGatherData f)
|
reorderMapInit :: Map Int (PartGatherData f)
|
||||||
reorderMapInit = Map.fromList $ reorderData <&> \d -> (_pgd_id d, d)
|
reorderMapInit = MapS.fromList $ reorderData <&> \d -> (_pgd_id d, d)
|
||||||
tryParsePartData :: Input -> PartGatherData f -> First (Int, Dynamic, Input, Bool, f ())
|
tryParsePartData :: Input -> PartGatherData f -> First (Int, Dynamic, Input, Bool, f ())
|
||||||
tryParsePartData input (PartGatherData pid _ pfe act allowMany) =
|
tryParsePartData input (PartGatherData pid _ pfe act allowMany) =
|
||||||
First [ (pid, toDyn r, rest, allowMany, act r)
|
First [ (pid, toDyn r, rest, allowMany, act r)
|
||||||
|
@ -653,17 +689,17 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
Nothing -> return $ pure ()
|
Nothing -> return $ pure ()
|
||||||
Just (pid, x, rest, more, act) -> do
|
Just (pid, x, rest, more, act) -> do
|
||||||
mSet rest
|
mSet rest
|
||||||
mModify $ Map.insertWith (++) pid [x]
|
mModify $ MapS.insertWith (++) pid [x]
|
||||||
when (not more) $ do
|
when (not more) $ do
|
||||||
mSet $ Map.delete pid m
|
mSet $ MapS.delete pid m
|
||||||
actRest <- parseLoop
|
actRest <- parseLoop
|
||||||
return $ act *> actRest
|
return $ act *> actRest
|
||||||
(finalMap, (fr, acts)) <- MultiRWSS.withMultiStateSA (Map.empty :: PartParsedData)
|
(finalMap, (fr, acts)) <- MultiRWSS.withMultiStateSA (MapS.empty :: PartParsedData)
|
||||||
$ MultiRWSS.withMultiStateA reorderMapInit
|
$ MultiRWSS.withMultiStateA reorderMapInit
|
||||||
$ do
|
$ do
|
||||||
acts <- parseLoop -- filling the map
|
acts <- parseLoop -- filling the map
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] "" stackCur
|
mSet $ StackLayer mempty "" stackCur
|
||||||
fr <- MultiRWSS.withMultiStateA (1::Int) $ processParsedParts next
|
fr <- MultiRWSS.withMultiStateA (1::Int) $ processParsedParts next
|
||||||
return (fr, acts)
|
return (fr, acts)
|
||||||
-- we check that all data placed in the map has been consumed while
|
-- we check that all data placed in the map has been consumed while
|
||||||
|
@ -671,7 +707,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
-- there can only be any rest if the collection of parts changed
|
-- there can only be any rest if the collection of parts changed
|
||||||
-- between the reorderPartGather traversal and the processParsedParts
|
-- between the reorderPartGather traversal and the processParsedParts
|
||||||
-- consumption.
|
-- consumption.
|
||||||
if Map.null finalMap
|
if MapS.null finalMap
|
||||||
then do
|
then do
|
||||||
actRest <- processMain fr
|
actRest <- processMain fr
|
||||||
return $ acts *> actRest
|
return $ acts *> actRest
|
||||||
|
@ -711,6 +747,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
CmdParserHelp{} -> restCase
|
CmdParserHelp{} -> restCase
|
||||||
CmdParserSynopsis{} -> restCase
|
CmdParserSynopsis{} -> restCase
|
||||||
CmdParserPeekDesc{} -> restCase
|
CmdParserPeekDesc{} -> restCase
|
||||||
|
CmdParserPeekInput{} -> restCase
|
||||||
CmdParserChild{} -> restCase
|
CmdParserChild{} -> restCase
|
||||||
CmdParserImpl{} -> restCase
|
CmdParserImpl{} -> restCase
|
||||||
CmdParserReorderStart{} -> restCase
|
CmdParserReorderStart{} -> restCase
|
||||||
|
@ -721,6 +758,36 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
mTell ["Did not find expected ReorderStop after the reordered parts"]
|
mTell ["Did not find expected ReorderStop after the reordered parts"]
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
childrenGather
|
||||||
|
:: ( MonadMultiWriter [ChildGather f out] m
|
||||||
|
, MonadMultiState (CmdParser f out ()) m
|
||||||
|
, MonadMultiState (CommandDesc out) m
|
||||||
|
)
|
||||||
|
=> CmdParser f out a
|
||||||
|
-> m (CmdParser f out a)
|
||||||
|
childrenGather = \case
|
||||||
|
Free (CmdParserChild cmdStr sub act next) -> do
|
||||||
|
mTell [ChildGather cmdStr sub act]
|
||||||
|
childrenGather next
|
||||||
|
Free (CmdParserPeekInput nextF) -> do
|
||||||
|
childrenGather $ nextF $ inputToString inputInitial
|
||||||
|
Free (CmdParserPeekDesc nextF) -> do
|
||||||
|
parser <- mGet
|
||||||
|
-- partialDesc :: CommandDesc out <- mGet
|
||||||
|
-- partialStack :: CmdDescStack <- mGet
|
||||||
|
-- run the rest without affecting the actual stack
|
||||||
|
-- to retrieve the complete cmddesc.
|
||||||
|
cmdCur :: CommandDesc out <- mGet
|
||||||
|
let (cmd :: CommandDesc out, stack)
|
||||||
|
= runIdentity
|
||||||
|
$ MultiRWSS.runMultiRWSTNil
|
||||||
|
$ MultiRWSS.withMultiStateSA emptyCommandDesc
|
||||||
|
{ _cmd_mParent = _cmd_mParent cmdCur } -- partialDesc
|
||||||
|
$ MultiRWSS.withMultiStateS (StackBottom mempty) -- partialStack
|
||||||
|
$ iterM processCmdShallow $ parser
|
||||||
|
childrenGather $ nextF $ () <$ postProcessCmd stack cmd
|
||||||
|
something -> return something
|
||||||
|
|
||||||
processParsedParts
|
processParsedParts
|
||||||
:: forall m r w s m0 a
|
:: forall m r w s m0 a
|
||||||
. ( MonadMultiState Int m
|
. ( MonadMultiState Int m
|
||||||
|
@ -747,11 +814,11 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
StackBottom{} -> do
|
StackBottom{} -> do
|
||||||
mTell ["unexpected stackBottom"]
|
mTell ["unexpected stackBottom"]
|
||||||
StackLayer descs _ up -> do
|
StackLayer descs _ up -> do
|
||||||
mSet $ descStackAdd (PartReorder (reverse descs)) up
|
mSet $ descStackAdd (PartReorder (Data.Foldable.toList descs)) up
|
||||||
return next
|
return next
|
||||||
Free (CmdParserGrouped groupName next) -> do
|
Free (CmdParserGrouped groupName next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] groupName stackCur
|
mSet $ StackLayer mempty groupName stackCur
|
||||||
processParsedParts $ next
|
processParsedParts $ next
|
||||||
Free (CmdParserGroupEnd next) -> do
|
Free (CmdParserGroupEnd next) -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
|
@ -760,7 +827,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
mTell $ ["butcher interface error: group end without group start"]
|
mTell $ ["butcher interface error: group end without group start"]
|
||||||
return $ next -- hard abort should be fine for this case.
|
return $ next -- hard abort should be fine for this case.
|
||||||
StackLayer descs groupName up -> do
|
StackLayer descs groupName up -> do
|
||||||
mSet $ descStackAdd (PartRedirect groupName (PartSeq (reverse descs))) up
|
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
|
||||||
processParsedParts $ next
|
processParsedParts $ next
|
||||||
Pure x -> return $ return $ x
|
Pure x -> return $ return $ x
|
||||||
f -> do
|
f -> do
|
||||||
|
@ -780,7 +847,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
pid <- mGet
|
pid <- mGet
|
||||||
mSet $ pid + 1
|
mSet $ pid + 1
|
||||||
parsedMap :: PartParsedData <- mGet
|
parsedMap :: PartParsedData <- mGet
|
||||||
mSet $ Map.delete pid parsedMap
|
mSet $ MapS.delete pid parsedMap
|
||||||
partMap :: Map Int (PartGatherData f) <- mGet
|
partMap :: Map Int (PartGatherData f) <- mGet
|
||||||
input :: Input <- mGet
|
input :: Input <- mGet
|
||||||
let errorResult = do
|
let errorResult = do
|
||||||
|
@ -795,8 +862,8 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
continueOrMisuse :: Maybe p -> m (CmdParser f out a)
|
continueOrMisuse :: Maybe p -> m (CmdParser f out a)
|
||||||
continueOrMisuse = maybe monadMisuseError
|
continueOrMisuse = maybe monadMisuseError
|
||||||
(processParsedParts . nextF)
|
(processParsedParts . nextF)
|
||||||
case Map.lookup pid parsedMap of
|
case MapS.lookup pid parsedMap of
|
||||||
Nothing -> case Map.lookup pid partMap of
|
Nothing -> case MapS.lookup pid partMap of
|
||||||
Nothing -> monadMisuseError -- it would still be in the map
|
Nothing -> monadMisuseError -- it would still be in the map
|
||||||
-- if it never had been successfully
|
-- if it never had been successfully
|
||||||
-- parsed, as indicicated by the
|
-- parsed, as indicicated by the
|
||||||
|
@ -823,8 +890,8 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
pid <- mGet
|
pid <- mGet
|
||||||
mSet $ pid + 1
|
mSet $ pid + 1
|
||||||
m :: PartParsedData <- mGet
|
m :: PartParsedData <- mGet
|
||||||
mSet $ Map.delete pid m
|
mSet $ MapS.delete pid m
|
||||||
let partDyns = case Map.lookup pid m of
|
let partDyns = case MapS.lookup pid m of
|
||||||
Nothing -> []
|
Nothing -> []
|
||||||
Just r -> r
|
Just r -> r
|
||||||
case mapM fromDynamic partDyns of
|
case mapM fromDynamic partDyns of
|
||||||
|
@ -848,7 +915,9 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
mSet $ cmd { _cmd_synopsis = Just $ PP.text s }
|
mSet $ cmd { _cmd_synopsis = Just $ PP.text s }
|
||||||
next
|
next
|
||||||
CmdParserPeekDesc nextF -> do
|
CmdParserPeekDesc nextF -> do
|
||||||
mGet >>= nextF
|
mGet >>= nextF . fmap (\(_ :: out) -> ())
|
||||||
|
CmdParserPeekInput nextF -> do
|
||||||
|
nextF $ inputToString inputInitial
|
||||||
CmdParserPart desc _parseF _act nextF -> do
|
CmdParserPart desc _parseF _act nextF -> do
|
||||||
do
|
do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
|
@ -870,14 +939,16 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
mSet $ descStackAdd (wrapBoundDesc bound desc) stackCur
|
mSet $ descStackAdd (wrapBoundDesc bound desc) stackCur
|
||||||
nextF monadMisuseError
|
nextF monadMisuseError
|
||||||
CmdParserChild cmdStr _sub _act next -> do
|
CmdParserChild cmdStr _sub _act next -> do
|
||||||
cmd_children %=+ ((cmdStr, emptyCommandDesc :: CommandDesc out):)
|
mExisting <- takeCommandChild cmdStr
|
||||||
|
let childDesc :: CommandDesc out = fromMaybe emptyCommandDesc mExisting
|
||||||
|
cmd_children %=+ Deque.snoc (cmdStr, childDesc)
|
||||||
next
|
next
|
||||||
CmdParserImpl out next -> do
|
CmdParserImpl out next -> do
|
||||||
cmd_out .=+ Just out
|
cmd_out .=+ Just out
|
||||||
next
|
next
|
||||||
CmdParserGrouped groupName next -> do
|
CmdParserGrouped groupName next -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] groupName stackCur
|
mSet $ StackLayer mempty groupName stackCur
|
||||||
next
|
next
|
||||||
CmdParserGroupEnd next -> do
|
CmdParserGroupEnd next -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
|
@ -887,19 +958,19 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
StackLayer _descs "" _up -> do
|
StackLayer _descs "" _up -> do
|
||||||
return ()
|
return ()
|
||||||
StackLayer descs groupName up -> do
|
StackLayer descs groupName up -> do
|
||||||
mSet $ descStackAdd (PartRedirect groupName (PartSeq (reverse descs))) up
|
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
|
||||||
next
|
next
|
||||||
CmdParserReorderStop next -> do
|
CmdParserReorderStop next -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
case stackCur of
|
case stackCur of
|
||||||
StackBottom{} -> return ()
|
StackBottom{} -> return ()
|
||||||
StackLayer descs "" up -> do
|
StackLayer descs "" up -> do
|
||||||
mSet $ descStackAdd (PartReorder (reverse descs)) up
|
mSet $ descStackAdd (PartReorder (Data.Foldable.toList descs)) up
|
||||||
StackLayer{} -> return ()
|
StackLayer{} -> return ()
|
||||||
next
|
next
|
||||||
CmdParserReorderStart next -> do
|
CmdParserReorderStart next -> do
|
||||||
stackCur <- mGet
|
stackCur <- mGet
|
||||||
mSet $ StackLayer [] "" stackCur
|
mSet $ StackLayer mempty "" stackCur
|
||||||
next
|
next
|
||||||
|
|
||||||
failureCurrentShallowRerun
|
failureCurrentShallowRerun
|
||||||
|
@ -921,9 +992,8 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
postProcessCmd descStack cmd
|
postProcessCmd descStack cmd
|
||||||
= descFixParents
|
= descFixParents
|
||||||
$ cmd { _cmd_parts = case descStack of
|
$ cmd { _cmd_parts = case descStack of
|
||||||
StackBottom l -> reverse l
|
StackBottom l -> Data.Foldable.toList l
|
||||||
StackLayer{} -> []
|
StackLayer{} -> []
|
||||||
, _cmd_children = reverse $ _cmd_children cmd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
monadMisuseError :: a
|
monadMisuseError :: a
|
||||||
|
@ -955,6 +1025,45 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
|
||||||
InputString s -> mSet $ InputString $ dropWhile Char.isSpace s
|
InputString s -> mSet $ InputString $ dropWhile Char.isSpace s
|
||||||
InputArgs{} -> return ()
|
InputArgs{} -> return ()
|
||||||
|
|
||||||
|
inputToString :: Input -> String
|
||||||
|
inputToString (InputString s) = s
|
||||||
|
inputToString (InputArgs ss) = List.unwords ss
|
||||||
|
|
||||||
|
dequeLookupRemove :: String -> Deque (String, a) -> (Maybe a, Deque (String, a))
|
||||||
|
dequeLookupRemove key deque = case Deque.uncons deque of
|
||||||
|
Nothing -> (Nothing, mempty)
|
||||||
|
Just ((k, v), rest) -> if k==key
|
||||||
|
then (Just v, rest)
|
||||||
|
else let (r, rest') = dequeLookupRemove key rest
|
||||||
|
in (r, Deque.cons (k, v) rest')
|
||||||
|
|
||||||
|
takeCommandChild
|
||||||
|
:: MonadMultiState (CommandDesc out) m
|
||||||
|
=> String
|
||||||
|
-> m (Maybe (CommandDesc out))
|
||||||
|
takeCommandChild key = do
|
||||||
|
cmd <- mGet
|
||||||
|
let (r, children') = dequeLookupRemove key $ _cmd_children cmd
|
||||||
|
mSet cmd { _cmd_children = children' }
|
||||||
|
return r
|
||||||
|
|
||||||
|
mapOut :: (outa -> outb) -> CmdParser f outa () -> CmdParser f outb ()
|
||||||
|
mapOut f = hoistFree $ \case
|
||||||
|
CmdParserHelp doc r -> CmdParserHelp doc r
|
||||||
|
CmdParserSynopsis s r -> CmdParserSynopsis s r
|
||||||
|
CmdParserPeekDesc fr -> CmdParserPeekDesc fr
|
||||||
|
CmdParserPeekInput fr -> CmdParserPeekInput fr
|
||||||
|
CmdParserPart desc fp fa fr -> CmdParserPart desc fp fa fr
|
||||||
|
CmdParserPartMany bound desc fp fa fr -> CmdParserPartMany bound desc fp fa fr
|
||||||
|
CmdParserPartInp desc fp fa fr -> CmdParserPartInp desc fp fa fr
|
||||||
|
CmdParserPartManyInp bound desc fp fa fr ->
|
||||||
|
CmdParserPartManyInp bound desc fp fa fr
|
||||||
|
CmdParserChild s child act r -> CmdParserChild s (mapOut f child) act r
|
||||||
|
CmdParserImpl out r -> CmdParserImpl (f out) r
|
||||||
|
CmdParserReorderStart r -> CmdParserReorderStart r
|
||||||
|
CmdParserReorderStop r -> CmdParserReorderStop r
|
||||||
|
CmdParserGrouped s r -> CmdParserGrouped s r
|
||||||
|
CmdParserGroupEnd r -> CmdParserGroupEnd r
|
||||||
|
|
||||||
-- cmdActionPartial :: CommandDesc out -> Either String out
|
-- cmdActionPartial :: CommandDesc out -> Either String out
|
||||||
-- cmdActionPartial = maybe (Left err) Right . _cmd_out
|
-- cmdActionPartial = maybe (Left err) Right . _cmd_out
|
||||||
|
|
|
@ -59,7 +59,8 @@ data ManyUpperBound
|
||||||
data CmdParserF f out a
|
data CmdParserF f out a
|
||||||
= CmdParserHelp PP.Doc a
|
= CmdParserHelp PP.Doc a
|
||||||
| CmdParserSynopsis String a
|
| CmdParserSynopsis String a
|
||||||
| CmdParserPeekDesc (CommandDesc out -> a)
|
| CmdParserPeekDesc (CommandDesc () -> a)
|
||||||
|
| CmdParserPeekInput (String -> a)
|
||||||
-- TODO: we can clean up this duplication by providing
|
-- TODO: we can clean up this duplication by providing
|
||||||
-- a function (String -> Maybe (p, String)) -> (Input -> Maybe (p, Input)).
|
-- a function (String -> Maybe (p, String)) -> (Input -> Maybe (p, Input)).
|
||||||
| forall p . Typeable p => CmdParserPart PartDesc (String -> Maybe (p, String)) (p -> f ()) (p -> a)
|
| forall p . Typeable p => CmdParserPart PartDesc (String -> Maybe (p, String)) (p -> f ()) (p -> a)
|
||||||
|
@ -112,7 +113,9 @@ data CommandDesc out = CommandDesc
|
||||||
, _cmd_help :: Maybe PP.Doc
|
, _cmd_help :: Maybe PP.Doc
|
||||||
, _cmd_parts :: [PartDesc]
|
, _cmd_parts :: [PartDesc]
|
||||||
, _cmd_out :: Maybe out
|
, _cmd_out :: Maybe out
|
||||||
, _cmd_children :: [(String, CommandDesc out)]
|
, _cmd_children :: Deque (String, CommandDesc out) -- we don't use a Map here
|
||||||
|
-- because we'd like to
|
||||||
|
-- retain the order.
|
||||||
}
|
}
|
||||||
|
|
||||||
-- type PartSeqDesc = [PartDesc]
|
-- type PartSeqDesc = [PartDesc]
|
||||||
|
@ -164,7 +167,7 @@ deriving instance Functor CommandDesc
|
||||||
--
|
--
|
||||||
|
|
||||||
emptyCommandDesc :: CommandDesc out
|
emptyCommandDesc :: CommandDesc out
|
||||||
emptyCommandDesc = CommandDesc Nothing Nothing Nothing [] Nothing []
|
emptyCommandDesc = CommandDesc Nothing Nothing Nothing [] Nothing mempty
|
||||||
|
|
||||||
instance Show (CommandDesc out) where
|
instance Show (CommandDesc out) where
|
||||||
show c = "Command help=" ++ show (_cmd_help c)
|
show c = "Command help=" ++ show (_cmd_help c)
|
||||||
|
|
|
@ -56,22 +56,25 @@ import UI.Butcher.Monadic.Internal.Core
|
||||||
-- | ppUsage exampleDesc yields:
|
-- | ppUsage exampleDesc yields:
|
||||||
--
|
--
|
||||||
-- > playground [--short] NAME [version | help]
|
-- > playground [--short] NAME [version | help]
|
||||||
ppUsage :: CommandDesc a
|
ppUsage :: CommandDesc a -> PP.Doc
|
||||||
-> PP.Doc
|
ppUsage (CommandDesc mParent _help _syn parts out children) = pparents mParent
|
||||||
ppUsage (CommandDesc mParent _help _syn parts out children) =
|
<+> PP.fsep (partDocs ++ [subsDoc])
|
||||||
pparents mParent <+> PP.fsep (partDocs ++ [subsDoc])
|
where
|
||||||
where
|
pparents :: Maybe (String, CommandDesc out) -> PP.Doc
|
||||||
pparents :: Maybe (String, CommandDesc out) -> PP.Doc
|
pparents Nothing = PP.empty
|
||||||
pparents Nothing = PP.empty
|
pparents (Just (n, cd)) = pparents (_cmd_mParent cd) <+> PP.text n
|
||||||
pparents (Just (n, cd)) = pparents (_cmd_mParent cd) <+> PP.text n
|
partDocs = parts <&> ppPartDescUsage
|
||||||
partDocs = parts <&> ppPartDescUsage
|
subsDoc = case out of
|
||||||
subsDoc = case out of
|
_ | null children -> PP.empty -- TODO: remove debug
|
||||||
_ | null children -> PP.empty -- TODO: remove debug
|
Nothing | null parts -> subDoc
|
||||||
Nothing | null parts -> subDoc
|
| otherwise -> PP.parens $ subDoc
|
||||||
| otherwise -> PP.parens $ subDoc
|
Just{} -> PP.brackets $ subDoc
|
||||||
Just{} -> PP.brackets $ subDoc
|
subDoc =
|
||||||
subDoc = PP.fcat $ PP.punctuate (PP.text " | ") $ children <&> \(n, _) ->
|
PP.fcat
|
||||||
PP.text n
|
$ PP.punctuate (PP.text " | ")
|
||||||
|
$ Data.Foldable.toList
|
||||||
|
$ children
|
||||||
|
<&> \(n, _) -> PP.text n
|
||||||
|
|
||||||
-- | ppUsageWithHelp exampleDesc yields:
|
-- | ppUsageWithHelp exampleDesc yields:
|
||||||
--
|
--
|
||||||
|
@ -81,22 +84,26 @@ ppUsage (CommandDesc mParent _help _syn parts out children) =
|
||||||
-- And yes, the line break is not optimal in this instance with default print.
|
-- And yes, the line break is not optimal in this instance with default print.
|
||||||
ppUsageWithHelp :: CommandDesc a -> PP.Doc
|
ppUsageWithHelp :: CommandDesc a -> PP.Doc
|
||||||
ppUsageWithHelp (CommandDesc mParent help _syn parts out children) =
|
ppUsageWithHelp (CommandDesc mParent help _syn parts out children) =
|
||||||
pparents mParent <+> PP.fsep (partDocs ++ [subsDoc]) PP.<> helpDoc
|
pparents mParent <+> PP.fsep (partDocs ++ [subsDoc]) PP.<> helpDoc
|
||||||
where
|
where
|
||||||
pparents :: Maybe (String, CommandDesc out) -> PP.Doc
|
pparents :: Maybe (String, CommandDesc out) -> PP.Doc
|
||||||
pparents Nothing = PP.empty
|
pparents Nothing = PP.empty
|
||||||
pparents (Just (n, cd)) = pparents (_cmd_mParent cd) <+> PP.text n
|
pparents (Just (n, cd)) = pparents (_cmd_mParent cd) <+> PP.text n
|
||||||
partDocs = parts <&> ppPartDescUsage
|
partDocs = parts <&> ppPartDescUsage
|
||||||
subsDoc = case out of
|
subsDoc = case out of
|
||||||
_ | null children -> PP.empty -- TODO: remove debug
|
_ | null children -> PP.empty -- TODO: remove debug
|
||||||
Nothing | null parts -> subDoc
|
Nothing | null parts -> subDoc
|
||||||
| otherwise -> PP.parens $ subDoc
|
| otherwise -> PP.parens $ subDoc
|
||||||
Just{} -> PP.brackets $ subDoc
|
Just{} -> PP.brackets $ subDoc
|
||||||
subDoc = PP.fcat $ PP.punctuate (PP.text " | ") $ children <&> \(n, _) ->
|
subDoc =
|
||||||
PP.text n
|
PP.fcat
|
||||||
helpDoc = case help of
|
$ PP.punctuate (PP.text " | ")
|
||||||
Nothing -> PP.empty
|
$ Data.Foldable.toList
|
||||||
Just h -> PP.text ":" PP.<+> h
|
$ children
|
||||||
|
<&> \(n, _) -> PP.text n
|
||||||
|
helpDoc = case help of
|
||||||
|
Nothing -> PP.empty
|
||||||
|
Just h -> PP.text ":" PP.<+> h
|
||||||
|
|
||||||
-- | > ppUsageAt [] = ppUsage
|
-- | > ppUsageAt [] = ppUsage
|
||||||
--
|
--
|
||||||
|
|
|
@ -91,7 +91,8 @@ import qualified Data.List as List
|
||||||
|
|
||||||
import qualified Data.IntMap as IntMap
|
import qualified Data.IntMap as IntMap
|
||||||
import qualified Data.IntMap.Strict as IntMapS
|
import qualified Data.IntMap.Strict as IntMapS
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map.Strict as MapS
|
||||||
|
import qualified Data.Map.Lazy as MapL
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
|
@ -128,6 +129,10 @@ import Data.Sequence ( Seq )
|
||||||
import Data.Map ( Map )
|
import Data.Map ( Map )
|
||||||
import Data.Set ( Set )
|
import Data.Set ( Set )
|
||||||
|
|
||||||
|
import Deque ( Deque )
|
||||||
|
|
||||||
|
import qualified Deque
|
||||||
|
|
||||||
import Prelude ( Char
|
import Prelude ( Char
|
||||||
, String
|
, String
|
||||||
, Int
|
, Int
|
||||||
|
|
Loading…
Reference in New Issue