/* token.H
 * John Viega
 *
 * Jul 28, 1999 (with some mods in Jan 2000)
 */

#ifndef __TOKEN_H__
#define __TOKEN_H__

#include "config.H"
#include "fatal.H"

typedef enum token_id_enum 
      { 
	OPERATOR, 
	STRING,  
	CHAR,
	IDENTIFIER,
	INTEGER, 
	REAL,
	PREPROC_START,
	PREPROC_END,
        PREPROC_NUM,
	COMMENT
      } TokenId;

const int DEFAULT_TOKEN_CONTAINER_UNIT = 1024;
class Token 
{
public:
  // RTTI isn't too portable, so just use this hack.
  TokenId GetTokenType() { return type; }
  int     GetLineNo() { return lineno; }
  virtual char  * GetValue() = 0;
  virtual int AllocedValue() = 0;
  virtual ~Token() {}
  //##TODO: You are responsible for deletes right now.
  virtual char* GetRepr() = 0;
protected:
  TokenId type;
  int lineno;
};

class TokenContainer 
{
public:
  TokenContainer();
  TokenContainer(int incr);
  ~TokenContainer()
  {
    for(int i=0;i<current_size;i++)
      {
	Token *t = list[i];
	delete t;
      }
    delete[] list;
  }
  void Add(Token* t);
  Token* GetToken(int i);
  int GetCurrentSize() { return current_size; }
  /* Debugging  */
  void PrintAll(); 
private:
  Token **list;
  int increment;
  int current_size;
  int current_capacity;
  void Resize();
};

class OperatorTok : public Token {
public:
  // char *name is almost always staticly alloc'd here.
  OperatorTok(char *name, int line)
    {
      lineno = line;
      type = OPERATOR;
      opname = name;
    }
  char *GetOperatorName() { return opname; }
  char *GetValue() { return GetOperatorName(); }
  int  AllocedValue() { return 0; }
  char *GetRepr()
    {
      char *fmt = "OPERATOR %s <line=%d>";
      int  len  = sizeof(lineno)*3 + strlen(GetOperatorName()) + 
	          strlen(fmt);
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
      // ITS4: ignore sprintf
      sprintf(buf, fmt, GetOperatorName(), lineno);
      return buf;
    }
private:
  char *opname;
};

class CommentTok : public Token {
 public:
  CommentTok(char *val, unsigned int str_size, int line, int cpp, int tindex, 
	     int line2, int df)
    {
      dont_free = df;
      type      = COMMENT;
      value     = val;
      val_len   = str_size;
      lineno    = line;
      cpp_style = cpp;
      tok_index = tindex;
      endline   = line2;
    }
  virtual ~CommentTok()
    {
      if(value && !dont_free) delete[] value;
    }
  int GetTokenIndex() { return tok_index; }
  char *GetValue() { return value; }
  int  AllocedValue() { return 0; }
  int GetEndLineNo() { return endline; }
  char *GetRepr()
    {
      char *format = "COMMENT <value = %s> <line=%d> <style=%s>\n";
      int len = strlen(format) + val_len + sizeof(lineno)*3 + 3;
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
#ifdef HAVE_SNPRINTF
      // The format string is constant, as above.
      // ITS4: Ignore snprintf 
      snprintf(buf, len, format, GetValue(), lineno, 
	       cpp_style ? "c++" : "c");
#else
      if(val_len < strlen(GetValue()))
	{
	  fprintf(stderr, "Internal inconsistancy, sorry :-(\n");
	  exit(-3);
	}
      // ITS4: Ignore sprintf
      sprintf(buf, format, GetValue(), lineno, cpp_style ? "c++" : "c");
#endif
      return buf;
    }
 private:
  int dont_free;
  char *value;
  unsigned int val_len;
  int cpp_style;
  int tok_index;
  int endline;
};

class StringTok : public Token {
public:
  StringTok(char *val, unsigned int str_size, int line)
    {
      type = STRING;
      value = val;
      val_len = str_size;
      lineno = line;
    }
  virtual ~StringTok()
  {
    if(value)
      delete[] value;
  }

