diff --git a/testtokenize.cpp b/testtokenize.cpp index 7df0802c4..12486588f 100644 --- a/testtokenize.cpp +++ b/testtokenize.cpp @@ -45,8 +45,10 @@ private: TEST_CASE( dupfuncname ); TEST_CASE( const_and_volatile_functions ); - - TEST_CASE( numeric_true_condition ); + + TEST_CASE( numeric_true_condition ); + + TEST_CASE( multi_compare ); } @@ -188,7 +190,7 @@ private: } } - + void numeric_true_condition() { const char code[] = "void f()\n" @@ -207,6 +209,23 @@ private: for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next) ostr << " " << tok->str(); ASSERT_EQUALS( std::string(" void f ( ) { if ( true ) ; }"), ostr.str() ); + } + + void multi_compare() + { + // Test for found + ASSERT_EQUALS( TOKEN::multiCompare( "one|two", "one" ), 1 ); + ASSERT_EQUALS( TOKEN::multiCompare( "one|two", "two" ), 1 ); + ASSERT_EQUALS( TOKEN::multiCompare( "verybig|two|", "two" ), 1 ); + + // Test for empty string found + ASSERT_EQUALS( TOKEN::multiCompare( "|one|two", "notfound" ), 0 ); + ASSERT_EQUALS( TOKEN::multiCompare( "one||two", "notfound" ), 0 ); + ASSERT_EQUALS( TOKEN::multiCompare( "one|two|", "notfound" ), 0 ); + + // Test for not found + ASSERT_EQUALS( TOKEN::multiCompare( "one|two", "notfound" ), -1 ); + ASSERT_EQUALS( TOKEN::multiCompare( "verybig|two", "s" ), -1 ); } }; diff --git a/token.cpp b/token.cpp index ffb3159c5..522b0cf49 100644 --- a/token.cpp +++ b/token.cpp @@ -51,7 +51,7 @@ void TOKEN::setstr( const char s[] ) _cstr = _strdup(s); #endif _isName = bool(_str[0]=='_' || isalpha(_str[0])); - _isNumber = bool(isdigit(_str[0]) != 0); + _isNumber = bool(isdigit(_str[0]) != 0); } void TOKEN::combineWithNext(const char str1[], const char str2[]) @@ -89,6 +89,37 @@ const char *TOKEN::strAt(int index) const const TOKEN *tok = this->tokAt(index); return tok ? tok->_cstr : ""; } + +int TOKEN::multiCompare( const char *needle, const char *haystack ) +{ + bool emptyStringFound = false; + bool findNextOr = false; + for( ; *needle; ++needle ) + { + if( *needle == '|' ) + { + if( findNextOr ) + findNextOr = false; + else + emptyStringFound = true; + + continue; + } + + // If this part of needle equals to the haystack + else if( strncmp( haystack, needle, strlen( haystack ) ) == 0 ) + return 1; + else + findNextOr = true; + } + + // If empty string was found or if last character in needle was '|' + if( emptyStringFound || findNextOr == false ) + return 0; + + return -1; +} + bool TOKEN::Match(const TOKEN *tok, const char pattern[], const char *varname1[], const char *varname2[]) { @@ -122,6 +153,12 @@ bool TOKEN::Match(const TOKEN *tok, const char pattern[], const char *varname1[] { if (!tok->isName()) return false; + } + + // Accept any token + else if (strcmp(str,"%any%")==0 ) + { + } // Variable name.. @@ -170,6 +207,22 @@ bool TOKEN::Match(const TOKEN *tok, const char pattern[], const char *varname1[] if ( strchr( str + 1, tok->_str[0] ) == 0 ) return false; } + + // Parse multi options, such as void|int|char (accept token which is one of these 3) + else if ( strchr(str, '|') && strlen( str ) > 2 ) + { + int res = multiCompare( str, tok->_cstr ); + if( res == 0 ) + { + // Empty alternative matches, use the same token on next round + continue; + } + else if( res == 2 ) + { + // No match + return false; + } + } else if (str != tok->_str) return false; diff --git a/token.h b/token.h index cf2f9aa43..c099bd1ce 100644 --- a/token.h +++ b/token.h @@ -30,7 +30,7 @@ public: const std::string &str() const { return _str; } - + const char *aaaa() const { return _cstr; } @@ -39,7 +39,7 @@ public: char aaaa1() const { return _cstr[1]; } - + /** * Combine two tokens that belong to each other. @@ -61,13 +61,56 @@ public: const char *strAt(int index) const; + /** + * Match given token (or list of tokens) to a pattern list. + * + * Possible patterns + * "%any%" any token + * "%var%" any token which is a name or type e.g. "hello" or "int" + * "%num%" Any numeric token, e.g. "23" + * "%str%" Any token starting with "-character (C-string). + * "[abc]" Any of the characters 'a' or 'b' or 'c' + * "int|void|char" Any of the strings, int, void or char + * "int|void|char|" Any of the strings, int, void or char or empty string + * "someRandomText" If token contains "someRandomText". + * + * The patterns can be also combined to compare to multiple tokens at once + * by separating tokens with a space, e.g. + * ") const|void {" will return true if first token is ')' next token is either + * "const" or "void" and token after that is '{'. If even one of the tokens does not + * match its pattern, false is returned. + * + * @param tok List of tokens to be compared to the pattern + * @param pattern The pattern where tokens are compared, e.g. "const" + * or ") const|volatile| {". + * @param varname1 Used with pattern "%var1%" and "%var2%" + * @param varname2 Used with pattern "%var1%" and "%var2%" + * @return true if given token matches with given pattern + * false if given token does not match with given pattern + */ static bool Match(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0); + bool isName() const; bool isNumber() const; bool isStandardType() const; static const TOKEN *findmatch(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0); static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]); + /** + * Needle is build from multiple alternatives. If one of + * them is equal to haystack, return value is 1. If there + * are no matches, but one alternative to needle is empty + * string, return value is 0. If needle was not found, return + * value is -1. + * + * @param needle e.g. "one|two" or "|one|two" + * @param haystack e.g. "one", "two" or "invalid" + * @return 1 if needle is found from the haystack + * 0 if needle was empty string + * -1 if needle was not found + */ + static int multiCompare( const char *needle, const char *haystack ); + unsigned int FileIndex; unsigned int linenr; TOKEN *next; diff --git a/tokenize.cpp b/tokenize.cpp index ab305505c..d5120a2dc 100644 --- a/tokenize.cpp +++ b/tokenize.cpp @@ -1005,7 +1005,7 @@ bool Tokenizer::simplifyConditions() tok->next->setstr((strcmp(tok->next->aaaa(), "0")!=0) ? "true" : "false"); ret = false; } - + // Reduce "(%num% == %num%)" => "(true)"/"(false)" if ( (TOKEN::Match(tok, "&&") || TOKEN::Match(tok, "||") || TOKEN::Match(tok, "(")) && TOKEN::Match(tok->tokAt(1), "%num%") && @@ -1031,13 +1031,13 @@ bool Tokenizer::simplifyConditions() result = (op1 < op2); else cmp = ""; - + if ( ! cmp.empty() ) { tok = tok->next; tok->deleteNext(); tok->deleteNext(); - + tok->setstr( result ? "true" : "false" ); ret = false; } @@ -1116,22 +1116,12 @@ void Tokenizer::fillFunctionList() } else if ( tok2->aaaa0() == ')' ) - { - if ( TOKEN::Match(tok2, ") {") ) - { - _functionList.push_back( tok ); - tok = tok2; - } - else if ( TOKEN::Match(tok2, ") const {") ) + { + if ( TOKEN::Match(tok2, ") const|volatile| {") ) { _functionList.push_back( tok ); tok = tok2; } - else if ( TOKEN::Match(tok2, ") volatile {") ) - { - _functionList.push_back( tok ); - tok = tok2; - } else { tok = tok2;