Emacsに mrubyを組み込んでみました. 以下のようなことができるようになります.
(let ((mrb (mruby-init)))
(mruby-eval mrb
"
def fizzbuzz(num)
(1..num).map do |n|
case
when n % 15 == 0 then \"fizzbuzz\"
when n % 5 == 0 then \"buzz\"
when n % 3 == 0 then \"fizz\"
else n
end
end
end
fizzbuzz(ARGV[0].to_i)
" 20))
このS式の結果は以下のようになります.
[1 2 "fizz" 4 "buzz" "fizz" 7 8 "fizz" "buzz" 11 "fizz" 13 14 "fizzbuzz" 16 17 "fizz" 19 "buzz"]
Emacs Lispのオブジェクトに対して mrubyのメソッドを呼ぶこともできます(注: mrubyの Arrayの機能を使う場合はリストでなくベクタを使用する必要があります)
(let ((mrb (mruby-init)))
(mruby-send mrb -10 'abs)) ;; => 10
(let ((mrb (mruby-init)))
(mruby-send mrb "hello WORLD" 'swapcase)) ;; => "HELLO world"
(let ((mrb (mruby-init)))
(mruby-send mrb [1 [2 3] [4 [5 6]]] 'flatten)) ;; => [1 2 3 4 5 6]
仕組み
Emacs 25で実装予定の Dynamic module機能を使って実現しています(なので実際のところ, 組み込んだというほどではないです). 要は dlopen的なもので, 共有ライブラリを実行時に読み込んでその機能を使うというものです. これにより Cライブラリの機能を再ビルドすることなく使えることになります. 高速化や機能の拡張が期待されます. 他のエディタの人からするとまだ実装していなかったのかというような機能かもしれないですが, それが Emacsでもついに使えるときが来たわけですね.
リポジトリ
セットアップ
Emacsの構築
現状 Dynamic module機能は emacs-25ブランチにしか実装されていないので, emacs-25ブランチのソースコードからビルドする必要があります.
% git clone git://git.savannah.gnu.org/emacs.git
% cd emacs
% git checkout -b emacs-25 origin/emacs-25
% ./configure --with-modules --prefix=installed_path # --with-modulesが必須
% make
% make install
mruby.elのインストール
現状規約も何もない感じですが, とりあえず Dynamic moduleのテストと同様に modules/
以下に置いておきます. 現状 Linux環境(Ubuntu 15.10 x86_64)でしかテストしていませんが, Dynamic module機能は Windowsでも MacOSXでも使えるように開発が行われています.
% cd modules
% git clone https://github.com/syohex/emacs-mruby-test.git
% make
mruby.elのロード
makeしたディレクトリで下記のように起動してください(同ディレクトリに load-pathを通して, (require 'mruby)
だけでもよいです). emacsは上記で構築した emacsを使ってください.
% emacs -Q -L . -l mruby.el
API
実装は @mattn さんの go-mrubyを参考にしました.
(mruby-init)
mrb_stateオブジェクトを作成. GCされるとき, mrb_close
関数が呼ばれるので明示的に終了関数を呼ぶ必要がありません.
(mruby-eval mrb code &rest args)
第一引数は mruby-init
の戻り値です. 第二引数が mrubyのコード, 第三引数以降は ARGV
に設定されます.
コード例
(let ((mrb (mruby-init)))
(mruby-eval mrb "ARGV.map{|x| x + 1}" 1 2 3)) ;; => [2 3 4]
(mruby-send mrb code method &rest args)
第一引数は mruby-init
の戻り値です. 第二引数が mrubyのコード, 第三引数がメソッド名(シンボル), 第四引数以降は ARGV
に設定されます.
コード例
(let ((mrb (mruby-init)))
(mruby-send mrb ["vim" "emacs" "atom"] 'join "+")) ;; => "vim+emacs+atom"
おわりに
Emacs 25で導入予定の Dynamic module機能を使って mrubyを使えるようにしてみたことを示しました. 現状はお遊びというレベルですが, lambda式の扱いとか mruby側から Emacs側を操作する方法等について検討していきたいと思います. また mruby力が超絶に低いのでそこを鍛えて面白い発想ができるようにもなっていければと思います. アイデア, 問題点等ありましたら, github issuesか twitterまでお知らせいただければと思います.