/*
 * 19 March 2001
 *
 * ANSI C PL/0 V3 compiler.
 *
 * Based on Professor Takaoka's Version 3 compiler.
 *
 * Ref: http://www.cosc.canterbury.ac.nz/~tad and http://www.cosc.canterbury.ac.nz/~tad/cosc230/
 *
 * Thong Nguyen (tng12@cosc)
 */

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

typedef struct tag_namerecord
{
  int kind;
  char name[10];
  int val;
  int level;
  int adr;
  int indirect;
}
namerecord;

/*
 * Relates a keyword (as a string) with a symbol (as a number).
 */
typedef struct tag_wordsymbol
{
	char word[255];	
	int symbol;
}
wordsymbol;

/*
 * Each PL/0 instruction is made up of a code (f) and 2 arguments (l and a).
 */
typedef struct tag_instruction
{
  int f; int l; int a;
}
instruction;

enum SymbolsEnum
{
	SYM_NULL = 0,
	SYM_IDENT = 1, SYM_NUMBER = 2, SYM_PLUS = 3, SYM_MINUS = 4, SYM_MULT = 5, SYM_SLASH = 6,
	SYM_ODD = 7, SYM_EQL = 8, SYM_NEQ = 9, SYM_LSS = 10, SYM_LEQ = 11, SYM_GTR = 12,
	SYM_GEQ = 13, SYM_LPAREN = 14, SYM_RPAREN = 15, SYM_COMMA = 16, SYM_SEMICOLON = 17,	SYM_PERIOD = 18,
	SYM_BECOMES = 19, SYM_BEGIN = 20, SYM_END = 21, SYM_IF = 22, SYM_THEN = 23, SYM_WHILE = 24,
	SYM_DO = 25, SYM_CALL = 26, SYM_CONST = 27, SYM_VAR = 28, SYM_PROC = 29, SYM_WRITE = 30, SYM_AMPER = 31
};

enum OpcodesEnum
{
	OC_LIT = 1,
	OC_OPR = 2,	OC_LOD = 3, OC_STO = 4, OC_CAL = 5, OC_INC = 6, OC_JMP = 7,
	OC_JPC = 8, OC_WRT = 9, OC_LDA = 10, OC_STP = 11, OC_LID = 12, OC_SID = 13
};

enum OperatorOpcodesEnum
{
	OPR_RET = 0,
	OPR_ADD = 1, OPR_SUB = 2, OPR_MUL = 3, OPR_DIV = 4, OPR_NEG = 5, OPR_EQL = 6,
	OPR_NEQ = 7, OPR_LSS = 8, OPR_LEQ = 9, OPR_GTR = 10, OPR_GEQ = 11, OPR_ODD = 12
};

#define MAX_LINE 1024
static char line[MAX_LINE];

/* max. no. of digits in numbers */
#define MAX_DIGITS 14
/* maximum address */
#define MAX_ADDRESS 2047
/* maximum depth of block nesting */
#define MAX_LEVEL 3
/* size of code array */
#define MAX_CODE 20000

/*
 * Types of identifier.
 */
#define KIND_CONSTANT 1
#define KIND_VARIABLE 2
#define KIND_PROCEDURE 3

static char ch;
/* last symbol read */
static int  sym ;  
/* Last identifier read */
static char *id;
/* Last number read */
static int num;
/* Character count */
static int cc;
/* Line length */
static int ll;
/* Code allocation index */
static int cx;

static int flag;

/* Identifier buffer */

#define MAX_IDENTLEN 255
static char ident[MAX_IDENTLEN];

/* datastore (stack) */
#define MAX_STACK 1024
static int s[MAX_STACK];

static namerecord table[1000];
static instruction code[10000];

#define MAX_WORDS 200
static int numwords = 0;
static wordsymbol wordsymbols[MAX_WORDS];

#define MAX_SSYM 100
static int ssym[MAX_SSYM];

static FILE *f;

/*
 * Predeclarations of some functions.
 */
static int position(char *id, int *ptx);
static void expression(int lev, int *ptx);

static void aparam(int lev, int *ptx);
void fparam(int lev, int *ptx, int *pdx);


