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.
data/lib/ruby-next/cli.rb CHANGED
@@ -80,18 +80,16 @@ module RubyNext
80
80
  end
81
81
 
82
82
  def optparser
83
- @optparser ||= begin
84
- OptionParser.new do |opts|
85
- opts.banner = "Usage: ruby-next COMMAND [options]"
86
-
87
- opts.on("-v", "--version", "Print version") do
88
- $stdout.puts RubyNext::VERSION
89
- exit 0
90
- end
91
-
92
- opts.on("-h", "--help", "Print help") do
93
- @print_help = true
94
- end
83
+ @optparser ||= OptionParser.new do |opts|
84
+ opts.banner = "Usage: ruby-next COMMAND [options]"
85
+
86
+ opts.on("-v", "--version", "Print version") do
87
+ $stdout.puts RubyNext::VERSION
88
+ exit 0
89
+ end
90
+
91
+ opts.on("-h", "--help", "Print help") do
92
+ @print_help = true
95
93
  end
96
94
  end
97
95
  end
@@ -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
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/LambdaCall
4
3
  RubyNext::Core.patch Proc, name: "ProcCompose", method: :<<, version: "2.6" do
5
4
  <<-RUBY
6
5
  def <<(other)
@@ -54,7 +54,7 @@ RubyNext::Core.singleton_class.module_eval do
54
54
 
55
55
  # Copy constants (they could be accessed from methods)
56
56
  other.constants.each do |name|
57
- Kernel.eval "#{name} = #{other}::#{name}", bind
57
+ Kernel.eval "#{name} = #{other}::#{name}", bind # rubocop:disable Style/EvalWithLocation
58
58
  end
59
59
  end
60
60
  end
