preval 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/Gemfile.lock +1 -1
- data/README.md +8 -1
- data/bin/console +1 -3
- data/bin/print +1 -3
- data/bin/start +3 -0
- data/docs/index.html +5 -3
- data/docs/public/favicon.png +0 -0
- data/docs/server.rb +1 -3
- data/lib/preval.rb +12 -2
- data/lib/preval/node.rb +2 -2
- data/lib/preval/version.rb +1 -1
- data/lib/preval/visitor.rb +0 -9
- data/lib/preval/visitors/arithmetic.rb +10 -10
- data/lib/preval/visitors/attr_accessor.rb +51 -0
- data/lib/preval/visitors/fasterer.rb +76 -0
- data/lib/preval/visitors/loops.rb +57 -19
- metadata +5 -2
- data/lib/preval/visitors/micro.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e8e8b1cc9579dafac71315c8138e9a07ece0a40e8c0e0ccdb3f9345574c4446
|
4
|
+
data.tar.gz: 5bc5ccf467619084b2243f082466881e1f491ecb844cd622ff612b9c7242ae42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aebce20a3d9332bf8e8eb20ea9f71789531b1462398713a22f9fb027c860aaee0d060bb94ddc6f400ceb1a6a94e0619638c54bedadcfa6eaea4a5cf3254cbb32
|
7
|
+
data.tar.gz: 515570a0d77a859529530b9113a8309b974c9bf09c7117afc2eee605406725155d3f35995bcac674cd0d3577e4da00891faa189925a573dde08fdd3b2fc9cda6
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
|
+
|
9
|
+
## [0.4.0] - 2019-04-19
|
10
|
+
### Added
|
11
|
+
- Replace `def foo=(value); @foo = value; end` with `attr_writer :foo`
|
12
|
+
- Replace `while false ... end` loops with nothing
|
13
|
+
- Replace `until false ... end` loops with `loop do ... end` loops
|
14
|
+
- Replace `until true ... end` loops with nothing
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
- Extracted out the `Preval::Visitors::AttrAccessor` visitor.
|
18
|
+
- Renamed the `Preval::Visitors::Micro` visitor to `Preval::Visitors::Fasterer`.
|
19
|
+
|
20
|
+
## [0.3.0] - 2019-04-19
|
8
21
|
### Added
|
9
22
|
- Fold constant for exponentiation if exponent is 0 and value is an integer.
|
10
23
|
- Replace `.reverse.each` usage with `.reverse_each`.
|
@@ -13,6 +26,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
13
26
|
- Replace `def foo; @foo; end` with `attr_reader :foo`.
|
14
27
|
- Replace `.shuffle.first` with `.sample`.
|
15
28
|
- Replace `.map { ... }.flatten(1)` with `.flat_map { ... }`.
|
29
|
+
- Replace `def foo=(value); @foo = value; end` with `attr_writer :foo`.
|
16
30
|
|
17
31
|
## [0.2.0] - 2019-04-18
|
18
32
|
### Added
|
@@ -22,6 +36,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
22
36
|
### Added
|
23
37
|
- Initial release. 🎉
|
24
38
|
|
25
|
-
[Unreleased]: https://github.com/kddeisz/preval/compare/v0.
|
39
|
+
[Unreleased]: https://github.com/kddeisz/preval/compare/v0.4.0...HEAD
|
40
|
+
[0.4.0]: https://github.com/kddeisz/preval/compare/v0.3.0...v0.4.0
|
41
|
+
[0.3.0]: https://github.com/kddeisz/preval/compare/v0.2.0...v0.3.0
|
26
42
|
[0.2.0]: https://github.com/kddeisz/preval/compare/v0.1.0...v0.2.0
|
27
43
|
[0.1.0]: https://github.com/kddeisz/preval/compare/49c899...v0.1.0
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -31,8 +31,10 @@ Each optimization is generally named for the function it performs, and can be en
|
|
31
31
|
* `Preval::Visitors::Arithmetic` replaces:
|
32
32
|
* constant expressions with their evaluation (e.g., `5 + 2` becomes `7`)
|
33
33
|
* arithmetic identities with their evaluation (e.g., `a * 1` becomes `a`)
|
34
|
-
* `Preval::Visitors::
|
34
|
+
* `Preval::Visitors::AttrAccessor` replaces:
|
35
35
|
* `def foo; @foo; end` with `attr_reader :foo`
|
36
|
+
* `def foo=(value); @foo = value; end` with `attr_writer :foo`
|
37
|
+
* `Preval::Visitors::Fasterer` replaces:
|
36
38
|
* `.gsub('...', '...')` with `.tr('...', '...')` if the arguments are strings and are both of length 1
|
37
39
|
* `.map { ... }.flatten(1)` with `.flat_map { ... }`
|
38
40
|
* `.reverse.each` with `.reverse_each`
|
@@ -40,6 +42,11 @@ Each optimization is generally named for the function it performs, and can be en
|
|
40
42
|
* `Preval::Visitors::Loops` replaces:
|
41
43
|
* `for ... in ... end` loops with `... each do ... end` loops
|
42
44
|
* `while true ... end` loops with `loop do ... end` loops
|
45
|
+
* `while false ... end` loops with nothing
|
46
|
+
* `until false ... end` loops with `loop do ... end` loops
|
47
|
+
* `until true ... end` loops with nothing
|
48
|
+
|
49
|
+
You can also call `Preval.enable_all!` which will enable every built-in visitor. Be especially careful when doing this.
|
43
50
|
|
44
51
|
## Development
|
45
52
|
|
data/bin/console
CHANGED
data/bin/print
CHANGED
@@ -3,9 +3,7 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'preval'
|
5
5
|
|
6
|
-
Preval
|
7
|
-
Preval::Visitors::Loops.enable!
|
8
|
-
Preval::Visitors::Micro.enable!
|
6
|
+
Preval.enable_all!
|
9
7
|
|
10
8
|
source = ARGV.first
|
11
9
|
puts Preval.process(File.exist?(source) ? File.read(source) : source)
|
data/bin/start
ADDED
data/docs/index.html
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
<meta charset="utf-8"
|
5
|
-
<meta http-equiv="X-UA-Compatible" content="IE=edge"
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1"
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7
7
|
|
8
8
|
<title>preval</title>
|
9
|
+
<link href="/favicon.png" rel="icon" type="image/png" />
|
10
|
+
|
9
11
|
<style>
|
10
12
|
html, body, main {
|
11
13
|
font-family: "Courier New";
|
Binary file
|
data/docs/server.rb
CHANGED
@@ -4,9 +4,7 @@ require 'bundler/setup'
|
|
4
4
|
require 'preval'
|
5
5
|
require 'sinatra'
|
6
6
|
|
7
|
-
Preval
|
8
|
-
Preval::Visitors::Loops.enable!
|
9
|
-
Preval::Visitors::Micro.enable!
|
7
|
+
Preval.enable_all!
|
10
8
|
|
11
9
|
get '/' do
|
12
10
|
send_file(File.expand_path('index.html', __dir__))
|
data/lib/preval.rb
CHANGED
@@ -8,8 +8,17 @@ module Preval
|
|
8
8
|
class << self
|
9
9
|
attr_reader :visitors
|
10
10
|
|
11
|
+
def enable_all!
|
12
|
+
Visitors::Arithmetic.enable!
|
13
|
+
Visitors::AttrAccessor.enable!
|
14
|
+
Visitors::Fasterer.enable!
|
15
|
+
Visitors::Loops.enable!
|
16
|
+
end
|
17
|
+
|
11
18
|
def process(source)
|
12
|
-
visitors.inject(source)
|
19
|
+
visitors.inject(Parser.parse(source)) do |current, visitor|
|
20
|
+
current.tap { |ast| ast.visit(visitor) }
|
21
|
+
end.to_source
|
13
22
|
end
|
14
23
|
end
|
15
24
|
|
@@ -23,8 +32,9 @@ require 'preval/version'
|
|
23
32
|
require 'preval/visitor'
|
24
33
|
|
25
34
|
require 'preval/visitors/arithmetic'
|
35
|
+
require 'preval/visitors/attr_accessor'
|
36
|
+
require 'preval/visitors/fasterer'
|
26
37
|
require 'preval/visitors/loops'
|
27
|
-
require 'preval/visitors/micro'
|
28
38
|
|
29
39
|
if defined?(Bootsnap)
|
30
40
|
load_iseq = RubyVM::InstructionSequence.method(:load_iseq)
|
data/lib/preval/node.rb
CHANGED
@@ -31,11 +31,11 @@ module Preval
|
|
31
31
|
@literal = literal
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
34
|
+
def [](index, *args)
|
35
35
|
node = body[index]
|
36
36
|
return nil unless node
|
37
37
|
|
38
|
-
args.any? ? node
|
38
|
+
args.any? ? node[*args] : node
|
39
39
|
end
|
40
40
|
|
41
41
|
def join(delim = '')
|
data/lib/preval/version.rb
CHANGED
data/lib/preval/visitor.rb
CHANGED
@@ -2,15 +2,6 @@
|
|
2
2
|
|
3
3
|
module Preval
|
4
4
|
class Visitor
|
5
|
-
def process(source)
|
6
|
-
sexp = Parser.parse(source)
|
7
|
-
sexp.tap { |node| node.visit(self) }.to_source if sexp
|
8
|
-
end
|
9
|
-
|
10
|
-
def process!(source)
|
11
|
-
process(source).tap { |response| raise SyntaxError unless response }
|
12
|
-
end
|
13
|
-
|
14
5
|
def self.enable!
|
15
6
|
Preval.visitors << new
|
16
7
|
end
|
@@ -3,19 +3,19 @@
|
|
3
3
|
module Preval
|
4
4
|
class Visitors
|
5
5
|
class Arithmetic < Visitor
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
using(
|
7
|
+
Module.new do
|
8
|
+
refine Node do
|
9
|
+
def int?(value)
|
10
|
+
is?(:@int) && to_int == value
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
def to_int
|
14
|
+
body[0].to_i
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
16
|
-
|
17
|
-
|
18
|
-
using IntNode
|
18
|
+
)
|
19
19
|
|
20
20
|
OPERATORS = %i[+ - * / % **].freeze
|
21
21
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Preval
|
4
|
+
class Visitors
|
5
|
+
class AttrAccessor < Visitor
|
6
|
+
def on_def(node)
|
7
|
+
# auto create attr_readers
|
8
|
+
if node.type_match?(:@ident, :params, :bodystmt) &&
|
9
|
+
# def foo; @foo; end
|
10
|
+
node[1].body.none? &&
|
11
|
+
# there are no params to this method
|
12
|
+
node[2, 0].type_match?(:stmts_new, :var_ref) &&
|
13
|
+
# there is only one statement in the body and its a var reference
|
14
|
+
node[2, 0, 1, 0].is?(:@ivar) &&
|
15
|
+
# the var reference is referencing an instance variable
|
16
|
+
node[0].body == node[2, 0, 1, 0].body[1..-1]
|
17
|
+
# the name of the variable matches the name of the method
|
18
|
+
|
19
|
+
ast = Parser.parse("attr_reader :#{node[0].body}")
|
20
|
+
node.update(:stmts_add, ast.body[0].body)
|
21
|
+
end
|
22
|
+
|
23
|
+
# auto create attr_writers
|
24
|
+
if node.type_match?(:@ident, :paren, :bodystmt) &&
|
25
|
+
# def foo=(value); @foo = value; end
|
26
|
+
node[0].body.end_with?('=') &&
|
27
|
+
# this is a setter method
|
28
|
+
node[1, 0, 0].length == 1 &&
|
29
|
+
# there is exactly one required param
|
30
|
+
node[1, 0].body[1..-1].none? &&
|
31
|
+
# there are no other params
|
32
|
+
node[2, 0, 0, 0]&.is?(:stmts_new) &&
|
33
|
+
# there is only one statement in the body
|
34
|
+
node[2, 0, 1].is?(:assign) &&
|
35
|
+
# the only statement is an assignment
|
36
|
+
node[2, 0, 1].type_match?(:var_field, :var_ref) &&
|
37
|
+
# assigning a variable
|
38
|
+
node[2, 0, 1, 0, 0].is?(:@ivar) &&
|
39
|
+
# assigning to an instance variable
|
40
|
+
node[0].body[0..-2] == node[2, 0, 1, 0, 0].body[1..-1] &&
|
41
|
+
# variable name matches the method name
|
42
|
+
node[1, 0, 0][0].body == node[2, 0, 1, 1, 0].body
|
43
|
+
# assignment variable matches the argument name
|
44
|
+
|
45
|
+
ast = Parser.parse("attr_writer :#{node[0].body[0..-2]}")
|
46
|
+
node.update(:stmts_add, ast.body[0].body)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Preval
|
4
|
+
class Visitors
|
5
|
+
# All of these optimizations come from the `fasterer` gem.
|
6
|
+
class Fasterer < Visitor
|
7
|
+
def on_call(node)
|
8
|
+
left, _period, right = node.body
|
9
|
+
|
10
|
+
# replace `.reverse.each` with `.reverse_each`
|
11
|
+
# replace `.shuffle.first` with `.sample`
|
12
|
+
if node.type_match?(:call, :@period, :@ident) &&
|
13
|
+
# foo.each
|
14
|
+
left.type_match?(%i[array vcall], :@period, :@ident)
|
15
|
+
# foo.reverse
|
16
|
+
|
17
|
+
callleft, callperiod, callright = left.body
|
18
|
+
|
19
|
+
if callright.body == 'reverse' && right.body == 'each'
|
20
|
+
callright.update(:@ident, 'reverse_each')
|
21
|
+
node.update(:call, [callleft, callperiod, callright])
|
22
|
+
elsif callright.body == 'shuffle' && right.body == 'first'
|
23
|
+
callright.update(:@ident, 'sample')
|
24
|
+
node.update(:call, [callleft, callperiod, callright])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_method_add_arg(node)
|
30
|
+
# replace `.gsub('...', '...')` with `.tr('...', '...')`
|
31
|
+
if node.type_match?(:call, :arg_paren) &&
|
32
|
+
# foo.gsub()
|
33
|
+
node[0].type_match?(:vcall, :@period, :@ident) &&
|
34
|
+
# foo.gsub
|
35
|
+
node[0, 2].body == 'gsub'
|
36
|
+
# the method being called is gsub
|
37
|
+
|
38
|
+
left = node[1, 0, 0, 0, 1]
|
39
|
+
right = node[1, 0, 0, 1]
|
40
|
+
|
41
|
+
if left.is?(:string_literal) &&
|
42
|
+
right.is?(:string_literal) &&
|
43
|
+
[left, right].all? do |node|
|
44
|
+
node[0, 1].is?(:@tstring_content) &&
|
45
|
+
node[0, 1].body.length == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
node[0, 2].update(:@ident, 'tr')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# replace `.map { ... }.flatten(1)` with `.flat_map { ... }`
|
53
|
+
if node.type_match?(:call, :arg_paren) &&
|
54
|
+
# foo.flatten()
|
55
|
+
node[0].type_match?(:method_add_block, :@period, :@ident) &&
|
56
|
+
# foo.map {}
|
57
|
+
node[0, 0, 0].type_match?(%i[array vcall], :@period, :@ident) &&
|
58
|
+
# foo.flatten
|
59
|
+
node[0, 0, 0, 2].body == 'map' &&
|
60
|
+
# the inner call is a call to map
|
61
|
+
node[0, 2].body == 'flatten' &&
|
62
|
+
# the outer call is a call to flatten
|
63
|
+
node[1].is?(:arg_paren) &&
|
64
|
+
# flatten has a param
|
65
|
+
node[1, 0, 0].type_match?(:args_new, :@int) &&
|
66
|
+
# there is only one argument to flatten and it is an integer
|
67
|
+
node[1, 0, 0, 1].body == '1'
|
68
|
+
# the value of the argument to flatten is 1
|
69
|
+
|
70
|
+
node[0, 0, 0, 2].update(:@ident, 'flat_map')
|
71
|
+
node.update(:method_add_block, node[0, 0].body)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -3,36 +3,74 @@
|
|
3
3
|
module Preval
|
4
4
|
class Visitors
|
5
5
|
class Loops < Visitor
|
6
|
-
module TrueNode
|
7
|
-
refine Node do
|
8
|
-
def true?
|
9
|
-
is?(:var_ref) && starts_with?(:@kw) && body[0].body == 'true'
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
using TrueNode
|
15
|
-
|
16
6
|
def on_for(node)
|
17
|
-
|
7
|
+
ast = Parser.parse(<<~CODE)
|
18
8
|
#{node.source(1)}.each do |#{node.source(0)}|
|
19
9
|
#{node.source(2)}
|
20
10
|
end
|
21
11
|
CODE
|
22
12
|
|
23
|
-
node.update(:stmts_add,
|
13
|
+
node.update(:stmts_add, ast[0].body)
|
24
14
|
end
|
25
15
|
|
26
16
|
def on_while(node)
|
27
|
-
|
17
|
+
# auto replace `while true` with `loop do`
|
18
|
+
if node[0].is?(:var_ref) &&
|
19
|
+
# the predicate to the while is a variable reference
|
20
|
+
node[0, 0].is?(:@kw) &&
|
21
|
+
# the variable reference is a keyword
|
22
|
+
node[0, 0].body == 'true'
|
23
|
+
# the keyword is "true"
|
28
24
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
25
|
+
ast = Parser.parse(<<~CODE)
|
26
|
+
loop do
|
27
|
+
#{node.source(1)}
|
28
|
+
end
|
29
|
+
CODE
|
30
|
+
|
31
|
+
node.update(:stmts_add, ast[0].body)
|
32
|
+
end
|
33
|
+
|
34
|
+
# ignore `while false`
|
35
|
+
if node[0].is?(:var_ref) &&
|
36
|
+
# the predicate to the while is a variable reference
|
37
|
+
node[0, 0].is?(:@kw) &&
|
38
|
+
# the variable reference is a keyword
|
39
|
+
node[0, 0].body == 'false'
|
40
|
+
# the kwyword is "false"
|
34
41
|
|
35
|
-
|
42
|
+
node.update(:void_stmt, [])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_until(node)
|
47
|
+
# auto replace `until false` with `loop do`
|
48
|
+
if node[0].is?(:var_ref) &&
|
49
|
+
# the predicate to the while is a variable reference
|
50
|
+
node[0, 0].is?(:@kw) &&
|
51
|
+
# the variable reference is a keyword
|
52
|
+
node[0, 0].body == 'false'
|
53
|
+
# the keyword is "false"
|
54
|
+
|
55
|
+
ast = Parser.parse(<<~CODE)
|
56
|
+
loop do
|
57
|
+
#{node.source(1)}
|
58
|
+
end
|
59
|
+
CODE
|
60
|
+
|
61
|
+
node.update(:stmts_add, ast[0].body)
|
62
|
+
end
|
63
|
+
|
64
|
+
# ignore `until true`
|
65
|
+
if node[0].is?(:var_ref) &&
|
66
|
+
# the predicate to the until is a variable reference
|
67
|
+
node[0, 0].is?(:@kw) &&
|
68
|
+
# the variable reference is a keyword
|
69
|
+
node[0, 0].body == 'true'
|
70
|
+
# the kwyword is "true"
|
71
|
+
|
72
|
+
node.update(:void_stmt, [])
|
73
|
+
end
|
36
74
|
end
|
37
75
|
end
|
38
76
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: preval
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Deisz
|
@@ -71,7 +71,9 @@ files:
|
|
71
71
|
- bin/parse
|
72
72
|
- bin/print
|
73
73
|
- bin/setup
|
74
|
+
- bin/start
|
74
75
|
- docs/index.html
|
76
|
+
- docs/public/favicon.png
|
75
77
|
- docs/server.rb
|
76
78
|
- lib/preval.rb
|
77
79
|
- lib/preval/format.rb
|
@@ -80,8 +82,9 @@ files:
|
|
80
82
|
- lib/preval/version.rb
|
81
83
|
- lib/preval/visitor.rb
|
82
84
|
- lib/preval/visitors/arithmetic.rb
|
85
|
+
- lib/preval/visitors/attr_accessor.rb
|
86
|
+
- lib/preval/visitors/fasterer.rb
|
83
87
|
- lib/preval/visitors/loops.rb
|
84
|
-
- lib/preval/visitors/micro.rb
|
85
88
|
- preval.gemspec
|
86
89
|
homepage: https://github.com/kddeisz/preval
|
87
90
|
licenses:
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Preval
|
4
|
-
class Visitors
|
5
|
-
class Micro < Visitor
|
6
|
-
def on_call(node)
|
7
|
-
left, _period, right = node.body
|
8
|
-
|
9
|
-
if node.type_match?(:call, :@period, :@ident) && left.type_match?(%i[array vcall], :@period, :@ident)
|
10
|
-
callleft, callperiod, callright = left.body
|
11
|
-
|
12
|
-
if callright.body == 'reverse' && right.body == 'each'
|
13
|
-
callright.update(:@ident, 'reverse_each')
|
14
|
-
node.update(:call, [callleft, callperiod, callright])
|
15
|
-
elsif callright.body == 'shuffle' && right.body == 'first'
|
16
|
-
callright.update(:@ident, 'sample')
|
17
|
-
node.update(:call, [callleft, callperiod, callright])
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def on_def(node)
|
23
|
-
if node.type_match?(:@ident, :params, :bodystmt) &&
|
24
|
-
node.body[1].body.none? &&
|
25
|
-
node.dig(2, 0, 0).is?(:stmts_new) &&
|
26
|
-
|
27
|
-
var_ref = node.dig(2, 0, 1)
|
28
|
-
|
29
|
-
if var_ref.is?(:var_ref) &&
|
30
|
-
var_ref.type_match?(:@ivar) &&
|
31
|
-
node.body[0].body == var_ref.body[0].body[1..-1]
|
32
|
-
|
33
|
-
sexp = Parser.parse("attr_reader :#{node.body[0].body}")
|
34
|
-
node.update(:stmts_add, sexp.body[0].body)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def on_method_add_arg(node)
|
40
|
-
if node.type_match?(:call, :arg_paren)
|
41
|
-
if node.body[0].type_match?(:vcall, :@period, :@ident) &&
|
42
|
-
node.dig(0, 2).body == 'gsub'
|
43
|
-
|
44
|
-
left = node.dig(1, 0, 0, 0, 1)
|
45
|
-
right = node.dig(1, 0, 0, 1)
|
46
|
-
|
47
|
-
if left.is?(:string_literal) &&
|
48
|
-
right.is?(:string_literal) &&
|
49
|
-
[left, right].all? do |node|
|
50
|
-
node.dig(0, 1).is?(:@tstring_content) &&
|
51
|
-
node.dig(0, 1).body.length == 1
|
52
|
-
end
|
53
|
-
|
54
|
-
node.dig(0, 2).update(:@ident, 'tr')
|
55
|
-
end
|
56
|
-
elsif node.dig(0).type_match?(:method_add_block, :@period, :@ident) &&
|
57
|
-
node.dig(0, 0, 0).type_match?(%i[array vcall], :@period, :@ident) &&
|
58
|
-
node.dig(0, 0, 0, 2).body == 'map' &&
|
59
|
-
node.dig(0, 2).body == 'flatten' &&
|
60
|
-
node.dig(1).is?(:arg_paren) &&
|
61
|
-
node.dig(1, 0, 0, 0).is?(:args_new) &&
|
62
|
-
node.dig(1, 0, 0, 1).is?(:@int) &&
|
63
|
-
node.dig(1, 0, 0, 1).body == '1'
|
64
|
-
|
65
|
-
node.dig(0, 0, 0, 2).update(:@ident, 'flat_map')
|
66
|
-
node.update(:method_add_block, node.dig(0, 0).body)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|