%{
#include "stdafx.h"
#include <iostream>
#include <string>

#include <interval.h>
#include <transducer.h>
#include <signal_expr.h>
#include <robustness.h>
#include <tools.h>

using namespace std;
using namespace STLRom;

%}

/*** yacc/bison Declarations ***/

/* Require bison 2.3 or later */
%require "2.3"

/* add debug output code to generated parser. disable this for release
 * versions. */
%debug

/* start symbol is named "start" */
%start start

/* write out a header file containing the token defines */
%defines

/* use newer C++ skeleton file */
%skeleton "lalr1.cc"

/* namespace to enclose parser in */
//%name-prefix="STLRom"
%define api.namespace {STLRom}
%define api.prefix {STLRom}

/* set the parser's class identifier */
%define parser_class_name {Parser}
//%define "parser_class_name" "Parser"

/* keep track of the current position within the input */
%locations
%initial-action
{
    // initialize the initial location object
    @$.begin.filename = @$.end.filename = &driver.streamname;
};

/* The driver is passed by reference to the parser and to the scanner. */
%parse-param { class STLDriver& driver }
%lex-param   { class STLDriver& driver }

/* verbose error messages */
%error-verbose

 /*** BEGIN STL TOKENS/TYPES ***/

%union {
    char                                   sval;
    int                                    integerVal;
    double                                 doubleVal;
    bool                                   boolVal;
    std::string*                           stringVal;
    STLRom::transducer*                 transducer_ptr;
    STLRom::interval*                   interval_ptr;
    std::map<string,double>*               param_map_ptr;
    Signal*                                signal_ptr;
}

%token                 LINT            "["
%token                 RINT            "]"
%token                 LPAREN          "("
%token                 RPAREN          ")"
%token                 LT              "<"
%token                 NOT             "not"
%token                 GT              ">"
%token                 AND             "and"
%token                 OR              "or"
%token                 IMPLIES         "=>"
%token                 BOX             "alw"
%token                 DIAMOND         "ev"
%token                 UNTIL           "until"
%token                 TIME            "time"
%token                 COMMA           ","
%token                 END	       0   "end of file"
%token                 EOL             "end of line"
%token                 ASSIGN          ":="
%token                 PLUS            "+"
%token                 MINUS           "-"
%token                 MULT            "*"
%token                 ABS             "abs"
%token                 PARAM_DECL      "param_decl"
%token                 SIGNAL_DECL     "signal_decl"
%token                 TEST            "test"


%token <boolVal>       BOOL_TRUE       "true"
%token <boolVal>       BOOL_FALSE      "false"
%token <doubleVal>     DOUBLE		   "double"
%token <stringVal>     CONSTANT        "constant"
%token <stringVal>     PARAM_ID        "param_id"
%token <stringVal>     PHI_ID          "phi_id"
%token <stringVal>     NEW_ID          "new_id"
%token <stringVal>     SIGNAL_ID       "signal_id"
%token <stringVal>     STL_TEST_ID     "stl_test_id"
%token <stringVal> 	   STRING		   "string"

%left AND
%left OR
%left NOT
%left IMPLIES
%left LT GT
%left BOX DIAMOND UNTIL ASSIGN
%nonassoc LPAREN RPAREN

%type <transducer_ptr> signal stl_atom
%type <transducer_ptr> signal_expr signal_atom signal_addexpr signal_multexpr signal_unaryexpr
%type <transducer_ptr> constant_signal
%type <transducer_ptr> stl_formula
%type <interval_ptr>   interval
%type <stringVal>      op
%type <stringVal>      constant
%type <boolVal>        boolean
%type <param_map_ptr>  local_param_assignements local_param_assignement_list local_param_assignement

%destructor { delete $$; } STRING PARAM_ID PHI_ID NEW_ID SIGNAL_ID STL_TEST_ID
%destructor { delete $$; } interval
%destructor { delete $$; } stl_atom stl_formula signal signal_atom
%destructor { delete $$; } signal_expr constant_signal signal_addexpr signal_multexpr signal_unaryexpr

 /*** END STL TOKENS/TYPES ***/

%{

#include "stl_driver.h"
#include "stl_scanner.h"

/* this "connects" the bison parser in the driver to the flex scanner class
 * object. it defines the yylex() function call to pull the next token from the
 * current lexer object of the driver context. */
#undef yylex
#define yylex driver.lexer->lex

%}

