/* $Header$ */
/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/* @(#)comm5.c	1.1 */

#include	"comm0.h"
#include	"comm1.h"
#include	"y.tab.h"

extern YYSTYPE	yylval;

yylex()
{
	register c;

	if (pass == PASS_1) {
		/* scan the input file */
		do
			c = nextchar();
		while (isspace(c) && c != '\n');
		if (ISALPHA(c))
			c = inident(c);
		else if (isdigit(c))
			c = innumber(c);
		else switch (c) {
		case '=':
		case '<':
		case '>':
		case '|':
		case '&':
			c = induo(c); break;
		case ASC_SQUO:
		case ASC_DQUO:
			c = instring(c); break;
		case ASC_COMM:
			do
				c = nextchar();
			while (c != '\n' && c != '\0');
			break;
		case CTRL('A'):
			c = CODE1; readcode(1); break;
		case CTRL('B'):
			c = CODE2; readcode(2); break;
		case CTRL('C'):
			c = CODE4; readcode(4); break;
		}

		/* produce the intermediate token file */
		if (c <= 0)
			return(0);
		if (c <= 127)
			putc(c, tempfile);
		else
			putval(c);
	} else {
		/* read from intermediate token file */
		c = getc(tempfile);
		if (c == EOF)
			return(0);
		if (c > 127) {
			c += 128;
			c = getval(c);
		}
	}
	return(c);
}

putval(c)
{
	register valu_t v;
	register n;
	register char *p;

	assert(c >= 256 && c < 256+128);
	switch (c) {
	case CODE1:
		n = 1; goto putnum;
	case CODE2:
		n = 2; goto putnum;
	case CODE4:
		n = 4; goto putnum;
	case NUMBER:
		v = yylval.y_valu;
		for (n = 0; n < sizeof(v); n++) {
			if (v == 0)
				break;
			v >>= 8;
		}
		c = NUMBER0 + n;
	putnum:
		putc(c-128, tempfile);
		v = yylval.y_valu;
		while (--n >= 0)
			putc((int) (v >> (n*8)), tempfile);
		return;
	case IDENT:
	case FBSYM:
		n = sizeof(item_t *);
		p = (char *) &yylval.y_item; break;
#ifdef ASLD
	case MODULE:
		n = sizeof(char *);
		p = (char *) &yylval.y_strp; break;
#endif
	case STRING:
		p = stringbuf;
		n = (*p & 0377) + 1; break;
	case OP_EQ:
	case OP_NE:
	case OP_LE:
	case OP_GE:
	case OP_LL:
	case OP_RR:
	case OP_OO:
	case OP_AA:
		n = 0; break;
	default:
		n = sizeof(word_t);
		p = (char *) &yylval.y_word; break;
	}
	putc(c-128, tempfile);
	while (--n >= 0)
		putc(*p++, tempfile);
}

getval(c)
{
	register n;
	register valu_t v;
	register char *p;

	switch (c) {
	case CODE1:
		n = 1; goto getnum;
	case CODE2:
		n = 2; goto getnum;
	case CODE4:
		n = 4; goto getnum;
	case NUMBER0:
		n = 0; c = NUMBER; goto getnum;
	case NUMBER1:
		n = 1; c = NUMBER; goto getnum;
	case NUMBER2:
		n = 2; c = NUMBER; goto getnum;
	case NUMBER3:
		n = 3; c = NUMBER; goto getnum;
	case NUMBER:
		n = 4;
	getnum:
		v = 0;
		while (--n >= 0) {
			v <<= 8;
			v |= getc(tempfile);
		}
		yylval.y_valu = v;
		return(c);
	case IDENT:
	case FBSYM:
		n = sizeof(item_t *);
		p = (char *) &yylval.y_item; break;
#ifdef ASLD
	case MODULE:
		n = sizeof(char *);
		p = (char *) &yylval.y_strp; break;
#endif
	case STRING:
		p = stringbuf;
		*p++ = n = getc(tempfile); p[n] = '\0'; break;
	case OP_EQ:
	case OP_NE:
	case OP_LE:
	case OP_GE:
	case OP_LL:
	case OP_RR:
	case OP_OO:
	case OP_AA:
		n = 0; break;
	default:
		n = sizeof(word_t);
		p = (char *) &yylval.y_word; break;
	}
	while (--n >= 0)
		*p++ = getc(tempfile);
	return(c);
}

