/* * Render C declarations as English. * Copyright © 2011 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 "cdecl.h" #include "typemap.h" static size_t advance_(char **buf, size_t *n, size_t amount) { if (amount >= *n) { *n = 0; *buf = 0; } else { *buf += amount; *n -= amount; } return amount; } static size_t advance(char **buf, size_t *n, size_t amount) { 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; } /* 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) { 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); } return ret + rc; } static size_t explain_storage(char *buf, size_t n, unsigned spec) { switch (spec) { case CDECL_STOR_TYPEDEF: return snprintf(buf, n, "typedef"); case CDECL_STOR_EXTERN: return snprintf(buf, n, "extern"); case CDECL_STOR_STATIC: return snprintf(buf, n, "static"); case CDECL_STOR_AUTO: return snprintf(buf, n, "auto"); case CDECL_STOR_REGISTER: return snprintf(buf, n, "register"); default: assert(0); } } /* 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; size_t ret = 0, rc = 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. */ rc = explain_storage(buf, n, c->type); break; } } if (funcmap & (1ul << (CDECL_FUNC_INLINE & 0xff))) { ret += advance(&buf, &n, rc); rc = snprintf(buf, n, "inline"); } return ret + rc; } /* * Renders the start of the thing being declared. If top is true, print * the "declare" or "type" keywords at the front, as appropriate. */ static size_t explain_prologue(char *buf, size_t n, struct cdecl_declarator *d, bool top) { size_t ret = 0, rc = 0; while (d) { switch (d->type) { case CDECL_DECL_NULL: if (top) return snprintf(buf, n, "type"); return 0; case CDECL_DECL_IDENT: if (top) rc = snprintf(buf, n, "declare"); ret += advance(&buf, &n, rc); return ret + snprintf(buf, n, "%s as", d->u.ident); } d = d->child; } } static size_t explain_pointer(char *buf, size_t n, struct cdecl_pointer *p) { size_t ret = 0, rc; rc = explain_qualifiers(buf, n, p->qualifiers); ret += advance(&buf, &n, rc); 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 += advance(&buf, &n, rc); 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); } return ret + snprintf(buf, n, "of"); } 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, bool top) { size_t ret = 0, rc; rc = explain_prologue(buf, n, decl->declarators, top); 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); return ret + explain_post_specs(buf, n, decl->specifiers); } static size_t explain_function(char *buf, size_t n, struct cdecl_function *f) { size_t ret = 0, rc = 0; rc = snprintf(buf, n, "function"); ret += advance(&buf, &n, rc); if (f->parameters) { rc = snprintf(buf, n, "("); ret += advance_(&buf, &n, rc); for (struct cdecl *p = f->parameters; p; p = p->next) { rc = explain_decl(buf, n, p, false); ret += 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 += advance(&buf, &n, rc); } } return ret + snprintf(buf, n, "returning"); } static size_t explain_declarators(char *buf, size_t n, 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); 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) { return explain_decl(buf, n, decl, true); }