/*	SC	A Spreadsheet Calculator
 *		Expression interpreter and assorted support routines.
 *
 *		James Gosling, September 1982
 *
 */



#include "sc.h"
#include <stdio.h>

double eval(e)
register struct enode *e; {
    if (e==0) return 0;
    switch (e->op) {
	case '+':	return (eval(e->e.o.left) + eval(e->e.o.right));
	case '-':	return (eval(e->e.o.left) - eval(e->e.o.right));
	case '*':	return (eval(e->e.o.left) * eval(e->e.o.right));
	case '/':     {	double denom = eval (e->e.o.right);
			return denom ? eval(e->e.o.left) / denom : 0; }
	case '<':	return (eval(e->e.o.left) < eval(e->e.o.right));
	case '=':	return (eval(e->e.o.left) == eval(e->e.o.right));
	case '>':	return (eval(e->e.o.left) > eval(e->e.o.right));
	case '&':	return (eval(e->e.o.left) && eval(e->e.o.right));
	case '|':	return (eval(e->e.o.left) || eval(e->e.o.right));
	case '?':	return eval(e->e.o.left) ? eval(e->e.o.right->e.o.left)
						 : eval(e->e.o.right->e.o.right);
	case 'm':	return (-eval(e->e.o.right));
	case 'f':	return (eval(e->e.o.right));
	case '~':	return (!eval(e->e.o.right));
	case 'k':	return (e->e.k);
	case 'v':	return (e->e.v->v);
	case O_REDUCE('+'):
	    {	register r,c;
		register struct ent *p;
		register double v = 0;
		register maxr, maxc;
		register minr, minc;
		maxr = ((struct ent *) e->e.o.right) -> row;
		maxc = ((struct ent *) e->e.o.right) -> col;
		minr = ((struct ent *) e->e.o.left) -> row;
		minc = ((struct ent *) e->e.o.left) -> col;
		if (minr>maxr) r = maxr, maxr = minr, minr = r;
		if (minc>maxc) c = maxc, maxc = minc, minc = c;
		for (r = minr; r<=maxr; r++)
		    for (c = minc; c<=maxc; c++)
			if ((p = tbl[r][c]) && p->flags&is_valid)
			    v += p->v;
		return v;
	    }
    }
}

EvalAll () {
    register i,j;
    register struct ent *p;
    for (i=0; i<=maxrow; i++)
	for (j=0; j<=maxcol; j++)
	    if ((p=tbl[i][j]) && p->expr) {
		double v = eval (p->expr);
		if (v != p->v) {
		    p->v = v;
		    p->flags |= (is_changed|is_valid);
		}
	    }
}

struct enode *new(op,a1,a2)
struct enode *a1, *a2; {
    register struct enode *p = (struct enode *) malloc (sizeof (struct enode));
    p->op = op;
    switch (op) {
    case O_VAR: p->e.v = (struct ent *) a1; break;
    case O_CONST: p->e.k = *(double *)&a1;
    default: p->e.o.left = a1; p->e.o.right = a2;
    }
    return p;
}

let (v, e)
struct ent *v;
struct enode *e; {
    efree (v->expr);
    if (constant(e)) {
	v->v = eval(e);
	v->expr = 0;
	efree(e);
    } else
	v->expr = e;
    v->flags |= (is_changed|is_valid);
    changed++;
}

constant (e)
register struct enode *e; {
    return e==0 || e->op == O_CONST
	|| (e->op != O_VAR
	 && (e->op&~0177) != O_REDUCE(0)
	 && constant (e->e.o.left)
	 && constant(e->e.o.right));
}

efree (e)
register struct enode *e; {
    if (e) {
	if (e->op != O_VAR && e->op !=O_CONST) {
	    efree (e->e.o.left);
	    efree (e->e.o.right);
	}
	free (e);
    }
}

label (v, s, flushdir)
register struct ent *v;
register char *s; {
    if (v) {
	if (flushdir==0 && v->flags&is_valid) {
	    register struct ent *tv;
	    if (v->col>0 && ((tv=lookat(v->row,v->col-1))->flags&is_valid)==0)
		v = tv, flushdir = 1;
	    else if (((tv=lookat (v->row,v->col+1))->flags&is_valid)==0)
		v = tv, flushdir = -1;
	    else flushdir = -1;
	}
	if (v->label) free(v->label);
	if (s && s[0]) {
	    v->label = (char *) malloc (strlen(s)+1);
	    strcpy (v->label, s);
	} else v->label = 0;
	v->flags |= is_lchanged;
	if (flushdir<0) v->flags |= is_leftflush;
	else v->flags &= ~is_leftflush;
	FullUpdate++;
    }
}

