[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]