Example #1
0
        private void ReportUnrecognizedArgument(ArgumentSetParseResult result, string argument)
        {
            switch (result.State)
            {
            case ArgumentSetParseResultType.UnknownNamedArgument:
                ReportLine(Strings.UnrecognizedArgument, argument);

                if (!string.IsNullOrEmpty(result.NamedArg))
                {
                    var possibleArgs = GetSimilarNamedArguments(result.NamedArgType, result.NamedArg).ToList();
                    if (possibleArgs.Any())
                    {
                        ReportLine(
                            "  " + (possibleArgs.Count == 1 ? Strings.PossibleIntendedNamedArgument : Strings.PossibleIntendedNamedArguments),
                            string.Join(", ", possibleArgs.Select(a => "'" + a + "'")));
                    }
                }

                break;

            case ArgumentSetParseResultType.UnknownPositionalArgument:
                ReportLine(Strings.UnrecognizedArgument, argument);
                break;

            case ArgumentSetParseResultType.RequiresOptionArgument:
                ReportLine(Strings.MissingRequiredOptionArgument, argument);
                break;
            }
        }
Example #2
0
        /// <summary>
        /// Tries to finalize parsing to the given output object.
        /// </summary>
        /// <param name="destination">Output object.</param>
        /// <param name="parseResult">Parse result.</param>
        /// <returns>Parse result.</returns>
        public ArgumentSetParseResult Finalize(object destination, ArgumentSetParseResult parseResult)
        {
            // Finalize all arguments: named args first, then positional default args.
            var result = parseResult;

            foreach (var arg in ArgumentSet.NamedArguments.Concat(ArgumentSet.PositionalArguments))
            {
                var argState = GetStateForArgument(arg, destination);
                if (!argState.TryFinalize(_options.FileSystemReader))
                {
                    result = ArgumentSetParseResult.FailedFinalizing;
                }
            }

            return(result);
        }
Example #3
0
        /// <summary>
        /// Parses an argument list into an object.
        /// </summary>
        /// <param name="args">String arguments to parse.</param>
        /// <param name="destination">Output arguments object.</param>
        /// <returns>Parse result.</returns>
        public ArgumentSetParseResult ParseTokens(IEnumerable <string> args, object destination)
        {
            var result = ArgumentSetParseResult.Ready(null);
            IReadOnlyList <string> argsList = args.ToList();

            for (var index = 0; index < argsList.Count;)
            {
                var currentResult = TryParseNextToken(argsList, index, destination, out int argsConsumed);
                if (!currentResult.IsReady)
                {
                    result = currentResult;
                }
                else if (result.IsReady)
                {
                    result = currentResult;
                }

                index += argsConsumed;
            }

            return(result);
        }
Example #4
0
        private ArgumentSetParseResult TryParseNextPositionalArgument(IReadOnlyList <string> args, int index, object destination, out int argsConsumed)
        {
            var argument = args[index];

            argsConsumed = 1;

            if (!ArgumentSet.TryGetPositionalArgument(NextPositionalArgIndexToParse, out ArgumentDefinition positionalArg))
            {
                var result = ArgumentSetParseResult.UnknownPositionalArgument;
                ReportUnrecognizedArgument(result, argument);

                return(result);
            }

            if (!positionalArg.AllowMultiple)
            {
                NextPositionalArgIndexToParse += 1;
            }

            var argState = GetStateForArgument(positionalArg, destination);

            if (positionalArg.TakesRestOfLine)
            {
                if (!argState.TrySetRestOfLine(args.Skip(index)))
                {
                    return(ArgumentSetParseResult.FailedParsing);
                }

                argsConsumed = args.Count - index; // skip the rest of the line
                return(ArgumentSetParseResult.Ready(positionalArg));
            }
            else
            {
                Debug.Assert(argument != null);
                return(TryParseAndStore(argState, argument)
                    ? ArgumentSetParseResult.Ready(positionalArg) : ArgumentSetParseResult.FailedParsing);
            }
        }
