/*****************************************************************************\
*                                                                             *
*  Name   : lexcaser                                                          *
*  Author : Chris Koeritz                                                     *
*                                                                             *
*******************************************************************************
* Copyright (c) 1991-$now By Author.  This program 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 2 of   *
* the License or (at your option) any later version.  This is online at:      *
*     http://www.fsf.org/copyleft/gpl.html                                    *
* Please send any updates to: fred@gruntose.com                               *
\*****************************************************************************/

#include <basis/istring.h>
#include <loggers/console_logger.h>
#include <data_struct/static_memory_gremlin.h>

#include <stdio.h>
#include <stdlib.h>

HOOPLE_STARTUP_CODE;

console_logger out;

void print_instructions_and_exit(const char *program_name)
{
  out.log(istring("\n") +  program_name +
": This program modifies a lex file that is grabbed from standard input\n\
by turning the characters in each quoted item that are lower case into a\n\
token rule that accepts either case.\n\n\
For example, the rule specified by:\n\n\
\t\"pragma\"               { u; r(PRAGMA_); };\n\
\n\nwould become:\n\n\
\t[pP][rR][aA][gG][mM][aA]               { u; r(PRAGMA_); };\n\
");
  exit(1);
}

void modify_character(char to_modify)
{
  istring to_up(to_modify, 1);
  to_up.to_upper();
  out.log(istring("[") + istring(to_modify, 1) + to_up + "]");
}

int main(int argc, char *argv[])
{
  out.eol(log_base::NO_ENDING);  // set for proper ending.

  if (argc != 1) print_instructions_and_exit(argv[0]);

  enum states {
    CHECKING_FIRST_POSITION,
    ATE_A_QUOTE,
    NOW_MODIFYING,
    AFTER_FIRST_POSITION,
    EATING_RETURN
  };
  states state = CHECKING_FIRST_POSITION;
  int quote_eaten_on_this_line = false;
  while (!feof(stdin)) {
    byte current = getc(stdin);
    if (current == '\n') {
      state = EATING_RETURN;
      quote_eaten_on_this_line = false;
    } else if (current == byte(EOF)) break;
    switch (state) {
    case EATING_RETURN:
      out.log(istring(current, 1));
      state = CHECKING_FIRST_POSITION;
      break;
    case CHECKING_FIRST_POSITION:
      if (current == '"') state = ATE_A_QUOTE;
      else {
        state = AFTER_FIRST_POSITION;
        out.log(istring(current, 1));
      }
      break;
    case AFTER_FIRST_POSITION:
      if (current != '"') out.log(istring(current, 1));
      // the first quote after the initially eaten quote is not printed.
      else if (quote_eaten_on_this_line) quote_eaten_on_this_line = false;
      else out.log(istring(current, 1));
      break;
    case ATE_A_QUOTE:
      if ( (current <= 'z') && (current >= 'a') ) {
        quote_eaten_on_this_line = true;
        modify_character(current);
        state = NOW_MODIFYING;
      } else {
        state = AFTER_FIRST_POSITION;
        out.log(istring("\"") + istring(current, 1));
      }
      break;
    case NOW_MODIFYING:
      if ( (current <= 'z') && (current >= 'a') ) modify_character(current);
      else if (current == ' ') out.log(istring("\\") + istring(current, 1));
      else if (current == '_') out.log(istring(current, 1));
      else if (current == '"') {
        state = AFTER_FIRST_POSITION;
        // reset the status for that quote, since we are eating it here.
        quote_eaten_on_this_line = false;
      } else {
        state = AFTER_FIRST_POSITION;
        out.log(istring(current, 1));
      }
      break;
    }
  }
  return 0;
}

#ifdef __BUILD_STATIC_APPLICATION__
  // static dependencies found by buildor_gen_deps.sh:
  #include <basis/array.cpp>
  #include <basis/byte_array.cpp>
  #include <basis/callstack_tracker.cpp>
  #include <basis/chaos.cpp>
  #include <basis/convert_utf.cpp>
  #include <basis/definitions.cpp>
  #include <basis/earth_time.cpp>
  #include <basis/guards.cpp>
  #include <basis/istring.cpp>
  #include <basis/log_base.cpp>
  #include <basis/memory_checker.cpp>
  #include <basis/mutex.cpp>
  #include <basis/object_base.cpp>
  #include <basis/outcome.cpp>
  #include <basis/packable.cpp>
  #include <basis/portable.cpp>
  #include <basis/sequence.cpp>
  #include <basis/set.cpp>
  #include <basis/utility.cpp>
  #include <basis/version_record.cpp>
  #include <data_struct/amorph.cpp>
  #include <data_struct/bit_vector.cpp>
  #include <data_struct/byte_hasher.cpp>
  #include <data_struct/configurator.cpp>
  #include <data_struct/hash_table.cpp>
  #include <data_struct/pointer_hash.cpp>
  #include <data_struct/stack.cpp>
  #include <data_struct/static_memory_gremlin.cpp>
  #include <data_struct/string_hash.cpp>
  #include <data_struct/string_hasher.cpp>
  #include <data_struct/string_table.cpp>
  #include <data_struct/symbol_table.cpp>
  #include <data_struct/table_configurator.cpp>
  #include <loggers/console_logger.cpp>
  #include <loggers/file_logger.cpp>
  #include <loggers/locked_logger.cpp>
  #include <loggers/null_logger.cpp>
  #include <loggers/program_wide_logger.cpp>
  #include <opsystem/byte_filer.cpp>
  #include <opsystem/command_line.cpp>
  #include <opsystem/critical_events.cpp>
  #include <opsystem/directory.cpp>
  #include <opsystem/filename.cpp>
  #include <opsystem/ini_config.cpp>
  #include <opsystem/ini_parser.cpp>
  #include <opsystem/path_configuration.cpp>
  #include <opsystem/rendezvous.cpp>
  #include <textual/byte_format.cpp>
  #include <textual/parser_bits.cpp>
  #include <textual/string_manipulation.cpp>
  #include <textual/tokenizer.cpp>
#endif // __BUILD_STATIC_APPLICATION__

