/*                                               -*- C++ -*- */
/*
 * @file  csv_parser.yy
 * @brief The parser definition in order to read CSV files
 *
 *  Copyright (C) 2005-2014 Airbus-EDF-Phimeca
 *
 *  This library is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This library 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author schueller
 * @date   2012-02-17 19:35:43 +0100 (Fri, 17 Feb 2012)
 */

/* This parser is based on RFC 4180 from www.ietf.org */

%pure-parser
%parse-param {OT::CSVParserState & theState}
%parse-param {yyscan_t yyscanner}
%parse-param {FILE * theFile}
%parse-param {OT::NumericalSampleImplementation &impl}
%parse-param {OT::UnsignedInteger & theDimension}
%parse-param {const char * theSeparator}
%lex-param   {yyscan_t yyscanner}
%lex-param   {FILE * theFile}
%lex-param   {const char * theSeparator}
%defines
%name-prefix="csv"

%union {
  double real;
  const char * st;
}

%token <real>      REAL
%token <st>        STRING
%token <st>        COMMA
%token             CRLF
%token <st>        SPACE
%token             DOUBLEQUOTE
%token             CHARACTER

%left              COMMA
%left              CRLF

%type <st>         escapedData
%type <st>         data
%type <st>         CRLF
%type <st>         DOUBLEQUOTE
%type <st>         error



%code requires {
#include <string>
#include "csv_parser_state.hxx"
#include "NumericalSampleImplementation.hxx"
typedef void*                 yyscan_t;
}

%code {
#include "Log.hxx"

int yylex                     (YYSTYPE *lvalp, yyscan_t yyscanner, FILE * theFile, const char * theSeparator);
int yyerror                   (OT::CSVParserState & theState, yyscan_t yyscanner, FILE * theFile, OT::NumericalSampleImplementation &impl, OT::UnsignedInteger & theDimension, const char * theSeparator, const char *s);
int csvget_lineno             (yyscan_t yyscanner);


std::string ToString(double val)
{
  std::ostringstream oss;
  oss << val;
  return oss.str();
}

void clearPoint(OT::CSVParserState & theState, yyscan_t yyscanner)
{
  theState.Point = OT::NumericalPoint();
}
void printPoint(OT::CSVParserState & theState, yyscan_t yyscanner, OT::NumericalSampleImplementation &impl, OT::UnsignedInteger & theDimension)
{
  LOGDEBUG(OT::OSS() << "file " << theState.theFileName << " line " << csvget_lineno(yyscanner) << ": Point=" << theState.Point.__repr__());

  if (theDimension == 0) {
    theDimension = theState.Point.getDimension();
    impl.add(theState.Point);
  } else if (theDimension == theState.Point.getDimension()) impl.add(theState.Point);
}


void clearHeader(OT::CSVParserState & theState, yyscan_t yyscanner)
{
  theState.Header = OT::Description();
}
void printHeader(OT::CSVParserState & theState, yyscan_t yyscanner, OT::NumericalSampleImplementation &impl, OT::UnsignedInteger & theDimension)
{
  LOGDEBUG(OT::OSS() << "file " << theState.theFileName << " line " << csvget_lineno(yyscanner) << ": Header=" << theState.Header.__repr__());

  if (theDimension == 0) {
    theDimension = theState.Header.getSize();
    impl.setDescription(theState.Header);
  }
}
}


%start CSVFile
%%

CSVFile:  endOfFile
	| recordSet endOfFile
	;

recordSet: Record 
	|  recordSet CRLF Record 
	;

Record: { clearHeader(theState,yyscanner); clearPoint(theState,yyscanner); theState.errors = false; }
	record
        { if (!theState.errors) { printHeader(theState,yyscanner,impl,theDimension); printPoint(theState,yyscanner,impl,theDimension); } } ;

record:   spacedField
	| record COMMA spacedField
	| record error
	;

spacedField: field
	| spaces field
        | field spaces
        | spaces field spaces
        ;

spaces: SPACE
	| spaces SPACE
	;

field:    { theState.St = ""; } escapedField    { theState.Header.add(theState.St); }
	  | { theState.St = ""; } nonEscapedField { if (theState.Type==OT::CSVParserState::RealField) theState.Point.add(theState.Val); else theState.Header.add(theState.St); }
	;



escapedField: { theState.Type = OT::CSVParserState::StringField; } DOUBLEQUOTE escapedDataList DOUBLEQUOTE ;

escapedDataList: escapedData 
	|        escapedDataList escapedData
	;

escapedData: data
	|    SPACE                   { theState.St += $1; }
	|    COMMA                   { theState.St += $1; }
	|    CRLF                    { theState.St += '\n'; }
	|    DOUBLEQUOTE DOUBLEQUOTE { theState.St += '"'; }
	;

nonEscapedField: { theState.Type = OT::CSVParserState::NotSet; } data 
	|        { theState.Type = OT::CSVParserState::NotSet; } error data
	;

data:     REAL   { if (theState.Type==OT::CSVParserState::NotSet) { theState.Val = $1; theState.Type = OT::CSVParserState::RealField; } else { theState.St += ToString($1); }  }
	| STRING { theState.St += $1; theState.Type = OT::CSVParserState::StringField; }
	;

endOfFile: /* Empty */
	| CRLF
	;

%%

int yyerror(OT::CSVParserState & theState, yyscan_t yyscanner, FILE * theFile, OT::NumericalSampleImplementation &impl, OT::UnsignedInteger & theDimension, const char * theSeparator, const char *s) {
  LOGINFO(OT::OSS() << "file " << theState.theFileName << " line " << csvget_lineno(yyscanner) << " is ignored: " << s);
  theState.errors = true;
  return 0;
}
