Mercurial > projects > ldc
annotate dmd/macro.c @ 650:aa6a0b7968f7
Added test case for bug #100
Removed dubious check for not emitting static private global in other modules without access. This should be handled properly somewhere else, it's causing unresolved global errors for stuff that should work (in MiniD)
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Sun, 05 Oct 2008 17:28:15 +0200 |
parents | cbd6c8073a32 |
children | b30fe7e1dbb9 |
rev | line source |
---|---|
1 | 1 |
2 // Copyright (c) 1999-2006 by Digital Mars | |
3 // All Rights Reserved | |
4 // written by Walter Bright | |
5 // http://www.digitalmars.com | |
6 // License for redistribution is by either the Artistic License | |
7 // in artistic.txt, or the GNU General Public License in gnu.txt. | |
8 // See the included readme.txt for details. | |
9 | |
10 /* Simple macro text processor. | |
11 */ | |
12 | |
13 #include <stdio.h> | |
14 #include <string.h> | |
15 #include <time.h> | |
16 #include <ctype.h> | |
17 #include <assert.h> | |
18 | |
19 #if IN_GCC || IN_LLVM | |
20 #include "mem.h" | |
21 #else | |
22 #if _WIN32 | |
23 #include "..\root\mem.h" | |
571
cbd6c8073a32
Changed all '#if linux || __APPLE__' to '#if POSIX' so we can support other platforms too, thanx for the suggestion anders.
Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
parents:
535
diff
changeset
|
24 #elif POSIX |
1 | 25 #include "../root/mem.h" |
26 #else | |
27 #error "fix this" | |
28 #endif | |
29 #endif | |
30 | |
31 #include "root.h" | |
32 #include "macro.h" | |
33 | |
34 #define isidstart(c) (isalpha(c) || (c) == '_') | |
35 #define isidchar(c) (isalnum(c) || (c) == '_') | |
36 | |
37 unsigned char *memdup(unsigned char *p, size_t len) | |
38 { | |
39 return (unsigned char *)memcpy(mem.malloc(len), p, len); | |
40 } | |
41 | |
42 Macro::Macro(unsigned char *name, size_t namelen, unsigned char *text, size_t textlen) | |
43 { | |
44 next = NULL; | |
45 | |
46 #if 1 | |
47 this->name = name; | |
48 this->namelen = namelen; | |
49 | |
50 this->text = text; | |
51 this->textlen = textlen; | |
52 #else | |
53 this->name = name; | |
54 this->namelen = namelen; | |
55 | |
56 this->text = text; | |
57 this->textlen = textlen; | |
58 #endif | |
59 inuse = 0; | |
60 } | |
61 | |
62 | |
63 Macro *Macro::search(unsigned char *name, size_t namelen) | |
64 { Macro *table; | |
65 | |
66 //printf("Macro::search(%.*s)\n", namelen, name); | |
67 for (table = this; table; table = table->next) | |
68 { | |
69 if (table->namelen == namelen && | |
70 memcmp(table->name, name, namelen) == 0) | |
71 { | |
72 //printf("\tfound %d\n", table->textlen); | |
73 break; | |
74 } | |
75 } | |
76 return table; | |
77 } | |
78 | |
79 Macro *Macro::define(Macro **ptable, unsigned char *name, size_t namelen, unsigned char *text, size_t textlen) | |
80 { | |
81 //printf("Macro::define('%.*s' = '%.*s')\n", namelen, name, textlen, text); | |
82 | |
83 Macro *table; | |
84 | |
85 //assert(ptable); | |
86 for (table = *ptable; table; table = table->next) | |
87 { | |
88 if (table->namelen == namelen && | |
89 memcmp(table->name, name, namelen) == 0) | |
90 { | |
91 table->text = text; | |
92 table->textlen = textlen; | |
93 return table; | |
94 } | |
95 } | |
96 table = new Macro(name, namelen, text, textlen); | |
97 table->next = *ptable; | |
98 *ptable = table; | |
99 return table; | |
100 } | |
101 | |
102 /********************************************************** | |
103 * Given buffer p[0..end], extract argument marg[0..marglen]. | |
104 * Params: | |
105 * n 0: get entire argument | |
106 * 1..9: get nth argument | |
107 * -1: get 2nd through end | |
108 */ | |
109 | |
110 unsigned extractArgN(unsigned char *p, unsigned end, unsigned char **pmarg, unsigned *pmarglen, int n) | |
111 { | |
112 /* Scan forward for matching right parenthesis. | |
113 * Nest parentheses. | |
114 * Skip over $( and $) | |
115 * Skip over "..." and '...' strings inside HTML tags. | |
116 * Skip over <!-- ... --> comments. | |
117 * Skip over previous macro insertions | |
118 * Set marglen. | |
119 */ | |
120 unsigned parens = 1; | |
121 unsigned char instring = 0; | |
122 unsigned incomment = 0; | |
123 unsigned intag = 0; | |
124 unsigned inexp = 0; | |
125 unsigned argn = 0; | |
126 | |
127 unsigned v = 0; | |
128 | |
129 Largstart: | |
130 #if 1 | |
131 // Skip first space, if any, to find the start of the macro argument | |
132 if (v < end && isspace(p[v])) | |
133 v++; | |
134 #else | |
135 // Skip past spaces to find the start of the macro argument | |
136 for (; v < end && isspace(p[v]); v++) | |
137 ; | |
138 #endif | |
139 *pmarg = p + v; | |
140 | |
141 for (; v < end; v++) | |
142 { unsigned char c = p[v]; | |
143 | |
144 switch (c) | |
145 { | |
146 case ',': | |
147 if (!inexp && !instring && !incomment && parens == 1) | |
148 { | |
149 argn++; | |
150 if (argn == 1 && n == -1) | |
151 { v++; | |
152 goto Largstart; | |
153 } | |
154 if (argn == n) | |
155 break; | |
156 if (argn + 1 == n) | |
157 { v++; | |
158 goto Largstart; | |
159 } | |
160 } | |
161 continue; | |
162 | |
163 case '(': | |
164 if (!inexp && !instring && !incomment) | |
165 parens++; | |
166 continue; | |
167 | |
168 case ')': | |
169 if (!inexp && !instring && !incomment && --parens == 0) | |
170 { | |
171 break; | |
172 } | |
173 continue; | |
174 | |
175 case '"': | |
176 case '\'': | |
177 if (!inexp && !incomment && intag) | |
178 { | |
179 if (c == instring) | |
180 instring = 0; | |
181 else if (!instring) | |
182 instring = c; | |
183 } | |
184 continue; | |
185 | |
186 case '<': | |
187 if (!inexp && !instring && !incomment) | |
188 { | |
189 if (v + 6 < end && | |
190 p[v + 1] == '!' && | |
191 p[v + 2] == '-' && | |
192 p[v + 3] == '-') | |
193 { | |
194 incomment = 1; | |
195 v += 3; | |
196 } | |
197 else if (v + 2 < end && | |
198 isalpha(p[v + 1])) | |
199 intag = 1; | |
200 } | |
201 continue; | |
202 | |
203 case '>': | |
204 if (!inexp) | |
205 intag = 0; | |
206 continue; | |
207 | |
208 case '-': | |
209 if (!inexp && | |
210 !instring && | |
211 incomment && | |
212 v + 2 < end && | |
213 p[v + 1] == '-' && | |
214 p[v + 2] == '>') | |
215 { | |
216 incomment = 0; | |
217 v += 2; | |
218 } | |
219 continue; | |
220 | |
221 case 0xFF: | |
222 if (v + 1 < end) | |
223 { | |
224 if (p[v + 1] == '{') | |
225 inexp++; | |
226 else if (p[v + 1] == '}') | |
227 inexp--; | |
228 } | |
229 continue; | |
230 | |
231 default: | |
232 continue; | |
233 } | |
234 break; | |
235 } | |
236 if (argn == 0 && n == -1) | |
237 *pmarg = p + v; | |
238 *pmarglen = p + v - *pmarg; | |
239 //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg); | |
240 return v; | |
241 } | |
242 | |
243 | |
244 /***************************************************** | |
245 * Expand macro in place in buf. | |
246 * Only look at the text in buf from start to end. | |
247 */ | |
248 | |
249 void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, | |
250 unsigned char *arg, unsigned arglen) | |
251 { | |
252 #if 0 | |
253 printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, *pend, arglen, arg); | |
254 printf("Buf is: '%.*s'\n", *pend - start, buf->data + start); | |
255 #endif | |
256 | |
257 static int nest; | |
258 if (nest > 100) // limit recursive expansion | |
259 return; | |
260 nest++; | |
261 | |
262 unsigned end = *pend; | |
263 assert(start <= end); | |
264 assert(end <= buf->offset); | |
265 | |
266 /* First pass - replace $0 | |
267 */ | |
268 arg = memdup(arg, arglen); | |
269 for (unsigned u = start; u + 1 < end; ) | |
270 { | |
271 unsigned char *p = buf->data; // buf->data is not loop invariant | |
272 | |
273 /* Look for $0, but not $$0, and replace it with arg. | |
274 */ | |
275 if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+')) | |
276 { | |
277 if (u > start && p[u - 1] == '$') | |
278 { // Don't expand $$0, but replace it with $0 | |
279 buf->remove(u - 1, 1); | |
280 end--; | |
281 u += 1; // now u is one past the closing '1' | |
282 continue; | |
283 } | |
284 | |
285 unsigned char c = p[u + 1]; | |
286 int n = (c == '+') ? -1 : c - '0'; | |
287 | |
288 unsigned char *marg; | |
289 unsigned marglen; | |
290 extractArgN(arg, arglen, &marg, &marglen, n); | |
291 if (marglen == 0) | |
292 { // Just remove macro invocation | |
293 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); | |
294 buf->remove(u, 2); | |
295 end -= 2; | |
296 } | |
297 else if (c == '+') | |
298 { | |
299 // Replace '$+' with 'arg' | |
300 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); | |
301 buf->remove(u, 2); | |
302 buf->insert(u, marg, marglen); | |
303 end += marglen - 2; | |
304 | |
305 // Scan replaced text for further expansion | |
306 unsigned mend = u + marglen; | |
307 expand(buf, u, &mend, NULL, 0); | |
308 end += mend - (u + marglen); | |
309 u = mend; | |
310 } | |
311 else | |
312 { | |
313 // Replace '$1' with '\xFF{arg\xFF}' | |
314 //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg); | |
315 buf->data[u] = 0xFF; | |
316 buf->data[u + 1] = '{'; | |
317 buf->insert(u + 2, marg, marglen); | |
318 buf->insert(u + 2 + marglen, "\xFF}", 2); | |
319 end += -2 + 2 + marglen + 2; | |
320 | |
321 // Scan replaced text for further expansion | |
322 unsigned mend = u + 2 + marglen; | |
323 expand(buf, u + 2, &mend, NULL, 0); | |
324 end += mend - (u + 2 + marglen); | |
325 u = mend; | |
326 } | |
327 //printf("u = %d, end = %d\n", u, end); | |
328 //printf("#%.*s#\n", end, &buf->data[0]); | |
329 continue; | |
330 } | |
331 | |
332 u++; | |
333 } | |
334 | |
335 /* Second pass - replace other macros | |
336 */ | |
337 for (unsigned u = start; u + 4 < end; ) | |
338 { | |
339 unsigned char *p = buf->data; // buf->data is not loop invariant | |
340 | |
341 /* A valid start of macro expansion is $(c, where c is | |
342 * an id start character, and not $$(c. | |
343 */ | |
344 if (p[u] == '$' && p[u + 1] == '(' && isidstart(p[u + 2])) | |
345 { | |
346 //printf("\tfound macro start '%c'\n", p[u + 2]); | |
347 unsigned char *name = p + u + 2; | |
348 unsigned namelen = 0; | |
349 | |
350 unsigned char *marg; | |
351 unsigned marglen; | |
352 | |
353 unsigned v; | |
354 /* Scan forward to find end of macro name and | |
355 * beginning of macro argument (marg). | |
356 */ | |
357 for (v = u + 2; v < end; v++) | |
358 { unsigned char c = p[v]; | |
359 | |
360 if (!isidchar(c)) | |
361 { // We've gone past the end of the macro name. | |
362 namelen = v - (u + 2); | |
363 break; | |
364 } | |
365 } | |
366 | |
367 v += extractArgN(p + v, end - v, &marg, &marglen, 0); | |
368 assert(v <= end); | |
369 | |
370 if (v < end) | |
371 { // v is on the closing ')' | |
372 if (u > start && p[u - 1] == '$') | |
373 { // Don't expand $$(NAME), but replace it with $(NAME) | |
374 buf->remove(u - 1, 1); | |
375 end--; | |
376 u = v; // now u is one past the closing ')' | |
377 continue; | |
378 } | |
379 | |
380 Macro *m = search(name, namelen); | |
381 if (m) | |
382 { | |
383 #if 0 | |
384 if (m->textlen && m->text[0] == ' ') | |
385 { m->text++; | |
386 m->textlen--; | |
387 } | |
388 #endif | |
389 if (m->inuse && marglen == 0) | |
390 { // Remove macro invocation | |
391 buf->remove(u, v + 1 - u); | |
392 end -= v + 1 - u; | |
393 } | |
394 else if (m->inuse && arglen == marglen && memcmp(arg, marg, arglen) == 0) | |
395 { // Recursive expansion; just leave in place | |
396 | |
397 } | |
398 else | |
399 { | |
400 //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m->namelen, m->name, marglen, marg, m->textlen, m->text); | |
401 #if 1 | |
402 marg = memdup(marg, marglen); | |
403 // Insert replacement text | |
404 buf->spread(v + 1, 2 + m->textlen + 2); | |
405 buf->data[v + 1] = 0xFF; | |
406 buf->data[v + 2] = '{'; | |
407 memcpy(buf->data + v + 3, m->text, m->textlen); | |
408 buf->data[v + 3 + m->textlen] = 0xFF; | |
409 buf->data[v + 3 + m->textlen + 1] = '}'; | |
410 | |
411 end += 2 + m->textlen + 2; | |
412 | |
413 // Scan replaced text for further expansion | |
414 m->inuse++; | |
415 unsigned mend = v + 1 + 2+m->textlen+2; | |
416 expand(buf, v + 1, &mend, marg, marglen); | |
417 end += mend - (v + 1 + 2+m->textlen+2); | |
418 m->inuse--; | |
419 | |
420 buf->remove(u, v + 1 - u); | |
421 end -= v + 1 - u; | |
422 u += mend - (v + 1); | |
423 #else | |
424 // Insert replacement text | |
425 buf->insert(v + 1, m->text, m->textlen); | |
426 end += m->textlen; | |
427 | |
428 // Scan replaced text for further expansion | |
429 m->inuse++; | |
430 unsigned mend = v + 1 + m->textlen; | |
431 expand(buf, v + 1, &mend, marg, marglen); | |
432 end += mend - (v + 1 + m->textlen); | |
433 m->inuse--; | |
434 | |
435 buf->remove(u, v + 1 - u); | |
436 end -= v + 1 - u; | |
437 u += mend - (v + 1); | |
438 #endif | |
439 mem.free(marg); | |
440 //printf("u = %d, end = %d\n", u, end); | |
441 //printf("#%.*s#\n", end - u, &buf->data[u]); | |
442 continue; | |
443 } | |
444 } | |
445 else | |
446 { | |
447 // Replace $(NAME) with nothing | |
448 buf->remove(u, v + 1 - u); | |
449 end -= (v + 1 - u); | |
450 continue; | |
451 } | |
452 } | |
453 } | |
454 u++; | |
455 } | |
456 mem.free(arg); | |
457 *pend = end; | |
458 nest--; | |
459 } |