diff --git a/src/Docopt/Docopt.purs b/src/Docopt/Docopt.purs index c60bacad..b76ebfaa 100644 --- a/src/Docopt/Docopt.purs +++ b/src/Docopt/Docopt.purs @@ -80,8 +80,7 @@ run :: forall e run d o = do argv <- maybe (A.drop 2 <$> Process.argv) (return <<< id) o.argv env <- maybe Process.getEnv (return <<< id) o.env - either onError return - do + either onError return do { specification, usage } <- parseDocopt d o.smartOptions lmap ((help usage) ++ _) do evalDocopt specification env argv o.optionsFirst diff --git a/src/Language/Docopt.purs b/src/Language/Docopt.purs index f4c300a8..40cf4cf3 100644 --- a/src/Language/Docopt.purs +++ b/src/Language/Docopt.purs @@ -43,20 +43,6 @@ data Origin | Environment | Default --- newtype RichValue = RichValue { --- value :: D.Value --- , origin :: Origin --- , env :: Maybe { --- key :: String --- , value :: Maybe String --- } --- , default :: Maybe D.Value --- } - --- data Output --- = SimpleOutput (StrMap D.Value) --- | RichOutput (StrMap RichValue) - -- | -- | Parse the docopt text and produce a parser -- | that can be applied to user input. diff --git a/src/Language/Docopt/Trans/Origin.purs b/src/Language/Docopt/Origin.purs similarity index 73% rename from src/Language/Docopt/Trans/Origin.purs rename to src/Language/Docopt/Origin.purs index 27d766ff..bc4cbe49 100644 --- a/src/Language/Docopt/Trans/Origin.purs +++ b/src/Language/Docopt/Origin.purs @@ -1,4 +1,4 @@ -module Language.Docopt.Trans.Origin ( +module Language.Docopt.Origin ( Origin(..) ) where @@ -17,6 +17,12 @@ weight Environment = 2 weight Default = 1 weight Empty = 0 +instance showOrigin :: Show Origin where + show Argv = "Argv" + show Environment = "Environment" + show Default = "Default" + show Empty = "Empty" + instance eqOrigin :: Eq Origin where eq Argv Argv = true eq Environment Environment = true diff --git a/src/Language/Docopt/ParserGen.purs b/src/Language/Docopt/ParserGen.purs index efbb0bdf..b6aded19 100644 --- a/src/Language/Docopt/ParserGen.purs +++ b/src/Language/Docopt/ParserGen.purs @@ -22,9 +22,9 @@ import Language.Docopt.ParserGen.Token (PositionedToken(..), Token(..), getSource, prettyPrintToken, unPositionedToken) as G import Language.Docopt.ParserGen.Parser (Parser, genUsageParser, - initialState) as G + RichValue(..), unRichValue, + from, initialState, ValueMapping()) as G import Language.Docopt.ParserGen.Lexer (lex) as G -import Language.Docopt.ParserGen.ValueMapping (ValueMapping) as G type Result = Tuple D.Branch (List G.ValueMapping) diff --git a/src/Language/Docopt/ParserGen/Parser.purs b/src/Language/Docopt/ParserGen/Parser.purs index d2c59650..deb6a454 100644 --- a/src/Language/Docopt/ParserGen/Parser.purs +++ b/src/Language/Docopt/ParserGen/Parser.purs @@ -8,8 +8,13 @@ module Language.Docopt.ParserGen.Parser ( genUsageParser , initialState - , Parser() - , StateObj + , Parser () + , StateObj () + , RichValue (..) + , RichValueObj (..) + , ValueMapping () + , unRichValue + , from ) where import Prelude @@ -42,39 +47,67 @@ import Control.Monad.State (State, evalState) import Control.Monad.State as State import Data.StrMap (StrMap()) import Control.Monad.Trans (lift) -import Control.MonadPlus.Partial (mrights, mlefts) +import Control.MonadPlus.Partial (mrights, mlefts, mpartition) import Text.Parsing.Parser (PState(..), ParseError(..), ParserT(..), fail, parseFailed, unParserT) as P import Text.Parsing.Parser.Combinators (option, try, lookAhead, ()) as P import Text.Parsing.Parser.Pos (Position, initialPos) as P -import Language.Docopt.Value (Value(..), isBoolValue) as D import Language.Docopt.Argument (Argument(..), Branch(..), isFree, runBranch, prettyPrintArg, prettyPrintArgNaked, hasEnvBacking, getArgument, hasDefault, isRepeatable, isFlag, setRequired) as D import Language.Docopt.Usage (Usage, runUsage) as D -import Language.Docopt.Env (Env) as D +import Language.Docopt.Env (Env ()) +import Language.Docopt.Env as Env import Language.Docopt.Option as O -import Language.Docopt.Value as Value +import Language.Docopt.Origin as Origin +import Language.Docopt.Origin (Origin()) +import Language.Docopt.Value as Value +import Language.Docopt.Value (Value(..)) import Language.Docopt.Parser.Base (getInput) import Language.Docopt.ParserGen.Token as Token import Language.Docopt.ParserGen.Token (PositionedToken(..), Token(..), unPositionedToken, prettyPrintToken) -import Language.Docopt.ParserGen.ValueMapping (ValueMapping) import Data.String.Ext (startsWith) debug :: Boolean debug = false --- | --- | Unfortunately, the State Monad is needed because we try matching all --- | program branches and must select the best fit. --- | + +-- | The value type the parser collects +type RichValueObj = { + value :: Value +, origin :: Origin +} + +newtype RichValue = RichValue RichValueObj + +instance showRichValue :: Show RichValue where + show (RichValue v) = "RichValue { origin: " ++ show v.origin + ++ ", value: " ++ show v.value ++ "}" + +instance eqRichValue :: Eq RichValue where + eq (RichValue v) (RichValue v') = (v.origin == v'.origin) && + (v.value == v'.value) + +unRichValue :: RichValue -> RichValueObj +unRichValue (RichValue o) = o + +from :: Origin -> Value -> RichValue +from o v = RichValue $ { value: v, origin: o } + +fromArgv :: Value -> RichValue +fromArgv = from Origin.Argv + +-- | The output value mappings of arg -> val +type ValueMapping = Tuple D.Argument RichValue + +-- | The stateful parser type type StateObj = { depth :: Int , fatal :: Maybe P.ParseError } type Parser a = P.ParserT (List PositionedToken) - (ReaderT D.Env (State StateObj)) + (ReaderT Env (State StateObj)) a initialState :: StateObj @@ -85,41 +118,6 @@ modifyDepth :: (Int -> Int) -> Parser Unit modifyDepth f = do lift (State.modify \s -> s { depth = f s.depth }) -newtype ScoredResult a = ScoredResult { - score :: Int -- ^ the score of the parse -, result :: a -- ^ the result of the parse -} - -unScoredResult :: forall a. ScoredResult a -> { score :: Int, result :: a } -unScoredResult (ScoredResult r) = r - -instance semigroupScoredResult :: (Semigroup a) => Semigroup (ScoredResult a) - where append (ScoredResult s) (ScoredResult s') - = ScoredResult { score: s.score + s'.score - , result: s.result ++ s'.result } - -instance monoidScoredResult :: (Monoid a) => Monoid (ScoredResult a) - where mempty = ScoredResult { score: 0, result: mempty } - -instance showScoredResult :: (Show a) => Show (ScoredResult a) - where show (ScoredResult { score, result }) - = "ScoredResult " ++ show score ++ ": " ++ show result - -instance ordScoredResult :: Ord (ScoredResult a) - where compare = compare `on` (_.score <<< unScoredResult) - -instance eqScoredResult :: Eq (ScoredResult a) - where eq = eq `on` (_.score <<< unScoredResult) - -score :: forall a. Int -> List a -> ScoredResult (List a) -score score result = ScoredResult { score, result } - -scoreFromList :: forall a. List a -> ScoredResult (List a) -scoreFromList xs = ScoredResult { score: length xs, result: xs } - -rmapScoreResult :: forall a b. (a -> b) -> ScoredResult a -> ScoredResult b -rmapScoreResult f (ScoredResult (x@{ result })) = ScoredResult $ x { result = f result } - -------------------------------------------------------------------------------- -- Input Token Parser ---------------------------------------------------------- -------------------------------------------------------------------------------- @@ -148,34 +146,34 @@ data Acc a = Free (Parser a) | Pending (Parser a) (List D.Argument) -eoa :: Parser D.Value +eoa :: Parser Value eoa = token go P. "--" where - go (EOA xs) = Just (D.ArrayValue (fromList xs)) + go (EOA xs) = Just (ArrayValue (fromList xs)) go _ = Nothing -command :: String -> Parser D.Value +command :: String -> Parser Value command n = token go P. "command " ++ show n where - go (Lit s) | s == n = Just (D.BoolValue true) + go (Lit s) | s == n = Just (BoolValue true) go _ = Nothing -positional :: String -> Parser D.Value +positional :: String -> Parser Value positional n = token go P. "positional argument " ++ show n where go (Lit v) = Just (Value.read v false) go _ = Nothing -stdin :: Parser D.Value +stdin :: Parser Value stdin = token go P. "stdin flag" where - go Stdin = Just (D.BoolValue true) + go Stdin = Just (BoolValue true) go _ = Nothing type HasConsumedArg = Boolean -data OptParse = OptParse D.Value (Maybe Token) HasConsumedArg +data OptParse = OptParse Value (Maybe Token) HasConsumedArg -longOption :: O.Name -> (Maybe O.Argument) -> Parser D.Value +longOption :: O.Name -> (Maybe O.Argument) -> Parser Value longOption n a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> return $ case toks of Cons (PositionedToken { token: tok, sourcePos: npos, source: s }) xs -> @@ -213,7 +211,7 @@ longOption n a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> Just (Lit s) -> return $ OptParse (Value.read s false) Nothing true otherwise -> if (fromMaybe true (_.optional <<< O.runArgument <$> a)) - then Right $ OptParse (D.BoolValue true) Nothing false + then Right $ OptParse (BoolValue true) Nothing false else Left $ "Option requires argument: --" ++ n' -- case 2: @@ -221,7 +219,7 @@ longOption n a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> go (LOpt n' v) _ | isFlag && (n' == n) = case v of Just _ -> Left $ "Option takes no argument: --" ++ n' - Nothing -> return $ OptParse (D.BoolValue true) Nothing false + Nothing -> return $ OptParse (BoolValue true) Nothing false -- case 3: -- The name is a substring of the input and no explicit argument has been @@ -233,7 +231,7 @@ longOption n a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> go a b = Left $ "Invalid token" ++ show a ++ " (input: " ++ show b ++ ")" -shortOption :: Char -> (Maybe O.Argument) -> Parser D.Value +shortOption :: Char -> (Maybe O.Argument) -> Parser Value shortOption f a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> do return $ case toks of Cons (PositionedToken { token: tok, source: s }) xs -> @@ -274,7 +272,7 @@ shortOption f a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> do Just (Lit s) -> return $ OptParse (Value.read s false) Nothing true otherwise -> if (fromMaybe true (_.optional <<< O.runArgument <$> a)) - then Right $ OptParse (D.BoolValue true) Nothing false + then Right $ OptParse (BoolValue true) Nothing false else Left $ "Option requires argument: -" ++ fromChar f' -- case 2: @@ -291,7 +289,7 @@ shortOption f a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> do -- The leading flag matches, there are stacked options, the option takes -- no argument and an explicit argument has not been provided. go (SOpt f' xs v) _ | (f' == f) && (isFlag) && (not $ A.null xs) - = return $ OptParse (D.BoolValue true) + = return $ OptParse (BoolValue true) (Just $ SOpt (AU.head xs) (AU.tail xs) v) false @@ -301,7 +299,7 @@ shortOption f a = P.ParserT $ \(P.PState { input: toks, position: pos }) -> do go (SOpt f' xs v) _ | (f' == f) && (isFlag) && (A.null xs) = case v of Just _ -> Left $ "Option takes no argument: -" ++ fromChar f' - Nothing -> return $ OptParse (D.BoolValue true) + Nothing -> return $ OptParse (BoolValue true) Nothing false @@ -326,11 +324,10 @@ genUsageParser :: List D.Usage -- ^ The list of usage specs -> Boolean -- ^ Enable "options-first" -> Parser (Tuple D.Branch (List ValueMapping)) genUsageParser xs optsFirst = do - _.result <<< unScoredResult - <$> genBranchesParser (concat $ D.runUsage <$> xs) - true - optsFirst - true + genBranchesParser (concat $ D.runUsage <$> xs) + true + optsFirst + true -- | Generate a parser that selects the best branch it parses and -- | fails if no branch was parsed. @@ -338,14 +335,14 @@ genBranchesParser :: List D.Branch -- ^ The branches to test -> Boolean -- ^ Expect EOF after each branch -> Boolean -- ^ Enable "options-first" -> Boolean -- ^ Can we skip input via fallbacks? - -> Parser (ScoredResult (Tuple D.Branch (List ValueMapping))) + -> Parser (Tuple D.Branch (List ValueMapping)) genBranchesParser xs term optsFirst canSkip = P.ParserT \(s@(P.PState { input: i, position: pos })) -> do - env :: D.Env <- ask + env :: Env <- ask state :: StateObj <- lift State.get let - ps = xs <#> \x -> rmapScoreResult (Tuple x) <$> do + ps = xs <#> \x -> (Tuple x) <$> do genBranchParser x optsFirst canSkip <* unless (not term) eof rs = evalState (runReaderT (collect s ps) env) initialState @@ -356,13 +353,12 @@ genBranchesParser xs term optsFirst canSkip -- elements, take the highest element and sort the inner values by their -- fallback score. winner = - maximumBy (compare `on` (_.score <<< unScoredResult - <<< _.value - <<< _.result)) - =<< do - last $ groupBy (eq `on` (_.depth <<< _.result)) - (sortBy (compare `on` (_.depth <<< _.result)) - (mrights rs')) + maximumBy (compare `on` + (((_.origin <<< unRichValue <<< snd) <$> _) <<< snd + <<< _.value <<< _.result)) =<< do + last $ groupBy (eq `on` (_.depth <<< _.result)) + (sortBy (compare `on` (_.depth <<< _.result)) + (mrights rs')) -- Evaluate the losing candidates, if any. losers = @@ -429,7 +425,7 @@ genBranchesParser xs term optsFirst canSkip genBranchParser :: D.Branch -- ^ The branch to match -> Boolean -- ^ Enable "options-first" -> Boolean -- ^ Can we skip input via fallbacks? - -> Parser (ScoredResult (List ValueMapping)) + -> Parser (List ValueMapping) genBranchParser (D.Branch xs) optsFirst canSkip = do modifyDepth (const 0) -- reset the depth counter either @@ -448,7 +444,7 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do -- The only requirement is that all input is consumed in the end. genExhaustiveParser :: List D.Argument -- ^ The free arguments -> Boolean -- ^ Can we skip input via fallbacks? - -> Parser (ScoredResult (List ValueMapping)) + -> Parser (List ValueMapping) genExhaustiveParser Nil canSkip = return mempty genExhaustiveParser ps canSkip = do if debug @@ -465,34 +461,31 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do draw :: List D.Argument -- ^ the arguments to parse -> Int -- ^ the number of options left to parse -> List D.Argument -- ^ the unique arguments parsed - -> Parser (ScoredResult (List ValueMapping)) + -> Parser (List ValueMapping) draw pss@(Cons p ps') n tot | n >= 0 = (do - if debug - then do - i <- getInput - traceA $ - "draw: (" ++ (D.prettyPrintArg p) ++ ":" - ++ (intercalate ":" (D.prettyPrintArg <$> ps')) - ++ ") - n: " ++ show n - ++ "from input: " - ++ (intercalate " " (prettyPrintToken - <<< _.token - <<< unPositionedToken <$> i)) - else return unit + when debug do + i <- getInput + traceA $ + "draw: (" ++ (D.prettyPrintArg p) ++ ":" + ++ (intercalate ":" (D.prettyPrintArg <$> ps')) + ++ ") - n: " ++ show n + ++ "from input: " + ++ (intercalate " " (prettyPrintToken + <<< _.token + <<< unPositionedToken <$> i)) -- Generate the parser for the argument `p`. For groups, temporarily -- set the required flag to "true", such that it will fail and we have -- a chance to retry as part of the exhaustive parsing mechanism - r <- unScoredResult <$> (P.try $ genParser (D.setRequired p true) - (not $ n > 0)) + r <- P.try $ genParser (D.setRequired p true) (not $ n > 0) - r' <- unScoredResult <$> P.try do + r' <- P.try do if D.isRepeatable p then draw pss (length pss) (p:tot) else draw ps' (length ps') (p:tot) - return $ ScoredResult r ++ ScoredResult r' + return $ r ++ r' ) <|> (defer \_ -> do state :: StateObj <- lift State.get case state.fatal of @@ -503,48 +496,78 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do draw ps' n tot | (length ps' > 0) && (n < 0) = do env :: StrMap String <- lift ask - -- Find flags missing from the input. - -- If we are explicitly allowed to skip arguments because of lack of - -- input, or we have *at least* one match (i.e. the remainder is less - -- than the original), then ignore arguments for which a suitable - -- fallback value can be provided. - let missing = filter (\o -> not $ - (canSkip || (length ps' < length ps)) - && isSkippable env o - ) ps' - - if (length missing > 0) - then P.fail $ - "Expected option(s): " - ++ intercalate ", " (D.prettyPrintArgNaked <$> missing) - else return $ ScoredResult { - score: sum $ ps' <#> \o -> - if D.hasEnvBacking o env - then 2 - else if D.hasDefault o - then 1 - else 0 - , result: Nil - } + let + vs = ps' <#> \o -> + maybe + (Left o) + (Right <<< Tuple o) + do + (RichValue v) <- do + guard (canSkip || (length ps' < length ps)) + (getEnvValue env o <#> from Origin.Environment) <|> + (getDefaultValue o <#> from Origin.Default) <|> + (getEmptyValue o <#> from Origin.Empty) + + return $ RichValue v { + value = if D.isRepeatable o + then ArrayValue $ Value.intoArray v.value + else v.value + } + + missing = filter (not <<< isSkippable) (mlefts vs) + fallbacks = mrights vs + + if canSkip + then do + xs <- genExhaustiveParser missing false + return $ fallbacks ++ xs + else + if (length missing > 0) + then P.fail $ + "Expected option(s): " + ++ intercalate ", " (D.prettyPrintArgNaked <$> missing) + else return fallbacks where - isSkippable env (D.Group o bs _) - = o || (all (all (isSkippable env) <<< D.runBranch) bs) - isSkippable env o - = (D.hasDefault o) - || (maybe true id do - arg <- O.runArgument <$> do - D.getArgument o - return arg.optional - ) - || (D.hasEnvBacking o env) - || (D.isRepeatable o && (any (_ == o) tot)) + isSkippable (D.Group o bs _) + = o || (all (all isSkippable <<< D.runBranch) bs) + isSkippable o = D.isRepeatable o && (any (_ == o) tot) + + getEnvValue :: Env -> D.Argument -> Maybe Value + getEnvValue env (D.Option (O.Option o@{ env: Just k })) = do + StringValue <$> Env.lookup k env + getEnvValue _ _ = Nothing + + getDefaultValue :: D.Argument -> Maybe Value + getDefaultValue (D.Option (O.Option o@{ + arg: Just (O.Argument { default: Just v }) + })) = return if o.repeatable + then ArrayValue $ Value.intoArray v + else v + getDefaultValue _ = Nothing + + getEmptyValue :: D.Argument -> Maybe Value + getEmptyValue = go + where + go (D.Option (O.Option o@{ arg: Nothing })) + = return + $ if o.repeatable then ArrayValue [] + else BoolValue false + go (D.Option (O.Option o@{ arg: Just (O.Argument { optional: true }) })) + = return + $ if o.repeatable then ArrayValue [] + else BoolValue false + go (D.Positional _ r) | r = return $ ArrayValue [] + go (D.Command _ r) | r = return $ ArrayValue [] + go (D.Stdin) = return $ BoolValue false + go (D.EOA) = return $ ArrayValue [] + go _ = Nothing draw _ _ _ = return mempty - step :: Acc (ScoredResult (List ValueMapping)) + step :: Acc (List ValueMapping) -> D.Argument - -> Either P.ParseError (Acc (ScoredResult (List ValueMapping))) + -> Either P.ParseError (Acc (List ValueMapping)) -- Options always transition to the `Pending state` step (Free p) x@(D.Option _) @@ -580,44 +603,44 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do -- values int an array ("options-first") terminate arg = do input <- getInput - let rest = Tuple arg <$> do - D.StringValue <<< Token.getSource <$> input + let rest = Tuple arg <<< fromArgv <$> do + StringValue <<< Token.getSource <$> input P.ParserT \(P.PState { position: pos }) -> return { consumed: true , input: Nil - , result: return $ ScoredResult { - score: 1 - , result: rest - } + , result: return rest , position: pos } -- Parser generator for a single `Argument` genParser :: D.Argument -- ^ The argument to generate a parser for -> Boolean -- ^ Can we skip input via fallbacks? - -> Parser (ScoredResult (List ValueMapping)) + -> Parser (List ValueMapping) -- Generate a parser for a `Command` argument genParser x@(D.Command n r) _ = do i <- getInput - score 0 <$> (do + (do if r then (some go) else (singleton <$> go) ) <|> (P.fail $ "Expected " ++ D.prettyPrintArg x ++ butGot i) - where go = do - Tuple x <$> (command n) - <* modifyDepth (_ + 1) + where go = do Tuple x <<< fromArgv <$> (do + v <- command n + return if r then ArrayValue $ Value.intoArray v + else v + ) + <* modifyDepth (_ + 1) -- Generate a parser for a `EOA` argument genParser x@(D.EOA) _ = do - score 0 <<< singleton <<< Tuple x <$> (do - eoa <|> (return $ D.ArrayValue []) -- XXX: Fix type + singleton <<< Tuple x <<< fromArgv <$> (do + eoa <|> (return $ ArrayValue []) -- XXX: Fix type <* modifyDepth (_ + 1) ) <|> P.fail "Expected \"--\"" -- Generate a parser for a `Stdin` argument genParser x@(D.Stdin) _ = do - score 0 <<< singleton <<< Tuple x <$> (do + singleton <<< Tuple x <<< fromArgv <$> (do stdin <* modifyDepth (_ + 1) ) <|> P.fail "Expected \"-\"" @@ -629,12 +652,15 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do -- Generate a parser for a `Positional` argument genParser x@(D.Positional n r) _ = do i <- getInput - score 0 <$> (do + (do if r then (some go) else (singleton <$> go) ) <|> P.fail ("Expected " ++ D.prettyPrintArg x ++ butGot i) - where go = do - Tuple x <$> (positional n) - <* modifyDepth (_ + 1) + where go = do Tuple x <<< fromArgv <$> (do + v <- positional n + return if r then ArrayValue $ Value.intoArray v + else v + ) + <* modifyDepth (_ + 1) genParser x@(D.Group optional bs r) _ | optsFirst && (length bs == 1) && @@ -647,7 +673,7 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do -- Generate a parser for a `Option` argument genParser x@(D.Option (O.Option o)) _ = (do - score 0 <$> do + do if o.repeatable then (some go) else (singleton <$> go) <* modifyDepth (_ + 1) ) @@ -662,9 +688,19 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do isSopt <- P.option false (P.lookAhead $ P.try $ token isAnySopt) P.ParserT \s -> do o <- P.unParserT (if isLopt - then P.try $ Tuple x <$> mkLoptParser o.name o.arg + then P.try do + Tuple x <<< fromArgv <$> (do + v <- mkLoptParser o.name o.arg + return if o.repeatable then ArrayValue $ Value.intoArray v + else v + ) else if isSopt - then P.try $ Tuple x <$> mkSoptParser o.flag o.arg + then P.try do + Tuple x <<< fromArgv <$> (do + v <- mkSoptParser o.flag o.arg + return if o.repeatable then ArrayValue $ Value.intoArray v + else v + ) else P.fail "long or short option") s case o.result of (Left e) -> do @@ -699,13 +735,13 @@ genBranchParser (D.Branch xs) optsFirst canSkip = do go | length bs == 0 = return mempty go = do x <- step - if repeated && length (_.result $ unScoredResult x) > 0 + if repeated && length x > 0 then do xs <- step <|> return mempty return $ x ++ xs else return x - step = rmapScoreResult snd <$> do + step = snd <$> do genBranchesParser bs false optsFirst diff --git a/src/Language/Docopt/Trans/Flat.purs b/src/Language/Docopt/Trans/Flat.purs index 4bd1c0d1..79770e83 100644 --- a/src/Language/Docopt/Trans/Flat.purs +++ b/src/Language/Docopt/Trans/Flat.purs @@ -9,8 +9,7 @@ import Language.Docopt.Env (Env) import Language.Docopt.Value (Value()) import Language.Docopt.Usage (Usage()) as D import Language.Docopt.Trans.Rich (reduce) as Rich -import Language.Docopt.Trans.Rich (unRichValue) -import Language.Docopt.ParserGen.ValueMapping (ValueMapping) +import Language.Docopt.ParserGen (ValueMapping, unRichValue) import Language.Docopt.Argument (Branch()) as D reduce :: List D.Usage -- ^ the program specification diff --git a/src/Language/Docopt/Trans/Rich.purs b/src/Language/Docopt/Trans/Rich.purs index 6f7c290e..42369dc9 100644 --- a/src/Language/Docopt/Trans/Rich.purs +++ b/src/Language/Docopt/Trans/Rich.purs @@ -1,13 +1,12 @@ module Language.Docopt.Trans.Rich ( reduce - , RichValue - , RichValueObj - , unRichValue ) where import Prelude -import Data.List (List(), catMaybes, singleton, concat, toList) +import Debug.Trace +import Data.List (List(..), catMaybes, singleton, concat, toList, filter, + reverse, null, nub) import Data.Array (length, filter) as A import Data.Maybe (Maybe(..), fromMaybe) import Data.Maybe.Unsafe (fromJust) @@ -16,7 +15,7 @@ import Data.Tuple (Tuple(Tuple)) import Data.Map (Map()) import Data.StrMap as StrMap import Data.StrMap (StrMap()) -import Data.Bifunctor (lmap) +import Data.Bifunctor (rmap, lmap) import Data.Foldable (foldl, maximum) import Control.Alt ((<|>)) @@ -29,43 +28,25 @@ import Language.Docopt.Env as Env import Language.Docopt.Argument (Argument(..), Branch(..), isRepeatable, setRepeatable, runBranch, setRepeatableOr, isCommand, isFlag) as D -import Language.Docopt.ParserGen.ValueMapping (ValueMapping) -import Language.Docopt.Trans.Origin as Origin -import Language.Docopt.Trans.Origin (Origin()) +import Language.Docopt.ParserGen (ValueMapping, RichValue(..), unRichValue) +import Language.Docopt.Origin as Origin +import Language.Docopt.Origin (Origin()) import Language.Docopt.Trans.Key (Key(..), key, toKeys) -type RichValueObj = { - value :: Value -, origin :: Origin -} - -newtype RichValue = RichValue RichValueObj - -unRichValue :: RichValue -> RichValueObj -unRichValue (RichValue o) = o - reduce :: List D.Usage -- ^ the program specification -> Env -- ^ the environment -> D.Branch -- ^ the matched specification -> List ValueMapping -- ^ the parse result -> StrMap RichValue -- ^ the output set of (arg => val) reduce us env b vs = - let vm = Map.fromFoldableWith mergeVals $ fromArgv vs - m = applyValues vm $ reduceUsage (D.Usage (singleton b)) - in finalFold m + let vm = Map.fromFoldableWith (++) (rmap singleton <$> + lmap key <$> + reverse vs) + m = applyValues vm $ reduceUsage (D.Usage (singleton b)) + in finalFold m where - fromArgv :: List ValueMapping -> List (Tuple Key RichValue) - fromArgv vs = lmap key <$> (go <$> vs) - where - go (Tuple a v) = Tuple a $ RichValue { - origin: Origin.Argv - , value: if D.isRepeatable a - then ArrayValue (Value.intoArray v) - else v - } - mergeVals :: RichValue -> RichValue -> RichValue mergeVals (RichValue v) (RichValue v') = RichValue $ { origin: fromJust $ maximum [ v.origin, v'.origin ] @@ -73,60 +54,28 @@ reduce us env b vs = ++ Value.intoArray v.value } - applyValues :: Map Key RichValue -> List D.Argument -> Map Key RichValue + applyValues :: Map Key (List RichValue) -> List D.Argument -> Map Key RichValue applyValues vm as = Map.fromFoldableWith mergeVals + $ concat $ catMaybes - $ as <#> \a -> Tuple (key a) <$> do - (RichValue v) <- getValue vm a - <|> getEnvValue a - <|> getDefaultValue a - <|> getEmptyValue a - - return $ RichValue v { - value = if D.isRepeatable a - then ArrayValue $ Value.intoArray v.value - else v.value - } - where + $ as <#> \a -> do + vs <- Map.lookup (key a) vm + let vs' = filter (origin (/=) Origin.Empty) vs + vs'' = filter (origin (/=) Origin.Default) vs' + vs''' = case vs'' of + Nil -> nub vs' + vs -> vs + vs'''' = vs''' <#> \(RichValue v) -> RichValue $ v { + value = if D.isRepeatable a + then ArrayValue $ Value.intoArray v.value + else v.value + } + + return $ (Tuple (key a)) <$> vs'''' - getValue :: Map Key RichValue -> D.Argument -> Maybe RichValue - getValue vm a = Map.lookup (key a) vm - - getEnvValue :: D.Argument -> Maybe RichValue - getEnvValue (D.Option (O.Option o@{ env: Just k })) = do - s <- Env.lookup k env - return $ RichValue { - origin: Origin.Environment - , value: StringValue s - } - getEnvValue _ = Nothing - - getDefaultValue :: D.Argument -> Maybe RichValue - getDefaultValue (D.Option (O.Option o@{ - arg: Just (O.Argument { default: Just v }) - })) = return - $ RichValue { - origin: Origin.Default - , value: if (o.repeatable) - then ArrayValue $ Value.intoArray v - else v - } - getDefaultValue _ = Nothing - - getEmptyValue :: D.Argument -> Maybe RichValue - getEmptyValue x = RichValue <<< { origin: Origin.Empty - , value: _ - } <$> go x - where - go (D.Option (O.Option o@{ arg: Nothing })) - = return - $ if o.repeatable then ArrayValue [] - else BoolValue false - go (D.Positional _ r) | r = return $ ArrayValue [] - go (D.Command _ r) | r = return $ ArrayValue [] - go (D.Stdin) = return $ BoolValue false - go (D.EOA) = return $ ArrayValue [] - go _ = Nothing + where + isRelevant a = Map.member (key a) vm + origin cmp o = \x -> (_.origin $ unRichValue x) `cmp` o finalFold :: Map Key RichValue -> StrMap RichValue finalFold m = @@ -146,7 +95,7 @@ reduce us env b vs = BoolValue b -> if D.isRepeatable a then - return $ if b + return if b then IntValue 1 else IntValue 0 else Nothing diff --git a/test/Test/Spec/ParserGenSpec.purs b/test/Test/Spec/ParserGenSpec.purs index c172fa8b..4e2d7f8b 100644 --- a/test/Test/Spec/ParserGenSpec.purs +++ b/test/Test/Spec/ParserGenSpec.purs @@ -76,6 +76,8 @@ fail' i e err = Case i e (Left err) infixr 0 :> parserGenSpec = \_ -> describe "The parser generator" do + it "" do + pure unit -- Some options that will be used for these tests let testCases = [ @@ -163,7 +165,7 @@ parserGenSpec = \_ -> describe "The parser generator" do , D.opt 'o' "output" (D.oa_ "FILE") ] [ fail [] - $ "Expected option(s): -o|--output=FILE, -i|--input=FILE" + $ "Expected option(s): -i|--input=FILE, -o|--output=FILE" , fail [ "-i", "bar" ] $ "Expected option(s): -o|--output=FILE" @@ -192,7 +194,7 @@ parserGenSpec = \_ -> describe "The parser generator" do , D.opt 'o' "output" (D.oa_ "FILE") ] [ fail [] - $ "Expected option(s): -o|--output=FILE, -i|--input=FILE -r|--redirect=FILE" + $ "Expected option(s): -i|--input=FILE -r|--redirect=FILE, -o|--output=FILE" , fail [ "-i", "bar", "-r", "bar" ] "Expected option(s): -o|--output=FILE" @@ -275,7 +277,7 @@ parserGenSpec = \_ -> describe "The parser generator" do [ "-b" :> D.bool true ] , pass [] - [ "-a" :> D.bool false ] + [] ] , test' @@ -364,8 +366,6 @@ parserGenSpec = \_ -> describe "The parser generator" do , "--input" :> D.bool true , "-i" :> D.bool true , "baz" :> D.bool true - , "--qux" :> D.int 0 - , "-q" :> D.int 0 ] , pass @@ -380,8 +380,6 @@ parserGenSpec = \_ -> describe "The parser generator" do , "baz" :> D.bool true , "--baz" :> D.str "ax" , "-b" :> D.str "ax" - , "--qux" :> D.int 0 - , "-q" :> D.int 0 , "--foo" :> D.array [ D.str "ox" ] , "-f" :> D.array [ D.str "ox" ] ] diff --git a/testcases.docopt b/testcases.docopt index 9745defa..8e18474b 100644 --- a/testcases.docopt +++ b/testcases.docopt @@ -15,7 +15,7 @@ Options: -a All. """ $ prog -{"-a": false} +{} $ prog -a {"-a": true} @@ -30,7 +30,7 @@ Options: --all All. """ $ prog -{"--all": false} +{} $ prog --all {"--all": true} @@ -158,12 +158,10 @@ Options: --version """ $ prog --version -{"--verbose": false, - "--version": true} +{"--version": true} $ prog --verbose -{"--verbose": true, - "--version": false} +{"--verbose": true} r"""usage: prog [-a -r -m ] @@ -213,19 +211,13 @@ $ prog -arm=Hello "-r": true} $ prog -m=Hello -{"-a": false, - "-m": "Hello", - "-r": false} +{"-m": "Hello"} $ prog -m Hello -{"-a": false, - "-m": "Hello", - "-r": false} +{"-m": "Hello"} $ prog -mHello -{"-a": false, - "-m": "Hello", - "-r": false} +{"-m": "Hello"} $ prog -m "user-error" @@ -263,19 +255,13 @@ $ prog -arm=Hello "-r": true} $ prog -m=Hello -{"-a": false, - "-m": "Hello", - "-r": false} +{"-m": "Hello"} $ prog -m Hello -{"-a": false, - "-m": "Hello", - "-r": false} +{"-m": "Hello"} $ prog -mHello -{"-a": false, - "-m": "Hello", - "-r": false} +{"-m": "Hello"} $ prog -m "user-error" @@ -300,10 +286,10 @@ $ prog -ba {"-a": true, "-b": true} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -{"-a": false, "-b": false} +{} r"""usage: prog (-a -b) @@ -325,10 +311,10 @@ $ prog -ba {"-a": true, "-b": true} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -{"-a": false, "-b": false} +{} r"""usage: prog [-a] -b @@ -344,13 +330,13 @@ $ prog -b -a {"-a": true, "-b": true} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -b -{"-a": false, "-b": true} +{"-b": true} $ prog -{"-a": false, "-b": false} +{} r"""usage: prog [(-a -b)] @@ -372,13 +358,13 @@ $ prog -ba {"-a": true, "-b": true} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -b -{"-a": false, "-b": true} +{"-b": true} $ prog -{"-a": false, "-b": false} +{} # @@ -390,13 +376,13 @@ $ prog -a -b "user-error" $ prog -{"-a": false, "-b": false} +{} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -b -{"-a": false, "-b": true} +{"-b": true} r"""usage: prog [ -a | -b ] @@ -409,13 +395,13 @@ $ prog -a -b "user-error" $ prog -{"-a": false, "-b": false} +{} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -b -{"-a": false, "-b": true} +{"-b": true} r"""usage: prog """ @@ -490,7 +476,7 @@ $ prog 10 --all "KIND": 10} $ prog 10 -{"--all": false, "": 10, "KIND": 10} +{"": 10, "KIND": 10} $ prog "user-error" @@ -511,7 +497,7 @@ $ prog 10 "user-error" $ prog -{"": [], "NAME": []} +{} # Added test case: # (See note above) @@ -523,7 +509,7 @@ $ prog 10 {"": [10], "NAME": [10]} $ prog -{"": [], "NAME": []} +{} r"""usage: prog [( )]""" @@ -534,7 +520,7 @@ $ prog 10 "user-error" $ prog -{"": [], "NAME": []} +{} r"""usage: prog NAME...""" $ prog 10 20 @@ -556,7 +542,7 @@ $ prog 10 {"": [10], "NAME": [10]} $ prog -{"": [], "NAME": []} +{} r"""usage: prog [NAME...]""" @@ -567,7 +553,7 @@ $ prog 10 {"": [10], "NAME": [10]} $ prog -{"": [], "NAME": []} +{} r"""usage: prog [NAME [NAME ...]]""" @@ -578,7 +564,7 @@ $ prog 10 {"": [10], "NAME": [10]} $ prog -{"": [], "NAME": []} +{} r"""usage: prog (NAME | --foo NAME) @@ -587,7 +573,7 @@ options: --foo """ $ prog 10 -{"--foo": false, "": 10, "NAME": 10} +{"": 10, "NAME": 10} $ prog --foo 10 {"--foo": true, "": 10, "NAME": 10} @@ -602,13 +588,13 @@ options: --bar """ $ prog 10 -{"--bar": false, "--foo": false, "": [10], "NAME": [10]} +{"": [10], "NAME": [10]} $ prog 10 20 -{"--bar": false, "--foo": false, "": [10, 20], "NAME": [10, 20]} +{"": [10, 20], "NAME": [10, 20]} $ prog --foo --bar -{"--bar": true, "--foo": true, "": [], "NAME": []} +{"--bar": true, "--foo": true} r"""Naval Fate. @@ -630,30 +616,6 @@ Options: """ -# XXX: We are deviating here. See `Language.Docopt.Trans.reduce` for a full -# explanation of the issue. In brief, this implementation, collects values -# only for arguments of the branch that was actually matched. -# Secondly, `` does not evaluate to an array. I don't see why it -# would in the original implementation either? What's the rationale here? -# -# Original: -# -# {"--drifting": false, -# "--help": false, -# "--moored": false, -# "--speed": 20, -# "--version": false, -# "": ["Guardian"], -# "": 150, -# "": 300, -# "mine": false, -# "move": true, -# "new": false, -# "remove": false, -# "set": false, -# "ship": true, -# "shoot": false} - $ prog ship Guardian move 150 300 --speed=20 {"--speed": 20, "": "Guardian", @@ -681,7 +643,7 @@ $ prog --hello wrld r"""usage: prog [-o]""" $ prog -{"-o": false} +{} $ prog -o {"-o": true} @@ -689,7 +651,7 @@ $ prog -o r"""usage: prog [-opr]""" $ prog -op -{"-o": true, "-p": true, "-r": false} +{"-o": true, "-p": true} r"""usage: prog --aabb | --aa""" @@ -715,7 +677,7 @@ $ prog -v r"""Usage: prog [-v -v]""" $ prog -{"-v": 0} +{} $ prog -v {"-v": 1} @@ -728,7 +690,7 @@ r"""Usage: prog -v ...""" # Note: Deviation: We allow not passing flags. # Original "user-error" $ prog -{"-v": 0} +{} $ prog -v {"-v": 1} @@ -746,7 +708,7 @@ This one is probably most readable user-friednly variant. """ $ prog -{"-v": 0} +{} $ prog -v {"-v": 1} @@ -776,7 +738,7 @@ $ prog go # r"""usage: prog [go go]""" r"""usage: prog [go [go]]""" $ prog -{"go": 0} +{} $ prog go {"go": 1} @@ -802,7 +764,7 @@ options: -a """ $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -aa "user-error" @@ -819,13 +781,13 @@ Options: """ $ prog arg -{"-q": false, "-v": false, "": "arg", "A": "arg"} +{"": "arg", "A": "arg"} $ prog -v arg -{"-q": false, "-v": true, "": "arg", "A": "arg"} +{"-v": true, "": "arg", "A": "arg"} $ prog -q arg -{"-q": true, "-v": false, "": "arg", "A": "arg"} +{"-q": true, "": "arg", "A": "arg"} # # Test single dash @@ -837,7 +799,7 @@ $ prog - {"-": true} $ prog -{"-": false} +{} # # If argument is repeated, its value should always be a list @@ -849,7 +811,7 @@ $ prog a b {"": ["a", "b"], "NAME": ["a", "b"]} $ prog -{"": [], "NAME": []} +{} # # Option's argument defaults to null/None @@ -885,22 +847,22 @@ $ prog --hello wrld r"""usage: prog [-o]""" $ prog -{"-o": false} +{} $ prog -o {"-o": true} r"""usage: prog [-opr]""" $ prog -op -{"-o": true, "-p": true, "-r": false} +{"-o": true, "-p": true} r"""usage: git [-v | --verbose]""" $ prog -v -{"--verbose": false, "-v": true} +{"-v": true} r"""usage: git remote [-v | --verbose]""" $ prog remote -v -{"--verbose": false, "-v": true, "remote": true} +{"-v": true, "remote": true} # # Test empty usage pattern @@ -1142,11 +1104,8 @@ other options: """ $ prog --baz --egg -{"--bar": false, - "--baz": true, - "--egg": true, - "--foo": false, - "--spam": false} +{"--baz": true, + "--egg": true} # # README example usage @@ -1368,13 +1327,11 @@ Options: -0 This is a zero """ -# XXX: this returns superflous "-0" in the output: $ prog/p foo bar -0 -{"-0": false, "ARG": ["foo", "bar", "-0"], "": ["foo", "bar", "-0"]} +{"ARG": ["foo", "bar", "-0"], "": ["foo", "bar", "-0"]} -# XXX: this returns superflous "-0" in the output: $ prog/p -0 -{"-0": false, "ARG": ["-0"], "": ["-0"]} +{"ARG": ["-0"], "": ["-0"]} r""" Usage: create_ec2.py [ARG...] [options] @@ -1383,13 +1340,11 @@ Options: -0 This is a zero """ -# XXX: this returns superflous "-0" in the output: $ prog/p foo bar -0 -{"-0": false, "ARG": ["foo", "bar", "-0"], "": ["foo", "bar", "-0"]} +{"ARG": ["foo", "bar", "-0"], "": ["foo", "bar", "-0"]} -# XXX: this returns superflous "-0" in the output: $ prog/p -0 -{"-0": false, "ARG": ["-0"], "": ["-0"]} +{"ARG": ["-0"], "": ["-0"]} r""" @@ -1405,7 +1360,7 @@ $ prog -0 # XXX: this returns superflous "-0" and "-1" in the output: $ prog/p -01 -{"-0": false, "-1": false, "ARG": ["-01"], "": ["-01"]} +{"ARG": ["-01"], "": ["-01"]} # # Options-first fix for issue #21 @@ -1416,7 +1371,7 @@ Usage: foo [[...]] """ $ prog/p -{"ARGS": [], "": []} +{} $ prog/p bar -a -b -c {"ARGS": ["bar", "-a", "-b", "-c"], @@ -1427,7 +1382,7 @@ Usage: foo [ [...]] """ $ prog/p -{"ARGS": [], "": []} +{} $ prog/p bar -a -b -c {"COMMAND": "bar", "": "bar", @@ -1525,6 +1480,26 @@ $ prog $ prog --foo {"--foo": [true]} +r""" +Usage: manage.py --foo (--foo[=BAR]...) (--foo[=BAR]...) (--foo[=BAR]...) +""" + +$ prog +{} + +$ prog --foo +{"--foo": [true]} + +r""" +Usage: manage.py --foo (--foo[=BAR]... (--foo[=BAR]...)) (--foo[=BAR]...) +""" + +$ prog +{} + +$ prog --foo +{"--foo": [true]} + $ prog --foo bar "user-error" # "bar" will be considered trailing since the first appearance # of '--foo' does not take an argument. XXX: Is this intutive @@ -1597,40 +1572,40 @@ $ prog -c -a -b r"""usage: prog -a [-b -d] -c""" $ prog -a -b -c -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -abc -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -a -c -b -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -acb -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -b -a -c -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -bac -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -b -c -a -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -bca -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -c -b -a -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -cba -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -c -a -b -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -cab -{"-a": true, "-b": true, "-c": true, "-d": false} +{"-a": true, "-b": true, "-c": true} $ prog -d -a -b -c "user-error" # -b and -d must appear together @@ -1660,13 +1635,13 @@ $ prog -ba {"-a": true, "-b": true} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -b -{"-a": false, "-b": true} +{"-b": true} $ prog -{"-a": false, "-b": false} +{} r"""usage: prog [(-a[=BAR] -b)]""" @@ -1677,28 +1652,28 @@ $ prog -b -a FOO {"-a": "FOO", "-b": true} $ prog -ab -{"-a": "b", "-b": false} +{"-a": "b"} $ prog -ba {"-a": true, "-b": true} $ prog -a -{"-a": true, "-b": false} +{"-a": true} $ prog -b {"-b": true} $ prog -{"-b": false} +{} r"""usage: prog -a (--foo|--no-foo) -b""" -$ prog -b -a --foo -{"-a": true, "-b": true, "--foo": true, "--no-foo": false} +# $ prog -b -a --foo +# {"-a": true, "-b": true, "--foo": true} $ prog -b --no-foo -a -{"-a": true, "-b": true, "--foo": false, "--no-foo": true} +{"-a": true, "-b": true, "--no-foo": true} $ prog -b --no-foo -a --foo "user-error" @@ -1860,7 +1835,7 @@ Usage: """ $ prog a b c -{"Y": ["a", "b", "c"], "": ["a", "b", "c"], "X": [], "": []} +{"Y": ["a", "b", "c"], "": ["a", "b", "c"]} r""" Usage: @@ -1868,7 +1843,7 @@ Usage: """ $ prog a b c -{"X": ["a", "b", "c"], "": ["a", "b", "c"], "Y": [], "": []} +{"X": ["a", "b", "c"], "": ["a", "b", "c"]} r""" Usage: @@ -1913,7 +1888,7 @@ Options: -v, --verbose """ $ prog -{"-v": false, "--verbose": false} +{} $ prog -v {"-v": true, "--verbose": true} @@ -1927,7 +1902,7 @@ Options: -v, --verbose... """ $ prog -{"-v": 0, "--verbose": 0} +{} $ prog -v {"-v": 1, "--verbose": 1} @@ -1941,7 +1916,7 @@ Options: -v..., --verbose """ $ prog -{"-v": 0, "--verbose": 0} +{} $ prog -v {"-v": 1, "--verbose": 1} @@ -1955,7 +1930,7 @@ Options: -v..., --verbose... """ $ prog -{"-v": 0, "--verbose": 0} +{} $ prog -v {"-v": 1, "--verbose": 1} @@ -1973,7 +1948,7 @@ Options: -h """ $ prog -{"-h": false} +{} $ prog -h {"-h": true} @@ -1987,7 +1962,7 @@ Options: -h """ $ prog -{"-h": 0} +{} $ prog -h {"-h": 1} @@ -2001,7 +1976,7 @@ Options: --input """ $ prog -{"--input": false} +{} $ prog --input {"--input": true} @@ -2015,7 +1990,7 @@ Options: -i, --input """ $ prog -{"-i": false, "--input": false} +{} $ prog -i {"-i": true, "--input": true} @@ -2125,10 +2100,10 @@ Usage: foo [-oi ARG] """ $ prog/s -{"-o": false} # XXX: Awkward +{} $ prog/s -i 123 -{"-i": 123, "-o": false} +{"-i": 123} $ prog/s -i "user-error" @@ -2138,13 +2113,13 @@ Usage: foo [-oi [ARG]] """ $ prog/s -{"-o": false} # XXX: Awkward +{} $ prog/s -i 123 -{"-i": 123, "-o": false} +{"-i": 123} $ prog/s -i -{"-i": true, "-o": false} +{"-i": true} r""" Usage: foo (-oi ARG) @@ -2154,7 +2129,7 @@ $ prog/s "user-error" $ prog/s -i 123 -{"-i": 123, "-o": false} +{"-i": 123} $ prog/s -i "user-error" @@ -2164,13 +2139,13 @@ Usage: foo [-oi ARG]... """ $ prog/s -{"-o": 0} # XXX: Awkward +{} $ prog/s -i 123 -{"-i": [123], "-o": 0} +{"-i": [123]} $ prog/s -i 123 -i 123 -{"-i": [123, 123], "-o": 0} +{"-i": [123, 123]} $ prog/s -i "user-error" @@ -2180,13 +2155,13 @@ Usage: foo [-oi ARG...] """ $ prog/s -{"-o": 0} # XXX: Awkward +{} $ prog/s -i 123 -{"-i": [123], "-o": 0} +{"-i": [123]} $ prog/s -i 123 -i 123 -{"-i": [123, 123], "-o": 0} +{"-i": [123, 123]} $ prog/s -i "user-error" @@ -2196,10 +2171,10 @@ Usage: foo [-oi ARG...]... """ $ prog/s -{"-o": 0} # XXX: Awkward +{} $ prog/s -i 123 -{"-i": [123], "-o": 0} +{"-i": [123]} $ prog/s -i 123 -o {"-i": [123], "-o": 1} @@ -2208,7 +2183,7 @@ $ prog/s -i 123 -o 456 "user-error" $ prog/s -i 123 -i 123 -{"-i": [123, 123], "-o": 0} +{"-i": [123, 123]} $ prog/s -i "user-error" @@ -2329,7 +2304,7 @@ Usage: foo [-] """ $ prog -{"-": false} +{} $ prog - {"-": true} @@ -2465,7 +2440,7 @@ Options: -f, --foo ARG """ $ prog -{"-b": false} +{} r""" usage: git (([-b | -f[=ARG]])) @@ -2473,7 +2448,7 @@ Options: -f, --foo ARG """ $ prog -f -{"-f": true, "--foo": true, "-b": false} +{"-f": true, "--foo": true} # # Given an option-argument to a flag is a fatal error, no pardon: