http://rua.rubyforge.org/
http://rubyforge.org/projects/rua/
http://storehouse.sakura.ne.jp/viewvc/viewvc.cgi/rua/rua.tar.gz?root=svn&view=tar
extconf.rbでコンパイルが通って、動作確認もできたので正式公開。
近日中にRubyForgeに移す予定。
これは何?
Rubyから組み込み用言語Luaを使うための拡張ライブラリです。
Luaのライブラリはすでにいくつかあるんですが、
- Lua 5.1に対応したライブラリが欲しかった(RAAに登録されているものは対応していないみたい…)
- win32用のバイナリが欲しかった
ということで作成しました。
インストール
gem
~$ gem install rua Select which gem to install for your platform (i386-mswin32) 1. rua 0.1.0 (mswin32) 2. rua 0.1.0 (ruby) 3. Skip this gem 4. Cancel installation >
unix
Lua5.1の開発用パッケージをインストールして、以下のようにrua.soを作成します。
~$ ruby extconf.rb --with-lua-include=/usr/include/lua5.1 --with-lua-lib=/usr/lib checking for lua.h... yes checking for lualib.h... yes checking for lauxlib.h... yes checking for main() in -llua5.1... yes creating Makefile ~$ make cc -I. -I. -I/usr/lib/ruby/1.8/i486-linux -I. -DHAVE_LUA_H -DHAVE_LUALIB_H -DHAVE_LAUXLIB_H -I/usr/i nclude/lua5.1 -fPIC -fno-strict-aliasing -O2 -fPIC -c rua.c cc -shared -rdynamic -Wl,-export-dynamic -L"/usr/lib" -o rua.so rua.o -lruby1.8 -llua5.1 -lpthre ad -ldl -lcrypt -lm -lc
とりあえず動かす
とりあえず動かすだけならこんな感じです。
require 'rua' rua = Rua.new rua.openlibs(:all) # ライブラリの読み込み rua.foo = 65535 # 大域変数の書き込み rua.func = lambda do |x| puts x end p rua.eval(<<EOS) # 文字列の評価 print('hello Rua!') print(foo) func(100) f = function() print('f() called.') end return 100 EOS f = rua.f # 大域変数の読み込み f.call
基本的にopenlibsメソッドでライブラリを開かないと、出来ることは少ないです。
標準ライブラリはここにあるとおり。
「rua.openlibs(:all)」ですべてのライブラリを開きます。
個別にライブラリを開く場合は「rua.openlibs(:base, :io, :string)」と必要なだけ渡します。
evalメソッドで文字列を評価しますが、評価する文字列で明示的にreturnをしないと、戻り値はnilになります。
オブジェクトの変換
オブジェクトの変換は次のようになります。
- Ruby→Lua
- nil/true/false→nil/true/false
- Numeric→数値
- String→文字列
- Hash/Array→テーブル
- Proc/Method→関数
- RuaFunc/RuaThread→元に戻る
- その他→テーブル
- Lua→Ruby
- nil/true/false→nil/true/false
- 数値→Float/Integer
- 文字列→String
- テーブル→Hash
- 関数→RuaFunc
- スレッド→RuaThread
- その他→nil
Arayは1オリジンの数列をキーとしたテーブルになります。
require 'rua' rua = Rua.new rua.openlibs(:all) rua.ary = ['foo', 'bar', 'zoo'] rua.eval(<<EOS) #=> 「1:foo、2:bar、3:zoo」と表示 for i, v in ipairs(ary) do print(i .. ':' .. v) end EOS
また、Rubyのその他のオブジェクトは、定義されているメソッド名をキーに持つテーブルになります。
require 'rua' rua = Rua.new rua.openlibs(:all) rua.Time = Time rua.eval(<<EOS) time = Time.new() -- 大域変数Timeのテーブルにnewというキーで関数が保持されている print(time.to_s()) -- > Sat Nov 10 23:02:01 +0900 2007 EOS
Luaで定義した関数・RubyからLuaに渡した関数は、Ruby側でRuaFuncインスタンスに変換されます。
使い方はProcとほとんど変わりませんが、例外を投げることがありません。
例外の扱いについて
Luaは例外機構を持たない言語です。
なので、RubyからLuaに定義した関数で例外が投げられても、処理を続行します。Ruby側に例外は返ってきません。
※0.3.1からRuaErrorで実行を中止するようにしました。処理を続行する場合は Rua#abort_by_error に false をセットしてください。
Luaの実行中に発生した例外を補足するには、エラーハンドラを使います。
require 'rua' rua = Rua.new rua.abort_by_error = false rua.error_handler = lambda {|e| p e } # ハンドラを設定 rua.openlibs(:all) rua[:f] = lambda do raise 'xxxxx' end rua.eval(<<EOS) f() -- 例外が発生するが処理は続行 print(100) EOS
セキュアモード
Lua側からRubyへメタプログラミングされるのを防ぐため、デフォルトではセキュアモードが有効になっています。
セキュアモードでは以下のような動作になります。
- Lua側へModule・Classクラス
インスタンスを渡せない。- 渡すと例外が投げられるか、
fatalでとまりますnilに変換されます。
- 渡すと例外が投げられるか、
- 以下のメソッドが変換されない
- __id__
- __send__
- ancestors
- autoload
- autoload?
- class
- class_eval
- class_variable_defined?
- class_variables
- const_defined?
- const_get
- const_missing
- const_set
- constants
- extend
- freeze
- id
- include?
- included_modules
- instance_eval
- instance_method
- instance_methods
- instance_variable_defined?
- instance_variable_get
- instance_variable_set
- instance_variables
- method
- method_defined?
- method_missing
- methods
- module_eval
- private_class_method
- private_instance_methods
- private_method_defined?
- private_methods
- protected_instance_methods
- protected_method_defined?
- protected_methods
- public_class_method
- public_instance_methods
- public_method_defined?
- public_methods
- respond_to?
- send
- singleton_methods
- taint
- to_ary
- to_hash
- to_int
- to_str
- type
- untaint
セキュアモードを無効にするには「rua.secure = false」としてください。
コルーチン
require 'rua' co = rua.eval(<<-EOS) function foo (a) print('foo', a) return coroutine.yield(2 * a) end return coroutine.create(function (a, b) print('co-body', a, b) local r = foo(a + 1) print('co-body', r) local r, s = coroutine.yield(a + b, a - b) print('co-body', r, s) return b, 'end' end) EOS p co.resume(1, 10) p co.resume('r') p co.resume('x', 'y')