ruby-next-core 0.5.3 → 0.9.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 +65 -0
- data/README.md +153 -40
- data/bin/transform +12 -5
- data/lib/ruby-next.rb +3 -2
- data/lib/ruby-next/cli.rb +17 -2
- data/lib/ruby-next/commands/base.rb +15 -2
- data/lib/ruby-next/commands/core_ext.rb +5 -3
- data/lib/ruby-next/commands/nextify.rb +47 -20
- data/lib/ruby-next/core.rb +13 -2
- data/lib/ruby-next/core/time/ceil.rb +2 -1
- data/lib/ruby-next/core_ext.rb +1 -1
- data/lib/ruby-next/language.rb +15 -4
- data/lib/ruby-next/language/edge.rb +9 -0
- data/lib/ruby-next/language/eval.rb +10 -8
- data/lib/ruby-next/language/parser.rb +6 -2
- data/lib/ruby-next/language/proposed.rb +6 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +10 -8
- data/lib/ruby-next/language/rewriters/base.rb +1 -5
- data/lib/ruby-next/language/rewriters/endless_method.rb +40 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +0 -6
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +2 -1
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +44 -0
- data/lib/ruby-next/language/rewriters/runtime.rb +6 -0
- data/lib/ruby-next/language/rewriters/runtime/dir.rb +32 -0
- data/lib/ruby-next/language/runtime.rb +3 -2
- data/lib/ruby-next/language/setup.rb +21 -2
- data/lib/ruby-next/logging.rb +1 -1
- data/lib/ruby-next/rubocop.rb +70 -1
- data/lib/ruby-next/utils.rb +30 -0
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +4 -0
- metadata +12 -6
data/lib/ruby-next/cli.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "ruby-next"
|
4
|
-
require "ruby-next/language"
|
5
4
|
|
6
5
|
require "ruby-next/commands/base"
|
7
6
|
require "ruby-next/commands/nextify"
|
@@ -11,10 +10,14 @@ module RubyNext
|
|
11
10
|
# Command line interface for RubyNext
|
12
11
|
class CLI
|
13
12
|
class << self
|
14
|
-
attr_accessor :verbose
|
13
|
+
attr_accessor :verbose, :dry_run
|
14
|
+
|
15
|
+
alias verbose? verbose
|
16
|
+
alias dry_run? dry_run
|
15
17
|
end
|
16
18
|
|
17
19
|
self.verbose = false
|
20
|
+
self.dry_run = false
|
18
21
|
|
19
22
|
COMMANDS = {
|
20
23
|
"nextify" => Commands::Nextify,
|
@@ -37,6 +40,8 @@ module RubyNext
|
|
37
40
|
|
38
41
|
args.delete(command)
|
39
42
|
|
43
|
+
args.unshift(*load_args_from_rc(command))
|
44
|
+
|
40
45
|
COMMANDS.fetch(command) do
|
41
46
|
raise "Unknown command: #{command}. Available commands: #{COMMANDS.keys.join(",")}"
|
42
47
|
end.run(args)
|
@@ -90,5 +95,15 @@ module RubyNext
|
|
90
95
|
end
|
91
96
|
end
|
92
97
|
end
|
98
|
+
|
99
|
+
def load_args_from_rc(command)
|
100
|
+
return [] unless File.file?(".rbnextrc")
|
101
|
+
|
102
|
+
require "yaml"
|
103
|
+
command_args = YAML.load_file(".rbnextrc")[command]
|
104
|
+
return [] unless command_args
|
105
|
+
|
106
|
+
command_args.lines.flat_map { |line| line.chomp.split(/\s+/) }
|
107
|
+
end
|
93
108
|
end
|
94
109
|
end
|
@@ -11,6 +11,9 @@ module RubyNext
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
attr_reader :dry_run
|
15
|
+
alias dry_run? dry_run
|
16
|
+
|
14
17
|
def initialize(args)
|
15
18
|
parse! args
|
16
19
|
end
|
@@ -24,8 +27,13 @@ module RubyNext
|
|
24
27
|
end
|
25
28
|
|
26
29
|
def log(msg)
|
27
|
-
return unless CLI.verbose
|
28
|
-
|
30
|
+
return unless CLI.verbose?
|
31
|
+
|
32
|
+
if CLI.dry_run?
|
33
|
+
$stdout.puts "[DRY RUN] #{msg}"
|
34
|
+
else
|
35
|
+
$stdout.puts msg
|
36
|
+
end
|
29
37
|
end
|
30
38
|
|
31
39
|
def base_parser
|
@@ -35,6 +43,11 @@ module RubyNext
|
|
35
43
|
opts.on("-V", "Turn on verbose mode") do
|
36
44
|
CLI.verbose = true
|
37
45
|
end
|
46
|
+
|
47
|
+
opts.on("--dry-run", "Print verbose output without generating files") do
|
48
|
+
CLI.dry_run = true
|
49
|
+
CLI.verbose = true
|
50
|
+
end
|
38
51
|
end
|
39
52
|
end
|
40
53
|
end
|
@@ -69,7 +69,7 @@ module RubyNext
|
|
69
69
|
RubyNext::Core.patches.extensions
|
70
70
|
.values
|
71
71
|
.flatten
|
72
|
-
.
|
72
|
+
.select do |patch|
|
73
73
|
next if min_version && Gem::Version.new(patch.version) <= min_version
|
74
74
|
next if filter && !filter.match?(patch.name)
|
75
75
|
true
|
@@ -136,8 +136,10 @@ module RubyNext
|
|
136
136
|
|
137
137
|
return $stdout.puts(contents) if out_path == "stdout"
|
138
138
|
|
139
|
-
|
140
|
-
|
139
|
+
unless CLI.dry_run?
|
140
|
+
FileUtils.mkdir_p File.dirname(out_path)
|
141
|
+
File.write(out_path, contents)
|
142
|
+
end
|
141
143
|
|
142
144
|
log "Generated: #{out_path}"
|
143
145
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "fileutils"
|
4
4
|
require "pathname"
|
5
5
|
|
6
|
+
require "ruby-next/language"
|
7
|
+
|
6
8
|
module RubyNext
|
7
9
|
module Commands
|
8
10
|
class Nextify < Base
|
@@ -12,6 +14,10 @@ module RubyNext
|
|
12
14
|
|
13
15
|
def run
|
14
16
|
log "RubyNext core strategy: #{RubyNext::Core.strategy}"
|
17
|
+
log "RubyNext transpile mode: #{RubyNext::Language.mode}"
|
18
|
+
|
19
|
+
remove_rbnext!
|
20
|
+
|
15
21
|
paths.each do |path|
|
16
22
|
contents = File.read(path)
|
17
23
|
transpile path, contents
|
@@ -38,9 +44,12 @@ module RubyNext
|
|
38
44
|
@single_version = true
|
39
45
|
end
|
40
46
|
|
41
|
-
opts.on("--
|
42
|
-
require "ruby-next/language/
|
43
|
-
|
47
|
+
opts.on("--edge", "Enable edge (master) Ruby features") do |val|
|
48
|
+
require "ruby-next/language/edge"
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("--proposed", "Enable proposed/experimental Ruby features") do |val|
|
52
|
+
require "ruby-next/language/proposed"
|
44
53
|
end
|
45
54
|
|
46
55
|
opts.on(
|
@@ -59,6 +68,8 @@ module RubyNext
|
|
59
68
|
end
|
60
69
|
end
|
61
70
|
|
71
|
+
optparser.parse!(args)
|
72
|
+
|
62
73
|
@lib_path = args[0]
|
63
74
|
|
64
75
|
if print_help
|
@@ -67,12 +78,11 @@ module RubyNext
|
|
67
78
|
end
|
68
79
|
|
69
80
|
unless lib_path&.then(&File.method(:exist?))
|
81
|
+
$stdout.puts "Path not found: #{lib_path}"
|
70
82
|
$stdout.puts optparser.help
|
71
83
|
exit 2
|
72
84
|
end
|
73
85
|
|
74
|
-
optparser.parse!(args)
|
75
|
-
|
76
86
|
@paths =
|
77
87
|
if File.directory?(lib_path)
|
78
88
|
Dir[File.join(lib_path, "**/*.rb")]
|
@@ -89,7 +99,13 @@ module RubyNext
|
|
89
99
|
rewriters = Language.rewriters.select { |rw| rw.unsupported_version?(version) }
|
90
100
|
|
91
101
|
context = Language::TransformContext.new
|
92
|
-
|
102
|
+
|
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
|
93
109
|
|
94
110
|
return unless context.dirty?
|
95
111
|
|
@@ -106,34 +122,45 @@ module RubyNext
|
|
106
122
|
end
|
107
123
|
|
108
124
|
def save(contents, path, version)
|
109
|
-
return $stdout.puts(contents) if
|
125
|
+
return $stdout.puts(contents) if stdout?
|
110
126
|
|
111
127
|
paths = [Pathname.new(path).relative_path_from(Pathname.new(lib_path))]
|
112
128
|
|
113
129
|
paths.unshift(version.segments[0..1].join(".")) unless single_version?
|
114
130
|
|
115
131
|
next_path =
|
116
|
-
if
|
117
|
-
|
118
|
-
out_path
|
119
|
-
else
|
120
|
-
File.join(out_path, *paths)
|
121
|
-
end
|
132
|
+
if next_dir_path.end_with?(".rb")
|
133
|
+
out_path
|
122
134
|
else
|
123
|
-
File.join(
|
124
|
-
lib_path,
|
125
|
-
RUBY_NEXT_DIR,
|
126
|
-
*paths
|
127
|
-
)
|
135
|
+
File.join(next_dir_path, *paths)
|
128
136
|
end
|
129
137
|
|
130
|
-
|
138
|
+
unless CLI.dry_run?
|
139
|
+
FileUtils.mkdir_p File.dirname(next_path)
|
131
140
|
|
132
|
-
|
141
|
+
File.write(next_path, contents)
|
142
|
+
end
|
133
143
|
|
134
144
|
log "Generated: #{next_path}"
|
135
145
|
end
|
136
146
|
|
147
|
+
def remove_rbnext!
|
148
|
+
return if CLI.dry_run? || stdout?
|
149
|
+
|
150
|
+
return unless File.directory?(next_dir_path)
|
151
|
+
|
152
|
+
log "Remove old files: #{next_dir_path}"
|
153
|
+
FileUtils.rm_r(next_dir_path)
|
154
|
+
end
|
155
|
+
|
156
|
+
def next_dir_path
|
157
|
+
@next_dir_path ||= (out_path || File.join(lib_path, RUBY_NEXT_DIR))
|
158
|
+
end
|
159
|
+
|
160
|
+
def stdout?
|
161
|
+
out_path == "stdout"
|
162
|
+
end
|
163
|
+
|
137
164
|
alias single_version? single_version
|
138
165
|
end
|
139
166
|
end
|
data/lib/ruby-next/core.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "set"
|
4
4
|
|
5
|
+
require_relative "utils"
|
6
|
+
|
5
7
|
module RubyNext
|
6
8
|
module Core
|
7
9
|
# Patch contains the extension implementation
|
@@ -95,7 +97,7 @@ module RubyNext
|
|
95
97
|
end
|
96
98
|
|
97
99
|
class << self
|
98
|
-
STRATEGIES = %i[refine core_ext].freeze
|
100
|
+
STRATEGIES = %i[refine core_ext backports].freeze
|
99
101
|
|
100
102
|
attr_reader :strategy
|
101
103
|
|
@@ -109,7 +111,11 @@ module RubyNext
|
|
109
111
|
end
|
110
112
|
|
111
113
|
def core_ext?
|
112
|
-
strategy == :core_ext
|
114
|
+
strategy == :core_ext || strategy == :backports
|
115
|
+
end
|
116
|
+
|
117
|
+
def backports?
|
118
|
+
strategy == :backports
|
113
119
|
end
|
114
120
|
|
115
121
|
def patch(*args, **kwargs, &block)
|
@@ -136,6 +142,8 @@ module RubyNext
|
|
136
142
|
end
|
137
143
|
end
|
138
144
|
|
145
|
+
require "backports/2.5" if RubyNext::Core.backports?
|
146
|
+
|
139
147
|
require_relative "core/kernel/then"
|
140
148
|
|
141
149
|
require_relative "core/proc/compose"
|
@@ -176,6 +184,9 @@ require_relative "core/struct/deconstruct_keys"
|
|
176
184
|
# Generate refinements
|
177
185
|
RubyNext.module_eval do
|
178
186
|
RubyNext::Core.patches.refined.each do |mod, patches|
|
187
|
+
# Only refine modules when supported
|
188
|
+
next unless mod.is_a?(Class) || RubyNext::Utils.refine_modules?
|
189
|
+
|
179
190
|
refine mod do
|
180
191
|
patches.each do |patch|
|
181
192
|
module_eval(patch.body, *patch.location)
|
data/lib/ruby-next/core_ext.rb
CHANGED
data/lib/ruby-next/language.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
gem "parser", ">= 2.
|
3
|
+
gem "ruby-next-parser", ">= 2.8.0.3"
|
4
4
|
gem "unparser", ">= 0.4.7"
|
5
5
|
|
6
6
|
require "set"
|
@@ -83,6 +83,14 @@ module RubyNext
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def runtime!
|
86
|
+
require "ruby-next/language/rewriters/runtime"
|
87
|
+
|
88
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0") && !defined?(Unparser::Emitter::CaseMatch)
|
89
|
+
RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since Unparser doesn't support 2.7 AST yet.\n" \
|
90
|
+
"See https://github.com/mbj/unparser/pull/142"
|
91
|
+
self.mode = :rewrite
|
92
|
+
end
|
93
|
+
|
86
94
|
@runtime = true
|
87
95
|
end
|
88
96
|
|
@@ -166,9 +174,12 @@ module RubyNext
|
|
166
174
|
require "ruby-next/language/rewriters/endless_range"
|
167
175
|
rewriters << Rewriters::EndlessRange
|
168
176
|
|
169
|
-
if ENV["
|
170
|
-
require "ruby-next/language/
|
171
|
-
|
177
|
+
if ENV["RUBY_NEXT_EDGE"] == "1"
|
178
|
+
require "ruby-next/language/edge"
|
179
|
+
end
|
180
|
+
|
181
|
+
if ENV["RUBY_NEXT_PROPOSED"] == "1"
|
182
|
+
require "ruby-next/language/proposed"
|
172
183
|
end
|
173
184
|
end
|
174
185
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Load edge Ruby features
|
4
|
+
|
5
|
+
require "ruby-next/language/rewriters/endless_method"
|
6
|
+
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::EndlessMethod
|
7
|
+
|
8
|
+
require "ruby-next/language/rewriters/right_hand_assignment"
|
9
|
+
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::RightHandAssignment
|
@@ -3,14 +3,16 @@
|
|
3
3
|
module RubyNext
|
4
4
|
module Language
|
5
5
|
module KernelEval
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
if Utils.refine_modules?
|
7
|
+
refine Kernel do
|
8
|
+
def eval(source, bind = nil, *args)
|
9
|
+
new_source = ::RubyNext::Language::Runtime.transform(
|
10
|
+
source,
|
11
|
+
using: bind&.receiver == TOPLEVEL_BINDING.receiver || bind&.receiver&.is_a?(Module)
|
12
|
+
)
|
13
|
+
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
14
|
+
super new_source, bind, *args
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "parser/
|
3
|
+
require "parser/rubynext"
|
4
4
|
|
5
5
|
module RubyNext
|
6
6
|
module Language
|
@@ -10,7 +10,7 @@ module RubyNext
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def parser
|
13
|
-
::Parser::
|
13
|
+
::Parser::RubyNext.new(Builder.new).tap do |prs|
|
14
14
|
prs.diagnostics.tap do |diagnostics|
|
15
15
|
diagnostics.all_errors_are_fatal = true
|
16
16
|
end
|
@@ -23,6 +23,8 @@ module RubyNext
|
|
23
23
|
end
|
24
24
|
|
25
25
|
parser.parse(buffer)
|
26
|
+
rescue ::Parser::SyntaxError => e
|
27
|
+
raise ::SyntaxError, e.message
|
26
28
|
end
|
27
29
|
|
28
30
|
def parse_with_comments(source, file = "(string)")
|
@@ -31,6 +33,8 @@ module RubyNext
|
|
31
33
|
end
|
32
34
|
|
33
35
|
parser.parse_with_comments(buffer)
|
36
|
+
rescue ::Parser::SyntaxError => e
|
37
|
+
raise ::SyntaxError, e.message
|
34
38
|
end
|
35
39
|
end
|
36
40
|
end
|
@@ -25,21 +25,23 @@ module RubyNext
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def on_send(node)
|
28
|
-
return unless node.children[2]&.type == :forwarded_args
|
28
|
+
return super(node) unless node.children[2]&.type == :forwarded_args
|
29
29
|
|
30
30
|
replace(node.children[2].loc.expression, "*#{REST}, &#{BLOCK}")
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
process(
|
33
|
+
node.updated(
|
34
|
+
nil,
|
35
|
+
[
|
36
|
+
*node.children[0..1],
|
37
|
+
*forwarded_args
|
38
|
+
]
|
39
|
+
)
|
38
40
|
)
|
39
41
|
end
|
40
42
|
|
41
43
|
def on_super(node)
|
42
|
-
return unless node.children[0]&.type == :forwarded_args
|
44
|
+
return super(node) unless node.children[0]&.type == :forwarded_args
|
43
45
|
|
44
46
|
replace(node.children[0].loc.expression, "*#{REST}, &#{BLOCK}")
|
45
47
|
|