#include "opast.hh"

using namespace ebcl;
using namespace opast;


/*= A_Node ===================================================================*/

A_Node::A_Node( const E_Type type ,
		A_Node* const parent ) noexcept
	: type_( type ) , parent_( parent )
{
	assert( ( type == ROOT && !parent ) || ( type != ROOT && parent ) );
}

A_Node::~A_Node( ) { }

T_RootNode& A_Node::root( ) const noexcept
{
	A_Node const* node( this );
	while ( node->parent_ ) {
		node = node->parent_;
	}
	assert( node );
	assert( node->type_ == ROOT );
	return *dynamic_cast< T_RootNode* >(
			const_cast< A_Node* >( node ) );
}


/*= T_InstrListNode ==========================================================*/

T_InstrListNode::T_InstrListNode(
		A_Node& parent ) noexcept
	: A_Node( ILIST , &parent )
{ }


/*= A_FuncNode ===============================================================*/

A_FuncNode::A_FuncNode(
		const bool isInit ,
		T_RootNode* const root ) noexcept
	: A_Node( isInit ? DECL_INIT : DECL_FRAME , root ) ,
		name_( isInit ? "*init*" : "*frame*" ) ,
		instructions_( *this )
{ }

A_FuncNode::A_FuncNode(
		T_String const& name ,
		T_RootNode* const root ) noexcept
	: A_Node( DECL_FN , root ) , name_( name ) ,
		instructions_( *this )
{ }


/*= T_RootNode ===============================================================*/

T_RootNode::T_RootNode( ) noexcept
	: A_Node( ROOT , nullptr ) , functions_(
			[]( T_OwnPtr< A_FuncNode > const& f ) {
				return f->name( );
			} )
{ }


T_RootNode::T_AddFunctionResult T_RootNode::addFunction(
		T_OwnPtr< A_FuncNode >& function ) noexcept
{
	T_String const& fn( function->name( ) );
	auto const* const pf( functions_.get( fn ) );
	if ( !pf ) {
		auto* const rv( function.get( ) );
		functions_.add( std::move( function ) );
		return *rv;
	}

	T_String dfn;
	uint32_t dupCtr( 0 );
	do {
		T_StringBuilder fnsb;
		fnsb << fn << " dup " << dupCtr ++;
		dfn = std::move( fnsb );
	} while ( functions_.contains( dfn ) );

	T_OwnPtr< A_FuncNode > df( NewOwned< T_FuncNode >( dfn , *this ) );
	auto* const rv( df.get( ) );
	functions_.add( std::move( df ) );
	return T_AddFunctionResult{ *rv , (*pf)->location( ) };
}


/*= T_SpecialFuncNode ========================================================*/

T_SpecialFuncNode::T_SpecialFuncNode(
		bool isInit ,
		T_RootNode& parent ) noexcept
	: A_FuncNode( isInit , &parent )
{ }


/*= T_FuncNode ===============================================================*/

T_FuncNode::T_FuncNode(
		T_String const& name ,
		T_RootNode& parent ) noexcept
	: A_FuncNode( name , &parent )
{ }

T_Optional< T_SRDLocation > T_FuncNode::addArgument(
		T_SRDToken const& token ) noexcept
{
	assert( token.type( ) == E_SRDTokenType::WORD );
	assert( token.hasLocation( ) );

	const auto pnp( argNames_.indexOf( token.stringValue( ) ) );
	if ( pnp != -1 ) {
		return argLocations_[ pnp ];
	}
	argNames_.add( token.stringValue( ) );
	argLocations_.add( token.location( ) );
	return {};
}


/*= T_PipelineInstrNode ======================================================*/

T_PipelineInstrNode::T_PipelineInstrNode(
		T_InstrListNode& parent ,
		T_SRDToken const& idToken ) noexcept
	: A_InstructionNode( OP_PIPELINE , parent ) ,
		id_( idToken.stringValue( ) ) ,
		idLocation_( idToken.location( ) )
{ }

T_PipelineInstrNode::T_PipelineInstrNode(
		T_InstrListNode& parent ) noexcept
	: A_InstructionNode( OP_PIPELINE , parent ) ,
		id_( "*invalid*" ) ,
		idLocation_{ }
{ }

