X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/c733aff6d0b2b107c88e37a1491f270db0634819..HEAD:/src/parse-decl.c diff --git a/src/parse-decl.c b/src/parse-decl.c index c3ba32d..3c60dd2 100644 --- a/src/parse-decl.c +++ b/src/parse-decl.c @@ -1,6 +1,6 @@ /* * Parse and validate C declarations. - * Copyright © 2011-2012, 2020-2021, 2023 Nick Bowler + * Copyright © 2011-2012, 2020-2021, 2023-2024 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,35 @@ #include "scan.h" #include "errmsg.h" +static struct cdecl *fake_function_param(struct cdecl_declarator *); + +/* + * 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 +75,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 +120,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 +128,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 && @@ -149,6 +179,36 @@ static bool valid_declspecs(struct cdecl *decl, bool top) return true; } +/* + * Find the tree pointer which leads to the parameter's leaf node. + * + * Return a null pointer if the traversal locates a syntactic element which + * prevents function reduction. This occurs if the leaf node declares an + * identifier, or for nontrivial fake function parameters (see below). + */ +static struct cdecl_declarator **leaf_pointer(struct cdecl *param) +{ + struct cdecl_declarator *d, **p = ¶m->declarators; + + if ((param = fake_function_param(param->declarators))) { + if (param->declarators->type != CDECL_DECL_NULL) + return NULL; /* e.g. int (x (*)) */ + } + + while ((d = *p)->child) { + p = &d->child; + + if (fake_function_param(d->child)) + return NULL; /* e.g. int (x (*)[][1]) */ + } + + if (d->type != CDECL_DECL_NULL) + return NULL; /* e.g. int (x y) */ + + return p; +} + + /* * The C grammar leaves ambiguous some cases where parentheses represent a * function declarator or just parentheses. The language uses additional @@ -173,22 +233,26 @@ 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; - - for (last = decl; last && last->type != CDECL_DECL_NULL;) - last = last->child; + struct parse_item *spec = (void *)param->specifiers; + struct cdecl_declarator *d, **p; - if (!last) + if (!(p = leaf_pointer(param))) 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) @@ -225,12 +289,11 @@ simplify_functions(struct cdecl_declarator **p, struct cdecl_declarator *d) new = reduce_function(d->u.function.parameters); if (!new) - return 0; /* e.g. int (foo bar) */ + return 0; *p = new; - free(d->child); free(d); - return 0; + return 1; } /* @@ -260,6 +323,9 @@ simplify_functions(struct cdecl_declarator **p, struct cdecl_declarator *d) * 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 struct cdecl *fake_function_param(struct cdecl_declarator *d) { @@ -281,26 +347,28 @@ reduce_parentheses(struct cdecl_declarator **p, struct cdecl_declarator *d) { struct cdecl *param; - while ((param = fake_function_param(d))) { - struct cdecl_declarator *decl = param->declarators; - d->u.function.parameters = NULL; - - if (decl->type != CDECL_DECL_NULL) { - if (d->child->type != CDECL_DECL_NULL) { - /* Found fake parameter on real function. */ - d->u.function.parameters = param; - 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; + + 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; + } + + param->declarators = d; + *p = d = decl; } - param->declarators = d; - *p = d = decl; + cdecl__free(param); } + } while (simplify_functions(p, d)); - cdecl__free(param); - } - - simplify_functions(p, d); return 0; } @@ -462,9 +530,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; @@ -476,8 +544,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);