packages feed

docopt 0.6.0.2 → 0.7.0.0

raw patch · 10 files changed

+637/−145 lines, 10 filesdep +template-haskelldep +th-liftPVP ok

version bump matches the API change (PVP)

Dependencies added: template-haskell, th-lift

API changes (from Hackage documentation)

- System.Console.Docopt: optionsWithUsage :: String -> [String] -> IO Arguments
- System.Console.Docopt: optionsWithUsageDebug :: String -> [String] -> IO Arguments
- System.Console.Docopt: optionsWithUsageFile :: FilePath -> IO Arguments
- System.Console.Docopt: optionsWithUsageFileDebug :: FilePath -> IO Arguments
+ System.Console.Docopt: data Docopt
+ System.Console.Docopt: data ParseError :: *
+ System.Console.Docopt: docopt :: QuasiQuoter
+ System.Console.Docopt: docoptFile :: QuasiQuoter
+ System.Console.Docopt: exitWithUsage :: Docopt -> IO a
+ System.Console.Docopt: exitWithUsageMessage :: Docopt -> String -> IO a
+ System.Console.Docopt: getArgOrExitWith :: Docopt -> Arguments -> Option -> IO String
+ System.Console.Docopt: parseArgs :: Docopt -> [String] -> Either ParseError Arguments
+ System.Console.Docopt: parseArgsOrExit :: Docopt -> [String] -> IO Arguments
+ System.Console.Docopt: usage :: Docopt -> String
+ System.Console.Docopt.NoTH: argument :: String -> Option
+ System.Console.Docopt.NoTH: command :: String -> Option
+ System.Console.Docopt.NoTH: data Docopt
+ System.Console.Docopt.NoTH: data Option
+ System.Console.Docopt.NoTH: data ParseError :: *
+ System.Console.Docopt.NoTH: exitWithUsage :: Docopt -> IO a
+ System.Console.Docopt.NoTH: exitWithUsageMessage :: Docopt -> String -> IO a
+ System.Console.Docopt.NoTH: getAllArgs :: Arguments -> Option -> [String]
+ System.Console.Docopt.NoTH: getAllArgsM :: Monad m => Arguments -> Option -> m [String]
+ System.Console.Docopt.NoTH: getArg :: Arguments -> Option -> Maybe String
+ System.Console.Docopt.NoTH: getArgCount :: Arguments -> Option -> Int
+ System.Console.Docopt.NoTH: getArgOrExitWith :: Docopt -> Arguments -> Option -> IO String
+ System.Console.Docopt.NoTH: getArgWithDefault :: Arguments -> String -> Option -> String
+ System.Console.Docopt.NoTH: getFirstArg :: Monad m => Arguments -> Option -> m String
+ System.Console.Docopt.NoTH: isPresent :: Arguments -> Option -> Bool
+ System.Console.Docopt.NoTH: isPresentM :: Monad m => Arguments -> Option -> m Bool
+ System.Console.Docopt.NoTH: longOption :: String -> Option
+ System.Console.Docopt.NoTH: notPresent :: Arguments -> Option -> Bool
+ System.Console.Docopt.NoTH: notPresentM :: Monad m => Arguments -> Option -> m Bool
+ System.Console.Docopt.NoTH: parseArgs :: Docopt -> [String] -> Either ParseError Arguments
+ System.Console.Docopt.NoTH: parseArgsOrExit :: Docopt -> [String] -> IO Arguments
+ System.Console.Docopt.NoTH: parseUsage :: String -> Either ParseError Docopt
+ System.Console.Docopt.NoTH: parseUsageOrExit :: String -> IO Docopt
+ System.Console.Docopt.NoTH: shortOption :: Char -> Option
+ System.Console.Docopt.NoTH: type Arguments = Map Option ArgValue
+ System.Console.Docopt.NoTH: usage :: Docopt -> String
- System.Console.Docopt: getArg :: Monad m => Arguments -> Option -> m String
+ System.Console.Docopt: getArg :: Arguments -> Option -> Maybe String

Files

