From: Nick Bowler Date: Fri, 23 Jun 2023 01:21:46 +0000 (-0400) Subject: libcdecl: Rework cdecl_explain output logic. X-Git-Tag: v1.3~154 X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/commitdiff_plain/056280bf2e3188551539d87852ddbdaf322eae52 libcdecl: Rework cdecl_explain output logic. Instead of adjusting the output pointer/length values after each internal call, tweak all the functions in explain.c to take an extra level of indirection so they can just directly adjust the destination as they go. A new helper, cdecl__emit, is provided to simplify the common pattern of "print a single C string" followed by "advance pointer/length." --- diff --git a/src/cdecl-internal.h b/src/cdecl-internal.h index ab88489..0008c33 100644 --- a/src/cdecl-internal.h +++ b/src/cdecl-internal.h @@ -45,4 +45,6 @@ size_t cdecl__advance(char **buf, size_t *n, size_t amount); size_t cdecl__explain_specs(char *buf, size_t n, struct cdecl_declspec *s, unsigned mask); +size_t cdecl__emit(char **dst, size_t *dstlen, const char *src); + #endif diff --git a/src/explain.c b/src/explain.c index fe3ed7d..4deadac 100644 --- a/src/explain.c +++ b/src/explain.c @@ -18,135 +18,153 @@ #include #include -#include -#include #include -#include #include #include "cdecl.h" #include "cdecl-internal.h" -static size_t explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s) +#define PRE_SPECS (CDECL_SPEC_FUNC|CDECL_SPEC_STOR) +#define POST_SPECS (CDECL_SPEC_QUAL|CDECL_SPEC_TYPE) + +static size_t explain_specs(char **dst, size_t *dstlen, struct cdecl_declspec *s, unsigned mask) { - return cdecl__explain_specs(buf, n, s, CDECL_SPEC_FUNC|CDECL_SPEC_STOR); + size_t rc; + + rc = cdecl__explain_specs(*dst, *dstlen, s, mask); + return cdecl__advance(dst, dstlen, rc); } -static size_t explain_post_specs(char *buf, size_t n, struct cdecl_declspec *s) +static size_t +explain_specs_post(char **dst, size_t *dstlen, struct cdecl_declspec *s) { - return cdecl__explain_specs(buf, n, s, CDECL_SPEC_QUAL|CDECL_SPEC_TYPE); + size_t rc; + + rc = cdecl__explain_specs(*dst, *dstlen, s, POST_SPECS); + return cdecl__advance_(dst, dstlen, rc); } +/* + * If declarator declares an identifier foo, then emit "foo as "; otherwise + * emit nothing. + */ static size_t -explain_name(char *buf, size_t n, struct cdecl_declarator *d) +explain_name(char **dst, size_t *dstlen, struct cdecl_declarator *d) { + size_t ret; + while (d->child) d = d->child; if (d->type != CDECL_DECL_IDENT) return 0; - return snprintf(buf, n, "%s as", d->u.ident); + ret = cdecl__emit(dst, dstlen, d->u.ident); + return ret + cdecl__emit(dst, dstlen, " as "); } +/* + * For a pointer declarator, emit "[QUAL ]pointer to ", where + * QUAL is the (possibly empty) list of qualifiers. + */ static size_t -explain_pointer(char *buf, size_t n, struct cdecl_pointer *p) +explain_pointer(char **dst, size_t *dstlen, struct cdecl_pointer *p) { - size_t ret = 0, rc; - - rc = cdecl__explain_specs(buf, n, p->qualifiers, CDECL_SPEC_QUAL); - ret += cdecl__advance(&buf, &n, rc); + size_t ret; - return ret + snprintf(buf, n, "pointer to"); + ret = explain_specs(dst, dstlen, p->qualifiers, -1); + return ret + cdecl__emit(dst, dstlen, "pointer to "); } +/* + * For an array declarator, emit "[variable-length ]array [X ]of ", where + * for a variable-length array X is the (possibly omitted) identifier name, + * and for normal arrays X is the (possibly omitted) length. + */ static size_t -explain_array(char *buf, size_t n, struct cdecl_array *a) +explain_array(char **dst, size_t *dstlen, struct cdecl_array *a) { size_t ret = 0, rc = 0; if (a->vla) - rc = snprintf(buf, n, "variable-length array"); - else - rc = snprintf(buf, n, "array"); - ret += cdecl__advance(&buf, &n, rc); + ret += cdecl__emit(dst, dstlen, "variable-length "); + ret += cdecl__emit(dst, dstlen, "array "); if (a->vla) { - rc = snprintf(buf, n, "%s", a->vla); - ret += cdecl__advance(&buf, &n, rc); - } else if (a->length) { - rc = snprintf(buf, n, "%" PRIuMAX, a->length); - ret += cdecl__advance(&buf, &n, rc); + rc = cdecl__emit(dst, dstlen, a->vla); + ret += rc; + } else { + rc = snprintf(*dst, *dstlen, "%.0" PRIuMAX, a->length); + ret += cdecl__advance_(dst, dstlen, rc); } - return ret + snprintf(buf, n, "of"); + return ret + cdecl__emit(dst, dstlen, " of " + !rc); } static size_t -explain_declarators(char *buf, size_t n, struct cdecl_declarator *decl); +explain_declarators(char **dst, size_t *dstlen, struct cdecl_declarator *decl); -static size_t explain_decl(char *buf, size_t n, struct cdecl *decl) +static size_t explain_decl(char **dst, size_t *dstlen, struct cdecl *decl) { - size_t ret = 0, rc; - - rc = explain_name(buf, n, decl->declarators); - ret += cdecl__advance(&buf, &n, rc); - - rc = explain_pre_specs(buf, n, decl->specifiers); - ret += cdecl__advance(&buf, &n, rc); + size_t ret; - rc = explain_declarators(buf, n, decl->declarators); - ret += cdecl__advance(&buf, &n, rc); + ret = explain_name(dst, dstlen, decl->declarators); + ret += explain_specs(dst, dstlen, decl->specifiers, PRE_SPECS); + ret += explain_declarators(dst, dstlen, decl->declarators); + ret += explain_specs_post(dst, dstlen, decl->specifiers); - return ret + explain_post_specs(buf, n, decl->specifiers); + return ret; } -static size_t explain_function(char *buf, size_t n, struct cdecl_function *f) +/* + * For a function declarator, emit "function [PARAM ]returning ", where + * PARAM is the (omitted in the case of an empty non-prototype identifier + * list) parameter or identifier lists enclosed in parentheses. + */ +static size_t +explain_function(char **dst, size_t *dstlen, struct cdecl_function *f) { - size_t ret = 0, rc = 0; + size_t ret = 0; - rc = snprintf(buf, n, "function"); - ret += cdecl__advance(&buf, &n, rc); + ret += cdecl__emit(dst, dstlen, "function "); if (f->parameters) { - rc = snprintf(buf, n, "("); - ret += cdecl__advance_(&buf, &n, rc); + struct cdecl *p; - for (struct cdecl *p = f->parameters; p; p = p->next) { - rc = explain_decl(buf, n, p); - ret += cdecl__advance_(&buf, &n, rc); + ret += cdecl__emit(dst, dstlen, "("); + for (p = f->parameters; p; p = p->next) { + ret += explain_decl(dst, dstlen, p); 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); + ret += cdecl__emit(dst, dstlen, ", "); } + + if (f->variadic) + ret += cdecl__emit(dst, dstlen, ", ...) "); + else + ret += cdecl__emit(dst, dstlen, ") "); } - return ret + snprintf(buf, n, "returning"); + return ret + cdecl__emit(dst, dstlen, "returning "); } static size_t -explain_declarators(char *buf, size_t n, struct cdecl_declarator *d) +explain_declarators(char **dst, size_t *dstlen, struct cdecl_declarator *d) { - size_t ret = 0, rc; + size_t ret; if (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_NULL) return 0; - rc = explain_declarators(buf, n, d->child); - ret += cdecl__advance(&buf, &n, rc); + ret = explain_declarators(dst, dstlen, d->child); switch (d->type) { case CDECL_DECL_POINTER: - return ret + explain_pointer(buf, n, &d->u.pointer); + return ret + explain_pointer(dst, dstlen, &d->u.pointer); case CDECL_DECL_ARRAY: - return ret + explain_array(buf, n, &d->u.array); + return ret + explain_array(dst, dstlen, &d->u.array); case CDECL_DECL_FUNCTION: - return ret + explain_function(buf, n, &d->u.function); + return ret + explain_function(dst, dstlen, &d->u.function); default: assert(0); } @@ -154,13 +172,12 @@ explain_declarators(char *buf, size_t n, struct cdecl_declarator *d) size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl) { - size_t ret, rc; + size_t ret; if (cdecl_is_abstract(decl->declarators)) - rc = snprintf(buf, n, "type"); + ret = cdecl__emit(&buf, &n, "type "); else - rc = snprintf(buf, n, "declare"); - ret = cdecl__advance(&buf, &n, rc); + ret = cdecl__emit(&buf, &n, "declare "); - return ret + explain_decl(buf, n, decl); + return ret + explain_decl(&buf, &n, decl); } diff --git a/src/output.c b/src/output.c index a8d7ea5..d2359eb 100644 --- a/src/output.c +++ b/src/output.c @@ -47,6 +47,12 @@ size_t cdecl__advance(char **buf, size_t *n, size_t amount) return ret + cdecl__advance_(buf, n, rc); } +size_t cdecl__emit(char **dst, size_t *dstlen, const char *src) +{ + size_t rc = snprintf(*dst, *dstlen, "%s", src); + return cdecl__advance_(dst, dstlen, rc); +} + #include "specstr.h" static size_t explain_spec(char *buf, size_t n, struct cdecl_declspec *s)