]> git.draconx.ca Git - cdecl99.git/blob - src/explain.c
libcdecl: Rework cdecl_declare output logic.
[cdecl99.git] / src / explain.c
1 /*
2  *  Render C declarations as English.
3  *  Copyright © 2011, 2021, 2023 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
19 #include <config.h>
20 #include <stdio.h>
21 #include <inttypes.h>
22 #include <assert.h>
23
24 #include "cdecl.h"
25 #include "cdecl-internal.h"
26
27 #define PRE_SPECS (CDECL_SPEC_FUNC|CDECL_SPEC_STOR)
28 #define POST_SPECS (CDECL_SPEC_QUAL|CDECL_SPEC_TYPE)
29
30 static size_t explain_specs(char **dst, size_t *dstlen, struct cdecl_declspec *s, unsigned mask)
31 {
32         size_t rc;
33
34         rc = cdecl__explain_specs(*dst, *dstlen, s, mask);
35         return cdecl__advance(dst, dstlen, rc);
36 }
37
38 static size_t
39 explain_specs_post(char **dst, size_t *dstlen, struct cdecl_declspec *s)
40 {
41         size_t rc;
42
43         rc = cdecl__explain_specs(*dst, *dstlen, s, POST_SPECS);
44         return cdecl__advance_(dst, dstlen, rc);
45 }
46
47 /*
48  * If declarator declares an identifier foo, then emit "foo as "; otherwise
49  * emit nothing.
50  */
51 static size_t
52 explain_name(char **dst, size_t *dstlen, struct cdecl_declarator *d)
53 {
54         size_t ret;
55
56         while (d->child)
57                 d = d->child;
58
59         if (d->type != CDECL_DECL_IDENT)
60                 return 0;
61
62         ret = cdecl__emit(dst, dstlen, d->u.ident);
63         return ret + cdecl__emit(dst, dstlen, " as ");
64 }
65
66 /*
67  * For a pointer declarator, emit "[QUAL ]pointer to ", where
68  * QUAL is the (possibly empty) list of qualifiers.
69  */
70 static size_t
71 explain_pointer(char **dst, size_t *dstlen, struct cdecl_pointer *p)
72 {
73         size_t ret;
74
75         ret = explain_specs(dst, dstlen, p->qualifiers, -1);
76         return ret + cdecl__emit(dst, dstlen, "pointer to ");
77 }
78
79 /*
80  * For an array declarator, emit "[variable-length ]array [X ]of ", where
81  * for a variable-length array X is the (possibly omitted) identifier name,
82  * and for normal arrays X is the (possibly omitted) length.
83  */
84 static size_t
85 explain_array(char **dst, size_t *dstlen, struct cdecl_array *a)
86 {
87         size_t ret = 0, rc = 0;
88
89         if (a->vla)
90                 ret += cdecl__emit(dst, dstlen, "variable-length ");
91         ret += cdecl__emit(dst, dstlen, "array ");
92
93         if (a->vla) {
94                 rc = cdecl__emit(dst, dstlen, a->vla);
95                 ret += rc;
96         } else {
97                 rc = snprintf(*dst, *dstlen, "%.0" PRIuMAX, a->length);
98                 ret += cdecl__advance_(dst, dstlen, rc);
99         }
100
101         return ret + cdecl__emit(dst, dstlen, " of " + !rc);
102 }
103
104 static size_t
105 explain_declarators(char **dst, size_t *dstlen, struct cdecl_declarator *decl);
106
107 static size_t explain_decl(char **dst, size_t *dstlen, struct cdecl *decl)
108 {
109         size_t ret;
110
111         ret  = explain_name(dst, dstlen, decl->declarators);
112         ret += explain_specs(dst, dstlen, decl->specifiers, PRE_SPECS);
113         ret += explain_declarators(dst, dstlen, decl->declarators);
114         ret += explain_specs_post(dst, dstlen, decl->specifiers);
115
116         return ret;
117 }
118
119 /*
120  * For a function declarator, emit "function [PARAM ]returning ", where
121  * PARAM is the (omitted in the case of an empty non-prototype identifier
122  * list) parameter or identifier lists enclosed in parentheses.
123  */
124 static size_t
125 explain_function(char **dst, size_t *dstlen, struct cdecl_function *f)
126 {
127         size_t ret = 0;
128
129         ret += cdecl__emit(dst, dstlen, "function ");
130
131         if (f->parameters) {
132                 struct cdecl *p;
133
134                 ret += cdecl__emit(dst, dstlen, "(");
135
136                 for (p = f->parameters; p; p = p->next) {
137                         ret += explain_decl(dst, dstlen, p);
138                         if (p->next)
139                                 ret += cdecl__emit(dst, dstlen, ", ");
140                 }
141
142                 if (f->variadic)
143                         ret += cdecl__emit(dst, dstlen, ", ...) ");
144                 else
145                         ret += cdecl__emit(dst, dstlen, ") ");
146         }
147
148         return ret + cdecl__emit(dst, dstlen, "returning ");
149 }
150
151 static size_t
152 explain_declarators(char **dst, size_t *dstlen, struct cdecl_declarator *d)
153 {
154         size_t ret;
155
156         if (d->type == CDECL_DECL_IDENT || d->type == CDECL_DECL_NULL)
157                 return 0;
158
159         ret = explain_declarators(dst, dstlen, d->child);
160
161         switch (d->type) {
162         case CDECL_DECL_POINTER:
163                 return ret + explain_pointer(dst, dstlen, &d->u.pointer);
164         case CDECL_DECL_ARRAY:
165                 return ret + explain_array(dst, dstlen, &d->u.array);
166         case CDECL_DECL_FUNCTION:
167                 return ret + explain_function(dst, dstlen, &d->u.function);
168         default:
169                 assert(0);
170         }
171 }
172
173 size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl)
174 {
175         size_t ret;
176
177         if (cdecl_is_abstract(decl->declarators))
178                 ret = cdecl__emit(&buf, &n, "type ");
179         else
180                 ret = cdecl__emit(&buf, &n, "declare ");
181
182         return ret + explain_decl(&buf, &n, decl);
183 }