来自ANTLR解析树的Python AST



我找到了ANTLRv4 Python3语法,但是它生成了一个解析树,通常有许多无用的节点。

我正在寻找一个已知的包从解析树中获得Python AST。

这样的东西存在吗?

编辑:关于使用Python ast包的澄清:我的项目是在Java中,我需要解析Python文件。

编辑2:这里的"AST"是指http://docs.python.org/2/library/ast.html#abstract-grammar,而"解析树"是指http://docs.python.org/2/reference/grammar.html。

可以从下面开始:

public class AST {
    private final Object payload;
    private final List<AST> children;
    public AST(ParseTree tree) {
        this(null, tree);
    }
    private AST(AST ast, ParseTree tree) {
        this(ast, tree, new ArrayList<AST>());
    }
    private AST(AST parent, ParseTree tree, List<AST> children) {
        this.payload = getPayload(tree);
        this.children = children;
        if (parent == null) {
            walk(tree, this);
        }
        else {
            parent.children.add(this);
        }
    }
    public Object getPayload() {
        return payload;
    }
    public List<AST> getChildren() {
        return new ArrayList<>(children);
    }
    private Object getPayload(ParseTree tree) {
        if (tree.getChildCount() == 0) {
            return tree.getPayload();
        }
        else {
            String ruleName = tree.getClass().getSimpleName().replace("Context", "");
            return Character.toLowerCase(ruleName.charAt(0)) + ruleName.substring(1);
        }
    }
    private static void walk(ParseTree tree, AST ast) {
        if (tree.getChildCount() == 0) {
            new AST(ast, tree);
        }
        else if (tree.getChildCount() == 1) {
            walk(tree.getChild(0), ast);
        }
        else if (tree.getChildCount() > 1) {
            for (int i = 0; i < tree.getChildCount(); i++) {
                AST temp = new AST(ast, tree.getChild(i));
                if (!(temp.payload instanceof Token)) {
                    walk(tree.getChild(i), temp);
                }
            }
        }
    }
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        AST ast = this;
        List<AST> firstStack = new ArrayList<>();
        firstStack.add(ast);
        List<List<AST>> childListStack = new ArrayList<>();
        childListStack.add(firstStack);
        while (!childListStack.isEmpty()) {
            List<AST> childStack = childListStack.get(childListStack.size() - 1);
            if (childStack.isEmpty()) {
                childListStack.remove(childListStack.size() - 1);
            }
            else {
                ast = childStack.remove(0);
                String caption;
                if (ast.payload instanceof Token) {
                    Token token = (Token) ast.payload;
                    caption = String.format("TOKEN[type: %s, text: %s]",
                            token.getType(), token.getText().replace("n", "\n"));
                }
                else {
                    caption = String.valueOf(ast.payload);
                }
                String indent = "";
                for (int i = 0; i < childListStack.size() - 1; i++) {
                    indent += (childListStack.get(i).size() > 0) ? "|  " : "   ";
                }
                builder.append(indent)
                        .append(childStack.isEmpty() ? "'- " : "|- ")
                        .append(caption)
                        .append("n");
                if (ast.children.size() > 0) {
                    List<AST> children = new ArrayList<>();
                    for (int i = 0; i < ast.children.size(); i++) {
                        children.add(ast.children.get(i));
                    }
                    childListStack.add(children);
                }
            }
        }
        return builder.toString();
    }
}

,可用于为输入"f(arg1='1')n"创建AST,如下所示:

public static void main(String[] args) {
    Python3Lexer lexer = new Python3Lexer(new ANTLRInputStream("f(arg1='1')n"));
    Python3Parser parser = new Python3Parser(new CommonTokenStream(lexer));
    ParseTree tree = parser.file_input();
    AST ast = new AST(tree);
    System.out.println(ast);
}

将打印:

<>前"——file_input| -支撑| |- small_stmt| | |-原子| | | '- TOKEN[type: 35, text: f]-预告片| | |- TOKEN[type: 47, text: []]| | |- arglist| | | |-测试| | | | '- TOKEN[type: 35, text: arg1]| | | |- TOKEN[type: 53, text: =]| | | '-测试| | | '- TOKEN[type: 36, text: '1']| | '- TOKEN[type: 48, text:)]| '- TOKEN[type: 34, text: n]'- TOKEN[type: -1, text:]

我意识到这仍然包含您可能不想要的节点,但是您甚至可以添加一组您想要排除的令牌类型。随便砍吧!

这里是一个Gist,包含了上面代码的一个版本,带有适当的import语句和一些javadoc和内联注释。

Eclipse DLTK项目Python子项目在Java中实现了自定义Python AST模型。它是从一个AntlrV3实例构建的,但是从一个AntlrV4解析树进行重构应该不会太难。

Eclipse PyDev项目可能还为python源代码实现了基于java的AST。注意,两个项目中源代码树的布局应该非常相似。

当然,在使用这些源代码的代码之前,您应该检查许可证,只是为了确保。

我找到了一个解决方法:

使用Jythonast(感谢@delnan引导我到那里)。或者,直接在Python代码中完成所需的所有操作,然后将结果返回给Java。

PythonInterpreter interpreter = new PythonInterpreter();
interpreter.exec("import ast");
PyObject o = interpreter.eval(
    "ast.dump(ast.parse('f(arg1=\'1\')', 'filename', 'eval'))" + "n");
System.out.print(o.toString());

输出
Expression(body=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[keyword(arg='arg1', value=Str(s='1'))], starargs=None, kwargs=None))

这并没有严格地回答这个问题,可能并不适用于所有用户,所以我不选择这个答案。

ANTLR4可以生成一个访问者,你可以用它来遍历解析树和构造AST。Python有一个ast包,所以这应该不是问题(如果你正在使用Python)。

我使用ANTLR4在Python 3中编写了一个玩具Python解释器(作为我学习的一部分)。访问者代码位于/tinypy/AST/builder/,因此您可以了解它是如何完成的。

相关内容

  • 没有找到相关文章

最新更新