X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/blobdiff_plain/fc08e91e04cff34f5e3c6510223d79fbf0826b19..9cb5aae31e1c126970843ef9b6e54036b185fd51:/src/explain.c diff --git a/src/explain.c b/src/explain.c index b0e9a84..10a7064 100644 --- a/src/explain.c +++ b/src/explain.c @@ -1,246 +1,147 @@ -#include -#include -#include -#include +/* + * Render C declarations as English. + * Copyright © 2011, 2021, 2023-2024 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 "cdecl.h" -#include "typemap.h" +#include "cdecl-internal.h" -/* declare [ident] as [storage] [function specs] [other crap] [qualifiers] [type specs] */ +#define PRE_SPECS (CDECL_SPEC_FUNC|CDECL_SPEC_STOR) +#define POST_SPECS (CDECL_SPEC_QUAL|CDECL_SPEC_TYPE) -static size_t -voutput(char *buf, size_t n, size_t off, const char *fmt, va_list ap) +static void explain_specs(struct output_state *dst, struct cdecl_declspec *s, unsigned mask) { - if (off >= n) - return vsnprintf(NULL, 0, fmt, ap); - return vsnprintf(buf+off, n-off, fmt, ap); + cdecl__emit(dst, cdecl__emit_specs(dst, s, mask)); } -static size_t output(char *buf, size_t n, size_t off, const char *fmt, ...) +/* + * If declarator declares an identifier foo, then emit "foo as "; otherwise + * emit nothing. + */ +static void +explain_name(struct output_state *dst, struct cdecl_declarator *d) { - va_list ap; - size_t ret; - - va_start(ap, fmt); - ret = voutput(buf, n, off, fmt, ap); - va_end(ap); - - return ret; -} + while (d->child) + d = d->child; -static size_t advance_(char **buf, size_t *n, size_t amount) -{ - if (amount >= *n) { - *n = 0; - *buf = 0; - } else { - *buf += amount; - *n -= amount; + if (d->type == CDECL_DECL_IDENT) { + cdecl__emit(dst, d->u.ident); + cdecl__emit(dst, " as "); } - - return amount; } -static size_t advance(char **buf, size_t *n, size_t amount) +/* + * For a pointer declarator, emit "[QUAL ]pointer to ", where + * QUAL is the (possibly empty) list of qualifiers. + */ +static void +explain_pointer(struct output_state *dst, struct cdecl_pointer *p) { - size_t ret, rc; - - if (!amount) - return 0; - - ret = advance_(buf, n, amount); - rc = snprintf(*buf, *n, " "); - return ret + advance_(buf, n, rc); -} - -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 += advance(&buf, &n, rc); - rc = snprintf(buf, n, "restrict"); - } - if (qualmap & (1ul << (CDECL_QUAL_VOLATILE & 0xff))) { - ret += advance(&buf, &n, rc); - rc = snprintf(buf, n, "volatile"); - } - if (qualmap & (1ul << (CDECL_QUAL_CONST & 0xff))) { - ret += advance(&buf, &n, rc); - rc = snprintf(buf, n, "const"); - } - - return ret + rc; + explain_specs(dst, p->qualifiers, -1); + cdecl__emit(dst, "pointer to "); } -/* 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) +/* + * 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 void +explain_array(struct output_state *dst, struct cdecl_array *a) { - 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 += advance(&buf, &n, rc); - - rc = snprintf(buf, n, "%s", cdecl__explain_typemap(typemap)); - if (tag) { - ret += advance(&buf, &n, rc); - rc = snprintf(buf, n, "%s", tag); - } + size_t rc = 0; - return ret + rc; -} - -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: - assert(0); + cdecl__emit(dst, "variable-length array " + (a->vla ? 0 : 16)); + if (a->vla) { + rc = cdecl__emit(dst, a->vla); + } else { + rc = cdecl__emit_uint(dst, a->length); } + cdecl__emit(dst, " of " + !rc); } -/* 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 void +explain_declarators(struct output_state *dst, struct cdecl_declarator *decl); -/* Renders the name of the thing being declared. */ -static size_t -explain_prologue(char *buf, size_t n, struct cdecl_declarator *d) +static void explain_decl(struct output_state *dst, struct cdecl *decl) { - while (d) { - switch (d->type) { - case CDECL_DECL_NULL: - return snprintf(buf, n, "type"); - case CDECL_DECL_IDENT: - return snprintf(buf, n, "declare %s as", d->u.ident); - } - - d = d->child; - } + explain_name(dst, decl->declarators); + explain_specs(dst, decl->specifiers, PRE_SPECS); + explain_declarators(dst, decl->declarators); + cdecl__emit_specs(dst, decl->specifiers, POST_SPECS); } -static size_t -explain_pointer(char *buf, size_t n, struct cdecl_pointer *p) +/* + * 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 void +explain_function(struct output_state *dst, struct cdecl_function *f) { - size_t ret = 0, rc; - - rc = explain_qualifiers(buf, n, p->qualifiers); - ret += advance(&buf, &n, rc); + int tail_offset = 7; - return ret + snprintf(buf, n, "pointer to"); -} + cdecl__emit(dst, "function "); + if (f->parameters) { + struct cdecl *p; -static size_t -explain_array(char *buf, size_t n, struct cdecl_array *a) -{ - size_t ret = 0, rc = 0; + cdecl__emit(dst, "("); - if (a->vla) - rc = snprintf(buf, n, "variable-length array"); - else - rc = snprintf(buf, n, "array"); - ret += advance(&buf, &n, rc); + for (p = f->parameters; p; p = p->next) { + explain_decl(dst, p); + if (p->next) + cdecl__emit(dst, ", "); + } - if (a->vla) { - rc = snprintf(buf, n, "%s", a->vla); - ret += advance(&buf, &n, rc); - } else if (a->length) { - rc = snprintf(buf, n, "%ju", a->length); - ret += advance(&buf, &n, rc); + tail_offset = f->variadic ? 0 : 5; } - - return ret + snprintf(buf, n, "of"); + cdecl__emit(dst, ", ...) returning " + tail_offset); } -static size_t -explain_declarators(char *buf, size_t n, struct cdecl_declarator *d) +static void +explain_declarators(struct output_state *dst, struct cdecl_declarator *d) { - size_t ret = 0, rc; - - if (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_NULL) - return 0; - - rc = explain_declarators(buf, n, d->child); - ret += advance(&buf, &n, rc); + if (d->child) + explain_declarators(dst, d->child); switch (d->type) { case CDECL_DECL_POINTER: - return ret + explain_pointer(buf, n, &d->u.pointer); + explain_pointer(dst, &d->u.pointer); + return; case CDECL_DECL_ARRAY: - return ret + explain_array(buf, n, &d->u.array); - default: - assert(0); + explain_array(dst, &d->u.array); + return; + case CDECL_DECL_FUNCTION: + explain_function(dst, &d->u.function); + return; } } size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl) { - size_t ret = 0, rc; + struct output_state dst = { buf, n }; - rc = explain_prologue(buf, n, decl->declarators); - ret += advance(&buf, &n, rc); - - rc = explain_pre_specs(buf, n, decl->specifiers); - ret += advance(&buf, &n, rc); - - rc = explain_declarators(buf, n, decl->declarators); - ret += advance(&buf, &n, rc); + if (cdecl_is_abstract(decl->declarators)) + cdecl__emit(&dst, "type "); + else + cdecl__emit(&dst, "declare "); - return ret + explain_post_specs(buf, n, decl->specifiers); + explain_decl(&dst, decl); + return dst.accum; }