1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
19 ***********************************************************************/
25 * preprocessor macro call
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
41 ppcall(register struct ppsymbol
* sym
, int tok
)
46 register struct ppmacro
* mac
;
57 struct ppinstk
* old_in
;
62 sym
->flags
|= SYM_NOTICED
;
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
)
70 if ((pp
.state
& (COMPATIBILITY
|TRANSITION
)) != COMPATIBILITY
|| !mac
->arity
)
75 if ((pp
.state
& (COMPATIBILITY
|STRICT
)) == (COMPATIBILITY
|STRICT
))
76 error(1, "%s: macro recursion inhibited", sym
->name
);
80 if ((sym
->flags
& SYM_PREDEFINED
) && !(pp
.mode
& (HOSTED
|INACTIVE
)))
83 if (*sym
->name
!= '_' && !(pp
.state
& COMPATIBILITY
))
85 if (*sym
->name
!= '_')
88 if (pp
.state
& STRICT
)
90 error(1, "%s: obsolete predefined symbol expansion disabled", sym
->name
);
93 error(1, "%s: obsolete predefined symbol expanded%s", sym
->name
, (pp
.state
& DIRECTIVE
) ? "" : " outside of directive");
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
);
98 debug((-5, "macro %s = %s", sym
->name
, mac
->value
));
100 (*pp
.macref
)(sym
, error_info
.file
, error_info
.line
, (pp
.state
& CONDITIONAL
) ? REF_IF
: REF_NORMAL
, 0L);
103 old_state
= pp
.state
;
104 pp
.state
|= DEFINITION
|NOSPACE
;
105 old_token
= pp
.token
;
107 pp
.token
= p
= oldof(0, char, 0, n
);
110 old_hidden
= pp
.hidden
;
116 pp
.state
|= HIDDEN
|NEWLINE
;
117 old_state
|= HIDDEN
|NEWLINE
;
129 if (streq(pp
.token
, tp
->token
))
131 if (!(tp
= tp
->match
))
136 pp
.state
= old_state
;
137 pp
.token
= old_token
;
138 PUSH_TUPLE(sym
, tp
->token
);
143 else if (!(tp
= tp
->nomatch
))
148 pp
.token
= pp
.toknxt
;
152 if ((pp
.token
= pp
.toknxt
) > q
)
155 p
= newof(p
, char, n
+= MAXTOKEN
, 0);
156 q
= p
+ n
- MAXTOKEN
;
161 if (pp
.token
> p
&& *(pp
.token
- 1) == ' ')
163 if (pp
.hidden
!= old_hidden
)
168 pp
.state
= old_state
;
169 pp
.token
= old_token
;
177 if (sym
->flags
& SYM_FUNCTION
)
180 * a quick and dirty '(' peek to avoid possibly
181 * inappropriate ungetchr()'s below
184 for (p
= pp
.in
->nextchr
; isspace(*p
); p
++);
185 if ((c
= *p
) != '(' && c
!= '/' && c
!= 0 && c
!= MARK
)
187 old_next
= (c
== MARK
) ? pp
.in
->nextchr
: NiL
;
188 old_token
= pp
.token
;
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')
198 pp
.state
|= HIDDEN
|NEWLINE
;
199 old_state
|= HIDDEN
|NEWLINE
;
204 pp
.state
= old_state
;
206 pp
.in
->nextchr
= old_next
;
215 if ((pp
.state
& (COMPATIBILITY
|STRICT
)) == (COMPATIBILITY
|STRICT
))
216 error(1, "%s: macro arguments omitted", sym
->name
);
218 if (c
== T_ID
&& !(pp
.state
& HIDDEN
))
221 if (pp
.hidden
!= old_hidden
)
225 if (pp
.hidden
&& !--pp
.hidden
)
229 pp
.token
= old_token
;
232 pp
.state
= old_state
;
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
245 p
= pp
.token
= (char*)&mp
->arg
[mac
->arity
+ 1];
246 pp
.state
|= COLLECTING
|NOEXPAND
;
247 pp
.state
&= ~FILEPOP
;
248 sym
->flags
|= SYM_ACTIVE
;
250 last_line
= error_info
.line
;
251 last_file
= error_info
.file
;
252 mp
->line
= error_info
.line
;
254 if (pp
.option
& KEYARGS
)
256 for (c
= 0; c
< mac
->arity
; c
++)
257 mp
->arg
[c
] = mac
->args
.key
[c
].value
+ 1;
268 if (pp
.option
& KEYARGS
)
275 case ')': /* no actual key args */
276 if (!(pp
.state
& NOEXPAND
))
277 pp
.state
|= NOEXPAND
;
278 for (c
= 0; c
< mac
->arity
; c
++)
283 error(3, "%s: invalid keyword macro argument", pp
.token
);
286 for (c
= 0; c
< mac
->arity
; c
++)
287 if (streq(pp
.token
, mac
->args
.key
[c
].name
)) break;
289 error(2, "%s: invalid macro argument keyword", pp
.token
);
291 error(2, "= expected in keyword macro argument");
292 pp
.state
&= ~NOSPACE
;
295 pp
.token
= mp
->arg
[c
] = ++p
;
300 if ((pp
.mactop
= pp
.token
= p
) >= pp
.maxmac
)
301 error(3, "%s: too many nested function-like macros", sym
->name
);
310 if (p
> mp
->arg
[c
] && *(p
- 1) == ' ')
312 if (p
> mp
->arg
[c
] && *(p
- 1) == '\\')
314 for (q
= mp
->arg
[c
]; q
< p
; q
++)
328 if (!n
&& (m
++, (c
< mac
->arity
- 1 || !(sym
->flags
& SYM_VARIADIC
))))
330 if (p
> mp
->arg
[c
] && *(p
- 1) == ' ')
333 if (!(pp
.state
& NOEXPAND
))
334 pp
.state
|= NOEXPAND
;
338 if (pp
.option
& KEYARGS
)
345 if ((pp
.state
& STRICT
) && p
== mp
->arg
[c
])
346 error(1, "%s: macro call argument %d is null", sym
->name
, c
+ 1);
351 pp
.toknxt
= mp
->arg
[c
] = p
;
358 for (kp
= pp
.in
; kp
&& kp
!= old_in
; kp
= kp
->prev
);
363 (pp
.state
& COMPATIBILITY
) ? 3 :
365 2, "%s: %s in macro argument list", sym
->name
, pptokchr(0));
375 if (p
> mp
->arg
[c
] && *(p
- 1) != ' ') *p
++ = ' ';
379 if (error_info
.line
!= last_line
)
381 SETLINE(p
, error_info
.line
);
382 last_line
= error_info
.line
;
384 if (error_info
.file
!= last_file
)
386 SETFILE(p
, error_info
.file
);
387 last_file
= error_info
.file
;
391 if (pp
.state
& NOEXPAND
)
393 pp
.token
= old_token
;
396 for (kp
= pp
.in
; kp
&& kp
!= old_in
; kp
= kp
->prev
);
398 error(2, "%s: macro call starts and ends in different files", sym
->name
);
400 pp
.state
&= ~(COLLECTING
|FILEPOP
|NOEXPAND
);
401 sym
->flags
&= ~SYM_ACTIVE
;
403 if (!(pp
.option
& KEYARGS
))
406 if (p
> mp
->arg
[0] && ++m
|| (sym
->flags
& SYM_VARIADIC
))
408 if (c
!= mac
->arity
&& !(sym
->flags
& SYM_EMPTY
))
411 if (!(sym
->flags
& SYM_VARIADIC
))
412 error(1, "%s: %d actual argument%s expected", sym
->name
, n
, n
== 1 ? "" : "s");
414 error(1, "%s: at least %d actual argument%s expected", sym
->name
, n
, n
== 1 ? "" : "s");
416 if (!c
&& (pp
.state
& (COMPATIBILITY
|STRICT
)) == (COMPATIBILITY
|STRICT
))
422 while (c
< mac
->arity
)
423 mp
->arg
[c
++] = (char*)"\0" + 1;
430 if (!tok
&& (sym
->flags
& SYM_NOEXPAND
))
432 if (sym
->flags
& SYM_FUNCTION
)
436 else if (!(pp
.state
& HEADER
) || (pp
.option
& HEADEREXPANDALL
) || pp
.in
->type
!= IN_COPY
)
438 if (sym
->flags
& SYM_MULTILINE
)
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
))
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
);
455 pp
.state
&= ~NEWLINE
;
456 pp
.in
->flags
|= IN_tokens
;