%{
#define YYDEBUG 1

#define yyerror printf
int yyparse();
int yylex();

#include <string>
#include <map>
#include <iostream>
using namespace std;

#include "scalar.hpp"
#include "list.hpp"

map<string, scalar*> scalars;
map<string, list*>   lists;
%}


%union
{
    scalar* scalar_value;
    list*   list_value;
    int     int_value;
    char*   string_value;
};

%type <scalar_value> scalar_expr
%type <list_value>   list_expr, list_elements
%left '+' '-'
%left '*' '/'
%left 'x' '.'
%nonassoc '[' ']'
%token SCALAR_ID LIST_ID PRINT PUSH POP
%token <int_value> NUM_LITERAL
%token <string_value> STRING_LITERAL, SCALAR_ID, LIST_ID

%%

file : file line 
	|	
	;	

line : SCALAR_ID '=' scalar_expr ';'
     {
        if (scalars.find($1) != scalars.end())
	      delete scalars[$1];
        scalars[$1] = $3;
        free($1);
     }
     | LIST_ID '=' list_expr ';'
     {
        if (lists.find($1) != lists.end())
	      delete lists[$1];
        lists[$1] = $3;
        free($1);
     }
     | PRINT '(' scalar_expr ')' ';'
     {
        cout<<*$3<<endl;
	delete $3;
     }
     | PRINT '(' LIST_ID ')' ';'
     {
        map<string, list*>::iterator li = lists.find($3);
	if (li != lists.end())
  	    cout<<*(li->second)<<endl;
     }
     | PUSH '(' LIST_ID ',' scalar_expr ')' ';'
     {
       if (lists.find($3) == lists.end())
	 lists[$3] = new list();

       lists[$3]->push_back($5);
       free($3);
     }
     ;	

scalar_expr : scalar_expr '+' scalar_expr
	    { $$ = new scalar((*$1).GetIntValue() + (*$3).GetIntValue()); 
	      delete $1;
	      delete $3;
	    }
	    | scalar_expr '-' scalar_expr
	    { $$ = new scalar((*$1).GetIntValue() - (*$3).GetIntValue()); 
	      delete $1;
	      delete $3;
	    }
	    | scalar_expr '*' scalar_expr
	    { $$ = new scalar((*$1).GetIntValue() * (*$3).GetIntValue()); 
	      delete $1;
	      delete $3;
	    }
	    | scalar_expr '/' scalar_expr
	    { $$ = new scalar((*$1).GetIntValue() / (*$3).GetIntValue()); 
	      delete $1;
	      delete $3;
	    }
	    | scalar_expr '.' scalar_expr
	    { $$ = new scalar((*$1).GetStringValue() + (*$3).GetStringValue()); 
	      delete $1;
	      delete $3;
	    }
            | scalar_expr 'x' scalar_expr
            {
	      string s = (*$1).GetStringValue(), result;
	      for (int i = 0; i<(*$3).GetIntValue(); i++)
		   result += s;

	      $$ = new scalar(result);
	      delete $1;
	      delete $3;

	    }
            | scalar_expr '[' NUM_LITERAL ',' NUM_LITERAL ']'
            {
	      $$ = new scalar((*$1).GetStringValue().substr($3, $5));
	      delete $1;
	    }
            | NUM_LITERAL
            {  $$ = new scalar($1); }
            | STRING_LITERAL
            {  $$ = new scalar($1); 
               free($1);
	    }
            | SCALAR_ID
            { $$ = scalars[$1]->Copy(); 
              free($1);
            } 
            | '(' scalar_expr ')'
            {  $$ = $2; }
            | POP '(' LIST_ID ')'
            {   
	      map<string, list*>::iterator li = lists.find($3);
	        if (li != lists.end() && 
		    li->second->size()>0)
                {
		  $$ = li->second->back();
		  li->second->pop_back();
                }
		else 
                  $$ = new scalar(0);
		free($3);
            }
            | LIST_ID '[' NUM_LITERAL ']'
            {
 	        map<string, list*>::iterator li = lists.find($1);
	        if (li != lists.end() &&
		    0<=$3 && $3<li->second->size())
 		        $$ = (*li->second)[$3]->Copy();
		else
   		        $$ = new scalar(0);
            }
	    ;
list_expr   : LIST_ID
            { $$ = lists[$1]; 
              free($1);
	    }
            | '(' list_elements ')'
            { $$ = $2; }
	    | '(' ')'
            {  $$ = new list(); }
            ;

list_elements : list_elements ',' scalar_expr
           {  $$->push_back($3); }       
           | scalar_expr
           {  $$ = new list();
              $$->push_back($1); 
           }
           ;
%%

main()
{
  yydebug = 0;
  yyparse();
}