decodev (v)
register struct ent *v; {
	if (v) sprintf (line+linelim,"r%dc%d",v->row,v->col);
	else sprintf (line+linelim,"VAR?");
	linelim += strlen (line+linelim);
}

decompile(e, priority)
register struct enode *e; {
    if (e) {
	int mypriority;
	switch (e->op) {
	default: mypriority = 99; break;
	case '?': mypriority = 1; break;
	case ':': mypriority = 2; break;
	case '|': mypriority = 3; break;
	case '&': mypriority = 4; break;
	case '<': case '=': case '>': mypriority = 6; break;
	case '+': case '-': mypriority = 8; break;
	case '*': case '/': mypriority = 10; break;
	}
	if (mypriority<priority) line[linelim++] = '(';
	switch (e->op) {
	case 'f':	{   register char *s;
			    for (s="fixed "; line[linelim++] = *s++;);
			    linelim--;
			    decompile (e->e.o.right, 30);
			    break;
			}
	case 'm':	line[linelim++] = '-';
			decompile (e->e.o.right, 30);
			break;
	case '~':	line[linelim++] = '~';
			decompile (e->e.o.right, 30);
			break;
	case 'v':	decodev (e->e.v);
			break;
	case 'k':	sprintf (line+linelim,"%g",e->e.k);
			linelim += strlen (line+linelim);
			break;
	case O_REDUCE('+'):
	case O_REDUCE('*'):
			line[linelim++] = e->op&0177;
			line[linelim++] = '/';
			decodev (e->e.o.left);
			line[linelim++] = ':';
			decodev (e->e.o.right);
			break;
	default:	decompile (e->e.o.left, mypriority);
			line[linelim++] = e->op;
			decompile (e->e.o.right, mypriority+1);
			break;
	}
	if (mypriority<priority) line[linelim++] = ')';
    } else line[linelim++] = '?';
}

editv (row, col) {
    register struct ent *p;
    sprintf (line, "let r%dc%d = ", row, col);
    linelim = strlen(line);
    p = lookat (row, col);
    if (p->flags&is_valid)
	if (p->expr) {
	    decompile (p->expr);
	    line[linelim] = 0;
	} else {
	    sprintf (line+linelim, "%g", p->v);
	    linelim += strlen (line+linelim);
	}
}

printfile (fname) {
    FILE *f = fopen(fname, "w");
    register row, col;
    register struct ent **p;
    if (f==0) {
	error ("Can't create %s", fname);
	return;
    }
    for (row=0;row<=maxrow; row++) {
	register c = 0;
	linelim = 0;
	for (p = &tbl[row][col=0]; col<=maxcol; col++, p++) {
	    if (*p) {
		char *s;
		while (linelim<c) line[linelim++] = ' ';
		linelim = c;
		if ((*p)->flags&is_valid) {
		    sprintf (line+linelim,"%*.*f",fwidth[col],precision[col],
				(*p)->v);
		    linelim += strlen (line+linelim);
		}
		if (s = (*p)->label) {
		    register char *d;
		    d = line+((*p)->flags&is_leftflush
			? c : c-strlen(s)+fwidth[col]);
		    while (d>line+linelim) line[linelim++] = ' ';
		    if (d<line) d = line;
		    while (*s) *d++ = *s++;
		    if (d-line>linelim) linelim = d-line;
		}
	    }
	    c += fwidth [col];
	}
	fprintf (f,"%.*s\n",linelim,line);
    }
    fclose (f);
}

struct enode *copye (e, Rdelta, Cdelta)
register struct enode *e; {
    register struct enode *ret;
    if (e==0) ret = 0;
    else {
	ret = (struct enode *) malloc (sizeof (struct enode));
	*ret = *e;
	switch (ret->op) {
	case 'v':
		ret->e.v = lookat (ret->e.v->row+Rdelta, ret->e.v->col+Cdelta);
		break;
	case 'k':
		break;
	case 'f':
		ret->e.o.right = copye (ret->e.o.right,0,0);
		break;
	default:
		ret->e.o.right = copye (ret->e.o.right,Rdelta,Cdelta);
		ret->e.o.left = copye (ret->e.o.left,Rdelta,Cdelta);
		break;
	}
    }
    return ret;
}
