Change how subparsers are executed:

Previously seachparser would return a Maybe a, which would then be
wrapped in a NilP . Just and swapped into the parser. This means
that subparsers needed to execute completely before returning, and
gives a somewhat unsatifactory notion of backtracking, where one
could supply commands to the top level, but only after fully
completing the subparser.

If instead of returning a Maybe a, we return a Maybe (Parse a) then
for flags, args, and options we can bahave the same and just wrap a
little earlier, but for subcommands we can do something a bit different
and replace the subparser with the contents of the selected command.

This means that we're essentially inlining the selected subparser,
and all parent options are still 'in scope' of the current parse.

Given that we can still enter the context (but not exit it), not much
changes with this, but backtracking is now quite different and can not
be turned off.
This commit is contained in:
Huw Campbell 2016-06-17 08:04:29 +10:00 committed by Huw Campbell
parent b04118b039
commit 9108ccd1f6

View File

@ -51,7 +51,7 @@ module Options.Applicative.Common (
) where
import Control.Applicative
import Control.Monad (guard, mzero, msum, when, liftM)
import Control.Monad (guard, mzero, msum, when)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (StateT(..), get, put, runStateT)
import Data.List (isPrefixOf)
@ -79,22 +79,6 @@ isOptionPrefix _ _ = False
liftOpt :: Option a -> Parser a
liftOpt = OptP
argMatches :: MonadP m => OptReader a -> String
-> Maybe (StateT Args m a)
argMatches opt arg = case opt of
ArgReader rdr -> Just . lift $
runReadM (crReader rdr) arg
CmdReader _ _ f ->
flip fmap (f arg) $ \subp -> StateT $ \args -> do
prefs <- getPrefs
let runSubparser
| prefBacktrack prefs = \i a ->
runParser (infoPolicy i) CmdStart (infoParser i) a
| otherwise = \i a
-> (,) <$> runParserInfo i a <*> pure []
enterContext arg subp *> runSubparser subp args <* exitContext
_ -> Nothing
optMatches :: MonadP m => Bool -> OptReader a -> OptWord -> Maybe (StateT Args m a)
optMatches disambiguate opt (OptWord arg1 val) = case opt of
OptReader names rdr no_arg_err -> do
@ -150,10 +134,10 @@ parseWord ('-' : w) = case w of
parseWord _ = Nothing
searchParser :: Monad m
=> (forall r . Option r -> NondetT m r)
=> (forall r . Option r -> NondetT m (Parser r))
-> Parser a -> NondetT m (Parser a)
searchParser _ (NilP _) = mzero
searchParser f (OptP opt) = liftM pure (f opt)
searchParser f (OptP opt) = f opt
searchParser f (MultP p1 p2) = foldr1 (<!>)
[ do p1' <- searchParser f p1
return (p1' <*> p2)
@ -175,16 +159,23 @@ searchOpt pprefs w = searchParser $ \opt -> do
let disambiguate = prefDisambiguate pprefs
&& optVisibility opt > Internal
case optMatches disambiguate (optMain opt) w of
Just matcher -> lift matcher
Just matcher -> lift $ fmap pure matcher
Nothing -> mzero
searchArg :: MonadP m => String -> Parser a
-> NondetT (StateT Args m) (Parser a)
searchArg arg = searchParser $ \opt -> do
when (isArg (optMain opt)) cut
case argMatches (optMain opt) arg of
Just matcher -> lift matcher
Nothing -> mzero
case optMain opt of
CmdReader _ _ f ->
case f arg of
Just subp -> do
lift . lift $ enterContext arg subp
return $ infoParser subp
Nothing -> mzero
ArgReader rdr ->
fmap pure . lift . lift $ runReadM (crReader rdr) arg
_ -> mzero
stepParser :: MonadP m => ParserPrefs -> ArgPolicy -> String
-> Parser a -> NondetT (StateT Args m) (Parser a)