// 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
// .
/* 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 {
// Parses a block expression, including the curly braces at start and end.
template
tl::expected, Parse::Error::Node>
Parser::parse_block_expr (
AST::AttrVec outer_attrs, tl::optional label,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
if (!skip_token (LEFT_CURLY))
{
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
AST::AttrVec inner_attrs = parse_inner_attributes ();
// parse statements and expression
std::vector> stmts;
std::unique_ptr expr = nullptr;
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () != RIGHT_CURLY)
{
auto expr_or_stmt = parse_stmt_or_expr ();
if (!expr_or_stmt)
{
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
t = lexer.peek_token ();
if (expr_or_stmt->stmt != nullptr)
{
stmts.push_back (std::move (expr_or_stmt->stmt));
}
else
{
// assign to expression and end parsing inside
expr = std::move (expr_or_stmt->expr);
}
}
location_t end_locus = t->get_locus ();
if (!skip_token (RIGHT_CURLY))
{
// We don't need to throw an error as it already reported by skip_token
skip_after_end_block ();
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// grammar allows for empty block expressions
stmts.shrink_to_fit ();
return std::unique_ptr (
new AST::BlockExpr (std::move (stmts), std::move (expr),
std::move (inner_attrs), std::move (outer_attrs),
std::move (label), locus, end_locus));
}
/* Parse an anonymous const expression. This can be a regular const expression
* or an underscore for deferred const inference */
template
tl::expected
Parser::parse_anon_const ()
{
auto current = lexer.peek_token ();
auto locus = current->get_locus ();
// Special case deferred inference constants
if (maybe_skip_token (UNDERSCORE))
return AST::AnonConst (locus);
auto expr = parse_expr ();
if (!expr)
return tl::make_unexpected (Parse::Error::Node{});
return AST::AnonConst (std::move (expr.value ()), locus);
}
/* Parse a "const block", a block preceded by the `const` keyword whose
* statements can be const evaluated and used in constant contexts */
template
tl::expected, Parse::Error::Node>
Parser::parse_const_block_expr (AST::AttrVec outer_attrs,
location_t locus)
{
auto block_res = parse_block_expr ();
if (!block_res)
{
add_error (Error (locus, "failed to parse inner block in const block"));
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
auto block = std::move (block_res.value ());
auto block_locus = block->get_locus ();
return std::make_unique (AST::AnonConst (std::move (block),
block_locus),
locus, std::move (outer_attrs));
}
/* Parses a "grouped" expression (expression in parentheses), used to control
* precedence. */
template
tl::expected, Parse::Error::Node>
Parser::parse_grouped_expr (AST::AttrVec outer_attrs)
{
location_t locus = lexer.peek_token ()->get_locus ();
skip_token (LEFT_PAREN);
AST::AttrVec inner_attrs = parse_inner_attributes ();
// parse required expr inside parentheses
auto expr_in_parens = parse_expr ();
if (!expr_in_parens)
{
// skip after somewhere?
// error?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (RIGHT_PAREN))
{
// skip after somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
return std::unique_ptr (
new AST::GroupedExpr (std::move (expr_in_parens.value ()),
std::move (inner_attrs), std::move (outer_attrs),
locus));
}
// Parses a closure expression (closure definition).
template
tl::expected, Parse::Error::Node>
Parser::parse_closure_expr (AST::AttrVec outer_attrs)
{
location_t locus = lexer.peek_token ()->get_locus ();
// detect optional "move"
bool has_move = false;
if (lexer.peek_token ()->get_id () == MOVE)
{
lexer.skip_token ();
has_move = true;
}
// handle parameter list
std::vector params;
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case OR:
// skip token, no parameters
lexer.skip_token ();
break;
case PIPE:
// actually may have parameters
lexer.skip_token ();
t = lexer.peek_token ();
while (t->get_id () != PIPE)
{
AST::ClosureParam param = parse_closure_param ();
if (param.is_error ())
{
// TODO is this really an error?
Error error (t->get_locus (), "could not parse closure param");
add_error (std::move (error));
break;
}
params.push_back (std::move (param));
if (lexer.peek_token ()->get_id () != COMMA)
{
lexer.skip_token ();
// not an error but means param list is done
break;
}
// skip comma
lexer.skip_token ();
t = lexer.peek_token ();
}
params.shrink_to_fit ();
break;
default:
add_error (Error (t->get_locus (),
"unexpected token %qs in closure expression - expected "
"%<|%> or %<||%>",
t->get_token_description ()));
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// again branch based on next token
t = lexer.peek_token ();
if (t->get_id () == RETURN_TYPE)
{
// must be return type closure with block expr
// skip "return type" token
lexer.skip_token ();
// parse actual type, which is required
std::unique_ptr type = parse_type_no_bounds ();
if (type == nullptr)
{
// error
Error error (t->get_locus (), "failed to parse type for closure");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
// parse block expr, which is required
auto block = parse_block_expr ();
if (!block)
{
// error
Error error (lexer.peek_token ()->get_locus (),
"failed to parse block expr in closure");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::ClosureExprInnerTyped (std::move (type),
std::move (block.value ()),
std::move (params), locus, has_move,
std::move (outer_attrs)));
}
else
{
// must be expr-only closure
// parse expr, which is required
auto expr = parse_expr ();
if (!expr)
{
Error error (t->get_locus (),
"failed to parse expression in closure");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::ClosureExprInner (std::move (expr.value ()),
std::move (params), locus, has_move,
std::move (outer_attrs)));
}
}
// Parses a literal token (to literal expression).
template
tl::expected, Parse::Error::Node>
Parser::parse_literal_expr (AST::AttrVec outer_attrs)
{
// TODO: change if literal representation in lexer changes
std::string literal_value;
AST::Literal::LitType type = AST::Literal::STRING;
// branch based on token
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case CHAR_LITERAL:
type = AST::Literal::CHAR;
literal_value = t->get_str ();
lexer.skip_token ();
break;
case STRING_LITERAL:
type = AST::Literal::STRING;
literal_value = t->get_str ();
lexer.skip_token ();
break;
case BYTE_CHAR_LITERAL:
type = AST::Literal::BYTE;
literal_value = t->get_str ();
lexer.skip_token ();
break;
case BYTE_STRING_LITERAL:
type = AST::Literal::BYTE_STRING;
literal_value = t->get_str ();
lexer.skip_token ();
break;
case RAW_STRING_LITERAL:
type = AST::Literal::RAW_STRING;
literal_value = t->get_str ();
lexer.skip_token ();
break;
case INT_LITERAL:
type = AST::Literal::INT;
literal_value = t->get_str ();
lexer.skip_token ();
break;
case FLOAT_LITERAL:
type = AST::Literal::FLOAT;
literal_value = t->get_str ();
lexer.skip_token ();
break;
// case BOOL_LITERAL
// use true and false keywords rather than "bool literal" Rust terminology
case TRUE_LITERAL:
type = AST::Literal::BOOL;
literal_value = Values::Keywords::TRUE_LITERAL;
lexer.skip_token ();
break;
case FALSE_LITERAL:
type = AST::Literal::BOOL;
literal_value = Values::Keywords::FALSE_LITERAL;
lexer.skip_token ();
break;
default:
// error - cannot be a literal expr
add_error (Error (t->get_locus (),
"unexpected token %qs when parsing literal expression",
t->get_token_description ()));
// skip?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// create literal based on stuff in switch
return std::unique_ptr (
new AST::LiteralExpr (std::move (literal_value), std::move (type),
t->get_type_hint (), std::move (outer_attrs),
t->get_locus ()));
}
template
tl::expected, Parse::Error::Node>
Parser::parse_box_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (BOX);
}
ParseRestrictions restrictions;
restrictions.expr_can_be_null = false;
auto expr = parse_expr (AST::AttrVec (), restrictions);
if (!expr)
return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
return std::unique_ptr (
new AST::BoxExpr (std::move (expr.value ()), std::move (outer_attrs),
locus));
}
// Parses a return expression (including any expression to return).
template
tl::expected, Parse::Error::Node>
Parser::parse_return_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (RETURN_KW);
}
// parse expression to return, if it exists
ParseRestrictions restrictions;
restrictions.expr_can_be_null = true;
auto returned_expr = parse_expr (AST::AttrVec (), restrictions);
tl::optional> expr = tl::nullopt;
if (returned_expr)
expr = std::move (returned_expr.value ());
return std::make_unique (std::move (expr),
std::move (outer_attrs), locus);
}
// Parses a try expression.
template
tl::expected, Parse::Error::Node>
Parser::parse_try_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (TRY);
}
auto block_expr = parse_block_expr ();
if (!block_expr)
return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
return std::unique_ptr (
new AST::TryExpr (std::move (block_expr.value ()), std::move (outer_attrs),
locus));
}
/* Parses a break expression (including any label to break to AND any return
* expression). */
template
tl::expected, Parse::Error::Node>
Parser::parse_break_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (BREAK);
}
auto parsed_label = parse_lifetime (false);
auto label = (parsed_label)
? tl::optional (parsed_label.value ())
: tl::nullopt;
// parse break return expression if it exists
ParseRestrictions restrictions;
restrictions.expr_can_be_null = true;
auto return_expr = parse_expr (AST::AttrVec (), restrictions);
if (return_expr)
return std::unique_ptr (
new AST::BreakExpr (std::move (label), std::move (return_expr.value ()),
std::move (outer_attrs), locus));
else if (return_expr.error () == Parse::Error::Expr::NULL_EXPR)
return std::unique_ptr (
new AST::BreakExpr (std::move (label), tl::nullopt,
std::move (outer_attrs), locus));
else
return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
}
// Parses a continue expression (including any label to continue from).
template
std::unique_ptr
Parser::parse_continue_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (CONTINUE);
}
auto parsed_label = parse_lifetime (false);
auto label = (parsed_label)
? tl::optional (parsed_label.value ())
: tl::nullopt;
return std::make_unique (std::move (label),
std::move (outer_attrs), locus);
}
/* Parses an if expression of any kind, including with else, else if, else if
* let, and neither. Note that any outer attributes will be ignored because if
* expressions don't support them. */
template
tl::expected, Parse::Error::Node>
Parser::parse_if_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
// TODO: make having outer attributes an error?
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
if (!skip_token (IF))
{
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
// detect accidental if let
if (lexer.peek_token ()->get_id () == LET)
{
Error error (lexer.peek_token ()->get_locus (),
"if let expression probably exists, but is being parsed "
"as an if expression. This may be a parser error");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
/* parse required condition expr - HACK to prevent struct expr from being
* parsed */
ParseRestrictions no_struct_expr;
no_struct_expr.can_be_struct_expr = false;
auto condition = parse_expr ({}, no_struct_expr);
if (!condition)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse condition expression in if expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
// parse required block expr
auto if_body = parse_block_expr ();
if (!if_body)
return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
// branch to parse end or else (and then else, else if, or else if let)
if (lexer.peek_token ()->get_id () != ELSE)
{
// single selection - end of if expression
return std::unique_ptr (
new AST::IfExpr (std::move (condition.value ()),
std::move (if_body.value ()), std::move (outer_attrs),
locus));
}
else
{
// double or multiple selection - branch on end, else if, or else if let
// skip "else"
lexer.skip_token ();
// branch on whether next token is '{' or 'if'
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case LEFT_CURLY:
{
// double selection - else
// parse else block expr (required)
auto else_body = parse_block_expr ();
if (!else_body)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse else body block expression in "
"if expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::IfExprConseqElse (std::move (condition.value ()),
std::move (if_body.value ()),
std::move (else_body.value ()),
std::move (outer_attrs), locus));
}
case IF:
{
// multiple selection - else if or else if let
// branch on whether next token is 'let' or not
if (lexer.peek_token (1)->get_id () == LET)
{
// parse if let expr (required)
auto if_let_expr = parse_if_let_expr ();
if (!if_let_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse (else) if let expression "
"after if expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::IfExprConseqElse (std::move (condition.value ()),
std::move (if_body.value ()),
std::move (if_let_expr.value ()),
std::move (outer_attrs), locus));
}
else
{
// parse if expr (required)
auto if_expr = parse_if_expr ();
if (!if_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse (else) if expression after "
"if expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::IfExprConseqElse (std::move (condition.value ()),
std::move (if_body.value ()),
std::move (if_expr.value ()),
std::move (outer_attrs), locus));
}
}
default:
// error - invalid token
add_error (Error (t->get_locus (),
"unexpected token %qs after else in if expression",
t->get_token_description ()));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
}
/* Parses an if let expression of any kind, including with else, else if, else
* if let, and none. Note that any outer attributes will be ignored as if let
* expressions don't support them. */
template
tl::expected, Parse::Error::Node>
Parser::parse_if_let_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
// TODO: make having outer attributes an error?
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
if (!skip_token (IF))
{
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
// detect accidental if expr parsed as if let expr
if (lexer.peek_token ()->get_id () != LET)
{
Error error (lexer.peek_token ()->get_locus (),
"if expression probably exists, but is being parsed as an "
"if let expression. This may be a parser error");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
lexer.skip_token ();
// parse match arm patterns (which are required)
std::unique_ptr match_arm_pattern
= parse_match_arm_pattern (EQUAL);
if (match_arm_pattern == nullptr)
{
Error error (
lexer.peek_token ()->get_locus (),
"failed to parse any match arm patterns in if let expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (EQUAL))
{
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// parse expression (required) - HACK to prevent struct expr being parsed
ParseRestrictions no_struct_expr;
no_struct_expr.can_be_struct_expr = false;
auto scrutinee_expr = parse_expr ({}, no_struct_expr);
if (!scrutinee_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse scrutinee expression in if let expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
/* TODO: check for expression not being a struct expression or lazy boolean
* expression here? or actually probably in semantic analysis. */
// parse block expression (required)
auto if_let_body = parse_block_expr ();
if (!if_let_body)
{
Error error (
lexer.peek_token ()->get_locus (),
"failed to parse if let body block expression in if let expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
// branch to parse end or else (and then else, else if, or else if let)
if (lexer.peek_token ()->get_id () != ELSE)
{
// single selection - end of if let expression
return std::unique_ptr (new AST::IfLetExpr (
std::move (match_arm_pattern), std::move (scrutinee_expr.value ()),
std::move (if_let_body.value ()), std::move (outer_attrs), locus));
}
else
{
// double or multiple selection - branch on end, else if, or else if let
// skip "else"
lexer.skip_token ();
// branch on whether next token is '{' or 'if'
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case LEFT_CURLY:
{
// double selection - else
// parse else block expr (required)
auto else_body = parse_block_expr ();
if (!else_body)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse else body block expression in "
"if let expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::IfLetExprConseqElse (std::move (match_arm_pattern),
std::move (scrutinee_expr.value ()),
std::move (if_let_body.value ()),
std::move (else_body.value ()),
std::move (outer_attrs), locus));
}
case IF:
{
// multiple selection - else if or else if let
// branch on whether next token is 'let' or not
if (lexer.peek_token (1)->get_id () == LET)
{
// parse if let expr (required)
auto if_let_expr = parse_if_let_expr ();
if (!if_let_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse (else) if let expression "
"after if let expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::IfLetExprConseqElse (
std::move (match_arm_pattern),
std::move (scrutinee_expr.value ()),
std::move (if_let_body.value ()),
std::move (if_let_expr.value ()), std::move (outer_attrs),
locus));
}
else
{
// parse if expr (required)
auto if_expr = parse_if_expr ();
if (!if_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse (else) if expression after "
"if let expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::IfLetExprConseqElse (
std::move (match_arm_pattern),
std::move (scrutinee_expr.value ()),
std::move (if_let_body.value ()),
std::move (if_expr.value ()), std::move (outer_attrs),
locus));
}
}
default:
// error - invalid token
add_error (
Error (t->get_locus (),
"unexpected token %qs after else in if let expression",
t->get_token_description ()));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
}
/* TODO: possibly decide on different method of handling label (i.e. not
* parameter) */
/* Parses a "loop" infinite loop expression. Label is not parsed and should be
* parsed via parse_labelled_loop_expr, which would call this. */
template
tl::expected, Parse::Error::Node>
Parser::parse_loop_expr (AST::AttrVec outer_attrs,
tl::optional label,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
if (label)
locus = label->get_locus ();
else
locus = lexer.peek_token ()->get_locus ();
if (!skip_token (LOOP))
{
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
else
{
if (label)
locus = label->get_locus ();
}
// parse loop body, which is required
auto loop_body = parse_block_expr ();
if (!loop_body)
return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
return std::unique_ptr (
new AST::LoopExpr (std::move (loop_body.value ()), locus, std::move (label),
std::move (outer_attrs)));
}
/* Parses a "while" loop expression. Label is not parsed and should be parsed
* via parse_labelled_loop_expr, which would call this. */
template
tl::expected, Parse::Error::Node>
Parser::parse_while_loop_expr (
AST::AttrVec outer_attrs, tl::optional label,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
if (label)
locus = label->get_locus ();
else
locus = lexer.peek_token ()->get_locus ();
if (!skip_token (WHILE))
{
skip_after_end_block ();
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
else
{
if (label)
locus = label->get_locus ();
}
// ensure it isn't a while let loop
if (lexer.peek_token ()->get_id () == LET)
{
Error error (lexer.peek_token ()->get_locus (),
"appears to be while let loop but is being parsed by "
"while loop - this may be a compiler issue");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// parse loop predicate (required) with HACK to prevent struct expr parsing
ParseRestrictions no_struct_expr;
no_struct_expr.can_be_struct_expr = false;
auto predicate = parse_expr ({}, no_struct_expr);
if (!predicate)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse predicate expression in while loop");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
/* TODO: check that it isn't struct expression here? actually, probably in
* semantic analysis */
// parse loop body (required)
auto body = parse_block_expr ();
if (!body)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse loop body block expression in while loop");
add_error (std::move (error));
// skip somewhere
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::WhileLoopExpr (std::move (predicate.value ()),
std::move (body.value ()), locus, std::move (label),
std::move (outer_attrs)));
}
/* Parses a "while let" loop expression. Label is not parsed and should be
* parsed via parse_labelled_loop_expr, which would call this. */
template
tl::expected, Parse::Error::Node>
Parser::parse_while_let_loop_expr (
AST::AttrVec outer_attrs, tl::optional label)
{
location_t locus = UNKNOWN_LOCATION;
if (label)
locus = label->get_locus ();
else
locus = lexer.peek_token ()->get_locus ();
maybe_skip_token (WHILE);
/* check for possible accidental recognition of a while loop as a while let
* loop */
if (lexer.peek_token ()->get_id () != LET)
{
Error error (lexer.peek_token ()->get_locus (),
"appears to be a while loop but is being parsed by "
"while let loop - this may be a compiler issue");
add_error (std::move (error));
// skip somewhere
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// as this token is definitely let now, save the computation of comparison
lexer.skip_token ();
// parse predicate patterns
std::unique_ptr predicate_pattern
= parse_match_arm_pattern (EQUAL);
// ensure that there is at least 1 pattern
if (predicate_pattern == nullptr)
{
Error error (lexer.peek_token ()->get_locus (),
"should be at least 1 pattern");
add_error (std::move (error));
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
if (!skip_token (EQUAL))
{
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
/* parse predicate expression, which is required (and HACK to prevent struct
* expr) */
ParseRestrictions no_struct_expr;
no_struct_expr.can_be_struct_expr = false;
auto predicate_expr = parse_expr ({}, no_struct_expr);
if (!predicate_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse predicate expression in while let loop");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
/* TODO: ensure that struct expression is not parsed? Actually, probably in
* semantic analysis. */
// parse loop body, which is required
auto body = parse_block_expr ();
if (!body)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse block expr (loop body) of while let loop");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::WhileLetLoopExpr (std::move (predicate_pattern),
std::move (predicate_expr.value ()),
std::move (body.value ()), locus,
std::move (label), std::move (outer_attrs)));
}
/* Parses a "for" iterative loop. Label is not parsed and should be parsed via
* parse_labelled_loop_expr, which would call this. */
template
tl::expected, Parse::Error::Node>
Parser::parse_for_loop_expr (
AST::AttrVec outer_attrs, tl::optional label)
{
location_t locus = UNKNOWN_LOCATION;
if (label)
locus = label->get_locus ();
else
locus = lexer.peek_token ()->get_locus ();
maybe_skip_token (FOR);
// parse pattern, which is required
std::unique_ptr pattern = parse_pattern ();
if (!pattern)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse iterator pattern in for loop");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (IN))
{
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
/* parse iterator expression, which is required - also HACK to prevent
* struct expr */
ParseRestrictions no_struct_expr;
no_struct_expr.can_be_struct_expr = false;
auto expr = parse_expr ({}, no_struct_expr);
if (!expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse iterator expression in for loop");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
// TODO: check to ensure this isn't struct expr? Or in semantic analysis.
// parse loop body, which is required
auto body = parse_block_expr ();
if (!body)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse loop body block expression in for loop");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::ForLoopExpr (std::move (pattern), std::move (expr.value ()),
std::move (body.value ()), locus, std::move (label),
std::move (outer_attrs)));
}
// Parses a loop expression with label (any kind of loop - disambiguates).
template
tl::expected, Parse::Error::Node>
Parser::parse_labelled_loop_expr (const_TokenPtr tok,
AST::AttrVec outer_attrs)
{
// parse loop label (required)
auto parsed_label = parse_loop_label (tok);
if (!parsed_label)
{
/* TODO: decide whether it should not work if there is no label, or parse
* it with no label at the moment, I will make it not work with no label
* because that's the implication. */
if (parsed_label.error ().kind
== Parse::Error::LoopLabel::Kind::NOT_LOOP_LABEL)
{
Error error (tok->get_locus (),
"expected lifetime in labelled loop expr (to parse loop "
"label) - found %qs",
tok->get_token_description ());
add_error (std::move (error));
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
else
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse loop label in labelled loop expr");
add_error (std::move (error));
// skip?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
}
auto label = parsed_label
? tl::optional (parsed_label.value ())
: tl::nullopt;
// branch on next token
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case LOOP:
return parse_loop_expr (std::move (outer_attrs), std::move (label));
case FOR:
return parse_for_loop_expr (std::move (outer_attrs), std::move (label));
case WHILE:
// further disambiguate into while vs while let
if (lexer.peek_token (1)->get_id () == LET)
return parse_while_let_loop_expr (std::move (outer_attrs),
std::move (label));
else
return parse_while_loop_expr (std::move (outer_attrs),
std::move (label));
case LEFT_CURLY:
return parse_block_expr (std::move (outer_attrs), std::move (label));
default:
// error
add_error (Error (t->get_locus (),
"unexpected token %qs when parsing labelled loop",
t->get_token_description ()));
// skip?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
}
// Parses a match expression.
template
tl::expected, Parse::Error::Node>
Parser::parse_match_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (MATCH_KW);
}
/* parse scrutinee expression, which is required (and HACK to prevent struct
* expr) */
ParseRestrictions no_struct_expr;
no_struct_expr.can_be_struct_expr = false;
auto scrutinee = parse_expr ({}, no_struct_expr);
if (!scrutinee)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse scrutinee expression in match expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
/* TODO: check for scrutinee expr not being struct expr? or do so in
* semantic analysis */
if (!skip_token (LEFT_CURLY))
{
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
// parse inner attributes (if they exist)
AST::AttrVec inner_attrs = parse_inner_attributes ();
// parse match arms (if they exist)
// std::vector > match_arms;
std::vector match_arms;
// parse match cases
while (lexer.peek_token ()->get_id () != RIGHT_CURLY)
{
// parse match arm itself, which is required
AST::MatchArm arm = parse_match_arm ();
if (arm.is_error ())
{
// TODO is this worth throwing everything away?
Error error (lexer.peek_token ()->get_locus (),
"failed to parse match arm in match arms");
add_error (std::move (error));
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (MATCH_ARROW))
{
// skip after somewhere?
// TODO is returning here a good idea? or is break better?
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
ParseRestrictions restrictions;
restrictions.expr_can_be_stmt = true;
auto expr = parse_expr ({}, restrictions);
if (!expr)
{
/* We don't need to throw an error as it already reported by
* parse_expr
*/
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
bool is_expr_without_block = expr.value ()->is_expr_without_block ();
match_arms.push_back (
AST::MatchCase (std::move (arm), std::move (expr.value ())));
// handle comma presence
if (lexer.peek_token ()->get_id () != COMMA)
{
if (!is_expr_without_block)
{
// allowed even if not final case
continue;
}
else if (is_expr_without_block
&& lexer.peek_token ()->get_id () != RIGHT_CURLY)
{
// not allowed if not final case
Error error (lexer.peek_token ()->get_locus (),
"exprwithoutblock requires comma after match case "
"expression in match arm (if not final case)");
add_error (std::move (error));
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
else
{
// otherwise, must be final case, so fine
break;
}
}
lexer.skip_token ();
}
if (!skip_token (RIGHT_CURLY))
{
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
match_arms.shrink_to_fit ();
return std::unique_ptr (
new AST::MatchExpr (std::move (scrutinee.value ()), std::move (match_arms),
std::move (inner_attrs), std::move (outer_attrs),
locus));
}
// Parses an async block expression.
template
tl::expected, Parse::Error::Node>
Parser::parse_async_block_expr (AST::AttrVec outer_attrs)
{
location_t locus = lexer.peek_token ()->get_locus ();
skip_token (ASYNC);
// detect optional move token
bool has_move = false;
if (lexer.peek_token ()->get_id () == MOVE)
{
lexer.skip_token ();
has_move = true;
}
// parse block expression (required)
auto block_expr = parse_block_expr ();
if (!block_expr)
{
Error error (
lexer.peek_token ()->get_locus (),
"failed to parse block expression of async block expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::AsyncBlockExpr (std::move (block_expr.value ()), has_move,
std::move (outer_attrs), locus));
}
// Parses an unsafe block expression.
template
tl::expected, Parse::Error::Node>
Parser::parse_unsafe_block_expr (
AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (UNSAFE);
}
// parse block expression (required)
auto block_expr = parse_block_expr ();
if (!block_expr)
{
Error error (
lexer.peek_token ()->get_locus (),
"failed to parse block expression of unsafe block expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr (
new AST::UnsafeBlockExpr (std::move (block_expr.value ()),
std::move (outer_attrs), locus));
}
// Parses an array definition expression.
template
tl::expected, Parse::Error::Node>
Parser::parse_array_expr (AST::AttrVec outer_attrs,
location_t pratt_parsed_loc)
{
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (LEFT_SQUARE);
}
// parse optional inner attributes
AST::AttrVec inner_attrs = parse_inner_attributes ();
// parse the "array elements" section, which is optional
if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
{
// no array elements
lexer.skip_token ();
std::vector> exprs;
auto array_elems
= std::make_unique (std::move (exprs), locus);
return std::make_unique (std::move (array_elems),
std::move (inner_attrs),
std::move (outer_attrs), locus);
}
else
{
// should have array elements
// parse initial expression, which is required for either
auto initial_expr = parse_expr ();
if (!initial_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"could not parse expression in array expression "
"(even though arrayelems seems to be present)");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
if (lexer.peek_token ()->get_id () == SEMICOLON)
{
// copy array elems
lexer.skip_token ();
// parse copy amount expression (required)
auto copy_amount = parse_expr ();
if (!copy_amount)
{
Error error (lexer.peek_token ()->get_locus (),
"could not parse copy amount expression in array "
"expression (arrayelems)");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
skip_token (RIGHT_SQUARE);
std::unique_ptr copied_array_elems (
new AST::ArrayElemsCopied (std::move (initial_expr.value ()),
std::move (copy_amount.value ()),
locus));
return std::unique_ptr (
new AST::ArrayExpr (std::move (copied_array_elems),
std::move (inner_attrs),
std::move (outer_attrs), locus));
}
else if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
{
// single-element array expression
std::vector> exprs;
exprs.reserve (1);
exprs.push_back (std::move (initial_expr.value ()));
exprs.shrink_to_fit ();
skip_token (RIGHT_SQUARE);
std::unique_ptr array_elems (
new AST::ArrayElemsValues (std::move (exprs), locus));
return std::unique_ptr (
new AST::ArrayExpr (std::move (array_elems),
std::move (inner_attrs),
std::move (outer_attrs), locus));
}
else if (lexer.peek_token ()->get_id () == COMMA)
{
// multi-element array expression (or trailing comma)
std::vector> exprs;
exprs.push_back (std::move (initial_expr.value ()));
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () == COMMA)
{
lexer.skip_token ();
// quick break if right square bracket
if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
break;
// parse expression (required)
auto expr = parse_expr ();
if (!expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse element in array expression");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
exprs.push_back (std::move (expr.value ()));
t = lexer.peek_token ();
}
skip_token (RIGHT_SQUARE);
exprs.shrink_to_fit ();
std::unique_ptr array_elems (
new AST::ArrayElemsValues (std::move (exprs), locus));
return std::unique_ptr (
new AST::ArrayExpr (std::move (array_elems),
std::move (inner_attrs),
std::move (outer_attrs), locus));
}
else
{
// error
Error error (lexer.peek_token ()->get_locus (),
"unexpected token %qs in array expression (arrayelems)",
lexer.peek_token ()->get_token_description ());
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::MALFORMED);
}
}
}
// Parses a grouped or tuple expression (disambiguates).
template
tl::expected, Parse::Error::Node>
Parser::parse_grouped_or_tuple_expr (
AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
{
// adjustment to allow Pratt parsing to reuse function without copy-paste
location_t locus = pratt_parsed_loc;
if (locus == UNKNOWN_LOCATION)
{
locus = lexer.peek_token ()->get_locus ();
skip_token (LEFT_PAREN);
}
// parse optional inner attributes
AST::AttrVec inner_attrs = parse_inner_attributes ();
if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
{
// must be empty tuple
lexer.skip_token ();
// create tuple with empty tuple elems
return std::unique_ptr (
new AST::TupleExpr (std::vector> (),
std::move (inner_attrs), std::move (outer_attrs),
locus));
}
// parse first expression (required)
auto first_expr = parse_expr ();
if (!first_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse expression in grouped or tuple expression");
add_error (std::move (error));
// skip after somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
// detect whether grouped expression with right parentheses as next token
if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
{
// must be grouped expr
lexer.skip_token ();
// create grouped expr
return std::unique_ptr (
new AST::GroupedExpr (std::move (first_expr.value ()),
std::move (inner_attrs), std::move (outer_attrs),
locus));
}
else if (lexer.peek_token ()->get_id () == COMMA)
{
// tuple expr
std::vector> exprs;
exprs.push_back (std::move (first_expr.value ()));
// parse potential other tuple exprs
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () == COMMA)
{
lexer.skip_token ();
// break out if right paren
if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
break;
// parse expr, which is now required
auto expr = parse_expr ();
if (!expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse expr in tuple expr");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (
Parse::Error::Node::CHILD_ERROR);
}
exprs.push_back (std::move (expr.value ()));
t = lexer.peek_token ();
}
// skip right paren
skip_token (RIGHT_PAREN);
return std::unique_ptr (
new AST::TupleExpr (std::move (exprs), std::move (inner_attrs),
std::move (outer_attrs), locus));
}
else
{
// error
const_TokenPtr t = lexer.peek_token ();
Error error (t->get_locus (),
"unexpected token %qs in grouped or tuple expression "
"(parenthesised expression) - expected %<)%> for grouped "
"expr and %<,%> for tuple expr",
t->get_token_description ());
add_error (std::move (error));
// skip somewhere?
return tl::unexpected (Parse::Error::Node::MALFORMED);
}
}
// Parses a struct expression field.
template
tl::expected,
Parse::Error::StructExprField>
Parser::parse_struct_expr_field ()
{
AST::AttrVec outer_attrs = parse_outer_attributes ();
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case IDENTIFIER:
if (lexer.peek_token (1)->get_id () == COLON)
{
// struct expr field with identifier and expr
Identifier ident = {t};
lexer.skip_token (1);
// parse expression (required)
auto expr = parse_expr ();
if (!expr)
{
Error error (t->get_locus (),
"failed to parse struct expression field with "
"identifier and expression");
add_error (std::move (error));
return tl::unexpected (
Parse::Error::StructExprField::CHILD_ERROR);
}
return std::unique_ptr (
new AST::StructExprFieldIdentifierValue (std::move (ident),
std::move (expr.value ()),
std::move (outer_attrs),
t->get_locus ()));
}
else
{
// struct expr field with identifier only
Identifier ident{t};
lexer.skip_token ();
return std::unique_ptr (
new AST::StructExprFieldIdentifier (std::move (ident),
std::move (outer_attrs),
t->get_locus ()));
}
case INT_LITERAL:
{
// parse tuple index field
int index = atoi (t->get_str ().c_str ());
lexer.skip_token ();
if (!skip_token (COLON))
{
// skip somewhere?
return tl::unexpected (
Parse::Error::StructExprField::MALFORMED);
}
// parse field expression (required)
auto expr = parse_expr ();
if (!expr)
{
Error error (t->get_locus (),
"failed to parse expr in struct (or enum) expr "
"field with tuple index");
add_error (std::move (error));
return tl::unexpected (
Parse::Error::StructExprField::CHILD_ERROR);
}
return std::unique_ptr (
new AST::StructExprFieldIndexValue (index, std::move (expr.value ()),
std::move (outer_attrs),
t->get_locus ()));
}
case DOT_DOT:
/* this is a struct base and can't be parsed here, so just return
* nothing without erroring */
return tl::unexpected (
Parse::Error::StructExprField::STRUCT_BASE);
default:
add_error (
Error (t->get_locus (),
"unrecognised token %qs as first token of struct expr field - "
"expected identifier or integer literal",
t->get_token_description ()));
return tl::unexpected (
Parse::Error::StructExprField::MALFORMED);
}
}
/* Pratt parser impl of parse_expr. FIXME: this is only provisional and
* probably will be changed. */
template
tl::expected, Parse::Error::Expr>
Parser::parse_expr (int right_binding_power,
AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
const_TokenPtr current_token = lexer.peek_token ();
// Special hack because we are allowed to return nullptr, in that case we
// don't want to skip the token, since we don't actually parse it. But if
// null isn't allowed it indicates an error, and we want to skip past that.
// So return early if it is one of the tokens that ends an expression
// (or at least cannot start a new expression).
if (restrictions.expr_can_be_null)
{
TokenId id = current_token->get_id ();
if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY
|| id == RIGHT_SQUARE || id == COMMA || id == LEFT_CURLY)
return tl::unexpected (
Parse::Error::Expr::NULL_EXPR);
}
ParseRestrictions null_denotation_restrictions = restrictions;
null_denotation_restrictions.expr_can_be_stmt = false;
// parse null denotation (unary part of expression)
tl::expected, Parse::Error::Expr> expr
= null_denotation ({}, null_denotation_restrictions);
if (!expr)
return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
if (expr.value () == nullptr)
return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
return left_denotations (std::move (expr), right_binding_power,
std::move (outer_attrs), restrictions);
}
// Parse expression with lowest left binding power.
template
tl::expected, Parse::Error::Expr>
Parser::parse_expr (AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
}
template
tl::expected, Parse::Error::Expr>
Parser::left_denotations (
tl::expected, Parse::Error::Expr> expr,
int right_binding_power, AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
if (!expr)
{
// DEBUG
rust_debug ("null denotation is null; returning null for parse_expr");
return tl::unexpected (
Parse::Error::Expr::NULL_DENOTATION);
}
const_TokenPtr current_token = lexer.peek_token ();
if (restrictions.expr_can_be_stmt && !expr.value ()->is_expr_without_block ()
&& current_token->get_id () != DOT
&& current_token->get_id () != QUESTION_MARK)
{
rust_debug ("statement expression with block");
expr.value ()->set_outer_attrs (std::move (outer_attrs));
return expr;
}
restrictions.expr_can_be_stmt = false;
// stop parsing if find lower priority token - parse higher priority first
while (right_binding_power < left_binding_power (current_token))
{
lexer.skip_token ();
// FIXME attributes should generally be applied to the null denotation.
expr = left_denotation (current_token, std::move (expr.value ()),
std::move (outer_attrs), restrictions);
if (!expr)
{
// DEBUG
rust_debug ("left denotation is null; returning null for parse_expr");
return tl::unexpected (
Parse::Error::Expr::LEFT_DENOTATION);
}
current_token = lexer.peek_token ();
}
return expr;
}
/* Determines action to take when finding token at beginning of expression. */
template
tl::expected, Parse::Error::Expr>
Parser::null_denotation (AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
/* note: tok is previous character in input stream, not current one, as
* parse_expr skips it before passing it in */
/* as a Pratt parser (which works by decomposing expressions into a null
* denotation and then a left denotation), null denotations handle primaries
* and unary operands (but only prefix unary operands) */
auto tok = lexer.peek_token ();
switch (tok->get_id ())
{
case IDENTIFIER:
case SELF:
case SELF_ALIAS:
case DOLLAR_SIGN:
case CRATE:
case SUPER:
case SCOPE_RESOLUTION:
{
// DEBUG
rust_debug ("beginning null denotation identifier handling");
/* best option: parse as path, then extract identifier, macro,
* struct/enum, or just path info from it */
AST::PathInExpression path = parse_path_in_expression ();
return null_denotation_path (std::move (path), std::move (outer_attrs),
restrictions);
}
case HASH:
{
// Parse outer attributes and then the expression that follows
AST::AttrVec attrs = parse_outer_attributes ();
// Merge with any existing outer attributes
if (!outer_attrs.empty ())
attrs.insert (attrs.begin (), outer_attrs.begin (),
outer_attrs.end ());
// Try to parse the expression that should follow the attributes
auto expr = parse_expr (std::move (attrs), restrictions);
if (!expr)
{
/* If parsing failed and we're at a semicolon, provide a better
* error
*/
const_TokenPtr next_tok = lexer.peek_token ();
if (next_tok->get_id () == SEMICOLON)
add_error (Error (next_tok->get_locus (),
"expected expression, found %<;%>"));
return tl::unexpected (
Parse::Error::Expr::CHILD_ERROR);
}
return expr;
}
default:
if (tok->get_id () == LEFT_SHIFT)
{
lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
tok = lexer.peek_token ();
}
lexer.skip_token ();
return null_denotation_not_path (std::move (tok), std::move (outer_attrs),
restrictions);
}
}
// Handling of expresions that start with a path for `null_denotation`.
template
tl::expected, Parse::Error::Expr>
Parser::null_denotation_path (
AST::PathInExpression path, AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
rust_debug ("parsing null denotation after path");
// HACK: always make "self" by itself a path (regardless of next
// tokens)
if (path.is_single_segment () && path.get_segments ()[0].is_lower_self_seg ())
{
// HACK: add outer attrs to path
path.set_outer_attrs (std::move (outer_attrs));
return std::make_unique (std::move (path));
}
// branch on next token
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case EXCLAM:
{
// macro
auto macro = parse_macro_invocation_partial (std::move (path),
std::move (outer_attrs));
if (macro == nullptr)
return tl::unexpected (
Parse::Error::Expr::CHILD_ERROR);
return std::unique_ptr (std::move (macro));
}
case LEFT_CURLY:
{
bool not_a_block = lexer.peek_token (1)->get_id () == IDENTIFIER
&& (lexer.peek_token (2)->get_id () == COMMA
|| (lexer.peek_token (2)->get_id () == COLON
&& (lexer.peek_token (4)->get_id () == COMMA
|| !Parse::Utils::can_tok_start_type (
lexer.peek_token (3)->get_id ()))));
/* definitely not a block:
* path '{' ident ','
* path '{' ident ':' [anything] ','
* path '{' ident ':' [not a type]
* otherwise, assume block expr and thus path */
// DEBUG
rust_debug ("values of lookahead: '%s' '%s' '%s' '%s' ",
lexer.peek_token (1)->get_token_description (),
lexer.peek_token (2)->get_token_description (),
lexer.peek_token (3)->get_token_description (),
lexer.peek_token (4)->get_token_description ());
rust_debug ("can be struct expr: '%s', not a block: '%s'",
restrictions.can_be_struct_expr ? "true" : "false",
not_a_block ? "true" : "false");
// struct/enum expr struct
if (!restrictions.can_be_struct_expr && !not_a_block)
{
// HACK: add outer attrs to path
path.set_outer_attrs (std::move (outer_attrs));
return std::unique_ptr (
new AST::PathInExpression (std::move (path)));
}
auto struct_expr
= parse_struct_expr_struct_partial (std::move (path),
std::move (outer_attrs));
if (struct_expr == nullptr)
{
return tl::unexpected (
Parse::Error::Expr::CHILD_ERROR);
}
return struct_expr;
}
case LEFT_PAREN:
{
// struct/enum expr tuple
if (!restrictions.can_be_struct_expr)
{
// assume path is returned
// HACK: add outer attributes to path
path.set_outer_attrs (std::move (outer_attrs));
return std::make_unique (std::move (path));
}
auto tuple_expr
= parse_struct_expr_tuple_partial (std::move (path),
std::move (outer_attrs));
if (tuple_expr == nullptr)
{
return tl::unexpected (
Parse::Error::Expr::CHILD_ERROR);
}
return tuple_expr;
}
default:
// assume path is returned if not single segment
if (path.is_single_segment ())
{
// FIXME: This should probably be returned as a path.
/* HACK: may have to become permanent, but this is my current
* identifier expression */
return std::unique_ptr (new AST::IdentifierExpr (
path.get_segments ()[0].get_ident_segment ().as_string (), {},
path.get_locus ()));
}
// HACK: add outer attrs to path
path.set_outer_attrs (std::move (outer_attrs));
return std::unique_ptr (
new AST::PathInExpression (std::move (path)));
}
rust_unreachable ();
}
// Handling of expresions that do not start with a path for `null_denotation`.
template
tl::expected, Parse::Error::Expr>
Parser::null_denotation_not_path (
const_TokenPtr tok, AST::AttrVec outer_attrs, ParseRestrictions restrictions)
{
switch (tok->get_id ())
{
// FIXME: Handle in null_denotation_path?
case LEFT_SHIFT:
case LEFT_ANGLE:
{
// qualified path
// HACK: add outer attrs to path
AST::QualifiedPathInExpression path
= parse_qualified_path_in_expression (tok->get_locus ());
path.set_outer_attrs (std::move (outer_attrs));
return std::make_unique (
std::move (path));
}
// FIXME: delegate to parse_literal_expr instead? would have to rejig
// tokens and whatever.
// FIXME: for literal exprs, outer attrs should be passed in, and later
// error if it does not make up the entire statement.
case INT_LITERAL:
// we should check the range, but ignore for now
// encode as int?
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::INT,
tok->get_type_hint (), {}, tok->get_locus ()));
case FLOAT_LITERAL:
// encode as float?
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT,
tok->get_type_hint (), {}, tok->get_locus ()));
case STRING_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
tok->get_type_hint (), {}, tok->get_locus ()));
case BYTE_STRING_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE_STRING,
tok->get_type_hint (), {}, tok->get_locus ()));
case RAW_STRING_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::RAW_STRING,
tok->get_type_hint (), {}, tok->get_locus ()));
case CHAR_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR,
tok->get_type_hint (), {}, tok->get_locus ()));
case BYTE_CHAR_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE,
tok->get_type_hint (), {}, tok->get_locus ()));
case TRUE_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (Values::Keywords::TRUE_LITERAL,
AST::Literal::BOOL, tok->get_type_hint (), {},
tok->get_locus ()));
case FALSE_LITERAL:
return std::unique_ptr (
new AST::LiteralExpr (Values::Keywords::FALSE_LITERAL,
AST::Literal::BOOL, tok->get_type_hint (), {},
tok->get_locus ()));
case LEFT_PAREN:
{
auto grouped_or_tuple_expr
= parse_grouped_or_tuple_expr (std::move (outer_attrs),
tok->get_locus ());
if (grouped_or_tuple_expr)
return std::move (grouped_or_tuple_expr.value ());
else
return tl::unexpected (
Parse::Error::Expr::CHILD_ERROR);
}
/*case PLUS: { // unary plus operator
// invoke parse_expr recursively with appropriate priority, etc. for
below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS);
if (expr == nullptr)
return nullptr;
// can only apply to integer and float expressions
if (expr->get_type() != integer_type_node || expr->get_type() !=
float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
plus must be int or float but it is %s", print_type(expr->get_type()));
return nullptr;
}
return Tree(expr, tok->get_locus());
}*/
// Rust has no unary plus operator
case MINUS:
{ // unary minus
ParseRestrictions entered_from_unary;
entered_from_unary.entered_from_unary = true;
if (!restrictions.can_be_struct_expr)
entered_from_unary.can_be_struct_expr = false;
auto expr = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
if (!expr)
return tl::unexpected (
Parse::Error::Expr::CHILD_ERROR);
// can only apply to integer and float expressions
/*if (expr.get_type() != integer_type_node || expr.get_type() !=
float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
minus must be int or float but it is %s",
print_type(expr.get_type())); return Tree::error();
}*/
/* FIXME: when implemented the "get type" method on expr, ensure it is
* int or float type (except unsigned int). Actually, this would
* probably have to be done in semantic analysis (as type checking).
*/
/* FIXME: allow outer attributes on these expressions by having an
* outer attrs parameter in function*/
return std::make_unique