| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2012 Stephen Kelly <steveire@gmail.com> |
| |
| 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<cmGeneratorExpressionToken> &tokens) |
| : Tokens(tokens), NestingLevel(0) |
| { |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGeneratorExpressionParser::Parse( |
| std::vector<cmGeneratorExpressionEvaluator*> &result) |
| { |
| it = this->Tokens.begin(); |
| |
| while (this->it != this->Tokens.end()) |
| { |
| this->ParseContent(result); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| static void extendText(std::vector<cmGeneratorExpressionEvaluator*> &result, |
| std::vector<cmGeneratorExpressionToken>::const_iterator it) |
| { |
| if (!result.empty() |
| && (*(result.end() - 1))->GetType() |
| == cmGeneratorExpressionEvaluator::Text) |
| { |
| TextContent *textContent = static_cast<TextContent*>(*(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<cmGeneratorExpressionEvaluator*> &result, |
| const std::vector<cmGeneratorExpressionEvaluator*> &contents) |
| { |
| if (!result.empty() |
| && (*(result.end() - 1))->GetType() |
| == cmGeneratorExpressionEvaluator::Text |
| && (*contents.begin())->GetType() |
| == cmGeneratorExpressionEvaluator::Text) |
| { |
| TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1)); |
| textContent->Extend( |
| static_cast<TextContent*>(*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<cmGeneratorExpressionEvaluator*> &result) |
| { |
| assert(this->it != this->Tokens.end()); |
| unsigned int nestedLevel = this->NestingLevel; |
| ++this->NestingLevel; |
| |
| std::vector<cmGeneratorExpressionToken>::const_iterator startToken |
| = this->it - 1; |
| |
| std::vector<cmGeneratorExpressionEvaluator*> 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) |
| { |
| 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<std::vector<cmGeneratorExpressionEvaluator*> > 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; |
| } |
| |
| 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; |
| if(this->it == this->Tokens.end()) |
| { |
| emptyParamTermination = true; |
| } |
| } |
| 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; |
| if(this->it == this->Tokens.end()) |
| { |
| emptyParamTermination = true; |
| } |
| } |
| 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<cmGeneratorExpressionEvaluator*> EvaluatorVector; |
| typedef std::vector<cmGeneratorExpressionToken> TokenVector; |
| std::vector<EvaluatorVector>::const_iterator pit = parameters.begin(); |
| const std::vector<EvaluatorVector>::const_iterator pend = |
| parameters.end(); |
| std::vector<TokenVector::const_iterator>::const_iterator commaIt = |
| commaTokens.begin(); |
| assert(parameters.size() > commaTokens.size()); |
| for ( ; pit != pend; ++pit, ++commaIt) |
| { |
| if (!pit->empty() && !emptyParamTermination) |
| { |
| extendResult(result, *pit); |
| } |
| if (commaIt != commaTokens.end()) |
| { |
| extendText(result, *commaIt); |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| return; |
| } |
| |
| size_t 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<cmGeneratorExpressionEvaluator*> &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. |
| TextContent *textContent = |
| static_cast<TextContent*>(*(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(0 && "Got unexpected syntax token."); |
| } |
| assert(this->it != this->Tokens.end()); |
| ++this->it; |
| return; |
| } |
| assert(0 && "Unhandled token in generator expression."); |
| } |