  char *GetContents() { return value; }
  int  GetContentLength() { return val_len; }
  char *GetValue()
  {
    char *str = new char[val_len+3];
    sprintf(str, "\"%s\"", value); // ITS4: ignore sprintf
    return str;
  }
  int  AllocedValue() { return 1; }
  char *GetRepr()
    {
      char *format = "STRING <value = %s> <line=%d>";
      unsigned int  len = strlen(format) + val_len + sizeof(lineno)*3;
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
#ifdef HAVE_SNPRINTF
      // The format string is constant, as above.
      // ITS4: Ignore snprintf
      snprintf(buf, len, format, GetValue(), lineno);
#else
      if(val_len +2 < strlen(GetValue()))
	{
	  fprintf(stderr, "Internal inconsistancy, sorry...\n");
	  exit(-4);
	}
      // ITS4: Ignore sprintf
      sprintf(buf, format, GetValue(), lineno);
#endif
      return buf;
    }
private:
  char *value;
  unsigned int  val_len;
};

class CharTok : public Token {
public:
  CharTok(long val, int line)
    {
      lineno = line;
      type = CHAR;
      value = val;
    }
  long GetNumericValue() { return value; }
  char *GetValue() {
    char *buf = new char[13];
    sprintf(buf, "chr(%ld)", value);
    return buf;
  }
  int  AllocedValue() { return 1; }
  char *GetRepr()
    {
      char *format = "CHAR <ascii=%ld> <line=%d>";
      unsigned int  len = strlen(format) + sizeof(value)*3 + sizeof(lineno)*3;
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
      sprintf(buf, format, value, lineno);  // ITS4: ignore sprintf
      return buf;
    }
private:
  long value;
};

class IdTok : public Token {
public:
  virtual ~IdTok()
  {
    delete[] value;
  }

  IdTok(char *name, unsigned int len_str, int line, int line2)
    {
      lineno  = line;
      type    = IDENTIFIER;
      value   = name;
      val_len = len_str;
      endline = line2;
    }
  int GetEndLineNo() { return endline; }
  char *GetName() { return value; }
  char *GetValue(){ return GetName(); }
  int  AllocedValue() { return 0; }
  char *GetRepr()
    {
      char *format = "ID <name = %s> <line=%d>";
      int  len = strlen(format) + val_len + sizeof(lineno)*3;
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
#ifdef HAVE_SNPRINTF     
      // The format string is constant, as above.
      // ITS4: Ignore snprintf
      snprintf(buf, len, format, GetName(), lineno);
#else
      if(val_len < strlen(GetName()))
	{
	  fprintf(stderr, "Internal inconsistancy, sorry!\n");
	  exit(-3);
	}
      sprintf(buf, format, GetName(), lineno); // ITS4: ignore sprintf
#endif
      return buf;
    }

private:
  char *value;
  unsigned int  val_len;
  int  endline;
};

class IntegerTok : public Token {
public:
  IntegerTok(long num, int u, int l, int line) {
    lineno = line;
    type = INTEGER;
    value = num;
    is_unsigned = u;
    is_long = l;
  }
  
  char *GetRepr()
    {
      char *format = "INT <value = %ld> <long=%c> <unsigned=%c> <line=%d>";
      int  len = strlen(format) + sizeof(GetValue()) * 3 + sizeof(lineno) * 3;
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
      // ITS4: ignore sprintf
      sprintf(buf, format, GetValue(), IsLong() ? 't' : 'f', 	
       IsUnsigned() ? 't' : 'f', lineno);
      return buf;
    }

  long GetNumericValue() { return value; }

  char *GetValue() {
    char *buf = new char[13];
    sprintf(buf, "%ld", value);
    return buf;
  }
  int  AllocedValue() { return 1; }

  int IsLong() { return is_long; }
  int IsUnsigned() { return is_unsigned; }
private:
  long value;
  int is_unsigned;
  int is_long;
};

