예제 #1
0
        internal static IEnumerable <SymbolResult> AllSymbolResults(this SymbolResult symbolResult)
        {
            if (symbolResult == null)
            {
                throw new ArgumentNullException(nameof(symbolResult));
            }

            yield return(symbolResult);

            foreach (var item in symbolResult
                     .Children
                     .FlattenBreadthFirst(o => o.Children))
            {
                yield return(item);
            }
        }
예제 #2
0
        public static T GetValueOrDefault <T>(this SymbolResult symbolResult)
        {
            ArgumentResult result = symbolResult.GetValueAs(typeof(T));

            switch (result)
            {
            case SuccessfulArgumentResult successful:
                return((T)successful.Value);

            case FailedArgumentResult failed:
                throw new InvalidOperationException(failed.ErrorMessage);

            case NoArgumentResult _:
            default:
                return(default);
            }
        }
예제 #3
0
        internal static FailedArgumentConversionArityResult Validate(
            SymbolResult symbolResult,
            IArgument argument,
            int minimumNumberOfValues,
            int maximumNumberOfValues)
        {
            var argumentResult = symbolResult.Children.ResultFor(argument);

            var tokenCount = argumentResult?.Tokens.Count ?? 0;

            if (tokenCount < minimumNumberOfValues)
            {
                if (symbolResult.UseDefaultValueFor(argument))
                {
                    return(null);
                }

                return(new MissingArgumentConversionResult(
                           argument,
                           symbolResult.ValidationMessages.RequiredArgumentMissing(symbolResult)));
            }

            if (tokenCount > maximumNumberOfValues)
            {
                if (maximumNumberOfValues == 1)
                {
                    return(new TooManyArgumentsConversionResult(
                               argument,
                               symbolResult.ValidationMessages.ExpectsOneArgument(symbolResult)));
                }
                else
                {
                    return(new TooManyArgumentsConversionResult(
                               argument,
                               symbolResult.ValidationMessages.ExpectsFewerArguments(
                                   symbolResult.Token,
                                   tokenCount,
                                   maximumNumberOfValues)));
                }
            }

            return(null);
        }
예제 #4
0
        internal static ArgumentConversionResult?Validate(
            SymbolResult symbolResult,
            Argument argument,
            int minimumNumberOfValues,
            int maximumNumberOfValues)
        {
            var argumentResult = symbolResult switch
            {
                ArgumentResult a => a,
                _ => symbolResult.Root !.FindResultFor(argument)
            };

            var tokenCount = argumentResult?.Tokens.Count ?? 0;

            if (tokenCount < minimumNumberOfValues)
            {
                if (symbolResult.UseDefaultValueFor(argument))
                {
                    return(null);
                }

                return(ArgumentConversionResult.Failure(
                           argument,
                           symbolResult.LocalizationResources.RequiredArgumentMissing(symbolResult),
                           ArgumentConversionResultType.FailedMissingArgument));
            }

            if (tokenCount > maximumNumberOfValues)
            {
                if (symbolResult is OptionResult optionResult)
                {
                    if (!optionResult.Option.AllowMultipleArgumentsPerToken)
                    {
                        return(ArgumentConversionResult.Failure(
                                   argument,
                                   symbolResult !.LocalizationResources.ExpectsOneArgument(symbolResult),
                                   ArgumentConversionResultType.FailedTooManyArguments));
                    }
                }
            }

            return(null);
        }
        internal void AddToSymbolMap(SymbolResult result)
        {
            switch (result)
            {
            case ArgumentResult argumentResult:
                _allArgumentResults.Add(argumentResult.Argument, argumentResult);
                break;

            case CommandResult commandResult:
                _allCommandResults.Add(commandResult.Command, commandResult);
                break;

            case OptionResult optionResult:
                _allOptionResults.Add(optionResult.Option, optionResult);
                break;

            default:
                throw new ArgumentException($"Unsupported {nameof(SymbolResult)} type: {result.GetType()}");
            }
        }