@@ -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)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-next"
4
+ # Include RubyNext into TOPLEVEL_BINDING for polyfills to work
5
+ eval("using RubyNext", TOPLEVEL_BINDING, __FILE__, __LINE__)
6
+
7
+ require "ruby-next/language"
8
+
9
+ # IRB extension to transpile code before evaluating
10
+ module RubyNext
11
+ module IRBExt
12
+ def evaluate(context, statements, *args)
13
+ new_statements = ::RubyNext::Language.transform(
14
+ statements,
15
+ rewriters: ::RubyNext::Language.current_rewriters,
16
+ using: false
17
+ )
18
+
19
+ super(context, new_statements, *args)
20
+ end
21
+ end
22
+ end
23
+
24
+ IRB::WorkSpace.prepend(RubyNext::IRBExt)
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class RescueWithinBlock < Base
7
+ NAME = "rescue-within-block"
8
+ SYNTAX_PROBE = "lambda do
9
+ raise 'err'
10
+ rescue
11
+ $! # => #<RuntimeError: err>
12
+ end.call"
13
+
14
+ MIN_SUPPORTED_VERSION = Gem::Version.new("2.5.0")
15
+
16
+ def on_block(block_node)
17
+ exception_node = block_node.children.find do |node|
18
+ node && (node.type == :rescue || node.type == :ensure)
19
+ end
20
+
21
+ return super(block_node) unless exception_node
22
+
23
+ context.track! self
24
+
25
+ insert_before(exception_node.loc.expression, "begin;")
26
+ insert_after(exception_node.loc.expression, ";end")
27
+
28
+ new_children = block_node.children.map do |child|
29
+ next s(:kwbegin, exception_node) if child == exception_node
30
+
31
+ child
32
+ end
33
+
34
+ process(
35
+ block_node.updated(:block, new_children)
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
41
+ 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 { 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
 
@@ -66,7 +66,7 @@ module RubyNext
66
66
  end
67
67
 
68
68
  class << self
69
- # Returns true if the syntax is supported
69
+ # Returns true if the syntax is not supported
70
70
  # by the current Ruby (performs syntax check, not version check)
71
71
  def unsupported_syntax?
72
72
  save_verbose, $VERBOSE = $VERBOSE, nil
@@ -67,7 +67,7 @@ end
67
67
 
68
68
  # Patch Kernel to hijack require/require_relative/load/eval
69
69
  module Kernel
70
- module_function # rubocop:disable Style/ModuleFunction
70
+ module_function
71
71
 
72
72
  alias_method :require_without_ruby_next, :require
73
73
  def require(path)
@@ -12,7 +12,7 @@ module RubyNext
12
12
  return if File.directory?(target_dir)
13
13
 
14
14
  Dir.chdir(root_dir) do
15
- unless system("bundle exec ruby-next nextify ./#{lib_dir} -o #{target_dir} > /dev/null 2>&1")
15
+ unless system("bundle exec ruby-next nextify ./#{lib_dir} -o #{target_dir} --min-version=#{RUBY_VERSION} > /dev/null 2>&1")
16
16
  RubyNext.warn "Traspiled files are missing in: #{target_dir}. \n" \
17
17
  "Make sure you have gem 'ruby-next' in your Gemfile to auto-transpile the required files from source on load. " \
18
18
  "Otherwise the code from #{root_dir} may not work correctly."
@@ -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
 
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file contains patches for Pry integration.
4
+ # It's supposed to be required inside .pryrc files.
5
+
6
+ require "ruby-next/language/setup"
7
+ require "ruby-next/language/runtime"
8
+
9
+ # Enables refinements by injecting "using RubyNext"
10
+ # before Pry copies and memorizes the TOPLEVEL_BINDING.
11
+ Pry.singleton_class.prepend(Module.new do
12
+ def toplevel_binding
13
+ unless defined?(@_using_injected) && @_using_injected
14
+ orig_binding = super
15
+
16
+ # Copy TOPLEVEL_BINDING without local variables.
17
+ TOPLEVEL_BINDING.eval <<-RUBY
18
+ using RubyNext
19
+
20
+ def self.__pry__
21
+ binding
22
+ end
23
+
24
+ Pry.toplevel_binding = __pry__
25
+
26
+ class << self; undef __pry__; end
27
+ RUBY
28
+
29
+ # Inject local variables from the original binding.
30
+ orig_binding.local_variables.each do |var|
31
+ value = orig_binding.local_variable_get(var)
32
+ @toplevel_binding.local_variable_set(var, value)
33
+ end
34
+
35
+ @_using_injected = true
36
+ end
37
+
38
+ super
39
+ end
40
+ end)
41
+
42
+ # Enables edge Ruby syntax by transpiling the code
43
+ # before it's evaluated in the context of a binding.
44
+ Pry.prepend(Module.new do
45
+ def current_binding
46
+ super.tap do |obj|
47
+ next if obj.respond_to?(:__nextified__)
48
+
49
+ obj.instance_eval do
50
+ def eval(code, *args)
51
+ new_code = ::RubyNext::Language::Runtime.transform(code, using: false)
52
+
53
+ super(new_code, *args)
54
+ end
55
+
56
+ def __nextified__
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end)
62
+
63
+ # Enables edge Ruby syntax for multi-line input.
64
+ Pry::Code.singleton_class.prepend(Module.new do
65
+ def complete_expression?(str)
66
+ silence_stderr do
67
+ ::Parser::RubyNext.parse(str)
68
+ end
69
+
70
+ true
71
+ rescue Parser::SyntaxError => ex
72
+ case ex.message
73
+ when /unexpected token \$end/
74
+ false
75
+ else
76
+ true
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def silence_stderr
83
+ stderr = StringIO.new
84
+ orig_stderr, $stderr = $stderr, stderr
85
+
86
+ yield
87
+
88
+ $stderr = orig_stderr
89
+ end
90
+ end)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyNext
4
- VERSION = "0.14.0"
4
+ VERSION = "0.15.1"
5
5
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-next/irb"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-next/pry"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-next-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-29 00:00:00.000000000 Z
11
+ date: 2022-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-next-parser
@@ -67,6 +67,7 @@ files:
67
67
  - lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb
68
68
  - lib/.rbnext/2.3/ruby-next/utils.rb
69
69
  - lib/.rbnext/2.7/ruby-next/core.rb
70
+ - lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb
70
71
  - lib/ruby-next.rb
71
72
  - lib/ruby-next/cli.rb
72
73
  - lib/ruby-next/commands/base.rb
@@ -102,6 +103,7 @@ files:
102
103
  - lib/ruby-next/core/time/floor.rb
103
104
  - lib/ruby-next/core/unboundmethod/bind_call.rb
104
105
  - lib/ruby-next/core_ext.rb
106
+ - lib/ruby-next/irb.rb
105
107
  - lib/ruby-next/language.rb
106
108
  - lib/ruby-next/language/bootsnap.rb
107
109
  - lib/ruby-next/language/eval.rb
@@ -111,6 +113,7 @@ files:
111
113
  - lib/ruby-next/language/rewriters/2.3/safe_navigation.rb
112
114
  - lib/ruby-next/language/rewriters/2.3/squiggly_heredoc.rb
113
115
  - lib/ruby-next/language/rewriters/2.4/dir.rb
116
+ - lib/ruby-next/language/rewriters/2.5/rescue_within_block.rb
114
117
  - lib/ruby-next/language/rewriters/2.6/endless_range.rb
115
118
  - lib/ruby-next/language/rewriters/2.7/args_forward.rb
116
119
  - lib/ruby-next/language/rewriters/2.7/numbered_params.rb
@@ -135,11 +138,14 @@ files:
135
138
  - lib/ruby-next/language/setup.rb
136
139
  - lib/ruby-next/language/unparser.rb
137
140
  - lib/ruby-next/logging.rb
141
+ - lib/ruby-next/pry.rb
138
142
  - lib/ruby-next/rubocop.rb
139
143
  - lib/ruby-next/setup_self.rb
140
144
  - lib/ruby-next/utils.rb
141
145
  - lib/ruby-next/version.rb
142
146
  - lib/uby-next.rb
147
+ - lib/uby-next/irb.rb
148
+ - lib/uby-next/pry.rb
143
149
  homepage: http://github.com/palkan/ruby-next
144
150
  licenses:
145
151
  - MIT
@@ -164,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
170
  - !ruby/object:Gem::Version
165
171
  version: '0'
166
172
  requirements: []
167
- rubygems_version: 3.2.22
173
+ rubygems_version: 3.3.7
168
174
  signing_key:
169
175
  specification_version: 4
170
176
  summary: Ruby Next core functionality