/**************************************************************************************
 * Chaga compiler                                                                     *
 * Copyright (C) 2026 Robert James Bruce                                              *
 *                                                                                    *
 * This file is part of the Chaga compiler.                                           *
 *                                                                                    *
 * The Chaga compiler is free software: you can redistribute it and/or modify it      *
 * under the terms of the GNU General Public License as published by the              *
 * Free Software Foundation, either version 3 of the License, or (at your option) any *
 * later version.                                                                     *
 *                                                                                    *
 * The Chaga compiler is distributed in the hope that it will be useful, but WITHOUT  *
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or              *
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License               *
 * for more details.                                                                  *
 *                                                                                    *
 * You should have received a copy of the GNU General Public License                  *
 * along with the Chaga compiler. If not, see <https://www.gnu.org/licenses/>.        *
 **************************************************************************************/
#include "include/chaga_compiler-data_structure_management.h"

size_t string_length (string_node *string_list)
{
  string_node *current_element;
  size_t length_of_string;

  length_of_string = 0;
  current_element = string_list;
  while (current_element != NULL)
  {
    length_of_string++;
    current_element = current_element->next;
  }
  return length_of_string;
}




expression_stack_node * push_expression_stack (expression_stack_node *expression_stack, char *token, int token_type, char *temporary_variable_identifier, stackframe_node *stackframe_table_entry)
{
  expression_stack_node *new_expression_stack_element;
  size_t temporary_variable_identifier_string_length;

  new_expression_stack_element = NULL;
  if ((token != NULL) || (temporary_variable_identifier != NULL))
  {
    new_expression_stack_element = (expression_stack_node *) malloc(sizeof(expression_stack_node));
    if (new_expression_stack_element != NULL)
    {
      new_expression_stack_element->stackframe_table_entry        = stackframe_table_entry;
      new_expression_stack_element->token                         = NULL;
      new_expression_stack_element->token_type                    = token_type;
      new_expression_stack_element->temporary_variable_identifier = NULL;
      new_expression_stack_element->next                          = NULL;
      if (token != NULL)
      {
        new_expression_stack_element->token = token;
      }
      else
      {
        temporary_variable_identifier_string_length = strlen (temporary_variable_identifier) + 1; // add one for NULL terminator
        new_expression_stack_element->temporary_variable_identifier = (unsigned char *) malloc(sizeof(char) * temporary_variable_identifier_string_length);
        if (new_expression_stack_element->temporary_variable_identifier != NULL)
        {
          strncpy (new_expression_stack_element->temporary_variable_identifier, temporary_variable_identifier, temporary_variable_identifier_string_length);
        }
      }
      if (expression_stack != NULL)
      {
        new_expression_stack_element->next = expression_stack;
      }
      expression_stack = new_expression_stack_element; 
    }
  }
  return expression_stack;
}




expression_stack_node * pop_expression_stack (expression_stack_node *expression_stack)
{
  expression_stack_node *expression_stack_element;

  if (expression_stack != NULL)
  {
    expression_stack_element = expression_stack;
    expression_stack = expression_stack->next;
    if (expression_stack_element->temporary_variable_identifier != NULL)
    {
      free (expression_stack_element->temporary_variable_identifier);
    }
    free (expression_stack_element);
  }
  return expression_stack;
}






statement_stack_node * push_statement_stack (statement_stack_node *statement_stack, chaga_statement_type statement_type, unsigned long long int left_curly_brace_count_before_this_statement)
{
  statement_stack_node *new_statement_stack_element;

  new_statement_stack_element = (statement_stack_node *) malloc(sizeof(statement_stack_node));
  if (new_statement_stack_element != NULL)
  {
    new_statement_stack_element->statement_type = statement_type;
    new_statement_stack_element->left_curly_brace_count_before_this_statement = left_curly_brace_count_before_this_statement;
    new_statement_stack_element->next = NULL;
    if (statement_stack != NULL)
    {
      new_statement_stack_element->next = statement_stack;
    }
    statement_stack = new_statement_stack_element; 
  }
  return statement_stack;
}




statement_stack_node * pop_statement_stack (statement_stack_node *statement_stack)
{
  statement_stack_node *temp;

  if (statement_stack != NULL)
  {
    temp = statement_stack;
    statement_stack = statement_stack->next;
    free (temp);
  }
  return statement_stack;
}




string_node * append_to_string (string_node *string_list, char byte)
{
  string_node *new_element, *current_element;

  new_element = (string_node *) malloc(sizeof(string_node));
  if (new_element != NULL)
  {
    new_element->byte = byte;
    new_element->next = NULL;
    if (string_list != NULL)
    {
      current_element = string_list;
      while (current_element->next != NULL)
      {
        current_element = current_element->next;
      }
      current_element->next = new_element;
    }
    else
    {
      string_list = new_element;
    }
  }
  return string_list;
}




string_node * delete_string (string_node *string_list)
{
  string_node *current_element;

  while (string_list != NULL)
  {
    current_element = string_list;
    string_list = string_list->next;
    free (current_element);
  }
  return string_list;
}




