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