ruby-next-core 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +14 -4
  4. data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
  5. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
  6. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
  7. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
  8. data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
  10. data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
  11. data/lib/ruby-next.rb +8 -6
  12. data/lib/ruby-next/cli.rb +2 -2
  13. data/lib/ruby-next/commands/core_ext.rb +1 -1
  14. data/lib/ruby-next/core.rb +27 -21
  15. data/lib/ruby-next/core/array/deconstruct.rb +9 -9
  16. data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
  17. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
  18. data/lib/ruby-next/core/enumerable/filter.rb +8 -8
  19. data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
  20. data/lib/ruby-next/core/enumerable/tally.rb +7 -7
  21. data/lib/ruby-next/core/enumerator/produce.rb +12 -12
  22. data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
  23. data/lib/ruby-next/core/hash/except.rb +11 -0
  24. data/lib/ruby-next/core/hash/merge.rb +8 -8
  25. data/lib/ruby-next/core/kernel/then.rb +2 -2
  26. data/lib/ruby-next/core/proc/compose.rb +11 -11
  27. data/lib/ruby-next/core/string/split.rb +6 -6
  28. data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
  29. data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
  30. data/lib/ruby-next/core/symbol/end_with.rb +4 -4
  31. data/lib/ruby-next/core/symbol/start_with.rb +4 -4
  32. data/lib/ruby-next/core/time/ceil.rb +6 -6
  33. data/lib/ruby-next/core/time/floor.rb +4 -4
  34. data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
  35. data/lib/ruby-next/core_ext.rb +1 -1
  36. data/lib/ruby-next/language.rb +12 -1
  37. data/lib/ruby-next/language/parser.rb +0 -3
  38. data/lib/ruby-next/language/proposed.rb +3 -0
  39. data/lib/ruby-next/language/rewriters/args_forward.rb +23 -20
  40. data/lib/ruby-next/language/rewriters/base.rb +1 -1
  41. data/lib/ruby-next/language/rewriters/endless_method.rb +25 -3
  42. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  43. data/lib/ruby-next/language/rewriters/method_reference.rb +1 -1
  44. data/lib/ruby-next/language/rewriters/pattern_matching.rb +102 -12
  45. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +1 -1
  46. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  47. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  48. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  49. data/lib/ruby-next/language/unparser.rb +0 -14
  50. data/lib/ruby-next/logging.rb +1 -1
  51. data/lib/ruby-next/rubocop.rb +15 -9
  52. data/lib/ruby-next/setup_self.rb +22 -0
  53. data/lib/ruby-next/version.rb +1 -1
  54. data/lib/uby-next.rb +8 -4
  55. metadata +20 -7
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Utils
5
+ module_function
6
+
7
+ if $LOAD_PATH.respond_to?(:resolve_feature_path)
8
+ def resolve_feature_path(feature)
9
+ ((!$LOAD_PATH.resolve_feature_path(feature).nil?) || nil) && $LOAD_PATH.resolve_feature_path(feature).last
10
+ rescue LoadError
11
+ end
12
+ else
13
+ def resolve_feature_path(path)
14
+ if File.file?(relative = File.expand_path(path))
15
+ path = relative
16
+ end
17
+
18
+ path = "#{path}.rb" if File.extname(path).empty?
19
+
20
+ return path if Pathname.new(path).absolute?
21
+
22
+ $LOAD_PATH.find do |lp|
23
+ lpath = File.join(lp, path)
24
+ return File.realpath(lpath) if File.file?(lpath)
25
+ end
26
+ end
27
+ end
28
+
29
+ def source_with_lines(source, path)
30
+ source.lines.map.with_index do |line, i|
31
+ "#{(i + 1).to_s.rjust(4)}: #{line}"
32
+ end.tap do |lines|
33
+ lines.unshift " 0: # source: #{path}"
34
+ end
35
+ end
36
+
37
+ # Returns true if modules refinement is supported in current version
38
+ def refine_modules?
39
+ @refine_modules ||=
40
+ begin
41
+ # Make sure that including modules within refinements works
42
+ # See https://github.com/oracle/truffleruby/issues/2026
43
+ eval <<-RUBY, TOPLEVEL_BINDING, __FILE__, __LINE__ + 1
44
+ module RubyNext::Utils::A; end
45
+ class RubyNext::Utils::B
46
+ include RubyNext::Utils::A
47
+ end
48
+ using(Module.new do
49
+ refine RubyNext::Utils::A do
50
+ include(Module.new do
51
+ def i_am_refinement
52
+ "yes, you are!"
53
+ end
54
+ end)
55
+ end
56
+ end)
57
+ RubyNext::Utils::B.new.i_am_refinement
58
+ RUBY
59
+ true
60
+ rescue TypeError, NoMethodError
61
+ false
62
+ end
63
+ end
64
+ end
65
+ end
@@ -4,17 +4,18 @@ require "ruby-next/version"
4
4
 
