About this Blog

This Blog has English posts and Japanese posts. About Mac, iOS, Objective-C, and so on.

2013年3月24日日曜日

mruby on Objective-C - puts,pをUITextViewに出力-

方針

__printstr__を(Objective-)Cで実装し、それを使ってRubyでp,puts,print,printfを定義する。
mrubyのメソッドとして使うCの関数から、AppDelegate経由でUITextViewにアクセス。


参考



Rubyのスクリプトをプロジェクトに追加しておきます。

print.rb
module Kernel
# needs __printstr__
  def print(*args)
    i = 0
    len = args.size
    while i < len
      __printstr__ args[i].to_s
      i += 1
    end
  end
  def puts(*args)
    i = 0
    len = args.size
    while i < len
      s = args[i].to_s
      __printstr__ s
      __printstr__ "\n" if (s[-1] != "\n")
      i += 1
    end
    __printstr__ "\n" if len == 0
    nil
  end
  def p(*args)
    i = 0
    len = args.size
    while i < len
      __printstr__ args[i].inspect
      __printstr__ "\n"
      i += 1
    end
    args[0]
  end
  def printf(*args)
    __printstr__(sprintf(*args))
    nil
  end
end


あとは、Objective-Cを編集します。
DelegateへのUITextViewの登録、実行するRubyスクリプトの取得などは省略してあります。
また、puts,p,print,printfの度にUITextViewにsetText:するので、最後の出力しか残りません。
クラスを拡張してaddText:のようなメソッドを定義したほうが便利なはずです。


viewController.h
#import <Foundation/Foundation.h>
#include "mruby.h"
#include "mruby/string.h"
#include "mruby/compile.h"
#include "mruby/class.h"
#include "mruby/value.h"
#include "mruby/proc.h"
#include "mruby/variable.h"
#include "mruby/array.h"

mrb_value mrb_printstr(mrb_state*, mrb_value);
void mrb_interrupt(mrb_state*, mrb_irep*, mrb_code*, mrb_value*);//別エントリで解説します。

@interface viewController : UIViewController{
    NSOperationQueue *_queue;
    NSBlockOperation *_operation;
    struct mrb_state *_mrb;
}
-(void)mrbInitPrint;
-(void)mrbRun:(NSString *)code;
-(IBAction)mrbQuit;//別エントリで解説します。
@end


viewController.m
//mruby-master/src/print.cからコピペしました。
mrb_value mrb_printstr(mrb_state *mrb, mrb_value self_){
    mrb_value argv;
    mrb_get_args(mrb, "o", &argv);
    struct RString *str;
    char *s;
    int len;
    if(mrb_string_p(argv)){
        str = mrb_str_ptr(argv);
        s = str->ptr;
        len = str->len;
        //以下3行を環境に応じて書き換えてください。
        AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
        UITextView *targetView = appDel.targetTextView;
        [targetView performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%s",s] waitUntilDone:YES];
    }
    return argv;
}
@implementation viewController
-(void)mrbInitPrint{
    //Kernelにp,puts,print,printfの定義に使う__printstr__を登録します。
    struct RClass *krn;
    krn = _mrb->kernel_module;
    mrb_define_method(_mrb, krn, "__printstr__", mrb_printstr, ARGS_REQ(1));

    //プロジェクトにバンドルしたprint.rbを読み込み、mrubyにロードします。
    NSError *error = nil;
    NSString *script_str;
    NSString *path = [[NSBundle mainBundle] pathForResource:[scripts objectAtIndex:i] ofType:@"rb"];
    NSString *print_rb_str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
    if(!error){
        mrb_load_string(_mrb, [print_rb_str cStringUsingEncoding:NSUTF8StringEncoding]);
    }
}
-(void)mrbRun:(NSString *)c{
    //mruby VMの実行を別スレッドにしている以外は、参考URLの通りです。
    if([_operation isExecuting]){ return; }
    __block NSString *code = [c copy];
    if(!_queue){
        _queue = [[NSOperationQueue alloc] init];
    }
    _operation = [[NSBlockOperation alloc] init];
    [_operation addExecutionBlock:^{
        const char *codeStr = [code cStringUsingEncoding:NSUTF8StringEncoding];
        mrbc_context *ctx;
        _mrb = mrb_open();

        [self mrbInitPrint];

        ctx = mrbc_context_new(_mrb);
        struct mrb_parser_state *st = mrb_parse_string(_mrb, codeStr, ctx);
        int gen_code_num = mrb_generate_code(_mrb, st);
        mrb_pool_close(st->pool);
        mrb_value result = mrb_run(_mrb, mrb_proc_new(_mrb,_mrb->irep[gen_code_num]), mrb_nil_value());
        if(_mrb->exc){//catch exception
            mrb_value r = mrb_funcall(_mrb, result, "inspect", 0);
            if(mrb_string_p(r)){
                struct RString *str = mrb_str_ptr(r);
                NSLog(@"%s",str->ptr);
            }
        }
        mrb_close(_mrb);
        mrbc_context_free(_mrb, ctx);
        _mrb = NULL;
        [_operation release];
        _operation = nil;
        [code release];
    }];
    [_queue addOperation:_operation];
}

0 件のコメント:

コメントを投稿