1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity high) 4 module dil.lexer.IdTable; 5 6 import dil.lexer.TokensEnum, 7 dil.lexer.Funcs; 8 import dil.Unicode, 9 dil.String; 10 import common; 11 12 public import dil.lexer.Identifier, 13 dil.lexer.IDs, 14 dil.lexer.IDsEnum; 15 16 17 18 19 /// A table for hoarding and retrieving identifiers. 20 class IdTable 21 { 22 /// A set of common, predefined identifiers for fast lookups. 23 static Identifier*[hash_t] staticTable; 24 /// A table that grows with every newly found, unique identifier. 25 Identifier*[hash_t] growingTable; 26 27 alias LookupMethod = Identifier* delegate(hash_t, cstring); 28 /// Looks up idString in the growing table. 29 LookupMethod inGrowing; 30 31 /// Constructs an IdTable object. 32 /// 33 /// Loads keywords and predefined identifiers into the static table. 34 this() 35 { 36 inGrowing = &_inGrowing_unsafe; // Default to unsafe function. 37 38 if (staticTable is null) // Initialize global static table? 39 { 40 foreach (ref id; IDs.getAllIDs()) 41 staticTable[hashOf(id.str)] = &id; 42 staticTable.rehash; 43 } 44 } 45 46 /// Returns true if str is a valid D identifier. 47 static bool isIdentifierString(cstring str) 48 { 49 if (str.length == 0 || isdigit(str[0])) 50 return false; 51 size_t idx; 52 do 53 { 54 auto c = dil.Unicode.decode(str, idx); 55 if (c == ERROR_CHAR || !(isident(c) || !isascii(c) && isUniAlpha(c))) 56 return false; 57 } while (idx < str.length); 58 return true; 59 } 60 61 /// Returns true if str is a keyword or 62 /// a special token (__FILE__, __LINE__ etc.) 63 bool isReservedIdentifier(cstring str) 64 { 65 if (str.length == 0) 66 return false; 67 auto id = inStatic(str); 68 // True if id is in the table and if it's not a normal identifier. 69 return id && id.kind != TOK.Identifier; 70 } 71 72 /// Returns true if this is a valid identifier and if it's not reserved. 73 bool isValidUnreservedIdentifier(cstring str) 74 { 75 return isIdentifierString(str) && !isReservedIdentifier(str); 76 } 77 78 /// Looks up idString in both tables. 79 Identifier* lookup(cstring idString) 80 { 81 auto idHash = hashOf(idString); 82 auto id = inStatic(idHash); 83 assert(!id || idString == id.str, 84 Format("bad hash function:\n ‘{}’ != ‘{}’; hash={}", 85 idString, id.str, idHash)); 86 if (id) 87 return id; 88 return inGrowing(idHash, idString); 89 } 90 91 /// Looks up the hash of an id in the static table. 92 Identifier* inStatic(hash_t idHash) 93 { 94 auto id = idHash in staticTable; 95 return id ? *id : null; 96 } 97 98 /// Looks up idString in the static table. 99 Identifier* inStatic(cstring idString) 100 { 101 auto id = hashOf(idString) in staticTable; 102 return id ? *id : null; 103 } 104 105 /// Sets the thread safety mode of the growing table. 106 void setThreadsafe(bool safe) 107 { 108 inGrowing = safe ? &_inGrowing_safe : &_inGrowing_unsafe; 109 } 110 111 /// Returns true if access to the growing table is thread-safe. 112 bool isThreadsafe() 113 { 114 return inGrowing is &_inGrowing_safe; 115 } 116 117 /// Looks up idString in the table. 118 /// 119 /// Adds idString to the table if not found. 120 private Identifier* _inGrowing_unsafe(hash_t idHash, cstring idString) 121 out(id) 122 { assert(id !is null); } 123 body 124 { 125 auto id = idHash in growingTable; 126 assert(!id || idString == (*id).str, 127 Format("bad hash function:\n ‘{}’ != ‘{}’", idString, (*id).str)); 128 if (id) 129 return *id; 130 auto newID = new Identifier(idString.idup, TOK.Identifier); 131 growingTable[idHash] = newID; 132 return newID; 133 } 134 135 /// Looks up idString in the table. 136 /// 137 /// Adds idString to the table if not found. 138 /// Access to the data structure is synchronized. 139 private Identifier* _inGrowing_safe(hash_t idHash, cstring idString) 140 { 141 synchronized 142 return _inGrowing_unsafe(idHash, idString); 143 } 144 145 /// Counter for anonymous identifiers. 146 static uint anonCount; 147 148 /// Generates an anonymous identifier. 149 /// 150 /// Concatenates prefix with anonCount. 151 /// The identifier is not inserted into the table. 152 Identifier* genAnonymousID(cstring prefix) 153 { 154 auto num = itoa(++anonCount); 155 return new Identifier((prefix ~ num).idup, TOK.Identifier); 156 } 157 158 /// Generates an identifier for an anonymous enum. 159 Identifier* genAnonEnumID() 160 { 161 return genAnonymousID("__anonenum"); 162 } 163 164 /// Generates an identifier for an anonymous class. 165 Identifier* genAnonClassID() 166 { 167 return genAnonymousID("__anonclass"); 168 } 169 170 /// Generates an identifier for an anonymous struct. 171 Identifier* genAnonStructID() 172 { 173 return genAnonymousID("__anonstruct"); 174 } 175 176 /// Generates an identifier for an anonymous union. 177 Identifier* genAnonUnionID() 178 { 179 return genAnonymousID("__anonunion"); 180 } 181 182 /// Generates an identifier for a module which has got no valid name. 183 Identifier* genModuleID() 184 { 185 return genAnonymousID("__module"); 186 } 187 } 188 189 void testIdTable() 190 { 191 // TODO: write benchmark. 192 // Single table 193 194 // Single table. synchronized 195 196 // Two tables. 197 198 // Two tables. synchronized 199 }