symtab_node * delete_symbol_table (symtab_node *symbol_table)
{
  symtab_node *symbol_table_element;

  while (symbol_table != NULL)
  {
    symbol_table_element = symbol_table;
    symbol_table = symbol_table->next;
    free (symbol_table_element);
  }
  return symbol_table;
}




token_node * add_token_element (string_node *string_list, int token_type, unsigned long long int line_number)
{
  token_node *new_element;
  string_node *string_element;
  size_t i, token_string_length;

  new_element = NULL;
  if (string_list != NULL)
  {
    new_element = (token_node *) malloc(sizeof(token_node));
    if (new_element != NULL)
    {
      new_element->next = NULL;
      new_element->token = NULL;
      new_element->token_type = token_type;
      new_element->line_number = line_number;
      token_string_length = string_length (string_list) + 1; // allocate one additional null terminator byte!
      if (token_string_length > 0)
      {
        new_element->token = (char *) malloc(sizeof(char) * token_string_length);
        if (new_element->token != NULL)
        {
          i = 0;
          string_element = string_list;
          while (string_element != NULL)
          {
            if (i < token_string_length)
            {
              new_element->token[i] = string_element->byte;
              i++;
            }
            string_element = string_element->next;
          }
          if (i < token_string_length)
          {
            new_element->token[i] = '\0';
          }
        }
      }
    }
  }
  return new_element;
}




// *************************************************************************************************
// * add_element_to_concrete_syntax_tree                                                           *
// *                                                                                               *
// * This subroutine adds a token to the Concrete Syntax Tree (CST).                               *
// *                                                                                               *
// * INPUTS:                                                                                       *
// * token_element   : This is the token from the token_list to be added to the CST.               *
// * extra_token     : This is a way to inject an additional token into the CST. It is rarely used *
// *                   except in numerical expressions where a unary operator is followed by an    *
// *                   identifier. For example:                                                    *
// *                                            token list is:        foo = - bar ...              *
// *                                            then change this to:  foo = 0 - bar ...            *
// *                   In this example, I inject an extra "0" token as a placeholder. This makes   *
// *                   it much easier to process numerical expressions, especially when I convert  *
// *                   numerical expressions from infix to postfix notation.                       *
// * extra_token_type: this describes the attributes of the extra_token. Most likely it will be    *
// *                   the value, TOKEN_IDENTIFIER_INTEGER_CONSTANT to decribe a "0" token.        *
// *                                                                                               *
// * OUTPUT:                                                                                       *
// * Upon success, a new CST element or NULL upon failure.                                         *
// *************************************************************************************************
cst_node * add_element_to_concrete_syntax_tree (token_node *token_element, char *extra_token, int extra_token_type)
{
  cst_node *new_cst_element, *new_cst_element2;
  size_t token_string_length;

  new_cst_element  = NULL;
  new_cst_element2 = NULL;
  if (token_element != NULL)
  {
    if (token_element->token != NULL)
    {
      new_cst_element = (cst_node *) malloc(sizeof(cst_node));
      if (new_cst_element != NULL)
      {
        new_cst_element->sibling     = NULL;
        new_cst_element->child       = NULL;
        new_cst_element->token_entry = token_element;
        new_cst_element->token_type  = token_element->token_type;
        token_string_length = strlen (token_element->token);
        new_cst_element->token = malloc(sizeof(char) * (token_string_length + 1));  // add one for NULL terminator byte
        if (new_cst_element->token != NULL)
        {
          strncpy (new_cst_element->token, token_element->token, token_string_length + 1);
        }
      }
      if (extra_token != NULL)
      {
        new_cst_element2 = (cst_node *) malloc(sizeof(cst_node));
        if (new_cst_element2 != NULL)
        {
          new_cst_element2->sibling     = NULL;
          new_cst_element2->child       = NULL;
          new_cst_element2->token_entry = NULL;
          new_cst_element2->token_type  = extra_token_type;
          token_string_length = strlen (extra_token);
          new_cst_element2->token = malloc(sizeof(char) * (token_string_length + 1));  // add one for NULL terminator byte
          if (new_cst_element2->token != NULL)
          {
            strncpy (new_cst_element2->token, extra_token, token_string_length + 1);
          }
          new_cst_element2->sibling = new_cst_element;
          new_cst_element = new_cst_element2;
        }
      }
    }
  }
  return new_cst_element;
}




ast_node * add_element_to_abstract_syntax_tree (char *token, int token_type, chaga_statement_type statement_type, token_node *token_entry, symtab_node *symbol_table_entry)
{
  ast_node *new_ast_element;
  size_t token_string_length;

  new_ast_element = NULL;
  if (token != NULL)
  {
    new_ast_element = (ast_node *) malloc(sizeof(ast_node));
    if (new_ast_element != NULL)
    {
      new_ast_element->token_type         = token_type;
      new_ast_element->statement_type     = statement_type;
      new_ast_element->symbol_table_entry = symbol_table_entry;
      new_ast_element->token_entry        = token_entry;
      new_ast_element->token              = NULL;
      new_ast_element->sibling            = NULL;
      new_ast_element->child              = NULL;
      token_string_length = strlen (token);
      new_ast_element->token = (char *) malloc(sizeof(char) * (token_string_length + 1)); // add one for NULL terminator byte
      if (new_ast_element->token != NULL)
      {
        strncpy (new_ast_element->token, token, token_string_length + 1);
      }
    }
  }
  return new_ast_element;
}




param_node * add_element_to_parameter_list (param_node *parameter_list, chaga_attribute_datatype datatype, char *identifier)
{
  param_node *new_datatype_element, *datatype_element;

  new_datatype_element = (param_node *) malloc(sizeof(param_node));
  if (new_datatype_element != NULL)
  {
    new_datatype_element->datatype   = datatype;
    new_datatype_element->identifier = identifier;
    new_datatype_element->next       = NULL;   
    if (parameter_list != NULL)
    {
      datatype_element = parameter_list;
      while (datatype_element->next != NULL)
      {
        datatype_element = datatype_element->next;
      }
      datatype_element->next = new_datatype_element;
    }
    else
    {
      parameter_list = new_datatype_element;
    }
  }
  return parameter_list;
}




symtab_node * add_symbol_table_element (symtab_node *symbol_table, char *identifier, chaga_attribute_type attribute_type, chaga_attribute_datatype datatype, char num_array_dimensions, unsigned long long int first_indice_size, unsigned long long int second_indice_size, unsigned long long int third_indice_size, unsigned long long int scope_level, int is_internal_use_only_variable, int internal_use_only_stores_string_number_constants)
{
  symtab_node *new_symbol_table_element, *temp_current_symbol_table_element, *temp_previous_symbol_table_element;
  size_t identifier_string_length;
  int done;

  identifier_string_length = 0;
  if (identifier != NULL)
  {
    identifier_string_length = strlen(identifier);
  }
  if (identifier_string_length > 0)
  {
    new_symbol_table_element = (symtab_node *) malloc(sizeof(symtab_node));
    if (new_symbol_table_element != NULL) 
    {
      new_symbol_table_element->identifier = (char *) malloc(sizeof(char) * (identifier_string_length + 1));
      if (new_symbol_table_element->identifier != NULL)
      {
        strncpy (new_symbol_table_element->identifier, identifier, identifier_string_length + 1);
      }
      new_symbol_table_element->attribute_type                = attribute_type;
      new_symbol_table_element->datatype                      = datatype;
      new_symbol_table_element->num_array_dimensions          = num_array_dimensions;
      new_symbol_table_element->first_indice_size             = first_indice_size;
      new_symbol_table_element->second_indice_size            = second_indice_size;
      new_symbol_table_element->third_indice_size             = third_indice_size;
      new_symbol_table_element->scope_level                   = scope_level;
      new_symbol_table_element->is_internal_use_only_variable = is_internal_use_only_variable;
      new_symbol_table_element->internal_use_only_stores_string_number_constants = internal_use_only_stores_string_number_constants;
      new_symbol_table_element->internal_use_only_variable_is_available = FALSE;
      new_symbol_table_element->parameter_list                = NULL;
      new_symbol_table_element->return_parameter_list         = NULL;
      new_symbol_table_element->next                          = NULL;
      if (symbol_table != NULL)
      { 
        done = FALSE;
        temp_previous_symbol_table_element = NULL;
        temp_current_symbol_table_element = symbol_table;
        while ((temp_current_symbol_table_element != NULL) && (!done))
        {
          if (strncmp(new_symbol_table_element->identifier, temp_current_symbol_table_element->identifier, identifier_string_length) <= 0)
          {
            new_symbol_table_element->next = temp_current_symbol_table_element;
            if (temp_previous_symbol_table_element != NULL)
            {
              temp_previous_symbol_table_element->next = new_symbol_table_element;
            }
            else
            {
              symbol_table = new_symbol_table_element;
            }
            done = TRUE;
          } 
          if (!done)
          {
            temp_previous_symbol_table_element = temp_current_symbol_table_element;
            temp_current_symbol_table_element = temp_current_symbol_table_element->next;
          }
        }
        if (!done)
        {
          temp_previous_symbol_table_element->next = new_symbol_table_element;
        }
      }
      else
      {
        symbol_table = new_symbol_table_element;
      }
    }
  }
  return symbol_table;
}




