///////////////////////////////////////////////////////////////////////////////
//                                                         
// LALRAnalyser.cc
// ---------------
// Dragon LALR parse table analyser implementation
// 
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2007 by Bjoern Lemke
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// IMPLEMENTATION MODULE
//
// Class: LALRAnalyser
// 
// Description:  
//
///////////////////////////////////////////////////////////////////////////////

#include "LALRAnalyser.h"

LALRAnalyser::LALRAnalyser(SetT<Terminal>* pTerminalSet, SetT<Production>* pProductionSet,
	    SetT<FirstHash>* pFirstHashSet)
{
    _pTerminalSet = pTerminalSet;
    _pProductionSet = pProductionSet;
    _pFirstHashSet = pFirstHashSet;
    _rot =0;
}

LALRAnalyser::~LALRAnalyser()
{
}

int LALRAnalyser::analyse(SetT<LR1Hash>& LR1HashSet, SetT<LR1Trans>& LR1TransSet)
{
    
    cout << "Starting LALR analysis ..." << endl;

    hashProdTrans();
    
    SetT<Chain> symbolSet;
    getSymbolSet(symbolSet);

    LR0Element startElement;
    getStartElement(startElement);
    
    SetT<LR0Element> startClosure;
       
    int nextId = 0;
    int setId = nextId;
    startClosure.Insert(startElement);
    LR0Hash h(startClosure, setId);
    
    _LR0HashSet.Insert(h);
    bool collapsed = false;

    // 1. calculate lr0 core set

    cout << "Calculating LR(0) elements ... " << endl;
    while ( ! collapsed ) 
    {
	// cout << _LR0HashSet.Size() << "...";
	// cout.flush();

	SetT<LR0Hash> tmpSet;
	LR0Hash* pH = _LR0HashSet.First();
	while (pH)
	{

	    Chain* pSym = symbolSet.First();	    
	    while (pSym) 
	    {
		SetT<LR0Element> c2;
		
		if ( getCoreJump(c2, pH->getSet(), *pSym) )
		{
		    LR0Hash h2(c2);		 
		    LR0Hash *pH2 = _LR0HashSet.Find(h2);
		    if (pH2)
		    {
			setId = pH2->getId();			
		    }
		    else
		    {
			LR0Hash *pH3 = tmpSet.Find(h2);
			if (pH3)
			{
			    setId = pH3->getId();
			}
			else
			{
			    
			    nextId++;
			    setId = nextId;
			    LR0Hash h(c2,setId); 
			    if ( tmpSet.Insert(h) == false )
			    {
				cout << "FATAL ERROR !!! " << endl;
			    }
			}
		    }
		    
		    LR1Trans t(pH->getId(),setId, *pSym);
		    LR1TransSet.Insert(t);
		    
		    rotate(Chain(_LR0HashSet.Size()));
		}
		pSym = symbolSet.Next();
	    }
	    pH=_LR0HashSet.Next();
	}
	
	int actSize = _LR0HashSet.Size();
	_LR0HashSet += tmpSet;

	if ( _LR0HashSet.Size() == actSize )
	{
	    collapsed = true;
	}
	tmpSet.Empty();
    }

    
    // FOR TEST
    
    /*
    LR0Hash* pHx = _LR0HashSet.First();
    while ( pHx )
    {
	cout << endl;
	cout << "Set " << pHx->getId() << endl;
	cout << "----------------------" << endl;
	LR0Element *pLR0 = pHx->getSet().First();
	while( pLR0 )
	{
	    cout << pLR0->asChain() << endl;
	    pLR0 = pHx->getSet().Next();
	}
	pHx = _LR0HashSet.Next();
    }

    
    LR1Trans* pXTrans = _LR1TransSet.First();
    while ( pXTrans )
    {
	cout << pXTrans->getSid() << " "<< pXTrans->getTid() << " " << pXTrans->getToken() << endl;
	pXTrans = _LR1TransSet.Next();
    }
    */
    // END OF TEST

    // 2. prepare LALRHashSet

    LR0Hash* pH = _LR0HashSet.First();
    while ( pH )
    {
	SetT<LR1Element> initSet;
	if ( pH->getId() == 0 )
	    initSet.Insert(LR1Element(startElement.getProd(), startElement.getPos(), "$"));
	LALRHash lr1h( initSet, pH->getId() );
	_LALRHashSet.Insert(lr1h); 
	pH = _LR0HashSet.Next();
    }
    
    // 3. calculate spontaneous and spread lookahead

    cout << "Lookahead calculation ..." << endl;

    bool growing = true;

    int i = 0;
    while (  growing == true ) 
    {
	growing = false;

	i++;
	Chain* pSym = symbolSet.First();
	while (pSym) 
	{

	    rotate(Chain(i));
		    
	    LR0Hash* pH = _LR0HashSet.First();
	    while ( pH )
	    {
		LR0Element *pLR0 = pH->getSet().First();
		while( pLR0 )
		{	
		    SetT<LR1Element> c2;
		    LR1Element e(pLR0->getProd(), pLR0->getPos(), Chain("#"));	    

		    ClosureCache* pCC;
		    if ( ( pCC = _closureCacheSet.Find(ClosureCache(e))) != 0 )
		    {
			c2 = pCC->getClosure();
		    }
		    else
		    {	         
			getClosure(c2, e);
			_closureCacheSet.Insert(ClosureCache(c2, e));
		    }
		    
		    LR1Element *pE = c2.First();
		    while ( pE )
		    {
			
			Chain s;
			if ( pE->getSymbolAtPos(s))
			{
			    if ( s == *pSym )
			    {
				if ( pE->getFirst() != Chain("#") ) 
				{
				    LR1Element e2 = pE->jumpOver();
				    e2.setFirst(pE->getFirst());

				    if ( jumpAndAdd(LR1TransSet, pH->getId(), *pSym, e2) )
				    {
					growing = true;
				    }
				}
				else // ( pE->getFirst() == Chain("#") )
				{
				    
				    SetT<Chain> lhSet;
				   
				    getLookAhead(pH->getId(), e, lhSet);
			   
				    Chain *pLH = lhSet.First();
				    LR1Element e2 = pE->jumpOver();
				    
				    while ( pLH )
				    {
					e2.setFirst(*pLH);
					if (  jumpAndAdd(LR1TransSet, pH->getId(), *pSym, e2) )
					{
					    growing = true;
					}
					pLH = lhSet.Next();
				    } 
				} 
			    }			
			}
			pE = c2.Next();
		    }
		    pLR0 = pH->getSet().Next();
		}
		
		pH = _LR0HashSet.Next();
	    }
	    pSym = symbolSet.Next();
	}
	
    }
    
    // 4. create closure of LR(0) core an propagate to LR1Hash

    createLALRClosure(LR1HashSet);    

    /*
    LR1Hash *pH1 = _LR1HashSet.First();
    while ( pH1 )
    {
	cout << endl;
	cout << "Set = " <<  pH1->getId() << endl;
	cout << "-------------------------" << endl;
	LR1Element *pLR1 =  pH1->getSet().First();
	while ( pLR1 )
	{
	    cout << pLR1->asChain() << endl;
	    pLR1 =  pH1->getSet().Next();
	}
	pH1 = _LR1HashSet.Next();
    }
    LR1Trans *pTrans = _LR1TransSet.First();
    while ( pTrans )
    {
	cout << pTrans->getSid() << " " << pTrans->getTid() << " " << pTrans->getToken() << endl;
	pTrans = _LR1TransSet.Next();
    }
    */

    return nextId;
}

