昨年2019の夏頃から libperl-rs というライブラリを作っています(現状では仕事とは無関係の、純然たる自宅研究です。進みも間欠的です)。これは Perl5 のランタイムライブラリーである libperl を Rust から呼び出すためのラッパーライブラリーです。
libperl-rs については、これまでは YAPC Japan 2019 名古屋で発表した以外は、 これといった情報開示をしてきませんでした。
別に秘密というわけではなく、 単に blog を書くことを面倒臭がっていただけで、 github 上には 少しずつ進展を出してはいました。ですが最近、私が前もって現状を整理して公開していれば、誰かさんを困らせなくて済んだのかもなー…と思うこともあり…
ちょっと現状を整理して開示しておこうと思います。
libperl-rs の開発目標、libperl-rs で何を出来るようにしたいか
私が libperl-rs で一番やりたいことは、 Perl プログラムの AST (OP Tree) の解析です。 特に、動的に生成されるスクリプトの AST を解析し、その実行前にエラーを検出したり、 Language Server やデバッガーを提供したり…ということが出来るような道を開きたいと考えています。 (そこまでたどり着けるとは言い切れませんが…)
libperl-rs の現状
現在の libperl-rs は3つの crate から構成されています。
- libperl-config - env perl からビルド情報を取り出すためのライブラリーです。 2., 3. のビルドに用います。
- libperl-sys - env perl 付属の libperl に対する Rust binding を生成する crate です。bindgen を用いて C のヘッダーから Rust のコードを自動生成します。
- libperl-rs - libperl-sys の機能を Rust らしく扱えるようにするためのラッパーライブラリーです。
% tree -L 2 . ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── build.rs ├── examples │ ├── 000_perl_parse.rs │ ├── 001_perl_parse_args.rs │ ├── 100_scan_ops.rs │ ├── 101_scan_ops_debug.rs │ ├── 102_padname_type.rs │ ├── 103_scan_op_tree.rs │ ├── 104_enum_op_tree.rs │ ├── 105_scan_stash.rs │ ├── 106_scan_allpackage.rs │ ├── 107_scan_subs_in_a_file.rs │ ├── 108_scan_subs_in_a_file2.rs │ ├── 109_scan_subs_intro1.rs │ ├── 110_call_method.rs │ └── eg ├── libperl-config │ ├── Cargo.lock │ ├── Cargo.toml │ └── src ├── libperl-sys │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── build.rs │ ├── examples │ ├── script │ ├── src │ └── wrapper.h ├── release.toml ├── runtest-docker.zsh ├── src │ ├── lib.rs │ └── perl.rs └── target ├── debug └── rls
現状では 1., 2. については API はほぼ安定しているのではないかと思います。(改良の余地は有るかもしれません)
問題は 3. の libperl-rs です。 2. のbindgen によって生成された libperl-sys は Perl の C API からマクロを取り去った、裸の API です。しかも threaded 版と非スレッド版の2系統が存在します。それを Rust の層で抽象化し直す必要があります。 私がまだ Rust の修行中であることもあり、一発で正解にたどり着く自信はありません。 とても API が固まったとは言えない状況です。
そのため、現在は主として examples ディレクトリーの下に 実験的なコードを書き、その中で今後 libperl-rs の本体に組み入れると良さそうなコードを eg サブディレクトリーに作り貯めている所です。 皆さんのツッコミをお待ちしております。
今の libperl-rs で出来ること
今の段階の libperl-rs で出来ることは、どれも B モジュール で (とても頑張れば)出来ることばかりだと思います。
とはいえ、OP Tree に対してパターンマッチが出来るようになりつつあることは、
少なからぬメリットなのではないでしょうか。以下は Perl のサブルーチンの冒頭から
my (...) = @_
に相当する AST を抜き出すための、パターンマッチのコードです。
(少し冗長ですが)
他にも (perl_parse の作る) 静的な OP Tree を調べるコードだけではなく、 perl_parse 後に任意のクラスの任意のメソッドを Rust から呼び出してみるサンプルも、(まだまだベタな書き方ですし不足もありますが)動き始めています(抜粋)。
libperl-rs の試し方
現段階では github の master を試して頂くのが良いと思います。(crates.io にあるものは、YAPC Japan 2019 名古屋の 時のバージョンです。それ以後の改良部分を crates.io に送るのは、もう少し API が固まってからが良いのかなと…)
私自身の開発環境は Fedora 30 + System Perl (perl5.28) + System Rust (1.42) ですが、 travis-ci 上では Perl 公式 Docker イメージを用いて Perl 5.30〜5.22 での動作を確認してあります。 (発展的な例は threaded build な Perl5.26以上限定です) (libperl-sys までなら、もっと古い Perl でもビルド出来そうな気がします)
ですので、Debian, Ubuntu の上であれば以下の手順で動かしてみることは可能でしょう>
準備
apt update && apt install -y llvm-dev libclang-dev clang && curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh && sh rustup.sh -y && source $HOME/.cargo/env && rustup component add rustfmt git clone https://github.com/hkoba/libperl-rs && cd libperl-rs
実行
examples/109_scan_subs_intro1.rs
を実行してみる(渡したスクリプトの namespace から
my (...) = @_
形式の引数宣言だけを抜き出すサンプル)
% Z-chtholly(pts/2)% cargo run --example 109_scan_subs_intro1 -- -le ' package Int; sub foo { (my Int $x, my Int $y) = @_; $x + $y } ' Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/examples/109_scan_subs_intro1 -le ' package Int; sub foo { (my Int $x, my Int $y) = @_; $x + $y } '` $0 = "-e" sub "foo" preamble! first array assignment from @_, lvalue = [PadNameType { name: Some("$x"), typ: Some("Int") }, PadNameType { name: Some("$y"), typ: Some("Int") }]
最後に
ここまで読んで下さり有難うございました。ご意見お待ちしております!