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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +70 -0
- data/README.md +163 -56
- data/bin/mspec +11 -0
- data/lib/.rbnext/2.1/ruby-next/commands/nextify.rb +295 -0
- data/lib/.rbnext/2.1/ruby-next/core.rb +12 -4
- data/lib/.rbnext/2.1/ruby-next/language.rb +62 -12
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +97 -3
- data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
- data/lib/.rbnext/2.3/ruby-next/core/data.rb +163 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/args_forward.rb +134 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +122 -47
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -32
- data/lib/.rbnext/2.3/ruby-next/utils.rb +3 -22
- data/lib/.rbnext/2.6/ruby-next/core/data.rb +163 -0
- data/lib/.rbnext/2.7/ruby-next/core/data.rb +163 -0
- data/lib/.rbnext/2.7/ruby-next/core.rb +12 -4
- data/lib/.rbnext/2.7/ruby-next/language/paco_parsers/string_literals.rb +109 -0
- data/lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
- data/lib/.rbnext/2.7/ruby-next/language/rewriters/text.rb +132 -0
- data/lib/.rbnext/3.2/ruby-next/commands/base.rb +55 -0
- data/lib/.rbnext/3.2/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
- data/lib/.rbnext/3.2/ruby-next/rubocop.rb +210 -0
- data/lib/ruby-next/cli.rb +10 -15
- data/lib/ruby-next/commands/nextify.rb +99 -3
- data/lib/ruby-next/config.rb +31 -4
- data/lib/ruby-next/core/data.rb +163 -0
- data/lib/ruby-next/core/matchdata/deconstruct.rb +9 -0
- data/lib/ruby-next/core/matchdata/deconstruct_keys.rb +20 -0
- data/lib/ruby-next/core/matchdata/named_captures.rb +11 -0
- data/lib/ruby-next/core/proc/compose.rb +0 -1
- data/lib/ruby-next/core/refinement/import.rb +44 -36
- data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
- data/lib/ruby-next/core.rb +11 -3
- data/lib/ruby-next/irb.rb +24 -0
- data/lib/ruby-next/language/bootsnap.rb +2 -25
- data/lib/ruby-next/language/eval.rb +4 -4
- data/lib/ruby-next/language/paco_parser.rb +7 -0
- data/lib/ruby-next/language/paco_parsers/base.rb +47 -0
- data/lib/ruby-next/language/paco_parsers/comments.rb +26 -0
- data/lib/ruby-next/language/paco_parsers/string_literals.rb +109 -0
- data/lib/ruby-next/language/parser.rb +31 -6
- data/lib/ruby-next/language/rewriters/2.5/rescue_within_block.rb +41 -0
- data/lib/ruby-next/language/rewriters/2.7/args_forward.rb +57 -0
- data/lib/ruby-next/language/rewriters/2.7/pattern_matching.rb +120 -45
- data/lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb +2 -2
- data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +1 -1
- data/lib/ruby-next/language/rewriters/3.1/shorthand_hash.rb +2 -1
- data/lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb +104 -0
- data/lib/ruby-next/language/rewriters/abstract.rb +57 -0
- data/lib/ruby-next/language/rewriters/base.rb +6 -32
- data/lib/ruby-next/language/rewriters/edge/it_param.rb +58 -0
- data/lib/ruby-next/language/rewriters/edge.rb +12 -0
- data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +3 -0
- data/lib/ruby-next/language/rewriters/proposed/method_reference.rb +9 -20
- data/lib/ruby-next/language/rewriters/text.rb +132 -0
- data/lib/ruby-next/language/runtime.rb +9 -86
- data/lib/ruby-next/language/setup.rb +5 -2
- data/lib/ruby-next/language/unparser.rb +5 -0
- data/lib/ruby-next/language.rb +62 -12
- data/lib/ruby-next/pry.rb +90 -0
- data/lib/ruby-next/rubocop.rb +2 -0
- data/lib/ruby-next/utils.rb +3 -22
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next/irb.rb +3 -0
- data/lib/uby-next/pry.rb +3 -0
- data/lib/uby-next.rb +2 -2
- metadata +70 -10
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RubyNext::Core.patch MatchData, method: :named_captures, version: "3.3", supported: "a".match(/a/).method(:named_captures).arity != 0, core_ext: :prepend do
|
4
|
+
<<-'RUBY'
|
5
|
+
def named_captures(symbolize_names: false)
|
6
|
+
return super() unless symbolize_names
|
7
|
+
|
8
|
+
super().transform_keys!(&:to_sym)
|
9
|
+
end
|
10
|
+
RUBY
|
11
|
+
end
|
@@ -4,57 +4,65 @@
|
|
4
4
|
# So, we use a defined method instead (and transpile source code to use it).
|
5
5
|
# NOTE: We have to transpile the source code anyway, since we need to pass a binding.
|
6
6
|
RubyNext::Core.singleton_class.module_eval do
|
7
|
-
def import_methods(
|
7
|
+
def import_methods(*others, bind)
|
8
8
|
import = []
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
# First, validate passed modules
|
11
|
+
others.each do |other|
|
12
|
+
raise TypeError, "wrong argument type #{other.class} (expected Module)" unless other.is_a?(::Module)
|
13
|
+
raise TypeError, "wrong argument type Class (expected Module)" if other.is_a?(::Class)
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
others.each do |other|
|
17
|
+
(other.instance_methods(false) + other.private_instance_methods(false)).each do |mid|
|
18
|
+
# check for non-Ruby methods
|
19
|
+
meth = other.instance_method(mid)
|
20
|
+
location = meth.source_location
|
18
21
|
|
19
|
-
|
22
|
+
if location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core|uri:classloader:\/jruby)/)
|
23
|
+
raise ArgumentError, "Can't import method which is not defined with Ruby code: #{other}##{mid} from #{location}"
|
24
|
+
end
|
20
25
|
|
21
|
-
|
26
|
+
source_file, lineno = *location
|
22
27
|
|
23
|
-
|
28
|
+
raise ArgumentError, "Can't import dynamicly added methods: #{other}##{mid}" unless File.file?(source_file)
|
24
29
|
|
25
|
-
|
30
|
+
lines = File.open(source_file).readlines
|
26
31
|
|
27
|
-
|
28
|
-
buffer << line + "\n"
|
32
|
+
buffer = []
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
lines[(lineno - 1)..-1].each do |line|
|
35
|
+
buffer << line + "\n"
|
36
|
+
|
37
|
+
begin
|
38
|
+
if defined?(::RubyNext::Language) && ::RubyNext::Language.runtime?
|
39
|
+
new_source = ::RubyNext::Language.transform(buffer.join, rewriters: RubyNext::Language.current_rewriters, using: false)
|
40
|
+
# Transformed successfully => valid method => evaluate transpiled code
|
41
|
+
import << [new_source, source_file, lineno]
|
42
|
+
buffer.clear
|
43
|
+
break
|
44
|
+
end
|
38
45
|
|
39
|
-
|
40
|
-
|
41
|
-
|
46
|
+
# Borrowed from https://github.com/banister/method_source/blob/81d039c966ffd95d26e12eb2e205c0eb8377f49d/lib/method_source/code_helpers.rb#L66
|
47
|
+
catch(:valid) do
|
48
|
+
eval("BEGIN{throw :valid}\nObject.new.instance_eval { #{buffer.join} }") # rubocop:disable all
|
49
|
+
end
|
50
|
+
break
|
51
|
+
rescue SyntaxError
|
42
52
|
end
|
43
|
-
break
|
44
|
-
rescue SyntaxError
|
45
53
|
end
|
46
|
-
end
|
47
54
|
|
48
|
-
|
49
|
-
|
55
|
+
import << [buffer.join, source_file, lineno] unless buffer.empty?
|
56
|
+
end
|
50
57
|
|
51
|
-
|
52
|
-
|
53
|
-
|
58
|
+
import.each do |(definition, file, lino)|
|
59
|
+
Kernel.eval definition, bind, file, lino
|
60
|
+
end
|
54
61
|
|
55
|
-
|
56
|
-
|
57
|
-
|
62
|
+
# Copy constants (they could be accessed from methods)
|
63
|
+
other.constants.each do |name|
|
64
|
+
Kernel.eval "#{name} = #{other}::#{name}", bind # rubocop:disable Style/EvalWithLocation
|
65
|
+
end
|
58
66
|
end
|
59
67
|
end
|
60
68
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RubyNext::Core.patch Time, method: :deconstruct_keys, version: "3.2" do
|
4
|
+
<<-'RUBY'
|
5
|
+
def deconstruct_keys(keys)
|
6
|
+
raise TypeError, "wrong argument type #{keys.class} (expected Array or nil)" if keys && !keys.is_a?(Array)
|
7
|
+
|
8
|
+
if !keys
|
9
|
+
return {
|
10
|
+
year: year,
|
11
|
+
month: month,
|
12
|
+
day: day,
|
13
|
+
yday: yday,
|
14
|
+
wday: wday,
|
15
|
+
hour: hour,
|
16
|
+
min: min,
|
17
|
+
sec: sec,
|
18
|
+
subsec: subsec,
|
19
|
+
dst: dst?,
|
20
|
+
zone: zone
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
keys.each_with_object({}) do |key, hash|
|
25
|
+
hash[key] = public_send(key) if key.is_a?(Symbol) && respond_to?(key)
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
RUBY
|
30
|
+
end
|
data/lib/ruby-next/core.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
3
|
+
require "set" # rubocop:disable Lint/RedundantRequireStatement
|
4
4
|
|
5
5
|
require "ruby-next/config"
|
6
6
|
require "ruby-next/utils"
|
@@ -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/, "")
|
65
|
+
"#{mod_name}#{camelized_method_name}".gsub(/\W/, "")
|
66
66
|
end
|
67
67
|
|
68
68
|
def build_location(trace_locations)
|
@@ -78,7 +78,7 @@ module RubyNext
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def native_location?(location)
|
81
|
-
location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core)/)
|
81
|
+
location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core|uri:classloader:\/jruby)/)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -197,6 +197,11 @@ require "ruby-next/core/matchdata/match"
|
|
197
197
|
require "ruby-next/core/enumerable/compact"
|
198
198
|
require "ruby-next/core/integer/try_convert"
|
199
199
|
|
200
|
+
require "ruby-next/core/matchdata/deconstruct"
|
201
|
+
require "ruby-next/core/matchdata/deconstruct_keys"
|
202
|
+
require "ruby-next/core/matchdata/named_captures"
|
203
|
+
require "ruby-next/core/time/deconstruct_keys"
|
204
|
+
|
200
205
|
# Generate refinements
|
201
206
|
RubyNext.module_eval do
|
202
207
|
RubyNext::Core.patches.refined.each do |mod, patches|
|
@@ -210,3 +215,6 @@ RubyNext.module_eval do
|
|
210
215
|
end
|
211
216
|
end
|
212
217
|
end
|
218
|
+
|
219
|
+
# Load backports
|
220
|
+
require "ruby-next/core/data" unless ENV["RUBY_NEXT_DISABLE_DATA"] == "true"
|
@@ -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 eval(statements, *args)
|
13
|
+
new_statements = ::RubyNext::Language.transform(
|
14
|
+
statements,
|
15
|
+
rewriters: ::RubyNext::Language.current_rewriters,
|
16
|
+
using: false
|
17
|
+
)
|
18
|
+
|
19
|
+
super(new_statements, *args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
IRB::WorkSpace.prepend(RubyNext::IRBExt)
|
@@ -1,28 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "ruby-next/utils"
|
5
|
-
require "ruby-next/language"
|
3
|
+
warn "[DEPRECATED] Using ruby-next/language/bootsnap is deprecated. Please use ruby-next/language/runtime instead."
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
# Patch bootsnap to transform source code.
|
10
|
-
# Based on https://github.com/kddeisz/preval/blob/master/lib/preval.rb
|
11
|
-
load_iseq = RubyVM::InstructionSequence.method(:load_iseq)
|
12
|
-
|
13
|
-
if load_iseq.source_location[0].include?("/bootsnap/")
|
14
|
-
Bootsnap::CompileCache::ISeq.singleton_class.prepend(
|
15
|
-
Module.new do
|
16
|
-
def input_to_storage(source, path, *)
|
17
|
-
return super unless RubyNext::Language.transformable?(path)
|
18
|
-
source = RubyNext::Language.transform(source, rewriters: RubyNext::Language.current_rewriters)
|
19
|
-
|
20
|
-
RubyNext.debug_source(source, path)
|
21
|
-
|
22
|
-
RubyVM::InstructionSequence.compile(source, path, path).to_binary
|
23
|
-
rescue SyntaxError
|
24
|
-
raise Bootsnap::CompileCache::Uncompilable, "syntax error"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
)
|
28
|
-
end
|
5
|
+
require "ruby-next/language/runtime"
|
@@ -11,7 +11,7 @@ module RubyNext
|
|
11
11
|
using: bind&.receiver == TOPLEVEL_BINDING.receiver || bind&.receiver&.is_a?(Module)
|
12
12
|
)
|
13
13
|
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
14
|
-
super
|
14
|
+
super(new_source, bind, *args)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -25,7 +25,7 @@ module RubyNext
|
|
25
25
|
source = args.shift
|
26
26
|
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
27
27
|
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
28
|
-
super
|
28
|
+
super(new_source, *args)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -39,7 +39,7 @@ module RubyNext
|
|
39
39
|
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
40
40
|
|
41
41
|
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
42
|
-
super
|
42
|
+
super(new_source, *args)
|
43
43
|
end
|
44
44
|
|
45
45
|
def class_eval(*args, &block)
|
@@ -48,7 +48,7 @@ module RubyNext
|
|
48
48
|
source = args.shift
|
49
49
|
new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
|
50
50
|
RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
|
51
|
-
super
|
51
|
+
super(new_source, *args)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Language
|
5
|
+
module PacoParsers
|
6
|
+
class Base
|
7
|
+
include Paco
|
8
|
+
|
9
|
+
def parse(io)
|
10
|
+
default.parse(io)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def anything_between(left, right)
|
16
|
+
seq(
|
17
|
+
left,
|
18
|
+
many(not_followed_by(right).bind { any_char }).join,
|
19
|
+
right
|
20
|
+
).join
|
21
|
+
end
|
22
|
+
|
23
|
+
def starting_string(str)
|
24
|
+
index.bind do |index|
|
25
|
+
(index.column > 1) ? failed("1 column") : string(str)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def balanced(l, r, inner)
|
30
|
+
left = string(l)
|
31
|
+
right = string(r)
|
32
|
+
|
33
|
+
many(
|
34
|
+
alt(
|
35
|
+
seq(
|
36
|
+
left,
|
37
|
+
lazy { balanced(l, r, inner) },
|
38
|
+
right
|
39
|
+
),
|
40
|
+
not_followed_by(right).bind { inner }
|
41
|
+
)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Language
|
5
|
+
module PacoParsers
|
6
|
+
class Comments < Base
|
7
|
+
def default
|
8
|
+
alt(
|
9
|
+
line_comment,
|
10
|
+
block_comment
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Matches a Ruby line comment (from `#` till the end of the line)
|
15
|
+
def line_comment
|
16
|
+
anything_between(string("#"), end_of_line)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Matches a Ruby block comment (from `=begin` till `=end`)
|
20
|
+
def block_comment
|
21
|
+
anything_between(starting_string("=begin"), starting_string("=end"))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Language
|
5
|
+
module PacoParsers
|
6
|
+
class StringLiterals < Base
|
7
|
+
PAIRS = {"[" => "]", "{" => "}", "<" => ">"}.freeze
|
8
|
+
|
9
|
+
def default
|
10
|
+
all_strings.fmap do |result|
|
11
|
+
reduce_tokens(result.flatten)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def all_strings
|
16
|
+
alt(
|
17
|
+
single_quoted,
|
18
|
+
double_quoted,
|
19
|
+
external_cmd_exec,
|
20
|
+
quoted,
|
21
|
+
quoted_expanded
|
22
|
+
)
|
23
|
+
# heredoc,
|
24
|
+
# heredoc_expanded
|
25
|
+
end
|
26
|
+
|
27
|
+
def quoted
|
28
|
+
seq(
|
29
|
+
string("%q"),
|
30
|
+
any_char.bind do |char|
|
31
|
+
end_symbol = string(PAIRS[char] || char)
|
32
|
+
escapable_string(succeed(char), end_symbol)
|
33
|
+
end
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def single_quoted
|
38
|
+
escapable_string(string("'"))
|
39
|
+
end
|
40
|
+
|
41
|
+
def quoted_expanded
|
42
|
+
seq(
|
43
|
+
alt(string("%Q"), string("%")),
|
44
|
+
any_char.bind do |char|
|
45
|
+
end_symbol = string(PAIRS[char] || char)
|
46
|
+
escapable_string(succeed(char), end_symbol, interpolate: true)
|
47
|
+
end
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def external_cmd_exec
|
52
|
+
escapable_string(string("`"), interpolate: true)
|
53
|
+
end
|
54
|
+
|
55
|
+
def double_quoted
|
56
|
+
escapable_string(string('"'), interpolate: true)
|
57
|
+
end
|
58
|
+
|
59
|
+
def escapable_string(left, right = nil, interpolate: false)
|
60
|
+
right ||= left
|
61
|
+
seq(
|
62
|
+
left,
|
63
|
+
many(
|
64
|
+
alt(
|
65
|
+
*[
|
66
|
+
seq(string("\\"), right).fmap { [:literal, _1] },
|
67
|
+
interpolate ? seq(
|
68
|
+
string('#{'),
|
69
|
+
lazy { alt(balanced("{", "}", alt(all_strings, any_char)), many(none_of("}"))) },
|
70
|
+
string("}")
|
71
|
+
) : nil,
|
72
|
+
not_followed_by(right).bind { any_char }.fmap { [:literal, _1] }
|
73
|
+
].compact
|
74
|
+
)
|
75
|
+
),
|
76
|
+
right
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def reduce_tokens(tokens)
|
83
|
+
state = :literal
|
84
|
+
|
85
|
+
tokens.each_with_object([]) do |v, acc|
|
86
|
+
if v == :literal
|
87
|
+
acc << [:literal, +""] unless state == :literal
|
88
|
+
state = :next_literal
|
89
|
+
next acc
|
90
|
+
end
|
91
|
+
|
92
|
+
if state == :next_literal
|
93
|
+
state = :literal
|
94
|
+
acc.last[1] << v
|
95
|
+
next acc
|
96
|
+
end
|
97
|
+
|
98
|
+
if state == :literal
|
99
|
+
acc << [:code, +""]
|
100
|
+
end
|
101
|
+
|
102
|
+
state = :code
|
103
|
+
acc.last[1] << v
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
begin
|
4
|
+
require "parser/prism"
|
5
|
+
rescue LoadError
|
6
|
+
require "parser/ruby33"
|
7
|
+
end
|
4
8
|
|
5
9
|
module RubyNext
|
6
10
|
module Language
|
@@ -22,11 +26,18 @@ module RubyNext
|
|
22
26
|
unless method_defined?(:match_pattern_p)
|
23
27
|
include BuilderExt
|
24
28
|
end
|
29
|
+
|
30
|
+
def check_reserved_for_numparam(name, loc)
|
31
|
+
# We don't want to raise SyntaxError, 'cause we want to use _x vars for older Rubies.
|
32
|
+
# The exception should be raised by Ruby itself for versions supporting numbered parameters
|
33
|
+
end
|
25
34
|
end
|
26
35
|
|
27
36
|
class << self
|
37
|
+
attr_accessor :parser_class, :parser_syntax_errors
|
38
|
+
|
28
39
|
def parser
|
29
|
-
|
40
|
+
parser_class.new(Builder.new).tap do |prs|
|
30
41
|
prs.diagnostics.tap do |diagnostics|
|
31
42
|
diagnostics.all_errors_are_fatal = true
|
32
43
|
end
|
@@ -39,8 +50,8 @@ module RubyNext
|
|
39
50
|
end
|
40
51
|
|
41
52
|
parser.parse(buffer)
|
42
|
-
rescue
|
43
|
-
raise ::SyntaxError, e.message
|
53
|
+
rescue *parser_syntax_errors => e
|
54
|
+
raise ::SyntaxError, e.message, e.backtrace
|
44
55
|
end
|
45
56
|
|
46
57
|
def parse_with_comments(source, file = "(string)")
|
@@ -49,8 +60,22 @@ module RubyNext
|
|
49
60
|
end
|
50
61
|
|
51
62
|
parser.parse_with_comments(buffer)
|
52
|
-
rescue
|
53
|
-
raise ::SyntaxError, e.message
|
63
|
+
rescue *parser_syntax_errors => e
|
64
|
+
raise ::SyntaxError, e.message, e.backtrace
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
self.parser_syntax_errors = [::Parser::SyntaxError]
|
69
|
+
|
70
|
+
# Set up default parser
|
71
|
+
unless parser_class
|
72
|
+
self.parser_class = if defined?(::Parser::RubyNext)
|
73
|
+
::Parser::RubyNext
|
74
|
+
elsif defined?(::Parser::Prism)
|
75
|
+
parser_syntax_errors << ::Prism::ParserCompiler::CompilationError
|
76
|
+
::Parser::Prism
|
77
|
+
else
|
78
|
+
::Parser::Ruby33
|
54
79
|
end
|
55
80
|
end
|
56
81
|
end
|
@@ -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
|
@@ -45,8 +45,65 @@ module RubyNext
|
|
45
45
|
process_fargs(node, fargs)
|
46
46
|
end
|
47
47
|
|
48
|
+
def on_def(node)
|
49
|
+
return super unless forward_arg?(node.children[1])
|
50
|
+
|
51
|
+
new_node = super
|
52
|
+
|
53
|
+
name = node.children[0]
|
54
|
+
|
55
|
+
insert_after(node.loc.expression, "; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :#{name})")
|
56
|
+
|
57
|
+
s(:begin,
|
58
|
+
new_node,
|
59
|
+
ruby2_keywords_node(nil, name))
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_defs(node)
|
63
|
+
return super unless forward_arg?(node.children[2])
|
64
|
+
|
65
|
+
new_node = super
|
66
|
+
|
67
|
+
receiver = node.children[0]
|
68
|
+
name = node.children[1]
|
69
|
+
|
70
|
+
# Using self.ruby2_keywords :name results in undefined method error,
|
71
|
+
# singleton_class works as expected
|
72
|
+
receiver = s(:send, nil, :singleton_class) if receiver.type == :self
|
73
|
+
|
74
|
+
receiver_name =
|
75
|
+
case receiver.type
|
76
|
+
when :send
|
77
|
+
receiver.children[1]
|
78
|
+
when :const
|
79
|
+
receiver.children[1]
|
80
|
+
end
|
81
|
+
|
82
|
+
insert_after(node.loc.expression, "; #{receiver_name}.respond_to?(:ruby2_keywords, true) && (#{receiver_name}.send(:ruby2_keywords, :#{name}))")
|
83
|
+
|
84
|
+
s(:begin,
|
85
|
+
new_node,
|
86
|
+
ruby2_keywords_node(receiver, name))
|
87
|
+
end
|
88
|
+
|
48
89
|
private
|
49
90
|
|
91
|
+
def ruby2_keywords_node(receiver, name)
|
92
|
+
s(:and,
|
93
|
+
s(:send, receiver, :respond_to?,
|
94
|
+
s(:sym, :ruby2_keywords), s(:true)),
|
95
|
+
s(:begin,
|
96
|
+
s(:send, receiver, :send,
|
97
|
+
s(:sym, :ruby2_keywords),
|
98
|
+
s(:sym, name))))
|
99
|
+
end
|
100
|
+
|
101
|
+
def forward_arg?(args)
|
102
|
+
return false unless args&.children
|
103
|
+
|
104
|
+
args.children.any? { |arg| arg.type == :forward_arg }
|
105
|
+
end
|
106
|
+
|
50
107
|
def extract_fargs(node)
|
51
108
|
node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
|
52
109
|
end
|