/*  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) ;

/* ------------ 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 ctagSym: t = eofSym ; break;
  case eofSym: t = errorSym  ; break;
  case errorSym: t = otagSym ; break;
  case otagSym: t = stuffSym ; 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 == ctagSym)        strcpy (s, "ctagSym");
  else if (c == initSym)        strcpy (s, "initSym");
  else if (c == otagSym)        strcpy (s, "otagSym");
  else if (c == stuffSym)        strcpy (s, "stuffSym");
  else if (c == eofSym)        strcpy (s, "eofSym");
  else if (c == errorSym)        strcpy (s, "errorSym");
  leaving("tokenTypeStr");
  return strdup(s);
}
void Token::write(Text F)
{
     cout << "[" << tokenTypeStr(P->type) << ", " << P->val <<  "]" ;
}



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);
  }
  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);
      while (moreChar() && ch != '<')
         getChar();
      cout << "Found '" << (int)ch << "' with moreChar =="<<  moreChar()<< endl;
      if (ch == endfileChar){
         type = eofSym;
      }
      else {  // char must be a <
         getChar(); val = "";
         if (!moreChar()) {
            type = eofSym;
         }
         else {
            if (ch == '/')  {
               type = ctagSym;
               getChar();
            }
            else 
               type = otagSym;

            while (moreChar() && ch != '>') {
               val = val + ch;  
               getChar();
            }
            // ch is now a '>' or we've hit eof
            if ( !moreChar()) {
               cout << "Tag end not found when reading tag. Found '" << (int)ch << "'"<<  endl;
               exit(1);
            }
         }

      }

   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");
}

