Edit

kc3-lang/angle/src/compiler/preprocessor/DirectiveParser.cpp

Branch :

  • Show log

    Commit

  • Author : Corentin Wallez
    Date : 2016-09-20 17:15:59
    Hash : 054f7ed0
    Message : preprocessor: Miscellaneous cleanups - Use full header paths in includes - Use ASSERT instead of assert - Use angle::NonCopyable instead of PP_DISALLOW_COPY_AND_ASSIGN - Use range-for in a couple places - Remove pp_utils.h BUG=angleproject:1522 Change-Id: If107fef89e8465bca65cf664926d1051c5d1e232 Reviewed-on: https://chromium-review.googlesource.com/387212 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/compiler/preprocessor/DirectiveParser.cpp
  • //
    // Copyright (c) 2011-2013 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    #include "compiler/preprocessor/DirectiveParser.h"
    
    #include <algorithm>
    #include <cstdlib>
    #include <sstream>
    
    #include "common/debug.h"
    #include "compiler/preprocessor/DiagnosticsBase.h"
    #include "compiler/preprocessor/DirectiveHandlerBase.h"
    #include "compiler/preprocessor/ExpressionParser.h"
    #include "compiler/preprocessor/MacroExpander.h"
    #include "compiler/preprocessor/Token.h"
    #include "compiler/preprocessor/Tokenizer.h"
    
    namespace {
    enum DirectiveType
    {
        DIRECTIVE_NONE,
        DIRECTIVE_DEFINE,
        DIRECTIVE_UNDEF,
        DIRECTIVE_IF,
        DIRECTIVE_IFDEF,
        DIRECTIVE_IFNDEF,
        DIRECTIVE_ELSE,
        DIRECTIVE_ELIF,
        DIRECTIVE_ENDIF,
        DIRECTIVE_ERROR,
        DIRECTIVE_PRAGMA,
        DIRECTIVE_EXTENSION,
        DIRECTIVE_VERSION,
        DIRECTIVE_LINE
    };
    
    DirectiveType getDirective(const pp::Token *token)
    {
        const char kDirectiveDefine[] = "define";
        const char kDirectiveUndef[] = "undef";
        const char kDirectiveIf[] = "if";
        const char kDirectiveIfdef[] = "ifdef";
        const char kDirectiveIfndef[] = "ifndef";
        const char kDirectiveElse[] = "else";
        const char kDirectiveElif[] = "elif";
        const char kDirectiveEndif[] = "endif";
        const char kDirectiveError[] = "error";
        const char kDirectivePragma[] = "pragma";
        const char kDirectiveExtension[] = "extension";
        const char kDirectiveVersion[] = "version";
        const char kDirectiveLine[] = "line";
    
        if (token->type != pp::Token::IDENTIFIER)
            return DIRECTIVE_NONE;
    
        if (token->text == kDirectiveDefine)
            return DIRECTIVE_DEFINE;
        if (token->text == kDirectiveUndef)
            return DIRECTIVE_UNDEF;
        if (token->text == kDirectiveIf)
            return DIRECTIVE_IF;
        if (token->text == kDirectiveIfdef)
            return DIRECTIVE_IFDEF;
        if (token->text == kDirectiveIfndef)
            return DIRECTIVE_IFNDEF;
        if (token->text == kDirectiveElse)
            return DIRECTIVE_ELSE;
        if (token->text == kDirectiveElif)
            return DIRECTIVE_ELIF;
        if (token->text == kDirectiveEndif)
            return DIRECTIVE_ENDIF;
        if (token->text == kDirectiveError)
            return DIRECTIVE_ERROR;
        if (token->text == kDirectivePragma)
            return DIRECTIVE_PRAGMA;
        if (token->text == kDirectiveExtension)
            return DIRECTIVE_EXTENSION;
        if (token->text == kDirectiveVersion)
            return DIRECTIVE_VERSION;
        if (token->text == kDirectiveLine)
            return DIRECTIVE_LINE;
    
        return DIRECTIVE_NONE;
    }
    
    bool isConditionalDirective(DirectiveType directive)
    {
        switch (directive)
        {
          case DIRECTIVE_IF:
          case DIRECTIVE_IFDEF:
          case DIRECTIVE_IFNDEF:
          case DIRECTIVE_ELSE:
          case DIRECTIVE_ELIF:
          case DIRECTIVE_ENDIF:
            return true;
          default:
            return false;
        }
    }
    
    // Returns true if the token represents End Of Directive.
    bool isEOD(const pp::Token *token)
    {
        return (token->type == '\n') || (token->type == pp::Token::LAST);
    }
    
    void skipUntilEOD(pp::Lexer *lexer, pp::Token *token)
    {
        while(!isEOD(token))
        {
            lexer->lex(token);
        }
    }
    
    bool isMacroNameReserved(const std::string &name)
    {
        // Names prefixed with "GL_" and the name "defined" are reserved.
        return name == "defined" || (name.substr(0, 3) == "GL_");
    }
    
    bool hasDoubleUnderscores(const std::string &name)
    {
        return (name.find("__") != std::string::npos);
    }
    
    bool isMacroPredefined(const std::string &name,
                           const pp::MacroSet &macroSet)
    {
        pp::MacroSet::const_iterator iter = macroSet.find(name);
        return iter != macroSet.end() ? iter->second.predefined : false;
    }
    
    }  // namespace anonymous
    
    namespace pp
    {
    
    class DefinedParser : public Lexer
    {
      public:
        DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics)
            : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics)
        {
        }
    
      protected:
        void lex(Token *token) override
        {
            const char kDefined[] = "defined";
    
            mLexer->lex(token);
            if (token->type != Token::IDENTIFIER)
                return;
            if (token->text != kDefined)
                return;
    
            bool paren = false;
            mLexer->lex(token);
            if (token->type == '(')
            {
                paren = true;
                mLexer->lex(token);
            }
    
            if (token->type != Token::IDENTIFIER)
            {
                mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
                skipUntilEOD(mLexer, token);
                return;
            }
            MacroSet::const_iterator iter = mMacroSet->find(token->text);
            std::string expression        = iter != mMacroSet->end() ? "1" : "0";
    
            if (paren)
            {
                mLexer->lex(token);
                if (token->type != ')')
                {
                    mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
                                         token->text);
                    skipUntilEOD(mLexer, token);
                    return;
                }
            }
    
            // We have a valid defined operator.
            // Convert the current token into a CONST_INT token.
            token->type = Token::CONST_INT;
            token->text = expression;
        }
    
      private:
        Lexer *mLexer;
        const MacroSet *mMacroSet;
        Diagnostics *mDiagnostics;
    };
    
    DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
                                     MacroSet *macroSet,
                                     Diagnostics *diagnostics,
                                     DirectiveHandler *directiveHandler)
        : mPastFirstStatement(false),
          mSeenNonPreprocessorToken(false),
          mTokenizer(tokenizer),
          mMacroSet(macroSet),
          mDiagnostics(diagnostics),
          mDirectiveHandler(directiveHandler),
          mShaderVersion(100)
    {
    }
    
    void DirectiveParser::lex(Token *token)
    {
        do
        {
            mTokenizer->lex(token);
    
            if (token->type == Token::PP_HASH)
            {
                parseDirective(token);
                mPastFirstStatement = true;
            }
            else if (!isEOD(token))
            {
                mSeenNonPreprocessorToken = true;
            }
    
            if (token->type == Token::LAST)
            {
                if (!mConditionalStack.empty())
                {
                    const ConditionalBlock &block = mConditionalStack.back();
                    mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED,
                                         block.location, block.type);
                }
                break;
            }
    
        }
        while (skipping() || (token->type == '\n'));
    
        mPastFirstStatement = true;
    }
    
    void DirectiveParser::parseDirective(Token *token)
    {
        ASSERT(token->type == Token::PP_HASH);
    
        mTokenizer->lex(token);
        if (isEOD(token))
        {
            // Empty Directive.
            return;
        }
    
        DirectiveType directive = getDirective(token);
    
        // While in an excluded conditional block/group,
        // we only parse conditional directives.
        if (skipping() && !isConditionalDirective(directive))
        {
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        switch(directive)
        {
          case DIRECTIVE_NONE:
            mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            break;
          case DIRECTIVE_DEFINE:
            parseDefine(token);
            break;
          case DIRECTIVE_UNDEF:
            parseUndef(token);
            break;
          case DIRECTIVE_IF:
            parseIf(token);
            break;
          case DIRECTIVE_IFDEF:
            parseIfdef(token);
            break;
          case DIRECTIVE_IFNDEF:
            parseIfndef(token);
            break;
          case DIRECTIVE_ELSE:
            parseElse(token);
            break;
          case DIRECTIVE_ELIF:
            parseElif(token);
            break;
          case DIRECTIVE_ENDIF:
            parseEndif(token);
            break;
          case DIRECTIVE_ERROR:
            parseError(token);
            break;
          case DIRECTIVE_PRAGMA:
            parsePragma(token);
            break;
          case DIRECTIVE_EXTENSION:
            parseExtension(token);
            break;
          case DIRECTIVE_VERSION:
            parseVersion(token);
            break;
          case DIRECTIVE_LINE:
            parseLine(token);
            break;
          default:
              UNREACHABLE();
              break;
        }
    
        skipUntilEOD(mTokenizer, token);
        if (token->type == Token::LAST)
        {
            mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE,
                                 token->location, token->text);
        }
    }
    
    void DirectiveParser::parseDefine(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_DEFINE);
    
        mTokenizer->lex(token);
        if (token->type != Token::IDENTIFIER)
        {
            mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            return;
        }
        if (isMacroPredefined(token->text, *mMacroSet))
        {
            mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED,
                                 token->location, token->text);
            return;
        }
        if (isMacroNameReserved(token->text))
        {
            mDiagnostics->report(Diagnostics::PP_MACRO_NAME_RESERVED,
                                 token->location, token->text);
            return;
        }
        // Using double underscores is allowed, but may result in unintended
        // behavior, so a warning is issued. At the time of writing this was
        // specified in ESSL 3.10, but the intent judging from Khronos
        // discussions and dEQP tests was that double underscores should be
        // allowed in earlier ESSL versions too.
        if (hasDoubleUnderscores(token->text))
        {
            mDiagnostics->report(Diagnostics::PP_WARNING_MACRO_NAME_RESERVED, token->location,
                                 token->text);
        }
    
        Macro macro;
        macro.type = Macro::kTypeObj;
        macro.name = token->text;
    
        mTokenizer->lex(token);
        if (token->type == '(' && !token->hasLeadingSpace())
        {
            // Function-like macro. Collect arguments.
            macro.type = Macro::kTypeFunc;
            do
            {
                mTokenizer->lex(token);
                if (token->type != Token::IDENTIFIER)
                    break;
    
                if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) != macro.parameters.end())
                {
                    mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES,
                                         token->location, token->text);
                    return;
                }
    
                macro.parameters.push_back(token->text);
    
                mTokenizer->lex(token);  // Get ','.
            }
            while (token->type == ',');
    
            if (token->type != ')')
            {
                mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                     token->location,
                                     token->text);
                return;
            }
            mTokenizer->lex(token);  // Get ')'.
        }
    
        while ((token->type != '\n') && (token->type != Token::LAST))
        {
            // Reset the token location because it is unnecessary in replacement
            // list. Resetting it also allows us to reuse Token::equals() to
            // compare macros.
            token->location = SourceLocation();
            macro.replacements.push_back(*token);
            mTokenizer->lex(token);
        }
        if (!macro.replacements.empty())
        {
            // Whitespace preceding the replacement list is not considered part of
            // the replacement list for either form of macro.
            macro.replacements.front().setHasLeadingSpace(false);
        }
    
        // Check for macro redefinition.
        MacroSet::const_iterator iter = mMacroSet->find(macro.name);
        if (iter != mMacroSet->end() && !macro.equals(iter->second))
        {
            mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED,
                                 token->location,
                                 macro.name);
            return;
        }
        mMacroSet->insert(std::make_pair(macro.name, macro));
    }
    
    void DirectiveParser::parseUndef(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_UNDEF);
    
        mTokenizer->lex(token);
        if (token->type != Token::IDENTIFIER)
        {
            mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            return;
        }
    
        MacroSet::iterator iter = mMacroSet->find(token->text);
        if (iter != mMacroSet->end())
        {
            if (iter->second.predefined)
            {
                mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED,
                                     token->location, token->text);
                return;
            }
            else if (iter->second.expansionCount > 0)
            {
                mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
                                     token->text);
                return;
            }
            else
            {
                mMacroSet->erase(iter);
            }
        }
    
        mTokenizer->lex(token);
        if (!isEOD(token))
        {
            mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
        }
    }
    
    void DirectiveParser::parseIf(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_IF);
        parseConditionalIf(token);
    }
    
    void DirectiveParser::parseIfdef(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_IFDEF);
        parseConditionalIf(token);
    }
    
    void DirectiveParser::parseIfndef(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_IFNDEF);
        parseConditionalIf(token);
    }
    
    void DirectiveParser::parseElse(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_ELSE);
    
        if (mConditionalStack.empty())
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        ConditionalBlock &block = mConditionalStack.back();
        if (block.skipBlock)
        {
            // No diagnostics. Just skip the whole line.
            skipUntilEOD(mTokenizer, token);
            return;
        }
        if (block.foundElseGroup)
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        block.foundElseGroup = true;
        block.skipGroup = block.foundValidGroup;
        block.foundValidGroup = true;
    
        // Check if there are extra tokens after #else.
        mTokenizer->lex(token);
        if (!isEOD(token))
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
        }
    }
    
    void DirectiveParser::parseElif(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_ELIF);
    
        if (mConditionalStack.empty())
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        ConditionalBlock &block = mConditionalStack.back();
        if (block.skipBlock)
        {
            // No diagnostics. Just skip the whole line.
            skipUntilEOD(mTokenizer, token);
            return;
        }
        if (block.foundElseGroup)
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return;
        }
        if (block.foundValidGroup)
        {
            // Do not parse the expression.
            // Also be careful not to emit a diagnostic.
            block.skipGroup = true;
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        int expression = parseExpressionIf(token);
        block.skipGroup = expression == 0;
        block.foundValidGroup = expression != 0;
    }
    
    void DirectiveParser::parseEndif(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_ENDIF);
    
        if (mConditionalStack.empty())
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        mConditionalStack.pop_back();
    
        // Check if there are tokens after #endif.
        mTokenizer->lex(token);
        if (!isEOD(token))
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
        }
    }
    
    void DirectiveParser::parseError(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_ERROR);
    
        std::ostringstream stream;
        mTokenizer->lex(token);
        while ((token->type != '\n') && (token->type != Token::LAST))
        {
            stream << *token;
            mTokenizer->lex(token);
        }
        mDirectiveHandler->handleError(token->location, stream.str());
    }
    
    // Parses pragma of form: #pragma name[(value)].
    void DirectiveParser::parsePragma(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_PRAGMA);
    
        enum State
        {
            PRAGMA_NAME,
            LEFT_PAREN,
            PRAGMA_VALUE,
            RIGHT_PAREN
        };
    
        bool valid = true;
        std::string name, value;
        int state = PRAGMA_NAME;
    
        mTokenizer->lex(token);
        bool stdgl = token->text == "STDGL";
        if (stdgl)
        {
            mTokenizer->lex(token);
        }
        while ((token->type != '\n') && (token->type != Token::LAST))
        {
            switch(state++)
            {
              case PRAGMA_NAME:
                name = token->text;
                valid = valid && (token->type == Token::IDENTIFIER);
                break;
              case LEFT_PAREN:
                valid = valid && (token->type == '(');
                break;
              case PRAGMA_VALUE:
                value = token->text;
                valid = valid && (token->type == Token::IDENTIFIER);
                break;
              case RIGHT_PAREN:
                valid = valid && (token->type == ')');
                break;
              default:
                valid = false;
                break;
            }
            mTokenizer->lex(token);
        }
    
        valid = valid && ((state == PRAGMA_NAME) ||     // Empty pragma.
                          (state == LEFT_PAREN) ||      // Without value.
                          (state == RIGHT_PAREN + 1));  // With value.
        if (!valid)
        {
            mDiagnostics->report(Diagnostics::PP_UNRECOGNIZED_PRAGMA,
                                 token->location, name);
        }
        else if (state > PRAGMA_NAME)  // Do not notify for empty pragma.
        {
            mDirectiveHandler->handlePragma(token->location, name, value, stdgl);
        }
    }
    
    void DirectiveParser::parseExtension(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_EXTENSION);
    
        enum State
        {
            EXT_NAME,
            COLON,
            EXT_BEHAVIOR
        };
    
        bool valid = true;
        std::string name, behavior;
        int state = EXT_NAME;
    
        mTokenizer->lex(token);
        while ((token->type != '\n') && (token->type != Token::LAST))
        {
            switch (state++)
            {
              case EXT_NAME:
                if (valid && (token->type != Token::IDENTIFIER))
                {
                    mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME,
                                         token->location, token->text);
                    valid = false;
                }
                if (valid) name = token->text;
                break;
              case COLON:
                if (valid && (token->type != ':'))
                {
                    mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                         token->location, token->text);
                    valid = false;
                }
                break;
              case EXT_BEHAVIOR:
                if (valid && (token->type != Token::IDENTIFIER))
                {
                    mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR,
                                         token->location, token->text);
                    valid = false;
                }
                if (valid) behavior = token->text;
                break;
              default:
                if (valid)
                {
                    mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                         token->location, token->text);
                    valid = false;
                }
                break;
            }
            mTokenizer->lex(token);
        }
        if (valid && (state != EXT_BEHAVIOR + 1))
        {
            mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_DIRECTIVE,
                                 token->location, token->text);
            valid = false;
        }
        if (valid && mSeenNonPreprocessorToken)
        {
            if (mShaderVersion >= 300)
            {
                mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3,
                                     token->location, token->text);
                valid = false;
            }
            else
            {
                mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1,
                                     token->location, token->text);
            }
        }
        if (valid)
            mDirectiveHandler->handleExtension(token->location, name, behavior);
    }
    
    void DirectiveParser::parseVersion(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_VERSION);
    
        if (mPastFirstStatement)
        {
            mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return;
        }
    
        enum State
        {
            VERSION_NUMBER,
            VERSION_PROFILE,
            VERSION_ENDLINE
        };
    
        bool valid = true;
        int version = 0;
        int state = VERSION_NUMBER;
    
        mTokenizer->lex(token);
        while (valid && (token->type != '\n') && (token->type != Token::LAST))
        {
            switch (state)
            {
              case VERSION_NUMBER:
                if (token->type != Token::CONST_INT)
                {
                    mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER,
                                         token->location, token->text);
                    valid = false;
                }
                if (valid && !token->iValue(&version))
                {
                    mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW,
                                         token->location, token->text);
                    valid = false;
                }
                if (valid)
                {
                    state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE;
                }
                break;
              case VERSION_PROFILE:
                if (token->type != Token::IDENTIFIER || token->text != "es")
                {
                    mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE,
                                         token->location, token->text);
                    valid = false;
                }
                state = VERSION_ENDLINE;
                break;
              default:
                mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                     token->location, token->text);
                valid = false;
                break;
            }
    
            mTokenizer->lex(token);
        }
    
        if (valid && (state != VERSION_ENDLINE))
        {
            mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE,
                                 token->location, token->text);
            valid = false;
        }
    
        if (valid && version >= 300 && token->location.line > 1)
        {
            mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3,
                                 token->location, token->text);
            valid = false;
        }
    
        if (valid)
        {
            mDirectiveHandler->handleVersion(token->location, version);
            mShaderVersion = version;
            PredefineMacro(mMacroSet, "__VERSION__", version);
        }
    }
    
    void DirectiveParser::parseLine(Token *token)
    {
        ASSERT(getDirective(token) == DIRECTIVE_LINE);
    
        bool valid = true;
        bool parsedFileNumber = false;
        int line = 0, file = 0;
    
        MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics);
    
        // Lex the first token after "#line" so we can check it for EOD.
        macroExpander.lex(token);
    
        if (isEOD(token))
        {
            mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text);
            valid = false;
        }
        else
        {
            ExpressionParser expressionParser(&macroExpander, mDiagnostics);
            ExpressionParser::ErrorSettings errorSettings;
    
            // See GLES3 section 12.42
            errorSettings.integerLiteralsMustFit32BitSignedRange = true;
    
            errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER;
            // The first token was lexed earlier to check if it was EOD. Include
            // the token in parsing for a second time by setting the
            // parsePresetToken flag to true.
            expressionParser.parse(token, &line, true, errorSettings, &valid);
            if (!isEOD(token) && valid)
            {
                errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER;
                // After parsing the line expression expressionParser has also
                // advanced to the first token of the file expression - this is the
                // token that makes the parser reduce the "input" rule for the line
                // expression and stop. So we're using parsePresetToken = true here
                // as well.
                expressionParser.parse(token, &file, true, errorSettings, &valid);
                parsedFileNumber = true;
            }
            if (!isEOD(token))
            {
                if (valid)
                {
                    mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                         token->location, token->text);
                    valid = false;
                }
                skipUntilEOD(mTokenizer, token);
            }
        }
    
        if (valid)
        {
            mTokenizer->setLineNumber(line);
            if (parsedFileNumber)
                mTokenizer->setFileNumber(file);
        }
    }
    
    bool DirectiveParser::skipping() const
    {
        if (mConditionalStack.empty())
            return false;
    
        const ConditionalBlock& block = mConditionalStack.back();
        return block.skipBlock || block.skipGroup;
    }
    
    void DirectiveParser::parseConditionalIf(Token *token)
    {
        ConditionalBlock block;
        block.type = token->text;
        block.location = token->location;
    
        if (skipping())
        {
            // This conditional block is inside another conditional group
            // which is skipped. As a consequence this whole block is skipped.
            // Be careful not to parse the conditional expression that might
            // emit a diagnostic.
            skipUntilEOD(mTokenizer, token);
            block.skipBlock = true;
        }
        else
        {
            DirectiveType directive = getDirective(token);
    
            int expression = 0;
            switch (directive)
            {
              case DIRECTIVE_IF:
                expression = parseExpressionIf(token);
                break;
              case DIRECTIVE_IFDEF:
                expression = parseExpressionIfdef(token);
                break;
              case DIRECTIVE_IFNDEF:
                expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
                break;
              default:
                  UNREACHABLE();
                  break;
            }
            block.skipGroup = expression == 0;
            block.foundValidGroup = expression != 0;
        }
        mConditionalStack.push_back(block);
    }
    
    int DirectiveParser::parseExpressionIf(Token *token)
    {
        ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF));
    
        DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics);
        MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics);
        ExpressionParser expressionParser(&macroExpander, mDiagnostics);
    
        int expression = 0;
        ExpressionParser::ErrorSettings errorSettings;
        errorSettings.integerLiteralsMustFit32BitSignedRange = false;
        errorSettings.unexpectedIdentifier                   = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN;
    
        bool valid = true;
        expressionParser.parse(token, &expression, false, errorSettings, &valid);
    
        // Check if there are tokens after #if expression.
        if (!isEOD(token))
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
        }
    
        return expression;
    }
    
    int DirectiveParser::parseExpressionIfdef(Token *token)
    {
        ASSERT((getDirective(token) == DIRECTIVE_IFDEF) || (getDirective(token) == DIRECTIVE_IFNDEF));
    
        mTokenizer->lex(token);
        if (token->type != Token::IDENTIFIER)
        {
            mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
            return 0;
        }
    
        MacroSet::const_iterator iter = mMacroSet->find(token->text);
        int expression = iter != mMacroSet->end() ? 1 : 0;
    
        // Check if there are tokens after #ifdef expression.
        mTokenizer->lex(token);
        if (!isEOD(token))
        {
            mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                 token->location, token->text);
            skipUntilEOD(mTokenizer, token);
        }
        return expression;
    }
    
    }  // namespace pp