/*
 * Bison Parser
 */	
%{
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
%}

%start program

%token IDENTIFIER
%token NUMBER
%token INTEGER
%token FLOAT
%token BOOLEAN
%token STRING

%token OR 
%token AND 
%token NOT 
%token EQUALS 
%token NOT_EQUALS 
%token GREATER_THAN 
%token LESS_THAN 
%token GREATER_THAN_EQUAL 
%token LESS_THAN_EQUAL 

%token FOR 
%token WHILE 

%token IF 
%token ELSE 
%token SEMICOLON
%token <string> type
%token VALUE

%left '+' '-'
%left '*' '/'
%left OR
%left AND
%nonassoc EQUALS NOT_EQUALS GREATER_THAN LESS_THAN GREATER_THAN_EQUAL LESS_THAN_EQUAL
%right NOT
%left IDENTIFIER
%left type
%left STRING

//Define yyval
%union {
    int number;
    char *string;
}

%type <string> IDENTIFIER
%type <number> NUMBER
%type <string> STRING
%type <string> declaration
%type <string> loop
%type <string> condition
%type <bool> expression
%type <string> comparator
%type <statement_list> statement_list
%%

// Program
program: statement_list | YYEOF ;
// Statement List Action
statement_list: statement_list statement {printf("Statement List: \n");}
              | statement {printf("Statement List: \n");} ;

statement: declaration SEMICOLON | loop | condition | expression SEMICOLON ;
/*
 * Action Code
 */

// Declaration Action
declaration: type IDENTIFIER SEMICOLON {
    printf("Declared %s of type %s\n", $2, $1);
    // Allocate memory for the new variable
    if (strcmp($1, "int") == 0) {
        int *var = malloc(sizeof(int));
        // Save the pointer in the symbol table
        SymbolTable_add($2, var);
    } else if (strcmp($1, "float") == 0) {
        float *var = malloc(sizeof(float));
        // Save the pointer in the symbol table
        SymbolTable_add($2, var);
    } else if (strcmp($1, "bool") == 0) {
        bool *var = malloc(sizeof(bool));
        // Save the pointer in the symbol table
        SymbolTable_add($2, var);
    } else if (strcmp($1, "string") == 0) {
        char *var = malloc(sizeof(char*));
        // Save the pointer in the symbol table
        SymbolTable_add($2, var);
    }
} ;

// Loop Action
loop: FOR '(' type IDENTIFIER ':' type IDENTIFIER ')' statement_list SEMICOLON {
    printf("Loop: for (%s) {\n", $3);
    // Execute the loop body
    executeLoop($3);
}
    | WHILE '(' type condition ')' statement_list {
    printf("Loop: while (%s) {\n", $3);
    // Execute the loop body
    executeLoop($3);
} ;

// Condition Action
condition: IF'('expression comparator expression')' {
    // Evaluate the expression
    bool result = evaluateCondition($3);
    if (result) {
        // Execute the statement list
        executeStatements(statement_list);
    } else {
        printf("Condition: %s\n", $3->string);
    }
};

// Expression Action
expression: IDENTIFIER {
	printf("Expression: %s\n", $1->string);
	// Lookup the variable in the symbol table
	void *var = SymbolTable_lookup($1->string);
	// Return the result of the expression
	return var;
}
          | NUMBER {
	printf("Expression: %d\n", $1->number);
	// Return the result of the expression
	return $1->number;
}
          | STRING {
	printf("Expression: %s\n", $1->string);
	// Return the result
	return $1->string;
}
          | expression '+' expression {
	printf("Addition: %d + %d\n", $1, $3);
	// Add the two operands
	return $1 + $3;
} 
          | expression '-' expression {
	printf("Subtraction: %d - %d\n", $1, $3);
	// Subtract the second operand from the first
	return $1 - $3;
} 
          | expression '*' expression {
	printf("Multiplication: %d * %d\n", $1, $3);
	// Multiply the two operands
	return $1 * $3;
}
          | expression '/' expression {
	printf("Division: %d / %d\n", $1, $3);
	// Check for division by zero
	if ($3 == 0) {
		fprintf(stderr, "Error: division by zero\n");
		exit(1);
	}
	// Divide the first operand by the second
	return $1 / $3;
} 

// Comparator Action
comparator: OR {printf("Comparator: ||\n");}
          | AND {printf("Comparator: &&\n");}
          | NOT {printf("Comparator: !\n");}
          | EQUALS {printf("Comparator: ==\n");}
          | NOT_EQUALS {printf("Comparator: !=\n");}
          | GREATER_THAN {printf("Comparator: >\n");}

          | LESS_THAN {printf("Comparator: <\n");}

          | GREATER_THAN_EQUAL {printf("Comparator: >=\n");}

          | LESS_THAN_EQUAL {printf("Comparator: <=\n");} ;

%%

// Forward declarations
struct StatementList;
struct Statement;
struct Declaration;
struct Loop;
struct Condition;
struct Expression;
struct Comparator;

typedef struct StatementList {
    struct Statement** statements;
    int size;
} StatementList;

typedef struct Statement {
    enum {
        DECLARATION,
        LOOP,
        CONDITION,
        EXPRESSION
    } type;
    union {
        struct Declaration* declaration;
        struct Loop* loop;
        struct Condition* condition;
        struct Expression* expression;
    };
} Statement;

typedef struct Declaration {
    char* type;
    char* identifier;
} Declaration;

typedef struct Loop {
    enum {
        FOR_LOOP,
        WHILE_LOOP
    } loopType;
    char* type;
    struct Condition* condition;
    struct StatementList* statementList;
} Loop;

typedef struct Condition {
    struct Expression* expression1;
    struct Comparator* comparator;
    struct Expression* expression2;
    struct StatementList* statementList;
} Condition;

typedef struct Expression {
    enum {
        IDENTIFIER_EXP,
        NUMBER_EXP,
        STRING_EXP,
        BINARY_EXP
    } expressionType;
    union {
        char* identifier;
        int number;
        char* string;
        struct {
            struct Expression* left;
            struct Expression* right;
            char* operator;
        } binaryExp;
    };
} Expression;

typedef struct Comparator {
    char* type;
} Comparator;


// Helper Functions
void executeStatements(StatementList *list) {
	// Execute each statement in the list
	for (int i=0; i < list->size; i++) {
		executeStatement(list->statements[i]);
	}
}

void executeStatement(Statement *statement) {
	if (statement->type == DECLARATION) {
		executeDeclaration(statement->declaration);
	} else if (statement->type == LOOP) {
		executeLoop(statement->loop);
	} else if (statement->type == CONDITION) {
		executeCondition(statement->condition);
	}
}

void executeDeclaration(Declaration *declaration) {
	// Allocate memory for the new variable
	if (strcmp(declaration->type, "int") == 0) {
		int *var = malloc(sizeof(int));
		// Save the pointer in the symbol table
		SymbolTable_add(declaration->identifier, var);
	} else if (strcmp(declaration->type, "float") == 0) {
		float *var = malloc(sizeof(float));
		// Save the pointer in the symbol table
		SymbolTable_add(declaration->identifier, var);
	} else if (strcmp(declaration->type, "bool") == 0) {
		bool *var = malloc(sizeof(bool));
		// Save the pointer in the symbol table
		SymbolTable_add(declaration->identifier, var);
	} else if (strcmp(declaration->type, "string") == 0) {
		char *var = malloc(sizeof(char*));
		// Save the pointer in the symbol table
		SymbolTable_add(declaration->identifier, var);
	}
}

void executeLoop(Loop *loop) {
	// Evaluate the condition
	bool result = evaluateCondition(loop->condition);
	while (result) {
		// Execute the loop body
		executeStatements(loop->statementList);
		// Re-evaluate the condition
		result = evaluateCondition
		(loop->condition);
	}
}

void executeCondition(Condition *condition) {
	// Evaluate the expression
	bool result = evaluateCondition(condition->expression1, condition->comparator, condition->expression2);
	if (result) {
		// Execute the statement list
		executeStatements(condition->statementList);
	}
}