T_Optional< T_SRDLocation > T_PipelineInstrNode::addProgram(
		T_SRDToken const& pidToken ) noexcept
{
	T_String const& name( pidToken.stringValue( ) );
	const auto pIndex( pids_.indexOf( name ) );
	if ( pIndex != -1 ) {
		return pidLocations_[ pIndex ];
	}
	pids_.add( name );
	pidLocations_.add( pidToken.location( ) );
	return {};
}


/*= T_ProfileInstrNode =======================================================*/

T_ProfileInstrNode::T_ProfileInstrNode(
		T_InstrListNode& parent ,
		T_String const& text ) noexcept
	: A_InstructionNode( OP_PROFILE , parent ) ,
		text_( text ) ,
		instructions_( *this )
{ }


/*= T_ProgramInstrNode =======================================================*/

T_ProgramInstrNode::T_ProgramInstrNode(
		T_InstrListNode& parent ,
		T_SRDToken const& idToken ,
		T_SRDToken const& pathToken ) noexcept
	: A_InstructionNode( OP_PROGRAM , parent ) ,
		id_( idToken.stringValue( ) ) ,
		idLocation_( idToken.location( ) ) ,
		path_( pathToken.stringValue( ) ) ,
		pathLocation_( pathToken.location( ) )
{ }


/*= T_SetInstrNode ===========================================================*/

T_SetInstrNode::T_SetInstrNode(
		T_InstrListNode& parent ,
		T_SRDToken const& idToken ) noexcept
	: A_InstructionNode( OP_PROGRAM , parent ) ,
		id_( idToken.stringValue( ) ) ,
		idLocation_( idToken.location( ) )
{ }


/*= T_ConstantExprNode =======================================================*/

T_ConstantExprNode::T_ConstantExprNode(
		A_Node& parent ,
		T_SRDToken const& token ) noexcept
	: A_ExpressionNode( EXPR_CONST , parent ) ,
		wasFloat_( token.type( ) == E_SRDTokenType::FLOAT ) ,
		vFloat_( token.floatValue( ) ) ,
		vInt_( token.longValue( ) )
{
	location( ) = token.location( );
}

T_ConstantExprNode::T_ConstantExprNode(
		A_Node& parent ,
		double value ) noexcept
	 : A_ExpressionNode( EXPR_CONST , parent ) , wasFloat_( true ) ,
		vFloat_( value ) , vInt_( (int64_t) value )
{ }

T_ConstantExprNode::T_ConstantExprNode(
		A_Node& parent ,
		int64_t value ) noexcept
	 : A_ExpressionNode( EXPR_CONST , parent ) , wasFloat_( false ) ,
		vFloat_( value ) , vInt_( value )
{ }


/*= T_IdentifierExprNode =====================================================*/

T_IdentifierExprNode::T_IdentifierExprNode(
		A_Node& parent ,
		T_SRDToken const& token ) noexcept
	: T_IdentifierExprNode( parent , token.stringValue( ) )
{
	location( ) = token.location( );
}

T_IdentifierExprNode::T_IdentifierExprNode(
		A_Node& parent ,
		T_String const& id ) noexcept
	 : A_ExpressionNode( EXPR_ID , parent ) , id_( id )
{ }


/*= T_UnaryOperatorNode ======================================================*/

T_UnaryOperatorNode::T_UnaryOperatorNode(
		A_Node& parent ,
		const E_Operator op ) noexcept
	: A_ExpressionNode( ([op]() {
		switch ( op ) {
			case NEG: return EXPR_NEG;
			case INV: return EXPR_INV;
			case NOT: return EXPR_NOT;
			case SIN: return EXPR_SIN;
			case COS: return EXPR_COS;
			case TAN: return EXPR_TAN;
			case SQRT: return EXPR_SQRT;
			case EXP: return EXPR_EXP;
			case LN: return EXPR_LN;
		}
		std::abort( );
	})( ) , parent ) , op_( op )
{ }


/*= T_BinaryOperatorNode =====================================================*/

