ryujinx-mirror/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs

3368 lines
118 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast;
namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
{
class Demangler
{
private static readonly string BASE_36 = "0123456789abcdefghijklmnopqrstuvwxyz";
private List<BaseNode> SubstitutionList = new List<BaseNode>();
private List<BaseNode> TemplateParamList = new List<BaseNode>();
private List<ForwardTemplateReference> ForwardTemplateReferenceList = new List<ForwardTemplateReference>();
public string Mangled { get; private set; }
private int Position;
private int Length;
private bool CanForwardTemplateReference;
private bool CanParseTemplateArgs;
public Demangler(string Mangled)
{
this.Mangled = Mangled;
Position = 0;
Length = Mangled.Length;
CanParseTemplateArgs = true;
}
private bool ConsumeIf(string ToConsume)
{
string MangledPart = Mangled.Substring(Position);
if (MangledPart.StartsWith(ToConsume))
{
Position += ToConsume.Length;
return true;
}
return false;
}
private string PeekString(int Offset = 0, int Length = 1)
{
if (Position + Offset >= Length)
{
return null;
}
return Mangled.Substring(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 = BASE_36.IndexOf(ReversedEncoded[i]);
if (Value == -1)
{
return -1;
}
Result += Value * (int)Math.Pow(36, i);
}
return Result;
}
private int ParseSeqId()
{
string Part = Mangled.Substring(Position);
int SeqIdLen = 0;
for (; SeqIdLen < Part.Length; SeqIdLen++)
{
if (!char.IsLetterOrDigit(Part[SeqIdLen]))
{
break;
}
}
Position += SeqIdLen;
return FromBase36(Part.Substring(0, SeqIdLen));
}
// <substitution> ::= S <seq-id> _
// ::= S_
// ::= St # std::
// ::= Sa # std::allocator
// ::= Sb # std::basic_string
// ::= Ss # std::basic_string<char, std::char_traits<char>, std::allocator<char> >
// ::= Si # std::basic_istream<char, std::char_traits<char> >
// ::= So # std::basic_ostream<char, std::char_traits<char> >
// ::= Sd # std::basic_iostream<char, std::char_traits<char> >
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 <seq-id> _
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
// <call-offset> ::= h <nv-offset> _
// ::= v <v-offset> _
// <nv-offset> ::= <offset number>
// # non-virtual base override
// <v-offset> ::= <offset number> _ <virtual offset number>
// # 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;
}
// <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier
// ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class'
// ::= Tu <name> # dependent elaborated type specifier using 'union'
// ::= Te <name> # 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);
}
// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
// <bare-function-type> ::= <signature type>+
// # types are possible return type, then parameter types
// <exception-spec> ::= Do # non-throwing exception-specification (e.g., noexcept, throw())
// ::= DO <expression> E # computed (instantiation-dependent) noexcept
// ::= Dw <type>+ 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<BaseNode> Types = new List<BaseNode>();
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<BaseNode> Params = new List<BaseNode>();
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;
}
Params.Add(Type);
}
return new FunctionType(ReturnType, new NodeArray(Params), new CVType(CVQualifiers, null), new SimpleReferenceType(ReferenceQualifier, null), ExceptionSpec);
}
// <array-type> ::= A <positive dimension number> _ <element type>
// ::= A [<dimension expression>] _ <element type>
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);
}
// <type> ::= <builtin-type>
// ::= <qualified-type> (PARTIAL)
// ::= <function-type>
// ::= <class-enum-type>
// ::= <array-type> (TODO)
// ::= <pointer-to-member-type> (TODO)
// ::= <template-param>
// ::= <template-template-param> <template-args>
// ::= <decltype>
// ::= P <type> # pointer
// ::= R <type> # l-value reference
// ::= O <type> # r-value reference (C++11)
// ::= C <type> # complex pair (C99)
// ::= G <type> # imaginary (C99)
// ::= <substitution> # See Compression below
private BaseNode ParseType(NameParserContext Context = null)
{
// Temporary context
if (Context == null)
{
Context = new NameParserContext();
}
BaseNode Result = null;
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: <extended-qualifier>
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: <pointer-to-member-type>
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;
}
// <special-name> ::= TV <type> # virtual table
// ::= TT <type> # VTT structure (construction vtable index)
// ::= TI <type> # typeinfo structure
// ::= TS <type> # typeinfo name (null-terminated byte string)
// ::= Tc <call-offset> <call-offset> <base encoding>
// ::= TW <object name> # Thread-local wrapper
// ::= TH <object name> # Thread-local initialization
// ::= T <call-offset> <base encoding>
// # base is the nominal target function of thunk
// ::= GV <object name> # 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 <type> # virtual table
case 'V':
Position += 2;
Node = ParseType(Context);
if (Node == null)
{
return null;
}
return new SpecialName("vtable for ", Node);
// ::= TT <type> # VTT structure (construction vtable index)
case 'T':
Position += 2;
Node = ParseType(Context);
if (Node == null)
{
return null;
}
return new SpecialName("VTT for ", Node);
// ::= TI <type> # typeinfo structure
case 'I':
Position += 2;
Node = ParseType(Context);
if (Node == null)
{
return null;
}
return new SpecialName("typeinfo for ", Node);
// ::= TS <type> # 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 <call-offset> <call-offset> <base encoding>
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 <first type> <number> _ <second type>
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 <object name> # Thread-local initialization
case 'H':
Position += 2;
Node = ParseName();
if (Node == null)
{
return null;
}
return new SpecialName("thread-local initialization routine for ", Node);
// ::= TW <object name> # 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);
}
}
// <CV-qualifiers> ::= [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;
}
// <ref-qualifier> ::= R # & ref-qualifier
// <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 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()
{
string Part = Mangled.Substring(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.Substring(0, NumberLength));
}
private string ParseNumber(bool IsSigned = false)
{
if (IsSigned)
{
ConsumeIf("n");
}
if (Count() == 0 || !char.IsDigit(Mangled[Position]))
{
return null;
}
string Part = Mangled.Substring(Position);
int NumberLength = 0;
for (; NumberLength < Part.Length; NumberLength++)
{
if (!char.IsDigit(Part[NumberLength]))
{
break;
}
}
Position += NumberLength;
return Part.Substring(0, NumberLength);
}
// <source-name> ::= <positive length number> <identifier>
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);
}
// <operator-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 <expression> context)
// ::= mm # -- (postfix in <expression> context)
// ::= cm # ,
// ::= pm # ->*
// ::= pt # ->
// ::= cl # ()
// ::= ix # []
// ::= qu # ?
// ::= cv <type> # (cast) (TODO)
// ::= li <source-name> # operator ""
// ::= v <digit> <source-name> # 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 <digit> <source-name> # vendor extended operator
return null;
default:
return null;
}
}
// <unqualified-name> ::= <operator-name> [<abi-tags> (TODO)]
// ::= <ctor-dtor-name> (TODO)
// ::= <source-name>
// ::= <unnamed-type-name> (TODO)
// ::= DC <source-name>+ 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;
}
// <ctor-dtor-name> ::= 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)
{
((SpecialSubstitution)Prev).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;
}
// <function-param> ::= fp <top-level CV-qualifiers> _ # L == 0, first parameter
// ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters
// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _ # L > 0, first parameter
// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> <parameter-2 non-negative number> _ # 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;
}
// <fold-expr> ::= fL <binary-operator-name> <expression> <expression>
// ::= fR <binary-operator-name> <expression> <expression>
// ::= fl <binary-operator-name> <expression>
// ::= fr <binary-operator-name> <expression>
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 = null;
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)
{
BaseNode Temp = Expression;
Expression = Initializer;
Initializer = Temp;
}
return new FoldExpression(IsLeftFold, OperatorName, new PackedTemplateParameterExpansion(Expression), Initializer);
}
// ::= cv <type> <expression> # type (expression), conversion with one argument
// ::= cv <type> _ <expression>* 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<BaseNode> Expressions = new List<BaseNode>();
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);
}
// <braced-expression> ::= <expression>
// ::= di <field source-name> <braced-expression> # .name = expr
// ::= dx <index expression> <braced-expression> # [expr] = expr
// ::= dX <range begin expression> <range end expression> <braced-expression>
// # [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 <expression>* _ <type> E # new (expr-list) type
// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type
// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
//
// <initializer> ::= pi <expression>* E # parenthesized initialization
private BaseNode ParseNewExpression()
{
bool IsGlobal = ConsumeIf("gs");
bool IsArray = Peek(1) == 'a';
if (!ConsumeIf("nw") || !ConsumeIf("na"))
{
return null;
}
List<BaseNode> Expressions = new List<BaseNode>();
List<BaseNode> Initializers = new List<BaseNode>();
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);
}
// <expression> ::= <unary operator-name> <expression>
// ::= <binary operator-name> <expression> <expression>
// ::= <ternary operator-name> <expression> <expression> <expression>
// ::= pp_ <expression> # prefix ++
// ::= mm_ <expression> # prefix --
// ::= cl <expression>+ E # expression (expr-list), call
// ::= cv <type> <expression> # type (expression), conversion with one argument
// ::= cv <type> _ <expression>* E # type (expr-list), conversion with other than one argument
// ::= tl <type> <braced-expression>* E # type {expr-list}, conversion with braced-init-list argument
// ::= il <braced-expression>* E # {expr-list}, braced-init-list in any other context
// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type
// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type
// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
// ::= [gs] dl <expression> # delete expression
// ::= [gs] da <expression> # delete[] expression
// ::= dc <type> <expression> # dynamic_cast<type> (expression)
// ::= sc <type> <expression> # static_cast<type> (expression)
// ::= cc <type> <expression> # const_cast<type> (expression)
// ::= rc <type> <expression> # reinterpret_cast<type> (expression)
// ::= ti <type> # typeid (type)
// ::= te <expression> # typeid (expression)
// ::= st <type> # sizeof (type)
// ::= sz <expression> # sizeof (expression)
// ::= at <type> # alignof (type)
// ::= az <expression> # alignof (expression)
// ::= nx <expression> # noexcept (expression)
// ::= <template-param>
// ::= <function-param>
// ::= dt <expression> <unresolved-name> # expr.name
// ::= pt <expression> <unresolved-name> # expr->name
// ::= ds <expression> <expression> # expr.*expr
// ::= sZ <template-param> # sizeof...(T), size of a template parameter pack
// ::= sZ <function-param> # sizeof...(parameter), size of a function parameter pack
// ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template
// ::= sp <expression> # expression..., pack expansion
// ::= tw <expression> # throw expression
// ::= tr # throw with no operand (rethrow)
// ::= <unresolved-name> # f(p), N::f(p), ::f(p),
// # freestanding dependent name (e.g., T::x),
// # objectless nonstatic member reference
// ::= <expr-primary>
private BaseNode ParseExpression()
{
bool IsGlobal = ConsumeIf("gs");
BaseNode Expression = null;
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<BaseNode> Names = new List<BaseNode>();
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 = null;
BaseNode RightNode = null;
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<BaseNode> BracedExpressions = new List<BaseNode>();
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 = null;
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<BaseNode> Arguments = new List<BaseNode>();
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<BaseNode> BracedExpressions = new List<BaseNode>();
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);
}
// <expr-primary> ::= L <type> <value number> E # integer literal
// ::= L <type> <value float> E # floating literal (TODO)
// ::= L <string type> E # string literal
// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
// ::= L <pointer type> 0 E # null pointer template argument
// ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
// ::= L _Z <encoding> 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);
}
}
// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x)
// ::= DT <expression> 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, ")");
}
// <template-param> ::= T_ # first template parameter
// ::= T <parameter-2 non-negative number> _
// <template-template-param> ::= <template-param>
// ::= <substitution>
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 ForwardTemplateReference(Index);
ForwardTemplateReferenceList.Add(ForwardTemplateReference);
return ForwardTemplateReference;
}
if (Index >= TemplateParamList.Count)
{
return null;
}
return TemplateParamList[Index];
}
// <template-args> ::= I <template-arg>+ E
private BaseNode ParseTemplateArguments(bool HasContext = false)
{
if (!ConsumeIf("I"))
{
return null;
}
if (HasContext)
{
TemplateParamList.Clear();
}
List<BaseNode> Args = new List<BaseNode>();
while (!ConsumeIf("E"))
{
if (HasContext)
{
List<BaseNode> TemplateParamListTemp = new List<BaseNode>(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);
}
// <template-arg> ::= <type> # type or template
// ::= X <expression> E # expression
// ::= <expr-primary> # simple expressions
// ::= J <template-arg>* E # argument pack
private BaseNode ParseTemplateArgument()
{
switch (Peek())
{
// X <expression> E
case 'X':
Position++;
BaseNode Expression = ParseExpression();
if (Expression == null || !ConsumeIf("E"))
{
return null;
}
return Expression;
// <expr-primary>
case 'L':
return ParseExpressionPrimary();
// J <template-arg>* E
case 'J':
Position++;
List<BaseNode> TemplateArguments = new List<BaseNode>();
while (!ConsumeIf("E"))
{
BaseNode TemplateArgument = ParseTemplateArgument();
if (TemplateArgument == null)
{
return null;
}
TemplateArguments.Add(TemplateArgument);
}
return new NodeArray(TemplateArguments, NodeType.PackedTemplateArgument);
// <type>
default:
return ParseType();
}
}
class NameParserContext
{
public CVType CV;
public SimpleReferenceType Ref;
public bool FinishWithTemplateArguments;
public bool CtorDtorConversion;
}
// <unresolved-type> ::= <template-param> [ <template-args> ] # T:: or T<X,Y>::
// ::= <decltype> # decltype(p)::
// ::= <substitution>
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();
}
// <simple-id> ::= <source-name> [ <template-args> ]
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;
}
// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f())
// ::= <simple-id> # 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);
}
// <base-unresolved-name> ::= <simple-id> # unresolved name
// extension ::= <operator-name> # unresolved operator-function-id
// extension ::= <operator-name> <template-args> # unresolved operator template-id
// ::= on <operator-name> # unresolved operator-function-id
// ::= on <operator-name> <template-args> # unresolved operator template-id
// ::= dn <destructor-name> # destructor or pseudo-destructor;
// # e.g. ~X or ~X<N-1>
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;
}
// <unresolved-name> ::= [gs] <base-unresolved-name> # x or (with "gs") ::x
// ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x
// ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
// # T::N::x /decltype(p)::N::x
// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
// # A::x, N::y, A<T>::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] <base-unresolved-name> # 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 <unresolved-qualifier-level>+ E <base-unresolved-name>
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 <unresolved-type> [tempate-args] <base-unresolved-name> # 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);
}
// <unscoped-name> ::= <unqualified-name>
// ::= St <unqualified-name> # ::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);
}
// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix (TODO)> <unqualified-name> E
// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix (TODO)> <template-args (TODO)> E
private BaseNode ParseNestedName(NameParserContext Context)
{
// Impossible in theory
if (Consume() != 'N')
{
return null;
}
BaseNode Result = null;
CVType CV = new CVType(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"))
{
// <data-member-prefix> 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;
}
// <template-prefix> <template-args>
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;
}
// <decltype>
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;
}
// <substitution>
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;
}
// <ctor-dtor-name> 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;
}
// <discriminator> ::= _ <non-negative number> # when number < 10
// ::= __ <non-negative number> _ # 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("_");
}
}
// <local-name> ::= Z <function encoding> E <entity name> [<discriminator>]
// ::= Z <function encoding> E s [<discriminator>]
// ::= Z <function encoding> Ed [ <parameter number> ] _ <entity name>
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);
}
// <name> ::= <nested-name>
// ::= <unscoped-name>
// ::= <unscoped-template-name> <template-args>
// ::= <local-name> # 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 == '_';
}
// <encoding> ::= <function name> <bare-function-type>
// ::= <data name>
// ::= <special-name>
private BaseNode ParseEncoding()
{
NameParserContext Context = new NameParserContext();
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<BaseNode> Params = new List<BaseNode>();
// 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;
}
Params.Add(Param);
}
return new EncodedFunction(Name, new NodeArray(Params), CV, Ref, null, ReturnType);
}
// <mangled-name> ::= _Z <encoding>
// ::= <type>
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 Demangler(OriginalMangled);
BaseNode ResNode = Instance.Parse();
if (ResNode != null)
{
StringWriter Writer = new StringWriter();
ResNode.Print(Writer);
return Writer.ToString();
}
return OriginalMangled;
}
}
}