diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Diagnostics | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Diagnostics')
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; + } + } +} |
