ruby-next-core 0.11.0 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|