First cut at ReadM shakeup

This commit is contained in:
Huw Campbell 2018-01-29 19:00:08 +11:00
parent ff58895437
commit f3e7b0011c
8 changed files with 51 additions and 44 deletions

View File

@ -114,6 +114,7 @@ module Options.Applicative (
HasCompleter,
HasValue,
HasMetavar,
-- ** Readers
--
-- | A reader is used by the 'option' and 'argument' builders to parse

View File

@ -92,7 +92,7 @@ bashCompletionQuery pinfo pprefs richness ws i _ = case runCompletion compl ppre
-- For options and flags, ensure that the user
-- hasn't disabled them with `--`.
opt_completions argPolicy hinfo opt = case optMain opt of
OptReader ns _ _
OptReader ns _
| argPolicy /= AllPositionals
-> return . add_opt_help opt $ show_names ns
| otherwise

View File

@ -189,7 +189,8 @@ helpDoc doc = optionMod $ \p -> p { propHelp = Chunk doc }
-- | Specify the error to display when no argument is provided to this option.
noArgError :: ParseError -> Mod OptionFields a
noArgError e = fieldMod $ \p -> p { optNoArgError = const e }
noArgError = const mempty
{-# WARNING noArgError "This builder no longer has any effect. Please examine the ReadM documentation" #-}
-- | Specify a metavariable for the argument.
--
@ -337,8 +338,7 @@ switch = flag False True
-- 'infoOption' instead.
abortOption :: ParseError -> Mod OptionFields (a -> a) -> Parser (a -> a)
abortOption err m = option (readerAbort err) . (`mappend` m) $ mconcat
[ noArgError err
, value id
[ value id
, metavar "" ]
-- | An option that always fails and displays a message.
@ -365,9 +365,9 @@ option :: ReadM a -> Mod OptionFields a -> Parser a
option r m = mkParser d g rdr
where
Mod f d g = metavar "ARG" `mappend` m
fields = f (OptionFields [] mempty ExpectsArgError)
fields = f (OptionFields [] mempty)
crdr = CReader (optCompleter fields) r
rdr = OptReader (optNames fields) crdr (optNoArgError fields)
rdr = OptReader (optNames fields) crdr
-- | Modifier for 'ParserInfo'.
newtype InfoMod a = InfoMod

View File

@ -33,8 +33,7 @@ import Options.Applicative.Types
data OptionFields a = OptionFields
{ optNames :: [OptName]
, optCompleter :: Completer
, optNoArgError :: String -> ParseError }
, optCompleter :: Completer }
data FlagFields a = FlagFields
{ flagNames :: [OptName]
@ -160,9 +159,10 @@ mkParser :: DefaultProp a
-> (OptProperties -> OptProperties)
-> OptReader a
-> Parser a
mkParser d@(DefaultProp def _) g rdr = liftOpt opt <|> maybe empty pure def
where
opt = mkOption d g rdr
mkParser d@(DefaultProp def _) g rdr =
maybe opt (\def' -> opt <|> pure def') def
where
opt = liftOpt $ mkOption d g rdr
mkOption :: DefaultProp a
-> (OptProperties -> OptProperties)

View File

@ -66,7 +66,7 @@ showOption (OptLong n) = "--" ++ n
showOption (OptShort n) = '-' : [n]
optionNames :: OptReader a -> [OptName]
optionNames (OptReader names _ _) = names
optionNames (OptReader names _) = names
optionNames (FlagReader names _) = names
optionNames _ = []
@ -82,8 +82,8 @@ 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
ArgReader rdr -> Just $ StateT $ \args ->
execReadM (crReader rdr) (arg : args)
CmdReader _ _ f ->
flip fmap (f arg) $ \subp -> StateT $ \args -> do
prefs <- getPrefs
@ -97,15 +97,11 @@ argMatches opt arg = case opt of
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
OptReader names rdr -> do
guard $ has_name arg1 names
Just $ do
args <- get
let mb_args = uncons $ maybeToList val ++ args
let missing_arg = missingArgP (no_arg_err $ showOption arg1) (crCompleter rdr)
(arg', args') <- maybe (lift missing_arg) return mb_args
put args'
lift $ runReadM (withReadM (errorFor arg1) (crReader rdr)) arg'
return $ StateT $ \args -> do
let mb_args = maybeToList val ++ args
execReadM (withReadM (errorFor arg1) (crReader rdr)) mb_args
FlagReader names x -> do
guard $ has_name arg1 names

View File

@ -258,7 +258,7 @@ parserFailure pprefs pinfo msg ctx = ParserFailure $ \progn ->
-- reader also ensure that it can be immediately
-- reachable from where the error was given.
opt_completions hinfo opt = case optMain opt of
OptReader ns _ _ -> fmap showOption ns
OptReader ns _ -> fmap showOption ns
FlagReader ns _ -> fmap showOption ns
ArgReader _ -> []
CmdReader _ ns _ | hinfoUnreachableArgs hinfo

View File

@ -7,6 +7,7 @@ module Options.Applicative.Internal
, hoistMaybe
, hoistEither
, runReadM
, execReadM
, withReadM
, runP
@ -30,9 +31,9 @@ import Prelude
import Control.Monad (MonadPlus(..), liftM, ap, guard)
import Control.Monad.Trans.Class (MonadTrans, lift)
import Control.Monad.Trans.Except
(runExcept, runExceptT, withExcept, ExceptT(..), throwE, catchE)
(runExceptT, ExceptT(..), throwE, catchE)
import Control.Monad.Trans.Reader
(mapReaderT, runReader, runReaderT, Reader, ReaderT, ask)
(runReader, runReaderT, Reader, ReaderT, ask)
import Control.Monad.Trans.State (StateT, get, put, modify, evalStateT, runStateT)
import Options.Applicative.Types
@ -96,11 +97,14 @@ uncons :: [a] -> Maybe (a, [a])
uncons [] = Nothing
uncons (x : xs) = Just (x, xs)
runReadM :: MonadP m => ReadM a -> String -> m a
runReadM (ReadM r) s = hoistEither . runExcept $ runReaderT r s
execReadM :: MonadP m => ReadM a -> [String] -> m (a, [String])
execReadM (ReadM r) s = hoistEither $ r s
withReadM :: (String -> String) -> ReadM a -> ReadM a
withReadM f = ReadM . mapReaderT (withExcept f') . unReadM
withReadM f (ReadM r)= ReadM $ \input ->
case r input of
Left e -> Left (f' e)
Right x -> Right x
where
f' (ErrorMsg err) = ErrorMsg (f err)
f' e = e

View File

@ -45,9 +45,6 @@ module Options.Applicative.Types (
import Control.Applicative
import Control.Monad (ap, liftM, MonadPlus, mzero, mplus)
import Control.Monad.Trans.Except (Except, throwE)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT, ask)
import qualified Control.Monad.Fail as Fail
import Data.Semigroup hiding (Option)
import Prelude
@ -157,16 +154,17 @@ instance Show (Option a) where
instance Functor Option where
fmap f (Option m p) = Option (fmap f m) p
-- | A newtype over 'ReaderT String Except', used by option readers.
newtype ReadM a = ReadM
{ unReadM :: ReaderT String (Except ParseError) a }
newtype ReadM a = ReadM { runReadM :: [String] -> Either ParseError (a, [String]) }
instance Functor ReadM where
fmap f (ReadM r) = ReadM (fmap f r)
fmap f (ReadM r) = ReadM $ \input ->
case r input of
Right (a, rest) -> Right (f a, rest)
Left err -> Left err
instance Applicative ReadM where
pure = ReadM . pure
ReadM x <*> ReadM y = ReadM $ x <*> y
pure a = ReadM $ \inputs -> Right (a, inputs)
(<*>) = ap
instance Alternative ReadM where
empty = mzero
@ -174,23 +172,31 @@ instance Alternative ReadM where
instance Monad ReadM where
return = pure
ReadM r >>= f = ReadM $ r >>= unReadM . f
ReadM r >>= f = ReadM $ \inputs -> do
(a, rest) <- r inputs
runReadM (f a) rest
fail = Fail.fail
instance Fail.MonadFail ReadM where
fail = readerError
instance MonadPlus ReadM where
mzero = ReadM mzero
mplus (ReadM x) (ReadM y) = ReadM $ mplus x y
mzero = ReadM $ const (Left UnknownError)
mplus (ReadM x) (ReadM y) = ReadM $ \input ->
case x input of
Right res -> Right res
Left _ -> y input
-- | Return the value being read.
readerAsk :: ReadM String
readerAsk = ReadM ask
readerAsk = ReadM $ \input -> case input of
x:xs -> Right (x, xs)
[] -> Left (ErrorMsg "expected an argument")
-- | Abort option reader by exiting with a 'ParseError'.
readerAbort :: ParseError -> ReadM a
readerAbort = ReadM . lift . throwE
readerAbort e = ReadM $ \_ -> Left e
-- | Abort option reader by exiting with an error message.
readerError :: String -> ReadM a
@ -205,7 +211,7 @@ instance Functor CReader where
-- | An 'OptReader' defines whether an option matches an command line argument.
data OptReader a
= OptReader [OptName] (CReader a) (String -> ParseError)
= OptReader [OptName] (CReader a)
-- ^ option reader
| FlagReader [OptName] !a
-- ^ flag reader
@ -215,7 +221,7 @@ data OptReader a
-- ^ command reader
instance Functor OptReader where
fmap f (OptReader ns cr e) = OptReader ns (fmap f cr) e
fmap f (OptReader ns cr) = OptReader ns (fmap f cr)
fmap f (FlagReader ns x) = FlagReader ns (f x)
fmap f (ArgReader cr) = ArgReader (fmap f cr)
fmap f (CmdReader n cs g) = CmdReader n cs ((fmap . fmap) f . g)