X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/c92cdbdde6876aa06ad07828c8c315c7967128d5..6f166c6c36bda84191ed916a52b311ab417a4890:/src/explain.c diff --git a/src/explain.c b/src/explain.c index d7edc74..fe3ed7d 100644 --- a/src/explain.c +++ b/src/explain.c @@ -1,149 +1,166 @@ +/* + * Render C declarations as English. + * Copyright © 2011, 2021, 2023 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 . + */ + +#include #include #include #include +#include +#include +#include + #include "cdecl.h" -#include "typemap.h" +#include "cdecl-internal.h" -/* declare [ident] as [storage] [function specs] [other crap] [qualifiers] [type specs] */ +static size_t explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s) +{ + return cdecl__explain_specs(buf, n, s, CDECL_SPEC_FUNC|CDECL_SPEC_STOR); +} -static size_t -voutput(char *buf, size_t n, size_t off, const char *fmt, va_list ap) +static size_t explain_post_specs(char *buf, size_t n, struct cdecl_declspec *s) { - if (off >= n) - return vsnprintf(NULL, 0, fmt, ap); - return vsnprintf(buf+off, n-off, fmt, ap); + return cdecl__explain_specs(buf, n, s, CDECL_SPEC_QUAL|CDECL_SPEC_TYPE); } -static size_t output(char *buf, size_t n, size_t off, const char *fmt, ...) +static size_t +explain_name(char *buf, size_t n, struct cdecl_declarator *d) { - va_list ap; - size_t ret; + while (d->child) + d = d->child; - va_start(ap, fmt); - ret = voutput(buf, n, off, fmt, ap); - va_end(ap); + if (d->type != CDECL_DECL_IDENT) + return 0; - return ret; + return snprintf(buf, n, "%s as", d->u.ident); } -/* 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) +explain_pointer(char *buf, size_t n, struct cdecl_pointer *p) { - unsigned long qualmap = 0, typemap; - const char *tag = NULL; - size_t ret = 0; + size_t ret = 0, rc; - typemap = cdecl__build_typemap(s); - if (typemap == -1) - return 0; + rc = cdecl__explain_specs(buf, n, p->qualifiers, CDECL_SPEC_QUAL); + ret += cdecl__advance(&buf, &n, rc); - 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; - } + return ret + snprintf(buf, n, "pointer to"); +} + +static size_t +explain_array(char *buf, size_t n, 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); + + 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); } - 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; + return ret + snprintf(buf, n, "of"); } -static const char *explain_storage(unsigned spec) +static size_t +explain_declarators(char *buf, size_t n, struct cdecl_declarator *decl); + +static size_t explain_decl(char *buf, size_t n, struct cdecl *decl) { - 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(); - } + 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); + + rc = explain_declarators(buf, n, decl->declarators); + ret += cdecl__advance(&buf, &n, rc); + + return ret + explain_post_specs(buf, n, decl->specifiers); } -/* 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) +static size_t explain_function(char *buf, size_t n, struct cdecl_function *f) { - 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; + size_t ret = 0, rc = 0; + + rc = snprintf(buf, n, "function"); + ret += cdecl__advance(&buf, &n, rc); + + if (f->parameters) { + rc = snprintf(buf, n, "("); + ret += cdecl__advance_(&buf, &n, rc); + + for (struct cdecl *p = f->parameters; p; p = p->next) { + rc = explain_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); } } - 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; + return ret + snprintf(buf, n, "returning"); } -static size_t advance(char **buf, size_t *n, size_t amount) +static size_t +explain_declarators(char *buf, size_t n, struct cdecl_declarator *d) { - if (!amount) + size_t ret = 0, rc; + + if (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_NULL) 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; - } - } + rc = explain_declarators(buf, n, d->child); + ret += cdecl__advance(&buf, &n, rc); - return amount + 1; + switch (d->type) { + case CDECL_DECL_POINTER: + return ret + explain_pointer(buf, n, &d->u.pointer); + case CDECL_DECL_ARRAY: + return ret + explain_array(buf, n, &d->u.array); + case CDECL_DECL_FUNCTION: + return ret + explain_function(buf, n, &d->u.function); + default: + assert(0); + } } 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); + size_t ret, rc; - rc = explain_pre_specs(buf, n, decl->specifiers); - ret += advance(&buf, &n, rc); + if (cdecl_is_abstract(decl->declarators)) + rc = snprintf(buf, n, "type"); + else + rc = snprintf(buf, n, "declare"); + ret = cdecl__advance(&buf, &n, rc); - return ret + explain_post_specs(buf, n, decl->specifiers); + return ret + explain_decl(buf, n, decl); }