/****************************************************************
 *
 *  fcc.c
 *
 *  Übersetzt einen mathematischen Ausdruck in ein Feld von IPN-
 *  Regeln und eines von float-Konstanten, die von fci.c schnell
 *  interpretiert werden können.
 *
 *  Peter Gober, 09-May-88
 *               12-Oct-88 kleine Veränderungen
 *
 ****************************************************************/

#include "fc.h"

/*
 *  interner Stack und Pufferspeicher, TIEFE WIRD NICHT ÜBERWACHT!
 */

#define STACK_DEPTH 128
#define BUFFER_SIZE 128

float atof();

/*
 *  trigonomaterische Funktionen und ihre Codierung für fci
 */

struct
{
   char name[6];
   int code;
} functs[] = {
                 {  "sinh", SINH_CODE },
                 {  "cosh", COSH_CODE },
                 {  "tanh", TANH_CODE },
                 {  "sin", SIN_CODE },
                 {  "cos", COS_CODE },
                 {  "tan", TAN_CODE },
                 {  "cot", COTAN_CODE },
                 {  "cotan", COTAN_CODE },
                 {  "asin", ASIN_CODE },
                 {  "acos", ACOS_CODE },
                 {  "atan", ATAN_CODE },
                 {  "atn", ATAN_CODE },
                 {  "floor", FLOOR_CODE },
                 {  "int", FLOOR_CODE },
                 {  "ceil", CEIL_CODE },
                 {  "fabs", FABS_CODE },
                 {  "abs", FABS_CODE },
                 {  "log10", LOG10_CODE },
                 {  "lg", LOG10_CODE },
                 {  "log", LOG_CODE },
                 {  "ln", LOG_CODE },
                 {  "exp",EXP_CODE },
                 {  "sqrt", SQRT_CODE },
                 {  "sqr", SQRT_CODE },
                 {  "", 0 }
             };

/*
 *  fcc
 *
 *  Gebrauch:       returncode = fcc(formel, rules, constants)
 *                  char *formel;
 *                  int *rules;
 *                  float *constants;
 *
 *  Eingaben:       formel       - Zeiger auf Formelausdruck, mit '\0' abschlie-
 *                                 ßend
 *                  rules,       - Zeiger auf genügend große Felder
 *                   constants
 *  Ergebnisse:     returncode   - -1 im Fehlerfall, sonst NULL
 *                  rules[i]     - Rechenregeln in IPN-Form
 *                  constants[i] - Konstanten
 */

