lib/opal/nodes/call_special.rb



require 'opal/nodes/base'
require 'opal/nodes/call'

module Opal
  module Nodes
    # recv.mid = rhs
    # s(:recv, :mid=, s(:arglist, rhs))
    class AttrAssignNode < CallNode
      handle :attrasgn

      children :recvr, :meth, :arglist

      def default_compile
        # Skip, for now, if the method has square brackets: []=
        return super if meth.to_s !~ /^\w+=$/

        with_temp do |args_tmp|
          with_temp do |recv_tmp|
            args = expr(arglist)
            mid = mid_to_jsid meth.to_s
            push "((#{args_tmp} = [", args, "]), "+
                 "#{recv_tmp} = ", recv(recv_sexp), ", ",
                 recv_tmp, mid, ".apply(#{recv_tmp}, #{args_tmp}), "+
                 "#{args_tmp}[#{args_tmp}.length-1])"
          end
        end
      end
    end

    # lhs =~ rhs
    # s(:match3, lhs, rhs)
    class Match3Node < Base
      handle :match3

      children :lhs, :rhs

      def compile
        sexp = s(:call, lhs, :=~, s(:arglist, rhs))
        push process(sexp, @level)
      end
    end

    # a ||= rhs
    # s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, rhs))
    class OpAsgnOrNode < Base
      handle :op_asgn_or

      children :recvr, :rhs

      def compile
        sexp = s(:or, recvr, rhs)
        push expr(sexp)
      end
    end

    # a &&= rhs
    # s(:op_asgn_and, s(:lvar, :a), s(:lasgn, a:, rhs))
    class OpAsgnAndNode < Base
      handle :op_asgn_and

      children :recvr, :rhs

      def compile
        sexp = s(:and, recvr, rhs)
        push expr(sexp)
      end
    end

    # lhs[args] ||= rhs
    # s(:op_asgn1, lhs, args, :||, rhs)
    class OpAsgn1Node < Base
      handle :op_asgn1

      children :lhs, :args, :op, :rhs

      def first_arg
        args[1]
      end

      def compile
        case op.to_s
        when '||' then compile_or
        when '&&' then compile_and
        else compile_operator
        end
      end

      def compile_operator
        with_temp do |a| # args
          with_temp do |r| # recv
            cur = s(:call, s(:js_tmp, r), :[], s(:arglist, s(:js_tmp, a)))
            rhs = s(:call, cur, op.to_sym, s(:arglist, self.rhs))
            call = s(:call, s(:js_tmp, r), :[]=, s(:arglist, s(:js_tmp, a), rhs))

            push "(#{a} = ", expr(first_arg), ", #{r} = ", expr(lhs)
            push ", ", expr(call), ")"
          end
        end
      end

      def compile_or
        with_temp do |a| # args
          with_temp do |r| # recv
            aref = s(:call, s(:js_tmp, r), :[], s(:arglist, s(:js_tmp, a)))
            aset = s(:call, s(:js_tmp, r), :[]=, s(:arglist, s(:js_tmp, a), rhs))
            orop = s(:or, aref, aset)

            push "(#{a} = ", expr(first_arg), ", #{r} = ", expr(lhs)
            push ", ", expr(orop), ")"
          end
        end
      end

      def compile_and
        with_temp do |a| # args
          with_temp do |r| # recv
            aref = s(:call, s(:js_tmp, r), :[], s(:arglist, s(:js_tmp, a)))
            aset = s(:call, s(:js_tmp, r), :[]=, s(:arglist, s(:js_tmp, a), rhs))
            andop = s(:and, aref, aset)

            push "(#{a} = ", expr(first_arg), ", #{r} = ", expr(lhs)
            push ", ", expr(andop), ")"
          end
        end
      end
    end

    # lhs.b += rhs
    # s(:op_asgn2, lhs, :b=, :+, rhs)
    class OpAsgn2Node < Base
      handle :op_asgn2

      children :lhs, :mid, :op, :rhs

      def meth
        mid.to_s[0..-2]
      end

      def compile
        case op.to_s
        when '||' then compile_or
        when '&&' then compile_and
        else compile_operator
        end
      end

      def compile_or
        with_temp do |tmp|
          getr = s(:call, s(:js_tmp, tmp), meth, s(:arglist))
          asgn = s(:call, s(:js_tmp, tmp), mid, s(:arglist, rhs))
          orop = s(:or, getr, asgn)

          push "(#{tmp} = ", expr(lhs), ", ", expr(orop), ")"
        end
      end

      def compile_and
        with_temp do |tmp|
          getr = s(:call, s(:js_tmp, tmp), meth, s(:arglist))
          asgn = s(:call, s(:js_tmp, tmp), mid, s(:arglist, rhs))
          andop = s(:and, getr, asgn)

          push "(#{tmp} = ", expr(lhs), ", ", expr(andop), ")"
        end
      end

      def compile_operator
        with_temp do |tmp|
          getr = s(:call, s(:js_tmp, tmp), meth, s(:arglist))
          oper = s(:call, getr, op, s(:arglist, rhs))
          asgn = s(:call, s(:js_tmp, tmp), mid, s(:arglist, oper))

          push "(#{tmp} = ", expr(lhs), ", ", expr(asgn), ")"
        end
      end
    end
  end
end