void LALRAnalyser::hashProdTrans()
{

    // calculate prod transitions
    
    SetT<Production> privateProdSet = *_pProductionSet;
    Production* pP = privateProdSet.First();
    while (pP)
    {	
	SetT<Chain> transSet;
	getSubTrans(pP->getName(), transSet);
	
	TransHash th(pP->getName());
	th.setTransSet( transSet);
	_transHashSet.Insert(th);

	pP = privateProdSet.Next();
    }
}

void LALRAnalyser::getSubTrans(const Chain& prodName, SetT<Chain>& transSet)
{

    Chain s;
    SetT<Production> privateProdSet = *_pProductionSet;
    
    Chain subProd;
    Production* pP = privateProdSet.First();
    while ( pP )
    {
	if ( pP->getName() == prodName && pP->getSymbolAtPos(subProd, 0) )
	{
	    if ( pP->isTermAtPos(0) == false && subProd != prodName)
	    {
		if ( transSet.Insert(subProd) )
		    getSubTrans(subProd, transSet);
	    }
	}
	pP = privateProdSet.Next();
    }
    
}

void LALRAnalyser::getSymbolSet(SetT<Chain>& symbolSet)
{

    // insert terminals
    
    Terminal *pT = _pTerminalSet->First();
    
    while (pT)
    {
	symbolSet.Insert(pT->getName());
	pT = _pTerminalSet->Next();
    }

    Production *pP = _pProductionSet->First();

    while(pP)
    {
	symbolSet.Insert(pP->getName());
	pP = _pProductionSet->Next();
    }
}

