Commit 9cafc5e1 authored by ksss's avatar ksss

Implement Proc#parameters

parent d4e49c9f
#include "mruby.h"
#include "mruby/proc.h"
#include "mruby/opcode.h"
#include "mruby/array.h"
#include "mruby/string.h"
#include "mruby/debug.h"
......@@ -122,6 +123,73 @@ mrb_kernel_proc(mrb_state *mrb, mrb_value self)
return blk;
}
/*
* call-seq:
* prc.parameters -> array
*
* Returns the parameter information of this proc.
*
* prc = lambda{|x, y=42, *other|}
* prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]]
*/
static mrb_value
mrb_proc_parameters(mrb_state *mrb, mrb_value self)
{
struct parameters_type {
int size;
const char *name;
} *p, parameters_list [] = {
{0, "req"},
{0, "opt"},
{0, "rest"},
{0, "req"},
{0, "block"},
{0, NULL}
};
const struct RProc *proc = mrb_proc_ptr(self);
const struct mrb_irep *irep = proc->body.irep;
mrb_aspec aspec;
mrb_value parameters;
int i, j;
if (MRB_PROC_CFUNC_P(proc)) {
// TODO cfunc aspec is not implemented yet
return mrb_ary_new(mrb);
}
if (!irep->lv) {
return mrb_ary_new(mrb);
}
if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
return mrb_ary_new(mrb);
}
if (!MRB_PROC_STRICT_P(proc)) {
parameters_list[0].name = "opt";
parameters_list[3].name = "opt";
}
aspec = GETARG_Ax(*irep->iseq);
parameters_list[0].size = MRB_ASPEC_REQ(aspec);
parameters_list[1].size = MRB_ASPEC_OPT(aspec);
parameters_list[2].size = MRB_ASPEC_REST(aspec);
parameters_list[3].size = MRB_ASPEC_POST(aspec);
parameters_list[4].size = MRB_ASPEC_BLOCK(aspec);
parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
for (i = 0, p = parameters_list; p->name; p++) {
mrb_value sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
for (j = 0; j < p->size; i++, j++) {
mrb_assert(i < (irep->nlocals-1));
mrb_ary_push(mrb, parameters, mrb_assoc_new(mrb,
sname,
mrb_symbol_value(irep->lv[i].name)
));
}
}
return parameters;
}
void
mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
{
......@@ -130,6 +198,7 @@ mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "to_s", mrb_proc_inspect, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "inspect", mrb_proc_inspect, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "parameters", mrb_proc_parameters, MRB_ARGS_NONE());
mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
......
......@@ -43,6 +43,18 @@ assert('Proc#curry') do
assert_raise(ArgumentError) { b.curry(1) }
end
assert('Proc#parameters') do
assert_equal([], Proc.new {}.parameters)
assert_equal([], Proc.new {||}.parameters)
assert_equal([[:opt, :a]], Proc.new {|a|}.parameters)
assert_equal([[:req, :a]], lambda {|a|}.parameters)
assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters)
assert_equal([[:req, :a]], ->(a){}.parameters)
assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters)
assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
end
assert('Proc#to_proc') do
proc = Proc.new {}
assert_equal proc, proc.to_proc
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment