Exemplo n.º 1
0
        /// <summary>
        ///     This method is used by the code that validates the command-line grouping. It is
        ///     called for every iteration of the arguments.
        /// </summary>
        /// <param name="previousType">The type of the previously-checked argument.</param>
        /// <param name="currentType">The type of the currently-checked argument.</param>
        /// <param name="grouping">The expected arg grouping.</param>
        private static void VerifyCommandLineGrouping(ArgumentType previousType, ArgumentType currentType,
                                                      ArgGrouping grouping)
        {
            if (grouping == ArgGrouping.DoesNotMatter)
            {
                return;
            }

            if (previousType == ArgumentType.NotSet || currentType == ArgumentType.NotSet)
            {
                return;
            }

            if (grouping == ArgGrouping.OptionsAfterArguments && previousType == ArgumentType.Option &&
                currentType == ArgumentType.Argument)
            {
                throw new ParserException(ParserException.Codes.OptionsAfterParameters,
                                          Messages.OptionsAfterParameters);
            }

            if (grouping == ArgGrouping.OptionsBeforeArguments && previousType == ArgumentType.Argument &&
                currentType == ArgumentType.Option)
            {
                throw new ParserException(ParserException.Codes.OptionsBeforeParameters,
                                          Messages.OptionsBeforeParameters);
            }
        }
Exemplo n.º 2
0
        /// <inheritdoc/>
        public override ArgGrouping GetGrouping(ArgGrouping specifiedGrouping, IReadOnlyList <Option> options,
                                                IReadOnlyList <Argument> arguments)
        {
            // If any option has variable number of parameters (i.e. ExpectedParameters = null),
            // then the groupings is changed to DoesNotMatter.
            if (specifiedGrouping == ArgGrouping.OptionsBeforeArguments)
            {
                bool optionsHaveVariableParameters = options.Any(option => !option.Usage.ExpectedParameters.HasValue);
                if (optionsHaveVariableParameters)
                {
                    specifiedGrouping = ArgGrouping.DoesNotMatter;
                }
            }

            // If any option has unlimited parameters, then options must appear after arguments.
            bool optionsHaveUnlimitedParameters =
                options.Any(option => option.Usage.MaxParameters == OptionUsage.Unlimited);

            if (specifiedGrouping != ArgGrouping.OptionsAfterArguments && optionsHaveUnlimitedParameters)
            {
                specifiedGrouping = ArgGrouping.OptionsAfterArguments;
            }

            return(specifiedGrouping);
        }
Exemplo n.º 3
0
        public Parser(Command command, ArgStyle argStyle, ArgGrouping grouping = ArgGrouping.DoesNotMatter)
        {
            if (command is null)
            {
                throw new ArgumentNullException(nameof(command));
            }
            if (argStyle is null)
            {
                throw new ArgumentNullException(nameof(argStyle));
            }

            Command  = command;
            ArgStyle = argStyle;
            Grouping = grouping;
        }
Exemplo n.º 4
0
 /// <summary>
 ///     Identifies all provided tokens as arguments, options and option parameters.
 ///     <para/>
 ///     Option and argument validators are not checked in this phase. Only the arg grouping
 ///     is checked.
 /// </summary>
 /// <param name="tokens">All the specified tokens.</param>
 /// <param name="options">
 ///     All available options. If any of the tokens match, add its details to this parameter.
 /// </param>
 /// <param name="grouping">The expected arg grouping to validate.</param>
 /// <returns>A collection of all the identified arguments.</returns>
 public abstract IEnumerable <string> IdentifyTokens(IEnumerable <string> tokens, IReadOnlyList <OptionRun> options,
                                                     ArgGrouping grouping);
Exemplo n.º 5
0
 /// <summary>
 ///     Allows the parser style to override the preferred grouping based on its rules for the
 ///     specified options and arguments.
 /// </summary>
 /// <param name="specifiedGrouping">The preferred grouping.</param>
 /// <param name="options">The list of allowed options.</param>
 /// <param name="arguments">The list of allowed arguments.</param>
 /// <returns>The final grouping for the specified options and arguments.</returns>
 public virtual ArgGrouping GetGrouping(ArgGrouping specifiedGrouping, IReadOnlyList <Option> options,
                                        IReadOnlyList <Argument> arguments)
 {
     return(specifiedGrouping);
 }
Exemplo n.º 6
0
 /// <summary>
 ///     Initializes a new instance of the <see cref="ConsoleProgram"/> class with the specified
 ///     name, arg style and grouping.
 /// </summary>
 /// <param name="name">The name of the program.</param>
 /// <param name="argStyle">The expected argument style.</param>
 /// <param name="grouping">The expected arg grouping.</param>
 public ConsoleProgram(string name, ArgStyle argStyle, ArgGrouping grouping = ArgGrouping.DoesNotMatter)
     : base(caseSensitive: false, name)
 {
     _argStyle = CreateArgStyle(argStyle);
     Grouping  = grouping;
 }
