packages feed

multiarg 0.4.0.0 → 0.6.0.0

raw patch · 7 files changed

+201/−124 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ System.Console.MultiArg.Combinator: ChoiceArg :: [(String, a)] -> ArgSpec a
+ System.Console.MultiArg.Combinator: ThreeArg :: (String -> String -> String -> a) -> ArgSpec a
+ System.Console.MultiArg.Combinator: instance Functor ArgSpec
+ System.Console.MultiArg.Combinator: instance Functor OptSpec

Files

NEWS view
@@ -30,3 +30,10 @@   complicated.  * Reworked included combinators in Combinator module.++Release 0.6.0.0, September 28, 2012+Changes since release 0.4.0.0:++* Add Functor instances for OptSpec, ArgSpec++* Add ThreeArg and ChoiceArg ArgSpecs
System/Console/MultiArg.hs view
@@ -19,7 +19,7 @@   -- vocabularies, from simple to complex.    -- * Terminology-  +   -- | Some terms are used throughout multiarg:   --   -- [@word@] When you run your program from the Unix shell prompt,@@ -96,7 +96,7 @@   -- can parse @l@ as a short option. This would result in a   -- \"pending\" of @20@. Then, the next parser can treat @20@ as an   -- option argument. After that parse there will be no pendings.-  +   -- * Getting started    -- |If your needs are simple to moderately complicated just look at the@@ -145,6 +145,17 @@   --   -- multiarg can be complicated, although I'd like to believe this is   -- because it addresses a complicated problem in a flexible way.++  -- * Projects usings multiarg++  -- | * Penny, an extensible double-entry accounting+  -- system. <http://hackage.haskell.org/package/penny-lib> The code+  -- using multiarg is woven throughout the system; for example, see+  -- the Penny.Liberty module.+  --+  -- * multiarg was originally written for Pantry, which I have yet to+  -- polish for release, but which is available on Github at+  -- <https://github.com/massysett/Pantry>.    module System.Console.MultiArg.SimpleParser ) where 
System/Console/MultiArg/Combinator.hs view
@@ -6,19 +6,21 @@   -- * Parser combinators   notFollowedBy,   (<?>),-  +   -- * Combined long and short option parser   OptSpec(OptSpec, longOpts, shortOpts, argSpec),-  ArgSpec(NoArg, OptionalArg, OneArg, TwoArg, VariableArg),+  ArgSpec(NoArg, OptionalArg, OneArg, TwoArg,+          ThreeArg, VariableArg, ChoiceArg),   parseOption,-  +   -- * Other words   matchApproxWord ) where-  -import Data.List (isPrefixOf, intersperse)++import Data.List (isPrefixOf, intersperse, nubBy, intercalate) import Data.Set ( Set ) import qualified Data.Set as Set-import Control.Applicative ((<*>), optional, (<$))+import Control.Applicative+       ((<$>), (<*>), optional, (<$), (*>), (<|>), many)  import System.Console.MultiArg.Prim   ( Parser, throw, try, approxLongOpt,@@ -28,9 +30,9 @@ import System.Console.MultiArg.Option   ( LongOpt, ShortOpt, unLongOpt,     makeLongOpt, makeShortOpt, unShortOpt )-import Control.Applicative ((<|>), many) import qualified Data.Map as M import Data.Map ((!))+import Data.Maybe (fromMaybe) import Data.Monoid ( mconcat )  @@ -51,7 +53,7 @@ -- all previous errors, replacing them with a single error of type -- Replaced containing the string given. (<?>) :: Parser a -> String -> Parser a-(<?>) l e = l <??> (const [Replaced e])+(<?>) l e = l <??> const [Replaced e]  infix 0 <?> @@ -66,7 +68,7 @@       matches = Set.filter p s       err = throw $ Expected             ("word matching one of: "-             ++ (concat . intersperse ", " $ Set.toList s))+             ++ (intercalate ", " $ Set.toList s))   case Set.toList matches of     [] -> err     (x:[]) -> return (a, x)@@ -74,14 +76,14 @@   unsafeShortOpt :: Char -> ShortOpt-unsafeShortOpt c = case makeShortOpt c of-  Nothing -> error $ "invalid short option: " ++ [c]-  Just o -> o+unsafeShortOpt c =+  fromMaybe (error $ "invalid short option: " ++ [c])+            (makeShortOpt c)  unsafeLongOpt :: String -> LongOpt-unsafeLongOpt c = case makeLongOpt c of-  Nothing -> error $ "invalid long option: " ++ c-  Just o -> o+unsafeLongOpt c =+  fromMaybe (error $ "invalid long option: " ++ c)+            (makeLongOpt c)   -- |Specifies options for the 'parseOption' function. Each OptSpec@@ -97,16 +99,19 @@   -- your long option does not meet these conditions, a runtime error   -- will occur. -          +   , shortOpts :: [Char]     -- ^ Each Char is a single short option, such as @v@. The     -- character cannot be a dash; if it is, a runtime error will occur.-    +   , argSpec :: ArgSpec a     -- ^ What to do each time one of the given long options or     -- short options appears on the command line.   } +instance Functor OptSpec where+  fmap f (OptSpec ls ss as) = OptSpec ls ss (fmap f as)+ -- | Specifies how many arguments each option takes. As with -- 'System.Console.GetOpt.ArgDescr', there are (at least) two ways to -- use this type. You can simply represent each possible option using@@ -137,10 +142,15 @@     -- argument, @-a -b@ will be parsed with @-b@ being an argument to     -- option @a@, even though @-b@ starts with a hyphen and therefore     -- \"looks like\" an option.-    +   | TwoArg (String -> String -> a)-    -- ^ This option takes two arguments. Parsed similarly to 'OneArg'.+    -- ^ This option takes two arguments. Parsed similarly to+    -- 'OneArg'. +  | ThreeArg (String -> String -> String -> a)+    -- ^ This option takes three arguments. Parsed similarly to+    -- 'OneArg'.+   | VariableArg ([String] -> a)     -- ^ This option takes a variable number of arguments--zero or     -- more. Option arguments continue until the command line contains@@ -151,8 +161,34 @@     -- @-a@ as the last option on the command line, then the only way     -- to indicate the end of arguments for @a@ and the beginning of     -- positional argments is with a stopper.-     +  | ChoiceArg [(String, a)]+    -- ^ This option takes a single argument, which must match one of+    -- the strings given in the list. The user may supply the shortest+    -- unambiguous string. If the argument list to ChoiceArg has+    -- duplicate strings, only the first string is used. For instance,+    -- ChoiceArg could be useful if you were parsing the @--color@+    -- option to GNU grep, which requires the user to supply one of+    -- three arguments: @always@, @never@, or @auto@.+++instance Functor ArgSpec where+  fmap f a = case a of+    NoArg i -> NoArg $ f i+    OptionalArg g ->+      OptionalArg $ \ms -> f (g ms)+    OneArg g ->+      OneArg $ \s1 -> f (g s1)+    TwoArg g ->+      TwoArg $ \s1 s2 -> f (g s1 s2)+    ThreeArg g ->+      ThreeArg $ \s1 s2 s3 -> f (g s1 s2 s3)+    VariableArg g ->+      VariableArg $ \ls -> f (g ls)+    ChoiceArg gs ->+      ChoiceArg . map (\(s, r) -> (s, f r)) $ gs++ -- | Parses a single command line option. Examines all the options -- specified using multiple OptSpec and parses one option on the -- command line accordingly. Fails without consuming any input if the@@ -161,6 +197,14 @@ -- options; for example, the user could type @--verb@ for @--verbose@ -- and @--vers@ for @--version@. --+-- This function is applied to a list of OptSpec, rather than to a+-- single OptSpec, because in order to correctly handle the parsing of+-- shortened long options (e.g. @--verb@ rather than @--verbose@) it+-- is necessary for one function to have access to all of the+-- OptSpec. Applying this function multiple times to different lists+-- of OptSpec and then using the @<|>@ function to combine them will+-- break the proper parsing of shortened long options.+-- -- For an example that uses this function, see -- "System.Console.MultiArg.SimpleParser". parseOption :: [OptSpec a] -> Parser a@@ -169,11 +213,11 @@   in case mconcat ([shortOpt] <*> os) of     Nothing -> longs     Just shorts -> longs <|> shorts-  + longOptParser :: [OptSpec a] -> Parser a longOptParser os = longOpt (longOptSet os) (longOptMap os)-       + longOptSet :: [OptSpec a] -> Set LongOpt longOptSet = Set.fromList . concatMap toOpts where   toOpts = map unsafeLongOpt . longOpts@@ -190,43 +234,42 @@ longOpt set mp = do   (_, lo, maybeArg) <- approxLongOpt set   let spec = mp ! lo+      maybeNextArg = maybe nextArg return maybeArg   case spec of     NoArg a -> case maybeArg of       Nothing -> return a       Just _ -> fail $ "option " ++ unLongOpt lo                   ++ " does not take argument"     OptionalArg f -> return (f maybeArg)-    OneArg f -> case maybeArg of-      Nothing -> do-        a1 <- nextArg-        return $ f a1-      Just a -> return $ f a-    TwoArg f -> case maybeArg of-      Nothing -> do-        a1 <- nextArg-        a2 <- nextArg-        return $ f a1 a2-      Just a1 -> do-        a2 <- nextArg-        return $ f a1 a2+    OneArg f -> f <$> maybeNextArg+    TwoArg f -> f <$> maybeNextArg <*> nextArg+    ThreeArg f -> f <$> maybeNextArg <*> nextArg <*> nextArg     VariableArg f -> do       as <- many nonOptionPosArg       return . f $ case maybeArg of         Nothing -> as         Just a1 -> a1 : as-+    ChoiceArg ls -> do+      s <- maybeNextArg+      case matchAbbrev ls s of+        Nothing -> fail $ "option " ++ unLongOpt lo+                   ++ " requires an argument: "+                   ++ (concat . intersperse ", " . map fst $ ls)+        Just g -> return g  shortOpt :: OptSpec a -> Maybe (Parser a) shortOpt o = mconcat parsers where   parsers = map mkParser . shortOpts $ o   mkParser c =     let opt = unsafeShortOpt c-    in Just $ case argSpec o of-      NoArg a -> a <$ nextShort opt-      OptionalArg f -> shortOptionalArg opt f-      OneArg f -> shortOneArg opt f-      TwoArg f -> shortTwoArg opt f-      VariableArg f -> shortVariableArg opt f+    in Just $ nextShort opt *> case argSpec o of+      NoArg a -> return a+      OptionalArg f -> shortOptionalArg f+      OneArg f -> shortOneArg f+      TwoArg f -> shortTwoArg f+      ThreeArg f -> shortThreeArg f+      VariableArg f -> shortVariableArg f+      ChoiceArg ls -> shortChoiceArg opt ls  -- | Parses a short option without an argument, either pending or -- non-pending. Fails with a single error message rather than two.@@ -236,44 +279,43 @@   err = Expected ("short option: " ++ [unShortOpt o])   e ls = err : (drop 2 ls) -shortVariableArg :: ShortOpt -> ([String] -> a) -> Parser a-shortVariableArg opt f = do-  nextShort opt+shortVariableArg :: ([String] -> a) -> Parser a+shortVariableArg f = do   maybeSameWordArg <- optional pendingShortOptArg   args <- many nonOptionPosArg   case maybeSameWordArg of     Nothing -> return (f args)     Just arg1 -> return (f (arg1:args))-   -shortTwoArg :: ShortOpt -> (String -> String -> a) -> Parser a-shortTwoArg opt f = do-  nextShort opt-  maybeSameWordArg <- optional pendingShortOptArg-  case maybeSameWordArg of-    Nothing -> do-      arg1 <- nextArg-      arg2 <- nextArg-      return (f arg1 arg2)-    Just arg1 -> do -      arg2 <- nextArg-      return (f arg1 arg2)-   -shortOneArg :: ShortOpt -> (String -> a) -> Parser a-shortOneArg opt f = do-  nextShort opt-  maybeSameWordArg <- optional pendingShortOptArg-  case maybeSameWordArg of-    Nothing -> do-      arg <- nextArg-      return (f arg)-    Just a -> return (f a)+shortOneArg :: (String -> a) -> Parser a+shortOneArg f = f <$> firstShortArg  -shortOptionalArg :: ShortOpt -> (Maybe String -> a) -> Parser a-shortOptionalArg opt f = do-  nextShort opt+firstShortArg :: Parser String+firstShortArg =+  optional pendingShortOptArg >>= maybe nextArg return+++shortChoiceArg :: ShortOpt -> [(String, a)] -> Parser a+shortChoiceArg opt ls =+  firstShortArg+  >>= maybe err return . matchAbbrev ls+  where+    err = fail $ "option " ++ [unShortOpt opt] ++ " requires "+          ++ "one argument: "+          ++ (concat . intersperse " " . map fst $ ls)++++shortTwoArg :: (String -> String -> a) -> Parser a+shortTwoArg f = f <$> firstShortArg <*> nextArg++shortThreeArg :: (String -> String -> String -> a) -> Parser a+shortThreeArg f = f <$> firstShortArg <*> nextArg <*> nextArg++shortOptionalArg :: (Maybe String -> a) -> Parser a+shortOptionalArg f = do   maybeSameWordArg <- optional pendingShortOptArg   case maybeSameWordArg of     Nothing -> do@@ -282,3 +324,19 @@         Nothing -> return (f Nothing)         Just a -> return (f (Just a))     Just a -> return (f (Just a))++-- | Finds the unambiguous short match for a string, if there is+-- one. Returns a string describing the error condition if there is+-- one, or the matching result if successful.+matchAbbrev :: [(String, a)] -> String -> Maybe a+matchAbbrev ls s =+  let ls' = nubBy (\x y -> fst x == fst y) ls+  in case lookup s ls' of+    Just a -> return a+    Nothing ->+      let pdct (t, _) = s `isPrefixOf` t+      in case filter pdct ls of+        (_, a):[] -> return a+        _ -> Nothing++
System/Console/MultiArg/Option.hs view
@@ -11,6 +11,7 @@   where  import Data.List (find)+import Data.Maybe (isNothing)  -- | Short options. Options that are preceded with a single dash on -- the command line and consist of a single letter. That single letter@@ -38,19 +39,19 @@ -- | Makes a long option. Returns Nothing if the string is not a valid -- long option. makeLongOpt :: String -> Maybe LongOpt-makeLongOpt t = case isValidLongOptText t of-  True -> Just $ LongOpt t-  False -> Nothing+makeLongOpt t =+  if isValidLongOptText t then Just $ LongOpt t else Nothing + isValidLongOptText :: String -> Bool isValidLongOptText s = case s of   [] -> False   x:xs ->-    if x == '-' || x == '='+    if x `elem` ['-', '=']     then False     else case xs of       [] -> True       y:_ ->-        if y == '-' || y == '='+        if y `elem` ['-', '=']         then False-        else maybe True (const False) (find (== '=') xs)+        else isNothing . find (== '=') $ xs
System/Console/MultiArg/Prim.hs view
@@ -7,13 +7,13 @@ module System.Console.MultiArg.Prim (     -- * Parser types   Parser,-  +   -- * Running a parser-  +   -- | Each parser runner is applied to a list of Strings, which are the-  -- command line arguments to parse. +  -- command line arguments to parse.   parse,-  +   -- * Higher-level parser combinators   parserMap,   good,@@ -21,7 +21,7 @@   choice,   combine,   lookAhead,-  +   -- ** Running parsers multiple times   several,   manyTill,@@ -32,13 +32,13 @@   genericThrow,   (<??>),   try,-  +   -- * Parsers   -- ** Short options and arguments   pendingShortOpt,   nonPendingShortOpt,-  pendingShortOptArg,  -  +  pendingShortOptArg,+   -- ** Long options and arguments   exactLongOpt,   approxLongOpt,@@ -46,14 +46,14 @@   -- ** Stoppers   stopper,   resetStopper,-  +   -- ** Positional (non-option) arguments   nextArg,   nonOptionPosArg,-  +   -- ** Miscellaneous   end,-  +   -- * Errors   Message(Expected, StrMsg, Replaced, UnknownError),   Error(Error),@@ -66,7 +66,7 @@   (ShortOpt,     unShortOpt,     LongOpt,-    unLongOpt, +    unLongOpt,     makeLongOpt ) import Control.Applicative ( Applicative, Alternative ) import qualified Control.Applicative@@ -77,7 +77,7 @@ import Control.Monad ( when, MonadPlus(mzero, mplus), guard ) import Data.Monoid ( Monoid ( mempty, mappend ) ) import qualified Data.List as L-import Data.List (isPrefixOf)+import Data.List (isPrefixOf, intercalate)  type Location = String @@ -104,10 +104,10 @@   -- something else. The string indicates what was expected.   | StrMsg String     -- ^ The 'fromString' function was applied.-    +   | Replaced String     -- ^ A previous list of error messages was replaced with this error message.-    +   | UnknownError     -- ^ Any other error; used by 'genericThrow'. @@ -182,10 +182,10 @@   -- Unicode strings, see the documentation in   -- "System.Console.MultiArg.GetArgs" before you use   -- 'System.Environment.getArgs'.-  +   -> Parser a   -- ^ Parser to run-  +   -> Exceptional Error a   -- ^ Success or failure. Any parser might fail; for example, the   -- command line might not have any values left to parse. Use of the@@ -355,7 +355,7 @@  pendingShortOpt :: ShortOpt -> Parser () pendingShortOpt so = Parser $ \s ->-  let err = Expected ("pending short option: " ++ [(unShortOpt so)])+  let err = Expected ("pending short option: " ++ [unShortOpt so])       es = (Bad, s { errors = err : errors s })       gd newSt = (Good (), newSt)   in maybe es gd $ do@@ -480,8 +480,8 @@   let (pre, word, afterEq) = splitLongWord str   guard (pre == "--")   return (word, afterEq)-   + nextWord :: ParseSt -> Maybe (String, ParseSt) nextWord st = case remaining st of   [] -> Nothing@@ -496,7 +496,7 @@ approxLongOptError set st = st { errors = newE : errors st } where   newE = Expected ex   ex = "a long option: " ++ longs-  longs = concat . L.intersperse ", " $ opts+  longs = intercalate ", " opts   opts = fmap unLongOpt . Set.toList $ set  -- | Examines the next word. If it matches a Text in the set@@ -517,7 +517,7 @@     if Set.member opt ts       then return ((word, opt, afterEq), s')       else do-      let p t = word `isPrefixOf` (unLongOpt t)+      let p t = word `isPrefixOf` unLongOpt t           matches = Set.filter p ts       case Set.toList matches of         [] -> Nothing@@ -584,8 +584,8 @@     Good g -> (Good g, s')     Bad -> (Bad, s'') where       s'' = s { errors = errors s' }-       + -- | Returns the next string on the command line as long as there are -- no pendings. Be careful - this will return the next string even if -- it looks like an option (that is, it starts with a dash.) Consider@@ -726,7 +726,7 @@         let (ls, finalGoodSt, finalBadSt) = parseRepeat st' f         in (a : ls, finalGoodSt, finalBadSt)     (Bad, st') -> ([], st1, st')-    +  -- | Succeeds if there is no more input left. end :: Parser ()
System/Console/MultiArg/SimpleParser.hs view
@@ -5,19 +5,19 @@ module System.Console.MultiArg.SimpleParser (   -- * Interspersion control   Intersperse (Intersperse, StopOptions)-  +   -- * Option specifications   , C.OptSpec (OptSpec, longOpts, shortOpts, argSpec)   , C.ArgSpec (NoArg, OptionalArg, OneArg, TwoArg, VariableArg)-    +     -- * Exceptions   , Ex.Exceptional (Exception, Success)   , P.Error (Error)   , P.Message (Expected, StrMsg, Replaced, UnknownError)-    +     -- * Get command line arguments   , G.getArgs-  +     -- * The parser   , parse   ) where@@ -26,8 +26,8 @@ import qualified System.Console.MultiArg.GetArgs as G import qualified System.Console.MultiArg.Combinator as C import qualified Control.Monad.Exception.Synchronous as Ex-import Control.Applicative ( many, (<|>), optional, (*>),-                             (<$))+import Control.Applicative ( many, (<|>), optional,+                             (<$), (<*>), (<*), (<$>)) import Data.Maybe (catMaybes)  -- | What to do after encountering the first non-option,@@ -42,21 +42,21 @@   -- command line is entered, then @-b@ will result in an error   -- because @-b@ starts with a hyphen and therefore \"looks like\" an   -- option.-  +   | StopOptions     -- ^ No additional options will be parsed after encountering the     -- first positional argument. For example, if @a@ and @b@ are     -- options, in the command line @-a posarg -b@, @b@ will be parsed     -- as a positional argument rather than as an option. --- | Parse a command line. +-- | Parse a command line. parse ::   Intersperse   -- ^ What to do after encountering the first positional argument-  +   -> [C.OptSpec a]   -- ^ All possible options-  +   -> (String -> a)   -- ^ How to handle positional arguments. This function is applied to   -- the appropriate string every time the parser encounters a@@ -82,15 +82,15 @@ parseOptsNoIntersperse p = P.manyTill p e where   e = P.end <|> nonOpt   nonOpt = P.lookAhead next-  next = ((() <$ P.nonOptionPosArg) <|> P.stopper)+  next = (() <$ P.nonOptionPosArg) <|> P.stopper   parseStopOpts :: P.Parser a -> (String -> a) -> P.Parser [a]-parseStopOpts optParser p = do-  opts <- parseOptsNoIntersperse optParser-  _ <- optional P.stopper-  args <- many P.nextArg-  return $ opts ++ (map p args)+parseStopOpts optParser p =+  (\opts args -> opts ++ map p args)+  <$> parseOptsNoIntersperse optParser+  <* optional P.stopper+  <*> many P.nextArg   -- | @parseIntersperse o p@ parses options and positional arguments,@@ -98,8 +98,8 @@ -- when applied to a string, returns the appropriate type. parseIntersperse :: P.Parser a -> (String -> a) -> P.Parser [a] parseIntersperse optParser p =-  let pa = P.nonOptionPosArg >>= return . Just . p-      po = optParser >>= return . Just-      ps = P.stopper *> return Nothing+  let pa = (Just . p) <$> P.nonOptionPosArg+      po = Just <$> optParser+      ps = Nothing <$ P.stopper       parser = po <|> ps <|> pa-  in P.manyTill parser P.end >>= return . catMaybes+  in catMaybes <$> P.manyTill parser P.end
multiarg.cabal view
@@ -1,5 +1,5 @@ Name: multiarg-Version: 0.4.0.0+Version: 0.6.0.0 Cabal-version: >=1.8 Build-Type: Simple License: MIT@@ -41,7 +41,7 @@         base ==4.*,         explicit-exception ==0.1.*,         containers ==0.4.*-        +     -- See documentation in System.Console.MultiArg.GetArgs for details     if flag(newbase)         Build-depends: