/**************************************************************************
 **                                                                      **
 ** XCalc v1.0                                                           **
 **                                                                      **
 ** Arithmetic expressions calculator                                    **
 **                                                                      **
 ** +, -, *, /, cos, sin, ^, (), -, integer, real                        **
 **                                                                      **
 **************************************************************************
 **                                                                      **
 ** Author  : Nicolas Mougel                                             **
 ** Contact : nicolas_mougel@hotmail.com                                 **
 **                                                                      **
 ** If you update this program, please contact the author.               **
 **                                                                      **
 **************************************************************************
 **                                                                      **
 ** XCalc.c                                                              **
 **                                                                      **
 ** 10/12/1995 : First Release (Windows, Unix)                           **
 ** 26/12/1998 : Let's go to the BeOS World !                            **
 **                                                                      **
 **************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#define N       3
#define OK      0
#define SYNTAX  1
#define DIV_Z   2

/* Fonctions' declarations
 */

int evaluate(double *x, char *string, int *pos, char **com);
int is_a_E(double *x, char **p);
int is_a_E1(double *x, char **p);
int is_a_S(double *x, char **p);
int is_a_S1(double *x, char **p);
int is_a_F2(double *x, char **p);
int is_a_F(double *x, char **p);
int is_a_G(double *x, char **p);
int is_a_I(double *x, char **p);
int is_a_H(double *x, char **p);
int is_a_J(double *x, char **p);
int is_a_K(double *x, char **p);
int is_a_C1(double *x, char **p);
int is_a_C(double *x, char **p);
int is_a_D1(double *x, char **p);
int is_a_D(double *x, char **p);

int digit_number, err;

/* Main Program
 */

main()
{
  char chaine[256], *com;
  double x;
  int i,pos;

  printf("Enter your expression ('q' to quit) :\n");
  gets(chaine);

  while (chaine[0]!='q')
  {
    if (evaluate(&x, chaine, &pos, &com)==TRUE)
      printf("= %lf\n", x);
    else
    {
      for (i=0; i<pos; i++)
        printf(" ");
      printf("^\n");
      printf("--->Error in your expression : %s\n", com);
    }
    printf("Enter your expression ('q' to quit) :\n");
    gets(chaine);

  }
  return(0);
}

/***************************************************************************
 ** evaluate() :                                                          **
 **                                                                       **
 **   Return the value of the arithmetic expression gived in the string   **
 **   Put the resultat in x                                               **
 **   Put the current character position in pos                           **
 **                                                                       **
 ***************************************************************************/

/* The XCalc Grammar : 
 *
 * E  -> SE1
 * E1 -> +SE1 | -SE1 | 0
 * S  -> F2S1
 * S1 -> *F2S1 | /F2S1 | 0
 * F2 -> +F1 | -F2 | F
 * F  -> cosF2 | sin F2 | G
 * G  -> HI
 * I  -> ^HI | 0
 * H  -> (E) | J
 * J  -> C1K
 * K  -> .C | 0
 * C1 -> +C1 | -C1 | C
 * C  -> DD1
 * D1 -> DD1 | 0
 * D  -> 0|1|2|3|4|5|6|7|8|9
 */

int evaluate(double *x, char *string, int *pos, char **com)
{
  char *p;              /* pointer on the current character */
  char *lib[N]={"Good expression", 
                "Syntax Error", 
                "Divison by zero" };
  p=string;

  err=SYNTAX;
  *com=lib[err];

  if (is_a_E(x,&p)==TRUE)
  {
    if (*p=='\0')
      return TRUE;
    else
    {
      *pos=p-string;
      *com=lib[err];
      return FALSE;
    }
  }
  else
  {
    *pos=p-string;
    *com=lib[err];
    return FALSE;
  }
}

/* is_a_E() :
 */

int is_a_E(double *x, char **p)
{
  /*printf(">E: '%c'\n",**p);*/
  if ( is_a_S(x,p)==TRUE && is_a_E1(x,p)==TRUE )
    return TRUE;
  return FALSE;
}

/* is_a_E1() :
 */

int is_a_E1(double *x, char **p)
{
  double y;
  /*printf(">E1\n");*/
  while (**p==' ')
    *p=*p+1;
  if (**p=='+')
  {
    *p=*p+1;
    if ( is_a_S(&y,p)==TRUE && is_a_E1(&y,p)==TRUE )
    {
      *x=*x+y;
      return TRUE;
    }
    return FALSE;
  }
  else if (**p=='-')
  {
    *p=*p+1;
    if ( is_a_S(&y,p)==TRUE && is_a_E1(&y,p)==TRUE )
    {
      *x=*x-y;
      return TRUE;
    }
    else
      return FALSE;
  }

  return TRUE;
}

/* is_a_S() :
 */

int is_a_S(double *x, char **p)
{
  /*printf(">S\n");*/
  if ( is_a_F2(x,p)==TRUE && is_a_S1(x,p)==TRUE )
    return TRUE;
  return FALSE;
}

/* is_a_S1() :
 */

