ruby-changes:73927
From: yui-knk <ko1@a...>
Date: Sat, 8 Oct 2022 17:59:33 +0900 (JST)
Subject: [ruby-changes:73927] fbbdbdd891 (master): Add error_tolerant option to RubyVM::AST
https://git.ruby-lang.org/ruby.git/commit/?id=fbbdbdd891 From fbbdbdd8911ffb24d98bb71c7c33d24609ce7dfe Mon Sep 17 00:00:00 2001 From: yui-knk <spiketeika@g...> Date: Sun, 25 Sep 2022 17:53:44 +0900 Subject: 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] --- ast.c | 29 ++++++++++++++++------------- ast.rb | 12 ++++++------ internal/parse.h | 1 + parse.y | 15 ++++++++++++++- test/ruby/test_ast.rb | 13 +++++++++++++ 5 files changed, 50 insertions(+), 20 deletions(-) diff --git a/ast.c b/ast.c index 42d4126a5b..67275c47b3 100644 --- a/ast.c +++ b/ast.c @@ -64,8 +64,8 @@ ast_new_internal(rb_ast_t *ast, const NODE *node) https://github.com/ruby/ruby/blob/trunk/ast.c#L64 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) https://github.com/ruby/ruby/blob/trunk/ast.c#L85 } 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) https://github.com/ruby/ruby/blob/trunk/ast.c#L121 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) https://github.com/ruby/ruby/blob/trunk/ast.c#L141 } 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) https://github.com/ruby/ruby/blob/trunk/ast.c#L196 } 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 https://github.com/ruby/ruby/blob/trunk/ast.c#L235 } 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); diff --git a/ast.rb b/ast.rb index f866bd23e5..24fd8e5526 100644 --- a/ast.rb +++ b/ast.rb @@ -29,8 +29,8 @@ module RubyVM::AbstractSyntaxTree https://github.com/ruby/ruby/blob/trunk/ast.rb#L29 # # 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 https://github.com/ruby/ruby/blob/trunk/ast.rb#L44 # # 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 https://github.com/ruby/ruby/blob/trunk/ast.rb#L63 # # 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 diff --git a/internal/parse.h b/internal/parse.h index d9f5b56bc5..37133827f5 100644 --- a/internal/parse.h +++ b/internal/parse.h @@ -15,6 +15,7 @@ struct rb_iseq_struct; /* in vm_core.h */ https://github.com/ruby/ruby/blob/trunk/internal/parse.h#L15 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); diff --git a/parse.y b/parse.y index b16dc35b88..c05ce48068 100644 --- a/parse.y +++ b/parse.y @@ -348,6 +348,7 @@ struct parser_params { https://github.com/ruby/ruby/blob/trunk/parse.y#L348 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) https://github.com/ruby/ruby/blob/trunk/parse.y#L6385 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) https://github.com/ruby/ruby/blob/trunk/parse.y#L13316 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 diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index d28d7e1fab..c2c5356f83 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -565,4 +565,17 @@ dummy https://github.com/ruby/ruby/blob/trunk/test/ruby/test_ast.rb#L565 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 -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/