Edit

kc3-lang/angle/src/compiler/preprocessor/ExpressionParser.y

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2015-09-09 15:07:24
    Hash : 247374cb
    Message : Allow limited form of expressions in #line directives Reuse ExpressionParser that's also used for parsing preprocessor conditionals to parse line and file numbers in #line directives. According to recent Khronos discussions, the intent of the spec is that expressions in #line directives should be interpreted similarly to expressions in conditional directives, so reusing ExpressionParser is a natural way to implement this. This enables simple math operators operating on integers. There are a few unclear corner cases, but this approach is enough to support practical use cases and pass the dEQP tests. Valid line directives have one of the following forms: #line line-expression #line line-expression file-expression ExpressionParser is first run to parse the line-expression. In ambiguous cases the ExpressionParser consumes as much of the line as possible to form line-expression. Then, if end-of-line hasn't been reached, file-expression is parsed by running ExpressionParser again. As an example of an ambiguous case: #line 1 + 2 This could alternatively be interpreted to mean line-expression "1" and file-expression "+ 2" where + is the unary + operator, but ANGLE now interprets it to mean line-expression "1 + 2". Because of these ambiguous cases, a bison grammar that would parse multiple expressions on the same line couldn't be easily constructed, so this solution where ExpressionParser is run twice was chosen instead. The problematic corner cases are: - ExpressionParser uses 64-bit integers internally for evaluating the expression's value. It's possible to interpret the ESSL3 spec so that 32-bit integer wraparound behavior would be required also for #line directive expressions. - It's unclear whether the defined operator can be used in #line expressions. In this patch it is disabled. Hoping for further clarification from Khronos. - It's unclear how short-circuiting should affect the parsing of undefined identifiers in #line expressions. Now it's consistent with #if expressions (undefined identifiers are OK if they're short-circuited). dEQP expectations are updated for preprocessor tests, including ones not affected specifically by this change. BUG=angleproject:989 TEST=angle_unittests, dEQP-GLES3.functional.shaders.preprocessor.* (4 start passing), dEQP-GLES2.functional.shaders.preprocessor.* (4 start passing) Change-Id: I55c5bf75857da5de855cc600d3603ee19399f328 Reviewed-on: https://chromium-review.googlesource.com/300964 Reviewed-by: Jamie Madill <jmadill@chromium.org> Tryjob-Request: Olli Etuaho <oetuaho@nvidia.com> Tested-by: Olli Etuaho <oetuaho@nvidia.com>

  • src/compiler/preprocessor/ExpressionParser.y
  • /*
    //
    // Copyright (c) 2012 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.
    //
    
    This file contains the Yacc grammar for GLSL ES preprocessor expression.
    
    IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh,
    WHICH GENERATES THE GLSL ES preprocessor expression parser.
    */
    
    %{
    //
    // Copyright (c) 2012 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.
    //
    
    // This file is auto-generated by generate_parser.sh. DO NOT EDIT!
    
    #if defined(__GNUC__)
    // Triggered by the auto-generated pplval variable.
    #if !defined(__clang__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
    #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
    #else
    #pragma GCC diagnostic ignored "-Wuninitialized"
    #endif
    #elif defined(_MSC_VER)
    #pragma warning(disable: 4065 4244 4701 4702)
    #endif
    
    #include "ExpressionParser.h"
    
    #if defined(_MSC_VER)
    #include <malloc.h>
    #else
    #include <stdlib.h>
    #endif
    
    #include <cassert>
    #include <sstream>
    
    #include "DiagnosticsBase.h"
    #include "Lexer.h"
    #include "Token.h"
    
    #if defined(_MSC_VER)
    typedef __int64 YYSTYPE;
    #else
    #include <stdint.h>
    typedef intmax_t YYSTYPE;
    #endif  // _MSC_VER
    
    #define YYENABLE_NLS 0
    #define YYLTYPE_IS_TRIVIAL 1
    #define YYSTYPE_IS_TRIVIAL 1
    #define YYSTYPE_IS_DECLARED 1
    
    namespace {
    struct Context
    {
        pp::Diagnostics* diagnostics;
        pp::Lexer* lexer;
        pp::Token* token;
        int* result;
        bool parsePresetToken;
    
        pp::ExpressionParser::ErrorSettings errorSettings;
        bool *valid;
    
        void startIgnoreErrors() { ++ignoreErrors; }
        void endIgnoreErrors() { --ignoreErrors; }
    
        bool isIgnoringErrors() { return ignoreErrors > 0; }
    
        int ignoreErrors;
    };
    }  // namespace
    %}
    
    %pure-parser
    %name-prefix "pp"
    %parse-param {Context *context}
    %lex-param {Context *context}
    
    %{
    static int yylex(YYSTYPE* lvalp, Context* context);
    static void yyerror(Context* context, const char* reason);
    %}
    
    %token TOK_CONST_INT
    %token TOK_IDENTIFIER
    %left TOK_OP_OR
    %left TOK_OP_AND
    %left '|'
    %left '^'
    %left '&'
    %left TOK_OP_EQ TOK_OP_NE
    %left '<' '>' TOK_OP_LE TOK_OP_GE
    %left TOK_OP_LEFT TOK_OP_RIGHT
    %left '+' '-'
    %left '*' '/' '%'
    %right TOK_UNARY
    
    %%
    
    input
        : expression {
            *(context->result) = static_cast<int>($1);
            YYACCEPT;
        }
    ;
    
    expression
        : TOK_CONST_INT
        | TOK_IDENTIFIER {
            if (!context->isIgnoringErrors())
            {
                // This rule should be applied right after the token is lexed, so we can
                // refer to context->token in the error message.
                context->diagnostics->report(context->errorSettings.unexpectedIdentifier,
                                             context->token->location, context->token->text);
                *(context->valid) = false;
            }
            $$ = $1;
        }
        | expression TOK_OP_OR {
            if ($1 != 0)
            {
                // Ignore errors in the short-circuited part of the expression.
                // ESSL3.00 section 3.4:
                // If an operand is not evaluated, the presence of undefined identifiers
                // in the operand will not cause an error.
                // Unevaluated division by zero should not cause an error either.
                context->startIgnoreErrors();
            }
        } expression {
            if ($1 != 0)
            {
                context->endIgnoreErrors();
                $$ = static_cast<YYSTYPE>(1);
            }
            else
            {
                $$ = $1 || $4;
            }
        }
        | expression TOK_OP_AND {
            if ($1 == 0)
            {
                // Ignore errors in the short-circuited part of the expression.
                // ESSL3.00 section 3.4:
                // If an operand is not evaluated, the presence of undefined identifiers
                // in the operand will not cause an error.
                // Unevaluated division by zero should not cause an error either.
                context->startIgnoreErrors();
            }
        } expression {
            if ($1 == 0)
            {
                context->endIgnoreErrors();
                $$ = static_cast<YYSTYPE>(0);
            }
            else
            {
                $$ = $1 && $4;
            }
        }
        | expression '|' expression {
            $$ = $1 | $3;
        }
        | expression '^' expression {
            $$ = $1 ^ $3;
        }
        | expression '&' expression {
            $$ = $1 & $3;
        }
        | expression TOK_OP_NE expression {
            $$ = $1 != $3;
        }
        | expression TOK_OP_EQ expression {
            $$ = $1 == $3;
        }
        | expression TOK_OP_GE expression {
            $$ = $1 >= $3;
        }
        | expression TOK_OP_LE expression {
            $$ = $1 <= $3;
        }
        | expression '>' expression {
            $$ = $1 > $3;
        }
        | expression '<' expression {
            $$ = $1 < $3;
        }
        | expression TOK_OP_RIGHT expression {
            $$ = $1 >> $3;
        }
        | expression TOK_OP_LEFT expression {
            $$ = $1 << $3;
        }
        | expression '-' expression {
            $$ = $1 - $3;
        }
        | expression '+' expression {
            $$ = $1 + $3;
        }
        | expression '%' expression {
            if ($3 == 0)
            {
                if (!context->isIgnoringErrors())
                {
                    std::ostringstream stream;
                    stream << $1 << " % " << $3;
                    std::string text = stream.str();
                    context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
                                                 context->token->location,
                                                 text.c_str());
                    *(context->valid) = false;
                }
                $$ = static_cast<YYSTYPE>(0);
            }
            else
            {
                $$ = $1 % $3;
            }
        }
        | expression '/' expression {
            if ($3 == 0)
            {
                if (!context->isIgnoringErrors())
                {
                    std::ostringstream stream;
                    stream << $1 << " / " << $3;
                    std::string text = stream.str();
                    context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
                                                context->token->location,
                                                text.c_str());
                    *(context->valid) = false;
                }
                $$ = static_cast<YYSTYPE>(0);
            }
            else
            {
                $$ = $1 / $3;
            }
        }
        | expression '*' expression {
            $$ = $1 * $3;
        }
        | '!' expression %prec TOK_UNARY {
            $$ = ! $2;
        }
        | '~' expression %prec TOK_UNARY {
            $$ = ~ $2;
        }
        | '-' expression %prec TOK_UNARY {
            $$ = - $2;
        }
        | '+' expression %prec TOK_UNARY {
            $$ = + $2;
        }
        | '(' expression ')' {
            $$ = $2;
        }
    ;
    
    %%
    
    int yylex(YYSTYPE *lvalp, Context *context)
    {
        pp::Token *token = context->token;
        if (!context->parsePresetToken)
        {
            context->lexer->lex(token);
        }
        context->parsePresetToken = false;
    
        int type = 0;
    
        switch (token->type)
        {
          case pp::Token::CONST_INT: {
            unsigned int val = 0;
            int testVal = 0;
            if (!token->uValue(&val) || (!token->iValue(&testVal) &&
                                         context->errorSettings.integerLiteralsMustFit32BitSignedRange))
            {
                context->diagnostics->report(pp::Diagnostics::PP_INTEGER_OVERFLOW,
                                             token->location, token->text);
                *(context->valid) = false;
            }
            *lvalp = static_cast<YYSTYPE>(val);
            type = TOK_CONST_INT;
            break;
          }
          case pp::Token::IDENTIFIER:
            *lvalp = static_cast<YYSTYPE>(-1);
            type = TOK_IDENTIFIER;
            break;
          case pp::Token::OP_OR:
            type = TOK_OP_OR;
            break;
          case pp::Token::OP_AND:
            type = TOK_OP_AND;
            break;
          case pp::Token::OP_NE:
            type = TOK_OP_NE;
            break;
          case pp::Token::OP_EQ:
            type = TOK_OP_EQ;
            break;
          case pp::Token::OP_GE:
            type = TOK_OP_GE;
            break;
          case pp::Token::OP_LE:
            type = TOK_OP_LE;
            break;
          case pp::Token::OP_RIGHT:
            type = TOK_OP_RIGHT;
            break;
          case pp::Token::OP_LEFT:
            type = TOK_OP_LEFT;
            break;
          case '|':
          case '^':
          case '&':
          case '>':
          case '<':
          case '-':
          case '+':
          case '%':
          case '/':
          case '*':
          case '!':
          case '~':
          case '(':
          case ')':
            type = token->type;
            break;
    
          default:
            break;
        }
    
        return type;
    }
    
    void yyerror(Context *context, const char *reason)
    {
        context->diagnostics->report(pp::Diagnostics::PP_INVALID_EXPRESSION,
                                     context->token->location,
                                     reason);
    }
    
    namespace pp {
    
    ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics)
        : mLexer(lexer),
          mDiagnostics(diagnostics)
    {
    }
    
    bool ExpressionParser::parse(Token *token,
                                 int *result,
                                 bool parsePresetToken,
                                 const ErrorSettings &errorSettings,
                                 bool *valid)
    {
        Context context;
        context.diagnostics = mDiagnostics;
        context.lexer = mLexer;
        context.token = token;
        context.result = result;
        context.ignoreErrors = 0;
        context.parsePresetToken = parsePresetToken;
        context.errorSettings    = errorSettings;
        context.valid            = valid;
        int ret = yyparse(&context);
        switch (ret)
        {
          case 0:
          case 1:
            break;
    
          case 2:
            mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token->location, "");
            break;
    
          default:
            assert(false);
            mDiagnostics->report(Diagnostics::PP_INTERNAL_ERROR, token->location, "");
            break;
        }
    
        return ret == 0;
    }
    
    }  // namespace pp