If you have ever looked at an objdump of a C++ program, you have likely seen something like this:
_ZN3foo3bar3bazI6sampleEE3quxvi3foo
This is a C++ mangled symbol, which encodes the namespaces, classes, and function/template arguments, using the Itanium ABI.
Specifically, it is for the following function:
void foo::bar::baz<sample>::qux(int, foo);
Your job is to demangle a C++ symbol in a dramatically simplified version of the Itanium ABI. (That is incompatible with c++filt
or __cxa_demangle()
, so don't try it).
Everything will be plain identifiers (so no int
, void
, or "special" identifiers like std::
), no const/pointers, no return type encoding, no reuse of template args, etc.
Specifically, these are the syntax rules:
- All mangled symbols start with
_Z
. - Everything is case sensitive.
- All identifiers are encoded as
<length><name>
, where<length>
is the positive length of<name>
in base 10, sofoo
is encoded as3foo
,sample
is encoded as6sample
, etc.- Naturally, this means identifiers will never start with a number (but they can contain numbers after the first letter/underscore). For you regex fans, it is
[a-zA-Z_][a-zA-Z0-9_]*
.
- Naturally, this means identifiers will never start with a number (but they can contain numbers after the first letter/underscore). For you regex fans, it is
- Each symbol is one base identifier, optionally followed by a list of function parameters.
- Each identifier can be prefixed with a namespace, or suffixed with a template.
- Namespaces are encoded as a sequence of 1 or more identifiers between an
N
and anE
. Each identifier is printed as the identifier, followed by::
. SoN3fooE
isfoo::
, andN3foo3barE
isfoo::bar::
.- Namespaces will never have a namespace themselves: you don't have to worry about
NN3fooE3barE
.
- Namespaces will never have a namespace themselves: you don't have to worry about
- Templates are encoded similar to namespaces, only using
I..E
instead ofN..E
. They are printed as a comma separated list of identifiers, wrapped in<angle brackets>
. These come before::
in a namespace. SoI3fooE
is<foo>
, andI3foo3barE
is<foo,bar>
. - These may be nested.
- Namespaces are encoded as a sequence of 1 or more identifiers between an
- All identifiers after the base symbol are to be treated as function parameters, and they are to be printed as a comma separated list wrapped in
(parentheses)
. This does not apply to namespaces or template arguments.
So, let's take a simpler example:
_ZN3fooI3barEE3baz3qux
_Z Mangle prefix
3baz Base: Identifier baz
N E Namespace
3foo Identifier foo
I E Template
3bar Identifier bar
3qux Parameters: Identifier qux
The result is this:
foo<bar>::baz(qux)
Your function or program will take a single string containing a mangled symbol, and the output will be the demangled symbol.
You can safely assume each string will only contain numbers, letters, and underscores, and that every identifier will be 99 characters or less.
Assume all symbols are valid, standard input/output format, you know the deal.
You can have any amount of whitespace between identifiers, however, empty parameter/template/namespaces and trailing commas are not allowed.
This is code-golf, so shortest program in bytes wins.
Test cases:
_Z1x -> x
_Z3foo3bar -> foo(bar)
_ZN3fooE3bar -> foo::bar
_Z3FOOI3barE -> FOO<bar>
_Z3foo3bar3baz -> foo(bar,baz)
_Z3fooI3barE3baz -> foo<bar>(baz)
_Z3fooI3bar3bazE -> foo<bar,baz>
_ZN3fooE3bar3baZ -> foo::bar(baZ)
_ZN3fooI3barEE3baz3qux -> foo<bar>::baz(qux)
_ZN9CPlusPlusI2isEE11soooooooooo5great -> CPlusPlus<is>::soooooooooo(great)
_ZN2soI1II4herdEE1UI4liekEE9templates -> so<I<herd>>::U<liek>::templates
_Z12identifier12 -> identifier12
_Z2_Z -> _Z
_ZN1a1b1c1d1e1f1g1hE1i -> a::b::c::d::e::f::g::h::i
_ZN1a1bI1c1d1eE1fE1gN1hE1iIN1jE1kI1lEEN1m1nE1o -> a::b<c,d,e>::f::g(h::i<j::k<l>>,m::n::o)
_Z14N3redE7herring1x7foo_bar -> N3redE7herring(x,foo_bar)