ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

GWなのでRustでLuaを実装し(ようとし)た話

GWは、ゲートウェイじゃなくゴールデンウィークです。

タイトルの通り、連休の多くの空き時間を言語実装に費やしてしまった...。

github.com

とにかく、スターがついて承認されたいという気持ちが強いです(結論ファースト)。


今のところ、この辺りの機能はできている。

  • 四則演算
  • Rustでの/Luaでのグローバル関数定義
  • 関数内ローカル変数
  • if/then/else文
  • 配列ベースの繰り返し(ただ、配列操作ができない)

フィボナッチ数を求めるとこんな感じ。 2.4 GHz / 8コア Intel Core i9Macで実行した。といってもシングルコアしか使わないのだが。

function dofib(n)
   if n < 2 then
      return 1
   else
      return dofib(n-1) + dofib(n-2)
   end
   print("Unreachable!\n")
end

print(dofib(25))
print("\n")
$ time ./target/release/purua lua_examples/fib2.lua
121393
./target/release/purua lua_examples/fib2.lua  2.12s user 0.01s system 99% cpu 2.132 total

## 比較用 ruby 2.7.1
$ time ruby /tmp/rubyfib.rb
121393
ruby /tmp/rubyfib.rb  0.07s user 0.07s system 56% cpu 0.259 total

振り返っての所感

を雑に書き留める。

Lua を何も知らないところから書いた

たまたま Lua の BNF を見かけて 、非常に簡潔なのに感銘を受けて、GWの課題にパーサぐらい作ってみようかと思ったのが始まり。これが甘かったのですが...。

正直、Luaの言語仕様を調べながらだったのでその点で時間がかかったのは否めない。配列とか連想配列どうやるの? とか、繰り返しは? とか、ごく普通の操作がわからない...。

もちろん仕様書があるわけだけれど、どうしてもサンプルコードを動かさないとピンとこないところもありつつ。とにかく、Luaに詳しくなったのでいろいろ無駄にはならないだろう。

パーサは combine を使用

Rustのパーサ実装には有名なものとして Nomcombine の2つがあるそうで、今回はcombineを用いた。

パーサコンビネーター自体使うのが初めて(一応、Rust実践入門でパーサの章は読んだ)だし、最新の情報も意外となく、手探りでまずは 四則計算 から書いてみたりした*1

最初は基本的な機能も把握していなくて恥ずかしい感じだったが、今のところLuaの文法の範囲ならパースできてるような気がする...。

Rust の勉強になる

小学生並みの感想だがそう思った。

今回、C Luaやmrubyのようにstate構造体を持ち回すようにして実装を進めた。

で、参考実装としてgopher-luaを見ながら、ほぼ移植するように実装していった。

github.com

この際、state構造体は所有権のお化けみたいな感じで、たくさん状態を持ち、いろいろなところからスタックを更新しようとするので難儀した。言語の設計思想の違いも大きく、GoのコードをそのまんまRustに持っていっても動かすのが難しい箇所(特にコールフレームなど循環参照があるところ)は、なんとなく誤魔化したり...。

言語実装、Rustの知識両方の足りなさを痛感したが、今回の実装を経て1ミクロンほど慣れたように思う。あまりライフタイムを気にしないように潔くクローン、ムーブした方がいいよね、とか。Book を読んでも意味がよくわからなかったRc/RefCellとか。

やることが無限にある

言語実装、ある機能Aを実現したい際に、機能Bと機能Cと機能Dが必要です! ということが多く、とにかくキリがいいタイミングが存在しないとわかってきた。非常に時間を食われる。GW、シレンしたかったのに...。

このブログを一区切りでペースは落としたいが引き続きしつこく実装は続ける。


結論、言語実装の初心者でも勢いで動くものはできるのだなあという気持ちになった。できたのか?

でも、Rustのメモリ安全性を持ってしてもまだ不安なところがたくさんあり、細かく作り込まれた既存の自作された言語はめまいがするほどすごい。

今後について

以下のような機能ができていない。

これがLuaと言えるかと問われれば、言えないと思う。

一方で、内部的には多値の引数、多値の戻り値、連想配列、メタテーブルなどは扱える状態なので、地道に文法を実装するという感じになる。地道にやるしかないのだ...。

逆にいえば、ここに列挙したぐらいの内容を実装すればluatestが動くのではという淡い期待。

あと、Lua自体はVMを持つ言語で、参考実装の gopher-luaVMを持っている。なので、どこかのタイミングでVMにしたいが、Luagopher-lua)のVMレジスタマシンで、VM実装初心者には大変そう...。

craftinginterpreters.com

この辺の手順をRustでさらって、まずはスタックマシンを実装してから挑戦したい。

あと、やっぱりWASM上でPuruaを動かせると強いと思うのでそういう機能はサポートしたい(emscriptenLua本体をWASM化できそうだけど)。

謝辞

Rust自作言語先輩である @monochrome さん、 @yhara さんにはいろいろなアドバイスをいただいた。引き続きいろいろ突っ込んでください...。

最後に言いたいこと

$ cargo install purua

crates.io に公開したのでこれだけでインストールできる! 試してみましょう。ほとんどの機能はまともに動きませんが...。

あと、スターをry

github.com

*1:このcalc.rsは実際には使っていない