方針
__printstr__を(Objective-)Cで実装し、それを使ってRubyでp,puts,print,printfを定義する。mrubyのメソッドとして使うCの関数から、AppDelegate経由でUITextViewにアクセス。
参考
- 手探りでおぼえるmruby その1:クラスを定義する、メソッドを定義する
- mruby-master/mrblib/print.rb
- mruby-master/src/print.c
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 件のコメント:
コメントを投稿