diff --git a/ChangeLog b/ChangeLog
index 8384acb..af915b7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+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.
diff --git a/object.c b/object.c
index d053c2f..76880d4 100644
--- a/object.c
+++ b/object.c
@@ -31,7 +31,7 @@ VALUE rb_cTrueClass;
 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:
@@ -113,6 +113,33 @@ rb_obj_id_obsolete(obj)
     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;
@@ -1081,6 +1108,21 @@ rb_obj_pattern_match(obj1, obj2)
     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
  *
@@ -2734,9 +2776,12 @@ Init_Object()
 
     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);
 
@@ -2904,6 +2949,7 @@ Init_Object()
 
     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");
 }
diff --git a/parse.y b/parse.y
index 4180ae1..6722c01 100644
--- a/parse.y
+++ b/parse.y
@@ -129,7 +129,6 @@ static NODE *deferred_nodes;
 
 static NODE *cond();
 static NODE *logop();
-static int cond_negative();
 
 static NODE *newline_node();
 static void fixpos();
@@ -447,19 +446,11 @@ stmt		: kALIAS fitem {lex_state = EXPR_FNAME;} fitem
 		    {
 			$$ = 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
 		    {
@@ -469,9 +460,6 @@ stmt		: kALIAS fitem {lex_state = EXPR_FNAME;} fitem
 			else {
 			    $$ = NEW_WHILE(cond($3), $1, 1);
 			}
-			if (cond_negative(&$$->nd_cond)) {
-			    nd_set_type($$, NODE_UNTIL);
-			}
 		    }
 		| stmt kUNTIL_MOD expr_value
 		    {
@@ -481,9 +469,6 @@ stmt		: kALIAS fitem {lex_state = EXPR_FNAME;} fitem
 			else {
 			    $$ = NEW_UNTIL(cond($3), $1, 1);
 			}
-			if (cond_negative(&$$->nd_cond)) {
-			    nd_set_type($$, NODE_WHILE);
-			}
 		    }
 		| stmt kRESCUE_MOD stmt
 		    {
@@ -632,11 +617,11 @@ expr		: command_call
 		    }
 		| kNOT expr
 		    {
-			$$ = NEW_NOT(cond($2));
+			$$ = call_op($2, '!', 0, 0);
 		    }
 		| '!' command_call
 		    {
-			$$ = NEW_NOT(cond($2));
+			$$ = call_op($2, '!', 0, 0);
 		    }
 		| arg
 		;
@@ -952,10 +937,12 @@ op		: '|'		{ $$ = '|'; }
 		| tEQ		{ $$ = tEQ; }
 		| tEQQ		{ $$ = tEQQ; }
 		| tMATCH	{ $$ = tMATCH; }
+		| tNMATCH	{ $$ = tNMATCH; }
 		| '>'		{ $$ = '>'; }
 		| tGEQ		{ $$ = tGEQ; }
 		| '<'		{ $$ = '<'; }
 		| tLEQ		{ $$ = tLEQ; }
+		| tNEQ		{ $$ = tNEQ; }
 		| tLSHFT	{ $$ = tLSHFT; }
 		| tRSHFT	{ $$ = tRSHFT; }
 		| '+'		{ $$ = '+'; }
@@ -965,6 +952,7 @@ op		: '|'		{ $$ = '|'; }
 		| '/'		{ $$ = '/'; }
 		| '%'		{ $$ = '%'; }
 		| tPOW		{ $$ = tPOW; }
+		| '!'		{ $$ = '!'; }
 		| '~'		{ $$ = '~'; }
 		| tUPLUS	{ $$ = tUPLUS; }
 		| tUMINUS	{ $$ = tUMINUS; }
@@ -1189,7 +1177,7 @@ arg		: lhs '=' arg
 		    }
 		| arg tNEQ arg
 		    {
-			$$ = NEW_NOT(call_op($1, tEQ, 1, $3));
+			$$ = call_op($1, tNEQ, 1, $3);
 		    }
 		| arg tMATCH arg
 		    {
@@ -1197,11 +1185,11 @@ arg		: lhs '=' 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
 		    {
@@ -1559,11 +1547,6 @@ primary		: literal
 		    {
 			$$ = 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
@@ -1572,11 +1555,6 @@ primary		: literal
 		    {
 			$$ = 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
@@ -1584,9 +1562,6 @@ primary		: literal
 		    {
 			$$ = 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
@@ -1594,9 +1569,6 @@ primary		: literal
 		    {
 			$$ = 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
@@ -3547,8 +3519,17 @@ yylex()
 	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 == '~') {
@@ -5568,26 +5549,6 @@ logop(type, left, right)
     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;
@@ -6023,6 +5984,7 @@ static struct {
     {'|',	"|"},
     {'^',	"^"},
     {'&',	"&"},
+    {'!',	"!"},
     {tCMP,	"<=>"},
     {'>',	">"},
     {tGEQ,	">="},
@@ -6033,12 +5995,12 @@ static struct {
     {tNEQ,	"!="},
     {tMATCH,	"=~"},
     {tNMATCH,	"!~"},
-    {'!',	"!"},
     {'~',	"~"},
-    {'!',	"!(unary)"},
     {'~',	"~(unary)"},
-    {'!',	"!@"},
     {'~',	"~@"},
+    {'!',	"!"},
+    {'!',	"!(unary)"},
+    {'!',	"!@"},
     {tAREF,	"[]"},
     {tASET,	"[]="},
     {tLSHFT,	"<<"},