Exemplo n.º 7
0
        /// <inheritdoc/>
        public override IEnumerable <string> IdentifyTokens(IEnumerable <string> tokens, IReadOnlyList <OptionRun> options,
                                                            ArgGrouping grouping)
        {
            ArgumentType previousType = ArgumentType.NotSet;
            ArgumentType currentType  = ArgumentType.NotSet;

            foreach (string token in tokens)
            {
                VerifyCommandLineGrouping(previousType, currentType, grouping);

                previousType = currentType;

                var optionMatch = OptionPattern.Match(token);
                if (!optionMatch.Success)
                {
                    currentType = ArgumentType.Argument;
                    yield return(token);
                }
                else
                {
                    currentType = ArgumentType.Option;

                    string specifiedOptionName = optionMatch.Groups[groupnum : 1].Value;

                    // Find the corresponding option run for the specified option name.
                    // If not found, throw a parse exception.
                    OptionRun availableOption = options.SingleOrDefault(or => or.Option.HasName(specifiedOptionName));
                    if (availableOption is null)
                    {
                        throw new ParserException(ParserException.Codes.InvalidOptionSpecified,
                                                  string.Format(Messages.InvalidOptionSpecified, specifiedOptionName));
                    }

                    // Increase the number of occurrences of the option run.
                    availableOption.Occurrences += 1;

                    // If no option parameters are specified, we're done with this option.
                    // Continue the loop to process the next token.
                    if (token.Length == specifiedOptionName.Length + 1)
                    {
                        continue;
                    }

                    // Option name and its parameters must be separated by a colon.
                    // If not, throw a parse exception.
                    if (token[specifiedOptionName.Length + 1] != ':')
                    {
                        throw new ParserException(ParserException.Codes.InvalidOptionParameterSpecifier,
                                                  string.Format(Messages.InvalidOptionParameterSpecifier, specifiedOptionName));
                    }

                    // Match any parameters and add to the option run's Parameters property.
                    var parameterMatches = OptionParameterPattern.Matches(token, optionMatch.Length + 1);
                    foreach (Match parameterMatch in parameterMatches)
                    {
                        string value = parameterMatch.Groups[groupnum : 1].Value;
                        if (value.StartsWith(",", StringComparison.OrdinalIgnoreCase))
                        {
                            value = value.Remove(startIndex: 0, count: 1);
                        }
                        availableOption.Parameters.Add(value);
                    }
                }
            }

            VerifyCommandLineGrouping(previousType, currentType, grouping);
        }
Exemplo n.º 8
0
        /// <summary>
        ///     Parses the given set of tokens based on the rules specified by the <see cref="Command"/>.
        /// </summary>
        /// <param name="tokens">Token strings to parse.</param>
        /// <returns>A <see cref="ParseResult" /> instance.</returns>
        public ParseResult Parse(IEnumerable <string> tokens)
        {
            DebugOutput.Write("Tokens passed", tokens);

            IReadOnlyList <string> tokenList = tokens.ToList();

            // Creates a ParseRun instance, which specifies the sequence of commands specified and
            // the tokens and any options and arguments that apply to the specified commands.
            ParseRun run = CreateRun(tokenList);

            // Extract out just the option and argument objects from their respective run collections.
            // We want to pass these to parser style methods that only need to deal with the Option
            // and Argument objects and not have to deal with the 'run' aspects. See some of the calls
            // to protected methods from this method.
            IReadOnlyList <Option>   justOptions   = run.Options.Select(o => o.Option).ToList();
            IReadOnlyList <Argument> justArguments = run.Arguments.Select(a => a.Argument).ToList();

            // Even though the caller can define the grouping, the parser style can override it based
            // on the available options and arguments. See the UnixArgStyle class for an example.
            Grouping = ArgStyle.GetGrouping(Grouping, justOptions, justArguments);

            // Validate all the available options based on the parser style rules.
            // See the UnixArgStyle for an example.
            ArgStyle.ValidateDefinedOptions(justOptions);

            // Identify all tokens as options or arguments. Identified option details are stored in
            // the Option instance itself. Identified arguments are returned from the method.
            List <string> specifiedArguments =
                ArgStyle.IdentifyTokens(run.Tokens, run.Options, Grouping).ToList();

            // Get the groups that match the specified options and arguments.
            IReadOnlyList <int> matchingGroups = GetMatchingGroups(run, specifiedArguments);

            // If no groups match, we cannot proceed, so throw a parser exception.
            if (matchingGroups.Count == 0)
            {
                throw new ParserException(-1, "The specified arguments and options are invalid.");
            }

            // Trim the argument and option runs to those that contain the matcing groups.
            TrimRunsToMatchingGroups(run, matchingGroups);

            //TODO: Come back to this later
            //if (ConfigReader != null)
            //    specifiedArguments = ConfigReader.Run(specifiedArguments, Options);

            // Process the specified options and arguments, and resolve their values.
            ProcessOptions(run.Options);
            ProcessArguments(specifiedArguments, run.Arguments);

            var parseResult = new ParseResult(run);

            // Runs the custom validator, if it is assigned.
            CommandCustomValidator customCommandValidator = parseResult.Command.CustomValidator;
            string validationError = customCommandValidator?.Invoke(parseResult.Arguments, parseResult.Options);

            if (validationError != null)
            {
                throw new ValidationException(validationError, null, null);
            }

            return(parseResult);
        }