symtab_node * add_temporary_variables_to_symbol_table_to_evaluate_numerical_expressions (symtab_node *symbol_table, ast_node *abstract_syntax_tree)
{
  ast_node *ast_element;
  token_node *token_placeholder;
  symtab_node *symbol_table_element, *temporary_storage_variable;
  expression_stack_node *expression_stack;
  unsigned long long int scope_level, counter_for_new_variables;
  chaga_attribute_type attribute_type;
  chaga_attribute_datatype datatype;
  int state, exists, token_type;
  unsigned char new_variable[1024];
  unsigned char *token;

  expression_stack = NULL;
  if (abstract_syntax_tree != NULL)
  {
// ********************************************************************************************************************
// * FIRST PASS: Create a temporary variable for every constant found in the input expression. I must do this because *
// * I need to convert these integer constants to string integers before the integer constants can be mathematically  *
// * evaluated.                                                                                                       *
// ********************************************************************************************************************
    counter_for_new_variables = 0;
    scope_level = 0;
    state       = 0;
    ast_element = abstract_syntax_tree;
    while ((ast_element != NULL) && (state != 99999))
    {
      token = ast_element->token;
      token_type = ast_element->token_type;
      if (token != NULL)
      {
        //printf ("In add_temporary_variables_to_symbol_table_to_evaluate_numerical_expressions: state = %d, ast_element->token = %s\n", state, token);
        //printf ("EXPRESSION STACK FRAME:");
        //display_expression_stack (expression_stack);
        //putchar ('\n');
        switch (state)
        {
          case 0:
            if (strncmp(token, "set", 3) == 0)
            {
              state = 10;
            }
          break;
          case 10:
            if (token_type == TOKEN_IDENTIFIER)
            {
              symbol_table_element = ast_element->symbol_table_entry;
              if (symbol_table_element != NULL)
              {
                scope_level    = symbol_table_element->scope_level;
                attribute_type = symbol_table_element->attribute_type;
                datatype       = symbol_table_element->datatype;              
// *************************************************************************************************
// * Create one temporary variable (might need it) for later evaluating a mathematical expression. *
// * It may be that this temporary variable is not needed (in some cases) while it could be useful *
// * in others.                                                                                    *
// *                                                                                               *
// * EXAMPLE 1: num1 = num2;                                                                       *
// *            In example 1, a temporary variable is not necessary:                               *
// *            num1 = stringint_copy (num2);                                                      *
// *                                                                                               *
// * Example 2: num1 = -num2:                                                                      *
// *            In example 2, a temporary variable is not necesssary:                              *
// *            num1 = stringint_copy (num2);                                                      *
// *            num1 = stringint_set_sign (num1, "-");                                             *
// *                                                                                               *
// * EXAMPLE 2: num1 = num2 + num3;                                                                *
// *            In example 2, a temporary variable is necessary:                                   *
// *            ___temp1___ = stringint_add (num2, num3);                                          *
// *            num1 = stringint_copy (___temp1__);                                                *
// *************************************************************************************************

// **************************************************************************************************************************
// * First check if we already have at least one temporary variable already created in the same scope level. Otherwise, I'd *
// * be creating a temporary variable for each SET statement in a given scope! Do not want to do that!                      *
// *                                                                                                                        *
// * First step is to reset all temporary variables that do not hold constants (i.e. general purpose storage) to "unused".  *
// *                                                                                                                        *
// * Second step: see if at least one temporary variable exists that is not dedicated to holding a constant. If one does    *
// *              exist, then we know we have at last one general-purpose variable in which to evaluate mathematical        *
// *              expressions.                                                                                              *
// **************************************************************************************************************************
                symbol_table_set_status_to_available_unused_temporary_variables_for_numerical_expression_processing (symbol_table, scope_level);
                temporary_storage_variable = retrieve_unused_temporary_variable_for_numerical_expression_processing (symbol_table, attribute_type, datatype, scope_level);
                if (temporary_storage_variable == NULL)
                {
                  counter_for_new_variables++;
                  snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                  while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
                  {
                    counter_for_new_variables++;
                    snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                  } 
                  symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], attribute_type, datatype, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                }
                state = 20;
              }
              else
              {
                state = 99999;
              }
            }
            else
            {
              state = 99999;
            }
          break;
          case 20:
            if (token_type == TOKEN_IDENTIFIER_INTEGER_CONSTANT)
            {
              counter_for_new_variables++;
              snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
              while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
              {
                counter_for_new_variables++;
                snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
              } 
              if (datatype == _int)
              {
                symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _int, 0, 0, 0, 0, scope_level, TRUE, TRUE);
                symbol_table_element = retrieve_symbol_table_element (symbol_table, &new_variable[0], attribute_type, scope_level);
                if (symbol_table_element != NULL)
                {
                  ast_element->symbol_table_entry = symbol_table_element;
                }
              }
              else
              {
                if (datatype == _float)
                {
                  symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _float, 0, 0, 0, 0, scope_level, TRUE, TRUE);
                  symbol_table_element = retrieve_symbol_table_element (symbol_table, &new_variable[0], attribute_type, scope_level);
                  if (symbol_table_element != NULL)
                  {
                    ast_element->symbol_table_entry = symbol_table_element;
                  }
                }
              }
            }
            else
            {
              if (token_type == TOKEN_IDENTIFIER_FLOATING_POINT_CONSTANT)
              {
                counter_for_new_variables++;
                snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
                {
                  counter_for_new_variables++;
                  snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                } 
                symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _float, 0, 0, 0, 0, scope_level, TRUE, TRUE);
                symbol_table_element = retrieve_symbol_table_element (symbol_table, &new_variable[0], attribute_type, scope_level);
                if (symbol_table_element != NULL)
                {
                  ast_element->symbol_table_entry = symbol_table_element;
                }
              }
              else
              {
                if ((token_type == TOKEN_TRUE) || (token_type == TOKEN_FALSE)) 
                {
                  counter_for_new_variables++;
                  snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                  while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
                  {
                    counter_for_new_variables++;
                    snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                  } 
                  symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _bool, 0, 0, 0, 0, scope_level, TRUE, TRUE);
                  symbol_table_element = retrieve_symbol_table_element (symbol_table, &new_variable[0], attribute_type, scope_level);
                  if (symbol_table_element != NULL)
                  {
                    ast_element->symbol_table_entry = symbol_table_element;
                  }
                }
                else
                {
                  if (token_type == TOKEN_ASSIGNMENT)
                  {
                    state = 0;
                  }
                }
              }
            }
          break;
          default:
          break;
        } 
      } 
      else
      {
        state = 99999;
      }
      if (state != 99999)
      {
        if (ast_element->sibling != NULL)
        {
          ast_element = ast_element->sibling;
        }
        else
        {
          ast_element = ast_element->child;
        }
      }
    }
