/* * 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 "cdecl-internal.h" #define PRE_SPECS (CDECL_SPEC_FUNC|CDECL_SPEC_STOR) #define POST_SPECS (CDECL_SPEC_QUAL|CDECL_SPEC_TYPE) static void explain_specs(struct output_state *dst, struct cdecl_declspec *s, unsigned mask) { cdecl__emit(dst, cdecl__emit_specs(dst, s, mask)); } /* * 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) { while (d->child) d = d->child; if (d->type == CDECL_DECL_IDENT) { cdecl__emit(dst, d->u.ident); cdecl__emit(dst, " as "); } } /* * 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) { explain_specs(dst, p->qualifiers, -1); 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 void explain_array(struct output_state *dst, struct cdecl_array *a) { size_t rc = 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); } static void explain_declarators(struct output_state *dst, struct cdecl_declarator *decl); static void explain_decl(struct output_state *dst, struct cdecl *decl) { 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); } /* * 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) { int tail_offset = 7; cdecl__emit(dst, "function "); if (f->parameters) { struct cdecl *p; cdecl__emit(dst, "("); for (p = f->parameters; p; p = p->next) { explain_decl(dst, p); if (p->next) cdecl__emit(dst, ", "); } tail_offset = f->variadic ? 0 : 5; } cdecl__emit(dst, ", ...) returning " + tail_offset); } static void explain_declarators(struct output_state *dst, struct cdecl_declarator *d) { if (d->child) explain_declarators(dst, d->child); switch (d->type) { case CDECL_DECL_POINTER: explain_pointer(dst, &d->u.pointer); return; case CDECL_DECL_ARRAY: 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) { struct output_state dst = { buf, n }; if (cdecl_is_abstract(decl->declarators)) cdecl__emit(&dst, "type "); else cdecl__emit(&dst, "declare "); explain_decl(&dst, decl); return dst.accum; }