Skip to content

Commit

Permalink
fixes for single array block argument
Browse files Browse the repository at this point in the history
  • Loading branch information
iliabylich committed Jan 3, 2020
1 parent f8903e3 commit d9a60f2
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 60 deletions.
21 changes: 2 additions & 19 deletions tags/language/block_tags.txt
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
fails:A block yielded a single Array assigns the Array to a single rest argument
fails:A block yielded a single Array assigns nil to unassigned required arguments
fails:A block yielded a single Array assigns elements to post arguments
fails:A block yielded a single Array assigns the last element to a non-keyword argument if #to_hash returns nil
fails:A block yielded a single Array raises a TypeError if #to_hash does not return a Hash
fails:A block yielded a single Object calls #to_ary on the object when taking multiple arguments
fails:A block yielded a single Object receives the object if #to_ary returns nil
fails:A block yielded a single Object raises a TypeError if #to_ary does not return an Array
fails:A block allows to define a block variable with the same name as the enclosing block
fails:A block does not capture a local when the block argument has the same name
fails:A block taking |a| arguments assigns the first value yielded to the argument
fails:A block taking |a, b| arguments destructures a splatted Array
fails:A block taking |a, b| arguments calls #to_ary to convert a single yielded object to an Array
fails:A block taking |a, b| arguments raises a TypeError if #to_ary does not return an Array
fails:A block taking |a, b| arguments raises the original exception if #to_ary raises an exception
fails:A block taking |a, *b| arguments destructures a splatted Array
fails:A block taking |a, *b| arguments calls #to_ary to convert a single yielded object to an Array
fails:A block taking |a, *b| arguments raises a TypeError if #to_ary does not return an Array
fails:A block taking |*a| arguments assigns '[[]]' to the argument when passed an empty Array
fails:A block taking |*a| arguments assigns a single Array value passed to the argument by wrapping it in an Array
fails:A block taking |*a| arguments does not call #to_ary if the single yielded object is an Array
fails:A block taking |a, | arguments calls #to_ary to convert a single yielded object to an Array
fails:A block taking |a, | arguments raises a TypeError if #to_ary does not return an Array
fails:A block taking |(a, b), c| arguments calls #to_ary to convert a single yielded object to an Array
fails:A block taking |*a| arguments assigns all the values passed to the argument as an Array
fails:A block taking |*a, b:| merges the hash into the splatted array
fails:A block arguments with _ extracts arguments with _
fails:A block arguments with _ assigns the first variable named
3 changes: 3 additions & 0 deletions tags/language/proc_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
fails:A Proc taking |a, b| arguments does not call #to_ary to convert a single passed object to an Array
fails:A Proc taking |a, *b| arguments does not destructure a single Array value yielded
fails:A Proc taking |a, *b| arguments does not call #to_ary to convert a single passed object to an Array
fails:A Proc taking |a, | arguments does not destructure when passed a single Array
fails:A Proc taking |a, | arguments does not call #to_ary to convert a single passed object to an Array
11 changes: 7 additions & 4 deletions tags/language/yield_tags.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
fails:The yield call taking no arguments raises a LocalJumpError when the method is not passed a block
fails:The yield call taking no arguments ignores assignment to the explicit block argument and calls the passed block
fails:The yield call taking a single argument when no block is given raises a LocalJumpError
fails:The yield call taking a single argument yielding to a literal block passes an empty Array when the argument is an empty Array
fails:The yield call taking a single argument yielding to a literal block passes a single, multi-value Array
fails:The yield call taking a single argument yielding to a lambda passes an empty Array when the argument is an empty Array
fails:The yield call taking a single argument yielding to a lambda passes a single, multi-value Array
fails:The yield call taking a single argument yielding to a lambda should not destructure an Array into multiple arguments
fails:The yield call taking multiple arguments raises a LocalJumpError when the method is not passed a block
fails:The yield call taking a single splatted argument raises a LocalJumpError when the method is not passed a block
fails:The yield call taking a single splatted argument passes a single value
fails:The yield call taking a single splatted argument passes no arguments when the argument is an empty Array
fails:The yield call taking a single splatted argument passes the value when the argument is an Array containing a single value
fails:The yield call taking a single splatted argument passes the values of the Array as individual arguments
fails:The yield call taking a single splatted argument passes the element of a single element Array
fails:The yield call taking a single splatted argument passes no values when give nil as an argument
fails:The yield call taking multiple arguments with a splat raises a LocalJumpError when the method is not passed a block
fails:The yield call taking multiple arguments with a splat passes the arguments to the block
fails:The yield call taking multiple arguments with a splat does not pass an argument value if the splatted argument is an empty Array
fails:The yield call taking multiple arguments with a splat passes the Array elements as arguments if the splatted argument is a non-empty Array
fails:The yield call taking multiple arguments with a splat does not pass an argument value if the splatted argument is nil
fails:The yield call taking matching arguments with splats and post args raises a LocalJumpError when the method is not passed a block
fails:The yield call taking matching arguments with splats and post args passes the arguments to the block
fails:The yield call taking a splat and a keyword argument passes it as an array of the values and a hash
7 changes: 4 additions & 3 deletions vm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,11 @@ def execute(iseq, **payload)
before = frame_stack.size

