logop (RubyParser)

    # File lib/ruby_parser_extras.rb, line 404
      def logop(type, left, right) # TODO: rename logical_op
        left = value_expr left

        if left and left[0] == type and not left.paren then
          node, second = left, nil

          while (second = node[2]) && second[0] == type and not second.paren do
            node = second
          end

          node[2] = s(type, second, right)

          return left
        end

        return s(type, left, right)
      end

      def new_aref val
        val[2] ||= s(:arglist)
        val[2][0] = :arglist if val[2][0] == :array # REFACTOR
        if val[0].node_type == :self then
          result = new_call nil, "[]""[]", val[2]
        else
          result = new_call val[0], "[]""[]", val[2]
        end
        result
      end

      def new_body val
        result = val[0]

        if val[1] then
          result = s(:rescue)
          result << val[0] if val[0]

          resbody = val[1]

          while resbody do
            result << resbody
            resbody = resbody.resbody(true)
          end

          result << val[2] if val[2]

          result.line = (val[0] || val[1]).line
        elsif not val[2].nil? then
          warning("else without rescue is useless")
          result = block_append(result, val[2])
        end

        result = s(:ensure, result, val[3]).compact if val[3]
        return result
      end

      def new_call recv, meth, args = nil
        result = s(:call, recv, meth)
        result.line = recv.line if recv

        args ||= s(:arglist)
        args[0] = :arglist if args.first == :array
        args = s(:arglist, args) unless args.first == :arglist
        result << args
        result
      end

      def new_case expr, body
        result = s(:case, expr)
        line = (expr || body).line

        while body and body.node_type == :when
          result << body
          body = body.delete_at 3
        end

        # else
        body = nil if body == s(:block)
        result << body

        result.line = line
        result
      end

      def new_class val
        line, path, superclass, body = val[1], val[2], val[3], val[5]
        scope = s(:scope, body).compact
        result = s(:class, path, superclass, scope)
        result.line = line
        result.comments = self.comments.pop
        result
      end

      def new_compstmt val
        result = void_stmts(val[0])
        result = remove_begin(result) if result
        result
      end

      def new_defn val
        (line, bol), name, args, body = val[2], val[1], val[3], val[4]
        body ||= s(:nil)

        body ||= s(:block)
        body = s(:block, body) unless body.first == :block

        result = s(:defn, name.to_sym, args, s(:scope, body))
        result.line = line
        result.line -= 1 if bol
        result.comments = self.comments.pop
        result
      end

      def new_defs val
        recv, name, args, body = val[1], val[4], val[6], val[7]

        body ||= s(:block)
        body = s(:block, body) unless body.first == :block

        result = s(:defs, recv, name.to_sym, args, s(:scope, body))
        result.line = recv.line
        result.comments = self.comments.pop
        result
      end

      def new_for expr, var, body
        result = s(:for, expr, var).line(var.line)
        result << body if body
        result
      end

      def new_if c, t, f
        l = [c.line, t && t.line, f && f.line].compact.min
        c = cond c
        c, t, f = c.last, f, t if c[0] == :not
        s(:if, c, t, f).line(l)
      end

      def new_iter call, args, body
        result = s(:iter)
        result << call if call
        result << args
        result << body if body
        result
      end

      def new_masgn lhs, rhs, wrap = false
        rhs = value_expr(rhs)
        rhs = lhs[1] ? s(:to_ary, rhs) : s(:array, rhs) if wrap

        lhs.delete_at 1 if lhs[1].nil?
        lhs << rhs

        lhs
      end

      def new_module val
        line, path, body = val[1], val[2], val[4]
        body = s(:scope, body).compact
        result = s(:module, path, body)
        result.line = line
        result.comments = self.comments.pop
        result
      end

      def new_op_asgn val
        lhs, asgn_op, arg = val[0], val[1].to_sym, val[2]
        name = lhs.value
        arg = remove_begin(arg)
        result = case asgn_op # REFACTOR
                 when "||""||" then
                   lhs << arg
                   s(:op_asgn_or, self.gettable(name), lhs)
                 when "&&""&&" then
                   lhs << arg
                   s(:op_asgn_and, self.gettable(name), lhs)
                 else
                   # TODO: why [2] ?
                   lhs[2] = new_call(self.gettable(name), asgn_op,
                                     s(:arglist, arg))
                   lhs
                 end
        result.line = lhs.line
        result
      end

      def new_regexp val
        node = val[1] || s(:str, '')
        options = val[2]

        o, k = 0, nil
        options.split(//).uniq.each do |c| # FIX: this has a better home
          v = {
            'x' => Regexp::EXTENDED,
            'i' => Regexp::IGNORECASE,
            'm' => Regexp::MULTILINE,
            'o' => Regexp::ONCE,
            'n' => Regexp::ENC_NONE,
            'e' => Regexp::ENC_EUC,
            's' => Regexp::ENC_SJIS,
            'u' => Regexp::ENC_UTF8,
          }[c]
          raise "unknown regexp option: #{c}" unless v
          o += v
          k = c if c =~ /[esu]/
        end

        case node[0]
        when :str then
          node[0] = :lit
          node[1] = if k then
                      Regexp.new(node[1], o, k)
                    else
                      Regexp.new(node[1], o)
                    end
        when :dstr then
          if options =~ /o/ then
            node[0] = :dregx_once
          else
            node[0] = :dregx
          end
          node << o if o and o != 0
        else
          node = s(:dregx, '', node);
          node[0] = :dregx_once if options =~ /o/
          node << o if o and o != 0
        end

        node
      end

      def new_sclass val
        recv, in_def, in_single, body = val[3], val[4], val[6], val[7]
        scope = s(:scope, body).compact
        result = s(:sclass, recv, scope)
        result.line = val[2]
        self.in_def = in_def
        self.in_single = in_single
        result
      end

      def new_super args
        if args && args.node_type == :block_pass then
          s(:super, args)
        else
          args ||= s(:arglist)
          s(:super, *args[1..-1])
        end
      end

      def new_undef n, m = nil
        if m then
          block_append(n, s(:undef, m))
        else
          s(:undef, n)
        end
      end

      def new_until block, expr, pre
        expr = (expr.first == :not ? expr.last : s(:not, expr)).line(expr.line)
        new_while block, expr, pre
      end

      def new_while block, expr, pre
        line = [block && block.line, expr.line].compact.min
        block, pre = block.last, false if block && block[0] == :begin

        expr = cond expr
        result = if expr.first == :not then
                   s(:until, expr.last, block, pre)
                 else
                   s(:while, expr, block, pre)
                 end

        result.line = line
        result
      end

      def new_xstring str
        if str then
          case str[0]
          when :str
            str[0] = :xstr
          when :dstr
            str[0] = :dxstr
          else
            str = s(:dxstr, '', str)
          end
          str
        else
          s(:xstr, '')
        end
      end

      def new_yield args = nil
        # TODO: raise args.inspect unless [:arglist].include? args.first # HACK
        raise SyntaxError, "Block argument should not be given." if
          args && args.node_type == :block_pass

        args ||= s(:arglist)

        # TODO: I can prolly clean this up
        args[0] = :arglist       if args.first == :array
        args = s(:arglist, args) unless args.first == :arglist

        return s(:yield, *args[1..-1])
      end

      def next_token
        if self.lexer.advance then
          return self.lexer.token, self.lexer.yacc_value
        else
          return [false, '$end']
        end
      end

      def node_assign(lhs, rhs) # TODO: rename new_assign
        return nil unless lhs

        rhs = value_expr rhs

        case lhs[0]
        when :gasgn, :iasgn, :lasgn, :dasgn, :dasgn_curr,
          :masgn, :cdecl, :cvdecl, :cvasgn then
          lhs << rhs
        when :attrasgn, :call then
          args = lhs.pop unless Symbol === lhs.last
          lhs << arg_add(args, rhs)
        when :const then
          lhs[0] = :cdecl
          lhs << rhs
        else
          raise "unknown lhs #{lhs.inspect}"
        end

        lhs
      end

      def process(str, file = "(string)")
        raise "bad val: #{str.inspect}" unless String === str

        self.file = file
        self.lexer.src = str

        @yydebug = ENV.has_key? 'DEBUG'

        do_parse
      end
      alias :parse :process

      def remove_begin node
        oldnode = node
        if node and :begin == node[0] and node.size == 2 then
          node = node[-1]
          node.line = oldnode.line
        end
        node
      end

      def reset
        lexer.reset
        self.in_def = false
        self.in_single = 0
        self.env.reset
        self.comments.clear
      end

      def ret_args node
        if node then
          raise SyntaxError, "block argument should not be given" if
            node[0] == :block_pass

          node = node.last if node[0] == :array && node.size == 2
          # HACK matz wraps ONE of the FOUR splats in a newline to
          # distinguish. I use paren for now. ugh
          node = s(:svalue, node) if node[0] == :splat and not node.paren
          node[0] = :svalue if node[0] == :arglist && node[1][0] == :splat
        end

        node
      end

      def s(*args)
        result = Sexp.new(*args)
        result.line ||= lexer.lineno if lexer.src          # otherwise...
        result.file = self.file
        result
      end

      def value_expr oldnode # HACK
        node = remove_begin oldnode
        node.line = oldnode.line if oldnode
        node[2] = value_expr(node[2]) if node and node[0] == :if
        node
      end

      def void_stmts node
        return nil unless node
        return node unless node[0] == :block

        node[1..-1] = node[1..-1].map { |n| remove_begin(n) }
        node
      end

      def warning s
        # do nothing for now
      end

      alias :old_yyerror :yyerror
      def yyerror msg
        # for now do nothing with the msg
        old_yyerror
      end
    end