Revision control

++ //
++ // Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
++ // Copyright (C) 2013 LunarG, Inc.
++ // Copyright (C) 2017 ARM Limited.
++ // Copyright (C) 2015-2018 Google, Inc.
++ // Copyright (c) 2023, Mobica Limited
++ //
++ // All rights reserved.
++ //
++ // Redistribution and use in source and binary forms, with or without
++ // modification, are permitted provided that the following conditions
++ // are met:
++ //
++ // Redistributions of source code must retain the above copyright
++ // notice, this list of conditions and the following disclaimer.
++ //
++ // Redistributions in binary form must reproduce the above
++ // copyright notice, this list of conditions and the following
++ // disclaimer in the documentation and/or other materials provided
++ // with the distribution.
++ //
++ // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
++ // contributors may be used to endorse or promote products derived
++ // from this software without specific prior written permission.
++ //
++ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++ // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++ // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++ // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
++ // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
++ // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ // POSSIBILITY OF SUCH DAMAGE.
++ //
++ /****************************************************************************\
++ Copyright (c) 2002, NVIDIA Corporation.
++
++ NVIDIA Corporation("NVIDIA") supplies this software to you in
++ consideration of your agreement to the following terms, and your use,
++ installation, modification or redistribution of this NVIDIA software
++ constitutes acceptance of these terms. If you do not agree with these
++ terms, please do not use, install, modify or redistribute this NVIDIA
++ software.
++
++ In consideration of your agreement to abide by the following terms, and
++ subject to these terms, NVIDIA grants you a personal, non-exclusive
++ license, under NVIDIA's copyrights in this original NVIDIA software (the
++ "NVIDIA Software"), to use, reproduce, modify and redistribute the
++ NVIDIA Software, with or without modifications, in source and/or binary
++ forms; provided that if you redistribute the NVIDIA Software, you must
++ retain the copyright notice of NVIDIA, this notice and the following
++ text and disclaimers in all such redistributions of the NVIDIA Software.
++ Neither the name, trademarks, service marks nor logos of NVIDIA
++ Corporation may be used to endorse or promote products derived from the
++ NVIDIA Software without specific prior written permission from NVIDIA.
++ Except as expressly stated in this notice, no other rights or licenses
++ express or implied, are granted by NVIDIA herein, including but not
++ limited to any patent rights that may be infringed by your derivative
++ works or by other works in which the NVIDIA Software may be
++ incorporated. No hardware is licensed hereunder.
++
++ THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
++ WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
++ INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
++ NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
++ ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
++ PRODUCTS.
++
++ IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
++ INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
++ TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
++ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
++ OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
++ NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
++ TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
++ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ \****************************************************************************/
++
++ #ifndef _CRT_SECURE_NO_WARNINGS
++ #define _CRT_SECURE_NO_WARNINGS
++ #endif
++
++ #include <cstdlib>
++ #include <cstring>
++
++ #include "PpContext.h"
++ #include "PpTokens.h"
++ #include "../Scan.h"
++
++ namespace QtShaderTools {
++ namespace glslang {
++
++ ///////////////////////////////////////////////////////////////////////////////////////////////
++ /////////////////////////////////// Floating point constants: /////////////////////////////////
++ ///////////////////////////////////////////////////////////////////////////////////////////////
++
++ // Scan a single- or double-precision floating point constant.
++ // Assumes that the scanner has seen at least one digit,
++ // followed by either a decimal '.' or the letter 'e', or a
++ // precision ending (e.g., F or LF).
++ //
++ // This is technically not correct, as the preprocessor should just
++ // accept the numeric literal along with whatever suffix it has, but
++ // currently, it stops on seeing a bad suffix, treating that as the
++ // next token. This effects things like token pasting, where it is
++ // relevant how many tokens something was broken into.
++ //
++ // See peekContinuedPasting().
++ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken)
++ {
++ const auto saveName = [&](int ch) {
++ if (len <= MaxTokenLength)
++ ppToken->name[len++] = static_cast<char>(ch);
++ };
++
++ // find the range of non-zero digits before the decimal point
++ int startNonZero = 0;
++ while (startNonZero < len && ppToken->name[startNonZero] == '0')
++ ++startNonZero;
++ int endNonZero = len;
++ while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0')
++ --endNonZero;
++ int numWholeNumberDigits = endNonZero - startNonZero;
++
++ // accumulate the range's value
++ bool fastPath = numWholeNumberDigits <= 15; // when the number gets too complex, set to false
++ unsigned long long wholeNumber = 0;
++ if (fastPath) {
++ for (int i = startNonZero; i < endNonZero; ++i)
++ wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0');
++ }
++ int decimalShift = len - endNonZero;
++
++ // Decimal point:
++ bool hasDecimalOrExponent = false;
++ if (ch == '.') {
++ hasDecimalOrExponent = true;
++ saveName(ch);
++ ch = getChar();
++ int firstDecimal = len;
++
++ #ifdef ENABLE_HLSL
++ // 1.#INF or -1.#INF
++ if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) {
++ if ((len < 2) ||
++ (len == 2 && ppToken->name[0] != '1') ||
++ (len == 3 && ppToken->name[1] != '1' && !(ppToken->name[0] == '-' || ppToken->name[0] == '+')) ||
++ (len > 3))
++ parseContext.ppError(ppToken->loc, "unexpected use of", "#", "");
++ else {
++ // we have 1.# or -1.# or +1.#, check for 'INF'
++ if ((ch = getChar()) != 'I' ||
++ (ch = getChar()) != 'N' ||
++ (ch = getChar()) != 'F')
++ parseContext.ppError(ppToken->loc, "expected 'INF'", "#", "");
++ else {
++ // we have [+-].#INF, and we are targeting IEEE 754, so wrap it up:
++ saveName('I');
++ saveName('N');
++ saveName('F');
++ ppToken->name[len] = '\0';
++ if (ppToken->name[0] == '-')
++ ppToken->i64val = 0xfff0000000000000; // -Infinity
++ else
++ ppToken->i64val = 0x7ff0000000000000; // +Infinity
++ return PpAtomConstFloat;
++ }
++ }
++ }
++ #endif
++
++ // Consume leading-zero digits after the decimal point
++ while (ch == '0') {
++ saveName(ch);
++ ch = getChar();
++ }
++ int startNonZeroDecimal = len;
++ int endNonZeroDecimal = len;
++
++ // Consume remaining digits, up to the exponent
++ while (ch >= '0' && ch <= '9') {
++ saveName(ch);
++ if (ch != '0')
++ endNonZeroDecimal = len;
++ ch = getChar();
++ }
++
++ // Compute accumulation up to the last non-zero digit
++ if (endNonZeroDecimal > startNonZeroDecimal) {
++ numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "."
++ if (numWholeNumberDigits > 15)
++ fastPath = false;
++ if (fastPath) {
++ for (int i = endNonZero; i < endNonZeroDecimal; ++i) {
++ if (ppToken->name[i] != '.')
++ wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0');
++ }
++ }
++ decimalShift = firstDecimal - endNonZeroDecimal;
++ }
++ }
++
++ // Exponent:
++ bool negativeExponent = false;
++ double exponentValue = 0.0;
++ int exponent = 0;
++ {
++ if (ch == 'e' || ch == 'E') {
++ hasDecimalOrExponent = true;
++ saveName(ch);
++ ch = getChar();
++ if (ch == '+' || ch == '-') {
++ negativeExponent = ch == '-';
++ saveName(ch);
++ ch = getChar();
++ }
++ if (ch >= '0' && ch <= '9') {
++ while (ch >= '0' && ch <= '9') {
++ if (exponent < 500) {
++ exponent = exponent * 10 + (ch - '0');
++ }
++ saveName(ch);
++ ch = getChar();
++ }
++ } else {
++ parseContext.ppError(ppToken->loc, "bad character in float exponent", "", "");
++ }
++ }
++
++ // Compensate for location of decimal
++ if (negativeExponent)
++ exponent -= decimalShift;
++ else {
++ exponent += decimalShift;
++ if (exponent < 0) {
++ negativeExponent = true;
++ exponent = -exponent;
++ }
++ }
++ if (exponent > 22)
++ fastPath = false;
++
++ if (fastPath) {
++ // Compute the floating-point value of the exponent
++ exponentValue = 1.0;
++ if (exponent > 0) {
++ double expFactor = 10;
++ while (exponent > 0) {
++ if (exponent & 0x1)
++ exponentValue *= expFactor;
++ expFactor *= expFactor;
++ exponent >>= 1;
++ }
++ }
++ }
++ }
++
++ // Suffix:
++ bool isDouble = false;
++ bool isFloat16 = false;
++ bool isFE2M1 = false;
++ bool isFE3M2 = false;
++ bool isFE2M3 = false;
++ bool isFUE8M0 = false;
++ bool isFMXINT8 = false;
++ if (ch == 'l' || ch == 'L') {
++ if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl)
++ parseContext.doubleCheck(ppToken->loc, "double floating-point suffix");
++ if (ifdepth == 0 && !hasDecimalOrExponent)
++ parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
++ if (parseContext.intermediate.getSource() == EShSourceGlsl) {
++ int ch2 = getChar();
++ if (ch2 != 'f' && ch2 != 'F') {
++ ungetChar();
++ ungetChar();
++ } else {
++ saveName(ch);
++ saveName(ch2);
++ isDouble = true;
++ }
++ } else if (parseContext.intermediate.getSource() == EShSourceHlsl) {
++ saveName(ch);
++ isDouble = true;
++ }
++ } else if (ch == 'h' || ch == 'H') {
++ if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl)
++ parseContext.float16Check(ppToken->loc, "half floating-point suffix");
++ if (ifdepth == 0 && !hasDecimalOrExponent)
++ parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
++ if (parseContext.intermediate.getSource() == EShSourceGlsl) {
++ int ch2 = getChar();
++ if (ch2 != 'f' && ch2 != 'F') {
++ ungetChar();
++ ungetChar();
++ } else {
++ saveName(ch);
++ saveName(ch2);
++ isFloat16 = true;
++ }
++ } else if (parseContext.intermediate.getSource() == EShSourceHlsl) {
++ saveName(ch);
++ isFloat16 = true;
++ }
++ } else
++ if (ch == 'f' || ch == 'F') {
++ if (ifdepth == 0)
++ parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix");
++ if (ifdepth == 0 && !parseContext.relaxedErrors())
++ parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix");
++ if (ifdepth == 0 && !hasDecimalOrExponent)
++ parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
++ saveName(ch);
++
++ auto const &try_str = [&](const char *str, size_t numChar) {
++ size_t counter = 0;
++ for (size_t i = 0; i < numChar; ++i) {
++ if (getChar() != str[i]) {
++ break;
++ }
++ counter++;
++ }
++ if (counter == numChar) {
++ for (size_t i = 0; i < numChar; ++i) {
++ saveName(str[i]);
++ }
++ return true;
++ } else {
++ // called getChar one more than counter times
++ counter++;
++ while (counter) {
++ ungetChar();
++ counter--;
++ }
++ return false;
++ }
++ };
++
++ if (ch == 'f') {
++ if (try_str("e2m1", 4)) {
++ isFE2M1 = true;
++ } else if (try_str("e3m2", 4)) {
++ isFE3M2 = true;
++ } else if (try_str("e2m3", 4)) {
++ isFE2M3 = true;
++ } else if (try_str("ue8m0", 5)) {
++ isFUE8M0 = true;
++ } else if (try_str("mxint8", 6)) {
++ isFMXINT8 = true;
++ }
++ }
++ } else
++ ungetChar();
++
++ // Patch up the name and length for overflow
++
++ if (len > MaxTokenLength) {
++ len = MaxTokenLength;
++ parseContext.ppError(ppToken->loc, "float literal too long", "", "");
++ }
++ ppToken->name[len] = '\0';
++
++ // Compute the numerical value
++ if (fastPath) {
++ // compute the floating-point value of the exponent
++ if (exponentValue == 0.0)
++ ppToken->dval = (double)wholeNumber;
++ else if (negativeExponent)
++ ppToken->dval = (double)wholeNumber / exponentValue;
++ else
++ ppToken->dval = (double)wholeNumber * exponentValue;
++ } else {
++ // slow path
++ ppToken->dval = 0.0;
++
++ // remove suffix
++ TString numstr(ppToken->name);
++ if (numstr.back() == 'f' || numstr.back() == 'F')
++ numstr.pop_back();
++ if (numstr.back() == 'h' || numstr.back() == 'H')
++ numstr.pop_back();
++ if (numstr.back() == 'l' || numstr.back() == 'L')
++ numstr.pop_back();
++
++ // use platform library
++ strtodStream.clear();
++ strtodStream.str(numstr.c_str());
++ strtodStream >> ppToken->dval;
++ if (strtodStream.fail()) {
++ // Assume failure combined with a large exponent was overflow, in
++ // an attempt to set INF.
++ if (!negativeExponent && exponent + numWholeNumberDigits > 300)
++ ppToken->i64val = 0x7ff0000000000000; // +Infinity
++ // Assume failure combined with a small exponent was overflow.
++ if (negativeExponent && exponent + numWholeNumberDigits > 300)
++ ppToken->dval = 0.0;
++ // Unknown reason for failure. Theory is that either
++ // - the 0.0 is still there, or
++ // - something reasonable was written that is better than 0.0
++ }
++ }
++
++ // Return the right token type
++ if (isDouble)
++ return PpAtomConstDouble;
++ else if (isFloat16)
++ return PpAtomConstFloat16;
++ else if (isFE2M1)
++ return PpAtomConstFloatE2M1;
++ else if (isFE3M2)
++ return PpAtomConstFloatE3M2;
++ else if (isFE2M3)
++ return PpAtomConstFloatE2M3;
++ else if (isFUE8M0)
++ return PpAtomConstFloatUE8M0;
++ else if (isFMXINT8)
++ return PpAtomConstFloatMXINT8;
++ else
++ return PpAtomConstFloat;
++ }
++
++ // Recognize a character literal.
++ //
++ // The first ' has already been accepted, read the rest, through the closing '.
++ //
++ // Always returns PpAtomConstInt.
++ //
++ int TPpContext::characterLiteral(TPpToken* ppToken)
++ {
++ ppToken->name[0] = 0;
++ ppToken->ival = 0;
++
++ if (parseContext.intermediate.getSource() != EShSourceHlsl) {
++ // illegal, except in macro definition, for which case we report the character
++ return '\'';
++ }
++
++ int ch = getChar();
++ switch (ch) {
++ case '\'':
++ // As empty sequence: ''
++ parseContext.ppError(ppToken->loc, "unexpected", "\'", "");
++ return PpAtomConstInt;
++ case '\\':
++ // As escape sequence: '\XXX'
++ switch (ch = getChar()) {
++ case 'a':
++ ppToken->ival = 7;
++ break;
++ case 'b':
++ ppToken->ival = 8;
++ break;
++ case 't':
++ ppToken->ival = 9;
++ break;
++ case 'n':
++ ppToken->ival = 10;
++ break;
++ case 'v':
++ ppToken->ival = 11;
++ break;
++ case 'f':
++ ppToken->ival = 12;
++ break;
++ case 'r':
++ ppToken->ival = 13;
++ break;
++ case 'x':
++ case '0':
++ parseContext.ppError(ppToken->loc, "octal and hex sequences not supported", "\\", "");
++ break;
++ default:
++ // This catches '\'', '\"', '\?', etc.
++ // Also, things like '\C' mean the same thing as 'C'
++ // (after the above cases are filtered out).
++ ppToken->ival = ch;
++ break;
++ }
++ break;
++ default:
++ ppToken->ival = ch;
++ break;
++ }
++ ppToken->name[0] = (char)ppToken->ival;
++ ppToken->name[1] = '\0';
++ ch = getChar();
++ if (ch != '\'') {
++ parseContext.ppError(ppToken->loc, "expected", "\'", "");
++ // Look ahead for a closing '
++ do {
++ ch = getChar();
++ } while (ch != '\'' && ch != EndOfInput && ch != '\n');
++ }
++
++ return PpAtomConstInt;
++ }
++
++ //
++ // Scanner used to tokenize source stream.
++ //
++ // N.B. Invalid numeric suffixes are not consumed.//
++ // This is technically not correct, as the preprocessor should just
++ // accept the numeric literal along with whatever suffix it has, but
++ // currently, it stops on seeing a bad suffix, treating that as the
++ // next token. This effects things like token pasting, where it is
++ // relevant how many tokens something was broken into.
++ // See peekContinuedPasting().
++ //
++ int TPpContext::tStringInput::scan(TPpToken* ppToken)
++ {
++ int AlreadyComplained = 0;
++ int len = 0;
++ int ch = 0;
++ int ii = 0;
++ unsigned long long ival = 0;
++ const auto floatingPointChar = [&](int ch) { return ch == '.' || ch == 'e' || ch == 'E' ||
++ ch == 'f' || ch == 'F' ||
++ ch == 'h' || ch == 'H'; };
++
++ static const char* const Int64_Extensions[] = {
++ E_GL_ARB_gpu_shader_int64,
++ E_GL_EXT_shader_explicit_arithmetic_types,
++ E_GL_NV_gpu_shader5,
++ E_GL_EXT_shader_explicit_arithmetic_types_int64 };
++ static const int Num_Int64_Extensions = sizeof(Int64_Extensions) / sizeof(Int64_Extensions[0]);
++
++ static const char* const Int16_Extensions[] = {
++ E_GL_AMD_gpu_shader_int16,
++ E_GL_EXT_shader_explicit_arithmetic_types,
++ E_GL_EXT_shader_explicit_arithmetic_types_int16 };
++ static const int Num_Int16_Extensions = sizeof(Int16_Extensions) / sizeof(Int16_Extensions[0]);
++
++ ppToken->clear();
++ ch = getch();
++ for (;;) {
++ while (ch == ' ' || ch == '\t') {
++ ppToken->space = true;
++ ch = getch();
++ }
++
++ ppToken->loc = pp->parseContext.getCurrentLoc();
++ len = 0;
++ switch (ch) {
++ default:
++ // Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token)
++ if (ch > PpAtomMaxSingle)
++ ch = PpAtomBadToken;
++ return ch;
++
++ case 'A': case 'B': case 'C': case 'D': case 'E':
++ case 'F': case 'G': case 'H': case 'I': case 'J':
++ case 'K': case 'L': case 'M': case 'N': case 'O':
++ case 'P': case 'Q': case 'R': case 'S': case 'T':
++ case 'U': case 'V': case 'W': case 'X': case 'Y':
++ case 'Z': case '_':
++ case 'a': case 'b': case 'c': case 'd': case 'e':
++ case 'f': case 'g': case 'h': case 'i': case 'j':
++ case 'k': case 'l': case 'm': case 'n': case 'o':
++ case 'p': case 'q': case 'r': case 's': case 't':
++ case 'u': case 'v': case 'w': case 'x': case 'y':
++ case 'z':
++ do {
++ if (len < MaxTokenLength) {
++ ppToken->name[len++] = (char)ch;
++ ch = getch();
++ } else {
++ if (! AlreadyComplained) {
++ pp->parseContext.ppError(ppToken->loc, "name too long", "", "");
++ AlreadyComplained = 1;
++ }
++ ch = getch();
++ }
++ } while ((ch >= 'a' && ch <= 'z') ||
++ (ch >= 'A' && ch <= 'Z') ||
++ (ch >= '0' && ch <= '9') ||
++ ch == '_');
++
++ // line continuation with no token before or after makes len == 0, and need to start over skipping white space, etc.
++ if (len == 0)
++ continue;
++
++ ppToken->name[len] = '\0';
++ ungetch();
++ return PpAtomIdentifier;
++ case '0':
++ ppToken->name[len++] = (char)ch;
++ ch = getch();
++ if (ch == 'x' || ch == 'X') {
++ // must be hexadecimal
++
++ bool isUnsigned = false;
++ bool isInt64 = false;
++ bool isInt16 = false;
++ ppToken->name[len++] = (char)ch;
++ ch = getch();
++ if ((ch >= '0' && ch <= '9') ||
++ (ch >= 'A' && ch <= 'F') ||
++ (ch >= 'a' && ch <= 'f')) {
++
++ ival = 0;
++ do {
++ if (len < MaxTokenLength && ival <= 0x7fffffffffffffffull) {
++ ppToken->name[len++] = (char)ch;
++ if (ch >= '0' && ch <= '9') {
++ ii = ch - '0';
++ } else if (ch >= 'A' && ch <= 'F') {
++ ii = ch - 'A' + 10;
++ } else if (ch >= 'a' && ch <= 'f') {
++ ii = ch - 'a' + 10;
++ } else
++ pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", "");
++ ival = (ival << 4) | ii;
++ } else {
++ if (! AlreadyComplained) {
++ if(len < MaxTokenLength)
++ pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", "");
++ else
++ pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too long", "", "");
++ AlreadyComplained = 1;
++ }
++ ival = 0xffffffffffffffffull;
++ }
++ ch = getch();
++ } while ((ch >= '0' && ch <= '9') ||
++ (ch >= 'A' && ch <= 'F') ||
++ (ch >= 'a' && ch <= 'f'));
++ } else {
++ pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", "");
++ }
++ if (ch == 'u' || ch == 'U') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isUnsigned = true;
++
++ int nextCh = getch();
++ if (nextCh == 'l' || nextCh == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt64 = true;
++ } else
++ ungetch();
++
++ nextCh = getch();
++ if ((nextCh == 's' || nextCh == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt16 = true;
++ } else
++ ungetch();
++ } else if (ch == 'l' || ch == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt64 = true;
++ } else if ((ch == 's' || ch == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt16 = true;
++ } else
++ ungetch();
++ ppToken->name[len] = '\0';
++
++ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (pp->ifdepth == 0) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "64-bit hexadecimal literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int64_Extensions, Int64_Extensions, "64-bit hexadecimal literal");
++ }
++ ppToken->i64val = ival;
++ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
++ } else if (isInt16) {
++ if (pp->ifdepth == 0) {
++ if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "16-bit hexadecimal literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int16_Extensions, Int16_Extensions, "16-bit hexadecimal literal");
++ }
++ }
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
++ } else {
++ if (ival > 0xffffffffu && !AlreadyComplained)
++ pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", "");
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
++ }
++ } else if ((ch == 'b' || ch == 'B') && pp->parseContext.intermediate.getSource() == EShSourceHlsl) {
++ // must be binary
++ bool isUnsigned = false;
++ bool isInt64 = false;
++ bool isInt16 = false;
++ ppToken->name[len++] = (char)ch;
++ ch = getch();
++
++ // Check value
++ if ((ch == '0' || ch == '1'))
++ {
++ ival = 0;
++ do {
++ if (len < MaxTokenLength && ival <= 0x7fffffffffffffffull) {
++ ppToken->name[len++] = (char)ch;
++ if (ch == '0' || ch == '1') {
++ ii = ch - '0';
++ } else {
++ pp->parseContext.ppError(ppToken->loc, "bad digit in binary literal", "", "");
++ }
++ ival = (ival << 1) | ii;
++ }
++ else
++ {
++ if (! AlreadyComplained) {
++ if(len < MaxTokenLength)
++ pp->parseContext.ppError(ppToken->loc, "binary literal too big", "", "");
++ else
++ pp->parseContext.ppError(ppToken->loc, "binary literal too long", "", "");
++ AlreadyComplained = 1;
++ }
++ ival = 0xffffffffffffffffull;
++ }
++ ch = getch();
++ } while (ch == '0' || ch == '1');
++ }
++ else
++ {
++ pp->parseContext.ppError(ppToken->loc, "bad digit in binary literal", "", "");
++ }
++
++ // check type
++ if (ch == 'u' || ch == 'U') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isUnsigned = true;
++
++ int nextCh = getch();
++ if (nextCh == 'l' || nextCh == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt64 = true;
++ } else
++ ungetch();
++
++ nextCh = getch();
++ if ((nextCh == 's' || nextCh == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt16 = true;
++ } else
++ ungetch();
++ } else if (ch == 'l' || ch == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt64 = true;
++ } else if ((ch == 's' || ch == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt16 = true;
++ } else {
++ ungetch();
++ }
++ ppToken->name[len] = '\0';
++
++ // Assign value
++ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (pp->ifdepth == 0) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "64-bit binary literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int64_Extensions, Int64_Extensions, "64-bit binary literal");
++ }
++ ppToken->i64val = ival;
++ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
++ } else if (isInt16) {
++ if (pp->ifdepth == 0) {
++ if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "16-bit binary literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int16_Extensions, Int16_Extensions, "16-bit binary literal");
++ }
++ }
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
++ } else {
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
++ }
++ } else {
++ // could be octal integer or floating point, speculative pursue octal until it must be floating point
++
++ bool isUnsigned = false;
++ bool isInt64 = false;
++ bool isInt16 = false;
++ bool octalOverflow = false;
++ bool nonOctal = false;
++ ival = 0;
++
++ // see how much octal-like stuff we can read
++ while (ch >= '0' && ch <= '7') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ else if (! AlreadyComplained) {
++ pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
++ AlreadyComplained = 1;
++ }
++ if (ival <= 0x1fffffffffffffffull) {
++ ii = ch - '0';
++ ival = (ival << 3) | ii;
++ } else
++ octalOverflow = true;
++ ch = getch();
++ }
++
++ // could be part of a float...
++ if (ch == '8' || ch == '9') {
++ nonOctal = true;
++ do {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ else if (! AlreadyComplained) {
++ pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
++ AlreadyComplained = 1;
++ }
++ ch = getch();
++ } while (ch >= '0' && ch <= '9');
++ }
++ if (floatingPointChar(ch))
++ return pp->lFloatConst(len, ch, ppToken);
++
++ // wasn't a float, so must be octal...
++ if (nonOctal)
++ pp->parseContext.ppError(ppToken->loc, "octal literal digit too large", "", "");
++
++ if (ch == 'u' || ch == 'U') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isUnsigned = true;
++
++ int nextCh = getch();
++ if (nextCh == 'l' || nextCh == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt64 = true;
++ } else
++ ungetch();
++
++ nextCh = getch();
++ if ((nextCh == 's' || nextCh == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt16 = true;
++ } else
++ ungetch();
++ } else if (ch == 'l' || ch == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt64 = true;
++ } else if ((ch == 's' || ch == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt16 = true;
++ } else
++ ungetch();
++ ppToken->name[len] = '\0';
++
++ if (!isInt64 && ival > 0xffffffffu)
++ octalOverflow = true;
++
++ if (octalOverflow)
++ pp->parseContext.ppError(ppToken->loc, "octal literal too big", "", "");
++
++ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (pp->ifdepth == 0) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "64-bit octal literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int64_Extensions, Int64_Extensions, "64-bit octal literal");
++ }
++ ppToken->i64val = ival;
++ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
++ } else if (isInt16) {
++ if (pp->ifdepth == 0) {
++ if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "16-bit octal literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int16_Extensions, Int16_Extensions, "16-bit octal literal");
++ }
++ }
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
++ } else {
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
++ }
++ }
++ break;
++ case '1': case '2': case '3': case '4':
++ case '5': case '6': case '7': case '8': case '9':
++ // can't be hexadecimal or octal, is either decimal or floating point
++
++ do {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ else if (! AlreadyComplained) {
++ pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
++ AlreadyComplained = 1;
++ }
++ ch = getch();
++ } while (ch >= '0' && ch <= '9');
++ if (floatingPointChar(ch))
++ return pp->lFloatConst(len, ch, ppToken);
++ else {
++ // Finish handling signed and unsigned integers
++ int numericLen = len;
++ bool isUnsigned = false;
++ bool isInt64 = false;
++ bool isInt16 = false;
++ if (ch == 'u' || ch == 'U') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isUnsigned = true;
++
++ int nextCh = getch();
++ if (nextCh == 'l' || nextCh == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt64 = true;
++ } else
++ ungetch();
++
++ nextCh = getch();
++ if ((nextCh == 's' || nextCh == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)nextCh;
++ isInt16 = true;
++ } else
++ ungetch();
++ } else if (ch == 'l' || ch == 'L') {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt64 = true;
++ } else if ((ch == 's' || ch == 'S') &&
++ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (len < MaxTokenLength)
++ ppToken->name[len++] = (char)ch;
++ isInt16 = true;
++ } else
++ ungetch();
++
++ ppToken->name[len] = '\0';
++ ival = 0;
++ const unsigned oneTenthMaxInt = 0xFFFFFFFFu / 10;
++ const unsigned remainderMaxInt = 0xFFFFFFFFu - 10 * oneTenthMaxInt;
++ const unsigned long long oneTenthMaxInt64 = 0xFFFFFFFFFFFFFFFFull / 10;
++ const unsigned long long remainderMaxInt64 = 0xFFFFFFFFFFFFFFFFull - 10 * oneTenthMaxInt64;
++ const unsigned short oneTenthMaxInt16 = 0xFFFFu / 10;
++ const unsigned short remainderMaxInt16 = 0xFFFFu - 10 * oneTenthMaxInt16;
++ for (int i = 0; i < numericLen; i++) {
++ ch = ppToken->name[i] - '0';
++ bool overflow = false;
++ if (isInt64)
++ overflow = (ival > oneTenthMaxInt64 || (ival == oneTenthMaxInt64 && (unsigned long long)ch > remainderMaxInt64));
++ else if (isInt16)
++ overflow = (ival > oneTenthMaxInt16 || (ival == oneTenthMaxInt16 && (unsigned short)ch > remainderMaxInt16));
++ else
++ overflow = (ival > oneTenthMaxInt || (ival == oneTenthMaxInt && (unsigned)ch > remainderMaxInt));
++ if (overflow) {
++ pp->parseContext.ppError(ppToken->loc, "numeric literal too big", "", "");
++ ival = 0xFFFFFFFFFFFFFFFFull;
++ break;
++ } else
++ ival = ival * 10 + ch;
++ }
++
++ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ if (pp->ifdepth == 0) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "64-bit literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int64_Extensions, Int64_Extensions, "64-bit literal");
++ }
++ ppToken->i64val = ival;
++ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
++ } else if (isInt16) {
++ if (pp->ifdepth == 0 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
++ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
++ "16-bit literal");
++ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
++ Num_Int16_Extensions, Int16_Extensions, "16-bit literal");
++ }
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
++ } else {
++ ppToken->ival = (int)ival;
++ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
++ }
++ }
++ break;
++ case '-':
++ ch = getch();
++ if (ch == '-') {
++ return PpAtomDecrement;
++ } else if (ch == '=') {
++ return PPAtomSubAssign;
++ } else {
++ ungetch();
++ return '-';
++ }
++ case '+':
++ ch = getch();
++ if (ch == '+') {
++ return PpAtomIncrement;
++ } else if (ch == '=') {
++ return PPAtomAddAssign;
++ } else {
++ ungetch();
++ return '+';
++ }
++ case '*':
++ ch = getch();
++ if (ch == '=') {
++ return PPAtomMulAssign;
++ } else {
++ ungetch();
++ return '*';
++ }
++ case '%':
++ ch = getch();
++ if (ch == '=') {
++ return PPAtomModAssign;
++ } else {
++ ungetch();
++ return '%';
++ }
++ case '^':
++ ch = getch();
++ if (ch == '^') {
++ return PpAtomXor;
++ } else {
++ if (ch == '=')
++ return PpAtomXorAssign;
++ else{
++ ungetch();
++ return '^';
++ }
++ }
++
++ case '=':
++ ch = getch();
++ if (ch == '=') {
++ return PpAtomEQ;
++ } else {
++ ungetch();
++ return '=';
++ }
++ case '!':
++ ch = getch();
++ if (ch == '=') {
++ return PpAtomNE;
++ } else {
++ ungetch();
++ return '!';
++ }
++ case '|':
++ ch = getch();
++ if (ch == '|') {
++ return PpAtomOr;
++ } else if (ch == '=') {
++ return PpAtomOrAssign;
++ } else {
++ ungetch();
++ return '|';
++ }
++ case '&':
++ ch = getch();
++ if (ch == '&') {
++ return PpAtomAnd;
++ } else if (ch == '=') {
++ return PpAtomAndAssign;
++ } else {
++ ungetch();
++ return '&';
++ }
++ case '<':
++ ch = getch();
++ if (ch == '<') {
++ ch = getch();
++ if (ch == '=')
++ return PpAtomLeftAssign;
++ else {
++ ungetch();
++ return PpAtomLeft;
++ }
++ } else if (ch == '=') {
++ return PpAtomLE;
++ } else {
++ ungetch();
++ return '<';
++ }
++ case '>':
++ ch = getch();
++ if (ch == '>') {
++ ch = getch();
++ if (ch == '=')
++ return PpAtomRightAssign;
++ else {
++ ungetch();
++ return PpAtomRight;
++ }
++ } else if (ch == '=') {
++ return PpAtomGE;
++ } else {
++ ungetch();
++ return '>';
++ }
++ case '.':
++ ch = getch();
++ if (ch >= '0' && ch <= '9') {
++ ungetch();
++ return pp->lFloatConst(0, '.', ppToken);
++ } else {
++ ungetch();
++ return '.';
++ }
++ case '/':
++ ch = getch();
++ if (ch == '/') {
++ pp->inComment = true;
++ do {
++ ch = getch();
++ } while (ch != '\n' && ch != EndOfInput);
++ ppToken->space = true;
++ pp->inComment = false;
++
++ return ch;
++ } else if (ch == '*') {
++ ch = getch();
++ do {
++ while (ch != '*') {
++ if (ch == EndOfInput) {
++ pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", "");
++ return ch;
++ }
++ ch = getch();
++ }
++ ch = getch();
++ if (ch == EndOfInput) {
++ pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", "");
++ return ch;
++ }
++ } while (ch != '/');
++ ppToken->space = true;
++ // loop again to get the next token...
++ break;
++ } else if (ch == '=') {
++ return PPAtomDivAssign;
++ } else {
++ ungetch();
++ return '/';
++ }
++ break;
++ case '\'':
++ return pp->characterLiteral(ppToken);
++ case '"':
++ // #include uses scanHeaderName() to ignore these escape sequences.
++ ch = getch();
++ while (ch != '"' && ch != '\n' && ch != EndOfInput) {
++ if (len < MaxTokenLength) {
++ if (ch == '\\' && !pp->disableEscapeSequences) {
++ int nextCh = getch();
++ switch (nextCh) {
++ case '\'': ch = 0x27; break;
++ case '"': ch = 0x22; break;
++ case '?': ch = 0x3f; break;
++ case '\\': ch = 0x5c; break;
++ case 'a': ch = 0x07; break;
++ case 'b': ch = 0x08; break;
++ case 'f': ch = 0x0c; break;
++ case 'n': ch = 0x0a; break;
++ case 'r': ch = 0x0d; break;
++ case 't': ch = 0x09; break;
++ case 'v': ch = 0x0b; break;
++ case 'x':
++ // Hex value, arbitrary number of characters. Terminated by the first
++ // non-hex digit
++ {
++ int numDigits = 0;
++ ch = 0;
++ while (true) {
++ nextCh = getch();
++ if (nextCh >= '0' && nextCh <= '9')
++ nextCh -= '0';
++ else if (nextCh >= 'A' && nextCh <= 'F')
++ nextCh -= 'A' - 10;
++ else if (nextCh >= 'a' && nextCh <= 'f')
++ nextCh -= 'a' - 10;
++ else {
++ ungetch();
++ break;
++ }
++ numDigits++;
++ ch = ch * 0x10 + nextCh;
++ }
++ if (numDigits == 0) {
++ pp->parseContext.ppError(ppToken->loc, "Expected hex value in escape sequence", "string", "");
++ }
++ break;
++ }
++ case '0':
++ case '1':
++ case '2':
++ case '3':
++ case '4':
++ case '5':
++ case '6':
++ case '7':
++ // Octal value, up to three octal digits
++ {
++ int numDigits = 1;
++ ch = nextCh - '0';
++ while (numDigits < 3) {
++ nextCh = getch();
++ if (nextCh >= '0' && nextCh <= '7')
++ nextCh -= '0';
++ else {
++ ungetch();
++ break;
++ }
++ numDigits++;
++ ch = ch * 8 + nextCh;
++ }
++ break;
++ }
++ default:
++ pp->parseContext.ppError(ppToken->loc, "Invalid escape sequence", "string", "");
++ break;
++ }
++ }
++ ppToken->name[len] = (char)ch;
++ len++;
++ ch = getch();
++ } else
++ break;
++ };
++ ppToken->name[len] = '\0';
++ if (ch != '"') {
++ ungetch();
++ pp->parseContext.ppError(ppToken->loc, "End of line in string", "string", "");
++ }
++ return PpAtomConstString;
++ case ':':
++ ch = getch();
++ if (ch == ':')
++ return PpAtomColonColon;
++ ungetch();
++ return ':';
++ }
++
++ ch = getch();
++ }
++ }
++
++ //
++ // The main functional entry point into the preprocessor, which will
++ // scan the source strings to figure out and return the next processing token.
++ //
++ // Return the token, or EndOfInput when no more tokens.
++ //
++ int TPpContext::tokenize(TPpToken& ppToken)
++ {
++ int stringifyDepth = 0;
++ TPpToken stringifiedToken; // Tokens are appended to this as they come in
++ for (;;) {
++ int token = scanToken(&ppToken);
++
++ // Handle token-pasting logic
++ token = tokenPaste(token, ppToken);
++
++ if (token == EndOfInput) {
++ missingEndifCheck();
++ return EndOfInput;
++ }
++ if (token == '#') {
++ if (previous_token == '\n') {
++ token = readCPPline(&ppToken);
++ if (token == EndOfInput) {
++ missingEndifCheck();
++ return EndOfInput;
++ }
++ continue;
++ } else {
++ parseContext.ppError(ppToken.loc, "preprocessor directive cannot be preceded by another token", "#", "");
++ return EndOfInput;
++ }
++ }
++ previous_token = token;
++
++ if (token == '\n')
++ continue;
++
++ if (token == tStringifyLevelInput::PUSH) {
++ stringifyDepth++;
++ continue;
++ }
++ if (token == tStringifyLevelInput::POP) {
++ assert(stringifyDepth > 0);
++ stringifyDepth--;
++ if (stringifyDepth == 0) {
++ snprintf(ppToken.name, sizeof(ppToken.name), "%s", stringifiedToken.name);
++ return PpAtomConstString;
++ }
++ continue;
++ }
++
++ // expand macros
++ if (token == PpAtomIdentifier) {
++ switch (MacroExpand(&ppToken, false, true)) {
++ case MacroExpandNotStarted:
++ break;
++ case MacroExpandError:
++ return EndOfInput;
++ case MacroExpandStarted:
++ case MacroExpandUndef:
++ continue;
++ }
++ }
++
++ bool needStringSupport = ifdepth == 0 && (token == PpAtomConstString || stringifyDepth > 0);
++ if (needStringSupport && parseContext.intermediate.getSource() != EShSourceHlsl) {
++ // HLSL allows string literals.
++ // GLSL allows string literals with GL_EXT_debug_printf.
++ const char* const string_literal_EXTs[] = { E_GL_EXT_debug_printf, E_GL_EXT_spirv_intrinsics, E_GL_EXT_abort };
++ parseContext.requireExtensions(ppToken.loc, 3, string_literal_EXTs, "string literal");
++ if (!parseContext.extensionTurnedOn(E_GL_EXT_debug_printf) &&
++ !parseContext.extensionTurnedOn(E_GL_EXT_spirv_intrinsics)&&
++ !parseContext.extensionTurnedOn(E_GL_EXT_abort)) {
++ continue;
++ }
++ }
++
++ switch (token) {
++ case PpAtomConstString:
++ break;
++ case PpAtomIdentifier:
++ case PpAtomConstInt:
++ case PpAtomConstUint:
++ case PpAtomConstFloat:
++ case PpAtomConstInt64:
++ case PpAtomConstUint64:
++ case PpAtomConstInt16:
++ case PpAtomConstUint16:
++ case PpAtomConstDouble:
++ case PpAtomConstFloat16:
++ case PpAtomConstFloatE2M1:
++ case PpAtomConstFloatE3M2:
++ case PpAtomConstFloatE2M3:
++ case PpAtomConstFloatUE8M0:
++ case PpAtomConstFloatMXINT8:
++ if (ppToken.name[0] == '\0')
++ continue;
++ break;
++ case '\'':
++ parseContext.ppError(ppToken.loc, "character literals not supported", "\'", "");
++ continue;
++ default:
++ snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(token));
++ break;
++ }
++ if (stringifyDepth > 0) {
++ size_t existingLen = strlen(stringifiedToken.name);
++ char* dst = stringifiedToken.name + existingLen;
++ // stringify_depth would determine how many layers of \\\"\\\" are needed, if we wanted to.
++ if (ppToken.space) {
++ snprintf(dst, sizeof(stringifiedToken.name) - existingLen - 1, " %s", ppToken.name);
++ } else {
++ snprintf(dst, sizeof(stringifiedToken.name) - existingLen, "%s", ppToken.name);
++ }
++ continue;
++ }
++
++ return token;
++ }
++ }
++
++ //
++ // Do all token-pasting related combining of two pasted tokens when getting a
++ // stream of tokens from a replacement list. Degenerates to no processing if a
++ // replacement list is not the source of the token stream.
++ //
++ int TPpContext::tokenPaste(int token, TPpToken& ppToken)
++ {
++ // starting with ## is illegal, skip to next token
++ if (token == PpAtomPaste) {
++ parseContext.ppError(ppToken.loc, "unexpected location", "##", "");
++ return scanToken(&ppToken);
++ }
++
++ int resultToken = token; // "foo" pasted with "35" is an identifier, not a number
++
++ // ## can be chained, process all in the chain at once
++ while (peekPasting()) {
++ TPpToken pastedPpToken;
++
++ // next token has to be ##
++ token = scanToken(&pastedPpToken);
++ assert(token == PpAtomPaste);
++
++ // This covers end of macro expansion
++ if (endOfReplacementList()) {
++ // this should be unreachable, incomplete #/## sequences are caught at macro parsing time.
++ parseContext.ppError(ppToken.loc, "unexpected location; end of replacement list", "##", "");
++ break;
++ }
++
++ // Get the token(s) after the ##.
++ // Because of "space" semantics, and prior tokenization, what
++ // appeared a single token, e.g. "3A", might have been tokenized
++ // into two tokens "3" and "A", but the "A" will have 'space' set to
++ // false. Accumulate all of these to recreate the original lexical
++ // appearing token.
++ do {
++ token = scanToken(&pastedPpToken);
++
++ // This covers end of argument expansion
++ if (token == tMarkerInput::marker) {
++ parseContext.ppError(ppToken.loc, "unexpected location; end of argument", "##", "");
++ return resultToken;
++ }
++
++ // get the token text
++ switch (resultToken) {
++ case PpAtomIdentifier:
++ // already have the correct text in token.names
++ break;
++ case '=':
++ case '!':
++ case '-':
++ case '~':
++ case '+':
++ case '*':
++ case '/':
++ case '%':
++ case '<':
++ case '>':
++ case '|':
++ case '^':
++ case '&':
++ case PpAtomRight:
++ case PpAtomLeft:
++ case PpAtomAnd:
++ case PpAtomOr:
++ case PpAtomXor:
++ snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(resultToken));
++ snprintf(pastedPpToken.name, sizeof(pastedPpToken.name), "%s", atomStrings.getString(token));
++ break;
++ default:
++ parseContext.ppError(ppToken.loc, "not supported for these tokens", "##", "");
++ return resultToken;
++ }
++
++ // combine the tokens
++ if (strlen(ppToken.name) + strlen(pastedPpToken.name) > MaxTokenLength) {
++ parseContext.ppError(ppToken.loc, "combined tokens are too long", "##", "");
++ return resultToken;
++ }
++ snprintf(&ppToken.name[0] + strlen(ppToken.name), sizeof(ppToken.name) - strlen(ppToken.name),
++ "%s", pastedPpToken.name);
++
++ // correct the kind of token we are making, if needed (identifiers stay identifiers)
++ if (resultToken != PpAtomIdentifier) {
++ int newToken = atomStrings.getAtom(ppToken.name);
++ if (newToken > 0)
++ resultToken = newToken;
++ else
++ parseContext.ppError(ppToken.loc, "combined token is invalid", "##", "");
++ }
++ } while (peekContinuedPasting(resultToken));
++ }
++
++ return resultToken;
++ }
++
++ // Checks if we've seen balanced #if...#endif
++ void TPpContext::missingEndifCheck()
++ {
++ if (ifdepth > 0)
++ parseContext.ppError(parseContext.getCurrentLoc(), "missing #endif", "", "");
++ }
++
++ } // end namespace glslang
++ } // namespace QtShaderTools