#ident "$Header: expr.c 1.2 88/06/28 $"

/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#include "crash.h"
#ifdef CROSS
#include "/usr/include/setjmp.h"
#else
#include <setjmp.h>
#endif

#include <sys/types.h>
#include <string.h>

#define error(s) longjmp(jmp, s)
static jmp_buf jmp;
char *err_atoi;

static char *str;
static int nbkt, nbrc;
static struct val term(), lval(), qqqq(), factor(), _expr(), getnum(), getsym();
struct val dot;

#define	ID(c)	(isalpha(c) || (c) == '_' || isdigit(c))

expr()
{
	register char *s;
	int n;

	s = token();
	if (debug[15]) {
	    if (s == NULL)
		printf("expr:1: s = token() returns NULL, replacing s with \".\"\n");
	    else
		printf("expr:1: s = token() returns '%s'\n", s);
	}
	if (s == NULL)
		s = ".";
	n = atoi(s);
	if (debug[15]) {
	    if (err_atoi)
		printf("expr:2: atoi() returns error\n");
	    else
		printf("expr:2: atoi() returns 0x%x\n", n);
	}
	if ( err_atoi )
		printf("'%s', %s?\n", s, err_atoi);
	else {
		printf("= %#x\n", n);
		if (dot.v_sdb.sdb_name)
			printf("(sdb info) name '%s', type '%#x', desc '%#x', offset '%#x', size '%#x'\n", 
			  dot.v_sdb.sdb_name, dot.v_sdb.sdb_type, dot.v_sdb.sdb_desc, dot.v_sdb.sdb_soff, dot.v_sdb.sdb_size);
	}
}

set()
{
	register char *s, *p;
	register n;
	register struct set_val *v;

	s = token();
	if (s == NULL) {
		for (v = &set_val; v->sv_name; v = v->sv_next)
			printf("%s\tvalue '%#x', type '%#x', desc '%#x', offset '%#x', size '%#x'\n",
				v->sv_name, v->sv_val.v_value, 
				v->sv_val.v_sdb.sdb_type, v->sv_val.v_sdb.sdb_desc,
				v->sv_val.v_sdb.sdb_soff, v->sv_val.v_sdb.sdb_size);
		return;
	}
	if ((p=strchr(s, '=')) == NULL) {
		printf("'%s', missing '='\n", s);
		return;
	}
	*p = 0;
	if (isalpha(*s) == 0 && *s != '_') {
		printf("'%s', illegal lhs for a token\n", s);
		return;
	}
	if (searchbynam(s) || searchsdb(s)) {
		printf("'%s' already exists\n", s);
		return;
	}
	for (p=s+1; *p; p++) {
		if ( ID(*p) == 0 ) {
			printf("'%s', illegal lhs for token\n", s);
			return;
		}
	}
	(void) atoi(p+1);	/* void cause value stored in dot */
	if ( err_atoi ) {
		printf("'%s', %s?\n", p+1, err_atoi);
		return;
	}
	add_value(s);
}

struct set_val set_val;

add_value(s)
	char *s;
{
	register struct set_val *v;

	v = searchsetval(s);
	if (v) {  /* already exists */
		v->sv_val = dot;
		return;
	}
	/*
	 * Name not in list, so add it
	 */
	for (v = &set_val; v->sv_name; v = v->sv_next)
		/* void */;
	v->sv_next = (struct set_val *) malloc(sizeof (struct set_val));
	memset(v->sv_next, 0, sizeof (struct set_val));
	v->sv_name = malloc( strlen(s) + 1 );
	strcpy(v->sv_name, s);
	v->sv_val = dot;
	return;
}

struct set_val *
searchsetval(s)
	register char *s;
{
	register struct set_val *v;

	for (v = &set_val; v->sv_name; v = v->sv_next)
		if (strcmp(s, v->sv_name) == 0)
			return v;
	return NULL;
}

