Feature #1866 ยป redefinable_not.patch
ChangeLog | ||
---|---|---|
Mon Aug 3 15:04:01 2009 URABE Shyouhei <shyouhei@ruby-lang.org>
|
||
* parse.y (expr): bug fix to prevent SEGV
|
||
Sun Aug 2 21:23:49 2009 URABE Shyouhei <shyouhei@ruby-lang.org>
|
||
* parse.y (expr): redefinable not operators. Backport from trunk.
|
||
* parse.y (op): ditto.
|
||
* parse.y (arg): ditto.
|
||
* object.c (rb_obj_not_match): new method.
|
||
* object.c (rb_obj_not_equal): ditto.
|
||
* object.c (rb_obj_not): ditto.
|
||
Sun Aug 2 06:08:17 2009 URABE Shyouhei <shyouhei@ruby-lang.org>
|
||
* node.h (rb_thread_status): ISO C89 do not allow a comma at the end of enum.
|
object.c | ||
---|---|---|
VALUE rb_cFalseClass;
|
||
VALUE rb_cSymbol;
|
||
static ID id_eq, id_eql, id_inspect, id_init_copy;
|
||
static ID id_eq, id_eql, id_match, id_inspect, id_init_copy;
|
||
/*
|
||
* call-seq:
|
||
... | ... | |
return rb_obj_id(obj);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* !obj => true or false
|
||
*
|
||
* Boolean negate.
|
||
*/
|
||
VALUE
|
||
rb_obj_not(VALUE obj)
|
||
{
|
||
return RTEST(obj) ? Qfalse : Qtrue;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* obj != other => true or false
|
||
*
|
||
* Returns true if two objects are not-equal, otherwise false.
|
||
*/
|
||
VALUE
|
||
rb_obj_not_equal(VALUE obj1, VALUE obj2)
|
||
{
|
||
VALUE result = rb_funcall(obj1, id_eq, 1, obj2);
|
||
return RTEST(result) ? Qfalse : Qtrue;
|
||
}
|
||
VALUE
|
||
rb_class_real(cl)
|
||
VALUE cl;
|
||
... | ... | |
return Qfalse;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* obj !~ other => true or false
|
||
*
|
||
* Returns true if two objects do not match (using the <i>=~</i>
|
||
* method), otherwise false.
|
||
*/
|
||
static VALUE
|
||
rb_obj_not_match(VALUE obj1, VALUE obj2)
|
||
{
|
||
VALUE result = rb_funcall(obj1, id_match, 1, obj2);
|
||
return RTEST(result) ? Qfalse : Qtrue;
|
||
}
|
||
/**********************************************************************
|
||
* Document-class: Symbol
|
||
*
|
||
... | ... | |
rb_define_method(rb_mKernel, "nil?", rb_false, 0);
|
||
rb_define_method(rb_mKernel, "==", rb_obj_equal, 1);
|
||
rb_define_method(rb_mKernel, "!", rb_obj_not, 0);
|
||
rb_define_method(rb_mKernel, "!=", rb_obj_not_equal, 1);
|
||
rb_define_method(rb_mKernel, "equal?", rb_obj_equal, 1);
|
||
rb_define_method(rb_mKernel, "===", rb_equal, 1);
|
||
rb_define_method(rb_mKernel, "=~", rb_obj_pattern_match, 1);
|
||
rb_define_method(rb_mKernel, "!~", rb_obj_not_match, 1);
|
||
rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1);
|
||
... | ... | |
id_eq = rb_intern("==");
|
||
id_eql = rb_intern("eql?");
|
||
id_match = rb_intern("match");
|
||
id_inspect = rb_intern("inspect");
|
||
id_init_copy = rb_intern("initialize_copy");
|
||
}
|
parse.y | ||
---|---|---|
static NODE *cond();
|
||
static NODE *logop();
|
||
static int cond_negative();
|
||
static NODE *newline_node();
|
||
static void fixpos();
|
||
... | ... | |
{
|
||
$$ = NEW_IF(cond($3), remove_begin($1), 0);
|
||
fixpos($$, $3);
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
$$->nd_else = $$->nd_body;
|
||
$$->nd_body = 0;
|
||
}
|
||
}
|
||
| stmt kUNLESS_MOD expr_value
|
||
{
|
||
$$ = NEW_UNLESS(cond($3), remove_begin($1), 0);
|
||
fixpos($$, $3);
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
$$->nd_body = $$->nd_else;
|
||
$$->nd_else = 0;
|
||
}
|
||
}
|
||
| stmt kWHILE_MOD expr_value
|
||
{
|
||
... | ... | |
else {
|
||
$$ = NEW_WHILE(cond($3), $1, 1);
|
||
}
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
nd_set_type($$, NODE_UNTIL);
|
||
}
|
||
}
|
||
| stmt kUNTIL_MOD expr_value
|
||
{
|
||
... | ... | |
else {
|
||
$$ = NEW_UNTIL(cond($3), $1, 1);
|
||
}
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
nd_set_type($$, NODE_WHILE);
|
||
}
|
||
}
|
||
| stmt kRESCUE_MOD stmt
|
||
{
|
||
... | ... | |
}
|
||
| kNOT expr
|
||
{
|
||
$$ = NEW_NOT(cond($2));
|
||
$$ = call_op($2, '!', 0, 0);
|
||
}
|
||
| '!' command_call
|
||
{
|
||
$$ = NEW_NOT(cond($2));
|
||
$$ = call_op($2, '!', 0, 0);
|
||
}
|
||
| arg
|
||
;
|
||
... | ... | |
| tEQ { $$ = tEQ; }
|
||
| tEQQ { $$ = tEQQ; }
|
||
| tMATCH { $$ = tMATCH; }
|
||
| tNMATCH { $$ = tNMATCH; }
|
||
| '>' { $$ = '>'; }
|
||
| tGEQ { $$ = tGEQ; }
|
||
| '<' { $$ = '<'; }
|
||
| tLEQ { $$ = tLEQ; }
|
||
| tNEQ { $$ = tNEQ; }
|
||
| tLSHFT { $$ = tLSHFT; }
|
||
| tRSHFT { $$ = tRSHFT; }
|
||
| '+' { $$ = '+'; }
|
||
... | ... | |
| '/' { $$ = '/'; }
|
||
| '%' { $$ = '%'; }
|
||
| tPOW { $$ = tPOW; }
|
||
| '!' { $$ = '!'; }
|
||
| '~' { $$ = '~'; }
|
||
| tUPLUS { $$ = tUPLUS; }
|
||
| tUMINUS { $$ = tUMINUS; }
|
||
... | ... | |
}
|
||
| arg tNEQ arg
|
||
{
|
||
$$ = NEW_NOT(call_op($1, tEQ, 1, $3));
|
||
$$ = call_op($1, tNEQ, 1, $3);
|
||
}
|
||
| arg tMATCH arg
|
||
{
|
||
... | ... | |
}
|
||
| arg tNMATCH arg
|
||
{
|
||
$$ = NEW_NOT(match_gen($1, $3));
|
||
$$ = call_op($1, tNMATCH, 1, $3);
|
||
}
|
||
| '!' arg
|
||
{
|
||
$$ = NEW_NOT(cond($2));
|
||
$$ = call_op($2, '!', 0, 0);
|
||
}
|
||
| '~' arg
|
||
{
|
||
... | ... | |
{
|
||
$$ = NEW_IF(cond($2), $4, $5);
|
||
fixpos($$, $2);
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
NODE *tmp = $$->nd_body;
|
||
$$->nd_body = $$->nd_else;
|
||
$$->nd_else = tmp;
|
||
}
|
||
}
|
||
| kUNLESS expr_value then
|
||
compstmt
|
||
... | ... | |
{
|
||
$$ = NEW_UNLESS(cond($2), $4, $5);
|
||
fixpos($$, $2);
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
NODE *tmp = $$->nd_body;
|
||
$$->nd_body = $$->nd_else;
|
||
$$->nd_else = tmp;
|
||
}
|
||
}
|
||
| kWHILE {COND_PUSH(1);} expr_value do {COND_POP();}
|
||
compstmt
|
||
... | ... | |
{
|
||
$$ = NEW_WHILE(cond($3), $6, 1);
|
||
fixpos($$, $3);
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
nd_set_type($$, NODE_UNTIL);
|
||
}
|
||
}
|
||
| kUNTIL {COND_PUSH(1);} expr_value do {COND_POP();}
|
||
compstmt
|
||
... | ... | |
{
|
||
$$ = NEW_UNTIL(cond($3), $6, 1);
|
||
fixpos($$, $3);
|
||
if (cond_negative(&$$->nd_cond)) {
|
||
nd_set_type($$, NODE_WHILE);
|
||
}
|
||
}
|
||
| kCASE expr_value opt_terms
|
||
case_body
|
||
... | ... | |
return c;
|
||
case '!':
|
||
lex_state = EXPR_BEG;
|
||
if ((c = nextc()) == '=') {
|
||
c = nextc();
|
||
if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
|
||
lex_state = EXPR_ARG;
|
||
if (c == '@') {
|
||
return '!';
|
||
}
|
||
}
|
||
else {
|
||
lex_state = EXPR_BEG;
|
||
}
|
||
if (c == '=') {
|
||
return tNEQ;
|
||
}
|
||
if (c == '~') {
|
||
... | ... | |
return NEW_NODE(type, left, right, 0);
|
||
}
|
||
static int
|
||
cond_negative(nodep)
|
||
NODE **nodep;
|
||
{
|
||
NODE *c = *nodep;
|
||
if (!c) return 0;
|
||
switch (nd_type(c)) {
|
||
case NODE_NOT:
|
||
*nodep = c->nd_body;
|
||
return 1;
|
||
case NODE_NEWLINE:
|
||
if (c->nd_next && nd_type(c->nd_next) == NODE_NOT) {
|
||
c->nd_next = c->nd_next->nd_body;
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
static void
|
||
no_blockarg(node)
|
||
NODE *node;
|
||
... | ... | |
{'|', "|"},
|
||
{'^', "^"},
|
||
{'&', "&"},
|
||
{'!', "!"},
|
||
{tCMP, "<=>"},
|
||
{'>', ">"},
|
||
{tGEQ, ">="},
|
||
... | ... | |
{tNEQ, "!="},
|
||
{tMATCH, "=~"},
|
||
{tNMATCH, "!~"},
|
||
{'!', "!"},
|
||
{'~', "~"},
|
||
{'!', "!(unary)"},
|
||
{'~', "~(unary)"},
|
||
{'!', "!@"},
|
||
{'~', "~@"},
|
||
{'!', "!"},
|
||
{'!', "!(unary)"},
|
||
{'!', "!@"},
|
||
{tAREF, "[]"},
|
||
{tASET, "[]="},
|
||
{tLSHFT, "<<"},
|