From c92cdbdde6876aa06ad07828c8c315c7967128d5 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Thu, 23 Jun 2011 20:24:50 -0400 Subject: [PATCH] Initial support for explaining declarations. This should support everything that the parser currently does. --- Makefile.am | 3 +- src/cdecl.h | 4 ++ src/cdecl99.c | 26 ++++++++- src/explain.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 src/explain.c diff --git a/Makefile.am b/Makefile.am index 6b98b4c..83f5e60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,8 @@ noinst_HEADERS = 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 +libcdecl_la_SOURCES = src/scan.c src/parse.c src/parse-decl.c src/typemap.c \ + src/explain.c bin_PROGRAMS = cdecl99 cdecl99_SOURCES = src/cdecl99.c diff --git a/src/cdecl.h b/src/cdecl.h index 90a8804..489be83 100644 --- a/src/cdecl.h +++ b/src/cdecl.h @@ -1,6 +1,8 @@ #ifndef CDECL_H_ #define CEDCL_H_ +#include + /* Declaration specifier kinds. */ enum { CDECL_SPEC_TYPE = 0, @@ -58,6 +60,8 @@ struct cdecl { 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); + static inline int cdecl_spec_kind(struct cdecl_declspec *spec) { return spec->type & ~0xffu; diff --git a/src/cdecl99.c b/src/cdecl99.c index 6e15712..a077191 100644 --- a/src/cdecl99.c +++ b/src/cdecl99.c @@ -36,11 +36,35 @@ static void print_help(void) static int cmd_explain(char *cmd, char *arg) { + static size_t bufsz; + static char *buf; + struct cdecl *decl; + size_t rc; decl = cdecl_parse_decl(arg); - cdecl_free(decl); + if (!decl) + goto out; + +retry: + rc = cdecl_explain(buf, bufsz, decl); + if (rc >= bufsz) { + char *tmp; + + tmp = realloc(buf, rc + 1); + if (!tmp) { + fprintf(stderr, "failed to allocate memory\n"); + return 1; + } + buf = tmp; + bufsz = rc + 1; + goto retry; + } + + printf("%s\n", buf); +out: + cdecl_free(decl); return 1; } diff --git a/src/explain.c b/src/explain.c new file mode 100644 index 0000000..d7edc74 --- /dev/null +++ b/src/explain.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include "cdecl.h" +#include "typemap.h" + +/* declare [ident] as [storage] [function specs] [other crap] [qualifiers] [type specs] */ + +static size_t +voutput(char *buf, size_t n, size_t off, const char *fmt, va_list ap) +{ + if (off >= n) + return vsnprintf(NULL, 0, fmt, ap); + return vsnprintf(buf+off, n-off, fmt, ap); +} + +static size_t output(char *buf, size_t n, size_t off, const char *fmt, ...) +{ + va_list ap; + size_t ret; + + va_start(ap, fmt); + ret = voutput(buf, n, off, fmt, ap); + va_end(ap); + + return ret; +} + +/* 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) +{ + unsigned long qualmap = 0, typemap; + const char *tag = NULL; + size_t ret = 0; + + typemap = cdecl__build_typemap(s); + if (typemap == -1) + return 0; + + for (struct cdecl_declspec *c = s; c; c = c->next) { + switch (cdecl_spec_kind(c)) { + case CDECL_SPEC_QUAL: + qualmap |= 1ul << (c->type & 0xff); + break; + case CDECL_SPEC_TYPE: + /* Valid C types have at most one identifier. */ + if (c->ident) + tag = c->ident; + break; + } + } + + if (qualmap & (1ul << (CDECL_QUAL_RESTRICT & 0xff))) + ret += output(buf, n, ret, "restrict "); + if (qualmap & (1ul << (CDECL_QUAL_VOLATILE & 0xff))) + ret += output(buf, n, ret, "volatile "); + if (qualmap & (1ul << (CDECL_QUAL_CONST & 0xff))) + ret += output(buf, n, ret, "const "); + + ret += output(buf, n, ret, "%s", cdecl__explain_typemap(typemap)); + if (tag) + ret += output(buf, n, ret, " %s", tag); + return ret; +} + +static const char *explain_storage(unsigned spec) +{ + switch (spec) { + case CDECL_STOR_TYPEDEF: + return "typedef"; + case CDECL_STOR_EXTERN: + return "extern"; + case CDECL_STOR_STATIC: + return "static"; + case CDECL_STOR_AUTO: + return "auto"; + case CDECL_STOR_REGISTER: + return "register"; + default: + abort(); + } +} + +/* 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; + const char *storage = NULL; + size_t ret = 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. */ + storage = explain_storage(c->type); + break; + } + } + + if (storage) + ret += output(buf, n, ret, "%s", storage); + if (funcmap & (1ul << (CDECL_FUNC_INLINE & 0xff))) + ret += output(buf, n, ret, "%.*s%s", !!ret, "", "inline"); + return ret; +} + +static size_t advance(char **buf, size_t *n, size_t amount) +{ + if (!amount) + return 0; + + if (amount >= *n) { + *n = 0; + *buf = 0; + } else { + (*buf)[amount] = ' '; + if (amount + 1 >= *n) { + *buf = 0; + *n = 0; + } else { + *buf += amount + 1; + *n -= amount + 1; + } + } + + return amount + 1; +} + +size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl) +{ + size_t ret = 0, rc; + + /* + * XXX: This won't work when we add support for more complicated + * declarators. + */ + rc = output(buf, n, 0, "declare %s as", decl->declarators->ident); + ret += advance(&buf, &n, rc); + + rc = explain_pre_specs(buf, n, decl->specifiers); + ret += advance(&buf, &n, rc); + + return ret + explain_post_specs(buf, n, decl->specifiers); +} -- 2.43.2