Keyword argument implemented.

parent 891839b9
......@@ -77,7 +77,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
|OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1))
|OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv)
|OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1))
|OP_ARGARY' |BS |R(a) = argument array (16=6:1:5:4)
|OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4)
|OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1)
|OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo
|OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo
......@@ -85,7 +85,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
|OP_RETURN' |B |return R(a) (normal)
|OP_RETURN_BLK' |B |return R(a) (in-block return)
|OP_BREAK' |B |break R(a)
|OP_BLKPUSH' |BS |R(a) = block (16=6:1:5:4)
|OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4)
|OP_ADD" |BB |R(a) = R(a)+R(a+1) (Syms[b]=:+)
|OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c) (Syms[b]=:+)
|OP_SUB" |BB |R(a) = R(a)-R(a+1) (Syms[b]=:-)
......@@ -207,7 +207,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
|OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1))
|OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv)
|OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1))
|OP_ARGARY' |BS |R(a) = argument array (16=6:1:5:4)
|OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4)
|OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1)
|OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo
|OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo
......@@ -215,7 +215,7 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
|OP_RETURN' |B |return R(a) (normal)
|OP_RETURN_BLK' |B |return R(a) (in-block return)
|OP_BREAK' |B |break R(a)
|OP_BLKPUSH' |BS |R(a) = block (16=6:1:5:4)
|OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4)
|OP_ADD" |BB |R(a) = R(a)+R(a+1) (Syms[b]=:+)
|OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c) (Syms[b]=:+)
|OP_SUB" |BB |R(a) = R(a)-R(a+1) (Syms[b]=:-)
......
......@@ -25,6 +25,7 @@ struct RHash {
#define mrb_hash_value(p) mrb_obj_value((void*)(p))
MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int);
MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
/*
* Initializes a new hash.
......@@ -110,7 +111,19 @@ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value
* @return An array with the keys of the hash.
*/
MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
/*
* Check if the hash has the key.
*
* Equivalent to:
*
* hash.key?(key)
*
* @param mrb The mruby state reference.
* @param hash The target hash.
* @param key The key to check existence.
* @return True if the hash has the key
*/
MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key);
/*
* Check if the hash is empty
......@@ -123,7 +136,7 @@ MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
* @param self The target hash.
* @return True if the hash is empty, false otherwise.
*/
MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
/*
* Gets an array of values.
......@@ -151,6 +164,16 @@ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
*/
MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
/*
* Copies the hash.
*
*
* @param mrb The mruby state reference.
* @param hash The target hash.
* @return The copy of the hash
*/
MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash);
/* declaration of struct kh_ht */
/* be careful when you touch the internal */
typedef struct {
......
......@@ -61,15 +61,16 @@ OPCODE(SEND, BBB) /* R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c)) */
OPCODE(SENDB, BBB) /* R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) */
OPCODE(CALL, Z) /* R(0) = self.call(frame.argc, frame.argv) */
OPCODE(SUPER, BB) /* R(a) = super(R(a+1),... ,R(a+b+1)) */
OPCODE(ARGARY, BS) /* R(a) = argument array (16=6:1:5:4) */
OPCODE(ENTER, W) /* arg setup according to flags (23=5:5:1:5:5:1:1) */
OPCODE(KARG, BB) /* R(a) = kdict[Syms(Bx)] # todo */
OPCODE(KARG2, BB) /* R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo */
OPCODE(ARGARY, BS) /* R(a) = argument array (16=m5:r1:m5:d1:lv4) */
OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */
OPCODE(KEY_P, BB) /* R(a) = kdict.key?(Syms(b)) # todo */
OPCODE(KEYEND, Z) /* raise unless kdict.empty? # todo */
OPCODE(KARG, BB) /* R(a) = kdict[Syms(b)]; kdict.delete(Syms(b)) # todo */
OPCODE(KDICT, B) /* R(a) = kdict # todo */
OPCODE(RETURN, B) /* return R(a) (normal) */
OPCODE(RETURN_BLK, B) /* return R(a) (in-block return) */
OPCODE(BREAK, B) /* break R(a) */
OPCODE(BLKPUSH, BS) /* R(a) = block (16=6:1:5:4) */
OPCODE(BLKPUSH, BS) /* R(a) = block (16=m5:r1:m5:d1:lv4) */
OPCODE(ADD, BB) /* R(a) = R(a)+R(a+1) (Syms[b]=:+) */
OPCODE(ADDI, BBB) /* R(a) = R(a)+mrb_int(c) (Syms[b]=:+) */
OPCODE(SUB, BB) /* R(a) = R(a)-R(a+1) (Syms[b]=:-) */
......
......@@ -420,6 +420,9 @@ gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
if (no_peephole(s)) {
normal:
genop_2(s, OP_MOVE, dst, src);
if (on_eval(s)) {
genop_0(s, OP_NOP);
}
return;
}
else {
......@@ -453,6 +456,25 @@ gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
}
}
static void
gen_return(codegen_scope *s, uint8_t op, uint16_t src)
{
if (no_peephole(s)) {
genop_1(s, op, src);
}
else {
struct mrb_insn_data data = mrb_last_insn(s);
if (data.insn == OP_MOVE && src == data.a) {
s->pc = s->lastpc;
genop_1(s, op, data.b);
}
else {
genop_1(s, op, src);
}
}
}
static void
gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst, uint16_t idx)
{
......@@ -514,30 +536,28 @@ dispatch_linked(codegen_scope *s, uint16_t pos)
#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
static void
push_(codegen_scope *s)
push_n_(codegen_scope *s, int n)
{
if (s->sp >= 0xffff) {
if (s->sp+n >= 0xffff) {
codegen_error(s, "too complex expression");
}
s->sp++;
s->sp+=n;
nregs_update;
}
static void
push_n_(codegen_scope *s, int n)
pop_n_(codegen_scope *s, int n)
{
if (s->sp+n >= 0xffff) {
codegen_error(s, "too complex expression");
if ((int)s->sp-n < 0) {
codegen_error(s, "stack pointer underflow");
}
s->sp+=n;
nregs_update;
s->sp-=n;
}
#define push() push_(s)
#define push() push_n_(s,1)
#define push_n(n) push_n_(s,n)
#define pop_(s) ((s)->sp--)
#define pop() pop_(s)
#define pop_n(n) (s->sp-=(n))
#define pop() pop_n_(s,1)
#define pop_n(n) pop_n_(s,n)
#define cursp() (s->sp)
static inline int
......@@ -644,8 +664,12 @@ node_len(node *tree)
return n;
}
#define nint(x) ((int)(intptr_t)(x))
#define nchar(x) ((char)(intptr_t)(x))
#define nsym(x) ((mrb_sym)(intptr_t)(x))
#define lv_name(lv) nsym((lv)->car)
static int
lv_idx(codegen_scope *s, mrb_sym id)
{
......@@ -694,7 +718,7 @@ for_body(codegen_scope *s, node *tree)
/* loop body */
codegen(s, tree->cdr->cdr->car, VAL);
pop();
genop_1(s, OP_RETURN, cursp());
gen_return(s, OP_RETURN, cursp());
loop_pop(s, NOVAL);
scope_finish(s);
s = prev;
......@@ -726,32 +750,44 @@ lambda_body(codegen_scope *s, node *tree, int blk)
int ma, oa, ra, pa, ka, kd, ba;
int pos, i;
node *n, *opt;
node *tail;
/* mandatory arguments */
ma = node_len(tree->car->car);
n = tree->car->car;
while (n) {
n = n->cdr;
}
tail = tree->car->cdr->cdr->cdr->cdr;
/* optional arguments */
oa = node_len(tree->car->cdr->car);
/* rest argument? */
ra = tree->car->cdr->cdr->car ? 1 : 0;
/* mandatory arugments after rest argument */
pa = node_len(tree->car->cdr->cdr->cdr->car);
ka = kd = 0;
ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
/* keyword arguments */
ka = tail? node_len(tail->cdr->car) : 0;
/* keyword dictionary? */
kd = tail && tail->cdr->cdr->car? 1 : 0;
/* block argument? */
ba = tail && tail->cdr->cdr->cdr->car ? 1 : 0;
if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
codegen_error(s, "too many formal arguments");
}
a = ((mrb_aspec)(ma & 0x1f) << 18)
| ((mrb_aspec)(oa & 0x1f) << 13)
| ((ra & 1) << 12)
| ((pa & 0x1f) << 7)
| ((ka & 0x1f) << 2)
| ((kd & 1)<< 1)
| (ba & 1);
s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
| ((ra & 1) << 5)
| (pa & 0x1f);
a = MRB_ARGS_REQ(ma)
| MRB_ARGS_OPT(oa)
| (ra? MRB_ARGS_REST() : 0)
| MRB_ARGS_POST(pa)
| MRB_ARGS_KEY(ka, kd)
| (ba? MRB_ARGS_BLOCK() : 0);
s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */
| ((ra & 0x1) << 6)
| ((pa & 0x1f) << 1)
| (kd & 0x1);
genop_W(s, OP_ENTER, a);
/* generate jump table for optional arguments initializer */
pos = new_label(s);
for (i=0; i<oa; i++) {
new_label(s);
......@@ -776,11 +812,50 @@ lambda_body(codegen_scope *s, node *tree, int blk)
if (oa > 0) {
dispatch(s, pos+i*3+1);
}
if (tail) {
node *kwds = tail->cdr->car;
int kwrest = 0;
if (tail->cdr->cdr->car) {
kwrest = 1;
}
mrb_assert(nint(tail->car) == NODE_ARGS_TAIL);
mrb_assert(node_len(tail) == 4);
while (kwds) {
int jmpif_key_p, jmp_def_set = -1;
node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car;
mrb_sym kwd_sym = nsym(kwd->cdr->car);
mrb_assert(nint(kwd->car) == NODE_KW_ARG);
if (def_arg) {
genop_2(s, OP_KEY_P, cursp(), new_sym(s, kwd_sym));
jmpif_key_p = genjmp2(s, OP_JMPIF, cursp(), 0, 0);
codegen(s, def_arg, VAL);
pop();
gen_move(s, lv_idx(s, kwd_sym), cursp(), 0);
jmp_def_set = genjmp(s, OP_JMP, 0);
dispatch(s, jmpif_key_p);
}
genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
if (jmp_def_set != -1) {
dispatch(s, jmp_def_set);
}
i++;
kwds = kwds->cdr;
}
if (tail->cdr->car && !kwrest) {
genop_0(s, OP_KEYEND);
}
}
}
codegen(s, tree->cdr->car, VAL);
pop();
if (s->pc > 0) {
genop_1(s, OP_RETURN, cursp());
gen_return(s, OP_RETURN, cursp());
}
if (blk) {
loop_pop(s, NOVAL);
......@@ -798,7 +873,7 @@ scope_body(codegen_scope *s, node *tree, int val)
}
codegen(scope, tree->cdr, VAL);
genop_1(scope, OP_RETURN, scope->sp-1);
gen_return(scope, OP_RETURN, scope->sp-1);
if (!s->iseq) {
genop_0(scope, OP_STOP);
}
......@@ -810,9 +885,6 @@ scope_body(codegen_scope *s, node *tree, int val)
return s->irep->rlen - 1;
}
#define nint(x) ((int)(intptr_t)(x))
#define nchar(x) ((char)(intptr_t)(x))
static mrb_bool
nosplat(node *t)
{
......@@ -1703,11 +1775,18 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_HASH:
case NODE_KW_HASH:
{
int len = 0;
mrb_bool update = FALSE;
while (tree) {
if (nt == NODE_KW_HASH &&
nint(tree->car->car->car) == NODE_KW_REST_ARGS) {
tree = tree->cdr;
continue;
}
codegen(s, tree->car->car, val);
codegen(s, tree->car->cdr, val);
len++;
......@@ -2055,10 +2134,10 @@ codegen(codegen_scope *s, node *tree, int val)
genop_1(s, OP_LOADNIL, cursp());
}
if (s->loop) {
genop_1(s, OP_RETURN_BLK, cursp());
gen_return(s, OP_RETURN_BLK, cursp());
}
else {
genop_1(s, OP_RETURN, cursp());
gen_return(s, OP_RETURN, cursp());
}
if (val) push();
break;
......@@ -2116,7 +2195,7 @@ codegen(codegen_scope *s, node *tree, int val)
else {
genop_1(s, OP_LOADNIL, cursp());
}
genop_1(s, OP_RETURN, cursp());
gen_return(s, OP_RETURN, cursp());
}
if (val) push();
break;
......@@ -2999,7 +3078,7 @@ loop_break(codegen_scope *s, node *tree)
if (!tree) {
genop_1(s, OP_LOADNIL, cursp());
}
genop_1(s, OP_BREAK, cursp());
gen_return(s, OP_BREAK, cursp());
}
}
}
......
......@@ -46,6 +46,7 @@ enum node_type {
NODE_ARRAY,
NODE_ZARRAY,
NODE_HASH,
NODE_KW_HASH,
NODE_RETURN,
NODE_YIELD,
NODE_LVAR,
......@@ -73,6 +74,9 @@ enum node_type {
NODE_DREGX_ONCE,
NODE_LIST,
NODE_ARG,
NODE_ARGS_TAIL,
NODE_KW_ARG,
NODE_KW_REST_ARGS,
NODE_ARGSCAT,
NODE_ARGSPUSH,
NODE_SPLAT,
......
This diff is collapsed.
......@@ -57,7 +57,7 @@ os_count_objects(mrb_state *mrb, mrb_value self)
hash = mrb_hash_new(mrb);
}
if (!mrb_test(mrb_hash_empty_p(mrb, hash))) {
if (!mrb_hash_empty_p(mrb, hash)) {
mrb_hash_clear(mrb, hash);
}
......
......@@ -149,7 +149,15 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
a = mrb_ary_new(mrb);
mrb_ary_push(mrb, a, sname);
if (i < max && irep->lv[i].name) {
mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
mrb_sym sym = irep->lv[i].name;
const char *name = mrb_sym2name(mrb, sym);
switch (name[0]) {
case '*': case '&':
break;
default:
mrb_ary_push(mrb, a, mrb_symbol_value(sym));
break;
}
}
mrb_ary_push(mrb, parameters, a);
}
......
......@@ -78,6 +78,16 @@ codedump(mrb_state *mrb, mrb_irep *irep)
printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep,
irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen);
if (irep->lv) {
int i;
printf("local variable names:\n");
for (i = 1; i < irep->nlocals; ++i) {
char const *n = mrb_sym2name(mrb, irep->lv[i - 1].name);
printf(" R%d:%s\n", irep->lv[i - 1].r, n? n : "");
}
}
pc = irep->iseq;
pcend = pc + irep->ilen;
while (pc < pcend) {
......@@ -246,10 +256,11 @@ codedump(mrb_state *mrb, mrb_irep *irep)
printf("OP_SUPER\tR%d\t%d\n", a, b);
break;
CASE(OP_ARGARY, BS):
printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", a,
(b>>10)&0x3f,
(b>>9)&0x1,
(b>>4)&0x1f,
printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a,
(b>>11)&0x3f,
(b>>10)&0x1,
(b>>5)&0x1f,
(b>>4)&0x1,
(b>>0)&0xf);
print_lv_a(mrb, irep, a);
break;
......@@ -263,32 +274,39 @@ codedump(mrb_state *mrb, mrb_irep *irep)
(a>>1)&0x1,
a & 0x1);
break;
CASE(OP_KARG, BB):
printf("OP_KARG\tR(%d)\tK(%d)\n", a, b);
CASE(OP_KEY_P, BB):
printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
print_lv_a(mrb, irep, a);
break;
CASE(OP_KARG2, BB):
printf("OP_KARG2\tR(%d)\tK(%d)\n", a, b);
CASE(OP_KEYEND, Z):
printf("OP_KEYEND\n");
break;
CASE(OP_KARG, BB):
printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
print_lv_a(mrb, irep, a);
break;
CASE(OP_KDICT, B):
printf("OP_KDICt\tR(%d)\n", a);
printf("OP_KDICT\tR%d\t\t", a);
print_lv_a(mrb, irep, a);
break;
CASE(OP_RETURN, B):
printf("OP_RETURN\tR%d", a);
printf("OP_RETURN\tR%d\t\t", a);
print_lv_a(mrb, irep, a);
break;
CASE(OP_RETURN_BLK, B):
printf("OP_RETURN_BLK\tR%d", a);
printf("OP_RETURN_BLK\tR%d\t\t", a);
print_lv_a(mrb, irep, a);
break;
CASE(OP_BREAK, B):
printf("OP_BREAK\tR%d", a);
printf("OP_BREAK\tR%d\t\t", a);
print_lv_a(mrb, irep, a);
break;
CASE(OP_BLKPUSH, BS):
printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", a,
(b>>10)&0x3f,
(b>>9)&0x1,
(b>>4)&0x1f,
printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a,
(b>>11)&0x3f,
(b>>10)&0x1,
(b>>5)&0x1f,
(b>>4)&0x1,
(b>>0)&0xf);
print_lv_a(mrb, irep, a);
break;
......
......@@ -208,6 +208,54 @@ mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
return vret;
}
void
mrb_hash_check_kdict(mrb_state *mrb, mrb_value self)
{
khash_t(ht) *orig_h;
khiter_t k;
int nosym = FALSE;
orig_h = RHASH_TBL(self);
if (!orig_h || kh_size(orig_h) == 0) return;
for (k = kh_begin(orig_h); k != kh_end(orig_h); k++) {
if (kh_exist(orig_h, k)) {
mrb_value key = kh_key(orig_h, k);
if (!mrb_symbol_p(key)) nosym = TRUE;
}
}
if (nosym) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys");
}
}
MRB_API mrb_value
mrb_hash_dup(mrb_state *mrb, mrb_value self)
{
struct RHash* copy;
khash_t(ht) *orig_h;
orig_h = RHASH_TBL(self);
copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
copy->ht = kh_init(ht, mrb);
if (orig_h && kh_size(orig_h) > 0) {
int ai = mrb_gc_arena_save(mrb);
khash_t(ht) *copy_h = copy->ht;
khiter_t k, copy_k;
for (k = kh_begin(orig_h); k != kh_end(orig_h); k++) {
if (kh_exist(orig_h, k)) {
copy_k = kh_put(ht, mrb, copy_h, KEY(kh_key(orig_h, k)));
mrb_gc_arena_restore(mrb, ai);
kh_val(copy_h, copy_k).v = kh_val(orig_h, k).v;
kh_val(copy_h, copy_k).n = kh_size(copy_h)-1;
}
}
}
return mrb_obj_value(copy);
}
MRB_API mrb_value
mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
{
......@@ -716,13 +764,21 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self)
* {}.empty? #=> true
*
*/
MRB_API mrb_value
MRB_API mrb_bool
mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
{
khash_t(ht) *h = RHASH_TBL(self);
if (h) return mrb_bool_value(kh_size(h) == 0);
return mrb_true_value();
if (h) return kh_size(h) == 0;
return TRUE;
}
static mrb_value
mrb_hash_empty_m(mrb_state *mrb, mrb_value self)
{
if (mrb_hash_empty_p(mrb, self))
return mrb_true_value();
return mrb_false_value();
}
/* 15.2.13.4.29 (x)*/
......@@ -833,21 +889,29 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash)
*
*/
static mrb_value
mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
MRB_API mrb_bool
mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key)
{
mrb_value key;
khash_t(ht) *h;
khiter_t k;
mrb_get_args(mrb, "o", &key);
h = RHASH_TBL(hash);
if (h) {
k = kh_get(ht, mrb, h, key);
return mrb_bool_value(k != kh_end(h));
return k != kh_end(h);
}
return mrb_false_value();
return FALSE;
}
static mrb_value
mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
{
mrb_value key;
mrb_bool key_p;
mrb_get_args(mrb, "o", &key);
key_p = mrb_hash_key_p(mrb, hash, key);
return mrb_bool_value(key_p);
}
/* 15.2.13.4.14 */
......@@ -904,7 +968,7 @@ mrb_init_hash(mrb_state *mrb)
mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */
mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */
mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */
mrb_define_method(mrb, h, "empty?", mrb_hash_empty_p, MRB_ARGS_NONE()); /* 15.2.13.4.12 */
mrb_define_method(mrb, h, "empty?", mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */
mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */
mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */
mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */
......
......@@ -1194,7 +1194,15 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
if (!irep->lv) break;
for (i = 0; i + 1 < irep->nlocals; ++i) {
if (irep->lv[i].name) {
mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
mrb_sym sym = irep->lv[i].name;
const char *name = mrb_sym2name(mrb, sym);
switch (name[0]) {
case '*': case '&':
break;
default:
mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
break;
}
}
}
if (!MRB_PROC_ENV_P(proc)) break;
......
......@@ -969,6 +969,8 @@ check_target_class(mrb_state *mrb)
return TRUE;
}
void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
MRB_API mrb_value
mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
{
......@@ -1639,9 +1641,10 @@ RETRY_TRY_BLOCK:
}
CASE(OP_ARGARY, BS) {
int m1 = (b>>10)&0x3f;
int r = (b>>9)&0x1;
int m2 = (b>>4)&0x1f;
int m1 = (b>>11)&0x3f;
int r = (b>>10)&0x1;
int m2 = (b>>5)&0x1f;
int kd = (b>>4)&0x1;
int lv = (b>>0)&0xf;
mrb_value *stack;
......@@ -1657,12 +1660,12 @@ RETRY_TRY_BLOCK:
else {
struct REnv *e = uvenv(mrb, lv-1);
if (!e) goto L_NOSUPER;
if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1)
if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1)
goto L_NOSUPER;
stack = e->stack + 1;
}
if (r == 0) {
regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack);
regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack);
}
else {
mrb_value *pp = NULL;
......@@ -1675,7 +1678,7 @@ RETRY_TRY_BLOCK:
pp = ARY_PTR(ary);
len = (int)ARY_LEN(ary);
}
regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd);
rest = mrb_ary_ptr(regs[a]);
if (m1 > 0) {
stack_copy(ARY_PTR(rest), stack, m1);
......@@ -1686,7 +1689,10 @@ RETRY_TRY_BLOCK:
if (m2 > 0) {
stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2);
}
ARY_SET_LEN(rest, m1+len+m2);
if (kd) {
stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd);
}
ARY_SET_LEN(rest, m1+len+m2+kd);
}
regs[a+1] = stack[m1+r+m2];
mrb_gc_arena_restore(mrb, ai);
......@@ -1698,74 +1704,114 @@ RETRY_TRY_BLOCK:
int o = MRB_ASPEC_OPT(a);
int r = MRB_ASPEC_REST(a);
int m2 = MRB_ASPEC_POST(a);
int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0;
/* unused
int k = MRB_ASPEC_KEY(a);
int kd = MRB_ASPEC_KDICT(a);
int b = MRB_ASPEC_BLOCK(a);
*/
int argc = mrb->c->ci->argc;
mrb_value *argv = regs+1;
mrb_value *argv0 = argv;
int len = m1 + o + r + m2;
mrb_value * const argv0 = argv;
int const len = m1 + o + r + m2;
int const blk_pos = len + kd + 1;
mrb_value *blk = &argv[argc < 0 ? 1 : argc];
mrb_value kdict;
int kargs = kd;
/* arguments is passed with Array */
if (argc < 0) {
struct RArray *ary = mrb_ary_ptr(regs[1]);
argv = ARY_PTR(ary);
argc = (int)ARY_LEN(ary);
mrb_gc_protect(mrb, regs[1]);
}
/* strict argument check */
if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
if (argc >= 0) {
if (argc < m1 + m2 || (r == 0 && argc > len)) {
if (argc >= 0 && !(argc <= 1 && kd)) {
if (argc < m1 + m2 + kd || (r == 0 && argc > len + kd)) {
argnum_error(mrb, m1+m2);
goto L_RAISE;
}
}
}
/* extract first argument array to arguments */
else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
mrb_gc_protect(mrb, argv[0]);
argc = (int)RARRAY_LEN(argv[0]);
argv = RARRAY_PTR(argv[0]);
}
if (kd) {
/* check last arguments is hash if method takes keyword arguments */
if (argc == m1+m2) {
kdict = mrb_hash_new(mrb);
kargs = 0;
}
else {
if (!mrb_hash_p(argv[argc - 1])) {
if (r) {
kdict = mrb_hash_new(mrb);
kargs = 0;
}
else {
mrb_value str = mrb_str_new_lit(mrb, "Excepcted `Hash` as last argument for keyword arguments");
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
}
else {
kdict = argv[argc-1];
}
mrb_hash_check_kdict(mrb, kdict);
if (MRB_ASPEC_KEY(a) > 0) {
kdict = mrb_hash_dup(mrb, kdict);
}
}
}
/* no rest arguments */
if (argc < len) {
int mlen = m2;
if (argc < m1+m2) {
if (m1 < argc)
mlen = argc - m1;
else
mlen = 0;
mlen = m1 < argc ? argc - m1 : 0;
}
regs[len+1] = *blk; /* move block */
regs[blk_pos] = *blk; /* move block */
if (kd) regs[len + 1] = kdict;
SET_NIL_VALUE(regs[argc+1]);
/* copy mandatory and optional arguments */
if (argv0 != argv) {
value_move(&regs[1], argv, argc-mlen); /* m1 + o */
}
if (argc < m1) {
stack_clear(&regs[argc+1], m1-argc);
}
/* copy post mandatory arguments */
if (mlen) {
value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
}
if (mlen < m2) {
stack_clear(&regs[len-m2+mlen+1], m2-mlen);
}
/* initalize rest arguments with empty Array */
if (r) {
regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
}
if (o > 0 && argc >= m1+m2)
pc += (argc - m1 - m2)*3;
/* skip initailizer of passed arguments */
if (o > 0 && argc-kargs >= m1+m2)
pc += (argc - kargs - m1 - m2)*3;
}
else {
int rnum = 0;
if (argv0 != argv) {
regs[len+1] = *blk; /* move block */
regs[blk_pos] = *blk; /* move block */
if (kd) regs[len + 1] = kdict;
value_move(&regs[1], argv, m1+o);
}
if (r) {
mrb_value ary;
rnum = argc-m1-o-m2;
rnum = argc-m1-o-m2-kargs;
ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
regs[m1+o+1] = ary;
}
......@@ -1775,29 +1821,60 @@ RETRY_TRY_BLOCK:
}
}
if (argv0 == argv) {
regs[len+1] = *blk; /* move block */
regs[blk_pos] = *blk; /* move block */
if (kd) regs[len + 1] = kdict;
}
pc += o*3;
}
mrb->c->ci->argc = len;
/* format arguments for generated code */
mrb->c->ci->argc = len + kd;
/* clear local (but non-argument) variables */
if (irep->nlocals-len-2 > 0) {
stack_clear(&regs[len+2], irep->nlocals-len-2);
if (irep->nlocals-blk_pos-1 > 0) {
stack_clear(&regs[blk_pos+1], irep->nlocals-blk_pos-1);
}
JUMP;
}
CASE(OP_KARG, BB) {
/* not implemented yet */
mrb_value k = mrb_symbol_value(syms[b]);
mrb_value kdict = regs[mrb->c->ci->argc];
if (!mrb_hash_key_p(mrb, kdict, k)) {
mrb_value str = mrb_format(mrb, "missing keyword: %S", k);
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
regs[a] = mrb_hash_get(mrb, kdict, k);
mrb_hash_delete_key(mrb, kdict, k);
NEXT;
}
CASE(OP_KARG2, BB) {
/* not implemented yet */
CASE(OP_KEY_P, BB) {
mrb_value k = mrb_symbol_value(syms[b]);
mrb_value kdict = regs[mrb->c->ci->argc];
mrb_bool key_p = mrb_hash_key_p(mrb, kdict, k);
regs[a] = mrb_bool_value(key_p);
NEXT;
}
CASE(OP_KEYEND, Z) {
mrb_value kdict = regs[mrb->c->ci->argc];
if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) {
mrb_value keys = mrb_hash_keys(mrb, kdict);
mrb_value key1 = RARRAY_PTR(keys)[0];
mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1);
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
NEXT;
}
CASE(OP_KDICT, B) {
/* not implemented yet */
regs[a] = regs[mrb->c->ci->argc];
NEXT;
}
......@@ -2064,9 +2141,10 @@ RETRY_TRY_BLOCK:
}
CASE(OP_BLKPUSH, BS) {
int m1 = (b>>10)&0x3f;
int r = (b>>9)&0x1;
int m2 = (b>>4)&0x1f;
int m1 = (b>>11)&0x3f;
int r = (b>>10)&0x1;
int m2 = (b>>5)&0x1f;
int kd = (b>>4)&0x1;
int lv = (b>>0)&0xf;
mrb_value *stack;
......@@ -2084,7 +2162,7 @@ RETRY_TRY_BLOCK:
localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
goto L_RAISE;
}
regs[a] = stack[m1+r+m2];
regs[a] = stack[m1+r+m2+kd];
NEXT;
}
......
......@@ -403,6 +403,9 @@ assert('External command execution.') do
assert_equal 'test dynamic `', t
assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results
results = []
assert_equal 'test sym test sym test', `test #{:sym} test #{:sym} test`
alias_method sym, :old_cmd
end
true
......@@ -466,3 +469,183 @@ this is a comment that has extra after =begin and =end with tabs after it
=end xxxxxxxxxxxxxxxxxxxxxxxxxx
assert_equal(line + 4, __LINE__)
end
assert 'keyword arguments' do
def m(a, b:) [a, b] end
assert_equal [1, 2], m(1, b: 2)
assert_raise(ArgumentError) { m b: 1 }
assert_raise(ArgumentError) { m 1 }
def m(a:) a end
assert_equal 1, m(a: 1)
assert_raise(ArgumentError) { m }
assert_raise(ArgumentError) { m 'a' => 1, a: 1 }
h = { a: 1 }
assert_equal 1, m(h)
assert_equal({ a: 1 }, h)
def m(a: 1) a end
assert_equal 1, m
assert_equal 2, m(a: 2)
assert_raise(ArgumentError) { m 1 }
def m(**) end
assert_nil m
assert_nil m a: 1, b: 2
assert_raise(ArgumentError) { m 2 }
def m(a, **) a end
assert_equal 1, m(1)
assert_equal 1, m(1, a: 2, b: 3)
assert_equal({ 'a' => 1, b: 2 }, m('a' => 1, b: 2))
def m(a, **k) [a, k] end
assert_equal [1, {}], m(1)
assert_equal [1, {a: 2, b: 3}], m(1, a: 2, b: 3)
assert_equal [{'a' => 1, b: 2}, {}], m('a' => 1, b: 2)
def m(a=1, **) a end
assert_equal 1, m
assert_equal 2, m(2, a: 1, b: 0)
assert_raise(ArgumentError) { m('a' => 1, a: 2) }
def m(a=1, **k) [a, k] end
assert_equal [1, {}], m
assert_equal [2, {a: 1, b: 2}], m(2, a: 1, b: 2)
assert_equal [{a: 1}, {b: 2}], m({a: 1}, {b: 2})
def m(*, a:) a end
assert_equal 1, m(a: 1)
assert_equal 3, m(1, 2, a: 3)
assert_raise(ArgumentError) { m('a' => 1, a: 2) }
def m(*a, b:) [a, b] end
assert_equal [[], 1], m(b: 1)
assert_equal [[1, 2], 3], m(1, 2, b: 3)
assert_raise(ArgumentError) { m('a' => 1, b: 2) }
def m(*a, b: 1) [a, b] end
assert_equal [[], 1], m
assert_equal [[1, 2, 3], 4], m(1, 2, 3, b: 4)
assert_raise(ArgumentError) { m('a' => 1, b: 2) }
def m(*, **) end
assert_nil m()
assert_nil m(a: 1, b: 2)
assert_nil m(1, 2, 3, a: 4, b: 5)
def m(*a, **) a end
assert_equal [], m()
assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5)
assert_raise(ArgumentError) { m("a" => 1, a: 1) }
assert_equal [1], m(1, **{a: 2})
def m(*, **k) k end
assert_equal({}, m())
assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5))
assert_raise(ArgumentError) { m("a" => 1, a: 1) }
def m(a = nil, b = nil, **k) [a, k] end
assert_equal [nil, {}], m()
assert_equal([nil, {a: 1}], m(a: 1))
assert_raise(ArgumentError) { m("a" => 1, a: 1) }
assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1))
assert_equal([{a: 1}, {}], m({a: 1}, {}))
assert_equal([nil, {}], m({}))
def m(*a, **k) [a, k] end
assert_equal([[], {}], m())
assert_equal([[1], {}], m(1))
assert_equal([[], {a: 1, b: 2}], m(a: 1, b: 2))
assert_equal([[1, 2, 3], {a: 2}], m(1, 2, 3, a: 2))
assert_raise(ArgumentError) { m("a" => 1, a: 1) }
assert_raise(ArgumentError) { m("a" => 1) }
assert_equal([[], {a: 1}], m(a: 1))
assert_raise(ArgumentError) { m("a" => 1, a: 1) }
assert_equal([[{"a" => 1}], {a: 1}], m({ "a" => 1 }, a: 1))
assert_equal([[{a: 1}], {}], m({a: 1}, {}))
assert_raise(ArgumentError) { m({a: 1}, {"a" => 1}) }
def m(a:, b:) [a, b] end
assert_equal([1, 2], m(a: 1, b: 2))
assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
def m(a:, b: 1) [a, b] end
assert_equal([1, 1], m(a: 1))
assert_equal([1, 2], m(a: 1, b: 2))
assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
def m(a:, **) a end
assert_equal(1, m(a: 1))
assert_equal(1, m(a: 1, b: 2))
assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
def m(a:, **k) [a, k] end
assert_equal([1, {}], m(a: 1))
assert_equal([1, {b: 2, c: 3}], m(a: 1, b: 2, c: 3))
assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
=begin
def m(a:, &b) [a, b] end
assert_equal([1, nil], m(a: 1))
assert_equal([1, l], m(a: 1, &(l = ->{})))
=end
def m(a: 1, b:) [a, b] end
assert_equal([1, 0], m(b: 0))
assert_equal([3, 2], m(b: 2, a: 3))
assert_raise(ArgumentError) { m a: 1 }
def m(a: def m(a: 1) a end, b:)
[a, b]
end
assert_equal([2, 3], m(a: 2, b: 3))
assert_equal([:m, 1], m(b: 1))
# Note the default value of a: in the original method.
assert_equal(1, m())
def m(a: 1, b: 2) [a, b] end
assert_equal([1, 2], m())
assert_equal([4, 3], m(b: 3, a: 4))
def m(a: 1, **) a end
assert_equal(1, m())
assert_equal(2, m(a: 2, b: 1))
def m(a: 1, **k) [a, k] end
assert_equal([1, {b: 2, c: 3}], m(b: 2, c: 3))
def m(a:, **) yield end
assert_raise(ArgumentError) { m { :blk } }
assert_equal :blk, m(a: 1){ :blk }
def m(a:, **k, &b) [b.call, k] end
assert_raise(ArgumentError) { m { :blk } }
assert_equal [:blk, {b: 2}], m(a: 1, b: 2){ :blk }
def m(**k, &b) [k, b] end
assert_equal([{ a: 1, b: 2}, nil], m(a: 1, b: 2))
assert_equal :blk, m{ :blk }[1].call
def m(hsh = {}) hsh end
assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2))
assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2))
def m(hsh) hsh end
assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2))
assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2))
=begin
def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)
[a, b, c, d, e, f, g, h, k, l]
end
result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
assert_equal([9, 8, [7], [], 6, 5, 4, 3, {}, l], result)
def m a, b=1, *c, d, e:, f: 2, g:, **k, &l
[a, b, c, d, e, f, g, k, l]
end
result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
assert_equal([1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l], result)
=end
end
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