mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-02 03:39:43 -06:00
cmArgumentParser: Implement trailing positional args
This commit is contained in:
@@ -147,6 +147,24 @@ void Instance::Consume(std::size_t pos, cm::string_view arg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->DoneWithPositional) {
|
||||
auto const pit = this->Bindings.Positions.Find(pos);
|
||||
if (pit != this->Bindings.Positions.end()) {
|
||||
pit->second(*this, pos, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->Bindings.TrailingArgs) {
|
||||
this->Keyword = ""_s;
|
||||
this->KeywordValuesSeen = 0;
|
||||
this->DoneWithPositional = true;
|
||||
this->Bindings.TrailingArgs(*this);
|
||||
if (!this->KeywordValueFunc) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->KeywordValueFunc) {
|
||||
switch (this->KeywordValueFunc(arg)) {
|
||||
case Continue::Yes:
|
||||
@@ -159,14 +177,6 @@ void Instance::Consume(std::size_t pos, cm::string_view arg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->DoneWithPositional) {
|
||||
auto const pit = this->Bindings.Positions.Find(pos);
|
||||
if (pit != this->Bindings.Positions.end()) {
|
||||
pit->second(*this, pos, arg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->UnparsedArguments) {
|
||||
this->UnparsedArguments->emplace_back(arg);
|
||||
}
|
||||
@@ -174,7 +184,7 @@ void Instance::Consume(std::size_t pos, cm::string_view arg)
|
||||
|
||||
void Instance::FinishKeyword()
|
||||
{
|
||||
if (this->Keyword.empty()) {
|
||||
if (!this->DoneWithPositional) {
|
||||
return;
|
||||
}
|
||||
if (this->KeywordValuesSeen < this->KeywordValuesExpected) {
|
||||
|
||||
@@ -113,6 +113,7 @@ public:
|
||||
KeywordNameAction KeywordMissingValue;
|
||||
KeywordNameAction ParsedKeyword;
|
||||
PositionActionMap Positions;
|
||||
KeywordAction TrailingArgs;
|
||||
};
|
||||
|
||||
class Base
|
||||
@@ -149,6 +150,12 @@ public:
|
||||
this->Bindings.KeywordMissingValue = std::move(action);
|
||||
}
|
||||
|
||||
void BindTrailingArgs(KeywordAction action)
|
||||
{
|
||||
assert(!this->Bindings.TrailingArgs);
|
||||
this->Bindings.TrailingArgs = std::move(action);
|
||||
}
|
||||
|
||||
void Bind(std::size_t pos, PositionAction action)
|
||||
{
|
||||
bool const inserted =
|
||||
@@ -354,6 +361,18 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename cT = cm::member_pointer_class_t<T>,
|
||||
typename mT = cm::remove_member_pointer_t<T>,
|
||||
typename = cm::enable_if_t<std::is_base_of<cT, Result>::value>,
|
||||
typename = cm::enable_if_t<!std::is_function<mT>::value>>
|
||||
cmArgumentParser& BindTrailingArgs(T member)
|
||||
{
|
||||
this->Base::BindTrailingArgs([member](Instance& instance) {
|
||||
instance.Bind(static_cast<Result*>(instance.Result)->*member);
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
bool Parse(Result& result, Range const& args,
|
||||
std::vector<std::string>* unparsedArguments,
|
||||
@@ -431,6 +450,14 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
cmArgumentParser& BindTrailingArgs(T& ref)
|
||||
{
|
||||
this->Base::BindTrailingArgs(
|
||||
[&ref](Instance& instance) { instance.Bind(ref); });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
ParseResult Parse(Range const& args,
|
||||
std::vector<std::string>* unparsedArguments,
|
||||
|
||||
@@ -44,6 +44,7 @@ struct Result : public ArgumentParser::ParseResult
|
||||
cm::optional<std::string> Pos0;
|
||||
cm::optional<std::string> Pos1;
|
||||
cm::optional<std::string> Pos2;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> TrailingPos;
|
||||
|
||||
bool Func0_ = false;
|
||||
ArgumentParser::Continue Func0(cm::string_view)
|
||||
@@ -133,6 +134,31 @@ std::initializer_list<cm::string_view> const args = {
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
struct ResultTrailingPos : public ArgumentParser::ParseResult
|
||||
{
|
||||
bool Option1 = false;
|
||||
ArgumentParser::NonEmpty<std::vector<std::string>> List1;
|
||||
|
||||
cm::optional<std::string> Pos0;
|
||||
cm::optional<std::string> Pos1;
|
||||
ArgumentParser::NonEmpty<std::vector<std::string>> TrailingPos;
|
||||
};
|
||||
|
||||
struct DerivedTrailingPos : ResultTrailingPos
|
||||
{
|
||||
};
|
||||
|
||||
std::initializer_list<cm::string_view> const args_trailingpos = {
|
||||
/* clang-format off */
|
||||
"pos0", // position index 0
|
||||
"pos1", // position index 1
|
||||
"pos_trailing0", // trailing positional 0
|
||||
"pos_trailing1", // trailing positional 1
|
||||
"OPTION_1", // option
|
||||
"LIST_1", "foo", "bar", // list arg with 2 elems
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
bool verifyResult(Result const& result,
|
||||
std::vector<std::string> const& unparsedArguments)
|
||||
{
|
||||
@@ -216,6 +242,7 @@ bool verifyResult(Result const& result,
|
||||
ASSERT_TRUE(result.Pos0 == "pos0");
|
||||
ASSERT_TRUE(!result.Pos1);
|
||||
ASSERT_TRUE(!result.Pos2);
|
||||
ASSERT_TRUE(result.TrailingPos.empty());
|
||||
|
||||
ASSERT_TRUE(result.Func0_ == false);
|
||||
ASSERT_TRUE(result.Func1_ == "foo");
|
||||
@@ -242,6 +269,26 @@ bool verifyResult(Result const& result,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verifyResult(ResultTrailingPos const& result,
|
||||
std::vector<std::string> const& unparsedArguments)
|
||||
{
|
||||
static std::vector<std::string> const foobar = { "foo", "bar" };
|
||||
static std::vector<std::string> const trailing = { "pos_trailing0",
|
||||
"pos_trailing1" };
|
||||
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_TRUE(unparsedArguments.empty());
|
||||
|
||||
ASSERT_TRUE(result.Option1);
|
||||
ASSERT_TRUE(result.List1 == foobar);
|
||||
|
||||
ASSERT_TRUE(result.Pos0 == "pos0");
|
||||
ASSERT_TRUE(result.Pos1 == "pos1");
|
||||
ASSERT_TRUE(result.TrailingPos == trailing);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool testArgumentParserDynamic()
|
||||
{
|
||||
Result result;
|
||||
@@ -258,6 +305,7 @@ bool testArgumentParserDynamic()
|
||||
.Bind(0, result.Pos0)
|
||||
.Bind(1, result.Pos1)
|
||||
.Bind(2, result.Pos2)
|
||||
.BindTrailingArgs(result.TrailingPos)
|
||||
.Bind("OPTION_1"_s, result.Option1)
|
||||
.Bind("OPTION_2"_s, result.Option2)
|
||||
.Bind("STRING_1"_s, result.String1)
|
||||
@@ -299,7 +347,23 @@ bool testArgumentParserDynamic()
|
||||
.BindParsedKeywords(result.ParsedKeywords)
|
||||
.Parse(args, &unparsedArguments);
|
||||
|
||||
return verifyResult(result, unparsedArguments);
|
||||
if (!verifyResult(result, unparsedArguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unparsedArguments.clear();
|
||||
|
||||
ResultTrailingPos result_trailing;
|
||||
static_cast<ArgumentParser::ParseResult&>(result_trailing) =
|
||||
cmArgumentParser<void>{}
|
||||
.Bind(0, result_trailing.Pos0)
|
||||
.Bind(1, result_trailing.Pos1)
|
||||
.BindTrailingArgs(result_trailing.TrailingPos)
|
||||
.Bind("OPTION_1"_s, result_trailing.Option1)
|
||||
.Bind("LIST_1"_s, result_trailing.List1)
|
||||
.Parse(args_trailingpos, &unparsedArguments);
|
||||
|
||||
return verifyResult(result_trailing, unparsedArguments);
|
||||
}
|
||||
|
||||
static auto const parserStaticFunc4 =
|
||||
@@ -314,6 +378,7 @@ static auto const parserStaticFunc4 =
|
||||
.Bind(0, &resultType::Pos0) \
|
||||
.Bind(1, &resultType::Pos1) \
|
||||
.Bind(2, &resultType::Pos2) \
|
||||
.BindTrailingArgs(&resultType::TrailingPos) \
|
||||
.Bind("OPTION_1"_s, &resultType::Option1) \
|
||||
.Bind("OPTION_2"_s, &resultType::Option2) \
|
||||
.Bind("STRING_1"_s, &resultType::String1) \
|
||||
@@ -346,18 +411,43 @@ static auto const parserStaticFunc4 =
|
||||
BIND_ALL(parserStatic, Result);
|
||||
BIND_ALL(parserDerivedStatic, Derived);
|
||||
|
||||
#define BIND_TRAILING(name, resultType) \
|
||||
static auto const name = cmArgumentParser<resultType>{} \
|
||||
.Bind(0, &resultType::Pos0) \
|
||||
.Bind(1, &resultType::Pos1) \
|
||||
.BindTrailingArgs(&resultType::TrailingPos) \
|
||||
.Bind("OPTION_1"_s, &resultType::Option1) \
|
||||
.Bind("LIST_1"_s, &resultType::List1)
|
||||
|
||||
BIND_TRAILING(parserTrailingStatic, ResultTrailingPos);
|
||||
BIND_TRAILING(parserTrailingDerivedStatic, DerivedTrailingPos);
|
||||
|
||||
bool testArgumentParserStatic()
|
||||
{
|
||||
std::vector<std::string> unparsedArguments;
|
||||
Result const result = parserStatic.Parse(args, &unparsedArguments);
|
||||
return verifyResult(result, unparsedArguments);
|
||||
if (!verifyResult(result, unparsedArguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unparsedArguments.clear();
|
||||
ResultTrailingPos const result_trailing =
|
||||
parserTrailingStatic.Parse(args_trailingpos, &unparsedArguments);
|
||||
return verifyResult(result_trailing, unparsedArguments);
|
||||
}
|
||||
|
||||
bool testArgumentParserDerivedStatic()
|
||||
{
|
||||
std::vector<std::string> unparsedArguments;
|
||||
Derived const result = parserDerivedStatic.Parse(args, &unparsedArguments);
|
||||
return verifyResult(result, unparsedArguments);
|
||||
if (!verifyResult(result, unparsedArguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unparsedArguments.clear();
|
||||
ResultTrailingPos const result_trailing =
|
||||
parserTrailingDerivedStatic.Parse(args_trailingpos, &unparsedArguments);
|
||||
return verifyResult(result_trailing, unparsedArguments);
|
||||
}
|
||||
|
||||
bool testArgumentParserStaticBool()
|
||||
|
||||
Reference in New Issue
Block a user