mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Add error_tolerant option to RubyVM::AST
If this option is enabled, SyntaxError is not raised and Node is returned even if passed script is broken. [Feature #19013]
This commit is contained in:
parent
7775d14356
commit
fbbdbdd891
Notes:
git
2022-10-08 17:59:35 +09:00
5 changed files with 50 additions and 20 deletions
29
ast.c
29
ast.c
|
@ -64,8 +64,8 @@ ast_new_internal(rb_ast_t *ast, const NODE *node)
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines);
|
static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant);
|
||||||
static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines);
|
static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant);
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
ast_parse_new(void)
|
ast_parse_new(void)
|
||||||
|
@ -85,31 +85,32 @@ ast_parse_done(rb_ast_t *ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines)
|
ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines, VALUE error_tolerant)
|
||||||
{
|
{
|
||||||
return rb_ast_parse_str(str, keep_script_lines);
|
return rb_ast_parse_str(str, keep_script_lines, error_tolerant);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_ast_parse_str(VALUE str, VALUE keep_script_lines)
|
rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant)
|
||||||
{
|
{
|
||||||
rb_ast_t *ast = 0;
|
rb_ast_t *ast = 0;
|
||||||
|
|
||||||
StringValue(str);
|
StringValue(str);
|
||||||
VALUE vparser = ast_parse_new();
|
VALUE vparser = ast_parse_new();
|
||||||
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
|
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
|
||||||
|
if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
|
||||||
ast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
|
ast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
|
||||||
return ast_parse_done(ast);
|
return ast_parse_done(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines)
|
ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines, VALUE error_tolerant)
|
||||||
{
|
{
|
||||||
return rb_ast_parse_file(path, keep_script_lines);
|
return rb_ast_parse_file(path, keep_script_lines, error_tolerant);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_ast_parse_file(VALUE path, VALUE keep_script_lines)
|
rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant)
|
||||||
{
|
{
|
||||||
VALUE f;
|
VALUE f;
|
||||||
rb_ast_t *ast = 0;
|
rb_ast_t *ast = 0;
|
||||||
|
@ -120,6 +121,7 @@ rb_ast_parse_file(VALUE path, VALUE keep_script_lines)
|
||||||
rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
|
rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
|
||||||
VALUE vparser = ast_parse_new();
|
VALUE vparser = ast_parse_new();
|
||||||
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
|
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
|
||||||
|
if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
|
||||||
ast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
|
ast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
|
||||||
rb_io_close(f);
|
rb_io_close(f);
|
||||||
return ast_parse_done(ast);
|
return ast_parse_done(ast);
|
||||||
|
@ -139,13 +141,14 @@ lex_array(VALUE array, int index)
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_ast_parse_array(VALUE array, VALUE keep_script_lines)
|
rb_ast_parse_array(VALUE array, VALUE keep_script_lines, VALUE error_tolerant)
|
||||||
{
|
{
|
||||||
rb_ast_t *ast = 0;
|
rb_ast_t *ast = 0;
|
||||||
|
|
||||||
array = rb_check_array_type(array);
|
array = rb_check_array_type(array);
|
||||||
VALUE vparser = ast_parse_new();
|
VALUE vparser = ast_parse_new();
|
||||||
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
|
if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
|
||||||
|
if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
|
||||||
ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1);
|
ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1);
|
||||||
return ast_parse_done(ast);
|
return ast_parse_done(ast);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +196,7 @@ script_lines(VALUE path)
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines)
|
ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines, VALUE error_tolerant)
|
||||||
{
|
{
|
||||||
VALUE node, lines = Qnil;
|
VALUE node, lines = Qnil;
|
||||||
const rb_iseq_t *iseq;
|
const rb_iseq_t *iseq;
|
||||||
|
@ -232,13 +235,13 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
|
if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
|
||||||
node = rb_ast_parse_array(lines, keep_script_lines);
|
node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant);
|
||||||
}
|
}
|
||||||
else if (e_option) {
|
else if (e_option) {
|
||||||
node = rb_ast_parse_str(rb_e_script, keep_script_lines);
|
node = rb_ast_parse_str(rb_e_script, keep_script_lines, error_tolerant);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node = rb_ast_parse_file(path, keep_script_lines);
|
node = rb_ast_parse_file(path, keep_script_lines, error_tolerant);
|
||||||
}
|
}
|
||||||
|
|
||||||
return node_find(node, node_id);
|
return node_find(node, node_id);
|
||||||
|
|
12
ast.rb
12
ast.rb
|
@ -29,8 +29,8 @@ module RubyVM::AbstractSyntaxTree
|
||||||
#
|
#
|
||||||
# RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
|
# RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
|
||||||
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
|
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
|
||||||
def self.parse string, keep_script_lines: false
|
def self.parse string, keep_script_lines: false, error_tolerant: false
|
||||||
Primitive.ast_s_parse string, keep_script_lines
|
Primitive.ast_s_parse string, keep_script_lines, error_tolerant
|
||||||
end
|
end
|
||||||
|
|
||||||
# call-seq:
|
# call-seq:
|
||||||
|
@ -44,8 +44,8 @@ module RubyVM::AbstractSyntaxTree
|
||||||
#
|
#
|
||||||
# RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
|
# RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
|
||||||
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
|
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
|
||||||
def self.parse_file pathname, keep_script_lines: false
|
def self.parse_file pathname, keep_script_lines: false, error_tolerant: false
|
||||||
Primitive.ast_s_parse_file pathname, keep_script_lines
|
Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant
|
||||||
end
|
end
|
||||||
|
|
||||||
# call-seq:
|
# call-seq:
|
||||||
|
@ -63,8 +63,8 @@ module RubyVM::AbstractSyntaxTree
|
||||||
#
|
#
|
||||||
# RubyVM::AbstractSyntaxTree.of(method(:hello))
|
# RubyVM::AbstractSyntaxTree.of(method(:hello))
|
||||||
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
|
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
|
||||||
def self.of body, keep_script_lines: false
|
def self.of body, keep_script_lines: false, error_tolerant: false
|
||||||
Primitive.ast_s_of body, keep_script_lines
|
Primitive.ast_s_of body, keep_script_lines, error_tolerant
|
||||||
end
|
end
|
||||||
|
|
||||||
# RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
|
# RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct rb_iseq_struct; /* in vm_core.h */
|
||||||
VALUE rb_parser_set_yydebug(VALUE, VALUE);
|
VALUE rb_parser_set_yydebug(VALUE, VALUE);
|
||||||
void *rb_parser_load_file(VALUE parser, VALUE name);
|
void *rb_parser_load_file(VALUE parser, VALUE name);
|
||||||
void rb_parser_keep_script_lines(VALUE vparser);
|
void rb_parser_keep_script_lines(VALUE vparser);
|
||||||
|
void rb_parser_error_tolerant(VALUE vparser);
|
||||||
|
|
||||||
RUBY_SYMBOL_EXPORT_BEGIN
|
RUBY_SYMBOL_EXPORT_BEGIN
|
||||||
VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
|
VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
|
||||||
|
|
13
parse.y
13
parse.y
|
@ -348,6 +348,7 @@ struct parser_params {
|
||||||
unsigned int do_chomp: 1;
|
unsigned int do_chomp: 1;
|
||||||
unsigned int do_split: 1;
|
unsigned int do_split: 1;
|
||||||
unsigned int keep_script_lines: 1;
|
unsigned int keep_script_lines: 1;
|
||||||
|
unsigned int error_tolerant: 1;
|
||||||
|
|
||||||
NODE *eval_tree_begin;
|
NODE *eval_tree_begin;
|
||||||
NODE *eval_tree;
|
NODE *eval_tree;
|
||||||
|
@ -6384,8 +6385,10 @@ yycompile0(VALUE arg)
|
||||||
mesg = rb_class_new_instance(0, 0, rb_eSyntaxError);
|
mesg = rb_class_new_instance(0, 0, rb_eSyntaxError);
|
||||||
}
|
}
|
||||||
rb_set_errinfo(mesg);
|
rb_set_errinfo(mesg);
|
||||||
|
if (!p->error_tolerant) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
tree = p->eval_tree;
|
tree = p->eval_tree;
|
||||||
if (!tree) {
|
if (!tree) {
|
||||||
tree = NEW_NIL(&NULL_LOC);
|
tree = NEW_NIL(&NULL_LOC);
|
||||||
|
@ -13313,6 +13316,16 @@ rb_parser_keep_script_lines(VALUE vparser)
|
||||||
TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
|
TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
|
||||||
p->keep_script_lines = 1;
|
p->keep_script_lines = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_parser_error_tolerant(VALUE vparser)
|
||||||
|
{
|
||||||
|
struct parser_params *p;
|
||||||
|
|
||||||
|
TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
|
||||||
|
p->error_tolerant = 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef RIPPER
|
#ifdef RIPPER
|
||||||
|
|
|
@ -565,4 +565,17 @@ dummy
|
||||||
assert_in_out_err(["-e", "def foo; end; pp RubyVM::AbstractSyntaxTree.of(method(:foo)).type"],
|
assert_in_out_err(["-e", "def foo; end; pp RubyVM::AbstractSyntaxTree.of(method(:foo)).type"],
|
||||||
"", [":SCOPE"], [])
|
"", [":SCOPE"], [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_error_tolerant
|
||||||
|
node = RubyVM::AbstractSyntaxTree.parse(<<~STR, error_tolerant: true)
|
||||||
|
class A
|
||||||
|
def m
|
||||||
|
if;
|
||||||
|
a = 10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
STR
|
||||||
|
|
||||||
|
assert_equal(:SCOPE, node.type)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue