/*  This  defines Tokens  by inheritance from Envs

To use this  must include Token in your #include clause, e.g.,

#include <string.h>
#include "Tokens.h"

Author: Larry Morell

Revision history

Date       Modification
2/17/93    Original version
2/26/93    Added XUnitSym"s to the Token class
4/2/99     Rewrote in C++ from Pascal
3/27/2000  Added other initialization routines
*/

using namespace std;
#include <stdlib.h>
#include "Tokens.h"
#include "CharSets.h"
#include <iostream>
#include <string>

#define MAKELOCALS(P) TokenType type=P->type; char* val=P->val;
#define STORELOCALS(P) P->type=type;

/* ------------ Global Variables ------------------ */
char ch;   
const bool DEBUG=0;
/* ----------------------------------------------- */ 

// ------------- Prototypes ------------------------
void search (char *s, TokenType &type) ;
// -------------------------------------------------

void entering (char* s) { if (DEBUG) cout << "Entering: " << s << endl; }
void leaving (char* s) {if (DEBUG)  cout << "Leaving: " << s << endl; }

TokenType succ(TokenType s) {
  TokenType t;
  switch (s) {
  case assignSym: t = assignUnitSym; break;
  case assignUnitSym: t = beginSym; break;
  case beginSym: t = colonSym; break;
  case colonSym: t = constSym; break;
  case constSym: t = doSym; break;
  case doSym: t = doneSym; break;
  case doneSym: t = eofSym; break;
  case eofSym: t =  errorSym; break;
  case  errorSym: t = fiSym; break;
  case  fiSym: t = ifSym; break;
  case ifSym: t = ifthenSym; break;
  case ifthenSym: t = ifUnitSym; break;
  case ifUnitSym: t = initSym; break;
  case initSym: t = gotoSym; break;
  case gotoSym: t = gotoUnitSym; break;
  case gotoUnitSym: t = labelUnitSym; break;
  case labelUnitSym: t = minusSym; break;
  case minusSym: t = periodSym; break;
  case periodSym: t = plusSym; break;
  case plusSym: t = programUnitSym; break;
  case programUnitSym: t = stmtListUnitSym; break;
  case stmtListUnitSym: t = stmtUnitSym; break;
  case stmtUnitSym: t = thenSym; break;
  case thenSym: t = varSym; break;
  case varSym: t = whileSym; break;
  }
  return t;
}

Token::Token()  // initialization for an empty token
{
  entering("Token::Token()");
  P = new TokenDesc;
  P->type = initSym;
  strcpy(P->val,"");
  //Token::Token(initSym);
  leaving("Token::Token()");
}

Token::Token(TokenType C) // initialization
{
  entering("Token::Token(C)");
  P =  new TokenDesc;
  P->type = C;
  strcpy(P->val,"");
  //cout << "Val is " << P->val << "!"<< endl;
  leaving("Token::Token(C)");
}

Token::Token(TokenType C, char *s) // initialization
{
  entering("Token::Token(C)");
  P =  new TokenDesc;
  P->type = C;
  strcpy(P->val,s);
  //cout << "Val is " << P->val << "!"<< endl;
  leaving("Token::Token(C)");
}

void Token::init(TokenType C)
{ 
  entering("Token::init(C)");
  P =  new TokenDesc;
  P->type = C;
  strcpy(P->val,"");
  leaving("Token::init(C)");
}

void Token::init(TokenType C, char *s)
{ 
  entering("Token::init(C)");
  P =  new TokenDesc;
  P->type = C;
  strcpy(P->val,s);
  leaving("Token::init(C)");
}

Token::~Token()
{ 
   entering("Token::~Token");
   delete(P);
   leaving("Token::~Token");
}


void Token::done()
{
   entering("Token::done");
   delete(P);
   leaving("Token::done");
}

void Token::copy(Token &A) {
   A.done();
   initcopy(A);
}
void Token::initcopy(Token &A)
{
  entering("Token::copy");
    if (A.P != P){ /* safe to Copy */
     A.P =  new TokenDesc;
     A.P->type=P->type;
     for (int i=0; i < 40; i++) // copy contents
         A.P->val[i] = P->val[i];
  }
leaving("Token::copy");
}

