cmArgumentParser.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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(NonEmpty<std::string>& val);
  144. void Bind(Maybe<std::string>& val);
  145. void Bind(MaybeEmpty<std::vector<std::string>>& val);
  146. void Bind(NonEmpty<std::vector<std::string>>& val);
  147. void Bind(std::vector<std::vector<std::string>>& val);
  148. // cm::optional<> records the presence the keyword to which it binds.
  149. template <typename T>
  150. void Bind(cm::optional<T>& optVal)
  151. {
  152. if (!optVal) {
  153. optVal.emplace();
  154. }
  155. this->Bind(*optVal);
  156. }
  157. template <typename Range>
  158. void Parse(Range const& args, std::size_t pos = 0)
  159. {
  160. for (cm::string_view arg : args) {
  161. this->Consume(pos++, arg);
  162. }
  163. this->FinishKeyword();
  164. }
  165. private:
  166. ActionMap const& Bindings;
  167. ParseResult* ParseResults = nullptr;
  168. std::vector<std::string>* UnparsedArguments = nullptr;
  169. void* Result = nullptr;
  170. cm::string_view Keyword;
  171. std::size_t KeywordValuesSeen = 0;
  172. std::size_t KeywordValuesExpected = 0;
  173. std::function<Continue(cm::string_view)> KeywordValueFunc;
  174. bool DoneWithPositional = false;
  175. void Consume(std::size_t pos, cm::string_view arg);
  176. void FinishKeyword();
  177. template <typename Result>
  178. friend class ::cmArgumentParser;
  179. };
  180. } // namespace ArgumentParser
  181. template <typename Result>
  182. class cmArgumentParser : private ArgumentParser::Base
  183. {
  184. public:
  185. // I *think* this function could be made `constexpr` when the code is
  186. // compiled as C++20. This would allow building a parser at compile time.
  187. template <typename T>
  188. cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
  189. {
  190. this->Base::Bind(name, [member](Instance& instance) {
  191. instance.Bind(static_cast<Result*>(instance.Result)->*member);
  192. });
  193. return *this;
  194. }
  195. cmArgumentParser& Bind(cm::static_string_view name,
  196. Continue (Result::*member)(cm::string_view),
  197. ExpectAtLeast expect = { 1 })
  198. {
  199. this->Base::Bind(name, [member, expect](Instance& instance) {
  200. Result* result = static_cast<Result*>(instance.Result);
  201. instance.Bind(
  202. [result, member](cm::string_view arg) -> Continue {
  203. return (result->*member)(arg);
  204. },
  205. expect);
  206. });
  207. return *this;
  208. }
  209. cmArgumentParser& Bind(cm::static_string_view name,
  210. Continue (Result::*member)(cm::string_view,
  211. cm::string_view),
  212. ExpectAtLeast expect = { 1 })
  213. {
  214. this->Base::Bind(name, [member, expect](Instance& instance) {
  215. Result* result = static_cast<Result*>(instance.Result);
  216. cm::string_view keyword = instance.Keyword;
  217. instance.Bind(
  218. [result, member, keyword](cm::string_view arg) -> Continue {
  219. return (result->*member)(keyword, arg);
  220. },
  221. expect);
  222. });
  223. return *this;
  224. }
  225. cmArgumentParser& Bind(cm::static_string_view name,
  226. std::function<Continue(Result&, cm::string_view)> f,
  227. ExpectAtLeast expect = { 1 })
  228. {
  229. this->Base::Bind(name, [f, expect](Instance& instance) {
  230. Result* result = static_cast<Result*>(instance.Result);
  231. instance.Bind(
  232. [result, &f](cm::string_view arg) -> Continue {
  233. return f(*result, arg);
  234. },
  235. expect);
  236. });
  237. return *this;
  238. }
  239. cmArgumentParser& Bind(
  240. cm::static_string_view name,
  241. std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
  242. ExpectAtLeast expect = { 1 })
  243. {
  244. this->Base::Bind(name, [f, expect](Instance& instance) {
  245. Result* result = static_cast<Result*>(instance.Result);
  246. cm::string_view keyword = instance.Keyword;
  247. instance.Bind(
  248. [result, keyword, &f](cm::string_view arg) -> Continue {
  249. return f(*result, keyword, arg);
  250. },
  251. expect);
  252. });
  253. return *this;
  254. }
  255. cmArgumentParser& Bind(std::size_t position,
  256. cm::optional<std::string> Result::*member)
  257. {
  258. this->Base::Bind(
  259. position,
  260. [member](Instance& instance, std::size_t, cm::string_view arg) {
  261. Result* result = static_cast<Result*>(instance.Result);
  262. result->*member = arg;
  263. });
  264. return *this;
  265. }
  266. cmArgumentParser& BindParsedKeywords(
  267. std::vector<cm::string_view> Result::*member)
  268. {
  269. this->Base::BindParsedKeyword(
  270. [member](Instance& instance, cm::string_view arg) {
  271. (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
  272. });
  273. return *this;
  274. }
  275. template <typename Range>
  276. bool Parse(Result& result, Range const& args,
  277. std::vector<std::string>* unparsedArguments,
  278. std::size_t pos = 0) const
  279. {
  280. using ArgumentParser::AsParseResultPtr;
  281. ParseResult* parseResultPtr = AsParseResultPtr(result);
  282. Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
  283. &result);
  284. instance.Parse(args, pos);
  285. return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
  286. }
  287. template <typename Range>
  288. Result Parse(Range const& args, std::vector<std::string>* unparsedArguments,
  289. std::size_t pos = 0) const
  290. {
  291. Result result;
  292. this->Parse(result, args, unparsedArguments, pos);
  293. return result;
  294. }
  295. };
  296. template <>
  297. class cmArgumentParser<void> : private ArgumentParser::Base
  298. {
  299. public:
  300. template <typename T>
  301. cmArgumentParser& Bind(cm::static_string_view name, T& ref)
  302. {
  303. this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
  304. return *this;
  305. }
  306. cmArgumentParser& Bind(cm::static_string_view name,
  307. std::function<Continue(cm::string_view)> f,
  308. ExpectAtLeast expect = { 1 })
  309. {
  310. this->Base::Bind(name, [f, expect](Instance& instance) {
  311. instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
  312. expect);
  313. });
  314. return *this;
  315. }
  316. cmArgumentParser& Bind(
  317. cm::static_string_view name,
  318. std::function<Continue(cm::string_view, cm::string_view)> f,
  319. ExpectAtLeast expect = { 1 })
  320. {
  321. this->Base::Bind(name, [f, expect](Instance& instance) {
  322. cm::string_view keyword = instance.Keyword;
  323. instance.Bind(
  324. [keyword, &f](cm::string_view arg) -> Continue {
  325. return f(keyword, arg);
  326. },
  327. expect);
  328. });
  329. return *this;
  330. }
  331. cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref)
  332. {
  333. this->Base::Bind(position,
  334. [&ref](Instance&, std::size_t, cm::string_view arg) {
  335. ref = std::string(arg);
  336. });
  337. return *this;
  338. }
  339. cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
  340. {
  341. this->Base::BindParsedKeyword(
  342. [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
  343. return *this;
  344. }
  345. template <typename Range>
  346. ParseResult Parse(Range const& args,
  347. std::vector<std::string>* unparsedArguments,
  348. std::size_t pos = 0) const
  349. {
  350. ParseResult parseResult;
  351. Instance instance(this->Bindings, &parseResult, unparsedArguments);
  352. instance.Parse(args, pos);
  353. return parseResult;
  354. }
  355. protected:
  356. using Base::Instance;
  357. using Base::BindKeywordMissingValue;
  358. template <typename T>
  359. bool Bind(cm::string_view name, T& ref)
  360. {
  361. return this->MaybeBind(name,
  362. [&ref](Instance& instance) { instance.Bind(ref); });
  363. }
  364. };