// ********************************************************************************************************************
// * SECOND PASS: Use an expression stack to determine how many additional temporary variables need to be created in  *
// * order to evaluate two operands and one operator. Each of these evaluations then requires a temporary variable to *
// * store the result.                                                                                                *
// ********************************************************************************************************************
    if (state != 99999)
    {
      state = 0;
      expression_stack = NULL;
      ast_element = abstract_syntax_tree;
      while ((ast_element != NULL) && (state != 99999))
      {
        token_type = ast_element->token_type;
        token = ast_element->token;
        if (token != NULL)
        {
          //printf ("IN add_temporary_variables_to_symbol_table_to_evaluate_numerical_expressions (second pass): state = %d, ast_element->token = %s\n", state, token);
          //printf ("EXPRESSION STACK FRAME:");
          //display_expression_stack (expression_stack);
          //putchar ('\n');
          switch (state)
          {
            case 0:
              if (strncmp(token, "set", 3) == 0)
              {
                state = 10;
              }
            break;
            case 10:
              if (token_type == TOKEN_IDENTIFIER)
              {
                symbol_table_element = ast_element->symbol_table_entry;
                if (symbol_table_element != NULL)
                {
                  scope_level      = symbol_table_element->scope_level;
                  attribute_type   = symbol_table_element->attribute_type;
                  datatype         = symbol_table_element->datatype;              
                  expression_stack = push_expression_stack (expression_stack, token, token_type, NULL, NULL);
                  state = 20;
                }
                else
                {
                  state = 99999;
                }
              }
              else
              {
                state = 99999;
              }
            break;
            case 20:
              if ((token_type == TOKEN_IDENTIFIER) || (token_type == TOKEN_IDENTIFIER_INTEGER_CONSTANT) || (token_type == TOKEN_IDENTIFIER_FLOATING_POINT_CONSTANT) || 
                  (token_type == TOKEN_TRUE) || (token_type == TOKEN_FALSE))
              {
                expression_stack = push_expression_stack (expression_stack, token, token_type, NULL, NULL);
                state = 20;
              }
              else
              {
// **************************************************************************************************************************************************************************
// * Any math operation will require a temporary string integer variable to store the result. That will mean 8 bytes on the stack (64 bit pointer to a stringint datatype). *
// **************************************************************************************************************************************************************************
                if ((token_type == TOKEN_PLUS)     || 
                    (token_type == TOKEN_MINUS)    || 
                    (token_type == TOKEN_ASTERISK) || 
                    (token_type == TOKEN_DIVIDE)   || 
                    (token_type == TOKEN_CARET)    || 
                    (token_type == TOKEN_MODULO))
                {
                  if (expression_stack != NULL)
                  {
                    expression_stack = pop_expression_stack (expression_stack);
                  }
                  if (expression_stack != NULL)
                  {
                    expression_stack = pop_expression_stack (expression_stack);
                  }
                  counter_for_new_variables++;
                  snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                  while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
                  {
                    counter_for_new_variables++;
                    snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                  } 
                  if (datatype == _int)
                  {
                    symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _int, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                    expression_stack = push_expression_stack (expression_stack, NULL, TOKEN_UNKNOWN, &new_variable[0], NULL);
                    state = 20;
                  }
                  else
                  {
                    if (datatype == _float)
                    {
                      symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _float, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                      expression_stack = push_expression_stack (expression_stack, NULL, TOKEN_UNKNOWN, &new_variable[0], NULL);
                      state = 20;
                    }
                  }
                }
                else
                {
// ***************************************************************************************
// * Any Boolean operation will require a temporary 1-byte variable to store the result. *
// ***************************************************************************************
                  if ((token_type == TOKEN_AND)                   ||
                      (token_type == TOKEN_OR)                    ||
                      (token_type == TOKEN_EQUAL)                 ||
                      (token_type == TOKEN_NOT_EQUAL)             ||
                      (token_type == TOKEN_GREATER_THAN)          ||
                      (token_type == TOKEN_GREATER_THAN_OR_EQUAL) ||
                      (token_type == TOKEN_LESS_THAN)             ||
                      (token_type == TOKEN_LESS_THAN_OR_EQUAL))
                  {
                    if (expression_stack != NULL)
                    {
                      expression_stack = pop_expression_stack (expression_stack);
                    }
                    if (expression_stack != NULL)
                    {
                      expression_stack = pop_expression_stack (expression_stack);
                    }
                    counter_for_new_variables++;
                    snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                    while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
                    {
                      counter_for_new_variables++;
                      snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                    } 
                    if (datatype == _bool)
                    {
                      symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _bool, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                      expression_stack = push_expression_stack (expression_stack, NULL, TOKEN_UNKNOWN, &new_variable[0], NULL);
                      state = 20;
                    }
                    else
                    {
                      if (datatype == _int)
                      {
                        symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _int, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                        expression_stack = push_expression_stack (expression_stack, NULL, TOKEN_UNKNOWN, &new_variable[0], NULL);
                        state = 20;
                      }
                    }
                  }
                  else
                  {
                    if (token_type == TOKEN_UNARY_NOT)
                    {
                      if (expression_stack != NULL)
                      {
                        expression_stack = pop_expression_stack (expression_stack);
                      }
                      counter_for_new_variables++;
                      snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                      while (identifier_already_exists_in_symbol_table(symbol_table, &new_variable[0], scope_level))
                      {
                        counter_for_new_variables++;
                        snprintf (&new_variable[0], sizeof(new_variable), "___temp%lld___", counter_for_new_variables);
                      } 
                      if (datatype == _bool)
                      {
                        symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _bool, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                        expression_stack = push_expression_stack (expression_stack, NULL, TOKEN_UNKNOWN, &new_variable[0], NULL);
                      }
                      else
                      {
                        if (datatype == _int)
                        {
                          symbol_table = add_symbol_table_element (symbol_table, &new_variable[0], _datatype, _int, 0, 0, 0, 0, scope_level, TRUE, FALSE);
                          expression_stack = push_expression_stack (expression_stack, NULL, TOKEN_UNKNOWN, &new_variable[0], NULL);
                        }
                      }
                    }
                    else
                    {
                      if (token_type == TOKEN_ASSIGNMENT)
                      {
                        if (expression_stack != NULL)
                        {
                          expression_stack = pop_expression_stack (expression_stack);
                        }
                        if (expression_stack != NULL)
                        {
                          expression_stack = pop_expression_stack (expression_stack);
                        }
                        if (expression_stack == NULL) // expression stack should be empty since we came to an assignment operator in the postfix input. 
                        {
                          state = 0;
                        }
                        else
                        {
                          while (expression_stack != NULL) // empty the expression stack before exiting.
                          {
                            expression_stack = pop_expression_stack (expression_stack);
                          }
                          state = 99999;
                        }
                      }
                      else
                      {
                        state = 99999;
                      }
                    }
                  }
                }
              }
            break;
            default:
            break;
          } 
        }
        else
        {
          state = 99999;
        }
        if (state != 99999)
        {
          if (ast_element->sibling != NULL)
          {
            ast_element = ast_element->sibling;
          }
          else
          {
            ast_element = ast_element->child;
          }
        }
      }
    }
  }
  return symbol_table;
}




