#ifndef EMPATHY_IR_VECTOR_H
#define EMPATHY_IR_VECTOR_H
namespace empathy::ir
{
struct Repetition
{
Repetition( Term&& t ) :
m_term( move( t ) )
{}
Term m_term;
};
class VecLength
{
public:
VecLength( size_t length, bool isVariable ) :
m_length( length ),
m_isVariable( isVariable )
{}
auto minLength() const { return m_length; }
auto isVariable() const { return m_isVariable; }
private:
size_t m_length = 0;
bool m_isVariable = false;
};
class Vector
{
public:
Vector() {}
template< typename T >
using container_type = immer::flex_vector< T >;
const auto& terms() const { return m_terms; }
template< typename... T >
static auto Make( T&&... terms )
{
immer::vector< Term > v;
auto vt = v.transient();
bool faulty = Append( vt, forward< T >( terms )... );
auto pVec = make_shared< Vector >( vt.persistent(), faulty );
pVec->setupRepetition( forward< T >( terms )... );
return pVec;
}
template< typename... T >
static Vector MakeAppend( const Vector& vec, T&&... terms )
{
auto v = vec.m_terms;
auto vt = v.transient();
bool faulty = Append( vt, forward< T >( terms )... );
Vector newVec( vt.persistent(), vec.m_faulty || faulty );
newVec.setupRepetition( forward< T >( terms )... );
return newVec;
}
template< typename... T >
static Vector MakeConcat( const Vector& vec1, const Vector& vec2 )
{
auto v = vec1.m_terms;
auto vt = v.transient();
for( auto&& t : vec2.terms() )
Append( vt, t );
return Vector( vt.persistent(), vec1.m_faulty || vec2.m_faulty );
}
friend ostream& ToString( ostream& out, const pvec& v );
template< typename V >
Vector( V&& terms, bool faulty ) :
m_terms( forward< V >( terms ) ),
m_faulty( faulty )
{}
template< typename V >
Vector( V&& terms, Term&& repetition, bool faulty ) :
m_terms( forward< V >( terms ) ),
m_repetitionTerm( move( repetition ) ),
m_faulty( faulty )
{}
bool isFaulty() const { return m_faulty; }
auto length() const { return VecLength( m_terms.size(), !!m_repetitionTerm ); }
const auto& repetitionTerm() const { return m_repetitionTerm; }
bool empty() const { return m_terms.empty(); }
Generator< reference_wrapper< const Term > > forEachTerm() const
{
for( auto&& t : terms() )
co_yield t;
if( m_repetitionTerm )
co_yield *m_repetitionTerm;
}
template< typename F >
pvec transform( F&& func ) const
{
immer::vector< Term > v;
auto vt = v.transient();
for( auto&& x : m_terms )
{
auto newX = func( x );
if( !newX )
return nullptr;
vt.push_back( move( *newX ) );
}
return make_shared< Vector >( vt.persistent(), m_faulty );
}
Vector take( size_t n ) const
{
return Vector( m_terms.take( n ), m_faulty );
}
Vector drop( size_t n ) const
{
return Vector( m_terms.drop( n ), m_faulty );
}
template< typename T >
void setRepetitionTerm( T&& term )
{
m_repetitionTerm = forward< T >( term );
}
bool operator==( const Vector& rhs ) const;
const auto& operator[]( size_t i ) const
{
return m_terms[i];
}
private:
template< typename V, typename H, typename... T >
static bool Append( V& vt, H&& head, T&&... tail )
{
bool faulty1 = Append( vt, forward< H >( head ) );
bool faulty2 = Append( vt, forward< T >( tail )... );
return faulty1 || faulty2;
}
template< typename V, typename H >
static bool Append( V& vt, H&& head )
{
if constexpr( !is_same_v< Repetition, remove_constref_t< H > > )
{
bool faulty = head.isFaulty();
vt.push_back( forward< H >( head ) );
return faulty;
}
else
return false;
}
template< typename V >
static bool Append( V& vt )
{
return false;
}
template< typename H, typename... T >
void setupRepetition( H&& head, T&&... tail )
{
if constexpr( is_same_v< Repetition, remove_constref_t< H > > )
setupRepetition( forward< H >( head ) );
else
setupRepetition( forward< T >( tail )... );
}
template< typename H >
void setupRepetition( H&& head )
{
if constexpr( is_same_v< Repetition, remove_constref_t< H > > )
m_repetitionTerm = forward< H >( head ).m_term;
}
void setupRepetition()
{}
container_type< Term > m_terms;
optional< Term > m_repetitionTerm;
bool m_faulty = false;
};
}
#endif