/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
#include "bem.h"

#ifndef NORSCID
static char rcs_id[] = "$Header$" ;
#endif


/* Here you find all routines to evaluate expressions and
   generate code for assignment statements
*/

exprtype(ltype,rtype)
int	ltype,rtype;
{
	/* determine the result type of an expression */
	if( ltype== STRINGTYPE || rtype==STRINGTYPE)
	{
		if( ltype!=rtype)
			error("type conflict, string expected");
		return( STRINGTYPE);
	}
	/* take maximum */
	if( ltype<rtype) return(rtype);
	return(ltype);
}

conversion(oldtype,newtype)
int oldtype,newtype;
{
	/* the value on top of the stack should be converted */
	if( oldtype==newtype) return;
	switch( oldtype)
	{
	case INTTYPE:
		if( newtype==FLOATTYPE || newtype==DOUBLETYPE)
		{
			emcode("loc",EMINTSIZE);
			emcode("loc",EMFLTSIZE);
			emcode("cif","");
		}else{
			if(debug) 
				printf("type n=%d o=%d\n",newtype,oldtype);
			error("conversion error");
		}
		break;
	case FLOATTYPE:
	case DOUBLETYPE:
		if( newtype==INTTYPE)
		{
			/* rounded ! */
			emcode("cal","$_cint");
			emcode("asp",EMFLTSIZE);
			emcode("lfr",EMINTSIZE);
			break;
		}else
		if( newtype== FLOATTYPE || newtype==DOUBLETYPE)
			break;
	default:
		if(debug) 
			printf("type n=%d o=%d\n",newtype,oldtype);
		error("conversion error");
	}
}
extraconvert(oldtype,newtype,topstack)
int oldtype,newtype,topstack;
{
	/* the value below the top of the stack should be converted */
	if( oldtype==newtype ) return;
	if( debug) printf("extra convert %d %d %d\n",oldtype,newtype,topstack);
	/* save top in dummy */
	switch( topstack)
	{
	case INTTYPE:
		emcode("ste","dummy1");
		break;
	case FLOATTYPE:
	case DOUBLETYPE:
		/* rounded ! */
		emcode("lae","dummy1");
		emcode("sti",EMFLTSIZE);
		break;
	default:
		error("conversion error");
		return;
	}
	/* now its on top of the stack */
	conversion(oldtype,newtype);
	/* restore top */
	switch( topstack)
	{
	case INTTYPE:
		emcode("loe","dummy1");
		break;
	case FLOATTYPE:
	case DOUBLETYPE:
		/* rounded ! */
		emcode("lae","dummy1");
		emcode("loi",EMFLTSIZE);
	}
}

