parser 2.7.1.1 → 3.0.2.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/lib/parser.rb +1 -0
- data/lib/parser/all.rb +2 -0
- data/lib/parser/ast/processor.rb +5 -0
- data/lib/parser/base.rb +7 -5
- data/lib/parser/builders/default.rb +263 -23
- data/lib/parser/context.rb +5 -0
- data/lib/parser/current.rb +24 -6
- data/lib/parser/current_arg_stack.rb +5 -2
- data/lib/parser/diagnostic.rb +1 -1
- data/lib/parser/diagnostic/engine.rb +1 -2
- data/lib/parser/lexer.rb +887 -803
- data/lib/parser/macruby.rb +2214 -2189
- data/lib/parser/max_numparam_stack.rb +13 -5
- data/lib/parser/messages.rb +18 -0
- data/lib/parser/meta.rb +6 -5
- data/lib/parser/ruby18.rb +9 -3
- data/lib/parser/ruby19.rb +2297 -2289
- data/lib/parser/ruby20.rb +2413 -2397
- data/lib/parser/ruby21.rb +2419 -2411
- data/lib/parser/ruby22.rb +2468 -2460
- data/lib/parser/ruby23.rb +2452 -2452
- data/lib/parser/ruby24.rb +2435 -2430
- data/lib/parser/ruby25.rb +2220 -2214
- data/lib/parser/ruby26.rb +2220 -2214
- data/lib/parser/ruby27.rb +3715 -3615
- data/lib/parser/ruby28.rb +8047 -0
- data/lib/parser/ruby30.rb +8060 -0
- data/lib/parser/ruby31.rb +8226 -0
- data/lib/parser/rubymotion.rb +2190 -2182
- data/lib/parser/runner.rb +31 -2
- data/lib/parser/runner/ruby_rewrite.rb +2 -2
- data/lib/parser/source/buffer.rb +53 -28
- data/lib/parser/source/comment.rb +14 -1
- data/lib/parser/source/comment/associator.rb +31 -8
- data/lib/parser/source/map/method_definition.rb +25 -0
- data/lib/parser/source/range.rb +10 -3
- data/lib/parser/source/tree_rewriter.rb +100 -10
- data/lib/parser/source/tree_rewriter/action.rb +114 -21
- data/lib/parser/static_environment.rb +4 -0
- data/lib/parser/tree_rewriter.rb +1 -2
- data/lib/parser/variables_stack.rb +4 -0
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +3 -18
- metadata +17 -98
- data/.gitignore +0 -33
- data/.travis.yml +0 -42
- data/.yardopts +0 -21
- data/CHANGELOG.md +0 -1075
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -10
- data/README.md +0 -309
- data/Rakefile +0 -166
- data/ci/run_rubocop_specs +0 -14
- data/doc/AST_FORMAT.md +0 -2180
- data/doc/CUSTOMIZATION.md +0 -37
- data/doc/INTERNALS.md +0 -21
- data/doc/css/.gitkeep +0 -0
- data/doc/css/common.css +0 -68
- data/lib/parser/lexer.rl +0 -2536
- data/lib/parser/macruby.y +0 -2198
- data/lib/parser/ruby18.y +0 -1934
- data/lib/parser/ruby19.y +0 -2175
- data/lib/parser/ruby20.y +0 -2353
- data/lib/parser/ruby21.y +0 -2357
- data/lib/parser/ruby22.y +0 -2364
- data/lib/parser/ruby23.y +0 -2370
- data/lib/parser/ruby24.y +0 -2408
- data/lib/parser/ruby25.y +0 -2405
- data/lib/parser/ruby26.y +0 -2413
- data/lib/parser/ruby27.y +0 -2941
- data/lib/parser/rubymotion.y +0 -2182
- data/test/bug_163/fixtures/input.rb +0 -5
- data/test/bug_163/fixtures/output.rb +0 -5
- data/test/bug_163/rewriter.rb +0 -20
- data/test/helper.rb +0 -60
- data/test/parse_helper.rb +0 -319
- data/test/racc_coverage_helper.rb +0 -133
- data/test/test_base.rb +0 -31
- data/test/test_current.rb +0 -29
- data/test/test_diagnostic.rb +0 -96
- data/test/test_diagnostic_engine.rb +0 -62
- data/test/test_encoding.rb +0 -99
- data/test/test_lexer.rb +0 -3608
- data/test/test_lexer_stack_state.rb +0 -78
- data/test/test_parse_helper.rb +0 -80
- data/test/test_parser.rb +0 -9430
- data/test/test_runner_parse.rb +0 -35
- data/test/test_runner_rewrite.rb +0 -47
- data/test/test_source_buffer.rb +0 -162
- data/test/test_source_comment.rb +0 -36
- data/test/test_source_comment_associator.rb +0 -367
- data/test/test_source_map.rb +0 -15
- data/test/test_source_range.rb +0 -187
- data/test/test_source_rewriter.rb +0 -541
- data/test/test_source_rewriter_action.rb +0 -46
- data/test/test_source_tree_rewriter.rb +0 -253
- data/test/test_static_environment.rb +0 -45
- data/test/using_tree_rewriter/fixtures/input.rb +0 -3
- data/test/using_tree_rewriter/fixtures/output.rb +0 -3
- data/test/using_tree_rewriter/using_tree_rewriter.rb +0 -9
data/lib/parser/runner.rb
CHANGED
@@ -14,9 +14,8 @@ module Parser
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def initialize
|
17
|
-
Parser::Builders::Default.modernize
|
18
|
-
|
19
17
|
@option_parser = OptionParser.new { |opts| setup_option_parsing(opts) }
|
18
|
+
@legacy = {}
|
20
19
|
@parser_class = nil
|
21
20
|
@parser = nil
|
22
21
|
@files = []
|
@@ -30,6 +29,7 @@ module Parser
|
|
30
29
|
|
31
30
|
def execute(options)
|
32
31
|
parse_options(options)
|
32
|
+
setup_builder_default
|
33
33
|
prepare_parser
|
34
34
|
|
35
35
|
process_all_input
|
@@ -37,6 +37,8 @@ module Parser
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
+
LEGACY_MODES = %i[lambda procarg0 encoding index arg_inside_procarg0 forward_arg kwargs match_pattern].freeze
|
41
|
+
|
40
42
|
def runner_name
|
41
43
|
raise NotImplementedError, "implement #{self.class}##{__callee__}"
|
42
44
|
end
|
@@ -111,6 +113,16 @@ module Parser
|
|
111
113
|
@parser_class = Parser::Ruby27
|
112
114
|
end
|
113
115
|
|
116
|
+
opts.on '--30', 'Parse as Ruby 3.0 would' do
|
117
|
+
require 'parser/ruby30'
|
118
|
+
@parser_class = Parser::Ruby30
|
119
|
+
end
|
120
|
+
|
121
|
+
opts.on '--31', 'Parse as Ruby 3.1 would' do
|
122
|
+
require 'parser/ruby31'
|
123
|
+
@parser_class = Parser::Ruby31
|
124
|
+
end
|
125
|
+
|
114
126
|
opts.on '--mac', 'Parse as MacRuby 0.12 would' do
|
115
127
|
require 'parser/macruby'
|
116
128
|
@parser_class = Parser::MacRuby
|
@@ -121,6 +133,17 @@ module Parser
|
|
121
133
|
@parser_class = Parser::RubyMotion
|
122
134
|
end
|
123
135
|
|
136
|
+
opts.on '--legacy', "Parse with all legacy modes" do
|
137
|
+
@legacy = Hash.new(true)
|
138
|
+
end
|
139
|
+
|
140
|
+
LEGACY_MODES.each do |mode|
|
141
|
+
opt_name = "--legacy-#{mode.to_s.gsub('_', '-')}"
|
142
|
+
opts.on opt_name, "Parse with legacy mode for emit_#{mode}" do
|
143
|
+
@legacy[mode] = true
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
124
147
|
opts.on '-w', '--warnings', 'Enable warnings' do |w|
|
125
148
|
@warnings = w
|
126
149
|
end
|
@@ -159,6 +182,12 @@ module Parser
|
|
159
182
|
end
|
160
183
|
end
|
161
184
|
|
185
|
+
def setup_builder_default
|
186
|
+
LEGACY_MODES.each do |mode|
|
187
|
+
Parser::Builders::Default.send(:"emit_#{mode}=", !@legacy[mode])
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
162
191
|
def prepare_parser
|
163
192
|
@parser = @parser_class.new
|
164
193
|
|
@@ -55,8 +55,8 @@ module Parser
|
|
55
55
|
new_source = rewriter.rewrite(buffer, ast)
|
56
56
|
|
57
57
|
new_buffer = Source::Buffer.new(initial_buffer.name +
|
58
|
-
'|after ' + rewriter_class.name
|
59
|
-
|
58
|
+
'|after ' + rewriter_class.name,
|
59
|
+
source: new_source)
|
60
60
|
|
61
61
|
@parser.reset
|
62
62
|
new_ast = @parser.parse(new_buffer)
|
data/lib/parser/source/buffer.rb
CHANGED
@@ -102,7 +102,7 @@ module Parser
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
def initialize(name, first_line = 1)
|
105
|
+
def initialize(name, first_line = 1, source: nil)
|
106
106
|
@name = name.to_s
|
107
107
|
@source = nil
|
108
108
|
@first_line = first_line
|
@@ -114,8 +114,9 @@ module Parser
|
|
114
114
|
@slice_source = nil
|
115
115
|
|
116
116
|
# Cache for fast lookup
|
117
|
-
@
|
118
|
-
|
117
|
+
@line_index_for_position = {}
|
118
|
+
|
119
|
+
self.source = source if source
|
119
120
|
end
|
120
121
|
|
121
122
|
##
|
@@ -205,9 +206,10 @@ module Parser
|
|
205
206
|
# @return [[Integer, Integer]] `[line, column]`
|
206
207
|
#
|
207
208
|
def decompose_position(position)
|
208
|
-
|
209
|
+
line_index = line_index_for_position(position)
|
210
|
+
line_begin = line_begins[line_index]
|
209
211
|
|
210
|
-
[ @first_line +
|
212
|
+
[ @first_line + line_index , position - line_begin ]
|
211
213
|
end
|
212
214
|
|
213
215
|
##
|
@@ -218,10 +220,7 @@ module Parser
|
|
218
220
|
# @api private
|
219
221
|
#
|
220
222
|
def line_for_position(position)
|
221
|
-
|
222
|
-
line_no, _ = line_for(position)
|
223
|
-
@first_line + line_no
|
224
|
-
end
|
223
|
+
line_index_for_position(position) + @first_line
|
225
224
|
end
|
226
225
|
|
227
226
|
##
|
@@ -232,10 +231,8 @@ module Parser
|
|
232
231
|
# @api private
|
233
232
|
#
|
234
233
|
def column_for_position(position)
|
235
|
-
|
236
|
-
|
237
|
-
position - line_begin
|
238
|
-
end
|
234
|
+
line_index = line_index_for_position(position)
|
235
|
+
position - line_begins[line_index]
|
239
236
|
end
|
240
237
|
|
241
238
|
##
|
@@ -276,15 +273,13 @@ module Parser
|
|
276
273
|
# @raise [IndexError] if `lineno` is out of bounds
|
277
274
|
#
|
278
275
|
def line_range(lineno)
|
279
|
-
index = lineno - @first_line
|
280
|
-
if index
|
276
|
+
index = lineno - @first_line
|
277
|
+
if index < 0 || index + 1 >= line_begins.size
|
281
278
|
raise IndexError, 'Parser::Source::Buffer: range for line ' \
|
282
279
|
"#{lineno} requested, valid line numbers are #{@first_line}.." \
|
283
|
-
"#{@first_line + line_begins.size -
|
284
|
-
elsif index == line_begins.size
|
285
|
-
Range.new(self, line_begins[-index][1], @source.size)
|
280
|
+
"#{@first_line + line_begins.size - 2}"
|
286
281
|
else
|
287
|
-
Range.new(self, line_begins[
|
282
|
+
Range.new(self, line_begins[index], line_begins[index + 1] - 1)
|
288
283
|
end
|
289
284
|
end
|
290
285
|
|
@@ -301,27 +296,57 @@ module Parser
|
|
301
296
|
# @return [Integer]
|
302
297
|
#
|
303
298
|
def last_line
|
304
|
-
line_begins.size + @first_line -
|
299
|
+
line_begins.size + @first_line - 2
|
300
|
+
end
|
301
|
+
|
302
|
+
# :nodoc:
|
303
|
+
def freeze
|
304
|
+
source_lines; line_begins; source_range # build cache
|
305
|
+
super
|
306
|
+
end
|
307
|
+
|
308
|
+
# :nodoc:
|
309
|
+
def inspect
|
310
|
+
"#<#{self.class} #{name}>"
|
305
311
|
end
|
306
312
|
|
307
313
|
private
|
308
314
|
|
315
|
+
# @returns [0, line_begin_of_line_1, ..., source.size + 1]
|
309
316
|
def line_begins
|
310
|
-
|
311
|
-
|
312
|
-
|
317
|
+
@line_begins ||= begin
|
318
|
+
begins = [0]
|
319
|
+
index = 0
|
313
320
|
while index = @source.index("\n".freeze, index)
|
314
321
|
index += 1
|
315
|
-
|
322
|
+
begins << index
|
316
323
|
end
|
324
|
+
begins << @source.size + 1
|
325
|
+
begins
|
317
326
|
end
|
327
|
+
end
|
318
328
|
|
319
|
-
|
329
|
+
# @returns 0-based line index of position
|
330
|
+
def line_index_for_position(position)
|
331
|
+
@line_index_for_position[position] || begin
|
332
|
+
index = bsearch(line_begins, position) - 1
|
333
|
+
@line_index_for_position[position] = index unless @line_index_for_position.frozen?
|
334
|
+
index
|
335
|
+
end
|
320
336
|
end
|
321
337
|
|
322
|
-
|
323
|
-
line_begins
|
324
|
-
|
338
|
+
if Array.method_defined?(:bsearch_index) # RUBY_VERSION >= 2.3
|
339
|
+
def bsearch(line_begins, position)
|
340
|
+
line_begins.bsearch_index do |line_begin|
|
341
|
+
position < line_begin
|
342
|
+
end || line_begins.size - 1 # || only for out of bound values
|
343
|
+
end
|
344
|
+
else
|
345
|
+
def bsearch(line_begins, position)
|
346
|
+
@line_range ||= 0...line_begins.size
|
347
|
+
@line_range.bsearch do |i|
|
348
|
+
position < line_begins[i]
|
349
|
+
end || line_begins.size - 1 # || only for out of bound values
|
325
350
|
end
|
326
351
|
end
|
327
352
|
end
|
@@ -10,7 +10,7 @@ module Parser
|
|
10
10
|
# @return [String]
|
11
11
|
#
|
12
12
|
# @!attribute [r] location
|
13
|
-
# @return [Parser::Source::
|
13
|
+
# @return [Parser::Source::Range]
|
14
14
|
#
|
15
15
|
# @api public
|
16
16
|
#
|
@@ -48,6 +48,19 @@ module Parser
|
|
48
48
|
associator.associate_locations
|
49
49
|
end
|
50
50
|
|
51
|
+
##
|
52
|
+
# Associate `comments` with `ast` nodes using identity.
|
53
|
+
#
|
54
|
+
# @param [Parser::AST::Node] ast
|
55
|
+
# @param [Array<Comment>] comments
|
56
|
+
# @return [Hash<Parser::Source::Node, Array<Comment>>]
|
57
|
+
# @see Parser::Source::Comment::Associator#associate_by_identity
|
58
|
+
#
|
59
|
+
def self.associate_by_identity(ast, comments)
|
60
|
+
associator = Associator.new(ast, comments)
|
61
|
+
associator.associate_by_identity
|
62
|
+
end
|
63
|
+
|
51
64
|
##
|
52
65
|
# @param [Parser::Source::Range] range
|
53
66
|
#
|
@@ -84,12 +84,24 @@ module Parser
|
|
84
84
|
#
|
85
85
|
# Note that {associate} produces unexpected result for nodes which are
|
86
86
|
# equal but have distinct locations; comments for these nodes are merged.
|
87
|
+
# You may prefer using {associate_by_identity} or {associate_locations}.
|
87
88
|
#
|
88
89
|
# @return [Hash<Parser::AST::Node, Array<Parser::Source::Comment>>]
|
89
90
|
# @deprecated Use {associate_locations}.
|
90
91
|
#
|
91
92
|
def associate
|
92
|
-
@
|
93
|
+
@map_using = :eql
|
94
|
+
do_associate
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Same as {associate}, but compares by identity, thus producing an unambiguous
|
99
|
+
# result even in presence of equal nodes.
|
100
|
+
#
|
101
|
+
# @return [Hash<Parser::Source::Node, Array<Parser::Source::Comment>>]
|
102
|
+
#
|
103
|
+
def associate_locations
|
104
|
+
@map_using = :location
|
93
105
|
do_associate
|
94
106
|
end
|
95
107
|
|
@@ -100,15 +112,29 @@ module Parser
|
|
100
112
|
#
|
101
113
|
# @return [Hash<Parser::Source::Map, Array<Parser::Source::Comment>>]
|
102
114
|
#
|
103
|
-
def
|
104
|
-
@
|
115
|
+
def associate_by_identity
|
116
|
+
@map_using = :identity
|
105
117
|
do_associate
|
106
118
|
end
|
107
119
|
|
108
120
|
private
|
109
121
|
|
122
|
+
POSTFIX_TYPES = Set[:if, :while, :while_post, :until, :until_post, :masgn].freeze
|
123
|
+
def children_in_source_order(node)
|
124
|
+
if POSTFIX_TYPES.include?(node.type)
|
125
|
+
# All these types have either nodes with expressions, or `nil`
|
126
|
+
# so a compact will do, but they need to be sorted.
|
127
|
+
node.children.compact.sort_by { |child| child.loc.expression.begin_pos }
|
128
|
+
else
|
129
|
+
node.children.select do |child|
|
130
|
+
child.is_a?(AST::Node) && child.loc && child.loc.expression
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
110
135
|
def do_associate
|
111
136
|
@mapping = Hash.new { |h, k| h[k] = [] }
|
137
|
+
@mapping.compare_by_identity if @map_using == :identity
|
112
138
|
@comment_num = -1
|
113
139
|
advance_comment
|
114
140
|
|
@@ -131,10 +157,7 @@ module Parser
|
|
131
157
|
node_loc = node.location
|
132
158
|
if @current_comment.location.line <= node_loc.last_line ||
|
133
159
|
node_loc.is_a?(Map::Heredoc)
|
134
|
-
node.
|
135
|
-
next unless child.is_a?(AST::Node) && child.loc && child.loc.expression
|
136
|
-
visit(child)
|
137
|
-
end
|
160
|
+
children_in_source_order(node).each { |child| visit(child) }
|
138
161
|
|
139
162
|
process_trailing_comments(node)
|
140
163
|
end
|
@@ -181,7 +204,7 @@ module Parser
|
|
181
204
|
end
|
182
205
|
|
183
206
|
def associate_and_advance_comment(node)
|
184
|
-
key = @
|
207
|
+
key = @map_using == :location ? node.location : node
|
185
208
|
@mapping[key] << @current_comment
|
186
209
|
advance_comment
|
187
210
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parser
|
4
|
+
module Source
|
5
|
+
|
6
|
+
class Map::MethodDefinition < Map
|
7
|
+
attr_reader :keyword
|
8
|
+
attr_reader :operator
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :end
|
11
|
+
attr_reader :assignment
|
12
|
+
|
13
|
+
def initialize(keyword_l, operator_l, name_l, end_l, assignment_l, body_l)
|
14
|
+
@keyword = keyword_l
|
15
|
+
@operator = operator_l
|
16
|
+
@name = name_l
|
17
|
+
@end = end_l
|
18
|
+
@assignment = assignment_l
|
19
|
+
|
20
|
+
super(@keyword.join(end_l || body_l))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/lib/parser/source/range.rb
CHANGED
@@ -13,7 +13,7 @@ module Parser
|
|
13
13
|
# ^^
|
14
14
|
#
|
15
15
|
# @!attribute [r] source_buffer
|
16
|
-
# @return [Parser::
|
16
|
+
# @return [Parser::Source::Buffer]
|
17
17
|
#
|
18
18
|
# @!attribute [r] begin_pos
|
19
19
|
# @return [Integer] index of the first character in the range
|
@@ -112,11 +112,11 @@ module Parser
|
|
112
112
|
# @raise RangeError
|
113
113
|
#
|
114
114
|
def column_range
|
115
|
-
if
|
115
|
+
if line != last_line
|
116
116
|
raise RangeError, "#{self.inspect} spans more than one line"
|
117
117
|
end
|
118
118
|
|
119
|
-
|
119
|
+
column...last_column
|
120
120
|
end
|
121
121
|
|
122
122
|
##
|
@@ -149,6 +149,13 @@ module Parser
|
|
149
149
|
(@begin_pos...@end_pos).to_a
|
150
150
|
end
|
151
151
|
|
152
|
+
##
|
153
|
+
# @return [Range] a Ruby range with the same `begin_pos` and `end_pos`
|
154
|
+
#
|
155
|
+
def to_range
|
156
|
+
self.begin_pos...self.end_pos
|
157
|
+
end
|
158
|
+
|
152
159
|
##
|
153
160
|
# Composes a GNU/Clang-style string representation of the beginning of this
|
154
161
|
# range.
|
@@ -129,6 +129,8 @@ module Parser
|
|
129
129
|
##
|
130
130
|
# Merges the updates of argument with the receiver.
|
131
131
|
# Policies of the receiver are used.
|
132
|
+
# This action is atomic in that it won't change the receiver
|
133
|
+
# unless it succeeds.
|
132
134
|
#
|
133
135
|
# @param [Rewriter] with
|
134
136
|
# @return [Rewriter] self
|
@@ -154,6 +156,32 @@ module Parser
|
|
154
156
|
dup.merge!(with)
|
155
157
|
end
|
156
158
|
|
159
|
+
##
|
160
|
+
# For special cases where one needs to merge a rewriter attached to a different source_buffer
|
161
|
+
# or that needs to be offset. Policies of the receiver are used.
|
162
|
+
#
|
163
|
+
# @param [TreeRewriter] rewriter from different source_buffer
|
164
|
+
# @param [Integer] offset
|
165
|
+
# @return [Rewriter] self
|
166
|
+
# @raise [IndexError] if action ranges (once offset) don't fit the current buffer
|
167
|
+
#
|
168
|
+
def import!(foreign_rewriter, offset: 0)
|
169
|
+
return self if foreign_rewriter.empty?
|
170
|
+
|
171
|
+
contracted = foreign_rewriter.action_root.contract
|
172
|
+
merge_effective_range = ::Parser::Source::Range.new(
|
173
|
+
@source_buffer,
|
174
|
+
contracted.range.begin_pos + offset,
|
175
|
+
contracted.range.end_pos + offset,
|
176
|
+
)
|
177
|
+
check_range_validity(merge_effective_range)
|
178
|
+
|
179
|
+
merge_with = contracted.moved(@source_buffer, offset)
|
180
|
+
|
181
|
+
@action_root = @action_root.combine(merge_with)
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
157
185
|
##
|
158
186
|
# Replaces the code of the source range `range` with `content`.
|
159
187
|
#
|
@@ -222,19 +250,54 @@ module Parser
|
|
222
250
|
# @return [String]
|
223
251
|
#
|
224
252
|
def process
|
225
|
-
source = @source_buffer.source
|
226
|
-
adjustment = 0
|
253
|
+
source = @source_buffer.source
|
227
254
|
|
255
|
+
chunks = []
|
256
|
+
last_end = 0
|
228
257
|
@action_root.ordered_replacements.each do |range, replacement|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
source[begin_pos...end_pos] = replacement
|
233
|
-
|
234
|
-
adjustment += replacement.length - range.length
|
258
|
+
chunks << source[last_end...range.begin_pos] << replacement
|
259
|
+
last_end = range.end_pos
|
235
260
|
end
|
261
|
+
chunks << source[last_end...source.length]
|
262
|
+
chunks.join
|
263
|
+
end
|
236
264
|
|
237
|
-
|
265
|
+
##
|
266
|
+
# Returns a representation of the rewriter as an ordered list of replacements.
|
267
|
+
#
|
268
|
+
# rewriter.as_replacements # => [ [1...1, '('],
|
269
|
+
# [2...4, 'foo'],
|
270
|
+
# [5...6, ''],
|
271
|
+
# [6...6, '!'],
|
272
|
+
# [10...10, ')'],
|
273
|
+
# ]
|
274
|
+
#
|
275
|
+
# This representation is sufficient to recreate the result of `process` but it is
|
276
|
+
# not sufficient to recreate completely the rewriter for further merging/actions.
|
277
|
+
# See `as_nested_actions`
|
278
|
+
#
|
279
|
+
# @return [Array<Range, String>] an ordered list of pairs of range & replacement
|
280
|
+
#
|
281
|
+
def as_replacements
|
282
|
+
@action_root.ordered_replacements
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# Returns a representation of the rewriter as nested insertions (:wrap) and replacements.
|
287
|
+
#
|
288
|
+
# rewriter.as_actions # =>[ [:wrap, 1...10, '(', ')'],
|
289
|
+
# [:wrap, 2...6, '', '!'], # aka "insert_after"
|
290
|
+
# [:replace, 2...4, 'foo'],
|
291
|
+
# [:replace, 5...6, ''], # aka "removal"
|
292
|
+
# ],
|
293
|
+
#
|
294
|
+
# Contrary to `as_replacements`, this representation is sufficient to recreate exactly
|
295
|
+
# the rewriter.
|
296
|
+
#
|
297
|
+
# @return [Array<(Symbol, Range, String{, String})>]
|
298
|
+
#
|
299
|
+
def as_nested_actions
|
300
|
+
@action_root.nested_actions
|
238
301
|
end
|
239
302
|
|
240
303
|
##
|
@@ -267,6 +330,11 @@ module Parser
|
|
267
330
|
@in_transaction
|
268
331
|
end
|
269
332
|
|
333
|
+
# :nodoc:
|
334
|
+
def inspect
|
335
|
+
"#<#{self.class} #{source_buffer.name}: #{action_summary}>"
|
336
|
+
end
|
337
|
+
|
270
338
|
##
|
271
339
|
# @api private
|
272
340
|
# @deprecated Use insert_after or wrap
|
@@ -298,6 +366,28 @@ module Parser
|
|
298
366
|
|
299
367
|
private
|
300
368
|
|
369
|
+
def action_summary
|
370
|
+
replacements = as_replacements
|
371
|
+
case replacements.size
|
372
|
+
when 0 then return 'empty'
|
373
|
+
when 1..3 then #ok
|
374
|
+
else
|
375
|
+
replacements = replacements.first(3)
|
376
|
+
suffix = '…'
|
377
|
+
end
|
378
|
+
parts = replacements.map do |(range, str)|
|
379
|
+
if str.empty? # is this a deletion?
|
380
|
+
"-#{range.to_range}"
|
381
|
+
elsif range.size == 0 # is this an insertion?
|
382
|
+
"+#{str.inspect}@#{range.begin_pos}"
|
383
|
+
else # it is a replacement
|
384
|
+
"^#{str.inspect}@#{range.to_range}"
|
385
|
+
end
|
386
|
+
end
|
387
|
+
parts << suffix if suffix
|
388
|
+
parts.join(', ')
|
389
|
+
end
|
390
|
+
|
301
391
|
ACTIONS = %i[accept warn raise].freeze
|
302
392
|
def check_policy_validity
|
303
393
|
invalid = @policy.values - ACTIONS
|
@@ -313,7 +403,7 @@ module Parser
|
|
313
403
|
|
314
404
|
def check_range_validity(range)
|
315
405
|
if range.begin_pos < 0 || range.end_pos > @source_buffer.source.size
|
316
|
-
raise IndexError, "The range #{range} is outside the bounds of the source"
|
406
|
+
raise IndexError, "The range #{range.to_range} is outside the bounds of the source"
|
317
407
|
end
|
318
408
|
range
|
319
409
|
end
|