X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/f522ffe295db90f2072c70ae3583a5f0d064677d..a2c3dad92b2bf55488174c203563224880380c9b:/src/parse-decl.c diff --git a/src/parse-decl.c b/src/parse-decl.c index 0f66029..8f0bd38 100644 --- a/src/parse-decl.c +++ b/src/parse-decl.c @@ -27,6 +27,33 @@ #include "scan.h" #include "errmsg.h" +/* + * Allocate a "parse item", which is a union of several parse tree + * structure types, together with a string buffer. The s_sz argument + * specifies the size of the string (including its terminator), which + * may be zero. + * + * The union's declarator member is pre-initialized to a valid "identifier" + * declarator, which shares several interesting offsets with the "declspec" + * structure for an "identifier" type specifier. + */ +struct parse_item *cdecl__alloc_item(size_t s_sz) +{ + struct parse_item *ret; + + ret = malloc(offsetof(struct parse_item, s) + s_sz); + if (!ret) { + cdecl__errmsg(CDECL__ENOMEM); + return NULL; + } + + ret->u.declarator.child = NULL; + ret->u.declarator.type = CDECL_DECL_IDENT; + ret->u.declarator.u.ident = ret->s; + + return ret; +} + /* * We can represent type specifiers as a bitmap, which gives us a finite * list of acceptable bitmap values according to the C standard. However, @@ -46,9 +73,10 @@ */ static int valid_typespec(struct cdecl_declspec *s) { + struct cdecl_declspec *c; unsigned long map = 0; - for (struct cdecl_declspec *c = s; c; c = c->next) { + for (c = s; c; c = c->next) { unsigned long bit; if (cdecl_spec_kind(c) != CDECL_SPEC_TYPE) @@ -90,7 +118,7 @@ static int valid_typespec(struct cdecl_declspec *s) */ static bool valid_declspecs(struct cdecl *decl, bool top) { - struct cdecl_declspec *specs = decl->specifiers; + struct cdecl_declspec *c, *specs = decl->specifiers; struct cdecl_declarator *d = decl->declarators; bool abstract = cdecl_is_abstract(d); unsigned num_storage = 0; @@ -98,7 +126,7 @@ static bool valid_declspecs(struct cdecl *decl, bool top) if (!valid_typespec(specs)) return false; - for (struct cdecl_declspec *c = specs; c; c = c->next) { + for (c = specs; c; c = c->next) { switch (cdecl_spec_kind(c)) { case CDECL_SPEC_TYPE: if (c->type == CDECL_TYPE_VOID && @@ -173,22 +201,29 @@ static bool valid_declspecs(struct cdecl *decl, bool top) static struct cdecl_declarator *reduce_function(struct cdecl *param) { - struct cdecl_declspec *spec = param->specifiers; - struct cdecl_declarator *decl = param->declarators; - struct cdecl_declarator *last; + struct cdecl_declarator *d, **p = ¶m->declarators; + struct parse_item *spec = (void *)param->specifiers; - for (last = decl; last && last->type != CDECL_DECL_NULL;) - last = last->child; + while ((d = *p)->child) + p = &d->child; - if (!last) + if (d->type != CDECL_DECL_NULL) return NULL; - last->type = CDECL_DECL_IDENT; - last->u.ident = spec->ident; - free(param); - free(spec); + /* + * The child and u.ident members of cdecl_declarator are expected + * to be located at identical offsets as, respectively, the next + * and ident members within cdecl_declspec, so the expectation is + * that the compiler can elide both assignments. + */ + spec->u.declarator.child = (void *)spec->u.declspec.next; + spec->u.declarator.u.ident = spec->u.declspec.ident; + spec->u.declarator.type = CDECL_DECL_IDENT; + *p = &spec->u.declarator; - return decl; + d = param->declarators; + free(param); + return d; } static bool function_is_reducible(struct cdecl_declarator *d) @@ -227,204 +262,200 @@ simplify_functions(struct cdecl_declarator **p, struct cdecl_declarator *d) if (!new) return 0; /* e.g. int (foo bar) */ *p = new; - free(d->child); free(d); - return 0; + return 1; } /* - * The parser's bias towards considering things as functions whenever possible - * makes nested parentheses tricky. (x) is considered to be part of a function - * declarator until simplify_functions converts it. The problem is that - * (((x))) is not valid as part of a function declarator, but it *is* valid - * as an identifier enclosed 3 times in parentheses. This is complicated by - * the fact that things like (((int))) are not valid anywhere. + * The main parser's bias towards considering things as functions whenever + * possible makes nested parentheses tricky. "(x)" is considered to be part + * of a function declarator until simplify_functions converts it. The problem + * is that "(((x)))" is not valid as part of a function declarator, but it _is_ + * valid as either an identifier enclosed thrice in parentheses, or an abstract + * function declarator enclosed twice in parentheses. + * + * To avoid ambiguities, the main parser actually returns a function declarator + * for every pair of parentheses. The ones we need to look at consist of a + * single parameter with an empty specifier list (noting that every real + * function parameter will have at least one type specifier). * - * To avoid ambiguities, the parser actually emits a "function" declarator for - * every pair of parentheses. The ones that can't reasonably be functions - * consist of a single "parameter" with no declaration specifiers (note that - * every valid function parameter will have at least one type specifier). + * There are two cases: * - * This pass is to remove these fake functions from the parse tree. We take - * care to avoid turning invalid things like ((int)) into valid things like - * (int) by observing that the only valid function declarators that appear - * in these "fake" parentheses are those that have a non-null child declarator - * (for instance, int ((*)(int)) *or* those that will be eliminated by the - * simplify_functions pass. + * - For (), the parser emits a parameter with a lone null declarator. + * This fake parameter simply gets deleted, leaving us with a normal + * function declarator with an empty identifier list. + * + * - Otherwise, the parameter's outermost declarator is not null. The + * function itself is deleted, replaced in the parse tree with the + * fake parameter's declarator. + * + * Repeating until there no fake parameters, this reduction transforms, for + * example, "(((x)))" into "(x)", an abstract function declarator. The result + * is then subject to the function simplification step, which will turn "(x)" + * into x (declaring an identifier). + * + * The whole process is repeated until no more changes are made to the parse + * tree, or a syntax error is detected. */ - -static int -reduce_parentheses(struct cdecl_declarator **p, struct cdecl_declarator *d) +static struct cdecl *fake_function_param(struct cdecl_declarator *d) { struct cdecl *param; if (d->type != CDECL_DECL_FUNCTION) - return 0; + return NULL; param = d->u.function.parameters; - if (param && param->specifiers == NULL) { - struct cdecl_declarator *decl; + if (!param || param->specifiers) + return NULL; - assert(!param->next); + assert(!param->next); + return param; +} - decl = param->declarators; - if (decl->type == CDECL_DECL_NULL) { - free(decl); - free(param); - d->u.function.parameters = NULL; - return 0; - } +static int +reduce_parentheses(struct cdecl_declarator **p, struct cdecl_declarator *d) +{ + struct cdecl *param; - if (d->child->type != CDECL_DECL_NULL) { - cdecl__errmsg(CDECL__EBADPARAM); - return -1; - } + do { + d = *p; + while ((param = fake_function_param(d))) { + struct cdecl_declarator *decl = param->declarators; + d->u.function.parameters = NULL; - free(d->child); - free(param); - free(d); - *p = decl; + if (decl->type != CDECL_DECL_NULL) { + if (d->child->type != CDECL_DECL_NULL) { + /* Fake parameter on real function. */ + d->u.function.parameters = param; + cdecl__errmsg(CDECL__EBADPARAM); + return -1; + } - /* - * We may have replaced d with another fake function which - * also needs to be eliminated. - */ - if (reduce_parentheses(p, decl) < 0) - return -1; + param->declarators = d; + *p = d = decl; + } - /* - * If the remaining declarator is a function, make sure it's - * valid by checking its reducibility. - */ - decl = *p; - if (decl->type == CDECL_DECL_FUNCTION - && decl->child->type == CDECL_DECL_NULL - && !function_is_reducible(decl)) { - cdecl__errmsg(CDECL__EMANYPAREN); - return -1; + cdecl__free(param); } - - return 0; - } + } while (simplify_functions(p, d)); return 0; } /* - * Function parameters and return types have a few restrictions that are - * really easy to check in comparison to the above absurdity. + * Returns nonzero iff the given specifier list contains a specifier + * of the indicated type. */ -static int -check_parameters(struct cdecl_declarator **p, struct cdecl_declarator *d) +static int have_specifier(struct cdecl_declspec *s, unsigned type) { - struct cdecl_declspec *spec; - struct cdecl *param; - - if (d->type != CDECL_DECL_FUNCTION) - return 0; - - for (param = d->u.function.parameters; param; param = param->next) { - if (!valid_declspecs(param, false)) - return -1; - - /* Check for "void" function parameters as a special case. */ - for (spec = param->specifiers; spec; spec = spec->next) { - if (param->declarators->type != CDECL_DECL_NULL) - continue; - if (spec->type != CDECL_TYPE_VOID) - continue; - - if (spec != param->specifiers || spec->next != NULL) { - cdecl__errmsg(CDECL__EVOIDPARAM); - return -1; - } else if (d->u.function.parameters->next) { - cdecl__errmsg(CDECL__EVOIDPARAM); - return -1; - } else if (d->u.function.variadic) { - cdecl__errmsg(CDECL__EVOIDPARAM); - return -1; - } - } - } - + for (; s; s = s->next) + if (s->type == type) + return 1; return 0; } /* - * Functions cannot return arrays or functions. Since the parse tree is - * "inside-out", we need to look for functions as the child declarator. + * Check syntax restrictions on a function declarator's child declarator. + * That is, "pointer to function", "array of function" and "function + * returning function". + * + * Returns -1 if the declaration is invalid, or 0 otherwise. */ -static int -check_rettypes(struct cdecl_declarator **p, struct cdecl_declarator *d) +static int check_function_child(struct cdecl_declarator *d) { - if (!d->child || d->child->type != CDECL_DECL_FUNCTION) - return 0; + struct cdecl_pointer *ptr; switch (d->type) { + case CDECL_DECL_POINTER: + ptr = &d->u.pointer; + if (have_specifier(ptr->qualifiers, CDECL_QUAL_RESTRICT)) { + /* pointer to function cannot be restrict qualified. */ + cdecl__errmsg(CDECL__ERESTRICTFUNC); + return -1; + } + return 0; case CDECL_DECL_FUNCTION: + /* function returning function is never allowed. */ cdecl__errmsg(CDECL__ERETFUNC); return -1; case CDECL_DECL_ARRAY: - cdecl__errmsg(CDECL__ERETARRAY); + /* array of function is never allowed. */ + cdecl__errmsg(CDECL__EFUNCARRAY); return -1; } return 0; } -static int -check_arrays(struct cdecl_declarator **p, struct cdecl_declarator *d) +/* + * Check a function parameter declaration for validity, which means it has a + * valid combination of declaration specifiers and, if it is a void parameter, + * that it is the one special case where this is allowed. + * + * Returns -1 if the declaration is invalid, or 0 otherwise. + */ +static int check_function_param(struct cdecl_function *f, struct cdecl *param) { - if (!d->child || d->child->type != CDECL_DECL_ARRAY) - return 0; - - switch (d->type) { - case CDECL_DECL_FUNCTION: - cdecl__errmsg(CDECL__EFUNCARRAY); + if (!valid_declspecs(param, false)) return -1; + + /* Check for "void" function parameters as a special case. */ + if (param->declarators->type == CDECL_DECL_NULL + && have_specifier(param->specifiers, CDECL_TYPE_VOID)) + { + struct cdecl *fp = f->parameters; + + if (f->variadic || fp->next || fp->specifiers->next) { + cdecl__errmsg(CDECL__EVOIDPARAM); + return -1; + } } return 0; } -static int -normalize_specs(struct cdecl_declarator **p, struct cdecl_declarator *d) +/* + * Normalize the specifier lists for function parameters, and then check the + * function declarator for validity. + * + * Returns -1 if the declaration is invalid, or 0 otherwise. + */ +static int postproc_function(struct cdecl_declarator *d) { - struct cdecl_function *func; - struct cdecl_pointer *ptr; + struct cdecl_function *func = &d->u.function; + struct cdecl *param; + int rc; - switch (d->type) { - case CDECL_DECL_POINTER: - ptr = &d->u.pointer; - ptr->qualifiers = cdecl__normalize_specs(ptr->qualifiers); - break; - case CDECL_DECL_FUNCTION: - func = &d->u.function; - for (struct cdecl *i = func->parameters; i; i = i->next) - i->specifiers = cdecl__normalize_specs(i->specifiers); - break; + for (param = func->parameters; param; param = param->next) { + param->specifiers = cdecl__normalize_specs(param->specifiers); + + if ((rc = check_function_param(func, param)) < 0) + return rc; } - return 0; + return check_function_child(d->child); } static int -check_qualifiers(struct cdecl_declarator **p, struct cdecl_declarator *d) +postproc_common(struct cdecl_declarator **p, struct cdecl_declarator *d) { - struct cdecl_declspec *spec; struct cdecl_pointer *ptr; - if (!d->child || d->child->type != CDECL_DECL_POINTER) + switch (d->type) { + case CDECL_DECL_POINTER: + ptr = &d->u.pointer; + ptr->qualifiers = cdecl__normalize_specs(ptr->qualifiers); return 0; - - ptr = &d->child->u.pointer; - for (spec = ptr->qualifiers; spec; spec = spec->next) { - if (spec->type == CDECL_QUAL_RESTRICT - && d->type == CDECL_DECL_FUNCTION) { - cdecl__errmsg(CDECL__ERESTRICTFUNC); + case CDECL_DECL_FUNCTION: + return postproc_function(d); + case CDECL_DECL_ARRAY: + if (d->child && d->child->type == CDECL_DECL_FUNCTION) { + /* function returning array is never allowed. */ + cdecl__errmsg(CDECL__ERETARRAY); return -1; } + return 0; } return 0; @@ -470,9 +501,9 @@ static int forall_declarators(struct cdecl *decl, static struct cdecl *do_parse(const char *str, int english_mode) { + struct cdecl *decl = NULL; YY_BUFFER_STATE state; yyscan_t scanner; - struct cdecl *decl; #if YYDEBUG extern int cdecl__yydebug; @@ -484,8 +515,15 @@ static struct cdecl *do_parse(const char *str, int english_mode) return NULL; state = cdecl__yy_scan_string(str, scanner); - if (cdecl__yyparse(scanner, &decl) != 0) + if (cdecl__yyparse(scanner, &decl) != 0) { + /* + * If the input consists of a complete, valid declaration + * followed by some garbage, that parsed declaration will + * be output by the parser and we need to free it here. + */ + cdecl__free(decl); decl = NULL; + } cdecl__yy_delete_buffer(state, scanner); cdecl__yylex_destroy(scanner); @@ -513,26 +551,16 @@ static int do_postprocess(struct cdecl *decl, int english_mode) if (!english_mode) { if (forall_declarators(i, reduce_parentheses) < 0) return 0; - if (forall_declarators(i, simplify_functions) < 0) - return 0; } - if (forall_declarators(i, check_parameters) < 0) - return 0; - if (forall_declarators(i, check_rettypes) < 0) - return 0; - if (forall_declarators(i, check_arrays) < 0) - return 0; - if (forall_declarators(i, normalize_specs) < 0) - return 0; - if (forall_declarators(i, check_qualifiers) < 0) + if (forall_declarators(i, postproc_common) < 0) return 0; if (!valid_declspecs(i, true)) return 0; - if (cdecl_is_abstract(i->declarators) - && (i != decl || i->next)) { + if (decl->next && cdecl_is_abstract(i->declarators)) { + /* Abstract full declarators: there can only be one. */ cdecl__errmsg(CDECL__EDECLTYPE); return 0; }