示例#1
0
        private void ScanStackedOptions(
            ElementsCursor <SyntaxToken> cursor, List <SyntaxError> errors,
            SyntaxToken nameMarker, SyntaxToken name, IReadOnlyList <string> commandName, List <ArgumentSyntax> arguments)
        {
            var    builder      = new SyntaxTokenBuilder();
            var    optionsSpan  = name.Span;
            string optionNames  = name.StringValue;
            int    optionsCount = optionNames.Length;

            GetShortOptionName(errors, builder, optionsSpan, optionNames, 0);
            var shortName = builder.Build();
            var option    = new OptionSyntax(nameMarker, shortName, valueMarker: null, value: null);

            arguments.Add(option);
            nameMarker = null;

            for (int i = 1; i < optionsCount - 1; i++)
            {
                GetShortOptionName(errors, builder, optionsSpan, optionNames, i);
                shortName = builder.Build();
                option    = new OptionSyntax(nameMarker, shortName, valueMarker: null, value: null);
                arguments.Add(option);
            }

            GetShortOptionName(errors, builder, optionsSpan, optionNames, optionsCount - 1);
            if (name.HasTrailingTrivia)
            {
                builder.TrailingTrivia = name.TrailingTrivia;
            }
            name = builder.Build();

            option = ScanOption(cursor, errors, nameMarker, name, commandName);
            arguments.Add(option);
        }
示例#2
0
        private SyntaxToken ScanEndOfInput(
            ElementsCursor <SyntaxToken> cursor, List <SyntaxError> errors)
        {
            var token = cursor.Peek();

            if (token.Kind == SyntaxKind.EndOfInputToken)
            {
                return(token);
            }

            var builder = new SyntaxTokenBuilder();
            var span    = token.FullSpan;

            _syntaxFactory.EndOfInputToken(builder, span.Source, span.Start, missing: true);

            errors.Add(_syntaxFactory.MissingEndOfInputError(builder.Span));

            if (!span.IsEmpty)
            {
                int start  = span.Start;
                int length = cursor.TerminalElement.FullSpan.End - start;
                builder.TrailingTrivia = _syntaxFactory.UndefinedTrivia(span.Source, start, length);

                errors.Add(_syntaxFactory.UnrecognizedInputError(builder.TrailingTrivia.Span));
            }

            return(builder.Build());
        }
示例#3
0
        private ValueSyntax ScanValue(ElementsCursor <SyntaxToken> cursor, List <SyntaxError> errors)
        {
            var token = cursor.Peek();

            cursor.MoveNext();

            if (token.Kind == SyntaxKind.QuotedLiteralToken || !HasMoreTokens(cursor))
            {
                if (!HasSpaceAfterOrEnd(token, cursor.Peek(1)))
                {
                    // Quoted literals do not merge with any other literal
                    // even if they don't have spaces between them.
                    var builder = new SyntaxTokenBuilder(token);
                    builder.TrailingTrivia = _syntaxFactory.WhitespaceTriviaAfter(token, length: 0, missing: true);
                    token = builder.Build();
                    errors.Add(_syntaxFactory.MissingWhitespaceError(token.TrailingTrivia.Span));
                }

                if (token.Kind == SyntaxKind.QuotedLiteralToken &&
                    !HasClosingQuote(token))
                {
                    var span = token.Span;
                    errors.Add(_syntaxFactory.MissingClosingQuoteError(
                                   new TextSpan(span.Source, span.End, 0)));
                }

                return(new ValueSyntax(token));
            }

            return(ScanMultiTokenValue(cursor, errors, token));
        }
示例#4
0
 public void LiteralToken(SyntaxTokenBuilder builder,
                          string input, int position, int length, string stringValue,
                          bool quoted = false, bool missing = false)
 {
     builder.InitializeWith(quoted ? SyntaxKind.QuotedLiteralToken : SyntaxKind.LiteralToken,
                            new TextSpan(input, position, length), stringValue, missing);
 }
