mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 20:01:31 -05:00
1254 lines
35 KiB
C++
1254 lines
35 KiB
C++
// 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>> 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
|