scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

parser.c (12747B)


      1 #include <assert.h>
      2 #include <ctype.h>
      3 #include <errno.h>
      4 #include <limits.h>
      5 #include <stdarg.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include "make.h"
     11 
     12 #define MAXREPL  30
     13 #define TABSIZ   64
     14 #define MAXTOKEN 256
     15 #define ITEM     128
     16 
     17 typedef struct macro Macro;
     18 
     19 enum inputype {
     20 	FTFILE,
     21 	FTEXPAN,
     22 };
     23 
     24 struct loc {
     25 	char *fname;
     26 	int lineno;
     27 };
     28 
     29 struct input {
     30 	int siz;
     31 	int type;
     32 
     33 	FILE *fp;
     34 	struct loc loc;
     35 
     36 	int pos;
     37 	char *buf;
     38 
     39 	struct input *prev;
     40 };
     41 
     42 struct macro {
     43 	char *name;
     44 	char *value;
     45 
     46 	struct macro *next;
     47 };
     48 
     49 static struct input *input;
     50 static char token[MAXTOKEN];
     51 static int tok;
     52 static Macro *htab[TABSIZ];
     53 
     54 void
     55 dumpmacros(void)
     56 {
     57 	Macro **pp, *p;
     58 
     59 	for (pp = htab; pp < &htab[TABSIZ]; ++pp) {
     60 		for (p = *pp; p; p = p->next)
     61 			printf("%s = %s\n", p->name, getmacro(p->name));
     62 	}
     63 }
     64 
     65 static Macro *
     66 lookup(char *name)
     67 {
     68 	Macro *mp;
     69 	int h = hash(name) & TABSIZ-1;
     70 
     71 	for (mp = htab[h]; mp && strcmp(mp->name, name); mp = mp->next)
     72 		;
     73 
     74 	if (mp)
     75 		return mp;
     76 
     77 	mp = emalloc(sizeof(*mp));
     78 	mp->name = estrdup(name);
     79 	mp->value = NULL;
     80 	mp->next = htab[h];
     81 	htab[h] = mp;
     82 
     83 	return mp;
     84 }
     85 
     86 void
     87 setmacro(char *name, char *val, int export)
     88 {
     89 	int n;
     90 	char *buf;
     91 	Macro *mp;
     92 
     93 	mp = lookup(name);
     94 	free(mp->value);
     95 	mp->value = estrdup(val);
     96 
     97 	if (export && strcmp(name, "SHELL") != 0) {
     98 		n = snprintf(NULL, 0, "%s=%s", name, val);
     99 		buf = emalloc(n+1);
    100 		snprintf(buf, n+1, "%s=%s", name, val);
    101 		putenv(buf);
    102 	}
    103 }
    104 
    105 char *
    106 getmacro(char *name)
    107 {
    108 	int hide;
    109 	char *s, *t;
    110 	Macro *mp = lookup(name);
    111 
    112 	hide = 0;
    113 	if (!strcmp(name, "SHELL") || !strcmp(name, "MAKEFLAGS"))
    114 		hide = 1;
    115 
    116 	s = mp->value;
    117 	if (!s && !hide)
    118 		s = getenv(name);
    119 	if (!s)
    120 		s = "";
    121 
    122 	if (eflag && !hide) {
    123 		t = getenv(name);
    124 		if (t)
    125 			s = t;
    126 	}
    127 
    128 	return s;
    129 }
    130 
    131 static struct loc *
    132 getloc(void)
    133 {
    134 	struct input *ip;
    135 
    136 	for (ip = input; ip && ip->type != FTFILE; ip = ip->prev)
    137 		;
    138 	if (!ip)
    139 		return NULL;
    140 
    141 	return &ip->loc;
    142 }
    143 
    144 
    145 void
    146 error(char *fmt, ...)
    147 {
    148 	va_list va;
    149 	struct loc *loc;
    150 
    151 	fprintf(stderr, "make: error: ");
    152 	if ((loc = getloc()) != NULL)
    153 		fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);
    154 
    155 	va_start(va, fmt);
    156 	vfprintf(stderr, fmt, va);
    157 	va_end(va);
    158 	putc('\n', stderr);
    159 
    160 	exit(EXIT_FAILURE);
    161 }
    162 
    163 void
    164 warning(char *fmt, ...)
    165 {
    166 	va_list va;
    167 	struct loc *loc;
    168 
    169 	fprintf(stderr, "make: warning: ");
    170 	if ((loc = getloc()) != NULL)
    171 		fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);
    172 
    173 	va_start(va, fmt);
    174 	vfprintf(stderr, fmt, va);
    175 	va_end(va);
    176 	putc('\n', stderr);
    177 }
    178 
    179 static void
    180 pop(void)
    181 {
    182 	struct input *ip = input->prev;
    183 
    184 	if (input->type == FTFILE) {
    185 		fclose(input->fp);
    186 		free(input->loc.fname);
    187 	}
    188 	free(input->buf);
    189 	free(input);
    190 
    191 	input = ip;
    192 }
    193 
    194 static void
    195 push(int type, ...)
    196 {
    197 	int len, pos;
    198 	FILE *fp = NULL;
    199 	char *buf, *s, *fname = NULL;
    200 	va_list va;
    201 	struct input *ip;
    202 
    203 	va_start(va, type);
    204 	switch (type) {
    205 	case FTFILE:
    206 		s = va_arg(va, char *);
    207 		fp = va_arg(va, FILE *);
    208 		fname = estrdup(s);
    209 		buf = emalloc(BUFSIZ);
    210 		pos = len = BUFSIZ;
    211 		break;
    212 	case FTEXPAN:
    213 		s = va_arg(va, char *);
    214 		buf = estrdup(s);
    215 		pos = 0;
    216 		len = strlen(s);
    217 		break;
    218 	}
    219 	va_end(va);
    220 
    221 	ip = emalloc(sizeof(*ip));
    222 	ip->siz = len;
    223 	ip->buf = buf;
    224 	ip->type = type;
    225 	ip->fp = fp;
    226 	ip->loc.fname = fname;
    227 	ip->loc.lineno = 1;
    228 	ip->pos = pos;
    229 	ip->prev = input;
    230 
    231 	input = ip;
    232 }
    233 
    234 static char *
    235 trim(char *s)
    236 {
    237 	size_t len;
    238 
    239 	while (isspace(*s))
    240 		s++;
    241 
    242 	for (len = strlen(s); len > 0 && isspace(s[len-1]); --len)
    243 		s[len-1] = '\0';
    244 
    245 	return s;
    246 }
    247 
    248 static void
    249 include(char *s)
    250 {
    251 	int len;
    252 	FILE *fp;
    253 	char *fil, *t;
    254 
    255 	s = trim(s);
    256 	fil = expandstring(s, NULL);
    257 
    258 	t = trim(fil);
    259 	if (strlen(t) != 0) {
    260 		debug("including '%s'", t);
    261 		if ((fp = fopen(t, "r")) == NULL)
    262 			error("opening %s:%s", t, strerror(errno));
    263 		push(FTFILE, t, fp);
    264 	}
    265 
    266 end:
    267 	free(fil);
    268 }
    269 
    270 static char *
    271 nextline(void)
    272 {
    273 	int c;
    274 	FILE *fp;
    275 	char *s, *lim;
    276 
    277 	assert(input->type == FTFILE);
    278 
    279 repeat:
    280 	fp = input->fp;
    281 	if (feof(fp))
    282 		return NULL;
    283 
    284 	lim = &input->buf[input->siz];
    285 	for (s = input->buf; s < lim; *s++ = c) {
    286 		c = getc(fp);
    287 		if (c == '\n' || c == EOF) {
    288 			input->loc.lineno++;
    289 			*s++ = '\n';
    290 			break;
    291 		}
    292 		if (c > UCHAR_MAX || c < 0)
    293 			error("invalid character '%c' (%d)", c, c);
    294 	}
    295 
    296 
    297 	if (s == lim)
    298 		error("too long line");
    299 	if (ferror(fp))
    300 		error(strerror(errno));
    301 	*s = '\0';
    302 
    303 	if (!strcmp(input->buf, ""))
    304 		goto repeat;
    305 
    306 	if (strncmp(input->buf, "include", 7) == 0) {
    307 		input->pos = input->siz;
    308 		include(input->buf+7);
    309 		goto repeat;
    310 	}
    311 
    312 	input->pos = 0;
    313 
    314 
    315 	return input->buf;
    316 }
    317 
    318 static int
    319 empty(struct input *ip)
    320 {
    321 	return ip->pos == ip->siz || ip->buf[ip->pos] == '\0';
    322 }
    323 
    324 static int
    325 moreinput(void)
    326 {
    327 	while (input) {
    328 		if (!empty(input))
    329 			break;
    330 
    331 		switch (input->type) {
    332 		case FTEXPAN:
    333 			pop();
    334 			break;
    335 		case FTFILE:
    336 			if (!nextline())
    337 				pop();
    338 			break;
    339 		}
    340 	}
    341 
    342 	return input != NULL;
    343 }
    344 
    345 static int
    346 nextc(void)
    347 {
    348 	if (!moreinput())
    349 		return EOF;
    350 
    351 	return input->buf[input->pos++];
    352 }
    353 
    354 static int
    355 back(int c)
    356 {
    357 	if (c == EOF)
    358 		return c;
    359 	assert(input->pos > 0);
    360 	return input->buf[--input->pos] = c;
    361 }
    362 
    363 static void
    364 comment(void)
    365 {
    366 	int c;
    367 
    368 	while ((c = nextc()) != EOF && c != '\n') {
    369 		if (c == '\\' && nextc() == EOF)
    370 			break;
    371 	}
    372 }
    373 
    374 static void
    375 skipspaces(void)
    376 {
    377 	int c;
    378 
    379 	for (c = nextc(); c == ' ' || c == '\t'; c = nextc())
    380 		;
    381 	back(c);
    382 }
    383 
    384 static int
    385 validchar(int c)
    386 {
    387 	if (c == EOF)
    388 		return 0;
    389 	return c == '.' || c == '/' || c == '_' || c == '-' || isalnum(c);
    390 }
    391 
    392 static int
    393 item(void)
    394 {
    395 	int c;
    396 	char *s;
    397 
    398 	for (s = token; s < &token[MAXTOKEN] - 1; *s++ = c) {
    399 		c = nextc();
    400 		if (!validchar(c))
    401 			break;
    402 	}
    403 
    404 	if (s >= &token[MAXTOKEN] - 1)
    405 		error("token too long");
    406 	if (s == token)
    407 		error("invalid empty token");
    408 	*s = '\0';
    409 	back(c);
    410 
    411 	return ITEM;
    412 }
    413 
    414 static void
    415 expandmacro(char *name, char *repl, char *to)
    416 {
    417 	int pos, siz, replsiz, tosiz;
    418 	char *s, *t, *p, *buf;
    419 
    420 	s = expandstring(getmacro(name), NULL);
    421 
    422 	pos = 0;
    423 	buf = NULL;
    424 	tosiz = strlen(to);
    425 	replsiz = strlen(repl);
    426 
    427 	t = s;
    428 	for (pos = 0; *t; pos += siz) {
    429 		if (replsiz > 0 && strncmp(t, repl, replsiz) == 0) {
    430 			siz = tosiz;
    431 			p = to;
    432 			t += replsiz;
    433 		} else {
    434 			siz = 1;
    435 			p = t;
    436 			t++;
    437 		}
    438 
    439 		buf = erealloc(buf, pos + siz + 1);
    440 		memcpy(buf+pos, p, siz);
    441 	}
    442 
    443 	if (pos > 0) {
    444 		buf[pos] = '\0';
    445 		push(FTEXPAN, buf);
    446 		free(buf);
    447 	}
    448 	free(s);
    449 }
    450 
    451 static void
    452 expandsimple(Target *tp)
    453 {
    454 	char *s;
    455 	Target **p;
    456 	int len, c;
    457 
    458 	switch (c = nextc()) {
    459 	case '@':
    460 		if (!tp || !tp->target)
    461 			return;
    462 		push(FTEXPAN, tp->target);
    463 		break;
    464 	case '<':
    465 		if (!tp || !tp->req)
    466 			return;
    467 		push(FTEXPAN, tp->req);
    468 		break;
    469 	case '*':
    470 		if (!tp || !tp->target)
    471 			return;
    472 		s = strrchr(tp->target, '.');
    473 		if (!s) {
    474 			push(FTEXPAN, tp->target);
    475 			return;
    476 		}
    477 
    478 		len = s - tp->target;
    479 		s = emalloc(len+1);
    480 		memcpy(s, tp->target, len);
    481 		s[len] = '\0';
    482 		push(FTEXPAN, s);
    483 		free(s);
    484 		break;
    485 	case '?':
    486 		if (!tp)
    487 			return;
    488 
    489 		if (tp->req && stamp(tp->req) > tp->stamp) {
    490 			push(FTEXPAN, " ");
    491 			push(FTEXPAN, tp->req);
    492 		}
    493 
    494 		for (p = tp->deps; p && *p; ++p) {
    495 			if (stamp((*p)->name) > tp->stamp) {
    496 				push(FTEXPAN, " ");
    497 				push(FTEXPAN, (*p)->name);
    498 			}
    499 		}
    500 		break;
    501 	default:
    502 		token[0] = c;
    503 		token[1] = '\0';
    504 		expandmacro(token, "", "");
    505 		break;
    506 	}
    507 }
    508 
    509 static void
    510 expansion(Target *tp)
    511 {
    512 	int delim, c, repli, toi, namei, st;
    513 	char name[MAXTOKEN], repl[MAXREPL], to[MAXREPL];
    514 
    515 	c = nextc();
    516 	if (c == '(')
    517 		delim = ')';
    518 	else if (c == '{')
    519 		delim = '}';
    520 	else
    521 		delim = 0;
    522 
    523 	if (!delim) {
    524 		back(c);
    525 		expandsimple(tp);
    526 	} else {
    527 		st = namei = repli = toi = 0;
    528 		while ((c = nextc()) != EOF) {
    529 			if (c == delim)
    530 				break;
    531 
    532 			switch (st) {
    533 			case 0:
    534 				if (c == ':') {
    535 					st = 1;
    536 					continue;
    537 				}
    538 				if (!validchar(c))
    539 					error("invalid macro name in expansion");
    540 				if (namei == MAXTOKEN-1)
    541 					error("expansion text too long");
    542 				name[namei++] = c;
    543 				break;
    544 			case 1:
    545 				if (c == '=') {
    546 					st = 2;
    547 					continue;
    548 				}
    549 				if (repli == MAXREPL-1)
    550 					error("macro replacement too big");
    551 				repl[repli++] = c;
    552 				break;
    553 			case 2:
    554 				if (toi == MAXREPL-1)
    555 					error("macro substiturion too big");
    556 				to[toi++] = c;
    557 				break;
    558 			}
    559 		}
    560 
    561 		if (c == EOF)
    562 			error("found eof while parsing expansion");
    563 		if (st > 0 && (namei == 0 ||  repli == 0 || to == 0))
    564 			error("invalid macro expansion");
    565 
    566 		name[namei] = '\0';
    567 		repl[repli] = '\0';
    568 		to[toi] = '\0';
    569 		expandmacro(name, repl, to);
    570 	}
    571 }
    572 
    573 /*
    574  * Horrible hack to do string expansion.
    575  * We cannot use normal push and nextc because that
    576  * would consume characters of the current file too.
    577  * For that reason it cleans the input and it recovers
    578  * it later.
    579  */
    580 char *
    581 expandstring(char *line, Target *tp)
    582 {
    583 	int c, n;
    584 	char *s;
    585 	struct input *ip = input;
    586 
    587 	input = NULL;
    588 	push(FTEXPAN, line);
    589 
    590 	n = 0;
    591 	s = NULL;
    592 	while ((c = nextc()) != EOF) {
    593 		if (c == '$') {
    594 			if ((c = nextc()) != '$') {
    595 				back(c);
    596 				expansion(tp);
    597 				continue;
    598 			}
    599 		}
    600 
    601 		s = erealloc(s, ++n);
    602 		s[n-1] = c;
    603 	}
    604 
    605 	s = erealloc(s, n+1);
    606 	s[n] = '\0';
    607 	input = ip;
    608 
    609 	return s;
    610 }
    611 
    612 static int
    613 readchar(void)
    614 {
    615 	int c;
    616 
    617 	while ((c = nextc()) != EOF) {
    618 		if (c == ' ' || c == '\t')
    619 			continue;
    620 		if (c == '\\') {
    621 			if ((c = nextc()) == '\n')
    622 				continue;
    623 			back(c);
    624 			c = '\\';
    625 		}
    626 		break;
    627 	}
    628 
    629 	return c;
    630 }
    631 
    632 static int
    633 next(void)
    634 {
    635 	int c;
    636 
    637 repeat:
    638 	c = readchar();
    639 
    640 	switch (c) {
    641 	case EOF:
    642 		strcpy(token, "<EOF>");
    643 		tok = EOF;
    644 		break;
    645 	case '$':
    646 		if ((c = nextc()) == '$')
    647 			goto single;
    648 		back(c);
    649 		expansion(NULL);
    650 		goto repeat;
    651 	case '#':
    652 		comment();
    653 		c = '\n';
    654 	case ';':
    655 	case ':':
    656 	case '=':
    657 	case '\n':
    658 	single:
    659 		token[0] = c;
    660 		token[1] = '\0';
    661 		tok = c;
    662 		break;
    663 	default:
    664 		if (!validchar(c))
    665 			error("unexpected character '%c'", c);
    666 		back(c);
    667 		tok = item();
    668 		break;
    669 	}
    670 
    671 	return tok;
    672 }
    673 
    674 static char *
    675 readmacrodef(void)
    676 {
    677 	int n, c;
    678 	char *line;
    679 
    680 	n = 0;
    681 	line = NULL;
    682 	while ((c = nextc()) != EOF) {
    683 		line = erealloc(line, n+1);
    684 		if (c == '\n')
    685 			break;
    686 		if (c == '#') {
    687 			comment();
    688 			break;
    689 		}
    690 		if (c == '\\') {
    691 			if ((c = nextc()) != '\n') {
    692 				back(c);
    693 				c = '\\';
    694 			} else {
    695 				skipspaces();
    696 				c = ' ';
    697 			}
    698 		}
    699 
    700 		line[n++] = c;
    701 	}
    702 	if (c == EOF)
    703 		error("EOF while looking for end of line");
    704 	line[n] = '\0';
    705 
    706 	return line;
    707 }
    708 
    709 static char *
    710 readcmd(void)
    711 {
    712 	int n, c;
    713 	char *line;
    714 
    715 	skipspaces();
    716 
    717 	n = 0;
    718 	line = NULL;
    719 	while ((c = nextc()) != EOF) {
    720 		line = erealloc(line, n+1);
    721 		if (c == '\n')
    722 			break;
    723 		if (c == '\\') {
    724 			if ((c = nextc()) == '\n') {
    725 				if ((c = nextc()) != '\t')
    726 					back(c);
    727 				continue;
    728 			}
    729 			back(c);
    730 			c = '\\';
    731 		}
    732 		line[n++] = c;
    733 	}
    734 	if (c == EOF)
    735 		error("EOF while looking for end of command");
    736 	line[n] = '\0';
    737 
    738 	return line;
    739 }
    740 
    741 static void
    742 rule(char *targets[], int ntargets)
    743 {
    744 	int c, i, j, ndeps, nactions;
    745 	char **actions, **deps = NULL;
    746 
    747 	if (ntargets == 0)
    748 		error("missing target");
    749 
    750 	for (ndeps = 0; next() == ITEM; ++ndeps) {
    751 		deps = erealloc(deps, (ndeps+1) * sizeof(char *));
    752 		deps[ndeps] = estrdup(token);
    753 	}
    754 
    755 	if (tok != '\n' && tok != ';')
    756 		error("garbage at the end of the line");
    757 
    758 	nactions = 0;
    759 	actions = NULL;
    760 	if (tok == ';') {
    761 		nactions++;
    762 		actions = erealloc(actions, nactions * sizeof(char *));
    763 		actions[nactions-1] = readcmd();
    764 	}
    765 
    766 	for (;;) {
    767 		if ((c = nextc()) == '#') {
    768 			comment();
    769 			continue;
    770 		}
    771 		if (c != '\t')
    772 			break;
    773 		nactions++;
    774 		actions = erealloc(actions, nactions * sizeof(char *));
    775 		actions[nactions-1] = readcmd();
    776 	}
    777 	back(c);
    778 
    779 	for (i = 0; i < ntargets; i++) {
    780 		addtarget(targets[i], ndeps);
    781 		for (j = 0; j < ndeps; j++)
    782 			adddep(targets[i], deps[j]);
    783 		if (nactions > 0)
    784 			addrule(targets[i], actions, nactions);
    785 	}
    786 
    787 	for (i = 0; i < ndeps; i++)
    788 		free(deps[i]);
    789 	free(deps);
    790 
    791 	for (i = 0; i < nactions; i++)
    792 		free(actions[i]);
    793 	free(actions);
    794 }
    795 
    796 static void
    797 assign(char *macros[], int n)
    798 {
    799 	int len, c;
    800 	char *defs;
    801 
    802 	if (n != 1)
    803 		error("invalid macro definition");
    804 
    805 	skipspaces();
    806 	defs = readmacrodef();
    807 	setmacro(*macros, defs, NOEXPORT);
    808 	free(defs);
    809 }
    810 
    811 void
    812 parseinput(void)
    813 {
    814 	int i, n;
    815 	char **targets;
    816 
    817 	while (moreinput()) {
    818 		n = 0;
    819 		targets = NULL;
    820 
    821 		next();
    822 		if (tok == '\n')
    823 			continue;
    824 
    825 		while (tok == ITEM) {
    826 			n++;
    827 			targets = erealloc(targets, n * sizeof(char *));
    828 			targets[n-1] = estrdup(token);
    829 			next();
    830 		}
    831 
    832 		switch (tok) {
    833 		case ':':
    834 			rule(targets, n);
    835 			break;
    836 		case '=':
    837 			assign(targets, n);
    838 			break;
    839 		default:
    840 			error("unexpected token '%s'(%d)", token, tok);
    841 		}
    842 
    843 		for (i = 0; i < n; i++)
    844 			free(targets[i]);
    845 		free(targets);
    846 	}
    847 }
    848 
    849 int
    850 parse(char *fname)
    851 {
    852 	FILE *fp;
    853 
    854 	if (!fname) {
    855 		fp = stdin;
    856 		fname = "<stdin>";
    857 	} else if ((fp = fopen(fname, "r")) == NULL) {
    858 		return 0;
    859 	}
    860 
    861 	debug("parsing %s", fname);
    862 	push(FTFILE, fname, fp);
    863 	parseinput();
    864 
    865 	return 1;
    866 }
    867 
    868 void
    869 inject(char *s)
    870 {
    871 	push(FTEXPAN, s);
    872 	parseinput();
    873 }