#include <parser/Parser.h>

#include <ast/statements/CastStatement.h>
#include <ast/statements/SingleStatement.h>
#include <ast/statements/ContainerStatement.h>
#include <ast/statements/ResultFileStatement.h>
#include <ast/statements/ResultVariableStatement.h>
#include <ast/statements/VariableStatement.h>
#include <ast/statements/ValueStatement.h>
#include <ast/statements/FileStatement.h>
#include <ast/statements/ImmediateStatement.h>
#include <ast/statements/PreviousResultStatement.h>

#include <ast/args/BindedArg.h>
#include <ast/args/ImmediateArg.h>

#include <ast/command/PrintCommand.h>
#include <ast/command/ExecuteCommand.h>
#include <ast/command/QuitCommand.h>
#include <ast/command/EOTCommand.h>
#include <ast/command/HelpCommand.h>
#include <ast/command/AlgorithmsIntrospectionCommand.h>
#include <ast/command/OperatorsIntrospectionCommand.h>
#include <ast/command/OverloadsIntrospectionCommand.h>
#include <ast/command/DataTypesIntrospectionCommand.h>
#include <ast/command/CastsIntrospectionCommand.h>
#include <ast/command/BindingsIntrospectionCommand.h>
#include <ast/command/VariablesIntrospectionCommand.h>
#include <ast/command/SetCommand.h>
#include <ast/command/ShowMeasurements.h>
#include <ast/command/ClearMeasurements.h>
#include <ast/command/StartMeasurementFrame.h>
#include <ast/command/StopMeasurementFrame.h>
#include <ast/command/LoadCommand.h>
#include <ast/command/UnloadCommand.h>
#include <ast/command/CalcCommand.h>
#include <ast/command/BlockCommand.h>
#include <ast/command/EvalCommand.h>
#include <ast/command/InterpretCommand.h>
#include <ast/command/IfCommand.h>
#include <ast/command/WhileCommand.h>
#include <ast/command/BreakCommand.h>
#include <ast/command/ContinueCommand.h>
#include <ast/command/ReturnCommand.h>
#include <ast/command/VarDeclareCommand.h>
#include <ast/command/DeclareRunnableCommand.h>

#include <ast/expression/BatchExpression.h>
#include <ast/expression/BinaryExpression.h>
#include <ast/expression/PrefixExpression.h>
#include <ast/expression/PostfixExpression.h>
#include <ast/expression/CastExpression.h>
#include <ast/expression/FunctionCallExpression.h>
#include <ast/expression/MethodCallExpression.h>
#include <ast/expression/VariableExpression.h>
#include <ast/expression/ImmediateExpression.h>
#include <ast/expression/TypeExpression.h>

namespace cli {

std::unique_ptr < CategoryOption > Parser::category_option ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::COLON_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < CategoryOption > ( value );
	} else {
		return nullptr;
	}
}

std::unique_ptr < TypeOption > Parser::type_option ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::COLON_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < TypeOption > ( std::move ( value ) );
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched colon sign while expanding type_option rule. Token is " + Lexer::tokenTypeToString ( m_current.m_type ) + "." );
	}
}

std::unique_ptr < TypeOption > Parser::optional_type_option ( ) {
	if ( check ( cli::Lexer::TokenType::COLON_SIGN ) )
		return type_option ( );
	else
		return nullptr;
}

std::unique_ptr < Arg > Parser::file ( ) {
	setHint ( Lexer::Hint::FILE );
	if ( check_then_match ( cli::Lexer::TokenType::HASH_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < BindedArg > ( std::move ( value ) );
	} else if ( check ( cli::Lexer::TokenType::STRING ) ) {
		return std::make_unique < ImmediateArg > ( matchString ( ) );
	} else {
		return std::make_unique < ImmediateArg > ( matchFile ( ) );
	}
}

std::unique_ptr < Arg > Parser::type ( ) {
	setHint ( Lexer::Hint::TYPE );
	if ( check ( cli::Lexer::TokenType::STRING ) )
		return std::make_unique < ImmediateArg > ( matchString ( ) );
	else
		return std::make_unique < ImmediateArg > ( matchType ( ) );
}

std::unique_ptr < Arg > Parser::arg ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::HASH_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < BindedArg > ( std::move ( value ) );
	} else {
		std::string value = matchIdentifier ( );
		return std::make_unique < ImmediateArg > ( value );
	}
}

std::unique_ptr < Arg > Parser::optional_arg ( ) {
	if ( check ( cli::Lexer::TokenType::HASH_SIGN, cli::Lexer::TokenType::IDENTIFIER ) )
		return arg ( );
	else
		return nullptr;
}

std::unique_ptr < Arg > Parser::template_arg ( ) {
	match ( cli::Lexer::TokenType::AT_SIGN );
	return arg ( );
}

