aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler.cs416
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs25
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs59
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs113
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs41
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs40
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs34
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs25
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs30
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs29
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs24
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs15
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs24
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs24
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs33
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs15
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs16
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs21
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs25
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs77
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs48
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs36
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs24
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs61
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs15
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs30
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs22
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs41
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs16
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs23
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs25
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs29
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs27
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs26
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs55
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs31
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs16
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs39
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs24
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs17
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs45
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs22
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs20
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs22
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs23
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs120
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs47
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs20
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs89
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs15
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs27
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs20
-rw-r--r--Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs3367
-rw-r--r--Ryujinx.HLE/HOS/Process.cs2
54 files changed, 5113 insertions, 417 deletions
diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs
deleted file mode 100644
index aea979c2..00000000
--- 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<string, string> BuiltinTypes = new Dictionary<string, string>
- {
- { "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<string, string> SubstitutionExtra = new Dictionary<string, string>
- {
- {"Sa", "std::allocator"},
- {"Sb", "std::basic_string"},
- {"Ss", "std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>"},
- {"Si", "std::basic_istream<char, ::std::char_traits<char>>"},
- {"So", "std::basic_ostream<char, ::std::char_traits<char>>"},
- {"Sd", "std::basic_iostream<char, ::std::char_traits<char>>"}
- };
-
- private 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<string> 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<string> 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<string> ReadName(string mangled, List<string> compressionData, out int pos, bool isNested = true)
- {
- List<string> res = new List<string>();
- 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<string> ReadParameters(string mangledParams, List<string> compressionData, out int pos)
- {
- List<string> res = new List<string>();
- List<string> refQualifiers = new List<string>();
- 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<string> 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<string> compressionData = new List<string>();
- 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<string> 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 00000000..435789e0
--- /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 00000000..16797360
--- /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 00000000..87075846
--- /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 00000000..9cd1dd77
--- /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 00000000..59222ea3
--- /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 00000000..e459f1a3
--- /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 00000000..7e59ab09
--- /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<BaseNode> Nodes) : base(Nodes, NodeType.CallExpression)
+ {
+ this.Callee = Callee;
+ }
+
+ public override void PrintLeft(TextWriter Writer)
+ {
+ Callee.Print(Writer);
+
+ Writer.Write("(");
+ Writer.Write(string.Join<BaseNode>(", ", 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 00000000..2415c6c0
--- /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 00000000..17ac7c1a
--- /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 00000000..7c5d35d8
--- /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 00000000..55d4eeca
--- /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 00000000..49ed386d
--- /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 00000000..7630dbe5
--- /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 00000000..22c34c42
--- /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 00000000..c65c4cfb
--- /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 00000000..dca5f0df
--- /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 00000000..11f89c8d
--- /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 00000000..dc991aa0
--- /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 00000000..37a9a7af
--- /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 00000000..e0152998
--- /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 00000000..6456e47b
--- /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 00000000..5a1ca61d
--- /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 00000000..c727eab9
--- /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 00000000..2346c1bf
--- /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 00000000..cd534590
--- /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<BaseNode> Nodes;
+
+ public InitListExpression(BaseNode TypeNode, List<BaseNode> 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<BaseNode>(", ", 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 00000000..984c9aef
--- /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 00000000..215cf6dc
--- /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 00000000..f9bd4a6e
--- /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 00000000..44c21628
--- /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 00000000..dd3d02db
--- /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 00000000..029440cb
--- /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 00000000..e16bd150
--- /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 00000000..0ec6d982
--- /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 00000000..5cc14ad9
--- /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 00000000..9720a8e4
--- /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<BaseNode> Nodes { get; protected set; }
+
+ public NodeArray(List<BaseNode> Nodes) : base(NodeType.NodeArray)
+ {
+ this.Nodes = Nodes;
+ }
+
+ public NodeArray(List<BaseNode> Nodes, NodeType Type) : base(Type)
+ {
+ this.Nodes = Nodes;
+ }
+
+ public override bool IsArray()
+ {
+ return true;
+ }
+
+ public override void PrintLeft(TextWriter Writer)
+ {
+ Writer.Write(string.Join<BaseNode>(", ", 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 00000000..5bee9cfa
--- /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 00000000..66ad1122
--- /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<BaseNode> 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 00000000..ce9fa4a3
--- /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 00000000..f1c28347
--- /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 00000000..a60776a2
--- /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 00000000..021f2de8
--- /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 00000000..465450d3
--- /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 00000000..619d0538
--- /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 00000000..ce356e16
--- /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 00000000..3721b8de
--- /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 00000000..602814af
--- /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 00000000..1a299af4
--- /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 00000000..f4e9a14a
--- /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<char, std::char_traits<char>, std::allocator<char> >";
+ case SpecialType.IStream:
+ return "std::basic_istream<char, std::char_traits<char> >";
+ case SpecialType.OStream:
+ return "std::basic_ostream<char, std::char_traits<char> >";
+ case SpecialType.IOStream:
+ return "std::basic_iostream<char, std::char_traits<char> >";
+ }
+
+ 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 00000000..ed1b5994
--- /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 00000000..4de66e00
--- /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<BaseNode> Nodes) : base(Nodes, NodeType.TemplateArguments) { }
+
+ public override void PrintLeft(TextWriter Writer)
+ {
+ string Params = string.Join<BaseNode>(", ", 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 00000000..bb146617
--- /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 00000000..164d5618
--- /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<BaseNode> SubstitutionList = new List<BaseNode>();
+ private List<BaseNode> TemplateParamList = new List<BaseNode>();
+
+ private List<ForwardTemplateReference> ForwardTemplateReferenceList = new List<ForwardTemplateReference>();
+
+ public string Mangled { get; private set; }
+
+ private int Position;
+ private int Length;
+
+ private bool CanForwardTemplateReference;
+ private bool CanParseTemplateArgs;
+
+ public Demangler(string Mangled)
+ {
+ this.Mangled = Mangled;
+ Position = 0;
+ Length = Mangled.Length;
+ CanParseTemplateArgs = true;
+ }
+
+ private bool ConsumeIf(string ToConsume)
+ {
+ string MangledPart = Mangled.Substring(Position);
+
+ if (MangledPart.StartsWith(ToConsume))
+ {
+ Position += ToConsume.Length;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private string PeekString(int Offset = 0, int Length = 1)
+ {
+ if (Position + Offset >= Length)
+ {
+ return null;
+ }
+
+ return Mangled.Substring(Position + Offset, Length);
+ }
+
+ private char Peek(int Offset = 0)
+ {
+ if (Position + Offset >= Length)
+ {
+ return '\0';
+ }
+
+ return Mangled[Position + Offset];
+ }
+
+ private char Consume()
+ {
+ if (Position < Length)
+ {
+ return Mangled[Position++];
+ }
+
+ return '\0';
+ }
+
+ private int Count()
+ {
+ return Length - Position;
+ }
+
+ private static int FromBase36(string Encoded)
+ {
+ char[] ReversedEncoded = Encoded.ToLower().ToCharArray().Reverse().ToArray();
+
+ int Result = 0;
+
+ for (int i = 0; i < ReversedEncoded.Length; i++)
+ {
+ int Value = BASE_36.IndexOf(ReversedEncoded[i]);
+ if (Value == -1)
+ {
+ return -1;
+ }
+
+ Result += Value * (int)Math.Pow(36, i);
+ }
+
+ return Result;
+ }
+
+ private int ParseSeqId()
+ {
+ string Part = Mangled.Substring(Position);
+ int SeqIdLen = 0;
+
+ for (; SeqIdLen < Part.Length; SeqIdLen++)
+ {
+ if (!char.IsLetterOrDigit(Part[SeqIdLen]))
+ {
+ break;
+ }
+ }
+
+ Position += SeqIdLen;
+
+ return FromBase36(Part.Substring(0, SeqIdLen));
+ }
+
+ // <substitution> ::= S <seq-id> _
+ // ::= S_
+ // ::= St # std::
+ // ::= Sa # std::allocator
+ // ::= Sb # std::basic_string
+ // ::= Ss # std::basic_string<char, std::char_traits<char>, std::allocator<char> >
+ // ::= Si # std::basic_istream<char, std::char_traits<char> >
+ // ::= So # std::basic_ostream<char, std::char_traits<char> >
+ // ::= Sd # std::basic_iostream<char, std::char_traits<char> >
+ private BaseNode ParseSubstitution()
+ {
+ if (!ConsumeIf("S"))
+ {
+ return null;
+ }
+
+ char SubstitutionSecondChar = Peek();
+ if (char.IsLower(SubstitutionSecondChar))
+ {
+ switch (SubstitutionSecondChar)
+ {
+ case 'a':
+ Position++;
+ return new SpecialSubstitution(SpecialSubstitution.SpecialType.Allocator);
+ case 'b':
+ Position++;
+ return new SpecialSubstitution(SpecialSubstitution.SpecialType.BasicString);
+ case 's':
+ Position++;
+ return new SpecialSubstitution(SpecialSubstitution.SpecialType.String);
+ case 'i':
+ Position++;
+ return new SpecialSubstitution(SpecialSubstitution.SpecialType.IStream);
+ case 'o':
+ Position++;
+ return new SpecialSubstitution(SpecialSubstitution.SpecialType.OStream);
+ case 'd':
+ Position++;
+ return new SpecialSubstitution(SpecialSubstitution.SpecialType.IOStream);
+ default:
+ return null;
+ }
+ }
+
+ // ::= S_
+ if (ConsumeIf("_"))
+ {
+ if (SubstitutionList.Count != 0)
+ {
+ return SubstitutionList[0];
+ }
+
+ return null;
+ }
+
+ // ::= S <seq-id> _
+ int SeqId = ParseSeqId();
+ if (SeqId < 0)
+ {
+ return null;
+ }
+
+ SeqId++;
+
+ if (!ConsumeIf("_") || SeqId >= SubstitutionList.Count)
+ {
+ return null;
+ }
+
+ return SubstitutionList[SeqId];
+ }
+
+ // NOTE: thoses data aren't used in the output
+ // <call-offset> ::= h <nv-offset> _
+ // ::= v <v-offset> _
+ // <nv-offset> ::= <offset number>
+ // # non-virtual base override
+ // <v-offset> ::= <offset number> _ <virtual offset number>
+ // # virtual base override, with vcall offset
+ private bool ParseCallOffset()
+ {
+ if (ConsumeIf("h"))
+ {
+ return ParseNumber(true).Length == 0 || !ConsumeIf("_");
+ }
+ else if (ConsumeIf("v"))
+ {
+ return ParseNumber(true).Length == 0 || !ConsumeIf("_") || ParseNumber(true).Length == 0 || !ConsumeIf("_");
+ }
+
+ return true;
+ }
+
+
+ // <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier
+ // ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class'
+ // ::= Tu <name> # dependent elaborated type specifier using 'union'
+ // ::= Te <name> # dependent elaborated type specifier using 'enum'
+ private BaseNode ParseClassEnumType()
+ {
+ string ElaboratedType = null;
+
+ if (ConsumeIf("Ts"))
+ {
+ ElaboratedType = "struct";
+ }
+ else if (ConsumeIf("Tu"))
+ {
+ ElaboratedType = "union";
+ }
+ else if (ConsumeIf("Te"))
+ {
+ ElaboratedType = "enum";
+ }
+
+ BaseNode Name = ParseName();
+ if (Name == null)
+ {
+ return null;
+ }
+
+ if (ElaboratedType == null)
+ {
+ return Name;
+ }
+
+ return new ElaboratedType(ElaboratedType, Name);
+ }
+
+ // <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
+ // <bare-function-type> ::= <signature type>+
+ // # types are possible return type, then parameter types
+ // <exception-spec> ::= Do # non-throwing exception-specification (e.g., noexcept, throw())
+ // ::= DO <expression> E # computed (instantiation-dependent) noexcept
+ // ::= Dw <type>+ E # dynamic exception specification with instantiation-dependent types
+ private BaseNode ParseFunctionType()
+ {
+ CV CVQualifiers = ParseCVQualifiers();
+
+ BaseNode ExceptionSpec = null;
+
+ if (ConsumeIf("Do"))
+ {
+ ExceptionSpec = new NameType("noexcept");
+ }
+ else if (ConsumeIf("DO"))
+ {
+ BaseNode Expression = ParseExpression();
+ if (Expression == null || !ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ ExceptionSpec = new NoexceptSpec(Expression);
+ }
+ else if (ConsumeIf("Dw"))
+ {
+ List<BaseNode> Types = new List<BaseNode>();
+
+ while (!ConsumeIf("E"))
+ {
+ BaseNode Type = ParseType();
+ if (Type == null)
+ {
+ return null;
+ }
+
+ Types.Add(Type);
+ }
+
+ ExceptionSpec = new DynamicExceptionSpec(new NodeArray(Types));
+ }
+
+ // We don't need the transaction
+ ConsumeIf("Dx");
+
+ if (!ConsumeIf("F"))
+ {
+ return null;
+ }
+
+ // extern "C"
+ ConsumeIf("Y");
+
+ BaseNode ReturnType = ParseType();
+ if (ReturnType == null)
+ {
+ return null;
+ }
+
+ Reference ReferenceQualifier = Reference.None;
+ List<BaseNode> Params = new List<BaseNode>();
+
+ while (true)
+ {
+ if (ConsumeIf("E"))
+ {
+ break;
+ }
+
+ if (ConsumeIf("v"))
+ {
+ continue;
+ }
+
+ if (ConsumeIf("RE"))
+ {
+ ReferenceQualifier = Reference.LValue;
+ break;
+ }
+ else if (ConsumeIf("OE"))
+ {
+ ReferenceQualifier = Reference.RValue;
+ break;
+ }
+
+ BaseNode Type = ParseType();
+ if (Type == null)
+ {
+ return null;
+ }
+
+ Params.Add(Type);
+ }
+
+ return new FunctionType(ReturnType, new NodeArray(Params), new CVType(CVQualifiers, null), new SimpleReferenceType(ReferenceQualifier, null), ExceptionSpec);
+ }
+
+ // <array-type> ::= A <positive dimension number> _ <element type>
+ // ::= A [<dimension expression>] _ <element type>
+ private BaseNode ParseArrayType()
+ {
+ if (!ConsumeIf("A"))
+ {
+ return null;
+ }
+
+ BaseNode ElementType;
+ if (char.IsDigit(Peek()))
+ {
+ string Dimension = ParseNumber();
+ if (Dimension.Length == 0 || !ConsumeIf("_"))
+ {
+ return null;
+ }
+
+ ElementType = ParseType();
+ if (ElementType == null)
+ {
+ return null;
+ }
+
+ return new ArrayType(ElementType, Dimension);
+ }
+
+ if (!ConsumeIf("_"))
+ {
+ BaseNode DimensionExpression = ParseExpression();
+ if (DimensionExpression == null || !ConsumeIf("_"))
+ {
+ return null;
+ }
+
+ ElementType = ParseType();
+ if (ElementType == null)
+ {
+ return null;
+ }
+
+ return new ArrayType(ElementType, DimensionExpression);
+ }
+
+ ElementType = ParseType();
+ if (ElementType == null)
+ {
+ return null;
+ }
+
+ return new ArrayType(ElementType);
+ }
+
+ // <type> ::= <builtin-type>
+ // ::= <qualified-type> (PARTIAL)
+ // ::= <function-type>
+ // ::= <class-enum-type>
+ // ::= <array-type> (TODO)
+ // ::= <pointer-to-member-type> (TODO)
+ // ::= <template-param>
+ // ::= <template-template-param> <template-args>
+ // ::= <decltype>
+ // ::= P <type> # pointer
+ // ::= R <type> # l-value reference
+ // ::= O <type> # r-value reference (C++11)
+ // ::= C <type> # complex pair (C99)
+ // ::= G <type> # imaginary (C99)
+ // ::= <substitution> # See Compression below
+ private BaseNode ParseType(NameParserContext Context = null)
+ {
+ // Temporary context
+ if (Context == null)
+ {
+ Context = new NameParserContext();
+ }
+
+ BaseNode Result = null;
+ switch (Peek())
+ {
+ case 'r':
+ case 'V':
+ case 'K':
+ int TypePos = 0;
+
+ if (Peek(TypePos) == 'r')
+ {
+ TypePos++;
+ }
+
+ if (Peek(TypePos) == 'V')
+ {
+ TypePos++;
+ }
+
+ if (Peek(TypePos) == 'K')
+ {
+ TypePos++;
+ }
+
+ if (Peek(TypePos) == 'F' || (Peek(TypePos) == 'D' && (Peek(TypePos + 1) == 'o' || Peek(TypePos + 1) == 'O' || Peek(TypePos + 1) == 'w' || Peek(TypePos + 1) == 'x')))
+ {
+ Result = ParseFunctionType();
+ break;
+ }
+
+ CV CV = ParseCVQualifiers();
+
+ Result = ParseType(Context);
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ Result = new CVType(CV, Result);
+ break;
+ case 'U':
+ // TODO: <extended-qualifier>
+ return null;
+ case 'v':
+ Position++;
+ return new NameType("void");
+ case 'w':
+ Position++;
+ return new NameType("wchar_t");
+ case 'b':
+ Position++;
+ return new NameType("bool");
+ case 'c':
+ Position++;
+ return new NameType("char");
+ case 'a':
+ Position++;
+ return new NameType("signed char");
+ case 'h':
+ Position++;
+ return new NameType("unsigned char");
+ case 's':
+ Position++;
+ return new NameType("short");
+ case 't':
+ Position++;
+ return new NameType("unsigned short");
+ case 'i':
+ Position++;
+ return new NameType("int");
+ case 'j':
+ Position++;
+ return new NameType("unsigned int");
+ case 'l':
+ Position++;
+ return new NameType("long");
+ case 'm':
+ Position++;
+ return new NameType("unsigned long");
+ case 'x':
+ Position++;
+ return new NameType("long long");
+ case 'y':
+ Position++;
+ return new NameType("unsigned long long");
+ case 'n':
+ Position++;
+ return new NameType("__int128");
+ case 'o':
+ Position++;
+ return new NameType("unsigned __int128");
+ case 'f':
+ Position++;
+ return new NameType("float");
+ case 'd':
+ Position++;
+ return new NameType("double");
+ case 'e':
+ Position++;
+ return new NameType("long double");
+ case 'g':
+ Position++;
+ return new NameType("__float128");
+ case 'z':
+ Position++;
+ return new NameType("...");
+ case 'u':
+ Position++;
+ return ParseSourceName();
+ case 'D':
+ switch (Peek(1))
+ {
+ case 'd':
+ Position += 2;
+ return new NameType("decimal64");
+ case 'e':
+ Position += 2;
+ return new NameType("decimal128");
+ case 'f':
+ Position += 2;
+ return new NameType("decimal32");
+ case 'h':
+ Position += 2;
+ // FIXME: GNU c++flit returns this but that is not what is supposed to be returned.
+ return new NameType("half");
+ //return new NameType("decimal16");
+ case 'i':
+ Position += 2;
+ return new NameType("char32_t");
+ case 's':
+ Position += 2;
+ return new NameType("char16_t");
+ case 'a':
+ Position += 2;
+ return new NameType("decltype(auto)");
+ case 'n':
+ Position += 2;
+ // FIXME: GNU c++flit returns this but that is not what is supposed to be returned.
+ return new NameType("decltype(nullptr)");
+ //return new NameType("std::nullptr_t");
+ case 't':
+ case 'T':
+ Position += 2;
+ Result = ParseDecltype();
+ break;
+ case 'o':
+ case 'O':
+ case 'w':
+ case 'x':
+ Result = ParseFunctionType();
+ break;
+ default:
+ return null;
+ }
+ break;
+ case 'F':
+ Result = ParseFunctionType();
+ break;
+ case 'A':
+ return ParseArrayType();
+ case 'M':
+ // TODO: <pointer-to-member-type>
+ Position++;
+ return null;
+ case 'T':
+ // might just be a class enum type
+ if (Peek(1) == 's' || Peek(1) == 'u' || Peek(1) == 'e')
+ {
+ Result = ParseClassEnumType();
+ break;
+ }
+
+ Result = ParseTemplateParam();
+ if (Result == null)
+ {
+ return null;
+ }
+
+ if (CanParseTemplateArgs && Peek() == 'I')
+ {
+ BaseNode TemplateArguments = ParseTemplateArguments();
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ Result = new NameTypeWithTemplateArguments(Result, TemplateArguments);
+ }
+ break;
+ case 'P':
+ Position++;
+ Result = ParseType(Context);
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ Result = new PointerType(Result);
+ break;
+ case 'R':
+ Position++;
+ Result = ParseType(Context);
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ Result = new ReferenceType("&", Result);
+ break;
+ case 'O':
+ Position++;
+ Result = ParseType(Context);
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ Result = new ReferenceType("&&", Result);
+ break;
+ case 'C':
+ Position++;
+ Result = ParseType(Context);
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ Result = new PostfixQualifiedType(" complex", Result);
+ break;
+ case 'G':
+ Position++;
+ Result = ParseType(Context);
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ Result = new PostfixQualifiedType(" imaginary", Result);
+ break;
+ case 'S':
+ if (Peek(1) != 't')
+ {
+ BaseNode Substitution = ParseSubstitution();
+ if (Substitution == null)
+ {
+ return null;
+ }
+
+ if (CanParseTemplateArgs && Peek() == 'I')
+ {
+ BaseNode TemplateArgument = ParseTemplateArgument();
+ if (TemplateArgument == null)
+ {
+ return null;
+ }
+
+ Result = new NameTypeWithTemplateArguments(Substitution, TemplateArgument);
+ break;
+ }
+ return Substitution;
+ }
+ else
+ {
+ Result = ParseClassEnumType();
+ break;
+ }
+ default:
+ Result = ParseClassEnumType();
+ break;
+ }
+ if (Result != null)
+ {
+ SubstitutionList.Add(Result);
+ }
+
+ return Result;
+ }
+
+ // <special-name> ::= TV <type> # virtual table
+ // ::= TT <type> # VTT structure (construction vtable index)
+ // ::= TI <type> # typeinfo structure
+ // ::= TS <type> # typeinfo name (null-terminated byte string)
+ // ::= Tc <call-offset> <call-offset> <base encoding>
+ // ::= TW <object name> # Thread-local wrapper
+ // ::= TH <object name> # Thread-local initialization
+ // ::= T <call-offset> <base encoding>
+ // # base is the nominal target function of thunk
+ // ::= GV <object name> # Guard variable for one-time initialization
+ private BaseNode ParseSpecialName(NameParserContext Context = null)
+ {
+ if (Peek() != 'T')
+ {
+ if (ConsumeIf("GV"))
+ {
+ BaseNode Name = ParseName();
+ if (Name == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("guard variable for ", Name);
+ }
+ return null;
+ }
+
+ BaseNode Node;
+ switch (Peek(1))
+ {
+ // ::= TV <type> # virtual table
+ case 'V':
+ Position += 2;
+ Node = ParseType(Context);
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("vtable for ", Node);
+ // ::= TT <type> # VTT structure (construction vtable index)
+ case 'T':
+ Position += 2;
+ Node = ParseType(Context);
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("VTT for ", Node);
+ // ::= TI <type> # typeinfo structure
+ case 'I':
+ Position += 2;
+ Node = ParseType(Context);
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("typeinfo for ", Node);
+ // ::= TS <type> # typeinfo name (null-terminated byte string)
+ case 'S':
+ Position += 2;
+ Node = ParseType(Context);
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("typeinfo name for ", Node);
+ // ::= Tc <call-offset> <call-offset> <base encoding>
+ case 'c':
+ Position += 2;
+ if (ParseCallOffset() || ParseCallOffset())
+ {
+ return null;
+ }
+
+ Node = ParseEncoding();
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("covariant return thunk to ", Node);
+ // extension ::= TC <first type> <number> _ <second type>
+ case 'C':
+ Position += 2;
+ BaseNode FirstType = ParseType();
+ if (FirstType == null || ParseNumber(true).Length == 0 || !ConsumeIf("_"))
+ {
+ return null;
+ }
+
+ BaseNode SecondType = ParseType();
+
+ return new CtorVtableSpecialName(SecondType, FirstType);
+ // ::= TH <object name> # Thread-local initialization
+ case 'H':
+ Position += 2;
+ Node = ParseName();
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("thread-local initialization routine for ", Node);
+ // ::= TW <object name> # Thread-local wrapper
+ case 'W':
+ Position += 2;
+ Node = ParseName();
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new SpecialName("thread-local wrapper routine for ", Node);
+ default:
+ Position++;
+ bool IsVirtual = Peek() == 'v';
+ if (ParseCallOffset())
+ {
+ return null;
+ }
+
+ Node = ParseEncoding();
+ if (Node == null)
+ {
+ return null;
+ }
+
+ if (IsVirtual)
+ {
+ return new SpecialName("virtual thunk to ", Node);
+ }
+
+ return new SpecialName("non-virtual thunk to ", Node);
+ }
+ }
+
+ // <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const
+ private CV ParseCVQualifiers()
+ {
+ CV Qualifiers = CV.None;
+
+ if (ConsumeIf("r"))
+ {
+ Qualifiers |= CV.Restricted;
+ }
+ if (ConsumeIf("V"))
+ {
+ Qualifiers |= CV.Volatile;
+ }
+ if (ConsumeIf("K"))
+ {
+ Qualifiers |= CV.Const;
+ }
+
+ return Qualifiers;
+ }
+
+
+ // <ref-qualifier> ::= R # & ref-qualifier
+ // <ref-qualifier> ::= O # && ref-qualifier
+ private SimpleReferenceType ParseRefQualifiers()
+ {
+ Reference Result = Reference.None;
+ if (ConsumeIf("O"))
+ {
+ Result = Reference.RValue;
+ }
+ else if (ConsumeIf("R"))
+ {
+ Result = Reference.LValue;
+ }
+ return new SimpleReferenceType(Result, null);
+ }
+
+ private BaseNode CreateNameNode(BaseNode Prev, BaseNode Name, NameParserContext Context)
+ {
+ BaseNode Result = Name;
+ if (Prev != null)
+ {
+ Result = new NestedName(Name, Prev);
+ }
+
+ if (Context != null)
+ {
+ Context.FinishWithTemplateArguments = false;
+ }
+
+ return Result;
+ }
+
+ private int ParsePositiveNumber()
+ {
+ string Part = Mangled.Substring(Position);
+ int NumberLength = 0;
+
+ for (; NumberLength < Part.Length; NumberLength++)
+ {
+ if (!char.IsDigit(Part[NumberLength]))
+ {
+ break;
+ }
+ }
+
+ Position += NumberLength;
+
+ if (NumberLength == 0)
+ {
+ return -1;
+ }
+
+ return int.Parse(Part.Substring(0, NumberLength));
+ }
+
+ private string ParseNumber(bool IsSigned = false)
+ {
+ if (IsSigned)
+ {
+ ConsumeIf("n");
+ }
+
+ if (Count() == 0 || !char.IsDigit(Mangled[Position]))
+ {
+ return null;
+ }
+
+ string Part = Mangled.Substring(Position);
+ int NumberLength = 0;
+
+ for (; NumberLength < Part.Length; NumberLength++)
+ {
+ if (!char.IsDigit(Part[NumberLength]))
+ {
+ break;
+ }
+ }
+
+ Position += NumberLength;
+
+ return Part.Substring(0, NumberLength);
+ }
+
+ // <source-name> ::= <positive length number> <identifier>
+ private BaseNode ParseSourceName()
+ {
+ int Length = ParsePositiveNumber();
+ if (Count() < Length || Length <= 0)
+ {
+ return null;
+ }
+
+ string Name = Mangled.Substring(Position, Length);
+ Position += Length;
+ if (Name.StartsWith("_GLOBAL__N"))
+ {
+ return new NameType("(anonymous namespace)");
+ }
+
+ return new NameType(Name);
+ }
+
+ // <operator-name> ::= nw # new
+ // ::= na # new[]
+ // ::= dl # delete
+ // ::= da # delete[]
+ // ::= ps # + (unary)
+ // ::= ng # - (unary)
+ // ::= ad # & (unary)
+ // ::= de # * (unary)
+ // ::= co # ~
+ // ::= pl # +
+ // ::= mi # -
+ // ::= ml # *
+ // ::= dv # /
+ // ::= rm # %
+ // ::= an # &
+ // ::= or # |
+ // ::= eo # ^
+ // ::= aS # =
+ // ::= pL # +=
+ // ::= mI # -=
+ // ::= mL # *=
+ // ::= dV # /=
+ // ::= rM # %=
+ // ::= aN # &=
+ // ::= oR # |=
+ // ::= eO # ^=
+ // ::= ls # <<
+ // ::= rs # >>
+ // ::= lS # <<=
+ // ::= rS # >>=
+ // ::= eq # ==
+ // ::= ne # !=
+ // ::= lt # <
+ // ::= gt # >
+ // ::= le # <=
+ // ::= ge # >=
+ // ::= ss # <=>
+ // ::= nt # !
+ // ::= aa # &&
+ // ::= oo # ||
+ // ::= pp # ++ (postfix in <expression> context)
+ // ::= mm # -- (postfix in <expression> context)
+ // ::= cm # ,
+ // ::= pm # ->*
+ // ::= pt # ->
+ // ::= cl # ()
+ // ::= ix # []
+ // ::= qu # ?
+ // ::= cv <type> # (cast) (TODO)
+ // ::= li <source-name> # operator ""
+ // ::= v <digit> <source-name> # vendor extended operator (TODO)
+ private BaseNode ParseOperatorName(NameParserContext Context)
+ {
+ switch (Peek())
+ {
+ case 'a':
+ switch (Peek(1))
+ {
+ case 'a':
+ Position += 2;
+ return new NameType("operator&&");
+ case 'd':
+ case 'n':
+ Position += 2;
+ return new NameType("operator&");
+ case 'N':
+ Position += 2;
+ return new NameType("operator&=");
+ case 'S':
+ Position += 2;
+ return new NameType("operator=");
+ default:
+ return null;
+ }
+ case 'c':
+ switch (Peek(1))
+ {
+ case 'l':
+ Position += 2;
+ return new NameType("operator()");
+ case 'm':
+ Position += 2;
+ return new NameType("operator,");
+ case 'o':
+ Position += 2;
+ return new NameType("operator~");
+ case 'v':
+ Position += 2;
+
+ bool CanParseTemplateArgsBackup = CanParseTemplateArgs;
+ bool CanForwardTemplateReferenceBackup = CanForwardTemplateReference;
+
+ CanParseTemplateArgs = false;
+ CanForwardTemplateReference = CanForwardTemplateReferenceBackup || Context != null;
+
+ BaseNode Type = ParseType();
+
+ CanParseTemplateArgs = CanParseTemplateArgsBackup;
+ CanForwardTemplateReference = CanForwardTemplateReferenceBackup;
+
+ if (Type == null)
+ {
+ return null;
+ }
+
+ if (Context != null)
+ {
+ Context.CtorDtorConversion = true;
+ }
+
+ return new ConversionOperatorType(Type);
+ default:
+ return null;
+ }
+ case 'd':
+ switch (Peek(1))
+ {
+ case 'a':
+ Position += 2;
+ return new NameType("operator delete[]");
+ case 'e':
+ Position += 2;
+ return new NameType("operator*");
+ case 'l':
+ Position += 2;
+ return new NameType("operator delete");
+ case 'v':
+ Position += 2;
+ return new NameType("operator/");
+ case 'V':
+ Position += 2;
+ return new NameType("operator/=");
+ default:
+ return null;
+ }
+ case 'e':
+ switch (Peek(1))
+ {
+ case 'o':
+ Position += 2;
+ return new NameType("operator^");
+ case 'O':
+ Position += 2;
+ return new NameType("operator^=");
+ case 'q':
+ Position += 2;
+ return new NameType("operator==");
+ default:
+ return null;
+ }
+ case 'g':
+ switch (Peek(1))
+ {
+ case 'e':
+ Position += 2;
+ return new NameType("operator>=");
+ case 't':
+ Position += 2;
+ return new NameType("operator>");
+ default:
+ return null;
+ }
+ case 'i':
+ if (Peek(1) == 'x')
+ {
+ Position += 2;
+ return new NameType("operator[]");
+ }
+ return null;
+ case 'l':
+ switch (Peek(1))
+ {
+ case 'e':
+ Position += 2;
+ return new NameType("operator<=");
+ case 'i':
+ Position += 2;
+ BaseNode SourceName = ParseSourceName();
+ if (SourceName == null)
+ {
+ return null;
+ }
+
+ return new LiteralOperator(SourceName);
+ case 's':
+ Position += 2;
+ return new NameType("operator<<");
+ case 'S':
+ Position += 2;
+ return new NameType("operator<<=");
+ case 't':
+ Position += 2;
+ return new NameType("operator<");
+ default:
+ return null;
+ }
+ case 'm':
+ switch (Peek(1))
+ {
+ case 'i':
+ Position += 2;
+ return new NameType("operator-");
+ case 'I':
+ Position += 2;
+ return new NameType("operator-=");
+ case 'l':
+ Position += 2;
+ return new NameType("operator*");
+ case 'L':
+ Position += 2;
+ return new NameType("operator*=");
+ case 'm':
+ Position += 2;
+ return new NameType("operator--");
+ default:
+ return null;
+ }
+ case 'n':
+ switch (Peek(1))
+ {
+ case 'a':
+ Position += 2;
+ return new NameType("operator new[]");
+ case 'e':
+ Position += 2;
+ return new NameType("operator!=");
+ case 'g':
+ Position += 2;
+ return new NameType("operator-");
+ case 't':
+ Position += 2;
+ return new NameType("operator!");
+ case 'w':
+ Position += 2;
+ return new NameType("operator new");
+ default:
+ return null;
+ }
+ case 'o':
+ switch (Peek(1))
+ {
+ case 'o':
+ Position += 2;
+ return new NameType("operator||");
+ case 'r':
+ Position += 2;
+ return new NameType("operator|");
+ case 'R':
+ Position += 2;
+ return new NameType("operator|=");
+ default:
+ return null;
+ }
+ case 'p':
+ switch (Peek(1))
+ {
+ case 'm':
+ Position += 2;
+ return new NameType("operator->*");
+ case 's':
+ case 'l':
+ Position += 2;
+ return new NameType("operator+");
+ case 'L':
+ Position += 2;
+ return new NameType("operator+=");
+ case 'p':
+ Position += 2;
+ return new NameType("operator++");
+ case 't':
+ Position += 2;
+ return new NameType("operator->");
+ default:
+ return null;
+ }
+ case 'q':
+ if (Peek(1) == 'u')
+ {
+ Position += 2;
+ return new NameType("operator?");
+ }
+ return null;
+ case 'r':
+ switch (Peek(1))
+ {
+ case 'm':
+ Position += 2;
+ return new NameType("operator%");
+ case 'M':
+ Position += 2;
+ return new NameType("operator%=");
+ case 's':
+ Position += 2;
+ return new NameType("operator>>");
+ case 'S':
+ Position += 2;
+ return new NameType("operator>>=");
+ default:
+ return null;
+ }
+ case 's':
+ if (Peek(1) == 's')
+ {
+ Position += 2;
+ return new NameType("operator<=>");
+ }
+ return null;
+ case 'v':
+ // TODO: ::= v <digit> <source-name> # vendor extended operator
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ // <unqualified-name> ::= <operator-name> [<abi-tags> (TODO)]
+ // ::= <ctor-dtor-name> (TODO)
+ // ::= <source-name>
+ // ::= <unnamed-type-name> (TODO)
+ // ::= DC <source-name>+ E # structured binding declaration (TODO)
+ private BaseNode ParseUnqualifiedName(NameParserContext Context)
+ {
+ BaseNode Result = null;
+ char C = Peek();
+ if (C == 'U')
+ {
+ // TODO: Unnamed Type Name
+ // throw new Exception("Unnamed Type Name not implemented");
+ }
+ else if (char.IsDigit(C))
+ {
+ Result = ParseSourceName();
+ }
+ else if (ConsumeIf("DC"))
+ {
+ // TODO: Structured Binding Declaration
+ // throw new Exception("Structured Binding Declaration not implemented");
+ }
+ else
+ {
+ Result = ParseOperatorName(Context);
+ }
+
+ if (Result != null)
+ {
+ // TODO: ABI Tags
+ //throw new Exception("ABI Tags not implemented");
+ }
+ return Result;
+ }
+
+ // <ctor-dtor-name> ::= C1 # complete object constructor
+ // ::= C2 # base object constructor
+ // ::= C3 # complete object allocating constructor
+ // ::= D0 # deleting destructor
+ // ::= D1 # complete object destructor
+ // ::= D2 # base object destructor
+ private BaseNode ParseCtorDtorName(NameParserContext Context, BaseNode Prev)
+ {
+ if (Prev.Type == NodeType.SpecialSubstitution && Prev is SpecialSubstitution)
+ {
+ ((SpecialSubstitution)Prev).SetExtended();
+ }
+
+ if (ConsumeIf("C"))
+ {
+ bool IsInherited = ConsumeIf("I");
+
+ char CtorDtorType = Peek();
+ if (CtorDtorType != '1' && CtorDtorType != '2' && CtorDtorType != '3')
+ {
+ return null;
+ }
+
+ Position++;
+
+ if (Context != null)
+ {
+ Context.CtorDtorConversion = true;
+ }
+
+ if (IsInherited && ParseName(Context) == null)
+ {
+ return null;
+ }
+
+ return new CtorDtorNameType(Prev, false);
+ }
+
+ if (ConsumeIf("D"))
+ {
+ char C = Peek();
+ if (C != '0' && C != '1' && C != '2')
+ {
+ return null;
+ }
+
+ Position++;
+
+ if (Context != null)
+ {
+ Context.CtorDtorConversion = true;
+ }
+
+ return new CtorDtorNameType(Prev, true);
+ }
+
+ return null;
+ }
+
+ // <function-param> ::= fp <top-level CV-qualifiers> _ # L == 0, first parameter
+ // ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters
+ // ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _ # L > 0, first parameter
+ // ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters
+ private BaseNode ParseFunctionParameter()
+ {
+ if (ConsumeIf("fp"))
+ {
+ // ignored
+ ParseCVQualifiers();
+
+ if (!ConsumeIf("_"))
+ {
+ return null;
+ }
+
+ return new FunctionParameter(ParseNumber());
+ }
+ else if (ConsumeIf("fL"))
+ {
+ string L1Number = ParseNumber();
+ if (L1Number == null || L1Number.Length == 0)
+ {
+ return null;
+ }
+
+ if (!ConsumeIf("p"))
+ {
+ return null;
+ }
+
+ // ignored
+ ParseCVQualifiers();
+
+ if (!ConsumeIf("_"))
+ {
+ return null;
+ }
+
+ return new FunctionParameter(ParseNumber());
+ }
+
+ return null;
+ }
+
+ // <fold-expr> ::= fL <binary-operator-name> <expression> <expression>
+ // ::= fR <binary-operator-name> <expression> <expression>
+ // ::= fl <binary-operator-name> <expression>
+ // ::= fr <binary-operator-name> <expression>
+ private BaseNode ParseFoldExpression()
+ {
+ if (!ConsumeIf("f"))
+ {
+ return null;
+ }
+
+ char FoldKind = Peek();
+ bool HasInitializer = FoldKind == 'L' || FoldKind == 'R';
+ bool IsLeftFold = FoldKind == 'l' || FoldKind == 'L';
+
+ if (!IsLeftFold && !(FoldKind == 'r' || FoldKind == 'R'))
+ {
+ return null;
+ }
+
+ Position++;
+
+ string OperatorName = null;
+
+ switch (PeekString(0, 2))
+ {
+ case "aa":
+ OperatorName = "&&";
+ break;
+ case "an":
+ OperatorName = "&";
+ break;
+ case "aN":
+ OperatorName = "&=";
+ break;
+ case "aS":
+ OperatorName = "=";
+ break;
+ case "cm":
+ OperatorName = ",";
+ break;
+ case "ds":
+ OperatorName = ".*";
+ break;
+ case "dv":
+ OperatorName = "/";
+ break;
+ case "dV":
+ OperatorName = "/=";
+ break;
+ case "eo":
+ OperatorName = "^";
+ break;
+ case "eO":
+ OperatorName = "^=";
+ break;
+ case "eq":
+ OperatorName = "==";
+ break;
+ case "ge":
+ OperatorName = ">=";
+ break;
+ case "gt":
+ OperatorName = ">";
+ break;
+ case "le":
+ OperatorName = "<=";
+ break;
+ case "ls":
+ OperatorName = "<<";
+ break;
+ case "lS":
+ OperatorName = "<<=";
+ break;
+ case "lt":
+ OperatorName = "<";
+ break;
+ case "mi":
+ OperatorName = "-";
+ break;
+ case "mI":
+ OperatorName = "-=";
+ break;
+ case "ml":
+ OperatorName = "*";
+ break;
+ case "mL":
+ OperatorName = "*=";
+ break;
+ case "ne":
+ OperatorName = "!=";
+ break;
+ case "oo":
+ OperatorName = "||";
+ break;
+ case "or":
+ OperatorName = "|";
+ break;
+ case "oR":
+ OperatorName = "|=";
+ break;
+ case "pl":
+ OperatorName = "+";
+ break;
+ case "pL":
+ OperatorName = "+=";
+ break;
+ case "rm":
+ OperatorName = "%";
+ break;
+ case "rM":
+ OperatorName = "%=";
+ break;
+ case "rs":
+ OperatorName = ">>";
+ break;
+ case "rS":
+ OperatorName = ">>=";
+ break;
+ default:
+ return null;
+ }
+
+ Position += 2;
+
+ BaseNode Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ BaseNode Initializer = null;
+
+ if (HasInitializer)
+ {
+ Initializer = ParseExpression();
+ if (Initializer == null)
+ {
+ return null;
+ }
+ }
+
+ if (IsLeftFold && Initializer != null)
+ {
+ BaseNode Temp = Expression;
+ Expression = Initializer;
+ Initializer = Temp;
+ }
+
+ return new FoldExpression(IsLeftFold, OperatorName, new PackedTemplateParameterExpansion(Expression), Initializer);
+ }
+
+
+ // ::= cv <type> <expression> # type (expression), conversion with one argument
+ // ::= cv <type> _ <expression>* E # type (expr-list), conversion with other than one argument
+ private BaseNode ParseConversionExpression()
+ {
+ if (!ConsumeIf("cv"))
+ {
+ return null;
+ }
+
+ bool CanParseTemplateArgsBackup = CanParseTemplateArgs;
+ CanParseTemplateArgs = false;
+ BaseNode Type = ParseType();
+ CanParseTemplateArgs = CanParseTemplateArgsBackup;
+
+ if (Type == null)
+ {
+ return null;
+ }
+
+ List<BaseNode> Expressions = new List<BaseNode>();
+ if (ConsumeIf("_"))
+ {
+ while (!ConsumeIf("E"))
+ {
+ BaseNode Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ Expressions.Add(Expression);
+ }
+ }
+ else
+ {
+ BaseNode Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ Expressions.Add(Expression);
+ }
+
+ return new ConversionExpression(Type, new NodeArray(Expressions));
+ }
+
+ private BaseNode ParseBinaryExpression(string Name)
+ {
+ BaseNode LeftPart = ParseExpression();
+ if (LeftPart == null)
+ {
+ return null;
+ }
+
+ BaseNode RightPart = ParseExpression();
+ if (RightPart == null)
+ {
+ return null;
+ }
+
+ return new BinaryExpression(LeftPart, Name, RightPart);
+ }
+
+ private BaseNode ParsePrefixExpression(string Name)
+ {
+ BaseNode Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new PrefixExpression(Name, Expression);
+ }
+
+
+ // <braced-expression> ::= <expression>
+ // ::= di <field source-name> <braced-expression> # .name = expr
+ // ::= dx <index expression> <braced-expression> # [expr] = expr
+ // ::= dX <range begin expression> <range end expression> <braced-expression>
+ // # [expr ... expr] = expr
+ private BaseNode ParseBracedExpression()
+ {
+ if (Peek() == 'd')
+ {
+ BaseNode BracedExpressionNode;
+ switch (Peek(1))
+ {
+ case 'i':
+ Position += 2;
+ BaseNode Field = ParseSourceName();
+ if (Field == null)
+ {
+ return null;
+ }
+
+ BracedExpressionNode = ParseBracedExpression();
+ if (BracedExpressionNode == null)
+ {
+ return null;
+ }
+
+ return new BracedExpression(Field, BracedExpressionNode, false);
+ case 'x':
+ Position += 2;
+ BaseNode Index = ParseExpression();
+ if (Index == null)
+ {
+ return null;
+ }
+
+ BracedExpressionNode = ParseBracedExpression();
+ if (BracedExpressionNode == null)
+ {
+ return null;
+ }
+
+ return new BracedExpression(Index, BracedExpressionNode, true);
+ case 'X':
+ Position += 2;
+ BaseNode RangeBeginExpression = ParseExpression();
+ if (RangeBeginExpression == null)
+ {
+ return null;
+ }
+
+ BaseNode RangeEndExpression = ParseExpression();
+ if (RangeEndExpression == null)
+ {
+ return null;
+ }
+
+ BracedExpressionNode = ParseBracedExpression();
+ if (BracedExpressionNode == null)
+ {
+ return null;
+ }
+
+ return new BracedRangeExpression(RangeBeginExpression, RangeEndExpression, BracedExpressionNode);
+ }
+ }
+
+ return ParseExpression();
+ }
+
+ // ::= [gs] nw <expression>* _ <type> E # new (expr-list) type
+ // ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
+ // ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type
+ // ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
+ //
+ // <initializer> ::= pi <expression>* E # parenthesized initialization
+ private BaseNode ParseNewExpression()
+ {
+ bool IsGlobal = ConsumeIf("gs");
+ bool IsArray = Peek(1) == 'a';
+
+ if (!ConsumeIf("nw") || !ConsumeIf("na"))
+ {
+ return null;
+ }
+
+ List<BaseNode> Expressions = new List<BaseNode>();
+ List<BaseNode> Initializers = new List<BaseNode>();
+
+ while (!ConsumeIf("_"))
+ {
+ BaseNode Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ Expressions.Add(Expression);
+ }
+
+ BaseNode TypeNode = ParseType();
+ if (TypeNode == null)
+ {
+ return null;
+ }
+
+ if (ConsumeIf("pi"))
+ {
+ while (!ConsumeIf("E"))
+ {
+ BaseNode Initializer = ParseExpression();
+ if (Initializer == null)
+ {
+ return null;
+ }
+
+ Initializers.Add(Initializer);
+ }
+ }
+ else if (!ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ return new NewExpression(new NodeArray(Expressions), TypeNode, new NodeArray(Initializers), IsGlobal, IsArray);
+ }
+
+
+ // <expression> ::= <unary operator-name> <expression>
+ // ::= <binary operator-name> <expression> <expression>
+ // ::= <ternary operator-name> <expression> <expression> <expression>
+ // ::= pp_ <expression> # prefix ++
+ // ::= mm_ <expression> # prefix --
+ // ::= cl <expression>+ E # expression (expr-list), call
+ // ::= cv <type> <expression> # type (expression), conversion with one argument
+ // ::= cv <type> _ <expression>* E # type (expr-list), conversion with other than one argument
+ // ::= tl <type> <braced-expression>* E # type {expr-list}, conversion with braced-init-list argument
+ // ::= il <braced-expression>* E # {expr-list}, braced-init-list in any other context
+ // ::= [gs] nw <expression>* _ <type> E # new (expr-list) type
+ // ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
+ // ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type
+ // ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
+ // ::= [gs] dl <expression> # delete expression
+ // ::= [gs] da <expression> # delete[] expression
+ // ::= dc <type> <expression> # dynamic_cast<type> (expression)
+ // ::= sc <type> <expression> # static_cast<type> (expression)
+ // ::= cc <type> <expression> # const_cast<type> (expression)
+ // ::= rc <type> <expression> # reinterpret_cast<type> (expression)
+ // ::= ti <type> # typeid (type)
+ // ::= te <expression> # typeid (expression)
+ // ::= st <type> # sizeof (type)
+ // ::= sz <expression> # sizeof (expression)
+ // ::= at <type> # alignof (type)
+ // ::= az <expression> # alignof (expression)
+ // ::= nx <expression> # noexcept (expression)
+ // ::= <template-param>
+ // ::= <function-param>
+ // ::= dt <expression> <unresolved-name> # expr.name
+ // ::= pt <expression> <unresolved-name> # expr->name
+ // ::= ds <expression> <expression> # expr.*expr
+ // ::= sZ <template-param> # sizeof...(T), size of a template parameter pack
+ // ::= sZ <function-param> # sizeof...(parameter), size of a function parameter pack
+ // ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template
+ // ::= sp <expression> # expression..., pack expansion
+ // ::= tw <expression> # throw expression
+ // ::= tr # throw with no operand (rethrow)
+ // ::= <unresolved-name> # f(p), N::f(p), ::f(p),
+ // # freestanding dependent name (e.g., T::x),
+ // # objectless nonstatic member reference
+ // ::= <expr-primary>
+ private BaseNode ParseExpression()
+ {
+ bool IsGlobal = ConsumeIf("gs");
+ BaseNode Expression = null;
+ if (Count() < 2)
+ {
+ return null;
+ }
+
+ switch (Peek())
+ {
+ case 'L':
+ return ParseExpressionPrimary();
+ case 'T':
+ return ParseTemplateParam();
+ case 'f':
+ char C = Peek(1);
+ if (C == 'p' || (C == 'L' && char.IsDigit(Peek(2))))
+ {
+ return ParseFunctionParameter();
+ }
+
+ return ParseFoldExpression();
+ case 'a':
+ switch (Peek(1))
+ {
+ case 'a':
+ Position += 2;
+ return ParseBinaryExpression("&&");
+ case 'd':
+ case 'n':
+ Position += 2;
+ return ParseBinaryExpression("&");
+ case 'N':
+ Position += 2;
+ return ParseBinaryExpression("&=");
+ case 'S':
+ Position += 2;
+ return ParseBinaryExpression("=");
+ case 't':
+ Position += 2;
+ BaseNode Type = ParseType();
+ if (Type == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("alignof (", Type, ")");
+ case 'z':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("alignof (", Expression, ")");
+ }
+ return null;
+ case 'c':
+ switch (Peek(1))
+ {
+ case 'c':
+ Position += 2;
+ BaseNode To = ParseType();
+ if (To == null)
+ {
+ return null;
+ }
+
+ BaseNode From = ParseExpression();
+ if (From == null)
+ {
+ return null;
+ }
+
+ return new CastExpression("const_cast", To, From);
+ case 'l':
+ Position += 2;
+ BaseNode Callee = ParseExpression();
+ if (Callee == null)
+ {
+ return null;
+ }
+
+ List<BaseNode> Names = new List<BaseNode>();
+ while (!ConsumeIf("E"))
+ {
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ Names.Add(Expression);
+ }
+ return new CallExpression(Callee, Names);
+ case 'm':
+ Position += 2;
+ return ParseBinaryExpression(",");
+ case 'o':
+ Position += 2;
+ return ParsePrefixExpression("~");
+ case 'v':
+ return ParseConversionExpression();
+ }
+ return null;
+ case 'd':
+ BaseNode LeftNode = null;
+ BaseNode RightNode = null;
+ switch (Peek(1))
+ {
+ case 'a':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return Expression;
+ }
+
+ return new DeleteExpression(Expression, IsGlobal, true);
+ case 'c':
+ Position += 2;
+ BaseNode Type = ParseType();
+ if (Type == null)
+ {
+ return null;
+ }
+
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return Expression;
+ }
+
+ return new CastExpression("dynamic_cast", Type, Expression);
+ case 'e':
+ Position += 2;
+ return ParsePrefixExpression("*");
+ case 'l':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new DeleteExpression(Expression, IsGlobal, false);
+ case 'n':
+ return ParseUnresolvedName();
+ case 's':
+ Position += 2;
+ LeftNode = ParseExpression();
+ if (LeftNode == null)
+ {
+ return null;
+ }
+
+ RightNode = ParseExpression();
+ if (RightNode == null)
+ {
+ return null;
+ }
+
+ return new MemberExpression(LeftNode, ".*", RightNode);
+ case 't':
+ Position += 2;
+ LeftNode = ParseExpression();
+ if (LeftNode == null)
+ {
+ return null;
+ }
+
+ RightNode = ParseExpression();
+ if (RightNode == null)
+ {
+ return null;
+ }
+
+ return new MemberExpression(LeftNode, ".", RightNode);
+ case 'v':
+ Position += 2;
+ return ParseBinaryExpression("/");
+ case 'V':
+ Position += 2;
+ return ParseBinaryExpression("/=");
+ }
+ return null;
+ case 'e':
+ switch (Peek(1))
+ {
+ case 'o':
+ Position += 2;
+ return ParseBinaryExpression("^");
+ case 'O':
+ Position += 2;
+ return ParseBinaryExpression("^=");
+ case 'q':
+ Position += 2;
+ return ParseBinaryExpression("==");
+ }
+ return null;
+ case 'g':
+ switch (Peek(1))
+ {
+ case 'e':
+ Position += 2;
+ return ParseBinaryExpression(">=");
+ case 't':
+ Position += 2;
+ return ParseBinaryExpression(">");
+ }
+ return null;
+ case 'i':
+ switch (Peek(1))
+ {
+ case 'x':
+ Position += 2;
+ BaseNode Base = ParseExpression();
+ if (Base == null)
+ {
+ return null;
+ }
+
+ BaseNode Subscript = ParseExpression();
+ if (Base == null)
+ {
+ return null;
+ }
+
+ return new ArraySubscriptingExpression(Base, Subscript);
+ case 'l':
+ Position += 2;
+
+ List<BaseNode> BracedExpressions = new List<BaseNode>();
+ while (!ConsumeIf("E"))
+ {
+ Expression = ParseBracedExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ BracedExpressions.Add(Expression);
+ }
+ return new InitListExpression(null, BracedExpressions);
+ }
+ return null;
+ case 'l':
+ switch (Peek(1))
+ {
+ case 'e':
+ Position += 2;
+ return ParseBinaryExpression("<=");
+ case 's':
+ Position += 2;
+ return ParseBinaryExpression("<<");
+ case 'S':
+ Position += 2;
+ return ParseBinaryExpression("<<=");
+ case 't':
+ Position += 2;
+ return ParseBinaryExpression("<");
+ }
+ return null;
+ case 'm':
+ switch (Peek(1))
+ {
+ case 'i':
+ Position += 2;
+ return ParseBinaryExpression("-");
+ case 'I':
+ Position += 2;
+ return ParseBinaryExpression("-=");
+ case 'l':
+ Position += 2;
+ return ParseBinaryExpression("*");
+ case 'L':
+ Position += 2;
+ return ParseBinaryExpression("*=");
+ case 'm':
+ Position += 2;
+ if (ConsumeIf("_"))
+ {
+ return ParsePrefixExpression("--");
+ }
+
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new PostfixExpression(Expression, "--");
+ }
+ return null;
+ case 'n':
+ switch (Peek(1))
+ {
+ case 'a':
+ case 'w':
+ Position += 2;
+ return ParseNewExpression();
+ case 'e':
+ Position += 2;
+ return ParseBinaryExpression("!=");
+ case 'g':
+ Position += 2;
+ return ParsePrefixExpression("-");
+ case 't':
+ Position += 2;
+ return ParsePrefixExpression("!");
+ case 'x':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("noexcept (", Expression, ")");
+ }
+ return null;
+ case 'o':
+ switch (Peek(1))
+ {
+ case 'n':
+ return ParseUnresolvedName();
+ case 'o':
+ Position += 2;
+ return ParseBinaryExpression("||");
+ case 'r':
+ Position += 2;
+ return ParseBinaryExpression("|");
+ case 'R':
+ Position += 2;
+ return ParseBinaryExpression("|=");
+ }
+ return null;
+ case 'p':
+ switch (Peek(1))
+ {
+ case 'm':
+ Position += 2;
+ return ParseBinaryExpression("->*");
+ case 'l':
+ case 's':
+ Position += 2;
+ return ParseBinaryExpression("+");
+ case 'L':
+ Position += 2;
+ return ParseBinaryExpression("+=");
+ case 'p':
+ Position += 2;
+ if (ConsumeIf("_"))
+ {
+ return ParsePrefixExpression("++");
+ }
+
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new PostfixExpression(Expression, "++");
+ case 't':
+ Position += 2;
+ LeftNode = ParseExpression();
+ if (LeftNode == null)
+ {
+ return null;
+ }
+
+ RightNode = ParseExpression();
+ if (RightNode == null)
+ {
+ return null;
+ }
+
+ return new MemberExpression(LeftNode, "->", RightNode);
+ }
+ return null;
+ case 'q':
+ if (Peek(1) == 'u')
+ {
+ Position += 2;
+ BaseNode Condition = ParseExpression();
+ if (Condition == null)
+ {
+ return null;
+ }
+
+ LeftNode = ParseExpression();
+ if (LeftNode == null)
+ {
+ return null;
+ }
+
+ RightNode = ParseExpression();
+ if (RightNode == null)
+ {
+ return null;
+ }
+
+ return new ConditionalExpression(Condition, LeftNode, RightNode);
+ }
+ return null;
+ case 'r':
+ switch (Peek(1))
+ {
+ case 'c':
+ Position += 2;
+ BaseNode To = ParseType();
+ if (To == null)
+ {
+ return null;
+ }
+
+ BaseNode From = ParseExpression();
+ if (From == null)
+ {
+ return null;
+ }
+
+ return new CastExpression("reinterpret_cast", To, From);
+ case 'm':
+ Position += 2;
+ return ParseBinaryExpression("%");
+ case 'M':
+ Position += 2;
+ return ParseBinaryExpression("%");
+ case 's':
+ Position += 2;
+ return ParseBinaryExpression(">>");
+ case 'S':
+ Position += 2;
+ return ParseBinaryExpression(">>=");
+ }
+ return null;
+ case 's':
+ switch (Peek(1))
+ {
+ case 'c':
+ Position += 2;
+ BaseNode To = ParseType();
+ if (To == null)
+ {
+ return null;
+ }
+
+ BaseNode From = ParseExpression();
+ if (From == null)
+ {
+ return null;
+ }
+
+ return new CastExpression("static_cast", To, From);
+ case 'p':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new PackedTemplateParameterExpansion(Expression);
+ case 'r':
+ return ParseUnresolvedName();
+ case 't':
+ Position += 2;
+ BaseNode EnclosedType = ParseType();
+ if (EnclosedType == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("sizeof (", EnclosedType, ")");
+ case 'z':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("sizeof (", Expression, ")");
+ case 'Z':
+ Position += 2;
+ BaseNode SizeofParamNode = null;
+ switch (Peek())
+ {
+ case 'T':
+ // FIXME: ??? Not entire sure if it's right
+ SizeofParamNode = ParseFunctionParameter();
+ if (SizeofParamNode == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("sizeof...(", new PackedTemplateParameterExpansion(SizeofParamNode), ")");
+ case 'f':
+ SizeofParamNode = ParseFunctionParameter();
+ if (SizeofParamNode == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("sizeof...(", SizeofParamNode, ")");
+ }
+ return null;
+ case 'P':
+ Position += 2;
+ List<BaseNode> Arguments = new List<BaseNode>();
+ while (!ConsumeIf("E"))
+ {
+ BaseNode Argument = ParseTemplateArgument();
+ if (Argument == null)
+ {
+ return null;
+ }
+
+ Arguments.Add(Argument);
+ }
+ return new EnclosedExpression("sizeof...(", new NodeArray(Arguments), ")");
+ }
+ return null;
+ case 't':
+ switch (Peek(1))
+ {
+ case 'e':
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("typeid (", Expression, ")");
+ case 't':
+ BaseNode EnclosedType = ParseExpression();
+ if (EnclosedType == null)
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("typeid (", EnclosedType, ")");
+ case 'l':
+ Position += 2;
+ BaseNode TypeNode = ParseType();
+ if (TypeNode == null)
+ {
+ return null;
+ }
+
+ List<BaseNode> BracedExpressions = new List<BaseNode>();
+ while (!ConsumeIf("E"))
+ {
+ Expression = ParseBracedExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ BracedExpressions.Add(Expression);
+ }
+ return new InitListExpression(TypeNode, BracedExpressions);
+ case 'r':
+ Position += 2;
+ return new NameType("throw");
+ case 'w':
+ Position += 2;
+ Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ return new ThrowExpression(Expression);
+ }
+ return null;
+ }
+
+ if (char.IsDigit(Peek()))
+ {
+ return ParseUnresolvedName();
+ }
+
+ return null;
+ }
+
+ private BaseNode ParseIntegerLiteral(string LiteralName)
+ {
+ string Number = ParseNumber(true);
+ if (Number == null || Number.Length == 0 || !ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ return new IntegerLiteral(LiteralName, Number);
+ }
+
+ // <expr-primary> ::= L <type> <value number> E # integer literal
+ // ::= L <type> <value float> E # floating literal (TODO)
+ // ::= L <string type> E # string literal
+ // ::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
+ // ::= L <pointer type> 0 E # null pointer template argument
+ // ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
+ // ::= L _Z <encoding> E # external name
+ private BaseNode ParseExpressionPrimary()
+ {
+ if (!ConsumeIf("L"))
+ {
+ return null;
+ }
+
+ switch (Peek())
+ {
+ case 'w':
+ Position++;
+ return ParseIntegerLiteral("wchar_t");
+ case 'b':
+ if (ConsumeIf("b0E"))
+ {
+ return new NameType("false", NodeType.BooleanExpression);
+ }
+
+ if (ConsumeIf("b1E"))
+ {
+ return new NameType("true", NodeType.BooleanExpression);
+ }
+
+ return null;
+ case 'c':
+ Position++;
+ return ParseIntegerLiteral("char");
+ case 'a':
+ Position++;
+ return ParseIntegerLiteral("signed char");
+ case 'h':
+ Position++;
+ return ParseIntegerLiteral("unsigned char");
+ case 's':
+ Position++;
+ return ParseIntegerLiteral("short");
+ case 't':
+ Position++;
+ return ParseIntegerLiteral("unsigned short");
+ case 'i':
+ Position++;
+ return ParseIntegerLiteral("");
+ case 'j':
+ Position++;
+ return ParseIntegerLiteral("u");
+ case 'l':
+ Position++;
+ return ParseIntegerLiteral("l");
+ case 'm':
+ Position++;
+ return ParseIntegerLiteral("ul");
+ case 'x':
+ Position++;
+ return ParseIntegerLiteral("ll");
+ case 'y':
+ Position++;
+ return ParseIntegerLiteral("ull");
+ case 'n':
+ Position++;
+ return ParseIntegerLiteral("__int128");
+ case 'o':
+ Position++;
+ return ParseIntegerLiteral("unsigned __int128");
+ case 'd':
+ case 'e':
+ case 'f':
+ // TODO: floating literal
+ return null;
+ case '_':
+ if (ConsumeIf("_Z"))
+ {
+ BaseNode Encoding = ParseEncoding();
+ if (Encoding != null && ConsumeIf("E"))
+ {
+ return Encoding;
+ }
+ }
+ return null;
+ case 'T':
+ return null;
+ default:
+ BaseNode Type = ParseType();
+ if (Type == null)
+ {
+ return null;
+ }
+
+ string Number = ParseNumber();
+ if (Number == null || Number.Length == 0 || !ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ return new IntegerCastExpression(Type, Number);
+ }
+ }
+
+ // <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x)
+ // ::= DT <expression> E # decltype of an expression (C++0x)
+ private BaseNode ParseDecltype()
+ {
+ if (!ConsumeIf("D") || (!ConsumeIf("t") && !ConsumeIf("T")))
+ {
+ return null;
+ }
+
+ BaseNode Expression = ParseExpression();
+ if (Expression == null)
+ {
+ return null;
+ }
+
+ if (!ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ return new EnclosedExpression("decltype(", Expression, ")");
+ }
+
+ // <template-param> ::= T_ # first template parameter
+ // ::= T <parameter-2 non-negative number> _
+ // <template-template-param> ::= <template-param>
+ // ::= <substitution>
+ private BaseNode ParseTemplateParam()
+ {
+ if (!ConsumeIf("T"))
+ {
+ return null;
+ }
+
+ int Index = 0;
+ if (!ConsumeIf("_"))
+ {
+ Index = ParsePositiveNumber();
+ if (Index < 0)
+ {
+ return null;
+ }
+
+ Index++;
+ if (!ConsumeIf("_"))
+ {
+ return null;
+ }
+ }
+
+ // 5.1.8: TODO: lambda?
+ // if (IsParsingLambdaParameters)
+ // return new NameType("auto");
+
+ if (CanForwardTemplateReference)
+ {
+ ForwardTemplateReference ForwardTemplateReference = new ForwardTemplateReference(Index);
+ ForwardTemplateReferenceList.Add(ForwardTemplateReference);
+ return ForwardTemplateReference;
+ }
+ if (Index >= TemplateParamList.Count)
+ {
+ return null;
+ }
+
+ return TemplateParamList[Index];
+ }
+
+ // <template-args> ::= I <template-arg>+ E
+ private BaseNode ParseTemplateArguments(bool HasContext = false)
+ {
+ if (!ConsumeIf("I"))
+ {
+ return null;
+ }
+
+ if (HasContext)
+ {
+ TemplateParamList.Clear();
+ }
+
+ List<BaseNode> Args = new List<BaseNode>();
+ while (!ConsumeIf("E"))
+ {
+ if (HasContext)
+ {
+ List<BaseNode> TemplateParamListTemp = new List<BaseNode>(TemplateParamList);
+ BaseNode TemplateArgument = ParseTemplateArgument();
+ TemplateParamList = TemplateParamListTemp;
+ if (TemplateArgument == null)
+ {
+ return null;
+ }
+
+ Args.Add(TemplateArgument);
+ if (TemplateArgument.GetType().Equals(NodeType.PackedTemplateArgument))
+ {
+ TemplateArgument = new PackedTemplateParameter(((NodeArray)TemplateArgument).Nodes);
+ }
+ TemplateParamList.Add(TemplateArgument);
+ }
+ else
+ {
+ BaseNode TemplateArgument = ParseTemplateArgument();
+ if (TemplateArgument == null)
+ {
+ return null;
+ }
+
+ Args.Add(TemplateArgument);
+ }
+ }
+ return new TemplateArguments(Args);
+ }
+
+
+ // <template-arg> ::= <type> # type or template
+ // ::= X <expression> E # expression
+ // ::= <expr-primary> # simple expressions
+ // ::= J <template-arg>* E # argument pack
+ private BaseNode ParseTemplateArgument()
+ {
+ switch (Peek())
+ {
+ // X <expression> E
+ case 'X':
+ Position++;
+ BaseNode Expression = ParseExpression();
+ if (Expression == null || !ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ return Expression;
+ // <expr-primary>
+ case 'L':
+ return ParseExpressionPrimary();
+ // J <template-arg>* E
+ case 'J':
+ Position++;
+ List<BaseNode> TemplateArguments = new List<BaseNode>();
+ while (!ConsumeIf("E"))
+ {
+ BaseNode TemplateArgument = ParseTemplateArgument();
+ if (TemplateArgument == null)
+ {
+ return null;
+ }
+
+ TemplateArguments.Add(TemplateArgument);
+ }
+ return new NodeArray(TemplateArguments, NodeType.PackedTemplateArgument);
+ // <type>
+ default:
+ return ParseType();
+ }
+ }
+
+ class NameParserContext
+ {
+ public CVType CV;
+ public SimpleReferenceType Ref;
+ public bool FinishWithTemplateArguments;
+ public bool CtorDtorConversion;
+ }
+
+
+ // <unresolved-type> ::= <template-param> [ <template-args> ] # T:: or T<X,Y>::
+ // ::= <decltype> # decltype(p)::
+ // ::= <substitution>
+ private BaseNode ParseUnresolvedType()
+ {
+ if (Peek() == 'T')
+ {
+ BaseNode TemplateParam = ParseTemplateParam();
+ if (TemplateParam == null)
+ {
+ return null;
+ }
+
+ SubstitutionList.Add(TemplateParam);
+ return TemplateParam;
+ }
+ else if (Peek() == 'D')
+ {
+ BaseNode DeclType = ParseDecltype();
+ if (DeclType == null)
+ {
+ return null;
+ }
+
+ SubstitutionList.Add(DeclType);
+ return DeclType;
+ }
+ return ParseSubstitution();
+ }
+
+ // <simple-id> ::= <source-name> [ <template-args> ]
+ private BaseNode ParseSimpleId()
+ {
+ BaseNode SourceName = ParseSourceName();
+ if (SourceName == null)
+ {
+ return null;
+ }
+
+ if (Peek() == 'I')
+ {
+ BaseNode TemplateArguments = ParseTemplateArguments();
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ return new NameTypeWithTemplateArguments(SourceName, TemplateArguments);
+ }
+ return SourceName;
+ }
+
+ // <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f())
+ // ::= <simple-id> # e.g., ~A<2*N>
+ private BaseNode ParseDestructorName()
+ {
+ BaseNode Node;
+ if (char.IsDigit(Peek()))
+ {
+ Node = ParseSimpleId();
+ }
+ else
+ {
+ Node = ParseUnresolvedType();
+ }
+ if (Node == null)
+ {
+ return null;
+ }
+
+ return new DtorName(Node);
+ }
+
+ // <base-unresolved-name> ::= <simple-id> # unresolved name
+ // extension ::= <operator-name> # unresolved operator-function-id
+ // extension ::= <operator-name> <template-args> # unresolved operator template-id
+ // ::= on <operator-name> # unresolved operator-function-id
+ // ::= on <operator-name> <template-args> # unresolved operator template-id
+ // ::= dn <destructor-name> # destructor or pseudo-destructor;
+ // # e.g. ~X or ~X<N-1>
+ private BaseNode ParseBaseUnresolvedName()
+ {
+ if (char.IsDigit(Peek()))
+ {
+ return ParseSimpleId();
+ }
+ else if (ConsumeIf("dn"))
+ {
+ return ParseDestructorName();
+ }
+
+ ConsumeIf("on");
+ BaseNode OperatorName = ParseOperatorName(null);
+ if (OperatorName == null)
+ {
+ return null;
+ }
+
+ if (Peek() == 'I')
+ {
+ BaseNode TemplateArguments = ParseTemplateArguments();
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ return new NameTypeWithTemplateArguments(OperatorName, TemplateArguments);
+ }
+ return OperatorName;
+ }
+
+ // <unresolved-name> ::= [gs] <base-unresolved-name> # x or (with "gs") ::x
+ // ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x
+ // ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
+ // # T::N::x /decltype(p)::N::x
+ // ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
+ // # A::x, N::y, A<T>::z; "gs" means leading "::"
+ private BaseNode ParseUnresolvedName(NameParserContext Context = null)
+ {
+ BaseNode Result = null;
+ if (ConsumeIf("srN"))
+ {
+ Result = ParseUnresolvedType();
+ if (Result == null)
+ {
+ return null;
+ }
+
+ if (Peek() == 'I')
+ {
+ BaseNode TemplateArguments = ParseTemplateArguments();
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ Result = new NameTypeWithTemplateArguments(Result, TemplateArguments);
+ if (Result == null)
+ {
+ return null;
+ }
+ }
+
+ while (!ConsumeIf("E"))
+ {
+ BaseNode SimpleId = ParseSimpleId();
+ if (SimpleId == null)
+ {
+ return null;
+ }
+
+ Result = new QualifiedName(Result, SimpleId);
+ if (Result == null)
+ {
+ return null;
+ }
+ }
+
+ BaseNode BaseName = ParseBaseUnresolvedName();
+ if (BaseName == null)
+ {
+ return null;
+ }
+
+ return new QualifiedName(Result, BaseName);
+ }
+
+ bool IsGlobal = ConsumeIf("gs");
+
+ // ::= [gs] <base-unresolved-name> # x or (with "gs") ::x
+ if (!ConsumeIf("sr"))
+ {
+ Result = ParseBaseUnresolvedName();
+ if (Result == null)
+ {
+ return null;
+ }
+
+ if (IsGlobal)
+ {
+ Result = new GlobalQualifiedName(Result);
+ }
+
+ return Result;
+ }
+
+ // ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
+ if (char.IsDigit(Peek()))
+ {
+ do
+ {
+ BaseNode Qualifier = ParseSimpleId();
+ if (Qualifier == null)
+ {
+ return null;
+ }
+
+ if (Result != null)
+ {
+ Result = new QualifiedName(Result, Qualifier);
+ }
+ else if (IsGlobal)
+ {
+ Result = new GlobalQualifiedName(Qualifier);
+ }
+ else
+ {
+ Result = Qualifier;
+ }
+
+ if (Result == null)
+ {
+ return null;
+ }
+ } while (!ConsumeIf("E"));
+ }
+ // ::= sr <unresolved-type> [tempate-args] <base-unresolved-name> # T::x / decltype(p)::x
+ else
+ {
+ Result = ParseUnresolvedType();
+ if (Result == null)
+ {
+ return null;
+ }
+
+ if (Peek() == 'I')
+ {
+ BaseNode TemplateArguments = ParseTemplateArguments();
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ Result = new NameTypeWithTemplateArguments(Result, TemplateArguments);
+ if (Result == null)
+ {
+ return null;
+ }
+ }
+ }
+
+ if (Result == null)
+ {
+ return null;
+ }
+
+ BaseNode BaseUnresolvedName = ParseBaseUnresolvedName();
+ if (BaseUnresolvedName == null)
+ {
+ return null;
+ }
+
+ return new QualifiedName(Result, BaseUnresolvedName);
+ }
+
+ // <unscoped-name> ::= <unqualified-name>
+ // ::= St <unqualified-name> # ::std::
+ private BaseNode ParseUnscopedName(NameParserContext Context)
+ {
+ if (ConsumeIf("St"))
+ {
+ BaseNode UnresolvedName = ParseUnresolvedName(Context);
+ if (UnresolvedName == null)
+ {
+ return null;
+ }
+
+ return new StdQualifiedName(UnresolvedName);
+ }
+ return ParseUnresolvedName(Context);
+ }
+
+ // <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix (TODO)> <unqualified-name> E
+ // ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix (TODO)> <template-args (TODO)> E
+ private BaseNode ParseNestedName(NameParserContext Context)
+ {
+ // Impossible in theory
+ if (Consume() != 'N')
+ {
+ return null;
+ }
+
+ BaseNode Result = null;
+ CVType CV = new CVType(ParseCVQualifiers(), null);
+ if (Context != null)
+ {
+ Context.CV = CV;
+ }
+
+ SimpleReferenceType Ref = ParseRefQualifiers();
+ if (Context != null)
+ {
+ Context.Ref = Ref;
+ }
+
+ if (ConsumeIf("St"))
+ {
+ Result = new NameType("std");
+ }
+
+ while (!ConsumeIf("E"))
+ {
+ // <data-member-prefix> end
+ if (ConsumeIf("M"))
+ {
+ if (Result == null)
+ {
+ return null;
+ }
+
+ continue;
+ }
+ char C = Peek();
+
+ // TODO: template args
+ if (C == 'T')
+ {
+ BaseNode TemplateParam = ParseTemplateParam();
+ if (TemplateParam == null)
+ {
+ return null;
+ }
+
+ Result = CreateNameNode(Result, TemplateParam, Context);
+ SubstitutionList.Add(Result);
+ continue;
+ }
+
+ // <template-prefix> <template-args>
+ if (C == 'I')
+ {
+ BaseNode TemplateArgument = ParseTemplateArguments(Context != null);
+ if (TemplateArgument == null || Result == null)
+ {
+ return null;
+ }
+
+ Result = new NameTypeWithTemplateArguments(Result, TemplateArgument);
+ if (Context != null)
+ {
+ Context.FinishWithTemplateArguments = true;
+ }
+
+ SubstitutionList.Add(Result);
+ continue;
+ }
+
+ // <decltype>
+ if (C == 'D' && (Peek(1) == 't' || Peek(1) == 'T'))
+ {
+ BaseNode Decltype = ParseDecltype();
+ if (Decltype == null)
+ {
+ return null;
+ }
+
+ Result = CreateNameNode(Result, Decltype, Context);
+ SubstitutionList.Add(Result);
+ continue;
+ }
+
+ // <substitution>
+ if (C == 'S' && Peek(1) != 't')
+ {
+ BaseNode Substitution = ParseSubstitution();
+ if (Substitution == null)
+ {
+ return null;
+ }
+
+ Result = CreateNameNode(Result, Substitution, Context);
+ if (Result != Substitution)
+ {
+ SubstitutionList.Add(Substitution);
+ }
+
+ continue;
+ }
+
+ // <ctor-dtor-name> of ParseUnqualifiedName
+ if (C == 'C' || (C == 'D' && Peek(1) != 'C'))
+ {
+ // We cannot have nothing before this
+ if (Result == null)
+ {
+ return null;
+ }
+
+ BaseNode CtOrDtorName = ParseCtorDtorName(Context, Result);
+
+ if (CtOrDtorName == null)
+ {
+ return null;
+ }
+
+ Result = CreateNameNode(Result, CtOrDtorName, Context);
+
+ // TODO: ABI Tags (before)
+ if (Result == null)
+ {
+ return null;
+ }
+
+ SubstitutionList.Add(Result);
+ continue;
+ }
+
+ BaseNode UnqualifiedName = ParseUnqualifiedName(Context);
+ if (UnqualifiedName == null)
+ {
+ return null;
+ }
+ Result = CreateNameNode(Result, UnqualifiedName, Context);
+
+ SubstitutionList.Add(Result);
+ }
+ if (Result == null || SubstitutionList.Count == 0)
+ {
+ return null;
+ }
+
+ SubstitutionList.RemoveAt(SubstitutionList.Count - 1);
+ return Result;
+ }
+
+ // <discriminator> ::= _ <non-negative number> # when number < 10
+ // ::= __ <non-negative number> _ # when number >= 10
+ private void ParseDiscriminator()
+ {
+ if (Count() == 0)
+ {
+ return;
+ }
+ // We ignore the discriminator, we don't need it.
+ if (ConsumeIf("_"))
+ {
+ ConsumeIf("_");
+ while (char.IsDigit(Peek()) && Count() != 0)
+ {
+ Consume();
+ }
+ ConsumeIf("_");
+ }
+ }
+
+ // <local-name> ::= Z <function encoding> E <entity name> [<discriminator>]
+ // ::= Z <function encoding> E s [<discriminator>]
+ // ::= Z <function encoding> Ed [ <parameter number> ] _ <entity name>
+ private BaseNode ParseLocalName(NameParserContext Context)
+ {
+ if (!ConsumeIf("Z"))
+ {
+ return null;
+ }
+
+ BaseNode Encoding = ParseEncoding();
+ if (Encoding == null || !ConsumeIf("E"))
+ {
+ return null;
+ }
+
+ BaseNode EntityName;
+ if (ConsumeIf("s"))
+ {
+ ParseDiscriminator();
+ return new LocalName(Encoding, new NameType("string literal"));
+ }
+ else if (ConsumeIf("d"))
+ {
+ ParseNumber(true);
+ if (!ConsumeIf("_"))
+ {
+ return null;
+ }
+
+ EntityName = ParseName(Context);
+ if (EntityName == null)
+ {
+ return null;
+ }
+
+ return new LocalName(Encoding, EntityName);
+ }
+
+ EntityName = ParseName(Context);
+ if (EntityName == null)
+ {
+ return null;
+ }
+
+ ParseDiscriminator();
+ return new LocalName(Encoding, EntityName);
+ }
+
+ // <name> ::= <nested-name>
+ // ::= <unscoped-name>
+ // ::= <unscoped-template-name> <template-args>
+ // ::= <local-name> # See Scope Encoding below (TODO)
+ private BaseNode ParseName(NameParserContext Context = null)
+ {
+ ConsumeIf("L");
+
+ if (Peek() == 'N')
+ {
+ return ParseNestedName(Context);
+ }
+
+ if (Peek() == 'Z')
+ {
+ return ParseLocalName(Context);
+ }
+
+ if (Peek() == 'S' && Peek(1) != 't')
+ {
+ BaseNode Substitution = ParseSubstitution();
+ if (Substitution == null)
+ {
+ return null;
+ }
+
+ if (Peek() != 'I')
+ {
+ return null;
+ }
+
+ BaseNode TemplateArguments = ParseTemplateArguments(Context != null);
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ if (Context != null)
+ {
+ Context.FinishWithTemplateArguments = true;
+ }
+
+ return new NameTypeWithTemplateArguments(Substitution, TemplateArguments);
+ }
+
+ BaseNode Result = ParseUnscopedName(Context);
+ if (Result == null)
+ {
+ return null;
+ }
+
+ if (Peek() == 'I')
+ {
+ SubstitutionList.Add(Result);
+ BaseNode TemplateArguments = ParseTemplateArguments(Context != null);
+ if (TemplateArguments == null)
+ {
+ return null;
+ }
+
+ if (Context != null)
+ {
+ Context.FinishWithTemplateArguments = true;
+ }
+
+ return new NameTypeWithTemplateArguments(Result, TemplateArguments);
+ }
+
+ return Result;
+ }
+
+ private bool IsEncodingEnd()
+ {
+ char C = Peek();
+ return Count() == 0 || C == 'E' || C == '.' || C == '_';
+ }
+
+ // <encoding> ::= <function name> <bare-function-type>
+ // ::= <data name>
+ // ::= <special-name>
+ private BaseNode ParseEncoding()
+ {
+ NameParserContext Context = new NameParserContext();
+ if (Peek() == 'T' || (Peek() == 'G' && Peek(1) == 'V'))
+ {
+ return ParseSpecialName(Context);
+ }
+
+ BaseNode Name = ParseName(Context);
+ if (Name == null)
+ {
+ return null;
+ }
+
+ // TODO: compute template refs here
+
+ if (IsEncodingEnd())
+ {
+ return Name;
+ }
+
+ // TODO: Ua9enable_ifI
+
+ BaseNode ReturnType = null;
+ if (!Context.CtorDtorConversion && Context.FinishWithTemplateArguments)
+ {
+ ReturnType = ParseType();
+ if (ReturnType == null)
+ {
+ return null;
+ }
+ }
+
+ if (ConsumeIf("v"))
+ {
+ return new EncodedFunction(Name, null, Context.CV, Context.Ref, null, ReturnType);
+ }
+
+ List<BaseNode> Params = new List<BaseNode>();
+
+ // backup because that can be destroyed by parseType
+ CVType CV = Context.CV;
+ SimpleReferenceType Ref = Context.Ref;
+
+ while (!IsEncodingEnd())
+ {
+ BaseNode Param = ParseType();
+ if (Param == null)
+ {
+ return null;
+ }
+
+ Params.Add(Param);
+ }
+
+ return new EncodedFunction(Name, new NodeArray(Params), CV, Ref, null, ReturnType);
+ }
+
+ // <mangled-name> ::= _Z <encoding>
+ // ::= <type>
+ private BaseNode Parse()
+ {
+ if (ConsumeIf("_Z"))
+ {
+ BaseNode Encoding = ParseEncoding();
+ if (Encoding != null && Count() == 0)
+ {
+ return Encoding;
+ }
+ return null;
+ }
+ else
+ {
+ BaseNode Type = ParseType();
+ if (Type != null && Count() == 0)
+ {
+ return Type;
+ }
+ return null;
+ }
+ }
+
+ public static string Parse(string OriginalMangled)
+ {
+ Demangler Instance = new Demangler(OriginalMangled);
+ BaseNode ResNode = Instance.Parse();
+
+ if (ResNode != null)
+ {
+ StringWriter Writer = new StringWriter();
+ ResNode.Print(Writer);
+ return Writer.ToString();
+ }
+
+ return OriginalMangled;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs
index bfda93f4..72ed6b0d 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;