import sys

from clang.cindex import Config
from clang.cindex import CursorKind
from clang.cindex import Index
from clang.cindex import TranslationUnit

# Set these according to the location and version of libclang on your system
LIBCLANG_PATH = '/usr/lib/llvm-3.4/lib'
LIBCLANG_NAME = 'libclang-3.4.so'


def display_node(node):
    """ A helper function to pretty print the particulars of the passed in
    Cursor object
    """
    start_line = node.extent.start.line
    start_col = node.extent.start.column
    end_line = node.extent.end.line
    end_col = node.extent.end.column
    print '{:s} ({:d}:{:d} - {:d}:{:d})'.format(node.displayname, 
                                                start_line, start_col,
                                                end_line, end_col)

def find_function_calls(node):
    """ A recursive function call to find all function calls that are children 
    of the passed in Cursor object
    """
    results = []
    for child_node in node.get_children():
        if child_node.kind == CursorKind.CALL_EXPR:
            results.append(child_node)
        else:
            results.extend(find_function_calls(child_node))
    return results   

def main():
    """ This is a very simple utility that uses the Clang compiler frontend
    to parse a C source file and return a list of function declarations within
    that file along with the lines on which each declaration starts and ends.
    """
    
    if len(sys.argv) < 2:
        print 'ERROR: not enough arguments provided\n'
        print 'USAGE: python ddc_preproc.py <file.c>'
        return -1
    
    # Configure the library (not sure why this has to be done manually)
    Config.set_library_path(LIBCLANG_PATH)
    Config.set_library_file(LIBCLANG_NAME)
    
    # Build the TU from the passed file argument
    source_file = sys.argv[1]
    tu = TranslationUnit.from_source(source_file)

    # Loop through all of the children nodes and extract function definitions
    # along with their start and end lines from the specified source file
    print 'Function declarations:'
    print '-' * 80
    main_func = None
    for child in tu.cursor.get_children():
        if str(child.location.file) == source_file and \
           child.kind == CursorKind.FUNCTION_DECL:
            # Store a reference to the main function 
            if child.spelling == 'main':
                main_func = child
            display_node(child)    
    print '\n'
    
    # Find all function calls originating from the main function 
    print 'Function calls:'
    print '-' * 80
    for child in find_function_calls(main_func):
        display_node(child)
    

if __name__ == '__main__':
    sys.exit(main())
