ruby-next-core 0.9.2 → 0.10.4

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +15 -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 +201 -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/language/rewriters/right_hand_assignment.rb +107 -0
  11. data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
  12. data/lib/ruby-next.rb +8 -6
  13. data/lib/ruby-next/cli.rb +2 -2
  14. data/lib/ruby-next/commands/core_ext.rb +1 -1
  15. data/lib/ruby-next/commands/nextify.rb +3 -0
  16. data/lib/ruby-next/core.rb +27 -21
  17. data/lib/ruby-next/core/array/deconstruct.rb +9 -9
  18. data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
  19. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
  20. data/lib/ruby-next/core/enumerable/filter.rb +8 -8
  21. data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
  22. data/lib/ruby-next/core/enumerable/tally.rb +7 -7
  23. data/lib/ruby-next/core/enumerator/produce.rb +12 -12
  24. data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
  25. data/lib/ruby-next/core/hash/except.rb +11 -0
  26. data/lib/ruby-next/core/hash/merge.rb +8 -8
  27. data/lib/ruby-next/core/kernel/then.rb +2 -2
  28. data/lib/ruby-next/core/proc/compose.rb +11 -11
  29. data/lib/ruby-next/core/string/split.rb +6 -6
  30. data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
  31. data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
  32. data/lib/ruby-next/core/symbol/end_with.rb +4 -4
  33. data/lib/ruby-next/core/symbol/start_with.rb +4 -4
  34. data/lib/ruby-next/core/time/ceil.rb +6 -6
  35. data/lib/ruby-next/core/time/floor.rb +4 -4
  36. data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
  37. data/lib/ruby-next/core_ext.rb +1 -1
  38. data/lib/ruby-next/language.rb +12 -1
  39. data/lib/ruby-next/language/parser.rb +0 -3
  40. data/lib/ruby-next/language/proposed.rb +3 -0
  41. data/lib/ruby-next/language/rewriters/args_forward.rb +23 -20
  42. data/lib/ruby-next/language/rewriters/base.rb +1 -1
  43. data/lib/ruby-next/language/rewriters/endless_method.rb +25 -3
  44. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  45. data/lib/ruby-next/language/rewriters/method_reference.rb +1 -1
  46. data/lib/ruby-next/language/rewriters/pattern_matching.rb +102 -12
  47. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +73 -11
  48. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  49. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  50. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  51. data/lib/ruby-next/language/unparser.rb +0 -14
  52. data/lib/ruby-next/logging.rb +1 -1
  53. data/lib/ruby-next/rubocop.rb +91 -9
  54. data/lib/ruby-next/setup_self.rb +22 -0
  55. data/lib/ruby-next/version.rb +1 -1
  56. data/lib/uby-next.rb +8 -4
  57. metadata +23 -9
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class RightHandAssignment < Base
7
+ NAME = "right-hand-assignment"
8
+ SYNTAX_PROBE = "1 + 2 => a"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
10
+
11
+ def on_rasgn(node)
12
+ context.track! self
13
+
14
+ node = super(node)
15
+
16
+ val_node, asgn_node = *node
17
+
18
+ remove(val_node.loc.expression.end.join(asgn_node.loc.expression))
19
+ insert_before(val_node.loc.expression, "#{asgn_node.loc.expression.source} = ")
20
+
21
+ asgn_node.updated(
22
+ nil,
23
+ asgn_node.children + [val_node]
24
+ )
25
+ end
26
+
27
+ def on_vasgn(node)
28
+ return super(node) unless rightward?(node)
29
+
30
+ context.track! self
31
+
32
+ name, val_node = *node
33
+
34
+ remove(val_node.loc.expression.end.join(node.loc.name))
35
+ insert_before(val_node.loc.expression, "#{name} = ")
36
+
37
+ super(node)
38
+ end
39
+
40
+ def on_casgn(node)
41
+ return super(node) unless rightward?(node)
42
+
43
+ context.track! self
44
+
45
+ scope_node, name, val_node = *node
46
+
47
+ if scope_node
48
+ scope = scope_node.type == :cbase ? scope_node.loc.expression.source : "#{scope_node.loc.expression.source}::"
49
+ name = "#{scope}#{name}"
50
+ end
51
+
52
+ remove(val_node.loc.expression.end.join(node.loc.name))
53
+ insert_before(val_node.loc.expression, "#{name} = ")
54
+
55
+ super(node)
56
+ end
57
+
58
+ def on_mrasgn(node)
59
+ context.track! self
60
+
61
+ node = super(node)
62
+
63
+ lhs, rhs = *node
64
+
65
+ replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
66
+ insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
67
+
68
+ node.updated(
69
+ :masgn,
70
+ [rhs, lhs]
71
+ )
72
+ end
73
+
74
+ def on_masgn(node)
75
+ return super(node) unless rightward?(node)
76
+
77
+ context.track! self
78
+
79
+ rhs, lhs = *node
80
+
81
+ replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
82
+ insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
83
+
84
+ super(node)
85
+ end
86
+
87
+ private
88
+
89
+ def rightward?(node)
90
+ # Location could be empty for node built by rewriters
91
+ return false unless ((!node.loc.nil?) || nil) && node.loc.operator
92
+
93
+ assignee_loc =
94
+ if node.type == :masgn
95
+ node.children[0].loc.expression
96
+ else
97
+ node.loc.name
98
+ end
99
+
100
+ return false unless assignee_loc
101
+
102
+ assignee_loc.begin_pos > node.loc.operator.end_pos
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -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
@@ -148,6 +148,9 @@ module RubyNext
148
148
 
149
149
  # Then, generate the source code for the next version
150
150
  transpile path, contents, version: version
151
+ rescue SyntaxError, StandardError => e
152
+ warn "Failed to transpile #{path}: #{e.class} — #{e.message}"
153
+ exit 1
151
154
  end
152
155
 
153
156
  def save(contents, path, version)
@@ -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