#include <assert.h>
#include <ctype.h>
#include "lexan.h"
#include "nodes.h"
#include "sTable.h"

using namespace std;

//konstanty VHDL
#define L_END 0
#define L_ERR 1
#define L_LPARENT 2
#define L_RPARENT 3
#define L_IDENT 4
#define L_NUMBER 5
#define L_EQUATE 6
#define L_AND 7
#define L_OR 8
#define L_NOT 9
#define L_SEMICOLON 10
#define L_LBRACKET 11
#define L_RBRACKET 12
#define L_LSETBRACKET 13
#define L_RSETBRACKET 14
#define L_ASSIGN 15
#define L_OPASSIGN 16
#define L_LVECTOR 17
#define L_LVALUE 18
#define L_XOR 19
#define L_NAND 20
#define L_NOR 21
#define L_XNOR 22
#define L_VAR 23
#define L_COMMA 24


#define NRKEYWORDS 18
#define LENGTHKEYW 20
#define MAXSTRLENGTH 100

#define S_NOT 0
#define S_AND 1
#define S_OR 2
#define S_NAND 3
#define S_NOR 4
#define S_XOR 5
#define S_XNOR 6
#define S_ASSIGN 7


unsigned int L_Value;
int L_VLength = 1;
istream * in_file;
//ofstream * out_file;
string lex_str;
int line = 1;
bool err = false;

#define NRERRSTRINGS 5
string statement [NRERRSTRINGS]=
{
  "Invalid lexical element at line:",
  "Too long identifier at line:",
  "Syntax error at line:",
  "Warning: Different vector lengths at line:",
  "Warning: Invalid output index at line:"
};


struct Keyword
{
 int token;
 string str; 
 //char str[LENGTHKEYW]; //pro borland win32 prekladac
};

//klicova slova pro VHDL
Keyword Keywords[NRKEYWORDS] =
{
 {L_AND, "and"},
 {L_OR, "or"},
 {L_NOT, "not"},
 {L_XOR, "xor"},
 {L_NAND, "nand"},
 {L_NOR, "nor"},
 {L_XNOR, "xnor"},
 {L_AND, "AND"},
 {L_OR, "OR"},
 {L_NOT, "NOT"},
 {L_XOR, "XOR"},
 {L_NAND, "NAND"},
 {L_NOR, "NOR"},
 {L_XNOR, "XNOR"},
 {L_VAR, "variable"},
 {L_VAR, "VARIABLE"}
};


//ziskani delky naposledy cteneho vektoru
int getVLength()
{
	return L_VLength;
} 

//varovne hlaseni
void warning(int j)
{
	cout<<statement[j]<<line<<endl;
}

//chybove hlaseni
void error(int i)
{
	cout<<statement[i]<<line<<endl;
	err = true;
}

/*test zda je dany retezec klicove slovo*/
int iskeyword(string& s)
{
  for (int i=0;i<NRKEYWORDS;i++)
  {
    if (Keywords[i].str == s) return Keywords[i].token;
  }

 return -1;
}


/*==========================================*/
/*lexikalni analyza-cteni lexikalniho tokenu*/
int gettoken ()
{
 int state=0, act_char=0;
 lex_str="";
 L_Value=0;
 //L_VLength=0; presunuto

 while(1)
 {
   act_char=in_file->get();

   switch (state)
   {
   	 //pocatecni stav
     case 0:
     {
	   
	   //typy zavorek
       if (act_char=='(') return (L_LPARENT);
       if (act_char==')') return (L_RPARENT);
       if (act_char=='[') return (L_LBRACKET);
       if (act_char==']') return (L_RBRACKET);
       if (act_char=='{') return (L_LSETBRACKET);
       if (act_char=='}') return (L_RSETBRACKET);
       if (act_char==';') return (L_SEMICOLON);
       //carka
       if (act_char==',') return (L_COMMA);
       
       //=> nebo =
       if (act_char=='=') {state=9; break;}

	   //whitespace
       if ( act_char == ' ' || act_char == '\n' || act_char == '\t' )
       {
         if (act_char == '\n') {line++;}
         break;
       }
       
       //desitkova cislice
       if ( isdigit(act_char) )
       {
       		L_Value = act_char - '0';
       		state=10;
       		break;
       }
		
	   //logicky vektor: napr "bbbbb"
       if ( act_char=='"') {state=5; break;}
       
       //logicka hodnota 'b'
       if (act_char==39) {state=7; break;}

       if ( isalpha(act_char) || (act_char=='_') ) /*identifikator*/
       {
         state=1;
         lex_str=lex_str+char(act_char);
         break;
       }

       if (act_char=='<') { state=2; break;} /*prirazeni*/
       if (act_char=='-') { state=3; break;} /*komentar*/

       if ( act_char == EOF ) return ( L_END );

       error(0);
       return ( L_ERR );
     }

	 //identifikator: napr. a_11	
     case 1:
     {
       if ( isalnum(act_char) || (act_char=='_') )
       {
         if (lex_str.length()<=MAXSTRLENGTH)
         {lex_str=lex_str+(char)act_char;}
         break;
       }

       in_file->unget();
       int keywcode=iskeyword(lex_str);
       if (lex_str.length()<=MAXSTRLENGTH)
       {
         if (keywcode!=-1) return (keywcode);
         return (L_IDENT);
       }
       error(1);
       return (L_ERR);
     }
	 
	 //prirazeni: <=
     case 2:
     {
       if (act_char=='=') return (L_ASSIGN);
       in_file->unget();
       return (L_ERR);
     }

	 //komentar: --
     case 3:
     {
       if (act_char=='-') {state=4; break;}
       in_file->unget();
       return (L_ERR);
     }
	
	 //telo komentare
     case 4:
     { 
       if (act_char=='\n') {line++; state=0; break;}
       if (act_char== EOF) return (L_END);
       break;
     }
	 
	 //logicky vektor: napr. "bbbbb"
	 case 5:
	 {
	   if (act_char=='0' || act_char=='1') 
	   {
	   	 L_Value=act_char - '0';
	   	 L_VLength=0;
	   	 L_VLength++;
	   	 state=6; 
	   	 break;
	   }
	   
	   return (L_ERR);
	 }
	 /*logicky vektor*/
	 case 6:
	 {
	   if (act_char=='0' || act_char=='1')
	   {
	   	 L_Value = (L_Value<<1) + act_char - '0';
	   	 L_VLength++;
	   	 break;
	   }
	   
	   if (act_char=='"') {return (L_LVECTOR);} 
	   return (L_ERR);
	 }
	 
	 //logicka hodnota 'b'
	 case 7:
	 {
	   if (act_char=='0' || act_char=='1')
	   {
	   	 L_Value=act_char - '0';
	   	 state=8;
	   	 break;
	   }
	 }
	 
	 /*logicka hodnota*/
	 case 8:
	 {
	   if (act_char==39) {return (L_LVALUE);} 
	   return (L_ERR);
	 }
	 
	 // => nebo =
	 case 9:
	 {
	   if (act_char=='>') return (L_OPASSIGN);
	   in_file->unget();
	   return (L_EQUATE);
	 
	 }
	 
	 case 10:
	 {
	 	if (isdigit (act_char))
	    {
	   	 L_Value = (L_Value*10) + act_char - '0';
	   	 break;
	    }
	   
	    in_file->unget();
	    return L_NUMBER;
	    
	 }

     default:
     {
       assert ( "Invalid state" == NULL );
     }
   }



 }

}

/*===========================================================================*/
/*syntakticka analyza - pravidla pro budovani stromu abstraktni syntaxe*/

Node * S();
Node * P();
Node * V();
Node * VC(Node * op);
Node * T();
void D();
void DC();

int Token;

//1. S-> P;
//2. S-> variable D;
Node * S()
{
	Node * parent;
	switch (Token)
	{
		case L_VAR:
		{
			Token = gettoken();
			D();
			if ( Token != L_SEMICOLON )  {error(2); return (NULL);}
			Token = gettoken(); 
		}
		
		default: parent = P();	
	}
	
	if ( Token != L_SEMICOLON )  {error(2); return (NULL);} 
	return parent;
}


//3. P-> id<=V
Node * P()
{
	//Node * parent;
	
	switch (Token)
	{
		case L_IDENT:
		{
			l_name = lex_str;
			Token=gettoken();
			if (Token==L_ASSIGN)
			{
				Token=gettoken();
				//parent = new BNode ( new IdentNode(l_name),V(),S_ASSIGN );
				return V();
				
			}
			error(2);
			return NULL;
		}
		default: {error(2); return NULL;}
	}
}

//4. V-> T V'
Node * V()
{
	Node * parent;
	parent = VC(T());
	return parent;

}

//5. V'-> bop T V'
//6. V'-> eps
Node * VC(Node * op)
{
	Node * parent;
	
	switch (Token)
	{

		case L_AND:
		{
			Token=gettoken();
			parent = VC( new BNode(op,T(),S_AND) );
			return parent;
		}
		
		case L_OR:
		{
			Token=gettoken();
			parent = VC( new BNode(op,T(),S_OR) );
			return parent;
		}
		
		case L_NAND:
		{
			Token=gettoken();
			parent = VC( new BNode(op,T(),S_NAND) );
			return parent;
		}
		
		case L_NOR:
		{
			Token=gettoken();
			parent = VC( new BNode(op,T(),S_NOR) );
			return parent;
		}
		
		case L_XOR:
		{
			Token=gettoken();
			parent = VC( new BNode(op,T(),S_XOR) );
			return parent;
		}
		
		case L_XNOR:
		{
			Token=gettoken();
			parent = VC( new BNode(op,T(),S_XNOR) );
			return parent;
		}
		
        //6. V'-> eps
		default: return (op);
	}
}

//7. T-> uop T
//8. T-> (V)
//9. T-> val
Node * T()
{
	Node * parent;
	
	switch (Token)
	{
		case L_NOT:
		{
			Token=gettoken();
			parent=new UNode( T(),S_NOT );
			return parent;
		}
		
		case L_LPARENT:
		{
			Token=gettoken();
			parent = V();
			
			if (Token==L_RPARENT)
			{
				Token=gettoken();
				return parent; 
			}
			
			error(2);
			return (NULL);
			
		}
		
		case L_IDENT:
		{
			Element * elm;
			bool matches = false;
			
			for (int i = 0; i < treeArray->outvars; i++)
			{
				elm = treeArray->find(i);
				if ((elm != NULL) && (elm->root != NULL))
				{
					if (elm->name == lex_str)
					{
						parent = elm->root->copy();
						matches = true;
					}
				}
			}
			
			if (!matches)
			{
				parent = new IdentNode(lex_str);
				symbTable->add(lex_str);
			}
			Token = gettoken();
			return parent;
		}
		
		case L_NUMBER:
		{
	   		Element * elm;
	   		elm = treeArray->find(L_Value);
	   		if ((elm == NULL) || (elm->root == NULL))
	   		{
	   			parent = new VectNode(0,1);
	   			warning(4);
	   		}
	   		else
	   		{
	   			parent = elm->root->copy();
	   		}
			Token=gettoken();
			return parent;
		}
		
		case L_LVECTOR:
		{
			parent = new VectNode(L_Value,L_VLength);
			Token=gettoken();
			return parent;
		}
		
		default: {error(2); return NULL;}
	}

}

//10. D-> id D'
void D()
{
	switch (Token)
	{
		case L_IDENT:
		{
			treeArray->symbNames->add(lex_str);
			treeArray->invars++;
			Token = gettoken();
			DC();
			return;
		}
		
		default: {error(2); return;}
	}
}

//11. D'-> , id D'
//12. D'-> eps
void DC()
{
	switch (Token)
	{
		case L_COMMA:
		{
			Token = gettoken();
			if (Token == L_IDENT)
			{
				treeArray->symbNames->add(lex_str);
				treeArray->invars++;
				Token = gettoken();
				DC();
			}
			else {error(2); return;}
		}
		
		default: {return;}
	}
}


//syntakticka analyza - vytvoreni stromu abstraktni syntaxe
Node * analyze (istream * input)
{
	in_file = input;
	root = NULL;
	
	symbTable = new STable(); 
	Token = gettoken();
	if (Token != L_END) root = S();

	if ( (Token==L_SEMICOLON) && (!err) )
	{
		if ( !root->checkLength(L_VLength) ) {warning(3);}
		return root;
	}
	
	delete symbTable;
	if (root != NULL) delete root;
	//kvuli pridani formuli
	line = 1;
	return (NULL);
}
