aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Diagnostics
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Diagnostics
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Diagnostics')
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs25
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs59
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs113
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs41
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs40
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs34
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs28
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs29
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs15
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs33
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs15
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs16
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs21
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs25
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs77
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs48
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs36
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs61
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs15
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs29
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs22
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs42
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs16
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs23
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs25
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs29
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs27
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs26
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs55
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs30
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs16
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs39
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs17
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs45
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs22
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs20
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs22
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs23
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs120
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs47
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs20
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs89
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs15
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs26
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs20
-rw-r--r--src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs3367
52 files changed, 5107 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs
new file mode 100644
index 00000000..5145ff7b
--- /dev/null
+++ b/src/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)
+ {
+ _leftNode = leftNode;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs
new file mode 100644
index 00000000..4b1041ab
--- /dev/null
+++ b/src/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)
+ {
+ _base = Base;
+ _dimensionExpression = dimensionExpression;
+ }
+
+ public ArrayType(BaseNode Base, string dimensionString) : base(NodeType.ArrayType)
+ {
+ _base = Base;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs
new file mode 100644
index 00000000..ca4b98f8
--- /dev/null
+++ b/src/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)
+ {
+ 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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs
new file mode 100644
index 00000000..0c492df3
--- /dev/null
+++ b/src/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)
+ {
+ _leftPart = leftPart;
+ _name = name;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs
new file mode 100644
index 00000000..6b9782f5
--- /dev/null
+++ b/src/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)
+ {
+ _element = element;
+ _expression = expression;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs
new file mode 100644
index 00000000..802422d9
--- /dev/null
+++ b/src/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)
+ {
+ _firstNode = firstNode;
+ _lastNode = lastNode;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs
new file mode 100644
index 00000000..8e3fc3e6
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs
@@ -0,0 +1,24 @@
+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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs
new file mode 100644
index 00000000..1149a788
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs
@@ -0,0 +1,28 @@
+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)
+ {
+ _kind = kind;
+ _to = to;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs
new file mode 100644
index 00000000..c0dd6717
--- /dev/null
+++ b/src/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)
+ {
+ _thenNode = thenNode;
+ _conditionNode = conditionNode;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs
new file mode 100644
index 00000000..dd1f7a00
--- /dev/null
+++ b/src/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)
+ {
+ _typeNode = typeNode;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs
new file mode 100644
index 00000000..8a5cde86
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs
new file mode 100644
index 00000000..5f458123
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs
new file mode 100644
index 00000000..3bb5b163
--- /dev/null
+++ b/src/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)
+ {
+ _firstType = firstType;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs
new file mode 100644
index 00000000..14715d25
--- /dev/null
+++ b/src/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)
+ {
+ _isGlobal = isGlobal;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs
new file mode 100644
index 00000000..5cc4e6cf
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs
new file mode 100644
index 00000000..faa91443
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs
new file mode 100644
index 00000000..086cd3dc
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs
new file mode 100644
index 00000000..b45481dd
--- /dev/null
+++ b/src/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)
+ {
+ _prefix = prefix;
+ _expression = expression;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs
new file mode 100644
index 00000000..c7b6dab1
--- /dev/null
+++ b/src/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)
+ {
+ _name = name;
+ _params = Params;
+ _cv = cv;
+ _ref = Ref;
+ _attrs = attrs;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs
new file mode 100644
index 00000000..04f7053e
--- /dev/null
+++ b/src/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)
+ {
+ _isLeftFold = isLeftFold;
+ _operatorName = operatorName;
+ _expression = expression;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs
new file mode 100644
index 00000000..1bbf6ef9
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs
new file mode 100644
index 00000000..5654a048
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs
new file mode 100644
index 00000000..4ad0c9f5
--- /dev/null
+++ b/src/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)
+ {
+ _returnType = returnType;
+ _params = Params;
+ _cvQualifier = cvQualifier;
+ _referenceQualifier = referenceQualifier;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs
new file mode 100644
index 00000000..d3b6a558
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs
new file mode 100644
index 00000000..7155dd60
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs
@@ -0,0 +1,29 @@
+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)
+ {
+ _typeNode = typeNode;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs
new file mode 100644
index 00000000..ef07414d
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs
new file mode 100644
index 00000000..33752d00
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
+{
+ public class IntegerLiteral : BaseNode
+ {
+ private string _literalName;
+ private string _literalValue;
+
+ public IntegerLiteral(string literalName, string literalValue) : base(NodeType.IntegerLiteral)
+ {
+ _literalValue = literalValue;
+ _literalName = literalName;
+ }
+
+ public override void PrintLeft(TextWriter writer)
+ {
+ if (_literalName.Length > 3)
+ {
+ writer.Write("(");
+ writer.Write(_literalName);
+ writer.Write(")");
+ }
+
+ if (_literalValue[0] == 'n')
+ {
+ writer.Write("-");
+ writer.Write(_literalValue.AsSpan(1));
+ }
+ else
+ {
+ writer.Write(_literalValue);
+ }
+
+ if (_literalName.Length <= 3)
+ {
+ writer.Write(_literalName);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs
new file mode 100644
index 00000000..f7e86c9e
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs
new file mode 100644
index 00000000..15d46b38
--- /dev/null
+++ b/src/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)
+ {
+ _encoding = encoding;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs
new file mode 100644
index 00000000..9b91f6f5
--- /dev/null
+++ b/src/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)
+ {
+ _leftNode = leftNode;
+ _kind = kind;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs
new file mode 100644
index 00000000..f9f4cb20
--- /dev/null
+++ b/src/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)
+ {
+ _nameValue = nameValue;
+ }
+
+ public NameType(string nameValue) : base(NodeType.NameType)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs
new file mode 100644
index 00000000..ee725f36
--- /dev/null
+++ b/src/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)
+ {
+ _prev = prev;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs
new file mode 100644
index 00000000..640c200c
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs
new file mode 100644
index 00000000..ba4690af
--- /dev/null
+++ b/src/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)
+ {
+ _expressions = expressions;
+ _typeNode = typeNode;
+ _initializers = initializers;
+
+ _isGlobal = isGlobal;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs
new file mode 100644
index 00000000..1482dfc3
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs
@@ -0,0 +1,30 @@
+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)
+ {
+ Nodes = nodes;
+ }
+
+ public NodeArray(List<BaseNode> nodes, NodeType type) : base(type)
+ {
+ 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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs
new file mode 100644
index 00000000..49044493
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs
new file mode 100644
index 00000000..4c820095
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs
new file mode 100644
index 00000000..c3645044
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs
new file mode 100644
index 00000000..786abced
--- /dev/null
+++ b/src/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)
+ {
+ Child = child;
+ }
+
+ public override string GetName()
+ {
+ return Child.GetName();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs
new file mode 100644
index 00000000..b1a3ec42
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs
new file mode 100644
index 00000000..ccaea3ba
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs
new file mode 100644
index 00000000..5024a8f9
--- /dev/null
+++ b/src/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)
+ {
+ _postfixQualifier = postfixQualifier;
+ }
+
+ public override void PrintLeft(TextWriter writer)
+ {
+ Child.Print(writer);
+ writer.Write(_postfixQualifier);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs
new file mode 100644
index 00000000..9c3d4552
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs
new file mode 100644
index 00000000..2e18f564
--- /dev/null
+++ b/src/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)
+ {
+ _qualifier = qualifier;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs
new file mode 100644
index 00000000..cb6dd6bf
--- /dev/null
+++ b/src/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)
+ {
+ 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)
+ {
+ 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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs
new file mode 100644
index 00000000..a3214171
--- /dev/null
+++ b/src/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)
+ {
+ _reference = reference;
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs
new file mode 100644
index 00000000..1447458b
--- /dev/null
+++ b/src/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)
+ {
+ _specialValue = specialValue;
+ }
+
+ public override void PrintLeft(TextWriter writer)
+ {
+ writer.Write(_specialValue);
+ Child.Print(writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs
new file mode 100644
index 00000000..8d45e180
--- /dev/null
+++ b/src/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)
+ {
+ _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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs
new file mode 100644
index 00000000..c3a97d60
--- /dev/null
+++ b/src/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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs
new file mode 100644
index 00000000..aefd668d
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs
@@ -0,0 +1,26 @@
+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/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs
new file mode 100644
index 00000000..2972a31c
--- /dev/null
+++ b/src/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)
+ {
+ _expression = expression;
+ }
+
+ public override void PrintLeft(TextWriter writer)
+ {
+ writer.Write("throw ");
+ _expression.Print(writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs
new file mode 100644
index 00000000..1bfd7ac0
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs
@@ -0,0 +1,3367 @@
+using Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
+{
+ class Demangler
+ {
+ private static readonly string Base36 = "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)
+ {
+ Mangled = mangled;
+ _position = 0;
+ _length = mangled.Length;
+ _canParseTemplateArgs = true;
+ }
+
+ private bool ConsumeIf(string toConsume)
+ {
+ var mangledPart = Mangled.AsSpan(_position);
+
+ if (mangledPart.StartsWith(toConsume.AsSpan()))
+ {
+ _position += toConsume.Length;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private ReadOnlySpan<char> PeekString(int offset = 0, int length = 1)
+ {
+ if (_position + offset >= length)
+ {
+ return null;
+ }
+
+ return Mangled.AsSpan(_position + offset, length);
+ }
+
+ private char Peek(int offset = 0)
+ {
+ if (_position + offset >= _length)
+ {
+ return '\0';
+ }
+
+ return Mangled[_position + offset];
+ }
+
+ private char Consume()
+ {
+ if (_position < _length)
+ {
+ return Mangled[_position++];
+ }
+
+ return '\0';
+ }
+
+ private int Count()
+ {
+ return _length - _position;
+ }
+
+ private static int FromBase36(string encoded)
+ {
+ char[] reversedEncoded = encoded.ToLower().ToCharArray().Reverse().ToArray();
+
+ int result = 0;
+
+ for (int i = 0; i < reversedEncoded.Length; i++)
+ {
+ int value = Base36.IndexOf(reversedEncoded[i]);
+ if (value == -1)
+ {
+ return -1;
+ }
+
+ result += value * (int)Math.Pow(36, i);
+ }
+
+ return result;
+ }
+
+ private int ParseSeqId()
+ {
+ ReadOnlySpan<char> part = Mangled.AsSpan(_position);
+ int seqIdLen = 0;
+
+ for (; seqIdLen < part.Length; seqIdLen++)
+ {
+ if (!char.IsLetterOrDigit(part[seqIdLen]))
+ {
+ break;
+ }
+ }
+
+ _position += seqIdLen;
+
+ return FromBase36(new string(part[..seqIdLen]));
+ }
+
+ // <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()
+ {
+ ReadOnlySpan<char> part = Mangled.AsSpan(_position);
+ int numberLength = 0;
+
+ for (; numberLength < part.Length; numberLength++)
+ {
+ if (!char.IsDigit(part[numberLength]))
+ {
+ break;
+ }
+ }
+
+ _position += numberLength;
+
+ if (numberLength == 0)
+ {
+ return -1;
+ }
+
+ return int.Parse(part[..numberLength]);
+ }
+
+ private string ParseNumber(bool isSigned = false)
+ {
+ if (isSigned)
+ {
+ ConsumeIf("n");
+ }
+
+ if (Count() == 0 || !char.IsDigit(Mangled[_position]))
+ {
+ return null;
+ }
+
+ ReadOnlySpan<char> part = Mangled.AsSpan(_position);
+ int numberLength = 0;
+
+ for (; numberLength < part.Length; numberLength++)
+ {
+ if (!char.IsDigit(part[numberLength]))
+ {
+ break;
+ }
+ }
+
+ _position += numberLength;
+
+ return new string(part[..numberLength]);
+ }
+
+ // <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> [template-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;
+ }
+ }
+}