5
5
  module RubyNext
6
6
  # Mininum Ruby version supported by RubyNext
7
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.5.0")
7
+ MIN_SUPPORTED_VERSION = Gem::Version.new("2.2.0")
8
8
 
9
9
  # Where to store transpiled files (relative from the project LOAD_PATH, usually `lib/`)
10
10
  RUBY_NEXT_DIR = ".rbnext"
11
11
 
12
12
  # Defines last minor version for every major version
13
13
  LAST_MINOR_VERSIONS = {
14
- 2 => 8
14
+ 2 => 8, # 2.8 is required for backward compatibility: some gems already uses it
15
+ 3 => 0
15
16
  }.freeze
16
17
 
17
- LATEST_VERSION = [2, 8].freeze
18
+ LATEST_VERSION = [3, 0].freeze
18
19
 
19
20
  class << self
20
21
  def next_version(version = RUBY_VERSION)
@@ -33,7 +34,8 @@ module RubyNext
33
34
  end
34
35
  end
35
36
 
36
- require_relative "ruby-next/core"
37
- require_relative "ruby-next/core_ext" if RubyNext::Core.core_ext?
38
- require_relative "ruby-next/logging"
37
+ require "ruby-next/setup_self"
38
+ require "ruby-next/core"
39
+ require "ruby-next/core_ext" if RubyNext::Core.core_ext?
40
+ require "ruby-next/logging"
39
41
  end
@@ -61,7 +61,7 @@ module RubyNext
61
61
  def maybe_print_help
62
62
  return unless @print_help
63
63
 
64
- STDOUT.puts optparser.help
64
+ $stdout.puts optparser.help
65
65
  exit 0
66
66
  end
67
67
 
@@ -85,7 +85,7 @@ module RubyNext
85
85
  opts.banner = "Usage: ruby-next COMMAND [options]"
86
86
 
87
87
  opts.on("-v", "--version", "Print version") do
88
- STDOUT.puts RubyNext::VERSION
88
+ $stdout.puts RubyNext::VERSION
89
89
  exit 0
90
90
  end
91
91
 
@@ -160,7 +160,7 @@ module RubyNext
160
160
  # remove empty lines
161
161
  new_src.gsub!(/^\s+$/, "")
162
162
  # remove traling blank lines
163
- new_src.gsub!(/\n\z/, "")
163
+ new_src.delete_suffix!("\n")
164
164
  new_src
165
165
  end
166
166
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "set"
4
4
 
5
- require_relative "utils"
5
+ require "ruby-next/utils"
6
6
 
7
7
  module RubyNext
8
8
  module Core
@@ -25,7 +25,7 @@ module RubyNext
25
25
  @supported = supported.nil? ? mod.method_defined?(method_name) : supported
26
26
  # define whether running Ruby has a native implementation for this method
27
27
  # for that, we check the source_location (which is nil for C defined methods)
28
- @native = native.nil? ? (supported? && mod.instance_method(method_name).source_location.nil?) : native
28
+ @native = native.nil? ? (supported? && native_location?(mod.instance_method(method_name).source_location)) : native
29
29
  end
30
30
  @singleton = singleton
31
31
  @refineables = Array(refineable)
@@ -75,6 +75,10 @@ module RubyNext
75
75
 
76
76
  [trace_location.absolute_path, trace_location.lineno + 2]
77
77
  end
78
+
79
+ def native_location?(location)
80
+ location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core)/)
81
+ end
78
82
  end
79
83
 
80
84
  # Registry for patches
@@ -144,29 +148,29 @@ end
144
148
 
145
149
  require "backports/2.5" if RubyNext::Core.backports?
146
150
 
147
- require_relative "core/kernel/then"
151
+ require "ruby-next/core/kernel/then"
148
152
 
149
- require_relative "core/proc/compose"
153
+ require "ruby-next/core/proc/compose"
150
154
 
151
- require_relative "core/enumerable/tally"
152
- require_relative "core/enumerable/filter"
153
- require_relative "core/enumerable/filter_map"
155
+ require "ruby-next/core/enumerable/tally"
156
+ require "ruby-next/core/enumerable/filter"
157
+ require "ruby-next/core/enumerable/filter_map"
154
158
 
155
- require_relative "core/enumerator/produce"
159
+ require "ruby-next/core/enumerator/produce"
156
160
 
