/**************************************************************************************
 * 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-symbol_table.h"

symtab_node * create_symbol_table (cst_node *concrete_syntax_tree)
{
  token_node               *token_element;
  symtab_node              *symbol_table, *temporary_symbol_table_element;
  cst_node                 *cst_element;
  param_node               *function_parameter_list, *procedure_parameter_list, *return_function_parameter_list;
  size_t                   token_string_length;
  int                      state, num_left_curly_braces, num_right_curly_braces, current_token_type;
  chaga_attribute_type     attribute_type;
  chaga_attribute_datatype datatype;
  char                     num_array_dimensions, *identifier, *current_function_identifier, *current_procedure_identifier, *current_token;
  unsigned long long int   first_indice_size, second_indice_size, third_indice_size, scope_level, line_number_for_identifier;

  symbol_table                   = NULL;
  function_parameter_list        = NULL;
  procedure_parameter_list       = NULL;
  return_function_parameter_list = NULL;
  current_function_identifier    = NULL;
  current_procedure_identifier   = NULL;
  scope_level = 0;
  state       = 0;
  cst_element = concrete_syntax_tree;
  while ((cst_element != NULL) && (state < 9999))
  { 
    current_token = cst_element->token;
    current_token_type = cst_element->token_type;
    if (current_token != NULL)
    {
      //printf ("state = %d, token = %s, scope = %lld\n", state, current_token->token, scope_level);
      switch (state)
      {
        case 0:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 6:
                if (strncmp(current_token, "define", 6) == 0)
                {
                  state = 2000;
                }
              break;
              case 7:
                if (strncmp(current_token, "declare", 7) == 0)
                {
                  attribute_type = _datatype;
                  state = 100;
                }
              break;
              default:
                state = 50;
              break;
            }
          }
        break;
        case 100:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 3:
                if (strncmp(current_token, "int", 3) == 0)
                {
                  datatype = _int;
                  state = 150;
                }
              break;
              case 4:
                if (strncmp(current_token, "bool", 4) == 0)
                {
                  datatype = _bool;
                  state = 150;
                }
                else
                {
                  if (strncmp(current_token, "char", 4) == 0)
                  {
                    datatype = _char;
                    state = 150;
                  }
                  else
                  {
                    if (strncmp(current_token, "date", 4) == 0)
                    {
                      datatype = _date;
                      state = 150;
                    }
                    else
                    {
                      if (strncmp(current_token, "file", 4) == 0)
                      {
                        datatype = _file;
                        state = 150;
                      }
                      else
                      {
                        if (strncmp(current_token, "time", 4) == 0)
                        {
                          datatype = _time;
                          state = 150;
                        }
                        else
                        {
                          state = 9999;
                        }
                      }
                    }
                  }
                }
              break;
              case 5:
                if (strncmp(current_token, "float", 5) == 0)
                {
                  datatype = _float;
                  state = 150;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 6:
                if (strncmp(current_token, "string", 6) == 0)
                {
                  datatype = _string;
                  state = 150;
                }
              break;
              case 7:
                if (strncmp(current_token, "complex", 7) == 0)
                {
                  datatype = _complex;
                  state = 150;
                }
                else
                {
                  if (strncmp(current_token, "pointer", 7) == 0)
                  {
                    datatype = _pointer;
                    state = 150;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              case 8:
                if (strncmp(current_token, "datetime", 8) == 0)
                {
                  datatype = _datetime;
                  state = 150;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 9:
                if (strncmp(current_token, "attribute", 9) == 0)
                {
                  datatype = _attribute;
                  state = 150;
                }
                else
                {
                  if (strncmp(current_token, "imaginary", 9) == 0)
                  {
                    datatype = _imaginary;
                    state = 150;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              default:
                state = 50;
              break;
            }
          }
        break;
        case 150:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            identifier = current_token;
            token_element = cst_element->token_entry;
            line_number_for_identifier = 0;
            if (token_element != NULL)
            {
              line_number_for_identifier = token_element->line_number;
            }
            num_array_dimensions = 0;
            first_indice_size    = 0;
            second_indice_size   = 0;
            third_indice_size    = 0;
            state = 200;
          }
          else
          {
            state = 9999;
          }
        break;
        case 200:
          if (current_token_type == TOKEN_COMMA)
          {
            if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
            {
              symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
              state = 150;
            }
            else
            {
              printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
              symbol_table = delete_symbol_table (symbol_table);
              state = 9999;
            }
          }
          else
          {
            if (current_token_type == TOKEN_LEFT_BRACKET)
            { 
              num_array_dimensions++;
              if (num_array_dimensions <= 4)
              {               
                state = 250;
              }
              else
              {
                state = 9999;
              }
            }
            else
            {
              if (current_token_type == TOKEN_SEMICOLON)
              {
                if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
                {
                  symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
                  state = 0;
                }
                else
                {
                  printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
                  symbol_table = delete_symbol_table (symbol_table);
                  state = 9999;
                }
              }
              else
              {
                state = 9999;
              }
            }
          }
        break;
        case 250:
          if (current_token_type == TOKEN_IDENTIFIER_INTEGER_CONSTANT)
          {
            if (num_array_dimensions == 1)
            {
              first_indice_size = atol (current_token);
            }
            else
            {
              if (num_array_dimensions == 2)
              {
                second_indice_size = atol (current_token);
              }
              else
              {
                if (num_array_dimensions == 3)
                {
                  third_indice_size = atol (current_token);
                }
              }
            }
            state = 300;
          }
          else
          {
            state = 9999;
          }
        break;    
        case 300:
          if (current_token_type == TOKEN_RIGHT_BRACKET)
          {
            state = 200;
          }
          else
          {
            state = 9999;
          }
        break;    
// **************
// * define ... *
// **************
        case 2000:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 8:
                if (strncmp(current_token, "function", 8) == 0)
                {
                  attribute_type                 = _function;
                  function_parameter_list        = NULL;
                  return_function_parameter_list = NULL;
                  current_function_identifier    = NULL;
                  num_left_curly_braces          = 0;
                  num_right_curly_braces         = 0;
                  scope_level                    = retrieve_highest_scope_level (symbol_table) + 1;
                  state = 3000;
                }
              break;
              case 9:
                if (strncmp(current_token, "procedure", 9) == 0)
                {
                  attribute_type                   = _procedure;
                  procedure_parameter_list         = NULL;
                  current_procedure_identifier     = NULL;
                  num_left_curly_braces            = 0;
                  num_right_curly_braces           = 0;
                  scope_level                      = retrieve_highest_scope_level (symbol_table) + 1;
                  state = 2050;
                }
              break;
              default:
                state = 9999;
              break;
            }
          }               
          else
          {
            state = 9999;
          }
        break;
// ************************
// * define procedure ... *
// ************************
        case 2050:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            identifier = current_token;
            line_number_for_identifier = 0;
            token_element = cst_element->token_entry;
            if (token_element != NULL)
            {
              line_number_for_identifier = token_element->line_number;
            }
            if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
            {
              num_array_dimensions = 0;
              first_indice_size    = 0;
              second_indice_size   = 0;
              third_indice_size    = 0;
              symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
              current_procedure_identifier = identifier;
              procedure_parameter_list = NULL;
              state = 2100;
            }
            else
            {
              printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
              symbol_table = delete_symbol_table (symbol_table);
              state = 9999;
            }
          }
          else
          {
            state = 9999;
          }
        break;
// *************************************
// * define procedure <IDENTIFIER> ... *
// *************************************
        case 2100:
          if (current_token_type == TOKEN_LEFT_PARENTHESIS)
          {
            state = 2150;
          }
          else
          {
            state = 9999;
          }
        break;
// ***************************************
// * define procedure <IDENTIFIER> ( ... *
// ***************************************
        case 2150:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            num_array_dimensions = 0;
            first_indice_size    = 0;
            second_indice_size   = 0;
            third_indice_size    = 0;
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 3:
                if (strncmp(current_token, "int", 3) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _int;
                  state = 2200;
                }
              break;
              case 4:
                if (strncmp(current_token, "bool", 4) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _bool;
                  state = 2200;
                }
                else
                {
                  if (strncmp(current_token, "char", 4) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _char;
                    state = 2200;
                  }
                  else
                  {
                    if (strncmp(current_token, "date", 4) == 0)
                    {
                      attribute_type = _datatype;
                      datatype = _date;
                      state = 2200;
                    }
                    else
                    {
                      if (strncmp(current_token, "file", 4) == 0)
                      {
                        attribute_type = _datatype;
                        datatype = _file;
                        state = 2200;
                      }
                      else
                      {
                        if (strncmp(current_token, "time", 4) == 0)
                        {
                          attribute_type = _datatype;
                          datatype = _time;
                          state = 2200;
                        }
                        else
                        {
                          if (strncmp(current_token, "void", 4) == 0)
                          {
                            attribute_type = _datatype;
                            datatype = _void;
                            state = 2210;
                          }
                          else
                          {
                            state = 9999;
                          }
                        }
                      }
                    }
                  }
                }
              break;
              case 5:
                if (strncmp(current_token, "float", 5) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _float;
                  state = 2200;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 6:
                if (strncmp(current_token, "string", 6) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _string;
                  state = 2200;
                }
              break;
              case 7:
                if (strncmp(current_token, "complex", 7) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _complex;
                  state = 2200;
                }
                else
                {
                  if (strncmp(current_token, "pointer", 7) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _pointer;
                    state = 2200;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              case 8:
                if (strncmp(current_token, "datetime", 8) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _datetime;
                  state = 2200;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 9:
                if (strncmp(current_token, "attribute", 9) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _attribute;
                  state = 2200;
                }
                else
                {
                  if (strncmp(current_token, "imaginary", 9) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _imaginary;
                    state = 2200;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              default:
                state = 9999;
              break;
            }
          }
          else
          {
            state = 9999;
          }
        break;
// **************************************************
// * define procedure <IDENTIFIER> ( <DATATYPE> ... *
// **************************************************
        case 2200:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            identifier = current_token;
            line_number_for_identifier = 0;
            token_element = cst_element->token_entry;
            if (token_element != NULL)
            {
              line_number_for_identifier = token_element->line_number;
            }
            state = 2250;
          }
          else
          {
            state = 9999;
          } 
        break;
// ********************************************
// * define procedure <IDENTIFIER> ( void ... *
// ********************************************
        case 2210:
          if (current_token_type == TOKEN_RIGHT_PARENTHESIS)
          {
            state = 2300;
          }
          else
          {
            state = 9999;
          }
        break;
// ***************************************************************
// * define procedure <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ... *
// ***************************************************************
        case 2250:
          if (current_token_type == TOKEN_COMMA)
          {
            if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
            {
              procedure_parameter_list = add_element_to_parameter_list (procedure_parameter_list, datatype, identifier);
              symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
              state = 2150;
            }
            else
            {
              printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
              symbol_table = delete_symbol_table (symbol_table);
// TODO: need to delete procedure_parameter_list if it is not null;
              state = 9999;
            }
          }
          else
          {
            if (current_token_type == TOKEN_RIGHT_PARENTHESIS)
            {
              if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
              {
                procedure_parameter_list = add_element_to_parameter_list (procedure_parameter_list, datatype, identifier);
                symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
                temporary_symbol_table_element = retrieve_symbol_table_element (symbol_table, current_procedure_identifier, _procedure, scope_level);
                if (temporary_symbol_table_element != NULL)
                {
                  temporary_symbol_table_element->parameter_list = procedure_parameter_list;
                }
                state = 2300;
              }
              else
              {
                printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
                symbol_table = delete_symbol_table (symbol_table);
                state = 9999;
              }
            }
            else
            {
              state = 9999;
            }
          }
        break;
// *****************************************************************
// * define procedure <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ) ... *
// *****************************************************************
        case 2300:
          if (current_token_type == TOKEN_LEFT_CURLY_BRACE)
          {
            num_left_curly_braces++;
            state = 5000;
          }
          else
          {
            state = 9999;
          }
        break;
// ***********************
// * define function ... *
// ***********************
        case 3000:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            identifier                  = current_token;
            current_function_identifier = identifier;

            line_number_for_identifier = 0;
            token_element = cst_element->token_entry;
            if (token_element != NULL)
            {
              line_number_for_identifier = token_element->line_number;
            }
            num_array_dimensions        = 0;
            first_indice_size           = 0;
            second_indice_size          = 0;
            third_indice_size           = 0;
            state                       = 3050;
          }
          else
          {
            state = 9999;
          }
        break;
// ************************************
// * define function <IDENTIFIER> ... *
// ************************************
        case 3050:
          if (current_token_type == TOKEN_LEFT_PARENTHESIS)
          {
            if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
            {
              symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
              state = 3100;
            }
            else
            {
              printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
              symbol_table = delete_symbol_table (symbol_table);
              state = 9999;
            }
          }
          else
          {
            state = 9999;
          }
        break;
// **************************************
// * define function <IDENTIFIER> ( ... *
// **************************************
        case 3100:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            num_array_dimensions = 0;
            first_indice_size    = 0;
            second_indice_size   = 0;
            third_indice_size    = 0;
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 3:
                if (strncmp(current_token, "int", 3) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _int;
                  state = 3150;
                }
              break;
              case 4:
                if (strncmp(current_token, "bool", 4) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _bool;
                  state = 3150;
                }
                else
                {
                  if (strncmp(current_token, "char", 4) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _char;
                    state = 3150;
                  }
                  else
                  {
                    if (strncmp(current_token, "date", 4) == 0)
                    {
                      attribute_type = _datatype;
                      datatype = _date;
                      state = 3150;
                    }
                    else
                    {
                      if (strncmp(current_token, "file", 4) == 0)
                      {
                        attribute_type = _datatype;
                        datatype = _file;
                        state = 3150;
                      }
                      else
                      {
                        if (strncmp(current_token, "time", 4) == 0)
                        {
                          attribute_type = _datatype;
                          datatype = _time;
                          state = 3150;
                        }
                        else
                        {
                          if (strncmp(current_token, "void", 4) == 0)
                          {
                            attribute_type = _datatype;
                            datatype = _void;
                            state = 3200;
                          }
                          else
                          {
                            state = 9999;
                          }
                        }
                      }
                    }
                  }
                }
              break;
              case 5:
                if (strncmp(current_token, "float", 5) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _float;
                  state = 3150;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 6:
                if (strncmp(current_token, "string", 6) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _string;
                  state = 3150;
                }
              break;
              case 7:
                if (strncmp(current_token, "complex", 7) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _complex;
                  state = 3150;
                }
                else
                {
                  if (strncmp(current_token, "pointer", 7) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _pointer;
                    state = 3150;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              case 8:
                if (strncmp(current_token, "datetime", 8) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _datetime;
                  state = 3150;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 9:
                if (strncmp(current_token, "attribute", 9) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _attribute;
                  state = 3150;
                }
                else
                {
                  if (strncmp(current_token, "imaginary", 9) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _imaginary;
                    state = 3150;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              default:
                state = 9999;
              break;
            }
          }
          else
          {
            state = 9999;
          }
        break;
// *************************************************
// * define function <IDENTIFIER> ( <DATATYPE> ... *
// *************************************************
        case 3150:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            identifier = current_token;
            line_number_for_identifier = 0;
            token_element = cst_element->token_entry;
            if (token_element != NULL)
            {
              line_number_for_identifier = token_element->line_number;
            }
            state = 3250;
          }
          else
          {
            state = 9999;
          } 
        break;
// *******************************************
// * define function <IDENTIFIER> ( void ... *
// *******************************************
        case 3200:
          if (current_token_type == TOKEN_RIGHT_PARENTHESIS)
          {
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE BELOW !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            state = 9999;
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE ABOVE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE ABOVE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE ABOVE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE ABOVE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEMPORARY VALUE ABOVE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
          }
          else
          {
            state = 9999;
          }
        break;
// **************************************************************
// * define function <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ... *
// **************************************************************
        case 3250:
          if (current_token_type == TOKEN_COMMA)
          {
            if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
            {
              function_parameter_list = add_element_to_parameter_list (function_parameter_list, datatype, identifier);
              symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
              state = 3100;
            }
            else
            {
              printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
              symbol_table = delete_symbol_table (symbol_table);
              state = 9999;
            }
          }
          else
          {
            if (current_token_type == TOKEN_RIGHT_PARENTHESIS)
            {
              if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
              {
                function_parameter_list = add_element_to_parameter_list (function_parameter_list, datatype, identifier);
                symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
                temporary_symbol_table_element = retrieve_symbol_table_element (symbol_table, current_function_identifier, _function, scope_level);
                if (temporary_symbol_table_element != NULL)
                {
                  temporary_symbol_table_element->parameter_list = function_parameter_list;
                }
                state = 3300;
              }
              else
              {
                printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
                symbol_table = delete_symbol_table (symbol_table);
                state = 9999;
              }
            }
            else
            {
              state = 9999;
            }
          }
        break;
// ****************************************************************
// * define function <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ) ... *
// ****************************************************************
        case 3300:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 6:
                if (strncmp(current_token, "return", 6) == 0)
                {
                  state = 3350;
                }
              break;
              default:
                state = 9999;
              break;
            }
          }
          else
          {
            state = 9999;
          }
        break;
// ***********************************************************************
// * define function <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ) return ... *
// ***********************************************************************
        case 3350:
          if (current_token_type == TOKEN_LEFT_PARENTHESIS)
          {
            state = 3400;
          }
          else
          {
            state = 9999;
          }
        break;
// *************************************************************************
// * define function <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ) return ( ... *
// *************************************************************************
        case 3400:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            num_array_dimensions = 0;
            first_indice_size    = 0;
            second_indice_size   = 0;
            third_indice_size    = 0;
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 3:
                if (strncmp(current_token, "int", 3) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _int;
                  state = 3450;
                }
              break;
              case 4:
                if (strncmp(current_token, "bool", 4) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _bool;
                  state = 3450;
                }
                else
                {
                  if (strncmp(current_token, "char", 4) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _char;
                    state = 3450;
                  }
                  else
                  {
                    if (strncmp(current_token, "date", 4) == 0)
                    {
                      attribute_type = _datatype;
                      datatype = _date;
                      state = 3450;
                    }
                    else
                    {
                      if (strncmp(current_token, "file", 4) == 0)
                      {
                        attribute_type = _datatype;
                        datatype = _file;
                        state = 3450;
                      }
                      else
                      {
                        if (strncmp(current_token, "time", 4) == 0)
                        {
                          attribute_type = _datatype;
                          datatype = _time;
                          state = 3450;
                        }
                        else
                        {
                          state = 9999;
                        }
                      }
                    }
                  }
                }
              break;
              case 5:
                if (strncmp(current_token, "float", 5) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _float;
                  state = 3450;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 6:
                if (strncmp(current_token, "string", 6) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _string;
                  state = 3450;
                }
              break;
              case 7:
                if (strncmp(current_token, "complex", 7) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _complex;
                  state = 3450;
                }
                else
                {
                  if (strncmp(current_token, "pointer", 7) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _pointer;
                    state = 3450;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              case 8:
                if (strncmp(current_token, "datetime", 8) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _datetime;
                  state = 3450;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 9:
                if (strncmp(current_token, "attribute", 9) == 0)
                {
                  attribute_type = _datatype;
                  datatype = _attribute;
                  state = 3450;
                }
                else
                {
                  if (strncmp(current_token, "imaginary", 9) == 0)
                  {
                    attribute_type = _datatype;
                    datatype = _imaginary;
                    state = 3450;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              default:
                state = 9999;
              break;
            }
          }
          else
          {
            state = 9999;
          }
        break;
// ************************************************************************************
// * define function <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ) return ( <DATATYPE> ... *
// ************************************************************************************
        case 3450:
          if (current_token_type == TOKEN_COMMA)
          {
            return_function_parameter_list = add_element_to_parameter_list (return_function_parameter_list, datatype, NULL);
            state = 3400;
          }
          else
          {
            if (current_token_type == TOKEN_RIGHT_PARENTHESIS)
            {
              return_function_parameter_list = add_element_to_parameter_list (return_function_parameter_list, datatype, NULL);
              temporary_symbol_table_element = retrieve_symbol_table_element (symbol_table, current_function_identifier, _function, scope_level);
              if (temporary_symbol_table_element != NULL)
              {
                temporary_symbol_table_element->return_parameter_list = return_function_parameter_list;
                state = 3500;
              }
              else
              {
                printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
                symbol_table = delete_symbol_table (symbol_table);
                state = 9999;
              }
            }
            else
            {
              state = 9999;
            }
          }
        break;
// **************************************************************************************
// * define function <IDENTIFIER> ( <DATATYPE> <IDENTIFIER> ) return ( <DATATYPE> ) ... *
// **************************************************************************************
        case 3500:
          if (current_token_type == TOKEN_LEFT_CURLY_BRACE)
          {
            num_left_curly_braces++;
            state = 5000;
          }
          else
          {
            state = 9999;
          }
        break;
// ********************************************************************************************************
// * We are now inside the scope of a procedure or function declaration processing individual statements. *
// ********************************************************************************************************
        case 5000:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 7:
                if (strncmp(current_token, "declare", 7) == 0)
                {
                  attribute_type = _datatype;
                  state = 5100;
                }
              break;
              default:
                state = 5050;
              break;
            }
          }
          else
          {
            if (current_token_type == TOKEN_LEFT_CURLY_BRACE)
            {
              num_left_curly_braces++;
              state = 5000;
            }
            else
            {
              if (current_token_type == TOKEN_RIGHT_CURLY_BRACE)
              {
                num_right_curly_braces++;
                if (num_left_curly_braces < num_right_curly_braces)
                {
                  state = 5000;
                }
                else
                {
                  if (num_left_curly_braces == num_right_curly_braces)
                  {
                    scope_level = 0;
                    state = 0;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              }
            }
          }
        break;
// ***************
// * declare ... *
// ***************
        case 5100:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            token_string_length = strlen (current_token);
            switch (token_string_length)
            {
              case 3:
                if (strncmp(current_token, "int", 3) == 0)
                {
                  datatype = _int;
                  state = 5150;
                }
              break;
              case 4:
                if (strncmp(current_token, "bool", 4) == 0)
                {
                  datatype = _bool;
                  state = 5150;
                }
                else
                {
                  if (strncmp(current_token, "char", 4) == 0)
                  {
                    datatype = _char;
                    state = 5150;
                  }
                  else
                  {
                    if (strncmp(current_token, "date", 4) == 0)
                    {
                      datatype = _date;
                      state = 5150;
                    }
                    else
                    {
                      if (strncmp(current_token, "file", 4) == 0)
                      {
                        datatype = _file;
                        state = 5150;
                      }
                      else
                      {
                        if (strncmp(current_token, "time", 4) == 0)
                        {
                          datatype = _time;
                          state = 5150;
                        }
                        else
                        {
                          state = 9999;
                        }
                      }
                    }
                  }
                }
              break;
              case 5:
                if (strncmp(current_token, "float", 5) == 0)
                {
                  datatype = _float;
                  state = 5150;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 6:
                if (strncmp(current_token, "string", 6) == 0)
                {
                  datatype = _string;
                  state = 5150;
                }
              break;
              case 7:
                if (strncmp(current_token, "complex", 7) == 0)
                {
                  datatype = _complex;
                  state = 5150;
                }
                else
                {
                  if (strncmp(current_token, "pointer", 7) == 0)
                  {
                    datatype = _pointer;
                    state = 5150;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              case 8:
                if (strncmp(current_token, "datetime", 8) == 0)
                {
                  datatype = _datetime;
                  state = 5150;
                }
                else
                {
                  state = 9999;
                }
              break;
              case 9:
                if (strncmp(current_token, "attribute", 9) == 0)
                {
                  datatype = _attribute;
                  state = 5150;
                }
                else
                {
                  if (strncmp(current_token, "imaginary", 9) == 0)
                  {
                    datatype = _imaginary;
                    state = 5150;
                  }
                  else
                  {
                    state = 9999;
                  }
                }
              break;
              default:
                state = 5050;
              break;
            }
          }
        break;
// **************************
// * declare <DATATYPE> ... *
// **************************
        case 5150:
          if (current_token_type == TOKEN_IDENTIFIER)
          {
            identifier = current_token;

            line_number_for_identifier = 0;
            token_element = cst_element->token_entry;
            if (token_element != NULL)
            {
              line_number_for_identifier = token_element->line_number;
            }
            num_array_dimensions = 0;
            first_indice_size    = 0;
            second_indice_size   = 0;
            third_indice_size    = 0;
            state = 5200;
          }
          else
          {
            state = 9999;
          }
        break;
// ***************************************
// * declare <DATATYPE> <IDENTIFIER> ... *
// ***************************************
        case 5200:
          if (current_token_type == TOKEN_COMMA)
          {
            if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
            {
              symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
              state = 5150;
            }
            else
            {
              printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
              symbol_table = delete_symbol_table (symbol_table);
              state = 9999;
            }
          }
          else
          {
            if (current_token_type == TOKEN_LEFT_BRACKET)
            { 
              num_array_dimensions++;
              if (num_array_dimensions <= 4)
              {               
                state = 5250;
              }
              else
              {
                state = 9999;
              }
            }
            else
            {
              if (current_token_type == TOKEN_SEMICOLON)
              {
                if (!identifier_already_exists_in_symbol_table(symbol_table, identifier, scope_level))
                {
                  symbol_table = add_symbol_table_element (symbol_table, identifier, attribute_type, datatype, num_array_dimensions, first_indice_size, second_indice_size, third_indice_size, scope_level, FALSE, FALSE);
                  state = 5000;
                }
                else
                {
                  printf ("Error on line %lld: %s already defined.\n", line_number_for_identifier, identifier);
                  symbol_table = delete_symbol_table (symbol_table);
                  state = 9999;
                }
              }
              else
              {
                state = 9999;
              }
            }
          }
        break;
// ***************************************************
// * declare <DATATYPE> <IDENTIFIER> <L_BRACKET> ... *
// ***************************************************
        case 5250:
          if (current_token_type == TOKEN_IDENTIFIER_INTEGER_CONSTANT)
          {
            if (num_array_dimensions == 1)
            {
              first_indice_size = atol (current_token);
            }
            else
            {
              if (num_array_dimensions == 2)
              {
                second_indice_size = atol (current_token);
              }
              else
              {
                if (num_array_dimensions == 3)
                {
                  third_indice_size = atol (current_token);
                }
              }
            }
            state = 5300;
          }
          else
          {
            state = 9999;
          }
        break;    
// *************************************************************************
// * declare <DATATYPE> <IDENTIFIER> <L_BRACKET> <INTEGER> <R_BRACKET> ... *
// *************************************************************************
        case 5300:
          if (current_token_type == TOKEN_RIGHT_BRACKET)
          {
            state = 5200;
          }
          else
          {
            state = 9999;
          }
        break;    


        default:
        break;
      }
    }
    else
    {
      state = 9999;
    }
    if ((state == 50) || (state == 5050))
    {
// ************************************************************************************
// * Advance to next statement since current statement is not a variable declaration. *
// ************************************************************************************
      if (state == 50)
      {
        state = 0;
      }
      else
      {
        state = 5000;
      }
      while (cst_element->sibling != NULL)
      {
        cst_element = cst_element->sibling;
      }
      cst_element = cst_element->child;
    }
    else
    {
      if (state != 9999)
      {
// ****************************************************************************
// * Current token IS a variable declaration; therefore, begin processing it. *
// ****************************************************************************
        if (cst_element->sibling != NULL)
        {
          cst_element = cst_element->sibling;
        }
        else
        {
          cst_element = cst_element->child;
        }
      }
    }
  }
  return symbol_table;
}
