Class: Preval::Visitors::Fasterer

Inherits:
Preval::Visitor show all
Defined in:
lib/preval/visitors/fasterer.rb

Overview

All of these optimizations come from the ‘fasterer` gem.

Instance Method Summary collapse

Methods inherited from Preval::Visitor

enable!

Instance Method Details

#on_call(node) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/preval/visitors/fasterer.rb', line 7

def on_call(node)
  left, _period, right = node.body

  # replace `.reverse.each` with `.reverse_each`
  # replace `.shuffle.first` with `.sample`
  # replace `.sort.first` with `min`
  # replace `.sort.last` with `max`
  if node.type_match?(:call, :@period, :@ident) &&
     # foo.each
     left.type_match?(%i[array vcall], :@period, :@ident)
     # foo.reverse

    callleft, callperiod, callright = left.body

    if callright.body == 'reverse' && right.body == 'each'
      callright.update(:@ident, 'reverse_each')
      node.update(:call, [callleft, callperiod, callright])
    elsif callright.body == 'sort' && right.body == 'first'
      callright.update(:@ident, 'min')
      node.update(:call, [callleft, callperiod, callright])
    elsif callright.body == 'sort' && right.body == 'last'
      callright.update(:@ident, 'max')
      node.update(:call, [callleft, callperiod, callright])
    elsif callright.body == 'shuffle' && right.body == 'first'
      callright.update(:@ident, 'sample')
      node.update(:call, [callleft, callperiod, callright])
    end
  end
end

#on_method_add_arg(node) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/preval/visitors/fasterer.rb', line 37

def on_method_add_arg(node)
  # replace `.gsub('...', '...')` with `.tr('...', '...')`
  if node.type_match?(:call, :arg_paren) &&
     # foo.gsub()
     node[0].type_match?(:vcall, :@period, :@ident) &&
     # foo.gsub
     node[0, 2].body == 'gsub'
     # the method being called is gsub

    left = node[1, 0, 0, 0, 1]
    right = node[1, 0, 0, 1]

    if left.is?(:string_literal) &&
       right.is?(:string_literal) &&
       [left, right].all? do |string|
         string[0, 1].is?(:@tstring_content) &&
         string[0, 1].body.length == 1
       end

      node[0, 2].update(:@ident, 'tr')
    end
  end

  # replace `.map { ... }.flatten(1)` with `.flat_map { ... }`
  if node.type_match?(:call, :arg_paren) &&
     # foo.flatten()
     node[0].type_match?(:method_add_block, :@period, :@ident) &&
     # foo.map {}
     node[0, 0, 0].type_match?(%i[array vcall], :@period, :@ident) &&
     # foo.flatten
     node[0, 0, 0, 2].body == 'map' &&
     # the inner call is a call to map
     node[0, 2].body == 'flatten' &&
     # the outer call is a call to flatten
     node[1].is?(:arg_paren) &&
     # flatten has a param
     node[1, 0, 0].type_match?(:args_new, :@int) &&
     # there is only one argument to flatten and it is an integer
     node[1, 0, 0, 1].body == '1'
     # the value of the argument to flatten is 1

    node[0, 0, 0, 2].update(:@ident, 'flat_map')
    node.update(:method_add_block, node[0, 0].body)
  end
end