private static TemplateToken ReadOption(TextBuffer buffer) { var position = buffer.Position; buffer.Consume('-'); if (buffer.IsNext('-')) { buffer.Consume('-'); var longValue = ReadOptionName(buffer); return(new TemplateToken(TemplateToken.Kind.LongName, position, longValue, $"--{longValue}")); } var shortValue = ReadOptionName(buffer); return(new TemplateToken(TemplateToken.Kind.ShortName, position, shortValue, $"-{shortValue}")); }
private static TemplateToken ReadValue(TextBuffer buffer, bool required) { var start = required ? '<' : '['; var end = required ? '>' : ']'; var position = buffer.Position; var kind = required ? TemplateToken.Kind.RequiredValue : TemplateToken.Kind.OptionalValue; // Consume start of value character (< or [). buffer.Consume(start); var builder = new StringBuilder(); while (!buffer.ReachedEnd) { var character = buffer.Peek(); if (character == end) { break; } buffer.Read(); builder.Append(character); } if (buffer.ReachedEnd) { var name = builder.ToString(); var token = new TemplateToken(kind, position, name, $"{start}{name}"); throw CommandTemplateException.UnterminatedValueName(buffer.Original, token); } // Consume end of value character (> or ]). buffer.Consume(end); // Get the value (the text within the brackets). var value = builder.ToString(); // Create a token and return it. return(new TemplateToken(kind, position, value, required ? $"<{value}>" : $"[{value}]")); }
private static IEnumerable <CommandTreeToken> ScanOptions(CommandTreeTokenizerContext context, TextBuffer reader) { var result = new List <CommandTreeToken>(); var position = reader.Position; reader.Consume('-'); context.AddRemaining('-'); if (!reader.TryPeek(out var character)) { var token = new CommandTreeToken(CommandTreeToken.Kind.ShortOption, position, "-", "-"); throw CommandParseException.OptionHasNoName(reader.Original, token); } switch (character) { case '-': var option = ScanLongOption(context, reader, position); if (option != null) { result.Add(option); } break; default: result.AddRange(ScanShortOptions(context, reader, position)); break; } if (reader.TryPeek(out character)) { // Encountered a separator? if (character == '=' || character == ':') { reader.Read(); // Consume context.AddRemaining(character); if (!reader.TryPeek(out _)) { var token = new CommandTreeToken(CommandTreeToken.Kind.String, reader.Position, "=", "="); throw CommandParseException.OptionValueWasExpected(reader.Original, token); } result.Add(ScanString(context, reader)); } } return(result); }
private static CommandTreeToken ScanLongOption(CommandTreeTokenizerContext context, TextBuffer reader, int position) { reader.Consume('-'); context.AddRemaining('-'); if (reader.ReachedEnd) { // Rest of the arguments are remaining ones. context.Mode = Mode.Remaining; return(new CommandTreeToken(CommandTreeToken.Kind.Remaining, position, "--", "--")); } var name = ScanString(context, reader, new[] { '=', ':' }); // Perform validation of the name. if (name.Value.Length == 0) { throw CommandParseException.LongOptionNameIsMissing(reader, position); } if (name.Value.Length == 1) { throw CommandParseException.LongOptionNameIsOneCharacter(reader, position, name.Value); } if (char.IsDigit(name.Value[0])) { throw CommandParseException.LongOptionNameStartWithDigit(reader, position, name.Value); } for (var index = 0; index < name.Value.Length; index++) { if (!char.IsLetterOrDigit(name.Value[index]) && name.Value[index] != '-' && name.Value[index] != '_') { throw CommandParseException.LongOptionNameContainSymbol(reader, position + 2 + index, name.Value[index]); } } return(new CommandTreeToken(CommandTreeToken.Kind.LongOption, position, name.Value, $"--{name.Value}")); }
private static CommandTreeToken ScanQuotedString(CommandTreeTokenizerContext context, TextBuffer reader) { var position = reader.Position; context.FlushRemaining(); reader.Consume('\"'); var builder = new StringBuilder(); var terminated = false; while (!reader.ReachedEnd) { var character = reader.Peek(); if (character == '\"') { terminated = true; reader.Read(); break; } builder.Append(reader.Read()); } if (!terminated) { var unterminatedQuote = builder.ToString(); var token = new CommandTreeToken(CommandTreeToken.Kind.String, position, unterminatedQuote, $"\"{unterminatedQuote}"); throw CommandParseException.UnterminatedQuote(reader.Original, token); } var quotedString = builder.ToString(); // Add to the context context.AddRemaining(quotedString); return(new CommandTreeToken( CommandTreeToken.Kind.String, position, quotedString, quotedString)); }
public static IReadOnlyList <TemplateToken> Tokenize(string template) { using var buffer = new TextBuffer(template); var result = new List <TemplateToken>(); while (!buffer.ReachedEnd) { EatWhitespace(buffer); if (!buffer.TryPeek(out var character)) { break; } if (character == '-') { result.Add(ReadOption(buffer)); } else if (character == '|') { buffer.Consume('|'); } else if (character == '<') { result.Add(ReadValue(buffer, true)); } else if (character == '[') { result.Add(ReadValue(buffer, false)); } else { throw CommandTemplateException.UnexpectedCharacter(buffer.Original, buffer.Position, character); } } return(result); }
private static int ParseToken(CommandTreeTokenizerContext context, TextBuffer reader, int position, int start, List <CommandTreeToken> tokens) { while (reader.Peek() != -1) { if (reader.ReachedEnd) { position += reader.Position - start; break; } var character = reader.Peek(); // Eat whitespace if (char.IsWhiteSpace(character)) { reader.Consume(); continue; } if (character == '-') { // Option tokens.AddRange(ScanOptions(context, reader)); } else { // Command or argument tokens.Add(ScanString(context, reader)); } // Flush remaining tokens context.FlushRemaining(); } return(position); }