/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char sccsid[] = "@(#)stkrval.c	5.1 (Berkeley) 6/5/85";
#endif not lint

#include "whoami.h"
#include "0.h"
#include "tree.h"
#include "opcode.h"
#include "objfmt.h"
#ifdef PC
#   include <pcc.h>
#endif PC
#include "tree_ty.h"

/*
 * stkrval Rvalue - an expression, and coerce it to be a stack quantity.
 *
 * Contype is the type that the caller would prefer, nand is important
 * if constant sets or constant strings are involved, the latter
 * because of string padding.
 */
/*
 * for the obj version, this is a copy of rvalue hacked to use fancy new
 * push-onto-stack-and-convert opcodes.
 * for the pc version, i just call rvalue and convert if i have to,
 * based on the return type of rvalue.
 */
struct nl *
stkrval(r, contype , required )
	register struct tnode *r;
	struct nl *contype;
	long	required;
{
	register struct nl *p;
	register struct nl *q;
	register char *cp, *cp1;
	register int c, w;
	struct tnode *pt;
	long l;
	union
	{
		double pdouble;
		long   plong[2];
	}f;

	if (r == TR_NIL)
		return (NLNIL);
	if (nowexp(r))
		return (NLNIL);
	/*
	 * The root of the tree tells us what sort of expression we have.
	 */
	switch (r->tag) {

	/*
	 * The constant nil
	 */
	case T_NIL:
#		ifdef OBJ
		    (void) put(2, O_CON14, 0);
#		endif OBJ
#		ifdef PC
		    putleaf( PCC_ICON , 0 , 0 , PCCT_INT , (char *) 0 );
#		endif PC
		return (nl+TNIL);

	case T_FCALL:
	case T_VAR:
		p = lookup(r->var_node.cptr);
		if (p == NLNIL || p->class == BADUSE)
			return (NLNIL);
		switch (p->class) {
		case VAR:
			/*
			 * if a variable is
			 * qualified then get
			 * the rvalue by a
			 * stklval and an ind.
			 */
			if (r->var_node.qual != TR_NIL)
				goto ind;
			q = p->type;
			if (q == NLNIL)
				return (NLNIL);
			if (classify(q) == TSTR)
				return(stklval(r, NOFLAGS));
#			ifdef OBJ
				return (stackRV(p));
#			endif OBJ
#			ifdef PC
			    q = rvalue( r , contype , (int) required );
			    if (isa(q, "sbci")) {
				sconv(p2type(q),PCCT_INT);
			    }
			    return q;
#			endif PC

		case WITHPTR:
		case REF:
			/*
			 * A stklval for these
			 * is actually what one
			 * might consider a rvalue.
			 */
ind:
			q = stklval(r, NOFLAGS);
			if (q == NLNIL)
				return (NLNIL);
			if (classify(q) == TSTR)
				return(q);
#			ifdef OBJ
			    w = width(q);
			    switch (w) {
				    case 8:
					    (void) put(1, O_IND8);
					    return(q);
				    case 4:
					    (void) put(1, O_IND4);
					    return(q);
				    case 2:
					    (void) put(1, O_IND24);
					    return(q);
				    case 1:
					    (void) put(1, O_IND14);
					    return(q);
				    default:
					    (void) put(2, O_IND, w);
					    return(q);
			    }
#			endif OBJ
#			ifdef PC
			    if ( required == RREQ ) {
				putop( PCCOM_UNARY PCC_MUL , p2type( q ) );
				if (isa(q,"sbci")) {
				    sconv(p2type(q),PCCT_INT);
				}
			    }
			    return q;
#			endif PC

		case CONST:
			if (r->var_node.qual != TR_NIL) {
				error("%s is a constant and cannot be qualified", r->var_node.cptr);
				return (NLNIL);
			}
			q = p->type;
			if (q == NLNIL)
				return (NLNIL);
			if (q == nl+TSTR) {
				/*
				 * Find the size of the string
				 * constant if needed.
				 */
				cp = (char *) p->ptr[0];
cstrng:
				cp1 = cp;
				for (c = 0; *cp++; c++)
					continue;
				w = c;
				if (contype != NIL && !opt('s')) {
					if (width(contype) < c && classify(contype) == TSTR) {
						error("Constant string too long");
						return (NLNIL);
					}
					w = width(contype);
				}
#				ifdef OBJ
				    (void) put(2, O_LVCON, lenstr(cp1, w - c));
				    putstr(cp1, w - c);
#				endif OBJ
#				ifdef PC
				    putCONG( cp1 , w , LREQ );
#				endif PC
				/*
				 * Define the string temporarily
				 * so later people can know its
				 * width.
				 * cleaned out by stat.
				 */
				q = defnl((char *) 0, STR, NLNIL, w);
				q->type = q;
				return (q);
			}
			if (q == nl+T1CHAR) {
#			    ifdef OBJ
				(void) put(2, O_CONC4, (int)p->value[0]);
#			    endif OBJ
#			    ifdef PC
				putleaf(PCC_ICON, p -> value[0], 0, PCCT_INT, 
						(char *) 0);
#			    endif PC
			    return(q);
			}
			/*
			 * Every other kind of constant here
			 */
#			ifdef OBJ
			    switch (width(q)) {
			    case 8:
#ifndef DEBUG
				    (void) put(2, O_CON8, p->real);
				    return(q);
#else
				    if (hp21mx) {
					    f.pdouble = p->real;
					    conv((int *) (&f.pdouble));
					    l = f.plong[1];
					    (void) put(2, O_CON4, l);
				    } else
					    (void) put(2, O_CON8, p->real);
				    return(q);
#endif
			    case 4:
				    (void) put(2, O_CON4, p->range[0]);
				    return(q);
			    case 2:
				    (void) put(2, O_CON24, (short)p->range[0]);
				    return(q);
			    case 1:
				    (void) put(2, O_CON14, p->value[0]);
				    return(q);
			    default:
				    panic("stkrval");
			    }
#			endif OBJ
#			ifdef PC
			    q = rvalue( r , contype , (int) required );
			    if (isa(q,"sbci")) {
				sconv(p2type(q),PCCT_INT);
			    }
			    return q;
#			endif PC

		case FUNC:
		case FFUNC:
			/*
			 * Function call
			 */
			pt = r->var_node.qual;
			if (pt != TR_NIL) {
				switch (pt->list_node.list->tag) {
				case T_PTR:
				case T_ARGL:
				case T_ARY:
				case T_FIELD:
					error("Can't qualify a function result value");
					return (NLNIL);
				}
			}
#			ifdef OBJ
			    q = p->type;
			    if (classify(q) == TSTR) {
				    c = width(q);
				    (void) put(2, O_LVCON, even(c+1));
				    putstr("", c);
				    (void) put(1, PTR_DUP);
				    p = funccod(r);
				    (void) put(2, O_AS, c);
				    return(p);
			    }
			    p = funccod(r);
			    if (width(p) <= 2)
				    (void) put(1, O_STOI);
#			endif OBJ
#			ifdef PC
			    p = pcfunccod( r );
			    if (isa(p,"sbci")) {
				sconv(p2type(p),PCCT_INT);
			    }
#			endif PC
			return (p);

		case TYPE:
			error("Type names (e.g. %s) allowed only in declarations", p->symbol);
			return (NLNIL);

		case PROC:
		case FPROC:
			error("Procedure %s found where expression required", p->symbol);
			return (NLNIL);
		default:
			panic("stkrvid");
		}
	case T_PLUS:
	case T_MINUS:
	case T_NOT:
	case T_AND:
	case T_OR:
	case T_DIVD:
	case T_MULT:
	case T_SUB:
	case T_ADD:
	case T_MOD:
	case T_DIV:
	case T_EQ:
	case T_NE:
	case T_GE:
	case T_LE:
	case T_GT:
	case T_LT:
	case T_IN:
		p = rvalue(r, contype , (int) required );
#		ifdef OBJ
		    if (width(p) <= 2)
			    (void) put(1, O_STOI);
#		endif OBJ
#		ifdef PC
		    if (isa(p,"sbci")) {
			sconv(p2type(p),PCCT_INT);
		    }
#		endif PC
		return (p);
	case T_CSET:
		p = rvalue(r, contype , (int) required );
		return (p);
	default:
		if (r->const_node.cptr == (char *) NIL)
			return (NLNIL);
		switch (r->tag) {
		default:
			panic("stkrval3");

		/*
		 * An octal number
		 */
		case T_BINT:
			f.pdouble = a8tol(r->const_node.cptr);
			goto conint;
	
		/*
		 * A decimal number
		 */
		case T_INT:
			f.pdouble = atof(r->const_node.cptr);
conint:
			if (f.pdouble > MAXINT || f.pdouble < MININT) {
				error("Constant too large for this implementation");
				return (NLNIL);
			}
			l = f.pdouble;
			if (bytes(l, l) <= 2) {
#			    ifdef OBJ
				(void) put(2, O_CON24, (short)l);
#			    endif OBJ
#			    ifdef PC
				putleaf( PCC_ICON , (short) l , 0 , PCCT_INT , 
						(char *) 0 );
#			    endif PC
				return(nl+T4INT);
			}
#			ifdef OBJ
			    (void) put(2, O_CON4, l); 
#			endif OBJ
#			ifdef PC
			    putleaf( PCC_ICON , (int) l , 0 , PCCT_INT , (char *) 0 );
#			endif PC
			return (nl+T4INT);
	
		/*
		 * A floating point number
		 */
		case T_FINT:
#		   	ifdef OBJ
			    (void) put(2, O_CON8, atof(r->const_node.cptr));
#			endif OBJ
#			ifdef PC
			    putCON8( atof( r->const_node.cptr ) );
#			endif PC
			return (nl+TDOUBLE);
	
		/*
		 * Constant strings.  Note that constant characters
		 * are constant strings of length one; there is
		 * no constant string of length one.
		 */
		case T_STRNG:
			cp = r->const_node.cptr;
			if (cp[1] == 0) {
#				ifdef OBJ
				    (void) put(2, O_CONC4, cp[0]);
#				endif OBJ
#				ifdef PC
				    putleaf( PCC_ICON , cp[0] , 0 , PCCT_INT , 
						(char *) 0 );
#				endif PC
				return(nl+T1CHAR);
			}
			goto cstrng;
		}
	
	}
}

#ifdef OBJ
/*
 * push a value onto the interpreter stack, longword aligned.
 */
struct nl 
*stackRV(p)
	struct nl *p;
{
	struct nl *q;
	int w, bn;

	q = p->type;
	if (q == NLNIL)
		return (NLNIL);
	bn = BLOCKNO(p->nl_block);
	w = width(q);
	switch (w) {
	case 8:
		(void) put(2, O_RV8 | bn << 8+INDX, (int)p->value[0]);
		break;
	case 4:
		(void) put(2, O_RV4 | bn << 8+INDX, (int)p->value[0]);
		break;
	case 2:
		(void) put(2, O_RV24 | bn << 8+INDX, (int)p->value[0]);
		break;
	case 1:
		(void) put(2, O_RV14 | bn << 8+INDX, (int)p->value[0]);
		break;
	default:
		(void) put(3, O_RV | bn << 8+INDX, (int)p->value[0], w);
		break;
	}
	return (q);
}
#endif OBJ
