
/*
 * parse.y
 *
 * Bison input file for Apple2 assembler
 * (C) Copyright 1995 Archie L. Cobbs
 *
 */

/***** C DECLARATIONS *****/

%{

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "util.h"
#include "error.h"
#include "names.h"
#include "obj.h"
#include "asm.h"

/* For bison's error reporting */

	#define	yyerror		uerror

/* These are for constant expressions, a special case of IValue */

	#define	ConstExpr(ivalue)		\
		((ivalue).type == iSegRel && (ivalue).u.segrel.segment == AbsSegment)
	#define	ConstExprVal(ivalue)	((ivalue).u.segrel.offset)

/* Generic constant expression evaluation */

	#define	UnaryOp(a,b,OPR)												\
	{																		\
		if (a.type == iUnknown)												\
			b.type = iUnknown;												\
		else if (!ConstExpr(a))												\
			uerror("illegal expression");									\
		else																\
		{																	\
			b.type = iSegRel;												\
			b.u.segrel.segment = AbsSegment;								\
			b.u.segrel.offset = OPR ConstExprVal(a);						\
		}																	\
	}

	#define	BinaryOp(a,b,c,OPR)												\
	{																		\
		if (a.type == iUnknown || b.type == iUnknown)						\
			c.type = iUnknown;												\
		else if (!ConstExpr(a) || !ConstExpr(b))							\
			uerror("illegal expression");									\
		else																\
		{																	\
			c.type = iSegRel;												\
			c.u.segrel.segment = AbsSegment;								\
			c.u.segrel.offset = ConstExprVal(a) OPR ConstExprVal(b);		\
		}																	\
	}

/* This tells the lister to show the offset in the current segment */

	#define	ListLocation()													\
	{																		\
		gSegrel.segment = gSegment;											\
		gSegrel.offset = gAddress[gSegment];								\
	}

/* This tells the lister to show any bytes emitted by this line */

	#define	ListEmittedBytes()		(gListBytes = TRUE)

/* This is how we implement the if-else-endif stack */

	struct IfStack
	{
		unsigned	assembling:1;
		unsigned	doing_else:1;
	};

	static int				gIfStackPtr;
	static struct IfStack	gIfStack[MAX_IF_NESTING];
	static int				gIfLine;

%}

/***** BISON DECLARATIONS *****/

/* Tokens, precedence */

	%left	','
	%right	'?' ':'
	%left	LOR
	%left	LAND
	%left	'|'
	%left	'^'
	%left	'&'
	%left	EQU NEQ
	%left	'<' '>' LE GE
	%left	LSH RSH
	%left	'+' '-'
	%left	'*' '/' '%'
	%right	'!' '~'

	%token	LSB MSB

	%token	ALIGN ASCII ASCIZ ASSERT BEGNN BSS BYTE
	%token	CODE DATA DBYT DCI ELSE END ENDIF ENDM IF
	%token	EXPAND FLOAT GLOBAL HIBIT MACRO LONG
	%token	SPACE WORD ZERO IMPORT ZIMPORT

/* Semantic types */

	%union
	{
		unsigned		id;				/* Identifier */
		int				intval;			/* Integer literal */
		double			floatval;		/* Floating point literal */
		char			charval;		/* Character literal */
		struct TValue	textval;		/* String literal */
		struct IValue	ivalue;			/* Ivalue */
		struct List		ivlist;			/* List of ivalues */
		struct List		idlist;			/* List of identifiers */
		struct List		flist;			/* List of floats */
	}

/* Default semantic types */

	%token	<intval>	INTLIT
	%token	<floatval>	FLOATLIT
	%token	<charval>	CHARLIT
	%token	<textval>	TEXTLIT
	%token	<id>		LABEL IDENT

	%type	<intval>	const_int
	%type	<ivalue>	i_value
	%type	<textval>	t_value
	%type	<floatval>	f_value
	%type	<ivlist>	iv_list
	%type	<idlist>	id_list
	%type	<flist>		f_list

%%

/***** GRAMMER DEFINITION *****/

start:
	{

	/* Initialize state */

		gIfStackPtr = 0;
		gAssembleOn = 
		gIfStack[0].assembling = TRUE;
		gIfStack[0].doing_else = TRUE;

		gHiBit = DEFAULT_HIBIT;
		gExpand = DEFAULT_EXPAND;
	}
	program
	;

