虽然我们实作了好几个方法,但忘记了要处理方法回传值,在 mruby 中处理回传值也是相当简单的,因为编译器在生成指令的时候都已经帮我们处理好对应的机制,只需要将数值复制到正确的位置上就自然而然能够正常的将数值回传。
我们原本在 lib/iron/iron.h
定义的 mrb_func_t
是像这样
typedef void (*mrb_func_t)(mrb_state* mrb);
在 mruby 的设计中每个方法的定义都应该回传 mrb_value
作为回传值,也因此我们需要将他修正为
typedef mrb_value (*mrb_func_t)(mrb_state* mrb);
因为修改了 mrb_func_t
也因此之前定义的方法都会变成无法使用的状态,所以需要修正方法的实作,这边用 mrb_puts
当作示范。
mrb_value mrb_puts(mrb_state* mrb) {
int argc = mrb_get_argc(mrb);
mrb_value* argv = mrb_get_argv(mrb);
for(int i = 0; i < argc; i++) {
if(argv[i].tt == MRB_TT_STRING) {
printf("%s\n", mrb_string(argv[i]));
} else {
printf("%d\n", mrb_fixnum(argv[i]));
}
}
return mrb_nil_value();
}
实际上并不困难,只需要修改回传值以及加入 return mrb_nil_value()
回传即可。
在实际的 mruby 实作(CRuby 也是类似)还会传入
mrb_value self
当作参数,大多数时候都会是return self
但因为我们没有实作物件的概念,因此即使回传self
也会是nil
接下来我们需要把 OP_SEND
相关的处理加上修正,让回传值可以正确的被存到寄存器里面。
// lib/iron/vm.c
// ...
CASE(OP_SEND, BBB) {
const char* name = (const char*)irep_get(data, IREP_TYPE_SYMBOL, b);
khiter_t key = kh_get(mt, mrb->mt, name);
if (key == kh_end(mrb->mt)) {
DEBUG_LOG("func %s not found", name);
stack[a] = mrb_nil_value();
} else {
// ...
stack[a] = func(mrb);
}
NEXT;
}
CASE(OP_SENDB, BBB) {
const char* name = (const char*)irep_get(data, IREP_TYPE_SYMBOL, b);
khiter_t key = kh_get(mt, mrb->mt, name);
if (key == kh_end(mrb->mt)) {
DEBUG_LOG("func %s not found", name);
stack[a] = mrb_nil_value();
} else {
// ...
stack[a] = func(mrb);
}
NEXT;
}
// ...
实际上就是将找不到方法时直接回传 nil
确保不会抓取到错误的数值,同时将刚刚修改过的 mrb_func_t
回传值储存到原本要用来储存回传值的 stack[a]
上面。
经过前面实作 Block 的经验,我们会发现一个 Block 的运行单位刚好是 mrb_exec
而呼叫 return
的时候也刚好是从 mrb_exec
离开,因此我们也要将 mrb_exec
的回传值修改为 mrb_value
搭配其他机制。
先把原本在 lib/iron/vm.h
的 int
替换为 mrb_value
mrb_value mrb_exec(mrb_state* mrb, const uint8_t* irep);
然後修改实作将回传值以及用到 return
的地方都用 mrb_value
替代
// lib/iron/vm.c
mrb_value mrb_exec(mrb_state* mrb, const uint8_t* data) {
// ...
CASE(OP_BREAK, B) goto L_RETURN;
CASE(OP_RETURN, B) {
L_RETURN:
DEBUG_LOG("%s r[%d]", insn == OP_RETURN ? "return" : "break", a);
if (mrb->ctx && insn == OP_BREAK) {
mrb->ctx->block = 0;
}
return stack[a];
}
// ...
return mrb_nil_value();
}
如此一来我们就可以取用回传值来做一些处理了!
为了验证前面的修改能够正常使用,我们实做一个简单的 mrb_add
方法来确认。
sum = add(1, 2)
puts sum
mrb_value mrb_add(mrb_state* mrb) {
mrb_value* argv = mrb_get_argv(mrb);
int sum = mrb_fixnum(argv[0]) + mrb_fixnum(argv[1]);
return mrb_fixnum_value(sum);
}
// ...
mrb_define_method(mrb, "add", mrb_add);
调整测试的程序码加入上面两段程序後,运行 pio test
就可以看到 3
被正确的显示在萤幕上。
要注意上面的程序码只是参考不能直接使用
不过这个回传值处理还是非常阳春的,像是「阵列」就无法处理,这些情况就需要未来持续扩充我们的 Ruby VM 实作来支援。
<<: Arduino 扩充版 W5100 - EEPROM 烧录
想了很久要针对哪个主题进行资料分析实作,後来想来想去决定选择最常见的『网站』来进行资料分析的实作,那...
先简单回顾一下,今天预计分析的题目: Valid Parentheses 昨天问到,如果 ([)] ...
Python 还有很多不同功能的内建函式,以下列出一些满常用到的 数学相关 abs(x) 回传 x ...
很快地已经学了十天,今天又是一个新的开始,今天要来认识「阵列」。 阵列(Array)是由同型别的相关...
3 图的资料结构 今天来介绍我们储存一张图的时候,几种常见的资料结构:相邻矩阵(Adjacency ...