3368 lines
118 KiB
C#
3368 lines
118 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|