/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 | 416 | { |
18 | 416 | auto const it = std::lower_bound( |
19 | 416 | this->begin(), this->end(), name, |
20 | 1.32k | [](value_type const& elem, cm::string_view k) { return elem.first < k; }); |
21 | 416 | return (it != this->end() && it->first == name) |
22 | 416 | ? std::make_pair(it, false) |
23 | 416 | : std::make_pair(this->emplace(it, name, std::move(action)), true); |
24 | 416 | } |
25 | | |
26 | | auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator |
27 | 0 | { |
28 | 0 | auto const it = std::lower_bound( |
29 | 0 | this->begin(), this->end(), name, |
30 | 0 | [](value_type const& elem, cm::string_view k) { return elem.first < k; }); |
31 | 0 | return (it != this->end() && it->first == name) ? it : this->end(); |
32 | 0 | } |
33 | | |
34 | | auto PositionActionMap::Emplace(std::size_t pos, PositionAction action) |
35 | | -> std::pair<iterator, bool> |
36 | 44 | { |
37 | 44 | auto const it = std::lower_bound( |
38 | 44 | this->begin(), this->end(), pos, |
39 | 44 | [](value_type const& elem, std::size_t k) { return elem.first < k; }); |
40 | 44 | return (it != this->end() && it->first == pos) |
41 | 44 | ? std::make_pair(it, false) |
42 | 44 | : std::make_pair(this->emplace(it, pos, std::move(action)), true); |
43 | 44 | } |
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 | 0 | { |
56 | 0 | this->GetState().KeywordValueFunc = std::move(f); |
57 | 0 | this->GetState().KeywordValuesExpected = expect.Count; |
58 | 0 | } |
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 | 0 | { |
68 | 0 | this->Bind( |
69 | 0 | [&val](cm::string_view arg) -> Continue { |
70 | 0 | val = std::string(arg); |
71 | 0 | return Continue::No; |
72 | 0 | }, |
73 | 0 | ExpectAtLeast{ 1 }); |
74 | 0 | } |
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 | 0 | { |
112 | 0 | this->Bind( |
113 | 0 | [&val](cm::string_view arg) -> Continue { |
114 | 0 | val.emplace_back(arg); |
115 | 0 | return Continue::Yes; |
116 | 0 | }, |
117 | 0 | ExpectAtLeast{ 0 }); |
118 | 0 | } |
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(std::vector<std::vector<std::string>>& multiVal) |
131 | 0 | { |
132 | 0 | multiVal.emplace_back(); |
133 | 0 | std::vector<std::string>& val = multiVal.back(); |
134 | 0 | this->Bind( |
135 | 0 | [&val](cm::string_view arg) -> Continue { |
136 | 0 | val.emplace_back(arg); |
137 | 0 | return Continue::Yes; |
138 | 0 | }, |
139 | 0 | ExpectAtLeast{ 0 }); |
140 | 0 | } |
141 | | |
142 | | void Instance::Consume(cm::string_view arg) |
143 | 0 | { |
144 | 0 | ParserState& state = this->GetState(); |
145 | |
|
146 | 0 | auto const it = state.Bindings.Keywords.Find(arg); |
147 | 0 | if (it != state.Bindings.Keywords.end()) { |
148 | 0 | this->FinishKeyword(); |
149 | 0 | state.Keyword = it->first; |
150 | 0 | state.KeywordValuesSeen = 0; |
151 | 0 | state.DoneWithPositional = true; |
152 | 0 | if (state.Bindings.ParsedKeyword) { |
153 | 0 | state.Bindings.ParsedKeyword(*this, it->first); |
154 | 0 | } |
155 | 0 | it->second(*this); |
156 | 0 | return; |
157 | 0 | } |
158 | | |
159 | 0 | if (!state.DoneWithPositional) { |
160 | 0 | auto const pit = state.Bindings.Positions.Find(state.Pos); |
161 | 0 | if (pit != state.Bindings.Positions.end()) { |
162 | 0 | pit->second(*this, state.Pos, arg); |
163 | 0 | return; |
164 | 0 | } |
165 | | |
166 | 0 | if (state.Bindings.TrailingArgs) { |
167 | 0 | state.Keyword = ""_s; |
168 | 0 | state.KeywordValuesSeen = 0; |
169 | 0 | state.DoneWithPositional = true; |
170 | 0 | state.Bindings.TrailingArgs(*this); |
171 | 0 | if (!state.KeywordValueFunc) { |
172 | 0 | return; |
173 | 0 | } |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 0 | if (state.KeywordValueFunc) { |
178 | 0 | switch (state.KeywordValueFunc(arg)) { |
179 | 0 | case Continue::Yes: |
180 | 0 | break; |
181 | 0 | case Continue::No: |
182 | 0 | state.KeywordValueFunc = nullptr; |
183 | 0 | break; |
184 | 0 | } |
185 | 0 | ++state.KeywordValuesSeen; |
186 | 0 | return; |
187 | 0 | } |
188 | | |
189 | 0 | if (this->UnparsedArguments) { |
190 | 0 | this->UnparsedArguments->emplace_back(arg); |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | void Instance::FinishKeyword() |
195 | 0 | { |
196 | 0 | ParserState const& state = this->GetState(); |
197 | 0 | if (!state.DoneWithPositional) { |
198 | 0 | return; |
199 | 0 | } |
200 | | |
201 | 0 | if (state.KeywordValuesSeen < state.KeywordValuesExpected) { |
202 | 0 | if (this->ParseResults) { |
203 | 0 | this->ParseResults->AddKeywordError(state.Keyword, |
204 | 0 | " missing required value\n"); |
205 | 0 | } |
206 | 0 | if (state.Bindings.KeywordMissingValue) { |
207 | 0 | state.Bindings.KeywordMissingValue(*this, state.Keyword); |
208 | 0 | } |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | | bool ParseResult::MaybeReportError(cmMakefile& mf) const |
213 | 0 | { |
214 | 0 | if (*this) { |
215 | 0 | return false; |
216 | 0 | } |
217 | 0 | std::string e; |
218 | 0 | for (auto const& kel : this->KeywordErrors) { |
219 | 0 | e = cmStrCat(std::move(e), "Error after keyword \"", kel.first, "\":\n", |
220 | 0 | cmJoin(kel.second, {})); |
221 | 0 | } |
222 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, e); |
223 | 0 | return true; |
224 | 0 | } |
225 | | |
226 | | bool ParseResult::Check(cm::string_view context, |
227 | | std::vector<std::string> const* unparsedArguments, |
228 | | cmExecutionStatus& status) const |
229 | 0 | { |
230 | 0 | if (unparsedArguments && !unparsedArguments->empty()) { |
231 | 0 | status.SetError(cmStrCat(context, " given unknown argument: \""_s, |
232 | 0 | unparsedArguments->front(), "\"."_s)); |
233 | 0 | return false; |
234 | 0 | } |
235 | | |
236 | 0 | if (!this->KeywordErrors.empty()) { |
237 | 0 | std::string msg = cmStrCat( |
238 | 0 | context, (context.empty() ? ""_s : " "_s), "given invalid "_s, |
239 | 0 | (this->KeywordErrors.size() > 1 ? "arguments:"_s : "argument:"_s)); |
240 | 0 | for (auto const& kel : this->KeywordErrors) { |
241 | 0 | for (auto const& ke : kel.second) { |
242 | 0 | msg = |
243 | 0 | cmStrCat(msg, "\n "_s, kel.first, ": "_s, cmStripWhitespace(ke)); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | status.SetError(msg); |
247 | 0 | return false; |
248 | 0 | } |
249 | | |
250 | 0 | return true; |
251 | 0 | } |
252 | | |
253 | | } // namespace ArgumentParser |