Exemplo n.º 9
0
        /// <inheritdoc/>
        public override IEnumerable <string> IdentifyTokens(IEnumerable <string> tokens, IReadOnlyList <OptionRun> options,
                                                            ArgGrouping grouping)
        {
            OptionRun currentOption = null;

            bool forceArguments = false;

            foreach (string token in tokens)
            {
                if (token == "--")
                {
                    forceArguments = true;
                    continue;
                }

                if (forceArguments)
                {
                    yield return(token);

                    continue;
                }

                Match optionMatch = OptionPattern.Match(token);

                // If the token is not an option and we are not iterating over the parameters of an
                // option, then the token is an argument.
                if (!optionMatch.Success)
                {
                    // If we're processing an option (currentOption != null), then it's an argument.
                    // Otherwise, it's a parameter on the current option.
                    if (currentOption is null)
                    {
                        yield return(token);
                    }
                    else
                    {
                        currentOption.Parameters.Add(token);
                    }
                }
                else
                {
                    bool   isShortOption        = optionMatch.Groups[1].Value.Length == 1;
                    string optionName           = optionMatch.Groups[2].Value;
                    string parameterValue       = optionMatch.Groups[3].Value;
                    bool   isParameterSpecified = !string.IsNullOrEmpty(parameterValue);

                    // If multiple short options are specified as a single combined option, then none
                    // of them can have parameters.
                    if (isShortOption && optionName.Length > 1 && isParameterSpecified)
                    {
                        throw new ParserException(-1, $"Cannot specify a parameter for the combined option {optionName}.");
                    }

                    // Get the specified option names.
                    // If a short option name is specified and it has multiple characters, each
                    // character is a short name.
                    string[] optionNames = isShortOption && optionName.Length > 1
                        ? optionName.Select(c => c.ToString()).ToArray()
                        : new[] { optionName };

                    // Add each option to its corresponding option run.
                    foreach (string name in optionNames)
                    {
                        OptionRun matchingOption = options.SingleOrDefault(or => or.Option.HasName(name));
                        if (matchingOption is null)
                        {
                            throw new ParserException(ParserException.Codes.InvalidOptionSpecified,
                                                      string.Format(Messages.InvalidOptionSpecified, optionName));
                        }

                        // Increase the number of occurrences of the option
                        matchingOption.Occurrences += 1;
                    }

                    // If only one option was specified (and not a combined short option set), we deal
                    // with the parameters now.
                    if (optionNames.Length == 1)
                    {
                        // We know its option run exists, so use Single here.
                        OptionRun matchingOption = options.Single(or => or.Option.HasName(optionNames[0]));

                        // If the parameter was specified in the same token with a "=" symbol, then
                        // that is the only parameter we can allow.
                        // Add it and proceed with the next token.
                        if (isParameterSpecified)
                        {
                            matchingOption.Parameters.Add(parameterValue);
                            currentOption = null;
                        }

                        // Otherwise, mark the current option as this one and process all subsequent
                        // tokens as parameters until we encounter another option or we've reached the
                        // max limit of parameters for this option.
                        else
                        {
                            currentOption = matchingOption;
                        }
                    }
                    else
                    {
                        currentOption = null;
                    }
                }

                // If we're on an option (currentOption != null) and the number of parameters has
                // reached the maximum allowed, then we can stop handling that option by setting
                // currentOption to null so that the next arg will be treated as a new option or
                // argument.
                if (currentOption != null && currentOption.Parameters.Count >= currentOption.Option.Usage.MaxParameters)
                {
                    currentOption = null;
                }
            }
        }
Exemplo n.º 10
0
 public Parser(ParserStyle parserStyle, ArgGrouping grouping = ArgGrouping.DoesNotMatter)
 {
     ParserStyle = parserStyle;
     Grouping = grouping;
 }