int is_a_S1(double *x, char **p)
{
  double y;
  /*printf(">S1\n");*/
  while (**p==' ')
    *p=*p+1;
  if (**p=='*')
  {
    *p=*p+1;
    if ( is_a_F2(&y,p)==TRUE && is_a_S1(&y,p)==TRUE )
    {
      *x=*x*y;
      return TRUE;
    }
    return FALSE;
  }
  else if (**p=='/')
  {
    *p=*p+1;
    if ( is_a_F2(&y,p)==TRUE && is_a_S1(&y,p)==TRUE )
    {
      if (y==0)
      {
        err=DIV_Z;
        return FALSE;
      }
      else
      {
        *x=*x/y;
        return TRUE;
      }
    }
    return FALSE;
  }

  return TRUE;
}

/* is_a_F2() :
 */

int is_a_F2(double *x, char **p)
{
  /*printf(">F2\n");*/
  while (**p==' ')
    *p=*p+1;
  if (**p=='+')
  {
    *p=*p+1;
    if ( is_a_F2(x,p)==TRUE )
      return TRUE;
  }
  else if (**p=='-')
  {
    *p=*p+1;
    if ( is_a_F2(x,p)==TRUE )
    {
      *x=-*x;
      return TRUE;
    }
  }
  else if ( is_a_F(x,p)==TRUE )
    return TRUE;
  else
    return FALSE;
}

/* is_a_F() :
 */

int is_a_F(double *x, char **p)
{
  double y;
  /*printf(">F\n");*/
  if ( **p=='c' && *(*p+1)=='o' && *(*p+2)=='s' )
  {
    *p=*p+3;
    if ( is_a_F2(&y,p)==TRUE)
    {
      *x=cos(y);
      return TRUE;
    }
    else
      return FALSE;
  }
  else if ( **p=='s' && *(*p+1)=='i' && *(*p+2)=='n' )
  {
    *p=*p+3;
    if ( is_a_F2(&y,p)==TRUE)
    {
      *x=sin(y);
      return TRUE;
    }
    else
      return FALSE;
  }
  else
  {
    if ( is_a_G(x,p)==TRUE )
      return TRUE;
    else
      return FALSE;
  }
}

/* is_a_G() :
 */

int is_a_G(double *x, char **p)
{
  /*printf(">G\n");*/
  if ( is_a_H(x,p)==TRUE && is_a_I(x,p)==TRUE )
      return TRUE;
  return FALSE;
}

/* is_a_I() :
 */

int is_a_I(double *x, char **p)
{
  double y;
  /*printf(">I\n");*/
  while (**p==' ')
    *p=*p+1;
  if (**p=='^')
  {
    *p=*p+1;
    if ( is_a_H(&y,p)==TRUE && is_a_I(&y,p)==TRUE )
    {
      *x=exp(y*log(*x));
      return TRUE;
    }
    else
      return FALSE;
  }

  return TRUE;
}

/* is_a_H() :
 */

int is_a_H(double *x, char **p)
{
  /*printf(">H\n");*/
  while (**p==' ')
    *p=*p+1;
  if (**p=='(')
  {
    *p=*p+1;
    if ( is_a_E(x,p)==TRUE )
    {
      if (**p==')')
      {
        *p=*p+1;
        return TRUE;
      }
      else
        return FALSE;
    }
    else
      return FALSE;
  }
  else 
  { 
    if ( is_a_J(x,p)==TRUE )
      return TRUE;
    else
      return FALSE;
  }
}

/* is_a_J() :
 */

int is_a_J(double *x, char **p)
{
  /*printf(">J\n");*/
  if ( is_a_C1(x,p)==TRUE && is_a_K(x,p)==TRUE )
    return TRUE;
  return FALSE;
}

/* is_a_K() :
 */

int is_a_K(double *x, char **p)
{
  double y;
  /*printf(">K\n");*/
  if (**p=='.')
  {
    *p=*p+1;
    if ( is_a_C(&y,p)==TRUE )
    {
      while (digit_number>0)
      {
        y=y/10;
        digit_number--;
      }
      *x=*x+y;
      return TRUE;
    }
    return FALSE;
  }

  return TRUE;
}

/* is_a_C1() :
 */

int is_a_C1(double *x, char **p)
{
  /*printf(">C1\n");*/
  if (**p=='+')
  {
    *p=*p+1;
    if ( is_a_C1(x,p)==TRUE )
      return TRUE;
    else
      return FALSE;
  }
  else if (**p=='-')
  {
    *p=*p+1;
    if ( is_a_C1(x,p)==TRUE )
    {
      *x=-*x;
      return TRUE;
    }
    else
      return FALSE;
  }
  else
  {
    if ( is_a_C(x,p)==TRUE )
      return TRUE;
    else
      return FALSE;
  }
}

/* is_a_C() :
 */

int is_a_C(double *x, char **p)
{
  /*printf(">C\n");*/
  digit_number=0;
  if ( is_a_D(x,p)==TRUE && is_a_D1(x,p)==TRUE )
    return TRUE;
  return FALSE;
}

/* is_a_D1() :
 */

int is_a_D1(double *x, char **p)
{
  double y;
  /*printf(">D1\n");*/
  if ( is_a_D(&y,p)==TRUE )
  {
    *x=(*x*10)+y;
    if ( is_a_D1(x,p)==TRUE )
      return TRUE;
    else
      return FALSE;
  }

  return TRUE;
}

/* is_a_D() :
 */

int is_a_D(double *x, char **p)
{
  /*printf(">D\n");*/
  switch (**p)
  {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      digit_number++;
      *x=*(*p)-'0';
      *p=*p+1;
      return TRUE;
    default:
     return FALSE;
  }
}

