ruby-next 0.3.0 → 0.4.0
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 +12 -1
- data/README.md +11 -7
- metadata +3 -48
- data/bin/parse +0 -19
- data/bin/ruby-next +0 -16
- data/bin/transform +0 -21
- data/lib/ruby-next.rb +0 -37
- data/lib/ruby-next/cli.rb +0 -94
- data/lib/ruby-next/commands/base.rb +0 -42
- data/lib/ruby-next/commands/core_ext.rb +0 -166
- data/lib/ruby-next/commands/nextify.rb +0 -133
- data/lib/ruby-next/core.rb +0 -182
- data/lib/ruby-next/core/array/deconstruct.rb +0 -21
- data/lib/ruby-next/core/array/difference_union_intersection.rb +0 -25
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +0 -17
- data/lib/ruby-next/core/enumerable/filter.rb +0 -25
- data/lib/ruby-next/core/enumerable/filter_map.rb +0 -38
- data/lib/ruby-next/core/enumerable/tally.rb +0 -14
- data/lib/ruby-next/core/enumerator/produce.rb +0 -20
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +0 -21
- data/lib/ruby-next/core/hash/merge.rb +0 -14
- data/lib/ruby-next/core/kernel/then.rb +0 -10
- data/lib/ruby-next/core/proc/compose.rb +0 -19
- data/lib/ruby-next/core/runtime.rb +0 -10
- data/lib/ruby-next/core/string/split.rb +0 -11
- data/lib/ruby-next/core/struct/deconstruct.rb +0 -7
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +0 -34
- data/lib/ruby-next/core/time/ceil.rb +0 -10
- data/lib/ruby-next/core/time/floor.rb +0 -9
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +0 -9
- data/lib/ruby-next/core_ext.rb +0 -18
- data/lib/ruby-next/language.rb +0 -119
- data/lib/ruby-next/language/bootsnap.rb +0 -26
- data/lib/ruby-next/language/eval.rb +0 -64
- data/lib/ruby-next/language/parser.rb +0 -28
- data/lib/ruby-next/language/rewriters/args_forward.rb +0 -57
- data/lib/ruby-next/language/rewriters/base.rb +0 -105
- data/lib/ruby-next/language/rewriters/endless_range.rb +0 -60
- data/lib/ruby-next/language/rewriters/method_reference.rb +0 -33
- data/lib/ruby-next/language/rewriters/numbered_params.rb +0 -41
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +0 -541
- data/lib/ruby-next/language/runtime.rb +0 -95
- data/lib/ruby-next/language/setup.rb +0 -43
- data/lib/ruby-next/language/unparser.rb +0 -8
- data/lib/ruby-next/utils.rb +0 -36
- data/lib/ruby-next/version.rb +0 -5
- data/lib/uby-next.rb +0 -68
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Refine Array seprately, 'cause refining modules is vulnerable to prepend:
|
4
|
-
# - https://bugs.ruby-lang.org/issues/13446
|
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
|
13
|
-
RUBY
|
14
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
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
|
9
|
-
|
10
|
-
Enumerator.new(Float::INFINITY) do |y|
|
11
|
-
val = rest.empty? ? yield() : rest.pop
|
12
|
-
|
13
|
-
loop do
|
14
|
-
y << val
|
15
|
-
val = yield(val)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
RUBY
|
20
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RubyNext::Core.patch Hash, method: :deconstruct_keys, version: "2.7" do
|
4
|
-
<<~RUBY
|
5
|
-
def deconstruct_keys(_)
|
6
|
-
self
|
7
|
-
end
|
8
|
-
RUBY
|
9
|
-
end
|
10
|
-
|
11
|
-
# We need to hack `respond_to?` in Ruby 2.5, since it's not working with refinements
|
12
|
-
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6")
|
13
|
-
RubyNext::Core.patch refineable: Hash, name: "HashRespondToDeconstructKeys", method: :deconstruct_keys, version: "2.7" do
|
14
|
-
<<~RUBY
|
15
|
-
def respond_to?(mid, *)
|
16
|
-
return true if mid == :deconstruct_keys
|
17
|
-
super
|
18
|
-
end
|
19
|
-
RUBY
|
20
|
-
end
|
21
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RubyNext::Core.patch Hash, method: :merge, version: "2.6", supported: {}.method(:merge).arity < 0, core_ext: :prepend do
|
4
|
-
<<~RUBY
|
5
|
-
def merge(*others)
|
6
|
-
return super if others.size == 1
|
7
|
-
return dup if others.size == 0
|
8
|
-
|
9
|
-
merge(others.shift).tap do |new_h|
|
10
|
-
others.each { |h| new_h.merge!(h) }
|
11
|
-
end
|
12
|
-
end
|
13
|
-
RUBY
|
14
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Refine object, 'cause refining modules (Kernel) is vulnerable to prepend:
|
4
|
-
# - https://bugs.ruby-lang.org/issues/13446
|
5
|
-
# - Rails added `Kernel.prepend` in 6.1: https://github.com/rails/rails/commit/3124007bd674dcdc9c3b5c6b2964dfb7a1a0733c
|
6
|
-
RubyNext::Core.patch Kernel, method: :then, version: "2.6", refineable: Object do
|
7
|
-
<<~RUBY
|
8
|
-
alias then yield_self
|
9
|
-
RUBY
|
10
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# rubocop:disable Style/LambdaCall
|
4
|
-
RubyNext::Core.patch Proc, name: "ProcCompose", method: :<<, version: "2.6" do
|
5
|
-
<<~RUBY
|
6
|
-
def <<(other)
|
7
|
-
raise TypeError, "callable object is expected" unless other.respond_to?(:call)
|
8
|
-
this = self
|
9
|
-
proc { |*args, &block| this.(other.(*args, &block)) }
|
10
|
-
end
|
11
|
-
|
12
|
-
def >>(other)
|
13
|
-
raise TypeError, "callable object is expected" unless other.respond_to?(:call)
|
14
|
-
this = self
|
15
|
-
proc { |*args, &block| other.(this.(*args, &block)) }
|
16
|
-
end
|
17
|
-
RUBY
|
18
|
-
end
|
19
|
-
# rubocop:enable Style/LambdaCall
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Extend `Language.transform` to inject `using RubyNext` to every file
|
4
|
-
RubyNext::Language.singleton_class.prepend(Module.new do
|
5
|
-
def transform(contents, using: true, **hargs)
|
6
|
-
# We cannot activate refinements in eval
|
7
|
-
new_contents = RubyNext::Core.inject!(contents) if using
|
8
|
-
super(new_contents || contents, using: using, **hargs)
|
9
|
-
end
|
10
|
-
end)
|
@@ -1,11 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RubyNext::Core.patch String, method: :split, version: "2.6", supported: ("a b".split(" ", &proc {}) == "a b"), core_ext: :prepend do
|
4
|
-
<<~RUBY
|
5
|
-
def split(*args, &block)
|
6
|
-
return super unless block_given?
|
7
|
-
super.each { |el| yield el }
|
8
|
-
self
|
9
|
-
end
|
10
|
-
RUBY
|
11
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Source: https://github.com/ruby/ruby/blob/b76a21aa45fff75909a66f8b20fc5856705f7862/struct.c#L953-L980
|
4
|
-
RubyNext::Core.patch Struct, method: :deconstruct_keys, version: "2.7" do
|
5
|
-
<<~'RUBY'
|
6
|
-
def deconstruct_keys(keys)
|
7
|
-
raise TypeError, "wrong argument type #{keys.class} (expected Array or nil)" if keys && !keys.is_a?(Array)
|
8
|
-
|
9
|
-
return to_h unless keys
|
10
|
-
|
11
|
-
return {} if size < keys.size
|
12
|
-
|
13
|
-
keys.each_with_object({}) do |k, acc|
|
14
|
-
# if k is Symbol and not a member of a Struct return {}
|
15
|
-
next if (Symbol === k || String === k) && !members.include?(k.to_sym)
|
16
|
-
# if k is Integer check that index is not ouf of bounds
|
17
|
-
next if Integer === k && k > size - 1
|
18
|
-
acc[k] = self[k]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
RUBY
|
22
|
-
end
|
23
|
-
|
24
|
-
# We need to hack `respond_to?` in Ruby 2.5, since it's not working with refinements
|
25
|
-
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6")
|
26
|
-
RubyNext::Core.patch refineable: Struct, name: "StructRespondToDeconstruct", method: :deconstruct_keys, version: "2.7" do
|
27
|
-
<<~RUBY
|
28
|
-
def respond_to?(mid, *)
|
29
|
-
return true if mid == :deconstruct_keys || mid == :deconstruct
|
30
|
-
super
|
31
|
-
end
|
32
|
-
RUBY
|
33
|
-
end
|
34
|
-
end
|
data/lib/ruby-next/core_ext.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "core"
|
4
|
-
|
5
|
-
# Monkey-patch core classes using the same patches as for refinements
|
6
|
-
RubyNext::Core.patches.extensions.each do |mod, patches|
|
7
|
-
patches.each do |patch|
|
8
|
-
next if patch.supported?
|
9
|
-
|
10
|
-
if patch.prepend?
|
11
|
-
mod.prepend(patch.to_module)
|
12
|
-
else
|
13
|
-
mod.module_eval(patch.body, *patch.location)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
RubyNext::Core.strategy = :core_ext
|
data/lib/ruby-next/language.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
gem "parser", ">= 2.7.0.0"
|
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
|
-
class TransformContext
|
27
|
-
attr_reader :versions, :use_ruby_next
|
28
|
-
|
29
|
-
def initialize
|
30
|
-
# Minimum supported RubyNext version
|
31
|
-
@min_version = MIN_SUPPORTED_VERSION
|
32
|
-
@dirty = false
|
33
|
-
@versions = Set.new
|
34
|
-
@use_ruby_next = false
|
35
|
-
end
|
36
|
-
|
37
|
-
# Called by rewriter when it performs transfomrations
|
38
|
-
def track!(rewriter)
|
39
|
-
@dirty = true
|
40
|
-
versions << rewriter.class::MIN_SUPPORTED_VERSION
|
41
|
-
end
|
42
|
-
|
43
|
-
def use_ruby_next!
|
44
|
-
@use_ruby_next = true
|
45
|
-
end
|
46
|
-
|
47
|
-
alias use_ruby_next? use_ruby_next
|
48
|
-
|
49
|
-
def dirty?
|
50
|
-
@dirty == true
|
51
|
-
end
|
52
|
-
|
53
|
-
def min_version
|
54
|
-
versions.min
|
55
|
-
end
|
56
|
-
|
57
|
-
def sorted_versions
|
58
|
-
versions.to_a.sort
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
class << self
|
63
|
-
attr_accessor :rewriters
|
64
|
-
attr_reader :watch_dirs
|
65
|
-
|
66
|
-
def transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, context: TransformContext.new)
|
67
|
-
parse(source).then do |ast|
|
68
|
-
rewriters.inject(ast) do |tree, rewriter|
|
69
|
-
rewriter.new(context).process(tree)
|
70
|
-
end.then do |new_ast|
|
71
|
-
next source unless context.dirty?
|
72
|
-
|
73
|
-
Unparser.unparse(new_ast)
|
74
|
-
end.then do |source|
|
75
|
-
next source unless RubyNext::Core.refine?
|
76
|
-
next source unless using && context.use_ruby_next?
|
77
|
-
|
78
|
-
Core.inject! source.dup
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def transformable?(path)
|
84
|
-
watch_dirs.any? { |dir| path.start_with?(dir) }
|
85
|
-
end
|
86
|
-
|
87
|
-
# Rewriters required for the current version
|
88
|
-
def current_rewriters
|
89
|
-
@current_rewriters ||= rewriters.select(&:unsupported_syntax?)
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
attr_writer :watch_dirs
|
95
|
-
end
|
96
|
-
|
97
|
-
self.rewriters = []
|
98
|
-
self.watch_dirs = %w[app lib spec test].map { |path| File.join(Dir.pwd, path) }
|
99
|
-
|
100
|
-
require "ruby-next/language/rewriters/base"
|
101
|
-
|
102
|
-
require "ruby-next/language/rewriters/endless_range"
|
103
|
-
rewriters << Rewriters::EndlessRange
|
104
|
-
|
105
|
-
require "ruby-next/language/rewriters/pattern_matching"
|
106
|
-
rewriters << Rewriters::PatternMatching
|
107
|
-
|
108
|
-
require "ruby-next/language/rewriters/args_forward"
|
109
|
-
rewriters << Rewriters::ArgsForward
|
110
|
-
|
111
|
-
require "ruby-next/language/rewriters/numbered_params"
|
112
|
-
rewriters << Rewriters::NumberedParams
|
113
|
-
|
114
|
-
if ENV["RUBY_NEXT_ENABLE_METHOD_REFERENCE"] == "1"
|
115
|
-
require "ruby-next/language/rewriters/method_reference"
|
116
|
-
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::MethodReference
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "ruby-next"
|
4
|
-
require "ruby-next/utils"
|
5
|
-
require "ruby-next/language"
|
6
|
-
|
7
|
-
# Patch bootsnap to transform source code.
|
8
|
-
# Based on https://github.com/kddeisz/preval/blob/master/lib/preval.rb
|
9
|
-
load_iseq = RubyVM::InstructionSequence.method(:load_iseq)
|
10
|
-
|
11
|
-
if load_iseq.source_location[0].include?("/bootsnap/")
|
12
|
-
Bootsnap::CompileCache::ISeq.singleton_class.prepend(
|
13
|
-
Module.new do
|
14
|
-
def input_to_storage(source, path)
|
15
|
-
return super unless RubyNext::Language.transformable?(path)
|
16
|
-
source = RubyNext::Language.transform(source, rewriters: RubyNext::Language.current_rewriters)
|
17
|
-
|
18
|
-
$stdout.puts ::RubyNext::Utils.source_with_lines(source, path) if ENV["RUBY_NEXT_DEBUG"] == "1"
|
19
|
-
|
20
|
-
RubyVM::InstructionSequence.compile(source, path, path).to_binary
|
21
|
-
rescue SyntaxError
|
22
|
-
raise Bootsnap::CompileCache::Uncompilable, "syntax error"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
)
|
26
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyNext
|
4
|
-
module Language
|
5
|
-
module KernelEval
|
6
|
-
refine Kernel do
|
7
|
-
def eval(source, bind = nil, *args)
|
8
|
-
new_source = ::RubyNext::Language::Runtime.transform(
|
9
|
-
source,
|
10
|
-
using: bind&.receiver == TOPLEVEL_BINDING.receiver || bind&.receiver&.is_a?(Module)
|
11
|
-
)
|
12
|
-
$stdout.puts ::RubyNext::Utils.source_with_lines(new_source, "(#{caller_locations(1, 1).first})") if ENV["RUBY_NEXT_DEBUG"] == "1"
|
13
|
-
super new_source, bind, *args
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module InstanceEval # :nodoc:
|
19
|
-
refine Object do
|
20
|
-
def instance_eval(*args, &block)
|
21
|
-
return super(*args, &block) if block_given?
|
22
|
-
|
23
|
-
source = args.shift
|
24
|
-
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
25
|
-
$stdout.puts ::RubyNext::Utils.source_with_lines(new_source, "(#{caller_locations(1, 1).first})") if ENV["RUBY_NEXT_DEBUG"] == "1"
|
26
|
-
super new_source, *args
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
module ClassEval
|
32
|
-
refine Module do
|
33
|
-
def module_eval(*args, &block)
|
34
|
-
return super(*args, &block) if block_given?
|
35
|
-
|
36
|
-
source = args.shift
|
37
|
-
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
38
|
-
$stdout.puts ::RubyNext::Utils.source_with_lines(new_source, "(#{caller_locations(1, 1).first})") if ENV["RUBY_NEXT_DEBUG"] == "1"
|
39
|
-
super new_source, *args
|
40
|
-
end
|
41
|
-
|
42
|
-
def class_eval(*args, &block)
|
43
|
-
return super(*args, &block) if block_given?
|
44
|
-
|
45
|
-
source = args.shift
|
46
|
-
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
47
|
-
$stdout.puts ::RubyNext::Utils.source_with_lines(new_source, "(#{caller_locations(1, 1).first})") if ENV["RUBY_NEXT_DEBUG"] == "1"
|
48
|
-
super new_source, *args
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Refinements for `eval`-like methods.
|
54
|
-
# Transpiling eval is only possible if we do not use local from the binding,
|
55
|
-
# because we cannot access the binding of caller (without non-production ready hacks).
|
56
|
-
#
|
57
|
-
# This module is meant mainly for testing purposes.
|
58
|
-
module Eval
|
59
|
-
include InstanceEval
|
60
|
-
include ClassEval
|
61
|
-
include KernelEval
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|