/* Parser for SSS */
/* Tue,30 Jul 1991 */
/* Copyright C.T.Stretch 1991 */

#include "ssshdr.h"
#include <signal.h>
#include <setjmp.h>
#include <float.h>

#define CHK(c) if(*nextc++!=c) longjmp(env,2)
#define DO(f)  CHK('(');val=f(rexp(0));CHK(')');break
#define R(a,b) CHK('[');a=iexp(0);CHK(',');b=iexp(0);CHK(']')
#define DR     R(x0,y0);R(x1,y1)

static jmp_buf env;
static double sx,sxx;
static int nx,x0,y0,x1,y1;
int vx,vy;
double value;
char *expr;
BOOL perr;
static char *nextc;

static double rexp(int);
static int iexp(int);

static double cell(int x,int y)
{ entry *cb;
  fixed=0;
  if(x<0 || x>=ncols || y<0 || y>=nrows)
  { errno=1;
    return 1;
  }
  cb=sheet[x+NCOLS*y];
  if(!cb||cb->t<FINT||cb->a||cb->p)
  { errno=1;
    return 1;
  }
  return cb->v;
}

static double cmax()
{ entry *cb;
  int x,y;
  double best=-DBL_MAX;
  fixed=0;
  if(x0<0 || x1>=ncols || y0<0 || y1>=nrows)
  { errno=1;
    return 0;
  }
  for(x=x0;x<=x1;x++) for(y=y0;y<=y1;y++)
  { cb=sheet[x+NCOLS*y];
    if(cb&&cb->t>=FINT&&!(cb->a||cb->p))
    { if(cb->v>best) best=cb->v;
    }
  }
  if(best==-DBL_MAX) {errno=1;return 0;}
  return best;
}

static double cmin()
{ entry *cb;
  int x,y;
  double best=DBL_MAX;
  fixed=0;
  if(x0<0 || x1>=ncols || y0<0 || y1>=nrows)
  { errno=1;
    return 0;
  }
  for(x=x0;x<=x1;x++) for(y=y0;y<=y1;y++)
  { cb=sheet[x+NCOLS*y];
    if(cb&&cb->t>=FINT&&!(cb->a||cb->p))
    { if(cb->v<best) best=cb->v;
    }
  }
  if(best==DBL_MAX) {errno=1;return 0;}
  return best;
}

static void countup()
{ entry *cb;
  int x,y;
  fixed=0;
  nx=0;sx=0;sxx=0;
  if(x0<0 || x1>=ncols || y0<0 || y1>=nrows)
  { errno=1;
    return ;
  }
  for(x=x0;x<=x1;x++) for(y=y0;y<=y1;y++)
  { cb=sheet[x+NCOLS*y];
    if(cb&&cb->t>=FINT&&!(cb->p||cb->a))
    { nx++;sx+=cb->v;sxx+=cb->v*cb->v;
    }
  }
  return ;
}

static int ibexp()
{ int val;
  switch(*nextc)
  { case '(':nextc++;
             val=iexp(0);
             CHK(')');
             break;
    case '+':nextc++;
             val=iexp(0);
             break;
    case '-':nextc++;
             val=-iexp(0);
             break;
    case 'x':nextc++;val=vx;break;
    case 'y':nextc++;val=vy;break;
     default:if(isdigit(*nextc)) val=(int)strtol(nextc,&nextc,10);
             else longjmp(env,2);
  }
  return val;
}

static int iexp(int n)
{ int a=ibexp();
  for(;;) switch(*nextc)
  { case '+':if(n>=1) return a;nextc++;a+=iexp(1);break;
    case '-':if(n>=1) return a;nextc++;a-=iexp(1);break;
    case '*':if(n>=2) return a;nextc++;a*=iexp(2);break;
    case '/':if(n>=2) return a;nextc++;a/=iexp(2);break;
    case ' ':nextc++;break;
    default :return a;
  }
}

static double rbexp()
{ double val;
  int i;
  switch(*nextc)
  { case '(':nextc++;
             val=rexp(0);
             CHK(')');
             break;
    case '[':nextc++;
             i=iexp(0);
             CHK(',');
             val=cell(i,iexp(0));
             CHK(']');
             break;
    case '+':nextc++;
             val=rexp(0);
             break;
    case '-':nextc++;
             val=-rexp(0);
             break;
    case '.':val=strtod(nextc,&nextc);break;
    default :if(isdigit(*nextc)) { val=strtod(nextc,&nextc);break;}
             switch((int)strtol(nextc,&nextc,36))
             { case    33:val=vx;break;
               case    34:val=vy;break;
               case 36959:DO(sin);
               case 16444:DO(cos);
               case 37967:DO(tan);
               case 28096:DO(log);
               case 19357:DO(exp);
               case 1341065:DO(sqrt);
               case 1034:CHK('(');val=rexp(0);CHK(')');val=val*val;break;
               case 578685:DO(ceil);
               case 26206011:DO(floor);
               case 46509097:CHK('(');val=floor(rexp(0)+0.5);CHK(')');break;
               case 503519:DO(asin);
               case 483004:DO(acos);
               case 504527:DO(atan);
               case 18162974:CHK('(');val=rexp(0);;CHK(',');
                                      val=atan2(val,rexp(0));
                                      CHK('(');break;
               case 1330541:DO(sinh);
               case 592001:DO(cosh);
               case 1366829:DO(tanh);
               case 37390:DR;countup();val=sx;break;
               case 21314873:DR;countup();val=nx;break;
               case 13384:DO(fabs);
               case 1044959:DR;countup();val=sx/nx;break;
               case 37309:DR;countup();
                          val=sqrt((nx*sxx-sx*sx)/(nx*(nx-1)));break;
               case 33421:DR;countup();
                             val=sqrt(sxx-sx*sx/nx);break;
               case   918:val=4*atan(1);break;
               case    14:val=exp(1);break;
               case 28905:DR;val=cmax();break;
               case 29183:DR;val=cmin();break;
               default:longjmp(env,2);
             }
             break;
  }
  return val;
}

static double rexp(int n)
{ double a=rbexp();
  for(;;) switch(*nextc)
  { case '+':if(n>=1) return a;nextc++;a+=rexp(1);break;
    case '-':if(n>=1) return a;nextc++;a-=rexp(1);break;
    case '*':if(n>=2) return a;nextc++;a*=rexp(2);break;
    case '/':if(n>=2) return a;nextc++;a/=rexp(2);break;
    case '^':if(n>=3) return a;nextc++;a=pow(a,rexp(4));break;
    case ' ':nextc++;break;
    default :return a;
  }
}

static void badfpe(int snag)
{ if(snag==SIGFPE) longjmp(env,1);
}

void eval()
{ int e;
  nextc=expr;
  errno=0;
  fixed=1;
  if(e=setjmp(env))
  { if(e==1) { errno=1;perr=0;} else perr=1;
    fixed=0;return;
  }
  signal(SIGFPE,badfpe);
  value=rexp(0);
  perr=*nextc;
  signal(SIGFPE,SIG_DFL);
}
