/*
 * SLR1ParseTable.cpp
 *
 *  Created on: 4. 5. 2016
 *	  Author: Martin Kocicka
 */

#include "SLR1ParseTable.h"

#include "LRParser.h"
#include "LR0Parser.h"
#include "Follow.h"

#include <exception/CommonException.h>

namespace grammar {

namespace parsing {

void SLR1ParseTable::insertToActionTable ( LRActionTable & actionTable, LRActionTable::key_type key, LRActionTable::mapped_type value ) {
	std::pair<LRActionTable::iterator, bool> result = actionTable.emplace ( std::move ( key ), std::move ( value ) );
	if ( ! result.second ) {
		throw exception::CommonException ( "Grammar is not SLR(1)!" );
	}
}

LRActionTable SLR1ParseTable::getActionTable ( const grammar::CFG < > & originalGrammar ) {
	LRActionTable actionTable;
	grammar::CFG < > augmentedGrammar = LRParser::getAugmentedGrammar ( originalGrammar );
	automaton::DFA < ext::variant < DefaultSymbolType, DefaultSymbolType >, LR0Items > parsingAutomaton = LR0Parser::getAutomaton ( originalGrammar );
	for ( const LR0Items & state : parsingAutomaton.getStates ( ) ) {
		for ( const LR0Items::value_type & nonterminalItems : state ) {
			DefaultSymbolType leftHandSide = nonterminalItems.first;
			for ( const std::pair < unsigned, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > & item : nonterminalItems.second ) {
				unsigned position = item.first;
				ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rightHandSide = item.second;

				if ( position == rightHandSide.size ( ) ) {
					if ( leftHandSide == augmentedGrammar.getInitialSymbol ( ) ) {
						insertToActionTable(actionTable, { state, LRParser::getEndOfInputSymbol(augmentedGrammar) }, { LRAction::Accept, state } );
						continue;
					}

					ext::set < ext::vector < DefaultSymbolType > > followSet = grammar::parsing::Follow::follow ( augmentedGrammar, leftHandSide );
					ext::pair < DefaultSymbolType, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > currentRule = { leftHandSide, rightHandSide };
					for ( const ext::vector < DefaultSymbolType > & followSymbol : followSet ) {
						if ( followSymbol.size ( ) == 1 ) {
							insertToActionTable(actionTable, { state, followSymbol.front ( ) }, { LRAction::Reduce, currentRule } );
						} else {
							insertToActionTable(actionTable, { state, LRParser::getEndOfInputSymbol(augmentedGrammar) }, { LRAction::Reduce, currentRule } );
						}
					}
					continue;
				}

				ext::variant < DefaultSymbolType, DefaultSymbolType > currentSymbol = rightHandSide[position];
				if ( originalGrammar.getTerminalAlphabet ( ) . find ( currentSymbol ) != originalGrammar.getTerminalAlphabet ( ) . end ( ) ) {
					insertToActionTable(actionTable, { state, currentSymbol.get < DefaultSymbolType /* terminal */ > ( ) }, { LRAction::Shift, parsingAutomaton.getTransitions ( ).find ( { state, currentSymbol } )->second } );
				}
			}
		}
	}

	return actionTable;
}

LRGotoTable SLR1ParseTable::getGotoTable ( const grammar::CFG < > & originalGrammar ) {
	LRGotoTable gotoTable;
	grammar::CFG < > augmentedGrammar = LRParser::getAugmentedGrammar ( originalGrammar );
	automaton::DFA < ext::variant < DefaultSymbolType, DefaultSymbolType >, LR0Items > parsingAutomaton = LR0Parser::getAutomaton ( originalGrammar );
	for ( const LR0Items & state : parsingAutomaton.getStates ( ) ) {
		for ( const DefaultSymbolType & nonterminal : augmentedGrammar.getNonterminalAlphabet ( ) ) {
			ext::map < ext::pair < LR0Items, ext::variant < DefaultSymbolType, DefaultSymbolType > >, LR0Items >::const_iterator transitionIterator = parsingAutomaton.getTransitions ( ).find ( { state, nonterminal } );
			if ( transitionIterator != parsingAutomaton.getTransitions ( ).end ( ) ) {
				gotoTable.insert ( { { state, nonterminal }, transitionIterator->second } );
			}
		}
	}

	return gotoTable;
}

} /* namespace parsing */

} /* namespace grammar */
