multiarg-0.30.0.0: lib/Multiarg/Examples/Grover.hs
-- | Grover is a simple example program that shows how to write a
-- parser for commands with multiple modes. You build such parsers
-- using "Multiarg.Mode". It provides an example for the
-- documentation, and it also provides fodder for the QuickCheck
-- tests. You will want to look at the source code.
--
-- Grover has three modes: @int@, @string@, and @maybe@. Each of
-- these modes has three options: @-z@ or @--zero@, which takes no
-- arguments; @-s@ or @--single@, which takes one argument; @-d@ or
-- @--double@, which takes two arguments; and @-t@ or @--triple@,
-- which takes three arguments. The type of the argument depends on
-- the mode. For @int@, the argument or arguments must be an integer;
-- for @string@ the arguments can be any string; and for @maybe@ the
-- arguments must be a Maybe Int, such as @Nothing@ or @Just 5@.
--
-- Each mode also accepts any number of positional arguments, which
-- can be any string.
--
-- Grover handles simple errors right inside the parser by using the
-- @Either@ type as a return value.
module Multiarg.Examples.Grover where
import Control.Applicative
import Multiarg.Mode
import Text.Read (readMaybe)
-- | Grover's global options.
data Global
= Help
| Verbose Int
-- ^ The Int would indicate, for example, the desired level of
-- verbosity.
| Version
deriving (Eq, Ord, Show)
-- | Handles all options and positional arguments for any Grover mode.
data GroverOpt a
= Zero
| Single a
| Double a a
| Triple a a a
| PosArg String
deriving (Eq, Ord, Show)
instance Functor GroverOpt where
fmap f g = case g of
Zero -> Zero
Single a -> Single (f a)
Double a b -> Double (f a) (f b)
Triple a b c -> Triple (f a) (f b) (f c)
PosArg a -> PosArg a
-- | All of Grover's global options. The 'OptSpec' is parameterized
-- on an 'Either' to allow for error handling. If the user enters a
-- non-integer argument for the @--verbose@ option, a @Left@ with an
-- error message is returned.
globalOptSpecs :: [OptSpec (Either String Global)]
globalOptSpecs =
[ optSpec "h" ["help"] . ZeroArg . return $ Help
, optSpec "v" ["verbose"] . OneArg $ \s ->
Verbose <$> readErr s
, optSpec "" ["version"] . ZeroArg . return $ Version
]
-- | A list of 'OptSpec' that works for any 'Mode'.
modeOptSpecs :: Read a => [OptSpec (Either String (GroverOpt a))]
modeOptSpecs =
[ optSpec "z" ["zero"] . ZeroArg . Right $ Zero
, optSpec "s" ["single"] . OneArg $ \s -> Single <$> readErr s
, optSpec "d" ["double"] . TwoArg $ \s1 s2 ->
Double <$> readErr s1 <*> readErr s2
, optSpec "t" ["triple"] . ThreeArg $ \s1 s2 s3 ->
Triple <$> readErr s1 <*> readErr s2 <*> readErr s3
]
-- | Holds the results of parsing Grover's modes.
data Result
= Ints [Either String (GroverOpt Int)]
| Strings [Either String (GroverOpt String)]
| Maybes [Either String (GroverOpt (Maybe Int))]
deriving (Eq, Ord, Show)
-- | All Grover modes.
modes :: [Mode Result]
modes =
[ mode "int" modeOptSpecs (return . PosArg) Ints
, mode "string" modeOptSpecs (return . PosArg) Strings
, mode "maybe" modeOptSpecs (return . PosArg) Maybes
]
-- | Reads a value. If it cannot be read, returns an error message.
readErr :: Read a => String -> Either String a
readErr s = case readMaybe s of
Nothing -> Left $ "could not read value: " ++ s
Just a -> Right a
-- | Parses all of Grover's options and modes.
parseGrover
:: [String]
-- ^ Command line arguments, presumably from 'getArgs'
-> Either (String, [String])
(ModeResult (Either String Global) Result)
-- ^ Returns a 'Left' if there are errors, or a 'Right' if there are
-- no errors. (In an actual application, further processing of a
-- 'Right' would be necessary to determine whether all entered
-- arguments were valid.)
parseGrover = parseModeLine globalOptSpecs modes