class CodeTools::AST::MultipleAssignment

Attributes

block[RW]
left[RW]
post[RW]
right[RW]
splat[RW]

Public Class Methods

new(line, left, right, splat) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 543
def initialize(line, left, right, splat)
  @line = line
  @left = left
  @right = right
  @splat = nil
  @block = nil # support for |&b|
  @post = nil # in `a,*b,c`, c is in post.

  if splat.kind_of?(PostArg)
    @fixed = false
    @post = splat.rest
    splat = splat.into
  elsif right.kind_of?(ArrayLiteral)
    @fixed = right.body.size > 1
  else
    @fixed = false
  end

  if splat.kind_of? Node
    if @left
      if right
        @splat = SplatAssignment.new line, splat
      else
        @splat = SplatWrapped.new line, splat
      end
    elsif @fixed
      @splat = SplatArray.new line, splat, right.body.size
    elsif right.kind_of? SplatValue
      @splat = splat
    else
      @splat = SplatWrapped.new line, splat
    end
  elsif splat
    # We need a node for eg { |*| } and { |a, *| }
    size = @fixed ? right.body.size : 0
    @splat = EmptySplat.new line, size
  end
end

Public Instance Methods

assign_values(g, array, index) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 799
def assign_values(g, array, index)
  array.body.each do |x|
    get_element(g, index)
    g.dup if x.kind_of? MultipleAssignment
    x.bytecode(g)
    g.pop
  end
end
bytecode(g, array_on_stack=false) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 609
def bytecode(g, array_on_stack=false)
  declare_local_scope(g.state.scope)

  case @right
  when ArrayLiteral, SplatValue
    @right.bytecode(g)
    g.dup
  when ToArray
    @right.value.bytecode(g)
    g.dup
    convert_to_ary(g)
  when nil
    convert_to_ary(g)
  else
    @right.bytecode(g)
    g.dup
    convert_to_ary(g)
  end

  size = g.new_stack_local
  g.dup
  g.send :size, 0, true
  g.set_stack_local size
  g.pop

  index = g.new_stack_local
  g.push_int 0
  g.set_stack_local index
  g.pop

  g.state.push_masgn

  assign_values g, @left, index if @left

  if @splat
    g.dup
    g.push_stack_local index

    check_count = g.new_label

    if @post
      g.push_stack_local size
      g.push_int @post.body.size
      g.send :-, 1, true

      g.push_stack_local index
      g.send :-, 1, true

      g.goto check_count
    else
      g.push_stack_local size
      g.push_stack_local index
      g.send :-, 1, true

      g.goto check_count
    end

    underflow = g.new_label
    assign_splat = g.new_label

    underflow.set!
    g.pop_many 3
    g.make_array 0

    g.goto assign_splat

    check_count.set!
    g.dup
    g.push_int 0
    g.send :<, 1, true
    g.goto_if_true underflow

    g.dup
    g.push_stack_local index
    g.send :+, 1, true
    g.set_stack_local index
    g.pop

    g.send :[], 2, true

    assign_splat.set!

    # TODO: Fix nodes to work correctly.
    case @splat
    when EmptySplat
      # nothing
    when SplatArray, SplatWrapped
      @splat.value.bytecode(g)
    else
      @splat.bytecode(g)
    end
    g.pop
  end

  assign_values g, @post, index if @post

  g.state.pop_masgn
  g.pop
end
convert_to_ary(g) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 709
def convert_to_ary(g)
  done = g.new_label
  coerce = g.new_label
  make_array = g.new_label
  dup_as_array = g.new_label

  instance_of_array(g, done)
  kind_of_array(g, dup_as_array)

  g.dup
  g.push_literal :to_ary
  g.push_true
  g.send :respond_to?, 2, true
  g.goto_if_true coerce

  make_array.set!
  g.make_array 1
  g.goto done

  discard = g.new_label

  dup_as_array.set!
  g.dup
  g.push_rubinius
  g.find_const :Runtime
  g.swap
  g.send :dup_as_array, 1, true
  g.goto discard

  coerce.set!
  g.dup
  g.send :to_ary, 0, true

  check_array = g.new_label

  g.dup
  g.goto_if_not_nil check_array

  g.pop
  g.goto make_array

  check_array.set!
  kind_of_array(g, discard)

  g.push_type
  g.move_down 2
  g.push_literal :to_ary
  g.push_cpath_top
  g.find_const :Array
  g.send :coerce_to_type_error, 4, true
  g.goto done

  discard.set!
  g.swap
  g.pop

  done.set!
end
declare_local_scope(scope) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 586
def declare_local_scope(scope)
  # Fix the scope for locals introduced by the left. We
  # do this before running the code for the right so that
  # right side sees the proper scoping of the locals on the left.

  if @left
    @left.body.each do |var|
      case var
      when LocalVariable
        scope.assign_local_reference var
      when MultipleAssignment
        var.declare_local_scope(scope)
      end
    end
  end

  if @splat and @splat.kind_of?(SplatAssignment)
    if @splat.value.kind_of?(LocalVariable)
      scope.assign_local_reference @splat.value
    end
  end
end
defined(g) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 808
def defined(g)
  g.push_literal "assignment"
end
get_element(g, index) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 786
def get_element(g, index)
  g.dup
  g.push_stack_local index

  g.dup
  g.push_int 1
  g.send :+, 1, true
  g.set_stack_local index
  g.pop

  g.send :[], 1, true
end
instance_of_array(g, label) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 768
def instance_of_array(g, label)
  g.dup
  g.push_cpath_top
  g.find_const :Array
  g.swap
  g.instance_of
  g.goto_if_true label
end
iter_arguments() click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 582
def iter_arguments
  @iter_arguments = true
end
kind_of_array(g, label) click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 777
def kind_of_array(g, label)
  g.dup
  g.push_cpath_top
  g.find_const :Array
  g.swap
  g.kind_of
  g.goto_if_true label
end
to_sexp() click to toggle source
# File lib/rubinius/code/ast/variables.rb, line 812
def to_sexp
  left = @left ? @left.to_sexp : [:array]
  case @splat
  when EmptySplat
    left << [:splat]
  when nil
  else
    left << [:splat, @splat.to_sexp]
  end
  left << @block.to_sexp if @block

  sexp = [:masgn, left]
  sexp += @post.body.map { |x| x.to_sexp } if @post
  sexp << @right.to_sexp if @right
  sexp
end