/*
 * DeterministicLL1Grammar.cpp
 *
 *  Created on: 9. 6. 2015
 *	  Author: Jan Travnicek
 */

#include "DeterministicLL1Grammar.h"
#include "LL1ParseTable.h"
#include "HandleFirstFirstConflict.h"
#include "HandleFirstFollowConflict.h"

#include <grammar/ContextFree/CFG.h>
#include <exception/CommonException.h>
#include <alib/algorithm>

#include <grammar/properties/RecursiveNonterminal.h>
#include <registration/AlgoRegistration.hpp>

namespace grammar {

namespace parsing {

grammar::CFG < > DeterministicLL1Grammar::convert ( const grammar::CFG < > & param ) {

	if ( std::any_of ( param.getNonterminalAlphabet ( ).begin ( ), param.getNonterminalAlphabet ( ).end ( ), [&] ( const DefaultSymbolType & nonterminal ) {
				return grammar::properties::RecursiveNonterminal::isNonterminalRecursive ( param, nonterminal );
			} ) )
		throw exception::CommonException ( "Grammar contains left recursion" );

	grammar::CFG < > grammar = param;

	while ( true ) {
		ext::map < ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType >, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > parseTable = LL1ParseTable::parseTable ( grammar );

		bool deterministic = true;

		for ( const std::pair < const ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType >, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > & elem : parseTable )
			if ( elem.second.size ( ) > 1 )
				if ( elem.first.first.empty ( ) )
					throw exception::CommonException ( "Cant handle conflict in epsilon" );

		for ( const std::pair < const ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType >, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > & elem : parseTable ) {
			if ( elem.first.first.empty ( ) ) continue;

			const DefaultSymbolType & terminal = elem.first.first.front ( );
			const DefaultSymbolType & nonterminal = elem.first.second;
			const ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > & rhsds = elem.second;

			if ( ( elem.second.size ( ) > 1 ) && std::all_of ( rhsds.begin ( ), rhsds.end ( ), [] ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rhs ) {
						return !rhs.empty ( );
					} ) ) {
				HandleFirstFirstConflict::handleFirstFirstConflict ( grammar, terminal, nonterminal, rhsds );
				deterministic = false;
				break;
			}
		}

		if ( !deterministic ) continue;

		for ( const std::pair < const ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType >, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > & elem : parseTable ) {
			if ( elem.first.first.empty ( ) ) continue;

			const DefaultSymbolType & terminal = elem.first.first.front ( );
			const DefaultSymbolType & nonterminal = elem.first.second;
			const ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > & rhsds = elem.second;

			if ( ( elem.second.size ( ) > 1 ) && std::any_of ( rhsds.begin ( ), rhsds.end ( ), [] ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rhs ) {
						return rhs.empty ( );
					} ) ) {
				HandleFirstFollowConflict::handleFirstFollowConflict ( grammar, terminal, nonterminal, rhsds );
				deterministic = false;
				break;
			}
		}

		if ( !deterministic ) continue;

		return grammar;
	}
}

auto DeterministicLL1GrammarCFG = registration::AbstractRegister < DeterministicLL1Grammar, grammar::CFG < >, const grammar::CFG < > & > ( DeterministicLL1Grammar::convert );

} /* namespace parsing */

} /* namespace grammar */