%% /*** Grammar Rules ***/

constant : CONSTANT
        {
            $$ = $1;
        }
        ;
        | PARAM_ID
        {
           $$ = $1;
        };


constant_signal : CONSTANT
        {
            $$ = new constant_transducer(*$1);
            $$->trace_data_ptr = &driver.data;
            $$->param_map = driver.param_map;
            $$->signal_map = driver.signal_map;
            delete $1;
        }
        ;
        | PARAM_ID
        {
           $$ = new constant_transducer(*$1);
           $$->trace_data_ptr = &driver.data;
           $$->param_map = driver.param_map;
           $$->signal_map = driver.signal_map;
           delete $1;
        };

signal: SIGNAL_ID LINT TIME RINT
        {
            $$ = new signal_transducer(*$1);
            $$->trace_data_ptr = &driver.data;
            $$->param_map = driver.param_map;
            $$->signal_map = driver.signal_map;

            int i = driver.signal_map[*$1];

            if (i==0) {
                cout << "Parsing error: unknown signal " << *$1 << endl;
                delete $$;
                $$ = nullptr;
                YYERROR;
            }
            delete $1;
        }
        ;

signal_atom : signal
        {
            $$ = $1;
        }
        ;
        | constant_signal
        {
            $$ = $1;
        }
        ;
        | LPAREN signal_expr RPAREN
        {
	       $$ = $2;
	    }

signal_unaryexpr : signal_atom
        {
	      $$ = $1;
	    }
        | ABS LPAREN signal_expr RPAREN
        {
            $$ = new abs_transducer($3);
            $$->trace_data_ptr = &driver.data;
            $$->param_map = driver.param_map;
            $$->signal_map = driver.signal_map;

        }

signal_multexpr : signal_unaryexpr
        {
	      $$ = $1;
	    }
        | signal_addexpr MULT signal_atom
          {
	      $$ = new mult_transducer($1, $3);
          $$->trace_data_ptr = &driver.data;
          $$->param_map = driver.param_map;
          $$->signal_map = driver.signal_map;

          }

signal_addexpr : signal_multexpr
        {
	      $$ = $1;
	    }
        | signal_addexpr PLUS signal_atom
          {
	      $$ = new plus_transducer($1, $3);
          $$->trace_data_ptr = &driver.data;
          $$->param_map = driver.param_map;
          $$->signal_map = driver.signal_map;

          }
        | signal_addexpr MINUS signal_atom
          {
	      $$ = new minus_transducer($1, $3);
          $$->trace_data_ptr = &driver.data;
          $$->param_map = driver.param_map;
          $$->signal_map = driver.signal_map;

          }

signal_expr : signal_addexpr
        {
            $$ = $1;
        }


stl_atom : signal_expr op signal_expr
          {
              $$ = new stl_atom($1, *$2, $3);
              $$->trace_data_ptr = &driver.data;
              $$->param_map = driver.param_map;
              $$->signal_map = driver.signal_map;
          }
          ;

op        : LT { $$ = new string("<"); }
          | GT { $$ = new string(">"); }
          ;

interval : LINT constant COMMA constant RINT
         {
             $$ = new interval(*$2, *$4);
             delete $2;
             delete $4;
         }
         ;
         | LINT constant constant RINT
         {
             $$ = new interval(*$2, *$3);
             delete $2;
             delete $3;
         }