static int do_stack_trace = 0;

/*
 * Report an error.
 */
void error(int i)
{
	printf("error %d\n",i);
}

/*
 * Fills [line] with the next line in the source code file.
 */
int getline()
{
	cc = 0; ll = 0;

	line[0] = 0;

	fgets(line, MAX_LINE, f);

	ll = strlen(line);
	
	printf("%s", line);
	
	return 1;
}

/*
 * Sets [ch] to be the next character read.
 */
void getch()
{	
	/*
	 * Read the next line if the current line has been fully read.
	 */
	if (cc >= ll)
	{
		if (ch == -1)
		{
			printf("PROGRAM INCOMPLETE\n");
			
			getchar();
			
			return;
		}

		if (getline() != 1)
		{
			cc = 0;
			ch = 0;

			return;
		}
	}

	ch = line[cc++];
}

/*
 * Searches the keyword table for a keyword that matches the current string in [ident].
 * Returns the 1 based index in the wordsymbols[] array if is exists.  Otherwise returns 0.
 */
int search()
{	
	int i;
	
	for (i = numwords; i >= 1; i--)
	{
		if (strcmp(ident, wordsymbols[i].word) == 0)
		{
			return i;
		}
	}
	
	return 0;
}

/*
 * Sets [sym] to be the current symbol (eg. SYM_IF) read.
 */
