فهرست منبع

cmArgumentParser: Implement trailing positional args

Vito Gamberini 7 ماه پیش
والد
کامیت
df870e17f5
3فایلهای تغییر یافته به همراه139 افزوده شده و 12 حذف شده
  1. 19 9
      Source/cmArgumentParser.cxx
  2. 27 0
      Source/cmArgumentParser.h
  3. 93 3
      Tests/CMakeLib/testArgumentParser.cxx

+ 19 - 9
Source/cmArgumentParser.cxx

@@ -147,6 +147,24 @@ void Instance::Consume(std::size_t pos, cm::string_view arg)
     return;
     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) {
   if (this->KeywordValueFunc) {
     switch (this->KeywordValueFunc(arg)) {
     switch (this->KeywordValueFunc(arg)) {
       case Continue::Yes:
       case Continue::Yes:
@@ -159,14 +177,6 @@ void Instance::Consume(std::size_t pos, cm::string_view arg)
     return;
     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) {
   if (this->UnparsedArguments) {
     this->UnparsedArguments->emplace_back(arg);
     this->UnparsedArguments->emplace_back(arg);
   }
   }
@@ -174,7 +184,7 @@ void Instance::Consume(std::size_t pos, cm::string_view arg)
 
 
 void Instance::FinishKeyword()
 void Instance::FinishKeyword()
 {
 {
-  if (this->Keyword.empty()) {
+  if (!this->DoneWithPositional) {
     return;
     return;
   }
   }
   if (this->KeywordValuesSeen < this->KeywordValuesExpected) {
   if (this->KeywordValuesSeen < this->KeywordValuesExpected) {

+ 27 - 0
Source/cmArgumentParser.h

@@ -113,6 +113,7 @@ public:
   KeywordNameAction KeywordMissingValue;
   KeywordNameAction KeywordMissingValue;
   KeywordNameAction ParsedKeyword;
   KeywordNameAction ParsedKeyword;
   PositionActionMap Positions;
   PositionActionMap Positions;
+  KeywordAction TrailingArgs;
 };
 };
 
 
 class Base
 class Base
@@ -149,6 +150,12 @@ public:
     this->Bindings.KeywordMissingValue = std::move(action);
     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)
   void Bind(std::size_t pos, PositionAction action)
   {
   {
     bool const inserted =
     bool const inserted =
@@ -354,6 +361,18 @@ public:
     return *this;
     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>
   template <typename Range>
   bool Parse(Result& result, Range const& args,
   bool Parse(Result& result, Range const& args,
              std::vector<std::string>* unparsedArguments,
              std::vector<std::string>* unparsedArguments,
@@ -431,6 +450,14 @@ public:
     return *this;
     return *this;
   }
   }
 
 
+  template <typename T>
+  cmArgumentParser& BindTrailingArgs(T& ref)
+  {
+    this->Base::BindTrailingArgs(
+      [&ref](Instance& instance) { instance.Bind(ref); });
+    return *this;
+  }
+
   template <typename Range>
   template <typename Range>
   ParseResult Parse(Range const& args,
   ParseResult Parse(Range const& args,
                     std::vector<std::string>* unparsedArguments,
                     std::vector<std::string>* unparsedArguments,

+ 93 - 3
Tests/CMakeLib/testArgumentParser.cxx

@@ -44,6 +44,7 @@ struct Result : public ArgumentParser::ParseResult
   cm::optional<std::string> Pos0;
   cm::optional<std::string> Pos0;
   cm::optional<std::string> Pos1;
   cm::optional<std::string> Pos1;
   cm::optional<std::string> Pos2;
   cm::optional<std::string> Pos2;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> TrailingPos;
 
 
   bool Func0_ = false;
   bool Func0_ = false;
   ArgumentParser::Continue Func0(cm::string_view)
   ArgumentParser::Continue Func0(cm::string_view)
@@ -133,6 +134,31 @@ std::initializer_list<cm::string_view> const args = {
   /* clang-format on */
   /* 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,
 bool verifyResult(Result const& result,
                   std::vector<std::string> const& unparsedArguments)
                   std::vector<std::string> const& unparsedArguments)
 {
 {
@@ -216,6 +242,7 @@ bool verifyResult(Result const& result,
   ASSERT_TRUE(result.Pos0 == "pos0");
   ASSERT_TRUE(result.Pos0 == "pos0");
   ASSERT_TRUE(!result.Pos1);
   ASSERT_TRUE(!result.Pos1);
   ASSERT_TRUE(!result.Pos2);
   ASSERT_TRUE(!result.Pos2);
+  ASSERT_TRUE(result.TrailingPos.empty());
 
 
   ASSERT_TRUE(result.Func0_ == false);
   ASSERT_TRUE(result.Func0_ == false);
   ASSERT_TRUE(result.Func1_ == "foo");
   ASSERT_TRUE(result.Func1_ == "foo");
@@ -242,6 +269,26 @@ bool verifyResult(Result const& result,
   return true;
   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()
 bool testArgumentParserDynamic()
 {
 {
   Result result;
   Result result;
@@ -258,6 +305,7 @@ bool testArgumentParserDynamic()
       .Bind(0, result.Pos0)
       .Bind(0, result.Pos0)
       .Bind(1, result.Pos1)
       .Bind(1, result.Pos1)
       .Bind(2, result.Pos2)
       .Bind(2, result.Pos2)
+      .BindTrailingArgs(result.TrailingPos)
       .Bind("OPTION_1"_s, result.Option1)
       .Bind("OPTION_1"_s, result.Option1)
       .Bind("OPTION_2"_s, result.Option2)
       .Bind("OPTION_2"_s, result.Option2)
       .Bind("STRING_1"_s, result.String1)
       .Bind("STRING_1"_s, result.String1)
@@ -299,7 +347,23 @@ bool testArgumentParserDynamic()
       .BindParsedKeywords(result.ParsedKeywords)
       .BindParsedKeywords(result.ParsedKeywords)
       .Parse(args, &unparsedArguments);
       .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 =
 static auto const parserStaticFunc4 =
@@ -314,6 +378,7 @@ static auto const parserStaticFunc4 =
       .Bind(0, &resultType::Pos0)                                             \
       .Bind(0, &resultType::Pos0)                                             \
       .Bind(1, &resultType::Pos1)                                             \
       .Bind(1, &resultType::Pos1)                                             \
       .Bind(2, &resultType::Pos2)                                             \
       .Bind(2, &resultType::Pos2)                                             \
+      .BindTrailingArgs(&resultType::TrailingPos)                             \
       .Bind("OPTION_1"_s, &resultType::Option1)                               \
       .Bind("OPTION_1"_s, &resultType::Option1)                               \
       .Bind("OPTION_2"_s, &resultType::Option2)                               \
       .Bind("OPTION_2"_s, &resultType::Option2)                               \
       .Bind("STRING_1"_s, &resultType::String1)                               \
       .Bind("STRING_1"_s, &resultType::String1)                               \
@@ -346,18 +411,43 @@ static auto const parserStaticFunc4 =
 BIND_ALL(parserStatic, Result);
 BIND_ALL(parserStatic, Result);
 BIND_ALL(parserDerivedStatic, Derived);
 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()
 bool testArgumentParserStatic()
 {
 {
   std::vector<std::string> unparsedArguments;
   std::vector<std::string> unparsedArguments;
   Result const result = parserStatic.Parse(args, &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()
 bool testArgumentParserDerivedStatic()
 {
 {
   std::vector<std::string> unparsedArguments;
   std::vector<std::string> unparsedArguments;
   Derived const result = parserDerivedStatic.Parse(args, &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()
 bool testArgumentParserStaticBool()