#ifndef EMPATHY_IR_DECOMPOSE_INL
#define EMPATHY_IR_DECOMPOSE_INL
namespace empathy::ir
{
template< typename T >
auto Lit( T&& val )
{
return LiteralSpec< T >( forward< T >( val ) );
}
template< typename... S >
auto Vec( S&&... specs )
{
return VectorSpec< S... >( forward< S >( specs )... );
}
template< typename T >
bool Decompose( const Term& t, const LiteralSpec< T >& spec )
{
const auto* pContent = get_if< T >( &t.content() );
if( !pContent )
return false;
return *pContent == spec.m_val;
}
template< typename T >
optional< reference_wrapper< const T > > Decompose( const Term& t, const Val< T >& spec )
{
const auto* pContent = get_if< T >( &t.content() );
if( !pContent )
return nullopt;
return *pContent;
}
const Term& Decompose( const Term& t, const SubTerm& spec )
{
return t;
}
template< size_t IR, size_t IS, typename... S, typename... T >
bool Decompose( const Vector::container_type< Term >& terms, const tuple< S... >& specs, tuple< T... >& result )
{
auto res = Decompose( terms[IS], get< IS >( specs ) );
if constexpr( is_same_v< decltype( res ), bool > )
{
if( !res )
return false;
// For specs that are just predicates, we have no result to insert
// so don't increment the result index.
if constexpr( IS < ( sizeof... ( S ) - 1 ) )
return Decompose< IR, IS + 1 >( terms, specs, result );
else
return true;
}
else if constexpr( is_optional_v< decltype( res ) > )
{
if( !res )
return false;
get< IR >( result ) = move( *res );
if constexpr( IS < ( sizeof... ( S ) - 1 ) )
return Decompose< IR + 1, IS + 1 >( terms, specs, result );
else
return true;
}
else
{
get< IR >( result ) = move( res );
if constexpr( IS < ( sizeof... ( S ) - 1 ) )
Decompose< IR + 1, IS + 1 >( terms, specs, result );
return true;
}
}
template< typename... S >
optional< typename VectorSpec< S... >::return_type > Decompose( const Term& t, const VectorSpec< S... >& spec )
{
const auto* ppVec = get_if< pvec >( &t.content() );
if( !ppVec )
return nullopt;
const auto& terms = ( *ppVec )->terms();
if( terms.size() != sizeof...( S ) )
return nullopt;
typename VectorSpec< S... >::return_type result;
if( !Decompose< 0, 0 >( terms, spec.m_specs, result ) )
return nullopt;
return result;
}
}
#endif