• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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