boolop(ltype,rtype,operator)
int	ltype,rtype,operator;
{
	if( operator != NOTSYM)
	{
		extraconvert(ltype,INTTYPE,rtype);
		conversion(rtype,INTTYPE);
	} else conversion(ltype,INTTYPE);
	switch( operator)
	{
	case NOTSYM:	emcode("com",EMINTSIZE); break;
	case ANDSYM:	emcode("and",EMINTSIZE); break;
	case ORSYM:	emcode("ior",EMINTSIZE); break;
	case XORSYM:	emcode("xor",EMINTSIZE); break;
	case EQVSYM:
		emcode("xor",EMINTSIZE);
		emcode("com",EMINTSIZE);
		break;
	case IMPSYM:
		/* implies */
		emcode("com",EMINTSIZE);
		emcode("and",EMINTSIZE);
		emcode("com",EMINTSIZE);
		break;
	default:	error("boolop:unexpected");
	}
	return(INTTYPE);
}
genbool(opcode)
char *opcode;
{
	int l1,l2;
	l1= genlabel();
	l2= genlabel();
	emcode(opcode,instrlabel(l1));
	emcode("loc",itoa(0));
	emcode("bra",instrlabel(l2));
	fprintf(Tmpfile,"%d\n",l1); emlinecount++;
	emcode("loc",itoa(-1));
	fprintf(Tmpfile,"%d\n",l2); emlinecount++;
}
relop( ltype,rtype,operator)
int	ltype,rtype,operator;
{
	int	result;
	if(debug) printf("relop %d %d op=%d\n",ltype,rtype,operator);
	result= exprtype(ltype,rtype);
	extraconvert(ltype,result,rtype);
	conversion(rtype,result);
	/* compare the objects */
	if( result== INTTYPE)
		emcode("cmi", EMINTSIZE);
	else
	if( result==FLOATTYPE || result==DOUBLETYPE)
		emcode("cmf",EMFLTSIZE);
	else
	if( result==STRINGTYPE)
	{
		emcode("cal","$_strcomp");
		emcode("asp",EMPTRSIZE);
		emcode("asp",EMPTRSIZE);
		emcode("lfr",EMINTSIZE);
	} else	error("relop:unexpected");
	/* handle the relational operators */
	switch(operator)
	{
	case '<':	genbool("zlt"); break;
	case '>':	genbool("zgt"); break;
	case '=':	genbool("zeq"); break;
	case NESYM:	genbool("zne"); break;
	case LESYM:	genbool("zle"); break;
	case GESYM:	genbool("zge"); break;
	default:	error("relop:unexpected operator");
	}
	return(INTTYPE);
}
plusmin(ltype,rtype,operator)
int	ltype,rtype,operator;
{
	int result;
	result= exprtype(ltype,rtype);

	if( result== STRINGTYPE)
	{
		if( operator== '+')
		{
			emcode("cal","$_concat");
			emcode("asp",EMPTRSIZE);
			emcode("asp",EMPTRSIZE);
			emcode("lfr",EMPTRSIZE);
		} else error("illegal operator");
	} else {
		extraconvert(ltype,result,rtype);
		conversion(rtype,result);
		if( result== INTTYPE)
		{
			if( operator=='+') 
				emcode("adi",EMINTSIZE);
			else	emcode("sbi",EMINTSIZE);
		} else{
			if( operator=='+') 
				emcode("adf",EMFLTSIZE);
			else	emcode("sbf",EMFLTSIZE);
		}
	}
	return(result);
}
muldiv(ltype,rtype,operator)
int	ltype,rtype,operator;
{
	int result;

	result= exprtype(ltype,rtype);
	if(operator==MODSYM || operator== '\\') result=INTTYPE;
	extraconvert(ltype,result,rtype);
	conversion(rtype,result);
	if( result== INTTYPE)
	{
		if( operator=='/') 
		{
			result= DOUBLETYPE;
			extraconvert(ltype,result,rtype);
			conversion(rtype,result);
			emcode("dvf",EMFLTSIZE);
		} else
		if( operator=='\\')
			emcode("dvi",EMINTSIZE);
		else
		if( operator=='*') 
			emcode("mli",EMINTSIZE);
		else	
		if( operator==MODSYM)
			emcode("rmi",EMINTSIZE);
		else	error("illegal operator");
	} else{
		if( operator=='/') 
			emcode("dvf",EMFLTSIZE);
		else
		if( operator=='*') 
			emcode("mlf",EMFLTSIZE);
		else	error("illegal operator");
	}
	return(result);
}
negate(type)
int type;
{
	switch(type)
	{
	case INTTYPE:
		emcode("ngi",EMINTSIZE); break;
	case DOUBLETYPE:
	case FLOATTYPE:
		emcode("ngf",EMFLTSIZE); break;
	default:
		error("Illegal operator");
	}
	return(type);
}
power(ltype,rtype)
int	ltype,rtype;
{
	extraconvert(ltype,DOUBLETYPE,rtype);
	conversion(rtype,DOUBLETYPE);
	emcode("cal","$_power");
	emcode("asp",EMFLTSIZE);
	emcode("asp",EMFLTSIZE);
	emcode("lfr",EMFLTSIZE);
	return(DOUBLETYPE);
}
char *typesize(ltype)
int ltype;
{
	switch( ltype)
	{
	case INTTYPE:
		return(EMINTSIZE);
	case FLOATTYPE:
	case DOUBLETYPE:
		return(EMFLTSIZE);
	case STRINGTYPE:
		return(EMPTRSIZE);
	default:
		error("typesize:unexpected");
		if(debug) printf("type received %d\n",ltype);
	}
	return(EMINTSIZE);
}
/*
loadptr(s)
Symbol *s;
{
	if( POINTERSIZE==WORDSIZE)
		fprintf(Tmpfile," loe l%d\n",s->symalias);
	else 
	if( POINTERSIZE== 2*WORDSIZE)
		fprintf(Tmpfile," lde l%d\n",s->symalias);
	else error("loadptr:unexpected pointersize");
}
*/
char *typestring(type)
int type;
{
	switch(type)
	{
	case INTTYPE:
		return(EMINTSIZE);
	case FLOATTYPE:
	case DOUBLETYPE:
		return(EMFLTSIZE);
	case STRINGTYPE:
		return(EMPTRSIZE);
	default:
		error("typestring: unexpected type");
	}
	return("0");
}
loadvar(type)
int type;
{
	/* load a simple variable  its address is on the stack*/
	emcode("loi",typestring(type));
}
loadint(value)
int value;
{
	emcode("loc",itoa(value));
	return(INTTYPE);
}
loaddbl(value)
double value;
{
	int index;
	index= genlabel();
	fprintf(emfile,"l%d\n bss 8,%fF8,1\n",index,value);
	emcode("lae",datalabel(index));
	emcode("loi",EMFLTSIZE);
	return(DOUBLETYPE);
}
loadstr(value)
int value;
{
	emcode("lae",datalabel(value));
}
loadaddr(s)
Symbol *s;
{
	extern Symbol *fcn;
	int i,j;

	if(debug) printf("load %s %d\n",s->symname,s->symtype);
	if( s->symalias>0)
		emcode("lae",datalabel(s->symalias));
	else{	
		j= -s->symalias;
		if(debug) printf("load parm %d\n",j);
		fprintf(Tmpfile," lal ");
		for(i=fcn->dimensions;i>j;i--)
			fprintf(Tmpfile,"%s+",typesize(fcn->dimlimit[i-1]));
		fprintf(Tmpfile,"0\n");
		emlinecount++;
		/*
		emcode("lal",datalabel(fcn->dimalias[-s->symalias]));
		*/
	}
	return(s->symtype);
}
assign(type,lt)
int type,lt;
{
	extern int e1,e2;
	conversion(lt,type);
	exchange(e1,e2);
	/* address is on stack already */
	emcode("sti",typestring(type) );
}
storevar(lab,type)
int lab,type;
{
	/*store value back */
	emcode("lae",datalabel(lab));
	emcode("sti",typestring(type));
}

