diff options
Diffstat (limited to 'wrapperhelper/src/parse.c')
| -rw-r--r-- | wrapperhelper/src/parse.c | 3164 |
1 files changed, 3164 insertions, 0 deletions
diff --git a/wrapperhelper/src/parse.c b/wrapperhelper/src/parse.c new file mode 100644 index 00000000..cba0d9de --- /dev/null +++ b/wrapperhelper/src/parse.c @@ -0,0 +1,3164 @@ +#include "parse.h" + +#include <stdint.h> +#include <string.h> + +#include "cstring.h" +#include "khash.h" +#include "prepare.h" +#include "preproc.h" + +void dump_prepare(const char *filename, FILE *file) { + prepare_t *prep = prepare_new_file(file, filename); + if (!prep) { + printf("Failed to create the prepare structure\n"); + return; + } + while (1) { + preproc_token_t tok = pre_next_token(prep, 0); + preproc_token_print(&tok); + if (preproc_token_isend(&tok)) { + preproc_token_del(&tok); + prepare_del(prep); + return; + } + preproc_token_del(&tok); + } +} +void dump_preproc(const char *filename, FILE *file) { + char *dirname = strchr(filename, '/') ? strndup(filename, (size_t)(strrchr(filename, '/') - filename)) : NULL; + preproc_t *prep = preproc_new_file(file, dirname, filename); + if (!prep) { + printf("Failed to create the preproc structure\n"); + if (dirname) free(dirname); + return; + } + while (1) { + proc_token_t tok = proc_next_token(prep); + proc_token_print(&tok); + if (proc_token_isend(&tok)) { + proc_token_del(&tok); + preproc_del(prep); + return; + } + proc_token_del(&tok); + } +} + +enum decl_storage { + STORAGE_NONE, + STORAGE_TYPEDEF, + STORAGE_EXTERN, + STORAGE_STATIC, + STORAGE_TLS, + STORAGE_TLS_EXTERN, + STORAGE_TLS_STATIC, + STORAGE_AUTO, + STORAGE_REG, +}; +enum decl_spec { + SPEC_NONE, + SPEC_BUILTIN, + SPEC_COMPLEX, + SPEC_IMAGINARY, + SPEC_LONGCOMPLEX, + SPEC_LONGIMAGINARY, + SPEC_BUILTIN_NOINT, + SPEC_TYPE, +}; +VECTOR_DECLARE_STATIC(size_t, size_t) +VECTOR_IMPL_STATIC(size_t, (void)) + +#define VALIDATION_DECL 1 +#define VALIDATION_LAST_DECL 2 +#define VALIDATION_FUN 3 +// Assumes sizeof(void*) == sizeof(unsigned long) +static int validate_type(type_t *typ, type_t *(*builtins)[LAST_BUILTIN + 1]) { + if (typ->is_validated) return 1; + typ->is_validated = 1; + if (typ->is_restrict) { + if (typ->typ != TYPE_PTR) { + printf("Error: only pointers to object types may be restrict-qualified\n"); + return 0; + } + if (typ->val.typ->typ == TYPE_FUNCTION) { + printf("Error: only pointers to object types may be restrict-qualified\n"); + return 0; + } + } + if (typ->is_atomic) { + if ((typ->typ == TYPE_ARRAY) || (typ->typ == TYPE_FUNCTION)) { + printf("Error: array types and function types may not be atomic-qualified\n"); + return 0; + } + } + switch (typ->typ) { + case TYPE_BUILTIN: + typ->szinfo = (*builtins)[typ->val.builtin]->szinfo; + return 1; + case TYPE_ARRAY: + if (typ->val.array.typ->is_incomplete || (typ->val.array.typ->typ == TYPE_FUNCTION)) { + printf("Error: array types must point to complete object types\n"); + return 0; + } + if ((typ->val.array.typ->typ == TYPE_STRUCT_UNION) && typ->val.array.typ->val.st->has_incomplete) { + printf("Error: array types may not (inductively) point to structures which last element is incomplete\n"); + return 0; + } + if ((typ->is_atomic) || (typ->is_const) || (typ->is_restrict) || (typ->is_volatile)) { + // qualifier-type-list in array declaration is only allowed in function argument declaration under certain circumstances + printf("Error: array types may not be qualified\n"); + return 0; + } + if (!validate_type(typ->val.array.typ, builtins)) return 0; + if (typ->val.array.array_sz == (size_t)-1) { + typ->szinfo.size = 0; + typ->szinfo.align = (typ->val.array.typ->szinfo.align < 16) ? 16 : typ->val.array.typ->szinfo.align; + } else { + typ->szinfo.size = typ->val.array.array_sz * typ->val.array.typ->szinfo.size; + typ->szinfo.align = + ((typ->szinfo.size >= 16) && (typ->val.array.typ->szinfo.align < 16)) ? + 16 : + typ->val.array.typ->szinfo.align; + } + return 1; + case TYPE_PTR: + typ->szinfo.size = LONG_IS_32BITS ? 4 : 8; + typ->szinfo.align = LONG_IS_32BITS ? 4 : 8; + return validate_type(typ->val.typ, builtins); + case TYPE_FUNCTION: + if ((typ->val.fun.ret->typ == TYPE_FUNCTION) || (typ->val.fun.ret->typ == TYPE_ARRAY)) { + printf("Error: function types may not return function or array types\n"); + return 0; + } + if (typ->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + // Adjust the argument if necessary + if (typ->val.fun.args[i]->typ == TYPE_ARRAY) { + // Adjustment to pointer + typ->val.fun.args[i]->typ = TYPE_PTR; + typ->val.fun.args[i]->val.typ = typ->val.fun.args[i]->val.array.typ; + } else if (typ->val.fun.args[i]->typ == TYPE_FUNCTION) { + // Adjustment to pointer + type_t *t2 = type_new_ptr(typ->val.fun.args[i]); + if (!t2) { + printf("Error: failed to adjust type of argument from function to pointer\n"); + return 0; + } + typ->val.fun.args[i] = t2; + } + if (!validate_type(typ->val.fun.args[i], builtins)) return 0; + } + } + typ->szinfo.size = 0; + typ->szinfo.align = 0; + return validate_type(typ->val.fun.ret, builtins); + case TYPE_STRUCT_UNION: { + if (!typ->val.st->is_defined) return typ->is_incomplete; + size_t max_align = 1, cur_sz = 0, cur_bit = 0; + for (size_t i = 0; i < typ->val.st->nmembers; ++i) { + // Adjust the argument if necessary + st_member_t *mem = &typ->val.st->members[i]; + if (mem->typ->typ == TYPE_FUNCTION) { + printf("Error: structures may not contain function members\n"); + return 0; + } + if (mem->typ->is_incomplete) { + if ((i != typ->val.st->nmembers - 1) || !typ->val.st->is_struct || (mem->typ->typ != TYPE_ARRAY)) { + // The last element of a structure may be a VLA + printf("Error: structures may not contain incomplete members\n"); + return 0; + } + typ->val.st->has_incomplete = 1; + } + if (!validate_type(mem->typ, builtins)) return 0; + if (!typ->val.st->is_struct && (mem->typ->typ == TYPE_STRUCT_UNION)) { + typ->val.st->has_incomplete |= mem->typ->val.st->has_incomplete; + } + if (mem->is_bitfield) { + if (!typ->val.st->is_struct) { + printf("Error: TODO: bitfield in union\n"); + return 0; + } + if (mem->typ->is_atomic) { + printf("Error: atomic bitfields are not supported\n"); + return 0; + } + if (mem->typ->typ != TYPE_BUILTIN) { + printf("Error: bitfields can only have a specific subset of types\n"); + return 0; + } + if ((mem->typ->val.builtin != BTT_BOOL) && (mem->typ->val.builtin != BTT_INT) + && (mem->typ->val.builtin != BTT_SINT) && (mem->typ->val.builtin != BTT_UINT)) { + printf("Error: bitfields can only have a specific subset of types\n"); + return 0; + } + if (!mem->name && (mem->typ->szinfo.align > max_align)) { + printf("Error: TODO: unnamed bitfield member with greater alignment (width=%zu)\n", mem->bitfield_width); + return 0; + } + if (mem->bitfield_width) { + if (mem->name && (max_align < mem->typ->szinfo.align)) max_align = mem->typ->szinfo.align; + size_t cur_block = cur_sz / mem->typ->szinfo.align; + size_t end_block = (cur_sz + (cur_bit + mem->bitfield_width - 1) / 8) / mem->typ->szinfo.align; + if (cur_block == end_block) { + cur_bit += mem->bitfield_width; + cur_sz += cur_bit / 8; + cur_bit %= 8; + } else { + cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + (mem->bitfield_width / 8); + cur_bit = mem->bitfield_width % 8; + } + } else { + if (max_align < mem->typ->szinfo.align) max_align = mem->typ->szinfo.align; + cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + mem->typ->szinfo.size; + printf("Error: TODO: unnamed zero-width bitfield member\n"); + return 0; + } + } else { + if (max_align < mem->typ->szinfo.align) max_align = mem->typ->szinfo.align; + if (typ->val.st->is_struct) { + if (cur_bit) { + cur_bit = 0; + ++cur_sz; + } + cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + mem->typ->szinfo.size; + } else { + if (cur_sz < mem->typ->szinfo.size) cur_sz = mem->typ->szinfo.size; + } + } + } + if (cur_bit) { + cur_bit = 0; + ++cur_sz; + } + typ->szinfo.align = max_align; + typ->szinfo.size = (cur_sz + max_align - 1) & ~(max_align - 1); + return 1; } + case TYPE_ENUM: + if (typ->val.typ->typ != TYPE_BUILTIN) return 0; + typ->szinfo = typ->val.typ->szinfo; + return 1; + } + return 0; +} +static int validate_storage_type(enum decl_storage storage, type_t *(*builtins)[LAST_BUILTIN + 1], type_t *typ, enum token_sym_type_e sym) { + // We may still do adjustments here + if (!validate_type(typ, builtins)) return 0; + if (typ->typ == TYPE_FUNCTION) { + if ((storage == STORAGE_TLS) || (storage == STORAGE_TLS_EXTERN) || (storage == STORAGE_TLS_STATIC)) { + printf("Error: functions cannot be thread local\n"); + return 0; + } + if ((sym == SYM_COMMA) || (sym == SYM_RPAREN) || (sym == SYM_SEMICOLON)) { + return (sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL; + } else if (sym == SYM_LBRACKET) { + return VALIDATION_FUN; + } + } else { + if ((sym == SYM_COMMA) || (sym == SYM_RPAREN) || (sym == SYM_SEMICOLON) || ((storage != STORAGE_TYPEDEF) && (sym == SYM_EQ))) { + return (sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL; + } + } + printf("TODO: validate_storage_type on storage %u, sym %s (%u), valid type ", storage, sym2str[sym], sym); + type_print(typ); + printf("\n"); + return 0; +} + +static void promote_csts(num_constant_t *v1, num_constant_t *v2) { + switch (v1->typ) { + case NCT_FLOAT: + switch (v2->typ) { + case NCT_FLOAT: break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = v1->val.f; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = v1->val.f; break; + case NCT_INT32: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.i64; break; + case NCT_UINT64: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.u64; break; + } + break; + case NCT_DOUBLE: + switch (v2->typ) { + case NCT_FLOAT: v2->typ = NCT_DOUBLE; v2->val.d = v2->val.f; break; + case NCT_DOUBLE: break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = v1->val.d; break; + case NCT_INT32: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.i64; break; + case NCT_UINT64: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.u64; break; + } + break; + case NCT_LDOUBLE: + switch (v2->typ) { + case NCT_FLOAT: v2->typ = NCT_LDOUBLE; v2->val.l = v2->val.f; break; + case NCT_DOUBLE: v2->typ = NCT_LDOUBLE; v2->val.l = v2->val.d; break; + case NCT_LDOUBLE: break; + case NCT_INT32: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.i64; break; + case NCT_UINT64: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.u64; break; + } + break; + case NCT_INT32: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.i32; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.i32; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.i32; break; + case NCT_INT32: break; + case NCT_UINT32: v1->typ = NCT_UINT32; v1->val.u32 = (uint32_t)v1->val.i32; break; + case NCT_INT64: v1->typ = NCT_INT64; v1->val.i64 = v1->val.i32; break; + case NCT_UINT64: v1->typ = NCT_UINT64; v1->val.u64 = (uint64_t)v1->val.i32; break; + } + break; + case NCT_UINT32: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.u32; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.u32; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.u32; break; + case NCT_INT32: v2->typ = NCT_UINT32; v2->val.u32 = (uint32_t)v2->val.i32; break; + case NCT_UINT32: break; + case NCT_INT64: v1->typ = NCT_INT64; v1->val.i64 = v1->val.u32; break; + case NCT_UINT64: v1->typ = NCT_UINT64; v1->val.u64 = v1->val.u32; break; + } + break; + case NCT_INT64: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.i64; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.i64; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.i64; break; + case NCT_INT32: v2->typ = NCT_INT64; v2->val.i64 = v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_INT64; v2->val.i64 = v2->val.u32; break; + case NCT_INT64: break; + case NCT_UINT64: v1->typ = NCT_UINT64; v1->val.u64 = (uint64_t)v1->val.i64; break; + } + break; + case NCT_UINT64: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.u64; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.u64; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.u64; break; + case NCT_INT32: v2->typ = NCT_UINT64; v2->val.u64 = (uint64_t)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_UINT64; v2->val.u64 = v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_UINT64; v2->val.u64 = (uint64_t)v2->val.i64; break; + case NCT_UINT64: break; + } + break; + } +} + +VECTOR_DECLARE_STATIC(exprs, expr_t*) +#define expr_del_ptr(p) expr_del(*(p)) +VECTOR_IMPL_STATIC(exprs, expr_del_ptr) +#undef expr_del_ptr +struct expr_partial_op { + enum expr_partial_op_e { + EPO_LPAREN, + EPO_SIZEOF, + EPO_CAST, + EPO_FUNCALL, + EPO_UNARY, + EPO_BINARY_ARG, + EPO_BINARY_ARG_SYM, + EPO_TERNARY_ARG1, + EPO_TERNARY_ARG2, + } typ; + int once_done_want_level; + int once_done_is_level; + union { + char c; // Empty destructor + type_t *typ; + struct { + expr_t *f; + VECTOR(exprs) *exprs; + } funcall; + enum unary_op_e unop; + struct { + enum binary_op_e op; + expr_t *e1; + enum token_sym_type_e last_sym; + } binop; + struct { + enum ternary_op_e op; + expr_t *e1; + expr_t *e2; + int once_done2_want_level; + enum token_sym_type_e arg23_sep; + } ternop; + } val; +}; +void expr_partial_op_del(struct expr_partial_op *epo) { + switch (epo->typ) { + case EPO_LPAREN: + case EPO_SIZEOF: + return; + case EPO_CAST: + type_del(epo->val.typ); + return; + case EPO_FUNCALL: + expr_del(epo->val.funcall.f); + vector_del(exprs, epo->val.funcall.exprs); + return; + case EPO_UNARY: + return; + case EPO_BINARY_ARG: + case EPO_BINARY_ARG_SYM: + expr_del(epo->val.binop.e1); + return; + case EPO_TERNARY_ARG1: + expr_del(epo->val.ternop.e1); + return; + case EPO_TERNARY_ARG2: + expr_del(epo->val.ternop.e1); + expr_del(epo->val.ternop.e2); + return; + } +} +VECTOR_DECLARE_STATIC(expr_pops, struct expr_partial_op) +VECTOR_IMPL_STATIC(expr_pops, expr_partial_op_del) + +VECTOR_DECLARE_STATIC(types, type_t*) +#define type_ptr_del(t) type_del(*(t)) +VECTOR_IMPL_STATIC(types, type_ptr_del) +#undef type_ptr_del +VECTOR_DECLARE_STATIC(st_members, st_member_t) +VECTOR_IMPL_STATIC(st_members, st_member_del) + +struct parse_declarator_dest_s { + file_t *f; // is_init, is_list + struct { + khash_t(struct_map) *struct_map; + khash_t(type_map) *type_map; + khash_t(type_map) *enum_map; + khash_t(type_set) *type_set; + type_t *(*builtins)[LAST_BUILTIN + 1]; + khash_t(const_map) *const_map; + type_t *dest; + } argt; // !is_init, !is_list + struct { + khash_t(struct_map) *struct_map; + khash_t(type_map) *type_map; + khash_t(type_map) *enum_map; + khash_t(type_set) *type_set; + type_t *(*builtins)[LAST_BUILTIN + 1]; + khash_t(const_map) *const_map; + VECTOR(st_members) *dest; + } structms; // !is_init, is_list +}; +#define PDECL_STRUCT_MAP ((is_init && is_list) ? dest->f->struct_map : (!is_init && is_list) ? dest->structms.struct_map : dest->argt.struct_map) +#define PDECL_TYPE_MAP ((is_init && is_list) ? dest->f->type_map : (!is_init && is_list) ? dest->structms.type_map : dest->argt.type_map) +#define PDECL_ENUM_MAP ((is_init && is_list) ? dest->f->enum_map : (!is_init && is_list) ? dest->structms.enum_map : dest->argt.enum_map) +#define PDECL_TYPE_SET ((is_init && is_list) ? dest->f->type_set : (!is_init && is_list) ? dest->structms.type_set : dest->argt.type_set) +#define PDECL_BUILTINS ((is_init && is_list) ? &dest->f->builtins : (!is_init && is_list) ? dest->structms.builtins : dest->argt.builtins) +#define PDECL_CONST_MAP ((is_init && is_list) ? dest->f->const_map : (!is_init && is_list) ? dest->structms.const_map : dest->argt.const_map) +static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, type_t *base_type, + int is_init, int is_list, int allow_decl, int allow_abstract); + +// declaration-specifier with storage != NULL +// specifier-qualifier-list + static_assert-declaration with storage == NULL +static int parse_declaration_specifier(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum decl_spec *spec, type_t *typ); + + +void expr_print(expr_t *e) { + switch (e->typ) { + case ETY_VAR: printf("%s", string_content(e->val.var)); break; + case ETY_CONST: + switch (e->val.cst.typ) { + case NCT_FLOAT: printf("%ff", e->val.cst.val.f); break; + case NCT_DOUBLE: printf("%f", e->val.cst.val.d); break; + case NCT_LDOUBLE: printf("%Lfl", e->val.cst.val.l); break; + case NCT_INT32: printf("%d", e->val.cst.val.i32); break; + case NCT_UINT32: printf("%uu", e->val.cst.val.u32); break; + case NCT_INT64: printf("%ldll", e->val.cst.val.i64); break; + case NCT_UINT64: printf("%lullu", e->val.cst.val.u64); break; + } + break; + case ETY_CALL: printf("(call)"); break; + case ETY_ACCESS: printf("(.)"); break; + case ETY_PTRACCESS: printf("(->)"); break; + case ETY_UNARY: + switch (e->val.unary.typ) { + case UOT_POSTINCR: printf("(++)"); expr_print(e->val.unary.e); break; + case UOT_POSTDECR: printf("(--)"); expr_print(e->val.unary.e); break; + case UOT_PREINCR: printf("++"); expr_print(e->val.unary.e); break; + case UOT_PREDECR: printf("--"); expr_print(e->val.unary.e); break; + case UOT_REF: printf("&"); expr_print(e->val.unary.e); break; + case UOT_POS: printf("(+)"); expr_print(e->val.unary.e); break; + case UOT_NEG: printf("(-)"); expr_print(e->val.unary.e); break; + case UOT_DEREF: printf("(*)"); expr_print(e->val.unary.e); break; + case UOT_ANOT: printf("~"); expr_print(e->val.unary.e); break; + case UOT_BNOT: printf("!"); expr_print(e->val.unary.e); break; + } + break; + case ETY_BINARY: { + printf("("); + expr_print(e->val.binary.e1); + switch (e->val.binary.typ) { + case BOT_ADD: printf(") + ("); break; + case BOT_SUB: printf(") - ("); break; + case BOT_MUL: printf(") * ("); break; + case BOT_DIV: printf(") / ("); break; + case BOT_MOD: printf(") %% ("); break; + case BOT_LSH: printf(") << ("); break; + case BOT_RSH: printf(") >> ("); break; + case BOT_LT: printf(") < ("); break; + case BOT_GT: printf(") > ("); break; + case BOT_LE: printf(") <= ("); break; + case BOT_GE: printf(") >= ("); break; + case BOT_EQ: printf(") == ("); break; + case BOT_NE: printf(") != ("); break; + case BOT_AAND: printf(") & ("); break; + case BOT_AXOR: printf(") ^ ("); break; + case BOT_AOR: printf(") | ("); break; + case BOT_BAND: printf(") && ("); break; + case BOT_BOR: printf(") || ("); break; + case BOT_ASSGN_EQ: printf(") = ("); break; + case BOT_ASSGN_ADD: printf(") += ("); break; + case BOT_ASSGN_SUB: printf(") -= ("); break; + case BOT_ASSGN_MUL: printf(") *= ("); break; + case BOT_ASSGN_DIV: printf(") /= ("); break; + case BOT_ASSGN_MOD: printf(") %%= ("); break; + case BOT_ASSGN_LSH: printf(") <<= ("); break; + case BOT_ASSGN_RSH: printf(") >>= ("); break; + case BOT_ASSGN_AAND: printf(") &= ("); break; + case BOT_ASSGN_AXOR: printf(") ^= ("); break; + case BOT_ASSGN_AOR: printf(") |= ("); break; + case BOT_COMMA: printf("), ("); break; + case BOT_ARRAY: printf(")["); break; + } + expr_print(e->val.binary.e2); + printf(e->val.binary.typ == BOT_ARRAY ? "]" : ")"); + break; } + case ETY_TERNARY: { + printf("("); + expr_print(e->val.ternary.e1); + switch (e->val.ternary.typ) { + case TOT_COND: printf(") ? "); break; + } + expr_print(e->val.ternary.e2); + switch (e->val.ternary.typ) { + case TOT_COND: printf(" : ("); break; + } + expr_print(e->val.ternary.e3); + printf(")"); + break; } + case ETY_CAST: printf("("); type_print(e->val.cast.typ); printf(")"); expr_print(e->val.cast.e); break; + } +} + + +static int is_type_spec_qual_kw(enum token_keyword_type_e kw) { + return + (kw == KW_ATOMIC) || + (kw == KW_BOOL) || + (kw == KW_CHAR) || + (kw == KW_COMPLEX) || + (kw == KW_CONST) || + (kw == KW_DOUBLE) || + (kw == KW_ENUM) || + (kw == KW_FLOAT) || + (kw == KW_IMAGINARY) || + (kw == KW_INT) || + (kw == KW_INT128) || + (kw == KW_LONG) || + (kw == KW_RESTRICT) || + (kw == KW_SHORT) || + (kw == KW_SIGNED) || + (kw == KW_STRUCT) || + (kw == KW_UNION) || + (kw == KW_UNSIGNED) || + (kw == KW_VOID) || + (kw == KW_VOLATILE) || + 0; +} +#define IS_BEGIN_TYPE_NAME \ + (((tok->tokt == PTOK_KEYWORD) && is_type_spec_qual_kw(tok->tokv.kw)) || \ + ((tok->tokt == PTOK_IDENT) && ((it = kh_get(type_map, type_map, string_content(tok->tokv.str))) != kh_end(type_map)))) +static int parse_type_name(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum token_sym_type_e end_sym, type_t **typ) { + enum decl_spec spec = SPEC_NONE; + if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, &spec, *typ)) { + type_del(*typ); + goto failed; + } + *typ = type_try_merge(*typ, type_set); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == end_sym)) { + return validate_type(*typ, builtins); + } + struct parse_declarator_dest_s dest2; + dest2.argt.dest = NULL; + dest2.argt.struct_map = struct_map; + dest2.argt.type_map = type_map; + dest2.argt.enum_map = enum_map; + dest2.argt.type_set = type_set; + dest2.argt.builtins = builtins; + dest2.argt.const_map = const_map; + if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, *typ, 0, 0, 0, 1)) { + // Token is deleted + type_del(*typ); + goto failed; + } + type_del(*typ); + if (!dest2.argt.dest) { + printf("Internal error: argument type is NULL\n"); + // Empty destructor + goto failed; + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != end_sym)) { + type_del(dest2.argt.dest); + proc_token_del(tok); + goto failed; + } + *typ = dest2.argt.dest; + *typ = type_try_merge(*typ, type_set); + return validate_type(*typ, builtins); + +failed: + return 0; +} + +static expr_t *parse_expression(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, int expr_level) { + // Note that expr_level >= 1; expr_level = 0 doesn't appear in the grammar + if ((expr_level < 1) || (expr_level > 16)) { + printf("Internal error: invalid expression level %d\n", expr_level); + return NULL; + } + + VECTOR(expr_pops) *op_stack = vector_new(expr_pops); + if (!op_stack) { + printf("Error: failed to create operation stack"); + proc_token_del(tok); + return NULL; + } + + int has_level; + expr_t *e; +pushed_expr: + has_level = -1; + e = NULL; + +expr_new_token: + // printf("expr_new_token with level %d / %d\n", has_level, expr_level); + // proc_token_print(tok); + if (tok->tokt == PTOK_IDENT) { + if (has_level != -1) { + printf("Error: invalid expression: unexpected identifier '%s'\n", string_content(tok->tokv.str)); + string_del(tok->tokv.str); + goto failed; + } + has_level = 0; + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + e->typ = ETY_VAR; + e->val.var = tok->tokv.str; + *tok = proc_next_token(prep); + goto expr_new_token; + } else if (tok->tokt == PTOK_NUM) { + if (has_level != -1) { + printf("Error: invalid expression: unexpected number '%s'\n", string_content(tok->tokv.str)); + string_del(tok->tokv.str); + goto failed; + } + has_level = 0; + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + e->typ = ETY_CONST; + if (!num_constant_convert(tok->tokv.str, &e->val.cst)) { + string_del(tok->tokv.str); + goto failed; + } + string_del(tok->tokv.str); + *tok = proc_next_token(prep); + goto expr_new_token; + } + +#define UNOP(toksym, opt, main_lv, right_lv) \ + if ((expr_level >= main_lv) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == toksym)) { \ + if (has_level == -1) { \ + struct expr_partial_op pop = { \ + .once_done_want_level = expr_level, \ + .once_done_is_level = main_lv, \ + .typ = EPO_UNARY, \ + .val.unop = opt \ + }; \ + if (!vector_push(expr_pops, op_stack, pop)) { \ + printf("Error: failed to add partial operation to operation stack\n"); \ + /* Empty destructor */ \ + goto failed; \ + } \ + expr_level = right_lv; \ + *tok = proc_next_token(prep); \ + goto pushed_expr; \ + } \ + } +#define BINOP(toksym, opt, main_lv, left_lv, right_lv) \ + if ((expr_level >= main_lv) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == toksym)) { \ + if ((has_level != -1) && (has_level <= left_lv)) { \ + struct expr_partial_op pop = { \ + .once_done_want_level = expr_level, \ + .once_done_is_level = main_lv, \ + .typ = EPO_BINARY_ARG, \ + .val.binop = {.op = opt, .e1 = e} \ + }; \ + if (!vector_push(expr_pops, op_stack, pop)) { \ + printf("Error: failed to add partial operation to operation stack\n"); \ + /* Empty destructor */ \ + goto failed; \ + } \ + expr_level = right_lv; \ + *tok = proc_next_token(prep); \ + goto pushed_expr; \ + } \ + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_PLUSPLUS)) { + if ((has_level != -1) && (has_level <= 1)) { + has_level = 1; + expr_t *new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + new_e->typ = ETY_UNARY; + new_e->val.unary.typ = UOT_POSTINCR; + new_e->val.unary.e = e; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_DASHDASH)) { + if ((has_level != -1) && (has_level <= 1)) { + has_level = 1; + expr_t *new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + new_e->typ = ETY_UNARY; + new_e->val.unary.typ = UOT_POSTDECR; + new_e->val.unary.e = e; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LSQBRACKET)) { + if ((has_level != -1) && (has_level <= 1)) { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 1, + .typ = EPO_BINARY_ARG_SYM, + .val.binop = {.last_sym = SYM_RSQBRACKET, .op = BOT_ARRAY, .e1 = e} + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + /* Empty destructor */ + goto failed; + } + expr_level = 16; + *tok = proc_next_token(prep); + goto pushed_expr; + } + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && ((tok->tokv.sym == SYM_DOT) || (tok->tokv.sym == SYM_DASHGT))) { + if ((has_level != -1) && (has_level <= 1)) { + has_level = 1; + int is_ptr = tok->tokv.sym == SYM_DASHGT; + + *tok = proc_next_token(prep); + if (tok->tokt != PTOK_IDENT) { + printf("Error: invalid expression: unexpected token after access symbol\n"); + proc_token_del(tok); + goto failed; + } + expr_t *new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + new_e->typ = is_ptr ? ETY_PTRACCESS : ETY_ACCESS; + new_e->val.access.val = e; + new_e->val.access.member = tok->tokv.str; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + } + UNOP(SYM_PLUSPLUS, UOT_PREINCR, 2, 2) + UNOP(SYM_DASHDASH, UOT_PREDECR, 2, 2) + UNOP(SYM_AMP, UOT_REF, 2, 3) + UNOP(SYM_PLUS, UOT_POS, 2, 3) + UNOP(SYM_DASH, UOT_NEG, 2, 3) + UNOP(SYM_STAR, UOT_DEREF, 2, 3) + UNOP(SYM_TILDE, UOT_ANOT, 2, 3) + UNOP(SYM_EXCL, UOT_BNOT, 2, 3) + BINOP(SYM_STAR, BOT_MUL, 4, 4, 3) + BINOP(SYM_SLASH, BOT_DIV, 4, 4, 3) + BINOP(SYM_PERCENT, BOT_MOD, 4, 4, 3) + BINOP(SYM_PLUS, BOT_ADD, 5, 5, 4) + BINOP(SYM_DASH, BOT_SUB, 5, 5, 4) + BINOP(SYM_LTLT, BOT_LSH, 6, 6, 5) + BINOP(SYM_GTGT, BOT_RSH, 6, 6, 5) + BINOP(SYM_LT, BOT_LT, 7, 7, 6) + BINOP(SYM_GT, BOT_GT, 7, 7, 6) + BINOP(SYM_LTEQ, BOT_LE, 7, 7, 6) + BINOP(SYM_GTEQ, BOT_GE, 7, 7, 6) + BINOP(SYM_EQEQ, BOT_EQ, 8, 8, 7) + BINOP(SYM_EXCLEQ, BOT_NE, 8, 8, 7) + BINOP(SYM_AMP, BOT_AAND, 9, 9, 8) + BINOP(SYM_HAT, BOT_AXOR, 10, 10, 9) + BINOP(SYM_PIPE, BOT_AOR, 11, 11, 10) + BINOP(SYM_AMPAMP, BOT_BAND, 12, 12, 11) + BINOP(SYM_PIPEPIPE, BOT_BOR, 13, 13, 12) + if ((expr_level >= 14) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) { + if ((has_level != -1) && (has_level <= 13)) { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 14, + .typ = EPO_TERNARY_ARG1, + .val.ternop = {.arg23_sep = SYM_COLON, .once_done2_want_level = 14, .op = TOT_COND, .e1 = e} + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + // Empty destructor + goto failed; + } + expr_level = 16; + *tok = proc_next_token(prep); + goto pushed_expr; + } + } + BINOP(SYM_EQ, BOT_ASSGN_EQ, 15, 2, 15) + BINOP(SYM_STAREQ, BOT_ASSGN_MUL, 15, 2, 15) + BINOP(SYM_SLASHEQ, BOT_ASSGN_DIV, 15, 2, 15) + BINOP(SYM_PERCENTEQ, BOT_ASSGN_MOD, 15, 2, 15) + BINOP(SYM_PLUSEQ, BOT_ASSGN_ADD, 15, 2, 15) + BINOP(SYM_DASHEQ, BOT_ASSGN_SUB, 15, 2, 15) + BINOP(SYM_LTLTEQ, BOT_ASSGN_LSH, 15, 2, 15) + BINOP(SYM_GTGTEQ, BOT_ASSGN_RSH, 15, 2, 15) + BINOP(SYM_AMPEQ, BOT_ASSGN_AAND, 15, 2, 15) + BINOP(SYM_HATEQ, BOT_ASSGN_AXOR, 15, 2, 15) + BINOP(SYM_PIPEEQ, BOT_ASSGN_AOR, 15, 2, 15) + BINOP(SYM_COMMA, BOT_COMMA, 16, 16, 15) + + // expr2 ::= sizeof expr2 + // which includes expr2 ::= sizeof ( expr16 ) + // expr2 ::= sizeof ( type-name ) + if ((has_level == -1) && (expr_level >= 2) && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw = KW_SIZEOF)) { + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 2, + .typ = EPO_SIZEOF, + .val.c = '\0' + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + expr_level = 2; + goto pushed_expr; + } + // Empty destructor + *tok = proc_next_token(prep); + khiter_t it; + if (IS_BEGIN_TYPE_NAME) { + type_t *typ = type_new(); + if (!typ) { + printf("Error: failed to create new type info structure\n"); + proc_token_del(tok); + goto failed; + } + if (!parse_type_name(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, SYM_RPAREN, &typ)) { + proc_token_del(tok); + goto failed; + } + if (!typ->is_validated || typ->is_incomplete) { + printf("Error: cannot get the size of an incomplete type\n"); + proc_token_del(tok); + goto failed; + } + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + type_del(typ); + proc_token_del(tok); + goto failed; + } + e->typ = ETY_CONST; + e->val.cst.typ = NCT_UINT64; + e->val.cst.val.u64 = typ->szinfo.size; + has_level = 2; + type_del(typ); + if (!e->val.cst.val.u64) { + proc_token_del(tok); + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + goto expr_new_token; + } else { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 2, + .typ = EPO_SIZEOF, + .val.c = 0 + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + pop = (struct expr_partial_op){ + .once_done_want_level = 2, + .once_done_is_level = 0, + .typ = EPO_LPAREN, + .val.c = 0 + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + expr_level = 16; + goto pushed_expr; + } + } + + // expr0 ::= ( expr16 ) + // expr1 ::= expr1 ( ) + // expr1 ::= expr1 ( expr15 {, expr15}* ) + // expr1 ::= ( type-name ) { initializer-list ,? } + // expr3 ::= ( type-name ) expr3 + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + if ((has_level != -1) && (expr_level >= 1) && (has_level <= 1)) { + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + expr_t *new_e = malloc(sizeof *new_e); + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + // Empty destructor + goto failed; + } + new_e->typ = ETY_CALL; + new_e->val.call.fun = e; + new_e->val.call.nargs = 0; + new_e->val.call.args = NULL; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + // We want a function call with at least one argument + VECTOR(exprs) *exprs = vector_new_cap(exprs, 1); + if (!exprs) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 1, + .typ = EPO_FUNCALL, + .val.funcall = {.f = e, .exprs = exprs} + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + vector_del(exprs, exprs); + proc_token_del(tok); + goto failed; + } + expr_level = 15; + goto pushed_expr; + } + if (has_level == -1) { + *tok = proc_next_token(prep); + khiter_t it; + if ((expr_level >= 1) && IS_BEGIN_TYPE_NAME) { + type_t *typ = type_new(); + if (!typ) { + printf("Error: failed to create new type info structure\n"); + proc_token_del(tok); + goto failed; + } + if (!parse_type_name(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, SYM_RPAREN, &typ)) { + type_del(typ); + proc_token_del(tok); + goto failed; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) { + printf("Error: TODO: initializer-list\n"); + type_del(typ); + proc_token_del(tok); + goto failed; + } else if (expr_level < 3) { + printf("Error: cast expression (level 3) but the expression level is at most 2\n"); + type_del(typ); + proc_token_del(tok); + goto failed; + } else { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 3, + .typ = EPO_CAST, + .val.typ = typ + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + /* Empty destructor */ + goto failed; + } + expr_level = 3; + *tok = proc_next_token(prep); + goto pushed_expr; + } + } else { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 0, + .typ = EPO_LPAREN, + .val.c = 0 + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + expr_level = 16; + goto pushed_expr; + } + } + } + + if (vector_size(expr_pops, op_stack)) { + if (has_level != -1) { + struct expr_partial_op *pop = &vector_last(expr_pops, op_stack); + expr_t *new_e; + switch (pop->typ) { + case EPO_LPAREN: + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + vector_pop_nodel(expr_pops, op_stack); + // Empty destructor + *tok = proc_next_token(prep); + goto expr_new_token; + } + break; + case EPO_SIZEOF: + printf("Error: TODO: find type of expression\n"); + proc_token_del(tok); + goto failed; + case EPO_CAST: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_CAST; + new_e->val.cast.typ = pop->val.typ; + new_e->val.cast.e = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + case EPO_FUNCALL: + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + if (!vector_push(exprs, pop->val.funcall.exprs, e)) { + printf("Error: failed to add argument to argument stack\n"); + // Empty destructor + goto failed; + } + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + // Empty destructor + goto failed; + } + e->typ = ETY_CALL; + e->val.call.fun = pop->val.funcall.f; + e->val.call.nargs = vector_size(exprs, pop->val.funcall.exprs); + e->val.call.args = vector_steal(exprs, pop->val.funcall.exprs); + vector_pop_nodel(expr_pops, op_stack); + // Empty destructor + *tok = proc_next_token(prep); + goto expr_new_token; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) { + if (!vector_push(exprs, pop->val.funcall.exprs, e)) { + printf("Error: failed to add argument to argument stack\n"); + // Empty destructor + goto failed; + } + // expr_level is already 15 + // Empty destructor + *tok = proc_next_token(prep); + goto pushed_expr; + } + break; + case EPO_UNARY: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_UNARY; + new_e->val.unary.typ = pop->val.unop; + new_e->val.unary.e = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + case EPO_BINARY_ARG_SYM: + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != pop->val.binop.last_sym)) break; + // Empty destructor + *tok = proc_next_token(prep); + /* FALLTHROUGH */ + case EPO_BINARY_ARG: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_BINARY; + new_e->val.binary.typ = pop->val.binop.op; + new_e->val.binary.e1 = pop->val.binop.e1; + new_e->val.binary.e2 = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + case EPO_TERNARY_ARG1: + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == pop->val.ternop.arg23_sep)) { + pop->typ = EPO_TERNARY_ARG2; + pop->val.ternop.e2 = e; + expr_level = pop->val.ternop.once_done2_want_level; + // Empty destructor + *tok = proc_next_token(prep); + goto pushed_expr; + } + break; + case EPO_TERNARY_ARG2: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_TERNARY; + new_e->val.ternary.typ = pop->val.ternop.op; + new_e->val.ternary.e1 = pop->val.ternop.e1; + new_e->val.ternary.e2 = pop->val.ternop.e2; + new_e->val.ternary.e3 = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + } + } + } + + if ((vector_size(expr_pops, op_stack) == 0) && (has_level != -1)) { + vector_del(expr_pops, op_stack); + return e; + } + printf("Error: invalid expression: unexpected token\n"); + proc_token_print(tok); + proc_token_del(tok); +failed: + vector_del(expr_pops, op_stack); + if (e) expr_del(e); + return NULL; +} +static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constant_t *dest) { + // Evaluate the expression (we suppose it is constant) + switch (e->typ) { + case ETY_VAR: { + khiter_t it = kh_get(const_map, const_map, string_content(e->val.var)); + if (it != kh_end(const_map)) { + *dest = kh_val(const_map, it); + return 1; + } + printf("Error: failed to evaluate expression: expression is not constant (variable)\n"); + return 0; } + + case ETY_CONST: + *dest = e->val.cst; + return 1; + + // case ETY_GENERIC: + + case ETY_CALL: + printf("Error: failed to evaluate expression: expression is not constant (function call)\n"); + return 0; + + case ETY_ACCESS: + case ETY_PTRACCESS: + printf("Error: failed to evaluate expression: expression is not constant (member access)\n"); + return 0; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + case ETY_UNARY: + if (!eval_expression(e->val.unary.e, const_map, dest)) return 0; + + switch (e->val.unary.typ) { + case UOT_POSTINCR: + case UOT_POSTDECR: + case UOT_PREINCR: + case UOT_PREDECR: + case UOT_REF: + case UOT_DEREF: + printf("Error: failed to evaluate expression: expression is not constant (assignment or memory accesses)\n"); + return 0; + case UOT_POS: + return 1; // Nothing to do + case UOT_NEG: + switch (dest->typ) { + case NCT_FLOAT: dest->val.f = -dest->val.f; return 1; + case NCT_DOUBLE: dest->val.d = -dest->val.d; return 1; + case NCT_LDOUBLE: dest->val.l = -dest->val.l; return 1; + case NCT_INT32: dest->val.i32 = -dest->val.i32; return 1; + case NCT_UINT32: dest->val.u32 = -dest->val.u32; return 1; + case NCT_INT64: dest->val.i64 = -dest->val.i64; return 1; + case NCT_UINT64: dest->val.u64 = -dest->val.u64; return 1; + default: return 0; + } + case UOT_ANOT: + switch (dest->typ) { + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + printf("Error: failed to evaluate expression: cannot bitwise-negate a floating point number\n"); + return 0; + case NCT_INT32: dest->val.i32 = ~dest->val.i32; return 1; + case NCT_UINT32: dest->val.u32 = ~dest->val.u32; return 1; + case NCT_INT64: dest->val.i64 = ~dest->val.i64; return 1; + case NCT_UINT64: dest->val.u64 = ~dest->val.u64; return 1; + default: return 0; + } + case UOT_BNOT: + switch (dest->typ) { + case NCT_FLOAT: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.f; return 1; + case NCT_DOUBLE: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.d; return 1; + case NCT_LDOUBLE: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.l; return 1; + case NCT_INT32: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.i32; return 1; + case NCT_UINT32: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.u32; return 1; + case NCT_INT64: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.i64; return 1; + case NCT_UINT64: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.u64; return 1; + default: return 0; + } + default: return 0; + } + + case ETY_BINARY: { + num_constant_t dest1, dest2; + if (!eval_expression(e->val.binary.e1, const_map, &dest1)) return 0; + if (!eval_expression(e->val.binary.e2, const_map, &dest2)) return 0; + + switch (e->val.binary.typ) { + case BOT_ASSGN_EQ: + case BOT_ASSGN_ADD: + case BOT_ASSGN_SUB: + case BOT_ASSGN_MUL: + case BOT_ASSGN_DIV: + case BOT_ASSGN_MOD: + case BOT_ASSGN_LSH: + case BOT_ASSGN_RSH: + case BOT_ASSGN_AAND: + case BOT_ASSGN_AXOR: + case BOT_ASSGN_AOR: + case BOT_ARRAY: // Is this possible? + printf("Error: failed to evaluate expression: expression is not constant (assignments)\n"); + return 0; +#define DOIT(op) \ + promote_csts(&dest1, &dest2); \ + switch ((dest->typ = dest1.typ)) { \ + case NCT_FLOAT: dest->val.f = dest1.val.f op dest2.val.f ; return 1; \ + case NCT_DOUBLE: dest->val.d = dest1.val.d op dest2.val.d ; return 1; \ + case NCT_LDOUBLE: dest->val.l = dest1.val.l op dest2.val.l ; return 1; \ + case NCT_INT32: dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \ + case NCT_UINT32: dest->val.u32 = dest1.val.u32 op dest2.val.u32; return 1; \ + case NCT_INT64: dest->val.i64 = dest1.val.i64 op dest2.val.i64; return 1; \ + case NCT_UINT64: dest->val.u64 = dest1.val.u64 op dest2.val.u64; return 1; \ + default: return 0; \ + } +#define DOIT_INT(op) \ + promote_csts(&dest1, &dest2); \ + switch ((dest->typ = dest1.typ)) { \ + case NCT_FLOAT: \ + case NCT_DOUBLE: \ + case NCT_LDOUBLE: \ + printf("Error: failed to evaluate expression: binary operation %u incompatible with floating point numbers\n", e->val.binary.typ); \ + return 0; \ + case NCT_INT32: dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \ + case NCT_UINT32: dest->val.u32 = dest1.val.u32 op dest2.val.u32; return 1; \ + case NCT_INT64: dest->val.i64 = dest1.val.i64 op dest2.val.i64; return 1; \ + case NCT_UINT64: dest->val.u64 = dest1.val.u64 op dest2.val.u64; return 1; \ + default: return 0; \ + } +#define DOIT_BOOL(op) \ + promote_csts(&dest1, &dest2); \ + switch (dest1.typ) { \ + case NCT_FLOAT: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.f op dest2.val.f ; return 1; \ + case NCT_DOUBLE: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.d op dest2.val.d ; return 1; \ + case NCT_LDOUBLE: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.l op dest2.val.l ; return 1; \ + case NCT_INT32: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \ + case NCT_UINT32: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.u32 op dest2.val.u32; return 1; \ + case NCT_INT64: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.i64 op dest2.val.i64; return 1; \ + case NCT_UINT64: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.u64 op dest2.val.u64; return 1; \ + default: return 0; \ + } + case BOT_ADD: DOIT(+) + case BOT_SUB: DOIT(-) + case BOT_MUL: DOIT(*) + case BOT_DIV: DOIT(/) + case BOT_MOD: DOIT_INT(%) + case BOT_LSH: DOIT_INT(<<) + case BOT_RSH: DOIT_INT(>>) + case BOT_LT: DOIT_BOOL(<) + case BOT_GT: DOIT_BOOL(>) + case BOT_LE: DOIT_BOOL(<=) + case BOT_GE: DOIT_BOOL(>=) + case BOT_EQ: DOIT_BOOL(==) + case BOT_NE: DOIT_BOOL(!=) + case BOT_AAND: DOIT_INT(&) + case BOT_AXOR: DOIT_INT(^) + case BOT_AOR: DOIT_INT(|) + case BOT_BAND: DOIT(&&) + case BOT_BOR: DOIT(||) + case BOT_COMMA: *dest = dest2; return 1; +#undef DOIT_BOOL +#undef DOIT_INT +#undef DOIT + default: return 0; + } } + + case ETY_TERNARY: { + num_constant_t dest1, dest2, dest3; + if (!eval_expression(e->val.ternary.e1, const_map, &dest1)) return 0; + if (!eval_expression(e->val.ternary.e2, const_map, &dest2)) return 0; + if (!eval_expression(e->val.ternary.e3, const_map, &dest3)) return 0; + + switch (e->val.ternary.typ) { + case TOT_COND: + promote_csts(&dest2, &dest3); + switch (dest1.typ) { + case NCT_FLOAT: *dest = dest1.val.f ? dest2 : dest3; return 1; + case NCT_DOUBLE: *dest = dest1.val.d ? dest2 : dest3; return 1; + case NCT_LDOUBLE: *dest = dest1.val.l ? dest2 : dest3; return 1; + case NCT_INT32: *dest = dest1.val.i32 ? dest2 : dest3; return 1; + case NCT_UINT32: *dest = dest1.val.u32 ? dest2 : dest3; return 1; + case NCT_INT64: *dest = dest1.val.i64 ? dest2 : dest3; return 1; + case NCT_UINT64: *dest = dest1.val.u64 ? dest2 : dest3; return 1; + default: return 0; + } + default: return 0; + } } +#pragma GCC diagnostic pop + + // case ETY_INIT_LIST: + + case ETY_CAST: + if (!eval_expression(e->val.cast.e, const_map, dest)) return 0; + + if (e->val.cast.typ->typ == TYPE_BUILTIN) { + switch (e->val.cast.typ->val.builtin) { + case BTT_INT: + switch (dest->typ) { + case NCT_FLOAT: dest->val.i32 = (int32_t)dest->val.f; break; + case NCT_DOUBLE: dest->val.i32 = (int32_t)dest->val.d; break; + case NCT_LDOUBLE: dest->val.i32 = (int32_t)dest->val.l; break; + case NCT_INT32: break; + case NCT_UINT32: dest->val.i32 = (int32_t)dest->val.u32; break; + case NCT_INT64: dest->val.i32 = (int32_t)dest->val.i64; break; + case NCT_UINT64: dest->val.i32 = (int32_t)dest->val.u64; break; + } + dest->typ = NCT_INT32; + return 1; + case BTT_VOID: + case BTT_BOOL: + case BTT_CHAR: + case BTT_SCHAR: + case BTT_UCHAR: + case BTT_SHORT: + case BTT_SSHORT: + case BTT_USHORT: + case BTT_SINT: + case BTT_UINT: + case BTT_LONG: + case BTT_SLONG: + case BTT_ULONG: + case BTT_LONGLONG: + case BTT_SLONGLONG: + case BTT_ULONGLONG: + case BTT_INT128: + case BTT_SINT128: + case BTT_UINT128: + case BTT_S8: + case BTT_U8: + case BTT_S16: + case BTT_U16: + case BTT_S32: + case BTT_U32: + case BTT_S64: + case BTT_U64: + case BTT_FLOAT: + case BTT_CFLOAT: + case BTT_IFLOAT: + case BTT_DOUBLE: + case BTT_CDOUBLE: + case BTT_IDOUBLE: + case BTT_LONGDOUBLE: + case BTT_CLONGDOUBLE: + case BTT_ILONGDOUBLE: + case BTT_VA_LIST: + default: + printf("Error: TODO: cast to builtin in constant expression\n"); + return 0; + } + } else { + printf("Error: cast in constant expression\n"); + return 0; + } + + default: return 0; + } +} + +// declaration-specifier with storage != NULL +// specifier-qualifier-list + static_assert-declaration with storage == NULL +static int parse_declaration_specifier(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum decl_spec *spec, type_t *typ) { + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_STATIC_ASSERT)) { + // _Static_assert ( constant-expression , string-literal ) ; + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) { + printf("Error: unexpected token in static assertion declaration\n"); + proc_token_del(tok); + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + expr_t *e = parse_expression(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, 14); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_COMMA)) { + printf("Error: unexpected token in static assertion expression\n"); + expr_del(e); + proc_token_del(tok); + goto failed; + } + num_constant_t eval; + if (!eval_expression(e, const_map, &eval)) { + expr_del(e); + // Empty destructor + goto failed; + } + expr_del(e); + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_STRING) || !tok->tokv.sisstr) { + printf("Error: unexpected token in static assertion message\n"); + proc_token_del(tok); + goto failed; + } + string_t *errmsg = tok->tokv.str; + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) { + printf("Error: unexpected token in static assertion message\n"); + proc_token_del(tok); + goto failed; + } + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_SEMICOLON)) { + printf("Error: unexpected token after static assertion\n"); + proc_token_del(tok); + goto failed; + } + + int iserror; + switch (eval.typ) { + case NCT_FLOAT: iserror = (int)eval.val.f == 0; break; + case NCT_DOUBLE: iserror = (int)eval.val.d == 0; break; + case NCT_LDOUBLE: iserror = (int)eval.val.l == 0; break; + case NCT_INT32: iserror = eval.val.i32 == 0; break; + case NCT_UINT32: iserror = eval.val.u32 == 0; break; + case NCT_INT64: iserror = eval.val.i64 == 0; break; + case NCT_UINT64: iserror = eval.val.u64 == 0; break; + default: iserror = 1; + } + if (iserror) { + printf("Error: static assertion failed: %s\n", string_content(errmsg)); + string_del(errmsg); + // Empty destructor + goto failed; + } + string_del(errmsg); + // Empty destructor + return 1; + } + +parse_cur_token_decl: + if (tok->tokt == PTOK_EOF) { + printf("Error: unexpected end of file in declaration\n"); + proc_token_print(tok); + goto failed; + } + // Storage + if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_AUTO)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_AUTO; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_EXTERN)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_EXTERN; + else if (*storage == STORAGE_TLS) *storage = STORAGE_TLS_EXTERN; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_REGISTER)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_REG; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_STATIC)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_STATIC; + else if (*storage == STORAGE_TLS) *storage = STORAGE_TLS_STATIC; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_THREAD_LOCAL)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_TLS; + else if (*storage == STORAGE_EXTERN) *storage = STORAGE_TLS_EXTERN; + else if (*storage == STORAGE_STATIC) *storage = STORAGE_TLS_STATIC; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_TYPEDEF)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_TYPEDEF; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + + // Qualifier + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) { + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + printf("Error: TODO: _Atomic(type-name)\n"); + // Empty destructor + goto failed; + } else { + typ->is_atomic = 1; + goto parse_cur_token_decl; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CONST)) { + typ->is_const = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_RESTRICT)) { + typ->is_restrict = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOLATILE)) { + typ->is_volatile = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + + // Specifier +#define SPEC(bt,post) \ + if (*spec == SPEC_NONE) { \ + *spec = SPEC_BUILTIN; \ + typ->typ = TYPE_BUILTIN; \ + typ->val.builtin = BTT_ ## bt; \ + post \ + } else { \ + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); \ + goto failed; \ + } \ + *tok = proc_next_token(prep); \ + goto parse_cur_token_decl; +#define SPEC_SIGNED(bt, allow_int) \ + if ((*spec == SPEC_NONE) || (allow_int && (*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT))) { \ + *spec = (allow_int && (*spec == SPEC_NONE)) ? SPEC_BUILTIN_NOINT : SPEC_BUILTIN; \ + typ->typ = TYPE_BUILTIN; \ + typ->val.builtin = BTT_ ## bt; \ + } else if (((*spec == SPEC_BUILTIN_NOINT) || (allow_int && (*spec == SPEC_BUILTIN))) && (typ->val.builtin == BTT_SINT)) { \ + *spec = allow_int ? *spec : SPEC_BUILTIN; \ + typ->val.builtin = BTT_S ## bt; \ + } else if (((*spec == SPEC_BUILTIN_NOINT) || (allow_int && (*spec == SPEC_BUILTIN))) && (typ->val.builtin == BTT_UINT)) { \ + *spec = allow_int ? *spec : SPEC_BUILTIN; \ + typ->val.builtin = BTT_U ## bt; \ + } else { \ + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); \ + goto failed; \ + } \ + *tok = proc_next_token(prep); \ + goto parse_cur_token_decl; + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_BOOL)) { + SPEC(BOOL,) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_COMPLEX)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_COMPLEX; + } else if ((*spec == SPEC_BUILTIN_NOINT) && (typ->val.builtin == BTT_LONG)) { + *spec = SPEC_LONGCOMPLEX; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_FLOAT)) { + typ->val.builtin = BTT_CFLOAT; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_DOUBLE)) { + typ->val.builtin = BTT_CDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_LONGDOUBLE)) { + typ->val.builtin = BTT_CLONGDOUBLE; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_IMAGINARY)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_IMAGINARY; + } else if ((*spec == SPEC_BUILTIN_NOINT) && (typ->val.builtin == BTT_LONG)) { + *spec = SPEC_LONGIMAGINARY; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_FLOAT)) { + typ->val.builtin = BTT_IFLOAT; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_DOUBLE)) { + typ->val.builtin = BTT_IDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_LONGDOUBLE)) { + typ->val.builtin = BTT_ILONGDOUBLE; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_DOUBLE)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_DOUBLE; + } else if (*spec == SPEC_COMPLEX) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_CDOUBLE; + } else if (*spec == SPEC_IMAGINARY) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_IDOUBLE; + } else if (*spec == SPEC_LONGCOMPLEX) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_CLONGDOUBLE; + } else if (*spec == SPEC_LONGIMAGINARY) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_ILONGDOUBLE; + } else if ((*spec == SPEC_BUILTIN_NOINT) && (typ->val.builtin == BTT_LONG)) { + *spec = SPEC_BUILTIN; + typ->val.builtin = BTT_LONGDOUBLE; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_FLOAT)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_FLOAT; + } else if (*spec == SPEC_COMPLEX) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_CFLOAT; + } else if (*spec == SPEC_IMAGINARY) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_IFLOAT; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CHAR)) { + SPEC_SIGNED(CHAR, 0) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_INT)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_INT; + } else if (*spec == SPEC_BUILTIN_NOINT) { + *spec = SPEC_BUILTIN; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_INT128)) { + SPEC_SIGNED(INT128, 0) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_LONG)) { + if ((*spec == SPEC_NONE) || ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT))) { + *spec = (*spec == SPEC_NONE) ? SPEC_BUILTIN_NOINT : SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_LONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SINT)) { + typ->val.builtin = BTT_SLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_UINT)) { + typ->val.builtin = BTT_ULONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONG)) { + typ->val.builtin = BTT_LONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_ULONG)) { + typ->val.builtin = BTT_ULONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SLONG)) { + typ->val.builtin = BTT_SLONGLONG; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_DOUBLE)) { + typ->val.builtin = BTT_LONGDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_CDOUBLE)) { + typ->val.builtin = BTT_CLONGDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_IDOUBLE)) { + typ->val.builtin = BTT_ILONGDOUBLE; + } else if (*spec == SPEC_COMPLEX) { + *spec = SPEC_LONGCOMPLEX; + } else if (*spec == SPEC_IMAGINARY) { + *spec = SPEC_LONGIMAGINARY; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_SHORT)) { + SPEC_SIGNED(SHORT, 1) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_SIGNED)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN_NOINT; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_SINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_CHAR)) { + typ->val.builtin = BTT_SCHAR; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT)) { + typ->val.builtin = BTT_SINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONG)) { + typ->val.builtin = BTT_SLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONGLONG)) { + typ->val.builtin = BTT_SLONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SHORT)) { + typ->val.builtin = BTT_SSHORT; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_UNSIGNED)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN_NOINT; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_UINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_CHAR)) { + typ->val.builtin = BTT_UCHAR; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT)) { + typ->val.builtin = BTT_UINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONG)) { + typ->val.builtin = BTT_ULONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONGLONG)) { + typ->val.builtin = BTT_ULONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SHORT)) { + typ->val.builtin = BTT_USHORT; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOID)) { + SPEC(VOID, typ->is_incomplete = 1;) + } +#undef SPEC +#undef SPEC_SIGNED + if ((tok->tokt == PTOK_IDENT) && (*spec == SPEC_NONE)) { + // The ident is the type-specifier + khiter_t it = kh_get(type_map, type_map, string_content(tok->tokv.str)); + if (it == kh_end(type_map)) { + printf("Error: invalid type '%s' (ident is not a typedef)\n" + "Current state:\n" + " storage: %p/%u\n" + " spec: %u\n" + " type: ", string_content(tok->tokv.str), storage, storage ? *storage : STORAGE_NONE, *spec); + type_print(typ); + printf("\n"); + string_del(tok->tokv.str); + goto failed; + } else { + *spec = SPEC_TYPE; + if (!type_copy_into(typ, kh_val(type_map, it))) { + printf("Failed to duplicate type infos\n"); + string_del(tok->tokv.str); + goto failed; + } + string_del(tok->tokv.str); + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + } + if ((tok->tokt == PTOK_KEYWORD) && (*spec == SPEC_NONE) && ((tok->tokv.kw == KW_STRUCT) || (tok->tokv.kw == KW_UNION))) { + int is_struct = tok->tokv.kw == KW_STRUCT; + *spec = SPEC_TYPE; + + // Empty destructor + *tok = proc_next_token(prep); + khiter_t it = kh_end(struct_map); // Iterator into the struct_map + if (tok->tokt == PTOK_IDENT) { + string_t *tag = tok->tokv.str; + // Token moved + *tok = proc_next_token(prep); + + int iret; + it = kh_put(struct_map, struct_map, string_content(tag), &iret); + if (iret < 0) { + printf("Error: failed to add structure to struct map\n"); + proc_token_del(tok); + goto failed; + } else if (iret == 0) { + // Structure already declared or defined + if (kh_val(struct_map, it)->is_struct != is_struct) { + printf("Error: incoherent struct/union tagging of %s\n", string_content(tag)); + string_del(tag); + proc_token_del(tok); + goto failed; + } + ++kh_val(struct_map, it)->nrefs; + string_del(tag); + } else { + kh_val(struct_map, it) = struct_new(is_struct, tag); + if (!kh_val(struct_map, it)) { + printf("Error: failed to create new structure metadata structure\n"); + string_del(tag); + proc_token_del(tok); + goto failed; + } + } + typ->typ = TYPE_STRUCT_UNION; + typ->val.st = kh_val(struct_map, it); + typ->is_incomplete = !typ->val.st->is_defined; + } else { + typ->typ = TYPE_STRUCT_UNION; + typ->val.st = struct_new(is_struct, NULL); + if (!typ->val.st) { + printf("Error: failed to create new structure metadata structure\n"); + proc_token_del(tok); + goto failed; + } + typ->is_incomplete = 1; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) { + if (typ->val.st->is_defined) { + printf("Error: TODO: struct redefinition\n" + "Current state:\n" + " storage: %p/%u\n" + " spec: %u\n" + " type: ", storage, storage ? *storage : STORAGE_NONE, *spec); + type_print(typ); + printf("\n"); + goto failed; + } + + VECTOR(st_members) *members = vector_new(st_members); + if (!members) { + printf("Failed to create a members vector\n"); + goto failed; + } + type_t *typ2 = type_new(); + if (!typ2) { + printf("Failed to create a type info structure\n"); + vector_del(st_members, members); + goto failed; + } + *tok = proc_next_token(prep); + while (!proc_token_isend(tok) && ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET))) { + enum decl_spec spec2 = SPEC_NONE; + if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, &spec2, typ2)) { + vector_del(st_members, members); + type_del(typ2); + goto failed; + } + if (spec2 == SPEC_NONE) { + // Empty destructor + *tok = proc_next_token(prep); + continue; // Declaration was an assert, typ2 is unchanged + } + typ2 = type_try_merge(typ2, type_set); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_SEMICOLON)) { + // A struct-declaration that does not declare an anonymous structure or anonymous union + // shall contain a struct-declarator-list. + if ((typ2->typ != TYPE_STRUCT_UNION) || typ2->val.st->tag) { + printf("Error: missing struct-declarator-list\n"); + vector_del(st_members, members); + type_del(typ2); + goto failed; + } + if (!vector_push(st_members, members, ((st_member_t){.name = NULL, .typ = typ2, .is_bitfield = 0}))) { + printf("Error: failed to add anonymous structure member\n"); + vector_del(st_members, members); + type_del(typ2); + // Empty destructor + goto failed; + } + typ2 = type_new(); + if (!typ2) { + printf("Failed to create a type info structure\n"); + vector_del(st_members, members); + // Empty destructor + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + continue; + } + + struct parse_declarator_dest_s dest2; + dest2.structms.struct_map = struct_map; + dest2.structms.type_map = type_map; + dest2.structms.enum_map = enum_map; + dest2.structms.type_set = type_set; + dest2.structms.builtins = builtins; + dest2.structms.const_map = const_map; + dest2.structms.dest = members; + if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, typ2, 0, 1, 1, 1)) { + printf("Error parsing struct-declarator-list\n"); + vector_del(st_members, members); + type_del(typ2); + // Token is deleted + goto failed; + } + type_del(typ2); + if ((tok->tokt != PTOK_SYM) && (tok->tokv.sym != SYM_SEMICOLON)) { + printf("Error parsing struct-declarator-list (invalid next token)\n"); + vector_del(st_members, members); + proc_token_del(tok); + goto failed; + } + typ2 = type_new(); + if (!typ2) { + printf("Failed to create a type info structure\n"); + vector_del(st_members, members); + // Empty destructor + goto failed; + } + *tok = proc_next_token(prep); + } + type_del(typ2); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET)) { + printf("Error parsing struct-declarator-list (invalid next token)\n"); + vector_del(st_members, members); + proc_token_del(tok); + goto failed; + } + + typ->is_incomplete = 0; + typ->val.st->has_incomplete = 0; // Filled by the validate_type step + typ->val.st->nmembers = vector_size(st_members, members); + typ->val.st->members = vector_steal(st_members, members); + typ->val.st->is_defined = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else { + if (it == kh_end(struct_map)) { + printf("Error: invalid structure declaration: missing tag and/or definition\n"); + proc_token_del(tok); + goto failed; + } + goto parse_cur_token_decl; + } + } + if ((tok->tokt == PTOK_KEYWORD) && (*spec == SPEC_NONE) && (tok->tokv.kw == KW_ENUM)) { + *spec = SPEC_TYPE; + + // Empty destructor + *tok = proc_next_token(prep); + string_t *tag = NULL; + if (tok->tokt == PTOK_IDENT) { + tag = tok->tokv.str; + *tok = proc_next_token(prep); + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LBRACKET)) { + if (!tag) { + printf("Error: unexpected token after keyword 'enum'\n"); + proc_token_del(tok); + goto failed; + } + khiter_t it = kh_get(type_map, enum_map, string_content(tag)); + if (it == kh_end(enum_map)) { + printf("Error: enumeration %s has not been defined yet\n", string_content(tag)); // TODO? + string_del(tag); + proc_token_del(tok); + goto failed; + } + if (!type_copy_into(typ, kh_val(enum_map, it))) { + printf("Failed to duplicate enum type infos\n"); + string_del(tag); + proc_token_del(tok); + goto failed; + } + string_del(tag); + goto parse_cur_token_decl; + } + // We are defining the enum + // Try in order: + // If all values are 0 <= . <= UINT32_MAX, BTT_U32 + // If any value is negative and all values are <= INT32_MAX, BTT_S32 + // If any value is negative and all values are <= INT64_MAX, BTT_S64 + // If all values are 0 <= . <= UINT64_MAX, BTT_U64 + // Otherwise, error + // By default, BTT_U32* and the constant is an NCT_INT32 + // *The AMD ABI says this should rather be BTT_INT (or BTT_S32) + // Note that BTT_S32 only when has_neg is true + int has_neg = 0, not_in_i32 = 0; + enum type_builtin_e btt = BTT_U32; + num_constant_t cst = { .typ = NCT_INT32, .val.i32 = -1 }; + // Empty destructor + *tok = proc_next_token(prep); + int iret; + while ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET)) { + if (tok->tokt != PTOK_IDENT) { + printf("Error: invalid token in enumeration definition\n"); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + char *ident = string_steal(tok->tokv.str); + *tok = proc_next_token(prep); + khiter_t it = kh_put(const_map, const_map, ident, &iret); + if (iret < 0) { + printf("Error: failed to add constant %s to the constants map\n", ident); + free(ident); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } else if (iret == 0) { + printf("Error: constant %s is already in the constants map\n", ident); + free(ident); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + if ((tok->tokt == PTOK_SYM) && ((tok->tokv.sym == SYM_COMMA) || tok->tokv.sym == SYM_RBRACKET)) { + switch (cst.typ) { + case NCT_INT32: + if (cst.val.i32 == INT32_MAX) { + cst.typ = NCT_UINT32; + cst.val.u32 = (uint32_t)INT32_MAX + 1; + } else ++cst.val.i32; + break; + case NCT_UINT32: + if (cst.val.u32 == UINT32_MAX) { + cst.typ = NCT_INT64; + cst.val.i64 = (int64_t)UINT32_MAX + 1; + } else ++cst.val.u32; + break; + case NCT_INT64: + if (cst.val.i64 == INT64_MAX) { + cst.typ = NCT_UINT64; + cst.val.u64 = (uint64_t)INT64_MAX + 1; + } else ++cst.val.i64; + break; + case NCT_UINT64: + if (cst.val.u64 == UINT64_MAX) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } else ++cst.val.u64; + break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Internal error: enum constant is a float/double/ldouble\n"); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_EQ)) { + // Empty destructor + *tok = proc_next_token(prep); + expr_t *e = parse_expression(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, 14); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_RBRACKET))) { + printf("Error: unexpected token during enumeration declaration\n"); + expr_del(e); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + if (!eval_expression(e, const_map, &cst)) { + expr_del(e); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + expr_del(e); + } + switch (cst.typ) { + case NCT_INT32: + if (cst.val.i32 < 0) { + has_neg = 1; + if (btt == BTT_U64) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + btt = not_in_i32 ? BTT_S64 : BTT_S32; + } + break; + case NCT_UINT32: + if (cst.val.u32 > (uint32_t)INT32_MAX) { + not_in_i32 = 1; + btt = has_neg ? BTT_S64 : BTT_U32; + } + break; + case NCT_INT64: + if (cst.val.i64 < 0) { + has_neg = 1; + if (cst.val.i64 < (int64_t)INT32_MIN) { + not_in_i32 = 1; + } + if (btt == BTT_U64) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + btt = + not_in_i32 ? BTT_S64 : + BTT_S32; + } + if ((cst.val.i64 > (int64_t)INT32_MAX)) { + not_in_i32 = 1; + if (has_neg) btt = BTT_S64; + else if (((btt == BTT_S32) || (btt == BTT_U32)) && (cst.val.i64 <= (int64_t)UINT32_MAX)) + btt = BTT_U32; + else if (btt != BTT_U64) btt = BTT_S64; + } + break; + case NCT_UINT64: + if (cst.val.u64 > (uint32_t)INT32_MAX) { + not_in_i32 = 1; + if (has_neg && (cst.val.u64 > (uint64_t)INT64_MAX)) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + btt = (cst.val.u64 > (uint64_t)INT64_MAX) ? BTT_U64 : (has_neg || (cst.val.u64 > (uint64_t)UINT32_MAX)) ? BTT_S64 : BTT_U32; + } + break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Error: invalid floating-point enumeration constant\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + kh_val(const_map, it) = cst; + if (tok->tokv.sym == SYM_COMMA) { + *tok = proc_next_token(prep); + } + } + if (tag) { + char *ctag = string_steal(tag); + khiter_t it = kh_put(type_map, enum_map, ctag, &iret); + if (iret < 0) { + printf("Error: failed to add enumeration %s to the type map\n", ctag); + free(ctag); + // Empty destructor + goto failed; + } else if (iret == 0) { + printf("Error: enumeration %s already exists\n", ctag); + free(ctag); + // Empty destructor + goto failed; + } + type_t *new_typ = type_new(); + if (!new_typ) { + printf("Error: failed to create type info for enumeration %s\n", ctag); + free(ctag); + kh_del(type_map, enum_map, it); + // Empty destructor + goto failed; + } + typ->typ = new_typ->typ = TYPE_ENUM; + typ->is_incomplete = new_typ->is_incomplete = 0; + typ->val.typ = new_typ->val.typ = (*builtins)[btt]; + typ->val.typ->nrefs += 2; + new_typ = type_try_merge(new_typ, type_set); + validate_type(new_typ, builtins); // Assume it returns 1 + kh_val(enum_map, it) = new_typ; + } else { + typ->typ = TYPE_ENUM; + typ->is_incomplete = 0; + typ->val.typ = (*builtins)[btt]; + ++typ->val.typ->nrefs; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + + if ((*spec != SPEC_BUILTIN) && (*spec != SPEC_BUILTIN_NOINT) && (*spec != SPEC_TYPE)) goto invalid_token; + if ((tok->tokt != PTOK_IDENT) && ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON) + && (tok->tokv.sym != SYM_STAR) && (tok->tokv.sym != SYM_LSQBRACKET) + && (tok->tokv.sym != SYM_LPAREN) && (tok->tokv.sym != SYM_RPAREN) + && (tok->tokv.sym != SYM_COLON)))) + goto invalid_token; + + return 1; + +invalid_token: + printf("Error: unexpected token (parse_declaration_specifier)\n" + "Current state:\n" + " storage: %p/%u\n" + " spec: %u\n" + " type: ", storage, storage ? *storage : STORAGE_NONE, *spec); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + +failed: + return 0; +} + +static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, type_t *base_type, + int is_init, int is_list, int allow_decl, int allow_abstract) { + int has_list = 0, has_ident = 0; + // TODO: allow_abstract and 'direct-abstract-declarator(opt) ( parameter-type-list(opt) )' + + string_t *cur_ident = NULL; + type_t *typ = base_type; ++typ->nrefs; + type_t *cur_bottom = NULL; + VECTOR(size_t) *nptr_stack = vector_new_cap(size_t, 1); + if (!nptr_stack) { + printf("Failed to parse init_declarator_list (allocate nptr_stack)\n"); + proc_token_del(tok); + goto failed0; + } + vector_push(size_t, nptr_stack, 0); // Always succeed (size < cap) + while (1) { + switch (tok->tokt) { + case PTOK_IDENT: + if (!has_ident) { + if (allow_decl) { + cur_ident = tok->tokv.str; + has_ident = 1; + *tok = proc_next_token(prep); + break; + } else { + printf("Error: unexpected identifier: abstract declarator do not contain a name\n" + "Current state:\n" + " storage: %u\n" + " type: ", storage); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + goto failed; + } + } else { + printf("Error: unexpected identifier\n" + "Current state:\n" + " storage: %u\n" + " type: ", storage); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + goto failed; + } + case PTOK_SYM: + if (tok->tokv.sym == SYM_LPAREN) { + if (has_ident) { + type_t *new_typ; + + VECTOR(types) *args = vector_new(types); + if (!args) { + printf("Error: failed to create new type (function argument)\n"); + // Empty destructor + goto failed; + } + int has_varargs = 0; + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOID)) { + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + vector_del(types, args); + new_typ = type_new(); + if (!new_typ) { + printf("Error: failed to create new function type\n"); + // Empty destructor + goto failed; + } + new_typ->typ = TYPE_FUNCTION; + new_typ->val.fun.has_varargs = 0; + new_typ->val.fun.nargs = 0; + new_typ->val.fun.args = NULL; + // ret will be set later + goto end_fun; + } + if (!proc_unget_token(prep, tok)) { + printf("Error: failed to unget processor token\n"); + // Empty destructor + vector_del(types, args); + goto failed; + } + tok->tokt = PTOK_KEYWORD; + tok->tokv.kw = KW_VOID; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + goto no_arg; + } + while (1) { + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_VARIADIC)) { + has_varargs = 1; + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) { + printf("Error: invalid token after function variadic argument\n"); + proc_token_del(tok); + vector_del(types, args); + goto failed; + } + break; + } else { + type_t *typ2 = type_new(); + if (!typ2) { + printf("Error: failed to create new type (function argument)\n"); + // Empty destructor + vector_del(types, args); + goto failed; + } + enum decl_storage storage2 = STORAGE_NONE; + enum decl_spec spec2 = SPEC_NONE; + if (!parse_declaration_specifier(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS, + PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, &storage2, &spec2, typ2)) { + // Token is deleted + vector_del(types, args); + type_del(typ2); + goto failed; + } + if (spec2 == SPEC_NONE) { + // _Static_assert declaration; empty destructor + printf("Invalid _Static_assert declaration\n"); + vector_del(types, args); + type_del(typ2); + goto failed; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + // Unnamed argument + if (!vector_push(types, args, typ2)) { + printf("Error: failed to add argument to argument vector\n"); + vector_del(types, args); + type_del(typ2); + // Empty destructor + goto failed; + } + break; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) { + // Unnamed argument + if (!vector_push(types, args, typ2)) { + printf("Error: failed to add argument to argument vector\n"); + vector_del(types, args); + type_del(typ2); + // Empty destructor + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + continue; + } + // FIXME: Storage specifiers are ignored most of the time? + struct parse_declarator_dest_s dest2; + dest2.argt.dest = NULL; + dest2.argt.struct_map = PDECL_STRUCT_MAP; + dest2.argt.type_map = PDECL_TYPE_MAP; + dest2.argt.enum_map = PDECL_ENUM_MAP; + dest2.argt.type_set = PDECL_TYPE_SET; + dest2.argt.builtins = PDECL_BUILTINS; + dest2.argt.const_map = PDECL_CONST_MAP; + if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, typ2, 0, 0, 1, 1)) { + // Token is deleted + vector_del(types, args); + type_del(typ2); + goto failed; + } + type_del(typ2); + if (!dest2.argt.dest) { + printf("Internal error: argument type is NULL\n"); + vector_del(types, args); + // Empty destructor + goto failed; + } + if (!vector_push(types, args, dest2.argt.dest)) { + printf("Error: failed to add argument to argument vector\n"); + vector_del(types, args); + type_del(dest2.argt.dest); + // Empty destructor + goto failed; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + break; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) { + // Empty destructor + *tok = proc_next_token(prep); + continue; + } + printf("Error: invalid token after function argument\n"); + vector_del(types, args); + proc_token_del(tok); + goto failed; + } + } + no_arg: + new_typ = type_new(); + if (!new_typ) { + printf("Error: failed to create new function type\n"); + // Empty destructor + goto failed; + } + new_typ->typ = TYPE_FUNCTION; + new_typ->val.fun.has_varargs = has_varargs; + new_typ->val.fun.nargs = vector_size(types, args) ? vector_size(types, args) : (size_t)-1; + new_typ->val.fun.args = vector_steal(types, args); + + end_fun: // (void) + if (cur_bottom) { + // cur_bottom is a pointer, an array or a function + // We have cur_bottom(old) --> below, we want cur_bottom(old) --> array=cur_bottom(new) --> below + new_typ->val.fun.ret = + (cur_bottom->typ == TYPE_PTR) ? cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? cur_bottom->val.array.typ : + cur_bottom->val.fun.ret; + *((cur_bottom->typ == TYPE_PTR) ? &cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? &cur_bottom->val.array.typ : &cur_bottom->val.fun.ret) = new_typ; + cur_bottom = new_typ; + } else { + // We have top(old), we want array=top(new)=cur_bottom(new) --> top(old) + new_typ->val.fun.ret = typ; + cur_bottom = typ = new_typ; + } + *tok = proc_next_token(prep); + break; + } else { + if (!vector_push(size_t, nptr_stack, 0)) { + printf("Failed to parse init_declarator_list (open parenthesis)\n"); + // Empty destructor + goto failed; + } + *tok = proc_next_token(prep); + break; + } + } else if (tok->tokv.sym == SYM_STAR) { + if (has_ident) { + printf("Error: invalid token '*' after identifier in declaration\n"); + // Empty destructor + goto failed; + } else { + type_t *new_typ = type_new_ptr(typ); + if (!new_typ) { + printf("Failed to parse init_declarator_list (create new pointer typ)\n"); + // Empty destructor + goto failed; + } + typ = new_typ; + ++vector_last(size_t, nptr_stack); + *tok = proc_next_token(prep); + break; + } + } else if (tok->tokv.sym == SYM_LSQBRACKET) { + if (!has_ident) { + if (!allow_abstract) { + printf("Error: invalid token '[' before identifier in declaration\n"); + // Empty destructor + goto failed; + } + has_ident = 1; + } + // Empty destructor + *tok = proc_next_token(prep); + // Here we have only two array constructors: + // direct-declaration [ assignment-expression(opt) ] + // direct-declaration [ * ] (complete VLA) + size_t nelems; _Bool is_incomplete; + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RSQBRACKET)) { + // Incomplete VLA + nelems = (size_t)-1; + is_incomplete = 1; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_STAR)) { + // Complete VLA, expecting a ']' + nelems = (size_t)-1; + is_incomplete = 0; + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RSQBRACKET)) { + printf("Error: unexpected token during variable length array declaration\n"); + proc_token_del(tok); + goto failed; + } + } else { + // Constant expression, followed by ']' + is_incomplete = 0; + expr_t *e = parse_expression(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS, PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, 15); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RSQBRACKET)) { + printf("Error: unexpected token during array declaration\n"); + expr_del(e); + proc_token_del(tok); + goto failed; + } + num_constant_t cst; + if (!eval_expression(e, PDECL_CONST_MAP, &cst)) { + expr_del(e); + // Empty destructor + goto failed; + } + expr_del(e); + int is_neg; + switch (cst.typ) { + case NCT_FLOAT: is_neg = cst.val.f < 0; nelems = (size_t)cst.val.f; break; + case NCT_DOUBLE: is_neg = cst.val.d < 0; nelems = (size_t)cst.val.d; break; + case NCT_LDOUBLE: is_neg = cst.val.l < 0; nelems = (size_t)cst.val.l; break; + case NCT_INT32: is_neg = cst.val.i32 < 0; nelems = (size_t)cst.val.i32; break; + case NCT_UINT32: is_neg = 0; nelems = (size_t)cst.val.u32; break; + case NCT_INT64: is_neg = cst.val.i64 < 0; nelems = (size_t)cst.val.i64; break; + case NCT_UINT64: is_neg = 0; nelems = (size_t)cst.val.u64; break; + default: is_neg = 1; + } + if (is_neg) { + printf("Error: the size of an array must be nonnegative"); + // Empty destructor + goto failed; + } + } + // Token is ']' + + if (cur_bottom) { + type_t *tmp = type_new(); + if (!tmp) { + printf("Failed to parse init_declarator_list (create new array type)\n"); + // Empty destructor + goto failed; + } + tmp->typ = TYPE_ARRAY; + tmp->val.array.array_sz = nelems; + tmp->is_incomplete = is_incomplete; + // cur_bottom is a pointer, an array or a function + // We have cur_bottom(old) --> below, we want cur_bottom(old) --> array=cur_bottom(new) --> below + tmp->val.array.typ = + (cur_bottom->typ == TYPE_PTR) ? cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? cur_bottom->val.array.typ : + cur_bottom->val.fun.ret; + *((cur_bottom->typ == TYPE_PTR) ? &cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? &cur_bottom->val.array.typ : &cur_bottom->val.fun.ret) = tmp; + cur_bottom = tmp; + } else { + type_t *new_typ = type_new(); + if (!new_typ) { + printf("Failed to parse init_declarator_list (create new array type)\n"); + // Empty destructor + goto failed; + } + new_typ->typ = TYPE_ARRAY; + new_typ->val.array.array_sz = nelems; + new_typ->is_incomplete = is_incomplete; + // We have top(old), we want array=top(new)=cur_bottom(new) --> top(old) + new_typ->val.array.typ = typ; + cur_bottom = typ = new_typ; + } + *tok = proc_next_token(prep); + break; + } else if (tok->tokv.sym == SYM_RPAREN) { + if (!has_ident) { + if (!allow_abstract) { + printf("Error: invalid token ')' before identifier in declaration\n"); + // Empty destructor + goto failed; + } + has_ident = 1; + } + if (vector_size(size_t, nptr_stack) == 1) { + if (!is_init && !is_list) goto rparen_ok_ret; + printf("Error: closing unopened parenthesis in declaration\n"); + // Empty destructor + goto failed; + } + size_t ndecr; + if (cur_bottom) { + ndecr = vector_last(size_t, nptr_stack); + } else { + if (vector_last(size_t, nptr_stack)) { + cur_bottom = typ; + ndecr = vector_last(size_t, nptr_stack) - 1; + } else ndecr = 0; + } + for (size_t i = 0; i < ndecr; ++i) { + cur_bottom = + (cur_bottom->typ == TYPE_PTR) ? cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? cur_bottom->val.array.typ : + cur_bottom->val.fun.ret; + } + vector_pop(size_t, nptr_stack); + *tok = proc_next_token(prep); + break; + } else if ((is_init && (tok->tokv.sym == SYM_EQ)) + || (tok->tokv.sym == SYM_COMMA) + || (tok->tokv.sym == SYM_SEMICOLON) + || (is_init && (tok->tokv.sym == SYM_LBRACKET))) { + rparen_ok_ret: // Last function argument + if (!allow_abstract && !has_ident) { + printf("Error: invalid symbol '%s' before identifier\n", sym2str[tok->tokv.sym]); + // Empty destructor + goto failed; + } + if (vector_size(size_t, nptr_stack) != 1) { + printf("Error: invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]); + // Empty destructor + goto failed; + } + + // Try to free some redundant types + typ = type_try_merge(typ, PDECL_TYPE_SET); + + int validation = validate_storage_type(storage, PDECL_BUILTINS, typ, tok->tokv.sym); + if (!validation) { + // Empty destructor + goto failed; + } + + if (validation == VALIDATION_FUN) { + // Function definition; tok is '{' + if (!is_init || !is_list || has_list) { + // We are not at the top-level or we have an initialization list + printf("Error: invalid function definition\n"); + // Empty destructor + goto failed; + } + // Note that here, dest is a file_t + // No argument in function definition means that the function takes no argument, + // whereas no argument in function declaration means the function takes an unspecified number of arguments + if (typ->val.fun.nargs == (size_t)-1) typ->val.fun.nargs = 0; + + int iret; + char *cident = string_steal(cur_ident); cur_ident = NULL; + khiter_t it = kh_put(type_map, dest->f->decl_map, cident, &iret); + if (iret < 0) { + printf("Failed to add function '%s' to the declaration map\n", cident); + free(cident); + // Empty destructor + goto failed; + } else if (iret == 0) { + printf("Error: function '%s' is already in the declaration map\n", cident); + free(cident); + // Empty destructor + goto failed; + } + + kh_val(dest->f->decl_map, it) = typ; + + // Skip the function body + int nlbraces = 0; + do { + proc_token_del(tok); + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) ++nlbraces; + else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RBRACKET)) { + if (nlbraces) --nlbraces; + else goto success; + } + } while (!proc_token_isend(tok)); + printf("Error: unexpected token in function body\n"); + goto failed; + } + + if (storage == STORAGE_TYPEDEF) { + if (!is_init || !is_list) { + // We are not at the top-level (note that storage is set to NONE in function arguments) + printf("Error: invalid function definition\n"); + // Empty destructor + goto failed; + } + // Note that here, dest is a file_t + int iret; + char *cident = string_steal(cur_ident); cur_ident = NULL; + khiter_t it = kh_put(type_map, dest->f->type_map, cident, &iret); + if (iret < 0) { + printf("Failed to add '%s' to the type map\n", cident); + free(cident); + // Empty destructor + goto failed; + } else if (iret == 0) { + if (!type_t_equal(typ, kh_val(dest->f->type_map, it))) { + printf("Error: '%s' is already in the type map with a different type\n", cident); + free(cident); + type_del(typ); + // Empty destructor + goto failed; + } + // We can safely ignore this since we have typedef-ed the same type + free(cident); + type_del(typ); + } else { + kh_val(dest->f->type_map, it) = typ; + } + } else if ((storage != STORAGE_STATIC) && (storage != STORAGE_TLS_STATIC)) { + if (is_init && is_list) { + // static variables/functions are not exposed + int iret; + char *cident = string_steal(cur_ident); cur_ident = NULL; + khiter_t it = kh_put(type_map, dest->f->decl_map, cident, &iret); + if (iret < 0) { + printf("Failed to add '%s' to the declaration map\n", cident); + free(cident); + // Empty destructor + goto failed; + } else if (iret == 0) { + /* if ((storage == STORAGE_NONE) || !type_t_equal(typ, kh_val(dest->f->decl_map, it))) { + printf("Error: '%s' is already in the declaration map (storage=%u)\n", cident, storage); + free(cident); + type_del(typ); + // Empty destructor + goto failed; + } else */ { + printf("Warning: '%s' is already in the declaration map with the same type\n", cident); + free(cident); + type_del(typ); + } + } else { + kh_val(dest->f->decl_map, it) = typ; + } + } else if (!is_init && !is_list) { + dest->argt.dest = typ; + if (cur_ident) string_del(cur_ident); + goto success; + } else if (!is_init && is_list) { + if (!vector_push(st_members, dest->structms.dest, ((st_member_t){.name = cur_ident, .typ = typ, .is_bitfield = 0}))) { + printf("Error: failed to add structure member %s\n", string_content(cur_ident)); + string_del(cur_ident); + // Empty destructor + goto failed; + } + } else { + printf("Internal error: unknown is_init/is_list combination %d%d\n", is_init, is_list); + // Empty destructor + goto failed; + } + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_EQ)) { + // Initialization + if (!is_init) { + printf("Error: unexpected initializer\n"); + goto failed; + } + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) { + // { ... } + int nlbraces = 0; + do { + proc_token_del(tok); + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) ++nlbraces; + else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RBRACKET)) { + if (nlbraces) --nlbraces; + else break; + } + } while (!proc_token_isend(tok)); + if (proc_token_isend(tok)) { + printf("Error: unexpected token in declaration initializer\n"); + proc_token_del(tok); + goto failed; + } + } else { + expr_t *e = parse_expression(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS, PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, 15); + if (!e) { + printf("Error: invalid declaration initializer\n"); + goto failed; + } + expr_del(e); + } + if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON))) { + printf("Error: unexpected token in declaration initializer\n"); + proc_token_del(tok); + goto failed; + } + validation = (tok->tokv.sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL; + } + if (validation == VALIDATION_LAST_DECL) goto success; + else { + cur_ident = NULL; has_ident = 0; + typ = base_type; ++typ->nrefs; + cur_bottom = NULL; + vector_last(size_t, nptr_stack) = 0; + *tok = proc_next_token(prep); + break; + } + } else if (!is_init && is_list && (tok->tokv.sym == SYM_COLON)) { + if (vector_size(size_t, nptr_stack) != 1) { + printf("Error: invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]); + // Empty destructor + goto failed; + } + // Try to free some redundant types + typ = type_try_merge(typ, PDECL_TYPE_SET); + + // storage == STORAGE_NONE + *tok = proc_next_token(prep); + expr_t *e = parse_expression(dest->structms.struct_map, dest->structms.type_map, dest->structms.enum_map, + dest->structms.builtins, dest->structms.const_map, dest->structms.type_set, + prep, tok, 14); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON))) { + printf("Error: unexpected token in bitfield width\n"); + expr_del(e); + proc_token_del(tok); + goto failed; + } + num_constant_t eval; + if (!eval_expression(e, dest->structms.const_map, &eval)) { + expr_del(e); + // Empty destructor + goto failed; + } + expr_del(e); + + int validation = validate_storage_type(storage, PDECL_BUILTINS, typ, tok->tokv.sym); + if (!validation) { + // Empty destructor + goto failed; + } + + if (validation == VALIDATION_FUN) { + // We are not at the top-level or we have an initialization list + // Should never happen + printf("Error: invalid function definition in structure definition\n"); + // Empty destructor + goto failed; + } + + size_t width; + switch (eval.typ) { + case NCT_INT32: + if (eval.val.i32 < 0) { + printf("Error: invalid negative bitfield width\n"); + goto failed; + } + width = (size_t)eval.val.i32; + break; + case NCT_UINT32: + width = (size_t)eval.val.u32; + break; + case NCT_INT64: + if (eval.val.i64 < 0) { + printf("Error: invalid negative bitfield width\n"); + goto failed; + } + width = (size_t)eval.val.i64; + break; + case NCT_UINT64: + width = (size_t)eval.val.u64; + break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Error: invalid non-integer bitfield width\n"); + goto failed; + } + + if (!vector_push(st_members, dest->structms.dest, + ((st_member_t){.name = cur_ident, .typ = typ, .is_bitfield = 1, .bitfield_width = width}))) { + printf("Error: failed to add structure member %s\n", string_content(cur_ident)); + string_del(cur_ident); + // Empty destructor + goto failed; + } + if (validation == VALIDATION_LAST_DECL) goto success; + else { + cur_ident = NULL; has_ident = 0; + typ = base_type; ++typ->nrefs; + cur_bottom = NULL; + vector_last(size_t, nptr_stack) = 0; + *tok = proc_next_token(prep); + break; + } + } + /* FALLTHROUGH */ + case PTOK_KEYWORD: + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) { + if (has_ident) { + printf("Error: invalid keyword '_Atomic' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword '_Atomic' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_atomic = 1; + *tok = proc_next_token(prep); + break; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CONST)) { + if (has_ident) { + printf("Error: invalid keyword 'const' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword 'const' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_const = 1; + *tok = proc_next_token(prep); + break; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_RESTRICT)) { + if (has_ident) { + printf("Error: invalid keyword 'restrict' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword 'restrict' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_restrict = 1; + *tok = proc_next_token(prep); + break; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOLATILE)) { + if (has_ident) { + printf("Error: invalid keyword 'volatile' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword 'volatile' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_volatile = 1; + *tok = proc_next_token(prep); + break; + } + } + /* FALLTHROUGH */ + case PTOK_INVALID: + case PTOK_NUM: + case PTOK_STRING: + case PTOK_PRAGMA: + case PTOK_EOF: + printf("Error: unexpected token (parse_declarator %d%d%d%d)\n" + "Current state:\n" + " storage: %u\n" + " type: ", is_init, is_list, allow_decl, allow_abstract, storage); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + goto failed; + } + } + +success: + vector_del(size_t, nptr_stack); + // typ has moved, we must not destroy it + return 1; +failed: + vector_del(size_t, nptr_stack); +failed0: + if (cur_ident) string_del(cur_ident); + type_del(typ); + return 0; +} + +file_t *parse_file(const char *filename, FILE *file) { + char *dirname = strchr(filename, '/') ? strndup(filename, (size_t)(strrchr(filename, '/') - filename)) : NULL; + preproc_t *prep = preproc_new_file(file, dirname, filename); + if (!prep) { + printf("Failed to create the preproc structure\n"); + if (dirname) free(dirname); + return NULL; + } + file_t *ret = file_new(); + if (!ret) { + printf("Failed to create the file structure\n"); + preproc_del(prep); + return NULL; + } + + type_t *typ = type_new(); + if (!typ) { + printf("Failed to create a type info structure\n"); + goto failed; + } + while (1) { + proc_token_t tok = proc_next_token(prep); + if (tok.tokt == PTOK_EOF) { + goto success; + } else if (tok.tokt == PTOK_PRAGMA) { + switch (tok.tokv.pragma.typ) { + case PRAGMA_ALLOW_INTS: { + const char *typenames[] = {BTT_INT_EXTS}; + for (size_t i = 0; i < sizeof typenames / sizeof *typenames; ++i) { + int iret; + char *dup = strdup(typenames[i]); + if (!dup) { + printf("Failed to create a type info structure\n"); + goto failed; + } + type_t *t = ret->builtins[BTT_START_INT_EXT + i]; + khiter_t it = kh_put(type_map, ret->type_map, dup, &iret); + if (iret < 0) { + printf("Failed to add an intrinsic to the type map\n"); + goto failed; + } else if (iret == 0) { + if (!type_t_equal(t, kh_val(ret->type_map, it))) { + printf("Error: %s is already defined\n", dup); + free(dup); + goto failed; + } + free(dup); + } else { + ++t->nrefs; + kh_val(ret->type_map, it) = t; + } + } + break; } + case PRAGMA_MARK_SIMPLE: { + khiter_t it = kh_get(type_map, ret->type_map, string_content(tok.tokv.pragma.val)); + string_del(tok.tokv.pragma.val); + if (it == kh_end(ret->type_map)) { + printf("Invalid explicit_simple pragma: ident is not a typedef\n"); + goto failed; + } + type_t *typ0 = kh_val(ret->type_map, it); + if (typ0->typ != TYPE_STRUCT_UNION) { + printf("Invalid explicit_simple pragma: ident is not a typedef to a structure or union\n"); + goto failed; + } + typ0->val.st->explicit_simple = 1; + break; } + } + } else if (proc_token_iserror(&tok)) { + printf("Error: unexpected error token\n"); + proc_token_del(&tok); + goto failed; + } else { + enum decl_storage storage = STORAGE_NONE; + enum decl_spec spec = SPEC_NONE; + if (!parse_declaration_specifier(ret->struct_map, ret->type_map, ret->enum_map, &ret->builtins, ret->const_map, + ret->type_set, prep, &tok, &storage, &spec, typ)) { + goto failed; + } + if (spec == SPEC_NONE) continue; // Declaration was an assert, typ is unchanged + int ok; + typ = type_try_merge(typ, ret->type_set); + if ((tok.tokt != PTOK_SYM) || (tok.tokv.sym != SYM_SEMICOLON)) { + ok = parse_declarator(&(struct parse_declarator_dest_s){.f = ret}, prep, &tok, storage, typ, 1, 1, 1, 0); + } else { + ok = validate_storage_type(storage, &ret->builtins, typ, tok.tokv.sym); + } + if (!ok) { + goto failed; + } else { + // Current token is ';' (or '}' for functions), ie. end of declaration + type_del(typ); + typ = type_new(); + if (!typ) { + printf("Failed to create a type info structure\n"); + goto failed; + } + } + } + } + +success: + preproc_del(prep); + type_del(typ); + return ret; +failed: + preproc_del(prep); + if (typ) type_del(typ); + file_del(ret); + return NULL; +} |