1 # see http://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B_Name_Mangling
2
3 class StupidDict(dict): pass
4
5 calling_conventions = StupidDict({
6 "A": "__cdecl",
7 "B": "__cdecl", # according to wine's source (http://source.winehq.org/source/dlls/msvcrt/undname.c#L561) - verify?
8 "G": "__stdcall",
9 "I": "__fastcall",
10 "E": "__thiscall",
11 })
12
13 opcodes = {
14 "?0": "(constructor)",
15 "?1": "(destructor)",
16 "?2": "(new)",
17 "?3": "(delete)",
18 "?4": "=",
19 "?5": ">>",
20 "?6": "<<",
21 "?7": "!",
22 "?8": "==",
23 "?9": "!=",
24 "?A": "[]",
25 "?B": "(conversion)",
26 "?C": "->",
27 "?D": "*",
28 "?E": "++",
29 "?F": "--",
30 "?G": "-",
31 "?H": "+",
32 "?I": "&",
33 "?J": "->*",
34 "?K": "/",
35 "?L": "%",
36 "?M": "<",
37 "?N": "<=",
38 "?O": ">",
39 "?P": ">=",
40 "?Q": ",",
41 "?R": "(call)",
42 "?S": "~",
43 "?T": "^",
44 "?U": "|",
45 "?V": "&&",
46 "?W": "||",
47 "?X": "*=",
48 "?Y": "+=",
49 "?Z": "-=",
50 "?_0": "/=",
51 "?_1": "%=",
52 "?_2": ">>=",
53 "?_3": "<<=",
54 "?_4": "&=",
55 "?_5": "|=",
56 "?_6": "^=",
57 "?_7": "(vtable)",
58 "?_C": "(unknown)",
59 "?_F": "(default constructor closure)",
60 "?_O": "(unknown)",
61 "?_R0?AV": "(RTTI Type Descriptor)",
62 "?_R1A@?OA@EA@": "(RTTI Base Class Descriptor at (0,-1,0,64))",
63 "?_R2": "(RTTI Base Class Array)",
64 "?_R3": "(RTTI Class Hierachy Descriptor)",
65 "?_R4": "(RTTI Complete Object Locator)",
66 "?_U": "(new[])",
67 "?_V": "(delete[])",
68 }
69
70 # XXX: ugly, anyway to do this without typechecking or similar?
71
72 def getshort_template(name):
73 """returns the short form of a template object if `name` is a template
74 for example when given Foo<class bar> returns Foo, if `name` isn't a template
75 it itself is returned"""
76 if isinstance(name, Template):
77 return name.name
78 else:
79 return name
80
81 opformatters = {
82 "(constructor)": lambda op: "%s::%s" % (op.name, getshort_template(op.name[-1])),
83 "(destructor)": lambda op: "%s::~%s" % (op.name, getshort_template(op.name[-1])),
84 "(new)": lambda op: "%s::operator new" % op.name,
85 "(delete)": lambda op: "%s::operator delete" % op.name,
86 "(conversion)": lambda op: "%s::(conversion)" % op.name,
87 "(call)": lambda op: "%s()()" % op.name,
88 "(unknown)": lambda op: "(unknown operator %s for %s)" % (op.opcode, op.name),
89 "(new[])": lambda op: "%s::operator new[]" % op.name,
90 "(delete[])": lambda op: "%s::operator delete[]" % op.name,
91 }
92
93 # XXX: according to wine there's a "thunk" type, figure out what it is,
94 # http://source.winehq.org/source/dlls/msvcrt/undname.c#L917
95 typecodes = {
96 "A": {"visibility": "private", "type": "method", "special": None},
97 "C": {"visibility": "private", "type": "method", "special": "static"},
98 "E": {"visibility": "private", "type": "method", "special": "virtual"},
99 "I": {"visibility": "protected", "type": "method", "special": None},
100 "K": {"visibility": "protected", "type": "method", "special": "static"},
101 "M": {"visibility": "protected", "type": "method", "special": "virtual"},
102 "Q": {"visibility": "public", "type": "method", "special": None},
103 "S": {"visibility": "public", "type": "method", "special": "static"},
104 "U": {"visibility": "public", "type": "method", "special": "virtual"},
105 "Y": {"type": "function"},
106 "0": {"type": "data", "name": 0},
107 "1": {"type": "data", "name": 1},
108 "2": {"type": "data", "name": 2},
109 "3": {"type": "data", "name": 3},
110 "6": {"type": "compiler_data", "name": 6},
111 }
112
113 datatypecodes = frozenset(("0", "1", "2", "3"))
114 compiler_types = frozenset(("6",))
115 functypecodes = frozenset(typecodes) - datatypecodes - compiler_types
116
117 sdatatypes = {
118 "C": "signed char",
119 "D": "char",
120 "E": "unsigned char",
121 "F": "short",
122 "G": "unsigned short",
123 "H": "int",
124 "I": "unsigned int",
125 "J": "long",
126 "K": "unsigned long",
127 "M": "float",
128 "N": "double",
129 "O": "long double",
130 "_J": "__int64",
131 "_K": "unsigned __int64",
132 "_N": "bool",
133 "_W": "wchar_t",
134 }
135
136 backrefable_sdatatypes = set(("_J", "_K", "_N", "_W"))
137
138 namedatatypes = {
139 "T": "union",
140 "U": "struct",
141 "V": "class",
142 "W4": "enum", # 4-byte
143 }
144
145 # ReferenceOrPointer in the BNF Syntax
146 ptrtypes = {
147 "A": (None, "&"),
148 "P": (None, "*"),
149 "Q": ("const", "*"),
150 "R": ("volatile", "*"),
151 }
152
153 # Pointer in the BNF Syntax
154 # Note this conflicts with the "A|B|C" syntax, but it looks like the
155 # second char of each of these doesn't conflict with SimpleDataType/X
156 # this might change in the future so be weary of breakage
157 ptrtypes2 = {
158 "AP": (None, "*"),
159 "BQ": ("const", "*"),
160 "CR": ("volatile", "*"),
161 }
162
163 storageclasses = {
164 "A": "Normal",
165 "B": "Volatile",
166 "C": "const",
167 "Z": "Executable",
168 }
169
170 # XXX: modifiers: not mentioned on the wikipedia page
171 modifiers = {
172 "A": None,
173 "B": "const",
174 "C": "volatile",
175 "D": "const volatile",
176 }
177
178 digits = {
179 "A": "0",
180 "B": "1",
181 "C": "2",
182 "D": "3",
183 "E": "4",
184 "F": "5",
185 "G": "6",
186 "H": "7",
187 "I": "8",
188 "J": "9",
189 "K": "A",
190 "L": "B",
191 "M": "C",
192 "N": "D",
193 "O": "E",
194 "P": "F",
195 }
196
197 import re
198 import string
199 import collections
200
201 def collapse_name(name, base):
202 """collapses the given name in-place based on `base`"""
203 # make sure base is a name and not an Operator or etc.
204 while isinstance(base, Reference):
205 base = base.to
206 while isinstance(name, Reference):
207 name = name.to
208 if isinstance(name, Name):
209 for i in xrange(len(name)-1):
210 if base[i] == name[0]:
211 del name[0]
212 # extra pass to remove stuff in containers
213 for part in name:
214 if hasattr(part, "collapse"):
215 part.collapse(base)
216
217 def collapsed_name(name, base):
218 """returns a new collapsed name based on `base`"""
219 import copy
220 n = copy.deepcopy(name)
221 collapse_name(n, base)
222 return n
223
224 class Args(list):
225 def collapse(self, base):
226 """modifies self in-place collapsing the names in it based on `base`"""
227 for arg in self:
228 collapse_name(arg, base)
229
230 def get_collapsed(self, base):
231 """returns a new Args instance with names collapsed based on `base`"""
232 import copy
233 n = copy.deepcopy(self)
234 n.collapse(base)
235 return n
236
237 def __str__(self):
238 return ", ".join(str(arg) for arg in self)
239
240 class Reference(object):
241 """base Reference class, anything that is mostly just a 1:1 container with special formatting should derive from this"""
242 def __init__(self, to):
243 self.to = to
244
245 class Name(list):
246 def __str__(self):
247 return "::".join(str(s) for s in self)
248
249 class Template(object):
250 def __init__(self, name, args):
251 self.name = name
252 self.args = args
253
254 def __str__(self):
255 return "%s<%s>" % (self.name, self.args)
256
257 def collapse(self, base):
258 self.args.collapse(base)
259
260 class Operator(Reference):
261 def __init__(self, name, opcode):
262 Reference.__init__(self, name)
263 self.name = name
264 self.opcode = opcode
265 self.opstr = opcodes[opcode]
266
267 def __str__(self):
268 if self.opstr in opformatters:
269 return opformatters[self.opstr](self)
270 return "%s::operator%s" % (self.name, self.opstr)
271
272 class NamedDataType(Reference):
273 def __init__(self, typecode, name):
274 Reference.__init__(self, name)
275 self.typecode = typecode
276 self.typestr = namedatatypes[typecode]
277 self.name = name
278
279 def __str__(self):
280 return "%s %s" % (self.typestr, self.name)
281
282 class ModifiedDataType(Reference):
283 def __init__(self, type, modifier):
284 Reference.__init__(self, type)
285 self.type = type
286 self.modifier = modifier
287
288 def __str__(self):
289 return "%s %s" % (self.modifier, self.type)
290
291 class Pointer(Reference):
292 def __init__(self, to, types):
293 Reference.__init__(self, to)
294 self.types = types
295
296 def __str__(self):
297 # XXX: what's the proper pointer syntax? - Think I got it right now (http://articles.techrepublic.com.com/5100-22-1052161.html)
298 # XXX: should these be reversed here or when constructing the object?
299 types = reversed(self.types)
300 pairs = ["%s%s " % (ptrtype, modifier) if modifier else "%s" % ptrtype for modifier, ptrtype in types]
301 #prefixes = [t[0] for t in self.types if t[0]]
302 #ptrs = reversed([t[1] for t in self.types])
303 return "%s %s" % (self.to, "".join(pairs).rstrip())
304
305 class Array(Reference):
306 def __init__(self, of, dimensions):
307 Reference.__init__(self, of)
308 self.of = of
309 self.dimensions = dimensions
310
311 def __str__(self):
312 return "%s %s" % (self.of, "".join("[%d]" % dim for dim in self.dimensions))
313
314 class Function(object):
315 def __init__(self, name, type, callingconv, ret_type, args, storage):
316 self.name = name
317 self.type = type
318 self.callingconv = callingconv
319 self.ret_type = ret_type
320 self.args = args
321 self.storage = storage
322
323 def __str__(self):
324 # XXX: calling convention, storage class, static/visibility?
325 # XXX: do collapsing, should probably be done optionally in __format__ later
326 args = self.args.get_collapsed(self.name)
327 ret_type = collapsed_name(self.ret_type, self.name)
328 return "%s %s(%s)" % (ret_type, self.name, args)
329
330 class Method(Function):
331 def __init__(self, name, type, modifier, callingconv, ret_type, args, storage):
332 Function.__init__(self, name, type, callingconv, ret_type, args, storage)
333 self.modifier = modifier
334
335 def __str__(self):
336 #args = ", ".join(str(arg) for arg in self.args)
337 postfix = " %s" % self.modifier if self.modifier else ""
338 prefix = "%s " % self.type["special"] if self.type["special"] else ""
339 return "%s%s%s" % (prefix, Function.__str__(self), postfix)
340
341 class Variable(object):
342 def __init__(self, name, type, datatype, storage):
343 self.name = name
344 self.type = type
345 self.datatype = datatype
346 self.storage = storage
347
348 def __str__(self):
349 return "%s %s" % (self.datatype, self.name)
350
351 class CompilerData(object):
352 def __init__(self, name, type, modifier, extname):
353 self.name = name
354 self.type = type
355 self.modifier = modifier
356 self.extname = extname
357
358 def __str__(self):
359 prefix = "%s " % self.modifier if self.modifier else ""
360 return "%s%s" % (prefix, self.name)
361
362 class FuncPointer(Pointer):
363 def __str__(self):
364 # TODO: is there any reasonable case where you'd have a pointer to a func pointer?
365 # XXX: include Calling Convention?
366 ptrs = [t[1] for t in self.types]
367 args = self.to.args
368 return "%s (%s%s)(%s)" % (self.to.ret_type, "".join(ptrs), self.to.name, args)
369
370 def get_name(s, backrefs, reverse=True):
371 nbackrefs = backrefs["name"]
372 validc = r"[a-zA-Z_]\w*"
373 identifier = re.compile(r"(?P<backref>\d)|(?P<name>%s)@" % validc)
374 def identname(match):
375 """returns (should_to_backrefs, name)"""
376 gd = match.groupdict()
377 name = None
378 backref = gd.get("backref", None)
379 if backref:
380 name = nbackrefs[int(backref)]
381 else:
382 name = gd["name"]
383 return (False if backref else True, name)
384 curr = s
385 identifiers = []
386 while True:
387 if curr[:2] == "?$":
388 curr = curr[2:]
389 unqmatch = identifier.match(curr)
390 add, unQualifiedName = identname(unqmatch)
391 if add:
392 nbackrefs.append(unQualifiedName)
393 curr = curr[unqmatch.end():]
394 tbr = collections.defaultdict(list)
395 (args, curr) = get_arglist(curr, tbr)
396 (name, curr) = get_name(curr, backrefs, reverse=False)
397 identifiers.append(Template(unQualifiedName, args))
398 identifiers.extend(name)
399 continue
400 m = identifier.match(curr)
401 if not m:
402 break
403 add, name = identname(m)
404 if add:
405 nbackrefs.append(name)
406 identifiers.append(name)
407 curr = curr[m.end():]
408 if reverse:
409 name = list(reversed(identifiers))
410 else:
411 name = identifiers
412 return (Name(name), curr)
413
414 sdatatype_p = "|".join(re.escape(dt) for dt in sdatatypes)
415 ndatatype_p = "|".join(re.escape(dt) for dt in namedatatypes)
416 simpledtype_re = re.compile(r"(?P<simple>%s)|(?P<named>%s)" % (sdatatype_p, ndatatype_p))
417
418 rop_re = re.compile("|".join(re.escape(ptype) for ptype in ptrtypes))
419 ptrref_re = re.compile("|".join(re.escape(ptype) for ptype in ptrtypes2))
420
421 sane_y_re = re.compile(r"Y\d[%s]" % re.escape("".join(digits)))
422
423 class InvalidDataType(Exception):
424 def __init__(self, s):
425 self.s = s
426
427 def __str__(self):
428 return "Invalid Data Type at the start of %r" % self.s
429
430 class CantParse(Exception):
431 def __init__(self, symbol, scheme):
432 self.symbol = symbol
433 self.scheme = scheme
434
435 def __str__(self):
436 return "Can't parse %r with the %s mangling scheme" % (self.symbol, self.scheme)
437
438 def get_number(curr, backrefs):
439 nstr, sep, curr = curr.partition("@")
440 return (int("".join(digits[c] for c in nstr), 16), curr)
441
442 def get_datatype(s, backrefs, record=True):
443 pbackrefs = backrefs["pointer"]
444 curr = s
445 # SimpleDataType stuff first
446 m = simpledtype_re.match(curr)
447 if m:
448 gd = m.groupdict()
449 curr = curr[m.end():]
450 assert gd.get("simple") or gd.get("named")
451 if gd.get("simple"):
452 dtype = sdatatypes[gd["simple"]]
453 if record:
454 pbackrefs.append(dtype)
455 return (dtype, curr)
456 elif gd.get("named"):
457 name, curr = get_name(curr, backrefs)
458 assert curr[0] == "@"
459 curr = curr[1:]
460 dtype = NamedDataType(gd["named"], Name(name))
461 if record:
462 pbackrefs.append(dtype)
463 return (dtype, curr)
464 # XXX: Pointers?
465 m = rop_re.match(curr)
466 if m:
467 types = [ptrtypes[m.group(0)]]
468 curr = curr[m.end():]
469 while True:
470 m = ptrref_re.match(curr)
471 if not m:
472 break
473 types.append(ptrtypes2[m.group(0)])
474 curr = curr[m.end():]
475 if curr[0] in ("A", "B", "C"):
476 # XXX: looks like A == normal, B == const, C == volatile
477 modifier = modifiers[curr[0]]
478 curr = curr[1:]
479 if curr[0] == "X":
480 base, curr = "void", curr[1:]
481 else:
482 base, curr = get_datatype(curr, backrefs, False)
483 if modifier:
484 base = ModifiedDataType(base, modifier)
485 dtype = Pointer(base, types)
486 elif curr[0] in ("6", "8"):
487 curr = curr[1:]
488 fdict, curr = get_functype(curr, backrefs)
489 # XXX: anonymous and no typecode, what to do?
490 func = Function("", typecodes["Y"], fdict["cconv"], fdict["ret"], fdict["args"], fdict["storage_class"])
491 dtype = FuncPointer(func, types)
492 pbackrefs.append(dtype)
493 return dtype, curr
494 if curr[0] in string.digits:
495 dtype = pbackrefs[int(curr[0])]
496 curr = curr[1:]
497 return dtype, curr
498 # XXX: wtf, looks like there's a second syntax for Y: Y<amount><firstnum in base10>$$C<modifier><type>
499 if sane_y_re.match(curr):
500 curr = curr[1:]
501 amount = int(curr[0]) + 1
502 curr = curr[1:]
503 dimensions = []
504 for i in xrange(amount):
505 dimension, curr = get_number(curr, backrefs)
506 dimensions.append(dimension)
507 base, curr = get_datatype(curr, backrefs)
508 dtype = Array(base, dimensions)
509 return (dtype, curr)
510 raise InvalidDataType(curr)
511
512 # this is in a separate function than get_datatype because it seems it only applies to the return value
513 def get_modifieddatatype(s, backrefs):
514 curr = s
515 assert curr[0] == "?"
516 curr = curr[1:]
517 modifier = modifiers[curr[0]]
518 curr = curr[1:]
519 dtype, curr = get_datatype(curr, backrefs)
520 if modifier:
521 dtype = ModifiedDataType(dtype, modifier)
522 return (dtype, curr)
523
524 def get_arglist(s, backrefs):
525 if s[0] == "X":
526 return (Args(), s[1:])
527 curr = s
528 args = Args()
529 while True:
530 if curr[0] == "@" or curr[0] == "Z":
531 curr = curr[1:]
532 break
533 try:
534 dt, curr = get_datatype(curr, backrefs)
535 except InvalidDataType:
536 break
537 args.append(dt)
538 #m = simpledtype_re.match(curr)
539 #if not m:
540 # break
541 #gd = m.groupdict()
542 #curr = curr[m.end():]
543 #assert gd.get("simple") or gd.get("named")
544 #if gd.get("simple"):
545 # args.append(sdatatypes[gd["simple"]])
546 #elif gd.get("named"):
547 # name, curr = get_name(curr, backrefs)
548 # assert curr[0] == "@"
549 # curr = curr[1:]
550 # args.append(NamedDataType(gd["named"], Name(name)))
551 return (args, curr)
552
553 ftypecode_p = "|".join(re.escape(typecode) for typecode in functypecodes)
554 dtypecode_p = "|".join(re.escape(typecode) for typecode in datatypecodes)
555 ctypecode_p = "|".join(re.escape(typecode) for typecode in compiler_types)
556 typecodes_re = re.compile(r"(?P<function>%s)|(?P<datatype>%s)|(?P<compilertype>%s)" % (ftypecode_p, dtypecode_p, ctypecode_p))
557
558 def get_typecode(s, backrefs):
559 m = typecodes_re.match(s)
560 curr = s[m.end():]
561 gd = m.groupdict()
562 assert gd["function"] or gd["datatype"] or gd["compilertype"]
563 typecode_d = {}
564 if gd["function"]:
565 typecode_d["type"] = typecodes[gd["function"]]
566 if typecode_d["type"]["type"] == "method":
567 if typecode_d["type"]["special"] not in ("static", "thunk"):
568 # read the modifier
569 typecode_d["modifier"] = modifiers[curr[0]]
570 curr = curr[1:]
571 else:
572 # static method, has no modifier, fake it though
573 typecode_d["modifier"] = None
574 functype, curr = get_functype(curr, backrefs)
575 typecode_d.update(functype)
576 elif gd["datatype"]:
577 typecode_d["type"] = typecodes[gd["datatype"]]
578 datatype, curr = get_datatype(curr, backrefs)
579 sclass = storageclasses[curr[0]]
580 curr = curr[1:]
581 typecode_d["datatype"] = datatype
582 typecode_d["storage_class"] = sclass
583 elif gd["compilertype"]:
584 typecode_d["type"] = typecodes[gd["compilertype"]]
585 typecode_d["modifier"] = modifiers[curr[0]]
586 curr = curr[1:]
587 typecode_d["name"], curr = get_name(curr, backrefs)
588 return typecode_d, curr
589
590 callingconvre = re.compile("|".join(re.escape(convention) for convention in calling_conventions))
591
592 def get_functype(s, backrefs):
593 m = callingconvre.match(s)
594 curr = s[m.end():]
595 cconv = calling_conventions[m.group(0)]
596 ret = None
597 if curr[0] in ("X", "@"):
598 curr = curr[1:]
599 ret = "void"
600 elif curr[0] == "?":
601 ret, curr = get_modifieddatatype(curr, backrefs)
602 if ret is None:
603 ret, curr = get_datatype(curr, backrefs)
604 args, curr = get_arglist(curr, backrefs)
605 sclass = storageclasses[curr[0]]
606 curr = curr[1:]
607 ftype_dict = {
608 "cconv": cconv,
609 "ret": ret,
610 "args": args,
611 "storage_class": sclass,
612 }
613 return ftype_dict, curr
614
615 opre = re.compile("|".join(re.escape(opcode) for opcode in opcodes))
616
617 def parse_mangledsymbol(s):
618 backrefs = collections.defaultdict(list)
619 if s[0] != "?":
620 # XXX: Except here?
621 raise CantParse(s, "MSVC++")
622 curr = s[1:]
623 opmatch = opre.match(curr)
624 if opmatch:
625 curr = curr[opmatch.end():]
626 name, curr = get_name(curr, backrefs)
627 if opmatch:
628 name = Operator(name, opmatch.group(0))
629 assert curr[0] == "@"
630 curr = curr[1:]
631 typecode, curr = get_typecode(curr, backrefs)
632 type = typecode["type"]["type"]
633 if type == "data":
634 ret = Variable(name, typecode["type"], typecode["datatype"], typecode["storage_class"])
635 elif type == "method":
636 ret = Method(name, typecode["type"], typecode["modifier"], typecode["cconv"], typecode["ret"], typecode["args"], typecode["storage_class"])
637 elif type == "compiler_data":
638 ret = CompilerData(name, typecode["type"], typecode["modifier"], typecode["name"])
639 elif type == "function":
640 ret = Function(name, typecode["type"], typecode["cconv"], typecode["ret"], typecode["args"], typecode["storage_class"])
641 return ret
642
643 # Just do the basic Separation of parts with pyparsing, don't try to
644 # understand it with it (too hard, it seems to be against me whatever
645 # I try)
646 #from pyparsing import Or, Literal, Word, OneOrMore, alphas, Regex, Optional, ZeroOrMore, Group
647 #from string import digits
648 #ops = Or([Literal(op) for op in opcodes])("opcode")
649 #del op
650
651 #validc = Regex(r"[a-zA-Z_]\w*")
652
653 #anydigit = Or([Literal(digit) for digit in digits])
654 #backref = anydigit
655 #identifier = (validc + "@") ^ backref
656 # XXX: template handling, verify this is correct
657 #name = ("?$" + identifier + (lambda: args)() + OneOrMore(identifier)) ^ OneOrMore(identifier)
658
659 #cconv = Or([Literal(convention) for convention in calling_conventions])
660 #del convention
661
662 #ftypecode = Or([Literal(typecode) for typecode in functypecodes])
663 #datatypecode = Or([Literal(typecode) for typecode in datatypecodes])
664 #del typecode
665
666 #simpledtype = Or([Literal(dtype) for dtype in sdatatypes])
667 #namedtype = Or([Literal(dtype) for dtype in namedatatypes])
668 #simpletype = simpledtype ^ (namedtype + name + "@")
669
670 #rop = Or([Literal(ptype) for ptype in ptrtypes]) # reference or pointer
671 #pointerrefs = ZeroOrMore(Or([Literal(ptype) for ptype in ptrtypes2]))
672 #del ptype
673 #ptrref = anydigit
674 # XXX: FunctionType forms of ptrtype
675 #ptrtype = (rop + pointerrefs + (Literal("A") ^ "B" ^ "C") + (simpletype ^ "X")) ^ (ptrref)
676
677 #dtype = simpletype ^ ptrtype
678
679 #storageclass = Or([Literal(stype) for stype in storageclasses])
680 #del stype
681
682
683 #args = Group("X" ^ (OneOrMore(dtype) + Optional(Literal("@") ^ "Z")))
684 # Note: looks like ReturnValue can be @ for constructors atleast
685 #functype = cconv + (Literal("X") ^ "@" ^ dtype) + args + storageclass
686
687 #typecode = (ftypecode + functype) ^ (datatypecode + dtype + storageclass)
688
689 #mangledsymbol = "?" + Optional(ops) + name.copy()("name") + "@" + typecode.copy()("TypeCode")
690
691 tests = [
692 "?xorWith@BitSet@xercesc_2_8@@QAEXABV12@@Z",
693 "??0ASCIIRangeFactory@xercesc_2_8@@QAE@XZ",
694 "??0?$XMLHolder@U_RTL_CRITICAL_SECTION@@@xercesc_2_8@@QAE@XZ",
695 "??0ArrayIndexOutOfBoundsException@xercesc_2_8@@QAE@QBDIW4Codes@XMLExcepts@1@QB_W222PAVMemoryManager@1@@Z"
696 ]
697
698 def reorder_ident(parsetree):
699 # kill @'s
700 inreverse = parsetree[::2]
701 # reverse to get (namespace, namespace, class, whatever, etc.)
702 return tuple(reversed(inreverse))
703