ruby-next-core 0.14.0 → 1.0.1
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 +70 -0
- data/README.md +163 -56
- data/bin/mspec +11 -0
- data/lib/.rbnext/2.1/ruby-next/commands/nextify.rb +295 -0
- data/lib/.rbnext/2.1/ruby-next/core.rb +12 -4
- data/lib/.rbnext/2.1/ruby-next/language.rb +62 -12
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +97 -3
- data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
- data/lib/.rbnext/2.3/ruby-next/core/data.rb +163 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/args_forward.rb +134 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +122 -47
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -32
- data/lib/.rbnext/2.3/ruby-next/utils.rb +3 -22
- data/lib/.rbnext/2.6/ruby-next/core/data.rb +163 -0
- data/lib/.rbnext/2.7/ruby-next/core/data.rb +163 -0
- data/lib/.rbnext/2.7/ruby-next/core.rb +12 -4
- data/lib/.rbnext/2.7/ruby-next/language/paco_parsers/string_literals.rb +109 -0
- data/lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
- data/lib/.rbnext/2.7/ruby-next/language/rewriters/text.rb +132 -0
- data/lib/.rbnext/3.2/ruby-next/commands/base.rb +55 -0
- data/lib/.rbnext/3.2/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
- data/lib/.rbnext/3.2/ruby-next/rubocop.rb +210 -0
- data/lib/ruby-next/cli.rb +10 -15
- data/lib/ruby-next/commands/nextify.rb +99 -3
- data/lib/ruby-next/config.rb +31 -4
- data/lib/ruby-next/core/data.rb +163 -0
- data/lib/ruby-next/core/matchdata/deconstruct.rb +9 -0
- data/lib/ruby-next/core/matchdata/deconstruct_keys.rb +20 -0
- data/lib/ruby-next/core/matchdata/named_captures.rb +11 -0
- data/lib/ruby-next/core/proc/compose.rb +0 -1
- data/lib/ruby-next/core/refinement/import.rb +44 -36
- data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
- data/lib/ruby-next/core.rb +11 -3
- data/lib/ruby-next/irb.rb +24 -0
- data/lib/ruby-next/language/bootsnap.rb +2 -25
- data/lib/ruby-next/language/eval.rb +4 -4
- data/lib/ruby-next/language/paco_parser.rb +7 -0
- data/lib/ruby-next/language/paco_parsers/base.rb +47 -0
- data/lib/ruby-next/language/paco_parsers/comments.rb +26 -0
- data/lib/ruby-next/language/paco_parsers/string_literals.rb +109 -0
- data/lib/ruby-next/language/parser.rb +31 -6
- data/lib/ruby-next/language/rewriters/2.5/rescue_within_block.rb +41 -0
- data/lib/ruby-next/language/rewriters/2.7/args_forward.rb +57 -0
- data/lib/ruby-next/language/rewriters/2.7/pattern_matching.rb +120 -45
- data/lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb +2 -2
- data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +1 -1
- data/lib/ruby-next/language/rewriters/3.1/shorthand_hash.rb +2 -1
- data/lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb +104 -0
- data/lib/ruby-next/language/rewriters/abstract.rb +57 -0
- data/lib/ruby-next/language/rewriters/base.rb +6 -32
- data/lib/ruby-next/language/rewriters/edge/it_param.rb +58 -0
- data/lib/ruby-next/language/rewriters/edge.rb +12 -0
- data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +3 -0
- data/lib/ruby-next/language/rewriters/proposed/method_reference.rb +9 -20
- data/lib/ruby-next/language/rewriters/text.rb +132 -0
- data/lib/ruby-next/language/runtime.rb +9 -86
- data/lib/ruby-next/language/setup.rb +5 -2
- data/lib/ruby-next/language/unparser.rb +5 -0
- data/lib/ruby-next/language.rb +62 -12
- data/lib/ruby-next/pry.rb +90 -0
- data/lib/ruby-next/rubocop.rb +2 -0
- data/lib/ruby-next/utils.rb +3 -22
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next/irb.rb +3 -0
- data/lib/uby-next/pry.rb +3 -0
- data/lib/uby-next.rb +2 -2
- metadata +70 -10
@@ -0,0 +1,210 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file contains patches to RuboCop to support
|
4
|
+
# edge features and fix some bugs with 2.7+ syntax
|
5
|
+
|
6
|
+
require "parser/ruby-next/version"
|
7
|
+
require "ruby-next/config"
|
8
|
+
require "ruby-next/language/parser"
|
9
|
+
|
10
|
+
module RuboCop
|
11
|
+
# Transform Ruby Next parser version to a float, e.g.: "2.8.0.1" => 2.801
|
12
|
+
RUBY_NEXT_VERSION = Parser::NEXT_VERSION.match(/(^\d+)\.(.+)$/)[1..-1].map { |part| part.delete(".") }.join(".").to_f
|
13
|
+
|
14
|
+
class TargetRuby
|
15
|
+
class RuboCopNextConfig < RuboCopConfig
|
16
|
+
private
|
17
|
+
|
18
|
+
def find_version
|
19
|
+
version = @config.for_all_cops["TargetRubyVersion"]
|
20
|
+
return unless version == "next"
|
21
|
+
|
22
|
+
RUBY_NEXT_VERSION
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
new_rubies = KNOWN_RUBIES + [RUBY_NEXT_VERSION]
|
27
|
+
remove_const :KNOWN_RUBIES
|
28
|
+
const_set :KNOWN_RUBIES, new_rubies
|
29
|
+
|
30
|
+
new_sources = [RuboCopNextConfig] + SOURCES
|
31
|
+
remove_const :SOURCES
|
32
|
+
const_set :SOURCES, new_sources
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module RuboCop
|
37
|
+
class ProcessedSource
|
38
|
+
module ParserClassExt
|
39
|
+
def parser_class(version)
|
40
|
+
return super unless version == RUBY_NEXT_VERSION
|
41
|
+
|
42
|
+
require "parser/rubynext"
|
43
|
+
Parser::RubyNext
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
prepend ParserClassExt
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Let's make this file Ruby 2.2 compatible to avoid transpiling
|
52
|
+
# rubocop:disable Layout/HeredocIndentation
|
53
|
+
module RuboCop
|
54
|
+
module AST
|
55
|
+
module Traversal
|
56
|
+
# Fixed in https://github.com/rubocop-hq/rubocop/pull/7786
|
57
|
+
%i[case_match in_pattern find_pattern match_pattern match_pattern_p].each do |type|
|
58
|
+
next if method_defined?(:"on_#{type}")
|
59
|
+
module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
60
|
+
def on_#{type}(node)
|
61
|
+
node.children.each { |child| send(:"on_\#{child.type}", child) if child }
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
unless Builder.method_defined?(:match_pattern_p)
|
69
|
+
Builder.include RubyNext::Language::BuilderExt
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# rubocop:enable Layout/HeredocIndentation
|
74
|
+
|
75
|
+
module RuboCop
|
76
|
+
module Cop
|
77
|
+
# Commissioner class is responsible for processing the AST and delegating
|
78
|
+
# work to the specified cops.
|
79
|
+
class Commissioner
|
80
|
+
def on_meth_ref(node)
|
81
|
+
trigger_responding_cops(:on_meth_ref, node)
|
82
|
+
end
|
83
|
+
|
84
|
+
unless method_defined?(:on_numblock)
|
85
|
+
def on_numblock(node)
|
86
|
+
children = node.children
|
87
|
+
child = children[0]
|
88
|
+
send(:"on_#{child.type}", child)
|
89
|
+
# children[1] is the number of parameters
|
90
|
+
return unless (child = children[2])
|
91
|
+
|
92
|
+
send(:"on_#{child.type}", child)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
unless method_defined?(:on_def_e)
|
97
|
+
def on_def_e(node)
|
98
|
+
_name, _args_node, body_node = *node
|
99
|
+
send(:"on_#{body_node.type}", body_node)
|
100
|
+
end
|
101
|
+
|
102
|
+
def on_defs_e(node)
|
103
|
+
_definee_node, _name, _args_node, body_node = *node
|
104
|
+
send(:"on_#{body_node.type}", body_node)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Commissioner.prepend(Module.new do
|
110
|
+
# Ignore anonymous blocks
|
111
|
+
def on_block_pass(node)
|
112
|
+
return if node.children == [nil]
|
113
|
+
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
def on_blockarg(node)
|
118
|
+
return if node.children == [nil]
|
119
|
+
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end)
|
123
|
+
|
124
|
+
module Layout
|
125
|
+
require "rubocop/cop/layout/assignment_indentation"
|
126
|
+
|
127
|
+
POTENTIAL_RIGHT_TYPES = %i[ivasgn lvasgn cvasgn gvasgn casgn masgn].freeze
|
128
|
+
|
129
|
+
AssignmentIndentation.prepend(Module.new do
|
130
|
+
def check_assignment(node, *__rest__)
|
131
|
+
return if rightward?(node)
|
132
|
+
super
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def rightward?(node)
|
138
|
+
return unless POTENTIAL_RIGHT_TYPES.include?(node.type)
|
139
|
+
|
140
|
+
return unless node.loc.operator
|
141
|
+
|
142
|
+
assignee_loc =
|
143
|
+
if node.type == :masgn
|
144
|
+
node.children[0].loc.expression
|
145
|
+
else
|
146
|
+
node.loc.name
|
147
|
+
end
|
148
|
+
|
149
|
+
return false unless assignee_loc
|
150
|
+
|
151
|
+
assignee_loc.begin_pos > node.loc.operator.end_pos
|
152
|
+
end
|
153
|
+
end)
|
154
|
+
|
155
|
+
require "rubocop/cop/layout/empty_line_between_defs"
|
156
|
+
EmptyLineBetweenDefs.prepend(Module.new do
|
157
|
+
def def_end(node)
|
158
|
+
return super unless node.loc.end.nil?
|
159
|
+
|
160
|
+
node.loc.expression.line
|
161
|
+
end
|
162
|
+
end)
|
163
|
+
|
164
|
+
require "rubocop/cop/layout/space_after_colon"
|
165
|
+
SpaceAfterColon.prepend(Module.new do
|
166
|
+
def on_pair(node)
|
167
|
+
return if node.children[0].loc.last_column == node.children[1].loc.last_column
|
168
|
+
|
169
|
+
super(node)
|
170
|
+
end
|
171
|
+
end)
|
172
|
+
end
|
173
|
+
|
174
|
+
module Style
|
175
|
+
require "rubocop/cop/style/single_line_methods"
|
176
|
+
SingleLineMethods.prepend(Module.new do
|
177
|
+
def on_def(node)
|
178
|
+
return if node.loc.end.nil?
|
179
|
+
super
|
180
|
+
end
|
181
|
+
|
182
|
+
def on_defs(node)
|
183
|
+
return if node.loc.end.nil?
|
184
|
+
super
|
185
|
+
end
|
186
|
+
end)
|
187
|
+
|
188
|
+
require "rubocop/cop/style/def_with_parentheses"
|
189
|
+
DefWithParentheses.prepend(Module.new do
|
190
|
+
def on_def(node)
|
191
|
+
return if node.loc.end.nil?
|
192
|
+
super
|
193
|
+
end
|
194
|
+
|
195
|
+
def on_defs(node)
|
196
|
+
return if node.loc.end.nil?
|
197
|
+
super
|
198
|
+
end
|
199
|
+
end)
|
200
|
+
|
201
|
+
require "rubocop/cop/style/trailing_method_end_statement"
|
202
|
+
TrailingMethodEndStatement.prepend(Module.new do
|
203
|
+
def on_def(node)
|
204
|
+
return if node.loc.end.nil?
|
205
|
+
super
|
206
|
+
end
|
207
|
+
end)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
data/lib/ruby-next/cli.rb
CHANGED
@@ -24,9 +24,6 @@ module RubyNext
|
|
24
24
|
"core_ext" => Commands::CoreExt
|
25
25
|
}.freeze
|
26
26
|
|
27
|
-
def initialize
|
28
|
-
end
|
29
|
-
|
30
27
|
def run(args = ARGV)
|
31
28
|
maybe_print_version(args)
|
32
29
|
|
@@ -80,18 +77,16 @@ module RubyNext
|
|
80
77
|
end
|
81
78
|
|
82
79
|
def optparser
|
83
|
-
@optparser ||=
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
@print_help = true
|
94
|
-
end
|
80
|
+
@optparser ||= OptionParser.new do |opts|
|
81
|
+
opts.banner = "Usage: ruby-next COMMAND [options]"
|
82
|
+
|
83
|
+
opts.on("-v", "--version", "Print version") do
|
84
|
+
$stdout.puts RubyNext::VERSION
|
85
|
+
exit 0
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("-h", "--help", "Print help") do
|
89
|
+
@print_help = true
|
95
90
|
end
|
96
91
|
end
|
97
92
|
end
|
@@ -10,9 +10,45 @@ module RubyNext
|
|
10
10
|
class Nextify < Base
|
11
11
|
using RubyNext
|
12
12
|
|
13
|
-
|
13
|
+
class Stats
|
14
|
+
def initialize
|
15
|
+
@started_at = ::Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
16
|
+
@files = 0
|
17
|
+
@scans = 0
|
18
|
+
@transpiled_files = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def file!
|
22
|
+
@files += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def scan!
|
26
|
+
@scans += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def transpiled!
|
30
|
+
@transpiled_files += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def report
|
34
|
+
<<~TXT
|
35
|
+
|
36
|
+
Files processed: #{@files}
|
37
|
+
Total scans: #{@scans}
|
38
|
+
Files transpiled: #{@transpiled_files}
|
39
|
+
|
40
|
+
Completed in #{::Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at}s
|
41
|
+
TXT
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :lib_path, :paths, :out_path, :min_version, :single_version, :specified_rewriters, :overwrite
|
46
|
+
alias_method :overwrite?, :overwrite
|
47
|
+
attr_reader :stats
|
14
48
|
|
15
49
|
def run
|
50
|
+
@stats = Stats.new
|
51
|
+
|
16
52
|
log "RubyNext core strategy: #{RubyNext::Core.strategy}"
|
17
53
|
log "RubyNext transpile mode: #{RubyNext::Language.mode}"
|
18
54
|
|
@@ -21,16 +57,24 @@ module RubyNext
|
|
21
57
|
@min_version ||= MIN_SUPPORTED_VERSION
|
22
58
|
|
23
59
|
paths.each do |path|
|
60
|
+
stats.file!
|
61
|
+
|
24
62
|
contents = File.read(path)
|
25
63
|
transpile path, contents
|
26
64
|
end
|
65
|
+
|
66
|
+
ensure_rbnext!
|
67
|
+
|
68
|
+
log stats.report
|
27
69
|
end
|
28
70
|
|
29
71
|
def parse!(args)
|
30
72
|
print_help = false
|
31
73
|
print_rewriters = false
|
32
74
|
rewriter_names = []
|
75
|
+
custom_rewriters = []
|
33
76
|
@single_version = false
|
77
|
+
@overwrite = false
|
34
78
|
|
35
79
|
optparser = base_parser do |opts|
|
36
80
|
opts.banner = "Usage: ruby-next nextify DIRECTORY_OR_FILE [options]"
|
@@ -47,11 +91,17 @@ module RubyNext
|
|
47
91
|
@single_version = true
|
48
92
|
end
|
49
93
|
|
94
|
+
opts.on("--overwrite", "Overwrite original file") do
|
95
|
+
@overwrite = true
|
96
|
+
end
|
97
|
+
|
50
98
|
opts.on("--edge", "Enable edge (master) Ruby features") do |val|
|
99
|
+
ENV["RUBY_NEXT_EDGE"] = val.to_s
|
51
100
|
require "ruby-next/language/rewriters/edge"
|
52
101
|
end
|
53
102
|
|
54
103
|
opts.on("--proposed", "Enable proposed/experimental Ruby features") do |val|
|
104
|
+
ENV["RUBY_NEXT_PROPOSED"] = val.to_s
|
55
105
|
require "ruby-next/language/rewriters/proposed"
|
56
106
|
end
|
57
107
|
|
@@ -74,6 +124,10 @@ module RubyNext
|
|
74
124
|
rewriter_names << val
|
75
125
|
end
|
76
126
|
|
127
|
+
opts.on("--import-rewriter=REWRITERS...", "Specify paths to load custom rewritiers") do |val|
|
128
|
+
custom_rewriters << val
|
129
|
+
end
|
130
|
+
|
77
131
|
opts.on("-h", "--help", "Print help") do
|
78
132
|
print_help = true
|
79
133
|
end
|
@@ -88,9 +142,14 @@ module RubyNext
|
|
88
142
|
exit 0
|
89
143
|
end
|
90
144
|
|
145
|
+
# Load custom rewriters
|
146
|
+
custom_rewriters.each do |path|
|
147
|
+
Kernel.load path
|
148
|
+
end
|
149
|
+
|
91
150
|
if print_rewriters
|
92
151
|
Language.rewriters.each do |rewriter|
|
93
|
-
$stdout.puts "#{rewriter::NAME} (\"#{rewriter::SYNTAX_PROBE}\")"
|
152
|
+
$stdout.puts "#{rewriter::NAME} (\"#{rewriter::SYNTAX_PROBE}\")#{rewriter.unsupported_syntax? ? " (unsupported)" : ""}"
|
94
153
|
end
|
95
154
|
exit 0
|
96
155
|
end
|
@@ -117,6 +176,11 @@ module RubyNext
|
|
117
176
|
end
|
118
177
|
end
|
119
178
|
|
179
|
+
if overwrite? && !single_version?
|
180
|
+
$stdout.puts "--overwrite only works with --single-version or explcit rewritires specified (via --rewrite)"
|
181
|
+
exit 2
|
182
|
+
end
|
183
|
+
|
120
184
|
@paths =
|
121
185
|
if File.directory?(lib_path)
|
122
186
|
Dir[File.join(lib_path, "**/*.rb")]
|
@@ -130,6 +194,8 @@ module RubyNext
|
|
130
194
|
private
|
131
195
|
|
132
196
|
def transpile(path, contents, version: min_version)
|
197
|
+
stats.scan!
|
198
|
+
|
133
199
|
rewriters = specified_rewriters || Language.rewriters.select { |rw| rw.unsupported_version?(version) }
|
134
200
|
|
135
201
|
context = Language::TransformContext.new
|
@@ -150,16 +216,25 @@ module RubyNext
|
|
150
216
|
transpile path, contents, version: version
|
151
217
|
rescue SyntaxError, StandardError => e
|
152
218
|
warn "Failed to transpile #{path}: #{e.class} — #{e.message}"
|
219
|
+
warn e.backtrace.take(10).join("\n") if ENV["RUBY_NEXT_DEBUG"] == "1"
|
153
220
|
exit 1
|
154
221
|
end
|
155
222
|
|
156
223
|
def save(contents, path, version)
|
224
|
+
stats.transpiled!
|
225
|
+
|
157
226
|
return $stdout.puts(contents) if stdout?
|
158
227
|
|
159
228
|
paths = [Pathname.new(path).relative_path_from(Pathname.new(lib_path))]
|
160
229
|
|
161
230
|
paths.unshift(version.segments[0..1].join(".")) unless single_version?
|
162
231
|
|
232
|
+
if overwrite?
|
233
|
+
overwrite_file_content!(path: path, contents: contents)
|
234
|
+
|
235
|
+
return
|
236
|
+
end
|
237
|
+
|
163
238
|
next_path =
|
164
239
|
if next_dir_path.end_with?(".rb")
|
165
240
|
out_path
|
@@ -176,6 +251,14 @@ module RubyNext
|
|
176
251
|
log "Generated: #{next_path}"
|
177
252
|
end
|
178
253
|
|
254
|
+
def overwrite_file_content!(path:, contents:)
|
255
|
+
unless CLI.dry_run?
|
256
|
+
File.write(path, contents)
|
257
|
+
end
|
258
|
+
|
259
|
+
log "Overwritten: #{path}"
|
260
|
+
end
|
261
|
+
|
179
262
|
def remove_rbnext!
|
180
263
|
return if CLI.dry_run? || stdout?
|
181
264
|
|
@@ -185,8 +268,21 @@ module RubyNext
|
|
185
268
|
FileUtils.rm_r(next_dir_path)
|
186
269
|
end
|
187
270
|
|
271
|
+
def ensure_rbnext!
|
272
|
+
return if CLI.dry_run? || stdout?
|
273
|
+
|
274
|
+
return if File.directory?(next_dir_path)
|
275
|
+
|
276
|
+
return if next_dir_path.end_with?(".rb")
|
277
|
+
|
278
|
+
return if overwrite?
|
279
|
+
|
280
|
+
FileUtils.mkdir_p next_dir_path
|
281
|
+
File.write(File.join(next_dir_path, ".keep"), "")
|
282
|
+
end
|
283
|
+
|
188
284
|
def next_dir_path
|
189
|
-
@next_dir_path ||=
|
285
|
+
@next_dir_path ||= out_path || File.join(lib_path, RUBY_NEXT_DIR)
|
190
286
|
end
|
191
287
|
|
192
288
|
def stdout?
|
data/lib/ruby-next/config.rb
CHANGED
@@ -10,17 +10,19 @@ module RubyNext
|
|
10
10
|
# Defines last minor version for every major version
|
11
11
|
LAST_MINOR_VERSIONS = {
|
12
12
|
2 => 8, # 2.8 is required for backward compatibility: some gems already uses it
|
13
|
-
3 =>
|
13
|
+
3 => 4
|
14
14
|
}.freeze
|
15
15
|
|
16
|
-
LATEST_VERSION = [3,
|
16
|
+
LATEST_VERSION = [3, 4].freeze
|
17
17
|
|
18
18
|
# A virtual version number used for proposed features
|
19
19
|
NEXT_VERSION = "1995.next.0"
|
20
20
|
|
21
21
|
class << self
|
22
|
-
# TruffleRuby claims
|
23
|
-
|
22
|
+
# TruffleRuby claims its RUBY_VERSION to be X.Y while not supporting all the features
|
23
|
+
# Currently (23.0.1), it still doesn't support pattern matching, although claims to be "like 3.1".
|
24
|
+
# So, we fallback to 2.6.5 (since we cannot use 2.7).
|
25
|
+
if defined?(TruffleRuby)
|
24
26
|
def current_ruby_version
|
25
27
|
"2.6.5"
|
26
28
|
end
|
@@ -30,6 +32,15 @@ module RubyNext
|
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
35
|
+
# Returns true if we want to use edge syntax
|
36
|
+
def edge_syntax?
|
37
|
+
%w[y true 1].include?(ENV["RUBY_NEXT_EDGE"])
|
38
|
+
end
|
39
|
+
|
40
|
+
def proposed_syntax?
|
41
|
+
%w[y true 1].include?(ENV["RUBY_NEXT_PROPOSED"])
|
42
|
+
end
|
43
|
+
|
33
44
|
def next_ruby_version(version = current_ruby_version)
|
34
45
|
return if version == Gem::Version.new(NEXT_VERSION)
|
35
46
|
|
@@ -46,5 +57,21 @@ module RubyNext
|
|
46
57
|
|
47
58
|
Gem::Version.new(nxt)
|
48
59
|
end
|
60
|
+
|
61
|
+
# Load transpile settings from the RC file (nextify command flags)
|
62
|
+
def load_from_rc(path = ".rbnextrc")
|
63
|
+
return unless File.exist?(path)
|
64
|
+
|
65
|
+
require "yaml"
|
66
|
+
|
67
|
+
args = YAML.load_file(path)&.fetch("nextify", "")&.lines&.flat_map { |line| line.chomp.split(/\s+/) }
|
68
|
+
|
69
|
+
ENV["RUBY_NEXT_EDGE"] ||= "true" if args.delete("--edge")
|
70
|
+
ENV["RUBY_NEXT_PROPOSED"] ||= "true" if args.delete("--proposed")
|
71
|
+
ENV["RUBY_NEXT_TRANSPILE_MODE"] ||= "rewrite" if args.delete("--transpile-mode=rewrite")
|
72
|
+
ENV["RUBY_NEXT_TRANSPILE_MODE"] ||= "ast" if args.delete("--transpile-mode=ast")
|
73
|
+
end
|
49
74
|
end
|
75
|
+
|
76
|
+
load_from_rc
|
50
77
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The code below originates from https://github.com/saturnflyer/polyfill-data
|
4
|
+
|
5
|
+
if !Object.const_defined?(:Data) || !Data.respond_to?(:define)
|
6
|
+
|
7
|
+
# Drop legacy Data class
|
8
|
+
begin
|
9
|
+
Object.send(:remove_const, :Data)
|
10
|
+
rescue
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
class Data < Object
|
15
|
+
using RubyNext
|
16
|
+
|
17
|
+
class << self
|
18
|
+
undef_method :new
|
19
|
+
attr_reader :members
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.define(*args, &block)
|
23
|
+
raise ArgumentError if args.any?(/=/)
|
24
|
+
if block
|
25
|
+
mod = Module.new
|
26
|
+
mod.define_singleton_method(:_) do |klass|
|
27
|
+
klass.class_eval(&block)
|
28
|
+
end
|
29
|
+
arity_converter = mod.method(:_)
|
30
|
+
end
|
31
|
+
klass = ::Class.new(self)
|
32
|
+
|
33
|
+
klass.instance_variable_set(:@members, args.map(&:to_sym).freeze)
|
34
|
+
|
35
|
+
klass.define_singleton_method(:new) do |*new_args, **new_kwargs, &block|
|
36
|
+
init_kwargs = if new_args.any?
|
37
|
+
raise ArgumentError, "unknown arguments #{new_args[members.size..].join(", ")}" if new_args.size > members.size
|
38
|
+
members.take(new_args.size).zip(new_args).to_h
|
39
|
+
else
|
40
|
+
new_kwargs
|
41
|
+
end
|
42
|
+
|
43
|
+
allocate.tap do |instance|
|
44
|
+
instance.send(:initialize, **init_kwargs, &block)
|
45
|
+
end.freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
class << klass
|
49
|
+
alias_method :[], :new
|
50
|
+
undef_method :define
|
51
|
+
end
|
52
|
+
|
53
|
+
args.each do |arg|
|
54
|
+
if klass.method_defined?(arg)
|
55
|
+
raise ArgumentError, "duplicate member #{arg}"
|
56
|
+
end
|
57
|
+
klass.define_method(arg) do
|
58
|
+
@attributes[arg]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if arity_converter
|
63
|
+
klass.class_eval(&arity_converter)
|
64
|
+
end
|
65
|
+
|
66
|
+
klass
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.inherited(subclass)
|
70
|
+
subclass.instance_variable_set(:@members, members)
|
71
|
+
end
|
72
|
+
|
73
|
+
def members
|
74
|
+
self.class.members
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(**kwargs)
|
78
|
+
kwargs_size = kwargs.size
|
79
|
+
members_size = members.size
|
80
|
+
|
81
|
+
if kwargs_size > members_size
|
82
|
+
extras = kwargs.except(*members).keys
|
83
|
+
|
84
|
+
if extras.size > 1
|
85
|
+
raise ArgumentError, "unknown keywords: #{extras.map { ":#{_1}" }.join(", ")}"
|
86
|
+
else
|
87
|
+
raise ArgumentError, "unknown keyword: :#{extras.first}"
|
88
|
+
end
|
89
|
+
elsif kwargs_size < members_size
|
90
|
+
missing = members.select { |k| !kwargs.include?(k) }
|
91
|
+
|
92
|
+
if missing.size > 1
|
93
|
+
raise ArgumentError, "missing keywords: #{missing.map { ":#{_1}" }.join(", ")}"
|
94
|
+
else
|
95
|
+
raise ArgumentError, "missing keyword: :#{missing.first}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@attributes = members.map { |m| [m, kwargs[m]] }.to_h
|
100
|
+
end
|
101
|
+
|
102
|
+
def deconstruct
|
103
|
+
@attributes.values
|
104
|
+
end
|
105
|
+
|
106
|
+
def deconstruct_keys(array)
|
107
|
+
raise TypeError unless array.is_a?(Array) || array.nil?
|
108
|
+
return @attributes if array&.first.nil?
|
109
|
+
|
110
|
+
@attributes.slice(*array)
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_h(&block)
|
114
|
+
@attributes.to_h(&block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def hash
|
118
|
+
to_h.hash
|
119
|
+
end
|
120
|
+
|
121
|
+
def eql?(other)
|
122
|
+
self.class == other.class && hash == other.hash
|
123
|
+
end
|
124
|
+
|
125
|
+
def ==(other)
|
126
|
+
self.class == other.class && to_h == other.to_h
|
127
|
+
end
|
128
|
+
|
129
|
+
def inspect
|
130
|
+
attribute_markers = @attributes.map do |key, value|
|
131
|
+
insect_key = key.to_s.start_with?("@") ? ":#{key}" : key
|
132
|
+
"#{insect_key}=#{value}"
|
133
|
+
end.join(", ")
|
134
|
+
|
135
|
+
display = ["data", self.class.name, attribute_markers].compact.join(" ")
|
136
|
+
|
137
|
+
"#<#{display}>"
|
138
|
+
end
|
139
|
+
alias_method :to_s, :inspect
|
140
|
+
|
141
|
+
def with(**kwargs)
|
142
|
+
return self if kwargs.empty?
|
143
|
+
|
144
|
+
self.class.new(**@attributes.merge(kwargs))
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def marshal_dump
|
150
|
+
@attributes
|
151
|
+
end
|
152
|
+
|
153
|
+
def marshal_load(attributes)
|
154
|
+
@attributes = attributes
|
155
|
+
freeze
|
156
|
+
end
|
157
|
+
|
158
|
+
def initialize_copy(source)
|
159
|
+
super.freeze
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RubyNext::Core.patch MatchData, method: :deconstruct_keys, version: "3.2" do
|
4
|
+
<<-'RUBY'
|
5
|
+
def deconstruct_keys(keys)
|
6
|
+
raise TypeError, "wrong argument type #{keys.class} (expected Array)" if keys && !keys.is_a?(Array)
|
7
|
+
|
8
|
+
captured = named_captures.transform_keys!(&:to_sym)
|
9
|
+
return captured if keys.nil?
|
10
|
+
|
11
|
+
return {} if keys.size > captured.size
|
12
|
+
|
13
|
+
keys.each_with_object({}) do |k, acc|
|
14
|
+
raise TypeError, "wrong argument type #{k.class} (expected Symbol)" unless Symbol === k
|
15
|
+
return acc unless captured.key?(k)
|
16
|
+
acc[k] = self[k]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
end
|