mirror of https://github.com/procxx/kepka.git
				
				
				
			
		
			
				
	
	
		
			1233 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1233 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
| // v1.0-develop.2
 | |
| // See https://github.com/philsquared/Clara
 | |
| 
 | |
| #ifndef CATCH_CLARA_HPP_INCLUDED
 | |
| #define CATCH_CLARA_HPP_INCLUDED
 | |
| 
 | |
| #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
 | |
| #define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
 | |
| #endif
 | |
| 
 | |
| #ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
 | |
| #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
 | |
| #endif
 | |
| 
 | |
| // ----------- #included from clara_textflow.hpp -----------
 | |
| 
 | |
| // TextFlowCpp
 | |
| //
 | |
| // A single-header library for wrapping and laying out basic text, by Phil Nash
 | |
| //
 | |
| // This work is licensed under the BSD 2-Clause license.
 | |
| // See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause
 | |
| //
 | |
| // This project is hosted at https://github.com/philsquared/textflowcpp
 | |
| 
 | |
| #ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
 | |
| #define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
 | |
| 
 | |
| #include <cassert>
 | |
| #include <ostream>
 | |
| #include <sstream>
 | |
| #include <vector>
 | |
| 
 | |
| #ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
 | |
| #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
 | |
| #endif
 | |
| 
 | |
| 
 | |
