diff options
Diffstat (limited to 'chromium/third_party/glslang/src/glslang/MachineIndependent/preprocessor/Pp.cpp')
-rw-r--r-- | chromium/third_party/glslang/src/glslang/MachineIndependent/preprocessor/Pp.cpp | 1151 |
1 files changed, 1151 insertions, 0 deletions
diff --git a/chromium/third_party/glslang/src/glslang/MachineIndependent/preprocessor/Pp.cpp b/chromium/third_party/glslang/src/glslang/MachineIndependent/preprocessor/Pp.cpp new file mode 100644 index 00000000000..35097588c43 --- /dev/null +++ b/chromium/third_party/glslang/src/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -0,0 +1,1151 @@ +// +//Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +//Copyright (C) 2013 LunarG, 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. +\****************************************************************************/ +// +// cpp.c +// + +#define _CRT_SECURE_NO_WARNINGS + +#include <stdarg.h> +#include <stdio.h> +#include <sstream> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +int TPpContext::InitCPP() +{ + pool = mem_CreatePool(0, 0); + + return 1; +} + +// Handle #define +int TPpContext::CPPdefine(TPpToken* ppToken) +{ + MacroSymbol mac; + Symbol *symb; + + // get macro name + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#define", ""); + return token; + } + if (ppToken->loc.string >= 0) { + // We are in user code; check for reserved name use: + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#define"); + } + + // save the original atom + const int defAtom = ppToken->atom; + + // gather parameters to the macro, between (...) + token = scanToken(ppToken); + if (token == '(' && ! ppToken->space) { + int argc = 0; + int args[maxMacroArgs]; + do { + token = scanToken(ppToken); + if (argc == 0 && token == ')') + break; + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "bad argument", "#define", ""); + + return token; + } + // check for duplication of parameter name + bool duplicate = false; + for (int a = 0; a < argc; ++a) { + if (args[a] == ppToken->atom) { + parseContext.ppError(ppToken->loc, "duplicate macro parameter", "#define", ""); + duplicate = true; + break; + } + } + if (! duplicate) { + if (argc < maxMacroArgs) + args[argc++] = ppToken->atom; + else + parseContext.ppError(ppToken->loc, "too many macro parameters", "#define", ""); + } + token = scanToken(ppToken); + } while (token == ','); + if (token != ')') { + parseContext.ppError(ppToken->loc, "missing parenthesis", "#define", ""); + + return token; + } + mac.argc = argc; + mac.args = (int*)mem_Alloc(pool, argc * sizeof(int)); + memcpy(mac.args, args, argc * sizeof(int)); + token = scanToken(ppToken); + } + + // record the definition of the macro + TSourceLoc defineLoc = ppToken->loc; // because ppToken is going to go to the next line before we report errors + mac.body = new TokenStream; + while (token != '\n' && token != EndOfInput) { + RecordToken(mac.body, token, ppToken); + token = scanToken(ppToken); + if (token != '\n' && ppToken->space) + RecordToken(mac.body, ' ', ppToken); + } + + // check for duplicate definition + symb = LookUpSymbol(defAtom); + if (symb) { + if (! symb->mac.undef) { + // Already defined -- need to make sure they are identical: + // "Two replacement lists are identical if and only if the preprocessing tokens in both have the same number, + // ordering, spelling, and white-space separation, where all white-space separations are considered identical." + if (symb->mac.argc != mac.argc) + parseContext.ppError(defineLoc, "Macro redefined; different number of arguments:", "#define", GetAtomString(defAtom)); + else { + for (int argc = 0; argc < mac.argc; argc++) { + if (symb->mac.args[argc] != mac.args[argc]) + parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define", GetAtomString(defAtom)); + } + RewindTokenStream(symb->mac.body); + RewindTokenStream(mac.body); + int newToken; + do { + int oldToken; + TPpToken oldPpToken; + TPpToken newPpToken; + oldToken = ReadToken(symb->mac.body, &oldPpToken); + newToken = ReadToken(mac.body, &newPpToken); + if (oldToken != newToken || oldPpToken != newPpToken) { + parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define", GetAtomString(defAtom)); + break; + } + } while (newToken > 0); + } + } + } else + symb = AddSymbol(defAtom); + + delete symb->mac.body; + symb->mac = mac; + + return '\n'; +} + +// Handle #undef +int TPpContext::CPPundef(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + Symbol *symb; + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#undef", ""); + + return token; + } + + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#undef"); + + symb = LookUpSymbol(ppToken->atom); + if (symb) { + symb->mac.undef = 1; + } + token = scanToken(ppToken); + if (token != '\n') + parseContext.ppError(ppToken->loc, "can only be followed by a single macro name", "#undef", ""); + + return token; +} + +// Handle #else +/* Skip forward to appropriate spot. This is used both +** to skip to a #endif after seeing an #else, AND to skip to a #else, +** #elif, or #endif after a #if/#ifdef/#ifndef/#elif test was false. +*/ +int TPpContext::CPPelse(int matchelse, TPpToken* ppToken) +{ + int atom; + int depth = 0; + int token = scanToken(ppToken); + + while (token != EndOfInput) { + if (token != '#') { + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + if (token == EndOfInput) + return token; + + token = scanToken(ppToken); + continue; + } + + if ((token = scanToken(ppToken)) != PpAtomIdentifier) + continue; + + atom = ppToken->atom; + if (atom == PpAtomIf || atom == PpAtomIfdef || atom == PpAtomIfndef) { + depth++; + ifdepth++; + elsetracker++; + } else if (atom == PpAtomEndif) { + token = extraTokenCheck(atom, ppToken, scanToken(ppToken)); + elseSeen[elsetracker] = false; + --elsetracker; + if (depth == 0) { + // found the #endif we are looking for + if (ifdepth) + --ifdepth; + break; + } + --depth; + --ifdepth; + } else if (matchelse && depth == 0) { + if (atom == PpAtomElse) { + elseSeen[elsetracker] = true; + token = extraTokenCheck(atom, ppToken, scanToken(ppToken)); + // found the #else we are looking for + break; + } else if (atom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + /* we decrement ifdepth here, because CPPif will increment + * it and we really want to leave it alone */ + if (ifdepth) { + --ifdepth; + elseSeen[elsetracker] = false; + --elsetracker; + } + + return CPPif(ppToken); + } + } else if (atom == PpAtomElse) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + else + elseSeen[elsetracker] = true; + token = extraTokenCheck(atom, ppToken, scanToken(ppToken)); + } else if (atom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + } + } + + return token; +} + +// Call when there should be no more tokens left on a line. +int TPpContext::extraTokenCheck(int atom, TPpToken* ppToken, int token) +{ + if (token != '\n' && token != EndOfInput) { + static const char* message = "unexpected tokens following directive"; + + const char* label; + if (atom == PpAtomElse) + label = "#else"; + else if (atom == PpAtomElif) + label = "#elif"; + else if (atom == PpAtomEndif) + label = "#endif"; + else if (atom == PpAtomIf) + label = "#if"; + else if (atom == PpAtomLine) + label = "#line"; + else + label = ""; + + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, label, ""); + else + parseContext.ppError(ppToken->loc, message, label, ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + + return token; +} + +enum eval_prec { + MIN_PRECEDENCE, + COND, LOGOR, LOGAND, OR, XOR, AND, EQUAL, RELATION, SHIFT, ADD, MUL, UNARY, + MAX_PRECEDENCE +}; + +namespace { + + int op_logor(int a, int b) { return a || b; } + int op_logand(int a, int b) { return a && b; } + int op_or(int a, int b) { return a | b; } + int op_xor(int a, int b) { return a ^ b; } + int op_and(int a, int b) { return a & b; } + int op_eq(int a, int b) { return a == b; } + int op_ne(int a, int b) { return a != b; } + int op_ge(int a, int b) { return a >= b; } + int op_le(int a, int b) { return a <= b; } + int op_gt(int a, int b) { return a > b; } + int op_lt(int a, int b) { return a < b; } + int op_shl(int a, int b) { return a << b; } + int op_shr(int a, int b) { return a >> b; } + int op_add(int a, int b) { return a + b; } + int op_sub(int a, int b) { return a - b; } + int op_mul(int a, int b) { return a * b; } + int op_div(int a, int b) { return a / b; } + int op_mod(int a, int b) { return a % b; } + int op_pos(int a) { return a; } + int op_neg(int a) { return -a; } + int op_cmpl(int a) { return ~a; } + int op_not(int a) { return !a; } + +}; + +struct TBinop { + int token, precedence, (*op)(int, int); +} binop[] = { + { PpAtomOr, LOGOR, op_logor }, + { PpAtomAnd, LOGAND, op_logand }, + { '|', OR, op_or }, + { '^', XOR, op_xor }, + { '&', AND, op_and }, + { PpAtomEQ, EQUAL, op_eq }, + { PpAtomNE, EQUAL, op_ne }, + { '>', RELATION, op_gt }, + { PpAtomGE, RELATION, op_ge }, + { '<', RELATION, op_lt }, + { PpAtomLE, RELATION, op_le }, + { PpAtomLeft, SHIFT, op_shl }, + { PpAtomRight, SHIFT, op_shr }, + { '+', ADD, op_add }, + { '-', ADD, op_sub }, + { '*', MUL, op_mul }, + { '/', MUL, op_div }, + { '%', MUL, op_mod }, +}; + +struct TUnop { + int token, (*op)(int); +} unop[] = { + { '+', op_pos }, + { '-', op_neg }, + { '~', op_cmpl }, + { '!', op_not }, +}; + +#define NUM_ELEMENTS(A) (sizeof(A) / sizeof(A[0])) + +int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + TSourceLoc loc = ppToken->loc; // because we sometimes read the newline before reporting the error + if (token == PpAtomIdentifier) { + if (ppToken->atom == PpAtomDefined) { + bool needclose = 0; + token = scanToken(ppToken); + if (token == '(') { + needclose = true; + token = scanToken(ppToken); + } + if (token != PpAtomIdentifier) { + parseContext.ppError(loc, "incorrect directive, expected identifier", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + Symbol* s = LookUpSymbol(ppToken->atom); + res = s ? ! s->mac.undef : 0; + token = scanToken(ppToken); + if (needclose) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + token = evalToToken(token, shortCircuit, res, err, ppToken); + return eval(token, precedence, shortCircuit, res, err, ppToken); + } + } else if (token == PpAtomConstInt) { + res = ppToken->ival; + token = scanToken(ppToken); + } else if (token == '(') { + token = scanToken(ppToken); + token = eval(token, MIN_PRECEDENCE, shortCircuit, res, err, ppToken); + if (! err) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + int op = NUM_ELEMENTS(unop) - 1; + for (; op >= 0; op--) { + if (unop[op].token == token) + break; + } + if (op >= 0) { + token = scanToken(ppToken); + token = eval(token, UNARY, shortCircuit, res, err, ppToken); + res = unop[op].op(res); + } else { + parseContext.ppError(loc, "bad expression", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + } + + token = evalToToken(token, shortCircuit, res, err, ppToken); + + // Perform evaluation of binary operation, if there is one, otherwise we are done. + while (! err) { + if (token == ')' || token == '\n') + break; + int op; + for (op = NUM_ELEMENTS(binop) - 1; op >= 0; op--) { + if (binop[op].token == token) + break; + } + if (op < 0 || binop[op].precedence <= precedence) + break; + int leftSide = res; + + // Setup short-circuiting, needed for ES, unless already in a short circuit. + // (Once in a short-circuit, can't turn off again, until that whole subexpression is done. + if (! shortCircuit) { + if ((token == PpAtomOr && leftSide == 1) || + (token == PpAtomAnd && leftSide == 0)) + shortCircuit = true; + } + + token = scanToken(ppToken); + token = eval(token, binop[op].precedence, shortCircuit, res, err, ppToken); + + if (binop[op].op == op_div || binop[op].op == op_mod) { + if (res == 0) { + parseContext.ppError(loc, "division by 0", "preprocessor evaluation", ""); + res = 1; + } + } + res = binop[op].op(leftSide, res); + } + + return token; +} + +// Expand macros, skipping empty expansions, to get to the first real token in those expansions. +int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + while (token == PpAtomIdentifier && ppToken->atom != PpAtomDefined) { + int macroReturn = MacroExpand(ppToken->atom, ppToken, true, false); + if (macroReturn == 0) { + parseContext.ppError(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", ""); + err = true; + res = 0; + token = scanToken(ppToken); + break; + } + if (macroReturn == -1) { + if (! shortCircuit && parseContext.profile == EEsProfile) { + const char* message = "undefined macro in expression not allowed in es profile"; + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + else + parseContext.ppError(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + } + } + token = scanToken(ppToken); + } + + return token; +} + +// Handle #if +int TPpContext::CPPif(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + elsetracker++; + ifdepth++; + if (ifdepth > maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if", ""); + return 0; + } + int res = 0; + bool err = false; + token = eval(token, MIN_PRECEDENCE, false, res, err, ppToken); + token = extraTokenCheck(PpAtomIf, ppToken, token); + if (!res && !err) + token = CPPelse(1, ppToken); + + return token; +} + +// Handle #ifdef +int TPpContext::CPPifdef(int defined, TPpToken* ppToken) +{ + int token = scanToken(ppToken); + int name = ppToken->atom; + if (++ifdepth > maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#ifdef", ""); + return 0; + } + elsetracker++; + if (token != PpAtomIdentifier) { + if (defined) + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifdef", ""); + else + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifndef", ""); + } else { + Symbol *s = LookUpSymbol(name); + token = scanToken(ppToken); + if (token != '\n') { + parseContext.ppError(ppToken->loc, "unexpected tokens following #ifdef directive - expected a newline", "#ifdef", ""); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + if (((s && !s->mac.undef) ? 1 : 0) != defined) + token = CPPelse(1, ppToken); + } + + return token; +} + +// Handle #include +int TPpContext::CPPinclude(TPpToken* ppToken) +{ + const TSourceLoc directiveLoc = ppToken->loc; + int token = scanToken(ppToken); + if (token != PpAtomConstString) { + // TODO: handle angle brackets. + parseContext.ppError(directiveLoc, "must be followed by a file designation", "#include", ""); + } else { + // Make a copy of the name because it will be overwritten by the next token scan. + const std::string filename = ppToken->name; + token = scanToken(ppToken); + if (token != '\n' && token != EndOfInput) { + parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", ""); + } else { + TShader::Includer::IncludeResult* res = includer.include(filename.c_str(), TShader::Includer::EIncludeRelative, currentSourceFile.c_str(), includeStack.size() + 1); + if (res && !res->file_name.empty()) { + if (res->file_data && res->file_length) { + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream prologue; + std::ostringstream epilogue; + prologue << "#line " << forNextLine << " " << "\"" << res->file_name << "\"\n"; + epilogue << (res->file_data[res->file_length - 1] == '\n'? "" : "\n") << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; + pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this)); + } + // At EOF, there's no "current" location anymore. + if (token != EndOfInput) parseContext.setCurrentColumn(0); + // Don't accidentally return EndOfInput, which will end all preprocessing. + return '\n'; + } else { + std::string message = + res ? std::string(res->file_data, res->file_length) + : std::string("Could not process include directive"); + parseContext.ppError(directiveLoc, message.c_str(), "#include", ""); + if (res) { + includer.releaseInclude(res); + } + } + } + } + return token; +} + +// Handle #line +int TPpContext::CPPline(TPpToken* ppToken) +{ + // "#line must have, after macro substitution, one of the following forms: + // "#line line + // "#line line source-string-number" + + int token = scanToken(ppToken); + const TSourceLoc directiveLoc = ppToken->loc; + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must by followed by an integral literal", "#line", ""); + return token; + } + + int lineRes = 0; // Line number after macro expansion. + int lineToken = 0; + bool hasFile = false; + int fileRes = 0; // Source file number after macro expansion. + const char* sourceName = nullptr; // Optional source file name. + bool lineErr = false; + bool fileErr = false; + token = eval(token, MIN_PRECEDENCE, false, lineRes, lineErr, ppToken); + if (! lineErr) { + lineToken = lineRes; + if (token == '\n') + ++lineRes; + + if (parseContext.lineDirectiveShouldSetNextLine()) + --lineRes; + parseContext.setCurrentLine(lineRes); + + if (token != '\n') { + if (token == PpAtomConstString) { + parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); + // We need to save a copy of the string instead of pointing + // to the name field of the token since the name field + // will likely be overwritten by the next token scan. + sourceName = GetAtomString(LookUpAddString(ppToken->name)); + parseContext.setCurrentSourceName(sourceName); + hasFile = true; + token = scanToken(ppToken); + } else { + token = eval(token, MIN_PRECEDENCE, false, fileRes, fileErr, ppToken); + if (! fileErr) { + parseContext.setCurrentString(fileRes); + hasFile = true; + } + } + } + } + if (!fileErr && !lineErr) { + parseContext.notifyLineDirective(directiveLoc.line, lineToken, hasFile, fileRes, sourceName); + } + token = extraTokenCheck(PpAtomLine, ppToken, token); + + return token; +} + +// Handle #error +int TPpContext::CPPerror(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + std::string message; + TSourceLoc loc = ppToken->loc; + + while (token != '\n' && token != EndOfInput) { + if (token == PpAtomConstInt || token == PpAtomConstUint || + token == PpAtomConstInt64 || token == PpAtomConstUint64 || + token == PpAtomConstFloat || token == PpAtomConstDouble) { + message.append(ppToken->name); + } else if (token == PpAtomIdentifier || token == PpAtomConstString) { + message.append(ppToken->name); + } else { + message.append(GetAtomString(token)); + } + message.append(" "); + token = scanToken(ppToken); + } + parseContext.notifyErrorDirective(loc.line, message.c_str()); + //store this msg into the shader's information log..set the Compile Error flag!!!! + parseContext.ppError(loc, message.c_str(), "#error", ""); + + return '\n'; +} + +// Handle #pragma +int TPpContext::CPPpragma(TPpToken* ppToken) +{ + char SrcStrName[2]; + TVector<TString> tokens; + + TSourceLoc loc = ppToken->loc; // because we go to the next line before processing + int token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) { + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstFloat: + case PpAtomConstDouble: + tokens.push_back(ppToken->name); + break; + default: + SrcStrName[0] = (char)token; + SrcStrName[1] = '\0'; + tokens.push_back(SrcStrName); + } + token = scanToken(ppToken); + } + + if (token == EndOfInput) + parseContext.ppError(loc, "directive must end with a newline", "#pragma", ""); + else + parseContext.handlePragma(loc, tokens); + + return token; +} + +// #version: This is just for error checking: the version and profile are decided before preprocessing starts +int TPpContext::CPPversion(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (errorOnVersion || versionSeen) + parseContext.ppError(ppToken->loc, "must occur first in shader", "#version", ""); + versionSeen = true; + + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + return token; + } + + if (token != PpAtomConstInt) + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + ppToken->ival = atoi(ppToken->name); + int versionNumber = ppToken->ival; + int line = ppToken->loc.line; + token = scanToken(ppToken); + + if (token == '\n') { + parseContext.notifyVersion(line, versionNumber, nullptr); + return token; + } else { + if (ppToken->atom != PpAtomCore && + ppToken->atom != PpAtomCompatibility && + ppToken->atom != PpAtomEs) + parseContext.ppError(ppToken->loc, "bad profile name; use es, core, or compatibility", "#version", ""); + parseContext.notifyVersion(line, versionNumber, ppToken->name); + token = scanToken(ppToken); + + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "bad tokens following profile -- expected newline", "#version", ""); + } + + return token; +} + +// Handle #extension +int TPpContext::CPPextension(TPpToken* ppToken) +{ + int line = ppToken->loc.line; + int token = scanToken(ppToken); + char extensionName[MaxTokenLength + 1]; + + if (token=='\n') { + parseContext.ppError(ppToken->loc, "extension name not specified", "#extension", ""); + return token; + } + + if (token != PpAtomIdentifier) + parseContext.ppError(ppToken->loc, "extension name expected", "#extension", ""); + + assert(strlen(ppToken->name) <= MaxTokenLength); + strcpy(extensionName, ppToken->name); + + token = scanToken(ppToken); + if (token != ':') { + parseContext.ppError(ppToken->loc, "':' missing after extension name", "#extension", ""); + return token; + } + + token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "behavior for extension not specified", "#extension", ""); + return token; + } + + parseContext.updateExtensionBehavior(line, extensionName, ppToken->name); + parseContext.notifyExtensionDirective(line, extensionName, ppToken->name); + + token = scanToken(ppToken); + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "extra tokens -- expected newline", "#extension",""); + + return token; +} + +int TPpContext::readCPPline(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (token == PpAtomIdentifier) { + switch (ppToken->atom) { + case PpAtomDefine: + token = CPPdefine(ppToken); + break; + case PpAtomElse: + if (elsetracker[elseSeen]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + elsetracker[elseSeen] = true; + if (! ifdepth) + parseContext.ppError(ppToken->loc, "mismatched statements", "#else", ""); + token = extraTokenCheck(PpAtomElse, ppToken, scanToken(ppToken)); + token = CPPelse(0, ppToken); + break; + case PpAtomElif: + if (! ifdepth) + parseContext.ppError(ppToken->loc, "mismatched statements", "#elif", ""); + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + // this token is really a dont care, but we still need to eat the tokens + token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + token = CPPelse(0, ppToken); + break; + case PpAtomEndif: + if (! ifdepth) + parseContext.ppError(ppToken->loc, "mismatched statements", "#endif", ""); + else { + elseSeen[elsetracker] = false; + --elsetracker; + --ifdepth; + } + token = extraTokenCheck(PpAtomEndif, ppToken, scanToken(ppToken)); + break; + case PpAtomIf: + token = CPPif(ppToken); + break; + case PpAtomIfdef: + token = CPPifdef(1, ppToken); + break; + case PpAtomIfndef: + token = CPPifdef(0, ppToken); + break; + case PpAtomInclude: + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include"); + token = CPPinclude(ppToken); + break; + case PpAtomLine: + token = CPPline(ppToken); + break; + case PpAtomPragma: + token = CPPpragma(ppToken); + break; + case PpAtomUndef: + token = CPPundef(ppToken); + break; + case PpAtomError: + token = CPPerror(ppToken); + break; + case PpAtomVersion: + token = CPPversion(ppToken); + break; + case PpAtomExtension: + token = CPPextension(ppToken); + break; + default: + parseContext.ppError(ppToken->loc, "invalid directive:", "#", ppToken->name); + break; + } + } else if (token != '\n' && token != EndOfInput) + parseContext.ppError(ppToken->loc, "invalid directive", "#", ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + return token; +} + +TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream* a, TPpToken* ppToken, bool newLineOkay) +{ + int token; + TokenStream *n; + RewindTokenStream(a); + do { + token = ReadToken(a, ppToken); + if (token == PpAtomIdentifier && LookUpSymbol(ppToken->atom)) + break; + } while (token != EndOfInput); + + if (token == EndOfInput) + return a; + + n = new TokenStream; + pushInput(new tMarkerInput(this)); + pushTokenStreamInput(a); + while ((token = scanToken(ppToken)) != tMarkerInput::marker) { + if (token == PpAtomIdentifier && MacroExpand(ppToken->atom, ppToken, false, newLineOkay) != 0) + continue; + RecordToken(n, token, ppToken); + } + popInput(); + delete a; + + return n; +} + +// +// Return the next token for a macro expansion, handling macro args. +// +int TPpContext::tMacroInput::scan(TPpToken* ppToken) +{ + int token; + do { + token = pp->ReadToken(mac->body, ppToken); + } while (token == ' '); // handle white space in macro + + // TODO: preprocessor: properly handle whitespace (or lack of it) between tokens when expanding + if (token == PpAtomIdentifier) { + int i; + for (i = mac->argc - 1; i >= 0; i--) + if (mac->args[i] == ppToken->atom) + break; + if (i >= 0) { + pp->pushTokenStreamInput(args[i]); + + return pp->scanToken(ppToken); + } + } + + if (token == EndOfInput) + mac->busy = 0; + + return token; +} + +// return a textual zero, for scanning a macro that was never defined +int TPpContext::tZeroInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + strcpy(ppToken->name, "0"); + ppToken->ival = 0; + ppToken->space = false; + done = true; + + return PpAtomConstInt; +} + +// +// Check an identifier (atom) to see if it is a macro that should be expanded. +// If it is, and defined, push a tInput that will produce the appropriate expansion +// and return 1. +// If it is, but undefined, and expandUndef is requested, push a tInput that will +// expand to 0 and return -1. +// Otherwise, return 0 to indicate no expansion, which is not necessarily an error. +// +int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef, bool newLineOkay) +{ + ppToken->space = false; + switch (atom) { + case PpAtomLineMacro: + ppToken->ival = parseContext.getCurrentLoc().line; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return 1; + + case PpAtomFileMacro: { + if (parseContext.getCurrentLoc().name) + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); + ppToken->ival = parseContext.getCurrentLoc().string; + snprintf(ppToken->name, sizeof(ppToken->name), "%s", ppToken->loc.getStringNameOrNum().c_str()); + UngetToken(PpAtomConstInt, ppToken); + return 1; + } + + case PpAtomVersionMacro: + ppToken->ival = parseContext.version; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return 1; + + default: + break; + } + + Symbol *sym = LookUpSymbol(atom); + int token; + int depth = 0; + + // no recursive expansions + if (sym && sym->mac.busy) + return 0; + + // not expanding undefined macros + if ((! sym || sym->mac.undef) && ! expandUndef) + return 0; + + // 0 is the value of an undefined macro + if ((! sym || sym->mac.undef) && expandUndef) { + pushInput(new tZeroInput(this)); + return -1; + } + + tMacroInput *in = new tMacroInput(this); + + TSourceLoc loc = ppToken->loc; // in case we go to the next line before discovering the error + in->mac = &sym->mac; + if (sym->mac.args) { + token = scanToken(ppToken); + if (newLineOkay) { + while (token == '\n') + token = scanToken(ppToken); + } + if (token != '(') { + parseContext.ppError(loc, "expected '(' following", "macro expansion", GetAtomString(atom)); + UngetToken(token, ppToken); + ppToken->atom = atom; + + delete in; + return 0; + } + in->args.resize(in->mac->argc); + for (int i = 0; i < in->mac->argc; i++) + in->args[i] = new TokenStream; + int arg = 0; + bool tokenRecorded = false; + do { + depth = 0; + while (1) { + token = scanToken(ppToken); + if (token == EndOfInput) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", GetAtomString(atom)); + delete in; + return 0; + } + if (token == '\n') { + if (! newLineOkay) { + parseContext.ppError(loc, "End of line in macro substitution:", "macro expansion", GetAtomString(atom)); + delete in; + return 0; + } + continue; + } + if (token == '#') { + parseContext.ppError(ppToken->loc, "unexpected '#'", "macro expansion", GetAtomString(atom)); + delete in; + return 0; + } + if (in->mac->argc == 0 && token != ')') + break; + if (depth == 0 && (token == ',' || token == ')')) + break; + if (token == '(') + depth++; + if (token == ')') + depth--; + RecordToken(in->args[arg], token, ppToken); + tokenRecorded = true; + } + if (token == ')') { + if (in->mac->argc == 1 && tokenRecorded == 0) + break; + arg++; + break; + } + arg++; + } while (arg < in->mac->argc); + + if (arg < in->mac->argc) + parseContext.ppError(loc, "Too few args in Macro", "macro expansion", GetAtomString(atom)); + else if (token != ')') { + depth=0; + while (token != EndOfInput && (depth > 0 || token != ')')) { + if (token == ')') + depth--; + token = scanToken(ppToken); + if (token == '(') + depth++; + } + + if (token == EndOfInput) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", GetAtomString(atom)); + delete in; + return 0; + } + parseContext.ppError(loc, "Too many args in macro", "macro expansion", GetAtomString(atom)); + } + for (int i = 0; i < in->mac->argc; i++) + in->args[i] = PrescanMacroArg(in->args[i], ppToken, newLineOkay); + } + + pushInput(in); + sym->mac.busy = 1; + RewindTokenStream(sym->mac.body); + + return 1; +} + +} // end namespace glslang |