/*	ldcc.c

	loQ Don C Compiler.

	This is a small C subset compiler created for the Fall
	EE480 loQ Don (Klingon for "slightly parallel") assembly
	language and processor implementations.  It's a mixed
	16-bit instruction, 32-bit data, word-oriented machine.
	The parallelism it supports is partly 8-bit SWAR fields
	within a 32-bit data word, but it is also intended that
	a 32-bit pair of 16-bit instructions could be fetched in
	a single clock and hence easily sustain 2-way
	superscalar execution.  See
	http://aggregate.org/EE480/loQDon.html for details.

	2016 by Hank Dietz, http://aggregate.org/hankd

	Initial version: 20161005
*/

#define	VERSION	20161005

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

//	#define	USE_LI32	1

//	#define	LINETRACE	1
#define	HOISTADDR	1

int	haveinput = 0;

char	*prelab = "_";
int	callerreg, calleereg;
int	scope = 0;
int	labnum = 0;	/* next compiler-generated label */
int	lexsym;

int	beginlab, endlab;	/* for function begin/end code */
int	gpoffset, fpoffset;	/* for globals/locals */
int	isleaf;			/* is this a leaf procedure? */
int	linestart = 0;		/* start of current line */

#define	DATABASE	0x0000	/* where data starts */

#define	INT	'a'
#define	IF	'b'
#define	ELSE	'c'
#define	WHILE	'd'
#define	RETURN	'e'
#define	FUNC	'f'
#define	WORD	'g'
#define	NUM	'h'
#define	DO	'i'
#define	VAR	'j'
#define	AFP	'k'
#define	ASP	'l'
#define	SHORT	'n'
#define	CHAR	'o'
#define RETVAL	'p'
#define	MULBY4	'q'
#define	MULBY2	'r'
#define	STRING	's'
#define	FOR	't'
#define	GOTO	'u'
#define	TARGET	'v'

#define	EQ	'A'
#define	NE	'B'
#define	GE	'C'
#define	LE	'D'
#define	SL	'E'
#define	PP	'F'
#define	MM	'G'
#define	OE	'H'
#define	XE	'I'
#define	AE	'J'
#define	PE	'K'
#define	ME	'L'
#define	TE	'M'
#define	DE	'N'
#define	RE	'O'
#define	OO	'P'
#define	AA	'Q'
#define	NEG	'R'
#define	SR	'S'
#define	SUBR	'T'	/* Subtract reversed */
#define	MYEOF	'Z'

#define	MAXINPUT	(1024*1024)
char	input[MAXINPUT];
int	eof;
int	ipos;

char	*myname;	/* name of this command */

int	nextt;		/* next token */
int	lexnum;		/* lexical number value */
int	lexstr;		/* lexical string ipos */
int	lineno = 1;	/* current line number */

#define	STACKSIZE	64
int	objsize[STACKSIZE];
int	sp = 0;

int	highwater = 0;

#define	sym	struct _sym
sym {
	int	ipos;
	int	type;
	int	scope;
	int	base;
	int	size;
	int	dim;
} symtab[513];		/* symbol table */
int	symsp = 0;

void	expr(void);
void	decl(void);


int
isnamechar(register int t)
{
	return(((t >= '0') && (t <= '9')) ||
	       ((t >= 'A') && (t <= 'Z')) ||
	       ((t >= 'a') && (t <= 'z')) ||
	       (t == '_'));
}

char *
namestring(register int ipos)
{
	static char name[256];
	register int i = 0;

	while (isnamechar(name[i] = input[ipos+i])) ++i;
	name[i] = 0;
	return(&(name[0]));
}

void
warn(fmt, a, b, c)
char *fmt;
int a, b, c;
{
	fprintf(stderr, "#line %d: ", lineno);
	fprintf(stderr, fmt, a, b, c);
	fprintf(stderr, "\n");
}

void
error(fmt, a, b, c)
char *fmt;
int a, b, c;
{
	warn(fmt, a, b, c);
	fprintf(stderr,
"#compilation terminated on this error\n"
		);
	exit(1);
}


/*	Code generation stuff
*/

void
incsp(void)
{
	++sp;
}

void
decsp(void)
{
	--sp;
}

void
prchar(register int c)
{
	putchar(c);
}

void
pr(register char *s,
register int len)
{
	while (--len >= 0) {
		putchar(*s);
		++s;
	}
}

typedef enum {
	ZERO,	PC,	SP,	FP,	RA,	RV,	U0,	U1,
	U2,	U3,	U4,	U5,	U6,	U7,	U8,	U9
} reg_t;

#define	ARG(N)	(U9-(N))

#define	TOS	(U0 + (sp-1))
#define	NOS	(U0 + (sp-2))
#define	TMP	(U0 + sp)
#define	TMP2	(U0 + (sp+1))

char *regname[16] = {
	"zero",	"pc",	"sp",	"fp",	"ra",	"rv",	"u0",	"u1",
	"u2",	"u3",	"u4",	"u5",	"u6",	"u7",	"u8",	"u9"
};


static inline void
loQDon_nop(void)
{
	printf("\tnop\n");
}

#define	MK3REGOP(OP) \
static inline void \
loQDon_##OP(reg_t d, reg_t s, reg_t t) \
{ \
	printf("\t" #OP "\t$%s,$%s,$%s\n", regname[d&0xf], regname[s&0xf], regname[t&0xf]); \
}

MK3REGOP(and)
MK3REGOP(or)
MK3REGOP(xor)
MK3REGOP(add)
MK3REGOP(addv)
MK3REGOP(shift)

static inline void
loQDon_pack(reg_t d, reg_t s, int p)
{
	printf("\tpack\t$%s[0x%x],$%s\n", regname[d&0xf], p&0xf, regname[s&0xf]);
}

static inline void
loQDon_unpack(reg_t d, reg_t s, int p)
{
	printf("\tunpack\t$%s,$%s[0x%x]\n", regname[d&0xf], regname[s&0xf], p&0xf);
}

static inline void
loQDon_li(reg_t d, int i)
{
	printf("\tli\t$%s,%d\n", regname[d&0xf], i);
}

static inline void
loQDon_morei(reg_t d, int i)
{
	printf("\tmorei\t$%s,0x%x\n", regname[d&0xf], i&0xff);
}

#define	MK2REGOP(OP) \
static inline void \
loQDon_##OP(reg_t d, reg_t s) \
{ \
	printf("\t" #OP "\t$%s,$%s\n", regname[d&0xf], regname[s&0xf]); \
}

MK2REGOP(ld)
MK2REGOP(any)
MK2REGOP(anyv)
MK2REGOP(neg)
MK2REGOP(negv)
MK2REGOP(st)
MK2REGOP(jz)
MK2REGOP(jnz)

static inline void
loQDon_sys(void)
{
	printf("\tsys\n");
}

/*	Some helper pseudo-instructions...
*/

static inline void
loQDon_li32(reg_t d, int i)
{
	/* Do what it takes to load 32-bit constant */

#ifdef	USE_LI32
	printf("\tli32\t$%s,%d\n", regname[d&0xf], i);
#else
	register int j;

	if (((j = (i & 0xffffff80)) == 0) || (j == 0xffffff80)) {
		loQDon_li(d, i);
	} else if (((j = (i & 0xffff8000)) == 0) || (j == 0xffff8000)) {
		loQDon_li(d, i>>8);
		loQDon_morei(d, i);
	} else if (((j = (i & 0xff800000)) == 0) || (j == 0xff800000)) {
		loQDon_li(d, i>>16);
		loQDon_morei(d, i>>8);
		loQDon_morei(d, i);
	} else {
		loQDon_li(d, i>>24);
		loQDon_morei(d, i>>16);
		loQDon_morei(d, i>>8);
		loQDon_morei(d, i);
	}
#endif
}

static inline void
loQDon_la(reg_t d, char *s)
{
#ifdef	USE_LI32
	printf("\tli32\t$%s,%s\n", regname[d&0xf], s);
#else
	printf("\tli\t$%s,%s>>8\n", regname[d&0xf], s);
	printf("\tmorei\t$%s,%s\n", regname[d&0xf], s);
#endif
}

static inline void
loQDon_dup(reg_t d, reg_t s)
{
	/* d=s done by d=s&s */
	loQDon_and(d, s, s);
}

static inline void
loQDon_la_(reg_t d, int i)
{
	char buf[256];
	sprintf(buf, "%s%u", prelab, i);
	loQDon_la(d, buf);
}

void
loQDon_space(register int n)
{
	printf("\t.space\t%d\n", n);
}

void
loQDon_text(void)
{
	printf("\t.text\n");
}

void
loQDon_data(int n)
{
	printf("\t.data\n\t.origin\t%d\n", n);
}

void
loQDon_label(register char *s)
{
	printf("%s:\n", s);
}

void
label(register int n)
{
	printf("%s%u:\n", prelab, n);
}

void
loQDon_prelabel(register char *s)
{
	printf("%s%s:\n", prelab, s);
}

void
pushnum(register int n)
{
	incsp();
	loQDon_li32(TOS, n);
	objsize[sp-1] = 0;
}

void
pushgpoff(register int off)
{
	incsp();
	loQDon_li32(TOS, off);
	objsize[sp-1] = 0;
}

void
pushfpoff(register int off)
{
	incsp();
	loQDon_li32(TOS, off);
	loQDon_add(TOS, TOS, FP);
	objsize[sp-1] = 0;
}

void
pushdup(void)
{
	incsp();
	loQDon_dup(TOS, NOS);
	objsize[sp-1] = objsize[sp-2];
}

void
pusharg(register int argno)
{
	incsp();
	loQDon_dup(TOS, ARG(argno));
	objsize[sp-1] = 0;
}

void
lval(register int size)
{
	objsize[sp-1] = size;
}

void
loadtos(void)
{
	/* Need TOS loaded from memory? */

	switch (objsize[sp-1]) {
	case 0:	return;
	case 1: loQDon_ld(TOS, TOS); break;
	default:
		error("cannot load %d-byte object",
		      objsize[sp-1]);
	}

	objsize[sp-1] = 0;
}

void
loadnostos(void)
{
	/* Need either NOS or TOS loaded from memory? */

	decsp();
	loadtos();
	incsp();
	loadtos();
}

void
loadnos(void)
{
	/* Need NOS loaded from memory? */

	decsp();
	loadtos();
	incsp();
}

void
setarg(register int argno)
{
	loadtos();
	loQDon_dup(ARG(argno), TOS);
	decsp();
}

void
pushop(register int op)
{
	int lab;

	switch (op) {
	case NEG:
		loadtos();
		loQDon_neg(TOS, TOS);
		break;
	case '!':
		/* (1 ^ any(TOS)) */
		loadtos();
		loQDon_any(TOS, TOS);
		incsp();
		loQDon_li(TOS, 1);
		loQDon_xor(NOS, NOS, TOS);
		decsp();
		break;
	case '~':
		loadtos();
		incsp();
		loQDon_li(TOS, -1);
		loQDon_xor(NOS, NOS, TOS);
		decsp();
		break;
	case AFP:
		loadtos();
		loQDon_add(TOS, TOS, FP);
		break;
	case ASP:
		loadtos();
		loQDon_add(TOS, TOS, SP);
		break;
	case RETVAL:
		loadtos();
		loQDon_dup(RV, TOS);
		decsp();
		break;
	case '+':
		loadnostos();
		loQDon_add(NOS, NOS, TOS);
		decsp();
		break;
	case '-':
		/* NOS += -TOS */
		loadnostos();
		loQDon_neg(TOS, TOS);
		loQDon_add(NOS, NOS, TOS);
		decsp();
		break;
	case SUBR:
		/* NOS = (-NOS) + TOS */
		loadnostos();
		loQDon_neg(NOS, NOS);
		loQDon_add(NOS, NOS, TOS);
		decsp();
		break;
	case SL:
		loadnostos();
		loQDon_shift(NOS, NOS, TOS);
		decsp();
		break;
	case SR:
		/* Really SL by negative */
		loadnostos();
		loQDon_neg(TOS, TOS);
		loQDon_shift(NOS, NOS, TOS);
		decsp();
		break;
	case LE:
		/* Really ! GT */
		pushop('>');
		incsp();
		loQDon_li(TOS, 1);
		loQDon_xor(NOS, NOS, TOS);
		decsp();
		break;
	case '<':
		/* any((TOS-NOS) >> 31) */
		pushop('-');
		incsp();
		loQDon_li(TOS, -31);
		loQDon_shift(NOS, NOS, TOS);
		decsp();
		loQDon_any(TOS, TOS);
		break;
	case GE:
		/* Really ! LT */
		pushop('<');
		incsp();
		loQDon_li(TOS, 1);
		loQDon_xor(NOS, NOS, TOS);
		decsp();
		break;
	case '>':
		/* any((NOS-TOS) >> 31) */
		pushop(SUBR);
		incsp();
		loQDon_li(TOS, -31);
		loQDon_shift(NOS, NOS, TOS);
		decsp();
		loQDon_any(TOS, TOS);
		break;
	case EQ:
		/* ! NE */
		pushop(NE);
		incsp();
		loQDon_li(TOS, 1);
		loQDon_xor(NOS, NOS, TOS);
		decsp();
		break;
	case NE:
		loadnostos();
		loQDon_xor(NOS, NOS, TOS);
		loQDon_any(NOS, NOS);
		decsp();
		break;
	case '&':
		loadnostos();
		loQDon_and(NOS, NOS, TOS);
		decsp();
		break;
	case '^':
		loadnostos();
		loQDon_xor(NOS, NOS, TOS);
		decsp();
		break;
	case '|':
		loadnostos();
		loQDon_or(NOS, NOS, TOS);
		decsp();
		break;
	default:
		error("cannot yet handle op (%c)", op);
	}
}

void
store(register int prop)
{
	/* Store TOS into NOS */

	loadtos();
	switch (objsize[sp-2]) {
	case 0: error("lvalue required");
	case 1: loQDon_st(TOS, NOS); break;
	default:
		error("cannot store %d-byte object",
		      objsize[sp-2]);
	}

	if (prop) {
		/* Just in case we have x=(y=z)...
		   Strictly speaking, x=(y=z) sets x=((typeof(y))z),
		   but I don't think we have to be that precise here
		*/
		loQDon_dup(NOS, TOS);
		decsp();
		objsize[sp-1] = 0;
	} else {
		decsp();
		decsp();
	}
}

void
jumpreg(register int r)
{
	loQDon_jz(ZERO, r);
}

void
jump(register int a)
{
	/* Compiler-generated labels are nearby...
	   so we could use a branch (if we had one)
	*/

	loQDon_la_(TMP, a);
	jumpreg(TMP);
}

void
jumpfreg(register int r)
{
	loadtos();
	loQDon_jz(TOS, r);
	decsp();
}

void
jumpf(register int a)
{
	loQDon_la_(TMP, a);
	jumpfreg(TMP);
}

void
jumptreg(register int r)
{
	loadtos();
	loQDon_jnz(TOS, r);
	decsp();
}

void
jumpt(register int a)
{
	loQDon_la_(TMP, a);
	jumptreg(TMP);
}


void
ghoto(register char *s)
{
	loQDon_la(TMP, s);
	loQDon_jz(ZERO, TMP);
}

void
target(register char *s)
{
	loQDon_prelabel(s);
}

void
startup(void)
{
	loQDon_text();
	loQDon_la(TMP, "main");
	loQDon_jz(ZERO, TMP);
	loQDon_label("_exit");
	loQDon_sys();
}

void
call(register int mysym)
{
	register char *n = namestring(symtab[mysym].ipos);
	register int i;

	/* We're not a leaf procedure.... */
	isleaf = 0;

	/* Save registers */
	if (sp > 0) {
		loQDon_li(TMP, -1);
		for (i=U0; i<=TOS; ++i) {
			loQDon_st(i, SP);
			loQDon_add(SP, SP, TMP);
		}
	}

	/* Call the function */
	loQDon_la(TMP, n);
	loQDon_jz(ZERO, TMP);

	/* Restore registers */
	if (sp > 0) {
		loQDon_li(TMP, 1);
		for (i=TOS; i>=U0; --i) {
			loQDon_add(SP, SP, TMP);
			loQDon_st(i, SP);
		}
	}

	/* Copy the return value to someplace useful */
	incsp();
	loQDon_dup(TOS, RV);
	objsize[sp-1] = 0;
}


void
funcbegin(register int mysym)
{
	register char *n = namestring(symtab[mysym].ipos);

	fpoffset = -4;
	highwater = 0;
	beginlab = labnum++;
	endlab = labnum++;

	/* For now, functions always return an int */
	symtab[mysym].type = FUNC;
	symtab[mysym].base = -4;
	symtab[mysym].size = 4;
	symtab[mysym].dim = 1;

	++scope;

	/* So far, we could be a leaf procedure.... */
	isleaf = 1;

	loQDon_text();
	label(beginlab);
}


void
funcend(register int mysym)
{
	register char *n = namestring(symtab[mysym].ipos);

	/* For now, functions always return an int */
	symtab[mysym].type = FUNC;
	symtab[mysym].base = -1;
	symtab[mysym].size = 1;
	symtab[mysym].dim = 1;

	/* Now the common end return code...
	   but special-case leaf procedures
	*/
	if ((isleaf == 0) || (highwater != 0)) {
		/* Not a leaf; do full stack frame...
		   Stack looks like:
		   [old fp] [ret addr] [locals]
		   we do this here because that's when we know
		   how much space we need for locals...
		*/

		/* Add space for fp and return address */
		highwater += 2;	

		label(endlab);
		loQDon_dup(RA, FP);		/* ra = mem[fp-1] */
		loQDon_li(TMP, -1);
		loQDon_add(RA, RA, TMP);
		loQDon_ld(RA, RA);
		loQDon_dup(SP, FP);		/* sp = fp */
		loQDon_ld(FP, SP);		/* fp = mem[sp] (old fp) */
		loQDon_li(TMP, 1);
		loQDon_add(SP, SP, TMP);	/* sp = old sp */
		loQDon_jz(ZERO, RA);		/* return */

		loQDon_prelabel(n);

		loQDon_dup(TMP, SP);		/* save sp value */
		loQDon_li32(TMP2, -highwater);	/* sp -= highwater */
		loQDon_add(SP, SP, TMP2);
		loQDon_li(TMP2, -1);
		loQDon_add(TMP, TMP, TMP2);	/* mem[sp+highwater-1] = fp */
		loQDon_st(FP, TMP);
		loQDon_add(TMP, TMP, TMP2);	/* mem[fp-1] = ra */
		loQDon_st(RA, TMP);
	} else {
		/* A leaf without locals... */

		label(endlab);
		loQDon_jz(ZERO, RA);		/* return */

		loQDon_prelabel(n);
	}

	jump(beginlab);

	--scope;
}

void
def(register int mysym)
{
	register char *n = namestring(symtab[mysym].ipos);
	register int asize;

	asize = (symtab[mysym].size * symtab[mysym].dim);

	symtab[mysym].type = VAR;
	if ((symtab[mysym].scope = scope) == 0) {
		/* Offset from $gp */
		symtab[mysym].base = gpoffset;
		gpoffset += asize;

		loQDon_data(DATABASE + symtab[mysym].base);
		loQDon_label(n);
		loQDon_space(asize);
	} else {
		/* Offset from $fp */
		fpoffset -= asize;
		symtab[mysym].base = fpoffset;

		if (highwater < -fpoffset) {
			highwater = -fpoffset;
		}
	}
}


int
defstr(register int spos)
{
	register int num;
	register int asize;

	loQDon_data(DATABASE + gpoffset);

	asize = 0;
	while (input[++spos] != '"') {
		if (++asize > 1) {
			if ((asize & 7) == 1) {
				printf("\n\t.word\t");
			} else {
				printf(", ");
			}
		} else {
			printf("\t.word\t");
		}
		if (input[spos] == '\\') {
			switch (input[++spos]) {
			case 't':	printf("%3d", '\t'); break;
			case 'n':	printf("%3d", '\n'); break;
			case 'r':	printf("%3d", '\r'); break;
			case 'b':	printf("%3d", '\b'); break;
			case '0':	case '1':	case '2':
			case '3':	case '4':	case '5':
			case '6':	case '7':
				num = (input[spos] - '0');
				while ((input[spos+1] >= '0') &&
				       (input[spos+1] <= '7')) {
					num *= 8;
					num += (input[++spos] - '0');
				}
				printf("%3d", num);
				break;
			default:
				printf("%3d", input[spos]);
			}
		} else {
			printf("%3d", input[spos]);
		}
	}

	++asize;
	printf("\n"
	       "\t.word\t0\n"
	       "\n"
	       "\t.text\n");

	gpoffset += asize;
	return(DATABASE + gpoffset - asize);
}



/*	Lexicals...
*/

int
prefixis(register char *p)
{
	register int i = 0;

	for (;;) {
		register int t = p[i];

		if (t == 0) {
			ipos += i;
			return(1);
		}
		if (t != input[ipos + i]) {
			return(0);
		}
		++i;
	}
}

int
nameis(register char *p)
{
	register int i = 0;

	for (;;) {
		register int t = p[i];

		if (!isnamechar(t)) {
			if (!isnamechar(input[ipos + i])) {
				ipos += i;
				return(1);
			} else {
				return(0);
			}
		}
		if (t != input[ipos + i]) {
			return(0);
		}
		++i;
	}
}

int
lexhelp(void)
{
	register int base = 10;

again:

	/* Recognize all the non-name stuff */
	switch (input[ipos]) {

	/* Handle whitespace, etc. */
	case '\n':
#ifdef	LINETRACE
		printf("#line\t%d:\t", lineno);
		while (linestart <= ipos) {
			putchar(input[linestart]);
			++linestart;
		}
#endif
		++lineno;
		/* Fall through... */
	case ' ':	case '\t':	case '\r':
		++ipos;
		goto again;
	case '\000':
		return(MYEOF);

	/* Handling of punctuation... */
	case '=':	case '!':
	case '<':	case '>':
	case '+':	case '-':	case '~':
	case '*':	case '/':	case '%':
	case '|':	case '&':
		if (prefixis("==")) return(EQ);
		if (prefixis("!=")) return(NE);
		if (prefixis(">=")) return(GE);
		if (prefixis("<=")) return(LE);
		if (prefixis("<<")) return(SL);
		if (prefixis("++")) return(PP);
		if (prefixis("--")) return(MM);
		if (prefixis("|=")) return(OE);
		if (prefixis("^=")) return(XE);
		if (prefixis("&=")) return(AE);
		if (prefixis("+=")) return(PE);
		if (prefixis("-=")) return(ME);
		if (prefixis("*=")) return(TE);
		if (prefixis("/=")) return(DE);
		if (prefixis("%=")) return(RE);
		if (prefixis("||")) return(OO);
		if (prefixis("&&")) return(AA);
		/* Fall through... */
	case ',':	case '?':	case ':':
	case '{':	case '}':	case '^':
	case '[':	case ']':
	case '(':	case ')':
	case ';':
		return(input[ipos++]);

	/* Handling of numbers... */
	case '0':
		base = 8;
		switch (input[++ipos]) {
		case 'b': base = 2; ++ipos; break;
		case 'x': base = 16; ++ipos; break;
		}
	case '1':	case '2':	case '3':
	case '4':	case '5':	case '6':
	case '7':	case '8':	case '9':
		lexnum = 0;
		for (;;) {
			register int t = input[ipos];

			if ((t >= '0') && (t <= '9')) {
				t -= '0';
			} else if (((t |= ('a'-'A')) >= 'a') &&
				   (t <= 'f')) {
				t -= ('a' - 10);
			} else {
				return(NUM);
			}

			if (t >= base) {
				error("invalid digit");
			}

			lexnum = (lexnum * base) + t;
			++ipos;
		}
	case '\'':
		++ipos;
		lexnum = input[ipos++];
		if (input[ipos++] != '\'') {
			error("ill-formed character constant");
		}
		return(NUM);
	case '"':
		lexstr = ipos;
		do {
			++ipos;
			if (input[ipos] == 0) {
				error("string ends in end of input");
			}
		} while ((input[ipos] != '"') ||
			 (input[ipos-1] == '\\'));
		++ipos;
		return(STRING);
	default:
		if (!isnamechar(input[ipos])) {
			error("illegal character 0x%02x (%c)",
			      input[ipos],
			      input[ipos]);
			++ipos;
			goto again;
		}
	}

	/* Must be a name... */
	if (nameis("int")) return(INT);
	if (nameis("short")) return(SHORT);
	if (nameis("char")) return(CHAR);
	if (nameis("if")) return(IF);
	if (nameis("else")) return(ELSE);
	if (nameis("while")) return(WHILE);
	if (nameis("do")) return(DO);
	if (nameis("return")) return(RETURN);
	if (nameis("for")) return(FOR);
	if (nameis("goto")) return(GOTO);

	/* Find it in the symbol table */
	for (lexsym=(symsp-1); lexsym>=0; --lexsym) {
		if (nameis(&(input[symtab[lexsym].ipos]))) {
			return(symtab[lexsym].type);
		}
	}

	/* Make a new symbol table entry */
	symtab[lexsym = (symsp++)].ipos = ipos;
	nameis(&(input[ipos]));
	return(symtab[lexsym].type = ((input[ipos] == ':') ?
				      TARGET :
				      WORD));
}

int
lex(void)
{
	nextt = lexhelp();
	return(nextt);
}

int
match(register int t)
{
	if (nextt == t) {
		lex();
		return(1);
	}
	return(0);
}

int
assume(register int t)
{
	if (!match(t)) {
		warn("missing %c assumed", t);
		return(0);
	}
	return(1);
}



/*	Parsing...
*/

void
memaddr(register int mysym)
{
	/* Base address */
	if (symtab[mysym].scope != 0) {
		pushfpoff(symtab[mysym].base);
	} else {
		pushgpoff(symtab[mysym].base);
	}

	lex();
	if (match('[')) {
		/* subscripted */
		expr();		/* Index value */
		assume(']');

		/* Multiply by element size */
		switch (symtab[mysym].size) {
		case 2:	pushop(MULBY2); break;
		case 1: break;
		default:
			pushop(MULBY4);
		}

		pushop('+');	/* Add to base address */
	}

	lval(symtab[mysym].size);
}

void
unary(void)
{
	register int mysym;
	register int args = 0;

	switch (nextt) {
	case PP:
		lex();
		unary();
		pushdup();
		pushnum(1);
		pushop('+');
		store(1);
		break;
	case MM:
		lex();
		unary();
		pushdup();
		pushnum(-1);
		pushop('+');
		store(1);
		break;
	case '(':
		lex();
		expr();
		assume(')');
		break;
	case '-':
		lex();
		unary();
		pushop(NEG);
		break;
	case '!':
		lex();
		unary();
		pushop('!');
		break;
	case '~':
		lex();
		unary();
		pushop('~');
		break;
	case VAR:
		memaddr(lexsym);
		break;
	case WORD:
		symtab[lexsym].type = FUNC;
		symtab[lexsym].base = 0;
		/* Fall through... */
	case FUNC:
		/* Function call */
		mysym = lexsym;
		lex();
		if (!match('(')) {
			error("undefined variable %s",
			      namestring(symtab[mysym].ipos));
		}
		args = 0;
		while (!match(')')) {
			expr();
			setarg(args++);
			match(',');
		}
		call(mysym);
		break;
	case NUM:
		pushnum(lexnum);
		lex();
		break;
	case STRING:
		pushnum(defstr(lexstr));
		lex();
		break;
	default:
		error("malformed expression");
	}

	/* Suffix operation */
	switch (nextt) {
	case PP:
		lex();
		pushdup();
		loadnos();
		pushdup();
		pushnum(1);
		pushop('+');
		store(0);
		break;
	case MM:
		lex();
		pushdup();
		loadnos();
		pushdup();
		pushnum(1);
		pushop('+');
		store(0);
		break;
	}
}

void
mul(void)
{
	register int t;

	unary();
	for (;;) {
		switch (nextt) {
		case '*':
		case '/':
		case '%':
			t = nextt;
			lex();
			unary();
			pushop(t);
		default:
			return;
		}
	}
}

void
add(void)
{
	register int t;

	mul();
	for (;;) {
		switch (nextt) {
		case '+':
		case '-':
			t = nextt;
			lex();
			mul();
			pushop(t);
		default:
			return;
		}
	}
}

void
slsr(void)
{
	register int t;

	add();
	for (;;) {
		switch (nextt) {
		case SL:
		case SR:
			t = nextt;
			lex();
			add();
			pushop(t);
		default:
			return;
		}
	}
}

void
leltgegt(void)
{
	register int t;

	slsr();
	for (;;) {
		switch (nextt) {
		case LE:
		case '<':
		case GE:
		case '>':
			t = nextt;
			lex();
			slsr();
			pushop(t);
		default:
			return;
		}
	}
}

void
eqne(void)
{
	register int t;

	leltgegt();
	for (;;) {
		switch (nextt) {
		case EQ:
		case NE:
			t = nextt;
			lex();
			leltgegt();
			pushop(t);
		default:
			return;
		}
	}
}

void
and(void)
{
	eqne();
	while (match('&')) {
		eqne();
		pushop('&');
	}
}

void
xor(void)
{
	and();
	while (match('^')) {
		and();
		pushop('^');
	}
}

void
or(void)
{
	xor();
	while (match('|')) {
		xor();
		pushop('|');
	}
}

void
andand(void)
{
	register int lab;

	or();
	if (match(AA)) {
		lab = labnum;
		labnum += 3;

		do {
			jumpf(lab);
			++labnum;
			or();
		} while (match(AA));

		jumpt(lab+1);
		label(lab);
		pushnum(0);
		decsp();
		jump(lab+2);
		label(lab+1);
		pushnum(1);
		label(lab+2);
	}
}

void
oror(void)
{
	register int lab;

	andand();
	if (match(OO)) {
		lab = labnum;
		labnum += 3;

		do {
			jumpt(lab);
			++labnum;
			andand();
		} while (match(OO));

		jumpf(lab+1);
		label(lab);
		pushnum(1);
		decsp();
		jump(lab+2);
		label(lab+1);
		pushnum(0);
		label(lab+2);
	}
}

void
cond(void)
{
	register int lab;

	oror();
	if (match('?')) {
		lab = labnum;
		labnum += 2;

		jumpf(lab);
		expr();
		decsp();
		jump(lab+1);
		assume(':');
		label(lab);
		cond();
		label(lab+1);
	}
}

void
assign(void)
{
	register int t;

	cond();
	switch (nextt) {
	case '=':
		lex();
		assign();
		store(1);
		break;
	case OE:
		lex();
		pushdup();
		assign();
		pushop('|');
		store(1);
		break;
	case XE:
		lex();
		pushdup();
		assign();
		pushop('^');
		store(1);
		break;
	case AE:
		lex();
		pushdup();
		assign();
		pushop('&');
		store(1);
		break;
	case PE:
		lex();
		pushdup();
		assign();
		pushop('+');
		store(1);
		break;
	case ME:
		lex();
		pushdup();
		assign();
		pushop('-');
		store(1);
		break;
	case TE:
		lex();
		pushdup();
		assign();
		pushop('*');
		store(1);
		break;
	case DE:
		lex();
		pushdup();
		assign();
		pushop('/');
		store(1);
		break;
	case RE:
		lex();
		pushdup();
		assign();
		pushop('%');
		store(1);
		break;
	}
}

void
expr(void)
{
	assign();
	while (match(',')) {
		decsp();
		assign();
	}
}

int
newsym(void)
{
	/* Create a new symbol table entry */
	register int mysym;

	switch (nextt) {
	case WORD:
		mysym = lexsym;
		break;
	case VAR:
	case FUNC:
		if (scope == symtab[lexsym].scope) {
			warn("redefinition of identifier");
		}
		symtab[mysym = (symsp++)].ipos = symtab[lexsym].ipos;
		break;
	default:
		error("ill-formed declaration of %s",
		      namestring(symtab[mysym].ipos));
	}
	lex();
	symtab[mysym].scope = scope;
	return(mysym);
}

void
stat(void)
{
	register int scopesymsp, scopeoffset;
	register int lab;
	register int mysym;
	register int labreg, labreg1, labreg2, labreg3;

	switch (nextt) {
	case '{':
		lex();
		scopesymsp = symsp;
		scopeoffset = fpoffset;

		decl();
		while (!match('}')) {
			stat();
		}

		symsp = scopesymsp;
		fpoffset = scopeoffset;
		break;
	case IF:
		lex();
		expr();
		lab = labnum;
		labnum += 2;
		jumpf(lab);
		stat();
		if (nextt == ELSE) {
			lex();
			jump(lab+1);
			label(lab);
			stat();
			label(lab+1);
		} else {
			label(lab);
		}
		break;
	case FOR:
		printf("; for loop\n");
		lex();
		assume('(');
		lab = labnum;
		labnum += 4;
		if (!match(';')) {
			expr();
			decsp();
			match(';');
		}
#ifdef	HOISTADDR
		incsp();
		loQDon_la_((labreg = TOS), lab);
		incsp();
		loQDon_la_((labreg1 = TOS), lab+1);
		incsp();
		loQDon_la_((labreg2 = TOS), lab+2);
		incsp();
		loQDon_la_((labreg3 = TOS), lab+3);

		printf("; for condition\n");
		label(lab);
		if (!match(';')) {
			expr();
			jumpfreg(labreg1);
			match(';');
		}
		jumpreg(labreg2);
		printf("; for increment\n");
		label(lab+3);
		if (!match(')')) {
			expr();
			decsp();
			match(')');
		}
		jumpreg(labreg);
		printf("; for body\n");
		label(lab+2);
		stat();
		jumpreg(labreg3);
		label(lab+1);

		decsp();
		decsp();
		decsp();
		decsp();
#else
		label(lab);
		if (!match(';')) {
			expr();
			jumpf(lab+1);
			match(';');
		}
		jump(lab+2);
		label(lab+3);
		if (!match(')')) {
			expr();
			decsp();
			match(')');
		}
		jump(lab);
		label(lab+2);
		stat();
		jump(lab+3);
		label(lab+1);

#endif
		break;
	case WHILE:
		lex();
		lab = labnum;
		labnum += 2;
#ifdef	HOISTADDR
		incsp();
		loQDon_la_((labreg = TOS), lab);
		incsp();
		loQDon_la_((labreg1 = TOS), lab+1);
		label(lab);
		expr();
		jumpfreg(labreg1);
		stat();
		jumpreg(labreg);
		decsp();
		decsp();
#else
		label(lab);
		expr();
		jumpf(lab+1);
		stat();
		jump(lab);
#endif
		label(lab+1);
		break;
	case DO:
		lex();
		lab = (labnum++);
#ifdef	HOISTADDR
		incsp();
		loQDon_la_((labreg = TOS), lab);
		label(lab);
		stat();
		if (match(WHILE)) {
			error("do missing while");
		}
		expr();
		assume(';');
		jumptreg(labreg);
		decsp();
#else
		label(lab);
		stat();
		if (match(WHILE)) {
			error("do missing while");
		}
		expr();
		assume(';');
		jumpt(lab);
#endif
		break;
	case RETURN:
		lex();
		if (nextt != ';') {
			expr();
			pushop(RETVAL);
		}
		match(';');
		break;
	case GOTO:
		lex();
		ghoto(namestring(symtab[newsym()].ipos));
		--symsp;
		break;
	case TARGET:
		target(namestring(symtab[symsp-1].ipos));
		--symsp;
		lex();
		assume(':');
		break;
	case ';':
		lex();
		break;
	default:
		expr();
		assume(';');
		decsp();
		break;
	}
}

int
ctype(void)
{
	switch (nextt) {
	case INT:	lex(); return(1);	/* All things are 16 bit units */
	case SHORT:	lex(); return(1);
	case CHAR:	lex(); return(1);
	case WORD:	if (scope == 0) {
				warn("missing int keyword assumed");
				return(1);
			}
	}
	return(0);
}

void
decl(void)
{
	register int scopeoffset;
	register int mysym, argsym;
	register int size, args;

	while ((size = ctype()) != 0) {
moredecls:
		mysym = newsym();
		symtab[mysym].size = size;

		switch (nextt) {
		case '[':
			lex();
			symtab[mysym].type = VAR;
			if (nextt != NUM) {
				error("non-constant dim for %s",
				      namestring(symtab[mysym].ipos));
			}
			symtab[mysym].dim = lexnum;
			def(mysym);
			lex();
			assume(']');
			if (match(',')) goto moredecls;
			assume(';');
			break;
		case ';':
			symtab[mysym].dim = 1;
			def(mysym);
			lex();
			break;
		case ',':
			symtab[mysym].dim = 1;
			def(mysym);
			lex();
			goto moredecls;
		case '(':
			if (scope != 0) {
				error("nested definition of function %s",
				      namestring(symtab[mysym].ipos));
			}
			lex();

			funcbegin(mysym);

			args = 0;
			while ((size = ctype()) != 0) {
				argsym = newsym();
				symtab[argsym].type = VAR;
				symtab[argsym].size = 1;
				symtab[argsym].dim = 1;
				def(argsym);

				/* Copy arg from register to local */
				pushnum(symtab[argsym].base);
				pushop(AFP);
				lval(1);
				pusharg(args++);
				store(0);

				if (match('[')) {
					error("array arguments currently not supported");
				}
				match(',');
			}
			if (!match(')')) {
				warn("missing ) in function argument declaration");
			}
			stat();
			funcend(mysym);

			break;
		default:
			error("declaration missing ; or argument list");
		}
	}
}

int
main(int argc, char **argv)
{
	int c;

	printf("; generated by %s version %d\n", argv[0], VERSION);
	eof = 0;
	while ((c = getchar()) != EOF) input[eof++] = c;
	input[eof] = 0;
	ipos = 0;
	nextt = lex();
	startup();
	decl();
}
