ruby-next-core 0.8.0 → 0.10.0
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 +47 -0
- data/README.md +85 -11
- data/bin/transform +9 -1
- data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
- data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
- data/lib/ruby-next.rb +8 -5
- data/lib/ruby-next/cli.rb +2 -2
- data/lib/ruby-next/commands/core_ext.rb +2 -2
- data/lib/ruby-next/commands/nextify.rb +64 -22
- data/lib/ruby-next/core.rb +39 -22
- data/lib/ruby-next/core/array/deconstruct.rb +9 -9
- data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
- data/lib/ruby-next/core/enumerable/filter.rb +8 -8
- data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
- data/lib/ruby-next/core/enumerable/tally.rb +7 -7
- data/lib/ruby-next/core/enumerator/produce.rb +12 -12
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
- data/lib/ruby-next/core/hash/except.rb +11 -0
- data/lib/ruby-next/core/hash/merge.rb +8 -8
- data/lib/ruby-next/core/kernel/then.rb +2 -2
- data/lib/ruby-next/core/proc/compose.rb +11 -11
- data/lib/ruby-next/core/string/split.rb +6 -6
- data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
- data/lib/ruby-next/core/symbol/end_with.rb +4 -4
- data/lib/ruby-next/core/symbol/start_with.rb +4 -4
- data/lib/ruby-next/core/time/ceil.rb +6 -5
- data/lib/ruby-next/core/time/floor.rb +4 -4
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
- data/lib/ruby-next/core_ext.rb +2 -2
- data/lib/ruby-next/language.rb +31 -5
- data/lib/ruby-next/language/eval.rb +10 -8
- data/lib/ruby-next/language/proposed.rb +3 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
- data/lib/ruby-next/language/rewriters/base.rb +2 -2
- data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
- data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
- data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +105 -13
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +2 -1
- data/lib/ruby-next/language/rewriters/runtime.rb +6 -0
- data/lib/ruby-next/language/rewriters/runtime/dir.rb +32 -0
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
- data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
- data/lib/ruby-next/language/runtime.rb +3 -2
- data/lib/ruby-next/logging.rb +1 -1
- data/lib/ruby-next/rubocop.rb +15 -9
- data/lib/ruby-next/setup_self.rb +22 -0
- data/lib/ruby-next/utils.rb +30 -0
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +8 -4
- metadata +22 -7
| @@ -3,14 +3,16 @@ | |
| 3 3 | 
             
            module RubyNext
         | 
| 4 4 | 
             
              module Language
         | 
| 5 5 | 
             
                module KernelEval
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
                     | 
| 8 | 
            -
                       | 
| 9 | 
            -
                         | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 6 | 
            +
                  if Utils.refine_modules?
         | 
| 7 | 
            +
                    refine Kernel do
         | 
| 8 | 
            +
                      def eval(source, bind = nil, *args)
         | 
| 9 | 
            +
                        new_source = ::RubyNext::Language::Runtime.transform(
         | 
| 10 | 
            +
                          source,
         | 
| 11 | 
            +
                          using: bind&.receiver == TOPLEVEL_BINDING.receiver || bind&.receiver&.is_a?(Module)
         | 
| 12 | 
            +
                        )
         | 
| 13 | 
            +
                        RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
         | 
| 14 | 
            +
                        super new_source, bind, *args
         | 
| 15 | 
            +
                      end
         | 
| 14 16 | 
             
                    end
         | 
| 15 17 | 
             
                  end
         | 
| 16 18 | 
             
                end
         | 
| @@ -4,3 +4,6 @@ | |
| 4 4 |  | 
| 5 5 | 
             
            require "ruby-next/language/rewriters/method_reference"
         | 
| 6 6 | 
             
            RubyNext::Language.rewriters << RubyNext::Language::Rewriters::MethodReference
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require "ruby-next/language/rewriters/shorthand_hash"
         | 
| 9 | 
            +
            RubyNext::Language.rewriters << RubyNext::Language::Rewriters::ShorthandHash
         | 