void LALRAnalyser::getStartElement(LR0Element& e)
{   
    Production* pP = _pProductionSet->First();

    while (pP && ( pP->getId() != 0 ) )
    {
	pP = _pProductionSet->Next();
    }
    if (pP)
    {
	e = LR0Element(pP, 0);
	return;
    }
    throw Exception(EXLOC, "Start production not found.");
}

void LALRAnalyser::getClosure(SetT<LR1Element>& t, const LR1Element& e)
{   
    
    // cout << "Getting Closure of Element " << e.asChain() << endl;
    
    t.Insert(e);
    
    bool collapsed = false;
    
    while (! collapsed)
    {
	collapsed = true;
	
	LR1Element* pE = t.First();
	
	while (pE)
	{
	    
	    if ( pE->isProdAtPos() )
	    {
		Chain symbol;
		if ( pE->getSymbolAtPos(symbol) )
		{
		    Production* pP = _pProductionSet->First();
		    while (pP)
		    {
			if ( (Chain)pP->getName() == symbol )
			{			    
			    // cout << "Treating Production " << pP->asChain() << endl;
			    Chain s;
			    if ( pE->getFollowUpSymbol(s) )
			    {
			      
				// first check if we have symbol
				// in first set

				FirstHash *pFH = _pFirstHashSet->Find(s);
				if (pFH)
				{
				    Chain* pS = pFH->getFirstSet().First();
				    while (pS)
				    {
					LR1Element e2(pP, 0, *pS);
					if ( t.Insert(e2) )
					{
					    // cout << "Inserting element " << e2.asChain() << endl;
					    collapsed = false;
					}
					pS = pFH->getFirstSet().Next();
				    }	    
				}
				else
				{				  
				    // check if symbol is a terminal	    
				    if (_pTerminalSet->Find(Terminal(s)))
				    {				     
					LR1Element e2(pP, 0, s);
					if ( t.Insert(e2) )
					{
					    // cout << "Inserting element " << e2.asChain() << endl;
					    collapsed = false;
					}
				    }				    
				}
			    }
			    else
			    {
				LR1Element e2(pP, 0, pE->getFirst());
				if ( t.Insert(e2) )
				{
				    // cout << "Inserting element " << e2.asChain() << endl;
				    collapsed = false;
				}
			    }
			}
			pP = _pProductionSet->Next();
		    }
		}
	    }
	    pE = t.Next();
	}
    }
}

bool LALRAnalyser::jumpAndAdd(const SetT<LR1Trans>& LR1TransSet, int id, const Chain& symbol, const LR1Element& e)
{
	
    LR1Trans *pTrans = LR1TransSet.First();
    while ( pTrans )
    {
	if ( pTrans->getSid() == id && pTrans->getToken() == symbol )
	{
	    LALRHash *pH = _LALRHashSet.First();
	    while ( pH )
	    {
		if ( pH->getId() == pTrans->getTid() )
		{			
		    return  pH->getSet().Insert( e );
		}
		pH = _LALRHashSet.Next();
	    }
	}
	pTrans = LR1TransSet.Next();
    }
    return false;
}

void LALRAnalyser::getLookAhead(int id, const LR1Element& e, SetT<Chain>& lhset)
{
    LALRHash *pH = _LALRHashSet.First();
    while ( pH )
    {
	if ( pH->getId() == id )
	{
	    LR1Element *pLR1 =  pH->getSet().First();
	    while ( pLR1 )
	    {
		if ( pLR1->getProdId() == e.getProdId() && pLR1->getPos() == e.getPos() ) 
		    lhset.Insert( pLR1->getFirst() );
		pLR1 =  pH->getSet().Next();
	    }
	    
	}
	pH = _LALRHashSet.Next();
    }
    
}

