diff options
Diffstat (limited to 'src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp')
-rw-r--r-- | src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp | 1246 |
1 files changed, 1246 insertions, 0 deletions
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp new file mode 100644 index 0000000..f6f52d7 --- /dev/null +++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -0,0 +1,1246 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// 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 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; + + // 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; + } + } + } + + // 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') { + 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; + 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); + } 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 + 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_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[] = { +#ifdef AMD_EXTENSIONS + E_GL_AMD_gpu_shader_int16, +#endif + 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->ival = 0; + ppToken->i64val = 0; + ppToken->space = false; + 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 <= 0x0fffffffffffffffull) { + 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(); + +#ifdef AMD_EXTENSIONS + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); +#endif + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; +#ifdef AMD_EXTENSIONS + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } 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 { + // 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(); + +#ifdef AMD_EXTENSIONS + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); +#endif + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; +#ifdef AMD_EXTENSIONS + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } 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(); + +#ifdef AMD_EXTENSIONS + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); +#endif + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; +#ifdef AMD_EXTENSIONS + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } 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 '"': + // TODO: If this gets enhanced to handle escape sequences, or + // anything that is different than what #include needs, then + // #include needs to use scanHeaderName() for this. + ch = getch(); + while (ch != '"' && ch != '\n' && ch != EndOfInput) { + if (len < MaxTokenLength) { + 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) +{ + 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; + + // expand macros + if (token == PpAtomIdentifier) { + switch (MacroExpand(&ppToken, false, true)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + return EndOfInput; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstFloat: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstDouble: + case PpAtomConstFloat16: + if (ppToken.name[0] == '\0') + continue; + break; + case PpAtomConstString: + if (ifdepth == 0 && parseContext.intermediate.getSource() != EShSourceHlsl) { + // HLSL allows string literals. + parseContext.ppError(ppToken.loc, "string literals not supported", "\"\"", ""); + continue; + } + break; + case '\'': + parseContext.ppError(ppToken.loc, "character literals not supported", "\'", ""); + continue; + default: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(token)); + break; + } + + 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()) { + 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 |