begin
push_frame(iseq, **payload)
pushed_frame = push_frame(iseq, **payload)
rescue Exception => e
pop_frame(reason: "propagating error during push_frame #{e}")
raise
puts e
puts "Errors inside push_frame are not allowed"
Kernel.exit(1)
end

pushed_frame = current_frame
Expand Down
37 changes: 31 additions & 6 deletions vm/frames/block_frame.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,28 @@ def initialize(parent_frame:, arg_values:, block:)

@block = block
@parent_frame = parent_frame

if arg_values.is_a?(Array) && arg_values.length == 1 && arg_values[0].is_a?(Array) && !iseq.args_info[:ambiguous_param0]
arg_values = arg_values[0]
end

@arg_values = arg_values
end

def prepare
values = arg_values
if expand_single_array_argument?
arg = @arg_values[0]

if arg.is_a?(Array)
values = arg
else
case (to_ary = arg.to_ary)
when nil
values = [arg]
when Array
values = to_ary
else
raise TypeError, "can't convert #{arg.class} to Array (#{arg.class}#to_ary gives #{to_ary.class})"
end
end
else
values = @arg_values
end

MethodArguments.new(
iseq: iseq,
Expand All @@ -44,4 +56,17 @@ def can_do_next?
def can_do_break?
true
end

private

def expand_single_array_argument?
return false if iseq.args_info[:ambiguous_param0]
return false if arg_values.length != 1
first_element = arg_values[0]
return false if !first_element.is_a?(Array) && !first_element.respond_to?(:to_ary)
lead_num = iseq.args_info[:lead_num] || 0
opt = iseq.args_info[:opt] || []
return false if lead_num == 0 && opt.empty?
true
end
end
36 changes: 8 additions & 28 deletions vm/helpers/method_arguments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ def extract(arity_check: false)
end

if kw.respond_to?(:to_hash)
kw = values[values.length - 1] = kw.to_hash
kw_to_hash = kw.to_hash
if kw_to_hash.is_a?(Hash)
kw = values[values.length - 1] = kw_to_hash
elsif !kw_to_hash.nil?
raise TypeError, "can't convert #{kw.class} to Hash (#{kw.class}#to_hash gives #{kw_to_hash.class})"
end
end

if kw.is_a?(Hash)
Expand All @@ -55,10 +60,6 @@ def extract(arity_check: false)
if kw.key?(kwarg.name)
value = kw.delete(kwarg.name)
__kw_initializers << [:kwreq, name, value]
# -> {
# locals.find(name: name).set(value)
# VM.instance.__log { "kwreq: #{name} = #{value.inspect}" }
# }
else
raise ArgumentError, "missing keyword #{name.inspect}"
end
Expand All @@ -69,17 +70,9 @@ def extract(arity_check: false)
value = kwarg.default
end
__kw_initializers << [:kwopt, name, value]
# -> {
# locals.find(name: name).set(value)
# VM.instance.__log { "kwopt: #{name} = #{value.inspect}" }
# }
when CategorizedArguments::DynamicKwOpt
if kw.is_a?(Hash) && kw.key?(name)
value = kw.delete(name)
# -> {
# locals.find(name: name).set(value)
# VM.instance.__log { "kwopt: #{name} = #{value.inspect}" }
# }
__kw_initializers << [:kwopt, name, value]
else
# complex default value,
Expand All @@ -102,8 +95,6 @@ def extract(arity_check: false)
kwrest_names.each do |name|
next if name.nil?
__kw_initializers << [:kwrest, name, value]
# locals.find(name: name).set(value)
# VM.instance.__log { "kwrest(#{name}): #{value.inspect}" }
end
end

Expand Down Expand Up @@ -131,7 +122,7 @@ def extract(arity_check: false)

# Rest positional argument
if (name = args.rest)
value = values.first(values.length - args.post.length)
value = values.first([values.length - args.post.length, 0].max)
@values = values.last(args.post.length)

locals.find(name: name).set(value)
Expand All @@ -140,7 +131,7 @@ def extract(arity_check: false)

# Required post positional arguments
args.post.each do |name|
if values.empty?
if arity_check && values.empty?
raise ArgumentError, 'Broken arguments, cannot extract required argument'
end

Expand All @@ -157,16 +148,5 @@ def extract(arity_check: false)
if arity_check && values.any?
raise ArgumentError, 'wrong number of arguments (too many)'
end

# if has_kw && kw.is_a?(Hash) && kw.any?
# extra_kw = kw.keys.grep(Symbol)
# if reusing_kw
# if extra_kw.any?
# raise ArgumentError, "unknown keywords: #{extra_kw.join(', ')}"
# end
# else
# binding.irb
# end
# end
end
end

0 comments on commit d9a60f2

Please sign in to comment.