Skip to content

Commit 08f881c

Browse files
committed
Fix array pattern unparsing
Parser represents %-encoded array patterns with the `ArrayNode`, not `ArrayPatternNode`. We can infer required %-notation variant given array's children elements. ```bash $ bundle exec bin/unparser -e 'a => %i[a b]' (string) Original-Source: a => %i[a b] Generated-Source: a => [:a, :b] Original-Node: (match-pattern (send nil :a) (array (sym :a) (sym :b))) Generated-Node: (match-pattern (send nil :a) (array-pattern (sym :a) (sym :b))) Node-Diff: @@ -1,5 +1,5 @@ (match-pattern (send nil :a) - (array + (array-pattern (sym :a) (sym :b))) Error: (string) ```
1 parent ecf6158 commit 08f881c

File tree

7 files changed

+139
-3
lines changed

7 files changed

+139
-3
lines changed

lib/unparser.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ def self.buffer(source, identification = '(string)')
295295
require 'unparser/emitter/match_pattern'
296296
require 'unparser/emitter/match_pattern_p'
297297
require 'unparser/writer'
298+
require 'unparser/writer/array'
298299
require 'unparser/writer/binary'
299300
require 'unparser/writer/dynamic_string'
300301
require 'unparser/writer/regexp'

lib/unparser/emitter/in_pattern.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def dispatch
1616

1717
ws
1818

19-
visit(target)
19+
dispatch_target(target)
2020

2121
if unless_guard
2222
ws
@@ -31,6 +31,14 @@ def dispatch
3131
nl
3232
end
3333
end
34+
35+
def dispatch_target(target)
36+
if n_array?(target)
37+
writer_with(Writer::Array, node: target).emit_compact
38+
else
39+
visit(target)
40+
end
41+
end
3442
end # InPattern
3543
end # Emitter
3644
end # Unparser

lib/unparser/emitter/match_pattern.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ class MatchPattern < self
1414
def dispatch
1515
visit(target)
1616
write(' => ')
17-
visit(pattern)
17+
18+
if n_array?(pattern)
19+
writer_with(Writer::Array, node: pattern).emit_compact
20+
else
21+
visit(pattern)
22+
end
1823
end
1924
end # MatchPattern
2025
end # Emitter

lib/unparser/emitter/match_pattern_p.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ class MatchPatternP < self
1313
def dispatch
1414
visit(target)
1515
write(' in ')
16-
visit(pattern)
16+
17+
if n_array?(pattern)
18+
writer_with(Writer::Array, node: pattern).emit_compact
19+
else
20+
visit(pattern)
21+
end
1722
end
1823
end # MatchPatternP
1924
end # Emitter

lib/unparser/writer/array.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# frozen_string_literal: true
2+
3+
module Unparser
4+
module Writer
5+
class Array
6+
include Writer, Adamantium
7+
8+
MAP = {
9+
dsym: '%I',
10+
sym: '%i',
11+
dstr: '%W',
12+
str: '%w'
13+
}.freeze
14+
private_constant(*constants(false))
15+
16+
def emit_compact # rubocop:disable Metrics/AbcSize
17+
children_generic_type = array_elements_generic_type || :sym
18+
19+
write(MAP.fetch(children_generic_type))
20+
21+
# NOTE: I'd like to use stricter `children.fetch(0)` here instead of `children.first`,
22+
# but mutant tries to inject `fetch(-1)` and complains that tests pass.
23+
# fetch(0) and fetch(-1) are equivalent for singleton arrays and there's not `sole` in the stdlb.
24+
parentheses('[', ']') do
25+
delimited(children, ' ') do |child|
26+
if n_sym?(child) || n_str?(child)
27+
write(child.children.first.to_s)
28+
else
29+
write('#{')
30+
emitter(unwrap_single_begin(child.children.first)).write_to_buffer
31+
write('}')
32+
end
33+
end
34+
end
35+
end
36+
37+
private
38+
39+
def array_elements_generic_type
40+
children_types = children.to_set(&:type)
41+
42+
if children_types == Set[:sym, :dsym]
43+
:dsym
44+
elsif children_types == Set[:str, :dstr]
45+
:dstr
46+
else
47+
children_types.first
48+
end
49+
end
50+
end # Array
51+
end # Writer
52+
end # Unparser

test/corpus/literal/pattern.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,40 @@
4343
1 => [*]
4444
1 in [*, 42, *]
4545
1 in [*, a, *foo]
46+
a => %i[a b]
47+
a => %w[a b]
48+
a => ["a", "b"]
49+
a => [:a, :b]
50+
a => [:a, "b"]
51+
a => [true, false, nil]
52+
a => {a: 1, b: 2}
53+
a in %i[a b]
54+
a in %w[a b]
55+
a in ["a", "b"]
56+
a in [:a, :b]
57+
a in [:a, "b"]
58+
a in [true, false, nil]
59+
a in {a: 1, b: 2}
60+
case foo
61+
in %i[a b]
62+
end
63+
case foo
64+
in %w[a b]
65+
end
66+
case foo
67+
in [:a, "b"]
68+
end
69+
case foo
70+
in [1, 2]
71+
end
72+
case foo
73+
in [true, false, nil]
74+
end
75+
a => %I[a b #{foo(1)}]
76+
a => %W[a b #{foo(1)}]
77+
case a
78+
in %I[#{1 + 1}]
79+
end
80+
case foo
81+
in %i[a b c $FILE]
82+
end

test/corpus/semantic/pattern.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
case foo
2+
in %s[a b]
3+
end
4+
case foo
5+
in %r[foo]
6+
end
7+
case foo
8+
in %r[/foo.+bar/]
9+
end
10+
case foo
11+
in %r:/foo.+bar/:
12+
end
13+
case foo
14+
in %x(rm -rf /)
15+
end
16+
case foo
17+
in %i[]
18+
end
19+
case foo
20+
in %w[]
21+
end
22+
case foo
23+
in %q[a b c #{foo}]
24+
end
25+
case foo
26+
in %q[a b c $FILE]
27+
end
28+

0 commit comments

Comments
 (0)