char * tokenTypeStr(TokenType c)
{
  entering("tokenTypeStr");
  char *s = (char *) malloc(40);
  if (c == periodSym)          strcpy(s, "periodSym");
  else if (c == assignSym)     strcpy (s, "assignSym");
  else if (c == beginSym)      strcpy (s, "beginSym");
  else if (c == constSym)      strcpy (s, "constSym");
  else if (c == colonSym)      strcpy (s, "colonSym");
  else if (c == doSym)         strcpy (s, "doSym");
  else if (c == doneSym)       strcpy (s, "doneSym");
  else if (c == eofSym)        strcpy (s, "eofSym");
  else if (c == fiSym)         strcpy (s, "fiSym");
  else if (c == ifSym)         strcpy (s, "ifSym");
  else if (c == initSym)       strcpy (s, "initSym");
  else if (c == gotoSym)       strcpy (s, "gotoSym");
  else if (c == minusSym)      strcpy (s, "minusSym");
  else if (c == periodSym)     strcpy (s, "periodSym");
  else if (c == plusSym)       strcpy (s, "plusSym");
  else if (c == thenSym)       strcpy (s, "thenSym");
  else if (c == varSym)        strcpy (s, "varSym");
  else if (c == whileSym)      strcpy (s, "whileSym");
  else if (c == errorSym)      strcpy (s, "errorSym");
  else if (c == programUnitSym)strcpy (s, "programUnit");
  else if (c == stmtUnitSym)   strcpy (s, "stmtUnit");
  else if (c == stmtListUnitSym)strcpy (s, "stmtListUnit");
  else if (c == stmtUnitSym)   strcpy (s, "stmtUnit");
  else if (c == assignUnitSym) strcpy (s, "assignUnit");
  else if (c == ifUnitSym)     strcpy (s, "ifUnit");
  else if (c == labelUnitSym)  strcpy (s, "labelUnit");
  else if (c == gotoUnitSym)   strcpy (s, "gotoUnit");
  leaving("tokenTypeStr");
  return strdup(s);
}
void Token::write(Text F)
{
  if (P->type == varSym || P->type==constSym)
     cout << P->val;
  else
     cout << tokenTypeStr(P->type);
}



const char crKey = 13;
const char lfKey = 10;
const char spaceKey = ' ';
const char endfileChar = 26;
CharSet alpha ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
CharSet lowercase ("abcdefghijklmnopqrstuvwxyz");
CharSet digit ("0123456789");
CharSet oper(":+-<.\032");  

int moreChar()
{
  entering("moreChar");
  //cout << (!cin.eof() ? "more characters" : "no more chars")<< endl;
  leaving("moreChar");
  return !cin.eof();
}

char upCase(char c) 
{
  return c+ ('A'-'a');
}

void getChar()
{
  entering("getChar"); 
  if (moreChar())
  {
     cin.get(ch);
     //cout << "Got char" << ch << endl;
     if ((ch  != lfKey && ch !=endfileChar)) 
        cout << ch;
     if (ch == lfKey )
        cout << endl;
     else if (lowercase.contains(ch))
        ch = upCase(ch);
   }
   else {
     //cout << "Setting eof" <<endl;
     ch = endfileChar;
   }
  leaving("getChar");
}

void Token::getBuf(CharSet member)
{   
   entering("getBuf");
   MAKELOCALS(P); 
   val[0] = '\0';
   while (member.contains(ch))
   {
      val[strlen(val)] = ch;
      getChar();
   }
   val[strlen(val)+1] = '\0';
   leaving("getBuf");
}

