%{
#define YYDEBUG 1
#define yyerror printf
extern int yylex();

#include <string>
#include <map>
using namespace std;
#include "lambda.hpp"

map<string, LambdaExpression*> defines;

%}

%union {
	string* string_attr;
	int int_attr;
	LambdaExpression* expression_attr;	
}

%token DEFINE
%token<int_attr> NUM
%token<string_attr> ID
%type<expression_attr> expr lambda_expr

%left '+'
%left '*'

%%

lines : lines line
	|
	;

line : lambda_expr {
		LambdaExpression* e1 = $1;
		LambdaExpression* e2;

		// Iscrpno vrsimo korak beta redukcije dok god ima promena
		while(e1->betaReduction(e2)) {
			delete e1;
			e1 = e2;
			cout << ":::"; e1->print(); cout << endl;
		}
		delete e2;

		e1->print();
		cout << endl;
		delete e1;
      }
      | '(' DEFINE ID lambda_expr ')' {
		map<string, LambdaExpression*>::iterator i;
		i = defines.find(*$3);
		if (i != defines.end())
			delete i->second;
		defines[*$3] = $4;
		delete $3;
	}
      ;

lambda_expr : '(' lambda_expr lambda_expr ')' {
	$$ = new Application($2, $3);
    }
    | '(' '%' ID '.' lambda_expr ')' {
	$$ = new Abstraction(*$3, $5);	
    }
    | expr {
	$$ = $1;
    }
    ;

expr : ID {
	map<string, LambdaExpression*>::const_iterator i;
	i = defines.find(*$1);
	if (i != defines.end())
		$$ = i->second->clone();
	else 
		$$ = new Variable(*$1);	
	delete $1;
    }
    | NUM {
	$$ = new Constant($1);
    }
    | expr '+' expr {
	$$ = new Plus($1, $3);
    }
    | expr '*' expr {}
    ;

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