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 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