Skip to content
9 changes: 7 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,13 @@ Note: We're only listing outstanding class updates.
* Binding

* `Binding#local_variables` does no longer include numbered parameters.
Also, `Binding#local_variable_get` and `Binding#local_variable_set` reject
to handle numbered parameters. [[Bug #21049]]
Also, `Binding#local_variable_get`, `Binding#local_variable_set`, and
`Binding#local_variable_defined?` reject to handle numbered parameters.
[[Bug #21049]]

* `Binding#implicit_parameters`, `Binding#implicit_parameter_get`, and
`Binding#implicit_parameter_defined?` have been added to access
numbered parameters and "it" parameter. [[Bug #21049]]

* File

Expand Down
95 changes: 95 additions & 0 deletions array.c
Original file line number Diff line number Diff line change
Expand Up @@ -2088,6 +2088,99 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary)
return RARRAY_AREF(ary, idx);
}

/*
* call-seq:
* find(if_none_proc = nil) {|element| ... } -> object or nil
* find(if_none_proc = nil) -> enumerator
*
* Returns the first element for which the block returns a truthy value.
*
* With a block given, calls the block with successive elements of the array;
* returns the first element for which the block returns a truthy value:
*
* (0..9).find {|element| element > 2} # => 3
*
* If no such element is found, calls +if_none_proc+ and returns its return value.
*
* (0..9).find(proc {false}) {|element| element > 12} # => false
* {foo: 0, bar: 1, baz: 2}.find {|key, value| key.start_with?('b') } # => [:bar, 1]
* {foo: 0, bar: 1, baz: 2}.find(proc {[]}) {|key, value| key.start_with?('c') } # => []
*
* With no block given, returns an Enumerator.
*
*/

static VALUE
rb_ary_find(int argc, VALUE *argv, VALUE ary)
{
VALUE if_none;
long idx;

RETURN_ENUMERATOR(ary, argc, argv);
if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;

for (idx = 0; idx < RARRAY_LEN(ary); idx++) {
VALUE elem = RARRAY_AREF(ary, idx);
if (RTEST(rb_yield(elem))) {
return elem;
}
}

if (!NIL_P(if_none)) {
return rb_funcallv(if_none, idCall, 0, 0);
}
return Qnil;
}

/*
* call-seq:
* rfind(if_none_proc = nil) {|element| ... } -> object or nil
* rfind(if_none_proc = nil) -> enumerator
*
* Returns the last element for which the block returns a truthy value.
*
* With a block given, calls the block with successive elements of the array in
* reverse order; returns the last element for which the block returns a truthy
* value:
*
* (0..9).rfind {|element| element < 5} # => 4
*
* If no such element is found, calls +if_none_proc+ and returns its return value.
*
* (0..9).rfind(proc {false}) {|element| element < -2} # => false
* {foo: 0, bar: 1, baz: 2}.rfind {|key, value| key.start_with?('b') } # => [:baz, 2]
* {foo: 0, bar: 1, baz: 2}.rfind(proc {[]}) {|key, value| key.start_with?('c') } # => []
*
* With no block given, returns an Enumerator.
*
*/

static VALUE
rb_ary_rfind(int argc, VALUE *argv, VALUE ary)
{
VALUE if_none;
long len, idx;

RETURN_ENUMERATOR(ary, argc, argv);
if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;

idx = RARRAY_LEN(ary);
while (idx--) {
VALUE elem = RARRAY_AREF(ary, idx);
if (RTEST(rb_yield(elem))) {
return elem;
}

len = RARRAY_LEN(ary);
idx = (idx >= len) ? len : idx;
}

if (!NIL_P(if_none)) {
return rb_funcallv(if_none, idCall, 0, 0);
}
return Qnil;
}

/*
* call-seq:
* find_index(object) -> integer or nil
Expand Down Expand Up @@ -8816,6 +8909,8 @@ Init_Array(void)
rb_define_method(rb_cArray, "length", rb_ary_length, 0);
rb_define_method(rb_cArray, "size", rb_ary_length, 0);
rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0);
rb_define_method(rb_cArray, "find", rb_ary_find, -1);
rb_define_method(rb_cArray, "rfind", rb_ary_rfind, -1);
rb_define_method(rb_cArray, "find_index", rb_ary_index, -1);
rb_define_method(rb_cArray, "index", rb_ary_index, -1);
rb_define_method(rb_cArray, "rindex", rb_ary_rindex, -1);
Expand Down
2 changes: 2 additions & 0 deletions defs/id.def
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ firstline, predefined = __LINE__+1, %[\
_7 NUMPARAM_7
_8 NUMPARAM_8
_9 NUMPARAM_9
<it> ItImplicit
it It

"/*NULL*/" NULL
empty?
Expand Down
13 changes: 7 additions & 6 deletions iseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#endif

#include "eval_intern.h"
#include "id.h"
#include "id_table.h"
#include "internal.h"
#include "internal/bits.h"
Expand Down Expand Up @@ -3363,7 +3364,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
for (i=0; i<iseq_body->local_table_size; i++) {
ID lid = iseq_body->local_table[i];
if (lid) {
if (rb_id2str(lid)) {
if (lid != idItImplicit && rb_id2str(lid)) {
rb_ary_push(locals, ID2SYM(lid));
}
else { /* hidden variable from id_internal() */
Expand Down Expand Up @@ -3673,10 +3674,10 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
ID req, opt, rest, block, key, keyrest;
#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
#define PARAM_ID(i) body->local_table[(i)]
#define PARAM(i, type) ( \
PARAM_TYPE(type), \
rb_id2str(PARAM_ID(i)) ? \
rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
#define PARAM(i, type) ( \
PARAM_TYPE(type), \
PARAM_ID(i) != idItImplicit && rb_id2str(PARAM_ID(i)) ? \
rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
a)

CONST_ID(req, "req");
Expand All @@ -3695,7 +3696,7 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
if (is_proc) {
for (i = 0; i < body->param.lead_num; i++) {
PARAM_TYPE(opt);
if (rb_id2str(PARAM_ID(i))) {
if (PARAM_ID(i) != idItImplicit && rb_id2str(PARAM_ID(i))) {
rb_ary_push(a, ID2SYM(PARAM_ID(i)));
}
rb_ary_push(args, a);
Expand Down
62 changes: 46 additions & 16 deletions lib/prism/translation/ruby_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -415,14 +415,18 @@ def visit_class_node(node)
visit(node.constant_path)
end

if node.body.nil?
s(node, :class, name, visit(node.superclass))
elsif node.body.is_a?(StatementsNode)
compiler = copy_compiler(in_def: false)
s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
else
s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
end
result =
if node.body.nil?
s(node, :class, name, visit(node.superclass))
elsif node.body.is_a?(StatementsNode)
compiler = copy_compiler(in_def: false)
s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
else
s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
end

attach_comments(result, node)
result
end

# ```
Expand Down Expand Up @@ -611,7 +615,9 @@ def visit_def_node(node)
s(node, :defs, visit(node.receiver), name)
end

attach_comments(result, node)
result.line(node.name_loc.start_line)

if node.parameters.nil?
result << s(node, :args).line(node.name_loc.start_line)
else
Expand Down Expand Up @@ -1270,14 +1276,18 @@ def visit_module_node(node)
visit(node.constant_path)
end

if node.body.nil?
s(node, :module, name)
elsif node.body.is_a?(StatementsNode)
compiler = copy_compiler(in_def: false)
s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
else
s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
end
result =
if node.body.nil?
s(node, :module, name)
elsif node.body.is_a?(StatementsNode)
compiler = copy_compiler(in_def: false)
s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
else
s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
end

attach_comments(result, node)
result
end

# ```
Expand Down Expand Up @@ -1820,6 +1830,17 @@ def visit_yield_node(node)

private

# Attach prism comments to the given sexp.
def attach_comments(sexp, node)
return unless node.comments
return if node.comments.empty?

extra = node.location.start_line - node.comments.last.location.start_line
comments = node.comments.map(&:slice)
comments.concat([nil] * [0, extra].max)
sexp.comments = comments.join("\n")
end

# Create a new compiler with the given options.
def copy_compiler(in_def: self.in_def, in_pattern: self.in_pattern)
Compiler.new(file, in_def: in_def, in_pattern: in_pattern)
Expand Down Expand Up @@ -1898,6 +1919,14 @@ def parse_file(filepath)
translate(Prism.parse_file(filepath, partial_script: true), filepath)
end

# Parse the give file and translate it into the
# seattlerb/ruby_parser gem's Sexp format. This method is
# provided for API compatibility to RubyParser and takes an
# optional +timeout+ argument.
def process(ruby, file = "(string)", timeout = nil)
Timeout.timeout(timeout) { parse(ruby, file) }
end

class << self
# Parse the given source and translate it into the seattlerb/ruby_parser
# gem's Sexp format.
Expand All @@ -1922,6 +1951,7 @@ def translate(result, filepath)
raise ::RubyParser::SyntaxError, "#{filepath}:#{error.location.start_line} :: #{error.message}"
end

result.attach_comments!
result.value.accept(Compiler.new(filepath))
end
end
Expand Down
4 changes: 2 additions & 2 deletions parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -13035,14 +13035,14 @@ gettable(struct parser_params *p, ID id, const YYLTYPE *loc)
}
# endif
/* method call without arguments */
if (dyna_in_block(p) && id == rb_intern("it") && !(DVARS_TERMINAL_P(p->lvtbl->args) || DVARS_TERMINAL_P(p->lvtbl->args->prev))) {
if (dyna_in_block(p) && id == idIt && !(DVARS_TERMINAL_P(p->lvtbl->args) || DVARS_TERMINAL_P(p->lvtbl->args->prev))) {
if (numparam_used_p(p)) return 0;
if (p->max_numparam == ORDINAL_PARAM) {
compile_error(p, "ordinary parameter is defined");
return 0;
}
if (!p->it_id) {
p->it_id = internal_id(p);
p->it_id = idItImplicit;
vtable_add(p->lvtbl->args, p->it_id);
}
NODE *node = NEW_DVAR(p->it_id, loc);
Expand Down
3 changes: 1 addition & 2 deletions prism_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6398,8 +6398,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
}

if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
ID local = rb_make_temporary_id(local_index);
local_table_for_iseq->ids[local_index++] = local;
local_table_for_iseq->ids[local_index++] = idItImplicit;
}

// def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
Expand Down
Loading