/* maintain a stack of array references */
int	dimstk[MAXDIMENSIONS], dimtop= -1;
Symbol  *arraystk[MAXDIMENSIONS];

newarrayload(s)
Symbol *s;
{
	if( dimtop<MAXDIMENSIONS) dimtop++;
	if( s->dimensions==0)
	{
		s->dimensions=1;
		defarray(s);
	}
	dimstk[dimtop]= s->dimensions;
	arraystk[dimtop]= s;
	emcode("lae",datalabel(s->symalias));
}
endarrayload()
{
	return(arraystk[dimtop--]->symtype);
}
loadarray(type)
int	type;
{
	int	dim;
	Symbol	*s;

	if( dimtop<0 || dimtop>=MAXDIMENSIONS)
		fatal("too many nested array references");
	/* index expression is on top of stack */
	s=arraystk[dimtop];
	dim= dimstk[dimtop];
	if( dim==0)
	{
		error("too many indices");
		dimstk[dim--]=0;
		return;
	}
	conversion(type,INTTYPE);
	dim--;
	/* first check index range */
	fprintf(Tmpfile," lae r%d\n",s->dimalias[dim]);
	emlinecount++;
	emcode("rck",EMINTSIZE);
	emcode("lae",datalabel(s->dimalias[dim]));
	emcode("aar",EMINTSIZE);
	dimstk[dimtop]--;
}
storearray(type)
{
	/* used only in let statement */
	extern int e1,e2;
	exchange(e1,e2);
	emcode("sti",typestring(type));
}