15
|
1 #include <assert.h>
|
|
2 #include <stdbool.h>
|
|
3 #include <stdlib.h>
|
|
4 #include <string.h>
|
|
5
|
|
6 #include "utils.h"
|
|
7 #include "mode.h"
|
|
8 #include "place.h"
|
|
9 #include "files.h"
|
|
10 #include "directive.h"
|
|
11 #include "macro.h"
|
|
12 #include "eval.h"
|
|
13
|
|
14 struct ifstate {
|
|
15 struct ifstate *prev;
|
|
16 struct place startplace;
|
|
17 bool curtrue;
|
|
18 bool evertrue;
|
|
19 bool seenelse;
|
|
20 };
|
|
21
|
|
22 static struct ifstate *ifstate;
|
|
23
|
|
24 ////////////////////////////////////////////////////////////
|
|
25 // common parsing bits
|
|
26
|
|
27 static
|
|
28 void
|
|
29 oneword(const char *what, struct place *p2, char *line)
|
|
30 {
|
|
31 size_t pos;
|
|
32
|
|
33 pos = strcspn(line, ws);
|
|
34 if (line[pos] != '\0') {
|
|
35 p2->column += pos;
|
|
36 complain(p2, "Garbage after %s argument", what);
|
|
37 complain_fail();
|
|
38 line[pos] = '\0';
|
|
39 }
|
|
40 }
|
|
41
|
|
42 ////////////////////////////////////////////////////////////
|
|
43 // if handling
|
|
44
|
|
45 static
|
|
46 struct ifstate *
|
|
47 ifstate_create(struct ifstate *prev, struct place *p, bool startstate)
|
|
48 {
|
|
49 struct ifstate *is;
|
|
50
|
|
51 is = domalloc(sizeof(*is));
|
|
52 is->prev = prev;
|
|
53 if (p != NULL) {
|
|
54 is->startplace = *p;
|
|
55 } else {
|
|
56 place_setbuiltin(&is->startplace, 1);
|
|
57 }
|
|
58 is->curtrue = startstate;
|
|
59 is->evertrue = is->curtrue;
|
|
60 is->seenelse = false;
|
|
61 return is;
|
|
62 }
|
|
63
|
|
64 static
|
|
65 void
|
|
66 ifstate_destroy(struct ifstate *is)
|
|
67 {
|
|
68 free(is);
|
|
69 }
|
|
70
|
|
71 static
|
|
72 void
|
|
73 ifstate_push(struct place *p, bool startstate)
|
|
74 {
|
|
75 ifstate = ifstate_create(ifstate, p, startstate);
|
|
76 }
|
|
77
|
|
78 static
|
|
79 void
|
|
80 ifstate_pop(void)
|
|
81 {
|
|
82 struct ifstate *is;
|
|
83
|
|
84 is = ifstate;
|
|
85 ifstate = ifstate->prev;
|
|
86 ifstate_destroy(is);
|
|
87 }
|
|
88
|
|
89 static
|
|
90 void
|
|
91 d_if(struct place *p, struct place *p2, char *line, size_t len)
|
|
92 {
|
|
93 char *expr;
|
|
94 bool val;
|
16
|
95 struct place p3 = *p2;
|
15
|
96
|
|
97 expr = macroexpand(p2, line, len, true);
|
16
|
98 val = eval(&p3, expr);
|
15
|
99 ifstate_push(p, val);
|
|
100 free(expr);
|
|
101 }
|
|
102
|
|
103 static
|
|
104 void
|
|
105 d_ifdef(struct place *p, struct place *p2, char *line, size_t len)
|
|
106 {
|
|
107 oneword("#ifdef", p2, line);
|
|
108 ifstate_push(p, macro_isdefined(line));
|
|
109 }
|
|
110
|
|
111 static
|
|
112 void
|
|
113 d_ifndef(struct place *p, struct place *p2, char *line, size_t len)
|
|
114 {
|
|
115 oneword("#ifndef", p2, line);
|
|
116 ifstate_push(p, !macro_isdefined(line));
|
|
117 }
|
|
118
|
|
119 static
|
|
120 void
|
|
121 d_elif(struct place *p, struct place *p2, char *line, size_t len)
|
|
122 {
|
|
123 char *expr;
|
16
|
124 struct place p3 = *p2;
|
15
|
125
|
|
126 if (ifstate->seenelse) {
|
|
127 complain(p, "#elif after #else");
|
|
128 complain_fail();
|
|
129 }
|
|
130
|
|
131 if (ifstate->evertrue) {
|
|
132 ifstate->curtrue = false;
|
|
133 } else {
|
|
134 expr = macroexpand(p2, line, len, true);
|
16
|
135 ifstate->curtrue = eval(&p3, expr);
|
15
|
136 ifstate->evertrue = ifstate->curtrue;
|
|
137 free(expr);
|
|
138 }
|
|
139 }
|
|
140
|
|
141 static
|
|
142 void
|
|
143 d_else(struct place *p, struct place *p2, char *line, size_t len)
|
|
144 {
|
|
145 if (ifstate->seenelse) {
|
|
146 complain(p, "Multiple #else directives in one conditional");
|
|
147 complain_fail();
|
|
148 }
|
|
149
|
|
150 ifstate->curtrue = !ifstate->evertrue;
|
|
151 ifstate->evertrue = true;
|
|
152 ifstate->seenelse = true;
|
|
153 }
|
|
154
|
|
155 static
|
|
156 void
|
|
157 d_endif(struct place *p, struct place *p2, char *line, size_t len)
|
|
158 {
|
|
159 if (ifstate->prev == NULL) {
|
|
160 complain(p, "Unmatched #endif");
|
|
161 complain_fail();
|
|
162 } else {
|
|
163 ifstate_pop();
|
|
164 }
|
|
165 }
|
|
166
|
|
167 ////////////////////////////////////////////////////////////
|
|
168 // macros
|
|
169
|
|
170 static
|
|
171 void
|
|
172 d_define(struct place *p, struct place *p2, char *line, size_t len)
|
|
173 {
|
18
|
174 size_t pos, argpos;
|
|
175 struct place p3, p4;
|
15
|
176
|
|
177 /*
|
|
178 * line may be:
|
|
179 * macro expansion
|
|
180 * macro(arg, arg, ...) expansion
|
|
181 */
|
|
182
|
|
183 pos = strcspn(line, " \t\f\v(");
|
|
184 if (line[pos] == '(') {
|
18
|
185 line[pos++] = '\0';
|
|
186 argpos = pos;
|
15
|
187 pos = pos + strcspn(line+pos, "()");
|
|
188 if (line[pos] == '(') {
|
|
189 p2->column += pos;
|
|
190 complain(p2, "Left parenthesis in macro parameters");
|
|
191 complain_fail();
|
|
192 return;
|
|
193 }
|
|
194 if (line[pos] != ')') {
|
|
195 p2->column += pos;
|
|
196 complain(p2, "Unclosed macro parameter list");
|
|
197 complain_fail();
|
|
198 return;
|
|
199 }
|
18
|
200 line[pos++] = '\0';
|
15
|
201 if (!strchr(ws, line[pos])) {
|
|
202 p2->column += pos;
|
|
203 complain(p2, "Trash after macro parameter list");
|
|
204 complain_fail();
|
|
205 return;
|
|
206 }
|
|
207 } else if (line[pos] == '\0') {
|
18
|
208 argpos = 0;
|
15
|
209 } else {
|
|
210 line[pos++] = '\0';
|
18
|
211 argpos = 0;
|
15
|
212 }
|
|
213
|
|
214 pos += strspn(line+pos, ws);
|
|
215
|
|
216 p3 = *p2;
|
18
|
217 p3.column += argpos;
|
|
218
|
|
219 p4 = *p2;
|
|
220 p4.column += pos;
|
|
221
|
|
222 if (argpos) {
|
|
223 macro_define_params(p2, line, &p3,
|
|
224 line + argpos, &p4,
|
|
225 line + pos);
|
|
226 } else {
|
|
227 macro_define_plain(p2, line, &p4, line + pos);
|
|
228 }
|
15
|
229 }
|
|
230
|
|
231 static
|
|
232 void
|
|
233 d_undef(struct place *p, struct place *p2, char *line, size_t len)
|
|
234 {
|
|
235 oneword("#undef", p2, line);
|
|
236 macro_undef(line);
|
|
237 }
|
|
238
|
|
239 ////////////////////////////////////////////////////////////
|
|
240 // includes
|
|
241
|
|
242 static
|
|
243 bool
|
|
244 tryinclude(struct place *p, char *line, size_t len)
|
|
245 {
|
|
246 if (len > 2 && line[0] == '"' && line[len-1] == '"') {
|
|
247 line[len-1] = '\0';
|
|
248 file_readquote(p, line+1);
|
|
249 return true;
|
|
250 }
|
|
251 if (len > 2 && line[0] == '<' && line[len-1] == '>') {
|
|
252 line[len-1] = '\0';
|
|
253 file_readbracket(p, line+1);
|
|
254 return true;
|
|
255 }
|
|
256 return false;
|
|
257 }
|
|
258
|
|
259 static
|
|
260 void
|
|
261 d_include(struct place *p, struct place *p2, char *line, size_t len)
|
|
262 {
|
|
263 char *text;
|
|
264
|
|
265 if (tryinclude(p, line, len)) {
|
|
266 return;
|
|
267 }
|
|
268 text = macroexpand(p2, line, len, false);
|
|
269 if (tryinclude(p, text, strlen(text))) {
|
|
270 free(text);
|
|
271 return;
|
|
272 }
|
|
273 free(text);
|
|
274 complain(p, "Illegal #include directive");
|
|
275 complain_fail();
|
|
276 }
|
|
277
|
|
278 static
|
|
279 void
|
|
280 d_line(struct place *p, struct place *p2, char *line, size_t len)
|
|
281 {
|
|
282 /* XXX */
|
|
283 complain(p, "Sorry, no #line yet");
|
|
284 }
|
|
285
|
|
286 ////////////////////////////////////////////////////////////
|
|
287 // messages
|
|
288
|
|
289 static
|
|
290 void
|
|
291 d_warning(struct place *p, struct place *p2, char *line, size_t len)
|
|
292 {
|
|
293 char *msg;
|
|
294
|
|
295 msg = macroexpand(p2, line, len, false);
|
|
296 complain(p, "#warning: %s", msg);
|
|
297 if (mode.werror) {
|
|
298 complain_fail();
|
|
299 }
|
|
300 free(msg);
|
|
301 }
|
|
302
|
|
303 static
|
|
304 void
|
|
305 d_error(struct place *p, struct place *p2, char *line, size_t len)
|
|
306 {
|
|
307 char *msg;
|
|
308
|
|
309 msg = macroexpand(p2, line, len, false);
|
|
310 complain(p, "#error: %s", msg);
|
|
311 complain_fail();
|
|
312 free(msg);
|
|
313 }
|
|
314
|
|
315 ////////////////////////////////////////////////////////////
|
|
316 // other
|
|
317
|
|
318 static
|
|
319 void
|
|
320 d_pragma(struct place *p, struct place *p2, char *line, size_t len)
|
|
321 {
|
|
322 complain(p, "#pragma %s", line);
|
|
323 complain_fail();
|
|
324 }
|
|
325
|
|
326 ////////////////////////////////////////////////////////////
|
|
327 // directive table
|
|
328
|
|
329 static const struct {
|
|
330 const char *name;
|
|
331 bool ifskip;
|
|
332 void (*func)(struct place *, struct place *, char *line, size_t len);
|
|
333 } directives[] = {
|
|
334 { "define", true, d_define },
|
|
335 { "elif", false, d_elif },
|
|
336 { "else", false, d_else },
|
|
337 { "endif", false, d_endif },
|
|
338 { "error", true, d_error },
|
|
339 { "if", false, d_if },
|
|
340 { "ifdef", false, d_ifdef },
|
|
341 { "ifndef", false, d_ifndef },
|
|
342 { "include", true, d_include },
|
|
343 { "line", true, d_line },
|
|
344 { "pragma", true, d_pragma },
|
|
345 { "undef", true, d_undef },
|
|
346 { "warning", true, d_warning },
|
|
347 };
|
|
348 static const unsigned numdirectives = HOWMANY(directives);
|
|
349
|
|
350 static
|
|
351 void
|
|
352 directive_gotdirective(struct place *p, char *line, size_t linelen)
|
|
353 {
|
|
354 struct place p2;
|
|
355 size_t len, skip;
|
|
356 unsigned i;
|
|
357
|
|
358 p2 = *p;
|
|
359 for (i=0; i<numdirectives; i++) {
|
|
360 len = strlen(directives[i].name);
|
|
361 if (!strncmp(line, directives[i].name, len) &&
|
|
362 strchr(ws, line[len])) {
|
|
363 if (directives[i].ifskip && !ifstate->curtrue) {
|
|
364 return;
|
|
365 }
|
|
366 skip = len + strspn(line+len, ws);
|
|
367 p2.column += skip;
|
|
368 line += skip;
|
|
369 linelen -= skip;
|
|
370 linelen = notrailingws(line, linelen);
|
|
371 directives[i].func(p, &p2, line, linelen);
|
|
372 return;
|
|
373 }
|
|
374 }
|
|
375 skip = strcspn(line, ws);
|
|
376 complain(p, "Unknown directive #%.*s", (int)skip, line);
|
|
377 complain_fail();
|
|
378 }
|
|
379
|
|
380 void
|
|
381 directive_gotline(struct place *p, char *line, size_t len)
|
|
382 {
|
|
383 size_t skip;
|
|
384
|
|
385 /* check if we have a directive line */
|
|
386 skip = strspn(line, ws);
|
|
387 if (line[skip] == '#') {
|
|
388 skip = skip + 1 + strspn(line + skip + 1, ws);
|
|
389 p->column += skip;
|
|
390 directive_gotdirective(p, line+skip, len-skip);
|
|
391 } else if (ifstate->curtrue) {
|
|
392 macro_sendline(p, line, len);
|
|
393 }
|
|
394 }
|
|
395
|
|
396
|
|
397 void
|
|
398 directive_goteof(struct place *p)
|
|
399 {
|
|
400 while (ifstate->prev != NULL) {
|
|
401 complain(p, "Missing #endif");
|
|
402 complain(&ifstate->startplace, "...opened at this point");
|
|
403 complain_failed();
|
|
404 ifstate_pop();
|
|
405 }
|
|
406 macro_sendeof(p);
|
|
407 }
|
|
408
|
|
409 ////////////////////////////////////////////////////////////
|
|
410 // module initialization
|
|
411
|
|
412 void
|
|
413 directive_init(void)
|
|
414 {
|
|
415 ifstate = ifstate_create(NULL, NULL, true);
|
|
416 }
|
|
417
|
|
418 void
|
|
419 directive_cleanup(void)
|
|
420 {
|
|
421 assert(ifstate->prev == NULL);
|
|
422 ifstate_destroy(ifstate);
|
|
423 ifstate = NULL;
|
|
424 }
|