cmArgumentParser.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #pragma once
  4. #include "cmConfigure.h" // IWYU pragma: keep
  5. #include <cassert>
  6. #include <functional>
  7. #include <map>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include <cm/optional>
  12. #include <cm/string_view>
  13. #include <cm/type_traits>
  14. #include <cmext/string_view>
  15. #include "cmArgumentParserTypes.h" // IWYU pragma: keep
  16. template <typename Result>
  17. class cmArgumentParser; // IWYU pragma: keep
  18. class cmMakefile;
  19. namespace ArgumentParser {
  20. class ParseResult
  21. {
  22. std::map<cm::string_view, std::string> KeywordErrors;
  23. public:
  24. explicit operator bool() const { return this->KeywordErrors.empty(); }
  25. void AddKeywordError(cm::string_view key, cm::string_view text)
  26. {
  27. this->KeywordErrors[key] += text;
  28. }
  29. std::map<cm::string_view, std::string> const& GetKeywordErrors() const
  30. {
  31. return this->KeywordErrors;
  32. }
  33. bool MaybeReportError(cmMakefile& mf) const;
  34. };
  35. template <typename Result>
  36. typename std::enable_if<std::is_base_of<ParseResult, Result>::value,
  37. ParseResult*>::type
  38. AsParseResultPtr(Result& result)
  39. {
  40. return &result;
  41. }
  42. template <typename Result>
  43. typename std::enable_if<!std::is_base_of<ParseResult, Result>::value,
  44. ParseResult*>::type
  45. AsParseResultPtr(Result&)
  46. {
  47. return nullptr;
  48. }
  49. enum class Continue
  50. {
  51. No,
  52. Yes,
  53. };
  54. struct ExpectAtLeast
  55. {
  56. std::size_t Count = 0;
  57. ExpectAtLeast(std::size_t count)
  58. : Count(count)
  59. {
  60. }
  61. };
  62. class Instance;
  63. using KeywordAction = std::function<void(Instance&)>;
  64. using KeywordNameAction = std::function<void(Instance&, cm::string_view)>;
  65. // using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>;
  66. class KeywordActionMap
  67. : public std::vector<std::pair<cm::string_view, KeywordAction>>
  68. {
  69. public:
  70. std::pair<iterator, bool> Emplace(cm::string_view name,
  71. KeywordAction action);
  72. const_iterator Find(cm::string_view name) const;
  73. };
  74. class ActionMap
  75. {
  76. public:
  77. KeywordActionMap Keywords;
  78. KeywordNameAction KeywordMissingValue;
  79. KeywordNameAction ParsedKeyword;
  80. };
  81. class Base
  82. {
  83. public:
  84. using ExpectAtLeast = ArgumentParser::ExpectAtLeast;
  85. using Continue = ArgumentParser::Continue;
  86. using Instance = ArgumentParser::Instance;
  87. using ParseResult = ArgumentParser::ParseResult;
  88. ArgumentParser::ActionMap Bindings;
  89. bool MaybeBind(cm::string_view name, KeywordAction action)
  90. {
  91. return this->Bindings.Keywords.Emplace(name, std::move(action)).second;
  92. }
  93. void Bind(cm::string_view name, KeywordAction action)
  94. {
  95. bool const inserted = this->MaybeBind(name, std::move(action));
  96. assert(inserted);
  97. static_cast<void>(inserted);
  98. }
  99. void BindParsedKeyword(KeywordNameAction action)
  100. {
  101. assert(!this->Bindings.ParsedKeyword);
  102. this->Bindings.ParsedKeyword = std::move(action);
  103. }
  104. void BindKeywordMissingValue(KeywordNameAction action)
  105. {
  106. assert(!this->Bindings.KeywordMissingValue);
  107. this->Bindings.KeywordMissingValue = std::move(action);
  108. }
  109. };
  110. class Instance
  111. {
  112. public:
  113. Instance(ActionMap const& bindings, ParseResult* parseResult,
  114. std::vector<std::string>* unparsedArguments, void* result = nullptr)
  115. : Bindings(bindings)
  116. , ParseResults(parseResult)
  117. , UnparsedArguments(unparsedArguments)
  118. , Result(result)
  119. {
  120. }
  121. void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect);
  122. void Bind(bool& val);
  123. void Bind(std::string& val);
  124. void Bind(Maybe<std::string>& val);
  125. void Bind(MaybeEmpty<std::vector<std::string>>& val);
  126. void Bind(NonEmpty<std::vector<std::string>>& val);
  127. void Bind(std::vector<std::vector<std::string>>& val);
  128. // cm::optional<> records the presence the keyword to which it binds.
  129. template <typename T>
  130. void Bind(cm::optional<T>& optVal)
  131. {
  132. if (!optVal) {
  133. optVal.emplace();
  134. }
  135. this->Bind(*optVal);
  136. }
  137. template <typename Range>
  138. void Parse(Range const& args)
  139. {
  140. for (cm::string_view arg : args) {
  141. this->Consume(arg);
  142. }
  143. this->FinishKeyword();
  144. }
  145. private:
  146. ActionMap const& Bindings;
  147. ParseResult* ParseResults = nullptr;
  148. std::vector<std::string>* UnparsedArguments = nullptr;
  149. void* Result = nullptr;
  150. cm::string_view Keyword;
  151. std::size_t KeywordValuesSeen = 0;
  152. std::size_t KeywordValuesExpected = 0;
  153. std::function<Continue(cm::string_view)> KeywordValueFunc;
  154. void Consume(cm::string_view arg);
  155. void FinishKeyword();
  156. template <typename Result>
  157. friend class ::cmArgumentParser;
  158. };
  159. } // namespace ArgumentParser
  160. template <typename Result>
  161. class cmArgumentParser : private ArgumentParser::Base
  162. {
  163. public:
  164. // I *think* this function could be made `constexpr` when the code is
  165. // compiled as C++20. This would allow building a parser at compile time.
  166. template <typename T>
  167. cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
  168. {
  169. this->Base::Bind(name, [member](Instance& instance) {
  170. instance.Bind(static_cast<Result*>(instance.Result)->*member);
  171. });
  172. return *this;
  173. }
  174. cmArgumentParser& Bind(cm::static_string_view name,
  175. Continue (Result::*member)(cm::string_view),
  176. ExpectAtLeast expect = { 1 })
  177. {
  178. this->Base::Bind(name, [member, expect](Instance& instance) {
  179. Result* result = static_cast<Result*>(instance.Result);
  180. instance.Bind(
  181. [result, member](cm::string_view arg) -> Continue {
  182. return (result->*member)(arg);
  183. },
  184. expect);
  185. });
  186. return *this;
  187. }
  188. cmArgumentParser& Bind(cm::static_string_view name,
  189. Continue (Result::*member)(cm::string_view,
  190. cm::string_view),
  191. ExpectAtLeast expect = { 1 })
  192. {
  193. this->Base::Bind(name, [member, expect](Instance& instance) {
  194. Result* result = static_cast<Result*>(instance.Result);
  195. cm::string_view keyword = instance.Keyword;
  196. instance.Bind(
  197. [result, member, keyword](cm::string_view arg) -> Continue {
  198. return (result->*member)(keyword, arg);
  199. },
  200. expect);
  201. });
  202. return *this;
  203. }
  204. cmArgumentParser& Bind(cm::static_string_view name,
  205. std::function<Continue(Result&, cm::string_view)> f,
  206. ExpectAtLeast expect = { 1 })
  207. {
  208. this->Base::Bind(name, [f, expect](Instance& instance) {
  209. Result* result = static_cast<Result*>(instance.Result);
  210. instance.Bind(
  211. [result, &f](cm::string_view arg) -> Continue {
  212. return f(*result, arg);
  213. },
  214. expect);
  215. });
  216. return *this;
  217. }
  218. cmArgumentParser& Bind(
  219. cm::static_string_view name,
  220. std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
  221. ExpectAtLeast expect = { 1 })
  222. {
  223. this->Base::Bind(name, [f, expect](Instance& instance) {
  224. Result* result = static_cast<Result*>(instance.Result);
  225. cm::string_view keyword = instance.Keyword;
  226. instance.Bind(
  227. [result, keyword, &f](cm::string_view arg) -> Continue {
  228. return f(*result, keyword, arg);
  229. },
  230. expect);
  231. });
  232. return *this;
  233. }
  234. cmArgumentParser& BindParsedKeywords(
  235. std::vector<cm::string_view> Result::*member)
  236. {
  237. this->Base::BindParsedKeyword(
  238. [member](Instance& instance, cm::string_view arg) {
  239. (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
  240. });
  241. return *this;
  242. }
  243. template <typename Range>
  244. bool Parse(Result& result, Range const& args,
  245. std::vector<std::string>* unparsedArguments) const
  246. {
  247. using ArgumentParser::AsParseResultPtr;
  248. ParseResult* parseResultPtr = AsParseResultPtr(result);
  249. Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
  250. &result);
  251. instance.Parse(args);
  252. return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
  253. }
  254. template <typename Range>
  255. Result Parse(Range const& args,
  256. std::vector<std::string>* unparsedArguments) const
  257. {
  258. Result result;
  259. this->Parse(result, args, unparsedArguments);
  260. return result;
  261. }
  262. };
  263. template <>
  264. class cmArgumentParser<void> : private ArgumentParser::Base
  265. {
  266. public:
  267. template <typename T>
  268. cmArgumentParser& Bind(cm::static_string_view name, T& ref)
  269. {
  270. this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
  271. return *this;
  272. }
  273. cmArgumentParser& Bind(cm::static_string_view name,
  274. std::function<Continue(cm::string_view)> f,
  275. ExpectAtLeast expect = { 1 })
  276. {
  277. this->Base::Bind(name, [f, expect](Instance& instance) {
  278. instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
  279. expect);
  280. });
  281. return *this;
  282. }
  283. cmArgumentParser& Bind(
  284. cm::static_string_view name,
  285. std::function<Continue(cm::string_view, cm::string_view)> f,
  286. ExpectAtLeast expect = { 1 })
  287. {
  288. this->Base::Bind(name, [f, expect](Instance& instance) {
  289. cm::string_view keyword = instance.Keyword;
  290. instance.Bind(
  291. [keyword, &f](cm::string_view arg) -> Continue {
  292. return f(keyword, arg);
  293. },
  294. expect);
  295. });
  296. return *this;
  297. }
  298. cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
  299. {
  300. this->Base::BindParsedKeyword(
  301. [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
  302. return *this;
  303. }
  304. template <typename Range>
  305. ParseResult Parse(Range const& args,
  306. std::vector<std::string>* unparsedArguments) const
  307. {
  308. ParseResult parseResult;
  309. Instance instance(this->Bindings, &parseResult, unparsedArguments);
  310. instance.Parse(args);
  311. return parseResult;
  312. }
  313. protected:
  314. using Base::Instance;
  315. using Base::BindKeywordMissingValue;
  316. template <typename T>
  317. bool Bind(cm::string_view name, T& ref)
  318. {
  319. return this->MaybeBind(name,
  320. [&ref](Instance& instance) { instance.Bind(ref); });
  321. }
  322. };