| namespace Catch { namespace clara { namespace TextFlow {
 | |
| 
 | |
|     inline auto isWhitespace( char c ) -> bool {
 | |
|         static std::string chars = " \t\n\r";
 | |
|         return chars.find( c ) != std::string::npos;
 | |
|     }
 | |
|     inline auto isBreakableBefore( char c ) -> bool {
 | |
|         static std::string chars = "[({<|";
 | |
|         return chars.find( c ) != std::string::npos;
 | |
|     }
 | |
|     inline auto isBreakableAfter( char c ) -> bool {
 | |
|         static std::string chars = "])}>.,:;*+-=&/\\";
 | |
|         return chars.find( c ) != std::string::npos;
 | |
|     }
 | |
| 
 | |
|     class Columns;
 | |
| 
 | |
|     class Column {
 | |
|         std::vector<std::string> m_strings;
 | |
|         size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
 | |
|         size_t m_indent = 0;
 | |
|         size_t m_initialIndent = std::string::npos;
 | |
| 
 | |
|     public:
 | |
|         class iterator {
 | |
|             friend Column;
 | |
| 
 | |
|             Column const& m_column;
 | |
|             size_t m_stringIndex = 0;
 | |
|             size_t m_pos = 0;
 | |
| 
 | |
|             size_t m_len = 0;
 | |
|             size_t m_end = 0;
 | |
|             bool m_suffix = false;
 | |
| 
 | |
|             iterator( Column const& column, size_t stringIndex )
 | |
|             :   m_column( column ),
 | |
|                 m_stringIndex( stringIndex )
 | |
|             {}
 | |
| 
 | |
|             auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
 | |
| 
 | |
|             auto isBoundary( size_t at ) const -> bool {
 | |
|                 assert( at > 0 );
 | |
|                 assert( at <= line().size() );
 | |
| 
 | |
|                 return at == line().size() ||
 | |
|                        ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) ||
 | |
|                        isBreakableBefore( line()[at] ) ||
 | |
|                        isBreakableAfter( line()[at-1] );
 | |
|             }
 | |
| 
 | |
|             void calcLength() {
 | |
|                 assert( m_stringIndex < m_column.m_strings.size() );
 | |
| 
 | |
|                 m_suffix = false;
 | |
|                 auto width = m_column.m_width-indent();
 | |
|                 m_end = m_pos;
 | |
|                 while( m_end < line().size() && line()[m_end] != '\n' )
 | |
|                     ++m_end;
 | |
| 
 | |
|                 if( m_end < m_pos + width ) {
 | |
|                     m_len = m_end - m_pos;
 | |
|                 }
 | |
|                 else {
 | |
|                     size_t len = width;
 | |
|                     while (len > 0 && !isBoundary(m_pos + len))
 | |
|                         --len;
 | |
|                     while (len > 0 && isWhitespace( line()[m_pos + len - 1] ))
 | |
|                         --len;
 | |
| 
 | |
|                     if (len > 0) {
 | |
|                         m_len = len;
 | |
|                     } else {
 | |
|                         m_suffix = true;
 | |
|                         m_len = width - 1;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             auto indent() const -> size_t {
 | |
|                 auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
 | |
|                 return initial == std::string::npos ? m_column.m_indent : initial;
 | |
|             }
 | |
| 
 | |
|             auto addIndentAndSuffix(std::string const &plain) const -> std::string {
 | |
|                 return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain);
 | |
|             }
 | |
| 
 | |
|         public:
 | |
|             explicit iterator( Column const& column ) : m_column( column ) {
 | |
|                 assert( m_column.m_width > m_column.m_indent );
 | |
|                 assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent );
 | |
|                 calcLength();
 | |
|                 if( m_len == 0 )
 | |
|                     m_stringIndex++; // Empty string
 | |
|             }
 | |
| 
 | |
|             auto operator *() const -> std::string {
 | |
|                 assert( m_stringIndex < m_column.m_strings.size() );
 | |
|                 assert( m_pos <= m_end );
 | |
|                 if( m_pos + m_column.m_width < m_end )
 | |
|                     return addIndentAndSuffix(line().substr(m_pos, m_len));
 | |
|                 else
 | |
|                     return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos));
 | |
|             }
 | |
| 
 | |
|             auto operator ++() -> iterator& {
 | |
|                 m_pos += m_len;
 | |
|                 if( m_pos < line().size() && line()[m_pos] == '\n' )
 | |
|                     m_pos += 1;
 | |
|                 else
 | |
|                     while( m_pos < line().size() && isWhitespace( line()[m_pos] ) )
 | |
|                         ++m_pos;
 | |
| 
 | |
|                 if( m_pos == line().size() ) {
 | |
|                     m_pos = 0;
 | |
|                     ++m_stringIndex;
 | |
|                 }
 | |
|                 if( m_stringIndex < m_column.m_strings.size() )
 | |
|                     calcLength();
 | |
|                 return *this;
 | |
|             }
 | |
|             auto operator ++(int) -> iterator {
 | |
|                 iterator prev( *this );
 | |
|                 operator++();
 | |
|                 return prev;
 | |
|             }
 | |
| 
 | |
|             auto operator ==( iterator const& other ) const -> bool {
 | |
|                 return
 | |
|                     m_pos == other.m_pos &&
 | |
|                     m_stringIndex == other.m_stringIndex &&
 | |
|                     &m_column == &other.m_column;
 | |
|             }
 | |
|             auto operator !=( iterator const& other ) const -> bool {
 | |
|                 return !operator==( other );
 | |
|             }
 | |
|         };
 | |
|         using const_iterator = iterator;
 | |
| 
 | |
|         explicit Column( std::string const& text ) { m_strings.push_back( text ); }
 | |
| 
 | |
|         auto width( size_t newWidth ) -> Column& {
 | |
|             assert( newWidth > 0 );
 | |
|             m_width = newWidth;
 | |
|             return *this;
 | |
|         }
 | |
|         auto indent( size_t newIndent ) -> Column& {
 | |
|             m_indent = newIndent;
 | |
|             return *this;
 | |
|         }
 | |
|         auto initialIndent( size_t newIndent ) -> Column& {
 | |
|             m_initialIndent = newIndent;
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         auto width() const -> size_t { return m_width; }
 | |
|         auto begin() const -> iterator { return iterator( *this ); }
 | |
|         auto end() const -> iterator { return { *this, m_strings.size() }; }
 | |
| 
 | |
|         inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) {
 | |
|             bool first = true;
 | |
|             for( auto line : col ) {
 | |
|                 if( first )
 | |
|                     first = false;
 | |
|                 else
 | |
|                     os << "\n";
 | |
|                 os <<  line;
 | |
|             }
 | |
|             return os;
 | |
|         }
 | |
| 
 | |
|         auto operator + ( Column const& other ) -> Columns;
 | |
| 
 | |
|         auto toString() const -> std::string {
 | |
|             std::ostringstream oss;
 | |
|             oss << *this;
 | |
|             return oss.str();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     class Spacer : public Column {
 | |
| 
 | |
|     public:
 | |
|         explicit Spacer( size_t spaceWidth ) : Column( "" ) {
 | |
|             width( spaceWidth );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     class Columns {
 | |
|         std::vector<Column> m_columns;
 | |
| 
 | |
|     public:
 | |
| 
 | |
|         class iterator {
 | |
|             friend Columns;
 | |
|             struct EndTag {};
 | |
| 
 | |
|             std::vector<Column> const& m_columns;
 | |
|             std::vector<Column::iterator> m_iterators;
 | |
|             size_t m_activeIterators;
 | |
| 
 | |
|             iterator( Columns const& columns, EndTag )
 | |
|             :   m_columns( columns.m_columns ),
 | |
|                 m_activeIterators( 0 )
 | |
|             {
 | |
|                 m_iterators.reserve( m_columns.size() );
 | |
| 
 | |
|                 for( auto const& col : m_columns )
 | |
|                     m_iterators.push_back( col.end() );
 | |
|             }
 | |
| 
 | |
|         public:
 | |
|             explicit iterator( Columns const& columns )
 | |
|             :   m_columns( columns.m_columns ),
 | |
|                 m_activeIterators( m_columns.size() )
 | |
|             {
 | |
|                 m_iterators.reserve( m_columns.size() );
 | |
| 
 | |
|                 for( auto const& col : m_columns )
 | |
|                     m_iterators.push_back( col.begin() );
 | |
|             }
 | |
| 
 | |
|             auto operator ==( iterator const& other ) const -> bool {
 | |
|                 return m_iterators == other.m_iterators;
 | |
|             }
 | |
|             auto operator !=( iterator const& other ) const -> bool {
 | |
|                 return m_iterators != other.m_iterators;
 | |
|             }
 | |
|             auto operator *() const -> std::string {
 | |
|                 std::string row, padding;
 | |
| 
 | |
|                 for( size_t i = 0; i < m_columns.size(); ++i ) {
 | |
|                     auto width = m_columns[i].width();
 | |
|                     if( m_iterators[i] != m_columns[i].end() ) {
 | |
|                         std::string col = *m_iterators[i];
 | |
|                         row += padding + col;
 | |
|                         if( col.size() < width )
 | |
|                             padding = std::string( width - col.size(), ' ' );
 | |
|                         else
 | |
|                             padding = "";
 | |
|                     }
 | |
|                     else {
 | |
|                         padding += std::string( width, ' ' );
 | |
|                     }
 | |
|                 }
 | |
|                 return row;
 | |
|             }
 | |
|             auto operator ++() -> iterator& {
 | |
|                 for( size_t i = 0; i < m_columns.size(); ++i ) {
 | |
|                     if (m_iterators[i] != m_columns[i].end())
 | |
|                         ++m_iterators[i];
 | |
|                 }
 | |
|                 return *this;
 | |
|             }
 | |
|             auto operator ++(int) -> iterator {
 | |
|                 iterator prev( *this );
 | |
|                 operator++();
 | |
|                 return prev;
 | |
|             }
 | |
|         };
 | |
|         using const_iterator = iterator;
 | |
| 
 | |
|         auto begin() const -> iterator { return iterator( *this ); }
 | |
|         auto end() const -> iterator { return { *this, iterator::EndTag() }; }
 | |
| 
 | |
|         auto operator += ( Column const& col ) -> Columns& {
 | |
|             m_columns.push_back( col );
 | |
|             return *this;
 | |
|         }
 | |
|         auto operator + ( Column const& col ) -> Columns {
 | |
|             Columns combined = *this;
 | |
|             combined += col;
 | |
|             return combined;
 | |
|         }
 | |
| 
 | |
|         inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) {
 | |
| 
 | |
|             bool first = true;
 | |
|             for( auto line : cols ) {
 | |
|                 if( first )
 | |
|                     first = false;
 | |
|                 else
 | |
|                     os << "\n";
 | |
|                 os << line;
 | |
|             }
 | |
|             return os;
 | |
|         }
 | |
| 
 | |
|         auto toString() const -> std::string {
 | |
|             std::ostringstream oss;
 | |
|             oss << *this;
 | |
|             return oss.str();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     inline auto Column::operator + ( Column const& other ) -> Columns {
 | |
|         Columns cols;
 | |
|         cols += *this;
 | |
|         cols += other;
 | |
|         return cols;
 | |
|     }
 | |
| }}} // namespace Catch::clara::TextFlow
 | |
| 
 | |
| #endif // CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
 | |
| 
 | |
| // ----------- end of #include from clara_textflow.hpp -----------
 | |
| // ........... back in clara.hpp
 | |
| 
 | |
| 
 | |
| #include <memory>
 | |
| #include <set>
 | |
| #include <algorithm>
 | |
| 
 | |
| #if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
 | |
| #define CATCH_PLATFORM_WINDOWS
 | |
| #endif
 | |
| 
 | |
| namespace Catch { namespace clara {
 | |
| namespace detail {
 | |
| 
 | |
|     // Traits for extracting arg and return type of lambdas (for single argument lambdas)
 | |
|     template<typename L>
 | |
|     struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
 | |
| 
 | |
|     template<typename ClassT, typename ReturnT, typename... Args>
 | |
|     struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
 | |
|         static const bool isValid = false;
 | |
|     };
 | |
| 
 | |
|     template<typename ClassT, typename ReturnT, typename ArgT>
 | |
|     struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
 | |
|         static const bool isValid = true;
 | |
|         using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;;
 | |
|         using ReturnType = ReturnT;
 | |
|     };
 | |
| 
 | |
|     class TokenStream;
 | |
| 
 | |
|     // Transport for raw args (copied from main args, or supplied via init list for testing)
 | |
|     class Args {
 | |
|         friend TokenStream;
 | |
|         std::string m_exeName;
 | |
|         std::vector<std::string> m_args;
 | |
| 
 | |
|     public:
 | |
|         Args( int argc, char *argv[] ) {
 | |
|             m_exeName = argv[0];
 | |
|             for( int i = 1; i < argc; ++i )
 | |
|                 m_args.push_back( argv[i] );
 | |
|         }
 | |
| 
 | |
|         Args( std::initializer_list<std::string> args )
 | |
|         :   m_exeName( *args.begin() ),
 | |
|             m_args( args.begin()+1, args.end() )
 | |
|         {}
 | |
| 
 | |
|         auto exeName() const -> std::string {
 | |
|             return m_exeName;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
 | |
|     // may encode an option + its argument if the : or = form is used
 | |
|     enum class TokenType {
 | |
|         Option, Argument
 | |
|     };
 | |
|     struct Token {
 | |
|         TokenType type;
 | |
|         std::string token;
 | |
|     };
 | |
| 
 | |
|     inline auto isOptPrefix( char c ) -> bool {
 | |
|         return c == '-'
 | |
| #ifdef CATCH_PLATFORM_WINDOWS
 | |
|             || c == '/'
 | |
| #endif
 | |
|         ;
 | |
|     }
 | |
| 
 | |
|     // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
 | |
|     class TokenStream {
 | |
|         using Iterator = std::vector<std::string>::const_iterator;
 | |
|         Iterator it;
 | |
|         Iterator itEnd;
 | |
|         std::vector<Token> m_tokenBuffer;
 | |
| 
 | |
|         void loadBuffer() {
 | |
|             m_tokenBuffer.resize( 0 );
 | |
| 
 | |
|             // Skip any empty strings
 | |
|             while( it != itEnd && it->empty() )
 | |
|                 ++it;
 | |
| 
 | |
|             if( it != itEnd ) {
 | |
|                 auto const &next = *it;
 | |
|                 if( isOptPrefix( next[0] ) ) {
 | |
|                     auto delimiterPos = next.find_first_of( " :=" );
 | |
|                     if( delimiterPos != std::string::npos ) {
 | |
|                         m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
 | |
|                         m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
 | |
|                     } else {
 | |
|                         if( next[1] != '-' && next.size() > 2 ) {
 | |
|                             std::string opt = "- ";
 | |
|                             for( size_t i = 1; i < next.size(); ++i ) {
 | |
|                                 opt[1] = next[i];
 | |
|                                 m_tokenBuffer.push_back( { TokenType::Option, opt } );
 | |
|                             }
 | |
|                         } else {
 | |
|                             m_tokenBuffer.push_back( { TokenType::Option, next } );
 | |
|                         }
 | |
|                     }
 | |
|                 } else {
 | |
|                     m_tokenBuffer.push_back( { TokenType::Argument, next } );
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     public:
 | |
|         explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
 | |
| 
 | |
|         TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
 | |
|             loadBuffer();
 | |
|         }
 | |
| 
 | |
|         explicit operator bool() const {
 | |
|             return !m_tokenBuffer.empty() || it != itEnd;
 | |
|         }
 | |
| 
 | |
|         auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
 | |
| 
 | |
|         auto operator*() const -> Token {
 | |
|             assert( !m_tokenBuffer.empty() );
 | |
|             return m_tokenBuffer.front();
 | |
|         }
 | |
| 
 | |
|         auto operator->() const -> Token const * {
 | |
|             assert( !m_tokenBuffer.empty() );
 | |
|             return &m_tokenBuffer.front();
 | |
|         }
 | |
| 
 | |
|         auto operator++() -> TokenStream & {
 | |
|             if( m_tokenBuffer.size() >= 2 ) {
 | |
|                 m_tokenBuffer.erase( m_tokenBuffer.begin() );
 | |
|             } else {
 | |
|                 if( it != itEnd )
 | |
|                     ++it;
 | |
|                 loadBuffer();
 | |
|             }
 | |
|             return *this;
 | |
|         }
 | |
|     };
 | |
| 
 | |
| 
 | |
|     class ResultBase {
 | |
|     public:
 | |
|         enum Type {
 | |
|             Ok, LogicError, RuntimeError
 | |
|         };
 | |
| 
 | |
|     protected:
 | |
|         ResultBase( Type type ) : m_type( type ) {}
 | |
|         virtual ~ResultBase() = default;
 | |
| 
 | |
|         virtual void enforceOk() const = 0;
 | |
| 
 | |
|         Type m_type;
 | |
|     };
 | |
| 
 | |
|     template<typename T>
 | |
|     class ResultValueBase : public ResultBase {
 | |
|     public:
 | |
|         auto value() const -> T const & {
 | |
|             enforceOk();
 | |
|             return m_value;
 | |
|         }
 | |
| 
 | |
|     protected:
 | |
|         ResultValueBase( Type type ) : ResultBase( type ) {}
 | |
| 
 | |
|         ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
 | |
|             if( m_type == ResultBase::Ok )
 | |
|                 new( &m_value ) T( other.m_value );
 | |
|         }
 | |
| 
 | |
|         ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
 | |
|             new( &m_value ) T( value );
 | |
|         }
 | |
| 
 | |
|         auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
 | |
|             if( m_type == ResultBase::Ok )
 | |
|                 m_value.~T();
 | |
|             ResultBase::operator=(other);
 | |
|             if( m_type == ResultBase::Ok )
 | |
|                 new( &m_value ) T( other.m_value );
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         ~ResultValueBase() {
 | |
|             if( m_type == Ok )
 | |
|                 m_value.~T();
 | |
|         }
 | |
| 
 | |
|         union {
 | |
|             T m_value;
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     template<>
 | |
|     class ResultValueBase<void> : public ResultBase {
 | |
|     protected:
 | |
|         using ResultBase::ResultBase;
 | |
|     };
 | |
| 
 | |
|     template<typename T = void>
 | |
|     class BasicResult : public ResultValueBase<T> {
 | |
|     public:
 | |
|         template<typename U>
 | |
|         explicit BasicResult( BasicResult<U> const &other )
 | |
|         :   ResultValueBase<T>( other.type() ),
 | |
|             m_errorMessage( other.errorMessage() )
 | |
|         {
 | |
|             assert( type() != ResultBase::Ok );
 | |
|         }
 | |
| 
 | |
|         template<typename U>
 | |
|         static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
 | |
|         static auto ok() -> BasicResult { return { ResultBase::Ok }; }
 | |
|         static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
 | |
|         static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
 | |
| 
 | |
|         explicit operator bool() const { return m_type == ResultBase::Ok; }
 | |
|         auto type() const -> ResultBase::Type { return m_type; }
 | |
|         auto errorMessage() const -> std::string { return m_errorMessage; }
 | |
| 
 | |
|     protected:
 | |
|         virtual void enforceOk() const {
 | |
|             // !TBD: If no exceptions, std::terminate here or something
 | |
|             switch( m_type ) {
 | |
|                 case ResultBase::LogicError:
 | |
|                     throw std::logic_error( m_errorMessage );
 | |
|                 case ResultBase::RuntimeError:
 | |
|                     throw std::runtime_error( m_errorMessage );
 | |
|                 case ResultBase::Ok:
 | |
|                     break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         std::string m_errorMessage; // Only populated if resultType is an error
 | |
| 
 | |
|         BasicResult( ResultBase::Type type, std::string const &message )
 | |
|         :   ResultValueBase<T>(type),
 | |
|             m_errorMessage(message)
 | |
|         {
 | |
|             assert( m_type != ResultBase::Ok );
 | |
|         }
 | |
| 
 | |
|         using ResultValueBase<T>::ResultValueBase;
 | |
|         using ResultBase::m_type;
 | |
|     };
 | |
| 
 | |
|     enum class ParseResultType {
 | |
|         Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
 | |
|     };
 | |
| 
 | |
|     class ParseState {
 | |
|     public:
 | |
| 
 | |
|         ParseState( ParseResultType type, TokenStream const &remainingTokens )
 | |
|         : m_type(type),
 | |
|           m_remainingTokens( remainingTokens )
 | |
|         {}
 | |
| 
 | |
|         auto type() const -> ParseResultType { return m_type; }
 | |
|         auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
 | |
| 
 | |
|     private:
 | |
|         ParseResultType m_type;
 | |
|         TokenStream m_remainingTokens;
 | |
|     };
 | |
| 
 | |
|     using Result = BasicResult<void>;
 | |
|     using ParserResult = BasicResult<ParseResultType>;
 | |
|     using InternalParseResult = BasicResult<ParseState>;
 | |
| 
 | |
|     struct HelpColumns {
 | |
|         std::string left;
 | |
|         std::string right;
 | |
|     };
 | |
| 
 | |
|     template<typename T>
 | |
|     inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
 | |
|         std::stringstream ss;
 | |
|         ss << source;
 | |
|         ss >> target;
 | |
|         if( ss.fail() )
 | |
|             return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
 | |
|         else
 | |
|             return ParserResult::ok( ParseResultType::Matched );
 | |
|     }
 | |
|     inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
 | |
|         target = source;
 | |
|         return ParserResult::ok( ParseResultType::Matched );
 | |
|     }
 | |
|     inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
 | |
|         std::string srcLC = source;
 | |
|         std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } );
 | |
|         if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
 | |
|             target = true;
 | |
|         else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
 | |
|             target = false;
 | |
|         else
 | |
|             return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
 | |
|         return ParserResult::ok( ParseResultType::Matched );
 | |
|     }
 | |
| 
 | |
|     struct BoundRefBase {
 | |
|         BoundRefBase() = default;
 | |
|         BoundRefBase( BoundRefBase const & ) = delete;
 | |
|         BoundRefBase( BoundRefBase && ) = delete;
 | |
|         BoundRefBase &operator=( BoundRefBase const & ) = delete;
 | |
|         BoundRefBase &operator=( BoundRefBase && ) = delete;
 | |
| 
 | |
|         virtual ~BoundRefBase() = default;
 | |
| 
 | |
|         virtual auto isFlag() const -> bool = 0;
 | |
|         virtual auto isContainer() const -> bool { return false; }
 | |
|         virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
 | |
|         virtual auto setFlag( bool flag ) -> ParserResult = 0;
 | |
|     };
 | |
| 
 | |
|     struct BoundValueRefBase : BoundRefBase {
 | |
|         auto isFlag() const -> bool override { return false; }
 | |
| 
 | |
|         auto setFlag( bool ) -> ParserResult override {
 | |
|             return ParserResult::logicError( "Flags can only be set on boolean fields" );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     struct BoundFlagRefBase : BoundRefBase {
 | |
|         auto isFlag() const -> bool override { return true; }
 | |
| 
 | |
|         auto setValue( std::string const &arg ) -> ParserResult override {
 | |
|             bool flag;
 | |
|             auto result = convertInto( arg, flag );
 | |
|             if( result )
 | |
|                 setFlag( flag );
 | |
|             return result;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename T>
 | |
|     struct BoundRef : BoundValueRefBase {
 | |
|         T &m_ref;
 | |
| 
 | |
|         explicit BoundRef( T &ref ) : m_ref( ref ) {}
 | |
| 
 | |
|         auto setValue( std::string const &arg ) -> ParserResult override {
 | |
|             return convertInto( arg, m_ref );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename T>
 | |
|     struct BoundRef<std::vector<T>> : BoundValueRefBase {
 | |
|         std::vector<T> &m_ref;
 | |
| 
 | |
|         explicit BoundRef( std::vector<T> &ref ) : m_ref( ref ) {}
 | |
| 
 | |
|         auto isContainer() const -> bool override { return true; }
 | |
| 
 | |
|         auto setValue( std::string const &arg ) -> ParserResult override {
 | |
|             T temp;
 | |
|             auto result = convertInto( arg, temp );
 | |
|             if( result )
 | |
|                 m_ref.push_back( temp );
 | |
|             return result;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     struct BoundFlagRef : BoundFlagRefBase {
 | |
|         bool &m_ref;
 | |
| 
 | |
|         explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
 | |
| 
 | |
|         auto setFlag( bool flag ) -> ParserResult override {
 | |
|             m_ref = flag;
 | |
|             return ParserResult::ok( ParseResultType::Matched );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename ReturnType>
 | |
|     struct LambdaInvoker {
 | |
|         static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
 | |
| 
 | |
|         template<typename L, typename ArgType>
 | |
|         static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
 | |
|             return lambda( arg );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<>
 | |
|     struct LambdaInvoker<void> {
 | |
|         template<typename L, typename ArgType>
 | |
|         static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
 | |
|             lambda( arg );
 | |
|             return ParserResult::ok( ParseResultType::Matched );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename ArgType, typename L>
 | |
|     inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
 | |
|         ArgType temp;
 | |
|         auto result = convertInto( arg, temp );
 | |
|         return !result
 | |
|            ? result
 | |
|            : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
 | |
|     };
 | |
| 
 | |
| 
 | |
|     template<typename L>
 | |
|     struct BoundLambda : BoundValueRefBase {
 | |
|         L m_lambda;
 | |
| 
 | |
|         static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
 | |
|         explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
 | |
| 
 | |
|         auto setValue( std::string const &arg ) -> ParserResult override {
 | |
|             return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename L>
 | |
|     struct BoundFlagLambda : BoundFlagRefBase {
 | |
|         L m_lambda;
 | |
| 
 | |
|         static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
 | |
|         static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
 | |
| 
 | |
|         explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
 | |
| 
 | |
|         auto setFlag( bool flag ) -> ParserResult override {
 | |
|             return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     enum class Optionality { Optional, Required };
 | |
| 
 | |
|     struct Parser;
 | |
| 
 | |
|     class ParserBase {
 | |
|     public:
 | |
|         virtual ~ParserBase() = default;
 | |
|         virtual auto validate() const -> Result { return Result::ok(); }
 | |
|         virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult  = 0;
 | |
|         virtual auto cardinality() const -> size_t { return 1; }
 | |
| 
 | |
|         auto parse( Args const &args ) const -> InternalParseResult {
 | |
|             return parse( args.exeName(), TokenStream( args ) );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename DerivedT>
 | |
|     class ComposableParserImpl : public ParserBase {
 | |
|     public:
 | |
|         template<typename T>
 | |
|         auto operator|( T const &other ) const -> Parser;
 | |
|     };
 | |
| 
 | |
|     // Common code and state for Args and Opts
 | |
|     template<typename DerivedT>
 | |
|     class ParserRefImpl : public ComposableParserImpl<DerivedT> {
 | |
|     protected:
 | |
|         Optionality m_optionality = Optionality::Optional;
 | |
|         std::shared_ptr<BoundRefBase> m_ref;
 | |
|         std::string m_hint;
 | |
|         std::string m_description;
 | |
| 
 | |
|         explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {}
 | |
| 
 | |
|     public:
 | |
|         template<typename T>
 | |
|         ParserRefImpl( T &ref, std::string const &hint )
 | |
|         :   m_ref( std::make_shared<BoundRef<T>>( ref ) ),
 | |
|             m_hint( hint )
 | |
|         {}
 | |
| 
 | |
|         template<typename LambdaT>
 | |
|         ParserRefImpl( LambdaT const &ref, std::string const &hint )
 | |
|         :   m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
 | |
|             m_hint(hint)
 | |
|         {}
 | |
| 
 | |
|         auto operator()( std::string const &description ) -> DerivedT & {
 | |
|             m_description = description;
 | |
|             return static_cast<DerivedT &>( *this );
 | |
|         }
 | |
| 
 | |
|         auto optional() -> DerivedT & {
 | |
|             m_optionality = Optionality::Optional;
 | |
|             return static_cast<DerivedT &>( *this );
 | |
|         };
 | |
| 
 | |
|         auto required() -> DerivedT & {
 | |
|             m_optionality = Optionality::Required;
 | |
|             return static_cast<DerivedT &>( *this );
 | |
|         };
 | |
| 
 | |
|         auto isOptional() const -> bool {
 | |
|             return m_optionality == Optionality::Optional;
 | |
|         }
 | |
| 
 | |
|         auto cardinality() const -> size_t override {
 | |
|             if( m_ref->isContainer() )
 | |
|                 return 0;
 | |
|             else
 | |
|                 return 1;
 | |
|         }
 | |
| 
 | |
|         auto hint() const -> std::string { return m_hint; }
 | |
|     };
 | |
| 
 | |
|     class ExeName : public ComposableParserImpl<ExeName> {
 | |
|         std::shared_ptr<std::string> m_name;
 | |
|         std::shared_ptr<BoundRefBase> m_ref;
 | |
| 
 | |
|         template<typename LambdaT>
 | |
|         static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> {
 | |
|             return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
 | |
|         }
 | |
| 
 | |
|     public:
 | |
|         ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
 | |
| 
 | |
|         explicit ExeName( std::string &ref ) : ExeName() {
 | |
|             m_ref = std::make_shared<BoundRef<std::string>>( ref );
 | |
|         }
 | |
| 
 | |
|         template<typename LambdaT>
 | |
|         explicit ExeName( LambdaT const& lambda ) : ExeName() {
 | |
|             m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
 | |
|         }
 | |
| 
 | |
|         // The exe name is not parsed out of the normal tokens, but is handled specially
 | |
|         auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
 | |
|             return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
 | |
|         }
 | |
| 
 | |
|         auto name() const -> std::string { return *m_name; }
 | |
|         auto set( std::string const& newName ) -> ParserResult {
 | |
| 
 | |
|             auto lastSlash = newName.find_last_of( "\\/" );
 | |
|             auto filename = ( lastSlash == std::string::npos )
 | |
|                     ? newName
 | |
|                     : newName.substr( lastSlash+1 );
 | |
| 
 | |
|             *m_name = filename;
 | |
|             if( m_ref )
 | |
|                 return m_ref->setValue( filename );
 | |
|             else
 | |
|                 return ParserResult::ok( ParseResultType::Matched );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     class Arg : public ParserRefImpl<Arg> {
 | |
|     public:
 | |
|         using ParserRefImpl::ParserRefImpl;
 | |
| 
 | |
|         auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
 | |
|             auto validationResult = validate();
 | |
|             if( !validationResult )
 | |
|                 return InternalParseResult( validationResult );
 | |
| 
 | |
|             auto remainingTokens = tokens;
 | |
|             auto const &token = *remainingTokens;
 | |
|             if( token.type != TokenType::Argument )
 | |
|                 return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
 | |
| 
 | |
|             auto result = m_ref->setValue( remainingTokens->token );
 | |
|             if( !result )
 | |
|                 return InternalParseResult( result );
 | |
|             else
 | |
|                 return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     inline auto normaliseOpt( std::string const &optName ) -> std::string {
 | |
| #ifdef CATCH_PLATFORM_WINDOWS
 | |
|         if( optName[0] == '/' )
 | |
|             return "-" + optName.substr( 1 );
 | |
|         else
 | |
| #endif
 | |
|             return optName;
 | |
|     }
 | |
| 
 | |
|     class Opt : public ParserRefImpl<Opt> {
 | |
|     protected:
 | |
|         std::vector<std::string> m_optNames;
 | |
| 
 | |
|     public:
 | |
|         template<typename LambdaT>
 | |
|         explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
 | |
| 
 | |
|         explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
 | |
| 
 | |
|         template<typename LambdaT>
 | |
|         Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
 | |
| 
 | |
|         template<typename T>
 | |
|         Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
 | |
| 
 | |
|         auto operator[]( std::string const &optName ) -> Opt & {
 | |
|             m_optNames.push_back( optName );
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         auto getHelpColumns() const -> std::vector<HelpColumns> {
 | |
|             std::ostringstream oss;
 | |
|             bool first = true;
 | |
|             for( auto const &opt : m_optNames ) {
 | |
|                 if (first)
 | |
|                     first = false;
 | |
|                 else
 | |
|                     oss << ", ";
 | |
|                 oss << opt;
 | |
|             }
 | |
|             if( !m_hint.empty() )
 | |
|                 oss << " <" << m_hint << ">";
 | |
|             return { { oss.str(), m_description } };
 | |
|         }
 | |
| 
 | |
|         auto isMatch( std::string const &optToken ) const -> bool {
 | |
|             auto normalisedToken = normaliseOpt( optToken );
 | |
|             for( auto const &name : m_optNames ) {
 | |
|                 if( normaliseOpt( name ) == normalisedToken )
 | |
|                     return true;
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         using ParserBase::parse;
 | |
| 
 | |
|         auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
 | |
|             auto validationResult = validate();
 | |
|             if( !validationResult )
 | |
|                 return InternalParseResult( validationResult );
 | |
| 
 | |
|             auto remainingTokens = tokens;
 | |
|             if( remainingTokens && remainingTokens->type == TokenType::Option ) {
 | |
|                 auto const &token = *remainingTokens;
 | |
|                 if( isMatch(token.token ) ) {
 | |
|                     if( m_ref->isFlag() ) {
 | |
|                         auto result = m_ref->setFlag( true );
 | |
|                         if( !result )
 | |
|                             return InternalParseResult( result );
 | |
|                         if( result.value() == ParseResultType::ShortCircuitAll )
 | |
|                             return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
 | |
|                     } else {
 | |
|                         ++remainingTokens;
 | |
|                         if( !remainingTokens )
 | |
|                             return InternalParseResult::runtimeError( "Expected argument following " + token.token );
 | |
|                         auto const &argToken = *remainingTokens;
 | |
|                         if( argToken.type != TokenType::Argument )
 | |
|                             return InternalParseResult::runtimeError( "Expected argument following " + token.token );
 | |
|                         auto result = m_ref->setValue( argToken.token );
 | |
|                         if( !result )
 | |
|                             return InternalParseResult( result );
 | |
|                         if( result.value() == ParseResultType::ShortCircuitAll )
 | |
|                             return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
 | |
|                     }
 | |
|                     return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
 | |
|                 }
 | |
|             }
 | |
|             return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
 | |
|         }
 | |
| 
 | |
|         auto validate() const -> Result override {
 | |
|             if( m_optNames.empty() )
 | |
|                 return Result::logicError( "No options supplied to Opt" );
 | |
|             for( auto const &name : m_optNames ) {
 | |
|                 if( name.empty() )
 | |
|                     return Result::logicError( "Option name cannot be empty" );
 | |
| #ifdef CATCH_PLATFORM_WINDOWS
 | |
|                 if( name[0] != '-' && name[0] != '/' )
 | |
|                     return Result::logicError( "Option name must begin with '-' or '/'" );
 | |
| #else
 | |
|                 if( name[0] != '-' )
 | |
|                     return Result::logicError( "Option name must begin with '-'" );
 | |
| #endif
 | |
|             }
 | |
|             return ParserRefImpl::validate();
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     struct Help : Opt {
 | |
|         Help( bool &showHelpFlag )
 | |
|         :   Opt([&]( bool flag ) {
 | |
|                 showHelpFlag = flag;
 | |
|                 return ParserResult::ok( ParseResultType::ShortCircuitAll );
 | |
|             })
 | |
|         {
 | |
|             static_cast<Opt &>( *this )
 | |
|                     ("display usage information")
 | |
|                     ["-?"]["-h"]["--help"]
 | |
|                     .optional();
 | |
|         }
 | |
|     };
 | |
| 
 | |
| 
 | |
|     struct Parser : ParserBase {
 | |
| 
 | |
|         mutable ExeName m_exeName;
 | |
|         std::vector<Opt> m_options;
 | |
|         std::vector<Arg> m_args;
 | |
| 
 | |
|         auto operator|=( ExeName const &exeName ) -> Parser & {
 | |
|             m_exeName = exeName;
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         auto operator|=( Arg const &arg ) -> Parser & {
 | |
|             m_args.push_back(arg);
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         auto operator|=( Opt const &opt ) -> Parser & {
 | |
|             m_options.push_back(opt);
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         auto operator|=( Parser const &other ) -> Parser & {
 | |
|             m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
 | |
|             m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         template<typename T>
 | |
|         auto operator|( T const &other ) const -> Parser {
 | |
|             return Parser( *this ) |= other;
 | |
|         }
 | |
| 
 | |
|         auto getHelpColumns() const -> std::vector<HelpColumns> {
 | |
|             std::vector<HelpColumns> cols;
 | |
|             for (auto const &o : m_options) {
 | |
|                 auto childCols = o.getHelpColumns();
 | |
|                 cols.insert( cols.end(), childCols.begin(), childCols.end() );
 | |
|             }
 | |
|             return cols;
 | |
|         }
 | |
| 
 | |
|         void writeToStream( std::ostream &os ) const {
 | |
|             if (!m_exeName.name().empty()) {
 | |
|                 os << "usage:\n" << "  " << m_exeName.name() << " ";
 | |
|                 bool required = true, first = true;
 | |
|                 for( auto const &arg : m_args ) {
 | |
|                     if (first)
 | |
|                         first = false;
 | |
|                     else
 | |
|                         os << " ";
 | |
|                     if( arg.isOptional() && required ) {
 | |
|                         os << "[";
 | |
|                         required = false;
 | |
|                     }
 | |
|                     os << "<" << arg.hint() << ">";
 | |
|                     if( arg.cardinality() == 0 )
 | |
|                         os << " ... ";
 | |
|                 }
 | |
|                 if( !required )
 | |
|                     os << "]";
 | |
|                 if( !m_options.empty() )
 | |
|                     os << " options";
 | |
|                 os << "\n\nwhere options are:" << std::endl;
 | |
|             }
 | |
| 
 | |
|             auto rows = getHelpColumns();
 | |
|             size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
 | |
|             size_t optWidth = 0;
 | |
|             for( auto const &cols : rows )
 | |
|                 optWidth = (std::max)(optWidth, cols.left.size() + 2);
 | |
| 
 | |
|             for( auto const &cols : rows ) {
 | |
|                 auto row =
 | |
|                         TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
 | |
|                         TextFlow::Spacer(4) +
 | |
|                         TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
 | |
|                 os << row << std::endl;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
 | |
|             parser.writeToStream( os );
 | |
|             return os;
 | |
|         }
 | |
| 
 | |
|         auto validate() const -> Result override {
 | |
|             for( auto const &opt : m_options ) {
 | |
|                 auto result = opt.validate();
 | |
|                 if( !result )
 | |
|                     return result;
 | |
|             }
 | |
|             for( auto const &arg : m_args ) {
 | |
|                 auto result = arg.validate();
 | |
|                 if( !result )
 | |
|                     return result;
 | |
|             }
 | |
|             return Result::ok();
 | |
|         }
 | |
| 
 | |
|         using ParserBase::parse;
 | |
| 
 | |
|         auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
 | |
| 
 | |
|             struct ParserInfo {
 | |
|                 ParserBase const* parser = nullptr;
 | |
|                 size_t count = 0;
 | |
|             };
 | |
|             const size_t totalParsers = m_options.size() + m_args.size();
 | |
|             assert( totalParsers < 512 );
 | |
|             // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
 | |
|             ParserInfo parseInfos[512];
 | |
| 
 | |
|             {
 | |
|                 size_t i = 0;
 | |
|                 for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
 | |
|                 for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
 | |
|             }
 | |
| 
 | |
|             m_exeName.set( exeName );
 | |
| 
 | |
|             auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
 | |
|             while( result.value().remainingTokens() ) {
 | |
|                 bool tokenParsed = false;
 | |
| 
 | |
|                 for( size_t i = 0; i < totalParsers; ++i ) {
 | |
|                     auto&  parseInfo = parseInfos[i];
 | |
|                     if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
 | |
|                         result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
 | |
|                         if (!result)
 | |
|                             return result;
 | |
|                         if (result.value().type() != ParseResultType::NoMatch) {
 | |
|                             tokenParsed = true;
 | |
|                             ++parseInfo.count;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if( result.value().type() == ParseResultType::ShortCircuitAll )
 | |
|                     return result;
 | |
|                 if( !tokenParsed )
 | |
|                     return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
 | |
|             }
 | |
|             // !TBD Check missing required options
 | |
|             return result;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     template<typename DerivedT>
 | |
|     template<typename T>
 | |
|     auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
 | |
|         return Parser() | static_cast<DerivedT const &>( *this ) | other;
 | |
|     }
 | |
| } // namespace detail
 | |
| 
 | |
| 
 | |
| // A Combined parser
 | |
| using detail::Parser;
 | |
| 
 | |
| // A parser for options
 | |
| using detail::Opt;
 | |
| 
 | |
| // A parser for arguments
 | |
| using detail::Arg;
 | |
| 
 | |
| // Wrapper for argc, argv from main()
 | |
| using detail::Args;
 | |
| 
 | |
| // Specifies the name of the executable
 | |
| using detail::ExeName;
 | |
| 
 | |
| // Convenience wrapper for option parser that specifies the help option
 | |
| using detail::Help;
 | |
| 
 | |
| // enum of result types from a parse
 | |
| using detail::ParseResultType;
 | |
| 
 | |
| // Result type for parser operation
 | |
| using detail::ParserResult;
 | |
| 
 | |
| 
 | |
| }} // namespace Catch::clara
 | |
| 
 | |
| 
 | |
| #endif // CATCH_CLARA_HPP_INCLUDED
 |