2012/04/20


C言語の構造体をラップする方法が分からなかった(rubyでいうData_Wrap_Structが見当たらなかった)のでissueに書いた僕と名前の良く似た方matzさんが音速で入れてくれた。
ただrubyの時の様にalloc_funcが無いのでinitializeでメンバに放り込んでる。
まぁしょうがないか。
#include <mruby.h>
#include <mruby/proc.h>
#include <mruby/data.h>
#include <compile.h>
#include <variable.h>
#include <uv.h>

typedef struct {
  mrb_state* mrb;
  mrb_value proc;
  void* data;
} mrb_uv_data;

static void
uv_handle_free(mrb_state *mrb, void *p)
{
  uv_close((uv_handle_t*) ((mrb_uv_data*)p)->data, NULL);
  free(((mrb_uv_data*)p)->data);
}

static const struct mrb_data_type uv_handle_data_type = {
  "uv_handle", uv_handle_free,
};

static mrb_value
mrb_uv_run(mrb_state *mrb, mrb_value self)
{
  return mrb_fixnum_value(uv_run(uv_default_loop()));
}

static mrb_value
mrb_uv_timer_init(mrb_state *mrb, mrb_value self)
{
  mrb_uv_data* uvdata = (mrb_uv_data*) malloc(sizeof(mrb_uv_data));
  uvdata->mrb = mrb;
  uvdata->data = malloc(sizeof(uv_timer_t));
  uvdata->proc = mrb_nil_value();
  uv_timer_init(uv_default_loop(), (uv_timer_t*) uvdata->data);
  mrb_iv_set(mrb, self, mrb_intern(mrb, "data"), mrb_obj_value(
    Data_Wrap_Struct(mrb, (struct RClass*) &self,
    &uv_handle_data_type, (void*) uvdata)));
  return self;
}

void
_uv_timer_cb(uv_timer_t* timer, int status) {
  mrb_uv_data* uvdata = (mrb_uv_data*) timer->data;
  mrb_yield(uvdata->mrb, uvdata->proc, mrb_fixnum_value(status));
}

static mrb_value
mrb_uv_timer_start(mrb_state *mrb, mrb_value self) {
  mrb_value value;
  mrb_uv_data* uvdata;
  struct RProc *b;
  mrb_value argc, *argv;
  int nargs;

  mrb_get_args(mrb, "b*", &b, &argv, &argc);
  if (mrb_fixnum(argc) != 2) {
    mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments");
  }
  value = mrb_iv_get(mrb, self, mrb_intern(mrb, "data"));
  Data_Get_Struct(mrb, value, &uv_handle_data_type, uvdata);
  uvdata->proc = mrb_obj_value(b);
  ((uv_timer_t*) uvdata->data)->data = uvdata;

  uv_timer_start((uv_timer_t*) uvdata->data, _uv_timer_cb,
    mrb_fixnum(argv[0]),
    mrb_fixnum(argv[1]));
  return mrb_nil_value();
}

int
main() {
  int n;
  mrb_state* mrb;
  struct mrb_parser_state* st;
  struct RClass *uv, *uv_timer;

  mrb = mrb_open();

  uv = mrb_define_module(mrb, "UV");
  mrb_define_class_method(mrb, uv, "run", mrb_uv_run, ARGS_REQ(1));

  uv_timer = mrb_define_class_under(mrb, uv, "Timer", mrb_class_obj_get(mrb, "Object"));
  mrb_define_method(mrb, uv_timer, "initialize", mrb_uv_timer_init, ARGS_ANY());
  mrb_define_method(mrb, uv_timer, "start", mrb_uv_timer_start, ARGS_ANY());

  {
    char* code =
"require 'UV'             \n"
"t = UV::Timer.new()      \n"
"t.start(1000, 1000) {|x| \n"
"  p x                    \n"
"}                        \n"
"p UV.run()               \n";
    st = mrb_parse_string(mrb, code);
    n = mrb_generate_code(mrb, st->tree);
    mrb_pool_close(st->pool);
    mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_nil_value());
  }
}

/* vim:set et ts=2 sts=2 sw=2 tw=0: */
実行するとrubyのコード require 'UV'
t = UV::Timer.new()
t.start(10001000) {|x|
  p x
}
UV.run()
の通り、1秒間隔で1秒後に再発動するタイマ、つまりは1秒おきにステータスが表示される。
この辺の引数はUVと合わせた。エラーチェックとかサボリまくってるのでお手本のコードにはならない。


. .: : : : : : : : :: :::: :: :: : :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    . . : : : :: : : :: : ::: :: : :::: :: ::: ::: ::::::::::::::::::::::::::::::::::::::
   . . .... ..: : :: :: :: :: :: :: .。oO(寿司食べたい…):::::::
        Λ_Λ . . . .: : : ::: : :: ::::::::: :::::::::::::::::::::::::::::
       /:彡ミ゛ヽ;)ー、 . . .: : : :::::: :::::::::::::::::::::::::::::::::
      / :::/:: ヽ、ヽ、 ::i . .:: :.: ::: . :::::::::::::::::::::::::::::::::::::::
      / :::/;;:   ヽ ヽ ::l . :. :. .:: : :: :: :::::::: : ::::::::::::::::::
 ̄ ̄ ̄(_,ノ  ̄ ̄ ̄ヽ、_ノ ̄ ̄ ̄ ̄

あとは適当にゆっくりやっときます。
mattn/mruby-uv - GitHub

inteface to libuv for mruby

https://github.com/mattn/mruby-uv
Posted at by




FizzBuzzに並んで有名な教育向けな題材、Acme::Oppaiです。
#include <mruby.h>
#include <mruby/proc.h>
#include <compile.h>

static mrb_value
oppailib_oppaifunc(mrb_state *mrb, mrb_value self) {
  static int i = 0;
  puts(i++ % 2 == 0 ?
"    _    おっ \n"
"  ( ゜∀゜)    \n"
"  (  ⊂彡     \n"
"   |   |     \n"
"   し ⌒J     \n" :
"    _  ∩ おっ \n"
"  ( ゜∀゜)彡 ぱい\n"
"  (    |    \n"
"   |   |    \n"
"   し ⌒J     \n");
  return self;
}

int
main() {
  int n;
  mrb_state* mrb;
  struct mrb_parser_state* st;
  struct RClass *oppailib;
  char* code =
"require 'Oppai'                       \n"
"Oppai.oppai().oppai().oppai().oppai() \n";

  mrb = mrb_open();

  oppailib = mrb_define_module(mrb, "Oppai");
  mrb_define_class_method(mrb, oppailib, "oppai", oppailib_oppaifunc, ARGS_REQ(1));

  st = mrb_parse_string(mrb, code);
  n = mrb_generate_code(mrb, st->tree);
  mrb_pool_close(st->pool);
  mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_nil_value());
}

/* vim:set et ts=2 sts=2 sw=2 tw=0: */
ruby のAPIと若干形が異なるので、既存コードとか移植する人はいろいろあるかも。
Posted at by




たぶんこれからガチャガチャ変わるので参考にはならない。まぁネタなので。あと殆ど tools/mruby の下にある物と同じ。
またAPIとか整理されてないので、コンパイルにはソースディレクトリを参照に含めたりする必要がある。
#include <mruby.h>
#include <mruby/proc.h>
#include <compile.h>

int
main() {
  int n;
  mrb_state* mrb;
  struct mrb_parser_state* st;
  char* code =
"[1,2,3].each {|x| " \
"  p x             " \
"}                 ";

  mrb = mrb_open();
  st = mrb_parse_string(mrb, code);
  n = mrb_generate_code(mrb, st->tree);
  mrb_pool_close(st->pool);
  mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_nil_value());
}

/* vim:set et ts=2 sts=2 sw=2 tw=0: */
mrb_open() で状態遷移マシンを作り、文字列をパースして構文木を作る(mrb_parse_string())。yaccによるAST。その構文木からジャンプ命令等の実行コードを吐いて(mrb_generate_code())実行する(mrb_run())。
まぁ一般的な奴ですね。これからいろいろ出来る様になるんだろうけど、Lightweight を歌ってるのでどの辺が線引きなのか難しそう。
MinGW32 だと以下の様にしてビルド出来た。
gcc -Iinclude -Isrc foo.c lib/ritevm.lib
Posted at by