///////////////////////////////////////////////////////////////////////////////
//                                                         
// LR1Analyser.cc
// --------------
// dragon LR1 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: LR1Analyser
// 
// Description: access to dragon termianal information 
//
///////////////////////////////////////////////////////////////////////////////

#include "LR1Analyser.h"

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

LR1Analyser::~LR1Analyser()
{
}

int LR1Analyser::analyse(SetT<LR1Hash>& LR1HashSet, SetT<LR1Trans>& LR1TransSet)
{
    
    cout << "Starting LR1 analysis ..." << endl;
    // create symbol set
    
    SetT<Chain> symbolSet;
    getSymbolSet(symbolSet);
 
    LR1Element startElement;
    getStartElement(startElement);
    
    SetT<LR1Element> startClosure;
    
    getClosure(startClosure, startElement);

    int nextId = 0;
    int setId = nextId;
    LR1Hash h(startClosure, setId);
    LR1HashSet.Insert(h);
    bool collapsed = false;

    cout << "Reached LR(1) elements: " << endl;
    while ( ! collapsed ) 
    {

	cout << LR1HashSet.Size() << "...";
	cout.flush();

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

    cout << endl;

    return nextId;
    
}

void LR1Analyser::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 LR1Analyser::getStartElement(LR1Element& e)
{   
    Production* pP = _pProductionSet->First();

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

void LR1Analyser::getClosure(SetT<LR1Element>& t, const LR1Element& e)
{   
        
    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 LR1Analyser::getJump(SetT<LR1Element>& t, SetT<LR1Element>& s, const Chain& symbol)
{    
        
    bool isJump = false;

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

	if (pE->getSymbolAtPos(symAtPos))
	{
	    if ((Chain)symbol == (Chain)symAtPos)
	    {
		LR1Element e = pE->jumpOver();
		
		ClosureCache* pCC;
		if ( (pCC = _closureCacheSet.Find(ClosureCache(e))) != 0 )
		{
		    t += pCC->getClosure();
		}
		else
		{	         
		    SetT<LR1Element> es;
		    getClosure(es, e);
		    _closureCacheSet.Insert(ClosureCache(es, e));
		    t += es;
		}
		isJump=true;
	    }
	}
	pE = s.Next();
    }
    
    return isJump;
}