typedef enum real_size_enum {FLOAT, DOUBLE, LONG_DOUBLE} RealSize;
class RealTok : public Token {
public:
  RealTok(long i, long m, long e, RealSize s, int line) {
    lineno = line;
    type = REAL;
    integer_part = i;
    mantissa = m;
    exponent = e;
    size = s;
  }

  char *GetValue()
  {
    char *arr = new char[30];
    sprintf(arr, "%ld.%lde%ld", GetIntegerPart(), GetMantissa(), GetExponent());
    return arr;
  }

  int  AllocedValue() { return 1; }

  char *GetRepr()
    {
      char *format = "REAL <INTPART = %ld> <MANT=%ld> <EXP=%ld> <size=%c> <line=%d>";
      int  len = strlen(format) + sizeof(GetIntegerPart())*3 + 
	         sizeof(GetMantissa()) * 3 + sizeof(GetExponent())*3 +
	         sizeof(lineno) * 3;

      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
      // ITS4: ignore sprintf
      sprintf(buf, format, GetIntegerPart(), GetMantissa(), GetExponent(),
	       (size == FLOAT) ? 'f' : (size == DOUBLE) ? 'd' : 'l', lineno);
      return buf;
    }

  long GetIntegerPart() { return integer_part; }
  long GetMantissa() { return mantissa; }
  long GetExponent() { return exponent; }
private:
  long integer_part;
  long mantissa;
  long exponent;
  RealSize  size;
};

typedef 
enum preproc_cond_type_enum {IF, ELIF, ELSE, IFDEF, IFNDEF}
PreprocCond;

class PreprocStartToken : public Token {
public:
  PreprocStartToken(int line)
    {
      lineno = line;
      type = PREPROC_START;
    }
  char *GetValue() { return "#"; }
  int  AllocedValue() { return 0; }
  char *GetRepr()
    {
      char *fmt = "PREPROC_START <line=%d>";
      int len = sizeof(lineno)*3 + strlen(fmt);
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
      sprintf(buf, fmt, lineno); // ITS4: ignore sprintf
      return buf;
    }

};

typedef 
enum preproc_macro_type_enum {DEF, UNDEF}
PreprocMacro;


class PreprocEndToken : public Token {
public:
  PreprocEndToken(int line)
    {
      lineno = line;
      type = PREPROC_END;
    }
  char *GetValue() { return "<<null>>"; }
  int  AllocedValue() { return 0; }
  char *GetRepr()
    {
      char *fmt = "PREPROC_END <line=%d>";
      int len = sizeof(lineno)*3 + strlen(fmt);
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
      sprintf(buf, fmt, lineno); // ITS4: ignore sprintf
      return buf;
    }
};


class PreprocNumTok : public Token {
public:
  PreprocNumTok(char *val, unsigned int str_size, int line)
    {
      type = PREPROC_NUM;
      value = val;
      val_len = str_size;
      lineno = line;
    }
  virtual ~PreprocNumTok()
  {
    if(value)
      delete[] value;
  }

  char *GetContents() { return value; }
  int  GetContentLength() { return val_len; }
  char *GetValue()
  {
    char *str = new char[val_len+3];
    sprintf(str, "\"%s\"", value); // ITS4: ignore sprintf
    return str;
  }
  int  AllocedValue() { return 1; }
  char *GetRepr()
    {
      char *format = "PPNUM <value = %s> <line=%d>";
      unsigned int  len = strlen(format) + val_len + sizeof(lineno)*3;
      char *buf = new char[len];
      if(!buf)
	OutOfMemory();
#ifdef HAVE_SNPRINTF
      // The format string is constant, as above.
      // ITS4: Ignore snprintf
      snprintf(buf, len, format, GetValue(), lineno);
#else
      if(val_len +2 < strlen(GetValue()))
	{
	  fprintf(stderr, "Internal inconsistancy, sorry...\n");
	  exit(-4);
	}
      // ITS4: Ignore sprintf
      sprintf(buf, format, GetValue(), lineno);
#endif
      return buf;
    }
private:
  char *value;
  unsigned int  val_len;
};

#endif
