/*
 * as82.c
 * Expressions.
 */
#include <stdio.h>
#include <string.h>
#include "asm.h"

#ifndef lint
static char
# ifdef __GNUC__
    __attribute__ ((unused))
# endif
*sccsid = "@(#)as82.c        1.2";
#endif

/* Private function prototypes */
static int getopr(void);
static int validop(int c);
static int term(int *vp);
static int getnum(char c);

/*
 * Read an expression.
 * Return its value.
 * All expressions are evaluated
 * left to right; parentheses
 * may be used to alter the order
 * of evaluation.
 */
int
expr()
{
	register int c;
	int l, r;

	if (! term(&l))
		return 0;
	while (validop(c = getopr())) {
		if (! term(&r)) {
			err('e');
			return l;
		}
		switch (c) {
		case '+':
			l += r;
			break;

		case '-':
			l -= r;
			break;

		case '*':
			l *= r;
			break;

		case '/':
			l /= r;
			break;

		case '&':
			l &= r;
			break;

		case '|':
			l |= r;
			break;

		case '^':
			l ^= r;
			break;

		case '%':
			l %= r;
			break;

		case '<':
			l <<= r;
			break;

		case '>':
			l >>= r;
			break;
		}
	}
	putback(c);
	return(l);
}

/*
 * instr( c,str)
 *
 * returns the index to locate c in str if c is in str.
 * if c is not in str then returns -1.
 */

static int
instr(char c, char *str)
{
	register char *lp;
	lp = str;
	while (*lp != '\0')
		if (*lp++ == c)
			return lp - str - 1;
	return -1;
}

/*
 * Check for valid arithmetic
 * operators.
 */
static int
validop(int c)
{
	return instr(c, "+-*/%&|<>^") >= 0;
}

/*
 * Get a term.
 * Store its value in the
 * indicated place.
 * Return true if a term is
 * found.
 * If no term is found no
 * characters are read and
 * false is returned.
 */

static int
term(int *vp)
{
	register struct sym *sp;
	register int c;
	char id[NCPS];

	c = getnb();
	if (digit(c)) {
		/* Number */
		*vp = getnum(c);
		return 1;
	}
	else if (alpha(c)) {
		/* ID */
		getid(c, id);
		if (strcmp(id, "not") == 0) { /* if the id is 'not' */
			term(vp);
			*vp = ~*vp;
		}
		else {
			sp = install(id, 'e');
			if (sp == NULL)
				err('U');
			else if (sp->s_type == S_UND)
				err('u');
			*vp = sp->s_value;
		}
		return 1;
	}
	else if (c == '-') {
		/* Unary ops. */
		if (! term(vp)) {
			err('e');
			return 0;
		}
		*vp = -*vp;
		return 1;		/* Originally missing --dt@hcc */
	}
	else if (c == '(') {
		/* Parentheses.  */
		*vp = expr();
		if (getnb() != ')')
			err('(');
		return 1;
	}
	else if (c == '\'') {
		/* Character constant. */
		*vp = getmap();
		if (getnb() != '\'') {
			putback(c);
			*vp = *vp + (getmap() << 8);
			if (getnb() != '\'')
				err('\'');
		}
		return 1;
	}
	else if (c == CDOT) {
		/* The current value of DOT */
		*vp = dot->s_value;
		return 1;
	}
	else {
		/* None. */
		putback(c);
		return 0;
	}
}

/*
 * getopr()
 *
 * Get a valid operator.
 *
 * Finds non-unix type operators and returns them as valid
 * Unix operators.
 *
 */
static int
getopr()
{
	char ch;
	char id[NCPS];
	int index;

	if (validop(ch = getnb()))
		return ch;
	if (alpha(ch))
		getid(ch, id);
	else
		return ch;
	if (strcmp(id, "and") == 0)
		return '&';
	else if (strcmp(id, "or") == 0)
		return '|';
	else if (strcmp(id, "xor") == 0)
		return '^';
	else if (strcmp(id, "mod") == 0)
		return '%';
	else if (strcmp(id, "shr") == 0)
		return '>';
	else if (strcmp(id, "shl") == 0)
		return'<';
	else
		for (index = strlen(id); index == 1; index--)
			putback(id[index]);
	return id[0];
}

static  char *hexstr = "0123456789ABCDEF";

static int
scnnumb(char *str, int radix)
{
	int v, d;
	v = 0;
	while (*str)
		if ((d = instr(*str++, hexstr)) >= 0 && d < radix)
			v = v * radix + d;
		else
			return (err( 'N' ), 0);
	return v;
}

/*
 * get a number
 */
static int
getnum(char c)
{
	static  char *radix = "BDHOQ";
	static int radv[]= { 10, 2, 10, 16, 8, 8 };
	char numbstr[16];
	char *np;

	np = numbstr;
	while (instr(ucase(c), hexstr) >= 0) {
		*np++ = ucase(c);
		c = getraw();
	}
	*np-- = '\0';
	if (instr(ucase(c), radix) < 0) {
		putback(c);                     /* if terminal not right */
		c = *np;                        /* then the previous is */
		if (instr(ucase(c), radix) >= 0)
			*np = '\0';
	}
	/*
	 * The following code uses the index into the radix array to
	 * calculate the correct index into radix value table and
	 * use the value found to determine the correct conversion
	 * routine.
	 *
	 * Think carefully before changing the following code.
	 *
	 */
	return scnnumb(numbstr, radv[1 + instr(ucase(c), radix)]);
}
