1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmGeneratorExpressionParser.h"
10 #include <cmext/algorithm>
11 #include <cmext/memory>
13 #include "cmGeneratorExpressionEvaluator.h"
15 cmGeneratorExpressionParser::cmGeneratorExpressionParser(
16 std::vector<cmGeneratorExpressionToken> tokens)
17 : Tokens(std::move(tokens))
21 void cmGeneratorExpressionParser::Parse(
22 cmGeneratorExpressionEvaluatorVector& result)
24 this->it = this->Tokens.begin();
26 while (this->it != this->Tokens.end()) {
27 this->ParseContent(result);
31 static void extendText(
32 cmGeneratorExpressionEvaluatorVector& result,
33 std::vector<cmGeneratorExpressionToken>::const_iterator it)
35 if (!result.empty() &&
36 (*(result.end() - 1))->GetType() ==
37 cmGeneratorExpressionEvaluator::Text) {
38 cm::static_reference_cast<TextContent>(*(result.end() - 1))
41 auto textContent = cm::make_unique<TextContent>(it->Content, it->Length);
42 result.push_back(std::move(textContent));
46 static void extendResult(
47 cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector& result,
48 cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector&& contents)
50 if (!result.empty() &&
51 (*(result.end() - 1))->GetType() ==
52 cmGeneratorExpressionEvaluator::Text &&
53 contents.front()->GetType() == cmGeneratorExpressionEvaluator::Text) {
54 cm::static_reference_cast<TextContent>(*(result.end() - 1))
56 cm::static_reference_cast<TextContent>(contents.front()).GetLength());
57 contents.erase(contents.begin());
59 cm::append(result, std::move(contents));
62 void cmGeneratorExpressionParser::ParseGeneratorExpression(
63 cmGeneratorExpressionEvaluatorVector& result)
65 assert(this->it != this->Tokens.end());
66 unsigned int nestedLevel = this->NestingLevel;
69 auto startToken = this->it - 1;
71 cmGeneratorExpressionEvaluatorVector identifier;
72 while (this->it->TokenType != cmGeneratorExpressionToken::EndExpression &&
73 this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator) {
74 if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) {
75 extendText(identifier, this->it);
78 this->ParseContent(identifier);
80 if (this->it == this->Tokens.end()) {
84 if (identifier.empty()) {
88 if (this->it != this->Tokens.end() &&
89 this->it->TokenType == cmGeneratorExpressionToken::EndExpression) {
90 auto content = cm::make_unique<GeneratorExpressionContent>(
92 this->it->Content - startToken->Content + this->it->Length);
93 assert(this->it != this->Tokens.end());
96 content->SetIdentifier(std::move(identifier));
97 result.push_back(std::move(content));
101 std::vector<cmGeneratorExpressionEvaluatorVector> parameters;
102 std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator>
104 std::vector<cmGeneratorExpressionToken>::const_iterator colonToken;
106 bool emptyParamTermination = false;
108 if (this->it != this->Tokens.end() &&
109 this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) {
110 colonToken = this->it;
111 parameters.resize(parameters.size() + 1);
112 assert(this->it != this->Tokens.end());
114 if (this->it == this->Tokens.end()) {
115 emptyParamTermination = true;
118 while (this->it != this->Tokens.end() &&
119 this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) {
120 commaTokens.push_back(this->it);
121 parameters.resize(parameters.size() + 1);
122 assert(this->it != this->Tokens.end());
124 if (this->it == this->Tokens.end()) {
125 emptyParamTermination = true;
128 while (this->it != this->Tokens.end() &&
129 this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) {
130 extendText(*(parameters.end() - 1), this->it);
131 assert(this->it != this->Tokens.end());
134 while (this->it != this->Tokens.end() &&
135 this->it->TokenType != cmGeneratorExpressionToken::EndExpression) {
136 this->ParseContent(*(parameters.end() - 1));
137 if (this->it == this->Tokens.end()) {
140 while (this->it != this->Tokens.end() &&
141 this->it->TokenType ==
142 cmGeneratorExpressionToken::CommaSeparator) {
143 commaTokens.push_back(this->it);
144 parameters.resize(parameters.size() + 1);
145 assert(this->it != this->Tokens.end());
147 if (this->it == this->Tokens.end()) {
148 emptyParamTermination = true;
151 while (this->it != this->Tokens.end() &&
152 this->it->TokenType ==
153 cmGeneratorExpressionToken::ColonSeparator) {
154 extendText(*(parameters.end() - 1), this->it);
155 assert(this->it != this->Tokens.end());
159 if (this->it != this->Tokens.end() &&
160 this->it->TokenType == cmGeneratorExpressionToken::EndExpression) {
161 --this->NestingLevel;
162 assert(this->it != this->Tokens.end());
167 if (nestedLevel != this->NestingLevel) {
168 // There was a '$<' in the text, but no corresponding '>'. Rebuild to
169 // treat the '$<' as having been plain text, along with the
170 // corresponding : and , tokens that might have been found.
171 extendText(result, startToken);
172 extendResult(result, std::move(identifier));
173 if (!parameters.empty()) {
174 extendText(result, colonToken);
176 auto pit = parameters.begin();
177 const auto pend = parameters.end();
178 auto commaIt = commaTokens.begin();
179 assert(parameters.size() > commaTokens.size());
180 for (; pit != pend; ++pit, ++commaIt) {
181 if (!pit->empty() && !emptyParamTermination) {
182 extendResult(result, std::move(*pit));
184 if (commaIt != commaTokens.end()) {
185 extendText(result, *commaIt);
194 size_t contentLength =
195 ((this->it - 1)->Content - startToken->Content) + (this->it - 1)->Length;
196 auto content = cm::make_unique<GeneratorExpressionContent>(
197 startToken->Content, contentLength);
198 content->SetIdentifier(std::move(identifier));
199 content->SetParameters(std::move(parameters));
200 result.push_back(std::move(content));
203 void cmGeneratorExpressionParser::ParseContent(
204 cmGeneratorExpressionEvaluatorVector& result)
206 assert(this->it != this->Tokens.end());
207 switch (this->it->TokenType) {
208 case cmGeneratorExpressionToken::Text: {
209 if (this->NestingLevel == 0) {
210 if (!result.empty() &&
211 (*(result.end() - 1))->GetType() ==
212 cmGeneratorExpressionEvaluator::Text) {
213 // A comma in 'plain text' could have split text that should
214 // otherwise be continuous. Extend the last text content instead of
215 // creating a new one.
216 cm::static_reference_cast<TextContent>(*(result.end() - 1))
217 .Extend(this->it->Length);
218 assert(this->it != this->Tokens.end());
224 cm::make_unique<TextContent>(this->it->Content, this->it->Length);
225 result.push_back(std::move(n));
226 assert(this->it != this->Tokens.end());
230 case cmGeneratorExpressionToken::BeginExpression:
231 assert(this->it != this->Tokens.end());
233 this->ParseGeneratorExpression(result);
235 case cmGeneratorExpressionToken::EndExpression:
236 case cmGeneratorExpressionToken::ColonSeparator:
237 case cmGeneratorExpressionToken::CommaSeparator:
238 if (this->NestingLevel == 0) {
239 extendText(result, this->it);
241 assert(false && "Got unexpected syntax token.");
243 assert(this->it != this->Tokens.end());
247 assert(false && "Unhandled token in generator expression.");