stackframe_node * add_stackframe_element (stackframe_node *stackframe_list, symtab_node *symbol_table_element, size_t stackframe_offset)
{
  stackframe_node *new_stackframe_element, *stackframe_element;
  chaga_attribute_type attribute_type;
  chaga_attribute_datatype datatype;

  if (symbol_table_element != NULL)
  {
    new_stackframe_element = (stackframe_node *) malloc(sizeof(stackframe_node));
    if (new_stackframe_element != NULL)
    {
      new_stackframe_element->stackframe_offset     = stackframe_offset;
      new_stackframe_element->symbol_table_entry    = symbol_table_element;
      new_stackframe_element->storage_size_in_bytes = 0;
      new_stackframe_element->next                  = NULL;
      if (symbol_table_element->attribute_type == _datatype)
      {
        if (symbol_table_element->datatype == _int)
        {
          new_stackframe_element->storage_size_in_bytes = 8; // An _int uses the stringint pointer datatype thus 8 bytes are required for pointer to a string integer.
        }
      }
      if (stackframe_list != NULL)
      { 
        stackframe_element = stackframe_list;
        while (stackframe_element->next != NULL)
        {
          stackframe_element = stackframe_element->next;
        }
        stackframe_element->next = new_stackframe_element;
      }
      else
      {
        stackframe_list = new_stackframe_element;
      }
    }
  }
  return stackframe_list;
}




token_node * delete_token_list (token_node *token_list)
{
  token_node *element;

  while (token_list != NULL)
  {
    element = token_list;
    token_list = token_list->next;
    if (element->token != NULL)
    {
      free (element->token);
    }
    free (element);
  }
  return token_list;
}




