%{
/* NdBox = [(lowerleft),(upperright)] */
/* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */

#define YYERROR_VERBOSE   
#define YYPARSE_PARAM result  /* need this to pass a pointer (void *) to yyparse */
#define YYSTYPE char *
#define YYDEBUG 1

#include "postgres.h"

#include "cubedata.h"
#include "buffer.h"

#include "utils/palloc.h"
#include "utils/elog.h"

#undef yylex                 /* falure to redefine yylex will result in a call to  the */
#define yylex cube_yylex     /* wrong scanner when running inside the postgres backend  */

extern int yylex();           /* defined as cube_yylex in cubescan.c */
extern int errno;

int cube_yyerror( char *msg );
int cube_yyparse(void *result);

static int delim_count(char *s, char delim);
static NDBOX * write_box(unsigned int dim, char *str1, char *str2);
static NDBOX * write_point_as_box(char *s, int dim);

%}

/* BISON Declarations */
%token FLOAT O_PAREN C_PAREN O_BRACKET C_BRACKET COMMA
%start box

/* Grammar follows */
%%

box:
          O_BRACKET paren_list COMMA paren_list C_BRACKET {

	    int dim;
	    int c = parse_buffer_curr_char();
	    int pos = parse_buffer_pos();

	    /* We can't let the parser recognize more than one valid expression:
	       the job is done and memory is allocated. */
	    if ( c != '\0' ) {
	      /* Not at EOF */
	      reset_parse_buffer();     
	      elog(ERROR, "(0) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c );
	      YYERROR;
	    }
	    
	    dim = delim_count($2, ',') + 1;
	    if ( (delim_count($4, ',') + 1) != dim ) {
	      reset_parse_buffer();     
	      elog(ERROR, "(1) bad cube representation; different point dimensions in (%s) and (%s)\n", $2, $4);
	      YYABORT;
	    }
	    if (dim > CUBE_MAX_DIM) {
              reset_parse_buffer();
              elog(ERROR, "(8) bad cube representation; more than %d dimensions\n", CUBE_MAX_DIM);
              YYABORT;
            }
	    
	    *((void **)result) = write_box( dim, $2, $4 );
    
          }
      |
          paren_list COMMA paren_list {
	    int dim;
	    int c = parse_buffer_curr_char();
	    int pos = parse_buffer_pos();

	    if ( c != '\0' ) {  /* Not at EOF */
	      reset_parse_buffer();     
	      elog(ERROR, "(2) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c );
	      YYABORT;
	    }

	    dim = delim_count($1, ',') + 1;
	    
	    if ( (delim_count($3, ',') + 1) != dim ) {
	      reset_parse_buffer();     
	      elog(ERROR, "(3) bad cube representation; different point dimensions in (%s) and (%s)\n", $1, $3);
	      YYABORT;
	    }
	    if (dim > CUBE_MAX_DIM) {
              reset_parse_buffer();
              elog(ERROR, "(8) bad cube representation; more than %d dimensions\n", CUBE_MAX_DIM);
              YYABORT;
            }
	    
	    *((void **)result) = write_box( dim, $1, $3 );
          }
      |

          paren_list {
            int dim;
	    int c = parse_buffer_curr_char();
	    int pos = parse_buffer_pos();

	    if ( c != '\0') {  /* Not at EOF */
	      reset_parse_buffer();     
	      elog(ERROR, "(4) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c );
	      YYABORT;
	    }

	    if ( yychar != YYEOF) {
	      /* There's still a lookahead token to be parsed */
	      reset_parse_buffer();     
	      elog(ERROR, "(5) bad cube representation; garbage at or before char %d, ('end of input', \\%03o)\n", pos, c);
	      YYABORT;
	    }

            dim = delim_count($1, ',') + 1;
	    if (dim > CUBE_MAX_DIM) {
              reset_parse_buffer();
              elog(ERROR, "(8) bad cube representation; more than %d dimensions\n", CUBE_MAX_DIM);
              YYABORT;
            }

	    *((void **)result) = write_point_as_box($1, dim);
          }

      |

          list {
            int dim;
	    int c = parse_buffer_curr_char();
	    int pos = parse_buffer_pos();

	    if ( c != '\0') {  /* Not at EOF */
	      reset_parse_buffer();
	      elog(ERROR, "(6) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c);
	      YYABORT;
	    }

	    if ( yychar != YYEOF) {
	      /* There's still a lookahead token to be parsed */
	      reset_parse_buffer();
	      elog(ERROR, "(7) bad cube representation; garbage at or before char %d, ('end of input', \\%03o)\n", pos, c);
	      YYABORT;
	    }

            dim = delim_count($1, ',') + 1;
	    if (dim > CUBE_MAX_DIM) {
              reset_parse_buffer();
              elog(ERROR, "(8) bad cube representation; more than %d dimensions\n", CUBE_MAX_DIM);
              YYABORT;
            }
	    *((void **)result) = write_point_as_box($1, dim);
          }
      ;

paren_list:
          O_PAREN list C_PAREN {
             $$ = $2;
	  }
      ;

list:
          FLOAT {
             $$ = palloc(strlen(parse_buffer()) + 1);
	     strcpy($$, $1);
	  }
      | 
	  list COMMA FLOAT {
             $$ = $1;
	     strcat($$, ",");
	     strcat($$, $3);
	  }
      ;

%%


int cube_yyerror ( char *msg ) {
  char *buf = (char *) palloc(256);
  int position;

  yyclearin;

  if ( !strcmp(msg, "parse error, expecting `$'") ) {
    msg = "expecting end of input";
  }

  position = parse_buffer_pos() > parse_buffer_size() ? parse_buffer_pos() - 1 : parse_buffer_pos();

  snprintf(
	  buf, 
	  256,
	  "%s at or before position %d, character ('%c', \\%03o), input: '%s'\n", 
	  msg,
	  position,
	  parse_buffer()[position - 1],
	  parse_buffer()[position - 1],
	  parse_buffer()
	  );

  reset_parse_buffer();     
  elog(ERROR, buf);
  return 0;
}

static int
delim_count(char *s, char delim)
{
      int        ndelim = 0;

      while ((s = strchr(s, delim)) != NULL)
      {
        ndelim++;
        s++;
      }
      return (ndelim);
}

static NDBOX * 
write_box(unsigned int dim, char *str1, char *str2)
{
  NDBOX * bp;
  char * s;
  int i; 
  int size = offsetof(NDBOX, x[0]) + sizeof(double) * dim * 2;
	    
  bp = palloc(size);
  memset(bp, 0, size);
  bp->size = size;
  bp->dim = dim;
	    
  s = str1;
  bp->x[i=0] = strtod(s, NULL);
  while ((s = strchr(s, ',')) != NULL) {
    s++; i++;
    bp->x[i] = strtod(s, NULL);
  }	
  
  s = str2;
  bp->x[i=dim] = strtod(s, NULL);
  while ((s = strchr(s, ',')) != NULL) {
    s++; i++;
    bp->x[i] = strtod(s, NULL);
  }	

  return(bp);
}


static NDBOX * write_point_as_box(char *str, int dim)
{
  NDBOX * bp;
  int i, size;
  double x;
  char * s = str;
  
  size = offsetof(NDBOX, x[0]) + sizeof(double) * dim * 2;

  bp = palloc(size);
  memset(bp, 0, size);
  bp->size = size;
  bp->dim = dim;
  
  i = 0;
  x = strtod(s, NULL);
  bp->x[0] = x;
  bp->x[dim] = x;
  while ((s = strchr(s, ',')) != NULL) {
    s++; i++;
    x = strtod(s, NULL);
    bp->x[i] = x;
    bp->x[i+dim] = x;
  }	

  return(bp);
}

