comparison dmd2/macro.c @ 758:f04dde6e882c

Added initial D2 support, D2 frontend and changes to codegen to make things compile.
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Tue, 11 Nov 2008 01:38:48 +0100
parents
children 638d16625da2
comparison
equal deleted inserted replaced
757:2c730d530c98 758:f04dde6e882c
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"
24 #elif POSIX
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 }