std::unique_ptr < Arg > Parser::optional_variable ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::DOLLAR_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < ImmediateArg > ( value );
	} else {
		return nullptr;
	}
}

std::unique_ptr < Arg > Parser::optional_binding ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::HASH_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < ImmediateArg > ( value );
	} else {
		return nullptr;
	}
}

std::shared_ptr < Statement > Parser::in_redirect ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::LEFT_PAREN ) ) {
		std::shared_ptr < StatementList > res = statement_list ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );
		return res;
	} else {
		std::unique_ptr < Arg > fileType;

		if ( check_then_match ( cli::Lexer::TokenType::LEFT_BRACKET ) ) {
			fileType = arg ( );
			match ( cli::Lexer::TokenType::RIGHT_BRACKET );
		}

		std::unique_ptr < TypeOption > type = optional_type_option ( );
		ext::vector < std::unique_ptr < cli::Arg > > templateArgs;
		while ( check ( cli::Lexer::TokenType::AT_SIGN ) )
			templateArgs.emplace_back ( template_arg ( ) );

		return std::make_shared < FileStatement > ( file ( ), std::move ( fileType ), std::move ( type ), std::move ( templateArgs ) );
	}
}

std::unique_ptr < Statement > Parser::out_redirect ( ) {
	if ( check_then_match ( cli::Lexer::TokenType::DOLLAR_SIGN ) ) {
		std::unique_ptr < Arg > name = arg ( );
		return std::make_unique < ResultVariableStatement > ( std::move ( name ) );
	} else {
		std::unique_ptr < Arg > fileType;

		if ( check_then_match ( cli::Lexer::TokenType::LEFT_BRACKET ) ) {
			fileType = arg ( );
			match ( cli::Lexer::TokenType::RIGHT_BRACKET );
		}

		return std::make_unique < ResultFileStatement > ( file ( ), std::move ( fileType ) );
	}
}

std::shared_ptr < Statement > Parser::common ( ) {
	clearCheckOptions ( );
	if ( check_then_match ( cli::Lexer::TokenType::DOLLAR_SIGN ) ) {
		std::unique_ptr < Arg > name = arg ( );
		return std::make_shared < VariableStatement > ( std::move ( name ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::LESS_THAN ) ) {
		return in_redirect ( );
	} else if ( check ( cli::Lexer::TokenType::STRING ) ) {
		std::string value = matchString ( );
		return std::make_shared < ImmediateStatement < std::string > > ( value );
	} else if ( check ( cli::Lexer::TokenType::UNSIGNED ) ) {
		int value = matchInteger ( );
		return std::make_shared < ImmediateStatement < int > > ( value );
	} else if ( check ( cli::Lexer::TokenType::DOUBLE ) ) {
		double value = matchDouble ( );
		return std::make_shared < ImmediateStatement < double > > ( value );
	} else if ( check_then_match ( cli::Lexer::TokenType::HASH_SIGN ) ) {
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_shared < ValueStatement > ( std::make_unique < BindedArg > ( std::move ( value ) ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::LEFT_BRACE ) ) {
		std::unique_ptr < TypeOption > type = type_option ( );

		ext::vector < std::shared_ptr < Statement > > params;
		while ( ! check ( cli::Lexer::TokenType::RIGHT_BRACE ) )
			params.emplace_back ( param ( ) );

		std::shared_ptr < Statement > res = std::make_shared < ContainerStatement > ( "Set", std::move ( params ), std::move ( type ) );
		match ( cli::Lexer::TokenType::RIGHT_BRACE );
		return res;
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding common rule. Token is " + ( ( std::string ) m_current ) + "." );
	}
}

std::shared_ptr < Statement > Parser::param ( ) {
	clearCheckOptions ( );
	if ( check ( cli::Lexer::TokenType::DOLLAR_SIGN, cli::Lexer::TokenType::LESS_THAN, cli::Lexer::TokenType::STRING, cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::DOUBLE, cli::Lexer::TokenType::HASH_SIGN, cli::Lexer::TokenType::LEFT_BRACE ) ) {
		return common ( );
	} else if ( check_then_match ( cli::Lexer::TokenType::MINUS_SIGN ) ) {
		return std::make_shared < PreviousResultStatement > ( );
	} else if ( check ( cli::Lexer::TokenType::IDENTIFIER ) ) {
		std::string value = matchIdentifier ( );
		return std::make_shared < ImmediateStatement < std::string > > ( value );
	} else if ( check_then_match ( cli::Lexer::TokenType::LEFT_PAREN ) ) {
		std::unique_ptr < Arg > result_type = type ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );
		std::shared_ptr < Statement > castedParam = param ( );
		return std::make_shared < CastStatement > ( std::move ( result_type ), castedParam );
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding param rule. Token is " + ( ( std::string ) m_current ) + "." );
	}
}

std::shared_ptr < Statement > Parser::statement ( ) {
	clearCheckOptions ( );
	if ( check ( cli::Lexer::TokenType::DOLLAR_SIGN, cli::Lexer::TokenType::LESS_THAN, cli::Lexer::TokenType::STRING, cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::DOUBLE, cli::Lexer::TokenType::HASH_SIGN, cli::Lexer::TokenType::LEFT_BRACE ) ) {
		return common ( );
	} else if ( check ( cli::Lexer::TokenType::IDENTIFIER ) ) {
		std::unique_ptr < Arg > name = std::make_unique < ImmediateArg > ( matchIdentifier ( ) );
		ext::vector < std::unique_ptr < cli::Arg > > templateArgs;
		while ( check ( cli::Lexer::TokenType::AT_SIGN ) )
			templateArgs.emplace_back ( template_arg ( ) );

		std::unique_ptr < CategoryOption > category = category_option ( );
		ext::vector < std::shared_ptr < Statement > > params;
		while ( ! check ( cli::Lexer::TokenType::MORE_THAN, cli::Lexer::TokenType::PIPE_SIGN, cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::RIGHT_PAREN, cli::Lexer::TokenType::SEMICOLON_SIGN ) ) {
			params.emplace_back ( param ( ) );
		}

		return std::make_shared < SingleStatement > ( std::move ( name ), std::move ( templateArgs ), std::move ( params ), std::move ( category ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::LEFT_PAREN ) ) {
		std::unique_ptr < Arg > result_type = type ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );
		std::shared_ptr < Statement > castedStatement = statement ( );
		return std::make_shared < CastStatement > ( std::move ( result_type ), castedStatement );
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding statement rule. Token is " + ( ( std::string ) m_current ) + "." );
	}
}

std::shared_ptr < StatementList > Parser::statement_list ( ) {
	ext::vector < std::shared_ptr < Statement > > list;
	list.emplace_back ( statement ( ) );
	while ( check ( cli::Lexer::TokenType::PIPE_SIGN, cli::Lexer::TokenType::MORE_THAN ) ) {
		if ( check_then_match ( cli::Lexer::TokenType::PIPE_SIGN ) ) {
			list.emplace_back ( statement ( ) );
		} else {
			match ( cli::Lexer::TokenType::MORE_THAN );
			list.emplace_back ( out_redirect ( ) );
		}
	}
	return std::make_shared < StatementList > ( std::move ( list ) );
}

std::pair < bool, bool > Parser::introspect_cast_from_to ( ) {
	bool from = false;
	bool to = false;
	while ( check_then_match ( cli::Lexer::TokenType::COLON_SIGN ) ) {
		clearCheckOptions ( );
		if ( check_nonreserved_kw ( "from" ) ) {
			match_nonreserved_kw ( "from" );
			from = true;
		} else if ( check_nonreserved_kw ( "to" ) ) {
			match_nonreserved_kw ( "to" );
			to = true;
		} else {
			throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding introspect_cast_type. Token is " + ( ( std::string ) m_current ) + "." );
		}
	}
	return std::make_pair ( from, to );
}

std::unique_ptr < Command > Parser::introspect_command ( ) {
	clearCheckOptions ( );
	if ( check_then_match_nonreserved_kw ( "algorithms" ) ) {
		std::unique_ptr < cli::Arg > param = optional_arg ( );
		return std::make_unique < AlgorithmsIntrospectionCommand > ( std::move ( param ) );
	} else if ( check_then_match_nonreserved_kw ( "overloads" ) ) {
		std::unique_ptr < cli::Arg > param = arg ( );

		ext::vector < std::unique_ptr < cli::Arg > > templateArgs;
		while ( check ( cli::Lexer::TokenType::AT_SIGN ) )
			templateArgs.emplace_back ( template_arg ( ) );

		return std::make_unique < OverloadsIntrospectionCommand > ( std::move ( param ), std::move ( templateArgs ) );
	} else if ( check_then_match_nonreserved_kw ( "operators" ) ) {
		return std::make_unique < OperatorsIntrospectionCommand > ( );
	} else if ( check_then_match_nonreserved_kw ( "datatypes" ) ) {
		std::unique_ptr < cli::Arg > param = optional_arg ( );
		return std::make_unique < DataTypesIntrospectionCommand > ( std::move ( param ) );
	} else if ( check_then_match_nonreserved_kw ( "casts" ) ) {
		std::pair < bool, bool > from_to = introspect_cast_from_to ( );
		std::unique_ptr < cli::Arg > param = optional_arg ( );
		return std::make_unique < CastsIntrospectionCommand > ( std::move ( param ), from_to.first, from_to.second );
	} else if ( check_then_match_nonreserved_kw ( "variables" ) ) {
		std::unique_ptr < cli::Arg > param = optional_variable ( );
		return std::make_unique < VariablesIntrospectionCommand > ( std::move ( param ) );
	} else if ( check_then_match_nonreserved_kw ( "bindings" ) ) {
		std::unique_ptr < cli::Arg > param = optional_arg ( );
		return std::make_unique < BindingsIntrospectionCommand > ( std::move ( param ) );
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding introspection_command rule. Token is " + ( ( std::string ) m_current ) + "." );
	}
}

std::unique_ptr < CommandList > Parser::block ( ) {
	incNestedLevel ( );
	match_nonreserved_kw ( "begin" );

	ext::vector < std::unique_ptr < Command > > list;
	while ( ! check_nonreserved_kw ( "end" ) )
		list.emplace_back ( semicolon_command ( ) );

	decNestedLevel ( );
	match_nonreserved_kw ( "end" );
	return std::make_unique < CommandList > ( std::move ( list ) );
}

std::unique_ptr < Command > Parser::semicolon_command ( ) {
	bool semicolonFreeCommand = check_nonreserved_kw ( "begin", "if", "while" );
	std::unique_ptr < Command > res = command ( );
	if ( ! semicolonFreeCommand )
		match ( cli::Lexer::TokenType::SEMICOLON_SIGN );
	return res;
}

std::unique_ptr < Expression > Parser::expression ( ) {
	return assign_expression ( );
}

std::unique_ptr < Expression > Parser::batch ( ) {
	std::shared_ptr < StatementList > res = statement_list ( );
	return std::make_unique < BatchExpression > ( std::move ( res ) );
}

std::unique_ptr < Expression > Parser::expression_or_batch ( ) {
	if ( check_then_match_nonreserved_kw ( "batch" ) ) {
		return batch ( );
	} else {
		check_then_match_nonreserved_kw ( "expression" ); // optional token
		return expression ( );
	}
}

std::unique_ptr < Expression > Parser::batch_or_expression ( ) {
	if ( check_then_match_nonreserved_kw ( "expression" ) ) {
		return expression ( );
	} else {
		check_then_match_nonreserved_kw ( "batch" ); // optional token
		return batch ( );
	}
}

std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > Parser::qualifiedType ( ) {
	abstraction::TypeQualifiers::TypeQualifierSet typeQualifiers = abstraction::TypeQualifiers::TypeQualifierSet::NONE;

	if ( check_then_match_nonreserved_kw ( "const" ) )
		typeQualifiers = typeQualifiers | abstraction::TypeQualifiers::TypeQualifierSet::CONST;

	std::unique_ptr < Arg > type = arg ( );

	if ( check_then_match ( cli::Lexer::TokenType::AND ) )
		typeQualifiers = typeQualifiers | abstraction::TypeQualifiers::TypeQualifierSet::RREF;
	else if ( check_then_match ( cli::Lexer::TokenType::AMPERSAND_SIGN ) )
		typeQualifiers = typeQualifiers | abstraction::TypeQualifiers::TypeQualifierSet::LREF;

	return std::make_pair ( typeQualifiers, std::move ( type ) );
}

std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > Parser::runnableParam ( ) {
	std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > qualType = qualifiedType ( );

	match ( cli::Lexer::TokenType::DOLLAR_SIGN );
	std::unique_ptr < Arg > name = arg ( );

	return std::make_pair ( qualType.first, std::move ( name ) );
}

std::unique_ptr < Command > Parser::command ( ) {
	clearCheckOptions ( );
	if ( check_then_match_nonreserved_kw ( "execute" ) ) {
		std::unique_ptr < Expression > expr = batch_or_expression ( );
		return std::make_unique < ExecuteCommand > ( std::move ( expr ) );
	} else if ( check_then_match_nonreserved_kw ( "print" ) ) {
		std::unique_ptr < Expression > expr = batch_or_expression ( );
		return std::make_unique < PrintCommand > ( std::move ( expr ) );
	} else if ( check_then_match_nonreserved_kw ( "quit" ) ) {
		std::unique_ptr < Expression > expr;
		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
			expr = batch_or_expression ( );

		return std::make_unique < QuitCommand > ( std::move ( expr ) );
	} else if ( check_then_match_nonreserved_kw ( "exit" ) ) {
		std::unique_ptr < Expression > expr;
		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
			expr = batch_or_expression ( );

		return std::make_unique < QuitCommand > ( std::move ( expr ) );
	} else if ( check_then_match_nonreserved_kw ( "return" ) ) {
		std::unique_ptr < Expression > expr;
		if ( ! check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT, cli::Lexer::TokenType::SEMICOLON_SIGN ) )
			expr = expression ( );

		return std::make_unique < ReturnCommand > ( std::move ( expr ) );
	} else if ( check_then_match_nonreserved_kw ( "help" ) ) {
		std::unique_ptr < cli::Arg > command = optional_arg ( );
		return std::make_unique < HelpCommand > ( std::move ( command ) );
	} else if ( check_then_match_nonreserved_kw ( "introspect" ) ) {
		return introspect_command ( );
	} else if ( check_then_match_nonreserved_kw ( "set" ) ) {
		std::string param = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		std::string value = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING );
		return std::make_unique < SetCommand > ( std::move ( param ), std::move ( value ) );
	} else if ( check_then_match_nonreserved_kw ( "show" ) ) {
		std::string param = getTokenValue ( );
		match_nonreserved_kw ( "measurements" );
		match_nonreserved_kw ( "as" );
		if ( check_then_match_nonreserved_kw ( "list" ) ) {
			return std::make_unique < ShowMeasurements > ( measurements::MeasurementFormat::LIST );
		} else {
			match_nonreserved_kw ( "tree" );
			return std::make_unique < ShowMeasurements > ( measurements::MeasurementFormat::TREE );
		}
	} else if ( check_then_match_nonreserved_kw ( "clear" ) ) {
		match_nonreserved_kw ( "measurements" );
		return std::make_unique < ClearMeasurements > ( );
	} else if ( check_then_match_nonreserved_kw ( "start" ) ) {
		measurements::Type type;
		if ( check_then_match_nonreserved_kw ( "root" ) ) {
			type = measurements::Type::ROOT;
		} else if ( check_then_match_nonreserved_kw ( "overall" ) ) {
			type = measurements::Type::OVERALL;
		} else if ( check_then_match_nonreserved_kw ( "init" ) ) {
			type = measurements::Type::INIT;
		} else if ( check_then_match_nonreserved_kw ( "finalize" ) ) {
			type = measurements::Type::FINALIZE;
		} else if ( check_then_match_nonreserved_kw ( "main" ) ) {
			type = measurements::Type::MAIN;
		} else if ( check_then_match_nonreserved_kw ( "auxiliary" ) ) {
			type = measurements::Type::AUXILIARY;
		} else if ( check_then_match_nonreserved_kw ( "preprocess" ) ) {
			type = measurements::Type::PREPROCESS;
		} else {
			match_nonreserved_kw ( "algorithm" );
			type = measurements::Type::ALGORITHM;
		}

		match_nonreserved_kw ( "measurement" );
		match_nonreserved_kw ( "frame" );

		std::string frameName = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER );
		return std::make_unique < StartMeasurementFrame > ( std::move ( frameName ), type );
	} else if ( check_then_match_nonreserved_kw ( "stop" ) ) {
		match_nonreserved_kw ( "measurement" );
		match_nonreserved_kw ( "frame" );

		return std::make_unique < StopMeasurementFrame > ( );
	} else if ( check_then_match_nonreserved_kw ( "load" ) ) {
		setHint ( Lexer::Hint::FILE );
		std::string libraryName = getTokenValue ( );
		match ( cli::Lexer::TokenType::FILE, cli::Lexer::TokenType::STRING );

		return std::make_unique < LoadCommand > ( std::move ( libraryName ) );
	} else if ( check_then_match_nonreserved_kw ( "unload" ) ) {
		setHint ( Lexer::Hint::FILE );
		std::string libraryName = getTokenValue ( );
		match ( cli::Lexer::TokenType::FILE, cli::Lexer::TokenType::STRING );

		return std::make_unique < UnloadCommand > ( std::move ( libraryName ) );
	} else if ( check_then_match_nonreserved_kw ( "calc" ) ) {
		std::unique_ptr < Expression > expr = expression ( );
		return std::make_unique < CalcCommand > ( std::move ( expr ) );
	} else if ( check_nonreserved_kw ( "begin" ) ) {
		return std::make_unique < BlockCommand > ( block ( ) );
	} else if ( check_then_match_nonreserved_kw ( "eval" ) ) {
		std::string evalString = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING );
		return std::make_unique < EvalCommand > ( std::move ( evalString ) );
	} else if ( check_then_match_nonreserved_kw ( "interpret" ) ) {
		setHint ( Lexer::Hint::FILE );
		std::string fileName = getTokenValue ( );
		match ( cli::Lexer::TokenType::FILE, cli::Lexer::TokenType::STRING );

		return std::make_unique < InterpretCommand > ( std::move ( fileName ) );
	} else if ( check_then_match_nonreserved_kw ( "if" ) ) {
		if ( globalScope ( ) )
			throw exception::CommonException ( "Statement not available in global scope." );
		match ( cli::Lexer::TokenType::LEFT_PAREN );
		std::unique_ptr < Expression > condition = expression_or_batch ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );

		match_nonreserved_kw ( "then" );
		std::unique_ptr < Command > thenBranch = semicolon_command ( );

		std::unique_ptr < Command > elseBranch;
		if ( check_nonreserved_kw ( "else" ) ) {
			match_nonreserved_kw ( "else" );
			elseBranch = semicolon_command ( );
		}

		return std::make_unique < IfCommand > ( std::move ( condition ), std::move ( thenBranch ), std::move ( elseBranch ) );
	} else if ( check_then_match_nonreserved_kw ( "while" ) ) {
		if ( globalScope ( ) )
			throw exception::CommonException ( "Statement not available in global scope." );

		match ( cli::Lexer::TokenType::LEFT_PAREN );
		std::unique_ptr < Expression > condition = expression_or_batch ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );

		match_nonreserved_kw ( "do" );
		std::unique_ptr < Command > body = semicolon_command ( );

		return std::make_unique < WhileCommand > ( std::move ( condition ), std::move ( body ) );
	} else if ( check_then_match_nonreserved_kw ( "break" ) ) {
		if ( globalScope ( ) )
			throw exception::CommonException ( "Statement not available in global scope." );
		return std::make_unique < BreakCommand > ( );
	} else if ( check_then_match_nonreserved_kw ( "continue" ) ) {
		if ( globalScope ( ) )
			throw exception::CommonException ( "Statement not available in global scope." );
		return std::make_unique < ContinueCommand > ( );
	} else if ( check_then_match_nonreserved_kw ( "declare" ) ) {
		std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > qualType = qualifiedType ( );

		match ( cli::Lexer::TokenType::DOLLAR_SIGN );
		std::unique_ptr < Arg > name = arg ( );

		match ( cli::Lexer::TokenType::ASSIGN );

		std::unique_ptr < Expression > expr = expression ( );

		return std::make_unique < VarDeclareCommand > ( std::move ( name ), qualType.first, std::move ( expr ) );
	} else if ( check_then_match_nonreserved_kw ( "procedure" ) ) {
		if ( ! globalScope ( ) )
			throw exception::CommonException ( "Statement available in global scope only." );

		std::string routineName = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING );

		std::vector < std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > > params;
		match ( cli::Lexer::TokenType::LEFT_PAREN );
		if ( ! check ( cli::Lexer::TokenType::RIGHT_PAREN ) ) {
			params.push_back ( Parser::runnableParam ( ) );

			while ( check ( cli::Lexer::TokenType::COMMA ) ) {
				match ( cli::Lexer::TokenType::COMMA );
				params.push_back ( Parser::runnableParam ( ) );
			}
		}
		match ( cli::Lexer::TokenType::RIGHT_PAREN );

		std::unique_ptr < Command > body = command ( );

		return std::make_unique < DeclareRunnableCommand > ( std::move ( routineName ), std::move ( params ), std::move ( body ) );
	} else if ( check_then_match_nonreserved_kw ( "function" ) ) {
		if ( ! globalScope ( ) )
			throw exception::CommonException ( "Statement available in global scope only." );

		std::string routineName = getTokenValue ( );
		match ( cli::Lexer::TokenType::UNSIGNED, cli::Lexer::TokenType::IDENTIFIER, cli::Lexer::TokenType::STRING );

		std::vector < std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > > params;
		match ( cli::Lexer::TokenType::LEFT_PAREN );
		if ( ! check ( cli::Lexer::TokenType::RIGHT_PAREN ) ) {
			params.push_back ( Parser::runnableParam ( ) );

			while ( check ( cli::Lexer::TokenType::COMMA ) ) {
				match ( cli::Lexer::TokenType::COMMA );
				params.push_back ( Parser::runnableParam ( ) );
			}
		}
		match ( cli::Lexer::TokenType::RIGHT_PAREN );

		match_nonreserved_kw ( "returning" );

		std::pair < abstraction::TypeQualifiers::TypeQualifierSet, std::unique_ptr < Arg > > returnType = qualifiedType ( );

		std::unique_ptr < Command > body = command ( );

		return std::make_unique < DeclareRunnableCommand > ( std::move ( routineName ), std::move ( params ), std::move ( returnType ), std::move ( body ) );
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding parse rule. Token is " + ( ( std::string ) m_current ) + "." );
	}
}

