ruby-next-core 0.14.0 → 1.0.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -0
  3. data/README.md +163 -56
  4. data/bin/mspec +11 -0
  5. data/lib/.rbnext/2.1/ruby-next/commands/nextify.rb +295 -0
  6. data/lib/.rbnext/2.1/ruby-next/core.rb +12 -4
  7. data/lib/.rbnext/2.1/ruby-next/language.rb +62 -12
  8. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +97 -3
  9. data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
  10. data/lib/.rbnext/2.3/ruby-next/core/data.rb +163 -0
  11. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
  12. data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/args_forward.rb +134 -0
  13. data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +122 -47
  14. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -32
  15. data/lib/.rbnext/2.3/ruby-next/utils.rb +3 -22
  16. data/lib/.rbnext/2.6/ruby-next/core/data.rb +163 -0
  17. data/lib/.rbnext/2.7/ruby-next/core/data.rb +163 -0
  18. data/lib/.rbnext/2.7/ruby-next/core.rb +12 -4
  19. data/lib/.rbnext/2.7/ruby-next/language/paco_parsers/string_literals.rb +109 -0
  20. data/lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
  21. data/lib/.rbnext/2.7/ruby-next/language/rewriters/text.rb +132 -0
  22. data/lib/.rbnext/3.2/ruby-next/commands/base.rb +55 -0
  23. data/lib/.rbnext/3.2/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
  24. data/lib/.rbnext/3.2/ruby-next/rubocop.rb +210 -0
  25. data/lib/ruby-next/cli.rb +10 -15
  26. data/lib/ruby-next/commands/nextify.rb +99 -3
  27. data/lib/ruby-next/config.rb +31 -4
  28. data/lib/ruby-next/core/data.rb +163 -0
  29. data/lib/ruby-next/core/matchdata/deconstruct.rb +9 -0
  30. data/lib/ruby-next/core/matchdata/deconstruct_keys.rb +20 -0
  31. data/lib/ruby-next/core/matchdata/named_captures.rb +11 -0
  32. data/lib/ruby-next/core/proc/compose.rb +0 -1
  33. data/lib/ruby-next/core/refinement/import.rb +44 -36
  34. data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
  35. data/lib/ruby-next/core.rb +11 -3
  36. data/lib/ruby-next/irb.rb +24 -0
  37. data/lib/ruby-next/language/bootsnap.rb +2 -25
  38. data/lib/ruby-next/language/eval.rb +4 -4
  39. data/lib/ruby-next/language/paco_parser.rb +7 -0
  40. data/lib/ruby-next/language/paco_parsers/base.rb +47 -0
  41. data/lib/ruby-next/language/paco_parsers/comments.rb +26 -0
  42. data/lib/ruby-next/language/paco_parsers/string_literals.rb +109 -0
  43. data/lib/ruby-next/language/parser.rb +31 -6
  44. data/lib/ruby-next/language/rewriters/2.5/rescue_within_block.rb +41 -0
  45. data/lib/ruby-next/language/rewriters/2.7/args_forward.rb +57 -0
  46. data/lib/ruby-next/language/rewriters/2.7/pattern_matching.rb +120 -45
  47. data/lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb +2 -2
  48. data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +1 -1
  49. data/lib/ruby-next/language/rewriters/3.1/shorthand_hash.rb +2 -1
  50. data/lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb +104 -0
  51. data/lib/ruby-next/language/rewriters/abstract.rb +57 -0
  52. data/lib/ruby-next/language/rewriters/base.rb +6 -32
  53. data/lib/ruby-next/language/rewriters/edge/it_param.rb +58 -0
  54. data/lib/ruby-next/language/rewriters/edge.rb +12 -0
  55. data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +3 -0
  56. data/lib/ruby-next/language/rewriters/proposed/method_reference.rb +9 -20
  57. data/lib/ruby-next/language/rewriters/text.rb +132 -0
  58. data/lib/ruby-next/language/runtime.rb +9 -86
  59. data/lib/ruby-next/language/setup.rb +5 -2
  60. data/lib/ruby-next/language/unparser.rb +5 -0
  61. data/lib/ruby-next/language.rb +62 -12
  62. data/lib/ruby-next/pry.rb +90 -0
  63. data/lib/ruby-next/rubocop.rb +2 -0
  64. data/lib/ruby-next/utils.rb +3 -22
  65. data/lib/ruby-next/version.rb +1 -1
  66. data/lib/uby-next/irb.rb +3 -0
  67. data/lib/uby-next/pry.rb +3 -0
  68. data/lib/uby-next.rb +2 -2
  69. metadata +70 -10
