| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmGeneratorExpressionParser.h" | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cmext/algorithm> | |
| #include <cmext/memory> | |
| #include "cmGeneratorExpressionEvaluator.h" | |
| #include "cmUnreachable.h" | |
| cmGeneratorExpressionParser::cmGeneratorExpressionParser( | |
| std::vector<cmGeneratorExpressionToken> tokens) | |
| : Tokens(std::move(tokens)) | |
| { | |
| } | |
| void cmGeneratorExpressionParser::Parse( | |
| cmGeneratorExpressionEvaluatorVector& result) | |
| { | |
| this->it = this->Tokens.begin(); | |
| while (this->it != this->Tokens.end()) { | |
| this->ParseContent(result); | |
| } | |
| } | |
| static void extendText( | |
| cmGeneratorExpressionEvaluatorVector& result, | |
| std::vector<cmGeneratorExpressionToken>::const_iterator it) | |
| { | |
| if (!result.empty() && | |
| (*(result.end() - 1))->GetType() == | |
| cmGeneratorExpressionEvaluator::Text) { | |
| cm::static_reference_cast<TextContent>(*(result.end() - 1)) | |
| .Extend(it->Length); | |
| } else { | |
| auto textContent = cm::make_unique<TextContent>(it->Content, it->Length); | |
| result.push_back(std::move(textContent)); | |
| } | |
| } | |
| static void extendResult( | |
| cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector& result, | |
| cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector&& contents) | |
| { | |
| if (!result.empty() && | |
| (*(result.end() - 1))->GetType() == | |
| cmGeneratorExpressionEvaluator::Text && | |
| contents.front()->GetType() == cmGeneratorExpressionEvaluator::Text) { | |
| cm::static_reference_cast<TextContent>(*(result.end() - 1)) | |
| .Extend( | |
| cm::static_reference_cast<TextContent>(contents.front()).GetLength()); | |
| contents.erase(contents.begin()); | |
| } | |
| cm::append(result, std::move(contents)); | |
| } | |
| void cmGeneratorExpressionParser::ParseGeneratorExpression( | |
| cmGeneratorExpressionEvaluatorVector& result) | |
| { | |
| assert(this->it != this->Tokens.end()); | |
| unsigned int nestedLevel = this->NestingLevel; | |
| ++this->NestingLevel; | |
| auto startToken = this->it - 1; | |
| cmGeneratorExpressionEvaluatorVector identifier; | |
| while (this->it->TokenType != cmGeneratorExpressionToken::EndExpression && | |
| this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator) { | |
| if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { | |
| extendText(identifier, this->it); | |
| ++this->it; | |
| } else { | |
| this->ParseContent(identifier); | |
| } | |
| if (this->it == this->Tokens.end()) { | |
| break; | |
| } | |
| } | |
| if (identifier.empty()) { | |
| // ERROR | |
| } | |
| if (this->it != this->Tokens.end() && | |
| this->it->TokenType == cmGeneratorExpressionToken::EndExpression) { | |
| auto content = cm::make_unique<GeneratorExpressionContent>( | |
| startToken->Content, | |
| this->it->Content - startToken->Content + this->it->Length); | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| --this->NestingLevel; | |
| content->SetIdentifier(std::move(identifier)); | |
| result.push_back(std::move(content)); | |
| return; | |
| } | |
| std::vector<cmGeneratorExpressionEvaluatorVector> parameters; | |
| std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator> | |
| commaTokens; | |
| std::vector<cmGeneratorExpressionToken>::const_iterator colonToken; | |
| bool emptyParamTermination = false; | |
| if (this->it != this->Tokens.end() && | |
| this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) { | |
| colonToken = this->it; | |
| parameters.resize(parameters.size() + 1); | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| if (this->it == this->Tokens.end()) { | |
| emptyParamTermination = true; | |
| } | |
| auto handleCommaOrColon = [this, &commaTokens, ¶meters, | |
| &emptyParamTermination]() -> void { | |
| if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { | |
| commaTokens.push_back(this->it); | |
| parameters.resize(parameters.size() + 1); | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| if (this->it == this->Tokens.end()) { | |
| emptyParamTermination = true; | |
| } | |
| } else if (this->it->TokenType == | |
| cmGeneratorExpressionToken::ColonSeparator) { | |
| extendText(*(parameters.end() - 1), this->it); | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| } | |
| }; | |
| while ( | |
| this->it != this->Tokens.end() && | |
| (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator || | |
| this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) { | |
| handleCommaOrColon(); | |
| } | |
| while (this->it != this->Tokens.end() && | |
| this->it->TokenType != cmGeneratorExpressionToken::EndExpression) { | |
| this->ParseContent(*(parameters.end() - 1)); | |
| if (this->it == this->Tokens.end()) { | |
| break; | |
| } | |
| while ( | |
| this->it != this->Tokens.end() && | |
| (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator || | |
| this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) { | |
| handleCommaOrColon(); | |
| } | |
| } | |
| if (this->it != this->Tokens.end() && | |
| this->it->TokenType == cmGeneratorExpressionToken::EndExpression) { | |
| --this->NestingLevel; | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| } | |
| } | |
| if (nestedLevel != this->NestingLevel) { | |
| // There was a '$<' in the text, but no corresponding '>'. Rebuild to | |
| // treat the '$<' as having been plain text, along with the | |
| // corresponding : and , tokens that might have been found. | |
| extendText(result, startToken); | |
| extendResult(result, std::move(identifier)); | |
| if (!parameters.empty()) { | |
| extendText(result, colonToken); | |
| auto pit = parameters.begin(); | |
| auto const pend = parameters.end(); | |
| auto commaIt = commaTokens.begin(); | |
| assert(parameters.size() > commaTokens.size()); | |
| for (; pit != pend; ++pit, ++commaIt) { | |
| if (!pit->empty() && !emptyParamTermination) { | |
| extendResult(result, std::move(*pit)); | |
| } | |
| if (commaIt != commaTokens.end()) { | |
| extendText(result, *commaIt); | |
| } else { | |
| break; | |
| } | |
| } | |
| } | |
| return; | |
| } | |
| size_t contentLength = | |
| ((this->it - 1)->Content - startToken->Content) + (this->it - 1)->Length; | |
| auto content = cm::make_unique<GeneratorExpressionContent>( | |
| startToken->Content, contentLength); | |
| content->SetIdentifier(std::move(identifier)); | |
| content->SetParameters(std::move(parameters)); | |
| result.push_back(std::move(content)); | |
| } | |
| void cmGeneratorExpressionParser::ParseContent( | |
| cmGeneratorExpressionEvaluatorVector& result) | |
| { | |
| assert(this->it != this->Tokens.end()); | |
| switch (this->it->TokenType) { | |
| case cmGeneratorExpressionToken::Text: { | |
| if (this->NestingLevel == 0) { | |
| if (!result.empty() && | |
| (*(result.end() - 1))->GetType() == | |
| cmGeneratorExpressionEvaluator::Text) { | |
| // A comma in 'plain text' could have split text that should | |
| // otherwise be continuous. Extend the last text content instead of | |
| // creating a new one. | |
| cm::static_reference_cast<TextContent>(*(result.end() - 1)) | |
| .Extend(this->it->Length); | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| return; | |
| } | |
| } | |
| auto n = | |
| cm::make_unique<TextContent>(this->it->Content, this->it->Length); | |
| result.push_back(std::move(n)); | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| return; | |
| } | |
| case cmGeneratorExpressionToken::BeginExpression: | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| this->ParseGeneratorExpression(result); | |
| return; | |
| case cmGeneratorExpressionToken::EndExpression: | |
| case cmGeneratorExpressionToken::ColonSeparator: | |
| case cmGeneratorExpressionToken::CommaSeparator: | |
| if (this->NestingLevel == 0) { | |
| extendText(result, this->it); | |
| } else { | |
| CM_UNREACHABLE; | |
| } | |
| assert(this->it != this->Tokens.end()); | |
| ++this->it; | |
| return; | |
| } | |
| CM_UNREACHABLE; | |
| } |