/* Designed, written and commented by Paul Falstad */
/* This program was extracted from a much, much larger program */
/* by the same author, so don't expect streamlined code.       */

extern double Term1(),Parens(),LocalConv(),Term2();
char *Eval(),*FetchSym(),*SkipSpace(),*RetPDB();

main(argc,argv)
int argc;
char **argv;
{
   if (argc != 2)
      printf("Format: %s (expression)\n",argv[0]);
   else
      {
      printf(Eval(argv[1],0,0L));
      printf("\n");
      }
}

int isstring(str)
char *str;
{
int isstring = TRUE,parens = 0;

   while (*str)
      {
      if (*str == '(')
         parens++;
      if (*str == ')')
         parens--;
      if (parens)
         {
         str++;
         continue;
         }
      if (*str == '+' || *str == '-' || *str == '*' || *str == '/' ||
          *str == '^' || *str == '%' || (*str >= '0' && *str <= '9'))
         {
         isstring = FALSE;
         break;
         }
      str++;
      }
   return(isstring);
}

char *Eval(str,rnd,db)
char *str;
int rnd;
double *db;
{
double val = 0;
static char ebuf[512];

   while (*str == ' ')
      str++;
   if (!*str)
      return(0);
   if (isstring(str))
      return(FetchSym(str,0L));
   while (*str)
      val += Term1(&str);
   if (db)
      {
      *db = val;
      return(0L);
      }
   else
      {
      DoublePrint(ebuf,val,8);
      return(SkipSpace(ebuf));
      }
}

double Term1(str)
char **str;
{
char *ptr,buf[80];
long paren = 0;
BOOL first;

   if (**str == 0)
      return(0);
   sprintf(buf,"+%s",*str);
   first = TRUE;
   for (ptr = buf+1; *ptr; ptr++,(*str)++)
      {
      if (*ptr == '(')
         paren++;
      if (*ptr == ')')
         paren--;
      if (paren)
         continue;
      if (*ptr == '-' && first)
         {
         first = FALSE;
         continue;
         }
      first = FALSE;
      if (*ptr == '*' || *ptr == '/' || *ptr == '^' || *ptr == '%'
                      || *ptr == '^')
         {
         first = TRUE;
         continue;
         }
      if (*ptr == '+' || *ptr == '-')
         break;
      }
   if (paren)
      return(0);
   *ptr = NULL;
   if (**str == '+')
      (*str)++;
   return(Term2(buf));
}

double Parens(str)
char *str;
{
char buf[70],*pt;
long parct = 1;
double ret = 0;

   sprintf(buf,"%s",str+1);
   for (pt = buf; *pt; pt++)
      {
      if (*pt == '(')
         parct++;
      if (*pt == ')')
         parct--;
      if (!parct)
         break;
      }
   if (parct)
      return(0);
   *pt = NULL;
   pt = buf;
   while (*pt)
      ret += Term1(&pt,&ret);
   return(ret);
}

double LocalConv(ch)
char *ch;
{
double val = 0.0,shift = .100;
long mult = 1;
BOOL ped = FALSE;

   while (*ch)
      {
      if (*ch == '.')
         {
         ped = TRUE;
         ch++;
         continue;
         }
      if (*ch == '-' && val == 0 && mult == 1)
         {
         mult = -1;
         ch++;
         continue;
         }
      if (*ch == '*' || *ch == '/' || *ch == '%' || *ch == '^' || *ch == ' ')
         break;
      if (*ch < '0' || *ch > '9')
         return(0);
      if (ped)
         {
         val += shift*(*ch-'0');
         shift /= 10;
         }
      else
         val = val*10+(*ch-'0');
      ch++;
      }
   return(val*mult);
}

