false || not(true) のパースが通らない件

RubyKaigi 2008 の増原先生の発表 (資料) で飛ばされた部分に、「false || not(true) のパースが失敗してはまる」ということが書いてありました。全くその通りだと思います。訓練された Rubyist は無意識に not を避けてると思います。
not は見た目が関数っぽすぎるのです。

$ ./ruby -e 'p(not(1))'
-e:1: syntax error, unexpected keyword_not, expecting ')'
p(not(1))
     ^
-e:1: syntax error, unexpected ')', expecting $end
$ ./ruby -e 'p((not(1)))'
false
$ ./ruby -e 'p (not(1))'
false

もうやだこんな not 。


そこで、not を関数にしてみました。

$ ./ruby -e 'p(not(true))'
false
$ ./ruby -e 'p(false || not(true))'
false

カタルシス


以下が通らなくなるという非互換が難点。

$ ./ruby -e 'p not(true and true)'
-e:1: syntax error, unexpected keyword_and, expecting ')'
p not(true and true)
              ^
$ ./ruby -e 'p not((true and true))'
false

追記: まつもとさんが直してくれました。以下のパッチとは別の直し方で、上記の非互換もなくていい感じ。

以下パッチ。

Index: lex.c.blt
===================================================================
--- lex.c.blt	(revision 17564)
+++ lex.c.blt	(working copy)
@@ -1,5 +1,5 @@
 /* C code produced by gperf version 3.0.2 */
-/* Command-line: gperf --output-file=lex.c.tmp -C -p -j1 -i 1 -g -o -t -N rb_reserved_word -k'1,3,$' keywords  */
+/* Command-line: gperf -C -p -j1 -i 1 -g -o -t -N rb_reserved_word -k'1,3,$' keywords  */
 
 #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
       && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
@@ -36,12 +36,12 @@
 #line 7 "keywords"
 struct kwtable;
 
-#define TOTAL_KEYWORDS 41
+#define TOTAL_KEYWORDS 40
 #define MIN_WORD_LENGTH 2
 #define MAX_WORD_LENGTH 12
 #define MIN_HASH_VALUE 8
-#define MAX_HASH_VALUE 50
-/* maximum key range = 43, duplicates = 0 */
+#define MAX_HASH_VALUE 47
+/* maximum key range = 40, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -57,32 +57,32 @@
 {
   static const unsigned char asso_values[] =
     {
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 26, 51, 51, 14, 51, 16,  8,
-      11, 13, 51, 51, 51, 51, 10, 51, 13, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 11, 51, 13,  1, 26,
-       4,  1,  8, 28, 51, 23, 51,  1,  1, 27,
-       5, 19, 21, 51,  8,  3,  3, 11, 51, 21,
-      24, 16, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
-      51, 51, 51, 51, 51, 51
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 30, 48, 48,  1, 48, 17,  9,
+      12,  1, 48, 48, 48, 48, 11, 48,  1, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 12, 48,  8, 17, 27,
+       5,  2,  4, 12, 48, 24, 48, 16,  7, 25,
+       7, 24, 29, 48,  1,  2, 12,  5, 48,  1,
+      13, 10, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+      48, 48, 48, 48, 48, 48
     };
   register int hval = len;
 
@@ -110,70 +110,76 @@
   static const struct kwtable wordlist[] =
     {
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 17 "keywords"
-      {"break", {keyword_break, keyword_break}, EXPR_MID},
+#line 12 "keywords"
+      {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
+#line 28 "keywords"
+      {"for", {keyword_for, keyword_for}, EXPR_VALUE},
 #line 23 "keywords"
       {"else", {keyword_else, keyword_else}, EXPR_BEG},
-#line 33 "keywords"
-      {"nil", {keyword_nil, keyword_nil}, EXPR_END},
+#line 36 "keywords"
+      {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID},
 #line 26 "keywords"
       {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG},
+#line 24 "keywords"
+      {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE},
+#line 46 "keywords"
+      {"when", {keyword_when, keyword_when}, EXPR_VALUE},
 #line 25 "keywords"
       {"end", {keyword_end, keyword_end}, EXPR_END},
-#line 42 "keywords"
-      {"then", {keyword_then, keyword_then}, EXPR_BEG},
-#line 34 "keywords"
-      {"not", {keyword_not, keyword_not}, EXPR_VALUE},
+#line 20 "keywords"
+      {"def", {keyword_def, keyword_def}, EXPR_FNAME},
+#line 39 "keywords"
+      {"self", {keyword_self, keyword_self}, EXPR_END},
 #line 27 "keywords"
       {"false", {keyword_false, keyword_false}, EXPR_END},
-#line 40 "keywords"
-      {"self", {keyword_self, keyword_self}, EXPR_END},
-#line 24 "keywords"
-      {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE},
-#line 37 "keywords"
-      {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID},
 #line 43 "keywords"
-      {"true", {keyword_true, keyword_true}, EXPR_END},
-#line 46 "keywords"
-      {"until", {keyword_until, modifier_until}, EXPR_VALUE},
-#line 45 "keywords"
+      {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME},
+#line 44 "keywords"
       {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE},
-#line 39 "keywords"
-      {"return", {keyword_return, keyword_return}, EXPR_MID},
-#line 20 "keywords"
-      {"def", {keyword_def, keyword_def}, EXPR_FNAME},
 #line 15 "keywords"
       {"and", {keyword_and, keyword_and}, EXPR_VALUE},
-#line 22 "keywords"
-      {"do", {keyword_do, keyword_do}, EXPR_BEG},
-#line 49 "keywords"
+#line 48 "keywords"
       {"yield", {keyword_yield, keyword_yield}, EXPR_ARG},
-#line 28 "keywords"
-      {"for", {keyword_for, keyword_for}, EXPR_VALUE},
-#line 44 "keywords"
-      {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME},
-#line 35 "keywords"
-      {"or", {keyword_or, keyword_or}, EXPR_VALUE},
-#line 30 "keywords"
-      {"in", {keyword_in, keyword_in}, EXPR_VALUE},
-#line 47 "keywords"
-      {"when", {keyword_when, keyword_when}, EXPR_VALUE},
+#line 42 "keywords"
+      {"true", {keyword_true, keyword_true}, EXPR_END},
+#line 33 "keywords"
+      {"nil", {keyword_nil, keyword_nil}, EXPR_END},
+#line 41 "keywords"
+      {"then", {keyword_then, keyword_then}, EXPR_BEG},
 #line 38 "keywords"
+      {"return", {keyword_return, keyword_return}, EXPR_MID},
+#line 34 "keywords"
+      {"or", {keyword_or, keyword_or}, EXPR_VALUE},
+#line 37 "keywords"
       {"retry", {keyword_retry, keyword_retry}, EXPR_END},
+#line 45 "keywords"
+      {"until", {keyword_until, modifier_until}, EXPR_VALUE},
 #line 29 "keywords"
       {"if", {keyword_if, modifier_if}, EXPR_VALUE},
+#line 22 "keywords"
+      {"do", {keyword_do, keyword_do}, EXPR_BEG},
+#line 47 "keywords"
+      {"while", {keyword_while, modifier_while}, EXPR_VALUE},
+#line 30 "keywords"
+      {"in", {keyword_in, keyword_in}, EXPR_VALUE},
+#line 35 "keywords"
+      {"redo", {keyword_redo, keyword_redo}, EXPR_END},
 #line 18 "keywords"
       {"case", {keyword_case, keyword_case}, EXPR_VALUE},
-#line 36 "keywords"
-      {"redo", {keyword_redo, keyword_redo}, EXPR_END},
 #line 32 "keywords"
       {"next", {keyword_next, keyword_next}, EXPR_MID},
-#line 41 "keywords"
+#line 40 "keywords"
       {"super", {keyword_super, keyword_super}, EXPR_ARG},
 #line 31 "keywords"
       {"module", {keyword_module, keyword_module}, EXPR_VALUE},
+#line 14 "keywords"
+      {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME},
+#line 17 "keywords"
+      {"break", {keyword_break, keyword_break}, EXPR_MID},
 #line 16 "keywords"
       {"begin", {keyword_begin, keyword_begin}, EXPR_BEG},
+#line 19 "keywords"
+      {"class", {keyword_class, keyword_class}, EXPR_CLASS},
 #line 10 "keywords"
       {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END},
 #line 11 "keywords"
@@ -182,17 +188,8 @@
       {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
 #line 13 "keywords"
       {"END", {keyword_END, keyword_END}, EXPR_END},
-#line 14 "keywords"
-      {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME},
-#line 12 "keywords"
-      {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
 #line 21 "keywords"
-      {"defined?", {keyword_defined, keyword_defined}, EXPR_ARG},
-#line 19 "keywords"
-      {"class", {keyword_class, keyword_class}, EXPR_CLASS},
-      {""}, {""},
-#line 48 "keywords"
-      {"while", {keyword_while, modifier_while}, EXPR_VALUE}
+      {"defined?", {keyword_defined, keyword_defined}, EXPR_ARG}
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -209,6 +206,6 @@
     }
   return 0;
 }
-#line 50 "keywords"
+#line 49 "keywords"
 
 #endif
Index: lex.c.src
===================================================================
--- lex.c.src	(revision 17564)
+++ lex.c.src	(working copy)
@@ -31,7 +31,6 @@
 module, {keyword_module, keyword_module}, EXPR_VALUE
 next, {keyword_next, keyword_next}, EXPR_MID
 nil, {keyword_nil, keyword_nil}, EXPR_END
-not, {keyword_not, keyword_not}, EXPR_VALUE
 or, {keyword_or, keyword_or}, EXPR_VALUE
 redo, {keyword_redo, keyword_redo}, EXPR_END
 rescue, {keyword_rescue, modifier_rescue}, EXPR_MID
Index: object.c
===================================================================
--- object.c	(revision 17564)
+++ object.c	(working copy)
@@ -2276,7 +2276,20 @@
     return rb_Array(arg);
 }
 
+/*
+ *  call-seq:
+ *     not(obj)    => true or false
+ *
+ *  Boolean negate.
+ */
+
 static VALUE
