module Ruby

  # A CallStatement is the abstraction of Send and Yield. The two are really
  # much more similar than different.
  #
  # A CallStatement has a name, receiver and arguments
  #
  # Using the "sol_brother" we can create the right Sol class for it.
  # Arguments in sol must be simple, so any complex expressions get
  # hoisted and assigned to temporary variables.
  #
  class CallStatement < Statement
    attr_reader :name , :receiver , :arguments

    def initialize(name , receiver , arguments )
      @name , @receiver , @arguments = name , receiver , arguments
      @arguments ||= []
    end

    # we "normalize" or flatten any complex argument expressions into a list
    def to_sol
      statements = Sol::Statements.new([])
      receiver = normalize_arg(@receiver , statements)
      arguments = []
      @arguments.each_with_index do |arg , index |
        arguments << normalize_arg(arg , statements)
      end
      if statements.empty?
        return sol_brother.new(@name, receiver , arguments)
      else
        statements << sol_brother.new(@name, receiver , arguments)
        return statements
      end
    end

    # this is called for each arg and if the arg is not constant or Variable
    # we create a tmp variable and assign to that, hoising all the calls.
    # the effect is of walking the call tree now,
    # rather than using a stack to do that at runtime
    def normalize_arg(arg , statements)
      sol_arg = arg.to_sol
      return sol_arg if sol_arg.is_a?(Sol::Expression)
      if( sol_arg.is_a?(Sol::Statements))
        while(sol_arg.length > 1)
          statements << sol_arg.shift
        end
        sol_arg = sol_arg.shift
      end
      assign = Sol::LocalAssignment.new( "tmp_#{arg.object_id}".to_sym, sol_arg)
      statements << assign
      return Sol::LocalVariable.new(assign.name)
    end

  end
end