/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2012 Stephen Kelly Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ #include "cmGeneratorExpressionParser.h" #include "cmGeneratorExpressionEvaluator.h" #include "assert.h" //---------------------------------------------------------------------------- cmGeneratorExpressionParser::cmGeneratorExpressionParser( const std::vector &tokens) : Tokens(tokens), NestingLevel(0) { } //---------------------------------------------------------------------------- void cmGeneratorExpressionParser::Parse( std::vector &result) { it = this->Tokens.begin(); while (this->it != this->Tokens.end()) { this->ParseContent(result); } } //---------------------------------------------------------------------------- static void extendText(std::vector &result, std::vector::const_iterator it) { if (result.size() > 0 && (*(result.end() - 1))->GetType() == cmGeneratorExpressionEvaluator::Text) { TextContent *textContent = static_cast(*(result.end() - 1)); textContent->Extend(it->Length); } else { TextContent *textContent = new TextContent(it->Content, it->Length); result.push_back(textContent); } } //---------------------------------------------------------------------------- static void extendResult(std::vector &result, const std::vector &contents) { if (result.size() > 0 && (*(result.end() - 1))->GetType() == cmGeneratorExpressionEvaluator::Text && (*contents.begin())->GetType() == cmGeneratorExpressionEvaluator::Text) { TextContent *textContent = static_cast(*(result.end() - 1)); textContent->Extend( static_cast(*contents.begin())->GetLength()); delete *contents.begin(); result.insert(result.end(), contents.begin() + 1, contents.end()); } else { result.insert(result.end(), contents.begin(), contents.end()); } } //---------------------------------------------------------------------------- void cmGeneratorExpressionParser::ParseGeneratorExpression( std::vector &result) { assert(this->it != this->Tokens.end()); unsigned int nestedLevel = this->NestingLevel; ++this->NestingLevel; std::vector::const_iterator startToken = this->it - 1; std::vector identifier; while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression && this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator) { 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) { GeneratorExpressionContent *content = new GeneratorExpressionContent( startToken->Content, this->it->Content - startToken->Content + this->it->Length); assert(this->it != this->Tokens.end()); ++this->it; --this->NestingLevel; content->SetIdentifier(identifier); result.push_back(content); return; } std::vector > parameters; std::vector::const_iterator> commaTokens; std::vector::const_iterator colonToken; 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; while (this->it != this->Tokens.end() && this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { commaTokens.push_back(this->it); parameters.resize(parameters.size() + 1); assert(this->it != this->Tokens.end()); ++this->it; } while (this->it != this->Tokens.end() && 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::EndExpression) { this->ParseContent(*(parameters.end() - 1)); if (this->it == this->Tokens.end()) { break; } while (this->it != this->Tokens.end() && this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { commaTokens.push_back(this->it); parameters.resize(parameters.size() + 1); assert(this->it != this->Tokens.end()); ++this->it; } while (this->it != this->Tokens.end() && this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) { extendText(*(parameters.end() - 1), this->it); assert(this->it != this->Tokens.end()); ++this->it; } } 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, identifier); if (!parameters.empty()) { extendText(result, colonToken); typedef std::vector EvaluatorVector; typedef std::vector TokenVector; std::vector::const_iterator pit = parameters.begin(); const std::vector::const_iterator pend = parameters.end(); std::vector::const_iterator commaIt = commaTokens.begin(); assert(parameters.size() > commaTokens.size()); for ( ; pit != pend; ++pit, ++commaIt) { extendResult(result, *pit); if (commaIt != commaTokens.end()) { extendText(result, *commaIt); } else { break; } } } return; } int contentLength = ((this->it - 1)->Content - startToken->Content) + (this->it - 1)->Length; GeneratorExpressionContent *content = new GeneratorExpressionContent( startToken->Content, contentLength); content->SetIdentifier(identifier); content->SetParameters(parameters); result.push_back(content); } //---------------------------------------------------------------------------- void cmGeneratorExpressionParser::ParseContent( std::vector &result) { assert(this->it != this->Tokens.end()); switch(this->it->TokenType) { case cmGeneratorExpressionToken::Text: { if (this->NestingLevel == 0) { if (result.size() > 0 && (*(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. TextContent *textContent = static_cast(*(result.end() - 1)); textContent->Extend(this->it->Length); assert(this->it != this->Tokens.end()); ++this->it; return; } } cmGeneratorExpressionEvaluator* n = new TextContent(this->it->Content, this->it->Length); result.push_back(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 { assert(!"Got unexpected syntax token."); } assert(this->it != this->Tokens.end()); ++this->it; return; } assert(!"Unhandled token in generator expression."); }