class Opal::Rewriters::ForRewriter

Public Class Methods

next_tmp() click to toggle source
# File lib/opal/rewriters/for_rewriter.rb, line 12
def self.next_tmp
  @counter ||= 0
  @counter += 1
  :"$for_tmp#{@counter}"
end
reset_tmp_counter!() click to toggle source
# File lib/opal/rewriters/for_rewriter.rb, line 8
def self.reset_tmp_counter!
  @counter = 0
end

Public Instance Methods

on_for(node) click to toggle source

Handles

for i in 0..3; j = i + 1; end

The problem here is that in Ruby for loop makes its loop variable + all local variables available outside. I.e. after this loop variable ‘i` is 3 and `j` is 4

This class rewrites it to the following code:

j = nil
i = nil
(0..3).each { |__jstmp| i = __jstmp; j = i + 1 }

Complex stuff with multiple loop variables:

for i, j in [[1, 2], [3, 4]]; end

Becomes multiple left-hand assignment:

i = nil
j = nil
[[1, 2], [3, 4]].each { |__jstmp| i, j = __jstmp }
# File lib/opal/rewriters/for_rewriter.rb, line 38
def on_for(node)
  loop_variable, loop_range, loop_body = *node

  # Declare local variables used in the loop and the loop body at the outer scope
  outer_assignments = generate_outer_assignments(loop_variable, loop_body)

  # Generate temporary loop variable
  tmp_loop_variable = self.class.next_tmp
  get_tmp_loop_variable = s(:js_tmp, tmp_loop_variable)

  # Assign the loop variables in the loop body
  loop_body = prepend_to_body(loop_body, assign_loop_variable(loop_variable, get_tmp_loop_variable))

  # Transform the for-loop into each-loop with updated loop body
  node = transform_for_to_each_loop(node, loop_range, tmp_loop_variable, loop_body)

  node.updated(:begin, [*outer_assignments, node])
end

Private Instance Methods

assign_loop_variable(loop_variable, tmp_loop_variable) click to toggle source
# File lib/opal/rewriters/for_rewriter.rb, line 66
def assign_loop_variable(loop_variable, tmp_loop_variable)
  case loop_variable.type
  when :mlhs # multiple left-hand statement like in "for i,j in [[1, 2], [3, 4]]"
    loop_variable.updated(:masgn, [loop_variable, tmp_loop_variable])
  else # single argument like "for i in (0..3)"
    loop_variable << tmp_loop_variable
  end
end
generate_outer_assignments(loop_variable, loop_body) click to toggle source
# File lib/opal/rewriters/for_rewriter.rb, line 59
def generate_outer_assignments(loop_variable, loop_body)
  loop_local_vars = LocalVariableAssigns.find(loop_variable)
  body_local_vars = LocalVariableAssigns.find(loop_body)

  (loop_local_vars + body_local_vars).map { |lvar_name| s(:lvdeclare, lvar_name) }
end
transform_for_to_each_loop(node, loop_range, tmp_loop_variable, loop_body) click to toggle source

rubocop:disable Layout/MultilineMethodCallBraceLayout,Layout/MultilineArrayBraceLayout

# File lib/opal/rewriters/for_rewriter.rb, line 76
def transform_for_to_each_loop(node, loop_range, tmp_loop_variable, loop_body)
  node.updated(:send, [loop_range, :each,                                         # (0..3).each {
                       node.updated(:iter, [s(:args, s(:arg, tmp_loop_variable)), # |__jstmp|
                                            process(loop_body)                    # i = __jstmp; j = i + 1 }
                                           ])])
end