program:
	complete_line
	| program complete_line 
	;

complete_line:
	line '\n'
	{

	/* Print the line if desired */

		if (gListFile && gPass == 2)
		{
			if (!gAssembleOn && !gExpand)
			{
				if (gIfLine)
				{
					ListLine(gListFile);
					fprintf(gListFile, "\n");
					gIfLine = FALSE;
				}
			}
			else
				ListLine(gListFile);
		}

	/* Record maximum extent of each segment */

		if (gAddress[gSegment] > gMaxAddress[gSegment])
			gMaxAddress[gSegment] = gAddress[gSegment];

	/* Set these up for the next line */

		gLineNumber++;
		gSegrel.segment = -1;
		gListBytes = FALSE;
	}
	;

line:
	unlabeled_line
	| local_label unlabeled_line
	| LABEL '=' i_value
	{
		Symbol	sym = GetSym($1);

	/* Check stuff */

		switch (gPass)
		{
			case 1:
				if (sym)
					uerror("symbol \"%s\" defined previously: "
						"file \"%s\", line %d", Name(gNames, $1),
						Name(gNames, sym->file), sym->line);
				else
				{
					sym = DefineSym($1);
					sym->file = NameID(gNames, gFileName);
					sym->line = gLineNumber;
				}
				break;
			case 2:
				if (!sym)
					cerror("lost symbol \"%s\"", Name(gNames, sym->id));
				break;
			default:
				assert(0);
		}

	/* Set value */

		switch ($3.type)
		{
			case iSegRel:
				sym->type = sSegRel;
				sym->u.segrel = $3.u.segrel;
				sym->valid = TRUE;
				gSegrel = sym->u.segrel;	/* Show value in listing */
				break;
			case iSymOff:
				sym->type = sSymOff;
				sym->u.symoff = $3.u.symoff;
				sym->valid = TRUE;
				break;
			case iUnknown:
				if (gPass == 1)
				{
					sym->type = sSegRel;
					sym->u.segrel.segment = AbsSegment;
					sym->u.segrel.offset = 0;
				}
				else
					uerror("can't resolve this expression");
				break;
			default:
				assert(0);
		}

	/* Debugging */

		if (gDebug)
		{
			fprintf(stderr, "--> DEFINED: ");
			ShowSym(stderr, sym);
		}
	}
	;

/* Note that a label will get reduced before the rest of the line */

local_label:
	LABEL
	{
		Symbol	sym = GetSym($1);

		switch (gPass)
		{
			case 1:
				if (sym)
					uerror("symbol \"%s\" defined previously: "
						"file \"%s\", line %d", Name(gNames, $1),
						Name(gNames, sym->file), sym->line);
				else
				{
					sym = DefineSym($1);
					sym->file = NameID(gNames, gFileName);
					sym->line = gLineNumber;
					sym->type = sSegRel;
					sym->u.segrel.segment = gSegment;
					sym->u.segrel.offset = gAddress[gSegment];
					sym->valid = TRUE;
					if (gDebug)
					{
						fprintf(stderr, "--> DEFINED: ");
						ShowSym(stderr, sym);
					}
				}
				break;

			case 2:
				if (!sym)
					cerror("symbol not found");
				if (!sym->valid || sym->type != sSegRel)
					cerror("symbol inconsistency");
				if (sym->u.segrel.segment != gSegment
					|| sym->u.segrel.offset != gAddress[gSegment])
					cerror("sync error");
				ListLocation();
				break;

			default:
				assert(0);
		}
	}
	;

unlabeled_line:
		/* empty */

