X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/d9e803fa14295c584c784c4244aab95de41eefdf..59c178a65021036c13ddd6bb2eaad18db071f4e9:/test/declgen.c diff --git a/test/declgen.c b/test/declgen.c index d7bdbc1..0e76348 100644 --- a/test/declgen.c +++ b/test/declgen.c @@ -1,6 +1,6 @@ /* * Generate random C declarations for testing. - * Copyright © 2011 Nick Bowler + * Copyright © 2012, 2021-2022 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 @@ -24,34 +24,71 @@ #include #include #include -#include #include "declgen.h" #include "cdecl.h" #include "test.h" -struct gen_rng { - gsl_rng *rng; -}; +/* + * Return a newly-allocated null declarator. The child member is set by the + * argument; other members are initialized to zero. + */ +struct cdecl_declarator *new_declarator(struct cdecl_declarator *child) +{ + struct cdecl_declarator *d, init = { child, CDECL_DECL_NULL }; + + d = malloc_nofail(sizeof *d); + *d = init; + + return d; +} + +/* + * Return a newly-allocated void specifier. The next member is set by the + * argument; other members are initialized to zero. + */ +struct cdecl_declspec *new_specifier(struct cdecl_declspec *next) +{ + struct cdecl_declspec *d, init = { next, CDECL_TYPE_VOID }; + + d = malloc_nofail(sizeof *d); + *d = init; + + return d; +} + +/* + * Return a newly-allocated declaration. The next member is set by the + * argument; other members are initialized to zero. + */ +struct cdecl *new_cdecl(struct cdecl *next) +{ + struct cdecl *d, init = { next }; + + d = malloc_nofail(sizeof *d); + *d = init; + + return d; +} /* * 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) +char *gen_identifier(struct test_rng *rng) { static const char valid[59] = "_bcdefghijklmpqrsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; char *str; size_t n; - n = gsl_rng_uniform_int(rng->rng, 10)+1; + n = test_rng_uniform_int(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)]; + str[0] = valid[test_rng_uniform_int(rng, sizeof valid - 10)]; for (size_t i = 1; i < n; i++) - str[i] = valid[gsl_rng_uniform_int(rng->rng, sizeof valid)]; + str[i] = valid[test_rng_uniform_int(rng, sizeof valid)]; str[n] = 0; return str; @@ -64,18 +101,14 @@ char *gen_identifier(struct gen_rng *rng) * 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 *gen_qualifiers(struct test_rng *rng, bool restrictqual) { - struct cdecl_declspec *s = NULL, *p; + struct cdecl_declspec *s = NULL; - while (gsl_rng_uniform(rng->rng) < 0.5) { - p = s; - s = malloc_nofail(sizeof *s); - *s = (struct cdecl_declspec) { - .next = p, - }; + while (test_rng_uniform(rng) < 0.5) { + s = new_specifier(s); - switch (gsl_rng_uniform_int(rng->rng, 2+restrictqual)) { + switch (test_rng_uniform_int(rng, 2+restrictqual)) { case 0: s->type = CDECL_QUAL_CONST; break; @@ -97,18 +130,13 @@ struct cdecl_declspec *gen_qualifiers(struct gen_rng *rng, bool restrictqual) * Generate random function specifiers. Like qualifiers, function specifiers * can appear multiple times. */ -struct cdecl_declspec *gen_funcspecs(struct gen_rng *rng) +struct cdecl_declspec *gen_funcspecs(struct test_rng *rng) { - struct cdecl_declspec *s = NULL, *p; - - while (gsl_rng_uniform(rng->rng) < 0.5) { - p = s; - s = malloc_nofail(sizeof *s); + struct cdecl_declspec *s = NULL; - *s = (struct cdecl_declspec) { - .type = CDECL_FUNC_INLINE, - .next = p, - }; + while (test_rng_uniform(rng) < 0.5) { + s = new_specifier(s); + s->type = CDECL_FUNC_INLINE; } return s; @@ -119,24 +147,17 @@ struct cdecl_declspec *gen_funcspecs(struct gen_rng *rng) * 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 *gen_storspecs(struct test_rng *rng, bool registeronly) { struct cdecl_declspec *s; - if (gsl_rng_uniform(rng->rng) < 0.5) + if (test_rng_uniform(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; + s = new_specifier(NULL); + switch (test_rng_uniform_int(rng, registeronly ? 1 : 5)) { + case 0: s->type = CDECL_STOR_REGISTER; break; + case 1: s->type = CDECL_STOR_TYPEDEF; 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; @@ -150,27 +171,23 @@ struct cdecl_declspec *gen_storspecs(struct gen_rng *rng, bool registeronly) * 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; +#include "typegen.h" -struct cdecl_declspec *gen_typespecs(struct gen_rng *rng, bool voidtype) +struct cdecl_declspec *gen_typespecs(struct test_rng *rng, bool voidtype) { struct cdecl_declspec *specs; retry: - specs = gen_raw_typespecs(rng); + specs = gen_raw_typespecs(test_rng_uniform_int(rng, GEN_TOTAL_TYPES)); switch (specs->type) { /* void is not always valid, so we might need to pick again. */ case CDECL_TYPE_VOID: - if (!voidtype) + if (!voidtype) { + assert(!specs->next); + free(specs); goto retry; + } break; /* A few kinds of type specifiers need identifiers to go with them. */ case CDECL_TYPE_STRUCT: @@ -189,7 +206,7 @@ retry: } struct cdecl_declspec * -gen_randomize_specs(struct gen_rng *rng, struct cdecl_declspec *specs) +gen_randomize_specs(struct test_rng *rng, struct cdecl_declspec *specs) { struct cdecl_declspec **p; size_t n = 0; @@ -213,7 +230,7 @@ gen_randomize_specs(struct gen_rng *rng, struct cdecl_declspec *specs) struct cdecl_declspec *tmp; size_t r; - r = gsl_rng_uniform_int(rng->rng, n-i); + r = test_rng_uniform_int(rng, n-i); tmp = p[i]; p[i] = p[r+i]; p[r+i] = tmp; @@ -247,7 +264,7 @@ gen_randomize_specs(struct gen_rng *rng, struct cdecl_declspec *specs) * be generated by this function: use gen_qualifiers directly. */ struct cdecl_declspec * -gen_declspecs(struct gen_rng *rng, unsigned flags) +gen_declspecs(struct test_rng *rng, unsigned flags) { struct cdecl_declspec *s, *p; @@ -269,13 +286,13 @@ gen_declspecs(struct gen_rng *rng, unsigned flags) return gen_randomize_specs(rng, s); } -static uintmax_t gen_uintmax(struct gen_rng *rng) +static uintmax_t gen_uintmax(struct test_rng *rng) { unsigned char tmp; uintmax_t ret = 0; for (size_t i = 0; i < sizeof ret; i++) { - tmp = gsl_rng_uniform_int(rng->rng, UCHAR_MAX+1); + tmp = test_rng_uniform_int(rng, UCHAR_MAX+1); ret <<= CHAR_BIT; ret |= tmp; } @@ -287,45 +304,42 @@ static uintmax_t gen_uintmax(struct gen_rng *rng) * 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) +static void gen_array(struct test_rng *rng, struct cdecl_declarator *d) { - d->type = CDECL_DECL_ARRAY; - d->u.array = (struct cdecl_array){0}; + struct cdecl_array tmp = {0}; - switch (gsl_rng_uniform_int(rng->rng, 4)) { + switch (test_rng_uniform_int(rng, 4)) { case 0: - d->u.array.vla = malloc_nofail(1); - d->u.array.vla[0] = 0; + tmp.vla = malloc_nofail(1); + tmp.vla[0] = 0; break; case 1: - d->u.array.vla = gen_identifier(rng); + tmp.vla = gen_identifier(rng); break; case 2: - d->u.array.length = 0; - break; + tmp.length = gen_uintmax(rng); case 3: - d->u.array.length = gen_uintmax(rng); break; default: assert(0); } + + d->type = CDECL_DECL_ARRAY; + d->u.array = tmp; } /* Generate a function declarator. */ -static void gen_function(struct gen_rng *rng, struct cdecl_declarator *d) +static void gen_function(struct test_rng *rng, struct cdecl_declarator *d) { d->type = CDECL_DECL_FUNCTION; d->u.function.parameters = NULL; - while (gsl_rng_uniform(rng->rng) < 0.5) { + while (test_rng_uniform(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), - }; + param = new_cdecl(d->u.function.parameters); + param->declarators = gen_declarators(rng); if (param->declarators->type != CDECL_DECL_POINTER && param->declarators->type != CDECL_DECL_FUNCTION) @@ -336,7 +350,16 @@ static void gen_function(struct gen_rng *rng, struct cdecl_declarator *d) } if (d->u.function.parameters) { - d->u.function.variadic = gsl_rng_uniform(rng->rng) < 0.5; + d->u.function.variadic = test_rng_uniform(rng) < 0.5; + } else if (test_rng_uniform(rng) < 0.5) { + struct cdecl *param; + + /* We will never generate (void) above; do it here. */ + param = new_cdecl(NULL); + param->declarators = new_declarator(NULL); + param->specifiers = new_specifier(NULL); + + d->u.function.parameters = param; } } @@ -346,36 +369,24 @@ static void gen_function(struct gen_rng *rng, struct cdecl_declarator *d) * 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 *gen_declarators(struct test_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), - }; + d = new_declarator(NULL); + if (test_rng_uniform(rng) < 0.5) { + d->type = CDECL_DECL_IDENT; + d->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, - }; + while (test_rng_uniform(rng) < 0.5) { + d = new_declarator((p = d)); - switch (gsl_rng_uniform_int(rng->rng, limit)) { + switch (test_rng_uniform_int(rng, limit)) { case 0: d->type = CDECL_DECL_POINTER; - d->u.pointer = (struct cdecl_pointer) { - .qualifiers = gen_qualifiers(rng, true), - }; + d->u.pointer.qualifiers = gen_qualifiers(rng, true); limit = 3; break; case 1: @@ -384,6 +395,12 @@ struct cdecl_declarator *gen_declarators(struct gen_rng *rng) break; case 2: gen_function(rng, d); + if (p && p->type == CDECL_DECL_POINTER) { + struct cdecl_pointer *ptr = &p->u.pointer; + + gen_free_declspecs(ptr->qualifiers); + ptr->qualifiers = gen_qualifiers(rng, false); + } limit = 1; break; default: @@ -394,37 +411,6 @@ struct cdecl_declarator *gen_declarators(struct gen_rng *rng) 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_nofail(sizeof *rng); - rng->rng = gsl_rng_alloc(gsl_rng_mt19937); - gsl_rng_set(rng->rng, seed); - - return rng; -} - -void gen_free_rng(struct gen_rng *rng) -{ - if (rng) - gsl_rng_free(rng->rng); - free(rng); -} - static void gen_free_parameters(struct cdecl *x) { struct cdecl *p;