/* formula parser
   Copyright (C) 1992-2000 Michigan State University

   The CAPA system is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CAPA system is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with the CAPA system; see the file COPYING.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   As a special exception, you have permission to link this program
   with the TtH/TtM library and distribute executables, as long as you
   follow the requirements of the GNU GPL in regard to all of the
   software in the executable aside from TtH/TtM.
*/

/* ====================================================== */
/*      capaFormula.y  created by Isaac Tsai @ Feb 1999   */
/*  TODO: checking user inputs   2/27/99      IT          */
/* ====================================================== */
%{
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "capaParser.h"   /* _symbol structure def */
#include "capaCommon.h"
#include "capaFunction.h"
#ifdef  YYSTYPE
#undef  YYSTYPE
#endif
#define YYSTYPE  Symbol_p
#include "capaToken.h"

#ifdef __hpux
#include <stdlib.h>
#include <alloca.h>
#endif

#ifdef   FML_DBUG
#define  FMLDBUG_PR1(xx)        { printf(xx);    fflush(stdout); }
#define  FMLDBUG_PR2(xx,yy)     { printf(xx,yy); fflush(stdout); }
#define  FMLDBUG_PR3(xx,yy,zz)  { printf(xx,yy,zz); fflush(stdout); }
#else
#define  FMLDBUG_PR1(xx)        { }
#define  FMLDBUG_PR2(xx,yy)     { }
#define  FMLDBUG_PR3(xx,yy,zz)  { }
#endif

#define ADD_op          1
#define SUB_op          2
#define MUL_op          3
#define DIV_op          4
#define IDIV_op         5
#define NOT_DEFINED_op  9


/* =============================================================== */

extern      int         Func_idx;
extern      Symbol      FuncStack[MAX_FUNC_NEST];
void   fml_error(char *msg);
double      FormulaVal;
int         FormulaParseOK=1;

%}


%token    F_NUMBER    V_ID      F_ID     EoI      F_ERROR 
%left     F_PLUS      F_MINUS   
%left     F_MULT      F_DIV     F_MOD
%token    F_POW
%token    F_LPAR    F_RPAR   F_COMMA 


%start    f_expr


%%

f_expr       : block                             { switch($1->s_type) {
                                                      case I_VAR:
                                                      case I_CONSTANT: FormulaVal = (double)($1->s_int);
                                                          break;
                                                      case R_VAR: 
                                                      case R_CONSTANT: FormulaVal = $1->s_real;
                                                          break;
                                                      case S_VAR:
                                                      case S_CONSTANT: FormulaParseOK = 0;
                                                          break;
                                                      default:         FormulaParseOK = 0;
                                                          break;
                                                    }
                                                    capa_mfree((char *)$1);
                                                    FMLDBUG_PR1("[f_expr <= block ]\n");
                                                  }
             ;

block        : block F_PLUS   term                { $$ = symbols_op($1, $3, ADD_op);  }
             | block F_MINUS  term                { $$ = symbols_op($1, $3, SUB_op);  }
             | F_MINUS block                       { $$ = negate($2); }
             | term                               { $$ = $1; }
             | F_ERROR                            { FormulaParseOK = 0; FMLDBUG_PR1("[F_ERROR]\n"); return 0;}
             | error                              { FormulaParseOK = 0; FMLDBUG_PR1("[ERROR]\n"); return 0;  }
             ;

term         : term   F_MULT basic_constr         { $$ = symbols_op($1, $3, MUL_op);  }
             | term   F_MULT F_MINUS basic_constr { $$ = symbols_op($1, negate($4), MUL_op);  }
             | term   F_DIV  basic_constr         { $$ = symbols_op($1, $3, DIV_op);  }
             | term   F_DIV  F_MINUS basic_constr { $$ = symbols_op($1, negate($4), DIV_op);  }
             | term   F_MOD  basic_constr         { $$ = symbols_op($1, $3, IDIV_op); }
             | term   F_MOD  F_MINUS basic_constr { $$ = symbols_op($1, negate($4), IDIV_op); }
             | basic_constr                       { $$ = $1; }
             ;

basic_constr : basic_constr  F_POW   basic_item   { $$ = f_symbol_pow($1,$3);
                                                    FMLDBUG_PR3("[%.16g ^ %.16g] ",$1->s_real,$3->s_real);       }
             | basic_constr F_POW F_MINUS basic_item   { $$ = f_symbol_pow($1,negate($4));
                                                    FMLDBUG_PR3("[%.16g ^ %.16g] ",$1->s_real,$4->s_real);       }
             | basic_item                         { $$ = $1; }
             ;

