ruby-next-core 0.13.0 → 0.14.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 +40 -0
- data/README.md +31 -5
- data/bin/transform +5 -1
- data/lib/.rbnext/2.1/ruby-next/core.rb +7 -1
- data/lib/.rbnext/2.1/ruby-next/language.rb +41 -23
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +2 -2
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +1 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/{endless_range.rb → 2.6/endless_range.rb} +0 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/{pattern_matching.rb → 2.7/pattern_matching.rb} +53 -7
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/3.1/anonymous_block.rb +68 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +13 -1
- data/lib/.rbnext/2.7/ruby-next/core.rb +7 -1
- data/lib/ruby-next/commands/nextify.rb +2 -2
- data/lib/ruby-next/config.rb +2 -2
- data/lib/ruby-next/core/enumerable/compact.rb +22 -0
- data/lib/ruby-next/core/integer/try_convert.rb +16 -0
- data/lib/ruby-next/core/matchdata/match.rb +9 -0
- data/lib/ruby-next/core/refinement/import.rb +60 -0
- data/lib/ruby-next/core.rb +7 -1
- data/lib/ruby-next/language/eval.rb +1 -0
- data/lib/ruby-next/language/rewriters/{numeric_literals.rb → 2.1/numeric_literals.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{required_kwargs.rb → 2.1/required_kwargs.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{safe_navigation.rb → 2.3/safe_navigation.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{squiggly_heredoc.rb → 2.3/squiggly_heredoc.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{runtime → 2.4}/dir.rb +0 -0
- data/lib/ruby-next/language/rewriters/{endless_range.rb → 2.6/endless_range.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{args_forward.rb → 2.7/args_forward.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{numbered_params.rb → 2.7/numbered_params.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{pattern_matching.rb → 2.7/pattern_matching.rb} +53 -7
- data/lib/ruby-next/language/rewriters/{args_forward_leading.rb → 3.0/args_forward_leading.rb} +0 -0
- data/lib/ruby-next/language/rewriters/{endless_method.rb → 3.0/endless_method.rb} +15 -12
- data/lib/ruby-next/language/rewriters/{find_pattern.rb → 3.0/find_pattern.rb} +1 -3
- data/lib/ruby-next/language/rewriters/3.0/in_pattern.rb +22 -0
- data/lib/ruby-next/language/rewriters/3.1/anonymous_block.rb +68 -0
- data/lib/ruby-next/language/rewriters/3.1/endless_method_command.rb +46 -0
- data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +41 -0
- data/lib/ruby-next/language/rewriters/3.1/pin_vars_pattern.rb +50 -0
- data/lib/ruby-next/language/rewriters/3.1/refinement_import_methods.rb +60 -0
- data/lib/ruby-next/language/rewriters/{shorthand_hash.rb → 3.1/shorthand_hash.rb} +6 -4
- data/lib/ruby-next/language/rewriters/base.rb +13 -1
- data/lib/ruby-next/language/{edge.rb → rewriters/edge.rb} +0 -0
- data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +50 -0
- data/lib/ruby-next/language/rewriters/{method_reference.rb → proposed/method_reference.rb} +0 -0
- data/lib/ruby-next/language/rewriters/proposed.rb +9 -0
- data/lib/ruby-next/language/rewriters/runtime.rb +1 -2
- data/lib/ruby-next/language/setup.rb +2 -1
- data/lib/ruby-next/language.rb +41 -23
- data/lib/ruby-next/rubocop.rb +24 -4
- data/lib/ruby-next/version.rb +1 -1
- metadata +35 -24
- data/lib/ruby-next/language/proposed.rb +0 -9
- data/lib/ruby-next/language/rewriters/in_pattern.rb +0 -56
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# We cannot use refinements here, since Ruby 2.6- doesn't support them in refine modules.
|
|
4
|
+
# So, we use a defined method instead (and transpile source code to use it).
|
|
5
|
+
# NOTE: We have to transpile the source code anyway, since we need to pass a binding.
|
|
6
|
+
RubyNext::Core.singleton_class.module_eval do
|
|
7
|
+
def import_methods(other, bind)
|
|
8
|
+
import = []
|
|
9
|
+
|
|
10
|
+
other.instance_methods(false).each do |mid|
|
|
11
|
+
# check for non-Ruby methods
|
|
12
|
+
meth = other.instance_method(mid)
|
|
13
|
+
location = meth.source_location
|
|
14
|
+
|
|
15
|
+
if location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core)/)
|
|
16
|
+
raise ArgumentError, "Can't import method: #{other}##{mid} from #{location}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
source_file, lineno = *location
|
|
20
|
+
|
|
21
|
+
raise ArgumentError, "Can't import dynamicly added methods: #{other}##{mid}" unless File.file?(source_file)
|
|
22
|
+
|
|
23
|
+
lines = File.open(source_file).readlines
|
|
24
|
+
|
|
25
|
+
buffer = []
|
|
26
|
+
|
|
27
|
+
lines[(lineno - 1)..-1].each do |line|
|
|
28
|
+
buffer << line + "\n"
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
if defined?(::RubyNext::Language) && ::RubyNext::Language.runtime?
|
|
32
|
+
new_source = ::RubyNext::Language.transform(buffer.join, rewriters: RubyNext::Language.current_rewriters, using: false)
|
|
33
|
+
# Transformed successfully => valid method => evaluate transpiled code
|
|
34
|
+
import << [new_source, source_file, lineno]
|
|
35
|
+
buffer.clear
|
|
36
|
+
break
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Borrowed from https://github.com/banister/method_source/blob/81d039c966ffd95d26e12eb2e205c0eb8377f49d/lib/method_source/code_helpers.rb#L66
|
|
40
|
+
catch(:valid) do
|
|
41
|
+
eval("BEGIN{throw :valid}\nObject.new.instance_eval { #{buffer.join} }") # rubocop:disable all
|
|
42
|
+
end
|
|
43
|
+
break
|
|
44
|
+
rescue SyntaxError
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
import << [buffer.join, source_file, lineno] unless buffer.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
import.each do |(definition, file, lino)|
|
|
52
|
+
Kernel.eval definition, bind, file, lino
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Copy constants (they could be accessed from methods)
|
|
56
|
+
other.constants.each do |name|
|
|
57
|
+
Kernel.eval "#{name} = #{other}::#{name}", bind
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/ruby-next/core.rb
CHANGED
|
@@ -62,7 +62,7 @@ module RubyNext
|
|
|
62
62
|
mod_name = singleton? ? singleton.name : mod.name
|
|
63
63
|
camelized_method_name = method_name.to_s.split("_").map(&:capitalize).join
|
|
64
64
|
|
|
65
|
-
"#{mod_name}#{camelized_method_name}".gsub(/\W/, "")
|
|
65
|
+
"#{mod_name}#{camelized_method_name}".gsub(/\W/, "") # rubocop:disable Performance/StringReplacement
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def build_location(trace_locations)
|
|
@@ -173,6 +173,8 @@ require "ruby-next/core/unboundmethod/bind_call"
|
|
|
173
173
|
require "ruby-next/core/time/floor"
|
|
174
174
|
require "ruby-next/core/time/ceil"
|
|
175
175
|
|
|
176
|
+
require "ruby-next/core/refinement/import"
|
|
177
|
+
|
|
176
178
|
# Core extensions required for pattern matching
|
|
177
179
|
# Required for pattern matching with refinements
|
|
178
180
|
unless defined?(NoMatchingPatternError)
|
|
@@ -191,6 +193,10 @@ require "ruby-next/core/hash/except"
|
|
|
191
193
|
|
|
192
194
|
require "ruby-next/core/array/intersect"
|
|
193
195
|
|
|
196
|
+
require "ruby-next/core/matchdata/match"
|
|
197
|
+
require "ruby-next/core/enumerable/compact"
|
|
198
|
+
require "ruby-next/core/integer/try_convert"
|
|
199
|
+
|
|
194
200
|
# Generate refinements
|
|
195
201
|
RubyNext.module_eval do
|
|
196
202
|
RubyNext::Core.patches.refined.each do |mod, patches|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -306,6 +306,38 @@ module RubyNext
|
|
|
306
306
|
|
|
307
307
|
alias on_in_match on_match_pattern
|
|
308
308
|
|
|
309
|
+
def on_match_pattern_p(node)
|
|
310
|
+
context.track! self
|
|
311
|
+
|
|
312
|
+
@deconstructed_keys = {}
|
|
313
|
+
@predicates = Predicates::Noop.new
|
|
314
|
+
|
|
315
|
+
matchee =
|
|
316
|
+
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
|
317
|
+
|
|
318
|
+
pattern =
|
|
319
|
+
locals.with(
|
|
320
|
+
matchee: MATCHEE,
|
|
321
|
+
arr: MATCHEE_ARR,
|
|
322
|
+
hash: MATCHEE_HASH
|
|
323
|
+
) do
|
|
324
|
+
send(
|
|
325
|
+
:"#{node.children[1].type}_clause",
|
|
326
|
+
node.children[1]
|
|
327
|
+
)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
node.updated(
|
|
331
|
+
:and,
|
|
332
|
+
[
|
|
333
|
+
matchee,
|
|
334
|
+
pattern
|
|
335
|
+
]
|
|
336
|
+
).tap do |new_node|
|
|
337
|
+
replace(node.loc.expression, inline_blocks(unparse(new_node)))
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
309
341
|
private
|
|
310
342
|
|
|
311
343
|
def rewrite_case_in!(node, matchee, new_node)
|
|
@@ -407,13 +439,14 @@ module RubyNext
|
|
|
407
439
|
end
|
|
408
440
|
|
|
409
441
|
def match_var_clause(node, left = s(:lvar, locals[:matchee]))
|
|
410
|
-
|
|
442
|
+
var = node.children[0]
|
|
443
|
+
return s(:true) if var == :_
|
|
411
444
|
|
|
412
|
-
check_match_var_alternation!
|
|
445
|
+
check_match_var_alternation!(var) unless var.is_a?(::Parser::AST::Node)
|
|
413
446
|
|
|
414
447
|
s(:begin,
|
|
415
448
|
s(:or,
|
|
416
|
-
s(:begin,
|
|
449
|
+
s(:begin, build_var_assignment(var, left)),
|
|
417
450
|
s(:true)))
|
|
418
451
|
end
|
|
419
452
|
|
|
@@ -512,7 +545,7 @@ module RubyNext
|
|
|
512
545
|
|
|
513
546
|
head_match =
|
|
514
547
|
unless head.children.empty?
|
|
515
|
-
match_vars <<
|
|
548
|
+
match_vars << build_var_assignment(head.children[0].children[0])
|
|
516
549
|
|
|
517
550
|
arr_take = s(:send,
|
|
518
551
|
s(:lvar, locals[:arr]),
|
|
@@ -524,16 +557,16 @@ module RubyNext
|
|
|
524
557
|
|
|
525
558
|
tail_match =
|
|
526
559
|
unless tail.children.empty?
|
|
527
|
-
match_vars <<
|
|
560
|
+
match_vars << build_var_assignment(tail.children[0].children[0])
|
|
528
561
|
|
|
529
562
|
match_var_clause(tail.children[0], arr_slice(index + nodes.size, -1))
|
|
530
563
|
end
|
|
531
564
|
|
|
532
565
|
nodes.each do |node|
|
|
533
566
|
if node.type == :match_var
|
|
534
|
-
match_vars <<
|
|
567
|
+
match_vars << build_var_assignment(node.children[0])
|
|
535
568
|
elsif node.type == :match_as
|
|
536
|
-
match_vars <<
|
|
569
|
+
match_vars << build_var_assignment(node.children[1].children[0])
|
|
537
570
|
end
|
|
538
571
|
end
|
|
539
572
|
|
|
@@ -861,6 +894,10 @@ module RubyNext
|
|
|
861
894
|
match_var_clause(child, s(:lvar, locals[:hash]))
|
|
862
895
|
end
|
|
863
896
|
|
|
897
|
+
def pin_hash_element(node, index)
|
|
898
|
+
case_eq_hash_element node.children[0], index
|
|
899
|
+
end
|
|
900
|
+
|
|
864
901
|
def case_eq_hash_element(node, key)
|
|
865
902
|
case_eq_clause node, hash_value_at(key)
|
|
866
903
|
end
|
|
@@ -968,6 +1005,15 @@ module RubyNext
|
|
|
968
1005
|
def inline_blocks(source)
|
|
969
1006
|
source.gsub(/(?:do|{) \|_, __i__\|\n\s*([^\n]+)\n\s*(?:end|})/, '{ |_, __i__| \1 }')
|
|
970
1007
|
end
|
|
1008
|
+
|
|
1009
|
+
# Value could be omitted for mass assignment
|
|
1010
|
+
def build_var_assignment(var, value = nil)
|
|
1011
|
+
return s(:lvasgn, *[var, value].compact) unless var.is_a?(::Parser::AST::Node)
|
|
1012
|
+
|
|
1013
|
+
asign_type = :"#{var.type.to_s[0]}vasgn"
|
|
1014
|
+
|
|
1015
|
+
s(asign_type, *[var.children[0], value].compact)
|
|
1016
|
+
end
|
|
971
1017
|
end
|
|
972
1018
|
end
|
|
973
1019
|
end
|
data/lib/ruby-next/language/rewriters/{args_forward_leading.rb → 3.0/args_forward_leading.rb}
RENAMED
|
File without changes
|
|
@@ -8,14 +8,13 @@ module RubyNext
|
|
|
8
8
|
SYNTAX_PROBE = "obj = Object.new; def obj.foo() = 42"
|
|
9
9
|
MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
11
|
+
def on_def(node)
|
|
12
|
+
return process_def(node) if endless?(node)
|
|
13
|
+
|
|
14
|
+
super(node)
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
def
|
|
17
|
+
def process_def(node)
|
|
19
18
|
context.track! self
|
|
20
19
|
|
|
21
20
|
replace(node.loc.assignment, "; ")
|
|
@@ -33,14 +32,12 @@ module RubyNext
|
|
|
33
32
|
)
|
|
34
33
|
end
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
super(node)
|
|
40
|
-
end
|
|
35
|
+
def on_defs(node)
|
|
36
|
+
return process_defs(node) if endless?(node)
|
|
37
|
+
super(node)
|
|
41
38
|
end
|
|
42
39
|
|
|
43
|
-
def
|
|
40
|
+
def process_defs(node)
|
|
44
41
|
context.track! self
|
|
45
42
|
|
|
46
43
|
replace(node.loc.assignment, "; ")
|
|
@@ -57,6 +54,12 @@ module RubyNext
|
|
|
57
54
|
)
|
|
58
55
|
)
|
|
59
56
|
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def endless?(node)
|
|
61
|
+
node.loc.end.nil?
|
|
62
|
+
end
|
|
60
63
|
end
|
|
61
64
|
end
|
|
62
65
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ruby-next/language/rewriters/pattern_matching"
|
|
4
|
-
|
|
5
3
|
module RubyNext
|
|
6
4
|
module Language
|
|
7
5
|
module Rewriters
|
|
@@ -22,7 +20,7 @@ module RubyNext
|
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
|
|
25
|
-
def
|
|
23
|
+
def on_match_pattern(node)
|
|
26
24
|
@has_find_pattern = false
|
|
27
25
|
process_regular_node(node).then do |new_node|
|
|
28
26
|
return new_node unless has_find_pattern
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyNext
|
|
4
|
+
module Language
|
|
5
|
+
module Rewriters
|
|
6
|
+
using RubyNext
|
|
7
|
+
|
|
8
|
+
# Separate pattern matching rewriter for Ruby 2.7 to
|
|
9
|
+
# transpile only `in` patterns
|
|
10
|
+
class InPattern < PatternMatching
|
|
11
|
+
NAME = "pattern-matching-in"
|
|
12
|
+
SYNTAX_PROBE = "1 in 2"
|
|
13
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
|
|
14
|
+
|
|
15
|
+
# Make case-match no-op
|
|
16
|
+
def on_case_match(node)
|
|
17
|
+
process_regular_node(node)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyNext
|
|
4
|
+
module Language
|
|
5
|
+
module Rewriters
|
|
6
|
+
class AnonymousBlock < Base
|
|
7
|
+
NAME = "anonymous-block"
|
|
8
|
+
SYNTAX_PROBE = "obj = Object.new; def obj.foo(&) bar(&); end"
|
|
9
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
|
|
10
|
+
|
|
11
|
+
BLOCK = :__block__
|
|
12
|
+
|
|
13
|
+
def on_args(node)
|
|
14
|
+
block = node.children.last
|
|
15
|
+
|
|
16
|
+
return super unless block&.type == :blockarg
|
|
17
|
+
return super unless block.children.first.nil?
|
|
18
|
+
|
|
19
|
+
context.track! self
|
|
20
|
+
|
|
21
|
+
replace(block.loc.expression, "&#{BLOCK}")
|
|
22
|
+
|
|
23
|
+
node.updated(
|
|
24
|
+
:args,
|
|
25
|
+
[
|
|
26
|
+
*node.children.slice(0, node.children.index(block)),
|
|
27
|
+
s(:blockarg, BLOCK)
|
|
28
|
+
]
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_send(node)
|
|
33
|
+
block = extract_block_pass(node)
|
|
34
|
+
return super unless block&.children == [nil]
|
|
35
|
+
|
|
36
|
+
process_block(node, block)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def on_super(node)
|
|
40
|
+
block = extract_block_pass(node)
|
|
41
|
+
return super unless block&.children == [nil]
|
|
42
|
+
|
|
43
|
+
process_block(node, block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def extract_block_pass(node)
|
|
49
|
+
node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :block_pass }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def process_block(node, block)
|
|
53
|
+
replace(block.loc.expression, "&#{BLOCK}")
|
|
54
|
+
|
|
55
|
+
process(
|
|
56
|
+
node.updated(
|
|
57
|
+
nil,
|
|
58
|
+
[
|
|
59
|
+
*node.children.take(node.children.index(block)),
|
|
60
|
+
s(:block_pass, s(:lvar, BLOCK))
|
|
61
|
+
]
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyNext
|
|
4
|
+
module Language
|
|
5
|
+
module Rewriters
|
|
6
|
+
class EndlessMethodCommand < EndlessMethod
|
|
7
|
+
NAME = "endless-method-command"
|
|
8
|
+
SYNTAX_PROBE = "obj = Object.new; def obj.foo = puts 'Hello'"
|
|
9
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
|
|
10
|
+
|
|
11
|
+
def process_def(node)
|
|
12
|
+
return node unless command?(node)
|
|
13
|
+
|
|
14
|
+
super(node)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process_defs(node)
|
|
18
|
+
return node unless command?(node)
|
|
19
|
+
|
|
20
|
+
super(node)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def command?(node)
|
|
26
|
+
buffer = ::Parser::Source::Buffer.new("(endless-method-rewriter)").tap do |buffer|
|
|
27
|
+
buffer.source = node.loc.expression.source
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
parser30.parse(buffer)
|
|
31
|
+
false
|
|
32
|
+
rescue ::Parser::SyntaxError
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def parser30
|
|
37
|
+
require "parser/ruby30" unless defined?(::Parser::Ruby30)
|
|
38
|
+
|
|
39
|
+
::Parser::Ruby30.new(Language::Builder.new).tap do |prs|
|
|
40
|
+
prs.diagnostics.all_errors_are_fatal = true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyNext
|
|
4
|
+
module Language
|
|
5
|
+
module Rewriters
|
|
6
|
+
using RubyNext
|
|
7
|
+
|
|
8
|
+
# Allow omitting parentheses around patterns in `=>` and `in`
|
|
9
|
+
class OnelinePatternParensless < Base
|
|
10
|
+
NAME = "pattern-matching-oneline-parensless"
|
|
11
|
+
SYNTAX_PROBE = "[1, 2] => a, b"
|
|
12
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
|
|
13
|
+
|
|
14
|
+
def on_match_pattern(node)
|
|
15
|
+
_, pattern = *node.children
|
|
16
|
+
|
|
17
|
+
# When no parens, children boundaries are the same as the whole pattern
|
|
18
|
+
if (
|
|
19
|
+
pattern.type == :array_pattern ||
|
|
20
|
+
pattern.type == :hash_pattern
|
|
21
|
+
) &&
|
|
22
|
+
pattern.children.any? &&
|
|
23
|
+
pattern.loc.column == pattern.children.first.loc.column &&
|
|
24
|
+
pattern.loc.last_column == pattern.children.last.loc.last_column
|
|
25
|
+
|
|
26
|
+
context.track! self
|
|
27
|
+
|
|
28
|
+
left_p, right_p = pattern.type == :array_pattern ? %w([ ]) : %w[{ }]
|
|
29
|
+
|
|
30
|
+
insert_before(pattern.loc.expression, left_p)
|
|
31
|
+
insert_after(pattern.loc.expression, right_p)
|
|
32
|
+
else
|
|
33
|
+
super(node)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
alias on_match_pattern_p on_match_pattern
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyNext
|
|
4
|
+
module Language
|
|
5
|
+
module Rewriters
|
|
6
|
+
using RubyNext
|
|
7
|
+
|
|
8
|
+
# Separate pattern matching rewriter for Ruby 2.7 and 3.0 to
|
|
9
|
+
# transpile only ^(ivar|cvar|gvar)
|
|
10
|
+
class PinVarsPattern < PatternMatching
|
|
11
|
+
NAME = "pattern-matching-pin-vars"
|
|
12
|
+
SYNTAX_PROBE = "@a = 0; case 0; in ^@a; true; end"
|
|
13
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
|
|
14
|
+
|
|
15
|
+
def on_case_match(node)
|
|
16
|
+
@has_pin_vars = false
|
|
17
|
+
process_regular_node(node).then do |new_node|
|
|
18
|
+
return new_node unless has_pin_vars
|
|
19
|
+
super(node)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def on_match_pattern(node)
|
|
24
|
+
@has_pin_vars = false
|
|
25
|
+
process_regular_node(node).then do |new_node|
|
|
26
|
+
return new_node unless has_pin_vars
|
|
27
|
+
super(node)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def on_match_pattern_p(node)
|
|
32
|
+
@has_pin_vars = false
|
|
33
|
+
process_regular_node(node).then do |new_node|
|
|
34
|
+
return new_node unless has_pin_vars
|
|
35
|
+
super(node)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def on_pin(node)
|
|
40
|
+
@has_pin_vars = node.children.first.type != :lvar
|
|
41
|
+
super(node)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
attr_reader :has_pin_vars
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Add binding argument to all self-less eval's
|
|
4
|
+
module RubyNext
|
|
5
|
+
module Language
|
|
6
|
+
module Rewriters
|
|
7
|
+
class RefinementImportMethods < Language::Rewriters::Base
|
|
8
|
+
NAME = "refinement-import-methods"
|
|
9
|
+
SYNTAX_PROBE = "a = Module.new{}; Module.new do; refine String do; import_methods a end; end"
|
|
10
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
|
|
11
|
+
|
|
12
|
+
def on_block(node)
|
|
13
|
+
sender, args, body = *node
|
|
14
|
+
receiver, mid, * = *sender
|
|
15
|
+
|
|
16
|
+
return super unless mid == :refine && receiver.nil?
|
|
17
|
+
|
|
18
|
+
return super unless body
|
|
19
|
+
|
|
20
|
+
@within_refinement = true
|
|
21
|
+
|
|
22
|
+
node.updated(
|
|
23
|
+
nil,
|
|
24
|
+
[
|
|
25
|
+
sender,
|
|
26
|
+
args,
|
|
27
|
+
process(body)
|
|
28
|
+
]
|
|
29
|
+
).tap do
|
|
30
|
+
@within_refinement = false
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def on_send(node)
|
|
35
|
+
return super unless @within_refinement
|
|
36
|
+
|
|
37
|
+
_receiver, mid, *children = *node
|
|
38
|
+
|
|
39
|
+
return super unless mid == :import_methods
|
|
40
|
+
|
|
41
|
+
context.track! self
|
|
42
|
+
|
|
43
|
+
updated = node.updated(
|
|
44
|
+
nil,
|
|
45
|
+
[
|
|
46
|
+
s(:const, s(:const, s(:cbase), :RubyNext), :Core),
|
|
47
|
+
mid,
|
|
48
|
+
*children,
|
|
49
|
+
s(:send, nil, :binding)
|
|
50
|
+
]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
replace(node.loc.expression, updated)
|
|
54
|
+
|
|
55
|
+
updated
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -5,13 +5,15 @@ module RubyNext
|
|
|
5
5
|
module Rewriters
|
|
6
6
|
class ShorthandHash < Base
|
|
7
7
|
NAME = "shorthand-hash"
|
|
8
|
-
SYNTAX_PROBE = "data = {x}"
|
|
9
|
-
MIN_SUPPORTED_VERSION = Gem::Version.new(
|
|
8
|
+
SYNTAX_PROBE = "data = {x:}"
|
|
9
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
|
|
10
|
+
|
|
11
|
+
def on_pair(node)
|
|
12
|
+
return super(node) unless node.children[0].loc.last_column == node.children[1].loc.last_column
|
|
10
13
|
|
|
11
|
-
def on_ipair(node)
|
|
12
14
|
context.track! self
|
|
13
15
|
|
|
14
|
-
ident, = *node.children
|
|
16
|
+
_, ident, = *node.children
|
|
15
17
|
|
|
16
18
|
key = key_from_ident(ident)
|
|
17
19
|
|
|
@@ -15,6 +15,18 @@ module RubyNext
|
|
|
15
15
|
|
|
16
16
|
class Base < ::Parser::TreeRewriter
|
|
17
17
|
class LocalsTracker
|
|
18
|
+
using(Module.new do
|
|
19
|
+
refine ::Parser::AST::Node do
|
|
20
|
+
def to_index
|
|
21
|
+
children&.first || type
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
refine ::Object do
|
|
26
|
+
alias to_index itself
|
|
27
|
+
end
|
|
28
|
+
end)
|
|
29
|
+
|
|
18
30
|
attr_reader :stacks
|
|
19
31
|
|
|
20
32
|
def initialize
|
|
@@ -29,7 +41,7 @@ module RubyNext
|
|
|
29
41
|
def [](name, suffix = nil)
|
|
30
42
|
fetch(name).then do |name|
|
|
31
43
|
next name unless suffix
|
|
32
|
-
:"#{name}#{suffix}__"
|
|
44
|
+
:"#{name}#{suffix.to_index}__"
|
|
33
45
|
end
|
|
34
46
|
end
|
|
35
47
|
|
|
File without changes
|