private bool ScanIdentifier_SlowPath()

in src/Compilers/CSharp/Portable/Parser/Lexer.cs [1632:1888]


        private bool ScanIdentifier_SlowPath(ref TokenInfo info)
        {
            int start = TextWindow.Position;
            this.ResetIdentBuffer();

            info.IsVerbatim = TextWindow.PeekChar() == '@';
            if (info.IsVerbatim)
            {
                TextWindow.AdvanceChar();
            }

            bool isObjectAddress = false;

            while (true)
            {
                char surrogateCharacter = SlidingTextWindow.InvalidCharacter;
                bool isEscaped = false;
                char ch = TextWindow.PeekChar();
                top:
                switch (ch)
                {
                    case '\\':
                        if (!isEscaped && TextWindow.IsUnicodeEscape())
                        {
                            // ^^^^^^^ otherwise \u005Cu1234 looks just like \u1234! (i.e. escape within escape)
                            info.HasIdentifierEscapeSequence = true;
                            isEscaped = true;
                            ch = TextWindow.PeekUnicodeEscape(out surrogateCharacter);
                            goto top;
                        }

                        goto default;
                    case '$':
                        if (!this.ModeIs(LexerMode.DebuggerSyntax) || _identLen > 0)
                        {
                            goto LoopExit;
                        }

                        break;
                    case SlidingTextWindow.InvalidCharacter:
                        if (!TextWindow.IsReallyAtEnd())
                        {
                            goto default;
                        }

                        goto LoopExit;
                    case '_':
                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'E':
                    case 'F':
                    case 'G':
                    case 'H':
                    case 'I':
                    case 'J':
                    case 'K':
                    case 'L':
                    case 'M':
                    case 'N':
                    case 'O':
                    case 'P':
                    case 'Q':
                    case 'R':
                    case 'S':
                    case 'T':
                    case 'U':
                    case 'V':
                    case 'W':
                    case 'X':
                    case 'Y':
                    case 'Z':
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'e':
                    case 'f':
                    case 'g':
                    case 'h':
                    case 'i':
                    case 'j':
                    case 'k':
                    case 'l':
                    case 'm':
                    case 'n':
                    case 'o':
                    case 'p':
                    case 'q':
                    case 'r':
                    case 's':
                    case 't':
                    case 'u':
                    case 'v':
                    case 'w':
                    case 'x':
                    case 'y':
                    case 'z':
                        {
                            // Again, these are the 'common' identifier characters...
                            break;
                        }

                    case '0':
                        {
                            if (_identLen == 0)
                            {
                                // Debugger syntax allows @0x[hexdigit]+ for object address identifiers.
                                if (info.IsVerbatim &&
                                    this.ModeIs(LexerMode.DebuggerSyntax) &&
                                    (char.ToLower(TextWindow.PeekChar(1)) == 'x'))
                                {
                                    isObjectAddress = true;
                                }
                                else
                                {
                                    goto LoopExit;
                                }
                            }

                            // Again, these are the 'common' identifier characters...
                            break;
                        }
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        {
                            if (_identLen == 0)
                            {
                                goto LoopExit;
                            }

                            // Again, these are the 'common' identifier characters...
                            break;
                        }

                    case ' ':
                    case '\t':
                    case '.':
                    case ';':
                    case '(':
                    case ')':
                    case ',':
                        // ...and these are the 'common' stop characters.
                        goto LoopExit;
                    case '<':
                        if (_identLen == 0 && this.ModeIs(LexerMode.DebuggerSyntax) && TextWindow.PeekChar(1) == '>')
                        {
                            // In DebuggerSyntax mode, identifiers are allowed to begin with <>.
                            TextWindow.AdvanceChar(2);
                            this.AddIdentChar('<');
                            this.AddIdentChar('>');
                            continue;
                        }

                        goto LoopExit;
                    default:
                        {
                            // This is the 'expensive' call
                            if (_identLen == 0 && ch > 127 && SyntaxFacts.IsIdentifierStartCharacter(ch))
                            {
                                break;
                            }
                            else if (_identLen > 0 && ch > 127 && SyntaxFacts.IsIdentifierPartCharacter(ch))
                            {
                                //// BUG 424819 : Handle identifier chars > 0xFFFF via surrogate pairs
                                if (UnicodeCharacterUtilities.IsFormattingChar(ch))
                                {
                                    if (isEscaped)
                                    {
                                        SyntaxDiagnosticInfo error;
                                        TextWindow.NextCharOrUnicodeEscape(out surrogateCharacter, out error);
                                        AddError(error);
                                    }
                                    else
                                    {
                                        TextWindow.AdvanceChar();
                                    }

                                    continue; // Ignore formatting characters
                                }

                                break;
                            }
                            else
                            {
                                // Not a valid identifier character, so bail.
                                goto LoopExit;
                            }
                        }
                }

                if (isEscaped)
                {
                    SyntaxDiagnosticInfo error;
                    TextWindow.NextCharOrUnicodeEscape(out surrogateCharacter, out error);
                    AddError(error);
                }
                else
                {
                    TextWindow.AdvanceChar();
                }

                this.AddIdentChar(ch);
                if (surrogateCharacter != SlidingTextWindow.InvalidCharacter)
                {
                    this.AddIdentChar(surrogateCharacter);
                }
            }

            LoopExit:
            var width = TextWindow.Width; // exact size of input characters
            if (_identLen > 0)
            {
                info.Text = TextWindow.GetInternedText();

                // id buffer is identical to width in input
                if (_identLen == width)
                {
                    info.StringValue = info.Text;
                }
                else
                {
                    info.StringValue = TextWindow.Intern(_identBuffer, 0, _identLen);
                }

                if (isObjectAddress)
                {
                    // @0x[hexdigit]+
                    const int objectAddressOffset = 2;
                    Debug.Assert(string.Equals(info.Text.Substring(0, objectAddressOffset + 1), "@0x", StringComparison.OrdinalIgnoreCase));
                    var valueText = TextWindow.Intern(_identBuffer, objectAddressOffset, _identLen - objectAddressOffset);
                    // Verify valid hex value.
                    if ((valueText.Length == 0) || !valueText.All(IsValidHexDigit))
                    {
                        goto Fail;
                    }
                    // Parse hex value to check for overflow.
                    this.GetValueUInt64(valueText, isHex: true, isBinary: false);
                }

                return true;
            }

            Fail:
            info.Text = null;
            info.StringValue = null;
            TextWindow.Reset(start);
            return false;
        }