#!/bin/python

import re

def tokens(s):
    patterns = [
        ('newline', '\n'),
        ('cpp', '#(\\\\\n|.)*\n'),
        ('comment', '/\*[\w\W]*?\*/'),
        ('comment', '//.*'),
        ('lpar', '\('),
        ('rpar', '\)'),
        ('lbra', '\{'),
        ('rbra', '\}'),
        ('str', r'"(\\"|\\[ntr]|\\\d\d\d|\\x[0-9a-f][0-9a-f]|\\\\|[^\\"])*"'),
        ('char', "'[^']+'"),
        ('ident', '\w+'),
        ('int', '\d+'),
        ('float', '\d+\.\d+'),
        ('space', '\s+'),
        ('op', '&&|\|\||==|->|[?:=!|<>+*/&^~,%.[\]-]'),
        ('semi', ';'),
        ]

    while s:
        match = None

        for name, expr in patterns:
            match = re.match(expr, s)

            if match:
                break

        if match:
            yield (name, match.group(0))
            s = s[len(match.group(0)):]
        else:
            if len(s) > 20:
                raise RuntimeError("no match for '%s...'" % repr(s)[1:21])
            else:
                raise RuntimeError("no match for %r" % s)

def graph(f):
    cur = None
    calls = {}
    stop = ('if', 'for', 'while', 'switch', 'sizeof', 'return')
    in_comment = False
    state = []
    ts = tokens(f.read())

    while True:
        line = []

        for kind, token in ts:
            if kind in ('comment', 'cpp'):
                continue

            line.append((kind, token))

            if kind == 'newline':
                break

        if line == []:
            # eof
            break

        if line[0][0] == 'ident':
            mode = 'decl'
        else:
            mode = 'call'

        line = [(k, t) for (k, t) in line if k != 'space']

        if mode == 'decl':
            for i in xrange(len(line) - 1):
                if line[i][0] == 'ident' and line[i+1][0] == 'lpar':
                    cur = line[i][1]
                    calls[cur] = []
        else:
            for i in xrange(len(line) - 1):
                if (line[i][0] == 'ident' and
                    line[i][1] not in stop and
                    line[i+1][0] == 'lpar' and
                    cur is not None):
                    calls[cur].append(line[i][1])

    return calls

def close(g):
    for a in g.keys():
        g[a] = [b for b in g[a] if b in g]

def digraph(g):
    print 'digraph {'

    for a in sorted(g.keys()):
        for b in sorted(g[a]):
            print '  %s -> %s;' % (a, b)

    print '}'

if __name__ == '__main__':
    import sys

    calls = graph(file(sys.argv[1]))
    close(calls)
    digraph(calls)

