1 /** 2 * Defines an identifier, which is the name of a `Dsymbol`. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d, _identifier.d) 8 * Documentation: https://dlang.org/phobos/dmd_identifier.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/identifier.d 10 */ 11 12 module dmd.identifier; 13 14 import core.stdc.ctype; 15 import core.stdc.stdio; 16 import core.stdc..string; 17 import dmd.globals; 18 import dmd.id; 19 import dmd.root.outbuffer; 20 import dmd.root.rootobject; 21 import dmd.root..string; 22 import dmd.root.stringtable; 23 import dmd.tokens; 24 import dmd.utf; 25 26 27 /*********************************************************** 28 */ 29 extern (C++) final class Identifier : RootObject 30 { 31 private: 32 const int value; 33 const char[] name; 34 35 public: 36 /** 37 Construct an identifier from a D slice 38 39 Note: Since `name` needs to be `\0` terminated for `toChars`, 40 no slice overload is provided yet. 41 42 Params: 43 name = the identifier name 44 There must be `'\0'` at `name[length]`. 45 length = the length of `name`, excluding the terminating `'\0'` 46 value = Identifier value (e.g. `Id.unitTest`) or `TOK.identifier` 47 */ 48 extern (D) this(const(char)* name, size_t length, int value) nothrow 49 { 50 //printf("Identifier('%s', %d)\n", name, value); 51 this.name = name[0 .. length]; 52 this.value = value; 53 } 54 55 extern (D) this(const(char)[] name, int value) nothrow 56 { 57 //printf("Identifier('%.*s', %d)\n", cast(int)name.length, name.ptr, value); 58 this.name = name; 59 this.value = value; 60 } 61 62 extern (D) this(const(char)* name) nothrow 63 { 64 //printf("Identifier('%s', %d)\n", name, value); 65 this(name.toDString(), TOK.identifier); 66 } 67 68 /// Sentinel for an anonymous identifier. 69 static Identifier anonymous() nothrow 70 { 71 __gshared Identifier anonymous; 72 73 if (anonymous) 74 return anonymous; 75 76 return anonymous = new Identifier("__anonymous", TOK.identifier); 77 } 78 79 static Identifier create(const(char)* name) nothrow 80 { 81 return new Identifier(name); 82 } 83 84 nothrow: 85 override const(char)* toChars() const pure 86 { 87 return name.ptr; 88 } 89 90 extern (D) override const(char)[] toString() const pure 91 { 92 return name; 93 } 94 95 int getValue() const pure 96 { 97 return value; 98 } 99 100 const(char)* toHChars2() const 101 { 102 const(char)* p = null; 103 if (this == Id.ctor) 104 p = "this"; 105 else if (this == Id.dtor) 106 p = "~this"; 107 else if (this == Id.unitTest) 108 p = "unittest"; 109 else if (this == Id.dollar) 110 p = "$"; 111 else if (this == Id.withSym) 112 p = "with"; 113 else if (this == Id.result) 114 p = "result"; 115 else if (this == Id.returnLabel) 116 p = "return"; 117 else 118 { 119 p = toChars(); 120 if (*p == '_') 121 { 122 if (strncmp(p, "_staticCtor", 11) == 0) 123 p = "static this"; 124 else if (strncmp(p, "_staticDtor", 11) == 0) 125 p = "static ~this"; 126 else if (strncmp(p, "__invariant", 11) == 0) 127 p = "invariant"; 128 } 129 } 130 return p; 131 } 132 133 override DYNCAST dyncast() const 134 { 135 return DYNCAST.identifier; 136 } 137 138 private extern (D) __gshared StringTable!Identifier stringtable; 139 140 extern(D) static Identifier generateId(const(char)[] prefix) 141 { 142 __gshared size_t i; 143 return generateId(prefix, ++i); 144 } 145 146 extern(D) static Identifier generateId(const(char)[] prefix, size_t i) 147 { 148 OutBuffer buf; 149 buf.write(prefix); 150 buf.print(i); 151 return idPool(buf[]); 152 } 153 154 /*************************************** 155 * Generate deterministic named identifier based on a source location, 156 * such that the name is consistent across multiple compilations. 157 * A new unique name is generated. If the prefix+location is already in 158 * the stringtable, an extra suffix is added (starting the count at "_1"). 159 * 160 * Params: 161 * prefix = first part of the identifier name. 162 * loc = source location to use in the identifier name. 163 * Returns: 164 * Identifier (inside Identifier.idPool) with deterministic name based 165 * on the source location. 166 */ 167 extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc) 168 { 169 // generate `<prefix>_L<line>_C<col>` 170 OutBuffer idBuf; 171 idBuf.writestring(prefix); 172 idBuf.writestring("_L"); 173 idBuf.print(loc.linnum); 174 idBuf.writestring("_C"); 175 idBuf.print(loc.charnum); 176 177 /** 178 * Make sure the identifiers are unique per filename, i.e., per module/mixin 179 * (`path/to/foo.d` and `path/to/foo.d-mixin-<line>`). See issues 180 * https://issues.dlang.org/show_bug.cgi?id=16995 181 * https://issues.dlang.org/show_bug.cgi?id=18097 182 * https://issues.dlang.org/show_bug.cgi?id=18111 183 * https://issues.dlang.org/show_bug.cgi?id=18880 184 * https://issues.dlang.org/show_bug.cgi?id=18868 185 * https://issues.dlang.org/show_bug.cgi?id=19058 186 */ 187 static struct Key { Loc loc; string prefix; } 188 __gshared uint[Key] counters; 189 190 static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u))) 191 { 192 // 2.082+ 193 counters.update(Key(loc, prefix), 194 () => 1u, // insertion 195 (ref uint counter) // update 196 { 197 idBuf.writestring("_"); 198 idBuf.print(counter); 199 return counter + 1; 200 } 201 ); 202 } 203 else 204 { 205 const key = Key(loc, prefix); 206 if (auto pCounter = key in counters) 207 { 208 idBuf.writestring("_"); 209 idBuf.print((*pCounter)++); 210 } 211 else 212 counters[key] = 1; 213 } 214 215 return idPool(idBuf[]); 216 } 217 218 /******************************************** 219 * Create an identifier in the string table. 220 */ 221 static Identifier idPool(const(char)* s, uint len) 222 { 223 return idPool(s[0 .. len]); 224 } 225 226 extern (D) static Identifier idPool(const(char)[] s) 227 { 228 auto sv = stringtable.update(s); 229 auto id = sv.value; 230 if (!id) 231 { 232 id = new Identifier(sv.toString(), TOK.identifier); 233 sv.value = id; 234 } 235 return id; 236 } 237 238 extern (D) static Identifier idPool(const(char)* s, size_t len, int value) 239 { 240 return idPool(s[0 .. len], value); 241 } 242 243 extern (D) static Identifier idPool(const(char)[] s, int value) 244 { 245 auto sv = stringtable.insert(s, null); 246 assert(sv); 247 auto id = new Identifier(sv.toString(), value); 248 sv.value = id; 249 return id; 250 } 251 252 /********************************** 253 * Determine if string is a valid Identifier. 254 * Params: 255 * str = string to check 256 * Returns: 257 * false for invalid 258 */ 259 static bool isValidIdentifier(const(char)* str) 260 { 261 return str && isValidIdentifier(str.toDString); 262 } 263 264 /********************************** 265 * ditto 266 */ 267 extern (D) static bool isValidIdentifier(const(char)[] str) 268 { 269 if (str.length == 0 || 270 (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars 271 { 272 return false; 273 } 274 275 size_t idx = 0; 276 while (idx < str.length) 277 { 278 dchar dc; 279 const s = utf_decodeChar(str, idx, dc); 280 if (s || 281 !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_')) 282 { 283 return false; 284 } 285 } 286 return true; 287 } 288 289 extern (D) static Identifier lookup(const(char)* s, size_t len) 290 { 291 return lookup(s[0 .. len]); 292 } 293 294 extern (D) static Identifier lookup(const(char)[] s) 295 { 296 auto sv = stringtable.lookup(s); 297 if (!sv) 298 return null; 299 return sv.value; 300 } 301 302 extern (D) static void initTable() 303 { 304 stringtable._init(28_000); 305 } 306 }