ruby-next-core 0.14.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
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)