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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46184af7a1e0d843e62e720cd7795bb196972603e588eed69f47791b3c799288
4
- data.tar.gz: 1a9d809fc7443fb09deb3a36ea19ad418ff56119040b14a292c9ed8d112d1f29
3
+ metadata.gz: fb7c9797808fcd8920ef34ccce6dae7f416d0fe3084a863fa3fec7fa30b086fc
4
+ data.tar.gz: 828a937688a35e277c7d435197ca2d023458de366a8e94fbaa67bdd6d3a04145
5
5
  SHA512:
6
- metadata.gz: 9a91995ac131a4048976358a7bce1bd09b09893bb0b13fa39f2128c47fb0b1179f830a4adbb766df768bfbd2f89a1c5a5ee437dc1433ccaf5ad2307ff7c09d77
7
- data.tar.gz: 4fe835bc9fe1ff030d57e9a4d9a4657a0d244db23bac3c117e8f78bfc51d9dc330f51257e04c8ba5ef737d3feab11152db2e1ecde1e8b136d4fcc4f253ed549d
6
+ metadata.gz: 679bc103f3d8b2663a1ec2de6c3acac41c90c1d738b40bf4b5ed945b8e3f51e22359caa0789e55009f3bd2c45ac832d00182db9ba9be1f43b18e6fe0e0508278
7
+ data.tar.gz: b4f4d29766e23ca406644fb578e2134f28a9983d765d4aa45117dcb5a9ee012f27c2b8b65eccf11b7e3818b1543a62cc0483aa510a51352bc2260ad0fd650acd
@@ -2,8 +2,8 @@ dist: trusty
2
2
  language: ruby
3
3
  matrix:
4
4
  include:
5
- - name: 2.4.9 / Parser tests
6
- rvm: 2.4.9
5
+ - name: 2.4.10 / Parser tests
6
+ rvm: 2.4.10
7
7
  script: bundle exec rake test_cov
8
8
  - name: 2.5.8 / Parser tests
9
9
  rvm: 2.5.8
@@ -1,9 +1,16 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- Not released (2020-04-03)
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
 
@@ -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 unless action.insertion? || action.replacement # Ignore empty action
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
- def with(range: @range, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
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, @enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser
4
- VERSION = '2.7.1.0'
4
+ VERSION = '2.7.1.1'
5
5
  end
@@ -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
- # - String (Normal operation)
22
+ # - yield rewriter
21
23
  # - [diagnostic, ...] (Diagnostics)
22
24
  # - Parser::ClobberingError
23
25
  #
24
- def apply(actions, **policy)
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.process
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.0
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-03 00:00:00.000000000 Z
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.0/CHANGELOG.md
301
- documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.0
302
- source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.0
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: