/*	logickcc.c

	Logick C Compiler.

	This is a small C subset compiler created for the Fall
	2017 EE480 Logick (LOGarithmic number system arithmetic
	-- ICK!) assembly language and processor
	implementations.  It's a 16-bit word-oriented machine,
	including slightly mutant 16-bit LNS, which are not yet
	implemented here.  See
	http://aggregate.org/EE480/f17a1.html for details.

	2017 by Hank Dietz, http://aggregate.org/hankd
*/

#define	VERSION	20171002

#ifdef	CGI
#include "cgic.h"
#endif

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

//	#define	LINETRACE	1

int	haveinput = 0;
FILE	*myout;

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	0x8000	/* 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,	SP,	FP,	RA,	RV,	U10,	U9,	U8,
	U7,	U6,	U5,	U4,	U3,	U2,	U1,	U0
} reg_t;

#define	ARG(N)	(U10+(N))

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

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


#define	MKREGOP(OP) \
static inline void \
logick_##OP(reg_t d, reg_t s, reg_t t) \
{ \
	fprintf(myout, "\t" #OP "\t$%s,$%s,$%s\n", regname[d&15], regname[s&15], regname[t&15]); \
}

MKREGOP(ad)
MKREGOP(al)
MKREGOP(an)
MKREGOP(dl)
MKREGOP(eo)
MKREGOP(ml)
MKREGOP(or)
MKREGOP(sr)

#define	MKREGOP2(OP) \
static inline void \
logick_##OP(reg_t d, reg_t s) \
{ \
	fprintf(myout, "\t" #OP "\t$%s,$%s\n", regname[d&15], regname[s&15]); \
}

MKREGOP2(cl)
MKREGOP2(co)
MKREGOP2(lo)
MKREGOP2(mi)
MKREGOP2(nl)
MKREGOP2(no)
MKREGOP2(st)

#define	logick_dup(D, S)	logick_or(D, S, S)

static inline void
logick_sy(void)
{
	fprintf(myout, "\tsy\n");
}

static inline void
logick_li(reg_t d, int i)
{
	i &= 0xffff;
	if (((i & 0xff80) == 0) || ((i & 0xff80) == 0xff80)) {
		fprintf(myout, "\tli\t$%s,0x%02x\n", regname[d&15], (i & 0xff));
	} else {
		fprintf(myout, "\tli\t$%s,0x%02x\n", regname[d&15], ((i >> 8) & 0xff));
		fprintf(myout, "\tsi\t$%s,0x%02x\n", regname[d&15], (i & 0xff));
	}
}

static inline void
logick_la_(reg_t d, char *s)
{
	fprintf(myout, "\tla\t$%s,%s%s\n", regname[d&15], prelab, s);
}

static inline void
logick_la(reg_t d, int i)
{
	/* Force i to be a signed 16-bit number */
	i = (i & 0xffff);
	if (i & 0x8000) i |= 0xffff0000;
	fprintf(myout, "\tla\t$%s,%d\n", regname[d&15], i);
}

static inline void
logick_jb(char *c, int i)
{
	fprintf(myout, "\tjb\t%s,%s%d\n", c, prelab, i);
}

static inline void
logick_jb_(char *c, char *n)
{
	fprintf(myout, "\tjb\t%s,%s%s\n", c, prelab, n);
}

static inline void
logick_jr(char *c, int i)
{
	fprintf(myout, "\tjr\t%s,$%s\n", c, regname[i&15]);
}

void
logick_space(register int n)
{
	fprintf(myout, "\t.space\t%d\n", n);
}

void
logick_text(void)
{
	fprintf(myout, "\t.text\n");
}

void
logick_data(int n)
{
	fprintf(myout, "\t.data\n\t.origin\t%d\n", n);
}

void
label(register int a)
{
	fprintf(myout, "%s%d:\n", prelab, a);
}

void
label_(register char *s)
{
	fprintf(myout, "%s%s:\n", prelab, s);
}

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

void
pushgpoff(char *s)
{
	incsp();
	logick_la_(TOS, s);
	objsize[sp-1] = 0;
}

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

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

void
pusharg(register int argno)
{
	incsp();
	logick_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: logick_lo(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();
	logick_dup(ARG(argno), TOS);
	decsp();
}

void
jumpreg(register int r)
{
	logick_jr("t", r);
}

void
jump(register int a)
{
	logick_jb("t", a);
}

void
jumpf(register int a)
{
	logick_co(TOS, ZERO);
	logick_jb("eq", a);
	decsp();
}

void
jumpt(register int a)
{
	logick_co(TOS, ZERO);
	logick_jb("ne", a);
	decsp();
}


void
ghoto(register char *s)
{
	logick_jb_("t", s);
}

void
target(register char *s)
{
	fprintf(myout, "%s%s:\n", prelab, s);
}

void
pushop(register int op)
{
	int lab;

	switch (op) {
	case NEG:
		loadtos();
		logick_mi(TOS, TOS);
		break;
	case '!':
		loadtos();
		logick_no(TOS, TOS);
		++labnum;
		break;
	case '~':
		loadtos();
		logick_la(TMP, -1);
		logick_eo(TOS, TOS, TMP);
		break;
	case AFP:
		loadtos();
		logick_ad(TOS, TOS, FP);
		break;
	case ASP:
		loadtos();
		logick_ad(TOS, TOS, SP);
		break;
	case RETVAL:
		loadtos();
		logick_dup(RV, TOS);
		decsp();
		break;
	case '+':
		loadnostos();
		logick_ad(NOS, NOS, TOS);
		decsp();
		break;
	case '-':
		/* NOS = NOS - TOS */
		loadnostos();
		logick_mi(TOS, TOS);
		logick_ad(NOS, NOS, TOS);
		decsp();
		break;
	case SUBR:
		/* NOS = TOS - NOS */
		loadnostos();
		logick_mi(NOS, NOS);
		logick_ad(NOS, NOS, TOS);
		decsp();
		break;
	case SL:
		/* NOS = NOS << TOS */
		loadnostos();
#ifdef	SL_LOOP
		logick_la(TMP, -1);
		label(labnum);
		logick_co(TOS, ZERO);
		logick_jb("le", labnum+1);
		logick_ad(TOS, TOS, TMP);
		logick_ad(NOS, NOS, NOS);
		logick_jb("t", labnum);
		label(labnum+1);
		labnum += 2;
#else
		logick_mi(TOS, TOS);
		logick_sr(NOS, NOS, TOS);
#endif
		decsp();
		break;
	case SR:
		/* NOS = NOS >> TOS */
		loadnostos();
		logick_sr(NOS, NOS, TOS);
		decsp();
		break;
	case LE:
		loadnostos();
		logick_co(NOS, TOS);
		logick_no(NOS, ZERO);
		logick_jb("le", labnum);
		logick_dup(NOS, ZERO);
		label(labnum);
		++labnum;
		decsp();
		break;
	case '<':
		loadnostos();
		logick_co(NOS, TOS);
		logick_no(NOS, ZERO);
		logick_jb("lt", labnum);
		logick_dup(NOS, ZERO);
		label(labnum);
		++labnum;
		decsp();
		break;
	case GE:
		loadnostos();
		logick_co(NOS, TOS);
		logick_no(NOS, ZERO);
		logick_jb("ge", labnum);
		logick_dup(NOS, ZERO);
		label(labnum);
		++labnum;
		decsp();
		break;
	case '>':
		loadnostos();
		logick_co(NOS, TOS);
		logick_no(NOS, ZERO);
		logick_jb("gt", labnum);
		logick_dup(NOS, ZERO);
		label(labnum);
		++labnum;
		decsp();
		break;
	case EQ:
		loadnostos();
		logick_eo(NOS, NOS, TOS);
		logick_no(NOS, NOS);
		decsp();
		break;
	case NE:
		loadnostos();
		logick_eo(NOS, NOS, TOS);
		logick_no(NOS, NOS);
		logick_no(NOS, NOS);
		decsp();
		break;
	case '&':
		loadnostos();
		logick_an(NOS, NOS, TOS);
		decsp();
		break;
	case '^':
		loadnostos();
		logick_eo(NOS, NOS, TOS);
		decsp();
		break;
	case '|':
		loadnostos();
		logick_or(NOS , NOS, TOS);
		decsp();
		break;
	case '*':
		loadnostos();
		logick_dup(TMP, ZERO);
		logick_la(TMP1, 1);
		lab = labnum;
		labnum += 3;
		label(lab);
		logick_co(TMP1, ZERO);
		logick_jb("ne", lab+1);
		logick_an(TMP2, TMP1, TOS);
		logick_co(TMP2, ZERO);
		logick_jb("eq", lab+2);
		logick_ad(TMP, TMP, NOS);
		label(lab+2);
		logick_ad(TMP1, TMP1, TMP1);
		logick_ad(NOS, NOS, NOS);
		jump(lab);
		label(lab+1);
		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: logick_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
		*/
		logick_dup(NOS, TOS);
		decsp();
		objsize[sp-1] = 0;
	} else {
		decsp();
		decsp();
	}
}

void
startup(void)
{
	logick_text();
	logick_jb_("t", "main");
	label_("exit");
	logick_sy();
}

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) {
		logick_la(TMP, -1);
		for (i=TOS; i<=U0; ++i) {
			logick_st(i, SP);
			logick_ad(SP, SP, TMP);
		}
	}

	/* Call the function */
	logick_jb_("t", n);

	/* Restore registers */
	if (sp > 0) {
		logick_la(TMP, 1);
		for (i=U0; i>=TOS; --i) {
			logick_ad(SP, SP, TMP);
			logick_st(i, SP);
		}
	}

	/* Copy the return value to someplace useful */
	incsp();
	logick_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;

	logick_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);
		logick_dup(RA, FP);		/* ra = mem[fp-1] */
		logick_la(TMP, -1);
		logick_ad(RA, RA, TMP);
		logick_lo(RA, RA);
		logick_dup(SP, FP);		/* sp = fp */
		logick_lo(FP, SP);		/* fp = mem[sp] (old fp) */
		logick_la(TMP, 1);
		logick_ad(SP, SP, TMP);		/* sp = old sp */
		logick_jr("t", RA);		/* return */

		label_(n);

		logick_dup(TMP, SP);		/* save sp value */
		logick_la(TMP1, -highwater);	/* sp -= highwater */
		logick_ad(SP, SP, TMP1);
		logick_la(TMP1, -1);
		logick_ad(TMP, TMP, TMP1);	/* mem[sp+highwater-1] = fp */
		logick_st(FP, TMP);
		logick_ad(TMP, TMP, TMP1);	/* mem[fp-1] = ra */
		logick_st(RA, TMP);
	} else {
		/* A leaf without locals... */

		label(endlab);
		logick_jr("t", RA);		/* return */

		label_(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;

		logick_data(DATABASE + symtab[mysym].base);
		label_(n);
		logick_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;

	logick_data(DATABASE + gpoffset);

	asize = 0;
	while (input[++spos] != '"') {
		if (++asize > 1) {
			if ((asize & 7) == 1) {
				fprintf(myout, "\n\t.word\t");
			} else {
				fprintf(myout, ", ");
			}
		} else {
			fprintf(myout, "\t.word\t");
		}
		if (input[spos] == '\\') {
			switch (input[++spos]) {
			case 't':	fprintf(myout, "%3d", '\t'); break;
			case 'n':	fprintf(myout, "%3d", '\n'); break;
			case 'r':	fprintf(myout, "%3d", '\r'); break;
			case 'b':	fprintf(myout, "%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');
				}
				fprintf(myout, "%3d", num);
				break;
			default:
				fprintf(myout, "%3d", input[spos]);
			}
		} else {
			fprintf(myout, "%3d", input[spos]);
		}
	}

	++asize;
	fprintf(myout, "\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
		fprintf(myout, "#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(namestring(symtab[mysym].ipos));
	}

	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:
		fprintf(myout, "; for loop\n");
		lex();
		assume('(');
		lab = labnum;
		labnum += 4;
		if (!match(';')) {
			expr();
			decsp();
			match(';');
		}
		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);
		break;
	case WHILE:
		lex();
		lab = labnum;
		labnum += 2;
		label(lab);
		expr();
		jumpf(lab+1);
		stat();
		jump(lab);
		label(lab+1);
		break;
	case DO:
		lex();
		lab = (labnum++);
		label(lab);
		stat();
		if (match(WHILE)) {
			error("do missing while");
		}
		expr();
		assume(';');
		jumpt(lab);
		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");
		}
	}
}

#ifndef	CGI
int
main(int arc, char **argv)
{
	int c;

	myout = stdout;
	eof = 0;
	while ((c = getchar()) != EOF) input[eof++] = c;
	input[eof] = 0;
	ipos = 0;
	nextt = lex();
	startup();
	decl();
}
#else
void
html0(void)
{
	register int i;

	cgiHeaderContentType("text/html");

	fprintf(cgiOut,
"<HTML>\n"
"<HEAD>\n"
"<TITLE>EE480 LogickCC (Logick C Compiler)</TITLE>\n"
"</HEAD>\n"
"<BODY>\n"
"<H1>EE480 LogickCC (Logick C Compiler)</H1>\n"
"<FORM METHOD=\"POST\" ACTION=\"/cgi-bin/logickcc.cgi\">\n"
"<INPUT NAME=\"haveinput\" TYPE=\"hidden\" VALUE=\"1\">\n"
"<P>\n"
"\n"
"This is a very dumb C-subset compiler generating Logick\n"
"code using a stack-based register allocation scheme.\n"
"No real attempt at optimization has been made;\n"
"even constant folding is not done.\n"
"<P>\n"
"\n"
"Enter/edit your Logick C-subset program here:\n"
"<BR>\n"
"<TEXTAREA NAME=\"input\" ROWS=24 COLS=64>\n"
"%s"
"</TEXTAREA>\n"
"<P>\n"
"<INPUT TYPE=\"SUBMIT\" VALUE=\"Compile It\">\n"
"<INPUT TYPE=\"RESET\" VALUE=\"Reset\">\n"
"<P>\n",
		(haveinput ?
		 &(input[0]) :
		 (
"int i, a, b, c;\n"
"\n"
"main()\n"
"{\n"
"\tfor (i=0; i<10; ++i) {\n"
"\t\ta = b + c;\n"
"\t}\n"
"}\n"
		  )));
}

void
html1(void)
{
	fprintf(cgiOut,
"</FORM>\n"
"<P>\n"
"<HR>\n"
"<P>\n"
"The C program that generated this page was written by\n"
"<A HREF=\"http://aggregate.org/hankd/\">Hank Dietz</A>\n"
"using the <A HREF=\"http://www.boutell.com/cgic/\">CGIC</A>\n"
"library to implement the CGI interface.\n"
"<P>\n"
"<HR>\n"
"<P>\n"
"<A HREF=\"http://aggregate.org/EE480/\"\n"
"><IMG SRC=\"http://aggregate.org/EE480/ee480logo.png\" WIDTH=40 HEIGHT=30 ALT=\"EE480\"\n"
"</A> Advanced Computer Architecture.\n"
"</BODY>\n"
"</HTML>\n");
}

int
cgiMain(void)
{
	myout = cgiOut;
	input[0] = 0;

	cgiFormInteger("haveinput", &haveinput, 0);
	cgiFormString("input", &(input[0]), (sizeof(input)-1));

	html0();
	if (haveinput) {
		fprintf(cgiOut,
"The following Logick assembly code was generated:\n"
"<PRE>\n"
			);
		eof = 0;
		while (input[eof]) ++eof;
		ipos = 0;
		nextt = lex();
		startup();
		decl();
		fprintf(cgiOut,
"</PRE>\n"
"<P>\n"
			);
	}
	html1();
}
#endif
