From: Nick Bowler Date: Fri, 12 Mar 2021 04:54:24 +0000 (-0500) Subject: Restructure the type specifier check. X-Git-Tag: v1.1~4 X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/commitdiff_plain/6d601969de9687819347cf27df8ddf6ab026e4b0 Restructure the type specifier check. As the typemap.c functionality is only used for one thing and is quite simple, let's integrate that more closely with the declaration specifier checks. This further reduces the code size of the library somewhat. The old sed line noise is replaced by a similar awk script which should be much easier to adjust as needed. --- diff --git a/Makefile.am b/Makefile.am index 9698512..36bba03 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,11 +23,11 @@ MAINTAINERCLEANFILES = src/scan.c src/scan.h src/scan.stamp \ DISTCLEANFILES = -CLEANFILES = src/validtypes.h src/namespecs.h $(EXTRA_LTLIBRARIES) +CLEANFILES = 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/parse.y src/parse.stamp src/scan.l src/scan.stamp \ + src/types.lst src/specs.lst src/namespecs.sed 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 @@ -41,10 +41,9 @@ noinst_DATA = $(MOFILES) lib_LTLIBRARIES = libcdecl.la libcdecl_la_LDFLAGS = -export-symbols-regex '^cdecl_[[:lower:]]' \ -no-undefined -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/normalize.c src/cdecl-internal.h \ - src/errtab.h +libcdecl_la_SOURCES = src/scan.c src/parse.c src/parse-decl.c src/output.c \ + src/explain.c src/declare.c src/i18n.c src/error.c \ + src/normalize.c src/cdecl-internal.h src/errtab.h libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD) $(libcdecl_la_OBJECTS): $(gnulib_headers) @@ -86,17 +85,11 @@ $(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/parse-decl.lo: src/scan.h src/parse.h src/typemap.h src/error.lo: src/errtab.h src/output.lo: src/namespecs.h test/declgen.lo: test/typegen.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/namespecs.h: $(srcdir)/src/specs.lst $(srcdir)/src/namespecs.sed $(AM_V_GEN) sed -f $(srcdir)/src/namespecs.sed \ < $(srcdir)/src/specs.lst > $@.tmp @@ -210,6 +203,13 @@ src/cmdlist.h: src/gen-cmdlist.awk src/execute.c DISTCLEANFILES += src/cmdlist.h EXTRA_DIST += src/gen-cmdlist.awk +src/typemap.h: src/gen-typemap.awk src/types.lst + $(AM_V_GEN) $(AWK) -f $(srcdir)/src/gen-typemap.awk \ + $(srcdir)/src/types.lst >$@.tmp + $(AM_V_at) mv -f $@.tmp $@ +DISTCLEANFILES += src/typemap.h +EXTRA_DIST += src/gen-typemap.awk + test/typegen.h: test/gen-typegen.awk src/types.lst $(AM_V_GEN) $(AWK) -f $(srcdir)/test/gen-typegen.awk \ $(srcdir)/src/types.lst >$@.tmp diff --git a/src/.gitignore b/src/.gitignore index 8456972..a82fb99 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,10 +1,10 @@ /*.stamp -/parse.[ch] -/scan.[ch] +/cmdlist.h +/commands.h /errtab.h +/execute.c /namespecs.h /options.h -/commands.h -/validtypes.h -/execute.c -/cmdlist.h +/parse.[ch] +/scan.[ch] +/typemap.h diff --git a/src/gen-typemap.awk b/src/gen-typemap.awk new file mode 100755 index 0000000..68cc8a1 --- /dev/null +++ b/src/gen-typemap.awk @@ -0,0 +1,55 @@ +#!/bin/awk -f +# +# Copyright © 2021 Nick Bowler +# +# Generate a test for whether a particuar combination of type specifiers +# represents a valid type, for internal use by the declaration parser. +# +# 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. + +END { + print "/*" + if (FILENAME) { + print " * Automatically generated by gen-typemap.awk from " FILENAME + } else { + print " * Automatically generated by gen-typemap.awk" + } + print " * Do not edit." + print " */" +} + +BEGIN { + OFS = "\n\t | " + count = 0 +} + +$0 ~ /^[abcdefghijklmnopqrstuvwxyz_]/ { + long = 0 + for (i = 1; i <= NF; i++) { + if ($i == "long") { + if (long++) { + $i = "llong" + } + } + + sub(/_/, "", $i) + $i = "1ul<<(CDECL_TYPE_" toupper($i) "-CDECL_SPEC_TYPE)" + } + types[count++] = $0 +} + +END { + print "static inline int typemap_is_valid(unsigned long map)\n{" + print "\tswitch (map) {" + + for (i = 0; i < count; i++) { + print "\tcase " types[i] ":" + } + + print "\t\treturn 1;" + print "\t}\n" + print "\treturn 0;" + print "}" +} diff --git a/src/parse-decl.c b/src/parse-decl.c index 1ad3e8e..f25c4c4 100644 --- a/src/parse-decl.c +++ b/src/parse-decl.c @@ -26,6 +26,62 @@ #include "parse.h" #include "scan.h" +/* + * We can represent type specifiers as a bitmap, which gives us a finite + * list of acceptable bitmap values according to the C standard. However, + * the "long" specifier is allowed to occur more than once, but only at most + * 2 times. Treat it as a special case, assigning an unused bit to represent + * the second long. + */ +#define MAP_LLONG_BIT 31 +#define MAP_LONG_BIT (CDECL_TYPE_LONG-CDECL_SPEC_TYPE) +#define CDECL_TYPE_LLONG (CDECL_SPEC_TYPE+MAP_LLONG_BIT) + +#include "typemap.h" + +/* + * Convert the declaration specifiers to a bitmap with each bit + * corresponding to one specific type specifier. + */ +static int valid_typespec(struct cdecl_declspec *s) +{ + unsigned long map = 0; + + for (struct cdecl_declspec *c = s; c; c = c->next) { + unsigned long bit; + + if (cdecl_spec_kind(c) != CDECL_SPEC_TYPE) + continue; + + bit = c->type - CDECL_SPEC_TYPE; + assert(bit < MAP_LLONG_BIT); + bit = 1ul << bit; + + /* "long" special case */ + if ((map & bit) == 1ul << MAP_LONG_BIT) + bit = 1ul << MAP_LLONG_BIT; + + if (map & bit) { + if (bit == 1ul << MAP_LLONG_BIT) + fprintf(stderr, "too many long specifiers\n"); + else + fprintf(stderr, "duplicate type specifier\n"); + return false; + } + map |= bit; + } + + if (typemap_is_valid(map)) + return true; + + if (map == 0) + fprintf(stderr, "no type specified\n"); + else + fprintf(stderr, "invalid type specified\n"); + + return false; +} + /* * Verify the declaration specifiers of a declaration. If top is true, treat * this as a top-level declaration. Otherwise, treat this as a function @@ -37,10 +93,8 @@ static bool valid_declspecs(struct cdecl *decl, bool top) struct cdecl_declarator *d = decl->declarators; bool abstract = cdecl_is_abstract(d); unsigned num_storage = 0; - unsigned long typemap; - typemap = cdecl__build_typemap(specs); - if (typemap == -1) + if (!valid_typespec(specs)) return false; for (struct cdecl_declspec *c = specs; c; c = c->next) { diff --git a/src/typemap.c b/src/typemap.c deleted file mode 100644 index 918a037..0000000 --- a/src/typemap.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Helpers for dealing with type specifiers. - * Copyright © 2011, 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 . - */ - -#include -#include -#include -#include - -#include "cdecl.h" -#include "cdecl-internal.h" - -/* - * We can represent type specifiers as a bitmap, which gives us a finite - * list of acceptable bitmap values according to the C standard. However, - * the "long" specifier is allowed to occur more than once, but only at most - * 2 times. Treat it as a special case, assigning an unused bit to represent - * the second long. - */ -#define CDECL_TYPE_LLONG (CDECL_SPEC_TYPE + 31) - -static inline unsigned long spec_bit(unsigned type) -{ - return 1ul << (type & (CDECL_SPEC_TYPE - 1)); -} - -static unsigned long add_typespec(unsigned long map, struct cdecl_declspec *s) -{ - assert(s->type >= CDECL_SPEC_TYPE && s->type < CDECL_TYPE_LLONG); - - if (s->type == CDECL_TYPE_LONG) { - if (map & spec_bit(CDECL_TYPE_LLONG)) { - fprintf(stderr, "too many long specifiers\n"); - return -1; - } else if (map & spec_bit(CDECL_TYPE_LONG)) { - return map | spec_bit(CDECL_TYPE_LLONG); - } - } - - if (map & spec_bit(s->type)) { - fprintf(stderr, "duplicate type specifier\n"); - return -1; - } - - return map | spec_bit(s->type); -} - -unsigned long cdecl__build_typemap(struct cdecl_declspec *s) -{ - unsigned long map = 0; - - for (struct cdecl_declspec *c = s; c; c = c->next) { - if (cdecl_spec_kind(c) != CDECL_SPEC_TYPE) - continue; - - map = add_typespec(map, c); - if (map == -1) - return -1; - } - - switch (map) { - case 0: - fprintf(stderr, "no type specifiers\n"); - return -1; -# include "validtypes.h" - return map; - default: - fprintf(stderr, "conflicting type specifiers\n"); - return -1; - } -} diff --git a/src/validtypes.sed b/src/validtypes.sed deleted file mode 100644 index 7052915..0000000 --- a/src/validtypes.sed +++ /dev/null @@ -1,10 +0,0 @@ -1i\ -/* This file is automatically generated by validtypes.sed */ -/[^_[:alpha:][:space:]]/b -s/[[:space:]][[:space:]]*/ /g -s/_//g -y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ -s/LONG LONG/LLONG LONG/ -s/[[:upper:]][[:upper:]]*/(1ul<<(CDECL_TYPE_&-CDECL_SPEC_TYPE))/g -s/ /|/g -s/.*/case &:/