T_BinaryOperatorNode::T_BinaryOperatorNode(
		A_Node& parent ,
		const E_Operator op ) noexcept
	: A_ExpressionNode( ([op]() {
		switch ( op ) {
			case ADD: return EXPR_ADD;
			case SUB: return EXPR_SUB;
			case MUL: return EXPR_MUL;
			case DIV: return EXPR_DIV;
			case POW: return EXPR_POW;
			case CMP_EQ: return EXPR_CMP_EQ;
			case CMP_NE: return EXPR_CMP_NE;
			case CMP_GT: return EXPR_CMP_GT;
			case CMP_GE: return EXPR_CMP_GE;
			case CMP_LT: return EXPR_CMP_LT;
			case CMP_LE: return EXPR_CMP_LE;
		}
		std::abort( );
	})( ) , parent ) , op_( op )
{ }


/*= T_Parser =================================================================*/

namespace {

struct T_ParserImpl_
{
	enum class E_InstrType {
		IF ,
		PIPELINE ,
		PROFILE ,
		PROGRAM ,
		SET ,
	};

	const T_KeyValueTable< T_String , E_InstrType > instrMap{ ([]() {
		T_KeyValueTable< T_String , E_InstrType > temp{ 256 , 64 , 64 };
		const auto add{ [&temp]( char const* name , E_InstrType it ) {
			temp.add( T_String::Pooled( name ) , it );
		} };

		add( "if" , E_InstrType::IF );
		add( "pipeline" , E_InstrType::PIPELINE );
		add( "profiling" , E_InstrType::PROFILE );
		add( "program" , E_InstrType::PROGRAM );
		add( "set" , E_InstrType::SET );

		return temp;
	})( ) };

	const T_KeyValueTable< T_String , T_UnaryOperatorNode::E_Operator > unaryOpMap{ ([]() {
		T_KeyValueTable< T_String , T_UnaryOperatorNode::E_Operator > temp{ 64 , 32 , 32 };
		const auto add{ [&temp]( char const* name ,
				const T_UnaryOperatorNode::E_Operator it ) {
			temp.add( T_String::Pooled( name ) , it );
		} };

		add( "neg" , T_UnaryOperatorNode::NEG );
		add( "inv" , T_UnaryOperatorNode::INV );
		add( "not" , T_UnaryOperatorNode::NOT );
		add( "sin" , T_UnaryOperatorNode::SIN );
		add( "cos" , T_UnaryOperatorNode::COS );
		add( "tan" , T_UnaryOperatorNode::TAN );
		add( "sqrt" , T_UnaryOperatorNode::SQRT );
		add( "exp" , T_UnaryOperatorNode::EXP );
		add( "ln" , T_UnaryOperatorNode::LN );

		return temp;
	})( ) };

	const T_KeyValueTable< T_String , T_BinaryOperatorNode::E_Operator > binOpMap{ ([]() {
		T_KeyValueTable< T_String , T_BinaryOperatorNode::E_Operator > temp{ 64 , 32 , 32 };
		const auto add{ [&temp]( char const* name ,
				const T_BinaryOperatorNode::E_Operator it ) {
			temp.add( T_String::Pooled( name ) , it );
		} };

		add( "add" , T_BinaryOperatorNode::ADD );
		add( "sub" , T_BinaryOperatorNode::SUB );
		add( "mul" , T_BinaryOperatorNode::MUL );
		add( "div" , T_BinaryOperatorNode::DIV );
		add( "pow" , T_BinaryOperatorNode::POW );
		add( "cmp-eq" , T_BinaryOperatorNode::CMP_EQ );
		add( "cmp-ne" , T_BinaryOperatorNode::CMP_NE );
		add( "cmp-gt" , T_BinaryOperatorNode::CMP_GT );
		add( "cmp-ge" , T_BinaryOperatorNode::CMP_GE );
		add( "cmp-lt" , T_BinaryOperatorNode::CMP_LT );
		add( "cmp-le" , T_BinaryOperatorNode::CMP_LE );

		return temp;
	})( ) };

	// ---------------------------------------------------------------------

	T_OwnPtr< T_RootNode >& root;
	T_Array< T_SRDError >& errors;

	T_ParserImpl_( T_Array< T_SRDError >* errors ,
			T_OwnPtr< T_RootNode >* root ) noexcept;

	// ---------------------------------------------------------------------

	void parseFunction( T_SRDList const& funcList ) noexcept;
	void parseFunctionArguments(
			T_FuncNode& function ,
			T_SRDToken const& argsToken ) noexcept;

	// ---------------------------------------------------------------------

