]> git.draconx.ca Git - cdecl99.git/blob - src/declare.c
libcdecl: Rework cdecl_explain output logic.
[cdecl99.git] / src / declare.c
1 /*
2  *  Render C declarations.
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 <stdbool.h>
22 #include <assert.h>
23
24 #include "cdecl.h"
25 #include "cdecl-internal.h"
26
27 static size_t
28 declare_declarator(char *buf, size_t n, struct cdecl_declarator *d);
29
30 static size_t declare_decl(char *buf, size_t n, struct cdecl *decl)
31 {
32         size_t ret = 0, rc;
33
34         rc = cdecl__explain_specs(buf, n, decl->specifiers, -1);
35         if (decl->declarators->type != CDECL_DECL_NULL)
36                 ret += cdecl__advance(&buf, &n, rc);
37         else
38                 ret += cdecl__advance_(&buf, &n, rc);
39
40         return ret + declare_declarator(buf, n, decl->declarators);
41 }
42
43 static size_t
44 declare_postfix_child(char *buf, size_t n, struct cdecl_declarator *d)
45 {
46         size_t ret = 0, rc;
47
48         if (d->type == CDECL_DECL_POINTER) {
49                 rc = snprintf(buf, n, "(");
50                 ret += cdecl__advance_(&buf, &n, rc);
51         }
52
53         rc = declare_declarator(buf, n, d);
54         ret += cdecl__advance_(&buf, &n, rc);
55
56         if (d->type == CDECL_DECL_POINTER) {
57                 rc = snprintf(buf, n, ")");
58                 ret += cdecl__advance_(&buf, &n, rc);
59         }
60
61         return ret;
62 }
63
64 static size_t declare_pointer(char *buf, size_t n, struct cdecl_pointer *p)
65 {
66         size_t ret = 0, rc;
67
68         rc = snprintf(buf, n, "*");
69         if (p->qualifiers)
70                 ret += cdecl__advance(&buf, &n, rc);
71         else
72                 ret += cdecl__advance_(&buf, &n, rc);
73
74         rc = cdecl__explain_specs(buf, n, p->qualifiers, CDECL_SPEC_QUAL);
75         return ret + rc;
76 }
77
78 static bool pointer_needs_space(struct cdecl_declarator *d)
79 {
80         assert(d->type == CDECL_DECL_POINTER);
81
82         if (d->u.pointer.qualifiers)
83                 return d->child->type != CDECL_DECL_NULL;
84
85         return false;
86 }
87
88 static size_t declare_array(char *buf, size_t n, struct cdecl_array *a)
89 {
90         size_t ret = 0, rc;
91
92         rc = snprintf(buf, n, "[");
93         ret += cdecl__advance_(&buf, &n, rc);
94
95         if (a->vla)
96                 rc = snprintf(buf, n, "%s", a->vla[0] ? a->vla : "*");
97         else
98                 rc = snprintf(buf, n, "%.0ju", a->length);
99         ret += cdecl__advance_(&buf, &n, rc);
100
101         return ret + snprintf(buf, n, "]");
102 }
103
104 static size_t declare_function(char *buf, size_t n, struct cdecl_function *f)
105 {
106         size_t ret = 0, rc;
107
108         rc = snprintf(buf, n, "(");
109         ret += cdecl__advance_(&buf, &n, rc);
110
111         for (struct cdecl *p = f->parameters; p; p = p->next) {
112                 rc = declare_decl(buf, n, p);
113                 ret += cdecl__advance_(&buf, &n, rc);
114
115                 rc = 0;
116                 if (p->next)
117                         rc = snprintf(buf, n, ", ");
118                 else if (f->variadic)
119                         rc = snprintf(buf, n, ", ...");
120                 ret += cdecl__advance_(&buf, &n, rc);
121         }
122
123         return ret + snprintf(buf, n, ")");
124 }
125
126 static size_t
127 declare_declarator(char *buf, size_t n, struct cdecl_declarator *d)
128 {
129         size_t ret = 0, rc;
130
131         for (; d; d = d->child) {
132                 switch (d->type) {
133                 case CDECL_DECL_NULL:
134                         break;
135                 case CDECL_DECL_IDENT:
136                         rc = snprintf(buf, n, "%s", d->u.ident);
137                         ret += cdecl__advance_(&buf, &n, rc);
138                         break;
139                 case CDECL_DECL_POINTER:
140                         rc = declare_pointer(buf, n, &d->u.pointer);
141                         if (pointer_needs_space(d))
142                                 ret += cdecl__advance(&buf, &n, rc);
143                         else
144                                 ret += cdecl__advance_(&buf, &n, rc);
145                         break;
146                 /*
147                  * Arrays and functions are special: since they are postfix,
148                  * we need to render the children before rendering their
149                  * "bodies".
150                  */
151                 case CDECL_DECL_ARRAY:
152                         rc = declare_postfix_child(buf, n, d->child);
153                         ret += cdecl__advance_(&buf, &n, rc);
154                         return ret + declare_array(buf, n, &d->u.array);
155                 case CDECL_DECL_FUNCTION:
156                         rc = declare_postfix_child(buf, n, d->child);
157                         ret += cdecl__advance_(&buf, &n, rc);
158                         return ret + declare_function(buf, n, &d->u.function);
159                 default:
160                         assert(0);
161                 }
162         }
163
164         return ret;
165 }
166
167 size_t cdecl_declare(char *buf, size_t n, struct cdecl *decl)
168 {
169         return declare_decl(buf, n, decl);
170 }