diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 517348c3f..0baf1427d 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -21,8 +21,10 @@ #include #include +//========================================================================= bool cmIfFunctionBlocker:: -IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, +IsFunctionBlocked(const cmListFileFunction& lff, + cmMakefile &mf, cmExecutionStatus &inStatus) { // Prevent recusion and don't let this blocker block its own @@ -140,6 +142,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf, return true; } +//========================================================================= bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, cmMakefile&) { @@ -157,8 +160,8 @@ bool cmIfFunctionBlocker::ShouldRemove(const cmListFileFunction& lff, return false; } -void cmIfFunctionBlocker:: -ScopeEnded(cmMakefile &mf) +//========================================================================= +void cmIfFunctionBlocker::ScopeEnded(cmMakefile &mf) { std::string errmsg = "The end of a CMakeLists file was reached with an " "IF statement that was not closed properly.\nWithin the directory: "; @@ -175,6 +178,7 @@ ScopeEnded(cmMakefile &mf) cmSystemTools::Message(errmsg.c_str(), "Warning"); } +//========================================================================= bool cmIfCommand ::InvokeInitialPass(const std::vector& args, cmExecutionStatus &) @@ -221,6 +225,7 @@ bool cmIfCommand namespace { +//========================================================================= void IncrementArguments(std::list &newArgs, std::list::iterator &argP1, std::list::iterator &argP2) @@ -235,60 +240,140 @@ namespace } } } -} + //========================================================================= + // helper function to reduce code duplication + void HandlePredicate(bool value, int &reducible, + std::list::iterator &arg, + std::list &newArgs, + std::list::iterator &argP1, + std::list::iterator &argP2) + { + if(value) + { + *arg = "1"; + } + else + { + *arg = "0"; + } + newArgs.erase(argP1); + argP1 = arg; + IncrementArguments(newArgs,argP1,argP2); + reducible = 1; + } -// order of operations, -// IS_DIRECTORY EXISTS COMMAND DEFINED -// MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL -// AND OR -// -// There is an issue on whether the arguments should be values of references, -// for example IF (FOO AND BAR) should that compare the strings FOO and BAR -// or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY -// EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can -// take numeric values or variable names. STRLESS and STRGREATER take -// variable names but if the variable name is not found it will use the name -// directly. AND OR take variables or the values 0 or 1. + //========================================================================= + // helper function to reduce code duplication + void HandleBinaryOp(bool value, int &reducible, + std::list::iterator &arg, + std::list &newArgs, + std::list::iterator &argP1, + std::list::iterator &argP2) + { + if(value) + { + *arg = "1"; + } + else + { + *arg = "0"; + } + newArgs.erase(argP2); + newArgs.erase(argP1); + argP1 = arg; + IncrementArguments(newArgs,argP1,argP2); + reducible = 1; + } - -bool cmIfCommand::IsTrue(const std::vector &args, - char **errorString, cmMakefile *makefile) -{ - // check for the different signatures - const char *def; - const char *def2; - const char* msg = "Unknown arguments specified"; - *errorString = new char[strlen(msg) + 1]; - strcpy(*errorString, msg); - - // handle empty invocation - if (args.size() < 1) - { - delete [] *errorString; - *errorString = 0; - return false; - } - - // store the reduced args in this vector - std::list newArgs; + //========================================================================= + // level 0 processes parenthetical expressions + bool HandleLevel0(std::list &newArgs, + cmMakefile *makefile, + char **errorString) + { int reducible; - unsigned int i; - - // copy to the list structure - for(i = 0; i < args.size(); ++i) - { - newArgs.push_back(args[i]); - } - std::list::iterator argP1; - std::list::iterator argP2; - - // now loop through the arguments and see if we can reduce any of them - // we do this multiple times. Once for each level of precedence do { reducible = 0; std::list::iterator arg = newArgs.begin(); + while (arg != newArgs.end()) + { + if (*arg == "(") + { + // search for the closing paren for this opening one + std::list::iterator argClose; + argClose = arg; + argClose++; + unsigned int depth = 1; + while (argClose != newArgs.end() && depth) + { + if (*argClose == "(") + { + depth++; + } + if (*argClose == ")") + { + depth--; + } + argClose++; + } + if (depth) + { + cmOStringStream error; + error << "mismatched parenthesis in condition"; + delete [] *errorString; + *errorString = new char[error.str().size() + 1]; + strcpy(*errorString, error.str().c_str()); + return false; + } + // store the reduced args in this vector + std::vector newArgs2; + + // copy to the list structure + std::list::iterator argP1 = arg; + argP1++; + for(; argP1 != argClose; argP1++) + { + newArgs2.push_back(*argP1); + } + newArgs2.pop_back(); + // now recursively invoke IsTrue to handle the values inside the parenthetical expression + bool value = + cmIfCommand::IsTrue(newArgs2, errorString, makefile); + if(value) + { + *arg = "1"; + } + else + { + *arg = "0"; + } + argP1 = arg; + argP1++; + // remove the now evaluated parenthetical expression + newArgs.erase(argP1,argClose); + } + ++arg; + } + } + while (reducible); + return true; + } + + //========================================================================= + // level one handles most predicates except for NOT + bool HandleLevel1(std::list &newArgs, + cmMakefile *makefile, + char **) + { + int reducible; + do + { + reducible = 0; + std::list::iterator arg = newArgs.begin(); + std::list::iterator argP1; + std::list::iterator argP2; while (arg != newArgs.end()) { argP1 = arg; @@ -296,83 +381,38 @@ bool cmIfCommand::IsTrue(const std::vector &args, // does a file exist if (*arg == "EXISTS" && argP1 != newArgs.end()) { - if(cmSystemTools::FileExists((argP1)->c_str())) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + cmSystemTools::FileExists((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // does a directory with this name exist if (*arg == "IS_DIRECTORY" && argP1 != newArgs.end()) { - if(cmSystemTools::FileIsDirectory((argP1)->c_str())) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + cmSystemTools::FileIsDirectory((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // is the given path an absolute path ? if (*arg == "IS_ABSOLUTE" && argP1 != newArgs.end()) { - if(cmSystemTools::FileIsFullPath((argP1)->c_str())) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + cmSystemTools::FileIsFullPath((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // does a command exist if (*arg == "COMMAND" && argP1 != newArgs.end()) { - if(makefile->CommandExists((argP1)->c_str())) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + makefile->CommandExists((argP1)->c_str()), + reducible, arg, newArgs, argP1, argP2); } // does a policy exist if (*arg == "POLICY" && argP1 != newArgs.end()) { cmPolicies::PolicyID pid; - if(makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid)) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate( + makefile->GetPolicies()->GetPolicyID((argP1)->c_str(), pid), + reducible, arg, newArgs, argP1, argP2); } // is a variable defined if (*arg == "DEFINED" && argP1 != newArgs.end()) @@ -389,32 +429,30 @@ bool cmIfCommand::IsTrue(const std::vector &args, { bdef = makefile->IsDefinitionSet((argP1)->c_str()); } - if(bdef) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2); } ++arg; } } while (reducible); + return true; + } - - // now loop through the arguments and see if we can reduce any of them - // we do this multiple times. Once for each level of precedence + //========================================================================= + // level two handles most binary operations except for AND OR + bool HandleLevel2(std::list &newArgs, + cmMakefile *makefile, + char **errorString) + { + int reducible; + const char *def; + const char *def2; do { reducible = 0; std::list::iterator arg = newArgs.begin(); - + std::list::iterator argP1; + std::list::iterator argP2; while (arg != newArgs.end()) { argP1 = arg; @@ -468,49 +506,26 @@ bool cmIfCommand::IsTrue(const std::vector &args, def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); double lhs; double rhs; + bool result; if(sscanf(def, "%lg", &lhs) != 1 || sscanf(def2, "%lg", &rhs) != 1) { - *arg = "0"; + result = false; } else if (*(argP1) == "LESS") { - if(lhs < rhs) - { - *arg = "1"; - } - else - { - *arg = "0"; - } + result = (lhs < rhs); } else if (*(argP1) == "GREATER") { - if(lhs > rhs) - { - *arg = "1"; - } - else - { - *arg = "0"; - } + result = (lhs > rhs); } else { - if(lhs == rhs) - { - *arg = "1"; - } - else - { - *arg = "0"; - } + result = (lhs == rhs); } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp(result, + reducible, arg, newArgs, argP1, argP2); } if (argP1 != newArgs.end() && argP2 != newArgs.end() && @@ -521,7 +536,7 @@ bool cmIfCommand::IsTrue(const std::vector &args, def = cmIfCommand::GetVariableOrString(arg->c_str(), makefile); def2 = cmIfCommand::GetVariableOrString((argP2)->c_str(), makefile); int val = strcmp(def,def2); - int result; + bool result; if (*(argP1) == "STRLESS") { result = (val < 0); @@ -534,19 +549,8 @@ bool cmIfCommand::IsTrue(const std::vector &args, { result = (val == 0); } - if(result) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp(result, + reducible, arg, newArgs, argP1, argP2); } // is file A newer than file B @@ -557,33 +561,32 @@ bool cmIfCommand::IsTrue(const std::vector &args, bool success=cmSystemTools::FileTimeCompare(arg->c_str(), (argP2)->c_str(), &fileIsNewer); - if(success==false || fileIsNewer==1 || fileIsNewer==0) - { - *arg = "1"; - } - else - { - *arg = "0"; - } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp( + (success==false || fileIsNewer==1 || fileIsNewer==0), + reducible, arg, newArgs, argP1, argP2); } ++arg; } } while (reducible); + return true; + } - - // now loop through the arguments and see if we can reduce any of them - // we do this multiple times. Once for each level of precedence + //========================================================================= + // level 3 handles NOT + bool HandleLevel3(std::list &newArgs, + cmMakefile *makefile, + char **) + { + int reducible; + const char *def; do { reducible = 0; std::list::iterator arg = newArgs.begin(); + std::list::iterator argP1; + std::list::iterator argP2; while (arg != newArgs.end()) { argP1 = arg; @@ -591,30 +594,31 @@ bool cmIfCommand::IsTrue(const std::vector &args, if (argP1 != newArgs.end() && *arg == "NOT") { def = cmIfCommand::GetVariableOrNumber((argP1)->c_str(), makefile); - if(!cmSystemTools::IsOff(def)) - { - *arg = "0"; - } - else - { - *arg = "1"; - } - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandlePredicate(cmSystemTools::IsOff(def), + reducible, arg, newArgs, argP1, argP2); } ++arg; } } while (reducible); + return true; + } - // now loop through the arguments and see if we can reduce any of them - // we do this multiple times. Once for each level of precedence + //========================================================================= + // level 4 handles AND OR + bool HandleLevel4(std::list &newArgs, + cmMakefile *makefile, + char **) + { + int reducible; + const char *def; + const char *def2; do { reducible = 0; std::list::iterator arg = newArgs.begin(); + std::list::iterator argP1; + std::list::iterator argP2; while (arg != newArgs.end()) { argP1 = arg; @@ -624,19 +628,9 @@ bool cmIfCommand::IsTrue(const std::vector &args, { def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile); def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile); - if(cmSystemTools::IsOff(def) || cmSystemTools::IsOff(def2)) - { - *arg = "0"; - } - else - { - *arg = "1"; - } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp( + !(cmSystemTools::IsOff(def) || cmSystemTools::IsOff(def2)), + reducible, arg, newArgs, argP1, argP2); } if (argP1 != newArgs.end() && *(argP1) == "OR" && @@ -644,25 +638,84 @@ bool cmIfCommand::IsTrue(const std::vector &args, { def = cmIfCommand::GetVariableOrNumber(arg->c_str(), makefile); def2 = cmIfCommand::GetVariableOrNumber((argP2)->c_str(), makefile); - if(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2)) - { - *arg = "0"; - } - else - { - *arg = "1"; - } - newArgs.erase(argP2); - newArgs.erase(argP1); - argP1 = arg; - IncrementArguments(newArgs,argP1,argP2); - reducible = 1; + HandleBinaryOp( + !(cmSystemTools::IsOff(def) && cmSystemTools::IsOff(def2)), + reducible, arg, newArgs, argP1, argP2); } - ++arg; } } while (reducible); + return true; + } +} + + +//========================================================================= +// order of operations, +// 1. ( ) -- parenthetical groups +// 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates +// 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops +// 4. NOT +// 5. AND OR +// +// There is an issue on whether the arguments should be values of references, +// for example IF (FOO AND BAR) should that compare the strings FOO and BAR +// or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY +// EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can +// take numeric values or variable names. STRLESS and STRGREATER take +// variable names but if the variable name is not found it will use the name +// directly. AND OR take variables or the values 0 or 1. + + +bool cmIfCommand::IsTrue(const std::vector &args, + char **errorString, cmMakefile *makefile) +{ + // check for the different signatures + const char *def; + const char* msg = "Unknown arguments specified"; + *errorString = new char[strlen(msg) + 1]; + strcpy(*errorString, msg); + + // handle empty invocation + if (args.size() < 1) + { + delete [] *errorString; + *errorString = 0; + return false; + } + + // store the reduced args in this vector + std::list newArgs; + + // copy to the list structure + for(unsigned int i = 0; i < args.size(); ++i) + { + newArgs.push_back(args[i]); + } + + // now loop through the arguments and see if we can reduce any of them + // we do this multiple times. Once for each level of precedence + if (!HandleLevel0(newArgs, makefile, errorString)) // parens + { + return false; + } + if (!HandleLevel1(newArgs, makefile, errorString)) //predicates + { + return false; + } + if (!HandleLevel2(newArgs, makefile, errorString)) // binary ops + { + return false; + } + if (!HandleLevel3(newArgs, makefile, errorString)) // NOT + { + return false; + } + if (!HandleLevel4(newArgs, makefile, errorString)) // AND OR + { + return false; + } // now at the end there should only be one argument left if (newArgs.size() == 1) @@ -687,6 +740,7 @@ bool cmIfCommand::IsTrue(const std::vector &args, return true; } +//========================================================================= const char* cmIfCommand::GetVariableOrString(const char* str, const cmMakefile* mf) { @@ -698,6 +752,7 @@ const char* cmIfCommand::GetVariableOrString(const char* str, return def; } +//========================================================================= const char* cmIfCommand::GetVariableOrNumber(const char* str, const cmMakefile* mf) { diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index a11ab9836..93d32908c 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -242,11 +242,26 @@ bool cmListFileCacheParseFunction(cmListFileLexer* lexer, // Arguments. unsigned long lastLine = cmListFileLexer_GetCurrentLine(lexer); + unsigned long parenDepth = 0; while((token = cmListFileLexer_Scan(lexer))) { - if(token->type == cmListFileLexer_Token_ParenRight) + if(token->type == cmListFileLexer_Token_ParenLeft) { - return true; + parenDepth++; + cmListFileArgument a("(", + false, filename, token->line); + function.Arguments.push_back(a); + } + else if(token->type == cmListFileLexer_Token_ParenRight) + { + if (parenDepth == 0) + { + return true; + } + parenDepth--; + cmListFileArgument a(")", + false, filename, token->line); + function.Arguments.push_back(a); } else if(token->type == cmListFileLexer_Token_Identifier || token->type == cmListFileLexer_Token_ArgumentUnquoted) diff --git a/Tests/Complex/CMakeLists.txt b/Tests/Complex/CMakeLists.txt index 1814528cd..c3f94b72a 100644 --- a/Tests/Complex/CMakeLists.txt +++ b/Tests/Complex/CMakeLists.txt @@ -336,6 +336,12 @@ if (NOT ELSEIF_RESULT EQUAL 2) set (ELSEIF_RESULT 0) endif (NOT ELSEIF_RESULT EQUAL 2) +# test handling of parenthetical groups in conditionals +if (2 GREATER 1 AND (4 LESS 3 OR 5 LESS 6) AND NOT (7 GREATER 8)) + set(CONDITIONAL_PARENTHESES 1) +endif() + + # # Configure file # (plug vars to #define so that they can be tested) diff --git a/Tests/Complex/Executable/complex.cxx b/Tests/Complex/Executable/complex.cxx index 95e77b640..0ecd8fe2e 100644 --- a/Tests/Complex/Executable/complex.cxx +++ b/Tests/Complex/Executable/complex.cxx @@ -369,6 +369,12 @@ int main() cmFailed("ELSEIF did not work"); #endif +#ifdef CONDITIONAL_PARENTHESES + cmPassed("CONDITIONAL_PARENTHESES did work"); +#else + cmFailed("CONDITIONAL_PARENTHESES did not work"); +#endif + if(file2() != 1) { cmFailed("Call to file2 function from library failed."); diff --git a/Tests/Complex/cmTestConfigure.h.in b/Tests/Complex/cmTestConfigure.h.in index 80342036a..d7952da1e 100644 --- a/Tests/Complex/cmTestConfigure.h.in +++ b/Tests/Complex/cmTestConfigure.h.in @@ -81,3 +81,7 @@ // test elseif #cmakedefine ELSEIF_RESULT + +// test parenthesis in conditionals +#cmakedefine CONDITIONAL_PARENTHESES + diff --git a/Tests/ComplexOneConfig/CMakeLists.txt b/Tests/ComplexOneConfig/CMakeLists.txt index 1814528cd..c3f94b72a 100644 --- a/Tests/ComplexOneConfig/CMakeLists.txt +++ b/Tests/ComplexOneConfig/CMakeLists.txt @@ -336,6 +336,12 @@ if (NOT ELSEIF_RESULT EQUAL 2) set (ELSEIF_RESULT 0) endif (NOT ELSEIF_RESULT EQUAL 2) +# test handling of parenthetical groups in conditionals +if (2 GREATER 1 AND (4 LESS 3 OR 5 LESS 6) AND NOT (7 GREATER 8)) + set(CONDITIONAL_PARENTHESES 1) +endif() + + # # Configure file # (plug vars to #define so that they can be tested) diff --git a/Tests/ComplexOneConfig/Executable/complex.cxx b/Tests/ComplexOneConfig/Executable/complex.cxx index 95e77b640..0ecd8fe2e 100644 --- a/Tests/ComplexOneConfig/Executable/complex.cxx +++ b/Tests/ComplexOneConfig/Executable/complex.cxx @@ -369,6 +369,12 @@ int main() cmFailed("ELSEIF did not work"); #endif +#ifdef CONDITIONAL_PARENTHESES + cmPassed("CONDITIONAL_PARENTHESES did work"); +#else + cmFailed("CONDITIONAL_PARENTHESES did not work"); +#endif + if(file2() != 1) { cmFailed("Call to file2 function from library failed."); diff --git a/Tests/ComplexOneConfig/cmTestConfigure.h.in b/Tests/ComplexOneConfig/cmTestConfigure.h.in index 80342036a..d7952da1e 100644 --- a/Tests/ComplexOneConfig/cmTestConfigure.h.in +++ b/Tests/ComplexOneConfig/cmTestConfigure.h.in @@ -81,3 +81,7 @@ // test elseif #cmakedefine ELSEIF_RESULT + +// test parenthesis in conditionals +#cmakedefine CONDITIONAL_PARENTHESES + diff --git a/Tests/ComplexRelativePaths/CMakeLists.txt b/Tests/ComplexRelativePaths/CMakeLists.txt index 1814528cd..c3f94b72a 100644 --- a/Tests/ComplexRelativePaths/CMakeLists.txt +++ b/Tests/ComplexRelativePaths/CMakeLists.txt @@ -336,6 +336,12 @@ if (NOT ELSEIF_RESULT EQUAL 2) set (ELSEIF_RESULT 0) endif (NOT ELSEIF_RESULT EQUAL 2) +# test handling of parenthetical groups in conditionals +if (2 GREATER 1 AND (4 LESS 3 OR 5 LESS 6) AND NOT (7 GREATER 8)) + set(CONDITIONAL_PARENTHESES 1) +endif() + + # # Configure file # (plug vars to #define so that they can be tested) diff --git a/Tests/ComplexRelativePaths/Executable/complex.cxx b/Tests/ComplexRelativePaths/Executable/complex.cxx index 95e77b640..0ecd8fe2e 100644 --- a/Tests/ComplexRelativePaths/Executable/complex.cxx +++ b/Tests/ComplexRelativePaths/Executable/complex.cxx @@ -369,6 +369,12 @@ int main() cmFailed("ELSEIF did not work"); #endif +#ifdef CONDITIONAL_PARENTHESES + cmPassed("CONDITIONAL_PARENTHESES did work"); +#else + cmFailed("CONDITIONAL_PARENTHESES did not work"); +#endif + if(file2() != 1) { cmFailed("Call to file2 function from library failed."); diff --git a/Tests/ComplexRelativePaths/cmTestConfigure.h.in b/Tests/ComplexRelativePaths/cmTestConfigure.h.in index 80342036a..d7952da1e 100644 --- a/Tests/ComplexRelativePaths/cmTestConfigure.h.in +++ b/Tests/ComplexRelativePaths/cmTestConfigure.h.in @@ -81,3 +81,7 @@ // test elseif #cmakedefine ELSEIF_RESULT + +// test parenthesis in conditionals +#cmakedefine CONDITIONAL_PARENTHESES +