Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -82,13 +82,6 @@ check_include_file(mach-o/dyld.h HAVE_MACH_O_DYLD_H) check_include_file(histedit.h HAVE_HISTEDIT_H) -# size_t must be defined before including cxxabi.h on FreeBSD 10.0. -check_cxx_source_compiles(" -#include -#include -int main() { return 0; } -" HAVE_CXXABI_H) - # library checks if( NOT PURE_WINDOWS ) check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD) Index: include/llvm/Config/config.h.cmake =================================================================== --- include/llvm/Config/config.h.cmake +++ include/llvm/Config/config.h.cmake @@ -28,9 +28,6 @@ /* Define to 1 if you have the `closedir' function. */ #cmakedefine HAVE_CLOSEDIR ${HAVE_CLOSEDIR} -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_CXXABI_H ${HAVE_CXXABI_H} - /* Define to 1 if you have the header file. */ #undef HAVE_CRASHREPORTERCLIENT_H Index: include/llvm/Demangle/Demangle.h =================================================================== --- /dev/null +++ include/llvm/Demangle/Demangle.h @@ -0,0 +1,28 @@ +//===--- Demangle.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +namespace llvm { +/// This is a llvm local version of __cxa_demangle. Other than the name and +/// being in the llvm namespace it is identical. +/// +/// The mangled_name is demangled into buf and returned. If the buffer is not +/// large enough, realloc is used to expand it. +/// +/// The *status will be set to +/// unknown_error: -4 +/// invalid_args: -3 +/// invalid_mangled_name: -2 +/// memory_alloc_failure: -1 +/// success: 0 + +char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, + int *status); +} Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -1,5 +1,6 @@ # `Support' and `TableGen' libraries are added on the top-level CMakeLists.txt +add_subdirectory(Demangle) add_subdirectory(IR) add_subdirectory(IRReader) add_subdirectory(CodeGen) Index: lib/Demangle/CMakeLists.txt =================================================================== --- /dev/null +++ lib/Demangle/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMDemangle + ItaniumDemangle.cpp +) Index: lib/Demangle/ItaniumDemangle.cpp =================================================================== --- /dev/null +++ lib/Demangle/ItaniumDemangle.cpp @@ -0,0 +1,4431 @@ +//===- ItaniumDemangle.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. +// +//===----------------------------------------------------------------------===// + +#include + +// This file exports a single function: llvm::itanium_demangle. +// It also has no dependencies on the rest of llvm. It is implemented this way +// so that it can be easily reused in libcxxabi. + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +// snprintf is implemented in VS 2015 +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif + +enum { + unknown_error = -4, + invalid_args = -3, + invalid_mangled_name, + memory_alloc_failure, + success +}; + +template +static const char *parse_type(const char *first, const char *last, C &db); +template +static const char *parse_encoding(const char *first, const char *last, C &db); +template +static const char *parse_name(const char *first, const char *last, C &db, + bool *ends_with_template_args = 0); +template +static const char *parse_expression(const char *first, const char *last, C &db); +template +static const char *parse_template_args(const char *first, const char *last, + C &db); +template +static const char *parse_operator_name(const char *first, const char *last, + C &db); +template +static const char *parse_unqualified_name(const char *first, const char *last, + C &db); +template +static const char *parse_decltype(const char *first, const char *last, C &db); + +// ::= [n] + +static 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; +} + +namespace { +template struct float_data; + +template <> struct float_data { + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static const char *spec; +}; +const char *float_data::spec = "%af"; + +template <> struct float_data { + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static const char *spec; +}; + +const char *float_data::spec = "%a"; + +template <> struct float_data { +#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ + defined(__wasm__) + static const size_t mangled_size = 32; +#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) + 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 const char *spec; +}; + +const char *float_data::spec = "%LaL"; +} + +template +static 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 +static 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 +static 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 +static 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] + +static 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 +static 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 +static 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(); + if (db.names.empty()) + return first; + db.names.back() = + "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// dc # dynamic_cast +// (expression) + +template +static 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(); + if (db.names.empty()) + return first; + db.names.back() = + "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// rc # reinterpret_cast +// (expression) + +template +static 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(); + if (db.names.empty()) + return first; + db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sc # static_cast +// (expression) + +template +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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(); + if (db.names.empty()) + return first; + db.names.back().first += "." + name; + first = t1; + } + } + } + return first; +} + +// cl + E # call + +template +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 static 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; + } + } + if (pe - pf <= 1) + return String(); + 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 +static 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 +static 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) { + if (!db.names.empty()) + 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; + } + if (db.names.empty()) + return first; + db.names.back().first.append(")"); + } + if (t0 == last || *t0 != 'E') { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + ++t0; + if (t0 == last) { + if (!db.names.empty()) + 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 != '_') { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + first = t0 + 1; + } break; + } + } + return first; +} + +// ::= +// ::= +// ::= +// ::= + +template +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 +static 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 if (!db.names.empty()) + 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 +static 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 if (!db.names.empty()) + 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 { + if (db.names.size() < 2) + return first; + db.names.pop_back(); + db.names.pop_back(); + } + } else if (!db.names.empty()) + 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 +static 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 +static 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) + if (!db.names.empty()) + 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 +// +// ::=