/**************************************************************************
// Copyright (c) 2004  Max-Planck-Institut Saarbruecken (Germany)
// All rights reserved.
//
// This file is part of BenchmarkParser
//
// $URL$
// $Id$
// SPDX-License-Identifier: LGPL-3.0-or-later
// 
//
// Author(s) : Lutz Kettner
//             Franziska Ebert <febert@mpi-sb.mpg.de>
**************************************************************************/

%{
/* C/C++ declaration section */
/* ========================= */
//#include <cstdlib>  /* for atoi */
#include <string>    /* for std::string */
#include <fstream>   /* for std::ifstream */
#include <benchmark_parser.h>
#include <benchmark_visitor.h>
#include <iostream>
static Benchmark_visitor* visitor; /* global visitor used during parsing */

/* declaration for flex parser call yylex */
int yylex( void);

/* error function called for parse errors */
void yyerror( char *s) { visitor->parse_error( std::string(s)); }

/* Use C++ std::string as semantic value to communicate with lexer */
#define YYSTYPE std::string

// Public parser interface, check with decl. in benchmark_parser.h
// ---------------------------------------------------------------

// Opens file 'name' and parses it. Uses visitor 'v' while parsing.
// Returns false if something went wrong. See the visitor for details.
bool benchmark_parse_file( std::string name, Benchmark_visitor* v);

// Starts parsing from stream 'in' with the associated filename 'name'
// (or analogous meaning for different streams) counting linenumbers
// starting from 'n'. Uses visitor 'v' while parsing. Returns false if
// something went wrong. See the visitor for details of the error reporting.
bool benchmark_parse_stream( std::istream&  in, std::string name, 
                             Benchmark_visitor* v, int n);

%}

/* Elementary data types */
/* --------------------- */
%token INTEGER
%token FNUMBER
%token STRING
%token ERROR
%token UNKNOWN_TOKEN
%token MINUS_INFTY
%token PLUS_INFTY
%token COUNTERCLOCKWISE
%token CLOCKWISE
%token VOID

/* Structure tokens */
/* ---------------- */
%token FileFormat
%token BenchmarkName
%token Classification

%token List
%token Rational
%token Polynomial_1
%token Point_2
%token AlgebraicReal


%token ConicPoint_2
%token LineSegment_2
%token Conic_2

%token CircularArc_2
%token LineArc_2
%token CircularPoint_2

%token ConicArc_2
%token Circle_2
%token Cubic_2


%token Quadric_3


%%
/* Grammar */
/* ======= */

input: /* an input is a potentially empty sequence of files */
  /* empty */
  | error                       { /* parse error restart here */ }
  | input file
;

file: /* must start with unique header */
    file_format file_header_options file_classification file_body
;

file_format: /* mandatory fileformat descriptor */
    error_rules                 {}
  | FileFormat '(' STRING ',' INTEGER ',' INTEGER ')' 
                                { visitor->accept_file_format( $3, 
                                      atoi( $5.c_str()), atoi( $7.c_str()),
                                      std::string("")); }
  | FileFormat '(' STRING ',' INTEGER ',' INTEGER ',' STRING ')' 
                                { visitor->accept_file_format( $3, 
                                     atoi( $5.c_str()), atoi( $7.c_str()),
                                                              $9); }    
;

file_header_options: /* sequence of optional file header entries */
    /* empty */
  | file_header_options file_header_option
;

file_header_option: /* single optional file header entry */
    BenchmarkName '(' STRING ')' { visitor->accept_benchmark_name( $3); } 
;

file_classification: /* */
    error_rules             {}
  | classificat 
  | file_classification classificat 
;

classificat: /* */
  Classification '(' STRING ',' STRING ',' STRING ',' STRING ',' 
                     STRING ',' STRING ')'
                            { visitor->accept_classification( $3, $5, $7, 
                                                              $9, $11, $13); }
;

file_body: /* sequence of statements (stmt) */
    /* empty */
  | file_body stmt 
;

stmt_sequence: /* comma separated sequence of statements (stmt) */
    /* empty */
  | stmt_sequence_non_empty
;

stmt_sequence_non_empty: /* comma separated sequence of statements (stmt) */
    stmt
  | stmt_sequence_non_empty ',' stmt
;

stmt:
    error                        { /* parse error restart here */ }
    error_rules                  {}
  | List                         { visitor->begin_list(); }
       '(' stmt_sequence ')'     { visitor->end_list(); }
  | Circle_2      		 { visitor->begin_circle_2(); } 
       '(' circle_2 ')'   	 { visitor->end_circle_2(); }
  | Conic_2 conic_2
  | LineSegment_2                { visitor->begin_line_segment_2(); } 
       '(' point_2 ',' point_2 ')' 
                                 { visitor->end_line_segment_2(); } 
  | ConicArc_2 conic_arc_2 

  | Cubic_2 '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ',' 
                INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
                                 { visitor->accept_cubic_2( $3, $5, $7, $9, 
                                                            $11, $13, $15, 
                                                            $17, $19, $21); }
  | Quadric_3 '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ',' 
                  INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
                                 { visitor->accept_quadric_3( $3, $5, $7, $9, 
                                                              $11, $13, $15, 
                                                              $17, $19, $21); }

  | CircularPoint_2 		{visitor->begin_CircularPoint_2();}
	'('circular_arc_point')'
				{visitor->end_CircularPoint_2();}
  | LineArc_2			{visitor->begin_LineArc_2();}
	'('line_arc_2')'	
				{visitor->end_LineArc_2();}
  |CircularArc_2         	{ visitor->begin_CircularArc_2();} 
      '('circular_arc_2 ')'  
                                 { visitor->end_CircularArc_2();} 
