%{
#ifdef WIN32
#pragma warning (disable: 4786) // disable warning for STL maps
#pragma warning (disable: 4996) // disable warning for deprecated POSIX name
#endif /* WIN32 */

#include <string.h>
#include <vector>

#include "../dpuser2c/tools.h"

#define YYSTYPE char*
int yylex();
void idl2cerror(const char *s);
std::string cNidl;
%}

%token INTEGER FUNCTION IF FOR REAL COMPLEX STRING VARIABLE WHERE WHILE EXIT OPTION PGPLOT RETVAL

%nonassoc IFX
%nonassoc ELSE

%left AND OR
%left EQ NE
%left GE LE '>' '<'
%left '+' '-'
%left '%'
%left '*' '#' '/'
%left PlusE MinusE MulE DivE
%left PlusP MinusM
%left '^'
%nonassoc UMINUS
%nonassoc '!'
%nonassoc '[' ']'


%%

function:
          function stmt { addLineToOutput($2); }
		| /* NULL */
        ;

stmt:
          ';'                                { $$ = strdup(""); }
        | pgplot_arg                         { $$ = strdup((std::string($1) + ");").c_str()); }
		| procedure                          { $$ = strdup((std::string($1) + ");").c_str()); }
        | FOR '(' varchange ';' boolean ';' varchange ')' stmt   { $$ = strdup((std::string("for(") + $3 + ";" + $5 + ";" + $7 + ")" + $9).c_str()); }
		| FOR varchange ',' expr stmt        { std::string var = extractsetvar($2); $$ = strdup((std::string("for(") + $2 + "; " + var + "<=" + $4 + "; " + var + "++)" + $5).c_str()); }
		| varchange ';'                      { $$ = strdup((std::string($1) + ";\n").c_str()); }
        | WHILE boolean stmt                 { $$ = strdup((std::string("while(") + $2 + ")" + $3).c_str()); }
        | IF boolean stmt %prec IFX          { $$ = strdup((std::string("if (") + $2 + ")" + $3).c_str()); }
        | IF boolean stmt ELSE stmt          { $$ = strdup((std::string("if (") + $2 + ")" + $3 + " else " + $5 + "").c_str()); }
        | IF expr stmt %prec IFX             { $$ = strdup((std::string("if (") + $2 + ")" + $3).c_str()); }
        | IF expr stmt ELSE stmt             { $$ = strdup((std::string("if (") + $2 + ")" + $3 + " else " + $5 + "").c_str()); }
        | '{' stmt_list '}'                  { $$ = strdup((std::string("{\n") + $2 + "}\n").c_str()); }
		| error ';'                          { $$ = strdup(";"); }
		| error '}'                          { $$ = strdup("}"); }
		| EXIT                               { $$ = strdup("exit"); }
        ;

stmt_list:
          stmt                               { $$ = strdup((preline + $1).c_str()); preline = ""; }
        | stmt_list stmt                     { $$ = strdup((preline + std::string($1) + $2).c_str()); preline = ""; }
        ;

assignment:
		  VARIABLE '=' expr                  { tools_variables.push_back($1); $$ = strdup((std::string($1) + "=" + $3).c_str()); }
        | VARIABLE '=' boolean               { tools_variables.push_back($1); $$ = strdup((std::string($1) + "=" + $3).c_str()); }
		| VARIABLE '=' assignment            { tools_variables.push_back($1); $$ = strdup((std::string($1) + "=" + $3).c_str()); } 
		;