std::unique_ptr < CommandList > Parser::parse ( ) {
	ext::vector < std::unique_ptr < Command > > list;

	if ( check ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT ) ) {
		list.emplace_back ( std::make_unique < EOTCommand > ( ) );
		return std::make_unique < CommandList > ( std::move ( list ) );
	}

	list.emplace_back ( command ( ) );
	while ( check_then_match ( cli::Lexer::TokenType::SEMICOLON_SIGN ) )
		list.emplace_back ( command ( ) );

	match ( cli::Lexer::TokenType::EOS, cli::Lexer::TokenType::EOT );
	return std::make_unique < CommandList > ( std::move ( list ) );
}

std::unique_ptr < Expression > Parser::assign_expression ( ) {
	std::unique_ptr < Expression > res = or_expression ( );
	if ( check_then_match ( cli::Lexer::TokenType::ASSIGN ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );

		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::ASSIGN, std::move ( res ), assign_expression ( ) );

		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::or_expression ( ) {
	std::unique_ptr < Expression > res = and_expression ( );
	while ( check_then_match ( cli::Lexer::TokenType::OR ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );

		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::LOGICAL_OR, std::move ( res ), and_expression ( ) );

		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::and_expression ( ) {
	std::unique_ptr < Expression > res = bitwise_or_expression ( );
	while ( check_then_match ( cli::Lexer::TokenType::AND ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );

		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::LOGICAL_AND, std::move ( res ), bitwise_or_expression ( ) );

		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::bitwise_or_expression ( ) {
	std::unique_ptr < Expression > res = bitwise_and_expression ( );
	while ( check_then_match ( cli::Lexer::TokenType::PIPE_SIGN ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );

		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::BINARY_OR, std::move ( res ), bitwise_and_expression ( ) );

		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::bitwise_and_expression ( ) {
	std::unique_ptr < Expression > res = bitwise_xor_expression ( );
	while ( check_then_match ( cli::Lexer::TokenType::AMPERSAND_SIGN ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );

		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::BINARY_AND, std::move ( res ), bitwise_xor_expression ( ) );

		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::bitwise_xor_expression ( ) {
	std::unique_ptr < Expression > res = equality_expression ( );
	while ( check_then_match ( cli::Lexer::TokenType::CARET_SIGN ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );

		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::BINARY_XOR, std::move ( res ), equality_expression ( ) );

		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::equality_expression ( ) {
	std::unique_ptr < Expression > res = relational_expression ( );
	auto checkOptions = getCheckOptions ( );
	clearCheckOptions ( );
	if ( check_then_match ( cli::Lexer::TokenType::EQUAL ) ) {
		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::EQUALS, std::move ( res ), relational_expression ( ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::NOT_EQUAL ) ) {
		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::NOT_EQUALS, std::move ( res ), relational_expression ( ) );
	}
	restoreCheckOptions ( checkOptions );
	return res;
}

std::unique_ptr < Expression > Parser::relational_expression ( ) {
	std::unique_ptr < Expression > res = add_expression ( );
	auto checkOptions = getCheckOptions ( );
	clearCheckOptions ( );
	if ( check_then_match ( cli::Lexer::TokenType::LESS_THAN ) ) {
		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::LESS, std::move ( res ), add_expression ( ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::LESS_THAN_OR_EQUAL ) ) {
		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::LESS_OR_EQUAL, std::move ( res ), add_expression ( ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::MORE_THAN ) ) {
		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::MORE, std::move ( res ), add_expression ( ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::MORE_THAN_OR_EQUAL ) ) {
		res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::MORE_OR_EQUAL, std::move ( res ), add_expression ( ) );
	}
	restoreCheckOptions ( checkOptions );
	return res;
}

std::unique_ptr < Expression > Parser::add_expression ( ) {
	std::unique_ptr < Expression > res = mul_expression ( );
	while ( check ( cli::Lexer::TokenType::PLUS_SIGN, cli::Lexer::TokenType::MINUS_SIGN ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );
		if ( check_then_match ( cli::Lexer::TokenType::PLUS_SIGN ) ) {
			res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::ADD, std::move ( res ), mul_expression ( ) );
		} else {
			match ( cli::Lexer::TokenType::MINUS_SIGN );
			res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::SUB, std::move ( res ), mul_expression ( ) );
		}
		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::mul_expression ( ) {
	std::unique_ptr < Expression > res = prefix_expression ( );
	while ( check ( cli::Lexer::TokenType::ASTERISK_SIGN, cli::Lexer::TokenType::PERCENTAGE_SIGN, cli::Lexer::TokenType::SLASH_SIGN ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );
		if ( check_then_match ( cli::Lexer::TokenType::ASTERISK_SIGN ) ) {
			res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::MUL, std::move ( res ), prefix_expression ( ) );
		} else if ( check_then_match ( cli::Lexer::TokenType::PERCENTAGE_SIGN ) ) {
			res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::MOD, std::move ( res ), prefix_expression ( ) );
		} else {
			match ( cli::Lexer::TokenType::SLASH_SIGN );
			res = std::make_unique < BinaryExpression > ( abstraction::Operators::BinaryOperators::DIV, std::move ( res ), prefix_expression ( ) );
		}
		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::prefix_expression ( ) {
	clearCheckOptions ( );
	if ( check_then_match_nonreserved_kw ( "cast"  ) ) {
		match ( cli::Lexer::TokenType::LEFT_PAREN );
		std::unique_ptr < Arg > result_type = type ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < CastExpression > ( std::move ( result_type ), std::move ( expression ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::PLUS_SIGN ) ) {
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < PrefixExpression > ( abstraction::Operators::PrefixOperators::PLUS, std::move ( expression ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::MINUS_SIGN ) ) {
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < PrefixExpression > ( abstraction::Operators::PrefixOperators::MINUS, std::move ( expression ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::EXCLAMATION_SIGN ) ) {
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < PrefixExpression > ( abstraction::Operators::PrefixOperators::LOGICAL_NOT, std::move ( expression ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::TILDE_SIGN ) ) {
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < PrefixExpression > ( abstraction::Operators::PrefixOperators::BINARY_NEG, std::move ( expression ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::INC ) ) {
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < PrefixExpression > ( abstraction::Operators::PrefixOperators::INCREMENT, std::move ( expression ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::DEC ) ) {
		std::unique_ptr < Expression > expression = prefix_expression ( );
		return std::make_unique < PrefixExpression > ( abstraction::Operators::PrefixOperators::DECREMENT, std::move ( expression ) );
	} else {
		return suffix_expression ( );
	}
}

std::unique_ptr < Expression > Parser::suffix_expression ( ) {
	std::unique_ptr < Expression > res = atom ( );
	clearCheckOptions ( );
	while ( check ( cli::Lexer::TokenType::DOT, cli::Lexer::TokenType::INC, cli::Lexer::TokenType::DEC ) ) {
		auto checkOptions = getCheckOptions ( );
		clearCheckOptions ( );
		if ( check_then_match ( cli::Lexer::TokenType::DOT ) ) {
			std::string memberName = getTokenValue ( );
			match ( cli::Lexer::TokenType::IDENTIFIER );
			std::vector < std::unique_ptr < Expression > > params = bracketed_expression_list ( );
			res = std::make_unique < MethodCallExpression > ( std::move ( res ), std::move ( memberName ), std::move ( params ) );
		} else if ( check_then_match ( cli::Lexer::TokenType::INC ) ){
			res = std::make_unique < PostfixExpression > ( abstraction::Operators::PostfixOperators::INCREMENT, std::move ( res ) );
		} else if ( check_then_match ( cli::Lexer::TokenType::DEC ) ){
			res = std::make_unique < PostfixExpression > ( abstraction::Operators::PostfixOperators::DECREMENT, std::move ( res ) );
		} else {
			throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding suffix_expression rule. Token is " + ( ( std::string ) m_current ) + "." );
		}
		restoreCheckOptions ( checkOptions );
	}
	return res;
}

std::unique_ptr < Expression > Parser::atom ( ) {
	if ( check_then_match_nonreserved_kw ( "type" ) ) {
		match ( cli::Lexer::TokenType::LEFT_PAREN );
		std::unique_ptr < Expression > expr = expression ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );
		return std::make_unique < TypeExpression > ( std::move ( expr ) );
	} else if ( check ( cli::Lexer::TokenType::IDENTIFIER ) ) {
		std::string routineName = getTokenValue ( );
		match ( cli::Lexer::TokenType::IDENTIFIER );
		std::vector < std::unique_ptr < Expression > > params = bracketed_expression_list ( );
		return std::make_unique < FunctionCallExpression > ( routineName, std::move ( params ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::DOLLAR_SIGN ) ) {
		std::unique_ptr < Arg > name = arg ( );
		return std::make_unique < VariableExpression > ( std::move ( name ) );
	} else if ( check_then_match ( cli::Lexer::TokenType::LEFT_PAREN ) ) {
		std::unique_ptr < Expression > expr = expression ( );
		match ( cli::Lexer::TokenType::RIGHT_PAREN );
		return expr;
	} else if ( check ( cli::Lexer::TokenType::STRING ) ) {
		std::string value = matchString ( );
		return std::make_unique < ImmediateExpression < std::string > > ( value );
	} else if ( check ( cli::Lexer::TokenType::UNSIGNED ) ) {
		int value = matchInteger ( );
		return std::make_unique < ImmediateExpression < int > > ( value );
	} else if ( check ( cli::Lexer::TokenType::DOUBLE ) ) {
		double value = matchDouble ( );
		return std::make_unique < ImmediateExpression < double > > ( value );
	} else {
		throw exception::CommonException ( lineInfo ( ) + "Mismatched set " + ext::to_string ( getCheckOptions ( ) ) + " while expanding prefix_expression and atom rules. Token is " + ( ( std::string ) m_current ) + "." );
	}
}

std::vector < std::unique_ptr < Expression > > Parser::bracketed_expression_list ( ) {
	std::vector < std::unique_ptr < Expression > > res;
	match ( cli::Lexer::TokenType::LEFT_PAREN );
	if ( ! check ( cli::Lexer::TokenType::RIGHT_PAREN ) ) {
		res.push_back ( expression ( ) );
		while ( check_then_match ( cli::Lexer::TokenType::COMMA ) )
			res.push_back ( expression ( ) );
	}
	match ( cli::Lexer::TokenType::RIGHT_PAREN );
	return res;
}

} /* namespace cli */
