• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
3 // Copyright (C) 2013 LunarG, Inc.
4 // Copyright (C) 2017 ARM Limited.
5 // Copyright (C) 2015-2018 Google, Inc.
6 //
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions
11 // are met:
12 //
13 //    Redistributions of source code must retain the above copyright
14 //    notice, this list of conditions and the following disclaimer.
15 //
16 //    Redistributions in binary form must reproduce the above
17 //    copyright notice, this list of conditions and the following
18 //    disclaimer in the documentation and/or other materials provided
19 //    with the distribution.
20 //
21 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
22 //    contributors may be used to endorse or promote products derived
23 //    from this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 // POSSIBILITY OF SUCH DAMAGE.
37 //
38 /****************************************************************************\
39 Copyright (c) 2002, NVIDIA Corporation.
40 
41 NVIDIA Corporation("NVIDIA") supplies this software to you in
42 consideration of your agreement to the following terms, and your use,
43 installation, modification or redistribution of this NVIDIA software
44 constitutes acceptance of these terms.  If you do not agree with these
45 terms, please do not use, install, modify or redistribute this NVIDIA
46 software.
47 
48 In consideration of your agreement to abide by the following terms, and
49 subject to these terms, NVIDIA grants you a personal, non-exclusive
50 license, under NVIDIA's copyrights in this original NVIDIA software (the
51 "NVIDIA Software"), to use, reproduce, modify and redistribute the
52 NVIDIA Software, with or without modifications, in source and/or binary
53 forms; provided that if you redistribute the NVIDIA Software, you must
54 retain the copyright notice of NVIDIA, this notice and the following
55 text and disclaimers in all such redistributions of the NVIDIA Software.
56 Neither the name, trademarks, service marks nor logos of NVIDIA
57 Corporation may be used to endorse or promote products derived from the
58 NVIDIA Software without specific prior written permission from NVIDIA.
59 Except as expressly stated in this notice, no other rights or licenses
60 express or implied, are granted by NVIDIA herein, including but not
61 limited to any patent rights that may be infringed by your derivative
62 works or by other works in which the NVIDIA Software may be
63 incorporated. No hardware is licensed hereunder.
64 
65 THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
66 WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
67 INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
68 NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
69 ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
70 PRODUCTS.
71 
72 IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
73 INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
74 TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
75 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
76 OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
77 NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
78 TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
79 NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
80 \****************************************************************************/
81 
82 #ifndef _CRT_SECURE_NO_WARNINGS
83 #define _CRT_SECURE_NO_WARNINGS
84 #endif
85 
86 #include <cstdlib>
87 #include <cstring>
88 
89 #include "PpContext.h"
90 #include "PpTokens.h"
91 #include "../Scan.h"
92 
93 namespace glslang {
94 
95 ///////////////////////////////////////////////////////////////////////////////////////////////
96 /////////////////////////////////// Floating point constants: /////////////////////////////////
97 ///////////////////////////////////////////////////////////////////////////////////////////////
98 
99 /*
100 * lFloatConst() - Scan a single- or double-precision floating point constant.  Assumes that the scanner
101 *         has seen at least one digit, followed by either a decimal '.' or the
102 *         letter 'e', or a precision ending (e.g., F or LF).
103 */
104 
lFloatConst(int len,int ch,TPpToken * ppToken)105 int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken)
106 {
107     const auto saveName = [&](int ch) {
108         if (len <= MaxTokenLength)
109             ppToken->name[len++] = static_cast<char>(ch);
110     };
111 
112     // find the range of non-zero digits before the decimal point
113     int startNonZero = 0;
114     while (startNonZero < len && ppToken->name[startNonZero] == '0')
115         ++startNonZero;
116     int endNonZero = len;
117     while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0')
118         --endNonZero;
119     int numWholeNumberDigits = endNonZero - startNonZero;
120 
121     // accumulate the range's value
122     bool fastPath = numWholeNumberDigits <= 15;  // when the number gets too complex, set to false
123     unsigned long long wholeNumber = 0;
124     if (fastPath) {
125         for (int i = startNonZero; i < endNonZero; ++i)
126             wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0');
127     }
128     int decimalShift = len - endNonZero;
129 
130     // Decimal point:
131     bool hasDecimalOrExponent = false;
132     if (ch == '.') {
133         hasDecimalOrExponent = true;
134         saveName(ch);
135         ch = getChar();
136         int firstDecimal = len;
137 
138         // 1.#INF or -1.#INF
139         if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) {
140             if ((len <  2) ||
141                 (len == 2 && ppToken->name[0] != '1') ||
142                 (len == 3 && ppToken->name[1] != '1' && !(ppToken->name[0] == '-' || ppToken->name[0] == '+')) ||
143                 (len >  3))
144                 parseContext.ppError(ppToken->loc, "unexpected use of", "#", "");
145             else {
146                 // we have 1.# or -1.# or +1.#, check for 'INF'
147                 if ((ch = getChar()) != 'I' ||
148                     (ch = getChar()) != 'N' ||
149                     (ch = getChar()) != 'F')
150                     parseContext.ppError(ppToken->loc, "expected 'INF'", "#", "");
151                 else {
152                     // we have [+-].#INF, and we are targeting IEEE 754, so wrap it up:
153                     saveName('I');
154                     saveName('N');
155                     saveName('F');
156                     ppToken->name[len] = '\0';
157                     if (ppToken->name[0] == '-')
158                         ppToken->i64val = 0xfff0000000000000; // -Infinity
159                     else
160                         ppToken->i64val = 0x7ff0000000000000; // +Infinity
161                     return PpAtomConstFloat;
162                 }
163             }
164         }
165 
166         // Consume leading-zero digits after the decimal point
167         while (ch == '0') {
168             saveName(ch);
169             ch = getChar();
170         }
171         int startNonZeroDecimal = len;
172         int endNonZeroDecimal = len;
173 
174         // Consume remaining digits, up to the exponent
175         while (ch >= '0' && ch <= '9') {
176             saveName(ch);
177             if (ch != '0')
178                 endNonZeroDecimal = len;
179             ch = getChar();
180         }
181 
182         // Compute accumulation up to the last non-zero digit
183         if (endNonZeroDecimal > startNonZeroDecimal) {
184             numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "."
185             if (numWholeNumberDigits > 15)
186                 fastPath = false;
187             if (fastPath) {
188                 for (int i = endNonZero; i < endNonZeroDecimal; ++i) {
189                     if (ppToken->name[i] != '.')
190                         wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0');
191                 }
192             }
193             decimalShift = firstDecimal - endNonZeroDecimal;
194         }
195     }
196 
197     // Exponent:
198     bool negativeExponent = false;
199     double exponentValue = 0.0;
200     int exponent = 0;
201     {
202         if (ch == 'e' || ch == 'E') {
203             hasDecimalOrExponent = true;
204             saveName(ch);
205             ch = getChar();
206             if (ch == '+' || ch == '-') {
207                 negativeExponent = ch == '-';
208                 saveName(ch);
209                 ch = getChar();
210             }
211             if (ch >= '0' && ch <= '9') {
212                 while (ch >= '0' && ch <= '9') {
213                     exponent = exponent * 10 + (ch - '0');
214                     saveName(ch);
215                     ch = getChar();
216                 }
217             } else {
218                 parseContext.ppError(ppToken->loc, "bad character in float exponent", "", "");
219             }
220         }
221 
222         // Compensate for location of decimal
223         if (negativeExponent)
224             exponent -= decimalShift;
225         else {
226             exponent += decimalShift;
227             if (exponent < 0) {
228                 negativeExponent = true;
229                 exponent = -exponent;
230             }
231         }
232         if (exponent > 22)
233             fastPath = false;
234 
235         if (fastPath) {
236             // Compute the floating-point value of the exponent
237             exponentValue = 1.0;
238             if (exponent > 0) {
239                 double expFactor = 10;
240                 while (exponent > 0) {
241                     if (exponent & 0x1)
242                         exponentValue *= expFactor;
243                     expFactor *= expFactor;
244                     exponent >>= 1;
245                 }
246             }
247         }
248     }
249 
250     // Suffix:
251     bool isDouble = false;
252     bool isFloat16 = false;
253     if (ch == 'l' || ch == 'L') {
254         if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl)
255             parseContext.doubleCheck(ppToken->loc, "double floating-point suffix");
256         if (ifdepth == 0 && !hasDecimalOrExponent)
257             parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
258         if (parseContext.intermediate.getSource() == EShSourceGlsl) {
259             int ch2 = getChar();
260             if (ch2 != 'f' && ch2 != 'F') {
261                 ungetChar();
262                 ungetChar();
263             } else {
264                 saveName(ch);
265                 saveName(ch2);
266                 isDouble = true;
267             }
268         } else if (parseContext.intermediate.getSource() == EShSourceHlsl) {
269             saveName(ch);
270             isDouble = true;
271         }
272     } else if (ch == 'h' || ch == 'H') {
273         if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl)
274             parseContext.float16Check(ppToken->loc, "half floating-point suffix");
275         if (ifdepth == 0 && !hasDecimalOrExponent)
276             parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
277         if (parseContext.intermediate.getSource() == EShSourceGlsl) {
278             int ch2 = getChar();
279             if (ch2 != 'f' && ch2 != 'F') {
280                 ungetChar();
281                 ungetChar();
282             } else {
283                 saveName(ch);
284                 saveName(ch2);
285                 isFloat16 = true;
286             }
287         } else if (parseContext.intermediate.getSource() == EShSourceHlsl) {
288             saveName(ch);
289             isFloat16 = true;
290         }
291     } else if (ch == 'f' || ch == 'F') {
292         if (ifdepth == 0)
293             parseContext.profileRequires(ppToken->loc,  EEsProfile, 300, nullptr, "floating-point suffix");
294         if (ifdepth == 0 && !parseContext.relaxedErrors())
295             parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix");
296         if (ifdepth == 0 && !hasDecimalOrExponent)
297             parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
298         saveName(ch);
299     } else
300         ungetChar();
301 
302     // Patch up the name and length for overflow
303 
304     if (len > MaxTokenLength) {
305         len = MaxTokenLength;
306         parseContext.ppError(ppToken->loc, "float literal too long", "", "");
307     }
308     ppToken->name[len] = '\0';
309 
310     // Compute the numerical value
311     if (fastPath) {
312         // compute the floating-point value of the exponent
313         if (exponentValue == 0.0)
314             ppToken->dval = (double)wholeNumber;
315         else if (negativeExponent)
316             ppToken->dval = (double)wholeNumber / exponentValue;
317         else
318             ppToken->dval = (double)wholeNumber * exponentValue;
319     } else {
320         // slow path
321         ppToken->dval = 0.0;
322 
323         // remove suffix
324         TString numstr(ppToken->name);
325         if (numstr.back() == 'f' || numstr.back() == 'F')
326             numstr.pop_back();
327         if (numstr.back() == 'h' || numstr.back() == 'H')
328             numstr.pop_back();
329         if (numstr.back() == 'l' || numstr.back() == 'L')
330             numstr.pop_back();
331 
332         // use platform library
333         strtodStream.clear();
334         strtodStream.str(numstr.c_str());
335         strtodStream >> ppToken->dval;
336         if (strtodStream.fail()) {
337             // Assume failure combined with a large exponent was overflow, in
338             // an attempt to set INF.
339             if (!negativeExponent && exponent + numWholeNumberDigits > 300)
340                 ppToken->i64val = 0x7ff0000000000000; // +Infinity
341             // Assume failure combined with a small exponent was overflow.
342             if (negativeExponent && exponent + numWholeNumberDigits > 300)
343                 ppToken->dval = 0.0;
344             // Unknown reason for failure. Theory is that either
345             //  - the 0.0 is still there, or
346             //  - something reasonable was written that is better than 0.0
347         }
348     }
349 
350     // Return the right token type
351     if (isDouble)
352         return PpAtomConstDouble;
353     else if (isFloat16)
354         return PpAtomConstFloat16;
355     else
356         return PpAtomConstFloat;
357 }
358 
359 // Recognize a character literal.
360 //
361 // The first ' has already been accepted, read the rest, through the closing '.
362 //
363 // Always returns PpAtomConstInt.
364 //
characterLiteral(TPpToken * ppToken)365 int TPpContext::characterLiteral(TPpToken* ppToken)
366 {
367     ppToken->name[0] = 0;
368     ppToken->ival = 0;
369 
370     if (parseContext.intermediate.getSource() != EShSourceHlsl) {
371         // illegal, except in macro definition, for which case we report the character
372         return '\'';
373     }
374 
375     int ch = getChar();
376     switch (ch) {
377     case '\'':
378         // As empty sequence:  ''
379         parseContext.ppError(ppToken->loc, "unexpected", "\'", "");
380         return PpAtomConstInt;
381     case '\\':
382         // As escape sequence:  '\XXX'
383         switch (ch = getChar()) {
384         case 'a':
385             ppToken->ival = 7;
386             break;
387         case 'b':
388             ppToken->ival = 8;
389             break;
390         case 't':
391             ppToken->ival = 9;
392             break;
393         case 'n':
394             ppToken->ival = 10;
395             break;
396         case 'v':
397             ppToken->ival = 11;
398             break;
399         case 'f':
400             ppToken->ival = 12;
401             break;
402         case 'r':
403             ppToken->ival = 13;
404             break;
405         case 'x':
406         case '0':
407             parseContext.ppError(ppToken->loc, "octal and hex sequences not supported", "\\", "");
408             break;
409         default:
410             // This catches '\'', '\"', '\?', etc.
411             // Also, things like '\C' mean the same thing as 'C'
412             // (after the above cases are filtered out).
413             ppToken->ival = ch;
414             break;
415         }
416         break;
417     default:
418         ppToken->ival = ch;
419         break;
420     }
421     ppToken->name[0] = (char)ppToken->ival;
422     ppToken->name[1] = '\0';
423     ch = getChar();
424     if (ch != '\'') {
425         parseContext.ppError(ppToken->loc, "expected", "\'", "");
426         // Look ahead for a closing '
427         do {
428             ch = getChar();
429         } while (ch != '\'' && ch != EndOfInput && ch != '\n');
430     }
431 
432     return PpAtomConstInt;
433 }
434 
435 //
436 // Scanner used to tokenize source stream.
437 //
scan(TPpToken * ppToken)438 int TPpContext::tStringInput::scan(TPpToken* ppToken)
439 {
440     int AlreadyComplained = 0;
441     int len = 0;
442     int ch = 0;
443     int ii = 0;
444     unsigned long long ival = 0;
445     const auto floatingPointChar = [&](int ch) { return ch == '.' || ch == 'e' || ch == 'E' ||
446                                                                      ch == 'f' || ch == 'F' ||
447                                                                      ch == 'h' || ch == 'H'; };
448 
449     static const char* const Int64_Extensions[] = {
450         E_GL_ARB_gpu_shader_int64,
451         E_GL_EXT_shader_explicit_arithmetic_types,
452         E_GL_EXT_shader_explicit_arithmetic_types_int64 };
453     static const int Num_Int64_Extensions = sizeof(Int64_Extensions) / sizeof(Int64_Extensions[0]);
454 
455     static const char* const Int16_Extensions[] = {
456 #ifdef AMD_EXTENSIONS
457         E_GL_AMD_gpu_shader_int16,
458 #endif
459         E_GL_EXT_shader_explicit_arithmetic_types,
460         E_GL_EXT_shader_explicit_arithmetic_types_int16 };
461     static const int Num_Int16_Extensions = sizeof(Int16_Extensions) / sizeof(Int16_Extensions[0]);
462 
463     ppToken->ival = 0;
464     ppToken->i64val = 0;
465     ppToken->space = false;
466     ch = getch();
467     for (;;) {
468         while (ch == ' ' || ch == '\t') {
469             ppToken->space = true;
470             ch = getch();
471         }
472 
473         ppToken->loc = pp->parseContext.getCurrentLoc();
474         len = 0;
475         switch (ch) {
476         default:
477             // Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token)
478             if (ch > PpAtomMaxSingle)
479                 ch = PpAtomBadToken;
480             return ch;
481 
482         case 'A': case 'B': case 'C': case 'D': case 'E':
483         case 'F': case 'G': case 'H': case 'I': case 'J':
484         case 'K': case 'L': case 'M': case 'N': case 'O':
485         case 'P': case 'Q': case 'R': case 'S': case 'T':
486         case 'U': case 'V': case 'W': case 'X': case 'Y':
487         case 'Z': case '_':
488         case 'a': case 'b': case 'c': case 'd': case 'e':
489         case 'f': case 'g': case 'h': case 'i': case 'j':
490         case 'k': case 'l': case 'm': case 'n': case 'o':
491         case 'p': case 'q': case 'r': case 's': case 't':
492         case 'u': case 'v': case 'w': case 'x': case 'y':
493         case 'z':
494             do {
495                 if (len < MaxTokenLength) {
496                     ppToken->name[len++] = (char)ch;
497                     ch = getch();
498                 } else {
499                     if (! AlreadyComplained) {
500                         pp->parseContext.ppError(ppToken->loc, "name too long", "", "");
501                         AlreadyComplained = 1;
502                     }
503                     ch = getch();
504                 }
505             } while ((ch >= 'a' && ch <= 'z') ||
506                      (ch >= 'A' && ch <= 'Z') ||
507                      (ch >= '0' && ch <= '9') ||
508                      ch == '_');
509 
510             // line continuation with no token before or after makes len == 0, and need to start over skipping white space, etc.
511             if (len == 0)
512                 continue;
513 
514             ppToken->name[len] = '\0';
515             ungetch();
516             return PpAtomIdentifier;
517         case '0':
518             ppToken->name[len++] = (char)ch;
519             ch = getch();
520             if (ch == 'x' || ch == 'X') {
521                 // must be hexadecimal
522 
523                 bool isUnsigned = false;
524                 bool isInt64 = false;
525                 bool isInt16 = false;
526                 ppToken->name[len++] = (char)ch;
527                 ch = getch();
528                 if ((ch >= '0' && ch <= '9') ||
529                     (ch >= 'A' && ch <= 'F') ||
530                     (ch >= 'a' && ch <= 'f')) {
531 
532                     ival = 0;
533                     do {
534                         if (len < MaxTokenLength && ival <= 0x0fffffffffffffffull) {
535                             ppToken->name[len++] = (char)ch;
536                             if (ch >= '0' && ch <= '9') {
537                                 ii = ch - '0';
538                             } else if (ch >= 'A' && ch <= 'F') {
539                                 ii = ch - 'A' + 10;
540                             } else if (ch >= 'a' && ch <= 'f') {
541                                 ii = ch - 'a' + 10;
542                             } else
543                                 pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", "");
544                             ival = (ival << 4) | ii;
545                         } else {
546                             if (! AlreadyComplained) {
547                                 if(len < MaxTokenLength)
548                                     pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", "");
549                                 else
550                                     pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too long", "", "");
551                                 AlreadyComplained = 1;
552                             }
553                             ival = 0xffffffffffffffffull;
554                         }
555                         ch = getch();
556                     } while ((ch >= '0' && ch <= '9') ||
557                              (ch >= 'A' && ch <= 'F') ||
558                              (ch >= 'a' && ch <= 'f'));
559                 } else {
560                     pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", "");
561                 }
562                 if (ch == 'u' || ch == 'U') {
563                     if (len < MaxTokenLength)
564                         ppToken->name[len++] = (char)ch;
565                     isUnsigned = true;
566 
567                     int nextCh = getch();
568                     if (nextCh == 'l' || nextCh == 'L') {
569                         if (len < MaxTokenLength)
570                             ppToken->name[len++] = (char)nextCh;
571                         isInt64 = true;
572                     } else
573                         ungetch();
574 
575 #ifdef AMD_EXTENSIONS
576                     nextCh = getch();
577                     if ((nextCh == 's' || nextCh == 'S') &&
578                             pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
579                         if (len < MaxTokenLength)
580                             ppToken->name[len++] = (char)nextCh;
581                         isInt16 = true;
582                     } else
583                         ungetch();
584 #endif
585                 } else if (ch == 'l' || ch == 'L') {
586                     if (len < MaxTokenLength)
587                         ppToken->name[len++] = (char)ch;
588                     isInt64 = true;
589 #ifdef AMD_EXTENSIONS
590                 } else if ((ch == 's' || ch == 'S') &&
591                            pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
592                     if (len < MaxTokenLength)
593                         ppToken->name[len++] = (char)ch;
594                     isInt16 = true;
595 #endif
596                 } else
597                     ungetch();
598                 ppToken->name[len] = '\0';
599 
600                 if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
601                     if (pp->ifdepth == 0) {
602                         pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
603                                                         "64-bit hexadecimal literal");
604                         pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
605                             Num_Int64_Extensions, Int64_Extensions, "64-bit hexadecimal literal");
606                     }
607                     ppToken->i64val = ival;
608                     return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
609                 } else if (isInt16) {
610                     if (pp->ifdepth == 0) {
611                         if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
612                             pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
613                                                              "16-bit hexadecimal literal");
614                             pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
615                                 Num_Int16_Extensions, Int16_Extensions, "16-bit hexadecimal literal");
616                         }
617                     }
618                     ppToken->ival = (int)ival;
619                     return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
620                 } else {
621                     if (ival > 0xffffffffu && !AlreadyComplained)
622                         pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", "");
623                     ppToken->ival = (int)ival;
624                     return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
625                 }
626             } else {
627                 // could be octal integer or floating point, speculative pursue octal until it must be floating point
628 
629                 bool isUnsigned = false;
630                 bool isInt64 = false;
631                 bool isInt16 = false;
632                 bool octalOverflow = false;
633                 bool nonOctal = false;
634                 ival = 0;
635 
636                 // see how much octal-like stuff we can read
637                 while (ch >= '0' && ch <= '7') {
638                     if (len < MaxTokenLength)
639                         ppToken->name[len++] = (char)ch;
640                     else if (! AlreadyComplained) {
641                         pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
642                         AlreadyComplained = 1;
643                     }
644                     if (ival <= 0x1fffffffffffffffull) {
645                         ii = ch - '0';
646                         ival = (ival << 3) | ii;
647                     } else
648                         octalOverflow = true;
649                     ch = getch();
650                 }
651 
652                 // could be part of a float...
653                 if (ch == '8' || ch == '9') {
654                     nonOctal = true;
655                     do {
656                         if (len < MaxTokenLength)
657                             ppToken->name[len++] = (char)ch;
658                         else if (! AlreadyComplained) {
659                             pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
660                             AlreadyComplained = 1;
661                         }
662                         ch = getch();
663                     } while (ch >= '0' && ch <= '9');
664                 }
665                 if (floatingPointChar(ch))
666                     return pp->lFloatConst(len, ch, ppToken);
667 
668                 // wasn't a float, so must be octal...
669                 if (nonOctal)
670                     pp->parseContext.ppError(ppToken->loc, "octal literal digit too large", "", "");
671 
672                 if (ch == 'u' || ch == 'U') {
673                     if (len < MaxTokenLength)
674                         ppToken->name[len++] = (char)ch;
675                     isUnsigned = true;
676 
677                     int nextCh = getch();
678                     if (nextCh == 'l' || nextCh == 'L') {
679                         if (len < MaxTokenLength)
680                             ppToken->name[len++] = (char)nextCh;
681                         isInt64 = true;
682                     } else
683                         ungetch();
684 
685 #ifdef AMD_EXTENSIONS
686                     nextCh = getch();
687                     if ((nextCh == 's' || nextCh == 'S') &&
688                                 pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
689                         if (len < MaxTokenLength)
690                             ppToken->name[len++] = (char)nextCh;
691                         isInt16 = true;
692                     } else
693                         ungetch();
694 #endif
695                 } else if (ch == 'l' || ch == 'L') {
696                     if (len < MaxTokenLength)
697                         ppToken->name[len++] = (char)ch;
698                     isInt64 = true;
699 #ifdef AMD_EXTENSIONS
700                 } else if ((ch == 's' || ch == 'S') &&
701                                 pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
702                     if (len < MaxTokenLength)
703                         ppToken->name[len++] = (char)ch;
704                     isInt16 = true;
705 #endif
706                 } else
707                     ungetch();
708                 ppToken->name[len] = '\0';
709 
710                 if (!isInt64 && ival > 0xffffffffu)
711                     octalOverflow = true;
712 
713                 if (octalOverflow)
714                     pp->parseContext.ppError(ppToken->loc, "octal literal too big", "", "");
715 
716                 if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
717                     if (pp->ifdepth == 0) {
718                         pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
719                                                         "64-bit octal literal");
720                         pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
721                             Num_Int64_Extensions, Int64_Extensions, "64-bit octal literal");
722                     }
723                     ppToken->i64val = ival;
724                     return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
725                 } else if (isInt16) {
726                     if (pp->ifdepth == 0) {
727                         if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
728                             pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
729                                                             "16-bit octal literal");
730                             pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
731                                 Num_Int16_Extensions, Int16_Extensions, "16-bit octal literal");
732                         }
733                     }
734                     ppToken->ival = (int)ival;
735                     return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
736                 } else {
737                     ppToken->ival = (int)ival;
738                     return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
739                 }
740             }
741             break;
742         case '1': case '2': case '3': case '4':
743         case '5': case '6': case '7': case '8': case '9':
744             // can't be hexadecimal or octal, is either decimal or floating point
745 
746             do {
747                 if (len < MaxTokenLength)
748                     ppToken->name[len++] = (char)ch;
749                 else if (! AlreadyComplained) {
750                     pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
751                     AlreadyComplained = 1;
752                 }
753                 ch = getch();
754             } while (ch >= '0' && ch <= '9');
755             if (floatingPointChar(ch))
756                 return pp->lFloatConst(len, ch, ppToken);
757             else {
758                 // Finish handling signed and unsigned integers
759                 int numericLen = len;
760                 bool isUnsigned = false;
761                 bool isInt64 = false;
762                 bool isInt16 = false;
763                 if (ch == 'u' || ch == 'U') {
764                     if (len < MaxTokenLength)
765                         ppToken->name[len++] = (char)ch;
766                     isUnsigned = true;
767 
768                     int nextCh = getch();
769                     if (nextCh == 'l' || nextCh == 'L') {
770                         if (len < MaxTokenLength)
771                             ppToken->name[len++] = (char)nextCh;
772                         isInt64 = true;
773                     } else
774                         ungetch();
775 
776 #ifdef AMD_EXTENSIONS
777                     nextCh = getch();
778                     if ((nextCh == 's' || nextCh == 'S') &&
779                                 pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
780                         if (len < MaxTokenLength)
781                             ppToken->name[len++] = (char)nextCh;
782                         isInt16 = true;
783                     } else
784                         ungetch();
785 #endif
786                 } else if (ch == 'l' || ch == 'L') {
787                     if (len < MaxTokenLength)
788                         ppToken->name[len++] = (char)ch;
789                     isInt64 = true;
790 #ifdef AMD_EXTENSIONS
791                 } else if ((ch == 's' || ch == 'S') &&
792                                 pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
793                     if (len < MaxTokenLength)
794                         ppToken->name[len++] = (char)ch;
795                     isInt16 = true;
796 #endif
797                 } else
798                     ungetch();
799 
800                 ppToken->name[len] = '\0';
801                 ival = 0;
802                 const unsigned oneTenthMaxInt  = 0xFFFFFFFFu / 10;
803                 const unsigned remainderMaxInt = 0xFFFFFFFFu - 10 * oneTenthMaxInt;
804                 const unsigned long long oneTenthMaxInt64  = 0xFFFFFFFFFFFFFFFFull / 10;
805                 const unsigned long long remainderMaxInt64 = 0xFFFFFFFFFFFFFFFFull - 10 * oneTenthMaxInt64;
806                 const unsigned short oneTenthMaxInt16  = 0xFFFFu / 10;
807                 const unsigned short remainderMaxInt16 = 0xFFFFu - 10 * oneTenthMaxInt16;
808                 for (int i = 0; i < numericLen; i++) {
809                     ch = ppToken->name[i] - '0';
810                     bool overflow = false;
811                     if (isInt64)
812                         overflow = (ival > oneTenthMaxInt64 || (ival == oneTenthMaxInt64 && (unsigned long long)ch > remainderMaxInt64));
813                     else if (isInt16)
814                         overflow = (ival > oneTenthMaxInt16 || (ival == oneTenthMaxInt16 && (unsigned short)ch > remainderMaxInt16));
815                     else
816                         overflow = (ival > oneTenthMaxInt || (ival == oneTenthMaxInt && (unsigned)ch > remainderMaxInt));
817                     if (overflow) {
818                         pp->parseContext.ppError(ppToken->loc, "numeric literal too big", "", "");
819                         ival = 0xFFFFFFFFFFFFFFFFull;
820                         break;
821                     } else
822                         ival = ival * 10 + ch;
823                 }
824 
825                 if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
826                     if (pp->ifdepth == 0) {
827                         pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
828                                                         "64-bit literal");
829                         pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
830                             Num_Int64_Extensions, Int64_Extensions, "64-bit literal");
831                     }
832                     ppToken->i64val = ival;
833                     return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
834                 } else if (isInt16) {
835                     if (pp->ifdepth == 0 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
836                         pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
837                                                         "16-bit  literal");
838                         pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
839                             Num_Int16_Extensions, Int16_Extensions, "16-bit literal");
840                     }
841                     ppToken->ival = (int)ival;
842                     return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
843                 } else {
844                     ppToken->ival = (int)ival;
845                     return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
846                 }
847             }
848             break;
849         case '-':
850             ch = getch();
851             if (ch == '-') {
852                 return PpAtomDecrement;
853             } else if (ch == '=') {
854                 return PPAtomSubAssign;
855             } else {
856                 ungetch();
857                 return '-';
858             }
859         case '+':
860             ch = getch();
861             if (ch == '+') {
862                 return PpAtomIncrement;
863             } else if (ch == '=') {
864                 return PPAtomAddAssign;
865             } else {
866                 ungetch();
867                 return '+';
868             }
869         case '*':
870             ch = getch();
871             if (ch == '=') {
872                 return PPAtomMulAssign;
873             } else {
874                 ungetch();
875                 return '*';
876             }
877         case '%':
878             ch = getch();
879             if (ch == '=') {
880                 return PPAtomModAssign;
881             } else {
882                 ungetch();
883                 return '%';
884             }
885         case '^':
886             ch = getch();
887             if (ch == '^') {
888                 return PpAtomXor;
889             } else {
890                 if (ch == '=')
891                     return PpAtomXorAssign;
892                 else{
893                     ungetch();
894                     return '^';
895                 }
896             }
897 
898         case '=':
899             ch = getch();
900             if (ch == '=') {
901                 return PpAtomEQ;
902             } else {
903                 ungetch();
904                 return '=';
905             }
906         case '!':
907             ch = getch();
908             if (ch == '=') {
909                 return PpAtomNE;
910             } else {
911                 ungetch();
912                 return '!';
913             }
914         case '|':
915             ch = getch();
916             if (ch == '|') {
917                 return PpAtomOr;
918             } else if (ch == '=') {
919                 return PpAtomOrAssign;
920             } else {
921                 ungetch();
922                 return '|';
923             }
924         case '&':
925             ch = getch();
926             if (ch == '&') {
927                 return PpAtomAnd;
928             } else if (ch == '=') {
929                 return PpAtomAndAssign;
930             } else {
931                 ungetch();
932                 return '&';
933             }
934         case '<':
935             ch = getch();
936             if (ch == '<') {
937                 ch = getch();
938                 if (ch == '=')
939                     return PpAtomLeftAssign;
940                 else {
941                     ungetch();
942                     return PpAtomLeft;
943                 }
944             } else if (ch == '=') {
945                 return PpAtomLE;
946             } else {
947                 ungetch();
948                 return '<';
949             }
950         case '>':
951             ch = getch();
952             if (ch == '>') {
953                 ch = getch();
954                 if (ch == '=')
955                     return PpAtomRightAssign;
956                 else {
957                     ungetch();
958                     return PpAtomRight;
959                 }
960             } else if (ch == '=') {
961                 return PpAtomGE;
962             } else {
963                 ungetch();
964                 return '>';
965             }
966         case '.':
967             ch = getch();
968             if (ch >= '0' && ch <= '9') {
969                 ungetch();
970                 return pp->lFloatConst(0, '.', ppToken);
971             } else {
972                 ungetch();
973                 return '.';
974             }
975         case '/':
976             ch = getch();
977             if (ch == '/') {
978                 pp->inComment = true;
979                 do {
980                     ch = getch();
981                 } while (ch != '\n' && ch != EndOfInput);
982                 ppToken->space = true;
983                 pp->inComment = false;
984 
985                 return ch;
986             } else if (ch == '*') {
987                 ch = getch();
988                 do {
989                     while (ch != '*') {
990                         if (ch == EndOfInput) {
991                             pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", "");
992                             return ch;
993                         }
994                         ch = getch();
995                     }
996                     ch = getch();
997                     if (ch == EndOfInput) {
998                         pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", "");
999                         return ch;
1000                     }
1001                 } while (ch != '/');
1002                 ppToken->space = true;
1003                 // loop again to get the next token...
1004                 break;
1005             } else if (ch == '=') {
1006                 return PPAtomDivAssign;
1007             } else {
1008                 ungetch();
1009                 return '/';
1010             }
1011             break;
1012         case '\'':
1013             return pp->characterLiteral(ppToken);
1014         case '"':
1015             // TODO: If this gets enhanced to handle escape sequences, or
1016             // anything that is different than what #include needs, then
1017             // #include needs to use scanHeaderName() for this.
1018             ch = getch();
1019             while (ch != '"' && ch != '\n' && ch != EndOfInput) {
1020                 if (len < MaxTokenLength) {
1021                     ppToken->name[len] = (char)ch;
1022                     len++;
1023                     ch = getch();
1024                 } else
1025                     break;
1026             };
1027             ppToken->name[len] = '\0';
1028             if (ch != '"') {
1029                 ungetch();
1030                 pp->parseContext.ppError(ppToken->loc, "End of line in string", "string", "");
1031             }
1032             return PpAtomConstString;
1033         case ':':
1034             ch = getch();
1035             if (ch == ':')
1036                 return PpAtomColonColon;
1037             ungetch();
1038             return ':';
1039         }
1040 
1041         ch = getch();
1042     }
1043 }
1044 
1045 //
1046 // The main functional entry point into the preprocessor, which will
1047 // scan the source strings to figure out and return the next processing token.
1048 //
1049 // Return the token, or EndOfInput when no more tokens.
1050 //
tokenize(TPpToken & ppToken)1051 int TPpContext::tokenize(TPpToken& ppToken)
1052 {
1053     for(;;) {
1054         int token = scanToken(&ppToken);
1055 
1056         // Handle token-pasting logic
1057         token = tokenPaste(token, ppToken);
1058 
1059         if (token == EndOfInput) {
1060             missingEndifCheck();
1061             return EndOfInput;
1062         }
1063         if (token == '#') {
1064             if (previous_token == '\n') {
1065                 token = readCPPline(&ppToken);
1066                 if (token == EndOfInput) {
1067                     missingEndifCheck();
1068                     return EndOfInput;
1069                 }
1070                 continue;
1071             } else {
1072                 parseContext.ppError(ppToken.loc, "preprocessor directive cannot be preceded by another token", "#", "");
1073                 return EndOfInput;
1074             }
1075         }
1076         previous_token = token;
1077 
1078         if (token == '\n')
1079             continue;
1080 
1081         // expand macros
1082         if (token == PpAtomIdentifier) {
1083             switch (MacroExpand(&ppToken, false, true)) {
1084             case MacroExpandNotStarted:
1085                 break;
1086             case MacroExpandError:
1087                 return EndOfInput;
1088             case MacroExpandStarted:
1089             case MacroExpandUndef:
1090                 continue;
1091             }
1092         }
1093 
1094         switch (token) {
1095         case PpAtomIdentifier:
1096         case PpAtomConstInt:
1097         case PpAtomConstUint:
1098         case PpAtomConstFloat:
1099         case PpAtomConstInt64:
1100         case PpAtomConstUint64:
1101         case PpAtomConstInt16:
1102         case PpAtomConstUint16:
1103         case PpAtomConstDouble:
1104         case PpAtomConstFloat16:
1105             if (ppToken.name[0] == '\0')
1106                 continue;
1107             break;
1108         case PpAtomConstString:
1109             if (ifdepth == 0 && parseContext.intermediate.getSource() != EShSourceHlsl) {
1110                 // HLSL allows string literals.
1111                 parseContext.ppError(ppToken.loc, "string literals not supported", "\"\"", "");
1112                 continue;
1113             }
1114             break;
1115         case '\'':
1116             parseContext.ppError(ppToken.loc, "character literals not supported", "\'", "");
1117             continue;
1118         default:
1119             strcpy(ppToken.name, atomStrings.getString(token));
1120             break;
1121         }
1122 
1123         return token;
1124     }
1125 }
1126 
1127 //
1128 // Do all token-pasting related combining of two pasted tokens when getting a
1129 // stream of tokens from a replacement list. Degenerates to no processing if a
1130 // replacement list is not the source of the token stream.
1131 //
tokenPaste(int token,TPpToken & ppToken)1132 int TPpContext::tokenPaste(int token, TPpToken& ppToken)
1133 {
1134     // starting with ## is illegal, skip to next token
1135     if (token == PpAtomPaste) {
1136         parseContext.ppError(ppToken.loc, "unexpected location", "##", "");
1137         return scanToken(&ppToken);
1138     }
1139 
1140     int resultToken = token; // "foo" pasted with "35" is an identifier, not a number
1141 
1142     // ## can be chained, process all in the chain at once
1143     while (peekPasting()) {
1144         TPpToken pastedPpToken;
1145 
1146         // next token has to be ##
1147         token = scanToken(&pastedPpToken);
1148         assert(token == PpAtomPaste);
1149 
1150         // This covers end of macro expansion
1151         if (endOfReplacementList()) {
1152             parseContext.ppError(ppToken.loc, "unexpected location; end of replacement list", "##", "");
1153             break;
1154         }
1155 
1156         // get the token after the ##
1157         token = scanToken(&pastedPpToken);
1158 
1159         // This covers end of argument expansion
1160         if (token == tMarkerInput::marker) {
1161             parseContext.ppError(ppToken.loc, "unexpected location; end of argument", "##", "");
1162             break;
1163         }
1164 
1165         // get the token text
1166         switch (resultToken) {
1167         case PpAtomIdentifier:
1168             // already have the correct text in token.names
1169             break;
1170         case '=':
1171         case '!':
1172         case '-':
1173         case '~':
1174         case '+':
1175         case '*':
1176         case '/':
1177         case '%':
1178         case '<':
1179         case '>':
1180         case '|':
1181         case '^':
1182         case '&':
1183         case PpAtomRight:
1184         case PpAtomLeft:
1185         case PpAtomAnd:
1186         case PpAtomOr:
1187         case PpAtomXor:
1188             strcpy(ppToken.name, atomStrings.getString(resultToken));
1189             strcpy(pastedPpToken.name, atomStrings.getString(token));
1190             break;
1191         default:
1192             parseContext.ppError(ppToken.loc, "not supported for these tokens", "##", "");
1193             return resultToken;
1194         }
1195 
1196         // combine the tokens
1197         if (strlen(ppToken.name) + strlen(pastedPpToken.name) > MaxTokenLength) {
1198             parseContext.ppError(ppToken.loc, "combined tokens are too long", "##", "");
1199             return resultToken;
1200         }
1201         strncat(ppToken.name, pastedPpToken.name, MaxTokenLength - strlen(ppToken.name));
1202 
1203         // correct the kind of token we are making, if needed (identifiers stay identifiers)
1204         if (resultToken != PpAtomIdentifier) {
1205             int newToken = atomStrings.getAtom(ppToken.name);
1206             if (newToken > 0)
1207                 resultToken = newToken;
1208             else
1209                 parseContext.ppError(ppToken.loc, "combined token is invalid", "##", "");
1210         }
1211     }
1212 
1213     return resultToken;
1214 }
1215 
1216 // Checks if we've seen balanced #if...#endif
missingEndifCheck()1217 void TPpContext::missingEndifCheck()
1218 {
1219     if (ifdepth > 0)
1220         parseContext.ppError(parseContext.getCurrentLoc(), "missing #endif", "", "");
1221 }
1222 
1223 } // end namespace glslang
1224