void getsym()
{
	/*
	 * Read leading or tailing space, tab, newline and carriage return characters.
	 */

	int i, k;

	while (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') 
	{
		getch();
	}

	if (isalpha((int)ch) /* ch is an alphabetical character */)
	{
		/*
		 * Sym is a keyword or variable name.
		 */

		k = 0;
		
		do
		{
			if (k < MAX_IDENTLEN)
			{				
				ident[k++] = ch;
			}

			getch(); 
		}
		while(((ch >= '0') && (ch <= '9')) || isalpha((int)ch));

		ident[k] = 0;
		id = ident;
	
		if ((i = search()) != 0)
		{
			sym = wordsymbols[i].symbol;
		}
		else
		{
			sym = SYM_IDENT; 
		}
	}
	else if ((ch >= '0') && (ch <= '9'))
	{
		/**
		 * Sym is a number.
		 */

		k = 0;
		num = 0;
		sym = SYM_NUMBER;

		do 
		{
			num = 10 * num + (ch - '0');

			k++;
			
			getch();

			if (k > MAX_DIGITS)
			{
				error(30);

				break;
			}
		}
		while ((ch >= '0') && (ch <= '9'));
	}
	else if (ch == ':')
	{
		/*
		 * Sym is the assigning operator ":="
		 */

		getch();

		if (ch == '=')
		{
			sym = SYM_BECOMES;
			
			getch();
		}
		else
		{
			sym = SYM_NULL;
		}
	}
	else if (ch >= 0 && ch < MAX_SSYM)
	{
		sym = ssym[(int)ch];
		
		getch();
	} 
	else if (ch == -1)
	{
		/**
		 * End of file or error.
		 */

		sym = SYM_NULL;

		return;
	}
	else
	{
		/*
		 * End of file or error.
		 */

		sym = SYM_NULL;

		return;
	}
}

/*
 * Generates a PL/0 machine instruction.
 */
void gen(int f, int l, int a)
{
	if (cx > MAX_CODE)
	{
		printf("program too long\n");
	}
	else
	{
		code[cx].f = f;
		code[cx].l = l;
		code[cx].a = a;
	}

	cx++;
}

/*
 * Enters a variable/identifier into the name table [table].
 */
void enter(int kind, int *ptx, int *pdx, int lev)
{
	(*ptx)++; 

	/* Copies the identifier into the name table. */

	strcpy(&table[*ptx].name[0], id);
  
	table[*ptx].kind = kind;

	if (kind == KIND_CONSTANT)
	{
		table[*ptx].val = num;
	}
	else if (kind == KIND_VARIABLE) 
	{ 
		table[*ptx].level = lev;
		table[*ptx].adr = *pdx;
		table[*ptx].indirect = flag;
		(*pdx)++;
	}
	else if (kind == KIND_PROCEDURE)
	{ 
		table[*ptx].level = lev;
	}
	else
	{
		error(103); /* unknown kind */
	}
}

/*
 * Finds and returns the position of an identfier in the name table.
 * Returns 0 if no entry is found.
 */
int position(char *id, int *ptx)
{
	int i;
			
	for (i = *ptx; i > 0; i--)
	{
		if (strcmp(table[i].name, id) == 0)
		{
			/* id found in name table at position 'i' */

			break;
		}
	}	

	return i;
}

/*
 * Handles declarations of constants.
 */
void constdeclaration(int lev, int *ptx, int *pdx)
{
	if (sym == SYM_IDENT)
	{
		getsym();
		
		if ((sym == SYM_EQL) || (sym == SYM_BECOMES))
		{
			if (sym == SYM_BECOMES) error(1);
			
			getsym();
			
			if (sym == SYM_NUMBER)
			{
				enter(KIND_CONSTANT, ptx, pdx, lev);

				getsym();
			}
		}
	}
}

/*
 * Handles declarations of variables.
 */
void vardeclaration(int lev, int *ptx, int *pdx)
{
	if (sym == SYM_IDENT) 
	{
		enter(KIND_VARIABLE, ptx, pdx, lev);

		getsym();
	}
	else
	{
		error(4); 
	}
}

void factor(int lev, int *ptx)
{
	int i, kind, level, adr, val;

	while ((sym == SYM_IDENT) || (sym == SYM_NUMBER) || (sym == SYM_LPAREN)) 
	{
		if (sym == SYM_IDENT) 
		{
			i = position(id, ptx);

			if (i == 0)
			{
				error(11);
			}
			else
			{
				kind = table[i].kind;
				level = table[i].level;
				adr  =table[i].adr;
				val = table[i].val;

				if (kind == KIND_CONSTANT)
				{
					gen(OC_LIT, 0, val);
				}
				else if (kind == KIND_VARIABLE)
				{
					if (table[i].indirect == 0)
					{
						gen(OC_LOD, lev - level, adr);
					}
					else
					{
						gen(OC_LID, lev - level, adr);
					}
				}
				else
				{
					error(21);
				}
			}

			getsym(); 
		}
		else if (sym == SYM_NUMBER)
		{
			if (num > MAX_ADDRESS)
			{
				error(31);
				num = 0;
			}
			
			gen(OC_LIT, 0, num);
			
			getsym();
		}
		else if (sym == SYM_LPAREN)
		{
			getsym();
			
			expression(lev, ptx);
			
			if (sym == SYM_RPAREN)
			{
				getsym();
			}
			else
			{
				error(22);
			}
		}
	}
}

void term(int lev,int *ptx)
{
	int mulop;

	factor(lev, ptx);

	while ((sym == SYM_MULT) || (sym == SYM_SLASH))
	{
		mulop = sym;
		getsym();
		factor(lev,ptx);
		
		if (mulop == SYM_MULT)
		{
			gen(OC_OPR, 0, OPR_MUL);
		}
		else
		{
			gen(OC_OPR, 0, OPR_DIV);
		}
	}
}

void expression(int lev, int *ptx)
{
	int addop;

	if ((sym == SYM_PLUS) || (sym == SYM_MINUS))
	{
		addop = sym;
		getsym();
		term(lev, ptx);
		
		if (addop == SYM_MINUS)
		{
			gen(OC_OPR, 0, OPR_NEG);
		}
	}
	else
	{
		term(lev,ptx);
	}

	while ((sym == SYM_PLUS) || (sym == SYM_MINUS))
	{
		addop = sym;
		
		getsym();
		
		term(lev,ptx);

		if (addop == SYM_PLUS)
		{
			gen(OC_OPR, 0, OPR_ADD);
		}
		else
		{
			gen(OC_OPR, 0, OPR_SUB);
		}
	}
}

/*
 * Conditions push 1 or 0 onto the stack (true or false).
 */
void condition(int lev, int *ptx)
{
	int relsym;
	
	if(sym == SYM_ODD)
	{
		getsym();
		expression(lev, ptx);
		gen(OC_OPR, 0, OPR_ODD);
	}
	else 
	{
		expression(lev,ptx);
		
		if ((sym != SYM_EQL) && (sym != SYM_NEQ) && (sym != SYM_LSS)
			&& (sym != SYM_LEQ) && (sym != SYM_GTR) && (sym != SYM_GEQ))
		{
			error(20);
		}
		else
		{
			relsym = sym;
			
			getsym();
			
			expression(lev, ptx);
			
			switch (relsym)
			{
			case SYM_EQL:
				
				gen(OC_OPR, 0, OPR_EQL);

				break;

			case SYM_NEQ:

				gen(OC_OPR, 0, OPR_NEQ);
				
				break;

			case SYM_LSS:

				gen(OC_OPR, 0, OPR_LSS);
				
				break;

			case SYM_LEQ:
				
				gen(OC_OPR, 0, OPR_LEQ);
				
				break;

			case SYM_GTR:
				
				gen(OC_OPR, 0, OPR_GTR);
				
				break;

			case SYM_GEQ:

				gen(OC_OPR, 0, OPR_GEQ);
				
				break;
			}
		}
	}
}

void statement(int lev, int *ptx)
{
	int i, cx1,cx2;

	if (sym == SYM_IDENT)
	{
		i = position(id, ptx);

		if (i == 0)
		{
			error(11);
		}
		else
		{
			if (table[i].kind != KIND_VARIABLE)
			{
				error(12);
				i = 0;
			}
		}

		getsym(); 

		if (sym == SYM_BECOMES)
		{
			getsym();
		}
		else
		{
			error(13);
		}

		expression(lev, ptx);
		
		if (i != 0)
		{
			if (table[i].indirect == 0)
			{
				gen(OC_STO, lev-table[i].level, table[i].adr);
			}
			else
			{
				gen(OC_SID, lev-table[i].level, table[i].adr);
			}
		}
	} 
	else if (sym == SYM_CALL)
	{
		getsym();
		
		if (sym != SYM_IDENT)
		{
			error(14);
		}
		else
		{
			i = position(id, ptx);
			
			if(i == 0)
			{
				error(11);
			}
			else
			{
				getsym();

				if (sym == SYM_LPAREN)
				{
					getsym();
					aparam(lev, ptx);

					if (sym == SYM_RPAREN)
					{
						getsym();
					}
					else
					{
						error(89);
					}
				}

	
				if (table[i].kind == KIND_PROCEDURE)
				{
					gen(OC_CAL, lev - table[i].level, table[i].adr);
				}
				else
				{
					error(15); 
				}
			}
		}
	}
	else if (sym == SYM_IF)
	{
		getsym();
		condition(lev, ptx);
		
		if (sym == SYM_THEN)
		{
			getsym();
		}
		else
		{
			error(16);
		}

		cx1 = cx;
		gen(OC_JPC, 0, 0);

		statement(lev, ptx);
		code[cx1].a = cx; 
	}
	else if (sym == SYM_BEGIN)
	{
		getsym();
		statement(lev,ptx);
	
		while((sym == SYM_SEMICOLON) || (sym == SYM_BEGIN) ||
			(sym == SYM_IF) || (sym == SYM_WHILE) ||
				(sym == SYM_CALL) || (sym == SYM_WRITE))
		{
			if(sym == SYM_SEMICOLON)
			{
				getsym();
			}
			else
			{
				error(10);
			}
			
			statement(lev, ptx);
		}

		
		if (sym == SYM_END)
		{
			getsym();
		}
		else
		{
			error(17);
		}
	}
	else if (sym == SYM_WHILE)
	{
		cx1 = cx;
		getsym();
		
		condition(lev, ptx);
		
		cx2 = cx;
		
		gen(OC_JPC, 0, 0);

		if (sym == SYM_DO)
		{
			getsym();
		}
		else
		{
			error(18);
		}

		statement(lev, ptx);
		gen(OC_JMP, 0, cx1);
		code[cx2].a = cx;
	}
	else if (sym == SYM_WRITE)
	{
		getsym(); 
		
		if (sym != SYM_LPAREN)
		{
			error(98);
		}
		else
		{
			getsym();
			expression(lev, ptx);
			gen(OC_WRT, 0, 0);
			
			if (sym != SYM_RPAREN)
			{
				error(99);
			}
			else
			{
				getsym(); 
			}
		}
	}
}

void block(int lev, int tx)
{
	int dx, tx0, cx0;

	dx = 3; tx0 = tx;
	table[tx].adr = cx;
	
	gen(OC_JMP, 0, 0);
	
	/* 
	 * Can only have levmax amount of embedded functions.
	 */
	if (lev > MAX_LEVEL)
	{
		error(32);
	}

	do
	{
		if (sym == SYM_CONST)
		{
			getsym();
			
			do
			{
				constdeclaration(lev, &tx, &dx);

				while(sym == SYM_COMMA)
				{
					getsym();
					constdeclaration(lev, &tx, &dx);
				}

	
				if(sym == SYM_SEMICOLON)
				{
					getsym();
				}
				else
				{
					error(5);
				}
			}
			while (sym == SYM_IDENT);
		}
		
		if (sym == SYM_VAR)
		{
			getsym();

			do
			{
				vardeclaration(lev, &tx, &dx);

				while (sym == SYM_COMMA)
				{
					getsym();
					vardeclaration(lev, &tx,&dx);
				}
 
				if(sym == SYM_SEMICOLON)
				{
					getsym();
				}
				else
				{
					error(5);
				}
			}
			while (sym == SYM_IDENT);
		}

		if (sym == SYM_LPAREN)
		{
			getsym();
			fparam(lev, &tx, &dx);

			if (sym == SYM_RPAREN)
				getsym();
			else
				error(88);
		}

		while (sym == SYM_PROC)
		{
			getsym();
			
			if(sym == SYM_IDENT)
			{
				enter(KIND_PROCEDURE, &tx, &dx, lev);
				
				getsym();
			}
			else
			{
				error(4);
			}

			if (sym == SYM_SEMICOLON)
			{
				getsym();
			}
			else if (sym == SYM_LPAREN)
			{
				/* Does nothing */
			}
			else
			{
				error(5);
			}

			block(lev + 1, tx);

			if(sym == SYM_SEMICOLON)
			{
				getsym();
			}
			else
			{
				error(5);
			}
		}
	}
	while ((sym == SYM_CONST) || (sym == SYM_VAR) || (sym == SYM_PROC));


	code[table[tx0].adr].a = cx;
	table[tx0].adr = cx;
	cx0 = cx;
	gen(OC_INC, 0, dx);
	
	statement(lev, &tx);
	
	gen(OC_OPR, 0, 0);
}

/* 
 * Enters formal parameters (e.g. declared paramater list for a procedure).
 */
void fparam(int lev, int *ptx, int *pdx)
{   
	if (sym == SYM_VAR)
	{
		flag = 1;
		getsym();
	}
	else
	{
		flag = 0;
	}

	if(sym == SYM_IDENT)
	{
		vardeclaration(lev,ptx,pdx);
	}
	else
	{
		error(81);
	}

    flag = 0;
	
	while (sym == SYM_COMMA)
	{
		getsym();
		
		if (sym == SYM_VAR)
		{
			flag = 1;
			getsym();
		}
		else
		{
			flag = 0;
		}
		
		vardeclaration(lev, ptx, pdx);
		
		flag = 0;
   }

   if (sym != SYM_RPAREN)
   {
	   error(82);
   }
}

/* 
 * Loads actual parameters (e.g. arguments to a procedure).
 */
void aparam(int lev,int *ptx)
{
	int i, pnum = 0, adr, level;

	if(sym == SYM_AMPER)
	{
	
		getsym();
		
		if(sym != SYM_IDENT)
			error(70);

		i = position(id, ptx);
		adr = table[i].adr;
		level = table[i].level;

		gen(OC_LDA, lev-level, adr);
		getsym();
	} 
	else 
	{
		expression(lev, ptx);
	}

	pnum++;

	while (sym == SYM_COMMA)
	{ 
		getsym(); 

		if(sym == SYM_AMPER)
		{
			getsym();

			if (sym != SYM_IDENT)
			{
				error(70);
			}

			i = position(id, ptx);
			adr = table[i].adr;
			level = table[i].level;
			gen(OC_LDA, lev - level, adr);
			getsym();
		}
		else
		{
			expression(lev,ptx);
		}

		pnum++; 
	}

	while(pnum > 0)
	{
		gen(OC_STP,0,0);
		pnum--;
	}
}

/*
 * Finds the base pointer l levels down.
 */
int base(int l, int b)
{  
	while (l > 0)
	{
		b = s[b];
		l--;
	}

	return b;
}

/*
 * Interpretor for  PL/0 machine instructions.
 */
void interpret()
{
	/* instruction register */
	int i = 0, j = 0;
	/* program-, base-, topstack-registers */
	int p, b, t; 

	
	printf("Start PL/0\n");

	t = 0; b = 1; p = 0;
	s[1] = 0; s[2] = 0; s[3] = 0;

	do
	{
		i = code[p].f; 
		
		if (do_stack_trace)
		{
			printf("%2d:", p);
		}

		switch (i)
		{			
			case OC_LIT:
				
				t++;
				s[t]=code[p].a;
				p++;

				break;

			case OC_OPR:

				switch (code[p].a)
				{
				case OPR_RET: 
					
					/**
					 * Return from proc.
					 */

					t = b - 1;
					p = s[t + 3];
					b = s[t+2];

					break;

				case OPR_NEG:
					
					s[t] = -s[t]; p++; break;

				case OPR_ADD:
					
					t--; s[t] = s[t] + s[t + 1]; p++; break;

				case OPR_SUB:
					
					t--; s[t] = s[t] - s[t + 1]; p++; break;

				case OPR_MUL:
					
					t--; s[t] = s[t] * s[t + 1]; p++; break;

				case OPR_DIV:
					
					t--; s[t] = s[t] / s[t + 1]; p++; break;

				case OPR_ODD:					
					
					s[t] = (s[t] % 2 == 1) ? 1 : 0;
					p++;
					
					break;

				case OPR_EQL:
					
					t--; s[t]=(s[t]==s[t+1]); p++; break;

				case OPR_NEQ:
					
					t--; s[t]=(s[t]!=s[t+1]); p++; break;

				case OPR_LSS:
					
					t--; s[t]=(s[t]<s[t+1]); p++; break;

				case OPR_LEQ:
					
					t--; s[t]=(s[t]<=s[t+1]); p++; break;

				case OPR_GTR:
					
					t--; s[t]=(s[t]>s[t+1]); p++; break;

				case OPR_GEQ:
					
					t--; s[t]=(s[t]>=s[t+1]); p++; break;

				}
				
				break;

			case OC_LOD:
				
				t++;
				s[t] = s[base(code[p].l,b) + code[p].a];
				p++;

				break;

			case OC_STO:
				
				s[base(code[p].l,b) + code[p].a] = s[t];   
			
				p++; t--;
				
				break;

			case OC_CAL: /* generate new block mark */
				
				s[t + 1] = base(code[p].l, b);
				s[t + 2] = b;
				s[t + 3] = p + 1;
				b = t + 1; p = code[p].a;
				
				break;

			case OC_INC:
				
				t = t + code[p].a;
				p++;
				
				break;

			case OC_JMP:
				
				p = code[p].a;
				
				break;

			case OC_JPC:
				
				if (s[t] == 0)
					p = code[p].a;
				else p++;

				t--;
				
				break;

			case OC_WRT:
				
				printf("%d\n",s[t]);
				t--;
				p++;
				
				break;

			case OC_STP:
				
				s[t + 3] = s[t];
				t--;
				p++;
				
				break;

			case OC_LDA:
				
				t++;
				s[t] = base(code[p].l,b) + code[p].a;
				p++;
				
				break;

			case OC_LID:
				
				t++;
				s[t] = s[s[base(code[p].l,b) + code[p].a]];
				p++;
				
				break;

			case OC_SID:
				
				s[s[base(code[p].l,b) + code[p].a]] = s[t];
				p++;
				t--;
				
				break;
			
		}

		if (do_stack_trace)
		{
			for (j = 1; j <= t; j++)
			{
				printf("%3d ", s[j]);
			}

			printf("\n");
		}

		if (t >= (MAX_STACK - 0x10))
		{
			printf("PL/0 machine stack overflow error.\n");

			break;
		}
	}
	while (p != 0);
	
	printf("End PL/0\n");
}

/*
 * Prints out the opcodes is readable form.
 */
void printcode()
{
	int i;

	for (i = 0; i <= cx - 1; i++)
	{
		printf("%2d ",i);

		switch (code[i].f)
		{
			case OC_LIT: printf("lit "); break;
			case OC_OPR: printf("opr "); break;
			case OC_LOD: printf("lod "); break;
			case OC_STO: printf("sto "); break;
			case OC_CAL: printf("cal "); break;
			case OC_INC: printf("inc "); break;
			case OC_JMP: printf("jmp "); break;
			case OC_JPC: printf("jpc "); break;
			case OC_WRT: printf("wrt "); break;
			case OC_STP: printf("stp "); break;
			case OC_LDA: printf("lda "); break;
			case OC_LID: printf("lid "); break;
			case OC_SID: printf("sid "); break;
		}

		printf("%d ", code[i].l);
		printf("%d ", code[i].a);
		printf("\n");
	}
}

/*
 * Adds a keyword and relates it to a symbol (int).
 */
void addword(const char *word, int symbol)
{
	if (numwords >= MAX_WORDS)
	{
		printf("Too many words.");

		return;
	}

	numwords++;

	strcpy(wordsymbols[numwords].word, word);
	wordsymbols[numwords].symbol = symbol;
}

int main(int argx, char *argv[])
{
	char *fname;
	char buffer[0x100];

	addword("begin", SYM_BEGIN);
	addword("call", SYM_CALL);
	addword("const", SYM_CONST);
	addword("do", SYM_DO);
	addword("end", SYM_END);
	addword("if", SYM_IF);
	addword("odd", SYM_ODD);
	addword("procedure", SYM_PROC);
	addword("then", SYM_THEN);
	addword("var", SYM_VAR);
	addword("while", SYM_WHILE);
	addword("write", SYM_WRITE);

	ssym['+'] = SYM_PLUS; ssym['-'] = SYM_MINUS; ssym['*'] = SYM_MULT; ssym['/'] = SYM_SLASH;
	ssym['('] = SYM_LPAREN; ssym[')'] = SYM_RPAREN; ssym['='] = SYM_EQL; ssym[','] = SYM_COMMA;
	ssym['.'] = SYM_PERIOD; ssym['#'] = SYM_NEQ; ssym['<'] = SYM_LSS; ssym['>'] = SYM_GTR;
	ssym['$'] = SYM_LEQ; ssym['%'] = SYM_GEQ; ssym[';'] = SYM_SEMICOLON; ssym['&'] = SYM_AMPER;
		
	if (argx <= 1)
	{
		/*
		 * If a filename isn't supplied as an argument.  Ask for one.
		 */
		printf("Input source file name: ");
	
		gets(buffer);
		fname = buffer;
	}
	else
	{
		/*
		 * The filename is supplied as an argument.
		 */
		fname = argv[1];

		if (argx >= 3)
		{
			/*
			 * If the second argument is "-trace" then do stack traces when interpreting.
			 */
			if (strcmp(argv[2], "-trace") == 0)
			{
				do_stack_trace = 1;
			}
		}
	}
	
	f = fopen(fname,"r");

	if (f == NULL)
	{
		printf("File [%s] not found.\n", fname);

		return 0;
	}
	
	cc = 0; ll = 0; ch = ' '; cx = 0;
	
	printf("Compiling...\n");

	getsym();	
	block(0, 0);

	/*
	 * Check that the last symbol in the code is a period.
	 */
	if (sym != SYM_PERIOD)
	{
		error(9);
	}
	
	/*
	 * Compilation is complete.  Close the file.
	 */
	fclose(f);
	
	printf("\nPress enter to list opcodes...\n");
	getchar();	

	printcode();

	printf("\nPress enter to interpret...\n");
	getchar();

	interpret();
	
	printf("\nPress enter to exit...\n");	
	getchar();

	return 0;
}


