1 /** ELF Symbol Name (De)Mangling.
2 	Copyright: Per Nordlöw 2022-.
3 	License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 	Authors: $(WEB Per Nordlöw)
5 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html
6 
7 	TODO: Only check for emptyness before any optionals.
8 
9 	TODO: Search for pattern "X> <Y" and assure that they all use
10 	return r.tryEvery(X, Y).
11 
12 	TODO: 1. Replace calls to decode ~ decode with separate decodes
13 	TODO: 2 : Replace calls to decode ~ decode with a sequence call.
14 
15 	TODO: Detect recursion:
16 		  See: http://forum.dlang.org/thread/edaduxaxmihvzkoudeqa@forum.dlang.org#post-edaduxaxmihvzkoudeqa:40forum.dlang.org
17 		  See: http://code.dlang.org/packages/backtrace-d
18 
19 	TODO: What role does _ZL have? See localFlag for details.
20  */
21 module nxt.mangling;
22 
23 // version = show;
24 
25 import std.range.primitives: empty, front, popFront, moveFront, popFrontExactly, isInputRange;
26 import std.algorithm: joiner, min;
27 import std.conv: to;
28 import std.ascii: isDigit;
29 
30 import std.algorithm.comparison : either;
31 import nxt.algorithm.searching : startsWith, skipOver;
32 import nxt.algorithm_ex: tryEvery, split, splitBefore;
33 
34 @safe:
35 
36 /** C++ Demangler. */
37 extern(C++) class Demangler(R) if (isInputRange!R) {
38 extern(D):
39 	this(R r,
40 		 bool explicitVoidParameter = false,
41 		 bool show = false)
42 	{
43 		this.r = r;
44 		this.explicitVoidParameter = explicitVoidParameter;
45 		this.show = show;
46 	}
47 	R r;
48 	bool show = false;
49 	bool explicitVoidParameter = false; // set to true make void parameters explicit
50 private:
51 	string[] sourceNames;
52 	CxxType[] ids; // ids demangled so far
53 	R scopeSeparator = "::";
54 }
55 auto demangler(T...)(T args) if (isInputRange!(T[0])) => new Demangler!(T[0])(args);
56 
57 /** Like $(D skipOver) but return $(D string) instead of $(D bool).
58 	Bool-conversion of returned value gives same result as r.skipOver(lit).
59 */
60 string skipLiteral(R, E)(Demangler!R x, E lit) if (isInputRange!R) => x.r.skipOver(lit) ? "" : null;
61 
62 /** Decode Unqualified C++ Type at $(D r).
63 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-type
64 */
65 R decodeCxxUnqualifiedType(R)(Demangler!R x) if (isInputRange!R)
66 {
67 	return either(x.decodeCxxBuiltinType(),
68 				  x.decodeCxxSubstitution(),
69 				  x.decodeCxxFunctionType());
70 }
71 
72 struct CxxType
73 {
74 	string typeName;
75 	bool isRef = false;	  // & <ref-qualifier>
76 	bool isRvalueRef = false;	// && ref-qualifier (C++11)
77 	bool isComplexPair = false;	// complex pair (C 2000)
78 	bool isImaginary = false;	// imaginary (C 2000)
79 	byte pointyness = 0;		   // pointer level
80 	CXXCVQualifiers cvQ;
81 
82 	void toString(Sink)(ref scope Sink sink) const
83 	{
84 		if (cvQ.isVolatile) { sink("volatile "); }
85 		if (cvQ.isRestrict) { sink("restrict "); }
86 
87 		sink(typeName);
88 
89 		if (cvQ.isConst) { sink(" const"); }
90 
91 		// suffix qualifiers
92 		foreach (immutable _; 0 .. pointyness)
93 		{
94 			sink(`*`);// str ~= "*".replicate(pointyness);
95 		}
96 		if (isRef) { sink(`&`); }
97 		if (isRvalueRef) { sink(`&&`); }
98 	}
99 }
100 
101 /** Decode C++ Type at $(D r).
102 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-type
103 */
104 R decodeCxxType(R)(Demangler!R x)
105 if (isInputRange!R)
106 {
107 
108 	const packExpansion = x.r.skipOver(`Dp`); // (C++11)
109 
110 	CxxType cxxType;
111 
112 	while (!x.r.empty)
113 	{
114 		if (const cvQ_ = x.r.decodeCxxCVQualifiers()) /+ TODO: Optimize +/
115 		{
116 			cxxType.cvQ.isRestrict |= cvQ_.isRestrict;
117 			cxxType.cvQ.isVolatile |= cvQ_.isVolatile;
118 			cxxType.cvQ.isConst |= cvQ_.isConst;
119 			continue;
120 		}
121 		else
122 		{
123 			auto miss = false;
124 			switch (x.r[0])
125 			{
126 				case 'P': x.r.popFront(); cxxType.pointyness++; break;
127 					// <ref-qualifier>: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ref-qualifier
128 				case 'R': x.r.popFront(); cxxType.isRef = true; break;
129 				case 'O': x.r.popFront(); cxxType.isRvalueRef = true; break;
130  				case 'C': x.r.popFront(); cxxType.isComplexPair = true; assert(0, "TODO: Handle complex pair (C 2000)"); break;
131 				case 'G': x.r.popFront(); cxxType.isImaginary = true; assert(0, "TODO: Handle imaginary (C 2000)"); break;
132 				case 'U': x.r.popFront();
133 					const sourceName = x.decodeCxxSourceName();
134 					cxxType.typeName = sourceName ~ x.decodeCxxType();
135 					// "TODO: Handle vendor extended type qualifier <source-name>", x.r
136 					break;
137 				default: miss = true; break;
138 			}
139 			if (miss)
140 			{
141 				break;
142 			}
143 		}
144 	}
145 	assert(!(cxxType.isRef && cxxType.isRvalueRef));
146 
147 	if (x.r.empty) { return cxxType.to!string; }
148 
149 	cxxType.typeName = either(x.decodeCxxBuiltinType(),
150 							  x.decodeCxxFunctionType(),
151 							  x.decodeCxxClassEnumType(),
152 							  x.decodeCxxArrayType(),
153 							  x.decodeCxxPointerToMemberType(),
154 							  x.decodeCxxTemplateTemplateParamAndArgs(),
155 							  x.decodeCxxDecltype(),
156 							  x.decodeCxxSubstitution());
157 
158 	x.ids ~= cxxType;
159 
160 	return cxxType.to!string;
161 }
162 
163 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.class-enum-type */
164 R decodeCxxClassEnumType(R)(Demangler!R x)
165 if (isInputRange!R)
166 {
167 	R type;
168 	R prefix;
169 	enum n = 2;
170 	if (x.r.length >= n)
171 	{
172 		switch (x.r[0..n])
173 		{
174 			case `Ts`: prefix = `struct `; break;
175 			case `Tu`: prefix = `union `; break;
176 			case `Te`: prefix = `enum `; break;
177 			default: break;
178 		}
179 		if (prefix)
180 		{
181 			x.r.popFrontExactly(n);
182 		}
183 	}
184 	const name = x.decodeCxxName();
185 	if (name)
186 	{
187 		type = prefix ~ name;
188 	}
189 	else
190 	{
191 		assert(!prefix); // if we failed to decode name prefix should not have existed either
192 	}
193 	return type;
194 }
195 
196 R decodeCxxExpression(R)(Demangler!R x)
197 if (isInputRange!R)
198 {
199 	R exp;
200 	assert(0, "TODO");
201 }
202 
203 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.array-type */
204 R decodeCxxArrayType(R)(Demangler!R x)
205 if (isInputRange!R)
206 {
207 	R type;
208 	if (x.r.skipOver('A'))
209 	{
210 		if (const num = x.decodeCxxNumber())
211 		{
212 			assert(x.r.skipOver('_'));
213 			type = x.decodeCxxType() ~ `[]` ~ num ~ `[]`;
214 		}
215 		else
216 		{
217 			const dimensionExpression = x.decodeCxxExpression();
218 			assert(x.r.skipOver('_'));
219 			type = x.decodeCxxType() ~ `[]` ~ dimensionExpression ~ `[]`;
220 		}
221 	}
222 	return type;
223 }
224 
225 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.pointer-to-member-type */
226 R decodeCxxPointerToMemberType(R)(Demangler!R x)
227 if (isInputRange!R)
228 {
229 	R type;
230 	if (x.r.skipOver('M'))
231 	{
232 		const classType = x.decodeCxxType(); // <class type>
233 		const memberType = x.decodeCxxType(); // <mmeber type>
234 		type = classType ~ memberType;
235 	}
236 	return type;
237 }
238 
239 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-param */
240 R decodeCxxTemplateParam(R)(Demangler!R x)
241 if (isInputRange!R)
242 {
243 	R param;
244 	if (x.r.skipOver('T'))
245 	{
246 		if (x.r.skipOver('_'))
247 		{
248 			param = `first template parameter`;
249 		}
250 		else
251 		{
252 			param = x.decodeCxxNumber();
253 			assert(x.r.skipOver('_'));
254 		}
255 	}
256 	return param;
257 }
258 
259 R decodeCxxTemplateTemplateParamAndArgs(R)(Demangler!R x)
260 if (isInputRange!R)
261 {
262 	R value;
263 	if (const param = either(x.decodeCxxTemplateParam(),
264 							 x.decodeCxxSubstitution()))
265 	{
266 		auto args = x.decodeCxxTemplateArgs();
267 		value = param ~ args.joiner(`, `).to!R;
268 	}
269 	return value;
270 }
271 
272 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.decltype */
273 R decodeCxxDecltype(R)(Demangler!R x)
274 if (isInputRange!R)
275 {
276 	R type;
277 	if (x.r.skipOver(`Dt`) ||
278 		x.r.skipOver(`DT`))
279 	{
280 		type = x.decodeCxxExpression();
281 		assert(x.r.skipOver('E'));
282 	}
283 	return type;
284 }
285 
286 R decodeCxxDigit(R)(Demangler!R x)
287 if (isInputRange!R)
288 {
289 	auto digit = x.r[0..1];
290 	x.r.popFront();
291 	return digit;
292 }
293 
294 /** Try to Decode C++ Operator at $(D r).
295 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling-operator
296 */
297 R decodeCxxOperatorName(R)(Demangler!R x)
298 if (isInputRange!R)
299 {
300 
301 	if (x.r.skipOver('v'))	 // vendor extended operator
302 	{
303 		const digit = x.decodeCxxDigit();
304 		const sourceName = x.decodeCxxSourceName();
305 		return digit ~ sourceName;
306 	}
307 
308 	R op;
309 	enum n = 2;
310 	if (x.r.length < n) { return typeof(return).init; }
311 	const code = x.r[0..n];
312 	switch (code)
313 	{
314 		case `nw`: op = `operator new`; break;
315 		case `na`: op = `operator new[]`; break;
316 		case `dl`: op = `operator delete`; break;
317 		case `da`: op = `operator delete[]`; break;
318 		case `ps`: op = `operator+`; break; // unary plus
319 		case `ng`: op = `operator-`; break; // unary minus
320 
321 		case `ad`: op = `operator&`; break; // address of
322 		case `de`: op = `operator*`; break; // dereference
323 
324 		case `co`: op = `operator~`; break; // bitwise complement
325 		case `pl`: op = `operator+`; break; // plus
326 		case `mi`: op = `operator-`; break; // minus
327 
328 		case `ml`: op = `operator*`; break; // multiplication
329 		case `dv`: op = `operator/`; break; // division
330 		case `rm`: op = `operator%`; break; // remainder
331 
332 		case `an`: op = `operator&`; break; // bitwise and
333 		case `or`: op = `operator|`; break; // bitwise of
334 
335 		case `eo`: op = `operator^`; break;
336 		case `aS`: op = `operator=`; break;
337 
338 		case `pL`: op = `operator+=`; break;
339 		case `mI`: op = `operator-=`; break;
340 		case `mL`: op = `operator*=`; break;
341 		case `dV`: op = `operator/=`; break;
342 		case `rM`: op = `operator%=`; break;
343 
344 		case `aN`: op = `operator&=`; break;
345 		case `oR`: op = `operator|=`; break;
346 		case `eO`: op = `operator^=`; break;
347 
348 		case `ls`: op = `operator<<`; break;
349 		case `rs`: op = `operator>>`; break;
350 		case `lS`: op = `operator<<=`; break;
351 		case `rS`: op = `operator>>=`; break;
352 
353 		case `eq`: op = `operator==`; break;
354 		case `ne`: op = `operator!=`; break;
355 		case `lt`: op = `operator<`; break;
356 		case `gt`: op = `operator>`; break;
357 		case `le`: op = `operator<=`; break;
358 		case `ge`: op = `operator>=`; break;
359 
360 		case `nt`: op = `operator!`; break;
361 		case `aa`: op = `operator&&`; break;
362 		case `oo`: op = `operator||`; break;
363 
364 		case `pp`: op = `operator++`; break; // (postfix in <expression> context)
365 		case `mm`: op = `operator--`; break; // (postfix in <expression> context)
366 
367 		case `cm`: op = `operator,`; break;
368 
369 		case `pm`: op = `operator->*`; break;
370 		case `pt`: op = `operator->`; break;
371 
372 		case `cl`: op = `operator()`; break;
373 		case `ix`: op = `operator[]`; break;
374 		case `qu`: op = `operator?`; break;
375 		case `cv`: op = `(cast)`; break;
376 		case `li`: op = `operator""`; break;
377 		default: break;
378 	}
379 
380 	if (op)
381 	{
382 		x.r.popFrontExactly(n); // digest it
383 	}
384 
385 	switch (code)
386 	{
387 		case `cv`: op = '(' ~ x.decodeCxxType() ~ ')'; break;
388 		case `li`: op = (`operator ""` ~ x.decodeCxxSourceName()); break;
389 		default: break;
390 	}
391 
392 	return op;
393 }
394 
395 /** Try to Decode C++ Builtin Type at $(D r).
396 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.builtin-type
397 */
398 R decodeCxxBuiltinType(R)(Demangler!R x)
399 if (isInputRange!R)
400 {
401 	R type;
402 	enum n = 1;
403 	if (x.r.length < n) { return type; }
404 	switch (x.r[0])
405 	{
406 		case 'v': x.r.popFront(); type = `void`; break;
407 		case 'w': x.r.popFront(); type = `wchar_t`; break;
408 
409 		case 'b': x.r.popFront(); type = `bool`; break;
410 
411 		case 'c': x.r.popFront(); type = `char`; break;
412 		case 'a': x.r.popFront(); type = `signed char`; break;
413 		case 'h': x.r.popFront(); type = `unsigned char`; break;
414 
415 		case 's': x.r.popFront(); type = `short`; break;
416 		case 't': x.r.popFront(); type = `unsigned short`; break;
417 
418 		case 'i': x.r.popFront(); type = `int`; break;
419 		case 'j': x.r.popFront(); type = `unsigned int`; break;
420 
421 		case 'l': x.r.popFront(); type = `long`; break;
422 		case 'm': x.r.popFront(); type = `unsigned long`; break;
423 
424 		case 'x': x.r.popFront(); type = `long long`; break;  // __int64
425 		case 'y': x.r.popFront(); type = `unsigned long long`; break; // __int64
426 
427 		case 'n': x.r.popFront(); type = `__int128`; break;
428 		case 'o': x.r.popFront(); type = `unsigned __int128`; break;
429 
430 		case 'f': x.r.popFront(); type = `float`; break;
431 		case 'd': x.r.popFront(); type = `double`; break;
432 		case 'e': x.r.popFront(); type = `long double`; break; // __float80
433 		case 'g': x.r.popFront(); type = `__float128`; break;
434 
435 		case 'z': x.r.popFront(); type = `...`; break; // ellipsis
436 
437 		case 'D':
438 			x.r.popFront();
439 			assert(!x.r.empty); // need one more
440 			switch (x.r[0])
441 			{
442 				case 'd': x.r.popFront(); type = `IEEE 754r decimal floating point (64 bits)`; break;
443 				case 'e': x.r.popFront(); type = `IEEE 754r decimal floating point (128 bits)`; break;
444 				case 'f': x.r.popFront(); type = `IEEE 754r decimal floating point (32 bits)`; break;
445 				case 'h': x.r.popFront(); type = `IEEE 754r half-precision floating point (16 bits)`; break;
446 				case 'i': x.r.popFront(); type = `char32_t`; break;
447 				case 's': x.r.popFront(); type = `char16_t`; break;
448 				case 'a': x.r.popFront(); type = `auto`; break;
449 				case 'c': x.r.popFront(); type = `decltype(auto)`; break;
450 				case 'n': x.r.popFront(); type = `std::nullptr_t`; break; // (i.e., decltype(nullptr))
451 				default: assert(0, `TODO: Handle `);
452 			}
453 			break;
454 
455 			/* TODO: */
456 			/* ::= u <source-name>	# vendor extended type */
457 
458 		default:
459 			break;
460 	}
461 
462 	return type;
463 }
464 
465 /** Decode C++ Substitution Type at $(D r).
466 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.substitution
467 */
468 R decodeCxxSubstitution(R)(Demangler!R x, R stdPrefix = `::std::`)
469 if (isInputRange!R)
470 {
471 	R type;
472 	if (x.r.skipOver('S'))
473 	{
474 		if (x.r.front == '_') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id
475 		{
476 			type = x.ids[0].to!R;
477 			x.r.popFront();
478 		}
479 		else if ('0' <= x.r.front && x.r.front <= '9') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id
480 		{
481 			const ix = (x.r.front - '0');
482 			auto ids_ = x.ids[min(x.ids.length - 1, ix + 1)]; /+ TODO: Use of min here is hacky. Investigate. +/
483 			if (ix == 0)
484 			{
485 				/* NOTE: Undocumented: decrease pointyness.
486 				   See for example: parse_arch(size_t argc, const char** argv, const char* arch)
487 				   in dmd/src/mars.c
488 				*/
489 				ids_.pointyness = ids_.pointyness >= 1 ? cast(byte)(ids_.pointyness - 1): 0;
490 			}
491 			type = ids_.to!R;
492 			x.r.popFront();
493 			x.r.skipOver('_'); /+ TODO: Relaxed this to optional by removing surrounding assert. Investigate. +/
494 		}
495 		else if ('A' <= x.r.front && x.r.front <= 'Z') // See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.seq-id
496 		{
497 			const ix = (x.r.front - 'A' + 11);
498 			type = x.ids[ix].to!R;
499 			x.r.popFront();
500 			assert(x.r.skipOver('_'));
501 		}
502 		else
503 		{
504 			type = stdPrefix;
505 			switch (x.r.front)
506 			{
507 				case 't': x.r.popFront(); type ~= `ostream`; break;
508 				case 'a': x.r.popFront(); type ~= `allocator`; break;
509 				case 'b': x.r.popFront(); type ~= `basic_string`; break;
510 				case 's': x.r.popFront(); type ~= `basic_string<char, std::char_traits<char>, std::allocator<char> >`; break;
511 				case 'i': x.r.popFront(); type ~= `istream`; break;
512 				case 'o': x.r.popFront(); type ~= `ostream`; break;
513 				case 'd': x.r.popFront(); type ~= `iostream`; break;
514 
515 				default:
516 					assert(0, `Cannot handle C++ standard prefix character`);
517 					x.r.popFront();
518 					break;
519 			}
520 		}
521 	}
522 	return type;
523 }
524 
525 /** Try to Decode C++ Function Type at $(D r).
526 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.function-type
527 */
528 R decodeCxxFunctionType(R)(Demangler!R x)
529 if (isInputRange!R) {
530 	auto restLookAhead = x.r; // needed for lookahead parsing of CV-qualifiers
531 	const cvQ = restLookAhead.decodeCxxCVQualifiers();
532 	R type;
533 	if (restLookAhead.skipOver('F')) {
534 		x.r = restLookAhead; // we have found it
535 		x.r.skipOver('Y'); // optional
536 		type = x.decodeCxxBareFunctionType().to!R;
537 		const refQ = x.decodeCxxRefQualifier();
538 		type ~= refQ.toCxxString;
539 
540 	}
541 	return type;
542 }
543 
544 struct CxxBareFunctionType(R)
545 if (isInputRange!R) {
546 	R[] types; // optional return and parameter types
547 	bool explicitVoidParameter = false; // set to true make void parameters explicit
548 	R toString() pure {				/+ TODO: use sink +/
549 		R value;
550 		if (!types.empty) {
551 			value ~= `(`;
552 			if (this.explicitVoidParameter ||
553 				!(types.length == 1 && types.front == "void")) {
554 				value ~= types.joiner(`, `).to!R;
555 			}
556 			value ~= `)`;
557 		}
558 		return value;
559 	}
560 }
561 
562 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.bare-function-type */
563 CxxBareFunctionType!R decodeCxxBareFunctionType(R)(Demangler!R x)
564 if (isInputRange!R) {
565 	typeof(return) bareFunctionType;
566 	bareFunctionType.explicitVoidParameter = x.explicitVoidParameter;
567 
568 	/* TODO: This behaviour may not follow grammar. */
569 	if (const firstType = x.decodeCxxType()) {
570 		bareFunctionType.types ~= firstType;
571 	}
572 
573 	while (!x.r.empty) {
574 		auto type = x.decodeCxxType();
575 		if (type)
576 			bareFunctionType.types ~= type;
577 		else
578 			break;
579 	}
580 
581 	return bareFunctionType;
582 }
583 
584 struct CXXCVQualifiers
585 {
586 	bool isRestrict; // (C99)
587 	bool isVolatile; // volatile
588 	bool isConst; // const
589 	auto opCast(T : bool)() pure nothrow const
590 		=> (isRestrict ||
591 			isVolatile ||
592 			isConst);
593 	void toString(Sink)(ref scope Sink sink) const {
594 		if (isRestrict) sink(`restrict `);
595 		if (isVolatile) sink(`volatile `);
596 		if (isConst)	sink(`const `);
597 	}
598 }
599 
600 /** Decode <CV-qualifiers>
601 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
602 */
603 CXXCVQualifiers decodeCxxCVQualifiers(R)(ref R r)
604 if (isInputRange!R) {
605 	typeof(return) cvQ;
606 	if (r.skipOver('r')) { cvQ.isRestrict = true; }
607 	if (r.skipOver('V')) { cvQ.isVolatile = true; }
608 	if (r.skipOver('K')) { cvQ.isConst	= true; }
609 	return cvQ;
610 }
611 
612 enum CxxRefQualifier {
613 	none,
614 	normalRef,
615 	rvalueRef
616 }
617 
618 /* See_Also: http://forum.dlang.org/thread/cvhapzsrhjdnpkdspavg@forum.dlang.org#post-cvhapzsrhjdnpkdspavg:40forum.dlang.org */
619 string toCxxString(CxxRefQualifier refQ) pure nothrow {
620 	final switch (refQ) {
621 		case CxxRefQualifier.none: return "";
622 		case CxxRefQualifier.normalRef: return "&";
623 		case CxxRefQualifier.rvalueRef: return "&&";
624 	}
625 }
626 
627 /** Decode <ref-qualifier>
628 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ref-qualifier
629 */
630 CxxRefQualifier decodeCxxRefQualifier(R)(Demangler!R x)
631 if (isInputRange!R) {
632 	if (x.r.skipOver('R'))
633 		return CxxRefQualifier.normalRef;
634 	else if (x.r.skipOver('O'))
635 		return CxxRefQualifier.rvalueRef;
636 	else
637 		return CxxRefQualifier.none;
638 }
639 
640 /** Decode Identifier <source-name>.
641 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.source-name
642 */
643 R decodeCxxSourceName(R)(Demangler!R x)
644 if (isInputRange!R) {
645 	R id;
646 	const sign = x.r.skipOver('n'); // if negative number
647 	assert(!sign);
648 	const match = x.r.splitBefore!(a => !a.isDigit);
649 	const digits = match[0];
650 	x.r = match[1];
651 	if (!digits.empty) { // digit prefix
652 		/+ TODO: Functionize these three lines +/
653 		const num = digits.to!uint;
654 		id = x.r[0..num]; // identifier, x.r.take(num)
655 		x.r = x.r[num..$]; // x.r.drop(num);
656 		x.sourceNames ~= id;
657 	}
658 	return id;
659 }
660 
661 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.nested-name
662    Note: Second alternative
663    <template-prefix> <template-args>
664    in
665    <nested-name>
666    is redundant as it is included in <prefix> and is skipped here.
667  */
668 R decodeCxxNestedName(R)(Demangler!R x)
669 if (isInputRange!R) {
670 	if (x.r.skipOver('N')) // nested name: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.nested-name
671 	{
672 		const cvQ = x.r.decodeCxxCVQualifiers();
673 		const refQ = x.decodeCxxRefQualifier();
674 		const prefix = x.decodeCxxPrefix();
675 		const name = x.decodeCxxUnqualifiedName();
676 		assert(x.r.skipOver('E'));
677 		auto ret = (cvQ.to!R ~
678 					prefix ~
679 					name ~
680 					refQ.toCxxString);
681 		return ret;
682 	}
683 	return null;
684 }
685 
686 /** TODO: Use this
687 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ctor-dtor-name
688  */
689 enum CtorDtorName
690 {
691 	completeObjectConstructor,
692 	baseObjectConstructor,
693 	completeObjectAllocatingConstructor,
694 	deletingDestructor,
695 	completeObjectDestructor,
696 	baseObjectDestructor
697 }
698 
699 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.ctor-dtor-name */
700 R decodeCxxCtorDtorName(R)(Demangler!R x)
701 if (isInputRange!R) {
702 	R name;
703 	enum n = 2;
704 	if (x.r.length < n) { return typeof(return).init; }
705 	import std.array: back;
706 	switch (x.r[0..n]) {
707 		case `C1`: name = x.sourceNames.back; break; // complete object constructor
708 		case `C2`: name = x.sourceNames.back; break; // base object constructor
709 		case `C3`: name = x.sourceNames.back; break; // complete object allocating constructor
710 		case `D0`: name = '~' ~ x.sourceNames.back; break; // deleting destructor
711 		case `D1`: name = '~' ~ x.sourceNames.back; break; // complete object destructor
712 		case `D2`: name = '~' ~ x.sourceNames.back; break; // base object destructor
713 		default: break;
714 	}
715 	if (name)
716 		x.r.popFrontExactly(n);
717 	return name;
718 }
719 
720 /** https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unqualified-name */
721 R decodeCxxUnqualifiedName(R)(Demangler!R x)
722 if (isInputRange!R) {
723 	return either(x.decodeCxxOperatorName(),
724 				  x.decodeCxxSourceName(),
725 				  x.decodeCxxCtorDtorName(),
726 				  x.decodeCxxUnnamedTypeName());
727 }
728 
729 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unnamed-type-name */
730 R decodeCxxUnnamedTypeName(R)(Demangler!R x)
731 if (isInputRange!R) {
732 	R type;
733 	if (x.r.skipOver(`Ut`)) {
734 		type = x.decodeCxxNumber();
735 		assert(x.r.skipOver('_'));
736 	}
737 	return type;
738 }
739 
740 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-prefix
741  */
742 R decodeCxxTemplatePrefix(R)(Demangler!R x)
743 if (isInputRange!R) {
744 	// NOTE: Removed <prefix> because of recursion
745 	return either(x.decodeCxxUnqualifiedName(),
746 				  x.decodeCxxTemplateParam(),
747 				  x.decodeCxxSubstitution());
748 }
749 
750 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-args */
751 R[] decodeCxxTemplateArgs(R)(Demangler!R x)
752 if (isInputRange!R) {
753 	typeof(return) args;
754 	if (x.r.skipOver('I')) {
755 		args ~= x.decodeCxxTemplateArg();
756 		while (!x.r.empty) {
757 			auto arg = x.decodeCxxTemplateArg();
758 			if (arg)
759 				args ~= arg;
760 			else
761 				break;
762 		}
763 		assert(x.r.skipOver('E'));
764 	}
765 	return args;
766 }
767 
768 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.mangled-name */
769 R decodeCxxMangledName(R)(Demangler!R x)
770 if (isInputRange!R) {
771 	R name;
772 	if (x.r.skipOver(`_Z`))
773 		return x.decodeCxxEncoding();
774 	return name;
775 }
776 
777 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.expr-primary */
778 R decodeCxxExprPrimary(R)(Demangler!R x)
779 if (isInputRange!R) {
780 	R expr;
781 	if (x.r.skipOver('L')) {
782 		expr = x.decodeCxxMangledName();
783 		if (!expr) {
784 			auto number = x.decodeCxxNumber();
785 			/+ TODO: Howto demangle <float>? +/
786 			/+ TODO: Howto demangle <float> _ <float> E +/
787 			expr = x.decodeCxxType(); // <R>, <nullptr>, <pointer> type
788 			bool pointerType = x.r.skipOver('0'); // null pointer template argument
789 		}
790 		assert(x.r.skipOver('E'));
791 	}
792 	return expr;
793 }
794 
795 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.template-arg */
796 R decodeCxxTemplateArg(R)(Demangler!R x)
797 if (isInputRange!R)
798 {
799 	R arg;
800 	if (x.r.skipOver('X')) {
801 		arg = x.decodeCxxExpression();
802 		assert(x.r.skipOver('E'));
803 	} else if (x.r.skipOver('J')) {
804 		R[] args;
805 		while (!x.r.empty) {
806 			const subArg = x.decodeCxxTemplateArg();
807 			if (subArg)
808 				args ~= subArg;
809 			else
810 				break;
811 		}
812 		arg = args.joiner(`, `).to!R;
813 		assert(x.r.skipOver('E'));
814 	}
815 	else
816 		arg = either(x.decodeCxxExprPrimary(),
817 					 x.decodeCxxType());
818 	return arg;
819 }
820 
821 R decodeCxxTemplatePrefixAndArgs(R)(Demangler!R x)
822 if (isInputRange!R) {
823 	auto restBackup = x.r;
824 	if (const prefix = x.decodeCxxTemplatePrefix()) {
825 		auto args = x.decodeCxxTemplateArgs();
826 		if (args)
827 			return prefix ~ args.joiner(`, `).to!R;
828 	}
829 	x.r = restBackup; // restore upon failure
830 	return typeof(return).init;
831 }
832 
833 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.prefix */
834 R decodeCxxPrefix(R)(Demangler!R x)
835 if (isInputRange!R) {
836 	typeof(return) prefix;
837 	for (size_t i = 0; !x.r.empty; ++i) { // NOTE: Turned self-recursion into iteration
838 		if (const name = x.decodeCxxUnqualifiedName()) {
839 			if (i >= 1)
840 				prefix ~= x.scopeSeparator;
841 			prefix ~= name;
842 			continue;
843 		} else if (const name = x.decodeCxxTemplatePrefixAndArgs()) {
844 			prefix ~= name;
845 			continue;
846 		} else if (const templateParam = x.decodeCxxTemplateParam()) {
847 			prefix ~= templateParam;
848 			continue;
849 		} else if (const decltype = x.decodeCxxDecltype()) {
850 			prefix ~= decltype;
851 			continue;
852 		} else if (const subst = x.decodeCxxSubstitution()) {
853 			prefix ~= subst;
854 			continue;
855 		}
856 		else
857 			break;
858 	}
859 	return prefix;
860 }
861 
862 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-name */
863 R decodeCxxUnscopedName(R)(Demangler!R x)
864 if (isInputRange!R) {
865 	auto restBackup = x.r;
866 	const prefix = x.r.skipOver(`St`) ? "::std::" : null;
867 	if (const name = x.decodeCxxUnqualifiedName())
868 		return prefix ~ name;
869 	else {
870 		x.r = restBackup; // restore
871 		return typeof(return).init;
872 	}
873 }
874 
875 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-template-name */
876 R decodeCxxUnscopedTemplateName(R)(Demangler!R x)
877 if (isInputRange!R)
878 {
879 	return either(x.decodeCxxSubstitution(), // faster backtracking with substitution
880 				  x.decodeCxxUnscopedName());
881 }
882 
883 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.unscoped-template-name */
884 R decodeCxxUnscopedTemplateNameAndArgs(R)(Demangler!R x)
885 if (isInputRange!R) {
886 	R nameAndArgs;
887 	if (const name = x.decodeCxxUnscopedTemplateName()) {
888 		nameAndArgs = name;
889 		if (auto args = x.decodeCxxTemplateArgs())
890 			nameAndArgs ~= args.joiner(`, `).to!R;
891 	}
892 	return nameAndArgs;
893 }
894 
895 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.number */
896 R decodeCxxNumber(R)(Demangler!R x)
897 if (isInputRange!R) {
898 	R number;
899 	const prefix = x.r.skipOver('n'); // optional prefix
900 	auto split = x.r.splitBefore!(a => !a.isDigit());
901 	if (prefix || !split[0].empty) { // if complete match
902 		x.r = split[1];
903 		number = split[0];
904 	}
905 	return number;
906 }
907 
908 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.discriminator */
909 R decodeCxxDescriminator(R)(Demangler!R x)
910 if (isInputRange!R) {
911 	R descriminator;
912 	if (x.r.skipOver('_')) {
913 		if (x.r.skipOver('_')) { // number >= 10
914 			descriminator = x.decodeCxxNumber();
915 			assert(x.r.skipOver('_')); // suffix
916 		} else {					// number < 10
917 			x.r.skipOver('n'); // optional prefix
918 			/* TODO: Merge these two into a variant of popFront() that returns
919 			 the popped element. What is best out of:
920 			 - General: x.r.takeOne().to!R
921 			 - Arrays only: r[0..1]
922 			 - Needs cast: x.r.front
923 			 and are we in need of a combined variant of front() and popFront()
924 			 say takeFront() that may fail and requires a cast.
925 			 */
926 			/* descriminator = r[0..1]; // single digit */
927 			/* x.r.popFront(); */
928 			descriminator = x.r.moveFront().to!R;
929 		}
930 	}
931 	return descriminator;
932 }
933 
934 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.local-name */
935 R decodeCxxLocalName(R)(Demangler!R x)
936 if (isInputRange!R) {
937 	if (x.r.skipOver('Z')) {
938 		const functionEncoding = x.decodeCxxEncoding();
939 		x.r.skipOver('E');
940 		if (x.r.skipOver('D'))
941 			assert(0, "TODO: Decode C++0x Closure Type (lambda)"); // see https://mentorembedded.github.io/cxx-abi/abi.html#closure-types
942 		else {
943 			const entityNameMaybe = either(x.skipLiteral('s'), // NOTE: Literal first to speed up
944 										   x.decodeCxxName());
945 			const discriminator = x.decodeCxxDescriminator(); // optional
946 			return (functionEncoding ~
947 					x.scopeSeparator ~
948 					entityNameMaybe ~
949 					discriminator.to!R); /+ TODO: Optional +/
950 		}
951 	}
952 	return R.init;
953 }
954 
955 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.name */
956 R decodeCxxName(R)(Demangler!R x)
957 if (isInputRange!R) {
958 	return either(x.decodeCxxNestedName(),
959 				  x.decodeCxxUnscopedName(),
960 				  x.decodeCxxLocalName(), /+ TODO: order flipped +/
961 				  x.decodeCxxUnscopedTemplateNameAndArgs()); // NOTE: order flipped
962 }
963 
964 R decodeCxxNVOffset(R)(Demangler!R x)
965 if (isInputRange!R) {
966 	return x.decodeCxxNumber();
967 }
968 
969 R decodeCxxVOffset(R)(Demangler!R x)
970 if (isInputRange!R) {
971 	auto offset = x.decodeCxxNumber();
972 	assert(x.r.skipOver('_'));
973 	return offset ~ x.decodeCxxNumber();
974 }
975 
976 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.call-offset */
977 R decodeCxxCallOffset(R)(Demangler!R x)
978 if (isInputRange!R) {
979 	typeof(return) offset;
980 	if (x.r.skipOver('h')) {
981 		offset = x.decodeCxxNVOffset();
982 		assert(x.r.skipOver('_'));
983 	} else if (x.r.skipOver('v')) {
984 		offset = x.decodeCxxVOffset();
985 		assert(x.r.skipOver('_'));
986 	}
987 	return offset;
988 }
989 
990 /** See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.special-name */
991 R decodeCxxSpecialName(R)(Demangler!R x)
992 if (isInputRange!R) {
993 	auto restBackup = x.r;
994 	typeof(return) name;
995 	if (x.r.skipOver('S')) {
996 		switch (x.r.moveFront) {
997 			case 'V': name = "virtual table: "; break;
998 			case 'T': name = "VTT structure: "; break;
999 			case 'I': name = "typeinfo structure: "; break;
1000 			case 'S': name = "typeinfo name (null-terminated byte R): "; break;
1001 			default:
1002 				x.r = restBackup; // restore
1003 				return name;
1004 		}
1005 		name ~= x.decodeCxxType();
1006 	}
1007 	else if (x.r.skipOver(`GV`))
1008 		name = x.decodeCxxName();
1009 	else if (x.r.skipOver('T')) {
1010 		if (x.r.skipOver('c'))
1011 			name = x.r.tryEvery(x.decodeCxxCallOffset(),
1012 								x.decodeCxxCallOffset(),
1013 								x.decodeCxxEncoding()).joiner(` `).to!R;
1014 		else
1015 			name = x.r.tryEvery(x.decodeCxxCallOffset(),
1016 								x.decodeCxxEncoding()).joiner(` `).to!R;
1017 	}
1018 	return name;
1019 }
1020 
1021 /* Decode C++ Symbol.
1022    See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.encoding
1023  */
1024 R decodeCxxEncoding(R)(Demangler!R x) /* pure nothrow @nogc */ if (isInputRange!R)
1025 {
1026 	const localFlag = x.r.skipOver('L'); /+ TODO: What role does the L have in symbols starting with _ZL have? +/
1027 	if (const name = x.decodeCxxSpecialName())
1028 		return name;
1029 	const name = x.decodeCxxName();
1030 	auto type = x.decodeCxxBareFunctionType();
1031 	return name ~ type.to!R;
1032 }
1033 
1034 /** Demangled Expression. */
1035 alias Expr = string;
1036 
1037 alias LanguageT = ubyte;
1038 
1039 /** Mangled language. */
1040 enum Language : LanguageT {
1041 	unknown, nullValue = unknown, // `HybridHashMap` null support
1042 	c,
1043 	d,
1044 	cxx,
1045 	holeValue = LanguageT.max,				  // `HybridHashMap` hole support
1046 }
1047 
1048 struct Demangling {
1049 	Language language;
1050 	Expr unmangled;
1051 	auto opCast(T : bool)() const scope pure nothrow @nogc => expr !is null;
1052 }
1053 
1054 /** Demangle Symbol $(D r) and Detect Language.
1055 	See_Also: https://en.wikipedia.org/wiki/Name_mangling
1056 	See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangling
1057 	See_Also: https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
1058 */
1059 Demangling decodeSymbol(R)(Demangler!R x) /* pure nothrow @nogc */
1060 if (isInputRange!R) {
1061 	if (x.r.empty)
1062 		return Demangling(Language.init, x.r);
1063 	if (!x.r.startsWith('_'))
1064 		return Demangling(Language.c, x.r); // assume C
1065 	// See_Also: https://mentorembedded.github.io/cxx-abi/abi.html#mangle.mangled-name
1066 	if (x.r.skipOver(`_Z`))
1067 		return Demangling(Language.cxx, x.decodeCxxEncoding());
1068 	else {
1069 		import core.demangle: demangle;
1070 		const symAsD = x.r.demangle;
1071 		import std.conv: to;
1072 		if (symAsD != x.r) /+ TODO: Why doesn't (symAsD is r) work here? +/
1073 			return Demangling(Language.d, symAsD.to!R);
1074 		else
1075 			return Demangling(Language.init, x.r);
1076 	}
1077 }
1078 
1079 @trusted unittest {
1080 	import nxt.assert_ex;
1081 
1082 	assertEqual(demangler(`memcpy`).decodeSymbol(),
1083 				Demangling(Language.c, `memcpy`));
1084 
1085 	assertEqual(demangler(`memcpy`).decodeSymbol(),
1086 				Demangling(Language.c, `memcpy`));
1087 
1088 	assertEqual(demangler(`_Z1hi`).decodeSymbol(),
1089 				Demangling(Language.cxx, `h(int)`));
1090 
1091 	assertEqual(demangler(`_Z3foo3bar`).decodeSymbol(),
1092 				Demangling(Language.cxx, `foo(bar)`));
1093 
1094 	assertEqual(demangler(`_ZN1N1fE`).decodeSymbol(),
1095 				Demangling(Language.cxx, `N::f`));
1096 
1097 	assertEqual(demangler(`_ZN3Foo3BarEv`).decodeSymbol(),
1098 				Demangling(Language.cxx, `Foo::Bar()`));
1099 
1100 	assertEqual(demangler(`_ZN3FooC1Ev`).decodeSymbol(),
1101 				Demangling(Language.cxx, `Foo::Foo()`));
1102 
1103 	assertEqual(demangler(`_ZN9wikipedia7article6formatE`).decodeSymbol(),
1104 				Demangling(Language.cxx, `wikipedia::article::format`));
1105 
1106 	assertEqual(demangler(`_ZSt5state`).decodeSymbol(),
1107 				Demangling(Language.cxx, `::std::state`));
1108 
1109 	assertEqual(demangler(`_ZN9wikipedia7article8print_toERSo`).decodeSymbol(),
1110 				Demangling(Language.cxx, `wikipedia::article::print_to(::std::ostream&)`));
1111 
1112 	assertEqual(demangler(`_ZN9wikipedia7article8print_toEOSo`).decodeSymbol(),
1113 				Demangling(Language.cxx, `wikipedia::article::print_to(::std::ostream&&)`));
1114 
1115 	assertEqual(demangler(`_ZN9wikipedia7article6formatEv`, true).decodeSymbol(),
1116 				Demangling(Language.cxx, `wikipedia::article::format(void)`));
1117 
1118 	assertEqual(demangler(`_ZN9wikipedia7article6formatEv`, false).decodeSymbol(),
1119 				Demangling(Language.cxx, `wikipedia::article::format()`));
1120 
1121 	assertEqual(demangler(`_ZL8next_argRPPc`).decodeSymbol(),
1122 				Demangling(Language.cxx, `next_arg(char**&)`));
1123 
1124 	assertEqual(demangler(`_ZL10parse_archmPPKcS0_`, true).decodeSymbol(),
1125 				Demangling(Language.cxx, `parse_arch(unsigned long, char const**, char const*)`));
1126 
1127 	assertEqual(demangler(`_ZN5LexerC2EP6ModulePKhmmii`, true).decodeSymbol(),
1128 				Demangling(Language.cxx, `Lexer::Lexer(Module*, unsigned char const*, unsigned long, unsigned long, int, int)`));
1129 
1130 	assertEqual(demangler(`_Zrm1XS_`).decodeSymbol(),
1131 				Demangling(Language.cxx, `operator%(X, X)`));
1132 
1133 	assertEqual(demangler(`_ZZL8next_argRPPcE4keys`).decodeSymbol(),
1134 				Demangling(Language.cxx, `next_arg(char**&)::keys`));
1135 
1136 	assertEqual(demangler(`_ZN12ExpStatement9scopeCodeEP5ScopePP9StatementS4_S4`).decodeSymbol(),
1137 				Demangling(Language.cxx, `ExpStatement::scopeCode(Scope*, Statement**, Statement**, Statement**)`));
1138 
1139 	assertEqual(demangler(`_ZZ8genCmainP5ScopeE9cmaincode`).decodeSymbol(),
1140 				Demangling(Language.cxx, `genCmain(Scope*)::cmaincode`));
1141 
1142 	assertEqual(demangler("_Z7DtoLValP6DValue").decodeSymbol(),
1143 				Demangling(Language.cxx, `DtoLVal(DValue*)`));
1144 }