]> git.draconx.ca Git - cdecl99.git/blob - src/explain.c
Don't gratuitously reject inline specifiers.
[cdecl99.git] / src / explain.c
1 /*
2  *  Render C declarations as English.
3  *  Copyright © 2011 Nick Bowler
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <assert.h>
23
24 #include "cdecl.h"
25 #include "typemap.h"
26
27 static size_t advance_(char **buf, size_t *n, size_t amount)
28 {
29         if (amount >= *n) {
30                 *n   = 0;
31                 *buf = 0;
32         } else {
33                 *buf += amount;
34                 *n   -= amount;
35         }
36
37         return amount;
38 }
39
40 static size_t advance(char **buf, size_t *n, size_t amount)
41 {
42         size_t ret, rc;
43
44         if (!amount)
45                 return 0;
46
47         ret = advance_(buf, n, amount);
48         rc = snprintf(*buf, *n, " ");
49         return ret + advance_(buf, n, rc);
50 }
51
52 static size_t
53 explain_qualifiers(char *buf, size_t n, struct cdecl_declspec *s)
54 {
55         unsigned long qualmap = 0;
56         size_t ret = 0, rc = 0;
57
58         for (struct cdecl_declspec *c = s; c; c = c->next) {
59                 if (cdecl_spec_kind(c) != CDECL_SPEC_QUAL)
60                         continue;
61                 qualmap |= 1ul << (c->type & 0xff);
62         }
63
64         if (qualmap & (1ul << (CDECL_QUAL_RESTRICT & 0xff))) {
65                 ret += advance(&buf, &n, rc);
66                 rc = snprintf(buf, n, "restrict");
67         }
68         if (qualmap & (1ul << (CDECL_QUAL_VOLATILE & 0xff))) {
69                 ret += advance(&buf, &n, rc);
70                 rc = snprintf(buf, n, "volatile");
71         }
72         if (qualmap & (1ul << (CDECL_QUAL_CONST & 0xff))) {
73                 ret += advance(&buf, &n, rc);
74                 rc = snprintf(buf, n, "const");
75         }
76
77         return ret + rc;
78 }
79
80 /* Renders the type qualifiers and type specifiers in canonical form. */
81 static size_t
82 explain_post_specs(char *buf, size_t n, struct cdecl_declspec *s)
83 {
84         const char *tag = NULL;
85         unsigned long typemap;
86         size_t ret = 0, rc;
87
88         typemap = cdecl__build_typemap(s);
89         if (typemap == -1)
90                 return 0;
91
92         for (struct cdecl_declspec *c = s; c; c = c->next) {
93                 if (cdecl_spec_kind(c) != CDECL_SPEC_TYPE)
94                         continue;
95
96                 /* Valid C types have at most one identifier. */
97                 if (c->ident)
98                         tag = c->ident;
99         }
100
101         rc = explain_qualifiers(buf, n, s);
102         ret += advance(&buf, &n, rc);
103
104         rc = snprintf(buf, n, "%s", cdecl__explain_typemap(typemap));
105         if (tag) {
106                 ret += advance(&buf, &n, rc);
107                 rc = snprintf(buf, n, "%s", tag);
108         }
109
110         return ret + rc;
111 }
112
113 static size_t explain_storage(char *buf, size_t n, unsigned spec)
114 {
115         switch (spec) {
116         case CDECL_STOR_TYPEDEF:
117                 return snprintf(buf, n, "typedef");
118         case CDECL_STOR_EXTERN:
119                 return snprintf(buf, n, "extern");
120         case CDECL_STOR_STATIC:
121                 return snprintf(buf, n, "static");
122         case CDECL_STOR_AUTO:
123                 return snprintf(buf, n, "auto");
124         case CDECL_STOR_REGISTER:
125                 return snprintf(buf, n, "register");
126         default:
127                 assert(0);
128         }
129 }
130
131 /* Renders the storage-class and function specifiers in canonical form. */
132 static size_t explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s)
133 {
134         unsigned long funcmap = 0;
135         size_t ret = 0, rc = 0;
136
137         for (struct cdecl_declspec *c = s; c; c = c->next) {
138                 switch (cdecl_spec_kind(c)) {
139                 case CDECL_SPEC_FUNC:
140                         funcmap |= 1ul << (c->type & 0xff);
141                         break;
142                 case CDECL_SPEC_STOR:
143                         /* Valid C declarations have at most one
144                          * storage-class specifier. */
145                         rc = explain_storage(buf, n, c->type);
146                         break;
147                 }
148         }
149
150         if (funcmap & (1ul << (CDECL_FUNC_INLINE & 0xff))) {
151                 ret += advance(&buf, &n, rc);
152                 rc = snprintf(buf, n, "inline");
153         }
154
155         return ret + rc;
156 }
157
158 /*
159  * Renders the start of the thing being declared.  If top is true, print
160  * the "declare" or "type" keywords at the front, as appropriate.
161  */
162 static size_t
163 explain_prologue(char *buf, size_t n, struct cdecl_declarator *d, bool top)
164 {
165         size_t ret = 0, rc = 0;
166
167         while (d) {
168                 switch (d->type) {
169                 case CDECL_DECL_NULL:
170                         if (top)
171                                 return snprintf(buf, n, "type");
172                         return 0;
173                 case CDECL_DECL_IDENT:
174                         if (top)
175                                 rc = snprintf(buf, n, "declare");
176                         ret += advance(&buf, &n, rc);
177                         return ret + snprintf(buf, n, "%s as", d->u.ident);
178                 }
179
180                 d = d->child;
181         }
182 }
183
184 static size_t
185 explain_pointer(char *buf, size_t n, struct cdecl_pointer *p)
186 {
187         size_t ret = 0, rc;
188
189         rc = explain_qualifiers(buf, n, p->qualifiers);
190         ret += advance(&buf, &n, rc);
191
192         return ret + snprintf(buf, n, "pointer to");
193 }
194
195 static size_t
196 explain_array(char *buf, size_t n, struct cdecl_array *a)
197 {
198         size_t ret = 0, rc = 0;
199
200         if (a->vla)
201                 rc = snprintf(buf, n, "variable-length array");
202         else
203                 rc = snprintf(buf, n, "array");
204         ret += advance(&buf, &n, rc);
205
206         if (a->vla) {
207                 rc = snprintf(buf, n, "%s", a->vla);
208                 ret += advance(&buf, &n, rc);
209         } else if (a->length) {
210                 rc = snprintf(buf, n, "%ju", a->length);
211                 ret += advance(&buf, &n, rc);
212         }
213
214         return ret + snprintf(buf, n, "of");
215 }
216
217 static size_t
218 explain_declarators(char *buf, size_t n, struct cdecl_declarator *decl);
219
220 static size_t explain_decl(char *buf, size_t n, struct cdecl *decl, bool top)
221 {
222         size_t ret = 0, rc;
223
224         rc = explain_prologue(buf, n, decl->declarators, top);
225         ret += advance(&buf, &n, rc);
226
227         rc = explain_pre_specs(buf, n, decl->specifiers);
228         ret += advance(&buf, &n, rc);
229
230         rc = explain_declarators(buf, n, decl->declarators);
231         ret += advance(&buf, &n, rc);
232
233         return ret + explain_post_specs(buf, n, decl->specifiers);
234 }
235
236 static size_t explain_function(char *buf, size_t n, struct cdecl_function *f)
237 {
238         size_t ret = 0, rc = 0;
239
240         rc = snprintf(buf, n, "function");
241         ret += advance(&buf, &n, rc);
242
243         if (f->parameters) {
244                 rc = snprintf(buf, n, "(");
245                 ret += advance_(&buf, &n, rc);
246
247                 for (struct cdecl *p = f->parameters; p; p = p->next) {
248                         rc = explain_decl(buf, n, p, false);
249                         ret += advance_(&buf, &n, rc);
250
251                         if (p->next)
252                                 rc = snprintf(buf, n, ",");
253                         else if (f->variadic)
254                                 rc = snprintf(buf, n, ", ...)");
255                         else
256                                 rc = snprintf(buf, n, ")");
257                         ret += advance(&buf, &n, rc);
258                 }
259         }
260
261         return ret + snprintf(buf, n, "returning");
262 }
263
264 static size_t
265 explain_declarators(char *buf, size_t n, struct cdecl_declarator *d)
266 {
267         size_t ret = 0, rc;
268
269         if (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_NULL)
270                 return 0;
271
272         rc = explain_declarators(buf, n, d->child);
273         ret += advance(&buf, &n, rc);
274
275         switch (d->type) {
276         case CDECL_DECL_POINTER:
277                 return ret + explain_pointer(buf, n, &d->u.pointer);
278         case CDECL_DECL_ARRAY:
279                 return ret + explain_array(buf, n, &d->u.array);
280         case CDECL_DECL_FUNCTION:
281                 return ret + explain_function(buf, n, &d->u.function);
282         default:
283                 assert(0);
284         }
285 }
286
287 size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl)
288 {
289         return explain_decl(buf, n, decl, true);
290 }