| File: Parsing\CommandLineParser.cs | Web Access |
| Project: src\command-line-api\src\System.CommandLine\System.CommandLine.csproj (System.CommandLine) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.Linq; namespace System.CommandLine.Parsing { /// <summary> /// Parses command line input. /// </summary> public static class CommandLineParser { /// <summary> /// Parses a list of arguments. /// </summary> /// <param name="command">The command to use to parse the command line input.</param> /// <param name="args">The string array typically passed to a program's <c>Main</c> method.</param> /// <param name="configuration">The configuration on which the parser's grammar and behaviors are based.</param> /// <returns>A <see cref="ParseResult"/> providing details about the parse operation.</returns> public static ParseResult Parse(Command command, IReadOnlyList<string> args, ParserConfiguration? configuration = null) => Parse(command, args, null, configuration); /// <summary> /// Parses a command line string. /// </summary> /// <param name="command">The command to use to parse the command line input.</param> /// <param name="commandLine">The complete command line input prior to splitting and tokenization. This input is not typically available when the parser is called from <c>Program.Main</c>. It is primarily used when calculating completions via the <c>dotnet-suggest</c> tool.</param> /// <param name="configuration">The configuration on which the parser's grammar and behaviors are based.</param> /// <remarks>The command line string input will be split into tokens as if it had been passed on the command line.</remarks> /// <returns>A <see cref="ParseResult"/> providing details about the parse operation.</returns> public static ParseResult Parse(Command command, string commandLine, ParserConfiguration? configuration = null) => Parse(command, SplitCommandLine(commandLine).ToArray(), commandLine, configuration); /// <summary> /// Splits a string into a sequence of strings based on whitespace and quotation marks. /// </summary> /// <param name="commandLine">A command line input string.</param> /// <returns>A sequence of strings.</returns> public static IEnumerable<string> SplitCommandLine(string commandLine) { var memory = commandLine.AsMemory(); var startTokenIndex = 0; var pos = 0; var seeking = Boundary.TokenStart; var seekingQuote = Boundary.QuoteStart; while (pos < memory.Length) { var c = memory.Span[pos]; if (char.IsWhiteSpace(c)) { if (seekingQuote == Boundary.QuoteStart) { switch (seeking) { case Boundary.WordEnd: yield return CurrentToken(); startTokenIndex = pos; seeking = Boundary.TokenStart; break; case Boundary.TokenStart: startTokenIndex = pos; break; } } } else if (c == '\"') { if (seeking == Boundary.TokenStart) { switch (seekingQuote) { case Boundary.QuoteEnd: yield return CurrentToken(); startTokenIndex = pos; seekingQuote = Boundary.QuoteStart; break; case Boundary.QuoteStart: startTokenIndex = pos + 1; seekingQuote = Boundary.QuoteEnd; break; } } else { switch (seekingQuote) { case Boundary.QuoteEnd: seekingQuote = Boundary.QuoteStart; break; case Boundary.QuoteStart: seekingQuote = Boundary.QuoteEnd; break; } } } else if (seeking == Boundary.TokenStart && seekingQuote == Boundary.QuoteStart) { seeking = Boundary.WordEnd; startTokenIndex = pos; } Advance(); if (IsAtEndOfInput()) { switch (seeking) { case Boundary.TokenStart: break; default: yield return CurrentToken(); break; } } } void Advance() => pos++; string CurrentToken() { return memory.Slice(startTokenIndex, IndexOfEndOfToken()).ToString().Replace("\"", ""); } int IndexOfEndOfToken() => pos - startTokenIndex; bool IsAtEndOfInput() => pos == memory.Length; } private static ParseResult Parse( Command command, IReadOnlyList<string> arguments, string? rawInput, ParserConfiguration? configuration) { if (arguments is null) { throw new ArgumentNullException(nameof(arguments)); } configuration ??= new ParserConfiguration(); arguments.Tokenize( command, configuration, inferRootCommand: rawInput is not null, out List<Token> tokens, out List<string>? tokenizationErrors); var operation = new ParseOperation( tokens, command, configuration, tokenizationErrors, rawInput); return operation.Parse(); } private enum Boundary { TokenStart, WordEnd, QuoteStart, QuoteEnd } } }