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