scc

Simple C99 Compiler
Log | Files | Refs | README | LICENSE

cpp.c (14137B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <limits.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <time.h>
      8 
      9 #include "../inc/sizes.h"
     10 #include "../inc/cc.h"
     11 #include "arch.h"
     12 #include "cc1.h"
     13 
     14 static char *argp, *macroname;
     15 static unsigned arglen;
     16 static unsigned ncmdlines;
     17 static Symbol *symline, *symfile;
     18 static unsigned char ifstatus[NR_COND];
     19 static int ninclude;
     20 static char **dirinclude;
     21 
     22 unsigned cppctx;
     23 int disexpand;
     24 
     25 void
     26 defdefine(char *macro, char *val)
     27 {
     28 	char *def, *fmt = "#define %s %s";
     29 
     30 	if (!val)
     31 		val = "";
     32 	def = xmalloc(strlen(fmt) + strlen(macro) + strlen(val));
     33 
     34 	sprintf(def, fmt, macro, val);
     35 	allocinput("command-line", NULL, def);
     36 	input->nline = ++ncmdlines;
     37 	cpp();
     38 	delinput();
     39 }
     40 
     41 void
     42 undefmacro(char *s)
     43 {
     44 	killsym(lookup(NS_CPP, s));
     45 }
     46 
     47 void
     48 icpp(void)
     49 {
     50 	static char sdate[14], stime[11];
     51 	struct tm *tm;
     52 	time_t t;
     53 	static char **bp, *list[] = {
     54 		"__STDC__",
     55 		"__STDC_HOSTED__",
     56 		"__SCC__",
     57 		NULL
     58 	};
     59 	static struct keyword keys[] = {
     60 		{"define", DEFINE, DEFINE},
     61 		{"include", INCLUDE, INCLUDE},
     62 		{"line", LINE, LINE},
     63 		{"ifdef", IFDEF, IFDEF},
     64 		{"if", IF, IF},
     65 		{"elif", ELIF, ELIF},
     66 		{"else", ELSE, ELSE},
     67 		{"ifndef", IFNDEF, IFNDEF},
     68 		{"endif", ENDIF, ENDIF},
     69 		{"undef", UNDEF, UNDEF},
     70 		{"pragma", PRAGMA, PRAGMA},
     71 		{"error", ERROR, ERROR},
     72 		{NULL, 0, 0}
     73 	};
     74 
     75 	keywords(keys, NS_CPPCLAUSES);
     76 
     77 	t = time(NULL);
     78 	tm = localtime(&t);
     79 	strftime(sdate, sizeof(sdate), "\"%b %d %Y\"", tm);
     80 	strftime(stime, sizeof(stime), "\"%H:%M:%S\"", tm);
     81 	defdefine("__DATE__", sdate);
     82 	defdefine("__TIME__", stime);
     83 	defdefine("__STDC_VERSION__", "199409L");
     84 	defdefine("__LINE__", NULL);
     85 	defdefine("__FILE__", NULL);
     86 
     87 	symline = lookup(NS_CPP, "__LINE__");
     88 	symfile = lookup(NS_CPP, "__FILE__");
     89 
     90 	for (bp = list; *bp; ++bp)
     91 		defdefine(*bp, NULL);
     92 
     93 	ncmdlines = 0;
     94 }
     95 
     96 static void
     97 nextcpp(void)
     98 {
     99 	next();
    100 	if (yytoken == EOFTOK)
    101 		error("unterminated argument list invoking macro \"%s\"",
    102 		      macroname);
    103 	if (yylen + 1 > arglen)
    104 		error("argument overflow invoking macro \"%s\"",
    105 		      macroname);
    106 	if (yytoken == IDEN)
    107 		yylval.sym->flags |= SUSED;
    108 	memcpy(argp, yytext, yylen);
    109 	argp += yylen;
    110 	*argp++ = ' ';
    111 	arglen -= yylen + 1;
    112 }
    113 
    114 static void
    115 paren(void)
    116 {
    117 	for (;;) {
    118 		nextcpp();
    119 		switch (yytoken) {
    120 		case ')':
    121 			return;
    122 		case '(':
    123 			paren();
    124 			break;
    125 		}
    126 	}
    127 }
    128 
    129 static void
    130 parameter(void)
    131 {
    132 	for (;;) {
    133 		nextcpp();
    134 		switch (yytoken) {
    135 		case ')':
    136 		case ',':
    137 			argp -= 3;  /* remove " , "  or " ) "*/
    138 			*argp++ = '\0';
    139 			return;
    140 		case '(':
    141 			paren();
    142 			break;
    143 		}
    144 	}
    145 }
    146 
    147 static int
    148 parsepars(char *buffer, char **listp, int nargs)
    149 {
    150 	int n;
    151 
    152 	if (nargs == -1)
    153 		return -1;
    154 	if (ahead() != '(' && nargs > 0)
    155 		return 0;
    156 
    157 	disexpand = 1;
    158 	next();
    159 	n = 0;
    160 	argp = buffer;
    161 	arglen = INPUTSIZ;
    162 	if (yytoken != ')') {
    163 		do {
    164 			*listp++ = argp;
    165 			parameter();
    166 		} while (++n < NR_MACROARG && yytoken == ',');
    167 	}
    168 	if (yytoken != ')')
    169 		error("incorrect macro function-alike invocation");
    170 	disexpand = 0;
    171 
    172 	if (n == NR_MACROARG)
    173 		error("too many parameters in macro \"%s\"", macroname);
    174 	if (n != nargs) {
    175 		error("macro \"%s\" received %d arguments, but it takes %d",
    176 		      macroname, n, nargs);
    177 	}
    178 
    179 	return 1;
    180 }
    181 
    182 /* FIXME: characters in the definition break the macro definition */
    183 static size_t
    184 copymacro(char *buffer, char *s, size_t bufsiz, char *arglist[])
    185 {
    186 	char prevc, c, *p, *arg, *bp = buffer;
    187 	size_t size;
    188 
    189 	for (prevc = '\0'; c = *s; prevc = c, ++s) {
    190 		if (c != '@') {
    191 			switch (c) {
    192 			case '$':
    193 				while (bp[-1] == ' ')
    194 					--bp, ++bufsiz;
    195 				while (s[1] == ' ')
    196 					++s;
    197 			case '#':
    198 				continue;
    199 			case '\"':
    200 				for (p = s; *++s != '"'; )
    201 					/* nothing */;
    202 				size = s - p + 1;
    203 				if (size > bufsiz)
    204 					goto expansion_too_long;
    205 				memcpy(bp, p, size);
    206 				bufsiz -= size;
    207 				bp += size;
    208 				continue;
    209 			// case '\'';
    210 			}
    211 			if (bufsiz-- == 0)
    212 				goto expansion_too_long;
    213 			*bp++ = c;
    214 		} else {
    215 			if (prevc == '#')
    216 				bufsiz -= 2;
    217 			arg = arglist[atoi(++s)];
    218 			size = strlen(arg);
    219 			if (size > bufsiz)
    220 				goto expansion_too_long;
    221 			if (prevc == '#')
    222 				*bp++ = '"';
    223 			memcpy(bp, arg, size);
    224 			bp += size;
    225 			if (prevc == '#')
    226 				*bp++ = '"';
    227 			bufsiz -= size;
    228 			s += 2;
    229 		}
    230 	}
    231 	*bp = '\0';
    232 
    233 	return bp - buffer;
    234 
    235 expansion_too_long:
    236 	error("macro expansion of \"%s\" too long", macroname);
    237 }
    238 
    239 #define BUFSIZE ((INPUTSIZ > FILENAME_MAX+2) ? INPUTSIZ : FILENAME_MAX+2)
    240 int
    241 expand(char *begin, Symbol *sym)
    242 {
    243 	size_t total, elen, rlen, llen, ilen;
    244 	int n;
    245 	char *s = sym->u.s;
    246 	char *arglist[NR_MACROARG], arguments[INPUTSIZ], buffer[BUFSIZE];
    247 
    248 	macroname = sym->name;
    249 	if ((sym->flags & SDECLARED) == 0) {
    250 		if (namespace == NS_CPP && !strcmp(sym->name, "defined"))
    251 			return 0;  /* we found a 'defined in an #if */
    252 		/*
    253 		 * This case happens in #if were macro not defined must
    254 		 * be expanded to 0
    255 		 */
    256 		buffer[0] = '0';
    257 		buffer[1] = '\0';
    258 		elen = 1;
    259 		goto substitute;
    260 	}
    261 	if (sym == symfile) {
    262 		elen = sprintf(buffer, "\"%s\" ", input->fname);
    263 		goto substitute;
    264 	}
    265 	if (sym == symline) {
    266 		elen = sprintf(buffer, "%d ", input->nline);
    267 		goto substitute;
    268 	}
    269 	if (!s)
    270 		return 1;
    271 
    272 	if (!parsepars(arguments, arglist, atoi(s)))
    273 		return 0;
    274 	for (n = 0; n < atoi(s); ++n)
    275 		DBG("MACRO par%d:%s", n, arglist[n]);
    276 
    277 	elen = copymacro(buffer, s+3, INPUTSIZ-1, arglist);
    278 
    279 substitute:
    280 	DBG("MACRO '%s' expanded to :'%s'", macroname, buffer);
    281 	rlen = strlen(input->p);      /* rigth length */
    282 	llen = begin - input->line;   /* left length */
    283 	ilen = input->p - begin;      /* invocation length */
    284 	total = llen + elen + rlen;
    285 
    286 	if (total >= LINESIZ)
    287 		error("macro expansion of \"%s\" too long", macroname);
    288 
    289 	/* cut macro invocation */
    290 	memmove(begin, begin + ilen, rlen);
    291 
    292 	/* paste macro expansion */
    293 	memmove(begin + elen, begin, rlen);
    294 	memcpy(begin, buffer, elen);
    295 	input->line[total] = '\0';
    296 
    297 	input->p = input->begin = begin;
    298 
    299 	if (!(sym->flags & SDECLARED))
    300 		killsym(sym);
    301 
    302 	return 1;
    303 }
    304 #undef BUFSIZE
    305 
    306 static int
    307 getpars(Symbol *args[NR_MACROARG])
    308 {
    309 	int n, c;
    310 	Symbol *sym;
    311 
    312 	c = *input->p;
    313 	next();
    314 	if (c != '(')
    315 		return -1;
    316 	next(); /* skip the '(' */
    317 	if (accept(')'))
    318 		return 0;
    319 
    320 	n = 0;
    321 	do {
    322 		if (n == NR_MACROARG) {
    323 			cpperror("too many parameters in macro");
    324 			return NR_MACROARG;
    325 		}
    326 		if (yytoken != IDEN) {
    327 			cpperror("macro arguments must be identifiers");
    328 			return NR_MACROARG;
    329 		}
    330 		sym = install(NS_IDEN, yylval.sym);
    331 		sym->flags |= SUSED;
    332 		args[n++] = sym;
    333 		next();
    334 	} while (accept(','));
    335 	expect(')');
    336 
    337 	return n;
    338 }
    339 
    340 static int
    341 getdefs(Symbol *args[NR_MACROARG], int nargs, char *bp, size_t bufsiz)
    342 {
    343 	Symbol **argp;
    344 	size_t len;
    345 	int prevc = 0, ispar;
    346 
    347 	if (yytoken == '$') {
    348 		cpperror("'##' cannot appear at either ends of a macro expansion");
    349 		return 0;
    350 	}
    351 
    352 	for (;;) {
    353 		ispar = 0;
    354 		if (yytoken == IDEN && nargs >= 0) {
    355 			for (argp = args; argp < &args[nargs]; ++argp) {
    356 				if (*argp == yylval.sym)
    357 					break;
    358 			}
    359 			if (argp != &args[nargs]) {
    360 				sprintf(yytext, "@%02d@", (int) (argp - args));
    361 				ispar = 1;
    362 			}
    363 		}
    364 		if (prevc == '#' && !ispar) {
    365 			cpperror("'#' is not followed by a macro parameter");
    366 			return 0;
    367 		}
    368 		if (yytoken == EOFTOK)
    369 			break;
    370 
    371 		if ((len = strlen(yytext)) >= bufsiz) {
    372 			cpperror("macro too long");
    373 			return 0;
    374 		}
    375 		if (yytoken == '$') {
    376 			*bp++ = '$';
    377 			 --bufsiz;
    378 		} else {
    379 			memcpy(bp, yytext, len);
    380 			bp += len;
    381 			bufsiz -= len;
    382 		}
    383 		if ((prevc  = yytoken) != '#')
    384 			*bp++ = ' ';
    385 		next();
    386 	}
    387 	*bp = '\0';
    388 	return 1;
    389 }
    390 
    391 static void
    392 define(void)
    393 {
    394 	Symbol *sym,*args[NR_MACROARG];
    395 	char buff[LINESIZ+1];
    396 	int n;
    397 
    398 	if (cppoff)
    399 		return;
    400 
    401 	namespace = NS_CPP;
    402 	next();
    403 
    404 	if (yytoken != IDEN) {
    405 		cpperror("macro names must be identifiers");
    406 		return;
    407 	}
    408 	sym = yylval.sym;
    409 	if (sym->flags & SDECLARED) {
    410 		warn("'%s' redefined", yytext);
    411 		free(sym->u.s);
    412 	} else {
    413 		sym = install(NS_CPP, sym);
    414 		sym->flags |= SDECLARED|SSTRING;
    415 	}
    416 
    417 	namespace = NS_IDEN;       /* Avoid polution in NS_CPP */
    418 	if ((n = getpars(args)) == NR_MACROARG)
    419 		goto delete;
    420 	sprintf(buff, "%02d#", n);
    421 	if (!getdefs(args, n, buff+3, LINESIZ-3))
    422 		goto delete;
    423 	sym->u.s = xstrdup(buff);
    424 	DBG("MACRO '%s' defined as '%s'", sym->name, buff);
    425 	return;
    426 
    427 delete:
    428 	killsym(sym);
    429 }
    430 
    431 void
    432 incdir(char *dir)
    433 {
    434 	if (!dir || *dir == '\0')
    435 		die("incorrect -I flag");
    436 	++ninclude;
    437 	dirinclude = xrealloc(dirinclude, sizeof(*dirinclude) * ninclude);
    438 	dirinclude[ninclude-1] = dir;
    439 }
    440 
    441 static int
    442 includefile(char *dir, char *file, size_t filelen)
    443 {
    444 	size_t dirlen;
    445 	char path[FILENAME_MAX];
    446 
    447 	if (!dir) {
    448 		dirlen = 0;
    449 		if (filelen > FILENAME_MAX-1)
    450 			return 0;
    451 	} else {
    452 		dirlen = strlen(dir);
    453 		if (dirlen + filelen > FILENAME_MAX-2)
    454 			return 0;
    455 		memcpy(path, dir, dirlen);
    456 		if (dir[dirlen-1] != '/')
    457 			path[dirlen++] = '/';
    458 	}
    459 	memcpy(path+dirlen, file, filelen);
    460 	path[dirlen + filelen] = '\0';
    461 
    462 	return addinput(path);
    463 }
    464 
    465 static void
    466 include(void)
    467 {
    468 	char *file, *p, **bp;
    469 	size_t filelen;
    470 	static char *sysinclude[] = {
    471 		PREFIX "/include/scc/" ARCH  "/",
    472 		PREFIX"/include/",
    473 		PREFIX"/local/include/",
    474 		NULL
    475 	};
    476 	int n;
    477 
    478 	if (cppoff)
    479 		return;
    480 
    481 	namespace = NS_IDEN;
    482 	next();
    483 
    484 	switch (*yytext) {
    485 	case '<':
    486 		if ((p = strchr(input->begin, '>')) == NULL || p == yytext + 1)
    487 			goto bad_include;
    488 		*p = '\0';
    489 		file = input->begin;
    490 		filelen = strlen(file);
    491 		input->begin = input->p = p+1;
    492 		break;
    493 	case '"':
    494 		if ((p = strchr(yytext + 1, '"')) == NULL || p == yytext + 1)
    495 			goto bad_include;
    496 		*p = '\0';
    497 		file = yytext+1;
    498 		filelen = strlen(file);
    499 		if (includefile(NULL, file, filelen))
    500 			goto its_done;
    501 		break;
    502 	default:
    503 		goto bad_include;
    504 	}
    505 
    506 	n = ninclude;
    507 	for (bp = dirinclude; n--; ++bp) {
    508 		if (includefile(*bp, file, filelen))
    509 			goto its_done;
    510 	}
    511 	for (bp = sysinclude; *bp; ++bp) {
    512 		if (includefile(*bp, file, filelen))
    513 			goto its_done;
    514 	}
    515 	cpperror("included file '%s' not found", file);
    516 
    517 its_done:
    518 	next();
    519 	return;
    520 
    521 bad_include:
    522 	cpperror("#include expects \"FILENAME\" or <FILENAME>");
    523 	return;
    524 }
    525 
    526 static void
    527 line(void)
    528 {
    529 	long n;
    530 	char *endp;
    531 
    532 	if (cppoff)
    533 		return;
    534 
    535 	disexpand = 0;
    536 	next();
    537 	n = strtol(yytext, &endp, 10);
    538 	if (n <= 0 || n > USHRT_MAX || *endp != '\0') {
    539 		cpperror("first parameter of #line is not a positive integer");
    540 		return;
    541 	}
    542 
    543 	next();
    544 	if (yytoken == EOFTOK)
    545 		goto set_line;
    546 
    547 	if (*yytext != '\"' || yylen == 1) {
    548 		cpperror("second parameter of #line is not a valid filename");
    549 		return;
    550 	}
    551 
    552 	free(input->fname);
    553 	input->fname = xstrdup(yylval.sym->u.s);
    554 	next();
    555 
    556 set_line:
    557 	input->nline = n - 1;
    558 }
    559 
    560 static void
    561 pragma(void)
    562 {
    563 	static char magic[] = {
    564 	#include "stallman.msg"
    565 	};
    566 
    567 	if (cppoff)
    568 		return;
    569 	next();
    570 	if (!strcmp(yytext, "GCC"))
    571 		warn(magic);
    572 	warn("ignoring pragma '%s'", yytext);
    573 	*input->p = '\0';
    574 	next();
    575 }
    576 
    577 static void
    578 usererr(void)
    579 {
    580 	if (cppoff)
    581 		return;
    582 	cpperror("#error %s", input->p);
    583 	*input->p = '\0';
    584 	next();
    585 }
    586 
    587 static void
    588 ifclause(int negate, int isifdef)
    589 {
    590 	Symbol *sym;
    591 	unsigned n;
    592 	int status;
    593 	Node *expr;
    594 
    595 	if (cppctx == NR_COND-1)
    596 		error("too many nesting levels of conditional inclusion");
    597 
    598 	n = cppctx++;
    599 	namespace = NS_CPP;
    600 	next();
    601 
    602 	if (isifdef) {
    603 		if (yytoken != IDEN) {
    604 			cpperror("no macro name given in #%s directive",
    605 			         (negate) ? "ifndef" : "ifdef");
    606 			return;
    607 		}
    608 		sym = yylval.sym;
    609 		next();
    610 		status = (sym->flags & SDECLARED) != 0;
    611 		if (!status)
    612 			killsym(sym);
    613 	} else {
    614 		/* TODO: catch recovery here */
    615 		if ((expr = iconstexpr()) == NULL) {
    616 			cpperror("parameter of #if is not an integer constant expression");
    617 			return;
    618 		}
    619 		status = expr->sym->u.i != 0;
    620 		freetree(expr);
    621 	}
    622 
    623 	if (negate)
    624 		status = !status;
    625 	if ((ifstatus[n] = status) == 0)
    626 		++cppoff;
    627 }
    628 
    629 static void
    630 cppif(void)
    631 {
    632 	disexpand = 0;
    633 	ifclause(0, 0);
    634 }
    635 
    636 static void
    637 ifdef(void)
    638 {
    639 	ifclause(0, 1);
    640 }
    641 
    642 static void
    643 ifndef(void)
    644 {
    645 	ifclause(1, 1);
    646 }
    647 
    648 static void
    649 elseclause(void)
    650 {
    651 	int status;
    652 
    653 	if (cppctx == 0)
    654 		error("#else without #ifdef/ifndef");
    655 
    656 	status = (ifstatus[cppctx-1] ^= 1);
    657 	cppoff += (status) ? -1 : 1;
    658 }
    659 
    660 static void
    661 cppelse(void)
    662 {
    663 	elseclause();
    664 	next();
    665 }
    666 
    667 static void
    668 elif(void)
    669 {
    670 	elseclause();
    671 	--cppctx;
    672 	cppif();
    673 }
    674 
    675 static void
    676 endif(void)
    677 {
    678 	if (cppctx == 0)
    679 		error("#endif without #if");
    680 	if (!ifstatus[--cppctx])
    681 		--cppoff;
    682 	next();
    683 }
    684 
    685 static void
    686 undef(void)
    687 {
    688 	if (cppoff)
    689 		return;
    690 
    691 	namespace = NS_CPP;
    692 	next();
    693 	if (yytoken != IDEN) {
    694 		error("no macro name given in #undef directive");
    695 		return;
    696 	}
    697 	killsym(yylval.sym);
    698 	next();
    699 }
    700 
    701 int
    702 cpp(void)
    703 {
    704 	static struct {
    705 		unsigned char token;
    706 		void (*fun)(void);
    707 	} *bp, clauses [] = {
    708 		{DEFINE, define},
    709 		{INCLUDE, include},
    710 		{LINE, line},
    711 		{IFDEF, ifdef},
    712 		{IF, cppif},
    713 		{ELIF, elif},
    714 		{IFNDEF, ifndef},
    715 		{ELSE, cppelse},
    716 		{ENDIF, endif},
    717 		{UNDEF, undef},
    718 		{PRAGMA, pragma},
    719 		{ERROR, usererr},
    720 		{0, NULL}
    721 	};
    722 	int ns;
    723 
    724 	if (*input->p != '#')
    725 		return 0;
    726 	++input->p;
    727 
    728 	disexpand = 1;
    729 	lexmode = CPPMODE;
    730 	ns = namespace;
    731 	namespace = NS_CPPCLAUSES;
    732 	next();
    733 	namespace = NS_IDEN;
    734 
    735 	for (bp = clauses; bp->token && bp->token != yytoken; ++bp)
    736 		/* nothing */;
    737 	if (!bp->token) {
    738 		errorp("incorrect preprocessor directive");
    739 		goto error;
    740 	}
    741 
    742 	pushctx();              /* create a new context to avoid polish */
    743 	(*bp->fun)();           /* the current context, and to get all  */
    744 	popctx();               /* the symbols freed at the  end        */
    745 
    746 	if (yytoken != EOFTOK && !cppoff)
    747 		errorp("trailing characters after preprocessor directive");
    748 
    749 error:
    750 	disexpand = 0;
    751 	lexmode = CCMODE;
    752 	namespace = ns;
    753 
    754 	return 1;
    755 }
    756 
    757 void
    758 outcpp(void)
    759 {
    760 	char c, *s, *t;
    761 
    762 	for (next(); yytoken != EOFTOK; next()) {
    763 		if (yytoken != STRING) {
    764 			printf("%s ", yytext);
    765 			continue;
    766 		}
    767 		for (s = yytext; c = *s; ++s) {
    768 			switch (c) {
    769 			case '\n':
    770 				t = "\\n";
    771 				goto print_str;
    772 			case '\v':
    773 				t = "\\v";
    774 				goto print_str;
    775 			case '\b':
    776 				t = "\\b";
    777 				goto print_str;
    778 			case '\t':
    779 				t = "\\t";
    780 				goto print_str;
    781 			case '\a':
    782 				t = "\\a";
    783 			print_str:
    784 				fputs(t, stdout);
    785 				break;
    786 			case '\\':
    787 				putchar('\\');
    788 			default:
    789 				if (!isprint(c))
    790 					printf("\\x%x", c);
    791 				else
    792 					putchar(c);
    793 				break;
    794 			}
    795 		}
    796 		putchar(' ');
    797 	}
    798 	putchar('\n');
    799 }
    800