/* // // 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 #else #include #endif #include #include #include #include "DiagnosticsBase.h" #include "Lexer.h" #include "Token.h" #include "common/mathutil.h" typedef int32_t YYSTYPE; typedef uint32_t UNSIGNED_TYPE; #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($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(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(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 { if ($3 < 0 || $3 > 31) { if (!context->isIgnoringErrors()) { std::ostringstream stream; stream << $1 << " >> " << $3; std::string text = stream.str(); context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT, context->token->location, text.c_str()); *(context->valid) = false; } $$ = static_cast(0); } else if ($1 < 0) { // Logical shift right. $$ = static_cast(static_cast($1) >> $3); } else { $$ = $1 >> $3; } } | expression TOK_OP_LEFT expression { if ($3 < 0 || $3 > 31) { if (!context->isIgnoringErrors()) { std::ostringstream stream; stream << $1 << " << " << $3; std::string text = stream.str(); context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT, context->token->location, text.c_str()); *(context->valid) = false; } $$ = static_cast(0); } else if ($1 < 0) { // Logical shift left. $$ = static_cast(static_cast($1) << $3); } else { $$ = $1 << $3; } } | expression '-' expression { $$ = gl::WrappingDiff($1, $3); } | expression '+' expression { $$ = gl::WrappingSum($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(0); } else if (($1 == std::numeric_limits::min()) && ($3 == -1)) { // Check for the special case where the minimum representable number is // divided by -1. If left alone this has undefined results. $$ = 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(0); } else if (($1 == std::numeric_limits::min()) && ($3 == -1)) { // Check for the special case where the minimum representable number is // divided by -1. If left alone this leads to integer overflow in C++, which // has undefined results. $$ = std::numeric_limits::max(); } else { $$ = $1 / $3; } } | expression '*' expression { $$ = gl::WrappingMul($1, $3); } | '!' expression %prec TOK_UNARY { $$ = ! $2; } | '~' expression %prec TOK_UNARY { $$ = ~ $2; } | '-' expression %prec TOK_UNARY { // Check for negation of minimum representable integer to prevent undefined signed int // overflow. if ($2 == std::numeric_limits::min()) { $$ = std::numeric_limits::min(); } else { $$ = -$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(val); type = TOK_CONST_INT; break; } case pp::Token::IDENTIFIER: *lvalp = static_cast(-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