#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,
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;
+ free(d);
+ d = param->declarators;
+ free(param);
+ return d;
}
static bool function_is_reducible(struct cdecl_declarator *d)
uintmax_t uintval;
unsigned spectype;
_Bool boolval;
- char *strval;
struct cdecl_declspec *declspec;
struct cdecl_declarator *declarator;
struct cdecl *decl;
+ struct parse_item *item;
}
%{
struct cdecl_declspec *p;
while (x) {
p = x->next;
- free(x->ident);
free(x);
x = p;
}
switch (x->type) {
case CDECL_DECL_NULL:
- break;
case CDECL_DECL_IDENT:
- free(x->u.ident);
+ case CDECL_DECL_ARRAY:
break;
case CDECL_DECL_POINTER:
free_declspec(x->u.pointer.qualifiers);
break;
- case CDECL_DECL_ARRAY:
- free(x->u.array.vla);
- break;
case CDECL_DECL_FUNCTION:
free_decl(x->u.function.parameters);
break;
* Alter an abstract declarator (type name) to declare an identifier instead,
* used by the English parser rules to reduce "identifier as type" sequences.
*/
-static struct cdecl *insert_identifier(struct cdecl *decl, char *ident)
+static struct cdecl *insert_identifier(struct cdecl *decl, struct parse_item *ident)
{
- struct cdecl_declarator *d = decl->declarators;
+ struct cdecl_declarator *d, **p = &decl->declarators;
- while (d->child)
- d = d->child;
-
- d->type = CDECL_DECL_IDENT;
- d->u.ident = ident;
+ while ((d = *p)->child)
+ p = &d->child;
+ free(d);
+ *p = d = &ident->u.declarator;
return decl;
}
%}
-%destructor { free($$); } <strval>
+%destructor { free($$); } <item>
%destructor { free_declspec($$); } <declspec>
%destructor { free_declarator($$); } <declarator>
%destructor { free_decl($$); } <decl>
/* Magic tokens */
%token T_LEX_ERROR
-%token <strval> T_IDENT "identifier"
-%token <uintval> T_UINT "integer constant"
+%token <item> T_IDENT "identifier"
+%token <uintval> T_UINT "integer constant"
%token T_SEMICOLON ";"
%token T_ASTERISK "*"
%token T_AS "as"
%token T_VLA "variable-length"
-%type <strval> vla_ident
+%type <item> vla_ident
%type <uintval> array_length
%type <boolval> varargs
%type <spectype> declspec_simple qualifier_simple
%type <decl> declaration declarators declarator_wrap
%type <decl> parameter
-%type <strval> english_vla
+%type <item> english_vla
%type <declspec> storage_func_specs post_specs
%type <declspec> type_qual_spec type_qual_specs typedef_name_qual
%type <declarator> english_declarator english_array english_function
}
typespec: typespec_noid | typespec_tagged T_IDENT {
- ALLOC_STRUCT($$, struct cdecl_declspec,
- .type = $1,
- .ident = $2);
+ /* Compiler should be able to elide this assignment. */
+ $2->u.declspec.ident = $2->u.declarator.u.ident;
+
+ $$ = &$2->u.declspec;
+ $$->type = $1;
}
declspec_noid: declspec_notype | typespec_noid
vla_ident: T_IDENT | T_ASTERISK {
- ALLOC($$, sizeof "");
- *$$ = 0;
+ if (!($$ = cdecl__alloc_item(1)))
+ YYERROR;
+ *$$->s = 0;
}
array: T_LBRACKET array_length T_RBRACKET {
.type = CDECL_DECL_ARRAY,
.u.array.length = $2);
} | T_LBRACKET vla_ident T_RBRACKET {
- ALLOC_STRUCT($$, struct cdecl_declarator,
- .type = CDECL_DECL_ARRAY,
- .u.array.vla = $2);
+ $$ = &$2->u.declarator;
+ $$->type = CDECL_DECL_ARRAY;
+ $$->u.array.vla = $$->u.ident;
+ $$->u.array.length = 0;
}
parameter: declspecs declarator {
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_NULL);
} | T_IDENT {
- ALLOC_STRUCT($$, struct cdecl_declarator,
- .type = CDECL_DECL_IDENT,
- .u.ident = $1);
+ $$ = &$1->u.declarator;
} | direct_declarator postfix {
$$ = $2;
$$->child = $1;
}
typedef_name_qual: T_IDENT qualifiers {
- ALLOC_STRUCT($$, struct cdecl_declspec,
- .type = CDECL_TYPE_IDENT,
- .ident = $1,
- .next = $2);
+ /* Compiler should be able to elide this assignment. */
+ $1->u.declspec.ident = $1->u.declarator.u.ident;
+
+ $$ = &$1->u.declspec;
+ $$->type = CDECL_TYPE_IDENT;
+ $$->next = $2;
}
null_decl: {
}
english_array: T_VLA T_ARRAY english_vla T_OF {
- ALLOC_STRUCT($$, struct cdecl_declarator,
- .type = CDECL_DECL_ARRAY,
- .u.array.vla = $3);
+ $$ = &$3->u.declarator;
+ $$->type = CDECL_DECL_ARRAY;
+ $$->u.array.vla = $$->u.ident;
+ $$->u.array.length = 0;
} | T_ARRAY array_length T_OF {
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
}
english_vla: T_IDENT | {
- ALLOC($$, sizeof "");
- *$$ = 0;
+ if (!($$ = cdecl__alloc_item(1)))
+ YYERROR;
+ *$$->s = 0;
}
%%