#include <cstdlib>
#include <string>
#include <cstring>
#include <iostream>
#include <fstream>

#include "ksource.h"

using namespace std;

/* ----------------------------------------------------- */

void ksource_type::open (const string& name)
{
  kfile.open(name);
  if (kfile.fail())
  {
    cerr << "source file (" << name
         << ") not found!" << endl;
    exit(EXIT_FAILURE);
  }
  else
    done = false;
}

void ksource_type::close (void)
{
  kfile.close();
}

string ksource_type::get_opcode (void)
{
  string result("");
  int i = 0;
  if (buffer[pos] == '_')
  {
  // KCODE directive
    while (isalpha(buffer[pos]) ||
           (buffer[pos] == '_'))
      result += toupper(buffer[pos++]);
    trim_lead_blanks();
  }
  else if (isalpha(buffer[pos]))
  {
  // KCODE instruction
    while (isalnum(buffer[pos]))
      result += toupper(buffer[pos++]);
    trim_lead_blanks();
  }
  else
  {
  // invalid KCODE entry
    cerr << "invalid line: " << buffer << endl;
    exit (EXIT_FAILURE);
  }
  return result;
}

string ksource_type::get_operand (void)
{
  string result("");
  int i = 0;
  if (is_eol())
    return result;
  else if (isalpha(buffer[pos]))
  {
  // label or identifier
    while (isalnum(buffer[pos]))
      result += toupper(buffer[pos++]);
    trim_lead_blanks();
  }
  else if ((buffer[pos] == '\"') ||
           (buffer[pos] == '\''))
  {
    // character or string literal
    char terminal = buffer[pos];
    result += buffer[pos++];
    while (buffer[pos] != terminal)
      result += buffer[pos++];
    result += buffer[pos++];
    trim_lead_blanks();
  }
  else if ((buffer[pos] == '+') ||
           (buffer[pos] == '-') ||
           (isdigit(buffer[pos])))
  {
    // numeric literal: integer
    if ((buffer[pos] == '+') || (buffer[pos] == '-'))
      result += buffer[pos++];
    while (isdigit(buffer[pos]))
      result += buffer[pos++];
    // numeric literal: floating point
    if (buffer[pos] == '.')
    {
      result += buffer[pos++];
      while (isdigit(buffer[pos]))
        result += buffer[pos++];
    }
    // numeric literal: floating point
    if ((buffer[pos] == 'e') || (buffer[pos] == 'E'))
    {
      result += buffer[pos++];
      while (isdigit(buffer[pos]))
        result += buffer[pos++];
    }
    // numeric literal: floating point
    if ((buffer[pos] == 'e') || (buffer[pos] == 'E'))
    {
      result += buffer[pos++];
      if ((buffer[pos] == '+') || (buffer[pos] == '-'))
      {
        result +=  buffer[pos++];
        while (isdigit(buffer[pos]))
          result += buffer[pos++];
      }
    }
    trim_lead_blanks();
  }
  else if (buffer[pos] == '=')
  {
  // memory address
    result += buffer[pos++];
    while (isalnum(buffer[pos]))
      result += toupper(buffer[pos++]);
    trim_lead_blanks();
  }
  else
  {
    trim_lead_blanks ();
  }
  return result;
}

bool ksource_type::read_line (void)
{
  if (kfile.eof())
  {
    done = true;
    buffer = "DONE#";
    pos = 0;
    return false;
  }
  getline(kfile,buffer);
  buffer += '#';
  pos = 0;
  trim_lead_blanks();
  if (is_eol())
    return read_line();
  else
    return true;
}

void ksource_type::trim_lead_blanks (void)
{
 while ((pos < buffer.size()) &&
        (isblank(buffer[pos])))
    pos++;
}

bool ksource_type::is_eol (void) const
{
  if ((pos < buffer.size()) &&
      (buffer[pos] == '#'))
    return true;
  else
    return false;
}

bool ksource_type::is_eof (void) const
{
  return done;
}
