blob: 306a0958d829aae64ed7f84d679b173572596473 [file] [log] [blame]
// 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 {
// Parses a block expression, including the curly braces at start and end.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::BlockExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::parse_block_expr (
AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> 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> (
Parse::Error::Node::MALFORMED);
}
}
AST::AttrVec inner_attrs = parse_inner_attributes ();
// parse statements and expression
std::vector<std::unique_ptr<AST::Stmt>> stmts;
std::unique_ptr<AST::Expr> 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> (
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> (Parse::Error::Node::MALFORMED);
}
// grammar allows for empty block expressions
stmts.shrink_to_fit ();
return std::unique_ptr<AST::BlockExpr> (
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 <typename ManagedTokenSource>
tl::expected<AST::AnonConst, Parse::Error::Node>
Parser<ManagedTokenSource>::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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ConstBlock>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
Parse::Error::Node::CHILD_ERROR);
}
auto block = std::move (block_res.value ());
auto block_locus = block->get_locus ();
return std::make_unique<AST::ConstBlock> (AST::AnonConst (std::move (block),
block_locus),
locus, std::move (outer_attrs));
}
/* Parses a "grouped" expression (expression in parentheses), used to control
* precedence. */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::GroupedExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (RIGHT_PAREN))
{
// skip after somewhere?
return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
}
return std::unique_ptr<AST::GroupedExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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<AST::ClosureParam> 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> (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<AST::TypeNoBounds> 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> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::ClosureExprInnerTyped> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::ClosureExprInner> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::LiteralExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (Parse::Error::Node::MALFORMED);
}
// create literal based on stuff in switch
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (std::move (literal_value), std::move (type),
t->get_type_hint (), std::move (outer_attrs),
t->get_locus ()));
}
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::BoxExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (Parse::Error::Node::CHILD_ERROR);
return std::unique_ptr<AST::BoxExpr> (
new AST::BoxExpr (std::move (expr.value ()), std::move (outer_attrs),
locus));
}
// Parses a return expression (including any expression to return).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ReturnExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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<std::unique_ptr<AST::Expr>> expr = tl::nullopt;
if (returned_expr)
expr = std::move (returned_expr.value ());
return std::make_unique<AST::ReturnExpr> (std::move (expr),
std::move (outer_attrs), locus);
}
// Parses a try expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::TryExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (Parse::Error::Node::CHILD_ERROR);
return std::unique_ptr<AST::TryExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::BreakExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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<AST::Lifetime> (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<AST::BreakExpr> (
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<AST::BreakExpr> (
new AST::BreakExpr (std::move (label), tl::nullopt,
std::move (outer_attrs), locus));
else
return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
}
// Parses a continue expression (including any label to continue from).
template <typename ManagedTokenSource>
std::unique_ptr<AST::ContinueExpr>
Parser<ManagedTokenSource>::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<AST::Lifetime> (parsed_label.value ())
: tl::nullopt;
return std::make_unique<AST::ContinueExpr> (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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::IfExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
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> (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> (
Parse::Error::Node::CHILD_ERROR);
}
// parse required block expr
auto if_body = parse_block_expr ();
if (!if_body)
return tl::unexpected<Parse::Error::Node> (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<AST::IfExpr> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::IfExprConseqElse> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::IfExprConseqElse> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::IfExprConseqElse> (
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> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::IfLetExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
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> (Parse::Error::Node::MALFORMED);
}
lexer.skip_token ();
// parse match arm patterns (which are required)
std::unique_ptr<AST::Pattern> 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> (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (EQUAL))
{
// skip somewhere?
return tl::unexpected<Parse::Error::Node> (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> (
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> (
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<AST::IfLetExpr> (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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::IfLetExprConseqElse> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::IfLetExprConseqElse> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::IfLetExprConseqElse> (
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> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::LoopExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
tl::optional<AST::LoopLabel> 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> (
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> (Parse::Error::Node::CHILD_ERROR);
return std::unique_ptr<AST::LoopExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::WhileLoopExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::parse_while_loop_expr (
AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> 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> (
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> (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> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::WhileLoopExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::WhileLetLoopExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::parse_while_let_loop_expr (
AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> 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> (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<AST::Pattern> 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> (Parse::Error::Node::MALFORMED);
}
if (!skip_token (EQUAL))
{
// skip somewhere?
return tl::unexpected<Parse::Error::Node> (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> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::WhileLetLoopExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ForLoopExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::parse_for_loop_expr (
AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> 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<AST::Pattern> 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> (
Parse::Error::Node::CHILD_ERROR);
}
if (!skip_token (IN))
{
// skip somewhere?
return tl::unexpected<Parse::Error::Node> (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> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::ForLoopExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
}
auto label = parsed_label
? tl::optional<AST::LoopLabel> (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> (
Parse::Error::Node::CHILD_ERROR);
}
}
// Parses a match expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::MatchExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
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> (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<std::unique_ptr<AST::MatchCase> > match_arms;
std::vector<AST::MatchCase> 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> (
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> (
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> (
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> (
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> (Parse::Error::Node::MALFORMED);
}
match_arms.shrink_to_fit ();
return std::unique_ptr<AST::MatchExpr> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::AsyncBlockExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::AsyncBlockExpr> (
new AST::AsyncBlockExpr (std::move (block_expr.value ()), has_move,
std::move (outer_attrs), locus));
}
// Parses an unsafe block expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::UnsafeBlockExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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> (
Parse::Error::Node::CHILD_ERROR);
}
return std::unique_ptr<AST::UnsafeBlockExpr> (
new AST::UnsafeBlockExpr (std::move (block_expr.value ()),
std::move (outer_attrs), locus));
}
// Parses an array definition expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArrayExpr>, Parse::Error::Node>
Parser<ManagedTokenSource>::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<std::unique_ptr<AST::Expr>> exprs;
auto array_elems
= std::make_unique<AST::ArrayElemsValues> (std::move (exprs), locus);
return std::make_unique<AST::ArrayExpr> (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> (
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> (
Parse::Error::Node::CHILD_ERROR);
}
skip_token (RIGHT_SQUARE);
std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
new AST::ArrayElemsCopied (std::move (initial_expr.value ()),
std::move (copy_amount.value ()),
locus));
return std::unique_ptr<AST::ArrayExpr> (
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<std::unique_ptr<AST::Expr>> exprs;
exprs.reserve (1);
exprs.push_back (std::move (initial_expr.value ()));
exprs.shrink_to_fit ();
skip_token (RIGHT_SQUARE);
std::unique_ptr<AST::ArrayElemsValues> array_elems (
new AST::ArrayElemsValues (std::move (exprs), locus));
return std::unique_ptr<AST::ArrayExpr> (
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<std::unique_ptr<AST::Expr>> 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> (
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<AST::ArrayElemsValues> array_elems (
new AST::ArrayElemsValues (std::move (exprs), locus));
return std::unique_ptr<AST::ArrayExpr> (
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> (
Parse::Error::Node::MALFORMED);
}
}
}
// Parses a grouped or tuple expression (disambiguates).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ExprWithoutBlock>, Parse::Error::Node>
Parser<ManagedTokenSource>::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<AST::TupleExpr> (
new AST::TupleExpr (std::vector<std::unique_ptr<AST::Expr>> (),
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> (
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<AST::GroupedExpr> (
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<std::unique_ptr<AST::Expr>> 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> (
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<AST::TupleExpr> (
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> (Parse::Error::Node::MALFORMED);
}
}
// Parses a struct expression field.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::StructExprField>,
Parse::Error::StructExprField>
Parser<ManagedTokenSource>::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> (
Parse::Error::StructExprField::CHILD_ERROR);
}
return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
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<AST::StructExprFieldIdentifier> (
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> (
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> (
Parse::Error::StructExprField::CHILD_ERROR);
}
return std::unique_ptr<AST::StructExprFieldIndexValue> (
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> (
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> (
Parse::Error::StructExprField::MALFORMED);
}
}
/* Pratt parser impl of parse_expr. FIXME: this is only provisional and
* probably will be changed. */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::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> (
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<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
= null_denotation ({}, null_denotation_restrictions);
if (!expr)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
if (expr.value () == nullptr)
return tl::unexpected<Parse::Error::Expr> (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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
}
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::left_denotations (
tl::expected<std::unique_ptr<AST::Expr>, 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> (
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> (
Parse::Error::Expr::LEFT_DENOTATION);
}
current_token = lexer.peek_token ();
}
return expr;
}
/* Determines action to take when finding token at beginning of expression. */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::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> (
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 <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::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<AST::PathInExpression> (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> (
Parse::Error::Expr::CHILD_ERROR);
return std::unique_ptr<AST::Expr> (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<AST::PathInExpression> (
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> (
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<AST::PathInExpression> (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> (
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<AST::IdentifierExpr> (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<AST::PathInExpression> (
new AST::PathInExpression (std::move (path)));
}
rust_unreachable ();
}
// Handling of expresions that do not start with a path for `null_denotation`.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::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<AST::QualifiedPathInExpression> (
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<AST::LiteralExpr> (
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<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT,
tok->get_type_hint (), {}, tok->get_locus ()));
case STRING_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
tok->get_type_hint (), {}, tok->get_locus ()));
case BYTE_STRING_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
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<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::RAW_STRING,
tok->get_type_hint (), {}, tok->get_locus ()));
case CHAR_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR,
tok->get_type_hint (), {}, tok->get_locus ()));
case BYTE_CHAR_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE,
tok->get_type_hint (), {}, tok->get_locus ()));
case TRUE_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (Values::Keywords::TRUE_LITERAL,
AST::Literal::BOOL, tok->get_type_hint (), {},
tok->get_locus ()));
case FALSE_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
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> (
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> (
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<AST::NegationExpr> (std::move (expr.value ()),
NegationOperator::NEGATE,
std::move (outer_attrs),
tok->get_locus ());
}
case EXCLAM:
{ // logical or bitwise not
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_EXCLAM, {}, entered_from_unary);
if (!expr)
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
// can only apply to boolean expressions
/*if (expr.get_type() != boolean_type_node) {
rust_error_at(tok->get_locus(),
"operand of logical not must be a boolean but it is %s",
print_type(expr.get_type()));
return Tree::error();
}*/
/* FIXME: type checking for boolean or integer expressions in semantic
* analysis */
// FIXME: allow outer attributes on these expressions
return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
NegationOperator::NOT,
std::move (outer_attrs),
tok->get_locus ());
}
case ASTERISK:
{
/* pointer dereference only - HACK: as struct expressions should
* always be value expressions, cannot be dereferenced */
ParseRestrictions entered_from_unary;
entered_from_unary.entered_from_unary = true;
entered_from_unary.can_be_struct_expr = false;
auto expr = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
if (!expr)
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
// FIXME: allow outer attributes on expression
return std::make_unique<AST::DereferenceExpr> (std::move (
expr.value ()),
std::move (outer_attrs),
tok->get_locus ());
}
case AMP:
{
// (single) "borrow" expression - shared (mutable) or immutable
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
= tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
Mutability mutability = Mutability::Imm;
bool raw_borrow = false;
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 is_mutability = [] (const_TokenPtr token) {
return token->get_id () == CONST || token->get_id () == MUT;
};
auto t = lexer.peek_token ();
// Weak raw keyword, we look (1) ahead and treat it as an identifier if
// there is no mut nor const.
if (t->get_id () == IDENTIFIER
&& t->get_str () == Values::WeakKeywords::RAW
&& is_mutability (lexer.peek_token (1)))
{
lexer.skip_token ();
switch (lexer.peek_token ()->get_id ())
{
case MUT:
mutability = Mutability::Mut;
break;
case CONST:
mutability = Mutability::Imm;
break;
default:
rust_error_at (lexer.peek_token ()->get_locus (),
"raw borrow should be either const or mut");
}
lexer.skip_token ();
auto expr_result
= parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
if (expr_result)
expr = std::move (expr_result.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
raw_borrow = true;
}
else if (t->get_id () == MUT)
{
lexer.skip_token ();
auto expr_result
= parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
if (expr_result)
expr = std::move (expr_result.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
mutability = Mutability::Mut;
raw_borrow = false;
}
else
{
auto expr_result
= parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
if (expr_result)
expr = std::move (expr_result.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
raw_borrow = false;
}
// FIXME: allow outer attributes on expression
return std::make_unique<AST::BorrowExpr> (std::move (expr.value ()),
mutability, raw_borrow, false,
std::move (outer_attrs),
tok->get_locus ());
}
case LOGICAL_AND:
{
// (double) "borrow" expression - shared (mutable) or immutable
std::unique_ptr<AST::Expr> expr = nullptr;
Mutability mutability = Mutability::Imm;
ParseRestrictions entered_from_unary;
entered_from_unary.entered_from_unary = true;
if (lexer.peek_token ()->get_id () == MUT)
{
lexer.skip_token ();
auto expr_res
= parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
if (!expr_res)
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
expr = std::move (expr_res.value ());
mutability = Mutability::Mut;
}
else
{
auto expr_result
= parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
if (expr_result)
expr = std::move (expr_result.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
mutability = Mutability::Imm;
}
// FIXME: allow outer attributes on expression
return std::make_unique<AST::BorrowExpr> (std::move (expr), mutability,
false, true,
std::move (outer_attrs),
tok->get_locus ());
}
case OR:
case PIPE:
case MOVE:
// closure expression
{
auto ret = parse_closure_expr_pratt (tok, std::move (outer_attrs));
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case DOT_DOT:
// either "range to" or "range full" expressions
{
auto ret
= parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case DOT_DOT_EQ:
// range to inclusive expr
{
auto ret = parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case RETURN_KW:
// FIXME: is this really a null denotation expression?
{
auto ret
= parse_return_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case TRY:
// FIXME: is this really a null denotation expression?
{
auto ret = parse_try_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case BREAK:
// FIXME: is this really a null denotation expression?
{
auto ret
= parse_break_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case CONTINUE:
return parse_continue_expr (std::move (outer_attrs), tok->get_locus ());
case LEFT_CURLY:
// ok - this is an expression with block for once.
{
auto ret = parse_block_expr (std::move (outer_attrs), tl::nullopt,
tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case IF:
// if or if let, so more lookahead to find out
if (lexer.peek_token ()->get_id () == LET)
{
// if let expr
auto ret
= parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
else
{
// if expr
auto ret = parse_if_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case LIFETIME:
{
auto ret = parse_labelled_loop_expr (tok, std::move (outer_attrs));
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case LOOP:
{
auto ret = parse_loop_expr (std::move (outer_attrs), tl::nullopt,
tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case WHILE:
if (lexer.peek_token ()->get_id () == LET)
{
auto ret = parse_while_let_loop_expr (std::move (outer_attrs));
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
else
{
auto ret = parse_while_loop_expr (std::move (outer_attrs),
tl::nullopt, tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case FOR:
{
auto ret = parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case MATCH_KW:
// also an expression with block
{
auto ret
= parse_match_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case LEFT_SQUARE:
// array definition expr (not indexing)
{
auto ret
= parse_array_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case UNSAFE:
{
auto ret = parse_unsafe_block_expr (std::move (outer_attrs),
tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case BOX:
{
auto ret = parse_box_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
case UNDERSCORE:
add_error (
Error (tok->get_locus (),
"use of %qs is not allowed on the right-side of an assignment",
tok->get_token_description ()));
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
case CONST:
{
auto ret
= parse_const_block_expr (std::move (outer_attrs), tok->get_locus ());
if (ret)
return std::move (ret.value ());
else
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
default:
if (!restrictions.expr_can_be_null)
add_error (Error (tok->get_locus (),
"found unexpected token %qs in null denotation",
tok->get_token_description ()));
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
}
/* Called for each token that can appear in infix (between) position. Can be
* operators or other punctuation. Returns a function pointer to member
* function that implements the left denotation for the token given. */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs,
ParseRestrictions restrictions)
{
// Token passed in has already been skipped, so peek gives "next" token
switch (tok->get_id ())
{
// FIXME: allow for outer attributes to be applied
case QUESTION_MARK:
{
location_t left_locus = left->get_locus ();
// error propagation expression - unary postfix
return std::make_unique<AST::ErrorPropagationExpr> (
std::move (left), std::move (outer_attrs), left_locus);
}
case PLUS:
// sum expression - binary infix
/*return parse_binary_plus_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (tok, std::move (left),
std::move (outer_attrs),
ArithmeticOrLogicalOperator::ADD,
restrictions);
case MINUS:
// difference expression - binary infix
/*return parse_binary_minus_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::SUBTRACT, restrictions);
case ASTERISK:
// product expression - binary infix
/*return parse_binary_mult_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::MULTIPLY, restrictions);
case DIV:
// quotient expression - binary infix
/*return parse_binary_div_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::DIVIDE, restrictions);
case PERCENT:
// modulo expression - binary infix
/*return parse_binary_mod_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::MODULUS, restrictions);
case AMP:
// logical or bitwise and expression - binary infix
/*return parse_bitwise_and_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::BITWISE_AND, restrictions);
case PIPE:
// logical or bitwise or expression - binary infix
/*return parse_bitwise_or_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::BITWISE_OR, restrictions);
case CARET:
// logical or bitwise xor expression - binary infix
/*return parse_bitwise_xor_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::BITWISE_XOR, restrictions);
case LEFT_SHIFT:
// left shift expression - binary infix
/*return parse_left_shift_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::LEFT_SHIFT, restrictions);
case RIGHT_SHIFT:
// right shift expression - binary infix
/*return parse_right_shift_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_arithmetic_or_logical_expr (
tok, std::move (left), std::move (outer_attrs),
ArithmeticOrLogicalOperator::RIGHT_SHIFT, restrictions);
case EQUAL_EQUAL:
// equal to expression - binary infix (no associativity)
/*return parse_binary_equal_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_comparison_expr (tok, std::move (left),
std::move (outer_attrs),
ComparisonOperator::EQUAL, restrictions);
case NOT_EQUAL:
// not equal to expression - binary infix (no associativity)
/*return parse_binary_not_equal_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_comparison_expr (tok, std::move (left),
std::move (outer_attrs),
ComparisonOperator::NOT_EQUAL,
restrictions);
case RIGHT_ANGLE:
// greater than expression - binary infix (no associativity)
/*return parse_binary_greater_than_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_comparison_expr (tok, std::move (left),
std::move (outer_attrs),
ComparisonOperator::GREATER_THAN,
restrictions);
case LEFT_ANGLE:
// less than expression - binary infix (no associativity)
/*return parse_binary_less_than_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_comparison_expr (tok, std::move (left),
std::move (outer_attrs),
ComparisonOperator::LESS_THAN,
restrictions);
case GREATER_OR_EQUAL:
// greater than or equal to expression - binary infix (no associativity)
/*return parse_binary_greater_equal_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_comparison_expr (tok, std::move (left),
std::move (outer_attrs),
ComparisonOperator::GREATER_OR_EQUAL,
restrictions);
case LESS_OR_EQUAL:
// less than or equal to expression - binary infix (no associativity)
/*return parse_binary_less_equal_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_comparison_expr (tok, std::move (left),
std::move (outer_attrs),
ComparisonOperator::LESS_OR_EQUAL,
restrictions);
case OR:
// lazy logical or expression - binary infix
return parse_lazy_or_expr (tok, std::move (left), std::move (outer_attrs),
restrictions);
case LOGICAL_AND:
// lazy logical and expression - binary infix
return parse_lazy_and_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);
case AS:
/* type cast expression - kind of binary infix (RHS is actually a
* TypeNoBounds) */
return parse_type_cast_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);
case EQUAL:
// assignment expression - binary infix (note right-to-left
// associativity)
return parse_assig_expr (tok, std::move (left), std::move (outer_attrs),
restrictions);
case PLUS_EQ:
/* plus-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_plus_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (tok, std::move (left),
std::move (outer_attrs),
CompoundAssignmentOperator::ADD,
restrictions);
case MINUS_EQ:
/* minus-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_minus_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::SUBTRACT, restrictions);
case ASTERISK_EQ:
/* multiply-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_mult_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::MULTIPLY, restrictions);
case DIV_EQ:
/* division-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_div_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (tok, std::move (left),
std::move (outer_attrs),
CompoundAssignmentOperator::DIVIDE,
restrictions);
case PERCENT_EQ:
/* modulo-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_mod_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::MODULUS, restrictions);
case AMP_EQ:
/* bitwise and-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_and_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::BITWISE_AND, restrictions);
case PIPE_EQ:
/* bitwise or-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_or_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::BITWISE_OR, restrictions);
case CARET_EQ:
/* bitwise xor-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_xor_assig_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::BITWISE_XOR, restrictions);
case LEFT_SHIFT_EQ:
/* left shift-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_left_shift_assig_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::LEFT_SHIFT, restrictions);
case RIGHT_SHIFT_EQ:
/* right shift-assignment expression - binary infix (note right-to-left
* associativity) */
/*return parse_right_shift_assig_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);*/
return parse_compound_assignment_expr (
tok, std::move (left), std::move (outer_attrs),
CompoundAssignmentOperator::RIGHT_SHIFT, restrictions);
case DOT_DOT:
/* range exclusive expression - binary infix (no associativity)
* either "range" or "range from" */
return parse_led_range_exclusive_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);
case DOT_DOT_EQ:
/* range inclusive expression - binary infix (no associativity)
* unambiguously RangeInclusiveExpr */
return parse_range_inclusive_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);
case SCOPE_RESOLUTION:
// path expression - binary infix? FIXME should this even be parsed
// here?
add_error (
Error (tok->get_locus (),
"found scope resolution operator in left denotation "
"function - this should probably be handled elsewhere"));
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
case DOT:
{
/* field expression or method call - relies on parentheses after next
* identifier or await if token after is "await" (unary postfix) or
* tuple index if token after is a decimal int literal */
const_TokenPtr next_tok = lexer.peek_token ();
if (next_tok->get_id () == IDENTIFIER
&& next_tok->get_str () == Values::Keywords::AWAIT)
{
// await expression
return parse_await_expr (tok, std::move (left),
std::move (outer_attrs));
}
else if (next_tok->get_id () == INT_LITERAL)
{
// tuple index expression - TODO check for decimal int literal
return parse_tuple_index_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);
}
else if (next_tok->get_id () == FLOAT_LITERAL)
{
// Lexer has misidentified a tuple index as a float literal
// eg: `(x, (y, z)).1.0` -> 1.0 has been identified as a float
// literal. This means we should split it into three new separate
// tokens, the first tuple index, the dot and the second tuple
// index.
auto current_loc = next_tok->get_locus ();
auto str = next_tok->get_str ();
auto dot_pos = str.find (".");
auto prefix = str.substr (0, dot_pos);
auto suffix = str.substr (dot_pos + 1);
if (dot_pos == str.size () - 1)
lexer.split_current_token (
{Token::make_int (current_loc, std::move (prefix),
CORETYPE_PURE_DECIMAL),
Token::make (DOT, current_loc + 1)});
else
lexer.split_current_token (
{Token::make_int (current_loc, std::move (prefix),
CORETYPE_PURE_DECIMAL),
Token::make (DOT, current_loc + 1),
Token::make_int (current_loc + 2, std::move (suffix),
CORETYPE_PURE_DECIMAL)});
return parse_tuple_index_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);
}
else if (next_tok->get_id () == IDENTIFIER
&& lexer.peek_token (1)->get_id () != LEFT_PAREN
&& lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION)
{
/* field expression (or should be) - FIXME: scope resolution right
* after identifier should always be method, I'm pretty sure */
return parse_field_access_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);
}
else
{
// method call (probably)
return parse_method_call_expr (tok, std::move (left),
std::move (outer_attrs),
restrictions);
}
}
case LEFT_PAREN:
// function call - method call is based on dot notation first
return parse_function_call_expr (tok, std::move (left),
std::move (outer_attrs), restrictions);
case LEFT_SQUARE:
// array or slice index expression (pseudo binary infix)
return parse_index_expr (tok, std::move (left), std::move (outer_attrs),
restrictions);
default:
add_error (Error (tok->get_locus (),
"found unexpected token %qs in left denotation",
tok->get_token_description ()));
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
}
/* Returns the left binding power for the given ArithmeticOrLogicalExpr type.
* TODO make constexpr? Would that even do anything useful? */
inline binding_powers
get_lbp_for_arithmetic_or_logical_expr (
AST::ArithmeticOrLogicalExpr::ExprType expr_type)
{
switch (expr_type)
{
case ArithmeticOrLogicalOperator::ADD:
return LBP_PLUS;
case ArithmeticOrLogicalOperator::SUBTRACT:
return LBP_MINUS;
case ArithmeticOrLogicalOperator::MULTIPLY:
return LBP_MUL;
case ArithmeticOrLogicalOperator::DIVIDE:
return LBP_DIV;
case ArithmeticOrLogicalOperator::MODULUS:
return LBP_MOD;
case ArithmeticOrLogicalOperator::BITWISE_AND:
return LBP_AMP;
case ArithmeticOrLogicalOperator::BITWISE_OR:
return LBP_PIPE;
case ArithmeticOrLogicalOperator::BITWISE_XOR:
return LBP_CARET;
case ArithmeticOrLogicalOperator::LEFT_SHIFT:
return LBP_L_SHIFT;
case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
return LBP_R_SHIFT;
default:
// WTF? should not happen, this is an error
rust_unreachable ();
return LBP_PLUS;
}
}
// Parses an arithmetic or logical expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr (
const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
AST::ArithmeticOrLogicalExpr::ExprType expr_type,
ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()), expr_type, locus);
}
// Parses a binary addition expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_plus_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::ADD, locus);
}
// Parses a binary subtraction expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_minus_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::SUBTRACT, locus);
}
// Parses a binary multiplication expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_mult_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::MULTIPLY, locus);
}
// Parses a binary division expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_div_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::DIVIDE, locus);
}
// Parses a binary modulo expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_mod_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::MODULUS, locus);
}
/* Parses a binary bitwise (or eager logical) and expression (with Pratt
* parsing). */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_bitwise_and_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::BITWISE_AND, locus);
}
/* Parses a binary bitwise (or eager logical) or expression (with Pratt
* parsing). */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_bitwise_or_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::BITWISE_OR, locus);
}
/* Parses a binary bitwise (or eager logical) xor expression (with Pratt
* parsing). */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_bitwise_xor_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::BITWISE_XOR, locus);
}
// Parses a binary left shift expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_left_shift_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::LEFT_SHIFT, locus);
}
// Parses a binary right shift expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_right_shift_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ArithmeticOrLogicalExpr> (
std::move (left), std::move (right.value ()),
ArithmeticOrLogicalOperator::RIGHT_SHIFT, locus);
}
/* Returns the left binding power for the given ComparisonExpr type.
* TODO make constexpr? Would that even do anything useful? */
inline binding_powers
get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type)
{
switch (expr_type)
{
case ComparisonOperator::EQUAL:
return LBP_EQUAL;
case ComparisonOperator::NOT_EQUAL:
return LBP_NOT_EQUAL;
case ComparisonOperator::GREATER_THAN:
return LBP_GREATER_THAN;
case ComparisonOperator::LESS_THAN:
return LBP_SMALLER_THAN;
case ComparisonOperator::GREATER_OR_EQUAL:
return LBP_GREATER_EQUAL;
case ComparisonOperator::LESS_OR_EQUAL:
return LBP_SMALLER_EQUAL;
default:
// WTF? should not happen, this is an error
rust_unreachable ();
return LBP_EQUAL;
}
}
/* Parses a ComparisonExpr of given type and LBP. TODO find a way to only
* specify one and have the other looked up - e.g. specify ExprType and
* binding power is looked up? */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_comparison_expr (
const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (get_lbp_for_comparison_expr (expr_type),
AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (std::move (left),
std::move (right.value ()),
expr_type, locus);
}
// Parses a binary equal to expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_equal_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (std::move (left),
std::move (right.value ()),
ComparisonOperator::EQUAL,
locus);
}
// Parses a binary not equal to expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_not_equal_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (std::move (left),
std::move (right.value ()),
ComparisonOperator::NOT_EQUAL,
locus);
}
// Parses a binary greater than expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_greater_than_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (
std::move (left), std::move (right.value ()),
ComparisonOperator::GREATER_THAN, locus);
}
// Parses a binary less than expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_less_than_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (std::move (left),
std::move (right.value ()),
ComparisonOperator::LESS_THAN,
locus);
}
// Parses a binary greater than or equal to expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_greater_equal_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (
std::move (left), std::move (right.value ()),
ComparisonOperator::GREATER_OR_EQUAL, locus);
}
// Parses a binary less than or equal to expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_binary_less_equal_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::ComparisonExpr> (
std::move (left), std::move (right.value ()),
ComparisonOperator::LESS_OR_EQUAL, locus);
}
// Parses a binary lazy boolean or expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_lazy_or_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::LazyBooleanExpr> (
std::move (left), std::move (right.value ()),
LazyBooleanOperator::LOGICAL_OR, locus);
}
// Parses a binary lazy boolean and expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_lazy_and_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::LazyBooleanExpr> (
std::move (left), std::move (right.value ()),
LazyBooleanOperator::LOGICAL_AND, locus);
}
// Parses a pseudo-binary infix type cast expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::TypeCastExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_type_cast_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED,
ParseRestrictions restrictions ATTRIBUTE_UNUSED)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto type = parse_type_no_bounds ();
if (!type)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: how do I get precedence put in here?
// TODO: check types. actually, do so during semantic analysis
location_t locus = expr_to_cast->get_locus ();
return std::make_unique<AST::TypeCastExpr> (std::move (expr_to_cast),
std::move (type), locus);
}
// Parses a binary assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::AssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
location_t locus = left->get_locus ();
return std::make_unique<AST::AssignmentExpr> (std::move (left),
std::move (right.value ()),
std::move (outer_attrs), locus);
}
/* Returns the left binding power for the given CompoundAssignmentExpr type.
* TODO make constexpr? Would that even do anything useful? */
inline binding_powers
get_lbp_for_compound_assignment_expr (
AST::CompoundAssignmentExpr::ExprType expr_type)
{
switch (expr_type)
{
case CompoundAssignmentOperator::ADD:
return LBP_PLUS;
case CompoundAssignmentOperator::SUBTRACT:
return LBP_MINUS;
case CompoundAssignmentOperator::MULTIPLY:
return LBP_MUL;
case CompoundAssignmentOperator::DIVIDE:
return LBP_DIV;
case CompoundAssignmentOperator::MODULUS:
return LBP_MOD;
case CompoundAssignmentOperator::BITWISE_AND:
return LBP_AMP;
case CompoundAssignmentOperator::BITWISE_OR:
return LBP_PIPE;
case CompoundAssignmentOperator::BITWISE_XOR:
return LBP_CARET;
case CompoundAssignmentOperator::LEFT_SHIFT:
return LBP_L_SHIFT;
case CompoundAssignmentOperator::RIGHT_SHIFT:
return LBP_R_SHIFT;
default:
// WTF? should not happen, this is an error
rust_unreachable ();
return LBP_PLUS;
}
}
// Parses a compound assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_compound_assignment_expr (
const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
AST::CompoundAssignmentExpr::ExprType expr_type,
ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()), expr_type, locus);
}
// Parses a binary add-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_plus_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::ADD, locus);
}
// Parses a binary minus-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_minus_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::SUBTRACT, locus);
}
// Parses a binary multiplication-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_mult_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::MULTIPLY, locus);
}
// Parses a binary division-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_div_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::DIVIDE, locus);
}
// Parses a binary modulo-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_mod_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::MODULUS, locus);
}
// Parses a binary and-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_and_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::BITWISE_AND, locus);
}
// Parses a binary or-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_or_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::BITWISE_OR, locus);
}
// Parses a binary xor-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_xor_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::BITWISE_XOR, locus);
}
// Parses a binary left shift-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_left_shift_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right
= parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::LEFT_SHIFT, locus);
}
// Parses a binary right shift-assignment expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_right_shift_assig_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right
= parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::CompoundAssignmentExpr> (
std::move (left), std::move (right.value ()),
CompoundAssignmentOperator::RIGHT_SHIFT, locus);
}
// Parses a postfix unary await expression (with Pratt parsing).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::AwaitExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_await_expr (
const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await,
AST::AttrVec outer_attrs)
{
/* skip "await" identifier (as "." has already been consumed in
* parse_expression) this assumes that the identifier was already identified
* as await */
if (!skip_token (IDENTIFIER))
{
Error error (tok->get_locus (), "failed to skip %<await%> in await expr "
"- this is probably a deep issue");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// TODO: check inside async block in semantic analysis
location_t locus = expr_to_await->get_locus ();
return std::unique_ptr<AST::AwaitExpr> (
new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs),
locus));
}
/* Parses an exclusive range ('..') in left denotation position (i.e.
* RangeFromExpr or RangeFromToExpr). */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// FIXME: this probably parses expressions accidently or whatever
// try parsing RHS (as tok has already been consumed in parse_expression)
// Can be nullptr, in which case it is a RangeFromExpr, otherwise a
// RangeFromToExpr.
restrictions.expr_can_be_null = true;
auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
location_t locus = left->get_locus ();
if (!right)
{
// range from expr
return std::make_unique<AST::RangeFromExpr> (std::move (left), locus);
}
else
{
return std::make_unique<AST::RangeFromToExpr> (std::move (left),
std::move (right.value ()),
locus);
}
// FIXME: make non-associative
}
/* Parses an exclusive range ('..') in null denotation position (i.e.
* RangeToExpr or RangeFullExpr). */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr (
const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
{
auto restrictions = ParseRestrictions ();
restrictions.expr_can_be_null = true;
// FIXME: this probably parses expressions accidently or whatever
// try parsing RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
location_t locus = tok->get_locus ();
if (!right)
{
// range from expr
return std::make_unique<AST::RangeFullExpr> (locus);
}
else
{
return std::make_unique<AST::RangeToExpr> (std::move (right.value ()),
locus);
}
// FIXME: make non-associative
}
// Parses a full binary range inclusive expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::RangeFromToInclExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_range_inclusive_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: make non-associative
// TODO: check types. actually, do so during semantic analysis
location_t locus = left->get_locus ();
return std::make_unique<AST::RangeFromToInclExpr> (std::move (left),
std::move (right.value ()),
locus);
}
// Parses an inclusive range-to prefix unary expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::RangeToInclExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_range_to_inclusive_expr (
const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
{
// parse RHS (as tok has already been consumed in parse_expression)
auto right = parse_expr (LBP_DOT_DOT_EQ);
if (!right)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// FIXME: make non-associative
// TODO: check types. actually, do so during semantic analysis
return std::make_unique<AST::RangeToInclExpr> (std::move (right.value ()),
tok->get_locus ());
}
// Parses a pseudo-binary infix tuple index expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::TupleIndexExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_tuple_index_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr,
AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
{
// parse int literal (as token already skipped)
const_TokenPtr index_tok = expect_token (INT_LITERAL);
if (index_tok == nullptr)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
std::string index = index_tok->get_str ();
// convert to integer
if (!index_tok->is_pure_decimal ())
{
Error error (index_tok->get_locus (),
"tuple index should be a pure decimal literal");
add_error (std::move (error));
}
int index_int = atoi (index.c_str ());
location_t locus = tuple_expr->get_locus ();
return std::make_unique<AST::TupleIndexExpr> (std::move (tuple_expr),
index_int,
std::move (outer_attrs), locus);
}
// Parses a pseudo-binary infix array (or slice) index expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ArrayIndexExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_index_expr (
const_TokenPtr, std::unique_ptr<AST::Expr> array_expr,
AST::AttrVec outer_attrs, ParseRestrictions)
{
// parse RHS (as tok has already been consumed in parse_expression)
/*std::unique_ptr<AST::Expr> index_expr
= parse_expr (LBP_ARRAY_REF, AST::AttrVec (),
restrictions);*/
// TODO: conceptually, should treat [] as brackets, so just parse all expr
auto index_expr = parse_expr ();
if (!index_expr)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
// skip ']' at end of array
if (!skip_token (RIGHT_SQUARE))
{
// skip somewhere?
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// TODO: check types. actually, do so during semantic analysis
location_t locus = array_expr->get_locus ();
return std::make_unique<AST::ArrayIndexExpr> (std::move (array_expr),
std::move (index_expr.value ()),
std::move (outer_attrs), locus);
}
// Parses a pseudo-binary infix struct field access expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::FieldAccessExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_field_access_expr (
const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr,
AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
{
/* get field name identifier (assume that this is a field access expr and
* not await, for instance) */
const_TokenPtr ident_tok = expect_token (IDENTIFIER);
if (ident_tok == nullptr)
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
Identifier ident{ident_tok};
location_t locus = struct_expr->get_locus ();
// TODO: check types. actually, do so during semantic analysis
return std::make_unique<AST::FieldAccessExpr> (std::move (struct_expr),
std::move (ident),
std::move (outer_attrs),
locus);
}
// Parses a pseudo-binary infix method call expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::MethodCallExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_method_call_expr (
const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
AST::AttrVec outer_attrs, ParseRestrictions)
{
// parse path expr segment
AST::PathExprSegment segment = parse_path_expr_segment ();
if (segment.is_error ())
{
Error error (tok->get_locus (),
"failed to parse path expr segment of method call expr");
add_error (std::move (error));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
// skip left parentheses
if (!skip_token (LEFT_PAREN))
{
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// parse method params (if they exist)
std::vector<std::unique_ptr<AST::Expr>> params;
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () != RIGHT_PAREN)
{
auto param = parse_expr ();
if (!param)
{
Error error (t->get_locus (),
"failed to parse method param in method call");
add_error (std::move (error));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
params.push_back (std::move (param.value ()));
if (lexer.peek_token ()->get_id () != COMMA)
break;
lexer.skip_token ();
t = lexer.peek_token ();
}
// skip right paren
if (!skip_token (RIGHT_PAREN))
{
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// TODO: check types. actually do so in semantic analysis pass.
location_t locus = receiver_expr->get_locus ();
return std::make_unique<AST::MethodCallExpr> (std::move (receiver_expr),
std::move (segment),
std::move (params),
std::move (outer_attrs), locus);
}
// Parses a pseudo-binary infix function call expression.
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_function_call_expr (
const_TokenPtr, std::unique_ptr<AST::Expr> function_expr,
AST::AttrVec outer_attrs, ParseRestrictions)
{
// parse function params (if they exist)
std::vector<std::unique_ptr<AST::Expr>> params;
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () != RIGHT_PAREN)
{
auto param = parse_expr ();
if (!param)
{
Error error (t->get_locus (),
"failed to parse function param in function call");
add_error (std::move (error));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
params.push_back (std::move (param.value ()));
if (lexer.peek_token ()->get_id () != COMMA)
break;
lexer.skip_token ();
t = lexer.peek_token ();
}
// skip ')' at end of param list
if (!skip_token (RIGHT_PAREN))
{
// skip somewhere?
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// TODO: check types. actually, do so during semantic analysis
location_t locus = function_expr->get_locus ();
return std::make_unique<AST::CallExpr> (std::move (function_expr),
std::move (params),
std::move (outer_attrs), locus);
}
/* Parses a struct expr struct with a path in expression already parsed (but
* not
* '{' token). */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::StructExprStruct>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
AST::PathInExpression path, AST::AttrVec outer_attrs)
{
// assume struct expr struct (as struct-enum disambiguation requires name
// lookup) again, make statement if final ';'
if (!skip_token (LEFT_CURLY))
{
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// parse inner attributes
AST::AttrVec inner_attrs = parse_inner_attributes ();
// branch based on next token
const_TokenPtr t = lexer.peek_token ();
location_t path_locus = path.get_locus ();
switch (t->get_id ())
{
case RIGHT_CURLY:
// struct with no body
lexer.skip_token ();
return std::make_unique<AST::StructExprStruct> (std::move (path),
std::move (inner_attrs),
std::move (outer_attrs),
path_locus);
case DOT_DOT:
/* technically this would give a struct base-only struct, but this
* algorithm should work too. As such, AST type not happening. */
case IDENTIFIER:
case HASH:
case INT_LITERAL:
{
// struct with struct expr fields
// parse struct expr fields
std::vector<std::unique_ptr<AST::StructExprField>> fields;
while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
{
auto field = parse_struct_expr_field ();
if (!field
&& field.error () != Parse::Error::StructExprField::STRUCT_BASE)
{
Error error (t->get_locus (),
"failed to parse struct (or enum) expr field");
add_error (std::move (error));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
// DEBUG:
rust_debug ("struct/enum expr field validated to not be null");
fields.push_back (std::move (field.value ()));
// DEBUG:
rust_debug ("struct/enum expr field pushed back");
if (lexer.peek_token ()->get_id () != COMMA)
{
// DEBUG:
rust_debug ("lack of comma detected in struct/enum expr "
"fields - break");
break;
}
lexer.skip_token ();
// DEBUG:
rust_debug ("struct/enum expr fields comma skipped ");
t = lexer.peek_token ();
}
// DEBUG:
rust_debug ("struct/enum expr about to parse struct base ");
// parse struct base if it exists
AST::StructBase struct_base = AST::StructBase::error ();
if (lexer.peek_token ()->get_id () == DOT_DOT)
{
location_t dot_dot_location = lexer.peek_token ()->get_locus ();
lexer.skip_token ();
// parse required struct base expr
auto base_expr = parse_expr ();
if (!base_expr)
{
Error error (lexer.peek_token ()->get_locus (),
"failed to parse struct base expression in struct "
"expression");
add_error (std::move (error));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
// DEBUG:
rust_debug ("struct/enum expr - parsed and validated base expr");
struct_base = AST::StructBase (std::move (base_expr.value ()),
dot_dot_location);
// DEBUG:
rust_debug ("assigned struct base to new struct base ");
}
if (!skip_token (RIGHT_CURLY))
{
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::MALFORMED);
}
// DEBUG:
rust_debug (
"struct/enum expr skipped right curly - done and ready to return");
return std::make_unique<AST::StructExprStructFields> (
std::move (path), std::move (fields), path_locus,
std::move (struct_base), std::move (inner_attrs),
std::move (outer_attrs));
}
default:
add_error (
Error (t->get_locus (),
"unrecognised token %qs in struct (or enum) expression - "
"expected %<}%>, identifier, integer literal, or %<..%>",
t->get_token_description ()));
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
}
/* Parses a struct expr tuple with a path in expression already parsed (but
* not
* '(' token).
* FIXME: this currently outputs a call expr, as they cannot be disambiguated.
* A better solution would be to just get this to call that function directly.
* */
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
AST::PathInExpression path, AST::AttrVec outer_attrs)
{
if (!skip_token (LEFT_PAREN))
{
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
AST::AttrVec inner_attrs = parse_inner_attributes ();
std::vector<std::unique_ptr<AST::Expr>> exprs;
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () != RIGHT_PAREN)
{
// parse expression (required)
auto expr = parse_expr ();
if (!expr)
{
Error error (t->get_locus (), "failed to parse expression in "
"struct (or enum) expression tuple");
add_error (std::move (error));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
exprs.push_back (std::move (expr.value ()));
if (lexer.peek_token ()->get_id () != COMMA)
break;
lexer.skip_token ();
t = lexer.peek_token ();
}
if (!skip_token (RIGHT_PAREN))
{
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
location_t path_locus = path.get_locus ();
auto pathExpr = std::make_unique<AST::PathInExpression> (std::move (path));
return std::make_unique<AST::CallExpr> (std::move (pathExpr),
std::move (exprs),
std::move (outer_attrs), path_locus);
}
// Parses a closure expression with pratt parsing (from null denotation).
template <typename ManagedTokenSource>
tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Expr>
Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
AST::AttrVec outer_attrs)
{
// TODO: does this need pratt parsing (for precedence)? probably not, but
// idk
location_t locus = tok->get_locus ();
bool has_move = false;
if (tok->get_id () == MOVE)
{
has_move = true;
tok = lexer.peek_token ();
lexer.skip_token ();
// skip token and reassign
}
// handle parameter list
std::vector<AST::ClosureParam> params;
switch (tok->get_id ())
{
case OR:
// no parameters, don't skip token
break;
case PIPE:
{
// actually may have parameters
// don't skip token
const_TokenPtr 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));
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
params.push_back (std::move (param));
if (lexer.peek_token ()->get_id () != COMMA)
{
if (lexer.peek_token ()->get_id () == OR)
lexer.split_current_token (PIPE, PIPE);
// not an error but means param list is done
break;
}
// skip comma
lexer.skip_token ();
if (lexer.peek_token ()->get_id () == OR)
lexer.split_current_token (PIPE, PIPE);
t = lexer.peek_token ();
}
if (!skip_token (PIPE))
{
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::MALFORMED);
}
break;
}
default:
add_error (Error (tok->get_locus (),
"unexpected token %qs in closure expression - expected "
"%<|%> or %<||%>",
tok->get_token_description ()));
// skip somewhere?
return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
}
// again branch based on next token
tok = lexer.peek_token ();
if (tok->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
auto type = parse_type_no_bounds ();
if (!type)
{
// error
Error error (tok->get_locus (), "failed to parse type for closure");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::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::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
return std::make_unique<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 (tok->get_locus (),
"failed to parse expression in closure");
add_error (std::move (error));
// skip somewhere?
return tl::unexpected<Parse::Error::Expr> (
Parse::Error::Expr::CHILD_ERROR);
}
return std::make_unique<AST::ClosureExprInner> (std::move (expr.value ()),
std::move (params), locus,
has_move,
std::move (outer_attrs));
}
}
} // namespace Rust