Implement UNLESS NODE keyword locations

This commit is contained in:
yui-knk 2024-07-22 16:28:00 +09:00 коммит произвёл Yuichiro Kaneko
Родитель f23485a8d6
Коммит 57b11be15a
5 изменённых файлов: 53 добавлений и 15 удалений

14
ast.c
Просмотреть файл

@ -747,12 +747,20 @@ ast_node_children(rb_execution_context_t *ec, VALUE self)
return node_children(data->ast_value, data->node);
}
static int
null_loc_p(rb_code_location_t *loc)
{
return (loc->beg_pos.lineno == 0 && loc->beg_pos.column == -1 && loc->end_pos.lineno == 0 && loc->end_pos.column == -1);
}
static VALUE
location_new(rb_code_location_t *loc)
{
VALUE obj;
struct ASTLocationData *data;
if (null_loc_p(loc)) return Qnil;
obj = TypedData_Make_Struct(rb_cLocation, struct ASTLocationData, &rb_location_type, data);
data->first_lineno = loc->beg_pos.lineno;
data->first_column = loc->beg_pos.column;
@ -767,6 +775,12 @@ node_locations(VALUE ast_value, const NODE *node)
{
enum node_type type = nd_type(node);
switch (type) {
case NODE_UNLESS:
return rb_ary_new_from_args(4,
location_new(nd_code_loc(node)),
location_new(&RNODE_UNLESS(node)->keyword_loc),
location_new(&RNODE_UNLESS(node)->then_keyword_loc),
location_new(&RNODE_UNLESS(node)->end_keyword_loc));
case NODE_ARGS_AUX:
case NODE_LAST:
break;

Просмотреть файл

@ -27,6 +27,10 @@
#define A_INT(val) rb_str_catf(buf, "%d", (val))
#define A_LONG(val) rb_str_catf(buf, "%ld", (val))
#define A_LIT(lit) AR(rb_dump_literal(lit))
#define A_LOC(loc) \
rb_str_catf(buf, "(%d,%d)-(%d,%d)", \
loc.beg_pos.lineno, loc.beg_pos.column, \
loc.end_pos.lineno, loc.end_pos.column)
#define A_NODE_HEADER(node, term) \
rb_str_catf(buf, "@ %s (id: %d, line: %d, location: (%d,%d)-(%d,%d))%s"term, \
ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node), \
@ -84,6 +88,7 @@
#define F_LIT(name, type, ann) SIMPLE_FIELD1(#name, ann) A_LIT(type(node)->name)
#define F_VALUE(name, val, ann) SIMPLE_FIELD1(#name, ann) A_LIT(val)
#define F_MSG(name, ann, desc) SIMPLE_FIELD1(#name, ann) A(desc)
#define F_LOC(name, type) SIMPLE_FIELD1(#name, "") A_LOC(type(node)->name)
#define F_SHAREABILITY(name, type, ann) SIMPLE_FIELD1(#name, ann) A_SHAREABILITY(type(node)->name)
#define F_NODE(name, type, ann) \
@ -244,8 +249,11 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
ANN("example: unless x == 1 then foo else bar end");
F_NODE(nd_cond, RNODE_UNLESS, "condition expr");
F_NODE(nd_body, RNODE_UNLESS, "then clause");
LAST_NODE;
F_NODE(nd_else, RNODE_UNLESS, "else clause");
F_LOC(keyword_loc, RNODE_UNLESS);
F_LOC(then_keyword_loc, RNODE_UNLESS);
LAST_NODE;
F_LOC(end_keyword_loc, RNODE_UNLESS);
return;
case NODE_CASE:

21
parse.y
Просмотреть файл

@ -1060,7 +1060,7 @@ static rb_node_scope_t *rb_node_scope_new(struct parser_params *p, rb_node_args_
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_unless_t *rb_node_unless_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_else, const YYLTYPE *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);
static rb_node_case2_t *rb_node_case2_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc);
static rb_node_case3_t *rb_node_case3_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc);
@ -1168,7 +1168,7 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE
#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_UNLESS(c,t,e,loc) (NODE *)rb_node_unless_new(p,c,t,e,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) (NODE *)rb_node_case_new(p,h,b,loc)
#define NEW_CASE2(b,loc) (NODE *)rb_node_case2_new(p,b,loc)
#define NEW_CASE3(h,b,loc) (NODE *)rb_node_case3_new(p,h,b,loc)
@ -1389,7 +1389,7 @@ 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_unless(struct parser_params*,NODE*,NODE*,NODE*,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*);
static NODE *newline_node(NODE*);
@ -3167,7 +3167,7 @@ stmt : keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
}
| stmt modifier_unless expr_value
{
$$ = new_unless(p, $3, remove_begin($1), 0, &@$);
$$ = new_unless(p, $3, remove_begin($1), 0, &@$, &@2, &NULL_LOC, &NULL_LOC);
fixpos($$, $3);
/*% ripper: unless_mod!($:3, $:1) %*/
}
@ -4516,7 +4516,7 @@ primary : literal
opt_else
k_end
{
$$ = new_unless(p, $2, $4, $5, &@$);
$$ = new_unless(p, $2, $4, $5, &@$, &@1, &@3, &@6);
fixpos($$, $2);
/*% ripper: unless!($:2, $:4, $:5) %*/
}
@ -5488,7 +5488,7 @@ p_top_expr : p_top_expr_body
}
| p_top_expr_body modifier_unless expr_value
{
$$ = new_unless(p, $3, $1, 0, &@$);
$$ = new_unless(p, $3, $1, 0, &@$, &@2, &NULL_LOC, &NULL_LOC);
fixpos($$, $3);
/*% ripper: unless_mod!($:3, $:1) %*/
}
@ -11530,12 +11530,15 @@ rb_node_if_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, NODE *nd_e
}
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)
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)
{
rb_node_unless_t *n = NODE_NEWNODE(NODE_UNLESS, rb_node_unless_t, loc);
n->nd_cond = nd_cond;
n->nd_body = nd_body;
n->nd_else = nd_else;
n->keyword_loc = *keyword_loc;
n->then_keyword_loc = *then_keyword_loc;
n->end_keyword_loc = *end_keyword_loc;
return n;
}
@ -14319,11 +14322,11 @@ new_if(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE
}
static NODE*
new_unless(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc)
new_unless(struct parser_params *p, NODE *cc, NODE *left, NODE *right, const YYLTYPE *loc, const YYLTYPE *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_UNLESS(cc, left, right, loc));
return newline_node(NEW_UNLESS(cc, left, right, loc, keyword_loc, then_keyword_loc, end_keyword_loc));
}
#define NEW_AND_OR(type, f, s, loc) (type == NODE_AND ? NEW_AND(f,s,loc) : NEW_OR(f,s,loc))

