From 2a1f8ae684eb4d9ac6dda20261295c8d354a0ef9 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 25 Sep 2017 15:12:00 -0700 Subject: [PATCH] New version of Clara --- include/external/clara.hpp | 497 ++++++++++++------------- include/internal/catch_commandline.cpp | 54 +-- third_party/clara.hpp | 490 ++++++++++++------------ 3 files changed, 507 insertions(+), 534 deletions(-) diff --git a/include/external/clara.hpp b/include/external/clara.hpp index 4abcd16f..e46574b4 100644 --- a/include/external/clara.hpp +++ b/include/external/clara.hpp @@ -4,11 +4,13 @@ #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 ----------- @@ -24,7 +26,6 @@ #ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED #define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED - #include #include #include @@ -350,9 +351,8 @@ namespace Catch { namespace clara { namespace TextFlow { #include #include -#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) -#define CLARA_PLATFORM_WINDOWS - +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS #endif namespace Catch { namespace clara { @@ -360,15 +360,15 @@ namespace detail { // Traits for extracting arg and return type of lambdas (for single argument lambdas) template - struct UnaryLambdaTraits : UnaryLambdaTraits {}; + struct UnaryLambdaTraits : UnaryLambdaTraits {}; template - struct UnaryLambdaTraits { + struct UnaryLambdaTraits { static const bool isValid = false; }; template - struct UnaryLambdaTraits { + struct UnaryLambdaTraits { static const bool isValid = true; using ArgType = typename std::remove_const::type>::type;; using ReturnType = ReturnT; @@ -383,13 +383,13 @@ namespace detail { std::vector m_args; public: - Args(int argc, char *argv[]) { + Args( int argc, char *argv[] ) { m_exeName = argv[0]; - for (int i = 1; i < argc; ++i) - m_args.push_back(argv[i]); + for( int i = 1; i < argc; ++i ) + m_args.push_back( argv[i] ); } - Args(std::initializer_list args) + Args( std::initializer_list args ) : m_exeName( *args.begin() ), m_args( args.begin()+1, args.end() ) {} @@ -417,40 +417,40 @@ namespace detail { std::vector m_tokenBuffer; void loadBuffer() { - m_tokenBuffer.resize(0); + m_tokenBuffer.resize( 0 ); // Skip any empty strings - while (it != itEnd && it->empty()) + while( it != itEnd && it->empty() ) ++it; - if (it != itEnd) { + if( it != itEnd ) { auto const &next = *it; - if (next[0] == '-' || 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)}); + if( next[0] == '-' || 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) { + if( next[1] != '-' && next.size() > 2 ) { std::string opt = "- "; - for (size_t i = 1; i < next.size(); ++i) { + for( size_t i = 1; i < next.size(); ++i ) { opt[1] = next[i]; - m_tokenBuffer.push_back({TokenType::Option, opt}); + m_tokenBuffer.push_back( { TokenType::Option, opt } ); } } else { - m_tokenBuffer.push_back({TokenType::Option, next}); + m_tokenBuffer.push_back( { TokenType::Option, next } ); } } } else { - m_tokenBuffer.push_back({TokenType::Argument, next}); + m_tokenBuffer.push_back( { TokenType::Argument, next } ); } } } public: - explicit TokenStream(Args const &args) : TokenStream(args.m_args.begin(), args.m_args.end()) {} + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} - TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) { + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { loadBuffer(); } @@ -461,20 +461,20 @@ namespace detail { auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto operator*() const -> Token { - assert(!m_tokenBuffer.empty()); + assert( !m_tokenBuffer.empty() ); return m_tokenBuffer.front(); } auto operator->() const -> Token const * { - assert(!m_tokenBuffer.empty()); + assert( !m_tokenBuffer.empty() ); return &m_tokenBuffer.front(); } auto operator++() -> TokenStream & { - if (m_tokenBuffer.size() >= 2) { - m_tokenBuffer.erase(m_tokenBuffer.begin()); + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); } else { - if (it != itEnd) + if( it != itEnd ) ++it; loadBuffer(); } @@ -490,7 +490,7 @@ namespace detail { }; protected: - ResultBase(Type type) : m_type(type) {} + ResultBase( Type type ) : m_type( type ) {} virtual ~ResultBase() = default; virtual void enforceOk() const = 0; @@ -507,28 +507,28 @@ namespace detail { } protected: - ResultValueBase(Type type) : ResultBase(type) {} + ResultValueBase( Type type ) : ResultBase( type ) {} - ResultValueBase(ResultValueBase const &other) : ResultBase(other) { - if (m_type == ResultBase::Ok) - new(&m_value) T(other.m_value); + 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); + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); } - auto operator=(ResultValueBase const &other) -> ResultValueBase & { - if (m_type == ResultBase::Ok) + 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); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); return *this; } ~ResultValueBase() { - if (m_type == Ok) + if( m_type == Ok ) m_value.~T(); } @@ -547,37 +547,31 @@ namespace detail { class BasicResult : public ResultValueBase { public: template - explicit BasicResult(BasicResult const &other) - : ResultValueBase(other.type()), - m_errorMessage(other.errorMessage()) { - assert(type() != ResultBase::Ok); + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); } - static auto ok() -> BasicResult { return {ResultBase::Ok}; } - template - static auto ok(U const &value) -> BasicResult { return {ResultBase::Ok, value}; } - - static auto logicError(std::string const &message) -> BasicResult { return {ResultBase::LogicError, message}; } - - static auto runtimeError(std::string const &message) -> BasicResult { - return {ResultBase::RuntimeError, message}; - } + 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) { + switch( m_type ) { case ResultBase::LogicError: - throw std::logic_error(m_errorMessage); + throw std::logic_error( m_errorMessage ); case ResultBase::RuntimeError: - throw std::runtime_error(m_errorMessage); + throw std::runtime_error( m_errorMessage ); case ResultBase::Ok: break; } @@ -585,10 +579,11 @@ namespace detail { std::string m_errorMessage; // Only populated if resultType is an error - BasicResult(ResultBase::Type type, std::string const &message) - : ResultValueBase(type), - m_errorMessage(message) { - assert(m_type != ResultBase::Ok); + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); } using ResultValueBase::ResultValueBase; @@ -602,12 +597,12 @@ namespace detail { class ParseState { public: - ParseState(ParseResultType type, TokenStream const &remainingTokens) - : m_type(type), - m_remainingTokens(remainingTokens) {} + 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: @@ -625,69 +620,62 @@ namespace detail { }; template - inline auto convertInto(std::string const &source, T& target) -> ParserResult { + 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"); + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); else - return ParserResult::ok(ParseResultType::Matched); + return ParserResult::ok( ParseResultType::Matched ); } - inline auto convertInto(std::string const &source, std::string& target) -> ParserResult { + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { target = source; - return ParserResult::ok(ParseResultType::Matched); + return ParserResult::ok( ParseResultType::Matched ); } - inline auto convertInto(std::string const &source, bool &target) -> ParserResult { + 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( ::tolower(c) ); } ); + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::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); + 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; + 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; + 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"); + 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 { + auto setValue( std::string const &arg ) -> ParserResult override { bool flag; - auto result = convertInto(arg, flag); - if (result) - setFlag(flag); + auto result = convertInto( arg, flag ); + if( result ) + setFlag( flag ); return result; } }; @@ -696,10 +684,10 @@ namespace detail { struct BoundRef : BoundValueRefBase { T &m_ref; - explicit BoundRef(T &ref) : m_ref(ref) {} + explicit BoundRef( T &ref ) : m_ref( ref ) {} - auto setValue(std::string const &arg) -> ParserResult override { - return convertInto(arg, m_ref); + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); } }; @@ -707,15 +695,15 @@ namespace detail { struct BoundRef> : BoundValueRefBase { std::vector &m_ref; - explicit BoundRef(std::vector &ref) : m_ref(ref) {} + explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} auto isContainer() const -> bool override { return true; } - auto setValue(std::string const &arg) -> ParserResult override { + auto setValue( std::string const &arg ) -> ParserResult override { T temp; - auto result = convertInto(arg, temp); - if (result) - m_ref.push_back(temp); + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); return result; } }; @@ -723,40 +711,40 @@ namespace detail { struct BoundFlagRef : BoundFlagRefBase { bool &m_ref; - explicit BoundFlagRef(bool &ref) : m_ref(ref) {} + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} - auto setFlag(bool flag) -> ParserResult override { + auto setFlag( bool flag ) -> ParserResult override { m_ref = flag; - return ParserResult::ok(ParseResultType::Matched); + return ParserResult::ok( ParseResultType::Matched ); } }; template struct LambdaInvoker { - static_assert(std::is_same::value, "Lambda must return void or clara::ParserResult"); + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); template - static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { - return lambda(arg); + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); } }; template<> struct LambdaInvoker { template - static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { - lambda(arg); - return ParserResult::ok(ParseResultType::Matched); + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); } }; template - inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { ArgType temp; - auto result = convertInto(arg, temp); + auto result = convertInto( arg, temp ); return !result - ? result - : LambdaInvoker::ReturnType>::invoke(lambda, temp); + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); }; @@ -765,10 +753,10 @@ namespace detail { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - explicit BoundLambda(L const &lambda) : m_lambda(lambda) {} + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} - auto setValue(std::string const &arg) -> ParserResult override { - return invokeLambda::ArgType>(m_lambda, arg); + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); } }; @@ -779,16 +767,14 @@ namespace detail { static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); - explicit BoundFlagLambda(L const &lambda) : m_lambda(lambda) {} + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} - auto setFlag(bool flag) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); } }; - enum class Optionality { - Optional, Required - }; + enum class Optionality { Optional, Required }; struct Parser; @@ -799,8 +785,8 @@ namespace detail { 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)); + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); } }; @@ -808,7 +794,7 @@ namespace detail { class ComposableParserImpl : public ParserBase { public: template - auto operator+(T const &other) const -> Parser; + auto operator|( T const &other ) const -> Parser; }; // Common code and state for Args and Opts @@ -820,17 +806,22 @@ namespace detail { std::string m_hint; std::string m_description; - explicit ParserRefImpl(std::shared_ptr const &ref) : m_ref(ref) {} + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} public: template - ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared>(ref)), m_hint(hint) {} + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} template - ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared>(ref)), - m_hint(hint) {} + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} - auto operator()(std::string const &description) -> DerivedT & { + auto operator()( std::string const &description ) -> DerivedT & { m_description = description; return static_cast( *this ); } @@ -850,7 +841,7 @@ namespace detail { } auto cardinality() const -> size_t override { - if (m_ref->isContainer()) + if( m_ref->isContainer() ) return 0; else return 1; @@ -865,13 +856,13 @@ namespace detail { template static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { - return std::make_shared>(lambda); + return std::make_shared>( lambda) ; } public: - ExeName() : m_name(std::make_shared("")) {} + ExeName() : m_name( std::make_shared( "" ) ) {} - explicit ExeName(std::string &ref) : ExeName() { + explicit ExeName( std::string &ref ) : ExeName() { m_ref = std::make_shared>( ref ); } @@ -882,14 +873,14 @@ namespace detail { // 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)); + 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) + auto filename = ( lastSlash == std::string::npos ) ? newName : newName.substr( lastSlash+1 ); @@ -905,27 +896,27 @@ namespace detail { public: using ParserRefImpl::ParserRefImpl; - auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); - if (!validationResult) - return InternalParseResult(validationResult); + if( !validationResult ) + return InternalParseResult( validationResult ); auto remainingTokens = tokens; auto const &token = *remainingTokens; - if (token.type != TokenType::Argument) - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, 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); + auto result = m_ref->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); else - return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } }; - inline auto normaliseOpt(std::string const &optName) -> std::string { - if (optName[0] == '/') - return "-" + optName.substr(1); + inline auto normaliseOpt( std::string const &optName ) -> std::string { + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); else return optName; } @@ -936,9 +927,9 @@ namespace detail { public: template - explicit Opt( LambdaT const &ref ) : ParserRefImpl(std::make_shared>(ref)) {} + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} - explicit Opt( bool &ref ) : ParserRefImpl(std::make_shared(ref)) {} + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} template Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} @@ -946,34 +937,34 @@ namespace detail { template Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} - auto operator[](std::string const &optName) -> Opt & { - m_optNames.push_back(optName); + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); return *this; } auto getHelpColumns() const -> std::vector { std::ostringstream oss; bool first = true; - for (auto const &opt : m_optNames) { + for( auto const &opt : m_optNames ) { if (first) first = false; else oss << ", "; oss << opt; } - if (!m_hint.empty()) + if( !m_hint.empty() ) oss << " <" << m_hint << ">"; - return {{oss.str(), m_description}}; + return { { oss.str(), m_description } }; } - auto isMatch(std::string const &optToken) const -> bool { -#ifdef CLARA_PLATFORM_WINDOWS + auto isMatch( std::string const &optToken ) const -> bool { +#ifdef CATCH_PLATFORM_WINDOWS auto normalisedToken = normaliseOpt( optToken ); #else auto const &normalisedToken = optToken; #endif - for (auto const &name : m_optNames) { - if (normaliseOpt(name) == normalisedToken) + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) return true; } return false; @@ -983,46 +974,46 @@ namespace detail { auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); - if (!validationResult) - return InternalParseResult(validationResult); + if( !validationResult ) + return InternalParseResult( validationResult ); auto remainingTokens = tokens; - if (remainingTokens && remainingTokens->type == TokenType::Option) { + 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)); + 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); + 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)); + 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::Matched, ++remainingTokens ) ); } } - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, 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"); - if (name[0] != '-' && name[0] != '/') - return Result::logicError("Option name must begin with '-' or '/'"); + 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" ); + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); } return ParserRefImpl::validate(); } @@ -1031,11 +1022,11 @@ namespace detail { struct Help : Opt { Help( bool &showHelpFlag ) : Opt([&]( bool flag ) { - showHelpFlag = flag; - return ParserResult::ok(ParseResultType::ShortCircuitAll); + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); }) { - static_cast(*this) + static_cast( *this ) ("display usage information") ["-?"]["-h"]["--help"] .optional(); @@ -1049,61 +1040,61 @@ namespace detail { std::vector m_options; std::vector m_args; - auto operator+=(ExeName const &exeName) -> Parser & { + auto operator|=( ExeName const &exeName ) -> Parser & { m_exeName = exeName; return *this; } - auto operator+=(Arg const &arg) -> Parser & { + auto operator|=( Arg const &arg ) -> Parser & { m_args.push_back(arg); return *this; } - auto operator+=(Opt const &opt) -> Parser & { + auto operator|=( Opt const &opt ) -> Parser & { m_options.push_back(opt); return *this; } - auto operator+=(Parser const &other) -> Parser & { + 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 - auto operator+(T const &other) const -> Parser { - return Parser(*this) += other; + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; } auto getHelpColumns() const -> std::vector { std::vector cols; for (auto const &o : m_options) { auto childCols = o.getHelpColumns(); - cols.insert(cols.end(), childCols.begin(), childCols.end()); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); } return cols; } - void writeToStream(std::ostream &os) const { + 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) { + for( auto const &arg : m_args ) { if (first) first = false; else os << " "; - if (arg.isOptional() && required) { + if( arg.isOptional() && required ) { os << "["; required = false; } os << "<" << arg.hint() << ">"; - if (arg.cardinality() == 0) + if( arg.cardinality() == 0 ) os << " ... "; } - if (!required) + if( !required ) os << "]"; - if (!m_options.empty()) + if( !m_options.empty() ) os << " options"; os << "\n\nwhere options are:" << std::endl; } @@ -1111,32 +1102,32 @@ namespace detail { auto rows = getHelpColumns(); size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; size_t optWidth = 0; - for (auto const &cols : rows) + for( auto const &cols : rows ) optWidth = std::max(optWidth, cols.left.size() + 2); - for (auto const &cols : rows) { + for( auto const &cols : rows ) { auto row = - TextFlow::Column(cols.left).width(optWidth).indent(2) + + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + TextFlow::Spacer(4) + - TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); + 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); + 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) { + for( auto const &opt : m_options ) { auto result = opt.validate(); - if (!result) + if( !result ) return result; } - for (auto const &arg : m_args) { + for( auto const &arg : m_args ) { auto result = arg.validate(); - if (!result) + if( !result ) return result; } return Result::ok(); @@ -1144,47 +1135,41 @@ namespace detail { using ParserBase::parse; - auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult override { - std::vector allParsers; - allParsers.reserve(m_args.size() + m_options.size()); - std::set requiredParsers; + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { - for (auto const &opt : m_options) { - allParsers.push_back(&opt); - if (!opt.isOptional()) - requiredParsers.insert(&opt); - } - - size_t optionalArgs = 0; - for (auto const &arg : m_args) { - allParsers.push_back(&arg); - if (!arg.isOptional()) { - if (optionalArgs > 0) - return InternalParseResult::logicError( - "Required arguments must preceed any optional arguments"); - else - ++optionalArgs; - requiredParsers.insert(&arg); - } - } + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + ParserInfo parseInfos[totalParsers]; + 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()) { - auto remainingTokenCount = result.value().remainingTokens().count(); - for (auto parser : allParsers) { - result = parser->parse( exeName, result.value().remainingTokens() ); - if (!result || result.value().type() != ParseResultType::NoMatch) { - if (parser->cardinality() == 1) - allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser), - allParsers.end()); - requiredParsers.erase(parser); - break; + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( auto& parseInfo : parseInfos ) { + 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 || remainingTokenCount == result.value().remainingTokens().count()) + + 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; @@ -1193,8 +1178,8 @@ namespace detail { template template - auto ComposableParserImpl::operator+(T const &other) const -> Parser { - return Parser() + static_cast( *this ) + other; + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; } } // namespace detail diff --git a/include/internal/catch_commandline.cpp b/include/internal/catch_commandline.cpp index 89329032..c0231d1a 100644 --- a/include/internal/catch_commandline.cpp +++ b/include/internal/catch_commandline.cpp @@ -98,84 +98,84 @@ namespace Catch { auto cli = ExeName( config.processName ) - + Help( config.showHelp ) - + Opt( config.listTests ) + | Help( config.showHelp ) + | Opt( config.listTests ) ["-l"]["--list-tests"] ( "list all/matching test cases" ) - + Opt( config.listTags ) + | Opt( config.listTags ) ["-t"]["--list-tags"] ( "list all/matching tags" ) - + Opt( config.showSuccessfulTests ) + | Opt( config.showSuccessfulTests ) ["-s"]["--success"] ( "include successful tests in output" ) - + Opt( config.shouldDebugBreak ) + | Opt( config.shouldDebugBreak ) ["-b"]["--break"] ( "break into debugger on failure" ) - + Opt( config.noThrow ) + | Opt( config.noThrow ) ["-e"]["--nothrow"] ( "skip exception tests" ) - + Opt( config.showInvisibles ) + | Opt( config.showInvisibles ) ["-i"]["--invisibles"] ( "show invisibles (tabs, newlines)" ) - + Opt( config.outputFilename, "filename" ) + | Opt( config.outputFilename, "filename" ) ["-o"]["--out"] ( "output filename" ) - + Opt( config.reporterNames, "name" ) + | Opt( config.reporterNames, "name" ) ["-r"]["--reporter"] ( "reporter to use (defaults to console)" ) - + Opt( config.name, "name" ) + | Opt( config.name, "name" ) ["-n"]["--name"] ( "suite name" ) - + Opt( [&]( bool ){ config.abortAfter = 1; } ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) ["-a"]["--abort"] ( "abort at first failure" ) - + Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) ["-x"]["--abortx"] ( "abort after x failures" ) - + Opt( setWarning, "warning name" ) + | Opt( setWarning, "warning name" ) ["-w"]["--warn"] ( "enable warnings" ) - + Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) ["-d"]["--durations"] ( "show test durations" ) - + Opt( loadTestNamesFromFile, "filename" ) + | Opt( loadTestNamesFromFile, "filename" ) ["-f"]["--input-file"] ( "load test names to run from a file" ) - + Opt( config.filenamesAsTags ) + | Opt( config.filenamesAsTags ) ["-#"]["--filenames-as-tags"] ( "adds a tag for the filename" ) - + Opt( config.sectionsToRun, "section name" ) + | Opt( config.sectionsToRun, "section name" ) ["-c"]["--section"] ( "specify section to run" ) - + Opt( setVerbosity, "quiet|normal|high" ) + | Opt( setVerbosity, "quiet|normal|high" ) ["-v"]["--verbosity"] ( "set output verbosity" ) - + Opt( config.listTestNamesOnly ) + | Opt( config.listTestNamesOnly ) ["--list-test-names-only"] ( "list all/matching test cases names only" ) - + Opt( config.listReporters ) + | Opt( config.listReporters ) ["--list-reporters"] ( "list all reporters" ) - + Opt( setTestOrder, "decl|lex|rand" ) + | Opt( setTestOrder, "decl|lex|rand" ) ["--order"] ( "test case order (defaults to decl)" ) - + Opt( setRngSeed, "'time'|number" ) + | Opt( setRngSeed, "'time'|number" ) ["--rng-seed"] ( "set a specific seed for random numbers" ) - + Opt( setColourUsage, "yes|no" ) + | Opt( setColourUsage, "yes|no" ) ["--use-colour"] ( "should output be colourised" ) - + Opt( config.libIdentify ) + | Opt( config.libIdentify ) ["--libidentify"] ( "report name and version according to libidentify standard" ) - + Opt( setWaitForKeypress, "start|exit|both" ) + | Opt( setWaitForKeypress, "start|exit|both" ) ["--wait-for-keypress"] ( "waits for a keypress before exiting" ) - + Opt( config.benchmarkResolutionMultiple, "multiplier" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) ["--benchmark-resolution-multiple"] ( "multiple of clock resolution to run benchmarks" ) - + Arg( config.testsOrTags, "test name|pattern|tags" ) + | Arg( config.testsOrTags, "test name|pattern|tags" ) ( "which test or tests to use" ); return cli; diff --git a/third_party/clara.hpp b/third_party/clara.hpp index 509c9b5a..5778c562 100644 --- a/third_party/clara.hpp +++ b/third_party/clara.hpp @@ -8,6 +8,9 @@ #define CLARA_CONFIG_CONSOLE_WIDTH 80 #endif +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#endif // ----------- #included from clara_textflow.hpp ----------- @@ -133,7 +136,7 @@ namespace clara { namespace TextFlow { auto operator *() const -> std::string { assert( m_stringIndex < m_column.m_strings.size() ); - assert( m_pos < m_end ); + assert( m_pos <= m_end ); if( m_pos + m_column.m_width < m_end ) return addIndentAndSuffix(line().substr(m_pos, m_len)); else @@ -357,15 +360,15 @@ namespace detail { // Traits for extracting arg and return type of lambdas (for single argument lambdas) template - struct UnaryLambdaTraits : UnaryLambdaTraits {}; + struct UnaryLambdaTraits : UnaryLambdaTraits {}; template - struct UnaryLambdaTraits { + struct UnaryLambdaTraits { static const bool isValid = false; }; template - struct UnaryLambdaTraits { + struct UnaryLambdaTraits { static const bool isValid = true; using ArgType = typename std::remove_const::type>::type;; using ReturnType = ReturnT; @@ -380,13 +383,13 @@ namespace detail { std::vector m_args; public: - Args(int argc, char *argv[]) { + Args( int argc, char *argv[] ) { m_exeName = argv[0]; - for (int i = 1; i < argc; ++i) - m_args.push_back(argv[i]); + for( int i = 1; i < argc; ++i ) + m_args.push_back( argv[i] ); } - Args(std::initializer_list args) + Args( std::initializer_list args ) : m_exeName( *args.begin() ), m_args( args.begin()+1, args.end() ) {} @@ -414,40 +417,40 @@ namespace detail { std::vector m_tokenBuffer; void loadBuffer() { - m_tokenBuffer.resize(0); + m_tokenBuffer.resize( 0 ); // Skip any empty strings - while (it != itEnd && it->empty()) + while( it != itEnd && it->empty() ) ++it; - if (it != itEnd) { + if( it != itEnd ) { auto const &next = *it; - if (next[0] == '-' || 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)}); + if( next[0] == '-' || 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) { + if( next[1] != '-' && next.size() > 2 ) { std::string opt = "- "; - for (size_t i = 1; i < next.size(); ++i) { + for( size_t i = 1; i < next.size(); ++i ) { opt[1] = next[i]; - m_tokenBuffer.push_back({TokenType::Option, opt}); + m_tokenBuffer.push_back( { TokenType::Option, opt } ); } } else { - m_tokenBuffer.push_back({TokenType::Option, next}); + m_tokenBuffer.push_back( { TokenType::Option, next } ); } } } else { - m_tokenBuffer.push_back({TokenType::Argument, next}); + m_tokenBuffer.push_back( { TokenType::Argument, next } ); } } } public: - explicit TokenStream(Args const &args) : TokenStream(args.m_args.begin(), args.m_args.end()) {} + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} - TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) { + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { loadBuffer(); } @@ -458,20 +461,20 @@ namespace detail { auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto operator*() const -> Token { - assert(!m_tokenBuffer.empty()); + assert( !m_tokenBuffer.empty() ); return m_tokenBuffer.front(); } auto operator->() const -> Token const * { - assert(!m_tokenBuffer.empty()); + assert( !m_tokenBuffer.empty() ); return &m_tokenBuffer.front(); } auto operator++() -> TokenStream & { - if (m_tokenBuffer.size() >= 2) { - m_tokenBuffer.erase(m_tokenBuffer.begin()); + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); } else { - if (it != itEnd) + if( it != itEnd ) ++it; loadBuffer(); } @@ -487,7 +490,7 @@ namespace detail { }; protected: - ResultBase(Type type) : m_type(type) {} + ResultBase( Type type ) : m_type( type ) {} virtual ~ResultBase() = default; virtual void enforceOk() const = 0; @@ -504,28 +507,28 @@ namespace detail { } protected: - ResultValueBase(Type type) : ResultBase(type) {} + ResultValueBase( Type type ) : ResultBase( type ) {} - ResultValueBase(ResultValueBase const &other) : ResultBase(other) { - if (m_type == ResultBase::Ok) - new(&m_value) T(other.m_value); + 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); + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); } - auto operator=(ResultValueBase const &other) -> ResultValueBase & { - if (m_type == ResultBase::Ok) + 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); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); return *this; } ~ResultValueBase() { - if (m_type == Ok) + if( m_type == Ok ) m_value.~T(); } @@ -544,37 +547,31 @@ namespace detail { class BasicResult : public ResultValueBase { public: template - explicit BasicResult(BasicResult const &other) - : ResultValueBase(other.type()), - m_errorMessage(other.errorMessage()) { - assert(type() != ResultBase::Ok); + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); } - static auto ok() -> BasicResult { return {ResultBase::Ok}; } - template - static auto ok(U const &value) -> BasicResult { return {ResultBase::Ok, value}; } - - static auto logicError(std::string const &message) -> BasicResult { return {ResultBase::LogicError, message}; } - - static auto runtimeError(std::string const &message) -> BasicResult { - return {ResultBase::RuntimeError, message}; - } + 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) { + switch( m_type ) { case ResultBase::LogicError: - throw std::logic_error(m_errorMessage); + throw std::logic_error( m_errorMessage ); case ResultBase::RuntimeError: - throw std::runtime_error(m_errorMessage); + throw std::runtime_error( m_errorMessage ); case ResultBase::Ok: break; } @@ -582,10 +579,11 @@ namespace detail { std::string m_errorMessage; // Only populated if resultType is an error - BasicResult(ResultBase::Type type, std::string const &message) - : ResultValueBase(type), - m_errorMessage(message) { - assert(m_type != ResultBase::Ok); + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); } using ResultValueBase::ResultValueBase; @@ -599,12 +597,12 @@ namespace detail { class ParseState { public: - ParseState(ParseResultType type, TokenStream const &remainingTokens) - : m_type(type), - m_remainingTokens(remainingTokens) {} + 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: @@ -622,69 +620,62 @@ namespace detail { }; template - inline auto convertInto(std::string const &source, T& target) -> ParserResult { + 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"); + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); else - return ParserResult::ok(ParseResultType::Matched); + return ParserResult::ok( ParseResultType::Matched ); } - inline auto convertInto(std::string const &source, std::string& target) -> ParserResult { + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { target = source; - return ParserResult::ok(ParseResultType::Matched); + return ParserResult::ok( ParseResultType::Matched ); } - inline auto convertInto(std::string const &source, bool &target) -> ParserResult { + 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( ::tolower(c) ); } ); + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::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); + 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; + 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; + 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"); + 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 { + auto setValue( std::string const &arg ) -> ParserResult override { bool flag; - auto result = convertInto(arg, flag); - if (result) - setFlag(flag); + auto result = convertInto( arg, flag ); + if( result ) + setFlag( flag ); return result; } }; @@ -693,10 +684,10 @@ namespace detail { struct BoundRef : BoundValueRefBase { T &m_ref; - explicit BoundRef(T &ref) : m_ref(ref) {} + explicit BoundRef( T &ref ) : m_ref( ref ) {} - auto setValue(std::string const &arg) -> ParserResult override { - return convertInto(arg, m_ref); + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); } }; @@ -704,15 +695,15 @@ namespace detail { struct BoundRef> : BoundValueRefBase { std::vector &m_ref; - explicit BoundRef(std::vector &ref) : m_ref(ref) {} + explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} auto isContainer() const -> bool override { return true; } - auto setValue(std::string const &arg) -> ParserResult override { + auto setValue( std::string const &arg ) -> ParserResult override { T temp; - auto result = convertInto(arg, temp); - if (result) - m_ref.push_back(temp); + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); return result; } }; @@ -720,40 +711,40 @@ namespace detail { struct BoundFlagRef : BoundFlagRefBase { bool &m_ref; - explicit BoundFlagRef(bool &ref) : m_ref(ref) {} + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} - auto setFlag(bool flag) -> ParserResult override { + auto setFlag( bool flag ) -> ParserResult override { m_ref = flag; - return ParserResult::ok(ParseResultType::Matched); + return ParserResult::ok( ParseResultType::Matched ); } }; template struct LambdaInvoker { - static_assert(std::is_same::value, "Lambda must return void or clara::ParserResult"); + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); template - static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { - return lambda(arg); + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); } }; template<> struct LambdaInvoker { template - static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { - lambda(arg); - return ParserResult::ok(ParseResultType::Matched); + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); } }; template - inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { ArgType temp; - auto result = convertInto(arg, temp); + auto result = convertInto( arg, temp ); return !result - ? result - : LambdaInvoker::ReturnType>::invoke(lambda, temp); + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); }; @@ -762,10 +753,10 @@ namespace detail { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - explicit BoundLambda(L const &lambda) : m_lambda(lambda) {} + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} - auto setValue(std::string const &arg) -> ParserResult override { - return invokeLambda::ArgType>(m_lambda, arg); + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); } }; @@ -776,16 +767,14 @@ namespace detail { static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); - explicit BoundFlagLambda(L const &lambda) : m_lambda(lambda) {} + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} - auto setFlag(bool flag) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); } }; - enum class Optionality { - Optional, Required - }; + enum class Optionality { Optional, Required }; struct Parser; @@ -796,8 +785,8 @@ namespace detail { 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)); + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); } }; @@ -805,7 +794,7 @@ namespace detail { class ComposableParserImpl : public ParserBase { public: template - auto operator+(T const &other) const -> Parser; + auto operator|( T const &other ) const -> Parser; }; // Common code and state for Args and Opts @@ -817,17 +806,22 @@ namespace detail { std::string m_hint; std::string m_description; - explicit ParserRefImpl(std::shared_ptr const &ref) : m_ref(ref) {} + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} public: template - ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared>(ref)), m_hint(hint) {} + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} template - ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared>(ref)), - m_hint(hint) {} + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} - auto operator()(std::string const &description) -> DerivedT & { + auto operator()( std::string const &description ) -> DerivedT & { m_description = description; return static_cast( *this ); } @@ -847,7 +841,7 @@ namespace detail { } auto cardinality() const -> size_t override { - if (m_ref->isContainer()) + if( m_ref->isContainer() ) return 0; else return 1; @@ -862,13 +856,13 @@ namespace detail { template static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { - return std::make_shared>(lambda); + return std::make_shared>( lambda) ; } public: - ExeName() : m_name(std::make_shared("")) {} + ExeName() : m_name( std::make_shared( "" ) ) {} - explicit ExeName(std::string &ref) : ExeName() { + explicit ExeName( std::string &ref ) : ExeName() { m_ref = std::make_shared>( ref ); } @@ -879,14 +873,14 @@ namespace detail { // 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)); + 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) + auto filename = ( lastSlash == std::string::npos ) ? newName : newName.substr( lastSlash+1 ); @@ -902,27 +896,27 @@ namespace detail { public: using ParserRefImpl::ParserRefImpl; - auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); - if (!validationResult) - return InternalParseResult(validationResult); + if( !validationResult ) + return InternalParseResult( validationResult ); auto remainingTokens = tokens; auto const &token = *remainingTokens; - if (token.type != TokenType::Argument) - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, 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); + auto result = m_ref->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); else - return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); } }; - inline auto normaliseOpt(std::string const &optName) -> std::string { - if (optName[0] == '/') - return "-" + optName.substr(1); + inline auto normaliseOpt( std::string const &optName ) -> std::string { + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); else return optName; } @@ -933,9 +927,9 @@ namespace detail { public: template - explicit Opt( LambdaT const &ref ) : ParserRefImpl(std::make_shared>(ref)) {} + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} - explicit Opt( bool &ref ) : ParserRefImpl(std::make_shared(ref)) {} + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} template Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} @@ -943,34 +937,34 @@ namespace detail { template Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} - auto operator[](std::string const &optName) -> Opt & { - m_optNames.push_back(optName); + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); return *this; } auto getHelpColumns() const -> std::vector { std::ostringstream oss; bool first = true; - for (auto const &opt : m_optNames) { + for( auto const &opt : m_optNames ) { if (first) first = false; else oss << ", "; oss << opt; } - if (!m_hint.empty()) + if( !m_hint.empty() ) oss << " <" << m_hint << ">"; - return {{oss.str(), m_description}}; + return { { oss.str(), m_description } }; } - auto isMatch(std::string const &optToken) const -> bool { + auto isMatch( std::string const &optToken ) const -> bool { #ifdef CLARA_PLATFORM_WINDOWS auto normalisedToken = normaliseOpt( optToken ); #else auto const &normalisedToken = optToken; #endif - for (auto const &name : m_optNames) { - if (normaliseOpt(name) == normalisedToken) + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) return true; } return false; @@ -980,46 +974,46 @@ namespace detail { auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { auto validationResult = validate(); - if (!validationResult) - return InternalParseResult(validationResult); + if( !validationResult ) + return InternalParseResult( validationResult ); auto remainingTokens = tokens; - if (remainingTokens && remainingTokens->type == TokenType::Option) { + 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)); + 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); + 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)); + 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::Matched, ++remainingTokens ) ); } } - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, 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"); - if (name[0] != '-' && name[0] != '/') - return Result::logicError("Option name must begin with '-' or '/'"); + 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" ); + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); } return ParserRefImpl::validate(); } @@ -1028,11 +1022,11 @@ namespace detail { struct Help : Opt { Help( bool &showHelpFlag ) : Opt([&]( bool flag ) { - showHelpFlag = flag; - return ParserResult::ok(ParseResultType::ShortCircuitAll); + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); }) { - static_cast(*this) + static_cast( *this ) ("display usage information") ["-?"]["-h"]["--help"] .optional(); @@ -1046,61 +1040,61 @@ namespace detail { std::vector m_options; std::vector m_args; - auto operator+=(ExeName const &exeName) -> Parser & { + auto operator|=( ExeName const &exeName ) -> Parser & { m_exeName = exeName; return *this; } - auto operator+=(Arg const &arg) -> Parser & { + auto operator|=( Arg const &arg ) -> Parser & { m_args.push_back(arg); return *this; } - auto operator+=(Opt const &opt) -> Parser & { + auto operator|=( Opt const &opt ) -> Parser & { m_options.push_back(opt); return *this; } - auto operator+=(Parser const &other) -> Parser & { + 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 - auto operator+(T const &other) const -> Parser { - return Parser(*this) += other; + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; } auto getHelpColumns() const -> std::vector { std::vector cols; for (auto const &o : m_options) { auto childCols = o.getHelpColumns(); - cols.insert(cols.end(), childCols.begin(), childCols.end()); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); } return cols; } - void writeToStream(std::ostream &os) const { + 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) { + for( auto const &arg : m_args ) { if (first) first = false; else os << " "; - if (arg.isOptional() && required) { + if( arg.isOptional() && required ) { os << "["; required = false; } os << "<" << arg.hint() << ">"; - if (arg.cardinality() == 0) + if( arg.cardinality() == 0 ) os << " ... "; } - if (!required) + if( !required ) os << "]"; - if (!m_options.empty()) + if( !m_options.empty() ) os << " options"; os << "\n\nwhere options are:" << std::endl; } @@ -1108,32 +1102,32 @@ namespace detail { auto rows = getHelpColumns(); size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; size_t optWidth = 0; - for (auto const &cols : rows) + for( auto const &cols : rows ) optWidth = std::max(optWidth, cols.left.size() + 2); - for (auto const &cols : rows) { + for( auto const &cols : rows ) { auto row = - TextFlow::Column(cols.left).width(optWidth).indent(2) + + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + TextFlow::Spacer(4) + - TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); + 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); + 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) { + for( auto const &opt : m_options ) { auto result = opt.validate(); - if (!result) + if( !result ) return result; } - for (auto const &arg : m_args) { + for( auto const &arg : m_args ) { auto result = arg.validate(); - if (!result) + if( !result ) return result; } return Result::ok(); @@ -1141,47 +1135,41 @@ namespace detail { using ParserBase::parse; - auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult override { - std::vector allParsers; - allParsers.reserve(m_args.size() + m_options.size()); - std::set requiredParsers; + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { - for (auto const &opt : m_options) { - allParsers.push_back(&opt); - if (!opt.isOptional()) - requiredParsers.insert(&opt); - } - - size_t optionalArgs = 0; - for (auto const &arg : m_args) { - allParsers.push_back(&arg); - if (!arg.isOptional()) { - if (optionalArgs > 0) - return InternalParseResult::logicError( - "Required arguments must preceed any optional arguments"); - else - ++optionalArgs; - requiredParsers.insert(&arg); - } - } + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + ParserInfo parseInfos[totalParsers]; + 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()) { - auto remainingTokenCount = result.value().remainingTokens().count(); - for (auto parser : allParsers) { - result = parser->parse( exeName, result.value().remainingTokens() ); - if (!result || result.value().type() != ParseResultType::NoMatch) { - if (parser->cardinality() == 1) - allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser), - allParsers.end()); - requiredParsers.erase(parser); - break; + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( auto& parseInfo : parseInfos ) { + 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 || remainingTokenCount == result.value().remainingTokens().count()) + + 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; @@ -1190,8 +1178,8 @@ namespace detail { template template - auto ComposableParserImpl::operator+(T const &other) const -> Parser { - return Parser() + static_cast( *this ) + other; + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; } } // namespace detail