double Term2(str)
char *str;
{
double a = 0,b;
long paren = 0;

   while (*str)
      {
      if (*(str+1) == '(')
         b = Parens(str+1);
      else
         {
         if (tolower(*(str+1)) >= 'a' && tolower(*(str+1)) <= 'z')
            FetchSym(str+1,&b);
         else
            {
            if (*(str+1) == '-')
               {
               if (tolower(*(str+2)) >= 'a' && tolower(*(str+2)) <= 'z')
                  {
                  FetchSym(str+2,&b);
                  b = -b;
                  }
               else
                 {
                 if (*(str+2) == '(')
                    b = -Parens(str+2);
                 else
                    b = LocalConv(str+1);
                 }
               }
            else
               b = LocalConv(str+1);
            }
         }
      switch (*str)
         {
         case '+':
            a = b;
            break;
         case '*':
            a *= b;
            break;
         case '/':
            if (b != 0)
               a /= b;
            else
               a = 0;
            break;
         case '%':
            if (b != 0)
               a = (int) a % (int) b;
            else
               a = 0;
            break;
         case '^':
            a = pow(a,b);
            break;
         default:
            return(0);
         };
      for (str++; *str; str++)
         {
         if (*str == '(')
            paren++;
         if (*str == ')')
            {
            paren--;
            continue;
            }
         if (paren)
            continue;
         if (*str == '*' || *str == '/' || *str == '%' || *str == '^')
            break;
         }
      }
   return(a);
}

char *FetchSym(str,pdb)
char *str;
double *pdb;
{
char *pt,*tp;
static char stufbuf[80],dobuf[80];
int s0;
double arg;

   for (pt = str; *pt; pt++)
      if ((tolower(*pt) < 'a' || tolower(*pt) > 'z') &&
         (*pt < '0' || *pt > '9') && *pt != '_')
         break;
   for (tp = stufbuf; str != pt; str++,tp++)
      *tp = tolower(*str);
   *tp = 0;
   if (*pt == '(')
      {
      if (!strcmp("int",stufbuf))
         return(RetPDB((double) ((long) Parens(pt)),pdb));
      if (!strcmp("abs",stufbuf))
         return(RetPDB(abs(Parens(pt)),pdb));
      if (!strcmp("sqrt",stufbuf))
         return(RetPDB(sqrt(Parens(pt)),pdb));
      if (!strcmp("sin",stufbuf))
         return(RetPDB(sin(Parens(pt)/180*PI),pdb));
      if (!strcmp("cos",stufbuf))
         return(RetPDB(cos(Parens(pt)/180*PI),pdb));
      if (!strcmp("tan",stufbuf))
         return(RetPDB(tan(Parens(pt)/180*PI),pdb));
      if (!strcmp("ln",stufbuf))
         {
         arg = Parens(pt);
         if (arg <= 0)
            return(RetPDB((double) 0,pdb));
         else
            return(RetPDB(log(arg),pdb));
         }
      if (!strcmp("log",stufbuf))
         {
         arg = Parens(pt);
         if (arg <= 0)
            return(RetPDB((double) 0,pdb));
         else
            return(RetPDB(log10(arg),pdb));
         return;
         }
      if (!strcmp("atan",stufbuf))
         return(RetPDB(atan(Parens(pt))/PI*180,pdb));
      }
   if (!strcmp(stufbuf,"pi"))
      {
      if (pdb)
         {
         *pdb = PI;
         return;
         }
      else
         return("3.141592654");
      }
   if (!strcmp(stufbuf,"e"))
      {
      if (pdb)
         {
         *pdb = 2.718281828;
         return;
         }
      else
         return("2.718281828");
      }
}

char *RetPDB(db,dbp)
double db,*dbp;
{
static char pdbuf[512];

   if (dbp)
      {
      *dbp = db;
      return(NULL);
      }
   else
      {
      DoublePrint(pdbuf,db,8);
      return(SkipSpace(pdbuf));
      }
}

DoublePrint(buf,val,sigs)
char *buf;
double val;
int sigs;
{
int ptr;

   sprintf(buf,"%60.*f",sigs,val);
   for (ptr = 59; ptr; ptr--)
      {
      if (buf[ptr] == '0')
         buf[ptr] = 0;
      else
         break;
      }
   if (buf[ptr] == '.')
      buf[ptr] = 0;
}

char *SkipSpace(ptr)
char *ptr;
{
   while (*ptr == ' ')
      ptr++;
   return(ptr);
}