예제 #6
0
        internal static FailedArgumentConversionArityResult?Validate(
            SymbolResult symbolResult,
            IArgument argument,
            int minimumNumberOfValues,
            int maximumNumberOfValues
            )
        {
            var argumentResult = symbolResult switch
            {
                ArgumentResult a => a,
                _ => symbolResult.Root !.FindResultFor(argument)
            };

            var tokenCount = argumentResult?.Tokens.Count ?? 0;

            if (tokenCount < minimumNumberOfValues)
            {
                if (symbolResult !.UseDefaultValueFor(argument))
                {
                    return(null);
                }

                return(new MissingArgumentConversionResult(
                           argument,
                           symbolResult.ValidationMessages.RequiredArgumentMissing(symbolResult)
                           ));
            }

            if (tokenCount > maximumNumberOfValues)
            {
                return(new TooManyArgumentsConversionResult(
                           argument,
                           symbolResult !.ValidationMessages.ExpectsOneArgument(symbolResult)
                           ));
            }

            return(null);
        }
예제 #7
0
        public bool TryGetValueForOption(IValueDescriptor valueDescriptor, out object value)
        {
            var children = Children
                           .Where(o => valueDescriptor.ValueName.IsMatch(o.Symbol))
                           .ToArray();

            SymbolResult symbolResult = null;

            if (children.Length > 1)
            {
                throw new ArgumentException($"Ambiguous match while trying to bind parameter {valueDescriptor.ValueName} among: {string.Join(",", children.Select(o => o.Name))}");
            }

            if (children.Length == 1)
            {
                symbolResult = children[0];
            }

            if (symbolResult is OptionResult optionResult &&
                optionResult.GetValueAs(valueDescriptor.Type) is SuccessfulArgumentResult successful)
            {
                value = successful.Value;
                return(true);
            }
예제 #8
0
        internal static FailedArgumentArityResult Validate(
            SymbolResult symbolResult,
            int minimumNumberOfArguments,
            int maximumNumberOfArguments)
        {
            if (symbolResult.Arguments.Count < minimumNumberOfArguments)
            {
                return(new FailedArgumentArityResult(symbolResult.ValidationMessages.RequiredArgumentMissing(symbolResult)));
            }

            if (symbolResult.Arguments.Count > maximumNumberOfArguments)
            {
                if (maximumNumberOfArguments == 1)
                {
                    return(new FailedArgumentArityResult(symbolResult.ValidationMessages.ExpectsOneArgument(symbolResult)));
                }
                else
                {
                    return(new FailedArgumentArityResult(symbolResult.ValidationMessages.ExpectsFewerArguments(symbolResult, maximumNumberOfArguments)));
                }
            }

            return(null);
        }
 public virtual string ExpectsOneArgument(SymbolResult symbolResult) =>
 symbolResult is CommandResult
         ? $"Command '{symbolResult.Token.Value}' expects a single argument but {symbolResult.Tokens.Count} were provided."
         : $"Option '{symbolResult.Token.Value}' expects a single argument but {symbolResult.Tokens.Count} were provided.";
예제 #10
0
 public virtual string ExpectsFewerArguments(SymbolResult symbolResult, int maximumNumberOfArguments) =>
 symbolResult is CommandResult
         ? $"Command '{symbolResult.Token}' expects no more than {maximumNumberOfArguments} arguments, but {symbolResult.Arguments.Count} were provided."
         : $"Option '{symbolResult.Token}' expects no more than {maximumNumberOfArguments} arguments, but {symbolResult.Arguments.Count} were provided.";
        private static void Diagram(
            this StringBuilder builder,
            SymbolResult symbolResult,
            ParseResult parseResult)
        {
            if (parseResult.Errors.Any(e => e.SymbolResult == symbolResult))
            {
                builder.Append("!");
            }

            if (symbolResult is OptionResult option &&
                option.IsImplicit)
            {
                builder.Append("*");
            }

            builder.Append("[ ");

            builder.Append(symbolResult.Token.Value);

            foreach (var child in symbolResult.Children)
            {
                builder.Append(" ");
                builder.Diagram(child, parseResult);
            }

            if (symbolResult.Tokens.Count > 0)
            {
                foreach (var arg in symbolResult.Tokens)
                {
                    builder.Append(" <");
                    builder.Append(arg.Value);
                    builder.Append(">");
                }
            }
            else
            {
                foreach (var result in symbolResult.ArgumentResults)
                {
                    if (result is SuccessfulArgumentResult successfulArgumentResult)
                    {
                        var value = successfulArgumentResult.Value;

                        switch (value)
                        {
                        case null:
                        case IReadOnlyCollection <string> a when a.Count == 0:
                            break;

                        default:
                            builder.Append(" <");
                            builder.Append(value);
                            builder.Append(">");
                            break;
                        }
                    }
                }
            }

            builder.Append(" ]");
        }
 public static T GetValueOrDefault <T>(this SymbolResult symbolResult)
 {
     if (symbolResult == null)
     {
         return(default);
예제 #13
0
        public virtual ParseResult Parse(
            IReadOnlyCollection <string> arguments,
            string rawInput = null)
        {
            var           rawTokens        = arguments; // allow a more user-friendly name for callers of Parse
            var           lexResult        = NormalizeRootCommand(rawTokens).Lex(Configuration);
            var           directives       = new List <string>();
            var           unparsedTokens   = new Queue <Token>(lexResult.Tokens);
            var           allSymbolResults = new List <SymbolResult>();
            var           errors           = new List <ParseError>(lexResult.Errors);
            var           unmatchedTokens  = new List <Token>();
            CommandResult rootCommand      = null;
            CommandResult innermostCommand = null;

            IList <IOption> optionQueue = GatherOptions(Configuration.Symbols);

            while (unparsedTokens.Any())
            {
                var token = unparsedTokens.Dequeue();

                if (token.Type == TokenType.EndOfArguments)
                {
                    // stop parsing further tokens
                    break;
                }

                if (token.Type != TokenType.Argument)
                {
                    var symbol =
                        Configuration.Symbols
                        .SingleOrDefault(o => o.HasAlias(token.Value));

                    if (symbol != null)
                    {
                        var result = allSymbolResults
                                     .LastOrDefault(o => o.HasAlias(token.Value));

                        if (result == null)
                        {
                            result = SymbolResult.Create(symbol, token.Value, validationMessages: Configuration.ValidationMessages);

                            rootCommand = (CommandResult)result;
                        }

                        allSymbolResults.Add(result);

                        continue;
                    }
                }

                if (token.Type == TokenType.Directive)
                {
                    var withoutBrackets = token.Value.Substring(1, token.Value.Length - 2);
                    directives.Add(withoutBrackets);
                    continue;
                }

                var added = false;

                foreach (var topLevelSymbol in Enumerable.Reverse(allSymbolResults))
                {
                    var symbolForToken = topLevelSymbol.TryTakeToken(token);

                    if (symbolForToken != null)
                    {
                        allSymbolResults.Add(symbolForToken);
                        added = true;

                        if (symbolForToken is CommandResult command)
                        {
                            ProcessImplicitTokens();
                            innermostCommand = command;
                        }

                        if (token.Type == TokenType.Option)
                        {
                            var existing = optionQueue.FirstOrDefault(option => option.Name == symbolForToken.Name);

                            if (existing != null)
                            {
                                // we've used this option - don't use it again
                                optionQueue.Remove(existing);
                            }
                        }
                        break;
                    }

                    if (token.Type == TokenType.Argument &&
                        topLevelSymbol.Symbol is ICommand)
                    {
                        break;
                    }
                }

                if (!added)
                {
                    unmatchedTokens.Add(token);
                }
            }

            ProcessImplicitTokens();

            if (Configuration.RootCommand.TreatUnmatchedTokensAsErrors)
            {
                errors.AddRange(
                    unmatchedTokens.Select(token => new ParseError(Configuration.ValidationMessages.UnrecognizedCommandOrArgument(token.Value))));
            }

            return(new ParseResult(
                       rootCommand,
                       innermostCommand ?? rootCommand,
                       directives,
                       rawTokens,
                       unparsedTokens.Select(t => t.Value).ToArray(),
                       unmatchedTokens.Select(t => t.Value).ToArray(),
                       errors,
                       rawInput));

            void ProcessImplicitTokens()
            {
                if (!Configuration.EnablePositionalOptions)
                {
                    return;
                }

                var currentCommand = innermostCommand ?? rootCommand;

                if (currentCommand == null)
                {
                    return;
                }

                Token[] tokensToAttemptByPosition =
                    Enumerable.Reverse(unmatchedTokens)
                    .TakeWhile(x => x.Type != TokenType.Command)
                    .Reverse()
                    .ToArray();

                foreach (Token token in tokensToAttemptByPosition)
                {
                    var option = optionQueue.FirstOrDefault();
                    if (option != null)
                    {
                        var newToken       = new Token(option.RawAliases.First(), TokenType.Option);
                        var optionResult   = currentCommand.TryTakeToken(newToken);
                        var optionArgument = optionResult?.TryTakeToken(token);
                        if (optionArgument != null)
                        {
                            optionQueue.RemoveAt(0);
                            allSymbolResults.Add(optionResult);
                            if (optionResult != optionArgument)
                            {
                                allSymbolResults.Add(optionArgument);
                            }
                            unmatchedTokens.Remove(token);
                        }
                        else if (optionResult != null)
                        {
                            currentCommand.Children.Remove(optionResult);
                        }
                    }
                }
            }
        }
예제 #14
0
 public virtual string NoArgumentsAllowed(SymbolResult symbolResult) =>
 $"Arguments not allowed for option: {symbolResult.Token}";
예제 #15
0
 public virtual string NoArgumentProvided(SymbolResult symbolResult) =>
 symbolResult is CommandResult
         ? $"No argument was provided for Command '{symbolResult.Token.Value}'."
         : $"No argument was provided for Option '{symbolResult.Token.Value}'.";
예제 #16
0
 public virtual string RequiredArgumentMissing(SymbolResult symbolResult) =>
 symbolResult is CommandResult
         ? $"Required argument missing for command: {symbolResult.Token.Value}"
         : $"Required argument missing for option: {symbolResult.Token.Value}";
예제 #17
0
 public virtual string ExpectsOneArgument(SymbolResult symbolResult) =>
 symbolResult is CommandResult
             ? GetResourceString(Properties.Resources.CommandExpectsOneArgument, symbolResult.Token().Value, symbolResult.Tokens.Count)
             : GetResourceString(Properties.Resources.OptionExpectsOneArgument, symbolResult.Token().Value, symbolResult.Tokens.Count);
 public static object GetValueOrDefault(this SymbolResult symbolResult)
 {
     return(symbolResult.GetValueOrDefault <object>());
 }
예제 #19
0
 public virtual string NoArgumentProvided(SymbolResult symbolResult) =>
 symbolResult is CommandResult
         ? GetResourceString(Properties.Resources.CommandNoArgumentProvided, symbolResult.Token().Value)
         : GetResourceString(Properties.Resources.OptionNoArgumentProvided, symbolResult.Token().Value);
        private static void Diagram(
            this StringBuilder builder,
            SymbolResult symbolResult,
            ParseResult parseResult)
        {
            if (parseResult.Errors.Any(e => e.SymbolResult == symbolResult))
            {
                builder.Append("!");
            }

            if (symbolResult is OptionResult optionResult &&
                optionResult.IsImplicit)
            {
                builder.Append("*");
            }

            if (symbolResult is ArgumentResult argumentResult)
            {
                var includeArgumentName =
                    argumentResult.Argument is Argument argument &&
                    argument.Parents[0] is ICommand command &&
                    command.Name != argument.Name;

                if (includeArgumentName)
                {
                    builder.Append("[ ");
                    builder.Append(argumentResult.Argument.Name);
                    builder.Append(" ");
                }

                switch (symbolResult.ArgumentConversionResult)
                {
                case SuccessfulArgumentConversionResult successful:

                    switch (successful.Value)
                    {
                    case null:
                    case IReadOnlyCollection <string> a when a.Count == 0:
                        break;

                    case IEnumerable <string> args:
                        builder.Append("<");
                        builder.Append(string.Join("> <", args));
                        builder.Append(">");
                        break;

                    default:
                        builder.Append("<");
                        builder.Append(successful.Value);
                        builder.Append(">");
                        break;
                    }

                    break;

                case FailedArgumentConversionResult _:

                    builder.Append("<");
                    builder.Append(string.Join("> <", symbolResult.Tokens.Select(t => t.Value)));
                    builder.Append(">");

                    break;
                }

                if (includeArgumentName)
                {
                    builder.Append(" ]");
                }
            }
            else
            {
                builder.Append("[ ");
                builder.Append(symbolResult.Token.Value);

                foreach (var child in symbolResult.Children)
                {
                    builder.Append(" ");
                    builder.Diagram(child, parseResult);
                }

                builder.Append(" ]");
            }
        }
예제 #21
0
 public virtual string RequiredArgumentMissing(SymbolResult symbolResult) =>
 symbolResult is CommandResult
         ? GetResourceString(Properties.Resources.CommandRequiredArgumentMissing, symbolResult.Token().Value)
         : GetResourceString(Properties.Resources.OptionRequiredArgumentMissing, symbolResult.Token().Value);