/*Operations*/
void Token::get()
{ /* The actual scanner */
   entering("Token::get");
   //cout << "with ch='"<< (int)ch << "'" << endl;
   MAKELOCALS(P);
   for (int i=0; i < 40; i++) { // set all nulls
      val[i] = '\0';
   }
   if (! moreChar() ){
      type = eofSym;
   }
   else {
      CharSet legal;
      legal=alpha+digit+oper;
      while (! (legal.contains(ch)) )
      {
        if (ch != crKey && ch != lfKey
                        && ch !=  spaceKey
                        && ch != endfileChar) 
	   cout << "Illegal character in input" 
                << "'"
                << ch 
                <<"' -- SKIPPED"
                << endl;

        getChar();
      }
      //cout << "Checking if '" <<ch <<"' is a letter" << endl;
      if (alpha.contains(ch)) {
         getBuf(alpha);
         type = varSym;
/*
         if (strcmp(val,"IF") == 0)
            type = ifSym;
         else if (strcmp(val,"THEN") == 0)
            type = ifthenSym;
         else if (strcmp(val, "GOTO") == 0)
            type = gotoSym;
*/
        search (val,type);   // search bst for val, modify type if found
      }
      else if (digit.contains(ch)){
         getBuf(digit);
         type = constSym;
      }
      else if (ch == '<'){
         getChar();
         if (ch == '-'){
            type = assignSym;
            strcpy (val, "<-");
         }
         else {
            type = errorSym;
            val[0] = '<';
            val[1] =  ch;
         }
         getChar();
      }
      else if (ch == ':'){
         type = colonSym;
         val[0] = ch;
         getChar();
      }
      else if (ch == '+'){
         type = plusSym;
         val[0] = ch;
         getChar();
      }
      else if (ch == '-'){
         type = minusSym;
         val[0] = ch;
         getChar();
      }
      else if (ch == '.'){
         type = periodSym;
         val[0] = '.';
         getChar();
      }
      else if (ch == endfileChar){
         type = eofSym;
      }
      else
         getChar();
   }

   STORELOCALS(P);
   //cout << val << endl;
   leaving("Token::get");
}

int Token::size()
{
   return strlen(P->val);
}

int Token::equal(Token &A)
{
   return (A.P->type == P->type)
               &&
         (strcmp(A.P->val, P->val) ==0);
}


TokenType Token::type()
{
   return  P->type;
}

char* Token::val()
{
   entering("Token::val()");
   //cout << "value returned is " << P->val << "!" << endl;
   leaving("Token::val()");
   return P->val;
}

class BinNode {
  friend void inserthelp(BinNode* &bst, Token &t) ;
  friend void searchhelp(BinNode* bst, char * s, Token &t) ;
  private:
    static BinNode * bst; // The global tree
    BinNode *left, *right;
    Token info;
  public:
    virtual bool isLeaf () {return left ==right;} 
    BinNode (Token &t, BinNode *l, BinNode *r) {
       t.copy(info);
       left = l;
       right = r;
    }
    Token &val() { Token *t =new Token; info.copy(*t); return *t;}
    BinNode * leftChild () { return left;}
    BinNode * rightChild () { return right;}
};


BinNode* bst;
void searchhelp(BinNode* bst, char * s, Token &t) { 
   entering ("searchhelp");
   if (bst == NULL) 
      return;
   else if (strcmp(s, bst->val().val()) ==  0)
      bst->val().copy(t);
   else if (strcmp(s, bst->val().val()) <  0)
      searchhelp (bst->left, s, t);
   else
      searchhelp (bst->right,s,  t);
   leaving ("searchhelp");
}

void search (char *s, TokenType &type) {
   Token t(type);
   searchhelp(bst, s, t);
   type=t.type(); 
}

// inserthelp -- insert t into bst
void inserthelp(BinNode* &bst, Token &t) {
   entering ("inserthelp");
   if (bst == NULL) 
      bst = new BinNode (t, NULL, NULL);
   else if (strcmp(t.val(), bst->val().val()) < 0)
      inserthelp (bst->left, t);
   else
      inserthelp (bst->right, t);
   leaving ("inserthelp");
}

// insert -- insert type,s into bst
void insert (TokenType type, char *s) {
   entering("insert");
   Token t(type,s);
   inserthelp (bst,t); 
   leaving("insert");
}

int tindent=0;
void printTree (BinNode *n){
   if ( n != NULL) {
      for (int i=0; i<tindent; i++) cout << ' '; n->val();
      tindent = tindent+4; 
      printTree(n->leftChild());
      printTree(n->rightChild());
      tindent = tindent-4; 
   }
}

void setTokenSource(char * Filename)
{
   /* build a binary search tree for keywords */
   entering("setTokenSource");
   insert(fiSym, "FI"); 
   insert(doSym, "DO"); 
   insert(doneSym, "DONE");
   insert(thenSym, "THEN");
   insert(ifSym, "IF"); 
   insert(whileSym, "WHILE"); 
   //   printTree(bst);
   /* initialize global ch to a separator*/
   ch = ' ';
   leaving("setTokenSource");
}

