The OpenD Programming Language

1 /// 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.
2 module arsd.mangle;
3 
4 import std.conv;
5 
6 static immutable string[23] primitives = [
7 	"char", // a
8 	"bool", // b
9 	"creal", // c
10 	"double", // d
11 	"real", // e
12 	"float", // f
13 	"byte", // g
14 	"ubyte", // h
15 	"int", // i
16 	"ireal", // j
17 	"uint", // k
18 	"long", // l
19 	"ulong", // m
20 	null, // n
21 	"ifloat", // o
22 	"idouble", // p
23 	"cfloat", // q
24 	"cdouble", // r
25 	"short", // s
26 	"ushort", // t
27 	"wchar", // u
28 	"void", // v
29 	"dchar", // w
30 ];
31 
32 // FIXME: using this will allocate at *runtime*! Unbelievable.
33 // it does that even if everything is enum
34 auto dTokensPain() {
35 	immutable p = cast(immutable(string[])) primitives[];
36 	string[] ret;
37 	foreach(i; (sort!"a.length > b.length"(
38 	p~
39 [
40 	"(",
41 	")",
42 	".",
43 	",",
44 	"!",
45 	"[",
46 	"]",
47 	"*",
48 	"const",
49 	"immutable",
50 	"shared",
51 	"extern",
52 ]))) { ret ~= i; }
53 
54 	return ret;
55 }
56 
57 static immutable string[] dTokens = dTokensPain();
58 
59 
60 char manglePrimitive(in char[] t) {
61 	foreach(i, p; primitives)
62 		if(p == t)
63 			return cast(char) ('a' + i);
64 	return 0;
65 }
66 
67 import std.algorithm;
68 import std.array;
69 
70 bool isIdentifierChar(char c) {
71 	// FIXME: match the D spec
72 	return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
73 }
74 
75 struct StackArray(Type, size_t capacity) {
76 	Type[capacity] buffer;
77 	size_t length;
78 	Type[] slice() { return buffer[0 .. length]; }
79 	void opOpAssign(string op : "~")(Type rhs, string file = __FILE__, size_t line = __LINE__) {
80 		if(length >= capacity) {
81 			throw new Error("no more room", file, line);
82 		}
83 		buffer[length] = rhs;
84 		length++;
85 	}
86 }
87 
88 char[] mangle(const(char)[] decl, char[] buffer) {
89 
90 
91 	StackArray!(const(char)[], 128) tokensBuffer;
92 	main: while(decl.length) {
93 		if(decl[0] == ' ' || decl[0] == '\t' || decl[0] == '\n') {
94 			decl = decl[1 .. $];
95 			continue;
96 		}
97 
98 		foreach(token; dTokens) {
99 			if(token is null) continue;
100 			if(decl.length >= token.length && decl[0 .. token.length] == token) {
101 				// make sure this isn't an identifier that coincidentally starts with a keyword
102 				if(decl.length == token.length || !token[$ - 1].isIdentifierChar() || !decl[token.length].isIdentifierChar()) {
103 					tokensBuffer ~= token;
104 					decl = decl[token.length .. $];
105 					continue main;
106 				}
107 			}
108 		}
109 
110 		// could be an identifier or literal
111 
112 		int pos = 0;
113 		while(pos < decl.length && decl[pos].isIdentifierChar)
114 			pos++;
115 		tokensBuffer ~= decl[0 .. pos];
116 		decl = decl[pos .. $];
117 		continue main;
118 
119 		// FIXME: literals should be handled too
120 	}
121 
122 	assert(decl.length == 0); // we should have consumed all the input into tokens
123 
124 	auto tokens = tokensBuffer.slice();
125 
126 
127 	char[64] returnTypeBuffer;
128 	auto returnType = parseAndMangleType(tokens, returnTypeBuffer);
129 	char[256] nameBuffer;
130 	auto name = parseName(tokens, nameBuffer[]);
131 	StackArray!(const(char)[], 64) arguments;
132 	// FIXME: templates and other types of thing should be handled
133 	assert(tokens[0] == "(", "other stuff not implemented " ~ tokens[0]);
134 	tokens = tokens[1 .. $];
135 
136 	char[64][32] argTypeBuffers;
137 	int i = 0;
138 
139 	while(tokens[0] != ")") {
140 		arguments ~= parseAndMangleType(tokens, argTypeBuffers[i]);
141 		i++;
142 		if(tokens[0] == ",")
143 			tokens = tokens[1 .. $];
144 	}
145 
146 	assert(tokens[0] == ")", "other stuff not implemented");
147 
148 	return mangleFunction(name, returnType, arguments.slice(), buffer);
149 }
150 
151 char[] parseName(ref const(char)[][] tokens, char[] nameBuffer) {
152 	size_t where = 0;
153 	more:
154 	nameBuffer[where .. where + tokens[0].length] = tokens[0][];
155 	where += tokens[0].length;
156 	tokens = tokens[1 .. $];
157 	if(tokens.length && tokens[0] == ".") {
158 		tokens = tokens[1 .. $];
159 		nameBuffer[where++] = '.';
160 		goto more;
161 	}
162 
163 	return nameBuffer[0 .. where];
164 }
165 
166 char[] intToString(int i, char[] buffer) {
167 	int pos = cast(int) buffer.length - 1;
168 
169 	if(i == 0) {
170 		buffer[pos] = '0';
171 		pos--;
172 	}
173 
174 	while(pos > 0 && i) {
175 		buffer[pos] = (i % 10) + '0';
176 		pos--;
177 		i /= 10;
178 	}
179 
180 	return buffer[pos + 1 .. $];
181 }
182 
183 
184 
185 char[] mangleName(in char[] name, char[] buffer) {
186 	import std.algorithm;
187 	import std.conv;
188 
189 	auto parts = name.splitter(".");
190 
191 	int bufferPos = 0;
192 	foreach(part; parts) {
193 		char[16] numberBuffer;
194 		auto number = intToString(cast(int) part.length, numberBuffer);
195 
196 		buffer[bufferPos .. bufferPos + number.length] = number[];
197 		bufferPos += number.length;
198 
199 		buffer[bufferPos .. bufferPos + part.length] = part[];
200 		bufferPos += part.length;
201 	}
202 
203 	return buffer[0 .. bufferPos];
204 }
205 
206 char[] mangleFunction(in char[] name, in char[] returnTypeMangled, in char[][] argumentsMangle, char[] buffer) {
207 	int bufferPos = 0;
208 	buffer[bufferPos++] = '_';
209 	buffer[bufferPos++] = 'D';
210 
211 	char[256] nameBuffer;
212 	auto mn = mangleName(name, nameBuffer);
213 	buffer[bufferPos .. bufferPos + mn.length] = mn[];
214 	bufferPos += mn.length;
215 
216 	buffer[bufferPos++] = 'F';
217 	foreach(arg; argumentsMangle) {
218 		buffer[bufferPos .. bufferPos + arg.length] = arg[];
219 		bufferPos += arg.length;
220 	}
221 	buffer[bufferPos++] = 'Z';
222 	buffer[bufferPos .. bufferPos + returnTypeMangled.length] = returnTypeMangled[];
223 	bufferPos += returnTypeMangled.length;
224 
225 	return buffer[0 .. bufferPos];
226 }
227 
228 char[] parseAndMangleType(ref const(char)[][] tokens, char[] buffer) {
229 	assert(tokens.length);
230 
231 	int bufferPos = 0;
232 
233 	void prepend(char p) {
234 		for(int i = bufferPos; i > 0; i--) {
235 			buffer[i] = buffer[i - 1];
236 		}
237 		buffer[0] = p;
238 		bufferPos++;
239 	}
240 
241 	// FIXME: handle all the random D type constructors
242 	if(tokens[0] == "const" || tokens[0] == "immutable") {
243 		if(tokens[0] == "const")
244 			buffer[bufferPos++] = 'x';
245 		else if(tokens[0] == "immutable")
246 			buffer[bufferPos++] = 'y';
247 		tokens = tokens[1 .. $];
248 		assert(tokens[0] == "(");
249 		tokens = tokens[1 .. $];
250 		auto next = parseAndMangleType(tokens, buffer[bufferPos .. $]);
251 		bufferPos += next.length;
252 		assert(tokens[0] == ")");
253 		tokens = tokens[1 .. $];
254 	} else {
255 		char primitive = manglePrimitive(tokens[0]);
256 		if(primitive) {
257 			buffer[bufferPos++] = primitive;
258 			tokens = tokens[1 .. $];
259 		} else {
260 			// probably a struct or something, parse it as an identifier
261 			// FIXME
262 			char[256] nameBuffer;
263 			auto name = parseName(tokens, nameBuffer[]);
264 
265 			char[256] mangledNameBuffer;
266 			auto mn = mangleName(name, mangledNameBuffer);
267 
268 			buffer[bufferPos++] = 'S';
269 			buffer[bufferPos .. bufferPos + mn.length] = mn[];
270 			bufferPos += mn.length;
271 		}
272 	}
273 
274 	while(tokens.length) {
275 		if(tokens[0] == "[") {
276 			tokens = tokens[1 .. $];
277 			prepend('A');
278 			assert(tokens[0] == "]", "other array not implemented");
279 			tokens = tokens[1 .. $];
280 		} else if(tokens[0] == "*") {
281 			prepend('P');
282 			tokens = tokens[1 .. $];
283 		} else break;
284 	}
285 
286 	return buffer[0 .. bufferPos];
287 }
288 
289 version(unittest) {
290 	int foo(int, string, int);
291 	string foo2(long, char[], int);
292 	struct S { int a; string b; }
293 	S foo3(S, S, string, long, int, S, int[], char[][]);
294 	long testcomplex(int, const(const(char)[]*)[], long);
295 }
296 
297 unittest {
298 	import core.demangle;
299 	char[512] buffer;
300 
301 	import std.stdio;
302 	assert(mangle(demangle(foo.mangleof), buffer) == foo.mangleof);
303 	assert(mangle(demangle(foo2.mangleof), buffer) == foo2.mangleof);
304 	assert(mangle(demangle(foo3.mangleof), buffer) == foo3.mangleof);
305 
306 	assert(mangle(demangle(testcomplex.mangleof), buffer) == testcomplex.mangleof);
307 	// FIXME: these all fail if the functions are defined inside the unittest{} block
308 	// so still something wrong parsing those complex names or something
309 }
310 
311 // _D6test303fooFiAyaZi
312 // _D6test303fooFiAyaZi
313 
314 version(unittest)
315 void main(string[] args) {
316 
317 	char[512] buffer;
318 	import std.stdio;
319 	if(args.length > 1)
320 		writeln(mangle(args[1], buffer));
321 	else
322 		writeln(mangle("int test30.foo(int, immutable(char)[])", buffer));
323 		//mangle("int test30.foo(int, immutable(char)[])", buffer);
324 }