1 // 2 // Copyright (C) 2013 LunarG, Inc. 3 // Copyright (C) 2015-2018 Google, Inc. 4 // All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions 8 // are met: 9 // 10 // Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // 13 // Redistributions in binary form must reproduce the above 14 // copyright notice, this list of conditions and the following 15 // disclaimer in the documentation and/or other materials provided 16 // with the distribution. 17 // 18 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 19 // contributors may be used to endorse or promote products derived 20 // from this software without specific prior written permission. 21 // 22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 // POSSIBILITY OF SUCH DAMAGE. 34 // 35 /****************************************************************************\ 36 Copyright (c) 2002, NVIDIA Corporation. 37 38 NVIDIA Corporation("NVIDIA") supplies this software to you in 39 consideration of your agreement to the following terms, and your use, 40 installation, modification or redistribution of this NVIDIA software 41 constitutes acceptance of these terms. If you do not agree with these 42 terms, please do not use, install, modify or redistribute this NVIDIA 43 software. 44 45 In consideration of your agreement to abide by the following terms, and 46 subject to these terms, NVIDIA grants you a personal, non-exclusive 47 license, under NVIDIA's copyrights in this original NVIDIA software (the 48 "NVIDIA Software"), to use, reproduce, modify and redistribute the 49 NVIDIA Software, with or without modifications, in source and/or binary 50 forms; provided that if you redistribute the NVIDIA Software, you must 51 retain the copyright notice of NVIDIA, this notice and the following 52 text and disclaimers in all such redistributions of the NVIDIA Software. 53 Neither the name, trademarks, service marks nor logos of NVIDIA 54 Corporation may be used to endorse or promote products derived from the 55 NVIDIA Software without specific prior written permission from NVIDIA. 56 Except as expressly stated in this notice, no other rights or licenses 57 express or implied, are granted by NVIDIA herein, including but not 58 limited to any patent rights that may be infringed by your derivative 59 works or by other works in which the NVIDIA Software may be 60 incorporated. No hardware is licensed hereunder. 61 62 THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT 63 WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 64 INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, 65 NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 66 ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER 67 PRODUCTS. 68 69 IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, 70 INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 71 TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 72 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY 73 OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE 74 NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, 75 TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF 76 NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 77 \****************************************************************************/ 78 79 #ifndef PPCONTEXT_H 80 #define PPCONTEXT_H 81 82 #include <stack> 83 #include <unordered_map> 84 #include <sstream> 85 86 #include "../ParseHelper.h" 87 #include "PpTokens.h" 88 89 namespace glslang { 90 91 class TPpToken { 92 public: TPpToken()93 TPpToken() { clear(); } clear()94 void clear() 95 { 96 space = false; 97 i64val = 0; 98 loc.init(); 99 name[0] = 0; 100 fullyExpanded = false; 101 } 102 103 // Used for comparing macro definitions, so checks what is relevant for that. 104 bool operator==(const TPpToken& right) const 105 { 106 return space == right.space && 107 ival == right.ival && dval == right.dval && i64val == right.i64val && 108 strncmp(name, right.name, MaxTokenLength) == 0; 109 } 110 bool operator!=(const TPpToken& right) const { return ! operator==(right); } 111 112 TSourceLoc loc; 113 // True if a space (for white space or a removed comment) should also be 114 // recognized, in front of the token returned: 115 bool space; 116 117 bool fullyExpanded; 118 // Numeric value of the token: 119 union { 120 int ival; 121 double dval; 122 long long i64val; 123 }; 124 // Text string of the token: 125 char name[MaxTokenLength + 1]; 126 }; 127 128 class TStringAtomMap { 129 // 130 // Implementation is in PpAtom.cpp 131 // 132 // Maintain a bi-directional mapping between relevant preprocessor strings and 133 // "atoms" which a unique integers (small, contiguous, not hash-like) per string. 134 // 135 public: 136 TStringAtomMap(); 137 138 // Map string -> atom. 139 // Return 0 if no existing string. getAtom(const char * s)140 int getAtom(const char* s) const 141 { 142 auto it = atomMap.find(s); 143 return it == atomMap.end() ? 0 : it->second; 144 } 145 146 // Map a new or existing string -> atom, inventing a new atom if necessary. getAddAtom(const char * s)147 int getAddAtom(const char* s) 148 { 149 int atom = getAtom(s); 150 if (atom == 0) { 151 atom = nextAtom++; 152 addAtomFixed(s, atom); 153 } 154 return atom; 155 } 156 157 // Map atom -> string. getString(int atom)158 const char* getString(int atom) const { return stringMap[atom]->c_str(); } 159 160 protected: 161 TStringAtomMap(TStringAtomMap&); 162 TStringAtomMap& operator=(TStringAtomMap&); 163 164 TUnorderedMap<TString, int> atomMap; 165 TVector<const TString*> stringMap; // these point into the TString in atomMap 166 int nextAtom; 167 168 // Bad source characters can lead to bad atoms, so gracefully handle those by 169 // pre-filling the table with them (to avoid if tests later). 170 TString badToken; 171 172 // Add bi-directional mappings: 173 // - string -> atom 174 // - atom -> string addAtomFixed(const char * s,int atom)175 void addAtomFixed(const char* s, int atom) 176 { 177 auto it = atomMap.insert(std::pair<TString, int>(s, atom)).first; 178 if (stringMap.size() < (size_t)atom + 1) 179 stringMap.resize(atom + 100, &badToken); 180 stringMap[atom] = &it->first; 181 } 182 }; 183 184 class TInputScanner; 185 186 enum MacroExpandResult { 187 MacroExpandNotStarted, // macro not expanded, which might not be an error 188 MacroExpandError, // a clear error occurred while expanding, no expansion 189 MacroExpandStarted, // macro expansion process has started 190 MacroExpandUndef // macro is undefined and will be expanded 191 }; 192 193 // This class is the result of turning a huge pile of C code communicating through globals 194 // into a class. This was done to allowing instancing to attain thread safety. 195 // Don't expect too much in terms of OO design. 196 class TPpContext { 197 public: 198 TPpContext(TParseContextBase&, const std::string& rootFileName, TShader::Includer&); 199 virtual ~TPpContext(); 200 201 void setPreamble(const char* preamble, size_t length); 202 203 int tokenize(TPpToken& ppToken); 204 int tokenPaste(int token, TPpToken&); 205 206 class tInput { 207 public: tInput(TPpContext * p)208 tInput(TPpContext* p) : done(false), pp(p) { } ~tInput()209 virtual ~tInput() { } 210 211 virtual int scan(TPpToken*) = 0; 212 virtual int getch() = 0; 213 virtual void ungetch() = 0; peekPasting()214 virtual bool peekPasting() { return false; } // true when about to see ## peekContinuedPasting(int)215 virtual bool peekContinuedPasting(int) { return false; } // true when non-spaced tokens can paste endOfReplacementList()216 virtual bool endOfReplacementList() { return false; } // true when at the end of a macro replacement list (RHS of #define) isMacroInput()217 virtual bool isMacroInput() { return false; } isStringInput()218 virtual bool isStringInput() { return false; } 219 220 // Will be called when we start reading tokens from this instance notifyActivated()221 virtual void notifyActivated() {} 222 // Will be called when we do not read tokens from this instance anymore notifyDeleted()223 virtual void notifyDeleted() {} 224 protected: 225 bool done; 226 TPpContext* pp; 227 }; 228 229 void setInput(TInputScanner& input, bool versionWillBeError); 230 pushInput(tInput * in)231 void pushInput(tInput* in) 232 { 233 inputStack.push_back(in); 234 in->notifyActivated(); 235 } popInput()236 void popInput() 237 { 238 inputStack.back()->notifyDeleted(); 239 delete inputStack.back(); 240 inputStack.pop_back(); 241 } 242 243 // 244 // From PpTokens.cpp 245 // 246 247 // Capture the needed parts of a token stream for macro recording/playback. 248 class TokenStream { 249 public: 250 // Manage a stream of these 'Token', which capture the relevant parts 251 // of a TPpToken, plus its atom. 252 class Token { 253 public: Token(int atom,const TPpToken & ppToken)254 Token(int atom, const TPpToken& ppToken) : 255 atom(atom), 256 space(ppToken.space), 257 i64val(ppToken.i64val), 258 name(ppToken.name) { } get(TPpToken & ppToken)259 int get(TPpToken& ppToken) 260 { 261 ppToken.clear(); 262 ppToken.space = space; 263 ppToken.i64val = i64val; 264 snprintf(ppToken.name, sizeof(ppToken.name), "%s", name.c_str()); 265 return atom; 266 } isAtom(int a)267 bool isAtom(int a) const { return atom == a; } getAtom()268 int getAtom() const { return atom; } nonSpaced()269 bool nonSpaced() const { return !space; } 270 protected: Token()271 Token() {} 272 int atom; 273 bool space; // did a space precede the token? 274 long long i64val; 275 TString name; 276 }; 277 TokenStream()278 TokenStream() : currentPos(0) { } 279 280 void putToken(int token, TPpToken* ppToken); peekToken(int atom)281 bool peekToken(int atom) { return !atEnd() && stream[currentPos].isAtom(atom); } peekContinuedPasting(int atom)282 bool peekContinuedPasting(int atom) 283 { 284 // This is basically necessary because, for example, the PP 285 // tokenizer only accepts valid numeric-literals plus suffixes, so 286 // separates numeric-literals plus bad suffix into two tokens, which 287 // should get both pasted together as one token when token pasting. 288 // 289 // The following code is a bit more generalized than the above example. 290 if (!atEnd() && atom == PpAtomIdentifier && stream[currentPos].nonSpaced()) { 291 switch(stream[currentPos].getAtom()) { 292 case PpAtomConstInt: 293 case PpAtomConstUint: 294 case PpAtomConstInt64: 295 case PpAtomConstUint64: 296 case PpAtomConstInt16: 297 case PpAtomConstUint16: 298 case PpAtomConstFloat: 299 case PpAtomConstDouble: 300 case PpAtomConstFloat16: 301 case PpAtomConstString: 302 case PpAtomIdentifier: 303 return true; 304 default: 305 break; 306 } 307 } 308 309 return false; 310 } 311 int getToken(TParseContextBase&, TPpToken*); atEnd()312 bool atEnd() { return currentPos >= stream.size(); } 313 bool peekTokenizedPasting(bool lastTokenPastes); 314 bool peekUntokenizedPasting(); reset()315 void reset() { currentPos = 0; } 316 317 protected: 318 TVector<Token> stream; 319 size_t currentPos; 320 }; 321 322 // 323 // From Pp.cpp 324 // 325 326 struct MacroSymbol { MacroSymbolMacroSymbol327 MacroSymbol() : functionLike(0), busy(0), undef(0) { } 328 TVector<int> args; 329 TokenStream body; 330 unsigned functionLike : 1; // 0 means object-like, 1 means function-like 331 unsigned busy : 1; 332 unsigned undef : 1; 333 }; 334 335 typedef TMap<int, MacroSymbol> TSymbolMap; 336 TSymbolMap macroDefs; // map atoms to macro definitions lookupMacroDef(int atom)337 MacroSymbol* lookupMacroDef(int atom) 338 { 339 auto existingMacroIt = macroDefs.find(atom); 340 return (existingMacroIt == macroDefs.end()) ? nullptr : &(existingMacroIt->second); 341 } addMacroDef(int atom,MacroSymbol & macroDef)342 void addMacroDef(int atom, MacroSymbol& macroDef) { macroDefs[atom] = macroDef; } 343 344 protected: 345 TPpContext(TPpContext&); 346 TPpContext& operator=(TPpContext&); 347 348 TStringAtomMap atomStrings; 349 char* preamble; // string to parse, all before line 1 of string 0, it is 0 if no preamble 350 int preambleLength; 351 char** strings; // official strings of shader, starting a string 0 line 1 352 size_t* lengths; 353 int numStrings; // how many official strings there are 354 int currentString; // which string we're currently parsing (-1 for preamble) 355 356 // Scanner data: 357 int previous_token; 358 TParseContextBase& parseContext; 359 std::vector<int> lastLineTokens; 360 std::vector<TSourceLoc> lastLineTokenLocs; 361 // Get the next token from *stack* of input sources, popping input sources 362 // that are out of tokens, down until an input source is found that has a token. 363 // Return EndOfInput when there are no more tokens to be found by doing this. scanToken(TPpToken * ppToken)364 int scanToken(TPpToken* ppToken) 365 { 366 int token = EndOfInput; 367 368 while (! inputStack.empty()) { 369 token = inputStack.back()->scan(ppToken); 370 if (token != EndOfInput || inputStack.empty()) 371 break; 372 popInput(); 373 } 374 if (!inputStack.empty() && inputStack.back()->isStringInput()) { 375 if (token == '\n') { 376 bool seenNumSign = false; 377 for (int i = 0; i < (int)lastLineTokens.size() - 1;) { 378 int curPos = i; 379 int curToken = lastLineTokens[i++]; 380 if (curToken == '#' && lastLineTokens[i] == '#') { 381 curToken = PpAtomPaste; 382 i++; 383 } 384 if (curToken == '#') { 385 if (seenNumSign) { 386 parseContext.ppError(lastLineTokenLocs[curPos], "(#) can be preceded in its line only by spaces or horizontal tabs", "#", ""); 387 } else { 388 seenNumSign = true; 389 } 390 } 391 } 392 lastLineTokens.clear(); 393 lastLineTokenLocs.clear(); 394 } else { 395 lastLineTokens.push_back(token); 396 lastLineTokenLocs.push_back(ppToken->loc); 397 } 398 } 399 return token; 400 } getChar()401 int getChar() { return inputStack.back()->getch(); } ungetChar()402 void ungetChar() { inputStack.back()->ungetch(); } peekPasting()403 bool peekPasting() { return !inputStack.empty() && inputStack.back()->peekPasting(); } peekContinuedPasting(int a)404 bool peekContinuedPasting(int a) 405 { 406 return !inputStack.empty() && inputStack.back()->peekContinuedPasting(a); 407 } endOfReplacementList()408 bool endOfReplacementList() { return inputStack.empty() || inputStack.back()->endOfReplacementList(); } isMacroInput()409 bool isMacroInput() { return inputStack.size() > 0 && inputStack.back()->isMacroInput(); } 410 411 static const int maxIfNesting = 65; 412 413 int ifdepth; // current #if-#else-#endif nesting in the cpp.c file (pre-processor) 414 bool elseSeen[maxIfNesting]; // Keep a track of whether an else has been seen at a particular depth 415 int elsetracker; // #if-#else and #endif constructs...Counter. 416 417 class tMacroInput : public tInput { 418 public: tMacroInput(TPpContext * pp)419 tMacroInput(TPpContext* pp) : tInput(pp), prepaste(false), postpaste(false) { } ~tMacroInput()420 virtual ~tMacroInput() 421 { 422 for (size_t i = 0; i < args.size(); ++i) 423 delete args[i]; 424 for (size_t i = 0; i < expandedArgs.size(); ++i) 425 delete expandedArgs[i]; 426 } 427 428 virtual int scan(TPpToken*) override; getch()429 virtual int getch() override { assert(0); return EndOfInput; } ungetch()430 virtual void ungetch() override { assert(0); } peekPasting()431 bool peekPasting() override { return prepaste; } peekContinuedPasting(int a)432 bool peekContinuedPasting(int a) override { return mac->body.peekContinuedPasting(a); } endOfReplacementList()433 bool endOfReplacementList() override { return mac->body.atEnd(); } isMacroInput()434 bool isMacroInput() override { return true; } 435 436 MacroSymbol *mac; 437 TVector<TokenStream*> args; 438 TVector<TokenStream*> expandedArgs; 439 440 protected: 441 bool prepaste; // true if we are just before ## 442 bool postpaste; // true if we are right after ## 443 }; 444 445 class tMarkerInput : public tInput { 446 public: tMarkerInput(TPpContext * pp)447 tMarkerInput(TPpContext* pp) : tInput(pp) { } scan(TPpToken *)448 virtual int scan(TPpToken*) override 449 { 450 if (done) 451 return EndOfInput; 452 done = true; 453 454 return marker; 455 } getch()456 virtual int getch() override { assert(0); return EndOfInput; } ungetch()457 virtual void ungetch() override { assert(0); } 458 static const int marker = -3; 459 }; 460 461 class tZeroInput : public tInput { 462 public: tZeroInput(TPpContext * pp)463 tZeroInput(TPpContext* pp) : tInput(pp) { } 464 virtual int scan(TPpToken*) override; getch()465 virtual int getch() override { assert(0); return EndOfInput; } ungetch()466 virtual void ungetch() override { assert(0); } 467 }; 468 469 std::vector<tInput*> inputStack; 470 bool errorOnVersion; 471 bool versionSeen; 472 473 // 474 // from Pp.cpp 475 // 476 477 // Used to obtain #include content. 478 TShader::Includer& includer; 479 480 int CPPdefine(TPpToken * ppToken); 481 int CPPundef(TPpToken * ppToken); 482 int CPPelse(int matchelse, TPpToken * ppToken); 483 int extraTokenCheck(int atom, TPpToken* ppToken, int token); 484 int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); 485 int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); 486 int CPPif (TPpToken * ppToken); 487 int CPPifdef(int defined, TPpToken * ppToken); 488 int CPPinclude(TPpToken * ppToken); 489 int CPPline(TPpToken * ppToken); 490 int CPPerror(TPpToken * ppToken); 491 int CPPpragma(TPpToken * ppToken); 492 int CPPversion(TPpToken * ppToken); 493 int CPPextension(TPpToken * ppToken); 494 int readCPPline(TPpToken * ppToken); 495 int scanHeaderName(TPpToken* ppToken, char delimit); 496 TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay); 497 MacroExpandResult MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay); 498 499 // 500 // From PpTokens.cpp 501 // 502 void pushTokenStreamInput(TokenStream&, bool pasting = false, bool expanded = false); 503 void UngetToken(int token, TPpToken*); 504 505 class tTokenInput : public tInput { 506 public: tTokenInput(TPpContext * pp,TokenStream * t,bool prepasting,bool expanded)507 tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting, bool expanded) : 508 tInput(pp), 509 tokens(t), 510 lastTokenPastes(prepasting), 511 preExpanded(expanded) { } scan(TPpToken * ppToken)512 virtual int scan(TPpToken *ppToken) override { 513 int token = tokens->getToken(pp->parseContext, ppToken); 514 ppToken->fullyExpanded = preExpanded; 515 if (tokens->atEnd() && token == PpAtomIdentifier) { 516 int macroAtom = pp->atomStrings.getAtom(ppToken->name); 517 MacroSymbol* macro = macroAtom == 0 ? nullptr : pp->lookupMacroDef(macroAtom); 518 if (macro && macro->functionLike) 519 ppToken->fullyExpanded = false; 520 } 521 return token; 522 } getch()523 virtual int getch() override { assert(0); return EndOfInput; } ungetch()524 virtual void ungetch() override { assert(0); } peekPasting()525 virtual bool peekPasting() override { return tokens->peekTokenizedPasting(lastTokenPastes); } peekContinuedPasting(int a)526 bool peekContinuedPasting(int a) override { return tokens->peekContinuedPasting(a); } 527 protected: 528 TokenStream* tokens; 529 bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token 530 bool preExpanded; 531 }; 532 533 class tUngotTokenInput : public tInput { 534 public: tUngotTokenInput(TPpContext * pp,int t,TPpToken * p)535 tUngotTokenInput(TPpContext* pp, int t, TPpToken* p) : tInput(pp), token(t), lval(*p) { } 536 virtual int scan(TPpToken *) override; getch()537 virtual int getch() override { assert(0); return EndOfInput; } ungetch()538 virtual void ungetch() override { assert(0); } 539 protected: 540 int token; 541 TPpToken lval; 542 }; 543 544 // 545 // From PpScanner.cpp 546 // 547 class tStringInput : public tInput { 548 public: tStringInput(TPpContext * pp,TInputScanner & i)549 tStringInput(TPpContext* pp, TInputScanner& i) : tInput(pp), input(&i) { } 550 virtual int scan(TPpToken*) override; isStringInput()551 bool isStringInput() override { return true; } 552 // Scanner used to get source stream characters. 553 // - Escaped newlines are handled here, invisibly to the caller. 554 // - All forms of newline are handled, and turned into just a '\n'. getch()555 int getch() override 556 { 557 int ch = input->get(); 558 559 if (ch == '\\') { 560 // Move past escaped newlines, as many as sequentially exist 561 do { 562 if (input->peek() == '\r' || input->peek() == '\n') { 563 bool allowed = pp->parseContext.lineContinuationCheck(input->getSourceLoc(), pp->inComment); 564 if (! allowed && pp->inComment) 565 return '\\'; 566 567 // escape one newline now 568 ch = input->get(); 569 int nextch = input->get(); 570 if (ch == '\r' && nextch == '\n') 571 ch = input->get(); 572 else 573 ch = nextch; 574 } else 575 return '\\'; 576 } while (ch == '\\'); 577 } 578 579 // handle any non-escaped newline 580 if (ch == '\r' || ch == '\n') { 581 if (ch == '\r' && input->peek() == '\n') 582 input->get(); 583 return '\n'; 584 } 585 586 return ch; 587 } 588 589 // Scanner used to backup the source stream characters. Newlines are 590 // handled here, invisibly to the caller, meaning have to undo exactly 591 // what getch() above does (e.g., don't leave things in the middle of a 592 // sequence of escaped newlines). ungetch()593 void ungetch() override 594 { 595 input->unget(); 596 597 do { 598 int ch = input->peek(); 599 if (ch == '\r' || ch == '\n') { 600 if (ch == '\n') { 601 // correct for two-character newline 602 input->unget(); 603 if (input->peek() != '\r') 604 input->get(); 605 } 606 // now in front of a complete newline, move past an escape character 607 input->unget(); 608 if (input->peek() == '\\') 609 input->unget(); 610 else { 611 input->get(); 612 break; 613 } 614 } else 615 break; 616 } while (true); 617 } 618 619 protected: 620 TInputScanner* input; 621 }; 622 623 // Holds a reference to included file data, as well as a 624 // prologue and an epilogue string. This can be scanned using the tInput 625 // interface and acts as a single source string. 626 class TokenizableIncludeFile : public tInput { 627 public: 628 // Copies prologue and epilogue. The includedFile must remain valid 629 // until this TokenizableIncludeFile is no longer used. TokenizableIncludeFile(const TSourceLoc & startLoc,const std::string & prologue,TShader::Includer::IncludeResult * includedFile,const std::string & epilogue,TPpContext * pp)630 TokenizableIncludeFile(const TSourceLoc& startLoc, 631 const std::string& prologue, 632 TShader::Includer::IncludeResult* includedFile, 633 const std::string& epilogue, 634 TPpContext* pp) 635 : tInput(pp), 636 prologue_(prologue), 637 epilogue_(epilogue), 638 includedFile_(includedFile), 639 scanner(3, strings, lengths, nullptr, 0, 0, true), 640 prevScanner(nullptr), 641 stringInput(pp, scanner) 642 { 643 strings[0] = prologue_.data(); 644 strings[1] = includedFile_->headerData; 645 strings[2] = epilogue_.data(); 646 647 lengths[0] = prologue_.size(); 648 lengths[1] = includedFile_->headerLength; 649 lengths[2] = epilogue_.size(); 650 651 scanner.setLine(startLoc.line); 652 scanner.setString(startLoc.string); 653 654 scanner.setFile(startLoc.getFilenameStr(), 0); 655 scanner.setFile(startLoc.getFilenameStr(), 1); 656 scanner.setFile(startLoc.getFilenameStr(), 2); 657 } 658 659 // tInput methods: scan(TPpToken * t)660 int scan(TPpToken* t) override { return stringInput.scan(t); } getch()661 int getch() override { return stringInput.getch(); } ungetch()662 void ungetch() override { stringInput.ungetch(); } 663 notifyActivated()664 void notifyActivated() override 665 { 666 prevScanner = pp->parseContext.getScanner(); 667 pp->parseContext.setScanner(&scanner); 668 pp->push_include(includedFile_); 669 } 670 notifyDeleted()671 void notifyDeleted() override 672 { 673 pp->parseContext.setScanner(prevScanner); 674 pp->pop_include(); 675 } 676 677 private: 678 TokenizableIncludeFile& operator=(const TokenizableIncludeFile&); 679 680 // Stores the prologue for this string. 681 const std::string prologue_; 682 683 // Stores the epilogue for this string. 684 const std::string epilogue_; 685 686 // Points to the IncludeResult that this TokenizableIncludeFile represents. 687 TShader::Includer::IncludeResult* includedFile_; 688 689 // Will point to prologue_, includedFile_->headerData and epilogue_ 690 // This is passed to scanner constructor. 691 // These do not own the storage and it must remain valid until this 692 // object has been destroyed. 693 const char* strings[3]; 694 // Length of str_, passed to scanner constructor. 695 size_t lengths[3]; 696 // Scans over str_. 697 TInputScanner scanner; 698 // The previous effective scanner before the scanner in this instance 699 // has been activated. 700 TInputScanner* prevScanner; 701 // Delegate object implementing the tInput interface. 702 tStringInput stringInput; 703 }; 704 705 int ScanFromString(char* s); 706 void missingEndifCheck(); 707 int lFloatConst(int len, int ch, TPpToken* ppToken); 708 int characterLiteral(TPpToken* ppToken); 709 push_include(TShader::Includer::IncludeResult * result)710 void push_include(TShader::Includer::IncludeResult* result) 711 { 712 currentSourceFile = result->headerName; 713 includeStack.push(result); 714 } 715 pop_include()716 void pop_include() 717 { 718 TShader::Includer::IncludeResult* include = includeStack.top(); 719 includeStack.pop(); 720 includer.releaseInclude(include); 721 if (includeStack.empty()) { 722 currentSourceFile = rootFileName; 723 } else { 724 currentSourceFile = includeStack.top()->headerName; 725 } 726 } 727 728 bool inComment; 729 std::string rootFileName; 730 std::stack<TShader::Includer::IncludeResult*> includeStack; 731 std::string currentSourceFile; 732 733 std::istringstream strtodStream; 734 bool disableEscapeSequences; 735 }; 736 737 } // end namespace glslang 738 739 #endif // PPCONTEXT_H 740