| @@ -4,16 +4,19 @@ module RubyNext | |
| 4 4 | 
             
              module Language
         | 
| 5 5 | 
             
                module Rewriters
         | 
| 6 6 | 
             
                  class ArgsForward < Base
         | 
| 7 | 
            -
                     | 
| 8 | 
            -
                     | 
| 7 | 
            +
                    NAME = "args-forward"
         | 
| 8 | 
            +
                    SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(1, ...); end"
         | 
| 9 | 
            +
                    MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.2")
         | 
| 9 10 |  | 
| 10 11 | 
             
                    REST = :__rest__
         | 
| 11 12 | 
             
                    BLOCK = :__block__
         | 
| 12 13 |  | 
| 13 | 
            -
                    def  | 
| 14 | 
            +
                    def on_forward_arg(node)
         | 
| 14 15 | 
             
                      context.track! self
         | 
| 15 16 |  | 
| 16 | 
            -
                       | 
| 17 | 
            +
                      node = super(node)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      replace(node.loc.expression, "*#{REST}, &#{BLOCK}")
         | 
| 17 20 |  | 
| 18 21 | 
             
                      node.updated(
         | 
| 19 22 | 
             
                        :args,
         | 
| @@ -25,34 +28,35 @@ module RubyNext | |
| 25 28 | 
             
                    end
         | 
| 26 29 |  | 
| 27 30 | 
             
                    def on_send(node)
         | 
| 28 | 
            -
                       | 
| 31 | 
            +
                      fargs = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
         | 
| 32 | 
            +
                      return super(node) unless fargs
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      process_fargs(node, fargs)
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    def on_super(node)
         | 
| 38 | 
            +
                      fargs = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
         | 
| 39 | 
            +
                      return super(node) unless fargs
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      process_fargs(node, fargs)
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    private
         | 
| 29 45 |  | 
| 30 | 
            -
             | 
| 46 | 
            +
                    def process_fargs(node, fargs)
         | 
| 47 | 
            +
                      replace(fargs.loc.expression, "*#{REST}, &#{BLOCK}")
         | 
| 31 48 |  | 
| 32 49 | 
             
                      process(
         | 
| 33 50 | 
             
                        node.updated(
         | 
| 34 51 | 
             
                          nil,
         | 
| 35 52 | 
             
                          [
         | 
| 36 | 
            -
                            *node.children | 
| 53 | 
            +
                            *node.children.take(node.children.index(fargs)),
         | 
| 37 54 | 
             
                            *forwarded_args
         | 
| 38 55 | 
             
                          ]
         | 
| 39 56 | 
             
                        )
         | 
| 40 57 | 
             
                      )
         | 
| 41 58 | 
             
                    end
         | 
| 42 59 |  | 
| 43 | 
            -
                    def on_super(node)
         | 
| 44 | 
            -
                      return super(node) unless node.children[0]&.type == :forwarded_args
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                      replace(node.children[0].loc.expression, "*#{REST}, &#{BLOCK}")
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                      node.updated(
         | 
| 49 | 
            -
                        nil,
         | 
| 50 | 
            -
                        forwarded_args
         | 
| 51 | 
            -
                      )
         | 
| 52 | 
            -
                    end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                    private
         | 
| 55 | 
            -
             | 
| 56 60 | 
             
                    def forwarded_args
         | 
| 57 61 | 
             
                      [
         | 
| 58 62 | 
             
                        s(:splat, s(:lvar, REST)),
         | 
| @@ -34,7 +34,7 @@ module RubyNext | |
| 34 34 | 
             
                      end
         | 
| 35 35 |  | 
| 36 36 | 
             
                      def key?(name)
         | 
| 37 | 
            -
                        !!fetch(name) { false }
         | 
| 37 | 
            +
                        !!fetch(name) { false } # rubocop:disable Style/RedundantFetchBlock
         | 
| 38 38 | 
             
                      end
         | 
| 39 39 |  | 
| 40 40 | 
             
                      def fetch(name)
         | 
| @@ -59,7 +59,7 @@ module RubyNext | |
| 59 59 | 
             
                      def unsupported_syntax?
         | 
| 60 60 | 
             
                        save_verbose, $VERBOSE = $VERBOSE, nil
         | 
| 61 61 | 
             
                        eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
         | 
| 62 | 
            -
                        Kernel.send eval_mid, self::SYNTAX_PROBE
         | 
| 62 | 
            +
                        Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
         | 
| 63 63 | 
             
                        false
         | 
| 64 64 | 
             
                      rescue SyntaxError, NameError
         | 
| 65 65 | 
             
                        true
         | 
| @@ -4,8 +4,16 @@ module RubyNext | |
| 4 4 | 
             
              module Language
         | 
| 5 5 | 
             
                module Rewriters
         | 
| 6 6 | 
             
                  class EndlessMethod < Base
         | 
| 7 | 
            +
                    NAME = "endless-method"
         | 
| 7 8 | 
             
                    SYNTAX_PROBE = "obj = Object.new; def obj.foo() = 42"
         | 
| 8 | 
            -
                    MIN_SUPPORTED_VERSION = Gem::Version.new(" | 
| 9 | 
            +
                    MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    unless Parser::Meta::NODE_TYPES.include?(:def_e)
         | 
| 12 | 
            +
                      def on_def(node)
         | 
| 13 | 
            +
                        return on_def_e(node) if node.loc.end.nil?
         | 
| 14 | 
            +
                        super(node)
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
                    end
         | 
| 9 17 |  | 
| 10 18 | 
             
                    def on_def_e(node)
         | 
| 11 19 | 
             
                      context.track! self
         | 
| @@ -13,24 +21,39 @@ module RubyNext | |
| 13 21 | 
             
                      replace(node.loc.assignment, "; ")
         | 
| 14 22 | 
             
                      insert_after(node.loc.expression, "; end")
         | 
| 15 23 |  | 
| 24 | 
            +
                      new_loc = node.loc.dup
         | 
| 25 | 
            +
                      new_loc.instance_variable_set(:@end, node.loc.expression)
         | 
| 26 | 
            +
             | 
| 16 27 | 
             
                      process(
         | 
| 17 28 | 
             
                        node.updated(
         | 
| 18 29 | 
             
                          :def,
         | 
| 19 | 
            -
                          node.children
         | 
| 30 | 
            +
                          node.children,
         | 
| 31 | 
            +
                          location: new_loc
         | 
| 20 32 | 
             
                        )
         | 
| 21 33 | 
             
                      )
         | 
| 22 34 | 
             
                    end
         | 
| 23 35 |  | 
| 36 | 
            +
                    unless Parser::Meta::NODE_TYPES.include?(:def_e)
         | 
| 37 | 
            +
                      def on_defs(node)
         | 
| 38 | 
            +
                        return on_defs_e(node) if node.loc.end.nil?
         | 
| 39 | 
            +
                        super(node)
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 24 43 | 
             
                    def on_defs_e(node)
         | 
| 25 44 | 
             
                      context.track! self
         | 
| 26 45 |  | 
| 27 46 | 
             
                      replace(node.loc.assignment, "; ")
         | 
| 28 47 | 
             
                      insert_after(node.loc.expression, "; end")
         | 
| 29 48 |  | 
| 49 | 
            +
                      new_loc = node.loc.dup
         | 
| 50 | 
            +
                      new_loc.instance_variable_set(:@end, node.loc.expression)
         | 
| 51 | 
            +
             | 
| 30 52 | 
             
                      process(
         | 
| 31 53 | 
             
                        node.updated(
         | 
| 32 54 | 
             
                          :defs,
         | 
| 33 | 
            -
                          node.children
         | 
| 55 | 
            +
                          node.children,
         | 
| 56 | 
            +
                          location: new_loc
         | 
| 34 57 | 
             
                        )
         | 
| 35 58 | 
             
                      )
         | 
| 36 59 | 
             
                    end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "ruby-next/language/rewriters/pattern_matching"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module RubyNext
         | 
| 6 | 
            +
              module Language
         | 
| 7 | 
            +
                module Rewriters
         | 
| 8 | 
            +
                  using RubyNext
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  # Separate pattern matching rewriter for Ruby 2.7 to
         | 
| 11 | 
            +
                  # transpile only case...in  with a find pattern
         | 
| 12 | 
            +
                  class FindPattern < PatternMatching
         | 
| 13 | 
            +
                    NAME = "pattern-matching-find-pattern"
         | 
| 14 | 
            +
                    SYNTAX_PROBE = "case 0; in [*,0,*]; true; else; 1; end"
         | 
| 15 | 
            +
                    MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    def on_case_match(node)
         | 
| 18 | 
            +
                      @has_find_pattern = false
         | 
| 19 | 
            +
                      process_regular_node(node).then do |new_node|
         | 
| 20 | 
            +
                        return new_node unless has_find_pattern
         | 
| 21 | 
            +
                        super(node)
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    def on_in_match(node)
         | 
| 26 | 
            +
                      @has_find_pattern = false
         | 
| 27 | 
            +
                      process_regular_node(node).then do |new_node|
         | 
| 28 | 
            +
                        return new_node unless has_find_pattern
         | 
| 29 | 
            +
                        super(node)
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def on_find_pattern(node)
         | 
| 34 | 
            +
                      @has_find_pattern = true
         | 
| 35 | 
            +
                      super(node)
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    private
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    attr_reader :has_find_pattern
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -4,8 +4,9 @@ module RubyNext | |
| 4 4 | 
             
              module Language
         | 
| 5 5 | 
             
                module Rewriters
         | 
| 6 6 | 
             
                  class MethodReference < Base
         | 
| 7 | 
            +
                    NAME = "method-reference"
         | 
| 7 8 | 
             
                    SYNTAX_PROBE = "Language.:transform"
         | 
| 8 | 
            -
                    MIN_SUPPORTED_VERSION = Gem::Version.new(" | 
| 9 | 
            +
                    MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
         | 
| 9 10 |  | 
| 10 11 | 
             
                    def on_meth_ref(node)
         | 
| 11 12 | 
             
                      context.track! self
         | 
| @@ -10,6 +10,16 @@ module RubyNext | |
| 10 10 | 
             
                      def to_ast_node
         | 
| 11 11 | 
             
                        self
         | 
| 12 12 | 
             
                      end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                      # Useful to generate simple operation nodes
         | 
| 15 | 
            +
                      # (e.g., 'a + b')
         | 
| 16 | 
            +
                      def -(val)
         | 
| 17 | 
            +
                        ::Parser::AST::Node.new(:send, [self, :-, val.to_ast_node])
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      def +(val)
         | 
| 21 | 
            +
                        ::Parser::AST::Node.new(:send, [self, :+, val.to_ast_node])
         | 
| 22 | 
            +
                      end
         | 
| 13 23 | 
             
                    end
         | 
| 14 24 |  | 
| 15 25 | 
             
                    refine String do
         | 
| @@ -216,6 +226,7 @@ module RubyNext | |
| 216 226 | 
             
                  end
         | 
| 217 227 |  | 
| 218 228 | 
             
                  class PatternMatching < Base
         | 
| 229 | 
            +
                    NAME = "pattern-matching"
         | 
| 219 230 | 
             
                    SYNTAX_PROBE = "case 0; in 0; true; else; 1; end"
         | 
| 220 231 | 
             
                    MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
         | 
| 221 232 |  | 
| @@ -248,7 +259,7 @@ module RubyNext | |
| 248 259 | 
             
                      rewrite_case_in! node, matchee_ast, case_clause
         | 
| 249 260 |  | 
| 250 261 | 
             
                      node.updated(
         | 
| 251 | 
            -
                        : | 
| 262 | 
            +
                        :kwbegin,
         | 
| 252 263 | 
             
                        [
         | 
| 253 264 | 
             
                          matchee_ast, case_clause
         | 
| 254 265 | 
             
                        ]
         | 
| @@ -287,7 +298,7 @@ module RubyNext | |
| 287 298 | 
             
                          pattern
         | 
| 288 299 | 
             
                        ]
         | 
| 289 300 | 
             
                      ).tap do |new_node|
         | 
| 290 | 
            -
                        replace(node.loc.expression, new_node)
         | 
| 301 | 
            +
                        replace(node.loc.expression, inline_blocks(unparse(new_node)))
         | 
| 291 302 | 
             
                      end
         | 
| 292 303 | 
             
                    end
         | 
| 293 304 |  | 
| @@ -306,14 +317,14 @@ module RubyNext | |
| 306 317 | 
             
                            body_indent = " " * clause.children[2].loc.column
         | 
| 307 318 | 
             
                            replace(
         | 
| 308 319 | 
             
                              clause.loc.expression,
         | 
| 309 | 
            -
                              "when #{unparse(new_node.children[i].children[0])}" \
         | 
| 320 | 
            +
                              "when #{inline_blocks(unparse(new_node.children[i].children[0]))}" \
         | 
| 310 321 | 
             
                              "#{padding}" \
         | 
| 311 322 | 
             
                              "#{body_indent}#{clause.children[2].loc.expression.source}"
         | 
| 312 323 | 
             
                            )
         | 
| 313 324 | 
             
                          else
         | 
| 314 325 | 
             
                            replace(
         | 
| 315 326 | 
             
                              clause.loc.keyword.end.join(clause.children[0].loc.expression.end),
         | 
| 316 | 
            -
                              new_node.children[i].children[0]
         | 
| 327 | 
            +
                              inline_blocks(unparse(new_node.children[i].children[0]))
         | 
| 317 328 | 
             
                            )
         | 
| 318 329 | 
             
                            remove(clause.children[1].loc.expression) if clause.children[1]
         | 
| 319 330 | 
             
                            replace(clause.loc.keyword, "when ")
         | 
| @@ -429,6 +440,8 @@ module RubyNext | |
| 429 440 | 
             
                        right =
         | 
| 430 441 | 
             
                          if node.children.empty?
         | 
| 431 442 | 
             
                            case_eq_clause(s(:array), s(:lvar, locals[:arr]))
         | 
| 443 | 
            +
                          elsif node.children.size > 1 && node.children.first.type == :match_rest && node.children.last.type == :match_rest
         | 
| 444 | 
            +
                            array_find(*node.children)
         | 
| 432 445 | 
             
                          else
         | 
| 433 446 | 
             
                            array_element(0, *node.children)
         | 
| 434 447 | 
             
                          end
         | 
| @@ -442,6 +455,7 @@ module RubyNext | |
| 442 455 | 
             
                    end
         | 
| 443 456 |  | 
| 444 457 | 
             
                    alias array_pattern_with_tail_clause array_pattern_clause
         | 
| 458 | 
            +
                    alias find_pattern_clause array_pattern_clause
         | 
| 445 459 |  | 
| 446 460 | 
             
                    def deconstruct_node(matchee)
         | 
| 447 461 | 
             
                      context.use_ruby_next!
         | 
| @@ -477,9 +491,80 @@ module RubyNext | |
| 477 491 | 
             
                      end
         | 
| 478 492 | 
             
                    end
         | 
| 479 493 |  | 
| 494 | 
            +
                    # [*a, 1, 2, *] -> arr.find.with_index { |_, i| (a = arr.take(i)) && arr[i] == 1 && arr[i + 1] == 2 }
         | 
| 495 | 
            +
                    def array_find(head, *nodes, tail)
         | 
| 496 | 
            +
                      index = s(:lvar, :__i__)
         | 
| 497 | 
            +
             | 
| 498 | 
            +
                      match_vars = []
         | 
| 499 | 
            +
             | 
| 500 | 
            +
                      head_match =
         | 
| 501 | 
            +
                        unless head.children.empty?
         | 
| 502 | 
            +
                          match_vars << s(:lvasgn, head.children[0].children[0])
         | 
| 503 | 
            +
             | 
| 504 | 
            +
                          arr_take = s(:send,
         | 
| 505 | 
            +
                            s(:lvar, locals[:arr]),
         | 
| 506 | 
            +
                            :take,
         | 
| 507 | 
            +
                            index)
         | 
| 508 | 
            +
             | 
| 509 | 
            +
                          match_var_clause(head.children[0], arr_take)
         | 
| 510 | 
            +
                        end
         | 
| 511 | 
            +
             | 
| 512 | 
            +
                      tail_match =
         | 
| 513 | 
            +
                        unless tail.children.empty?
         | 
| 514 | 
            +
                          match_vars << s(:lvasgn, tail.children[0].children[0])
         | 
| 515 | 
            +
             | 
| 516 | 
            +
                          match_var_clause(tail.children[0], arr_slice(index + nodes.size, -1))
         | 
| 517 | 
            +
                        end
         | 
| 518 | 
            +
             | 
| 519 | 
            +
                      nodes.each do |node|
         | 
| 520 | 
            +
                        if node.type == :match_var
         | 
| 521 | 
            +
                          match_vars << s(:lvasgn, node.children[0])
         | 
| 522 | 
            +
                        elsif node.type == :match_as
         | 
| 523 | 
            +
                          match_vars << s(:lvasgn, node.children[1].children[0])
         | 
| 524 | 
            +
                        end
         | 
| 525 | 
            +
                      end
         | 
| 526 | 
            +
             | 
| 527 | 
            +
                      pattern = array_rest_element(*nodes, index).then do |needle|
         | 
| 528 | 
            +
                        next needle unless head_match
         | 
| 529 | 
            +
                        s(:and,
         | 
| 530 | 
            +
                          needle,
         | 
| 531 | 
            +
                          head_match)
         | 
| 532 | 
            +
                      end.then do |headed_needle|
         | 
| 533 | 
            +
                        next headed_needle unless tail_match
         | 
| 534 | 
            +
             | 
| 535 | 
            +
                        s(:and,
         | 
| 536 | 
            +
                          headed_needle,
         | 
| 537 | 
            +
                          tail_match)
         | 
| 538 | 
            +
                      end
         | 
| 539 | 
            +
             | 
| 540 | 
            +
                      s(:block,
         | 
| 541 | 
            +
                        s(:send,
         | 
| 542 | 
            +
                          s(:send,
         | 
| 543 | 
            +
                            s(:lvar, locals[:arr]),
         | 
| 544 | 
            +
                            :find),
         | 
| 545 | 
            +
                          :with_index),
         | 
| 546 | 
            +
                        s(:args,
         | 
| 547 | 
            +
                          s(:arg, :_),
         | 
| 548 | 
            +
                          s(:arg, :__i__)),
         | 
| 549 | 
            +
                        pattern).then do |block|
         | 
| 550 | 
            +
                        next block if match_vars.empty?
         | 
| 551 | 
            +
             | 
| 552 | 
            +
                        # We need to declare match vars outside of `find` block
         | 
| 553 | 
            +
                        locals_declare = s(:masgn,
         | 
| 554 | 
            +
                          s(:mlhs, *match_vars),
         | 
| 555 | 
            +
                          s(:nil))
         | 
| 556 | 
            +
             | 
| 557 | 
            +
                        s(:or,
         | 
| 558 | 
            +
                          locals_declare,
         | 
| 559 | 
            +
                          block)
         | 
| 560 | 
            +
                      end
         | 
| 561 | 
            +
                    end
         | 
| 562 | 
            +
             | 
| 480 563 | 
             
                    def array_match_rest(index, node, *tail)
         | 
| 564 | 
            +
                      size = tail.size + 1
         | 
| 481 565 | 
             
                      child = node.children[0]
         | 
| 482 | 
            -
             | 
| 566 | 
            +
             | 
| 567 | 
            +
                      rest = arr_slice(index, -size).then do |r|
         | 
| 483 568 | 
             
                        next r unless child
         | 
| 484 569 |  | 
| 485 570 | 
             
                        match_var_clause(
         | 
| @@ -492,16 +577,16 @@ module RubyNext | |
| 492 577 |  | 
| 493 578 | 
             
                      s(:and,
         | 
| 494 579 | 
             
                        rest,
         | 
| 495 | 
            -
                        array_rest_element(*tail))
         | 
| 580 | 
            +
                        array_rest_element(*tail, -(size - 1)))
         | 
| 496 581 | 
             
                    end
         | 
| 497 582 |  | 
| 498 | 
            -
                    def array_rest_element(head, *tail)
         | 
| 499 | 
            -
                      send("#{head.type}_array_element", head,  | 
| 583 | 
            +
                    def array_rest_element(head, *tail, index)
         | 
| 584 | 
            +
                      send("#{head.type}_array_element", head, index).then do |node|
         | 
| 500 585 | 
             
                        next node if tail.empty?
         | 
| 501 586 |  | 
| 502 587 | 
             
                        s(:and,
         | 
| 503 588 | 
             
                          node,
         | 
| 504 | 
            -
                          array_rest_element(*tail))
         | 
| 589 | 
            +
                          array_rest_element(*tail, index + 1))
         | 
| 505 590 | 
             
                      end
         | 
| 506 591 | 
             
                    end
         | 
| 507 592 |  | 
| @@ -548,12 +633,12 @@ module RubyNext | |
| 548 633 | 
             
                      s(:index, arr, index.to_ast_node)
         | 
| 549 634 | 
             
                    end
         | 
| 550 635 |  | 
| 551 | 
            -
                    def  | 
| 636 | 
            +
                    def arr_slice(lindex, rindex, arr = s(:lvar, locals[:arr]))
         | 
| 552 637 | 
             
                      s(:index,
         | 
| 553 638 | 
             
                        arr,
         | 
| 554 639 | 
             
                        s(:irange,
         | 
| 555 | 
            -
                           | 
| 556 | 
            -
                           | 
| 640 | 
            +
                          lindex.to_ast_node,
         | 
| 641 | 
            +
                          rindex.to_ast_node))
         | 
| 557 642 | 
             
                    end
         | 
| 558 643 |  | 
| 559 644 | 
             
                    #=========== ARRAY PATTERN (END) ===============
         | 
| @@ -816,11 +901,12 @@ module RubyNext | |
| 816 901 | 
             
                    end
         | 
| 817 902 |  | 
| 818 903 | 
             
                    def respond_to_missing?(mid, *)
         | 
| 819 | 
            -
                      return true if mid.match?(/_(clause|array_element)/)
         | 
| 904 | 
            +
                      return true if mid.to_s.match?(/_(clause|array_element)/)
         | 
| 820 905 | 
             
                      super
         | 
| 821 906 | 
             
                    end
         | 
| 822 907 |  | 
| 823 908 | 
             
                    def method_missing(mid, *args, &block)
         | 
| 909 | 
            +
                      mid = mid.to_s
         | 
| 824 910 | 
             
                      return case_eq_clause(*args) if mid.match?(/_clause$/)
         | 
| 825 911 | 
             
                      return case_eq_array_element(*args) if mid.match?(/_array_element$/)
         | 
| 826 912 | 
             
                      return case_eq_hash_element(*args) if mid.match?(/_hash_element$/)
         | 
| @@ -846,6 +932,12 @@ module RubyNext | |
| 846 932 |  | 
| 847 933 | 
             
                      deconstructed_keys[key] = :"k#{deconstructed_keys.size}"
         | 
| 848 934 | 
             
                    end
         | 
| 935 | 
            +
             | 
| 936 | 
            +
                    # Unparser generates `do .. end`  blocks, we want to
         | 
| 937 | 
            +
                    # have single-line blocks with `{ ... }`.
         | 
| 938 | 
            +
                    def inline_blocks(source)
         | 
| 939 | 
            +
                      source.gsub(/do \|_, __i__\|\n\s*([^\n]+)\n\s*end/, '{ |_, __i__| \1 }')
         | 
| 940 | 
            +
                    end
         | 
| 849 941 | 
             
                  end
         | 
| 850 942 | 
             
                end
         | 
| 851 943 | 
             
              end
         |