/// a D name mangler. You probably do not need this, use the language's built in `.mangleof` property instead. I don't even remember why I wrote it. module arsd.mangle; import std.conv; static immutable string[23] primitives = [ "char", // a "bool", // b "creal", // c "double", // d "real", // e "float", // f "byte", // g "ubyte", // h "int", // i "ireal", // j "uint", // k "long", // l "ulong", // m null, // n "ifloat", // o "idouble", // p "cfloat", // q "cdouble", // r "short", // s "ushort", // t "wchar", // u "void", // v "dchar", // w ]; // FIXME: using this will allocate at *runtime*! Unbelievable. // it does that even if everything is enum auto dTokensPain() { immutable p = cast(immutable(string[])) primitives[]; string[] ret; foreach(i; (sort!"a.length > b.length"( p~ [ "(", ")", ".", ",", "!", "[", "]", "*", "const", "immutable", "shared", "extern", ]))) { ret ~= i; } return ret; } static immutable string[] dTokens = dTokensPain(); char manglePrimitive(in char[] t) { foreach(i, p; primitives) if(p == t) return cast(char) ('a' + i); return 0; } import std.algorithm; import std.array; bool isIdentifierChar(char c) { // FIXME: match the D spec return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } struct StackArray(Type, size_t capacity) { Type[capacity] buffer; size_t length; Type[] slice() { return buffer[0 .. length]; } void opOpAssign(string op : "~")(Type rhs, string file = __FILE__, size_t line = __LINE__) { if(length >= capacity) { throw new Error("no more room", file, line); } buffer[length] = rhs; length++; } } char[] mangle(const(char)[] decl, char[] buffer) { StackArray!(const(char)[], 128) tokensBuffer; main: while(decl.length) { if(decl[0] == ' ' || decl[0] == '\t' || decl[0] == '\n') { decl = decl[1 .. $]; continue; } foreach(token; dTokens) { if(token is null) continue; if(decl.length >= token.length && decl[0 .. token.length] == token) { // make sure this isn't an identifier that coincidentally starts with a keyword if(decl.length == token.length || !token[$ - 1].isIdentifierChar() || !decl[token.length].isIdentifierChar()) { tokensBuffer ~= token; decl = decl[token.length .. $]; continue main; } } } // could be an identifier or literal int pos = 0; while(pos < decl.length && decl[pos].isIdentifierChar) pos++; tokensBuffer ~= decl[0 .. pos]; decl = decl[pos .. $]; continue main; // FIXME: literals should be handled too } assert(decl.length == 0); // we should have consumed all the input into tokens auto tokens = tokensBuffer.slice(); char[64] returnTypeBuffer; auto returnType = parseAndMangleType(tokens, returnTypeBuffer); char[256] nameBuffer; auto name = parseName(tokens, nameBuffer[]); StackArray!(const(char)[], 64) arguments; // FIXME: templates and other types of thing should be handled assert(tokens[0] == "(", "other stuff not implemented " ~ tokens[0]); tokens = tokens[1 .. $]; char[64][32] argTypeBuffers; int i = 0; while(tokens[0] != ")") { arguments ~= parseAndMangleType(tokens, argTypeBuffers[i]); i++; if(tokens[0] == ",") tokens = tokens[1 .. $]; } assert(tokens[0] == ")", "other stuff not implemented"); return mangleFunction(name, returnType, arguments.slice(), buffer); } char[] parseName(ref const(char)[][] tokens, char[] nameBuffer) { size_t where = 0; more: nameBuffer[where .. where + tokens[0].length] = tokens[0][]; where += tokens[0].length; tokens = tokens[1 .. $]; if(tokens.length && tokens[0] == ".") { tokens = tokens[1 .. $]; nameBuffer[where++] = '.'; goto more; } return nameBuffer[0 .. where]; } char[] intToString(int i, char[] buffer) { int pos = cast(int) buffer.length - 1; if(i == 0) { buffer[pos] = '0'; pos--; } while(pos > 0 && i) { buffer[pos] = (i % 10) + '0'; pos--; i /= 10; } return buffer[pos + 1 .. $]; } char[] mangleName(in char[] name, char[] buffer) { import std.algorithm; import std.conv; auto parts = name.splitter("."); int bufferPos = 0; foreach(part; parts) { char[16] numberBuffer; auto number = intToString(cast(int) part.length, numberBuffer); buffer[bufferPos .. bufferPos + number.length] = number[]; bufferPos += number.length; buffer[bufferPos .. bufferPos + part.length] = part[]; bufferPos += part.length; } return buffer[0 .. bufferPos]; } char[] mangleFunction(in char[] name, in char[] returnTypeMangled, in char[][] argumentsMangle, char[] buffer) { int bufferPos = 0; buffer[bufferPos++] = '_'; buffer[bufferPos++] = 'D'; char[256] nameBuffer; auto mn = mangleName(name, nameBuffer); buffer[bufferPos .. bufferPos + mn.length] = mn[]; bufferPos += mn.length; buffer[bufferPos++] = 'F'; foreach(arg; argumentsMangle) { buffer[bufferPos .. bufferPos + arg.length] = arg[]; bufferPos += arg.length; } buffer[bufferPos++] = 'Z'; buffer[bufferPos .. bufferPos + returnTypeMangled.length] = returnTypeMangled[]; bufferPos += returnTypeMangled.length; return buffer[0 .. bufferPos]; } char[] parseAndMangleType(ref const(char)[][] tokens, char[] buffer) { assert(tokens.length); int bufferPos = 0; void prepend(char p) { for(int i = bufferPos; i > 0; i--) { buffer[i] = buffer[i - 1]; } buffer[0] = p; bufferPos++; } // FIXME: handle all the random D type constructors if(tokens[0] == "const" || tokens[0] == "immutable") { if(tokens[0] == "const") buffer[bufferPos++] = 'x'; else if(tokens[0] == "immutable") buffer[bufferPos++] = 'y'; tokens = tokens[1 .. $]; assert(tokens[0] == "("); tokens = tokens[1 .. $]; auto next = parseAndMangleType(tokens, buffer[bufferPos .. $]); bufferPos += next.length; assert(tokens[0] == ")"); tokens = tokens[1 .. $]; } else { char primitive = manglePrimitive(tokens[0]); if(primitive) { buffer[bufferPos++] = primitive; tokens = tokens[1 .. $]; } else { // probably a struct or something, parse it as an identifier // FIXME char[256] nameBuffer; auto name = parseName(tokens, nameBuffer[]); char[256] mangledNameBuffer; auto mn = mangleName(name, mangledNameBuffer); buffer[bufferPos++] = 'S'; buffer[bufferPos .. bufferPos + mn.length] = mn[]; bufferPos += mn.length; } } while(tokens.length) { if(tokens[0] == "[") { tokens = tokens[1 .. $]; prepend('A'); assert(tokens[0] == "]", "other array not implemented"); tokens = tokens[1 .. $]; } else if(tokens[0] == "*") { prepend('P'); tokens = tokens[1 .. $]; } else break; } return buffer[0 .. bufferPos]; } version(unittest) { int foo(int, string, int); string foo2(long, char[], int); struct S { int a; string b; } S foo3(S, S, string, long, int, S, int[], char[][]); long testcomplex(int, const(const(char)[]*)[], long); } unittest { import core.demangle; char[512] buffer; import std.stdio; assert(mangle(demangle(foo.mangleof), buffer) == foo.mangleof); assert(mangle(demangle(foo2.mangleof), buffer) == foo2.mangleof); assert(mangle(demangle(foo3.mangleof), buffer) == foo3.mangleof); assert(mangle(demangle(testcomplex.mangleof), buffer) == testcomplex.mangleof); // FIXME: these all fail if the functions are defined inside the unittest{} block // so still something wrong parsing those complex names or something } // _D6test303fooFiAyaZi // _D6test303fooFiAyaZi version(unittest) void main(string[] args) { char[512] buffer; import std.stdio; if(args.length > 1) writeln(mangle(args[1], buffer)); else writeln(mangle("int test30.foo(int, immutable(char)[])", buffer)); //mangle("int test30.foo(int, immutable(char)[])", buffer); }