Apply autoformatting and Refactor slightly

pull/5/head
Lennart Spitzner 2018-01-10 00:53:51 +01:00
parent 0f5aa00bb3
commit 548f2ccd8f
1 changed files with 784 additions and 723 deletions

View File

@ -5,7 +5,6 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TemplateHaskell #-}
module UI.Butcher.Monadic.Internal.Core
( addCmdSynopsis
, addCmdHelp
@ -39,14 +38,21 @@ where
#include "prelude.inc"
import Control.Monad.Free
import qualified Control.Monad.Trans.MultiRWS.Strict as MultiRWSS
import qualified Control.Monad.Trans.MultiState.Strict as MultiStateS
import qualified Control.Monad.Trans.MultiRWS.Strict
as MultiRWSS
import qualified Control.Monad.Trans.MultiState.Strict
as MultiStateS
import qualified Lens.Micro as Lens
import Lens.Micro ( (%~), (.~) )
import Lens.Micro ( (%~)
, (.~)
)
import qualified Text.PrettyPrint as PP
import Text.PrettyPrint ( (<+>), ($$), ($+$) )
import Text.PrettyPrint ( (<+>)
, ($$)
, ($+$)
)
import Data.HList.ContainsType
@ -67,12 +73,10 @@ mModify f = mGet >>= mSet . f
-- arising around s in the signatures below. That's the price of not having
-- the functional dependency in MonadMulti*T.
(.=+) :: MonadMultiState s m
=> Lens.ASetter s s a b -> b -> m ()
(.=+) :: MonadMultiState s m => Lens.ASetter s s a b -> b -> m ()
l .=+ b = mModify $ l .~ b
(%=+) :: MonadMultiState s m
=> Lens.ASetter s s a b -> (a -> b) -> m ()
(%=+) :: MonadMultiState s m => Lens.ASetter s s a b -> (a -> b) -> m ()
l %=+ f = mModify (l %~ f)
-- inflateStateProxy :: (Monad m, ContainsType s ss)
@ -306,29 +310,35 @@ descStackAdd d = \case
-- This method also yields a _complete_ @CommandDesc@ output, where the other
-- runCmdParser* functions all traverse only a shallow structure around the
-- parts of the 'CmdParser' touched while parsing the current input.
checkCmdParser :: forall f out
checkCmdParser
:: forall f out
. Maybe String -- ^ top-level command name
-> CmdParser f out () -- ^ parser to check
-> Either String (CommandDesc ())
checkCmdParser mTopLevel cmdParser
= (>>= final)
checkCmdParser mTopLevel cmdParser =
(>>= final)
$ MultiRWSS.runMultiRWSTNil
$ MultiRWSS.withMultiStateAS (StackBottom mempty)
$ MultiRWSS.withMultiStateS emptyCommandDesc
$ processMain cmdParser
where
final :: (CommandDesc out, CmdDescStack)
-> Either String (CommandDesc ())
final (desc, stack)
= case stack of
StackBottom descs -> Right
$ descFixParentsWithTopM (mTopLevel <&> \n -> (Just n, emptyCommandDesc))
$ () <$ desc
{ _cmd_parts = Data.Foldable.toList descs
}
final :: (CommandDesc out, CmdDescStack) -> Either String (CommandDesc ())
final (desc, stack) = case stack of
StackBottom descs ->
Right
$ descFixParentsWithTopM
(mTopLevel <&> \n -> (Just n, emptyCommandDesc))
$ ()
<$ desc { _cmd_parts = Data.Foldable.toList descs }
StackLayer _ _ _ -> Left "unclosed ReorderStart or GroupStart"
processMain :: CmdParser f out ()
-> MultiRWSS.MultiRWST '[] '[] '[CommandDesc out, CmdDescStack] (Either String) ()
processMain
:: CmdParser f out ()
-> MultiRWSS.MultiRWST
'[]
'[]
'[CommandDesc out, CmdDescStack]
(Either String)
()
processMain = \case
Pure x -> return x
Free (CmdParserHelp h next) -> do
@ -398,7 +408,9 @@ checkCmdParser mTopLevel cmdParser
StackLayer _descs "" _up -> do
lift $ Left $ "GroupEnd found, but expected ReorderStop first"
StackLayer descs groupName up -> do
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
mSet $ descStackAdd
(PartRedirect groupName (PartSeq (Data.Foldable.toList descs)))
up
processMain $ next
Free (CmdParserReorderStop next) -> do
stackCur <- mGet
@ -406,7 +418,8 @@ checkCmdParser mTopLevel cmdParser
StackBottom{} -> lift $ Left $ "ReorderStop without reorderStart"
StackLayer descs "" up -> do
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
Free (CmdParserReorderStart next) -> do
stackCur <- mGet
@ -414,7 +427,10 @@ checkCmdParser mTopLevel cmdParser
processMain next
monadMisuseError :: a
monadMisuseError = error "CmdParser definition error - used Monad powers where only Applicative/Arrow is allowed"
monadMisuseError =
error
$ "CmdParser definition error -"
++ " used Monad powers where only Applicative/Arrow is allowed"
newtype PastCommandInput = PastCommandInput Input
@ -433,9 +449,8 @@ runCmdParser
-> Input -- ^ input to be processed
-> CmdParser Identity out () -- ^ parser to use
-> (CommandDesc (), Either ParsingError (CommandDesc out))
runCmdParser mTopLevel inputInitial cmdParser
= runIdentity
$ runCmdParserA mTopLevel inputInitial cmdParser
runCmdParser mTopLevel inputInitial cmdParser =
runIdentity $ runCmdParserA mTopLevel inputInitial cmdParser
-- | Like 'runCmdParser', but also returning all input after the last
-- successfully parsed subcommand. E.g. for some input
@ -446,31 +461,34 @@ runCmdParserExt
-> Input -- ^ input to be processed
-> CmdParser Identity out () -- ^ parser to use
-> (CommandDesc (), Input, Either ParsingError (CommandDesc out))
runCmdParserExt mTopLevel inputInitial cmdParser
= runIdentity
$ runCmdParserAExt mTopLevel inputInitial cmdParser
runCmdParserExt mTopLevel inputInitial cmdParser =
runIdentity $ runCmdParserAExt mTopLevel inputInitial cmdParser
-- | The Applicative-enabled version of 'runCmdParser'.
runCmdParserA :: forall f out
runCmdParserA
:: forall f out
. Applicative f
=> Maybe String -- ^ program name to be used for the top-level @CommandDesc@
-> Input -- ^ input to be processed
-> CmdParser f out () -- ^ parser to use
-> f ( CommandDesc ()
, Either ParsingError (CommandDesc out)
)
-> f (CommandDesc (), Either ParsingError (CommandDesc out))
runCmdParserA mTopLevel inputInitial cmdParser =
(\(x, _, z) -> (x, z)) <$> runCmdParserAExt mTopLevel inputInitial cmdParser
-- | The Applicative-enabled version of 'runCmdParserExt'.
runCmdParserAExt
:: forall f out . Applicative f
:: forall f out
. Applicative f
=> Maybe String -- ^ program name to be used for the top-level @CommandDesc@
-> Input -- ^ input to be processed
-> CmdParser f out () -- ^ parser to use
-> f (CommandDesc (), Input, Either ParsingError (CommandDesc out))
runCmdParserAExt mTopLevel inputInitial cmdParser
= runIdentity
-> f
( CommandDesc ()
, Input
, Either ParsingError (CommandDesc out)
)
runCmdParserAExt mTopLevel inputInitial cmdParser =
runIdentity
$ MultiRWSS.runMultiRWSTNil
$ (<&> captureFinal)
$ MultiRWSS.withMultiWriterWA
@ -482,13 +500,20 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
$ processMain cmdParser
where
initialCommandDesc = emptyCommandDesc
{ _cmd_mParent = mTopLevel <&> \n -> (Just n, emptyCommandDesc) }
{ _cmd_mParent = mTopLevel <&> \n -> (Just n, emptyCommandDesc)
}
captureFinal
:: ([String], (CmdDescStack, (Input, (PastCommandInput, (CommandDesc out, f())))))
:: ( [String]
, (CmdDescStack, (Input, (PastCommandInput, (CommandDesc out, f ()))))
)
-> f (CommandDesc (), Input, Either ParsingError (CommandDesc out))
captureFinal (errs, (descStack, (inputRest, (PastCommandInput pastCmdInput, (cmd, act))))) =
act $> (() <$ cmd', pastCmdInput, res)
captureFinal tuple1 = act $> (() <$ cmd', pastCmdInput, res)
where
(errs , tuple2) = tuple1
(descStack , tuple3) = tuple2
(inputRest , tuple4) = tuple3
(PastCommandInput pastCmdInput, tuple5) = tuple4
(cmd , act ) = tuple5
errs' = errs ++ inputErrs ++ stackErrs
inputErrs = case inputRest of
InputString s | all Char.isSpace s -> []
@ -499,14 +524,17 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
StackBottom{} -> []
_ -> ["butcher interface error: unclosed group"]
cmd' = postProcessCmd descStack cmd
res = if null errs'
then Right cmd'
else Left $ ParsingError errs' inputRest
processMain :: CmdParser f out ()
res =
if null errs' then Right cmd' else Left $ ParsingError errs' inputRest
processMain
:: CmdParser f out ()
-> MultiRWSS.MultiRWS
'[]
'[[String]]
'[CommandDesc out, PastCommandInput, Input, CmdDescStack, CmdParser f out ()]
'[CommandDesc out, PastCommandInput, Input, CmdDescStack, CmdParser
f
out
()]
(f ())
processMain = \case
Pure () -> return $ pure $ ()
@ -525,13 +553,15 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
-- run the rest without affecting the actual stack
-- to retrieve the complete cmddesc.
cmdCur :: CommandDesc out <- mGet
let (cmd :: CommandDesc out, stack)
= runIdentity
let (cmd :: CommandDesc out, stack) =
runIdentity
$ MultiRWSS.runMultiRWSTNil
$ MultiRWSS.withMultiStateSA emptyCommandDesc
{ _cmd_mParent = _cmd_mParent cmdCur } -- partialDesc
{ _cmd_mParent = _cmd_mParent cmdCur
} -- partialDesc
$ MultiRWSS.withMultiStateS (StackBottom mempty) -- partialStack
$ iterM processCmdShallow $ parser
$ iterM processCmdShallow
$ parser
processMain $ nextF $ () <$ postProcessCmd stack cmd
Free (CmdParserPeekInput nextF) -> do
processMain $ nextF $ inputToString inputInitial
@ -642,32 +672,37 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
)
(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 <&> \(mname, (child, act, vis)) ->
combined_child_list =
Data.Foldable.toList child_name_list <&> \n -> (n, child_map MapS.! n)
let
mRest = asum $ combined_child_list <&> \(mname, (child, act, vis)) ->
case (mname, input) of
(Just name, InputString str) | name == str ->
Just $ (Just name, child, act, vis, InputString "")
(Just name, InputString str) | (name ++ " ") `isPrefixOf` str ->
Just $ (Just name, child, act, vis, InputString $ drop (length name + 1) str)
Just
$ ( Just name
, child
, act
, vis
, InputString $ drop (length name + 1) str
)
(Just name, InputArgs (str:strr)) | name == str ->
Just $ (Just name, child, act, vis, InputArgs strr)
(Nothing, _) ->
Just $ (Nothing, child, act, vis, input)
(Nothing, _) -> Just $ (Nothing, child, act, vis, input)
_ -> Nothing
case mRest of
Nothing -> do -- a child not matching what we have in the input
let initialDesc :: CommandDesc out = emptyCommandDesc
-- get the shallow desc for the child in a separate env.
combined_child_list `forM_` \(child_name, (child, _, vis)) -> do
let (subCmd, subStack)
= runIdentity
let (subCmd, subStack) =
runIdentity
$ MultiRWSS.runMultiRWSTNil
$ MultiRWSS.withMultiStateSA initialDesc
$ MultiRWSS.withMultiStateS (StackBottom mempty)
$ iterM processCmdShallow child
cmd_children
%=+ Deque.snoc
cmd_children %=+ Deque.snoc
( child_name
, postProcessCmd subStack subCmd { _cmd_visibility = vis }
)
@ -684,8 +719,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
return $ postProcessCmd prevStack c
mSet $ rest
mSet $ PastCommandInput rest
mSet $ emptyCommandDesc
{ _cmd_mParent = Just (name, cmd)
mSet $ emptyCommandDesc { _cmd_mParent = Just (name, cmd)
, _cmd_visibility = vis
}
mSet $ child
@ -707,21 +741,28 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
mTell $ ["butcher interface error: group end without group start"]
return $ pure () -- hard abort should be fine for this case.
StackLayer descs groupName up -> do
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
mSet $ descStackAdd
(PartRedirect groupName (PartSeq (Data.Foldable.toList descs)))
up
processMain $ next
Free (CmdParserReorderStop next) -> do
mTell $ ["butcher interface error: reorder stop without reorder start"]
processMain next
Free (CmdParserReorderStart next) -> do
reorderData <- MultiRWSS.withMultiStateA (1::Int)
reorderData <-
MultiRWSS.withMultiStateA (1 :: Int)
$ MultiRWSS.withMultiWriterW
$ iterM reorderPartGather $ next
$ iterM reorderPartGather
$ next
let
reorderMapInit :: Map Int (PartGatherData f)
reorderMapInit = MapS.fromList $ reorderData <&> \d -> (_pgd_id d, d)
tryParsePartData :: Input -> PartGatherData f -> First (Int, Dynamic, Input, Bool, f ())
tryParsePartData input (PartGatherData pid _ pfe act allowMany) =
First [ (pid, toDyn r, rest, allowMany, act r)
tryParsePartData
:: Input
-> PartGatherData f
-> First (Int, Dynamic, Input, Bool, f ())
tryParsePartData input (PartGatherData pid _ pfe act allowMany) = First
[ (pid, toDyn r, rest, allowMany, act r)
| (r, rest) <- case pfe of
Left pfStr -> case input of
InputString str -> case pfStr str of
@ -749,7 +790,8 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
mSet $ MapS.delete pid m
actRest <- parseLoop
return $ act *> actRest
(finalMap, (fr, acts)) <- MultiRWSS.withMultiStateSA (MapS.empty :: PartParsedData)
(finalMap, (fr, acts)) <-
MultiRWSS.withMultiStateSA (MapS.empty :: PartParsedData)
$ MultiRWSS.withMultiStateA reorderMapInit
$ do
acts <- parseLoop -- filling the map
@ -833,13 +875,15 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
-- run the rest without affecting the actual stack
-- to retrieve the complete cmddesc.
cmdCur :: CommandDesc out <- mGet
let (cmd :: CommandDesc out, stack)
= runIdentity
let (cmd :: CommandDesc out, stack) =
runIdentity
$ MultiRWSS.runMultiRWSTNil
$ MultiRWSS.withMultiStateSA emptyCommandDesc
{ _cmd_mParent = _cmd_mParent cmdCur } -- partialDesc
{ _cmd_mParent = _cmd_mParent cmdCur
} -- partialDesc
$ MultiRWSS.withMultiStateS (StackBottom mempty) -- partialStack
$ iterM processCmdShallow $ parser
$ iterM processCmdShallow
$ parser
childrenGather $ nextF $ () <$ postProcessCmd stack cmd
something -> return something
@ -859,10 +903,13 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
=> CmdParser f out a
-> m (CmdParser f out a)
processParsedParts = \case
Free (CmdParserPart desc _ _ (nextF :: p -> CmdParser f out a)) -> part desc nextF
Free (CmdParserPartInp desc _ _ (nextF :: p -> CmdParser f out a)) -> part desc nextF
Free (CmdParserPart desc _ _ (nextF :: p -> CmdParser f out a)) ->
part desc nextF
Free (CmdParserPartInp desc _ _ (nextF :: p -> CmdParser f out a)) ->
part desc nextF
Free (CmdParserPartMany bound desc _ _ nextF) -> partMany bound desc nextF
Free (CmdParserPartManyInp bound desc _ _ nextF) -> partMany bound desc nextF
Free (CmdParserPartManyInp bound desc _ _ nextF) ->
partMany bound desc nextF
Free (CmdParserReorderStop next) -> do
stackCur <- mGet
case stackCur of
@ -882,7 +929,9 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
mTell $ ["butcher interface error: group end without group start"]
return $ next -- hard abort should be fine for this case.
StackLayer descs groupName up -> do
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
mSet $ descStackAdd
(PartRedirect groupName (PartSeq (Data.Foldable.toList descs)))
up
processParsedParts $ next
Pure x -> return $ return $ x
f -> do
@ -905,8 +954,10 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
mSet $ MapS.delete pid parsedMap
partMap :: Map Int (PartGatherData f) <- mGet
input :: Input <- mGet
let errorResult = do
mTell ["could not parse expected input "
let
errorResult = do
mTell
[ "could not parse expected input "
++ getPartSeqDescPositionName desc
++ " with remaining input: "
++ show input
@ -914,8 +965,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
failureCurrentShallowRerun
processParsedParts $ nextF monadMisuseError
continueOrMisuse :: Maybe p -> m (CmdParser f out a)
continueOrMisuse = maybe monadMisuseError
(processParsedParts . nextF)
continueOrMisuse = maybe monadMisuseError (processParsedParts . nextF)
case MapS.lookup pid parsedMap of
Nothing -> case MapS.lookup pid partMap of
Nothing -> monadMisuseError -- it would still be in the map
@ -954,9 +1004,8 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
-- this does no error reporting at all.
-- user needs to use check for that purpose instead.
processCmdShallow :: ( MonadMultiState (CommandDesc out) m
, MonadMultiState CmdDescStack m
)
processCmdShallow
:: (MonadMultiState (CommandDesc out) m, MonadMultiState CmdDescStack m)
=> CmdParserF f out (m ())
-> m ()
processCmdShallow = \case
@ -1013,7 +1062,9 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
StackLayer _descs "" _up -> do
return ()
StackLayer descs groupName up -> do
mSet $ descStackAdd (PartRedirect groupName (PartSeq (Data.Foldable.toList descs))) up
mSet $ descStackAdd
(PartRedirect groupName (PartSeq (Data.Foldable.toList descs)))
up
next
CmdParserReorderStop next -> do
stackCur <- mGet
@ -1038,21 +1089,23 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
=> m ()
failureCurrentShallowRerun = do
parser <- mGet
cmd :: CommandDesc out
<- MultiRWSS.withMultiStateS emptyCommandDesc
cmd :: CommandDesc out <-
MultiRWSS.withMultiStateS emptyCommandDesc
$ iterM processCmdShallow parser
mSet cmd
postProcessCmd :: CmdDescStack -> CommandDesc out -> CommandDesc out
postProcessCmd descStack cmd
= descFixParents
$ cmd { _cmd_parts = case descStack of
postProcessCmd descStack cmd = descFixParents $ cmd
{ _cmd_parts = case descStack of
StackBottom l -> Data.Foldable.toList l
StackLayer{} -> []
}
monadMisuseError :: a
monadMisuseError = error "CmdParser definition error - used Monad powers where only Applicative/Arrow is allowed"
monadMisuseError =
error
$ "CmdParser definition error -"
++ " used Monad powers where only Applicative/Arrow is allowed"
getPartSeqDescPositionName :: PartDesc -> String
@ -1070,9 +1123,7 @@ runCmdParserAExt mTopLevel inputInitial cmdParser
PartSeq ds -> List.unwords $ f <$> ds
PartReorder ds -> List.unwords $ f <$> ds
PartHidden d -> f d
where
f = getPartSeqDescPositionName
where f = getPartSeqDescPositionName
dropSpaces :: MonadMultiState Input m => m ()
dropSpaces = do
@ -1090,7 +1141,8 @@ 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
else
let (r, rest') = dequeLookupRemove key rest
in (r, Deque.cons (k, v) rest')
takeCommandChild
@ -1156,27 +1208,36 @@ descFixParents = descFixParentsWithTopM Nothing
-- descFixParentsWithTop :: String -> CommandDesc a -> CommandDesc a
-- descFixParentsWithTop s = descFixParentsWithTopM (Just (s, emptyCommandDesc))
descFixParentsWithTopM :: Maybe (Maybe String, CommandDesc a) -> CommandDesc a -> CommandDesc a
descFixParentsWithTopM
:: Maybe (Maybe String, CommandDesc a) -> CommandDesc a -> CommandDesc a
descFixParentsWithTopM mTop topDesc = Data.Function.fix $ \fixed -> topDesc
{ _cmd_mParent = goUp fixed <$> (mTop <|> _cmd_mParent topDesc)
, _cmd_children = _cmd_children topDesc <&> goDown fixed
}
where
goUp :: CommandDesc a -> (Maybe String, CommandDesc a) -> (Maybe String, CommandDesc a)
goUp child (childName, parent) = (,) childName $ Data.Function.fix $ \fixed -> parent
goUp
:: CommandDesc a
-> (Maybe String, CommandDesc a)
-> (Maybe String, CommandDesc a)
goUp child (childName, parent) =
(,) childName $ Data.Function.fix $ \fixed -> parent
{ _cmd_mParent = goUp fixed <$> _cmd_mParent parent
, _cmd_children = _cmd_children parent <&> \(n, c) -> if n==childName
then (n, child)
else (n, c)
, _cmd_children = _cmd_children parent
<&> \(n, c) -> if n == childName then (n, child) else (n, c)
}
goDown :: CommandDesc a -> (Maybe String, CommandDesc a) -> (Maybe String, CommandDesc a)
goDown parent (childName, child) = (,) childName $ Data.Function.fix $ \fixed -> child
goDown
:: CommandDesc a
-> (Maybe String, CommandDesc a)
-> (Maybe String, CommandDesc a)
goDown parent (childName, child) =
(,) childName $ Data.Function.fix $ \fixed -> child
{ _cmd_mParent = Just (childName, parent)
, _cmd_children = _cmd_children child <&> goDown fixed
}
_tooLongText :: Int -- max length
_tooLongText
:: Int -- max length
-> String -- alternative if actual length is bigger than max.
-> String -- text to print, if length is fine.
-> PP.Doc