/* Instructions */

	| IDENT									/* Implied */
	{
		ListLocation();
		ListEmittedBytes();
		EmitIns($1, NULL, mImplied, -1);
	}
	| IDENT LSB i_value						/* Immediate, low byte */
	{
		ListLocation();
		ListEmittedBytes();
		EmitImm($1, &$3, FALSE);
	}
	| IDENT MSB i_value						/* Immediate, high byte */
	{
		ListLocation();
		ListEmittedBytes();
		EmitImm($1, &$3, TRUE);
	}
	| IDENT i_value							/* Absolute, relative, or ZP */
	{
		ListLocation();
		ListEmittedBytes();
		EmitIns($1, &$2, mAbsolute, mZeroPage);
	}
	| IDENT '(' i_value ')'					/* Indirect */
	{
		ListLocation();
		ListEmittedBytes();
		EmitIns($1, &$3, mIndirect, mZPIndirect);
	}
	| IDENT i_value ',' IDENT				/* Indexed */
	{
		int	mode = -1, zmode = -1;

	/* Show bytes, etc. */

		ListLocation();
		ListEmittedBytes();

	/* Index should be X or Y */

		if ($4 == NameID(gNames, "x") || $4 == NameID(gNames, "X"))
			mode = mAbsoluteX, zmode = mZeroPageX;
		if ($4 == NameID(gNames, "y") || $4 == NameID(gNames, "Y"))
			mode = mAbsoluteY, zmode = mZeroPageY;

		if (mode >= 0)
			EmitIns($1, &$2, mode, zmode);
		else
			uerror("bad addressing mode");
	}
	| IDENT '(' i_value ')' ',' IDENT
	{

	/* Show bytes, etc. */

		ListLocation();
		ListEmittedBytes();

	/* Index should be Y */

		if ($6 == NameID(gNames, "y") || $6 == NameID(gNames, "Y"))
			EmitIns($1, &$3, -1, mZPIndirectY);
		else
			uerror("bad addressing mode");
	}
	| IDENT '(' i_value ',' IDENT ')'
	{

	/* Show bytes, etc. */

		ListLocation();
		ListEmittedBytes();

	/* Index should be X */

		if ($5 == NameID(gNames, "x") || $5 == NameID(gNames, "X"))
			EmitIns($1, &$3, mIndirectX, mZPIndirectX);
		else
			uerror("bad addressing mode");
	}

/* .ascii .asciz .dci */

	| ASCII t_value
	{	
		ListLocation();
		ListEmittedBytes();
		PsAscii(&$2, FALSE, FALSE); free($2.chars);
	}
	| ASCIZ t_value
	{
		ListLocation();
		ListEmittedBytes();
		PsAscii(&$2, TRUE, FALSE); free($2.chars);
	}
	| DCI t_value
	{
		ListLocation();
		ListEmittedBytes();
		PsAscii(&$2, FALSE, TRUE); free($2.chars);
	}

/* .code, .data, .bss */

	| CODE						{ gSegment = CodeSegment; }
	| DATA						{ gSegment = DataSegment; }
	| BSS						{ gSegment = BssSegment; }

/* .hibit, .expand */

	| HIBIT i_value
	{
		if (!ConstExpr($2))
			uerror("constant expression required");
		else
			gHiBit = ConstExprVal($2) ? 0x80 : 0x00;
	}
	| EXPAND i_value
	{
		if (!ConstExpr($2))
			uerror("constant expression required");
		else
			gExpand = ConstExprVal($2) ? TRUE : FALSE;
	}

/* .byte, .word, .dbyt, .long */

	| BYTE iv_list
	{
		ListLocation();
		ListEmittedBytes();
		PsData(&$2, 1, FALSE);
	}
	| WORD iv_list
	{
		ListLocation();
		ListEmittedBytes();
		PsData(&$2, 2, FALSE);
	}
	| DBYT iv_list
	{
		ListLocation();
		ListEmittedBytes();
		PsData(&$2, 2, TRUE);
	}
	| LONG iv_list
	{
		ListLocation();
		ListEmittedBytes();
		PsData(&$2, 4, FALSE);
	}

/* .assert */

	| ASSERT i_value
	{
		if (!ConstExpr($2))
			uerror("constant expression required");
		if (gPass == 2 && !ConstExprVal($2))
			uerror("assertion failed");
	}

/* .align */

	| ALIGN i_value
	{
		ListLocation();
		if (!ConstExpr($2))
			uerror("constant expression required");
		else if (ConstExprVal($2) < 0)
			uerror("non-negative value required");
		else
			gAddress[gSegment] = RoundUp(gAddress[gSegment], ConstExprVal($2));
	}

