cmArgumentParser.h 11 KB

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