From: nobuyoshi nakada Date: 2005-10-21T20:23:55+09:00 Subject: [ruby-dev:27478] Re: [ruby-core:03573] [BUG] Small issues with Symbols なかだです。 [ruby-core:03573]の件ですが、一年近くも経ってるし今更めんどくさ いので、-devに振ります。 Index: intern.h =================================================================== RCS file: /cvs/ruby/src/ruby/intern.h,v retrieving revision 1.187 diff -U2 -p -r1.187 intern.h --- intern.h 21 Oct 2005 06:46:38 -0000 1.187 +++ intern.h 21 Oct 2005 06:55:42 -0000 @@ -399,4 +399,5 @@ int rb_is_class_id(ID); int rb_is_local_id(ID); int rb_is_junk_id(ID); +int rb_symname_p(const char*); VALUE rb_backref_get(void); void rb_backref_set(VALUE); Index: object.c =================================================================== RCS file: /cvs/ruby/src/ruby/object.c,v retrieving revision 1.179 diff -U2 -p -r1.179 object.c --- object.c 5 Oct 2005 16:15:15 -0000 1.179 +++ object.c 21 Oct 2005 06:55:42 -0000 @@ -1038,5 +1038,5 @@ sym_inspect(VALUE sym) RSTRING(str)->ptr[0] = ':'; strcpy(RSTRING(str)->ptr+1, name); - if (rb_is_junk_id(id)) { + if (!rb_symname_p(name)) { str = rb_str_dump(str); strncpy(RSTRING(str)->ptr, ":\"", 2); Index: parse.y =================================================================== RCS file: /cvs/ruby/src/ruby/parse.y,v retrieving revision 1.422 diff -U2 -p -r1.422 parse.y --- parse.y 20 Oct 2005 13:15:19 -0000 1.422 +++ parse.y 21 Oct 2005 07:32:30 -0000 @@ -5588,4 +5588,5 @@ parser_yylex(struct parser_params *parse int cmd_state; unsigned char uc; + enum lex_state_e last_state; #ifdef RIPPER int fallthru = Qfalse; @@ -6557,4 +6558,5 @@ parser_yylex(struct parser_params *parse case '$': + last_state = lex_state; lex_state = EXPR_END; newtok(); @@ -6600,5 +6602,12 @@ parser_yylex(struct parser_params *parse tokadd(c); c = nextc(); - tokadd(c); + uc = (unsigned char)c; + if (is_identchar(uc)) { + tokadd(c); + } + else { + pushback(c); + } + gvar: tokfix(); set_yylval_id(rb_intern(tok())); @@ -6613,4 +6622,9 @@ parser_yylex(struct parser_params *parse case '\'': /* $': string after last match */ case '+': /* $+: string matches last paren. */ + if (last_state == EXPR_FNAME) { + tokadd('$'); + tokadd(c); + goto gvar; + } set_yylval_node(NEW_BACK_REF(c)); return tBACK_REF; @@ -6625,4 +6639,5 @@ parser_yylex(struct parser_params *parse } while (ISDIGIT(c)); pushback(c); + if (last_state == EXPR_FNAME) goto gvar; tokfix(); set_yylval_node(NEW_NTH_REF(atoi(tok()+1))); @@ -6713,6 +6728,6 @@ parser_yylex(struct parser_params *parse { int result = 0; - enum lex_state_e last_state = lex_state; + last_state = lex_state; switch (tok()[0]) { case '$': @@ -8286,4 +8301,95 @@ internal_id(void) } +static int +is_special_global_name(const char *m) +{ + switch (*m) { + case '~': case '*': case '$': case '?': case '!': case '@': + case '/': case '\\': case ';': case ',': case '.': case '=': + case ':': case '<': case '>': case '\"': + case '&': case '`': case '\'': case '+': + case '0': + ++m; + break; + case '-': + ++m; + if (is_identchar(*m)) m += mbclen(*m); + break; + default: + if (!ISDIGIT(*m)) return 0; + do ++m; while (ISDIGIT(*m)); + } + return !*m; +} + +int +rb_symname_p(const char *name) +{ + const char *m = name; + int localid = Qfalse; + + if (!m) return Qfalse; + switch (*m) { + case '\0': + return Qfalse; + + case '$': + if (is_special_global_name(++m)) return Qtrue; + goto id; + + case '@': + if (*++m == '@') ++m; + goto id; + + case '<': + switch (*++m) { + case '<': ++m; break; + case '=': if (*++m == '>') ++m; break; + default: break; + } + break; + + case '>': + if (*++m == '>') ++m; + break; + + case '=': + switch (*++m) { + case '~': ++m; break; + case '=': if (*++m == '=') ++m; break; + default: return Qfalse; + } + break; + + case '*': + if (*++m == '*') ++m; + break; + + case '+': case '-': + if (*++m == '@') ++m; + break; + + case '|': case '^': case '&': case '/': case '%': case '~': case '`': + break; + + case '[': + if (*++m == ']' && *++m == '=') ++m; + break; + + default: + localid = !ISUPPER(*m); + id: + if (*m != '_' && !ISALPHA(*m) && !ismbchar(*m)) return Qfalse; + while (is_identchar(*m)) m += mbclen(*m); + if (localid) { + switch (*m) { + case '!': case '?': case '=': ++m; + } + } + break; + } + return *m ? Qfalse : Qtrue; +} + ID rb_intern(const char *name) @@ -8301,6 +8407,5 @@ rb_intern(const char *name) case '$': id |= ID_GLOBAL; - m++; - if (!is_identchar(*m)) m++; + if (is_special_global_name(++m)) goto new_id; break; case '@': @@ -8315,5 +8420,5 @@ rb_intern(const char *name) break; default: - if (name[0] != '_' && !ISALPHA(name[0]) && !ismbchar(name[0])) { + if (!is_identchar(*m)) { /* operators */ int i; @@ -8349,8 +8454,11 @@ rb_intern(const char *name) break; } - while (m <= name + last && is_identchar(*m)) { - m += mbclen(*m); + if (!ISDIGIT(*m)) { + while (m <= name + last && is_identchar(*m)) { + m += mbclen(*m); + } } if (*m) id = ID_JUNK; + new_id: id |= ++global_symbols.last_id << ID_SCOPE_SHIFT; id_regist: Index: test/ruby/test_symbol.rb =================================================================== RCS file: test/ruby/test_symbol.rb diff -N test/ruby/test_symbol.rb --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/ruby/test_symbol.rb 21 Oct 2005 05:58:37 -0000 @@ -0,0 +1,50 @@ +require 'test/unit' + +class TestSymbol < Test::Unit::TestCase + # [ruby-core:3573] + + def assert_eval_inspected(sym) + n = sym.inspect + assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(n))} + end + + def test_inspect_invalid + # 2) Symbol#inspect sometimes returns invalid symbol representations: + assert_eval_inspected(:"!") + assert_eval_inspected(:"=") + assert_eval_inspected(:"0") + assert_eval_inspected(:"$1") + assert_eval_inspected(:"@1") + assert_eval_inspected(:"@@1") + assert_eval_inspected(:"@") + assert_eval_inspected(:"@@") + end + + def assert_inspect_evaled(n) + assert_nothing_raised(SyntaxError) {assert_equal(n, eval(n).inspect)} + end + + def test_inspect_suboptimal + # 3) Symbol#inspect sometimes returns suboptimal symbol representations: + assert_inspect_evaled(':foo') + assert_inspect_evaled(':foo!') + assert_inspect_evaled(':bar?') + end + + def test_inspect_dollar + # 4) :$- always treats next character literally: + sym = "$-".intern + assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(':$-'))} + assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(":$-\n"))} + assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(":$- "))} + assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(":$-#"))} + assert_raise(SyntaxError) {eval ':$-('} + end + + def test_inspect_number + # 5) Inconsistency between :$0 and :$1? The first one is valid, but the + # latter isn't. + assert_inspect_evaled(':$0') + assert_inspect_evaled(':$1') + end +end -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