arg_list     : arg_list F_COMMA  block            { $$ = $1;
                                                    $$->s_argc++;
                                                    $$->s_argp = addto_arglist($1->s_argp, $3);
                                                  }
             | block                              { $$ = $1;
                                                    $$->s_argc = 1;
                                                    $$->s_argp = new_arglist($1);
                                                  }
             ;

basic_item   : F_ID F_LPAR F_RPAR                 {  int tmp;
                                         
                                                     Func_idx--;
                                                     if(Func_idx >= 0 ) {
                                                       tmp = match_function(FuncStack[Func_idx].s_name,0);
                                                       $$ = do_function(tmp, 0, NULL );
                                                       capa_mfree(FuncStack[Func_idx].s_name);
                                                     }
                                                  }
             | F_ID F_LPAR arg_list  F_RPAR       {  int  tmp;
                                         
                                                     Func_idx--;
                                                     if(Func_idx >= 0 ) {
                                                        tmp = match_function(FuncStack[Func_idx].s_name,$3->s_argc);
					                $$ = do_function(tmp, $3->s_argc, $3->s_argp);
					                capa_mfree(FuncStack[Func_idx].s_name);
					                free_arglist($3->s_argp);
							$3->s_argp=NULL;
                                                      }
                                                  }
             | V_ID                               { FMLDBUG_PR3("[V %s = %.16g] ",$1->s_name, $1->s_real);
                                                    $$ = $1;
                                                  }
             | F_PLUS  basic_item                 { $$ = $2; }
             | F_NUMBER                           { FMLDBUG_PR2("[F %.16g] ",$1->s_real);
                                                    $$ = $1;
                                                  }
             | F_LPAR   block  F_RPAR             { $$ = $2; }
             ;
%%
void
fml_error(char *msg)
{
  FormulaParseOK=0;
  printf("Error Parsing: %s\n",msg);
  
}
/* ---------------------------------------------------- */
Symbol* negate(Symbol* symb) 
{
  Symbol* temp=symb;
  switch(symb->s_type) {
  case I_VAR:      temp = (Symbol *)capa_malloc(sizeof(Symbol),1);
    temp->s_type = I_CONSTANT;
  case I_CONSTANT: temp->s_int = - symb->s_int; break;
  case R_VAR: temp = (Symbol *)capa_malloc(sizeof(Symbol),1);
    temp->s_type = R_CONSTANT;
  case R_CONSTANT: temp->s_real =   (-1.0)*(symb->s_real); 
    break;
  case S_VAR:
  case S_CONSTANT: break;
  default:         break;
  }
  return temp;
}

Symbol *
f_symbol_pow(ap,bp) Symbol *ap; Symbol *bp;
{
  Symbol *cp;
  double  a, b;
  int     error = 0;
  
  /*Even if we can't do it we need to return something*/
  cp = (Symbol *)capa_malloc(sizeof(Symbol),1);
  cp->s_type = R_CONSTANT;
  cp->s_real = 0.0;
  switch(ap->s_type) {
     case I_VAR:      a = (double)(ap->s_int);
         break;
     case I_CONSTANT: a = (double)(ap->s_int); capa_mfree((char *)ap);
         break;
     case R_VAR:      a = ap->s_real;
         break;
     case R_CONSTANT: a = ap->s_real;   capa_mfree((char *)ap);
         break;
     case S_VAR:
     case S_CONSTANT: 
     default:         error = 1;  break;
  }
  switch(bp->s_type) {
     case I_VAR:      b = (double)(bp->s_int);
         break;
     case I_CONSTANT: b = (double)(bp->s_int);  capa_mfree((char *)bp);
         break;
     case R_VAR:      b = bp->s_real;
         break;
     case R_CONSTANT: b = bp->s_real;   capa_mfree((char *)bp);
         break;
     case S_VAR:
     case S_CONSTANT: 
     default:         error = 1; break;
  }
  if ((!(((double)((int)b)) == b)) && (a < 0.0)) {
    error = 1;
  }
  if (!error) {
    cp = (Symbol *)capa_malloc(sizeof(Symbol),1);
    cp->s_type = R_CONSTANT;
    cp->s_real = pow(a,b);
  } else {
    FormulaParseOK=0;
  }
  return (cp);
}

/* ============================================================================= */