/* ---------- lexical scan in pass 1 ---------- */

nextchar()
{
	register c;

	if (peekc != -1) {
		c = peekc;
		peekc = -1;
		return(c);
	}
#ifdef ASLD
	if (archmode && --archsize < 0)
		return(0);
#endif
	if ((c = getc(input)) == EOF)
		return(0);
	if (isascii(c) == 0)
		fatal("non-ascii character");
#ifdef LISTING
	if (listflag & 0440)
		putc(c, listfile);
#endif
	return(c);
}

readcode(n)
{
	register c;

	yylval.y_valu = 0;
	do {
		if (
#ifdef ASLD
			(archmode && --archsize < 0)
			||
#endif
			(c = getc(input)) == EOF
		)
			fatal("unexpected EOF in compact input");
		yylval.y_valu <<= 8;
		yylval.y_valu |= c;
	} while (--n);
}

induo(c)
register c;
{
	static short duo[] = {
		('='<<8) | '=', OP_EQ,
		('<'<<8) | '>', OP_NE,
		('<'<<8) | '=', OP_LE,
		('>'<<8) | '=', OP_GE,
		('<'<<8) | '<', OP_LL,
		('>'<<8) | '>', OP_RR,
		('|'<<8) | '|', OP_OO,
		('&'<<8) | '&', OP_AA,
	};
	register short *p;

	c = (c<<8) | nextchar();
	for (p = duo; *p; p++)
		if (*p++ == c)
			return(*p++);
	peekc = c & 0377;
	return(c>>8);
}

inident(c)
char c;
{
	register char *p;
	register item_t *ip;

	p = readident(c);
	ip = item_search(p);
	if (ip == 0) {
		ip = item_alloc(S_UND);
		ip->i_name = remember(p);
		/* printf("ident %s %o\n", ip->i_name, ip); */
		unresolved++;
		item_insert(ip, H_LOCAL + (hashindex%H_SIZE));
	} else if (hashindex < H_SIZE) {
		assert(H_KEY == 0);
		yylval.y_word = (word_t) ip->i_valu;
		return(ip->i_type);
	}
	yylval.y_item = ip;
	return(IDENT);
}

char *
readident(c)
register c;
{
	static char name[NAMEMAX+1];
	register n = NAMEMAX;
	register char *p = name;

	do {
		if (--n >= 0)
			*p++ = c;
		c = nextchar();
	} while (ISALNUM(c));
	*p++ = '\0';
	peekc = c;
	return(name);
}

innumber(c)
register c;
{
	register char *p;
	register radix;
	static char num[20+1];

	p = num;
	radix = 20;
	do {
		if (--radix < 0)
			fatal("number too long");
		if (isupper(c))
			c += ('a' - 'A');
		*p++ = c;
		c = nextchar();
	} while (ISALNUM(c));
	peekc = c;
	*p = '\0';
	c = *--p;
	p = num;
	radix = 10;
	if (*p == '0') {
		radix = 8;
		p++;
		if (*p == 'x') {
			radix = 16;
			p++;
		} else if (*p == 'b') {
			radix = 2;
			p++;
		}
	}
	if (radix != 16 && (c == 'f' || c == 'b'))
		return(infbsym(num));
	yylval.y_valu = 0;
	while (c = *p++) {
		if (c > '9')
			c -= ('a' - '9' - 1);
		c -= '0';
		if (c >= radix)
			serror("digit exceeds radix");
		yylval.y_valu = yylval.y_valu * radix + c;
	}
	return(NUMBER);
}

