1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1986-2009 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * preprocessor macro call
26 */
27
28 #include "pplib.h"
29
30 #include <ctype.h>
31
32 /*
33 * call a macro by pushing its value on the input stream
34 * only the macro token itself has been consumed
35 * -1 returned if macro disabled
36 * 0 returned if tok==0 and sym->mac->value to be copied to output by caller
37 * 1 returned if value pushed on input
38 */
39
40 int
ppcall(register struct ppsymbol * sym,int tok)41 ppcall(register struct ppsymbol* sym, int tok)
42 {
43 register int c;
44 register char* p;
45 register char* q;
46 register struct ppmacro* mac;
47 int n;
48 int m;
49 int ret;
50 int old_hidden;
51 int last_line;
52 long old_state;
53 char* last_file;
54 char* old_next;
55 char* old_token;
56 struct ppmacstk* mp;
57 struct ppinstk* old_in;
58 struct ppinstk* kp;
59 struct pptuple* tp;
60
61 ret = -1;
62 sym->flags |= SYM_NOTICED;
63 if (mac = sym->macro)
64 {
65 count(macro);
66 if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN))
67 error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name);
68 if (sym->flags & SYM_DISABLED)
69 #if COMPATIBLE
70 if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity)
71 #endif
72 {
73 pp.mode |= MARKMACRO;
74 #if COMPATIBLE
75 if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
76 error(1, "%s: macro recursion inhibited", sym->name);
77 #endif
78 goto disable;
79 }
80 if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE)))
81 {
82 #if COMPATIBLE
83 if (*sym->name != '_' && !(pp.state & COMPATIBILITY))
84 #else
85 if (*sym->name != '_')
86 #endif
87 {
88 if (pp.state & STRICT)
89 {
90 error(1, "%s: obsolete predefined symbol expansion disabled", sym->name);
91 goto disable;
92 }
93 error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive");
94 }
95 else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#'))
96 error(1, "%s: predefined symbol expanded outside of directive", sym->name);
97 }
98 debug((-5, "macro %s = %s", sym->name, mac->value));
99 if (pp.macref)
100 (*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L);
101 if (tp = mac->tuple)
102 {
103 old_state = pp.state;
104 pp.state |= DEFINITION|NOSPACE;
105 old_token = pp.token;
106 n = 2 * MAXTOKEN;
107 pp.token = p = oldof(0, char, 0, n);
108 q = p + MAXTOKEN;
109 *pp.token++ = ' ';
110 old_hidden = pp.hidden;
111 while (c = pplex())
112 {
113 if (c == '\n')
114 {
115 pp.hidden++;
116 pp.state |= HIDDEN|NEWLINE;
117 old_state |= HIDDEN|NEWLINE;
118 error_info.line++;
119 }
120 else if (c == '#')
121 {
122 ungetchr(c);
123 break;
124 }
125 else
126 {
127 for (;;)
128 {
129 if (streq(pp.token, tp->token))
130 {
131 if (!(tp = tp->match))
132 break;
133 if (!tp->nomatch)
134 {
135 free(p);
136 pp.state = old_state;
137 pp.token = old_token;
138 PUSH_TUPLE(sym, tp->token);
139 ret = 1;
140 goto disable;
141 }
142 }
143 else if (!(tp = tp->nomatch))
144 break;
145 }
146 if (!tp)
147 {
148 pp.token = pp.toknxt;
149 break;
150 }
151 }
152 if ((pp.token = pp.toknxt) > q)
153 {
154 c = pp.token - p;
155 p = newof(p, char, n += MAXTOKEN, 0);
156 q = p + n - MAXTOKEN;
157 pp.token = p + c;
158 }
159 *pp.token++ = ' ';
160 }
161 if (pp.token > p && *(pp.token - 1) == ' ')
162 pp.token--;
163 if (pp.hidden != old_hidden)
164 *pp.token++ = '\n';
165 else
166 *pp.token++ = ' ';
167 *pp.token = 0;
168 pp.state = old_state;
169 pp.token = old_token;
170 if (*p)
171 PUSH_RESCAN(p);
172 else
173 free(p);
174 if (!mac->value)
175 goto disable;
176 }
177 if (sym->flags & SYM_FUNCTION)
178 {
179 /*
180 * a quick and dirty '(' peek to avoid possibly
181 * inappropriate ungetchr()'s below
182 */
183
184 for (p = pp.in->nextchr; isspace(*p); p++);
185 if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK)
186 goto disable;
187 old_next = (c == MARK) ? pp.in->nextchr : NiL;
188 old_token = pp.token;
189 mp = pp.macp->next;
190 if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac)
191 error(3, "%s: too many nested function-like macros", sym->name);
192 old_hidden = pp.hidden;
193 old_state = pp.state;
194 pp.state |= DEFINITION|FILEPOP|NOSPACE;
195 while ((c = pplex()) == '\n')
196 {
197 pp.hidden++;
198 pp.state |= HIDDEN|NEWLINE;
199 old_state |= HIDDEN|NEWLINE;
200 error_info.line++;
201 }
202 if (c != '(')
203 {
204 pp.state = old_state;
205 if (old_next)
206 pp.in->nextchr = old_next;
207 else
208 {
209 if (c)
210 {
211 p = pp.toknxt;
212 while (p > pp.token)
213 ungetchr(*--p);
214 #if COMPATIBLE
215 if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
216 error(1, "%s: macro arguments omitted", sym->name);
217 #endif
218 if (c == T_ID && !(pp.state & HIDDEN))
219 ungetchr(' ');
220 }
221 if (pp.hidden != old_hidden)
222 {
223 ungetchr('\n');
224 error_info.line--;
225 if (pp.hidden && !--pp.hidden)
226 pp.state &= ~HIDDEN;
227 }
228 }
229 pp.token = old_token;
230 goto disable;
231 }
232 pp.state = old_state;
233
234 /*
235 * arg[i][-1] is an extra char for each actual i
236 * for a possible ungetchr('"') during IN_QUOTE
237 * arg[i][-1]==0 if arg i need not be expanded
238 * arg[0][-2] holds the actual arg count
239 */
240
241 c = 0;
242 m = 0;
243 n = 0;
244 mp = pp.macp->next;
245 p = pp.token = (char*)&mp->arg[mac->arity + 1];
246 pp.state |= COLLECTING|NOEXPAND;
247 pp.state &= ~FILEPOP;
248 sym->flags |= SYM_ACTIVE;
249 old_in = pp.in;
250 last_line = error_info.line;
251 last_file = error_info.file;
252 mp->line = error_info.line;
253 #if MACKEYARGS
254 if (pp.option & KEYARGS)
255 {
256 for (c = 0; c < mac->arity; c++)
257 mp->arg[c] = mac->args.key[c].value + 1;
258 mp->arg[0]++;
259 }
260 else
261 #endif
262 {
263 *++p = ' ';
264 mp->arg[0] = ++p;
265 }
266 #if MACKEYARGS
267 keyarg:
268 if (pp.option & KEYARGS)
269 {
270 pp.state |= NOSPACE;
271 switch (pplex())
272 {
273 case T_ID:
274 break;
275 case ')': /* no actual key args */
276 if (!(pp.state & NOEXPAND))
277 pp.state |= NOEXPAND;
278 for (c = 0; c < mac->arity; c++)
279 mp->arg[c][-1] = 0;
280 c = 0;
281 goto endactuals;
282 default:
283 error(3, "%s: invalid keyword macro argument", pp.token);
284 break;
285 }
286 for (c = 0; c < mac->arity; c++)
287 if (streq(pp.token, mac->args.key[c].name)) break;
288 if (c >= mac->arity)
289 error(2, "%s: invalid macro argument keyword", pp.token);
290 if (pplex() != '=')
291 error(2, "= expected in keyword macro argument");
292 pp.state &= ~NOSPACE;
293 if (!c)
294 p++;
295 pp.token = mp->arg[c] = ++p;
296 }
297 #endif
298 for (;;)
299 {
300 if ((pp.mactop = pp.token = p) >= pp.maxmac)
301 error(3, "%s: too many nested function-like macros", sym->name);
302 switch (pplex())
303 {
304 case '(':
305 n++;
306 break;
307 case ')':
308 if (!n--)
309 {
310 if (p > mp->arg[c] && *(p - 1) == ' ')
311 p--;
312 if (p > mp->arg[c] && *(p - 1) == '\\')
313 {
314 for (q = mp->arg[c]; q < p; q++)
315 if (*q == '\\')
316 q++;
317 if (q > p)
318 *p++ = '\\';
319 }
320 #if MACKEYARGS
321 *p = 0;
322 m++;
323 #endif
324 goto endactuals;
325 }
326 break;
327 case ',':
328 if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC))))
329 {
330 if (p > mp->arg[c] && *(p - 1) == ' ')
331 p--;
332 *p++ = 0;
333 if (!(pp.state & NOEXPAND))
334 pp.state |= NOEXPAND;
335 else
336 mp->arg[c][-1] = 0;
337 #if MACKEYARGS
338 if (pp.option & KEYARGS)
339 {
340 pp.token = p + 1;
341 goto keyarg;
342 }
343 #endif
344 {
345 if ((pp.state & STRICT) && p == mp->arg[c])
346 error(1, "%s: macro call argument %d is null", sym->name, c + 1);
347 if (c < mac->arity)
348 c++;
349 *p++ = ' ';
350 }
351 pp.toknxt = mp->arg[c] = p;
352 }
353 break;
354 case 0:
355 if (pp.in == old_in)
356 kp = 0;
357 else
358 for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
359 if (!kp)
360 {
361 error(
362 #if COMPATIBLE
363 (pp.state & COMPATIBILITY) ? 3 :
364 #endif
365 2, "%s: %s in macro argument list", sym->name, pptokchr(0));
366 goto endactuals;
367 }
368 continue;
369 case '\n':
370 pp.state |= HIDDEN;
371 error_info.line++;
372 pp.hidden++;
373 /*FALLTHROUGH*/
374 case ' ':
375 if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' ';
376 continue;
377 }
378 p = pp.toknxt;
379 if (error_info.line != last_line)
380 {
381 SETLINE(p, error_info.line);
382 last_line = error_info.line;
383 }
384 if (error_info.file != last_file)
385 {
386 SETFILE(p, error_info.file);
387 last_file = error_info.file;
388 }
389 }
390 endactuals:
391 if (pp.state & NOEXPAND)
392 mp->arg[c][-1] = 0;
393 pp.token = old_token;
394 if (pp.in != old_in)
395 {
396 for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
397 if (kp)
398 error(2, "%s: macro call starts and ends in different files", sym->name);
399 }
400 pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND);
401 sym->flags &= ~SYM_ACTIVE;
402 #if MACKEYARGS
403 if (!(pp.option & KEYARGS))
404 #endif
405 {
406 if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC))
407 c++;
408 if (c != mac->arity && !(sym->flags & SYM_EMPTY))
409 {
410 n = mac->arity;
411 if (!(sym->flags & SYM_VARIADIC))
412 error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
413 else if (c < --n)
414 error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
415 #if COMPATIBLE
416 if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
417 goto disable;
418 #endif
419 }
420 if (!c)
421 ++c;
422 while (c < mac->arity)
423 mp->arg[c++] = (char*)"\0" + 1;
424 }
425 mp->arg[0][-2] = m;
426 *p++ = 0;
427 nextframe(mp, p);
428 count(function);
429 }
430 if (!tok && (sym->flags & SYM_NOEXPAND))
431 {
432 if (sym->flags & SYM_FUNCTION)
433 popframe(mp);
434 ret = !mac->size;
435 }
436 else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL) || pp.in->type != IN_COPY)
437 {
438 if (sym->flags & SYM_MULTILINE)
439 PUSH_MULTILINE(sym);
440 else
441 PUSH_MACRO(sym);
442 ret = 1;
443 }
444 }
445 disable:
446 if (ret < 0 && sym->hidden && !(pp.mode & EXPOSE) && !(pp.state & HEADER) && (pp.in->type == IN_FILE || pp.in->type == IN_MACRO || pp.in->type == IN_EXPAND))
447 {
448 struct ppinstk* inp;
449
450 for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev);
451 sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name);
452 PUSH_STRING(pp.hidebuf);
453 ret = 1;
454 }
455 pp.state &= ~NEWLINE;
456 pp.in->flags |= IN_tokens;
457 count(token);
458 return ret;
459 }
460