1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Unfreeze string-literal-only interpolated string-literal

[Feature #17104]
This commit is contained in:
Nobuyoshi Nakada 2020-09-02 23:12:22 +09:00
parent 65e8a29389
commit 7b2bea42a2
No known key found for this signature in database
GPG key ID: 7CD2805BFA3770C6
5 changed files with 50 additions and 18 deletions

12
ast.c
View file

@ -485,9 +485,15 @@ node_children(rb_ast_t *ast, const NODE *node)
case NODE_DXSTR:
case NODE_DREGX:
case NODE_DSYM:
return rb_ary_new_from_args(3, node->nd_lit,
NEW_CHILD(ast, node->nd_next->nd_head),
NEW_CHILD(ast, node->nd_next->nd_next));
{
NODE *n = node->nd_next;
VALUE head = Qnil, next = Qnil;
if (n) {
head = NEW_CHILD(ast, n->nd_head);
next = NEW_CHILD(ast, n->nd_next);
}
return rb_ary_new_from_args(3, node->nd_lit, head, next);
}
case NODE_EVSTR:
return rb_ary_new_from_node_args(ast, 1, node->nd_body);
case NODE_ARGSCAT:

View file

@ -3828,8 +3828,16 @@ static int
compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
{
int cnt;
CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt));
if (!node->nd_next) {
VALUE lit = rb_fstring(node->nd_lit);
const int line = (int)nd_line(node);
ADD_INSN1(ret, line, putstring, lit);
RB_OBJ_WRITTEN(iseq, Qundef, lit);
}
else {
CHECK(compile_dstr_fragments(iseq, ret, node, &cnt));
ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt));
}
return COMPILE_OK;
}

1
node.c
View file

@ -741,6 +741,7 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
ANN("example: :\"foo#{ bar }baz\"");
dlit:
F_LIT(nd_lit, "preceding string");
if (!node->nd_next) return;
F_NODE(nd_next->nd_head, "interpolation");
LAST_NODE;
F_NODE(nd_next->nd_next, "tailing strings");

41
parse.y
View file

@ -9871,12 +9871,24 @@ literal_concat0(struct parser_params *p, VALUE head, VALUE tail)
return 1;
}
static VALUE
string_literal_head(enum node_type htype, NODE *head)
{
if (htype != NODE_DSTR) return Qfalse;
if (head->nd_next) {
head = head->nd_next->nd_end->nd_head;
if (!head || nd_type(head) != NODE_STR) return Qfalse;
}
const VALUE lit = head->nd_lit;
ASSUME(lit != Qfalse);
return lit;
}
/* concat two string literals */
static NODE *
literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *loc)
{
enum node_type htype;
NODE *headlast;
VALUE lit;
if (!head) return tail;
@ -9899,10 +9911,8 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l
}
switch (nd_type(tail)) {
case NODE_STR:
if (htype == NODE_DSTR && (headlast = head->nd_next->nd_end->nd_head) &&
nd_type(headlast) == NODE_STR) {
if ((lit = string_literal_head(htype, head)) != Qfalse) {
htype = NODE_STR;
lit = headlast->nd_lit;
}
else {
lit = head->nd_lit;
@ -9932,13 +9942,16 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l
else if (NIL_P(tail->nd_lit)) {
append:
head->nd_alen += tail->nd_alen - 1;
head->nd_next->nd_end->nd_next = tail->nd_next;
head->nd_next->nd_end = tail->nd_next->nd_end;
if (!head->nd_next) {
head->nd_next = tail->nd_next;
}
else if (tail->nd_next) {
head->nd_next->nd_end->nd_next = tail->nd_next;
head->nd_next->nd_end = tail->nd_next->nd_end;
}
rb_discard_node(p, tail);
}
else if (htype == NODE_DSTR && (headlast = head->nd_next->nd_end->nd_head) &&
nd_type(headlast) == NODE_STR) {
lit = headlast->nd_lit;
else if ((lit = string_literal_head(htype, head)) != Qfalse) {
if (!literal_concat0(p, lit, tail->nd_lit))
goto error;
tail->nd_lit = Qnil;
@ -9976,7 +9989,9 @@ new_evstr(struct parser_params *p, NODE *node, const YYLTYPE *loc)
if (node) {
switch (nd_type(node)) {
case NODE_STR: case NODE_DSTR: case NODE_EVSTR:
case NODE_STR:
nd_set_type(node, NODE_DSTR);
case NODE_DSTR: case NODE_EVSTR:
return node;
}
}
@ -10273,8 +10288,10 @@ new_regexp(struct parser_params *p, NODE *node, int options, const YYLTYPE *loc)
node->nd_cflag = options & RE_OPTION_MASK;
if (!NIL_P(node->nd_lit)) reg_fragment_check(p, node->nd_lit, options);
for (list = (prev = node)->nd_next; list; list = list->nd_next) {
if (nd_type(list->nd_head) == NODE_STR) {
VALUE tail = list->nd_head->nd_lit;
NODE *frag = list->nd_head;
enum node_type type = nd_type(frag);
if (type == NODE_STR || (type == NODE_DSTR && !frag->nd_next)) {
VALUE tail = frag->nd_lit;
if (reg_fragment_check(p, tail, options) && prev && !NIL_P(prev->nd_lit)) {
VALUE lit = prev == node ? prev->nd_lit : prev->nd_head->nd_lit;
if (!literal_concat0(p, lit, tail)) {

View file

@ -188,7 +188,7 @@ class TestISeq < Test::Unit::TestCase
assert_predicate(s1, :frozen?)
assert_predicate(s2, :frozen?)
assert_not_predicate(s3, :frozen?)
assert_predicate(s4, :frozen?) # should probably not be frozen, but unrealistic code
assert_not_predicate(s4, :frozen?)
end
# Safe call chain is not optimized when Coverage is running.