diff --git a/parse.y b/parse.y index 909a4ec3b1..37ad94950c 100644 --- a/parse.y +++ b/parse.y @@ -268,6 +268,7 @@ struct parser_params { VALUE prevline; VALUE lastline; VALUE nextline; + st_table *new_nodes; const char *pbeg; const char *pcur; const char *pend; @@ -450,7 +451,7 @@ static NODE* node_newnode_with_locals(struct parser_params *, enum node_type, VA static NODE* node_newnode(struct parser_params *, enum node_type, VALUE, VALUE, VALUE, const rb_code_location_t*); #define rb_node_newnode(type, a1, a2, a3, loc) node_newnode(p, (type), (a1), (a2), (a3), (loc)) -static NODE *nd_set_loc(NODE *nd, const YYLTYPE *loc); +static NODE *nd_set_loc(struct parser_params *, NODE *nd, const YYLTYPE *loc); static int parser_get_node_id(struct parser_params *p) @@ -560,7 +561,7 @@ static NODE *new_defined(struct parser_params *p, NODE *expr, const YYLTYPE *loc static NODE *new_regexp(struct parser_params *, NODE *, int, const YYLTYPE *); -#define make_list(list, loc) ((list) ? (nd_set_loc(list, loc), list) : NEW_ZLIST(loc)) +#define make_list(list, loc) ((list) ? (nd_set_loc(p, list, loc), list) : NEW_ZLIST(loc)) static NODE *new_xstring(struct parser_params *, NODE *, const YYLTYPE *loc); @@ -4580,7 +4581,7 @@ string1 : tSTRING_BEG string_contents tSTRING_END { /*%%%*/ $$ = heredoc_dedent(p, $2); - if ($$) nd_set_loc($$, &@$); + if ($$) nd_set_loc(p, $$, &@$); /*% %*/ /*% ripper: string_literal!(heredoc_dedent(p, $2)) %*/ } @@ -9206,9 +9207,54 @@ parser_yylex(struct parser_params *p) } #endif goto normal_newline; + case '^': { + const char *down = RSTRING_PTR(p->lex.lastline); + if (p->lex.prevline && !p->eofp) p->lex.lastline = p->lex.prevline; + + int i = 0; + while (1) { + while (ISSPACE(down[i]) && down[i] != '\n') i++; + if (down[i] == '\n') break; + if (down[i] != '^') { + yyerror0("invalid syntax in down assignment line"); + break; + } + + int j = i; + while (down[j] == '^') j++; + + int k = j; + while (down[k] != '^' && !ISSPACE(down[k]) && down[k] != '\n' && ISASCII(down[k])) k++; + + rb_code_location_t loc = { { p->ruby_sourceline - 1, i }, { p->ruby_sourceline - 1, j } }; + NODE *node; + if (p->lex.new_nodes && st_lookup(p->lex.new_nodes, (st_data_t)&loc, (st_data_t*)&node)) { + NODE *n = rb_ast_newnode(p->ast, nd_type(node)); + rb_node_init(n, nd_type(node), node->u1.value, node->u2.value, node->u3.value); + nd_set_loc(p, n, &node->nd_loc); + n->node_id = node->node_id; + ID id = intern_cstr(down + j, k - j, p->enc); + NODE *tmp = assignable(p, id, n, &loc); + *node = *tmp; + node->node_id = -1; + } + else { + yyerror0("cannot identify node"); + break; + } + + i = k; + } + p->lex.pbeg = RSTRING_PTR(p->lex.lastline); + p->lex.pend = p->lex.pcur = p->lex.pbeg + RSTRING_LEN(p->lex.lastline); + pushback(p, 1); /* always pushback */ + p->lex.ptok = p->lex.pcur; + goto normal_newline; + } } } normal_newline: + if (p->lex.new_nodes) st_clear(p->lex.new_nodes); p->command_start = TRUE; SET_LEX_STATE(EXPR_BEG); return '\n'; @@ -9815,6 +9861,31 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p) #define LVAR_USED ((ID)1 << (sizeof(ID) * CHAR_BIT - 1)) +int +st_loc_cmp(st_data_t x, st_data_t y) +{ + const YYLTYPE *loc1 = (const YYLTYPE *)x; + const YYLTYPE *loc2 = (const YYLTYPE *)y; + return !( + loc1->beg_pos.lineno == loc2->beg_pos.lineno && + loc1->beg_pos.column == loc2->beg_pos.column && + loc1->end_pos.lineno == loc2->end_pos.lineno && + loc1->end_pos.column == loc2->end_pos.column + ); +} + +st_index_t +st_loc_hash(st_data_t x) +{ + const YYLTYPE *loc = (const YYLTYPE *)x; + return loc->beg_pos.lineno ^ loc->beg_pos.column ^ loc->end_pos.lineno ^ loc->end_pos.column; +} + +static const struct st_hash_type st_hashtype_loc = { + st_loc_cmp, + st_loc_hash, +}; + static NODE* node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, VALUE a2, const rb_code_location_t *loc) { @@ -9822,15 +9893,19 @@ node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, V rb_node_init(n, type, a0, a1, a2); - nd_set_loc(n, loc); + nd_set_loc(p, n, loc); nd_set_node_id(n, parser_get_node_id(p)); return n; } static NODE * -nd_set_loc(NODE *nd, const YYLTYPE *loc) +nd_set_loc(struct parser_params *p, NODE *nd, const YYLTYPE *loc) { + if (!p->lex.new_nodes) p->lex.new_nodes = st_init_table(&st_hashtype_loc); nd->nd_loc = *loc; + if (!st_lookup(p->lex.new_nodes, (st_data_t)&nd->nd_loc, NULL)) { + st_insert(p->lex.new_nodes, (st_data_t)&nd->nd_loc, (st_data_t)nd); + } nd_set_line(nd, loc->beg_pos.lineno); return nd; } @@ -10404,7 +10479,7 @@ new_regexp(struct parser_params *p, NODE *node, int options, const YYLTYPE *loc) { VALUE src = node->nd_lit; nd_set_type(node, NODE_LIT); - nd_set_loc(node, loc); + nd_set_loc(p, node, loc); RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit = reg_compile(p, src, options)); } break; @@ -10415,7 +10490,7 @@ new_regexp(struct parser_params *p, NODE *node, int options, const YYLTYPE *loc) /* fall through */ case NODE_DSTR: nd_set_type(node, NODE_DREGX); - nd_set_loc(node, loc); + nd_set_loc(p, node, 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) { @@ -10474,11 +10549,11 @@ new_xstring(struct parser_params *p, NODE *node, const YYLTYPE *loc) switch (nd_type(node)) { case NODE_STR: nd_set_type(node, NODE_XSTR); - nd_set_loc(node, loc); + nd_set_loc(p, node, loc); break; case NODE_DSTR: nd_set_type(node, NODE_DXSTR); - nd_set_loc(node, loc); + nd_set_loc(p, node, loc); break; default: node = NEW_NODE(NODE_DXSTR, Qnil, 1, NEW_LIST(node, loc), loc); @@ -11300,12 +11375,12 @@ node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ct case NODE_MASGN: case NODE_CVASGN: lhs->nd_value = rhs; - nd_set_loc(lhs, loc); + nd_set_loc(p, lhs, loc); break; case NODE_ATTRASGN: lhs->nd_args = arg_append(p, lhs->nd_args, rhs, loc); - nd_set_loc(lhs, loc); + nd_set_loc(p, lhs, loc); break; default: @@ -11892,7 +11967,7 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N args->ruby2_keywords = rest_arg == idFWD_REST; p->ruby_sourceline = saved_line; - nd_set_loc(tail, loc); + nd_set_loc(p, tail, loc); return tail; } @@ -11968,7 +12043,7 @@ args_with_numbered(struct parser_params *p, NODE *args, int max_numparam) if (!args) { YYLTYPE loc = RUBY_INIT_YYLLOC(); args = new_args_tail(p, 0, 0, 0, 0); - nd_set_loc(args, &loc); + nd_set_loc(p, args, &loc); } args->nd_ainfo->pre_args_num = max_numparam; } @@ -12104,13 +12179,13 @@ dsym_node(struct parser_params *p, NODE *node, const YYLTYPE *loc) switch (nd_type(node)) { case NODE_DSTR: nd_set_type(node, NODE_DSYM); - nd_set_loc(node, loc); + nd_set_loc(p, node, loc); break; case NODE_STR: lit = node->nd_lit; RB_OBJ_WRITTEN(p->ast, Qnil, node->nd_lit = ID2SYM(rb_intern_str(lit))); nd_set_type(node, NODE_LIT); - nd_set_loc(node, loc); + nd_set_loc(p, node, loc); break; default: node = NEW_NODE(NODE_DSYM, Qnil, 1, NEW_LIST(node, loc), loc); @@ -12236,7 +12311,7 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c if (op == tOROP) { rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc); lhs->nd_value = rhs; - nd_set_loc(lhs, loc); + nd_set_loc(p, lhs, loc); asgn = NEW_OP_ASGN_OR(gettable(p, vid, &lhs_loc), lhs, loc); if (is_notop_id(vid)) { switch (id_type(vid)) { @@ -12252,7 +12327,7 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc); } lhs->nd_value = rhs; - nd_set_loc(lhs, loc); + nd_set_loc(p, lhs, loc); asgn = NEW_OP_ASGN_AND(gettable(p, vid, &lhs_loc), lhs, loc); } else { @@ -12262,7 +12337,7 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc); } asgn->nd_value = rhs; - nd_set_loc(asgn, loc); + nd_set_loc(p, asgn, loc); } } else {