| /** |
| * Defines an identifier, which is the name of a `Dsymbol`. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d, _identifier.d) |
| * Documentation: https://dlang.org/phobos/dmd_identifier.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/identifier.d |
| */ |
| |
| module dmd.identifier; |
| |
| import core.stdc.ctype; |
| import core.stdc.stdio; |
| import core.stdc.string; |
| import dmd.globals; |
| import dmd.id; |
| import dmd.common.outbuffer; |
| import dmd.root.rootobject; |
| import dmd.root.string; |
| import dmd.root.stringtable; |
| import dmd.root.utf; |
| import dmd.tokens; |
| |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class Identifier : RootObject |
| { |
| private const int value; |
| |
| // Indicates if this is an identifier used for an anonymous symbol. |
| private const bool isAnonymous_ = false; |
| |
| private const char[] name; |
| |
| nothrow: |
| |
| /// Construct an identifier from the given name. |
| extern (D) this(const(char)* name) |
| { |
| //printf("Identifier('%s', %d)\n", name, value); |
| this(name.toDString(), TOK.identifier); |
| } |
| |
| /** |
| Construct an identifier from the given name. |
| |
| Params: |
| name = the identifier name. There must be `'\0'` at `name[length]`. |
| length = the length of `name`, excluding the terminating `'\0'` |
| value = Identifier value (e.g. `Id.unitTest`) or `TOK.identifier` |
| */ |
| extern (D) this(const(char)* name, size_t length, int value) |
| in |
| { |
| assert(name[length] == '\0'); |
| } |
| do |
| { |
| //printf("Identifier('%s', %d)\n", name, value); |
| this(name[0 .. length], value); |
| } |
| |
| /// ditto |
| extern (D) this(const(char)[] name, int value) |
| { |
| //printf("Identifier('%.*s', %d)\n", cast(int)name.length, name.ptr, value); |
| this(name, value, false); |
| } |
| |
| extern (D) private this(const(char)[] name, int value, bool isAnonymous) |
| { |
| //printf("Identifier('%.*s', %d, %d)\n", cast(int)name.length, name.ptr, value, isAnonymous); |
| this.name = name; |
| this.value = value; |
| isAnonymous_ = isAnonymous; |
| } |
| |
| static Identifier create(const(char)* name) |
| { |
| return new Identifier(name); |
| } |
| |
| override const(char)* toChars() const pure |
| { |
| return name.ptr; |
| } |
| |
| extern (D) override const(char)[] toString() const pure @safe |
| { |
| return name; |
| } |
| |
| int getValue() const pure |
| { |
| return value; |
| } |
| |
| bool isAnonymous() const pure @nogc @safe |
| { |
| return isAnonymous_; |
| } |
| |
| const(char)* toHChars2() const |
| { |
| const(char)* p = null; |
| if (this == Id.ctor) |
| p = "this"; |
| else if (this == Id.dtor) |
| p = "~this"; |
| else if (this == Id.unitTest) |
| p = "unittest"; |
| else if (this == Id.dollar) |
| p = "$"; |
| else if (this == Id.withSym) |
| p = "with"; |
| else if (this == Id.result) |
| p = "result"; |
| else if (this == Id.returnLabel) |
| p = "return"; |
| else |
| { |
| p = toChars(); |
| if (*p == '_') |
| { |
| if (strncmp(p, "_staticCtor", 11) == 0) |
| p = "static this"; |
| else if (strncmp(p, "_staticDtor", 11) == 0) |
| p = "static ~this"; |
| else if (strncmp(p, "__invariant", 11) == 0) |
| p = "invariant"; |
| } |
| } |
| return p; |
| } |
| |
| override DYNCAST dyncast() const |
| { |
| return DYNCAST.identifier; |
| } |
| |
| private extern (D) __gshared StringTable!Identifier stringtable; |
| |
| /** |
| * Generates a new identifier. |
| * |
| * Params: |
| * prefix = this will be the prefix of the name of the identifier. For debugging |
| * purpose. |
| */ |
| extern(D) static Identifier generateId(const(char)[] prefix) |
| { |
| return generateId(prefix, newSuffix, false); |
| } |
| |
| /** |
| * Generates a new anonymous identifier. |
| * |
| * Params: |
| * name = this will be part of the name of the identifier. For debugging |
| * purpose. |
| */ |
| extern(D) static Identifier generateAnonymousId(const(char)[] name) |
| { |
| return generateId("__anon" ~ name, newSuffix, true); |
| } |
| |
| /** |
| * Generates a new identifier. |
| * |
| * Params: |
| * prefix = this will be the prefix of the name of the identifier. For debugging |
| * purpose. |
| * suffix = this will be the suffix of the name of the identifier. This is |
| * what makes the identifier unique |
| */ |
| extern(D) static Identifier generateId(const(char)[] prefix, size_t suffix) |
| { |
| return generateId(prefix, suffix, false); |
| } |
| |
| /// ditto |
| static Identifier generateId(const(char)* prefix, size_t length, size_t suffix) |
| { |
| return generateId(prefix[0 .. length], suffix); |
| } |
| |
| // Generates a new, unique, suffix for an identifier. |
| extern (D) private static size_t newSuffix() |
| { |
| __gshared size_t i; |
| return ++i; |
| } |
| |
| extern(D) private static Identifier generateId(const(char)[] prefix, size_t suffix, bool isAnonymous) |
| { |
| OutBuffer buf; |
| buf.write(prefix); |
| buf.print(suffix); |
| return idPool(buf[], isAnonymous); |
| } |
| |
| /*************************************** |
| * Generate deterministic named identifier based on a source location, |
| * such that the name is consistent across multiple compilations. |
| * A new unique name is generated. If the prefix+location is already in |
| * the stringtable, an extra suffix is added (starting the count at "_1"). |
| * |
| * Params: |
| * prefix = first part of the identifier name. |
| * loc = source location to use in the identifier name. |
| * Returns: |
| * Identifier (inside Identifier.idPool) with deterministic name based |
| * on the source location. |
| */ |
| extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc) |
| { |
| // generate `<prefix>_L<line>_C<col>` |
| OutBuffer idBuf; |
| idBuf.writestring(prefix); |
| idBuf.writestring("_L"); |
| idBuf.print(loc.linnum); |
| idBuf.writestring("_C"); |
| idBuf.print(loc.charnum); |
| |
| /** |
| * Make sure the identifiers are unique per filename, i.e., per module/mixin |
| * (`path/to/foo.d` and `path/to/foo.d-mixin-<line>`). See issues |
| * https://issues.dlang.org/show_bug.cgi?id=16995 |
| * https://issues.dlang.org/show_bug.cgi?id=18097 |
| * https://issues.dlang.org/show_bug.cgi?id=18111 |
| * https://issues.dlang.org/show_bug.cgi?id=18880 |
| * https://issues.dlang.org/show_bug.cgi?id=18868 |
| * https://issues.dlang.org/show_bug.cgi?id=19058 |
| */ |
| static struct Key { Loc loc; string prefix; } |
| __gshared uint[Key] counters; |
| |
| static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u))) |
| { |
| // 2.082+ |
| counters.update(Key(loc, prefix), |
| () => 1u, // insertion |
| (ref uint counter) // update |
| { |
| idBuf.writestring("_"); |
| idBuf.print(counter); |
| return counter + 1; |
| } |
| ); |
| } |
| else |
| { |
| const key = Key(loc, prefix); |
| if (auto pCounter = key in counters) |
| { |
| idBuf.writestring("_"); |
| idBuf.print((*pCounter)++); |
| } |
| else |
| counters[key] = 1; |
| } |
| |
| return idPool(idBuf[]); |
| } |
| |
| /******************************************** |
| * Create an identifier in the string table. |
| */ |
| static Identifier idPool(const(char)* s, uint len) |
| { |
| return idPool(s[0 .. len]); |
| } |
| |
| extern (D) static Identifier idPool(const(char)[] s) |
| { |
| return idPool(s, false); |
| } |
| |
| extern (D) private static Identifier idPool(const(char)[] s, bool isAnonymous) |
| { |
| auto sv = stringtable.update(s); |
| auto id = sv.value; |
| if (!id) |
| { |
| id = new Identifier(sv.toString(), TOK.identifier, isAnonymous); |
| sv.value = id; |
| } |
| return id; |
| } |
| |
| extern (D) static Identifier idPool(const(char)* s, size_t len, int value) |
| { |
| return idPool(s[0 .. len], value); |
| } |
| |
| extern (D) static Identifier idPool(const(char)[] s, int value) |
| { |
| auto sv = stringtable.insert(s, null); |
| assert(sv); |
| auto id = new Identifier(sv.toString(), value); |
| sv.value = id; |
| return id; |
| } |
| |
| /********************************** |
| * Determine if string is a valid Identifier. |
| * Params: |
| * str = string to check |
| * Returns: |
| * false for invalid |
| */ |
| static bool isValidIdentifier(const(char)* str) |
| { |
| return str && isValidIdentifier(str.toDString); |
| } |
| |
| /********************************** |
| * ditto |
| */ |
| extern (D) static bool isValidIdentifier(const(char)[] str) |
| { |
| if (str.length == 0 || |
| (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars |
| { |
| return false; |
| } |
| |
| size_t idx = 0; |
| while (idx < str.length) |
| { |
| dchar dc; |
| const s = utf_decodeChar(str, idx, dc); |
| if (s || |
| !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_')) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| extern (D) static Identifier lookup(const(char)* s, size_t len) |
| { |
| return lookup(s[0 .. len]); |
| } |
| |
| extern (D) static Identifier lookup(const(char)[] s) |
| { |
| auto sv = stringtable.lookup(s); |
| if (!sv) |
| return null; |
| return sv.value; |
| } |
| |
| extern (D) static void initTable() |
| { |
| stringtable._init(28_000); |
| } |
| } |