File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\EditorConfig\Parsing\Sections\SectionMatcher.Lexer.cs
Web Access
Project: src\src\CodeStyle\Core\Analyzers\Microsoft.CodeAnalysis.CodeStyle.csproj (Microsoft.CodeAnalysis.CodeStyle)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Text;
 
namespace Microsoft.CodeAnalysis.EditorConfig.Parsing;
 
internal readonly partial struct SectionMatcher
{
    private struct Lexer(string headerText)
    {
        public int Position { get; set; } = 0;
 
        public readonly bool IsDone => Position >= headerText.Length;
 
        public TokenKind Lex()
        {
            var tokenKind = GetTokenKindAtPosition(headerText, Position);
            switch (tokenKind)
            {
                case TokenKind.StarStar:
                    Position += 2;
                    break;
                case TokenKind.SimpleCharacter:
                    if (headerText[Position] == '\\')
                    {
                        // Backslash escapes the next character
                        Position++;
                    }
 
                    // Don't increment position, since caller needs to fetch the character
                    break;
                case TokenKind.Question:
                case TokenKind.OpenCurly:
                case TokenKind.Comma:
                case TokenKind.OpenBracket:
                case TokenKind.CloseCurly:
                case TokenKind.Star:
                    Position++;
                    break;
                case TokenKind.BadToken:
                default:
                    break;
            }
 
            return tokenKind;
        }
 
        public readonly bool TryPeekNext(out TokenKind kind)
        {
            var position = Position;
            position++;
            if (position < headerText.Length)
            {
                kind = GetTokenKindAtPosition(headerText, position);
                return true;
            }
 
            kind = default;
            return false;
        }
 
        public readonly bool TryPeekPrevious(out TokenKind kind)
        {
            var position = Position;
            position--;
            if (position >= 0)
            {
                kind = GetTokenKindAtPosition(headerText, position);
                return true;
            }
 
            kind = default;
            return false;
        }
 
        private static TokenKind GetTokenKindAtPosition(string headerText, int position)
        {
            switch (headerText[position])
            {
                case '*':
                    {
                        position++;
                        if (position < headerText.Length &&
                            headerText[position] == '*')
                        {
                            return TokenKind.StarStar;
                        }
                        else
                        {
                            return TokenKind.Star;
                        }
                    }
 
                case '?':
                    return TokenKind.Question;
 
                case '{':
                    return TokenKind.OpenCurly;
 
                case ',':
                    return TokenKind.Comma;
 
                case '}':
                    return TokenKind.CloseCurly;
 
                case '[':
                    return TokenKind.OpenBracket;
 
                case '\\':
                    position++;
                    if (position >= headerText.Length)
                    {
                        return TokenKind.BadToken;
                    }
 
                    return TokenKind.SimpleCharacter;
                default:
                    return TokenKind.SimpleCharacter;
            }
        }
 
        public readonly char CurrentCharacter => headerText[Position];
 
        public char EatCurrentCharacter() => headerText[Position++];
 
        public bool TryEatCurrentCharacter(out char nextChar)
        {
            if (IsDone)
            {
                nextChar = default;
                return false;
            }
            else
            {
                nextChar = EatCurrentCharacter();
                return true;
            }
        }
 
        public readonly char this[int position] => headerText[position];
 
        public string? TryLexNumber()
        {
            var start = true;
            var sb = new StringBuilder();
 
            while (!IsDone)
            {
                var currentChar = CurrentCharacter;
                if (start && currentChar == '-')
                {
                    Position++;
                    sb.Append('-');
                }
                else if (char.IsDigit(currentChar))
                {
                    Position++;
                    sb.Append(currentChar);
                }
                else
                {
                    break;
                }
 
                start = false;
            }
 
            var str = sb.ToString();
            return str.Length == 0 || str == "-"
                ? null
                : str;
        }
    }
 
    private enum TokenKind
    {
        BadToken,
        SimpleCharacter,
        Star,
        StarStar,
        Question,
        OpenCurly,
        CloseCurly,
        Comma,
        OpenBracket,
    }
}