/*
** Parser for the mapserver
*/

%{
/* C declarations */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include "map.h" /* for TRUE/FALSE and REGEX includes */
#include "maptime.h" /* for time comparison routines */

extern int msyylex(); /* lexer globals */
extern int msyyerror();

int msyyresult;
%}

/* Bison/Yacc declarations */

%union {
  double dblval;
  int intval;  
  char *strval;  
  struct tm tmval;
}

%token <dblval> NUMBER
%token <strval> STRING
%token <tmval> TIME
%token <strval> REGEX
%left OR
%left AND
%left NOT
%left RE EQ NE LT GT LE GE IN
%left LENGTH
%left '+' '-'
%left '*' '/'
%left NEG
%right '^'

%type <intval> logical_exp
%type <dblval> math_exp
%type <strval> string_exp
%type <strval> regular_exp
%type <tmval> time_exp

/* Bison/Yacc grammar */

%%

input: /* empty string */
       | logical_exp      { 
			    msyyresult = $1; 
			  }
       | math_exp	  {
			    if($1 != 0)
			      msyyresult = MS_TRUE;
			    else
			      msyyresult = MS_FALSE;			    
			  }
;

regular_exp: REGEX ;

logical_exp:
       logical_exp OR logical_exp      {
	                                 if($1 == MS_TRUE)
		                           $$ = MS_TRUE;
		                         else if($3 == MS_TRUE)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | logical_exp AND logical_exp   {
	                                 if($1 == MS_TRUE) {
			                   if($3 == MS_TRUE)
			                     $$ = MS_TRUE;
			                   else
			                     $$ = MS_FALSE;
			                 } else
			                   $$ = MS_FALSE;
		                       }
       | logical_exp OR math_exp       {
	                                 if($1 == MS_TRUE)
		                           $$ = MS_TRUE;
		                         else if($3 != 0)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | logical_exp AND math_exp      {
	                                 if($1 == MS_TRUE) {
			                   if($3 != 0)
			                     $$ = MS_TRUE;
			                   else
			                     $$ = MS_FALSE;
			                 } else
			                   $$ = MS_FALSE;
		                       }
       | math_exp OR logical_exp       {
	                                 if($1 != 0)
		                           $$ = MS_TRUE;
		                         else if($3 == MS_TRUE)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | math_exp AND logical_exp      {
	                                 if($1 != 0) {
			                   if($3 == MS_TRUE)
			                     $$ = MS_TRUE;
			                   else
			                     $$ = MS_FALSE;
			                 } else
			                   $$ = MS_FALSE;
		                       }
       | math_exp OR math_exp       {
	                                 if($1 != 0)
		                           $$ = MS_TRUE;
		                         else if($3 != 0)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | math_exp AND math_exp      {
	                                 if($1 != 0) {
			                   if($3 != 0)
			                     $$ = MS_TRUE;
			                   else
			                     $$ = MS_FALSE;
			                 } else
			                   $$ = MS_FALSE;
		                       }
       | NOT logical_exp	       { $$ = !$2; }
       | NOT math_exp	       	       { $$ = !$2; }
       | string_exp RE regular_exp     {
                                         regex_t re;

                                         if(regcomp(&re, $3, REG_EXTENDED|REG_NOSUB) != 0) 
                                           $$ = MS_FALSE;

                                         if(regexec(&re, $1, 0, NULL, 0) == 0)
                                  	   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;

                                         regfree(&re);
                                       }
       | math_exp EQ math_exp          {
	                                 if($1 == $3)
	 		                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | math_exp NE math_exp          {
	                                 if($1 != $3)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       } 
       | math_exp GT math_exp          {	                                 
	                                 if($1 > $3)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | math_exp LT math_exp          {
	                                 if($1 < $3)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | math_exp GE math_exp          {	                                 
	                                 if($1 >= $3)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | math_exp LE math_exp          {
	                                 if($1 <= $3)
			                   $$ = MS_TRUE;
			                 else
			                   $$ = MS_FALSE;
		                       }
       | '(' logical_exp ')'           { $$ = $2; }
       | string_exp EQ string_exp      {
                                         if(strcmp($1, $3) == 0)
					   $$ = MS_TRUE;
					 else
					   $$ = MS_FALSE;
				       }
       | string_exp NE string_exp      {
                                         if(strcmp($1, $3) != 0)
					   $$ = MS_TRUE;
					 else
					   $$ = MS_FALSE;
				       }
       | string_exp GT string_exp      {
                                         if(strcmp($1, $3) > 0)
					   $$ = MS_TRUE;
					 else
					   $$ = MS_FALSE;
                                       }
       | string_exp LT string_exp      {
                                         if(strcmp($1, $3) < 0)
					   $$ = MS_TRUE;
					 else
					   $$ = MS_FALSE;
                                       }
       | string_exp GE string_exp      {
                                         if(strcmp($1, $3) >= 0)
					   $$ = MS_TRUE;
					 else
					   $$ = MS_FALSE;
                                       }
       | string_exp LE string_exp      {
                                         if(strcmp($1, $3) <= 0)
					   $$ = MS_TRUE;
					 else
					   $$ = MS_FALSE;
                                       } 
       | time_exp EQ time_exp      {
                                     if(msTimeCompare(&($1), &($3)) == 0)
				       $$ = MS_TRUE;
				     else
				       $$ = MS_FALSE;
				   }
       | time_exp NE time_exp      {
                                     if(msTimeCompare(&($1), &($3)) != 0)
				       $$ = MS_TRUE;
				     else
				       $$ = MS_FALSE;
				   }
       | time_exp GT time_exp      {
                                     if(msTimeCompare(&($1), &($3)) > 0)
				       $$ = MS_TRUE;
				     else
				       $$ = MS_FALSE;
                                   }
       | time_exp LT time_exp      {
                                     if(msTimeCompare(&($1), &($3)) < 0)
				       $$ = MS_TRUE;
				     else
				       $$ = MS_FALSE;
                                   }
       | time_exp GE time_exp      {
                                     if(msTimeCompare(&($1), &($3)) >= 0)
				       $$ = MS_TRUE;
				     else
				       $$ = MS_FALSE;
                                   }
       | time_exp LE time_exp      {
                                     if(msTimeCompare(&($1), &($3)) <= 0)
				       $$ = MS_TRUE;
				     else
				       $$ = MS_FALSE;
                                   }
       | string_exp IN string_exp      {
					 char *delim,*bufferp;

					 $$ = MS_FALSE;
					 bufferp=$3;

					 while((delim=strchr(bufferp,',')) != NULL) {
					   *delim='\0';
					   if(strcmp($1,bufferp) == 0) {
					     $$ = MS_TRUE;
					     break;
					   } 
					   *delim=',';
					   bufferp=delim+1;
					 }

					 if(strcmp($1,bufferp) == 0) // is this test necessary?
					   $$ = MS_TRUE;
				       }
       | math_exp IN string_exp        {
					 char *delim,*bufferp;

					 $$ = MS_FALSE;
					 bufferp=$3;

					 while((delim=strchr(bufferp,',')) != NULL) {
					   *delim='\0';
					   if($1 == atof(bufferp)) {
					     $$ = MS_TRUE;
					     break;
					   } 
					   *delim=',';
					   bufferp=delim+1;
					 }

					 if($1 == atof(bufferp)) // is this test necessary?
					   $$ = MS_TRUE;
				       }
;

math_exp: NUMBER
       | math_exp '+' math_exp   { $$ = $1 + $3; }
       | math_exp '-' math_exp   { $$ = $1 - $3; }
       | math_exp '*' math_exp   { $$ = $1 * $3; }
       | math_exp '/' math_exp   {
	                           if($3 == 0.0) {
				     msyyerror("division by zero");
				     return(-1);
				   } else
	                             $$ = $1 / $3; 
				 }
       | '-' math_exp %prec NEG  { $$ = $2; }
       | math_exp '^' math_exp   { $$ = pow($1, $3); }
       | '(' math_exp ')'        { $$ = $2; }
       | LENGTH '(' string_exp ')' { $$ = strlen($3); }
;

string_exp: STRING
            | '(' string_exp ')'        { $$ = $2; }
            | string_exp '+' string_exp { sprintf($$, "%s%s", $1, $3); }
;

time_exp: TIME
          | '(' time_exp ')'        { $$ = $2; }
;

%%