+rb_f_not(VALUE obj, VALUE arg)
+{
+    return RTEST(arg) ? Qfalse : Qtrue;
+}
+
+static VALUE
 boot_defclass(const char *name, VALUE super)
 {
     extern st_table *rb_class_tbl;
@@ -2450,6 +2463,8 @@
     rb_define_global_function("String", rb_f_string, 1);
     rb_define_global_function("Array", rb_f_array, 1);
 
+    rb_define_global_function("not", rb_f_not, 1);
+
     rb_cNilClass = rb_define_class("NilClass", rb_cObject);
     rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0);
     rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0);
Index: parse.y
===================================================================
--- parse.y	(revision 17564)
+++ parse.y	(working copy)
@@ -636,7 +636,6 @@
 	keyword_false
 	keyword_and
 	keyword_or
-	keyword_not
 	modifier_if
 	modifier_unless
 	modifier_while
@@ -718,7 +717,6 @@
 
 %nonassoc  modifier_if modifier_unless modifier_while modifier_until
 %left  keyword_or keyword_and
-%right keyword_not
 %nonassoc keyword_defined
 %right '=' tOP_ASGN
 %left modifier_rescue
@@ -1153,14 +1151,6 @@
 			$$ = dispatch3(binary, $1, ripper_intern("or"), $3);
 		    %*/
 		    }
-		| keyword_not expr
-		    {
-		    /*%%%*/
-			$$ = call_uni_op(cond($2), '!');
-		    /*%
-			$$ = dispatch2(unary, ripper_intern("not"), $2);
-		    %*/
-		    }
 		| '!' command_call
 		    {
 		    /*%%%*/
@@ -1773,7 +1763,7 @@
 		| keyword_defined | keyword_do | keyword_else | keyword_elsif
 		| keyword_end | keyword_ensure | keyword_false
 		| keyword_for | keyword_in | keyword_module | keyword_next
-		| keyword_nil | keyword_not | keyword_or | keyword_redo
+		| keyword_nil | keyword_or | keyword_redo
 		| keyword_rescue | keyword_retry | keyword_return | keyword_self
 		| keyword_super | keyword_then | keyword_true | keyword_undef
 		| keyword_when | keyword_yield | keyword_if | keyword_unless
@@ -9603,7 +9593,6 @@
     {keyword_false,	"false"},
     {keyword_and,	"and"},
     {keyword_or,	"or"},
-    {keyword_not,	"not"},
     {modifier_if,	"if"},
     {modifier_unless,	"unless"},
     {modifier_while,	"while"},
Index: ext/ripper/eventids2.c
===================================================================
--- ext/ripper/eventids2.c	(revision 17564)
+++ ext/ripper/eventids2.c	(working copy)
@@ -160,7 +160,6 @@
     {keyword_module,	&ripper_id_kw},
     {keyword_next,	&ripper_id_kw},
     {keyword_nil,	&ripper_id_kw},
-    {keyword_not,	&ripper_id_kw},
     {keyword_or,	&ripper_id_kw},
     {keyword_redo,	&ripper_id_kw},
     {keyword_rescue,	&ripper_id_kw},
Index: keywords
===================================================================
--- keywords	(revision 17564)
+++ keywords	(working copy)
@@ -31,7 +31,6 @@
 module, {keyword_module, keyword_module}, EXPR_VALUE
 next, {keyword_next, keyword_next}, EXPR_MID
 nil, {keyword_nil, keyword_nil}, EXPR_END
-not, {keyword_not, keyword_not}, EXPR_VALUE
 or, {keyword_or, keyword_or}, EXPR_VALUE
 redo, {keyword_redo, keyword_redo}, EXPR_END
 rescue, {keyword_rescue, modifier_rescue}, EXPR_MID