mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 21:36:11 +01:00
Clara cleanups
* Clara is now split between a header and a cpp file. * Removed the deprecated `+` and `+=` operators for composing a parser. * Renamed `clara` and `detail` namespaces to be inline with the rest of Catch2 (they are now `Clara` and `Detail` respectively). * Taken most of user-exposed types out of the `Detail` namespace completely (instead of using `using` directives to bring them into the outer namespace).
This commit is contained in:
parent
b824d06844
commit
24b83edf8a
@ -44,7 +44,6 @@ set(INTERNAL_HEADERS
|
|||||||
${SOURCES_DIR}/catch_assertion_result.hpp
|
${SOURCES_DIR}/catch_assertion_result.hpp
|
||||||
${SOURCES_DIR}/internal/catch_test_macro_impl.hpp
|
${SOURCES_DIR}/internal/catch_test_macro_impl.hpp
|
||||||
${SOURCES_DIR}/internal/catch_clara.hpp
|
${SOURCES_DIR}/internal/catch_clara.hpp
|
||||||
${SOURCES_DIR}/internal/catch_clara_upstream.hpp
|
|
||||||
${SOURCES_DIR}/internal/catch_commandline.hpp
|
${SOURCES_DIR}/internal/catch_commandline.hpp
|
||||||
${SOURCES_DIR}/internal/catch_common.hpp
|
${SOURCES_DIR}/internal/catch_common.hpp
|
||||||
${SOURCES_DIR}/internal/catch_compiler_capabilities.hpp
|
${SOURCES_DIR}/internal/catch_compiler_capabilities.hpp
|
||||||
@ -138,6 +137,7 @@ set(IMPL_SOURCES
|
|||||||
${SOURCES_DIR}/internal/catch_assertion_handler.cpp
|
${SOURCES_DIR}/internal/catch_assertion_handler.cpp
|
||||||
${SOURCES_DIR}/catch_assertion_result.cpp
|
${SOURCES_DIR}/catch_assertion_result.cpp
|
||||||
${SOURCES_DIR}/matchers/internal/catch_matchers_combined_tu.cpp
|
${SOURCES_DIR}/matchers/internal/catch_matchers_combined_tu.cpp
|
||||||
|
${SOURCES_DIR}/internal/catch_clara.cpp
|
||||||
${SOURCES_DIR}/internal/catch_commandline.cpp
|
${SOURCES_DIR}/internal/catch_commandline.cpp
|
||||||
${SOURCES_DIR}/internal/catch_common.cpp
|
${SOURCES_DIR}/internal/catch_common.cpp
|
||||||
${SOURCES_DIR}/catch_config.cpp
|
${SOURCES_DIR}/catch_config.cpp
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
#include <catch2/interfaces/catch_interfaces_all.hpp>
|
#include <catch2/interfaces/catch_interfaces_all.hpp>
|
||||||
#include <catch2/internal/catch_assertion_handler.hpp>
|
#include <catch2/internal/catch_assertion_handler.hpp>
|
||||||
#include <catch2/internal/catch_clara.hpp>
|
#include <catch2/internal/catch_clara.hpp>
|
||||||
#include <catch2/internal/catch_clara_upstream.hpp>
|
|
||||||
#include <catch2/internal/catch_commandline.hpp>
|
#include <catch2/internal/catch_commandline.hpp>
|
||||||
#include <catch2/internal/catch_common.hpp>
|
#include <catch2/internal/catch_common.hpp>
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||||
|
@ -176,7 +176,7 @@ namespace Catch {
|
|||||||
if( m_startupExceptions )
|
if( m_startupExceptions )
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
auto result = m_cli.parse( clara::Args( argc, argv ) );
|
auto result = m_cli.parse( Clara::Args( argc, argv ) );
|
||||||
if( !result ) {
|
if( !result ) {
|
||||||
config();
|
config();
|
||||||
getCurrentMutableContext().setConfig(m_config.get());
|
getCurrentMutableContext().setConfig(m_config.get());
|
||||||
@ -239,10 +239,10 @@ namespace Catch {
|
|||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
clara::Parser const& Session::cli() const {
|
Clara::Parser const& Session::cli() const {
|
||||||
return m_cli;
|
return m_cli;
|
||||||
}
|
}
|
||||||
void Session::cli( clara::Parser const& newParser ) {
|
void Session::cli( Clara::Parser const& newParser ) {
|
||||||
m_cli = newParser;
|
m_cli = newParser;
|
||||||
}
|
}
|
||||||
ConfigData& Session::configData() {
|
ConfigData& Session::configData() {
|
||||||
|
@ -42,14 +42,14 @@ namespace Catch {
|
|||||||
|
|
||||||
int run();
|
int run();
|
||||||
|
|
||||||
clara::Parser const& cli() const;
|
Clara::Parser const& cli() const;
|
||||||
void cli( clara::Parser const& newParser );
|
void cli( Clara::Parser const& newParser );
|
||||||
ConfigData& configData();
|
ConfigData& configData();
|
||||||
Config& config();
|
Config& config();
|
||||||
private:
|
private:
|
||||||
int runInternal();
|
int runInternal();
|
||||||
|
|
||||||
clara::Parser m_cli;
|
Clara::Parser m_cli;
|
||||||
ConfigData m_configData;
|
ConfigData m_configData;
|
||||||
Detail::unique_ptr<Config> m_config;
|
Detail::unique_ptr<Config> m_config;
|
||||||
bool m_startupExceptions = false;
|
bool m_startupExceptions = false;
|
||||||
|
428
src/catch2/internal/catch_clara.cpp
Normal file
428
src/catch2/internal/catch_clara.cpp
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <catch2/internal/catch_clara.hpp>
|
||||||
|
#include <catch2/internal/catch_console_width.hpp>
|
||||||
|
#include <catch2/internal/catch_platform.hpp>
|
||||||
|
#include <catch2/internal/catch_string_manip.hpp>
|
||||||
|
#include <catch2/internal/catch_textflow.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool isOptPrefix( char c ) {
|
||||||
|
return c == '-'
|
||||||
|
#ifdef CATCH_PLATFORM_WINDOWS
|
||||||
|
|| c == '/'
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normaliseOpt( std::string const& optName ) {
|
||||||
|
#ifdef CATCH_PLATFORM_WINDOWS
|
||||||
|
if ( optName[0] == '/' )
|
||||||
|
return "-" + optName.substr( 1 );
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
return optName;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
namespace Clara {
|
||||||
|
namespace Detail {
|
||||||
|
|
||||||
|
void TokenStream::loadBuffer() {
|
||||||
|
m_tokenBuffer.clear();
|
||||||
|
|
||||||
|
// Skip any empty strings
|
||||||
|
while ( it != itEnd && it->empty() ) {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( it != itEnd ) {
|
||||||
|
auto const& next = *it;
|
||||||
|
if ( isOptPrefix( next[0] ) ) {
|
||||||
|
auto delimiterPos = next.find_first_of( " :=" );
|
||||||
|
if ( delimiterPos != std::string::npos ) {
|
||||||
|
m_tokenBuffer.push_back(
|
||||||
|
{ TokenType::Option,
|
||||||
|
next.substr( 0, delimiterPos ) } );
|
||||||
|
m_tokenBuffer.push_back(
|
||||||
|
{ TokenType::Argument,
|
||||||
|
next.substr( delimiterPos + 1 ) } );
|
||||||
|
} else {
|
||||||
|
if ( next[1] != '-' && next.size() > 2 ) {
|
||||||
|
std::string opt = "- ";
|
||||||
|
for ( size_t i = 1; i < next.size(); ++i ) {
|
||||||
|
opt[1] = next[i];
|
||||||
|
m_tokenBuffer.push_back(
|
||||||
|
{ TokenType::Option, opt } );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_tokenBuffer.push_back(
|
||||||
|
{ TokenType::Option, next } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_tokenBuffer.push_back(
|
||||||
|
{ TokenType::Argument, next } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenStream::TokenStream( Args const& args ):
|
||||||
|
TokenStream( args.m_args.begin(), args.m_args.end() ) {}
|
||||||
|
|
||||||
|
TokenStream::TokenStream( Iterator it, Iterator itEnd ):
|
||||||
|
it( it ), itEnd( itEnd ) {
|
||||||
|
loadBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenStream& TokenStream::operator++() {
|
||||||
|
if ( m_tokenBuffer.size() >= 2 ) {
|
||||||
|
m_tokenBuffer.erase( m_tokenBuffer.begin() );
|
||||||
|
} else {
|
||||||
|
if ( it != itEnd )
|
||||||
|
++it;
|
||||||
|
loadBuffer();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserResult convertInto( std::string const& source,
|
||||||
|
std::string& target ) {
|
||||||
|
target = source;
|
||||||
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserResult convertInto( std::string const& source,
|
||||||
|
bool& target ) {
|
||||||
|
std::string srcLC = toLower( source );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalParseResult ParserBase::parse( Args const& args ) const {
|
||||||
|
return parse( args.exeName(), TokenStream( args ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseState::ParseState( ParseResultType type,
|
||||||
|
TokenStream const& remainingTokens ):
|
||||||
|
m_type( type ), m_remainingTokens( remainingTokens ) {}
|
||||||
|
|
||||||
|
ParserResult BoundFlagRef::setFlag( bool flag ) {
|
||||||
|
m_ref = flag;
|
||||||
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
|
Detail::InternalParseResult Arg::parse(std::string const&,
|
||||||
|
Detail::TokenStream const& tokens) const {
|
||||||
|
auto validationResult = validate();
|
||||||
|
if (!validationResult)
|
||||||
|
return Detail::InternalParseResult(validationResult);
|
||||||
|
|
||||||
|
auto remainingTokens = tokens;
|
||||||
|
auto const& token = *remainingTokens;
|
||||||
|
if (token.type != Detail::TokenType::Argument)
|
||||||
|
return Detail::InternalParseResult::ok(Detail::ParseState(
|
||||||
|
ParseResultType::NoMatch, remainingTokens));
|
||||||
|
|
||||||
|
assert(!m_ref->isFlag());
|
||||||
|
auto valueRef =
|
||||||
|
static_cast<Detail::BoundValueRefBase*>(m_ref.get());
|
||||||
|
|
||||||
|
auto result = valueRef->setValue(remainingTokens->token);
|
||||||
|
if (!result)
|
||||||
|
return Detail::InternalParseResult(result);
|
||||||
|
else
|
||||||
|
return Detail::InternalParseResult::ok(Detail::ParseState(
|
||||||
|
ParseResultType::Matched, ++remainingTokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
Opt::Opt(bool& ref) :
|
||||||
|
ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {}
|
||||||
|
|
||||||
|
std::vector<Detail::HelpColumns> Opt::getHelpColumns() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
bool first = true;
|
||||||
|
for (auto const& opt : m_optNames) {
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
oss << ", ";
|
||||||
|
oss << opt;
|
||||||
|
}
|
||||||
|
if (!m_hint.empty())
|
||||||
|
oss << " <" << m_hint << '>';
|
||||||
|
return { { oss.str(), m_description } };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Opt::isMatch(std::string const& optToken) const {
|
||||||
|
auto normalisedToken = normaliseOpt(optToken);
|
||||||
|
for (auto const& name : m_optNames) {
|
||||||
|
if (normaliseOpt(name) == normalisedToken)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Detail::InternalParseResult Opt::parse(std::string const&,
|
||||||
|
Detail::TokenStream const& tokens) const {
|
||||||
|
auto validationResult = validate();
|
||||||
|
if (!validationResult)
|
||||||
|
return Detail::InternalParseResult(validationResult);
|
||||||
|
|
||||||
|
auto remainingTokens = tokens;
|
||||||
|
if (remainingTokens &&
|
||||||
|
remainingTokens->type == Detail::TokenType::Option) {
|
||||||
|
auto const& token = *remainingTokens;
|
||||||
|
if (isMatch(token.token)) {
|
||||||
|
if (m_ref->isFlag()) {
|
||||||
|
auto flagRef =
|
||||||
|
static_cast<Detail::BoundFlagRefBase*>(
|
||||||
|
m_ref.get());
|
||||||
|
auto result = flagRef->setFlag(true);
|
||||||
|
if (!result)
|
||||||
|
return Detail::InternalParseResult(result);
|
||||||
|
if (result.value() ==
|
||||||
|
ParseResultType::ShortCircuitAll)
|
||||||
|
return Detail::InternalParseResult::ok(Detail::ParseState(
|
||||||
|
result.value(), remainingTokens));
|
||||||
|
} else {
|
||||||
|
auto valueRef =
|
||||||
|
static_cast<Detail::BoundValueRefBase*>(
|
||||||
|
m_ref.get());
|
||||||
|
++remainingTokens;
|
||||||
|
if (!remainingTokens)
|
||||||
|
return Detail::InternalParseResult::runtimeError(
|
||||||
|
"Expected argument following " +
|
||||||
|
token.token);
|
||||||
|
auto const& argToken = *remainingTokens;
|
||||||
|
if (argToken.type != Detail::TokenType::Argument)
|
||||||
|
return Detail::InternalParseResult::runtimeError(
|
||||||
|
"Expected argument following " +
|
||||||
|
token.token);
|
||||||
|
auto result = valueRef->setValue(argToken.token);
|
||||||
|
if (!result)
|
||||||
|
return Detail::InternalParseResult(result);
|
||||||
|
if (result.value() ==
|
||||||
|
ParseResultType::ShortCircuitAll)
|
||||||
|
return Detail::InternalParseResult::ok(Detail::ParseState(
|
||||||
|
result.value(), remainingTokens));
|
||||||
|
}
|
||||||
|
return Detail::InternalParseResult::ok(Detail::ParseState(
|
||||||
|
ParseResultType::Matched, ++remainingTokens));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Detail::InternalParseResult::ok(
|
||||||
|
Detail::ParseState(ParseResultType::NoMatch, remainingTokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
Detail::Result Opt::validate() const {
|
||||||
|
if (m_optNames.empty())
|
||||||
|
return Detail::Result::logicError("No options supplied to Opt");
|
||||||
|
for (auto const& name : m_optNames) {
|
||||||
|
if (name.empty())
|
||||||
|
return Detail::Result::logicError(
|
||||||
|
"Option name cannot be empty");
|
||||||
|
#ifdef CATCH_PLATFORM_WINDOWS
|
||||||
|
if (name[0] != '-' && name[0] != '/')
|
||||||
|
return Detail::Result::logicError(
|
||||||
|
"Option name must begin with '-' or '/'");
|
||||||
|
#else
|
||||||
|
if (name[0] != '-')
|
||||||
|
return Detail::Result::logicError(
|
||||||
|
"Option name must begin with '-'");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ParserRefImpl::validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExeName::ExeName() :
|
||||||
|
m_name(std::make_shared<std::string>("<executable>")) {}
|
||||||
|
|
||||||
|
ExeName::ExeName(std::string& ref) : ExeName() {
|
||||||
|
m_ref = std::make_shared<Detail::BoundValueRef<std::string>>(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
Detail::InternalParseResult
|
||||||
|
ExeName::parse(std::string const&,
|
||||||
|
Detail::TokenStream const& tokens) const {
|
||||||
|
return Detail::InternalParseResult::ok(
|
||||||
|
Detail::ParseState(ParseResultType::NoMatch, tokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserResult ExeName::set(std::string const& newName) {
|
||||||
|
auto lastSlash = newName.find_last_of("\\/");
|
||||||
|
auto filename = (lastSlash == std::string::npos)
|
||||||
|
? newName
|
||||||
|
: newName.substr(lastSlash + 1);
|
||||||
|
|
||||||
|
*m_name = filename;
|
||||||
|
if (m_ref)
|
||||||
|
return m_ref->setValue(filename);
|
||||||
|
else
|
||||||
|
return ParserResult::ok(ParseResultType::Matched);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Parser& Parser::operator|=( Parser const& other ) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Detail::HelpColumns> Parser::getHelpColumns() const {
|
||||||
|
std::vector<Detail::HelpColumns> cols;
|
||||||
|
for ( auto const& o : m_options ) {
|
||||||
|
auto childCols = o.getHelpColumns();
|
||||||
|
cols.insert( cols.end(), childCols.begin(), childCols.end() );
|
||||||
|
}
|
||||||
|
return cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::writeToStream( std::ostream& os ) const {
|
||||||
|
if ( !m_exeName.name().empty() ) {
|
||||||
|
os << "usage:\n"
|
||||||
|
<< " " << m_exeName.name() << ' ';
|
||||||
|
bool required = true, first = true;
|
||||||
|
for ( auto const& arg : m_args ) {
|
||||||
|
if ( first )
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
os << ' ';
|
||||||
|
if ( arg.isOptional() && required ) {
|
||||||
|
os << '[';
|
||||||
|
required = false;
|
||||||
|
}
|
||||||
|
os << '<' << arg.hint() << '>';
|
||||||
|
if ( arg.cardinality() == 0 )
|
||||||
|
os << " ... ";
|
||||||
|
}
|
||||||
|
if ( !required )
|
||||||
|
os << ']';
|
||||||
|
if ( !m_options.empty() )
|
||||||
|
os << " options";
|
||||||
|
os << "\n\nwhere options are:\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rows = getHelpColumns();
|
||||||
|
size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH;
|
||||||
|
size_t optWidth = 0;
|
||||||
|
for ( auto const& cols : rows )
|
||||||
|
optWidth = ( std::max )( optWidth, cols.left.size() + 2 );
|
||||||
|
|
||||||
|
optWidth = ( std::min )( optWidth, consoleWidth / 2 );
|
||||||
|
|
||||||
|
for ( auto const& cols : rows ) {
|
||||||
|
auto row = TextFlow::Column( cols.left )
|
||||||
|
.width( optWidth )
|
||||||
|
.indent( 2 ) +
|
||||||
|
TextFlow::Spacer( 4 ) +
|
||||||
|
TextFlow::Column( cols.right )
|
||||||
|
.width( consoleWidth - 7 - optWidth );
|
||||||
|
os << row << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Detail::Result Parser::validate() const {
|
||||||
|
for ( auto const& opt : m_options ) {
|
||||||
|
auto result = opt.validate();
|
||||||
|
if ( !result )
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for ( auto const& arg : m_args ) {
|
||||||
|
auto result = arg.validate();
|
||||||
|
if ( !result )
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Detail::Result::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Detail::InternalParseResult
|
||||||
|
Parser::parse( std::string const& exeName,
|
||||||
|
Detail::TokenStream const& tokens ) const {
|
||||||
|
|
||||||
|
struct ParserInfo {
|
||||||
|
ParserBase const* parser = nullptr;
|
||||||
|
size_t count = 0;
|
||||||
|
};
|
||||||
|
std::vector<ParserInfo> parseInfos;
|
||||||
|
parseInfos.reserve( m_options.size() + m_args.size() );
|
||||||
|
for ( auto const& opt : m_options ) {
|
||||||
|
parseInfos.push_back( { &opt, 0 } );
|
||||||
|
}
|
||||||
|
for ( auto const& arg : m_args ) {
|
||||||
|
parseInfos.push_back( { &arg, 0 } );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_exeName.set( exeName );
|
||||||
|
|
||||||
|
auto result = Detail::InternalParseResult::ok(
|
||||||
|
Detail::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.value().type() == ParseResultType::ShortCircuitAll )
|
||||||
|
return result;
|
||||||
|
if ( !tokenParsed )
|
||||||
|
return Detail::InternalParseResult::runtimeError(
|
||||||
|
"Unrecognised token: " +
|
||||||
|
result.value().remainingTokens()->token );
|
||||||
|
}
|
||||||
|
// !TBD Check missing required options
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Args::Args(int argc, char const* const* argv) :
|
||||||
|
m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
|
||||||
|
|
||||||
|
Args::Args(std::initializer_list<std::string> args) :
|
||||||
|
m_exeName(*args.begin()),
|
||||||
|
m_args(args.begin() + 1, args.end()) {}
|
||||||
|
|
||||||
|
|
||||||
|
Help::Help( bool& showHelpFlag ):
|
||||||
|
Opt( [&]( bool flag ) {
|
||||||
|
showHelpFlag = flag;
|
||||||
|
return ParserResult::ok( ParseResultType::ShortCircuitAll );
|
||||||
|
} ) {
|
||||||
|
static_cast<Opt&> ( *this )(
|
||||||
|
"display usage information" )["-?"]["-h"]["--help"]
|
||||||
|
.optional();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Clara
|
||||||
|
} // namespace Catch
|
@ -6,46 +6,656 @@
|
|||||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
|
#ifndef CATCH_CLARA_HPP_INCLUDED
|
||||||
#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
|
#define CATCH_CLARA_HPP_INCLUDED
|
||||||
|
|
||||||
#include <catch2/internal/catch_console_width.hpp>
|
#if defined( __clang__ )
|
||||||
|
# pragma clang diagnostic push
|
||||||
// Use Catch's value for console width (store Clara's off to the side, if present)
|
# pragma clang diagnostic ignored "-Wweak-vtables"
|
||||||
#ifdef CLARA_CONFIG_CONSOLE_WIDTH
|
# pragma clang diagnostic ignored "-Wshadow"
|
||||||
#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
# pragma clang diagnostic ignored "-Wdeprecated"
|
||||||
#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
|
||||||
#endif
|
|
||||||
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wweak-vtables"
|
|
||||||
#pragma clang diagnostic ignored "-Wshadow"
|
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined( __GNUC__ )
|
||||||
#pragma GCC diagnostic push
|
# pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <catch2/internal/catch_clara_upstream.hpp>
|
#ifndef CLARA_CONFIG_OPTIONAL_TYPE
|
||||||
|
# ifdef __has_include
|
||||||
#if defined(__clang__)
|
# if __has_include( <optional>) && __cplusplus >= 201703L
|
||||||
#pragma clang diagnostic pop
|
# include <optional>
|
||||||
|
# define CLARA_CONFIG_OPTIONAL_TYPE std::optional
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#include <cassert>
|
||||||
#pragma GCC diagnostic pop
|
#include <cctype>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
namespace Clara {
|
||||||
|
|
||||||
|
class Args;
|
||||||
|
class Parser;
|
||||||
|
|
||||||
|
// enum of result types from a parse
|
||||||
|
enum class ParseResultType {
|
||||||
|
Matched,
|
||||||
|
NoMatch,
|
||||||
|
ShortCircuitAll,
|
||||||
|
ShortCircuitSame
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Detail {
|
||||||
|
|
||||||
|
// Traits for extracting arg and return type of lambdas (for single
|
||||||
|
// argument lambdas)
|
||||||
|
template <typename L>
|
||||||
|
struct UnaryLambdaTraits
|
||||||
|
: UnaryLambdaTraits<decltype( &L::operator() )> {};
|
||||||
|
|
||||||
|
template <typename ClassT, typename ReturnT, typename... Args>
|
||||||
|
struct UnaryLambdaTraits<ReturnT ( ClassT::* )( Args... ) const> {
|
||||||
|
static const bool isValid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ClassT, typename ReturnT, typename ArgT>
|
||||||
|
struct UnaryLambdaTraits<ReturnT ( ClassT::* )( ArgT ) const> {
|
||||||
|
static const bool isValid = true;
|
||||||
|
using ArgType = typename std::remove_const<
|
||||||
|
typename std::remove_reference<ArgT>::type>::type;
|
||||||
|
using ReturnType = ReturnT;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TokenStream;
|
||||||
|
|
||||||
|
// Wraps a token coming from a token stream. These may not directly
|
||||||
|
// correspond to strings as a single string may encode an option +
|
||||||
|
// its argument if the : or = form is used
|
||||||
|
enum class TokenType { Option, Argument };
|
||||||
|
struct Token {
|
||||||
|
TokenType type;
|
||||||
|
std::string token;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Abstracts iterators into args as a stream of tokens, with option
|
||||||
|
// arguments uniformly handled
|
||||||
|
class TokenStream {
|
||||||
|
using Iterator = std::vector<std::string>::const_iterator;
|
||||||
|
Iterator it;
|
||||||
|
Iterator itEnd;
|
||||||
|
std::vector<Token> m_tokenBuffer;
|
||||||
|
|
||||||
|
void loadBuffer();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TokenStream( Args const& args );
|
||||||
|
TokenStream( Iterator it, Iterator itEnd );
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !m_tokenBuffer.empty() || it != itEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count() const {
|
||||||
|
return m_tokenBuffer.size() + ( itEnd - it );
|
||||||
|
}
|
||||||
|
|
||||||
|
Token operator*() const {
|
||||||
|
assert( !m_tokenBuffer.empty() );
|
||||||
|
return m_tokenBuffer.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
Token const* operator->() const {
|
||||||
|
assert( !m_tokenBuffer.empty() );
|
||||||
|
return &m_tokenBuffer.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenStream& operator++();
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResultBase {
|
||||||
|
public:
|
||||||
|
enum Type { Ok, LogicError, RuntimeError };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ResultBase( Type type ): m_type( type ) {}
|
||||||
|
virtual ~ResultBase() = default;
|
||||||
|
|
||||||
|
virtual void enforceOk() const = 0;
|
||||||
|
|
||||||
|
Type m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class ResultValueBase : public ResultBase {
|
||||||
|
public:
|
||||||
|
auto value() const -> T const& {
|
||||||
|
enforceOk();
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ResultValueBase( Type type ): ResultBase( type ) {}
|
||||||
|
|
||||||
|
ResultValueBase( ResultValueBase const& other ):
|
||||||
|
ResultBase( other ) {
|
||||||
|
if ( m_type == ResultBase::Ok )
|
||||||
|
new ( &m_value ) T( other.m_value );
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultValueBase( Type, T const& value ): ResultBase( Ok ) {
|
||||||
|
new ( &m_value ) T( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator=( ResultValueBase const& other )
|
||||||
|
-> ResultValueBase& {
|
||||||
|
if ( m_type == ResultBase::Ok )
|
||||||
|
m_value.~T();
|
||||||
|
ResultBase::operator=( other );
|
||||||
|
if ( m_type == ResultBase::Ok )
|
||||||
|
new ( &m_value ) T( other.m_value );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ResultValueBase() override {
|
||||||
|
if ( m_type == Ok )
|
||||||
|
m_value.~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
union {
|
||||||
|
T m_value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> class ResultValueBase<void> : public ResultBase {
|
||||||
|
protected:
|
||||||
|
using ResultBase::ResultBase;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T = void>
|
||||||
|
class BasicResult : public ResultValueBase<T> {
|
||||||
|
public:
|
||||||
|
template <typename U>
|
||||||
|
explicit BasicResult( BasicResult<U> const& other ):
|
||||||
|
ResultValueBase<T>( other.type() ),
|
||||||
|
m_errorMessage( other.errorMessage() ) {
|
||||||
|
assert( type() != ResultBase::Ok );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static auto ok( U const& value ) -> BasicResult {
|
||||||
|
return { ResultBase::Ok, value };
|
||||||
|
}
|
||||||
|
static auto ok() -> BasicResult { return { ResultBase::Ok }; }
|
||||||
|
static auto logicError( std::string const& message )
|
||||||
|
-> BasicResult {
|
||||||
|
return { ResultBase::LogicError, message };
|
||||||
|
}
|
||||||
|
static auto runtimeError( std::string const& message )
|
||||||
|
-> BasicResult {
|
||||||
|
return { ResultBase::RuntimeError, message };
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return m_type == ResultBase::Ok;
|
||||||
|
}
|
||||||
|
auto type() const -> ResultBase::Type { return m_type; }
|
||||||
|
auto errorMessage() const -> std::string {
|
||||||
|
return m_errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void enforceOk() const override {
|
||||||
|
|
||||||
|
// Errors shouldn't reach this point, but if they do
|
||||||
|
// the actual error message will be in m_errorMessage
|
||||||
|
assert( m_type != ResultBase::LogicError );
|
||||||
|
assert( m_type != ResultBase::RuntimeError );
|
||||||
|
if ( m_type != ResultBase::Ok )
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
m_errorMessage; // Only populated if resultType is an error
|
||||||
|
|
||||||
|
BasicResult( ResultBase::Type type,
|
||||||
|
std::string const& message ):
|
||||||
|
ResultValueBase<T>( type ), m_errorMessage( message ) {
|
||||||
|
assert( m_type != ResultBase::Ok );
|
||||||
|
}
|
||||||
|
|
||||||
|
using ResultValueBase<T>::ResultValueBase;
|
||||||
|
using ResultBase::m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ParseState {
|
||||||
|
public:
|
||||||
|
ParseState( ParseResultType type,
|
||||||
|
TokenStream const& remainingTokens );
|
||||||
|
|
||||||
|
ParseResultType type() const { return m_type; }
|
||||||
|
TokenStream const& remainingTokens() const {
|
||||||
|
return m_remainingTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParseResultType m_type;
|
||||||
|
TokenStream m_remainingTokens;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Result = BasicResult<void>;
|
||||||
|
using ParserResult = BasicResult<ParseResultType>;
|
||||||
|
using InternalParseResult = BasicResult<ParseState>;
|
||||||
|
|
||||||
|
struct HelpColumns {
|
||||||
|
std::string left;
|
||||||
|
std::string right;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ParserResult convertInto( std::string const& source, T& target ) {
|
||||||
|
std::stringstream ss( source );
|
||||||
|
ss >> target;
|
||||||
|
if ( ss.fail() ) {
|
||||||
|
return ParserResult::runtimeError(
|
||||||
|
"Unable to convert '" + source +
|
||||||
|
"' to destination type" );
|
||||||
|
} else {
|
||||||
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParserResult convertInto( std::string const& source,
|
||||||
|
std::string& target );
|
||||||
|
ParserResult convertInto( std::string const& source, bool& target );
|
||||||
|
|
||||||
|
#ifdef CLARA_CONFIG_OPTIONAL_TYPE
|
||||||
|
template <typename T>
|
||||||
|
auto convertInto( std::string const& source,
|
||||||
|
CLARA_CONFIG_OPTIONAL_TYPE<T>& target )
|
||||||
|
-> ParserResult {
|
||||||
|
T temp;
|
||||||
|
auto result = convertInto( source, temp );
|
||||||
|
if ( result )
|
||||||
|
target = std::move( temp );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif // CLARA_CONFIG_OPTIONAL_TYPE
|
||||||
|
|
||||||
|
struct NonCopyable {
|
||||||
|
NonCopyable() = default;
|
||||||
|
NonCopyable( NonCopyable const& ) = delete;
|
||||||
|
NonCopyable( NonCopyable&& ) = delete;
|
||||||
|
NonCopyable& operator=( NonCopyable const& ) = delete;
|
||||||
|
NonCopyable& operator=( NonCopyable&& ) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundRef : NonCopyable {
|
||||||
|
virtual ~BoundRef() = default;
|
||||||
|
virtual auto isContainer() const -> bool { return false; }
|
||||||
|
virtual auto isFlag() const -> bool { return false; }
|
||||||
|
};
|
||||||
|
struct BoundValueRefBase : BoundRef {
|
||||||
|
virtual auto setValue( std::string const& arg )
|
||||||
|
-> ParserResult = 0;
|
||||||
|
};
|
||||||
|
struct BoundFlagRefBase : BoundRef {
|
||||||
|
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
||||||
|
bool isFlag() const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct BoundValueRef : BoundValueRefBase {
|
||||||
|
T& m_ref;
|
||||||
|
|
||||||
|
explicit BoundValueRef( T& ref ): m_ref( ref ) {}
|
||||||
|
|
||||||
|
auto setValue( std::string const& arg )
|
||||||
|
-> ParserResult override {
|
||||||
|
return convertInto( arg, m_ref );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
|
||||||
|
std::vector<T>& m_ref;
|
||||||
|
|
||||||
|
explicit BoundValueRef( std::vector<T>& ref ): m_ref( ref ) {}
|
||||||
|
|
||||||
|
auto isContainer() const -> bool override { return true; }
|
||||||
|
|
||||||
|
auto setValue( std::string const& arg )
|
||||||
|
-> ParserResult override {
|
||||||
|
T temp;
|
||||||
|
auto result = convertInto( arg, temp );
|
||||||
|
if ( result )
|
||||||
|
m_ref.push_back( temp );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundFlagRef : BoundFlagRefBase {
|
||||||
|
bool& m_ref;
|
||||||
|
|
||||||
|
explicit BoundFlagRef( bool& ref ): m_ref( ref ) {}
|
||||||
|
|
||||||
|
ParserResult setFlag( bool flag ) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ReturnType> struct LambdaInvoker {
|
||||||
|
static_assert(
|
||||||
|
std::is_same<ReturnType, ParserResult>::value,
|
||||||
|
"Lambda must return void or clara::ParserResult" );
|
||||||
|
|
||||||
|
template <typename L, typename ArgType>
|
||||||
|
static auto invoke( L const& lambda, ArgType const& arg )
|
||||||
|
-> ParserResult {
|
||||||
|
return lambda( arg );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct LambdaInvoker<void> {
|
||||||
|
template <typename L, typename ArgType>
|
||||||
|
static auto invoke( L const& lambda, ArgType const& arg )
|
||||||
|
-> ParserResult {
|
||||||
|
lambda( arg );
|
||||||
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ArgType, typename L>
|
||||||
|
auto invokeLambda( L const& lambda, std::string const& arg )
|
||||||
|
-> ParserResult {
|
||||||
|
ArgType temp{};
|
||||||
|
auto result = convertInto( arg, temp );
|
||||||
|
return !result ? result
|
||||||
|
: LambdaInvoker<typename UnaryLambdaTraits<
|
||||||
|
L>::ReturnType>::invoke( lambda, temp );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L> struct BoundLambda : BoundValueRefBase {
|
||||||
|
L m_lambda;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
UnaryLambdaTraits<L>::isValid,
|
||||||
|
"Supplied lambda must take exactly one argument" );
|
||||||
|
explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {}
|
||||||
|
|
||||||
|
auto setValue( std::string const& arg )
|
||||||
|
-> ParserResult override {
|
||||||
|
return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>(
|
||||||
|
m_lambda, arg );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L> struct BoundFlagLambda : BoundFlagRefBase {
|
||||||
|
L m_lambda;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
UnaryLambdaTraits<L>::isValid,
|
||||||
|
"Supplied lambda must take exactly one argument" );
|
||||||
|
static_assert(
|
||||||
|
std::is_same<typename UnaryLambdaTraits<L>::ArgType,
|
||||||
|
bool>::value,
|
||||||
|
"flags must be boolean" );
|
||||||
|
|
||||||
|
explicit BoundFlagLambda( L const& lambda ):
|
||||||
|
m_lambda( lambda ) {}
|
||||||
|
|
||||||
|
auto setFlag( bool flag ) -> ParserResult override {
|
||||||
|
return LambdaInvoker<typename UnaryLambdaTraits<
|
||||||
|
L>::ReturnType>::invoke( m_lambda, flag );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Optionality { Optional, Required };
|
||||||
|
|
||||||
|
class ParserBase {
|
||||||
|
public:
|
||||||
|
virtual ~ParserBase() = default;
|
||||||
|
virtual auto validate() const -> Result { return Result::ok(); }
|
||||||
|
virtual auto parse( std::string const& exeName,
|
||||||
|
TokenStream const& tokens ) const
|
||||||
|
-> InternalParseResult = 0;
|
||||||
|
virtual auto cardinality() const -> size_t { return 1; }
|
||||||
|
|
||||||
|
InternalParseResult parse( Args const& args ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename DerivedT>
|
||||||
|
class ComposableParserImpl : public ParserBase {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
auto operator|( T const& other ) const -> Parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Common code and state for Args and Opts
|
||||||
|
template <typename DerivedT>
|
||||||
|
class ParserRefImpl : public ComposableParserImpl<DerivedT> {
|
||||||
|
protected:
|
||||||
|
Optionality m_optionality = Optionality::Optional;
|
||||||
|
std::shared_ptr<BoundRef> m_ref;
|
||||||
|
std::string m_hint;
|
||||||
|
std::string m_description;
|
||||||
|
|
||||||
|
explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ):
|
||||||
|
m_ref( ref ) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
ParserRefImpl( T& ref, std::string const& hint ):
|
||||||
|
m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
|
||||||
|
m_hint( hint ) {}
|
||||||
|
|
||||||
|
template <typename LambdaT>
|
||||||
|
ParserRefImpl( LambdaT const& ref, std::string const& hint ):
|
||||||
|
m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
|
||||||
|
m_hint( hint ) {}
|
||||||
|
|
||||||
|
auto operator()( std::string const& description ) -> DerivedT& {
|
||||||
|
m_description = description;
|
||||||
|
return static_cast<DerivedT&>( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto optional() -> DerivedT& {
|
||||||
|
m_optionality = Optionality::Optional;
|
||||||
|
return static_cast<DerivedT&>( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto required() -> DerivedT& {
|
||||||
|
m_optionality = Optionality::Required;
|
||||||
|
return static_cast<DerivedT&>( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto isOptional() const -> bool {
|
||||||
|
return m_optionality == Optionality::Optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cardinality() const -> size_t override {
|
||||||
|
if ( m_ref->isContainer() )
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const& hint() const { return m_hint; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
// A parser for arguments
|
||||||
|
class Arg : public Detail::ParserRefImpl<Arg> {
|
||||||
|
public:
|
||||||
|
using ParserRefImpl::ParserRefImpl;
|
||||||
|
|
||||||
|
Detail::InternalParseResult
|
||||||
|
parse(std::string const&,
|
||||||
|
Detail::TokenStream const& tokens) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A parser for options
|
||||||
|
class Opt : public Detail::ParserRefImpl<Opt> {
|
||||||
|
protected:
|
||||||
|
std::vector<std::string> m_optNames;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename LambdaT>
|
||||||
|
explicit Opt(LambdaT const& ref) :
|
||||||
|
ParserRefImpl(
|
||||||
|
std::make_shared<Detail::BoundFlagLambda<LambdaT>>(ref)) {}
|
||||||
|
|
||||||
|
explicit Opt(bool& ref);
|
||||||
|
|
||||||
|
template <typename LambdaT>
|
||||||
|
Opt(LambdaT const& ref, std::string const& hint) :
|
||||||
|
ParserRefImpl(ref, hint) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Opt(T& ref, std::string const& hint) :
|
||||||
|
ParserRefImpl(ref, hint) {}
|
||||||
|
|
||||||
|
auto operator[](std::string const& optName) -> Opt& {
|
||||||
|
m_optNames.push_back(optName);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Detail::HelpColumns> getHelpColumns() const;
|
||||||
|
|
||||||
|
bool isMatch(std::string const& optToken) const;
|
||||||
|
|
||||||
|
using ParserBase::parse;
|
||||||
|
|
||||||
|
Detail::InternalParseResult
|
||||||
|
parse(std::string const&,
|
||||||
|
Detail::TokenStream const& tokens) const override;
|
||||||
|
|
||||||
|
Detail::Result validate() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specifies the name of the executable
|
||||||
|
class ExeName : public Detail::ComposableParserImpl<ExeName> {
|
||||||
|
std::shared_ptr<std::string> m_name;
|
||||||
|
std::shared_ptr<Detail::BoundValueRefBase> m_ref;
|
||||||
|
|
||||||
|
template <typename LambdaT>
|
||||||
|
static auto makeRef(LambdaT const& lambda)
|
||||||
|
-> std::shared_ptr<Detail::BoundValueRefBase> {
|
||||||
|
return std::make_shared<Detail::BoundLambda<LambdaT>>(lambda);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ExeName();
|
||||||
|
explicit ExeName(std::string& ref);
|
||||||
|
|
||||||
|
template <typename LambdaT>
|
||||||
|
explicit ExeName(LambdaT const& lambda) : ExeName() {
|
||||||
|
m_ref = std::make_shared<Detail::BoundLambda<LambdaT>>(lambda);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The exe name is not parsed out of the normal tokens, but is
|
||||||
|
// handled specially
|
||||||
|
Detail::InternalParseResult
|
||||||
|
parse(std::string const&,
|
||||||
|
Detail::TokenStream const& tokens) const override;
|
||||||
|
|
||||||
|
std::string const& name() const { return *m_name; }
|
||||||
|
Detail::ParserResult set(std::string const& newName);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A Combined parser
|
||||||
|
class Parser : Detail::ParserBase {
|
||||||
|
mutable ExeName m_exeName;
|
||||||
|
std::vector<Opt> m_options;
|
||||||
|
std::vector<Arg> m_args;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
auto operator|=(ExeName const& exeName) -> Parser& {
|
||||||
|
m_exeName = exeName;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator|=(Arg const& arg) -> Parser& {
|
||||||
|
m_args.push_back(arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator|=(Opt const& opt) -> Parser& {
|
||||||
|
m_options.push_back(opt);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser& operator|=(Parser const& other);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto operator|(T const& other) const -> Parser {
|
||||||
|
return Parser(*this) |= other;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Detail::HelpColumns> getHelpColumns() const;
|
||||||
|
|
||||||
|
void writeToStream(std::ostream& os) const;
|
||||||
|
|
||||||
|
friend auto operator<<(std::ostream& os, Parser const& parser)
|
||||||
|
-> std::ostream& {
|
||||||
|
parser.writeToStream(os);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
Detail::Result validate() const override;
|
||||||
|
|
||||||
|
using ParserBase::parse;
|
||||||
|
Detail::InternalParseResult
|
||||||
|
parse(std::string const& exeName,
|
||||||
|
Detail::TokenStream const& tokens) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transport for raw args (copied from main args, or supplied via
|
||||||
|
// init list for testing)
|
||||||
|
class Args {
|
||||||
|
friend Detail::TokenStream;
|
||||||
|
std::string m_exeName;
|
||||||
|
std::vector<std::string> m_args;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Args(int argc, char const* const* argv);
|
||||||
|
Args(std::initializer_list<std::string> args);
|
||||||
|
|
||||||
|
std::string const& exeName() const { return m_exeName; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Convenience wrapper for option parser that specifies the help option
|
||||||
|
struct Help : Opt {
|
||||||
|
Help(bool& showHelpFlag);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Result type for parser operation
|
||||||
|
using Detail::ParserResult;
|
||||||
|
|
||||||
|
namespace Detail {
|
||||||
|
template <typename DerivedT>
|
||||||
|
template <typename T>
|
||||||
|
Parser
|
||||||
|
ComposableParserImpl<DerivedT>::operator|(T const& other) const {
|
||||||
|
return Parser() | static_cast<DerivedT const&>(*this) | other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Clara
|
||||||
|
} // namespace Catch
|
||||||
|
|
||||||
|
#if defined( __clang__ )
|
||||||
|
# pragma clang diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined( __GNUC__ )
|
||||||
// Restore Clara's value for console width, if present
|
# pragma GCC diagnostic pop
|
||||||
#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
|
|
||||||
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
|
|
||||||
#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
|
#endif // CATCH_CLARA_HPP_INCLUDED
|
||||||
|
@ -1,924 +0,0 @@
|
|||||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
// See https://github.com/philsquared/Clara for more details
|
|
||||||
|
|
||||||
// Clara v1.1.5
|
|
||||||
|
|
||||||
#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 CLARA_CONFIG_OPTIONAL_TYPE
|
|
||||||
#ifdef __has_include
|
|
||||||
#if __has_include(<optional>) && __cplusplus >= 201703L
|
|
||||||
#include <optional>
|
|
||||||
#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <catch2/internal/catch_textflow.hpp>
|
|
||||||
|
|
||||||
// ........... back in clara.hpp
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <ostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
|
|
||||||
#define CATCH_PLATFORM_WINDOWS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Catch { namespace clara {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// Traits for extracting arg and return type of lambdas (for single argument lambdas)
|
|
||||||
template<typename L>
|
|
||||||
struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
|
|
||||||
|
|
||||||
template<typename ClassT, typename ReturnT, typename... Args>
|
|
||||||
struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
|
|
||||||
static const bool isValid = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ClassT, typename ReturnT, typename ArgT>
|
|
||||||
struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
|
|
||||||
static const bool isValid = true;
|
|
||||||
using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
|
|
||||||
using ReturnType = ReturnT;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TokenStream;
|
|
||||||
|
|
||||||
// Transport for raw args (copied from main args, or supplied via init list for testing)
|
|
||||||
class Args {
|
|
||||||
friend TokenStream;
|
|
||||||
std::string m_exeName;
|
|
||||||
std::vector<std::string> m_args;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Args( int argc, char const* const* argv )
|
|
||||||
: m_exeName(argv[0]),
|
|
||||||
m_args(argv + 1, argv + argc) {}
|
|
||||||
|
|
||||||
Args( std::initializer_list<std::string> args )
|
|
||||||
: m_exeName( *args.begin() ),
|
|
||||||
m_args( args.begin()+1, args.end() )
|
|
||||||
{}
|
|
||||||
|
|
||||||
auto exeName() const -> std::string {
|
|
||||||
return m_exeName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
|
|
||||||
// may encode an option + its argument if the : or = form is used
|
|
||||||
enum class TokenType {
|
|
||||||
Option, Argument
|
|
||||||
};
|
|
||||||
struct Token {
|
|
||||||
TokenType type;
|
|
||||||
std::string token;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline auto isOptPrefix( char c ) -> bool {
|
|
||||||
return c == '-'
|
|
||||||
#ifdef CATCH_PLATFORM_WINDOWS
|
|
||||||
|| c == '/'
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
|
|
||||||
class TokenStream {
|
|
||||||
using Iterator = std::vector<std::string>::const_iterator;
|
|
||||||
Iterator it;
|
|
||||||
Iterator itEnd;
|
|
||||||
std::vector<Token> m_tokenBuffer;
|
|
||||||
|
|
||||||
void loadBuffer() {
|
|
||||||
m_tokenBuffer.resize( 0 );
|
|
||||||
|
|
||||||
// Skip any empty strings
|
|
||||||
while( it != itEnd && it->empty() )
|
|
||||||
++it;
|
|
||||||
|
|
||||||
if( it != itEnd ) {
|
|
||||||
auto const &next = *it;
|
|
||||||
if( isOptPrefix( next[0] ) ) {
|
|
||||||
auto delimiterPos = next.find_first_of( " :=" );
|
|
||||||
if( delimiterPos != std::string::npos ) {
|
|
||||||
m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
|
|
||||||
m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
|
|
||||||
} else {
|
|
||||||
if( next[1] != '-' && next.size() > 2 ) {
|
|
||||||
std::string opt = "- ";
|
|
||||||
for( size_t i = 1; i < next.size(); ++i ) {
|
|
||||||
opt[1] = next[i];
|
|
||||||
m_tokenBuffer.push_back( { TokenType::Option, opt } );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_tokenBuffer.push_back( { TokenType::Option, next } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_tokenBuffer.push_back( { TokenType::Argument, next } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
|
|
||||||
|
|
||||||
TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
|
|
||||||
loadBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return !m_tokenBuffer.empty() || it != itEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
|
|
||||||
|
|
||||||
auto operator*() const -> Token {
|
|
||||||
assert( !m_tokenBuffer.empty() );
|
|
||||||
return m_tokenBuffer.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator->() const -> Token const * {
|
|
||||||
assert( !m_tokenBuffer.empty() );
|
|
||||||
return &m_tokenBuffer.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator++() -> TokenStream & {
|
|
||||||
if( m_tokenBuffer.size() >= 2 ) {
|
|
||||||
m_tokenBuffer.erase( m_tokenBuffer.begin() );
|
|
||||||
} else {
|
|
||||||
if( it != itEnd )
|
|
||||||
++it;
|
|
||||||
loadBuffer();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ResultBase {
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
Ok, LogicError, RuntimeError
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ResultBase( Type type ) : m_type( type ) {}
|
|
||||||
virtual ~ResultBase() = default;
|
|
||||||
|
|
||||||
virtual void enforceOk() const = 0;
|
|
||||||
|
|
||||||
Type m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class ResultValueBase : public ResultBase {
|
|
||||||
public:
|
|
||||||
auto value() const -> T const & {
|
|
||||||
enforceOk();
|
|
||||||
return m_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ResultValueBase( Type type ) : ResultBase( type ) {}
|
|
||||||
|
|
||||||
ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
|
|
||||||
if( m_type == ResultBase::Ok )
|
|
||||||
new( &m_value ) T( other.m_value );
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
|
|
||||||
new( &m_value ) T( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
|
|
||||||
if( m_type == ResultBase::Ok )
|
|
||||||
m_value.~T();
|
|
||||||
ResultBase::operator=(other);
|
|
||||||
if( m_type == ResultBase::Ok )
|
|
||||||
new( &m_value ) T( other.m_value );
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ResultValueBase() override {
|
|
||||||
if( m_type == Ok )
|
|
||||||
m_value.~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
union {
|
|
||||||
T m_value;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
class ResultValueBase<void> : public ResultBase {
|
|
||||||
protected:
|
|
||||||
using ResultBase::ResultBase;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T = void>
|
|
||||||
class BasicResult : public ResultValueBase<T> {
|
|
||||||
public:
|
|
||||||
template<typename U>
|
|
||||||
explicit BasicResult( BasicResult<U> const &other )
|
|
||||||
: ResultValueBase<T>( other.type() ),
|
|
||||||
m_errorMessage( other.errorMessage() )
|
|
||||||
{
|
|
||||||
assert( type() != ResultBase::Ok );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
|
||||||
static auto ok() -> BasicResult { return { ResultBase::Ok }; }
|
|
||||||
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
|
||||||
static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
|
|
||||||
|
|
||||||
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
|
||||||
auto type() const -> ResultBase::Type { return m_type; }
|
|
||||||
auto errorMessage() const -> std::string { return m_errorMessage; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void enforceOk() const override {
|
|
||||||
|
|
||||||
// Errors shouldn't reach this point, but if they do
|
|
||||||
// the actual error message will be in m_errorMessage
|
|
||||||
assert( m_type != ResultBase::LogicError );
|
|
||||||
assert( m_type != ResultBase::RuntimeError );
|
|
||||||
if( m_type != ResultBase::Ok )
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string m_errorMessage; // Only populated if resultType is an error
|
|
||||||
|
|
||||||
BasicResult( ResultBase::Type type, std::string const &message )
|
|
||||||
: ResultValueBase<T>(type),
|
|
||||||
m_errorMessage(message)
|
|
||||||
{
|
|
||||||
assert( m_type != ResultBase::Ok );
|
|
||||||
}
|
|
||||||
|
|
||||||
using ResultValueBase<T>::ResultValueBase;
|
|
||||||
using ResultBase::m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ParseResultType {
|
|
||||||
Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
|
|
||||||
};
|
|
||||||
|
|
||||||
class ParseState {
|
|
||||||
public:
|
|
||||||
|
|
||||||
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
|
||||||
: m_type(type),
|
|
||||||
m_remainingTokens( remainingTokens )
|
|
||||||
{}
|
|
||||||
|
|
||||||
auto type() const -> ParseResultType { return m_type; }
|
|
||||||
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ParseResultType m_type;
|
|
||||||
TokenStream m_remainingTokens;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Result = BasicResult<void>;
|
|
||||||
using ParserResult = BasicResult<ParseResultType>;
|
|
||||||
using InternalParseResult = BasicResult<ParseState>;
|
|
||||||
|
|
||||||
struct HelpColumns {
|
|
||||||
std::string left;
|
|
||||||
std::string right;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << source;
|
|
||||||
ss >> target;
|
|
||||||
if( ss.fail() )
|
|
||||||
return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
|
|
||||||
else
|
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
|
||||||
}
|
|
||||||
inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
|
|
||||||
target = source;
|
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
|
||||||
}
|
|
||||||
inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
|
|
||||||
std::string srcLC = source;
|
|
||||||
std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::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 );
|
|
||||||
}
|
|
||||||
#ifdef CLARA_CONFIG_OPTIONAL_TYPE
|
|
||||||
template<typename T>
|
|
||||||
inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
|
|
||||||
T temp;
|
|
||||||
auto result = convertInto( source, temp );
|
|
||||||
if( result )
|
|
||||||
target = std::move(temp);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif // CLARA_CONFIG_OPTIONAL_TYPE
|
|
||||||
|
|
||||||
struct NonCopyable {
|
|
||||||
NonCopyable() = default;
|
|
||||||
NonCopyable( NonCopyable const & ) = delete;
|
|
||||||
NonCopyable( NonCopyable && ) = delete;
|
|
||||||
NonCopyable &operator=( NonCopyable const & ) = delete;
|
|
||||||
NonCopyable &operator=( NonCopyable && ) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BoundRef : NonCopyable {
|
|
||||||
virtual ~BoundRef() = default;
|
|
||||||
virtual auto isContainer() const -> bool { return false; }
|
|
||||||
virtual auto isFlag() const -> bool { return false; }
|
|
||||||
};
|
|
||||||
struct BoundValueRefBase : BoundRef {
|
|
||||||
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
|
||||||
};
|
|
||||||
struct BoundFlagRefBase : BoundRef {
|
|
||||||
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
|
||||||
bool isFlag() const override { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct BoundValueRef : BoundValueRefBase {
|
|
||||||
T &m_ref;
|
|
||||||
|
|
||||||
explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
|
|
||||||
|
|
||||||
auto setValue( std::string const &arg ) -> ParserResult override {
|
|
||||||
return convertInto( arg, m_ref );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
|
|
||||||
std::vector<T> &m_ref;
|
|
||||||
|
|
||||||
explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
|
|
||||||
|
|
||||||
auto isContainer() const -> bool override { return true; }
|
|
||||||
|
|
||||||
auto setValue( std::string const &arg ) -> ParserResult override {
|
|
||||||
T temp;
|
|
||||||
auto result = convertInto( arg, temp );
|
|
||||||
if( result )
|
|
||||||
m_ref.push_back( temp );
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BoundFlagRef : BoundFlagRefBase {
|
|
||||||
bool &m_ref;
|
|
||||||
|
|
||||||
explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
|
|
||||||
|
|
||||||
auto setFlag( bool flag ) -> ParserResult override {
|
|
||||||
m_ref = flag;
|
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ReturnType>
|
|
||||||
struct LambdaInvoker {
|
|
||||||
static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
|
|
||||||
|
|
||||||
template<typename L, typename ArgType>
|
|
||||||
static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
|
|
||||||
return lambda( arg );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct LambdaInvoker<void> {
|
|
||||||
template<typename L, typename ArgType>
|
|
||||||
static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
|
|
||||||
lambda( arg );
|
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ArgType, typename L>
|
|
||||||
inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
|
|
||||||
ArgType temp{};
|
|
||||||
auto result = convertInto( arg, temp );
|
|
||||||
return !result
|
|
||||||
? result
|
|
||||||
: LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename L>
|
|
||||||
struct BoundLambda : BoundValueRefBase {
|
|
||||||
L m_lambda;
|
|
||||||
|
|
||||||
static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
|
|
||||||
explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
|
|
||||||
|
|
||||||
auto setValue( std::string const &arg ) -> ParserResult override {
|
|
||||||
return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename L>
|
|
||||||
struct BoundFlagLambda : BoundFlagRefBase {
|
|
||||||
L m_lambda;
|
|
||||||
|
|
||||||
static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
|
|
||||||
static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
|
|
||||||
|
|
||||||
explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
|
|
||||||
|
|
||||||
auto setFlag( bool flag ) -> ParserResult override {
|
|
||||||
return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Optionality { Optional, Required };
|
|
||||||
|
|
||||||
struct Parser;
|
|
||||||
|
|
||||||
class ParserBase {
|
|
||||||
public:
|
|
||||||
virtual ~ParserBase() = default;
|
|
||||||
virtual auto validate() const -> Result { return Result::ok(); }
|
|
||||||
virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0;
|
|
||||||
virtual auto cardinality() const -> size_t { return 1; }
|
|
||||||
|
|
||||||
auto parse( Args const &args ) const -> InternalParseResult {
|
|
||||||
return parse( args.exeName(), TokenStream( args ) );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename DerivedT>
|
|
||||||
class ComposableParserImpl : public ParserBase {
|
|
||||||
public:
|
|
||||||
template<typename T>
|
|
||||||
auto operator|( T const &other ) const -> Parser;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
auto operator+( T const &other ) const -> Parser;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Common code and state for Args and Opts
|
|
||||||
template<typename DerivedT>
|
|
||||||
class ParserRefImpl : public ComposableParserImpl<DerivedT> {
|
|
||||||
protected:
|
|
||||||
Optionality m_optionality = Optionality::Optional;
|
|
||||||
std::shared_ptr<BoundRef> m_ref;
|
|
||||||
std::string m_hint;
|
|
||||||
std::string m_description;
|
|
||||||
|
|
||||||
explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<typename T>
|
|
||||||
ParserRefImpl( T &ref, std::string const &hint )
|
|
||||||
: m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
|
|
||||||
m_hint( hint )
|
|
||||||
{}
|
|
||||||
|
|
||||||
template<typename LambdaT>
|
|
||||||
ParserRefImpl( LambdaT const &ref, std::string const &hint )
|
|
||||||
: m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
|
|
||||||
m_hint(hint)
|
|
||||||
{}
|
|
||||||
|
|
||||||
auto operator()( std::string const &description ) -> DerivedT & {
|
|
||||||
m_description = description;
|
|
||||||
return static_cast<DerivedT &>( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto optional() -> DerivedT & {
|
|
||||||
m_optionality = Optionality::Optional;
|
|
||||||
return static_cast<DerivedT &>( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto required() -> DerivedT & {
|
|
||||||
m_optionality = Optionality::Required;
|
|
||||||
return static_cast<DerivedT &>( *this );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto isOptional() const -> bool {
|
|
||||||
return m_optionality == Optionality::Optional;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto cardinality() const -> size_t override {
|
|
||||||
if( m_ref->isContainer() )
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto hint() const -> std::string { return m_hint; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExeName : public ComposableParserImpl<ExeName> {
|
|
||||||
std::shared_ptr<std::string> m_name;
|
|
||||||
std::shared_ptr<BoundValueRefBase> m_ref;
|
|
||||||
|
|
||||||
template<typename LambdaT>
|
|
||||||
static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
|
|
||||||
return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
|
|
||||||
|
|
||||||
explicit ExeName( std::string &ref ) : ExeName() {
|
|
||||||
m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename LambdaT>
|
|
||||||
explicit ExeName( LambdaT const& lambda ) : ExeName() {
|
|
||||||
m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
|
|
||||||
}
|
|
||||||
|
|
||||||
// The exe name is not parsed out of the normal tokens, but is handled specially
|
|
||||||
auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
|
|
||||||
return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto name() const -> std::string { return *m_name; }
|
|
||||||
auto set( std::string const& newName ) -> ParserResult {
|
|
||||||
|
|
||||||
auto lastSlash = newName.find_last_of( "\\/" );
|
|
||||||
auto filename = ( lastSlash == std::string::npos )
|
|
||||||
? newName
|
|
||||||
: newName.substr( lastSlash+1 );
|
|
||||||
|
|
||||||
*m_name = filename;
|
|
||||||
if( m_ref )
|
|
||||||
return m_ref->setValue( filename );
|
|
||||||
else
|
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Arg : public ParserRefImpl<Arg> {
|
|
||||||
public:
|
|
||||||
using ParserRefImpl::ParserRefImpl;
|
|
||||||
|
|
||||||
auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
|
|
||||||
auto validationResult = validate();
|
|
||||||
if( !validationResult )
|
|
||||||
return InternalParseResult( validationResult );
|
|
||||||
|
|
||||||
auto remainingTokens = tokens;
|
|
||||||
auto const &token = *remainingTokens;
|
|
||||||
if( token.type != TokenType::Argument )
|
|
||||||
return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
|
|
||||||
|
|
||||||
assert( !m_ref->isFlag() );
|
|
||||||
auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
|
|
||||||
|
|
||||||
auto result = valueRef->setValue( remainingTokens->token );
|
|
||||||
if( !result )
|
|
||||||
return InternalParseResult( result );
|
|
||||||
else
|
|
||||||
return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline auto normaliseOpt( std::string const &optName ) -> std::string {
|
|
||||||
#ifdef CATCH_PLATFORM_WINDOWS
|
|
||||||
if( optName[0] == '/' )
|
|
||||||
return "-" + optName.substr( 1 );
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
return optName;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Opt : public ParserRefImpl<Opt> {
|
|
||||||
protected:
|
|
||||||
std::vector<std::string> m_optNames;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<typename LambdaT>
|
|
||||||
explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
|
|
||||||
|
|
||||||
explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
|
|
||||||
|
|
||||||
template<typename LambdaT>
|
|
||||||
Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
|
|
||||||
|
|
||||||
auto operator[]( std::string const &optName ) -> Opt & {
|
|
||||||
m_optNames.push_back( optName );
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
|
||||||
std::ostringstream oss;
|
|
||||||
bool first = true;
|
|
||||||
for( auto const &opt : m_optNames ) {
|
|
||||||
if (first)
|
|
||||||
first = false;
|
|
||||||
else
|
|
||||||
oss << ", ";
|
|
||||||
oss << opt;
|
|
||||||
}
|
|
||||||
if( !m_hint.empty() )
|
|
||||||
oss << " <" << m_hint << '>';
|
|
||||||
return { { oss.str(), m_description } };
|
|
||||||
}
|
|
||||||
|
|
||||||
auto isMatch( std::string const &optToken ) const -> bool {
|
|
||||||
auto normalisedToken = normaliseOpt( optToken );
|
|
||||||
for( auto const &name : m_optNames ) {
|
|
||||||
if( normaliseOpt( name ) == normalisedToken )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
using ParserBase::parse;
|
|
||||||
|
|
||||||
auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
|
|
||||||
auto validationResult = validate();
|
|
||||||
if( !validationResult )
|
|
||||||
return InternalParseResult( validationResult );
|
|
||||||
|
|
||||||
auto remainingTokens = tokens;
|
|
||||||
if( remainingTokens && remainingTokens->type == TokenType::Option ) {
|
|
||||||
auto const &token = *remainingTokens;
|
|
||||||
if( isMatch(token.token ) ) {
|
|
||||||
if( m_ref->isFlag() ) {
|
|
||||||
auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
|
|
||||||
auto result = flagRef->setFlag( true );
|
|
||||||
if( !result )
|
|
||||||
return InternalParseResult( result );
|
|
||||||
if( result.value() == ParseResultType::ShortCircuitAll )
|
|
||||||
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
|
|
||||||
} else {
|
|
||||||
auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
|
|
||||||
++remainingTokens;
|
|
||||||
if( !remainingTokens )
|
|
||||||
return InternalParseResult::runtimeError( "Expected argument following " + token.token );
|
|
||||||
auto const &argToken = *remainingTokens;
|
|
||||||
if( argToken.type != TokenType::Argument )
|
|
||||||
return InternalParseResult::runtimeError( "Expected argument following " + token.token );
|
|
||||||
auto result = valueRef->setValue( argToken.token );
|
|
||||||
if( !result )
|
|
||||||
return InternalParseResult( result );
|
|
||||||
if( result.value() == ParseResultType::ShortCircuitAll )
|
|
||||||
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
|
|
||||||
}
|
|
||||||
return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto validate() const -> Result override {
|
|
||||||
if( m_optNames.empty() )
|
|
||||||
return Result::logicError( "No options supplied to Opt" );
|
|
||||||
for( auto const &name : m_optNames ) {
|
|
||||||
if( name.empty() )
|
|
||||||
return Result::logicError( "Option name cannot be empty" );
|
|
||||||
#ifdef CATCH_PLATFORM_WINDOWS
|
|
||||||
if( name[0] != '-' && name[0] != '/' )
|
|
||||||
return Result::logicError( "Option name must begin with '-' or '/'" );
|
|
||||||
#else
|
|
||||||
if( name[0] != '-' )
|
|
||||||
return Result::logicError( "Option name must begin with '-'" );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return ParserRefImpl::validate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Help : Opt {
|
|
||||||
Help( bool &showHelpFlag )
|
|
||||||
: Opt([&]( bool flag ) {
|
|
||||||
showHelpFlag = flag;
|
|
||||||
return ParserResult::ok( ParseResultType::ShortCircuitAll );
|
|
||||||
})
|
|
||||||
{
|
|
||||||
static_cast<Opt &>( *this )
|
|
||||||
("display usage information")
|
|
||||||
["-?"]["-h"]["--help"]
|
|
||||||
.optional();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Parser : ParserBase {
|
|
||||||
|
|
||||||
mutable ExeName m_exeName;
|
|
||||||
std::vector<Opt> m_options;
|
|
||||||
std::vector<Arg> m_args;
|
|
||||||
|
|
||||||
auto operator|=( ExeName const &exeName ) -> Parser & {
|
|
||||||
m_exeName = exeName;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator|=( Arg const &arg ) -> Parser & {
|
|
||||||
m_args.push_back(arg);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator|=( Opt const &opt ) -> Parser & {
|
|
||||||
m_options.push_back(opt);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator|=( Parser const &other ) -> Parser & {
|
|
||||||
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
|
||||||
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
auto operator|( T const &other ) const -> Parser {
|
|
||||||
return Parser( *this ) |= other;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forward deprecated interface with '+' instead of '|'
|
|
||||||
template<typename T>
|
|
||||||
auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
|
|
||||||
template<typename T>
|
|
||||||
auto operator+( T const &other ) const -> Parser { return operator|( other ); }
|
|
||||||
|
|
||||||
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
|
||||||
std::vector<HelpColumns> cols;
|
|
||||||
for (auto const &o : m_options) {
|
|
||||||
auto childCols = o.getHelpColumns();
|
|
||||||
cols.insert( cols.end(), childCols.begin(), childCols.end() );
|
|
||||||
}
|
|
||||||
return cols;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeToStream( std::ostream &os ) const {
|
|
||||||
if (!m_exeName.name().empty()) {
|
|
||||||
os << "usage:\n" << " " << m_exeName.name() << ' ';
|
|
||||||
bool required = true, first = true;
|
|
||||||
for( auto const &arg : m_args ) {
|
|
||||||
if (first)
|
|
||||||
first = false;
|
|
||||||
else
|
|
||||||
os << ' ';
|
|
||||||
if( arg.isOptional() && required ) {
|
|
||||||
os << '[';
|
|
||||||
required = false;
|
|
||||||
}
|
|
||||||
os << '<' << arg.hint() << '>';
|
|
||||||
if( arg.cardinality() == 0 )
|
|
||||||
os << " ... ";
|
|
||||||
}
|
|
||||||
if( !required )
|
|
||||||
os << ']';
|
|
||||||
if( !m_options.empty() )
|
|
||||||
os << " options";
|
|
||||||
os << "\n\nwhere options are:\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rows = getHelpColumns();
|
|
||||||
size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
|
|
||||||
size_t optWidth = 0;
|
|
||||||
for( auto const &cols : rows )
|
|
||||||
optWidth = (std::max)(optWidth, cols.left.size() + 2);
|
|
||||||
|
|
||||||
optWidth = (std::min)(optWidth, consoleWidth/2);
|
|
||||||
|
|
||||||
for( auto const &cols : rows ) {
|
|
||||||
auto row =
|
|
||||||
TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
|
|
||||||
TextFlow::Spacer(4) +
|
|
||||||
TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
|
|
||||||
os << row << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
|
|
||||||
parser.writeToStream( os );
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto validate() const -> Result override {
|
|
||||||
for( auto const &opt : m_options ) {
|
|
||||||
auto result = opt.validate();
|
|
||||||
if( !result )
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
for( auto const &arg : m_args ) {
|
|
||||||
auto result = arg.validate();
|
|
||||||
if( !result )
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return Result::ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
using ParserBase::parse;
|
|
||||||
|
|
||||||
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
|
||||||
|
|
||||||
struct ParserInfo {
|
|
||||||
ParserBase const* parser = nullptr;
|
|
||||||
size_t count = 0;
|
|
||||||
};
|
|
||||||
const size_t totalParsers = m_options.size() + m_args.size();
|
|
||||||
assert( totalParsers < 512 );
|
|
||||||
// ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
|
|
||||||
ParserInfo parseInfos[512];
|
|
||||||
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
|
|
||||||
for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_exeName.set( exeName );
|
|
||||||
|
|
||||||
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
|
||||||
while( result.value().remainingTokens() ) {
|
|
||||||
bool tokenParsed = false;
|
|
||||||
|
|
||||||
for( size_t i = 0; i < totalParsers; ++i ) {
|
|
||||||
auto& parseInfo = parseInfos[i];
|
|
||||||
if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
|
|
||||||
result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
|
|
||||||
if (!result)
|
|
||||||
return result;
|
|
||||||
if (result.value().type() != ParseResultType::NoMatch) {
|
|
||||||
tokenParsed = true;
|
|
||||||
++parseInfo.count;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( result.value().type() == ParseResultType::ShortCircuitAll )
|
|
||||||
return result;
|
|
||||||
if( !tokenParsed )
|
|
||||||
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
|
|
||||||
}
|
|
||||||
// !TBD Check missing required options
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename DerivedT>
|
|
||||||
template<typename T>
|
|
||||||
auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
|
|
||||||
return Parser() | static_cast<DerivedT const &>( *this ) | other;
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
|
|
||||||
// A Combined parser
|
|
||||||
using detail::Parser;
|
|
||||||
|
|
||||||
// A parser for options
|
|
||||||
using detail::Opt;
|
|
||||||
|
|
||||||
// A parser for arguments
|
|
||||||
using detail::Arg;
|
|
||||||
|
|
||||||
// Wrapper for argc, argv from main()
|
|
||||||
using detail::Args;
|
|
||||||
|
|
||||||
// Specifies the name of the executable
|
|
||||||
using detail::ExeName;
|
|
||||||
|
|
||||||
// Convenience wrapper for option parser that specifies the help option
|
|
||||||
using detail::Help;
|
|
||||||
|
|
||||||
// enum of result types from a parse
|
|
||||||
using detail::ParseResultType;
|
|
||||||
|
|
||||||
// Result type for parser operation
|
|
||||||
using detail::ParserResult;
|
|
||||||
|
|
||||||
|
|
||||||
}} // namespace Catch::clara
|
|
||||||
|
|
||||||
|
|
||||||
#endif // CATCH_CLARA_HPP_INCLUDED
|
|
@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
clara::Parser makeCommandLineParser( ConfigData& config ) {
|
Clara::Parser makeCommandLineParser( ConfigData& config ) {
|
||||||
|
|
||||||
using namespace clara;
|
using namespace Clara;
|
||||||
|
|
||||||
auto const setWarning = [&]( std::string const& warning ) {
|
auto const setWarning = [&]( std::string const& warning ) {
|
||||||
auto warningSet = [&]() {
|
auto warningSet = [&]() {
|
||||||
@ -68,12 +68,12 @@ namespace Catch {
|
|||||||
else if( startsWith( "random", order ) )
|
else if( startsWith( "random", order ) )
|
||||||
config.runOrder = RunTests::InRandomOrder;
|
config.runOrder = RunTests::InRandomOrder;
|
||||||
else
|
else
|
||||||
return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
|
return ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
};
|
};
|
||||||
auto const setRngSeed = [&]( std::string const& seed ) {
|
auto const setRngSeed = [&]( std::string const& seed ) {
|
||||||
if( seed != "time" )
|
if( seed != "time" )
|
||||||
return clara::detail::convertInto( seed, config.rngSeed );
|
return Clara::Detail::convertInto( seed, config.rngSeed );
|
||||||
config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
|
config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ namespace Catch {
|
|||||||
|
|
||||||
struct ConfigData;
|
struct ConfigData;
|
||||||
|
|
||||||
clara::Parser makeCommandLineParser( ConfigData& config );
|
Clara::Parser makeCommandLineParser( ConfigData& config );
|
||||||
|
|
||||||
} // end namespace Catch
|
} // end namespace Catch
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user