From 8c4c37ecd2f04402c23b18c2e0a25f7bcc5bdce3 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Fri, 8 Jul 2011 20:00:44 -0400 Subject: [PATCH] Add support for outputting declarations as C code. This permits us to define a nifty "simplify" function. --- Makefile.am | 2 +- src/cdecl.h | 1 + src/cdecl99.c | 95 ++++++++++++++++++++++++------- src/declare.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/explain.c | 113 +------------------------------------ src/output.c | 108 +++++++++++++++++++++++++++++++++++ src/output.h | 4 ++ src/typemap.h | 2 + 8 files changed, 346 insertions(+), 131 deletions(-) create mode 100644 src/declare.c diff --git a/Makefile.am b/Makefile.am index 07d3f8f..ad56e4a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ noinst_HEADERS = src/typemap.h src/output.h src/scan.h src/parse.h lib_LTLIBRARIES = libcdecl.la libcdecl_la_LDFLAGS = -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/output.c src/explain.c src/declare.c bin_PROGRAMS = cdecl99 cdecl99_SOURCES = src/cdecl99.c diff --git a/src/cdecl.h b/src/cdecl.h index 9b3e5da..29dab35 100644 --- a/src/cdecl.h +++ b/src/cdecl.h @@ -99,6 +99,7 @@ struct cdecl *cdecl_parse_decl(const char *declstr); 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) { diff --git a/src/cdecl99.c b/src/cdecl99.c index b47565d..44bcd3e 100644 --- a/src/cdecl99.c +++ b/src/cdecl99.c @@ -55,39 +55,93 @@ static void print_help(void) puts("Detailed help coming soon."); } -static int cmd_explain(char *cmd, char *arg) +/* + * Format a declaration according to the given function and return a pointer + * to the formatted string. The returned pointer remains valid until the + * next call, after which it must not be re-used. + * + * Returns NULL on failure. + */ +static const char * +do_format(size_t func(char *, size_t, struct cdecl *), struct cdecl *decl) { static size_t bufsz; static char *buf; - int ret = -1; - struct cdecl *decl; size_t rc; +retry: + rc = func(buf, bufsz, decl); + if (rc >= bufsz) { + char *tmp; + + tmp = realloc(buf, rc + 1); + if (!tmp) { + fprintf(stderr, "failed to allocate memory\n"); + return NULL; + } + + buf = tmp; + bufsz = rc + 1; + goto retry; + } + + return buf; +} + +static int cmd_explain(char *cmd, char *arg) +{ + struct cdecl *decl; + const char *str; + int ret = -1; + decl = cdecl_parse_decl(arg); if (!decl) goto out; for (struct cdecl *i = decl; i; i = i->next) { -retry: - rc = cdecl_explain(buf, bufsz, i); - if (rc >= bufsz) { - char *tmp; + str = do_format(cdecl_explain, i); + if (!str) + goto out; - tmp = realloc(buf, rc + 1); - if (!tmp) { - fprintf(stderr, "failed to allocate memory\n"); - goto out; - } + printf("%s\n", str); + } + + ret = 1; +out: + cdecl_free(decl); + return ret; +} + +static int cmd_simplify(char *cmd, char *arg) +{ + struct cdecl *decl; + const char *str; + int ret = -1; + + decl = cdecl_parse_decl(arg); + if (!decl) + goto out; - buf = tmp; - bufsz = rc + 1; - goto retry; + for (struct cdecl *i = decl; i; i = i->next) { + struct cdecl_declspec *s = i->specifiers; + + if (i != decl) { + i->specifiers = NULL; + printf(", "); } - printf("%s\n", buf); + str = do_format(cdecl_declare, i); + i->specifiers = s; + + if (!str) + goto out; + + printf("%s", str); } + putchar('\n'); + ret = 1; out: cdecl_free(decl); @@ -106,10 +160,11 @@ static const struct command { int (*func)(char *cmd, char *arg); const char *blurb; } commands[] = { - { "explain", cmd_explain, "Explain a C declaration." }, - { "help", cmd_help, "Print this list of commands." }, - { "quit", cmd_quit, "Quit the program." }, - { "exit", cmd_quit, NULL } + { "explain", cmd_explain, "Explain a C declaration." }, + { "simplify", cmd_simplify, "Simplify a C declaration." }, + { "help", cmd_help, "Print this list of commands." }, + { "quit", cmd_quit, "Quit the program." }, + { "exit", cmd_quit, NULL } }; static const size_t ncommands = sizeof commands / sizeof commands[0]; diff --git a/src/declare.c b/src/declare.c new file mode 100644 index 0000000..bc8af4a --- /dev/null +++ b/src/declare.c @@ -0,0 +1,152 @@ +#include +#include + +#include "cdecl.h" +#include "output.h" + +static size_t declare_specs(char *buf, size_t n, struct cdecl_declspec *s) +{ + size_t ret = 0, rc; + + if (!s) + return 0; + + rc = cdecl__explain_pre_specs(buf, n, s); + ret += cdecl__advance(&buf, &n, rc); + + rc = cdecl__explain_post_specs(buf, n, s); + return ret + rc; +} + +static size_t +declare_declarator(char *buf, size_t n, struct cdecl_declarator *d); + +static size_t declare_decl(char *buf, size_t n, struct cdecl *decl) +{ + size_t ret = 0, rc; + + rc = declare_specs(buf, n, decl->specifiers); + if (decl->declarators->type != CDECL_DECL_NULL) + ret += cdecl__advance(&buf, &n, rc); + else + ret += cdecl__advance_(&buf, &n, rc); + + return ret + declare_declarator(buf, n, decl->declarators); +} + +static size_t +declare_postfix_child(char *buf, size_t n, struct cdecl_declarator *d) +{ + size_t ret = 0, rc; + + if (d->type == CDECL_DECL_POINTER) { + rc = snprintf(buf, n, "("); + ret += cdecl__advance_(&buf, &n, rc); + } + + rc = declare_declarator(buf, n, d); + ret += cdecl__advance_(&buf, &n, rc); + + if (d->type == CDECL_DECL_POINTER) { + rc = snprintf(buf, n, ")"); + ret += cdecl__advance_(&buf, &n, rc); + } + + return ret; +} + +static size_t declare_pointer(char *buf, size_t n, struct cdecl_pointer *p) +{ + size_t ret = 0, rc; + + rc = snprintf(buf, n, "*"); + if (p->qualifiers) + ret += cdecl__advance(&buf, &n, rc); + else + ret += cdecl__advance_(&buf, &n, rc); + + rc = cdecl__explain_qualifiers(buf, n, p->qualifiers); + return ret + cdecl__advance(&buf, &n, rc); +} + +static size_t declare_array(char *buf, size_t n, struct cdecl_array *a) +{ + size_t ret = 0, rc; + + rc = snprintf(buf, n, "["); + ret += cdecl__advance_(&buf, &n, rc); + + if (a->vla) + rc = snprintf(buf, n, "%s", a->vla[0] ? a->vla : "*"); + else + rc = snprintf(buf, n, "%.0ju", a->length); + ret += cdecl__advance_(&buf, &n, rc); + + return ret + snprintf(buf, n, "]"); +} + +static size_t declare_function(char *buf, size_t n, struct cdecl_function *f) +{ + size_t ret = 0, rc; + + rc = snprintf(buf, n, "("); + ret += cdecl__advance_(&buf, &n, rc); + + for (struct cdecl *p = f->parameters; p; p = p->next) { + rc = declare_decl(buf, n, p); + ret += cdecl__advance_(&buf, &n, rc); + + if (p->next) + rc = snprintf(buf, n, ", "); + else if (f->variadic) + rc = snprintf(buf, n, ", ...)"); + else + rc = snprintf(buf, n, ")"); + ret += cdecl__advance_(&buf, &n, rc); + } + + return ret; +} + +static size_t +declare_declarator(char *buf, size_t n, struct cdecl_declarator *d) +{ + size_t ret = 0, rc; + + for (; d; d = d->child) { + switch (d->type) { + case CDECL_DECL_NULL: + break; + case CDECL_DECL_IDENT: + rc = snprintf(buf, n, "%s", d->u.ident); + ret += cdecl__advance_(&buf, &n, rc); + break; + case CDECL_DECL_POINTER: + rc = declare_pointer(buf, n, &d->u.pointer); + ret += cdecl__advance_(&buf, &n, rc); + break; + /* + * Arrays and functions are special: since they are postfix, + * we need to render the children before rendering their + * "bodies". + */ + case CDECL_DECL_ARRAY: + rc = declare_postfix_child(buf, n, d->child); + ret += cdecl__advance_(&buf, &n, rc); + return ret + declare_array(buf, n, &d->u.array); + case CDECL_DECL_FUNCTION: + rc = declare_postfix_child(buf, n, d->child); + ret += cdecl__advance_(&buf, &n, rc); + return ret + declare_function(buf, n, &d->u.function); + default: + assert(0); + } + } + + return ret; +} + +size_t cdecl_declare(char *buf, size_t n, struct cdecl *decl) +{ + return declare_decl(buf, n, decl); +} diff --git a/src/explain.c b/src/explain.c index dadc65f..41d4f7b 100644 --- a/src/explain.c +++ b/src/explain.c @@ -22,115 +22,8 @@ #include #include "cdecl.h" -#include "typemap.h" #include "output.h" -static size_t -explain_qualifiers(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; -} - -/* Renders the type qualifiers and type specifiers in canonical form. */ -static size_t -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 = 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; -} - -static size_t explain_storage(char *buf, size_t n, unsigned spec) -{ - switch (spec) { - case CDECL_STOR_TYPEDEF: - return snprintf(buf, n, "typedef"); - case CDECL_STOR_EXTERN: - return snprintf(buf, n, "extern"); - case CDECL_STOR_STATIC: - return snprintf(buf, n, "static"); - case CDECL_STOR_AUTO: - return snprintf(buf, n, "auto"); - case CDECL_STOR_REGISTER: - return snprintf(buf, n, "register"); - default: - assert(0); - } -} - -/* Renders the storage-class and function specifiers in canonical form. */ -static size_t explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s) -{ - 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)) { - 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; - } - } - - if (funcmap & (1ul << (CDECL_FUNC_INLINE & 0xff))) { - ret += cdecl__advance(&buf, &n, rc); - rc = snprintf(buf, n, "inline"); - } - - return ret + rc; -} - /* * Renders the start of the thing being declared. If top is true, print * the "declare" or "type" keywords at the front, as appropriate. @@ -162,7 +55,7 @@ explain_pointer(char *buf, size_t n, struct cdecl_pointer *p) { size_t ret = 0, rc; - rc = explain_qualifiers(buf, n, p->qualifiers); + rc = cdecl__explain_qualifiers(buf, n, p->qualifiers); ret += cdecl__advance(&buf, &n, rc); return ret + snprintf(buf, n, "pointer to"); @@ -200,13 +93,13 @@ static size_t explain_decl(char *buf, size_t n, struct cdecl *decl, bool top) rc = explain_prologue(buf, n, decl->declarators, top); ret += cdecl__advance(&buf, &n, rc); - rc = explain_pre_specs(buf, n, decl->specifiers); + rc = cdecl__explain_pre_specs(buf, n, decl->specifiers); ret += cdecl__advance(&buf, &n, rc); rc = explain_declarators(buf, n, decl->declarators); ret += cdecl__advance(&buf, &n, rc); - return ret + explain_post_specs(buf, n, decl->specifiers); + return ret + cdecl__explain_post_specs(buf, n, decl->specifiers); } static size_t explain_function(char *buf, size_t n, struct cdecl_function *f) diff --git a/src/output.c b/src/output.c index 8c2cd0b..3d0a923 100644 --- a/src/output.c +++ b/src/output.c @@ -1,5 +1,9 @@ #include +#include + +#include "typemap.h" #include "output.h" +#include "cdecl.h" size_t cdecl__advance_(char **buf, size_t *n, size_t amount) { @@ -25,3 +29,107 @@ size_t cdecl__advance(char **buf, size_t *n, size_t amount) rc = snprintf(*buf, *n, " "); return ret + cdecl__advance_(buf, n, rc); } + +static size_t explain_storage(char *buf, size_t n, unsigned spec) +{ + switch (spec) { + case CDECL_STOR_TYPEDEF: + return snprintf(buf, n, "typedef"); + case CDECL_STOR_EXTERN: + return snprintf(buf, n, "extern"); + case CDECL_STOR_STATIC: + return snprintf(buf, n, "static"); + case CDECL_STOR_AUTO: + return snprintf(buf, n, "auto"); + case CDECL_STOR_REGISTER: + return snprintf(buf, n, "register"); + 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) +{ + 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)) { + 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; + } + } + + 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) +{ + 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; +} + +/* 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; +} diff --git a/src/output.h b/src/output.h index 03f7acb..ecb9a75 100644 --- a/src/output.h +++ b/src/output.h @@ -2,8 +2,12 @@ #define CDECL_OUTPUT_H_ #include +#include "cdecl.h" 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_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 diff --git a/src/typemap.h b/src/typemap.h index ddb5dc6..10eeec3 100644 --- a/src/typemap.h +++ b/src/typemap.h @@ -1,6 +1,8 @@ #ifndef CDECL_TYPEMAP_H_ #define CDECL_TYPEMAP_H_ +#include "cdecl.h" + unsigned long cdecl__build_typemap(struct cdecl_declspec *s); const char *cdecl__explain_typemap(unsigned long map); -- 2.43.2