preval 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +12 -5
- data/bin/console +1 -0
- data/bin/parse +6 -0
- data/bin/print +1 -0
- data/docs/index.html +1 -0
- data/docs/server.rb +4 -0
- data/lib/preval.rb +2 -0
- data/lib/preval/format.rb +11 -11
- data/lib/preval/node.rb +30 -0
- data/lib/preval/version.rb +1 -1
- data/lib/preval/visitors/arithmetic.rb +3 -1
- data/lib/preval/visitors/loops.rb +17 -3
- data/lib/preval/visitors/micro.rb +72 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31253267bb39c592ddd4ee513348088c751021d98064f26f17c1a723877eaa45
|
4
|
+
data.tar.gz: f3b1607bda33cfd394a4730c1f443b572a2c101e0db73c05c270db3ff148cb96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86be3184bfbc1cae9aace4b5526c8e4ad6e47fd65c7a717df51b3a7e07c68acd7878474ebebc615c4525b5394dbe05b7039f3a4c6f6f25818ff60afeff12a762
|
7
|
+
data.tar.gz: 320346f7ecbd7c75326a2793c0f2751921cdcead050851fe32ca9a0c5b336e873c4e1d27dad121aea32663fb0673d23e377da1e538c3a3baed94d28131bbd33e
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,14 @@ 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
|
+
### Added
|
9
|
+
- Fold constant for exponentiation if exponent is 0 and value is an integer.
|
10
|
+
- Replace `.reverse.each` usage with `.reverse_each`.
|
11
|
+
- Replace `foo ... in` loops with `.each do` loops.
|
12
|
+
- Replace `.gsub('...', '...')` with `.tr('...', '...')` if the arguments are strings and they are of length 1.
|
13
|
+
- Replace `def foo; @foo; end` with `attr_reader :foo`.
|
14
|
+
- Replace `.shuffle.first` with `.sample`.
|
15
|
+
- Replace `.map { ... }.flatten(1)` with `.flat_map { ... }`.
|
8
16
|
|
9
17
|
## [0.2.0] - 2019-04-18
|
10
18
|
### Added
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -28,11 +28,18 @@ If you're using the `bootsnap` gem, `preval` will automatically hook into its co
|
|
28
28
|
|
29
29
|
Each optimization is generally named for the function it performs, and can be enabled through the `enable!` method on the visitor class. If you do not explicitly call `enable!` on any optimizations, nothing will change with your source.
|
30
30
|
|
31
|
-
* `Preval::Visitors::Arithmetic`
|
32
|
-
*
|
33
|
-
*
|
34
|
-
* `Preval::Visitors::
|
35
|
-
*
|
31
|
+
* `Preval::Visitors::Arithmetic` replaces:
|
32
|
+
* constant expressions with their evaluation (e.g., `5 + 2` becomes `7`)
|
33
|
+
* arithmetic identities with their evaluation (e.g., `a * 1` becomes `a`)
|
34
|
+
* `Preval::Visitors::Micro` replaces:
|
35
|
+
* `def foo; @foo; end` with `attr_reader :foo`
|
36
|
+
* `.gsub('...', '...')` with `.tr('...', '...')` if the arguments are strings and are both of length 1
|
37
|
+
* `.map { ... }.flatten(1)` with `.flat_map { ... }`
|
38
|
+
* `.reverse.each` with `.reverse_each`
|
39
|
+
* `.shuffle.first` with `.sample`
|
40
|
+
* `Preval::Visitors::Loops` replaces:
|
41
|
+
* `for ... in ... end` loops with `... each do ... end` loops
|
42
|
+
* `while true ... end` loops with `loop do ... end` loops
|
36
43
|
|
37
44
|
## Development
|
38
45
|
|
data/bin/console
CHANGED
data/bin/parse
ADDED
data/bin/print
CHANGED
data/docs/index.html
CHANGED
data/docs/server.rb
CHANGED
data/lib/preval.rb
CHANGED
@@ -21,8 +21,10 @@ require 'preval/node'
|
|
21
21
|
require 'preval/parser'
|
22
22
|
require 'preval/version'
|
23
23
|
require 'preval/visitor'
|
24
|
+
|
24
25
|
require 'preval/visitors/arithmetic'
|
25
26
|
require 'preval/visitors/loops'
|
27
|
+
require 'preval/visitors/micro'
|
26
28
|
|
27
29
|
if defined?(Bootsnap)
|
28
30
|
load_iseq = RubyVM::InstructionSequence.method(:load_iseq)
|
data/lib/preval/format.rb
CHANGED
@@ -10,23 +10,23 @@ module Preval
|
|
10
10
|
to(:aref) { body[1] ? "#{source(0)}[#{source(1)}]" : "#{source(0)}[]" }
|
11
11
|
to(:aref_field) { "#{source(0)}[#{source(1)}]" }
|
12
12
|
to(:arg_paren) { body[0].nil? ? '' : "(#{source(0)})" }
|
13
|
-
to(:args_add) { starts_with?(:args_new) ? source(1) : join(',') }
|
13
|
+
to(:args_add) { starts_with?(:args_new) ? source(1) : join(', ') }
|
14
14
|
to(:args_add_block) do
|
15
15
|
args, block = body
|
16
16
|
|
17
17
|
parts = args.is?(:args_new) ? [] : [args.to_source]
|
18
|
-
parts << "#{parts.any? ? ',' : ''}&#{block.to_source}" if block
|
18
|
+
parts << "#{parts.any? ? ', ' : ''}&#{block.to_source}" if block
|
19
19
|
|
20
20
|
parts.join
|
21
21
|
end
|
22
|
-
to(:args_add_star) { starts_with?(:args_new) ? "*#{source(1)}" : "#{source(0)}
|
22
|
+
to(:args_add_star) { starts_with?(:args_new) ? "*#{source(1)}" : "#{source(0)}, *#{source(1)}" }
|
23
23
|
to(:args_new) { '' }
|
24
24
|
to(:assign) { "#{source(0)} = #{source(1)}" }
|
25
25
|
to(:array) { body[0].nil? ? '[]' : "#{starts_with?(:args_add) ? '[' : ''}#{source(0)}]" }
|
26
26
|
to(:assoc_new) { starts_with?(:@label) ? join(' ') : join(' => ') }
|
27
27
|
to(:assoc_splat) { "**#{source(0)}" }
|
28
|
-
to(:assoclist_from_args) { body[0].map(&:to_source).join(',') }
|
29
|
-
to(:bare_assoc_hash) { body[0].map(&:to_source).join(',') }
|
28
|
+
to(:assoclist_from_args) { body[0].map(&:to_source).join(', ') }
|
29
|
+
to(:bare_assoc_hash) { body[0].map(&:to_source).join(', ') }
|
30
30
|
to(:begin) { "begin\n#{join("\n")}\nend" }
|
31
31
|
to(:BEGIN) { "BEGIN {\n#{source(0)}\n}"}
|
32
32
|
to(:binary) { "#{source(0)} #{body[1]} #{source(2)}" }
|
@@ -67,7 +67,7 @@ module Preval
|
|
67
67
|
to(:ensure) { "ensure\n#{source(0)}" }
|
68
68
|
to(:fcall) { join }
|
69
69
|
to(:field) { join }
|
70
|
-
to(:for) { "#{source(
|
70
|
+
to(:for) { "for #{source(0)} in #{source(1)} do\n#{source(2)}\nend" }
|
71
71
|
to(:hash) { body[0].nil? ? '{}' : "{ #{join} }" }
|
72
72
|
to(:if) { "if #{source(0)}\n#{source(1)}\n#{body[2] ? "#{source(2)}\n" : ''}end" }
|
73
73
|
to(:if_mod) { "#{source(1)} if #{source(0)}" }
|
@@ -78,11 +78,11 @@ module Preval
|
|
78
78
|
to(:method_add_arg) { body[1].is?(:args_new) ? source(0) : join }
|
79
79
|
to(:method_add_block) { join }
|
80
80
|
to(:methref) { join('.:') }
|
81
|
-
to(:mlhs_add) { starts_with?(:mlhs_new) ? source(1) : join(',') }
|
82
|
-
to(:mlhs_add_post) { join(',') }
|
83
|
-
to(:mlhs_add_star) { "#{starts_with?(:mlhs_new) ? '' : "#{source(0)},"}#{body[1] ? "*#{source(1)}" : '*'}" }
|
81
|
+
to(:mlhs_add) { starts_with?(:mlhs_new) ? source(1) : join(', ') }
|
82
|
+
to(:mlhs_add_post) { join(', ') }
|
83
|
+
to(:mlhs_add_star) { "#{starts_with?(:mlhs_new) ? '' : "#{source(0)}, "}#{body[1] ? "*#{source(1)}" : '*'}" }
|
84
84
|
to(:mlhs_paren) { "(#{source(0)})" }
|
85
|
-
to(:mrhs_add) { join(',') }
|
85
|
+
to(:mrhs_add) { join(', ') }
|
86
86
|
to(:mrhs_add_star) { "*#{join}" }
|
87
87
|
to(:mrhs_new) { '' }
|
88
88
|
to(:mrhs_new_from_args) { source(0) }
|
@@ -102,7 +102,7 @@ module Preval
|
|
102
102
|
parts << kwarg_rest.to_source if kwarg_rest
|
103
103
|
parts << block.to_source if block
|
104
104
|
|
105
|
-
parts.join(',')
|
105
|
+
parts.join(', ')
|
106
106
|
end
|
107
107
|
to(:program) { "#{join("\n")}\n" }
|
108
108
|
to(:qsymbols_add) { join(starts_with?(:qsymbols_new) ? '' : ' ') }
|
data/lib/preval/node.rb
CHANGED
@@ -2,6 +2,25 @@
|
|
2
2
|
|
3
3
|
module Preval
|
4
4
|
class Node
|
5
|
+
class TypeMatch
|
6
|
+
attr_reader :types
|
7
|
+
|
8
|
+
def initialize(types)
|
9
|
+
@types = types
|
10
|
+
end
|
11
|
+
|
12
|
+
def match?(node)
|
13
|
+
node.body.size == types.size &&
|
14
|
+
node.body.zip(types).all? do |(left, right)|
|
15
|
+
left.is_a?(Node) && Array(right).include?(left.type)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.match?(types, node)
|
20
|
+
new(types).match?(node)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
5
24
|
include Format
|
6
25
|
|
7
26
|
attr_reader :type, :body, :literal
|
@@ -12,6 +31,13 @@ module Preval
|
|
12
31
|
@literal = literal
|
13
32
|
end
|
14
33
|
|
34
|
+
def dig(index, *args)
|
35
|
+
node = body[index]
|
36
|
+
return nil unless node
|
37
|
+
|
38
|
+
args.any? ? node.dig(*args) : node
|
39
|
+
end
|
40
|
+
|
15
41
|
def join(delim = '')
|
16
42
|
body.map(&:to_source).join(delim)
|
17
43
|
end
|
@@ -44,6 +70,10 @@ module Preval
|
|
44
70
|
end
|
45
71
|
end
|
46
72
|
|
73
|
+
def type_match?(*types)
|
74
|
+
TypeMatch.new(types).match?(self)
|
75
|
+
end
|
76
|
+
|
47
77
|
def update(type, body)
|
48
78
|
@type = type
|
49
79
|
@body = body
|
data/lib/preval/version.rb
CHANGED
@@ -38,7 +38,9 @@ module Preval
|
|
38
38
|
node.replace(right)
|
39
39
|
end
|
40
40
|
elsif operation == :**
|
41
|
-
if right.int?(
|
41
|
+
if left.is?(:@int) && right.int?(0)
|
42
|
+
node.update(:@int, left.to_int < 0 ? -1 : 1)
|
43
|
+
elsif right.int?(1)
|
42
44
|
node.replace(left)
|
43
45
|
elsif left.int?(1)
|
44
46
|
node.replace(left)
|
@@ -13,11 +13,25 @@ module Preval
|
|
13
13
|
|
14
14
|
using TrueNode
|
15
15
|
|
16
|
+
def on_for(node)
|
17
|
+
sexp = Parser.parse(<<~CODE)
|
18
|
+
#{node.source(1)}.each do |#{node.source(0)}|
|
19
|
+
#{node.source(2)}
|
20
|
+
end
|
21
|
+
CODE
|
22
|
+
|
23
|
+
node.update(:stmts_add, sexp.body[0].body)
|
24
|
+
end
|
25
|
+
|
16
26
|
def on_while(node)
|
17
|
-
|
18
|
-
|
27
|
+
return unless node.body[0].true?
|
28
|
+
|
29
|
+
sexp = Parser.parse(<<~CODE)
|
30
|
+
loop do
|
31
|
+
#{node.source(1)}
|
32
|
+
end
|
33
|
+
CODE
|
19
34
|
|
20
|
-
sexp = Parser.parse("loop do\n#{statements.to_source}\nend")
|
21
35
|
node.update(:stmts_add, sexp.body[0].body)
|
22
36
|
end
|
23
37
|
end
|
@@ -0,0 +1,72 @@
|
|
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
|
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.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Deisz
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- README.md
|
69
69
|
- Rakefile
|
70
70
|
- bin/console
|
71
|
+
- bin/parse
|
71
72
|
- bin/print
|
72
73
|
- bin/setup
|
73
74
|
- docs/index.html
|
@@ -80,6 +81,7 @@ files:
|
|
80
81
|
- lib/preval/visitor.rb
|
81
82
|
- lib/preval/visitors/arithmetic.rb
|
82
83
|
- lib/preval/visitors/loops.rb
|
84
|
+
- lib/preval/visitors/micro.rb
|
83
85
|
- preval.gemspec
|
84
86
|
homepage: https://github.com/kddeisz/preval
|
85
87
|
licenses:
|