1 /*
2 //
3 // Copyright 2012 The ANGLE Project Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6 //
7
8 This file contains the Yacc grammar for GLSL ES preprocessor expression.
9
10 IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN scripts/run_code_generation.py
11 WHICH GENERATES THE GLSL ES preprocessor expression parser.
12 */
13
14 %{
15 // GENERATED FILE - DO NOT EDIT.
16 // Generated by generate_parser.py from preprocessor.y
17 //
18 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
19 // Use of this source code is governed by a BSD-style license that can be
20 // found in the LICENSE file.
21 //
22 // preprocessor.y:
23 // Parser for the OpenGL shading language preprocessor.
24
25 #if defined(__GNUC__)
26 // Triggered by the auto-generated pplval variable.
27 #if !defined(__clang__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
28 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
29 #else
30 #pragma GCC diagnostic ignored "-Wuninitialized"
31 #endif
32 #elif defined(_MSC_VER)
33 #pragma warning(disable: 4065 4244 4701 4702)
34 #endif
35 #if defined(__clang__)
36 #pragma clang diagnostic ignored "-Wunreachable-code"
37 #endif
38
39 #include "ExpressionParser.h"
40
41 #if defined(_MSC_VER)
42 #include <malloc.h>
43 #else
44 #include <stdlib.h>
45 #endif
46
47 #include <cassert>
48 #include <sstream>
49 #include <stdint.h>
50
51 #include "DiagnosticsBase.h"
52 #include "Lexer.h"
53 #include "Token.h"
54 #include "common/mathutil.h"
55
56 typedef int32_t YYSTYPE;
57 typedef uint32_t UNSIGNED_TYPE;
58
59 #define YYENABLE_NLS 0
60 #define YYLTYPE_IS_TRIVIAL 1
61 #define YYSTYPE_IS_TRIVIAL 1
62 #define YYSTYPE_IS_DECLARED 1
63
64 namespace {
65 struct Context
66 {
67 angle::pp::Diagnostics *diagnostics;
68 angle::pp::Lexer *lexer;
69 angle::pp::Token *token;
70 int* result;
71 bool parsePresetToken;
72
73 angle::pp::ExpressionParser::ErrorSettings errorSettings;
74 bool *valid;
75
startIgnoreErrorsContext76 void startIgnoreErrors() { ++ignoreErrors; }
endIgnoreErrorsContext77 void endIgnoreErrors() { --ignoreErrors; }
78
isIgnoringErrorsContext79 bool isIgnoringErrors() { return ignoreErrors > 0; }
80
81 int ignoreErrors;
82 };
83 } // namespace
84 %}
85
86 %pure-parser
87 %name-prefix "pp"
88 %parse-param {Context *context}
89 %lex-param {Context *context}
90
91 %{
92 static int yylex(YYSTYPE* lvalp, Context* context);
93 static void yyerror(Context* context, const char* reason);
94 %}
95
96 %token TOK_CONST_INT
97 %token TOK_IDENTIFIER
98 %left TOK_OP_OR
99 %left TOK_OP_AND
100 %left '|'
101 %left '^'
102 %left '&'
103 %left TOK_OP_EQ TOK_OP_NE
104 %left '<' '>' TOK_OP_LE TOK_OP_GE
105 %left TOK_OP_LEFT TOK_OP_RIGHT
106 %left '+' '-'
107 %left '*' '/' '%'
108 %right TOK_UNARY
109
110 %%
111
112 input
113 : expression {
114 *(context->result) = static_cast<int>($1);
115 YYACCEPT;
116 }
117 ;
118
119 expression
120 : TOK_CONST_INT
121 | TOK_IDENTIFIER {
122 if (!context->isIgnoringErrors())
123 {
124 // This rule should be applied right after the token is lexed, so we can
125 // refer to context->token in the error message.
126 context->diagnostics->report(context->errorSettings.unexpectedIdentifier,
127 context->token->location, context->token->text);
128 *(context->valid) = false;
129 }
130 $$ = $1;
131 }
132 | expression TOK_OP_OR {
133 if ($1 != 0)
134 {
135 // Ignore errors in the short-circuited part of the expression.
136 // ESSL3.00 section 3.4:
137 // If an operand is not evaluated, the presence of undefined identifiers
138 // in the operand will not cause an error.
139 // Unevaluated division by zero should not cause an error either.
140 context->startIgnoreErrors();
141 }
142 } expression {
143 if ($1 != 0)
144 {
145 context->endIgnoreErrors();
146 $$ = static_cast<YYSTYPE>(1);
147 }
148 else
149 {
150 $$ = $1 || $4;
151 }
152 }
153 | expression TOK_OP_AND {
154 if ($1 == 0)
155 {
156 // Ignore errors in the short-circuited part of the expression.
157 // ESSL3.00 section 3.4:
158 // If an operand is not evaluated, the presence of undefined identifiers
159 // in the operand will not cause an error.
160 // Unevaluated division by zero should not cause an error either.
161 context->startIgnoreErrors();
162 }
163 } expression {
164 if ($1 == 0)
165 {
166 context->endIgnoreErrors();
167 $$ = static_cast<YYSTYPE>(0);
168 }
169 else
170 {
171 $$ = $1 && $4;
172 }
173 }
174 | expression '|' expression {
175 $$ = $1 | $3;
176 }
177 | expression '^' expression {
178 $$ = $1 ^ $3;
179 }
180 | expression '&' expression {
181 $$ = $1 & $3;
182 }
183 | expression TOK_OP_NE expression {
184 $$ = $1 != $3;
185 }
186 | expression TOK_OP_EQ expression {
187 $$ = $1 == $3;
188 }
189 | expression TOK_OP_GE expression {
190 $$ = $1 >= $3;
191 }
192 | expression TOK_OP_LE expression {
193 $$ = $1 <= $3;
194 }
195 | expression '>' expression {
196 $$ = $1 > $3;
197 }
198 | expression '<' expression {
199 $$ = $1 < $3;
200 }
201 | expression TOK_OP_RIGHT expression {
202 if ($3 < 0 || $3 > 31)
203 {
204 if (!context->isIgnoringErrors())
205 {
206 std::ostringstream stream;
207 stream << $1 << " >> " << $3;
208 std::string text = stream.str();
209 context->diagnostics->report(angle::pp::Diagnostics::PP_UNDEFINED_SHIFT,
210 context->token->location,
211 text.c_str());
212 *(context->valid) = false;
213 }
214 $$ = static_cast<YYSTYPE>(0);
215 }
216 else if ($1 < 0)
217 {
218 // Logical shift right.
219 $$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) >> $3);
220 }
221 else
222 {
223 $$ = $1 >> $3;
224 }
225 }
226 | expression TOK_OP_LEFT expression {
227 if ($3 < 0 || $3 > 31)
228 {
229 if (!context->isIgnoringErrors())
230 {
231 std::ostringstream stream;
232 stream << $1 << " << " << $3;
233 std::string text = stream.str();
234 context->diagnostics->report(angle::pp::Diagnostics::PP_UNDEFINED_SHIFT,
235 context->token->location,
236 text.c_str());
237 *(context->valid) = false;
238 }
239 $$ = static_cast<YYSTYPE>(0);
240 }
241 else
242 {
243 // Logical shift left. Casting to unsigned is needed to ensure there's no signed integer
244 // overflow, which some tools treat as an error.
245 $$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) << $3);
246 }
247 }
248 | expression '-' expression {
249 $$ = gl::WrappingDiff<YYSTYPE>($1, $3);
250 }
251 | expression '+' expression {
252 $$ = gl::WrappingSum<YYSTYPE>($1, $3);
253 }
254 | expression '%' expression {
255 if ($3 == 0)
256 {
257 if (!context->isIgnoringErrors())
258 {
259 std::ostringstream stream;
260 stream << $1 << " % " << $3;
261 std::string text = stream.str();
262 context->diagnostics->report(angle::pp::Diagnostics::PP_DIVISION_BY_ZERO,
263 context->token->location,
264 text.c_str());
265 *(context->valid) = false;
266 }
267 $$ = static_cast<YYSTYPE>(0);
268 }
269 else if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
270 {
271 // Check for the special case where the minimum representable number is
272 // divided by -1. If left alone this has undefined results.
273 $$ = 0;
274 }
275 else
276 {
277 $$ = $1 % $3;
278 }
279 }
280 | expression '/' expression {
281 if ($3 == 0)
282 {
283 if (!context->isIgnoringErrors())
284 {
285 std::ostringstream stream;
286 stream << $1 << " / " << $3;
287 std::string text = stream.str();
288 context->diagnostics->report(angle::pp::Diagnostics::PP_DIVISION_BY_ZERO,
289 context->token->location,
290 text.c_str());
291 *(context->valid) = false;
292 }
293 $$ = static_cast<YYSTYPE>(0);
294 }
295 else if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
296 {
297 // Check for the special case where the minimum representable number is
298 // divided by -1. If left alone this leads to integer overflow in C++, which
299 // has undefined results.
300 $$ = std::numeric_limits<YYSTYPE>::max();
301 }
302 else
303 {
304 $$ = $1 / $3;
305 }
306 }
307 | expression '*' expression {
308 $$ = gl::WrappingMul($1, $3);
309 }
310 | '!' expression %prec TOK_UNARY {
311 $$ = ! $2;
312 }
313 | '~' expression %prec TOK_UNARY {
314 $$ = ~ $2;
315 }
316 | '-' expression %prec TOK_UNARY {
317 // Check for negation of minimum representable integer to prevent undefined signed int
318 // overflow.
319 if ($2 == std::numeric_limits<YYSTYPE>::min())
320 {
321 $$ = std::numeric_limits<YYSTYPE>::min();
322 }
323 else
324 {
325 $$ = -$2;
326 }
327 }
328 | '+' expression %prec TOK_UNARY {
329 $$ = + $2;
330 }
331 | '(' expression ')' {
332 $$ = $2;
333 }
334 ;
335
336 %%
337
338 int yylex(YYSTYPE *lvalp, Context *context)
339 {
340 angle::pp::Token *token = context->token;
341 if (!context->parsePresetToken)
342 {
343 context->lexer->lex(token);
344 }
345 context->parsePresetToken = false;
346
347 int type = 0;
348
349 switch (token->type)
350 {
351 case angle::pp::Token::CONST_INT: {
352 unsigned int val = 0;
353 int testVal = 0;
354 if (!token->uValue(&val) || (!token->iValue(&testVal) &&
355 context->errorSettings.integerLiteralsMustFit32BitSignedRange))
356 {
357 context->diagnostics->report(angle::pp::Diagnostics::PP_INTEGER_OVERFLOW,
358 token->location, token->text);
359 *(context->valid) = false;
360 }
361 *lvalp = static_cast<YYSTYPE>(val);
362 type = TOK_CONST_INT;
363 break;
364 }
365 case angle::pp::Token::IDENTIFIER:
366 *lvalp = static_cast<YYSTYPE>(-1);
367 type = TOK_IDENTIFIER;
368 break;
369 case angle::pp::Token::OP_OR:
370 type = TOK_OP_OR;
371 break;
372 case angle::pp::Token::OP_AND:
373 type = TOK_OP_AND;
374 break;
375 case angle::pp::Token::OP_NE:
376 type = TOK_OP_NE;
377 break;
378 case angle::pp::Token::OP_EQ:
379 type = TOK_OP_EQ;
380 break;
381 case angle::pp::Token::OP_GE:
382 type = TOK_OP_GE;
383 break;
384 case angle::pp::Token::OP_LE:
385 type = TOK_OP_LE;
386 break;
387 case angle::pp::Token::OP_RIGHT:
388 type = TOK_OP_RIGHT;
389 break;
390 case angle::pp::Token::OP_LEFT:
391 type = TOK_OP_LEFT;
392 break;
393 case '|':
394 case '^':
395 case '&':
396 case '>':
397 case '<':
398 case '-':
399 case '+':
400 case '%':
401 case '/':
402 case '*':
403 case '!':
404 case '~':
405 case '(':
406 case ')':
407 type = token->type;
408 break;
409
410 default:
411 break;
412 }
413
414 return type;
415 }
416
yyerror(Context * context,const char * reason)417 void yyerror(Context *context, const char *reason)
418 {
419 context->diagnostics->report(angle::pp::Diagnostics::PP_INVALID_EXPRESSION,
420 context->token->location,
421 reason);
422 }
423
424 namespace angle {
425
426 namespace pp {
427
ExpressionParser(Lexer * lexer,Diagnostics * diagnostics)428 ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics)
429 : mLexer(lexer),
430 mDiagnostics(diagnostics)
431 {
432 }
433
parse(Token * token,int * result,bool parsePresetToken,const ErrorSettings & errorSettings,bool * valid)434 bool ExpressionParser::parse(Token *token,
435 int *result,
436 bool parsePresetToken,
437 const ErrorSettings &errorSettings,
438 bool *valid)
439 {
440 Context context;
441 context.diagnostics = mDiagnostics;
442 context.lexer = mLexer;
443 context.token = token;
444 context.result = result;
445 context.ignoreErrors = 0;
446 context.parsePresetToken = parsePresetToken;
447 context.errorSettings = errorSettings;
448 context.valid = valid;
449 int ret = yyparse(&context);
450 switch (ret)
451 {
452 case 0:
453 case 1:
454 break;
455
456 case 2:
457 mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token->location, "");
458 break;
459
460 default:
461 assert(false);
462 mDiagnostics->report(Diagnostics::PP_INTERNAL_ERROR, token->location, "");
463 break;
464 }
465
466 return ret == 0;
467 }
468
469 } // namespace pp
470
471 } // namespace angle
472