	void parseInstructions(
			T_InstrListNode& instructions ,
			T_SRDList const& input ,
			uint32_t start ) noexcept;
	P_InstrListNode parseBlock(
			A_Node& parent ,
			T_SRDToken const& block ) noexcept;

	// ---------------------------------------------------------------------

	void parseIfInstruction(
			T_InstrListNode& instructions ,
			T_SRDList const& input ) noexcept;
	void parsePipelineInstruction(
			T_InstrListNode& instructions ,
			T_SRDList const& input ) noexcept;
	void parseProfileInstruction(
			T_InstrListNode& instructions ,
			T_SRDList const& input ) noexcept;
	void parseProgramInstruction(
			T_InstrListNode& instructions ,
			T_SRDList const& input ) noexcept;
	void parseSetInstruction(
			T_InstrListNode& instructions ,
			T_SRDList const& input ) noexcept;

	// ---------------------------------------------------------------------

	P_ExpressionNode parseExpression(
			A_Node& parent ,
			T_SRDToken const& token ) noexcept;
	P_ExpressionNode parseOperation(
			A_Node& parent ,
			T_SRDList const& opList ) noexcept;
	P_ExpressionNode parseBinOp(
			A_Node& parent ,
			T_SRDList const& opList ,
			T_BinaryOperatorNode::E_Operator op ) noexcept;
	P_ExpressionNode parseUnaryOp(
			A_Node& parent ,
			T_SRDList const& opList ,
			T_UnaryOperatorNode::E_Operator op ) noexcept;
};

T_ParserImpl_::T_ParserImpl_(
		T_Array< T_SRDError >* const errors ,
		T_OwnPtr< T_RootNode >* const root ) noexcept
	: root( *root ) , errors( *errors )
{ }

void T_ParserImpl_::parseFunction(
		T_SRDList const& funcList ) noexcept
{
	assert( funcList.size( ) != 0 );

	auto const& fw( funcList[ 0 ] );
	if ( fw.type( ) != E_SRDTokenType::WORD
			|| ( fw.stringValue( ) != "init" && fw.stringValue( ) != "frame"
				&& fw.stringValue( ) != "fn" ) ) {
		errors.addNew( "init, frame or fn expected" , fw.location( ) );
		return;
	}

	T_String const& ftw( fw.stringValue( ) );
	T_OwnPtr< A_FuncNode > fn;
	if ( ftw == "fn" ) {
		if ( funcList.size( ) < 3 ) {
			errors.addNew( "function name and arguments expected" ,
					fw.location( ) );
			return;
		}
		if ( funcList[ 1 ].type( ) != E_SRDTokenType::WORD ) {
			errors.addNew( "function name expected" , funcList[ 1 ].location( ) );
			return;
		}

		fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root );
		parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) ,
				funcList[ 2 ] );
	} else {
		fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root );
	}
	fn->location( ) = fw.location( );

	const auto af( root->addFunction( fn ) );
	if ( af.dupLocation.present( ) ) {
		T_StringBuilder esb( "duplicate " );
		switch ( fn->type( ) ) {
			case A_Node::DECL_FN:
				esb << "function '" << fn->name( ) << "'";
				break;
			case A_Node::DECL_INIT:
				esb << "initialisation function";
				break;
			case A_Node::DECL_FRAME:
				esb << "frame function";
				break;
			default: std::abort( );
		}
		esb << "; previous declaration: " << *af.dupLocation.target( );
		errors.addNew( std::move( esb ) , fw.location( ) );
	}

	parseInstructions( af.function.instructions( ) , funcList , ftw == "fn" ? 3 : 1 );
}

void T_ParserImpl_::parseFunctionArguments(
		T_FuncNode& function ,
		T_SRDToken const& argsToken ) noexcept
{
	if ( argsToken.type( ) != E_SRDTokenType::LIST ) {
		errors.addNew( "arguments list expected" , argsToken.location( ) );
		return;
	}
	for ( auto const& token : argsToken.list( ) ) {
		if ( token.type( ) != E_SRDTokenType::WORD ) {
			errors.addNew( "argument name expected" , token.location( ) );
			continue;
		}

		const auto rv( function.addArgument( token ) );
		if ( rv.present( ) ) {
			T_StringBuilder esb;
			esb << "duplicate argument '" << token.stringValue( )
				<< "'; previous declaration: " << *rv.target( );
			errors.addNew( std::move( esb ) , token.location( ) );
		}
	}
}

/*----------------------------------------------------------------------------*/

void T_ParserImpl_::parseInstructions(
		T_InstrListNode& instructions ,
		T_SRDList const& input ,
		const uint32_t start ) noexcept
{
	for ( auto iter( input.begin( ) + start ) ; iter.valid( ) ; iter ++ ) {
		T_SRDToken const& itok( *iter );
		if ( itok.type( ) != E_SRDTokenType::LIST ) {
			errors.addNew( "instruction expected" , itok.location( ) );
			continue;
		}

		T_SRDList const& ilist( itok.list( ) );
		if ( ilist.empty( ) ) {
			errors.addNew( "instruction expected" , itok.location( ) );
			continue;
		}

		T_SRDToken const& iname( ilist[ 0 ] );
		if ( iname.type( ) != E_SRDTokenType::WORD ) {
			errors.addNew( "instruction name expected" , iname.location( ) );
			continue;
		}

		T_String const& iword( iname.stringValue( ) );
		if ( !instrMap.contains( iword ) ) {
			errors.addNew( "unknown instruction" , iname.location( ) );
			continue;
		}

		switch ( *instrMap.get( iword ) ) {
			case E_InstrType::IF:
				parseIfInstruction( instructions , ilist );
				break;
			case E_InstrType::PIPELINE:
				parsePipelineInstruction( instructions , ilist );
				break;
			case E_InstrType::PROFILE:
				parseProfileInstruction( instructions , ilist );
				break;
			case E_InstrType::PROGRAM:
				parseProgramInstruction( instructions , ilist );
				break;
			case E_InstrType::SET:
				parseSetInstruction( instructions , ilist );
				break;
		}
	}
}

P_InstrListNode T_ParserImpl_::parseBlock(
		A_Node& parent ,
		T_SRDToken const& block ) noexcept
{
	if ( block.type( ) != E_SRDTokenType::LIST ) {
		errors.addNew( "block expected" , block.location( ) );
		return {};
	}

	P_InstrListNode rv{ NewOwned< T_InstrListNode >( parent ) };
	rv->location( ) = block.location( );
	parseInstructions( *rv , block.list( ) , 0 );
	return rv;
}

/*----------------------------------------------------------------------------*/

void T_ParserImpl_::parseIfInstruction(
		T_InstrListNode& instructions ,
		T_SRDList const& input ) noexcept
{
	if ( input.size( ) == 1 ) {
		errors.addNew( "expression and 'then' block expected" ,
				input[ 0 ].location( ) );
		return;
	}

	T_CondInstrNode& cond{ instructions.add< T_CondInstrNode >( ) };
	cond.location( ) = input[ 0 ].location( );
	cond.setExpression( parseExpression( cond , input[ 1 ] ) );

	if ( input.size( ) == 2 ) {
		errors.addNew( "'then' block expected" ,
				input[ 0 ].location( ) );
		return;
	}

	cond.setCase( 1 , parseBlock( cond , input[ 2 ] ) );
	if ( input.size( ) > 3 ) {
		cond.setDefaultCase( parseBlock( cond , input[ 3 ] ) );
		if ( input.size( ) > 4 ) {
			errors.addNew( "too many arguments" , input[ 4 ].location( ) );
		}
	}
}

/*----------------------------------------------------------------------------*/

void T_ParserImpl_::parsePipelineInstruction(
		T_InstrListNode& instructions ,
		T_SRDList const& input ) noexcept
{
	if ( input.size( ) < 3 ) {
		errors.addNew( "identifier and program identifiers expected" ,
				input[ 0 ].location( ) );
		return;
	}

	const bool validId( input[ 1 ].type( ) == E_SRDTokenType::WORD );
	if ( !validId ) {
		errors.addNew( "pipeline identifier expected" , input[ 1 ].location( ) );
	}

	T_PipelineInstrNode& pipeline{ ([&instructions,&input,validId]( ) -> T_PipelineInstrNode& {
		if ( validId ) {
			return instructions.add< T_PipelineInstrNode >( input[ 0 ] );
		}
		return instructions.add< T_PipelineInstrNode >( );
	})() };
	pipeline.location( ) = input[ 0 ].location( );

	const auto nMax{ std::min( input.size( ) , 8u ) };
	for ( auto i = 2u ; i < nMax ; i ++ ) {
		T_SRDToken const& tok( input[ i ] );
		if ( tok.type( ) != E_SRDTokenType::WORD ) {
			errors.addNew( "program identifier expected" ,
					tok.location( ) );
			continue;
		}
		const auto dup( pipeline.addProgram( tok ) );
		if ( dup.present( ) ) {
			T_StringBuilder esb;
			esb << "duplicate program identifier; previous use: "
				<< *dup.target( );
			errors.addNew( std::move( esb ) , tok.location( ) );
		}
	}

	if ( input.size( ) > 8 ) {
		errors.addNew( "too many arguments" , input[ 8 ].location( ) );
	}
}

