File:  [Research Unix] / researchv10no / cmd / basic / bas / expr.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:35 2018 UTC (8 years, 1 month ago) by root
Branches: belllabs, MAIN
CVS tags: researchv10, HEAD
researchv10 Norman

#include <stdio.h>
#include "ctype.h"
#include "typedef.h"
#include "basic.h"
#include "tokens.h"

#define NOOP		0
#define TYPEMASK	07
#define min(a,b)	((a)<(b)?(a):(b))
#define prio(x) 	priority[-x]
#define between(a,b,c)	((a)<=(b)&&(b)<=(c))
#define FIRSTOP 	OR
#define LASTOP		PLUS
#define OPCOUNT 	20
#define MAXOP		10	
#define MAXDIGITS	64

static char	priority[OPCOUNT];
static char	priodefs[] = {
	20,	EXP,
	11,	MUL,	DIV,
	10,	PLUS,	MINUS,
	9,	EQ,	NE,	GE,	LE,	LT,	GT,
	7,	AND,
	6,	OR,
	0,	NOOP
	};

Stkptr	pushvar(), nextframe();
Symptr	getvar();
char	*allocstr(), *getsvar();
double	popfloat(), modf(), atof(), cvtnumber(), exp(), log(), fabs(), floor();


/*
 * initprio --- initialize the operator-priority map
 */

initprio()
{
	register int	pri;
	register char	*p;

	for (p = priodefs; p < priodefs + sizeof priodefs; p++)
		if (*p >= 0)
			pri = *p;
		else
			prio(*p) = pri;
	if (tflg)
		for (pri = FIRSTOP; pri <= LASTOP; pri++)
			fprintf(stderr, "priority[%d] == %d\n", pri, prio(pri));
}


/*
 * expr --- interpret an expression, push result onto stack
 */

expr()
{
	register int	op, evop;
	register char	*opptr;
	char		opstk[MAXOP];
	int		c;

#define getop() 	(between(FIRSTOP,*inptr,LASTOP)? *inptr++ : 0)
#define getitem()	{\
			c = *inptr;\
			if (isalpha(c))\
				pushvar();\
			else if (isdigit(c))\
				pushfloat(cvtnumber(&inptr, MAXINT));\
			else\
				item();\
			}

	getitem();
	if ((op = getop()) == 0)
		return;
	opptr = opstk;
	*opptr++ = NOOP;
	for (;;)
		if (prio(opptr[-1]) >= prio(op)) {
			evop = *--opptr;
			if (evop == NOOP)
				return;
			eval(evop);
			}
		else {
			getitem();
			*opptr++ = op;
			if (opptr > opstk + MAXOP)
				err("expression too complex");
			op = getop();
			}
}


/*
 * eval --- evaluate the specified operator using stacked operands
 */

eval(op)
{
	double		fp1, fp2;
	register Stkptr s;

	s = (Stkptr)stkptr;
	if (s->k_type == STRINGEXPR) {
		streval(op);
		return;
		}
	fp2 = popfloat();
	s = (Stkptr)stkptr;
	if (s->k_type != FLOATEXPR)
		badstk(FLOATEXPR);
	fp1 = s->k_un.k_dbl;

	switch(op) {
	case PLUS:
		fp1 += fp2;
		break;
	case MINUS:
		fp1 -= fp2;
		break;
	case MUL:
		fp1 *= fp2;
		break;
	case DIV:
		fp1 /= fp2;
		break;
	case EXP:
		if (fp1 > 0)
			fp1 = exp(log(fp1) * fp2);
		else if (fp1 != 0 && fp2 == 0)
			fp1 = 1;
		else if (fp1 == 0 && fp2 != 0)
			fp1 = 0;
		else if (fp1 < 0 && (fp2 - 2*floor(fp2 / 2.)) == 0)
			fp1 = exp(log(fabs(fp1)) * fp2);
		else if (fp1 < 0 && (fp2 - floor(fp2)) == 0)
			fp1 = -exp(log(fabs(fp1)) * fp2);
		else {fp1 = 0;
		fprintf(stderr, "0 to the 0 power and negative numbers");
		fprintf(stderr, " to noninteger powers \n");
		fprintf(stderr, " can not be calculated. 0 was returned.\n");
			};
		break;
	case GT:
		fp1 = (fp1 > fp2);
		break;
	case LT:
		fp1 = (fp1 < fp2);
		break;
	case LE:
		fp1 = (fp1 <= fp2);
		break;
	case GE:
		fp1 = (fp1 >= fp2);
		break;
	case EQ:
		fp1 = (fp1 == fp2);
		break;
	case NE:
		fp1 = (fp1 != fp2);
		break;
	case OR:
		fp1 = ((fp1 != 0) || (fp2 != 0));
		break;
	case AND:
		fp1 = ((fp1 != 0) && (fp2 != 0));
		break;
	default:
		err("bad operator");
		}
	s->k_un.k_dbl = fp1;
}


