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 {
@ -549,25 +549,19 @@ namespace detail {
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:
@ -587,7 +581,8 @@ namespace detail {
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 );
} }
@ -604,10 +599,10 @@ namespace detail {
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:
@ -652,23 +647,16 @@ namespace detail {
struct BoundRefBase { struct BoundRefBase {
BoundRefBase() = default; BoundRefBase() = default;
BoundRefBase( BoundRefBase const & ) = delete; BoundRefBase( BoundRefBase const & ) = delete;
BoundRefBase( BoundRefBase && ) = delete; BoundRefBase( BoundRefBase && ) = delete;
BoundRefBase &operator=( BoundRefBase const & ) = delete; BoundRefBase &operator=( BoundRefBase const & ) = delete;
BoundRefBase &operator=( BoundRefBase && ) = 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;
}; };
@ -786,9 +774,7 @@ namespace detail {
} }
}; };
enum class Optionality { enum class Optionality { Optional, Required };
Optional, Required
};
struct Parser; struct Parser;
@ -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
@ -824,11 +810,16 @@ namespace detail {
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;
@ -967,7 +958,7 @@ namespace detail {
} }
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;
@ -1049,30 +1040,30 @@ 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> {
@ -1145,46 +1136,40 @@ 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;

120
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
@ -546,25 +549,19 @@ namespace detail {
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:
@ -584,7 +581,8 @@ namespace detail {
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 );
} }
@ -601,10 +599,10 @@ namespace detail {
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:
@ -649,23 +647,16 @@ namespace detail {
struct BoundRefBase { struct BoundRefBase {
BoundRefBase() = default; BoundRefBase() = default;
BoundRefBase( BoundRefBase const & ) = delete; BoundRefBase( BoundRefBase const & ) = delete;
BoundRefBase( BoundRefBase && ) = delete; BoundRefBase( BoundRefBase && ) = delete;
BoundRefBase &operator=( BoundRefBase const & ) = delete; BoundRefBase &operator=( BoundRefBase const & ) = delete;
BoundRefBase &operator=( BoundRefBase && ) = 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;
}; };
@ -783,9 +774,7 @@ namespace detail {
} }
}; };
enum class Optionality { enum class Optionality { Optional, Required };
Optional, Required
};
struct Parser; struct Parser;
@ -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
@ -821,11 +810,16 @@ namespace detail {
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;
@ -1046,30 +1040,30 @@ 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> {
@ -1142,46 +1136,40 @@ 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