varchange:
		  VARIABLE PlusP                     { $$ = strdup((std::string($1) + "++").c_str()); }
		| VARIABLE MinusM                    { $$ = strdup((std::string($1) + "--").c_str()); }
		| VARIABLE PlusE expr                { $$ = strdup((std::string($1) + "+=" + $3).c_str()); }
		| VARIABLE MinusE expr               { $$ = strdup((std::string($1) + "-=" + $3).c_str()); }
		| VARIABLE MulE expr                 { $$ = strdup((std::string($1) + "*=" + $3).c_str()); }
		| VARIABLE DivE expr                 { $$ = strdup((std::string($1) + "/=" + $3).c_str()); }
		| VARIABLE rangeargs '=' expr        { $$ = strdup((std::string($1) + ".setrange(" + ExtractRangeArgs($2) + ", " + $4 + ")").c_str()); }
		| VARIABLE rangeargs PlusE expr      { $$ = strdup((std::string($1) + ".addrange(" + ExtractRangeArgs($2) + ", " + $4 + ")").c_str()); }
		| VARIABLE rangeargs MinusE expr     { $$ = strdup((std::string($1) + ".subrange(" + ExtractRangeArgs($2) + ", " + $4 + ")").c_str()); }
		| VARIABLE rangeargs MulE expr       { $$ = strdup((std::string($1) + ".mulrange(" + ExtractRangeArgs($2) + ", " + $4 + ")").c_str()); }
		| VARIABLE rangeargs DivE expr       { $$ = strdup((std::string($1) + ".divrange(" + ExtractRangeArgs($2) + ", " + $4 + ")").c_str()); }
		| VARIABLE rangeargs PlusP           { $$ = strdup((std::string($1) + ".addrange(" + ExtractRangeArgs($2) + ", 1)").c_str()); }
		| VARIABLE rangeargs MinusM          { $$ = strdup((std::string($1) + ".subrange(" + ExtractRangeArgs($2) + ", 1)").c_str()); }
		| assignment                         { $$ = strdup($1); }
		;

funcarg_list:
		  FUNCTION '(' expr                  { $$ = strdup((renameFunction($1) + "(" + $3).c_str()); }
		| funcarg_list ',' expr              { $$ = strdup((std::string($1) + ", " + $3).c_str()); }
		| funcarg_list ',' assignment        { $$ = strdup((std::string($1) + ", " + $3).c_str()); }
		| funcarg_list OPTION                { $$ = strdup((std::string($1) + ", dpString(\"" + $2 + "\")").c_str()); }
		;

rangeargs:
          rangearg_begin ']'                 { $$ = strdup($1); }
		| rangearg_cont ']'                  { $$ = strdup($1); }
		;

rangearg_begin:
          '[' expr ':' expr                  { $$ = strdup((std::string("#") + $2 + ", " + $4 + "#").c_str()); }
		| '[' '*'                            { $$ = strdup("-1, -1"); }
		| '[' expr                           { $$ = strdup((std::string("%") + $2 + "%").c_str()); }

rangearg_cont:
          rangearg_begin ',' '*'             { $$ = strdup((std::string($1) + ", -1, -1").c_str()); }
		| rangearg_begin ',' expr ':' expr   { $$ = strdup((std::string($1) + ", #" + $3 + ", " + $5 + "#").c_str()); }
		| rangearg_begin ',' expr            { $$ = strdup((std::string($1) + ", %" + $3 + "%").c_str()); }
        | rangearg_cont ',' '*'              { $$ = strdup((std::string($1) + ", -1, -1").c_str()); }
		| rangearg_cont ',' expr ':' expr    { $$ = strdup((std::string($1) + ", #" + $3 + ", " + $5 + "#").c_str()); }
		| rangearg_cont ',' expr             { $$ = strdup((std::string($1) + ", %" + $3 + "%").c_str()); }
		;

procedure:
          PGPLOT                             { $$ = strdup((renameProcedure($1) + "(").c_str()); }
		| PGPLOT ','                         { $$ = strdup((renameProcedure($1) + "(").c_str()); }
		| RETVAL                             { $$ = strdup((std::string($1) + "(").c_str()); }
		| RETVAL ','                         { $$ = strdup((std::string($1) + "(").c_str()); }
		;

pgplot_arg:
		  procedure expr                     { cNidl = createTName(); tools_variables.push_back(cNidl); $$ = strdup((std::string($1) + cNidl + " = " + $2).c_str()); }
		| procedure VARIABLE                 { $$ = strdup((std::string($1) + $2).c_str()); }
		| procedure boolean                  { $$ = strdup((std::string($1) + $2).c_str()); }
		| pgplot_arg ',' expr                { cNidl = createTName(); tools_variables.push_back(cNidl); $$ = strdup((std::string($1) + ", " + cNidl + " = " + $3).c_str()); }
		| pgplot_arg ',' VARIABLE            { $$ = strdup((std::string($1) + ", " + $3).c_str()); }
		| pgplot_arg ',' boolean             { $$ = strdup((std::string($1) + ", " + $3).c_str()); }
		| pgplot_arg ',' assignment          { $$ = strdup((std::string($1) + ", " + $3).c_str()); }
		| pgplot_arg OPTION                  { $$ = strdup((std::string($1) + ", \"" + $2 + "\"").c_str()); }
		;

