preval 0.2.0 → 0.3.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 +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:
|