From f03d763d3e4ae061faefba25a4dc62b403bdce0d Mon Sep 17 00:00:00 2001 From: Ilya Bylich Date: Wed, 17 Jun 2020 17:45:35 +0300 Subject: [PATCH] + ruby28.y: add find pattern. (#714) This commit tracks upstream commits ruby/ruby@ddded11 and ruby/ruby@f7906a7. --- doc/AST_FORMAT.md | 35 +++++++++++++++++++ lib/parser/ast/processor.rb | 1 + lib/parser/builders/default.rb | 5 +++ lib/parser/meta.rb | 2 +- lib/parser/ruby28.y | 51 ++++++++++++++++++--------- test/test_parser.rb | 64 +++++++++++++++++++++++++++++++++- 6 files changed, 139 insertions(+), 19 deletions(-) diff --git a/doc/AST_FORMAT.md b/doc/AST_FORMAT.md index 34ded3c58..2b4eba28a 100644 --- a/doc/AST_FORMAT.md +++ b/doc/AST_FORMAT.md @@ -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 @@ -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 +~~~ diff --git a/lib/parser/ast/processor.rb b/lib/parser/ast/processor.rb index f265b72c5..04d52b673 100644 --- a/lib/parser/ast/processor.rb +++ b/lib/parser/ast/processor.rb @@ -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) diff --git a/lib/parser/builders/default.rb b/lib/parser/builders/default.rb index b55f76381..186121fc4 100644 --- a/lib/parser/builders/default.rb +++ b/lib/parser/builders/default.rb @@ -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 diff --git a/lib/parser/meta.rb b/lib/parser/meta.rb index e8a7b66ed..3267e9f70 100644 --- a/lib/parser/meta.rb +++ b/lib/parser/meta.rb @@ -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 diff --git a/lib/parser/ruby28.y b/lib/parser/ruby28.y index ea44fd09e..27c0ea248 100644 --- a/lib/parser/ruby28.y +++ b/lib/parser/ruby28.y @@ -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) @@ -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 @@ -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 @@ -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 { @@ -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 diff --git a/test/test_parser.rb b/test/test_parser.rb index a2948c727..0013d3262 100644 --- a/test/test_parser.rb +++ b/test/test_parser.rb @@ -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 @@ -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