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