From: Nobuyoshi Nakada Date: 2008-02-14T02:16:32+09:00 Subject: [ruby-dev:33768] Re: [ruby-cvs:22680] Ruby:r15443 (trunk): * bootstraptest/runner.rb, bootstraptest/test_method.rb, enc/depend, なかだです。 At Wed, 13 Feb 2008 14:24:03 +0900, Nobuyoshi Nakada wrote in [ruby-dev:33743]: > 簡単に#{Regexp.quote()}を書く方法があって、sub/gsubの第二引数が > Regexpだったらsourceを使う、とかならできるかもしれませんが、前者 > が難問です。 /\q#{string_expr}/で/#{Regexp.quote(string_expr)}/になるようにし てみました。しかし、1.8だと/\1/でも問題ないんですが、1.9では厳密 になっているらしくて invalid backref number/name というエラーに なってしまいます。いまいち使えない…。 Index: parse.y =================================================================== --- parse.y (revision 15457) +++ parse.y (working copy) @@ -438,4 +438,5 @@ extern int rb_dvar_defined(ID); extern int rb_local_defined(ID); extern int rb_parse_in_eval(void); +extern VALUE rb_reg_quote(VALUE); static VALUE reg_compile_gen(struct parser_params*, VALUE, int); @@ -708,5 +709,5 @@ static void ripper_compile_error(struct %token tLAMBDA /* -> */ %token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG -%token tSTRING_DBEG tSTRING_DVAR tSTRING_END tLAMBEG +%token tSTRING_DBEG tSTRING_DVAR tSTRING_END tREGEXP_QUOTE tLAMBEG /* @@ -3851,4 +3852,32 @@ string_content : tSTRING_CONTENT %*/ } + | tREGEXP_QUOTE + { + $$ = lex_strterm; + lex_strterm = 0; + lex_state = EXPR_BEG; + COND_PUSH(0); + CMDARG_PUSH(0); + } + compstmt '}' + { + lex_strterm = $2; + COND_LEXPOP(); + CMDARG_LEXPOP(); + /*%%%*/ + if ($3) $3->flags &= ~NODE_NEWLINE; + $$ = new_evstr($3); + if (nd_type($$) == NODE_STR) { + $$->nd_lit = rb_reg_quote($$->nd_lit); + } + else { + $$ = NEW_CALL(NEW_LIT(rb_cRegexp), rb_intern("quote"), + NEW_LIST($$)); + $$ = NEW_EVSTR($$); + } + /*% + $$ = dispatch1(regexp_quote, $3); + %*/ + } ; @@ -5342,4 +5371,24 @@ parser_tokadd_mbchar(struct parser_param static int +parser_regexp_quote_p(struct parser_params *parser, int func) +{ + if (!(func & STR_FUNC_EXPAND)) return 0; + if (lex_p + 1 >= lex_pend) return 0; + switch (lex_p[1]) { + case '{': + lex_p += 2; + return 1; + case '#': + if (lex_p + 2 < lex_pend && lex_p[2] == '{') { + lex_p += 3; + return 1; + } + } + return 0; +} + +#define regexp_quote_p(func) parser_regexp_quote_p(parser, func) + +static int parser_tokadd_string(struct parser_params *parser, int func, int term, int paren, long *nest, @@ -5414,4 +5463,9 @@ parser_tokadd_string(struct parser_param continue; + case 'q': + if (regexp_quote_p(func)) { + lex_p = beg; + goto exit; + } default: if (func & STR_FUNC_REGEXP) { @@ -5464,4 +5518,5 @@ parser_tokadd_string(struct parser_param tokadd(c); } + exit: *encp = enc; return c; @@ -5511,4 +5566,7 @@ parser_parse_string(struct parser_params tokadd('#'); } + else if (c == '\\' && peek('q') && regexp_quote_p(func)) { + return tREGEXP_QUOTE; + } pushback(c); if (tokadd_string(func, term, paren, "e->nd_nest, Index: re.c =================================================================== --- re.c (revision 15457) +++ re.c (working copy) @@ -306,5 +306,5 @@ rb_reg_desc(const char *s, long len, VAL */ -static VALUE +VALUE rb_reg_source(VALUE re) { @@ -2837,4 +2837,5 @@ rb_reg_regsub(VALUE str, VALUE src, stru rb_encoding *str_enc = rb_enc_get(str); rb_encoding *src_enc = rb_enc_get(src); + int quote = 0; p = s = RSTRING_PTR(str); @@ -2867,4 +2868,18 @@ rb_reg_regsub(VALUE str, VALUE src, stru s += clen; + switch (c) { + case 'Q': + quote = 1; + p = s; + continue; + case 'E': + quote = 0; + p = s; + continue; + default: + if (quote) continue; + break; + } + p = s; switch (c) { Index: string.c =================================================================== --- string.c (revision 15457) +++ string.c (working copy) @@ -2751,4 +2751,13 @@ get_pat(VALUE pat, int quote) } +extern VALUE rb_reg_source(VALUE); +static VALUE +get_repl(VALUE repl) +{ + if (TYPE(repl) == T_REGEXP) { + return rb_reg_source(repl); + } + return repl; +} /* @@ -2775,5 +2784,5 @@ rb_str_sub_bang(int argc, VALUE *argv, V } else if (argc == 2) { - repl = argv[1]; + repl = get_repl(argv[1]); StringValue(repl); if (OBJ_TAINTED(repl)) tainted = 1; @@ -2903,5 +2912,5 @@ str_gsub(int argc, VALUE *argv, VALUE st break; case 2: - repl = argv[1]; + repl = get_repl(argv[1]); StringValue(repl); if (OBJ_TAINTED(repl)) tainted = 1; -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