bool evaluateCondition(Expression *expression1, Comparator *comparator, Expression *expression2) {
	// Get the values of the expressions
	void *val1 = evaluateExpression(expression1);
	void *val2 = evaluateExpression(expression2);
	// Compare the values
	if (strcmp(comparator->type, "OR") == 0) {
		return val1 || val2;
	} else if (strcmp(comparator->type, "AND") == 0) {
		return val1 && val2;
	} else if (strcmp(comparator->type, "NOT") == 0) {
		return !val1;
	} else if (strcmp(
comparator->type, "EQUALS") == 0) {
		return val1 == val2;
	} else if (strcmp(comparator->type, "NOT_EQUALS") == 0) {
		return val1 != val2;
	} else if (strcmp(comparator->type, "GREATER_THAN") == 0) {
		return val1 > val2;
	} else if (strcmp(comparator->type, "LESS_THAN") == 0) {
		return val1 < val2;
	} else if (strcmp(comparator->type, "GREATER_THAN_EQUAL") == 0) {
		return val1 >= val2;
	} else if (strcmp(comparator->type, "LESS_THAN_EQUAL") == 0) {
		return val1 <= val2;
	}
	return false;
}

void* evaluateExpression(Expression *expression) {
	if (expression->type == IDENTIFIER) {
		// Lookup the variable in the symbol table
		return SymbolTable_lookup(expression->identifier);
	} else if (expression->type == NUMBER) {
		// Return the number
		return expression->number;
	} else if (expression->type == STRING) {
		// Return the string
		return expression->string;
	}
	return NULL;
}

typedef struct {
    SymbolTableEntry* entries;
    int size;
    int capacity;
} SymbolTable;

void SymbolTable_init() {
// Initialize the symbol table
symbol_table = malloc(sizeof(SymbolTable));
symbol_table->size = 0;
symbol_table->capacity = 16;
symbol_table->entries = malloc(symbol_table->capacity * sizeof(SymbolTableEntry));
}

// SymbolTable_add()
/*void SymbolTable_add(char* name, void* value) {
	// Create a new entry in the symbol table
	SymbolTableEntry *entry = malloc(sizeof(SymbolTableEntry));
	entry->name = name;
	entry->value = value;
	// Add the entry to the symbol table
	symbolTable[numEntries++] = entry;
}*/

void SymbolTable_add(char *key, void *value) {
// Resize the symbol table if necessary
if (symbol_table->size == symbol_table->capacity) {
	symbol_table->capacity *= 2;
	symbol_table->entries = realloc(symbol_table->entries, symbol_table->capacity * sizeof(SymbolTableEntry));
	}
// Add the entry to the symbol table
SymbolTableEntry *entry = &(symbol_table->entries[symbol_table->size++]);
entry->key = key;
entry->value = value;
}

void *SymbolTable_lookup(char *key) {
// Lookup the entry in the symbol table
for (int i=0; i < symbol_table->size; i++) {
	if (strcmp(symbol_table->entries[i].key, key) == 0) {
		return symbol_table->entries[i].value;
	}
// The variable is not defined
fprintf(stderr, "Error: variable '%s' is not defined\n", key);
exit(1);
}
}

/*// SymbolTable_lookup()
void* SymbolTable_lookup(char* name) {
	// Search the symbol table for the variable
	for (int i=0; i < numEntries; i++) {
		if (strcmp(symbolTable[i]->name, name) == 0) {
			return symbolTable[i]->value;
		}
	}
	// If the variable is not found, return NULL
	return NULL;
}*/

%%

int main(void) {
	SymbolTable_init();
	yyparse();
	return 0;
}
warning: shift/reduce conflict on token type [-Wcounterexamples]
  Example: WHILE '(' type condition ')' statement_list • type IDENTIFIER SEMICOLON SEMICOLON
  Shift derivation
    statement_list
    ↳ 4: statement
         ↳ 6: loop
              ↳ 11: WHILE '(' type condition ')' statement_list
                                                 ↳ 3: statement_list statement
                                                                     ↳ 5: declaration                      SEMICOLON
                                                                          ↳ 9: • type IDENTIFIER SEMICOLON
  Reduce derivation
    statement_list
    ↳ 3: statement_list                                                statement
         ↳ 4: statement                                                ↳ 5: declaration                    SEMICOLON
              ↳ 6: loop                                                     ↳ 9: type IDENTIFIER SEMICOLON
                   ↳ 11: WHILE '(' type condition ')' statement_list •