parser 2.7.1.0 → 2.7.1.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/.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:
|