char * read_input_file (char *input_filename, size_t *file_buffer_size)
{
  FILE *fp;
  struct stat file_info;
  size_t bytes_read;
  char *file_buffer;

  file_buffer = NULL;
  if (input_filename != NULL)
  {
    if (stat (input_filename, &file_info) != -1)
    {
      *file_buffer_size = file_info.st_size;
      fp = fopen (input_filename, "r+b");
      if (fp != NULL)
      {
        file_buffer = (char *) malloc(sizeof(char) * *file_buffer_size);
        if (file_buffer != NULL)
        {
          bytes_read = fread (file_buffer, 1, *file_buffer_size, fp);
          if (bytes_read < *file_buffer_size)
          {
            printf ("Read error occured: file size is %li but only read %li bytes.\n", file_info.st_size, bytes_read);
            free (file_buffer);
            file_buffer = NULL;
            *file_buffer_size = 0;
           }
        }
        fclose (fp);
      }
    }
  }
  return file_buffer;
}




// ****************************************************************************************************************
// * retrieve_unused_temporary_variable_for_numerical_expression_processing                                       *
// *                                                                                                              *
// * I use temporary variables to evaluate numerical expressions. Examples of an numerical expressions include:   *
// *                                                                                                              *
// * EXAMPLE 1: If (1+foo*bar > 5) ...                                                                            *
// * EXAMPLE 2: bar = 100 - 34 + (6 - foo) * (bar - baz)                                                          *
// *                                                                                                              *
// * To evaluate these expressions, I need some sort of temporary (hidden) variables to evaluate the expression.  *
// * Temporary variables are absolutely necessary in the Chaga programming language since every integer is stored *
// * as a string (a stringint data structure). Even contsns like "100" or "6" are need to be stored as string     *
// * integers. Temporary variables handle these tasks. When a temporary variable is no longer needed, I free it   *
// * up in the symbol table.                                                                                      *
// ****************************************************************************************************************
symtab_node * retrieve_unused_temporary_variable_for_numerical_expression_processing (symtab_node *symbol_table, chaga_attribute_type attribute_type, chaga_attribute_datatype datatype, unsigned long long int scope_level)
{
  symtab_node *symbol_table_element;
  int found;

  found = FALSE;
  symbol_table_element = symbol_table;
  while ((symbol_table_element != NULL) && (!found))
  {
    if (symbol_table_element->scope_level == scope_level)
    {
      if (symbol_table_element->is_internal_use_only_variable == TRUE)
      {
        if (symbol_table_element->internal_use_only_variable_is_available == TRUE)
        {
          if (symbol_table_element->attribute_type == attribute_type)
          {
            if (symbol_table_element->datatype == datatype)
            {
              found = TRUE;
            }
          }
        }
      }
    }
    if (!found)
    {
      symbol_table_element = symbol_table_element->next;
    }
  }
  return symbol_table_element;
}




symtab_node * retrieve_symbol_table_element (symtab_node *symbol_table, char *identifier, chaga_attribute_type attribute_type, unsigned long long int scope_level)
{
  symtab_node *symbol_table_element;
  size_t identifier_string_length;
  int found;

  symbol_table_element = NULL;
  if (identifier != NULL)
  {
    identifier_string_length = strlen (identifier);
    found = FALSE;
    symbol_table_element = symbol_table;
    while ((symbol_table_element != NULL) && (!found))
    {
      if (symbol_table_element->identifier != NULL)
      {
        if (strncmp(symbol_table_element->identifier, identifier, identifier_string_length) == 0)
        {
          if (symbol_table_element->attribute_type == attribute_type)
          {
            if (symbol_table_element->scope_level == scope_level)
            {
              found = TRUE;
            }
          }
        }
      }
      if (!found)
      {
        symbol_table_element = symbol_table_element->next;
      }
    }
  }
  return symbol_table_element;
}




symtab_node * retrieve_symbol_table_element_for_function_or_procedure_name (symtab_node *symbol_table, char *identifier, chaga_attribute_type attribute_type)
{
  symtab_node *symbol_table_element;
  size_t identifier_string_length;
  int found;

  symbol_table_element = NULL;
  if (identifier != NULL)
  {
    identifier_string_length = strlen (identifier);
    found = FALSE;
    symbol_table_element = symbol_table;
    while ((symbol_table_element != NULL) && (!found))
    {
      if (symbol_table_element->identifier != NULL)
      {
        if (strncmp(symbol_table_element->identifier, identifier, identifier_string_length) == 0)
        {
          if (symbol_table_element->attribute_type == attribute_type)
          {
            found = TRUE;
          }
        }
      }
      if (!found)
      {
        symbol_table_element = symbol_table_element->next;
      }
    }
  }
  return symbol_table_element;
}




