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

#include "AbsorbTerminalSymbol.h"

#include <grammar/ContextFree/CFG.h>
#include <common/createUnique.hpp>

namespace grammar {

namespace parsing {

void AbsorbTerminalSymbol::handleAbsobtion ( const grammar::CFG < > & orig, grammar::CFG < > & res, const DefaultSymbolType & terminal, const ext::set < DefaultSymbolType > & nonterminals, const ext::map < DefaultSymbolType, DefaultSymbolType > & nonterminalsPrimed ) {
	for ( const DefaultSymbolType & nonterminal : nonterminals )
		for ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rhs : orig.getRules ( ).find ( nonterminal )->second ) {
			ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > newRHS;

			for ( ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > >::const_iterator iter = rhs.begin ( ); iter != rhs.end ( ); ++iter ) {
				if ( nonterminals.contains ( * iter ) && ( ( iter + 1 == rhs.end ( ) ) || ( terminal == * ( iter + 1 ) ) ) ) {
					newRHS.push_back ( nonterminalsPrimed.find ( * iter )->second );

					if ( iter + 1 != rhs.end ( ) ) ++iter;
				} else if ( iter + 1 == rhs.end ( ) ) {
					newRHS.push_back ( * iter );
					newRHS.push_back ( terminal );
				} else {
					newRHS.push_back ( * iter );
				}
			}

			if ( rhs.empty ( ) )
				newRHS.push_back ( terminal );

			res.addRule ( nonterminalsPrimed.find ( nonterminal )->second, newRHS );
		}

}

void AbsorbTerminalSymbol::absorbTerminalSymbol ( grammar::CFG < > & grammar, const DefaultSymbolType & terminal, const ext::set < DefaultSymbolType > & nonterminals ) {
	grammar::CFG < > res ( grammar.getInitialSymbol ( ) );

	res.setNonterminalAlphabet ( grammar.getNonterminalAlphabet ( ) );
	res.setTerminalAlphabet ( grammar.getTerminalAlphabet ( ) );

	ext::map < DefaultSymbolType, DefaultSymbolType > nonterminalsPrimed; // terminal is fixed in particular calls

	for ( const DefaultSymbolType & nonterminal : nonterminals ) {
		DefaultSymbolType newSymbol = common::createUnique ( DefaultSymbolType ( object::AnyObject < ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( nonterminal, terminal ) ) ), res.getTerminalAlphabet ( ), res.getNonterminalAlphabet ( ) );
		res.addNonterminalSymbol ( newSymbol );
		nonterminalsPrimed.insert ( std::make_pair ( nonterminal, newSymbol ) );
	}

	handleAbsobtion ( grammar, res, terminal, nonterminals, nonterminalsPrimed );

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

		for ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rhs : rule.second ) {
			ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > newRHS;

			for ( ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > >::const_iterator iter = rhs.begin ( ); iter != rhs.end ( ); ++iter ) {
				if ( ( iter + 1 != rhs.end ( ) ) && nonterminals.contains ( * iter ) && ( terminal == * ( iter + 1 ) ) ) {
					newRHS.push_back ( nonterminalsPrimed.find ( * iter )->second );
					++iter;
				} else {
					newRHS.push_back ( * iter );
				}
			}

			res.addRule ( lhs, newRHS );
		}
	}

	grammar = res;
}

} /* namespace parsing */

} /* namespace grammar */
