何らかの理由でC言語を書けない状況で、mrubyを使いたい場合があると思います。
そんな時のためにNimを使ってmrubyを組み込んでみました
環境
- OSX Yosemite
- Nim 0.15
- mruby
- ruby
Nimとmrubyの設定
準備
mrubyをgithubからcloneしましょう。
以下、コンソール。
$ mkdir nim-mruby && cd nim-mruby
$ git clone --depth 1 https://github.com/mruby/mruby.git
$ mkdir -p nimruby/tools/nimruby
$ mkdir -p nimruby/mrblib
$ touch nimruby/mrblib/nimruby.rb nimruby/tools/nimruby/nimruby.nim nimruby/mrbgem.rake
こんな感じ
mruby/ <- githubから落としてきたmruby
nimruby/
mrbgem.rake
mrblib/
nimruby.rb
tools/
nimruby/
nimruby.nim
プログラムのビルドの設定
mruby側
mruby/build_config.rb
MRuby::Build.new do |conf|
toolchain :gcc
conf.gem '../nimruby' do |nim|
# nimbase.hなどが入っているところ
nim.cc.include_paths << "/<nimのパス>/lib"
nim.cc.option_include_path = '-I%s'
end
conf.gembox 'default'
end
nim側
nimruby/mrbgem.rake
MRuby::Gem::Specification.new('nimruby') do |spec|
spec.license = 'MIT'
spec.author = 'hogehoge'
spec.bins = %w(nimruby)
nimcache = File.join(File.dirname(__FILE__), %w(tools nimruby))
nim = File.join(nimcache, 'nimruby.nim')
# .nimから.cのファイルを生成するだけにする(プログラムは作らない)
`nim c --nimcache:#{nimcache} --compileOnly #{nim}`
end
ソースコード
フィボナッチ数を生成するプログラム。
nimruby/tools/nimruby/nimruby.nim
{.emit: """
#include "mruby.h"
""".}
type
mrb_int = cint
mrb_aspec = uint
mrb_value {.importc, header: "mruby.h"} = object
mrb_state {.importc, header: "mruby.h"} = object
object_class: ref RClass
RClass {.importc, header: "mruby.h"} = object
mrb_func_t {.importc, header: "mruby.h"} =
proc (mrb: ref mrb_state; val:mrb_value): mrb_value {.cdecl.}
proc mrb_get_args(mrb: ref mrb_state; format: cstring): cint
{.importc, varargs, header: "mruby.h"}
proc mrb_fixnum_value(v: cint): mrb_value
{.importc, header: "mruby.h"}
proc mrb_fixnum(v: mrb_value): cint
{.importc, header: "mruby.h"}
proc mrb_open(): ref mrb_state
{.importc, header: "mruby.h"}
proc mrb_close(mrb: ref mrb_state)
{.importc, header: "mruby.h"}
proc MRB_ARGS_REQ(n: cint): mrb_aspec
{.importc: "MRB_ARGS_REQ", header: "mruby.h"}
proc mrb_define_method(mrb: ref mrb_state; c: ref RClass; name: cstring;
f: mrb_func_t; aspec: mrb_aspec)
{.importc, header: "mruby.h"}
proc mrb_funcall(mrb: ref mrb_state; self: mrb_value;
name: cstring; argc: cint): mrb_value {.importc, varargs, header: "mruby.h"}
proc mrb_top_self(mrb: ref mrb_state): mrb_value
{.importc, header: "mruby.h"}
proc fib(a: cint): cint =
if a <= 2:
result = 1
else:
result = fib(a-1) + fib(a-2)
proc nim_fib(mrb: ref mrb_state, self: mrb_value): mrb_value {.exportc, cdecl.} =
var x: cint
let _ = mrb_get_args(mrb, "i", addr(x))
mrb_fixnum_value(fib(x))
proc nimruby(n: cint): cint {.exportc.} =
var mrb = mrb_open()
mrb_define_method(mrb, mrb.object_class, "nim_fib", nim_fib, MRB_ARGS_REQ(1))
let r = mrb_funcall(mrb, mrb_top_self(mrb), "__main__", 1, mrb_fixnum_value(n));
result = mrb_fixnum(r)
mrb_close(mrb)
echo nimruby(10)
nimbleが使えるようであれば、Nimのライブラリを利用することもできます。
nimruby/mrblib/nimruby.rb
def __main__(n)
nim_fib(n)
end
ビルドと実行
.cがあれば、コンパイル+ビルドを設定に従ってやってくれます。
$ cd mruby
$ ruby minirake
$ bin/nimruby
55
感想
Nimは.emitでC言語・C++をかけたり、マクロの機能や.c,.cpp,.jsを生成できたり、とても面白い言語だと思います。
Nimはまだ、0.15というバージョンですが個人的に注目していきたい言語です。
おまけ
軽くベンチマークしてみました。
(まあ、mrubyとnimだと用途は違うので、あまり意味はないですが。。。)
nimruby/tools/nimruby/nimruby.nim
(中略)
import strutils
import times
import os
{.experimental.}
import threadpool
proc fib2x(a: cint): cint =
if a <= 2:
result = 1
else:
var ch = newSeq[cint](2)
parallel:
if 1 <= len(ch) - 1: # bounds check
ch[0] = spawn fib(a-1)
ch[1] = spawn fib(a-2)
result = ch[0] + ch[1]
template bm(title: string, code: typed) =
let s = epochTime()
echo code
let e = epochTime() - s
echo title, " ", e.formatFloat(format = ffDecimal, precision = 10), "s"
proc nimruby(n: cint) =
var mrb = mrb_open()
bm "mruby版 fib":
let r = mrb_funcall(mrb, mrb_top_self(mrb), "fib", 1, mrb_fixnum_value(n));
mrb_fixnum(r)
mrb_close(mrb)
bm "nim版 fib":
fib(n)
bm "nim版 fib2x":
fib2x(n)
let n = parse_int(os.paramStr(1))
nimruby(n.cint)
nimruby/mrblib/nimruby.rb
def fib(n)
return 1 if n <= 2
fib(n-1) + fib(n-2)
end
$ ruby minirake && bin/nimruby 35
9227465
mruby版 fib 3.4108259678s
9227465
nim版 fib 0.0541160107s
9227465
nim版 fib2x 0.0345828533s