+ CHANGELOG.md view
@@ -0,0 +1,38 @@+# 0.7.0.0++- Add usage parsing QuasiQuoters [#7]+  - Add `docopt` usage parsing QuasiQuoter+  - Add `docoptFile` usage parsing QuasiQuoter+  - Add `System.Docopt.NoTH` module+    - Add `parseUsage`+    - Add `parseUsageOrExit`+- New API organization [#10]+  - Remove `optionsWithUsage`+  - Remove `optionsWithUsageDebug`+  - Remove `optionsWithUsageFile`+  - Remove `optionsWithUsageFileDebug`+  - Add `Docopt` type to represent a parsed usage string+  - Add `usage`+  - Add `parseArgs`+  - Add `parseArgsOrExit`+  - Add `exitWithUsage`+  - Add `exitWithUsageMessage`+  - Monomorphize `getArg` from `Monad m` to `Maybe`+  - Add `getArgOrExitWith`+  - Deprecate `getAllArgsM`+  - Deprecate `notPresentM`+  - Deprecate `isPresentM`+  - Deprecate `getFirstArg`+- Add thorough haddock API documentation+  +### 0.6.0.2++- Make `argument` not require its named option wrapped in angle brackets. [#4, #5]++### 0.6.0.1++- Fix haddock docs.++# 0.6.0.0++First release! Tracks features of reference Python implementation at version `0.6`.
+ README.md view
@@ -0,0 +1,210 @@+Docopt.hs+=========++A Haskell port of python's [docopt](http://docopt.org).++----------++## Want a command-line interface *without* building a parser?++How about writing your help text first, and getting a parser for free!++Save your help text to a file (i.e. `USAGE.txt`):++    Usage: +      myprog cat <file>+      myprog echo [--caps] <string>++    Options:+      -c, --caps    Caps-lock the echoed argument++Then, in your `Myprog.hs`:+    +```haskell+{-# LANGUAGE QuasiQuotes #-}+import Control.Monad (when)+import Data.Char (toUpper)+import System.Console.Docopt++patterns :: Docopt+patterns = [docoptFile|USAGE.txt|]++main = do+  args <- parseArgsOrExit patterns =<< getArgs++  when (args `isPresent` (command "cat")) $ do+    file <- args `getArgOrExit` (argument "file")+    putStr =<< readFile file++  when (args `isPresent` (command "echo")) $ do+    let charTransform = if args `isPresent` (longOption "caps")+                          then toUpper+                          else id+    string <- args `getArgOrExit` (argument "string")+    putStrLn $ map charTransform string+```++That's it! No Template Haskell, no unreadable syntax, no learning yet *another* finicky API. Write the usage patterns you support, and docopt builds the appropriate option parser for you (internally using [`parsec`](http://hackage.haskell.org/package/parsec)). If your user invokes your program correctly, you query for the arguments they provided. If the arguments provided do not match a supported usage pattern, you guessed it: docopt automatically prints the help text and exits!+++Installation+------------++    cabal sandbox init+    cabal install docopt++API Reference+-------------++See [the package on hackage](https://hackage.haskell.org/package/docopt)+++Help text format+================++Docopt only cares about 2 parts of your help text:+ +- **Usage patterns**, e.g.:++  ```+      Usage: +        my_program [-hs] [-o=<file>] [--quiet | --verbose] [<input>...]+  ```+  These begin with `Usage:` (case-insensitive), and end with a blank line. ++- **Option descriptions**, e.g.: ++  ```+      Options:+        -h --help    show this+        -s --sorted  sorted output+        -o=<file>    specify output file +                     [default: ./test.txt]+        --quiet      print less text+        --verbose    print more text+  ```++  Any line after the usage patterns that begins with a `-` is treated as an option description (though an option's default may be on a different line).++Usage Patterns+--------------++- #### `<argument>`++  Positional arguments. Constructed via `argument`, i.e. `argument "arg"` matches an `<arg>` element in the help text.++- #### `--flag` or `--option=<arg>`++  Options are typically optional (though this is up to you), and can be either boolean (present/absent), as in `--flag`, or expect a trailing argument, as in `--option=<arg>`. Arguments can be separated from the option name by an `=` or a single space, and can be in `<arg>` form or `ARG` form (though consistency of style is recommended, it is not enforced). ++  Short-style options, as in `-f` or `-f ARG`, are also allowed. Synonyms between different spellings of the same option (e.g. `-v` and `--verbose`) can be established in the option descriptions (see below). Short-style options can also be stacked, as in `-rfA`. When options are stacked, `-rfA` is effectively equivalent to `(-r | -f | -A)...` to the argument parser.++  You can match a long-style option `--flag` with `longOption "flag"`, and a short-style option `-f` with `shortOption 'f'` The same constructor is used whether the option expects an argument or not.++- #### `command`++  Anything not recognized as a positional argument or a short or long option is treated as a command (or subcommand, same thing to docopt). A command named `pull` can be matched with `command "pull"`. ++- #### `[]` (brackets) e.g. `command [--option]`++  Patterns inside brackets are **optional**.++- #### `()` (parens)++  Patterns inside parens are **required** (the same as patterns *not* in `()` are required). Parens are useful if you need to group some elements, either for use with `|` or `...`.++- #### `|` (pipe) e.g. `command [--quiet | --verbose]`++  A pipe `|` separates mutually elements in a group. A group could be elements inside `[]`, `()`, or the whole usage line. ++  ```+      Usage:+        myprog command [--opt1 | --opt2]  # valid+        myprog go (left | right)          # valid+        myprog -v | -h                    # valid+  ```++  When elements are separated by a pipe, the elements are tried from left to right until one succeeds. At least one of the elements are required unless in an eplicitly optional group surrounded by `[]`.++- #### `...` (ellipsis) e.g. `command <file>...`++  An ellipsis can trail any element or group to make it repeatable. Repeatable elements will be accumulated into a list of occurrences.++- #### `[options]` (case sensitive)++  The string `[options]` is a shortcut to match any options specified in your option descriptions.++- #### `[-]` and `[--]`++  Single hyphen `-` is used by convention to specify using `stdin` as input instead of reading a file. Double hyphen `--` is typically used to manually separate leading options from trailing positional arguments. Both of these are treated as `command`s, and so are perfectly legal in usage patterns. They are typically optional elements, but can be required if you drop the `[]`. ++Option descriptions+-------------------++Option descriptions establish:+- which short and long options are synonymous+- whether an option expects an argument or is a simple flag+- if an option's argument has a default value++**Rules**:++- Any line *after* the usage patterns whose first non-space character is a `-` is treated as an option description. (`Options:` prefix line not required).+      +  ```+      Options: --help       # invalid: line does not start with '-'+               --verbose    # good+  ```++- Options on the same line will be treated by the parser as synonyms (everywhere interchangeable). Synonymous options are separated by a space (with optional comma):++  ```+      Usage:+        myprog --help | --verbose++      Options: +        -h, --help      Print help text+        -v --verbose    Print help text twice +  ```++  Here, `myprog --help` and `myprog -h` will both work the same, as will `myprog --verbose` and `myprog -v`.++- If any synonymous options are specified in the description with an argument, the option parser will expect an argument for all synonyms. If not, all synonyms will be treated as flags.++  ```+      Usage:+        myprog analyze [--verbose] <file>++      Options:+        --verbose, -v LEVEL   The level of output verbosity.+  ```++  Here, in the arguments `myprog analyze --verbose ./file1.txt` would be invalid, because `-v` *and its synonyms* expect an argument, so `./file1.txt` is captured as the argument of `--verbose`, *not* as the positional argument `<file>`. Be careful!++  Options can be separated from arguments with a single space or a `=`, and arguments can have the form `<arg>` or `ARG`. Just be sure to separate synonyms and arguments from the beginning of the description by **at least 2 spaces**.++  ```+      --opt1 ARG1   Option 1.+      --opt2=<arg2> Option 2.        # BAD: use 2 spaces+      -a <arg3>     Option 3.+      -b=ARG4       Option 4. +  ```++- Options that expect arguments can be given a default value, in the form `[default: <default-val>]`. Default values do not need to be on the same line++  ```+      --host=NAME       Host to listen on. [default: localhost]+      --port=PORT       Port number [default: 8080]+      --directory=DIR   This option has an especially long description +                        explaining its meaning. [default: ./]+  ```++----------------+++#### Differences from reference python implementation:++  - does not automatically exclude from the `[options]` shortcut options that are already used elsewhere in the usage pattern (e.g. `usage: prog [options] -a` will try to parse `-a` twice).++  - does not automatically resolve partially-specified arguments, e.g. `--verb` does not match where `--verbose` is expected. This is planned to be deprecated in future versions of docopt, and will likely not be implemented in docopt.hs++  - is not insensitive to the ordering of adjacent options, e.g. `usage: prog -a -b` does not allow `prog -b -a` (reference implementation currently does).
System/Console/Docopt.hs view
@@ -1,7 +1,46 @@-module System.Console.Docopt -  ( -    module System.Console.Docopt.Public,+-- | Example:+--+-- @+-- {-\# LANGUAGE QuasiQuotes \#-}+-- module Main where+--+-- import Control.Monad (when)+-- import Data.Char (toUpper)+-- import System.Console.Docopt+--+-- patterns :: Docopt+-- patterns = [docopt|+-- docopt-sample version 0.1.0+--+-- Usage:+--   docopt-sample cat \<file\>+--   docopt-sample echo [--caps] \<string\>+--+-- Options:+--   -c, --caps    Caps-lock the echoed argument+-- |]+--+-- main :: IO ()+-- main = do+--   args <- parseArgsOrExit patterns+--+--   when (args \`isPresent\` (command \"cat\")) $ do+--     file <- args \`getArgOrExit\` (argument \"file\")+--     putStr =<< readFile file+--+--   when (args \`isPresent\` (command \"echo\")) $ do+--     let charTransform = if args \`isPresent\` (longOption \"caps\")+--                         then toUpper+--                         else id+--     string <- args \`getArgOrExit\` (argument \"string\")+--     putStrLn $ map charTransform string+-- @+module System.Console.Docopt+  (+    module System.Console.Docopt.QQ,+    module System.Console.Docopt.Public   )   where +import System.Console.Docopt.QQ import System.Console.Docopt.Public
+ System/Console/Docopt/NoTH.hs view
@@ -0,0 +1,38 @@+module System.Console.Docopt.NoTH+  (+    -- * Usage parsers+      parseUsage+    , parseUsageOrExit++    , module System.Console.Docopt.Public+  )+  where++import Data.Map as M hiding (null)+import System.Exit++import System.Console.Docopt.Types+import System.Console.Docopt.Public+import System.Console.Docopt.ParseUtils+import System.Console.Docopt.UsageParse (pDocopt)+++-- | Parse docopt-formatted usage patterns.+--+--   For help with the docopt usage format, see+--   <https://github.com/docopt/docopt.hs/blob/master/README.md#help-text-format the readme on github>.+parseUsage :: String -> Either ParseError Docopt+parseUsage usg =+  case runParser pDocopt M.empty "Usage" usg of+    Left e       -> Left e+    Right optfmt -> Right (Docopt optfmt usg)++-- | Same as 'parseUsage', but 'exitWithUsage' on parse failure. E.g.+--+-- > let usageStr = "Usage:\n  prog [--option]\n"+-- > patterns <- parseUsageOrExit usageStr+parseUsageOrExit :: String -> IO Docopt+parseUsageOrExit usg = exitUnless $ parseUsage usg+  where+    exit message = putStrLn message >> exitFailure+    exitUnless = either (const $ exit usg) return
System/Console/Docopt/OptParse.hs view
@@ -1,4 +1,4 @@-module System.Console.Docopt.OptParse +module System.Console.Docopt.OptParse   where  import Control.Monad (unless)@@ -10,20 +10,20 @@ import System.Console.Docopt.Types  --- | The meat and potatoes. +-- | The meat and potatoes. --   @delim@ is an obscure delimiter with which to intercalate the argv list, --   @fmt@ is the OptPattern together with metadata to tell the parser how to parse args.---   Together, these let @buildOptParser@ build a parsec parser that can be applied to an argv. +--   Together, these let @buildOptParser@ build a parsec parser that can be applied to an argv. buildOptParser :: String -> OptFormat -> CharParser OptParserState ()-buildOptParser delim fmt@(pattern, infomap) = -  +buildOptParser delim fmt@(pattern, infomap) =+   let -- Helpers       argDelim = (try $ string delim) <?> "space between arguments"-      +       makeParser p = buildOptParser delim (p, infomap)-      -      argDelimIfNotInShortOptStack = do -        st <- getState ++      argDelimIfNotInShortOptStack = do+        st <- getState         if not $ inShortOptStack st           then optional argDelim           else return ()@@ -41,20 +41,20 @@       updateSt_assertPresent opt = updateOptWith (\opt info _ -> assertPresent opt info) opt ""        updateSt_inShortOptStack = updateState . updateInShortOptStack-  +   in case pattern of   (Sequence pats) ->-      assertTopConsumesAll $ foldl (andThen) (return ()) ps +      assertTopConsumesAll $ foldl (andThen) (return ()) ps       where assertTopConsumesAll p = do               st <- getState               if inTopLevelSequence st-                then do +                then do                   updateState $ \st -> st {inTopLevelSequence = False}                   p <* eof-                else p +                else p             inner_pats = (\pat -> (pat, infomap)) `map` pats             ps = (buildOptParser delim) `map` inner_pats-            andThen = \p1 p2 -> do +            andThen = \p1 p2 -> do               p1               argDelimIfNotInShortOptStack               p2@@ -69,8 +69,8 @@                     argDelimIfNotInShortOptStack                     makeParser $ Unordered rest   (Optional pat) ->-        case pat of -          Unordered ps -> case ps of +        case pat of+          Unordered ps -> case ps of             p:[] -> makeParser $ Optional p             _  -> optional $ choice $ (parseThisThenRest ps) `map` ps                   where parseThisThenRest list pat = try $ do@@ -78,27 +78,27 @@                           let rest = list \\ [pat]                           argDelimIfNotInShortOptStack                           makeParser $ Optional $ Unordered rest-          _ -> optional $ try $ makeParser pat +          _ -> optional $ try $ makeParser pat   (Repeated pat) -> do-      case pat of +      case pat of         (Optional p) -> (try $ makeParser p) `sepBy` argDelimIfNotInShortOptStack         _            -> (try $ makeParser pat) `sepBy1` argDelimIfNotInShortOptStack       return ()-  (Atom pat) -> case pat of +  (Atom pat) -> case pat of       o@(ShortOption c) ->             do  st <- getState                 if inShortOptStack st then return () else char '-' >> return ()                 char c                 updateState $ updateInShortOptStack True-                val <- if expectsVal $ M.findWithDefault (fromSynList []) o infomap -                  then try $ do +                val <- if expectsVal $ M.findWithDefault (fromSynList []) o infomap+                  then try $ do                     optional $ string "=" <|> argDelim                     updateState $ updateInShortOptStack False                     manyTill1 anyChar (lookAhead_ argDelim <|> eof)                   else do                     stillInShortStack <- isNotFollowedBy argDelim-                    unless stillInShortStack $ -                      updateState $ updateInShortOptStack False +                    unless stillInShortStack $+                      updateState $ updateInShortOptStack False                     return ""                 updateState $ withEachSynonym o $                               \pa syn info -> saveOccurrence syn info val pa@@ -106,8 +106,8 @@       o@(LongOption name) ->             do string "--"                string name-               val <- if expectsVal $ M.findWithDefault (fromSynList []) o infomap -                 then do +               val <- if expectsVal $ M.findWithDefault (fromSynList []) o infomap+                 then do                    string "=" <|> argDelim                    --many (notFollowedBy (string delim) >> anyChar)                    manyTill1 anyChar (lookAhead_ argDelim <|> eof)@@ -122,7 +122,7 @@                 --synparsers = oneOf `map` synlists                 oneOfSyns = map (\ss -> OneOf (map Atom ss)) synlists                 unorderedSynParser = buildOptParser delim (Unordered oneOfSyns, infomap)-            in  unorderedSynParser +            in  unorderedSynParser                 <?> humanize o       o@(Argument name) ->             do val <- try $ many1 (notFollowedBy argDelim >> anyChar)@@ -154,7 +154,7 @@ saveOccurrence opt info newval argmap = M.alter updateCurrentVal opt argmap     where updateCurrentVal m_oldval = case m_oldval of             Nothing     -> (newval `updateFrom`) =<< (optInitialValue info opt)-            Just oldval -> newval `updateFrom` oldval +            Just oldval -> newval `updateFrom` oldval           updateFrom newval oldval = Just $ case oldval of             MultiValue vs -> MultiValue $ newval : vs             Value v       -> Value newval@@ -166,42 +166,42 @@ assertPresent :: Option -> OptionInfo -> Arguments -> Arguments assertPresent opt info argmap = saveOccurrence opt info "" argmap -withEachSynonym :: Option -> -                   (Arguments -> Option -> OptionInfo -> Arguments) -> -                   OptParserState -> +withEachSynonym :: Option ->+                   (Arguments -> Option -> OptionInfo -> Arguments) ->+                   OptParserState ->                    OptParserState-withEachSynonym opt savefn st = +withEachSynonym opt savefn st =   let infomap = optInfoMap st       args = parsedArgs st       syns = synonyms $ M.findWithDefault (fromSynList []) opt infomap       -- give the savefn each opt's info, as well-      foldsavefn = \args opt -> +      foldsavefn = \args opt ->                     let info = M.findWithDefault (fromSynList []) opt infomap-                    in savefn args opt info +                    in savefn args opt info   in st {parsedArgs = foldl foldsavefn args syns}   optInitialValue :: OptionInfo -> Option -> Maybe ArgValue-optInitialValue info opt = -  let repeatable = isRepeated info +optInitialValue info opt =+  let repeatable = isRepeated info   in case opt of     Command name  -> Just $ if repeatable then Counted 0 else NotPresent     Argument name -> Just $ if repeatable then MultiValue [] else NoValue     AnyOption     -> Nothing -- no storable value for [options] shortcut-    _             -> case expectsVal info of +    _             -> case expectsVal info of       True  -> Just $ if repeatable then MultiValue [] else NoValue       False -> Just $ if repeatable then Counted 0 else NotPresent  optDefaultValue :: OptionInfo -> Option -> Maybe ArgValue-optDefaultValue info opt = +optDefaultValue info opt =   let repeatable = isRepeated info-  in case opt of +  in case opt of     Command name  -> Just $ if repeatable then Counted 0 else NotPresent     Argument name -> Just $ if repeatable then MultiValue [] else NoValue     AnyOption     -> Nothing -- no storable value for [options] shortcut-    _               -> case expectsVal info of +    _               -> case expectsVal info of       True  -> case defaultVal info of-        Just dval -> Just $ if repeatable +        Just dval -> Just $ if repeatable                             then MultiValue $ reverse $ words dval                             else Value dval         Nothing   -> Just $ if repeatable then MultiValue [] else NoValue@@ -209,15 +209,16 @@   getArguments :: OptFormat -> [String] -> Either ParseError Arguments-getArguments optfmt argv = +getArguments optfmt argv =     let (pattern, infomap) = optfmt          -- delimiter used to flatten argv to parsable String-        delim = "«»" +        -- TODO: parse argv without a nasty intercalate hack+        delim = "«»"         argvString = delim `intercalate` argv          p = parsedArgs <$> (returnState $ buildOptParser delim optfmt)-        +         patAtoms = atoms pattern         infoKeys = (\\ [AnyOption]) $ M.keys infomap         allAtoms = nub $ patAtoms ++ infoKeys
System/Console/Docopt/Public.hs view
@@ -1,63 +1,86 @@-module System.Console.Docopt.Public +module System.Console.Docopt.Public   (-    -- everything locally declared-    module System.Console.Docopt.Public,+    -- * Command line arguments parsers+      parseArgs+    , parseArgsOrExit -    -- public types-    Option(),-    Arguments(),+    -- *** Re-exported from Parsec+    , ParseError++    -- * Parsed usage string+    , Docopt ()+    , usage+    , exitWithUsage+    , exitWithUsageMessage++    -- * Argument lookup+    , Option()+    , Arguments()++    -- ** Query functions+    , isPresent+    , notPresent+    , getArg+    , getArgOrExitWith+    , getArgWithDefault+    , getAllArgs+    , getArgCount++    -- ** 'Option' constructors+    , command+    , argument+    , shortOption+    , longOption++    -- ** Deprecated+    , getAllArgsM+    , notPresentM+    , isPresentM+    , getFirstArg   )   where -import System.Environment (getArgs) import System.Exit  import Data.Map as M hiding (null)--import Control.Applicative--import System.Console.Docopt.ParseUtils+import Data.Maybe (fromMaybe) import System.Console.Docopt.Types-import System.Console.Docopt.UsageParse (pDocopt)-import System.Console.Docopt.OptParse (getArguments)+import System.Console.Docopt.ApplicativeParsec (ParseError)+import System.Console.Docopt.OptParse  --- * Public API---- ** Main option parsing entry points--optionsWithUsage :: String -> [String] -> IO Arguments-optionsWithUsage usage rawArgs = -    case runParser pDocopt M.empty "Usage" usage of-        Left err -> do putStrLn usage-                       exitFailure-        Right fmt -> case getArguments fmt rawArgs of-            Left err         -> do putStrLn usage-                                   exitFailure-            Right parsedArgs -> return parsedArgs+-- | Parse command line arguments.+parseArgs :: Docopt -> [String] -> Either ParseError Arguments+parseArgs parser = getArguments (optFormat parser) -optionsWithUsageDebug :: String -> [String] -> IO Arguments-optionsWithUsageDebug usage rawArgs =-    case runParser pDocopt M.empty "Usage" usage of-        Left err  -> fail $ show err-        Right fmt -> case getArguments fmt rawArgs of-            Left err         -> fail $ show err-            Right parsedArgs -> return parsedArgs+-- | Same as 'parseArgs', but 'exitWithUsage' on parse failure. E.g.+--+-- > args <- parseArgsOrExit patterns =<< getArgs+parseArgsOrExit :: Docopt -> [String] -> IO Arguments+parseArgsOrExit parser argv = either (const $ exitWithUsage parser) return $ parseArgs parser argv -optionsWithUsageFile :: FilePath -> IO Arguments-optionsWithUsageFile path = do usageStr <- readFile path-                               rawArgs <- getArgs-                               optionsWithUsage usageStr rawArgs+-- | Exit after printing usage text.+exitWithUsage :: Docopt -> IO a+exitWithUsage doc = do+  putStr $ usage doc+  exitFailure -optionsWithUsageFileDebug :: FilePath -> IO Arguments-optionsWithUsageFileDebug path = do usageStr <- readFile path-                                    rawArgs <- getArgs-                                    optionsWithUsageDebug usageStr rawArgs+-- | Exit after printing a custom message followed by usage text.+--   Intended for convenience when more context can be given about what went wrong.+exitWithUsageMessage :: Docopt -> String -> IO a+exitWithUsageMessage doc msg = do+  putStrLn msg+  putStrLn ""+  exitWithUsage doc --- ** Option lookup methods+-- Query functions+------------------ +-- | 'True' if an option was present at all in an invocation.+--+--   Useful with 'longOption's and 'shortOption's, and in conjunction with 'Control.Monad.when'. isPresent :: Arguments -> Option -> Bool-isPresent args opt = +isPresent args opt =   case opt `M.lookup` args of     Nothing  -> False     Just val -> case val of@@ -65,56 +88,53 @@       NotPresent -> False       _          -> True -isPresentM :: Monad m => Arguments -> Option -> m Bool-isPresentM args o = return $ isPresent args o- notPresent :: Arguments -> Option -> Bool-notPresent args o = not $ isPresent args o--notPresentM :: Monad m => Arguments -> Option -> m Bool-notPresentM args o = return $ not $ isPresent args o--getArg :: Monad m => Arguments -> Option -> m String-getArg args opt = -  let failure = fail $ "no argument given: " ++ show opt-  in  case opt `M.lookup` args of-        Nothing  -> failure-        Just val -> case val of-          MultiValue (v:vs) -> return v-          Value v           -> return v-          _                 -> failure          +notPresent = (not .) . isPresent -getFirstArg :: Monad m => Arguments -> Option -> m String-getFirstArg args opt = -  let failure = fail $ "no argument given: " ++ show opt-  in  case opt `M.lookup` args of-        Nothing  -> failure-        Just val -> case val of-          MultiValue vs -> if null vs then failure else return $ last vs-          Value v       -> return v-          _             -> failure          +-- | 'Just' the value of the argument supplied, or 'Nothing' if one was not given.+--+--   If the option's presence is required by your 'Docopt' usage text+--   (e.g. a positional argument), as in+--+-- > Usage:+-- >   prog <required>+--+--   then @getArg args (argument \'required\')@ is guaranteed to be a 'Just'.+getArg :: Arguments -> Option -> Maybe String+getArg args opt =+  case opt `M.lookup` args of+    Nothing  -> Nothing+    Just val -> case val of+      MultiValue (v:_) -> Just v+      Value v          -> Just v+      _                -> Nothing +-- | Same as 'getArg', but 'exitWithUsage' if 'Nothing'.+--+--   As in 'getArg', if your usage pattern required the option, 'getArgOrExitWith' will not exit.+getArgOrExitWith :: Docopt -> Arguments -> Option -> IO String+getArgOrExitWith doc args opt = exitUnless $ getArg args opt+  where exitUnless = maybe (exitWithUsageMessage doc $ "argument expected for: " ++ show opt) return +-- | Same as 'getArg', but eliminate 'Nothing' with a default argument. getArgWithDefault :: Arguments -> String -> Option -> String-getArgWithDefault args def opt = -  case args `getArg` opt of-    Just val -> val-    Nothing -> def+getArgWithDefault args def opt = fromMaybe def (args `getArg` opt) +-- | Returns all occurrences of a repeatable option, e.g. @\<file\>...@. getAllArgs :: Arguments -> Option -> [String]-getAllArgs args opt = +getAllArgs args opt =   case opt `M.lookup` args of-     Nothing  -> []-     Just val -> case val of-       MultiValue vs -> reverse vs-       Value v       -> [v] -       _             -> []--getAllArgsM :: Monad m => Arguments -> Option -> m [String]-getAllArgsM o e = return $ getAllArgs o e+    Nothing  -> []+    Just val -> case val of+      MultiValue vs -> reverse vs+      Value v       -> [v]+      _             -> [] +-- | Return the number of occurrences of an option in an invocation.+--+--   Useful with repeatable flags, e.g. @[ -v | -vv | -vvv]@. getArgCount :: Arguments -> Option -> Int-getArgCount args opt = +getArgCount args opt =   case opt `M.lookup` args of     Nothing -> 0     Just val -> case val of@@ -125,16 +145,47 @@       _             -> 0  --- ** Public Option constructor functions+-- Option constructors+---------------------- +-- | For @Usage: prog cmd@, ask for @command \"cmd\"@. command :: String -> Option-command s = Command s+command = Command +-- | For @Usage: prog \<file\>@, ask for @argument \"file\"@. argument :: String -> Option-argument s = Argument s+argument = Argument +-- | For @Usage: prog -h@, ask for @shortOption \'h\'@. shortOption :: Char -> Option-shortOption c = ShortOption c+shortOption = ShortOption +-- | For @Usage: prog --version@, ask for @shortOption \"version\"@. longOption :: String -> Option-longOption s = LongOption s+longOption = LongOption++-- Deprecated+-------------++{-# DEPRECATED getAllArgsM "Monadic query functions will soon be removed" #-}+getAllArgsM :: Monad m => Arguments -> Option -> m [String]+getAllArgsM o e = return $ getAllArgs o e++{-# DEPRECATED notPresentM "Monadic query functions will soon be removed" #-}+notPresentM :: Monad m => Arguments -> Option -> m Bool+notPresentM args o = return $ not $ isPresent args o++{-# DEPRECATED isPresentM "Monadic query functions will soon be removed" #-}+isPresentM :: Monad m => Arguments -> Option -> m Bool+isPresentM args o = return $ isPresent args o++{-# DEPRECATED getFirstArg "Use 'getAllArgs' instead" #-}+getFirstArg :: Monad m => Arguments -> Option -> m String+getFirstArg args opt =+  let failure = fail $ "no argument given: " ++ show opt+  in  case opt `M.lookup` args of+        Nothing  -> failure+        Just val -> case val of+          MultiValue vs -> if null vs then failure else return $ last vs+          Value v       -> return v+          _             -> failure
+ System/Console/Docopt/QQ.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_HADDOCK prune #-}+module System.Console.Docopt.QQ+    (+    -- * QuasiQuoter usage parsers+      docopt+    , docoptFile+    ) where++import qualified Data.Map as M++import System.Console.Docopt.Types+import System.Console.Docopt.QQ.Instances ()+import System.Console.Docopt.ApplicativeParsec+import System.Console.Docopt.UsageParse++import Language.Haskell.TH+import Language.Haskell.TH.Quote++parseFmt :: FilePath -> String -> Either ParseError OptFormat+parseFmt = runParser pDocopt M.empty++docoptExp :: String -> Q Exp+docoptExp usg = do+  let mkDocopt fmt = Docopt { usage = usg, optFormat = fmt }+  loc <- loc_filename <$> location+  case mkDocopt <$> parseFmt loc usg of+    Left err     -> fail $ show err+    Right parser -> [| parser |]++-- | A 'QuasiQuoter' which parses a usage string and returns a+-- 'Docopt'.+--+-- Example usage:+--+-- @+-- patterns :: Docopt+-- patterns = [docopt|+-- docopt-sample version 0.1.0+--+-- Usage:+--   docopt-sample cat \<file\>+--   docopt-sample echo [--caps] \<string\>+--+-- Options:+--   -c, --caps    Caps-lock the echoed argument+-- |]+-- @+--+-- For help with the docopt usage format, see+-- <https://github.com/docopt/docopt.hs/blob/master/README.md#help-text-format the readme on github>.+docopt :: QuasiQuoter+docopt = QuasiQuoter { quoteExp  = docoptExp+                     , quoteDec  = unsupported "Declaration"+                     , quotePat  = unsupported "Pattern"+                     , quoteType = unsupported "Type"+                     }+    where unsupported = fail . (++ " context unsupported")++-- | Same as 'docopt', but parses the given file instead of a literal+-- string.+--+-- Example:+--+-- @+-- patterns :: Docopt+-- patterns = [docoptFile|USAGE|]+-- @+--+-- where @USAGE@ is the name of a file which contains the usage+-- string (relative to the directory from which ghc is invoked).+docoptFile :: QuasiQuoter+docoptFile = quoteFile docopt
+ System/Console/Docopt/QQ/Instances.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_HADDOCK hide, prune #-}+module System.Console.Docopt.QQ.Instances where++import System.Console.Docopt.Types+import Language.Haskell.TH.Lift++import qualified Data.Map as M++instance (Lift k, Lift v) => Lift (M.Map k v) where+    lift m = [| M.fromList assoc |]+        where assoc = M.toList m++$(deriveLiftMany [ ''Option+                 , ''Pattern+                 , ''OptionInfo+                 , ''Docopt+                 ])
System/Console/Docopt/Types.hs view
@@ -28,6 +28,7 @@ atoms (Repeated p)   = atoms p atoms (Atom a)       = [a] +-- | A named leaf node of the usage pattern tree data Option = LongOption Name             | ShortOption Char             | Command Name@@ -47,13 +48,13 @@  -- | Used when parsing through the available option descriptions. --   Holds a list of synonymous options, Maybe a default value (if specified),---   an expectsVal :: Bool that indicates whether this option is a flag (--flag) ---   or an option that needs an argument (--opt=arg), and isRepeated :: Bool +--   an expectsVal :: Bool that indicates whether this option is a flag (--flag)+--   or an option that needs an argument (--opt=arg), and isRepeated :: Bool --   that indicates whether this option is always single or needs to be accumulated-data OptionInfo = OptionInfo +data OptionInfo = OptionInfo                   { synonyms :: [Option]                   , defaultVal :: Maybe String-                  , expectsVal :: Bool +                  , expectsVal :: Bool                   , isRepeated :: Bool                   } deriving (Show, Eq) @@ -71,8 +72,8 @@ --   Used to build the actual command-line arg parser. type OptFormat = (OptPattern, OptInfoMap) --- | -data OptParserState = OptParserState +-- |+data OptParserState = OptParserState                       { optInfoMap :: OptInfoMap                       , parsedArgs :: Arguments                       , inShortOptStack :: Bool@@ -98,3 +99,8 @@ --   (in order of last to first, if multiple values encountered) type Arguments = Map Option ArgValue +-- | An abstract data type which represents Docopt usage patterns.+data Docopt = Docopt { optFormat :: OptFormat+                     -- | Retrieve the original usage string.+                     , usage :: String+                     }
docopt.cabal view
@@ -1,5 +1,5 @@ name:                docopt-version:             0.6.0.2+version:             0.7.0.0 synopsis:            A command-line interface parser that will make you smile description:         Docopt parses command-line interface usage text that adheres to a familiar syntax, and from it builds a command-line argument parser that will ensure your program is invoked correctly with the available options specified in the usage text. This allows the developer to write a usage text and get an argument parser for free. @@ -7,9 +7,8 @@ license-file:        LICENSE.txt author:              Ryan Artecona maintainer:          ryanartecona@gmail.com-copyright:           (c) 2013 Ryan Artecona +copyright:           (c) 2013-2015 Ryan Artecona -stability:           Experimental category:            Console  build-type:          Simple@@ -18,9 +17,18 @@ homepage:            https://github.com/docopt/docopt.hs bug-reports:         https://github.com/docopt/docopt.hs/issues +extra-source-files:  README.md+                     CHANGELOG.md++flag template-haskell+  default:      True+  manual:       True+  description:+    Build with QuasiQuoter usage parsers, which requires Template Haskell+ library-  exposed-modules:    System.Console.Docopt- +  exposed-modules:    System.Console.Docopt.NoTH+   other-modules:      System.Console.Docopt.ApplicativeParsec                       System.Console.Docopt.ParseUtils                       System.Console.Docopt.Types@@ -31,6 +39,15 @@   build-depends:      base == 4.*,                       parsec == 3.1.*,                       containers++  ghc-options:        -Wall -fno-warn-unused-do-bind++  if impl(ghc >= 6.10) && flag(template-haskell)+    exposed-modules:  System.Console.Docopt+    other-modules:    System.Console.Docopt.QQ+                      System.Console.Docopt.QQ.Instances+    build-depends:    template-haskell >= 2.7 && < 3.0,+                      th-lift >= 0.7 && < 1.0  test-suite tests   type:               exitcode-stdio-1.0