MAINTAINERCLEANFILES = src/scan.c src/scan.h src/scan.stamp \
src/parse.c src/parse.h src/parse.stamp
-CLEANFILES = src/typenames.h src/validtypes.h src/errtab.h
+CLEANFILES = src/validtypes.h src/errtab.h
-EXTRA_DIST = m4/gnulib-cache.m4 src/types.lst src/typenames.sed \
- src/validtypes.sed src/errors.lst src/strtab.sed \
+EXTRA_DIST = m4/gnulib-cache.m4 src/types.lst src/validtypes.sed \
+ src/errors.lst src/strtab.sed \
src/parse.stamp src/scan.stamp \
COPYING.WTFPL2
libcdecl_la_LDFLAGS = -no-undefined \
-export-symbols-regex '^cdecl_[[:lower:]]'
libcdecl_la_SOURCES = src/scan.c src/parse.c src/parse-decl.c src/typemap.c \
- src/output.c src/explain.c src/declare.c src/i18n.c src/error.c
+ src/output.c src/explain.c src/declare.c src/i18n.c src/error.c \
+ src/normalize.c
libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD)
$(libcdecl_la_OBJECTS): $(gnulib_headers)
src/parse.lo: src/scan.h
src/scan.lo: src/parse.h
src/parse-decl.lo: src/scan.h src/parse.h
-src/typemap.lo: src/validtypes.h src/typenames.h
+src/typemap.lo: src/validtypes.h
src/error.lo: src/errtab.h
+src/normalize.lo: src/ordspecs.h
src/validtypes.h: $(srcdir)/src/types.lst $(srcdir)/src/validtypes.sed
$(AM_V_GEN)sed -f $(srcdir)/src/validtypes.sed \
< $(srcdir)/src/types.lst > $@.tmp
$(AM_V_at)mv -f $@.tmp $@
-src/typenames.h: $(srcdir)/src/types.lst $(srcdir)/src/typenames.sed
- $(AM_V_GEN)sed -f $(srcdir)/src/typenames.sed \
- < $(srcdir)/src/types.lst > $@.tmp
- $(AM_V_at)mv -f $@.tmp $@
+src/ordspecs.h: $(srcdir)/src/specs.lst $(srcdir)/src/ordspecs.sed
+ $(AM_V_GEN) sed -f $(srcdir)/src/ordspecs.sed \
+ < $(srcdir)/src/specs.lst > $@.tmp
+ $(AM_V_at) mv -f $@.tmp $@
src/errtab.h: $(srcdir)/src/errors.lst $(srcdir)/src/strtab.sed
$(AM_V_GEN)sed -f $(srcdir)/src/strtab.sed \
scan.stamp
scan.[ch]
validtypes.h
-typenames.h
+ordspecs.h
errtab.h
else
ret += cdecl__advance_(&buf, &n, rc);
- rc = cdecl__explain_qualifiers(buf, n, p->qualifiers);
+ rc = cdecl__explain_specs(buf, n, p->qualifiers, CDECL_SPEC_QUAL);
return ret + cdecl__advance(&buf, &n, rc);
}
{
size_t ret = 0, rc;
- rc = cdecl__explain_qualifiers(buf, n, p->qualifiers);
+ rc = cdecl__explain_specs(buf, n, p->qualifiers, CDECL_SPEC_QUAL);
ret += cdecl__advance(&buf, &n, rc);
return ret + snprintf(buf, n, "pointer to");
--- /dev/null
+/*
+ * Normalize C declaration specifier lists.
+ * Copyright © 2011 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <config.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "cdecl.h"
+#include "normalize.h"
+
+/*
+ * Totally order the declaration specifier types by defining an injection into
+ * the natural numbers.
+ */
+static unsigned order_spec(struct cdecl_declspec *spec)
+{
+ switch (spec->type) {
+# include "ordspecs.h"
+ default:
+ assert(0);
+ }
+}
+
+static int compare_specs(struct cdecl_declspec *a, struct cdecl_declspec *b)
+{
+ return order_spec(a) - order_spec(b);
+}
+
+/*
+ * Merge two sorted specifier lists. Each list must have at least one element.
+ */
+static struct cdecl_declspec *
+merge_specs(struct cdecl_declspec *l1, size_t n1,
+ struct cdecl_declspec *l2, size_t n2)
+{
+ struct cdecl_declspec *top = l1, **p = ⊤
+
+ assert(n1 > 0 && n2 > 0);
+
+ for (size_t i = 0, j = 0; i < n1 && j < n2;) {
+ int ord = compare_specs(l1, l2);
+ struct cdecl_declspec *tmp;
+
+ if (ord <= 0) {
+ p = &l1->next;
+ l1 = l1->next;
+ i++;
+ } else if (ord > 0) {
+ tmp = l2->next;
+ l2->next = l1;
+ *p = l2;
+ p = &l2->next;
+ l2 = tmp;
+
+ j++;
+ }
+
+ if (!l1)
+ *p = l2;
+ }
+
+ return top;
+}
+
+
+/*
+ * A simple merge sort. Set n to the number of elements in the list, or 0
+ * to have the sort function count them.
+ */
+static struct cdecl_declspec *sort_specs(struct cdecl_declspec *l1, size_t n)
+{
+ struct cdecl_declspec *l2, *c;
+ size_t i;
+
+ /* Find the midpoint of the list. */
+ if (n > 1) {
+ for (i = 0, l2 = l1; i < (n+1)/2; i++)
+ l2 = l2->next;
+ } else if (n == 0) {
+ for (i = 0, c = l2 = l1; c; i++, c = c->next) {
+ if (i % 2 == 0)
+ l2 = l2->next;
+ }
+
+ n = i;
+ }
+
+ assert(n != 0);
+ if (n == 1) {
+ l1->next = NULL;
+ return l1;
+ }
+
+ /* l1 always has the longer "half". */
+ l1 = sort_specs(l1, (n+1)/2);
+ l2 = sort_specs(l2, n/2);
+
+ return merge_specs(l1, (n+1)/2, l2, n/2);
+}
+
+/*
+ * C allows declaration specifiers to appear in any order, and allows arbitrary
+ * repetition of some kinds of specifiers. Normalize the specifier lists by
+ * sorting the specifiers and removing repeated function specifiers and type
+ * qualifiers.
+ */
+struct cdecl_declspec *cdecl__normalize_specs(struct cdecl_declspec *specs)
+{
+ struct cdecl_declspec *c, *l;
+
+ if (!specs)
+ return NULL;
+
+ specs = sort_specs(specs, 0);
+
+ for (c = specs, l = NULL; c; l = c, c = c->next) {
+ switch (cdecl_spec_kind(c)) {
+ case CDECL_SPEC_QUAL:
+ case CDECL_SPEC_FUNC:
+ if (l && l->type == c->type) {
+ l->next = c->next;
+ free(c);
+ c = l;
+ }
+ }
+ }
+
+ return specs;
+}
--- /dev/null
+#ifndef CDECL_NORMALIZE_H_
+#define CDECL_NORMALIZE_H_
+
+struct cdecl_declspec;
+struct cdecl_declspec *cdecl__normalize_specs(struct cdecl_declspec *specs);
+
+#endif
--- /dev/null
+1i\
+/*\
+ * Copyright © 2011 Nick Bowler\
+ *\
+ * License WTFPL2: Do What The Fuck You Want To Public License, version 2.\
+ * This is free software: you are free to do what the fuck you want to.\
+ * There is NO WARRANTY, to the extent permitted by law.\
+ */
+1i\
+/*\
+ * This file is automatically generated by ordspecs.sed.\
+ */
+/[^_[:alpha:][:space:]]/b
+s/[[:space:]][[:space:]]*/ /g
+s/_//g
+y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
+s/\([[:upper:]]*\) \([[:upper:]]*\)/case CDECL_\1_\2:/
+p
+i\
+ return
+=
+a\
+ ;
+d
return ret + cdecl__advance_(buf, n, rc);
}
-static size_t explain_storage(char *buf, size_t n, unsigned spec)
+static size_t explain_spec(char *buf, size_t n, struct cdecl_declspec *s)
{
- switch (spec) {
+ switch (s->type) {
+ /* Function specifiers */
+ case CDECL_FUNC_INLINE:
+ return snprintf(buf, n, "inline");
+ /* Storage-class specifiers */
case CDECL_STOR_TYPEDEF:
return snprintf(buf, n, "typedef");
case CDECL_STOR_EXTERN:
return snprintf(buf, n, "auto");
case CDECL_STOR_REGISTER:
return snprintf(buf, n, "register");
+ /* Type qualifiers */
+ case CDECL_QUAL_RESTRICT:
+ return snprintf(buf, n, "restrict");
+ case CDECL_QUAL_VOLATILE:
+ return snprintf(buf, n, "volatile");
+ case CDECL_QUAL_CONST:
+ return snprintf(buf, n, "const");
+ /* Type specifiers */
+ case CDECL_TYPE_VOID:
+ return snprintf(buf, n, "void");
+ case CDECL_TYPE_CHAR:
+ return snprintf(buf, n, "char");
+ case CDECL_TYPE_SHORT:
+ return snprintf(buf, n, "short");
+ case CDECL_TYPE_INT:
+ return snprintf(buf, n, "int");
+ case CDECL_TYPE_LONG:
+ return snprintf(buf, n, "long");
+ case CDECL_TYPE_FLOAT:
+ return snprintf(buf, n, "float");
+ case CDECL_TYPE_DOUBLE:
+ return snprintf(buf, n, "double");
+ case CDECL_TYPE_SIGNED:
+ return snprintf(buf, n, "signed");
+ case CDECL_TYPE_UNSIGNED:
+ return snprintf(buf, n, "unsigned");
+ case CDECL_TYPE_BOOL:
+ return snprintf(buf, n, "_Bool");
+ case CDECL_TYPE_COMPLEX:
+ return snprintf(buf, n, "_Complex");
+ case CDECL_TYPE_IMAGINARY:
+ return snprintf(buf, n, "_Imaginary");
+ case CDECL_TYPE_STRUCT:
+ return snprintf(buf, n, "struct %s", s->ident);
+ case CDECL_TYPE_UNION:
+ return snprintf(buf, n, "union %s", s->ident);
+ case CDECL_TYPE_ENUM:
+ return snprintf(buf, n, "enum %s", s->ident);
+ case CDECL_TYPE_IDENT:
+ return snprintf(buf, n, "%s", s->ident);
default:
assert(0);
}
}
-/* Renders the storage-class and function specifiers in canonical form. */
-size_t cdecl__explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s)
+/*
+ * Render a list of declaration specifiers. Only the declaration specifiers
+ * listed in mask, which is the bitwise OR of the desired specifier kinds, are
+ * printed.
+ */
+size_t cdecl__explain_specs(char *buf, size_t n, struct cdecl_declspec *s,
+ unsigned mask)
{
- unsigned long funcmap = 0;
size_t ret = 0, rc = 0;
for (struct cdecl_declspec *c = s; c; c = c->next) {
- switch (cdecl_spec_kind(c)) {
+ switch (cdecl_spec_kind(c) & mask) {
case CDECL_SPEC_FUNC:
- funcmap |= 1ul << (c->type & 0xff);
- break;
case CDECL_SPEC_STOR:
- /* Valid C declarations have at most one
- * storage-class specifier. */
- rc = explain_storage(buf, n, c->type);
- break;
+ case CDECL_SPEC_QUAL:
+ case CDECL_SPEC_TYPE:
+ ret += cdecl__advance(&buf, &n, rc);
+ rc = explain_spec(buf, n, c);
}
}
- if (funcmap & (1ul << (CDECL_FUNC_INLINE & 0xff))) {
- ret += cdecl__advance(&buf, &n, rc);
- rc = snprintf(buf, n, "inline");
- }
-
return ret + rc;
}
-size_t cdecl__explain_qualifiers(char *buf, size_t n, struct cdecl_declspec *s)
+/* Renders the storage-class and function specifiers in canonical form. */
+size_t cdecl__explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s)
{
- unsigned long qualmap = 0;
- size_t ret = 0, rc = 0;
-
- for (struct cdecl_declspec *c = s; c; c = c->next) {
- if (cdecl_spec_kind(c) != CDECL_SPEC_QUAL)
- continue;
- qualmap |= 1ul << (c->type & 0xff);
- }
-
- if (qualmap & (1ul << (CDECL_QUAL_RESTRICT & 0xff))) {
- ret += cdecl__advance(&buf, &n, rc);
- rc = snprintf(buf, n, "restrict");
- }
- if (qualmap & (1ul << (CDECL_QUAL_VOLATILE & 0xff))) {
- ret += cdecl__advance(&buf, &n, rc);
- rc = snprintf(buf, n, "volatile");
- }
- if (qualmap & (1ul << (CDECL_QUAL_CONST & 0xff))) {
- ret += cdecl__advance(&buf, &n, rc);
- rc = snprintf(buf, n, "const");
- }
-
- return ret + rc;
+ return cdecl__explain_specs(buf, n, s, CDECL_SPEC_FUNC|CDECL_SPEC_STOR);
}
/* Renders the type qualifiers and type specifiers in canonical form. */
size_t cdecl__explain_post_specs(char *buf, size_t n, struct cdecl_declspec *s)
{
- const char *tag = NULL;
- unsigned long typemap;
- size_t ret = 0, rc;
-
- typemap = cdecl__build_typemap(s);
- if (typemap == -1)
- return 0;
-
- for (struct cdecl_declspec *c = s; c; c = c->next) {
- if (cdecl_spec_kind(c) != CDECL_SPEC_TYPE)
- continue;
-
- /* Valid C types have at most one identifier. */
- if (c->ident)
- tag = c->ident;
- }
-
- rc = cdecl__explain_qualifiers(buf, n, s);
- ret += cdecl__advance(&buf, &n, rc);
-
- rc = snprintf(buf, n, "%s", cdecl__explain_typemap(typemap));
- if (tag) {
- ret += cdecl__advance(&buf, &n, rc);
- rc = snprintf(buf, n, "%s", tag);
- }
-
- return ret + rc;
+ return cdecl__explain_specs(buf, n, s, CDECL_SPEC_QUAL|CDECL_SPEC_TYPE);
}
size_t cdecl__advance_(char **buf, size_t *n, size_t amount);
size_t cdecl__advance(char **buf, size_t *n, size_t amount);
+size_t cdecl__explain_specs(char *buf, size_t n, struct cdecl_declspec *s,
+ unsigned mask);
size_t cdecl__explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s);
-size_t cdecl__explain_qualifiers(char *buf, size_t n, struct cdecl_declspec *s);
size_t cdecl__explain_post_specs(char *buf, size_t n, struct cdecl_declspec *s);
#endif
#include "parse.h"
#include "scan.h"
#include "i18n.h"
+#include "normalize.h"
/*
* Determine if a declarator declares an identifier (other than a function
return 0;
}
+static int
+normalize_specs(struct cdecl_declarator **p, struct cdecl_declarator *d)
+{
+ struct cdecl_function *func;
+ struct cdecl_pointer *ptr;
+
+ 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;
+ }
+
+ return 0;
+}
+
/*
* Traverse the parse tree, calling a function on every declarator in a
* depth-first preorder traversal. The function is given a pointer to the
struct cdecl *cdecl_parse_decl(const char *declstr)
{
+ struct cdecl_declspec *norm_specs;
YY_BUFFER_STATE state;
yyscan_t scanner;
struct cdecl *decl;
if (rc != 0)
return NULL;
+ /*
+ * Since the top-level specifiers are shared between each top-level
+ * declarator, we need to normalize them once and then propagate the
+ * new specifier list.
+ */
+ norm_specs = cdecl__normalize_specs(decl->specifiers);
+
for (struct cdecl *i = decl; i; i = i->next) {
+ i->specifiers = norm_specs;
+
if (!forall_declarators(i, reduce_parentheses))
goto err;
if (!forall_declarators(i, simplify_functions))
goto err;
if (!forall_declarators(i, check_arrays))
goto err;
+ if (!forall_declarators(i, normalize_specs))
+ goto err;
if (!valid_declspecs(i, true))
goto err;
return NULL;
for (struct cdecl *i = decl; i; i = i->next) {
+ i->specifiers = cdecl__normalize_specs(i->specifiers);
+
if (!forall_declarators(i, check_parameters))
goto err;
if (!forall_declarators(i, check_rettypes))
goto err;
if (!forall_declarators(i, check_arrays))
goto err;
+ if (!forall_declarators(i, normalize_specs))
+ goto err;
if (!valid_declspecs(i, true))
goto err;
--- /dev/null
+/* The complete list of declaration specifiers, in desired output order. */
+stor typedef
+stor extern
+stor static
+stor auto
+stor register
+func inline
+qual restrict
+qual volatile
+qual const
+type void
+type signed
+type unsigned
+type char
+type short
+type long
+type int
+type float
+type double
+type _Complex
+type _Imaginary
+type _Bool
+type struct
+type union
+type enum
+type ident
return -1;
}
}
-
-const char *cdecl__explain_typemap(unsigned long map)
-{
- switch (map) {
-# include "typenames.h"
- default:
- fprintf(stderr, "invalid type specifiers\n");
- return NULL;
- }
-}
#include "cdecl.h"
unsigned long cdecl__build_typemap(struct cdecl_declspec *s);
-const char *cdecl__explain_typemap(unsigned long map);
#endif
+++ /dev/null
-1i\
-/* This file is automatically generated by typenames.sed */
-/[^_[:alpha:][:space:]]/b
-s/[[:space:]][[:space:]]*/ /g
-h
-s/_//g
-y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
-s/LONG LONG/LLONG LONG/
-s/[[:upper:]][[:upper:]]*/(1ul<<(CDECL_TYPE_&-CDECL_SPEC_TYPE))/g
-s/ /|/g
-s/.*/case &:/
-p
-x
-s/ident//
-s/.*/ return "&";/