ruby-next-core 0.11.0 → 0.13.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 +33 -0
- data/README.md +17 -3
- data/lib/.rbnext/2.1/ruby-next/core.rb +206 -0
- data/lib/.rbnext/2.1/ruby-next/language.rb +227 -0
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +1 -1
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +1 -1
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +4 -4
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +1 -1
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +3 -3
- data/lib/.rbnext/2.3/ruby-next/utils.rb +1 -1
- data/lib/.rbnext/2.7/ruby-next/core.rb +3 -0
- data/lib/ruby-next/config.rb +6 -1
- data/lib/ruby-next/core/array/intersect.rb +9 -0
- data/lib/ruby-next/core/constants/frozen_error.rb +15 -0
- data/lib/ruby-next/core/enumerable/tally.rb +46 -7
- data/lib/ruby-next/core.rb +3 -0
- data/lib/ruby-next/core_ext.rb +1 -1
- data/lib/ruby-next/language/parser.rb +0 -3
- data/lib/ruby-next/language/rewriters/args_forward.rb +6 -2
- data/lib/ruby-next/language/rewriters/args_forward_leading.rb +1 -1
- data/lib/ruby-next/language/rewriters/method_reference.rb +1 -1
- data/lib/ruby-next/language/rewriters/numeric_literals.rb +41 -0
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +1 -1
- data/lib/ruby-next/language/rewriters/required_kwargs.rb +39 -0
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +42 -29
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +1 -1
- data/lib/ruby-next/language/setup.rb +3 -2
- data/lib/ruby-next/language/unparser.rb +3 -8
- data/lib/ruby-next/language.rb +50 -43
- data/lib/ruby-next/setup_self.rb +3 -1
- data/lib/ruby-next/version.rb +1 -1
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 671c0ebc75d7ba834da83c55556fc9ed752fbbd6869788fc02435aef89f087b8
|
4
|
+
data.tar.gz: cb17c7ca0aafa112b50431f97170cdc16caba65fdbbb2bff6ebed039f1e2e489
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0642f3a3353f99d8ed5e76c4b530c748b7fc712c2ed04ba8e840a853087b3d94299ade88b83fd0ea472223bd5ee61f8c80618f818987ae541d4dc291eff8f0c2
|
7
|
+
data.tar.gz: 326aaccb2ec27ca7a28a802f755fcfc44748a825283d82d19055afe3a84d1b42eb9a48d41d5025b9b5be33230f6f88681e16b88537770270245b0435f457d49e
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,38 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.13.1 (2021-09-27)
|
6
|
+
|
7
|
+
- Fix checking for realpath during $LOAD_PATH setup. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.13.0 (2021-09-27)
|
10
|
+
|
11
|
+
- Added `Enumerable#tally` with the resulting hash. ([@skryukov][])
|
12
|
+
|
13
|
+
- Added `Array#intersect?`. ([@skryukov][])
|
14
|
+
|
15
|
+
## 0.12.0 (2021-01-12)
|
16
|
+
|
17
|
+
- Added required keyword arguments rewriter. ([@palkan][])
|
18
|
+
|
19
|
+
Required kwargs were introduced in 2.1. Now we make them possible in 2.0.
|
20
|
+
|
21
|
+
- Added numeric literals rewriter. ([@palkan][])
|
22
|
+
|
23
|
+
Now it's possible to generate Ruby 2.0 compatible code from `2i + 1/3r`.
|
24
|
+
|
25
|
+
- Fixed several safe navigation (`&.`) bugs. ([@palkan][])
|
26
|
+
|
27
|
+
See [#68](https://github.com/ruby-next/ruby-next/issues/68) and [#69](https://github.com/ruby-next/ruby-next/issues/69).
|
28
|
+
|
29
|
+
## 0.11.1 (2020-12-28)
|
30
|
+
|
31
|
+
- Use separate _namespace_ for proposed features to avoid conflicts with new Ruby version. ([@palkan][])
|
32
|
+
|
33
|
+
Previously, we used the upcoming Ruby version number for proposed features (e.g., `3.0.0`), which broke
|
34
|
+
the load path setup, since transpiled files were not loaded anymore.
|
35
|
+
Now that's fixed by using a _virtual_ version number for proposals (`1995.next.0`).
|
36
|
+
|
5
37
|
## 0.11.0 (2020-12-24) 🎄
|
6
38
|
|
7
39
|
- Extended proposed shorthand Hash syntax to support kwargs as well. ([@palkan][])
|
@@ -259,3 +291,4 @@ p a #=> 1
|
|
259
291
|
[@palkan]: https://github.com/palkan
|
260
292
|
[backports]: https://github.com/marcandre/backports
|
261
293
|
[@sl4vr]: https://github.com/sl4vr
|
294
|
+
[@skryukov]: https://github.com/skryukov
|
data/README.md
CHANGED
@@ -21,8 +21,20 @@ That's why Ruby Next implements the `master` features as fast as possible.
|
|
21
21
|
|
22
22
|
Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Make all Rubies quack alike](https://evilmartians.com/chronicles/ruby-next-make-all-rubies-quack-alike).
|
23
23
|
|
24
|
-
<
|
25
|
-
<
|
24
|
+
<table style="border:none;">
|
25
|
+
<tr>
|
26
|
+
<td>
|
27
|
+
<a href="https://evilmartians.com/?utm_source=ruby-next">
|
28
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
|
29
|
+
</a>
|
30
|
+
</td>
|
31
|
+
<td>
|
32
|
+
<a href="http://www.digitalfukuoka.jp/topics/169">
|
33
|
+
<img src="http://www.digitalfukuoka.jp/javascripts/kcfinder/upload/images/excellence.jpg" width="200">
|
34
|
+
</a>
|
35
|
+
</td>
|
36
|
+
</tr>
|
37
|
+
</table>
|
26
38
|
|
27
39
|
## Posts
|
28
40
|
|
@@ -445,6 +457,8 @@ AllCops:
|
|
445
457
|
|
446
458
|
We currently provide support for Ruby 2.2, 2.3 and 2.4.
|
447
459
|
|
460
|
+
**NOTE:** By "support" here we mean using `ruby-next` CLI and runtime transpiling. Transpiled code may run on Ruby 2.0+.
|
461
|
+
|
448
462
|
Ruby Next itself relies on 2.5 features and contains polyfills only for version 2.5+ (and that won't change).
|
449
463
|
Thus, to make it work with <2.5 we need to backport some APIs ourselves.
|
450
464
|
|
@@ -508,7 +522,7 @@ require "ruby-next/language/runtime"
|
|
508
522
|
|
509
523
|
### Supported edge features
|
510
524
|
|
511
|
-
|
525
|
+
`Array#intersect?` ([#15198](https://bugs.ruby-lang.org/issues/15198))
|
512
526
|
|
513
527
|
### Supported proposed features
|
514
528
|
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
require "ruby-next/config"
|
6
|
+
require "ruby-next/utils"
|
7
|
+
|
8
|
+
module RubyNext
|
9
|
+
module Core
|
10
|
+
# Patch contains the extension implementation
|
11
|
+
# and meta information (e.g., Ruby version).
|
12
|
+
class Patch
|
13
|
+
attr_reader :refineables, :name, :mod, :method_name, :version, :body, :singleton, :core_ext, :supported, :native, :location
|
14
|
+
|
15
|
+
# Create a new patch for module/class (mod)
|
16
|
+
# with the specified uniq name
|
17
|
+
#
|
18
|
+
# `core_ext` defines the strategy for core extensions:
|
19
|
+
# - :patch — extend class directly
|
20
|
+
# - :prepend — extend class by prepending a module (e.g., when needs `super`)
|
21
|
+
def initialize(mod = nil, method: ::Kernel.raise(::ArgumentError, "missing keyword: method"), version: ::Kernel.raise(::ArgumentError, "missing keyword: version"), name: nil, supported: nil, native: nil, location: nil, refineable: mod, core_ext: :patch, singleton: nil)
|
22
|
+
@mod = mod
|
23
|
+
@method_name = method
|
24
|
+
@version = version
|
25
|
+
if method_name && mod
|
26
|
+
@supported = supported.nil? ? mod.method_defined?(method_name) : supported
|
27
|
+
# define whether running Ruby has a native implementation for this method
|
28
|
+
# for that, we check the source_location (which is nil for C defined methods)
|
29
|
+
@native = native.nil? ? (supported? && native_location?(mod.instance_method(method_name).source_location)) : native
|
30
|
+
end
|
31
|
+
@singleton = singleton
|
32
|
+
@refineables = Array(refineable)
|
33
|
+
@body = yield
|
34
|
+
@core_ext = core_ext
|
35
|
+
@location = location || build_location(caller_locations(1, 5))
|
36
|
+
@name = name || build_module_name
|
37
|
+
end
|
38
|
+
|
39
|
+
def prepend?
|
40
|
+
core_ext == :prepend
|
41
|
+
end
|
42
|
+
|
43
|
+
def core_ext?
|
44
|
+
!mod.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
alias supported? supported
|
48
|
+
alias native? native
|
49
|
+
alias singleton? singleton
|
50
|
+
|
51
|
+
def to_module
|
52
|
+
Module.new.tap do |ext|
|
53
|
+
ext.module_eval(body, *location)
|
54
|
+
|
55
|
+
RubyNext::Core.const_set(name, ext)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def build_module_name
|
62
|
+
mod_name = singleton? ? singleton.name : mod.name
|
63
|
+
camelized_method_name = method_name.to_s.split("_").map(&:capitalize).join
|
64
|
+
|
65
|
+
"#{mod_name}#{camelized_method_name}".gsub(/\W/, "")
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_location(trace_locations)
|
69
|
+
# The caller_locations behaviour depends on implementaion,
|
70
|
+
# e.g. in JRuby https://github.com/jruby/jruby/issues/6055
|
71
|
+
while trace_locations.first.label != "patch"
|
72
|
+
trace_locations.shift
|
73
|
+
end
|
74
|
+
|
75
|
+
trace_location = trace_locations[1]
|
76
|
+
|
77
|
+
[trace_location.absolute_path, trace_location.lineno + 2]
|
78
|
+
end
|
79
|
+
|
80
|
+
def native_location?(location)
|
81
|
+
location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core)/)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Registry for patches
|
86
|
+
class Patches
|
87
|
+
attr_reader :extensions, :refined
|
88
|
+
|
89
|
+
def initialize
|
90
|
+
@names = Set.new
|
91
|
+
@extensions = Hash.new { |h, k| h[k] = [] }
|
92
|
+
@refined = Hash.new { |h, k| h[k] = [] }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Register new patch
|
96
|
+
def <<(patch)
|
97
|
+
raise ArgumentError, "Patch already registered: #{patch.name}" if @names.include?(patch.name)
|
98
|
+
@names << patch.name
|
99
|
+
@extensions[patch.mod] << patch if patch.core_ext?
|
100
|
+
patch.refineables.each { |r| @refined[r] << patch } unless patch.native?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class << self
|
105
|
+
STRATEGIES = %i[refine core_ext backports].freeze
|
106
|
+
|
107
|
+
attr_reader :strategy
|
108
|
+
|
109
|
+
def strategy=(val)
|
110
|
+
raise ArgumentError, "Unknown strategy: #{val}. Available: #{STRATEGIES.join(",")}" unless STRATEGIES.include?(val)
|
111
|
+
@strategy = val
|
112
|
+
end
|
113
|
+
|
114
|
+
def refine?
|
115
|
+
strategy == :refine
|
116
|
+
end
|
117
|
+
|
118
|
+
def core_ext?
|
119
|
+
strategy == :core_ext || strategy == :backports
|
120
|
+
end
|
121
|
+
|
122
|
+
def backports?
|
123
|
+
strategy == :backports
|
124
|
+
end
|
125
|
+
|
126
|
+
def patch(*__rest__, &__block__)
|
127
|
+
patches << Patch.new(*__rest__, &__block__)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Inject `using RubyNext` at the top of the source code
|
131
|
+
def inject!(contents)
|
132
|
+
if contents.frozen?
|
133
|
+
contents = contents.sub(/^(\s*[^#\s].*)/, 'using RubyNext;\1')
|
134
|
+
else
|
135
|
+
contents.sub!(/^(\s*[^#\s].*)/, 'using RubyNext;\1')
|
136
|
+
end
|
137
|
+
contents
|
138
|
+
end
|
139
|
+
|
140
|
+
def patches
|
141
|
+
@patches ||= Patches.new
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Use refinements by default
|
146
|
+
self.strategy = ENV.fetch("RUBY_NEXT_CORE_STRATEGY", "refine").to_sym
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
require "backports/2.5" if RubyNext::Core.backports?
|
151
|
+
|
152
|
+
require "ruby-next/core/kernel/then"
|
153
|
+
|
154
|
+
require "ruby-next/core/proc/compose"
|
155
|
+
|
156
|
+
require "ruby-next/core/enumerable/tally"
|
157
|
+
require "ruby-next/core/enumerable/filter"
|
158
|
+
require "ruby-next/core/enumerable/filter_map"
|
159
|
+
|
160
|
+
require "ruby-next/core/enumerator/produce"
|
161
|
+
|
162
|
+
require "ruby-next/core/array/difference_union_intersection"
|
163
|
+
|
164
|
+
require "ruby-next/core/hash/merge"
|
165
|
+
|
166
|
+
require "ruby-next/core/string/split"
|
167
|
+
|
168
|
+
require "ruby-next/core/symbol/start_with"
|
169
|
+
require "ruby-next/core/symbol/end_with"
|
170
|
+
|
171
|
+
require "ruby-next/core/unboundmethod/bind_call"
|
172
|
+
|
173
|
+
require "ruby-next/core/time/floor"
|
174
|
+
require "ruby-next/core/time/ceil"
|
175
|
+
|
176
|
+
# Core extensions required for pattern matching
|
177
|
+
# Required for pattern matching with refinements
|
178
|
+
unless defined?(NoMatchingPatternError)
|
179
|
+
class NoMatchingPatternError < StandardError
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
require "ruby-next/core/constants/no_matching_pattern_error"
|
184
|
+
require "ruby-next/core/constants/frozen_error"
|
185
|
+
require "ruby-next/core/array/deconstruct"
|
186
|
+
require "ruby-next/core/hash/deconstruct_keys"
|
187
|
+
require "ruby-next/core/struct/deconstruct"
|
188
|
+
require "ruby-next/core/struct/deconstruct_keys"
|
189
|
+
|
190
|
+
require "ruby-next/core/hash/except"
|
191
|
+
|
192
|
+
require "ruby-next/core/array/intersect"
|
193
|
+
|
194
|
+
# Generate refinements
|
195
|
+
RubyNext.module_eval do
|
196
|
+
RubyNext::Core.patches.refined.each do |mod, patches|
|
197
|
+
# Only refine modules when supported
|
198
|
+
next unless mod.is_a?(Class) || RubyNext::Utils.refine_modules?
|
199
|
+
|
200
|
+
refine mod do
|
201
|
+
patches.each do |patch|
|
202
|
+
module_eval(patch.body, *patch.location)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
gem "ruby-next-parser", ">= 2.8.0.3"
|
4
|
+
gem "unparser", ">= 0.4.7"
|
5
|
+
|
6
|
+
require "set"
|
7
|
+
|
8
|
+
require "ruby-next"
|
9
|
+
|
10
|
+
module RubyNext
|
11
|
+
# Language module contains tools to transpile newer Ruby syntax
|
12
|
+
# into an older one.
|
13
|
+
#
|
14
|
+
# It works the following way:
|
15
|
+
# - Takes a Ruby source code as input
|
16
|
+
# - Generates the AST using the edge parser (via the `parser` gem)
|
17
|
+
# - Pass this AST through the list of processors (one feature = one processor)
|
18
|
+
# - Each processor may modify the AST
|
19
|
+
# - Generates a transpiled source code from the transformed AST (via the `unparser` gem)
|
20
|
+
module Language
|
21
|
+
using RubyNext
|
22
|
+
|
23
|
+
require "ruby-next/language/parser"
|
24
|
+
require "ruby-next/language/unparser"
|
25
|
+
|
26
|
+
RewriterNotFoundError = Class.new(StandardError)
|
27
|
+
|
28
|
+
class TransformContext
|
29
|
+
attr_reader :versions, :use_ruby_next
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
# Minimum supported RubyNext version
|
33
|
+
@min_version = MIN_SUPPORTED_VERSION
|
34
|
+
@dirty = false
|
35
|
+
@versions = Set.new
|
36
|
+
@use_ruby_next = false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Called by rewriter when it performs transfomrations
|
40
|
+
def track!(rewriter)
|
41
|
+
@dirty = true
|
42
|
+
versions << rewriter.class::MIN_SUPPORTED_VERSION
|
43
|
+
end
|
44
|
+
|
45
|
+
def use_ruby_next!
|
46
|
+
@use_ruby_next = true
|
47
|
+
end
|
48
|
+
|
49
|
+
alias use_ruby_next? use_ruby_next
|
50
|
+
|
51
|
+
def dirty?
|
52
|
+
@dirty == true
|
53
|
+
end
|
54
|
+
|
55
|
+
def min_version
|
56
|
+
versions.min
|
57
|
+
end
|
58
|
+
|
59
|
+
def sorted_versions
|
60
|
+
versions.to_a.sort
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
attr_accessor :rewriters
|
66
|
+
attr_reader :watch_dirs
|
67
|
+
|
68
|
+
attr_accessor :strategy
|
69
|
+
|
70
|
+
MODES = %i[rewrite ast].freeze
|
71
|
+
|
72
|
+
attr_reader :mode
|
73
|
+
|
74
|
+
def mode=(val)
|
75
|
+
raise ArgumentError, "Unknown mode: #{val}. Available: #{MODES.join(",")}" unless MODES.include?(val)
|
76
|
+
@mode = val
|
77
|
+
end
|
78
|
+
|
79
|
+
def rewrite?
|
80
|
+
mode == :rewrite?
|
81
|
+
end
|
82
|
+
|
83
|
+
def ast?
|
84
|
+
mode == :ast
|
85
|
+
end
|
86
|
+
|
87
|
+
def runtime!
|
88
|
+
require "ruby-next/language/rewriters/runtime"
|
89
|
+
|
90
|
+
@runtime = true
|
91
|
+
end
|
92
|
+
|
93
|
+
def runtime?
|
94
|
+
@runtime
|
95
|
+
end
|
96
|
+
|
97
|
+
def transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, context: TransformContext.new)
|
98
|
+
retried = 0
|
99
|
+
new_source = nil
|
100
|
+
begin
|
101
|
+
new_source =
|
102
|
+
if mode == :rewrite
|
103
|
+
rewrite(source, rewriters: rewriters, using: using, context: context)
|
104
|
+
else
|
105
|
+
regenerate(source, rewriters: rewriters, using: using, context: context)
|
106
|
+
end
|
107
|
+
rescue Unparser::UnknownNodeError => err
|
108
|
+
RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since the version of Unparser you use doesn't support some syntax yet: #{err.message}.\n" \
|
109
|
+
"Try upgrading the Unparser or set transpiling mode to \"rewrite\" in case you use some edge or experimental syntax."
|
110
|
+
self.mode = :rewrite
|
111
|
+
retried += 1
|
112
|
+
retry unless retried > 1
|
113
|
+
raise
|
114
|
+
end
|
115
|
+
|
116
|
+
return new_source unless RubyNext::Core.refine?
|
117
|
+
return new_source unless using && context.use_ruby_next?
|
118
|
+
|
119
|
+
Core.inject! new_source.dup
|
120
|
+
end
|
121
|
+
|
122
|
+
def transformable?(path)
|
123
|
+
watch_dirs.any? { |dir| path.start_with?(dir) }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Rewriters required for the current version
|
127
|
+
def current_rewriters
|
128
|
+
@current_rewriters ||= rewriters.select(&:unsupported_syntax?)
|
129
|
+
end
|
130
|
+
|
131
|
+
# This method guarantees that rewriters will be returned in order they defined in Language module
|
132
|
+
def select_rewriters(*names)
|
133
|
+
rewriters_delta = names - rewriters.map { |rewriter| rewriter::NAME }
|
134
|
+
if rewriters_delta.any?
|
135
|
+
raise RewriterNotFoundError, "Rewriters not found: #{rewriters_delta.join(",")}"
|
136
|
+
end
|
137
|
+
|
138
|
+
rewriters.select { |rewriter| names.include?(rewriter::NAME) }
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def regenerate(source, rewriters: ::Kernel.raise(::ArgumentError, "missing keyword: rewriters"), using: ::Kernel.raise(::ArgumentError, "missing keyword: using"), context: ::Kernel.raise(::ArgumentError, "missing keyword: context"))
|
144
|
+
parse_with_comments(source).then do |(ast, comments)|
|
145
|
+
rewriters.inject(ast) do |tree, rewriter|
|
146
|
+
rewriter.new(context).process(tree)
|
147
|
+
end.then do |new_ast|
|
148
|
+
next source unless context.dirty?
|
149
|
+
|
150
|
+
Unparser.unparse(new_ast, comments)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def rewrite(source, rewriters: ::Kernel.raise(::ArgumentError, "missing keyword: rewriters"), using: ::Kernel.raise(::ArgumentError, "missing keyword: using"), context: ::Kernel.raise(::ArgumentError, "missing keyword: context"))
|
156
|
+
rewriters.inject(source) do |src, rewriter|
|
157
|
+
buffer = Parser::Source::Buffer.new("<dynamic>")
|
158
|
+
buffer.source = src
|
159
|
+
|
160
|
+
rewriter.new(context).rewrite(buffer, parse(src))
|
161
|
+
end.then do |new_source|
|
162
|
+
next source unless context.dirty?
|
163
|
+
|
164
|
+
new_source
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
attr_writer :watch_dirs
|
169
|
+
end
|
170
|
+
|
171
|
+
self.rewriters = []
|
172
|
+
self.watch_dirs = %w[app lib spec test].map { |path| File.join(Dir.pwd, path) }
|
173
|
+
self.mode = ENV.fetch("RUBY_NEXT_TRANSPILE_MODE", "rewrite").to_sym
|
174
|
+
|
175
|
+
require "ruby-next/language/rewriters/base"
|
176
|
+
|
177
|
+
require "ruby-next/language/rewriters/squiggly_heredoc"
|
178
|
+
rewriters << Rewriters::SquigglyHeredoc
|
179
|
+
|
180
|
+
require "ruby-next/language/rewriters/safe_navigation"
|
181
|
+
rewriters << Rewriters::SafeNavigation
|
182
|
+
|
183
|
+
require "ruby-next/language/rewriters/numeric_literals"
|
184
|
+
rewriters << Rewriters::NumericLiterals
|
185
|
+
|
186
|
+
require "ruby-next/language/rewriters/required_kwargs"
|
187
|
+
rewriters << Rewriters::RequiredKwargs
|
188
|
+
|
189
|
+
require "ruby-next/language/rewriters/args_forward"
|
190
|
+
rewriters << Rewriters::ArgsForward
|
191
|
+
|
192
|
+
# Must be added after general args forward rewriter to become
|
193
|
+
# no-op in Ruby <2.7
|
194
|
+
require "ruby-next/language/rewriters/args_forward_leading"
|
195
|
+
rewriters << Rewriters::ArgsForwardLeading
|
196
|
+
|
197
|
+
require "ruby-next/language/rewriters/numbered_params"
|
198
|
+
rewriters << Rewriters::NumberedParams
|
199
|
+
|
200
|
+
require "ruby-next/language/rewriters/pattern_matching"
|
201
|
+
rewriters << Rewriters::PatternMatching
|
202
|
+
|
203
|
+
# Must be added after general pattern matching rewriter to become
|
204
|
+
# no-op in Ruby <2.7
|
205
|
+
require "ruby-next/language/rewriters/find_pattern"
|
206
|
+
rewriters << Rewriters::FindPattern
|
207
|
+
|
208
|
+
require "ruby-next/language/rewriters/in_pattern"
|
209
|
+
rewriters << Rewriters::InPattern
|
210
|
+
|
211
|
+
# Put endless range in the end, 'cause Parser fails to parse it in
|
212
|
+
# pattern matching
|
213
|
+
require "ruby-next/language/rewriters/endless_range"
|
214
|
+
rewriters << Rewriters::EndlessRange
|
215
|
+
|
216
|
+
require "ruby-next/language/rewriters/endless_method"
|
217
|
+
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::EndlessMethod
|
218
|
+
|
219
|
+
if ENV["RUBY_NEXT_EDGE"] == "1"
|
220
|
+
require "ruby-next/language/edge"
|
221
|
+
end
|
222
|
+
|
223
|
+
if ENV["RUBY_NEXT_PROPOSED"] == "1"
|
224
|
+
require "ruby-next/language/proposed"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -95,7 +95,7 @@ module RubyNext
|
|
95
95
|
exit 0
|
96
96
|
end
|
97
97
|
|
98
|
-
unless ((!
|
98
|
+
unless ((((__safe_lvar__ = lib_path) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.then(&File.method(:exist?)))
|
99
99
|
$stdout.puts "Path not found: #{lib_path}"
|
100
100
|
$stdout.puts optparser.help
|
101
101
|
exit 2
|
@@ -8,7 +8,7 @@ module RubyNext
|
|
8
8
|
def eval(source, bind = nil, *args)
|
9
9
|
new_source = ::RubyNext::Language::Runtime.transform(
|
10
10
|
source,
|
11
|
-
using: ((!
|
11
|
+
using: ((((__safe_lvar__ = bind) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.receiver) == TOPLEVEL_BINDING.receiver || ((((__safe_lvar__ = ((((__safe_lvar__ = bind) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.receiver)) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.is_a?(Module))
|
12
12
|
)
|
13
13
|
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
14
14
|
super new_source, bind, *args
|
@@ -112,19 +112,19 @@ module RubyNext
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def replace(range, ast)
|
115
|
-
((
|
115
|
+
((((__safe_lvar__ = @source_rewriter) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.replace(range, unparse(ast)))
|
116
116
|
end
|
117
117
|
|
118
118
|
def remove(range)
|
119
|
-
((
|
119
|
+
((((__safe_lvar__ = @source_rewriter) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.remove(range))
|
120
120
|
end
|
121
121
|
|
122
122
|
def insert_after(range, ast)
|
123
|
-
((
|
123
|
+
((((__safe_lvar__ = @source_rewriter) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.insert_after(range, unparse(ast)))
|
124
124
|
end
|
125
125
|
|
126
126
|
def insert_before(range, ast)
|
127
|
-
((
|
127
|
+
((((__safe_lvar__ = @source_rewriter) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.insert_before(range, unparse(ast)))
|
128
128
|
end
|
129
129
|
|
130
130
|
def unparse(ast)
|
@@ -55,7 +55,7 @@ module RubyNext
|
|
55
55
|
attr_reader :current_index
|
56
56
|
|
57
57
|
def index_arg?(node)
|
58
|
-
((
|
58
|
+
((((__safe_lvar__ = ((((__safe_lvar__ = current_index) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.children)) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.include?(node))
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -313,7 +313,7 @@ module RubyNext
|
|
313
313
|
remove(node.children[0].loc.expression)
|
314
314
|
|
315
315
|
node.children[1..-1].each.with_index do |clause, i|
|
316
|
-
if ((!
|
316
|
+
if ((((__safe_lvar__ = clause) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.type) == :in_pattern
|
317
317
|
# handle multiline clauses differently
|
318
318
|
if clause.loc.last_line > clause.children[0].loc.last_line + 1
|
319
319
|
height = clause.loc.last_line - clause.children[0].loc.last_line
|
@@ -344,7 +344,7 @@ module RubyNext
|
|
344
344
|
clauses = []
|
345
345
|
|
346
346
|
nodes.each do |clause|
|
347
|
-
if ((!
|
347
|
+
if ((((__safe_lvar__ = clause) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.type) == :in_pattern
|
348
348
|
clauses << build_when_clause(clause)
|
349
349
|
else
|
350
350
|
else_clause = process(clause)
|
@@ -353,7 +353,7 @@ module RubyNext
|
|
353
353
|
|
354
354
|
else_clause = (else_clause || no_matching_pattern).then do |node|
|
355
355
|
next node unless node.type == :empty_else
|
356
|
-
|
356
|
+
nil
|
357
357
|
end
|
358
358
|
|
359
359
|
clauses << else_clause
|
@@ -6,7 +6,7 @@ module RubyNext
|
|
6
6
|
|
7
7
|
if $LOAD_PATH.respond_to?(:resolve_feature_path)
|
8
8
|
def resolve_feature_path(feature)
|
9
|
-
((
|
9
|
+
((((__safe_lvar__ = $LOAD_PATH.resolve_feature_path(feature)) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.last)
|
10
10
|
rescue LoadError
|
11
11
|
end
|
12
12
|
else
|
@@ -181,6 +181,7 @@ unless defined?(NoMatchingPatternError)
|
|
181
181
|
end
|
182
182
|
|
183
183
|
require "ruby-next/core/constants/no_matching_pattern_error"
|
184
|
+
require "ruby-next/core/constants/frozen_error"
|
184
185
|
require "ruby-next/core/array/deconstruct"
|
185
186
|
require "ruby-next/core/hash/deconstruct_keys"
|
186
187
|
require "ruby-next/core/struct/deconstruct"
|
@@ -188,6 +189,8 @@ require "ruby-next/core/struct/deconstruct_keys"
|
|
188
189
|
|
189
190
|
require "ruby-next/core/hash/except"
|
190
191
|
|
192
|
+
require "ruby-next/core/array/intersect"
|
193
|
+
|
191
194
|
# Generate refinements
|
192
195
|
RubyNext.module_eval do
|
193
196
|
RubyNext::Core.patches.refined.each do |mod, patches|
|
data/lib/ruby-next/config.rb
CHANGED
@@ -15,6 +15,9 @@ module RubyNext
|
|
15
15
|
|
16
16
|
LATEST_VERSION = [3, 0].freeze
|
17
17
|
|
18
|
+
# A virtual version number used for proposed features
|
19
|
+
NEXT_VERSION = "1995.next.0"
|
20
|
+
|
18
21
|
class << self
|
19
22
|
# TruffleRuby claims it's 2.7.2 compatible but...
|
20
23
|
if defined?(TruffleRuby) && ::RUBY_VERSION =~ /^2\.7/
|
@@ -28,9 +31,11 @@ module RubyNext
|
|
28
31
|
end
|
29
32
|
|
30
33
|
def next_ruby_version(version = current_ruby_version)
|
34
|
+
return if version == Gem::Version.new(NEXT_VERSION)
|
35
|
+
|
31
36
|
major, minor = Gem::Version.new(version).segments.map(&:to_i)
|
32
37
|
|
33
|
-
return if major >= LATEST_VERSION.first && minor >= LATEST_VERSION.last
|
38
|
+
return Gem::Version.new(NEXT_VERSION) if major >= LATEST_VERSION.first && minor >= LATEST_VERSION.last
|
34
39
|
|
35
40
|
nxt =
|
36
41
|
if LAST_MINOR_VERSIONS[major] == minor
|