зеркало из https://github.com/github/ruby.git
[Feature #20624] Enhance `RubyVM::AbstractSyntaxTree::Node#locations`
This commit introduce `RubyVM::AbstractSyntaxTree::Node#locations` method and `RubyVM::AbstractSyntaxTree::Location` class. Ruby AST node will hold multiple locations information. `RubyVM::AbstractSyntaxTree::Node#locations` provides a way to access these locations information. `RubyVM::AbstractSyntaxTree::Location` is a class which holds these location information: * `#first_lineno` * `#first_column` * `#last_lineno` * `#last_column`
This commit is contained in:
Родитель
5617fec1f8
Коммит
f23485a8d6
7
NEWS.md
7
NEWS.md
|
@ -44,6 +44,12 @@ Note: We're only listing outstanding class updates.
|
|||
|
||||
* Range#size now raises TypeError if the range is not iterable. [[Misc #18984]]
|
||||
|
||||
* RubyVM::AbstractSyntaxTree
|
||||
|
||||
* Add `RubyVM::AbstractSyntaxTree::Node#locations` method which returns location objects
|
||||
associated with the AST node. [[Feature #20624]]
|
||||
* Add `RubyVM::AbstractSyntaxTree::Location` class which holds location information. [[Feature #20624]]
|
||||
|
||||
## Stdlib updates
|
||||
|
||||
* Tempfile
|
||||
|
@ -160,3 +166,4 @@ See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/log
|
|||
[Feature #20429]: https://bugs.ruby-lang.org/issues/20429
|
||||
[Feature #20443]: https://bugs.ruby-lang.org/issues/20443
|
||||
[Feature #20497]: https://bugs.ruby-lang.org/issues/20497
|
||||
[Feature #20624]: https://bugs.ruby-lang.org/issues/20624
|
||||
|
|
123
ast.c
123
ast.c
|
@ -14,6 +14,7 @@
|
|||
|
||||
static VALUE rb_mAST;
|
||||
static VALUE rb_cNode;
|
||||
static VALUE rb_cLocation;
|
||||
|
||||
struct ASTNodeData {
|
||||
VALUE ast_value;
|
||||
|
@ -43,6 +44,32 @@ static const rb_data_type_t rb_node_type = {
|
|||
RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
struct ASTLocationData {
|
||||
int first_lineno;
|
||||
int first_column;
|
||||
int last_lineno;
|
||||
int last_column;
|
||||
};
|
||||
|
||||
static void
|
||||
location_gc_mark(void *ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static size_t
|
||||
location_memsize(const void *ptr)
|
||||
{
|
||||
return sizeof(struct ASTLocationData);
|
||||
}
|
||||
|
||||
static const rb_data_type_t rb_location_type = {
|
||||
"AST/location",
|
||||
{location_gc_mark, RUBY_TYPED_DEFAULT_FREE, location_memsize,},
|
||||
0, 0,
|
||||
RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
|
||||
static VALUE rb_ast_node_alloc(VALUE klass);
|
||||
|
||||
static void
|
||||
|
@ -720,6 +747,45 @@ ast_node_children(rb_execution_context_t *ec, VALUE self)
|
|||
return node_children(data->ast_value, data->node);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
location_new(rb_code_location_t *loc)
|
||||
{
|
||||
VALUE obj;
|
||||
struct ASTLocationData *data;
|
||||
|
||||
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;
|
||||
data->last_lineno = loc->end_pos.lineno;
|
||||
data->last_column = loc->end_pos.column;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
node_locations(VALUE ast_value, const NODE *node)
|
||||
{
|
||||
enum node_type type = nd_type(node);
|
||||
switch (type) {
|
||||
case NODE_ARGS_AUX:
|
||||
case NODE_LAST:
|
||||
break;
|
||||
default:
|
||||
return rb_ary_new_from_args(1, location_new(nd_code_loc(node)));
|
||||
}
|
||||
|
||||
rb_bug("node_locations: unknown node: %s", ruby_node_name(type));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_node_locations(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
struct ASTNodeData *data;
|
||||
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
|
||||
|
||||
return node_locations(data->ast_value, data->node);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_node_first_lineno(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
|
@ -823,6 +889,61 @@ ast_node_script_lines(rb_execution_context_t *ec, VALUE self)
|
|||
return rb_parser_build_script_lines_from(ret);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_location_first_lineno(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
struct ASTLocationData *data;
|
||||
TypedData_Get_Struct(self, struct ASTLocationData, &rb_location_type, data);
|
||||
|
||||
return INT2NUM(data->first_lineno);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_location_first_column(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
struct ASTLocationData *data;
|
||||
TypedData_Get_Struct(self, struct ASTLocationData, &rb_location_type, data);
|
||||
|
||||
return INT2NUM(data->first_column);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_location_last_lineno(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
struct ASTLocationData *data;
|
||||
TypedData_Get_Struct(self, struct ASTLocationData, &rb_location_type, data);
|
||||
|
||||
return INT2NUM(data->last_lineno);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_location_last_column(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
struct ASTLocationData *data;
|
||||
TypedData_Get_Struct(self, struct ASTLocationData, &rb_location_type, data);
|
||||
|
||||
return INT2NUM(data->last_column);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
ast_location_inspect(rb_execution_context_t *ec, VALUE self)
|
||||
{
|
||||
VALUE str;
|
||||
VALUE cname;
|
||||
struct ASTLocationData *data;
|
||||
TypedData_Get_Struct(self, struct ASTLocationData, &rb_location_type, data);
|
||||
|
||||
cname = rb_class_path(rb_obj_class(self));
|
||||
str = rb_str_new2("#<");
|
||||
|
||||
rb_str_append(str, cname);
|
||||
rb_str_catf(str, ":@%d:%d-%d:%d>",
|
||||
data->first_lineno, data->first_column,
|
||||
data->last_lineno, data->last_column);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#include "ast.rbinc"
|
||||
|
||||
void
|
||||
|
@ -830,5 +951,7 @@ Init_ast(void)
|
|||
{
|
||||
rb_mAST = rb_define_module_under(rb_cRubyVM, "AbstractSyntaxTree");
|
||||
rb_cNode = rb_define_class_under(rb_mAST, "Node", rb_cObject);
|
||||
rb_cLocation = rb_define_class_under(rb_mAST, "Location", rb_cObject);
|
||||
rb_undef_alloc_func(rb_cNode);
|
||||
rb_undef_alloc_func(rb_cLocation);
|
||||
}
|
||||
|
|
57
ast.rb
57
ast.rb
|
@ -272,5 +272,62 @@ module RubyVM::AbstractSyntaxTree
|
|||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# node.locations -> array
|
||||
#
|
||||
# Returns location objects associated with the AST node.
|
||||
# The returned array contains RubyVM::AbstractSyntaxTree::Location.
|
||||
def locations
|
||||
Primitive.ast_node_locations
|
||||
end
|
||||
end
|
||||
|
||||
# RubyVM::AbstractSyntaxTree::Location instances are created by
|
||||
# RubyVM::AbstractSyntaxTree#locations.
|
||||
#
|
||||
# This class is MRI specific.
|
||||
#
|
||||
class Location
|
||||
|
||||
# call-seq:
|
||||
# location.first_lineno -> integer
|
||||
#
|
||||
# The line number in the source code where this AST's text began.
|
||||
def first_lineno
|
||||
Primitive.ast_location_first_lineno
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# location.first_column -> integer
|
||||
#
|
||||
# The column number in the source code where this AST's text began.
|
||||
def first_column
|
||||
Primitive.ast_location_first_column
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# location.last_lineno -> integer
|
||||
#
|
||||
# The line number in the source code where this AST's text ended.
|
||||
def last_lineno
|
||||
Primitive.ast_location_last_lineno
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# location.last_column -> integer
|
||||
#
|
||||
# The column number in the source code where this AST's text ended.
|
||||
def last_column
|
||||
Primitive.ast_location_last_column
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# location.inspect -> string
|
||||
#
|
||||
# Returns debugging information about this location as a string.
|
||||
def inspect
|
||||
Primitive.ast_location_inspect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1297,6 +1297,13 @@ dummy
|
|||
end;
|
||||
end
|
||||
|
||||
def test_locations
|
||||
node = RubyVM::AbstractSyntaxTree.parse("1 + 2")
|
||||
locations = node.locations
|
||||
|
||||
assert_equal(RubyVM::AbstractSyntaxTree::Location, locations[0].class)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_error_tolerant(src, expected, keep_tokens: false)
|
||||
|
@ -1316,4 +1323,16 @@ dummy
|
|||
assert_equal(expected, str)
|
||||
node
|
||||
end
|
||||
|
||||
class TestLocation < Test::Unit::TestCase
|
||||
def test_lineno_and_column
|
||||
node = RubyVM::AbstractSyntaxTree.parse("1 + 2")
|
||||
location = node.locations[0]
|
||||
|
||||
assert_equal(1, location.first_lineno)
|
||||
assert_equal(0, location.first_column)
|
||||
assert_equal(1, location.last_lineno)
|
||||
assert_equal(5, location.last_column)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче