using Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Ryujinx.HLE.HOS.Diagnostics.Demangler { class Demangler { private const string Base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; private readonly List _substitutionList = new(); private List _templateParamList = new(); private readonly List _forwardTemplateReferenceList = new(); public string Mangled { get; private set; } private int _position; private readonly int _length; private bool _canForwardTemplateReference; private bool _canParseTemplateArgs; public Demangler(string mangled) { Mangled = mangled; _position = 0; _length = mangled.Length; _canParseTemplateArgs = true; } private bool ConsumeIf(string toConsume) { var mangledPart = Mangled.AsSpan(_position); if (mangledPart.StartsWith(toConsume.AsSpan())) { _position += toConsume.Length; return true; } return false; } private ReadOnlySpan PeekString(int offset = 0, int length = 1) { if (_position + offset >= length) { return null; } return Mangled.AsSpan(_position + offset, length); } private char Peek(int offset = 0) { if (_position + offset >= _length) { return '\0'; } return Mangled[_position + offset]; } private char Consume() { if (_position < _length) { return Mangled[_position++]; } return '\0'; } private int Count() { return _length - _position; } private static int FromBase36(string encoded) { char[] reversedEncoded = encoded.ToLower().ToCharArray().Reverse().ToArray(); int result = 0; for (int i = 0; i < reversedEncoded.Length; i++) { int value = Base36.IndexOf(reversedEncoded[i]); if (value == -1) { return -1; } result += value * (int)Math.Pow(36, i); } return result; } private int ParseSeqId() { ReadOnlySpan part = Mangled.AsSpan(_position); int seqIdLen = 0; for (; seqIdLen < part.Length; seqIdLen++) { if (!char.IsLetterOrDigit(part[seqIdLen])) { break; } } _position += seqIdLen; return FromBase36(new string(part[..seqIdLen])); } // ::= S _ // ::= S_ // ::= St # std:: // ::= Sa # std::allocator // ::= Sb # std::basic_string // ::= Ss # std::basic_string, std::allocator > // ::= Si # std::basic_istream > // ::= So # std::basic_ostream > // ::= Sd # std::basic_iostream > private BaseNode ParseSubstitution() { if (!ConsumeIf("S")) { return null; } char substitutionSecondChar = Peek(); if (char.IsLower(substitutionSecondChar)) { switch (substitutionSecondChar) { case 'a': _position++; return new SpecialSubstitution(SpecialSubstitution.SpecialType.Allocator); case 'b': _position++; return new SpecialSubstitution(SpecialSubstitution.SpecialType.BasicString); case 's': _position++; return new SpecialSubstitution(SpecialSubstitution.SpecialType.String); case 'i': _position++; return new SpecialSubstitution(SpecialSubstitution.SpecialType.IStream); case 'o': _position++; return new SpecialSubstitution(SpecialSubstitution.SpecialType.OStream); case 'd': _position++; return new SpecialSubstitution(SpecialSubstitution.SpecialType.IOStream); default: return null; } } // ::= S_ if (ConsumeIf("_")) { if (_substitutionList.Count != 0) { return _substitutionList[0]; } return null; } // ::= S _ int seqId = ParseSeqId(); if (seqId < 0) { return null; } seqId++; if (!ConsumeIf("_") || seqId >= _substitutionList.Count) { return null; } return _substitutionList[seqId]; } // NOTE: thoses data aren't used in the output // ::= h _ // ::= v _ // ::= // # non-virtual base override // ::= _ // # virtual base override, with vcall offset private bool ParseCallOffset() { if (ConsumeIf("h")) { return ParseNumber(true).Length == 0 || !ConsumeIf("_"); } else if (ConsumeIf("v")) { return ParseNumber(true).Length == 0 || !ConsumeIf("_") || ParseNumber(true).Length == 0 || !ConsumeIf("_"); } return true; } // ::= # non-dependent type name, dependent type name, or dependent typename-specifier // ::= Ts # dependent elaborated type specifier using 'struct' or 'class' // ::= Tu # dependent elaborated type specifier using 'union' // ::= Te # dependent elaborated type specifier using 'enum' private BaseNode ParseClassEnumType() { string elaboratedType = null; if (ConsumeIf("Ts")) { elaboratedType = "struct"; } else if (ConsumeIf("Tu")) { elaboratedType = "union"; } else if (ConsumeIf("Te")) { elaboratedType = "enum"; } BaseNode name = ParseName(); if (name == null) { return null; } if (elaboratedType == null) { return name; } return new ElaboratedType(elaboratedType, name); } // ::= [] [] [Dx] F [Y] [] E // ::= + // # types are possible return type, then parameter types // ::= Do # non-throwing exception-specification (e.g., noexcept, throw()) // ::= DO E # computed (instantiation-dependent) noexcept // ::= Dw + E # dynamic exception specification with instantiation-dependent types private BaseNode ParseFunctionType() { Cv cvQualifiers = ParseCvQualifiers(); BaseNode exceptionSpec = null; if (ConsumeIf("Do")) { exceptionSpec = new NameType("noexcept"); } else if (ConsumeIf("DO")) { BaseNode expression = ParseExpression(); if (expression == null || !ConsumeIf("E")) { return null; } exceptionSpec = new NoexceptSpec(expression); } else if (ConsumeIf("Dw")) { List types = new(); while (!ConsumeIf("E")) { BaseNode type = ParseType(); if (type == null) { return null; } types.Add(type); } exceptionSpec = new DynamicExceptionSpec(new NodeArray(types)); } // We don't need the transaction ConsumeIf("Dx"); if (!ConsumeIf("F")) { return null; } // extern "C" ConsumeIf("Y"); BaseNode returnType = ParseType(); if (returnType == null) { return null; } Reference referenceQualifier = Reference.None; List paramsList = new(); while (true) { if (ConsumeIf("E")) { break; } if (ConsumeIf("v")) { continue; } if (ConsumeIf("RE")) { referenceQualifier = Reference.LValue; break; } else if (ConsumeIf("OE")) { referenceQualifier = Reference.RValue; break; } BaseNode type = ParseType(); if (type == null) { return null; } paramsList.Add(type); } return new FunctionType(returnType, new NodeArray(paramsList), new CvType(cvQualifiers, null), new SimpleReferenceType(referenceQualifier, null), exceptionSpec); } // ::= A _ // ::= A [] _ private BaseNode ParseArrayType() { if (!ConsumeIf("A")) { return null; } BaseNode elementType; if (char.IsDigit(Peek())) { string dimension = ParseNumber(); if (dimension.Length == 0 || !ConsumeIf("_")) { return null; } elementType = ParseType(); if (elementType == null) { return null; } return new ArrayType(elementType, dimension); } if (!ConsumeIf("_")) { BaseNode dimensionExpression = ParseExpression(); if (dimensionExpression == null || !ConsumeIf("_")) { return null; } elementType = ParseType(); if (elementType == null) { return null; } return new ArrayType(elementType, dimensionExpression); } elementType = ParseType(); if (elementType == null) { return null; } return new ArrayType(elementType); } // ::= // ::= (PARTIAL) // ::= // ::= // ::= (TODO) // ::= (TODO) // ::= // ::= // ::= // ::= P # pointer // ::= R # l-value reference // ::= O # r-value reference (C++11) // ::= C # complex pair (C99) // ::= G # imaginary (C99) // ::= # See Compression below private BaseNode ParseType(NameParserContext context = null) { // Temporary context context ??= new NameParserContext(); BaseNode result; switch (Peek()) { case 'r': case 'V': case 'K': int typePos = 0; if (Peek(typePos) == 'r') { typePos++; } if (Peek(typePos) == 'V') { typePos++; } if (Peek(typePos) == 'K') { typePos++; } if (Peek(typePos) == 'F' || (Peek(typePos) == 'D' && (Peek(typePos + 1) == 'o' || Peek(typePos + 1) == 'O' || Peek(typePos + 1) == 'w' || Peek(typePos + 1) == 'x'))) { result = ParseFunctionType(); break; } Cv cv = ParseCvQualifiers(); result = ParseType(context); if (result == null) { return null; } result = new CvType(cv, result); break; case 'U': // TODO: return null; case 'v': _position++; return new NameType("void"); case 'w': _position++; return new NameType("wchar_t"); case 'b': _position++; return new NameType("bool"); case 'c': _position++; return new NameType("char"); case 'a': _position++; return new NameType("signed char"); case 'h': _position++; return new NameType("unsigned char"); case 's': _position++; return new NameType("short"); case 't': _position++; return new NameType("unsigned short"); case 'i': _position++; return new NameType("int"); case 'j': _position++; return new NameType("unsigned int"); case 'l': _position++; return new NameType("long"); case 'm': _position++; return new NameType("unsigned long"); case 'x': _position++; return new NameType("long long"); case 'y': _position++; return new NameType("unsigned long long"); case 'n': _position++; return new NameType("__int128"); case 'o': _position++; return new NameType("unsigned __int128"); case 'f': _position++; return new NameType("float"); case 'd': _position++; return new NameType("double"); case 'e': _position++; return new NameType("long double"); case 'g': _position++; return new NameType("__float128"); case 'z': _position++; return new NameType("..."); case 'u': _position++; return ParseSourceName(); case 'D': switch (Peek(1)) { case 'd': _position += 2; return new NameType("decimal64"); case 'e': _position += 2; return new NameType("decimal128"); case 'f': _position += 2; return new NameType("decimal32"); case 'h': _position += 2; // FIXME: GNU c++flit returns this but that is not what is supposed to be returned. return new NameType("half"); // return new NameType("decimal16"); case 'i': _position += 2; return new NameType("char32_t"); case 's': _position += 2; return new NameType("char16_t"); case 'a': _position += 2; return new NameType("decltype(auto)"); case 'n': _position += 2; // FIXME: GNU c++flit returns this but that is not what is supposed to be returned. return new NameType("decltype(nullptr)"); // return new NameType("std::nullptr_t"); case 't': case 'T': _position += 2; result = ParseDecltype(); break; case 'o': case 'O': case 'w': case 'x': result = ParseFunctionType(); break; default: return null; } break; case 'F': result = ParseFunctionType(); break; case 'A': return ParseArrayType(); case 'M': // TODO: _position++; return null; case 'T': // might just be a class enum type if (Peek(1) == 's' || Peek(1) == 'u' || Peek(1) == 'e') { result = ParseClassEnumType(); break; } result = ParseTemplateParam(); if (result == null) { return null; } if (_canParseTemplateArgs && Peek() == 'I') { BaseNode templateArguments = ParseTemplateArguments(); if (templateArguments == null) { return null; } result = new NameTypeWithTemplateArguments(result, templateArguments); } break; case 'P': _position++; result = ParseType(context); if (result == null) { return null; } result = new PointerType(result); break; case 'R': _position++; result = ParseType(context); if (result == null) { return null; } result = new ReferenceType("&", result); break; case 'O': _position++; result = ParseType(context); if (result == null) { return null; } result = new ReferenceType("&&", result); break; case 'C': _position++; result = ParseType(context); if (result == null) { return null; } result = new PostfixQualifiedType(" complex", result); break; case 'G': _position++; result = ParseType(context); if (result == null) { return null; } result = new PostfixQualifiedType(" imaginary", result); break; case 'S': if (Peek(1) != 't') { BaseNode substitution = ParseSubstitution(); if (substitution == null) { return null; } if (_canParseTemplateArgs && Peek() == 'I') { BaseNode templateArgument = ParseTemplateArgument(); if (templateArgument == null) { return null; } result = new NameTypeWithTemplateArguments(substitution, templateArgument); break; } return substitution; } else { result = ParseClassEnumType(); break; } default: result = ParseClassEnumType(); break; } if (result != null) { _substitutionList.Add(result); } return result; } // ::= TV # virtual table // ::= TT # VTT structure (construction vtable index) // ::= TI # typeinfo structure // ::= TS # typeinfo name (null-terminated byte string) // ::= Tc // ::= TW # Thread-local wrapper // ::= TH # Thread-local initialization // ::= T // # base is the nominal target function of thunk // ::= GV # Guard variable for one-time initialization private BaseNode ParseSpecialName(NameParserContext context = null) { if (Peek() != 'T') { if (ConsumeIf("GV")) { BaseNode name = ParseName(); if (name == null) { return null; } return new SpecialName("guard variable for ", name); } return null; } BaseNode node; switch (Peek(1)) { // ::= TV # virtual table case 'V': _position += 2; node = ParseType(context); if (node == null) { return null; } return new SpecialName("vtable for ", node); // ::= TT # VTT structure (construction vtable index) case 'T': _position += 2; node = ParseType(context); if (node == null) { return null; } return new SpecialName("VTT for ", node); // ::= TI # typeinfo structure case 'I': _position += 2; node = ParseType(context); if (node == null) { return null; } return new SpecialName("typeinfo for ", node); // ::= TS # typeinfo name (null-terminated byte string) case 'S': _position += 2; node = ParseType(context); if (node == null) { return null; } return new SpecialName("typeinfo name for ", node); // ::= Tc case 'c': _position += 2; if (ParseCallOffset() || ParseCallOffset()) { return null; } node = ParseEncoding(); if (node == null) { return null; } return new SpecialName("covariant return thunk to ", node); // extension ::= TC _ case 'C': _position += 2; BaseNode firstType = ParseType(); if (firstType == null || ParseNumber(true).Length == 0 || !ConsumeIf("_")) { return null; } BaseNode secondType = ParseType(); return new CtorVtableSpecialName(secondType, firstType); // ::= TH # Thread-local initialization case 'H': _position += 2; node = ParseName(); if (node == null) { return null; } return new SpecialName("thread-local initialization routine for ", node); // ::= TW # Thread-local wrapper case 'W': _position += 2; node = ParseName(); if (node == null) { return null; } return new SpecialName("thread-local wrapper routine for ", node); default: _position++; bool isVirtual = Peek() == 'v'; if (ParseCallOffset()) { return null; } node = ParseEncoding(); if (node == null) { return null; } if (isVirtual) { return new SpecialName("virtual thunk to ", node); } return new SpecialName("non-virtual thunk to ", node); } } // ::= [r] [V] [K] # restrict (C99), volatile, const private Cv ParseCvQualifiers() { Cv qualifiers = Cv.None; if (ConsumeIf("r")) { qualifiers |= Cv.Restricted; } if (ConsumeIf("V")) { qualifiers |= Cv.Volatile; } if (ConsumeIf("K")) { qualifiers |= Cv.Const; } return qualifiers; } // ::= R # & ref-qualifier // ::= O # && ref-qualifier private SimpleReferenceType ParseRefQualifiers() { Reference result = Reference.None; if (ConsumeIf("O")) { result = Reference.RValue; } else if (ConsumeIf("R")) { result = Reference.LValue; } return new SimpleReferenceType(result, null); } private static BaseNode CreateNameNode(BaseNode prev, BaseNode name, NameParserContext context) { BaseNode result = name; if (prev != null) { result = new NestedName(name, prev); } if (context != null) { context.FinishWithTemplateArguments = false; } return result; } private int ParsePositiveNumber() { ReadOnlySpan part = Mangled.AsSpan(_position); int numberLength = 0; for (; numberLength < part.Length; numberLength++) { if (!char.IsDigit(part[numberLength])) { break; } } _position += numberLength; if (numberLength == 0) { return -1; } return int.Parse(part[..numberLength]); } private string ParseNumber(bool isSigned = false) { if (isSigned) { ConsumeIf("n"); } if (Count() == 0 || !char.IsDigit(Mangled[_position])) { return null; } ReadOnlySpan part = Mangled.AsSpan(_position); int numberLength = 0; for (; numberLength < part.Length; numberLength++) { if (!char.IsDigit(part[numberLength])) { break; } } _position += numberLength; return new string(part[..numberLength]); } // ::= private BaseNode ParseSourceName() { int length = ParsePositiveNumber(); if (Count() < length || length <= 0) { return null; } string name = Mangled.Substring(_position, length); _position += length; if (name.StartsWith("_GLOBAL__N")) { return new NameType("(anonymous namespace)"); } return new NameType(name); } // ::= nw # new // ::= na # new[] // ::= dl # delete // ::= da # delete[] // ::= ps # + (unary) // ::= ng # - (unary) // ::= ad # & (unary) // ::= de # * (unary) // ::= co # ~ // ::= pl # + // ::= mi # - // ::= ml # * // ::= dv # / // ::= rm # % // ::= an # & // ::= or # | // ::= eo # ^ // ::= aS # = // ::= pL # += // ::= mI # -= // ::= mL # *= // ::= dV # /= // ::= rM # %= // ::= aN # &= // ::= oR # |= // ::= eO # ^= // ::= ls # << // ::= rs # >> // ::= lS # <<= // ::= rS # >>= // ::= eq # == // ::= ne # != // ::= lt # < // ::= gt # > // ::= le # <= // ::= ge # >= // ::= ss # <=> // ::= nt # ! // ::= aa # && // ::= oo # || // ::= pp # ++ (postfix in context) // ::= mm # -- (postfix in context) // ::= cm # , // ::= pm # ->* // ::= pt # -> // ::= cl # () // ::= ix # [] // ::= qu # ? // ::= cv # (cast) (TODO) // ::= li # operator "" // ::= v # vendor extended operator (TODO) private BaseNode ParseOperatorName(NameParserContext context) { switch (Peek()) { case 'a': switch (Peek(1)) { case 'a': _position += 2; return new NameType("operator&&"); case 'd': case 'n': _position += 2; return new NameType("operator&"); case 'N': _position += 2; return new NameType("operator&="); case 'S': _position += 2; return new NameType("operator="); default: return null; } case 'c': switch (Peek(1)) { case 'l': _position += 2; return new NameType("operator()"); case 'm': _position += 2; return new NameType("operator,"); case 'o': _position += 2; return new NameType("operator~"); case 'v': _position += 2; bool canParseTemplateArgsBackup = _canParseTemplateArgs; bool canForwardTemplateReferenceBackup = _canForwardTemplateReference; _canParseTemplateArgs = false; _canForwardTemplateReference = canForwardTemplateReferenceBackup || context != null; BaseNode type = ParseType(); _canParseTemplateArgs = canParseTemplateArgsBackup; _canForwardTemplateReference = canForwardTemplateReferenceBackup; if (type == null) { return null; } if (context != null) { context.CtorDtorConversion = true; } return new ConversionOperatorType(type); default: return null; } case 'd': switch (Peek(1)) { case 'a': _position += 2; return new NameType("operator delete[]"); case 'e': _position += 2; return new NameType("operator*"); case 'l': _position += 2; return new NameType("operator delete"); case 'v': _position += 2; return new NameType("operator/"); case 'V': _position += 2; return new NameType("operator/="); default: return null; } case 'e': switch (Peek(1)) { case 'o': _position += 2; return new NameType("operator^"); case 'O': _position += 2; return new NameType("operator^="); case 'q': _position += 2; return new NameType("operator=="); default: return null; } case 'g': switch (Peek(1)) { case 'e': _position += 2; return new NameType("operator>="); case 't': _position += 2; return new NameType("operator>"); default: return null; } case 'i': if (Peek(1) == 'x') { _position += 2; return new NameType("operator[]"); } return null; case 'l': switch (Peek(1)) { case 'e': _position += 2; return new NameType("operator<="); case 'i': _position += 2; BaseNode sourceName = ParseSourceName(); if (sourceName == null) { return null; } return new LiteralOperator(sourceName); case 's': _position += 2; return new NameType("operator<<"); case 'S': _position += 2; return new NameType("operator<<="); case 't': _position += 2; return new NameType("operator<"); default: return null; } case 'm': switch (Peek(1)) { case 'i': _position += 2; return new NameType("operator-"); case 'I': _position += 2; return new NameType("operator-="); case 'l': _position += 2; return new NameType("operator*"); case 'L': _position += 2; return new NameType("operator*="); case 'm': _position += 2; return new NameType("operator--"); default: return null; } case 'n': switch (Peek(1)) { case 'a': _position += 2; return new NameType("operator new[]"); case 'e': _position += 2; return new NameType("operator!="); case 'g': _position += 2; return new NameType("operator-"); case 't': _position += 2; return new NameType("operator!"); case 'w': _position += 2; return new NameType("operator new"); default: return null; } case 'o': switch (Peek(1)) { case 'o': _position += 2; return new NameType("operator||"); case 'r': _position += 2; return new NameType("operator|"); case 'R': _position += 2; return new NameType("operator|="); default: return null; } case 'p': switch (Peek(1)) { case 'm': _position += 2; return new NameType("operator->*"); case 's': case 'l': _position += 2; return new NameType("operator+"); case 'L': _position += 2; return new NameType("operator+="); case 'p': _position += 2; return new NameType("operator++"); case 't': _position += 2; return new NameType("operator->"); default: return null; } case 'q': if (Peek(1) == 'u') { _position += 2; return new NameType("operator?"); } return null; case 'r': switch (Peek(1)) { case 'm': _position += 2; return new NameType("operator%"); case 'M': _position += 2; return new NameType("operator%="); case 's': _position += 2; return new NameType("operator>>"); case 'S': _position += 2; return new NameType("operator>>="); default: return null; } case 's': if (Peek(1) == 's') { _position += 2; return new NameType("operator<=>"); } return null; case 'v': // TODO: ::= v # vendor extended operator return null; default: return null; } } // ::= [ (TODO)] // ::= (TODO) // ::= // ::= (TODO) // ::= DC + E # structured binding declaration (TODO) private BaseNode ParseUnqualifiedName(NameParserContext context) { BaseNode result = null; char c = Peek(); if (c == 'U') { // TODO: Unnamed Type Name // throw new Exception("Unnamed Type Name not implemented"); } else if (char.IsDigit(c)) { result = ParseSourceName(); } else if (ConsumeIf("DC")) { // TODO: Structured Binding Declaration // throw new Exception("Structured Binding Declaration not implemented"); } else { result = ParseOperatorName(context); } if (result != null) { // TODO: ABI Tags // throw new Exception("ABI Tags not implemented"); } return result; } // ::= C1 # complete object constructor // ::= C2 # base object constructor // ::= C3 # complete object allocating constructor // ::= D0 # deleting destructor // ::= D1 # complete object destructor // ::= D2 # base object destructor private BaseNode ParseCtorDtorName(NameParserContext context, BaseNode prev) { if (prev.Type == NodeType.SpecialSubstitution && prev is SpecialSubstitution substitution) { substitution.SetExtended(); } if (ConsumeIf("C")) { bool isInherited = ConsumeIf("I"); char ctorDtorType = Peek(); if (ctorDtorType != '1' && ctorDtorType != '2' && ctorDtorType != '3') { return null; } _position++; if (context != null) { context.CtorDtorConversion = true; } if (isInherited && ParseName(context) == null) { return null; } return new CtorDtorNameType(prev, false); } if (ConsumeIf("D")) { char c = Peek(); if (c != '0' && c != '1' && c != '2') { return null; } _position++; if (context != null) { context.CtorDtorConversion = true; } return new CtorDtorNameType(prev, true); } return null; } // ::= fp _ # L == 0, first parameter // ::= fp _ # L == 0, second and later parameters // ::= fL p _ # L > 0, first parameter // ::= fL p _ # L > 0, second and later parameters private BaseNode ParseFunctionParameter() { if (ConsumeIf("fp")) { // ignored ParseCvQualifiers(); if (!ConsumeIf("_")) { return null; } return new FunctionParameter(ParseNumber()); } else if (ConsumeIf("fL")) { string l1Number = ParseNumber(); if (l1Number == null || l1Number.Length == 0) { return null; } if (!ConsumeIf("p")) { return null; } // ignored ParseCvQualifiers(); if (!ConsumeIf("_")) { return null; } return new FunctionParameter(ParseNumber()); } return null; } // ::= fL // ::= fR // ::= fl // ::= fr private BaseNode ParseFoldExpression() { if (!ConsumeIf("f")) { return null; } char foldKind = Peek(); bool hasInitializer = foldKind == 'L' || foldKind == 'R'; bool isLeftFold = foldKind == 'l' || foldKind == 'L'; if (!isLeftFold && !(foldKind == 'r' || foldKind == 'R')) { return null; } _position++; string operatorName; switch (PeekString(0, 2)) { case "aa": operatorName = "&&"; break; case "an": operatorName = "&"; break; case "aN": operatorName = "&="; break; case "aS": operatorName = "="; break; case "cm": operatorName = ","; break; case "ds": operatorName = ".*"; break; case "dv": operatorName = "/"; break; case "dV": operatorName = "/="; break; case "eo": operatorName = "^"; break; case "eO": operatorName = "^="; break; case "eq": operatorName = "=="; break; case "ge": operatorName = ">="; break; case "gt": operatorName = ">"; break; case "le": operatorName = "<="; break; case "ls": operatorName = "<<"; break; case "lS": operatorName = "<<="; break; case "lt": operatorName = "<"; break; case "mi": operatorName = "-"; break; case "mI": operatorName = "-="; break; case "ml": operatorName = "*"; break; case "mL": operatorName = "*="; break; case "ne": operatorName = "!="; break; case "oo": operatorName = "||"; break; case "or": operatorName = "|"; break; case "oR": operatorName = "|="; break; case "pl": operatorName = "+"; break; case "pL": operatorName = "+="; break; case "rm": operatorName = "%"; break; case "rM": operatorName = "%="; break; case "rs": operatorName = ">>"; break; case "rS": operatorName = ">>="; break; default: return null; } _position += 2; BaseNode expression = ParseExpression(); if (expression == null) { return null; } BaseNode initializer = null; if (hasInitializer) { initializer = ParseExpression(); if (initializer == null) { return null; } } if (isLeftFold && initializer != null) { (initializer, expression) = (expression, initializer); } return new FoldExpression(isLeftFold, operatorName, new PackedTemplateParameterExpansion(expression), initializer); } // ::= cv # type (expression), conversion with one argument // ::= cv _ * E # type (expr-list), conversion with other than one argument private BaseNode ParseConversionExpression() { if (!ConsumeIf("cv")) { return null; } bool canParseTemplateArgsBackup = _canParseTemplateArgs; _canParseTemplateArgs = false; BaseNode type = ParseType(); _canParseTemplateArgs = canParseTemplateArgsBackup; if (type == null) { return null; } List expressions = new(); if (ConsumeIf("_")) { while (!ConsumeIf("E")) { BaseNode expression = ParseExpression(); if (expression == null) { return null; } expressions.Add(expression); } } else { BaseNode expression = ParseExpression(); if (expression == null) { return null; } expressions.Add(expression); } return new ConversionExpression(type, new NodeArray(expressions)); } private BaseNode ParseBinaryExpression(string name) { BaseNode leftPart = ParseExpression(); if (leftPart == null) { return null; } BaseNode rightPart = ParseExpression(); if (rightPart == null) { return null; } return new BinaryExpression(leftPart, name, rightPart); } private BaseNode ParsePrefixExpression(string name) { BaseNode expression = ParseExpression(); if (expression == null) { return null; } return new PrefixExpression(name, expression); } // ::= // ::= di # .name = expr // ::= dx # [expr] = expr // ::= dX // # [expr ... expr] = expr private BaseNode ParseBracedExpression() { if (Peek() == 'd') { BaseNode bracedExpressionNode; switch (Peek(1)) { case 'i': _position += 2; BaseNode field = ParseSourceName(); if (field == null) { return null; } bracedExpressionNode = ParseBracedExpression(); if (bracedExpressionNode == null) { return null; } return new BracedExpression(field, bracedExpressionNode, false); case 'x': _position += 2; BaseNode index = ParseExpression(); if (index == null) { return null; } bracedExpressionNode = ParseBracedExpression(); if (bracedExpressionNode == null) { return null; } return new BracedExpression(index, bracedExpressionNode, true); case 'X': _position += 2; BaseNode rangeBeginExpression = ParseExpression(); if (rangeBeginExpression == null) { return null; } BaseNode rangeEndExpression = ParseExpression(); if (rangeEndExpression == null) { return null; } bracedExpressionNode = ParseBracedExpression(); if (bracedExpressionNode == null) { return null; } return new BracedRangeExpression(rangeBeginExpression, rangeEndExpression, bracedExpressionNode); } } return ParseExpression(); } // ::= [gs] nw * _ E # new (expr-list) type // ::= [gs] nw * _ # new (expr-list) type (init) // ::= [gs] na * _ E # new[] (expr-list) type // ::= [gs] na * _ # new[] (expr-list) type (init) // // ::= pi * E # parenthesized initialization private BaseNode ParseNewExpression() { bool isGlobal = ConsumeIf("gs"); bool isArray = Peek(1) == 'a'; if (!ConsumeIf("nw") || !ConsumeIf("na")) { return null; } List expressions = new(); List initializers = new(); while (!ConsumeIf("_")) { BaseNode expression = ParseExpression(); if (expression == null) { return null; } expressions.Add(expression); } BaseNode typeNode = ParseType(); if (typeNode == null) { return null; } if (ConsumeIf("pi")) { while (!ConsumeIf("E")) { BaseNode initializer = ParseExpression(); if (initializer == null) { return null; } initializers.Add(initializer); } } else if (!ConsumeIf("E")) { return null; } return new NewExpression(new NodeArray(expressions), typeNode, new NodeArray(initializers), isGlobal, isArray); } // ::= // ::= // ::= // ::= pp_ # prefix ++ // ::= mm_ # prefix -- // ::= cl + E # expression (expr-list), call // ::= cv # type (expression), conversion with one argument // ::= cv _ * E # type (expr-list), conversion with other than one argument // ::= tl * E # type {expr-list}, conversion with braced-init-list argument // ::= il * E # {expr-list}, braced-init-list in any other context // ::= [gs] nw * _ E # new (expr-list) type // ::= [gs] nw * _ # new (expr-list) type (init) // ::= [gs] na * _ E # new[] (expr-list) type // ::= [gs] na * _ # new[] (expr-list) type (init) // ::= [gs] dl # delete expression // ::= [gs] da # delete[] expression // ::= dc # dynamic_cast (expression) // ::= sc # static_cast (expression) // ::= cc # const_cast (expression) // ::= rc # reinterpret_cast (expression) // ::= ti # typeid (type) // ::= te # typeid (expression) // ::= st # sizeof (type) // ::= sz # sizeof (expression) // ::= at # alignof (type) // ::= az # alignof (expression) // ::= nx # noexcept (expression) // ::= // ::= // ::= dt # expr.name // ::= pt # expr->name // ::= ds # expr.*expr // ::= sZ # sizeof...(T), size of a template parameter pack // ::= sZ # sizeof...(parameter), size of a function parameter pack // ::= sP * E # sizeof...(T), size of a captured template parameter pack from an alias template // ::= sp # expression..., pack expansion // ::= tw # throw expression // ::= tr # throw with no operand (rethrow) // ::= # f(p), N::f(p), ::f(p), // # freestanding dependent name (e.g., T::x), // # objectless nonstatic member reference // ::= private BaseNode ParseExpression() { bool isGlobal = ConsumeIf("gs"); BaseNode expression; if (Count() < 2) { return null; } switch (Peek()) { case 'L': return ParseExpressionPrimary(); case 'T': return ParseTemplateParam(); case 'f': char c = Peek(1); if (c == 'p' || (c == 'L' && char.IsDigit(Peek(2)))) { return ParseFunctionParameter(); } return ParseFoldExpression(); case 'a': switch (Peek(1)) { case 'a': _position += 2; return ParseBinaryExpression("&&"); case 'd': case 'n': _position += 2; return ParseBinaryExpression("&"); case 'N': _position += 2; return ParseBinaryExpression("&="); case 'S': _position += 2; return ParseBinaryExpression("="); case 't': _position += 2; BaseNode type = ParseType(); if (type == null) { return null; } return new EnclosedExpression("alignof (", type, ")"); case 'z': _position += 2; expression = ParseExpression(); if (expression == null) { return null; } return new EnclosedExpression("alignof (", expression, ")"); } return null; case 'c': switch (Peek(1)) { case 'c': _position += 2; BaseNode to = ParseType(); if (to == null) { return null; } BaseNode from = ParseExpression(); if (from == null) { return null; } return new CastExpression("const_cast", to, from); case 'l': _position += 2; BaseNode callee = ParseExpression(); if (callee == null) { return null; } List names = new(); while (!ConsumeIf("E")) { expression = ParseExpression(); if (expression == null) { return null; } names.Add(expression); } return new CallExpression(callee, names); case 'm': _position += 2; return ParseBinaryExpression(","); case 'o': _position += 2; return ParsePrefixExpression("~"); case 'v': return ParseConversionExpression(); } return null; case 'd': BaseNode leftNode; BaseNode rightNode; switch (Peek(1)) { case 'a': _position += 2; expression = ParseExpression(); if (expression == null) { return expression; } return new DeleteExpression(expression, isGlobal, true); case 'c': _position += 2; BaseNode type = ParseType(); if (type == null) { return null; } expression = ParseExpression(); if (expression == null) { return expression; } return new CastExpression("dynamic_cast", type, expression); case 'e': _position += 2; return ParsePrefixExpression("*"); case 'l': _position += 2; expression = ParseExpression(); if (expression == null) { return null; } return new DeleteExpression(expression, isGlobal, false); case 'n': return ParseUnresolvedName(); case 's': _position += 2; leftNode = ParseExpression(); if (leftNode == null) { return null; } rightNode = ParseExpression(); if (rightNode == null) { return null; } return new MemberExpression(leftNode, ".*", rightNode); case 't': _position += 2; leftNode = ParseExpression(); if (leftNode == null) { return null; } rightNode = ParseExpression(); if (rightNode == null) { return null; } return new MemberExpression(leftNode, ".", rightNode); case 'v': _position += 2; return ParseBinaryExpression("/"); case 'V': _position += 2; return ParseBinaryExpression("/="); } return null; case 'e': switch (Peek(1)) { case 'o': _position += 2; return ParseBinaryExpression("^"); case 'O': _position += 2; return ParseBinaryExpression("^="); case 'q': _position += 2; return ParseBinaryExpression("=="); } return null; case 'g': switch (Peek(1)) { case 'e': _position += 2; return ParseBinaryExpression(">="); case 't': _position += 2; return ParseBinaryExpression(">"); } return null; case 'i': switch (Peek(1)) { case 'x': _position += 2; BaseNode Base = ParseExpression(); if (Base == null) { return null; } BaseNode subscript = ParseExpression(); if (Base == null) { return null; } return new ArraySubscriptingExpression(Base, subscript); case 'l': _position += 2; List bracedExpressions = new(); while (!ConsumeIf("E")) { expression = ParseBracedExpression(); if (expression == null) { return null; } bracedExpressions.Add(expression); } return new InitListExpression(null, bracedExpressions); } return null; case 'l': switch (Peek(1)) { case 'e': _position += 2; return ParseBinaryExpression("<="); case 's': _position += 2; return ParseBinaryExpression("<<"); case 'S': _position += 2; return ParseBinaryExpression("<<="); case 't': _position += 2; return ParseBinaryExpression("<"); } return null; case 'm': switch (Peek(1)) { case 'i': _position += 2; return ParseBinaryExpression("-"); case 'I': _position += 2; return ParseBinaryExpression("-="); case 'l': _position += 2; return ParseBinaryExpression("*"); case 'L': _position += 2; return ParseBinaryExpression("*="); case 'm': _position += 2; if (ConsumeIf("_")) { return ParsePrefixExpression("--"); } expression = ParseExpression(); if (expression == null) { return null; } return new PostfixExpression(expression, "--"); } return null; case 'n': switch (Peek(1)) { case 'a': case 'w': _position += 2; return ParseNewExpression(); case 'e': _position += 2; return ParseBinaryExpression("!="); case 'g': _position += 2; return ParsePrefixExpression("-"); case 't': _position += 2; return ParsePrefixExpression("!"); case 'x': _position += 2; expression = ParseExpression(); if (expression == null) { return null; } return new EnclosedExpression("noexcept (", expression, ")"); } return null; case 'o': switch (Peek(1)) { case 'n': return ParseUnresolvedName(); case 'o': _position += 2; return ParseBinaryExpression("||"); case 'r': _position += 2; return ParseBinaryExpression("|"); case 'R': _position += 2; return ParseBinaryExpression("|="); } return null; case 'p': switch (Peek(1)) { case 'm': _position += 2; return ParseBinaryExpression("->*"); case 'l': case 's': _position += 2; return ParseBinaryExpression("+"); case 'L': _position += 2; return ParseBinaryExpression("+="); case 'p': _position += 2; if (ConsumeIf("_")) { return ParsePrefixExpression("++"); } expression = ParseExpression(); if (expression == null) { return null; } return new PostfixExpression(expression, "++"); case 't': _position += 2; leftNode = ParseExpression(); if (leftNode == null) { return null; } rightNode = ParseExpression(); if (rightNode == null) { return null; } return new MemberExpression(leftNode, "->", rightNode); } return null; case 'q': if (Peek(1) == 'u') { _position += 2; BaseNode condition = ParseExpression(); if (condition == null) { return null; } leftNode = ParseExpression(); if (leftNode == null) { return null; } rightNode = ParseExpression(); if (rightNode == null) { return null; } return new ConditionalExpression(condition, leftNode, rightNode); } return null; case 'r': switch (Peek(1)) { case 'c': _position += 2; BaseNode to = ParseType(); if (to == null) { return null; } BaseNode from = ParseExpression(); if (from == null) { return null; } return new CastExpression("reinterpret_cast", to, from); case 'm': _position += 2; return ParseBinaryExpression("%"); case 'M': _position += 2; return ParseBinaryExpression("%"); case 's': _position += 2; return ParseBinaryExpression(">>"); case 'S': _position += 2; return ParseBinaryExpression(">>="); } return null; case 's': switch (Peek(1)) { case 'c': _position += 2; BaseNode to = ParseType(); if (to == null) { return null; } BaseNode from = ParseExpression(); if (from == null) { return null; } return new CastExpression("static_cast", to, from); case 'p': _position += 2; expression = ParseExpression(); if (expression == null) { return null; } return new PackedTemplateParameterExpansion(expression); case 'r': return ParseUnresolvedName(); case 't': _position += 2; BaseNode enclosedType = ParseType(); if (enclosedType == null) { return null; } return new EnclosedExpression("sizeof (", enclosedType, ")"); case 'z': _position += 2; expression = ParseExpression(); if (expression == null) { return null; } return new EnclosedExpression("sizeof (", expression, ")"); case 'Z': _position += 2; BaseNode sizeofParamNode; switch (Peek()) { case 'T': // FIXME: ??? Not entire sure if it's right sizeofParamNode = ParseFunctionParameter(); if (sizeofParamNode == null) { return null; } return new EnclosedExpression("sizeof...(", new PackedTemplateParameterExpansion(sizeofParamNode), ")"); case 'f': sizeofParamNode = ParseFunctionParameter(); if (sizeofParamNode == null) { return null; } return new EnclosedExpression("sizeof...(", sizeofParamNode, ")"); } return null; case 'P': _position += 2; List arguments = new(); while (!ConsumeIf("E")) { BaseNode argument = ParseTemplateArgument(); if (argument == null) { return null; } arguments.Add(argument); } return new EnclosedExpression("sizeof...(", new NodeArray(arguments), ")"); } return null; case 't': switch (Peek(1)) { case 'e': expression = ParseExpression(); if (expression == null) { return null; } return new EnclosedExpression("typeid (", expression, ")"); case 't': BaseNode enclosedType = ParseExpression(); if (enclosedType == null) { return null; } return new EnclosedExpression("typeid (", enclosedType, ")"); case 'l': _position += 2; BaseNode typeNode = ParseType(); if (typeNode == null) { return null; } List bracedExpressions = new(); while (!ConsumeIf("E")) { expression = ParseBracedExpression(); if (expression == null) { return null; } bracedExpressions.Add(expression); } return new InitListExpression(typeNode, bracedExpressions); case 'r': _position += 2; return new NameType("throw"); case 'w': _position += 2; expression = ParseExpression(); if (expression == null) { return null; } return new ThrowExpression(expression); } return null; } if (char.IsDigit(Peek())) { return ParseUnresolvedName(); } return null; } private BaseNode ParseIntegerLiteral(string literalName) { string number = ParseNumber(true); if (number == null || number.Length == 0 || !ConsumeIf("E")) { return null; } return new IntegerLiteral(literalName, number); } // ::= L E # integer literal // ::= L E # floating literal (TODO) // ::= L E # string literal // ::= L E # nullptr literal (i.e., "LDnE") // ::= L 0 E # null pointer template argument // ::= L _ E # complex floating point literal (C 2000) // ::= L _Z E # external name private BaseNode ParseExpressionPrimary() { if (!ConsumeIf("L")) { return null; } switch (Peek()) { case 'w': _position++; return ParseIntegerLiteral("wchar_t"); case 'b': if (ConsumeIf("b0E")) { return new NameType("false", NodeType.BooleanExpression); } if (ConsumeIf("b1E")) { return new NameType("true", NodeType.BooleanExpression); } return null; case 'c': _position++; return ParseIntegerLiteral("char"); case 'a': _position++; return ParseIntegerLiteral("signed char"); case 'h': _position++; return ParseIntegerLiteral("unsigned char"); case 's': _position++; return ParseIntegerLiteral("short"); case 't': _position++; return ParseIntegerLiteral("unsigned short"); case 'i': _position++; return ParseIntegerLiteral(""); case 'j': _position++; return ParseIntegerLiteral("u"); case 'l': _position++; return ParseIntegerLiteral("l"); case 'm': _position++; return ParseIntegerLiteral("ul"); case 'x': _position++; return ParseIntegerLiteral("ll"); case 'y': _position++; return ParseIntegerLiteral("ull"); case 'n': _position++; return ParseIntegerLiteral("__int128"); case 'o': _position++; return ParseIntegerLiteral("unsigned __int128"); case 'd': case 'e': case 'f': // TODO: floating literal return null; case '_': if (ConsumeIf("_Z")) { BaseNode encoding = ParseEncoding(); if (encoding != null && ConsumeIf("E")) { return encoding; } } return null; case 'T': return null; default: BaseNode type = ParseType(); if (type == null) { return null; } string number = ParseNumber(); if (number == null || number.Length == 0 || !ConsumeIf("E")) { return null; } return new IntegerCastExpression(type, number); } } // ::= Dt E # decltype of an id-expression or class member access (C++0x) // ::= DT E # decltype of an expression (C++0x) private BaseNode ParseDecltype() { if (!ConsumeIf("D") || (!ConsumeIf("t") && !ConsumeIf("T"))) { return null; } BaseNode expression = ParseExpression(); if (expression == null) { return null; } if (!ConsumeIf("E")) { return null; } return new EnclosedExpression("decltype(", expression, ")"); } // ::= T_ # first template parameter // ::= T _ // ::= // ::= private BaseNode ParseTemplateParam() { if (!ConsumeIf("T")) { return null; } int index = 0; if (!ConsumeIf("_")) { index = ParsePositiveNumber(); if (index < 0) { return null; } index++; if (!ConsumeIf("_")) { return null; } } // 5.1.8: TODO: lambda? // if (IsParsingLambdaParameters) // return new NameType("auto"); if (_canForwardTemplateReference) { ForwardTemplateReference forwardTemplateReference = new(index); _forwardTemplateReferenceList.Add(forwardTemplateReference); return forwardTemplateReference; } if (index >= _templateParamList.Count) { return null; } return _templateParamList[index]; } // ::= I + E private BaseNode ParseTemplateArguments(bool hasContext = false) { if (!ConsumeIf("I")) { return null; } if (hasContext) { _templateParamList.Clear(); } List args = new(); while (!ConsumeIf("E")) { if (hasContext) { List templateParamListTemp = new(_templateParamList); BaseNode templateArgument = ParseTemplateArgument(); _templateParamList = templateParamListTemp; if (templateArgument == null) { return null; } args.Add(templateArgument); if (templateArgument.GetType().Equals(NodeType.PackedTemplateArgument)) { templateArgument = new PackedTemplateParameter(((NodeArray)templateArgument).Nodes); } _templateParamList.Add(templateArgument); } else { BaseNode templateArgument = ParseTemplateArgument(); if (templateArgument == null) { return null; } args.Add(templateArgument); } } return new TemplateArguments(args); } // ::= # type or template // ::= X E # expression // ::= # simple expressions // ::= J * E # argument pack private BaseNode ParseTemplateArgument() { switch (Peek()) { // X E case 'X': _position++; BaseNode expression = ParseExpression(); if (expression == null || !ConsumeIf("E")) { return null; } return expression; // case 'L': return ParseExpressionPrimary(); // J * E case 'J': _position++; List templateArguments = new(); while (!ConsumeIf("E")) { BaseNode templateArgument = ParseTemplateArgument(); if (templateArgument == null) { return null; } templateArguments.Add(templateArgument); } return new NodeArray(templateArguments, NodeType.PackedTemplateArgument); // default: return ParseType(); } } class NameParserContext { public CvType Cv; public SimpleReferenceType Ref; public bool FinishWithTemplateArguments; public bool CtorDtorConversion; } // ::= [ ] # T:: or T:: // ::= # decltype(p):: // ::= private BaseNode ParseUnresolvedType() { if (Peek() == 'T') { BaseNode templateParam = ParseTemplateParam(); if (templateParam == null) { return null; } _substitutionList.Add(templateParam); return templateParam; } else if (Peek() == 'D') { BaseNode declType = ParseDecltype(); if (declType == null) { return null; } _substitutionList.Add(declType); return declType; } return ParseSubstitution(); } // ::= [ ] private BaseNode ParseSimpleId() { BaseNode sourceName = ParseSourceName(); if (sourceName == null) { return null; } if (Peek() == 'I') { BaseNode templateArguments = ParseTemplateArguments(); if (templateArguments == null) { return null; } return new NameTypeWithTemplateArguments(sourceName, templateArguments); } return sourceName; } // ::= # e.g., ~T or ~decltype(f()) // ::= # e.g., ~A<2*N> private BaseNode ParseDestructorName() { BaseNode node; if (char.IsDigit(Peek())) { node = ParseSimpleId(); } else { node = ParseUnresolvedType(); } if (node == null) { return null; } return new DtorName(node); } // ::= # unresolved name // extension ::= # unresolved operator-function-id // extension ::= # unresolved operator template-id // ::= on # unresolved operator-function-id // ::= on # unresolved operator template-id // ::= dn # destructor or pseudo-destructor; // # e.g. ~X or ~X private BaseNode ParseBaseUnresolvedName() { if (char.IsDigit(Peek())) { return ParseSimpleId(); } else if (ConsumeIf("dn")) { return ParseDestructorName(); } ConsumeIf("on"); BaseNode operatorName = ParseOperatorName(null); if (operatorName == null) { return null; } if (Peek() == 'I') { BaseNode templateArguments = ParseTemplateArguments(); if (templateArguments == null) { return null; } return new NameTypeWithTemplateArguments(operatorName, templateArguments); } return operatorName; } // ::= [gs] # x or (with "gs") ::x // ::= sr # T::x / decltype(p)::x // ::= srN + E // # T::N::x /decltype(p)::N::x // ::= [gs] sr + E // # A::x, N::y, A::z; "gs" means leading "::" private BaseNode ParseUnresolvedName(NameParserContext context = null) { BaseNode result = null; if (ConsumeIf("srN")) { result = ParseUnresolvedType(); if (result == null) { return null; } if (Peek() == 'I') { BaseNode templateArguments = ParseTemplateArguments(); if (templateArguments == null) { return null; } result = new NameTypeWithTemplateArguments(result, templateArguments); if (result == null) { return null; } } while (!ConsumeIf("E")) { BaseNode simpleId = ParseSimpleId(); if (simpleId == null) { return null; } result = new QualifiedName(result, simpleId); if (result == null) { return null; } } BaseNode baseName = ParseBaseUnresolvedName(); if (baseName == null) { return null; } return new QualifiedName(result, baseName); } bool isGlobal = ConsumeIf("gs"); // ::= [gs] # x or (with "gs") ::x if (!ConsumeIf("sr")) { result = ParseBaseUnresolvedName(); if (result == null) { return null; } if (isGlobal) { result = new GlobalQualifiedName(result); } return result; } // ::= [gs] sr + E if (char.IsDigit(Peek())) { do { BaseNode qualifier = ParseSimpleId(); if (qualifier == null) { return null; } if (result != null) { result = new QualifiedName(result, qualifier); } else if (isGlobal) { result = new GlobalQualifiedName(qualifier); } else { result = qualifier; } if (result == null) { return null; } } while (!ConsumeIf("E")); } // ::= sr [template-args] # T::x / decltype(p)::x else { result = ParseUnresolvedType(); if (result == null) { return null; } if (Peek() == 'I') { BaseNode templateArguments = ParseTemplateArguments(); if (templateArguments == null) { return null; } result = new NameTypeWithTemplateArguments(result, templateArguments); if (result == null) { return null; } } } if (result == null) { return null; } BaseNode baseUnresolvedName = ParseBaseUnresolvedName(); if (baseUnresolvedName == null) { return null; } return new QualifiedName(result, baseUnresolvedName); } // ::= // ::= St # ::std:: private BaseNode ParseUnscopedName(NameParserContext context) { if (ConsumeIf("St")) { BaseNode unresolvedName = ParseUnresolvedName(context); if (unresolvedName == null) { return null; } return new StdQualifiedName(unresolvedName); } return ParseUnresolvedName(context); } // ::= N [] [] E // ::= N [] [] E private BaseNode ParseNestedName(NameParserContext context) { // Impossible in theory if (Consume() != 'N') { return null; } BaseNode result = null; CvType cv = new(ParseCvQualifiers(), null); if (context != null) { context.Cv = cv; } SimpleReferenceType Ref = ParseRefQualifiers(); if (context != null) { context.Ref = Ref; } if (ConsumeIf("St")) { result = new NameType("std"); } while (!ConsumeIf("E")) { // end if (ConsumeIf("M")) { if (result == null) { return null; } continue; } char c = Peek(); // TODO: template args if (c == 'T') { BaseNode templateParam = ParseTemplateParam(); if (templateParam == null) { return null; } result = CreateNameNode(result, templateParam, context); _substitutionList.Add(result); continue; } // if (c == 'I') { BaseNode templateArgument = ParseTemplateArguments(context != null); if (templateArgument == null || result == null) { return null; } result = new NameTypeWithTemplateArguments(result, templateArgument); if (context != null) { context.FinishWithTemplateArguments = true; } _substitutionList.Add(result); continue; } // if (c == 'D' && (Peek(1) == 't' || Peek(1) == 'T')) { BaseNode decltype = ParseDecltype(); if (decltype == null) { return null; } result = CreateNameNode(result, decltype, context); _substitutionList.Add(result); continue; } // if (c == 'S' && Peek(1) != 't') { BaseNode substitution = ParseSubstitution(); if (substitution == null) { return null; } result = CreateNameNode(result, substitution, context); if (result != substitution) { _substitutionList.Add(substitution); } continue; } // of ParseUnqualifiedName if (c == 'C' || (c == 'D' && Peek(1) != 'C')) { // We cannot have nothing before this if (result == null) { return null; } BaseNode ctOrDtorName = ParseCtorDtorName(context, result); if (ctOrDtorName == null) { return null; } result = CreateNameNode(result, ctOrDtorName, context); // TODO: ABI Tags (before) if (result == null) { return null; } _substitutionList.Add(result); continue; } BaseNode unqualifiedName = ParseUnqualifiedName(context); if (unqualifiedName == null) { return null; } result = CreateNameNode(result, unqualifiedName, context); _substitutionList.Add(result); } if (result == null || _substitutionList.Count == 0) { return null; } _substitutionList.RemoveAt(_substitutionList.Count - 1); return result; } // ::= _ # when number < 10 // ::= __ _ # when number >= 10 private void ParseDiscriminator() { if (Count() == 0) { return; } // We ignore the discriminator, we don't need it. if (ConsumeIf("_")) { ConsumeIf("_"); while (char.IsDigit(Peek()) && Count() != 0) { Consume(); } ConsumeIf("_"); } } // ::= Z E [] // ::= Z E s [] // ::= Z Ed [ ] _ private BaseNode ParseLocalName(NameParserContext context) { if (!ConsumeIf("Z")) { return null; } BaseNode encoding = ParseEncoding(); if (encoding == null || !ConsumeIf("E")) { return null; } BaseNode entityName; if (ConsumeIf("s")) { ParseDiscriminator(); return new LocalName(encoding, new NameType("string literal")); } else if (ConsumeIf("d")) { ParseNumber(true); if (!ConsumeIf("_")) { return null; } entityName = ParseName(context); if (entityName == null) { return null; } return new LocalName(encoding, entityName); } entityName = ParseName(context); if (entityName == null) { return null; } ParseDiscriminator(); return new LocalName(encoding, entityName); } // ::= // ::= // ::= // ::= # See Scope Encoding below (TODO) private BaseNode ParseName(NameParserContext context = null) { ConsumeIf("L"); if (Peek() == 'N') { return ParseNestedName(context); } if (Peek() == 'Z') { return ParseLocalName(context); } if (Peek() == 'S' && Peek(1) != 't') { BaseNode substitution = ParseSubstitution(); if (substitution == null) { return null; } if (Peek() != 'I') { return null; } BaseNode templateArguments = ParseTemplateArguments(context != null); if (templateArguments == null) { return null; } if (context != null) { context.FinishWithTemplateArguments = true; } return new NameTypeWithTemplateArguments(substitution, templateArguments); } BaseNode result = ParseUnscopedName(context); if (result == null) { return null; } if (Peek() == 'I') { _substitutionList.Add(result); BaseNode templateArguments = ParseTemplateArguments(context != null); if (templateArguments == null) { return null; } if (context != null) { context.FinishWithTemplateArguments = true; } return new NameTypeWithTemplateArguments(result, templateArguments); } return result; } private bool IsEncodingEnd() { char c = Peek(); return Count() == 0 || c == 'E' || c == '.' || c == '_'; } // ::= // ::= // ::= private BaseNode ParseEncoding() { NameParserContext context = new(); if (Peek() == 'T' || (Peek() == 'G' && Peek(1) == 'V')) { return ParseSpecialName(context); } BaseNode name = ParseName(context); if (name == null) { return null; } // TODO: compute template refs here if (IsEncodingEnd()) { return name; } // TODO: Ua9enable_ifI BaseNode returnType = null; if (!context.CtorDtorConversion && context.FinishWithTemplateArguments) { returnType = ParseType(); if (returnType == null) { return null; } } if (ConsumeIf("v")) { return new EncodedFunction(name, null, context.Cv, context.Ref, null, returnType); } List paramsList = new(); // backup because that can be destroyed by parseType CvType cv = context.Cv; SimpleReferenceType Ref = context.Ref; while (!IsEncodingEnd()) { BaseNode param = ParseType(); if (param == null) { return null; } paramsList.Add(param); } return new EncodedFunction(name, new NodeArray(paramsList), cv, Ref, null, returnType); } // ::= _Z // ::= private BaseNode Parse() { if (ConsumeIf("_Z")) { BaseNode encoding = ParseEncoding(); if (encoding != null && Count() == 0) { return encoding; } return null; } else { BaseNode type = ParseType(); if (type != null && Count() == 0) { return type; } return null; } } public static string Parse(string originalMangled) { Demangler instance = new(originalMangled); BaseNode resNode = instance.Parse(); if (resNode != null) { StringWriter writer = new(); resNode.Print(writer); return writer.ToString(); } return originalMangled; } } }