/*
 * streval --- evaluate an operator with string operands
 */

streval(op)
{
	register int	i;
	char		*ptr1, *ptr2;
	int		len1, len2;

	if (op == PLUS) {
		concat();
		return;
		}
	popstring(&ptr2, &len2);
	popstring(&ptr1, &len1);
	i = strcmpn(ptr1, len1, ptr2, len2);

	switch(op) {
	case GT:
		i = (i > 0);
		break;
	case LT:
		i = (i < 0);
		break;
	case LE:
		i = (i <= 0);
		break;
	case GE:
		i = (i >= 0);
		break;
	case EQ:
		i = (i == 0);
		break;
	case NE:
		i = (i != 0);
		break;
	default:
		err("bad operator");
		}
	pushint(i);
}


/*
 * strcmpn --- compare fixed length strings
 */

strcmpn(ptr1, len1, ptr2, len2)
char	*ptr1, *ptr2;
{
	register int	l;
	register char	*p1, *p2;

	l = min(len1, len2);
	len1 -= l;
	len2 -= l;
	p1 = ptr1;
	p2 = ptr2;
	while (l > 0 && *p1++ == *p2++)
		--l;
	if (l != 0)
		return(*--p1 - *--p2);
	while (len1 > 0) {	/* string 1 longer */
		if (*p1++ != ' ')
			return(*--p1 - ' ');
		--len1;
		}
	while (len2 > 0) {	/* string 2 longer */
		if (*p2++ != ' ')
			return(' ' - *--p2);
		--len2;
		}
	return(0);		/* strings are equal */
}


/*
 * item --- interpret a basic expression element
 */

item()
{
	register Stkptr s;
	register int	c;
	
	switch((c = *inptr++)) {

	case FN:	/* function call */
		--inptr;	/* back up to FN token */
		fn();
		break;
	case PLUS:	/* unary + */
		item();
		break;
	case MINUS:	/* unary - */
		item();
		s = (Stkptr)stkptr;
		if (s->k_type != FLOATEXPR)
			err("float required");
		s->k_un.k_dbl = -s->k_un.k_dbl;
		break;
	case LPAR:	/* parenthesized expr */
		expr();
		expectc(RPAR);
		break;
	case QUOTE:	/* string constant */
	case PRIME:
		strconst(c);
		break;
	default:	/* float constant, variable, or builtin func */
		--inptr;
		if (isdigit(c) || c == '.')
			pushfloat(cvtnumber(&inptr, MAXINT));
		else if (isalpha(c))
			pushvar();
		else if (function())
			;
		else
			err("bad operand");
		}
}


/*
 * cvtnumber --- convert a string to floating point
 */

double cvtnumber(ptr, len)
char		**ptr;
register int	len;
{
	register char	*n, *p;
	char		numbuff[MAXDIGITS];
	double		f;
	
	p = *ptr;
	n = numbuff;
	if (*p == '+') {
		++p;
		--len;
		}
	else if (*p == '-') {
		*n++ = *p++;
		--len;
		}
	for (; isdigit(*p) || *p == '.' || *p == 'e'; ) {
		if (n >= &numbuff[MAXDIGITS-1]) {
			*ptr = p;
			err("too many digits");
			}
		*n++ = *p++;
		if (--len <= 0)
			break;
		}
	*n = 0;
	f = atof(numbuff);
	*ptr = p;
	return(f);
}


/*
 * strconst --- interpret a string constant in an expression
 */

strconst(c)
{
	Stkfr		s;
	
	s.k_un.k_str.s_ptr = inptr;
	while (*inptr && *inptr != c)
		++inptr;
	s.k_un.k_str.s_len = inptr - s.k_un.k_str.s_ptr;
	s.k_len = STRFRLEN;
	s.k_type = STRINGEXPR;
	push(&s);
	if (*inptr == c)
		++inptr;
}


/*
 * badtype --- report a data type error
 */

badtype()
{

	err("bad type");
}


/*
 * pushvar --- push the value of a variable onto the stack
 */

