Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

+ ruby28.y: add find pattern. #714

Merged
merged 1 commit into from Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions doc/AST_FORMAT.md
Expand Up @@ -2191,6 +2191,24 @@ Format:
~~~ name (match-nil-pattern)
~~~

### Matching using find pattern

Format:

~~~
(find-pattern
(match-rest
(match-var :a))
(int 42)
(match-rest))
"in [*, 42, *]"
~ begin
~ end
~~~~~~~~~~ expression
~~~

Note that it can be used as a top-level pattern only when used in a `case` statement. In that case `begin` and `end` are empty.

### Matching using const pattern

#### With array pattern
Expand Down Expand Up @@ -2246,3 +2264,20 @@ Format:
~ expression (const-pattern.const)
~~ expression (const-pattern.array_pattern)
~~~

#### With find pattern

Format:

~~~
(const-pattern
(const nil :X)
(find-pattern
(match-rest)
(int 42)
(match-rest)))
"in X[*, 42, *]"
~ begin
~ end
~~~~~~~~~~~ expression
~~~
1 change: 1 addition & 0 deletions lib/parser/ast/processor.rb
Expand Up @@ -258,6 +258,7 @@ def on_numblock(node)
alias on_array_pattern_with_tail process_regular_node
alias on_hash_pattern process_regular_node
alias on_const_pattern process_regular_node
alias on_find_pattern process_regular_node

# @private
def process_variable_node(node)
Expand Down
5 changes: 5 additions & 0 deletions lib/parser/builders/default.rb
Expand Up @@ -1400,6 +1400,11 @@ def array_pattern(lbrack_t, elements, rbrack_t)
collection_map(lbrack_t, elements, rbrack_t))
end

def find_pattern(lbrack_t, elements, rbrack_t)
n(:find_pattern, elements,
collection_map(lbrack_t, elements, rbrack_t))
end

def match_with_trailing_comma(match, comma_t)
n(:match_with_trailing_comma, [ match ], expr_map(match.loc.expression.join(loc(comma_t))))
end
Expand Down
2 changes: 1 addition & 1 deletion lib/parser/meta.rb
Expand Up @@ -31,7 +31,7 @@ module class sclass def defs def_e defs_e undef alias args
match_var pin match_alt match_as match_rest
array_pattern match_with_trailing_comma array_pattern_with_tail
hash_pattern const_pattern if_guard unless_guard match_nil_pattern
empty_else
empty_else find_pattern
).map(&:to_sym).to_set.freeze

end # Meta
Expand Down
51 changes: 34 additions & 17 deletions lib/parser/ruby28.y
Expand Up @@ -1883,6 +1883,10 @@ opt_block_args_tail:
{
result = @builder.array_pattern(nil, [val[0]].concat(val[2]), nil)
}
| p_find
{
result = @builder.find_pattern(nil, val[0], nil)
}
| p_args_tail
{
result = @builder.array_pattern(nil, val[0], nil)
Expand Down Expand Up @@ -1925,6 +1929,12 @@ opt_block_args_tail:
pattern = @builder.array_pattern(nil, val[2], nil)
result = @builder.const_pattern(val[0], val[1], pattern, val[3])
}
| p_const p_lparen p_find rparen
{
@pattern_hash_keys.pop
pattern = @builder.find_pattern(nil, val[2], nil)
result = @builder.const_pattern(val[0], val[1], pattern, val[3])
}
| p_const p_lparen p_kwargs rparen
{
@pattern_hash_keys.pop
Expand All @@ -1942,6 +1952,12 @@ opt_block_args_tail:
pattern = @builder.array_pattern(nil, val[2], nil)
result = @builder.const_pattern(val[0], val[1], pattern, val[3])
}
| p_const p_lbracket p_find rbracket
{
@pattern_hash_keys.pop
pattern = @builder.find_pattern(nil, val[2], nil)
result = @builder.const_pattern(val[0], val[1], pattern, val[3])
}
| p_const p_lbracket p_kwargs rbracket
{
@pattern_hash_keys.pop
Expand All @@ -1953,14 +1969,13 @@ opt_block_args_tail:
pattern = @builder.array_pattern(val[1], nil, val[2])
result = @builder.const_pattern(val[0], val[1], pattern, val[2])
}
| tLBRACK
| tLBRACK p_args rbracket
{
@pattern_hash_keys.push
result = @builder.array_pattern(val[0], val[1], val[2])
}
p_args rbracket
| tLBRACK p_find rbracket
{
@pattern_hash_keys.pop
result = @builder.array_pattern(val[0], val[2], val[3])
result = @builder.find_pattern(val[0], val[1], val[2])
}
| tLBRACK rbracket
{
Expand Down Expand Up @@ -2041,25 +2056,27 @@ opt_block_args_tail:
result = [ *val[0], last_item ]
}

