blob: cb64e034504924db08dd07f92ccdad86d0b7fc18 [file] [log] [blame]
/*
//
// Copyright 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 scripts/run_code_generation.py
WHICH GENERATES THE GLSL ES preprocessor expression parser.
*/
%{
// GENERATED FILE - DO NOT EDIT.
// Generated by generate_parser.py from preprocessor.y
//
// Copyright 2019 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.
//
// preprocessor.y:
// Parser for the OpenGL shading language preprocessor.
// clang-format off
#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
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wunreachable-code"
#endif
#include "ExpressionParser.h"
#if defined(_MSC_VER)
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#include <cassert>
#include <sstream>
#include <stdint.h>
#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
{
angle::pp::Diagnostics *diagnostics;
angle::pp::Lexer *lexer;
angle::pp::Token *token;
int* result;
bool parsePresetToken;
angle::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 {
if ($3 < 0 || $3 > 31)
{
if (!context->isIgnoringErrors())
{
std::ostringstream stream;
stream << $1 << " >> " << $3;
std::string text = stream.str();
context->diagnostics->report(angle::pp::Diagnostics::PP_UNDEFINED_SHIFT,
context->token->location,
text.c_str());
*(context->valid) = false;
}
$$ = static_cast<YYSTYPE>(0);
}
else if ($1 < 0)
{
// Logical shift right.
$$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($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(angle::pp::Diagnostics::PP_UNDEFINED_SHIFT,
context->token->location,
text.c_str());
*(context->valid) = false;
}
$$ = static_cast<YYSTYPE>(0);
}
else
{
// Logical shift left. Casting to unsigned is needed to ensure there's no signed integer
// overflow, which some tools treat as an error.
$$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) << $3);
}
}
| expression '-' expression {
$$ = gl::WrappingDiff<YYSTYPE>($1, $3);
}
| expression '+' expression {
$$ = gl::WrappingSum<YYSTYPE>($1, $3);
}
| expression '%' expression {
if ($3 == 0)
{
if (!context->isIgnoringErrors())
{
std::ostringstream stream;
stream << $1 << " % " << $3;
std::string text = stream.str();
context->diagnostics->report(angle::pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
*(context->valid) = false;
}
$$ = static_cast<YYSTYPE>(0);
}
else if (($1 == std::numeric_limits<YYSTYPE>::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(angle::pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
*(context->valid) = false;
}
$$ = static_cast<YYSTYPE>(0);
}
else if (($1 == std::numeric_limits<YYSTYPE>::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<YYSTYPE>::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<YYSTYPE>::min())
{
$$ = std::numeric_limits<YYSTYPE>::min();
}
else
{
$$ = -$2;
}
}
| '+' expression %prec TOK_UNARY {
$$ = + $2;
}
| '(' expression ')' {
$$ = $2;
}
;
%%
int yylex(YYSTYPE *lvalp, Context *context)
{
angle::pp::Token *token = context->token;
if (!context->parsePresetToken)
{
context->lexer->lex(token);
}
context->parsePresetToken = false;
int type = 0;
switch (token->type)
{
case angle::pp::Token::CONST_INT: {
unsigned int val = 0;
int testVal = 0;
if (!token->uValue(&val) || (!token->iValue(&testVal) &&
context->errorSettings.integerLiteralsMustFit32BitSignedRange))
{
context->diagnostics->report(angle::pp::Diagnostics::PP_INTEGER_OVERFLOW,
token->location, token->text);
*(context->valid) = false;
}
*lvalp = static_cast<YYSTYPE>(val);
type = TOK_CONST_INT;
break;
}
case angle::pp::Token::IDENTIFIER:
*lvalp = static_cast<YYSTYPE>(-1);
type = TOK_IDENTIFIER;
break;
case angle::pp::Token::OP_OR:
type = TOK_OP_OR;
break;
case angle::pp::Token::OP_AND:
type = TOK_OP_AND;
break;
case angle::pp::Token::OP_NE:
type = TOK_OP_NE;
break;
case angle::pp::Token::OP_EQ:
type = TOK_OP_EQ;
break;
case angle::pp::Token::OP_GE:
type = TOK_OP_GE;
break;
case angle::pp::Token::OP_LE:
type = TOK_OP_LE;
break;
case angle::pp::Token::OP_RIGHT:
type = TOK_OP_RIGHT;
break;
case angle::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(angle::pp::Diagnostics::PP_INVALID_EXPRESSION,
context->token->location,
reason);
}
namespace angle {
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
} // namespace angle