Stkptr pushvar()
{
	register char	*s;
	register int	i;
	int		type;
	
	s = getsvar(&type);
	
	switch(type) {
	case STRING:
		pushstring(((String *)s)->s_ptr, ((String *)s)->s_len);
		break;
	case INT:
		pushint(*(int *)s);
		break;
	case FLOAT:
		if (SINGLE)
			pushfloat(*(float *)s);
		else
			pushfloat(*(double *)s);
		break;
	default:
		err("value expected");
		}
	return((Stkptr)stkptr);
}


/*
 * getsc --- convert multi-dimensional subscript to single-dimensional
 */

getsc(v)
register Symptr v;
{
	register int	i, j, n;
	
	if (nsubs != v->v_nsubs)
		err("wrong number of subscripts");
	for (j = 0, n = 0;; ) {
		i = subsc[j];
		if (i < 1 || i > v->v_un.v_vec.v_subsc[j])
			err("subscript %d out of range (%d)", j + 1, i);
		n += i - 1;
		if (++j >= nsubs)
			break;
		n *= v->v_un.v_vec.v_subsc[j];
		}
	return(n);
}


/*
 * intvalued --- determine if a floating point number is integral
 */

intvalued(f)
double	f;
{
	double	ipart;

	return(modf(f, &ipart) == 0.0);
}


/*
 * let --- interpret a LET statement
 */

let()
{
	register Stkptr s;
	register char	*v;
	register int	i;
	int		type, stype;
	

	v = getsvar(&type);	/* v points to value in variable */

	expectc(EQ);
	expr();
	s = (Stkptr)stkptr;	/* get the expression */

	stype = s->k_type & TYPEMASK;

	if (stype != type && (stype == STRING || type == STRING))
		mixed();

	switch(type) {
		case FLOAT:
			if (SINGLE)
				*(float *)v = popfloat();
			else
				*(double *)v = popfloat();
			break;
		case INT:
			*(int *)v = popint();
			/* pushfloat(*(double *)v); */
			break;
		case STRING:
			storestring(v);
			break;
		default:
			err("invalid variable");
		}

}


/*
 * cvt --- convert value at top of stack to type "type"
 */

cvt(type)
{
	register Stkptr s;
	register int	stype;
	
	s = (Stkptr)stkptr;
	type &= TYPEMASK;
	stype = s->k_type & TYPEMASK;
	if (stype == type)
		return;
	switch(type) {
	case FLOAT:
		if (stype == INT) {
			pushfloat((double)popint());
			return;
			}
		break;
	case INT:
		if (stype == FLOAT) {
			pushint((int)popfloat());
			return;
			}
		}
	err("invalid type conversion");
}


/*
 * concat --- concatenate the strings at the top of the stack
 *	      don't pop the strings until they have been copied
 *	      to their new location in case garbage collection
 *	      takes place during allocation
 */

concat()
{
	register Stkptr s, p;
	register char	*q;
	int		slen, plen;
	
	s = (Stkptr)stkptr;
	slen = s->k_un.k_str.s_len;
	p = nextframe(s);
	plen = p->k_un.k_str.s_len;
	if (p->k_type != STRINGEXPR)
		mixed();
	if (slen + plen > MAXSTRING)
		err("string too long");
	q = allocstr(NULL, slen + plen, 0);
	move(plen, p->k_un.k_str.s_ptr, q);
	move(slen, s->k_un.k_str.s_ptr, q + plen);
	pop(STRINGEXPR);		/* get rid of topmost string */
	p->k_un.k_str.s_ptr = q;	/* replace the other */
	p->k_un.k_str.s_len += slen;
}


/*
 * getsvar --- return pointer to value of a variable
 *	       return its type in "type"
 */

char *getsvar(type)
int	*type;
{
	register char	*p;
	register Symptr v;
	register int	i;
	
	v = getvar(type, NO);
	if (nsubs == 0)
		return((char *)&v->v_un);	/* not subscripted */
	i = getsc(v);		/* get and check subscript */
	switch(*type) {
	case FLOAT:
		if (SINGLE)
			p = (char *)&v->v_un.v_vec.v_vecun.v_fltvec[i];
		else
			p = (char *)&v->v_un.v_vec.v_vecun.v_dblvec[i];
		break;
	case INT:
		p = (char *)&v->v_un.v_vec.v_vecun.v_intvec[i];
		break;
	case STRING:
		p = (char *)&v->v_un.v_vec.v_vecun.v_strvec[i];
		break;
	default:
		badtype();
		}
	return(p);
}


/*
 * mixed --- report mixed data mode error
 */

mixed()
{

	err("mixed modes");
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.