// Copyright (C) 1999-2015
// Smithsonian Astrophysical Observatory, Cambridge, MA, USA
// For conditions of distribution and use, see copyright notice in "copyright"

%pure-parser
%parse-param {FitsFile* ff}
%lex-param {ffFlexLexer* ll}
%parse-param {ffFlexLexer* ll}

%{
#define YYDEBUG 1

#define GOTOFILT(x) {yyclearin; ffFilter(x);}
#define GOTOARR(x) {yyclearin; ffArray(x);}

#include "file.h"
#include "hpx.h"

#undef yyFlexLexer
#define yyFlexLexer ffFlexLexer
#include <FlexLexer.h>

extern int fflex(void*, ffFlexLexer*);
extern void fferror(FitsFile*, ffFlexLexer*, const char*);

char ff_filter[512];
extern void ffFilter(int);
extern void ffArray(int);

%}

%union {
  float real;
  int integer;
  char str[256];
  void* ptr;
}

%type <integer> endian

%token <integer> INT
%token <integer> INTP
%token <str> STRING

%token ARCH_
%token ARRAY_
%token BIG_
%token BIGENDIAN_
%token BIN_
%token BINKEY_
%token BINCOL_
%token BITPIX_
%token COL_
%token DIM_
%token DIMS_
%token ECLIPTIC_
%token ENDIAN_
%token EQUATORIAL_
%token GALACTIC_
%token KEY_
%token LAYOUT_
%token LITTLE_
%token LITTLEENDIAN_
%token NESTED_
%token NORTH_
%token ORDER_
%token QUAD_
%token RING_
%token SKIP_
%token SOUTH_
%token SYSTEM_
%token UNKNOWN_
%token XDIM_
%token YDIM_
%token ZDIM_

%%

// start: {yydebug=1;} command

// assume any error is the start of a filter
command	: filename
	| filename ext
 	| filename ext sect
	| filename ext bin
 	| filename ext sect bin
	| filename bin
	| filename bin sect
 	| filename sect
 	| filename '[' extb sectb ']'
 	| filename '[' extb binb ']'
	| filename '[' arrs ']'
	| filename '[' arrs ']' sect
	| filename '[' ARRAY_ {GOTOARR(0)} '(' array ')' ']'
	| filename '[' hpxs ']'
	| filename '[' hpxs ']' sect
	| filename '[' extb hpxs ']'
	| error {GOTOFILT(0)} STRING 
	  {ff->setValid(1);ff->setpFilter(ff_filter);}
	;

filename : /* empty */
        |  STRING {ff->setpName($1);}
	;

// we must do it this way so that a bare filter will be accepted
//ext	: '[' extb ']'
//      ;
//extb	: STRING {ff->setpExt($1);}
//	| INT {ff->setpIndex($1);}
//	;

ext	: '[' STRING ']' {ff->setpExt($2);}
        | '[' INT ']' {ff->setpIndex($2);}
        ;

extb	: STRING ',' {ff->setpExt($1);}
        | INT ',' {ff->setpIndex($1);}
	;

sect	: '[' sectb ']'

sectb	: rangex ',' rangey
	| rangex ',' rangey ',' rangez
	| INT '@' INT '@' INT
	{
	  ff->setXMin($3-$1/2); ff->setXMax($3+$1/2); ff->setXValid(1);
	  ff->setYMin($5-$1/2); ff->setYMax($5+$1/2); ff->setYValid(1);
 	}
	| INT '@' INT '@' INT '@' INT
	{
	  ff->setXMin($3-$1/2); ff->setXMax($3+$1/2); ff->setXValid(1);
	  ff->setYMin($5-$1/2); ff->setYMax($5+$1/2); ff->setYValid(1);
	  ff->setZMin($7-$1/2); ff->setZMax($7+$1/2); ff->setZValid(1);
 	}
	| INT '@' INT '@' INTP
	{
	  ff->setXMin($3-$1/2); ff->setXMax($3+$1/2); ff->setXValid(1);
	  ff->setYMin($5-$1/2); ff->setYMax($5+$1/2); ff->setYValid(1);
	  ff->setCoord(1);
 	}
	| INT '@' INT '@' INT '@' INTP
	{
	  ff->setXMin($3-$1/2); ff->setXMax($3+$1/2); ff->setXValid(1);
	  ff->setYMin($5-$1/2); ff->setYMax($5+$1/2); ff->setYValid(1);
	  ff->setZMin($7-$1/2); ff->setZMax($7+$1/2); ff->setZValid(1);
	  ff->setCoord(1);
 	}
	;

rangex	: INT ':' INT 
	  {ff->setXMin($1); ff->setXMax($3); ff->setXValid(1);}
	| INT ':' INTP
	{
	  ff->setXMin($1); ff->setXMax($3); ff->setXValid(1);
	  ff->setCoord(1);
	}
	| INT '@' INT 
	  {ff->setXMin($3-$1/2); ff->setXMax($3+$1/2); ff->setXValid(1);}
	| INT '@' INTP
	{
	  ff->setXMin($3-$1/2); ff->setXMax($3+$1/2); ff->setXValid(1);
	  ff->setCoord(1);
	}
	| '*' {ff->setXValid(0);}
	;

