The first of (hopefully) many components of a test suite.
MAINTAINERCLEANFILES = src/scan.c src/scan.h src/scan.stamp \
src/parse.c src/parse.h src/parse.stamp
-CLEANFILES = src/validtypes.h src/errtab.h
+CLEANFILES = src/validtypes.h src/errtab.h test/typegen.h
EXTRA_DIST = m4/gnulib-cache.m4 src/types.lst src/validtypes.sed \
src/errors.lst src/strtab.sed \
$(libcdecl_la_OBJECTS): $(gnulib_headers)
bin_PROGRAMS = cdecl99
+
+check_PROGRAMS =
+EXTRA_LTLIBRARIES = libtest.la
+libtest_la_CFLAGS = $(AM_CFLAGS) $(GSL_CFLAGS)
+libtest_la_LIBADD = $(GSL_LIBS)
+libtest_la_SOURCES = test/testlib.c
+
+if HAVE_GSL
+libtest_la_SOURCES += test/declgen.c
+check_PROGRAMS += test/randomdecl
+endif
+
cdecl99_SOURCES = src/cdecl99.c
cdecl99_LDADD = libcdecl.la libgnu.la $(LTLIBINTL) $(LTLIBREADLINE)
$(cdecl99_OBJECTS): $(gnulib_headers)
+test_randomdecl_LDADD = libcdecl.la libtest.la libgnu.la
+$(test_randomdecl_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/normalize.lo: src/ordspecs.h
src/output.lo: src/namespecs.h
+# This is gross.
+declgen_o = test/libtest_la-declgen.lo
+$(declgen_o): 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
< $(srcdir)/src/errors.lst > $@.tmp
$(AM_V_at)mv -f $@.tmp $@
+test/typegen.h: $(srcdir)/src/types.lst $(srcdir)/test/typegen.sh
+ $(AM_V_GEN) $(SHELL) $(srcdir)/test/typegen.sh \
+ < $(srcdir)/src/types.lst > $@.tmp
+ $(AM_V_at)mv -f $@.tmp $@
+
# Supporting rules for gettext.
ALL_MOFILES = $(POFILES:.po=.mo)
EXTRA_DIST += po/$(PACKAGE).pot po/LINGUAS $(POFILES) $(ALL_MOFILES)
AM_SILENT_RULES([yes])
AC_PROG_CC_C99
+AM_PROG_CC_C_O
gl_EARLY
LT_INIT
AH_BOTTOM([#include <cfgtail.h>])
+AC_ARG_WITH([gsl],
+ [AS_HELP_STRING([--with-gsl],
+ [specify whether to build test programs requiring the GNU Scientific Library. [default=auto]]
+ )], [with_gsl=$withval], [with_gsl=auto])
+
+case $with_gsl in
+no)
+ have_gsl=no
+;;
+yes)
+ DX_CHECK_GSL([1.0], [have_gsl=yes])
+;;
+*)
+ DX_CHECK_GSL([1.0], [have_gsl=yes], [have_gsl=no])
+;;
+esac
+
+AM_CONDITIONAL([HAVE_GSL], [test x"$have_gsl" = x"yes"])
+
AC_CONFIG_FILES([
exported.sh
Makefile
--- /dev/null
+dnl Copyright © 2011 Nick Bowler
+dnl
+dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+dnl This is free software: you are free to do what the fuck you want to.
+dnl There is NO WARRANTY, to the extent permitted by law.
+
+m4_define([_DX_COMPUTE_C_STRING_PROG], [AC_LANG_PROGRAM([dnl
+#include <stdio.h>
+#include <stdlib.h>
+$2
+char thestr@<:@@:>@ = $1;
+char padstr@<:@@:>@ = "\n!&?~" $1 "~?&!\n";
+], [dnl
+size_t i;
+FILE *f = fopen("conftest.out", "w");
+if (!f)
+ return EXIT_FAILURE;
+for (i = 0; i < sizeof thestr; i++) {
+ if (*(thestr+i) == '\n')
+ *(thestr+i) = ' ';
+}
+fprintf(f, "%s\n", thestr);
+return 0;
+])])
+
+AC_DEFUN([_DX_COMPUTE_C_STRING_NORUN], [dnl
+AC_REQUIRE([DX_PROG_STRINGS])
+if test x"$STRINGS" = x""; then
+ AC_MSG_WARN([strings is recommended for cross compilation])
+ dx_c_strings=cat
+else
+ dx_c_strings="$STRINGS -n 1"
+fi
+dnl else
+AC_COMPILE_IFELSE([_DX_COMPUTE_C_STRING_PROG([$2], [$3])], [dnl
+ $dx_c_strings conftest.$OBJEXT | sed -n '/^!&?~/ {
+ t top
+ :top
+ s/^!&?~\(.*\)~?&!$/\1/p
+ t
+ N
+ s/\n/ /g
+ t top
+ }' > conftest.out
+ if read $1 < conftest.out; then :; else
+ AC_MSG_ERROR([failed to extract string due to cross compilation.])
+ fi
+], [dnl
+ ifelse([$4], [], [:], [$4])])
+dnl fi
+])
+
+dnl DX_COMPUTE_C_STRING(variable, string, [prologue], [action-if-failed])
+dnl
+dnl Assigns the value of a C string literal specified by string to the shell
+dnl variable specified by variable. Any newlines in the string are converted
+dnl to spaces. In a cross compilation environment, it works by scanning a
+dnl compiled object file.
+AC_DEFUN([DX_COMPUTE_C_STRING], [dnl
+AC_LANG_PUSH([C])
+AC_RUN_IFELSE([_DX_COMPUTE_C_STRING_PROG([$2], [$3])],
+ [read $1 < conftest.out],
+ [ifelse([$4], [], [:], [$4])],
+ [_DX_COMPUTE_C_STRING_NORUN([$1], [$2], [$3], [$4])])
+AC_LANG_POP([C])
+])
--- /dev/null
+dnl Copyright © 2010-2011 Nick Bowler
+dnl
+dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+dnl This is free software: you are free to do what the fuck you want to.
+dnl There is NO WARRANTY, to the extent permitted by law.
+
+m4_pattern_forbid([^_?DX_])
+
+dnl DX_PKG_CONFIG(env-base, [pkg-id], [pkg-help])
+AC_DEFUN([DX_PKG_CONFIG], [dnl
+AC_ARG_VAR(m4_toupper([$1])[_CFLAGS], [C compiler flags for ]ifelse(
+ [$3], [], [$1], [$3]))dnl
+AC_ARG_VAR(m4_toupper([$1])[_LIBS], [linker flags for ]ifelse(
+ [$3], [], [$1], [$3]))dnl
+
+ifelse([$2], [], [], [dnl
+AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test x"$PKG_CONFIG" != x; then
+ AC_MSG_CHECKING([pkg-config database for $2])
+ pkg_failed=no
+ _PKG_CONFIG(m4_tolower([$1])[_cflags], [cflags], [$2])
+ _PKG_CONFIG(m4_tolower([$1])[_libs], [libs], [$2])
+ if test x$pkg_failed = xyes; then
+ errors=`$PKG_CONFIG --errors-to-stdout --print-errors $2`
+ echo "$errors" >&AS_MESSAGE_LOG_FD
+ AC_MSG_RESULT([no])
+ else
+ AC_MSG_RESULT([yes])
+ fi
+fi])])
+
+dnl _DX_CHECK_LIB(env-base, check-func, [cflags], [libs], [test])
+AC_DEFUN([_DX_CHECK_LIB], [dnl
+if test x"$dx_cv_[]m4_tolower([$1])_found" = x"no"; then
+if ifelse([$5], [], [:], [$5]); then
+ CFLAGS="$3 $dx_old_cflags"
+ LIBS="$4 $dx_old_libs"
+
+ $2([dnl
+ dx_cv_[]m4_tolower([$1])_found="yes"
+ dx_cv_[]m4_tolower([$1])_cflags="$3"
+ dx_cv_[]m4_tolower([$1])_libs="$4"
+ ])
+fi
+fi
+])
+
+dnl DX_CHECK_LIB_EXT(env-base, message, check-func,
+dnl [[[cflags1],[libs1],[test1]],[[cflags2],[libs2],[test2]],...])
+AC_DEFUN([DX_CHECK_LIB_EXT], [dnl
+AC_LANG_PUSH([C])
+dx_old_cflags=$CFLAGS
+dx_old_libs=$LIBS
+
+AC_CACHE_CHECK([$2], [dx_cv_]m4_tolower([$1])[_found], [dnl
+AC_CACHE_VAL([dx_cv_]m4_tolower([$1])[_libs], [dnl
+AC_CACHE_VAL([dx_cv_]m4_tolower([$1])[_cflags], [dnl
+ dx_cv_[]m4_tolower([$1])_found=no
+ m4_foreach([dx_tuple], [$4], [_DX_CHECK_LIB([$1], [$3], dx_tuple)])
+])])])
+
+if test x"$dx_cv_[]m4_tolower([$1])_found" = x"yes"; then
+ m4_toupper([$1])_CFLAGS=$dx_cv_[]m4_tolower([$1])_cflags
+ m4_toupper([$1])_LIBS=$dx_cv_[]m4_tolower([$1])_libs
+ AC_SUBST(m4_toupper([$1])[_CFLAGS])
+ AC_SUBST(m4_toupper([$1])[_LIBS])
+fi
+
+CFLAGS=$dx_old_cflags
+LIBS=$dx_old_libs
+AC_LANG_POP([C])
+])
+
+dnl DX_CHECK_LIB(env-base, message, test-program,
+dnl [[[cflags1],[libs1],[test1]],[[cflags2],[libs2],[test2]],...])
+AC_DEFUN([DX_CHECK_LIB], [dnl
+ DX_CHECK_LIB_EXT([$1], [$2], [m4_curry([AC_LINK_IFELSE], [$3])], [$4])
+])
--- /dev/null
+dnl Copyright © 2009-2011 Nick Bowler
+dnl
+dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+dnl This is free software: you are free to do what the fuck you want to.
+dnl There is NO WARRANTY, to the extent permitted by law.
+
+AC_DEFUN([_DX_CHECK_GSL_TEST], [dnl
+ DX_COMPUTE_C_STRING([gsl_version], [GSL_VERSION], [dnl
+#include <gsl/gsl_version.h>
+], [gsl_version=0])
+
+ AS_VERSION_COMPARE([$gsl_version], [$1],
+ [gsl_version_ok=no],
+ [gsl_version_ok=yes],
+ [gsl_version_ok=yes])
+
+ if test x"$gsl_version_ok" = x"yes"; then
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_blas.h>
+], [dnl
+double m1buf@<:@16@:>@, m2buf@<:@16@:>@, m3buf@<:@16@:>@;
+
+gsl_matrix_view m1 = gsl_matrix_view_array(m1buf, 4, 4);
+gsl_matrix_view m2 = gsl_matrix_view_array(m2buf, 4, 4);
+gsl_matrix_view m3 = gsl_matrix_view_array(m3buf, 4, 4);
+
+gsl_matrix_set_identity(&m1.matrix);
+gsl_matrix_set_identity(&m2.matrix);
+gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1, &m1.matrix, &m2.matrix,
+ 0, &m3.matrix);
+
+return 0;
+])], [$2])
+ fi
+])
+
+dnl DX_CHECK_GSL([min-version], [action-if-found], [action-if-not-found])
+AC_DEFUN([DX_CHECK_GSL], [dnl
+DX_PKG_CONFIG([gsl], [gsl], [GSL])
+AC_ARG_VAR([GSL_CONFIG], [path to gsl-config binary])
+AC_PATH_PROG([GSL_CONFIG], [gsl-config])
+
+if test x"$GSL_CONFIG" != x; then
+ gsl_config_cflags=`$GSL_CONFIG --cflags`
+ gsl_config_libs=`$GSL_CONFIG --libs`
+fi
+
+DX_CHECK_LIB_EXT([gsl],
+ [for GSL version at least ifelse([$1], [], [1.0], [$1])],
+ [m4_curry([_DX_CHECK_GSL_TEST], [ifelse([$1], [], [1.0], [$1])])],
+ [dnl
+ [[$GSL_CFLAGS], [$GSL_LIBS]],
+ [[$pkg_cv_gsl_cflags], [$pkg_cv_gsl_libs],
+ [test x"$pkg_failed" != x"yes"]],
+ [[$gsl_config_cflags], [$gsl_config_libs],
+ [test x"$SDL_CONFIG" != x""]],
+ ])
+
+if test x"$dx_cv_gsl_found" = x"yes"; then
+ ifelse([$2], [], [:], [$2])
+else
+ ifelse([$3], [], [AC_MSG_FAILURE([dnl
+GSL version at least ifelse([$1], [], [1.0], [$1]) is required. The
+latest version can be optained from http://gnu.org/software/gsl/.
+
+If GSL is installed but was not found by this configure script, consider
+adjusting GSL_CFLAGS, GSL_LIBS and/or GSL_CONFIG as necessary.
+
+If pkg-config is installed, it may help to adjust PKG_CONFIG_PATH
+if GSL is installed in a non-standard prefix.
+])], [$3])
+fi
+])
--- /dev/null
+AC_DEFUN([DX_PROG_STRINGS], [dnl
+AC_CHECK_TOOL([STRINGS], [strings])
+])
--- /dev/null
+typegen.h
+randomdecl
--- /dev/null
+/*
+ * Generate random C declarations for testing.
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <gsl/gsl_rng.h>
+
+#include "declgen.h"
+#include "cdecl.h"
+#include "test.h"
+
+struct gen_rng {
+ gsl_rng *rng;
+};
+
+/*
+ * Generate a random identifier. We arbitrarily pick 10 as the largest
+ * possible. Avoid generating keywords, including the English ones, by
+ * excluding the letters t, o, a and n. Reserved words are OK.
+ */
+char *gen_identifier(struct gen_rng *rng)
+{
+ static const char valid[59] = "_bcdefghijklmpqrsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ char *str;
+ size_t n;
+
+ n = gsl_rng_uniform_int(rng->rng, 10)+1;
+ str = malloc_nofail(n+1);
+
+ /* Exclude 10 digits from the first character. */
+ str[0] = valid[gsl_rng_uniform_int(rng->rng, sizeof valid - 10)];
+ for (size_t i = 1; i < n; i++)
+ str[i] = valid[gsl_rng_uniform_int(rng->rng, sizeof valid)];
+ str[n] = 0;
+
+ return str;
+}
+
+/*
+ * Generate random qualifiers. Qualifiers can appear multiple times, so the
+ * list is (potentially) unbounded. We handle the potential unboundedness by
+ * generating a list of n qualifiers with probability 1/2^(n+1). Each
+ * qualifier is chosen uniformly at random from the set of possibilities:
+ * const, volatile and, if restrictqual is true, restrict.
+ */
+struct cdecl_declspec *gen_qualifiers(struct gen_rng *rng, bool restrictqual)
+{
+ struct cdecl_declspec *s = NULL, *p;
+
+ while (gsl_rng_uniform(rng->rng) < 0.5) {
+ p = s;
+ s = malloc_nofail(sizeof *s);
+ *s = (struct cdecl_declspec) {
+ .next = p,
+ };
+
+ switch (gsl_rng_uniform_int(rng->rng, 2+restrictqual)) {
+ case 0:
+ s->type = CDECL_QUAL_CONST;
+ break;
+ case 1:
+ s->type = CDECL_QUAL_VOLATILE;
+ break;
+ case 2: /* Only if restrictqual is true. */
+ s->type = CDECL_QUAL_RESTRICT;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return s;
+}
+
+/*
+ * Generate random function specifiers. Like qualifiers, function specifiers
+ * can appear multiple times.
+ */
+struct cdecl_declspec *gen_funcspecs(struct gen_rng *rng)
+{
+ struct cdecl_declspec *s = NULL, *p;
+
+ while (gsl_rng_uniform(rng->rng) < 0.5) {
+ p = s;
+ s = malloc_nofail(sizeof *s);
+
+ *s = (struct cdecl_declspec) {
+ .type = CDECL_FUNC_INLINE,
+ .next = p,
+ };
+ }
+
+ return s;
+}
+
+/*
+ * Generate zero or one random storage-class specifier. If registeronly is
+ * true, then the only possible storage-class specifier is "register".
+ * Otherwise, a specifier type will be selected uniformly at random.
+ */
+struct cdecl_declspec *gen_storspecs(struct gen_rng *rng, bool registeronly)
+{
+ struct cdecl_declspec *s;
+
+ if (gsl_rng_uniform(rng->rng) < 0.5)
+ return NULL;
+
+ s = malloc_nofail(sizeof *s);
+ *s = (struct cdecl_declspec) {
+ .type = CDECL_STOR_REGISTER,
+ };
+
+ if (registeronly)
+ return s;
+
+ switch (gsl_rng_uniform_int(rng->rng, 5)) {
+ case 0: s->type = CDECL_STOR_TYPEDEF; break;
+ case 1: s->type = CDECL_STOR_REGISTER; break;
+ case 2: s->type = CDECL_STOR_STATIC; break;
+ case 3: s->type = CDECL_STOR_AUTO; break;
+ case 4: s->type = CDECL_STOR_EXTERN; break;
+ default: assert(0);
+ }
+
+ return s;
+}
+
+/*
+ * Generate random type specifiers. There is a short list of valid
+ * combinations, from which we can select one uniformly at random.
+ */
+static const unsigned long total_types;
+static struct cdecl_declspec *gen_raw_typespecs(struct gen_rng *rng)
+{
+ switch (gsl_rng_uniform_int(rng->rng, total_types)) {
+# include "typegen.h"
+ }
+}
+static const unsigned long total_types = TOTAL_TYPES;
+
+struct cdecl_declspec *gen_typespecs(struct gen_rng *rng, bool voidtype)
+{
+ struct cdecl_declspec *specs;
+
+retry:
+ specs = gen_raw_typespecs(rng);
+
+ switch (specs->type) {
+ /* void is not always valid, so we might need to pick again. */
+ case CDECL_TYPE_VOID:
+ if (!voidtype)
+ goto retry;
+ break;
+ /* A few kinds of type specifiers need identifiers to go with them. */
+ case CDECL_TYPE_STRUCT:
+ case CDECL_TYPE_UNION:
+ case CDECL_TYPE_ENUM:
+ case CDECL_TYPE_IDENT:
+ assert(!specs->next);
+ specs->ident = gen_identifier(rng);
+ }
+
+ return specs;
+}
+
+struct cdecl_declspec *
+gen_randomize_specs(struct gen_rng *rng, struct cdecl_declspec *specs)
+{
+ struct cdecl_declspec **p;
+ size_t n = 0;
+
+ if (!specs)
+ return specs;
+
+ for (struct cdecl_declspec *s = specs; s; s = s->next)
+ n++;
+
+ p = malloc_nofail((n+1) * sizeof *p);
+
+ /* Build a temporary array for easy element swapping. */
+ p[0] = specs;
+ p[n] = NULL;
+ for (size_t i = 1; i < n; i++)
+ p[i] = p[i-1]->next;
+
+ /* Knuth shuffle. */
+ for (size_t i = 0; i < n; i++) {
+ struct cdecl_declspec *tmp;
+ size_t r;
+
+ r = gsl_rng_uniform_int(rng->rng, n-i);
+ tmp = p[i];
+ p[i] = p[r+i];
+ p[r+i] = tmp;
+ }
+
+ /* Fix up the pointers. */
+ for (size_t i = 1; i <= n; i++)
+ p[i-1]->next = p[i];
+ specs = p[0];
+
+ free(p);
+ return specs;
+}
+
+/*
+ * Generate random declaration specifiers. This consists of random qualifiers
+ * and type specifiers, as described in the functions above, plus up to one
+ * storage-class specifier and zero or more function specifiers.
+ *
+ * The flags parameter controls what sort of specifiers can be generated in
+ * order to handle various special cases in the C language. It is the
+ * bitwise-or of zero or more values, which have the following meanings:
+ *
+ * GEN_NO_FUNCTION: Do not generate any function specifiers at all.
+ * GEN_NO_STORAGE: Do not generate any storage-class specifiers at all.
+ * GEN_NO_VOID: Do not generate the "void" type specifier.
+ * GEN_ONLY_REGISTER: Never generate any storage-class specifiers other than
+ * "register".
+ *
+ * Notwithstanding any of the above, the "restrict" type qualifier will never
+ * be generated by this function: use gen_qualifiers directly.
+ */
+struct cdecl_declspec *
+gen_declspecs(struct gen_rng *rng, unsigned flags)
+{
+ struct cdecl_declspec *s, *p;
+
+ s = gen_typespecs(rng, ~flags & GEN_NO_VOID);
+ for (p = s; p->next;)
+ p = p->next;
+
+ if (~flags & GEN_NO_FUNCTION)
+ p->next = gen_funcspecs(rng);
+ for (p = s; p->next;)
+ p = p->next;
+
+ if (~flags & GEN_NO_STORAGE)
+ p->next = gen_storspecs(rng, flags & GEN_ONLY_REGISTER);
+ for (p = s; p->next;)
+ p = p->next;
+
+ p->next = gen_qualifiers(rng, false);
+ return gen_randomize_specs(rng, s);
+}
+
+/*
+ * Generate a random array declarator, selecting one of four possibilities
+ * uniformly at random.
+ */
+static void gen_array(struct gen_rng *rng, struct cdecl_declarator *d)
+{
+ d->type = CDECL_DECL_ARRAY;
+ d->u.array = (struct cdecl_array){0};
+
+ switch (gsl_rng_uniform_int(rng->rng, 4)) {
+ case '0':
+ d->u.array.vla = malloc_nofail(1);
+ d->u.array.vla[0] = 0;
+ break;
+ case '1':
+ d->u.array.vla = gen_identifier(rng);
+ break;
+ case '2':
+ d->u.array.length = 0;
+ break;
+ case '3':
+ d->u.array.length = gsl_rng_uniform_int(rng->rng, -1);
+ break;
+ }
+}
+
+/* Generate a function declarator. */
+static void gen_function(struct gen_rng *rng, struct cdecl_declarator *d)
+{
+ d->type = CDECL_DECL_FUNCTION;
+ d->u.function.parameters = NULL;
+
+ while (gsl_rng_uniform(rng->rng) < 0.5) {
+ unsigned flags = GEN_ONLY_REGISTER | GEN_NO_FUNCTION;
+ struct cdecl *param;
+
+ param = malloc_nofail(sizeof *param);
+ *param = (struct cdecl) {
+ .next = d->u.function.parameters,
+ .declarators = gen_declarators(rng),
+ };
+
+ if (param->declarators->type == CDECL_DECL_ARRAY
+ || param->declarators->type == CDECL_DECL_IDENT)
+ flags |= GEN_NO_VOID;
+
+ param->specifiers = gen_declspecs(rng, flags);
+ d->u.function.parameters = param;
+ }
+
+ if (d->u.function.parameters) {
+ d->u.function.variadic = gsl_rng_uniform(rng->rng) < 0.5;
+ }
+}
+
+/*
+ * Generate a random chain of declarators. Like qualifiers above, a chain of
+ * length N has probability 1/2^(N+1) of occurring. All declarator chains
+ * are ultimately terminated by either a null declarator or an identifier,
+ * selected uniformly at random.
+ */
+struct cdecl_declarator *gen_declarators(struct gen_rng *rng)
+{
+ struct cdecl_declarator *d, *p;
+ unsigned limit = 3;
+
+ d = malloc_nofail(sizeof *d);
+ if (gsl_rng_uniform(rng->rng) < 0.5) {
+ *d = (struct cdecl_declarator) {
+ .type = CDECL_DECL_NULL,
+ };
+ } else {
+ *d = (struct cdecl_declarator) {
+ .type = CDECL_DECL_IDENT,
+ .u.ident = gen_identifier(rng),
+ };
+ }
+
+ while (gsl_rng_uniform(rng->rng) < 0.5) {
+ p = d;
+ d = malloc_nofail(sizeof *d);
+ *d = (struct cdecl_declarator) {
+ .child = p,
+ };
+
+ switch (gsl_rng_uniform_int(rng->rng, limit)) {
+ case 0:
+ d->type = CDECL_DECL_POINTER;
+ d->u.pointer = (struct cdecl_pointer) {
+ .qualifiers = gen_qualifiers(rng, true),
+ };
+ limit = 3;
+ break;
+ case 1:
+ gen_array(rng, d);
+ limit = 2;
+ break;
+ case 2:
+ gen_function(rng, d);
+ limit = 1;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return d;
+}
+
+struct gen_rng *gen_alloc_rng(const char *seed_str)
+{
+ unsigned long seed;
+ struct gen_rng *rng;
+ char *end;
+
+ errno = 0;
+ seed = strtoul(seed_str, &end, 0);
+ if (*end != 0) {
+ fprintf(stderr, "%s: invalid seed\n", seed_str);
+ return NULL;
+ } else if (errno != 0) {
+ fprintf(stderr, "%s: invalid seed: %s\n",
+ seed_str, strerror(errno));
+ return NULL;
+ }
+
+ rng = malloc(sizeof *rng);
+ if (rng) {
+ rng->rng = gsl_rng_alloc(gsl_rng_mt19937);
+ gsl_rng_set(rng->rng, seed);
+ }
+
+ return rng;
+}
--- /dev/null
+#ifndef CDECL_DECLGEN_H_
+#define CDECL_DECLGEN_H_
+
+struct gen_rng;
+
+enum {
+ GEN_NO_FUNCTION = 0x01,
+ GEN_NO_STORAGE = 0x02,
+ GEN_NO_VOID = 0x04,
+ GEN_ONLY_REGISTER = 0x08,
+};
+
+struct cdecl_declspec *gen_qualifiers(struct gen_rng *rng, _Bool pointer);
+struct cdecl_declspec *gen_typespecs(struct gen_rng *rng, _Bool novoid);
+struct cdecl_declspec *gen_randomize_specs(struct gen_rng *rng,
+ struct cdecl_declspec *specs);
+struct cdecl_declspec *gen_declspecs(struct gen_rng *rng, unsigned flags);
+struct cdecl_declarator *gen_declarators(struct gen_rng *rng);
+struct gen_rng *gen_alloc_rng(const char *seed);
+
+#endif
--- /dev/null
+/*
+ * Generate random C declarations for testing.
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <getopt.h>
+#include <cdecl.h>
+
+#include "declgen.h"
+#include "test.h"
+
+#define PROGNAME "randomdecl"
+static const char *progname = PROGNAME;
+static const char sopts[] = "s:n:VH";
+static const struct option lopts[] = {
+ { "seed", 1, NULL, 's' },
+ { "count", 1, NULL, 'n' },
+ { "version", 0, NULL, 'V' },
+ { "help", 0, NULL, 'H' },
+ { 0 }
+};
+
+static void print_version()
+{
+ puts(PROGNAME " (" PACKAGE_NAME ") " PACKAGE_VERSION);
+ puts("Copyright (C) 2011 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.");
+}
+
+static void print_usage(FILE *f)
+{
+ fprintf(f, "Usage: %s [options]\n", progname);
+}
+
+static void print_help(void)
+{
+ print_usage(stdout);
+ puts("Detailed help coming soon.");
+}
+
+static struct cdecl *random_decl(struct gen_rng *rng)
+{
+ struct cdecl *decl;
+ unsigned flags = 0;
+
+ decl = malloc_nofail(sizeof *decl);
+ *decl = (struct cdecl) { 0 };
+
+ decl->declarators = gen_declarators(rng);
+ if (decl->declarators->type != CDECL_DECL_FUNCTION)
+ flags |= GEN_NO_FUNCTION;
+ if (cdecl_is_abstract(decl->declarators))
+ flags |= GEN_NO_STORAGE | GEN_NO_FUNCTION;
+ if (decl->declarators->type == CDECL_DECL_ARRAY
+ || decl->declarators->type == CDECL_DECL_IDENT)
+ flags |= GEN_NO_VOID;
+
+ decl->specifiers = gen_declspecs(rng, flags);
+ return decl;
+}
+
+int main(int argc, char **argv)
+{
+ const char *seed = "", *count_str = NULL;
+ struct gen_rng *rng;
+ struct cdecl *decl;
+ unsigned long count = 0;
+ int opt;
+
+ if (argc > 0)
+ progname = argv[0];
+
+ while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ seed = optarg;
+ break;
+ case 'n':
+ count_str = optarg;
+ break;
+ case 'V':
+ print_version();
+ return EXIT_SUCCESS;
+ case 'H':
+ print_help();
+ return EXIT_SUCCESS;
+ default:
+ print_usage(stderr);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (count_str && !strict_strtoul(&count, count_str, 0)) {
+ fprintf(stderr, "%s: invalid count: %s\n", progname, count_str);
+ return EXIT_FAILURE;
+ }
+
+ rng = gen_alloc_rng(seed);
+ if (!rng)
+ return EXIT_FAILURE;
+
+ for (unsigned long i = 0; !count || i < count; i++) {
+ decl = random_decl(rng);
+ if (!decl)
+ return EXIT_FAILURE;
+
+ test_print_decl(decl);
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#ifndef CDECL_TEST_H_
+#define CDECL_TEST_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+
+struct cdecl_declspec;
+struct cdecl;
+
+void *malloc_nofail(size_t size);
+void *realloc_nofail(void *ptr, size_t size);
+void test_print_specifiers(struct cdecl_declspec *spec);
+void test_print_decl(struct cdecl *decl);
+
+bool strict_strtoul(unsigned long *val, const char *str, int base);
+
+#endif
--- /dev/null
+/*
+ * Miscellaneous functions used by the cdecl99 test suite.
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <cdecl.h>
+#include "test.h"
+
+void *realloc_nofail(void *ptr, size_t size)
+{
+ void *p;
+
+ p = realloc(ptr, size);
+ if (!p) {
+ perror("failed to allocate memory");
+ abort();
+ }
+
+ return p;
+}
+
+void *malloc_nofail(size_t size)
+{
+ return realloc_nofail(NULL, size);
+}
+
+void test_print_decl(struct cdecl *decl)
+{
+ static size_t bufsz;
+ static char *buf;
+ size_t rc;
+
+retry:
+ rc = cdecl_declare(buf, bufsz, decl);
+ if (rc >= bufsz) {
+ bufsz = rc + 1;
+ buf = realloc_nofail(buf, bufsz);
+ goto retry;
+ }
+
+ printf("%s\n", buf);
+}
+
+bool strict_strtoul(unsigned long *val, const char *str, int base)
+{
+ char *end;
+
+ errno = 0;
+ *val = strtoul(str, &end, base);
+ if (errno != 0 || *end != 0)
+ return false;
+
+ return true;
+}
--- /dev/null
+#!/bin/sh
+#
+# 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.
+
+cat <<EOF
+/* This file was automatically generated by $0 $*. */
+struct cdecl_declspec *s, *p;
+EOF
+
+i=0
+while read line
+do
+ case "$line" in
+ [[:alpha:]_]*)
+ :
+ ;;
+ *)
+ printf '%s\n' "$line";
+ continue
+ ;;
+ esac
+
+ cat <<EOF
+case $i:
+ s = NULL;
+EOF
+
+ set $line
+ while test $# -gt 0
+ do
+ token=`printf '%s' "$1" | tr [:lower:] [:upper:] | tr -d _`
+ cat <<EOF
+ p = s;
+ s = malloc_nofail(sizeof *s);
+ *s = (struct cdecl_declspec) {
+ .type = CDECL_TYPE_$token,
+ .next = p,
+ };
+EOF
+ shift
+ done
+
+ i=`expr $i + 1`
+ cat <<EOF
+ return s;
+EOF
+done
+
+cat <<EOF
+default:
+ assert(0);
+#define TOTAL_TYPES $i
+EOF