fcc(p_formel,rules,constants)
register char *p_formel;
int *rules;
float *constants;
{
   int opStack[STACK_DEPTH];           /* interner Stack */
   register int opSPtr = -1;           /* Stackpointer */

   char buffer[BUFFER_SIZE];           /* Zwischenspeicher für atof() */
   register int i;
   float a;

   int preminusflag, flag;
   int op;                             /* gerade gelesener Operator */

   for (;;)
   {
      do
      {
         preminusflag = 0;
         flag = 0;

         /*
          *  eventuelle Vorzeichen und führende Leerzeichen abgrasen
          */

         while (*p_formel == '+' || *p_formel == '-' || *p_formel == ' ')
         {
            if (*p_formel == '-') preminusflag = !preminusflag;
            p_formel++;
         }

         if (*p_formel == 'X' || *p_formel == 'x')
         {
            *rules++ = GETX_CODE;
            if (preminusflag) *rules++ = NEG_CODE;
            p_formel++;
         }

         else if (*p_formel == '¶')
         {
            *constants++ = (preminusflag) ? -3.1415926 : 3.1415926;
            *rules++ = READ_CODE;
            p_formel++;
         }

         else if (*p_formel >= '0' && *p_formel <= '9' || *p_formel == '.')
         {
            /*
             *  Operand ist Zahl, die mit atof() holen. Dazu Zeichen erst in
             *  Puffer kopieren, der mit '\0' abgeschlossen wird.
             */

            i = 0;
            do
            {
               buffer[i++] = *(p_formel++);
            }
            while (*p_formel >= '0' && *p_formel <= '9' ||
                   *p_formel == '.' || *p_formel == 'E' ||
                   *p_formel == 'e' ||
                   *p_formel == '-' &&
                      (*(p_formel-1) == 'E' || *(p_formel-1) == 'e'));
            buffer[i] = '\0';
            a = atof(buffer);
            if (preminusflag) a = -a;
            *constants++ = a;
            *rules++ = READ_CODE;
         }

         else if (*p_formel == '(')
         {
            /*
             * Operand ist Klammerausdruck
             */

            if (preminusflag) opStack[++opSPtr] = NEG_CODE;
            opStack[++opSPtr] = BRAC_CODE;
            p_formel++;
            flag = 1;                  /* signalisiert: hole Ausdruck nach ( */
         }

         else
         {
            /*
             * Operand sollte trigonometrische Funktion sein
             */

            for (i = 0; *functs[i].name != '\0'; i++)
            {
               if (strcmp2(functs[i].name,p_formel))
               {
                   opStack[++opSPtr] = functs[i].code;
                   p_formel += strlen(functs[i].name);
                   flag = 1;
                   break;
               }
            }
            if (!flag) return(-1);  /* "sollte" - wenn nicht ... */
         }
      }
      while (flag);

      do
      {
         flag = 0;

         while (*p_formel == ' ') p_formel++;  /* Leerzeichen überspringen */

         /*
          *  Operatorcode nach op
          */

         switch(*p_formel)
         {
            case '^':  op = POW_CODE;    break;
            case '*':  op = MULT_CODE;   break;
            case '/':  op = DIV_CODE;    break;
            case '+':  op = PLUS_CODE;   break;
            case '-':  op = MINUS_CODE;  break;
            case ')':  op = BRAC_CODE;   break;
            case '\0': op = END_CODE;    break;
            default:
               /* Multiplikation ohne * (z. B. -2¶ oder 3(...) */
               op = MULT_CODE;
               p_formel--;
               break;
         }

         while (opSPtr > -1 && (opStack[opSPtr] & PRI_MASK) >= (op & PRI_MASK))

         /*
          *  Stack abarbeiten, falls Priorität des Rechenzeichens auf dem Stack
          *  höher (das Rechenzeichen wichtiger ist) als das eben gelesene. Da
          *  BRAC_CODE die niedrigste Priorität hat, wird bei dessen Auftreten im
          *  im op erst der Stack abgearbeitet bis zur (; flag wird gesetzt und
          *  das Rechenzeichen nach der ) geprüft.
          */

         {
            if (opStack[opSPtr] == BRAC_CODE)
            {
               /*
                *  (...) wurde gelesen, lies jetzt den Operator danach
                */

               opSPtr--;               /* Nimm Bearbeitetes vom Stack */
               flag = 1;
               p_formel++;             /* Formelzeiger auf Operator */
               break;
            }
            else
            {
               /*
                *  Alles, was nicht BRAC_CODE ist, in die rules kopieren.
                */

               *rules++ = opStack[opSPtr];
               opSPtr--;               /* Nimm Bearbeitetes vom Stack */
            }
         }
      }
      while (flag);

      /*
       *  Ende
       */

      if (op == END_CODE)
      {
         if (opSPtr != -1) return(-1); /* Stack nicht ganz leer? Fehler! */
         *rules++ = END_CODE; /* Ende-Markierung */
         return(0);
      }

      /*
       *  Operator auf dem Stack ablegen
       */

      opStack[++opSPtr] = op;

      p_formel++;
   }
}


/*
 *  strcmp2
 *
 *  Gebrauch:          returncode = strcmp2(s1,s2)
 *                     char *s1, *s2;
 *
 *  Funktion:          Vergleicht s1 mit s2 und zwar solange, bis in s1 '\0'
 *                     erscheint. Groß- und Kleinbuchstaben werden gleich be-
 *                     handelt. Gibt bei Gleichheit 1, sonst 0 zurück.
 */

int
strcmp2(s1, s2)
char *s1, *s2;
{
   while (*s1 != '\0')
   {
      if (tolower(*s1++) != tolower(*s2++))
         return(0);
   }
   return(1);
}