/*----------------------------------------------------------------------------*/

void T_ParserImpl_::parseProfileInstruction(
		T_InstrListNode& instructions ,
		T_SRDList const& input ) noexcept
{
	const bool hasEnough( input.size( ) < 2 );
	if ( hasEnough || !input[ 1 ].isText( ) ) {
		errors.addNew( "profiling section name expected" ,
				hasEnough ? input[ 1 ].location( ) : T_SRDLocation{} );
		if ( !hasEnough ) {
			return;
		}
	}

	const T_String text( input[ 1 ].isText( ) ? input[ 1 ].stringValue( ) : "*invalid*" );
	T_ProfileInstrNode& profile{ instructions.add< T_ProfileInstrNode >( text ) };
	profile.location( ) = input[ 0 ].location( );
	parseInstructions( profile.instructions( ) , input , 2 );
}

/*----------------------------------------------------------------------------*/

void T_ParserImpl_::parseProgramInstruction(
		T_InstrListNode& instructions ,
		T_SRDList const& input ) noexcept
{
	bool ok{ true };
	if ( input.size( ) == 1 ) {
		errors.addNew( "identifier and program name required" ,
				input[ 0 ].location( ) );
		return;
	}
	if ( input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
		errors.addNew( "identifier (word) expected" , input[ 1 ].location( ) );
		ok = false;
	}
	if ( input.size( ) == 2 ) {
		errors.addNew( "program name required" , input[ 0 ].location( ) );
		return;
	}
	if ( !input[ 2 ].isText( ) ) {
		errors.addNew( "program name (string or word) expected" ,
				input[ 2 ].location( ) );
		ok = false;
	}

	if ( input.size( ) > 3 ) {
		errors.addNew( "too many arguments" , input[ 3 ].location( ) );
	}
	if ( !ok ) {
		return;
	}

	T_ProgramInstrNode& program{ instructions.add< T_ProgramInstrNode >(
				input[ 1 ] , input[ 2 ] ) };
	program.location( ) = input[ 0 ].location( );
}

/*----------------------------------------------------------------------------*/

void T_ParserImpl_::parseSetInstruction(
		T_InstrListNode& instructions ,
		T_SRDList const& input ) noexcept
{
	bool ok{ true };
	if ( input.size( ) == 1 ) {
		errors.addNew( "identifier and expression required" ,
				input[ 0 ].location( ) );
		return;
	}
	if ( input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
		errors.addNew( "variable identifier expected" , input[ 1 ].location( ) );
		ok = false;
	}
	if ( input.size( ) == 2 ) {
		errors.addNew( "expression required" , input[ 0 ].location( ) );
	}
	if ( input.size( ) > 3 ) {
		errors.addNew( "too many arguments" , input[ 3 ].location( ) );
	}
	if ( !ok ) {
		return;
	}

	T_SetInstrNode& set{ instructions.add< T_SetInstrNode >( input[ 1 ] ) };
	set.location( ) = input[ 0 ].location( );
	if ( input.size( ) > 2 ) {
		auto expr( parseExpression( set , input[ 2 ] ) );
		if ( expr ) {
			set.setExpression( std::move( expr ) );
		}
	}
}

/*----------------------------------------------------------------------------*/

