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