Internally these would treat the cast same as a normal conversion from int[7] to int[], which allows code at CTFE to erroneously succeed where it would raise a SEGV at run-time.
1 |
/**
|
|
2 |
* Text macro processor for Ddoc.
|
|
3 |
*
|
|
4 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
5 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
6 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
7 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmacro.d, _dmacro.d)
|
|
8 |
* Documentation: https://dlang.org/phobos/dmd_dmacro.html
|
|
9 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmacro.d
|
|
10 |
*/
|
|
11 |
|
|
12 |
module dmd.dmacro; |
|
13 |
|
|
14 |
import core.stdc.ctype; |
|
15 |
import core.stdc.string; |
|
16 |
import dmd.doc; |
|
17 |
import dmd.errors; |
|
18 |
import dmd.globals; |
|
19 |
import dmd.root.outbuffer; |
|
20 |
import dmd.root.rmem; |
|
21 |
|
|
22 |
extern (C++) struct MacroTable |
|
23 |
{
|
|
24 |
/**********************************
|
|
25 |
* Define name=text macro.
|
|
26 |
* If macro `name` already exists, replace the text for it.
|
|
27 |
* Params:
|
|
28 |
* name = name of macro
|
|
29 |
* text = text of macro
|
|
30 |
*/
|
|
31 |
extern (D) void define(const(char)[] name, const(char)[] text) |
|
32 |
{
|
|
33 |
//printf("MacroTable::define('%.*s' = '%.*s')\n", cast(int)name.length, name.ptr, text.length, text.ptr);
|
|
34 | 1 |
Macro* table; |
35 | 1 |
for (table = mactab; table; table = table.next) |
36 |
{
|
|
37 | 1 |
if (table.name == name) |
38 |
{
|
|
39 | 1 |
table.text = text; |
40 | 1 |
return; |
41 |
}
|
|
42 |
}
|
|
43 | 1 |
table = new Macro(name, text); |
44 | 1 |
table.next = mactab; |
45 | 1 |
mactab = table; |
46 |
}
|
|
47 |
|
|
48 |
/*****************************************************
|
|
49 |
* Look for macros in buf and expand them in place.
|
|
50 |
* Only look at the text in buf from start to pend.
|
|
51 |
*/
|
|
52 |
extern (D) void expand(ref OutBuffer buf, size_t start, ref size_t pend, const(char)[] arg) |
|
53 |
{
|
|
54 |
version (none) |
|
55 |
{
|
|
56 |
printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, pend, cast(int)arg.length, arg.ptr); |
|
57 |
printf("Buf is: '%.*s'\n", cast(int)(pend - start), buf.data + start); |
|
58 |
}
|
|
59 |
// limit recursive expansion
|
|
60 | 1 |
__gshared int nest; |
61 | 1 |
if (nest > global.recursionLimit) |
62 |
{
|
|
63 |
error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", |
|
64 |
global.recursionLimit); |
|
65 |
return; |
|
66 |
}
|
|
67 | 1 |
nest++; |
68 | 1 |
size_t end = pend; |
69 | 1 |
assert(start <= end); |
70 | 1 |
assert(end <= buf.length); |
71 |
/* First pass - replace $0
|
|
72 |
*/
|
|
73 | 1 |
arg = memdup(arg); |
74 | 1 |
for (size_t u = start; u + 1 < end;) |
75 |
{
|
|
76 | 1 |
char* p = cast(char*)buf[].ptr; // buf.data is not loop invariant |
77 |
/* Look for $0, but not $$0, and replace it with arg.
|
|
78 |
*/
|
|
79 | 1 |
if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+')) |
80 |
{
|
|
81 | 1 |
if (u > start && p[u - 1] == '$') |
82 |
{
|
|
83 |
// Don't expand $$0, but replace it with $0
|
|
84 |
buf.remove(u - 1, 1); |
|
85 |
end--; |
|
86 |
u += 1; // now u is one past the closing '1' |
|
87 |
continue; |
|
88 |
}
|
|
89 | 1 |
char c = p[u + 1]; |
90 | 1 |
int n = (c == '+') ? -1 : c - '0'; |
91 | 1 |
const(char)[] marg; |
92 | 1 |
if (n == 0) |
93 |
{
|
|
94 | 1 |
marg = arg; |
95 |
}
|
|
96 |
else
|
|
97 | 1 |
extractArgN(arg, marg, n); |
98 | 1 |
if (marg.length == 0) |
99 |
{
|
|
100 |
// Just remove macro invocation
|
|
101 |
//printf("Replacing '$%c' with '%.*s'\n", p[u + 1], cast(int)marg.length, marg.ptr);
|
|
102 | 1 |
buf.remove(u, 2); |
103 | 1 |
end -= 2; |
104 |
}
|
|
105 | 1 |
else if (c == '+') |
106 |
{
|
|
107 |
// Replace '$+' with 'arg'
|
|
108 |
//printf("Replacing '$%c' with '%.*s'\n", p[u + 1], cast(int)marg.length, marg.ptr);
|
|
109 | 1 |
buf.remove(u, 2); |
110 | 1 |
buf.insert(u, marg); |
111 | 1 |
end += marg.length - 2; |
112 |
// Scan replaced text for further expansion
|
|
113 | 1 |
size_t mend = u + marg.length; |
114 | 1 |
expand(buf, u, mend, null); |
115 | 1 |
end += mend - (u + marg.length); |
116 | 1 |
u = mend; |
117 |
}
|
|
118 |
else
|
|
119 |
{
|
|
120 |
// Replace '$1' with '\xFF{arg\xFF}'
|
|
121 |
//printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], cast(int)marg.length, marg.ptr);
|
|
122 | 1 |
ubyte[] slice = cast(ubyte[])buf[]; |
123 | 1 |
slice[u] = 0xFF; |
124 | 1 |
slice[u + 1] = '{'; |
125 | 1 |
buf.insert(u + 2, marg); |
126 | 1 |
buf.insert(u + 2 + marg.length, "\xFF}"); |
127 | 1 |
end += -2 + 2 + marg.length + 2; |
128 |
// Scan replaced text for further expansion
|
|
129 | 1 |
size_t mend = u + 2 + marg.length; |
130 | 1 |
expand(buf, u + 2, mend, null); |
131 | 1 |
end += mend - (u + 2 + marg.length); |
132 | 1 |
u = mend; |
133 |
}
|
|
134 |
//printf("u = %d, end = %d\n", u, end);
|
|
135 |
//printf("#%.*s#\n", cast(int)end, &buf.data[0]);
|
|
136 | 1 |
continue; |
137 |
}
|
|
138 | 1 |
u++; |
139 |
}
|
|
140 |
/* Second pass - replace other macros
|
|
141 |
*/
|
|
142 | 1 |
for (size_t u = start; u + 4 < end;) |
143 |
{
|
|
144 | 1 |
char* p = cast(char*)buf[].ptr; // buf.data is not loop invariant |
145 |
/* A valid start of macro expansion is $(c, where c is
|
|
146 |
* an id start character, and not $$(c.
|
|
147 |
*/
|
|
148 | 1 |
if (p[u] == '$' && p[u + 1] == '(' && isIdStart(p + u + 2)) |
149 |
{
|
|
150 |
//printf("\tfound macro start '%c'\n", p[u + 2]);
|
|
151 | 1 |
char* name = p + u + 2; |
152 | 1 |
size_t namelen = 0; |
153 | 1 |
const(char)[] marg; |
154 | 1 |
size_t v; |
155 |
/* Scan forward to find end of macro name and
|
|
156 |
* beginning of macro argument (marg).
|
|
157 |
*/
|
|
158 | 1 |
for (v = u + 2; v < end; v += utfStride(p + v)) |
159 |
{
|
|
160 | 1 |
if (!isIdTail(p + v)) |
161 |
{
|
|
162 |
// We've gone past the end of the macro name.
|
|
163 | 1 |
namelen = v - (u + 2); |
164 | 1 |
break; |
165 |
}
|
|
166 |
}
|
|
167 | 1 |
v += extractArgN(p[v .. end], marg, 0); |
168 | 1 |
assert(v <= end); |
169 | 1 |
if (v < end) |
170 |
{
|
|
171 |
// v is on the closing ')'
|
|
172 | 1 |
if (u > start && p[u - 1] == '$') |
173 |
{
|
|
174 |
// Don't expand $$(NAME), but replace it with $(NAME)
|
|
175 | 1 |
buf.remove(u - 1, 1); |
176 | 1 |
end--; |
177 | 1 |
u = v; // now u is one past the closing ')' |
178 | 1 |
continue; |
179 |
}
|
|
180 | 1 |
Macro* m = search(name[0 .. namelen]); |
181 | 1 |
if (!m) |
182 |
{
|
|
183 | 1 |
immutable undef = "DDOC_UNDEFINED_MACRO"; |
184 | 1 |
m = search(undef); |
185 | 1 |
if (m) |
186 |
{
|
|
187 |
// Macro was not defined, so this is an expansion of
|
|
188 |
// DDOC_UNDEFINED_MACRO. Prepend macro name to args.
|
|
189 |
// marg = name[ ] ~ "," ~ marg[ ];
|
|
190 | 1 |
if (marg.length) |
191 |
{
|
|
192 | 1 |
char* q = cast(char*)mem.xmalloc(namelen + 1 + marg.length); |
193 | 1 |
assert(q); |
194 | 1 |
memcpy(q, name, namelen); |
195 | 1 |
q[namelen] = ','; |
196 | 1 |
memcpy(q + namelen + 1, marg.ptr, marg.length); |
197 | 1 |
marg = q[0 .. marg.length + namelen + 1]; |
198 |
}
|
|
199 |
else
|
|
200 |
{
|
|
201 |
marg = name[0 .. namelen]; |
|
202 |
}
|
|
203 |
}
|
|
204 |
}
|
|
205 | 1 |
if (m) |
206 |
{
|
|
207 | 1 |
if (m.inuse && marg.length == 0) |
208 |
{
|
|
209 |
// Remove macro invocation
|
|
210 | 1 |
buf.remove(u, v + 1 - u); |
211 | 1 |
end -= v + 1 - u; |
212 |
}
|
|
213 | 1 |
else if (m.inuse && ((arg.length == marg.length && memcmp(arg.ptr, marg.ptr, arg.length) == 0) || |
214 | 1 |
(arg.length + 4 == marg.length && marg[0] == 0xFF && marg[1] == '{' && memcmp(arg.ptr, marg.ptr + 2, arg.length) == 0 && marg[marg.length - 2] == 0xFF && marg[marg.length - 1] == '}'))) |
215 |
{
|
|
216 |
/* Recursive expansion:
|
|
217 |
* marg is same as arg (with blue paint added)
|
|
218 |
* Just leave in place.
|
|
219 |
*/
|
|
220 |
}
|
|
221 |
else
|
|
222 |
{
|
|
223 |
//printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", cast(int)m.namelen, m.name, cast(int)marg.length, marg.ptr, cast(int)m.textlen, m.text);
|
|
224 | 1 |
marg = memdup(marg); |
225 |
// Insert replacement text
|
|
226 | 1 |
buf.spread(v + 1, 2 + m.text.length + 2); |
227 | 1 |
ubyte[] slice = cast(ubyte[])buf[]; |
228 | 1 |
slice[v + 1] = 0xFF; |
229 | 1 |
slice[v + 2] = '{'; |
230 | 1 |
slice[v + 3 .. v + 3 + m.text.length] = cast(ubyte[])m.text[]; |
231 | 1 |
slice[v + 3 + m.text.length] = 0xFF; |
232 | 1 |
slice[v + 3 + m.text.length + 1] = '}'; |
233 | 1 |
end += 2 + m.text.length + 2; |
234 |
// Scan replaced text for further expansion
|
|
235 | 1 |
m.inuse++; |
236 | 1 |
size_t mend = v + 1 + 2 + m.text.length + 2; |
237 | 1 |
expand(buf, v + 1, mend, marg); |
238 | 1 |
end += mend - (v + 1 + 2 + m.text.length + 2); |
239 | 1 |
m.inuse--; |
240 | 1 |
buf.remove(u, v + 1 - u); |
241 | 1 |
end -= v + 1 - u; |
242 | 1 |
u += mend - (v + 1); |
243 | 1 |
mem.xfree(cast(char*)marg.ptr); |
244 |
//printf("u = %d, end = %d\n", u, end);
|
|
245 |
//printf("#%.*s#\n", cast(int)(end - u), &buf.data[u]);
|
|
246 | 1 |
continue; |
247 |
}
|
|
248 |
}
|
|
249 |
else
|
|
250 |
{
|
|
251 |
// Replace $(NAME) with nothing
|
|
252 | 1 |
buf.remove(u, v + 1 - u); |
253 | 1 |
end -= (v + 1 - u); |
254 | 1 |
continue; |
255 |
}
|
|
256 |
}
|
|
257 |
}
|
|
258 | 1 |
u++; |
259 |
}
|
|
260 | 1 |
mem.xfree(cast(char*)arg); |
261 | 1 |
pend = end; |
262 | 1 |
nest--; |
263 |
}
|
|
264 |
|
|
265 |
private: |
|
266 |
|
|
267 |
extern (D) Macro* search(const(char)[] name) |
|
268 |
{
|
|
269 | 1 |
Macro* table; |
270 |
//printf("Macro::search(%.*s)\n", cast(int)name.length, name.ptr);
|
|
271 | 1 |
for (table = mactab; table; table = table.next) |
272 |
{
|
|
273 | 1 |
if (table.name == name) |
274 |
{
|
|
275 |
//printf("\tfound %d\n", table.textlen);
|
|
276 | 1 |
break; |
277 |
}
|
|
278 |
}
|
|
279 | 1 |
return table; |
280 |
}
|
|
281 |
|
|
282 |
Macro* mactab; |
|
283 |
}
|
|
284 |
|
|
285 |
/* ************************************************************************ */
|
|
286 |
|
|
287 |
private: |
|
288 |
|
|
289 |
struct Macro |
|
290 |
{
|
|
291 |
Macro* next; // next in list |
|
292 |
const(char)[] name; // macro name |
|
293 |
const(char)[] text; // macro replacement text |
|
294 |
int inuse; // macro is in use (don't expand) |
|
295 |
|
|
296 | 1 |
this(const(char)[] name, const(char)[] text) |
297 |
{
|
|
298 | 1 |
this.name = name; |
299 | 1 |
this.text = text; |
300 |
}
|
|
301 |
}
|
|
302 |
|
|
303 |
/************************
|
|
304 |
* Make mutable copy of slice p.
|
|
305 |
* Params:
|
|
306 |
* p = slice
|
|
307 |
* Returns:
|
|
308 |
* copy allocated with mem.xmalloc()
|
|
309 |
*/
|
|
310 |
|
|
311 |
char[] memdup(const(char)[] p) |
|
312 |
{
|
|
313 | 1 |
size_t len = p.length; |
314 | 1 |
return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len]; |
315 |
}
|
|
316 |
|
|
317 |
/**********************************************************
|
|
318 |
* Given buffer buf[], extract argument marg[].
|
|
319 |
* Params:
|
|
320 |
* buf = source string
|
|
321 |
* marg = set to slice of buf[]
|
|
322 |
* n = 0: get entire argument
|
|
323 |
* 1..9: get nth argument
|
|
324 |
* -1: get 2nd through end
|
|
325 |
*/
|
|
326 |
size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) |
|
327 |
{
|
|
328 |
/* Scan forward for matching right parenthesis.
|
|
329 |
* Nest parentheses.
|
|
330 |
* Skip over "..." and '...' strings inside HTML tags.
|
|
331 |
* Skip over <!-- ... --> comments.
|
|
332 |
* Skip over previous macro insertions
|
|
333 |
* Set marg.
|
|
334 |
*/
|
|
335 | 1 |
uint parens = 1; |
336 | 1 |
ubyte instring = 0; |
337 | 1 |
uint incomment = 0; |
338 | 1 |
uint intag = 0; |
339 | 1 |
uint inexp = 0; |
340 | 1 |
uint argn = 0; |
341 | 1 |
size_t v = 0; |
342 | 1 |
const p = buf.ptr; |
343 | 1 |
const end = buf.length; |
344 |
Largstart: |
|
345 |
// Skip first space, if any, to find the start of the macro argument
|
|
346 | 1 |
if (n != 1 && v < end && isspace(p[v])) |
347 | 1 |
v++; |
348 | 1 |
size_t vstart = v; |
349 | 1 |
for (; v < end; v++) |
350 |
{
|
|
351 | 1 |
char c = p[v]; |
352 | 1 |
switch (c) |
353 |
{
|
|
354 | 1 |
case ',': |
355 | 1 |
if (!inexp && !instring && !incomment && parens == 1) |
356 |
{
|
|
357 | 1 |
argn++; |
358 | 1 |
if (argn == 1 && n == -1) |
359 |
{
|
|
360 | 1 |
v++; |
361 | 1 |
goto Largstart; |
362 |
}
|
|
363 | 1 |
if (argn == n) |
364 | 1 |
break; |
365 | 1 |
if (argn + 1 == n) |
366 |
{
|
|
367 | 1 |
v++; |
368 | 1 |
goto Largstart; |
369 |
}
|
|
370 |
}
|
|
371 | 1 |
continue; |
372 | 1 |
case '(': |
373 | 1 |
if (!inexp && !instring && !incomment) |
374 | 1 |
parens++; |
375 | 1 |
continue; |
376 | 1 |
case ')': |
377 | 1 |
if (!inexp && !instring && !incomment && --parens == 0) |
378 |
{
|
|
379 | 1 |
break; |
380 |
}
|
|
381 | 1 |
continue; |
382 | 1 |
case '"': |
383 | 1 |
case '\'': |
384 | 1 |
if (!inexp && !incomment && intag) |
385 |
{
|
|
386 | 1 |
if (c == instring) |
387 | 1 |
instring = 0; |
388 | 1 |
else if (!instring) |
389 | 1 |
instring = c; |
390 |
}
|
|
391 | 1 |
continue; |
392 | 1 |
case '<': |
393 | 1 |
if (!inexp && !instring && !incomment) |
394 |
{
|
|
395 | 1 |
if (v + 6 < end && p[v + 1] == '!' && p[v + 2] == '-' && p[v + 3] == '-') |
396 |
{
|
|
397 | 1 |
incomment = 1; |
398 | 1 |
v += 3; |
399 |
}
|
|
400 | 1 |
else if (v + 2 < end && isalpha(p[v + 1])) |
401 | 1 |
intag = 1; |
402 |
}
|
|
403 | 1 |
continue; |
404 | 1 |
case '>': |
405 | 1 |
if (!inexp) |
406 | 1 |
intag = 0; |
407 | 1 |
continue; |
408 | 1 |
case '-': |
409 | 1 |
if (!inexp && !instring && incomment && v + 2 < end && p[v + 1] == '-' && p[v + 2] == '>') |
410 |
{
|
|
411 | 1 |
incomment = 0; |
412 | 1 |
v += 2; |
413 |
}
|
|
414 | 1 |
continue; |
415 | 1 |
case 0xFF: |
416 | 1 |
if (v + 1 < end) |
417 |
{
|
|
418 | 1 |
if (p[v + 1] == '{') |
419 | 1 |
inexp++; |
420 | 1 |
else if (p[v + 1] == '}') |
421 | 1 |
inexp--; |
422 |
}
|
|
423 | 1 |
continue; |
424 | 1 |
default: |
425 | 1 |
continue; |
426 |
}
|
|
427 | 1 |
break; |
428 |
}
|
|
429 | 1 |
if (argn == 0 && n == -1) |
430 | 1 |
marg = p[v .. v]; |
431 |
else
|
|
432 | 1 |
marg = p[vstart .. v]; |
433 |
//printf("extractArg%d('%.*s') = '%.*s'\n", n, cast(int)end, p, cast(int)marg.length, marg.ptr);
|
|
434 | 1 |
return v; |
435 |
}
|
Read our documentation on viewing source code .