blob: c46a630fb1dd06c7bc00ba35ddeddc3567cd891e [file] [log] [blame]
// 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