expr:
          INTEGER                            { cNidl = createTName(); constants_variables.push_back(std::string("dpuserType ") + cNidl + " = " + $1); $$ = strdup(cNidl.c_str()); }
		| REAL                               { cNidl = createTName(); constants_variables.push_back(std::string("dpuserType ") + cNidl + " = " + $1); $$ = strdup(cNidl.c_str()); }
		| COMPLEX                            { cNidl = createTName(); constants_variables.push_back(std::string("dpComplex(0.0, ") + cNidl + " = " + $1 + ")"); $$ = strdup(cNidl.c_str()); }
		| STRING                             { cNidl = createTName(); constants_variables.push_back(std::string("dpuserType ") + cNidl + " = \"" + $1 + "\""); $$ = strdup(cNidl.c_str()); }
        | VARIABLE                           { $$ = strdup($1); }
        | '-' expr %prec UMINUS              { $$ = strdup((std::string("-") + $2).c_str()); }
        | expr '+' expr                      { $$ = strdup((std::string($1) + "+" + $3).c_str()); }
        | expr '-' expr                      { $$ = strdup((std::string($1) + "-" + $3).c_str()); }
        | expr '*' expr                      { $$ = strdup((std::string($1) + "*" + $3).c_str()); }
        | expr '/' expr                      { $$ = strdup((std::string($1) + "/" + $3).c_str()); }
	| expr '^' expr                      { $$ = strdup((std::string("dpuserFunction_pow(") + $1 + ", " + $3 + ")").c_str()); }
		| expr '#' expr                      { $$ = strdup((std::string("dpuserFunction_matrixmul(") + $1 + ", " + $3 + ")").c_str()); }
		| expr '%' expr                      { $$ = strdup((std::string("dpuserFunction_modulo(") + $1 + ", " + $3 + ")").c_str()); }
		| boolean '?' expr ':' expr          { $$ = strdup((std::string($1) + "?" + $3 + ":" + $5).c_str()); }
        | '(' expr ')'                       { $$ = strdup((std::string("(") + $2 + ")").c_str()); }
        | FUNCTION '(' ')'                   { $$ = strdup((renameFunction($1) + "()").c_str()); }
	| WHERE '(' boolean ')'              { $$ = strdup((std::string("dpuserFunction_where(") + extractWhereArguments($3) + ")").c_str()); }
	| WHERE '(' boolean ',' VARIABLE ')'              { $$ = strdup((std::string("dpuserFunction_where(") + extractWhereArguments($3) + "," + $5 + ")").c_str()); }
	| WHERE '(' boolean ',' PGPLOT ')'              { tools_variables.push_back($5); $$ = strdup((std::string("dpuserFunction_where(") + extractWhereArguments($3) + "," + $5 + ")").c_str()); }
		| funcarg_list      ')'              { $$ = strdup((std::string($1) + ")").c_str()); }
		| expr rangeargs                     { $$ = strdup((std::string("extractrange(") + $1 + ", " + ExtractRangeArgs($2) + ")").c_str()); }
		| rangeargs                          { $$ = strdup(CreateRangeArgs($1).c_str()); }
        ;


boolean:
          expr '<' expr                      { $$ = strdup((std::string($1) + "<" + $3).c_str()); }
        | expr '>' expr                      { $$ = strdup((std::string($1) + ">" + $3).c_str()); }
        | expr GE expr                       { $$ = strdup((std::string($1) + ">=" + $3).c_str()); }
        | expr LE expr                       { $$ = strdup((std::string($1) + "<=" + $3).c_str()); }
        | expr NE expr                       { $$ = strdup((std::string($1) + "!=" + $3).c_str()); }
        | expr EQ expr                       { $$ = strdup((std::string($1) + "==" + $3).c_str()); }
		| '(' boolean ')'                    { $$ = strdup((std::string("(") + $2 + ")").c_str()); }
		| boolean AND boolean                { $$ = strdup((std::string($1) + "&&" + $3).c_str()); }
		| boolean OR boolean                 { $$ = strdup((std::string($1) + "||" + $3).c_str()); }
		| expr OR boolean                 { $$ = strdup((std::string($1) + "||" + $3).c_str()); }
		| boolean OR expr                 { $$ = strdup((std::string($1) + "||" + $3).c_str()); }
		| expr AND boolean                 { $$ = strdup((std::string($1) + "||" + $3).c_str()); }
		| boolean AND expr                 { $$ = strdup((std::string($1) + "||" + $3).c_str()); }
		| '!' boolean                        { $$ = strdup((std::string("!") + $2).c_str()); }
		;

%%
