blob: e3b4c06a8a24c39260604145970d6c30ce102f0d [file] [log] [blame]
/*
* Ported from LLVM's libcxxabi trunk/src/cxa_demangle.cpp
* LICENSE.TXT contents is available as ../THIRDPARTYLICENSE
*
* 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.
*
*/
/*
* Copyright 2018 Jason King.
*/
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <note.h>
#include <string.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/isa_defs.h>
#include <sys/debug.h>
#include "demangle-sys.h"
#include "demangle_int.h"
#include "cxx.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
#endif
#define CPP_QUAL_CONST (1U)
#define CPP_QUAL_VOLATILE (2U)
#define CPP_QUAL_RESTRICT (4U)
typedef struct cpp_db_s {
sysdem_ops_t *cpp_ops;
jmp_buf cpp_jmp;
name_t cpp_name;
sub_t cpp_subs;
templ_t cpp_templ;
unsigned cpp_cv;
unsigned cpp_ref;
unsigned cpp_depth;
boolean_t cpp_parsed_ctor_dtor_cv;
boolean_t cpp_tag_templates;
boolean_t cpp_fix_forward_references;
boolean_t cpp_try_to_parse_template_args;
locale_t cpp_loc;
} cpp_db_t;
#define CK(x) \
do { \
if (!(x)) { \
longjmp(db->cpp_jmp, 1); \
} \
NOTE(CONSTCOND) \
} while (0)
#define TOP_L(db) (&(name_top(&(db)->cpp_name)->strp_l))
#define RLEN(f, l) ((size_t)((l) - (f)))
#define NAMT(db, n) (nlen(db) - n)
static inline boolean_t is_xdigit(int);
static boolean_t nempty(cpp_db_t *);
static size_t nlen(cpp_db_t *);
static void nadd_l(cpp_db_t *, const char *, size_t);
static void njoin(cpp_db_t *, size_t, const char *);
static void nfmt(cpp_db_t *, const char *, const char *);
static void save_top(cpp_db_t *, size_t);
static void sub(cpp_db_t *, size_t);
static boolean_t tempty(const cpp_db_t *);
static size_t ttlen(const cpp_db_t *);
static void tsub(cpp_db_t *, size_t);
static void tpush(cpp_db_t *);
static void tpop(cpp_db_t *);
static void tsave(cpp_db_t *, size_t);
static boolean_t db_init(cpp_db_t *, sysdem_ops_t *);
static void db_fini(cpp_db_t *);
static void dump(cpp_db_t *, FILE *);
static void demangle(const char *, const char *, cpp_db_t *);
static const char *parse_type(const char *, const char *, cpp_db_t *);
static const char *parse_builtin_type(const char *, const char *, cpp_db_t *);
static const char *parse_qual_type(const char *, const char *, cpp_db_t *);
static const char *parse_encoding(const char *, const char *, cpp_db_t *);
static const char *parse_dot_suffix(const char *, const char *, cpp_db_t *);
static const char *parse_block_invoke(const char *, const char *, cpp_db_t *);
static const char *parse_special_name(const char *, const char *, cpp_db_t *);
static const char *parse_name(const char *, const char *, boolean_t *,
cpp_db_t *);
static const char *parse_call_offset(const char *, const char *, locale_t);
static const char *parse_number(const char *, const char *, locale_t);
static const char *parse_nested_name(const char *, const char *, boolean_t *,
cpp_db_t *);
static const char *parse_local_name(const char *, const char *, boolean_t *,
cpp_db_t *);
static const char *parse_unscoped_name(const char *, const char *, cpp_db_t *);
static const char *parse_template_args(const char *, const char *, cpp_db_t *);
static const char *parse_substitution(const char *, const char *, cpp_db_t *);
static const char *parse_discriminator(const char *, const char *, locale_t);
static const char *parse_cv_qualifiers(const char *, const char *, unsigned *);
static const char *parse_template_param(const char *, const char *, cpp_db_t *);
static const char *parse_decltype(const char *, const char *, cpp_db_t *);
static const char *parse_template_args(const char *, const char *, cpp_db_t *);
static const char *parse_unqualified_name(const char *, const char *,
cpp_db_t *);
static const char *parse_template_arg(const char *, const char *, cpp_db_t *);
static const char *parse_expression(const char *, const char *, cpp_db_t *);
static const char *parse_expr_primary(const char *, const char *, cpp_db_t *);
static const char *parse_binary_expr(const char *, const char *,
const char *, cpp_db_t *);
static const char *parse_prefix_expr(const char *, const char *,
const char *, cpp_db_t *);
static const char *parse_gs(const char *, const char *, cpp_db_t *);
static const char *parse_idx_expr(const char *, const char *, cpp_db_t *);
static const char *parse_mm_expr(const char *, const char *, cpp_db_t *);
static const char *parse_pp_expr(const char *, const char *, cpp_db_t *);
static const char *parse_trinary_expr(const char *, const char *, cpp_db_t *);
static const char *parse_new_expr(const char *, const char *, cpp_db_t *);
static const char *parse_del_expr(const char *, const char *, cpp_db_t *);
static const char *parse_cast_expr(const char *, const char *, cpp_db_t *);
static const char *parse_sizeof_param_pack_expr(const char *, const char *,
cpp_db_t *);
static const char *parse_typeid_expr(const char *, const char *, cpp_db_t *);
static const char *parse_throw_expr(const char *, const char *, cpp_db_t *);
static const char *parse_dot_star_expr(const char *, const char *, cpp_db_t *);
static const char *parse_dot_expr(const char *, const char *, cpp_db_t *);
static const char *parse_call_expr(const char *, const char *, cpp_db_t *);
static const char *parse_arrow_expr(const char *, const char *, cpp_db_t *);
static const char *parse_conv_expr(const char *, const char *, cpp_db_t *);
static const char *parse_function_param(const char *, const char *, cpp_db_t *);
static const char *parse_base_unresolved_name(const char *, const char *,
cpp_db_t *);
static const char *parse_unresolved_name(const char *, const char *,
cpp_db_t *);
static const char *parse_noexcept_expr(const char *, const char *, cpp_db_t *);
static const char *parse_alignof(const char *, const char *, cpp_db_t *);
static const char *parse_sizeof(const char *, const char *, cpp_db_t *);
static const char *parse_unnamed_type_name(const char *, const char *,
cpp_db_t *);
static const char *parse_ctor_dtor_name(const char *, const char *, cpp_db_t *);
static const char *parse_source_name(const char *, const char *, cpp_db_t *);
static const char *parse_operator_name(const char *, const char *, cpp_db_t *);
static const char *parse_pack_expansion(const char *, const char *, cpp_db_t *);
static const char *parse_unresolved_type(const char *, const char *,
cpp_db_t *);
static const char *parse_unresolved_qualifier_level(const char *, const char *,
cpp_db_t *);
static const char *parse_destructor_name(const char *, const char *,
cpp_db_t *);
static const char *parse_function_type(const char *, const char *, cpp_db_t *);
static const char *parse_array_type(const char *, const char *, cpp_db_t *);
static const char *parse_pointer_to_member_type(const char *, const char *,
cpp_db_t *);
static const char *parse_vector_type(const char *, const char *, cpp_db_t *);
size_t cpp_name_max_depth = 1024; /* max depth of name stack */
char *
cpp_demangle(const char *src, sysdem_ops_t *ops)
{
char *result = NULL;
cpp_db_t db;
size_t srclen = strlen(src);
if (!db_init(&db, ops))
goto done;
if (setjmp(db.cpp_jmp) != 0)
goto done;
errno = 0;
demangle(src, src + srclen, &db);
if (errno == 0 && db.cpp_fix_forward_references &&
!templ_empty(&db.cpp_templ) &&
!sub_empty(&db.cpp_templ.tpl_items[0])) {
db.cpp_fix_forward_references = B_FALSE;
db.cpp_tag_templates = B_FALSE;
name_clear(&db.cpp_name);
sub_clear(&db.cpp_subs);
if (setjmp(db.cpp_jmp) != 0)
goto done;
demangle(src, src + srclen, &db);
if (db.cpp_fix_forward_references) {
errno = EINVAL;
goto done;
}
}
if (errno != 0)
goto done;
if (nempty(&db)) {
errno = EINVAL;
goto done;
}
njoin(&db, 1, "");
if (nlen(&db) > 0) {
str_t *s = TOP_L(&db);
char *res = zalloc(ops, s->str_len + 1);
if (res == NULL)
goto done;
(void) memcpy(res, s->str_s, s->str_len);
result = res;
}
done:
if (demangle_debug)
dump(&db, stdout);
db_fini(&db);
return (result);
}
static void
demangle(const char *first, const char *last, cpp_db_t *db)
{
const char *t = NULL;
if (first >= last) {
errno = EINVAL;
return;
}
if (first[0] != '_') {
t = parse_type(first, last, db);
if (t == first) {
errno = EINVAL;
return;
}
goto done;
}
if (last - first < 4) {
errno = EINVAL;
return;
}
if (first[1] == 'Z') {
t = parse_encoding(first + 2, last, db);
if (t != first + 2 && t != last && t[0] == '.') {
t = parse_dot_suffix(t, last, db);
if (nlen(db) > 1)
njoin(db, 2, "");
}
goto done;
}
if (first[1] != '_' || first[2] != '_' || first[3] != 'Z')
goto done;
t = parse_encoding(first + 4, last, db);
if (t != first + 4 && t != last)
t = parse_block_invoke(t, last, db);
done:
if (t != last)
errno = EINVAL;
}
static const char *
parse_dot_suffix(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last || first[0] != '.')
return (first);
if (nempty(db))
return (first);
nadd_l(db, first, RLEN(first, last));
nfmt(db, " ({0})", NULL);
return (last);
}
/*
* _block_invoke
* _block_invoke<digit>*
* _block_invoke_<digit>+
*/
static const char *
parse_block_invoke(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 13)
return (first);
const char test[] = "_block_invoke";
const char *t = first;
if (strncmp(first, test, sizeof (test) - 1) != 0)
return (first);
t += sizeof (test);
if (t == last)
goto done;
if (t[0] == '_') {
/* need at least one digit */
if (t + 1 == last || !isdigit_l(t[1], db->cpp_loc))
return (first);
t += 2;
}
while (t < last && isdigit_l(t[0], db->cpp_loc))
t++;
done:
if (nempty(db))
return (first);
nfmt(db, "invocation function for block in {0}", NULL);
return (t);
}
/*
* <encoding> ::= <function name><bare-function-type>
* ::= <data name>
* ::= <special name>
*/
static const char *
parse_encoding(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
const char *t = NULL;
const char *t2 = NULL;
unsigned cv = 0;
unsigned ref = 0;
boolean_t tag_templ_save = db->cpp_tag_templates;
if (++db->cpp_depth > 1)
db->cpp_tag_templates = B_TRUE;
if (first[0] == 'G' || first[0] == 'T') {
t = parse_special_name(first, last, db);
goto done;
}
boolean_t ends_with_template_args = B_FALSE;
t = parse_name(first, last, &ends_with_template_args, db);
if (t == first)
goto fail;
cv = db->cpp_cv;
ref = db->cpp_ref;
if (t == last || t[0] == 'E' || t[0] == '.')
goto done;
db->cpp_tag_templates = B_FALSE;
if (nempty(db) || str_length(TOP_L(db)) == 0)
goto fail;
if (!db->cpp_parsed_ctor_dtor_cv && ends_with_template_args) {
t2 = parse_type(t, last, db);
if (t2 == t || nlen(db) < 2)
goto fail;
str_pair_t *sp = name_top(&db->cpp_name);
if (str_length(&sp->strp_r) == 0)
(void) str_append(&sp->strp_l, " ", 1);
nfmt(db, "{0:L}{1:L}", "{1:R}{0:R}");
t = t2;
}
if (t == last || nempty(db))
goto fail;
size_t n = nlen(db);
if (t[0] == 'v') {
t++;
} else {
for (;;) {
t2 = parse_type(t, last, db);
if (t2 == t || t == last)
break;
t = t2;
}
}
/*
* a bit of a hack, but a template substitution can apparently be
* an empty string at the end of an argument list, so avoid
* <...., >
*/
if (NAMT(db, n) > 1 && str_pair_len(name_top(&db->cpp_name)) == 0)
name_pop(&db->cpp_name, NULL);
njoin(db, NAMT(db, n), ", ");
nfmt(db, "({0})", NULL);
str_t *s = TOP_L(db);
if (cv & CPP_QUAL_CONST) {
CK(str_append(s, " const", 0));
}
if (cv & CPP_QUAL_VOLATILE) {
CK(str_append(s, " volatile", 0));
}
if (cv & CPP_QUAL_RESTRICT) {
CK(str_append(s, " restrict", 0));
}
if (ref == 1) {
CK(str_append(s, " &", 0));
}
if (ref == 2) {
CK(str_append(s, " &&", 0));
}
nfmt(db, "{1:L}{0}{1:R}", NULL);
done:
db->cpp_tag_templates = tag_templ_save;
db->cpp_depth--;
return (t);
fail:
db->cpp_tag_templates = tag_templ_save;
db->cpp_depth--;
return (first);
}
/*
* <special-name> ::= TV <type> # virtual table
* ::= TT <type> # VTT structure (construction vtable index)
* ::= TI <type> # typeinfo structure
* ::= TS <type> # typeinfo name (null-terminated byte string)
* ::= Tc <call-offset> <call-offset> <base encoding>
* # base is the nominal target function of thunk
* # first call-offset is 'this' adjustment
* # second call-offset is result adjustment
* ::= T <call-offset> <base encoding>
* # base is the nominal target function of thunk
* ::= GV <object name> # Guard variable for one-time init
* # No <type>
* ::= TW <object name> # Thread-local wrapper
* ::= TH <object name> # Thread-local initialization
* extension ::= TC <first type> <number> _ <second type>
* # construction vtable for second-in-first
* extension ::= GR <object name> # reference temporary for object
*/
static const char *
parse_special_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = first;
const char *t1 = NULL;
size_t n = nlen(db);
if (last - first < 2)
return (first);
switch (t[0]) {
case 'T':
switch (t[1]) {
case 'V':
nadd_l(db, "vtable for", 0);
t = parse_type(first + 2, last, db);
break;
case 'T':
nadd_l(db, "VTT for", 0);
t = parse_type(first + 2, last, db);
break;
case 'I':
nadd_l(db, "typeinfo for", 0);
t = parse_type(first + 2, last, db);
break;
case 'S':
nadd_l(db, "typeinfo name for", 0);
t = parse_type(first + 2, last, db);
break;
case 'c':
nadd_l(db, "covariant return thunk to", 0);
t1 = parse_call_offset(first + 2, last, db->cpp_loc);
if (t1 == t)
return (first);
t = parse_call_offset(t1, last, db->cpp_loc);
if (t == t1)
return (first);
t1 = parse_encoding(t, last, db);
if (t1 == t)
return (first);
break;
case 'C':
t = parse_type(first + 2, last, db);
if (t == first + 2)
return (first);
t1 = parse_number(t, last, db->cpp_loc);
if (*t1 != '_')
return (first);
t = parse_type(t1 + 1, last, db);
if (t == t1 + 1 || nlen(db) < 2)
return (first);
nfmt(db, "construction vtable for {0}-in-{1}", NULL);
return (t);
case 'W':
nadd_l(db, "thread-local wrapper routine for", 0);
t = parse_name(first + 2, last, NULL, db);
break;
case 'H':
nadd_l(db, "thread-local initialization routine for",
0);
t = parse_name(first + 2, last, NULL, db);
break;
default:
if (first[1] == 'v') {
nadd_l(db, "virtual thunk to", 0);
} else {
nadd_l(db, "non-virtual thunk to", 0);
}
t = parse_call_offset(first + 1, last, db->cpp_loc);
if (t == first + 1)
return (first);
t1 = parse_encoding(t, last, db);
if (t == t1)
return (first);
t = t1;
break;
}
break;
case 'G':
switch (first[1]) {
case 'V':
nadd_l(db, "guard variable for", 0);
t = parse_name(first + 2, last, NULL, db);
break;
case 'R':
nadd_l(db, "reference temporary for", 0);
t = parse_name(first + 2, last, NULL, db);
break;
default:
return (first);
}
break;
default:
return (first);
}
size_t amt = NAMT(db, n);
if (t == first + 2 || amt < 2)
return (first);
njoin(db, amt, " ");
return (t);
}
/*
* <call-offset> ::= h <nv-offset> _
* ::= v <v-offset> _
*
* <nv-offset> ::= <offset number>
* # non-virtual base override
*
* <v-offset> ::= <offset number> _ <virtual offset number>
* # virtual base override, with vcall offset
*/
static const char *
parse_call_offset(const char *first, const char *last, locale_t loc)
{
VERIFY3P(first, <=, last);
const char *t = NULL;
const char *t1 = NULL;
if (first == last)
return (first);
if (first[0] != 'h' && first[0] != 'v')
return (first);
t = parse_number(first + 1, last, loc);
if (t == first + 1 || t == last || t[0] != '_')
return (first);
/* skip _ */
t++;
if (first[0] == 'h')
return (t);
t1 = parse_number(t, last, loc);
if (t == t1 || t1 == last || t1[0] != '_')
return (first);
/* skip _ */
t1++;
return (t1);
}
/*
* <name> ::= <nested-name> // N
* ::= <local-name> # See Scope Encoding below // Z
* ::= <unscoped-template-name> <template-args>
* ::= <unscoped-name>
*
* <unscoped-template-name> ::= <unscoped-name>
* ::= <substitution>
*/
static const char *
parse_name(const char *first, const char *last,
boolean_t *ends_with_template_args, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = first;
const char *t1 = NULL;
if (last - first < 2)
return (first);
/* extension: ignore L here */
if (t[0] == 'L')
t++;
switch (t[0]) {
case 'N':
t1 = parse_nested_name(t, last, ends_with_template_args, db);
return ((t == t1) ? first : t1);
case 'Z':
t1 = parse_local_name(t, last, ends_with_template_args, db);
return ((t == t1) ? first : t1);
}
/*
* <unscoped-name>
* <unscoped-name> <template-args>
* <substitution> <template-args>
*/
t1 = parse_unscoped_name(t, last, db);
/* <unscoped-name> */
if (t != t1 && t1[0] != 'I')
return (t1);
if (t == t1) {
t1 = parse_substitution(t, last, db);
if (t == t1 || t1 == last || t1[0] != 'I')
return (first);
} else {
save_top(db, 1);
}
t = parse_template_args(t1, last, db);
if (t1 == t || nlen(db) < 2)
return (first);
nfmt(db, "{1:L}{0}", "{1:R}");
if (ends_with_template_args != NULL)
*ends_with_template_args = B_TRUE;
return (t);
}
/* BEGIN CSTYLED */
/*
* <local-name> := Z <function encoding> E <entity name> [<discriminator>]
* := Z <function encoding> E s [<discriminator>]
* := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
*/
/* END CSTYLED */
const char *
parse_local_name(const char *first, const char *last,
boolean_t *ends_with_template_args, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = NULL;
const char *t1 = NULL;
const char *t2 = NULL;
if (first == last || first[0] != 'Z')
return (first);
t = parse_encoding(first + 1, last, db);
if (t == first + 1 || t == last || t[0] != 'E')
return (first);
VERIFY(!nempty(db));
/* skip E */
t++;
if (t[0] == 's') {
nfmt(db, "{0:L}::string literal", "{0:R}");
return (parse_discriminator(t, last, db->cpp_loc));
}
if (t[0] == 'd') {
t1 = parse_number(t + 1, last, db->cpp_loc);
if (t1[0] != '_')
return (first);
t1++;
} else {
t1 = t;
}
t2 = parse_name(t1, last, ends_with_template_args, db);
if (t2 == t1)
return (first);
nfmt(db, "{1:L}::{0}", "{1:R}");
/* parsed, but ignored */
if (t[0] != 'd')
t2 = parse_discriminator(t2, last, db->cpp_loc);
return (t2);
}
/* BEGIN CSTYLED */
/*
* <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
* ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
*
* <prefix> ::= <prefix> <unqualified-name>
* ::= <template-prefix> <template-args>
* ::= <template-param>
* ::= <decltype>
* ::= # empty
* ::= <substitution>
* ::= <prefix> <data-member-prefix>
* extension ::= L
*
* <template-prefix> ::= <prefix> <template unqualified-name>
* ::= <template-param>
* ::= <substitution>
*/
/* END CSTYLED */
static const char *
parse_nested_name(const char *first, const char *last,
boolean_t *ends_with_template_args, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last || first[0] != 'N')
return (first);
unsigned cv = 0;
const char *t = parse_cv_qualifiers(first + 1, last, &cv);
if (t == last)
return (first);
boolean_t more = B_FALSE;
switch (t[0]) {
case 'R':
db->cpp_ref = 1;
t++;
break;
case 'O':
db->cpp_ref = 2;
t++;
break;
case 'S':
if (last - first < 2 || t[1] != 't')
break;
if (last - first == 2)
return (first);
nadd_l(db, "std", 3);
more = B_TRUE;
t += 2;
break;
}
boolean_t pop_subs = B_FALSE;
boolean_t component_ends_with_template_args = B_FALSE;
while (t[0] != 'E' && t != last) {
const char *t1 = NULL;
size_t n = nlen(db);
component_ends_with_template_args = B_FALSE;
switch (t[0]) {
case 'S':
if (t + 1 != last && t[1] == 't')
break;
t1 = parse_substitution(t, last, db);
if (t1 == t || t1 == last || NAMT(db, n) != 1)
return (first);
if (!more) {
nfmt(db, "{0}", NULL);
} else {
VERIFY3U(nlen(db), >, 1);
nfmt(db, "{1:L}::{0}", "{1:R}");
save_top(db, 1);
}
more = B_TRUE;
pop_subs = B_TRUE;
t = t1;
continue;
case 'T':
t1 = parse_template_param(t, last, db);
if (t1 == t || t1 == last || NAMT(db, n) != 1)
return (first);
if (!more) {
nfmt(db, "{0}", NULL);
} else {
VERIFY3U(nlen(db), >, 1);
nfmt(db, "{1:L}::{0}", "{1:R}");
}
save_top(db, 1);
more = B_TRUE;
pop_subs = B_TRUE;
t = t1;
continue;
case 'D':
if (t + 1 != last && t[1] != 't' && t[1] != 'T')
break;
t1 = parse_decltype(t, last, db);
if (t1 == t || t1 == last || NAMT(db, n) != 1)
return (first);
if (!more) {
nfmt(db, "{0}", NULL);
} else {
VERIFY3U(nlen(db), >, 1);
nfmt(db, "{1:L}::{0}", "{1:R}");
}
save_top(db, 1);
more = B_TRUE;
pop_subs = B_TRUE;
t = t1;
continue;
case 'I':
/*
* Must have at least one component before
* <template-args>
*/
if (!more)
return (first);
t1 = parse_template_args(t, last, db);
if (t1 == t || t1 == last)
return (first);
VERIFY3U(nlen(db), >, 1);
nfmt(db, "{1:L}{0}", "{1:R}");
save_top(db, 1);
t = t1;
component_ends_with_template_args = B_TRUE;
continue;
case 'L':
if (t + 1 == last)
return (first);
t++;
continue;
default:
break;
}
t1 = parse_unqualified_name(t, last, db);
if (t1 == t || t1 == last || NAMT(db, n) != 1)
return (first);
if (!more) {
nfmt(db, "{0}", NULL);
} else {
VERIFY3U(nlen(db), >, 1);
nfmt(db, "{1:L}::{0}", "{1:R}");
}
save_top(db, 1);
more = B_TRUE;
pop_subs = B_TRUE;
t = t1;
}
/* need to parse at least one thing */
if (!more)
return (first);
db->cpp_cv = cv;
if (pop_subs && !sub_empty(&db->cpp_subs))
sub_pop(&db->cpp_subs);
if (ends_with_template_args != NULL)
*ends_with_template_args = component_ends_with_template_args;
if (t[0] != 'E')
return (first);
return (t + 1);
}
/*
* <template-arg> ::= <type> # type or template
* ::= X <expression> E # expression
* ::= <expr-primary> # simple expressions
* ::= J <template-arg>* E # argument pack
* ::= LZ <encoding> E # extension
*/
static const char *
parse_template_arg(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = NULL;
const char *t1 = NULL;
if (first == last)
return (first);
switch (first[0]) {
case 'X':
t = parse_expression(first + 1, last, db);
if (t == first + 1 || t[0] != 'E')
return (first);
/* E */
t++;
break;
case 'J':
t = first + 1;
if (t == last)
return (first);
while (t[0] != 'E') {
t1 = parse_template_arg(t, last, db);
if (t == t1)
return (first);
t = t1;
}
/* E */
t++;
break;
case 'L':
if (first + 1 == last || first[1] != 'Z') {
t = parse_expr_primary(first, last, db);
} else {
t = parse_encoding(first + 2, last, db);
if (t == first + 2 || t == last || t[0] != 'E')
return (first);
/* E */
t++;
}
break;
default:
t = parse_type(first, last, db);
}
return (t);
}
/* BEGIN CSTYLED */
/*
* <expression> ::= <unary operator-name> <expression>
* ::= <binary operator-name> <expression> <expression>
* ::= <ternary operator-name> <expression> <expression> <expression>
* ::= cl <expression>+ E # call
* ::= cv <type> <expression> # conversion with one argument
* ::= cv <type> _ <expression>* E # conversion with a different number of arguments
* ::= [gs] nw <expression>* _ <type> E # new (expr-list) type
* ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
* ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type
* ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
* ::= [gs] dl <expression> # delete expression
* ::= [gs] da <expression> # delete[] expression
* ::= pp_ <expression> # prefix ++
* ::= mm_ <expression> # prefix --
* ::= ti <type> # typeid (type)
* ::= te <expression> # typeid (expression)
* ::= dc <type> <expression> # dynamic_cast<type> (expression)
* ::= sc <type> <expression> # static_cast<type> (expression)
* ::= cc <type> <expression> # const_cast<type> (expression)
* ::= rc <type> <expression> # reinterpret_cast<type> (expression)
* ::= st <type> # sizeof (a type)
* ::= sz <expression> # sizeof (an expression)
* ::= at <type> # alignof (a type)
* ::= az <expression> # alignof (an expression)
* ::= nx <expression> # noexcept (expression)
* ::= <template-param>
* ::= <function-param>
* ::= dt <expression> <unresolved-name> # expr.name
* ::= pt <expression> <unresolved-name> # expr->name
* ::= ds <expression> <expression> # expr.*expr
* ::= sZ <template-param> # size of a parameter pack
* ::= sZ <function-param> # size of a function parameter pack
* ::= sp <expression> # pack expansion
* ::= tw <expression> # throw expression
* ::= tr # throw with no operand (rethrow)
* ::= <unresolved-name> # f(p), N::f(p), ::f(p),
* # freestanding dependent name (e.g., T::x),
* # objectless nonstatic member reference
* ::= <expr-primary>
*/
/* END CSTYLED */
#define PA(cd, arg, fn) { \
.code = cd, \
.p.parse_expr_arg = fn, \
.fntype = EXPR_ARG, \
.val = arg \
}
#define PN(cd, fn) { \
.code = cd, \
.p.parse_expr_noarg = fn, \
.fntype = EXPR_NOARG \
}
static struct {
const char code[3];
union {
const char *(*parse_expr_arg)(const char *, const char *,
const char *, cpp_db_t *);
const char *(*parse_expr_noarg)(const char *, const char *,
cpp_db_t *);
} p;
enum {
EXPR_ARG,
EXPR_NOARG
} fntype;
const char val[4];
} expr_tbl[] = {
PA("aN", "&=", parse_binary_expr),
PA("aS", "=", parse_binary_expr),
PA("aa", "&&", parse_binary_expr),
PA("ad", "&", parse_prefix_expr),
PA("an", "&", parse_binary_expr),
PN("at", parse_alignof),
PN("az", parse_alignof),
PN("cc", parse_cast_expr),
PN("cl", parse_call_expr),
PA("cm", ",", parse_binary_expr),
PA("co", "~", parse_prefix_expr),
PN("cv", parse_conv_expr),
PN("da", parse_del_expr),
PA("dV", "/=", parse_binary_expr),
PN("dc", parse_cast_expr),
PA("de", "*", parse_prefix_expr),
PN("dl", parse_del_expr),
PN("dn", parse_unresolved_name),
PN("ds", parse_dot_star_expr),
PN("dt", parse_dot_expr),
PA("dv", "/", parse_binary_expr),
PA("eO", "^=", parse_binary_expr),
PA("eo", "^", parse_binary_expr),
PA("eq", "==", parse_binary_expr),
PA("ge", ">=", parse_binary_expr),
PN("gs", parse_gs),
PA("gt", ">", parse_binary_expr),
PN("ix", parse_idx_expr),
PA("lS", "<<=", parse_binary_expr),
PA("le", "<=", parse_binary_expr),
PA("ls", "<<", parse_binary_expr),
PA("lt", "<", parse_binary_expr),
PA("mI", "-=", parse_binary_expr),
PA("mL", "*=", parse_binary_expr),
PN("mm", parse_mm_expr),
PA("mi", "-", parse_binary_expr),
PA("ml", "*", parse_binary_expr),
PN("na", parse_new_expr),
PA("ne", "!=", parse_binary_expr),
PA("ng", "-", parse_prefix_expr),
PA("nt", "!", parse_prefix_expr),
PN("nw", parse_new_expr),
PN("nx", parse_noexcept_expr),
PA("oR", "|=", parse_binary_expr),
PN("on", parse_unresolved_name),
PA("oo", "||", parse_binary_expr),
PA("or", "|", parse_binary_expr),
PA("pL", "+=", parse_binary_expr),
PA("pl", "+", parse_binary_expr),
PA("pm", "->*", parse_binary_expr),
PN("pp", parse_pp_expr),
PA("ps", "+", parse_prefix_expr),
PN("pt", parse_arrow_expr),
PN("qu", parse_trinary_expr),
PA("rM", "%=", parse_binary_expr),
PA("rS", ">>=", parse_binary_expr),
PN("rc", parse_cast_expr),
PA("rm", "%", parse_binary_expr),
PA("rs", ">>", parse_binary_expr),
PN("sc", parse_cast_expr),
PN("sp", parse_pack_expansion),
PN("sr", parse_unresolved_name),
PN("st", parse_sizeof),
PN("sz", parse_sizeof),
PN("sZ", parse_sizeof_param_pack_expr),
PN("te", parse_typeid_expr),
PN("tr", parse_throw_expr),
PN("tw", parse_throw_expr)
};
#undef PA
#undef PN
static const char *
parse_expression(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
for (size_t i = 0; i < ARRAY_SIZE(expr_tbl); i++) {
if (strncmp(expr_tbl[i].code, first, 2) != 0)
continue;
switch (expr_tbl[i].fntype) {
case EXPR_ARG:
return (expr_tbl[i].p.parse_expr_arg(first, last,
expr_tbl[i].val, db));
case EXPR_NOARG:
return (expr_tbl[i].p.parse_expr_noarg(first, last,
db));
}
}
switch (first[0]) {
case 'L':
return (parse_expr_primary(first, last, db));
case 'T':
return (parse_template_param(first, last, db));
case 'f':
return (parse_function_param(first, last, db));
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);
}
static const char *
parse_binary_expr(const char *first, const char *last, const char *op,
cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
size_t n = nlen(db);
const char *t1 = parse_expression(first + 2, last, db);
if (t1 == first + 2)
return (first);
nadd_l(db, op, 0);
const char *t2 = parse_expression(t1, last, db);
if (t2 == t1)
return (first);
if (NAMT(db, n) != 3)
return (first);
VERIFY3U(nlen(db), >, 2);
nfmt(db, "({2}) {1} ({0})", NULL);
if (strcmp(op, ">") == 0)
nfmt(db, "({0})", NULL);
return (t2);
}
static const char *
parse_prefix_expr(const char *first, const char *last, const char *op,
cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
nadd_l(db, op, 0);
const char *t = parse_expression(first + 2, last, db);
if (t == first + 2) {
return (first);
}
VERIFY3U(nlen(db), >, 1);
nfmt(db, "{1}({0})", NULL);
return (t);
}
static const char *
parse_gs(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = NULL;
if (last - first < 4)
return (first);
if (first[2] == 'n' && (first[3] == 'a' || first[3] == 'w'))
t = parse_new_expr(first + 2, last, db);
else if (first[2] == 'd' && (first[3] == 'l' || first[3] == 'a'))
t = parse_del_expr(first + 2, last, db);
else
return (first);
if (t == first + 2)
return (first);
VERIFY3U(nlen(db), >, 0);
nfmt(db, "::{0}", NULL);
return (t);
}
/*
* [gs] nw <expression>* _ <type> E # new (expr-list) type
* [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
* [gs] na <expression>* _ <type> E # new[] (expr-list) type
* [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
* <initializer> ::= pi <expression>* E # parenthesized initialization
*/
static const char *
parse_new_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
/* note [gs] is already handled by parse_gs() */
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 'n');
VERIFY(first[1] == 'a' || first[1] == 'w');
const char *t1 = first + 2;
const char *t2 = NULL;
size_t n = nlen(db);
nadd_l(db, (first[1] == 'w') ? "new" : "new[]", 0);
while (t1 != last && t1[0] != '_') {
t2 = parse_expression(t1, last, db);
VERIFY3P(t2, !=, NULL);
if (t2 == t1)
return (first);
t1 = t2;
}
if (t1 == last)
return (first);
if (NAMT(db, n) > 1) {
njoin(db, NAMT(db, n) - 1, ", ");
nfmt(db, "({0})", NULL);
}
t2 = parse_type(t1 + 1, last, db);
if (t1 + 1 == t2)
return (first);
if (t2[0] != 'E') {
if (last - t2 < 3)
return (first);
if (t2[0] != 'p' && t2[1] != 'i')
return (first);
t2 += 2;
const char *t3 = t2;
size_t n1 = nlen(db);
while (t2[0] != 'E' && t2 != last) {
t3 = parse_expression(t2, last, db);
if (t2 == t3)
return (first);
t2 = t3;
}
if (t3 == last || t3[0] != 'E')
return (first);
if (NAMT(db, n1) > 0) {
njoin(db, NAMT(db, n1), ", ");
nfmt(db, "({0})", NULL);
}
}
njoin(db, NAMT(db, n), " ");
return (t2 + 1);
}
static const char *
parse_del_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 'd');
VERIFY(first[1] == 'l' || first[1] == 'a');
size_t n = nlen(db);
const char *t = parse_expression(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) != 1)
return (first);
nfmt(db, (first[1] == 'a') ? "delete[] {0}" : "delete {0}", NULL);
return (t);
}
static const char *
parse_idx_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
VERIFY3U(first[0], ==, 'i');
VERIFY3U(first[1], ==, 'x');
size_t n = nlen(db);
const char *t1 = parse_expression(first + 2, last, db);
if (t1 == first + 2)
return (first);
const char *t2 = parse_expression(t1, last, db);
if (t2 == t1 || NAMT(db, n) != 2)
return (first);
nfmt(db, "({0})[{1}]", NULL);
return (t2);
}
static const char *
parse_ppmm_expr(const char *first, const char *last, const char *fmt,
cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
const char *t = NULL;
size_t n = nlen(db);
if (first[2] == '_') {
t = parse_binary_expr(first + 3, last, "--", db);
if (t == first + 3)
return (first);
return (t);
}
t = parse_expression(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) < 1)
return (first);
nfmt(db, fmt, NULL);
return (t);
}
static const char *
parse_mm_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
VERIFY3U(first[0], ==, 'm');
VERIFY3U(first[1], ==, 'm');
return (parse_ppmm_expr(first, last, "({0})--", db));
}
static const char *
parse_pp_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
VERIFY3U(first[0], ==, 'p');
VERIFY3U(first[0], ==, 'p');
return (parse_ppmm_expr(first, last, "({0})++", db));
}
static const char *
parse_trinary_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t1, *t2, *t3;
size_t n = nlen(db);
if (last - first < 2)
return (first);
t1 = parse_expression(first + 2, last, db);
if (t1 == first + 2)
return (first);
t2 = parse_expression(t1, last, db);
if (t1 == t2)
return (first);
t3 = parse_expression(t2, last, db);
if (t3 == t2)
return (first);
if (NAMT(db, n) != 3)
return (first);
nfmt(db, "({2}) ? ({1}) : ({0})", NULL);
return (t3);
}
static const char *
parse_noexcept_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
size_t n = nlen(db);
const char *t = parse_expression(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) != 1)
return (first);
nfmt(db, "noexcept ({0})", NULL);
return (t);
}
/*
* cc <type> <expression> # const_cast<type> (expression)
* dc <type> <expression> # dynamic_cast<type> (expression)
* rc <type> <expression> # reinterpret_cast<type> (expression)
* sc <type> <expression> # static_cast<type> (expression)
*/
static const char *
parse_cast_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
const char *fmt = NULL;
switch (first[0]) {
case 'c':
fmt = "const_cast<{1}> ({0})";
break;
case 'd':
fmt = "dynamic_cast<{1}> ({0})";
break;
case 'r':
fmt = "reinterpret_cast<{1}> ({0})";
break;
case 's':
fmt = "static_cast<{1}> ({0})";
break;
default:
return (first);
}
VERIFY3U(first[1], ==, 'c');
const char *t1 = parse_type(first + 2, last, db);
if (t1 == first + 2)
return (first);
const char *t2 = parse_expression(t1, last, db);
if (t2 == t1)
return (first);
VERIFY3U(nlen(db), >, 1);
nfmt(db, fmt, NULL);
return (t2);
}
/* pt <expression> <expression> # expr->name */
static const char *
parse_arrow_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 4)
return (first);
size_t n = nlen(db);
const char *t1 = parse_expression(first + 2, last, db);
if (t1 == first + 2)
return (first);
const char *t2 = parse_expression(t1, last, db);
if (t2 == t1 || NAMT(db, n) != 2)
return (first);
nfmt(db, "{1}->{0}", NULL);
return (t2);
}
/* wrap value in () when necessary */
static void
paren(str_pair_t *sp)
{
str_t *l = &sp->strp_l;
str_t *r = &sp->strp_r;
if (str_length(r) > 1 &&
r->str_s[0] == ' ' && r->str_s[1] == '[') {
(void) str_append(l, " (", 2);
(void) str_insert(r, 0, ")", 1);
} else if (str_length(r) > 0 && r->str_s[0] == '(') {
(void) str_append(l, "(", 1);
(void) str_insert(r, 0, ")", 1);
}
}
/* BEGIN CSTYLED */
/*
* <type> ::= <builtin-type>
* ::= <function-type>
* ::= <class-enum-type>
* ::= <array-type>
* ::= <pointer-to-member-type>
* ::= <template-param>
* ::= <template-template-param> <template-args>
* ::= <decltype>
* ::= <substitution>
* ::= <CV-qualifiers> <type>
* ::= P <type> # pointer-to
* ::= R <type> # reference-to
* ::= O <type> # rvalue reference-to (C++0x)
* ::= C <type> # complex pair (C 2000)
* ::= G <type> # imaginary (C 2000)
* ::= Dp <type> # pack expansion (C++0x)
* ::= U <source-name> <type> # vendor extended type qualifier
* extension := U <objc-name> <objc-type> # objc-type<identifier>
* extension := <vector-type> # <vector-type> starts with Dv
*
* <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1
* <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
*/
/* END CSTYLED */
static const char *
parse_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
switch (first[0]) {
case 'r':
case 'V':
case 'K':
return (parse_qual_type(first, last, db));
}
const char *t = first;
const char *t1 = NULL;
str_pair_t *sp = NULL;
size_t n = nlen(db);
size_t amt = 0;
t = parse_builtin_type(first, last, db);
if (t != first)
return (t);
switch (first[0]) {
case 'A':
t = parse_array_type(first, last, db);
if (t == first || NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
case 'C':
t = parse_type(first + 1, last, db);
if (t == first + 1 || NAMT(db, n) == 0)
return (first);
(void) str_append(TOP_L(db), " complex", 8);
save_top(db, 1);
return (t);
case 'F':
t = parse_function_type(first, last, db);
if (t == first || NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
case 'G':
t = parse_type(first + 1, last, db);
if (t == first + 1 || NAMT(db, n) == 0)
return (first);
(void) str_append(TOP_L(db), " imaginary", 10);
save_top(db, 1);
return (t);
case 'M':
t = parse_pointer_to_member_type(first, last, db);
if (t == first || NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
case 'O':
t = parse_type(first + 1, last, db);
amt = NAMT(db, n);
if (t == first + 1 || amt == 0)
return (first);
sp = name_at(&db->cpp_name, amt - 1);
for (size_t i = 0; i < amt; i++, sp++) {
paren(sp);
if (str_pair_len(sp) > 0)
(void) str_append(&sp->strp_l, "&&", 2);
}
save_top(db, amt);
return (t);
case 'P':
t = parse_type(first + 1, last, db);
amt = NAMT(db, n);
if (t == first + 1 || amt == 0)
return (first);
sp = name_at(&db->cpp_name, amt - 1);
for (size_t i = 0; i < amt; i++, sp++) {
str_t *l = &sp->strp_l;
if (str_pair_len(sp) == 0)
continue;
paren(sp);
if (first[1] != 'U' ||
strncmp(l->str_s, "objc_object<", 12) != 0) {
(void) str_append(l, "*", 1);
} else {
(void) str_erase(l, 0, 11);
(void) str_insert(l, 0, "id", 2);
}
}
save_top(db, amt);
return (t);
case 'R':
t = parse_type(first + 1, last, db);
amt = NAMT(db, n);
if (t == first + 1 || amt == 0)
return (first);
sp = name_at(&db->cpp_name, amt - 1);
for (size_t i = 0; i < amt; i++, sp++) {
if (str_length(&sp->strp_l) == 0 &&
str_length(&sp->strp_r) == 0)
continue;
paren(sp);
(void) str_append(&sp->strp_l, "&", 1);
}
save_top(db, amt);
return (t);
case 'T':
t = parse_template_param(first, last, db);
if (t == first)
return (first);
amt = NAMT(db, n);
save_top(db, amt);
if (!db->cpp_try_to_parse_template_args || amt != 1)
return (t);
t1 = parse_template_args(t, last, db);
if (t1 == t)
return (t);
nfmt(db, "{1:L}{0}", "{1:R}");
save_top(db, 1);
return (t1);
case 'U':
if (first + 1 == last)
return (first);
t = parse_source_name(first + 1, last, db);
if (t == first + 1)
return (first);
nfmt(db, "{0}", NULL);
t1 = parse_type(t, last, db);
if (t1 == t || NAMT(db, n) < 2)
return (first);
const str_t *name = &name_at(&db->cpp_name, 1)->strp_l;
if (str_length(name) > 0 &&
strncmp(name->str_s, "objcproto", 9) != 0) {
nfmt(db, "{0} {1}", NULL);
} else {
t = parse_source_name(name->str_s + 9,
name->str_s + name->str_len, db);
if (t != name->str_s + 9) {
nfmt(db, "{1}<{0}>", NULL);
str_pair_t save = {0};
name_pop(&db->cpp_name, &save);
/* get rid of 'objcproto' */
name_pop(&db->cpp_name, NULL);
CK(name_add_str(&db->cpp_name, &save.strp_l,
&save.strp_r));
} else {
nfmt(db, "{1} {0}", NULL);
}
}
save_top(db, 1);
return (t1);
case 'S':
if (first + 1 != last && first[1] == 't') {
t = parse_name(first, last, NULL, db);
if (t == first || NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
}
t = parse_substitution(first, last, db);
if (t == first)
return (first);
/*
* If the substitution is a <template-param>, it might
* be followed by <template-args>
*/
t1 = parse_template_args(t, last, db);
if (t1 == t)
return (t);
if (NAMT(db, n) < 2)
return (t);
nfmt(db, "{1:L}{0}", "{1:R}");
save_top(db, 1);
return (t1);
case 'D':
if (first + 1 == last)
return (first);
switch (first[1]) {
case 'p':
t = parse_type(first + 2, last, db);
if (t == first + 2)
break;
save_top(db, NAMT(db, n));
return (t);
case 't':
case 'T':
t = parse_decltype(first, last, db);
if (first == t)
break;
save_top(db, 1);
return (t);
case 'v':
t = parse_vector_type(first, last, db);
if (first == t)
break;
if (NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
}
break;
}
/*
* 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)
return (t);
t = parse_name(first, last, NULL, db);
if (t == first || NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
}
static const char *
parse_qual_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = NULL;
const char *t1 = NULL;
unsigned cv = 0;
t = parse_cv_qualifiers(first, last, &cv);
if (t == first)
return (first);
size_t n = nlen(db);
boolean_t is_func = !!(t[0] == 'F');
t1 = parse_type(t, last, db);
size_t amt = NAMT(db, n);
if (t == t1 || amt == 0)
return (first);
if (is_func)
sub_pop(&db->cpp_subs);
str_pair_t *sp = name_at(&db->cpp_name, amt - 1);
for (size_t i = 0; i < amt; i++, sp++) {
str_t *s = NULL;
if (!is_func) {
s = &sp->strp_l;
if (str_length(s) == 0)
continue;
if (cv & 1)
(void) str_append(s, " const", 6);
if (cv & 2)
(void) str_append(s, " volatile", 9);
if (cv & 4)
(void) str_append(s, " restrict", 9);
continue;
}
s = &sp->strp_r;
size_t pos = str_length(s);
if (pos > 0 && s->str_s[pos - 1] == '&') {
pos--;
if (s->str_s[pos - 1] == '&')
pos--;
}
if (cv & 1) {
(void) str_insert(s, pos, " const", 6);
pos += 6;
}
if (cv & 2) {
(void) str_insert(s, pos, " volatile", 9);
pos += 9;
}
if (cv & 4) {
(void) str_insert(s, pos, " restrict", 9);
}
}
save_top(db, amt);
return (t1);
}
/*
* at <type> # alignof (a type)
* az <expression> # alignof (a expression)
*/
static const char *
parse_alignof(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
const char *(*fn)(const char *, const char *, cpp_db_t *);
fn = (first[1] == 't') ? parse_type : parse_expression;
size_t n = nlen(db);
const char *t = fn(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) != 1)
return (first);
nfmt(db, "alignof ({0})", NULL);
return (t);
}
/*
* st <type> # sizeof (a type)
* sz <expr> # sizeof (a expression)
*/
static const char *
parse_sizeof(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
VERIFY3U(first[0], ==, 's');
const char *t = NULL;
size_t n = nlen(db);
switch (first[1]) {
case 't':
t = parse_type(first + 2, last, db);
break;
case 'z':
t = parse_expression(first + 2, last, db);
break;
default:
return (first);
}
if (t == first + 2 || NAMT(db, n) != 1)
return (first);
nfmt(db, "sizeof ({0})", NULL);
return (t);
}
/* BEGIN CSTYLED */
/*
* <function-param> ::= fp <top-level CV-qualifiers> _ # L == 0, first parameter
* ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters
* ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _ # L > 0, first parameter
* ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters
*/
/* END CSTYLED */
static const char *
parse_function_param(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3 || first[0] != 'f')
return (first);
const char *t1 = first + 2;
const char *t2 = NULL;
unsigned cv = 0;
if (first[1] == 'L') {
t2 = parse_number(t1, last, db->cpp_loc);
if (t2 == last || t2[0] != 'p')
return (first);
t1 = t2;
}
if (first[1] != 'p')
return (first);
t1 = parse_cv_qualifiers(t1, last, &cv);
t2 = parse_number(t1, last, db->cpp_loc);
if (t2 == last || t2[0] != '_')
return (first);
if (t2 - t1 > 0)
nadd_l(db, t1, (size_t)(t2 - t1));
else
nadd_l(db, "", 0);
nfmt(db, "fp{0}", NULL);
return (t2 + 1);
}
/*
* sZ <template-param> # size of a parameter pack
* sZ <function-param> # size of a function parameter pack
*/
static const char *
parse_sizeof_param_pack_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 's');
VERIFY3U(first[1], ==, 'Z');
if (first[2] != 'T' && first[2] != 'f')
return (first);
const char *t = NULL;
size_t n = nlen(db);
if (first[2] == 'T')
t = parse_template_param(first + 2, last, db);
else
t = parse_function_param(first + 2, last, db);
if (t == first + 2)
return (first);
njoin(db, NAMT(db, n), ", ");
nfmt(db, "sizeof...({0})", NULL);
return (t);
}
/*
* te <expression> # typeid (expression)
* ti <type> # typeid (type)
*/
static const char *
parse_typeid_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 't');
VERIFY(first[1] == 'e' || first[1] == 'i');
const char *t = NULL;
size_t n = nlen(db);
if (first[1] == 'e')
t = parse_expression(first + 2, last, db);
else
t = parse_type(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) != 1)
return (first);
nfmt(db, "typeid ({0})", NULL);
return (t);
}
/*
* tr # throw
* tw <expression> # throw expression
*/
static const char *
parse_throw_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 't');
VERIFY(first[1] == 'w' || first[1] == 'r');
if (first[1] == 'r') {
nadd_l(db, "throw", 0);
return (first + 2);
}
size_t n = nlen(db);
const char *t = parse_expression(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) != 1)
return (first);
nfmt(db, "throw {0}", NULL);
return (t);
}
/* ds <expression> <expression> # expr.*expr */
static const char *
parse_dot_star_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 'd');
VERIFY3U(first[1], ==, 's');
size_t n = nlen(db);
const char *t = parse_expression(first + 2, last, db);
if (t == first + 2)
return (first);
const char *t2 = parse_expression(t, last, db);
if (t == t2 || NAMT(db, n) != 2)
return (first);
nfmt(db, "{1}.*{0}", NULL);
return (t2);
}
/* dt <expression> <unresolved-name> # expr.name */
static const char *
parse_dot_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 'd');
VERIFY3U(first[1], ==, 't');
const char *t = parse_expression(first + 2, last, db);
if (t == first + 2)
return (first);
const char *t1 = parse_unresolved_name(t, last, db);
if (t1 == t)
return (first);
nfmt(db, "{1}.{0}", NULL);
return (t1);
}
/* cl <expression>+ E # call */
static const char *
parse_call_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 4)
return (first);
VERIFY3U(first[0], ==, 'c');
VERIFY3U(first[1], ==, 'l');
const char *t = first + 2;
const char *t1 = NULL;
size_t n = nlen(db);
for (t = first + 2; t != last && t[0] != 'E'; t = t1) {
t1 = parse_expression(t, last, db);
if (t1 == t)
return (first);
}
size_t amt = NAMT(db, n);
if (t == last || amt == 0)
return (first);
njoin(db, amt - 1, ", ");
nfmt(db, "{1}({0})", NULL);
VERIFY3U(t[0], ==, 'E');
return (t + 1);
}
/* BEGIN CSTYLED */
/*
* cv <type> <expression> # conversion with one argument
* cv <type> _ <expression>* E # conversion with a different number of arguments
*/
/* END CSTYLED */
static const char *
parse_conv_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 'c');
VERIFY3U(first[1], ==, 'v');
const char *t = NULL;
const char *t1 = NULL;
size_t n = nlen(db);
boolean_t try_to_parse_template_args =
db->cpp_try_to_parse_template_args;
db->cpp_try_to_parse_template_args = B_FALSE;
t = parse_type(first + 2, last, db);
db->cpp_try_to_parse_template_args = try_to_parse_template_args;
if (t == first + 2)
return (first);
if (t[0] != '_') {
t1 = parse_expression(t, last, db);
if (t1 == t)
return (first);
t = t1;
} else {
size_t n1 = nlen(db);
/* skip _ */
t++;
while (t[0] != 'E' && t != last) {
t1 = parse_expression(t, last, db);
if (t1 == t)
return (first);
t1 = t;
}
/* E */
t++;
njoin(db, NAMT(db, n1), ", ");
}
if (NAMT(db, n) < 2)
return (first);
nfmt(db, "({1})({0})", NULL);
return (t);
}
/* <simple-id> ::= <source-name> [ <template-args> ] */
static const char *
parse_simple_id(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = parse_source_name(first, last, db);
if (t == first)
return (t);
const char *t1 = parse_template_args(t, last, db);
if (t == t1)
return (t);
nfmt(db, "{1}{0}", NULL);
return (t1);
}
/*
* <unresolved-type> ::= <template-param>
* ::= <decltype>
* ::= <substitution>
*/
static const char *
parse_unresolved_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
const char *t = first;
size_t n = nlen(db);
switch (first[0]) {
case 'T':
t = parse_template_param(first, last, db);
if (t == first || NAMT(db, n) != 1) {
for (size_t i = 0; i < NAMT(db, n); i++)
name_pop(&db->cpp_name, NULL);
return (first);
}
save_top(db, 1);
return (t);
case 'D':
t = parse_decltype(first, last, db);
if (t == first || NAMT(db, n) == 0)
return (first);
save_top(db, 1);
return (t);
case 'S':
t = parse_substitution(first, last, db);
if (t != first)
return (t);
if (last - first < 2 || first[1] != 't')
return (first);
t = parse_unqualified_name(first + 2, last, db);
if (t == first + 2 || NAMT(db, n) == 0)
return (first);
nfmt(db, "std::{0:L}", "{0:R}");
save_top(db, 1);
return (t);
}
return (first);
}
/* sp <expression> # pack expansion */
static const char *
parse_pack_expansion(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 's');
VERIFY3U(first[1], ==, 'p');
const char *t = parse_expression(first + 2, last, db);
if (t == first +2)
return (first);
return (t);
}
/*
* <unscoped-name> ::= <unqualified-name>
* ::= St <unqualified-name> # ::std::
* extension ::= StL<unqualified-name>
*/
static const char *
parse_unscoped_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
const char *t = first;
const char *t1 = NULL;
boolean_t st = B_FALSE;
if (first[0] == 'S' && first[1] == 't') {
st = B_TRUE;
t = first + 2;
if (first + 3 != last && first[2] == 'L')
t++;
}
t1 = parse_unqualified_name(t, last, db);
if (t == t1)
return (first);
if (st)
nfmt(db, "std::{0}", NULL);
return (t1);
}
/*
* <unqualified-name> ::= <operator-name>
* ::= <ctor-dtor-name>
* ::= <source-name>
* ::= <unnamed-type-name>
*/
const char *
parse_unqualified_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
switch (*first) {
case 'C':
case 'D':
return (parse_ctor_dtor_name(first, last, db));
case 'U':
return (parse_unnamed_type_name(first, last, db));
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (parse_source_name(first, last, db));
default:
return (parse_operator_name(first, last, db));
}
}
/*
* <unnamed-type-name> ::= Ut [ <nonnegative number> ] _
* ::= <closure-type-name>
*
* <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
*
* <lambda-sig> ::= <parameter type>+
* # Parameter types or "v" if the lambda has no parameters
*/
static const char *
parse_unnamed_type_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2 || first[0] != 'U')
return (first);
if (first[1] != 't' && first[1] != 'l')
return (first);
const char *t1 = first + 2;
const char *t2 = NULL;
if (first[1] == 't') {
while (t1 != last && t1[0] != '_' &&
isdigit_l(t1[0], db->cpp_loc))
t1++;
if (t1[0] != '_')
return (first);
if (t1 == first + 2)
nadd_l(db, "", 0);
else
nadd_l(db, first + 2, (size_t)(t1 - first - 2));
nfmt(db, "'unnamed{0}'", NULL);
return (t1 + 1);
}
size_t n = nlen(db);
if (first[2] != 'v') {
do {
t2 = parse_type(t1, last, db);
if (t1 == t2)
return (first);
t1 = t2;
} while (t1 != last && t1[0] != 'E');
if (t1 == last || NAMT(db, n) < 1)
return (first);
if (NAMT(db, n) < 1)
return (first);
} else {
t1++;
if (t1[0] != 'E')
return (first);
}
njoin(db, NAMT(db, n), ", ");
/* E */
t1++;
t2 = t1;
while (t2 != last && t2[0] != '_') {
if (!isdigit_l(*t2++, db->cpp_loc))
return (first);
}
if (t2[0] != '_')
return (first);
if (t2 - t1 > 0)
nadd_l(db, t1, (size_t)(t2 - t1));
else
nadd_l(db, "", 0);
nfmt(db, "'lambda{0}'({1})", NULL);
/* _ */
return (t2 + 1);
}
static struct {
const char *alias;
const char *fullname;
const char *basename;
} aliases[] = {
{
"std::string",
"std::basic_string<char, std::char_traits<char>, "
"std::allocator<char> >",
"basic_string"
},