Просмотреть файл

@ -273,6 +273,9 @@ typedef struct RNode_UNLESS {
struct RNode *nd_cond;
struct RNode *nd_body;
struct RNode *nd_else;
rb_code_location_t keyword_loc;
rb_code_location_t then_keyword_loc;
rb_code_location_t end_keyword_loc;
} rb_node_unless_t;
typedef struct RNode_CASE {

Просмотреть файл

@ -1327,12 +1327,22 @@ dummy
class TestLocation < Test::Unit::TestCase
def test_lineno_and_column
node = RubyVM::AbstractSyntaxTree.parse("1 + 2")
location = node.locations[0]
assert_locations(node.locations, [[1, 0, 1, 5]])
end
assert_equal(1, location.first_lineno)
assert_equal(0, location.first_column)
assert_equal(1, location.last_lineno)
assert_equal(5, location.last_column)
def test_unless_locations
node = RubyVM::AbstractSyntaxTree.parse("unless cond then 1 else 2 end")
assert_locations(node.children[-1].locations, [[1, 0, 1, 29], [1, 0, 1, 6], [1, 12, 1, 16], [1, 26, 1, 29]])
node = RubyVM::AbstractSyntaxTree.parse("1 unless 2")
assert_locations(node.children[-1].locations, [[1, 0, 1, 10], [1, 2, 1, 8], nil, nil])
end
private
def assert_locations(locations, expected)
ary = locations.map {|loc| loc && [loc.first_lineno, loc.first_column, loc.last_lineno, loc.last_column] }
assert_equal(ary, expected)
end
end
end