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"
|
|
24 #elif linux
|
|
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 }
|