atoi(s)
char *s;
{
	char *ret;
	struct val v;

	str = s;
	nbkt = nbrc = 0;
	err_atoi = 0;
	if (debug[15])
	    printf("atoi:1: set str = '%s'\n", s);
	if((ret = (char *)setjmp(jmp)) == 0) {
		v = _expr();
		if( nbkt == 0 && nbrc == 0) {
			dot = v;
			return v.v_value;	/* normal return */
		}
		ret = "unbalanced parens";
		/* and fall into ... */
	}
	err_atoi = ret;
	return 0;	/* error */
}

static struct val _expr()
{ 
	struct val lhs, v;

	if (debug[15])
	    if (str == NULL)
	        printf("_expr:1: str= NULL\n");
	    else
	        printf("_expr:1: str= '%s'\n", str);
	lhs = factor();
	if (debug[15]) {
	    if (str == NULL)
	        printf("_expr:2: str= NULL, lhs=> ", str);
	    else
	        printf("_expr:2: str= '%s', lhs=> ", str);
	    show_val(&lhs);
	    printf("\n");
	}
	for(;;) switch (*str++) {
		case ' ':
		case '\t':
			continue;
		case '+':
			v = factor(); 
			lhs.v_value += v.v_value;
			if (debug[15]) {
			    printf("_expr:3: '+', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		case '-':
			v = factor(); 
			lhs.v_value -= v.v_value;
			if (debug[15]) {
			    printf("_expr:3: '-', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		case '&':
			v = factor(); 
			lhs.v_value &= v.v_value;
			if (debug[15]) {
			    printf("_expr:3: '&', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		case '|':
			v = factor();
			lhs.v_value |= v.v_value;
			if (debug[15]) {
			    printf("_expr:3: '|', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		default:
			str--;
			if (debug[15]) {
			    printf("_expr:3: default case, decremented str, = ");
			    if (str == NULL)
				printf("NULL\n");
			    else
				printf("'%s'\n", str);
			    printf("_expr:4: returning lhs=> ");
			    show_val(&lhs);
			    printf("\n");
			}
			return lhs;
	}
}

static struct val factor()
{
	struct val lhs, v;

	if (debug[15])
	    if (str == NULL)
	        printf("factor:1: str= NULL\n", str);
	    else
	        printf("factor:1: str= '%s'\n", str);
	lhs = term();
	if (debug[15]) {
	    if (str == NULL)
		printf("factor:2: str= NULL, lhs=> ");
	    else
	        printf("factor:2: str= '%s', lhs=> ", str);
	    show_val(&lhs);
	    printf("\n");
	}
	for(;;) switch( *str++ ) {
		case ' ':
		case '\t':
			continue;
		case '*':
			v = term();
			lhs.v_value *= v.v_value;
			if (debug[15]) {
			    printf("factor:3: '*', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		case '/':
			v = term();
			lhs.v_value /= v.v_value;
			if (debug[15]) {
			    printf("factor:3: '/', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		case '%':
			v = term();
			lhs.v_value %= v.v_value;
			if (debug[15]) {
			    printf("factor:3: '%', v.value= 0x%x, new lhs val = 0x%x\n",
				v.v_value, lhs.v_value);
			}
			continue;
		default:
			str--;
			if (debug[15]) {
			    printf("factor:3: default case, decremented str, = ");
			    if (str == NULL)
				printf("NULL\n");
			    else
				printf("'%s'\n", str);
			    printf("factor:4: returning lhs=> ");
			    show_val(&lhs);
			    printf("\n");
			}
			return lhs;
	}
}

static struct val term()
{
	struct val v;

	if (debug[15])
	    if (str == NULL)
	        printf("term:1: str= NULL\n", str);
	    else
	        printf("term:1: str= '%s'\n", str);
	for(;;) switch ( *str++ ) {
		case ' ':
		case '\t':
			continue;
		case '.':
			if (debug[15]) {
			    printf("term:2: '.', returning dot, => ");
			    show_val(&dot);
			    printf("\n");
			}
			return dot;
		case '*':
			if (debug[15]) {
			    printf("term:2: '*', indirecting though addr\n");
			}
			v = term();
			if (debug[15]) {
			    printf("term:3: address computed as 0x%x\n", v.v_value);
			}
			readv(v.v_value, &v.v_value, sizeof (int *));
			if (debug[15]) {
			    printf("term:4: indirection gives value 0x%x\n", v.v_value);
			}
			return v;
		case '-':
			if (debug[15]) {
			    printf("term:2: '-', returning neg value\n");
			}
			v = term();
			if (debug[15]) {
			    printf("term:3: value computed as 0x%x = %d\n",
			        v.v_value, v.v_value);
			}
			v.v_value = -v.v_value;
			if (debug[15]) {
			    printf("term:4: returning neg as 0x%x = %d\n",
			        v.v_value, v.v_value);
			}
			return v;
		case '~':
			if (debug[15]) {
			    printf("term:2: '~', returning complement value\n");
			}
			v = term();
			if (debug[15]) {
			    printf("term:3: value computed as 0x%x\n", v.v_value);
			}
			v.v_value = ~v.v_value;
			if (debug[15]) {
			    printf("term:4: returning complement as 0x%x\n", v.v_value);
			}
			return v;
		case '(':
			nbkt++;
			if (debug[15]) {
			    printf("term:2: '(', returning expr\n");
			}
			v = _expr();
			if (*str!=')')
				error("unbalanced parens");
			else { 
				str++;
				nbkt--;
				if (debug[15]) {
				    printf("term:3: returning v=> ");
				    show_val(&v);
				    if (str == NULL)
					printf(", str= NULL\n");
				    else
					printf(", str= '%s'\n", str);
				}
				return v;
			}
		default:
			str--;
			if (debug[15]) {
			    printf("term:3: default case, decremented str, = ");
			    if (str == NULL)
				printf("NULL\n");
			    else
				printf("'%s'\n", str);
			}
			if (0) ;	/* assembler bug workaround! */
			return qqqq();
	}
}

static struct val qqqq()
{
	struct val lhs, v;

	if (debug[15])
	    if (str == NULL)
	        printf("qqqq:1: str= NULL\n", str);
	    else
	        printf("qqqq:1: str= '%s'\n", str);
	lhs = lval();
	if (debug[15]) {
	    if (str == NULL)
		printf("qqqq:2: str= NULL, lhs=> ");
	    else
	        printf("qqqq:2: str= '%s', lhs=> ", str);
	    show_val(&lhs);
	    printf("\n");
	}

	for (;;) switch( *str++ ) {
		 case ' ':
		 case '\t':
			if (debug[15])
			    printf("qqqq:3: case ' ' or '\t', continuing\n");
			continue;
		case '[':
			nbrc++;
			if (debug[15])
			    printf("qqqq:3: '[', caling _expr\n");
			v = _expr();
			if (debug[15]) {
			    printf("qqqq:4: expr returns v=> ");
			    show_val(&v);
			    if (str == NULL)
				printf(", str= NULL\n");
			    else
				printf(", str= '%s'\n", str);
			}
			if (*str!=']')
				error("unbalanced brackets");
			else {
				str++;
				nbrc--;
				if (debug[15]) {
				    printf("qqqq:4: adding \"lhs.v_sdb.sdb_size * v.v_value\" to lhs.v_value\n");
				    printf("   lhs=> ");
				    show_val(&lhs);
				    printf("\n    v=> ");
				    show_val(&v);
				    printf("\n");
				}
				lhs.v_value += lhs.v_sdb.sdb_size * v.v_value;
				if (debug[15]) {
				    printf("qqqq:5: set lhs=> ");
				    show_val(&lhs);
				    printf("\n");
				}
				continue;
			}
		 case '.':
			if (debug[15])
			    printf("qqqq:3: '.', calling lval\n");
			v = lval();
			if (debug[15]) {
			    printf("qqqq:4: v.v_value = lhs.v_value + v.v+sdb.sdb_soff\n");
			    printf("   lhs=>");
			    show_val(&lhs);
			    printf("\n    v=>");
			    show_val(&v);
			    printf("\n");
			}
			v.v_value = lhs.v_value + v.v_sdb.sdb_soff;
			lhs = v;
			if (debug[15]) {
			    printf("qqqq:5: set lhs=> ");
			    show_val(&lhs);
			    printf("\n");
			}
			continue;
		case '-':
			if( *str == '>' ) {
				if (debug[15]) {
				    printf("qqqq:3: '->', calling lval\n");
				}
				str++;
				v = lval();
				if (debug[15]) {
				    printf("qqqq:4: v.v_value = lhs.v_value + v.v_sdb.sdb_soff\n");
				    printf("   lhs=> ");
				    show_val(&lhs);
				    printf("\n    v=> ");
				    show_val(&v);
				    printf("n");
				}
				v.v_value = lhs.v_value + v.v_sdb.sdb_soff;
				lhs = v;
				if (debug[15]) {
			    	    printf("qqqq:5: set lhs=> ");
			    	    show_val(&lhs);
			    	    printf("\n");
				}
				continue;
			}
			if (debug[15])
			    printf("qqqq:3: '-', falls into default\n");
		default:
			str--;
			if (debug[15]) {
			    printf("qqqq:3: default case, decremented str, = ");
			    if (str == NULL)
				printf("NULL\n");
			    else
				printf("'%s'\n", str);
			    printf("qqqq:4: returning lhs=> ");
			    show_val(&lhs);
			    printf("\n");
			}
			return lhs;
	}
}

static struct val lval()
{
	if( isdigit(*str) )
		return getnum();
	if( isalpha(*str) || *str == '_')
		return getsym();
	error("badly formed expression");
}


static struct val getsym()
{
	char symbol[ MAXSTRLEN ], *p = symbol;
	struct val v;

	while (ID(*str) && p < &symbol[ MAXSTRLEN -1])
		*p++ = *str++;
	*p = '\0';
	v.v_value = search(symbol);
	if ( err_search )
		error(symbol);
	v.v_sdb = sdbinfo;
	if (xdebug)  printf("\n..getsym('%s'): value '%#x', size '%#x', type '%#x', desc '%#x', soff '%#x'..\n",
		    symbol, v.v_value, v.v_sdb.sdb_size, v.v_sdb.sdb_type, v.v_sdb.sdb_desc, v.v_sdb.sdb_soff);
	/* 
	 * if basic type is a pointer, must de-reference it now
	 */
	if (desc(&v.v_sdb.sdb_desc) == PCC_Q_POINTER && searchsetval(symbol) == NULL) {
		readv(v.v_value, &v.v_value, sizeof (int *));
		if (xdebug) printf("de-reference pointer to '%#x'\n", v.v_value);
	}
	return v;
}

static struct val getnum()
{
	register int c;
	register unsigned int n = 0;
	struct val v;

	c = *str++;
	if( (c=='0') && ( (*str=='x') || (*str=='X') ) ) {
		str++;
		while( isxdigit( c = *str++ ) ) {
			if( isdigit(c) )
				c = c - '0';
			else if( isupper(c) )
				c = c - 'A' + 10;
			else
				c = c - 'a' + 10;
			n = n * 16 + c;
		}
	} else for (n =  c - '0'; isdigit(c = *str++); ) {
		n = n * 10 + c - '0';
	}
	str--;
	v.v_value = n;
	memset(&v.v_sdb, 0, sizeof (struct sdb));
	v.v_sdb.sdb_size = sizeof (int);
	return v;
}

show_val(v)
	struct val *v;
{
	printf(" val= 0x%x", v->v_value);
	if (v->v_sdb.sdb_name == NULL)
		printf(" sdb_name= NULL");
	else {
		printf(" sdb_name= '%s'", v->v_sdb.sdb_name);
		printf(" sdb_soff= %d",   v->v_sdb.sdb_soff);
		printf(" sdb_size= 0x%x", v->v_sdb.sdb_size);
	}
}
