| // Copyright (C) 2025 Free Software Foundation, Inc. |
| |
| // This file is part of GCC. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with GCC; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| /* DO NOT INCLUDE ANYWHERE - this is automatically included |
| * by rust-parse-impl.h |
| * This is also the reason why there are no include guards. */ |
| |
| #include "rust-parse.h" |
| |
| namespace Rust { |
| |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::Pattern> |
| Parser<ManagedTokenSource>::parse_pattern () |
| { |
| location_t start_locus = lexer.peek_token ()->get_locus (); |
| |
| /* skip optional starting pipe */ |
| maybe_skip_token (PIPE); |
| |
| auto first = parse_pattern_no_alt (); |
| |
| if (lexer.peek_token ()->get_id () != PIPE) |
| /* no alternates */ |
| return first; |
| |
| std::vector<std::unique_ptr<AST::Pattern>> alts; |
| if (first != nullptr) |
| alts.push_back (std::move (first)); |
| |
| do |
| { |
| lexer.skip_token (); |
| auto follow = parse_pattern_no_alt (); |
| if (follow != nullptr) |
| alts.push_back (std::move (follow)); |
| } |
| |
| while (lexer.peek_token ()->get_id () == PIPE); |
| |
| if (alts.empty ()) |
| return nullptr; |
| |
| /* alternates */ |
| return std::unique_ptr<AST::Pattern> ( |
| new AST::AltPattern (std::move (alts), start_locus)); |
| } |
| |
| // Parses a pattern without alternates ('|') |
| // (will further disambiguate any pattern). |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::Pattern> |
| Parser<ManagedTokenSource>::parse_pattern_no_alt () |
| { |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case TRUE_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::LiteralPattern> ( |
| new AST::LiteralPattern (Values::Keywords::TRUE_LITERAL, |
| AST::Literal::BOOL, t->get_locus (), |
| t->get_type_hint ())); |
| case FALSE_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::LiteralPattern> ( |
| new AST::LiteralPattern (Values::Keywords::FALSE_LITERAL, |
| AST::Literal::BOOL, t->get_locus (), |
| t->get_type_hint ())); |
| case CHAR_LITERAL: |
| case BYTE_CHAR_LITERAL: |
| case INT_LITERAL: |
| case FLOAT_LITERAL: |
| return parse_literal_or_range_pattern (); |
| case STRING_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::LiteralPattern> ( |
| new AST::LiteralPattern (t->get_str (), AST::Literal::STRING, |
| t->get_locus (), t->get_type_hint ())); |
| case BYTE_STRING_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::LiteralPattern> ( |
| new AST::LiteralPattern (t->get_str (), AST::Literal::BYTE_STRING, |
| t->get_locus (), t->get_type_hint ())); |
| case RAW_STRING_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::LiteralPattern> ( |
| new AST::LiteralPattern (t->get_str (), AST::Literal::RAW_STRING, |
| t->get_locus (), t->get_type_hint ())); |
| // raw string and raw byte string literals too if they are readded to |
| // lexer |
| case MINUS: |
| if (lexer.peek_token (1)->get_id () == INT_LITERAL) |
| { |
| return parse_literal_or_range_pattern (); |
| } |
| else if (lexer.peek_token (1)->get_id () == FLOAT_LITERAL) |
| { |
| return parse_literal_or_range_pattern (); |
| } |
| else |
| { |
| Error error (t->get_locus (), "unexpected token %<-%> in pattern - " |
| "did you forget an integer literal"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| case UNDERSCORE: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::WildcardPattern> ( |
| new AST::WildcardPattern (t->get_locus ())); |
| case DOT_DOT: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::RestPattern> ( |
| new AST::RestPattern (t->get_locus ())); |
| case REF: |
| case MUT: |
| return parse_identifier_pattern (); |
| case IDENTIFIER: |
| /* if identifier with no scope resolution afterwards, identifier |
| * pattern. if scope resolution afterwards, path pattern (or range |
| * pattern or struct pattern or tuple struct pattern) or macro |
| * invocation */ |
| return parse_ident_leading_pattern (); |
| case AMP: |
| case LOGICAL_AND: |
| // reference pattern |
| return parse_reference_pattern (); |
| case LEFT_PAREN: |
| // tuple pattern or grouped pattern |
| return parse_grouped_or_tuple_pattern (); |
| case LEFT_SQUARE: |
| // slice pattern |
| return parse_slice_pattern (); |
| case LEFT_SHIFT: |
| case LEFT_ANGLE: |
| { |
| // qualified path in expression or qualified range pattern bound |
| AST::QualifiedPathInExpression path |
| = parse_qualified_path_in_expression (); |
| |
| if (lexer.peek_token ()->get_id () == DOT_DOT_EQ |
| || lexer.peek_token ()->get_id () == ELLIPSIS |
| || lexer.peek_token ()->get_id () == DOT_DOT) |
| { |
| // qualified range pattern bound, so parse rest of range pattern |
| AST::RangeKind kind |
| = AST::tokenid_to_rangekind (lexer.peek_token ()->get_id ()); |
| lexer.skip_token (); |
| |
| std::unique_ptr<AST::RangePatternBoundQualPath> lower_bound ( |
| new AST::RangePatternBoundQualPath (std::move (path))); |
| std::unique_ptr<AST::RangePatternBound> upper_bound |
| = parse_range_pattern_bound (); |
| |
| return std::unique_ptr<AST::RangePattern> ( |
| new AST::RangePattern (std::move (lower_bound), |
| std::move (upper_bound), kind, |
| t->get_locus ())); |
| } |
| else |
| { |
| // just qualified path in expression |
| return std::unique_ptr<AST::QualifiedPathInExpression> ( |
| new AST::QualifiedPathInExpression (std::move (path))); |
| } |
| } |
| case SUPER: |
| case SELF: |
| case SELF_ALIAS: |
| case CRATE: |
| case SCOPE_RESOLUTION: |
| case DOLLAR_SIGN: |
| { |
| // path in expression or range pattern bound |
| AST::PathInExpression path = parse_path_in_expression (); |
| |
| const_TokenPtr next = lexer.peek_token (); |
| switch (next->get_id ()) |
| { |
| case DOT_DOT_EQ: |
| case DOT_DOT: |
| case ELLIPSIS: |
| { |
| // qualified range pattern bound, so parse rest of range pattern |
| AST::RangeKind kind = AST::tokenid_to_rangekind (next->get_id ()); |
| lexer.skip_token (); |
| |
| std::unique_ptr<AST::RangePatternBoundPath> lower_bound ( |
| new AST::RangePatternBoundPath (std::move (path))); |
| std::unique_ptr<AST::RangePatternBound> upper_bound |
| = parse_range_pattern_bound (); |
| |
| return std::unique_ptr<AST::RangePattern> ( |
| new AST::RangePattern (std::move (lower_bound), |
| std::move (upper_bound), kind, |
| next->get_locus ())); |
| } |
| case EXCLAM: |
| return parse_macro_invocation_partial (std::move (path), |
| AST::AttrVec ()); |
| case LEFT_PAREN: |
| { |
| // tuple struct |
| lexer.skip_token (); |
| |
| // parse items |
| std::unique_ptr<AST::TupleStructItems> items |
| = parse_tuple_struct_items (); |
| if (items == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse tuple struct items"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| return nullptr; |
| } |
| |
| return std::unique_ptr<AST::TupleStructPattern> ( |
| new AST::TupleStructPattern (std::move (path), |
| std::move (items))); |
| } |
| case LEFT_CURLY: |
| { |
| // struct |
| lexer.skip_token (); |
| |
| // parse elements (optional) |
| AST::StructPatternElements elems = parse_struct_pattern_elems (); |
| |
| if (!skip_token (RIGHT_CURLY)) |
| { |
| return nullptr; |
| } |
| |
| return std::unique_ptr<AST::StructPattern> ( |
| new AST::StructPattern (std::move (path), t->get_locus (), |
| std::move (elems))); |
| } |
| default: |
| // assume path in expression |
| return std::unique_ptr<AST::PathInExpression> ( |
| new AST::PathInExpression (std::move (path))); |
| } |
| } |
| default: |
| add_error (Error (t->get_locus (), "unexpected token %qs in pattern", |
| t->get_token_description ())); |
| |
| return nullptr; |
| } |
| } |
| |
| // Parses a single or double reference pattern. |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::ReferencePattern> |
| Parser<ManagedTokenSource>::parse_reference_pattern () |
| { |
| // parse double or single ref |
| bool is_double_ref = false; |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case AMP: |
| // still false |
| lexer.skip_token (); |
| break; |
| case LOGICAL_AND: |
| is_double_ref = true; |
| lexer.skip_token (); |
| break; |
| default: |
| add_error (Error (t->get_locus (), |
| "unexpected token %qs in reference pattern", |
| t->get_token_description ())); |
| |
| return nullptr; |
| } |
| |
| // parse mut (if it exists) |
| bool is_mut = false; |
| if (lexer.peek_token ()->get_id () == MUT) |
| { |
| is_mut = true; |
| lexer.skip_token (); |
| } |
| |
| // parse pattern to get reference of (required) |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern_no_alt (); |
| if (pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse pattern in reference pattern"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return nullptr; |
| } |
| |
| return std::unique_ptr<AST::ReferencePattern> ( |
| new AST::ReferencePattern (std::move (pattern), is_mut, is_double_ref, |
| t->get_locus ())); |
| } |
| |
| /* Parses a grouped pattern or tuple pattern. Prefers grouped over tuple if |
| * only a single element with no commas. */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::Pattern> |
| Parser<ManagedTokenSource>::parse_grouped_or_tuple_pattern () |
| { |
| location_t paren_locus = lexer.peek_token ()->get_locus (); |
| skip_token (LEFT_PAREN); |
| |
| // detect '..' token (ranged with no lower range) |
| if (lexer.peek_token ()->get_id () == DOT_DOT) |
| { |
| lexer.skip_token (); |
| |
| // parse new patterns while next token is a comma |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () == COMMA) |
| { |
| lexer.skip_token (); |
| |
| // break if next token is ')' |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) |
| { |
| break; |
| } |
| |
| // parse pattern, which is required |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (pattern == nullptr) |
| { |
| Error error ( |
| lexer.peek_token ()->get_locus (), |
| "failed to parse pattern inside ranged tuple pattern"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return nullptr; |
| } |
| patterns.push_back (std::move (pattern)); |
| |
| t = lexer.peek_token (); |
| } |
| |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| // skip somewhere? |
| return nullptr; |
| } |
| |
| // create tuple pattern items with only upper pattern items |
| std::unique_ptr<AST::TuplePatternItemsHasRest> items ( |
| new AST::TuplePatternItemsHasRest ( |
| std::vector<std::unique_ptr<AST::Pattern>> (), std::move (patterns))); |
| return std::unique_ptr<AST::TuplePattern> ( |
| new AST::TuplePattern (std::move (items), paren_locus)); |
| } |
| else if (lexer.peek_token ()->get_id () == RIGHT_PAREN) |
| { |
| skip_token (RIGHT_PAREN); |
| auto items = std::unique_ptr<AST::TuplePatternItemsNoRest> ( |
| new AST::TuplePatternItemsNoRest ( |
| std::vector<std::unique_ptr<AST::Pattern>> ())); |
| return std::unique_ptr<AST::TuplePattern> ( |
| new AST::TuplePattern (std::move (items), paren_locus)); |
| } |
| |
| // parse initial pattern (required) |
| std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern (); |
| if (initial_pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse pattern in grouped or tuple pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| // branch on whether next token is a comma or not |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case RIGHT_PAREN: |
| // grouped pattern |
| lexer.skip_token (); |
| |
| return std::unique_ptr<AST::GroupedPattern> ( |
| new AST::GroupedPattern (std::move (initial_pattern), paren_locus)); |
| case COMMA: |
| { |
| // tuple pattern |
| lexer.skip_token (); |
| |
| // create vector of patterns |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; |
| patterns.push_back (std::move (initial_pattern)); |
| |
| t = lexer.peek_token (); |
| while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT) |
| { |
| // parse pattern (required) |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (pattern == nullptr) |
| { |
| Error error (t->get_locus (), |
| "failed to parse pattern in tuple pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| patterns.push_back (std::move (pattern)); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| break; |
| |
| lexer.skip_token (); |
| t = lexer.peek_token (); |
| } |
| |
| t = lexer.peek_token (); |
| if (t->get_id () == RIGHT_PAREN) |
| { |
| // non-ranged tuple pattern |
| lexer.skip_token (); |
| |
| std::unique_ptr<AST::TuplePatternItemsNoRest> items ( |
| new AST::TuplePatternItemsNoRest (std::move (patterns))); |
| return std::unique_ptr<AST::TuplePattern> ( |
| new AST::TuplePattern (std::move (items), paren_locus)); |
| } |
| else if (t->get_id () == DOT_DOT) |
| { |
| // ranged tuple pattern |
| lexer.skip_token (); |
| |
| // parse upper patterns |
| std::vector<std::unique_ptr<AST::Pattern>> upper_patterns; |
| t = lexer.peek_token (); |
| while (t->get_id () == COMMA) |
| { |
| lexer.skip_token (); |
| |
| // break if end |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) |
| break; |
| |
| // parse pattern (required) |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse pattern in tuple pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| upper_patterns.push_back (std::move (pattern)); |
| |
| t = lexer.peek_token (); |
| } |
| |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| return nullptr; |
| } |
| |
| std::unique_ptr<AST::TuplePatternItemsHasRest> items ( |
| new AST::TuplePatternItemsHasRest (std::move (patterns), |
| std::move (upper_patterns))); |
| return std::unique_ptr<AST::TuplePattern> ( |
| new AST::TuplePattern (std::move (items), paren_locus)); |
| } |
| else |
| { |
| // some kind of error |
| Error error (t->get_locus (), |
| "failed to parse tuple pattern (probably) or maybe " |
| "grouped pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| } |
| default: |
| // error |
| add_error (Error (t->get_locus (), |
| "unrecognised token %qs in grouped or tuple pattern " |
| "after first pattern", |
| t->get_token_description ())); |
| |
| return nullptr; |
| } |
| } |
| |
| /* Parses a slice pattern that can match arrays or slices. Parses the square |
| * brackets too. */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::SlicePattern> |
| Parser<ManagedTokenSource>::parse_slice_pattern () |
| { |
| location_t square_locus = lexer.peek_token ()->get_locus (); |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; |
| tl::optional<std::vector<std::unique_ptr<AST::Pattern>>> upper_patterns |
| = tl::nullopt; |
| |
| // lambda function to determine which vector to push new patterns into |
| auto get_pattern_ref |
| = [&] () -> std::vector<std::unique_ptr<AST::Pattern>> & { |
| return upper_patterns.has_value () ? upper_patterns.value () : patterns; |
| }; |
| |
| skip_token (LEFT_SQUARE); |
| |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) |
| { |
| skip_token (RIGHT_SQUARE); |
| std::unique_ptr<AST::SlicePatternItemsNoRest> items ( |
| new AST::SlicePatternItemsNoRest (std::move (patterns))); |
| return std::unique_ptr<AST::SlicePattern> ( |
| new AST::SlicePattern (std::move (items), square_locus)); |
| } |
| |
| // parse initial pattern (required) |
| if (lexer.peek_token ()->get_id () == DOT_DOT) |
| { |
| lexer.skip_token (); |
| upper_patterns = std::vector<std::unique_ptr<AST::Pattern>> (); |
| } |
| else |
| { |
| // Not a rest pattern `..`, parse normally |
| std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern (); |
| if (initial_pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse initial pattern in slice pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| patterns.push_back (std::move (initial_pattern)); |
| } |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () == COMMA) |
| { |
| lexer.skip_token (); |
| |
| // break if end bracket |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) |
| break; |
| |
| if (lexer.peek_token ()->get_id () == DOT_DOT) |
| { |
| if (upper_patterns.has_value ()) |
| { |
| // DOT_DOT has been parsed before |
| Error error (lexer.peek_token ()->get_locus (), "%s", |
| "`..` can only be used once per slice pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| upper_patterns = std::vector<std::unique_ptr<AST::Pattern>> (); |
| lexer.skip_token (); |
| t = lexer.peek_token (); |
| continue; |
| } |
| |
| // parse pattern (required) |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse pattern in slice pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| get_pattern_ref ().push_back (std::move (pattern)); |
| |
| t = lexer.peek_token (); |
| } |
| |
| if (!skip_token (RIGHT_SQUARE)) |
| { |
| return nullptr; |
| } |
| |
| if (upper_patterns.has_value ()) |
| { |
| // Slice pattern with rest |
| std::unique_ptr<AST::SlicePatternItemsHasRest> items ( |
| new AST::SlicePatternItemsHasRest ( |
| std::move (patterns), std::move (upper_patterns.value ()))); |
| return std::unique_ptr<AST::SlicePattern> ( |
| new AST::SlicePattern (std::move (items), square_locus)); |
| } |
| |
| // Rest-less slice pattern |
| std::unique_ptr<AST::SlicePatternItemsNoRest> items ( |
| new AST::SlicePatternItemsNoRest (std::move (patterns))); |
| return std::unique_ptr<AST::SlicePattern> ( |
| new AST::SlicePattern (std::move (items), square_locus)); |
| } |
| |
| /* Parses an identifier pattern (pattern that binds a value matched to a |
| * variable). */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::IdentifierPattern> |
| Parser<ManagedTokenSource>::parse_identifier_pattern () |
| { |
| location_t locus = lexer.peek_token ()->get_locus (); |
| |
| bool has_ref = false; |
| if (lexer.peek_token ()->get_id () == REF) |
| { |
| has_ref = true; |
| lexer.skip_token (); |
| |
| // DEBUG |
| rust_debug ("parsed ref in identifier pattern"); |
| } |
| |
| bool has_mut = false; |
| if (lexer.peek_token ()->get_id () == MUT) |
| { |
| has_mut = true; |
| lexer.skip_token (); |
| } |
| |
| // parse identifier (required) |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); |
| if (ident_tok == nullptr) |
| { |
| // skip somewhere? |
| return nullptr; |
| } |
| Identifier ident{ident_tok}; |
| |
| // DEBUG |
| rust_debug ("parsed identifier in identifier pattern"); |
| |
| // parse optional pattern binding thing |
| std::unique_ptr<AST::Pattern> bind_pattern = nullptr; |
| if (lexer.peek_token ()->get_id () == PATTERN_BIND) |
| { |
| lexer.skip_token (); |
| |
| // parse required pattern to bind |
| bind_pattern = parse_pattern_no_alt (); |
| if (bind_pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse pattern to bind in identifier pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| } |
| |
| // DEBUG |
| rust_debug ("about to return identifier pattern"); |
| |
| return std::unique_ptr<AST::IdentifierPattern> ( |
| new AST::IdentifierPattern (std::move (ident), locus, has_ref, has_mut, |
| std::move (bind_pattern))); |
| } |
| |
| /* Parses a pattern that opens with an identifier. This includes identifier |
| * patterns, path patterns (and derivatives such as struct patterns, tuple |
| * struct patterns, and macro invocations), and ranges. */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::Pattern> |
| Parser<ManagedTokenSource>::parse_ident_leading_pattern () |
| { |
| // ensure first token is actually identifier |
| const_TokenPtr initial_tok = lexer.peek_token (); |
| if (initial_tok->get_id () != IDENTIFIER) |
| { |
| return nullptr; |
| } |
| |
| // save initial identifier as it may be useful (but don't skip) |
| std::string initial_ident = initial_tok->get_str (); |
| |
| // parse next tokens as a PathInExpression |
| AST::PathInExpression path = parse_path_in_expression (); |
| |
| // branch on next token |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case EXCLAM: |
| return parse_macro_invocation_partial (std::move (path), AST::AttrVec ()); |
| case LEFT_PAREN: |
| { |
| // tuple struct |
| lexer.skip_token (); |
| |
| // DEBUG |
| rust_debug ("parsing tuple struct pattern"); |
| |
| // parse items |
| std::unique_ptr<AST::TupleStructItems> items |
| = parse_tuple_struct_items (); |
| if (items == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse tuple struct items"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| // DEBUG |
| rust_debug ("successfully parsed tuple struct items"); |
| |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| return nullptr; |
| } |
| |
| // DEBUG |
| rust_debug ("successfully parsed tuple struct pattern"); |
| |
| return std::unique_ptr<AST::TupleStructPattern> ( |
| new AST::TupleStructPattern (std::move (path), std::move (items))); |
| } |
| case LEFT_CURLY: |
| { |
| // struct |
| lexer.skip_token (); |
| |
| // parse elements (optional) |
| AST::StructPatternElements elems = parse_struct_pattern_elems (); |
| |
| if (!skip_token (RIGHT_CURLY)) |
| { |
| return nullptr; |
| } |
| |
| // DEBUG |
| rust_debug ("successfully parsed struct pattern"); |
| |
| return std::unique_ptr<AST::StructPattern> ( |
| new AST::StructPattern (std::move (path), initial_tok->get_locus (), |
| std::move (elems))); |
| } |
| case DOT_DOT_EQ: |
| case DOT_DOT: |
| case ELLIPSIS: |
| { |
| // range |
| AST::RangeKind kind |
| = AST::tokenid_to_rangekind (lexer.peek_token ()->get_id ()); |
| |
| lexer.skip_token (); |
| |
| std::unique_ptr<AST::RangePatternBoundPath> lower_bound ( |
| new AST::RangePatternBoundPath (std::move (path))); |
| std::unique_ptr<AST::RangePatternBound> upper_bound |
| = parse_range_pattern_bound (); |
| |
| return std::unique_ptr<AST::RangePattern> ( |
| new AST::RangePattern (std::move (lower_bound), |
| std::move (upper_bound), kind, |
| t->get_locus ())); |
| } |
| case PATTERN_BIND: |
| { |
| // only allow on single-segment paths |
| if (path.is_single_segment ()) |
| { |
| // identifier with pattern bind |
| lexer.skip_token (); |
| |
| std::unique_ptr<AST::Pattern> bind_pattern |
| = parse_pattern_no_alt (); |
| if (bind_pattern == nullptr) |
| { |
| Error error ( |
| t->get_locus (), |
| "failed to parse pattern to bind to identifier pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| return std::unique_ptr<AST::IdentifierPattern> ( |
| new AST::IdentifierPattern (std::move (initial_ident), |
| initial_tok->get_locus (), false, |
| false, std::move (bind_pattern))); |
| } |
| Error error ( |
| t->get_locus (), |
| "failed to parse pattern bind to a path, not an identifier"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| default: |
| // assume identifier if single segment |
| if (path.is_single_segment ()) |
| { |
| return std::unique_ptr<AST::IdentifierPattern> ( |
| new AST::IdentifierPattern (std::move (initial_ident), |
| initial_tok->get_locus ())); |
| } |
| // return path otherwise |
| return std::unique_ptr<AST::PathInExpression> ( |
| new AST::PathInExpression (std::move (path))); |
| } |
| } |
| |
| // Parses struct pattern elements if they exist. |
| template <typename ManagedTokenSource> |
| AST::StructPatternElements |
| Parser<ManagedTokenSource>::parse_struct_pattern_elems () |
| { |
| std::vector<std::unique_ptr<AST::StructPatternField>> fields; |
| |
| AST::AttrVec etc_attrs; |
| bool has_rest = false; |
| |
| // try parsing struct pattern fields |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () != RIGHT_CURLY) |
| { |
| AST::AttrVec outer_attrs = parse_outer_attributes (); |
| |
| // parse etc (must be last in struct pattern, so breaks) |
| if (lexer.peek_token ()->get_id () == DOT_DOT) |
| { |
| lexer.skip_token (); |
| etc_attrs = std::move (outer_attrs); |
| has_rest = true; |
| break; |
| } |
| |
| std::unique_ptr<AST::StructPatternField> field |
| = parse_struct_pattern_field_partial (std::move (outer_attrs)); |
| if (field == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse struct pattern field"); |
| add_error (std::move (error)); |
| |
| // skip after somewhere? |
| return AST::StructPatternElements::create_empty (); |
| } |
| fields.push_back (std::move (field)); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| break; |
| |
| // skip comma |
| lexer.skip_token (); |
| t = lexer.peek_token (); |
| } |
| |
| if (has_rest) |
| return AST::StructPatternElements (std::move (fields), |
| std::move (etc_attrs)); |
| else |
| return AST::StructPatternElements (std::move (fields)); |
| } |
| |
| /* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or |
| * identifier). */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::StructPatternField> |
| Parser<ManagedTokenSource>::parse_struct_pattern_field () |
| { |
| // parse outer attributes (if they exist) |
| AST::AttrVec outer_attrs = parse_outer_attributes (); |
| |
| return parse_struct_pattern_field_partial (std::move (outer_attrs)); |
| } |
| |
| /* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or |
| * identifier), with outer attributes passed in. */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::StructPatternField> |
| Parser<ManagedTokenSource>::parse_struct_pattern_field_partial ( |
| AST::AttrVec outer_attrs) |
| { |
| // branch based on next token |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case INT_LITERAL: |
| { |
| // tuple index |
| std::string index_str = t->get_str (); |
| int index = atoi (index_str.c_str ()); |
| |
| lexer.skip_token (); |
| |
| if (!skip_token (COLON)) |
| { |
| return nullptr; |
| } |
| |
| // parse required pattern |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (pattern == nullptr) |
| { |
| Error error ( |
| t->get_locus (), |
| "failed to parse pattern in tuple index struct pattern field"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| return std::unique_ptr<AST::StructPatternFieldTuplePat> ( |
| new AST::StructPatternFieldTuplePat (index, std::move (pattern), |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| case IDENTIFIER: |
| // identifier-pattern OR only identifier |
| // branch on next token |
| switch (lexer.peek_token (1)->get_id ()) |
| { |
| case COLON: |
| { |
| // identifier-pattern |
| Identifier ident{t}; |
| lexer.skip_token (); |
| |
| skip_token (COLON); |
| |
| // parse required pattern |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (pattern == nullptr) |
| { |
| Error error (t->get_locus (), |
| "failed to parse pattern in struct pattern field"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| return std::unique_ptr<AST::StructPatternFieldIdentPat> ( |
| new AST::StructPatternFieldIdentPat (std::move (ident), |
| std::move (pattern), |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| case COMMA: |
| case RIGHT_CURLY: |
| { |
| // identifier only |
| Identifier ident = {t}; |
| lexer.skip_token (); |
| |
| return std::unique_ptr<AST::StructPatternFieldIdent> ( |
| new AST::StructPatternFieldIdent (std::move (ident), false, false, |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| default: |
| // error |
| add_error (Error (t->get_locus (), |
| "unrecognised token %qs in struct pattern field", |
| t->get_token_description ())); |
| |
| return nullptr; |
| } |
| case REF: |
| case MUT: |
| { |
| // only identifier |
| bool has_ref = false; |
| if (t->get_id () == REF) |
| { |
| has_ref = true; |
| lexer.skip_token (); |
| } |
| |
| bool has_mut = false; |
| if (lexer.peek_token ()->get_id () == MUT) |
| { |
| has_mut = true; |
| lexer.skip_token (); |
| } |
| |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); |
| if (ident_tok == nullptr) |
| { |
| return nullptr; |
| } |
| Identifier ident{ident_tok}; |
| |
| return std::unique_ptr<AST::StructPatternFieldIdent> ( |
| new AST::StructPatternFieldIdent (std::move (ident), has_ref, has_mut, |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| default: |
| // not necessarily an error |
| return nullptr; |
| } |
| } |
| |
| /* Parses a literal pattern or range pattern. Assumes that literals passed in |
| * are valid range pattern bounds. Do not pass in paths in expressions, for |
| * instance. */ |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::Pattern> |
| Parser<ManagedTokenSource>::parse_literal_or_range_pattern () |
| { |
| const_TokenPtr range_lower = lexer.peek_token (); |
| AST::Literal::LitType type = AST::Literal::STRING; |
| bool has_minus = false; |
| |
| // get lit type |
| switch (range_lower->get_id ()) |
| { |
| case CHAR_LITERAL: |
| type = AST::Literal::CHAR; |
| lexer.skip_token (); |
| break; |
| case BYTE_CHAR_LITERAL: |
| type = AST::Literal::BYTE; |
| lexer.skip_token (); |
| break; |
| case INT_LITERAL: |
| type = AST::Literal::INT; |
| lexer.skip_token (); |
| break; |
| case FLOAT_LITERAL: |
| type = AST::Literal::FLOAT; |
| lexer.skip_token (); |
| break; |
| case MINUS: |
| // branch on next token |
| range_lower = lexer.peek_token (1); |
| switch (range_lower->get_id ()) |
| { |
| case INT_LITERAL: |
| type = AST::Literal::INT; |
| has_minus = true; |
| lexer.skip_token (1); |
| break; |
| case FLOAT_LITERAL: |
| type = AST::Literal::FLOAT; |
| has_minus = true; |
| lexer.skip_token (1); |
| break; |
| default: |
| add_error (Error (range_lower->get_locus (), |
| "token type %qs cannot be parsed as range pattern " |
| "bound or literal after minus symbol", |
| range_lower->get_token_description ())); |
| |
| return nullptr; |
| } |
| break; |
| default: |
| add_error ( |
| Error (range_lower->get_locus (), |
| "token type %qs cannot be parsed as range pattern bound", |
| range_lower->get_token_description ())); |
| |
| return nullptr; |
| } |
| |
| const_TokenPtr next = lexer.peek_token (); |
| if (next->get_id () == DOT_DOT_EQ || next->get_id () == ELLIPSIS |
| || next->get_id () == DOT_DOT) |
| { |
| AST::RangeKind kind = AST::tokenid_to_rangekind (next->get_id ()); |
| // range pattern |
| lexer.skip_token (); |
| std::unique_ptr<AST::RangePatternBound> lower ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), type, |
| PrimitiveCoreType::CORETYPE_UNKNOWN), |
| range_lower->get_locus (), has_minus)); |
| |
| std::unique_ptr<AST::RangePatternBound> upper |
| = parse_range_pattern_bound (); |
| if (upper == nullptr) |
| { |
| Error error (next->get_locus (), |
| "failed to parse range pattern bound in range pattern"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| |
| return std::unique_ptr<AST::RangePattern> ( |
| new AST::RangePattern (std::move (lower), std::move (upper), kind, |
| range_lower->get_locus ())); |
| } |
| else |
| { |
| // literal pattern |
| return std::unique_ptr<AST::LiteralPattern> ( |
| new AST::LiteralPattern (range_lower->get_str (), type, |
| range_lower->get_locus (), |
| range_lower->get_type_hint (), has_minus)); |
| } |
| } |
| |
| // Parses a range pattern bound (value only). |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::RangePatternBound> |
| Parser<ManagedTokenSource>::parse_range_pattern_bound () |
| { |
| const_TokenPtr range_lower = lexer.peek_token (); |
| location_t range_lower_locus = range_lower->get_locus (); |
| |
| // get lit type |
| switch (range_lower->get_id ()) |
| { |
| case CHAR_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), AST::Literal::CHAR, |
| range_lower->get_type_hint ()), |
| range_lower_locus)); |
| case BYTE_CHAR_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), AST::Literal::BYTE, |
| range_lower->get_type_hint ()), |
| range_lower_locus)); |
| case INT_LITERAL: |
| lexer.skip_token (); |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), AST::Literal::INT, |
| range_lower->get_type_hint ()), |
| range_lower_locus)); |
| case FLOAT_LITERAL: |
| lexer.skip_token (); |
| rust_debug ("warning: used deprecated float range pattern bound"); |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), AST::Literal::FLOAT, |
| range_lower->get_type_hint ()), |
| range_lower_locus)); |
| case MINUS: |
| // branch on next token |
| range_lower = lexer.peek_token (1); |
| switch (range_lower->get_id ()) |
| { |
| case INT_LITERAL: |
| lexer.skip_token (1); |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), AST::Literal::INT, |
| range_lower->get_type_hint ()), |
| range_lower_locus, true)); |
| case FLOAT_LITERAL: |
| lexer.skip_token (1); |
| rust_debug ("warning: used deprecated float range pattern bound"); |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( |
| new AST::RangePatternBoundLiteral ( |
| AST::Literal (range_lower->get_str (), AST::Literal::FLOAT, |
| range_lower->get_type_hint ()), |
| range_lower_locus, true)); |
| default: |
| add_error (Error (range_lower->get_locus (), |
| "token type %qs cannot be parsed as range pattern " |
| "bound after minus symbol", |
| range_lower->get_token_description ())); |
| |
| return nullptr; |
| } |
| case IDENTIFIER: |
| case SUPER: |
| case SELF: |
| case SELF_ALIAS: |
| case CRATE: |
| case SCOPE_RESOLUTION: |
| case DOLLAR_SIGN: |
| { |
| // path in expression |
| AST::PathInExpression path = parse_path_in_expression (); |
| if (path.is_error ()) |
| { |
| Error error ( |
| range_lower->get_locus (), |
| "failed to parse path in expression range pattern bound"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| return std::unique_ptr<AST::RangePatternBoundPath> ( |
| new AST::RangePatternBoundPath (std::move (path))); |
| } |
| case LEFT_SHIFT: |
| case LEFT_ANGLE: |
| { |
| // qualified path in expression |
| AST::QualifiedPathInExpression path |
| = parse_qualified_path_in_expression (); |
| if (path.is_error ()) |
| { |
| Error error (range_lower->get_locus (), |
| "failed to parse qualified path in expression range " |
| "pattern bound"); |
| add_error (std::move (error)); |
| |
| return nullptr; |
| } |
| return std::unique_ptr<AST::RangePatternBoundQualPath> ( |
| new AST::RangePatternBoundQualPath (std::move (path))); |
| } |
| default: |
| add_error ( |
| Error (range_lower->get_locus (), |
| "token type %qs cannot be parsed as range pattern bound", |
| range_lower->get_token_description ())); |
| |
| return nullptr; |
| } |
| } |
| |
| } // namespace Rust |