/* * Copyright (c) 2004 - 2007, Nils R. Weller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Parsing of ``advanced declarations''. * This module exports parse_func/struct/enum() used by the parse_decl() * subsystem */ #include "decl_adv.h" #include #include #include #include "type.h" #include "backend.h" #include "token.h" #include "misc.h" #include "decl.h" #include "symlist.h" #include "error.h" #include "scope.h" #include "reg.h" #include "typemap.h" #include "expr.h" #include "attribute.h" #include "inlineasm.h" #include "debug.h" #include "n_libc.h" /* Parsing framework for structure and function definitions/declarations */ #define STORE_FUNC (1) #define STORE_STRUCT (2) /* * Stores src argument as structure member(s) of deststr argument * The integer pointed to by the ``alloc'' argument must be set to * zero every time a new structure is parsed, but not be touched after- * wards */ static void store_member_struct(struct ty_struct *deststr, struct decl **src) { int nsrc; /* Determine number of declarations */ for (nsrc = 0; src[nsrc] != NULL; ++nsrc) ; deststr->nmemb += nsrc; store_decl_scope(deststr->scope, src); } /* Same as store_member_struct(), but for function arguments */ static void store_member_func(struct ty_func *destfun, struct decl **src) { int nsrc; /* Determine number of declarations */ for (nsrc = 0; src[nsrc] != NULL; ++nsrc) ; destfun->lastarg = src[nsrc - 1]; destfun->nargs += nsrc; store_decl_scope(curscope, src); } static int store_member_enum(struct ty_enum *dest, char *name, struct token *value, struct type *type, int *alloc) { int i; struct scope *scope; struct vreg *vr; struct decl *dec[2]; /* Resize space if necessary */ if (dest->nmemb >= *alloc) { *alloc += 8; dest->members = n_xrealloc(dest->members, *alloc * sizeof *dest->members); } vr = vreg_alloc(NULL, value, NULL, NULL); if (type == NULL) { vr->type = n_xmemdup(make_basic_type(TY_INT), sizeof *vr->type); } else { vr->type = type; } vr->size = backend->get_sizeof_type(vr->type, NULL); dec[0] = alloc_decl(); dec[0]->dtype = vr->type; dec[0]->dtype->name = name; dec[0]->dtype->tenum = dest; dec[0]->vreg = vr; dec[1] = NULL; /* Store */ dest->members[ dest->nmemb ].name = name; dest->members[ dest->nmemb ].dec = dec[0]; for (i = 0; i < dest->nmemb; ++i) { if (strcmp(dest->members[i].name, name) == 0) { return -1; } } ++dest->nmemb; /* * An enumeration constant need only appear in the symbol list, * as it doesn't have to be allocated anywhere */ for (scope = curscope; scope->type == SCOPE_STRUCT; scope = scope->parent) ; append_symlist(scope, &scope->slist, &scope->slist_tail, dec[0]); return 0; } /* * Function for glueing the member declarations of structures and unions * together. This must be called by the declaration parsing code as soon * as a structure *definition* (struct foo { ... }) is encountered, where * the curtok argument must point to the node after { * Returns a pointer to the newly allocated definition on success, else * a null pointer */ #define PS_RET_ERR(ret) do { \ if (ret && ret->scope) { \ /*free(ret->scope);*/ \ } \ free(ret); \ close_scope(); \ return NULL; \ } while (0) /* * XXX struct alignment is done in x86_emit_nasm.c, print_init_list() and * x86_emit_nasm.c, do_print_struct() ... there is some duplication of * efforts, should probably all be done here */ struct ty_struct * parse_struct(struct token **curtok, char *tag, int type) { struct token *t; struct token *prevtok = NULL; struct decl **dec = NULL; struct ty_struct *ret; struct sym_entry *se; unsigned long offset = 0; size_t maxalign = 0; size_t align; ret = alloc_ty_struct(); ret->tag = tag; #ifdef DEBUG2 printf("Parsing structure definition...\n"); #endif ret->scope = new_scope(SCOPE_STRUCT); for (t = *curtok; t != NULL; t = t->next) { prevtok = t; if (t->type == TOK_COMP_CLOSE) { /* } reached - definition complete */ break; } else if (!IS_KEYWORD(t->type) && t->type != TOK_IDENTIFIER) { errorfl(t, "Syntax error at ``%s'' - " "Expected start of structure member definition", t->ascii); PS_RET_ERR(ret); } if ((dec = parse_decl(&t, DECL_STRUCT)) == NULL) { PS_RET_ERR(ret); } store_member_struct(ret, dec); /* * If any of the structure members is incomplete, the entire * structure is incomplete * XXX implement incomplete check in decl.c! */ #if 0 /* dtype is not yet filled! */ if (dec->dtype->incomplete) { ret->incomplete = 1; } #endif } if (ret->scope->slist == NULL) { errorfl(t, "Empty structure declaration"); PS_RET_ERR(ret); } ret->size = 0; for (se = ret->scope->slist; se != NULL; se = se->next) { size_t size; struct type *ty = se->dec->dtype; if (ty->tlist != NULL) { if (ty->tlist->type == TN_ARRAY_OF && ty->tlist->arrarg->const_value == NULL) { /* C99 flexible array member */ /* * 09/15/07: Wow this was missing alignment * and member offset assignment... because * we used to ``break'' here */ if (se->next != NULL) { errorfl(NULL /* XXX */, "Flexible array not " "last member of " "structure"); continue; } else { ret->flexible = se->dec; /* break;*/ } /* continue;*/ } } if (ret->flexible != se->dec) { size = backend->get_sizeof_decl(se->dec, NULL); if (size == 0) { errorfl(NULL, "Incomplete type `%s'", ty->name); continue; } } if (type == TY_STRUCT) { /* struct */ align = backend->get_align_type(ty); if (/*align*/1) { /* Align all types > char */ while (offset % align/*size*/) ++offset; } se->dec->offset = offset; offset += size; } else { /* union */ size_t align; align = backend->get_align_type(ty); if (align > maxalign) { maxalign = align; } if (size > ret->size) { ret->size = size; } } } if (type == TY_UNION) { /* XXX this seems completely bogus ... */ if (/*align*/1) { while (ret->size % maxalign) ++ret->size; } } else { /* XXX ?!? */ ret->size = offset; se = ret->scope->slist; #if 0 align = backend->get_align_type(se->dec->dtype); #endif { static struct type ty; ty.code = TY_STRUCT; ty.tstruc = ret; align = backend->get_align_type(&ty); } while (ret->size % align) { ++ret->size; } } if (t == NULL) { errorfl(prevtok, "Unexpected end of file"); PS_RET_ERR(ret); } #ifdef DEBUG2 printf("Structure def with %d members parsed successfully!\n", ret->nmemb); #endif if (ret->nmemb == 0) { if (ret->tag != NULL) { errorfl(*curtok, "Empty structure declaration `%s'", ret->tag); } else { errorfl(*curtok, "Empty structure declaration"); } } /* XXX ? */ *curtok = t; close_scope(); return ret; } struct ty_enum * parse_enum(struct token **curtok) { struct ty_enum *ret; struct token *t; struct token *prevtok = NULL; struct token *valtok; struct expr *ex; char *name; int op; int val = 0; int alloc; int curval = 0; ret = alloc_ty_enum(); alloc = 0; #ifdef DEBUG2 puts("------------------------------------"); #endif for (t = *curtok; t != NULL; t = t->next) { prevtok = t; if (t->type == TOK_COMP_CLOSE) { /* End of definition reached */ break; } else if (t->type != TOK_IDENTIFIER) { errorfl(t, "Syntax error at `%s' - Expected enum " "member name", t->ascii); free(ret); return NULL; } /* * Ok, is identifier. Either read constant expression, if * specified, or add one to previous member value */ name = t->data; if (next_token(&t) != 0) { free(ret); return NULL; } if (t->type == TOK_COMP_CLOSE) { /* } - Definition ends here */ val = curval; valtok = const_from_value(&val, NULL); if (store_member_enum(ret, name, valtok, NULL, &alloc) != 0) { errorfl(t, "Multiple definitions of `%s'", name); } break; } else if (t->type == TOK_OPERATOR) { /* Must be ``,'' or ``='' */ op = *(int *)t->data; if (op == TOK_OP_ASSIGN) { if (next_token(&t) != 0) { return NULL; } ex = parse_expr(&t, TOK_OP_COMMA, TOK_COMP_CLOSE, EXPR_CONST, 1); if (ex == NULL) { #ifndef NO_EXPR return NULL; #endif } if (t->type == TOK_COMP_CLOSE) { t = t->prev; } /* XXX evaluate ex ... */ valtok = const_from_value( ex->const_value->value, ex->const_value->type); if (store_member_enum(ret, name, valtok, NULL, &alloc) != 0) { errorfl(t, "Multiple definitions of " "`%s'", name); } /* Now update current value counter */ curval = (int)cross_to_host_size_t( ex->const_value); ++curval; } else if (op == TOK_OP_COMMA) { /* * No initializer given, so this gets the value * of the previous member + 1. Note that C99 * permits a ``dangling'' comma at the end of * an enum def., as in ``enum foo { FOO, BAR, * };'', so we must also check whether the * definition ends here */ val = curval++; valtok = const_from_value(&val, NULL); if (store_member_enum(ret, name, valtok, NULL, &alloc) != 0) { errorfl(t, "Multiple definitions of " "`%s'", name); } if (t->next && t->next->type == TOK_COMP_CLOSE) { /* Definition ends */ t = t->next; break; } } } else { errorfl(t, "Syntax error at `%s' - " "Expected enum initializer, comma or }", t->ascii); free(ret); return NULL; } } #ifdef DEBUG2 puts("------------------------------------"); #endif if (t == NULL) { errorfl(prevtok, "Unexpected end of file"); free(ret); return NULL; } *curtok = t; return ret; } struct ntab { struct nentry { char *name; int refcount; int line; struct decl *dec; } *nametab; int alloc; int index; }; /* * Delete structure members or function arguments of dest, if existent, then * delete dest. Intended to use for early escape from syntax errors in struct * and func parsing */ static void drop_stuff(struct ty_func *dest, struct ntab *n) { #if 0 if (dest->args != NULL) { /* Is function */ free(dest->args); dest->args = NULL; } #endif if (dest->scope != NULL) { /* free(dest->scope); */ } free(dest); if (n != NULL && n->nametab != NULL) { free(n->nametab); n->nametab = NULL; } } /* * Stores pointer ``p'' in the name entry table pointed to by ``dest''. * The table must be created with all members set to zero, e.g. by * writing: struct ntab n = { 0, 0, 0 }; * Returns 1 if a string equivalent to the one to by ``p'' already * resides in the table (p is not stored if this happens), else 0 * n.nametab[n.index].name will always be a null pointer */ static int store_ident(struct ntab *dest, char *name, int line) { int i; if (dest->index >= (dest->alloc - 1)) { dest->alloc += 8; dest->nametab = n_xrealloc(dest->nametab, dest->alloc * sizeof *dest->nametab); } /* Make sure we have no multiple definitions */ for (i = 0; i < dest->index; ++i) { if (strcmp(dest->nametab[i].name, name) == 0) { /* Already exists - no need to save */ return 1; } } dest->nametab[ dest->index ].name = name; dest->nametab[ dest->index ].line = line; dest->nametab[ dest->index ].refcount = 0; dest->nametab[ ++dest->index ].name = NULL; return 0; } /* * Looks up string pointed to by ``name'' in name table pointed to by * ``n''. Returns a pointer to that on success, else NULL. */ static struct nentry * lookup_ident(struct ntab *n, char *name) { int i; for (i = 0; n->nametab[i].name != NULL; ++i) { if (strcmp(n->nametab[i].name, name) == 0) { ++n->nametab[i].refcount; return &n->nametab[i]; } } return NULL; } /* * Helper macro for parse_func() - Close scope only if it is a function * prototype scope, i.e. not a definition. If we are dealing with a * definition (prototype followed by a ``{''), this is block scope and * should not be closed */ #if 0 #define CLOSE_SCOPE(tok) do { \ if (tok) { \ struct token *_t2; \ if ((_t2 = ignore_attr(tok)) != NULL) { \ if (_t2->type != TOK_COMP_OPEN) { \ /* Is declaration */ \ close_scope(); \ } \ } \ } \ } while (0) #endif void CLOSE_SCOPE(struct token *tok) { do { \ if (tok) { \ struct token *_t2; \ if ((_t2 = ignore_attr(tok)) != NULL) { \ if (_t2->type != TOK_COMP_OPEN) { \ /* Is declaration */ \ close_scope(); \ } \ } \ } \ } while (0) ; } static void check_matches(struct token *t, struct ntab *n, struct decl **dec) { int i; struct nentry *np; for (i = 0; dec[i] != NULL; ++i) { if ((np = lookup_ident(n, dec[i]->dtype->name)) == NULL) { errorfl(t, "Declaration for non-existent parameter `%s'", dec[i]->dtype->name); } else if (np->refcount > 1) { errorfl(t, "Multiple declarations for parameter `%s'", dec[i]->dtype->name); } else { np->dec = dec[i]; } } } static int do_parse_kr_func(struct token **tok, struct ty_func *ret) { struct token *t; struct decl **dec; int errors = 0; int i; char **names = NULL; struct nentry *np; struct ntab n = { 0, 0, 0 }; ret->type = FDTYPE_KR; (void) names; for (t = *tok; t != NULL; t = t->next) { if (t->type == TOK_PAREN_CLOSE) { /* * End of parenthesized part of declaration reached - * read parameter declarations now! */ if (next_token(&t) != 0) { return -1; } for (; t != NULL; t = t->next) { if (t->type == TOK_COMP_OPEN) { /* * Opening compound statement - * end reached */ t = t->prev; break; } else if (IS_TYPE(t)) { dec = parse_decl(&t, DECL_FUNCARG_KR); if (dec == NULL) { return -1; } if (t && t->type != TOK_SEMICOLON) { errorfl(t, "Syntax error at `%s' - " "Expected semicolon", t->ascii); return -1; } else if (t == NULL) { break; } check_matches(t, &n, dec); #if 0 store_member_func(ret, dec); #endif } else { if (t->type == TOK_OPERATOR) { int i; for (i = 0; i < 3; ++i) { if (*(int *)t->data != TOK_OP_STRUMEMB) { break; } if (t->next == NULL) { break; } t = t->next; } if (i == 3) continue; } /* Garbage */ errorfl(t, "Syntax error at `%s' - " "Expected parameter " "declaration", t->ascii); return -1; } } /* * If this point is reached, either the declaration * list was valid, or the end-of-file has been * encountered prematurely. Both cases are handled by * the code below... * We must check whether all parameters enclosed by * parentheses have been resolved by declarations! */ if ((np = n.nametab) != NULL) { for (i = 0; n.nametab[i].name != NULL; ++i) { struct decl *dummy[2]; if (n.nametab[i].refcount < 1) { /* Undeclared - assume int */ n.nametab[i].dec = alloc_decl(); n.nametab[i].dec->dtype = n_xmemdup( make_basic_type(TY_INT), sizeof(struct type)); n.nametab[i].dec->dtype->name = n.nametab[i].name; } dummy[0] = n.nametab[i].dec; dummy[1] = NULL; store_member_func(ret, dummy); } } break; } else { /* * This must be an identifier. Everything else is * illegal! */ if (t->type != TOK_IDENTIFIER) { errorfl(t, "Syntax error at `%s' - " "Expected identifier", t->ascii); break; } if (store_ident(&n, t->data, t->line) == 1) { errorfl(t, "Multiple definitions of `%s'", t->ascii); ++errors; } if (next_token(&t) != 0) { return -1; } if (t->type == TOK_OPERATOR) { if (*(int *)t->data != TOK_OP_COMMA) { errorfl(t, "Syntax error at `%s' - " "expected comma or closing " "parentheses", t->ascii); } else { continue; } } else if (t->type != TOK_PAREN_CLOSE) { errorfl(t, "Syntax error at `%s' - " "expected comma or closing parentheses", t->ascii); } else { t = t->prev; continue; } return -1; } } if (errors) { return -1; } *tok = t; return 0; } static int do_parse_iso_func(struct token **tok, struct ty_func *ret) { struct token *t; struct token *prevtok; struct decl **dec; ret->type = FDTYPE_ISO; t = *tok; for (t = *tok; t != NULL; t = t->next) { prevtok = t; if (t->type == TOK_PAREN_CLOSE) { if (t->next == NULL) { break; } if (t->next->type != TOK_COMP_OPEN) { if (ret->nargs == 0) { /* * This is a declaration and no * arguments are given; -1 indicates * an unknown amount of arguments */ ret->nargs = -1; } if (t->next->type == TOK_KEY_ASM) { t = t->next; ret->asmname = parse_asm_varname(&t); if (ret->asmname == NULL) { return -1; } t = t->prev; } } #if 0 /* End of declaration reached */ if (ret->nargs == 0) { /* * This might either be a declaration like * void f(); * or a definition like * void f() { * In case of a declaration, -1 indicates * unknown number of arguments. */ if (t->next != NULL && t->next->type != TOK_COMP_OPEN) { ret->nargs = -1; } } #endif break; } else if (IS_TYPE(t)) { struct token *tmp; /* int, unsigned, static, etc or typedef */ if (ret->variadic) { errorfl(t, "`...' must be last argument"); } tmp = t; if ((dec = parse_decl(&t, DECL_FUNCARG)) == NULL) { return -1; } #ifdef DEBUG2 printf("parsed declaration:\n"); puts("--------------"); while (tmp != t) { printf("%s\n", tmp->ascii); tmp = tmp->next; } puts("--------------"); #endif if (t && (t->type != TOK_OPERATOR || *(int *)t->data != TOK_OP_COMMA)) { if (t->type != TOK_PAREN_CLOSE) { errorfl(t, "Syntax error at `%s' - " "expected comma", t->ascii); return -1; } else { t = t->prev; } } store_member_func(ret, dec); } else { /* Garbage or variadic function */ if (t->type == TOK_OPERATOR) { int i = 0; do { if (*(int *)t->data != TOK_OP_STRUMEMB) { break; } } while (++i < 3 && (t = t->next) && t->type == TOK_OPERATOR); if (i == 3) { if (ret->variadic) { errorfl(t, "Duplicate use of " "`...'"); } ret->variadic = 1; if (ret->nargs == 0) { errorfl(t, "At least one " "argument before `...'" "required"); } t = t->next; if (t && (t->type != TOK_OPERATOR || *(int *)t->data != TOK_OP_COMMA)) { if (t->type != TOK_PAREN_CLOSE) { errorfl(t, "Syntax " "error at `%s'" "- expected " "comma", t->ascii); return -1; } else { t = t->prev; } } continue; } } errorfl(t, "Syntax error at `%s' - Expected closing " "parentheses or start of parameter declaration", t->ascii); return -1; } } *tok = t; return 0; } static void put_func_name(const char *name) { size_t size = strlen(name) + 1; struct decl *d = alloc_decl(); struct initializer *init = alloc_initializer(); struct ty_string *ts; struct expr *ex = alloc_expr(); struct decl *decls[4]; ts = make_ty_string(name, size); ts->ty->tlist->arrarg = ex; ex->const_value = n_xmalloc(sizeof *ex->const_value); memset(ex->const_value, 0, sizeof *ex->const_value); ex->const_value->str = ts; ex->const_value->type = ts->ty; init->type = INIT_EXPR; init->data = ex; ts->ty->storage = TOK_KEY_STATIC; d->dtype = ts->ty; ts->ty->name = "__func__"; d->init = init; decls[0] = d; decls[1] = n_xmemdup(d, sizeof *d); decls[1]->dtype = n_xmemdup(decls[1]->dtype, sizeof *decls[1]->dtype); decls[1]->dtype->name = "__PRETTY_FUNCTION__"; /* 09/30/07: BitchX uses __FUNCTION__ */ decls[2] = n_xmemdup(d, sizeof *d); decls[2]->dtype = n_xmemdup(decls[1]->dtype, sizeof *decls[1]->dtype); decls[2]->dtype->name = "__FUNCTION__"; decls[3] = NULL; store_decl_scope(curscope, decls); decls[1]->dtype->name = decls[0]->dtype->name; decls[2]->dtype->name = decls[0]->dtype->name; #if 0 decls[1]->is_alias = 1; #endif /* * 070501: Setting a boolean alias flag is not correct. Instead we * must have a pointer to the base declaration, so that access_ * symbol() can reference it. And then only define that declaration * in the backend (currently only PowerPC seems to make use of * is_alias at all */ decls[1]->is_alias = decls[0]; decls[2]->is_alias = decls[0]; } /* * Must be called with a curtok pointing to the beginning of the function * argument list * void f(foo) * ^ * 09/30/07: Now we pass the declaration type to avoid breakage when a * cast is read; * * (void(*)())expr; * * ... here the (kludged) search for the identifier breaks. Maybe we * should pass the name instead of searching it here */ struct ty_func * parse_func(struct token **curtok, const char *name) { struct token *t = NULL; struct token *prevtok = NULL; struct type *td = NULL; struct ty_func *ret; struct ntab n = { 0, 0, 0 }; /* char *name;*/ ret = alloc_ty_func(); #if 0 /* XXX kludge */ for (t = (*curtok)->prev; t->type != TOK_IDENTIFIER; t = t->prev) { ; } name = t->ascii; #endif if (next_token(curtok) != 0) { free(ret); return NULL; } t = *curtok; ret->scope = new_scope(SCOPE_CODE); if (t->type == TOK_KEY_VOID) { if (t->next != NULL) { if (t->next->type == TOK_PAREN_CLOSE) { #ifdef DEBUG2 printf("Function takes no arguments\n"); #endif *curtok = t->next; if (t->next->next && t->next->next->type == TOK_COMP_OPEN) { put_func_name(name); CLOSE_SCOPE(t->next->next); return ret; } t = t->next; #if 0 CLOSE_SCOPE(t->next->next); return ret; #endif } } else { errorfl(t, "Premature end of file"); free(ret); return NULL; } } /* * There are two possible definition types: * ISO style or K&R style, i.e. * void f(char *foo, char bar, int x) { * or * void f(foo, bar, x) * char *foo, bar; * int x; { * K&R C has no prototypes, so every paramter declaration * indicates a function definition. Thus, it can be assumed * that the parameter declarations end when the ``{'' token * is encountered. * * If the first node of the token list at this point is an * identifier, we simply assume a K&R declaration, else ISO */ if (t->type == TOK_IDENTIFIER && (td = lookup_typedef(curscope, t->data)) == NULL) { /* Is K&R declaration */ if (do_parse_kr_func(&t, ret) != 0) { drop_stuff(ret, &n); recover(curtok, TOK_PAREN_CLOSE, 0); ret = NULL; } } else { /* Is ISO declaration */ if (do_parse_iso_func(&t, ret) != 0) { drop_stuff(ret, &n); recover(curtok, TOK_PAREN_CLOSE, 0); ret = NULL; } } if (t == NULL) { errorfl(prevtok, "Unexpected end of file"); drop_stuff(ret, ret->type == FDTYPE_KR? (void *)&n : (void *)NULL); return NULL; } if (t->next && t->next->type == TOK_COMP_OPEN) { put_func_name(name); } *curtok = t; CLOSE_SCOPE(t->next); return ret; }