ruby-next-core 0.13.1 → 0.14.1
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} +121 -34
- 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/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +1061 -0
- 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} +121 -34
- 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.rb +41 -23
- data/lib/ruby-next/rubocop.rb +24 -4
- data/lib/ruby-next/version.rb +1 -1
- metadata +35 -23
- data/lib/ruby-next/language/proposed.rb +0 -9
- data/lib/ruby-next/language/rewriters/in_pattern.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8f084701e3c87e432518f6367d883b9524dbc002d77352793b912504a63e8d0
|
4
|
+
data.tar.gz: bc44011f4183dd2133f259f07dc7a40c4be5dbd4cf2f85e35efb525463011ace
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eabbb2bc0ccfa86f033eabce8001d208638a3995c7f112c29ed0e5f3142597bc60d87112899e7d383b09739c7c4cbe2eff06d10c58df7577cf12f80d4cb19c4d
|
7
|
+
data.tar.gz: 000c76da160d8dba1231121b7e71b335da2d9775240bfc7a9fb2201ca55d6ac366f47f1c918a3c5ebb46abdcc90857b0c3a9d221455ed6eff68965f5b8f905e9
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,46 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.14.1 (2022-01-21)
|
6
|
+
|
7
|
+
- Fix nested find patterns transpiling. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.14.0 🎄
|
10
|
+
|
11
|
+
- Add `Integer.try_convert`. ([@palkan][])
|
12
|
+
|
13
|
+
- Add `Enumerable#compact`, `Enumerator::Lazy#compact`. ([@palkan][])
|
14
|
+
|
15
|
+
- [Proposed] Add support for binding instance, class, global variables in pattern matching. ([@palkan][])
|
16
|
+
|
17
|
+
This brings back rightward assignment: `42 => @v`.
|
18
|
+
|
19
|
+
- Add `MatchData#match`. ([@palkan][])
|
20
|
+
|
21
|
+
- Add support for command syntax in endless methods (`def foo() = puts "bar"`)
|
22
|
+
|
23
|
+
- Add `Refinement#import_methods` support. ([@palkan][])
|
24
|
+
|
25
|
+
This API only works in conjunction with transpiling, since it couldn't be backported purely as a method and requires passing an additional argument (Binding).
|
26
|
+
|
27
|
+
You can find the details in [the PR](https://github.com/ruby-next/ruby-next/pull/85).
|
28
|
+
|
29
|
+
- Added support for instance, class and global variables and expressions for pin operator. ([@palkan][])
|
30
|
+
|
31
|
+
- Support omitting parentheses in one-line pattern matching. ([@palkan][])
|
32
|
+
|
33
|
+
- Anonymous blocks `def b(&); c(&); end` ([@palkan][]).
|
34
|
+
|
35
|
+
## 0.13.3 (2021-12-08)
|
36
|
+
|
37
|
+
- Revert 0.13.2 and freeze Parser version. ([@skryukov][])
|
38
|
+
|
39
|
+
Postpone upgrade 'till v0.14 due to breaking shorthand hash changes.
|
40
|
+
|
41
|
+
## 0.13.2 (2021-11-28)
|
42
|
+
|
43
|
+
- Parser upgrade.
|
44
|
+
|
5
45
|
## 0.13.1 (2021-09-27)
|
6
46
|
|
7
47
|
- Fix checking for realpath during $LOAD_PATH setup. ([@palkan][])
|
data/README.md
CHANGED
@@ -73,6 +73,7 @@ _Please, submit a PR to add your project to the list!_
|
|
73
73
|
- [RuboCop](#rubocop)
|
74
74
|
- [Using with EOL Rubies](#using-with-eol-rubies)
|
75
75
|
- [Proposed & edge features](#proposed-and-edge-features)
|
76
|
+
- [Known limitations](#known-limitations)
|
76
77
|
|
77
78
|
## Overview
|
78
79
|
|
@@ -200,8 +201,6 @@ You can change the transpiler mode:
|
|
200
201
|
- Via environmental variable `RUBY_NEXT_TRANSPILE_MODE=ast`.
|
201
202
|
- Via CLI option ([see below](#cli)).
|
202
203
|
|
203
|
-
**NOTE:** For the time being, Unparser doesn't support Ruby 3.0 AST nodes, so we always use rewrite mode in Ruby 3.0+.
|
204
|
-
|
205
204
|
## CLI
|
206
205
|
|
207
206
|
Ruby Next ships with the command-line interface (`ruby-next`) which provides the following functionality:
|
@@ -231,7 +230,7 @@ Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
|
|
231
230
|
|
232
231
|
The behaviour depends on whether you transpile a single file or a directory:
|
233
232
|
|
234
|
-
- 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
|
233
|
+
- 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`, etc.). 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`).
|
235
234
|
|
236
235
|
- When transpiling a file and providing the output path as a _file_ path, only a single version is created. For example:
|
237
236
|
|
@@ -283,6 +282,8 @@ $ ruby-next core_ext -l --name=filter --name=deconstruct
|
|
283
282
|
- EnumeratorLazyFilterMap
|
284
283
|
- HashDeconstructKeys
|
285
284
|
- StructDeconstruct
|
285
|
+
|
286
|
+
...
|
286
287
|
```
|
287
288
|
|
288
289
|
### CLI configuration file
|
@@ -522,13 +523,38 @@ require "ruby-next/language/runtime"
|
|
522
523
|
|
523
524
|
### Supported edge features
|
524
525
|
|
525
|
-
|
526
|
+
It's too early, Ruby 3.1 has just been released. See its features in the [supported features list](./SUPPORTED_FEATURES.md).
|
526
527
|
|
527
528
|
### Supported proposed features
|
528
529
|
|
529
530
|
- _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
|
531
|
+
- Binding non-local variables in pattern matching (`42 => @v`) ([#18408](https://bugs.ruby-lang.org/issues/18408)).
|
532
|
+
|
533
|
+
## Known limitations
|
534
|
+
|
535
|
+
Ruby Next aims to be _reasonably compatible_ with MRI. That means, some edge cases could be uncovered. Below is the list of known limitations.
|
536
|
+
|
537
|
+
For gem authors, we recommend testing against all supported versions on CI to make sure you're not hit by edge cases.
|
538
|
+
|
539
|
+
### Enumerable methods
|
540
|
+
|
541
|
+
Using refinements (`using RubyNext`) for modules could lead to unexpected behaviour in case there is also a `prepend` for the same module in Ruby <2.7.
|
542
|
+
To eliminate this, we also refine Array (when appropriate), but other enumerables could be affected.
|
543
|
+
|
544
|
+
See [this issue](https://bugs.ruby-lang.org/issues/13446) for details.
|
545
|
+
|
546
|
+
### `Refinement#import_methods`
|
547
|
+
|
548
|
+
- Doesn't support importing methods generated with `eval`.
|
549
|
+
- Doesn't support aliases (both `alias` and `alias_method`).
|
550
|
+
- In JRuby, importing attribute accessors/readers/writers is not supported.
|
551
|
+
- When using AST transpiling in runtime, likely fails to import methods from a transpiled files (due to the updated source location).
|
552
|
+
|
553
|
+
See the [original PR](https://github.com/ruby-next/ruby-next/pull/85) for more details.
|
554
|
+
|
555
|
+
### Other
|
530
556
|
|
531
|
-
|
557
|
+
See [Parser's known issues](https://github.com/whitequark/parser#known-issues).
|
532
558
|
|
533
559
|
## Contributing
|
534
560
|
|
data/bin/transform
CHANGED
@@ -34,9 +34,13 @@ OptionParser.new do |opts|
|
|
34
34
|
transform_opts[:rewriters] = RubyNext::Language.current_rewriters
|
35
35
|
end
|
36
36
|
|
37
|
-
opts.on("--rewrite", "
|
37
|
+
opts.on("--rewrite", "Use rewrite transpiling mode") do
|
38
38
|
RubyNext::Language.mode = :rewrite
|
39
39
|
end
|
40
|
+
|
41
|
+
opts.on("--ast", "Use AST transpiling mode") do
|
42
|
+
RubyNext::Language.mode = :ast
|
43
|
+
end
|
40
44
|
end.parse!
|
41
45
|
|
42
46
|
puts RubyNext::Language.transform(contents, **transform_opts)
|
@@ -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|
|
@@ -174,54 +174,72 @@ module RubyNext
|
|
174
174
|
|
175
175
|
require "ruby-next/language/rewriters/base"
|
176
176
|
|
177
|
-
require "ruby-next/language/rewriters/
|
177
|
+
require "ruby-next/language/rewriters/2.1/numeric_literals"
|
178
|
+
rewriters << Rewriters::NumericLiterals
|
179
|
+
|
180
|
+
require "ruby-next/language/rewriters/2.1/required_kwargs"
|
181
|
+
rewriters << Rewriters::RequiredKwargs
|
182
|
+
|
183
|
+
require "ruby-next/language/rewriters/2.3/squiggly_heredoc"
|
178
184
|
rewriters << Rewriters::SquigglyHeredoc
|
179
185
|
|
180
|
-
require "ruby-next/language/rewriters/safe_navigation"
|
186
|
+
require "ruby-next/language/rewriters/2.3/safe_navigation"
|
181
187
|
rewriters << Rewriters::SafeNavigation
|
182
188
|
|
183
|
-
require "ruby-next/language/rewriters/
|
184
|
-
rewriters << Rewriters::
|
189
|
+
require "ruby-next/language/rewriters/2.7/args_forward"
|
190
|
+
rewriters << Rewriters::ArgsForward
|
185
191
|
|
186
|
-
require "ruby-next/language/rewriters/
|
187
|
-
rewriters << Rewriters::
|
192
|
+
require "ruby-next/language/rewriters/2.7/numbered_params"
|
193
|
+
rewriters << Rewriters::NumberedParams
|
188
194
|
|
189
|
-
require "ruby-next/language/rewriters/
|
190
|
-
rewriters << Rewriters::
|
195
|
+
require "ruby-next/language/rewriters/2.7/pattern_matching"
|
196
|
+
rewriters << Rewriters::PatternMatching
|
191
197
|
|
192
198
|
# Must be added after general args forward rewriter to become
|
193
199
|
# no-op in Ruby <2.7
|
194
|
-
require "ruby-next/language/rewriters/args_forward_leading"
|
200
|
+
require "ruby-next/language/rewriters/3.0/args_forward_leading"
|
195
201
|
rewriters << Rewriters::ArgsForwardLeading
|
196
202
|
|
197
|
-
require "ruby-next/language/rewriters/numbered_params"
|
198
|
-
rewriters << Rewriters::NumberedParams
|
199
|
-
|
200
|
-
require "ruby-next/language/rewriters/pattern_matching"
|
201
|
-
rewriters << Rewriters::PatternMatching
|
202
|
-
|
203
203
|
# Must be added after general pattern matching rewriter to become
|
204
204
|
# no-op in Ruby <2.7
|
205
|
-
require "ruby-next/language/rewriters/find_pattern"
|
205
|
+
require "ruby-next/language/rewriters/3.0/find_pattern"
|
206
206
|
rewriters << Rewriters::FindPattern
|
207
207
|
|
208
|
-
require "ruby-next/language/rewriters/in_pattern"
|
208
|
+
require "ruby-next/language/rewriters/3.0/in_pattern"
|
209
209
|
rewriters << Rewriters::InPattern
|
210
210
|
|
211
|
+
require "ruby-next/language/rewriters/3.0/endless_method"
|
212
|
+
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::EndlessMethod
|
213
|
+
|
214
|
+
require "ruby-next/language/rewriters/3.1/oneline_pattern_parensless"
|
215
|
+
rewriters << Rewriters::OnelinePatternParensless
|
216
|
+
|
217
|
+
require "ruby-next/language/rewriters/3.1/pin_vars_pattern"
|
218
|
+
rewriters << Rewriters::PinVarsPattern
|
219
|
+
|
220
|
+
require "ruby-next/language/rewriters/3.1/anonymous_block"
|
221
|
+
rewriters << Rewriters::AnonymousBlock
|
222
|
+
|
223
|
+
require "ruby-next/language/rewriters/3.1/refinement_import_methods"
|
224
|
+
rewriters << Rewriters::RefinementImportMethods
|
225
|
+
|
226
|
+
require "ruby-next/language/rewriters/3.1/endless_method_command"
|
227
|
+
rewriters << Rewriters::EndlessMethodCommand
|
228
|
+
|
229
|
+
require "ruby-next/language/rewriters/3.1/shorthand_hash"
|
230
|
+
rewriters << RubyNext::Language::Rewriters::ShorthandHash
|
231
|
+
|
211
232
|
# Put endless range in the end, 'cause Parser fails to parse it in
|
212
233
|
# pattern matching
|
213
|
-
require "ruby-next/language/rewriters/endless_range"
|
234
|
+
require "ruby-next/language/rewriters/2.6/endless_range"
|
214
235
|
rewriters << Rewriters::EndlessRange
|
215
236
|
|
216
|
-
require "ruby-next/language/rewriters/endless_method"
|
217
|
-
RubyNext::Language.rewriters << RubyNext::Language::Rewriters::EndlessMethod
|
218
|
-
|
219
237
|
if ENV["RUBY_NEXT_EDGE"] == "1"
|
220
|
-
require "ruby-next/language/edge"
|
238
|
+
require "ruby-next/language/rewriters/edge"
|
221
239
|
end
|
222
240
|
|
223
241
|
if ENV["RUBY_NEXT_PROPOSED"] == "1"
|
224
|
-
require "ruby-next/language/proposed"
|
242
|
+
require "ruby-next/language/rewriters/proposed"
|
225
243
|
end
|
226
244
|
end
|
227
245
|
end
|
@@ -48,11 +48,11 @@ module RubyNext
|
|
48
48
|
end
|
49
49
|
|
50
50
|
opts.on("--edge", "Enable edge (master) Ruby features") do |val|
|
51
|
-
require "ruby-next/language/edge"
|
51
|
+
require "ruby-next/language/rewriters/edge"
|
52
52
|
end
|
53
53
|
|
54
54
|
opts.on("--proposed", "Enable proposed/experimental Ruby features") do |val|
|
55
|
-
require "ruby-next/language/proposed"
|
55
|
+
require "ruby-next/language/rewriters/proposed"
|
56
56
|
end
|
57
57
|
|
58
58
|
opts.on(
|
File without changes
|
data/lib/.rbnext/2.3/ruby-next/language/rewriters/{pattern_matching.rb → 2.7/pattern_matching.rb}
RENAMED
@@ -243,6 +243,7 @@ module RubyNext
|
|
243
243
|
|
244
244
|
@deconstructed_keys = {}
|
245
245
|
@predicates = Predicates::CaseIn.new
|
246
|
+
@lvars = []
|
246
247
|
|
247
248
|
matchee_ast =
|
248
249
|
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
@@ -272,6 +273,7 @@ module RubyNext
|
|
272
273
|
|
273
274
|
@deconstructed_keys = {}
|
274
275
|
@predicates = Predicates::Noop.new
|
276
|
+
@lvars = []
|
275
277
|
|
276
278
|
matchee =
|
277
279
|
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
@@ -282,10 +284,12 @@ module RubyNext
|
|
282
284
|
arr: MATCHEE_ARR,
|
283
285
|
hash: MATCHEE_HASH
|
284
286
|
) do
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
287
|
+
with_declared_locals do
|
288
|
+
send(
|
289
|
+
:"#{node.children[1].type}_clause",
|
290
|
+
node.children[1]
|
291
|
+
)
|
292
|
+
end.then do |node|
|
289
293
|
s(:begin,
|
290
294
|
s(:or,
|
291
295
|
node,
|
@@ -306,6 +310,41 @@ module RubyNext
|
|
306
310
|
|
307
311
|
alias on_in_match on_match_pattern
|
308
312
|
|
313
|
+
def on_match_pattern_p(node)
|
314
|
+
context.track! self
|
315
|
+
|
316
|
+
@deconstructed_keys = {}
|
317
|
+
@predicates = Predicates::Noop.new
|
318
|
+
@lvars = []
|
319
|
+
|
320
|
+
matchee =
|
321
|
+
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
322
|
+
|
323
|
+
pattern =
|
324
|
+
locals.with(
|
325
|
+
matchee: MATCHEE,
|
326
|
+
arr: MATCHEE_ARR,
|
327
|
+
hash: MATCHEE_HASH
|
328
|
+
) do
|
329
|
+
with_declared_locals do
|
330
|
+
send(
|
331
|
+
:"#{node.children[1].type}_clause",
|
332
|
+
node.children[1]
|
333
|
+
)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
node.updated(
|
338
|
+
:and,
|
339
|
+
[
|
340
|
+
matchee,
|
341
|
+
pattern
|
342
|
+
]
|
343
|
+
).tap do |new_node|
|
344
|
+
replace(node.loc.expression, inline_blocks(unparse(new_node)))
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
309
348
|
private
|
310
349
|
|
311
350
|
def rewrite_case_in!(node, matchee, new_node)
|
@@ -363,13 +402,15 @@ module RubyNext
|
|
363
402
|
def build_when_clause(clause)
|
364
403
|
predicates.reset!
|
365
404
|
[
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
405
|
+
with_declared_locals do
|
406
|
+
with_guard(
|
407
|
+
send(
|
408
|
+
:"#{clause.children[0].type}_clause",
|
409
|
+
clause.children[0]
|
410
|
+
),
|
411
|
+
clause.children[1] # guard
|
412
|
+
)
|
413
|
+
end,
|
373
414
|
process(clause.children[2] || s(:nil)) # expression
|
374
415
|
].then do |children|
|
375
416
|
s(:when, *children)
|
@@ -407,13 +448,14 @@ module RubyNext
|
|
407
448
|
end
|
408
449
|
|
409
450
|
def match_var_clause(node, left = s(:lvar, locals[:matchee]))
|
410
|
-
|
451
|
+
var = node.children[0]
|
452
|
+
return s(:true) if var == :_
|
411
453
|
|
412
|
-
check_match_var_alternation!
|
454
|
+
check_match_var_alternation!(var)
|
413
455
|
|
414
456
|
s(:begin,
|
415
457
|
s(:or,
|
416
|
-
s(:begin,
|
458
|
+
s(:begin, build_var_assignment(var, left)),
|
417
459
|
s(:true)))
|
418
460
|
end
|
419
461
|
|
@@ -508,11 +550,10 @@ module RubyNext
|
|
508
550
|
def array_find(head, *nodes, tail)
|
509
551
|
index = s(:lvar, :__i__)
|
510
552
|
|
511
|
-
match_vars = []
|
512
|
-
|
513
553
|
head_match =
|
514
554
|
unless head.children.empty?
|
515
|
-
|
555
|
+
# we only need to call this to track the lvar usage
|
556
|
+
build_var_assignment(head.children[0].children[0])
|
516
557
|
|
517
558
|
arr_take = s(:send,
|
518
559
|
s(:lvar, locals[:arr]),
|
@@ -524,16 +565,19 @@ module RubyNext
|
|
524
565
|
|
525
566
|
tail_match =
|
526
567
|
unless tail.children.empty?
|
527
|
-
|
568
|
+
# we only need to call this to track the lvar usage
|
569
|
+
build_var_assignment(tail.children[0].children[0])
|
528
570
|
|
529
571
|
match_var_clause(tail.children[0], arr_slice(index + nodes.size, -1))
|
530
572
|
end
|
531
573
|
|
532
574
|
nodes.each do |node|
|
533
575
|
if node.type == :match_var
|
534
|
-
|
576
|
+
# we only need to call this to track the lvar usage
|
577
|
+
build_var_assignment(node.children[0])
|
535
578
|
elsif node.type == :match_as
|
536
|
-
|
579
|
+
# we only need to call this to track the lvar usage
|
580
|
+
build_var_assignment(node.children[1].children[0])
|
537
581
|
end
|
538
582
|
end
|
539
583
|
|
@@ -561,19 +605,7 @@ module RubyNext
|
|
561
605
|
s(:args,
|
562
606
|
s(:arg, :_),
|
563
607
|
s(:arg, :__i__)),
|
564
|
-
pattern)
|
565
|
-
next block if match_vars.empty?
|
566
|
-
|
567
|
-
# We need to declare match vars outside of `find` block
|
568
|
-
locals_declare = s(:begin, s(:masgn,
|
569
|
-
s(:mlhs, *match_vars),
|
570
|
-
s(:nil)))
|
571
|
-
|
572
|
-
s(:begin,
|
573
|
-
s(:or,
|
574
|
-
locals_declare,
|
575
|
-
block))
|
576
|
-
end
|
608
|
+
pattern)
|
577
609
|
end
|
578
610
|
|
579
611
|
def array_match_rest(index, node, *tail)
|
@@ -616,6 +648,14 @@ module RubyNext
|
|
616
648
|
end
|
617
649
|
end
|
618
650
|
|
651
|
+
def find_pattern_array_element(node, index)
|
652
|
+
element = arr_item_at(index)
|
653
|
+
locals.with(arr: locals[:arr, index]) do
|
654
|
+
predicates.push :"i#{index}"
|
655
|
+
find_pattern_clause(node, element).tap { predicates.pop }
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
619
659
|
def hash_pattern_array_element(node, index)
|
620
660
|
element = arr_item_at(index)
|
621
661
|
locals.with(hash: locals[:arr, index]) do
|
@@ -796,6 +836,15 @@ module RubyNext
|
|
796
836
|
end
|
797
837
|
end
|
798
838
|
|
839
|
+
def find_pattern_hash_element(node, key)
|
840
|
+
element = hash_value_at(key)
|
841
|
+
key_index = deconstructed_key(key)
|
842
|
+
locals.with(arr: locals[:hash, key_index]) do
|
843
|
+
predicates.push :"k#{key_index}"
|
844
|
+
find_pattern_clause(node, element).tap { predicates.pop }
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
799
848
|
def hash_element(head, *tail)
|
800
849
|
send("#{head.type}_hash_element", head).then do |node|
|
801
850
|
next node if tail.empty?
|
@@ -861,6 +910,10 @@ module RubyNext
|
|
861
910
|
match_var_clause(child, s(:lvar, locals[:hash]))
|
862
911
|
end
|
863
912
|
|
913
|
+
def pin_hash_element(node, index)
|
914
|
+
case_eq_hash_element node.children[0], index
|
915
|
+
end
|
916
|
+
|
864
917
|
def case_eq_hash_element(node, key)
|
865
918
|
case_eq_clause node, hash_value_at(key)
|
866
919
|
end
|
@@ -911,6 +964,24 @@ module RubyNext
|
|
911
964
|
end
|
912
965
|
end
|
913
966
|
|
967
|
+
def with_declared_locals
|
968
|
+
lvars.clear
|
969
|
+
node = yield
|
970
|
+
|
971
|
+
return node if lvars.empty?
|
972
|
+
|
973
|
+
# We need to declare match lvars outside of the outer `find` block,
|
974
|
+
# so we do that for that whole pattern
|
975
|
+
locals_declare = s(:begin, s(:masgn,
|
976
|
+
s(:mlhs, *lvars.uniq.map { |_1| s(:lvasgn, _1) }),
|
977
|
+
s(:nil)))
|
978
|
+
|
979
|
+
s(:begin,
|
980
|
+
s(:or,
|
981
|
+
locals_declare,
|
982
|
+
node))
|
983
|
+
end
|
984
|
+
|
914
985
|
def no_matching_pattern
|
915
986
|
raise_error(
|
916
987
|
:NoMatchingPatternError,
|
@@ -945,13 +1016,17 @@ module RubyNext
|
|
945
1016
|
|
946
1017
|
private
|
947
1018
|
|
948
|
-
attr_reader :deconstructed_keys, :predicates
|
1019
|
+
attr_reader :deconstructed_keys, :predicates, :lvars
|
949
1020
|
|
950
1021
|
# Raise SyntaxError if match-var is used within alternation
|
951
1022
|
# https://github.com/ruby/ruby/blob/672213ef1ca2b71312084057e27580b340438796/compile.c#L5900
|
952
1023
|
def check_match_var_alternation!(name)
|
953
1024
|
return unless locals.key?(ALTERNATION_MARKER)
|
954
1025
|
|
1026
|
+
if name.is_a?(::Parser::AST::Node)
|
1027
|
+
raise ::SyntaxError, "illegal variable in alternative pattern (#{name.children.first})"
|
1028
|
+
end
|
1029
|
+
|
955
1030
|
return if name.start_with?("_")
|
956
1031
|
|
957
1032
|
raise ::SyntaxError, "illegal variable in alternative pattern (#{name})"
|
@@ -968,6 +1043,18 @@ module RubyNext
|
|
968
1043
|
def inline_blocks(source)
|
969
1044
|
source.gsub(/(?:do|{) \|_, __i__\|\n\s*([^\n]+)\n\s*(?:end|})/, '{ |_, __i__| \1 }')
|
970
1045
|
end
|
1046
|
+
|
1047
|
+
# Value could be omitted for mass assignment
|
1048
|
+
def build_var_assignment(var, value = nil)
|
1049
|
+
unless var.is_a?(::Parser::AST::Node)
|
1050
|
+
lvars << var
|
1051
|
+
return s(:lvasgn, *[var, value].compact)
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
asign_type = :"#{var.type.to_s[0]}vasgn"
|
1055
|
+
|
1056
|
+
s(asign_type, *[var.children[0], value].compact)
|
1057
|
+
end
|
971
1058
|
end
|
972
1059
|
end
|
973
1060
|
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 ((((__safe_lvar__ = block) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.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 ((((__safe_lvar__ = block) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.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 ((((__safe_lvar__ = block) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.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
|
@@ -14,6 +14,18 @@ module RubyNext
|
|
14
14
|
|
15
15
|
class Base < ::Parser::TreeRewriter
|
16
16
|
class LocalsTracker
|
17
|
+
using(Module.new do
|
18
|
+
refine ::Parser::AST::Node do
|
19
|
+
def to_index
|
20
|
+
((((__safe_lvar__ = children) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.first) || type
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
refine ::Object do
|
25
|
+
alias to_index itself
|
26
|
+
end
|
27
|
+
end)
|
28
|
+
|
17
29
|
attr_reader :stacks
|
18
30
|
|
19
31
|
def initialize
|
@@ -28,7 +40,7 @@ module RubyNext
|
|
28
40
|
def [](name, suffix = nil)
|
29
41
|
fetch(name).then do |name|
|
30
42
|
next name unless suffix
|
31
|
-
:"#{name}#{suffix}__"
|
43
|
+
:"#{name}#{suffix.to_index}__"
|
32
44
|
end
|
33
45
|
end
|
34
46
|
|