#include <errno.h>
#include <note.h>
#include <string.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include "demangle-sys.h"
#include "demangle_int.h"
#include "cxx.h"
#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;
} 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 void 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 *);
static const char *parse_number(const char *, const char *);
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 *);
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;
char *
cpp_demangle(const char *src, size_t srclen, sysdem_ops_t *ops)
{
char *volatile result = NULL;
cpp_db_t db;
db_init(&db, ops);
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);
}
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] == '_') {
if (t + 1 == last || ISDIGIT(t[1]))
return (first);
t += 2;
}
while (t < last && ISDIGIT(t[0]))
t++;
done:
if (nempty(db))
return (first);
nfmt(db, "invocation function for block in {0}", NULL);
return (t);
}
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;
}
}
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);
}
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);
if (t1 == t)
return (first);
t = parse_call_offset(t1, last);
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);
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);
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);
}
static const char *
parse_call_offset(const char *first, const char *last)
{
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);
if (t == first + 1 || t == last || t[0] != '_')
return (first);
t++;
if (first[0] == 'h')
return (t);
t1 = parse_number(t, last);
if (t == t1 || t1 == last || t1[0] != '_')
return (first);
t1++;
return (t1);
}
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);
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);
}
t1 = parse_unscoped_name(t, last, db);
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);
}
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));
t++;
if (t[0] == 's') {
nfmt(db, "{0:L}::string literal", "{0:R}");
return (parse_discriminator(t, last));
}
if (t[0] == 'd') {
t1 = parse_number(t + 1, last);
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}");
if (t[0] != 'd')
t2 = parse_discriminator(t2, last);
return (t2);
}
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':
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;
}
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);
}
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);
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;
}
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);
t++;
}
break;
default:
t = parse_type(first, last, db);
}
return (t);
}
#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);
}
static const char *
parse_new_expr(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
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);
}
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);
}
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);
}
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);
}
}
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);
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);
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;
}
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);
}
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);
}
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);
}
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);
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);
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
t++;
while (t[0] != 'E' && t != last) {
t1 = parse_expression(t, last, db);
if (t1 == t)
return (first);
t1 = t;
}
t++;
njoin(db, NAMT(db, n1), ", ");
}
if (NAMT(db, n) < 2)
return (first);
nfmt(db, "({1})({0})", NULL);
return (t);
}
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);
}
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);
}
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);
}
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);
}
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));
}
}
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(t1[0]))
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), ", ");
t1++;
t2 = t1;
while (t2 != last && t2[0] != '_') {
char c = *t2++;
if (!ISDIGIT(c))
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"
},
{
"std::istream",
"std::basic_istream<char, std::char_traits<char> >",
"basic_istream"
},
{
"std::ostream",
"std::basic_ostream<char, std::char_traits<char> >",
"basic_ostream"
},
{
"std::iostream",
"std::basic_iostream<char, std::char_traits<char> >",
"basic_iostream"
}
};
static void
basename(cpp_db_t *db)
{
str_t *s = TOP_L(db);
for (size_t i = 0; i < ARRAY_SIZE(aliases); i++) {
if (str_length(s) != strlen(aliases[i].alias))
continue;
if (strncmp(aliases[i].alias, s->str_s, str_length(s)) != 0)
continue;
sysdem_ops_t *ops = s->str_ops;
str_fini(s);
str_init(s, ops);
str_set(s, aliases[i].fullname, 0);
nadd_l(db, aliases[i].basename, 0);
return;
}
const char *start = s->str_s;
const char *end = s->str_s + s->str_len;
unsigned c = 0;
if (end[-1] == '>') {
for (; end > start; end--) {
switch (end[-1]) {
case '<':
if (--c == 0) {
end--;
goto out;
}
break;
case '>':
c++;
break;
}
}
}
out:
VERIFY3P(end, >=, start);
if (end - start < 2) {
nadd_l(db, "", 0);
return;
}
for (start = end - 1; start > s->str_s; start--) {
if (start[0] == ':') {
start++;
break;
}
}
VERIFY3P(end, >=, start);
nadd_l(db, start, (size_t)(end - start));
}
static const char *
parse_ctor_dtor_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2 || nempty(db) || str_length(TOP_L(db)) == 0)
return (first);
switch (first[0]) {
case 'C':
switch (first[1]) {
case '1':
case '2':
case '3':
case '5':
basename(db);
break;
default:
return (first);
}
break;
case 'D':
switch (first[1]) {
case '0':
case '1':
case '2':
case '5':
basename(db);
(void) str_insert(TOP_L(db), 0, "~", 1);
break;
default:
return (first);
}
break;
default:
return (first);
}
db->cpp_parsed_ctor_dtor_cv = B_TRUE;
return (first + 2);
}
static const char *
parse_integer_literal(const char *first, const char *last, const char *fmt,
cpp_db_t *db)
{
VERIFY3P(first, <=, last);
const char *t = parse_number(first, last);
const char *start = first;
if (t == first || t == last || t[0] != 'E')
return (first);
if (first[0] == 'n')
start++;
nadd_l(db, start, (size_t)(t - start));
if (start != first)
nfmt(db, "-{0}", NULL);
nfmt(db, fmt, NULL);
return (t + 1);
}
static struct float_data_s {
const char *spec;
size_t mangled_size;
size_t max_demangled_size;
char type;
} float_info[] = {
{ "%af", 8, 24, 'f' },
{ "%a", 16, 32, 'd' },
{ "%LaL", 20, 40, 'e' }
};
static const char *
parse_floating_literal(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
VERIFY(first[0] == 'f' || first[0] == 'd' || first[0] == 'e');
const struct float_data_s *fd = NULL;
for (size_t i = 0; i < ARRAY_SIZE(float_info); i++) {
if (float_info[i].type != first[0])
continue;
fd = &float_info[i];
break;
}
if (fd == NULL || (size_t)(last - first) < fd->mangled_size)
return (first);
union {
union {
float v;
char buf[sizeof (float)];
} f;
union {
double v;
char buf[sizeof (double)];
} d;
union {
long double v;
char buf[sizeof (long double)];
} ld;
} conv;
const char *t = NULL;
char *e = NULL;
switch (first[0]) {
case 'f':
e = conv.f.buf;
break;
case 'd':
e = conv.d.buf;
break;
case 'e':
e = conv.ld.buf;
break;
}
last = first + fd->mangled_size + 1;
#if defined(_BIG_ENDIAN)
for (t = first + 1; t != last; t++, e++) {
if (!is_xdigit(t[0]))
return (first);
unsigned d1 = ISDIGIT(t[0]) ? t[0] - '0' : t[0] - 'a' + 10;
t++;
unsigned d0 = ISDIGIT(t[0]) ? t[0] - '0' : t[0] - 'a' + 10;
*e = (d1 << 4) + d0;
}
#elif defined(_LITTLE_ENDIAN)
for (t = last - 1; t > first; t--, e++) {
if (!is_xdigit(t[0]))
return (first);
unsigned d0 = ISDIGIT(t[0]) ? t[0] - '0' : t[0] - 'a' + 10;
t--;
unsigned d1 = ISDIGIT(t[0]) ? t[0] - '0' : t[0] - 'a' + 10;
*e = (d1 << 4) + d0;
}
t = last;
#else
#error One of _BIG_ENDIAN or _LITTLE_ENDIAN must be defined
#endif
if (t[0] != 'E')
return (first);
str_t num = { 0 };
str_init(&num, db->cpp_ops);
num.str_size = fd->max_demangled_size + 1;
num.str_s = zalloc(db->cpp_ops, num.str_size);
CK(num.str_s != NULL);
int n = 0;
switch (first[0]) {
case 'f':
n = snprintf(num.str_s, fd->max_demangled_size, fd->spec,
conv.f.v);
break;
case 'd':
n = snprintf(num.str_s, fd->max_demangled_size, fd->spec,
conv.d.v);
break;
case 'e':
n = snprintf(num.str_s, fd->max_demangled_size, fd->spec,
conv.ld.v);
}
if (n >= fd->max_demangled_size || n <= 0) {
str_fini(&num);
return (first);
}
num.str_len = n;
(void) name_add_str(&db->cpp_name, &num, NULL);
return (t + 1);
}
static struct {
int c;
const char *fmt;
} int_lits[] = {
{ 'a', "(signed char){0}" },
{ 'c', "(char){0}" },
{ 'h', "(unsigned char){0}" },
{ 'i', "{0}" },
{ 'j', "{0}u" },
{ 'l', "{0}l" },
{ 'm', "{0}ul" },
{ 'n', "(__int128){0}" },
{ 'o', "(unsigned __int128){0}" },
{ 's', "(short){0}" },
{ 't', "(unsigned short){0}" },
{ 'w', "(wchar_t){0}" },
{ 'x', "{0}ll" },
{ 'y', "{0}ull" }
};
static const char *
parse_expr_primary(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 4 || first[0] != 'L')
return (first);
const char *t = NULL;
for (size_t i = 0; i < ARRAY_SIZE(int_lits); i++) {
if (first[1] == int_lits[i].c) {
t = parse_integer_literal(first + 2, last,
int_lits[i].fmt, db);
return ((t == first + 2) ? first : t);
}
}
switch (first[1]) {
case 'b':
if (first[3] != 'E')
return (first);
switch (first[2]) {
case '0':
nadd_l(db, "false", 5);
break;
case '1':
nadd_l(db, "true", 4);
break;
default:
return (first);
}
return (first + 4);
case 'd':
case 'e':
case 'f':
t = parse_floating_literal(first + 1, last, db);
return ((t == first + 1) ? first : t);
case 'T':
return (first);
case '_':
if (first[2] != 'Z')
return (first);
t = parse_encoding(first + 3, last, db);
if (t == first + 3 || t == last || t[0] != 'E')
return (first);
return (t + 1);
default:
t = parse_type(first + 1, last, db);
if (t == first + 1 || t == last)
return (first);
if (t[0] == 'E')
return (t + 1);
const char *n;
for (n = t; n != last && ISDIGIT(n[0]); n++)
;
if (n == last || nempty(db) || n[0] != 'E')
return (first);
if (n == t)
return (t);
nadd_l(db, t, (size_t)(n - t));
nfmt(db, "({1}){0}", NULL);
return (n + 1);
}
}
static struct {
const char code[3];
const char *op;
} op_tbl[] = {
{ "aa", "operator&&" },
{ "ad", "operator&" },
{ "an", "operator&" },
{ "aN", "operator&=" },
{ "aS", "operator=" },
{ "cl", "operator()" },
{ "cm", "operator," },
{ "co", "operator~" },
{ "da", "operator delete[]" },
{ "de", "operator*" },
{ "dl", "operator delete" },
{ "dv", "operator/" },
{ "dV", "operator/=" },
{ "eo", "operator^" },
{ "eO", "operator^=" },
{ "eq", "operator==" },
{ "ge", "operator>=" },
{ "gt", "operator>" },
{ "ix", "operator[]" },
{ "le", "operator<=" },
{ "ls", "operator<<" },
{ "lS", "operator<<=" },
{ "lt", "operator<" },
{ "mi", "operator-" },
{ "mI", "operator-=" },
{ "ml", "operator*" },
{ "mL", "operator*=" },
{ "mm", "operator--" },
{ "na", "operator new[]" },
{ "ne", "operator!=" },
{ "ng", "operator-" },
{ "nt", "operator!" },
{ "nw", "operator new" },
{ "oo", "operator||" },
{ "or", "operator|" },
{ "oR", "operator|=" },
{ "pm", "operator->*" },
{ "pl", "operator+" },
{ "pL", "operator+=" },
{ "pp", "operator++" },
{ "ps", "operator+" },
{ "pt", "operator->" },
{ "qu", "operator?" },
{ "rm", "operator%" },
{ "rM", "operator%=" },
{ "rs", "operator>>" },
{ "rS", "operator>>=" }
};
static const char *
parse_operator_name(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(op_tbl); i++) {
if (strncmp(first, op_tbl[i].code, 2) != 0)
continue;
nadd_l(db, op_tbl[i].op, 0);
return (first + 2);
}
const char *t = NULL;
if (first[0] == 'l' && first[1] == 'i') {
t = parse_source_name(first + 2, last, db);
if (t == first + 2 || nempty(db))
return (first);
nfmt(db, "operator\"\" {0}", NULL);
return (t);
}
if (first[0] == 'v') {
if (!ISDIGIT(first[1]))
return (first);
t = parse_source_name(first + 2, last, db);
if (t == first + 2)
return (first);
nfmt(db, "operator {0}", NULL);
return (t);
}
if (first[0] != 'c' && first[1] != 'v')
return (first);
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 || nempty(db))
return (first);
nfmt(db, "operator {0}", NULL);
db->cpp_parsed_ctor_dtor_cv = B_TRUE;
return (t);
}
struct type_tbl_s {
int code;
const char *name;
};
static struct type_tbl_s type_tbl1[] = {
{ 'a', "signed char" },
{ 'b', "bool" },
{ 'c', "char" },
{ 'd', "double" },
{ 'e', "long double" },
{ 'f', "float" },
{ 'g', "__float128" },
{ 'h', "unsigned char" },
{ 'i', "int" },
{ 'j', "unsigned int" },
{ 'l', "long" },
{ 'm', "unsigned long" },
{ 'n', "__int128" },
{ 'o', "unsigned __int128" },
{ 's', "short" },
{ 't', "unsigned short" },
{ 'v', "void" },
{ 'w', "wchar_t" },
{ 'x', "long long" },
{ 'y', "unsigned long long" },
{ 'z', "..." }
};
static struct type_tbl_s type_tbl2[] = {
{ 'a', "auto" },
{ 'c', "decltype(auto)" },
{ 'd', "decimal64" },
{ 'e', "decimal128" },
{ 'f', "decimal32" },
{ 'h', "decimal16" },
{ 'i', "char32_t" },
{ 'n', "std::nullptr_t" },
{ 's', "char16_t" }
};
static const char *
parse_builtin_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
size_t i;
for (i = 0; i < ARRAY_SIZE(type_tbl1); i++) {
if (first[0] == type_tbl1[i].code) {
nadd_l(db, type_tbl1[i].name, 0);
return (first + 1);
}
}
if (first[0] == 'D') {
if (first + 1 == last)
return (first);
for (i = 0; i < ARRAY_SIZE(type_tbl2); i++) {
if (first[1] == type_tbl2[i].code) {
nadd_l(db, type_tbl2[i].name, 0);
return (first + 2);
}
}
}
if (first[0] == 'u') {
const char *t = parse_source_name(first + 1, last, db);
if (t == first + 1)
return (first);
return (t);
}
return (first);
}
static const char *
parse_base36(const char *first, const char *last, size_t *val)
{
VERIFY3P(first, <=, last);
const char *t;
for (t = first, *val = 0; t != last; t++) {
if (!ISDIGIT(t[0]) && !ISUPPER(t[0]))
return (t);
*val *= 36;
if (ISDIGIT(t[0]))
*val += t[0] - '0';
else
*val += t[0] - 'A' + 10;
}
return (t);
}
static struct type_tbl_s sub_tbl[] = {
{ 'a', "std::allocator" },
{ 'b', "std::basic_string" },
{ 's', "std::string" },
{ 'i', "std::istream" },
{ 'o', "std::ostream" },
{ 'd', "std::iostream" }
};
static const char *
parse_substitution(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last || last - first < 2)
return (first);
if (first[0] != 'S')
return (first);
for (size_t i = 0; i < ARRAY_SIZE(sub_tbl); i++) {
if (first[1] == sub_tbl[i].code) {
nadd_l(db, sub_tbl[i].name, 0);
return (first + 2);
}
}
const char *t = first + 1;
size_t n = 0;
if (t[0] != '_') {
t = parse_base36(first + 1, last, &n);
if (t == first + 1 || t[0] != '_')
return (first);
n++;
}
if (n >= sub_len(&db->cpp_subs))
return (first);
sub(db, n);
VERIFY3U(t[0], ==, '_');
return (t + 1);
}
static const char *
parse_source_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
const char *t = NULL;
size_t n = 0;
for (t = first; t != last && ISDIGIT(t[0]); t++) {
size_t nn = n * 10;
if (nn < n)
return (first);
nn += t[0] - '0';
if (nn < n)
return (first);
n = nn;
}
if (n == 0 || t == last || t + n > last ||
(uintptr_t)t + n < (uintptr_t)t)
return (first);
if (strncmp(t, "_GLOBAL__N", 10) == 0)
nadd_l(db, "(anonymous namespace)", 0);
else
nadd_l(db, t, n);
return (t + n);
}
static const char *
parse_vector_type(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], ==, 'v');
const char *t = first + 2;
const char *t1 = NULL;
if (ISDIGIT(first[2]) && first[2] != '0') {
t1 = parse_number(t, last);
if (t1 == last || t1 + 1 == last || t1[0] != '_')
return (first);
nadd_l(db, t, (size_t)(t1 - t));
t = t1 + 1;
if (t[0] != 'p') {
t1 = parse_type(t, last, db);
if (t1 == t)
return (first);
nfmt(db, "{0} vector[{1}]", NULL);
return (t1);
}
nfmt(db, "{0} pixel vector[{1}]", NULL);
return (t1);
}
if (first[2] != '_') {
t1 = parse_expression(first + 2, last, db);
if (first == last || t1 == first + 2 || t1[0] != '_')
return (first);
t = t1 + 1;
} else {
nadd_l(db, "", 0);
}
t1 = parse_type(t, last, db);
if (t == t1)
return (first);
nfmt(db, "{1:L} vector[{0}]", "{1:R}");
return (t1);
}
static const char *
parse_decltype(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 4)
return (first);
VERIFY3U(first[0], ==, 'D');
if (first[1] != 't' && first[1] != 'T')
return (first);
size_t n = nlen(db);
const char *t = parse_expression(first + 2, last, db);
if (NAMT(db, n) != 1 || t == first + 2 || t == last || t[0] != 'E')
return (first);
nfmt(db, "decltype({0})", NULL);
return (t + 1);
}
static const char *
parse_array_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
VERIFY3U(first[0], ==, 'A');
if (last - first < 3)
return (first);
const char *t = first + 1;
const char *t1 = NULL;
size_t n = nlen(db);
if (t[0] != '_') {
if (ISDIGIT(t[0]) && t[0] != '0') {
t1 = parse_number(t, last);
if (t1 == last)
return (first);
nadd_l(db, t, (size_t)(t1 - t));
} else {
t1 = parse_expression(t, last, db);
if (t1 == last || t == t1)
return (first);
}
if (t1[0] != '_')
return (first);
t = t1;
} else {
nadd_l(db, "", 0);
}
VERIFY3U(t[0], ==, '_');
t1 = parse_type(t + 1, last, db);
if (t1 == t + 1 || NAMT(db, n) != 2)
return (first);
str_t *r = &name_top(&db->cpp_name)->strp_r;
if (r->str_len > 1 && r->str_s[0] == ' ' && r->str_s[1] == '[')
(void) str_erase(r, 0, 1);
nfmt(db, "{0:L}", " [{1}]{0:R}");
return (t1);
}
static const char *
parse_pointer_to_member_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 3)
return (first);
VERIFY3U(first[0], ==, 'M');
const char *t1 = first + 1;
const char *t2 = NULL;
size_t n = nlen(db);
t2 = parse_type(t1, last, db);
if (t1 == t2)
return (first);
t1 = t2;
t2 = parse_type(t1, last, db);
if (t1 == t2)
return (first);
if (NAMT(db, n) != 2)
return (first);
str_pair_t *func = name_top(&db->cpp_name);
if (str_length(&func->strp_r) > 0 && func->strp_r.str_s[0] == '(')
nfmt(db, "{0:L}({1}::*", "){0:R}");
else
nfmt(db, "{0:L} {1}::*", "{0:R}");
return (t2);
}
static const char *
parse_unresolved_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 *t2 = NULL;
boolean_t global = B_FALSE;
size_t n;
if (t[0] == 'g' && t[1] == 's') {
global = B_TRUE;
t += 2;
}
if (t == last)
return (first);
t2 = parse_base_unresolved_name(t, last, db);
if (t != t2) {
if (global) {
if (nempty(db))
return (first);
(void) str_insert(TOP_L(db), 0, "::", 2);
}
return (t2);
}
if (t[0] != 's' || t[1] != 'r' || last - t < 2)
return (first);
n = nlen(db);
if (t[2] == 'N') {
t += 3;
t2 = parse_unresolved_type(t, last, db);
if (t2 == t || t2 == last)
return (first);
t = t2;
t2 = parse_template_args(t, last, db);
if (t2 != t) {
if (NAMT(db, n) < 2 || t2 == last)
return (first);
nfmt(db, "{1:L}{0}", "{1:R}");
t = t2;
}
VERIFY3U(NAMT(db, n), ==, 1);
while (t[0] != 'E') {
size_t nn = nlen(db);
t2 = parse_unresolved_qualifier_level(t, last, db);
if (t == t2 || t == last || NAMT(db, nn) != 1)
return (first);
t = t2;
}
t++;
t2 = parse_base_unresolved_name(t, last, db);
if (t == t2 || NAMT(db, n) < 2)
return (first);
njoin(db, NAMT(db, n), "::");
return (t2);
}
t += 2;
t2 = parse_unresolved_type(t, last, db);
if (t != t2) {
t = t2;
t2 = parse_template_args(t, last, db);
if (t2 != t)
nfmt(db, "{1:L}{0}", "{1:R}");
t = t2;
t2 = parse_base_unresolved_name(t, last, db);
if (t == t2 || nlen(db) < 2)
return (first);
nfmt(db, "{1:L}::{0}", "{1:R}");
return (t2);
}
t2 = parse_unresolved_qualifier_level(t, last, db);
if (t2 == t || t2 == last)
return (first);
t = t2;
if (global && nlen(db) > 0)
nfmt(db, "::{0:L}", "{0:R}");
while (t[0] != 'E') {
t2 = parse_unresolved_qualifier_level(t, last, db);
if (t == t2 || t == last || nlen(db) < 2)
return (first);
t = t2;
}
t++;
t2 = parse_base_unresolved_name(t, last, db);
if (t == t2 || nlen(db) < 2)
return (first);
njoin(db, NAMT(db, n), "::");
return (t2);
}
static const char *
parse_unresolved_qualifier_level(const char *first, const char *last,
cpp_db_t *db)
{
VERIFY3P(first, <=, last);
return (parse_simple_id(first, last, db));
}
static const char *
parse_base_unresolved_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
const char *t = NULL;
const char *t1 = NULL;
if ((first[0] != 'o' && first[0] != 'd') || first[1] != 'n') {
t = parse_simple_id(first, last, db);
if (t != first)
return (t);
t = parse_operator_name(first, last, db);
if (t == first)
return (first);
t1 = parse_template_args(t, last, db);
if (t1 != t) {
if (nlen(db) < 2)
return (first);
nfmt(db, "{1:L}{0}", "{1:R}");
}
return (t1);
}
if (first[0] == 'd') {
t = parse_destructor_name(first + 2, last, db);
return ((t != first + 2) ? t : first);
}
t = parse_operator_name(first + 2, last, db);
if (t == first + 2)
return (first);
t1 = parse_template_args(t, last, db);
if (t1 != t)
nfmt(db, "{1:L}{0}", "{1:R}");
return (t1);
}
static const char *
parse_destructor_name(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
const char *t = parse_unresolved_type(first, last, db);
if (t == first)
t = parse_simple_id(first, last, db);
if (t == first)
return (first);
nfmt(db, "~{0:L}", "{0:R}");
return (t);
}
static const char *
parse_function_type(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2)
return (first);
VERIFY3U(first[0], ==, 'F');
const char *t = first + 1;
if (t[0] == 'Y')
t++;
const char *t1 = parse_type(t, last, db);
if (t1 == t)
return (first);
size_t n = nlen(db);
int ref_qual = 0;
t = t1;
while (t != last && t[0] != 'E') {
if (t[0] == 'v') {
t++;
continue;
}
if (t[0] == 'R' && t + 1 != last && t[1] == 'E') {
ref_qual = 1;
t++;
continue;
}
if (t[0] == 'O' && t + 1 != last && t[1] == 'E') {
ref_qual = 2;
t++;
continue;
}
t1 = parse_type(t, last, db);
if (t1 == t || t == last)
return (first);
t = t1;
}
if (t == last)
return (first);
njoin(db, NAMT(db, n), ", ");
nfmt(db, "({0})", NULL);
switch (ref_qual) {
case 1:
nfmt(db, "{0} &", NULL);
break;
case 2:
nfmt(db, "{0} &&", NULL);
break;
}
nfmt(db, "{1:L} ", "{0}{1:R}");
return (t + 1);
}
static const char *
parse_template_param(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2 || first[0] != 'T')
return (first);
const char *t = first + 1;
size_t idx = 0;
while (t != last && t[0] != '_') {
if (!ISDIGIT(t[0]))
return (first);
idx *= 10;
idx += t[0] - '0';
t++;
}
if (t == last)
return (first);
VERIFY3U(t[0], ==, '_');
if (first[1] != '_')
idx++;
t++;
if (tempty(db))
return (first);
if (idx >= ttlen(db)) {
nadd_l(db, first, (size_t)(t - first));
db->cpp_fix_forward_references = B_TRUE;
return (t);
}
tsub(db, idx);
return (t);
}
static const char *
parse_template_args(const char *first, const char *last, cpp_db_t *db)
{
VERIFY3P(first, <=, last);
if (last - first < 2 || first[0] != 'I')
return (first);
if (db->cpp_tag_templates)
sub_clear(templ_top(&db->cpp_templ));
const char *t = first + 1;
size_t n = nlen(db);
while (t[0] != 'E') {
if (db->cpp_tag_templates)
tpush(db);
size_t n1 = nlen(db);
const char *t1 = parse_template_arg(t, last, db);
if (db->cpp_tag_templates)
tpop(db);
if (t1 == t || t == last)
return (first);
if (db->cpp_tag_templates)
tsave(db, NAMT(db, n1));
t = t1;
}
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), ", ");
VERIFY3U(nlen(db), >, 0);
str_t *top = TOP_L(db);
if (str_length(top) > 0 &&
top->str_s[top->str_len - 1] == '>')
nfmt(db, "<{0} >", NULL);
else
nfmt(db, "<{0}>", NULL);
return (t + 1);
}
static const char *
parse_discriminator(const char *first, const char *last)
{
VERIFY3P(first, <=, last);
const char *t = NULL;
if (first == last)
return (first);
if (ISDIGIT(first[0])) {
for (t = first; t != last && ISDIGIT(t[0]); t++)
;
if (t != last)
return (first);
return (t);
} else if (first[0] != '_' || first + 1 == last) {
return (first);
}
t = first + 1;
if (ISDIGIT(t[0]))
return (t + 1);
if (t[0] != '_' || t + 1 == last)
return (first);
for (t++; t != last && ISDIGIT(t[0]); t++)
;
if (t == last || t[0] != '_')
return (first);
return (t);
}
const char *
parse_cv_qualifiers(const char *first, const char *last, unsigned *cv)
{
VERIFY3P(first, <=, last);
if (first == last)
return (first);
*cv = 0;
if (first[0] == 'r') {
*cv |= CPP_QUAL_RESTRICT;
first++;
}
if (first != last && first[0] == 'V') {
*cv |= CPP_QUAL_VOLATILE;
first++;
}
if (first != last && first[0] == 'K') {
*cv |= CPP_QUAL_CONST;
first++;
}
return (first);
}
static const char *
parse_number(const char *first, const char *last)
{
VERIFY3P(first, <=, last);
const char *t = first;
if (first == last || (first[0] != 'n' && !ISDIGIT(first[0])))
return (first);
if (t[0] == 'n')
t++;
if (t[0] == '0')
return (t + 1);
while (ISDIGIT(t[0]))
t++;
return (t);
}
static inline boolean_t
is_xdigit(int c)
{
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
return (B_TRUE);
return (B_FALSE);
}
static boolean_t
nempty(cpp_db_t *db)
{
return (name_empty(&db->cpp_name));
}
static size_t
nlen(cpp_db_t *db)
{
return (name_len(&db->cpp_name));
}
static void
nadd_l(cpp_db_t *db, const char *s, size_t len)
{
CK(name_add(&db->cpp_name, s, len, NULL, 0));
}
static void
njoin(cpp_db_t *db, size_t amt, const char *sep)
{
name_t *nm = &db->cpp_name;
CK(name_join(nm, amt, sep));
}
static void
nfmt(cpp_db_t *db, const char *fmt_l, const char *fmt_r)
{
CK(name_fmt(&db->cpp_name, fmt_l, fmt_r));
}
static void
save_top(cpp_db_t *db, size_t amt)
{
CK(sub_save(&db->cpp_subs, &db->cpp_name, amt));
}
static void
sub(cpp_db_t *db, size_t n)
{
CK(sub_substitute(&db->cpp_subs, n, &db->cpp_name));
}
static boolean_t
tempty(const cpp_db_t *db)
{
return (templ_empty(&db->cpp_templ) ? B_TRUE : B_FALSE);
}
static size_t
ttlen(const cpp_db_t *db)
{
return (templ_top_len(&db->cpp_templ));
}
static void
tsub(cpp_db_t *db, size_t n)
{
CK(templ_sub(&db->cpp_templ, n, &db->cpp_name));
}
static void
tpush(cpp_db_t *db)
{
CK(templ_push(&db->cpp_templ));
}
static void
tpop(cpp_db_t *db)
{
templ_pop(&db->cpp_templ);
}
static void
tsave(cpp_db_t *db, size_t amt)
{
CK(templ_save(&db->cpp_name, amt, &db->cpp_templ));
}
static void
db_init(cpp_db_t *db, sysdem_ops_t *ops)
{
(void) memset(db, 0, sizeof (*db));
db->cpp_ops = ops;
name_init(&db->cpp_name, ops);
sub_init(&db->cpp_subs, ops);
templ_init(&db->cpp_templ, ops);
db->cpp_tag_templates = B_TRUE;
db->cpp_try_to_parse_template_args = B_TRUE;
tpush(db);
}
static void
db_fini(cpp_db_t *db)
{
name_fini(&db->cpp_name);
sub_fini(&db->cpp_subs);
templ_fini(&db->cpp_templ);
(void) memset(db, 0, sizeof (*db));
}
static void
print_sp(const str_pair_t *sp, FILE *out)
{
(void) fprintf(out, "{%.*s#%.*s}",
(int)sp->strp_l.str_len, sp->strp_l.str_s,
(int)sp->strp_r.str_len, sp->strp_r.str_s);
}
static void
print_name(const name_t *n, FILE *out)
{
const str_pair_t *sp;
size_t i;
(void) fprintf(out, "Name:\n");
if (name_len(n) == 0)
return;
sp = name_top((name_t *)n);
for (i = 0; i < n->nm_len; i++, sp--) {
(void) fprintf(out, " [%02zu] ", i);
print_sp(sp, out);
(void) fputc('\n', out);
}
(void) fputc('\n', out);
}
static char *
base36(char *buf, size_t val)
{
char tmp[16] = { 0 };
char *p = tmp;
if (val == 0) {
buf[0] = '0';
buf[1] = '\0';
return (buf);
}
while (val > 0) {
size_t r = val % 36;
if (r < 10)
*p++ = r + '0';
else
*p++ = r - 10 + 'A';
val /= 36;
}
char *q = buf;
while (--p >= tmp)
*q++ = *p;
return (buf);
}
static void
print_sub(const sub_t *sub, FILE *out)
{
const name_t *n = sub->sub_items;
(void) fprintf(out, "Substitutions:\n");
if (sub->sub_len == 0)
return;
for (size_t i = 0; i < sub->sub_len; i++, n++) {
(void) printf(" ");
if (i == 0) {
(void) fprintf(out, "%-4s", "S_");
} else {
char buf[16] = { 0 };
char buf2[16] = { 0 };
(void) snprintf(buf, sizeof (buf), "S%s_",
base36(buf2, i));
(void) fprintf(out, "%-4s", buf);
}
(void) fprintf(out, " = ");
(void) fputc('{', out);
for (size_t j = 0; j < n->nm_len; j++) {
if (j > 0)
(void) fputc(' ', out);
print_sp(&n->nm_items[j], out);
}
(void) fputc('}', out);
(void) fputc('\n', out);
}
(void) fputc('\n', out);
}
static void
print_templ(const templ_t *tpl, FILE *out)
{
(void) fprintf(out, "Template\n");
const sub_t *s = templ_top((templ_t *)tpl);
for (size_t i = 0; i < s->sub_len; i++) {
char buf[16] = { 0 };
if (i == 0)
(void) snprintf(buf, sizeof (buf), "%s", "T_");
else
(void) snprintf(buf, sizeof (buf), "T%zu_", i - 1);
(void) fprintf(out, " %-4s = ", buf);
(void) fputc('{', out);
const name_t *n = &s->sub_items[i];
for (size_t j = 0; j < n->nm_len; j++) {
const str_pair_t *sp = &n->nm_items[j];
if (j > 0)
(void) fputc(' ', out);
(void) fprintf(out, "{%.*s#%.*s}",
(int)sp->strp_l.str_len, sp->strp_l.str_s,
(int)sp->strp_r.str_len, sp->strp_r.str_s);
}
(void) fprintf(out, "}\n");
}
(void) fprintf(out, "\n");
}
static void
dump(cpp_db_t *db, FILE *out)
{
print_name(&db->cpp_name, out);
print_sub(&db->cpp_subs, out);
print_templ(&db->cpp_templ, out);
}