/* * 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 "cdecl.h" #include "cdecl-internal.h" #define PRE_SPECS (CDECL_SPEC_FUNC|CDECL_SPEC_STOR) #define POST_SPECS (CDECL_SPEC_QUAL|CDECL_SPEC_TYPE) static size_t explain_specs(struct output_state *dst, struct cdecl_declspec *s, unsigned mask) { size_t ret; ret = cdecl__emit_specs(dst, s, mask); return ret + cdecl__emit(dst, " " + !ret); } static size_t explain_specs_post(struct output_state *dst, struct cdecl_declspec *s) { return cdecl__emit_specs(dst, s, POST_SPECS); } /* * If declarator declares an identifier foo, then emit "foo as "; otherwise * emit nothing. */ static size_t explain_name(struct output_state *dst, struct cdecl_declarator *d) { size_t ret; while (d->child) d = d->child; if (d->type != CDECL_DECL_IDENT) return 0; ret = cdecl__emit(dst, d->u.ident); return ret + cdecl__emit(dst, " as "); } /* * For a pointer declarator, emit "[QUAL ]pointer to ", where * QUAL is the (possibly empty) list of qualifiers. */ static size_t explain_pointer(struct output_state *dst, struct cdecl_pointer *p) { size_t ret; ret = explain_specs(dst, p->qualifiers, -1); return ret + cdecl__emit(dst, "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(struct output_state *dst, struct cdecl_array *a) { size_t ret = 0, rc = 0; if (a->vla) ret += cdecl__emit(dst, "variable-length "); ret += cdecl__emit(dst, "array "); if (a->vla) { rc = cdecl__emit(dst, a->vla); ret += rc; } else { rc = snprintf(dst->dst, dst->dstlen, "%.0" PRIuMAX, a->length); ret += cdecl__advance(dst, rc); } return ret + cdecl__emit(dst, " of " + !rc); } static size_t explain_declarators(struct output_state *dst, struct cdecl_declarator *decl); static size_t explain_decl(struct output_state *dst, struct cdecl *decl) { size_t ret; ret = explain_name(dst, decl->declarators); ret += explain_specs(dst, decl->specifiers, PRE_SPECS); ret += explain_declarators(dst, decl->declarators); ret += explain_specs_post(dst, decl->specifiers); return ret; } /* * 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(struct output_state *dst, struct cdecl_function *f) { size_t ret = 0; ret += cdecl__emit(dst, "function "); if (f->parameters) { struct cdecl *p; ret += cdecl__emit(dst, "("); for (p = f->parameters; p; p = p->next) { ret += explain_decl(dst, p); if (p->next) ret += cdecl__emit(dst, ", "); } if (f->variadic) ret += cdecl__emit(dst, ", ...) "); else ret += cdecl__emit(dst, ") "); } return ret + cdecl__emit(dst, "returning "); } static size_t explain_declarators(struct output_state *dst, struct cdecl_declarator *d) { size_t ret; if (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_NULL) return 0; ret = explain_declarators(dst, d->child); switch (d->type) { case CDECL_DECL_POINTER: return ret + explain_pointer(dst, &d->u.pointer); case CDECL_DECL_ARRAY: return ret + explain_array(dst, &d->u.array); case CDECL_DECL_FUNCTION: return ret + explain_function(dst, &d->u.function); default: assert(0); } } size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl) { struct output_state dst = { buf, n }; size_t ret; if (cdecl_is_abstract(decl->declarators)) ret = cdecl__emit(&dst, "type "); else ret = cdecl__emit(&dst, "declare "); return ret + explain_decl(&dst, decl); }