Implement IF NODE locations

The following Location information has been added This is the information required for parse.y to be a universal parser:

```
❯ ruby --parser=prism --dump=parsetree -y -e "if a; elsif b; else end"
@ ProgramNode (location: (1,0)-(1,23))
+-- locals: []
+-- statements:
    @ StatementsNode (location: (1,0)-(1,23))
    +-- body: (length: 1)
        +-- @ IfNode (location: (1,0)-(1,23))
            +-- if_keyword_loc: (1,0)-(1,2) = "if"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            +-- predicate:
            |   @ CallNode (location: (1,3)-(1,4))
            |   +-- CallNodeFlags: variable_call, ignore_visibility
            |   +-- receiver: nil
            |   +-- call_operator_loc: nil
            |   +-- name: :a
            |   +-- message_loc: (1,3)-(1,4) = "a"
            |   +-- opening_loc: nil
            |   +-- arguments: nil
            |   +-- closing_loc: nil
            |   +-- block: nil
            +-- then_keyword_loc: nil
            ^^^^^^^^^^^^^^^^^^^^^^^^^
            +-- statements: nil
            +-- subsequent:
            |   @ IfNode (location: (1,6)-(1,23))
            |   +-- if_keyword_loc: (1,6)-(1,11) = "elsif"
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |   +-- predicate:
            |   |   @ CallNode (location: (1,12)-(1,13))
            |   |   +-- CallNodeFlags: variable_call, ignore_visibility
            |   |   +-- receiver: nil
            |   |   +-- call_operator_loc: nil
            |   |   +-- name: :b
            |   |   +-- message_loc: (1,12)-(1,13) = "b"
            |   |   +-- opening_loc: nil
            |   |   +-- arguments: nil
            |   |   +-- closing_loc: nil
            |   |   +-- block: nil
            |   +-- then_keyword_loc: nil
                ^^^^^^^^^^^^^^^^^^^^^^^^^
            |   +-- statements: nil
            |   +-- subsequent:
            |   |   @ ElseNode (location: (1,15)-(1,23))
            |   |   +-- else_keyword_loc: (1,15)-(1,19) = "else"
            |   |   +-- statements: nil
            |   |   +-- end_keyword_loc: (1,20)-(1,23) = "end"
            |   +-- end_keyword_loc: (1,20)-(1,23) = "end"
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            +-- end_keyword_loc: (1,20)-(1,23) = "end"
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
This commit is contained in:
ydah 2025-01-03 19:51:58 +09:00 committed by Yudai Takada
parent 23018c2fb4
commit 0643f08187
5 changed files with 48 additions and 12 deletions

6
ast.c
View File

@ -807,6 +807,12 @@ node_locations(VALUE ast_value, const NODE *node)
location_new(nd_code_loc(node)),
location_new(&RNODE_CASE3(node)->case_keyword_loc),
location_new(&RNODE_CASE3(node)->end_keyword_loc));
case NODE_IF:
return rb_ary_new_from_args(4,
location_new(nd_code_loc(node)),
location_new(&RNODE_IF(node)->if_keyword_loc),
location_new(&RNODE_IF(node)->then_keyword_loc),
location_new(&RNODE_IF(node)->end_keyword_loc));
case NODE_NEXT:
return rb_ary_new_from_args(2,
location_new(nd_code_loc(node)),

View File

@ -239,8 +239,11 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
ANN("example: if x == 1 then foo else bar end");
F_NODE(nd_cond, RNODE_IF, "condition expr");
F_NODE(nd_body, RNODE_IF, "then clause");
LAST_NODE;
F_NODE(nd_else, RNODE_IF, "else clause");
F_LOC(if_keyword_loc, RNODE_IF);
F_LOC(then_keyword_loc, RNODE_IF);
LAST_NODE;
F_LOC(end_keyword_loc, RNODE_IF);
return;
case NODE_UNLESS:

28
parse.y
View File

@ -1058,7 +1058,7 @@ rb_discard_node(struct parser_params *p, NODE *n)
static rb_node_scope_t *rb_node_scope_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc);
static rb_node_scope_t *rb_node_scope_new2(struct parser_params *p, rb_ast_id_table_t *nd_tbl, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc);
static rb_node_block_t *rb_node_block_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc);
static rb_node_if_t *rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc);
static rb_node_if_t *rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc, const YYLTYPE* if_keyword_loc, const YYLTYPE* then_keyword_loc, const YYLTYPE* end_keyword_loc);
static rb_node_unless_t *rb_node_unless_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *then_keyword_loc, const YYLTYPE *end_keyword_loc);
static rb_node_case_t *rb_node_case_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *case_keyword_loc, const YYLTYPE *end_keyword_loc);
static rb_node_case2_t *rb_node_case2_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *case_keyword_loc, const YYLTYPE *end_keyword_loc);
@ -1166,7 +1166,7 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE
#define NEW_SCOPE(a,b,loc) (NODE *)rb_node_scope_new(p,a,b,loc)
#define NEW_SCOPE2(t,a,b,loc) (NODE *)rb_node_scope_new2(p,t,a,b,loc)
#define NEW_BLOCK(a,loc) (NODE *)rb_node_block_new(p,a,loc)
#define NEW_IF(c,t,e,loc) (NODE *)rb_node_if_new(p,c,t,e,loc)
#define NEW_IF(c,t,e,loc,ik_loc,tk_loc,ek_loc) (NODE *)rb_node_if_new(p,c,t,e,loc,ik_loc,tk_loc,ek_loc)
#define NEW_UNLESS(c,t,e,loc,k_loc,t_loc,e_loc) (NODE *)rb_node_unless_new(p,c,t,e,loc,k_loc,t_loc,e_loc)
#define NEW_CASE(h,b,loc,ck_loc,ek_loc) (NODE *)rb_node_case_new(p,h,b,loc,ck_loc,ek_loc)
#define NEW_CASE2(b,loc,ck_loc,ek_loc) (NODE *)rb_node_case2_new(p,b,loc,ck_loc,ek_loc)
@ -1387,7 +1387,7 @@ static NODE* cond(struct parser_params *p, NODE *node, const YYLTYPE *loc);
static NODE* method_cond(struct parser_params *p, NODE *node, const YYLTYPE *loc);
#define new_nil(loc) NEW_NIL(loc)
static NODE *new_nil_at(struct parser_params *p, const rb_code_position_t *pos);
static NODE *new_if(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*);
static NODE *new_if(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*);
static NODE *new_unless(struct parser_params*,NODE*,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*,const YYLTYPE*);
static NODE *logop(struct parser_params*,ID,NODE*,NODE*,const YYLTYPE*,const YYLTYPE*);
@ -3159,7 +3159,7 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
}
| stmt modifier_if expr_value
{
$$ = new_if(p, $3, remove_begin($1), 0, &@$);
$$ = new_if(p, $3, remove_begin($1), 0, &@$, &@2, &NULL_LOC, &NULL_LOC);
fixpos($$, $3);
/*% ripper: if_mod!($:3, $:1) %*/
}
@ -4044,7 +4044,7 @@ arg : lhs '=' lex_ctxt arg_rhs
| arg '?' arg '\n'? ':' arg
{
value_expr($1);
$$ = new_if(p, $1, $3, $6, &@$);
$$ = new_if(p, $1, $3, $6, &@$, &NULL_LOC, &@5, &NULL_LOC);
fixpos($$, $1);
/*% ripper: ifop!($:1, $:3, $:6) %*/
}
@ -4488,7 +4488,10 @@ primary : inline_primary
if_tail
k_end
{
$$ = new_if(p, $2, $4, $5, &@$);
if ($5 && nd_type_p($5, NODE_IF))
RNODE_IF($5)->end_keyword_loc = @6;
$$ = new_if(p, $2, $4, $5, &@$, &@1, &@3, &@6);
fixpos($$, $2);
/*% ripper: if!($:2, $:4, $:5) %*/
}
@ -4900,7 +4903,7 @@ if_tail : opt_else
compstmt
if_tail
{
$$ = new_if(p, $2, $4, $5, &@$);
$$ = new_if(p, $2, $4, $5, &@$, &@1, &@3, &NULL_LOC);
fixpos($$, $2);
/*% ripper: elsif!($:2, $:4, $:5) %*/
}
@ -5465,7 +5468,7 @@ p_cases : opt_else
p_top_expr : p_top_expr_body
| p_top_expr_body modifier_if expr_value
{
$$ = new_if(p, $3, $1, 0, &@$);
$$ = new_if(p, $3, $1, 0, &@$, &@2, &NULL_LOC, &NULL_LOC);
fixpos($$, $3);
/*% ripper: if_mod!($:3, $:1) %*/
}
@ -11512,12 +11515,15 @@ rb_node_yield_new(struct parser_params *p, NODE *nd_head, const YYLTYPE *loc)
}
static rb_node_if_t *
rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc)
rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *loc, const YYLTYPE* if_keyword_loc, const YYLTYPE* then_keyword_loc, const YYLTYPE* end_keyword_loc)
{
rb_node_if_t *n = NODE_NEWNODE(NODE_IF, rb_node_if_t, loc);
n->nd_cond = nd_cond;
n->nd_body = nd_body;
n->nd_else = nd_else;
n->if_keyword_loc = *if_keyword_loc;
n->then_keyword_loc = *then_keyword_loc;
n->end_keyword_loc = *end_keyword_loc;
return n;
}
@ -14322,11 +14328,11 @@ new_nil_at(struct parser_params *p, const rb_code_position_t *pos)
}
static NODE*
new_if(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc)
new_if(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc, const YYLTYPE* if_keyword_loc, const YYLTYPE* then_keyword_loc, const YYLTYPE* end_keyword_loc)
{
if (!cc) return right;
cc = cond0(p, cc, COND_IN_COND, loc, true);
return newline_node(NEW_IF(cc, left, right, loc));
return newline_node(NEW_IF(cc, left, right, loc, if_keyword_loc, then_keyword_loc, end_keyword_loc));
}
static NODE*

View File

@ -265,6 +265,9 @@ typedef struct RNode_IF {
struct RNode *nd_cond;
struct RNode *nd_body;
struct RNode *nd_else;
rb_code_location_t if_keyword_loc;
rb_code_location_t then_keyword_loc;
rb_code_location_t end_keyword_loc;
} rb_node_if_t;
typedef struct RNode_UNLESS {

View File

@ -1376,6 +1376,24 @@ dummy
assert_locations(node.children[-1].locations, [[1, 0, 1, 17], [1, 0, 1, 4], [1, 14, 1, 17]])
end
def test_if_locations
node = ast_parse("if cond then 1 else 2 end")
assert_locations(node.children[-1].locations, [[1, 0, 1, 25], [1, 0, 1, 2], [1, 8, 1, 12], [1, 22, 1, 25]])
node = ast_parse("1 if 2")
assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 2, 1, 4], nil, nil])
node = ast_parse("if 1; elsif 2; else end")
assert_locations(node.children[-1].locations, [[1, 0, 1, 23], [1, 0, 1, 2], [1, 4, 1, 5], [1, 20, 1, 23]])
assert_locations(node.children[-1].children[-1].locations, [[1, 6, 1, 19], [1, 6, 1, 11], [1, 13, 1, 14], [1, 20, 1, 23]])
node = ast_parse("true ? 1 : 2")
assert_locations(node.children[-1].locations, [[1, 0, 1, 12], nil, [1, 9, 1, 10], nil])
node = ast_parse("case a; in b if c; end")
assert_locations(node.children[-1].children[1].children[0].locations, [[1, 11, 1, 17], [1, 13, 1, 15], nil, nil])
end
def test_next_locations
node = ast_parse("loop { next 1 }")
assert_locations(node.children[-1].children[-1].children[-1].locations, [[1, 7, 1, 13], [1, 7, 1, 11]])