8000 add 2.1 style f-suffix frozen strings · github/ruby@f70e6db · GitHub
[go: up one dir, main page]

Skip to content

Commit f70e6db

Browse files
author
Charlie Somerville
committed
add 2.1 style f-suffix frozen strings
1 parent 3830598 commit f70e6db

File tree

5 files changed

+140
-9
lines changed

5 files changed

+140
-9
lines changed

compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2351,7 +2351,7 @@ case_when_optimizable_literal(NODE * node)
23512351
modf(RFLOAT_VALUE(v), &ival) == 0.0) {
23522352
return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
23532353
}
2354-
if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) {
2354+
if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric) || RB_TYPE_P(v, T_STRING)) {
23552355
return v;
23562356
}
23572357
break;

ext/ripper/eventids2.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static ID ripper_id_symbeg;
3636
static ID ripper_id_tstring_beg;
3737
static ID ripper_id_tstring_content;
3838
static ID ripper_id_tstring_end;
39+
static ID ripper_id_tstring_suffix;
3940
static ID ripper_id_words_beg;
4041
static ID ripper_id_qwords_beg;
4142
static ID ripper_id_words_sep;
@@ -89,6 +90,7 @@ ripper_init_eventids2(VALUE self)
8990
ripper_id_tstring_beg = rb_intern_const("on_tstring_beg");
9091
ripper_id_tstring_content = rb_intern_const("on_tstring_content");
9192
ripper_id_tstring_end = rb_intern_const("on_tstring_end");
93+
ripper_id_tstring_suffix = rb_intern_const("on_tstring_suffix");
9294
ripper_id_words_beg = rb_intern_const("on_words_beg");
9395
ripper_id_qwords_beg = rb_intern_const("on_qwords_beg");
9496
ripper_id_words_sep = rb_intern_const("on_words_sep");
@@ -240,6 +242,7 @@ static const struct token_assoc {
240242
{tSTRING_DBEG, &ripper_id_embexpr_beg},
241243
{tSTRING_DVAR, &ripper_id_embvar},
242244
{tSTRING_END, &ripper_id_tstring_end},
245+
{tSTRING_SUFFIX, &ripper_id_tstring_suffix},
243246
{tSYMBEG, &ripper_id_symbeg},
244247
{tUMINUS, &ripper_id_op},
245248
{tUMINUS_NUM, &ripper_id_op},

parse.y

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ static NODE *new_evstr_gen(struct parser_params*,NODE*);
365365
#define new_evstr(n) new_evstr_gen(parser,(n))
366366
static NODE *evstr2dstr_gen(struct parser_params*,NODE*);
367367
#define evstr2dstr(n) evstr2dstr_gen(parser,(n))
368+
static NODE *str_suffix_gen(struct parser_params*, NODE*, long);
369+
#define str_suffix(n,o) str_suffix_gen(parser,(n),(o))
368370
static NODE *splat_array(NODE*);
369371

370372
static NODE *call_bin_op_gen(struct parser_params*,NODE*,ID,NODE*);
@@ -474,6 +476,8 @@ static int lvar_defined_gen(struct parser_params*, ID);
474476
#define RE_OPTION_MASK 0xff
475477
#define RE_OPTION_ARG_ENCODING_NONE 32
476478

479+
#define STR_OPTION_FROZEN 1
480+
477481
#define NODE_STRTERM NODE_ZARRAY /* nothing to gc */
478482
#define NODE_HEREDOC NODE_ARRAY /* 1, 3 to gc */
479483
#define SIGN_EXTEND(x,n) (((1<<(n)-1)^((x)&~(~0<<(n))))-(1<<(n)-1))
@@ -679,7 +683,7 @@ static void token_info_pop(struct parser_params*, const char *token);
679683
%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
680684
%token <node> tINTEGER tFLOAT tSTRING_CONTENT tCHAR
681685
%token <node> tNTH_REF tBACK_REF
682-
%token <num> tREGEXP_END
686+
%to 6D40 ken <num> tREGEXP_END tSTRING_SUFFIX
683687

684688
%type <node> singleton strings string string1 xstring regexp
685689
%type <node> string_contents xstring_contents regexp_contents string_content
@@ -703,6 +707,7 @@ static void token_info_pop(struct parser_params*, const char *token);
703707
%type <node> mlhs mlhs_head mlhs_basic mlhs_item mlhs_node mlhs_post mlhs_inner
704708
%type <id> fsym keyword_variable user_variable sym symbol operation operation2 operation3
705709
%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_bad_arg
710+
%type <num> opt_string_sfx
706711
/*%%%*/
707712
/*%
708713
%type <val> program reswords then do dot_or_colon
@@ -3839,7 +3844,7 @@ literal : numeric
38393844
| dsym
38403845
;
38413846

3842-
strings : string
3847+
strings : string opt_string_sfx
38433848
{
38443849
/*%%%*/
38453850
NODE *node = $1;
@@ -3849,6 +3854,7 @@ strings : string
38493854
else {
38503855
node = evstr2dstr(node);
38513856
}
3857+
node = str_suffix(node, $2);
38523858
$$ = node;
38533859
/*%
38543860
$$ = $1;
@@ -3878,6 +3884,10 @@ string1 : tSTRING_BEG string_contents tSTRING_END
38783884
}
38793885
;
38803886

3887+
opt_string_sfx : tSTRING_SUFFIX
3888+
| /* none */ { $$ = 0; }
3889+
;
3890+
38813891
xstring : tXSTRING_BEG xstring_contents tSTRING_END
38823892
{
38833893
/*%%%*/
@@ -4886,6 +4896,7 @@ none : /* none */
48864896
# define yylval (*((YYSTYPE*)(parser->parser_yylval)))
48874897

48884898
static int parser_regx_options(struct parser_params*);
4899+
static int parser_str_options(struct parser_params*);
48894900
static int parser_tokadd_string(struct parser_params*,int,int,int,long*,rb_encoding**);
48904901
static void parser_tokaddmbc(struct parser_params *parser, int c, rb_encoding *enc);
48914902
static int parser_parse_string(struct parser_params*,NODE*);
@@ -4901,6 +4912,7 @@ static int parser_here_document(struct parser_params*,NODE*);
49014912
# define read_escape(flags,e) parser_read_escape(parser, (flags), (e))
49024913
# define tokadd_escape(e) parser_tokadd_escape(parser, (e))
49034914
# define regx_options() parser_regx_options(parser)
4915+
# define str_options() parser_str_options(parser)
49044916
# define tokadd_string(f,t,p,n,e) parser_tokadd_string(parser,(f),(t),(p),(n),(e))
49054917
# define parse_string(n) parser_parse_string(parser,(n))
49064918
# define tokaddmbc(c, enc) parser_tokaddmbc(parser, (c), (enc))
@@ -5385,10 +5397,11 @@ rb_parser_compile_file(volatile VALUE vparser, const char *f, VALUE file, int st
53855397
#define STR_FUNC_QWORDS 0x08
53865398
#define STR_FUNC_SYMBOL 0x10
53875399
#define STR_FUNC_INDENT 0x20
5400+
#define STR_FUNC_OPTION 0x40
53885401

53895402
enum string_type {
5390-
str_squote = (0),
5391-
str_dquote = (STR_FUNC_EXPAND),
5403+
str_squote = (STR_FUNC_OPTION),
5404+
str_dquote = (STR_FUNC_EXPAND|STR_FUNC_OPTION),
53925405
str_xquote = (STR_FUNC_EXPAND),
53935406
str_regexp = (STR_FUNC_REGEXP|STR_FUNC_ESCAPE|STR_FUNC_EXPAND),
53945407
str_sword = (STR_FUNC_QWORDS),
@@ -5827,6 +5840,28 @@ parser_regx_options(struct parser_params *parser)
58275840
return options | RE_OPTION_ENCODING(kcode);
58285841
}
58295842

5843+
static int
5844+
parser_str_options(struct parser_params *parser)
5845+
{
5846+
int c, options = 0;
5847+
const char *save_p = lex_p;
5848+
5849+
while (c = nextc(), ISALPHA(c)) {
5850+
switch (c) {
5851+
#if STR_OPTION_FROZEN
5852+
case 'f':
5853+
options |= STR_OPTION_FROZEN;
5854+
break;
5855+
#endif
5856+
default:
5857+
lex_p = save_p;
5858+
return 0;
5859+
}
5860+
}
5861+
pushback(c);
5862+
return options;
5863+
}
5864+
58305865
static void
58315866
dispose_string(VALUE str)
58325867
{
@@ -5995,6 +6030,10 @@ parser_parse_string(struct parser_params *parser, NODE *quote)
59956030
rb_encoding *enc = parser->enc;
59966031

59976032
if (func == -1) return tSTRING_END;
6033+
if (func == 0) {
6034+
set_yylval_num(term);
6035+
return tSTRING_SUFFIX;
6036+
}
59986037
c = nextc();
59996038
if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) {
60006039
do {c = nextc();} while (ISSPACE(c));
@@ -6003,11 +6042,18 @@ parser_parse_string(struct parser_params *parser, NODE *quote)
60036042
if (c == term && !quote->nd_nest) {
60046043
if (func & STR_FUNC_QWORDS) {
60056044
quote->nd_func = -1;
6045+
quote->u2.id = 0;
60066046
return ' ';
60076047
}
6008-
if (!(func & STR_FUNC_REGEXP)) return tSTRING_END;
6009-
set_yylval_num(regx_options());
6010-
return tREGEXP_END;
6048+
if (func & STR_FUNC_REGEXP) {
6049+
set_yylval_num(regx_options());
6050+
return tREGEXP_END;
6051+
}
6052+
if ((func & STR_FUNC_OPTION) && (func = str_options()) != 0) {
6053+
quote->nd_func = 0;
6054+
quote->u2.id = func;
6055+
}
6056+
return tSTRING_END;
60116057
}
60126058
if (space) {
60136059
pushback(c);
@@ -6655,7 +6701,8 @@ parser_yylex(struct parser_params *parser)
66556701
}
66566702
else {
66576703
token = parse_string(lex_strterm);
6658-
if (token == tSTRING_END || token == tREGEXP_END) {
6704+
if ((token == tSTRING_END && lex_strterm->nd_func) ||
6705+
token == tSTRING_SUFFIX || token == tREGEXP_END) {
66596706
rb_gc_force_recycle((VALUE)lex_strterm);
66606707
lex_strterm = 0;
66616708
lex_state = EXPR_END;
@@ -8187,6 +8234,27 @@ evstr2dstr_gen(struct parser_params *parser, NODE *node)
81878234
return node;
81888235
}
81898236

8237+
static NODE *
8238+
str_suffix_gen(struct parser_params *parser, NODE *node, long opt)
8239+
{
8240+
if (nd_type(node) == NODE_STR) {
8241+
#if STR_OPTION_FROZEN
8242+
if (opt & STR_OPTION_FROZEN) {
8243+
OBJ_FREEZE(node->nd_lit);
8244+
nd_set_type(node, NODE_LIT);
8245+
}
8246+
#endif
8247+
}
8248+
else {
8249+
#if STR_OPTION_FROZEN
8250+
if (opt & STR_OPTION_FROZEN) {
8251+
node = NEW_CALL(node, rb_intern("freeze"), 0);
8252+
}
8253+
#endif
8254+
}
8255+
return node;
8256+
}
8257+
81908258
static NODE *
81918259
new_evstr_gen(struct parser_params *parser, NODE *node)
81928260
{

test/ripper/test_scanner_events.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,15 @@ def test_tstring_end
569569
scan('tstring_end', '%Q[abcdef]')
570570
end
571571

572+
def test_tstring_suffix
573+
assert_equal ['"f'],
574+
scan('tstring_end', '"abcdef"f')
575+
assert_equal [']f'],
576+
scan('tstring_end', '%q[abcdef]f')
577+
assert_equal [']f'],
578+
scan('tstring_end', '%Q[abcdef]f')
579+
end
580+
572581
def test_regexp_beg
573582
assert_equal [],
574583
scan('regexp_beg', '')

test/ruby/test_string.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,4 +1996,55 @@ def test_byteslice
19961996

19971997
assert_equal(u("\x82")+("\u3042"*9), ("\u3042"*10).byteslice(2, 28))
19981998
end
1999+
2000+
def test_unknown_string_option
2001+
str = nil
2002+
assert_nothing_raised(SyntaxError) do
2003+
eval(%{
2004+
str = begin"hello"end
2005+
})
2006+
end
2007+
assert_equal "hello", str
2008+
refute str.frozen?
2009+
end
2010+
2011+
def test_frozen_string
2012+
assert_equal "hello", "hello"f
2013+
2014+
assert "hello"f.frozen?
2015+
2016+
f = -> { "hello"f }
2017+
2018+
assert_equal f.call.object_id, f.call.object_id
2019+
end
2020+
2021+
def test_frozen_dstring
2022+
assert_equal "hello123", "hello#{123}"f
2023+
2024+
assert "hello#{123}"f.frozen?
2025+
2026+
i = 0
2027+
f = -> { "#{i += 1}"f }
2028+
assert_equal "1", f.call
2029+
assert_equal "2", f.call
2030+
end
2031+
2032+
def test_frozen_string_adjacent
2033+
str = nil
2034+
assert_nothing_raised(SyntaxError) do
2035+
eval(%{
2036+
str = "hello" "world"f
2037+
})
2038+
end
2039+
assert_equal "helloworld", str
2040+
assert str.frozen?
2041+
end
2042+
2043+
def test_frozen_string_cannot_be_adjacent
2044+
assert_raise(SyntaxError) do
2045+
eval(%{
2046+
"hello"f "world"
2047+
})
2048+
end
2049+
end
19992050
end

0 commit comments

Comments
 (0)
0