]> git.draconx.ca Git - cdecl99.git/commitdiff
Hand-code the normalized specifier ordering.
authorNick Bowler <nbowler@draconx.ca>
Fri, 12 Mar 2021 04:58:52 +0000 (23:58 -0500)
committerNick Bowler <nbowler@draconx.ca>
Fri, 12 Mar 2021 04:58:52 +0000 (23:58 -0500)
For the most part, the enumerated values for specifier types in the
library are already in an acceptable order for normalization.  This
list is part of the API and is expected to be stable.

We can achieve the desired ordering by simply tweaking these values
as needed.  There is also no need to totally order the specifiers
because some combinations are never valid.  This change not only
eliminates a pile of comparatively complex code generation, but
also reduces the overall size of the library somewhat.

Add a new testcase to validate the resulting ordering.

Makefile.am
src/.gitignore
src/cdecl.h
src/normalize.c
src/ordspecs.sed [deleted file]
test/.gitignore
test/normalize.c [new file with mode: 0644]
test/testlib.c
tests/internal.at [new file with mode: 0644]
testsuite.at

index 82a6c0dfb3b6ff8400d29fd06efb58d36b608934..9698512190b33e6f42221ea86b9675fda4f725a0 100644 (file)
@@ -23,13 +23,12 @@ MAINTAINERCLEANFILES = src/scan.c src/scan.h src/scan.stamp \
 
 DISTCLEANFILES =
 
-CLEANFILES = src/validtypes.h src/namespecs.h src/ordspecs.h \
-             $(EXTRA_LTLIBRARIES)
+CLEANFILES = src/validtypes.h src/namespecs.h $(EXTRA_LTLIBRARIES)
 
 EXTRA_DIST = bootstrap $(DX_BASEDIR)/scripts/fix-gnulib.pl m4/gnulib-cache.m4 \
              src/types.lst src/validtypes.sed src/specs.lst src/namespecs.sed \
-             src/ordspecs.sed src/parse.y src/parse.stamp src/scan.l \
-             src/scan.stamp COPYING.WTFPL2 README.md INSTALL
+             src/parse.y src/parse.stamp src/scan.l src/scan.stamp \
+             COPYING.WTFPL2 README.md INSTALL
 
 dist_man_MANS = doc/cdecl99.1 doc/libcdecl.3
 
@@ -66,7 +65,7 @@ libmain_a_SOURCES = src/cdecl99.c src/options.h
 $(libmain_a_OBJECTS): $(gnulib_headers)
 $(libmain_a_OBJECTS): src/options.h
 
-check_PROGRAMS = test/crossparse
+check_PROGRAMS = test/crossparse test/normalize
 check_LTLIBRARIES = libtest.la
 libtest_la_LIBADD = $(GSL_LIBS)
 libtest_la_SOURCES = test/testlib.c
@@ -81,13 +80,15 @@ test_crossparse_LDADD = libtest.la libcdecl.la libgnu.la
 $(test_crossparse_OBJECTS): $(gnulib_headers)
 test_randomdecl_LDADD = libtest.la libcdecl.la libgnu.la
 $(test_randomdecl_OBJECTS): $(gnulib_headers)
+test_normalize_LDADD = libtest.la src/output.lo src/normalize.lo \
+                       libcdecl.la libgnu.la
+$(test_normalize_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/error.lo: src/errtab.h
-src/normalize.lo: src/ordspecs.h
 src/output.lo: src/namespecs.h
 test/declgen.lo: test/typegen.h
 
@@ -101,11 +102,6 @@ src/namespecs.h: $(srcdir)/src/specs.lst $(srcdir)/src/namespecs.sed
                < $(srcdir)/src/specs.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 $@
-
 # Supporting rules for gettext.
 include $(top_srcdir)/common/snippet/gettext.mk
 
index 9428cc9c63ce457550253add9e0b6963bff38e25..8456972beac7c3def8036fb6ed72a440be435341 100644 (file)
@@ -5,7 +5,6 @@
 /namespecs.h
 /options.h
 /commands.h
-/ordspecs.h
 /validtypes.h
 /execute.c
 /cmdlist.h
index 695024f5e8d63b9b9ada57e3b2db4b3170b89770..11e0099ee28f5c47c3905a9413043b95e50116f3 100644 (file)
@@ -102,12 +102,12 @@ void cdecl_free(struct cdecl *decl);
 size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl);
 size_t cdecl_declare(char *buf, size_t n, struct cdecl *decl);
 
