Project

General

Profile

Feature #7701 ยป 0001-required-keyword-arguments.patch

nobu (Nobuyoshi Nakada), 02/16/2013 01:18 AM

View differences:

compile.c
if (args->kw_args) {
NODE *node = args->kw_args;
VALUE keywords = rb_ary_tmp_new(1);
int i = 0, j;
VALUE required = 0;
int i = 0, j, r = 0;
iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
COMPILE(optargs, "kwarg", args->kw_rest_arg);
while (node) {
rb_ary_push(keywords, INT2FIX(node->nd_body->nd_vid));
VALUE list = keywords;
if (node->nd_body->nd_value == (NODE *)-1) {
++r;
if (!required) required = rb_ary_tmp_new(1);
list = required;
}
rb_ary_push(list, INT2FIX(node->nd_body->nd_vid));
COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
node = node->nd_next;
i += 1;
}
iseq->arg_keyword_check = (args->kw_rest_arg->nd_vid & ID_SCOPE_MASK) == ID_JUNK;
iseq->arg_keywords = i;
iseq->arg_keyword_required = r;
iseq->arg_keyword_table = ALLOC_N(ID, i);
if (r) {
rb_ary_concat(required, keywords);
keywords = required;
}
for (j = 0; j < i; j++) {
iseq->arg_keyword_table[j] = FIX2INT(RARRAY_PTR(keywords)[j]);
}
......
}
case NODE_KW_ARG:{
LABEL *default_label = NEW_LABEL(nd_line(node));
LABEL *end_label = NEW_LABEL(nd_line(node));
LABEL *end_label = 0;
int idx, lv, ls;
ID id = node->nd_body->nd_vid;
......
default:
rb_bug("iseq_compile_each (NODE_KW_ARG): unknown node: %s", ruby_node_name(nd_type(node->nd_body)));
}
ADD_INSNL(ret, nd_line(node), jump, end_label);
if (node->nd_body->nd_value != (NODE *)-1) {
end_label = NEW_LABEL(nd_line(node));
ADD_INSNL(ret, nd_line(node), jump, end_label);
}
ADD_LABEL(ret, default_label);
COMPILE_POPED(ret, "keyword default argument", node->nd_body);
ADD_LABEL(ret, end_label);
if (node->nd_body->nd_value != (NODE *)-1) {
COMPILE_POPED(ret, "keyword default argument", node->nd_body);
ADD_LABEL(ret, end_label);
}
break;
}
case NODE_DSYM:{
parse.y
$$ = rb_assoc_new($$, $2);
%*/
}
| tLABEL
{
arg_var(formal_argument(get_id($1)));
$$ = assignable($1, (NODE *)-1);
/*%%%*/
$$ = NEW_KW_ARG(0, $$);
/*%
$$ = rb_assoc_new($$, 0);
%*/
}
;
f_block_kw : tLABEL primary_value
......
$$ = rb_assoc_new($$, $2);
%*/
}
| tLABEL
{
arg_var(formal_argument(get_id($1)));
$$ = assignable($1, (NODE *)-1);
/*%%%*/
$$ = NEW_KW_ARG(0, $$);
/*%
$$ = rb_assoc_new($$, 0);
%*/
}
;
f_block_kwarg : f_block_kw
test/ruby/test_keyword.rb
assert_valid_syntax("def bug7662(*, **) end")
assert_valid_syntax("def bug7662(a, **) end")
end
def test_required_keyword
feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
o = Object.new
assert_nothing_raised(SyntaxError, feature7701) do
eval("def o.foo(a:) a; end")
end
assert_raise(ArgumentError, feature7701) {o.foo}
assert_equal(42, o.foo(a: 42), feature7701)
end
def test_block_required_keyword
feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
b = assert_nothing_raised(SyntaxError, feature7701) do
break eval("proc {|a:| a}")
end
assert_raise(ArgumentError, feature7701) {b.call}
assert_equal(42, b.call(a: 42), feature7701)
end
end
vm_core.h
int arg_keyword;
int arg_keyword_check; /* if this is true, raise an ArgumentError when unknown keyword argument is passed */
int arg_keywords;
int arg_keyword_required;
ID *arg_keyword_table;
size_t stack_max; /* for stack overflow check */
vm_insnhelper.c
rb_exc_raise(exc);
}
NORETURN(static void keyword_error(const char *error, VALUE keys));
static void
keyword_error(const char *error, VALUE keys)
{
const char *msg = RARRAY_LEN(keys) == 1 ? "" : "s";
keys = rb_ary_join(keys, rb_usascii_str_new2(", "));
rb_raise(rb_eArgError, "%s keyword%s: %"PRIsVALUE, error, msg, keys);
}
NORETURN(static void unknown_keyword_error(const rb_iseq_t *iseq, VALUE hash));
static void
unknown_keyword_error(const rb_iseq_t *iseq, VALUE hash)
{
VALUE sep = rb_usascii_str_new2(", "), keys;
const char *msg;
VALUE keys;
int i;
for (i = 0; i < iseq->arg_keywords; i++) {
rb_hash_delete(hash, ID2SYM(iseq->arg_keyword_table[i]));
}
keys = rb_funcall(hash, rb_intern("keys"), 0, 0);
if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
msg = RARRAY_LEN(keys) == 1 ? "" : "s";
keys = rb_funcall(keys, rb_intern("join"), 1, sep);
rb_raise(rb_eArgError, "unknown keyword%s: %"PRIsVALUE, msg, keys);
keyword_error("unknown", keys);
}
void
......
argc--;
keyword_hash = rb_hash_dup(keyword_hash);
if (iseq->arg_keyword_check) {
for (i = j = 0; i < iseq->arg_keywords; i++) {
VALUE missing = Qnil;
for (i = 0; i < iseq->arg_keyword_required; i++) {
if (st_lookup(RHASH_TBL(keyword_hash), ID2SYM(iseq->arg_keyword_table[i]), 0))
continue;
if (NIL_P(missing)) missing = rb_ary_tmp_new(1);
rb_ary_push(missing, ID2SYM(iseq->arg_keyword_table[i]));
}
if (!NIL_P(missing)) {
keyword_error("missing", missing);
}
for (j = i; i < iseq->arg_keywords; i++) {
if (st_lookup(RHASH_TBL(keyword_hash), ID2SYM(iseq->arg_keyword_table[i]), 0)) j++;
}
if (RHASH_TBL(keyword_hash)->num_entries > (unsigned int) j) {
......
}
}
}
else if (iseq->arg_keyword_check && iseq->arg_keyword_required) {
VALUE missing = rb_ary_tmp_new(iseq->arg_keyword_required);
for (i = 0; i < iseq->arg_keyword_required; i++) {
rb_ary_push(missing, ID2SYM(iseq->arg_keyword_table[i]));
}
keyword_error("missing", missing);
}
else {
keyword_hash = rb_hash_new();
}
    (1-1/1)