void LALRAnalyser::createLALRClosure(SetT<LR1Hash>& LR1HashSet)
{

    cout << "Closure creation ..." << endl;
    
    int i = 0;

    LALRHash *pH = _LALRHashSet.First();
    while ( pH )
    {
	i++;
	
	SetT<LR1Element> closure = pH->getSet();
	LR1Element *pLR1 =  pH->getSet().First();
	while ( pLR1 )
	{
	    rotate(Chain(i));

	    SetT<LR1Element> es;
	    getClosure(es, *pLR1);
	    closure += es;
	    
	    pLR1 =  pH->getSet().Next();
	}
	
	LR1HashSet.Insert ( LR1Hash ( closure, pH->getId()));
	
	pH = _LALRHashSet.Next();
    }
}


bool LALRAnalyser::getCoreJump(SetT<LR0Element>& t, SetT<LR0Element>& s, const Chain& symbol)
{    
        
    bool isJump = false;

    LR0Element* pE = s.First();
    while (pE)
    {
	Chain symAtPos;

	if (pE->getSymbolAtPos(symAtPos))
	{
	    if ((Chain)symbol == (Chain)symAtPos)
	    {
		LR0Element e = pE->jumpOver();
		
		t.Insert(e);

		isJump=true;

		Chain epsCheck;
		bool moreEps=true;

		while ( e.getSymbolAtPos(epsCheck) && moreEps )
		{
		    if ( isEpsilonProd(epsCheck) )
		    {
			e = e.jumpOver();
			t.Insert(e);
		    }
		    else
		    {
			moreEps=false;
		    }
		}
	    }


       
	    Production *pProd =  _pProductionSet->First();
	    while ( pProd )
	    {
		Chain s;
		if ( pProd->getSymbolAtPos(s, 0) )
		{
		    if ( pProd->getName() == symAtPos && s == symbol )
		    {
			LR0Element e(pProd, 1);
			t.Insert(e);
			isJump=true;
		    }
		}
		pProd =  _pProductionSet->Next();
	    }
	    
	   
	    TransHash *pTH = _transHashSet.Find( TransHash( symAtPos));
	    
	    if ( pTH )
	    {
		Chain *pP = pTH->getTransSet().First();
		while ( pP )
		{
		    Production *pProd =  _pProductionSet->First();
		    while ( pProd )
		    {
			Chain s;
			if ( pProd->getSymbolAtPos(s, 0) )
			{
			    if ( pProd->getName() == *pP && s == symbol )
			    {
				LR0Element e(pProd, 1);
				t.Insert(e);
				isJump=true;
			    }
			}
			pProd =  _pProductionSet->Next();
		    }
		    pP = pTH->getTransSet().Next();
		}
	    }	    	    
	}
	pE = s.Next();
    }
    
    return isJump;
}

bool LALRAnalyser::isEpsilonProd(const Chain& prod)
{
    
    Production *pProd =  _pProductionSet->First();
    while ( pProd )
    {
	Chain s;
	if ( pProd->getName() == prod && pProd->getSymbolAtPos(s, 0) == false )
	{
	    return true;
	}
	pProd =  _pProductionSet->Next();
    }
    return false;
}


void LALRAnalyser::rotate(const Chain& msg)
{
    
    cout << "# " << msg << " ";
    if ( _rot % 8 == 0)
	cout << "|\r";
    else if ( _rot % 8 == 1)    
	cout << "/\r";
    else if ( _rot % 8 == 2)    
	cout << "-\r";
    else if ( _rot % 8 == 3)    
	cout << "\\\r";
    else if ( _rot % 8 == 4)    
	cout << "|\r";
    else if ( _rot % 8 == 5)    
	cout << "/\r";
    else if ( _rot % 8 == 6)    
	cout << "-\r";
    else if ( _rot % 8 == 7)    
	cout << "\\\r";
    cout.flush();
    _rot++;

}