instring(termc)
{
	register char *p;
	register c;

	p = stringbuf+1;
	for (;;) {
		c = nextchar();
		if (c == '\n' || c == '\0') {
			peekc = c;
			serror("non-terminated string");
			break;
		}
		if (c == termc)
			break;
		if (c == '\\')
			c = inescape();
		if (p >= &stringbuf[STRINGMAX - 1])
			fatal("string buffer overflow");
		*p++ = c;
	}
	stringbuf[0] = p - stringbuf - 1;
	*p = '\0';
	return(STRING);
}

inescape()
{
	register c, j, r;

	c = nextchar();
	if (c >= '0' && c <= '7') {
		r = c - '0';
		for (j = 0; j < 2; j++) {
			c = nextchar();
			if (c < '0' || c > '7') {
				peekc = c;
				return(r);
			}
			r <<= 3;
			r += (c - '0');
		}
		return(r);
	}
	switch (c) {
	case 'b':	return('\b');
	case 'f':	return('\f');
	case 'n':	return('\n');
	case 'r':	return('\r');
	case 't':	return('\t');
	case '\'':	return('\'');
	case '"':	return('"');
	}
	return(c);
}

infbsym(p)
register char *p;
{
	register lab;
	register item_t *ip;

	lab = *p++ - '0';
	if ((unsigned)lab < 10) {
		if (*p++ == 'f') {
			ip = fb_ptr[FB_FORW+lab];
			if (ip == 0) {
				ip = fb_alloc(lab);
				fb_ptr[FB_FORW+lab] = ip;
			}
			goto ok;
		}
		ip = fb_ptr[FB_BACK+lab];
		if (ip != 0 && *p == 0)
			goto ok;
	}
	serror("bad numeric label");
	ip = fb_alloc(0);
ok:
	yylval.y_item = ip;
	return(FBSYM);
}

hash(p)
register char *p;
{
	register unsigned h;
	register c;

	h = 0;
	while (c = *p++) {
		h <<= 2;
		h += c;
	}
	return(h % H_SIZE);
}

item_t *
item_search(p)
register char *p;
{
	register h;
	register item_t *ip;

	for (h = hash(p); h < H_TOTAL; h += H_SIZE) {
		ip = hashtab[h];
		while (ip != 0) {
			if (strcmp(p, ip->i_name) == 0)
				goto done;
			ip = ip->i_next;
		}
	}
done:
	hashindex = h;
	return(ip);
}

item_insert(ip, h)
item_t *ip;
{
	ip->i_next = hashtab[h];
	hashtab[h] = ip;
}

item_t *
item_alloc(typ)
{
	register item_t *ip;
	static nleft = 0;
	static item_t *next;

	if (--nleft < 0) {
		next = (item_t *) malloc(MEMINCR);
		if (next == 0)
			fatal("out of memory");
		nleft += (MEMINCR / sizeof(item_t));
	}
	ip = next++;
	ip->i_next = 0;
	ip->i_type = typ;
	ip->i_name = 0;
	ip->i_valu = 0;
	return(ip);
}

item_t *
fb_alloc(lab)
register lab;
{
	register item_t *ip, *p;

	ip = item_alloc(S_UND);
	p = fb_ptr[FB_TAIL+lab];
	if (p == 0)
		fb_ptr[FB_HEAD+lab] = ip;
	else
		p->i_next = ip;
	fb_ptr[FB_TAIL+lab] = ip;
	return(ip);
}

item_t *
fb_shift(lab)
register lab;
{
	register item_t *ip;

	ip = fb_ptr[FB_FORW+lab];
	if (ip == 0)
		if (pass == PASS_1)
			ip = fb_alloc(lab);
		else
			ip = fb_ptr[FB_HEAD+lab];
	fb_ptr[FB_BACK+lab] = ip;
	fb_ptr[FB_FORW+lab] = ip->i_next;
	return(ip);
}