unsigned long long int retrieve_highest_scope_level (symtab_node *symbol_table)
{
  unsigned long long int highest_scope_level;
  symtab_node *symbol_table_element;

  highest_scope_level = 0;
  symbol_table_element = symbol_table;
  while (symbol_table_element != NULL)
  {
    if (symbol_table_element->scope_level > highest_scope_level)
    {
      highest_scope_level = symbol_table_element->scope_level;
    }
    symbol_table_element = symbol_table_element->next;
  }
  return highest_scope_level;
}




// ***************************************************************************************************************************************
// * This function determines if the input identifier already exists in the symbol table.                                                *
// *                                                                                                                                     *
// * A duplicate match returning TRUE will occur if ALL of the following conditions are satisfied:                                       *
// *                                                                                                                                     *
// *      (The input identifier identically matches byte-for-byte with another identifier and both identifiers are the same length.) AND *
// *      ((The matching identifier in the symbol table is a global variable)                             OR                             *
// *       (The matching identifier in the symbol table has the same scope level as the input identifier) OR                             *
// *       (The matching identifier is the name of a function or procedure))                                                             *
// ***************************************************************************************************************************************
int identifier_already_exists_in_symbol_table (symtab_node *symbol_table, char *input_identifier, unsigned long long int input_identifier_scope_level)
{
  symtab_node *symbol_table_element;
  int is_match, stop_searching, comparison_result;
  size_t input_identifier_string_length;

  is_match       = FALSE;
  stop_searching = FALSE;
  if (input_identifier != NULL)
  {
    input_identifier_string_length = strlen (input_identifier);
    symbol_table_element = symbol_table;
    while ((symbol_table_element != NULL) && (!is_match) && (!stop_searching))
    {
      if (symbol_table_element->identifier != NULL)
      {
        comparison_result = strncmp (symbol_table_element->identifier, input_identifier, input_identifier_string_length);
        if (comparison_result == 0)
        {
          if (strlen(symbol_table_element->identifier) == input_identifier_string_length)
          {
            if ((symbol_table_element->attribute_type == _function) || 
                (symbol_table_element->attribute_type == _procedure))
            {
              is_match = TRUE;
            }
            else
            {
              if (symbol_table_element->attribute_type == _datatype)
              {
                if ((symbol_table_element->scope_level == 0) || (symbol_table_element->scope_level == input_identifier_scope_level))
                {
                  is_match = TRUE;
                }
              }
            }
          }
        }
        else
        {
          if (comparison_result > 0)
          {
            stop_searching = TRUE; // Don't need to search whole list since it is in alphabetical order.
          }
        }
      }
      if ((!is_match) && (!stop_searching))
      {
        symbol_table_element = symbol_table_element->next;
      }
    }
  }
  return is_match;
}




void symbol_table_set_status_to_available_unused_temporary_variables_for_numerical_expression_processing (symtab_node *symbol_table, unsigned long long int scope_level)
{
  symtab_node *symbol_table_element;

  symbol_table_element = symbol_table;
  while (symbol_table_element != NULL)
  {
    if (symbol_table_element->scope_level == scope_level)
    {
      if (symbol_table_element->is_internal_use_only_variable == TRUE)
      {
        symbol_table_element->internal_use_only_variable_is_available = TRUE;
      }
    }
    symbol_table_element = symbol_table_element->next;
  }
}





stackframe_node * retrieve_stackframe_offset (stackframe_node *stackframe_cross_reference_table, symtab_node *symbol_table_entry)
{
  stackframe_node *stackframe_element, *matching_stackframe_element;
  int found;
 
  found = FALSE;
  matching_stackframe_element = NULL;
  stackframe_element = stackframe_cross_reference_table;
  while ((stackframe_element != NULL) && (!found))
  {
    if (stackframe_element->symbol_table_entry == symbol_table_entry)
    {
      found = TRUE;
      matching_stackframe_element = stackframe_element;
    }
    stackframe_element = stackframe_element->next;
  }
  return matching_stackframe_element;
}




stackframe_node * retrieve_stackframe_offset_for_input_datatype_identifier_name_and_scope (stackframe_node *stackframe_cross_reference_table, char *identifier, unsigned long long int scope_level)
{
  stackframe_node *stackframe_element;
  symtab_node     *symbol_table_element;
  size_t          identifier_string_length;
  int             found;

  stackframe_element = NULL;
  if (identifier != NULL)
  {
    identifier_string_length = strlen (identifier);
    if (identifier_string_length > 0)
    {
      found = FALSE;
      stackframe_element = stackframe_cross_reference_table;
      while ((stackframe_element != NULL) && (!found))
      {
        symbol_table_element = stackframe_element->symbol_table_entry;
        if (symbol_table_element != NULL)
        {
          if (symbol_table_element->scope_level == scope_level)
          {
            if (symbol_table_element->attribute_type == _datatype)
            {
              if (symbol_table_element->identifier != NULL)
              {
                if (strncmp(symbol_table_element->identifier, identifier, identifier_string_length) == 0)
                {
                  found = TRUE;
                }
              }
            }
          }
        }
        if (!found)
        {
          stackframe_element = stackframe_element->next;
        }
      }
    }
  }
  return stackframe_element;
}