示例#5
0
 public void IdentifierToken(SyntaxTokenBuilder builder,
                             string input, int position, int length, string stringValue,
                             bool missing = false)
 {
     builder.InitializeWith(SyntaxKind.IdentifierToken,
                            new TextSpan(input, position, length), stringValue, missing);
 }
示例#6
0
        private SyntaxToken GetTerminalElement(SyntaxToken[] tokens, string commandLine)
        {
            var terminalElement = tokens.LastOrDefault();

            if (terminalElement == null || terminalElement.Kind != SyntaxKind.EndOfInputToken)
            {
                var builder = new SyntaxTokenBuilder();
                _syntaxFactory.EndOfInputToken(builder,
                                               commandLine, terminalElement?.FullSpan.End ?? 0, missing: true);
                terminalElement = builder.Build();
            }

            return(terminalElement);
        }
示例#7
0
        private void GetShortOptionName(
            List <SyntaxError> errors, SyntaxTokenBuilder builder,
            TextSpan optionsSpan, string optionNames, int optionIndex)
        {
            char optionName = optionNames[optionIndex];

            if (!Char.IsLetter(optionName))
            {
                errors.Add(_syntaxFactory.InvalidOptionNameError(
                               new TextSpan(optionsSpan.Source, optionsSpan.Start + optionIndex, 1)));
            }

            _syntaxFactory.IdentifierToken(builder,
                                           optionsSpan.Source, optionsSpan.Start + optionIndex, 1, optionName.ToString());
        }
示例#8
0
        private void LexSyntaxToken(
            SyntaxTokenBuilder builder, string input, ElementsCursor <char> cursor)
        {
            switch (cursor.Peek())
            {
            case EndOfInput:
                _syntaxFactory.EndOfInputToken(builder, input, cursor.Position);
                break;

            case '-':
                char next = cursor.Peek(1);
                if (next == '-')
                {
                    _syntaxFactory.DashDashToken(builder, input, cursor.Position);
                    cursor.MoveNext(2);
                }
                else if (Char.IsDigit(next))
                {
                    // Possibly a negative number.
                    ScanLiteralOrIdentifierToken(builder, input, cursor);
                }
                else
                {
                    _syntaxFactory.DashToken(builder, input, cursor.Position);
                    cursor.MoveNext();
                }
                break;

            case '=':
                _syntaxFactory.EqualsToken(builder, input, cursor.Position);
                cursor.MoveNext();
                break;

            case ':':
                _syntaxFactory.ColonToken(builder, input, cursor.Position);
                cursor.MoveNext();
                break;

            default:
                ScanLiteralOrIdentifierToken(builder, input, cursor);
                break;
            }
        }
示例#9
0
        private CommandNameSyntax ScanCommandName(
            ElementsCursor <SyntaxToken> cursor, List <SyntaxError> errors, CancellationToken cancellation,
            out IReadOnlyList <string> commandName)
        {
            var nameParts = new List <SyntaxToken>();
            var command   = new List <string>();

            while (HasMoreTokens(cursor) && !cancellation.IsCancellationRequested)
            {
                var token = cursor.Peek();
                if (!IsIdentifier(token))
                {
                    break;
                }

                string subcommand = token.StringValue;
                if (!_semanticModel.HasSubCommand(command, subcommand))
                {
                    break;
                }

                if (!HasSpaceAfterOrEnd(token, cursor.Peek(1)))
                {
                    var builder = new SyntaxTokenBuilder(token);
                    builder.TrailingTrivia = _syntaxFactory.WhitespaceTriviaAfter(token, length: 0, missing: true);
                    token = builder.Build();

                    errors.Add(_syntaxFactory.MissingWhitespaceError(token.TrailingTrivia.Span));
                }

                command.Add(subcommand);
                nameParts.Add(token);
                cursor.MoveNext();
            }

            cancellation.ThrowIfCancellationRequested();
            commandName = command;
            if (nameParts.Count == 0)
            {
                return(null);
            }
            return(new CommandNameSyntax(nameParts));
        }
