/src/CMake/Source/cmArgumentParser.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmArgumentParser.h" |
4 | | |
5 | | #include <algorithm> |
6 | | |
7 | | #include "cmArgumentParserTypes.h" |
8 | | #include "cmExecutionStatus.h" |
9 | | #include "cmMakefile.h" |
10 | | #include "cmMessageType.h" |
11 | | #include "cmStringAlgorithms.h" |
12 | | |
13 | | namespace ArgumentParser { |
14 | | |
15 | | auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action) |
16 | | -> std::pair<iterator, bool> |
17 | 1.07k | { |
18 | 1.07k | auto const it = std::lower_bound( |
19 | 1.07k | this->begin(), this->end(), name, |
20 | 3.47k | [](value_type const& elem, cm::string_view k) { return elem.first < k; }); |
21 | 1.07k | return (it != this->end() && it->first == name) |
22 | 1.07k | ? std::make_pair(it, false) |
23 | 1.07k | : std::make_pair(this->emplace(it, name, std::move(action)), true); |
24 | 1.07k | } |
25 | | |
26 | | auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator |
27 | 142k | { |
28 | 142k | auto const it = std::lower_bound( |
29 | 142k | this->begin(), this->end(), name, |
30 | 260k | [](value_type const& elem, cm::string_view k) { return elem.first < k; }); |
31 | 142k | return (it != this->end() && it->first == name) ? it : this->end(); |
32 | 142k | } |
33 | | |
34 | | auto PositionActionMap::Emplace(std::size_t pos, PositionAction action) |
35 | | -> std::pair<iterator, bool> |
36 | 110 | { |
37 | 110 | auto const it = std::lower_bound( |
38 | 110 | this->begin(), this->end(), pos, |
39 | 110 | [](value_type const& elem, std::size_t k) { return elem.first < k; }); |
40 | 110 | return (it != this->end() && it->first == pos) |
41 | 110 | ? std::make_pair(it, false) |
42 | 110 | : std::make_pair(this->emplace(it, pos, std::move(action)), true); |
43 | 110 | } |
44 | | |
45 | | auto PositionActionMap::Find(std::size_t pos) const -> const_iterator |
46 | 0 | { |
47 | 0 | auto const it = std::lower_bound( |
48 | 0 | this->begin(), this->end(), pos, |
49 | 0 | [](value_type const& elem, std::size_t k) { return elem.first < k; }); |
50 | 0 | return (it != this->end() && it->first == pos) ? it : this->end(); |
51 | 0 | } |
52 | | |
53 | | void Instance::Bind(std::function<Continue(cm::string_view)> f, |
54 | | ExpectAtLeast expect) |
55 | 23.7k | { |
56 | 23.7k | this->GetState().KeywordValueFunc = std::move(f); |
57 | 23.7k | this->GetState().KeywordValuesExpected = expect.Count; |
58 | 23.7k | } |
59 | | |
60 | | void Instance::Bind(bool& val) |
61 | 0 | { |
62 | 0 | val = true; |
63 | 0 | this->Bind(nullptr, ExpectAtLeast{ 0 }); |
64 | 0 | } |
65 | | |
66 | | void Instance::Bind(std::string& val) |
67 | 5.92k | { |
68 | 5.92k | this->Bind( |
69 | 5.92k | [&val](cm::string_view arg) -> Continue { |
70 | 5.92k | val = std::string(arg); |
71 | 5.92k | return Continue::No; |
72 | 5.92k | }, |
73 | 5.92k | ExpectAtLeast{ 1 }); |
74 | 5.92k | } |
75 | | |
76 | | void Instance::Bind(MaybeEmpty<std::string>& val) |
77 | 0 | { |
78 | 0 | this->Bind( |
79 | 0 | [&val](cm::string_view arg) -> Continue { |
80 | 0 | val = std::string(arg); |
81 | 0 | return Continue::No; |
82 | 0 | }, |
83 | 0 | ExpectAtLeast{ 1 }); |
84 | 0 | } |
85 | | |
86 | | void Instance::Bind(NonEmpty<std::string>& val) |
87 | 0 | { |
88 | 0 | this->Bind( |
89 | 0 | [this, &val](cm::string_view arg) -> Continue { |
90 | 0 | if (arg.empty() && this->ParseResults) { |
91 | 0 | this->ParseResults->AddKeywordError(this->GetState().Keyword, |
92 | 0 | " empty string not allowed\n"); |
93 | 0 | } |
94 | 0 | val = std::string(arg); |
95 | 0 | return Continue::No; |
96 | 0 | }, |
97 | 0 | ExpectAtLeast{ 1 }); |
98 | 0 | } |
99 | | |
100 | | void Instance::Bind(Maybe<std::string>& val) |
101 | 0 | { |
102 | 0 | this->Bind( |
103 | 0 | [&val](cm::string_view arg) -> Continue { |
104 | 0 | val = std::string(arg); |
105 | 0 | return Continue::No; |
106 | 0 | }, |
107 | 0 | ExpectAtLeast{ 0 }); |
108 | 0 | } |
109 | | |
110 | | void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val) |
111 | 11.8k | { |
112 | 11.8k | this->Bind( |
113 | 11.8k | [&val](cm::string_view arg) -> Continue { |
114 | 11.8k | val.emplace_back(arg); |
115 | 11.8k | return Continue::Yes; |
116 | 11.8k | }, |
117 | 11.8k | ExpectAtLeast{ 0 }); |
118 | 11.8k | } |
119 | | |
120 | | void Instance::Bind(NonEmpty<std::vector<std::string>>& val) |
121 | 0 | { |
122 | 0 | this->Bind( |
123 | 0 | [&val](cm::string_view arg) -> Continue { |
124 | 0 | val.emplace_back(arg); |
125 | 0 | return Continue::Yes; |
126 | 0 | }, |
127 | 0 | ExpectAtLeast{ 1 }); |
128 | 0 | } |
129 | | |
130 | | void Instance::Bind( |
131 | | MaybeEmpty<std::vector<std::vector<std::string>>>& multiVal) |
132 | 5.92k | { |
133 | 5.92k | multiVal.emplace_back(); |
134 | 5.92k | std::vector<std::string>& val = multiVal.back(); |
135 | 5.92k | this->Bind( |
136 | 29.6k | [&val](cm::string_view arg) -> Continue { |
137 | 29.6k | val.emplace_back(arg); |
138 | 29.6k | return Continue::Yes; |
139 | 29.6k | }, |
140 | 5.92k | ExpectAtLeast{ 0 }); |
141 | 5.92k | } |
142 | | |
143 | | void Instance::Bind(NonEmpty<std::vector<std::vector<std::string>>>& multiVal) |
144 | 0 | { |
145 | 0 | multiVal.emplace_back(); |
146 | 0 | std::vector<std::string>& val = multiVal.back(); |
147 | 0 | this->Bind( |
148 | 0 | [&val](cm::string_view arg) -> Continue { |
149 | 0 | val.emplace_back(arg); |
150 | 0 | return Continue::Yes; |
151 | 0 | }, |
152 | 0 | ExpectAtLeast{ 1 }); |
153 | 0 | } |
154 | | |
155 | | void Instance::Consume(cm::string_view arg) |
156 | 71.1k | { |
157 | 71.1k | ParserState& state = this->GetState(); |
158 | | |
159 | 71.1k | auto const it = state.Bindings.Keywords.Find(arg); |
160 | 71.1k | if (it != state.Bindings.Keywords.end()) { |
161 | 23.7k | this->FinishKeyword(); |
162 | 23.7k | state.Keyword = it->first; |
163 | 23.7k | state.KeywordValuesSeen = 0; |
164 | 23.7k | state.DoneWithPositional = true; |
165 | 23.7k | if (state.Bindings.ParsedKeyword) { |
166 | 0 | state.Bindings.ParsedKeyword(*this, it->first); |
167 | 0 | } |
168 | 23.7k | it->second(*this); |
169 | 23.7k | return; |
170 | 23.7k | } |
171 | | |
172 | 47.4k | if (!state.DoneWithPositional) { |
173 | 0 | auto const pit = state.Bindings.Positions.Find(state.Pos); |
174 | 0 | if (pit != state.Bindings.Positions.end()) { |
175 | 0 | pit->second(*this, state.Pos, arg); |
176 | 0 | return; |
177 | 0 | } |
178 | | |
179 | 0 | if (state.Bindings.TrailingArgs) { |
180 | 0 | state.Keyword = ""_s; |
181 | 0 | state.KeywordValuesSeen = 0; |
182 | 0 | state.DoneWithPositional = true; |
183 | 0 | state.Bindings.TrailingArgs(*this); |
184 | 0 | if (!state.KeywordValueFunc) { |
185 | 0 | return; |
186 | 0 | } |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | 47.4k | if (state.KeywordValueFunc) { |
191 | 47.4k | switch (state.KeywordValueFunc(arg)) { |
192 | 41.4k | case Continue::Yes: |
193 | 41.4k | break; |
194 | 5.92k | case Continue::No: |
195 | 5.92k | state.KeywordValueFunc = nullptr; |
196 | 5.92k | break; |
197 | 47.4k | } |
198 | 47.4k | ++state.KeywordValuesSeen; |
199 | 47.4k | return; |
200 | 47.4k | } |
201 | | |
202 | 0 | if (this->UnparsedArguments) { |
203 | 0 | this->UnparsedArguments->emplace_back(arg); |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | void Instance::FinishKeyword() |
208 | 35.5k | { |
209 | 35.5k | ParserState const& state = this->GetState(); |
210 | 35.5k | if (!state.DoneWithPositional) { |
211 | 11.8k | return; |
212 | 11.8k | } |
213 | | |
214 | 23.7k | if (state.KeywordValuesSeen < state.KeywordValuesExpected) { |
215 | 0 | if (this->ParseResults) { |
216 | 0 | this->ParseResults->AddKeywordError(state.Keyword, |
217 | 0 | " missing required value\n"); |
218 | 0 | } |
219 | 0 | if (state.Bindings.KeywordMissingValue) { |
220 | 0 | state.Bindings.KeywordMissingValue(*this, state.Keyword); |
221 | 0 | } |
222 | 0 | } |
223 | 23.7k | } |
224 | | |
225 | | bool ParseResult::MaybeReportError(cmMakefile& mf) const |
226 | 0 | { |
227 | 0 | if (*this) { |
228 | 0 | return false; |
229 | 0 | } |
230 | 0 | std::string e; |
231 | 0 | for (auto const& kel : this->KeywordErrors) { |
232 | 0 | e = cmStrCat(std::move(e), "Error after keyword \"", kel.first, "\":\n", |
233 | 0 | cmJoin(kel.second, {})); |
234 | 0 | } |
235 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, e); |
236 | 0 | return true; |
237 | 0 | } |
238 | | |
239 | | bool ParseResult::Check(cm::string_view context, |
240 | | std::vector<std::string> const* unparsedArguments, |
241 | | cmExecutionStatus& status) const |
242 | 0 | { |
243 | 0 | if (unparsedArguments && !unparsedArguments->empty()) { |
244 | 0 | status.SetError(cmStrCat(context, " given unknown argument: \""_s, |
245 | 0 | unparsedArguments->front(), "\"."_s)); |
246 | 0 | return false; |
247 | 0 | } |
248 | | |
249 | 0 | if (!this->KeywordErrors.empty()) { |
250 | 0 | std::string msg = cmStrCat( |
251 | 0 | context, (context.empty() ? ""_s : " "_s), "given invalid "_s, |
252 | 0 | (this->KeywordErrors.size() > 1 ? "arguments:"_s : "argument:"_s)); |
253 | 0 | for (auto const& kel : this->KeywordErrors) { |
254 | 0 | for (auto const& ke : kel.second) { |
255 | 0 | msg = cmStrCat(std::move(msg), "\n "_s, kel.first, ": "_s, |
256 | 0 | cmStripWhitespace(ke)); |
257 | 0 | } |
258 | 0 | } |
259 | 0 | status.SetError(msg); |
260 | 0 | return false; |
261 | 0 | } |
262 | | |
263 | 0 | return true; |
264 | 0 | } |
265 | | |
266 | | } // namespace ArgumentParser |