// Inferno utils/cc/lexbody
// http://code.google.com/p/inferno-os/source/browse/utils/cc/lexbody
//
//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//	Portions Copyright © 1997-1999 Vita Nuova Limited
//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//	Portions Copyright © 2004,2006 Bruce Ellis
//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

/*
 * common code for all the assemblers
 */

void
pragpack(void)
{
	while(getnsc() != '\n')
		;
}

void
pragvararg(void)
{
	while(getnsc() != '\n')
		;
}

void
pragcgo(char *name)
{
	USED(name);
	while(getnsc() != '\n')
		;
}

void
pragfpround(void)
{
	while(getnsc() != '\n')
		;
}

void
pragtextflag(void)
{
	while(getnsc() != '\n')
		;
}

void
pragdataflag(void)
{
	while(getnsc() != '\n')
		;
}

void
pragprofile(void)
{
	while(getnsc() != '\n')
		;
}

void
pragincomplete(void)
{
	while(getnsc() != '\n')
		;
}

void*
alloc(int32 n)
{
	void *p;

	p = malloc(n);
	if(p == nil) {
		print("alloc out of mem\n");
		exits("alloc: out of mem");
	}
	memset(p, 0, n);
	return p;
}

void*
allocn(void *p, int32 n, int32 d)
{
	if(p == nil)
		return alloc(n+d);
	p = realloc(p, n+d);
	if(p == nil) {
		print("allocn out of mem\n");
		exits("allocn: out of mem");
	}
	if(d > 0)
		memset((char*)p+n, 0, d);
	return p;
}

void
ensuresymb(int32 n)
{
	if(symb == nil) {
		symb = alloc(NSYMB+1);
		nsymb = NSYMB;
	}

	if(n > nsymb) {
		symb = allocn(symb, nsymb, n+1-nsymb);
		nsymb = n;
	}
}

void
setinclude(char *p)
{
	int i;

	if(p == 0)
		return;
	for(i=1; i < ninclude; i++)
		if(strcmp(p, include[i]) == 0)
			return;
	
	if(ninclude%8 == 0)
		include = allocn(include, ninclude*sizeof(char *), 
			8*sizeof(char *));
	include[ninclude++] = p;
}

void
errorexit(void)
{
	Bflush(&bstdout);
	if(outfile)
		remove(outfile);
	exits("error");
}

void
pushio(void)
{
	Io *i;

	i = iostack;
	if(i == I) {
		yyerror("botch in pushio");
		errorexit();
	}
	i->p = fi.p;
	i->c = fi.c;
}

void
newio(void)
{
	Io *i;
	static int pushdepth = 0;

	i = iofree;
	if(i == I) {
		pushdepth++;
		if(pushdepth > 1000) {
			yyerror("macro/io expansion too deep");
			errorexit();
		}
		i = alloc(sizeof(*i));
	} else
		iofree = i->link;
	i->c = 0;
	i->f = -1;
	ionext = i;
}

void
newfile(char *s, int f)
{
	Io *i;

	i = ionext;
	i->link = iostack;
	iostack = i;
	i->f = f;
	if(f < 0)
		i->f = open(s, 0);
	if(i->f < 0) {
		yyerror("%ca: %r: %s", thechar, s);
		errorexit();
	}
	fi.c = 0;
	linklinehist(ctxt, lineno, s, 0);
}

Sym*
slookup(char *s)
{
	ensuresymb(strlen(s));
	strcpy(symb, s);
	return lookup();
}

Sym*
lookup(void)
{
	Sym *s;
	uint32 h;
	char *p;
	int c, l;
	char *r, *w;

	if((uchar)symb[0] == 0xc2 && (uchar)symb[1] == 0xb7) {
		// turn leading · into ""·
		h = strlen(symb);
		ensuresymb(h+2);
		memmove(symb+2, symb, h+1);
		symb[0] = '"';
		symb[1] = '"';
	}

	for(r=w=symb; *r; r++) {
		// turn · (U+00B7) into .
		// turn ∕ (U+2215) into /
		if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) {
			*w++ = '.';
			r++;
		}else if((uchar)*r == 0xe2 && (uchar)*(r+1) == 0x88 && (uchar)*(r+2) == 0x95) {
			*w++ = '/';
			r++;
			r++;
		}else
			*w++ = *r;
	}
	*w = '\0';

	h = 0;
	for(p=symb; c = *p; p++)
		h = h+h+h + c;
	l = (p - symb) + 1;
	h &= 0xffffff;
	h %= NHASH;
	c = symb[0];
	for(s = hash[h]; s != S; s = s->link) {
		if(s->name[0] != c)
			continue;
		if(strcmp(s->name, symb) == 0)
			return s;
	}
	s = alloc(sizeof(*s));
	s->name = alloc(l);
	memmove(s->name, symb, l);

	s->link = hash[h];
	hash[h] = s;
	syminit(s);
	return s;
}

int
ISALPHA(int c)
{
	if(isalpha(c))
		return 1;
	if(c >= Runeself)
		return 1;
	return 0;
}

int32
yylex(void)
{
	int c, c1;
	char *cp;
	Sym *s;

	c = peekc;
	if(c != IGN) {
		peekc = IGN;
		goto l1;
	}
l0:
	c = GETC();

l1:
	if(c == EOF) {
		peekc = EOF;
		return -1;
	}
	if(isspace(c)) {
		if(c == '\n') {
			lineno++;
			return ';';
		}
		goto l0;
	}
	if(ISALPHA(c))
		goto talph;
	if(isdigit(c))
		goto tnum;
	switch(c)
	{
	case '\n':
		lineno++;
		return ';';

	case '#':
		domacro();
		goto l0;

	case '.':
		c = GETC();
		if(ISALPHA(c)) {
			cp = symb;
			*cp++ = '.';
			goto aloop;
		}
		if(isdigit(c)) {
			cp = symb;
			*cp++ = '.';
			goto casedot;
		}
		peekc = c;
		return '.';

	talph:
	case '_':
	case '@':
		cp = symb;

	aloop:
		*cp++ = c;
		c = GETC();
		if(ISALPHA(c) || isdigit(c) || c == '_' || c == '$')
			goto aloop;
		*cp = 0;
		peekc = c;
		s = lookup();
		if(s->macro) {
			newio();
			cp = ionext->b;
			macexpand(s, cp);
			pushio();
			ionext->link = iostack;
			iostack = ionext;
			fi.p = cp;
			fi.c = strlen(cp);
			if(peekc != IGN) {
				cp[fi.c++] = peekc;
				cp[fi.c] = 0;
				peekc = IGN;
			}
			goto l0;
		}
		if(s->type == 0)
			s->type = LNAME;
		if(s->type == LNAME ||
		   s->type == LVAR ||
		   s->type == LLAB) {
			yylval.sym = s;
			return s->type;
		}
		yylval.lval = s->value;
		return s->type;

	tnum:
		cp = symb;
		if(c != '0')
			goto dc;
		*cp++ = c;
		c = GETC();
		c1 = 3;
		if(c == 'x' || c == 'X') {
			c1 = 4;
			c = GETC();
		} else
		if(c < '0' || c > '7')
			goto dc;
		yylval.lval = 0;
		for(;;) {
			if(c >= '0' && c <= '9') {
				if(c > '7' && c1 == 3)
					break;
				yylval.lval = (uvlong)yylval.lval << c1;
				yylval.lval += c - '0';
				c = GETC();
				continue;
			}
			if(c1 == 3)
				break;
			if(c >= 'A' && c <= 'F')
				c += 'a' - 'A';
			if(c >= 'a' && c <= 'f') {
				yylval.lval = (uvlong)yylval.lval << c1;
				yylval.lval += c - 'a' + 10;
				c = GETC();
				continue;
			}
			break;
		}
		goto ncu;

	dc:
		for(;;) {
			if(!isdigit(c))
				break;
			*cp++ = c;
			c = GETC();
		}
		if(c == '.')
			goto casedot;
		if(c == 'e' || c == 'E')
			goto casee;
		*cp = 0;
		if(sizeof(yylval.lval) == sizeof(vlong))
			yylval.lval = strtoll(symb, nil, 10);
		else
			yylval.lval = strtol(symb, nil, 10);

	ncu:
		while(c == 'U' || c == 'u' || c == 'l' || c == 'L')
			c = GETC();
		peekc = c;
		return LCONST;

	casedot:
		for(;;) {
			*cp++ = c;
			c = GETC();
			if(!isdigit(c))
				break;
		}
		if(c == 'e' || c == 'E')
			goto casee;
		goto caseout;

	casee:
		*cp++ = 'e';
		c = GETC();
		if(c == '+' || c == '-') {
			*cp++ = c;
			c = GETC();
		}
		while(isdigit(c)) {
			*cp++ = c;
			c = GETC();
		}

	caseout:
		*cp = 0;
		peekc = c;
		if(FPCHIP) {
			yylval.dval = atof(symb);
			return LFCONST;
		}
		yyerror("assembler cannot interpret fp constants");
		yylval.lval = 1L;
		return LCONST;

	case '"':
		memcpy(yylval.sval, nullgen.u.sval, sizeof(yylval.sval));
		cp = yylval.sval;
		c1 = 0;
		for(;;) {
			c = escchar('"');
			if(c == EOF)
				break;
			if(c1 < sizeof(yylval.sval))
				*cp++ = c;
			c1++;
		}
		if(c1 > sizeof(yylval.sval))
			yyerror("string constant too long");
		return LSCONST;

	case '\'':
		c = escchar('\'');
		if(c == EOF)
			c = '\'';
		if(escchar('\'') != EOF)
			yyerror("missing '");
		yylval.lval = c;
		return LCONST;

	case '/':
		c1 = GETC();
		if(c1 == '/') {
			for(;;) {
				c = GETC();
				if(c == '\n')
					goto l1;
				if(c == EOF) {
					yyerror("eof in comment");
					errorexit();
				}
			}
		}
		if(c1 == '*') {
			for(;;) {
				c = GETC();
				while(c == '*') {
					c = GETC();
					if(c == '/')
						goto l0;
				}
				if(c == EOF) {
					yyerror("eof in comment");
					errorexit();
				}
				if(c == '\n')
					lineno++;
			}
		}
		break;

	default:
		return c;
	}
	peekc = c1;
	return c;
}

