RubyのコードをCに組込む方法はある程度知っているので、Vimがどういう作法でそれを読んでいるのかを調べる。
マルチプラットフォームな分岐が混じっていて非常に読みづらかったが、要はensure_ruby_initializedという関数で、ruby_init_stack()とruby_init()を読んで、load_pathを設定し、vimとやりとりするオブジェクトを仕込んでいるらしい。
そして、呼び出しのキック元はex_ruby()とかex_rubydo()とかの関数で、それぞれがVimの:rubyコマンド等に対応している。
その対応関係は、ex_docmd.cとかex_cmds.hあたりで定義されている。
ensure_ruby_initializedはこんな感じの関数。
static int ensure_ruby_initialized(void)
{
if (!ruby_initialized)
{
#ifdef DYNAMIC_RUBY
if (ruby_enabled(TRUE))
{
#endif
#ifdef _WIN32
int argc = 1;
char *argv[] = {"gvim.exe"};
NtInitialize(&argc, &argv);
#endif
{
#if defined(RUBY19_OR_LATER) || defined(RUBY_INIT_STACK)
ruby_init_stack(ruby_stack_start);
#endif
ruby_init();
}
#ifdef RUBY19_OR_LATER
{
int dummy_argc = 2;
char *dummy_argv[] = {"vim-ruby", "-e0"};
ruby_process_options(dummy_argc, dummy_argv);
}
ruby_script("vim-ruby");
#else
ruby_init_loadpath();
#endif
ruby_io_init();
ruby_vim_init();
ruby_initialized = 1;
#ifdef DYNAMIC_RUBY
}
else
{
EMSG(_("E266: Sorry, this command is disabled, the Ruby library could not be loaded."));
return 0;
}
#endif
}
return ruby_initialized;
}
:mrubyコマンドを動かせるようにする
マルチプラットフォームとかはとりあえずスルー。
Vimとのデータのやり取りも置いといて、:mrubyコマンドで構文を実行する所までを目指す。
#ifdef HAVE_CONFIG_H
# include "auto/config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <mruby.h>
#include <mruby/proc.h>
#include <mruby/compile.h>
#include "vim.h"
#include "version.h"
static int mruby_initialized = 0;
static int ensure_mruby_initialized(void);
static mrb_state* vimMrb;
void ex_mruby(exarg_T *eap)
{
char *script = NULL;
mrbc_context *cxt;
script = (char *)script_get(eap, eap->arg);
if (!eap->skip && ensure_mruby_initialized())
{
cxt = mrbc_context_new(vimMrb);
if (script == NULL) {
puts((char *)eap->arg);
mrb_load_string_cxt(vimMrb, (char *)eap->arg, cxt);
mrbc_context_free(vimMrb, cxt);
} else {
EMSG(_("no mruby script"));
}
}
vim_free(script);
}
static int ensure_mruby_initialized(void)
{
if (!mruby_initialized)
{
vimMrb = mrb_open();
mruby_initialized = 1;
return mruby_initialized;
} else {
return mruby_initialized;
}
}
void mruby_end()
{
mrb_close(vimMrb);
}
非常に適当だが、とりあえずif_ruby.cからコードの構造をパクってきてでっち上げた。
続いて、ex_cmds.hとex_docmd.cを弄る。
diff -r f6cacdc34495 src/ex_cmds.h
--- a/src/ex_cmds.h Wed Aug 07 21:13:23 2013 +0200
+++ b/src/ex_cmds.h Sun Aug 11 16:00:41 2013 +0900
@@ -785,6 +785,8 @@
NEEDARG|EXTRA|NOTRLCOM),
EX(CMD_runtime, "runtime", ex_runtime,
BANG|NEEDARG|FILES|TRLBAR|SBOXOK|CMDWIN),
+EX(CMD_mruby, "mruby", ex_mruby,
+ RANGE|EXTRA|NEEDARG|CMDWIN),
EX(CMD_ruby, "ruby", ex_ruby,
RANGE|EXTRA|NEEDARG|CMDWIN),
EX(CMD_rubydo, "rubydo", ex_rubydo,
diff -r f6cacdc34495 src/ex_docmd.c
--- a/src/ex_docmd.c Wed Aug 07 21:13:23 2013 +0200
+++ b/src/ex_docmd.c Sun Aug 11 16:00:41 2013 +0900
@@ -289,6 +289,9 @@
# define ex_rubydo ex_ni
# define ex_rubyfile ex_ni
#endif
+#ifndef FEAT_MRUBY
+# define ex_mruby ex_script_ni
+#endif
#ifndef FEAT_SNIFF
# define ex_sniff ex_ni
#endif
mrubyをちゃんと片付けられるように、mruby_endの呼び出しをmain.cに加える。
diff -r f6cacdc34495 src/main.c
--- a/src/main.c Wed Aug 07 21:13:23 2013 +0200
+++ b/src/main.c Sun Aug 11 16:00:41 2013 +0900
@@ -1478,6 +1478,9 @@
#ifdef FEAT_RUBY
ruby_end();
#endif
+#ifdef FEAT_MRUBY
+ mruby_end();
+#endif
#ifdef FEAT_PYTHON
python_end();
#endif
関数のプロトタイプ宣言を追加するために、proto/if_mruby.proを作成。
diff -r f6cacdc34495 src/proto/if_mruby.pro
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/proto/if_mruby.pro Sun Aug 11 16:00:41 2013 +0900
@@ -0,0 +1,4 @@
+
+void mruby_end __ARGS((void));
+void ex_mruby __ARGS((exarg_T *eap));
+
ヘッダの読み込みを追加する。
diff -r f6cacdc34495 src/proto.h
--- a/src/proto.h Wed Aug 07 21:13:23 2013 +0200
+++ b/src/proto.h Sun Aug 11 16:00:41 2013 +0900
@@ -196,6 +196,9 @@
# ifdef FEAT_RUBY
# include "if_ruby.pro"
# endif
+# ifdef FEAT_MRUBY
+# include "if_mruby.pro"
+# endif
/* Ugly solution for "BalloonEval" not being defined while it's used in some
* .pro files. */
後はせっせとautoconfのスクリプトを弄って、--enable-mrubyinterpみたいなオプション足したり、FEAT_MRUBYを定義したり、って感じでコンパイルをでっち上げる。
実際には、autoconfを弄る前に手動でMakefileを直で弄ってコンパイル通らねー、と頭を悩ませたりしてた。
一通り通って動かせるようになってからautoconfを弄っている。
結果、こんな感じでconfigureしてコンパイルすれば動くようになった。
% ./configure --with-features=huge --enable-multibyte --enable-luainterp=yes --with-luajit --with-lua-prefix=/usr/local --enable-rubyinterp=yes --enable-mrubyinterp=yes --with-libmruby=/Users/joker/mruby/build/host/lib/libmruby.a --with-mruby-include=/Users/joker/mruby/include
% make
なんか表示が狂ってておかしいけど、とりあえず実行出来てるっぽい。
パっと見:rubyコマンドと何も変わらないので、少し寂しいが結構楽しかった。
出力はさておき、Vim側とのインターフェースをちゃんと書けば、mrubyでvimプラグインとか書けるかもしれないし、mrbgemsの資産使えたりするんじゃね?とちょっと思っている。
が、そこまでやる気が続くかは分からないw
正直、C書いたりautoconf弄ったりするの辛い…。LLerには厳しい世界であった。
ぬるま湯最高!
編集差分のgistは → https://gist.github.com/joker1007/6203801