如何使用 pycparser 删除 AST 节点?

让我们从考虑这个片段开始:


import sys


from pycparser import c_parser, c_ast, c_generator



text = r"""

void main() {

    foo(1,3);


    foo1(4);


    x = 1;


     foo2(4,


        10,



        3);

    foo3(

        "xxx"


    );

}

"""



class FuncCallVisitor(c_ast.NodeVisitor):


    def visit_FuncCall(self, node):

        print('%s called at %s' % (node.name.name, node.name.coord))


        if node.args:

            self.visit(node.args)



class RemoveFuncCalls(c_generator.CGenerator):


    def visit_FuncCall(self, n):

        # fref = self._parenthesize_unless_simple(n.name)

        # return fref + '(' + self.visit(n.args) + ')'

        return ""



if __name__ == '__main__':

    parser = c_parser.CParser()

    ast = parser.parse(text)

    v = FuncCallVisitor()

    v.visit(ast)

    print('-' * 80)


    ast.show(showcoord=True)

    generator = RemoveFuncCalls()


    print('-' * 80)

    print(generator.visit(ast))

上面的输出将是:


void main()

{

  ;

  ;

  x = 1;

  ;

  ;

}

但我希望它变成这样:


void main()

{

  x = 1;

}

所以我的问题是,使用 pycparser 从 AST 中删除节点/子树的规范/惯用方法是什么?


德玛西亚99
浏览 99回答 1
1回答

青春有我

它看起来像是对类似作用域的结构的c_generator.CGenerator调用_generate_stmt方法,即使它是一个空字符串,它也会(带有缩进)附加到 for 语句的结果。';\n'visit要删除函数调用,我们可以像这样重载它class RemoveFuncCalls(c_generator.CGenerator):&nbsp; &nbsp; def _generate_stmt(self, n, add_indent=False):&nbsp; &nbsp; &nbsp; &nbsp; if isinstance(n, c_ast.FuncCall):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ''&nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return super()._generate_stmt(n, add_indent)接着就,随即void main(){&nbsp; x = 1;}这看起来像你想要的。让我们考虑一个案例if (bar(42, "something"))&nbsp; &nbsp; return;如果我们需要它成为if ()&nbsp; &nbsp; return;然后我们需要添加&nbsp; &nbsp; def visit_FuncCall(self, n):&nbsp; &nbsp; &nbsp; &nbsp; return ''就像在 OP 中一样,因为_generate_stmt不被字段序列化的RemoveFuncCalls.visit_If方法调用。cond走得更远我不知道“使用 pycparser 从 AST 中删除节点/子树的规范/惯用方法”,但我确实知道aststdlib 中的模块——ast.NodeTransformer类pycparser(由于某种原因不存在)。它将允许我们str通过重写私有方法和修改 AST 本身来避免弄乱 AST 的序列化方式from pycparser import c_astclass NodeTransformer(c_ast.NodeVisitor):&nbsp; &nbsp; def generic_visit(self, node):&nbsp; &nbsp; &nbsp; &nbsp; for field, old_value in iter_fields(node):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if isinstance(old_value, list):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_values = []&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for value in old_value:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if isinstance(value, c_ast.Node):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value = self.visit(value)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if value is None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elif not isinstance(value, c_ast.Node):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_values.extend(value)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_values.append(value)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; old_value[:] = new_values&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elif isinstance(old_value, c_ast.Node):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_node = self.visit(old_value)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setattr(node, field, new_node)&nbsp; &nbsp; &nbsp; &nbsp; return nodedef iter_fields(node):&nbsp; &nbsp; # this doesn't look pretty because `pycparser` decided to have structure&nbsp;&nbsp; &nbsp; # for AST node classes different from stdlib ones&nbsp; &nbsp; index = 0&nbsp; &nbsp; children = node.children()&nbsp; &nbsp; while index < len(children):&nbsp; &nbsp; &nbsp; &nbsp; name, child = children[index]&nbsp; &nbsp; &nbsp; &nbsp; try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bracket_index = name.index('[')&nbsp; &nbsp; &nbsp; &nbsp; except ValueError:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yield name, child&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index += 1&nbsp; &nbsp; &nbsp; &nbsp; else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name = name[:bracket_index]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; child = getattr(node, name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index += len(child)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; yield name, child对于我们的例子,它可以简单地被子类化class FuncCallsRemover(NodeTransformer):&nbsp; &nbsp; def visit_FuncCall(self, node):&nbsp; &nbsp; &nbsp; &nbsp; return None并像使用...ast = parser.parse(text)v = FuncCallsRemover()ast = v.visit(ast)&nbsp; # note that `NodeTransformer` returns modified AST instead of `None`之后我们可以使用未修改c_generator.CGenerator的实例并获得相同的结果。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python