p_args_tail: tSTAR tIDENTIFIER
p_args_tail: p_rest
{
match_rest = @builder.match_rest(val[0], val[1])
result = [ match_rest ]
result = [ val[0] ]
}
| tSTAR tIDENTIFIER tCOMMA p_args_post
| p_rest tCOMMA p_args_post
{
match_rest = @builder.match_rest(val[0], val[1])
result = [ match_rest, *val[3] ]
result = [ val[0], *val[2] ]
}
| tSTAR

p_find: p_rest tCOMMA p_args_post tCOMMA p_rest
{
match_rest = @builder.match_rest(val[0])
result = [ match_rest ]
result = [ val[0], *val[2], val[4] ]
}
| tSTAR tCOMMA p_args_post

p_rest: tSTAR tIDENTIFIER
{
result = @builder.match_rest(val[0], val[1])
}
| tSTAR
{
match_rest = @builder.match_rest(val[0])
result = [ match_rest, *val[2] ]
result = @builder.match_rest(val[0])
}

p_args_post: p_arg
Expand Down
64 changes: 63 additions & 1 deletion test/test_parser.rb
Expand Up @@ -8460,7 +8460,7 @@ def assert_parses_pattern_match(ast, code, source_maps = '', versions = SINCE_2_
nil),
"#{case_pre}#{code}; end",
source_maps,
SINCE_2_7
versions
)
end

Expand Down Expand Up @@ -9715,4 +9715,66 @@ def test_rasgn_line_continuation
%{ ^^ location},
SINCE_2_8)
end

def test_find_pattern
assert_parses_pattern_match(
s(:in_pattern,
s(:find_pattern,
s(:match_rest,
s(:match_var, :x)),
s(:match_as,
s(:int, 1),
s(:match_var, :a)),
s(:match_rest,
s(:match_var, :y))),
nil,
s(:true)),
%q{in [*x, 1 => a, *y] then true},
%q{ ~~~~~~~~~~~~~~~~ expression (in_pattern.find_pattern)
| ~ begin (in_pattern.find_pattern)
| ~ end (in_pattern.find_pattern)
| ~~ expression (in_pattern.find_pattern.match_rest/1)
| ~~ expression (in_pattern.find_pattern.match_rest/2)},
SINCE_2_8)

assert_parses_pattern_match(
s(:in_pattern,
s(:const_pattern,
s(:const, nil, :String),
s(:find_pattern,
s(:match_rest),
s(:int, 1),
s(:match_rest))),
nil,
s(:true)),
%q{in String(*, 1, *) then true},
%q{ ~~~~~~~ expression (in_pattern.const_pattern.find_pattern)},
SINCE_2_8)

assert_parses_pattern_match(
s(:in_pattern,
s(:const_pattern,
s(:const, nil, :Array),
s(:find_pattern,
s(:match_rest),
s(:int, 1),
s(:match_rest))),
nil,
s(:true)),
%q{in Array[*, 1, *] then true},
%q{ ~~~~~~~ expression (in_pattern.const_pattern.find_pattern)},
SINCE_2_8)

assert_parses_pattern_match(
s(:in_pattern,
s(:find_pattern,
s(:match_rest),
s(:int, 42),
s(:match_rest)),
nil,
s(:true)),
%q{in *, 42, * then true},
%q{ ~~~~~~~~ expression (in_pattern.find_pattern)},
SINCE_2_8)
end
end