int
getc(void)
{
	int c;

	c = peekc;
	if(c != IGN) {
		peekc = IGN;
		return c;
	}
	c = GETC();
	if(c == '\n')
		lineno++;
	if(c == EOF) {
		yyerror("End of file");
		errorexit();
	}
	return c;
}

int
getnsc(void)
{
	int c;

	for(;;) {
		c = getc();
		if(!isspace(c) || c == '\n')
			return c;
	}
}

void
unget(int c)
{

	peekc = c;
	if(c == '\n')
		lineno--;
}

int
escchar(int e)
{
	int c, l;

loop:
	c = getc();
	if(c == '\n') {
		yyerror("newline in string");
		return EOF;
	}
	if(c != '\\') {
		if(c == e)
			return EOF;
		return c;
	}
	c = getc();
	if(c >= '0' && c <= '7') {
		l = c - '0';
		c = getc();
		if(c >= '0' && c <= '7') {
			l = l*8 + c-'0';
			c = getc();
			if(c >= '0' && c <= '7') {
				l = l*8 + c-'0';
				return l;
			}
		}
		peekc = c;
		return l;
	}
	switch(c)
	{
	case '\n':	goto loop;
	case 'n':	return '\n';
	case 't':	return '\t';
	case 'b':	return '\b';
	case 'r':	return '\r';
	case 'f':	return '\f';
	case 'a':	return 0x07;
	case 'v':	return 0x0b;
	case 'z':	return 0x00;
	}
	return c;
}

void
pinit(char *f)
{
	int i;
	Sym *s;

	lineno = 1;
	newio();
	newfile(f, -1);
	pc = 0;
	peekc = IGN;
	sym = 1;
	for(i=0; i<NHASH; i++)
		for(s = hash[i]; s != S; s = s->link)
			s->macro = 0;
}

int
filbuf(void)
{
	Io *i;

loop:
	i = iostack;
	if(i == I)
		return EOF;
	if(i->f < 0)
		goto pop;
	fi.c = read(i->f, i->b, BUFSIZ) - 1;
	if(fi.c < 0) {
		close(i->f);
		linklinehist(ctxt, lineno, 0, 0);
		goto pop;
	}
	fi.p = i->b + 1;
	return i->b[0] & 0xff;

pop:
	iostack = i->link;
	i->link = iofree;
	iofree = i;
	i = iostack;
	if(i == I)
		return EOF;
	fi.p = i->p;
	fi.c = i->c;
	if(--fi.c < 0)
		goto loop;
	return *fi.p++ & 0xff;
}

void
yyerror(char *a, ...)
{
	char buf[200];
	va_list arg;

	/*
	 * hack to intercept message from yaccpar
	 */
	if(strcmp(a, "syntax error") == 0) {
		yyerror("syntax error, last name: %s", symb);
		return;
	}
	prfile(lineno);
	va_start(arg, a);
	vseprint(buf, buf+sizeof(buf), a, arg);
	va_end(arg);
	print("%s\n", buf);
	nerrors++;
	if(nerrors > 10) {
		print("too many errors\n");
		errorexit();
	}
}

void
prfile(int32 l)
{
	linkprfile(ctxt, l);
}
