Пример #1
0
        protected async IAsyncEnumerable <Token> Lex()
        {
            char c;

            while (!await InputStream.Eof())
            {
                int    line = TranslationUnit.CurrentLine, column = TranslationUnit.CurrentColumn;
                string filename = TranslationUnit.CurrentFilename;
                // #include <FILENAME_TO_PARSE>
                if (TranslationUnit.LexerState == LexingLibraryFilename)
                {
                    var sb = new StringBuilder();
                    while (!await InputStream.Eof() && ((c = await InputStream.Peek()) != '>'))
                    {
                        sb.Append(await InputStream.Read());
                    }
                    TranslationUnit.LexerState = LexerState.LexerReady;
                    yield return(new ValueToken <string>(Terminal.Filename, line, column, filename, sb.ToString()));

                    continue; // Parse the next token
                }
                c = await InputStream.Read();

                if (char.IsLetter(c) || c == '_')
                {
                    var    sb = new StringBuilder(c.ToString());
                    Symbol symbol;
                    while ((c = await InputStream.Peek()) == '_' || char.IsLetter(c) || char.IsDigit(c))
                    {
                        sb.Append(await InputStream.Read());
                    }
                    var ident = sb.ToString();
                    if (ReservedWords.ContainsKey(ident))
                    {
                        yield return(new Token(ReservedWords[ident], line, column, filename));
                    }
                    else if (TranslationUnit.Symbols.ContainsKey(ident) &&
                             ((symbol = TranslationUnit.Symbols[ident]).Type == SymbolType.EnumSymbol ||
                              symbol.Type == TypedefSymbol))
                    {
                        yield return(symbol.Type == TypedefSymbol
                            ? new ValueToken <Symbol>(TypedefName, line, column, filename, symbol) as Token
                            : new ValueToken <EnumSymbol>(EnumConstant, line, column, filename, (symbol as EnumSymbol) !) as Token);
                    }
                    else
                    {
                        yield return(new ValueToken <string>(Identifier, line, column, filename, ident));
                    }
                }
                else if (char.IsDigit(c))
                {
                    var integer    = 0UL + c - '0';
                    var fraction   = 0M;
                    var real       = 0M;
                    var hexOrOctal = false;
                    var floating   = false;
                    var unsigned   = false;
                    var isLong     = false;
                    var nonDouble  = false;
                    var d          = 0d;
                    if (c == '0')
                    {
                        if ((c = await InputStream.Peek()) == 'x' || c == 'X')
                        {
                            await InputStream.Read();

                            while (char.IsDigit(c = char.ToLower(await InputStream.Peek())) || (c >= 'a' && c <= 'f'))
                            {
                                await InputStream.Read();

                                integer *= 0x10;
                                integer += (ulong)(char.IsDigit(c) ? c - '0' : c - 'a' + 0xa);
                            }
                            hexOrOctal = true;
                        }
                        else if ((c = await InputStream.Peek()) >= '0' && c <= '7') // Octal
                        {
                            while ((c = await InputStream.Peek()) >= '0' && c <= '7')
                            {
                                c = await InputStream.Read();

                                integer *= 8;
                                integer += (ulong)(c - '0');
                            }
                            hexOrOctal = true;
                        }
                    }
                    if (!hexOrOctal)
                    {
                        while (char.IsDigit(await InputStream.Peek()))
                        {
                            c = await InputStream.Read();

                            integer *= 10;
                            integer += (ulong)(c - '0');
                        }
                        if (await InputStream.Peek() == '.')
                        {
                            var multiplier = 0.1M;
                            floating = true;
                            await InputStream.Read();

                            while (char.IsDigit(await InputStream.Peek()))
                            {
                                c = await InputStream.Read();

                                fraction   += (c - '0') * multiplier;
                                multiplier *= 0.1M;
                            }
                            d = (double)(integer + fraction);
                        }
                        if ((c = await InputStream.Peek()) == 'e' || c == 'E')
                        {
                            floating = true;
                            real     = integer + fraction;
                            var eSign = 1;
                            var e     = 0;
                            await InputStream.Read();

                            if ((c = await InputStream.Peek()) == '+' || c == '-')
                            {
                                await InputStream.Read();

                                eSign = c == '-' ? -1 : 1;
                            }
                            while (char.IsDigit(await InputStream.Peek()))
                            {
                                c = await InputStream.Read();

                                e *= 10;
                                e += c - '0';
                            }
                            if (real != 0m)
                            {
                                e *= eSign;
                                d  = (double)real * Math.Pow(10.0, (double)e);
                                while (real >= 10m)
                                {
                                    real /= 10m;
                                    e    += eSign;
                                }
                                while (real < 1m)
                                {
                                    real *= 10m;
                                    e    -= eSign;
                                }
                                if (e == 308 && real >= 1.7976931348623157m || e > 308)
                                {
                                    d = double.MaxValue;
                                }
                                else if (e == -324 && real <= 4.94065645841247m || e < -324)
                                {
                                    d = double.Epsilon;
                                }
                            }
                        }
                    }
                    while ((c = await InputStream.Peek()) == 'l' || c == 'L' || c == 'u' || c == 'U' || c == 'f' || c == 'F')
                    {
                        switch (c = char.ToLower(await InputStream.Read()))
                        {
                        case 'l':
                            isLong = true;
                            break;

                        case 'u':
                            unsigned = true;
                            break;

                        case 'f':
                            if (!floating)
                            {
                                real     = integer;
                                floating = true;
                            }
                            nonDouble = true;
                            break;
                        }
                    }
                    var valid = true;
                    if (floating && (isLong || unsigned))
                    {
                        Error("Invalid suffix combination");
                        valid = false;
                    }
                    if (floating)
                    {
                        var token = new FloatingToken(line, column, filename, d, nonDouble);
                        token.IsValid = valid;
                        yield return(token);
                    }
                    else
                    {
                        yield return(new IntegerToken(line, column, filename, integer, unsigned, isLong));
                    }
                }
                else
                {
                    switch (c)
                    {
                    case ' ':
                    case '\t':
                        while (!await InputStream.Eof() && ((c = await InputStream.Peek()) == ' ' || c == '\t'))
                        {
                            await InputStream.Read();
                        }
                        if (OutputTrivia)
                        {
                            yield return(new Token(Whitespace, line, column, filename));
                        }
                        break;

                    case '\r':
                        if (await InputStream.Peek() == '\n')
                        {
                            await InputStream.Read();
                        }
                        if (OutputTrivia)
                        {
                            yield return(new Token(Newline, line, column, filename));
                        }
                        break;

                    case '\n':
                        if (OutputTrivia)
                        {
                            yield return(new Token(Newline, line, column, filename));
                        }
                        break;

                    case '\"':
                    case '\'':
                    {
                        char delimeter = c;
                        var  sb        = new StringBuilder();
                        while (!await InputStream.Eof() && (c = await InputStream.Peek()) != delimeter && c != '\r' && c != '\n')
                        {
                            if (c == '\\')
                            {
                                await InputStream.Read();

                                if (!await InputStream.Eof())
                                {
                                    c = await InputStream.Read();

                                    if (char.IsDigit(c) && c < '8')
                                    {
                                        int cc = c - '0';
                                        for (var i = 0; i < 2 && char.IsDigit(c = await InputStream.Peek()) && c < '8'; i++)
                                        {
                                            cc *= 8;
                                            cc += await InputStream.Read() - '0';
                                        }
                                        sb.Append((char)cc);
                                    }
                                    else
                                    {
                                        switch (c)
                                        {
                                        case 'n':
                                            sb.Append('\n');
                                            break;

                                        case 't':
                                            sb.Append('\t');
                                            break;

                                        case 'v':
                                            sb.Append('\v');
                                            break;

                                        case 'b':
                                            sb.Append('\b');
                                            break;

                                        case 'r':
                                            sb.Append('\r');
                                            break;

                                        case 'f':
                                            sb.Append('\f');
                                            break;

                                        case 'a':
                                            sb.Append('\a');
                                            break;

                                        case '\\':
                                            sb.Append('\\');
                                            break;

                                        case '?':
                                            sb.Append('?');
                                            break;

                                        case '\'':
                                            sb.Append('\'');
                                            break;

                                        case '"':
                                            sb.Append('"');
                                            break;

                                        case 'x':
                                        case 'X':
                                        {
                                            var letter = c;
                                            int i;
                                            int cc = 0;
                                            for (i = 0; i < 2 && (char.IsDigit(c = char.ToLower(await InputStream.Peek())) || (c >= 'a' && c <= 'f')); i++)
                                            {
                                                await InputStream.Read();

                                                cc *= 0x10;
                                                if (char.IsDigit(c))
                                                {
                                                    cc += c - '0';
                                                }
                                                else
                                                {
                                                    cc += char.ToLower(c) - 'a' + 0xa;
                                                }
                                            }
                                            if (i == 0)
                                            {
                                                sb.Append("\\" + letter);
                                            }
                                            else
                                            {
                                                sb.Append((char)cc);
                                            }
                                            break;
                                        }

                                        default:
                                            sb.Append('\\');
                                            sb.Append(c);
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                sb.Append(await InputStream.Read());
                            }
                        }
                        var valid = true;
                        if (await InputStream.Eof() || (c = await InputStream.Peek()) != delimeter)
                        {
                            Error($"{delimeter} expected");
                            valid = false;
                        }
                        else
                        {
                            await InputStream.Read();
                        }
                        if (delimeter == '\'' && sb.Length != 1)
                        {
                            Error("Invalid character literal");
                            valid = false;
                        }
                        var token = new ValueToken <string>(delimeter == '\"' ? StringLiteral : CharLiteral, line, column, filename, sb.ToString());
                        token.IsValid = valid;
                        yield return(token);

                        break;
                    }

                    case '~':
                        yield return(new Token(Tilde, line, column, filename));

                        break;

                    case '#':
                        if (PreprocessorTokens && await InputStream.Peek() == '#')
                        {
                            await InputStream.Read();

                            yield return(new Token(DoublePound, line, column, filename));
                        }
                        else
                        {
                            if (!PreprocessorTokens)
                            {
                                Error("Unexpected symbol #");
                            }

                            yield return(PreprocessorTokens
                                    ? new Token(Pound, line, column, filename)
                                    : new ValueToken <char>(Terminal.Unknown, line, column, filename, '#'));
                        }
                        break;

                    case '(':
                        yield return(new Token(LeftParen, line, column, filename));

                        break;

                    case ')':
                        yield return(new Token(RightParen, line, column, filename));

                        break;

                    case '{':
                        yield return(new Token(LeftBrace, line, column, filename));

                        break;

                    case '}':
                        yield return(new Token(RightBrace, line, column, filename));

                        break;

                    case '[':
                        yield return(new Token(LeftBracket, line, column, filename));

                        break;

                    case ']':
                        yield return(new Token(RightBracket, line, column, filename));

                        break;

                    case ':':
                        yield return(new Token(Colon, line, column, filename));

                        break;

                    case ';':
                        yield return(new Token(Semicolon, line, column, filename));

                        break;

                    case ',':
                        yield return(new Token(Comma, line, column, filename));

                        break;

                    case '.':
                        yield return(new Token(Dot, line, column, filename));

                        break;

                    case '?':
                        yield return(new Token(Query, line, column, filename));

                        break;

                    case '!':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(NotEqual, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Bang, line, column, filename));
                        }
                        break;

                    case '%':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(ModAssign, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Percent, line, column, filename));
                        }
                        break;

                    case '^':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(XorAssign, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Caret, line, column, filename));
                        }
                        break;

                    case '&':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(AndAssign, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '&')
                        {
                            await InputStream.Read();

                            yield return(new Token(LogicalAnd, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Ampersand, line, column, filename));
                        }
                        break;

                    case '*':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(MultiplyAssign, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Star, line, column, filename));
                        }
                        break;

                    case '-':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(SubtractAssign, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '-')
                        {
                            await InputStream.Read();

                            yield return(new Token(Decrement, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '>')
                        {
                            await InputStream.Read();

                            yield return(new Token(Arrow, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Minus, line, column, filename));
                        }
                        break;

                    case '+':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(AddAssign, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '+')
                        {
                            await InputStream.Read();

                            yield return(new Token(Increment, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Plus, line, column, filename));
                        }
                        break;

                    case '=':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(DoubleEquals, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Assign, line, column, filename));
                        }
                        break;

                    case '|':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(OrAssign, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '|')
                        {
                            await InputStream.Read();

                            yield return(new Token(LogicalOr, line, column, filename));
                        }
                        else
                        {
                            yield return(new Token(Pipe, line, column, filename));
                        }
                        break;

                    case '<':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(LessThanOrEqual, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '<')
                        {
                            await InputStream.Read();

                            if (await InputStream.Peek() == '=')
                            {
                                await InputStream.Read();

                                yield return(new Token(ShiftLeftAssign, line, column, filename));
                            }
                            else
                            {
                                yield return(new Token(ShiftLeft, line, column, filename));
                            }
                        }
                        else
                        {
                            yield return(new Token(LessThan, line, column, filename));
                        }
                        break;

                    case '>':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(GreaterThanOrEqual, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '>')
                        {
                            await InputStream.Read();

                            if (await InputStream.Peek() == '=')
                            {
                                await InputStream.Read();

                                yield return(new Token(ShiftRightAssign, line, column, filename));
                            }
                            else
                            {
                                yield return(new Token(ShiftRight, line, column, filename));
                            }
                        }
                        else
                        {
                            yield return(new Token(GreaterThan, line, column, filename));
                        }
                        break;

                    case '/':
                        if (await InputStream.Peek() == '=')
                        {
                            await InputStream.Read();

                            yield return(new Token(DivideAssign, line, column, filename));
                        }
                        else if (await InputStream.Peek() == '/')
                        {
                            await InputStream.Read();

                            var sb = OutputTrivia ? new StringBuilder() : null;
                            while (!await InputStream.Eof() && await InputStream.Peek() != '\r' && await InputStream.Peek() != '\n')
                            {
                                c = await InputStream.Read();

                                if (OutputTrivia)
                                {
                                    sb !.Append(c);
                                }
                            }
                            if (OutputTrivia)
                            {
                                yield return(new ValueToken <string>(Comment, line, column, filename, sb !.ToString().Trim()));
                            }
                        }
                        else if (await InputStream.Peek() == '*')
                        {
                            await InputStream.Read();

                            var sb         = OutputTrivia ? new StringBuilder() : null;
                            var terminated = false;
                            while (!await InputStream.Eof())
                            {
                                if (await InputStream.Peek() == '*')
                                {
                                    await InputStream.Read();

                                    if (await InputStream.Peek() == '/')
                                    {
                                        await InputStream.Read();

                                        terminated = true;
                                        break;
                                    }
                                    else if (OutputTrivia)
                                    {
                                        sb !.Append('*');
                                    }
                                }
                                else
                                {
                                    c = await InputStream.Read();

                                    if (OutputTrivia)
                                    {
                                        sb !.Append(c);
                                    }
                                }
                            }
                            if (!terminated)
                            {
                                Error("Unterminated comment.");
                            }
                            if (OutputTrivia)
                            {
                                var token = new ValueToken <string>(Comment, line, column, filename, sb !.ToString().Trim());
                                if (!terminated)
                                {
                                    token.IsValid = false;
                                }
                                yield return(token);
                            }
                        }
                        else
                        {
                            yield return(new Token(Slash, line, column, filename));
                        }
                        break;

                    default:
                        yield return(new ValueToken <char>(Terminal.Unknown, line, column, filename, c));

                        Error($"Unexpected symbol {c}");
                        break;
                    }
                }
            }
            yield return(Sentinel);
        }