зеркало из https://github.com/github/ruby.git
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:
Родитель
7775d14356
Коммит
fbbdbdd891
29
ast.c
29
ast.c
|
@ -64,8 +64,8 @@ ast_new_internal(rb_ast_t *ast, const NODE *node)
|
|||
return obj;
|
||||
}
|
||||
|
||||
static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines);
|
||||
static VALUE rb_ast_parse_file(VALUE path, 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, VALUE error_tolerant);
|
||||
|
||||
static VALUE
|
||||
ast_parse_new(void)
|
||||
|
@ -85,31 +85,32 @@ ast_parse_done(rb_ast_t *ast)
|
|||
}
|
||||
|
||||
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
|
||||
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;
|
||||
|
||||
StringValue(str);
|
||||
VALUE vparser = ast_parse_new();
|
||||
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);
|
||||
return ast_parse_done(ast);
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
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("-"));
|
||||
VALUE vparser = ast_parse_new();
|
||||
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);
|
||||
rb_io_close(f);
|
||||
return ast_parse_done(ast);
|
||||
|
@ -139,13 +141,14 @@ lex_array(VALUE array, int index)
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
array = rb_check_array_type(array);
|
||||
VALUE vparser = ast_parse_new();
|
||||
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);
|
||||
return ast_parse_done(ast);
|
||||
}
|
||||
|
@ -193,7 +196,7 @@ script_lines(VALUE path)
|
|||
}
|
||||
|
||||
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;
|
||||
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))) {
|
||||
node = rb_ast_parse_array(lines, keep_script_lines);
|
||||
node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant);
|
||||
}
|
||||
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 {
|
||||
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);
|
||||
|
|
12
ast.rb
12
ast.rb
|
@ -29,8 +29,8 @@ module RubyVM::AbstractSyntaxTree
|
|||
#
|
||||
# RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
|
||||
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
|
||||
def self.parse string, keep_script_lines: false
|
||||
Primitive.ast_s_parse string, keep_script_lines
|
||||
def self.parse string, keep_script_lines: false, error_tolerant: false
|
||||
Primitive.ast_s_parse string, keep_script_lines, error_tolerant
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
|
@ -44,8 +44,8 @@ module RubyVM::AbstractSyntaxTree
|
|||
#
|
||||
# RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
|
||||
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
|
||||
def self.parse_file pathname, keep_script_lines: false
|
||||
Primitive.ast_s_parse_file pathname, keep_script_lines
|
||||
def self.parse_file pathname, keep_script_lines: false, error_tolerant: false
|
||||
Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
|
@ -63,8 +63,8 @@ module RubyVM::AbstractSyntaxTree
|
|||
#
|
||||
# RubyVM::AbstractSyntaxTree.of(method(:hello))
|
||||
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
|
||||
def self.of body, keep_script_lines: false
|
||||
Primitive.ast_s_of body, keep_script_lines
|
||||
def self.of body, keep_script_lines: false, error_tolerant: false
|
||||
Primitive.ast_s_of body, keep_script_lines, error_tolerant
|
||||
end
|
||||
|
||||
# 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);
|
||||
void *rb_parser_load_file(VALUE parser, VALUE name);
|
||||
void rb_parser_keep_script_lines(VALUE vparser);
|
||||
void rb_parser_error_tolerant(VALUE vparser);
|
||||
|
||||
RUBY_SYMBOL_EXPORT_BEGIN
|
||||
VALUE rb_parser_set_context(VALUE, const struct rb_iseq_struct *, int);
|
||||
|
|
15
parse.y
15
parse.y
|
@ -348,6 +348,7 @@ struct parser_params {
|
|||
unsigned int do_chomp: 1;
|
||||
unsigned int do_split: 1;
|
||||
unsigned int keep_script_lines: 1;
|
||||
unsigned int error_tolerant: 1;
|
||||
|
||||
NODE *eval_tree_begin;
|
||||
NODE *eval_tree;
|
||||
|
@ -6384,7 +6385,9 @@ yycompile0(VALUE arg)
|
|||
mesg = rb_class_new_instance(0, 0, rb_eSyntaxError);
|
||||
}
|
||||
rb_set_errinfo(mesg);
|
||||
return FALSE;
|
||||
if (!p->error_tolerant) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
tree = p->eval_tree;
|
||||
if (!tree) {
|
||||
|
@ -13313,6 +13316,16 @@ rb_parser_keep_script_lines(VALUE vparser)
|
|||
TypedData_Get_Struct(vparser, struct parser_params, &parser_data_type, p);
|
||||
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
|
||||
|
||||
#ifdef RIPPER
|
||||
|
|
|
@ -565,4 +565,17 @@ dummy
|
|||
assert_in_out_err(["-e", "def foo; end; pp RubyVM::AbstractSyntaxTree.of(method(:foo)).type"],
|
||||
"", [":SCOPE"], [])
|
||||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче