/*
 * LR0Parser.cpp
 *
 *  Created on: 2. 5. 2016
 *	  Author: Martin Kocicka
 */

#include "LR0Parser.h"

#include "LRParser.h"

#include <queue>

namespace grammar {

namespace parsing {

LR0Items LR0Parser::getClosure ( LR0Items items, const grammar::CFG < > & originalGrammar ) {
	bool changed;
	do {
		changed = false;

		for ( const LR0Items::value_type & symbolItems : items ) {
			for ( const std::pair < unsigned, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > & item : symbolItems.second ) {
				unsigned position = item.first;
				ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rightHandSide = item.second;

				if ( position == rightHandSide.size ( ) )
					continue;

				ext::map < DefaultSymbolType, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > >::const_iterator rulesIterator = originalGrammar.getRules ( ) . find(rightHandSide[position]);
				if ( rulesIterator == originalGrammar.getRules ( ) . end ( ) )
					continue;

				for ( const ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > & rule : rulesIterator->second ) {
					if (items[rightHandSide[position].get < DefaultSymbolType > ( ) ].find ( { 0, rule } ) == items[rightHandSide[position].get < DefaultSymbolType > ( ) ].end ( ) ) {
						changed = true;
						items[rightHandSide[position].get < DefaultSymbolType > ( ) ].insert ( { 0, rule } );
					}
				}
			}
		}
	} while ( changed );

	return items;
}

LR0Items LR0Parser::getNextStateItems ( const LR0Items & items, const ext::variant < DefaultSymbolType, DefaultSymbolType > & symbol, const grammar::CFG < > & originalGrammar ) {
	LR0Items transitionItems;
	for ( const LR0Items::value_type & symbolItems : items ) {
		DefaultSymbolType leftHandSide = symbolItems.first;
		for ( const std::pair < unsigned, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > & item : symbolItems.second) {
			unsigned position = item.first;
			ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rightHandSide = item.second;

			if ( position == rightHandSide.size ( ) ) {
				continue;
			}

			if ( rightHandSide[position] == symbol ) {
				transitionItems[leftHandSide].insert ( { position + 1, rightHandSide } );
			}
		}
	}

	return getClosure ( transitionItems, originalGrammar );
}

automaton::DFA < ext::variant < DefaultSymbolType, DefaultSymbolType >, LR0Items > LR0Parser::getAutomaton ( const grammar::CFG < > & originalGrammar ) {
	grammar::CFG < > augmentedGrammar = LRParser::getAugmentedGrammar ( originalGrammar );
	DefaultSymbolType initialSymbol = augmentedGrammar.getInitialSymbol ( );
	const ext::map < DefaultSymbolType, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > & rules = augmentedGrammar.getRules ( );

	LR0Items initialItems;
	initialItems[initialSymbol].insert ( { 0, *rules.find ( initialSymbol ) -> second.begin ( ) } );

	LR0Items initialState = getClosure ( initialItems, augmentedGrammar );

	automaton::DFA < ext::variant < DefaultSymbolType, DefaultSymbolType >, LR0Items > lr0Automaton ( initialState );

	for ( const DefaultSymbolType & nonterminal : augmentedGrammar.getNonterminalAlphabet ( ) )
		lr0Automaton.addInputSymbol ( nonterminal );

	for ( const DefaultSymbolType & terminal : augmentedGrammar.getTerminalAlphabet ( ) )
		lr0Automaton.addInputSymbol ( terminal );

	std::queue < LR0Items > itemsToProcess;
	itemsToProcess.push ( initialState );
	while ( !itemsToProcess.empty ( ) ) {
		LR0Items currentState = itemsToProcess.front ( );
		itemsToProcess.pop ( );

		for ( const ext::variant < DefaultSymbolType, DefaultSymbolType > & symbol : lr0Automaton.getInputAlphabet ( ) ) {
			LR0Items nextState = getNextStateItems ( currentState, symbol, augmentedGrammar );
			if ( !nextState.empty ( ) ) {
				ext::set < LR0Items > ::iterator stateIterator = lr0Automaton.getStates ( ) . find ( nextState );
				if ( stateIterator == lr0Automaton.getStates ( ) . end ( ) ) {
					lr0Automaton.addState ( nextState );
					lr0Automaton.addTransition ( currentState, symbol, nextState );
					itemsToProcess.push ( nextState );
				} else {
					lr0Automaton.addTransition ( currentState, symbol, *stateIterator );
				}
			}
		}
	}

	return lr0Automaton;
}

} /* namespace parsing */

} /* namespace grammar */
