Index: lldb/trunk/include/lldb/Core/CxaDemangle.h =================================================================== --- lldb/trunk/include/lldb/Core/CxaDemangle.h +++ lldb/trunk/include/lldb/Core/CxaDemangle.h @@ -0,0 +1,21 @@ +//===-- CxaDemangle.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CxaDemangle_h_ +#define liblldb_CxaDemangle_h_ + +namespace lldb_private +{ + + char* + __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status); + +} + +#endif Index: lldb/trunk/include/lldb/Core/FastDemangle.h =================================================================== --- lldb/trunk/include/lldb/Core/FastDemangle.h +++ lldb/trunk/include/lldb/Core/FastDemangle.h @@ -0,0 +1,24 @@ +//===-- FastDemangle.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FastDemangle_h_ +#define liblldb_FastDemangle_h_ + +namespace lldb_private +{ + + char * + FastDemangle(const char *mangled_name); + + char * + FastDemangle(const char *mangled_name, long mangled_name_length); + +} + +#endif Index: lldb/trunk/source/Core/CMakeLists.txt =================================================================== --- lldb/trunk/source/Core/CMakeLists.txt +++ lldb/trunk/source/Core/CMakeLists.txt @@ -14,6 +14,7 @@ ConnectionMachPort.cpp ConnectionSharedMemory.cpp ConstString.cpp + CxaDemangle.cpp DataBufferHeap.cpp DataBufferMemoryMap.cpp DataEncoder.cpp Index: lldb/trunk/source/Core/CxaDemangle.cpp =================================================================== --- lldb/trunk/source/Core/CxaDemangle.cpp +++ lldb/trunk/source/Core/CxaDemangle.cpp @@ -0,0 +1,4979 @@ +//---------------------------------------------------------------------- +// Inlined copy of: +// http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp +// revision 238263. +// +// Changes include: +// - Renamed the "__cxxabiv1" namespace to "lldb_private" +// - Stripped GCC attributes() +// - Removed extern "C" from the cxa_demangle function +// - Added "#undef _LIBCPP_EXTERN_TEMPLATE" to avoid warning +//---------------------------------------------------------------------- + +#undef _LIBCPP_EXTERN_TEMPLATE // Avoid warning below + +//===-------------------------- cxa_demangle.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LIBCPP_EXTERN_TEMPLATE(...) +#define _LIBCPP_NO_EXCEPTIONS + +#include +#include +#include +#include +#include +#include +#include + +namespace lldb_private +{ + +namespace +{ + +enum +{ + unknown_error = -4, + invalid_args = -3, + invalid_mangled_name, + memory_alloc_failure, + success +}; + +template + const char* parse_type(const char* first, const char* last, C& db); +template + const char* parse_encoding(const char* first, const char* last, C& db); +template + const char* parse_name(const char* first, const char* last, C& db, + bool* ends_with_template_args = 0); +template + const char* parse_expression(const char* first, const char* last, C& db); +template + const char* parse_template_args(const char* first, const char* last, C& db); +template + const char* parse_operator_name(const char* first, const char* last, C& db); +template + const char* parse_unqualified_name(const char* first, const char* last, C& db); +template + const char* parse_decltype(const char* first, const char* last, C& db); + +template +void +print_stack(const C& db) +{ + fprintf(stderr, "---------\n"); + fprintf(stderr, "names:\n"); + for (auto& s : db.names) + fprintf(stderr, "{%s#%s}\n", s.first.c_str(), s.second.c_str()); + int i = -1; + fprintf(stderr, "subs:\n"); + for (auto& v : db.subs) + { + if (i >= 0) + fprintf(stderr, "S%i_ = {", i); + else + fprintf(stderr, "S_ = {"); + for (auto& s : v) + fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); + fprintf(stderr, "}\n"); + ++i; + } + fprintf(stderr, "template_param:\n"); + for (auto& t : db.template_param) + { + fprintf(stderr, "--\n"); + i = -1; + for (auto& v : t) + { + if (i >= 0) + fprintf(stderr, "T%i_ = {", i); + else + fprintf(stderr, "T_ = {"); + for (auto& s : v) + fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); + fprintf(stderr, "}\n"); + ++i; + } + } + fprintf(stderr, "---------\n\n"); +} + +template +void +print_state(const char* msg, const char* first, const char* last, const C& db) +{ + fprintf(stderr, "%s: ", msg); + for (; first != last; ++first) + fprintf(stderr, "%c", *first); + fprintf(stderr, "\n"); + print_stack(db); +} + +// ::= [n] + +const char* +parse_number(const char* first, const char* last) +{ + if (first != last) + { + const char* t = first; + if (*t == 'n') + ++t; + if (t != last) + { + if (*t == '0') + { + first = t+1; + } + else if ('1' <= *t && *t <= '9') + { + first = t+1; + while (first != last && std::isdigit(*first)) + ++first; + } + } + } + return first; +} + +template +struct float_data; + +template <> +struct float_data +{ + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static constexpr const char* spec = "%af"; +}; + +constexpr const char* float_data::spec; + +template <> +struct float_data +{ + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static constexpr const char* spec = "%a"; +}; + +constexpr const char* float_data::spec; + +template <> +struct float_data +{ +#if defined(__arm__) + static const size_t mangled_size = 16; +#else + static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms +#endif + static const size_t max_demangled_size = 40; + static constexpr const char* spec = "%LaL"; +}; + +constexpr const char* float_data::spec; + +template +const char* +parse_floating_number(const char* first, const char* last, C& db) +{ + const size_t N = float_data::mangled_size; + if (static_cast(last - first) > N) + { + last = first + N; + union + { + Float value; + char buf[sizeof(Float)]; + }; + const char* t = first; + char* e = buf; + for (; t != last; ++t, ++e) + { + if (!isxdigit(*t)) + return first; + unsigned d1 = isdigit(*t) ? static_cast(*t - '0') : + static_cast(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast(*t - '0') : + static_cast(*t - 'a' + 10); + *e = static_cast((d1 << 4) + d0); + } + if (*t == 'E') + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[float_data::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), float_data::spec, value); + if (static_cast(n) >= sizeof(num)) + return first; + db.names.push_back(typename C::String(num, static_cast(n))); + first = t+1; + } + } + return first; +} + +// ::= + +template +const char* +parse_source_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + char c = *first; + if (isdigit(c) && first+1 != last) + { + const char* t = first+1; + size_t n = static_cast(c - '0'); + for (c = *t; isdigit(c); c = *t) + { + n = n * 10 + static_cast(c - '0'); + if (++t == last) + return first; + } + if (static_cast(last - t) >= n) + { + typename C::String r(t, n); + if (r.substr(0, 10) == "_GLOBAL__N") + db.names.push_back("(anonymous namespace)"); + else + db.names.push_back(std::move(r)); + first = t + n; + } + } + } + return first; +} + +// ::= S _ +// ::= S_ +// ::= Sa # ::std::allocator +// ::= Sb # ::std::basic_string +// ::= Ss # ::std::basic_string < char, +// ::std::char_traits, +// ::std::allocator > +// ::= Si # ::std::basic_istream > +// ::= So # ::std::basic_ostream > +// ::= Sd # ::std::basic_iostream > + +template +const char* +parse_substitution(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'S') + { + switch (first[1]) + { + case 'a': + db.names.push_back("std::allocator"); + first += 2; + break; + case 'b': + db.names.push_back("std::basic_string"); + first += 2; + break; + case 's': + db.names.push_back("std::string"); + first += 2; + break; + case 'i': + db.names.push_back("std::istream"); + first += 2; + break; + case 'o': + db.names.push_back("std::ostream"); + first += 2; + break; + case 'd': + db.names.push_back("std::iostream"); + first += 2; + break; + case '_': + if (!db.subs.empty()) + { + for (const auto& n : db.subs.front()) + db.names.push_back(n); + first += 2; + } + break; + default: + if (std::isdigit(first[1]) || std::isupper(first[1])) + { + size_t sub = 0; + const char* t = first+1; + if (std::isdigit(*t)) + sub = static_cast(*t - '0'); + else + sub = static_cast(*t - 'A') + 10; + for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t) + { + sub *= 36; + if (std::isdigit(*t)) + sub += static_cast(*t - '0'); + else + sub += static_cast(*t - 'A') + 10; + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < db.subs.size()) + { + for (const auto& n : db.subs[sub]) + db.names.push_back(n); + first = t+1; + } + } + break; + } + } + } + return first; +} + +// ::= v # void +// ::= w # wchar_t +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= n # __int128 +// ::= o # unsigned __int128 +// ::= f # float +// ::= d # double +// ::= e # long double, __float80 +// ::= g # __float128 +// ::= z # ellipsis +// ::= Dd # IEEE 754r decimal floating point (64 bits) +// ::= De # IEEE 754r decimal floating point (128 bits) +// ::= Df # IEEE 754r decimal floating point (32 bits) +// ::= Dh # IEEE 754r half-precision floating point (16 bits) +// ::= Di # char32_t +// ::= Ds # char16_t +// ::= Da # auto (in dependent new-expressions) +// ::= Dc # decltype(auto) +// ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) +// ::= u # vendor extended type + +template +const char* +parse_builtin_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'v': + db.names.push_back("void"); + ++first; + break; + case 'w': + db.names.push_back("wchar_t"); + ++first; + break; + case 'b': + db.names.push_back("bool"); + ++first; + break; + case 'c': + db.names.push_back("char"); + ++first; + break; + case 'a': + db.names.push_back("signed char"); + ++first; + break; + case 'h': + db.names.push_back("unsigned char"); + ++first; + break; + case 's': + db.names.push_back("short"); + ++first; + break; + case 't': + db.names.push_back("unsigned short"); + ++first; + break; + case 'i': + db.names.push_back("int"); + ++first; + break; + case 'j': + db.names.push_back("unsigned int"); + ++first; + break; + case 'l': + db.names.push_back("long"); + ++first; + break; + case 'm': + db.names.push_back("unsigned long"); + ++first; + break; + case 'x': + db.names.push_back("long long"); + ++first; + break; + case 'y': + db.names.push_back("unsigned long long"); + ++first; + break; + case 'n': + db.names.push_back("__int128"); + ++first; + break; + case 'o': + db.names.push_back("unsigned __int128"); + ++first; + break; + case 'f': + db.names.push_back("float"); + ++first; + break; + case 'd': + db.names.push_back("double"); + ++first; + break; + case 'e': + db.names.push_back("long double"); + ++first; + break; + case 'g': + db.names.push_back("__float128"); + ++first; + break; + case 'z': + db.names.push_back("..."); + ++first; + break; + case 'u': + { + const char*t = parse_source_name(first+1, last, db); + if (t != first+1) + first = t; + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'd': + db.names.push_back("decimal64"); + first += 2; + break; + case 'e': + db.names.push_back("decimal128"); + first += 2; + break; + case 'f': + db.names.push_back("decimal32"); + first += 2; + break; + case 'h': + db.names.push_back("decimal16"); + first += 2; + break; + case 'i': + db.names.push_back("char32_t"); + first += 2; + break; + case 's': + db.names.push_back("char16_t"); + first += 2; + break; + case 'a': + db.names.push_back("auto"); + first += 2; + break; + case 'c': + db.names.push_back("decltype(auto)"); + first += 2; + break; + case 'n': + db.names.push_back("std::nullptr_t"); + first += 2; + break; + } + } + break; + } + } + return first; +} + +// ::= [r] [V] [K] + +const char* +parse_cv_qualifiers(const char* first, const char* last, unsigned& cv) +{ + cv = 0; + if (first != last) + { + if (*first == 'r') + { + cv |= 4; + ++first; + } + if (*first == 'V') + { + cv |= 2; + ++first; + } + if (*first == 'K') + { + cv |= 1; + ++first; + } + } + return first; +} + +// ::= T_ # first template parameter +// ::= T _ + +template +const char* +parse_template_param(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'T') + { + if (first[1] == '_') + { + if (db.template_param.empty()) + return first; + if (!db.template_param.back().empty()) + { + for (auto& t : db.template_param.back().front()) + db.names.push_back(t); + first += 2; + } + else + { + db.names.push_back("T_"); + first += 2; + db.fix_forward_references = true; + } + } + else if (isdigit(first[1])) + { + const char* t = first+1; + size_t sub = static_cast(*t - '0'); + for (++t; t != last && isdigit(*t); ++t) + { + sub *= 10; + sub += static_cast(*t - '0'); + } + if (t == last || *t != '_' || db.template_param.empty()) + return first; + ++sub; + if (sub < db.template_param.back().size()) + { + for (auto& temp : db.template_param.back()[sub]) + db.names.push_back(temp); + first = t+1; + } + else + { + db.names.push_back(typename C::String(first, t+1)); + first = t+1; + db.fix_forward_references = true; + } + } + } + } + return first; +} + +// cc # const_cast (expression) + +template +const char* +parse_const_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// dc # dynamic_cast (expression) + +template +const char* +parse_dynamic_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// rc # reinterpret_cast (expression) + +template +const char* +parse_reinterpret_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sc # static_cast (expression) + +template +const char* +parse_static_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sp # pack expansion + +template +const char* +parse_pack_expansion(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'p') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + first = t; + } + return first; +} + +// st # sizeof (a type) + +template +const char* +parse_sizeof_type_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sz # sizeof (a expression) + +template +const char* +parse_sizeof_expr_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sZ # size of a parameter pack + +template +const char* +parse_sizeof_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') + { + size_t k0 = db.names.size(); + const char* t = parse_template_param(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + typename C::String tmp("sizeof...("); + size_t k = k0; + if (k != k1) + { + tmp += db.names[k].move_full(); + for (++k; k != k1; ++k) + tmp += ", " + db.names[k].move_full(); + } + tmp += ")"; + for (; k1 != k0; --k1) + db.names.pop_back(); + db.names.push_back(std::move(tmp)); + first = t; + } + } + return first; +} + +// ::= fp _ # L == 0, first parameter +// ::= fp _ # L == 0, second and later parameters +// ::= fL p _ # L > 0, first parameter +// ::= fL p _ # L > 0, second and later parameters + +template +const char* +parse_function_param(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && *first == 'f') + { + if (first[1] == 'p') + { + unsigned cv; + const char* t = parse_cv_qualifiers(first+2, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + else if (first[1] == 'L') + { + unsigned cv; + const char* t0 = parse_number(first+2, last); + if (t0 != last && *t0 == 'p') + { + ++t0; + const char* t = parse_cv_qualifiers(t0, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + } + } + return first; +} + +// sZ # size of a function parameter pack + +template +const char* +parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f') + { + const char* t = parse_function_param(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// te # typeid (expression) +// ti # typeid (type) + +template +const char* +parse_typeid_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) + { + const char* t; + if (first[1] == 'e') + t = parse_expression(first+2, last, db); + else + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "typeid(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// tw # throw expression + +template +const char* +parse_throw_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && first[1] == 'w') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "throw " + db.names.back().move_full(); + first = t; + } + } + return first; +} + +// ds # expr.*expr + +template +const char* +parse_dot_star_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 's') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += ".*" + expr; + first = t1; + } + } + } + return first; +} + +// ::= [ ] + +template +const char* +parse_simple_id(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_source_name(first, last, db); + if (t != first) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + first = t1; + } + else + first = t; + } + return first; +} + +// ::= +// ::= +// ::= + +template +const char* +parse_unresolved_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = first; + switch (*first) + { + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first && k1 == k0 + 1) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + else + { + for (; k1 != k0; --k1) + db.names.pop_back(); + } + break; + } + case 'D': + t = parse_decltype(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + break; + case 'S': + t = parse_substitution(first, last, db); + if (t != first) + first = t; + else + { + if (last - first > 2 && first[1] == 't') + { + t = parse_unqualified_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "std::"); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + } + } + return first; +} + +// ::= # e.g., ~T or ~decltype(f()) +// ::= # e.g., ~A<2*N> + +template +const char* +parse_destructor_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_unresolved_type(first, last, db); + if (t == first) + t = parse_simple_id(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "~"); + first = t; + } + } + return first; +} + +// ::= # unresolved name +// extension ::= # unresolved operator-function-id +// extension ::= # unresolved operator template-id +// ::= on # unresolved operator-function-id +// ::= on # unresolved operator template-id +// ::= dn # destructor or pseudo-destructor; +// # e.g. ~X or ~X + +template +const char* +parse_base_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n') + { + if (first[0] == 'o') + { + const char* t = parse_operator_name(first+2, last, db); + if (t != first+2) + { + first = parse_template_args(t, last, db); + if (first != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + { + const char* t = parse_destructor_name(first+2, last, db); + if (t != first+2) + first = t; + } + } + else + { + const char* t = parse_simple_id(first, last, db); + if (t == first) + { + t = parse_operator_name(first, last, db); + if (t != first) + { + first = parse_template_args(t, last, db); + if (first != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + first = t; + } + } + return first; +} + +// ::= + +template +const char* +parse_unresolved_qualifier_level(const char* first, const char* last, C& db) +{ + return parse_simple_id(first, last, db); +} + +// +// extension ::= srN [] * E +// ::= [gs] # x or (with "gs") ::x +// ::= [gs] sr + E +// # A::x, N::y, A::z; "gs" means leading "::" +// ::= sr # T::x / decltype(p)::x +// extension ::= sr +// # T::N::x /decltype(p)::N::x +// (ignored) ::= srN + E + +template +const char* +parse_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first > 2) + { + const char* t = first; + bool global = false; + if (t[0] == 'g' && t[1] == 's') + { + global = true; + t += 2; + } + const char* t2 = parse_base_unresolved_name(t, last, db); + if (t2 != t) + { + if (global) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + first = t2; + } + else if (last - t > 2 && t[0] == 's' && t[1] == 'r') + { + if (t[2] == 'N') + { + t += 3; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + if (t == last) + { + db.names.pop_back(); + return first; + } + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t += 2; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 != t) + { + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + } + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + if (global) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + } + } + } + return first; +} + +// dt # expr.name + +template +const char* +parse_dot_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_unresolved_name(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "." + name; + first = t1; + } + } + } + return first; +} + +// cl + E # call + +template +const char* +parse_call_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'c' && first[1] == 'l') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (t == last) + return first; + if (db.names.empty()) + return first; + db.names.back().first += db.names.back().second; + db.names.back().second = typename C::String(); + db.names.back().first.append("("); + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + if (!first_expr) + { + db.names.back().first.append(", "); + first_expr = false; + } + db.names.back().first.append(tmp); + } + t = t1; + } + ++t; + if (db.names.empty()) + return first; + db.names.back().first.append(")"); + first = t; + } + } + return first; +} + +// [gs] nw * _ E # new (expr-list) type +// [gs] nw * _ # new (expr-list) type (init) +// [gs] na * _ E # new[] (expr-list) type +// [gs] na * _ # new[] (expr-list) type (init) +// ::= pi * E # parenthesized initialization + +template +const char* +parse_new_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a')) + { + bool is_array = t[1] == 'a'; + t += 2; + if (t == last) + return first; + bool has_expr_list = false; + bool first_expr = true; + while (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + has_expr_list = true; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + ++t; + const char* t1 = parse_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + bool has_init = false; + if (last - t >= 3 && t[0] == 'p' && t[1] == 'i') + { + t += 2; + has_init = true; + first_expr = true; + while (*t != 'E') + { + t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + if (*t != 'E') + return first; + typename C::String init_list; + if (has_init) + { + if (db.names.empty()) + return first; + init_list = db.names.back().move_full(); + db.names.pop_back(); + } + if (db.names.empty()) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + typename C::String expr_list; + if (has_expr_list) + { + if (db.names.empty()) + return first; + expr_list = db.names.back().move_full(); + db.names.pop_back(); + } + typename C::String r; + if (parsed_gs) + r = "::"; + if (is_array) + r += "[] "; + else + r += " "; + if (has_expr_list) + r += "(" + expr_list + ") "; + r += type; + if (has_init) + r += " (" + init_list + ")"; + db.names.push_back(std::move(r)); + first = t+1; + } + } + return first; +} + +// cv # conversion with one argument +// cv _ * E # conversion with a different number of arguments + +template +const char* +parse_conversion_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'v') + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2 && t != last) + { + if (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t) + return first; + t = t1; + } + else + { + ++t; + if (t == last) + return first; + if (*t == 'E') + db.names.emplace_back(); + else + { + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + ++t; + } + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; + first = t; + } + } + return first; +} + +// pt # expr->name + +template +const char* +parse_arrow_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'p' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "->"; + db.names.back().first += tmp; + first = t1; + } + } + } + return first; +} + +// ::= R # & ref-qualifier +// ::= O # && ref-qualifier + +// ::= F [Y] [] E + +template +const char* +parse_function_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'F') + { + const char* t = first+1; + if (t != last) + { + if (*t == 'Y') + { + /* extern "C" */ + if (++t == last) + return first; + } + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + t = t1; + typename C::String sig("("); + int ref_qual = 0; + while (true) + { + if (t == last) + { + db.names.pop_back(); + return first; + } + if (*t == 'E') + { + ++t; + break; + } + if (*t == 'v') + { + ++t; + continue; + } + if (*t == 'R' && t+1 != last && t[1] == 'E') + { + ref_qual = 1; + ++t; + continue; + } + if (*t == 'O' && t+1 != last && t[1] == 'E') + { + ref_qual = 2; + ++t; + continue; + } + size_t k0 = db.names.size(); + t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 == t || t1 == last) + return first; + for (size_t k = k0; k < k1; ++k) + { + if (sig.size() > 1) + sig += ", "; + sig += db.names[k].move_full(); + } + for (size_t k = k0; k < k1; ++k) + db.names.pop_back(); + t = t1; + } + sig += ")"; + switch (ref_qual) + { + case 1: + sig += " &"; + break; + case 2: + sig += " &&"; + break; + } + if (db.names.empty()) + return first; + db.names.back().first += " "; + db.names.back().second.insert(0, sig); + first = t; + } + } + } + return first; +} + +// ::= M + +template +const char* +parse_pointer_to_member_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'M') + { + const char* t = parse_type(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto func = std::move(db.names.back()); + db.names.pop_back(); + auto class_type = std::move(db.names.back()); + if (!func.second.empty() && func.second.front() == '(') + { + db.names.back().first = std::move(func.first) + "(" + class_type.move_full() + "::*"; + db.names.back().second = ")" + std::move(func.second); + } + else + { + db.names.back().first = std::move(func.first) + " " + class_type.move_full() + "::*"; + db.names.back().second = std::move(func.second); + } + first = t2; + } + } + } + return first; +} + +// ::= A _ +// ::= A [] _ + +template +const char* +parse_array_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'A' && first+1 != last) + { + if (first[1] == '_') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " []"); + first = t; + } + } + else if ('1' <= first[1] && first[1] <= '9') + { + const char* t = parse_number(first+1, last); + if (t != last && *t == '_') + { + const char* t2 = parse_type(t+1, last, db); + if (t2 != t+1) + { + if (db.names.empty()) + return first; + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]"); + first = t2; + } + } + } + else + { + const char* t = parse_expression(first+1, last, db); + if (t != first+1 && t != last && *t == '_') + { + const char* t2 = parse_type(++t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto type = std::move(db.names.back()); + db.names.pop_back(); + auto expr = std::move(db.names.back()); + db.names.back().first = std::move(type.first); + if (type.second.substr(0, 2) == " [") + type.second.erase(0, 1); + db.names.back().second = " [" + expr.move_full() + "]" + std::move(type.second); + first = t2; + } + } + } + } + return first; +} + +// ::= Dt E # decltype of an id-expression or class member access (C++0x) +// ::= DT E # decltype of an expression (C++0x) + +template +const char* +parse_decltype(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'D') + { + switch (first[1]) + { + case 't': + case 'T': + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + { + if (db.names.empty()) + return first; + db.names.back() = "decltype(" + db.names.back().move_full() + ")"; + first = t+1; + } + } + break; + } + } + return first; +} + +// extension: +// ::= Dv _ +// +// ::= Dv [] _ +// ::= +// ::= p # AltiVec vector pixel + +template +const char* +parse_vector_type(const char* first, const char* last, C& db) +{ + if (last - first > 3 && first[0] == 'D' && first[1] == 'v') + { + if ('1' <= first[2] && first[2] <= '9') + { + const char* t = parse_number(first+2, last); + if (t == last || *t != '_') + return first; + const char* num = first + 2; + size_t sz = static_cast(t - num); + if (++t != last) + { + if (*t != 'p') + { + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + if (db.names.empty()) + return first; + db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; + first = t1; + } + } + else + { + ++t; + db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]"); + first = t; + } + } + } + else + { + typename C::String num; + const char* t1 = first+2; + if (*t1 != '_') + { + const char* t = parse_expression(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + num = db.names.back().move_full(); + db.names.pop_back(); + t1 = t; + } + } + if (t1 != last && *t1 == '_' && ++t1 != last) + { + const char* t = parse_type(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + db.names.back().first += " vector[" + num + "]"; + first = t; + } + } + } + } + return first; +} + +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= P # pointer-to +// ::= R # reference-to +// ::= O # rvalue reference-to (C++0x) +// ::= C # complex pair (C 2000) +// ::= G # imaginary (C 2000) +// ::= Dp # pack expansion (C++0x) +// ::= U # vendor extended type qualifier +// extension := U # objc-type +// extension := # starts with Dv + +// ::= objcproto # k0 = 9 + + k1 +// := # PU<11+>objcproto 11objc_object 11objc_object -> id + +template +const char* +parse_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'r': + case 'V': + case 'K': + { + unsigned cv = 0; + const char* t = parse_cv_qualifiers(first, last, cv); + if (t != first) + { + bool is_function = *t == 'F'; + size_t k0 = db.names.size(); + const char* t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 != t) + { + if (is_function) + db.subs.pop_back(); + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (is_function) + { + size_t p = db.names[k].second.size(); + if (db.names[k].second[p-2] == '&') + p -= 3; + else if (db.names[k].second.back() == '&') + p -= 2; + if (cv & 1) + { + db.names[k].second.insert(p, " const"); + p += 6; + } + if (cv & 2) + { + db.names[k].second.insert(p, " volatile"); + p += 9; + } + if (cv & 4) + db.names[k].second.insert(p, " restrict"); + } + else + { + if (cv & 1) + db.names[k].first.append(" const"); + if (cv & 2) + db.names[k].first.append(" volatile"); + if (cv & 4) + db.names[k].first.append(" restrict"); + } + db.subs.back().push_back(db.names[k]); + } + first = t1; + } + } + } + break; + default: + { + const char* t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + switch (*first) + { + case 'A': + t = parse_array_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'C': + t = parse_type(first+1, last, db); + if (t != first+1) + { + if (db.names.empty()) + return first; + db.names.back().first.append(" complex"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'F': + t = parse_function_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'G': + t = parse_type(first+1, last, db); + if (t != first+1) + { + if (db.names.empty()) + return first; + db.names.back().first.append(" imaginary"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'M': + t = parse_pointer_to_member_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'O': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (!db.names[k].second.empty() && + db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'P': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (!db.names[k].second.empty() && + db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + if (first[1] != 'U' || db.names[k].first.substr(0, 12) != "objc_object<") + { + db.names[k].first.append("*"); + } + else + { + db.names[k].first.replace(0, 11, "id"); + } + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'R': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (!db.names[k].second.empty() && + db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + if (db.try_to_parse_template_args && k1 == k0+1) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t = t1; + } + } + first = t; + } + break; + } + case 'U': + if (first+1 != last) + { + t = parse_source_name(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + if (db.names.back().first.substr(0, 9) != "objcproto") + { + db.names.back() = type + " " + db.names.back().move_full(); + } + else + { + auto proto = db.names.back().move_full(); + db.names.pop_back(); + t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db); + if (t != proto.data() + 9) + { + db.names.back() = type + "<" + db.names.back().move_full() + ">"; + } + else + { + db.names.push_back(type + " " + proto); + } + } + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t2; + } + } + } + break; + case 'S': + if (first+1 != last && first[1] == 't') + { + t = parse_name(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + else + { + t = parse_substitution(first, last, db); + if (t != first) + { + first = t; + // Parsed a substitution. If the substitution is a + // it might be followed by . + t = parse_template_args(first, last, db); + if (t != first) + { + if (db.names.size() < 2) + return first; + auto template_args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += template_args; + // Need to create substitution for + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'p': + { + size_t k0 = db.names.size(); + t = parse_type(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + first = t; + return first; + } + break; + } + case 't': + case 'T': + t = parse_decltype(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + case 'v': + t = parse_vector_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + } + } + // drop through + default: + // must check for builtin-types before class-enum-types to avoid + // ambiguities with operator-names + t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + t = parse_name(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + break; + } + } + break; + } + } + } + return first; +} + +// +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= li # operator "" +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= v # vendor extended operator + +template +const char* +parse_operator_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + switch (first[0]) + { + case 'a': + switch (first[1]) + { + case 'a': + db.names.push_back("operator&&"); + first += 2; + break; + case 'd': + case 'n': + db.names.push_back("operator&"); + first += 2; + break; + case 'N': + db.names.push_back("operator&="); + first += 2; + break; + case 'S': + db.names.push_back("operator="); + first += 2; + break; + } + break; + case 'c': + switch (first[1]) + { + case 'l': + db.names.push_back("operator()"); + first += 2; + break; + case 'm': + db.names.push_back("operator,"); + first += 2; + break; + case 'o': + db.names.push_back("operator~"); + first += 2; + break; + case 'v': + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator "); + db.parsed_ctor_dtor_cv = true; + first = t; + } + } + break; + } + break; + case 'd': + switch (first[1]) + { + case 'a': + db.names.push_back("operator delete[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator*"); + first += 2; + break; + case 'l': + db.names.push_back("operator delete"); + first += 2; + break; + case 'v': + db.names.push_back("operator/"); + first += 2; + break; + case 'V': + db.names.push_back("operator/="); + first += 2; + break; + } + break; + case 'e': + switch (first[1]) + { + case 'o': + db.names.push_back("operator^"); + first += 2; + break; + case 'O': + db.names.push_back("operator^="); + first += 2; + break; + case 'q': + db.names.push_back("operator=="); + first += 2; + break; + } + break; + case 'g': + switch (first[1]) + { + case 'e': + db.names.push_back("operator>="); + first += 2; + break; + case 't': + db.names.push_back("operator>"); + first += 2; + break; + } + break; + case 'i': + if (first[1] == 'x') + { + db.names.push_back("operator[]"); + first += 2; + } + break; + case 'l': + switch (first[1]) + { + case 'e': + db.names.push_back("operator<="); + first += 2; + break; + case 'i': + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator\"\" "); + first = t; + } + } + break; + case 's': + db.names.push_back("operator<<"); + first += 2; + break; + case 'S': + db.names.push_back("operator<<="); + first += 2; + break; + case 't': + db.names.push_back("operator<"); + first += 2; + break; + } + break; + case 'm': + switch (first[1]) + { + case 'i': + db.names.push_back("operator-"); + first += 2; + break; + case 'I': + db.names.push_back("operator-="); + first += 2; + break; + case 'l': + db.names.push_back("operator*"); + first += 2; + break; + case 'L': + db.names.push_back("operator*="); + first += 2; + break; + case 'm': + db.names.push_back("operator--"); + first += 2; + break; + } + break; + case 'n': + switch (first[1]) + { + case 'a': + db.names.push_back("operator new[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator!="); + first += 2; + break; + case 'g': + db.names.push_back("operator-"); + first += 2; + break; + case 't': + db.names.push_back("operator!"); + first += 2; + break; + case 'w': + db.names.push_back("operator new"); + first += 2; + break; + } + break; + case 'o': + switch (first[1]) + { + case 'o': + db.names.push_back("operator||"); + first += 2; + break; + case 'r': + db.names.push_back("operator|"); + first += 2; + break; + case 'R': + db.names.push_back("operator|="); + first += 2; + break; + } + break; + case 'p': + switch (first[1]) + { + case 'm': + db.names.push_back("operator->*"); + first += 2; + break; + case 'l': + db.names.push_back("operator+"); + first += 2; + break; + case 'L': + db.names.push_back("operator+="); + first += 2; + break; + case 'p': + db.names.push_back("operator++"); + first += 2; + break; + case 's': + db.names.push_back("operator+"); + first += 2; + break; + case 't': + db.names.push_back("operator->"); + first += 2; + break; + } + break; + case 'q': + if (first[1] == 'u') + { + db.names.push_back("operator?"); + first += 2; + } + break; + case 'r': + switch (first[1]) + { + case 'm': + db.names.push_back("operator%"); + first += 2; + break; + case 'M': + db.names.push_back("operator%="); + first += 2; + break; + case 's': + db.names.push_back("operator>>"); + first += 2; + break; + case 'S': + db.names.push_back("operator>>="); + first += 2; + break; + } + break; + case 'v': + if (std::isdigit(first[1])) + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator "); + first = t; + } + } + break; + } + } + return first; +} + +template +const char* +parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& db) +{ + const char* t = parse_number(first, last); + if (t != first && t != last && *t == 'E') + { + if (lit.size() > 3) + db.names.push_back("(" + lit + ")"); + else + db.names.emplace_back(); + if (*first == 'n') + { + db.names.back().first += '-'; + ++first; + } + db.names.back().first.append(first, t); + if (lit.size() <= 3) + db.names.back().first += lit; + first = t+1; + } + return first; +} + +// ::= L E # integer literal +// ::= L E # floating literal +// ::= L E # string literal +// ::= L E # nullptr literal (i.e., "LDnE") +// ::= L _ E # complex floating point literal (C 2000) +// ::= L E # external name + +template +const char* +parse_expr_primary(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && *first == 'L') + { + switch (first[1]) + { + case 'w': + { + const char* t = parse_integer_literal(first+2, last, "wchar_t", db); + if (t != first+2) + first = t; + } + break; + case 'b': + if (first[3] == 'E') + { + switch (first[2]) + { + case '0': + db.names.push_back("false"); + first += 4; + break; + case '1': + db.names.push_back("true"); + first += 4; + break; + } + } + break; + case 'c': + { + const char* t = parse_integer_literal(first+2, last, "char", db); + if (t != first+2) + first = t; + } + break; + case 'a': + { + const char* t = parse_integer_literal(first+2, last, "signed char", db); + if (t != first+2) + first = t; + } + break; + case 'h': + { + const char* t = parse_integer_literal(first+2, last, "unsigned char", db); + if (t != first+2) + first = t; + } + break; + case 's': + { + const char* t = parse_integer_literal(first+2, last, "short", db); + if (t != first+2) + first = t; + } + break; + case 't': + { + const char* t = parse_integer_literal(first+2, last, "unsigned short", db); + if (t != first+2) + first = t; + } + break; + case 'i': + { + const char* t = parse_integer_literal(first+2, last, "", db); + if (t != first+2) + first = t; + } + break; + case 'j': + { + const char* t = parse_integer_literal(first+2, last, "u", db); + if (t != first+2) + first = t; + } + break; + case 'l': + { + const char* t = parse_integer_literal(first+2, last, "l", db); + if (t != first+2) + first = t; + } + break; + case 'm': + { + const char* t = parse_integer_literal(first+2, last, "ul", db); + if (t != first+2) + first = t; + } + break; + case 'x': + { + const char* t = parse_integer_literal(first+2, last, "ll", db); + if (t != first+2) + first = t; + } + break; + case 'y': + { + const char* t = parse_integer_literal(first+2, last, "ull", db); + if (t != first+2) + first = t; + } + break; + case 'n': + { + const char* t = parse_integer_literal(first+2, last, "__int128", db); + if (t != first+2) + first = t; + } + break; + case 'o': + { + const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db); + if (t != first+2) + first = t; + } + break; + case 'f': + { + const char* t = parse_floating_number(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'd': + { + const char* t = parse_floating_number(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'e': + { + const char* t = parse_floating_number(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case '_': + if (first[2] == 'Z') + { + const char* t = parse_encoding(first+3, last, db); + if (t != first+3 && t != last && *t == 'E') + first = t+1; + } + break; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + break; + default: + { + // might be named type + const char* t = parse_type(first+1, last, db); + if (t != first+1 && t != last) + { + if (*t != 'E') + { + const char* n = t; + for (; n != last && isdigit(*n); ++n) + ; + if (n != t && n != last && *n == 'E') + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); + first = n+1; + break; + } + } + else + { + first = t+1; + break; + } + } + } + } + } + return first; +} + +template +String +base_name(String& s) +{ + if (s.empty()) + return s; + if (s == "std::string") + { + s = "std::basic_string, std::allocator >"; + return "basic_string"; + } + if (s == "std::istream") + { + s = "std::basic_istream >"; + return "basic_istream"; + } + if (s == "std::ostream") + { + s = "std::basic_ostream >"; + return "basic_ostream"; + } + if (s == "std::iostream") + { + s = "std::basic_iostream >"; + return "basic_iostream"; + } + const char* const pf = s.data(); + const char* pe = pf + s.size(); + if (pe[-1] == '>') + { + unsigned c = 1; + while (true) + { + if (--pe == pf) + return String(); + if (pe[-1] == '<') + { + if (--c == 0) + { + --pe; + break; + } + } + else if (pe[-1] == '>') + ++c; + } + } + const char* p0 = pe - 1; + for (; p0 != pf; --p0) + { + if (*p0 == ':') + { + ++p0; + break; + } + } + return String(p0, pe); +} + +// ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C5 # ? +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D5 # ? + +template +const char* +parse_ctor_dtor_name(const char* first, const char* last, C& db) +{ + if (last-first >= 2 && !db.names.empty()) + { + switch (first[0]) + { + case 'C': + switch (first[1]) + { + case '1': + case '2': + case '3': + case '5': + if (db.names.empty()) + return first; + db.names.push_back(base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + case 'D': + switch (first[1]) + { + case '0': + case '1': + case '2': + case '5': + if (db.names.empty()) + return first; + db.names.push_back("~" + base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + } + } + return first; +} + +// ::= Ut [ ] _ +// ::= +// +// ::= Ul E [ ] _ +// +// ::= + # Parameter types or "v" if the lambda has no parameters + +template +const char* +parse_unnamed_type_name(const char* first, const char* last, C& db) +{ + if (last - first > 2 && first[0] == 'U') + { + char type = first[1]; + switch (type) + { + case 't': + { + db.names.push_back(typename C::String("'unnamed")); + const char* t0 = first+2; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.append(t0, t1); + t0 = t1; + } + db.names.back().first.push_back('\''); + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + case 'l': + { + db.names.push_back(typename C::String("'lambda'(")); + const char* t0 = first+2; + if (first[2] == 'v') + { + db.names.back().first += ')'; + ++t0; + } + else + { + const char* t1 = parse_type(t0, last, db); + if (t1 == t0) + { + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append(tmp); + t0 = t1; + while (true) + { + t1 = parse_type(t0, last, db); + if (t1 == t0) + break; + if (db.names.size() < 2) + return first; + tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + } + t0 = t1; + } + db.names.back().first.append(")"); + } + if (t0 == last || *t0 != 'E') + { + db.names.pop_back(); + return first; + } + ++t0; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.insert(db.names.back().first.begin()+7, t0, t1); + t0 = t1; + } + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + } + } + return first; +} + +// ::= +// ::= +// ::= +// ::= + +template +const char* +parse_unqualified_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'C': + case 'D': + t = parse_ctor_dtor_name(first, last, db); + if (t != first) + first = t; + break; + case 'U': + t = parse_unnamed_type_name(first, last, db); + if (t != first) + first = t; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + t = parse_source_name(first, last, db); + if (t != first) + first = t; + break; + default: + t = parse_operator_name(first, last, db); + if (t != first) + first = t; + break; + }; + } + return first; +} + +// ::= +// ::= St # ::std:: +// extension ::= StL + +template +const char* +parse_unscoped_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t0 = first; + bool St = false; + if (first[0] == 'S' && first[1] == 't') + { + t0 += 2; + St = true; + if (t0 != last && *t0 == 'L') + ++t0; + } + const char* t1 = parse_unqualified_name(t0, last, db); + if (t1 != t0) + { + if (St) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "std::"); + } + first = t1; + } + } + return first; +} + +// at # alignof (a type) + +template +const char* +parse_alignof_type(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// az # alignof (a expression) + +template +const char* +parse_alignof_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +template +const char* +parse_noexcept_expression(const char* first, const char* last, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + if (db.names.empty()) + return first; + db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template +const char* +parse_prefix_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + if (db.names.empty()) + return first; + db.names.back().first = op + "(" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template +const char* +parse_binary_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + if (db.names.size() < 2) + return first; + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + auto& nm = db.names.back().first; + nm.clear(); + if (op == ">") + nm += '('; + nm += "(" + op1 + ") " + op + " (" + op2 + ")"; + if (op == ">") + nm += ')'; + first = t2; + } + else + db.names.pop_back(); + } + return first; +} + +// ::= +// ::= +// ::= +// ::= cl + E # call +// ::= cv # conversion with one argument +// ::= cv _ * E # conversion with a different number of arguments +// ::= [gs] nw * _ E # new (expr-list) type +// ::= [gs] nw * _ # new (expr-list) type (init) +// ::= [gs] na * _ E # new[] (expr-list) type +// ::= [gs] na * _ # new[] (expr-list) type (init) +// ::= [gs] dl # delete expression +// ::= [gs] da # delete[] expression +// ::= pp_ # prefix ++ +// ::= mm_ # prefix -- +// ::= ti # typeid (type) +// ::= te # typeid (expression) +// ::= dc # dynamic_cast (expression) +// ::= sc # static_cast (expression) +// ::= cc # const_cast (expression) +// ::= rc # reinterpret_cast (expression) +// ::= st # sizeof (a type) +// ::= sz # sizeof (an expression) +// ::= at # alignof (a type) +// ::= az # alignof (an expression) +// ::= nx # noexcept (expression) +// ::= +// ::= +// ::= dt # expr.name +// ::= pt # expr->name +// ::= ds # expr.*expr +// ::= sZ # size of a parameter pack +// ::= sZ # size of a function parameter pack +// ::= sp # pack expansion +// ::= tw # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= + +template +const char* +parse_expression(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t = first; + bool parsed_gs = false; + if (last - first >= 4 && t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + switch (*t) + { + case 'L': + first = parse_expr_primary(first, last, db); + break; + case 'T': + first = parse_template_param(first, last, db); + break; + case 'f': + first = parse_function_param(first, last, db); + break; + case 'a': + switch (t[1]) + { + case 'a': + t = parse_binary_expression(first+2, last, "&&", db); + if (t != first+2) + first = t; + break; + case 'd': + t = parse_prefix_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'n': + t = parse_binary_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'N': + t = parse_binary_expression(first+2, last, "&=", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "=", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_alignof_type(first, last, db); + break; + case 'z': + first = parse_alignof_expr(first, last, db); + break; + } + break; + case 'c': + switch (t[1]) + { + case 'c': + first = parse_const_cast_expr(first, last, db); + break; + case 'l': + first = parse_call_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, ",", db); + if (t != first+2) + first = t; + break; + case 'o': + t = parse_prefix_expression(first+2, last, "~", db); + if (t != first+2) + first = t; + break; + case 'v': + first = parse_conversion_expr(first, last, db); + break; + } + break; + case 'd': + switch (t[1]) + { + case 'a': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + if (db.names.empty()) + return first; + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete[] " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'c': + first = parse_dynamic_cast_expr(first, last, db); + break; + case 'e': + t = parse_prefix_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'l': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + if (db.names.empty()) + return first; + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'n': + return parse_unresolved_name(first, last, db); + case 's': + first = parse_dot_star_expr(first, last, db); + break; + case 't': + first = parse_dot_expr(first, last, db); + break; + case 'v': + t = parse_binary_expression(first+2, last, "/", db); + if (t != first+2) + first = t; + break; + case 'V': + t = parse_binary_expression(first+2, last, "/=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'e': + switch (t[1]) + { + case 'o': + t = parse_binary_expression(first+2, last, "^", db); + if (t != first+2) + first = t; + break; + case 'O': + t = parse_binary_expression(first+2, last, "^=", db); + if (t != first+2) + first = t; + break; + case 'q': + t = parse_binary_expression(first+2, last, "==", db); + if (t != first+2) + first = t; + break; + } + break; + case 'g': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, ">=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, ">", db); + if (t != first+2) + first = t; + break; + } + break; + case 'i': + if (t[1] == 'x') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + if (db.names.size() < 2) + return first; + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ")[" + op2 + "]"; + first = t2; + } + else + db.names.pop_back(); + } + } + break; + case 'l': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, "<=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, "<<", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "<<=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, "<", db); + if (t != first+2) + first = t; + break; + } + break; + case 'm': + switch (t[1]) + { + case 'i': + t = parse_binary_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 'I': + t = parse_binary_expression(first+2, last, "-=", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "*=", db); + if (t != first+2) + first = t; + break; + case 'm': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "--", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")--"; + first = t1; + } + } + break; + } + break; + case 'n': + switch (t[1]) + { + case 'a': + case 'w': + first = parse_new_expr(first, last, db); + break; + case 'e': + t = parse_binary_expression(first+2, last, "!=", db); + if (t != first+2) + first = t; + break; + case 'g': + t = parse_prefix_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_prefix_expression(first+2, last, "!", db); + if (t != first+2) + first = t; + break; + case 'x': + t = parse_noexcept_expression(first+2, last, db); + if (t != first+2) + first = t; + break; + } + break; + case 'o': + switch (t[1]) + { + case 'n': + return parse_unresolved_name(first, last, db); + case 'o': + t = parse_binary_expression(first+2, last, "||", db); + if (t != first+2) + first = t; + break; + case 'r': + t = parse_binary_expression(first+2, last, "|", db); + if (t != first+2) + first = t; + break; + case 'R': + t = parse_binary_expression(first+2, last, "|=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'p': + switch (t[1]) + { + case 'm': + t = parse_binary_expression(first+2, last, "->*", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "+=", db); + if (t != first+2) + first = t; + break; + case 'p': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "++", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")++"; + first = t1; + } + } + break; + case 's': + t = parse_prefix_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_arrow_expr(first, last, db); + break; + } + break; + case 'q': + if (t[1] == 'u') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + const char* t3 = parse_expression(t2, last, db); + if (t3 != t2) + { + if (db.names.size() < 3) + return first; + auto op3 = db.names.back().move_full(); + db.names.pop_back(); + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ") ? (" + op2 + ") : (" + op3 + ")"; + first = t3; + } + else + { + db.names.pop_back(); + db.names.pop_back(); + } + } + else + db.names.pop_back(); + } + } + break; + case 'r': + switch (t[1]) + { + case 'c': + first = parse_reinterpret_cast_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, "%", db); + if (t != first+2) + first = t; + break; + case 'M': + t = parse_binary_expression(first+2, last, "%=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, ">>", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, ">>=", db); + if (t != first+2) + first = t; + break; + } + break; + case 's': + switch (t[1]) + { + case 'c': + first = parse_static_cast_expr(first, last, db); + break; + case 'p': + first = parse_pack_expansion(first, last, db); + break; + case 'r': + return parse_unresolved_name(first, last, db); + case 't': + first = parse_sizeof_type_expr(first, last, db); + break; + case 'z': + first = parse_sizeof_expr_expr(first, last, db); + break; + case 'Z': + if (last - t >= 3) + { + switch (t[2]) + { + case 'T': + first = parse_sizeof_param_pack_expr(first, last, db); + break; + case 'f': + first = parse_sizeof_function_param_pack_expr(first, last, db); + break; + } + } + break; + } + break; + case 't': + switch (t[1]) + { + case 'e': + case 'i': + first = parse_typeid_expr(first, last, db); + break; + case 'r': + db.names.push_back("throw"); + first += 2; + break; + case 'w': + first = parse_throw_expr(first, last, db); + break; + } + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parse_unresolved_name(first, last, db); + } + } + return first; +} + +// ::= # type or template +// ::= X E # expression +// ::= # simple expressions +// ::= J * E # argument pack +// ::= LZ E # extension + +template +const char* +parse_template_arg(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'X': + t = parse_expression(first+1, last, db); + if (t != first+1) + { + if (t != last && *t == 'E') + first = t+1; + } + break; + case 'J': + t = first+1; + if (t == last) + return first; + while (*t != 'E') + { + const char* t1 = parse_template_arg(t, last, db); + if (t1 == t) + return first; + t = t1; + } + first = t+1; + break; + case 'L': + // or LZ E + if (first+1 != last && first[1] == 'Z') + { + t = parse_encoding(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + first = t+1; + } + else + first = parse_expr_primary(first, last, db); + break; + default: + // + first = parse_type(first, last, db); + break; + } + } + return first; +} + +// ::= I * E +// extension, the abi says + + +template +const char* +parse_template_args(const char* first, const char* last, C& db) +{ + if (last - first >= 2 && *first == 'I') + { + if (db.tag_templates) + db.template_param.back().clear(); + const char* t = first+1; + typename C::String args("<"); + while (*t != 'E') + { + if (db.tag_templates) + db.template_param.emplace_back(db.names.get_allocator()); + size_t k0 = db.names.size(); + const char* t1 = parse_template_arg(t, last, db); + size_t k1 = db.names.size(); + if (db.tag_templates) + db.template_param.pop_back(); + if (t1 == t || t1 == last) + return first; + if (db.tag_templates) + { + db.template_param.back().emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.template_param.back().back().push_back(db.names[k]); + } + for (size_t k = k0; k < k1; ++k) + { + if (args.size() > 1) + args += ", "; + args += db.names[k].move_full(); + } + for (; k1 != k0; --k1) + db.names.pop_back(); + t = t1; + } + first = t + 1; + if (args.back() != '>') + args += ">"; + else + args += " >"; + db.names.push_back(std::move(args)); + + } + return first; +} + +// ::= N [] [] E +// ::= N [] [] E +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= +// ::= +// extension ::= L +// +// ::=