-static inline int cdecl_spec_kind(struct cdecl_declspec *spec)
+static inline int cdecl_spec_kind(const struct cdecl_declspec *spec)
 {
        return spec->type & ~(CDECL_SPEC_TYPE-1u);
 }
 
-static inline _Bool cdecl_is_abstract(struct cdecl_declarator *d)
+static inline _Bool cdecl_is_abstract(const struct cdecl_declarator *d)
 {
        while (d->child)
                d = d->child;
index e6502d2d951123172ee691b9a35e3add8bb2f339..eac71f3ae12ba6c25c8e67cb7e44349bb8da32e8 100644 (file)
 #include "cdecl-internal.h"
 
 /*
- * Totally order the declaration specifier types by defining an injection into
- * the natural numbers.
+ * Partially order the declaration specifiers by assigning each specifier
+ * type an appropriate integer value.  These values need not be unique for
+ * specifiers that never appear together.
+ *
+ * This implementation relies implicitly on the specific enumeration values.
+ * These values are expected to be stable as they are exposed library API.
  */
-static unsigned order_spec(struct cdecl_declspec *spec)
+static int order_spec(const struct cdecl_declspec *spec)
 {
-       switch (spec->type) {
-#      include "ordspecs.h"
-       default:
-               assert(0);
+       unsigned kind = cdecl_spec_kind(spec);
+       unsigned ret = spec->type;
+
+       /* General sequence: storage, function, qualifier, type */
+       switch (kind) {
+       case CDECL_SPEC_QUAL:
+               ret |= CDECL_SPEC_FUNC;
+               break;
+       case CDECL_SPEC_TYPE:
+               /* Manually reorder some elements that are in the wrong place */
+               switch (ret) {
+               case CDECL_TYPE_INT:
+                       ret += 2; /* OK because "float int" is invalid. */
+                       break;
+               case CDECL_TYPE_SIGNED:
+               case CDECL_TYPE_UNSIGNED:
+                       ret = CDECL_TYPE_VOID;
+                       break;
+               }
+               ret |= CDECL_SPEC_FUNC|CDECL_SPEC_QUAL;
        }
+
+       return ret;
 }
 
-static int compare_specs(struct cdecl_declspec *a, struct cdecl_declspec *b)
+static int compare_specs(const struct cdecl_declspec *a,
+                         const struct cdecl_declspec *b)
 {
        return order_spec(a) - order_spec(b);
 }
diff --git a/src/ordspecs.sed b/src/ordspecs.sed
deleted file mode 100644 (file)
index 7eb559c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-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
index e8d2dda7225a0c00743180c9744b1594ffd325a1..3825ec68b72fe87da10c9e613864846913fed0e2 100644 (file)
@@ -1,3 +1,4 @@
-typegen.h
-randomdecl
-crossparse
+/typegen.h
+/randomdecl
+/crossparse
+/normalize
diff --git a/test/normalize.c b/test/normalize.c
new file mode 100644 (file)
index 0000000..b535973
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Helper application to test normalization of declaration specifiers.
+ * Copyright © 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "cdecl.h"
+#include "cdecl-internal.h"
+#include "test.h"
+
+#define PROGNAME "normalize"
+static const char *progname = PROGNAME;
+static const char sopts[] = "f:VH";
+static const struct option lopts[] = {
+       { "file", 1, NULL, 'f' },
+       { "version", 0, NULL, 'V' },
+       { "help", 0, NULL, 'H' },
+       { 0 }
+};
+
+static void print_usage(FILE *f)
+{
+       fprintf(f, "Usage: %s [options]\n", progname);
+}
+
+static void print_help(void)
+{
+       const struct option *opt;
+
+       print_usage(stdout);
+       puts("Test normalization of declaration specifiers.\n");
+
+       puts("Options:");
+       for (opt = lopts; opt->val; opt++) {
+               int w = print_option_start(opt, NULL);
+
+               if (w)
+                       putchar('\n');
+       }
+}
+
+static unsigned to_spectype(const char *tok)
+{
+       if (!strcmp(tok, "typedef")) return CDECL_STOR_TYPEDEF;
+       if (!strcmp(tok, "extern")) return CDECL_STOR_EXTERN;
+       if (!strcmp(tok, "static")) return CDECL_STOR_STATIC;
+       if (!strcmp(tok, "auto")) return CDECL_STOR_AUTO;
+       if (!strcmp(tok, "register")) return CDECL_STOR_REGISTER;
+       if (!strcmp(tok, "inline")) return CDECL_FUNC_INLINE;
+       if (!strcmp(tok, "restrict")) return CDECL_QUAL_RESTRICT;
+       if (!strcmp(tok, "volatile")) return CDECL_QUAL_VOLATILE;
+       if (!strcmp(tok, "const")) return CDECL_QUAL_CONST;
+       if (!strcmp(tok, "void")) return CDECL_TYPE_VOID;
+       if (!strcmp(tok, "signed")) return CDECL_TYPE_SIGNED;
+       if (!strcmp(tok, "unsigned")) return CDECL_TYPE_UNSIGNED;
+       if (!strcmp(tok, "char")) return CDECL_TYPE_CHAR;
+       if (!strcmp(tok, "short")) return CDECL_TYPE_SHORT;
+       if (!strcmp(tok, "long")) return CDECL_TYPE_LONG;
+       if (!strcmp(tok, "int")) return CDECL_TYPE_INT;
+       if (!strcmp(tok, "float")) return CDECL_TYPE_FLOAT;
+       if (!strcmp(tok, "double")) return CDECL_TYPE_DOUBLE;
+       if (!strcmp(tok, "_Complex")) return CDECL_TYPE_COMPLEX;
+       if (!strcmp(tok, "_Imaginary")) return CDECL_TYPE_IMAGINARY;
+       if (!strcmp(tok, "_Bool")) return CDECL_TYPE_BOOL;
+       if (!strcmp(tok, "struct")) return CDECL_TYPE_STRUCT;
+       if (!strcmp(tok, "union")) return CDECL_TYPE_UNION;
+       if (!strcmp(tok, "enum")) return CDECL_TYPE_ENUM;
+
+       return CDECL_TYPE_IDENT;
+}
+
+int do_normalize(char *line, size_t n)
+{
+       struct cdecl_declspec *specs = NULL, **newspec = &specs;
+       char *tok = NULL;
+
+       while ((tok = strtok(tok ? NULL : line, " "))) {
+               struct cdecl_declspec *spec = malloc_nofail(sizeof *spec);
+
+               spec->next = NULL;
+               spec->ident = NULL;
+               spec->type = to_spectype(tok);
+               switch (spec->type) {
+               case CDECL_TYPE_STRUCT:
+               case CDECL_TYPE_UNION:
+               case CDECL_TYPE_ENUM:
+                       spec->ident = strtok(NULL, " ");
+                       break;
+               case CDECL_TYPE_IDENT:
+                       spec->ident = tok;
+               }
+
+               *newspec = spec;
+               newspec = &spec->next;
+       }
+
+       line = malloc_nofail(n);
+       line[0] = 0;
+       if (specs) {
+               specs = cdecl__normalize_specs(specs);
+               cdecl__explain_specs(line, n, specs, -1);
+       }
+       printf("%s\n", line);
+       free(line);
+
+       while (specs) {
+               struct cdecl_declspec *c = specs;
+               specs = specs->next;
+               free(c);
+       }
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int opt, ret = EXIT_SUCCESS;
+       char *filename = NULL, *line = NULL;
+       FILE *infile = stdin;
+       size_t n = 0;
+       ssize_t rc;
+
+       while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
+               switch (opt) {
+               case 'f':
+                       filename = optarg;
+                       break;
+               case 'V':
+                       test_print_version(PROGNAME);
+                       return EXIT_SUCCESS;
+               case 'H':
+                       print_help();
+                       return EXIT_SUCCESS;
+               default:
+                       print_usage(stderr);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (filename) {
+               infile = fopen(filename, "r");
+               if (!infile) {
+                       perror(filename);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       while ((rc = getline(&line, &n, infile)) >= 0) {
+               if (rc > 0 && line[rc-1] == '\n')
+                       line[rc-1] = 0;
+               if (do_normalize(line, n) < 0)
+                       ret = EXIT_FAILURE;
+       }
+
+       free(line);
+       return ret;
+}
index 2c859a9e79a8d3ed9d6eeac9bdb02c3b929f8702..7471ece57e11ccf33ecd2408e7be0e25f829815b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Miscellaneous functions used by the cdecl99 test suite.
- *  Copyright © 2011 Nick Bowler
+ *  Copyright © 2011-2012, 2021 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
@@ -89,7 +89,7 @@ bool strict_strtoul(unsigned long *val, const char *str, int base)
 void test_print_version(const char *program)
 {
        printf("%s (%s) %s\n", program, PACKAGE_NAME, PACKAGE_VERSION);
-       puts("Copyright (C) 2011 Nick Bowler.");
+       puts("Copyright (C) 2021 Nick Bowler.");
        puts("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.");
        puts("This is free software: you are free to change and redistribute it.");
        puts("There is NO WARRANTY, to the extent permitted by law.");
diff --git a/tests/internal.at b/tests/internal.at
new file mode 100644 (file)
index 0000000..e9fdcf4
--- /dev/null
@@ -0,0 +1,71 @@
+# Copyright © 2021 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 <https://www.gnu.org/licenses/>.
+
+AT_BANNER([Internal library behaviour])
+
+AT_SETUP([cdecl__normalize_specs])
+
+TEST_NEED_PROGRAM([normalize])
+
+# Note that not all combinations of specifiers make a specifier list that can
+# appear in a valid C declaration.  We don't want to test normalization of
+# invalid specifier lists: each line in the below input file is a specifier
+# list that is possible to use.
+AT_DATA([input],
+[[int long signed long
+long int long unsigned
+short signed int
+int short unsigned
+char signed
+char unsigned
+_Complex float
+_Imaginary float
+_Imaginary double long
+long _Complex double
+
+const struct foo volatile restrict restrict const volatile const restrict
+volatile const const restrict union bar restrict volatile volatile const
+const const volatile volatile restrict restrict volatile enum baz volatile
+
+int signed long const volatile inline static
+short inline int extern volatile unsigned const
+_Complex volatile double const long typedef
+long const _Imaginary double register volatile
+volatile _Bool const auto
+]])
+
+AT_CHECK([normalize -f input], [0], [[signed long long int
+unsigned long long int
+signed short int
+unsigned short int
+signed char
+unsigned char
+float _Complex
+float _Imaginary
+long double _Imaginary
+long double _Complex
+
+restrict volatile const struct foo
+restrict volatile const union bar
+restrict volatile const enum baz
+
+static inline volatile const signed long int
+extern inline volatile const unsigned short int
+typedef volatile const long double _Complex
+register volatile const long double _Imaginary
+auto volatile const _Bool
+]])
+
+AT_CLEANUP
index 2fb25be017b84e7f0a5a3391e3c22d2c14f2c2be..1e6796580fa2a7c8325fbfba6edbf5cdf423f8c6 100644 (file)
@@ -45,4 +45,5 @@ exit 77])])
 m4_include([tests/general.at])
 m4_include([tests/decl-good.at])
 m4_include([tests/decl-bad.at])
+m4_include([tests/internal.at])
 m4_include([tests/stress.at])