为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 pycparser 删除 AST 节点?

如何使用 pycparser 删除 AST 节点?

德玛西亚99 2022-07-26 10:23:29
让我们从考虑这个片段开始:import sysfrom pycparser import c_parser, c_ast, c_generatortext = 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 中删除节点/子树的规范/惯用方法是什么?
查看完整描述

1 回答

?
青春有我

TA贡献1784条经验 获得超8个赞

它看起来像是对类似作用域的结构的c_generator.CGenerator调用_generate_stmt方法,即使它是一个空字符串,它也会(带有缩进)附加到 for 语句的结果。';\n'visit


要删除函数调用,我们可以像这样重载它


class RemoveFuncCalls(c_generator.CGenerator):

    def _generate_stmt(self, n, add_indent=False):

        if isinstance(n, c_ast.FuncCall):

            return ''

        else:

            return super()._generate_stmt(n, add_indent)

接着就,随即


void main()

{

  x = 1;

}

这看起来像你想要的。


让我们考虑一个案例


if (bar(42, "something"))

    return;

如果我们需要它成为


if ()

    return;

然后我们需要添加


    def visit_FuncCall(self, n):

        return ''

就像在 OP 中一样,因为_generate_stmt不被字段序列化的RemoveFuncCalls.visit_If方法调用。cond


走得更远

我不知道“使用 pycparser 从 AST 中删除节点/子树的规范/惯用方法”,但我确实知道aststdlib 中的模块——ast.NodeTransformer类pycparser(由于某种原因不存在)。


它将允许我们str通过重写私有方法和修改 AST 本身来避免弄乱 AST 的序列化方式


from pycparser import c_ast


class NodeTransformer(c_ast.NodeVisitor):

    def generic_visit(self, node):

        for field, old_value in iter_fields(node):

            if isinstance(old_value, list):

                new_values = []

                for value in old_value:

                    if isinstance(value, c_ast.Node):

                        value = self.visit(value)

                        if value is None:

                            continue

                        elif not isinstance(value, c_ast.Node):

                            new_values.extend(value)

                            continue

                    new_values.append(value)

                old_value[:] = new_values

            elif isinstance(old_value, c_ast.Node):

                new_node = self.visit(old_value)

                setattr(node, field, new_node)

        return node



def iter_fields(node):

    # this doesn't look pretty because `pycparser` decided to have structure 

    # for AST node classes different from stdlib ones

    index = 0

    children = node.children()

    while index < len(children):

        name, child = children[index]

        try:

            bracket_index = name.index('[')

        except ValueError:

            yield name, child

            index += 1

        else:

            name = name[:bracket_index]

            child = getattr(node, name)

            index += len(child)

            yield name, child

对于我们的例子,它可以简单地被子类化


class FuncCallsRemover(NodeTransformer):

    def visit_FuncCall(self, node):

        return None

并像使用


...

ast = parser.parse(text)

v = FuncCallsRemover()

ast = v.visit(ast)  # note that `NodeTransformer` returns modified AST instead of `None`

之后我们可以使用未修改c_generator.CGenerator的实例并获得相同的结果。


查看完整回答
反对 回复 2022-07-26
  • 1 回答
  • 0 关注
  • 98 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号