/src/CMake/Source/cmGeneratorExpressionParser.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 "cmGeneratorExpressionParser.h" |
4 | | |
5 | | #include <cassert> |
6 | | #include <cstddef> |
7 | | #include <utility> |
8 | | |
9 | | #include <cm/memory> |
10 | | #include <cmext/algorithm> |
11 | | #include <cmext/memory> |
12 | | |
13 | | #include "cmGeneratorExpressionEvaluator.h" |
14 | | #include "cmUnreachable.h" |
15 | | |
16 | | cmGeneratorExpressionParser::cmGeneratorExpressionParser( |
17 | | std::vector<cmGeneratorExpressionToken> tokens) |
18 | 0 | : Tokens(std::move(tokens)) |
19 | 0 | { |
20 | 0 | } |
21 | | |
22 | | void cmGeneratorExpressionParser::Parse( |
23 | | cmGeneratorExpressionEvaluatorVector& result) |
24 | 0 | { |
25 | 0 | this->it = this->Tokens.begin(); |
26 | |
|
27 | 0 | while (this->it != this->Tokens.end()) { |
28 | 0 | this->ParseContent(result); |
29 | 0 | } |
30 | 0 | } |
31 | | |
32 | | static void extendText( |
33 | | cmGeneratorExpressionEvaluatorVector& result, |
34 | | std::vector<cmGeneratorExpressionToken>::const_iterator it) |
35 | 0 | { |
36 | 0 | if (!result.empty() && |
37 | 0 | (*(result.end() - 1))->GetType() == |
38 | 0 | cmGeneratorExpressionEvaluator::Text) { |
39 | 0 | cm::static_reference_cast<TextContent>(*(result.end() - 1)) |
40 | 0 | .Extend(it->Length); |
41 | 0 | } else { |
42 | 0 | auto textContent = cm::make_unique<TextContent>(it->Content, it->Length); |
43 | 0 | result.push_back(std::move(textContent)); |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | static void extendResult( |
48 | | cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector& result, |
49 | | cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector&& contents) |
50 | 0 | { |
51 | 0 | if (!result.empty() && |
52 | 0 | (*(result.end() - 1))->GetType() == |
53 | 0 | cmGeneratorExpressionEvaluator::Text && |
54 | 0 | contents.front()->GetType() == cmGeneratorExpressionEvaluator::Text) { |
55 | 0 | cm::static_reference_cast<TextContent>(*(result.end() - 1)) |
56 | 0 | .Extend( |
57 | 0 | cm::static_reference_cast<TextContent>(contents.front()).GetLength()); |
58 | 0 | contents.erase(contents.begin()); |
59 | 0 | } |
60 | 0 | cm::append(result, std::move(contents)); |
61 | 0 | } |
62 | | |
63 | | void cmGeneratorExpressionParser::ParseGeneratorExpression( |
64 | | cmGeneratorExpressionEvaluatorVector& result) |
65 | 0 | { |
66 | 0 | assert(this->it != this->Tokens.end()); |
67 | 0 | unsigned int nestedLevel = this->NestingLevel; |
68 | 0 | ++this->NestingLevel; |
69 | |
|
70 | 0 | auto startToken = this->it - 1; |
71 | |
|
72 | 0 | cmGeneratorExpressionEvaluatorVector identifier; |
73 | 0 | while (this->it->TokenType != cmGeneratorExpressionToken::EndExpression && |
74 | 0 | this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator) { |
75 | 0 | if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { |
76 | 0 | extendText(identifier, this->it); |
77 | 0 | ++this->it; |
78 | 0 | } else { |
79 | 0 | this->ParseContent(identifier); |
80 | 0 | } |
81 | 0 | if (this->it == this->Tokens.end()) { |
82 | 0 | break; |
83 | 0 | } |
84 | 0 | } |
85 | 0 | if (identifier.empty()) { |
86 | | // ERROR |
87 | 0 | } |
88 | |
|
89 | 0 | if (this->it != this->Tokens.end() && |
90 | 0 | this->it->TokenType == cmGeneratorExpressionToken::EndExpression) { |
91 | 0 | auto content = cm::make_unique<GeneratorExpressionContent>( |
92 | 0 | startToken->Content, |
93 | 0 | this->it->Content - startToken->Content + this->it->Length); |
94 | 0 | assert(this->it != this->Tokens.end()); |
95 | 0 | ++this->it; |
96 | 0 | --this->NestingLevel; |
97 | 0 | content->SetIdentifier(std::move(identifier)); |
98 | 0 | result.push_back(std::move(content)); |
99 | 0 | return; |
100 | 0 | } |
101 | | |
102 | 0 | std::vector<cmGeneratorExpressionEvaluatorVector> parameters; |
103 | 0 | std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator> |
104 | 0 | commaTokens; |
105 | 0 | std::vector<cmGeneratorExpressionToken>::const_iterator colonToken; |
106 | |
|
107 | 0 | bool emptyParamTermination = false; |
108 | |
|
109 | 0 | if (this->it != this->Tokens.end() && |
110 | 0 | this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) { |
111 | 0 | colonToken = this->it; |
112 | 0 | parameters.resize(parameters.size() + 1); |
113 | 0 | assert(this->it != this->Tokens.end()); |
114 | 0 | ++this->it; |
115 | 0 | if (this->it == this->Tokens.end()) { |
116 | 0 | emptyParamTermination = true; |
117 | 0 | } |
118 | |
|
119 | 0 | auto handleCommaOrColon = [this, &commaTokens, ¶meters, |
120 | 0 | &emptyParamTermination]() -> void { |
121 | 0 | if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { |
122 | 0 | commaTokens.push_back(this->it); |
123 | 0 | parameters.resize(parameters.size() + 1); |
124 | 0 | assert(this->it != this->Tokens.end()); |
125 | 0 | ++this->it; |
126 | 0 | if (this->it == this->Tokens.end()) { |
127 | 0 | emptyParamTermination = true; |
128 | 0 | } |
129 | 0 | } else if (this->it->TokenType == |
130 | 0 | cmGeneratorExpressionToken::ColonSeparator) { |
131 | 0 | extendText(*(parameters.end() - 1), this->it); |
132 | 0 | assert(this->it != this->Tokens.end()); |
133 | 0 | ++this->it; |
134 | 0 | } |
135 | 0 | }; |
136 | |
|
137 | 0 | while ( |
138 | 0 | this->it != this->Tokens.end() && |
139 | 0 | (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator || |
140 | 0 | this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) { |
141 | 0 | handleCommaOrColon(); |
142 | 0 | } |
143 | 0 | while (this->it != this->Tokens.end() && |
144 | 0 | this->it->TokenType != cmGeneratorExpressionToken::EndExpression) { |
145 | 0 | this->ParseContent(*(parameters.end() - 1)); |
146 | 0 | if (this->it == this->Tokens.end()) { |
147 | 0 | break; |
148 | 0 | } |
149 | 0 | while ( |
150 | 0 | this->it != this->Tokens.end() && |
151 | 0 | (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator || |
152 | 0 | this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) { |
153 | 0 | handleCommaOrColon(); |
154 | 0 | } |
155 | 0 | } |
156 | 0 | if (this->it != this->Tokens.end() && |
157 | 0 | this->it->TokenType == cmGeneratorExpressionToken::EndExpression) { |
158 | 0 | --this->NestingLevel; |
159 | 0 | assert(this->it != this->Tokens.end()); |
160 | 0 | ++this->it; |
161 | 0 | } |
162 | 0 | } |
163 | |
|
164 | 0 | if (nestedLevel != this->NestingLevel) { |
165 | | // There was a '$<' in the text, but no corresponding '>'. Rebuild to |
166 | | // treat the '$<' as having been plain text, along with the |
167 | | // corresponding : and , tokens that might have been found. |
168 | 0 | extendText(result, startToken); |
169 | 0 | extendResult(result, std::move(identifier)); |
170 | 0 | if (!parameters.empty()) { |
171 | 0 | extendText(result, colonToken); |
172 | |
|
173 | 0 | auto pit = parameters.begin(); |
174 | 0 | auto const pend = parameters.end(); |
175 | 0 | auto commaIt = commaTokens.begin(); |
176 | 0 | assert(parameters.size() > commaTokens.size()); |
177 | 0 | for (; pit != pend; ++pit, ++commaIt) { |
178 | 0 | if (!pit->empty() && !emptyParamTermination) { |
179 | 0 | extendResult(result, std::move(*pit)); |
180 | 0 | } |
181 | 0 | if (commaIt != commaTokens.end()) { |
182 | 0 | extendText(result, *commaIt); |
183 | 0 | } else { |
184 | 0 | break; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | 0 | return; |
189 | 0 | } |
190 | | |
191 | 0 | size_t contentLength = |
192 | 0 | ((this->it - 1)->Content - startToken->Content) + (this->it - 1)->Length; |
193 | 0 | auto content = cm::make_unique<GeneratorExpressionContent>( |
194 | 0 | startToken->Content, contentLength); |
195 | 0 | content->SetIdentifier(std::move(identifier)); |
196 | 0 | content->SetParameters(std::move(parameters)); |
197 | 0 | result.push_back(std::move(content)); |
198 | 0 | } |
199 | | |
200 | | void cmGeneratorExpressionParser::ParseContent( |
201 | | cmGeneratorExpressionEvaluatorVector& result) |
202 | 0 | { |
203 | 0 | assert(this->it != this->Tokens.end()); |
204 | 0 | switch (this->it->TokenType) { |
205 | 0 | case cmGeneratorExpressionToken::Text: { |
206 | 0 | if (this->NestingLevel == 0) { |
207 | 0 | if (!result.empty() && |
208 | 0 | (*(result.end() - 1))->GetType() == |
209 | 0 | cmGeneratorExpressionEvaluator::Text) { |
210 | | // A comma in 'plain text' could have split text that should |
211 | | // otherwise be continuous. Extend the last text content instead of |
212 | | // creating a new one. |
213 | 0 | cm::static_reference_cast<TextContent>(*(result.end() - 1)) |
214 | 0 | .Extend(this->it->Length); |
215 | 0 | assert(this->it != this->Tokens.end()); |
216 | 0 | ++this->it; |
217 | 0 | return; |
218 | 0 | } |
219 | 0 | } |
220 | 0 | auto n = |
221 | 0 | cm::make_unique<TextContent>(this->it->Content, this->it->Length); |
222 | 0 | result.push_back(std::move(n)); |
223 | 0 | assert(this->it != this->Tokens.end()); |
224 | 0 | ++this->it; |
225 | 0 | return; |
226 | 0 | } |
227 | 0 | case cmGeneratorExpressionToken::BeginExpression: |
228 | 0 | assert(this->it != this->Tokens.end()); |
229 | 0 | ++this->it; |
230 | 0 | this->ParseGeneratorExpression(result); |
231 | 0 | return; |
232 | 0 | case cmGeneratorExpressionToken::EndExpression: |
233 | 0 | case cmGeneratorExpressionToken::ColonSeparator: |
234 | 0 | case cmGeneratorExpressionToken::CommaSeparator: |
235 | 0 | if (this->NestingLevel == 0) { |
236 | 0 | extendText(result, this->it); |
237 | 0 | } else { |
238 | 0 | CM_UNREACHABLE; |
239 | 0 | } |
240 | 0 | assert(this->it != this->Tokens.end()); |
241 | 0 | ++this->it; |
242 | 0 | return; |
243 | 0 | } |
244 | 0 | CM_UNREACHABLE; |
245 | 0 | } |