New version of Clara

This commit is contained in:
Phil Nash 2017-09-25 15:12:00 -07:00
parent 9541e89e6a
commit 2a1f8ae684
3 changed files with 507 additions and 534 deletions

View File

@ -4,11 +4,13 @@
#ifndef CATCH_CLARA_HPP_INCLUDED #ifndef CATCH_CLARA_HPP_INCLUDED
#define CATCH_CLARA_HPP_INCLUDED #define CATCH_CLARA_HPP_INCLUDED
#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 #define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
#endif #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 ----------- // ----------- #included from clara_textflow.hpp -----------
@ -24,7 +26,6 @@
#ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED #ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
#define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED #define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
#include <cassert> #include <cassert>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
@ -350,9 +351,8 @@ namespace Catch { namespace clara { namespace TextFlow {
#include <set> #include <set>
#include <algorithm> #include <algorithm>
#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) #if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
#define CLARA_PLATFORM_WINDOWS #define CATCH_PLATFORM_WINDOWS
#endif #endif
namespace Catch { namespace clara { namespace Catch { namespace clara {
@ -360,15 +360,15 @@ namespace detail {
// Traits for extracting arg and return type of lambdas (for single argument lambdas) // Traits for extracting arg and return type of lambdas (for single argument lambdas)
template<typename L> template<typename L>
struct UnaryLambdaTraits : UnaryLambdaTraits<decltype(&L::operator())> {}; struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
template<typename ClassT, typename ReturnT, typename... Args> template<typename ClassT, typename ReturnT, typename... Args>
struct UnaryLambdaTraits<ReturnT(ClassT::*)(Args...) const> { struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
static const bool isValid = false; static const bool isValid = false;
}; };
template<typename ClassT, typename ReturnT, typename ArgT> template<typename ClassT, typename ReturnT, typename ArgT>
struct UnaryLambdaTraits<ReturnT(ClassT::*)(ArgT) const> { struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
static const bool isValid = true; static const bool isValid = true;
using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;; using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;;
using ReturnType = ReturnT; using ReturnType = ReturnT;
@ -383,13 +383,13 @@ namespace detail {
std::vector<std::string> m_args; std::vector<std::string> m_args;
public: public:
Args(int argc, char *argv[]) { Args( int argc, char *argv[] ) {
m_exeName = argv[0]; m_exeName = argv[0];
for (int i = 1; i < argc; ++i) for( int i = 1; i < argc; ++i )
m_args.push_back(argv[i]); m_args.push_back( argv[i] );
} }
Args(std::initializer_list<std::string> args) Args( std::initializer_list<std::string> args )
: m_exeName( *args.begin() ), : m_exeName( *args.begin() ),
m_args( args.begin()+1, args.end() ) m_args( args.begin()+1, args.end() )
{} {}
@ -417,40 +417,40 @@ namespace detail {
std::vector<Token> m_tokenBuffer; std::vector<Token> m_tokenBuffer;
void loadBuffer() { void loadBuffer() {
m_tokenBuffer.resize(0); m_tokenBuffer.resize( 0 );
// Skip any empty strings // Skip any empty strings
while (it != itEnd && it->empty()) while( it != itEnd && it->empty() )
++it; ++it;
if (it != itEnd) { if( it != itEnd ) {
auto const &next = *it; auto const &next = *it;
if (next[0] == '-' || next[0] == '/') { if( next[0] == '-' || next[0] == '/' ) {
auto delimiterPos = next.find_first_of(" :="); auto delimiterPos = next.find_first_of( " :=" );
if (delimiterPos != std::string::npos) { if( delimiterPos != std::string::npos ) {
m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
} else { } else {
if (next[1] != '-' && next.size() > 2) { if( next[1] != '-' && next.size() > 2 ) {
std::string opt = "- "; 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]; opt[1] = next[i];
m_tokenBuffer.push_back({TokenType::Option, opt}); m_tokenBuffer.push_back( { TokenType::Option, opt } );
} }
} else { } else {
m_tokenBuffer.push_back({TokenType::Option, next}); m_tokenBuffer.push_back( { TokenType::Option, next } );
} }
} }
} else { } else {
m_tokenBuffer.push_back({TokenType::Argument, next}); m_tokenBuffer.push_back( { TokenType::Argument, next } );
} }
} }
} }
public: 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(); loadBuffer();
} }
@ -461,20 +461,20 @@ namespace detail {
auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
auto operator*() const -> Token { auto operator*() const -> Token {
assert(!m_tokenBuffer.empty()); assert( !m_tokenBuffer.empty() );
return m_tokenBuffer.front(); return m_tokenBuffer.front();
} }
auto operator->() const -> Token const * { auto operator->() const -> Token const * {
assert(!m_tokenBuffer.empty()); assert( !m_tokenBuffer.empty() );
return &m_tokenBuffer.front(); return &m_tokenBuffer.front();
} }
auto operator++() -> TokenStream & { auto operator++() -> TokenStream & {
if (m_tokenBuffer.size() >= 2) { if( m_tokenBuffer.size() >= 2 ) {
m_tokenBuffer.erase(m_tokenBuffer.begin()); m_tokenBuffer.erase( m_tokenBuffer.begin() );
} else { } else {
if (it != itEnd) if( it != itEnd )
++it; ++it;
loadBuffer(); loadBuffer();
} }
@ -490,7 +490,7 @@ namespace detail {
}; };
protected: protected:
ResultBase(Type type) : m_type(type) {} ResultBase( Type type ) : m_type( type ) {}
virtual ~ResultBase() = default; virtual ~ResultBase() = default;
virtual void enforceOk() const = 0; virtual void enforceOk() const = 0;
@ -507,28 +507,28 @@ namespace detail {
} }
protected: protected:
ResultValueBase(Type type) : ResultBase(type) {} ResultValueBase( Type type ) : ResultBase( type ) {}
ResultValueBase(ResultValueBase const &other) : ResultBase(other) { ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
if (m_type == ResultBase::Ok) if( m_type == ResultBase::Ok )
new(&m_value) T(other.m_value); new( &m_value ) T( other.m_value );
} }
ResultValueBase(Type, T const &value) : ResultBase(Ok) { ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
new(&m_value) T(value); new( &m_value ) T( value );
} }
auto operator=(ResultValueBase const &other) -> ResultValueBase & { auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
if (m_type == ResultBase::Ok) if( m_type == ResultBase::Ok )
m_value.~T(); m_value.~T();
ResultBase::operator=(other); ResultBase::operator=(other);
if (m_type == ResultBase::Ok) if( m_type == ResultBase::Ok )
new(&m_value) T(other.m_value); new( &m_value ) T( other.m_value );
return *this; return *this;
} }
~ResultValueBase() { ~ResultValueBase() {
if (m_type == Ok) if( m_type == Ok )
m_value.~T(); m_value.~T();
} }
@ -547,37 +547,31 @@ namespace detail {
class BasicResult : public ResultValueBase<T> { class BasicResult : public ResultValueBase<T> {
public: public:
template<typename U> template<typename U>
explicit BasicResult(BasicResult<U> const &other) explicit BasicResult( BasicResult<U> const &other )
: ResultValueBase<T>(other.type()), : ResultValueBase<T>( other.type() ),
m_errorMessage(other.errorMessage()) { m_errorMessage( other.errorMessage() )
assert(type() != ResultBase::Ok); {
assert( type() != ResultBase::Ok );
} }
static auto ok() -> BasicResult { return {ResultBase::Ok}; }
template<typename U> template<typename U>
static auto ok(U const &value) -> BasicResult { return {ResultBase::Ok, value}; } 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 logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
static auto runtimeError(std::string const &message) -> BasicResult {
return {ResultBase::RuntimeError, message};
}
explicit operator bool() const { return m_type == ResultBase::Ok; } explicit operator bool() const { return m_type == ResultBase::Ok; }
auto type() const -> ResultBase::Type { return m_type; } auto type() const -> ResultBase::Type { return m_type; }
auto errorMessage() const -> std::string { return m_errorMessage; } auto errorMessage() const -> std::string { return m_errorMessage; }
protected: protected:
virtual void enforceOk() const { virtual void enforceOk() const {
// !TBD: If no exceptions, std::terminate here or something // !TBD: If no exceptions, std::terminate here or something
switch (m_type) { switch( m_type ) {
case ResultBase::LogicError: case ResultBase::LogicError:
throw std::logic_error(m_errorMessage); throw std::logic_error( m_errorMessage );
case ResultBase::RuntimeError: case ResultBase::RuntimeError:
throw std::runtime_error(m_errorMessage); throw std::runtime_error( m_errorMessage );
case ResultBase::Ok: case ResultBase::Ok:
break; break;
} }
@ -585,10 +579,11 @@ namespace detail {
std::string m_errorMessage; // Only populated if resultType is an error std::string m_errorMessage; // Only populated if resultType is an error
BasicResult(ResultBase::Type type, std::string const &message) BasicResult( ResultBase::Type type, std::string const &message )
: ResultValueBase<T>(type), : ResultValueBase<T>(type),
m_errorMessage(message) { m_errorMessage(message)
assert(m_type != ResultBase::Ok); {
assert( m_type != ResultBase::Ok );
} }
using ResultValueBase<T>::ResultValueBase; using ResultValueBase<T>::ResultValueBase;
@ -602,12 +597,12 @@ namespace detail {
class ParseState { class ParseState {
public: public:
ParseState(ParseResultType type, TokenStream const &remainingTokens) ParseState( ParseResultType type, TokenStream const &remainingTokens )
: m_type(type), : m_type(type),
m_remainingTokens(remainingTokens) {} m_remainingTokens( remainingTokens )
{}
auto type() const -> ParseResultType { return m_type; } auto type() const -> ParseResultType { return m_type; }
auto remainingTokens() const -> TokenStream { return m_remainingTokens; } auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
private: private:
@ -625,69 +620,62 @@ namespace detail {
}; };
template<typename T> template<typename T>
inline auto convertInto(std::string const &source, T& target) -> ParserResult { inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
std::stringstream ss; std::stringstream ss;
ss << source; ss << source;
ss >> target; ss >> target;
if (ss.fail()) if( ss.fail() )
return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
else 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; 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::string srcLC = source;
std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), [](char c) { return static_cast<char>( ::tolower(c) ); } ); 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") if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
target = true; target = true;
else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
target = false; target = false;
else else
return ParserResult::runtimeError("Expected a boolean value but did not recognise: '" + source + "'"); return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
return ParserResult::ok(ParseResultType::Matched); return ParserResult::ok( ParseResultType::Matched );
} }
struct BoundRefBase { struct BoundRefBase {
BoundRefBase() = default; BoundRefBase() = default;
BoundRefBase( BoundRefBase const & ) = delete;
BoundRefBase(BoundRefBase const &) = delete; BoundRefBase( BoundRefBase && ) = delete;
BoundRefBase &operator=( BoundRefBase const & ) = delete;
BoundRefBase(BoundRefBase &&) = delete; BoundRefBase &operator=( BoundRefBase && ) = delete;
BoundRefBase &operator=(BoundRefBase const &) = delete;
BoundRefBase &operator=(BoundRefBase &&) = delete;
virtual ~BoundRefBase() = default; virtual ~BoundRefBase() = default;
virtual auto isFlag() const -> bool = 0; virtual auto isFlag() const -> bool = 0;
virtual auto isContainer() const -> bool { return false; } virtual auto isContainer() const -> bool { return false; }
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
virtual auto setValue(std::string const &arg) -> ParserResult = 0; virtual auto setFlag( bool flag ) -> ParserResult = 0;
virtual auto setFlag(bool flag) -> ParserResult = 0;
}; };
struct BoundValueRefBase : BoundRefBase { struct BoundValueRefBase : BoundRefBase {
auto isFlag() const -> bool override { return false; } auto isFlag() const -> bool override { return false; }
auto setFlag(bool) -> ParserResult override { auto setFlag( bool ) -> ParserResult override {
return ParserResult::logicError("Flags can only be set on boolean fields"); return ParserResult::logicError( "Flags can only be set on boolean fields" );
} }
}; };
struct BoundFlagRefBase : BoundRefBase { struct BoundFlagRefBase : BoundRefBase {
auto isFlag() const -> bool override { return true; } 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; bool flag;
auto result = convertInto(arg, flag); auto result = convertInto( arg, flag );
if (result) if( result )
setFlag(flag); setFlag( flag );
return result; return result;
} }
}; };
@ -696,10 +684,10 @@ namespace detail {
struct BoundRef : BoundValueRefBase { struct BoundRef : BoundValueRefBase {
T &m_ref; 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 { auto setValue( std::string const &arg ) -> ParserResult override {
return convertInto(arg, m_ref); return convertInto( arg, m_ref );
} }
}; };
@ -707,15 +695,15 @@ namespace detail {
struct BoundRef<std::vector<T>> : BoundValueRefBase { struct BoundRef<std::vector<T>> : BoundValueRefBase {
std::vector<T> &m_ref; std::vector<T> &m_ref;
explicit BoundRef(std::vector<T> &ref) : m_ref(ref) {} explicit BoundRef( std::vector<T> &ref ) : m_ref( ref ) {}
auto isContainer() const -> bool override { return true; } 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; T temp;
auto result = convertInto(arg, temp); auto result = convertInto( arg, temp );
if (result) if( result )
m_ref.push_back(temp); m_ref.push_back( temp );
return result; return result;
} }
}; };
@ -723,40 +711,40 @@ namespace detail {
struct BoundFlagRef : BoundFlagRefBase { struct BoundFlagRef : BoundFlagRefBase {
bool &m_ref; 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; m_ref = flag;
return ParserResult::ok(ParseResultType::Matched); return ParserResult::ok( ParseResultType::Matched );
} }
}; };
template<typename ReturnType> template<typename ReturnType>
struct LambdaInvoker { struct LambdaInvoker {
static_assert(std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult"); static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
template<typename L, typename ArgType> template<typename L, typename ArgType>
static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
return lambda(arg); return lambda( arg );
} }
}; };
template<> template<>
struct LambdaInvoker<void> { struct LambdaInvoker<void> {
template<typename L, typename ArgType> template<typename L, typename ArgType>
static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
lambda(arg); lambda( arg );
return ParserResult::ok(ParseResultType::Matched); return ParserResult::ok( ParseResultType::Matched );
} }
}; };
template<typename ArgType, typename L> template<typename ArgType, typename L>
inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
ArgType temp; ArgType temp;
auto result = convertInto(arg, temp); auto result = convertInto( arg, temp );
return !result return !result
? result ? result
: LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke(lambda, temp); : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
}; };
@ -765,10 +753,10 @@ namespace detail {
L m_lambda; L m_lambda;
static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); static_assert( UnaryLambdaTraits<L>::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 { auto setValue( std::string const &arg ) -> ParserResult override {
return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>(m_lambda, arg); return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
} }
}; };
@ -779,16 +767,14 @@ namespace detail {
static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); 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" ); static_assert( std::is_same<typename UnaryLambdaTraits<L>::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 { auto setFlag( bool flag ) -> ParserResult override {
return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke(m_lambda, flag); return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
} }
}; };
enum class Optionality { enum class Optionality { Optional, Required };
Optional, Required
};
struct Parser; struct Parser;
@ -799,8 +785,8 @@ namespace detail {
virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0;
virtual auto cardinality() const -> size_t { return 1; } virtual auto cardinality() const -> size_t { return 1; }
auto parse(Args const &args) const -> InternalParseResult { auto parse( Args const &args ) const -> InternalParseResult {
return parse( args.exeName(), TokenStream(args)); return parse( args.exeName(), TokenStream( args ) );
} }
}; };
@ -808,7 +794,7 @@ namespace detail {
class ComposableParserImpl : public ParserBase { class ComposableParserImpl : public ParserBase {
public: public:
template<typename T> template<typename T>
auto operator+(T const &other) const -> Parser; auto operator|( T const &other ) const -> Parser;
}; };
// Common code and state for Args and Opts // Common code and state for Args and Opts
@ -820,17 +806,22 @@ namespace detail {
std::string m_hint; std::string m_hint;
std::string m_description; std::string m_description;
explicit ParserRefImpl(std::shared_ptr<BoundRefBase> const &ref) : m_ref(ref) {} explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {}
public: public:
template<typename T> template<typename T>
ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared<BoundRef<T>>(ref)), m_hint(hint) {} ParserRefImpl( T &ref, std::string const &hint )
: m_ref( std::make_shared<BoundRef<T>>( ref ) ),
m_hint( hint )
{}
template<typename LambdaT> template<typename LambdaT>
ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared<BoundLambda<LambdaT>>(ref)), ParserRefImpl( LambdaT const &ref, std::string const &hint )
m_hint(hint) {} : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
m_hint(hint)
{}
auto operator()(std::string const &description) -> DerivedT & { auto operator()( std::string const &description ) -> DerivedT & {
m_description = description; m_description = description;
return static_cast<DerivedT &>( *this ); return static_cast<DerivedT &>( *this );
} }
@ -850,7 +841,7 @@ namespace detail {
} }
auto cardinality() const -> size_t override { auto cardinality() const -> size_t override {
if (m_ref->isContainer()) if( m_ref->isContainer() )
return 0; return 0;
else else
return 1; return 1;
@ -865,13 +856,13 @@ namespace detail {
template<typename LambdaT> template<typename LambdaT>
static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> { static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> {
return std::make_shared<BoundLambda<LambdaT>>(lambda); return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
} }
public: public:
ExeName() : m_name(std::make_shared<std::string>("<executable>")) {} ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
explicit ExeName(std::string &ref) : ExeName() { explicit ExeName( std::string &ref ) : ExeName() {
m_ref = std::make_shared<BoundRef<std::string>>( ref ); m_ref = std::make_shared<BoundRef<std::string>>( ref );
} }
@ -882,14 +873,14 @@ namespace detail {
// The exe name is not parsed out of the normal tokens, but is handled specially // 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 { 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 name() const -> std::string { return *m_name; }
auto set( std::string const& newName ) -> ParserResult { auto set( std::string const& newName ) -> ParserResult {
auto lastSlash = newName.find_last_of( "\\/" ); auto lastSlash = newName.find_last_of( "\\/" );
auto filename = (lastSlash == std::string::npos) auto filename = ( lastSlash == std::string::npos )
? newName ? newName
: newName.substr( lastSlash+1 ); : newName.substr( lastSlash+1 );
@ -905,27 +896,27 @@ namespace detail {
public: public:
using ParserRefImpl::ParserRefImpl; 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(); auto validationResult = validate();
if (!validationResult) if( !validationResult )
return InternalParseResult(validationResult); return InternalParseResult( validationResult );
auto remainingTokens = tokens; auto remainingTokens = tokens;
auto const &token = *remainingTokens; auto const &token = *remainingTokens;
if (token.type != TokenType::Argument) if( token.type != TokenType::Argument )
return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
auto result = m_ref->setValue(remainingTokens->token); auto result = m_ref->setValue( remainingTokens->token );
if (!result) if( !result )
return InternalParseResult(result); return InternalParseResult( result );
else else
return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
} }
}; };
inline auto normaliseOpt(std::string const &optName) -> std::string { inline auto normaliseOpt( std::string const &optName ) -> std::string {
if (optName[0] == '/') if( optName[0] == '/' )
return "-" + optName.substr(1); return "-" + optName.substr( 1 );
else else
return optName; return optName;
} }
@ -936,9 +927,9 @@ namespace detail {
public: public:
template<typename LambdaT> template<typename LambdaT>
explicit Opt( LambdaT const &ref ) : ParserRefImpl(std::make_shared<BoundFlagLambda<LambdaT>>(ref)) {} explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
explicit Opt( bool &ref ) : ParserRefImpl(std::make_shared<BoundFlagRef>(ref)) {} explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
template<typename LambdaT> template<typename LambdaT>
Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
@ -946,34 +937,34 @@ namespace detail {
template<typename T> template<typename T>
Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
auto operator[](std::string const &optName) -> Opt & { auto operator[]( std::string const &optName ) -> Opt & {
m_optNames.push_back(optName); m_optNames.push_back( optName );
return *this; return *this;
} }
auto getHelpColumns() const -> std::vector<HelpColumns> { auto getHelpColumns() const -> std::vector<HelpColumns> {
std::ostringstream oss; std::ostringstream oss;
bool first = true; bool first = true;
for (auto const &opt : m_optNames) { for( auto const &opt : m_optNames ) {
if (first) if (first)
first = false; first = false;
else else
oss << ", "; oss << ", ";
oss << opt; oss << opt;
} }
if (!m_hint.empty()) if( !m_hint.empty() )
oss << " <" << m_hint << ">"; 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 #ifdef CATCH_PLATFORM_WINDOWS
auto normalisedToken = normaliseOpt( optToken ); auto normalisedToken = normaliseOpt( optToken );
#else #else
auto const &normalisedToken = optToken; auto const &normalisedToken = optToken;
#endif #endif
for (auto const &name : m_optNames) { for( auto const &name : m_optNames ) {
if (normaliseOpt(name) == normalisedToken) if( normaliseOpt( name ) == normalisedToken )
return true; return true;
} }
return false; return false;
@ -983,46 +974,46 @@ namespace detail {
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(); auto validationResult = validate();
if (!validationResult) if( !validationResult )
return InternalParseResult(validationResult); return InternalParseResult( validationResult );
auto remainingTokens = tokens; auto remainingTokens = tokens;
if (remainingTokens && remainingTokens->type == TokenType::Option) { if( remainingTokens && remainingTokens->type == TokenType::Option ) {
auto const &token = *remainingTokens; auto const &token = *remainingTokens;
if (isMatch(token.token)) { if( isMatch(token.token ) ) {
if (m_ref->isFlag()) { if( m_ref->isFlag() ) {
auto result = m_ref->setFlag(true); auto result = m_ref->setFlag( true );
if (!result) if( !result )
return InternalParseResult(result); return InternalParseResult( result );
if (result.value() == ParseResultType::ShortCircuitAll) if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
} else { } else {
++remainingTokens; ++remainingTokens;
if (!remainingTokens) if( !remainingTokens )
return InternalParseResult::runtimeError("Expected argument following " + token.token); return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto const &argToken = *remainingTokens; auto const &argToken = *remainingTokens;
if (argToken.type != TokenType::Argument) if( argToken.type != TokenType::Argument )
return InternalParseResult::runtimeError("Expected argument following " + token.token); return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto result = m_ref->setValue(argToken.token); auto result = m_ref->setValue( argToken.token );
if (!result) if( !result )
return InternalParseResult(result); return InternalParseResult( result );
if (result.value() == ParseResultType::ShortCircuitAll) if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); 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 { auto validate() const -> Result override {
if (m_optNames.empty()) if( m_optNames.empty() )
return Result::logicError("No options supplied to Opt"); return Result::logicError( "No options supplied to Opt" );
for (auto const &name : m_optNames) { for( auto const &name : m_optNames ) {
if (name.empty()) if( name.empty() )
return Result::logicError("Option name cannot be empty"); return Result::logicError( "Option name cannot be empty" );
if (name[0] != '-' && name[0] != '/') if( name[0] != '-' && name[0] != '/' )
return Result::logicError("Option name must begin with '-' or '/'"); return Result::logicError( "Option name must begin with '-' or '/'" );
} }
return ParserRefImpl::validate(); return ParserRefImpl::validate();
} }
@ -1032,10 +1023,10 @@ namespace detail {
Help( bool &showHelpFlag ) Help( bool &showHelpFlag )
: Opt([&]( bool flag ) { : Opt([&]( bool flag ) {
showHelpFlag = flag; showHelpFlag = flag;
return ParserResult::ok(ParseResultType::ShortCircuitAll); return ParserResult::ok( ParseResultType::ShortCircuitAll );
}) })
{ {
static_cast<Opt &>(*this) static_cast<Opt &>( *this )
("display usage information") ("display usage information")
["-?"]["-h"]["--help"] ["-?"]["-h"]["--help"]
.optional(); .optional();
@ -1049,61 +1040,61 @@ namespace detail {
std::vector<Opt> m_options; std::vector<Opt> m_options;
std::vector<Arg> m_args; std::vector<Arg> m_args;
auto operator+=(ExeName const &exeName) -> Parser & { auto operator|=( ExeName const &exeName ) -> Parser & {
m_exeName = exeName; m_exeName = exeName;
return *this; return *this;
} }
auto operator+=(Arg const &arg) -> Parser & { auto operator|=( Arg const &arg ) -> Parser & {
m_args.push_back(arg); m_args.push_back(arg);
return *this; return *this;
} }
auto operator+=(Opt const &opt) -> Parser & { auto operator|=( Opt const &opt ) -> Parser & {
m_options.push_back(opt); m_options.push_back(opt);
return *this; 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_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()); m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
return *this; return *this;
} }
template<typename T> template<typename T>
auto operator+(T const &other) const -> Parser { auto operator|( T const &other ) const -> Parser {
return Parser(*this) += other; return Parser( *this ) |= other;
} }
auto getHelpColumns() const -> std::vector<HelpColumns> { auto getHelpColumns() const -> std::vector<HelpColumns> {
std::vector<HelpColumns> cols; std::vector<HelpColumns> cols;
for (auto const &o : m_options) { for (auto const &o : m_options) {
auto childCols = o.getHelpColumns(); auto childCols = o.getHelpColumns();
cols.insert(cols.end(), childCols.begin(), childCols.end()); cols.insert( cols.end(), childCols.begin(), childCols.end() );
} }
return cols; return cols;
} }
void writeToStream(std::ostream &os) const { void writeToStream( std::ostream &os ) const {
if (!m_exeName.name().empty()) { if (!m_exeName.name().empty()) {
os << "usage:\n" << " " << m_exeName.name() << " "; os << "usage:\n" << " " << m_exeName.name() << " ";
bool required = true, first = true; bool required = true, first = true;
for (auto const &arg : m_args) { for( auto const &arg : m_args ) {
if (first) if (first)
first = false; first = false;
else else
os << " "; os << " ";
if (arg.isOptional() && required) { if( arg.isOptional() && required ) {
os << "["; os << "[";
required = false; required = false;
} }
os << "<" << arg.hint() << ">"; os << "<" << arg.hint() << ">";
if (arg.cardinality() == 0) if( arg.cardinality() == 0 )
os << " ... "; os << " ... ";
} }
if (!required) if( !required )
os << "]"; os << "]";
if (!m_options.empty()) if( !m_options.empty() )
os << " options"; os << " options";
os << "\n\nwhere options are:" << std::endl; os << "\n\nwhere options are:" << std::endl;
} }
@ -1111,32 +1102,32 @@ namespace detail {
auto rows = getHelpColumns(); auto rows = getHelpColumns();
size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
size_t optWidth = 0; size_t optWidth = 0;
for (auto const &cols : rows) for( auto const &cols : rows )
optWidth = std::max(optWidth, cols.left.size() + 2); optWidth = std::max(optWidth, cols.left.size() + 2);
for (auto const &cols : rows) { for( auto const &cols : rows ) {
auto row = auto row =
TextFlow::Column(cols.left).width(optWidth).indent(2) + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
TextFlow::Spacer(4) + TextFlow::Spacer(4) +
TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
os << row << std::endl; os << row << std::endl;
} }
} }
friend auto operator<<(std::ostream &os, Parser const &parser) -> std::ostream & { friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
parser.writeToStream(os); parser.writeToStream( os );
return os; return os;
} }
auto validate() const -> Result override { auto validate() const -> Result override {
for (auto const &opt : m_options) { for( auto const &opt : m_options ) {
auto result = opt.validate(); auto result = opt.validate();
if (!result) if( !result )
return result; return result;
} }
for (auto const &arg : m_args) { for( auto const &arg : m_args ) {
auto result = arg.validate(); auto result = arg.validate();
if (!result) if( !result )
return result; return result;
} }
return Result::ok(); return Result::ok();
@ -1144,47 +1135,41 @@ namespace detail {
using ParserBase::parse; using ParserBase::parse;
auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult override { auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
std::vector<ParserBase const *> allParsers;
allParsers.reserve(m_args.size() + m_options.size());
std::set<ParserBase const *> requiredParsers;
for (auto const &opt : m_options) { struct ParserInfo {
allParsers.push_back(&opt); ParserBase const* parser = nullptr;
if (!opt.isOptional()) size_t count = 0;
requiredParsers.insert(&opt); };
} const size_t totalParsers = m_options.size() + m_args.size();
ParserInfo parseInfos[totalParsers];
size_t optionalArgs = 0; size_t i = 0;
for (auto const &arg : m_args) { for( auto const& opt : m_options ) parseInfos[i++].parser = &opt;
allParsers.push_back(&arg); for( auto const& arg : m_args ) parseInfos[i++].parser = &arg;
if (!arg.isOptional()) {
if (optionalArgs > 0)
return InternalParseResult::logicError(
"Required arguments must preceed any optional arguments");
else
++optionalArgs;
requiredParsers.insert(&arg);
}
}
m_exeName.set( exeName ); m_exeName.set( exeName );
auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
while (result.value().remainingTokens()) { while( result.value().remainingTokens() ) {
auto remainingTokenCount = result.value().remainingTokens().count(); bool tokenParsed = false;
for (auto parser : allParsers) {
result = parser->parse( exeName, result.value().remainingTokens() ); for( auto& parseInfo : parseInfos ) {
if (!result || result.value().type() != ParseResultType::NoMatch) { if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
if (parser->cardinality() == 1) result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser), if (!result)
allParsers.end()); return result;
requiredParsers.erase(parser); if (result.value().type() != ParseResultType::NoMatch) {
tokenParsed = true;
++parseInfo.count;
break; break;
} }
} }
if (!result || remainingTokenCount == result.value().remainingTokens().count()) }
if( result.value().type() == ParseResultType::ShortCircuitAll )
return result; return result;
if( !tokenParsed )
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
} }
// !TBD Check missing required options // !TBD Check missing required options
return result; return result;
@ -1193,8 +1178,8 @@ namespace detail {
template<typename DerivedT> template<typename DerivedT>
template<typename T> template<typename T>
auto ComposableParserImpl<DerivedT>::operator+(T const &other) const -> Parser { auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
return Parser() + static_cast<DerivedT const &>( *this ) + other; return Parser() | static_cast<DerivedT const &>( *this ) | other;
} }
} // namespace detail } // namespace detail

View File

@ -98,84 +98,84 @@ namespace Catch {
auto cli auto cli
= ExeName( config.processName ) = ExeName( config.processName )
+ Help( config.showHelp ) | Help( config.showHelp )
+ Opt( config.listTests ) | Opt( config.listTests )
["-l"]["--list-tests"] ["-l"]["--list-tests"]
( "list all/matching test cases" ) ( "list all/matching test cases" )
+ Opt( config.listTags ) | Opt( config.listTags )
["-t"]["--list-tags"] ["-t"]["--list-tags"]
( "list all/matching tags" ) ( "list all/matching tags" )
+ Opt( config.showSuccessfulTests ) | Opt( config.showSuccessfulTests )
["-s"]["--success"] ["-s"]["--success"]
( "include successful tests in output" ) ( "include successful tests in output" )
+ Opt( config.shouldDebugBreak ) | Opt( config.shouldDebugBreak )
["-b"]["--break"] ["-b"]["--break"]
( "break into debugger on failure" ) ( "break into debugger on failure" )
+ Opt( config.noThrow ) | Opt( config.noThrow )
["-e"]["--nothrow"] ["-e"]["--nothrow"]
( "skip exception tests" ) ( "skip exception tests" )
+ Opt( config.showInvisibles ) | Opt( config.showInvisibles )
["-i"]["--invisibles"] ["-i"]["--invisibles"]
( "show invisibles (tabs, newlines)" ) ( "show invisibles (tabs, newlines)" )
+ Opt( config.outputFilename, "filename" ) | Opt( config.outputFilename, "filename" )
["-o"]["--out"] ["-o"]["--out"]
( "output filename" ) ( "output filename" )
+ Opt( config.reporterNames, "name" ) | Opt( config.reporterNames, "name" )
["-r"]["--reporter"] ["-r"]["--reporter"]
( "reporter to use (defaults to console)" ) ( "reporter to use (defaults to console)" )
+ Opt( config.name, "name" ) | Opt( config.name, "name" )
["-n"]["--name"] ["-n"]["--name"]
( "suite name" ) ( "suite name" )
+ Opt( [&]( bool ){ config.abortAfter = 1; } ) | Opt( [&]( bool ){ config.abortAfter = 1; } )
["-a"]["--abort"] ["-a"]["--abort"]
( "abort at first failure" ) ( "abort at first failure" )
+ Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
["-x"]["--abortx"] ["-x"]["--abortx"]
( "abort after x failures" ) ( "abort after x failures" )
+ Opt( setWarning, "warning name" ) | Opt( setWarning, "warning name" )
["-w"]["--warn"] ["-w"]["--warn"]
( "enable warnings" ) ( "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"] ["-d"]["--durations"]
( "show test durations" ) ( "show test durations" )
+ Opt( loadTestNamesFromFile, "filename" ) | Opt( loadTestNamesFromFile, "filename" )
["-f"]["--input-file"] ["-f"]["--input-file"]
( "load test names to run from a file" ) ( "load test names to run from a file" )
+ Opt( config.filenamesAsTags ) | Opt( config.filenamesAsTags )
["-#"]["--filenames-as-tags"] ["-#"]["--filenames-as-tags"]
( "adds a tag for the filename" ) ( "adds a tag for the filename" )
+ Opt( config.sectionsToRun, "section name" ) | Opt( config.sectionsToRun, "section name" )
["-c"]["--section"] ["-c"]["--section"]
( "specify section to run" ) ( "specify section to run" )
+ Opt( setVerbosity, "quiet|normal|high" ) | Opt( setVerbosity, "quiet|normal|high" )
["-v"]["--verbosity"] ["-v"]["--verbosity"]
( "set output verbosity" ) ( "set output verbosity" )
+ Opt( config.listTestNamesOnly ) | Opt( config.listTestNamesOnly )
["--list-test-names-only"] ["--list-test-names-only"]
( "list all/matching test cases names only" ) ( "list all/matching test cases names only" )
+ Opt( config.listReporters ) | Opt( config.listReporters )
["--list-reporters"] ["--list-reporters"]
( "list all reporters" ) ( "list all reporters" )
+ Opt( setTestOrder, "decl|lex|rand" ) | Opt( setTestOrder, "decl|lex|rand" )
["--order"] ["--order"]
( "test case order (defaults to decl)" ) ( "test case order (defaults to decl)" )
+ Opt( setRngSeed, "'time'|number" ) | Opt( setRngSeed, "'time'|number" )
["--rng-seed"] ["--rng-seed"]
( "set a specific seed for random numbers" ) ( "set a specific seed for random numbers" )
+ Opt( setColourUsage, "yes|no" ) | Opt( setColourUsage, "yes|no" )
["--use-colour"] ["--use-colour"]
( "should output be colourised" ) ( "should output be colourised" )
+ Opt( config.libIdentify ) | Opt( config.libIdentify )
["--libidentify"] ["--libidentify"]
( "report name and version according to libidentify standard" ) ( "report name and version according to libidentify standard" )
+ Opt( setWaitForKeypress, "start|exit|both" ) | Opt( setWaitForKeypress, "start|exit|both" )
["--wait-for-keypress"] ["--wait-for-keypress"]
( "waits for a keypress before exiting" ) ( "waits for a keypress before exiting" )
+ Opt( config.benchmarkResolutionMultiple, "multiplier" ) | Opt( config.benchmarkResolutionMultiple, "multiplier" )
["--benchmark-resolution-multiple"] ["--benchmark-resolution-multiple"]
( "multiple of clock resolution to run benchmarks" ) ( "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" ); ( "which test or tests to use" );
return cli; return cli;

480
third_party/clara.hpp vendored
View File

@ -8,6 +8,9 @@
#define CLARA_CONFIG_CONSOLE_WIDTH 80 #define CLARA_CONFIG_CONSOLE_WIDTH 80
#endif #endif
#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
#endif
// ----------- #included from clara_textflow.hpp ----------- // ----------- #included from clara_textflow.hpp -----------
@ -133,7 +136,7 @@ namespace clara { namespace TextFlow {
auto operator *() const -> std::string { auto operator *() const -> std::string {
assert( m_stringIndex < m_column.m_strings.size() ); 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 ) if( m_pos + m_column.m_width < m_end )
return addIndentAndSuffix(line().substr(m_pos, m_len)); return addIndentAndSuffix(line().substr(m_pos, m_len));
else else
@ -357,15 +360,15 @@ namespace detail {
// Traits for extracting arg and return type of lambdas (for single argument lambdas) // Traits for extracting arg and return type of lambdas (for single argument lambdas)
template<typename L> template<typename L>
struct UnaryLambdaTraits : UnaryLambdaTraits<decltype(&L::operator())> {}; struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
template<typename ClassT, typename ReturnT, typename... Args> template<typename ClassT, typename ReturnT, typename... Args>
struct UnaryLambdaTraits<ReturnT(ClassT::*)(Args...) const> { struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
static const bool isValid = false; static const bool isValid = false;
}; };
template<typename ClassT, typename ReturnT, typename ArgT> template<typename ClassT, typename ReturnT, typename ArgT>
struct UnaryLambdaTraits<ReturnT(ClassT::*)(ArgT) const> { struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
static const bool isValid = true; static const bool isValid = true;
using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;; using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;;
using ReturnType = ReturnT; using ReturnType = ReturnT;
@ -380,13 +383,13 @@ namespace detail {
std::vector<std::string> m_args; std::vector<std::string> m_args;
public: public:
Args(int argc, char *argv[]) { Args( int argc, char *argv[] ) {
m_exeName = argv[0]; m_exeName = argv[0];
for (int i = 1; i < argc; ++i) for( int i = 1; i < argc; ++i )
m_args.push_back(argv[i]); m_args.push_back( argv[i] );
} }
Args(std::initializer_list<std::string> args) Args( std::initializer_list<std::string> args )
: m_exeName( *args.begin() ), : m_exeName( *args.begin() ),
m_args( args.begin()+1, args.end() ) m_args( args.begin()+1, args.end() )
{} {}
@ -414,40 +417,40 @@ namespace detail {
std::vector<Token> m_tokenBuffer; std::vector<Token> m_tokenBuffer;
void loadBuffer() { void loadBuffer() {
m_tokenBuffer.resize(0); m_tokenBuffer.resize( 0 );
// Skip any empty strings // Skip any empty strings
while (it != itEnd && it->empty()) while( it != itEnd && it->empty() )
++it; ++it;
if (it != itEnd) { if( it != itEnd ) {
auto const &next = *it; auto const &next = *it;
if (next[0] == '-' || next[0] == '/') { if( next[0] == '-' || next[0] == '/' ) {
auto delimiterPos = next.find_first_of(" :="); auto delimiterPos = next.find_first_of( " :=" );
if (delimiterPos != std::string::npos) { if( delimiterPos != std::string::npos ) {
m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
} else { } else {
if (next[1] != '-' && next.size() > 2) { if( next[1] != '-' && next.size() > 2 ) {
std::string opt = "- "; 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]; opt[1] = next[i];
m_tokenBuffer.push_back({TokenType::Option, opt}); m_tokenBuffer.push_back( { TokenType::Option, opt } );
} }
} else { } else {
m_tokenBuffer.push_back({TokenType::Option, next}); m_tokenBuffer.push_back( { TokenType::Option, next } );
} }
} }
} else { } else {
m_tokenBuffer.push_back({TokenType::Argument, next}); m_tokenBuffer.push_back( { TokenType::Argument, next } );
} }
} }
} }
public: 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(); loadBuffer();
} }
@ -458,20 +461,20 @@ namespace detail {
auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
auto operator*() const -> Token { auto operator*() const -> Token {
assert(!m_tokenBuffer.empty()); assert( !m_tokenBuffer.empty() );
return m_tokenBuffer.front(); return m_tokenBuffer.front();
} }
auto operator->() const -> Token const * { auto operator->() const -> Token const * {
assert(!m_tokenBuffer.empty()); assert( !m_tokenBuffer.empty() );
return &m_tokenBuffer.front(); return &m_tokenBuffer.front();
} }
auto operator++() -> TokenStream & { auto operator++() -> TokenStream & {
if (m_tokenBuffer.size() >= 2) { if( m_tokenBuffer.size() >= 2 ) {
m_tokenBuffer.erase(m_tokenBuffer.begin()); m_tokenBuffer.erase( m_tokenBuffer.begin() );
} else { } else {
if (it != itEnd) if( it != itEnd )
++it; ++it;
loadBuffer(); loadBuffer();
} }
@ -487,7 +490,7 @@ namespace detail {
}; };
protected: protected:
ResultBase(Type type) : m_type(type) {} ResultBase( Type type ) : m_type( type ) {}
virtual ~ResultBase() = default; virtual ~ResultBase() = default;
virtual void enforceOk() const = 0; virtual void enforceOk() const = 0;
@ -504,28 +507,28 @@ namespace detail {
} }
protected: protected:
ResultValueBase(Type type) : ResultBase(type) {} ResultValueBase( Type type ) : ResultBase( type ) {}
ResultValueBase(ResultValueBase const &other) : ResultBase(other) { ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
if (m_type == ResultBase::Ok) if( m_type == ResultBase::Ok )
new(&m_value) T(other.m_value); new( &m_value ) T( other.m_value );
} }
ResultValueBase(Type, T const &value) : ResultBase(Ok) { ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
new(&m_value) T(value); new( &m_value ) T( value );
} }
auto operator=(ResultValueBase const &other) -> ResultValueBase & { auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
if (m_type == ResultBase::Ok) if( m_type == ResultBase::Ok )
m_value.~T(); m_value.~T();
ResultBase::operator=(other); ResultBase::operator=(other);
if (m_type == ResultBase::Ok) if( m_type == ResultBase::Ok )
new(&m_value) T(other.m_value); new( &m_value ) T( other.m_value );
return *this; return *this;
} }
~ResultValueBase() { ~ResultValueBase() {
if (m_type == Ok) if( m_type == Ok )
m_value.~T(); m_value.~T();
} }
@ -544,37 +547,31 @@ namespace detail {
class BasicResult : public ResultValueBase<T> { class BasicResult : public ResultValueBase<T> {
public: public:
template<typename U> template<typename U>
explicit BasicResult(BasicResult<U> const &other) explicit BasicResult( BasicResult<U> const &other )
: ResultValueBase<T>(other.type()), : ResultValueBase<T>( other.type() ),
m_errorMessage(other.errorMessage()) { m_errorMessage( other.errorMessage() )
assert(type() != ResultBase::Ok); {
assert( type() != ResultBase::Ok );
} }
static auto ok() -> BasicResult { return {ResultBase::Ok}; }
template<typename U> template<typename U>
static auto ok(U const &value) -> BasicResult { return {ResultBase::Ok, value}; } 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 logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
static auto runtimeError(std::string const &message) -> BasicResult {
return {ResultBase::RuntimeError, message};
}
explicit operator bool() const { return m_type == ResultBase::Ok; } explicit operator bool() const { return m_type == ResultBase::Ok; }
auto type() const -> ResultBase::Type { return m_type; } auto type() const -> ResultBase::Type { return m_type; }
auto errorMessage() const -> std::string { return m_errorMessage; } auto errorMessage() const -> std::string { return m_errorMessage; }
protected: protected:
virtual void enforceOk() const { virtual void enforceOk() const {
// !TBD: If no exceptions, std::terminate here or something // !TBD: If no exceptions, std::terminate here or something
switch (m_type) { switch( m_type ) {
case ResultBase::LogicError: case ResultBase::LogicError:
throw std::logic_error(m_errorMessage); throw std::logic_error( m_errorMessage );
case ResultBase::RuntimeError: case ResultBase::RuntimeError:
throw std::runtime_error(m_errorMessage); throw std::runtime_error( m_errorMessage );
case ResultBase::Ok: case ResultBase::Ok:
break; break;
} }
@ -582,10 +579,11 @@ namespace detail {
std::string m_errorMessage; // Only populated if resultType is an error std::string m_errorMessage; // Only populated if resultType is an error
BasicResult(ResultBase::Type type, std::string const &message) BasicResult( ResultBase::Type type, std::string const &message )
: ResultValueBase<T>(type), : ResultValueBase<T>(type),
m_errorMessage(message) { m_errorMessage(message)
assert(m_type != ResultBase::Ok); {
assert( m_type != ResultBase::Ok );
} }
using ResultValueBase<T>::ResultValueBase; using ResultValueBase<T>::ResultValueBase;
@ -599,12 +597,12 @@ namespace detail {
class ParseState { class ParseState {
public: public:
ParseState(ParseResultType type, TokenStream const &remainingTokens) ParseState( ParseResultType type, TokenStream const &remainingTokens )
: m_type(type), : m_type(type),
m_remainingTokens(remainingTokens) {} m_remainingTokens( remainingTokens )
{}
auto type() const -> ParseResultType { return m_type; } auto type() const -> ParseResultType { return m_type; }
auto remainingTokens() const -> TokenStream { return m_remainingTokens; } auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
private: private:
@ -622,69 +620,62 @@ namespace detail {
}; };
template<typename T> template<typename T>
inline auto convertInto(std::string const &source, T& target) -> ParserResult { inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
std::stringstream ss; std::stringstream ss;
ss << source; ss << source;
ss >> target; ss >> target;
if (ss.fail()) if( ss.fail() )
return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
else 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; 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::string srcLC = source;
std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), [](char c) { return static_cast<char>( ::tolower(c) ); } ); 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") if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
target = true; target = true;
else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
target = false; target = false;
else else
return ParserResult::runtimeError("Expected a boolean value but did not recognise: '" + source + "'"); return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
return ParserResult::ok(ParseResultType::Matched); return ParserResult::ok( ParseResultType::Matched );
} }
struct BoundRefBase { struct BoundRefBase {
BoundRefBase() = default; BoundRefBase() = default;
BoundRefBase( BoundRefBase const & ) = delete;
BoundRefBase(BoundRefBase const &) = delete; BoundRefBase( BoundRefBase && ) = delete;
BoundRefBase &operator=( BoundRefBase const & ) = delete;
BoundRefBase(BoundRefBase &&) = delete; BoundRefBase &operator=( BoundRefBase && ) = delete;
BoundRefBase &operator=(BoundRefBase const &) = delete;
BoundRefBase &operator=(BoundRefBase &&) = delete;
virtual ~BoundRefBase() = default; virtual ~BoundRefBase() = default;
virtual auto isFlag() const -> bool = 0; virtual auto isFlag() const -> bool = 0;
virtual auto isContainer() const -> bool { return false; } virtual auto isContainer() const -> bool { return false; }
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
virtual auto setValue(std::string const &arg) -> ParserResult = 0; virtual auto setFlag( bool flag ) -> ParserResult = 0;
virtual auto setFlag(bool flag) -> ParserResult = 0;
}; };
struct BoundValueRefBase : BoundRefBase { struct BoundValueRefBase : BoundRefBase {
auto isFlag() const -> bool override { return false; } auto isFlag() const -> bool override { return false; }
auto setFlag(bool) -> ParserResult override { auto setFlag( bool ) -> ParserResult override {
return ParserResult::logicError("Flags can only be set on boolean fields"); return ParserResult::logicError( "Flags can only be set on boolean fields" );
} }
}; };
struct BoundFlagRefBase : BoundRefBase { struct BoundFlagRefBase : BoundRefBase {
auto isFlag() const -> bool override { return true; } 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; bool flag;
auto result = convertInto(arg, flag); auto result = convertInto( arg, flag );
if (result) if( result )
setFlag(flag); setFlag( flag );
return result; return result;
} }
}; };
@ -693,10 +684,10 @@ namespace detail {
struct BoundRef : BoundValueRefBase { struct BoundRef : BoundValueRefBase {
T &m_ref; 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 { auto setValue( std::string const &arg ) -> ParserResult override {
return convertInto(arg, m_ref); return convertInto( arg, m_ref );
} }
}; };
@ -704,15 +695,15 @@ namespace detail {
struct BoundRef<std::vector<T>> : BoundValueRefBase { struct BoundRef<std::vector<T>> : BoundValueRefBase {
std::vector<T> &m_ref; std::vector<T> &m_ref;
explicit BoundRef(std::vector<T> &ref) : m_ref(ref) {} explicit BoundRef( std::vector<T> &ref ) : m_ref( ref ) {}
auto isContainer() const -> bool override { return true; } 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; T temp;
auto result = convertInto(arg, temp); auto result = convertInto( arg, temp );
if (result) if( result )
m_ref.push_back(temp); m_ref.push_back( temp );
return result; return result;
} }
}; };
@ -720,40 +711,40 @@ namespace detail {
struct BoundFlagRef : BoundFlagRefBase { struct BoundFlagRef : BoundFlagRefBase {
bool &m_ref; 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; m_ref = flag;
return ParserResult::ok(ParseResultType::Matched); return ParserResult::ok( ParseResultType::Matched );
} }
}; };
template<typename ReturnType> template<typename ReturnType>
struct LambdaInvoker { struct LambdaInvoker {
static_assert(std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult"); static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
template<typename L, typename ArgType> template<typename L, typename ArgType>
static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
return lambda(arg); return lambda( arg );
} }
}; };
template<> template<>
struct LambdaInvoker<void> { struct LambdaInvoker<void> {
template<typename L, typename ArgType> template<typename L, typename ArgType>
static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
lambda(arg); lambda( arg );
return ParserResult::ok(ParseResultType::Matched); return ParserResult::ok( ParseResultType::Matched );
} }
}; };
template<typename ArgType, typename L> template<typename ArgType, typename L>
inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
ArgType temp; ArgType temp;
auto result = convertInto(arg, temp); auto result = convertInto( arg, temp );
return !result return !result
? result ? result
: LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke(lambda, temp); : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
}; };
@ -762,10 +753,10 @@ namespace detail {
L m_lambda; L m_lambda;
static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); static_assert( UnaryLambdaTraits<L>::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 { auto setValue( std::string const &arg ) -> ParserResult override {
return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>(m_lambda, arg); return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
} }
}; };
@ -776,16 +767,14 @@ namespace detail {
static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); 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" ); static_assert( std::is_same<typename UnaryLambdaTraits<L>::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 { auto setFlag( bool flag ) -> ParserResult override {
return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke(m_lambda, flag); return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
} }
}; };
enum class Optionality { enum class Optionality { Optional, Required };
Optional, Required
};
struct Parser; struct Parser;
@ -796,8 +785,8 @@ namespace detail {
virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0;
virtual auto cardinality() const -> size_t { return 1; } virtual auto cardinality() const -> size_t { return 1; }
auto parse(Args const &args) const -> InternalParseResult { auto parse( Args const &args ) const -> InternalParseResult {
return parse( args.exeName(), TokenStream(args)); return parse( args.exeName(), TokenStream( args ) );
} }
}; };
@ -805,7 +794,7 @@ namespace detail {
class ComposableParserImpl : public ParserBase { class ComposableParserImpl : public ParserBase {
public: public:
template<typename T> template<typename T>
auto operator+(T const &other) const -> Parser; auto operator|( T const &other ) const -> Parser;
}; };
// Common code and state for Args and Opts // Common code and state for Args and Opts
@ -817,17 +806,22 @@ namespace detail {
std::string m_hint; std::string m_hint;
std::string m_description; std::string m_description;
explicit ParserRefImpl(std::shared_ptr<BoundRefBase> const &ref) : m_ref(ref) {} explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {}
public: public:
template<typename T> template<typename T>
ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared<BoundRef<T>>(ref)), m_hint(hint) {} ParserRefImpl( T &ref, std::string const &hint )
: m_ref( std::make_shared<BoundRef<T>>( ref ) ),
m_hint( hint )
{}
template<typename LambdaT> template<typename LambdaT>
ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared<BoundLambda<LambdaT>>(ref)), ParserRefImpl( LambdaT const &ref, std::string const &hint )
m_hint(hint) {} : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
m_hint(hint)
{}
auto operator()(std::string const &description) -> DerivedT & { auto operator()( std::string const &description ) -> DerivedT & {
m_description = description; m_description = description;
return static_cast<DerivedT &>( *this ); return static_cast<DerivedT &>( *this );
} }
@ -847,7 +841,7 @@ namespace detail {
} }
auto cardinality() const -> size_t override { auto cardinality() const -> size_t override {
if (m_ref->isContainer()) if( m_ref->isContainer() )
return 0; return 0;
else else
return 1; return 1;
@ -862,13 +856,13 @@ namespace detail {
template<typename LambdaT> template<typename LambdaT>
static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> { static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> {
return std::make_shared<BoundLambda<LambdaT>>(lambda); return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
} }
public: public:
ExeName() : m_name(std::make_shared<std::string>("<executable>")) {} ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
explicit ExeName(std::string &ref) : ExeName() { explicit ExeName( std::string &ref ) : ExeName() {
m_ref = std::make_shared<BoundRef<std::string>>( ref ); m_ref = std::make_shared<BoundRef<std::string>>( ref );
} }
@ -879,14 +873,14 @@ namespace detail {
// The exe name is not parsed out of the normal tokens, but is handled specially // 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 { 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 name() const -> std::string { return *m_name; }
auto set( std::string const& newName ) -> ParserResult { auto set( std::string const& newName ) -> ParserResult {
auto lastSlash = newName.find_last_of( "\\/" ); auto lastSlash = newName.find_last_of( "\\/" );
auto filename = (lastSlash == std::string::npos) auto filename = ( lastSlash == std::string::npos )
? newName ? newName
: newName.substr( lastSlash+1 ); : newName.substr( lastSlash+1 );
@ -902,27 +896,27 @@ namespace detail {
public: public:
using ParserRefImpl::ParserRefImpl; 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(); auto validationResult = validate();
if (!validationResult) if( !validationResult )
return InternalParseResult(validationResult); return InternalParseResult( validationResult );
auto remainingTokens = tokens; auto remainingTokens = tokens;
auto const &token = *remainingTokens; auto const &token = *remainingTokens;
if (token.type != TokenType::Argument) if( token.type != TokenType::Argument )
return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
auto result = m_ref->setValue(remainingTokens->token); auto result = m_ref->setValue( remainingTokens->token );
if (!result) if( !result )
return InternalParseResult(result); return InternalParseResult( result );
else else
return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
} }
}; };
inline auto normaliseOpt(std::string const &optName) -> std::string { inline auto normaliseOpt( std::string const &optName ) -> std::string {
if (optName[0] == '/') if( optName[0] == '/' )
return "-" + optName.substr(1); return "-" + optName.substr( 1 );
else else
return optName; return optName;
} }
@ -933,9 +927,9 @@ namespace detail {
public: public:
template<typename LambdaT> template<typename LambdaT>
explicit Opt( LambdaT const &ref ) : ParserRefImpl(std::make_shared<BoundFlagLambda<LambdaT>>(ref)) {} explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
explicit Opt( bool &ref ) : ParserRefImpl(std::make_shared<BoundFlagRef>(ref)) {} explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
template<typename LambdaT> template<typename LambdaT>
Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
@ -943,34 +937,34 @@ namespace detail {
template<typename T> template<typename T>
Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
auto operator[](std::string const &optName) -> Opt & { auto operator[]( std::string const &optName ) -> Opt & {
m_optNames.push_back(optName); m_optNames.push_back( optName );
return *this; return *this;
} }
auto getHelpColumns() const -> std::vector<HelpColumns> { auto getHelpColumns() const -> std::vector<HelpColumns> {
std::ostringstream oss; std::ostringstream oss;
bool first = true; bool first = true;
for (auto const &opt : m_optNames) { for( auto const &opt : m_optNames ) {
if (first) if (first)
first = false; first = false;
else else
oss << ", "; oss << ", ";
oss << opt; oss << opt;
} }
if (!m_hint.empty()) if( !m_hint.empty() )
oss << " <" << m_hint << ">"; 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 #ifdef CLARA_PLATFORM_WINDOWS
auto normalisedToken = normaliseOpt( optToken ); auto normalisedToken = normaliseOpt( optToken );
#else #else
auto const &normalisedToken = optToken; auto const &normalisedToken = optToken;
#endif #endif
for (auto const &name : m_optNames) { for( auto const &name : m_optNames ) {
if (normaliseOpt(name) == normalisedToken) if( normaliseOpt( name ) == normalisedToken )
return true; return true;
} }
return false; return false;
@ -980,46 +974,46 @@ namespace detail {
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(); auto validationResult = validate();
if (!validationResult) if( !validationResult )
return InternalParseResult(validationResult); return InternalParseResult( validationResult );
auto remainingTokens = tokens; auto remainingTokens = tokens;
if (remainingTokens && remainingTokens->type == TokenType::Option) { if( remainingTokens && remainingTokens->type == TokenType::Option ) {
auto const &token = *remainingTokens; auto const &token = *remainingTokens;
if (isMatch(token.token)) { if( isMatch(token.token ) ) {
if (m_ref->isFlag()) { if( m_ref->isFlag() ) {
auto result = m_ref->setFlag(true); auto result = m_ref->setFlag( true );
if (!result) if( !result )
return InternalParseResult(result); return InternalParseResult( result );
if (result.value() == ParseResultType::ShortCircuitAll) if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
} else { } else {
++remainingTokens; ++remainingTokens;
if (!remainingTokens) if( !remainingTokens )
return InternalParseResult::runtimeError("Expected argument following " + token.token); return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto const &argToken = *remainingTokens; auto const &argToken = *remainingTokens;
if (argToken.type != TokenType::Argument) if( argToken.type != TokenType::Argument )
return InternalParseResult::runtimeError("Expected argument following " + token.token); return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto result = m_ref->setValue(argToken.token); auto result = m_ref->setValue( argToken.token );
if (!result) if( !result )
return InternalParseResult(result); return InternalParseResult( result );
if (result.value() == ParseResultType::ShortCircuitAll) if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); 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 { auto validate() const -> Result override {
if (m_optNames.empty()) if( m_optNames.empty() )
return Result::logicError("No options supplied to Opt"); return Result::logicError( "No options supplied to Opt" );
for (auto const &name : m_optNames) { for( auto const &name : m_optNames ) {
if (name.empty()) if( name.empty() )
return Result::logicError("Option name cannot be empty"); return Result::logicError( "Option name cannot be empty" );
if (name[0] != '-' && name[0] != '/') if( name[0] != '-' && name[0] != '/' )
return Result::logicError("Option name must begin with '-' or '/'"); return Result::logicError( "Option name must begin with '-' or '/'" );
} }
return ParserRefImpl::validate(); return ParserRefImpl::validate();
} }
@ -1029,10 +1023,10 @@ namespace detail {
Help( bool &showHelpFlag ) Help( bool &showHelpFlag )
: Opt([&]( bool flag ) { : Opt([&]( bool flag ) {
showHelpFlag = flag; showHelpFlag = flag;
return ParserResult::ok(ParseResultType::ShortCircuitAll); return ParserResult::ok( ParseResultType::ShortCircuitAll );
}) })
{ {
static_cast<Opt &>(*this) static_cast<Opt &>( *this )
("display usage information") ("display usage information")
["-?"]["-h"]["--help"] ["-?"]["-h"]["--help"]
.optional(); .optional();
@ -1046,61 +1040,61 @@ namespace detail {
std::vector<Opt> m_options; std::vector<Opt> m_options;
std::vector<Arg> m_args; std::vector<Arg> m_args;
auto operator+=(ExeName const &exeName) -> Parser & { auto operator|=( ExeName const &exeName ) -> Parser & {
m_exeName = exeName; m_exeName = exeName;
return *this; return *this;
} }
auto operator+=(Arg const &arg) -> Parser & { auto operator|=( Arg const &arg ) -> Parser & {
m_args.push_back(arg); m_args.push_back(arg);
return *this; return *this;
} }
auto operator+=(Opt const &opt) -> Parser & { auto operator|=( Opt const &opt ) -> Parser & {
m_options.push_back(opt); m_options.push_back(opt);
return *this; 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_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()); m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
return *this; return *this;
} }
template<typename T> template<typename T>
auto operator+(T const &other) const -> Parser { auto operator|( T const &other ) const -> Parser {
return Parser(*this) += other; return Parser( *this ) |= other;
} }
auto getHelpColumns() const -> std::vector<HelpColumns> { auto getHelpColumns() const -> std::vector<HelpColumns> {
std::vector<HelpColumns> cols; std::vector<HelpColumns> cols;
for (auto const &o : m_options) { for (auto const &o : m_options) {
auto childCols = o.getHelpColumns(); auto childCols = o.getHelpColumns();
cols.insert(cols.end(), childCols.begin(), childCols.end()); cols.insert( cols.end(), childCols.begin(), childCols.end() );
} }
return cols; return cols;
} }
void writeToStream(std::ostream &os) const { void writeToStream( std::ostream &os ) const {
if (!m_exeName.name().empty()) { if (!m_exeName.name().empty()) {
os << "usage:\n" << " " << m_exeName.name() << " "; os << "usage:\n" << " " << m_exeName.name() << " ";
bool required = true, first = true; bool required = true, first = true;
for (auto const &arg : m_args) { for( auto const &arg : m_args ) {
if (first) if (first)
first = false; first = false;
else else
os << " "; os << " ";
if (arg.isOptional() && required) { if( arg.isOptional() && required ) {
os << "["; os << "[";
required = false; required = false;
} }
os << "<" << arg.hint() << ">"; os << "<" << arg.hint() << ">";
if (arg.cardinality() == 0) if( arg.cardinality() == 0 )
os << " ... "; os << " ... ";
} }
if (!required) if( !required )
os << "]"; os << "]";
if (!m_options.empty()) if( !m_options.empty() )
os << " options"; os << " options";
os << "\n\nwhere options are:" << std::endl; os << "\n\nwhere options are:" << std::endl;
} }
@ -1108,32 +1102,32 @@ namespace detail {
auto rows = getHelpColumns(); auto rows = getHelpColumns();
size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
size_t optWidth = 0; size_t optWidth = 0;
for (auto const &cols : rows) for( auto const &cols : rows )
optWidth = std::max(optWidth, cols.left.size() + 2); optWidth = std::max(optWidth, cols.left.size() + 2);
for (auto const &cols : rows) { for( auto const &cols : rows ) {
auto row = auto row =
TextFlow::Column(cols.left).width(optWidth).indent(2) + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
TextFlow::Spacer(4) + TextFlow::Spacer(4) +
TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
os << row << std::endl; os << row << std::endl;
} }
} }
friend auto operator<<(std::ostream &os, Parser const &parser) -> std::ostream & { friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
parser.writeToStream(os); parser.writeToStream( os );
return os; return os;
} }
auto validate() const -> Result override { auto validate() const -> Result override {
for (auto const &opt : m_options) { for( auto const &opt : m_options ) {
auto result = opt.validate(); auto result = opt.validate();
if (!result) if( !result )
return result; return result;
} }
for (auto const &arg : m_args) { for( auto const &arg : m_args ) {
auto result = arg.validate(); auto result = arg.validate();
if (!result) if( !result )
return result; return result;
} }
return Result::ok(); return Result::ok();
@ -1141,47 +1135,41 @@ namespace detail {
using ParserBase::parse; using ParserBase::parse;
auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult override { auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
std::vector<ParserBase const *> allParsers;
allParsers.reserve(m_args.size() + m_options.size());
std::set<ParserBase const *> requiredParsers;
for (auto const &opt : m_options) { struct ParserInfo {
allParsers.push_back(&opt); ParserBase const* parser = nullptr;
if (!opt.isOptional()) size_t count = 0;
requiredParsers.insert(&opt); };
} const size_t totalParsers = m_options.size() + m_args.size();
ParserInfo parseInfos[totalParsers];
size_t optionalArgs = 0; size_t i = 0;
for (auto const &arg : m_args) { for( auto const& opt : m_options ) parseInfos[i++].parser = &opt;
allParsers.push_back(&arg); for( auto const& arg : m_args ) parseInfos[i++].parser = &arg;
if (!arg.isOptional()) {
if (optionalArgs > 0)
return InternalParseResult::logicError(
"Required arguments must preceed any optional arguments");
else
++optionalArgs;
requiredParsers.insert(&arg);
}
}
m_exeName.set( exeName ); m_exeName.set( exeName );
auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
while (result.value().remainingTokens()) { while( result.value().remainingTokens() ) {
auto remainingTokenCount = result.value().remainingTokens().count(); bool tokenParsed = false;
for (auto parser : allParsers) {
result = parser->parse( exeName, result.value().remainingTokens() ); for( auto& parseInfo : parseInfos ) {
if (!result || result.value().type() != ParseResultType::NoMatch) { if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
if (parser->cardinality() == 1) result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser), if (!result)
allParsers.end()); return result;
requiredParsers.erase(parser); if (result.value().type() != ParseResultType::NoMatch) {
tokenParsed = true;
++parseInfo.count;
break; break;
} }
} }
if (!result || remainingTokenCount == result.value().remainingTokens().count()) }
if( result.value().type() == ParseResultType::ShortCircuitAll )
return result; return result;
if( !tokenParsed )
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
} }
// !TBD Check missing required options // !TBD Check missing required options
return result; return result;
@ -1190,8 +1178,8 @@ namespace detail {
template<typename DerivedT> template<typename DerivedT>
template<typename T> template<typename T>
auto ComposableParserImpl<DerivedT>::operator+(T const &other) const -> Parser { auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
return Parser() + static_cast<DerivedT const &>( *this ) + other; return Parser() | static_cast<DerivedT const &>( *this ) | other;
} }
} // namespace detail } // namespace detail