ruby-next-core 0.15.2 → 1.0.0.rc.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 +32 -0
- data/README.md +118 -48
- 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 +10 -2
- data/lib/.rbnext/2.1/ruby-next/language.rb +54 -10
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +82 -2
- data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
- data/lib/.rbnext/2.3/ruby-next/core/data.rb +159 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +44 -9
- 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 +159 -0
- data/lib/.rbnext/2.7/ruby-next/core/data.rb +159 -0
- data/lib/.rbnext/2.7/ruby-next/core.rb +10 -2
- 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 +44 -9
- 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/commands/nextify.rb +84 -2
- data/lib/ruby-next/config.rb +27 -0
- data/lib/ruby-next/core/data.rb +159 -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/refinement/import.rb +44 -36
- data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
- data/lib/ruby-next/core.rb +10 -2
- data/lib/ruby-next/irb.rb +2 -2
- data/lib/ruby-next/language/bootsnap.rb +2 -25
- 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 +24 -2
- data/lib/ruby-next/language/rewriters/2.7/pattern_matching.rb +42 -7
- 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.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 +54 -10
- data/lib/ruby-next/pry.rb +1 -1
- 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.rb +2 -2
- metadata +65 -12
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The code below originates from https://github.com/saturnflyer/polyfill-data
|
4
|
+
|
5
|
+
if !Object.const_defined?(:Data) || !Data.respond_to?(:define)
|
6
|
+
|
7
|
+
# Drop legacy Data class
|
8
|
+
begin
|
9
|
+
Object.send(:remove_const, :Data)
|
10
|
+
rescue
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
class Data < Object
|
15
|
+
using RubyNext
|
16
|
+
|
17
|
+
class << self
|
18
|
+
undef_method :new
|
19
|
+
attr_reader :members
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.define(*args, &block)
|
23
|
+
raise ArgumentError if args.any?(/=/)
|
24
|
+
if block
|
25
|
+
mod = Module.new
|
26
|
+
mod.define_singleton_method(:_) do |klass|
|
27
|
+
klass.class_eval(&block)
|
28
|
+
end
|
29
|
+
arity_converter = mod.method(:_)
|
30
|
+
end
|
31
|
+
klass = ::Class.new(self)
|
32
|
+
|
33
|
+
klass.instance_variable_set(:@members, args.map(&:to_sym).freeze)
|
34
|
+
|
35
|
+
klass.define_singleton_method(:new) do |*new_args, **new_kwargs, &block|
|
36
|
+
init_kwargs = if new_args.any?
|
37
|
+
raise ArgumentError, "unknown arguments #{new_args[members.size..].join(", ")}" if new_args.size > members.size
|
38
|
+
members.take(new_args.size).zip(new_args).to_h
|
39
|
+
else
|
40
|
+
new_kwargs
|
41
|
+
end
|
42
|
+
|
43
|
+
allocate.tap do |instance|
|
44
|
+
instance.send(:initialize, **init_kwargs, &block)
|
45
|
+
end.freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
class << klass
|
49
|
+
alias_method :[], :new
|
50
|
+
undef_method :define
|
51
|
+
end
|
52
|
+
|
53
|
+
args.each do |arg|
|
54
|
+
if klass.method_defined?(arg)
|
55
|
+
raise ArgumentError, "duplicate member #{arg}"
|
56
|
+
end
|
57
|
+
klass.define_method(arg) do
|
58
|
+
@attributes[arg]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if arity_converter
|
63
|
+
klass.class_eval(&arity_converter)
|
64
|
+
end
|
65
|
+
|
66
|
+
klass
|
67
|
+
end
|
68
|
+
|
69
|
+
def members
|
70
|
+
self.class.members
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialize(**kwargs)
|
74
|
+
kwargs_size = kwargs.size
|
75
|
+
members_size = members.size
|
76
|
+
|
77
|
+
if kwargs_size > members_size
|
78
|
+
extras = kwargs.except(*members).keys
|
79
|
+
|
80
|
+
if extras.size > 1
|
81
|
+
raise ArgumentError, "unknown keywords: #{extras.map { |_1| ":#{_1}" }.join(", ")}"
|
82
|
+
else
|
83
|
+
raise ArgumentError, "unknown keyword: :#{extras.first}"
|
84
|
+
end
|
85
|
+
elsif kwargs_size < members_size
|
86
|
+
missing = members.select { |k| !kwargs.include?(k) }
|
87
|
+
|
88
|
+
if missing.size > 1
|
89
|
+
raise ArgumentError, "missing keywords: #{missing.map { |_1| ":#{_1}" }.join(", ")}"
|
90
|
+
else
|
91
|
+
raise ArgumentError, "missing keyword: :#{missing.first}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
@attributes = members.map { |m| [m, kwargs[m]] }.to_h
|
96
|
+
end
|
97
|
+
|
98
|
+
def deconstruct
|
99
|
+
@attributes.values
|
100
|
+
end
|
101
|
+
|
102
|
+
def deconstruct_keys(array)
|
103
|
+
raise TypeError unless array.is_a?(Array) || array.nil?
|
104
|
+
return @attributes if array&.first.nil?
|
105
|
+
|
106
|
+
@attributes.slice(*array)
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_h(&block)
|
110
|
+
@attributes.to_h(&block)
|
111
|
+
end
|
112
|
+
|
113
|
+
def hash
|
114
|
+
to_h.hash
|
115
|
+
end
|
116
|
+
|
117
|
+
def eql?(other)
|
118
|
+
self.class == other.class && hash == other.hash
|
119
|
+
end
|
120
|
+
|
121
|
+
def ==(other)
|
122
|
+
self.class == other.class && to_h == other.to_h
|
123
|
+
end
|
124
|
+
|
125
|
+
def inspect
|
126
|
+
attribute_markers = @attributes.map do |key, value|
|
127
|
+
insect_key = key.to_s.start_with?("@") ? ":#{key}" : key
|
128
|
+
"#{insect_key}=#{value}"
|
129
|
+
end.join(", ")
|
130
|
+
|
131
|
+
display = ["data", self.class.name, attribute_markers].compact.join(" ")
|
132
|
+
|
133
|
+
"#<#{display}>"
|
134
|
+
end
|
135
|
+
alias_method :to_s, :inspect
|
136
|
+
|
137
|
+
def with(**kwargs)
|
138
|
+
return self if kwargs.empty?
|
139
|
+
|
140
|
+
self.class.new(**@attributes.merge(kwargs))
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def marshal_dump
|
146
|
+
@attributes
|
147
|
+
end
|
148
|
+
|
149
|
+
def marshal_load(attributes)
|
150
|
+
@attributes = attributes
|
151
|
+
freeze
|
152
|
+
end
|
153
|
+
|
154
|
+
def initialize_copy(source)
|
155
|
+
super.freeze
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -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"
|
@@ -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,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 { |_1| [: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 { |_1| [: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
|
@@ -185,7 +185,7 @@ module RubyNext
|
|
185
185
|
# rubocop:disable Style/MissingRespondToMissing
|
186
186
|
class Noop < Base
|
187
187
|
# Return node itself, no memoization
|
188
|
-
def method_missing(mid, node, *)
|
188
|
+
def method_missing(mid, node, *__rest__)
|
189
189
|
node
|
190
190
|
end
|
191
191
|
end
|
@@ -425,15 +425,15 @@ module RubyNext
|
|
425
425
|
s(:begin,
|
426
426
|
s(:and,
|
427
427
|
node,
|
428
|
-
send(:"#{pattern.type}_clause", pattern)))
|
428
|
+
send(:"#{pattern.type}_clause", pattern, right)))
|
429
429
|
end
|
430
430
|
end
|
431
431
|
|
432
|
-
def match_alt_clause(node)
|
432
|
+
def match_alt_clause(node, matchee = s(:lvar, locals[:matchee]))
|
433
433
|
children = locals.with(ALTERNATION_MARKER => true) do
|
434
434
|
node.children.map.with_index do |child, i|
|
435
435
|
predicates.terminate! if i == 1
|
436
|
-
send :"#{child.type}_clause", child
|
436
|
+
send :"#{child.type}_clause", child, matchee
|
437
437
|
end
|
438
438
|
end
|
439
439
|
s(:begin, s(:or, *children))
|
@@ -663,6 +663,14 @@ module RubyNext
|
|
663
663
|
end
|
664
664
|
end
|
665
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
|
+
|
666
674
|
def match_alt_array_element(node, index)
|
667
675
|
children = node.children.map do |child, i|
|
668
676
|
send :"#{child.type}_array_element", child, index
|
@@ -671,11 +679,19 @@ module RubyNext
|
|
671
679
|
end
|
672
680
|
|
673
681
|
def match_var_array_element(node, index)
|
674
|
-
|
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
|
675
687
|
end
|
676
688
|
|
677
689
|
def match_as_array_element(node, index)
|
678
|
-
|
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
|
679
695
|
end
|
680
696
|
|
681
697
|
def pin_array_element(node, index)
|
@@ -844,6 +860,15 @@ module RubyNext
|
|
844
860
|
end
|
845
861
|
end
|
846
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
|
+
|
847
872
|
def hash_element(head, *tail)
|
848
873
|
send("#{head.type}_hash_element", head).then do |node|
|
849
874
|
next node if tail.empty?
|
@@ -884,12 +909,22 @@ module RubyNext
|
|
884
909
|
end
|
885
910
|
|
886
911
|
def match_as_hash_element(node, key)
|
887
|
-
|
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
|
888
918
|
end
|
889
919
|
|
890
920
|
def match_var_hash_element(node, key = nil)
|
891
921
|
key ||= node.children[0]
|
892
|
-
|
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
|
893
928
|
end
|
894
929
|
|
895
930
|
def match_nil_pattern_hash_element(node, _key = nil)
|
@@ -1000,7 +1035,7 @@ module RubyNext
|
|
1000
1035
|
s(:send, node, :respond_to?, mid.to_ast_node)
|
1001
1036
|
end
|
1002
1037
|
|
1003
|
-
def respond_to_missing?(mid, *)
|
1038
|
+
def respond_to_missing?(mid, *__rest__)
|
1004
1039
|
return true if mid.to_s.match?(/_(clause|array_element)/)
|
1005
1040
|
super
|
1006
1041
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ruby-next/language/paco_parser"
|
4
|
+
|
5
|
+
module RubyNext
|
6
|
+
module Language
|
7
|
+
module Rewriters
|
8
|
+
class Text < Abstract
|
9
|
+
using RubyNext
|
10
|
+
|
11
|
+
class Normalizer < PacoParsers::Base
|
12
|
+
attr_reader :store
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@store = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def normalizing(source)
|
19
|
+
many(
|
20
|
+
alt(
|
21
|
+
ruby_comment,
|
22
|
+
ruby_string,
|
23
|
+
ruby_code
|
24
|
+
)
|
25
|
+
).parse(source, with_callstack: true)
|
26
|
+
.then(&:join)
|
27
|
+
.then do |_1|
|
28
|
+
if block_given?
|
29
|
+
yield _1
|
30
|
+
else
|
31
|
+
_1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
.then do |new_source|
|
35
|
+
restore(new_source)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ruby_comment
|
40
|
+
parse_comments.fmap do |result|
|
41
|
+
store << result
|
42
|
+
"# A#{store.size}Я\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ruby_string
|
47
|
+
parse_strings.fmap do |result|
|
48
|
+
result.each_with_object([]) do |(type, str), acc|
|
49
|
+
if type == :literal
|
50
|
+
store << str
|
51
|
+
acc << "_A#{store.size}Я_"
|
52
|
+
else
|
53
|
+
acc << str
|
54
|
+
end
|
55
|
+
acc
|
56
|
+
end.join
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ruby_code
|
61
|
+
any_char
|
62
|
+
end
|
63
|
+
|
64
|
+
def restore(source)
|
65
|
+
source.gsub(/(?:\# |_)A(\d+)Я(?:_|\n)/m) do |*args|
|
66
|
+
store[$1.to_i - 1]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_comments
|
71
|
+
memoize { PacoParsers::Comments.new.default }
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_strings
|
75
|
+
memoize { PacoParsers::StringLiterals.new.default }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Base class for rewriting parsers which adds the #track! method
|
80
|
+
class PacoParser < PacoParsers::Base
|
81
|
+
attr_reader :rewriter, :context
|
82
|
+
|
83
|
+
def initialize(rewriter, context)
|
84
|
+
@rewriter = rewriter
|
85
|
+
@context = context
|
86
|
+
end
|
87
|
+
|
88
|
+
def track!
|
89
|
+
context.track!(rewriter)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
def parser(&block)
|
95
|
+
@paco_parser = Class.new(PacoParser, &block)
|
96
|
+
end
|
97
|
+
|
98
|
+
def paco_parser
|
99
|
+
return @paco_parser if @paco_parser
|
100
|
+
|
101
|
+
superclass.paco_parser if superclass.respond_to?(:paco_parser)
|
102
|
+
end
|
103
|
+
|
104
|
+
def text?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Rewrite source code by ignoring string literals and comments
|
110
|
+
def rewrite(source)
|
111
|
+
Normalizer.new.normalizing(source) do |normalized|
|
112
|
+
safe_rewrite(normalized)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def safe_rewrite(source)
|
117
|
+
source
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def parse(source)
|
123
|
+
parser_class = self.class.paco_parser
|
124
|
+
raise "No parser defined for #{self.class}" unless parser_class
|
125
|
+
|
126
|
+
paco_parser = self.class.paco_parser.new(self, context)
|
127
|
+
paco_parser.parse(source)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module RubyNext
|
6
|
+
module Commands
|
7
|
+
class Base
|
8
|
+
class << self
|
9
|
+
def run(args)
|
10
|
+
new(args).run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :dry_run
|
15
|
+
alias dry_run? dry_run
|
16
|
+
|
17
|
+
def initialize(args)
|
18
|
+
parse! args
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse!(*__rest__)
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
def log(msg)
|
30
|
+
return unless CLI.verbose?
|
31
|
+
|
32
|
+
if CLI.dry_run?
|
33
|
+
$stdout.puts "[DRY RUN] #{msg}"
|
34
|
+
else
|
35
|
+
$stdout.puts msg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def base_parser
|
40
|
+
OptionParser.new do |opts|
|
41
|
+
yield opts
|
42
|
+
|
43
|
+
opts.on("-V", "Turn on verbose mode") do
|
44
|
+
CLI.verbose = true
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--dry-run", "Print verbose output without generating files") do
|
48
|
+
CLI.dry_run = true
|
49
|
+
CLI.verbose = true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|