10#ifndef CLARA_HPP_INCLUDED
11#define CLARA_HPP_INCLUDED
13#ifndef CLARA_CONFIG_CONSOLE_WIDTH
14#define CLARA_CONFIG_CONSOLE_WIDTH 80
17#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
18#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
21#ifndef CLARA_CONFIG_OPTIONAL_TYPE
23#if __has_include(<optional>) && __cplusplus >= 201703L
25#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
42#ifndef CLARA_TEXTFLOW_HPP_INCLUDED
43#define CLARA_TEXTFLOW_HPP_INCLUDED
50#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
51#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
55namespace clara {
namespace TextFlow {
58 static std::string chars =
" \t\n\r";
59 return chars.find( c ) != std::string::npos;
62 static std::string chars =
"[({<|";
63 return chars.find( c ) != std::string::npos;
66 static std::string chars =
"])}>.,:;*+-=&/\\";
67 return chars.find( c ) != std::string::npos;
73 std::vector<std::string> m_strings;
76 size_t m_initialIndent = std::string::npos;
83 size_t m_stringIndex = 0;
88 bool m_suffix =
false;
92 m_stringIndex( stringIndex )
95 auto line()
const -> std::string
const& {
return m_column.m_strings[m_stringIndex]; }
97 auto isBoundary(
size_t at )
const ->
bool {
99 assert( at <= line().size() );
101 return at == line().size() ||
108 assert( m_stringIndex < m_column.m_strings.size() );
111 auto width = m_column.m_width-indent();
113 if (line()[m_pos] ==
'\n') {
116 while( m_end < line().size() && line()[m_end] !=
'\n' )
119 if( m_end < m_pos +
width ) {
120 m_len = m_end - m_pos;
124 while (
len > 0 && !isBoundary(m_pos +
len))
138 auto indent()
const ->
size_t {
139 auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
140 return initial == std::string::npos ? m_column.m_indent : initial;
143 auto addIndentAndSuffix(std::string
const &plain)
const -> std::string {
144 return std::string( indent(),
' ' ) + (m_suffix ? plain +
"-" : plain);
155 assert( m_column.m_width > m_column.m_indent );
156 assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent );
163 assert( m_stringIndex < m_column.m_strings.size() );
164 assert( m_pos <= m_end );
165 return addIndentAndSuffix(line().substr(m_pos, m_len));
170 if( m_pos < line().size() && line()[m_pos] ==
'\n' )
173 while( m_pos < line().size() &&
isWhitespace( line()[m_pos] ) )
176 if( m_pos == line().size() ) {
180 if( m_stringIndex < m_column.m_strings.size() )
192 m_pos == other.m_pos &&
193 m_stringIndex == other.m_stringIndex &&
194 &m_column == &other.m_column;
202 explicit Column( std::string
const& text ) { m_strings.push_back( text ); }
205 assert( newWidth > 0 );
210 m_indent = newIndent;
214 m_initialIndent = newIndent;
218 auto width() const ->
size_t {
return m_width; }
220 auto end() const ->
iterator {
return { *
this, m_strings.size() }; }
224 for(
auto line : col ) {
237 std::ostringstream oss;
252 std::vector<Column> m_columns;
260 std::vector<Column>
const& m_columns;
261 std::vector<Column::iterator> m_iterators;
262 size_t m_activeIterators;
265 : m_columns( columns.m_columns ),
266 m_activeIterators( 0 )
268 m_iterators.reserve( m_columns.size() );
270 for(
auto const& col : m_columns )
271 m_iterators.push_back( col.end() );
282 : m_columns( columns.m_columns ),
283 m_activeIterators( m_columns.size() )
285 m_iterators.reserve( m_columns.size() );
287 for(
auto const& col : m_columns )
288 m_iterators.push_back( col.begin() );
292 return m_iterators == other.m_iterators;
295 return m_iterators != other.m_iterators;
298 std::string row, padding;
300 for(
size_t i = 0; i < m_columns.size(); ++i ) {
301 auto width = m_columns[i].width();
302 if( m_iterators[i] != m_columns[i].
end() ) {
303 std::string col = *m_iterators[i];
304 row += padding + col;
305 if( col.size() <
width )
306 padding = std::string(
width - col.size(),
' ' );
311 padding += std::string(
width,
' ' );
317 for(
size_t i = 0; i < m_columns.size(); ++i ) {
318 if (m_iterators[i] != m_columns[i].
end())
332 auto end() const ->
iterator {
return { *
this, iterator::EndTag() }; }
335 m_columns.push_back( col );
347 for(
auto line : cols ) {
358 std::ostringstream oss;
382#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
383#define CLARA_PLATFORM_WINDOWS
393 template<
typename ClassT,
typename ReturnT,
typename...
Args>
395 static const bool isValid =
false;
398 template<
typename ClassT,
typename ReturnT,
typename ArgT>
400 static const bool isValid =
true;
401 using ArgType =
typename std::remove_const<typename std::remove_reference<ArgT>::type>
::type;
410 std::string m_exeName;
411 std::vector<std::string> m_args;
415 : m_exeName(
argv[0]),
418 Args( std::initializer_list<std::string> args )
419 : m_exeName( *args.begin() ),
420 m_args( args.begin()+1, args.end() )
440#ifdef CLARA_PLATFORM_WINDOWS
448 using Iterator = std::vector<std::string>::const_iterator;
451 std::vector<Token> m_tokenBuffer;
454 m_tokenBuffer.resize( 0 );
457 while( it != itEnd && it->empty() )
461 auto const &next = *it;
463 auto delimiterPos = next.find_first_of(
" :=" );
464 if( delimiterPos != std::string::npos ) {
465 m_tokenBuffer.push_back( {
TokenType::Option, next.substr( 0, delimiterPos ) } );
468 if( next[1] !=
'-' && next.size() > 2 ) {
469 std::string opt =
"- ";
470 for(
size_t i = 1; i < next.size(); ++i ) {
487 TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
491 explicit operator bool()
const {
492 return !m_tokenBuffer.empty() || it != itEnd;
495 auto count() const ->
size_t {
return m_tokenBuffer.size() + (itEnd - it); }
498 assert( !m_tokenBuffer.empty() );
499 return m_tokenBuffer.front();
503 assert( !m_tokenBuffer.empty() );
504 return &m_tokenBuffer.front();
508 if( m_tokenBuffer.size() >= 2 ) {
509 m_tokenBuffer.erase( m_tokenBuffer.begin() );
558 ResultBase::operator=(other);
580 template<
typename T =
void>
656 std::stringstream ss;
669 std::string srcLC =
source;
670 std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), [](
char c ) { return static_cast<char>( ::tolower(c) ); } );
671 if (srcLC ==
"y" || srcLC ==
"1" || srcLC ==
"true" || srcLC ==
"yes" || srcLC ==
"on")
673 else if (srcLC ==
"n" || srcLC ==
"0" || srcLC ==
"false" || srcLC ==
"no" || srcLC ==
"off")
679#ifdef CLARA_CONFIG_OPTIONAL_TYPE
685 target = std::move(temp);
701 virtual auto isFlag() const ->
bool {
return false; }
708 virtual auto isFlag() const ->
bool {
return true; }
734 m_ref.push_back( temp );
750 template<
typename ReturnType>
752 static_assert( std::is_same<ReturnType, ParserResult>::value,
"Lambda must return void or clara::ParserResult" );
754 template<
typename L,
typename ArgType>
756 return lambda( arg );
762 template<
typename L,
typename ArgType>
769 template<
typename ArgType,
typename L>
787 return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>(
m_lambda, arg );
796 static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType,
bool>
::value,
"flags must be boolean" );
821 template<
typename DerivedT>
832 template<
typename DerivedT>
849 template<
typename LambdaT>
855 auto operator()( std::string
const &description ) -> DerivedT & {
857 return static_cast<DerivedT &
>( *this );
862 return static_cast<DerivedT &
>( *this );
867 return static_cast<DerivedT &
>( *this );
875 if(
m_ref->isContainer() )
885 std::shared_ptr<std::string> m_name;
886 std::shared_ptr<BoundValueRefBase> m_ref;
888 template<
typename LambdaT>
889 static auto makeRef(LambdaT
const &lambda) -> std::shared_ptr<BoundValueRefBase> {
890 return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
897 m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
900 template<
typename LambdaT>
902 m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
910 auto name() const ->
std::
string {
return *m_name; }
913 auto lastSlash = newName.find_last_of(
"\\/" );
914 auto filename = ( lastSlash == std::string::npos )
916 : newName.substr( lastSlash+1 );
920 return m_ref->setValue( filename );
932 if( !validationResult )
935 auto remainingTokens = tokens;
936 auto const &token = *remainingTokens;
940 assert( !
m_ref->isFlag() );
943 auto result = valueRef->setValue( remainingTokens->token );
951 inline auto normaliseOpt( std::string
const &optName ) -> std::string {
952#ifdef CLARA_PLATFORM_WINDOWS
953 if( optName[0] ==
'/' )
954 return "-" + optName.substr( 1 );
965 template<
typename LambdaT>
970 template<
typename LambdaT>
982 std::ostringstream oss;
992 oss <<
" <" <<
m_hint <<
">";
996 auto isMatch( std::string
const &optToken )
const ->
bool {
1008 auto validationResult =
validate();
1009 if( !validationResult )
1012 auto remainingTokens = tokens;
1014 auto const &token = *remainingTokens;
1016 if(
m_ref->isFlag() ) {
1018 auto result = flagRef->setFlag(
true );
1026 if( !remainingTokens )
1028 auto const &argToken = *remainingTokens;
1031 auto result = valueRef->setValue( argToken.token );
1049#ifdef CLARA_PLATFORM_WINDOWS
1050 if(
name[0] !=
'-' &&
name[0] !=
'/' )
1053 if(
name[0] !=
'-' )
1063 :
Opt([&]( bool flag ) {
1064 showHelpFlag = flag;
1068 static_cast<Opt &
>( *this )
1069 (
"display usage information")
1070 [
"-?"][
"-h"][
"--help"]
1099 m_args.insert(
m_args.end(), other.m_args.begin(), other.m_args.end());
1103 template<
typename T>
1105 return Parser( *
this ) |= other;
1109 template<
typename T>
1111 template<
typename T>
1115 std::vector<HelpColumns> cols;
1117 auto childCols = o.getHelpColumns();
1118 cols.insert( cols.end(), childCols.begin(), childCols.end() );
1126 bool required =
true, first =
true;
1127 for(
auto const &arg :
m_args ) {
1132 if( arg.isOptional() && required ) {
1136 os <<
"<" << arg.hint() <<
">";
1137 if( arg.cardinality() == 0 )
1144 os <<
"\n\nwhere options are:" << std::endl;
1149 size_t optWidth = 0;
1150 for(
auto const &cols : rows )
1151 optWidth = (std::max)(optWidth, cols.left.size() + 2);
1153 optWidth = (std::min)(optWidth, consoleWidth/2);
1155 for(
auto const &cols : rows ) {
1160 os << row << std::endl;
1165 parser.writeToStream(
os );
1171 auto result = opt.validate();
1175 for(
auto const &arg :
m_args ) {
1176 auto result = arg.validate();
1192 assert( totalParsers < 512 );
1194 ParserInfo parseInfos[512];
1198 for (
auto const &opt :
m_options) parseInfos[i++].parser = &opt;
1199 for (
auto const &arg :
m_args) parseInfos[i++].parser = &arg;
1205 while( result.value().remainingTokens() ) {
1206 bool tokenParsed =
false;
1208 for(
size_t i = 0; i < totalParsers; ++i ) {
1209 auto& parseInfo = parseInfos[i];
1210 if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
1211 result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
1232 template<
typename DerivedT>
1233 template<
typename T>
1235 return Parser() |
static_cast<DerivedT
const &
>( *this ) | other;
auto operator*() const -> std::string
std::forward_iterator_tag iterator_category
auto operator++() -> iterator &
auto operator==(iterator const &other) const -> bool
iterator(Column const &column)
auto operator!=(iterator const &other) const -> bool
std::ptrdiff_t difference_type
Column(std::string const &text)
auto width(size_t newWidth) -> Column &
auto operator+(Column const &other) -> Columns
auto indent(size_t newIndent) -> Column &
auto toString() const -> std::string
auto initialIndent(size_t newIndent) -> Column &
auto begin() const -> iterator
friend std::ostream & operator<<(std::ostream &os, Column const &col)
auto end() const -> iterator
auto width() const -> size_t
std::forward_iterator_tag iterator_category
std::ptrdiff_t difference_type
auto operator++() -> iterator &
iterator(Columns const &columns)
auto operator*() const -> std::string
auto operator==(iterator const &other) const -> bool
auto operator!=(iterator const &other) const -> bool
auto operator+=(Column const &col) -> Columns &
auto toString() const -> std::string
auto end() const -> iterator
friend std::ostream & operator<<(std::ostream &os, Columns const &cols)
auto operator+(Column const &col) -> Columns
auto begin() const -> iterator
Spacer(size_t spaceWidth)
auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override
auto exeName() const -> std::string
Args(std::initializer_list< std::string > args)
Args(int argc, char const *const *argv)
BasicResult(BasicResult< U > const &other)
BasicResult(ResultBase::Type type, std::string const &message)
std::string m_errorMessage
auto type() const -> ResultBase::Type
static auto ok(U const &value) -> BasicResult
static auto runtimeError(std::string const &message) -> BasicResult
void enforceOk() const override
static auto ok() -> BasicResult
static auto logicError(std::string const &message) -> BasicResult
auto errorMessage() const -> std::string
auto operator+(T const &other) const -> Parser
auto operator|(T const &other) const -> Parser
auto set(std::string const &newName) -> ParserResult
ExeName(std::string &ref)
ExeName(LambdaT const &lambda)
auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override
auto name() const -> std::string
std::vector< std::string > m_optNames
auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override
Opt(LambdaT const &ref, std::string const &hint)
auto validate() const -> Result override
Opt(T &ref, std::string const &hint)
auto operator[](std::string const &optName) -> Opt &
auto isMatch(std::string const &optToken) const -> bool
auto getHelpColumns() const -> std::vector< HelpColumns >
auto type() const -> ParseResultType
ParseState(ParseResultType type, TokenStream const &remainingTokens)
auto remainingTokens() const -> TokenStream
auto parse(Args const &args) const -> InternalParseResult
virtual ~ParserBase()=default
virtual auto parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult=0
virtual auto validate() const -> Result
virtual auto cardinality() const -> size_t
std::shared_ptr< BoundRef > m_ref
std::string m_description
auto operator()(std::string const &description) -> DerivedT &
ParserRefImpl(T &ref, std::string const &hint)
auto optional() -> DerivedT &
ParserRefImpl(std::shared_ptr< BoundRef > const &ref)
auto required() -> DerivedT &
auto cardinality() const -> size_t override
ParserRefImpl(LambdaT const &ref, std::string const &hint)
Optionality m_optionality
auto hint() const -> std::string
auto isOptional() const -> bool
virtual ~ResultBase()=default
virtual void enforceOk() const =0
auto value() const -> T const &
ResultValueBase(Type type)
ResultValueBase(Type, T const &value)
ResultValueBase(ResultValueBase const &other)
auto operator=(ResultValueBase const &other) -> ResultValueBase &
~ResultValueBase() override
auto operator++() -> TokenStream &
TokenStream(Iterator it, Iterator itEnd)
TokenStream(Args const &args)
auto operator*() const -> Token
auto count() const -> size_t
auto operator->() const -> Token const *
auto isBreakableBefore(char c) -> bool
auto isBreakableAfter(char c) -> bool
auto isWhitespace(char c) -> bool
auto isOptPrefix(char c) -> bool
auto normaliseOpt(std::string const &optName) -> std::string
auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult
auto convertInto(std::string const &source, T &target) -> ParserResult
BasicResult< ParseState > InternalParseResult
const CharType(& source)[N]
#define T(meth, val, expected)
auto setFlag(bool flag) -> ParserResult override
BoundFlagLambda(L const &lambda)
virtual auto isFlag() const -> bool
virtual auto setFlag(bool flag) -> ParserResult=0
auto setFlag(bool flag) -> ParserResult override
BoundLambda(L const &lambda)
auto setValue(std::string const &arg) -> ParserResult override
virtual ~BoundRef()=default
virtual auto isFlag() const -> bool
virtual auto isContainer() const -> bool
BoundValueRef(std::vector< T > &ref)
auto setValue(std::string const &arg) -> ParserResult override
auto isContainer() const -> bool override
virtual auto setValue(std::string const &arg) -> ParserResult=0
auto setValue(std::string const &arg) -> ParserResult override
static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult
static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult
NonCopyable(NonCopyable const &)=delete
NonCopyable & operator=(NonCopyable &&)=delete
NonCopyable(NonCopyable &&)=delete
NonCopyable & operator=(NonCopyable const &)=delete
auto operator|(T const &other) const -> Parser
auto operator|=(ExeName const &exeName) -> Parser &
friend auto operator<<(std::ostream &os, Parser const &parser) -> std::ostream &
auto operator|=(Arg const &arg) -> Parser &
auto operator+(T const &other) const -> Parser
std::vector< Arg > m_args
auto operator|=(Opt const &opt) -> Parser &
auto getHelpColumns() const -> std::vector< HelpColumns >
auto validate() const -> Result override
auto operator+=(T const &other) -> Parser &
std::vector< Opt > m_options
void writeToStream(std::ostream &os) const
auto operator|=(Parser const &other) -> Parser &
auto parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult override
typename std::remove_const< typename std::remove_reference< ArgT >::type >::type ArgType
#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
#define CLARA_CONFIG_CONSOLE_WIDTH