ruby-next 0.3.0 → 0.4.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -1
  3. data/README.md +11 -7
  4. metadata +3 -48
  5. data/bin/parse +0 -19
  6. data/bin/ruby-next +0 -16
  7. data/bin/transform +0 -21
  8. data/lib/ruby-next.rb +0 -37
  9. data/lib/ruby-next/cli.rb +0 -94
  10. data/lib/ruby-next/commands/base.rb +0 -42
  11. data/lib/ruby-next/commands/core_ext.rb +0 -166
  12. data/lib/ruby-next/commands/nextify.rb +0 -133
  13. data/lib/ruby-next/core.rb +0 -182
  14. data/lib/ruby-next/core/array/deconstruct.rb +0 -21
  15. data/lib/ruby-next/core/array/difference_union_intersection.rb +0 -25
  16. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +0 -17
  17. data/lib/ruby-next/core/enumerable/filter.rb +0 -25
  18. data/lib/ruby-next/core/enumerable/filter_map.rb +0 -38
  19. data/lib/ruby-next/core/enumerable/tally.rb +0 -14
  20. data/lib/ruby-next/core/enumerator/produce.rb +0 -20
  21. data/lib/ruby-next/core/hash/deconstruct_keys.rb +0 -21
  22. data/lib/ruby-next/core/hash/merge.rb +0 -14
  23. data/lib/ruby-next/core/kernel/then.rb +0 -10
  24. data/lib/ruby-next/core/proc/compose.rb +0 -19
  25. data/lib/ruby-next/core/runtime.rb +0 -10
  26. data/lib/ruby-next/core/string/split.rb +0 -11
  27. data/lib/ruby-next/core/struct/deconstruct.rb +0 -7
  28. data/lib/ruby-next/core/struct/deconstruct_keys.rb +0 -34
  29. data/lib/ruby-next/core/time/ceil.rb +0 -10
  30. data/lib/ruby-next/core/time/floor.rb +0 -9
  31. data/lib/ruby-next/core/unboundmethod/bind_call.rb +0 -9
  32. data/lib/ruby-next/core_ext.rb +0 -18
  33. data/lib/ruby-next/language.rb +0 -119
  34. data/lib/ruby-next/language/bootsnap.rb +0 -26
  35. data/lib/ruby-next/language/eval.rb +0 -64
  36. data/lib/ruby-next/language/parser.rb +0 -28
  37. data/lib/ruby-next/language/rewriters/args_forward.rb +0 -57
  38. data/lib/ruby-next/language/rewriters/base.rb +0 -105
  39. data/lib/ruby-next/language/rewriters/endless_range.rb +0 -60
  40. data/lib/ruby-next/language/rewriters/method_reference.rb +0 -33
  41. data/lib/ruby-next/language/rewriters/numbered_params.rb +0 -41
  42. data/lib/ruby-next/language/rewriters/pattern_matching.rb +0 -541
  43. data/lib/ruby-next/language/runtime.rb +0 -95
  44. data/lib/ruby-next/language/setup.rb +0 -43
  45. data/lib/ruby-next/language/unparser.rb +0 -8
  46. data/lib/ruby-next/utils.rb +0 -36
  47. data/lib/ruby-next/version.rb +0 -5
  48. data/lib/uby-next.rb +0 -68
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "parser/ruby27"
4
-
5
- module RubyNext
6
- module Language
7
- class Builder < ::Parser::Builders::Default
8
- modernize
9
- end
10
-
11
- class << self
12
- def parser
13
- ::Parser::Ruby27.new(Builder.new).tap do |prs|
14
- prs.diagnostics.tap do |diagnostics|
15
- diagnostics.all_errors_are_fatal = true
16
- end
17
- end
18
- end
19
-
20
- def parse(source, file = "(string)")
21
- buffer = ::Parser::Source::Buffer.new(file).tap do |buffer|
22
- buffer.source = source
23
- end
24
- parser.parse(buffer)
25
- end
26
- end
27
- end
28
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyNext
4
- module Language
5
- module Rewriters
6
- class ArgsForward < Base
7
- SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(...); end"
8
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
9
-
10
- REST = :__rest__
11
- BLOCK = :__block__
12
-
13
- def on_forward_args(node)
14
- context.track! self
15
-
16
- node.updated(
17
- :args,
18
- [
19
- s(:restarg, REST),
20
- s(:blockarg, BLOCK)
21
- ]
22
- )
23
- end
24
-
25
- def on_send(node)
26
- return unless node.children[2]&.type == :forwarded_args
27
-
28
- node.updated(
29
- nil,
30
- [
31
- *node.children[0..1],
32
- *forwarded_args
33
- ]
34
- )
35
- end
36
-
37
- def on_super(node)
38
- return unless node.children[0]&.type == :forwarded_args
39
-
40
- node.updated(
41
- nil,
42
- forwarded_args
43
- )
44
- end
45
-
46
- private
47
-
48
- def forwarded_args
49
- [
50
- s(:splat, s(:lvar, REST)),
51
- s(:block_pass, s(:lvar, BLOCK))
52
- ]
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyNext
4
- module Language
5
- module Rewriters
6
- using RubyNext
7
-
8
- CUSTOM_PARSER_REQUIRED = <<~MSG
9
- The %s feature is not a part of the latest stable Ruby release
10
- and is not supported by your Parser gem version.
11
-
12
- Use RubyNext's parser to use it: https://github.com/ruby-next/parser
13
-
14
- MSG
15
-
16
- class Base < ::Parser::TreeRewriter
17
- class LocalsTracker
18
- attr_reader :stacks
19
-
20
- def initialize
21
- @stacks = []
22
- end
23
-
24
- def with(**locals)
25
- stacks << locals
26
- yield.tap { stacks.pop }
27
- end
28
-
29
- def [](name, suffix = nil)
30
- fetch(name).then do |name|
31
- next name unless suffix
32
- :"#{name}#{suffix}__"
33
- end
34
- end
35
-
36
- def key?(name)
37
- !!fetch(name) { false }
38
- end
39
-
40
- def fetch(name)
41
- ind = -1
42
-
43
- loop do
44
- break stacks[ind][name] if stacks[ind].key?(name)
45
- ind -= 1
46
- break if stacks[ind].nil?
47
- end.then do |name|
48
- next name unless name.nil?
49
-
50
- return yield if block_given?
51
- raise ArgumentError, "Local var not found in scope: #{name}"
52
- end
53
- end
54
- end
55
-
56
- class << self
57
- # Returns true if the syntax is supported
58
- # by the current Ruby (performs syntax check, not version check)
59
- def unsupported_syntax?
60
- save_verbose, $VERBOSE = $VERBOSE, nil
61
- eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
62
- Kernel.send eval_mid, self::SYNTAX_PROBE
63
- false
64
- rescue SyntaxError, NameError
65
- true
66
- ensure
67
- $VERBOSE = save_verbose
68
- end
69
-
70
- # Returns true if the syntax is supported
71
- # by the specified version
72
- def unsupported_version?(version)
73
- self::MIN_SUPPORTED_VERSION > version
74
- end
75
-
76
- private
77
-
78
- def transform(source)
79
- Language.transform(source, rewriters: [self], using: false)
80
- end
81
-
82
- def warn_custom_parser_required_for(feature)
83
- warn(CUSTOM_PARSER_REQUIRED % feature)
84
- end
85
- end
86
-
87
- attr_reader :locals
88
-
89
- def initialize(context)
90
- @context = context
91
- @locals = LocalsTracker.new
92
- super()
93
- end
94
-
95
- def s(type, *children)
96
- ::Parser::AST::Node.new(type, children)
97
- end
98
-
99
- private
100
-
101
- attr_reader :context
102
- end
103
- end
104
- end
105
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyNext
4
- module Language
5
- module Rewriters
6
- class EndlessRange < Base
7
- SYNTAX_PROBE = "[0, 1][1..]"
8
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.6.0")
9
-
10
- def on_index(node)
11
- @current_index = node
12
- new_index = process(node.children.last)
13
- return unless new_index != node.children.last
14
-
15
- node.updated(
16
- nil,
17
- [
18
- node.children.first,
19
- new_index
20
- ]
21
- )
22
- end
23
-
24
- def on_erange(node)
25
- return unless node.children.last.nil?
26
-
27
- context.track! self
28
-
29
- new_end =
30
- if index_arg?(node)
31
- s(:int, -1)
32
- else
33
- s(:const,
34
- s(:const,
35
- s(:cbase), :Float),
36
- :INFINITY)
37
- end
38
-
39
- node.updated(
40
- :irange,
41
- [
42
- node.children.first,
43
- new_end
44
- ]
45
- )
46
- end
47
-
48
- alias_method :on_irange, :on_erange
49
-
50
- private
51
-
52
- attr_reader :current_index
53
-
54
- def index_arg?(node)
55
- current_index&.children&.include?(node)
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyNext
4
- module Language
5
- module Rewriters
6
- class MethodReference < Base
7
- SYNTAX_PROBE = "Language.:transform"
8
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
9
-
10
- def on_meth_ref(node)
11
- context.track! self
12
-
13
- receiver, mid = *node.children
14
-
15
- node.updated(
16
- :send,
17
- [
18
- receiver,
19
- :method,
20
- s(:sym, mid)
21
- ]
22
- )
23
- end
24
-
25
- begin
26
- transform(SYNTAX_PROBE)
27
- rescue ::Parser::SyntaxError
28
- warn_custom_parser_required_for("method reference")
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyNext
4
- module Language
5
- module Rewriters
6
- class NumberedParams < Base
7
- using RubyNext
8
-
9
- SYNTAX_PROBE = "proc { _1 }.call(1)"
10
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
11
-
12
- def on_numblock(node)
13
- context.track! self
14
-
15
- proc_or_lambda, num, *rest = *node.children
16
-
17
- node.updated(
18
- :block,
19
- [
20
- proc_or_lambda,
21
- proc_args(num),
22
- *rest
23
- ]
24
- )
25
- end
26
-
27
- private
28
-
29
- def proc_args(n)
30
- return s(:args, s(:procarg0, s(:arg, :_1))) if n == 1
31
-
32
- (1..n).map do |numero|
33
- s(:arg, :"_#{numero}")
34
- end.then do |args|
35
- s(:args, *args)
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,541 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyNext
4
- module Language
5
- module Rewriters
6
- using RubyNext
7
-
8
- using(Module.new do
9
- refine ::Parser::AST::Node do
10
- def to_ast_node
11
- self
12
- end
13
- end
14
-
15
- refine String do
16
- def to_ast_node
17
- ::Parser::AST::Node.new(:str, [self])
18
- end
19
- end
20
-
21
- refine Symbol do
22
- def to_ast_node
23
- ::Parser::AST::Node.new(:sym, [self])
24
- end
25
- end
26
-
27
- refine Integer do
28
- def to_ast_node
29
- ::Parser::AST::Node.new(:int, [self])
30
- end
31
- end
32
- end)
33
-
34
- class PatternMatching < Base
35
- SYNTAX_PROBE = "case 0; in 0; true; else; 1; end"
36
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
37
-
38
- MATCHEE = :__m__
39
- MATCHEE_ARR = :__m_arr__
40
- MATCHEE_HASH = :__m_hash__
41
-
42
- def on_case_match(node)
43
- context.track! self
44
-
45
- @deconstructed = []
46
-
47
- matchee_ast =
48
- s(:lvasgn, MATCHEE, node.children[0])
49
-
50
- ifs_ast = locals.with(
51
- matchee: MATCHEE,
52
- arr: MATCHEE_ARR,
53
- hash: MATCHEE_HASH
54
- ) do
55
- build_if_clause(node.children[1], node.children[2..-1])
56
- end
57
-
58
- node.updated(
59
- :begin,
60
- [
61
- matchee_ast, ifs_ast
62
- ]
63
- )
64
- end
65
-
66
- def on_in_match(node)
67
- context.track! self
68
-
69
- matchee =
70
- s(:lvasgn, MATCHEE, node.children[0])
71
-
72
- pattern =
73
- locals.with(
74
- matchee: MATCHEE,
75
- arr: MATCHEE_ARR,
76
- hash: MATCHEE_HASH
77
- ) do
78
- send(
79
- :"#{node.children[1].type}_clause",
80
- node.children[1]
81
- ).then do |node|
82
- s(:or,
83
- node,
84
- no_matching_pattern)
85
- end
86
- end
87
-
88
- node.updated(
89
- :and,
90
- [
91
- matchee,
92
- pattern
93
- ]
94
- )
95
- end
96
-
97
- private
98
-
99
- def build_if_clause(node, rest)
100
- if node&.type == :in_pattern
101
- build_in_pattern(node, rest)
102
- else
103
- raise "Unexpected else in the middle of case ... in" if rest && rest.size > 0
104
- # else clause must be present
105
- node || no_matching_pattern
106
- end
107
- end
108
-
109
- def build_in_pattern(clause, rest)
110
- [
111
- with_guard(
112
- send(
113
- :"#{clause.children[0].type}_clause",
114
- clause.children[0]
115
- ),
116
- clause.children[1] # guard
117
- ),
118
- clause.children[2] || s(:nil) # expression
119
- ].then do |children|
120
- if rest && rest.size > 0
121
- children << build_if_clause(rest.first, rest[1..-1])
122
- end
123
-
124
- s(:if, *children)
125
- end
126
- end
127
-
128
- def const_pattern_clause(node)
129
- const, pattern = *node.children
130
-
131
- case_eq_clause(const).then do |node|
132
- next node if pattern.nil?
133
-
134
- s(:and,
135
- node,
136
- send(:"#{pattern.type}_clause", pattern))
137
- end
138
- end
139
-
140
- def match_alt_clause(node)
141
- children = node.children.map do |child|
142
- send :"#{child.type}_clause", child
143
- end
144
- s(:or, *children)
145
- end
146
-
147
- def match_as_clause(node, right = s(:lvar, locals[:matchee]))
148
- s(:and,
149
- case_eq_clause(node.children[0]),
150
- match_var_clause(node.children[1], right))
151
- end
152
-
153
- def match_var_clause(node, left = s(:lvar, locals[:matchee]))
154
- s(:or,
155
- s(:lvasgn, node.children[0], left),
156
- s(:true)) # rubocop:disable Lint/BooleanSymbol
157
- end
158
-
159
- def pin_clause(node)
160
- case_eq_clause node.children[0]
161
- end
162
-
163
- def case_eq_clause(node, right = s(:lvar, locals[:matchee]))
164
- s(:send,
165
- node, :===, right)
166
- end
167
-
168
- #=========== ARRAY PATTERN (START) ===============
169
-
170
- def array_pattern_clause(node, matchee = s(:lvar, locals[:matchee]))
171
- deconstruct_node(matchee).then do |dnode|
172
- right =
173
- if node.children.empty?
174
- case_eq_clause(s(:array), s(:lvar, locals[:arr]))
175
- else
176
- array_element(0, *node.children)
177
- end
178
-
179
- # already deconsrtructed
180
- next right if dnode.nil?
181
-
182
- # if there is no rest or tail, match the size first
183
- unless node.type == :array_pattern_with_tail || node.children.any? { |n| n.type == :match_rest }
184
- right =
185
- s(:and,
186
- s(:send,
187
- node.children.size.to_ast_node,
188
- :==,
189
- s(:send, s(:lvar, locals[:arr]), :size)),
190
- right)
191
- end
192
-
193
- s(:and,
194
- dnode,
195
- right)
196
- end.then do |right|
197
- s(:and,
198
- respond_to_check(matchee, :deconstruct),
199
- right)
200
- end
201
- end
202
-
203
- alias array_pattern_with_tail_clause array_pattern_clause
204
-
205
- def deconstruct_node(matchee)
206
- # only deconstruct once per case
207
- return if deconstructed&.include?(locals[:arr])
208
-
209
- context.use_ruby_next!
210
-
211
- right = s(:send, matchee, :deconstruct)
212
-
213
- deconstructed << locals[:arr] if deconstructed
214
-
215
- s(:and,
216
- s(:or,
217
- s(:lvasgn, locals[:arr], right),
218
- s(:true)), # rubocop:disable Lint/BooleanSymbol
219
- s(:or,
220
- case_eq_clause(s(:const, nil, :Array), s(:lvar, locals[:arr])),
221
- raise_error(:TypeError, "#deconstruct must return Array")))
222
- end
223
-
224
- def array_element(index, head, *tail)
225
- return array_match_rest(index, head, *tail) if head.type == :match_rest
226
-
227
- send("#{head.type}_array_element", head, index).then do |node|
228
- next node if tail.empty?
229
-
230
- s(:and,
231
- node,
232
- array_element(index + 1, *tail))
233
- end
234
- end
235
-
236
- def array_match_rest(index, node, *tail)
237
- child = node.children[0]
238
- rest = arr_rest_items(index, tail.size).then do |r|
239
- next r unless child
240
- match_var_clause(
241
- child,
242
- r
243
- )
244
- end
245
-
246
- return rest if tail.empty?
247
-
248
- s(:and,
249
- rest,
250
- array_rest_element(*tail))
251
- end
252
-
253
- def array_rest_element(head, *tail)
254
- send("#{head.type}_array_element", head, -(tail.size + 1)).then do |node|
255
- next node if tail.empty?
256
-
257
- s(:and,
258
- node,
259
- array_rest_element(*tail))
260
- end
261
- end
262
-
263
- def array_pattern_array_element(node, index)
264
- element = arr_item_at(index)
265
- locals.with(arr: locals[:arr, index]) do
266
- array_pattern_clause(node, element)
267
- end
268
- end
269
-
270
- def hash_pattern_array_element(node, index)
271
- element = arr_item_at(index)
272
- locals.with(hash: locals[:arr, index]) do
273
- hash_pattern_clause(node, element)
274
- end
275
- end
276
-
277
- def match_alt_array_element(node, index)
278
- children = node.children.map do |child, i|
279
- send :"#{child.type}_array_element", child, index
280
- end
281
- s(:or, *children)
282
- end
283
-
284
- def match_var_array_element(node, index)
285
- match_var_clause(node, arr_item_at(index))
286
- end
287
-
288
- def match_as_array_element(node, index)
289
- match_as_clause(node, arr_item_at(index))
290
- end
291
-
292
- def pin_array_element(node, index)
293
- case_eq_array_element node.children[0], index
294
- end
295
-
296
- def case_eq_array_element(node, index)
297
- case_eq_clause(node, arr_item_at(index))
298
- end
299
-
300
- def arr_item_at(index, arr = s(:lvar, locals[:arr]))
301
- s(:index, arr, index.to_ast_node)
302
- end
303
-
304
- def arr_rest_items(index, size, arr = s(:lvar, locals[:arr]))
305
- s(:index,
306
- arr,
307
- s(:irange,
308
- s(:int, index),
309
- s(:int, -(size + 1))))
310
- end
311
-
312
- #=========== ARRAY PATTERN (END) ===============
313
-
314
- #=========== HASH PATTERN (START) ===============
315
-
316
- def hash_pattern_clause(node, matchee = s(:lvar, locals[:matchee]))
317
- # Optimization: avoid hash modifications when not needed
318
- # (we use #dup and #delete when "reading" values when **rest is present
319
- # to assign the rest of the hash copy to it)
320
- @hash_match_rest = node.children.any? { |child| child.type == :match_rest || child.type == :match_nil_pattern }
321
- keys = hash_pattern_keys(node.children)
322
-
323
- deconstruct_keys_node(keys, matchee).then do |dnode|
324
- right =
325
- if node.children.empty?
326
- case_eq_clause(s(:hash), s(:lvar, locals[:hash]))
327
- else
328
- hash_element(*node.children)
329
- end
330
-
331
- next dnode if right.nil?
332
-
333
- s(:and,
334
- dnode,
335
- right)
336
- end.then do |right|
337
- s(:and,
338
- respond_to_check(matchee, :deconstruct_keys),
339
- right)
340
- end
341
- end
342
-
343
- def hash_pattern_keys(children)
344
- return s(:nil) if children.empty?
345
-
346
- children.filter_map do |child|
347
- # Skip ** without var
348
- next if child.type == :match_rest && child.children.empty?
349
- return s(:nil) if child.type == :match_rest || child.type == :match_nil_pattern
350
-
351
- send("#{child.type}_hash_key", child)
352
- end.then { |keys| s(:array, *keys) }
353
- end
354
-
355
- def pair_hash_key(node)
356
- node.children[0]
357
- end
358
-
359
- def match_var_hash_key(node)
360
- s(:sym, node.children[0])
361
- end
362
-
363
- def deconstruct_keys_node(keys, matchee = s(:lvar, locals[:matchee]))
364
- # Deconstruct once and use a copy of the hash for each pattern if we need **rest.
365
- hash_dup =
366
- if @hash_match_rest
367
- s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup))
368
- else
369
- s(:lvasgn, locals[:hash], s(:lvar, locals[:hash, :src]))
370
- end
371
-
372
- # Create a copy of the original hash if already deconstructed
373
- # FIXME: need better algorithm to handle different key sets
374
- # return hash_dup if deconstructed&.include?(locals[:hash])
375
-
376
- context.use_ruby_next!
377
-
378
- deconstructed << locals[:hash] if deconstructed
379
-
380
- right = s(:send,
381
- matchee, :deconstruct_keys, keys)
382
-
383
- s(:and,
384
- s(:or,
385
- s(:lvasgn, locals[:hash, :src], right),
386
- s(:true)), # rubocop:disable Lint/BooleanSymbol
387
- s(:and,
388
- s(:or,
389
- case_eq_clause(s(:const, nil, :Hash), s(:lvar, locals[:hash, :src])),
390
- raise_error(:TypeError, "#deconstruct_keys must return Hash")),
391
- hash_dup))
392
- end
393
-
394
- def hash_pattern_hash_element(node, key)
395
- element = hash_value_at(key)
396
- locals.with(hash: locals[:hash, deconstructed.size]) do
397
- hash_pattern_clause(node, element)
398
- end
399
- end
400
-
401
- def array_pattern_hash_element(node, key)
402
- element = hash_value_at(key)
403
- locals.with(arr: locals[:hash, deconstructed.size]) do
404
- array_pattern_clause(node, element)
405
- end
406
- end
407
-
408
- def hash_element(head, *tail)
409
- send("#{head.type}_hash_element", head).then do |node|
410
- next node if tail.empty?
411
-
412
- right = hash_element(*tail)
413
-
414
- next node if right.nil?
415
-
416
- s(:and,
417
- node,
418
- right)
419
- end
420
- end
421
-
422
- def pair_hash_element(node, _key = nil)
423
- key, val = *node.children
424
- send("#{val.type}_hash_element", val, key)
425
- end
426
-
427
- def match_alt_hash_element(node, key)
428
- element_node = s(:lvasgn, locals[:hash, :el], hash_value_at(key))
429
-
430
- children = locals.with(hash_element: locals[:hash, :el]) do
431
- node.children.map do |child, i|
432
- send :"#{child.type}_hash_element", child, key
433
- end
434
- end
435
-
436
- s(:and,
437
- s(:or,
438
- element_node,
439
- s(:true)), # rubocop:disable Lint/BooleanSymbol
440
- s(:or, *children))
441
- end
442
-
443
- def match_var_hash_element(node, key = nil)
444
- key ||= node.children[0]
445
- # We need to check whether key is present first
446
- s(:and,
447
- hash_has_key(key),
448
- match_var_clause(node, hash_value_at(key)))
449
- end
450
-
451
- def match_nil_pattern_hash_element(node, _key = nil)
452
- s(:send,
453
- s(:lvar, locals[:hash]),
454
- :empty?)
455
- end
456
-
457
- def match_rest_hash_element(node, _key = nil)
458
- # case {}; in **; end
459
- return if node.children.empty?
460
-
461
- child = node.children[0]
462
-
463
- raise ArgumentError, "Unknown hash match_rest child: #{child.type}" unless child.type == :match_var
464
-
465
- match_var_clause(child, s(:lvar, locals[:hash]))
466
- end
467
-
468
- def case_eq_hash_element(node, key)
469
- case_eq_clause node, hash_value_at(key)
470
- end
471
-
472
- def hash_value_at(key, hash = s(:lvar, locals[:hash]))
473
- return s(:lvar, locals.fetch(:hash_element)) if locals.key?(:hash_element)
474
-
475
- if @hash_match_rest
476
- s(:send,
477
- hash, :delete,
478
- key.to_ast_node)
479
- else
480
- s(:index,
481
- hash,
482
- key.to_ast_node)
483
- end
484
- end
485
-
486
- def hash_has_key(key, hash = s(:lvar, locals[:hash]))
487
- s(:send,
488
- hash, :key?,
489
- key.to_ast_node)
490
- end
491
-
492
- #=========== HASH PATTERN (END) ===============
493
-
494
- def with_guard(node, guard)
495
- return node unless guard
496
-
497
- s(:and,
498
- node,
499
- guard.children[0]).then do |expr|
500
- next expr unless guard.type == :unless_guard
501
- s(:send, expr, :!)
502
- end
503
- end
504
-
505
- def no_matching_pattern
506
- raise_error(
507
- :NoMatchingPatternError,
508
- s(:send,
509
- s(:lvar, locals[:matchee]), :inspect)
510
- )
511
- end
512
-
513
- def raise_error(type, msg = "")
514
- s(:send, s(:const, nil, :Kernel), :raise,
515
- s(:const, nil, type),
516
- msg.to_ast_node)
517
- end
518
-
519
- def respond_to_check(node, mid)
520
- s(:send, node, :respond_to?, mid.to_ast_node)
521
- end
522
-
523
- def respond_to_missing?(mid, *)
524
- return true if mid.match?(/_(clause|array_element)/)
525
- super
526
- end
527
-
528
- def method_missing(mid, *args, &block)
529
- return case_eq_clause(args.first) if mid.match?(/_clause$/)
530
- return case_eq_array_element(*args) if mid.match?(/_array_element$/)
531
- return case_eq_hash_element(*args) if mid.match?(/_hash_element$/)
532
- super
533
- end
534
-
535
- private
536
-
537
- attr_reader :deconstructed
538
- end
539
- end
540
- end
541
- end