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