nikkie-ftnextの日記

イベントレポートや読書メモを発信

PythonのAST(抽象構文木)をRustのプログラムからもdumpしたい!

はじめに

久石奏さん、お誕生日おめでとうございます。さいごのソリスト、胸を打つ...😭
nikkieです。

RustでPythonのASTを扱う、小さな小さな一歩の素振りをしました。

目次

PythonでASTをdumpする

標準ライブラリastを使います。
https://docs.python.org/ja/3/library/ast.html#command-line-usage

% python -m ast <<"EOF"
if True:
  pass # comment
EOF
Module(
   body=[
      If(
         test=Constant(value=True),
         body=[
            Pass()],
         orelse=[])],
   type_ignores=[])

rustpython-parser crate

https://crates.io/crates/rustpython-parser

偶然知りました。
ソースコードがあるのは、RustPython/Parserというリポジトリ

The parser is one of the core part of RustPython and Ruff project.

Ruffでも使われている(使われていた?1)ようです

Ruffのリポジトリに見つけたprint-ast

Contributingのページの「cargo dev」の項目。
https://docs.astral.sh/ruff/contributing/#cargo-dev
cargo dev print-ast <file>が紹介されています。

ここを見て、rustpython-parserを使ってもprint-ast相当のことができるのではないかと思いつきました。
crates.ioの「How to use」のコードでVecが返ってきます。
これをprint-astのように表示してみたくなったのです。

パクるために、print-astの実装を確認します。
https://github.com/astral-sh/ruff/blob/0.8.6/crates/ruff_dev/src/print_ast.rs

println!("{python_ast:#?}");

『Rustの練習帳』で見たところだ!

{:#?}を見て『Rustの練習帳』を思い出しました2

{:?}を使うことで、構造体をデバッグ用の形式(リンク省略)で表示できます。(2.2.1)

今回は{:#?}を使って、改行とインデントを入れて出力を読みやすくしています。これは、整形表示(pretty-printing)と呼ばれています。(2.2.3)

Ruffのprint-astでやっているのは、整形表示(pretty-print)!

この機にドキュメントも確認

? formatting.

When used with the alternate format specifier #?, the output is pretty-printed.

rustpython-parserによるASTをpretty-print

% cargo init hello-ast
% cd hello-ast
% cargo add rustpython-parser

インストールされたrustpython-parserは0.4.0です。

% cargo run -q
[
    If(
        StmtIf {
            range: 0..13,
            test: Constant(
                ExprConstant {
                    range: 3..7,
                    value: Bool(
                        true,
                    ),
                    kind: None,
                },
            ),
            body: [
                Pass(
                    StmtPass {
                        range: 9..13,
                    },
                ),
            ],
            orelse: [],
        },
    ),
]

できた!!
Ruffのドキュメントのprint-astの出力例と一致するように思われます。

(なお、ファイルからの読み込みは後回しにしています)

終わりに

ごくごく短いPythonコード片をrustpython-parserでASTに変換し、それをpretty-printしました。
Pythonのastモジュールと比べると、できあがるASTは全然違うのですね。
例えばRuffはこんなASTを扱っているのか〜


  1. https://github.com/astral-sh/ruff を検索したところ、現在はCargo.tomlに依存の記載はなさそうでした。Ruffのリポジトリ内にもparserのcrateがあります
  2. 取り組んだことがあります