157
- require_relative "core/array/difference_union_intersection"
161
+ require "ruby-next/core/array/difference_union_intersection"
158
162
 
159
- require_relative "core/hash/merge"
163
+ require "ruby-next/core/hash/merge"
160
164
 
161
- require_relative "core/string/split"
165
+ require "ruby-next/core/string/split"
162
166
 
163
- require_relative "core/symbol/start_with"
164
- require_relative "core/symbol/end_with"
167
+ require "ruby-next/core/symbol/start_with"
168
+ require "ruby-next/core/symbol/end_with"
165
169
 
166
- require_relative "core/unboundmethod/bind_call"
170
+ require "ruby-next/core/unboundmethod/bind_call"
167
171
 
168
- require_relative "core/time/floor"
169
- require_relative "core/time/ceil"
172
+ require "ruby-next/core/time/floor"
173
+ require "ruby-next/core/time/ceil"
170
174
 
171
175
  # Core extensions required for pattern matching
172
176
  # Required for pattern matching with refinements
@@ -175,11 +179,13 @@ unless defined?(NoMatchingPatternError)
175
179
  end
176
180
  end
177
181
 
178
- require_relative "core/constants/no_matching_pattern_error"
179
- require_relative "core/array/deconstruct"
180
- require_relative "core/hash/deconstruct_keys"
181
- require_relative "core/struct/deconstruct"
182
- require_relative "core/struct/deconstruct_keys"
182
+ require "ruby-next/core/constants/no_matching_pattern_error"
183
+ require "ruby-next/core/array/deconstruct"
184
+ require "ruby-next/core/hash/deconstruct_keys"
185
+ require "ruby-next/core/struct/deconstruct"
186
+ require "ruby-next/core/struct/deconstruct_keys"
187
+
188
+ require "ruby-next/core/hash/except"
183
189
 
184
190
  # Generate refinements
185
191
  RubyNext.module_eval do
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Array, method: :deconstruct, version: "2.7" do
4
- <<~RUBY
5
- def deconstruct
6
- self
7
- end
4
+ <<-RUBY
5
+ def deconstruct
6
+ self
7
+ end
8
8
  RUBY
9
9
  end
10
10
 
11
11
  # We need to hack `respond_to?` in Ruby 2.5, since it's not working with refinements
12
12
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6")
13
13
  RubyNext::Core.patch refineable: Array, name: "ArrayRespondToDeconstruct", method: :deconstruct, version: "2.7" do
14
- <<~RUBY
15
- def respond_to?(mid, *)
16
- return true if mid == :deconstruct
17
- super
18
- end
14
+ <<-RUBY
15
+ def respond_to?(mid, *)
16
+ return true if mid == :deconstruct
17
+ super
18
+ end
19
19
  RUBY
20
20
  end
21
21
  end
@@ -1,25 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Array, method: :union, version: "2.6" do
4
- <<~RUBY
5
- def union(*others)
6
- others.reduce(Array.new(self).uniq) { |acc, arr| acc | arr }
7
- end
4
+ <<-RUBY
5
+ def union(*others)
6
+ others.reduce(Array.new(self).uniq) { |acc, arr| acc | arr }
7
+ end
8
8
  RUBY
9
9
  end
10
10
 
11
11
  RubyNext::Core.patch Array, method: :difference, version: "2.6" do
12
- <<~RUBY
13
- def difference(*others)
14
- others.reduce(Array.new(self)) { |acc, arr| acc - arr }
15
- end
12
+ <<-RUBY
13
+ def difference(*others)
14
+ others.reduce(Array.new(self)) { |acc, arr| acc - arr }
15
+ end
16
16
  RUBY
17
17
  end
18
18
 
19
19
  RubyNext::Core.patch Array, method: :intersection, version: "2.7" do
20
- <<~RUBY
21
- def intersection(*others)
22
- others.reduce(Array.new(self)) { |acc, arr| acc & arr }
23
- end
20
+ <<-RUBY
21
+ def intersection(*others)
22
+ others.reduce(Array.new(self)) { |acc, arr| acc & arr }
23
+ end
24
24
  RUBY
25
25
  end
@@ -10,8 +10,8 @@ RubyNext::Core.patch Object,
10
10
  # we only use the contents in `ruby-next core_ext`.
11
11
  supported: true,
12
12
  location: [__FILE__, __LINE__ + 2] do
13
- <<~RUBY
14
- class NoMatchingPatternError < RuntimeError
15
- end
13
+ <<-RUBY
14
+ class NoMatchingPatternError < RuntimeError
15
+ end
16
16
  RUBY
17
17
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Enumerable, method: :filter, version: "2.6" do
4
- <<~RUBY
5
- alias filter select
4
+ <<-RUBY
5
+ alias filter select
6
6
  RUBY
