summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorRegina Pfeifer <regina@mailbox.org>2019-03-23 21:45:41 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2019-04-04 17:24:39 (GMT)
commit4359fe133b03aac5d0b0c1ed2aa98a49515edf56 (patch)
tree0092e4186bce033cd72135c4b1f7234a45d1bbb6 /Source
parent8c28e63cb4da99dbb894573a996c27e260b5baeb (diff)
downloadCMake-4359fe133b03aac5d0b0c1ed2aa98a49515edf56.zip
CMake-4359fe133b03aac5d0b0c1ed2aa98a49515edf56.tar.gz
CMake-4359fe133b03aac5d0b0c1ed2aa98a49515edf56.tar.bz2
Introduce cmArgumentParser
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmArgumentParser.cxx93
-rw-r--r--Source/cmArgumentParser.h143
3 files changed, 238 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 52f6742..a2836d6 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -143,6 +143,8 @@ set(SRCS
cmAffinity.cxx
cmAffinity.h
cmArchiveWrite.cxx
+ cmArgumentParser.cxx
+ cmArgumentParser.h
cmBase32.cxx
cmCacheManager.cxx
cmCacheManager.h
diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx
new file mode 100644
index 0000000..9a9932c
--- /dev/null
+++ b/Source/cmArgumentParser.cxx
@@ -0,0 +1,93 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmArgumentParser.h"
+
+#include <algorithm>
+#include <type_traits>
+
+namespace ArgumentParser {
+
+auto ActionMap::Emplace(cm::string_view name, Action action)
+ -> std::pair<iterator, bool>
+{
+ auto const it =
+ std::lower_bound(this->begin(), this->end(), name,
+ [](value_type const& elem, cm::string_view const& k) {
+ return elem.first < k;
+ });
+ return (it != this->end() && it->first == name)
+ ? std::make_pair(it, false)
+ : std::make_pair(this->emplace(it, name, std::move(action)), true);
+}
+
+auto ActionMap::Find(cm::string_view name) const -> const_iterator
+{
+ auto const it =
+ std::lower_bound(this->begin(), this->end(), name,
+ [](value_type const& elem, cm::string_view const& k) {
+ return elem.first < k;
+ });
+ return (it != this->end() && it->first == name) ? it : this->end();
+}
+
+void Instance::Bind(bool& val)
+{
+ val = true;
+ this->CurrentString = nullptr;
+ this->CurrentList = nullptr;
+ this->ExpectValue = false;
+}
+
+void Instance::Bind(std::string& val)
+{
+ this->CurrentString = &val;
+ this->CurrentList = nullptr;
+ this->ExpectValue = true;
+}
+
+void Instance::Bind(StringList& val)
+{
+ this->CurrentString = nullptr;
+ this->CurrentList = &val;
+ this->ExpectValue = true;
+}
+
+void Instance::Bind(MultiStringList& val)
+{
+ this->CurrentString = nullptr;
+ this->CurrentList = (val.emplace_back(), &val.back());
+ this->ExpectValue = false;
+}
+
+void Instance::Consume(cm::string_view arg, void* result,
+ std::vector<std::string>* unparsedArguments,
+ std::vector<std::string>* keywordsMissingValue)
+{
+ auto const it = this->Bindings.Find(arg);
+ if (it != this->Bindings.end()) {
+ it->second(*this, result);
+ if (this->ExpectValue && keywordsMissingValue != nullptr) {
+ keywordsMissingValue->emplace_back(arg);
+ }
+ return;
+ }
+
+ if (this->CurrentString != nullptr) {
+ this->CurrentString->assign(std::string(arg));
+ this->CurrentString = nullptr;
+ this->CurrentList = nullptr;
+ } else if (this->CurrentList != nullptr) {
+ this->CurrentList->emplace_back(arg);
+ } else if (unparsedArguments != nullptr) {
+ unparsedArguments->emplace_back(arg);
+ }
+
+ if (this->ExpectValue) {
+ if (keywordsMissingValue != nullptr) {
+ keywordsMissingValue->pop_back();
+ }
+ this->ExpectValue = false;
+ }
+}
+
+} // namespace ArgumentParser
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
new file mode 100644
index 0000000..6cfe946
--- /dev/null
+++ b/Source/cmArgumentParser.h
@@ -0,0 +1,143 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmArgumentParser_h
+#define cmArgumentParser_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_static_string_view.hxx"
+#include "cm_string_view.hxx"
+
+#include <cassert>
+#include <functional>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace ArgumentParser {
+
+using StringList = std::vector<std::string>;
+using MultiStringList = std::vector<StringList>;
+
+class Instance;
+using Action = std::function<void(Instance&, void*)>;
+
+// using ActionMap = cm::flat_map<cm::string_view, Action>;
+class ActionMap : public std::vector<std::pair<cm::string_view, Action>>
+{
+public:
+ std::pair<iterator, bool> Emplace(cm::string_view name, Action action);
+ const_iterator Find(cm::string_view name) const;
+};
+
+class Instance
+{
+public:
+ Instance(ActionMap const& bindings)
+ : Bindings(bindings)
+ {
+ }
+
+ void Bind(bool& val);
+ void Bind(std::string& val);
+ void Bind(StringList& val);
+ void Bind(MultiStringList& val);
+
+ void Consume(cm::string_view arg, void* result,
+ std::vector<std::string>* unparsedArguments,
+ std::vector<std::string>* keywordsMissingValue);
+
+private:
+ ActionMap const& Bindings;
+ std::string* CurrentString = nullptr;
+ StringList* CurrentList = nullptr;
+ bool ExpectValue = false;
+};
+
+} // namespace ArgumentParser
+
+template <typename Result>
+class cmArgumentParser
+{
+public:
+ // I *think* this function could be made `constexpr` when the code is
+ // compiled as C++20. This would allow building a parser at compile time.
+ template <typename T>
+ cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
+ {
+ bool const inserted =
+ this->Bindings
+ .Emplace(name,
+ [member](ArgumentParser::Instance& instance, void* result) {
+ instance.Bind(static_cast<Result*>(result)->*member);
+ })
+ .second;
+ assert(inserted), (void)inserted;
+ return *this;
+ }
+
+ template <typename Range>
+ void Parse(Result& result, Range const& args,
+ std::vector<std::string>* unparsedArguments = nullptr,
+ std::vector<std::string>* keywordsMissingValue = nullptr) const
+ {
+ ArgumentParser::Instance instance(this->Bindings);
+ for (cm::string_view arg : args) {
+ instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue);
+ }
+ }
+
+ template <typename Range>
+ Result Parse(Range const& args,
+ std::vector<std::string>* unparsedArguments = nullptr,
+ std::vector<std::string>* keywordsMissingValue = nullptr) const
+ {
+ Result result;
+ this->Parse(result, args, unparsedArguments, keywordsMissingValue);
+ return result;
+ }
+
+private:
+ ArgumentParser::ActionMap Bindings;
+};
+
+template <>
+class cmArgumentParser<void>
+{
+public:
+ template <typename T>
+ cmArgumentParser& Bind(cm::static_string_view name, T& ref)
+ {
+ bool const inserted = this->Bind(cm::string_view(name), ref);
+ assert(inserted), (void)inserted;
+ return *this;
+ }
+
+ template <typename Range>
+ void Parse(Range const& args,
+ std::vector<std::string>* unparsedArguments = nullptr,
+ std::vector<std::string>* keywordsMissingValue = nullptr) const
+ {
+ ArgumentParser::Instance instance(this->Bindings);
+ for (cm::string_view arg : args) {
+ instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue);
+ }
+ }
+
+protected:
+ template <typename T>
+ bool Bind(cm::string_view name, T& ref)
+ {
+ return this->Bindings
+ .Emplace(name,
+ [&ref](ArgumentParser::Instance& instance, void*) {
+ instance.Bind(ref);
+ })
+ .second;
+ }
+
+private:
+ ArgumentParser::ActionMap Bindings;
+};
+
+#endif