/****************************************************************** 
 *
 * calc.y
 *
 *     Yacc grammar for formulas.
 *
 *
 * Author : Dan Gunter
 * Created: 10 September 1998
 *
 * Modification history
 * --------------------
 *
 ****************************************************************** 
 */
%{
/*========= C DECLARATIONS ================================================*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "structs.h"
#include "calculationclass.h"
#include "builtinfn.h"
  
/* tree functions */
void tree_dump( struct t_node *cur, int indent );
struct t_node *op_node( int op,
			struct t_node *l,
			struct t_node *r );
struct t_node *num_node( double n );
struct t_node *var_node( char *vname );

/* simple calcerror */
#define calcerror(s) do {fprintf(stderr,"%s\n",s);} while(0)

  /* make strcmp() friendlier in the common case */
#define STREQ(s1,s2) (strcmp((s1),(s2))==0)

/* Internal functions */
static struct t_node *fn_node( char         *fn_name, 
			       struct t_node *expr );

%}

/*========= YACC DECLARATIONS =============================================*/

%union {
        struct t_node *nodeval;
	double dval;
	char  *string;
}

%token <string> VAR
%token <dval>   NUMBER
%token <string> FUNCTION
%left '-' '+'
%left '*' '/'
%left '^' '%'
%nonassoc UMINUS

%type <nodeval> expression

%%
/*========= YACC GRAMMAR =====================================================*/

calculation: init statement_list
           ;

init: /* empty */ 
{ /* do some init things */
      }
    ;

statement_list:	statement
	|	statement_list statement
	;

statement:  expression  { 
	                  printf("Inorder tree traversal:\n");
                          tree_dump($1, 0);
			  
                        }
	;

expression:  expression '+' expression { $$ = op_node( '+', $1, $3 ); }
|	     expression '-' expression { $$ = op_node( '-', $1, $3 ); } 
|	     expression '*' expression { $$ = op_node( '*', $1, $3 ); }
|            expression '/' expression { $$ = op_node( '/', $1, $3 ); }
|            expression '^' expression { $$ = op_node( '^', $1, $3 ); }
|            expression '%' expression { $$ = op_node( '%', $1, $3 ); }
|	     '(' expression ')'	       { $$ = $2; }
|	     NUMBER                    { $$ = num_node( $1 ); }
|            VAR                       { $$ = var_node( $1 ); }
|            FUNCTION '(' expression ')' { $$ = fn_node( $1, $3 ); }
;

%%

/*========= C IMPLEMENTATIONS =====================================================*/

/*
 *----------------------------------------------------------------------
 *
 * op_node -- Create a new node which is a binary operation.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

struct t_node *op_node( int op,
			struct t_node *l,
			struct t_node *r )
{
  struct t_node *tmpPtr;

  tmpPtr = (struct t_node *)malloc( sizeof(struct t_node));

  /* If both children are numbers, simply do the calculation */
  if ( l->valtype == VAL_NUM && r->valtype == VAL_NUM ) {
    switch( op ) {
    case '+' : 
      tmpPtr = num_node( l->val.number + r->val.number );
      break;
    case '-' :
      tmpPtr = num_node( l->val.number - r->val.number );
      break;
    case '*' :
      tmpPtr = num_node( l->val.number * r->val.number );
      break;
    case '/' : 
      if ( r->val.number == 0. )
	calcerror("Division by zero (0).\n");
      else
	tmpPtr = num_node( l->val.number / r->val.number );
      break;
    case '^':
      tmpPtr = num_node( pow( l->val.number, r->val.number ) );
      break;
    case '%':
      tmpPtr = num_node( ((int)l->val.number) % ((int)r->val.number) );
      break;
    }
    /* These nodes are no longer needed */
    free( l );
    free( r );
  }
  else {
    tmpPtr->valtype = VAL_OP;
    tmpPtr->val.op = op;
    tmpPtr->left = l; 
    tmpPtr->right = r;
  }

  return tmpPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * num_node -- Create a new node which is a constant (double precision).
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

struct t_node *num_node( double n )
{
  struct t_node *tmpPtr;

  tmpPtr = (struct t_node *)malloc( sizeof(struct t_node));
  tmpPtr->valtype = VAL_NUM;
  tmpPtr->val.number = n;
  tmpPtr->left = tmpPtr->right = NULL;

  return tmpPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * var_node -- Create a node which has a variable (or "parameter") 
 *             in it.
 *             Free 'vname' parameter before exiting.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

struct t_node *var_node( char *vname )
{
  struct t_node *tmpPtr;
  Nlv_CalcVar   *varPtr;
  char          *comma;

  tmpPtr = (struct t_node *)malloc( sizeof(struct t_node));
  tmpPtr->valtype = VAL_PARAM;

  /* Create new variable record */
  varPtr = ( Nlv_CalcVar * )malloc( sizeof(Nlv_CalcVar) );
  varPtr->ksIndex = 0; /** WIP **/
  varPtr->kIndex  = 0; /** WIP **/
  if ( (comma = strchr(vname,',')) != NULL )
    varPtr->fName = (char *)strdup( comma+1 );
  else
    varPtr->fName = NULL;
  varPtr->value = 0.;
  varPtr->mask  = 0;

  tmpPtr->val.param = (void *)varPtr;
  tmpPtr->left = tmpPtr->right = NULL;

  free( vname );

  return tmpPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * fn_node -- Create a node which is a function operating on the
 *            result of an expression. The expression is found
 *            on the left subtree, by convention.
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

struct t_node *fn_node( char         *fn_name, 
			 struct t_node *expr )
{
  struct t_node *tmpPtr;
  char           buf[ 512 ];

  tmpPtr = (struct t_node *)malloc( sizeof(struct t_node));
  tmpPtr->valtype = VAL_FN;
  if ( STREQ( fn_name, "mean" ) ) {
    tmpPtr->val.fn.calc = Nlv_CalcMean;
    tmpPtr->val.fn.clear = Nlv_ClearMean;
  }
  else if ( STREQ( fn_name, "sdev" ) ) {
    tmpPtr->val.fn.calc  = Nlv_CalcStddev;
    tmpPtr->val.fn.clear = Nlv_ClearStddev;
  }
  else {
    sprintf(buf,"Unknown function '%s'\n", fn_name ); 
    calcerror( buf );
  }
  tmpPtr->left  = expr;
  tmpPtr->right = NULL;

  return tmpPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * tree_dump -- Dump the tree [debugging hook].
 *
 * Calls:
 *
 * Algorithm:
 *
 *----------------------------------------------------------------------
 */

void tree_dump( struct t_node *cur, int indent )
{
  char *s;

  if ( !cur )
    return;
  tree_dump( cur->left, indent+1 );
  switch ( cur->valtype ) {
  case VAL_NUM:
    printf("%lf\n",cur->val.number);
    break;
  case VAL_OP:
    printf("%c\n",cur->val.op );
    break;
  case VAL_PARAM:
    s = ((Nlv_CalcVar *)cur->val.param)->fName;
    printf("%s\n", s ? s : "(null)" );
    break;
  }
  tree_dump( cur->right, indent+1 );

  return;
}