@@ -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)
@@ -417,15 +425,15 @@ module RubyNext
417
425
  s(:begin,
418
426
  s(:and,
419
427
  node,
420
- send(:"#{pattern.type}_clause", pattern)))
428
+ send(:"#{pattern.type}_clause", pattern, right)))
421
429
  end
422
430
  end
423
431
 
424
- def match_alt_clause(node)
432
+ def match_alt_clause(node, matchee = s(:lvar, locals[:matchee]))
425
433
  children = locals.with(ALTERNATION_MARKER => true) do
426
434
  node.children.map.with_index do |child, i|
427
435
  predicates.terminate! if i == 1
428
- send :"#{child.type}_clause", child
436
+ send :"#{child.type}_clause", child, matchee
429
437
  end
430
438
  end
431
439
  s(:begin, s(:or, *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
@@ -657,6 +663,14 @@ module RubyNext
657
663
  end
658
664
  end
659
665
 
666
+ def const_pattern_array_element(node, index)
667
+ element = arr_item_at(index)
668
+ locals.with(arr: locals[:arr, index]) do
669
+ predicates.push :"i#{index}"
670
+ const_pattern_clause(node, element).tap { predicates.pop }
671
+ end
672
+ end
673
+
660
674
  def match_alt_array_element(node, index)
661
675
  children = node.children.map do |child, i|
662
676
  send :"#{child.type}_array_element", child, index
@@ -665,11 +679,19 @@ module RubyNext
665
679
  end
666
680
 
667
681
  def match_var_array_element(node, index)
668
- match_var_clause(node, arr_item_at(index))
682
+ element = arr_item_at(index)
683
+ locals.with(arr: locals[:arr, index]) do
684
+ predicates.push :"i#{index}"
685
+ match_var_clause(node, element).tap { predicates.pop }
686
+ end
669
687
  end
670
688
 
671
689
  def match_as_array_element(node, index)
672
- match_as_clause(node, arr_item_at(index))
690
+ element = arr_item_at(index)
691
+ locals.with(arr: locals[:arr, index]) do
692
+ predicates.push :"i#{index}"
693
+ match_as_clause(node, element).tap { predicates.pop }
694
+ end
673
695
  end
674
696
 
675
697
  def pin_array_element(node, index)
@@ -829,6 +851,24 @@ module RubyNext
829
851
  end
830
852
  end
831
853
 
854
+ def find_pattern_hash_element(node, key)
855
+ element = hash_value_at(key)
856
+ key_index = deconstructed_key(key)
857
+ locals.with(arr: locals[:hash, key_index]) do
858
+ predicates.push :"k#{key_index}"
859
+ find_pattern_clause(node, element).tap { predicates.pop }
860
+ end
861
+ end
862
+
863
+ def const_pattern_hash_element(node, key)
864
+ element = hash_value_at(key)
865
+ key_index = deconstructed_key(key)
866
+ locals.with(hash: locals[:hash, key_index]) do
867
+ predicates.push :"k#{key_index}"
868
+ const_pattern_clause(node, element).tap { predicates.pop }
869
+ end
870
+ end
871
+
832
872
  def hash_element(head, *tail)
833
873
  send("#{head.type}_hash_element", head).then do |node|
834
874
  next node if tail.empty?
@@ -869,12 +909,22 @@ module RubyNext
869
909
  end
870
910
 
871
911
  def match_as_hash_element(node, key)
872
- match_as_clause(node, hash_value_at(key))
912
+ element = hash_value_at(key)
913
+ key_index = deconstructed_key(key)
914
+ locals.with(hash: locals[:hash, key_index]) do
915
+ predicates.push :"k#{key_index}"
916
+ match_as_clause(node, element).tap { predicates.pop }
917
+ end
873
918
  end
874
919
 
875
920
  def match_var_hash_element(node, key = nil)
876
921
  key ||= node.children[0]
877
- match_var_clause(node, hash_value_at(key))
922
+ element = hash_value_at(key)
923
+ key_index = deconstructed_key(key)
924
+ locals.with(hash: locals[:hash, key_index]) do
925
+ predicates.push :"k#{key_index}"
926
+ match_var_clause(node, element).tap { predicates.pop }
927
+ end
878
928
  end
879
929
 
880
930
  def match_nil_pattern_hash_element(node, _key = nil)
@@ -948,6 +998,24 @@ module RubyNext
948
998
  end
949
999
  end
950
1000
 
1001
+ def with_declared_locals
1002
+ lvars.clear
1003
+ node = yield
1004
+
1005
+ return node if lvars.empty?
1006
+
1007
+ # We need to declare match lvars outside of the outer `find` block,
1008
+ # so we do that for that whole pattern
1009
+ locals_declare = s(:begin, s(:masgn,
1010
+ s(:mlhs, *lvars.uniq.map { s(:lvasgn, _1) }),
1011
+ s(:nil)))
1012
+
1013
+ s(:begin,
1014
+ s(:or,
1015
+ locals_declare,
1016
+ node))
1017
+ end
1018
+
951
1019
  def no_matching_pattern
952
1020
  raise_error(
953
1021
  :NoMatchingPatternError,
@@ -982,13 +1050,17 @@ module RubyNext
982
1050
 
983
1051
  private
984
1052
 
985
- attr_reader :deconstructed_keys, :predicates
1053
+ attr_reader :deconstructed_keys, :predicates, :lvars
986
1054
 
987
1055
  # Raise SyntaxError if match-var is used within alternation
988
1056
  # https://github.com/ruby/ruby/blob/672213ef1ca2b71312084057e27580b340438796/compile.c#L5900
989
1057
  def check_match_var_alternation!(name)
990
1058
  return unless locals.key?(ALTERNATION_MARKER)
991
1059
 
1060
+ if name.is_a?(::Parser::AST::Node)
1061
+ raise ::SyntaxError, "illegal variable in alternative pattern (#{name.children.first})"
1062
+ end
1063
+
992
1064
  return if name.start_with?("_")
993
1065
 
994
1066
  raise ::SyntaxError, "illegal variable in alternative pattern (#{name})"
@@ -1008,7 +1080,10 @@ module RubyNext
1008
1080
 
1009
1081
  # Value could be omitted for mass assignment
1010
1082
  def build_var_assignment(var, value = nil)
1011
- return s(:lvasgn, *[var, value].compact) unless var.is_a?(::Parser::AST::Node)
1083
+ unless var.is_a?(::Parser::AST::Node)
1084
+ lvars << var
1085
+ return s(:lvasgn, *[var, value].compact)
1086
+ end
1012
1087
 
1013
1088
  asign_type = :"#{var.type.to_s[0]}vasgn"
1014
1089
 
@@ -50,7 +50,7 @@ module RubyNext
50
50
 
51
51
  return false unless fargs
52
52
 
53
- node.children.index(fargs) > (node.type == :send ? 2 : 0)
53
+ node.children.index(fargs) > ((node.type == :send) ? 2 : 0)
54
54
  end
55
55
 
56
56
  def method_with_leading_arg(node)
@@ -62,7 +62,7 @@ module RubyNext
62
62
  end
63
63
 
64
64
  def def_with_leading_farg(node)
65
- args = node.type == :defs ? node.children[2] : node.children[1]
65
+ args = (node.type == :defs) ? node.children[2] : node.children[1]
66
66
  args = args.children
67
67
 
68
68
  farg = args.detect { |child| child.type == :forward_arg }
@@ -25,7 +25,7 @@ module RubyNext
25
25
 
26
26
  context.track! self
27
27
 
28
- left_p, right_p = pattern.type == :array_pattern ? %w([ ]) : %w[{ }]
28
+ left_p, right_p = (pattern.type == :array_pattern) ? %w([ ]) : %w[{ }]
29
29
 
30
30
  insert_before(pattern.loc.expression, left_p)
31
31
  insert_after(pattern.loc.expression, right_p)
@@ -9,7 +9,8 @@ module RubyNext
9
9
  MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
10
10
 
11
11
  def on_pair(node)
12
- return super(node) unless node.children[0].loc.last_column == node.children[1].loc.last_column
12
+ return super(node) unless (node.children[0].loc.last_column == node.children[1].loc.last_column) &&
13
+ (node.children[1].loc.first_line == node.children[1].loc.last_line)
13
14
 
14
15
  context.track! self
15
16
 
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class AnonymousRestArgs < Base
7
+ NAME = "anonymous-rest-args"
8
+ SYNTAX_PROBE = "obj = Object.new; def obj.foo(*) bar(*); end"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.2.0")
10
+
11
+ REST = :__rest__
12
+ KWREST = :__kwrest__
13
+
14
+ def on_args(node)
15
+ rest = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :restarg && child.children.first.nil? }
16
+ kwrest = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :kwrestarg && child.children.first.nil? }
17
+
18
+ return super unless rest || kwrest
19
+
20
+ context.track! self
21
+
22
+ replace(rest.loc.expression, "*#{REST}") if rest
23
+ replace(kwrest.loc.expression, "**#{KWREST}") if kwrest
24
+
25
+ new_args = node.children.map do |child|
26
+ if child == rest
27
+ s(:restarg, REST)
28
+ elsif child == kwrest
29
+ s(:kwrestarg, KWREST)
30
+ else
31
+ child
32
+ end
33
+ end
34
+
35
+ node.updated(:args, new_args)
36
+ end
37
+
38
+ def on_send(node)
39
+ return super unless forwarded_args?(node)
40
+
41
+ process_send_args(node)
42
+ end
43
+
44
+ def on_super(node)
45
+ return super unless forwarded_args?(node)
46
+
47
+ process_send_args(node)
48
+ end
49
+
50
+ private
51
+
52
+ def forwarded_args?(node)
53
+ node.children.each do |child|
54
+ next unless child.is_a?(::Parser::AST::Node)
55
+
56
+ if child.type == :forwarded_restarg
57
+ return true
58
+ elsif child.type == :kwargs
59
+ child.children.each do |kwarg|
60
+ next unless kwarg.is_a?(::Parser::AST::Node)
61
+
62
+ return true if kwarg.type == :forwarded_kwrestarg
63
+ end
64
+ end
65
+ end
66
+
67
+ false
68
+ end
69
+
70
+ def process_send_args(node)
71
+ process(
72
+ node.updated(
73
+ nil,
74
+ node.children.map do |child|
75
+ next child unless child.is_a?(::Parser::AST::Node)
76
+
77
+ if child.type == :forwarded_restarg
78
+ replace(child.loc.expression, "*#{REST}")
79
+ s(:ksplat, s(:lvar, REST))
80
+ elsif child.type == :kwargs
81
+ child.updated(
82
+ nil,
83
+ child.children.map do |kwarg|
84
+ next kwarg unless kwarg.is_a?(::Parser::AST::Node)
85
+
86
+ if kwarg.type == :forwarded_kwrestarg
87
+ replace(kwarg.loc.expression, "**#{KWREST}")
88
+ s(:kwsplat, s(:lvar, KWREST))
89
+ else
90
+ kwarg
91
+ end
92
+ end
93
+ )
94
+ else
95
+ child
96
+ end
97
+ end
98
+ )
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class Abstract < ::Parser::TreeRewriter
7
+ NAME = "custom-rewriter"
8
+ SYNTAX_PROBE = "1 = [}"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new(RubyNext::NEXT_VERSION)
10
+
11
+ class << self
12
+ # Returns true if the syntax is not supported
13
+ # by the current Ruby (performs syntax check, not version check)
14
+ def unsupported_syntax?
15
+ save_verbose, $VERBOSE = $VERBOSE, nil
16
+ eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
17
+ Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
18
+ false
19
+ rescue SyntaxError, StandardError
20
+ true
21
+ ensure
22
+ $VERBOSE = save_verbose
23
+ end
24
+
25
+ # Returns true if the syntax is supported
26
+ # by the specified version
27
+ def unsupported_version?(version)
28
+ version < self::MIN_SUPPORTED_VERSION
29
+ end
30
+
31
+ def text?
32
+ false
33
+ end
34
+
35
+ def ast?
36
+ false
37
+ end
38
+
39
+ private
40
+
41
+ def transform(source)
42
+ Language.transform(source, rewriters: [self], using: false)
43
+ end
44
+ end
45
+
46
+ def initialize(context)
47
+ @context = context
48
+ super()
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :context
54
+ end
55
+ end
56
+ end
57
+ end
@@ -13,7 +13,7 @@ module RubyNext
13
13
 
14
14
  MSG
15
15
 
16
- class Base < ::Parser::TreeRewriter
16
+ class Base < Abstract
17
17
  class LocalsTracker
18
18
  using(Module.new do
19
19
  refine ::Parser::AST::Node do
@@ -65,39 +65,15 @@ module RubyNext
65
65
  end
66
66
  end
67
67
 
68
- class << self
69
- # Returns true if the syntax is supported
70
- # by the current Ruby (performs syntax check, not version check)
71
- def unsupported_syntax?
72
- save_verbose, $VERBOSE = $VERBOSE, nil
73
- eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
74
- Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
75
- false
76
- rescue SyntaxError, StandardError
77
- true
78
- ensure
79
- $VERBOSE = save_verbose
80
- end
81
-
82
- # Returns true if the syntax is supported
83
- # by the specified version
84
- def unsupported_version?(version)
85
- self::MIN_SUPPORTED_VERSION > version
86
- end
87
-
88
- private
68
+ attr_reader :locals
89
69
 
90
- def transform(source)
91
- Language.transform(source, rewriters: [self], using: false)
92
- end
70
+ def self.ast?
71
+ true
93
72
  end
94
73
 
95
- attr_reader :locals
96
-
97
- def initialize(context)
98
- @context = context
74
+ def initialize(*args)
99
75
  @locals = LocalsTracker.new
100
- super()
76
+ super
101
77
  end
102
78
 
103
79
  def s(type, *children)
@@ -145,8 +121,6 @@ module RubyNext
145
121
 
146
122
  Unparser.unparse(ast).chomp
147
123
  end
148
-
149
- attr_reader :context
150
124
  end
151
125
  end
152
126
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class ItParam < Base
7
+ using RubyNext
8
+
9
+ NAME = "it-param"
10
+ SYNTAX_PROBE = "proc { it.keys }.call({})"
11
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.4.0")
12
+
13
+ def on_block(node)
14
+ proc_or_lambda, args, body = *node.children
15
+
16
+ return super unless block_has_it?(body)
17
+
18
+ context.track! self
19
+
20
+ new_body = s(:begin,
21
+ s(:lvasgn, :it, s(:lvar, :_1)),
22
+ body)
23
+
24
+ insert_before(body.loc.expression, "it = _1;")
25
+
26
+ process(
27
+ node.updated(:numblock, [
28
+ proc_or_lambda,
29
+ args,
30
+ new_body
31
+ ])
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ # It's important to check if the current block refers to `it` variable somewhere
38
+ # (and not within a nested block), so we don't declare numbered params
39
+ def block_has_it?(node)
40
+ # traverse node children deeply
41
+ tree = [node]
42
+
43
+ while (child = tree.shift)
44
+ return true if it?(child)
45
+
46
+ if child.is_a?(Parser::AST::Node)
47
+ tree.unshift(*child.children.select { |c| c.is_a?(Parser::AST::Node) && c.type != :block && c.type != :numblock })
48
+ end
49
+ end
50
+ end
51
+
52
+ def it?(node)
53
+ node.is_a?(Parser::AST::Node) && node.type == :send && node.children[0].nil? && node.children[1] == :it && node.children[2].nil?
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Load edge Ruby features
4
+
5
+ require "ruby-next/language/rewriters/edge/it_param"
6
+
7
+ # We must add this rewriter before nubmered params rewriter to allow it to transform the source code further
8
+
9
+ number_params = RubyNext::Language.rewriters.index(RubyNext::Language::Rewriters::NumberedParams)
10
+
11
+ if number_params
12
+ RubyNext::Language.rewriters.insert(number_params, RubyNext::Language::Rewriters::ItParam)
13
+ else
14
+ RubyNext::Language.rewriters << RubyNext::Language::Rewriters::ItParam
15
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "parser/rubynext"
4
+ RubyNext::Language.parser_class = ::Parser::RubyNext
5
+
3
6
  module RubyNext
4
7
  module Language
5
8
  module Rewriters