parser 2.7.1.0 → 2.7.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CHANGELOG.md +8 -1
- data/lib/parser/source/range.rb +9 -0
- data/lib/parser/source/tree_rewriter.rb +42 -2
- data/lib/parser/source/tree_rewriter/action.rb +22 -6
- data/lib/parser/version.rb +1 -1
- data/test/test_source_range.rb +15 -0
- data/test/test_source_tree_rewriter.rb +83 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb7c9797808fcd8920ef34ccce6dae7f416d0fe3084a863fa3fec7fa30b086fc
|
4
|
+
data.tar.gz: 828a937688a35e277c7d435197ca2d023458de366a8e94fbaa67bdd6d3a04145
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 679bc103f3d8b2663a1ec2de6c3acac41c90c1d738b40bf4b5ed945b8e3f51e22359caa0789e55009f3bd2c45ac832d00182db9ba9be1f43b18e6fe0e0508278
|
7
|
+
data.tar.gz: b4f4d29766e23ca406644fb578e2134f28a9983d765d4aa45117dcb5a9ee012f27c2b8b65eccf11b7e3818b1543a62cc0483aa510a51352bc2260ad0fd650acd
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
-
Not released (2020-04-
|
4
|
+
Not released (2020-04-15)
|
5
5
|
-------------------------
|
6
6
|
|
7
|
+
Features implemented:
|
8
|
+
* Add Source::Range#eql? and hash (#675) (Marc-André Lafortune)
|
9
|
+
* Source::TreeRewriter: Add #merge, #merge! and #empty? (#674) (Marc-André Lafortune)
|
10
|
+
|
11
|
+
v2.7.1.0 (2020-04-03)
|
12
|
+
---------------------
|
13
|
+
|
7
14
|
API modifications:
|
8
15
|
* Bump ruby versions to 2.4.10, 2.5.8, 2.6.6, 2.7.1. (#665) (Ilya Bylich)
|
9
16
|
|
data/lib/parser/source/range.rb
CHANGED
@@ -298,6 +298,15 @@ module Parser
|
|
298
298
|
(@end_pos <=> other.end_pos)
|
299
299
|
end
|
300
300
|
|
301
|
+
alias_method :eql?, :==
|
302
|
+
|
303
|
+
##
|
304
|
+
# Support for Ranges be used in as Hash indices and in Sets.
|
305
|
+
#
|
306
|
+
def hash
|
307
|
+
[@source_buffer, @begin_pos, @end_pos].hash
|
308
|
+
end
|
309
|
+
|
301
310
|
##
|
302
311
|
# @return [String] a human-readable representation of this range.
|
303
312
|
#
|
@@ -117,6 +117,43 @@ module Parser
|
|
117
117
|
@action_root = TreeRewriter::Action.new(all_encompassing_range, @enforcer)
|
118
118
|
end
|
119
119
|
|
120
|
+
##
|
121
|
+
# Returns true iff no (non trivial) update has been recorded
|
122
|
+
#
|
123
|
+
# @return [Boolean]
|
124
|
+
#
|
125
|
+
def empty?
|
126
|
+
@action_root.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Merges the updates of argument with the receiver.
|
131
|
+
# Policies of the receiver are used.
|
132
|
+
#
|
133
|
+
# @param [Rewriter] with
|
134
|
+
# @return [Rewriter] self
|
135
|
+
# @raise [ClobberingError] when clobbering is detected
|
136
|
+
#
|
137
|
+
def merge!(with)
|
138
|
+
raise 'TreeRewriter are not for the same source_buffer' unless
|
139
|
+
source_buffer == with.source_buffer
|
140
|
+
|
141
|
+
@action_root = @action_root.combine(with.action_root)
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Returns a new rewriter that consists of the updates of the received
|
147
|
+
# and the given argument. Policies of the receiver are used.
|
148
|
+
#
|
149
|
+
# @param [Rewriter] with
|
150
|
+
# @return [Rewriter] merge of receiver and argument
|
151
|
+
# @raise [ClobberingError] when clobbering is detected
|
152
|
+
#
|
153
|
+
def merge(with)
|
154
|
+
dup.merge!(with)
|
155
|
+
end
|
156
|
+
|
120
157
|
##
|
121
158
|
# Replaces the code of the source range `range` with `content`.
|
122
159
|
#
|
@@ -203,10 +240,9 @@ module Parser
|
|
203
240
|
##
|
204
241
|
# Provides a protected block where a sequence of multiple rewrite actions
|
205
242
|
# are handled atomically. If any of the actions failed by clobbering,
|
206
|
-
# all the actions are rolled back.
|
243
|
+
# all the actions are rolled back. Transactions can be nested.
|
207
244
|
#
|
208
245
|
# @raise [RuntimeError] when no block is passed
|
209
|
-
# @raise [RuntimeError] when already in a transaction
|
210
246
|
#
|
211
247
|
def transaction
|
212
248
|
unless block_given?
|
@@ -256,6 +292,10 @@ module Parser
|
|
256
292
|
|
257
293
|
extend Deprecation
|
258
294
|
|
295
|
+
protected
|
296
|
+
|
297
|
+
attr_reader :action_root
|
298
|
+
|
259
299
|
private
|
260
300
|
|
261
301
|
ACTIONS = %i[accept warn raise].freeze
|
@@ -25,12 +25,18 @@ module Parser
|
|
25
25
|
freeze
|
26
26
|
end
|
27
27
|
|
28
|
-
# Assumes action.children.empty?
|
29
28
|
def combine(action)
|
30
|
-
return self
|
29
|
+
return self if action.empty? # Ignore empty action
|
31
30
|
do_combine(action)
|
32
31
|
end
|
33
32
|
|
33
|
+
def empty?
|
34
|
+
@insert_before.empty? &&
|
35
|
+
@insert_after.empty? &&
|
36
|
+
@children.empty? &&
|
37
|
+
(@replacement == nil || (@replacement.empty? && @range.empty?))
|
38
|
+
end
|
39
|
+
|
34
40
|
def ordered_replacements
|
35
41
|
reps = []
|
36
42
|
reps << [@range.begin, @insert_before] unless @insert_before.empty?
|
@@ -46,9 +52,11 @@ module Parser
|
|
46
52
|
|
47
53
|
protected
|
48
54
|
|
49
|
-
|
55
|
+
attr_reader :children
|
56
|
+
|
57
|
+
def with(range: @range, enforcer: @enforcer, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
|
50
58
|
children = swallow(children) if replacement
|
51
|
-
self.class.new(range,
|
59
|
+
self.class.new(range, enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
|
52
60
|
end
|
53
61
|
|
54
62
|
# Assumes range.contains?(action.range) && action.children.empty?
|
@@ -69,7 +77,8 @@ module Parser
|
|
69
77
|
extra_sibbling = if family[:parent] # action should be a descendant of one of the children
|
70
78
|
family[:parent][0].do_combine(action)
|
71
79
|
elsif family[:child] # or it should become the parent of some of the children,
|
72
|
-
action.with(children: family[:child])
|
80
|
+
action.with(children: family[:child], enforcer: @enforcer)
|
81
|
+
.combine_children(action.children)
|
73
82
|
else # or else it should become an additional child
|
74
83
|
action
|
75
84
|
end
|
@@ -77,6 +86,13 @@ module Parser
|
|
77
86
|
end
|
78
87
|
end
|
79
88
|
|
89
|
+
# Assumes more_children all contained within @range
|
90
|
+
def combine_children(more_children)
|
91
|
+
more_children.inject(self) do |parent, new_child|
|
92
|
+
parent.place_in_hierarchy(new_child)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
80
96
|
def fuse_deletions(action, fusible, other_sibblings)
|
81
97
|
without_fusible = with(children: other_sibblings)
|
82
98
|
fused_range = [action, *fusible].map(&:range).inject(:join)
|
@@ -109,7 +125,7 @@ module Parser
|
|
109
125
|
insert_before: "#{action.insert_before}#{insert_before}",
|
110
126
|
replacement: action.replacement || @replacement,
|
111
127
|
insert_after: "#{insert_after}#{action.insert_after}",
|
112
|
-
)
|
128
|
+
).combine_children(action.children)
|
113
129
|
end
|
114
130
|
|
115
131
|
def call_enforcer_for_merge(action)
|
data/lib/parser/version.rb
CHANGED
data/test/test_source_range.rb
CHANGED
@@ -169,4 +169,19 @@ class TestSourceRange < Minitest::Test
|
|
169
169
|
assert_equal 1, sr3.begin_pos
|
170
170
|
assert_equal 4, sr3.end_pos
|
171
171
|
end
|
172
|
+
|
173
|
+
def test_eql_and_hash
|
174
|
+
assert_equal false, @sr1_3.eql?(@sr3_3)
|
175
|
+
assert @sr1_3.hash != @sr3_3.hash
|
176
|
+
|
177
|
+
also_1_3 = @sr3_3.with(begin_pos: 1)
|
178
|
+
assert_equal true, @sr1_3.eql?(also_1_3)
|
179
|
+
assert_equal @sr1_3.hash, also_1_3.hash
|
180
|
+
|
181
|
+
buf2 = Parser::Source::Buffer.new('(string)')
|
182
|
+
buf2.source = "foobar\nbaz"
|
183
|
+
from_other_buf = Parser::Source::Range.new(buf2, 1, 3)
|
184
|
+
assert_equal false, @sr1_3.eql?(from_other_buf)
|
185
|
+
assert @sr1_3.hash != from_other_buf.hash
|
186
|
+
end
|
172
187
|
end
|
@@ -8,8 +8,10 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
8
8
|
@buf.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,7 +32,7 @@ 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
|
@@ -38,6 +40,15 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
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|
|
@@ -170,4 +181,73 @@ DIAGNOSTIC
|
|
170
181
|
rewriter = Parser::Source::TreeRewriter.new(@buf)
|
171
182
|
assert_raises(IndexError) { rewriter.insert_before(range(0, 100), 'hola') }
|
172
183
|
end
|
184
|
+
|
185
|
+
def test_empty
|
186
|
+
rewriter = Parser::Source::TreeRewriter.new(@buf)
|
187
|
+
assert_equal true, rewriter.empty?
|
188
|
+
|
189
|
+
# This is a trivial wrap
|
190
|
+
rewriter.wrap(range(2,3), '', '')
|
191
|
+
assert_equal true, rewriter.empty?
|
192
|
+
|
193
|
+
# This is a trivial deletion
|
194
|
+
rewriter.remove(range(2,0))
|
195
|
+
assert_equal true, rewriter.empty?
|
196
|
+
|
197
|
+
rewriter.remove(range(2,3))
|
198
|
+
assert_equal false, rewriter.empty?
|
199
|
+
end
|
200
|
+
|
201
|
+
# splits array into two groups, yield all such possible pairs of groups
|
202
|
+
# each_split([1, 2, 3, 4]) yields [1, 2], [3, 4];
|
203
|
+
# then [1, 3], [2, 4]
|
204
|
+
# ...
|
205
|
+
# and finally [3, 4], [1, 2]
|
206
|
+
def each_split(array)
|
207
|
+
n = array.size
|
208
|
+
first_split_size = n.div(2)
|
209
|
+
splitting = (0...n).to_set
|
210
|
+
splitting.to_a.combination(first_split_size) do |indices|
|
211
|
+
yield array.values_at(*indices),
|
212
|
+
array.values_at(*(splitting - indices))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Checks that `actions+extra` give the same result when
|
217
|
+
# made in order or from subgroups that are later merged.
|
218
|
+
# The `extra` actions are always added at the end of the second group.
|
219
|
+
#
|
220
|
+
def check_all_merge_possibilities(actions, extra, **policy)
|
221
|
+
expected = apply(actions + extra, **policy)
|
222
|
+
|
223
|
+
each_split(actions) do |actions_1, actions_2|
|
224
|
+
build(actions_1, **policy) do |rewriter_1|
|
225
|
+
build(actions_2 + extra, **policy) do |rewriter_2|
|
226
|
+
result = rewriter_1.merge(rewriter_2).process
|
227
|
+
assert_equal(expected, result,
|
228
|
+
"Group 1: #{actions_1.inspect}\n\n" +
|
229
|
+
"Group 2: #{(actions_2 + extra).inspect}"
|
230
|
+
)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_merge
|
237
|
+
check_all_merge_possibilities([
|
238
|
+
[:wrap, @whole, '<', '>'],
|
239
|
+
[:replace, @comma_space, ' => '],
|
240
|
+
[:wrap, @hello, '!', '!'],
|
241
|
+
# Following two wraps must have same value as they
|
242
|
+
# will be applied in different orders...
|
243
|
+
[:wrap, @hello.join(@world), '{', '}'],
|
244
|
+
[:wrap, @hello.join(@world), '{', '}'],
|
245
|
+
[:remove, @ll],
|
246
|
+
[:replace, @world, ':everybody'],
|
247
|
+
[:wrap, @world, '[', ']']
|
248
|
+
],
|
249
|
+
[ # ... but this one is always going to be applied last (extra)
|
250
|
+
[:wrap, @hello.join(@world), '@', '@'],
|
251
|
+
])
|
252
|
+
end
|
173
253
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.1.
|
4
|
+
version: 2.7.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- whitequark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ast
|
@@ -297,9 +297,9 @@ licenses:
|
|
297
297
|
- MIT
|
298
298
|
metadata:
|
299
299
|
bug_tracker_uri: https://github.com/whitequark/parser/issues
|
300
|
-
changelog_uri: https://github.com/whitequark/parser/blob/v2.7.1.
|
301
|
-
documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.
|
302
|
-
source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.
|
300
|
+
changelog_uri: https://github.com/whitequark/parser/blob/v2.7.1.1/CHANGELOG.md
|
301
|
+
documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.1
|
302
|
+
source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.1
|
303
303
|
post_install_message:
|
304
304
|
rdoc_options: []
|
305
305
|
require_paths:
|