ruby-next-core 0.2.0 → 0.3.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 +26 -0
- data/README.md +69 -7
- data/bin/ruby-next +1 -1
- data/lib/ruby-next/cli.rb +45 -6
- data/lib/ruby-next/commands/core_ext.rb +166 -0
- data/lib/ruby-next/commands/nextify.rb +18 -3
- data/lib/ruby-next/core.rb +149 -1
- data/lib/ruby-next/core/array/deconstruct.rb +21 -0
- data/lib/ruby-next/core/array/difference_union_intersection.rb +15 -21
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +17 -0
- data/lib/ruby-next/core/enumerable/filter.rb +20 -18
- data/lib/ruby-next/core/enumerable/filter_map.rb +28 -40
- data/lib/ruby-next/core/enumerable/tally.rb +9 -23
- data/lib/ruby-next/core/enumerator/produce.rb +12 -14
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +21 -0
- data/lib/ruby-next/core/hash/merge.rb +8 -10
- data/lib/ruby-next/core/kernel/then.rb +7 -9
- data/lib/ruby-next/core/proc/compose.rb +12 -14
- data/lib/ruby-next/core/string/split.rb +11 -0
- data/lib/ruby-next/core/struct/deconstruct.rb +7 -0
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +34 -0
- data/lib/ruby-next/core/time/ceil.rb +10 -0
- data/lib/ruby-next/core/time/floor.rb +9 -0
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +9 -0
- data/lib/ruby-next/core_ext.rb +18 -0
- data/lib/ruby-next/language.rb +4 -2
- data/lib/ruby-next/language/parser.rb +5 -1
- data/lib/ruby-next/language/rewriters/base.rb +2 -2
- data/lib/ruby-next/language/rewriters/method_reference.rb +3 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +2 -2
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +36 -17
- data/lib/ruby-next/language/runtime.rb +2 -3
- data/lib/ruby-next/version.rb +1 -1
- metadata +13 -3
- data/lib/ruby-next/core/pattern_matching.rb +0 -37
@@ -0,0 +1,21 @@
|
|
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,16 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
return dup if others.size == 0
|
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
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
merge(others.shift).tap do |new_h|
|
10
|
+
others.each { |h| new_h.merge!(h) }
|
13
11
|
end
|
14
12
|
end
|
15
|
-
|
13
|
+
RUBY
|
16
14
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
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
|
12
10
|
end
|
@@ -1,21 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# rubocop:disable Style/LambdaCall
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
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
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
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)) }
|
18
16
|
end
|
19
|
-
|
17
|
+
RUBY
|
20
18
|
end
|
21
19
|
# rubocop:enable Style/LambdaCall
|
@@ -0,0 +1,11 @@
|
|
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
|
@@ -0,0 +1,34 @@
|
|
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
|
@@ -0,0 +1,18 @@
|
|
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
CHANGED
@@ -6,7 +6,6 @@ gem "unparser", ">= 0.4.7"
|
|
6
6
|
require "set"
|
7
7
|
|
8
8
|
require "ruby-next"
|
9
|
-
using RubyNext
|
10
9
|
|
11
10
|
module RubyNext
|
12
11
|
# Language module contains tools to transpile newer Ruby syntax
|
@@ -19,6 +18,8 @@ module RubyNext
|
|
19
18
|
# - Each processor may modify the AST
|
20
19
|
# - Generates a transpiled source code from the transformed AST (via the `unparser` gem)
|
21
20
|
module Language
|
21
|
+
using RubyNext
|
22
|
+
|
22
23
|
require "ruby-next/language/parser"
|
23
24
|
require "ruby-next/language/unparser"
|
24
25
|
|
@@ -62,7 +63,7 @@ module RubyNext
|
|
62
63
|
attr_accessor :rewriters
|
63
64
|
attr_reader :watch_dirs
|
64
65
|
|
65
|
-
def transform(source, rewriters: self.rewriters, using:
|
66
|
+
def transform(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, context: TransformContext.new)
|
66
67
|
parse(source).then do |ast|
|
67
68
|
rewriters.inject(ast) do |tree, rewriter|
|
68
69
|
rewriter.new(context).process(tree)
|
@@ -71,6 +72,7 @@ module RubyNext
|
|
71
72
|
|
72
73
|
Unparser.unparse(new_ast)
|
73
74
|
end.then do |source|
|
75
|
+
next source unless RubyNext::Core.refine?
|
74
76
|
next source unless using && context.use_ruby_next?
|
75
77
|
|
76
78
|
Core.inject! source.dup
|
@@ -10,7 +10,11 @@ module RubyNext
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def parser
|
13
|
-
::Parser::Ruby27.new(Builder.new)
|
13
|
+
::Parser::Ruby27.new(Builder.new).tap do |prs|
|
14
|
+
prs.diagnostics.tap do |diagnostics|
|
15
|
+
diagnostics.all_errors_are_fatal = true
|
16
|
+
end
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
def parse(source, file = "(string)")
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
using RubyNext
|
4
|
-
|
5
3
|
module RubyNext
|
6
4
|
module Language
|
7
5
|
module Rewriters
|
6
|
+
using RubyNext
|
7
|
+
|
8
8
|
CUSTOM_PARSER_REQUIRED = <<~MSG
|
9
9
|
The %s feature is not a part of the latest stable Ruby release
|
10
10
|
and is not supported by your Parser gem version.
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
using RubyNext
|
4
|
-
|
5
3
|
module RubyNext
|
6
4
|
module Language
|
7
5
|
module Rewriters
|
6
|
+
using RubyNext
|
7
|
+
|
8
8
|
using(Module.new do
|
9
9
|
refine ::Parser::AST::Node do
|
10
10
|
def to_ast_node
|
@@ -66,8 +66,6 @@ module RubyNext
|
|
66
66
|
def on_in_match(node)
|
67
67
|
context.track! self
|
68
68
|
|
69
|
-
@deconstructed = []
|
70
|
-
|
71
69
|
matchee =
|
72
70
|
s(:lvasgn, MATCHEE, node.children[0])
|
73
71
|
|
@@ -146,10 +144,10 @@ module RubyNext
|
|
146
144
|
s(:or, *children)
|
147
145
|
end
|
148
146
|
|
149
|
-
def match_as_clause(node)
|
147
|
+
def match_as_clause(node, right = s(:lvar, locals[:matchee]))
|
150
148
|
s(:and,
|
151
149
|
case_eq_clause(node.children[0]),
|
152
|
-
match_var_clause(node.children[1],
|
150
|
+
match_var_clause(node.children[1], right))
|
153
151
|
end
|
154
152
|
|
155
153
|
def match_var_clause(node, left = s(:lvar, locals[:matchee]))
|
@@ -195,6 +193,10 @@ module RubyNext
|
|
195
193
|
s(:and,
|
196
194
|
dnode,
|
197
195
|
right)
|
196
|
+
end.then do |right|
|
197
|
+
s(:and,
|
198
|
+
respond_to_check(matchee, :deconstruct),
|
199
|
+
right)
|
198
200
|
end
|
199
201
|
end
|
200
202
|
|
@@ -202,20 +204,21 @@ module RubyNext
|
|
202
204
|
|
203
205
|
def deconstruct_node(matchee)
|
204
206
|
# only deconstruct once per case
|
205
|
-
return if deconstructed
|
207
|
+
return if deconstructed&.include?(locals[:arr])
|
206
208
|
|
207
209
|
context.use_ruby_next!
|
208
210
|
|
209
211
|
right = s(:send, matchee, :deconstruct)
|
210
212
|
|
211
|
-
deconstructed << locals[:arr]
|
213
|
+
deconstructed << locals[:arr] if deconstructed
|
214
|
+
|
212
215
|
s(:and,
|
213
216
|
s(:or,
|
214
217
|
s(:lvasgn, locals[:arr], right),
|
215
218
|
s(:true)), # rubocop:disable Lint/BooleanSymbol
|
216
219
|
s(:or,
|
217
220
|
case_eq_clause(s(:const, nil, :Array), s(:lvar, locals[:arr])),
|
218
|
-
raise_error(:TypeError)))
|
221
|
+
raise_error(:TypeError, "#deconstruct must return Array")))
|
219
222
|
end
|
220
223
|
|
221
224
|
def array_element(index, head, *tail)
|
@@ -282,6 +285,10 @@ module RubyNext
|
|
282
285
|
match_var_clause(node, arr_item_at(index))
|
283
286
|
end
|
284
287
|
|
288
|
+
def match_as_array_element(node, index)
|
289
|
+
match_as_clause(node, arr_item_at(index))
|
290
|
+
end
|
291
|
+
|
285
292
|
def pin_array_element(node, index)
|
286
293
|
case_eq_array_element node.children[0], index
|
287
294
|
end
|
@@ -321,11 +328,15 @@ module RubyNext
|
|
321
328
|
hash_element(*node.children)
|
322
329
|
end
|
323
330
|
|
324
|
-
|
331
|
+
next dnode if right.nil?
|
325
332
|
|
326
333
|
s(:and,
|
327
334
|
dnode,
|
328
335
|
right)
|
336
|
+
end.then do |right|
|
337
|
+
s(:and,
|
338
|
+
respond_to_check(matchee, :deconstruct_keys),
|
339
|
+
right)
|
329
340
|
end
|
330
341
|
end
|
331
342
|
|
@@ -359,11 +370,12 @@ module RubyNext
|
|
359
370
|
end
|
360
371
|
|
361
372
|
# Create a copy of the original hash if already deconstructed
|
362
|
-
|
373
|
+
# FIXME: need better algorithm to handle different key sets
|
374
|
+
# return hash_dup if deconstructed&.include?(locals[:hash])
|
363
375
|
|
364
376
|
context.use_ruby_next!
|
365
377
|
|
366
|
-
deconstructed << locals[:hash]
|
378
|
+
deconstructed << locals[:hash] if deconstructed
|
367
379
|
|
368
380
|
right = s(:send,
|
369
381
|
matchee, :deconstruct_keys, keys)
|
@@ -375,7 +387,7 @@ module RubyNext
|
|
375
387
|
s(:and,
|
376
388
|
s(:or,
|
377
389
|
case_eq_clause(s(:const, nil, :Hash), s(:lvar, locals[:hash, :src])),
|
378
|
-
raise_error(:TypeError)),
|
390
|
+
raise_error(:TypeError, "#deconstruct_keys must return Hash")),
|
379
391
|
hash_dup))
|
380
392
|
end
|
381
393
|
|
@@ -491,14 +503,21 @@ module RubyNext
|
|
491
503
|
end
|
492
504
|
|
493
505
|
def no_matching_pattern
|
494
|
-
raise_error
|
506
|
+
raise_error(
|
507
|
+
:NoMatchingPatternError,
|
508
|
+
s(:send,
|
509
|
+
s(:lvar, locals[:matchee]), :inspect)
|
510
|
+
)
|
495
511
|
end
|
496
512
|
|
497
|
-
def raise_error(type)
|
513
|
+
def raise_error(type, msg = "")
|
498
514
|
s(:send, s(:const, nil, :Kernel), :raise,
|
499
515
|
s(:const, nil, type),
|
500
|
-
|
501
|
-
|
516
|
+
msg.to_ast_node)
|
517
|
+
end
|
518
|
+
|
519
|
+
def respond_to_check(node, mid)
|
520
|
+
s(:send, node, :respond_to?, mid.to_ast_node)
|
502
521
|
end
|
503
522
|
|
504
523
|
def respond_to_missing?(mid, *)
|
@@ -7,13 +7,12 @@ require "ruby-next/utils"
|
|
7
7
|
require "ruby-next/language"
|
8
8
|
require "ruby-next/language/eval"
|
9
9
|
|
10
|
-
using RubyNext
|
11
|
-
|
12
10
|
module RubyNext
|
13
11
|
module Language
|
14
12
|
# Module responsible for runtime transformations
|
15
|
-
|
16
13
|
module Runtime
|
14
|
+
using RubyNext
|
15
|
+
|
17
16
|
class << self
|
18
17
|
include Utils
|
19
18
|
|