/* .space == .ds */

	| SPACE i_value
	{
		ListLocation();
		if (!ConstExpr($2))
			uerror("constant expression required");
		else
			if ((int) gAddress[gSegment] + ConstExprVal($2) < 0)
				uerror("negative space goes beyond beginning of segment");
			else
				gAddress[gSegment] += ConstExprVal($2);
	}

/* .zero */

	| ZERO i_value
	{
		int		k;

		ListLocation();
		ListEmittedBytes();
		if (!ConstExpr($2))
			uerror("constant expression required");
		else if (ConstExprVal($2) < 0)
			uerror("non-negative value required");
		else
			for (k = 0; k < ConstExprVal($2); k++)
				Emit(0);
	}

/* .float */

	| FLOAT f_list
	{ 
		int			k;
		List		list, next;
		Byte		value[FLOAT_SIZE_6502];

	/* Show the data */

		ListLocation();
		ListEmittedBytes();

	/* Emit floats */

		for (list = &$2; list; list = list->next)
		{
			EncodeFloat(list->u.fvalue, value);
			for (k = 0; k < sizeof(value); k++)
				Emit(value[k]);
		}

	/* Free tail of list */

		for (list = $2.next; list; list = next)
		{
			next = list->next;
			free(list);
		}
	}

/* .import and .zimport */

	| IMPORT id_list
	{
		Symbol	sym;
		List	list, next;

	/* Import symbols during pass one, verify during pass two */

		for (list = &$2; list; list = list->next)
			switch (gPass)
			{
				case 1:
					Import(list->u.id);
					break;
				case 2:
					if ((sym = GetSym(list->u.id)))
						uerror("imported symbol \"%s\" is defined locally",
							Name(gNames, list->u.id));
					break;
				default:
					assert(0);
			}

	/* Free tail of list */

		for (list = $2.next; list; list = next)
		{
			next = list->next;
			free(list);
		}
	}
	| ZIMPORT id_list
	{
		Symbol	sym;
		List	list, next;

	/* Import symbols during pass one, verify during pass two */

		for (list = &$2; list; list = list->next)
			switch (gPass)
			{
				case 1:
					ZpImport(list->u.id);
					break;
				case 2:
					if ((sym = GetSym(list->u.id)))
						uerror("imported symbol \"%s\" is defined locally",
							Name(gNames, list->u.id));
					break;
				default:
					assert(0);
			}

	/* Free tail of list */

		for (list = $2.next; list; list = next)
		{
			next = list->next;
			free(list);
		}
	}

/* .global == .export */

	| GLOBAL id_list
	{
		Symbol	sym;
		List	list, next;

	/* Export symbols during pass two */

		if (gPass == 2)
			for (list = &$2; list; list = list->next)
				if ((sym = GetSym(list->u.id)))
					Export(sym);
				else
					uerror("undefined symbol \"%s\"",
						Name(gNames, list->u.id));

	/* Free tail of list */

		for (list = $2.next; list; list = next)
		{
			next = list->next;
			free(list);
		}
	}

/* .if .else .endif */

	| IF i_value
	{
		if (!ConstExpr($2))
			uerror("constant expression required");
		if (++gIfStackPtr >= MAX_IF_NESTING)
			cerror("\".if\" nesting too deep");
		gAssembleOn = 
		gIfStack[gIfStackPtr].assembling = ConstExprVal($2);
		gIfStack[gIfStackPtr].doing_else = FALSE;
		gIfLine = TRUE;
	}
	| ELSE
	{
		if (gIfStack[gIfStackPtr].doing_else)
			uerror("\".else\" without \".if\"");
		else
		{
			gAssembleOn = 
			gIfStack[gIfStackPtr].assembling =
				!gIfStack[gIfStackPtr].assembling;
			gIfStack[gIfStackPtr].doing_else = TRUE;
		}
	}
	| ENDIF
	{
		if (!gIfStackPtr)
			uerror("\".endif\" without \".if\"");
		else
		{
			gIfStackPtr--;
			gAssembleOn = gIfStack[gIfStackPtr].assembling;
		}
	}

/* unimplementeds */

	| BEGNN						{ uerror("pseudo-op unimplemented"); }
	| END						{ uerror("pseudo-op unimplemented"); }
	| MACRO	IDENT				{ uerror("pseudo-op unimplemented"); }
	| ENDM						{ uerror("pseudo-op unimplemented"); }
	;

