parser 2.7.0.4 → 2.7.1.3
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/.gitignore +1 -0
- data/.travis.yml +18 -29
- data/CHANGELOG.md +53 -1
- data/README.md +6 -6
- data/Rakefile +2 -1
- data/doc/AST_FORMAT.md +54 -5
- data/lib/parser.rb +1 -0
- data/lib/parser/all.rb +1 -0
- data/lib/parser/ast/processor.rb +7 -0
- data/lib/parser/builders/default.rb +63 -14
- data/lib/parser/current.rb +13 -4
- data/lib/parser/diagnostic.rb +1 -1
- data/lib/parser/diagnostic/engine.rb +1 -2
- data/lib/parser/lexer.rl +7 -0
- data/lib/parser/messages.rb +15 -0
- data/lib/parser/meta.rb +2 -2
- data/lib/parser/ruby27.y +16 -5
- data/lib/parser/ruby28.y +3016 -0
- data/lib/parser/runner.rb +26 -2
- data/lib/parser/runner/ruby_rewrite.rb +2 -2
- data/lib/parser/source/buffer.rb +3 -1
- data/lib/parser/source/comment/associator.rb +14 -4
- data/lib/parser/source/map/endless_definition.rb +23 -0
- data/lib/parser/source/range.rb +16 -0
- data/lib/parser/source/tree_rewriter.rb +49 -12
- data/lib/parser/source/tree_rewriter/action.rb +96 -26
- data/lib/parser/tree_rewriter.rb +1 -2
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +2 -1
- data/test/helper.rb +25 -6
- data/test/parse_helper.rb +11 -17
- data/test/test_ast_processor.rb +32 -0
- data/test/test_base.rb +1 -1
- data/test/test_current.rb +2 -0
- data/test/test_diagnostic.rb +6 -7
- data/test/test_diagnostic_engine.rb +5 -8
- data/test/test_lexer.rb +17 -8
- data/test/test_meta.rb +12 -0
- data/test/test_parser.rb +260 -21
- data/test/test_runner_parse.rb +22 -1
- data/test/test_runner_rewrite.rb +1 -1
- data/test/test_source_buffer.rb +4 -1
- data/test/test_source_comment.rb +2 -2
- data/test/test_source_comment_associator.rb +47 -15
- data/test/test_source_map.rb +1 -2
- data/test/test_source_range.rb +29 -9
- data/test/test_source_rewriter.rb +4 -4
- data/test/test_source_rewriter_action.rb +2 -2
- data/test/test_source_tree_rewriter.rb +96 -6
- metadata +14 -7
data/test/test_runner_parse.rb
CHANGED
@@ -7,7 +7,7 @@ class TestRunnerParse < Minitest::Test
|
|
7
7
|
PATH_TO_RUBY_PARSE = File.expand_path('../bin/ruby-parse', __dir__).freeze
|
8
8
|
|
9
9
|
def assert_prints(argv, expected_output)
|
10
|
-
stdout,
|
10
|
+
stdout, _stderr, status = Open3.capture3(PATH_TO_RUBY_PARSE, *argv)
|
11
11
|
|
12
12
|
assert_equal 0, status.to_i
|
13
13
|
assert_includes(stdout, expected_output)
|
@@ -18,6 +18,27 @@ class TestRunnerParse < Minitest::Test
|
|
18
18
|
's(:int, 123)'
|
19
19
|
end
|
20
20
|
|
21
|
+
def test_emit_modern_ruby
|
22
|
+
assert_prints ['-e', '->{}'],
|
23
|
+
'(lambda)'
|
24
|
+
assert_prints ['-e', 'self[1] = 2'],
|
25
|
+
'indexasgn'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_emit_legacy
|
29
|
+
assert_prints ['--legacy', '-e', '->{}'],
|
30
|
+
'(send nil :lambda)'
|
31
|
+
assert_prints ['--legacy', '-e', 'self[1] = 2'],
|
32
|
+
':[]='
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_emit_legacy_lambda
|
36
|
+
assert_prints ['--legacy-lambda', '-e', '->{}'],
|
37
|
+
'(send nil :lambda)'
|
38
|
+
assert_prints ['--legacy-lambda', '-e', 'self[1] = 2'],
|
39
|
+
'indexasgn'
|
40
|
+
end
|
41
|
+
|
21
42
|
def test_emit_json
|
22
43
|
assert_prints ['--emit-json', '-e', '123'],
|
23
44
|
'["int",123]'
|
data/test/test_runner_rewrite.rb
CHANGED
@@ -21,7 +21,7 @@ class TestRunnerRewrite < Minitest::Test
|
|
21
21
|
expected_file = @fixtures_dir + output
|
22
22
|
|
23
23
|
FileUtils.cp(@fixtures_dir + input, sample_file_expanded)
|
24
|
-
stdout, stderr,
|
24
|
+
stdout, stderr, _exit_code = Dir.chdir @test_dir do
|
25
25
|
Open3.capture3 %Q{
|
26
26
|
#{Shellwords.escape(@ruby_rewrite.to_s)} #{args} \
|
27
27
|
#{Shellwords.escape(sample_file_expanded.to_s)}
|
data/test/test_source_buffer.rb
CHANGED
@@ -20,6 +20,9 @@ class TestSourceBuffer < Minitest::Test
|
|
20
20
|
|
21
21
|
buffer = Parser::Source::Buffer.new('(string)', 5)
|
22
22
|
assert_equal 5, buffer.first_line
|
23
|
+
|
24
|
+
buffer = Parser::Source::Buffer.new('(string)', source: '2+2')
|
25
|
+
assert_equal '2+2', buffer.source
|
23
26
|
end
|
24
27
|
|
25
28
|
def test_source_setter
|
@@ -45,7 +48,7 @@ class TestSourceBuffer < Minitest::Test
|
|
45
48
|
].join("\n")
|
46
49
|
end
|
47
50
|
|
48
|
-
assert_match
|
51
|
+
assert_match(/invalid byte sequence in UTF\-8/, error.message)
|
49
52
|
end
|
50
53
|
|
51
54
|
def test_read
|
data/test/test_source_comment.rb
CHANGED
@@ -4,8 +4,8 @@ require 'helper'
|
|
4
4
|
|
5
5
|
class TestSourceComment < Minitest::Test
|
6
6
|
def setup
|
7
|
-
@buf = Parser::Source::Buffer.new('(string)'
|
8
|
-
|
7
|
+
@buf = Parser::Source::Buffer.new('(string)',
|
8
|
+
source: "# foo\n=begin foo\nbar\n=end baz\n")
|
9
9
|
end
|
10
10
|
|
11
11
|
def range(s, e)
|
@@ -95,7 +95,7 @@ class Foo
|
|
95
95
|
end
|
96
96
|
END
|
97
97
|
|
98
|
-
|
98
|
+
_klass_node = ast
|
99
99
|
method_node = ast.children[2]
|
100
100
|
body = method_node.children[2]
|
101
101
|
f1_1_node = body.children[0]
|
@@ -175,7 +175,7 @@ class Foo
|
|
175
175
|
end
|
176
176
|
END
|
177
177
|
|
178
|
-
|
178
|
+
_klass_node = ast
|
179
179
|
method_node = ast.children[2]
|
180
180
|
body = method_node.children[2]
|
181
181
|
f1_1_node = body.children[0]
|
@@ -201,12 +201,12 @@ end
|
|
201
201
|
end
|
202
202
|
|
203
203
|
def test_associate_empty_tree
|
204
|
-
|
204
|
+
_ast, associations = associate("")
|
205
205
|
assert_equal 0, associations.size
|
206
206
|
end
|
207
207
|
|
208
208
|
def test_associate_shebang_only
|
209
|
-
|
209
|
+
_ast, associations = associate(<<-END)
|
210
210
|
#!ruby
|
211
211
|
class Foo
|
212
212
|
end
|
@@ -216,7 +216,7 @@ end
|
|
216
216
|
end
|
217
217
|
|
218
218
|
def test_associate_frozen_string_literal
|
219
|
-
|
219
|
+
_ast, associations = associate(<<-END)
|
220
220
|
# frozen_string_literal: true
|
221
221
|
class Foo
|
222
222
|
end
|
@@ -226,7 +226,7 @@ end
|
|
226
226
|
end
|
227
227
|
|
228
228
|
def test_associate_frozen_string_literal_dash_star_dash
|
229
|
-
|
229
|
+
_ast, associations = associate(<<-END)
|
230
230
|
# -*- frozen_string_literal: true -*-
|
231
231
|
class Foo
|
232
232
|
end
|
@@ -236,7 +236,7 @@ end
|
|
236
236
|
end
|
237
237
|
|
238
238
|
def test_associate_frozen_string_literal_no_space_after_colon
|
239
|
-
|
239
|
+
_ast, associations = associate(<<-END)
|
240
240
|
# frozen_string_literal:true
|
241
241
|
class Foo
|
242
242
|
end
|
@@ -246,7 +246,7 @@ end
|
|
246
246
|
end
|
247
247
|
|
248
248
|
def test_associate_warn_indent
|
249
|
-
|
249
|
+
_ast, associations = associate(<<-END)
|
250
250
|
# warn_indent: true
|
251
251
|
class Foo
|
252
252
|
end
|
@@ -256,7 +256,7 @@ end
|
|
256
256
|
end
|
257
257
|
|
258
258
|
def test_associate_warn_indent_dash_star_dash
|
259
|
-
|
259
|
+
_ast, associations = associate(<<-END)
|
260
260
|
# -*- warn_indent: true -*-
|
261
261
|
class Foo
|
262
262
|
end
|
@@ -266,7 +266,7 @@ end
|
|
266
266
|
end
|
267
267
|
|
268
268
|
def test_associate_warn_past_scope
|
269
|
-
|
269
|
+
_ast, associations = associate(<<-END)
|
270
270
|
# warn_past_scope: true
|
271
271
|
class Foo
|
272
272
|
end
|
@@ -276,7 +276,7 @@ end
|
|
276
276
|
end
|
277
277
|
|
278
278
|
def test_associate_warn_past_scope_dash_star_dash
|
279
|
-
|
279
|
+
_ast, associations = associate(<<-END)
|
280
280
|
# -*- warn_past_scope: true -*-
|
281
281
|
class Foo
|
282
282
|
end
|
@@ -286,7 +286,7 @@ end
|
|
286
286
|
end
|
287
287
|
|
288
288
|
def test_associate_multiple
|
289
|
-
|
289
|
+
_ast, associations = associate(<<-END)
|
290
290
|
# frozen_string_literal: true; warn_indent: true
|
291
291
|
class Foo
|
292
292
|
end
|
@@ -296,7 +296,7 @@ end
|
|
296
296
|
end
|
297
297
|
|
298
298
|
def test_associate_multiple_dash_star_dash
|
299
|
-
|
299
|
+
_ast, associations = associate(<<-END)
|
300
300
|
# -*- frozen_string_literal: true; warn_indent: true -*-
|
301
301
|
class Foo
|
302
302
|
end
|
@@ -306,7 +306,7 @@ end
|
|
306
306
|
end
|
307
307
|
|
308
308
|
def test_associate_no_comments
|
309
|
-
|
309
|
+
_ast, associations = associate(<<-END)
|
310
310
|
class Foo
|
311
311
|
end
|
312
312
|
END
|
@@ -315,7 +315,7 @@ end
|
|
315
315
|
end
|
316
316
|
|
317
317
|
def test_associate_comments_after_root_node
|
318
|
-
|
318
|
+
_ast, associations = associate(<<-END)
|
319
319
|
class Foo
|
320
320
|
end
|
321
321
|
# not associated
|
@@ -364,4 +364,36 @@ x
|
|
364
364
|
assert_equal ['# bar'],
|
365
365
|
associations[send_node].map(&:text)
|
366
366
|
end
|
367
|
+
|
368
|
+
def test_associate_conditional_parent_class
|
369
|
+
ast, associations = associate(<<-END)
|
370
|
+
class Foo
|
371
|
+
# bar
|
372
|
+
class Bar
|
373
|
+
end
|
374
|
+
end if some_condition
|
375
|
+
END
|
376
|
+
|
377
|
+
if_node = ast
|
378
|
+
_condition, foo_class = if_node.children
|
379
|
+
_foo, _sub_class, bar_class = foo_class.children
|
380
|
+
|
381
|
+
assert_equal 1, associations.size
|
382
|
+
assert_equal ['# bar'],
|
383
|
+
associations[bar_class].map(&:text)
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_children_in_source_order
|
387
|
+
obj = Parser::Source::Comment::Associator.new(nil, nil)
|
388
|
+
for_each_node do |node|
|
389
|
+
with_loc = node.children.select do |child|
|
390
|
+
child.is_a?(AST::Node) && child.loc && child.loc.expression
|
391
|
+
end
|
392
|
+
slow_sort = with_loc.sort_by.with_index do |child, index| # Index to ensure stable sort
|
393
|
+
[child.loc.expression.begin_pos, index]
|
394
|
+
end
|
395
|
+
optimized = obj.send(:children_in_source_order, node)
|
396
|
+
assert_equal slow_sort, optimized, "children_in_source_order incorrect for #{node}"
|
397
|
+
end
|
398
|
+
end
|
367
399
|
end
|
data/test/test_source_map.rb
CHANGED
@@ -7,8 +7,7 @@ class TestSourceMap < Minitest::Test
|
|
7
7
|
include ParseHelper
|
8
8
|
|
9
9
|
def test_to_hash
|
10
|
-
buf = Parser::Source::Buffer.new("<input>")
|
11
|
-
buf.source = "1"
|
10
|
+
buf = Parser::Source::Buffer.new("<input>", source: "1")
|
12
11
|
ast = parser_for_ruby_version('1.8').parse(buf)
|
13
12
|
assert_equal [:expression, :operator], ast.loc.to_hash.keys.sort_by(&:to_s)
|
14
13
|
end
|
data/test/test_source_range.rb
CHANGED
@@ -4,8 +4,8 @@ require 'helper'
|
|
4
4
|
|
5
5
|
class TestSourceRange < Minitest::Test
|
6
6
|
def setup
|
7
|
-
@buf = Parser::Source::Buffer.new('(string)'
|
8
|
-
|
7
|
+
@buf = Parser::Source::Buffer.new('(string)',
|
8
|
+
source: "foobar\nbaz")
|
9
9
|
@sr1_3 = Parser::Source::Range.new(@buf, 1, 3)
|
10
10
|
@sr2_2 = Parser::Source::Range.new(@buf, 2, 2)
|
11
11
|
@sr3_3 = Parser::Source::Range.new(@buf, 3, 3)
|
@@ -95,15 +95,15 @@ class TestSourceRange < Minitest::Test
|
|
95
95
|
|
96
96
|
def test_order
|
97
97
|
assert_equal 0, @sr1_3 <=> @sr1_3
|
98
|
-
assert_equal
|
99
|
-
assert_equal
|
100
|
-
assert_equal
|
98
|
+
assert_equal(-1, @sr1_3 <=> @sr5_8)
|
99
|
+
assert_equal(-1, @sr2_2 <=> @sr2_6)
|
100
|
+
assert_equal(+1, @sr2_6 <=> @sr2_2)
|
101
101
|
|
102
|
-
assert_equal
|
102
|
+
assert_equal(-1, @sr1_3 <=> @sr2_6)
|
103
103
|
|
104
|
-
assert_equal
|
105
|
-
assert_equal
|
106
|
-
assert_equal
|
104
|
+
assert_equal(+1, @sr2_2 <=> @sr1_3)
|
105
|
+
assert_equal(-1, @sr1_3 <=> @sr2_2)
|
106
|
+
assert_equal(-1, @sr5_7 <=> @sr5_8)
|
107
107
|
|
108
108
|
assert_nil @sr1_3 <=> Parser::Source::Range.new(@buf.dup, 1, 3)
|
109
109
|
assert_nil @sr1_3 <=> 4
|
@@ -155,6 +155,11 @@ class TestSourceRange < Minitest::Test
|
|
155
155
|
refute sr.is?('bar')
|
156
156
|
end
|
157
157
|
|
158
|
+
def test_to_range
|
159
|
+
sr = Parser::Source::Range.new(@buf, 10, 20)
|
160
|
+
assert_equal (10...20), sr.to_range
|
161
|
+
end
|
162
|
+
|
158
163
|
def test_to_s
|
159
164
|
sr = Parser::Source::Range.new(@buf, 8, 9)
|
160
165
|
assert_equal '(string):2:2', sr.to_s
|
@@ -169,4 +174,19 @@ class TestSourceRange < Minitest::Test
|
|
169
174
|
assert_equal 1, sr3.begin_pos
|
170
175
|
assert_equal 4, sr3.end_pos
|
171
176
|
end
|
177
|
+
|
178
|
+
def test_eql_and_hash
|
179
|
+
assert_equal false, @sr1_3.eql?(@sr3_3)
|
180
|
+
assert @sr1_3.hash != @sr3_3.hash
|
181
|
+
|
182
|
+
also_1_3 = @sr3_3.with(begin_pos: 1)
|
183
|
+
assert_equal true, @sr1_3.eql?(also_1_3)
|
184
|
+
assert_equal @sr1_3.hash, also_1_3.hash
|
185
|
+
|
186
|
+
buf2 = Parser::Source::Buffer.new('(string)',
|
187
|
+
source: "foobar\nbaz")
|
188
|
+
from_other_buf = Parser::Source::Range.new(buf2, 1, 3)
|
189
|
+
assert_equal false, @sr1_3.eql?(from_other_buf)
|
190
|
+
assert @sr1_3.hash != from_other_buf.hash
|
191
|
+
end
|
172
192
|
end
|
@@ -4,8 +4,8 @@ require 'helper'
|
|
4
4
|
|
5
5
|
class TestSourceRewriter < Minitest::Test
|
6
6
|
def setup
|
7
|
-
@buf = Parser::Source::Buffer.new('(rewriter)'
|
8
|
-
|
7
|
+
@buf = Parser::Source::Buffer.new('(rewriter)',
|
8
|
+
source: 'foo bar baz')
|
9
9
|
Parser::Source::Rewriter.warned_of_deprecation = true
|
10
10
|
@rewriter = Parser::Source::Rewriter.new(@buf)
|
11
11
|
end
|
@@ -522,7 +522,7 @@ class TestSourceRewriter < Minitest::Test
|
|
522
522
|
end
|
523
523
|
end
|
524
524
|
|
525
|
-
assert_match
|
525
|
+
assert_match(/nested/i, error.message)
|
526
526
|
end
|
527
527
|
|
528
528
|
def test_process_in_transaction_raises_error
|
@@ -532,7 +532,7 @@ class TestSourceRewriter < Minitest::Test
|
|
532
532
|
end
|
533
533
|
end
|
534
534
|
|
535
|
-
assert_match
|
535
|
+
assert_match(/transaction/, error.message)
|
536
536
|
end
|
537
537
|
|
538
538
|
def silence_diagnostics
|
@@ -4,8 +4,8 @@ require 'helper'
|
|
4
4
|
|
5
5
|
class TestSourceRewriterAction < Minitest::Test
|
6
6
|
def setup
|
7
|
-
@buf = Parser::Source::Buffer.new('(rewriter_action)'
|
8
|
-
|
7
|
+
@buf = Parser::Source::Buffer.new('(rewriter_action)',
|
8
|
+
source: 'foo bar baz')
|
9
9
|
end
|
10
10
|
|
11
11
|
def range(from, len)
|
@@ -4,12 +4,14 @@ require 'helper'
|
|
4
4
|
|
5
5
|
class TestSourceTreeRewriter < Minitest::Test
|
6
6
|
def setup
|
7
|
-
@buf = Parser::Source::Buffer.new('(rewriter)'
|
8
|
-
|
7
|
+
@buf = Parser::Source::Buffer.new('(rewriter)',
|
8
|
+
source: 'puts(:hello, :world)')
|
9
9
|
|
10
10
|
@hello = range(5, 6)
|
11
|
+
@ll = range(7, 2)
|
11
12
|
@comma_space = range(11,2)
|
12
13
|
@world = range(13,6)
|
14
|
+
@whole = range(0, @buf.source.length)
|
13
15
|
end
|
14
16
|
|
15
17
|
def range(from, len)
|
@@ -17,11 +19,11 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
17
19
|
end
|
18
20
|
|
19
21
|
# Returns either:
|
20
|
-
# -
|
22
|
+
# - yield rewriter
|
21
23
|
# - [diagnostic, ...] (Diagnostics)
|
22
24
|
# - Parser::ClobberingError
|
23
25
|
#
|
24
|
-
def
|
26
|
+
def build(actions, **policy)
|
25
27
|
diagnostics = []
|
26
28
|
diags = -> { diagnostics.flatten.map(&:strip).join("\n") }
|
27
29
|
rewriter = Parser::Source::TreeRewriter.new(@buf, **policy)
|
@@ -30,14 +32,23 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
30
32
|
rewriter.public_send(action, range, *args)
|
31
33
|
end
|
32
34
|
if diagnostics.empty?
|
33
|
-
rewriter
|
35
|
+
yield rewriter
|
34
36
|
else
|
35
37
|
diags.call
|
36
38
|
end
|
37
|
-
rescue ::Parser::ClobberingError =>
|
39
|
+
rescue ::Parser::ClobberingError => _e
|
38
40
|
[::Parser::ClobberingError, diags.call]
|
39
41
|
end
|
40
42
|
|
43
|
+
# Returns either:
|
44
|
+
# - String (Normal operation)
|
45
|
+
# - [diagnostic, ...] (Diagnostics)
|
46
|
+
# - Parser::ClobberingError
|
47
|
+
#
|
48
|
+
def apply(actions, **policy)
|
49
|
+
build(actions, **policy) { |rewriter| rewriter.process }
|
50
|
+
end
|
51
|
+
|
41
52
|
# Expects ordered actions to be grouped together
|
42
53
|
def check_actions(expected, grouped_actions, **policy)
|
43
54
|
grouped_actions.permutation do |sequence|
|
@@ -140,6 +151,16 @@ DIAGNOSTIC
|
|
140
151
|
[:wrap, @hello, '[', ']']]
|
141
152
|
end
|
142
153
|
|
154
|
+
def test_inserts_on_empty_ranges
|
155
|
+
assert_actions_result 'puts({x}:hello[y], :world)',
|
156
|
+
[:insert_before, @hello.begin, '{'],
|
157
|
+
[:replace, @hello.begin, 'x'],
|
158
|
+
[:insert_after, @hello.begin, '}'],
|
159
|
+
[:insert_before, @hello.end, '['],
|
160
|
+
[:replace, @hello.end, 'y'],
|
161
|
+
[:insert_after, @hello.end, ']']
|
162
|
+
end
|
163
|
+
|
143
164
|
def test_replace_same_range
|
144
165
|
assert_actions_result 'puts(:hey, :world)',
|
145
166
|
[[:replace, @hello, ':hi'],
|
@@ -170,4 +191,73 @@ DIAGNOSTIC
|
|
170
191
|
rewriter = Parser::Source::TreeRewriter.new(@buf)
|
171
192
|
assert_raises(IndexError) { rewriter.insert_before(range(0, 100), 'hola') }
|
172
193
|
end
|
194
|
+
|
195
|
+
def test_empty
|
196
|
+
rewriter = Parser::Source::TreeRewriter.new(@buf)
|
197
|
+
assert_equal true, rewriter.empty?
|
198
|
+
|
199
|
+
# This is a trivial wrap
|
200
|
+
rewriter.wrap(range(2,3), '', '')
|
201
|
+
assert_equal true, rewriter.empty?
|
202
|
+
|
203
|
+
# This is a trivial deletion
|
204
|
+
rewriter.remove(range(2,0))
|
205
|
+
assert_equal true, rewriter.empty?
|
206
|
+
|
207
|
+
rewriter.remove(range(2,3))
|
208
|
+
assert_equal false, rewriter.empty?
|
209
|
+
end
|
210
|
+
|
211
|
+
# splits array into two groups, yield all such possible pairs of groups
|
212
|
+
# each_split([1, 2, 3, 4]) yields [1, 2], [3, 4];
|
213
|
+
# then [1, 3], [2, 4]
|
214
|
+
# ...
|
215
|
+
# and finally [3, 4], [1, 2]
|
216
|
+
def each_split(array)
|
217
|
+
n = array.size
|
218
|
+
first_split_size = n.div(2)
|
219
|
+
splitting = (0...n).to_set
|
220
|
+
splitting.to_a.combination(first_split_size) do |indices|
|
221
|
+
yield array.values_at(*indices),
|
222
|
+
array.values_at(*(splitting - indices))
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Checks that `actions+extra` give the same result when
|
227
|
+
# made in order or from subgroups that are later merged.
|
228
|
+
# The `extra` actions are always added at the end of the second group.
|
229
|
+
#
|
230
|
+
def check_all_merge_possibilities(actions, extra, **policy)
|
231
|
+
expected = apply(actions + extra, **policy)
|
232
|
+
|
233
|
+
each_split(actions) do |actions_1, actions_2|
|
234
|
+
build(actions_1, **policy) do |rewriter_1|
|
235
|
+
build(actions_2 + extra, **policy) do |rewriter_2|
|
236
|
+
result = rewriter_1.merge(rewriter_2).process
|
237
|
+
assert_equal(expected, result,
|
238
|
+
"Group 1: #{actions_1.inspect}\n\n" +
|
239
|
+
"Group 2: #{(actions_2 + extra).inspect}"
|
240
|
+
)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_merge
|
247
|
+
check_all_merge_possibilities([
|
248
|
+
[:wrap, @whole, '<', '>'],
|
249
|
+
[:replace, @comma_space, ' => '],
|
250
|
+
[:wrap, @hello, '!', '!'],
|
251
|
+
# Following two wraps must have same value as they
|
252
|
+
# will be applied in different orders...
|
253
|
+
[:wrap, @hello.join(@world), '{', '}'],
|
254
|
+
[:wrap, @hello.join(@world), '{', '}'],
|
255
|
+
[:remove, @ll],
|
256
|
+
[:replace, @world, ':everybody'],
|
257
|
+
[:wrap, @world, '[', ']']
|
258
|
+
],
|
259
|
+
[ # ... but this one is always going to be applied last (extra)
|
260
|
+
[:wrap, @hello.join(@world), '@', '@'],
|
261
|
+
])
|
262
|
+
end
|
173
263
|
end
|