macro.c (10674B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include "cpp.h" 5 6 /* 7 * do a macro definition. tp points to the name being defined in the line 8 */ 9 void 10 dodefine(Tokenrow *trp) 11 { 12 Token *tp; 13 Nlist *np; 14 Tokenrow *def, *args; 15 16 tp = trp->tp+1; 17 if (tp>=trp->lp || tp->type!=NAME) { 18 error(ERROR, "#defined token is not a name"); 19 return; 20 } 21 np = lookup(tp, 1); 22 if (np->flag&ISUNCHANGE) { 23 error(ERROR, "#defined token %t can't be redefined", tp); 24 return; 25 } 26 /* collect arguments */ 27 tp += 1; 28 args = NULL; 29 if (tp<trp->lp && tp->type==LP && tp->wslen==0) { 30 /* macro with args */ 31 int narg = 0; 32 tp += 1; 33 args = new(Tokenrow); 34 maketokenrow(2, args); 35 if (tp->type!=RP) { 36 int err = 0; 37 for (;;) { 38 Token *atp; 39 if (tp->type!=NAME) { 40 err++; 41 break; 42 } 43 if (narg>=args->max) 44 growtokenrow(args); 45 for (atp=args->bp; atp<args->lp; atp++) 46 if (atp->len==tp->len 47 && strncmp((char*)atp->t, (char*)tp->t, tp->len)==0) 48 error(ERROR, "Duplicate macro argument"); 49 *args->lp++ = *tp; 50 narg++; 51 tp += 1; 52 if (tp->type==RP) 53 break; 54 if (tp->type!=COMMA) { 55 err++; 56 break; 57 } 58 tp += 1; 59 } 60 if (err) { 61 error(ERROR, "Syntax error in macro parameters"); 62 return; 63 } 64 } 65 tp += 1; 66 } 67 trp->tp = tp; 68 if (((trp->lp)-1)->type==NL) 69 trp->lp -= 1; 70 def = normtokenrow(trp); 71 if (np->flag&ISDEFINED) { 72 if (comparetokens(def, np->vp) 73 || (np->ap==NULL) != (args==NULL) 74 || np->ap && comparetokens(args, np->ap)) 75 error(ERROR, "Macro redefinition of %t", trp->bp+2); 76 } 77 if (args) { 78 Tokenrow *tap; 79 tap = normtokenrow(args); 80 dofree(args->bp); 81 args = tap; 82 } 83 np->ap = args; 84 np->vp = def; 85 np->flag |= ISDEFINED; 86 } 87 88 /* 89 * Definition received via -D or -U 90 */ 91 void 92 doadefine(Tokenrow *trp, int type) 93 { 94 Nlist *np; 95 static Token onetoken[1] = {{ NUMBER, 0, 0, 0, 1, (uchar*)"1" }}; 96 static Tokenrow onetr = { onetoken, onetoken, onetoken+1, 1 }; 97 98 trp->tp = trp->bp; 99 if (type=='U') { 100 if (trp->lp-trp->tp != 2 || trp->tp->type!=NAME) 101 goto syntax; 102 if ((np = lookup(trp->tp, 0)) == NULL) 103 return; 104 np->flag &= ~ISDEFINED; 105 return; 106 } 107 if (trp->tp >= trp->lp || trp->tp->type!=NAME) 108 goto syntax; 109 np = lookup(trp->tp, 1); 110 np->flag |= ISDEFINED; 111 trp->tp += 1; 112 if (trp->tp >= trp->lp || trp->tp->type==END) { 113 np->vp = &onetr; 114 return; 115 } 116 if (trp->tp->type!=ASGN) 117 goto syntax; 118 trp->tp += 1; 119 if ((trp->lp-1)->type == END) 120 trp->lp -= 1; 121 np->vp = normtokenrow(trp); 122 return; 123 syntax: 124 error(FATAL, "Illegal -D or -U argument %r", trp); 125 } 126 127 /* 128 * Do macro expansion in a row of tokens. 129 * Flag is NULL if more input can be gathered. 130 */ 131 void 132 expandrow(Tokenrow *trp, char *flag) 133 { 134 Token *tp; 135 Nlist *np; 136 137 if (flag) 138 setsource(flag, -1, ""); 139 for (tp = trp->tp; tp<trp->lp; ) { 140 if (tp->type!=NAME 141 || quicklook(tp->t[0], tp->len>1?tp->t[1]:0)==0 142 || (np = lookup(tp, 0))==NULL 143 || (np->flag&(ISDEFINED|ISMAC))==0 144 || tp->hideset && checkhideset(tp->hideset, np)) { 145 tp++; 146 continue; 147 } 148 trp->tp = tp; 149 if (np->val==KDEFINED) { 150 tp->type = DEFINED; 151 if ((tp+1)<trp->lp && (tp+1)->type==NAME) 152 (tp+1)->type = NAME1; 153 else if ((tp+3)<trp->lp && (tp+1)->type==LP 154 && (tp+2)->type==NAME && (tp+3)->type==RP) 155 (tp+2)->type = NAME1; 156 else 157 error(ERROR, "Incorrect syntax for `defined'"); 158 tp++; 159 continue; 160 } 161 if (np->flag&ISMAC) 162 builtin(trp, np->val); 163 else { 164 expand(trp, np); 165 } 166 tp = trp->tp; 167 } 168 if (flag) 169 unsetsource(); 170 } 171 172 /* 173 * Expand the macro whose name is np, at token trp->tp, in the tokenrow. 174 * Return trp->tp at the first token next to be expanded 175 * (ordinarily the beginning of the expansion) 176 */ 177 void 178 expand(Tokenrow *trp, Nlist *np) 179 { 180 Tokenrow ntr; 181 int ntokc, narg, i; 182 Token *tp; 183 Tokenrow *atr[NARG+1]; 184 int hs; 185 186 copytokenrow(&ntr, np->vp); /* copy macro value */ 187 if (np->ap==NULL) /* parameterless */ 188 ntokc = 1; 189 else { 190 ntokc = gatherargs(trp, atr, &narg); 191 if (narg<0) { /* not actually a call (no '(') */ 192 trp->tp++; 193 return; 194 } 195 if (narg != rowlen(np->ap)) { 196 error(ERROR, "Disagreement in number of macro arguments"); 197 trp->tp->hideset = newhideset(trp->tp->hideset, np); 198 trp->tp += ntokc; 199 return; 200 } 201 substargs(np, &ntr, atr); /* put args into replacement */ 202 for (i=0; i<narg; i++) { 203 dofree(atr[i]->bp); 204 dofree(atr[i]); 205 } 206 } 207 doconcat(&ntr); /* execute ## operators */ 208 hs = newhideset(trp->tp->hideset, np); 209 for (tp=ntr.bp; tp<ntr.lp; tp++) { /* distribute hidesets */ 210 if (tp->type==NAME) { 211 if (tp->hideset==0) 212 tp->hideset = hs; 213 else 214 tp->hideset = unionhideset(tp->hideset, hs); 215 } 216 } 217 ntr.tp = ntr.bp; 218 insertrow(trp, ntokc, &ntr); 219 trp->tp -= rowlen(&ntr); 220 dofree(ntr.bp); 221 return; 222 } 223 224 /* 225 * Gather an arglist, starting in trp with tp pointing at the macro name. 226 * Return total number of tokens passed, stash number of args found. 227 * trp->tp is not changed relative to the tokenrow. 228 */ 229 int 230 gatherargs(Tokenrow *trp, Tokenrow **atr, int *narg) 231 { 232 int parens = 1; 233 int ntok = 0; 234 Token *bp, *lp; 235 Tokenrow ttr; 236 int ntokp; 237 int needspace; 238 239 *narg = -1; /* means that there is no macro call */ 240 /* look for the ( */ 241 for (;;) { 242 trp->tp++; 243 ntok++; 244 if (trp->tp >= trp->lp) { 245 gettokens(trp, 0); 246 if ((trp->lp-1)->type==END) { 247 trp->lp -= 1; 248 trp->tp -= ntok; 249 return ntok; 250 } 251 } 252 if (trp->tp->type==LP) 253 break; 254 if (trp->tp->type!=NL) 255 return ntok; 256 } 257 *narg = 0; 258 ntok++; 259 ntokp = ntok; 260 trp->tp++; 261 /* search for the terminating ), possibly extending the row */ 262 needspace = 0; 263 while (parens>0) { 264 if (trp->tp >= trp->lp) 265 gettokens(trp, 0); 266 if (needspace) { 267 needspace = 0; 268 makespace(trp); 269 } 270 if (trp->tp->type==END) { 271 trp->lp -= 1; 272 trp->tp -= ntok; 273 error(ERROR, "EOF in macro arglist"); 274 return ntok; 275 } 276 if (trp->tp->type==NL) { 277 trp->tp += 1; 278 adjustrow(trp, -1); 279 trp->tp -= 1; 280 makespace(trp); 281 needspace = 1; 282 continue; 283 } 284 if (trp->tp->type==LP) 285 parens++; 286 else if (trp->tp->type==RP) 287 parens--; 288 trp->tp++; 289 ntok++; 290 } 291 trp->tp -= ntok; 292 /* Now trp->tp won't move underneath us */ 293 lp = bp = trp->tp+ntokp; 294 for (; parens>=0; lp++) { 295 if (lp->type == LP) { 296 parens++; 297 continue; 298 } 299 if (lp->type==RP) 300 parens--; 301 if (lp->type==DSHARP) 302 lp->type = DSHARP1; /* ## not special in arg */ 303 if (lp->type==COMMA && parens==0 || parens<0 && (lp-1)->type!=LP) { 304 if (*narg>=NARG-1) 305 error(FATAL, "Sorry, too many macro arguments"); 306 ttr.bp = ttr.tp = bp; 307 ttr.lp = lp; 308 atr[(*narg)++] = normtokenrow(&ttr); 309 bp = lp+1; 310 } 311 } 312 return ntok; 313 } 314 315 /* 316 * substitute the argument list into the replacement string 317 * This would be simple except for ## and # 318 */ 319 void 320 substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr) 321 { 322 Tokenrow tatr; 323 Token *tp; 324 int ntok, argno; 325 326 for (rtr->tp=rtr->bp; rtr->tp<rtr->lp; ) { 327 if (rtr->tp->type==SHARP) { /* string operator */ 328 tp = rtr->tp; 329 rtr->tp += 1; 330 if ((argno = lookuparg(np, rtr->tp))<0) { 331 error(ERROR, "# not followed by macro parameter"); 332 continue; 333 } 334 ntok = 1 + (rtr->tp - tp); 335 rtr->tp = tp; 336 insertrow(rtr, ntok, stringify(atr[argno])); 337 continue; 338 } 339 if (rtr->tp->type==NAME 340 && (argno = lookuparg(np, rtr->tp)) >= 0) { 341 if ((rtr->tp+1)->type==DSHARP 342 || rtr->tp!=rtr->bp && (rtr->tp-1)->type==DSHARP) 343 insertrow(rtr, 1, atr[argno]); 344 else { 345 copytokenrow(&tatr, atr[argno]); 346 expandrow(&tatr, "<macro>"); 347 insertrow(rtr, 1, &tatr); 348 dofree(tatr.bp); 349 } 350 continue; 351 } 352 rtr->tp++; 353 } 354 } 355 356 /* 357 * Evaluate the ## operators in a tokenrow 358 */ 359 void 360 doconcat(Tokenrow *trp) 361 { 362 Token *ltp, *ntp; 363 Tokenrow ntr; 364 int len; 365 366 for (trp->tp=trp->bp; trp->tp<trp->lp; trp->tp++) { 367 if (trp->tp->type==DSHARP1) 368 trp->tp->type = DSHARP; 369 else if (trp->tp->type==DSHARP) { 370 char tt[128]; 371 ltp = trp->tp-1; 372 ntp = trp->tp+1; 373 if (ltp<trp->bp || ntp>=trp->lp) { 374 error(ERROR, "## occurs at border of replacement"); 375 continue; 376 } 377 len = ltp->len + ntp->len; 378 strncpy((char*)tt, (char*)ltp->t, ltp->len); 379 strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len); 380 tt[len] = '\0'; 381 setsource("<##>", -1, tt); 382 maketokenrow(3, &ntr); 383 gettokens(&ntr, 1); 384 unsetsource(); 385 if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS) 386 error(WARNING, "Bad token %r produced by ##", &ntr); 387 ntr.lp = ntr.bp+1; 388 trp->tp = ltp; 389 makespace(&ntr); 390 insertrow(trp, (ntp-ltp)+1, &ntr); 391 dofree(ntr.bp); 392 trp->tp--; 393 } 394 } 395 } 396 397 /* 398 * tp is a potential parameter name of macro mac; 399 * look it up in mac's arglist, and if found, return the 400 * corresponding index in the argname array. Return -1 if not found. 401 */ 402 int 403 lookuparg(Nlist *mac, Token *tp) 404 { 405 Token *ap; 406 407 if (tp->type!=NAME || mac->ap==NULL) 408 return -1; 409 for (ap=mac->ap->bp; ap<mac->ap->lp; ap++) { 410 if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0) 411 return ap - mac->ap->bp; 412 } 413 return -1; 414 } 415 416 /* 417 * Return a quoted version of the tokenrow (from # arg) 418 */ 419 #define STRLEN 512 420 Tokenrow * 421 stringify(Tokenrow *vp) 422 { 423 static Token t = { STRING }; 424 static Tokenrow tr = { &t, &t, &t+1, 1 }; 425 Token *tp; 426 uchar s[STRLEN]; 427 uchar *sp = s, *cp; 428 int i, instring; 429 430 *sp++ = '"'; 431 for (tp = vp->bp; tp < vp->lp; tp++) { 432 instring = tp->type==STRING || tp->type==CCON; 433 if (sp+2*tp->len >= &s[STRLEN-10]) { 434 error(ERROR, "Stringified macro arg is too long"); 435 break; 436 } 437 if (tp->wslen && (tp->flag&XPWS)==0) 438 *sp++ = ' '; 439 for (i=0, cp=tp->t; i<tp->len; i++) { 440 if (instring && (*cp=='"' || *cp=='\\')) 441 *sp++ = '\\'; 442 *sp++ = *cp++; 443 } 444 } 445 *sp++ = '"'; 446 *sp = '\0'; 447 sp = s; 448 t.len = strlen((char*)sp); 449 t.t = newstring(sp, t.len, 0); 450 return &tr; 451 } 452 453 /* 454 * expand a builtin name 455 */ 456 void 457 builtin(Tokenrow *trp, int biname) 458 { 459 char *op; 460 Token *tp; 461 Source *s; 462 463 tp = trp->tp; 464 trp->tp++; 465 /* need to find the real source */ 466 s = cursource; 467 while (s && s->fd==-1) 468 s = s->next; 469 if (s==NULL) 470 s = cursource; 471 /* most are strings */ 472 tp->type = STRING; 473 if (tp->wslen) { 474 *outp++ = ' '; 475 tp->wslen = 1; 476 } 477 op = outp; 478 *op++ = '"'; 479 switch (biname) { 480 481 case KLINENO: 482 tp->type = NUMBER; 483 op = outnum(op-1, s->line); 484 break; 485 486 case KFILE: { 487 char *src = s->filename; 488 while ((*op++ = *src++) != 0) 489 if (src[-1] == '\\') 490 *op++ = '\\'; 491 op--; 492 break; 493 } 494 495 case KDATE: 496 strncpy(op, curtime+4, 7); 497 strncpy(op+7, curtime+20, 4); 498 op += 11; 499 break; 500 501 case KTIME: 502 strncpy(op, curtime+11, 8); 503 op += 8; 504 break; 505 506 default: 507 error(ERROR, "cpp botch: unknown internal macro"); 508 return; 509 } 510 if (tp->type==STRING) 511 *op++ = '"'; 512 tp->t = (uchar*)outp; 513 tp->len = op - outp; 514 outp = op; 515 }