;
circular_arc_point:
circular_arc_point:
error_rules{}
 | point_2
 | AlgebraicReal ',' AlgebraicReal
;
line_arc_2:
error_rules {}
 | LineSegment_2
 | point_2 ',' point_2 
 | CircularPoint_2 '(' circular_arc_point ')' 
       ',' CircularPoint_2 '(' circular_arc_point ')'
;
circular_arc_2:
error_rules {}
 | Circle_2 '(' circle_2 ')'
 | Circle_2 '(' circle_2 ')' ',' CircularPoint_2 '(' circular_arc_point ')' 
       ',' CircularPoint_2 '(' circular_arc_point ')'
 | point_2 ',' point_2 ',' rational
;
circle_2:
error_rules {}
|  point_2 ',' rational  
|  point_2 ',' INTEGER  { visitor->accept_integer($3); } 

;
conic_2: /* */
    error_rules                  {}

  | '(' INTEGER ',' INTEGER ',' INTEGER ',' 
        INTEGER ',' INTEGER ',' INTEGER ')' 
                                 { visitor->accept_conic_2( $2, $4, $6, 
                                                            $8, $10, $12); }


;
conic_arc_2: /* */
     error_rules {}
  | '(' Conic_2                  { visitor->begin_conic_arc_2(); } 
            conic_2
        ',' ConicPoint_2 conic_point_2 
        ',' ConicPoint_2 conic_point_2
	',' orientation          { visitor->accept_orientation( $12); }
     ')' 
                                 { visitor->end_conic_arc_2(); }


  | '(' ConicPoint_2             { visitor->begin_conic_arc_2(); }
            conic_point_2 ')'
                                 { visitor->end_conic_arc_2(); }
            
;

integer_sequence1: /* comma separated integers */
    error_rules                   {}
  | INTEGER                       { visitor->accept_integer( $1); }
  | integer_sequence1 ',' INTEGER { visitor->accept_integer( $3); }
;


point_2: /* a 2d point */
    error_rules                   {}
  | Point_2 '(' INTEGER ',' INTEGER ')' 
                                  { visitor->accept_point_2( $3, $5); }
  | Point_2 '(' INTEGER ',' INTEGER ',' INTEGER ')' 
                                  { visitor->accept_point_2( $3, $5, $7); }
  | Point_2 '(' Rational '(' INTEGER ',' INTEGER ')' ','
                Rational '(' INTEGER ',' INTEGER ')' ')' 
                                  { visitor->accept_point_2( $5, $7, 
                                                             $12, $14); }
;

conic_point_2: /* */
     error_rules                  {}
  | '(' Conic_2                   { visitor->begin_conic_point_2();} 
            conic_2
        ',' algorint
     ')'                          { visitor->end_conic_point_2(); }

;

algorint: /* */
    error_rules                   {}
  | algebraic_real ',' inti
  | infty ',' INTEGER             { visitor->accept_infty( $1); 
                                    visitor->accept_integer( $3); }
;


algebraic_real: /* */
    error_rules                   {}
  | AlgebraicReal                 { visitor->begin_algebraic_real(); }
        '(' Polynomial_1          { visitor->begin_polynomial_1(); }
              '(' integer_sequence1 ')' 
                                  { visitor->end_polynomial_1(); }

        ',' rational
        ',' rational
        ',' INTEGER               { visitor->accept_integer($15); }
        ')'                       { visitor->end_algebraic_real(); }
;

rational: /* */
    error_rules                   {}
  | Rational '(' INTEGER ',' INTEGER ')' 
                                  { visitor->accept_rational( $3, $5); }
;

inti: /* */ 
    INTEGER                       { visitor->accept_integer( $1); }  
  | infty                         { visitor->accept_infty( $1); }
;

infty: /* */
    MINUS_INFTY 
  | PLUS_INFTY
;

orientation: /* */
    COUNTERCLOCKWISE
  | CLOCKWISE
  | VOID
;

double_val: /* a double value can be either an FNUMBER or an INTEGER */
    FNUMBER
  | INTEGER                     { visitor->accept_integer( $1); } 
;

error_rules:
    ERROR                       { visitor->parse_error( $1); }
  | UNKNOWN_TOKEN               { visitor->unknown_token( $1); }
;

/* End of Grammar */
/* ============== */
%%

// Opens file 'name' and parses it. Uses visitor 'v' while parsing.
// Returns false if something went wrong. See the visitor for details.
bool benchmark_parse_file( std::string name, Benchmark_visitor* v) {
    std::ifstream in( name.c_str());
    if ( ! in) {
        v->parse_error( std::string( "cannot open file '") + name +
                        std::string( "'."));
        return false;
    }
    visitor = v;
    benchmark_init_lexer( in, name, 1);
    yyparse();
    return ! v->error();
}

// Starts parsing from stream 'in' with the associated filename 'name'
// (or analogous meaning for different streams) counting linenumbers
// starting from 'n'. Uses visitor 'v' while parsing. Returns false if
// something went wrong. See the visitor for details of the error reporting.
bool benchmark_parse_stream( std::istream&  in, std::string name, 
                             Benchmark_visitor* v, int n) {
    visitor = v;
    benchmark_init_lexer( in, name, n);
    yyparse();
    return ! v->error();
}

/* EOF */