i_value:
	const_int
	{
		$$.type = iSegRel;
		$$.u.segrel.segment = AbsSegment;
		$$.u.segrel.offset = $1;
	}
	| '[' i_value ']'
	{
		$$ = $2;
	}
	| i_value '?' i_value ':' i_value
	{
		if (!ConstExpr($1))
			uerror("constant expression required left of conditional");
		$$ = ConstExprVal($1) ? $3 : $5;
	}
	| '*'
	{
		$$.type = iSegRel;
		$$.u.segrel.segment = gSegment;
		$$.u.segrel.offset = gAddress[gSegment];
	}
	| IDENT
	{
		Symbol	sym = GetSym($1);

		switch (gPass)
		{
			case 1:
				if (sym)
					switch (sym->type)
					{
						case sSegRel:
							$$.type = iSegRel;
							$$.u.segrel = sym->u.segrel;
							break;
						case sSymOff:
							$$.type = iSymOff;
							$$.u.symoff = sym->u.symoff;
							break;
						default:
							assert(0);
					}
				else
					if (IsZpImport($1))
					{
						$$.type = iSymOff;
						$$.u.symoff.id = $1;
						$$.u.symoff.offset = 0;
					}
					else
						$$.type = iUnknown;
				break;

			case 2:
				if (sym)
					if (!sym->valid)
					{
						uerror("illegal forward reference to \"%s\"",
							Name(gNames, sym->id));
						$$.type = iSegRel;
						$$.u.segrel.segment = AbsSegment;
						$$.u.segrel.offset = 0;
					}
					else
						switch (sym->type)
						{
							case sSegRel:
								$$.type = iSegRel;
								$$.u.segrel = sym->u.segrel;
								break;
							case sSymOff:
								$$.type = iSymOff;
								$$.u.symoff = sym->u.symoff;
								break;
							default:
								assert(0);
						}
				else
				{
					$$.type = iSymOff;
					$$.u.symoff.id = $1;
					$$.u.symoff.offset = 0;
				}
				break;

			default:
				assert(0);
		}
	}

