tags:

views:

555

answers:

8

Hi all, using grep, vim's grep, or another unix shell command, I'd like to find the functions in a large cpp file that contain a specific word in their body.

In the files that I'm working with the word I'm looking for is on an indented line, the corresponding function header is the first line above the indented line that starts at position 0 and is not a '{'.

For example searching for JOHN_DOE in the following code snippet

int foo ( int arg1 ) 
{
    /// code 
}
void bar ( std::string arg2  )
{
    /// code
    aFunctionCall( JOHN_DOE );
    /// more code
}

should give me

void bar ( std::string arg2  )

The algorithm that I hope to catch in grep/vim/unix shell scripts would probably best use the indentation and formatting assumptions, rather than attempting to parse C/C++.

Thanks for your suggestions.

A: 

Tough call, although as a starting point I would suggest this wonderful VIM Regex Tutorial.

Robert S. Barnes
A: 

You cannot do that reliably with a regular expression, because code is not a regular language. You need a real parser for the language in question.

Svante
A: 

u can use grep -r -n -H JOHN_DOE * it will look for "JOHN_DOE" in the files recursively starting from the current directory

you can use the following code to practically find the function which contains the text expression:

    public void findFunction(File file, String expression) {
    Reader r = null;
    try {
        r = new FileReader(file);
    } catch (FileNotFoundException ex) {
        ex.printStackTrace();
    }
    BufferedReader br = new BufferedReader(r);

    String match = "";
    String lineWithNameOfFunction = "";

    Boolean matchFound = false;

    try {
        while(br.read() > 0) {
            match = br.readLine();
            if((match.endsWith(") {")) ||
                    (match.endsWith("){")) ||
                    (match.endsWith("()")) ||
                    (match.endsWith(")")) ||
                    (match.endsWith("( )"))) {
                // this here is because i guessed that method will start
                // at the 0
                if((match.charAt(0)!=' ') && !(match.startsWith("\t"))) {
                    lineWithNameOfFunction = match;                        
                }
            }

            if(match.contains(expression)) {
                matchFound = true;
                break;
            }
        }
        if(matchFound)
            System.out.println(lineWithNameOfFunction);
        else 
            System.out.println("No matching function found");
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

i wrote this in JAVA, tested it and works like a charm. has few drawbacks though, but for starters it's fine. didn't add support for multiple functions containing same expression and maybe some other things. try it.

darko.topolsek
So where does the function name came from?
Rob Kennedy
Yes, should come from the code above, but my grep command hasn't been able to get the name of the function.
darko.topolsek
A: 

Like Robert said Regex will help. In command mode start a regex search by typing the "/" character followed by your regex.

Ctags[1] may also be of use to you. It can generate a tag file for a project. This tag file allows a user to jump directly from a function call to it's definition even if it's in another file using "CTRL+]".

J.J.
A: 
ldigas
+2  A: 

As far as I know, this can't be done. Here's why:

First, you have to search across lines. No problem, in vim adding a _ to a character class tells it to include new lines. so {_.*} would match everything between those brackets across multiple lines.

So now you need to match whatever the pattern is for a function header(brittle even if you get it to work), then , and here's the problem, whatever lines are between it and your search string, and finally match your search string. So you might have a regex like

/^\(void \+\a\+ *(.*)\)\_.*JOHN_DOE

But what happens is the first time vim finds a function header, it starts matching. It then matches every character until it finds JOHN_DOE. Which includes all the function headers in the file.

So the problem is that, as far as I know, there's no way to tell vim to match every character except for this regex pattern. And even if there was, a regex is not the tool for this job. It's like opening a beer with a hammer. What we should do is write a simple script that gives you this info, and I have.

fun! FindMyFunction(searchPattern, funcPattern)
  call search(a:searchPattern)
  let lineNumber = line(".")
  let lineNumber = lineNumber - 1
  "call setpos(".", [0,  lineNumber, 0, 0])

  let lineString = getline(lineNumber)
  while lineString !~ a:funcPattern
    let lineNumber = lineNumber - 1
    if lineNumber < 0
      echo "Function not found :/"
    endif
    let lineString = getline(lineNumber)
  endwhile

  echo lineString

endfunction

That should give you the result you want and it's way easier to share, debug, and repurpose than a regular expression spit from the mouth of Cthulhu himself.

Whaledawg
A: 

Arggh! I admit this is a bit over the top:

A little program to filter stdin, strip comments, and put function bodies on the same line. It'll get fooled by namespaces and function definitions inside class declarations, besides other things. But it might be a good start:

#include <stdio.h>
#include <assert.h>

int main() {
    enum {
     NORMAL,
     LINE_COMMENT,
     MULTI_COMMENT,
     IN_STRING,
    } state = NORMAL;
    unsigned depth = 0;
    for(char c=getchar(),prev=0; !feof(stdin); prev=c,c=getchar()) {
     switch(state) {
     case NORMAL:
      if('/'==c && '/'==prev)
       state = LINE_COMMENT;
      else if('*'==c && '/'==prev)
       state = MULTI_COMMENT;
      else if('#'==c)
       state = LINE_COMMENT;
      else if('\"'==c) {
       state = IN_STRING;
       putchar(c);
      } else {
       if(('}'==c && !--depth) || (';'==c && !depth)) {
        putchar(c);
        putchar('\n');
       } else {
        if('{'==c)
         depth++;
        else if('/'==prev && NORMAL==state)
         putchar(prev);
        else if('\t'==c)
         c = ' ';
        if(' '==c && ' '!=prev)
         putchar(c);
        else if(' '<c && '/'!=c)
         putchar(c);
       }
      }
      break;
     case LINE_COMMENT:
      if(' '>c)
       state = NORMAL;
      break;
     case MULTI_COMMENT:
      if('/'==c && '*'==prev) {
       c = '\0';
       state = NORMAL;
      }
      break;
     case IN_STRING:
      if('\"'==c && '\\'!=prev)
       state = NORMAL;
      putchar(c);
      break;
     default:
      assert(!"bug");
     }
    }
    putchar('\n');
    return 0;
}

Its c++, so just it in a file, compile it to a file named 'stripper', and then:

cat my_source.cpp | ./stripper | grep JOHN_DOE

So consider the input:

int foo ( int arg1 ) 
{
    /// code 
}
void bar ( std::string arg2  )
{
    /// code
    aFunctionCall( JOHN_DOE );
    /// more code
}

The output of "cat example.cpp | ./stripper" is:

int foo ( int arg1 ) { }
void bar ( std::string arg2 ){  aFunctionCall( JOHN_DOE ); }

The output of "cat example.cpp | ./stripper | grep JOHN_DOE" is:

void bar ( std::string arg2 ){  aFunctionCall( JOHN_DOE ); }

The job of finding the function name (guess its the last identifier to precede a "(") is left as an exercise to the reader.

Will
+2  A: 
Pev
Don't worry, it'll take a few downvotes to get rid of my upvote for the suggestion. Admittedly I prefer Whaledawg vimscript because it matches the question pretty much exactly, whereas this requires the use of additional software.
Andy