4

ANTLRv4 Python3 grammerを見つけましたが、一般に多くの役に立たないノードを持つ解析ツリーが生成されます。

その解析ツリーから Python AST を取得するための既知のパッケージを探しています。

このようなものは存在しますか?

編集: Pythonastパッケージの使用に関する説明: 私のプロジェクトは Java であり、Python ファイルを解析する必要があります。

EDIT 2:「AST」とはhttp://docs.python.org/2/library/ast.html#abstract-grammarを意味し、「解析ツリー」とはhttp://docs.python.org/2を意味します/reference/grammar.html .

4

4 に答える 4

7

以下は、開始することができます。

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
   |- stmt
   | | |- small_stmt
   | | | | |- アトム
   | | | | | | '- TOKEN[タイプ: 35, テキスト: f]
   | | | | '- トレーラー
   | | | | |- TOKEN[タイプ: 47、テキスト: (]
   | | | | |- 引数リスト
   | | | | | | |- テスト
   | | | | | | | | '- TOKEN[タイプ: 35, テキスト: arg1]
   | | | | | | |- TOKEN[タイプ: 53、テキスト: =]
   | | | | | | '- テスト
   | | | | | | '-トークン[タイプ: 36、テキスト: '1']
   | | | | '- TOKEN[タイプ: 48, テキスト: )]
   | | '- TOKEN[タイプ: 34、テキスト: \n]
   '- TOKEN[タイプ: -1, テキスト: ]

これには不要なノードがまだ含まれていることは承知していますが、除外したい一連のトークン タイプを追加することもできます。お気軽にハックしてください!

これは、適切なインポート ステートメントといくつかの JavaDocs およびインライン コメントを含む上記のコードのバージョンを含むGistです。

于 2014-07-16T19:27:48.620 に答える
0

Eclipse DLTK プロジェクトの Python サブプロジェクトは、Java でカスタム Python AST モデルを実装します。これはAntlrV3 astから構築されていますが、AntlrV4 解析ツリーから構築するために再適合するのはそれほど難しくありません。

Eclipse PyDev プロジェクトは、おそらく Python ソース用の Java ベースの AST も実装しています。両方のプロジェクトのソース ツリーのレイアウトが非常に似ていることに注意してください。

当然のことながら、念のため、これらのソースのコードを使用する前にライセンスを確認する必要があります。

于 2014-07-15T22:31:43.103 に答える
0

回避策を見つけました:

andを使用Jythonしますast(@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))

これは質問に厳密に答えるものではなく、すべてのユーザーに当てはまるとは限らないため、この回答を選択せず​​に残します.

于 2014-07-16T03:46:00.543 に答える
0

ANTLR4 はビジターを生成できます。これを使用して解析ツリーをトラバースし、AST を構築できます。Python にはastパッケージがあるため、これは問題になりません (Python を使用している場合)。

私は(研究の一環として) ANTLR4 を使用して Python 3 でおもちゃの Python インタープリターを作成しました。訪問者コードは にある/tinypy/AST/builder/ので、その方法を理解することができます。

于 2015-12-25T19:42:40.773 に答える