diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs deleted file mode 100644 index aea979c26..000000000 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.HLE.HOS.Diagnostics -{ - static class Demangler - { - private static readonly Dictionary BuiltinTypes = new Dictionary - { - { "v", "void" }, - { "w", "wchar_t" }, - { "b", "bool" }, - { "c", "char" }, - { "a", "signed char" }, - { "h", "unsigned char" }, - { "s", "short" }, - { "t", "unsigned short" }, - { "i", "int" }, - { "j", "unsigned int" }, - { "l", "long" }, - { "m", "unsigned long" }, - { "x", "long long" }, - { "y", "unsigned long long" }, - { "n", "__int128" }, - { "o", "unsigned __int128" }, - { "f", "float" }, - { "d", "double" }, - { "e", "long double" }, - { "g", "__float128" }, - { "z", "..." }, - { "Dd", "__iec559_double" }, - { "De", "__iec559_float128" }, - { "Df", "__iec559_float" }, - { "Dh", "__iec559_float16" }, - { "Di", "char32_t" }, - { "Ds", "char16_t" }, - { "Da", "decltype(auto)" }, - { "Dn", "std::nullptr_t" }, - }; - - private static readonly Dictionary SubstitutionExtra = new Dictionary - { - {"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 static int FromBase36(string encoded) - { - string base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; - char[] reversedEncoded = encoded.ToLower().ToCharArray().Reverse().ToArray(); - int result = 0; - for (int i = 0; i < reversedEncoded.Length; i++) - { - char c = reversedEncoded[i]; - int value = base36.IndexOf(c); - if (value == -1) - return -1; - result += value * (int)Math.Pow(36, i); - } - return result; - } - - private static string GetCompressedValue(string compression, List compressionData, out int pos) - { - string res = null; - bool canHaveUnqualifiedName = false; - pos = -1; - if (compressionData.Count == 0 || !compression.StartsWith("S")) - return null; - - if (compression.Length >= 2 && SubstitutionExtra.TryGetValue(compression.Substring(0, 2), out string substitutionValue)) - { - pos = 1; - res = substitutionValue; - compression = compression.Substring(2); - } - else if (compression.StartsWith("St")) - { - pos = 1; - canHaveUnqualifiedName = true; - res = "std"; - compression = compression.Substring(2); - } - else if (compression.StartsWith("S_")) - { - pos = 1; - res = compressionData[0]; - canHaveUnqualifiedName = true; - compression = compression.Substring(2); - } - else - { - int id = -1; - int underscorePos = compression.IndexOf('_'); - if (underscorePos == -1) - return null; - string partialId = compression.Substring(1, underscorePos - 1); - - id = FromBase36(partialId); - if (id == -1 || compressionData.Count <= (id + 1)) - { - return null; - } - res = compressionData[id + 1]; - pos = partialId.Length + 1; - canHaveUnqualifiedName= true; - compression = compression.Substring(pos); - } - if (res != null) - { - if (canHaveUnqualifiedName) - { - List type = ReadName(compression, compressionData, out int endOfNameType); - if (endOfNameType != -1 && type != null) - { - pos += endOfNameType; - res = res + "::" + type[type.Count - 1]; - } - } - } - return res; - } - - private static List ReadName(string mangled, List compressionData, out int pos, bool isNested = true) - { - List res = new List(); - string charCountString = null; - int charCount = 0; - int i; - - pos = -1; - for (i = 0; i < mangled.Length; i++) - { - char chr = mangled[i]; - if (charCountString == null) - { - if (ReadCVQualifiers(chr) != null) - { - continue; - } - if (chr == 'S') - { - string data = GetCompressedValue(mangled.Substring(i), compressionData, out pos); - if (pos == -1) - { - return null; - } - if (res.Count == 0) - res.Add(data); - else - res.Add(res[res.Count - 1] + "::" + data); - i += pos; - if (i < mangled.Length && mangled[i] == 'E') - { - break; - } - continue; - } - else if (chr == 'E') - { - break; - } - } - if (Char.IsDigit(chr)) - { - charCountString += chr; - } - else - { - if (!int.TryParse(charCountString, out charCount)) - { - return null; - } - string demangledPart = mangled.Substring(i, charCount); - if (res.Count == 0) - res.Add(demangledPart); - else - res.Add(res[res.Count - 1] + "::" + demangledPart); - i = i + charCount - 1; - charCount = 0; - charCountString = null; - if (!isNested) - break; - } - } - if (res.Count == 0) - { - return null; - } - pos = i; - return res; - } - - private static string ReadBuiltinType(string mangledType, out int pos) - { - string res = null; - string possibleBuiltinType; - pos = -1; - possibleBuiltinType = mangledType[0].ToString(); - if (!BuiltinTypes.TryGetValue(possibleBuiltinType, out res)) - { - if (mangledType.Length >= 2) - { - // Try to match the first 2 chars if the first call failed - possibleBuiltinType = mangledType.Substring(0, 2); - BuiltinTypes.TryGetValue(possibleBuiltinType, out res); - } - } - if (res != null) - pos = possibleBuiltinType.Length; - return res; - } - - private static string ReadCVQualifiers(char qualifier) - { - if (qualifier == 'r') - return "restricted"; - else if (qualifier == 'V') - return "volatile"; - else if (qualifier == 'K') - return "const"; - return null; - } - - private static string ReadRefQualifiers(char qualifier) - { - if (qualifier == 'R') - return "&"; - else if (qualifier == 'O') - return "&&"; - return null; - } - - private static string ReadSpecialQualifiers(char qualifier) - { - if (qualifier == 'P') - return "*"; - else if (qualifier == 'C') - return "complex"; - else if (qualifier == 'G') - return "imaginary"; - return null; - } - - private static List ReadParameters(string mangledParams, List compressionData, out int pos) - { - List res = new List(); - List refQualifiers = new List(); - string parsedTypePart = null; - string currentRefQualifiers = null; - string currentBuiltinType = null; - string currentSpecialQualifiers = null; - string currentCompressedValue = null; - int i = 0; - pos = -1; - - for (i = 0; i < mangledParams.Length; i++) - { - if (currentBuiltinType != null) - { - string currentCVQualifier = String.Join(" ", refQualifiers); - // Try to mimic the compression indexing - if (currentRefQualifiers != null) - { - compressionData.Add(currentBuiltinType + currentRefQualifiers); - } - if (refQualifiers.Count != 0) - { - compressionData.Add(currentBuiltinType + " " + currentCVQualifier + currentRefQualifiers); - } - if (currentSpecialQualifiers != null) - { - compressionData.Add(currentBuiltinType + " " + currentCVQualifier + currentRefQualifiers + currentSpecialQualifiers); - } - if (currentRefQualifiers == null && currentCVQualifier == null && currentSpecialQualifiers == null) - { - compressionData.Add(currentBuiltinType); - } - currentBuiltinType = null; - currentCompressedValue = null; - currentCVQualifier = null; - currentRefQualifiers = null; - refQualifiers.Clear(); - currentSpecialQualifiers = null; - } - char chr = mangledParams[i]; - string part = mangledParams.Substring(i); - - // Try to read qualifiers - parsedTypePart = ReadCVQualifiers(chr); - if (parsedTypePart != null) - { - refQualifiers.Add(parsedTypePart); - - // need more data - continue; - } - - parsedTypePart = ReadRefQualifiers(chr); - if (parsedTypePart != null) - { - currentRefQualifiers = parsedTypePart; - - // need more data - continue; - } - - parsedTypePart = ReadSpecialQualifiers(chr); - if (parsedTypePart != null) - { - currentSpecialQualifiers = parsedTypePart; - - // need more data - continue; - } - - // TODO: extended-qualifier? - - if (part.StartsWith("S")) - { - parsedTypePart = GetCompressedValue(part, compressionData, out pos); - if (pos != -1 && parsedTypePart != null) - { - currentCompressedValue = parsedTypePart; - i += pos; - res.Add(currentCompressedValue + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); - currentBuiltinType = null; - currentCompressedValue = null; - currentRefQualifiers = null; - refQualifiers.Clear(); - currentSpecialQualifiers = null; - continue; - } - pos = -1; - return null; - } - else if (part.StartsWith("N")) - { - part = part.Substring(1); - List name = ReadName(part, compressionData, out pos); - if (pos != -1 && name != null) - { - i += pos + 1; - res.Add(name[name.Count - 1] + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); - currentBuiltinType = null; - currentCompressedValue = null; - currentRefQualifiers = null; - refQualifiers.Clear(); - currentSpecialQualifiers = null; - continue; - } - } - - // Try builting - parsedTypePart = ReadBuiltinType(part, out pos); - if (pos == -1) - { - return null; - } - currentBuiltinType = parsedTypePart; - res.Add(currentBuiltinType + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); - i = i + pos -1; - } - pos = i; - return res; - } - - private static string ParseFunctionName(string mangled) - { - List compressionData = new List(); - int pos = 0; - string res; - bool isNested = mangled.StartsWith("N"); - - // If it's start with "N" it must be a nested function name - if (isNested) - mangled = mangled.Substring(1); - compressionData = ReadName(mangled, compressionData, out pos, isNested); - if (pos == -1) - return null; - res = compressionData[compressionData.Count - 1]; - compressionData.Remove(res); - mangled = mangled.Substring(pos + 1); - - // more data? maybe not a data name so... - if (mangled != String.Empty) - { - List parameters = ReadParameters(mangled, compressionData, out pos); - // parameters parsing error, we return the original data to avoid information loss. - if (pos == -1) - return null; - parameters = parameters.Select(outer => outer.Trim()).ToList(); - res += "(" + String.Join(", ", parameters) + ")"; - } - return res; - } - - public static string Parse(string originalMangled) - { - if (originalMangled.StartsWith("_Z")) - { - // We assume that we have a name (TOOD: support special names) - string res = ParseFunctionName(originalMangled.Substring(2)); - if (res == null) - return originalMangled; - return res; - } - return originalMangled; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs new file mode 100644 index 000000000..435789e0f --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ArraySubscriptingExpression : BaseNode + { + private BaseNode LeftNode; + private BaseNode Subscript; + + public ArraySubscriptingExpression(BaseNode LeftNode, BaseNode Subscript) : base(NodeType.ArraySubscriptingExpression) + { + this.LeftNode = LeftNode; + this.Subscript = Subscript; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + LeftNode.Print(Writer); + Writer.Write(")["); + Subscript.Print(Writer); + Writer.Write("]"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs new file mode 100644 index 000000000..167973607 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs @@ -0,0 +1,59 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ArrayType : BaseNode + { + private BaseNode Base; + private BaseNode DimensionExpression; + private string DimensionString; + + public ArrayType(BaseNode Base, BaseNode DimensionExpression = null) : base(NodeType.ArrayType) + { + this.Base = Base; + this.DimensionExpression = DimensionExpression; + } + + public ArrayType(BaseNode Base, string DimensionString) : base(NodeType.ArrayType) + { + this.Base = Base; + this.DimensionString = DimensionString; + } + + public override bool HasRightPart() + { + return true; + } + + public override bool IsArray() + { + return true; + } + + public override void PrintLeft(TextWriter Writer) + { + Base.PrintLeft(Writer); + } + + public override void PrintRight(TextWriter Writer) + { + // FIXME: detect if previous char was a ]. + Writer.Write(" "); + + Writer.Write("["); + + if (DimensionString != null) + { + Writer.Write(DimensionString); + } + else if (DimensionExpression != null) + { + DimensionExpression.Print(Writer); + } + + Writer.Write("]"); + + Base.PrintRight(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs new file mode 100644 index 000000000..870758462 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs @@ -0,0 +1,113 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public enum NodeType + { + CVQualifierType, + SimpleReferenceType, + NameType, + EncodedFunction, + NestedName, + SpecialName, + LiteralOperator, + NodeArray, + ElaboratedType, + PostfixQualifiedType, + SpecialSubstitution, + ExpandedSpecialSubstitution, + CtorDtorNameType, + EnclosedExpression, + ForwardTemplateReference, + NameTypeWithTemplateArguments, + PackedTemplateArgument, + TemplateArguments, + BooleanExpression, + CastExpression, + CallExpression, + IntegerCastExpression, + PackedTemplateParameter, + PackedTemplateParameterExpansion, + IntegerLiteral, + DeleteExpression, + MemberExpression, + ArraySubscriptingExpression, + InitListExpression, + PostfixExpression, + ConditionalExpression, + ThrowExpression, + FunctionParameter, + ConversionExpression, + BinaryExpression, + PrefixExpression, + BracedExpression, + BracedRangeExpression, + NewExpression, + QualifiedName, + StdQualifiedName, + DtOrName, + GlobalQualifiedName, + NoexceptSpec, + DynamicExceptionSpec, + FunctionType, + PointerType, + ReferenceType, + ConversionOperatorType, + LocalName, + CtorVtableSpecialName, + ArrayType + } + + public abstract class BaseNode + { + public NodeType Type { get; protected set; } + + public BaseNode(NodeType Type) + { + this.Type = Type; + } + + public virtual void Print(TextWriter Writer) + { + PrintLeft(Writer); + + if (HasRightPart()) + { + PrintRight(Writer); + } + } + + public abstract void PrintLeft(TextWriter Writer); + + public virtual bool HasRightPart() + { + return false; + } + + public virtual bool IsArray() + { + return false; + } + + public virtual bool HasFunctions() + { + return false; + } + + public virtual string GetName() + { + return null; + } + + public virtual void PrintRight(TextWriter Writer) {} + + public override string ToString() + { + StringWriter Writer = new StringWriter(); + + Print(Writer); + + return Writer.ToString(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs new file mode 100644 index 000000000..9cd1dd779 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class BinaryExpression : BaseNode + { + private BaseNode LeftPart; + private string Name; + private BaseNode RightPart; + + public BinaryExpression(BaseNode LeftPart, string Name, BaseNode RightPart) : base(NodeType.BinaryExpression) + { + this.LeftPart = LeftPart; + this.Name = Name; + this.RightPart = RightPart; + } + + public override void PrintLeft(TextWriter Writer) + { + if (Name.Equals(">")) + { + Writer.Write("("); + } + + Writer.Write("("); + LeftPart.Print(Writer); + Writer.Write(") "); + + Writer.Write(Name); + + Writer.Write(" ("); + RightPart.Print(Writer); + Writer.Write(")"); + + if (Name.Equals(">")) + { + Writer.Write(")"); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs new file mode 100644 index 000000000..59222ea3f --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs @@ -0,0 +1,40 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class BracedExpression : BaseNode + { + private BaseNode Element; + private BaseNode Expression; + private bool IsArrayExpression; + + public BracedExpression(BaseNode Element, BaseNode Expression, bool IsArrayExpression) : base(NodeType.BracedExpression) + { + this.Element = Element; + this.Expression = Expression; + this.IsArrayExpression = IsArrayExpression; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsArrayExpression) + { + Writer.Write("["); + Element.Print(Writer); + Writer.Write("]"); + } + else + { + Writer.Write("."); + Element.Print(Writer); + } + + if (!Expression.GetType().Equals(NodeType.BracedExpression) || !Expression.GetType().Equals(NodeType.BracedRangeExpression)) + { + Writer.Write(" = "); + } + + Expression.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs new file mode 100644 index 000000000..e459f1a39 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs @@ -0,0 +1,34 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class BracedRangeExpression : BaseNode + { + private BaseNode FirstNode; + private BaseNode LastNode; + private BaseNode Expression; + + public BracedRangeExpression(BaseNode FirstNode, BaseNode LastNode, BaseNode Expression) : base(NodeType.BracedRangeExpression) + { + this.FirstNode = FirstNode; + this.LastNode = LastNode; + this.Expression = Expression; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("["); + FirstNode.Print(Writer); + Writer.Write(" ... "); + LastNode.Print(Writer); + Writer.Write("]"); + + if (!Expression.GetType().Equals(NodeType.BracedExpression) || !Expression.GetType().Equals(NodeType.BracedRangeExpression)) + { + Writer.Write(" = "); + } + + Expression.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs new file mode 100644 index 000000000..7e59ab09d --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CallExpression : NodeArray + { + private BaseNode Callee; + + public CallExpression(BaseNode Callee, List Nodes) : base(Nodes, NodeType.CallExpression) + { + this.Callee = Callee; + } + + public override void PrintLeft(TextWriter Writer) + { + Callee.Print(Writer); + + Writer.Write("("); + Writer.Write(string.Join(", ", Nodes.ToArray())); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs new file mode 100644 index 000000000..2415c6c00 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; + + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CastExpression : BaseNode + { + private string Kind; + private BaseNode To; + private BaseNode From; + + public CastExpression(string Kind, BaseNode To, BaseNode From) : base(NodeType.CastExpression) + { + this.Kind = Kind; + this.To = To; + this.From = From; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Kind); + Writer.Write("<"); + To.PrintLeft(Writer); + Writer.Write(">("); + From.PrintLeft(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs new file mode 100644 index 000000000..17ac7c1a2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs @@ -0,0 +1,29 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ConditionalExpression : BaseNode + { + private BaseNode ThenNode; + private BaseNode ElseNode; + private BaseNode ConditionNode; + + public ConditionalExpression(BaseNode ConditionNode, BaseNode ThenNode, BaseNode ElseNode) : base(NodeType.ConditionalExpression) + { + this.ThenNode = ThenNode; + this.ConditionNode = ConditionNode; + this.ElseNode = ElseNode; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + ConditionNode.Print(Writer); + Writer.Write(") ? ("); + ThenNode.Print(Writer); + Writer.Write(") : ("); + ElseNode.Print(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs new file mode 100644 index 000000000..7c5d35d81 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ConversionExpression : BaseNode + { + private BaseNode TypeNode; + private BaseNode Expressions; + + public ConversionExpression(BaseNode TypeNode, BaseNode Expressions) : base(NodeType.ConversionExpression) + { + this.TypeNode = TypeNode; + this.Expressions = Expressions; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + TypeNode.Print(Writer); + Writer.Write(")("); + Expressions.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs new file mode 100644 index 000000000..55d4eecab --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ConversionOperatorType : ParentNode + { + public ConversionOperatorType(BaseNode Child) : base(NodeType.ConversionOperatorType, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("operator "); + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs new file mode 100644 index 000000000..49ed386d0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CtorDtorNameType : ParentNode + { + private bool IsDestructor; + + public CtorDtorNameType(BaseNode Name, bool IsDestructor) : base(NodeType.CtorDtorNameType, Name) + { + this.IsDestructor = IsDestructor; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsDestructor) + { + Writer.Write("~"); + } + + Writer.Write(Child.GetName()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs new file mode 100644 index 000000000..7630dbe57 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CtorVtableSpecialName : BaseNode + { + private BaseNode FirstType; + private BaseNode SecondType; + + public CtorVtableSpecialName(BaseNode FirstType, BaseNode SecondType) : base(NodeType.CtorVtableSpecialName) + { + this.FirstType = FirstType; + this.SecondType = SecondType; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("construction vtable for "); + FirstType.Print(Writer); + Writer.Write("-in-"); + SecondType.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs new file mode 100644 index 000000000..22c34c421 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs @@ -0,0 +1,33 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class DeleteExpression : ParentNode + { + private bool IsGlobal; + private bool IsArrayExpression; + + public DeleteExpression(BaseNode Child, bool IsGlobal, bool IsArrayExpression) : base(NodeType.DeleteExpression, Child) + { + this.IsGlobal = IsGlobal; + this.IsArrayExpression = IsArrayExpression; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsGlobal) + { + Writer.Write("::"); + } + + Writer.Write("delete"); + + if (IsArrayExpression) + { + Writer.Write("[] "); + } + + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs new file mode 100644 index 000000000..c65c4cfb3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class DtorName : ParentNode + { + public DtorName(BaseNode Name) : base(NodeType.DtOrName, Name) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("~"); + Child.PrintLeft(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs new file mode 100644 index 000000000..dca5f0dfd --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class DynamicExceptionSpec : ParentNode + { + public DynamicExceptionSpec(BaseNode Child) : base(NodeType.DynamicExceptionSpec, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("throw("); + Child.Print(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs new file mode 100644 index 000000000..11f89c8d3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs @@ -0,0 +1,21 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ElaboratedType : ParentNode + { + private string Elaborated; + + public ElaboratedType(string Elaborated, BaseNode Type) : base(NodeType.ElaboratedType, Type) + { + this.Elaborated = Elaborated; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Elaborated); + Writer.Write(" "); + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs new file mode 100644 index 000000000..dc991aa09 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class EnclosedExpression : BaseNode + { + private string Prefix; + private BaseNode Expression; + private string Postfix; + + public EnclosedExpression(string Prefix, BaseNode Expression, string Postfix) : base(NodeType.EnclosedExpression) + { + this.Prefix = Prefix; + this.Expression = Expression; + this.Postfix = Postfix; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Prefix); + Expression.Print(Writer); + Writer.Write(Postfix); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs new file mode 100644 index 000000000..37a9a7afe --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs @@ -0,0 +1,77 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class EncodedFunction : BaseNode + { + private BaseNode Name; + private BaseNode Params; + private BaseNode CV; + private BaseNode Ref; + private BaseNode Attrs; + private BaseNode Ret; + + public EncodedFunction(BaseNode Name, BaseNode Params, BaseNode CV, BaseNode Ref, BaseNode Attrs, BaseNode Ret) : base(NodeType.NameType) + { + this.Name = Name; + this.Params = Params; + this.CV = CV; + this.Ref = Ref; + this.Attrs = Attrs; + this.Ret = Ret; + } + + public override void PrintLeft(TextWriter Writer) + { + if (Ret != null) + { + Ret.PrintLeft(Writer); + + if (!Ret.HasRightPart()) + { + Writer.Write(" "); + } + } + + Name.Print(Writer); + + } + + public override bool HasRightPart() + { + return true; + } + + public override void PrintRight(TextWriter Writer) + { + Writer.Write("("); + + if (Params != null) + { + Params.Print(Writer); + } + + Writer.Write(")"); + + if (Ret != null) + { + Ret.PrintRight(Writer); + } + + if (CV != null) + { + CV.Print(Writer); + } + + if (Ref != null) + { + Ref.Print(Writer); + } + + if (Attrs != null) + { + Attrs.Print(Writer); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs new file mode 100644 index 000000000..e01529984 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs @@ -0,0 +1,48 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class FoldExpression : BaseNode + { + private bool IsLeftFold; + private string OperatorName; + private BaseNode Expression; + private BaseNode Initializer; + + public FoldExpression(bool IsLeftFold, string OperatorName, BaseNode Expression, BaseNode Initializer) : base(NodeType.FunctionParameter) + { + this.IsLeftFold = IsLeftFold; + this.OperatorName = OperatorName; + this.Expression = Expression; + this.Initializer = Initializer; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + + if (IsLeftFold && Initializer != null) + { + Initializer.Print(Writer); + Writer.Write(" "); + Writer.Write(OperatorName); + Writer.Write(" "); + } + + Writer.Write(IsLeftFold ? "... " : " "); + Writer.Write(OperatorName); + Writer.Write(!IsLeftFold ? " ..." : " "); + Expression.Print(Writer); + + if (!IsLeftFold && Initializer != null) + { + Initializer.Print(Writer); + Writer.Write(" "); + Writer.Write(OperatorName); + Writer.Write(" "); + } + + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs new file mode 100644 index 000000000..6456e47bf --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs @@ -0,0 +1,36 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ForwardTemplateReference : BaseNode + { + // TODO: Compute inside the Demangler + public BaseNode Reference; + private int Index; + + public ForwardTemplateReference(int Index) : base(NodeType.ForwardTemplateReference) + { + this.Index = Index; + } + + public override string GetName() + { + return Reference.GetName(); + } + + public override void PrintLeft(TextWriter Writer) + { + Reference.PrintLeft(Writer); + } + + public override void PrintRight(TextWriter Writer) + { + Reference.PrintRight(Writer); + } + + public override bool HasRightPart() + { + return Reference.HasRightPart(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs new file mode 100644 index 000000000..5a1ca61d5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class FunctionParameter : BaseNode + { + private string Number; + + public FunctionParameter(string Number) : base(NodeType.FunctionParameter) + { + this.Number = Number; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("fp "); + + if (Number != null) + { + Writer.Write(Number); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs new file mode 100644 index 000000000..c727eab9c --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs @@ -0,0 +1,61 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class FunctionType : BaseNode + { + private BaseNode ReturnType; + private BaseNode Params; + private BaseNode CVQualifier; + private SimpleReferenceType ReferenceQualifier; + private BaseNode ExceptionSpec; + + public FunctionType(BaseNode ReturnType, BaseNode Params, BaseNode CVQualifier, SimpleReferenceType ReferenceQualifier, BaseNode ExceptionSpec) : base(NodeType.FunctionType) + { + this.ReturnType = ReturnType; + this.Params = Params; + this.CVQualifier = CVQualifier; + this.ReferenceQualifier = ReferenceQualifier; + this.ExceptionSpec = ExceptionSpec; + } + + public override void PrintLeft(TextWriter Writer) + { + ReturnType.PrintLeft(Writer); + Writer.Write(" "); + } + + public override void PrintRight(TextWriter Writer) + { + Writer.Write("("); + Params.Print(Writer); + Writer.Write(")"); + + ReturnType.PrintRight(Writer); + + CVQualifier.Print(Writer); + + if (ReferenceQualifier.Qualifier != Reference.None) + { + Writer.Write(" "); + ReferenceQualifier.PrintQualifier(Writer); + } + + if (ExceptionSpec != null) + { + Writer.Write(" "); + ExceptionSpec.Print(Writer); + } + } + + public override bool HasRightPart() + { + return true; + } + + public override bool HasFunctions() + { + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs new file mode 100644 index 000000000..2346c1bf7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class GlobalQualifiedName : ParentNode + { + public GlobalQualifiedName(BaseNode Child) : base(NodeType.GlobalQualifiedName, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("::"); + Child.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs new file mode 100644 index 000000000..cd534590b --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class InitListExpression : BaseNode + { + private BaseNode TypeNode; + private List Nodes; + + public InitListExpression(BaseNode TypeNode, List Nodes) : base(NodeType.InitListExpression) + { + this.TypeNode = TypeNode; + this.Nodes = Nodes; + } + + public override void PrintLeft(TextWriter Writer) + { + if (TypeNode != null) + { + TypeNode.Print(Writer); + } + + Writer.Write("{"); + Writer.Write(string.Join(", ", Nodes.ToArray())); + Writer.Write("}"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs new file mode 100644 index 000000000..984c9aefb --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class IntegerCastExpression : ParentNode + { + private string Number; + + public IntegerCastExpression(BaseNode Type, string Number) : base(NodeType.IntegerCastExpression, Type) + { + this.Number = Number; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + Child.Print(Writer); + Writer.Write(")"); + Writer.Write(Number); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs new file mode 100644 index 000000000..215cf6dce --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class IntegerLiteral : BaseNode + { + private string LitteralName; + private string LitteralValue; + + public IntegerLiteral(string LitteralName, string LitteralValue) : base(NodeType.IntegerLiteral) + { + this.LitteralValue = LitteralValue; + this.LitteralName = LitteralName; + } + + public override void PrintLeft(TextWriter Writer) + { + if (LitteralName.Length > 3) + { + Writer.Write("("); + Writer.Write(LitteralName); + Writer.Write(")"); + } + + if (LitteralValue[0] == 'n') + { + Writer.Write("-"); + Writer.Write(LitteralValue.Substring(1)); + } + else + { + Writer.Write(LitteralValue); + } + + if (LitteralName.Length <= 3) + { + Writer.Write(LitteralName); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs new file mode 100644 index 000000000..f9bd4a6ef --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class LiteralOperator : ParentNode + { + public LiteralOperator(BaseNode Child) : base(NodeType.LiteralOperator, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("operator \""); + Child.PrintLeft(Writer); + Writer.Write("\""); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs new file mode 100644 index 000000000..44c216289 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs @@ -0,0 +1,23 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class LocalName : BaseNode + { + private BaseNode Encoding; + private BaseNode Entity; + + public LocalName(BaseNode Encoding, BaseNode Entity) : base(NodeType.LocalName) + { + this.Encoding = Encoding; + this.Entity = Entity; + } + + public override void PrintLeft(TextWriter Writer) + { + Encoding.Print(Writer); + Writer.Write("::"); + Entity.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs new file mode 100644 index 000000000..dd3d02dbd --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class MemberExpression : BaseNode + { + private BaseNode LeftNode; + private string Kind; + private BaseNode RightNode; + + public MemberExpression(BaseNode LeftNode, string Kind, BaseNode RightNode) : base(NodeType.MemberExpression) + { + this.LeftNode = LeftNode; + this.Kind = Kind; + this.RightNode = RightNode; + } + + public override void PrintLeft(TextWriter Writer) + { + LeftNode.Print(Writer); + Writer.Write(Kind); + RightNode.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs new file mode 100644 index 000000000..029440cb7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs @@ -0,0 +1,29 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NameType : BaseNode + { + private string NameValue; + + public NameType(string NameValue, NodeType Type) : base(Type) + { + this.NameValue = NameValue; + } + + public NameType(string NameValue) : base(NodeType.NameType) + { + this.NameValue = NameValue; + } + + public override string GetName() + { + return NameValue; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(NameValue); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs new file mode 100644 index 000000000..e16bd1508 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NameTypeWithTemplateArguments : BaseNode + { + private BaseNode Prev; + private BaseNode TemplateArgument; + + public NameTypeWithTemplateArguments(BaseNode Prev, BaseNode TemplateArgument) : base(NodeType.NameTypeWithTemplateArguments) + { + this.Prev = Prev; + this.TemplateArgument = TemplateArgument; + } + + public override string GetName() + { + return Prev.GetName(); + } + + public override void PrintLeft(TextWriter Writer) + { + Prev.Print(Writer); + TemplateArgument.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs new file mode 100644 index 000000000..0ec6d9829 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs @@ -0,0 +1,26 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NestedName : ParentNode + { + private BaseNode Name; + + public NestedName(BaseNode Name, BaseNode Type) : base(NodeType.NestedName, Type) + { + this.Name = Name; + } + + public override string GetName() + { + return Name.GetName(); + } + + public override void PrintLeft(TextWriter Writer) + { + Child.Print(Writer); + Writer.Write("::"); + Name.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs new file mode 100644 index 000000000..5cc14ad9f --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs @@ -0,0 +1,55 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NewExpression : BaseNode + { + private NodeArray Expressions; + private BaseNode TypeNode; + private NodeArray Initializers; + + private bool IsGlobal; + private bool IsArrayExpression; + + public NewExpression(NodeArray Expressions, BaseNode TypeNode, NodeArray Initializers, bool IsGlobal, bool IsArrayExpression) : base(NodeType.NewExpression) + { + this.Expressions = Expressions; + this.TypeNode = TypeNode; + this.Initializers = Initializers; + + this.IsGlobal = IsGlobal; + this.IsArrayExpression = IsArrayExpression; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsGlobal) + { + Writer.Write("::operator "); + } + + Writer.Write("new "); + + if (IsArrayExpression) + { + Writer.Write("[] "); + } + + if (Expressions.Nodes.Count != 0) + { + Writer.Write("("); + Expressions.Print(Writer); + Writer.Write(")"); + } + + TypeNode.Print(Writer); + + if (Initializers.Nodes.Count != 0) + { + Writer.Write("("); + Initializers.Print(Writer); + Writer.Write(")"); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs new file mode 100644 index 000000000..9720a8e49 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NodeArray : BaseNode + { + public List Nodes { get; protected set; } + + public NodeArray(List Nodes) : base(NodeType.NodeArray) + { + this.Nodes = Nodes; + } + + public NodeArray(List Nodes, NodeType Type) : base(Type) + { + this.Nodes = Nodes; + } + + public override bool IsArray() + { + return true; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(string.Join(", ", Nodes.ToArray())); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs new file mode 100644 index 000000000..5bee9cfac --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NoexceptSpec : ParentNode + { + public NoexceptSpec(BaseNode Child) : base(NodeType.NoexceptSpec, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("noexcept("); + Child.Print(Writer); + Writer.Write(")"); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs new file mode 100644 index 000000000..66ad11227 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PackedTemplateParameter : NodeArray + { + public PackedTemplateParameter(List Nodes) : base(Nodes, NodeType.PackedTemplateParameter) { } + + public override void PrintLeft(TextWriter Writer) + { + foreach (BaseNode Node in Nodes) + { + Node.PrintLeft(Writer); + } + } + + public override void PrintRight(TextWriter Writer) + { + foreach (BaseNode Node in Nodes) + { + Node.PrintLeft(Writer); + } + } + + public override bool HasRightPart() + { + foreach (BaseNode Node in Nodes) + { + if (Node.HasRightPart()) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs new file mode 100644 index 000000000..ce9fa4a3b --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PackedTemplateParameterExpansion : ParentNode + { + public PackedTemplateParameterExpansion(BaseNode Child) : base(NodeType.PackedTemplateParameterExpansion, Child) {} + + public override void PrintLeft(TextWriter Writer) + { + if (Child is PackedTemplateParameter) + { + if (((PackedTemplateParameter)Child).Nodes.Count != 0) + { + Child.Print(Writer); + } + } + else + { + Writer.Write("..."); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs new file mode 100644 index 000000000..f1c283473 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public abstract class ParentNode : BaseNode + { + public BaseNode Child { get; private set; } + + public ParentNode(NodeType Type, BaseNode Child) : base(Type) + { + this.Child = Child; + } + + public override string GetName() + { + return Child.GetName(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs new file mode 100644 index 000000000..a60776a27 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs @@ -0,0 +1,45 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PointerType : BaseNode + { + private BaseNode Child; + + public PointerType(BaseNode Child) : base(NodeType.PointerType) + { + this.Child = Child; + } + + public override bool HasRightPart() + { + return Child.HasRightPart(); + } + + public override void PrintLeft(TextWriter Writer) + { + Child.PrintLeft(Writer); + if (Child.IsArray()) + { + Writer.Write(" "); + } + + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write("("); + } + + Writer.Write("*"); + } + + public override void PrintRight(TextWriter Writer) + { + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write(")"); + } + + Child.PrintRight(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs new file mode 100644 index 000000000..021f2de82 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PostfixExpression : ParentNode + { + private string Operator; + + public PostfixExpression(BaseNode Type, string Operator) : base(NodeType.PostfixExpression, Type) + { + this.Operator = Operator; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + Child.Print(Writer); + Writer.Write(")"); + Writer.Write(Operator); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs new file mode 100644 index 000000000..465450d3f --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PostfixQualifiedType : ParentNode + { + private string PostfixQualifier; + + public PostfixQualifiedType(string PostfixQualifier, BaseNode Type) : base(NodeType.PostfixQualifiedType, Type) + { + this.PostfixQualifier = PostfixQualifier; + } + + public override void PrintLeft(TextWriter Writer) + { + Child.Print(Writer); + Writer.Write(PostfixQualifier); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs new file mode 100644 index 000000000..619d05387 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PrefixExpression : ParentNode + { + private string Prefix; + + public PrefixExpression(string Prefix, BaseNode Child) : base(NodeType.PrefixExpression, Child) + { + this.Prefix = Prefix; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Prefix); + Writer.Write("("); + Child.Print(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs new file mode 100644 index 000000000..ce356e162 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs @@ -0,0 +1,23 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class QualifiedName : BaseNode + { + private BaseNode Qualifier; + private BaseNode Name; + + public QualifiedName(BaseNode Qualifier, BaseNode Name) : base(NodeType.QualifiedName) + { + this.Qualifier = Qualifier; + this.Name = Name; + } + + public override void PrintLeft(TextWriter Writer) + { + Qualifier.Print(Writer); + Writer.Write("::"); + Name.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs new file mode 100644 index 000000000..3721b8dee --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs @@ -0,0 +1,120 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public enum CV + { + None, + Const, + Volatile, + Restricted = 4 + } + + public enum Reference + { + None, + RValue, + LValue + } + + public class CVType : ParentNode + { + public CV Qualifier; + + public CVType(CV Qualifier, BaseNode Child) : base(NodeType.CVQualifierType, Child) + { + this.Qualifier = Qualifier; + } + + public void PrintQualifier(TextWriter Writer) + { + if ((Qualifier & CV.Const) != 0) + { + Writer.Write(" const"); + } + + if ((Qualifier & CV.Volatile) != 0) + { + Writer.Write(" volatile"); + } + + if ((Qualifier & CV.Restricted) != 0) + { + Writer.Write(" restrict"); + } + } + + public override void PrintLeft(TextWriter Writer) + { + if (Child != null) + { + Child.PrintLeft(Writer); + } + + PrintQualifier(Writer); + } + + public override bool HasRightPart() + { + return Child != null && Child.HasRightPart(); + } + + public override void PrintRight(TextWriter Writer) + { + if (Child != null) + { + Child.PrintRight(Writer); + } + } + } + + public class SimpleReferenceType : ParentNode + { + public Reference Qualifier; + + public SimpleReferenceType(Reference Qualifier, BaseNode Child) : base(NodeType.SimpleReferenceType, Child) + { + this.Qualifier = Qualifier; + } + + public void PrintQualifier(TextWriter Writer) + { + if ((Qualifier & Reference.LValue) != 0) + { + Writer.Write("&"); + } + + if ((Qualifier & Reference.RValue) != 0) + { + Writer.Write("&&"); + } + } + + public override void PrintLeft(TextWriter Writer) + { + if (Child != null) + { + Child.PrintLeft(Writer); + } + else if (Qualifier != Reference.None) + { + Writer.Write(" "); + } + + PrintQualifier(Writer); + } + + public override bool HasRightPart() + { + return Child != null && Child.HasRightPart(); + } + + public override void PrintRight(TextWriter Writer) + { + if (Child != null) + { + Child.PrintRight(Writer); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs new file mode 100644 index 000000000..602814afb --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs @@ -0,0 +1,47 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ReferenceType : BaseNode + { + private string Reference; + private BaseNode Child; + + public ReferenceType(string Reference, BaseNode Child) : base(NodeType.ReferenceType) + { + this.Reference = Reference; + this.Child = Child; + } + + public override bool HasRightPart() + { + return Child.HasRightPart(); + } + + public override void PrintLeft(TextWriter Writer) + { + Child.PrintLeft(Writer); + + if (Child.IsArray()) + { + Writer.Write(" "); + } + + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write("("); + } + + Writer.Write(Reference); + } + public override void PrintRight(TextWriter Writer) + { + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write(")"); + } + + Child.PrintRight(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs new file mode 100644 index 000000000..1a299af4d --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class SpecialName : ParentNode + { + private string SpecialValue; + + public SpecialName(string SpecialValue, BaseNode Type) : base(NodeType.SpecialName, Type) + { + this.SpecialValue = SpecialValue; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(SpecialValue); + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs new file mode 100644 index 000000000..f4e9a14a1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs @@ -0,0 +1,89 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class SpecialSubstitution : BaseNode + { + public enum SpecialType + { + Allocator, + BasicString, + String, + IStream, + OStream, + IOStream, + } + + private SpecialType SpecialSubstitutionKey; + + public SpecialSubstitution(SpecialType SpecialSubstitutionKey) : base(NodeType.SpecialSubstitution) + { + this.SpecialSubstitutionKey = SpecialSubstitutionKey; + } + + public void SetExtended() + { + Type = NodeType.ExpandedSpecialSubstitution; + } + + public override string GetName() + { + switch (SpecialSubstitutionKey) + { + case SpecialType.Allocator: + return "allocator"; + case SpecialType.BasicString: + return "basic_string"; + case SpecialType.String: + if (Type == NodeType.ExpandedSpecialSubstitution) + { + return "basic_string"; + } + + return "string"; + case SpecialType.IStream: + return "istream"; + case SpecialType.OStream: + return "ostream"; + case SpecialType.IOStream: + return "iostream"; + } + + return null; + } + + private string GetExtendedName() + { + switch (SpecialSubstitutionKey) + { + case SpecialType.Allocator: + return "std::allocator"; + case SpecialType.BasicString: + return "std::basic_string"; + case SpecialType.String: + return "std::basic_string, std::allocator >"; + case SpecialType.IStream: + return "std::basic_istream >"; + case SpecialType.OStream: + return "std::basic_ostream >"; + case SpecialType.IOStream: + return "std::basic_iostream >"; + } + + return null; + } + + public override void PrintLeft(TextWriter Writer) + { + if (Type == NodeType.ExpandedSpecialSubstitution) + { + Writer.Write(GetExtendedName()); + } + else + { + Writer.Write("std::"); + Writer.Write(GetName()); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs new file mode 100644 index 000000000..ed1b59942 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class StdQualifiedName : ParentNode + { + public StdQualifiedName(BaseNode Child) : base(NodeType.StdQualifiedName, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("std::"); + Child.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs new file mode 100644 index 000000000..4de66e000 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class TemplateArguments : NodeArray + { + public TemplateArguments(List Nodes) : base(Nodes, NodeType.TemplateArguments) { } + + public override void PrintLeft(TextWriter Writer) + { + string Params = string.Join(", ", Nodes.ToArray()); + + Writer.Write("<"); + + Writer.Write(Params); + + if (Params.EndsWith(">")) + { + Writer.Write(" "); + } + + Writer.Write(">"); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs new file mode 100644 index 000000000..bb1466174 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ThrowExpression : BaseNode + { + private BaseNode Expression; + + public ThrowExpression(BaseNode Expression) : base(NodeType.ThrowExpression) + { + this.Expression = Expression; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("throw "); + Expression.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs new file mode 100644 index 000000000..164d5618c --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -0,0 +1,3367 @@ +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 SubstitutionList = new List(); + private List TemplateParamList = new List(); + + private List ForwardTemplateReferenceList = new List(); + + 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)); + } + + // ::= 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 List(); + + 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 Params = new List(); + + 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); + } + + // ::= 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 + 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: + 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 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); + } + + // ::= + 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) + { + ((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; + } + + // ::= 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 = 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), 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 List(); + 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(); + List Initializers = new List(); + + 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 = 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 Names = new List(); + 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 BracedExpressions = new List(); + 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 Arguments = new List(); + 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 List(); + 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 ForwardTemplateReference(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 List(); + while (!ConsumeIf("E")) + { + if (HasContext) + { + List TemplateParamListTemp = new List(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 List(); + 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 [tempate-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 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")) + { + // 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 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 Params = new List(); + + // 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); + } + + // ::= _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 Demangler(OriginalMangled); + BaseNode ResNode = Instance.Parse(); + + if (ResNode != null) + { + StringWriter Writer = new StringWriter(); + ResNode.Print(Writer); + return Writer.ToString(); + } + + return OriginalMangled; + } + } +} diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index bfda93f49..72ed6b0d8 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -3,7 +3,7 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.HLE.Exceptions; -using Ryujinx.HLE.HOS.Diagnostics; +using Ryujinx.HLE.HOS.Diagnostics.Demangler; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.SystemState;