stl_formula :
             stl_atom
             {
                 $$ = $1;
             }
             | NOT stl_formula %prec NOT
             {
                 $$ = new not_transducer($2);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | stl_formula AND stl_formula %prec AND
             {
                 $$ = new and_transducer($1, $3);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | stl_formula OR stl_formula %prec AND
             {
                 $$ = new or_transducer($1, $3);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | stl_formula IMPLIES stl_formula %prec AND
             {
                 $$ = new implies_transducer($1, $3);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | DIAMOND interval stl_formula %prec DIAMOND
             {
                 $$ = new ev_transducer($2, $3);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | BOX interval stl_formula %prec BOX
             {
                 $$ = new alw_transducer($2, $3);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | stl_formula UNTIL interval stl_formula %prec UNTIL
             {
                 $$ = new until_transducer($1, $3, $4);
                 $$->trace_data_ptr = &driver.data;
                 $$->param_map = driver.param_map;
                 $$->signal_map = driver.signal_map;

             }
             | LPAREN stl_formula RPAREN
             {
                 $$ = $2;
             }
             | PHI_ID
             {

                 transducer * ref = driver.formula_map[*$1];

                 if (ref==nullptr) {
                     cout << "Parsing error: unknown identifier " << *$1 << endl;
                     $$ = nullptr;
                     YYERROR;
                 }
                 else {
                     $$ = ref->clone();
                     $$->trace_data_ptr = &driver.data;
                     delete $1;
                 }
             }
;


assignement : NEW_ID ASSIGN stl_formula
            {
                driver.formula_map[*$1] = $3;
                delete $1;
            }

trace_env: TEST NEW_ID ':' STRING
         {
             double x;
             driver.add_trace_test(*$2, *$4, 0., false);
             delete $2;
         }
         | TEST NEW_ID ':' STRING COMMA CONSTANT COMMA boolean  /* test id: environment, simulation time, visualization  */
         {
             double x;
             s_to_d(*$6, x);
             driver.add_trace_test(*$2, *$4, x, $8);
             delete $2;
             delete $4;
             delete $6;
         }

boolean: BOOL_TRUE
       {
           $$ = true;
       }
       | BOOL_FALSE
       {
           $$ = false;
       }

// Parameters

param_assignement: PARAM_ID '=' CONSTANT
                 {
                    double val;
                    s_to_d( *$3, val );
                    driver.param_map[*$1] = val;
                    delete $1;
                    delete $3;
                 }
                 | NEW_ID '=' CONSTANT
                 {
                    double val;
                    s_to_d( *$3, val );
                    driver.param_map[*$1] = val;
                    delete $1;
                    delete $3;
                 }

param_assignement_list: param_assignement
                      | param_assignement_list COMMA param_assignement

param_assignements: PARAM_DECL param_assignement_list



local_param_assignement: PARAM_ID '=' CONSTANT
                 {

                    double val;
                    s_to_d( *$3, val );

                    $$ = new map<string,double>();
                    (*$$)[*$1] = val;
                    delete $1;
                    delete $3;
                 }
                 | NEW_ID '=' CONSTANT
                 {
                    double val;
                    s_to_d( *$3, val );
                    $$ = new map<string,double>();
                    (*$$)[*$1] = val;
                    delete $1;
                    delete $3;
                 }


local_param_assignement_list: local_param_assignement
                            | local_param_assignement_list COMMA local_param_assignement
{
    $$ = new map<string,double>(*$1);
    auto elem = $3->begin();
    (*$$)[elem->first] = elem->second;
}

local_param_assignements: /* empty */
                       {
                           $$ = new map<string,double>();
                       }
                       | LPAREN local_param_assignement_list RPAREN
                       {
                           $$ = $2;
                       }

// Signals

signal_new: NEW_ID
          {
             short idx =  driver.signal_map.size()+1;
             driver.signal_map[*$1] = idx;
             delete $1;
          }
          | SIGNAL_ID
          {
              delete $1;
          }

signal_new_list: signal_new
                      | signal_new_list COMMA signal_new

signal_decl: SIGNAL_DECL signal_new_list

stl_test: NEW_ID  local_param_assignements '{'  stl_formula COMMA  STRING COMMA STRING  COMMA boolean '}'
        {
            driver.add_stl_test(*$1, *$2, $4, *$6, *$8, $10 );
            delete $1;
            delete $2;
            delete $6;
            delete $8;
        }
        | STL_TEST_ID local_param_assignements
        {
            if ($2->empty())
                driver.add_stl_test(*$1);
            else {
                driver.add_stl_test(*$1, *$2);
            }
            delete $1;
            delete $2;
        }

stl_tests: stl_tests stl_test
         | stl_test

trace_test : trace_env '{' stl_tests '}'

start : assignement
      | trace_test
      | param_assignements
      | signal_decl
      | start assignement
      | start trace_test
      | start param_assignements
%%

void STLRom::Parser::error(const Parser::location_type& l,
			    const std::string& m)
{
    driver.error(l, m);
}