7
7
  end
8
8
 
@@ -11,15 +11,15 @@ end
11
11
  #
12
12
  # Also, Array also have `filter!`
13
13
  RubyNext::Core.patch Array, method: :filter!, version: "2.6" do
14
- <<~RUBY
15
- alias filter select
16
- alias filter! select!
14
+ <<-RUBY
15
+ alias filter select
16
+ alias filter! select!
17
17
  RUBY
18
18
  end
19
19
 
20
20
  RubyNext::Core.patch Hash, method: :filter!, version: "2.6" do
21
- <<~RUBY
22
- alias filter select
23
- alias filter! select!
21
+ <<-RUBY
22
+ alias filter select
23
+ alias filter! select!
24
24
  RUBY
25
25
  end
@@ -3,36 +3,36 @@
3
3
  # Refine Array seprately, 'cause refining modules is vulnerable to prepend:
4
4
  # - https://bugs.ruby-lang.org/issues/13446
5
5
  RubyNext::Core.patch Enumerable, method: :filter_map, version: "2.7", refineable: [Enumerable, Array] do
6
- <<~RUBY
7
- def filter_map
8
- if block_given?
9
- result = []
10
- each do |element|
11
- res = yield element
12
- result << res if res
13
- end
14
- result
15
- else
16
- Enumerator.new do |yielder|
17
- result = []
18
- each do |element|
19
- res = yielder.yield element
20
- result << res if res
21
- end
22
- result
23
- end
6
+ <<-RUBY
7
+ def filter_map
8
+ if block_given?
9
+ result = []
10
+ each do |element|
11
+ res = yield element
12
+ result << res if res
13
+ end
14
+ result
15
+ else
16
+ Enumerator.new do |yielder|
17
+ result = []
18
+ each do |element|
19
+ res = yielder.yield element
20
+ result << res if res
24
21
  end
22
+ result
25
23
  end
24
+ end
25
+ end
26
26
  RUBY
27
27
  end
28
28
 
29
29
  RubyNext::Core.patch Enumerator::Lazy, method: :filter_map, version: "2.7" do
30
- <<~RUBY
31
- def filter_map
32
- Enumerator::Lazy.new(self) do |yielder, *values|
33
- result = yield(*values)
34
- yielder << result if result
35
- end
36
- end
30
+ <<-RUBY
31
+ def filter_map
32
+ Enumerator::Lazy.new(self) do |yielder, *values|
33
+ result = yield(*values)
34
+ yielder << result if result
35
+ end
36
+ end
37
37
  RUBY
38
38
  end
@@ -3,12 +3,12 @@
3
3
  # Refine Array seprately, 'cause refining modules is vulnerable to prepend:
4
4
  # - https://bugs.ruby-lang.org/issues/13446
5
5
  RubyNext::Core.patch Enumerable, method: :tally, version: "2.7", refineable: [Enumerable, Array] do
6
- <<~RUBY
7
- def tally
8
- each_with_object({}) do |v, acc|
9
- acc[v] ||= 0
10
- acc[v] += 1
11
- end
12
- end
6
+ <<-RUBY
7
+ def tally
8
+ each_with_object({}) do |v, acc|
9
+ acc[v] ||= 0
10
+ acc[v] += 1
11
+ end
12
+ end
13
13
  RUBY
14
14
  end
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Enumerator.singleton_class, method: :produce, singleton: Enumerator, version: "2.7" do
4
- <<~'RUBY'
5
- # Based on https://github.com/zverok/enumerator_generate
6
- def produce(*rest, &block)
7
- raise ArgumentError, "wrong number of arguments (given #{rest.size}, expected 0..1)" if rest.size > 1
8
- raise ArgumentError, "No block given" unless block
4
+ <<-'RUBY'
5
+ # Based on https://github.com/zverok/enumerator_generate
6
+ def produce(*rest, &block)
7
+ raise ArgumentError, "wrong number of arguments (given #{rest.size}, expected 0..1)" if rest.size > 1
8
+ raise ArgumentError, "No block given" unless block
9
9
 
10
- Enumerator.new(Float::INFINITY) do |y|
11
- val = rest.empty? ? yield() : rest.pop
10
+ Enumerator.new(Float::INFINITY) do |y|
11
+ val = rest.empty? ? yield() : rest.pop
12
12
 
13
- loop do
14
- y << val
15
- val = yield(val)
16
- end
17
- end
13
+ loop do
14
+ y << val
15
+ val = yield(val)
18
16
  end
17
+ end
18
+ end
19
19
  RUBY
20
20
  end