ruby-next-core 0.10.2 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +12 -14
- data/bin/reparse +19 -0
- data/bin/transform +22 -8
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +5 -2
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +25 -6
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +1 -1
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +133 -103
- data/lib/.rbnext/2.3/ruby-next/utils.rb +1 -1
- data/lib/.rbnext/2.7/ruby-next/core.rb +203 -0
- data/lib/ruby-next.rb +5 -38
- data/lib/ruby-next/commands/nextify.rb +4 -1
- data/lib/ruby-next/config.rb +50 -0
- data/lib/ruby-next/core.rb +5 -4
- data/lib/ruby-next/core/array/deconstruct.rb +1 -1
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +1 -1
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +1 -1
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +3 -3
- data/lib/ruby-next/core_ext.rb +2 -0
- data/lib/ruby-next/language.rb +15 -4
- data/lib/ruby-next/language/bootsnap.rb +1 -1
- data/lib/ruby-next/language/edge.rb +0 -6
- data/lib/ruby-next/language/eval.rb +3 -3
- data/lib/ruby-next/language/parser.rb +16 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +8 -4
- data/lib/ruby-next/language/rewriters/args_forward_leading.rb +75 -0
- data/lib/ruby-next/language/rewriters/base.rb +21 -2
- data/lib/ruby-next/language/rewriters/in_pattern.rb +56 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +1 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +6 -2
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +131 -101
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +30 -26
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +1 -1
- data/lib/ruby-next/language/setup.rb +6 -3
- data/lib/ruby-next/rubocop.rb +79 -12
- data/lib/ruby-next/setup_self.rb +2 -2
- data/lib/ruby-next/version.rb +1 -1
- metadata +17 -8
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +0 -107
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5408955fbe7d23afdc9d534e433c4c094b0a3ea46a47606a6caab7b6a4c446eb
|
4
|
+
data.tar.gz: 8b1edd6c3dbdc0322a4f3efa3eff5b4f278b2fd78bf8c9e995f1587b62278b0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f3483fb111153c9d9d14e4169302f4613a3428655b71ca83e77a5f7a6d906fc151712facec5f1c95742d77920db16bfa533fb5c34e01b8f3451f720a6fa442b
|
7
|
+
data.tar.gz: '09febbb7523724d5b23ba11814387dffe043d7574496923ed1731ba6664d2c9af2a02cba6568846ce6be96bdcb8e4a4eb341029c7d3099d0e02b64e9056d4744'
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,42 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.11.1 (2020-12-28)
|
6
|
+
|
7
|
+
- Use separate _namespace_ for proposed features to avoid conflicts with new Ruby version. ([@palkan][])
|
8
|
+
|
9
|
+
Previously, we used the upcoming Ruby version number for proposed features (e.g., `3.0.0`), which broke
|
10
|
+
the load path setup, since transpiled files were not loaded anymore.
|
11
|
+
Now that's fixed by using a _virtual_ version number for proposals (`1995.next.0`).
|
12
|
+
|
13
|
+
## 0.11.0 (2020-12-24) 🎄
|
14
|
+
|
15
|
+
- Extended proposed shorthand Hash syntax to support kwargs as well. ([@palkan][])
|
16
|
+
|
17
|
+
You can try it: `x = 1; y = 2; foo(x:, y:) # => foo(x: x, y: y)`.
|
18
|
+
|
19
|
+
- **Rewrite mode is used by default in transpiler**. ([@palkan][])
|
20
|
+
|
21
|
+
- Move 3.0 features to stable features. ([@palkan][])
|
22
|
+
|
23
|
+
- Refactor `a => x` and `a in x` to comply with Ruby 3.0. ([@palkan][])
|
24
|
+
|
25
|
+
## 0.10.5 (2020-10-13)
|
26
|
+
|
27
|
+
- Fix Unparser 0.5.0 compatibility. ([@palkan][])
|
28
|
+
|
29
|
+
## 0.10.4 (2020-10-09)
|
30
|
+
|
31
|
+
- Restrict Unparser dependency. ([@palkan][])
|
32
|
+
|
33
|
+
Unparser 0.5.0 is currently not supported.
|
34
|
+
|
35
|
+
## 0.10.3 (2020-09-28)
|
36
|
+
|
37
|
+
- Update RuboCop integration to handle the latest Parser changes. ([@palkan][])
|
38
|
+
|
39
|
+
Parser 2.7.1.5 unified endless and normal methods and rightward and leftward assignments, thus, making some cops report false negatives.
|
40
|
+
|
5
41
|
## 0.10.2 (2020-09-09)
|
6
42
|
|
7
43
|
- Fix regression when `nextify` produces incorrect files for 2.7. ([@palkan][])
|
data/README.md
CHANGED
@@ -35,6 +35,7 @@ Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Ma
|
|
35
35
|
## Examples
|
36
36
|
|
37
37
|
- Ruby gems
|
38
|
+
- [action_policy](https://github.com/palkan/action_policy)
|
38
39
|
- [anyway_config](https://github.com/palkan/anyway_config)
|
39
40
|
- [graphql-fragment_cache](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache)
|
40
41
|
- Rails applications
|
@@ -92,8 +93,7 @@ def greet(val) =
|
|
92
93
|
'👽'
|
93
94
|
end
|
94
95
|
|
95
|
-
greet(hello: 'martian')
|
96
|
-
puts greeting
|
96
|
+
puts greet(hello: 'martian')
|
97
97
|
"
|
98
98
|
|
99
99
|
=> 👽
|
@@ -178,17 +178,17 @@ In the AST mode, we parse the source code into AST, modifies this AST and **gene
|
|
178
178
|
|
179
179
|
In the rewrite mode, we apply changes to the source code itself, thus, keeping the original formatting of the unaffected code (in a similar way to RuboCop's autocorrect feature).
|
180
180
|
|
181
|
-
By default, we use the AST mode. That could likely change in the future when we collect enough feedback on the rewrite mode and fix potential bugs.
|
182
|
-
|
183
181
|
The main benefit of the rewrite mode is that it preserves the original code line numbers and layout, which is especially useful in debugging.
|
184
182
|
|
183
|
+
By default, we use the rewrite mode. If you found a bug with rewrite mode which is not reproducible in the AST mode, please, let us know.
|
184
|
+
|
185
185
|
You can change the transpiler mode:
|
186
186
|
|
187
187
|
- From code by setting `RubyNext::Language.mode = :ast` or `RubyNext::Language.mode = :rewrite`.
|
188
|
-
- Via environmental variable `RUBY_NEXT_TRANSPILE_MODE=
|
188
|
+
- Via environmental variable `RUBY_NEXT_TRANSPILE_MODE=ast`.
|
189
189
|
- Via CLI option ([see below](#cli)).
|
190
190
|
|
191
|
-
**NOTE:** For the time being, Unparser
|
191
|
+
**NOTE:** For the time being, Unparser doesn't support Ruby 3.0 AST nodes, so we always use rewrite mode in Ruby 3.0+.
|
192
192
|
|
193
193
|
## CLI
|
194
194
|
|
@@ -219,7 +219,7 @@ Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
|
|
219
219
|
|
220
220
|
The behaviour depends on whether you transpile a single file or a directory:
|
221
221
|
|
222
|
-
- When transpiling a directory, the `.rbnext` subfolder is created within the target folder with subfolders for each supported Ruby versions (e.g., `.rbnext/2.6`, `.rbnext/2.7`). If you want to create only a single version (the smallest), you can also pass `--single-version` flag. In that case, no version directory is created (i.e., transpiled files go into `.rbnext`).
|
222
|
+
- When transpiling a directory, the `.rbnext` subfolder is created within the target folder with subfolders for each supported Ruby versions (e.g., `.rbnext/2.6`, `.rbnext/2.7`, `.rbnext/3.0`). If you want to create only a single version (the smallest), you can also pass `--single-version` flag. In that case, no version directory is created (i.e., transpiled files go into `.rbnext`).
|
223
223
|
|
224
224
|
- When transpiling a file and providing the output path as a _file_ path, only a single version is created. For example:
|
225
225
|
|
@@ -322,7 +322,7 @@ due to the way feature resolving works in Ruby (scanning the `$LOAD_PATH` and ha
|
|
322
322
|
|
323
323
|
If you're using [runtime mode](#runtime-usage) a long with `setup_gem_load_path` (e.g., in tests), the transpiled files are ignored (i.e., we do not modify `$LOAD_PATH`).
|
324
324
|
|
325
|
-
\* Ruby Next avoids storing duplicates; instead, only the code for the earlier version is created and is assumed to be used with other versions. For example, if the transpiled code is the same for Ruby 2.5 and Ruby 2.6, only the `.rbnext/2.7/path/to/file.rb` is kept. That's why multiple entries are added to the `$LOAD_PATH` (`.rbnext/2.6
|
325
|
+
\* Ruby Next avoids storing duplicates; instead, only the code for the earlier version is created and is assumed to be used with other versions. For example, if the transpiled code is the same for Ruby 2.5 and Ruby 2.6, only the `.rbnext/2.7/path/to/file.rb` is kept. That's why multiple entries are added to the `$LOAD_PATH` (`.rbnext/2.6`, `.rbnext/2.7`, and `.rbnext/3.0` in the specified order for Ruby 2.5, and `.rbnext/2.7` and `.rbnext/3.0` for Ruby 2.6).
|
326
326
|
|
327
327
|
### Transpiled files vs. VCS vs. installing from source
|
328
328
|
|
@@ -475,6 +475,8 @@ RUBY_NEXT_CORE_STRATEGY=backports ruby-next nextify lib/
|
|
475
475
|
|
476
476
|
**NOTE:** For Ruby 2.2, safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`) support is provided.
|
477
477
|
|
478
|
+
**IMPORTANT:** Unparser `~> 0.4.8` is required to run the transpiler on Ruby <2.4.
|
479
|
+
|
478
480
|
## Proposed and edge features
|
479
481
|
|
480
482
|
Ruby Next aims to bring edge and proposed features to Ruby community before they (hopefully) reach an official Ruby release.
|
@@ -506,17 +508,13 @@ require "ruby-next/language/runtime"
|
|
506
508
|
|
507
509
|
### Supported edge features
|
508
510
|
|
509
|
-
|
510
|
-
|
511
|
-
- Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921)).
|
512
|
-
|
513
|
-
- Find pattern (`[0, 1, 2] in [*, 1 => a, *c]`) ([#16828](https://bugs.ruby-lang.org/issues/16828)).
|
511
|
+
No new features since 3.0 release.
|
514
512
|
|
515
513
|
### Supported proposed features
|
516
514
|
|
517
515
|
- _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
|
518
516
|
|
519
|
-
- Shorthand Hash notation (`data = {x, y}`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
|
517
|
+
- Shorthand Hash/kwarg notation (`data = {x, y}` or `foo(x:, y:)`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
|
520
518
|
|
521
519
|
## Contributing
|
522
520
|
|
data/bin/reparse
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path("../../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
$VERBOSE = nil
|
6
|
+
|
7
|
+
require "bundler/setup"
|
8
|
+
|
9
|
+
require "ruby-next/language"
|
10
|
+
|
11
|
+
contents =
|
12
|
+
if File.exist?(ARGV[0])
|
13
|
+
File.read(ARGV[0])
|
14
|
+
else
|
15
|
+
ARGV[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
ast = RubyNext::Language.parse(contents)
|
19
|
+
puts Unparser.unparse(ast)
|
data/bin/transform
CHANGED
@@ -5,6 +5,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
6
6
|
require "bundler/setup"
|
7
7
|
|
8
|
+
require "optparse"
|
9
|
+
|
10
|
+
begin
|
11
|
+
require "pry-byebug"
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
|
8
15
|
ENV["RUBY_NEXT_EDGE"] = "1"
|
9
16
|
ENV["RUBY_NEXT_PROPOSED"] = "1"
|
10
17
|
|
@@ -13,16 +20,23 @@ require "ruby-next/language/rewriters/runtime"
|
|
13
20
|
|
14
21
|
contents =
|
15
22
|
if File.exist?(ARGV[0])
|
16
|
-
File.read(ARGV
|
23
|
+
File.read(ARGV.shift)
|
17
24
|
else
|
18
|
-
ARGV
|
25
|
+
ARGV.shift
|
19
26
|
end
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
transform_opts = {}
|
29
|
+
|
30
|
+
OptionParser.new do |opts|
|
31
|
+
opts.banner = "Usage: transform filepath_or_code [options]"
|
32
|
+
|
33
|
+
opts.on("--current", "Use rewriters for the current Ruby version only") do
|
34
|
+
transform_opts[:rewriters] = RubyNext::Language.current_rewriters
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("--rewrite", "User rewrite transpiling mode") do
|
38
|
+
RubyNext::Language.mode = :rewrite
|
26
39
|
end
|
40
|
+
end.parse!
|
27
41
|
|
28
|
-
puts RubyNext::Language.transform(contents, **
|
42
|
+
puts RubyNext::Language.transform(contents, **transform_opts)
|
@@ -57,7 +57,7 @@ module RubyNext
|
|
57
57
|
|
58
58
|
opts.on(
|
59
59
|
"--transpile-mode=MODE",
|
60
|
-
"Transpiler mode (ast or rewrite). Default:
|
60
|
+
"Transpiler mode (ast or rewrite). Default: rewrite"
|
61
61
|
) do |val|
|
62
62
|
Language.mode = val.to_sym
|
63
63
|
end
|
@@ -95,7 +95,7 @@ module RubyNext
|
|
95
95
|
exit 0
|
96
96
|
end
|
97
97
|
|
98
|
-
unless ((!lib_path.nil?
|
98
|
+
unless ((!lib_path.nil? || nil) && lib_path.then(&File.method(:exist?)))
|
99
99
|
$stdout.puts "Path not found: #{lib_path}"
|
100
100
|
$stdout.puts optparser.help
|
101
101
|
exit 2
|
@@ -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)
|
@@ -8,7 +8,7 @@ module RubyNext
|
|
8
8
|
def eval(source, bind = nil, *args)
|
9
9
|
new_source = ::RubyNext::Language::Runtime.transform(
|
10
10
|
source,
|
11
|
-
using: ((!bind.nil?
|
11
|
+
using: ((!bind.nil? || nil) && bind.receiver) == TOPLEVEL_BINDING.receiver || ((!((!bind.nil? || nil) && bind.receiver).nil? || nil) && ((!bind.nil? || nil) && bind.receiver).is_a?(Module))
|
12
12
|
)
|
13
13
|
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
14
14
|
super new_source, bind, *args
|
@@ -20,7 +20,7 @@ module RubyNext
|
|
20
20
|
module InstanceEval # :nodoc:
|
21
21
|
refine Object do
|
22
22
|
def instance_eval(*args, &block)
|
23
|
-
return super(*args, &block) if
|
23
|
+
return super(*args, &block) if block
|
24
24
|
|
25
25
|
source = args.shift
|
26
26
|
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
@@ -33,7 +33,7 @@ module RubyNext
|
|
33
33
|
module ClassEval
|
34
34
|
refine Module do
|
35
35
|
def module_eval(*args, &block)
|
36
|
-
return super(*args, &block) if
|
36
|
+
return super(*args, &block) if block
|
37
37
|
|
38
38
|
source = args.shift
|
39
39
|
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
@@ -42,7 +42,7 @@ module RubyNext
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def class_eval(*args, &block)
|
45
|
-
return super(*args, &block) if
|
45
|
+
return super(*args, &block) if block
|
46
46
|
|
47
47
|
source = args.shift
|
48
48
|
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
@@ -60,7 +60,7 @@ module RubyNext
|
|
60
60
|
eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
|
61
61
|
Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
|
62
62
|
false
|
63
|
-
rescue SyntaxError,
|
63
|
+
rescue SyntaxError, StandardError
|
64
64
|
true
|
65
65
|
ensure
|
66
66
|
$VERBOSE = save_verbose
|
@@ -93,25 +93,44 @@ module RubyNext
|
|
93
93
|
|
94
94
|
private
|
95
95
|
|
96
|
+
# BFS with predicate block
|
97
|
+
def find_child(node)
|
98
|
+
queue = [node]
|
99
|
+
|
100
|
+
loop do
|
101
|
+
break if queue.empty?
|
102
|
+
|
103
|
+
child = queue.shift
|
104
|
+
next unless child.is_a?(::Parser::AST::Node)
|
105
|
+
|
106
|
+
return child if yield child
|
107
|
+
|
108
|
+
queue.push(*child.children)
|
109
|
+
end
|
110
|
+
|
111
|
+
nil
|
112
|
+
end
|
113
|
+
|
96
114
|
def replace(range, ast)
|
97
|
-
((!@source_rewriter.nil?
|
115
|
+
((!@source_rewriter.nil? || nil) && @source_rewriter.replace(range, unparse(ast)))
|
98
116
|
end
|
99
117
|
|
100
118
|
def remove(range)
|
101
|
-
((!@source_rewriter.nil?
|
119
|
+
((!@source_rewriter.nil? || nil) && @source_rewriter.remove(range))
|
102
120
|
end
|
103
121
|
|
104
122
|
def insert_after(range, ast)
|
105
|
-
((!@source_rewriter.nil?
|
123
|
+
((!@source_rewriter.nil? || nil) && @source_rewriter.insert_after(range, unparse(ast)))
|
106
124
|
end
|
107
125
|
|
108
126
|
def insert_before(range, ast)
|
109
|
-
((!@source_rewriter.nil?
|
127
|
+
((!@source_rewriter.nil? || nil) && @source_rewriter.insert_before(range, unparse(ast)))
|
110
128
|
end
|
111
129
|
|
112
130
|
def unparse(ast)
|
113
131
|
return ast if ast.is_a?(String)
|
114
|
-
|
132
|
+
|
133
|
+
Unparser.unparse(ast).chomp
|
115
134
|
end
|
116
135
|
|
117
136
|
attr_reader :context
|
@@ -55,7 +55,7 @@ module RubyNext
|
|
55
55
|
attr_reader :current_index
|
56
56
|
|
57
57
|
def index_arg?(node)
|
58
|
-
((!((
|
58
|
+
((!((!current_index.nil? || nil) && current_index.children).nil? || nil) && ((!current_index.nil? || nil) && current_index.children).include?(node))
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -13,12 +13,12 @@ module RubyNext
|
|
13
13
|
|
14
14
|
# Useful to generate simple operation nodes
|
15
15
|
# (e.g., 'a + b')
|
16
|
-
def -(
|
17
|
-
::Parser::AST::Node.new(:send, [self, :-,
|
16
|
+
def -(other)
|
17
|
+
::Parser::AST::Node.new(:send, [self, :-, other.to_ast_node])
|
18
18
|
end
|
19
19
|
|
20
|
-
def +(
|
21
|
-
::Parser::AST::Node.new(:send, [self, :+,
|
20
|
+
def +(other)
|
21
|
+
::Parser::AST::Node.new(:send, [self, :+, other.to_ast_node])
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -218,9 +218,10 @@ module RubyNext
|
|
218
218
|
predicate_clause(:respond_to_deconstruct_keys, node)
|
219
219
|
end
|
220
220
|
|
221
|
-
def
|
222
|
-
|
223
|
-
|
221
|
+
def hash_keys(node, keys)
|
222
|
+
keys = keys.map { |key| key.is_a?(::Parser::AST::Node) ? key.children.first : key }
|
223
|
+
|
224
|
+
predicate_clause(:"hash_keys_#{keys.join("_p_")}", node)
|
224
225
|
end
|
225
226
|
end
|
226
227
|
end
|
@@ -244,7 +245,7 @@ module RubyNext
|
|
244
245
|
@predicates = Predicates::CaseIn.new
|
245
246
|
|
246
247
|
matchee_ast =
|
247
|
-
s(:lvasgn, MATCHEE, node.children[0])
|
248
|
+
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
248
249
|
|
249
250
|
patterns = locals.with(
|
250
251
|
matchee: MATCHEE,
|
@@ -266,14 +267,14 @@ module RubyNext
|
|
266
267
|
)
|
267
268
|
end
|
268
269
|
|
269
|
-
def
|
270
|
+
def on_match_pattern(node)
|
270
271
|
context.track! self
|
271
272
|
|
272
273
|
@deconstructed_keys = {}
|
273
274
|
@predicates = Predicates::Noop.new
|
274
275
|
|
275
276
|
matchee =
|
276
|
-
s(:lvasgn, MATCHEE, node.children[0])
|
277
|
+
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
277
278
|
|
278
279
|
pattern =
|
279
280
|
locals.with(
|
@@ -285,9 +286,10 @@ module RubyNext
|
|
285
286
|
:"#{node.children[1].type}_clause",
|
286
287
|
node.children[1]
|
287
288
|
).then do |node|
|
288
|
-
s(:
|
289
|
-
|
290
|
-
|
289
|
+
s(:begin,
|
290
|
+
s(:or,
|
291
|
+
node,
|
292
|
+
no_matching_pattern))
|
291
293
|
end
|
292
294
|
end
|
293
295
|
|
@@ -302,6 +304,8 @@ module RubyNext
|
|
302
304
|
end
|
303
305
|
end
|
304
306
|
|
307
|
+
alias on_in_match on_match_pattern
|
308
|
+
|
305
309
|
private
|
306
310
|
|
307
311
|
def rewrite_case_in!(node, matchee, new_node)
|
@@ -309,7 +313,7 @@ module RubyNext
|
|
309
313
|
remove(node.children[0].loc.expression)
|
310
314
|
|
311
315
|
node.children[1..-1].each.with_index do |clause, i|
|
312
|
-
if ((!clause.nil?
|
316
|
+
if ((!clause.nil? || nil) && clause.type) == :in_pattern
|
313
317
|
# handle multiline clauses differently
|
314
318
|
if clause.loc.last_line > clause.children[0].loc.last_line + 1
|
315
319
|
height = clause.loc.last_line - clause.children[0].loc.last_line
|
@@ -340,7 +344,7 @@ module RubyNext
|
|
340
344
|
clauses = []
|
341
345
|
|
342
346
|
nodes.each do |clause|
|
343
|
-
if ((!clause.nil?
|
347
|
+
if ((!clause.nil? || nil) && clause.type) == :in_pattern
|
344
348
|
clauses << build_when_clause(clause)
|
345
349
|
else
|
346
350
|
else_clause = process(clause)
|
@@ -378,9 +382,10 @@ module RubyNext
|
|
378
382
|
predicates.const(case_eq_clause(const, right), const).then do |node|
|
379
383
|
next node if pattern.nil?
|
380
384
|
|
381
|
-
s(:
|
382
|
-
|
383
|
-
|
385
|
+
s(:begin,
|
386
|
+
s(:and,
|
387
|
+
node,
|
388
|
+
send(:"#{pattern.type}_clause", pattern)))
|
384
389
|
end
|
385
390
|
end
|
386
391
|
|
@@ -391,13 +396,14 @@ module RubyNext
|
|
391
396
|
send :"#{child.type}_clause", child
|
392
397
|
end
|
393
398
|
end
|
394
|
-
s(:or, *children)
|
399
|
+
s(:begin, s(:or, *children))
|
395
400
|
end
|
396
401
|
|
397
402
|
def match_as_clause(node, right = s(:lvar, locals[:matchee]))
|
398
|
-
s(:
|
399
|
-
|
400
|
-
|
403
|
+
s(:begin,
|
404
|
+
s(:and,
|
405
|
+
send(:"#{node.children[0].type}_clause", node.children[0], right),
|
406
|
+
match_var_clause(node.children[1], right)))
|
401
407
|
end
|
402
408
|
|
403
409
|
def match_var_clause(node, left = s(:lvar, locals[:matchee]))
|
@@ -405,9 +411,10 @@ module RubyNext
|
|
405
411
|
|
406
412
|
check_match_var_alternation! node.children[0]
|
407
413
|
|
408
|
-
s(:
|
409
|
-
s(:
|
410
|
-
|
414
|
+
s(:begin,
|
415
|
+
s(:or,
|
416
|
+
s(:begin, s(:lvasgn, node.children[0], left)),
|
417
|
+
s(:true)))
|
411
418
|
end
|
412
419
|
|
413
420
|
def pin_clause(node, right = s(:lvar, locals[:matchee]))
|
@@ -417,8 +424,8 @@ module RubyNext
|
|
417
424
|
|
418
425
|
def case_eq_clause(node, right = s(:lvar, locals[:matchee]))
|
419
426
|
predicates.terminate!
|
420
|
-
s(:send,
|
421
|
-
process(node), :===, right)
|
427
|
+
s(:begin, s(:send,
|
428
|
+
process(node), :===, right))
|
422
429
|
end
|
423
430
|
|
424
431
|
#=========== ARRAY PATTERN (START) ===============
|
@@ -429,10 +436,11 @@ module RubyNext
|
|
429
436
|
# if there is no rest or tail, match the size first
|
430
437
|
unless node.type == :array_pattern_with_tail || node.children.any? { |n| n.type == :match_rest }
|
431
438
|
size_check = predicates.array_size(
|
432
|
-
s(:
|
433
|
-
|
434
|
-
|
435
|
-
|
439
|
+
s(:begin,
|
440
|
+
s(:send,
|
441
|
+
node.children.size.to_ast_node,
|
442
|
+
:==,
|
443
|
+
s(:send, s(:lvar, locals[:arr]), :size))),
|
436
444
|
node.children.size
|
437
445
|
)
|
438
446
|
end
|
@@ -448,9 +456,10 @@ module RubyNext
|
|
448
456
|
|
449
457
|
right = s(:and, size_check, right) if size_check
|
450
458
|
|
451
|
-
s(:
|
452
|
-
|
453
|
-
|
459
|
+
s(:begin,
|
460
|
+
s(:and,
|
461
|
+
dnode,
|
462
|
+
right))
|
454
463
|
end
|
455
464
|
end
|
456
465
|
|
@@ -468,14 +477,17 @@ module RubyNext
|
|
468
477
|
predicates.array_deconstructed(
|
469
478
|
s(:and,
|
470
479
|
respond_check,
|
471
|
-
s(:
|
472
|
-
s(:
|
473
|
-
s(:
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
480
|
+
s(:begin,
|
481
|
+
s(:and,
|
482
|
+
s(:begin,
|
483
|
+
s(:or,
|
484
|
+
s(:begin, s(:lvasgn, locals[:arr], right)),
|
485
|
+
s(:true))),
|
486
|
+
s(:begin,
|
487
|
+
s(:or,
|
488
|
+
s(:send,
|
489
|
+
s(:const, nil, :Array), :===, s(:lvar, locals[:arr])),
|
490
|
+
raise_error(:TypeError, "#deconstruct must return Array"))))))
|
479
491
|
)
|
480
492
|
end
|
481
493
|
|
@@ -485,9 +497,10 @@ module RubyNext
|
|
485
497
|
send("#{head.type}_array_element", head, index).then do |node|
|
486
498
|
next node if tail.empty?
|
487
499
|
|
488
|
-
s(:
|
489
|
-
|
490
|
-
|
500
|
+
s(:begin,
|
501
|
+
s(:and,
|
502
|
+
node,
|
503
|
+
array_element(index + 1, *tail)))
|
491
504
|
end
|
492
505
|
end
|
493
506
|
|
@@ -526,15 +539,17 @@ module RubyNext
|
|
526
539
|
|
527
540
|
pattern = array_rest_element(*nodes, index).then do |needle|
|
528
541
|
next needle unless head_match
|
529
|
-
s(:
|
530
|
-
|
531
|
-
|
542
|
+
s(:begin,
|
543
|
+
s(:and,
|
544
|
+
needle,
|
545
|
+
head_match))
|
532
546
|
end.then do |headed_needle|
|
533
547
|
next headed_needle unless tail_match
|
534
548
|
|
535
|
-
s(:
|
536
|
-
|
537
|
-
|
549
|
+
s(:begin,
|
550
|
+
s(:and,
|
551
|
+
headed_needle,
|
552
|
+
tail_match))
|
538
553
|
end
|
539
554
|
|
540
555
|
s(:block,
|
@@ -550,13 +565,14 @@ module RubyNext
|
|
550
565
|
next block if match_vars.empty?
|
551
566
|
|
552
567
|
# We need to declare match vars outside of `find` block
|
553
|
-
locals_declare = s(:masgn,
|
568
|
+
locals_declare = s(:begin, s(:masgn,
|
554
569
|
s(:mlhs, *match_vars),
|
555
|
-
s(:nil))
|
570
|
+
s(:nil)))
|
556
571
|
|
557
|
-
s(:
|
558
|
-
|
559
|
-
|
572
|
+
s(:begin,
|
573
|
+
s(:or,
|
574
|
+
locals_declare,
|
575
|
+
block))
|
560
576
|
end
|
561
577
|
end
|
562
578
|
|
@@ -575,18 +591,20 @@ module RubyNext
|
|
575
591
|
|
576
592
|
return rest if tail.empty?
|
577
593
|
|
578
|
-
s(:
|
579
|
-
|
580
|
-
|
594
|
+
s(:begin,
|
595
|
+
s(:and,
|
596
|
+
rest,
|
597
|
+
array_rest_element(*tail, -(size - 1))))
|
581
598
|
end
|
582
599
|
|
583
600
|
def array_rest_element(head, *tail, index)
|
584
601
|
send("#{head.type}_array_element", head, index).then do |node|
|
585
602
|
next node if tail.empty?
|
586
603
|
|
587
|
-
s(:
|
588
|
-
|
589
|
-
|
604
|
+
s(:begin,
|
605
|
+
s(:and,
|
606
|
+
node,
|
607
|
+
array_rest_element(*tail, index + 1)))
|
590
608
|
end
|
591
609
|
end
|
592
610
|
|
@@ -610,7 +628,7 @@ module RubyNext
|
|
610
628
|
children = node.children.map do |child, i|
|
611
629
|
send :"#{child.type}_array_element", child, index
|
612
630
|
end
|
613
|
-
s(:or, *children)
|
631
|
+
s(:begin, s(:or, *children))
|
614
632
|
end
|
615
633
|
|
616
634
|
def match_var_array_element(node, index)
|
@@ -661,18 +679,20 @@ module RubyNext
|
|
661
679
|
elsif specified_key_names.empty?
|
662
680
|
hash_element(*node.children)
|
663
681
|
else
|
664
|
-
s(:
|
665
|
-
|
666
|
-
|
682
|
+
s(:begin,
|
683
|
+
s(:and,
|
684
|
+
having_hash_keys(specified_key_names),
|
685
|
+
hash_element(*node.children)))
|
667
686
|
end
|
668
687
|
|
669
688
|
predicates.pop
|
670
689
|
|
671
690
|
next dnode if right.nil?
|
672
691
|
|
673
|
-
s(:
|
674
|
-
|
675
|
-
|
692
|
+
s(:begin,
|
693
|
+
s(:and,
|
694
|
+
dnode,
|
695
|
+
right))
|
676
696
|
end
|
677
697
|
end
|
678
698
|
|
@@ -715,7 +735,7 @@ module RubyNext
|
|
715
735
|
# Duplicate the source hash when matching **rest, 'cause we mutate it
|
716
736
|
hash_dup =
|
717
737
|
if @hash_match_rest
|
718
|
-
s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup))
|
738
|
+
s(:begin, s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup)))
|
719
739
|
else
|
720
740
|
s(:true)
|
721
741
|
end
|
@@ -728,29 +748,33 @@ module RubyNext
|
|
728
748
|
key_names = keys.children.map { |node| node.children.last }
|
729
749
|
predicates.push locals[:hash]
|
730
750
|
|
731
|
-
s(:lvasgn, deconstruct_name,
|
751
|
+
s(:begin, s(:lvasgn, deconstruct_name,
|
732
752
|
s(:send,
|
733
|
-
matchee, :deconstruct_keys, keys)).then do |dnode|
|
753
|
+
matchee, :deconstruct_keys, keys))).then do |dnode|
|
734
754
|
next dnode if respond_to_checked
|
735
755
|
|
736
756
|
s(:and,
|
737
757
|
respond_check,
|
738
|
-
s(:
|
739
|
-
s(:
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
758
|
+
s(:begin,
|
759
|
+
s(:and,
|
760
|
+
s(:begin,
|
761
|
+
s(:or,
|
762
|
+
dnode,
|
763
|
+
s(:true))),
|
764
|
+
s(:begin,
|
765
|
+
s(:or,
|
766
|
+
s(:send,
|
767
|
+
s(:const, nil, :Hash), :===, s(:lvar, deconstruct_name)),
|
768
|
+
raise_error(:TypeError, "#deconstruct_keys must return Hash"))))))
|
746
769
|
end.then do |dnode|
|
747
770
|
predicates.hash_deconstructed(dnode, key_names)
|
748
771
|
end.then do |dnode|
|
749
772
|
next dnode unless @hash_match_rest
|
750
773
|
|
751
|
-
s(:
|
752
|
-
|
753
|
-
|
774
|
+
s(:begin,
|
775
|
+
s(:and,
|
776
|
+
dnode,
|
777
|
+
hash_dup))
|
754
778
|
end
|
755
779
|
end
|
756
780
|
|
@@ -780,9 +804,10 @@ module RubyNext
|
|
780
804
|
|
781
805
|
next node if right.nil?
|
782
806
|
|
783
|
-
s(:
|
784
|
-
|
785
|
-
|
807
|
+
s(:begin,
|
808
|
+
s(:and,
|
809
|
+
node,
|
810
|
+
right))
|
786
811
|
end
|
787
812
|
end
|
788
813
|
|
@@ -792,7 +817,7 @@ module RubyNext
|
|
792
817
|
end
|
793
818
|
|
794
819
|
def match_alt_hash_element(node, key)
|
795
|
-
element_node = s(:lvasgn, locals[:hash, :el], hash_value_at(key))
|
820
|
+
element_node = s(:begin, s(:lvasgn, locals[:hash, :el], hash_value_at(key)))
|
796
821
|
|
797
822
|
children = locals.with(hash_element: locals[:hash, :el]) do
|
798
823
|
node.children.map do |child, i|
|
@@ -800,11 +825,14 @@ module RubyNext
|
|
800
825
|
end
|
801
826
|
end
|
802
827
|
|
803
|
-
s(:
|
804
|
-
s(:
|
805
|
-
|
806
|
-
|
807
|
-
|
828
|
+
s(:begin,
|
829
|
+
s(:and,
|
830
|
+
s(:begin,
|
831
|
+
s(:or,
|
832
|
+
element_node,
|
833
|
+
s(:true))),
|
834
|
+
s(:begin,
|
835
|
+
s(:or, *children))))
|
808
836
|
end
|
809
837
|
|
810
838
|
def match_as_hash_element(node, key)
|
@@ -858,13 +886,14 @@ module RubyNext
|
|
858
886
|
end
|
859
887
|
|
860
888
|
def having_hash_keys(keys, hash = s(:lvar, locals[:hash]))
|
861
|
-
|
862
|
-
|
889
|
+
keys.reduce(nil) do |acc, key|
|
890
|
+
pnode = hash_has_key(key, hash)
|
891
|
+
next pnode unless acc
|
863
892
|
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
893
|
+
s(:begin,
|
894
|
+
s(:and, acc, pnode))
|
895
|
+
end.then do |node|
|
896
|
+
predicates.hash_keys(node, keys)
|
868
897
|
end
|
869
898
|
end
|
870
899
|
|
@@ -873,9 +902,10 @@ module RubyNext
|
|
873
902
|
def with_guard(node, guard)
|
874
903
|
return node unless guard
|
875
904
|
|
876
|
-
s(:
|
877
|
-
|
878
|
-
|
905
|
+
s(:begin,
|
906
|
+
s(:and,
|
907
|
+
node,
|
908
|
+
guard.children[0])).then do |expr|
|
879
909
|
next expr unless guard.type == :unless_guard
|
880
910
|
s(:send, expr, :!)
|
881
911
|
end
|
@@ -933,10 +963,10 @@ module RubyNext
|
|
933
963
|
deconstructed_keys[key] = :"k#{deconstructed_keys.size}"
|
934
964
|
end
|
935
965
|
|
936
|
-
# Unparser generates `do .. end`
|
966
|
+
# Unparser generates `do .. end` or `{ ... }` multiline blocks, we want to
|
937
967
|
# have single-line blocks with `{ ... }`.
|
938
968
|
def inline_blocks(source)
|
939
|
-
source.gsub(/do \|_, __i__\|\n\s*([^\n]+)\n\s*end/, '{ |_, __i__| \1 }')
|
969
|
+
source.gsub(/(?:do|{) \|_, __i__\|\n\s*([^\n]+)\n\s*(?:end|})/, '{ |_, __i__| \1 }')
|
940
970
|
end
|
941
971
|
end
|
942
972
|
end
|