ruby-next-core 0.9.0 → 0.10.2
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 +42 -0
- data/README.md +20 -6
- data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +107 -0
- data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
- data/lib/ruby-next.rb +8 -6
- data/lib/ruby-next/cli.rb +2 -2
- data/lib/ruby-next/commands/core_ext.rb +1 -1
- data/lib/ruby-next/commands/nextify.rb +41 -10
- data/lib/ruby-next/core.rb +27 -21
- data/lib/ruby-next/core/array/deconstruct.rb +9 -9
- data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
- data/lib/ruby-next/core/enumerable/filter.rb +8 -8
- data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
- data/lib/ruby-next/core/enumerable/tally.rb +7 -7
- data/lib/ruby-next/core/enumerator/produce.rb +12 -12
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
- data/lib/ruby-next/core/hash/except.rb +11 -0
- data/lib/ruby-next/core/hash/merge.rb +8 -8
- data/lib/ruby-next/core/kernel/then.rb +2 -2
- data/lib/ruby-next/core/proc/compose.rb +11 -11
- data/lib/ruby-next/core/string/split.rb +6 -6
- data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
- data/lib/ruby-next/core/symbol/end_with.rb +4 -4
- data/lib/ruby-next/core/symbol/start_with.rb +4 -4
- data/lib/ruby-next/core/time/ceil.rb +6 -6
- data/lib/ruby-next/core/time/floor.rb +4 -4
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
- data/lib/ruby-next/core_ext.rb +1 -1
- data/lib/ruby-next/language.rb +30 -6
- data/lib/ruby-next/language/proposed.rb +3 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
- data/lib/ruby-next/language/rewriters/base.rb +1 -1
- data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
- data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
- data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +103 -12
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +74 -11
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
- data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
- data/lib/ruby-next/logging.rb +1 -1
- data/lib/ruby-next/rubocop.rb +15 -9
- data/lib/ruby-next/setup_self.rb +22 -0
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +8 -4
- metadata +21 -7
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Language
|
5
|
+
module Rewriters
|
6
|
+
class RightHandAssignment < Base
|
7
|
+
NAME = "right-hand-assignment"
|
8
|
+
SYNTAX_PROBE = "1 + 2 => a"
|
9
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
|
10
|
+
|
11
|
+
def on_rasgn(node)
|
12
|
+
context.track! self
|
13
|
+
|
14
|
+
node = super(node)
|
15
|
+
|
16
|
+
val_node, asgn_node = *node
|
17
|
+
|
18
|
+
remove(val_node.loc.expression.end.join(asgn_node.loc.expression))
|
19
|
+
insert_before(val_node.loc.expression, "#{asgn_node.loc.expression.source} = ")
|
20
|
+
|
21
|
+
asgn_node.updated(
|
22
|
+
nil,
|
23
|
+
asgn_node.children + [val_node]
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_vasgn(node)
|
28
|
+
return super(node) unless rightward?(node)
|
29
|
+
|
30
|
+
context.track! self
|
31
|
+
|
32
|
+
name, val_node = *node
|
33
|
+
|
34
|
+
remove(val_node.loc.expression.end.join(node.loc.name))
|
35
|
+
insert_before(val_node.loc.expression, "#{name} = ")
|
36
|
+
|
37
|
+
super(node)
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_casgn(node)
|
41
|
+
return super(node) unless rightward?(node)
|
42
|
+
|
43
|
+
context.track! self
|
44
|
+
|
45
|
+
scope_node, name, val_node = *node
|
46
|
+
|
47
|
+
if scope_node
|
48
|
+
scope = scope_node.type == :cbase ? scope_node.loc.expression.source : "#{scope_node.loc.expression.source}::"
|
49
|
+
name = "#{scope}#{name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
remove(val_node.loc.expression.end.join(node.loc.name))
|
53
|
+
insert_before(val_node.loc.expression, "#{name} = ")
|
54
|
+
|
55
|
+
super(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_mrasgn(node)
|
59
|
+
context.track! self
|
60
|
+
|
61
|
+
node = super(node)
|
62
|
+
|
63
|
+
lhs, rhs = *node
|
64
|
+
|
65
|
+
replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
|
66
|
+
insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
|
67
|
+
|
68
|
+
node.updated(
|
69
|
+
:masgn,
|
70
|
+
[rhs, lhs]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_masgn(node)
|
75
|
+
return super(node) unless rightward?(node)
|
76
|
+
|
77
|
+
context.track! self
|
78
|
+
|
79
|
+
rhs, lhs = *node
|
80
|
+
|
81
|
+
replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
|
82
|
+
insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
|
83
|
+
|
84
|
+
super(node)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def rightward?(node)
|
90
|
+
# Location could be empty for node built by rewriters
|
91
|
+
return false unless ((!node.loc.nil?) || nil) && node.loc.operator
|
92
|
+
|
93
|
+
assignee_loc =
|
94
|
+
if node.type == :masgn
|
95
|
+
node.children[0].loc.expression
|
96
|
+
else
|
97
|
+
node.loc.name
|
98
|
+
end
|
99
|
+
|
100
|
+
return false unless assignee_loc
|
101
|
+
|
102
|
+
assignee_loc.begin_pos > node.loc.operator.end_pos
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Utils
|
5
|
+
module_function
|
6
|
+
|
7
|
+
if $LOAD_PATH.respond_to?(:resolve_feature_path)
|
8
|
+
def resolve_feature_path(feature)
|
9
|
+
((!$LOAD_PATH.resolve_feature_path(feature).nil?) || nil) && $LOAD_PATH.resolve_feature_path(feature).last
|
10
|
+
rescue LoadError
|
11
|
+
end
|
12
|
+
else
|
13
|
+
def resolve_feature_path(path)
|
14
|
+
if File.file?(relative = File.expand_path(path))
|
15
|
+
path = relative
|
16
|
+
end
|
17
|
+
|
18
|
+
path = "#{path}.rb" if File.extname(path).empty?
|
19
|
+
|
20
|
+
return path if Pathname.new(path).absolute?
|
21
|
+
|
22
|
+
$LOAD_PATH.find do |lp|
|
23
|
+
lpath = File.join(lp, path)
|
24
|
+
return File.realpath(lpath) if File.file?(lpath)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def source_with_lines(source, path)
|
30
|
+
source.lines.map.with_index do |line, i|
|
31
|
+
"#{(i + 1).to_s.rjust(4)}: #{line}"
|
32
|
+
end.tap do |lines|
|
33
|
+
lines.unshift " 0: # source: #{path}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns true if modules refinement is supported in current version
|
38
|
+
def refine_modules?
|
39
|
+
@refine_modules ||=
|
40
|
+
begin
|
41
|
+
# Make sure that including modules within refinements works
|
42
|
+
# See https://github.com/oracle/truffleruby/issues/2026
|
43
|
+
eval <<-RUBY, TOPLEVEL_BINDING, __FILE__, __LINE__ + 1
|
44
|
+
module RubyNext::Utils::A; end
|
45
|
+
class RubyNext::Utils::B
|
46
|
+
include RubyNext::Utils::A
|
47
|
+
end
|
48
|
+
using(Module.new do
|
49
|
+
refine RubyNext::Utils::A do
|
50
|
+
include(Module.new do
|
51
|
+
def i_am_refinement
|
52
|
+
"yes, you are!"
|
53
|
+
end
|
54
|
+
end)
|
55
|
+
end
|
56
|
+
end)
|
57
|
+
RubyNext::Utils::B.new.i_am_refinement
|
58
|
+
RUBY
|
59
|
+
true
|
60
|
+
rescue TypeError, NoMethodError
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/ruby-next.rb
CHANGED
@@ -4,17 +4,18 @@ require "ruby-next/version"
|
|
4
4
|
|
5
5
|
module RubyNext
|
6
6
|
# Mininum Ruby version supported by RubyNext
|
7
|
-
MIN_SUPPORTED_VERSION = Gem::Version.new("2.
|
7
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("2.2.0")
|
8
8
|
|
9
9
|
# Where to store transpiled files (relative from the project LOAD_PATH, usually `lib/`)
|
10
10
|
RUBY_NEXT_DIR = ".rbnext"
|
11
11
|
|
12
12
|
# Defines last minor version for every major version
|
13
13
|
LAST_MINOR_VERSIONS = {
|
14
|
-
2 => 8
|
14
|
+
2 => 8, # 2.8 is required for backward compatibility: some gems already uses it
|
15
|
+
3 => 0
|
15
16
|
}.freeze
|
16
17
|
|
17
|
-
LATEST_VERSION = [
|
18
|
+
LATEST_VERSION = [3, 0].freeze
|
18
19
|
|
19
20
|
class << self
|
20
21
|
def next_version(version = RUBY_VERSION)
|
@@ -33,7 +34,8 @@ module RubyNext
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
require "ruby-next/setup_self"
|
38
|
+
require "ruby-next/core"
|
39
|
+
require "ruby-next/core_ext" if RubyNext::Core.core_ext?
|
40
|
+
require "ruby-next/logging"
|
39
41
|
end
|
data/lib/ruby-next/cli.rb
CHANGED
@@ -61,7 +61,7 @@ module RubyNext
|
|
61
61
|
def maybe_print_help
|
62
62
|
return unless @print_help
|
63
63
|
|
64
|
-
|
64
|
+
$stdout.puts optparser.help
|
65
65
|
exit 0
|
66
66
|
end
|
67
67
|
|
@@ -85,7 +85,7 @@ module RubyNext
|
|
85
85
|
opts.banner = "Usage: ruby-next COMMAND [options]"
|
86
86
|
|
87
87
|
opts.on("-v", "--version", "Print version") do
|
88
|
-
|
88
|
+
$stdout.puts RubyNext::VERSION
|
89
89
|
exit 0
|
90
90
|
end
|
91
91
|
|
@@ -10,7 +10,7 @@ module RubyNext
|
|
10
10
|
class Nextify < Base
|
11
11
|
using RubyNext
|
12
12
|
|
13
|
-
attr_reader :lib_path, :paths, :out_path, :min_version, :single_version
|
13
|
+
attr_reader :lib_path, :paths, :out_path, :min_version, :single_version, :specified_rewriters
|
14
14
|
|
15
15
|
def run
|
16
16
|
log "RubyNext core strategy: #{RubyNext::Core.strategy}"
|
@@ -18,6 +18,8 @@ module RubyNext
|
|
18
18
|
|
19
19
|
remove_rbnext!
|
20
20
|
|
21
|
+
@min_version ||= MIN_SUPPORTED_VERSION
|
22
|
+
|
21
23
|
paths.each do |path|
|
22
24
|
contents = File.read(path)
|
23
25
|
transpile path, contents
|
@@ -26,7 +28,8 @@ module RubyNext
|
|
26
28
|
|
27
29
|
def parse!(args)
|
28
30
|
print_help = false
|
29
|
-
|
31
|
+
print_rewriters = false
|
32
|
+
rewriter_names = []
|
30
33
|
@single_version = false
|
31
34
|
|
32
35
|
optparser = base_parser do |opts|
|
@@ -63,6 +66,14 @@ module RubyNext
|
|
63
66
|
Core.strategy = :core_ext unless val
|
64
67
|
end
|
65
68
|
|
69
|
+
opts.on("--list-rewriters", "List available rewriters") do |val|
|
70
|
+
print_rewriters = true
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on("--rewrite=REWRITERS...", "Specify particular Ruby features to rewrite") do |val|
|
74
|
+
rewriter_names << val
|
75
|
+
end
|
76
|
+
|
66
77
|
opts.on("-h", "--help", "Print help") do
|
67
78
|
print_help = true
|
68
79
|
end
|
@@ -77,12 +88,35 @@ module RubyNext
|
|
77
88
|
exit 0
|
78
89
|
end
|
79
90
|
|
91
|
+
if print_rewriters
|
92
|
+
Language.rewriters.each do |rewriter|
|
93
|
+
$stdout.puts "#{rewriter::NAME} (\"#{rewriter::SYNTAX_PROBE}\")"
|
94
|
+
end
|
95
|
+
exit 0
|
96
|
+
end
|
97
|
+
|
80
98
|
unless lib_path&.then(&File.method(:exist?))
|
81
99
|
$stdout.puts "Path not found: #{lib_path}"
|
82
100
|
$stdout.puts optparser.help
|
83
101
|
exit 2
|
84
102
|
end
|
85
103
|
|
104
|
+
if rewriter_names.any? && min_version
|
105
|
+
$stdout.puts "--rewrite cannot be used with --min-version simultaneously"
|
106
|
+
exit 2
|
107
|
+
end
|
108
|
+
|
109
|
+
@specified_rewriters =
|
110
|
+
if rewriter_names.any?
|
111
|
+
begin
|
112
|
+
Language.select_rewriters(*rewriter_names)
|
113
|
+
rescue Language::RewriterNotFoundError => error
|
114
|
+
$stdout.puts error.message
|
115
|
+
$stdout.puts "Try --list-rewriters to see list of available rewriters"
|
116
|
+
exit 2
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
86
120
|
@paths =
|
87
121
|
if File.directory?(lib_path)
|
88
122
|
Dir[File.join(lib_path, "**/*.rb")]
|
@@ -96,16 +130,11 @@ module RubyNext
|
|
96
130
|
private
|
97
131
|
|
98
132
|
def transpile(path, contents, version: min_version)
|
99
|
-
rewriters = Language.rewriters.select { |rw| rw.unsupported_version?(version) }
|
133
|
+
rewriters = specified_rewriters || Language.rewriters.select { |rw| rw.unsupported_version?(version) }
|
100
134
|
|
101
135
|
context = Language::TransformContext.new
|
102
136
|
|
103
|
-
new_contents =
|
104
|
-
if Gem::Version.new(version) >= Gem::Version.new("2.7.0") && !defined?(Unparser::Emitter::CaseMatch)
|
105
|
-
Language.rewrite contents, context: context, rewriters: rewriters
|
106
|
-
else
|
107
|
-
Language.transform contents, context: context, rewriters: rewriters
|
108
|
-
end
|
137
|
+
new_contents = Language.transform contents, context: context, rewriters: rewriters
|
109
138
|
|
110
139
|
return unless context.dirty?
|
111
140
|
|
@@ -161,7 +190,9 @@ module RubyNext
|
|
161
190
|
out_path == "stdout"
|
162
191
|
end
|
163
192
|
|
164
|
-
|
193
|
+
def single_version?
|
194
|
+
single_version || specified_rewriters
|
195
|
+
end
|
165
196
|
end
|
166
197
|
end
|
167
198
|
end
|
data/lib/ruby-next/core.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "set"
|
4
4
|
|
5
|
-
|
5
|
+
require "ruby-next/utils"
|
6
6
|
|
7
7
|
module RubyNext
|
8
8
|
module Core
|
@@ -25,7 +25,7 @@ module RubyNext
|
|
25
25
|
@supported = supported.nil? ? mod.method_defined?(method_name) : supported
|
26
26
|
# define whether running Ruby has a native implementation for this method
|
27
27
|
# for that, we check the source_location (which is nil for C defined methods)
|
28
|
-
@native = native.nil? ? (supported? && mod.instance_method(method_name).source_location
|
28
|
+
@native = native.nil? ? (supported? && native_location?(mod.instance_method(method_name).source_location)) : native
|
29
29
|
end
|
30
30
|
@singleton = singleton
|
31
31
|
@refineables = Array(refineable)
|
@@ -75,6 +75,10 @@ module RubyNext
|
|
75
75
|
|
76
76
|
[trace_location.absolute_path, trace_location.lineno + 2]
|
77
77
|
end
|
78
|
+
|
79
|
+
def native_location?(location)
|
80
|
+
location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core)/)
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
# Registry for patches
|
@@ -144,29 +148,29 @@ end
|
|
144
148
|
|
145
149
|
require "backports/2.5" if RubyNext::Core.backports?
|
146
150
|
|
147
|
-
|
151
|
+
require "ruby-next/core/kernel/then"
|
148
152
|
|
149
|
-
|
153
|
+
require "ruby-next/core/proc/compose"
|
150
154
|
|
151
|
-
|
152
|
-
|
153
|
-
|
155
|
+
require "ruby-next/core/enumerable/tally"
|
156
|
+
require "ruby-next/core/enumerable/filter"
|
157
|
+
require "ruby-next/core/enumerable/filter_map"
|
154
158
|
|
155
|
-
|
159
|
+
require "ruby-next/core/enumerator/produce"
|
156
160
|
|
157
|
-
|
161
|
+
require "ruby-next/core/array/difference_union_intersection"
|
158
162
|
|
159
|
-
|
163
|
+
require "ruby-next/core/hash/merge"
|
160
164
|
|
161
|
-
|
165
|
+
require "ruby-next/core/string/split"
|
162
166
|
|
163
|
-
|
164
|
-
|
167
|
+
require "ruby-next/core/symbol/start_with"
|
168
|
+
require "ruby-next/core/symbol/end_with"
|
165
169
|
|
166
|
-
|
170
|
+
require "ruby-next/core/unboundmethod/bind_call"
|
167
171
|
|
168
|
-
|
169
|
-
|
172
|
+
require "ruby-next/core/time/floor"
|
173
|
+
require "ruby-next/core/time/ceil"
|
170
174
|
|
171
175
|
# Core extensions required for pattern matching
|
172
176
|
# Required for pattern matching with refinements
|
@@ -175,11 +179,13 @@ unless defined?(NoMatchingPatternError)
|
|
175
179
|
end
|
176
180
|
end
|
177
181
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
182
|
+
require "ruby-next/core/constants/no_matching_pattern_error"
|
183
|
+
require "ruby-next/core/array/deconstruct"
|
184
|
+
require "ruby-next/core/hash/deconstruct_keys"
|
185
|
+
require "ruby-next/core/struct/deconstruct"
|
186
|
+
require "ruby-next/core/struct/deconstruct_keys"
|
187
|
+
|
188
|
+
require "ruby-next/core/hash/except"
|
183
189
|
|
184
190
|
# Generate refinements
|
185
191
|
RubyNext.module_eval do
|
@@ -1,21 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RubyNext::Core.patch Array, method: :deconstruct, version: "2.7" do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
<<-RUBY
|
5
|
+
def deconstruct
|
6
|
+
self
|
7
|
+
end
|
8
8
|
RUBY
|
9
9
|
end
|
10
10
|
|
11
11
|
# We need to hack `respond_to?` in Ruby 2.5, since it's not working with refinements
|
12
12
|
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6")
|
13
13
|
RubyNext::Core.patch refineable: Array, name: "ArrayRespondToDeconstruct", method: :deconstruct, version: "2.7" do
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
<<-RUBY
|
15
|
+
def respond_to?(mid, *)
|
16
|
+
return true if mid == :deconstruct
|
17
|
+
super
|
18
|
+
end
|
19
19
|
RUBY
|
20
20
|
end
|
21
21
|
end
|