rangey	: INT ':' INT
	  {ff->setYMin($1); ff->setYMax($3); ff->setYValid(1);}
	| INT ':' INTP
	{
	  ff->setYMin($1); ff->setYMax($3); ff->setYValid(1);
	  ff->setCoord(1);
	}
	| INT '@' INT
	  {ff->setYMin($3-$1/2); ff->setYMax($3+$1/2); ff->setYValid(1);}
	| INT '@' INTP
	{
	  ff->setYMin($3-$1/2); ff->setYMax($3+$1/2); ff->setYValid(1);
	  ff->setCoord(1);
	}
	| '*' {ff->setYValid(0);}
	;

rangez	: INT ':' INT
	  {ff->setZMin($1); ff->setZMax($3); ff->setZValid(1);}
	| INT ':' INTP
	{
	  ff->setZMin($1); ff->setZMax($3); ff->setZValid(1);
	  ff->setCoord(1);
	}
	| INT '@' INT
	  {ff->setZMin($3-$1/2); ff->setZMax($3+$1/2); ff->setZValid(1);}
	| INT '@' INTP
	{
	  ff->setZMin($3-$1/2); ff->setZMax($3+$1/2); ff->setZValid(1);
	  ff->setCoord(1);
	}
	| '*' {ff->setZValid(0);}
	;

bin	: '[' binb ']'

binb	: binword '=' binkey
	| binword binkey
	;

binword	: BIN_
	| BINKEY_
	| BINCOL_
	| KEY_
	;

binkey	: '(' STRING ',' STRING ')' {ff->setpBinXY($2,$4);}
	| STRING ',' STRING {ff->setpBinXY($1,$3);}
	| '(' STRING ',' STRING ',' STRING ')' {ff->setpBinXYZ($2,$4,$6);}
	| STRING ',' STRING ',' STRING {ff->setpBinXYZ($1,$3,$5);}
	| '(' STRING ')' {ff->setpBinZ($2);}
	| STRING {ff->setpBinZ($1);}
	;

arrs	: arrs ',' arr
	| arr
	;

arr	: XDIM_ '=' INT {ff->setpWidth($3);}
	| YDIM_ '=' INT {ff->setpHeight($3);}
	| ZDIM_ '=' INT {ff->setpDepth($3);}
	| DIM_ '=' INT {ff->setpWidth($3);ff->setpHeight($3);}
	| DIMS_ '=' INT {ff->setpWidth($3);ff->setpHeight($3);}
	| BITPIX_ '=' INT {ff->setpBitpix($3);}
	| SKIP_ '=' INT {ff->setpSkip($3);}
        | ARCH_ '=' endian {ff->setpArch((FitsFile::ArchType)$3);} 
	| ENDIAN_ '=' endian {ff->setpArch((FitsFile::ArchType)$3);} 
	| endian {ff->setpArch((FitsFile::ArchType)$1);} 
	;

endian	: BIG_ {$$ = FitsFile::BIG;}
	| BIGENDIAN_ {$$ = FitsFile::BIG;}
	| LITTLE_ {$$ = FitsFile::LITTLE;}
	| LITTLEENDIAN_ {$$ = FitsFile::LITTLE;}
        ;

array	: atype adims askip aendian
	;

atype	: 'b' {ff->setpBitpix(8);}
	| 's' {ff->setpBitpix(16);}
	| 'u' {ff->setpBitpix(-16);}
	| 'i' {ff->setpBitpix(32);}
	| 'l' {ff->setpBitpix(64);}
	| 'r' {ff->setpBitpix(-32);}
	| 'f' {ff->setpBitpix(-32);}
	| 'd' {ff->setpBitpix(-64);}
	;

adims	: INT {ff->setpWidth($1);ff->setpHeight($1);}
	| INT '.' INT {ff->setpWidth($1);ff->setpHeight($3);}
	| INT '.' INT '.' INT 
	  {ff->setpWidth($1);ff->setpHeight($3);ff->setpDepth($5);}
	;

askip	: /* empty */
	| ':' INT {ff->setpSkip($2);}
	;

aendian	: /* empty */
	| 'l' {ff->setpArch(FitsFile::LITTLE);}
	| 'b' {ff->setpArch(FitsFile::BIG);}
	;

hpxs	: hpxs ',' hpx
	| hpx
	;

hpx	: SYSTEM_ '=' hpxSystem
	| ORDER_ '=' hpxOrder
	| LAYOUT_ '=' hpxLayout
	| COL_ '=' INT {ff->setpHPXColumn($3);}
	| QUAD_ '=' INT {ff->setpHPXQuad($3);}
	;

hpxSystem : EQUATORIAL_ {ff->setpHPXSystem(FitsHPX::EQU);}
	  | GALACTIC_ {ff->setpHPXSystem(FitsHPX::GAL);}
	  | ECLIPTIC_ {ff->setpHPXSystem(FitsHPX::ECL);}
	  | UNKNOWN_ {ff->setpHPXSystem(FitsHPX::UNKNOWN);}
	  ;

hpxOrder : RING_ {ff->setpHPXOrder(FitsHPX::RING);}
	 | NESTED_ {ff->setpHPXOrder(FitsHPX::NESTED);}
	 ;

hpxLayout : EQUATORIAL_ {ff->setpHPXLayout(FitsHPX::EQUATOR);}
	  | NORTH_ {ff->setpHPXLayout(FitsHPX::NORTH);}
	  | SOUTH_ {ff->setpHPXLayout(FitsHPX::SOUTH);}
	  ;

%%
