Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Defaults #38

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
153 changes: 109 additions & 44 deletions include/clara.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,9 +458,25 @@ namespace detail {
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; }
virtual auto validateSettings() const -> Result { return Result::ok(); }
virtual auto validateFinal() const -> Result { return Result::ok(); }
virtual auto canParse() const -> bool { return false; }
virtual auto internalParse( std::string const& exeName, TokenStream const &tokens ) const->InternalParseResult = 0;

auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult {
auto validationResult = validateSettings();
if( !validationResult )
return InternalParseResult( validationResult );

auto result = internalParse( exeName, tokens );

// Call this even if parsing failed in order to perform cleanup
validationResult = validateFinal();
if( result && result.value().type() != ParseResultType::ShortCircuitAll && !validationResult )
return InternalParseResult( validationResult );

return result;
}

auto parse( Args const &args ) const -> InternalParseResult {
return parse( args.exeName(), TokenStream( args ) );
Expand All @@ -482,20 +498,31 @@ namespace detail {
std::shared_ptr<BoundRefBase> m_ref;
std::string m_hint;
std::string m_description;

explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {}
mutable std::size_t m_count;
std::string m_default;
bool m_hasDefault;

explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref )
: m_ref( ref ),
m_count( 0 ),
m_hasDefault( false )
{}

public:
template<typename T>
ParserRefImpl( T &ref, std::string const &hint )
: m_ref( std::make_shared<BoundRef<T>>( ref ) ),
m_hint( hint )
m_hint( hint ),
m_count( 0 ),
m_hasDefault( false )
{}

template<typename LambdaT>
ParserRefImpl( LambdaT const &ref, std::string const &hint )
: m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
m_hint(hint)
m_hint( hint ),
m_count( 0 ),
m_hasDefault( false )
{}

auto operator()( std::string const &description ) -> DerivedT & {
Expand All @@ -508,6 +535,13 @@ namespace detail {
return static_cast<DerivedT &>( *this );
};

auto optional( std::string def ) -> DerivedT & {
m_optionality = Optionality::Optional;
m_hasDefault = true;
m_default = std::move( def );
return static_cast<DerivedT &>( *this );
};

auto required() -> DerivedT & {
m_optionality = Optionality::Required;
return static_cast<DerivedT &>( *this );
Expand All @@ -517,14 +551,42 @@ namespace detail {
return m_optionality == Optionality::Optional;
}

auto cardinality() const -> size_t override {
virtual auto cardinality() const -> size_t {
if( m_ref->isContainer() )
return 0;
else
return 1;
}

auto validateSettings() const -> Result override {
m_count = 0;
return ComposableParserImpl<DerivedT>::validateSettings();
}

auto validateFinal() const -> Result override {
if( !isOptional() && count() < 1 )
return Result::runtimeError( "Missing token: " + hint() );
if( count() == 0 && m_hasDefault )
m_ref->setValue( m_default );
return ComposableParserImpl<DerivedT>::validateFinal();
}

auto canParse() const -> bool override {
return (cardinality() == 0 || count() < cardinality());
}

auto hint() const -> std::string { return m_hint; }

auto description() const -> std::string {
std::string desc = m_description;
if( m_hasDefault ) {
desc += " Default: ";
desc += m_default;
}
return desc;
}

auto count() const -> std::size_t { return m_count; }
};

class ExeName : public ComposableParserImpl<ExeName> {
Expand All @@ -549,7 +611,7 @@ namespace detail {
}

// The exe name is not parsed out of the normal tokens, but is handled specially
auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
auto internalParse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
}

Expand All @@ -573,11 +635,7 @@ namespace detail {
public:
using ParserRefImpl::ParserRefImpl;

auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
auto validationResult = validate();
if( !validationResult )
return InternalParseResult( validationResult );

auto internalParse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
auto remainingTokens = tokens;
auto const &token = *remainingTokens;
if( token.type != TokenType::Argument )
Expand All @@ -586,8 +644,10 @@ namespace detail {
auto result = m_ref->setValue( remainingTokens->token );
if( !result )
return InternalParseResult( result );
else
else {
++m_count;
return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
}
}
};

Expand Down Expand Up @@ -631,9 +691,9 @@ namespace detail {
oss << ", ";
oss << opt;
}
if( !m_hint.empty() )
oss << " <" << m_hint << ">";
return { { oss.str(), m_description } };
if( !hint().empty() )
oss << " <" << hint() << ">";
return { { oss.str(), description() } };
}

auto isMatch( std::string const &optToken ) const -> bool {
Expand All @@ -647,22 +707,19 @@ namespace detail {

using ParserBase::parse;

auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
auto validationResult = validate();
if( !validationResult )
return InternalParseResult( validationResult );

auto internalParse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
auto remainingTokens = tokens;
if( remainingTokens && remainingTokens->type == TokenType::Option ) {
auto const &token = *remainingTokens;
if( isMatch(token.token ) ) {
if( isMatch( token.token ) ) {
if( m_ref->isFlag() ) {
auto result = m_ref->setFlag( true );
if( !result )
return InternalParseResult( result );
if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
} else {
}
else {
++remainingTokens;
if( !remainingTokens )
return InternalParseResult::runtimeError( "Expected argument following " + token.token );
Expand All @@ -675,13 +732,14 @@ namespace detail {
if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
}
++m_count;
return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
}
}
return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
}

auto validate() const -> Result override {
auto validateSettings() const -> Result override {
if( m_optNames.empty() )
return Result::logicError( "No options supplied to Opt" );
for( auto const &name : m_optNames ) {
Expand All @@ -695,7 +753,7 @@ namespace detail {
return Result::logicError( "Option name must begin with '-'" );
#endif
}
return ParserRefImpl::validate();
return ParserRefImpl::validateSettings();
}
};

Expand Down Expand Up @@ -801,37 +859,46 @@ namespace detail {
return os;
}

auto validate() const -> Result override {
auto validateSettings() const -> Result override {
for( auto const &opt : m_options ) {
auto result = opt.validate();
auto result = opt.validateSettings();
if( !result )
return result;
}
for( auto const &arg : m_args ) {
auto result = arg.validate();
auto result = arg.validateSettings();
if( !result )
return result;
}
return Result::ok();
}

using ParserBase::parse;
auto validateFinal() const -> Result override {
for( auto const &opt : m_options ) {
auto result = opt.validateFinal();
if( !result )
return result;
}
for( auto const &arg : m_args ) {
auto result = arg.validateFinal();
if( !result )
return result;
}
return Result::ok();
}

auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
using ParserBase::parse;

struct ParserInfo {
ParserBase const* parser = nullptr;
size_t count = 0;
};
auto internalParse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
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];
ParserBase const* parsers[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;
for (auto const &opt : m_options) parsers[i++] = &opt;
for (auto const &arg : m_args) parsers[i++] = &arg;
}

m_exeName.set( exeName );
Expand All @@ -841,14 +908,13 @@ namespace detail {
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());
auto& parser = parsers[i];
if( parser->canParse() ) {
result = parser->internalParse(exeName, result.value().remainingTokens());
if (!result)
return result;
if (result.value().type() != ParseResultType::NoMatch) {
tokenParsed = true;
++parseInfo.count;
break;
}
}
Expand All @@ -859,7 +925,6 @@ namespace detail {
if( !tokenParsed )
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
}
// !TBD Check missing required options
return result;
}
};
Expand Down
Loading