ruby-next-core 0.14.0 → 0.15.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55cb050984d91ee15965b5629bd998d7300af1051e7ad721f1dfed77fc6df7c7
4
- data.tar.gz: ac68a6f7da2b826b11a40be72948601f2d12ce8b1cd27ab9953977238c1e488d
3
+ metadata.gz: fc36fd395eedfde5c29e65f1f3a75c683630d5111349b3070d3b4e6d200477ad
4
+ data.tar.gz: e53e28aec62f7e425a98ba1eeb9591778ca1dbab13a0390113fc7440aab9663d
5
5
  SHA512:
6
- metadata.gz: 7cd0de569637973ff61866733a6a3d59dac4e317bf5e522b23a88fc5af8941e4a072ae1dfbf26163a661c71911f7801545cad00134cd826d4913f684b03473e8
7
- data.tar.gz: 2ebf8f0a42bd0771cfbc2f2240a3844436d3f108e5cec292ec2cd7e3bebef0a332088357a892cf3cd2b0c6835e9f39679cc472cd6a0ac4d120850194d8f2e783
6
+ metadata.gz: 50069c2ef6b59005d7efc640e1d84a74e17e4e385aaadce3ac54c2e3e061c7f71b727943140899210af997196f0bdf5a1157b86b97ce7af3de307e68a1cffe12
7
+ data.tar.gz: edf464100a254380bee320c586f48afc35782027303840fd319f32260ac5cf771c6f349d7447b9ed2f8a55bab424395664099dbd58600a05ffcd79e49080509f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.15.1 (2022-04-05)
6
+
7
+ - Fix transpiling `rescue` within nested blocks. ([@palkan][])
8
+
9
+ ## 0.15.0 (2022-03-21)
10
+
11
+ - Support IRB ([@palkan][])
12
+
13
+ - Create empty `.rbnext` folder during `nextify` if nothing to transpile. ([@palkan][])
14
+
15
+ This would prevent from auto-transpiling a library every time when no files should be transpiled.
16
+
17
+ - Auto-transpile using the current Ruby version. ([@palkan][])
18
+
19
+ - Support Pry. ([@baygeldin][])
20
+
21
+ - Add `rescue/ensure/else` within block rewriter for Ruby < 2.5. ([@fargelus][])
22
+
23
+ ## 0.14.1 (2022-01-21)
24
+
25
+ - Fix nested find patterns transpiling. ([@palkan][])
26
+
5
27
  ## 0.14.0 🎄
6
28
 
7
29
  - Add `Integer.try_convert`. ([@palkan][])