示例#10
0
        public IEnumerable <SyntaxToken> Lex(string commandLine, CancellationToken cancellation = default)
        {
            if (commandLine == null)
            {
                throw new ArgumentNullException(nameof(commandLine));
            }
            cancellation.ThrowIfCancellationRequested();

            var chars  = new CharsReadOnlyList(commandLine);
            var cursor = new ElementsCursor <char>(chars, EndOfInput);

            var builder = new SyntaxTokenBuilder();

            do
            {
                builder.LeadingTrivia = LexSyntaxTrivia(commandLine, cursor);
                LexSyntaxToken(builder, commandLine, cursor);
                builder.TrailingTrivia = LexSyntaxTrivia(commandLine, cursor);

                cancellation.ThrowIfCancellationRequested();
                yield return(builder.Build());
            } while (builder.Kind != SyntaxKind.EndOfInputToken);
        }
示例#11
0
        private ValueSyntax ScanMultiTokenValue(
            ElementsCursor <SyntaxToken> cursor, List <SyntaxError> errors,
            SyntaxToken first)
        {
            var tokens = new List <SyntaxToken>();
            var token  = first;

            tokens.Add(token);

            while (HasMoreTokens(cursor))
            {
                var next = cursor.Peek();
                if (HasSpaceAfterOrEnd(token, next))
                {
                    break;
                }

                if (next.Kind == SyntaxKind.QuotedLiteralToken)
                {
                    // Quoted literals do not merge with any other literal
                    // even if they don't have spaces between them.
                    var builder = new SyntaxTokenBuilder(token);
                    builder.TrailingTrivia   = _syntaxFactory.WhitespaceTriviaAfter(token, length: 0, missing: true);
                    tokens[tokens.Count - 1] = builder.Build();

                    errors.Add(_syntaxFactory.MissingWhitespaceError(builder.TrailingTrivia.Span));
                    break;
                }

                token = next;
                tokens.Add(token);
                cursor.MoveNext();
            }

            return(new ValueSyntax(tokens));
        }
示例#12
0
        private void ScanLiteralOrIdentifierToken(
            SyntaxTokenBuilder builder, string input, ElementsCursor <char> cursor)
        {
            int  start       = cursor.Position;
            char?quote       = null;
            var  stringValue = new StringBuilder(128);

            char first        = cursor.Peek();
            bool isIdentifier = Char.IsLetter(first);

            if (first == '"' || first == '\'')
            {
                quote        = first;
                isIdentifier = false;
                cursor.MoveNext();
            }

            while (!cursor.IsAtTheEnd())
            {
                char c = cursor.Peek();

                if (Char.IsWhiteSpace(c))
                {
                    if (!quote.HasValue)
                    {
                        break;
                    }
                    stringValue.Append(c);
                    cursor.MoveNext();
                }
                else if (c == '=' || c == ':')
                {
                    if (isIdentifier)
                    {
                        break;
                    }
                    stringValue.Append(c);
                    cursor.MoveNext();
                }
                else if (c == '"' || c == '\'')
                {
                    // Start of another quoted literal.
                    if (!quote.HasValue)
                    {
                        break;
                    }

                    if (c == quote)
                    {
                        char next = cursor.Peek(1);
                        if (next == c)
                        {
                            // Escaped quote sequence.
                            stringValue.Append(c);
                            cursor.MoveNext(2);
                        }
                        else
                        {
                            // Closing quote symbol.
                            cursor.MoveNext();
                            break;
                        }
                    }
                    else
                    {
                        // Another quote symbol.
                        stringValue.Append(c);
                        cursor.MoveNext();
                    }
                }
                else
                {
                    if (isIdentifier && !IsIdentifierSymbol(c))
                    {
                        isIdentifier = false;
                    }
                    stringValue.Append(c);
                    cursor.MoveNext();
                }
            }

            // We don't know for sure that this is a pure identifier without a context
            // but we definitely can identify a pure literal.
            if (isIdentifier)
            {
                _syntaxFactory.IdentifierOrLiteralToken(builder, input,
                                                        start, cursor.Position - start, stringValue.ToString());
            }
            else
            {
                _syntaxFactory.LiteralToken(builder, input,
                                            start, cursor.Position - start, stringValue.ToString(), quoted: quote.HasValue);
            }
        }
示例#13
0
        private OptionSyntax ScanOption(ElementsCursor <SyntaxToken> cursor, List <SyntaxError> errors,
                                        SyntaxToken nameMarker, SyntaxToken name, IReadOnlyList <string> commandName)
        {
            SyntaxToken valueMarker = null;
            ValueSyntax value       = null;

            if (!HasMoreTokens(cursor))
            {
                return(new OptionSyntax(nameMarker, name, valueMarker, value));
            }

            var token = cursor.Peek();

            if (IsOptionValueMarker(token))
            {
                valueMarker = token;
                cursor.MoveNext();
                token = cursor.Peek();
            }
            else
            {
                if (!HasSpaceAfterOrEnd(name, token))
                {
                    var builder = new SyntaxTokenBuilder(name);
                    builder.TrailingTrivia = _syntaxFactory.WhitespaceTriviaAfter(name, length: 0, missing: true);
                    name = builder.Build();

                    errors.Add(_syntaxFactory.MissingWhitespaceError(name.TrailingTrivia.Span));
                }

                _semanticModel.TryGetOptionType(commandName, name.StringValue, out Type valueType);

                if (IsFlag(valueType))
                {
                    // Flag can have a value only if a value marker is specified
                    // because it have value 'true' by default.
                    return(new OptionSyntax(nameMarker, name, valueMarker, value));
                }
            }

            bool missingValue = valueMarker != null &&
                                (!HasMoreTokens(cursor) || (valueMarker.HasTrailingTrivia && IsOptionNameOrEndOfOptions(token, cursor.Peek(1))));

            if (missingValue)
            {
                SyntaxToken valueToken = null;
                var         builder    = new SyntaxTokenBuilder();
                var         span       = valueMarker.Span;
                _syntaxFactory.LiteralToken(builder, span.Source, span.End, 0, null, missing: true);

                if (valueMarker.HasTrailingTrivia)
                {
                    // Place trailing trivia after missing option value
                    builder.TrailingTrivia = valueMarker.TrailingTrivia;
                    valueToken             = builder.Build();

                    // and remove it from value marker.
                    builder.InitializeWith(valueMarker);
                    builder.TrailingTrivia = null;
                    valueMarker            = builder.Build();
                }
                else
                {
                    valueToken = builder.Build();
                }

                errors.Add(_syntaxFactory.MissingOptionValueError(valueToken.Span));
                value = new ValueSyntax(valueToken);
                return(new OptionSyntax(nameMarker, name, valueMarker, value));
            }

            bool isValue = (valueMarker != null && !valueMarker.HasTrailingTrivia) ||
                           !IsOptionNameOrEndOfOptions(token, cursor.Peek(1));

            if (isValue)
            {
                value = ScanValue(cursor, errors);
            }

            return(new OptionSyntax(nameMarker, name, valueMarker, value));
        }
示例#14
0
 public void ColonToken(SyntaxTokenBuilder builder,
                        string input, int position, bool missing = false)
 {
     builder.InitializeWith(SyntaxKind.ColonToken,
                            new TextSpan(input, position, 1), ":", missing);
 }
示例#15
0
 public void DashDashToken(SyntaxTokenBuilder builder,
                           string input, int position, bool missing = false)
 {
     builder.InitializeWith(SyntaxKind.DashDashToken,
                            new TextSpan(input, position, 2), "--", missing);
 }
示例#16
0
 public void EndOfInputToken(SyntaxTokenBuilder builder,
                             string input, int position, bool missing = false)
 {
     builder.InitializeWith(SyntaxKind.EndOfInputToken,
                            new TextSpan(input, position, 0), "<eoi>", missing);
 }