%{
#include <stdio.h>
#include "calc.h"
#define YYDEBUG 1
%}
%union {
    char		*identifier;
    ParameterList	*parameter_list;
    Expression		*expression;
}
%token <expression>	INT_LITERAL
%token <expression>	DOUBLE_LITERAL
%token <identifier>	IDENTIFIER
%token DEFINE IF ELSE WHILE LP RP LC RC SEMICOLON COMMA ASSIGN
	EQ NE GT GE LT LE ADD SUB MUL DIV MOD
%type   <parameter_list> parameter_list
%type   <expression> expression expression_list
        equality_expression relational_expression
	additive_expression multiplicative_expression
	unary_expression postfix_expression primary_expression
	if_expression while_expression
%%
translation_unit
	: definition_or_expression
	| translation_unit definition_or_expression
	;
definition_or_expression
	: function_definition
	| expression SEMICOLON
	{
	    clc_eval_expression($1);
	}
	| error
	{
	    yyclearin;
	    clc_reopen_current_storage();
	}
	;
function_definition
	: DEFINE IDENTIFIER LP parameter_list RP LC expression_list RC
	{
	    clc_function_define($2, $4, $7);
	}
	| DEFINE IDENTIFIER LP RP LC expression_list RC
	{
	    clc_function_define($2, NULL, $6);
	}
	;
parameter_list
	: IDENTIFIER
	{
	    $$ = clc_create_parameter($1);
	}
	| parameter_list COMMA IDENTIFIER
	{
	    $$ = clc_chain_parameter($1, $3);
	}
	;
expression_list
	: expression
        {
	    $$ = clc_create_expression_list($1);
	}
	| expression_list COMMA expression
	{
	    $$ = clc_chain_expression_list($1, $3);
	}
	;
expression
	: equality_expression
	| IDENTIFIER ASSIGN equality_expression
	{
	    $$ = clc_create_assign_expression($1, $3);
	}
	;
equality_expression
	: relational_expression
	| equality_expression EQ relational_expression
	{
	    $$ = clc_create_binary_expression(EQ_EXPRESSION, $1, $3);
	}
	| equality_expression NE relational_expression
	{
	    $$ = clc_create_binary_expression(NE_EXPRESSION, $1, $3);
	}
	;
relational_expression
	: additive_expression
	| relational_expression GT additive_expression
	{
	    $$ = clc_create_binary_expression(GT_EXPRESSION, $1, $3);
	}
	| relational_expression GE additive_expression
	{
	    $$ = clc_create_binary_expression(GE_EXPRESSION, $1, $3);
	}
	| relational_expression LT additive_expression
	{
	    $$ = clc_create_binary_expression(LT_EXPRESSION, $1, $3);
	}
	| relational_expression LE additive_expression
	{
	    $$ = clc_create_binary_expression(LE_EXPRESSION, $1, $3);
	}
	;
additive_expression
	: multiplicative_expression
	| additive_expression ADD multiplicative_expression
	{
	    $$ = clc_create_binary_expression(ADD_EXPRESSION, $1, $3);
	}
	| additive_expression SUB multiplicative_expression
	{
	    $$ = clc_create_binary_expression(SUB_EXPRESSION, $1, $3);
	}
	;
multiplicative_expression
	: unary_expression
	| multiplicative_expression MUL unary_expression
	{
	    $$ = clc_create_binary_expression(MUL_EXPRESSION, $1, $3);
	}
	| multiplicative_expression DIV unary_expression
	{
	    $$ = clc_create_binary_expression(DIV_EXPRESSION, $1, $3);
	}
	| multiplicative_expression MOD unary_expression
	{
	    $$ = clc_create_binary_expression(MOD_EXPRESSION, $1, $3);
	}
	;
unary_expression
	: postfix_expression
	| SUB unary_expression
	{
	    $$ = clc_create_minus_expression($2);
	}
	;
postfix_expression
	: primary_expression
	;
primary_expression
	: IDENTIFIER LP expression_list RP
	{
	    $$ = clc_create_function_call_expression($1, $3);
	}
	| IDENTIFIER LP RP
	{
	    $$ = clc_create_function_call_expression($1, NULL);
	}
	| if_expression
	| while_expression
	| LP expression RP
	{
	    $$ = $2;
	}
	| IDENTIFIER
	{
	    $$ = clc_create_identifier_expression($1);
	}
	| INT_LITERAL
	| DOUBLE_LITERAL
	;
if_expression
	: IF expression LC expression_list RC
	{
	    $$ = clc_create_if_expression($2, $4, NULL);
	}
	| IF expression LC expression_list RC ELSE LC expression_list RC
	{
	    $$ = clc_create_if_expression($2, $4, $8);
	}
	;
while_expression
	: WHILE expression LC expression_list RC
	{
	    $$ = clc_create_while_expression($2, $4);
	}
	;
%%