1 // 2 //Copyright (C) 2002-2005 3Dlabs Inc. Ltd. 3 //Copyright (C) 2013 LunarG, Inc. 4 // 5 //All rights reserved. 6 // 7 //Redistribution and use in source and binary forms, with or without 8 //modification, are permitted provided that the following conditions 9 //are met: 10 // 11 // Redistributions of source code must retain the above copyright 12 // notice, this list of conditions and the following disclaimer. 13 // 14 // Redistributions in binary form must reproduce the above 15 // copyright notice, this list of conditions and the following 16 // disclaimer in the documentation and/or other materials provided 17 // with the distribution. 18 // 19 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 20 // contributors may be used to endorse or promote products derived 21 // from this software without specific prior written permission. 22 // 23 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 //POSSIBILITY OF SUCH DAMAGE. 35 // 36 37 #ifndef _SYMBOL_TABLE_INCLUDED_ 38 #define _SYMBOL_TABLE_INCLUDED_ 39 40 // 41 // Symbol table for parsing. Has these design characteristics: 42 // 43 // * Same symbol table can be used to compile many shaders, to preserve 44 // effort of creating and loading with the large numbers of built-in 45 // symbols. 46 // 47 // --> This requires a copy mechanism, so initial pools used to create 48 // the shared information can be popped. Done through "clone" 49 // methods. 50 // 51 // * Name mangling will be used to give each function a unique name 52 // so that symbol table lookups are never ambiguous. This allows 53 // a simpler symbol table structure. 54 // 55 // * Pushing and popping of scope, so symbol table will really be a stack 56 // of symbol tables. Searched from the top, with new inserts going into 57 // the top. 58 // 59 // * Constants: Compile time constant symbols will keep their values 60 // in the symbol table. The parser can substitute constants at parse 61 // time, including doing constant folding and constant propagation. 62 // 63 // * No temporaries: Temporaries made from operations (+, --, .xy, etc.) 64 // are tracked in the intermediate representation, not the symbol table. 65 // 66 67 #include "../Include/Common.h" 68 #include "../Include/intermediate.h" 69 #include "../Include/InfoSink.h" 70 71 namespace glslang { 72 73 // 74 // Symbol base class. (Can build functions or variables out of these...) 75 // 76 77 class TVariable; 78 class TFunction; 79 class TAnonMember; 80 81 class TSymbol { 82 public: 83 POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) 84 explicit TSymbol(const TString *n) : name(n), numExtensions(0), extensions(0), writable(true) { } 85 virtual TSymbol* clone() const = 0; 86 virtual ~TSymbol() { } // rely on all symbol owned memory coming from the pool 87 88 virtual const TString& getName() const { return *name; } 89 virtual void changeName(const TString* newName) { name = newName; } 90 virtual const TString& getMangledName() const { return getName(); } 91 virtual TFunction* getAsFunction() { return 0; } 92 virtual const TFunction* getAsFunction() const { return 0; } 93 virtual TVariable* getAsVariable() { return 0; } 94 virtual const TVariable* getAsVariable() const { return 0; } 95 virtual const TAnonMember* getAsAnonMember() const { return 0; } 96 virtual const TType& getType() const = 0; 97 virtual TType& getWritableType() = 0; 98 virtual void setUniqueId(int id) { uniqueId = id; } 99 virtual int getUniqueId() const { return uniqueId; } 100 virtual void setExtensions(int num, const char* const exts[]) 101 { 102 assert(extensions == 0); 103 assert(num > 0); 104 numExtensions = num; 105 extensions = NewPoolObject(exts[0], num); 106 for (int e = 0; e < num; ++e) 107 extensions[e] = exts[e]; 108 } 109 virtual int getNumExtensions() const { return numExtensions; } 110 virtual const char** getExtensions() const { return extensions; } 111 virtual void dump(TInfoSink &infoSink) const = 0; 112 113 virtual bool isReadOnly() const { return ! writable; } 114 virtual void makeReadOnly() { writable = false; } 115 116 protected: 117 explicit TSymbol(const TSymbol&); 118 TSymbol& operator=(const TSymbol&); 119 120 const TString *name; 121 unsigned int uniqueId; // For cross-scope comparing during code generation 122 123 // For tracking what extensions must be present 124 // (don't use if correct version/profile is present). 125 int numExtensions; 126 const char** extensions; // an array of pointers to existing constant char strings 127 128 // 129 // N.B.: Non-const functions that will be generally used should assert on this, 130 // to avoid overwriting shared symbol-table information. 131 // 132 bool writable; 133 }; 134 135 // 136 // Variable class, meaning a symbol that's not a function. 137 // 138 // There could be a separate class hierarchy for Constant variables; 139 // Only one of int, bool, or float, (or none) is correct for 140 // any particular use, but it's easy to do this way, and doesn't 141 // seem worth having separate classes, and "getConst" can't simply return 142 // different values for different types polymorphically, so this is 143 // just simple and pragmatic. 144 // 145 class TVariable : public TSymbol { 146 public: 147 TVariable(const TString *name, const TType& t, bool uT = false ) 148 : TSymbol(name), 149 userType(uT), 150 constSubtree(nullptr) { type.shallowCopy(t); } 151 virtual TVariable* clone() const; 152 virtual ~TVariable() { } 153 154 virtual TVariable* getAsVariable() { return this; } 155 virtual const TVariable* getAsVariable() const { return this; } 156 virtual const TType& getType() const { return type; } 157 virtual TType& getWritableType() { assert(writable); return type; } 158 virtual bool isUserType() const { return userType; } 159 virtual const TConstUnionArray& getConstArray() const { return constArray; } 160 virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; } 161 virtual void setConstArray(const TConstUnionArray& array) { constArray = array; } 162 virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } 163 virtual TIntermTyped* getConstSubtree() const { return constSubtree; } 164 165 virtual void dump(TInfoSink &infoSink) const; 166 167 protected: 168 explicit TVariable(const TVariable&); 169 TVariable& operator=(const TVariable&); 170 171 TType type; 172 bool userType; 173 // we are assuming that Pool Allocator will free the memory allocated to unionArray 174 // when this object is destroyed 175 176 // TODO: these two should be a union 177 // A variable could be a compile-time constant, or a specialization 178 // constant, or neither, but never both. 179 TConstUnionArray constArray; // for compile-time constant value 180 TIntermTyped* constSubtree; // for specialization constant computation 181 }; 182 183 // 184 // The function sub-class of symbols and the parser will need to 185 // share this definition of a function parameter. 186 // 187 struct TParameter { 188 TString *name; 189 TType* type; 190 void copyParam(const TParameter& param) 191 { 192 if (param.name) 193 name = NewPoolTString(param.name->c_str()); 194 else 195 name = 0; 196 type = param.type->clone(); 197 } 198 }; 199 200 // 201 // The function sub-class of a symbol. 202 // 203 class TFunction : public TSymbol { 204 public: 205 explicit TFunction(TOperator o) : 206 TSymbol(0), 207 op(o), 208 defined(false), prototyped(false) { } 209 TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) : 210 TSymbol(name), 211 mangledName(*name + '('), 212 op(tOp), 213 defined(false), prototyped(false) { returnType.shallowCopy(retType); } 214 virtual TFunction* clone() const; 215 virtual ~TFunction(); 216 217 virtual TFunction* getAsFunction() { return this; } 218 virtual const TFunction* getAsFunction() const { return this; } 219 220 virtual void addParameter(TParameter& p) 221 { 222 assert(writable); 223 parameters.push_back(p); 224 p.type->appendMangledName(mangledName); 225 } 226 227 virtual const TString& getMangledName() const { return mangledName; } 228 virtual const TType& getType() const { return returnType; } 229 virtual TType& getWritableType() { return returnType; } 230 virtual void relateToOperator(TOperator o) { assert(writable); op = o; } 231 virtual TOperator getBuiltInOp() const { return op; } 232 virtual void setDefined() { assert(writable); defined = true; } 233 virtual bool isDefined() const { return defined; } 234 virtual void setPrototyped() { assert(writable); prototyped = true; } 235 virtual bool isPrototyped() const { return prototyped; } 236 237 virtual int getParamCount() const { return static_cast<int>(parameters.size()); } 238 virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; } 239 virtual const TParameter& operator[](int i) const { return parameters[i]; } 240 241 virtual void dump(TInfoSink &infoSink) const; 242 243 protected: 244 explicit TFunction(const TFunction&); 245 TFunction& operator=(const TFunction&); 246 247 typedef TVector<TParameter> TParamList; 248 TParamList parameters; 249 TType returnType; 250 TString mangledName; 251 TOperator op; 252 bool defined; 253 bool prototyped; 254 }; 255 256 // 257 // Members of anonymous blocks are a kind of TSymbol. They are not hidden in 258 // the symbol table behind a container; rather they are visible and point to 259 // their anonymous container. (The anonymous container is found through the 260 // member, not the other way around.) 261 // 262 class TAnonMember : public TSymbol { 263 public: 264 TAnonMember(const TString* n, unsigned int m, const TVariable& a, int an) : TSymbol(n), anonContainer(a), memberNumber(m), anonId(an) { } 265 virtual TAnonMember* clone() const; 266 virtual ~TAnonMember() { } 267 268 virtual const TAnonMember* getAsAnonMember() const { return this; } 269 virtual const TVariable& getAnonContainer() const { return anonContainer; } 270 virtual unsigned int getMemberNumber() const { return memberNumber; } 271 272 virtual const TType& getType() const 273 { 274 const TTypeList& types = *anonContainer.getType().getStruct(); 275 return *types[memberNumber].type; 276 } 277 278 virtual TType& getWritableType() 279 { 280 assert(writable); 281 const TTypeList& types = *anonContainer.getType().getStruct(); 282 return *types[memberNumber].type; 283 } 284 285 virtual int getAnonId() const { return anonId; } 286 virtual void dump(TInfoSink &infoSink) const; 287 288 protected: 289 explicit TAnonMember(const TAnonMember&); 290 TAnonMember& operator=(const TAnonMember&); 291 292 const TVariable& anonContainer; 293 unsigned int memberNumber; 294 int anonId; 295 }; 296 297 class TSymbolTableLevel { 298 public: 299 POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) 300 TSymbolTableLevel() : defaultPrecision(0), anonId(0) { } 301 ~TSymbolTableLevel(); 302 303 bool insert(TSymbol& symbol, bool separateNameSpaces) 304 { 305 // 306 // returning true means symbol was added to the table with no semantic errors 307 // 308 tInsertResult result; 309 const TString& name = symbol.getName(); 310 if (name == "") { 311 // An empty name means an anonymous container, exposing its members to the external scope. 312 // Give it a name and insert its members in the symbol table, pointing to the container. 313 char buf[20]; 314 snprintf(buf, 20, "%s%d", AnonymousPrefix, anonId); 315 symbol.changeName(NewPoolTString(buf)); 316 317 bool isOkay = true; 318 const TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); 319 for (unsigned int m = 0; m < types.size(); ++m) { 320 TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, *symbol.getAsVariable(), anonId); 321 result = level.insert(tLevelPair(member->getMangledName(), member)); 322 if (! result.second) 323 isOkay = false; 324 } 325 326 ++anonId; 327 328 return isOkay; 329 } else { 330 // Check for redefinition errors: 331 // - STL itself will tell us if there is a direct name collision, with name mangling, at this level 332 // - additionally, check for function-redefining-variable name collisions 333 const TString& insertName = symbol.getMangledName(); 334 if (symbol.getAsFunction()) { 335 // make sure there isn't a variable of this name 336 if (! separateNameSpaces && level.find(name) != level.end()) 337 return false; 338 339 // insert, and whatever happens is okay 340 level.insert(tLevelPair(insertName, &symbol)); 341 342 return true; 343 } else { 344 result = level.insert(tLevelPair(insertName, &symbol)); 345 346 return result.second; 347 } 348 } 349 } 350 351 TSymbol* find(const TString& name) const 352 { 353 tLevel::const_iterator it = level.find(name); 354 if (it == level.end()) 355 return 0; 356 else 357 return (*it).second; 358 } 359 360 void findFunctionNameList(const TString& name, TVector<TFunction*>& list) 361 { 362 size_t parenAt = name.find_first_of('('); 363 TString base(name, 0, parenAt + 1); 364 365 tLevel::const_iterator begin = level.lower_bound(base); 366 base[parenAt] = ')'; // assume ')' is lexically after '(' 367 tLevel::const_iterator end = level.upper_bound(base); 368 for (tLevel::const_iterator it = begin; it != end; ++it) 369 list.push_back(it->second->getAsFunction()); 370 } 371 372 // See if there is already a function in the table having the given non-function-style name. 373 bool hasFunctionName(const TString& name) const 374 { 375 tLevel::const_iterator candidate = level.lower_bound(name); 376 if (candidate != level.end()) { 377 const TString& candidateName = (*candidate).first; 378 TString::size_type parenAt = candidateName.find_first_of('('); 379 if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) 380 381 return true; 382 } 383 384 return false; 385 } 386 387 // See if there is a variable at this level having the given non-function-style name. 388 // Return true if name is found, and set variable to true if the name was a variable. 389 bool findFunctionVariableName(const TString& name, bool& variable) const 390 { 391 tLevel::const_iterator candidate = level.lower_bound(name); 392 if (candidate != level.end()) { 393 const TString& candidateName = (*candidate).first; 394 TString::size_type parenAt = candidateName.find_first_of('('); 395 if (parenAt == candidateName.npos) { 396 // not a mangled name 397 if (candidateName == name) { 398 // found a variable name match 399 variable = true; 400 return true; 401 } 402 } else { 403 // a mangled name 404 if (candidateName.compare(0, parenAt, name) == 0) { 405 // found a function name match 406 variable = false; 407 return true; 408 } 409 } 410 } 411 412 return false; 413 } 414 415 // Use this to do a lazy 'push' of precision defaults the first time 416 // a precision statement is seen in a new scope. Leave it at 0 for 417 // when no push was needed. Thus, it is not the current defaults, 418 // it is what to restore the defaults to when popping a level. 419 void setPreviousDefaultPrecisions(const TPrecisionQualifier *p) 420 { 421 // can call multiple times at one scope, will only latch on first call, 422 // as we're tracking the previous scope's values, not the current values 423 if (defaultPrecision != 0) 424 return; 425 426 defaultPrecision = new TPrecisionQualifier[EbtNumTypes]; 427 for (int t = 0; t < EbtNumTypes; ++t) 428 defaultPrecision[t] = p[t]; 429 } 430 431 void getPreviousDefaultPrecisions(TPrecisionQualifier *p) 432 { 433 // can be called for table level pops that didn't set the 434 // defaults 435 if (defaultPrecision == 0 || p == 0) 436 return; 437 438 for (int t = 0; t < EbtNumTypes; ++t) 439 p[t] = defaultPrecision[t]; 440 } 441 442 void relateToOperator(const char* name, TOperator op); 443 void setFunctionExtensions(const char* name, int num, const char* const extensions[]); 444 void dump(TInfoSink &infoSink) const; 445 TSymbolTableLevel* clone() const; 446 void readOnly(); 447 448 protected: 449 explicit TSymbolTableLevel(TSymbolTableLevel&); 450 TSymbolTableLevel& operator=(TSymbolTableLevel&); 451 452 typedef std::map<TString, TSymbol*, std::less<TString>, pool_allocator<std::pair<const TString, TSymbol*> > > tLevel; 453 typedef const tLevel::value_type tLevelPair; 454 typedef std::pair<tLevel::iterator, bool> tInsertResult; 455 456 tLevel level; // named mappings 457 TPrecisionQualifier *defaultPrecision; 458 int anonId; 459 }; 460 461 class TSymbolTable { 462 public: 463 TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), separateNameSpaces(false), adoptedLevels(0) 464 { 465 // 466 // This symbol table cannot be used until push() is called. 467 // 468 } 469 ~TSymbolTable() 470 { 471 // this can be called explicitly; safest to code it so it can be called multiple times 472 473 // don't deallocate levels passed in from elsewhere 474 while (table.size() > adoptedLevels) 475 pop(0); 476 } 477 478 void adoptLevels(TSymbolTable& symTable) 479 { 480 for (unsigned int level = 0; level < symTable.table.size(); ++level) { 481 table.push_back(symTable.table[level]); 482 ++adoptedLevels; 483 } 484 uniqueId = symTable.uniqueId; 485 noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; 486 separateNameSpaces = symTable.separateNameSpaces; 487 } 488 489 // 490 // While level adopting is generic, the methods below enact a the following 491 // convention for levels: 492 // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables 493 // 1: per-stage built-ins, shared across all compiles, but a different copy per stage 494 // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins 495 // 3: user-shader globals 496 // 497 protected: 498 static const int globalLevel = 3; 499 bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels 500 bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals 501 bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals 502 public: 503 bool isEmpty() { return table.size() == 0; } 504 bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } 505 bool atGlobalLevel() { return isGlobalLevel(currentLevel()); } 506 507 void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } 508 void setSeparateNameSpaces() { separateNameSpaces = true; } 509 510 void push() 511 { 512 table.push_back(new TSymbolTableLevel); 513 } 514 515 void pop(TPrecisionQualifier *p) 516 { 517 table[currentLevel()]->getPreviousDefaultPrecisions(p); 518 delete table.back(); 519 table.pop_back(); 520 } 521 522 // 523 // Insert a visible symbol into the symbol table so it can 524 // be found later by name. 525 // 526 // Returns false if the was a name collision. 527 // 528 bool insert(TSymbol& symbol) 529 { 530 symbol.setUniqueId(++uniqueId); 531 532 // make sure there isn't a function of this variable name 533 if (! separateNameSpaces && ! symbol.getAsFunction() && table[currentLevel()]->hasFunctionName(symbol.getName())) 534 return false; 535 536 // check for not overloading or redefining a built-in function 537 if (noBuiltInRedeclarations) { 538 if (atGlobalLevel() && currentLevel() > 0) { 539 if (table[0]->hasFunctionName(symbol.getName())) 540 return false; 541 if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName())) 542 return false; 543 } 544 } 545 546 return table[currentLevel()]->insert(symbol, separateNameSpaces); 547 } 548 549 // 550 // To allocate an internal temporary, which will need to be uniquely 551 // identified by the consumer of the AST, but never need to 552 // found by doing a symbol table search by name, hence allowed an 553 // arbitrary name in the symbol with no worry of collision. 554 // 555 void makeInternalVariable(TSymbol& symbol) 556 { 557 symbol.setUniqueId(++uniqueId); 558 } 559 560 // 561 // Copy a variable or anonymous member's structure from a shared level so that 562 // it can be added (soon after return) to the symbol table where it can be 563 // modified without impacting other users of the shared table. 564 // 565 TSymbol* copyUpDeferredInsert(TSymbol* shared) 566 { 567 if (shared->getAsVariable()) { 568 TSymbol* copy = shared->clone(); 569 copy->setUniqueId(shared->getUniqueId()); 570 return copy; 571 } else { 572 const TAnonMember* anon = shared->getAsAnonMember(); 573 assert(anon); 574 TVariable* container = anon->getAnonContainer().clone(); 575 container->changeName(NewPoolTString("")); 576 container->setUniqueId(anon->getAnonContainer().getUniqueId()); 577 return container; 578 } 579 } 580 581 TSymbol* copyUp(TSymbol* shared) 582 { 583 TSymbol* copy = copyUpDeferredInsert(shared); 584 table[globalLevel]->insert(*copy, separateNameSpaces); 585 if (shared->getAsVariable()) 586 return copy; 587 else { 588 // return the copy of the anonymous member 589 return table[globalLevel]->find(shared->getName()); 590 } 591 } 592 593 TSymbol* find(const TString& name, bool* builtIn = 0, bool *currentScope = 0) 594 { 595 int level = currentLevel(); 596 TSymbol* symbol; 597 do { 598 symbol = table[level]->find(name); 599 --level; 600 } while (symbol == 0 && level >= 0); 601 level++; 602 if (builtIn) 603 *builtIn = isBuiltInLevel(level); 604 if (currentScope) 605 *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals 606 607 return symbol; 608 } 609 610 bool isFunctionNameVariable(const TString& name) const 611 { 612 if (separateNameSpaces) 613 return false; 614 615 int level = currentLevel(); 616 do { 617 bool variable; 618 bool found = table[level]->findFunctionVariableName(name, variable); 619 if (found) 620 return variable; 621 --level; 622 } while (level >= 0); 623 624 return false; 625 } 626 627 void findFunctionNameList(const TString& name, TVector<TFunction*>& list, bool& builtIn) 628 { 629 // For user levels, return the set found in the first scope with a match 630 builtIn = false; 631 int level = currentLevel(); 632 do { 633 table[level]->findFunctionNameList(name, list); 634 --level; 635 } while (list.empty() && level >= globalLevel); 636 637 if (! list.empty()) 638 return; 639 640 // Gather across all built-in levels; they don't hide each other 641 builtIn = true; 642 do { 643 table[level]->findFunctionNameList(name, list); 644 --level; 645 } while (level >= 0); 646 } 647 648 void relateToOperator(const char* name, TOperator op) 649 { 650 for (unsigned int level = 0; level < table.size(); ++level) 651 table[level]->relateToOperator(name, op); 652 } 653 654 void setFunctionExtensions(const char* name, int num, const char* const extensions[]) 655 { 656 for (unsigned int level = 0; level < table.size(); ++level) 657 table[level]->setFunctionExtensions(name, num, extensions); 658 } 659 660 void setVariableExtensions(const char* name, int num, const char* const extensions[]) 661 { 662 TSymbol* symbol = find(TString(name)); 663 if (symbol) 664 symbol->setExtensions(num, extensions); 665 } 666 667 int getMaxSymbolId() { return uniqueId; } 668 void dump(TInfoSink &infoSink) const; 669 void copyTable(const TSymbolTable& copyOf); 670 671 void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } 672 673 void readOnly() 674 { 675 for (unsigned int level = 0; level < table.size(); ++level) 676 table[level]->readOnly(); 677 } 678 679 protected: 680 TSymbolTable(TSymbolTable&); 681 TSymbolTable& operator=(TSymbolTableLevel&); 682 683 int currentLevel() const { return static_cast<int>(table.size()) - 1; } 684 685 std::vector<TSymbolTableLevel*> table; 686 int uniqueId; // for unique identification in code generation 687 bool noBuiltInRedeclarations; 688 bool separateNameSpaces; 689 unsigned int adoptedLevels; 690 }; 691 692 } // end namespace glslang 693 694 #endif // _SYMBOL_TABLE_INCLUDED_ 695