/*
 * Adding and subtracting IValues:
 * We allow constants to be added to & subtracted from
 * external symbols. That's why this is complicated.
 */

	| i_value '+' i_value
	{
		if ($1.type == iUnknown || $3.type == iUnknown)
			$$.type = iUnknown;
		else
			switch ($1.type)
			{
				case iSegRel:
					switch ($3.type)
					{
						case iSegRel:
							$$.type = iSegRel;
							if ($1.u.segrel.segment == AbsSegment)
								$$ = $3;
							else if ($3.u.segrel.segment == AbsSegment)
								$$ = $1;
							else
								uerror("illegal expression");
							$$.u.segrel.offset =
								$1.u.segrel.offset + $3.u.segrel.offset;
							break;
						case iSymOff:
							if ($1.u.segrel.segment != AbsSegment)
								uerror("illegal unresolved expression");
							$$ = $3;
							$$.u.symoff.offset += $1.u.segrel.offset;
							break;
						default:
							assert(0);
					}
					break;
				case iSymOff:
					switch ($3.type)
					{
						case iSegRel:
							if ($3.u.segrel.segment != AbsSegment)
								uerror("illegal unresolved expression");
							$$ = $1;
							$$.u.symoff.offset += $3.u.segrel.offset;
							break;
						case iSymOff:
							uerror("illegal unresolved expression");
							break;
					}
					break;
				default:
					assert(0);
			}
	}

	| i_value '-' i_value
	{
		if ($1.type == iUnknown || $3.type == iUnknown)
			$$.type = iUnknown;
		else
			switch ($3.type)
			{
				case iSegRel:
					switch ($1.type)
					{
						case iSegRel:
							if ($1.u.segrel.segment == $3.u.segrel.segment)
							{
								$$.type = iSegRel;
								$$.u.segrel.segment = AbsSegment;
								$$.u.segrel.offset =
									$1.u.segrel.offset - $3.u.segrel.offset;
							}
							else
							{
								if ($3.u.segrel.segment != AbsSegment)
									uerror("illegal expression");
								$$ = $1;
								$$.u.segrel.offset -= $3.u.segrel.offset;
							}
							break;
						case iSymOff:
							if ($3.u.segrel.segment != AbsSegment)
								uerror("illegal expression");
							$$ = $1;
							$$.u.symoff.offset -= $3.u.segrel.offset;
							break;
						default:
							assert(0);
					}
					break;
				case iSymOff:
					if ($1.type != iSymOff || $1.u.symoff.id != $3.u.symoff.id)
						uerror("illegal expression");
					$$.type = iSegRel;
					$$.u.segrel.segment = AbsSegment;
					$$.u.segrel.offset =
						$1.u.symoff.offset - $3.u.symoff.offset;
					break;
				default:
					assert(0);
			}
	}
	| '!' i_value					{ UnaryOp($2,$$,!); }
	| '~' i_value					{ UnaryOp($2,$$,~); }
	| i_value '*' i_value			{ BinaryOp($1,$3,$$,*); }
	| i_value '/' i_value			{ BinaryOp($1,$3,$$,/); }
	| i_value '%' i_value			{ BinaryOp($1,$3,$$,%); }
	| i_value LSH i_value			{ BinaryOp($1,$3,$$,<<); }
	| i_value RSH i_value			{ BinaryOp($1,$3,$$,>>); }
	| i_value LAND i_value			{ BinaryOp($1,$3,$$,&&); }
	| i_value LOR i_value			{ BinaryOp($1,$3,$$,||); }
	| i_value '&' i_value			{ BinaryOp($1,$3,$$,&); }
	| i_value '^' i_value			{ BinaryOp($1,$3,$$,^); }
	| i_value '|' i_value			{ BinaryOp($1,$3,$$,|); }
	| i_value EQU i_value			{ BinaryOp($1,$3,$$,==); }
	| i_value NEQ i_value			{ BinaryOp($1,$3,$$,!=); }
	| i_value '<' i_value			{ BinaryOp($1,$3,$$,<); }
	| i_value LE i_value			{ BinaryOp($1,$3,$$,<=); }
	| i_value '>' i_value			{ BinaryOp($1,$3,$$,>); }
	| i_value GE i_value			{ BinaryOp($1,$3,$$,>=); }
	;

const_int:
	INTLIT							{ $$ = $1; }
	| '+' INTLIT					{ $$ = $2; }
	| '-' INTLIT					{ $$ = -$2; }
	| CHARLIT						{ $$ = $1; }
	;

f_value:
	i_value
	{
		if (!ConstExpr($1))
			uerror("constant expression required");
		else
			$$ = (double) ConstExprVal($1);
	}
	| FLOATLIT						{ $$ = $1; }
	| '+' FLOATLIT					{ $$ = $2; }
	| '-' FLOATLIT					{ $$ = -$2; }
	;

t_value:
	TEXTLIT
	| TEXTLIT TEXTLIT
	{
		$$.chars = GetMem($$.len = $1.len + $2.len);
		memcpy($$.chars, $1.chars, $1.len);
		memcpy($$.chars + $1.len, $2.chars, $2.len);
		free($1.chars);
		free($2.chars);
	}
	;

/* Lists of things -- note these are backwards, reverse ### */

id_list:
	IDENT
	{
		$$.u.id = $1;
		$$.next = NULL;
	}
	| IDENT ',' id_list
	{
		$$.u.id = $1;
		$$.next = NewOpaque(List);
		$$.next->u.id = $3.u.id;
	 	$$.next->next = $3.next;
	}
	;

iv_list:
	i_value
	{
		$$.u.ivalue = $1;
		$$.next = NULL;
	}
	| i_value ',' iv_list
	{
		$$.u.ivalue = $1;
		$$.next = NewOpaque(List);
		$$.next->u.ivalue = $3.u.ivalue;
		$$.next->next = $3.next;
	}
	;

f_list:
	f_value
	{
		$$.u.fvalue = $1;
		$$.next = NULL;
	}
	| f_value ',' f_list
	{
		$$.u.fvalue = $1;
		$$.next = NewOpaque(List);
		$$.next->u.fvalue = $3.u.fvalue;
		$$.next->next = $3.next;
	}
	;


