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

#include "HandleFirstFollowConflict.h"
#include "First.h"
#include "Follow.h"
#include <grammar/properties/NullableNonterminals.h>
#include "AbsorbTerminalSymbol.h"
#include "ExtractRightContext.h"

#include <grammar/ContextFree/CFG.h>

namespace grammar {

namespace parsing {

void HandleFirstFollowConflict::handleFirstFollowConflict ( grammar::CFG < > & grammar, const DefaultSymbolType & terminal, const DefaultSymbolType & nonterminal, const ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > & /* rhsds */ ) {
	ext::set < DefaultSymbolType > nullableNonterminals = properties::NullableNonterminals::getNullableNonterminals ( grammar );

	ext::set < DefaultSymbolType > symbolsEndingWithNonterminal = { nonterminal };

	 // find all nonterminals X of the form X -> \alpha N \beta where \alpha is (NuT)*, N is in symbolsEndingWithNonterminal and \beta is nullable
	 // A -> aC | \eps; B -> \eps; X -> AB; Y -> Xa
	 // conflict is in A but algorithm needs to find a set {A, X} since the resolution is in creation [Xa] -> ABa | a
	while ( true ) {
		ext::set < DefaultSymbolType > symbolsEndingWithNonterminalOld = symbolsEndingWithNonterminal;

		for ( const std::pair < const DefaultSymbolType, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > & rule : grammar.getRules ( ) ) {
			const DefaultSymbolType & lhs = rule.first;

			if ( Follow::follow ( grammar, lhs ).contains ( ext::vector < DefaultSymbolType > { terminal } ) )
				for ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rhs : rule.second )
					for ( unsigned i = rhs.size ( ); i > 0; i-- ) {
						if ( symbolsEndingWithNonterminal.contains ( rhs[i - 1] ) )
							symbolsEndingWithNonterminal.insert ( lhs );

						if ( grammar.getTerminalAlphabet ( ).contains ( rhs[i - 1] ) || !nullableNonterminals.contains ( rhs[i - 1] ) ) break;
					}

		}

		if ( symbolsEndingWithNonterminalOld == symbolsEndingWithNonterminal ) break;
	}

	 // find whether all occurrences of a symbol in symbolsEndingWithNonterminal are followed by terminal symbol (the paremeter)
	for ( const std::pair < const DefaultSymbolType, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > & rule : grammar.getRules ( ) )
		for ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rhs : rule.second ) {
			if ( !rhs.empty ( ) )
				for ( ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > >::const_iterator iter = rhs.begin ( ); iter + 1 != rhs.end ( ); ++iter )
					if ( symbolsEndingWithNonterminal.contains ( * iter ) && grammar.getNonterminalAlphabet ( ).contains ( * ( iter + 1 ) ) && First::first ( grammar, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > ( iter + 1, rhs.end ( ) ) ).contains ( ext::vector < DefaultSymbolType > { terminal } ) ) {
						ExtractRightContext::extractRightContext ( grammar, terminal, symbolsEndingWithNonterminal );
						return;
					}

		}

	AbsorbTerminalSymbol::absorbTerminalSymbol ( grammar, terminal, symbolsEndingWithNonterminal );
}

} /* namespace parsing */

} /* namespace grammar */
