2 // Copyright (c) 1999-2006 by Digital Mars
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.
10 /* Simple macro text processor.
23 #include "..\root\mem.h"
25 #include "../root/mem.h"
34 #define isidstart(c) (isalpha(c) || (c) == '_')
35 #define isidchar(c) (isalnum(c) || (c) == '_')
37 unsigned char *memdup(unsigned char *p
, size_t len
)
39 return (unsigned char *)memcpy(mem
.malloc(len
), p
, len
);
42 Macro::Macro(unsigned char *name
, size_t namelen
, unsigned char *text
, size_t textlen
)
48 this->namelen
= namelen
;
51 this->textlen
= textlen
;
54 this->namelen
= namelen
;
57 this->textlen
= textlen
;
63 Macro
*Macro::search(unsigned char *name
, size_t namelen
)
66 //printf("Macro::search(%.*s)\n", namelen, name);
67 for (table
= this; table
; table
= table
->next
)
69 if (table
->namelen
== namelen
&&
70 memcmp(table
->name
, name
, namelen
) == 0)
72 //printf("\tfound %d\n", table->textlen);
79 Macro
*Macro::define(Macro
**ptable
, unsigned char *name
, size_t namelen
, unsigned char *text
, size_t textlen
)
81 //printf("Macro::define('%.*s' = '%.*s')\n", namelen, name, textlen, text);
86 for (table
= *ptable
; table
; table
= table
->next
)
88 if (table
->namelen
== namelen
&&
89 memcmp(table
->name
, name
, namelen
) == 0)
92 table
->textlen
= textlen
;
96 table
= new Macro(name
, namelen
, text
, textlen
);
97 table
->next
= *ptable
;
102 /**********************************************************
103 * Given buffer p[0..end], extract argument marg[0..marglen].
105 * n 0: get entire argument
106 * 1..9: get nth argument
107 * -1: get 2nd through end
110 unsigned extractArgN(unsigned char *p
, unsigned end
, unsigned char **pmarg
, unsigned *pmarglen
, int n
)
112 /* Scan forward for matching right parenthesis.
114 * Skip over $( and $)
115 * Skip over "..." and '...' strings inside HTML tags.
116 * Skip over <!-- ... --> comments.
117 * Skip over previous macro insertions
121 unsigned char instring
= 0;
122 unsigned incomment
= 0;
131 // Skip first space, if any, to find the start of the macro argument
132 if (v
< end
&& isspace(p
[v
]))
135 // Skip past spaces to find the start of the macro argument
136 for (; v
< end
&& isspace(p
[v
]); v
++)
142 { unsigned char c
= p
[v
];
147 if (!inexp
&& !instring
&& !incomment
&& parens
== 1)
150 if (argn
== 1 && n
== -1)
164 if (!inexp
&& !instring
&& !incomment
)
169 if (!inexp
&& !instring
&& !incomment
&& --parens
== 0)
177 if (!inexp
&& !incomment
&& intag
)
187 if (!inexp
&& !instring
&& !incomment
)
197 else if (v
+ 2 < end
&&
226 else if (p
[v
+ 1] == '}')
236 if (argn
== 0 && n
== -1)
238 *pmarglen
= p
+ v
- *pmarg
;
239 //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg);
244 /*****************************************************
245 * Expand macro in place in buf.
246 * Only look at the text in buf from start to end.
249 void Macro::expand(OutBuffer
*buf
, unsigned start
, unsigned *pend
,
250 unsigned char *arg
, unsigned arglen
)
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
);
258 if (nest
> 100) // limit recursive expansion
262 unsigned end
= *pend
;
263 assert(start
<= end
);
264 assert(end
<= buf
->offset
);
266 /* First pass - replace $0
268 arg
= memdup(arg
, arglen
);
269 for (unsigned u
= start
; u
+ 1 < end
; )
271 unsigned char *p
= buf
->data
; // buf->data is not loop invariant
273 /* Look for $0, but not $$0, and replace it with arg.
275 if (p
[u
] == '$' && (isdigit(p
[u
+ 1]) || p
[u
+ 1] == '+'))
277 if (u
> start
&& p
[u
- 1] == '$')
278 { // Don't expand $$0, but replace it with $0
279 buf
->remove(u
- 1, 1);
281 u
+= 1; // now u is one past the closing '1'
285 unsigned char c
= p
[u
+ 1];
286 int n
= (c
== '+') ? -1 : c
- '0';
290 extractArgN(arg
, arglen
, &marg
, &marglen
, n
);
292 { // Just remove macro invocation
293 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
299 // Replace '$+' with 'arg'
300 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
302 buf
->insert(u
, marg
, marglen
);
305 // Scan replaced text for further expansion
306 unsigned mend
= u
+ marglen
;
307 expand(buf
, u
, &mend
, NULL
, 0);
308 end
+= mend
- (u
+ marglen
);
313 // Replace '$1' with '\xFF{arg\xFF}'
314 //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg);
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;
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
);
327 //printf("u = %d, end = %d\n", u, end);
328 //printf("#%.*s#\n", end, &buf->data[0]);
335 /* Second pass - replace other macros
337 for (unsigned u
= start
; u
+ 4 < end
; )
339 unsigned char *p
= buf
->data
; // buf->data is not loop invariant
341 /* A valid start of macro expansion is $(c, where c is
342 * an id start character, and not $$(c.
344 if (p
[u
] == '$' && p
[u
+ 1] == '(' && isidstart(p
[u
+ 2]))
346 //printf("\tfound macro start '%c'\n", p[u + 2]);
347 unsigned char *name
= p
+ u
+ 2;
348 unsigned namelen
= 0;
354 /* Scan forward to find end of macro name and
355 * beginning of macro argument (marg).
357 for (v
= u
+ 2; v
< end
; v
++)
358 { unsigned char c
= p
[v
];
361 { // We've gone past the end of the macro name.
362 namelen
= v
- (u
+ 2);
367 v
+= extractArgN(p
+ v
, end
- v
, &marg
, &marglen
, 0);
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);
376 u
= v
; // now u is one past the closing ')'
380 Macro
*m
= search(name
, namelen
);
384 if (m
->textlen
&& m
->text
[0] == ' ')
389 if (m
->inuse
&& marglen
== 0)
390 { // Remove macro invocation
391 buf
->remove(u
, v
+ 1 - u
);
394 else if (m
->inuse
&& arglen
== marglen
&& memcmp(arg
, marg
, arglen
) == 0)
395 { // Recursive expansion; just leave in place
400 //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m->namelen, m->name, marglen, marg, m->textlen, m->text);
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] = '}';
411 end
+= 2 + m
->textlen
+ 2;
413 // Scan replaced text for further expansion
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);
420 buf
->remove(u
, v
+ 1 - u
);
424 // Insert replacement text
425 buf
->insert(v
+ 1, m
->text
, m
->textlen
);
428 // Scan replaced text for further expansion
430 unsigned mend
= v
+ 1 + m
->textlen
;
431 expand(buf
, v
+ 1, &mend
, marg
, marglen
);
432 end
+= mend
- (v
+ 1 + m
->textlen
);
435 buf
->remove(u
, v
+ 1 - u
);
440 //printf("u = %d, end = %d\n", u, end);
441 //printf("#%.*s#\n", end - u, &buf->data[u]);
447 // Replace $(NAME) with nothing
448 buf
->remove(u
, v
+ 1 - u
);