From 46a11460d4701f66926f70b5dc394d209e877854 Mon Sep 17 00:00:00 2001
From: Thomas Guillemard <thog@protonmail.com>
Date: Sat, 15 Sep 2018 15:29:18 +0200
Subject: [PATCH] Rewrite the C++ Demangler (#416)

* Rewrite the C++ Demangler

This new Demangler provides support to almost every possible mangled
symbols and should behaves like GNU c++filt.

It works on 98.9% of the sdk's symbols and 99.5%
of Puyo Puyo Tetris's symbols.

* Fix code style

* Fix noexcept enclosed expression parsing issues

* fix code style issues
---
 Ryujinx.HLE/HOS/Diagnostics/Demangler.cs      |  416 --
 .../Ast/ArraySubscriptingExpression.cs        |   25 +
 .../Diagnostics/Demangler/Ast/ArrayType.cs    |   59 +
 .../HOS/Diagnostics/Demangler/Ast/BaseNode.cs |  113 +
 .../Demangler/Ast/BinaryExpression.cs         |   41 +
 .../Demangler/Ast/BracedExpression.cs         |   40 +
 .../Demangler/Ast/BracedRangeExpression.cs    |   34 +
 .../Demangler/Ast/CallExpression.cs           |   25 +
 .../Demangler/Ast/CasExpression.cs            |   30 +
 .../Demangler/Ast/ConditionalExpression.cs    |   29 +
 .../Demangler/Ast/ConversionExpression.cs     |   24 +
 .../Demangler/Ast/ConversionOperatorType.cs   |   15 +
 .../Demangler/Ast/CtorDtorNameType.cs         |   24 +
 .../Demangler/Ast/CtorVtableSpecialName.cs    |   24 +
 .../Demangler/Ast/DeleteExpression.cs         |   33 +
 .../HOS/Diagnostics/Demangler/Ast/DtorName.cs |   15 +
 .../Demangler/Ast/DynamicExceptionSpec.cs     |   16 +
 .../Demangler/Ast/ElaboratedType.cs           |   21 +
 .../Demangler/Ast/EnclosedExpression.cs       |   25 +
 .../Demangler/Ast/EncodedFunction.cs          |   77 +
 .../Demangler/Ast/FoldExpression.cs           |   48 +
 .../Demangler/Ast/ForwardTemplateReference.cs |   36 +
 .../Demangler/Ast/FunctionParameter.cs        |   24 +
 .../Diagnostics/Demangler/Ast/FunctionType.cs |   61 +
 .../Demangler/Ast/GlobalQualifiedName.cs      |   15 +
 .../Demangler/Ast/InitListExpression.cs       |   30 +
 .../Demangler/Ast/IntegerCastExpression.cs    |   22 +
 .../Demangler/Ast/IntegerLiteral.cs           |   41 +
 .../Demangler/Ast/LiteralOperator.cs          |   16 +
 .../Diagnostics/Demangler/Ast/LocalName.cs    |   23 +
 .../Demangler/Ast/MemberExpression.cs         |   25 +
 .../HOS/Diagnostics/Demangler/Ast/NameType.cs |   29 +
 .../Ast/NameTypeWithTemplateArguments.cs      |   27 +
 .../Diagnostics/Demangler/Ast/NestedName.cs   |   26 +
 .../Demangler/Ast/NewExpression.cs            |   55 +
 .../Diagnostics/Demangler/Ast/NodeArray.cs    |   31 +
 .../Diagnostics/Demangler/Ast/NoexceptSpec.cs |   16 +
 .../Demangler/Ast/PackedTemplateParameter.cs  |   39 +
 .../Ast/PackedTemplateParameterExpansion.cs   |   24 +
 .../Diagnostics/Demangler/Ast/ParentNode.cs   |   17 +
 .../Diagnostics/Demangler/Ast/PointerType.cs  |   45 +
 .../Demangler/Ast/PostfixExpression.cs        |   22 +
 .../Demangler/Ast/PostfixQualifiedType.cs     |   20 +
 .../Demangler/Ast/PrefixExpression.cs         |   22 +
 .../Demangler/Ast/QualifiedName.cs            |   23 +
 .../Diagnostics/Demangler/Ast/Qualifier.cs    |  120 +
 .../Demangler/Ast/ReferenceType.cs            |   47 +
 .../Diagnostics/Demangler/Ast/SpecialName.cs  |   20 +
 .../Demangler/Ast/SpecialSubstitution.cs      |   89 +
 .../Demangler/Ast/StdQualifiedName.cs         |   15 +
 .../Demangler/Ast/TemplateArguments.cs        |   27 +
 .../Demangler/Ast/ThrowExpression.cs          |   20 +
 .../HOS/Diagnostics/Demangler/Demangler.cs    | 3367 +++++++++++++++++
 Ryujinx.HLE/HOS/Process.cs                    |    2 +-
 54 files changed, 5113 insertions(+), 417 deletions(-)
 delete mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs
 create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs

diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs
deleted file mode 100644
index aea979c26c..0000000000
--- 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 0000000000..435789e0f2
--- /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 0000000000..1679736072
--- /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 0000000000..870758462e
--- /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 0000000000..9cd1dd779a
--- /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 0000000000..59222ea3f9
--- /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 0000000000..e459f1a393
--- /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 0000000000..7e59ab09df
--- /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 0000000000..2415c6c000
--- /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 0000000000..17ac7c1a20
--- /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 0000000000..7c5d35d810
--- /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 0000000000..55d4eecab9
--- /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 0000000000..49ed386d08
--- /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 0000000000..7630dbe570
--- /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 0000000000..22c34c4219
--- /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 0000000000..c65c4cfb3a
--- /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 0000000000..dca5f0dfd0
--- /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 0000000000..11f89c8d33
--- /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 0000000000..dc991aa09c
--- /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 0000000000..37a9a7afe0
--- /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 0000000000..e015299846
--- /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 0000000000..6456e47bfa
--- /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 0000000000..5a1ca61d55
--- /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 0000000000..c727eab9c5
--- /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 0000000000..2346c1bf70
--- /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 0000000000..cd534590b9
--- /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 0000000000..984c9aefb0
--- /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 0000000000..215cf6dce5
--- /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 0000000000..f9bd4a6ef5
--- /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 0000000000..44c216289c
--- /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 0000000000..dd3d02dbd1
--- /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 0000000000..029440cb77
--- /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 0000000000..e16bd1508b
--- /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 0000000000..0ec6d98299
--- /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 0000000000..5cc14ad9f8
--- /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 0000000000..9720a8e49d
--- /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 0000000000..5bee9cfacb
--- /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 0000000000..66ad112277
--- /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 0000000000..ce9fa4a3ba
--- /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 0000000000..f1c2834739
--- /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 0000000000..a60776a272
--- /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 0000000000..021f2de820
--- /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 0000000000..465450d3f2
--- /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 0000000000..619d05387b
--- /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 0000000000..ce356e1628
--- /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 0000000000..3721b8dee3
--- /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 0000000000..602814afb3
--- /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 0000000000..1a299af4d2
--- /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 0000000000..f4e9a14a12
--- /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 0000000000..ed1b599429
--- /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 0000000000..4de66e000b
--- /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 0000000000..bb1466174e
--- /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 0000000000..164d5618ca
--- /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 bfda93f494..72ed6b0d8e 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;