P_ExpressionNode T_ParserImpl_::parseExpression(
		A_Node& parent ,
		T_SRDToken const& token ) noexcept
{
	if ( token.isNumeric( ) ) {
		return NewOwned< T_ConstantExprNode >( parent , token );
	}
	if ( token.type( ) == E_SRDTokenType::WORD || token.type( ) == E_SRDTokenType::VAR ) {
		return NewOwned< T_IdentifierExprNode >( parent , token );
	}

	if ( token.type( ) == E_SRDTokenType::LIST && !token.list( ).empty( ) ) {
		return parseOperation( parent , token.list( ) );
	}

	errors.addNew( "invalid expression" , token.location( ) );
	return {};
}

P_ExpressionNode T_ParserImpl_::parseOperation(
		A_Node& parent ,
		T_SRDList const& opList ) noexcept
{
	T_SRDToken const& opId( opList[ 0 ] );
	if ( opId.type( ) != E_SRDTokenType::WORD ) {
		errors.addNew( "operator expected" , opId.location( ) );
		return {};
	}

	if ( binOpMap.contains( opId.stringValue( ) ) ) {
		return parseBinOp( parent , opList ,
				*binOpMap.get( opId.stringValue( ) ) );
	}
	if ( unaryOpMap.contains( opId.stringValue( ) ) ) {
		return parseUnaryOp( parent , opList ,
				*unaryOpMap.get( opId.stringValue( ) ) );
	}

	errors.addNew( "unknown operator" , opId.location( ) );
	return {};
}

P_ExpressionNode T_ParserImpl_::parseBinOp(
		A_Node& parent ,
		T_SRDList const& opList ,
		T_BinaryOperatorNode::E_Operator op ) noexcept
{
	if ( opList.size( ) < 3 ) {
		errors.addNew( "not enough arguments" , opList[ 0 ].location( ) );
	} else if ( opList.size( ) > 3 ) {
		errors.addNew( "too many arguments" , opList[ 3 ].location( ) );
	}

	T_OwnPtr< T_BinaryOperatorNode > opNode{
		NewOwned< T_BinaryOperatorNode >( parent , op ) };
	opNode->location( ) = opList[ 0 ].location( );

	if ( opList.size( ) > 1 ) {
		auto left{ parseExpression( *opNode , opList[ 1 ] ) };
		if ( left ) {
			opNode->setLeft( std::move( left ) );
		}
	}
	if ( opList.size( ) > 2 ) {
		auto right{ parseExpression( *opNode , opList[ 2 ] ) };
		if ( right ) {
			opNode->setRight( std::move( right ) );
		}
	}

	return opNode;
}

P_ExpressionNode T_ParserImpl_::parseUnaryOp(
		A_Node& parent ,
		T_SRDList const& opList ,
		T_UnaryOperatorNode::E_Operator op ) noexcept
{
	if ( opList.size( ) < 2 ) {
		errors.addNew( "not enough arguments" , opList[ 0 ].location( ) );
	} else if ( opList.size( ) > 2 ) {
		errors.addNew( "too many arguments" , opList[ 2 ].location( ) );
	}

	T_OwnPtr< T_UnaryOperatorNode > opNode{
		NewOwned< T_UnaryOperatorNode >( parent , op ) };
	opNode->location( ) = opList[ 0 ].location( );

	if ( opList.size( ) > 1 ) {
		auto argument{ parseExpression( *opNode , opList[ 1 ] ) };
		if ( argument ) {
			opNode->setArgument( std::move( argument ) );
		}
	}

	return opNode;
}

} // namespace

/*----------------------------------------------------------------------------*/

T_Parser::T_Parser( ) noexcept
	: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) ,
		errors_( 64 ) , rootNode_{}
{}

bool T_Parser::parse(
		T_SRDList const& input ) noexcept
{
	errors_.clear( );
	rootNode_ = NewOwned< T_RootNode >( );

	for ( auto const& t : input ) {
		if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
			p< T_ParserImpl_ >( ).parseFunction( t.list( ) );
		} else {
			errors_.addNew( "function, init or frame list expected" ,
					t.location( ) );
		}
	}

	if ( errors_.empty( ) ) {
		T_SRDLocation loc( ([&input]() {
			if ( input.size( ) != 0 ) {
				return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
			}
			return T_SRDLocation{};
		})( ));
		if ( !rootNode_->hasInit( ) ) {
			errors_.addNew( "no initialisation block" , loc );
		}
		if ( !rootNode_->hasFrame( ) ) {
			errors_.addNew( "no initialisation block" , loc );
		}
	}

	return errors_.empty( );
}