aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryui-knk <spiketeika@gmail.com>2022-09-25 17:53:44 +0900
committerYuichiro Kaneko <spiketeika@gmail.com>2022-10-08 17:59:11 +0900
commitfbbdbdd8911ffb24d98bb71c7c33d24609ce7dfe (patch)
tree74e11b409521113dedae0e28e7013a22e61b8c3f
parent7775d14356c375536c915ea4bd0fae019acaaeb1 (diff)
downloadruby-fbbdbdd8911ffb24d98bb71c7c33d24609ce7dfe.tar.gz
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]
-rw-r--r--ast.c29
-rw-r--r--ast.rb12
-rw-r--r--internal/parse.h1
-rw-r--r--parse.y15
-rw-r--r--test/ruby/test_ast.rb13
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)
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);
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
#
# 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
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 */
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 {
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
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
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