arsd/mangle.d

325 lines
7.4 KiB
D

/// a D mangler
module 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);
}