Mercurial > ~dholland > hg > tradcpp > index.cgi
comparison directive.c @ 15:f6177d3ed5c2
handle directives
author | David A. Holland |
---|---|
date | Sun, 19 Dec 2010 21:42:01 -0500 |
parents | |
children | 9dda765ee85c |
comparison
equal
deleted
inserted
replaced
14:5045b9678bb0 | 15:f6177d3ed5c2 |
---|---|
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 } |