ruby-next 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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