pycparser: как получить конец функции в C-файле

Я использую pycparser для разбора файла C. Я хочу получить начало и конец каждого определения функции в C-файле. Но то, что я на самом деле получаю, это только начало определения функций.

memmgr_init at examples/c_files/memmgr.c:46
get_mem_from_pool at examples/c_files/memmgr.c:55

Я хочу получить что-то вроде:

memmgr_init at examples/c_files/memmgr.c: start :46 end : 52

class FuncDefVisitor(c_ast.NodeVisitor):

def visit_FuncDef(self, node):
print('%s at %s' % (node.decl.name, node.decl.coord))

2 ответа

Вы не можете сделать это с pycparser, потому что он не записывает конечную позицию функций при анализе.

Вы можете восстановить тело функции из AST:

from pycparser import c_parser, c_ast, parse_file, c_generator

class FuncDefVisitor(c_ast.NodeVisitor):
def __init__(self, bodies):
    self.bodies = bodies
    self.generator = c_generator.CGenerator()
def visit_FuncDef(self, node):
    self.bodies.append(self.generator.visit(node))

def show_func_defs(filename):
    ast = parse_file(filename, use_cpp=True,
                 cpp_args=r'-Iutils/fake_libc_include')
    bodies = []
    v = FuncDefVisitor(bodies)
    v.visit(ast)
    for body in bodies:
        print(body)

Но это может немного отличаться от исходного и поэтому не может быть использовано для определения того, через сколько строк конец функции находится в начале.

У меня есть быстрое и грязное решение вашей проблемы. Что вам нужно сделать, это получить ближайшую линию от AST. Я не люблю изменять библиотеки, если мне не нужно. Я предполагаю, что вы знакомы с анализом и манипулированием данными. Если нет, я могу добавить больше деталей. Метод parser.parse генерирует объект класса AST. gcc_or_cpp_output - это некоторый промежуточный код, сгенерированный gcc или cpp.

ast = parser.parse(gcc_or_cpp_output,filename)

Функция AST имеет метод show и аргументы по умолчанию. Вам нужно будет установить showcoord True для вашей проблемы.

ast.show(buf=fb,attrnames=True, nodenames=True, showcoord=True)

        buf:
            Open IO buffer into which the Node is printed.

        offset:
            Initial offset (amount of leading spaces)

        attrnames:
            True if you want to see the attribute names in
            name=value pairs. False to only see the values.

        nodenames:
            True if you want to see the actual node names
            within their parents.

        showcoord:
            Do you want the coordinates of each Node to be
            displayed

Затем вам нужно будет изменить значение по умолчанию для buf с sys.stdout на свой собственный буферный класс, чтобы вы могли захватывать аст-граф. Вы также можете пройтись по дереву, но я оставлю решение по обходу дерева на другой день. Я написал простой fake_buffer ниже.

class fake_buffer():
    def __init__(self):
        self.buffer =[]
    def write(self,string):
        self.buffer.append(string)
    def get_buffer(self):
        return self.buffer

Поэтому все, что вам нужно сейчас сделать, это сохранить, передать свой поддельный буфер методу ast.show(), чтобы получить AST.

fb = fake_buffer()
ast.show(buf=fb,attrnames=True, nodenames=True, showcoord=True)

На этом этапе у вас будет список AST. Объявления функции будут находиться внизу. Теперь вам просто нужно разобрать все лишние вещи и получить максимальную координату при отклонении этой функции.

  FuncCall <block_items[12]>:  (at ...blah_path_stuff.../year.c:48)

ABC Always Be Coding

Другие вопросы по тегам