diff --git a/tags/language/block_tags.txt b/tags/language/block_tags.txt index 0c61a79..8189908 100644 --- a/tags/language/block_tags.txt +++ b/tags/language/block_tags.txt @@ -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 diff --git a/tags/language/proc_tags.txt b/tags/language/proc_tags.txt index f9e4315..c0d517c 100644 --- a/tags/language/proc_tags.txt +++ b/tags/language/proc_tags.txt @@ -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 diff --git a/tags/language/yield_tags.txt b/tags/language/yield_tags.txt index 13903b3..f83e578 100644 --- a/tags/language/yield_tags.txt +++ b/tags/language/yield_tags.txt @@ -1,13 +1,15 @@ 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 @@ -15,3 +17,4 @@ fails:The yield call taking multiple arguments with a splat passes the Array ele 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 diff --git a/vm.rb b/vm.rb index 2966903..828efa7 100644 --- a/vm.rb +++ b/vm.rb @@ -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 diff --git a/vm/frames/block_frame.rb b/vm/frames/block_frame.rb index 27c5e71..42dc4db 100644 --- a/vm/frames/block_frame.rb +++ b/vm/frames/block_frame.rb @@ -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, @@ -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 diff --git a/vm/helpers/method_arguments.rb b/vm/helpers/method_arguments.rb index 2542ed3..3544744 100644 --- a/vm/helpers/method_arguments.rb +++ b/vm/helpers/method_arguments.rb @@ -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) @@ -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 @@ -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, @@ -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 @@ -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) @@ -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 @@ -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