| // Copyright (C) 2025-2026 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>> sub_patterns; | |
| skip_token (LEFT_SQUARE); | |
| bool is_first = true; | |
| while (true) | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (!is_first && t->get_id () == COMMA) | |
| { | |
| skip_token (COMMA); | |
| t = lexer.peek_token (); | |
| } | |
| if (t->get_id () == RIGHT_SQUARE) | |
| { | |
| skip_token (RIGHT_SQUARE); | |
| 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 slice pattern"); | |
| add_error (std::move (error)); | |
| // TODO: skip until closing square bracket | |
| return nullptr; | |
| } | |
| sub_patterns.push_back (std::move (pattern)); | |
| is_first = false; | |
| } | |
| return std::make_unique<AST::SlicePattern> (std::move (sub_patterns), | |
| 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; | |
| } | |
| std::string literal_value; | |
| if (range_lower->get_id () == INT_LITERAL) | |
| literal_value = LiteralResolve::evaluate_integer_literal (range_lower); | |
| else if (range_lower->get_id () == FLOAT_LITERAL) | |
| literal_value = LiteralResolve::evaluate_float_literal (range_lower); | |
| else | |
| literal_value = range_lower->get_str (); | |
| 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 (std::move (literal_value), 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 | |
| auto type_hint = (range_lower->get_id () == INT_LITERAL | |
| || range_lower->get_id () == FLOAT_LITERAL) | |
| ? LiteralResolve::resolve_literal_suffix (range_lower) | |
| : range_lower->get_type_hint (); | |
| return std::unique_ptr<AST::LiteralPattern> ( | |
| new AST::LiteralPattern (std::move (literal_value), type, | |
| range_lower->get_locus (), 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 (LiteralResolve::evaluate_integer_literal (range_lower), | |
| AST::Literal::INT, | |
| LiteralResolve::resolve_literal_suffix (range_lower)), | |
| 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 (LiteralResolve::evaluate_float_literal (range_lower), | |
| AST::Literal::FLOAT, | |
| LiteralResolve::resolve_literal_suffix (range_lower)), | |
| 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 ( | |
| LiteralResolve::evaluate_integer_literal (range_lower), | |
| AST::Literal::INT, | |
| LiteralResolve::resolve_literal_suffix (range_lower)), | |
| 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 ( | |
| LiteralResolve::evaluate_float_literal (range_lower), | |
| AST::Literal::FLOAT, | |
| LiteralResolve::resolve_literal_suffix (range_lower)), | |
| 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 |