Example #5
0
        private ArgumentSetParseResult TryParseNamedArgument(
            string argument,
            string argumentPrefix,
            ArgumentNameType namedArgType,
            out IReadOnlyList <ArgumentAndValue> parsedArgs)
        {
            var prefixLength = argumentPrefix.Length;

            Debug.Assert(argument.Length >= prefixLength);

            // Figure out where the argument name ends.
            var endIndex = argument.IndexOfAny(ArgumentSet.Attribute.ArgumentValueSeparators, prefixLength);

            // Special case: check for '+' and '-' for booleans.
            if (endIndex < 0 && argument.Length > argumentPrefix.Length)
            {
                var lastArgumentChar = argument[argument.Length - 1];
                if (ArgumentNameTerminators.Any(t => lastArgumentChar.Equals(t)))
                {
                    endIndex = argument.Length - 1;
                }
            }

            // If we don't have a separator or terminator, then consume the full string.
            if (endIndex < 0)
            {
                endIndex = argument.Length;
            }

            // Extract the argument name(s), separate from the prefix
            // or optional argument value.
            var options = argument.Substring(prefixLength, endIndex - prefixLength);

            // Extract the option argument (a.k.a. value), if there is one.
            string optionArgument = null;

            if (argument.Length > prefixLength + options.Length)
            {
                // If there's an argument value separator, then extract the value after the separator.
                if (ArgumentSet.Attribute.ArgumentValueSeparators.Any(sep => argument[prefixLength + options.Length] == sep))
                {
                    optionArgument = argument.Substring(prefixLength + options.Length + 1);
                }

                // Otherwise, it might be a terminator; extract the rest of the string.
                else
                {
                    optionArgument = argument.Substring(prefixLength + options.Length);
                }
            }

            // Now try to figure out how many names are present.
            if (namedArgType == ArgumentNameType.ShortName &&
                (ArgumentSet.Attribute.AllowMultipleShortNamesInOneToken || ArgumentSet.Attribute.AllowElidingSeparatorAfterShortName))
            {
                Debug.Assert(ArgumentSet.Attribute.ShortNamesAreOneCharacterLong);

                // Since short names are one character long, we parse them one at a
                // time, preparing for multiple arguments in this one token.
                var args = new List <ArgumentAndValue>();
                for (var index = 0; index < options.Length; ++index)
                {
                    // Try parsing it as a short name; bail immediately if we find an invalid
                    // one.
                    var possibleShortName = new string(options[index], 1);
                    if (!ArgumentSet.TryGetNamedArgument(ArgumentNameType.ShortName, possibleShortName, out ArgumentDefinition arg))
                    {
                        parsedArgs = null;
                        return(ArgumentSetParseResult.UnknownNamedArgument(namedArgType, possibleShortName));
                    }

                    // If this parsed as a short name that takes a required option argument,
                    // and we didn't see an option argument, and we allow mushing together
                    // short names and their option arguments, then try parsing the rest of
                    // this token as an option argument.
                    var lastChar = index == options.Length - 1;
                    if (arg.RequiresOptionArgumentEx(_options) &&
                        ArgumentSet.Attribute.AllowElidingSeparatorAfterShortName &&
                        optionArgument == null &&
                        !lastChar)
                    {
                        optionArgument = options.Substring(index + 1);
                        index          = options.Length - 1;
                        lastChar       = true;
                    }

                    if (!ArgumentSet.Attribute.AllowMultipleShortNamesInOneToken &&
                        args.Count > 0)
                    {
                        parsedArgs = null;
                        return(ArgumentSetParseResult.UnknownNamedArgument());
                    }

                    args.Add(new ArgumentAndValue
                    {
                        Arg   = arg,
                        Value = lastChar ? optionArgument : null
                    });
                }

                // Special case: if no arguments were found, return an error.
                if (args.Count == 0)
                {
                    parsedArgs = null;
                    return(ArgumentSetParseResult.FailedParsing);
                }

                parsedArgs = args;
            }
            else
            {
                // Try to look up the argument by name.
                if (!ArgumentSet.TryGetNamedArgument(namedArgType, options, out ArgumentDefinition arg))
                {
                    parsedArgs = null;
                    return(ArgumentSetParseResult.UnknownNamedArgument(namedArgType, options));
                }

                parsedArgs = new[]
                {
                    new ArgumentAndValue
                    {
                        Arg   = arg,
                        Value = optionArgument
                    }
                };
            }

            var lastArg = parsedArgs.GetLastOrDefault();
            var lastArgTakesRestOfLine = lastArg?.Arg.TakesRestOfLine ?? false;

            // If the last named argument we saw in this token required an
            // option argument to go with it, then yield that information
            // so it can be used by the caller (e.g. in completion generation).
            if (lastArg != null &&
                lastArg.Arg.RequiresOptionArgumentEx(_options) &&
                string.IsNullOrEmpty(lastArg.Value))
            {
                return(ArgumentSetParseResult.RequiresOptionArgument(lastArg.Arg));
            }

            return(ArgumentSetParseResult.Ready(lastArg?.Arg));
        }
Example #6
0
        private ArgumentSetParseResult TryParseNextNamedArgument(IReadOnlyList <string> args, int index, string longNameArgumentPrefix, string shortNameArgumentPrefix, object destination, out int argsConsumed)
        {
            argsConsumed = 1;

            var argument = args[index];
            var result   = ArgumentSetParseResult.UnknownNamedArgument();

            IReadOnlyList <ArgumentAndValue> parsedArgs = null;

            if (result.IsUnknown && longNameArgumentPrefix != null)
            {
                result = TryParseNamedArgument(argument, longNameArgumentPrefix, ArgumentNameType.LongName, out parsedArgs);
            }

            if (result.IsUnknown && shortNameArgumentPrefix != null)
            {
                result = TryParseNamedArgument(argument, shortNameArgumentPrefix, ArgumentNameType.ShortName, out parsedArgs);
            }

            // If our policy allows a named argument's value to be placed
            // in the following token, and if we're missing a required
            // value, and if there's at least one more token, then try
            // to parse the next token as the current argument's value.
            if (result.State == ArgumentSetParseResultType.RequiresOptionArgument &&
                ArgumentSet.Attribute.AllowNamedArgumentValueAsSucceedingToken &&
                index + 1 < args.Count)
            {
                var lastParsedArg = parsedArgs.GetLast();
                Debug.Assert(lastParsedArg.Arg.RequiresOptionArgumentEx(_options));
                Debug.Assert(string.IsNullOrEmpty(parsedArgs.GetLast().Value));

                ++index;
                ++argsConsumed;

                lastParsedArg.Value = args[index];

                result = ArgumentSetParseResult.Ready(lastParsedArg.Arg);
            }

            if (!result.IsReady)
            {
                ReportUnrecognizedArgument(result, argument);
                return(result);
            }

            Debug.Assert(parsedArgs != null);

            foreach (var parsedArg in parsedArgs)
            {
                // TODO: Obviate the need to use string.Empty here.
                var argValue = parsedArg.Value ?? string.Empty;
                var argState = GetStateForArgument(parsedArg.Arg, destination);
                if (parsedArg.Arg.TakesRestOfLine)
                {
                    if (!argState.TrySetRestOfLine(new[] { argValue }.Concat(args.Skip(index + 1))))
                    {
                        result = ArgumentSetParseResult.FailedParsing;
                        continue;
                    }

                    argsConsumed += args.Count - index; // skip the rest of the line
                }
                else
                {
                    if (!TryParseAndStore(argState, argValue))
                    {
                        result = ArgumentSetParseResult.FailedParsing;
                        continue;
                    }
                }
            }

            return(result);
        }