1 #include <u.h> 2 #include <libc.h> 3 #include <stdio.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", tp); 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 = { 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->flag&ISMAC) 150 builtin(trp, np->val); 151 else { 152 expand(trp, np); 153 } 154 tp = trp->tp; 155 } 156 if (flag) 157 unsetsource(); 158 } 159 160 /* 161 * Expand the macro whose name is np, at token trp->tp, in the tokenrow. 162 * Return trp->tp at the first token next to be expanded 163 * (ordinarily the beginning of the expansion) 164 */ 165 void 166 expand(Tokenrow *trp, Nlist *np) 167 { 168 Tokenrow ntr; 169 int ntokc, narg, i; 170 Token *tp; 171 Tokenrow *atr[NARG+1]; 172 int hs; 173 copytokenrow(&ntr, np->vp); /* copy macro value */ 174 if (np->ap==NULL) /* parameterless */ 175 ntokc = 1; 176 else { 177 ntokc = gatherargs(trp, atr, &narg); 178 if (narg<0) { /* not actually a call (no '(') */ 179 trp->tp += 1; 180 return; 181 } 182 if (narg != rowlen(np->ap)) { 183 error(ERROR, "Disagreement in number of macro arguments"); 184 trp->tp->hideset = newhideset(trp->tp->hideset, np); 185 trp->tp += ntokc; 186 return; 187 } 188 substargs(np, &ntr, atr); /* put args into replacement */ 189 for (i=0; i<narg; i++) { 190 dofree(atr[i]->bp); 191 dofree(atr[i]); 192 } 193 } 194 doconcat(&ntr); /* execute ## operators */ 195 hs = newhideset(trp->tp->hideset, np); 196 for (tp=ntr.bp; tp<ntr.lp; tp++) { /* distribute hidesets */ 197 if (tp->type==NAME) { 198 if (tp->hideset==0) 199 tp->hideset = hs; 200 else 201 tp->hideset = unionhideset(tp->hideset, hs); 202 } 203 } 204 ntr.tp = ntr.bp; 205 insertrow(trp, ntokc, &ntr); 206 trp->tp -= rowlen(&ntr); 207 dofree(ntr.bp); 208 return; 209 } 210 211 /* 212 * Gather an arglist, starting in trp with tp pointing at the macro name. 213 * Return total number of tokens passed, stash number of args found. 214 * trp->tp is not changed relative to the tokenrow. 215 */ 216 int 217 gatherargs(Tokenrow *trp, Tokenrow **atr, int *narg) 218 { 219 int parens = 1; 220 int ntok = 0; 221 Token *bp, *lp; 222 Tokenrow ttr; 223 int ntokp; 224 225 *narg = -1; /* means that there is no macro call */ 226 /* look for the ( */ 227 for (;;) { 228 trp->tp++; 229 ntok++; 230 if (trp->tp >= trp->lp) { 231 gettokens(trp, 0); 232 if ((trp->lp-1)->type==END) { 233 trp->lp -= 1; 234 trp->tp -= ntok; 235 return ntok; 236 } 237 } 238 if (trp->tp->type==LP) 239 break; 240 if (trp->tp->type!=NL) 241 return ntok; 242 } 243 *narg = 0; 244 ntok++; 245 ntokp = ntok; 246 trp->tp++; 247 /* search for the terminating ), possibly extending the row */ 248 while (parens>0) { 249 if (trp->tp >= trp->lp) 250 gettokens(trp, 0); 251 if (trp->tp->type==END) { 252 trp->lp -= 1; 253 trp->tp -= ntok; 254 error(ERROR, "EOF in macro arglist"); 255 return ntok; 256 } 257 if (trp->tp->type==NL) { 258 trp->tp += 1; 259 adjustrow(trp, -1); 260 trp->tp -= 1; 261 makespace(trp); 262 continue; 263 } 264 if (trp->tp->type==LP) 265 parens++; 266 else if (trp->tp->type==RP) 267 parens--; 268 trp->tp++; 269 ntok++; 270 } 271 trp->tp -= ntok; 272 /* Now trp->tp won't move underneath us */ 273 lp = bp = trp->tp+ntokp; 274 for (; parens>=0; lp++) { 275 if (lp->type == LP) { 276 parens++; 277 continue; 278 } 279 if (lp->type==RP) 280 parens--; 281 if (lp->type==DSHARP) 282 lp->type = DSHARP1; /* ## not special in arg */ 283 if (lp->type==COMMA && parens==0 || parens<0 && (lp-1)->type!=LP) { 284 if (*narg>=NARG-1) 285 error(FATAL, "Sorry, too many macro arguments"); 286 ttr.bp = ttr.tp = bp; 287 ttr.lp = lp; 288 atr[(*narg)++] = normtokenrow(&ttr); 289 bp = lp+1; 290 } 291 } 292 return ntok; 293 } 294 295 /* 296 * substitute the argument list into the replacement string 297 * This would be simple except for ## and # 298 */ 299 void 300 substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr) 301 { 302 Tokenrow tatr; 303 Token *tp; 304 int ntok, argno; 305 306 for (rtr->tp=rtr->bp; rtr->tp<rtr->lp; ) { 307 if (rtr->tp->type==SHARP) { /* string operator */ 308 tp = rtr->tp; 309 rtr->tp += 1; 310 if ((argno = lookuparg(np, rtr->tp))<0) { 311 error(ERROR, "# not followed by macro parameter"); 312 continue; 313 } 314 ntok = 1 + (rtr->tp - tp); 315 rtr->tp = tp; 316 insertrow(rtr, ntok, stringify(atr[argno])); 317 continue; 318 } 319 if (rtr->tp->type==NAME 320 && (argno = lookuparg(np, rtr->tp)) >= 0) { 321 if ((rtr->tp+1)->type==DSHARP 322 || (rtr->tp-1)->type==DSHARP) 323 insertrow(rtr, 1, atr[argno]); 324 else { 325 copytokenrow(&tatr, atr[argno]); 326 expandrow(&tatr, "<macro>"); 327 insertrow(rtr, 1, &tatr); 328 dofree(tatr.bp); 329 } 330 continue; 331 } 332 rtr->tp++; 333 } 334 } 335 336 /* 337 * Evaluate the ## operators in a tokenrow 338 */ 339 void 340 doconcat(Tokenrow *trp) 341 { 342 Token *ltp, *ntp; 343 Tokenrow ntr; 344 int len; 345 346 for (trp->tp=trp->bp; trp->tp<trp->lp; trp->tp++) { 347 if (trp->tp->type==DSHARP1) 348 trp->tp->type = DSHARP; 349 else if (trp->tp->type==DSHARP) { 350 char tt[128]; 351 ltp = trp->tp-1; 352 ntp = trp->tp+1; 353 if (ltp<trp->bp || ntp>=trp->lp) { 354 error(ERROR, "## occurs at border of replacement"); 355 continue; 356 } 357 len = ltp->len + ntp->len; 358 strncpy((char*)tt, (char*)ltp->t, ltp->len); 359 strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len); 360 tt[len] = '\0'; 361 setsource("<##>", -1, tt); 362 maketokenrow(3, &ntr); 363 gettokens(&ntr, 1); 364 unsetsource(); 365 if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS) 366 error(WARNING, "Bad token %r produced by ##", &ntr); 367 ntr.lp = ntr.bp+1; 368 trp->tp = ltp; 369 makespace(&ntr); 370 insertrow(trp, (ntp-ltp)+1, &ntr); 371 dofree(ntr.bp); 372 trp->tp--; 373 } 374 } 375 } 376 377 /* 378 * tp is a potential parameter name of macro mac; 379 * look it up in mac's arglist, and if found, return the 380 * corresponding index in the argname array. Return -1 if not found. 381 */ 382 int 383 lookuparg(Nlist *mac, Token *tp) 384 { 385 Token *ap; 386 387 if (tp->type!=NAME || mac->ap==NULL) 388 return -1; 389 for (ap=mac->ap->bp; ap<mac->ap->lp; ap++) { 390 if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0) 391 return ap - mac->ap->bp; 392 } 393 return -1; 394 } 395 396 /* 397 * Return a quoted version of the tokenrow (from # arg) 398 */ 399 #define STRLEN 512 400 Tokenrow * 401 stringify(Tokenrow *vp) 402 { 403 static Token t = { STRING }; 404 static Tokenrow tr = { &t, &t, &t+1, 1 }; 405 Token *tp; 406 uchar s[STRLEN]; 407 uchar *sp = s, *cp; 408 int i, instring; 409 410 *sp++ = '"'; 411 for (tp = vp->bp; tp < vp->lp; tp++) { 412 instring = tp->type==STRING || tp->type==CCON; 413 if (sp+2*tp->len >= &s[STRLEN-10]) { 414 error(ERROR, "Stringified macro arg is too long"); 415 break; 416 } 417 if (tp->wslen && (tp->flag&XPWS)==0) 418 *sp++ = ' '; 419 for (i=0, cp=tp->t; i<tp->len; i++) { 420 if (instring && (*cp=='"' || *cp=='\\')) 421 *sp++ = '\\'; 422 *sp++ = *cp++; 423 } 424 } 425 *sp++ = '"'; 426 *sp = '\0'; 427 sp = s; 428 t.len = strlen((char*)sp); 429 t.t = newstring(sp, t.len, 0); 430 return &tr; 431 } 432 433 /* 434 * expand a builtin name 435 */ 436 void 437 builtin(Tokenrow *trp, int biname) 438 { 439 char *op; 440 Token *tp; 441 Source *s; 442 443 tp = trp->tp; 444 trp->tp++; 445 /* need to find the real source */ 446 s = cursource; 447 while (s && s->fd==-1) 448 s = s->next; 449 if (s==NULL) 450 s = cursource; 451 /* most are strings */ 452 tp->type = STRING; 453 if (tp->wslen) { 454 *outp++ = ' '; 455 tp->wslen = 1; 456 } 457 op = outp; 458 *op++ = '"'; 459 switch (biname) { 460 461 case KLINENO: 462 tp->type = NUMBER; 463 op = outnum(op-1, s->line); 464 break; 465 466 case KFILE: 467 strcpy(op, s->filename); 468 op += strlen(s->filename); 469 break; 470 471 case KDATE: 472 strncpy(op, curtime+4, 7); 473 strncpy(op+7, curtime+24, 4); /* Plan 9 asctime disobeys standard */ 474 op += 11; 475 break; 476 477 case KTIME: 478 strncpy(op, curtime+11, 8); 479 op += 8; 480 break; 481 482 default: 483 error(ERROR, "cpp botch: unknown internal macro"); 484 return; 485 } 486 if (tp->type==STRING) 487 *op++ = '"'; 488 tp->t = (uchar*)outp; 489 tp->len = op - outp; 490 outp = op; 491 } 492