/*
 * LRParser.cpp
 *
 *  Created on: 30. 4. 2016
 *	  Author: Martin Kocicka
 */

#include "LRParser.h"
#include <alphabet/EndSymbol.h>

#include <stack>
#include <common/createUnique.hpp>

namespace grammar {

namespace parsing {

DefaultSymbolType LRParser::getEndOfInputSymbol ( const grammar::CFG < > & originalGrammar ) {
	return common::createUnique ( DefaultSymbolType ( alphabet::EndSymbol ( ) ), originalGrammar.getTerminalAlphabet ( ), originalGrammar.getNonterminalAlphabet ( ) );
}

grammar::CFG < > LRParser::getAugmentedGrammar ( grammar::CFG < > originalGrammar ) {
	DefaultSymbolType initialSymbol = common::createUnique ( originalGrammar.getInitialSymbol(), originalGrammar.getTerminalAlphabet ( ), originalGrammar.getNonterminalAlphabet ( ) );

	originalGrammar.addNonterminalSymbol ( initialSymbol );
	originalGrammar.addRule ( initialSymbol, { originalGrammar.getInitialSymbol ( ) } );
	originalGrammar.setInitialSymbol ( initialSymbol );

	return originalGrammar;
}

bool LRParser::parse ( const LRActionTable & actionTable, const LRGotoTable & gotoTable, const LR0Items & initialState, const ext::vector < DefaultSymbolType > & input ) {
	std::stack < LR0Items > states;
	states.push ( initialState );

	unsigned currentPosition = 0;
	while ( true ) {
		LRActionTable::const_iterator actionIterator = actionTable.find ( { states.top ( ), input[currentPosition] } );
		if ( actionIterator == actionTable.end ( ) )
			return false;

		switch ( actionIterator->second.first ) {
			case LRAction::Shift:
				states.push ( actionIterator->second.second.get < LR0Items > ( ) );

				++currentPosition;
				break;

			case LRAction::Reduce: {
				ext::pair < DefaultSymbolType, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > reduceBy = actionIterator->second.second.get < ext::pair < DefaultSymbolType, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > ( );
				for ( unsigned i = 0; i < reduceBy.second.size ( ); ++i ) {
					states.pop ( );
				}

				LRGotoTable::const_iterator nextStateIterator = gotoTable.find ( { states.top ( ), reduceBy.first } );
				if (nextStateIterator == gotoTable.end()) {
					return false;
				}

				states.push ( nextStateIterator->second );

				break;
			}

			case LRAction::Accept:
				return true;
				break;
		}
	}

	return false;
}

} /* namespace parsing */

} /* namespace grammar */