data/README.md CHANGED
@@ -71,6 +71,8 @@ _Please, submit a PR to add your project to the list!_
71
71
  - [`ruby -ruby-next`](#uby-next)
72
72
  - [Logging & Debugging](#logging-and-debugging)
73
73
  - [RuboCop](#rubocop)
74
+ - [Using with IRB](#irb)
75
+ - [Using with Pry](#pry)
74
76
  - [Using with EOL Rubies](#using-with-eol-rubies)
75
77
  - [Proposed & edge features](#proposed-and-edge-features)
76
78
  - [Known limitations](#known-limitations)
@@ -454,6 +456,38 @@ AllCops:
454
456
 
455
457
  **NOTE:** you need `ruby-next` gem available in the environment where you run RuboCop (having `ruby-next-core` is not enough).
456
458
 
459
+ ## IRB
460
+
461
+ Ruby Next supports IRB. In order to enable edge Ruby features for your REPL, add the following line to your `.irbrc`:
462
+
463
+ ```ruby
464
+ require "ruby-next/irb"
465
+ ```
466
+
467
+ Alternatively, you can require it at startup:
468
+
469
+ ```sh
470
+ irb -r ruby-next/irb
471
+ # or
472
+ irb -ruby-next/irb
473
+ ```
474
+
475
+ ## Pry
476
+
477
+ Ruby Next supports Pry. In order to enable edge Ruby features for your REPL, add the following line to your `.pryrc`:
478
+
479
+ ```ruby
480
+ require "ruby-next/pry"
481
+ ```
482
+
483
+ Alternatively, you can require it at startup:
484
+
485
+ ```sh
486
+ pry -r ruby-next/pry
487
+ # or
488
+ pry -ruby-next/pry
489
+ ```
490
+
457
491
  ## Using with EOL Rubies
458
492
 
459
493
  We currently provide support for Ruby 2.2, 2.3 and 2.4.
@@ -510,9 +544,9 @@ These features are disabled by default, you must opt-in in one of the following
510
544
  # It's important to load language module first
511
545
  require "ruby-next/language"
512
546
 
513
- require "ruby-next/language/edge"
547
+ require "ruby-next/language/rewriters/edge"
514
548
  # or
515
- require "ruby-next/language/proposed"
549
+ require "ruby-next/language/rewriters/proposed"
516
550
 
517
551
  # and then activate the runtime mode
518
552
  require "ruby-next/language/runtime"
@@ -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/, "") # rubocop:disable Performance/StringReplacement
65
+ "#{mod_name}#{camelized_method_name}".gsub(/\W/, "")
66
66
  end
67
67
 
68
68
  def build_location(trace_locations)
@@ -186,6 +186,9 @@ module RubyNext
186
186
  require "ruby-next/language/rewriters/2.3/safe_navigation"
187
187
  rewriters << Rewriters::SafeNavigation
188
188
 
189
+ require "ruby-next/language/rewriters/2.5/rescue_within_block"
190
+ rewriters << Rewriters::RescueWithinBlock
191
+
189
192
  require "ruby-next/language/rewriters/2.7/args_forward"
190
193
  rewriters << Rewriters::ArgsForward
191
194
 
@@ -24,6 +24,8 @@ module RubyNext
24
24
  contents = File.read(path)
25
25
  transpile path, contents
26
26
  end
27
+
28
+ ensure_rbnext!
27
29
  end
28
30
 
29
31
  def parse!(args)
@@ -150,6 +152,7 @@ module RubyNext
150
152
  transpile path, contents, version: version
151
153
  rescue SyntaxError, StandardError => e
152
154
  warn "Failed to transpile #{path}: #{e.class} — #{e.message}"
155
+ warn e.backtrace.take(10).join("\n") if ENV["RUBY_NEXT_DEBUG"] == "1"
153
156
  exit 1
154
157
  end
155
158
 
@@ -185,6 +188,17 @@ module RubyNext
185
188
  FileUtils.rm_r(next_dir_path)
186
189
  end
187
190
 
191
+ def ensure_rbnext!
192
+ return if CLI.dry_run? || stdout?
193
+
194
+ return if File.directory?(next_dir_path)
195
+
196
+ return if next_dir_path.end_with?(".rb")
197
+
198
+ FileUtils.mkdir_p next_dir_path
199
+ File.write(File.join(next_dir_path, ".keep"), "")
200
+ end
201
+
188
202
  def next_dir_path
189
203
  @next_dir_path ||= (out_path || File.join(lib_path, RUBY_NEXT_DIR))
190
204
  end
@@ -182,7 +182,6 @@ module RubyNext
182
182
  end
183
183
  end
184
184
 
185
- # rubocop:disable Style/MethodMissingSuper
186
185
  # rubocop:disable Style/MissingRespondToMissing
187
186
  class Noop < Base
188
187
  # Return node itself, no memoization
@@ -243,6 +242,7 @@ module RubyNext
243
242
 
244
243
  @deconstructed_keys = {}
245
244
  @predicates = Predicates::CaseIn.new
245
+ @lvars = []
246
246
 
247
247
  matchee_ast =
248
248
  s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
@@ -272,6 +272,7 @@ module RubyNext
272
272
 
273
273
  @deconstructed_keys = {}
274
274
  @predicates = Predicates::Noop.new
275
+ @lvars = []
275
276
 
276
277
  matchee =
277
278
  s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
@@ -282,10 +283,12 @@ module RubyNext
282
283
  arr: MATCHEE_ARR,
283
284
  hash: MATCHEE_HASH
284
285
  ) do
285
- send(
286
- :"#{node.children[1].type}_clause",
287
- node.children[1]
288
- ).then do |node|
286
+ with_declared_locals do
287
+ send(
288
+ :"#{node.children[1].type}_clause",
289
+ node.children[1]
290
+ )
291
+ end.then do |node|
289
292
  s(:begin,
290
293
  s(:or,
291
294
  node,
@@ -311,6 +314,7 @@ module RubyNext
311
314
 
312
315
  @deconstructed_keys = {}
313
316
  @predicates = Predicates::Noop.new
317
+ @lvars = []
314
318
 
315
319
  matchee =
316
320
  s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
@@ -321,10 +325,12 @@ module RubyNext
321
325
  arr: MATCHEE_ARR,
322
326
  hash: MATCHEE_HASH
323
327
  ) do
324
- send(
325
- :"#{node.children[1].type}_clause",
326
- node.children[1]
327
- )
328
+ with_declared_locals do
329
+ send(
330
+ :"#{node.children[1].type}_clause",
331
+ node.children[1]
332
+ )
333
+ end
328
334
  end
329
335
 
330
336
  node.updated(
@@ -395,13 +401,15 @@ module RubyNext
395
401
  def build_when_clause(clause)
396
402
  predicates.reset!
397
403
  [
398
- with_guard(
399
- send(
400
- :"#{clause.children[0].type}_clause",
401
- clause.children[0]
402
- ),
403
- clause.children[1] # guard
404
- ),
404
+ with_declared_locals do
405
+ with_guard(
406
+ send(
407
+ :"#{clause.children[0].type}_clause",
408
+ clause.children[0]
409
+ ),
410
+ clause.children[1] # guard
411
+ )
412
+ end,
405
413
  process(clause.children[2] || s(:nil)) # expression
406
414
  ].then do |children|
407
415
  s(:when, *children)
@@ -442,7 +450,7 @@ module RubyNext
442
450
  var = node.children[0]
443
451
  return s(:true) if var == :_
444
452
 
445
- check_match_var_alternation!(var) unless var.is_a?(::Parser::AST::Node)
453
+ check_match_var_alternation!(var)
446
454
 
447
455
  s(:begin,
448
456
  s(:or,
@@ -541,11 +549,10 @@ module RubyNext
541
549
  def array_find(head, *nodes, tail)
542
550
  index = s(:lvar, :__i__)
543
551
 
544
- match_vars = []
545
-
546
552
  head_match =
547
553
  unless head.children.empty?
548
- match_vars << build_var_assignment(head.children[0].children[0])
554
+ # we only need to call this to track the lvar usage
555
+ build_var_assignment(head.children[0].children[0])
549
556
 
550
557
  arr_take = s(:send,
551
558
  s(:lvar, locals[:arr]),
@@ -557,16 +564,19 @@ module RubyNext
557
564
 
558
565
  tail_match =
559
566
  unless tail.children.empty?
560
- match_vars << build_var_assignment(tail.children[0].children[0])
567
+ # we only need to call this to track the lvar usage
568
+ build_var_assignment(tail.children[0].children[0])
561
569
 
562
570
  match_var_clause(tail.children[0], arr_slice(index + nodes.size, -1))
563
571
  end
564
572
 
565
573
  nodes.each do |node|
566
574
  if node.type == :match_var
567
- match_vars << build_var_assignment(node.children[0])
575
+ # we only need to call this to track the lvar usage
576
+ build_var_assignment(node.children[0])
568
577
  elsif node.type == :match_as
569
- match_vars << build_var_assignment(node.children[1].children[0])
578
+ # we only need to call this to track the lvar usage
579
+ build_var_assignment(node.children[1].children[0])
570
580
  end
571
581
  end
572
582
 
@@ -594,19 +604,7 @@ module RubyNext
594
604
  s(:args,
595
605
  s(:arg, :_),
596
606
  s(:arg, :__i__)),
597
- pattern).then do |block|
598
- next block if match_vars.empty?
599
-
600
- # We need to declare match vars outside of `find` block
601
- locals_declare = s(:begin, s(:masgn,
602
- s(:mlhs, *match_vars),
603
- s(:nil)))
604
-
605
- s(:begin,
606
- s(:or,
607
- locals_declare,
608
- block))
609
- end
607
+ pattern)
610
608
  end
611
609
 
612
610
  def array_match_rest(index, node, *tail)
@@ -649,6 +647,14 @@ module RubyNext
649
647
  end
650
648
  end
651
649
 
650
+ def find_pattern_array_element(node, index)
651
+ element = arr_item_at(index)
652
+ locals.with(arr: locals[:arr, index]) do
653
+ predicates.push :"i#{index}"
654
+ find_pattern_clause(node, element).tap { predicates.pop }
655
+ end
656
+ end
657
+
652
658
  def hash_pattern_array_element(node, index)
653
659
  element = arr_item_at(index)
654
660
  locals.with(hash: locals[:arr, index]) do
@@ -829,6 +835,15 @@ module RubyNext
829
835
  end
830
836
  end
831
837
 
838
+ def find_pattern_hash_element(node, key)
839
+ element = hash_value_at(key)
840
+ key_index = deconstructed_key(key)
841
+ locals.with(arr: locals[:hash, key_index]) do
842
+ predicates.push :"k#{key_index}"
843
+ find_pattern_clause(node, element).tap { predicates.pop }
844
+ end
845
+ end
846
+
832
847
  def hash_element(head, *tail)
833
848
  send("#{head.type}_hash_element", head).then do |node|
834
849
  next node if tail.empty?
@@ -948,6 +963,24 @@ module RubyNext
948
963
  end
949
964
  end
950
965
 
966
+ def with_declared_locals
967
+ lvars.clear
968
+ node = yield
969
+
970
+ return node if lvars.empty?
971
+
972
+ # We need to declare match lvars outside of the outer `find` block,
973
+ # so we do that for that whole pattern
974
+ locals_declare = s(:begin, s(:masgn,
975
+ s(:mlhs, *lvars.uniq.map { |_1| s(:lvasgn, _1) }),
976
+ s(:nil)))
977
+
978
+ s(:begin,
979
+ s(:or,
980
+ locals_declare,
981
+ node))
982
+ end
983
+
951
984
  def no_matching_pattern
952
985
  raise_error(
953
986
  :NoMatchingPatternError,
@@ -982,13 +1015,17 @@ module RubyNext
982
1015
 
983
1016
  private
984
1017
 
985
- attr_reader :deconstructed_keys, :predicates
1018
+ attr_reader :deconstructed_keys, :predicates, :lvars
986
1019
 
987
1020
  # Raise SyntaxError if match-var is used within alternation
988
1021
  # https://github.com/ruby/ruby/blob/672213ef1ca2b71312084057e27580b340438796/compile.c#L5900
989
1022
  def check_match_var_alternation!(name)
990
1023
  return unless locals.key?(ALTERNATION_MARKER)
991
1024
 
1025
+ if name.is_a?(::Parser::AST::Node)
1026
+ raise ::SyntaxError, "illegal variable in alternative pattern (#{name.children.first})"
1027
+ end
1028
+
992
1029
  return if name.start_with?("_")
993
1030
 
994
1031
  raise ::SyntaxError, "illegal variable in alternative pattern (#{name})"
@@ -1008,7 +1045,10 @@ module RubyNext
1008
1045
 
1009
1046
  # Value could be omitted for mass assignment
1010
1047
  def build_var_assignment(var, value = nil)
1011
- return s(:lvasgn, *[var, value].compact) unless var.is_a?(::Parser::AST::Node)
1048
+ unless var.is_a?(::Parser::AST::Node)
1049
+ lvars << var
1050
+ return s(:lvasgn, *[var, value].compact)
1051
+ end
1012
1052
 
1013
1053
  asign_type = :"#{var.type.to_s[0]}vasgn"
1014
1054
 
@@ -65,7 +65,7 @@ module RubyNext
65
65
  end
66
66
 
67
67
  class << self
68
- # Returns true if the syntax is supported
68
+ # Returns true if the syntax is not supported
69
69
  # by the current Ruby (performs syntax check, not version check)
70
70
  def unsupported_syntax?
71
71
  save_verbose, $VERBOSE = $VERBOSE, nil
@@ -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/, "") # rubocop:disable Performance/StringReplacement
65
+ "#{mod_name}#{camelized_method_name}".gsub(/\W/, "")
66
66
  end
67
67
 
68
68
  def build_location(trace_locations)