--- /dev/null
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+/* DO NOT INCLUDE ANYWHERE - this is automatically included
+ * by rust-parse-impl.h
+ * This is also the reason why there are no include guards. */
+
+#include "rust-parse.h"
+
+namespace Rust {
+
+// Parses a block expression, including the curly braces at start and end.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::BlockExpr>
+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 nullptr;
+ }
+ }
+
+ 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)
+ {
+ ExprOrStmt expr_or_stmt = parse_stmt_or_expr ();
+ if (expr_or_stmt.is_error ())
+ {
+ skip_after_end_block ();
+ return nullptr;
+ }
+
+ 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);
+ break;
+ }
+ }
+
+ location_t end_locus = t->get_locus ();
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ Error error (t->get_locus (),
+ "error may be from having an expression (as opposed to "
+ "statement) in the body of the function but not last");
+ add_error (std::move (error));
+
+ skip_after_end_block ();
+ return nullptr;
+ }
+
+ // 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, AnonConstError>
+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 (AnonConstError::InvalidSizeExpr);
+
+ return AST::AnonConst (std::move (expr), 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>
+std::unique_ptr<AST::ConstBlock>
+Parser<ManagedTokenSource>::parse_const_block_expr (AST::AttrVec outer_attrs,
+ location_t locus)
+{
+ auto block = parse_block_expr ();
+
+ if (!block)
+ {
+ add_error (Error (locus, "failed to parse inner block in const block"));
+ skip_after_end_block ();
+
+ return nullptr;
+ }
+
+ 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>
+std::unique_ptr<AST::GroupedExpr>
+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
+ std::unique_ptr<AST::Expr> expr_in_parens = parse_expr ();
+ if (expr_in_parens == nullptr)
+ {
+ // skip after somewhere?
+ // error?
+ return nullptr;
+ }
+
+ if (!skip_token (RIGHT_PAREN))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::GroupedExpr> (
+ new AST::GroupedExpr (std::move (expr_in_parens), std::move (inner_attrs),
+ std::move (outer_attrs), locus));
+}
+
+// Parses a closure expression (closure definition).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ClosureExpr>
+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 nullptr;
+ }
+
+ // 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 nullptr;
+ }
+
+ // parse block expr, which is required
+ std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
+ if (block == nullptr)
+ {
+ // error
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse block expr in closure");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::ClosureExprInnerTyped> (
+ new AST::ClosureExprInnerTyped (std::move (type), std::move (block),
+ std::move (params), locus, has_move,
+ std::move (outer_attrs)));
+ }
+ else
+ {
+ // must be expr-only closure
+
+ // parse expr, which is required
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse expression in closure");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::ClosureExprInner> (
+ new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
+ has_move, std::move (outer_attrs)));
+ }
+}
+
+// Parses a literal token (to literal expression).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LiteralExpr>
+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 nullptr;
+ }
+
+ // 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>
+std::unique_ptr<AST::BoxExpr>
+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;
+
+ std::unique_ptr<AST::Expr> expr = parse_expr (AST::AttrVec (), restrictions);
+
+ return std::unique_ptr<AST::BoxExpr> (
+ new AST::BoxExpr (std::move (expr), std::move (outer_attrs), locus));
+}
+
+// Parses a return expression (including any expression to return).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ReturnExpr>
+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;
+ std::unique_ptr<AST::Expr> returned_expr
+ = parse_expr (AST::AttrVec (), restrictions);
+
+ return std::unique_ptr<AST::ReturnExpr> (
+ new AST::ReturnExpr (std::move (returned_expr), std::move (outer_attrs),
+ locus));
+}
+
+// Parses a try expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TryExpr>
+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);
+ }
+
+ std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+
+ if (!block_expr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse try block expression");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::TryExpr> (
+ new AST::TryExpr (std::move (block_expr), std::move (outer_attrs), locus));
+}
+
+/* Parses a break expression (including any label to break to AND any return
+ * expression). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::BreakExpr>
+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;
+ std::unique_ptr<AST::Expr> return_expr
+ = parse_expr (AST::AttrVec (), restrictions);
+
+ return std::unique_ptr<AST::BreakExpr> (
+ new AST::BreakExpr (std::move (label), std::move (return_expr),
+ std::move (outer_attrs), locus));
+}
+
+// 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::unique_ptr<AST::ContinueExpr> (
+ new 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>
+std::unique_ptr<AST::IfExpr>
+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 nullptr;
+ }
+ }
+
+ // 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 nullptr;
+ }
+
+ /* parse required condition expr - HACK to prevent struct expr from being
+ * parsed */
+ ParseRestrictions no_struct_expr;
+ no_struct_expr.can_be_struct_expr = false;
+ std::unique_ptr<AST::Expr> condition = parse_expr ({}, no_struct_expr);
+ if (condition == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse condition expression in if expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // parse required block expr
+ std::unique_ptr<AST::BlockExpr> if_body = parse_block_expr ();
+ if (if_body == nullptr)
+ return nullptr;
+
+ // 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), std::move (if_body),
+ 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)
+ std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr ();
+ if (else_body == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::IfExprConseqElse> (
+ new AST::IfExprConseqElse (std::move (condition),
+ std::move (if_body),
+ std::move (else_body),
+ 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)
+ std::unique_ptr<AST::IfLetExpr> if_let_expr
+ = parse_if_let_expr ();
+ if (if_let_expr == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::IfExprConseqElse> (
+ new AST::IfExprConseqElse (std::move (condition),
+ std::move (if_body),
+ std::move (if_let_expr),
+ std::move (outer_attrs), locus));
+ }
+ else
+ {
+ // parse if expr (required)
+ std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
+ if (if_expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse (else) if expression after "
+ "if expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::IfExprConseqElse> (
+ new AST::IfExprConseqElse (std::move (condition),
+ std::move (if_body),
+ std::move (if_expr),
+ 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 nullptr;
+ }
+ }
+}
+
+/* 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>
+std::unique_ptr<AST::IfLetExpr>
+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 nullptr;
+ }
+ }
+
+ // 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 nullptr;
+ }
+ lexer.skip_token ();
+
+ // parse match arm patterns (which are required)
+ std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns
+ = parse_match_arm_patterns (EQUAL);
+ if (match_arm_patterns.empty ())
+ {
+ 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 nullptr;
+ }
+
+ if (!skip_token (EQUAL))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // parse expression (required) - HACK to prevent struct expr being parsed
+ ParseRestrictions no_struct_expr;
+ no_struct_expr.can_be_struct_expr = false;
+ std::unique_ptr<AST::Expr> scrutinee_expr = parse_expr ({}, no_struct_expr);
+ if (scrutinee_expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse scrutinee expression in if let expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ /* TODO: check for expression not being a struct expression or lazy boolean
+ * expression here? or actually probably in semantic analysis. */
+
+ // parse block expression (required)
+ std::unique_ptr<AST::BlockExpr> if_let_body = parse_block_expr ();
+ if (if_let_body == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ // 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_patterns),
+ std::move (scrutinee_expr), std::move (if_let_body),
+ 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)
+ std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr ();
+ if (else_body == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::IfLetExprConseqElse> (
+ new AST::IfLetExprConseqElse (std::move (match_arm_patterns),
+ std::move (scrutinee_expr),
+ std::move (if_let_body),
+ std::move (else_body),
+ 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)
+ std::unique_ptr<AST::IfLetExpr> if_let_expr
+ = parse_if_let_expr ();
+ if (if_let_expr == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::IfLetExprConseqElse> (
+ new AST::IfLetExprConseqElse (
+ std::move (match_arm_patterns), std::move (scrutinee_expr),
+ std::move (if_let_body), std::move (if_let_expr),
+ std::move (outer_attrs), locus));
+ }
+ else
+ {
+ // parse if expr (required)
+ std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
+ if (if_expr == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::IfLetExprConseqElse> (
+ new AST::IfLetExprConseqElse (
+ std::move (match_arm_patterns), std::move (scrutinee_expr),
+ std::move (if_let_body), std::move (if_expr),
+ 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 nullptr;
+ }
+ }
+}
+
+/* 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>
+std::unique_ptr<AST::LoopExpr>
+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 nullptr;
+ }
+ }
+ else
+ {
+ if (label)
+ locus = label->get_locus ();
+ }
+
+ // parse loop body, which is required
+ std::unique_ptr<AST::BlockExpr> loop_body = parse_block_expr ();
+ if (loop_body == nullptr)
+ return nullptr;
+
+ return std::unique_ptr<AST::LoopExpr> (
+ new AST::LoopExpr (std::move (loop_body), 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>
+std::unique_ptr<AST::WhileLoopExpr>
+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 nullptr;
+ }
+ }
+ 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 nullptr;
+ }
+
+ // parse loop predicate (required) with HACK to prevent struct expr parsing
+ ParseRestrictions no_struct_expr;
+ no_struct_expr.can_be_struct_expr = false;
+ std::unique_ptr<AST::Expr> predicate = parse_expr ({}, no_struct_expr);
+ if (predicate == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse predicate expression in while loop");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ /* TODO: check that it isn't struct expression here? actually, probably in
+ * semantic analysis */
+
+ // parse loop body (required)
+ std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+ if (body == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::WhileLoopExpr> (
+ new AST::WhileLoopExpr (std::move (predicate), std::move (body), 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>
+std::unique_ptr<AST::WhileLetLoopExpr>
+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 nullptr;
+ }
+ // as this token is definitely let now, save the computation of comparison
+ lexer.skip_token ();
+
+ // parse predicate patterns
+ std::vector<std::unique_ptr<AST::Pattern>> predicate_patterns
+ = parse_match_arm_patterns (EQUAL);
+ // ensure that there is at least 1 pattern
+ if (predicate_patterns.empty ())
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "should be at least 1 pattern");
+ add_error (std::move (error));
+ return nullptr;
+ }
+
+ if (!skip_token (EQUAL))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ /* parse predicate expression, which is required (and HACK to prevent struct
+ * expr) */
+ ParseRestrictions no_struct_expr;
+ no_struct_expr.can_be_struct_expr = false;
+ std::unique_ptr<AST::Expr> predicate_expr = parse_expr ({}, no_struct_expr);
+ if (predicate_expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse predicate expression in while let loop");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ /* TODO: ensure that struct expression is not parsed? Actually, probably in
+ * semantic analysis. */
+
+ // parse loop body, which is required
+ std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+ if (body == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::WhileLetLoopExpr> (new AST::WhileLetLoopExpr (
+ std::move (predicate_patterns), std::move (predicate_expr),
+ std::move (body), 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>
+std::unique_ptr<AST::ForLoopExpr>
+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 == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse iterator pattern in for loop");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ if (!skip_token (IN))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ /* parse iterator expression, which is required - also HACK to prevent
+ * struct expr */
+ ParseRestrictions no_struct_expr;
+ no_struct_expr.can_be_struct_expr = false;
+ std::unique_ptr<AST::Expr> expr = parse_expr ({}, no_struct_expr);
+ if (expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse iterator expression in for loop");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ // TODO: check to ensure this isn't struct expr? Or in semantic analysis.
+
+ // parse loop body, which is required
+ std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+ if (body == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ return std::unique_ptr<AST::ForLoopExpr> (
+ new AST::ForLoopExpr (std::move (pattern), std::move (expr),
+ std::move (body), locus, std::move (label),
+ std::move (outer_attrs)));
+}
+
+// Parses a loop expression with label (any kind of loop - disambiguates).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
+ AST::AttrVec outer_attrs)
+{
+ /* 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 (tok->get_id () != LIFETIME)
+ {
+ 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));
+
+ // skip?
+ return nullptr;
+ }
+
+ // parse loop label (required)
+ // TODO: Convert this return type to tl::expected instead of tl::optional
+ auto parsed_label = parse_loop_label (tok);
+ if (!parsed_label)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse loop label in labelled loop expr");
+ add_error (std::move (error));
+
+ // skip?
+ return nullptr;
+ }
+
+ 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 nullptr;
+ }
+}
+
+// Parses a match expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MatchExpr>
+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;
+ std::unique_ptr<AST::Expr> scrutinee = parse_expr ({}, no_struct_expr);
+ if (scrutinee == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse scrutinee expression in match expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ /* TODO: check for scrutinee expr not being struct expr? or do so in
+ * semantic analysis */
+
+ if (!skip_token (LEFT_CURLY))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // 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 nullptr;
+ }
+
+ if (!skip_token (MATCH_ARROW))
+ {
+ // skip after somewhere?
+ // TODO is returning here a good idea? or is break better?
+ return nullptr;
+ }
+
+ ParseRestrictions restrictions;
+ restrictions.expr_can_be_stmt = true;
+
+ std::unique_ptr<AST::Expr> expr = parse_expr ({}, restrictions);
+
+ if (expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse expr in match arm in match expr");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ bool is_expr_without_block = expr->is_expr_without_block ();
+
+ match_arms.push_back (AST::MatchCase (std::move (arm), std::move (expr)));
+
+ // 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 nullptr;
+ }
+ else
+ {
+ // otherwise, must be final case, so fine
+ break;
+ }
+ }
+ lexer.skip_token ();
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ match_arms.shrink_to_fit ();
+
+ return std::unique_ptr<AST::MatchExpr> (
+ new AST::MatchExpr (std::move (scrutinee), std::move (match_arms),
+ std::move (inner_attrs), std::move (outer_attrs),
+ locus));
+}
+
+// Parses an async block expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AsyncBlockExpr>
+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)
+ std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+ if (block_expr == nullptr)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse block expression of async block expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::AsyncBlockExpr> (
+ new AST::AsyncBlockExpr (std::move (block_expr), has_move,
+ std::move (outer_attrs), locus));
+}
+
+// Parses an unsafe block expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::UnsafeBlockExpr>
+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)
+ std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+ if (block_expr == nullptr)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse block expression of unsafe block expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::UnsafeBlockExpr> (
+ new AST::UnsafeBlockExpr (std::move (block_expr), std::move (outer_attrs),
+ locus));
+}
+
+// Parses an array definition expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArrayExpr>
+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
+ std::unique_ptr<AST::Expr> initial_expr = parse_expr ();
+ if (initial_expr == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ {
+ // copy array elems
+ lexer.skip_token ();
+
+ // parse copy amount expression (required)
+ std::unique_ptr<AST::Expr> copy_amount = parse_expr ();
+ if (copy_amount == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ skip_token (RIGHT_SQUARE);
+
+ std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
+ new AST::ArrayElemsCopied (std::move (initial_expr),
+ std::move (copy_amount), 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));
+ 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));
+
+ 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)
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse element in array expression");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ exprs.push_back (std::move (expr));
+
+ 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 nullptr;
+ }
+ }
+}
+
+// Parses a grouped or tuple expression (disambiguates).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprWithoutBlock>
+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)
+ std::unique_ptr<AST::Expr> first_expr = parse_expr ();
+ if (first_expr == nullptr)
+ {
+ 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 nullptr;
+ }
+
+ // 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), 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));
+
+ // 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
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse expr in tuple expr");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+ exprs.push_back (std::move (expr));
+
+ 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 nullptr;
+ }
+}
+
+// Parses a struct expression field.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::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)
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse struct expression field with "
+ "identifier and expression");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
+ new AST::StructExprFieldIdentifierValue (std::move (ident),
+ std::move (expr),
+ 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 nullptr;
+ }
+
+ // parse field expression (required)
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse expr in struct (or enum) expr "
+ "field with tuple index");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::StructExprFieldIndexValue> (
+ new AST::StructExprFieldIndexValue (index, std::move (expr),
+ 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 nullptr;
+ 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 nullptr;
+ }
+}
+
+/* Pratt parser impl of parse_expr. FIXME: this is only provisional and
+ * probably will be changed. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::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 nullptr;
+ }
+
+ ParseRestrictions null_denotation_restrictions = restrictions;
+ null_denotation_restrictions.expr_can_be_stmt = false;
+
+ // parse null denotation (unary part of expression)
+ std::unique_ptr<AST::Expr> expr
+ = null_denotation ({}, null_denotation_restrictions);
+ if (expr == nullptr)
+ return nullptr;
+
+ return left_denotations (std::move (expr), right_binding_power,
+ std::move (outer_attrs), restrictions);
+}
+
+// Parse expression with lowest left binding power.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
+ ParseRestrictions restrictions)
+{
+ return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
+}
+
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::left_denotations (std::unique_ptr<AST::Expr> expr,
+ int right_binding_power,
+ AST::AttrVec outer_attrs,
+ ParseRestrictions restrictions)
+{
+ if (expr == nullptr)
+ {
+ // DEBUG
+ rust_debug ("null denotation is null; returning null for parse_expr");
+ return nullptr;
+ }
+
+ const_TokenPtr current_token = lexer.peek_token ();
+
+ if (restrictions.expr_can_be_stmt && !expr->is_expr_without_block ()
+ && current_token->get_id () != DOT
+ && current_token->get_id () != QUESTION_MARK)
+ {
+ rust_debug ("statement expression with block");
+ expr->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),
+ std::move (outer_attrs), restrictions);
+
+ if (expr == nullptr)
+ {
+ // DEBUG
+ rust_debug ("left denotation is null; returning null for parse_expr");
+
+ return nullptr;
+ }
+
+ current_token = lexer.peek_token ();
+ }
+
+ return expr;
+}
+
+/* Determines action to take when finding token at beginning of expression. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::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);
+ }
+ 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>
+std::unique_ptr<AST::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::unique_ptr<AST::PathInExpression> (
+ new AST::PathInExpression (std::move (path)));
+ }
+
+ // branch on next token
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case EXCLAM:
+ // macro
+ return parse_macro_invocation_partial (std::move (path),
+ std::move (outer_attrs));
+ 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)));
+ }
+ return parse_struct_expr_struct_partial (std::move (path),
+ std::move (outer_attrs));
+ }
+ 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::unique_ptr<AST::PathInExpression> (
+ new AST::PathInExpression (std::move (path)));
+ }
+ return parse_struct_expr_tuple_partial (std::move (path),
+ std::move (outer_attrs));
+ 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>
+std::unique_ptr<AST::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::unique_ptr<AST::QualifiedPathInExpression> (
+ new 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:
+ return parse_grouped_or_tuple_expr (std::move (outer_attrs),
+ tok->get_locus ());
+
+ /*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;
+ std::unique_ptr<AST::Expr> expr
+ = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
+
+ 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
+ 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::unique_ptr<AST::NegationExpr> (
+ new AST::NegationExpr (std::move (expr), 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;
+ std::unique_ptr<AST::Expr> expr
+ = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
+
+ if (expr == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::NegationExpr> (
+ new AST::NegationExpr (std::move (expr), 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;
+ std::unique_ptr<AST::Expr> expr
+ = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
+ // FIXME: allow outer attributes on expression
+ return std::unique_ptr<AST::DereferenceExpr> (
+ new AST::DereferenceExpr (std::move (expr), std::move (outer_attrs),
+ tok->get_locus ()));
+ }
+ case AMP:
+ {
+ // (single) "borrow" expression - shared (mutable) or immutable
+ std::unique_ptr<AST::Expr> expr = nullptr;
+ 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 ();
+ expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+ raw_borrow = true;
+ }
+ else if (t->get_id () == MUT)
+ {
+ lexer.skip_token ();
+ expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+ mutability = Mutability::Mut;
+ raw_borrow = false;
+ }
+ else
+ {
+ expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+ raw_borrow = false;
+ }
+
+ // FIXME: allow outer attributes on expression
+ return std::unique_ptr<AST::BorrowExpr> (
+ new AST::BorrowExpr (std::move (expr), 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 ();
+ expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+ mutability = Mutability::Mut;
+ }
+ else
+ {
+ expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+ mutability = Mutability::Imm;
+ }
+
+ // FIXME: allow outer attributes on expression
+ return std::unique_ptr<AST::BorrowExpr> (
+ new AST::BorrowExpr (std::move (expr), mutability, false, true,
+ std::move (outer_attrs), tok->get_locus ()));
+ }
+ case OR:
+ case PIPE:
+ case MOVE:
+ // closure expression
+ return parse_closure_expr_pratt (tok, std::move (outer_attrs));
+ case DOT_DOT:
+ // either "range to" or "range full" expressions
+ return parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
+ case DOT_DOT_EQ:
+ // range to inclusive expr
+ return parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
+ case RETURN_KW:
+ // FIXME: is this really a null denotation expression?
+ return parse_return_expr (std::move (outer_attrs), tok->get_locus ());
+ case TRY:
+ // FIXME: is this really a null denotation expression?
+ return parse_try_expr (std::move (outer_attrs), tok->get_locus ());
+ case BREAK:
+ // FIXME: is this really a null denotation expression?
+ return parse_break_expr (std::move (outer_attrs), tok->get_locus ());
+ 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.
+ return parse_block_expr (std::move (outer_attrs), tl::nullopt,
+ tok->get_locus ());
+ case IF:
+ // if or if let, so more lookahead to find out
+ if (lexer.peek_token ()->get_id () == LET)
+ {
+ // if let expr
+ return parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
+ }
+ else
+ {
+ // if expr
+ return parse_if_expr (std::move (outer_attrs), tok->get_locus ());
+ }
+ case LIFETIME:
+ return parse_labelled_loop_expr (tok, std::move (outer_attrs));
+ case LOOP:
+ return parse_loop_expr (std::move (outer_attrs), tl::nullopt,
+ tok->get_locus ());
+ case WHILE:
+ if (lexer.peek_token ()->get_id () == LET)
+ {
+ return parse_while_let_loop_expr (std::move (outer_attrs));
+ }
+ else
+ {
+ return parse_while_loop_expr (std::move (outer_attrs), tl::nullopt,
+ tok->get_locus ());
+ }
+ case FOR:
+ return parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
+ case MATCH_KW:
+ // also an expression with block
+ return parse_match_expr (std::move (outer_attrs), tok->get_locus ());
+ case LEFT_SQUARE:
+ // array definition expr (not indexing)
+ return parse_array_expr (std::move (outer_attrs), tok->get_locus ());
+ case UNSAFE:
+ return parse_unsafe_block_expr (std::move (outer_attrs),
+ tok->get_locus ());
+ case BOX:
+ return parse_box_expr (std::move (outer_attrs), tok->get_locus ());
+ 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 nullptr;
+ case CONST:
+ return parse_const_block_expr (std::move (outer_attrs),
+ tok->get_locus ());
+ 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 nullptr;
+ }
+}
+
+/* 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>
+std::unique_ptr<AST::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::unique_ptr<AST::ErrorPropagationExpr> (
+ new 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 nullptr;
+ 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 nullptr;
+ }
+}
+
+/* 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>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
+ AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ expr_type, locus));
+}
+
+// Parses a binary addition expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::ADD, locus));
+}
+
+// Parses a binary subtraction expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::SUBTRACT,
+ locus));
+}
+
+// Parses a binary multiplication expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::MULTIPLY,
+ locus));
+}
+
+// Parses a binary division expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::DIVIDE,
+ locus));
+}
+
+// Parses a binary modulo expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::MODULUS,
+ locus));
+}
+
+/* Parses a binary bitwise (or eager logical) and expression (with Pratt
+ * parsing). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::BITWISE_AND,
+ locus));
+}
+
+/* Parses a binary bitwise (or eager logical) or expression (with Pratt
+ * parsing). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::BITWISE_OR,
+ locus));
+}
+
+/* Parses a binary bitwise (or eager logical) xor expression (with Pratt
+ * parsing). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::BITWISE_XOR,
+ locus));
+}
+
+// Parses a binary left shift expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ ArithmeticOrLogicalOperator::LEFT_SHIFT,
+ locus));
+}
+
+// Parses a binary right shift expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ 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>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (get_lbp_for_comparison_expr (expr_type), AST::AttrVec (),
+ restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right), expr_type,
+ locus));
+}
+
+// Parses a binary equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ ComparisonOperator::EQUAL, locus));
+}
+
+// Parses a binary not equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ ComparisonOperator::NOT_EQUAL, locus));
+}
+
+// Parses a binary greater than expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ ComparisonOperator::GREATER_THAN, locus));
+}
+
+// Parses a binary less than expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ ComparisonOperator::LESS_THAN, locus));
+}
+
+// Parses a binary greater than or equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ ComparisonOperator::GREATER_OR_EQUAL, locus));
+}
+
+// Parses a binary less than or equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ ComparisonOperator::LESS_OR_EQUAL, locus));
+}
+
+// Parses a binary lazy boolean or expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LazyBooleanExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::LazyBooleanExpr> (
+ new AST::LazyBooleanExpr (std::move (left), std::move (right),
+ LazyBooleanOperator::LOGICAL_OR, locus));
+}
+
+// Parses a binary lazy boolean and expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LazyBooleanExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::LazyBooleanExpr> (
+ new AST::LazyBooleanExpr (std::move (left), std::move (right),
+ LazyBooleanOperator::LOGICAL_AND, locus));
+}
+
+// Parses a pseudo-binary infix type cast expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeCastExpr>
+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)
+ std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+ if (type == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::TypeCastExpr> (
+ new AST::TypeCastExpr (std::move (expr_to_cast), std::move (type), locus));
+}
+
+// Parses a binary assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::AssignmentExpr> (
+ new AST::AssignmentExpr (std::move (left), std::move (right),
+ 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>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
+ AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ expr_type, locus));
+}
+
+// Parses a binary add-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::ADD, locus));
+}
+
+// Parses a binary minus-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::SUBTRACT,
+ locus));
+}
+
+// Parses a binary multiplication-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::MULTIPLY,
+ locus));
+}
+
+// Parses a binary division-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::DIVIDE,
+ locus));
+}
+
+// Parses a binary modulo-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::MODULUS,
+ locus));
+}
+
+// Parses a binary and-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::BITWISE_AND,
+ locus));
+}
+
+// Parses a binary or-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::BITWISE_OR,
+ locus));
+}
+
+// Parses a binary xor-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::BITWISE_XOR,
+ locus));
+}
+
+// Parses a binary left shift-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::LEFT_SHIFT,
+ locus));
+}
+
+// Parses a binary right shift-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // 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::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ CompoundAssignmentOperator::RIGHT_SHIFT,
+ locus));
+}
+
+// Parses a postfix unary await expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AwaitExpr>
+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 nullptr;
+ }
+
+ // 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>
+std::unique_ptr<AST::RangeExpr>
+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;
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+
+ location_t locus = left->get_locus ();
+
+ if (right == nullptr)
+ {
+ // range from expr
+ return std::unique_ptr<AST::RangeFromExpr> (
+ new AST::RangeFromExpr (std::move (left), locus));
+ }
+ else
+ {
+ return std::unique_ptr<AST::RangeFromToExpr> (
+ new AST::RangeFromToExpr (std::move (left), std::move (right), locus));
+ }
+ // FIXME: make non-associative
+}
+
+/* Parses an exclusive range ('..') in null denotation position (i.e.
+ * RangeToExpr or RangeFullExpr). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+
+ location_t locus = tok->get_locus ();
+
+ if (right == nullptr)
+ {
+ // range from expr
+ return std::unique_ptr<AST::RangeFullExpr> (
+ new AST::RangeFullExpr (locus));
+ }
+ else
+ {
+ return std::unique_ptr<AST::RangeToExpr> (
+ new AST::RangeToExpr (std::move (right), locus));
+ }
+ // FIXME: make non-associative
+}
+
+// Parses a full binary range inclusive expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeFromToInclExpr>
+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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // FIXME: make non-associative
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = left->get_locus ();
+
+ return std::unique_ptr<AST::RangeFromToInclExpr> (
+ new AST::RangeFromToInclExpr (std::move (left), std::move (right), locus));
+}
+
+// Parses an inclusive range-to prefix unary expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeToInclExpr>
+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)
+ std::unique_ptr<AST::Expr> right = parse_expr (LBP_DOT_DOT_EQ);
+ if (right == nullptr)
+ return nullptr;
+ // FIXME: make non-associative
+
+ // TODO: check types. actually, do so during semantic analysis
+
+ return std::unique_ptr<AST::RangeToInclExpr> (
+ new AST::RangeToInclExpr (std::move (right), tok->get_locus ()));
+}
+
+// Parses a pseudo-binary infix tuple index expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TupleIndexExpr>
+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 nullptr;
+ }
+ 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::unique_ptr<AST::TupleIndexExpr> (
+ new 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>
+std::unique_ptr<AST::ArrayIndexExpr>
+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
+ std::unique_ptr<AST::Expr> index_expr = parse_expr ();
+ if (index_expr == nullptr)
+ return nullptr;
+
+ // skip ']' at end of array
+ if (!skip_token (RIGHT_SQUARE))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = array_expr->get_locus ();
+
+ return std::unique_ptr<AST::ArrayIndexExpr> (
+ new AST::ArrayIndexExpr (std::move (array_expr), std::move (index_expr),
+ std::move (outer_attrs), locus));
+}
+
+// Parses a pseudo-binary infix struct field access expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::FieldAccessExpr>
+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 nullptr;
+
+ Identifier ident{ident_tok};
+
+ location_t locus = struct_expr->get_locus ();
+
+ // TODO: check types. actually, do so during semantic analysis
+ return std::unique_ptr<AST::FieldAccessExpr> (
+ new 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>
+std::unique_ptr<AST::MethodCallExpr>
+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 nullptr;
+ }
+
+ // skip left parentheses
+ if (!skip_token (LEFT_PAREN))
+ {
+ return nullptr;
+ }
+
+ // 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)
+ {
+ std::unique_ptr<AST::Expr> param = parse_expr ();
+ if (param == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse method param in method call");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+ params.push_back (std::move (param));
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+
+ lexer.skip_token ();
+ t = lexer.peek_token ();
+ }
+
+ // skip right paren
+ if (!skip_token (RIGHT_PAREN))
+ {
+ return nullptr;
+ }
+
+ // TODO: check types. actually do so in semantic analysis pass.
+ location_t locus = receiver_expr->get_locus ();
+
+ return std::unique_ptr<AST::MethodCallExpr> (
+ new 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>
+std::unique_ptr<AST::CallExpr>
+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)
+ {
+ std::unique_ptr<AST::Expr> param = parse_expr ();
+ if (param == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse function param in function call");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+ params.push_back (std::move (param));
+
+ 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 nullptr;
+ }
+
+ // TODO: check types. actually, do so during semantic analysis
+ location_t locus = function_expr->get_locus ();
+
+ return std::unique_ptr<AST::CallExpr> (
+ new 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>
+std::unique_ptr<AST::StructExprStruct>
+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 nullptr;
+ }
+
+ // 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::unique_ptr<AST::StructExprStruct> (
+ new 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)
+ {
+ std::unique_ptr<AST::StructExprField> field
+ = parse_struct_expr_field ();
+ if (field == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse struct (or enum) expr field");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ // DEBUG:
+ rust_debug ("struct/enum expr field validated to not be null");
+
+ fields.push_back (std::move (field));
+
+ // 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
+ std::unique_ptr<AST::Expr> base_expr = parse_expr ();
+ if (base_expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse struct base expression in struct "
+ "expression");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ // DEBUG:
+ rust_debug ("struct/enum expr - parsed and validated base expr");
+
+ struct_base
+ = AST::StructBase (std::move (base_expr), dot_dot_location);
+
+ // DEBUG:
+ rust_debug ("assigned struct base to new struct base ");
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ return nullptr;
+ }
+
+ // DEBUG:
+ rust_debug (
+ "struct/enum expr skipped right curly - done and ready to return");
+
+ return std::unique_ptr<AST::StructExprStructFields> (
+ new 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 nullptr;
+ }
+}
+
+/* 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>
+std::unique_ptr<AST::CallExpr>
+Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
+ AST::PathInExpression path, AST::AttrVec outer_attrs)
+{
+ if (!skip_token (LEFT_PAREN))
+ {
+ return nullptr;
+ }
+
+ 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)
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (t->get_locus (), "failed to parse expression in "
+ "struct (or enum) expression tuple");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+ exprs.push_back (std::move (expr));
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+
+ lexer.skip_token ();
+
+ t = lexer.peek_token ();
+ }
+
+ if (!skip_token (RIGHT_PAREN))
+ {
+ return nullptr;
+ }
+
+ location_t path_locus = path.get_locus ();
+
+ auto pathExpr = std::unique_ptr<AST::PathInExpression> (
+ new AST::PathInExpression (std::move (path)));
+
+ return std::unique_ptr<AST::CallExpr> (
+ new 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>
+std::unique_ptr<AST::ClosureExpr>
+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 nullptr;
+ }
+ 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 nullptr;
+ }
+ break;
+ }
+ default:
+ add_error (Error (tok->get_locus (),
+ "unexpected token %qs in closure expression - expected "
+ "%<|%> or %<||%>",
+ tok->get_token_description ()));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // 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
+ std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+ if (type == nullptr)
+ {
+ // error
+ Error error (tok->get_locus (), "failed to parse type for closure");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // parse block expr, which is required
+ std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
+ if (block == nullptr)
+ {
+ // error
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse block expr in closure");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::ClosureExprInnerTyped> (
+ new AST::ClosureExprInnerTyped (std::move (type), std::move (block),
+ std::move (params), locus, has_move,
+ std::move (outer_attrs)));
+ }
+ else
+ {
+ // must be expr-only closure
+
+ // parse expr, which is required
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ Error error (tok->get_locus (),
+ "failed to parse expression in closure");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::ClosureExprInner> (
+ new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
+ has_move, std::move (outer_attrs)));
+ }
+}
+
+} // namespace Rust
#define INCLUDE_ALGORITHM
#include "rust-diagnostics.h"
#include "rust-dir-owner.h"
-#include "rust-attribute-values.h"
#include "rust-keyword-values.h"
#include "rust-edition.h"
#include "rust-parse-error.h"
#include "optional.h"
namespace Rust {
-// Left binding powers of operations.
-enum binding_powers
-{
- // Highest priority
- LBP_HIGHEST = 100,
-
- LBP_PATH = 95,
-
- LBP_METHOD_CALL = 90,
-
- LBP_FIELD_EXPR = 85,
-
- LBP_FUNCTION_CALL = 80,
- LBP_ARRAY_REF = LBP_FUNCTION_CALL,
-
- LBP_QUESTION_MARK = 75, // unary postfix - counts as left
-
- LBP_UNARY_PLUS = 70, // Used only when the null denotation is +
- LBP_UNARY_MINUS = LBP_UNARY_PLUS, // Used only when the null denotation is -
- LBP_UNARY_ASTERISK = LBP_UNARY_PLUS, // deref operator - unary prefix
- LBP_UNARY_EXCLAM = LBP_UNARY_PLUS,
- LBP_UNARY_AMP = LBP_UNARY_PLUS,
- LBP_UNARY_AMP_MUT = LBP_UNARY_PLUS,
-
- LBP_AS = 65,
-
- LBP_MUL = 60,
- LBP_DIV = LBP_MUL,
- LBP_MOD = LBP_MUL,
-
- LBP_PLUS = 55,
- LBP_MINUS = LBP_PLUS,
-
- LBP_L_SHIFT = 50,
- LBP_R_SHIFT = LBP_L_SHIFT,
-
- LBP_AMP = 45,
-
- LBP_CARET = 40,
-
- LBP_PIPE = 35,
-
- LBP_EQUAL = 30,
- LBP_NOT_EQUAL = LBP_EQUAL,
- LBP_SMALLER_THAN = LBP_EQUAL,
- LBP_SMALLER_EQUAL = LBP_EQUAL,
- LBP_GREATER_THAN = LBP_EQUAL,
- LBP_GREATER_EQUAL = LBP_EQUAL,
-
- LBP_LOGICAL_AND = 25,
-
- LBP_LOGICAL_OR = 20,
-
- LBP_DOT_DOT = 15,
- LBP_DOT_DOT_EQ = LBP_DOT_DOT,
-
- // TODO: note all these assig operators are RIGHT associative!
- LBP_ASSIG = 10,
- LBP_PLUS_ASSIG = LBP_ASSIG,
- LBP_MINUS_ASSIG = LBP_ASSIG,
- LBP_MULT_ASSIG = LBP_ASSIG,
- LBP_DIV_ASSIG = LBP_ASSIG,
- LBP_MOD_ASSIG = LBP_ASSIG,
- LBP_AMP_ASSIG = LBP_ASSIG,
- LBP_PIPE_ASSIG = LBP_ASSIG,
- LBP_CARET_ASSIG = LBP_ASSIG,
- LBP_L_SHIFT_ASSIG = LBP_ASSIG,
- LBP_R_SHIFT_ASSIG = LBP_ASSIG,
-
- // return, break, and closures as lowest priority?
- LBP_RETURN = 5,
- LBP_BREAK = LBP_RETURN,
- LBP_CLOSURE = LBP_RETURN, // unary prefix operators
-
-#if 0
- // rust precedences
- // used for closures
- PREC_CLOSURE = -40,
- // used for break, continue, return, and yield
- PREC_JUMP = -30,
- // used for range (although weird comment in rustc about this)
- PREC_RANGE = -10,
- // used for binary operators mentioned below - also cast, colon (type),
- // assign, assign_op
- PREC_BINOP = FROM_ASSOC_OP,
- // used for box, address_of, let, unary (again, weird comment on let)
- PREC_PREFIX = 50,
- // used for await, call, method call, field, index, try,
- // inline asm, macro invocation
- PREC_POSTFIX = 60,
- // used for array, repeat, tuple, literal, path, paren, if,
- // while, for, 'loop', match, block, try block, async, struct
- PREC_PAREN = 99,
- PREC_FORCE_PAREN = 100,
-#endif
-
- // lowest priority
- LBP_LOWEST = 0
-};
/* HACK-y special handling for skipping a right angle token at the end of
* generic arguments.
new AST::Crate (std::move (items), std::move (inner_attrs)));
}
-// Parse a contiguous block of inner attributes.
-template <typename ManagedTokenSource>
-AST::AttrVec
-Parser<ManagedTokenSource>::parse_inner_attributes ()
-{
- AST::AttrVec inner_attributes;
-
- auto has_valid_inner_attribute_prefix = [&] () {
- auto id = lexer.peek_token ()->get_id ();
- /* Outer attribute `#[` is not allowed, only accepts `#!` */
- return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
- || id == INNER_DOC_COMMENT;
- };
-
- while (has_valid_inner_attribute_prefix ())
- {
- auto inner_attr = parse_inner_attribute ();
-
- /* Ensure only valid inner attributes are added to the inner_attributes
- * list */
- if (inner_attr)
- {
- inner_attributes.push_back (std::move (inner_attr.value ()));
- }
- else
- {
- /* If no more valid inner attributes, break out of loop (only
- * contiguous inner attributes parsed). */
- break;
- }
- }
-
- inner_attributes.shrink_to_fit ();
- return inner_attributes;
-}
-
-// Parse a inner or outer doc comment into an doc attribute
+// Parses an identifier/keyword as a Token
template <typename ManagedTokenSource>
-std::tuple<AST::SimplePath, std::unique_ptr<AST::AttrInput>, location_t>
-Parser<ManagedTokenSource>::parse_doc_comment ()
+tl::expected<std::unique_ptr<AST::Token>, Parse::Error::Token>
+Parser<ManagedTokenSource>::parse_identifier_or_keyword_token ()
{
- const_TokenPtr token = lexer.peek_token ();
- location_t locus = token->get_locus ();
- AST::SimplePathSegment segment (Values::Attributes::DOC, locus);
- std::vector<AST::SimplePathSegment> segments;
- segments.push_back (std::move (segment));
- AST::SimplePath attr_path (std::move (segments), false, locus);
- AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING,
- PrimitiveCoreType::CORETYPE_STR, {}, locus);
- std::unique_ptr<AST::AttrInput> attr_input (
- new AST::AttrInputLiteral (std::move (lit_expr)));
- lexer.skip_token ();
- return std::make_tuple (std::move (attr_path), std::move (attr_input), locus);
-}
+ const_TokenPtr t = lexer.peek_token ();
-// Parse a single inner attribute.
-template <typename ManagedTokenSource>
-tl::expected<AST::Attribute, Parse::Error::Attribute>
-Parser<ManagedTokenSource>::parse_inner_attribute ()
-{
- if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
+ if (t->get_id () == IDENTIFIER || token_id_is_keyword (t->get_id ()))
{
- auto values = parse_doc_comment ();
- auto path = std::move (std::get<0> (values));
- auto input = std::move (std::get<1> (values));
- auto loc = std::get<2> (values);
- return AST::Attribute (std::move (path), std::move (input), loc, true);
+ lexer.skip_token ();
+ return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
}
-
- rust_assert (lexer.peek_token ()->get_id () == HASH);
-
- lexer.skip_token ();
-
- if (lexer.peek_token ()->get_id () != EXCLAM)
+ else
{
- Error error (lexer.peek_token ()->get_locus (),
- "expected %<!%> or %<[%> for inner attribute");
- add_error (std::move (error));
-
- return Parse::Error::Attribute::make_malformed ();
+ add_error (Error (t->get_locus (), "expected keyword or identifier"));
+ return Parse::Error::Token::make_malformed ();
}
- lexer.skip_token ();
-
- if (!skip_token (LEFT_SQUARE))
- return Parse::Error::Attribute::make_malformed ();
-
- auto body_res = parse_attribute_body ();
- if (!body_res)
- return Parse::Error::Attribute::make_malformed ();
- auto body = std::move (body_res.value ());
-
- auto actual_attribute
- = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
- true);
-
- if (!skip_token (RIGHT_SQUARE))
- return Parse::Error::Attribute::make_malformed ();
-
- return actual_attribute;
}
-// Parses the body of an attribute (inner or outer).
template <typename ManagedTokenSource>
-tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
-Parser<ManagedTokenSource>::parse_attribute_body ()
+bool
+Parser<ManagedTokenSource>::is_macro_rules_def (const_TokenPtr t)
{
- location_t locus = lexer.peek_token ()->get_locus ();
-
- auto attr_path = parse_simple_path ();
- // ensure path is valid to parse attribute input
- if (!attr_path)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "empty simple path in attribute");
- add_error (std::move (error));
+ auto macro_name = lexer.peek_token (2)->get_id ();
- // Skip past potential further info in attribute (i.e. attr_input)
- skip_after_end_attribute ();
- return Parse::Error::AttributeBody::make_invalid_path ();
- }
+ bool allowed_macro_name = (macro_name == IDENTIFIER || macro_name == TRY);
- auto attr_input = parse_attr_input ();
- // AttrInput is allowed to be null, so no checks here
- if (attr_input)
- return Parse::AttributeBody{std::move (attr_path.value ()),
- std::move (attr_input.value ()), locus};
- else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
- return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
- else
- return Parse::Error::AttributeBody::make_invalid_attrinput ();
+ return t->get_str () == Values::WeakKeywords::MACRO_RULES
+ && lexer.peek_token (1)->get_id () == EXCLAM && allowed_macro_name;
}
-// Parses a SimplePath AST node, if it exists. Does nothing otherwise.
+// Parses a single item
template <typename ManagedTokenSource>
-tl::expected<AST::SimplePath, Parse::Error::SimplePath>
-Parser<ManagedTokenSource>::parse_simple_path ()
+tl::expected<std::unique_ptr<AST::Item>, Parse::Error::Item>
+Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
{
- bool has_opening_scope_resolution = false;
- location_t locus = UNKNOWN_LOCATION;
-
- using Parse::Utils::is_simple_path_segment;
-
- // don't parse anything if not a path upfront
- if (!is_simple_path_segment (lexer.peek_token ()->get_id ())
- && !is_simple_path_segment (lexer.peek_token (1)->get_id ()))
- return Parse::Error::SimplePath::make_malformed ();
-
- /* Checks for opening scope resolution (i.e. global scope fully-qualified
- * path) */
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
- {
- has_opening_scope_resolution = true;
-
- locus = lexer.peek_token ()->get_locus ();
-
- lexer.skip_token ();
- }
-
- // Parse single required simple path segment
- auto segment = parse_simple_path_segment ();
-
- if (!segment)
- return Parse::Error::SimplePath::make_malformed ();
+ // has a "called_from_statement" parameter for better error message handling
- // get location if not gotten already
- if (locus == UNKNOWN_LOCATION)
- locus = segment->get_locus ();
+ // TODO: GCC 5 does not handle implicit return type correctly so we're forced
+ // to specify it almost every time until the baseline GCC gets bumped.
+ // Since this type is quite long and the code is dense we use an alias.
+ //
+ // When support for GCC 5 stops: remove this alias as well as the explicit
+ // ctor calls.
+ using RType = tl::expected<std::unique_ptr<AST::Item>, Parse::Error::Item>;
- std::vector<AST::SimplePathSegment> segments;
- segments.push_back (std::move (segment.value ()));
+ // parse outer attributes for item
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
+ const_TokenPtr t = lexer.peek_token ();
- // Parse all other simple path segments
- while (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+ switch (t->get_id ())
{
- auto new_segment = parse_simple_path_segment (1);
-
- using Error = Parse::Error::SimplePathSegment::Kind;
- // Return path as currently constructed if segment in error state.
- if (!new_segment)
+ case END_OF_FILE:
+ // not necessarily an error, unless we just read outer
+ // attributes which needs to be attached
+ if (!outer_attrs.empty ())
{
- if (new_segment.error ().kind == Error::INVALID_SIMPLE_PATH_TOKEN)
- break; /* Could be end of path */
- else /* Any other error is an hard error */
- return Parse::Error::SimplePath::make_malformed ();
+ Rust::AST::Attribute attr = outer_attrs.back ();
+ Error error (attr.get_locus (),
+ "expected item after outer attribute or doc comment");
+ add_error (std::move (error));
}
+ return Parse::Error::Item::make_end_of_file ();
- segments.push_back (std::move (new_segment.value ()));
- }
-
- return AST::SimplePath (std::move (segments), has_opening_scope_resolution,
- locus);
- /* TODO: now that is_simple_path_segment exists, could probably start
- * actually making errors upon parse failure of segments and whatever */
-}
-
-/* Parses a single SimplePathSegment (does not handle the scope resolution
- * operators)
- * Starts parsing at an offset of base_peek */
-template <typename ManagedTokenSource>
-tl::expected<AST::SimplePathSegment, Parse::Error::SimplePathSegment>
-Parser<ManagedTokenSource>::parse_simple_path_segment (int base_peek)
-{
- using namespace Values;
- const_TokenPtr t = lexer.peek_token (base_peek);
- switch (t->get_id ())
- {
- case IDENTIFIER:
- lexer.skip_token (base_peek);
-
- return AST::SimplePathSegment (t->get_str (), t->get_locus ());
+ case ASYNC:
+ case PUB:
+ case MOD:
+ case EXTERN_KW:
+ case USE:
+ case FN_KW:
+ case TYPE:
+ case STRUCT_KW:
+ case ENUM_KW:
+ case CONST:
+ case STATIC_KW:
+ case AUTO:
+ case TRAIT:
+ case IMPL:
+ case MACRO:
+ /* TODO: implement union keyword but not really because of
+ * context-dependence crappy hack way to parse a union written below to
+ * separate it from the good code. */
+ // case UNION:
+ case UNSAFE: // maybe - unsafe traits are a thing
+ // if any of these (should be all possible VisItem prefixes), parse a
+ // VisItem
+ {
+ auto vis_item = parse_vis_item (std::move (outer_attrs));
+ if (!vis_item)
+ return Parse::Error::Item::make_malformed ();
+ return RType{std::move (vis_item)};
+ }
case SUPER:
- lexer.skip_token (base_peek);
-
- return AST::SimplePathSegment (Keywords::SUPER, t->get_locus ());
case SELF:
- lexer.skip_token (base_peek);
-
- return AST::SimplePathSegment (Keywords::SELF, t->get_locus ());
case CRATE:
- lexer.skip_token (base_peek);
-
- return AST::SimplePathSegment (Keywords::CRATE, t->get_locus ());
case DOLLAR_SIGN:
- if (lexer.peek_token (base_peek + 1)->get_id () == CRATE)
+ // almost certainly macro invocation semi
+ {
+ auto macro_invoc_semi
+ = parse_macro_invocation_semi (std::move (outer_attrs));
+ if (!macro_invoc_semi)
+ return Parse::Error::Item::make_malformed ();
+ return RType{std::move (macro_invoc_semi)};
+ }
+ // crappy hack to do union "keyword"
+ case IDENTIFIER:
+ // TODO: ensure std::string and literal comparison works
+ if (t->get_str () == Values::WeakKeywords::UNION
+ && lexer.peek_token (1)->get_id () == IDENTIFIER)
{
- lexer.skip_token (base_peek + 1);
-
- return AST::SimplePathSegment ("$crate", t->get_locus ());
+ auto vis_item = parse_vis_item (std::move (outer_attrs));
+ if (!vis_item)
+ return Parse::Error::Item::make_malformed ();
+ return RType{std::move (vis_item)};
+ // or should this go straight to parsing union?
+ }
+ else if (t->get_str () == Values::WeakKeywords::DEFAULT
+ && lexer.peek_token (1)->get_id () != EXCLAM)
+ {
+ add_error (Error (t->get_locus (),
+ "%qs is only allowed on items within %qs blocks",
+ "default", "impl"));
+ return Parse::Error::Item::make_malformed ();
+ }
+ else if (is_macro_rules_def (t))
+ {
+ // macro_rules! macro item
+ auto macro_rule_def = parse_macro_rules_def (std::move (outer_attrs));
+ if (!macro_rule_def)
+ return Parse::Error::Item::make_malformed ();
+ return RType{std::move (macro_rule_def)};
+ }
+ else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
+ || lexer.peek_token (1)->get_id () == EXCLAM)
+ {
+ /* path (probably) or macro invocation, so probably a macro invocation
+ * semi */
+ auto macro_invocation_semi
+ = parse_macro_invocation_semi (std::move (outer_attrs));
+ if (!macro_invocation_semi)
+ return Parse::Error::Item::make_malformed ();
+ return RType{std::move (macro_invocation_semi)};
}
gcc_fallthrough ();
default:
- // do nothing but inactivates warning from gcc when compiling
- /* could put the rust_error_at thing here but fallthrough (from failing
- * $crate condition) isn't completely obvious if it is. */
+ // otherwise unrecognised
+ add_error (Error (t->get_locus (),
+ "unrecognised token %qs for start of %s",
+ t->get_token_description (),
+ called_from_statement ? "statement" : "item"));
- return Parse::Error::SimplePathSegment::make_invalid_token_or_path_end ();
+ // skip somewhere?
+ return Parse::Error::Item::make_malformed ();
+ break;
}
- rust_unreachable ();
}
-// Parses a PathIdentSegment - an identifier segment of a non-SimplePath path.
+// Parses a VisItem (item that can have non-default visibility).
template <typename ManagedTokenSource>
-tl::expected<AST::PathIdentSegment, Parse::Error::PathIdentSegment>
-Parser<ManagedTokenSource>::parse_path_ident_segment ()
+std::unique_ptr<AST::VisItem>
+Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
{
+ // parse visibility, which may or may not exist
+ auto vis_res = parse_visibility ();
+ if (!vis_res)
+ return nullptr;
+ auto vis = vis_res.value ();
+
+ // select VisItem to create depending on keyword
const_TokenPtr t = lexer.peek_token ();
+
switch (t->get_id ())
{
- case IDENTIFIER:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (t->get_str (), t->get_locus ());
- case SUPER:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (Values::Keywords::SUPER, t->get_locus ());
- case SELF:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (Values::Keywords::SELF, t->get_locus ());
- case SELF_ALIAS:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (Values::Keywords::SELF_ALIAS,
- t->get_locus ());
- case CRATE:
- lexer.skip_token ();
+ case MOD:
+ return parse_module (std::move (vis), std::move (outer_attrs));
+ case EXTERN_KW:
+ // lookahead to resolve syntactical production
+ t = lexer.peek_token (1);
- return AST::PathIdentSegment (Values::Keywords::CRATE, t->get_locus ());
- case DOLLAR_SIGN:
- if (lexer.peek_token (1)->get_id () == CRATE)
+ switch (t->get_id ())
{
- lexer.skip_token (1);
+ case CRATE:
+ return parse_extern_crate (std::move (vis), std::move (outer_attrs));
+ case FN_KW: // extern function
+ return parse_function (std::move (vis), std::move (outer_attrs));
+ case LEFT_CURLY: // extern block
+ return parse_extern_block (std::move (vis), std::move (outer_attrs));
+ case STRING_LITERAL: // for specifying extern ABI
+ // could be extern block or extern function, so more lookahead
+ t = lexer.peek_token (2);
- return AST::PathIdentSegment ("$crate", t->get_locus ());
- }
- gcc_fallthrough ();
- default:
- /* do nothing but inactivates warning from gcc when compiling
- * could put the error_at thing here but fallthrough (from failing $crate
- * condition) isn't completely obvious if it is. */
+ switch (t->get_id ())
+ {
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs));
+ case LEFT_CURLY:
+ return parse_extern_block (std::move (vis),
+ std::move (outer_attrs));
+ default:
+ add_error (
+ Error (t->get_locus (),
+ "unexpected token %qs in some sort of extern production",
+ t->get_token_description ()));
- // test prevent error
- return Parse::Error::PathIdentSegment::make_invalid_token ();
- }
- rust_unreachable ();
-}
+ lexer.skip_token (2); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ default:
+ add_error (
+ Error (t->get_locus (),
+ "unexpected token %qs in some sort of extern production",
+ t->get_token_description ()));
-// Parses an AttrInput AST node (polymorphic, as AttrInput is abstract)
-template <typename ManagedTokenSource>
-tl::expected<std::unique_ptr<AST::AttrInput>, Parse::Error::AttrInput>
-Parser<ManagedTokenSource>::parse_attr_input ()
-{
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case LEFT_PAREN:
- case LEFT_SQUARE:
- case LEFT_CURLY:
- {
- auto dtoken_tree = parse_delim_token_tree ();
- if (!dtoken_tree)
- return Parse::Error::AttrInput::make_bad_token_tree ();
-
- // must be a delimited token tree, so parse that
- std::unique_ptr<AST::AttrInput> input_tree (
- new AST::DelimTokenTree (dtoken_tree.value ()));
-
- return tl::expected<std::unique_ptr<AST::AttrInput>,
- Parse::Error::AttrInput>{std::move (input_tree)};
- }
- case EQUAL:
- {
- // = LiteralExpr
- lexer.skip_token ();
-
- t = lexer.peek_token ();
-
- // attempt to parse macro
- // TODO: macros may/may not be allowed in attributes
- // this is needed for "#[doc = include_str!(...)]"
- if (Parse::Utils::is_simple_path_segment (t->get_id ()))
- {
- std::unique_ptr<AST::MacroInvocation> invoke
- = parse_macro_invocation ({});
+ lexer.skip_token (1); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ case USE:
+ return parse_use_decl (std::move (vis), std::move (outer_attrs));
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs));
+ case TYPE:
+ return parse_type_alias (std::move (vis), std::move (outer_attrs));
+ case STRUCT_KW:
+ return parse_struct (std::move (vis), std::move (outer_attrs));
+ case ENUM_KW:
+ return parse_enum (std::move (vis), std::move (outer_attrs));
+ // TODO: implement union keyword but not really because of
+ // context-dependence case UNION: crappy hack to do union "keyword"
+ case IDENTIFIER:
+ if (t->get_str () == Values::WeakKeywords::UNION
+ && lexer.peek_token (1)->get_id () == IDENTIFIER)
+ {
+ return parse_union (std::move (vis), std::move (outer_attrs));
+ // or should item switch go straight to parsing union?
+ }
+ else
+ {
+ break;
+ }
+ case CONST:
+ // lookahead to resolve syntactical production
+ t = lexer.peek_token (1);
- if (!invoke)
- return Parse::Error::AttrInput::make_bad_macro_invocation ();
+ switch (t->get_id ())
+ {
+ case IDENTIFIER:
+ case UNDERSCORE:
+ return parse_const_item (std::move (vis), std::move (outer_attrs));
+ case ASYNC:
+ return parse_async_item (std::move (vis), std::move (outer_attrs));
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs));
+ default:
+ add_error (
+ Error (t->get_locus (),
+ "unexpected token %qs in some sort of const production",
+ t->get_token_description ()));
- return std::unique_ptr<AST::AttrInput> (
- new AST::AttrInputMacro (std::move (invoke)));
- }
+ lexer.skip_token (1); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ // for async functions
+ case ASYNC:
+ return parse_async_item (std::move (vis), std::move (outer_attrs));
- /* Ensure token is a "literal expression" (literally only a literal
- * token of any type) */
- if (!t->is_literal ())
- {
- Error error (
- t->get_locus (),
- "unknown token %qs in attribute body - literal expected",
- t->get_token_description ());
- add_error (std::move (error));
+ case STATIC_KW:
+ return parse_static_item (std::move (vis), std::move (outer_attrs));
+ case AUTO:
+ case TRAIT:
+ return parse_trait (std::move (vis), std::move (outer_attrs));
+ case IMPL:
+ return parse_impl (std::move (vis), std::move (outer_attrs));
+ case UNSAFE: // unsafe traits, unsafe functions, unsafe impls (trait impls),
+ // lookahead to resolve syntactical production
+ t = lexer.peek_token (1);
- skip_after_end_attribute ();
- return Parse::Error::AttrInput::make_malformed ();
- }
+ switch (t->get_id ())
+ {
+ case AUTO:
+ case TRAIT:
+ return parse_trait (std::move (vis), std::move (outer_attrs));
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs));
+ case IMPL:
+ return parse_impl (std::move (vis), std::move (outer_attrs));
+ case MOD:
+ return parse_module (std::move (vis), std::move (outer_attrs));
+ default:
+ add_error (
+ Error (t->get_locus (),
+ "unexpected token %qs in some sort of unsafe production",
+ t->get_token_description ()));
- AST::Literal::LitType lit_type = AST::Literal::STRING;
- // Crappy mapping of token type to literal type
- switch (t->get_id ())
- {
- case INT_LITERAL:
- lit_type = AST::Literal::INT;
- break;
- case FLOAT_LITERAL:
- lit_type = AST::Literal::FLOAT;
- break;
- case CHAR_LITERAL:
- lit_type = AST::Literal::CHAR;
- break;
- case BYTE_CHAR_LITERAL:
- lit_type = AST::Literal::BYTE;
- break;
- case BYTE_STRING_LITERAL:
- lit_type = AST::Literal::BYTE_STRING;
- break;
- case RAW_STRING_LITERAL:
- lit_type = AST::Literal::RAW_STRING;
- break;
- case STRING_LITERAL:
- default:
- lit_type = AST::Literal::STRING;
- break; // TODO: raw string? don't eliminate it from lexer?
- }
+ lexer.skip_token (1); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ case MACRO:
+ return parse_decl_macro_def (std::move (vis), std::move (outer_attrs));
+ default:
+ // otherwise vis item clearly doesn't exist, which is not an error
+ // has a catch-all post-switch return to allow other breaks to occur
+ break;
+ }
+ return nullptr;
+}
- // create actual LiteralExpr
- AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
- {}, t->get_locus ());
- lexer.skip_token ();
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Function>
+Parser<ManagedTokenSource>::parse_async_item (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
+{
+ auto offset = (lexer.peek_token ()->get_id () == CONST) ? 1 : 0;
+ const_TokenPtr t = lexer.peek_token (offset);
- std::unique_ptr<AST::AttrInput> attr_input_lit (
- new AST::AttrInputLiteral (std::move (lit_expr)));
+ if (get_rust_edition () == Edition::E2015)
+ {
+ add_error (Error (t->get_locus (), ErrorCode::E0670,
+ "%<async fn%> is not permitted in Rust 2015"));
+ add_error (
+ Error::Hint (t->get_locus (),
+ "to use %<async fn%>, switch to Rust 2018 or later"));
+ }
- // do checks or whatever? none required, really
+ t = lexer.peek_token (offset + 1);
- // FIXME: shouldn't a skip token be required here?
+ switch (t->get_id ())
+ {
+ case UNSAFE:
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs));
- return tl::expected<std::unique_ptr<AST::AttrInput>,
- Parse::Error::AttrInput>{
- std::move (attr_input_lit)};
- }
- break;
- case RIGHT_PAREN:
- case RIGHT_SQUARE:
- case RIGHT_CURLY:
- case END_OF_FILE:
- // means AttrInput is missing, which is allowed
- return Parse::Error::AttrInput::make_missing_attrinput ();
default:
add_error (
- Error (t->get_locus (),
- "unknown token %qs in attribute body - attribute input or "
- "none expected",
- t->get_token_description ()));
+ Error (t->get_locus (), "expected item, found keyword %<async%>"));
- skip_after_end_attribute ();
- return Parse::Error::AttrInput::make_malformed ();
+ lexer.skip_token (1);
+ return nullptr;
}
- rust_unreachable ();
- // TODO: find out how to stop gcc error on "no return value"
}
-// Parses a delimited token tree
+// Parses a macro rules definition syntax extension whatever thing.
template <typename ManagedTokenSource>
-tl::expected<AST::DelimTokenTree, Parse::Error::DelimTokenTree>
-Parser<ManagedTokenSource>::parse_delim_token_tree ()
+std::unique_ptr<AST::MacroRulesDefinition>
+Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
{
+ // ensure that first token is identifier saying "macro_rules"
const_TokenPtr t = lexer.peek_token ();
+ if (t->get_id () != IDENTIFIER
+ || t->get_str () != Values::WeakKeywords::MACRO_RULES)
+ {
+ Error error (
+ t->get_locus (),
+ "macro rules definition does not start with %<macro_rules%>");
+ add_error (std::move (error));
+
+ // skip after somewhere?
+ return nullptr;
+ }
lexer.skip_token ();
- location_t initial_loc = t->get_locus ();
+ location_t macro_locus = t->get_locus ();
+
+ if (!skip_token (EXCLAM))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
+
+ // parse macro name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ {
+ return nullptr;
+ }
+ Identifier rule_name{ident_tok};
+
+ // DEBUG
+ rust_debug ("in macro rules def, about to parse parens.");
// save delim type to ensure it is reused later
AST::DelimType delim_type = AST::PARENS;
// Map tokens to DelimType
+ t = lexer.peek_token ();
switch (t->get_id ())
{
case LEFT_PAREN:
default:
add_error (Error (t->get_locus (),
"unexpected token %qs - expecting delimiters (for a "
- "delimited token tree)",
+ "macro rules definition)",
t->get_token_description ()));
- return Parse::Error::DelimTokenTree::make_expected_delimiter ();
+ return nullptr;
+ }
+ lexer.skip_token ();
+
+ // parse actual macro rules
+ std::vector<AST::MacroRule> macro_rules;
+
+ // must be at least one macro rule, so parse it
+ AST::MacroRule initial_rule = parse_macro_rule ();
+ if (initial_rule.is_error ())
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "required first macro rule in macro rules definition "
+ "could not be parsed");
+ add_error (std::move (error));
+
+ // skip after somewhere?
+ return nullptr;
}
+ macro_rules.push_back (std::move (initial_rule));
- // parse actual token tree vector - 0 or more
- std::vector<std::unique_ptr<AST::TokenTree>> token_trees_in_tree;
- auto delim_open
- = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
- token_trees_in_tree.push_back (std::move (delim_open));
+ // DEBUG
+ rust_debug ("successfully pushed back initial macro rule");
- // repeat loop until finding the matching delimiter
t = lexer.peek_token ();
- while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type)
- && t->get_id () != END_OF_FILE)
+ // parse macro rules
+ while (t->get_id () == SEMICOLON)
{
- auto tok_tree = parse_token_tree ();
- if (!tok_tree)
- return Parse::Error::DelimTokenTree::make_invalid_token_tree ();
+ // skip semicolon
+ lexer.skip_token ();
+
+ // don't parse if end of macro rules
+ if (Parse::Utils::token_id_matches_delims (lexer.peek_token ()->get_id (),
+ delim_type))
+ {
+ // DEBUG
+ rust_debug (
+ "broke out of parsing macro rules loop due to finding delim");
- token_trees_in_tree.push_back (std::move (tok_tree.value ()));
+ break;
+ }
+
+ // try to parse next rule
+ AST::MacroRule rule = parse_macro_rule ();
+ if (rule.is_error ())
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse macro rule in macro rules definition");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ macro_rules.push_back (std::move (rule));
+
+ // DEBUG
+ rust_debug ("successfully pushed back another macro rule");
- // lexer.skip_token();
t = lexer.peek_token ();
}
- auto delim_close
- = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
- token_trees_in_tree.push_back (std::move (delim_close));
-
- AST::DelimTokenTree token_tree (delim_type, std::move (token_trees_in_tree),
- initial_loc);
// parse end delimiters
t = lexer.peek_token ();
-
if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
{
// tokens match opening delimiter, so skip.
lexer.skip_token ();
- return token_tree;
- }
- else
- {
- // tokens don't match opening delimiters, so produce error
- Error error (t->get_locus (),
- "unexpected token %qs - expecting closing delimiter %qs "
- "(for a delimited token tree)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
- add_error (std::move (error));
- return Parse::Error::DelimTokenTree::make_mismatched_delimiters ();
- }
-}
-
-// Parses an identifier/keyword as a Token
-template <typename ManagedTokenSource>
-tl::expected<std::unique_ptr<AST::Token>, Parse::Error::Token>
-Parser<ManagedTokenSource>::parse_identifier_or_keyword_token ()
-{
- const_TokenPtr t = lexer.peek_token ();
+ if (delim_type != AST::CURLY)
+ {
+ // skip semicolon at end of non-curly macro definitions
+ if (!skip_token (SEMICOLON))
+ {
+ // as this is the end, allow recovery (probably) - may change
+ return std::unique_ptr<AST::MacroRulesDefinition> (
+ AST::MacroRulesDefinition::mbe (
+ std::move (rule_name), delim_type, std::move (macro_rules),
+ std::move (outer_attrs), macro_locus));
+ }
+ }
- if (t->get_id () == IDENTIFIER || token_id_is_keyword (t->get_id ()))
- {
- lexer.skip_token ();
- return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+ return std::unique_ptr<AST::MacroRulesDefinition> (
+ AST::MacroRulesDefinition::mbe (std::move (rule_name), delim_type,
+ std::move (macro_rules),
+ std::move (outer_attrs), macro_locus));
}
else
{
- add_error (Error (t->get_locus (), "expected keyword or identifier"));
- return Parse::Error::Token::make_malformed ();
+ // tokens don't match opening delimiters, so produce error
+ Error error (t->get_locus (),
+ "unexpected token %qs - expecting closing delimiter %qs "
+ "(for a macro rules definition)",
+ t->get_token_description (),
+ (delim_type == AST::PARENS
+ ? ")"
+ : (delim_type == AST::SQUARE ? "]" : "}")));
+ add_error (std::move (error));
+
+ /* return empty macro definiton despite possibly parsing mostly valid one
+ * - TODO is this a good idea? */
+ return nullptr;
}
}
-/* Parses a TokenTree syntactical production. This is either a delimited token
- * tree or a non-delimiter token. */
+// Parses a declarative macro 2.0 definition.
template <typename ManagedTokenSource>
-tl::expected<std::unique_ptr<AST::TokenTree>, Parse::Error::TokenTree>
-Parser<ManagedTokenSource>::parse_token_tree ()
+std::unique_ptr<AST::MacroRulesDefinition>
+Parser<ManagedTokenSource>::parse_decl_macro_def (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
+ // ensure that first token is identifier saying "macro"
const_TokenPtr t = lexer.peek_token ();
-
- switch (t->get_id ())
+ if (t->get_id () != MACRO)
{
- case LEFT_PAREN:
- case LEFT_SQUARE:
- case LEFT_CURLY:
- {
- // Parse delimited token tree
- auto delim_token_tree = parse_delim_token_tree ();
- if (!delim_token_tree)
- return Parse::Error::TokenTree::
- make_malformed_delimited_token_tree ();
-
- // TODO: use move rather than copy constructor
- return std::unique_ptr<AST::DelimTokenTree> (
- new AST::DelimTokenTree (delim_token_tree.value ()));
- }
- case RIGHT_PAREN:
- case RIGHT_SQUARE:
- case RIGHT_CURLY:
- // error - should not be called when this a token
- add_error (Error (t->get_locus (), "unexpected closing delimiter %qs",
- t->get_token_description ()));
-
- add_error (Error (Error::Kind::Hint, t->get_locus (),
- "token tree requires either paired delimiters or "
- "non-delimiter tokens"));
+ Error error (
+ t->get_locus (),
+ "declarative macro definition does not start with %<macro%>");
+ add_error (std::move (error));
- lexer.skip_token ();
- return Parse::Error::TokenTree::make_malformed ();
- default:
- // parse token itself as TokenTree
- lexer.skip_token ();
- return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+ // skip after somewhere?
+ return nullptr;
}
-}
+ lexer.skip_token ();
+ location_t macro_locus = t->get_locus ();
-template <typename ManagedTokenSource>
-bool
-Parser<ManagedTokenSource>::is_macro_rules_def (const_TokenPtr t)
-{
- auto macro_name = lexer.peek_token (2)->get_id ();
+ // parse macro name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ {
+ return nullptr;
+ }
+ Identifier rule_name{ident_tok};
- bool allowed_macro_name = (macro_name == IDENTIFIER || macro_name == TRY);
+ t = lexer.peek_token ();
+ if (t->get_id () == LEFT_PAREN)
+ {
+ // single definiton of macro rule
+ // e.g. `macro foo($e:expr) {}`
- return t->get_str () == Values::WeakKeywords::MACRO_RULES
- && lexer.peek_token (1)->get_id () == EXCLAM && allowed_macro_name;
-}
+ // parse macro matcher
+ location_t locus = lexer.peek_token ()->get_locus ();
+ AST::MacroMatcher matcher = parse_macro_matcher ();
+ if (matcher.is_error ())
+ return nullptr;
-// Parses a single item
-template <typename ManagedTokenSource>
-tl::expected<std::unique_ptr<AST::Item>, Parse::Error::Item>
-Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
-{
- // has a "called_from_statement" parameter for better error message handling
+ // check delimiter of macro matcher
+ if (matcher.get_delim_type () != AST::DelimType::PARENS)
+ {
+ Error error (locus, "only parenthesis can be used for a macro "
+ "matcher in declarative macro definition");
+ add_error (std::move (error));
+ return nullptr;
+ }
- // TODO: GCC 5 does not handle implicit return type correctly so we're forced
- // to specify it almost every time until the baseline GCC gets bumped.
- // Since this type is quite long and the code is dense we use an alias.
- //
- // When support for GCC 5 stops: remove this alias as well as the explicit
- // ctor calls.
- using RType = tl::expected<std::unique_ptr<AST::Item>, Parse::Error::Item>;
+ location_t transcriber_loc = lexer.peek_token ()->get_locus ();
+ auto delim_tok_tree = parse_delim_token_tree ();
+ if (!delim_tok_tree)
+ return nullptr;
- // parse outer attributes for item
- AST::AttrVec outer_attrs = parse_outer_attributes ();
- const_TokenPtr t = lexer.peek_token ();
+ AST::MacroTranscriber transcriber (delim_tok_tree.value (),
+ transcriber_loc);
- switch (t->get_id ())
- {
- case END_OF_FILE:
- // not necessarily an error, unless we just read outer
- // attributes which needs to be attached
- if (!outer_attrs.empty ())
+ if (transcriber.get_token_tree ().get_delim_type ()
+ != AST::DelimType::CURLY)
{
- Rust::AST::Attribute attr = outer_attrs.back ();
- Error error (attr.get_locus (),
- "expected item after outer attribute or doc comment");
+ Error error (transcriber_loc,
+ "only braces can be used for a macro transcriber "
+ "in declarative macro definition");
add_error (std::move (error));
+ return nullptr;
}
- return Parse::Error::Item::make_end_of_file ();
- case ASYNC:
- case PUB:
- case MOD:
- case EXTERN_KW:
- case USE:
- case FN_KW:
- case TYPE:
- case STRUCT_KW:
- case ENUM_KW:
- case CONST:
- case STATIC_KW:
- case AUTO:
- case TRAIT:
- case IMPL:
- case MACRO:
- /* TODO: implement union keyword but not really because of
- * context-dependence crappy hack way to parse a union written below to
- * separate it from the good code. */
- // case UNION:
- case UNSAFE: // maybe - unsafe traits are a thing
- // if any of these (should be all possible VisItem prefixes), parse a
- // VisItem
- {
- auto vis_item = parse_vis_item (std::move (outer_attrs));
- if (!vis_item)
- return Parse::Error::Item::make_malformed ();
- return RType{std::move (vis_item)};
- }
- case SUPER:
- case SELF:
- case CRATE:
- case DOLLAR_SIGN:
- // almost certainly macro invocation semi
- {
- auto macro_invoc_semi
- = parse_macro_invocation_semi (std::move (outer_attrs));
- if (!macro_invoc_semi)
- return Parse::Error::Item::make_malformed ();
- return RType{std::move (macro_invoc_semi)};
- }
- // crappy hack to do union "keyword"
- case IDENTIFIER:
- // TODO: ensure std::string and literal comparison works
- if (t->get_str () == Values::WeakKeywords::UNION
- && lexer.peek_token (1)->get_id () == IDENTIFIER)
- {
- auto vis_item = parse_vis_item (std::move (outer_attrs));
- if (!vis_item)
- return Parse::Error::Item::make_malformed ();
- return RType{std::move (vis_item)};
- // or should this go straight to parsing union?
- }
- else if (t->get_str () == Values::WeakKeywords::DEFAULT
- && lexer.peek_token (1)->get_id () != EXCLAM)
- {
- add_error (Error (t->get_locus (),
- "%qs is only allowed on items within %qs blocks",
- "default", "impl"));
- return Parse::Error::Item::make_malformed ();
- }
- else if (is_macro_rules_def (t))
- {
- // macro_rules! macro item
- auto macro_rule_def = parse_macro_rules_def (std::move (outer_attrs));
- if (!macro_rule_def)
- return Parse::Error::Item::make_malformed ();
- return RType{std::move (macro_rule_def)};
- }
- else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
- || lexer.peek_token (1)->get_id () == EXCLAM)
- {
- /* path (probably) or macro invocation, so probably a macro invocation
- * semi */
- auto macro_invocation_semi
- = parse_macro_invocation_semi (std::move (outer_attrs));
- if (!macro_invocation_semi)
- return Parse::Error::Item::make_malformed ();
- return RType{std::move (macro_invocation_semi)};
- }
- gcc_fallthrough ();
- default:
- // otherwise unrecognised
- add_error (Error (t->get_locus (),
- "unrecognised token %qs for start of %s",
- t->get_token_description (),
- called_from_statement ? "statement" : "item"));
+ std::vector<AST::MacroRule> macro_rules;
+ macro_rules.emplace_back (std::move (matcher), std::move (transcriber),
+ locus);
- // skip somewhere?
- return Parse::Error::Item::make_malformed ();
- break;
+ return std::unique_ptr<AST::MacroRulesDefinition> (
+ AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
+ macro_rules,
+ std::move (outer_attrs),
+ macro_locus, vis));
}
-}
-
-// Parses a contiguous block of outer attributes.
-template <typename ManagedTokenSource>
-AST::AttrVec
-Parser<ManagedTokenSource>::parse_outer_attributes ()
-{
- AST::AttrVec outer_attributes;
-
- auto has_valid_attribute_prefix = [&] () {
- auto id = lexer.peek_token ()->get_id ();
- /* We allow inner attributes `#!` and catch the error later */
- return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
- };
-
- while (has_valid_attribute_prefix ()) /* For error handling. */
+ else if (t->get_id () == LEFT_CURLY)
{
- auto outer_attr = parse_outer_attribute ();
+ // multiple definitions of macro rule separated by comma
+ // e.g. `macro foo { () => {}, ($e:expr) => {}, }`
- /* Ensure only valid outer attributes are added to the outer_attributes
- * list */
- if (outer_attr)
+ // parse left curly
+ const_TokenPtr left_curly = expect_token (LEFT_CURLY);
+ if (left_curly == nullptr)
{
- outer_attributes.push_back (std::move (outer_attr.value ()));
+ return nullptr;
}
- else
+
+ // parse actual macro rules
+ std::vector<AST::MacroRule> macro_rules;
+
+ // must be at least one macro rule, so parse it
+ AST::MacroRule initial_rule = parse_macro_rule ();
+ if (initial_rule.is_error ())
{
- /* If no more valid outer attributes, break out of loop (only
- * contiguous outer attributes parsed). */
- break;
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "required first macro rule in declarative macro definition "
+ "could not be parsed");
+ add_error (std::move (error));
+
+ // skip after somewhere?
+ return nullptr;
}
- }
+ macro_rules.push_back (std::move (initial_rule));
+
+ t = lexer.peek_token ();
+ // parse macro rules
+ while (t->get_id () == COMMA)
+ {
+ // skip comma
+ lexer.skip_token ();
+
+ // don't parse if end of macro rules
+ if (Parse::Utils::token_id_matches_delims (
+ lexer.peek_token ()->get_id (), AST::CURLY))
+ {
+ break;
+ }
- outer_attributes.shrink_to_fit ();
- return outer_attributes;
+ // try to parse next rule
+ AST::MacroRule rule = parse_macro_rule ();
+ if (rule.is_error ())
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse macro rule in declarative macro definition");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ macro_rules.push_back (std::move (rule));
+
+ t = lexer.peek_token ();
+ }
- /* TODO: this shares basically all code with parse_inner_attributes except
- * function call - find way of making it more modular? function pointer? */
+ // parse right curly
+ const_TokenPtr right_curly = expect_token (RIGHT_CURLY);
+ if (right_curly == nullptr)
+ {
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::MacroRulesDefinition> (
+ AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
+ std::move (macro_rules),
+ std::move (outer_attrs),
+ macro_locus, vis));
+ }
+ else
+ {
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs - expecting delimiters "
+ "(for a declarative macro definiton)",
+ t->get_token_description ()));
+ return nullptr;
+ }
}
-// Parse a single outer attribute.
+/* Parses a visibility syntactical production (i.e. creating a non-default
+ * visibility) */
template <typename ManagedTokenSource>
-tl::expected<AST::Attribute, Parse::Error::Attribute>
-Parser<ManagedTokenSource>::parse_outer_attribute ()
+tl::expected<AST::Visibility, Parse::Error::Visibility>
+Parser<ManagedTokenSource>::parse_visibility ()
{
- if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT)
+ // check for no visibility
+ if (lexer.peek_token ()->get_id () != PUB)
{
- auto values = parse_doc_comment ();
- auto path = std::move (std::get<0> (values));
- auto input = std::move (std::get<1> (values));
- auto loc = std::get<2> (values);
- return AST::Attribute (std::move (path), std::move (input), loc, false);
+ return AST::Visibility::create_private ();
}
- if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
+ auto vis_loc = lexer.peek_token ()->get_locus ();
+ lexer.skip_token ();
+
+ // create simple pub visibility if
+ // - found no parentheses
+ // - found unit type `()`
+ if (lexer.peek_token ()->get_id () != LEFT_PAREN
+ || lexer.peek_token (1)->get_id () == RIGHT_PAREN)
{
- Error error (
- lexer.peek_token ()->get_locus (), ErrorCode::E0753,
- "expected outer doc comment, inner doc (%<//!%> or %</*!%>) only "
- "allowed at start of item "
- "and before any outer attribute or doc (%<#[%>, %<///%> or %</**%>)");
- add_error (std::move (error));
- lexer.skip_token ();
- return Parse::Error::Attribute::make_unexpected_inner ();
+ return AST::Visibility::create_public (vis_loc);
+ // or whatever
}
- /* OuterAttribute -> '#' '[' Attr ']' */
-
- if (lexer.peek_token ()->get_id () != HASH)
- return Parse::Error::Attribute::make_malformed ();
-
lexer.skip_token ();
- TokenId id = lexer.peek_token ()->get_id ();
- if (id != LEFT_SQUARE)
+ const_TokenPtr t = lexer.peek_token ();
+ auto path_loc = t->get_locus ();
+
+ switch (t->get_id ())
{
- if (id == EXCLAM)
- {
- // this is inner attribute syntax, so throw error
- // inner attributes were either already parsed or not allowed here.
- Error error (
- lexer.peek_token ()->get_locus (),
- "token %<!%> found, indicating inner attribute definition. Inner "
- "attributes are not possible at this location");
- add_error (std::move (error));
- }
- return Parse::Error::Attribute::make_unexpected_inner ();
- }
+ case CRATE:
+ lexer.skip_token ();
- lexer.skip_token ();
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_crate (path_loc, vis_loc);
+ case SELF:
+ lexer.skip_token ();
+
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_self (path_loc, vis_loc);
+ case SUPER:
+ lexer.skip_token ();
- auto body_res = parse_attribute_body ();
- if (!body_res)
- return Parse::Error::Attribute::make_malformed_body ();
- auto body = std::move (body_res.value ());
+ skip_token (RIGHT_PAREN);
- auto actual_attribute
- = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
- false);
+ return AST::Visibility::create_super (path_loc, vis_loc);
+ case IN:
+ {
+ lexer.skip_token ();
- if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
- return Parse::Error::Attribute::make_malformed ();
+ // parse the "in" path as well
+ auto path = parse_simple_path ();
+ if (!path)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "missing path in pub(in path) visibility");
+ add_error (std::move (error));
- lexer.skip_token ();
+ // skip after somewhere?
+ return Parse::Error::Visibility::make_missing_path ();
+ }
+
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_in_path (std::move (path.value ()),
+ vis_loc);
+ }
+ default:
+ add_error (Error (t->get_locus (), "unexpected token %qs in visibility",
+ t->get_token_description ()));
- return actual_attribute;
+ lexer.skip_token ();
+ return Parse::Error::Visibility::make_malformed ();
+ }
}
-// Parses a VisItem (item that can have non-default visibility).
+// Parses a module - either a bodied module or a module defined in another file.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::VisItem>
-Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
+std::unique_ptr<AST::Module>
+Parser<ManagedTokenSource>::parse_module (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- // parse visibility, which may or may not exist
- auto vis_res = parse_visibility ();
- if (!vis_res)
- return nullptr;
- auto vis = vis_res.value ();
+ location_t locus = lexer.peek_token ()->get_locus ();
+
+ Unsafety safety = Unsafety::Normal;
+ if (lexer.peek_token ()->get_id () == UNSAFE)
+ {
+ safety = Unsafety::Unsafe;
+ skip_token (UNSAFE);
+ }
+
+ skip_token (MOD);
+
+ const_TokenPtr module_name = expect_token (IDENTIFIER);
+ if (module_name == nullptr)
+ {
+ return nullptr;
+ }
+ Identifier name{module_name};
- // select VisItem to create depending on keyword
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
- case MOD:
- return parse_module (std::move (vis), std::move (outer_attrs));
- case EXTERN_KW:
- // lookahead to resolve syntactical production
- t = lexer.peek_token (1);
-
- switch (t->get_id ())
- {
- case CRATE:
- return parse_extern_crate (std::move (vis), std::move (outer_attrs));
- case FN_KW: // extern function
- return parse_function (std::move (vis), std::move (outer_attrs));
- case LEFT_CURLY: // extern block
- return parse_extern_block (std::move (vis), std::move (outer_attrs));
- case STRING_LITERAL: // for specifying extern ABI
- // could be extern block or extern function, so more lookahead
- t = lexer.peek_token (2);
+ case SEMICOLON:
+ lexer.skip_token ();
- switch (t->get_id ())
- {
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs));
- case LEFT_CURLY:
- return parse_extern_block (std::move (vis),
- std::move (outer_attrs));
- default:
- add_error (
- Error (t->get_locus (),
- "unexpected token %qs in some sort of extern production",
- t->get_token_description ()));
+ // Construct an external module
+ return std::unique_ptr<AST::Module> (
+ new AST::Module (std::move (name), std::move (vis),
+ std::move (outer_attrs), locus, safety,
+ lexer.get_filename (), inline_module_stack));
+ case LEFT_CURLY:
+ {
+ lexer.skip_token ();
- lexer.skip_token (2); // TODO: is this right thing to do?
- return nullptr;
- }
- default:
- add_error (
- Error (t->get_locus (),
- "unexpected token %qs in some sort of extern production",
- t->get_token_description ()));
+ // parse inner attributes
+ AST::AttrVec inner_attrs = parse_inner_attributes ();
- lexer.skip_token (1); // TODO: is this right thing to do?
- return nullptr;
- }
- case USE:
- return parse_use_decl (std::move (vis), std::move (outer_attrs));
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs));
- case TYPE:
- return parse_type_alias (std::move (vis), std::move (outer_attrs));
- case STRUCT_KW:
- return parse_struct (std::move (vis), std::move (outer_attrs));
- case ENUM_KW:
- return parse_enum (std::move (vis), std::move (outer_attrs));
- // TODO: implement union keyword but not really because of
- // context-dependence case UNION: crappy hack to do union "keyword"
- case IDENTIFIER:
- if (t->get_str () == Values::WeakKeywords::UNION
- && lexer.peek_token (1)->get_id () == IDENTIFIER)
- {
- return parse_union (std::move (vis), std::move (outer_attrs));
- // or should item switch go straight to parsing union?
- }
- else
- {
- break;
- }
- case CONST:
- // lookahead to resolve syntactical production
- t = lexer.peek_token (1);
+ std::string default_path = name.as_string ();
- switch (t->get_id ())
- {
- case IDENTIFIER:
- case UNDERSCORE:
- return parse_const_item (std::move (vis), std::move (outer_attrs));
- case ASYNC:
- return parse_async_item (std::move (vis), std::move (outer_attrs));
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs));
- default:
- add_error (
- Error (t->get_locus (),
- "unexpected token %qs in some sort of const production",
- t->get_token_description ()));
-
- lexer.skip_token (1); // TODO: is this right thing to do?
- return nullptr;
- }
- // for async functions
- case ASYNC:
- return parse_async_item (std::move (vis), std::move (outer_attrs));
+ if (inline_module_stack.empty ())
+ {
+ std::string filename = lexer.get_filename ();
+ auto slash_idx = filename.rfind (file_separator);
+ if (slash_idx == std::string::npos)
+ slash_idx = 0;
+ else
+ slash_idx++;
+ filename = filename.substr (slash_idx);
- case STATIC_KW:
- return parse_static_item (std::move (vis), std::move (outer_attrs));
- case AUTO:
- case TRAIT:
- return parse_trait (std::move (vis), std::move (outer_attrs));
- case IMPL:
- return parse_impl (std::move (vis), std::move (outer_attrs));
- case UNSAFE: // unsafe traits, unsafe functions, unsafe impls (trait impls),
- // lookahead to resolve syntactical production
- t = lexer.peek_token (1);
+ std::string subdir;
+ if (get_file_subdir (filename, subdir))
+ default_path = subdir + file_separator + name.as_string ();
+ }
- switch (t->get_id ())
- {
- case AUTO:
- case TRAIT:
- return parse_trait (std::move (vis), std::move (outer_attrs));
- case EXTERN_KW:
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs));
- case IMPL:
- return parse_impl (std::move (vis), std::move (outer_attrs));
- case MOD:
- return parse_module (std::move (vis), std::move (outer_attrs));
- default:
- add_error (
- Error (t->get_locus (),
- "unexpected token %qs in some sort of unsafe production",
- t->get_token_description ()));
+ std::string module_path_name
+ = extract_module_path (inner_attrs, outer_attrs, default_path);
+ InlineModuleStackScope scope (*this, std::move (module_path_name));
- lexer.skip_token (1); // TODO: is this right thing to do?
- return nullptr;
- }
- case MACRO:
- return parse_decl_macro_def (std::move (vis), std::move (outer_attrs));
- default:
- // otherwise vis item clearly doesn't exist, which is not an error
- // has a catch-all post-switch return to allow other breaks to occur
- break;
- }
- return nullptr;
-}
+ // parse items
+ std::vector<std::unique_ptr<AST::Item>> items;
+ const_TokenPtr tok = lexer.peek_token ();
+ while (tok->get_id () != RIGHT_CURLY)
+ {
+ auto item = parse_item (false);
+ if (!item)
+ {
+ Error error (tok->get_locus (),
+ "failed to parse item in module");
+ add_error (std::move (error));
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Function>
-Parser<ManagedTokenSource>::parse_async_item (AST::Visibility vis,
- AST::AttrVec outer_attrs)
-{
- auto offset = (lexer.peek_token ()->get_id () == CONST) ? 1 : 0;
- const_TokenPtr t = lexer.peek_token (offset);
+ return nullptr;
+ }
- if (get_rust_edition () == Edition::E2015)
- {
- add_error (Error (t->get_locus (), ErrorCode::E0670,
- "%<async fn%> is not permitted in Rust 2015"));
- add_error (
- Error::Hint (t->get_locus (),
- "to use %<async fn%>, switch to Rust 2018 or later"));
- }
+ items.push_back (std::move (item.value ()));
- t = lexer.peek_token (offset + 1);
+ tok = lexer.peek_token ();
+ }
- switch (t->get_id ())
- {
- case UNSAFE:
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs));
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+ return std::unique_ptr<AST::Module> (
+ new AST::Module (std::move (name), locus, std::move (items),
+ std::move (vis), safety, std::move (inner_attrs),
+ std::move (outer_attrs))); // module name?
+ }
default:
add_error (
- Error (t->get_locus (), "expected item, found keyword %<async%>"));
+ Error (t->get_locus (),
+ "unexpected token %qs in module declaration/definition item",
+ t->get_token_description ()));
- lexer.skip_token (1);
+ lexer.skip_token ();
return nullptr;
}
}
-// Parses a macro rules definition syntax extension whatever thing.
+// Parses an extern crate declaration (dependency on external crate)
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroRulesDefinition>
-Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
+std::unique_ptr<AST::ExternCrate>
+Parser<ManagedTokenSource>::parse_extern_crate (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- // ensure that first token is identifier saying "macro_rules"
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () != IDENTIFIER
- || t->get_str () != Values::WeakKeywords::MACRO_RULES)
- {
- Error error (
- t->get_locus (),
- "macro rules definition does not start with %<macro_rules%>");
- add_error (std::move (error));
-
- // skip after somewhere?
- return nullptr;
- }
- lexer.skip_token ();
- location_t macro_locus = t->get_locus ();
-
- if (!skip_token (EXCLAM))
+ location_t locus = lexer.peek_token ()->get_locus ();
+ if (!skip_token (EXTERN_KW))
{
- // skip after somewhere?
+ skip_after_semicolon ();
return nullptr;
}
- // parse macro name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
+ if (!skip_token (CRATE))
{
+ skip_after_semicolon ();
return nullptr;
}
- Identifier rule_name{ident_tok};
-
- // DEBUG
- rust_debug ("in macro rules def, about to parse parens.");
- // save delim type to ensure it is reused later
- AST::DelimType delim_type = AST::PARENS;
+ /* parse crate reference name - this has its own syntactical rule in reference
+ * but seems to not be used elsewhere, so i'm putting it here */
+ const_TokenPtr crate_name_tok = lexer.peek_token ();
+ std::string crate_name;
- // Map tokens to DelimType
- t = lexer.peek_token ();
- switch (t->get_id ())
+ switch (crate_name_tok->get_id ())
{
- case LEFT_PAREN:
- delim_type = AST::PARENS;
- break;
- case LEFT_SQUARE:
- delim_type = AST::SQUARE;
+ case IDENTIFIER:
+ crate_name = crate_name_tok->get_str ();
+ lexer.skip_token ();
break;
- case LEFT_CURLY:
- delim_type = AST::CURLY;
+ case SELF:
+ crate_name = Values::Keywords::SELF;
+ lexer.skip_token ();
break;
default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs - expecting delimiters (for a "
- "macro rules definition)",
- t->get_token_description ()));
+ add_error (
+ Error (crate_name_tok->get_locus (),
+ "expecting crate name (identifier or %<self%>), found %qs",
+ crate_name_tok->get_token_description ()));
+ skip_after_semicolon ();
return nullptr;
}
- lexer.skip_token ();
-
- // parse actual macro rules
- std::vector<AST::MacroRule> macro_rules;
- // must be at least one macro rule, so parse it
- AST::MacroRule initial_rule = parse_macro_rule ();
- if (initial_rule.is_error ())
+ // don't parse as clause if it doesn't exist
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
{
- Error error (lexer.peek_token ()->get_locus (),
- "required first macro rule in macro rules definition "
- "could not be parsed");
- add_error (std::move (error));
+ lexer.skip_token ();
- // skip after somewhere?
+ return std::unique_ptr<AST::ExternCrate> (
+ new AST::ExternCrate (std::move (crate_name), std::move (vis),
+ std::move (outer_attrs), locus));
+ }
+
+ /* parse as clause - this also has its own syntactical rule in reference and
+ * also seems to not be used elsewhere, so including here again. */
+ if (!skip_token (AS))
+ {
+ skip_after_semicolon ();
return nullptr;
}
- macro_rules.push_back (std::move (initial_rule));
- // DEBUG
- rust_debug ("successfully pushed back initial macro rule");
+ const_TokenPtr as_name_tok = lexer.peek_token ();
+ std::string as_name;
- t = lexer.peek_token ();
- // parse macro rules
- while (t->get_id () == SEMICOLON)
+ switch (as_name_tok->get_id ())
{
- // skip semicolon
+ case IDENTIFIER:
+ as_name = as_name_tok->get_str ();
lexer.skip_token ();
+ break;
+ case UNDERSCORE:
+ as_name = Values::Keywords::UNDERSCORE;
+ lexer.skip_token ();
+ break;
+ default:
+ add_error (
+ Error (as_name_tok->get_locus (),
+ "expecting as clause name (identifier or %<_%>), found %qs",
+ as_name_tok->get_token_description ()));
- // don't parse if end of macro rules
- if (Parse::Utils::token_id_matches_delims (lexer.peek_token ()->get_id (),
- delim_type))
- {
- // DEBUG
- rust_debug (
- "broke out of parsing macro rules loop due to finding delim");
-
- break;
- }
+ skip_after_semicolon ();
+ return nullptr;
+ }
- // try to parse next rule
- AST::MacroRule rule = parse_macro_rule ();
- if (rule.is_error ())
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse macro rule in macro rules definition");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- macro_rules.push_back (std::move (rule));
-
- // DEBUG
- rust_debug ("successfully pushed back another macro rule");
-
- t = lexer.peek_token ();
- }
-
- // parse end delimiters
- t = lexer.peek_token ();
- if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
- {
- // tokens match opening delimiter, so skip.
- lexer.skip_token ();
-
- if (delim_type != AST::CURLY)
- {
- // skip semicolon at end of non-curly macro definitions
- if (!skip_token (SEMICOLON))
- {
- // as this is the end, allow recovery (probably) - may change
- return std::unique_ptr<AST::MacroRulesDefinition> (
- AST::MacroRulesDefinition::mbe (
- std::move (rule_name), delim_type, std::move (macro_rules),
- std::move (outer_attrs), macro_locus));
- }
- }
-
- return std::unique_ptr<AST::MacroRulesDefinition> (
- AST::MacroRulesDefinition::mbe (std::move (rule_name), delim_type,
- std::move (macro_rules),
- std::move (outer_attrs), macro_locus));
- }
- else
+ if (!skip_token (SEMICOLON))
{
- // tokens don't match opening delimiters, so produce error
- Error error (t->get_locus (),
- "unexpected token %qs - expecting closing delimiter %qs "
- "(for a macro rules definition)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
- add_error (std::move (error));
-
- /* return empty macro definiton despite possibly parsing mostly valid one
- * - TODO is this a good idea? */
+ skip_after_semicolon ();
return nullptr;
}
+
+ return std::unique_ptr<AST::ExternCrate> (
+ new AST::ExternCrate (std::move (crate_name), std::move (vis),
+ std::move (outer_attrs), locus, std::move (as_name)));
}
-// Parses a declarative macro 2.0 definition.
+// Parses a use declaration.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroRulesDefinition>
-Parser<ManagedTokenSource>::parse_decl_macro_def (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::UseDeclaration>
+Parser<ManagedTokenSource>::parse_use_decl (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- // ensure that first token is identifier saying "macro"
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () != MACRO)
+ location_t locus = lexer.peek_token ()->get_locus ();
+ if (!skip_token (USE))
{
- Error error (
- t->get_locus (),
- "declarative macro definition does not start with %<macro%>");
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ // parse use tree, which is required
+ std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
+ if (use_tree == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse use tree in use declaration");
add_error (std::move (error));
- // skip after somewhere?
+ skip_after_semicolon ();
return nullptr;
}
- lexer.skip_token ();
- location_t macro_locus = t->get_locus ();
- // parse macro name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
+ if (!skip_token (SEMICOLON))
{
+ skip_after_semicolon ();
return nullptr;
}
- Identifier rule_name{ident_tok};
- t = lexer.peek_token ();
- if (t->get_id () == LEFT_PAREN)
- {
- // single definiton of macro rule
- // e.g. `macro foo($e:expr) {}`
+ return std::unique_ptr<AST::UseDeclaration> (
+ new AST::UseDeclaration (std::move (use_tree), std::move (vis),
+ std::move (outer_attrs), locus));
+}
- // parse macro matcher
- location_t locus = lexer.peek_token ()->get_locus ();
- AST::MacroMatcher matcher = parse_macro_matcher ();
- if (matcher.is_error ())
- return nullptr;
+// Parses a use tree (which can be recursive and is actually a base class).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::UseTree>
+Parser<ManagedTokenSource>::parse_use_tree ()
+{
+ /* potential syntax definitions in attempt to get algorithm:
+ * Glob:
+ * <- SimplePath :: *
+ * <- :: *
+ * <- *
+ * Nested tree thing:
+ * <- SimplePath :: { COMPLICATED_INNER_TREE_THING }
+ * <- :: COMPLICATED_INNER_TREE_THING }
+ * <- { COMPLICATED_INNER_TREE_THING }
+ * Rebind thing:
+ * <- SimplePath as IDENTIFIER
+ * <- SimplePath as _
+ * <- SimplePath
+ */
- // check delimiter of macro matcher
- if (matcher.get_delim_type () != AST::DelimType::PARENS)
- {
- Error error (locus, "only parenthesis can be used for a macro "
- "matcher in declarative macro definition");
- add_error (std::move (error));
- return nullptr;
- }
+ /* current plan of attack: try to parse SimplePath first - if fails, one of
+ * top two then try parse :: - if fails, one of top two. Next is deciding
+ * character for top two. */
- location_t transcriber_loc = lexer.peek_token ()->get_locus ();
- auto delim_tok_tree = parse_delim_token_tree ();
- if (!delim_tok_tree)
- return nullptr;
+ /* Thus, parsing smaller parts of use tree may require feeding into function
+ * via parameters (or could handle all in this single function because other
+ * use tree types aren't recognised as separate in the spec) */
- AST::MacroTranscriber transcriber (delim_tok_tree.value (),
- transcriber_loc);
+ // TODO: I think this function is too complex, probably should split it
- if (transcriber.get_token_tree ().get_delim_type ()
- != AST::DelimType::CURLY)
- {
- Error error (transcriber_loc,
- "only braces can be used for a macro transcriber "
- "in declarative macro definition");
- add_error (std::move (error));
- return nullptr;
- }
+ location_t locus = lexer.peek_token ()->get_locus ();
- std::vector<AST::MacroRule> macro_rules;
- macro_rules.emplace_back (std::move (matcher), std::move (transcriber),
- locus);
+ // bool has_path = false;
+ auto path = parse_simple_path ();
- return std::unique_ptr<AST::MacroRulesDefinition> (
- AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
- macro_rules,
- std::move (outer_attrs),
- macro_locus, vis));
- }
- else if (t->get_id () == LEFT_CURLY)
+ if (!path)
{
- // multiple definitions of macro rule separated by comma
- // e.g. `macro foo { () => {}, ($e:expr) => {}, }`
-
- // parse left curly
- const_TokenPtr left_curly = expect_token (LEFT_CURLY);
- if (left_curly == nullptr)
- {
- return nullptr;
- }
+ // has no path, so must be glob or nested tree UseTree type
- // parse actual macro rules
- std::vector<AST::MacroRule> macro_rules;
+ bool is_global = false;
- // must be at least one macro rule, so parse it
- AST::MacroRule initial_rule = parse_macro_rule ();
- if (initial_rule.is_error ())
+ // check for global scope resolution operator
+ if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
{
- Error error (
- lexer.peek_token ()->get_locus (),
- "required first macro rule in declarative macro definition "
- "could not be parsed");
- add_error (std::move (error));
-
- // skip after somewhere?
- return nullptr;
+ lexer.skip_token ();
+ is_global = true;
}
- macro_rules.push_back (std::move (initial_rule));
- t = lexer.peek_token ();
- // parse macro rules
- while (t->get_id () == COMMA)
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
{
- // skip comma
+ case ASTERISK:
+ // glob UseTree type
lexer.skip_token ();
- // don't parse if end of macro rules
- if (Parse::Utils::token_id_matches_delims (
- lexer.peek_token ()->get_id (), AST::CURLY))
- {
- break;
- }
+ if (is_global)
+ return std::unique_ptr<AST::UseTreeGlob> (
+ new AST::UseTreeGlob (AST::UseTreeGlob::GLOBAL,
+ AST::SimplePath::create_empty (), locus));
+ else
+ return std::unique_ptr<AST::UseTreeGlob> (
+ new AST::UseTreeGlob (AST::UseTreeGlob::NO_PATH,
+ AST::SimplePath::create_empty (), locus));
+ case LEFT_CURLY:
+ {
+ // nested tree UseTree type
+ lexer.skip_token ();
- // try to parse next rule
- AST::MacroRule rule = parse_macro_rule ();
- if (rule.is_error ())
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse macro rule in declarative macro definition");
- add_error (std::move (error));
+ std::vector<std::unique_ptr<AST::UseTree>> use_trees;
- return nullptr;
- }
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY)
+ {
+ std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
+ if (use_tree == nullptr)
+ {
+ break;
+ }
- macro_rules.push_back (std::move (rule));
+ use_trees.push_back (std::move (use_tree));
- t = lexer.peek_token ();
- }
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- // parse right curly
- const_TokenPtr right_curly = expect_token (RIGHT_CURLY);
- if (right_curly == nullptr)
- {
+ lexer.skip_token ();
+ t = lexer.peek_token ();
+ }
+
+ // skip end curly delimiter
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
+
+ if (is_global)
+ return std::unique_ptr<AST::UseTreeList> (
+ new AST::UseTreeList (AST::UseTreeList::GLOBAL,
+ AST::SimplePath::create_empty (),
+ std::move (use_trees), locus));
+ else
+ return std::unique_ptr<AST::UseTreeList> (
+ new AST::UseTreeList (AST::UseTreeList::NO_PATH,
+ AST::SimplePath::create_empty (),
+ std::move (use_trees), locus));
+ }
+ case AS:
+ // this is not allowed
+ add_error (Error (
+ t->get_locus (),
+ "use declaration with rebind %<as%> requires a valid simple path - "
+ "none found"));
+
+ skip_after_semicolon ();
return nullptr;
- }
+ default:
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in use tree with "
+ "no valid simple path (i.e. list"
+ " or glob use tree)",
+ t->get_token_description ()));
- return std::unique_ptr<AST::MacroRulesDefinition> (
- AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
- std::move (macro_rules),
- std::move (outer_attrs),
- macro_locus, vis));
+ skip_after_semicolon ();
+ return nullptr;
+ }
}
else
{
- add_error (Error (t->get_locus (),
- "unexpected token %qs - expecting delimiters "
- "(for a declarative macro definiton)",
- t->get_token_description ()));
- return nullptr;
- }
-}
+ const_TokenPtr t = lexer.peek_token ();
-// Parses a semi-coloned (except for full block) macro invocation item.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroInvocation>
-Parser<ManagedTokenSource>::parse_macro_invocation_semi (
- AST::AttrVec outer_attrs)
-{
- location_t macro_locus = lexer.peek_token ()->get_locus ();
- auto path = parse_simple_path ();
- if (!path)
- return nullptr;
+ switch (t->get_id ())
+ {
+ case AS:
+ {
+ // rebind UseTree type
+ lexer.skip_token ();
- if (!skip_token (EXCLAM))
- {
- // skip after somewhere?
- return nullptr;
- }
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case IDENTIFIER:
+ // skip lexer token
+ lexer.skip_token ();
- // save delim type to ensure it is reused later
- AST::DelimType delim_type = AST::PARENS;
+ return std::unique_ptr<AST::UseTreeRebind> (
+ new AST::UseTreeRebind (AST::UseTreeRebind::IDENTIFIER,
+ std::move (path.value ()), locus, t));
+ case UNDERSCORE:
+ // skip lexer token
+ lexer.skip_token ();
- // Map tokens to DelimType
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case LEFT_PAREN:
- delim_type = AST::PARENS;
- break;
- case LEFT_SQUARE:
- delim_type = AST::SQUARE;
- break;
- case LEFT_CURLY:
- delim_type = AST::CURLY;
- break;
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs - expecting delimiters (for a "
- "macro invocation semi body)",
- t->get_token_description ()));
+ return std::unique_ptr<AST::UseTreeRebind> (
+ new AST::UseTreeRebind (AST::UseTreeRebind::WILDCARD,
+ std::move (path.value ()), locus,
+ {Values::Keywords::UNDERSCORE,
+ t->get_locus ()}));
+ default:
+ add_error (Error (
+ t->get_locus (),
+ "unexpected token %qs in use tree with as clause - expected "
+ "identifier or %<_%>",
+ t->get_token_description ()));
- return nullptr;
- }
- location_t tok_tree_locus = t->get_locus ();
- lexer.skip_token ();
+ skip_after_semicolon ();
+ return nullptr;
+ }
+ }
+ case SEMICOLON:
+ // rebind UseTree type without rebinding - path only
+
+ // don't skip semicolon - handled in parse_use_tree
+ // lexer.skip_token();
+ case COMMA:
+ case RIGHT_CURLY:
+ // this may occur in recursive calls - assume it is ok and ignore it
+ return std::unique_ptr<AST::UseTreeRebind> (
+ new AST::UseTreeRebind (AST::UseTreeRebind::NONE,
+ std::move (path.value ()), locus));
+ case SCOPE_RESOLUTION:
+ // keep going
+ break;
+ default:
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in use tree with valid path",
+ t->get_token_description ()));
+ return nullptr;
+ }
- // parse actual token trees
- std::vector<std::unique_ptr<AST::TokenTree>> token_trees;
- auto delim_open
- = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
- token_trees.push_back (std::move (delim_open));
+ skip_token ();
+ t = lexer.peek_token ();
- t = lexer.peek_token ();
- // parse token trees until the initial delimiter token is found again
- while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type)
- && t->get_id () != END_OF_FILE)
- {
- auto tree = parse_token_tree ();
- if (!tree)
- return nullptr;
+ switch (t->get_id ())
+ {
+ case ASTERISK:
+ // glob UseTree type
+ lexer.skip_token ();
- token_trees.push_back (std::move (tree.value ()));
+ return std::unique_ptr<AST::UseTreeGlob> (
+ new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
+ std::move (path.value ()), locus));
+ case LEFT_CURLY:
+ {
+ // nested tree UseTree type
+ lexer.skip_token ();
- t = lexer.peek_token ();
- }
- auto delim_close
- = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
- token_trees.push_back (std::move (delim_close));
+ std::vector<std::unique_ptr<AST::UseTree>> use_trees;
- AST::DelimTokenTree delim_tok_tree (delim_type, std::move (token_trees),
- tok_tree_locus);
- AST::MacroInvocData invoc_data (std::move (path.value ()),
- std::move (delim_tok_tree));
+ // TODO: think of better control structure
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY)
+ {
+ std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
+ if (use_tree == nullptr)
+ {
+ break;
+ }
- // parse end delimiters
- t = lexer.peek_token ();
- if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
- {
- // tokens match opening delimiter, so skip.
- lexer.skip_token ();
+ use_trees.push_back (std::move (use_tree));
- if (delim_type != AST::CURLY)
- {
- // skip semicolon at end of non-curly macro invocation semis
- if (!skip_token (SEMICOLON))
- {
- // as this is the end, allow recovery (probably) - may change
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- return AST::MacroInvocation::Regular (std::move (invoc_data),
- std::move (outer_attrs),
- macro_locus, true);
- }
- }
+ lexer.skip_token ();
+ t = lexer.peek_token ();
+ }
- // DEBUG:
- rust_debug ("skipped token is '%s', next token (current peek) is '%s'",
- t->get_token_description (),
- lexer.peek_token ()->get_token_description ());
+ // skip end curly delimiter
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
- return AST::MacroInvocation::Regular (std::move (invoc_data),
- std::move (outer_attrs),
- macro_locus, true);
- }
- else
- {
- // tokens don't match opening delimiters, so produce error
- Error error (t->get_locus (),
- "unexpected token %qs - expecting closing delimiter %qs "
- "(for a macro invocation semi)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
- add_error (std::move (error));
+ return std::unique_ptr<AST::UseTreeList> (
+ new AST::UseTreeList (AST::UseTreeList::PATH_PREFIXED,
+ std::move (path.value ()),
+ std::move (use_trees), locus));
+ }
+ default:
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in use tree with valid path",
+ t->get_token_description ()));
- /* return empty macro invocation despite possibly parsing mostly valid one
- * - TODO is this a good idea? */
- return nullptr;
+ // skip_after_semicolon();
+ return nullptr;
+ }
}
}
-// Parses a non-semicoloned macro invocation (i.e. as pattern or expression).
+// Parses a function (not a method).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroInvocation>
-Parser<ManagedTokenSource>::parse_macro_invocation (AST::AttrVec outer_attrs)
+std::unique_ptr<AST::Function>
+Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
+ AST::AttrVec outer_attrs,
+ bool is_external)
{
- // parse macro path
- auto macro_path = parse_simple_path ();
- if (!macro_path)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse macro invocation path");
- add_error (std::move (error));
+ location_t locus = lexer.peek_token ()->get_locus ();
+ // Get qualifiers for function if they exist
+ AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
- // skip?
+ skip_token (FN_KW);
+
+ // Save function name token
+ const_TokenPtr function_name_tok = expect_token (IDENTIFIER);
+ if (function_name_tok == nullptr)
+ {
+ skip_after_next_block ();
return nullptr;
}
+ Identifier function_name{function_name_tok};
- if (!skip_token (EXCLAM))
+ // parse generic params - if exist
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
+
+ if (!skip_token (LEFT_PAREN))
{
- // skip after somewhere?
+ Error error (lexer.peek_token ()->get_locus (),
+ "function declaration missing opening parentheses before "
+ "parameter list");
+ add_error (std::move (error));
+
+ skip_after_next_block ();
return nullptr;
}
- // parse internal delim token tree
- auto delim_tok_tree = parse_delim_token_tree ();
- if (!delim_tok_tree)
- return nullptr;
+ auto initial_param = parse_self_param ();
- location_t macro_locus = macro_path->get_locus ();
+ if (!initial_param.has_value ()
+ && initial_param.error () != ParseSelfError::NOT_SELF)
+ return nullptr;
- return AST::MacroInvocation::Regular (
- AST::MacroInvocData (std::move (macro_path.value ()),
- std::move (delim_tok_tree.value ())),
- std::move (outer_attrs), macro_locus);
-}
+ if (initial_param.has_value () && lexer.peek_token ()->get_id () == COMMA)
+ skip_token ();
-// Parses a macro rule definition - does not parse semicolons.
-template <typename ManagedTokenSource>
-AST::MacroRule
-Parser<ManagedTokenSource>::parse_macro_rule ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
+ // parse function parameters (only if next token isn't right paren)
+ std::vector<std::unique_ptr<AST::Param>> function_params;
- // parse macro matcher
- AST::MacroMatcher matcher = parse_macro_matcher ();
+ if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
+ function_params
+ = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
- if (matcher.is_error ())
- return AST::MacroRule::create_error (locus);
+ if (initial_param.has_value ())
+ function_params.insert (function_params.begin (),
+ std::move (*initial_param));
- if (!skip_token (MATCH_ARROW))
+ if (!skip_token (RIGHT_PAREN))
{
- // skip after somewhere?
- return AST::MacroRule::create_error (locus);
+ Error error (lexer.peek_token ()->get_locus (),
+ "function declaration missing closing parentheses after "
+ "parameter list");
+ add_error (std::move (error));
+
+ skip_after_next_block ();
+ return nullptr;
}
- // parse transcriber (this is just a delim token tree)
- location_t token_tree_loc = lexer.peek_token ()->get_locus ();
- auto delim_token_tree = parse_delim_token_tree ();
- if (!delim_token_tree)
- return AST::MacroRule::create_error (token_tree_loc);
+ // parse function return type - if exists
+ std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+ // parse where clause - if exists
+ AST::WhereClause where_clause = parse_where_clause ();
- AST::MacroTranscriber transcriber (delim_token_tree.value (), token_tree_loc);
+ tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ lexer.skip_token ();
+ else
+ {
+ std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+ if (block_expr == nullptr)
+ return nullptr;
+ body = std::move (block_expr);
+ }
- return AST::MacroRule (std::move (matcher), std::move (transcriber), locus);
+ return std::unique_ptr<AST::Function> (
+ new AST::Function (std::move (function_name), std::move (qualifiers),
+ std::move (generic_params), std::move (function_params),
+ std::move (return_type), std::move (where_clause),
+ std::move (body), std::move (vis),
+ std::move (outer_attrs), locus, false, is_external));
}
-// Parses a macro matcher (part of a macro rule definition).
+// Parses function or method qualifiers (i.e. const, unsafe, and extern).
template <typename ManagedTokenSource>
-AST::MacroMatcher
-Parser<ManagedTokenSource>::parse_macro_matcher ()
+AST::FunctionQualifiers
+Parser<ManagedTokenSource>::parse_function_qualifiers ()
{
- // save delim type to ensure it is reused later
- AST::DelimType delim_type = AST::PARENS;
-
- // DEBUG
- rust_debug ("begun parsing macro matcher");
+ Async async_status = Async::No;
+ Const const_status = Const::No;
+ Unsafety unsafe_status = Unsafety::Normal;
+ bool has_extern = false;
+ std::string abi;
- // Map tokens to DelimType
- const_TokenPtr t = lexer.peek_token ();
- location_t locus = t->get_locus ();
- switch (t->get_id ())
+ const_TokenPtr t;
+ location_t locus;
+ // Check in order of const, unsafe, then extern
+ for (int i = 0; i < 2; i++)
{
- case LEFT_PAREN:
- delim_type = AST::PARENS;
- break;
- case LEFT_SQUARE:
- delim_type = AST::SQUARE;
- break;
- case LEFT_CURLY:
- delim_type = AST::CURLY;
- break;
- default:
- add_error (Error (
- t->get_locus (),
- "unexpected token %qs - expecting delimiters (for a macro matcher)",
- t->get_token_description ()));
+ t = lexer.peek_token ();
+ locus = t->get_locus ();
- return AST::MacroMatcher::create_error (t->get_locus ());
+ switch (t->get_id ())
+ {
+ case CONST:
+ lexer.skip_token ();
+ const_status = Const::Yes;
+ break;
+ case ASYNC:
+ lexer.skip_token ();
+ async_status = Async::Yes;
+ break;
+ default:
+ // const status is still none
+ break;
+ }
}
- lexer.skip_token ();
- // parse actual macro matches
- std::vector<std::unique_ptr<AST::MacroMatch>> matches;
- // Set of possible preceding macro matches to make sure follow-set
- // restrictions are respected.
- // TODO: Consider using std::reference_wrapper instead of raw pointers?
- std::vector<const AST::MacroMatch *> last_matches;
+ if (lexer.peek_token ()->get_id () == UNSAFE)
+ {
+ lexer.skip_token ();
+ unsafe_status = Unsafety::Unsafe;
+ }
- t = lexer.peek_token ();
- // parse token trees until the initial delimiter token is found again
- while (!Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
+ if (lexer.peek_token ()->get_id () == EXTERN_KW)
{
- std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
+ lexer.skip_token ();
+ has_extern = true;
- if (match == nullptr)
+ // detect optional abi name
+ const_TokenPtr next_tok = lexer.peek_token ();
+ if (next_tok->get_id () == STRING_LITERAL)
{
- Error error (
- t->get_locus (),
- "failed to parse macro match for macro matcher - found %qs",
- t->get_token_description ());
- add_error (std::move (error));
-
- return AST::MacroMatcher::create_error (t->get_locus ());
+ lexer.skip_token ();
+ abi = next_tok->get_str ();
}
+ }
- if (matches.size () > 0)
- {
- const auto *last_match = matches.back ().get ();
-
- // We want to check if we are dealing with a zeroable repetition
- bool zeroable = false;
- if (last_match->get_macro_match_type ()
- == AST::MacroMatch::MacroMatchType::Repetition)
- {
- auto repetition
- = static_cast<const AST::MacroMatchRepetition *> (last_match);
-
- if (repetition->get_op ()
- != AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE)
- zeroable = true;
- }
-
- if (!zeroable)
- last_matches.clear ();
-
- last_matches.emplace_back (last_match);
+ return AST::FunctionQualifiers (locus, async_status, const_status,
+ unsafe_status, has_extern, std::move (abi));
+}
- for (auto last : last_matches)
- if (!is_match_compatible (*last, *match))
- return AST::MacroMatcher::create_error (
- match->get_match_locus ());
- }
+// Parses generic (lifetime or type) params inside angle brackets (optional).
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::GenericParam>>
+Parser<ManagedTokenSource>::parse_generic_params_in_angles ()
+{
+ if (lexer.peek_token ()->get_id () != LEFT_ANGLE)
+ {
+ // seems to be no generic params, so exit with empty vector
+ return std::vector<std::unique_ptr<AST::GenericParam>> ();
+ }
+ lexer.skip_token ();
- matches.push_back (std::move (match));
+ // DEBUG:
+ rust_debug ("skipped left angle in generic param");
- // DEBUG
- rust_debug ("pushed back a match in macro matcher");
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params (Parse::Utils::is_right_angle_tok);
- t = lexer.peek_token ();
- }
+ // DEBUG:
+ rust_debug ("finished parsing actual generic params (i.e. inside angles)");
- // parse end delimiters
- t = lexer.peek_token ();
- if (Parse::Utils::token_id_matches_delims (t->get_id (), delim_type))
+ if (!skip_generics_right_angle ())
{
- // tokens match opening delimiter, so skip.
- lexer.skip_token ();
+ // DEBUG
+ rust_debug ("failed to skip generics right angle - returning empty "
+ "generic params");
- return AST::MacroMatcher (delim_type, std::move (matches), locus);
+ return std::vector<std::unique_ptr<AST::GenericParam>> ();
}
- else
- {
- // tokens don't match opening delimiters, so produce error
- Error error (t->get_locus (),
- "unexpected token %qs - expecting closing delimiter %qs "
- "(for a macro matcher)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
- add_error (std::move (error));
- /* return error macro matcher despite possibly parsing mostly correct one?
- * TODO is this the best idea? */
- return AST::MacroMatcher::create_error (t->get_locus ());
- }
+ return generic_params;
}
-// Parses a macro match (syntax match inside a matcher in a macro rule).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroMatch>
-Parser<ManagedTokenSource>::parse_macro_match ()
+template <typename EndTokenPred>
+std::unique_ptr<AST::GenericParam>
+Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token)
{
- // branch based on token available
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
+ auto outer_attrs = parse_outer_attributes ();
+ std::unique_ptr<AST::GenericParam> param;
+ auto token = lexer.peek_token ();
+
+ switch (token->get_id ())
{
- case LEFT_PAREN:
- case LEFT_SQUARE:
- case LEFT_CURLY:
+ case LIFETIME:
{
- // must be macro matcher as delimited
- AST::MacroMatcher matcher = parse_macro_matcher ();
- if (matcher.is_error ())
+ auto lifetime = parse_lifetime (false);
+ if (!lifetime)
{
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse macro matcher in macro match");
- add_error (std::move (error));
-
+ rust_error_at (
+ token->get_locus (),
+ "failed to parse lifetime in generic parameter list");
return nullptr;
}
- return std::unique_ptr<AST::MacroMatcher> (
- new AST::MacroMatcher (std::move (matcher)));
+
+ std::vector<AST::Lifetime> lifetime_bounds;
+ if (lexer.peek_token ()->get_id () == COLON)
+ {
+ lexer.skip_token ();
+ // parse required bounds
+ lifetime_bounds
+ = parse_lifetime_bounds ([is_end_token] (TokenId id) {
+ return is_end_token (id) || id == COMMA;
+ });
+ }
+
+ param = std::unique_ptr<AST::LifetimeParam> (new AST::LifetimeParam (
+ std::move (lifetime.value ()), std::move (lifetime_bounds),
+ std::move (outer_attrs), token->get_locus ()));
+ break;
}
- case DOLLAR_SIGN:
+ case IDENTIFIER:
{
- // have to do more lookahead to determine if fragment or repetition
- const_TokenPtr t2 = lexer.peek_token (1);
- switch (t2->get_id ())
+ auto type_ident = token->get_str ();
+ lexer.skip_token ();
+
+ std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+ if (lexer.peek_token ()->get_id () == COLON)
{
- case IDENTIFIER:
- case UNDERSCORE:
- // macro fragment
- return parse_macro_match_fragment ();
- case LEFT_PAREN:
- // macro repetition
- return parse_macro_match_repetition ();
- default:
- if (token_id_is_keyword (t2->get_id ()) && t2->get_id () != CRATE)
- {
- // keyword as macro fragment
- return parse_macro_match_fragment ();
- }
- else
- {
- // error: unrecognised
- add_error (Error (
- t2->get_locus (),
- "unrecognised token combination %<$%s%> at start of "
- "macro match - did you mean %<$identifier%> or %<$(%>?",
- t2->get_token_description ()));
+ lexer.skip_token ();
+
+ // parse optional type param bounds
+ type_param_bounds = parse_type_param_bounds ();
+ }
+
+ std::unique_ptr<AST::Type> type = nullptr;
+ if (lexer.peek_token ()->get_id () == EQUAL)
+ {
+ lexer.skip_token ();
- // skip somewhere?
+ // parse required type
+ type = parse_type ();
+ if (!type)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse type in type param in generic params");
return nullptr;
}
}
+
+ param = std::unique_ptr<AST::TypeParam> (
+ new AST::TypeParam (std::move (type_ident), token->get_locus (),
+ std::move (type_param_bounds), std::move (type),
+ std::move (outer_attrs)));
+ break;
}
- case RIGHT_PAREN:
- case RIGHT_SQUARE:
- case RIGHT_CURLY:
- // not allowed
- add_error (Error (
- t->get_locus (),
- "closing delimiters like %qs are not allowed at the start of a macro "
- "match",
- t->get_token_description ()));
+ case CONST:
+ {
+ lexer.skip_token ();
- // skip somewhere?
- return nullptr;
- default:
- // just the token
- lexer.skip_token ();
- return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
- }
-}
-
-// Parses a fragment macro match.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroMatchFragment>
-Parser<ManagedTokenSource>::parse_macro_match_fragment ()
-{
- location_t fragment_locus = lexer.peek_token ()->get_locus ();
- skip_token (DOLLAR_SIGN);
+ auto name_token = expect_token (IDENTIFIER);
- Identifier ident;
- auto identifier = lexer.peek_token ();
- if (identifier->get_id () == UNDERSCORE)
- ident = {Values::Keywords::UNDERSCORE, identifier->get_locus ()};
- else
- ident = {identifier};
+ if (!name_token || !expect_token (COLON))
+ return nullptr;
- if (ident.empty ())
- {
- Error error (lexer.peek_token ()->get_locus (),
- "missing identifier in macro match fragment");
- add_error (std::move (error));
+ auto type = parse_type ();
+ if (!type)
+ return nullptr;
- return nullptr;
- }
- skip_token (identifier->get_id ());
+ // optional default value
+ tl::optional<AST::GenericArg> default_expr = tl::nullopt;
+ if (lexer.peek_token ()->get_id () == EQUAL)
+ {
+ lexer.skip_token ();
+ auto tok = lexer.peek_token ();
+ default_expr = parse_generic_arg ();
- if (!skip_token (COLON))
- {
- // skip after somewhere?
- return nullptr;
- }
+ if (!default_expr)
+ {
+ rust_error_at (tok->get_locus (),
+ "invalid token for start of default value for "
+ "const generic parameter: expected %<block%>, "
+ "%<identifier%> or %<literal%>, got %qs",
+ token_id_to_str (tok->get_id ()));
+ return nullptr;
+ }
- // get MacroFragSpec for macro
- const_TokenPtr t = expect_token (IDENTIFIER);
- if (t == nullptr)
- return nullptr;
+ // At this point, we *know* that we are parsing a const
+ // expression
+ if (default_expr.value ().get_kind ()
+ == AST::GenericArg::Kind::Either)
+ default_expr = default_expr.value ().disambiguate_to_const ();
+ }
- AST::MacroFragSpec frag
- = AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ());
- if (frag.is_error ())
- {
- Error error (t->get_locus (),
- "invalid fragment specifier %qs in fragment macro match",
- t->get_str ().c_str ());
- add_error (std::move (error));
+ param = std::unique_ptr<AST::ConstGenericParam> (
+ new AST::ConstGenericParam (name_token->get_str (), std::move (type),
+ default_expr, std::move (outer_attrs),
+ token->get_locus ()));
+ break;
+ }
+ default:
+ // FIXME: Can we clean this last call with a method call?
+ rust_error_at (token->get_locus (),
+ "unexpected token when parsing generic parameters: %qs",
+ token->as_string ().c_str ());
return nullptr;
}
- return std::unique_ptr<AST::MacroMatchFragment> (
- new AST::MacroMatchFragment (std::move (ident), frag, fragment_locus));
+ return param;
}
-// Parses a repetition macro match.
+/* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost
+ * always parse_generic_params_in_angles is what is wanted. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroMatchRepetition>
-Parser<ManagedTokenSource>::parse_macro_match_repetition ()
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::GenericParam>>
+Parser<ManagedTokenSource>::parse_generic_params (EndTokenPred is_end_token)
{
- skip_token (DOLLAR_SIGN);
- skip_token (LEFT_PAREN);
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
- std::vector<std::unique_ptr<AST::MacroMatch>> matches;
+ /* can't parse lifetime and type params separately due to lookahead issues
+ * thus, parse them all here */
- // parse required first macro match
- std::unique_ptr<AST::MacroMatch> initial_match = parse_macro_match ();
- if (initial_match == nullptr)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "could not parse required first macro match in macro match repetition");
- add_error (std::move (error));
+ /* HACK: used to retain attribute data if a lifetime param is tentatively
+ * parsed but it turns out to be type param */
+ AST::Attribute parsed_outer_attr = AST::Attribute::create_empty ();
- // skip after somewhere?
- return nullptr;
- }
- matches.push_back (std::move (initial_match));
+ // Did we parse a generic type param yet
+ auto type_seen = false;
+ // Did we parse a const param with a default value yet
+ auto const_with_default_seen = false;
+ // Did the user write a lifetime parameter after a type one
+ auto order_error = false;
+ // Did the user write a const param with a default value after a type one
+ auto const_with_default_order_error = false;
- // parse optional later macro matches
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_PAREN)
+ // parse lifetime params
+ while (!is_end_token (lexer.peek_token ()->get_id ()))
{
- std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
-
- if (match == nullptr)
+ auto param = parse_generic_param (is_end_token);
+ if (param)
{
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse macro match in macro match repetition");
- add_error (std::move (error));
+ if (param->get_kind () == AST::GenericParam::Kind::Type)
+ {
+ type_seen = true;
+ if (const_with_default_seen)
+ const_with_default_order_error = true;
+ }
+ else if (param->get_kind () == AST::GenericParam::Kind::Lifetime
+ && type_seen)
+ {
+ order_error = true;
+ if (const_with_default_seen)
+ const_with_default_order_error = true;
+ }
+ else if (param->get_kind () == AST::GenericParam::Kind::Const)
+ {
+ type_seen = true;
+ AST::ConstGenericParam *const_param
+ = static_cast<AST::ConstGenericParam *> (param.get ());
+ if (const_param->has_default_value ())
+ const_with_default_seen = true;
+ else if (const_with_default_seen)
+ const_with_default_order_error = true;
+ }
- return nullptr;
+ generic_params.emplace_back (std::move (param));
+ maybe_skip_token (COMMA);
}
-
- matches.push_back (std::move (match));
-
- t = lexer.peek_token ();
+ else
+ break;
}
- if (!skip_token (RIGHT_PAREN))
+ // FIXME: Add reordering hint
+ if (order_error)
{
- // skip after somewhere?
- return nullptr;
+ Error error (generic_params.front ()->get_locus (),
+ "invalid order for generic parameters: lifetime parameters "
+ "must be declared prior to type and const parameters");
+ add_error (std::move (error));
}
-
- t = lexer.peek_token ();
- // see if separator token exists
- std::unique_ptr<AST::Token> separator = nullptr;
- switch (t->get_id ())
+ if (const_with_default_order_error)
{
- // repetition operators
- case ASTERISK:
- case PLUS:
- case QUESTION_MARK:
- // delimiters
- case LEFT_PAREN:
- case LEFT_CURLY:
- case LEFT_SQUARE:
- case RIGHT_PAREN:
- case RIGHT_CURLY:
- case RIGHT_SQUARE:
- // separator does not exist, so still null and don't skip token
- break;
- default:
- // separator does exist
- separator = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
- lexer.skip_token ();
- break;
+ Error error (generic_params.front ()->get_locus (),
+ "invalid order for generic parameters: generic parameters "
+ "with a default must be trailing");
+ add_error (std::move (error));
}
- // parse repetition operator
- t = lexer.peek_token ();
- AST::MacroMatchRepetition::MacroRepOp op = AST::MacroMatchRepetition::NONE;
- switch (t->get_id ())
+ generic_params.shrink_to_fit ();
+ return generic_params;
+}
+
+/* Parses lifetime generic parameters (pointers). Will also consume any
+ * trailing comma. No extra checks for end token. */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::LifetimeParam>>
+Parser<ManagedTokenSource>::parse_lifetime_params ()
+{
+ std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
+
+ while (lexer.peek_token ()->get_id () != END_OF_FILE)
{
- case ASTERISK:
- op = AST::MacroMatchRepetition::ANY;
- lexer.skip_token ();
- break;
- case PLUS:
- op = AST::MacroMatchRepetition::ONE_OR_MORE;
- lexer.skip_token ();
- break;
- case QUESTION_MARK:
- op = AST::MacroMatchRepetition::ZERO_OR_ONE;
- lexer.skip_token ();
+ auto lifetime_param = parse_lifetime_param ();
- if (separator != nullptr)
+ if (!lifetime_param)
{
- add_error (
- Error (separator->get_locus (),
- "the %<?%> macro repetition operator does not take a "
- "separator"));
- separator = nullptr;
+ // can't treat as error as only way to get out with trailing comma
+ break;
}
- break;
- default:
- add_error (
- Error (t->get_locus (),
- "expected macro repetition operator (%<*%>, %<+%>, or %<?%>) in "
- "macro match - found %qs",
- t->get_token_description ()));
+ lifetime_params.emplace_back (
+ new AST::LifetimeParam (std::move (lifetime_param.value ())));
- // skip after somewhere?
- return nullptr;
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+
+ // skip commas, including trailing commas
+ lexer.skip_token ();
}
- return std::unique_ptr<AST::MacroMatchRepetition> (
- new AST::MacroMatchRepetition (std::move (matches), op,
- std::move (separator), t->get_locus ()));
+ lifetime_params.shrink_to_fit ();
+
+ return lifetime_params;
}
-/* Parses a visibility syntactical production (i.e. creating a non-default
- * visibility) */
+/* Parses lifetime generic parameters (pointers). Will also consume any
+ * trailing comma. Has extra is_end_token predicate checking. */
template <typename ManagedTokenSource>
-tl::expected<AST::Visibility, Parse::Error::Visibility>
-Parser<ManagedTokenSource>::parse_visibility ()
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::LifetimeParam>>
+Parser<ManagedTokenSource>::parse_lifetime_params (EndTokenPred is_end_token)
{
- // check for no visibility
- if (lexer.peek_token ()->get_id () != PUB)
- {
- return AST::Visibility::create_private ();
- }
-
- auto vis_loc = lexer.peek_token ()->get_locus ();
- lexer.skip_token ();
+ std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
- // create simple pub visibility if
- // - found no parentheses
- // - found unit type `()`
- if (lexer.peek_token ()->get_id () != LEFT_PAREN
- || lexer.peek_token (1)->get_id () == RIGHT_PAREN)
+ // if end_token is not specified, it defaults to EOF, so should work fine
+ while (!is_end_token (lexer.peek_token ()->get_id ()))
{
- return AST::Visibility::create_public (vis_loc);
- // or whatever
- }
+ auto lifetime_param = parse_lifetime_param ();
- lexer.skip_token ();
+ if (!lifetime_param)
+ {
+ /* TODO: is it worth throwing away all lifetime params just because
+ * one failed? */
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime param in lifetime params");
+ add_error (std::move (error));
- const_TokenPtr t = lexer.peek_token ();
- auto path_loc = t->get_locus ();
+ return {};
+ }
- switch (t->get_id ())
- {
- case CRATE:
- lexer.skip_token ();
+ lifetime_params.emplace_back (
+ new AST::LifetimeParam (std::move (lifetime_param)));
- skip_token (RIGHT_PAREN);
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- return AST::Visibility::create_crate (path_loc, vis_loc);
- case SELF:
+ // skip commas, including trailing commas
lexer.skip_token ();
+ }
- skip_token (RIGHT_PAREN);
+ lifetime_params.shrink_to_fit ();
- return AST::Visibility::create_self (path_loc, vis_loc);
- case SUPER:
- lexer.skip_token ();
-
- skip_token (RIGHT_PAREN);
+ return lifetime_params;
+}
- return AST::Visibility::create_super (path_loc, vis_loc);
- case IN:
- {
- lexer.skip_token ();
+/* Parses lifetime generic parameters (objects). Will also consume any
+ * trailing comma. No extra checks for end token.
+ * TODO: is this best solution? implements most of the same algorithm.
+ * TODO: seems to be unused, remove? */
+template <typename ManagedTokenSource>
+std::vector<AST::LifetimeParam>
+Parser<ManagedTokenSource>::parse_lifetime_params_objs ()
+{
+ std::vector<AST::LifetimeParam> lifetime_params;
- // parse the "in" path as well
- auto path = parse_simple_path ();
- if (!path)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "missing path in pub(in path) visibility");
- add_error (std::move (error));
+ // bad control structure as end token cannot be guaranteed
+ while (true)
+ {
+ auto lifetime_param = parse_lifetime_param ();
- // skip after somewhere?
- return Parse::Error::Visibility::make_missing_path ();
- }
+ if (!lifetime_param)
+ {
+ // not an error as only way to exit if trailing comma
+ break;
+ }
- skip_token (RIGHT_PAREN);
+ lifetime_params.push_back (std::move (lifetime_param.value ()));
- return AST::Visibility::create_in_path (std::move (path.value ()),
- vis_loc);
- }
- default:
- add_error (Error (t->get_locus (), "unexpected token %qs in visibility",
- t->get_token_description ()));
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+ // skip commas, including trailing commas
lexer.skip_token ();
- return Parse::Error::Visibility::make_malformed ();
}
+
+ lifetime_params.shrink_to_fit ();
+
+ return lifetime_params;
}
-// Parses a module - either a bodied module or a module defined in another file.
+/* Parses lifetime generic parameters (objects). Will also consume any
+ * trailing comma. Has extra is_end_token predicate checking.
+ * TODO: is this best solution? implements most of the same algorithm. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Module>
-Parser<ManagedTokenSource>::parse_module (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+template <typename EndTokenPred>
+std::vector<AST::LifetimeParam>
+Parser<ManagedTokenSource>::parse_lifetime_params_objs (
+ EndTokenPred is_end_token)
{
- location_t locus = lexer.peek_token ()->get_locus ();
-
- Unsafety safety = Unsafety::Normal;
- if (lexer.peek_token ()->get_id () == UNSAFE)
- {
- safety = Unsafety::Unsafe;
- skip_token (UNSAFE);
- }
-
- skip_token (MOD);
+ std::vector<AST::LifetimeParam> lifetime_params;
- const_TokenPtr module_name = expect_token (IDENTIFIER);
- if (module_name == nullptr)
+ while (!is_end_token (lexer.peek_token ()->get_id ()))
{
- return nullptr;
- }
- Identifier name{module_name};
-
- const_TokenPtr t = lexer.peek_token ();
+ auto lifetime_param = parse_lifetime_param ();
- switch (t->get_id ())
- {
- case SEMICOLON:
- lexer.skip_token ();
+ if (!lifetime_param)
+ {
+ /* TODO: is it worth throwing away all lifetime params just because
+ * one failed? */
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime param in lifetime params");
+ add_error (std::move (error));
- // Construct an external module
- return std::unique_ptr<AST::Module> (
- new AST::Module (std::move (name), std::move (vis),
- std::move (outer_attrs), locus, safety,
- lexer.get_filename (), inline_module_stack));
- case LEFT_CURLY:
- {
- lexer.skip_token ();
+ return {};
+ }
- // parse inner attributes
- AST::AttrVec inner_attrs = parse_inner_attributes ();
+ lifetime_params.push_back (std::move (lifetime_param.value ()));
- std::string default_path = name.as_string ();
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- if (inline_module_stack.empty ())
- {
- std::string filename = lexer.get_filename ();
- auto slash_idx = filename.rfind (file_separator);
- if (slash_idx == std::string::npos)
- slash_idx = 0;
- else
- slash_idx++;
- filename = filename.substr (slash_idx);
+ // skip commas, including trailing commas
+ lexer.skip_token ();
+ }
- std::string subdir;
- if (get_file_subdir (filename, subdir))
- default_path = subdir + file_separator + name.as_string ();
- }
+ lifetime_params.shrink_to_fit ();
- std::string module_path_name
- = extract_module_path (inner_attrs, outer_attrs, default_path);
- InlineModuleStackScope scope (*this, std::move (module_path_name));
+ return lifetime_params;
+}
- // parse items
- std::vector<std::unique_ptr<AST::Item>> items;
- const_TokenPtr tok = lexer.peek_token ();
- while (tok->get_id () != RIGHT_CURLY)
- {
- auto item = parse_item (false);
- if (!item)
- {
- Error error (tok->get_locus (),
- "failed to parse item in module");
- add_error (std::move (error));
+/* Parses a sequence of a certain grammar rule in object form (not pointer or
+ * smart pointer), delimited by commas and ending when 'is_end_token' is
+ * satisfied (templated). Will also consume any trailing comma.
+ * FIXME: this cannot be used due to member function pointer problems (i.e.
+ * parsing_function cannot be specified properly) */
+template <typename ManagedTokenSource>
+template <typename ParseFunction, typename EndTokenPred>
+auto
+Parser<ManagedTokenSource>::parse_non_ptr_sequence (
+ ParseFunction parsing_function, EndTokenPred is_end_token,
+ std::string error_msg) -> std::vector<decltype (parsing_function ())>
+{
+ std::vector<decltype (parsing_function ())> params;
- return nullptr;
- }
+ while (!is_end_token (lexer.peek_token ()->get_id ()))
+ {
+ auto param = parsing_function ();
- items.push_back (std::move (item.value ()));
+ if (param.is_error ())
+ {
+ // TODO: is it worth throwing away all params just because one
+ // failed?
+ Error error (lexer.peek_token ()->get_locus (),
+ std::move (error_msg));
+ add_error (std::move (error));
- tok = lexer.peek_token ();
- }
+ return {};
+ }
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere?
- return nullptr;
- }
+ params.push_back (std::move (param));
- return std::unique_ptr<AST::Module> (
- new AST::Module (std::move (name), locus, std::move (items),
- std::move (vis), safety, std::move (inner_attrs),
- std::move (outer_attrs))); // module name?
- }
- default:
- add_error (
- Error (t->get_locus (),
- "unexpected token %qs in module declaration/definition item",
- t->get_token_description ()));
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+ // skip commas, including trailing commas
lexer.skip_token ();
- return nullptr;
}
+
+ params.shrink_to_fit ();
+
+ return params;
}
-// Parses an extern crate declaration (dependency on external crate)
+/* Parses a single lifetime generic parameter (not including comma). */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExternCrate>
-Parser<ManagedTokenSource>::parse_extern_crate (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+tl::expected<AST::LifetimeParam, ParseLifetimeParamError>
+Parser<ManagedTokenSource>::parse_lifetime_param ()
{
- location_t locus = lexer.peek_token ()->get_locus ();
- if (!skip_token (EXTERN_KW))
+ // parse outer attributes, which are optional and may not exist
+ auto outer_attrs = parse_outer_attributes ();
+
+ // save lifetime token - required
+ const_TokenPtr lifetime_tok = lexer.peek_token ();
+ if (lifetime_tok->get_id () != LIFETIME)
{
- skip_after_semicolon ();
- return nullptr;
+ // if lifetime is missing, must not be a lifetime param, so return error
+ return tl::make_unexpected<ParseLifetimeParamError> ({});
}
+ lexer.skip_token ();
+ AST::Lifetime lifetime (AST::Lifetime::NAMED, lifetime_tok->get_str (),
+ lifetime_tok->get_locus ());
- if (!skip_token (CRATE))
+ // parse lifetime bounds, if it exists
+ std::vector<AST::Lifetime> lifetime_bounds;
+ if (lexer.peek_token ()->get_id () == COLON)
{
- skip_after_semicolon ();
- return nullptr;
+ // parse lifetime bounds
+ lifetime_bounds = parse_lifetime_bounds ();
+ // TODO: have end token passed in?
}
- /* parse crate reference name - this has its own syntactical rule in reference
- * but seems to not be used elsewhere, so i'm putting it here */
- const_TokenPtr crate_name_tok = lexer.peek_token ();
- std::string crate_name;
+ return AST::LifetimeParam (std::move (lifetime), std::move (lifetime_bounds),
+ std::move (outer_attrs),
+ lifetime_tok->get_locus ());
+}
- switch (crate_name_tok->get_id ())
+// Parses type generic parameters. Will also consume any trailing comma.
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::TypeParam>>
+Parser<ManagedTokenSource>::parse_type_params ()
+{
+ std::vector<std::unique_ptr<AST::TypeParam>> type_params;
+
+ // infinite loop with break on failure as no info on ending token
+ while (true)
{
- case IDENTIFIER:
- crate_name = crate_name_tok->get_str ();
- lexer.skip_token ();
- break;
- case SELF:
- crate_name = Values::Keywords::SELF;
- lexer.skip_token ();
- break;
- default:
- add_error (
- Error (crate_name_tok->get_locus (),
- "expecting crate name (identifier or %<self%>), found %qs",
- crate_name_tok->get_token_description ()));
+ std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
- skip_after_semicolon ();
- return nullptr;
- }
+ if (type_param == nullptr)
+ {
+ // break if fails to parse
+ break;
+ }
- // don't parse as clause if it doesn't exist
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- {
- lexer.skip_token ();
+ type_params.push_back (std::move (type_param));
- return std::unique_ptr<AST::ExternCrate> (
- new AST::ExternCrate (std::move (crate_name), std::move (vis),
- std::move (outer_attrs), locus));
- }
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- /* parse as clause - this also has its own syntactical rule in reference and
- * also seems to not be used elsewhere, so including here again. */
- if (!skip_token (AS))
- {
- skip_after_semicolon ();
- return nullptr;
+ // skip commas, including trailing commas
+ lexer.skip_token ();
}
- const_TokenPtr as_name_tok = lexer.peek_token ();
- std::string as_name;
+ type_params.shrink_to_fit ();
+ return type_params;
+}
- switch (as_name_tok->get_id ())
+// Parses type generic parameters. Will also consume any trailing comma.
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::TypeParam>>
+Parser<ManagedTokenSource>::parse_type_params (EndTokenPred is_end_token)
+{
+ std::vector<std::unique_ptr<AST::TypeParam>> type_params;
+
+ while (!is_end_token (lexer.peek_token ()->get_id ()))
{
- case IDENTIFIER:
- as_name = as_name_tok->get_str ();
- lexer.skip_token ();
- break;
- case UNDERSCORE:
- as_name = Values::Keywords::UNDERSCORE;
- lexer.skip_token ();
- break;
- default:
- add_error (
- Error (as_name_tok->get_locus (),
- "expecting as clause name (identifier or %<_%>), found %qs",
- as_name_tok->get_token_description ()));
+ std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
- skip_after_semicolon ();
- return nullptr;
- }
+ if (type_param == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type param in type params");
+ add_error (std::move (error));
- if (!skip_token (SEMICOLON))
- {
- skip_after_semicolon ();
- return nullptr;
+ return {};
+ }
+
+ type_params.push_back (std::move (type_param));
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+
+ // skip commas, including trailing commas
+ lexer.skip_token ();
}
- return std::unique_ptr<AST::ExternCrate> (
- new AST::ExternCrate (std::move (crate_name), std::move (vis),
- std::move (outer_attrs), locus, std::move (as_name)));
+ type_params.shrink_to_fit ();
+ return type_params;
+ /* TODO: this shares most code with parse_lifetime_params - good place to
+ * use template (i.e. parse_non_ptr_sequence if doable) */
}
-// Parses a use declaration.
+/* Parses a single type (generic) parameter, not including commas. May change
+ * to return value. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::UseDeclaration>
-Parser<ManagedTokenSource>::parse_use_decl (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::TypeParam>
+Parser<ManagedTokenSource>::parse_type_param ()
{
- location_t locus = lexer.peek_token ()->get_locus ();
- if (!skip_token (USE))
+ // parse outer attributes, which are optional and may not exist
+ auto outer_attrs = parse_outer_attributes ();
+
+ const_TokenPtr identifier_tok = lexer.peek_token ();
+ if (identifier_tok->get_id () != IDENTIFIER)
{
- skip_after_semicolon ();
+ // return null as type param can't exist without this required
+ // identifier
return nullptr;
}
+ Identifier ident{identifier_tok};
+ lexer.skip_token ();
- // parse use tree, which is required
- std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
- if (use_tree == nullptr)
+ // parse type param bounds (if they exist)
+ std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+ if (lexer.peek_token ()->get_id () == COLON)
{
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse use tree in use declaration");
- add_error (std::move (error));
+ lexer.skip_token ();
- skip_after_semicolon ();
- return nullptr;
+ // parse type param bounds, which may or may not exist
+ type_param_bounds = parse_type_param_bounds ();
}
- if (!skip_token (SEMICOLON))
+ // parse type (if it exists)
+ std::unique_ptr<AST::Type> type = nullptr;
+ if (lexer.peek_token ()->get_id () == EQUAL)
{
- skip_after_semicolon ();
- return nullptr;
+ lexer.skip_token ();
+
+ // parse type (now required)
+ type = parse_type ();
+ if (type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type in type param");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
}
- return std::unique_ptr<AST::UseDeclaration> (
- new AST::UseDeclaration (std::move (use_tree), std::move (vis),
- std::move (outer_attrs), locus));
+ return std::unique_ptr<AST::TypeParam> (
+ new AST::TypeParam (std::move (ident), identifier_tok->get_locus (),
+ std::move (type_param_bounds), std::move (type),
+ std::move (outer_attrs)));
}
-// Parses a use tree (which can be recursive and is actually a base class).
+/* Parses regular (i.e. non-generic) parameters in functions or methods. Also
+ * has end token handling. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::UseTree>
-Parser<ManagedTokenSource>::parse_use_tree ()
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::Param>>
+Parser<ManagedTokenSource>::parse_function_params (EndTokenPred is_end_token)
{
- /* potential syntax definitions in attempt to get algorithm:
- * Glob:
- * <- SimplePath :: *
- * <- :: *
- * <- *
- * Nested tree thing:
- * <- SimplePath :: { COMPLICATED_INNER_TREE_THING }
- * <- :: COMPLICATED_INNER_TREE_THING }
- * <- { COMPLICATED_INNER_TREE_THING }
- * Rebind thing:
- * <- SimplePath as IDENTIFIER
- * <- SimplePath as _
- * <- SimplePath
- */
-
- /* current plan of attack: try to parse SimplePath first - if fails, one of
- * top two then try parse :: - if fails, one of top two. Next is deciding
- * character for top two. */
+ std::vector<std::unique_ptr<AST::Param>> params;
- /* Thus, parsing smaller parts of use tree may require feeding into function
- * via parameters (or could handle all in this single function because other
- * use tree types aren't recognised as separate in the spec) */
+ if (is_end_token (lexer.peek_token ()->get_id ()))
+ return params;
- // TODO: I think this function is too complex, probably should split it
+ auto initial_param = parse_function_param ();
- location_t locus = lexer.peek_token ()->get_locus ();
+ // Return empty parameter list if no parameter there
+ if (initial_param == nullptr)
+ {
+ // TODO: is this an error?
+ return params;
+ }
- // bool has_path = false;
- auto path = parse_simple_path ();
+ params.push_back (std::move (initial_param));
- if (!path)
+ // maybe think of a better control structure here - do-while with an initial
+ // error state? basically, loop through parameter list until can't find any
+ // more params
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () == COMMA)
{
- // has no path, so must be glob or nested tree UseTree type
-
- bool is_global = false;
+ // skip comma if applies
+ lexer.skip_token ();
- // check for global scope resolution operator
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
- {
- lexer.skip_token ();
- is_global = true;
- }
+ // TODO: strictly speaking, shouldn't there be no trailing comma?
+ if (is_end_token (lexer.peek_token ()->get_id ()))
+ break;
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
+ // now, as right paren would break, function param is required
+ auto param = parse_function_param ();
+ if (param == nullptr)
{
- case ASTERISK:
- // glob UseTree type
- lexer.skip_token ();
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse function param (in function params)");
+ add_error (std::move (error));
- if (is_global)
- return std::unique_ptr<AST::UseTreeGlob> (
- new AST::UseTreeGlob (AST::UseTreeGlob::GLOBAL,
- AST::SimplePath::create_empty (), locus));
- else
- return std::unique_ptr<AST::UseTreeGlob> (
- new AST::UseTreeGlob (AST::UseTreeGlob::NO_PATH,
- AST::SimplePath::create_empty (), locus));
- case LEFT_CURLY:
- {
- // nested tree UseTree type
- lexer.skip_token ();
+ // skip somewhere?
+ return std::vector<std::unique_ptr<AST::Param>> ();
+ }
- std::vector<std::unique_ptr<AST::UseTree>> use_trees;
+ params.push_back (std::move (param));
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
- {
- std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
- if (use_tree == nullptr)
- {
- break;
- }
+ t = lexer.peek_token ();
+ }
- use_trees.push_back (std::move (use_tree));
+ params.shrink_to_fit ();
+ return params;
+}
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+/* Parses a single regular (i.e. non-generic) parameter in a function or
+ * method, i.e. the "name: type" bit. Also handles it not existing. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Param>
+Parser<ManagedTokenSource>::parse_function_param ()
+{
+ // parse outer attributes if they exist
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
+ // TODO: should saved location be at start of outer attributes or pattern?
+ location_t locus = lexer.peek_token ()->get_locus ();
- // skip end curly delimiter
- if (!skip_token (RIGHT_CURLY))
- {
- // skip after somewhere?
- return nullptr;
- }
+ if (lexer.peek_token ()->get_id () == ELLIPSIS) // Unnamed variadic
+ {
+ lexer.skip_token (); // Skip ellipsis
+ return std::make_unique<AST::VariadicParam> (
+ AST::VariadicParam (std::move (outer_attrs), locus));
+ }
- if (is_global)
- return std::unique_ptr<AST::UseTreeList> (
- new AST::UseTreeList (AST::UseTreeList::GLOBAL,
- AST::SimplePath::create_empty (),
- std::move (use_trees), locus));
- else
- return std::unique_ptr<AST::UseTreeList> (
- new AST::UseTreeList (AST::UseTreeList::NO_PATH,
- AST::SimplePath::create_empty (),
- std::move (use_trees), locus));
- }
- case AS:
- // this is not allowed
- add_error (Error (
- t->get_locus (),
- "use declaration with rebind %<as%> requires a valid simple path - "
- "none found"));
+ std::unique_ptr<AST::Pattern> param_pattern = parse_pattern ();
- skip_after_semicolon ();
- return nullptr;
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in use tree with "
- "no valid simple path (i.e. list"
- " or glob use tree)",
- t->get_token_description ()));
+ // create error function param if it doesn't exist
+ if (param_pattern == nullptr)
+ {
+ // skip after something
+ return nullptr;
+ }
- skip_after_semicolon ();
- return nullptr;
- }
+ if (!skip_token (COLON))
+ {
+ // skip after something
+ return nullptr;
+ }
+
+ if (lexer.peek_token ()->get_id () == ELLIPSIS) // Named variadic
+ {
+ lexer.skip_token (); // Skip ellipsis
+ return std::make_unique<AST::VariadicParam> (
+ AST::VariadicParam (std::move (param_pattern), std::move (outer_attrs),
+ locus));
}
else
{
- const_TokenPtr t = lexer.peek_token ();
-
- switch (t->get_id ())
+ std::unique_ptr<AST::Type> param_type = parse_type ();
+ if (param_type == nullptr)
{
- case AS:
- {
- // rebind UseTree type
- lexer.skip_token ();
-
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case IDENTIFIER:
- // skip lexer token
- lexer.skip_token ();
-
- return std::unique_ptr<AST::UseTreeRebind> (
- new AST::UseTreeRebind (AST::UseTreeRebind::IDENTIFIER,
- std::move (path.value ()), locus, t));
- case UNDERSCORE:
- // skip lexer token
- lexer.skip_token ();
-
- return std::unique_ptr<AST::UseTreeRebind> (
- new AST::UseTreeRebind (AST::UseTreeRebind::WILDCARD,
- std::move (path.value ()), locus,
- {Values::Keywords::UNDERSCORE,
- t->get_locus ()}));
- default:
- add_error (Error (
- t->get_locus (),
- "unexpected token %qs in use tree with as clause - expected "
- "identifier or %<_%>",
- t->get_token_description ()));
-
- skip_after_semicolon ();
- return nullptr;
- }
- }
- case SEMICOLON:
- // rebind UseTree type without rebinding - path only
-
- // don't skip semicolon - handled in parse_use_tree
- // lexer.skip_token();
- case COMMA:
- case RIGHT_CURLY:
- // this may occur in recursive calls - assume it is ok and ignore it
- return std::unique_ptr<AST::UseTreeRebind> (
- new AST::UseTreeRebind (AST::UseTreeRebind::NONE,
- std::move (path.value ()), locus));
- case SCOPE_RESOLUTION:
- // keep going
- break;
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in use tree with valid path",
- t->get_token_description ()));
return nullptr;
}
+ return std::make_unique<AST::FunctionParam> (
+ AST::FunctionParam (std::move (param_pattern), std::move (param_type),
+ std::move (outer_attrs), locus));
+ }
+}
- skip_token ();
- t = lexer.peek_token ();
+/* Parses a function or method return type syntactical construction. Also
+ * handles a function return type not existing. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_function_return_type ()
+{
+ if (lexer.peek_token ()->get_id () != RETURN_TYPE)
+ return nullptr;
- switch (t->get_id ())
- {
- case ASTERISK:
- // glob UseTree type
- lexer.skip_token ();
+ // skip return type, as it now obviously exists
+ lexer.skip_token ();
- return std::unique_ptr<AST::UseTreeGlob> (
- new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
- std::move (path.value ()), locus));
- case LEFT_CURLY:
- {
- // nested tree UseTree type
- lexer.skip_token ();
+ std::unique_ptr<AST::Type> type = parse_type ();
- std::vector<std::unique_ptr<AST::UseTree>> use_trees;
+ return type;
+}
- // TODO: think of better control structure
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
- {
- std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
- if (use_tree == nullptr)
- {
- break;
- }
+/* Parses a "where clause" (in a function, struct, method, etc.). Also handles
+ * a where clause not existing, in which it will return
+ * WhereClause::create_empty(), which can be checked via
+ * WhereClause::is_empty(). */
+template <typename ManagedTokenSource>
+AST::WhereClause
+Parser<ManagedTokenSource>::parse_where_clause ()
+{
+ const_TokenPtr where_tok = lexer.peek_token ();
+ if (where_tok->get_id () != WHERE)
+ {
+ // where clause doesn't exist, so create empty one
+ return AST::WhereClause::create_empty ();
+ }
- use_trees.push_back (std::move (use_tree));
+ lexer.skip_token ();
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ /* parse where clause items - this is not a separate rule in the reference
+ * so won't be here */
+ std::vector<std::unique_ptr<AST::WhereClauseItem>> where_clause_items;
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
+ std::vector<AST::LifetimeParam> for_lifetimes;
+ if (lexer.peek_token ()->get_id () == FOR)
+ for_lifetimes = parse_for_lifetimes ();
- // skip end curly delimiter
- if (!skip_token (RIGHT_CURLY))
- {
- // skip after somewhere?
- return nullptr;
- }
+ /* HACK: where clauses end with a right curly or semicolon or equals in all
+ * uses currently */
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != LEFT_CURLY && t->get_id () != SEMICOLON
+ && t->get_id () != EQUAL)
+ {
+ std::unique_ptr<AST::WhereClauseItem> where_clause_item
+ = parse_where_clause_item (for_lifetimes);
- return std::unique_ptr<AST::UseTreeList> (
- new AST::UseTreeList (AST::UseTreeList::PATH_PREFIXED,
- std::move (path.value ()),
- std::move (use_trees), locus));
- }
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in use tree with valid path",
- t->get_token_description ()));
+ if (where_clause_item == nullptr)
+ {
+ Error error (t->get_locus (), "failed to parse where clause item");
+ add_error (std::move (error));
- // skip_after_semicolon();
- return nullptr;
+ return AST::WhereClause::create_empty ();
}
- }
-}
-// Parses a function (not a method).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Function>
-Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
- AST::AttrVec outer_attrs,
- bool is_external)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- // Get qualifiers for function if they exist
- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+ where_clause_items.push_back (std::move (where_clause_item));
- skip_token (FN_KW);
+ // also skip comma if it exists
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- // Save function name token
- const_TokenPtr function_name_tok = expect_token (IDENTIFIER);
- if (function_name_tok == nullptr)
- {
- skip_after_next_block ();
- return nullptr;
+ lexer.skip_token ();
+ t = lexer.peek_token ();
}
- Identifier function_name{function_name_tok};
- // parse generic params - if exist
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
+ where_clause_items.shrink_to_fit ();
+ return AST::WhereClause (std::move (where_clause_items));
+}
- if (!skip_token (LEFT_PAREN))
- {
- Error error (lexer.peek_token ()->get_locus (),
- "function declaration missing opening parentheses before "
- "parameter list");
- add_error (std::move (error));
+/* Parses a where clause item (lifetime or type bound). Does not parse any
+ * commas. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::WhereClauseItem>
+Parser<ManagedTokenSource>::parse_where_clause_item (
+ const std::vector<AST::LifetimeParam> &outer_for_lifetimes)
+{
+ // shitty cheat way of determining lifetime or type bound - test for
+ // lifetime
+ const_TokenPtr t = lexer.peek_token ();
- skip_after_next_block ();
+ if (t->get_id () == LIFETIME)
+ return parse_lifetime_where_clause_item ();
+ else
+ return parse_type_bound_where_clause_item (outer_for_lifetimes);
+}
+
+// Parses a lifetime where clause item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LifetimeWhereClauseItem>
+Parser<ManagedTokenSource>::parse_lifetime_where_clause_item ()
+{
+ auto parsed_lifetime = parse_lifetime (false);
+ if (!parsed_lifetime)
+ {
+ // TODO: error here?
return nullptr;
}
+ auto lifetime = parsed_lifetime.value ();
- auto initial_param = parse_self_param ();
-
- if (!initial_param.has_value ()
- && initial_param.error () != ParseSelfError::NOT_SELF)
- return nullptr;
+ if (!skip_token (COLON))
+ {
+ // TODO: skip after somewhere
+ return nullptr;
+ }
- if (initial_param.has_value () && lexer.peek_token ()->get_id () == COMMA)
- skip_token ();
+ std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds ();
+ // TODO: have end token passed in?
- // parse function parameters (only if next token isn't right paren)
- std::vector<std::unique_ptr<AST::Param>> function_params;
+ location_t locus = lifetime.get_locus ();
- if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
- function_params
- = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
+ return std::unique_ptr<AST::LifetimeWhereClauseItem> (
+ new AST::LifetimeWhereClauseItem (std::move (lifetime),
+ std::move (lifetime_bounds), locus));
+}
- if (initial_param.has_value ())
- function_params.insert (function_params.begin (),
- std::move (*initial_param));
+// Parses a type bound where clause item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeBoundWhereClauseItem>
+Parser<ManagedTokenSource>::parse_type_bound_where_clause_item (
+ const std::vector<AST::LifetimeParam> &outer_for_lifetimes)
+{
+ std::vector<AST::LifetimeParam> for_lifetimes = outer_for_lifetimes;
- if (!skip_token (RIGHT_PAREN))
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
{
- Error error (lexer.peek_token ()->get_locus (),
- "function declaration missing closing parentheses after "
- "parameter list");
- add_error (std::move (error));
-
- skip_after_next_block ();
return nullptr;
}
- // parse function return type - if exists
- std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
-
- // parse where clause - if exists
- AST::WhereClause where_clause = parse_where_clause ();
+ if (!skip_token (COLON))
+ {
+ // TODO: skip after somewhere
+ return nullptr;
+ }
- tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- lexer.skip_token ();
- else
+ if (lexer.peek_token ()->get_id () == FOR)
{
- std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
- if (block_expr == nullptr)
- return nullptr;
- body = std::move (block_expr);
+ auto for_lifetimes_inner = parse_for_lifetimes ();
+ for_lifetimes.insert (for_lifetimes.end (), for_lifetimes_inner.begin (),
+ for_lifetimes_inner.end ());
}
- return std::unique_ptr<AST::Function> (
- new AST::Function (std::move (function_name), std::move (qualifiers),
- std::move (generic_params), std::move (function_params),
- std::move (return_type), std::move (where_clause),
- std::move (body), std::move (vis),
- std::move (outer_attrs), locus, false, is_external));
+ // parse type param bounds if they exist
+ std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds
+ = parse_type_param_bounds ();
+
+ location_t locus = lexer.peek_token ()->get_locus ();
+
+ return std::unique_ptr<AST::TypeBoundWhereClauseItem> (
+ new AST::TypeBoundWhereClauseItem (std::move (for_lifetimes),
+ std::move (type),
+ std::move (type_param_bounds), locus));
}
-// Parses function or method qualifiers (i.e. const, unsafe, and extern).
+// Parses a for lifetimes clause, including the for keyword and angle
+// brackets.
template <typename ManagedTokenSource>
-AST::FunctionQualifiers
-Parser<ManagedTokenSource>::parse_function_qualifiers ()
+std::vector<AST::LifetimeParam>
+Parser<ManagedTokenSource>::parse_for_lifetimes ()
{
- Async async_status = Async::No;
- Const const_status = Const::No;
- Unsafety unsafe_status = Unsafety::Normal;
- bool has_extern = false;
- std::string abi;
+ std::vector<AST::LifetimeParam> params;
- const_TokenPtr t;
- location_t locus;
- // Check in order of const, unsafe, then extern
- for (int i = 0; i < 2; i++)
+ if (!skip_token (FOR))
{
- t = lexer.peek_token ();
- locus = t->get_locus ();
-
- switch (t->get_id ())
- {
- case CONST:
- lexer.skip_token ();
- const_status = Const::Yes;
- break;
- case ASYNC:
- lexer.skip_token ();
- async_status = Async::Yes;
- break;
- default:
- // const status is still none
- break;
- }
+ // skip after somewhere?
+ return params;
}
- if (lexer.peek_token ()->get_id () == UNSAFE)
+ if (!skip_token (LEFT_ANGLE))
{
- lexer.skip_token ();
- unsafe_status = Unsafety::Unsafe;
+ // skip after somewhere?
+ return params;
}
- if (lexer.peek_token ()->get_id () == EXTERN_KW)
+ /* cannot specify end token due to parsing problems with '>' tokens being
+ * nested */
+ params = parse_lifetime_params_objs (Parse::Utils::is_right_angle_tok);
+
+ if (!skip_generics_right_angle ())
{
- lexer.skip_token ();
- has_extern = true;
+ // DEBUG
+ rust_debug ("failed to skip generics right angle after (supposedly) "
+ "finished parsing where clause items");
+ // ok, well this gets called.
- // detect optional abi name
- const_TokenPtr next_tok = lexer.peek_token ();
- if (next_tok->get_id () == STRING_LITERAL)
- {
- lexer.skip_token ();
- abi = next_tok->get_str ();
- }
+ // skip after somewhere?
+ return params;
}
- return AST::FunctionQualifiers (locus, async_status, const_status,
- unsafe_status, has_extern, std::move (abi));
+ return params;
}
-// Parses generic (lifetime or type) params inside angle brackets (optional).
+// Parses type parameter bounds in where clause or generic arguments.
template <typename ManagedTokenSource>
-std::vector<std::unique_ptr<AST::GenericParam>>
-Parser<ManagedTokenSource>::parse_generic_params_in_angles ()
+std::vector<std::unique_ptr<AST::TypeParamBound>>
+Parser<ManagedTokenSource>::parse_type_param_bounds ()
{
- if (lexer.peek_token ()->get_id () != LEFT_ANGLE)
- {
- // seems to be no generic params, so exit with empty vector
- return std::vector<std::unique_ptr<AST::GenericParam>> ();
- }
- lexer.skip_token ();
-
- // DEBUG:
- rust_debug ("skipped left angle in generic param");
+ std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params (Parse::Utils::is_right_angle_tok);
+ std::unique_ptr<AST::TypeParamBound> initial_bound
+ = parse_type_param_bound ();
- // DEBUG:
- rust_debug ("finished parsing actual generic params (i.e. inside angles)");
+ // quick exit if null
+ if (initial_bound == nullptr)
+ {
+ /* error? type param bounds must have at least one term, but are bounds
+ * optional? */
+ return type_param_bounds;
+ }
+ type_param_bounds.push_back (std::move (initial_bound));
- if (!skip_generics_right_angle ())
+ while (lexer.peek_token ()->get_id () == PLUS)
{
- // DEBUG
- rust_debug ("failed to skip generics right angle - returning empty "
- "generic params");
+ lexer.skip_token ();
- return std::vector<std::unique_ptr<AST::GenericParam>> ();
+ std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ /* not an error: bound is allowed to be null as trailing plus is
+ * allowed */
+ return type_param_bounds;
+ }
+
+ type_param_bounds.push_back (std::move (bound));
}
- return generic_params;
+ type_param_bounds.shrink_to_fit ();
+ return type_param_bounds;
}
+/* Parses type parameter bounds in where clause or generic arguments, with end
+ * token handling. */
template <typename ManagedTokenSource>
template <typename EndTokenPred>
-std::unique_ptr<AST::GenericParam>
-Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token)
+std::vector<std::unique_ptr<AST::TypeParamBound>>
+Parser<ManagedTokenSource>::parse_type_param_bounds (EndTokenPred is_end_token)
{
- auto outer_attrs = parse_outer_attributes ();
- std::unique_ptr<AST::GenericParam> param;
- auto token = lexer.peek_token ();
+ std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
- switch (token->get_id ())
+ std::unique_ptr<AST::TypeParamBound> initial_bound
+ = parse_type_param_bound ();
+
+ // quick exit if null
+ if (initial_bound == nullptr)
{
- case LIFETIME:
- {
- auto lifetime = parse_lifetime (false);
- if (!lifetime)
- {
- rust_error_at (
- token->get_locus (),
- "failed to parse lifetime in generic parameter list");
- return nullptr;
- }
+ /* error? type param bounds must have at least one term, but are bounds
+ * optional? */
+ return type_param_bounds;
+ }
+ type_param_bounds.push_back (std::move (initial_bound));
- std::vector<AST::Lifetime> lifetime_bounds;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
- // parse required bounds
- lifetime_bounds
- = parse_lifetime_bounds ([is_end_token] (TokenId id) {
- return is_end_token (id) || id == COMMA;
- });
- }
+ while (lexer.peek_token ()->get_id () == PLUS)
+ {
+ lexer.skip_token ();
- param = std::unique_ptr<AST::LifetimeParam> (new AST::LifetimeParam (
- std::move (lifetime.value ()), std::move (lifetime_bounds),
- std::move (outer_attrs), token->get_locus ()));
+ // break if end token character
+ if (is_end_token (lexer.peek_token ()->get_id ()))
break;
- }
+
+ std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ // TODO how wise is it to ditch all bounds if only one failed?
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type param bound in type param bounds");
+ add_error (std::move (error));
+
+ return {};
+ }
+
+ type_param_bounds.push_back (std::move (bound));
+ }
+
+ type_param_bounds.shrink_to_fit ();
+ return type_param_bounds;
+}
+
+/* Parses a single type parameter bound in a where clause or generic argument.
+ * Does not parse the '+' between arguments. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeParamBound>
+Parser<ManagedTokenSource>::parse_type_param_bound ()
+{
+ // shitty cheat way of determining lifetime or trait bound - test for
+ // lifetime
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case LIFETIME:
+ return std::unique_ptr<AST::Lifetime> (
+ new AST::Lifetime (parse_lifetime (false).value ()));
+ case LEFT_PAREN:
+ case QUESTION_MARK:
+ case FOR:
case IDENTIFIER:
- {
- auto type_ident = token->get_str ();
- lexer.skip_token ();
+ case SUPER:
+ case SELF:
+ case SELF_ALIAS:
+ case CRATE:
+ case DOLLAR_SIGN:
+ case SCOPE_RESOLUTION:
+ return parse_trait_bound ();
+ default:
+ // don't error - assume this is fine TODO
+ return nullptr;
+ }
+}
- std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
+// Parses a trait bound type param bound.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitBound>
+Parser<ManagedTokenSource>::parse_trait_bound ()
+{
+ bool has_parens = false;
+ bool has_question_mark = false;
- // parse optional type param bounds
- type_param_bounds = parse_type_param_bounds ();
- }
+ location_t locus = lexer.peek_token ()->get_locus ();
- std::unique_ptr<AST::Type> type = nullptr;
- if (lexer.peek_token ()->get_id () == EQUAL)
- {
- lexer.skip_token ();
+ /* parse optional `for lifetimes`. */
+ std::vector<AST::LifetimeParam> for_lifetimes;
+ if (lexer.peek_token ()->get_id () == FOR)
+ for_lifetimes = parse_for_lifetimes ();
- // parse required type
- type = parse_type ();
- if (!type)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse type in type param in generic params");
- return nullptr;
- }
- }
+ // handle trait bound being in parentheses
+ if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+ {
+ has_parens = true;
+ lexer.skip_token ();
+ }
- param = std::unique_ptr<AST::TypeParam> (
- new AST::TypeParam (std::move (type_ident), token->get_locus (),
- std::move (type_param_bounds), std::move (type),
- std::move (outer_attrs)));
- break;
- }
- case CONST:
- {
- lexer.skip_token ();
+ // handle having question mark (optional)
+ if (lexer.peek_token ()->get_id () == QUESTION_MARK)
+ {
+ has_question_mark = true;
+ lexer.skip_token ();
+ }
- auto name_token = expect_token (IDENTIFIER);
+ // handle TypePath
+ AST::TypePath type_path = parse_type_path ();
- if (!name_token || !expect_token (COLON))
+ // handle closing parentheses
+ if (has_parens)
+ {
+ if (!skip_token (RIGHT_PAREN))
+ {
return nullptr;
+ }
+ }
- auto type = parse_type ();
- if (!type)
- return nullptr;
+ return std::unique_ptr<AST::TraitBound> (
+ new AST::TraitBound (std::move (type_path), locus, has_parens,
+ has_question_mark, std::move (for_lifetimes)));
+}
- // optional default value
- tl::optional<AST::GenericArg> default_expr = tl::nullopt;
- if (lexer.peek_token ()->get_id () == EQUAL)
- {
- lexer.skip_token ();
- auto tok = lexer.peek_token ();
- default_expr = parse_generic_arg ();
+// Parses lifetime bounds.
+template <typename ManagedTokenSource>
+std::vector<AST::Lifetime>
+Parser<ManagedTokenSource>::parse_lifetime_bounds ()
+{
+ std::vector<AST::Lifetime> lifetime_bounds;
- if (!default_expr)
- {
- rust_error_at (tok->get_locus (),
- "invalid token for start of default value for "
- "const generic parameter: expected %<block%>, "
- "%<identifier%> or %<literal%>, got %qs",
- token_id_to_str (tok->get_id ()));
- return nullptr;
- }
+ while (true)
+ {
+ auto lifetime = parse_lifetime (false);
- // At this point, we *know* that we are parsing a const
- // expression
- if (default_expr.value ().get_kind ()
- == AST::GenericArg::Kind::Either)
- default_expr = default_expr.value ().disambiguate_to_const ();
- }
+ // quick exit for parsing failure
+ if (!lifetime)
+ break;
- param = std::unique_ptr<AST::ConstGenericParam> (
- new AST::ConstGenericParam (name_token->get_str (), std::move (type),
- default_expr, std::move (outer_attrs),
- token->get_locus ()));
+ lifetime_bounds.push_back (std::move (lifetime.value ()));
+ /* plus is maybe not allowed at end - spec defines it weirdly, so
+ * assuming allowed at end */
+ if (lexer.peek_token ()->get_id () != PLUS)
break;
- }
- default:
- // FIXME: Can we clean this last call with a method call?
- rust_error_at (token->get_locus (),
- "unexpected token when parsing generic parameters: %qs",
- token->as_string ().c_str ());
- return nullptr;
+
+ lexer.skip_token ();
}
- return param;
+ lifetime_bounds.shrink_to_fit ();
+ return lifetime_bounds;
}
-/* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost
- * always parse_generic_params_in_angles is what is wanted. */
+// Parses lifetime bounds, with added check for ending token.
template <typename ManagedTokenSource>
template <typename EndTokenPred>
-std::vector<std::unique_ptr<AST::GenericParam>>
-Parser<ManagedTokenSource>::parse_generic_params (EndTokenPred is_end_token)
+std::vector<AST::Lifetime>
+Parser<ManagedTokenSource>::parse_lifetime_bounds (EndTokenPred is_end_token)
{
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
-
- /* can't parse lifetime and type params separately due to lookahead issues
- * thus, parse them all here */
-
- /* HACK: used to retain attribute data if a lifetime param is tentatively
- * parsed but it turns out to be type param */
- AST::Attribute parsed_outer_attr = AST::Attribute::create_empty ();
-
- // Did we parse a generic type param yet
- auto type_seen = false;
- // Did we parse a const param with a default value yet
- auto const_with_default_seen = false;
- // Did the user write a lifetime parameter after a type one
- auto order_error = false;
- // Did the user write a const param with a default value after a type one
- auto const_with_default_order_error = false;
+ std::vector<AST::Lifetime> lifetime_bounds;
- // parse lifetime params
while (!is_end_token (lexer.peek_token ()->get_id ()))
{
- auto param = parse_generic_param (is_end_token);
- if (param)
+ auto lifetime = parse_lifetime (false);
+
+ if (!lifetime)
{
- if (param->get_kind () == AST::GenericParam::Kind::Type)
- {
- type_seen = true;
- if (const_with_default_seen)
- const_with_default_order_error = true;
- }
- else if (param->get_kind () == AST::GenericParam::Kind::Lifetime
- && type_seen)
- {
- order_error = true;
- if (const_with_default_seen)
- const_with_default_order_error = true;
- }
- else if (param->get_kind () == AST::GenericParam::Kind::Const)
- {
- type_seen = true;
- AST::ConstGenericParam *const_param
- = static_cast<AST::ConstGenericParam *> (param.get ());
- if (const_param->has_default_value ())
- const_with_default_seen = true;
- else if (const_with_default_seen)
- const_with_default_order_error = true;
- }
+ /* TODO: is it worth throwing away all lifetime bound info just
+ * because one failed? */
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime in lifetime bounds");
+ add_error (std::move (error));
- generic_params.emplace_back (std::move (param));
- maybe_skip_token (COMMA);
+ return {};
}
- else
+
+ lifetime_bounds.push_back (std::move (lifetime.value ()));
+
+ /* plus is maybe not allowed at end - spec defines it weirdly, so
+ * assuming allowed at end */
+ if (lexer.peek_token ()->get_id () != PLUS)
break;
- }
- // FIXME: Add reordering hint
- if (order_error)
- {
- Error error (generic_params.front ()->get_locus (),
- "invalid order for generic parameters: lifetime parameters "
- "must be declared prior to type and const parameters");
- add_error (std::move (error));
- }
- if (const_with_default_order_error)
- {
- Error error (generic_params.front ()->get_locus (),
- "invalid order for generic parameters: generic parameters "
- "with a default must be trailing");
- add_error (std::move (error));
+ lexer.skip_token ();
}
- generic_params.shrink_to_fit ();
- return generic_params;
+ lifetime_bounds.shrink_to_fit ();
+ return lifetime_bounds;
}
-/* Parses lifetime generic parameters (pointers). Will also consume any
- * trailing comma. No extra checks for end token. */
+/* Parses a lifetime token (named, 'static, or '_). Also handles lifetime not
+ * existing. */
template <typename ManagedTokenSource>
-std::vector<std::unique_ptr<AST::LifetimeParam>>
-Parser<ManagedTokenSource>::parse_lifetime_params ()
+tl::expected<AST::Lifetime, ParseLifetimeError>
+Parser<ManagedTokenSource>::parse_lifetime (bool allow_elided)
{
- std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
-
- while (lexer.peek_token ()->get_id () != END_OF_FILE)
+ const_TokenPtr lifetime_tok = lexer.peek_token ();
+ if (lifetime_tok->get_id () != LIFETIME)
{
- auto lifetime_param = parse_lifetime_param ();
-
- if (!lifetime_param)
+ if (allow_elided)
{
- // can't treat as error as only way to get out with trailing comma
- break;
+ return AST::Lifetime::elided ();
+ }
+ else
+ {
+ return tl::make_unexpected<ParseLifetimeError> ({});
}
-
- lifetime_params.emplace_back (
- new AST::LifetimeParam (std::move (lifetime_param.value ())));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- // skip commas, including trailing commas
- lexer.skip_token ();
}
+ lexer.skip_token ();
- lifetime_params.shrink_to_fit ();
-
- return lifetime_params;
+ return lifetime_from_token (lifetime_tok);
}
-/* Parses lifetime generic parameters (pointers). Will also consume any
- * trailing comma. Has extra is_end_token predicate checking. */
template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<std::unique_ptr<AST::LifetimeParam>>
-Parser<ManagedTokenSource>::parse_lifetime_params (EndTokenPred is_end_token)
+AST::Lifetime
+Parser<ManagedTokenSource>::lifetime_from_token (const_TokenPtr tok)
{
- std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
+ location_t locus = tok->get_locus ();
+ std::string lifetime_ident = tok->get_str ();
- // if end_token is not specified, it defaults to EOF, so should work fine
- while (!is_end_token (lexer.peek_token ()->get_id ()))
+ if (lifetime_ident == "static")
{
- auto lifetime_param = parse_lifetime_param ();
-
- if (!lifetime_param)
- {
- /* TODO: is it worth throwing away all lifetime params just because
- * one failed? */
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime param in lifetime params");
- add_error (std::move (error));
-
- return {};
- }
-
- lifetime_params.emplace_back (
- new AST::LifetimeParam (std::move (lifetime_param)));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- // skip commas, including trailing commas
- lexer.skip_token ();
+ return AST::Lifetime (AST::Lifetime::STATIC, "", locus);
+ }
+ else if (lifetime_ident == "_")
+ {
+ // Explicitly and implicitly elided lifetimes follow the same rules.
+ return AST::Lifetime (AST::Lifetime::WILDCARD, "", locus);
+ }
+ else
+ {
+ return AST::Lifetime (AST::Lifetime::NAMED, std::move (lifetime_ident),
+ locus);
}
-
- lifetime_params.shrink_to_fit ();
-
- return lifetime_params;
}
-/* Parses lifetime generic parameters (objects). Will also consume any
- * trailing comma. No extra checks for end token.
- * TODO: is this best solution? implements most of the same algorithm.
- * TODO: seems to be unused, remove? */
template <typename ManagedTokenSource>
-std::vector<AST::LifetimeParam>
-Parser<ManagedTokenSource>::parse_lifetime_params_objs ()
+std::unique_ptr<AST::ExternalTypeItem>
+Parser<ManagedTokenSource>::parse_external_type_item (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- std::vector<AST::LifetimeParam> lifetime_params;
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (TYPE);
- // bad control structure as end token cannot be guaranteed
- while (true)
+ const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
+ if (alias_name_tok == nullptr)
{
- auto lifetime_param = parse_lifetime_param ();
-
- if (!lifetime_param)
- {
- // not an error as only way to exit if trailing comma
- break;
- }
-
- lifetime_params.push_back (std::move (lifetime_param.value ()));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse identifier in external opaque type");
+ add_error (std::move (error));
- // skip commas, including trailing commas
- lexer.skip_token ();
+ skip_after_semicolon ();
+ return nullptr;
}
- lifetime_params.shrink_to_fit ();
+ if (!skip_token (SEMICOLON))
+ return nullptr;
- return lifetime_params;
+ return std::unique_ptr<AST::ExternalTypeItem> (
+ new AST::ExternalTypeItem (alias_name_tok->get_str (), std::move (vis),
+ std::move (outer_attrs), std::move (locus)));
}
-/* Parses lifetime generic parameters (objects). Will also consume any
- * trailing comma. Has extra is_end_token predicate checking.
- * TODO: is this best solution? implements most of the same algorithm. */
+// Parses a "type alias" (typedef) item.
template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<AST::LifetimeParam>
-Parser<ManagedTokenSource>::parse_lifetime_params_objs (
- EndTokenPred is_end_token)
+std::unique_ptr<AST::TypeAlias>
+Parser<ManagedTokenSource>::parse_type_alias (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- std::vector<AST::LifetimeParam> lifetime_params;
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (TYPE);
- while (!is_end_token (lexer.peek_token ()->get_id ()))
+ // TODO: use this token for identifier when finished that
+ const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
+ if (alias_name_tok == nullptr)
{
- auto lifetime_param = parse_lifetime_param ();
-
- if (!lifetime_param)
- {
- /* TODO: is it worth throwing away all lifetime params just because
- * one failed? */
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime param in lifetime params");
- add_error (std::move (error));
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse identifier in type alias");
+ add_error (std::move (error));
- return {};
- }
+ skip_after_semicolon ();
+ return nullptr;
+ }
+ Identifier alias_name{alias_name_tok};
- lifetime_params.push_back (std::move (lifetime_param.value ()));
+ // parse generic params, which may not exist
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ // parse where clause, which may not exist
+ AST::WhereClause where_clause = parse_where_clause ();
- // skip commas, including trailing commas
- lexer.skip_token ();
+ if (!skip_token (EQUAL))
+ {
+ skip_after_semicolon ();
+ return nullptr;
}
- lifetime_params.shrink_to_fit ();
+ std::unique_ptr<AST::Type> type_to_alias = parse_type ();
- return lifetime_params;
+ if (!skip_token (SEMICOLON))
+ {
+ // should be skipping past this, not the next line
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::TypeAlias> (
+ new AST::TypeAlias (std::move (alias_name), std::move (generic_params),
+ std::move (where_clause), std::move (type_to_alias),
+ std::move (vis), std::move (outer_attrs), locus));
}
-/* Parses a sequence of a certain grammar rule in object form (not pointer or
- * smart pointer), delimited by commas and ending when 'is_end_token' is
- * satisfied (templated). Will also consume any trailing comma.
- * FIXME: this cannot be used due to member function pointer problems (i.e.
- * parsing_function cannot be specified properly) */
+// Parse a struct item AST node.
template <typename ManagedTokenSource>
-template <typename ParseFunction, typename EndTokenPred>
-auto
-Parser<ManagedTokenSource>::parse_non_ptr_sequence (
- ParseFunction parsing_function, EndTokenPred is_end_token,
- std::string error_msg) -> std::vector<decltype (parsing_function ())>
+std::unique_ptr<AST::Struct>
+Parser<ManagedTokenSource>::parse_struct (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- std::vector<decltype (parsing_function ())> params;
+ /* TODO: determine best way to parse the proper struct vs tuple struct -
+ * share most of initial constructs so lookahead might be impossible, and if
+ * not probably too expensive. Best way is probably unified parsing for the
+ * initial parts and then pass them in as params to more derived functions.
+ * Alternatively, just parse everything in this one function - do this if
+ * function not too long. */
- while (!is_end_token (lexer.peek_token ()->get_id ()))
- {
- auto param = parsing_function ();
+ /* Proper struct <- 'struct' IDENTIFIER generic_params? where_clause? ( '{'
+ * struct_fields? '}' | ';' ) */
+ /* Tuple struct <- 'struct' IDENTIFIER generic_params? '(' tuple_fields? ')'
+ * where_clause? ';' */
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (STRUCT_KW);
- if (param.is_error ())
- {
- // TODO: is it worth throwing away all params just because one
- // failed?
- Error error (lexer.peek_token ()->get_locus (),
- std::move (error_msg));
- add_error (std::move (error));
+ // parse struct name
+ const_TokenPtr name_tok = expect_token (IDENTIFIER);
+ if (name_tok == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse struct or tuple struct identifier");
+ add_error (std::move (error));
- return {};
- }
+ // skip after somewhere?
+ return nullptr;
+ }
+ Identifier struct_name{name_tok};
- params.push_back (std::move (param));
+ // parse generic params, which may or may not exist
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ // branch on next token - determines whether proper struct or tuple struct
+ if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+ {
+ // tuple struct
- // skip commas, including trailing commas
+ // skip left parenthesis
lexer.skip_token ();
- }
- params.shrink_to_fit ();
+ // parse tuple fields
+ std::vector<AST::TupleField> tuple_fields;
+ // Might be empty tuple for unit tuple struct.
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ tuple_fields = std::vector<AST::TupleField> ();
+ else
+ tuple_fields = parse_tuple_fields ();
- return params;
-}
+ // tuple parameters must have closing parenthesis
+ if (!skip_token (RIGHT_PAREN))
+ {
+ skip_after_semicolon ();
+ return nullptr;
+ }
-/* Parses a single lifetime generic parameter (not including comma). */
-template <typename ManagedTokenSource>
-tl::expected<AST::LifetimeParam, ParseLifetimeParamError>
-Parser<ManagedTokenSource>::parse_lifetime_param ()
-{
- // parse outer attributes, which are optional and may not exist
- auto outer_attrs = parse_outer_attributes ();
+ // parse where clause, which is optional
+ AST::WhereClause where_clause = parse_where_clause ();
- // save lifetime token - required
- const_TokenPtr lifetime_tok = lexer.peek_token ();
- if (lifetime_tok->get_id () != LIFETIME)
- {
- // if lifetime is missing, must not be a lifetime param, so return error
- return tl::make_unexpected<ParseLifetimeParamError> ({});
- }
- lexer.skip_token ();
- AST::Lifetime lifetime (AST::Lifetime::NAMED, lifetime_tok->get_str (),
- lifetime_tok->get_locus ());
+ if (!skip_token (SEMICOLON))
+ {
+ // can't skip after semicolon because it's meant to be here
+ return nullptr;
+ }
- // parse lifetime bounds, if it exists
- std::vector<AST::Lifetime> lifetime_bounds;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- // parse lifetime bounds
- lifetime_bounds = parse_lifetime_bounds ();
- // TODO: have end token passed in?
+ return std::unique_ptr<AST::TupleStruct> (
+ new AST::TupleStruct (std::move (tuple_fields), std::move (struct_name),
+ std::move (generic_params),
+ std::move (where_clause), std::move (vis),
+ std::move (outer_attrs), locus));
}
- return AST::LifetimeParam (std::move (lifetime), std::move (lifetime_bounds),
- std::move (outer_attrs),
- lifetime_tok->get_locus ());
-}
+ // assume it is a proper struct being parsed and continue outside of switch
+ // - label only here to suppress warning
-// Parses type generic parameters. Will also consume any trailing comma.
-template <typename ManagedTokenSource>
-std::vector<std::unique_ptr<AST::TypeParam>>
-Parser<ManagedTokenSource>::parse_type_params ()
-{
- std::vector<std::unique_ptr<AST::TypeParam>> type_params;
+ // parse where clause, which is optional
+ AST::WhereClause where_clause = parse_where_clause ();
- // infinite loop with break on failure as no info on ending token
- while (true)
+ // branch on next token - determines whether struct is a unit struct
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
{
- std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
+ case LEFT_CURLY:
+ {
+ // struct with body
- if (type_param == nullptr)
- {
- // break if fails to parse
- break;
- }
+ // skip curly bracket
+ lexer.skip_token ();
- type_params.push_back (std::move (type_param));
+ // parse struct fields, if any
+ std::vector<AST::StructField> struct_fields
+ = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::StructStruct> (new AST::StructStruct (
+ std::move (struct_fields), std::move (struct_name),
+ std::move (generic_params), std::move (where_clause), false,
+ std::move (vis), std::move (outer_attrs), locus));
+ }
+ case SEMICOLON:
+ // unit struct declaration
- // skip commas, including trailing commas
lexer.skip_token ();
- }
- type_params.shrink_to_fit ();
- return type_params;
+ return std::unique_ptr<AST::StructStruct> (
+ new AST::StructStruct (std::move (struct_name),
+ std::move (generic_params),
+ std::move (where_clause), std::move (vis),
+ std::move (outer_attrs), locus));
+ default:
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in struct declaration",
+ t->get_token_description ()));
+
+ // skip somewhere?
+ return nullptr;
+ }
}
-// Parses type generic parameters. Will also consume any trailing comma.
+// Parses struct fields in struct declarations.
template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<std::unique_ptr<AST::TypeParam>>
-Parser<ManagedTokenSource>::parse_type_params (EndTokenPred is_end_token)
+std::vector<AST::StructField>
+Parser<ManagedTokenSource>::parse_struct_fields ()
{
- std::vector<std::unique_ptr<AST::TypeParam>> type_params;
+ std::vector<AST::StructField> fields;
- while (!is_end_token (lexer.peek_token ()->get_id ()))
- {
- std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
+ AST::StructField initial_field = parse_struct_field ();
- if (type_param == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type param in type params");
- add_error (std::move (error));
+ // Return empty field list if no field there
+ if (initial_field.is_error ())
+ return fields;
- return {};
- }
+ fields.push_back (std::move (initial_field));
- type_params.push_back (std::move (type_param));
+ while (lexer.peek_token ()->get_id () == COMMA)
+ {
+ lexer.skip_token ();
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ AST::StructField field = parse_struct_field ();
- // skip commas, including trailing commas
- lexer.skip_token ();
+ if (field.is_error ())
+ {
+ // would occur with trailing comma, so allowed
+ break;
+ }
+
+ fields.push_back (std::move (field));
}
- type_params.shrink_to_fit ();
- return type_params;
- /* TODO: this shares most code with parse_lifetime_params - good place to
- * use template (i.e. parse_non_ptr_sequence if doable) */
+ fields.shrink_to_fit ();
+ return fields;
+ // TODO: template if possible (parse_non_ptr_seq)
}
-/* Parses a single type (generic) parameter, not including commas. May change
- * to return value. */
+// Parses struct fields in struct declarations.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeParam>
-Parser<ManagedTokenSource>::parse_type_param ()
+template <typename EndTokenPred>
+std::vector<AST::StructField>
+Parser<ManagedTokenSource>::parse_struct_fields (EndTokenPred is_end_tok)
{
- // parse outer attributes, which are optional and may not exist
- auto outer_attrs = parse_outer_attributes ();
+ std::vector<AST::StructField> fields;
- const_TokenPtr identifier_tok = lexer.peek_token ();
- if (identifier_tok->get_id () != IDENTIFIER)
- {
- // return null as type param can't exist without this required
- // identifier
- return nullptr;
- }
- Identifier ident{identifier_tok};
- lexer.skip_token ();
+ AST::StructField initial_field = parse_struct_field ();
- // parse type param bounds (if they exist)
- std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
+ // Return empty field list if no field there
+ if (initial_field.is_error ())
+ return fields;
- // parse type param bounds, which may or may not exist
- type_param_bounds = parse_type_param_bounds ();
- }
+ fields.push_back (std::move (initial_field));
- // parse type (if it exists)
- std::unique_ptr<AST::Type> type = nullptr;
- if (lexer.peek_token ()->get_id () == EQUAL)
+ while (lexer.peek_token ()->get_id () == COMMA)
{
lexer.skip_token ();
- // parse type (now required)
- type = parse_type ();
- if (type == nullptr)
+ if (is_end_tok (lexer.peek_token ()->get_id ()))
+ break;
+
+ AST::StructField field = parse_struct_field ();
+ if (field.is_error ())
{
+ /* TODO: should every field be ditched just because one couldn't be
+ * parsed? */
Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type in type param");
+ "failed to parse struct field in struct fields");
add_error (std::move (error));
- return nullptr;
+ return {};
}
- }
- return std::unique_ptr<AST::TypeParam> (
- new AST::TypeParam (std::move (ident), identifier_tok->get_locus (),
- std::move (type_param_bounds), std::move (type),
- std::move (outer_attrs)));
+ fields.push_back (std::move (field));
+ }
+
+ fields.shrink_to_fit ();
+ return fields;
+ // TODO: template if possible (parse_non_ptr_seq)
}
-/* Parses regular (i.e. non-generic) parameters in functions or methods. Also
- * has end token handling. */
+// Parses a single struct field (in a struct definition). Does not parse
+// commas.
template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<std::unique_ptr<AST::Param>>
-Parser<ManagedTokenSource>::parse_function_params (EndTokenPred is_end_token)
+AST::StructField
+Parser<ManagedTokenSource>::parse_struct_field ()
{
- std::vector<std::unique_ptr<AST::Param>> params;
+ // parse outer attributes, if they exist
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
- if (is_end_token (lexer.peek_token ()->get_id ()))
- return params;
+ // parse visibility, if it exists
+ auto vis = parse_visibility ();
+ if (!vis)
+ return AST::StructField::create_error ();
- auto initial_param = parse_function_param ();
+ location_t locus = lexer.peek_token ()->get_locus ();
- // Return empty parameter list if no parameter there
- if (initial_param == nullptr)
+ // parse field name
+ const_TokenPtr field_name_tok = lexer.peek_token ();
+ if (field_name_tok->get_id () != IDENTIFIER)
{
- // TODO: is this an error?
- return params;
+ // if not identifier, assumes there is no struct field and exits - not
+ // necessarily error
+ return AST::StructField::create_error ();
}
+ Identifier field_name{field_name_tok};
+ lexer.skip_token ();
- params.push_back (std::move (initial_param));
+ if (!skip_token (COLON))
+ {
+ // skip after somewhere?
+ return AST::StructField::create_error ();
+ }
+
+ // parse field type - this is required
+ std::unique_ptr<AST::Type> field_type = parse_type ();
+ if (field_type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse type in struct field definition");
+ add_error (std::move (error));
+
+ // skip after somewhere
+ return AST::StructField::create_error ();
+ }
+
+ return AST::StructField (std::move (field_name), std::move (field_type),
+ std::move (vis.value ()), locus,
+ std::move (outer_attrs));
+}
+
+// Parses tuple fields in tuple/tuple struct declarations.
+template <typename ManagedTokenSource>
+std::vector<AST::TupleField>
+Parser<ManagedTokenSource>::parse_tuple_fields ()
+{
+ std::vector<AST::TupleField> fields;
+
+ AST::TupleField initial_field = parse_tuple_field ();
+
+ // Return empty field list if no field there
+ if (initial_field.is_error ())
+ {
+ return fields;
+ }
+
+ fields.push_back (std::move (initial_field));
// maybe think of a better control structure here - do-while with an initial
- // error state? basically, loop through parameter list until can't find any
- // more params
+ // error state? basically, loop through field list until can't find any more
+ // params HACK: all current syntax uses of tuple fields have them ending
+ // with a right paren token
const_TokenPtr t = lexer.peek_token ();
while (t->get_id () == COMMA)
{
- // skip comma if applies
+ // skip comma if applies - e.g. trailing comma
lexer.skip_token ();
- // TODO: strictly speaking, shouldn't there be no trailing comma?
- if (is_end_token (lexer.peek_token ()->get_id ()))
- break;
+ // break out due to right paren if it exists
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ {
+ break;
+ }
- // now, as right paren would break, function param is required
- auto param = parse_function_param ();
- if (param == nullptr)
+ AST::TupleField field = parse_tuple_field ();
+ if (field.is_error ())
{
Error error (lexer.peek_token ()->get_locus (),
- "failed to parse function param (in function params)");
+ "failed to parse tuple field in tuple fields");
add_error (std::move (error));
- // skip somewhere?
- return std::vector<std::unique_ptr<AST::Param>> ();
+ return std::vector<AST::TupleField> ();
}
- params.push_back (std::move (param));
+ fields.push_back (std::move (field));
t = lexer.peek_token ();
}
- params.shrink_to_fit ();
- return params;
+ fields.shrink_to_fit ();
+ return fields;
+
+ // TODO: this shares basically all code with function params and struct
+ // fields
+ // - templates?
}
-/* Parses a single regular (i.e. non-generic) parameter in a function or
- * method, i.e. the "name: type" bit. Also handles it not existing. */
+/* Parses a single tuple struct field in a tuple struct definition. Does not
+ * parse commas. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Param>
-Parser<ManagedTokenSource>::parse_function_param ()
+AST::TupleField
+Parser<ManagedTokenSource>::parse_tuple_field ()
{
// parse outer attributes if they exist
AST::AttrVec outer_attrs = parse_outer_attributes ();
- // TODO: should saved location be at start of outer attributes or pattern?
+ // parse visibility if it exists
+ auto visibility = parse_visibility ();
+ if (!visibility)
+ return AST::TupleField::create_error ();
+
location_t locus = lexer.peek_token ()->get_locus ();
- if (lexer.peek_token ()->get_id () == ELLIPSIS) // Unnamed variadic
+ // parse type, which is required
+ std::unique_ptr<AST::Type> field_type = parse_type ();
+ if (field_type == nullptr)
{
- lexer.skip_token (); // Skip ellipsis
- return std::make_unique<AST::VariadicParam> (
- AST::VariadicParam (std::move (outer_attrs), locus));
+ // error if null
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse type in tuple struct field");
+ add_error (std::move (error));
+
+ // skip after something
+ return AST::TupleField::create_error ();
}
- std::unique_ptr<AST::Pattern> param_pattern = parse_pattern ();
+ return AST::TupleField (std::move (field_type),
+ std::move (visibility.value ()), locus,
+ std::move (outer_attrs));
+}
- // create error function param if it doesn't exist
- if (param_pattern == nullptr)
+// Parses a Rust "enum" tagged union item definition.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Enum>
+Parser<ManagedTokenSource>::parse_enum (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
+{
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (ENUM_KW);
+
+ // parse enum name
+ const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
+ if (enum_name_tok == nullptr)
+ return nullptr;
+
+ Identifier enum_name = {enum_name_tok};
+
+ // parse generic params (of enum container, not enum variants) if they exist
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
+
+ // parse where clause if it exists
+ AST::WhereClause where_clause = parse_where_clause ();
+
+ if (!skip_token (LEFT_CURLY))
{
- // skip after something
+ skip_after_end_block ();
return nullptr;
}
- if (!skip_token (COLON))
+ // parse actual enum variant definitions
+ std::vector<std::unique_ptr<AST::EnumItem>> enum_items
+ = parse_enum_items ([] (TokenId id) { return id == RIGHT_CURLY; });
+
+ if (!skip_token (RIGHT_CURLY))
{
- // skip after something
+ skip_after_end_block ();
return nullptr;
}
- if (lexer.peek_token ()->get_id () == ELLIPSIS) // Named variadic
- {
- lexer.skip_token (); // Skip ellipsis
- return std::make_unique<AST::VariadicParam> (
- AST::VariadicParam (std::move (param_pattern), std::move (outer_attrs),
- locus));
- }
- else
- {
- std::unique_ptr<AST::Type> param_type = parse_type ();
- if (param_type == nullptr)
- {
- return nullptr;
- }
- return std::make_unique<AST::FunctionParam> (
- AST::FunctionParam (std::move (param_pattern), std::move (param_type),
- std::move (outer_attrs), locus));
- }
+ return std::unique_ptr<AST::Enum> (
+ new AST::Enum (std::move (enum_name), std::move (vis),
+ std::move (generic_params), std::move (where_clause),
+ std::move (enum_items), std::move (outer_attrs), locus));
}
-/* Parses a function or method return type syntactical construction. Also
- * handles a function return type not existing. */
+// Parses the enum variants inside an enum definiton.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Type>
-Parser<ManagedTokenSource>::parse_function_return_type ()
+std::vector<std::unique_ptr<AST::EnumItem>>
+Parser<ManagedTokenSource>::parse_enum_items ()
{
- if (lexer.peek_token ()->get_id () != RETURN_TYPE)
- return nullptr;
+ std::vector<std::unique_ptr<AST::EnumItem>> items;
- // skip return type, as it now obviously exists
- lexer.skip_token ();
+ std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
- std::unique_ptr<AST::Type> type = parse_type ();
+ // Return empty item list if no field there
+ if (initial_item == nullptr)
+ return items;
- return type;
-}
+ items.push_back (std::move (initial_item));
-/* Parses a "where clause" (in a function, struct, method, etc.). Also handles
- * a where clause not existing, in which it will return
- * WhereClause::create_empty(), which can be checked via
- * WhereClause::is_empty(). */
-template <typename ManagedTokenSource>
-AST::WhereClause
-Parser<ManagedTokenSource>::parse_where_clause ()
-{
- const_TokenPtr where_tok = lexer.peek_token ();
- if (where_tok->get_id () != WHERE)
+ while (lexer.peek_token ()->get_id () == COMMA)
{
- // where clause doesn't exist, so create empty one
- return AST::WhereClause::create_empty ();
- }
+ lexer.skip_token ();
- lexer.skip_token ();
+ std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
+ if (item == nullptr)
+ {
+ // this would occur with a trailing comma, which is allowed
+ break;
+ }
- /* parse where clause items - this is not a separate rule in the reference
- * so won't be here */
- std::vector<std::unique_ptr<AST::WhereClauseItem>> where_clause_items;
+ items.push_back (std::move (item));
+ }
- std::vector<AST::LifetimeParam> for_lifetimes;
- if (lexer.peek_token ()->get_id () == FOR)
- for_lifetimes = parse_for_lifetimes ();
+ items.shrink_to_fit ();
+ return items;
- /* HACK: where clauses end with a right curly or semicolon or equals in all
- * uses currently */
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != LEFT_CURLY && t->get_id () != SEMICOLON
- && t->get_id () != EQUAL)
+ /* TODO: use template if doable (parse_non_ptr_sequence) */
+}
+
+// Parses the enum variants inside an enum definiton.
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::EnumItem>>
+Parser<ManagedTokenSource>::parse_enum_items (EndTokenPred is_end_tok)
+{
+ std::vector<std::unique_ptr<AST::EnumItem>> items;
+
+ std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
+
+ // Return empty item list if no field there
+ if (initial_item == nullptr)
+ return items;
+
+ items.push_back (std::move (initial_item));
+
+ while (lexer.peek_token ()->get_id () == COMMA)
{
- std::unique_ptr<AST::WhereClauseItem> where_clause_item
- = parse_where_clause_item (for_lifetimes);
+ lexer.skip_token ();
- if (where_clause_item == nullptr)
+ if (is_end_tok (lexer.peek_token ()->get_id ()))
+ break;
+
+ std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
+ if (item == nullptr)
{
- Error error (t->get_locus (), "failed to parse where clause item");
+ /* TODO should this ignore all successfully parsed enum items just
+ * because one failed? */
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse enum item in enum items");
add_error (std::move (error));
- return AST::WhereClause::create_empty ();
+ return {};
}
- where_clause_items.push_back (std::move (where_clause_item));
-
- // also skip comma if it exists
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- lexer.skip_token ();
- t = lexer.peek_token ();
+ items.push_back (std::move (item));
}
- where_clause_items.shrink_to_fit ();
- return AST::WhereClause (std::move (where_clause_items));
+ items.shrink_to_fit ();
+ return items;
+
+ /* TODO: use template if doable (parse_non_ptr_sequence) */
}
-/* Parses a where clause item (lifetime or type bound). Does not parse any
+/* Parses a single enum variant item in an enum definition. Does not parse
* commas. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::WhereClauseItem>
-Parser<ManagedTokenSource>::parse_where_clause_item (
- const std::vector<AST::LifetimeParam> &outer_for_lifetimes)
+std::unique_ptr<AST::EnumItem>
+Parser<ManagedTokenSource>::parse_enum_item ()
{
- // shitty cheat way of determining lifetime or type bound - test for
- // lifetime
- const_TokenPtr t = lexer.peek_token ();
+ // parse outer attributes if they exist
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
- if (t->get_id () == LIFETIME)
- return parse_lifetime_where_clause_item ();
- else
- return parse_type_bound_where_clause_item (outer_for_lifetimes);
-}
+ // parse visibility, which may or may not exist
+ auto vis_res = parse_visibility ();
+ if (!vis_res)
+ return nullptr;
+ auto vis = vis_res.value ();
-// Parses a lifetime where clause item.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::LifetimeWhereClauseItem>
-Parser<ManagedTokenSource>::parse_lifetime_where_clause_item ()
-{
- auto parsed_lifetime = parse_lifetime (false);
- if (!parsed_lifetime)
+ // parse name for enum item, which is required
+ const_TokenPtr item_name_tok = lexer.peek_token ();
+ if (item_name_tok->get_id () != IDENTIFIER)
{
- // TODO: error here?
+ // this may not be an error but it means there is no enum item here
return nullptr;
}
- auto lifetime = parsed_lifetime.value ();
+ lexer.skip_token ();
+ Identifier item_name{item_name_tok};
- if (!skip_token (COLON))
+ // branch based on next token
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
{
- // TODO: skip after somewhere
- return nullptr;
- }
-
- std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds ();
- // TODO: have end token passed in?
-
- location_t locus = lifetime.get_locus ();
+ case LEFT_PAREN:
+ {
+ // tuple enum item
+ lexer.skip_token ();
- return std::unique_ptr<AST::LifetimeWhereClauseItem> (
- new AST::LifetimeWhereClauseItem (std::move (lifetime),
- std::move (lifetime_bounds), locus));
-}
+ std::vector<AST::TupleField> tuple_fields;
+ // Might be empty tuple for unit tuple enum variant.
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ tuple_fields = std::vector<AST::TupleField> ();
+ else
+ tuple_fields = parse_tuple_fields ();
-// Parses a type bound where clause item.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeBoundWhereClauseItem>
-Parser<ManagedTokenSource>::parse_type_bound_where_clause_item (
- const std::vector<AST::LifetimeParam> &outer_for_lifetimes)
-{
- std::vector<AST::LifetimeParam> for_lifetimes = outer_for_lifetimes;
+ if (!skip_token (RIGHT_PAREN))
+ {
+ // skip after somewhere
+ return nullptr;
+ }
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- return nullptr;
- }
+ return std::unique_ptr<AST::EnumItemTuple> (new AST::EnumItemTuple (
+ std::move (item_name), std::move (vis), std::move (tuple_fields),
+ std::move (outer_attrs), item_name_tok->get_locus ()));
+ }
+ case LEFT_CURLY:
+ {
+ // struct enum item
+ lexer.skip_token ();
- if (!skip_token (COLON))
- {
- // TODO: skip after somewhere
- return nullptr;
- }
+ std::vector<AST::StructField> struct_fields
+ = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
- if (lexer.peek_token ()->get_id () == FOR)
- {
- auto for_lifetimes_inner = parse_for_lifetimes ();
- for_lifetimes.insert (for_lifetimes.end (), for_lifetimes_inner.begin (),
- for_lifetimes_inner.end ());
- }
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip after somewhere
+ return nullptr;
+ }
- // parse type param bounds if they exist
- std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds
- = parse_type_param_bounds ();
+ return std::unique_ptr<AST::EnumItemStruct> (new AST::EnumItemStruct (
+ std::move (item_name), std::move (vis), std::move (struct_fields),
+ std::move (outer_attrs), item_name_tok->get_locus ()));
+ }
+ case EQUAL:
+ {
+ // discriminant enum item
+ lexer.skip_token ();
- location_t locus = lexer.peek_token ()->get_locus ();
+ std::unique_ptr<AST::Expr> discriminant_expr = parse_expr ();
- return std::unique_ptr<AST::TypeBoundWhereClauseItem> (
- new AST::TypeBoundWhereClauseItem (std::move (for_lifetimes),
- std::move (type),
- std::move (type_param_bounds), locus));
+ return std::unique_ptr<AST::EnumItemDiscriminant> (
+ new AST::EnumItemDiscriminant (std::move (item_name), std::move (vis),
+ std::move (discriminant_expr),
+ std::move (outer_attrs),
+ item_name_tok->get_locus ()));
+ }
+ default:
+ // regular enum with just an identifier
+ return std::unique_ptr<AST::EnumItem> (
+ new AST::EnumItem (std::move (item_name), std::move (vis),
+ std::move (outer_attrs),
+ item_name_tok->get_locus ()));
+ }
}
-// Parses a for lifetimes clause, including the for keyword and angle
-// brackets.
+// Parses a C-style (and C-compat) untagged union declaration.
template <typename ManagedTokenSource>
-std::vector<AST::LifetimeParam>
-Parser<ManagedTokenSource>::parse_for_lifetimes ()
+std::unique_ptr<AST::Union>
+Parser<ManagedTokenSource>::parse_union (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- std::vector<AST::LifetimeParam> params;
+ /* hack - "weak keyword" by finding identifier called "union" (lookahead in
+ * item switch) */
+ const_TokenPtr union_keyword = expect_token (IDENTIFIER);
+ rust_assert (union_keyword->get_str () == Values::WeakKeywords::UNION);
+ location_t locus = union_keyword->get_locus ();
- if (!skip_token (FOR))
+ // parse actual union name
+ const_TokenPtr union_name_tok = expect_token (IDENTIFIER);
+ if (union_name_tok == nullptr)
{
- // skip after somewhere?
- return params;
+ skip_after_next_block ();
+ return nullptr;
}
+ Identifier union_name{union_name_tok};
- if (!skip_token (LEFT_ANGLE))
+ // parse optional generic parameters
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
+
+ // parse optional where clause
+ AST::WhereClause where_clause = parse_where_clause ();
+
+ if (!skip_token (LEFT_CURLY))
{
- // skip after somewhere?
- return params;
+ skip_after_end_block ();
+ return nullptr;
}
- /* cannot specify end token due to parsing problems with '>' tokens being
- * nested */
- params = parse_lifetime_params_objs (Parse::Utils::is_right_angle_tok);
+ /* parse union inner items as "struct fields" because hey, syntax reuse.
+ * Spec said so. */
+ std::vector<AST::StructField> union_fields
+ = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
- if (!skip_generics_right_angle ())
+ if (!skip_token (RIGHT_CURLY))
{
- // DEBUG
- rust_debug ("failed to skip generics right angle after (supposedly) "
- "finished parsing where clause items");
- // ok, well this gets called.
-
- // skip after somewhere?
- return params;
+ // skip after somewhere
+ return nullptr;
}
- return params;
+ return std::unique_ptr<AST::Union> (
+ new AST::Union (std::move (union_name), std::move (vis),
+ std::move (generic_params), std::move (where_clause),
+ std::move (union_fields), std::move (outer_attrs), locus));
}
-// Parses type parameter bounds in where clause or generic arguments.
+/* Parses a "constant item" (compile-time constant to maybe "inline"
+ * throughout the program - like constexpr). */
template <typename ManagedTokenSource>
-std::vector<std::unique_ptr<AST::TypeParamBound>>
-Parser<ManagedTokenSource>::parse_type_param_bounds ()
+std::unique_ptr<AST::ConstantItem>
+Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (CONST);
- std::unique_ptr<AST::TypeParamBound> initial_bound
- = parse_type_param_bound ();
+ /* get constant identifier - this is either a proper identifier or the _
+ * wildcard */
+ const_TokenPtr ident_tok = lexer.peek_token ();
+ // make default identifier the underscore wildcard one
+ std::string ident (Values::Keywords::UNDERSCORE);
+ switch (ident_tok->get_id ())
+ {
+ case IDENTIFIER:
+ ident = ident_tok->get_str ();
+ lexer.skip_token ();
+ break;
+ case UNDERSCORE:
+ // do nothing - identifier is already "_"
+ lexer.skip_token ();
+ break;
+ default:
+ add_error (
+ Error (ident_tok->get_locus (),
+ "expected item name (identifier or %<_%>) in constant item "
+ "declaration - found %qs",
+ ident_tok->get_token_description ()));
- // quick exit if null
- if (initial_bound == nullptr)
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ if (!skip_token (COLON))
{
- /* error? type param bounds must have at least one term, but are bounds
- * optional? */
- return type_param_bounds;
+ skip_after_semicolon ();
+ return nullptr;
}
- type_param_bounds.push_back (std::move (initial_bound));
- while (lexer.peek_token ()->get_id () == PLUS)
+ // parse constant type (required)
+ std::unique_ptr<AST::Type> type = parse_type ();
+
+ // A const with no given expression value
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
{
lexer.skip_token ();
+ return std::unique_ptr<AST::ConstantItem> (
+ new AST::ConstantItem (std::move (ident), std::move (vis),
+ std::move (type), std::move (outer_attrs),
+ locus));
+ }
- std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
- if (bound == nullptr)
- {
- /* not an error: bound is allowed to be null as trailing plus is
- * allowed */
- return type_param_bounds;
- }
+ if (!skip_token (EQUAL))
+ {
+ skip_after_semicolon ();
+ return nullptr;
+ }
- type_param_bounds.push_back (std::move (bound));
+ // parse constant expression (required)
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+
+ if (!skip_token (SEMICOLON))
+ {
+ // skip somewhere?
+ return nullptr;
}
- type_param_bounds.shrink_to_fit ();
- return type_param_bounds;
+ return std::unique_ptr<AST::ConstantItem> (
+ new AST::ConstantItem (std::move (ident), std::move (vis), std::move (type),
+ std::move (expr), std::move (outer_attrs), locus));
}
-/* Parses type parameter bounds in where clause or generic arguments, with end
- * token handling. */
+// Parses a "static item" (static storage item, with 'static lifetime).
template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<std::unique_ptr<AST::TypeParamBound>>
-Parser<ManagedTokenSource>::parse_type_param_bounds (EndTokenPred is_end_token)
+std::unique_ptr<AST::StaticItem>
+Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
-
- std::unique_ptr<AST::TypeParamBound> initial_bound
- = parse_type_param_bound ();
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (STATIC_KW);
- // quick exit if null
- if (initial_bound == nullptr)
+ // determine whether static item is mutable
+ bool is_mut = false;
+ if (lexer.peek_token ()->get_id () == MUT)
{
- /* error? type param bounds must have at least one term, but are bounds
- * optional? */
- return type_param_bounds;
+ is_mut = true;
+ lexer.skip_token ();
}
- type_param_bounds.push_back (std::move (initial_bound));
- while (lexer.peek_token ()->get_id () == PLUS)
- {
- lexer.skip_token ();
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ return nullptr;
- // break if end token character
- if (is_end_token (lexer.peek_token ()->get_id ()))
- break;
+ Identifier ident{ident_tok};
- std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
- if (bound == nullptr)
- {
- // TODO how wise is it to ditch all bounds if only one failed?
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type param bound in type param bounds");
- add_error (std::move (error));
+ if (!skip_token (COLON))
+ {
+ skip_after_semicolon ();
+ return nullptr;
+ }
- return {};
- }
+ // parse static item type (required)
+ std::unique_ptr<AST::Type> type = parse_type ();
- type_param_bounds.push_back (std::move (bound));
+ if (!skip_token (EQUAL))
+ {
+ skip_after_semicolon ();
+ return nullptr;
}
- type_param_bounds.shrink_to_fit ();
- return type_param_bounds;
-}
+ // parse static item expression (required)
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
-/* Parses a single type parameter bound in a where clause or generic argument.
- * Does not parse the '+' between arguments. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeParamBound>
-Parser<ManagedTokenSource>::parse_type_param_bound ()
-{
- // shitty cheat way of determining lifetime or trait bound - test for
- // lifetime
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
+ if (!skip_token (SEMICOLON))
{
- case LIFETIME:
- return std::unique_ptr<AST::Lifetime> (
- new AST::Lifetime (parse_lifetime (false).value ()));
- case LEFT_PAREN:
- case QUESTION_MARK:
- case FOR:
- case IDENTIFIER:
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case DOLLAR_SIGN:
- case SCOPE_RESOLUTION:
- return parse_trait_bound ();
- default:
- // don't error - assume this is fine TODO
+ // skip after somewhere
return nullptr;
}
+
+ return std::unique_ptr<AST::StaticItem> (
+ new AST::StaticItem (std::move (ident), is_mut, std::move (type),
+ std::move (expr), std::move (vis),
+ std::move (outer_attrs), locus));
}
-// Parses a trait bound type param bound.
+// Parses a trait definition item, including unsafe ones.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::TraitBound>
-Parser<ManagedTokenSource>::parse_trait_bound ()
+std::unique_ptr<AST::Trait>
+Parser<ManagedTokenSource>::parse_trait (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- bool has_parens = false;
- bool has_question_mark = false;
-
location_t locus = lexer.peek_token ()->get_locus ();
+ bool is_unsafe = false;
+ bool is_auto_trait = false;
- /* parse optional `for lifetimes`. */
- std::vector<AST::LifetimeParam> for_lifetimes;
- if (lexer.peek_token ()->get_id () == FOR)
- for_lifetimes = parse_for_lifetimes ();
-
- // handle trait bound being in parentheses
- if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+ if (lexer.peek_token ()->get_id () == UNSAFE)
{
- has_parens = true;
+ is_unsafe = true;
lexer.skip_token ();
}
- // handle having question mark (optional)
- if (lexer.peek_token ()->get_id () == QUESTION_MARK)
+ if (lexer.peek_token ()->get_id () == AUTO)
{
- has_question_mark = true;
+ is_auto_trait = true;
lexer.skip_token ();
}
- // handle TypePath
- AST::TypePath type_path = parse_type_path ();
-
- // handle closing parentheses
- if (has_parens)
- {
- if (!skip_token (RIGHT_PAREN))
- {
- return nullptr;
- }
- }
-
- return std::unique_ptr<AST::TraitBound> (
- new AST::TraitBound (std::move (type_path), locus, has_parens,
- has_question_mark, std::move (for_lifetimes)));
-}
-
-// Parses lifetime bounds.
-template <typename ManagedTokenSource>
-std::vector<AST::Lifetime>
-Parser<ManagedTokenSource>::parse_lifetime_bounds ()
-{
- std::vector<AST::Lifetime> lifetime_bounds;
+ skip_token (TRAIT);
- while (true)
- {
- auto lifetime = parse_lifetime (false);
+ // parse trait name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ return nullptr;
- // quick exit for parsing failure
- if (!lifetime)
- break;
+ Identifier ident{ident_tok};
- lifetime_bounds.push_back (std::move (lifetime.value ()));
+ // parse generic parameters (if they exist)
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
- /* plus is maybe not allowed at end - spec defines it weirdly, so
- * assuming allowed at end */
- if (lexer.peek_token ()->get_id () != PLUS)
- break;
+ // create placeholder type param bounds in case they don't exist
+ std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+ // parse type param bounds (if they exist)
+ if (lexer.peek_token ()->get_id () == COLON)
+ {
lexer.skip_token ();
+
+ type_param_bounds = parse_type_param_bounds (
+ [] (TokenId id) { return id == WHERE || id == LEFT_CURLY; });
+ // type_param_bounds = parse_type_param_bounds ();
}
- lifetime_bounds.shrink_to_fit ();
- return lifetime_bounds;
-}
+ // parse where clause (if it exists)
+ AST::WhereClause where_clause = parse_where_clause ();
-// Parses lifetime bounds, with added check for ending token.
-template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<AST::Lifetime>
-Parser<ManagedTokenSource>::parse_lifetime_bounds (EndTokenPred is_end_token)
-{
- std::vector<AST::Lifetime> lifetime_bounds;
+ if (!skip_token (LEFT_CURLY))
+ {
+ skip_after_end_block ();
+ return nullptr;
+ }
- while (!is_end_token (lexer.peek_token ()->get_id ()))
+ // parse inner attrs (if they exist)
+ AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+ // parse trait items
+ std::vector<std::unique_ptr<AST::AssociatedItem>> trait_items;
+
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY)
{
- auto lifetime = parse_lifetime (false);
+ std::unique_ptr<AST::AssociatedItem> trait_item = parse_trait_item ();
- if (!lifetime)
+ if (trait_item == nullptr)
{
- /* TODO: is it worth throwing away all lifetime bound info just
- * because one failed? */
Error error (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime in lifetime bounds");
+ "failed to parse trait item in trait");
add_error (std::move (error));
- return {};
+ return nullptr;
}
+ trait_items.push_back (std::move (trait_item));
- lifetime_bounds.push_back (std::move (lifetime.value ()));
-
- /* plus is maybe not allowed at end - spec defines it weirdly, so
- * assuming allowed at end */
- if (lexer.peek_token ()->get_id () != PLUS)
- break;
+ t = lexer.peek_token ();
+ }
- lexer.skip_token ();
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip after something
+ return nullptr;
}
- lifetime_bounds.shrink_to_fit ();
- return lifetime_bounds;
+ trait_items.shrink_to_fit ();
+ return std::unique_ptr<AST::Trait> (
+ new AST::Trait (std::move (ident), is_unsafe, is_auto_trait,
+ std::move (generic_params), std::move (type_param_bounds),
+ std::move (where_clause), std::move (trait_items),
+ std::move (vis), std::move (outer_attrs),
+ std::move (inner_attrs), locus));
}
-/* Parses a lifetime token (named, 'static, or '_). Also handles lifetime not
- * existing. */
+// Parses a trait item used inside traits (not trait, the Item).
template <typename ManagedTokenSource>
-tl::expected<AST::Lifetime, ParseLifetimeError>
-Parser<ManagedTokenSource>::parse_lifetime (bool allow_elided)
+std::unique_ptr<AST::AssociatedItem>
+Parser<ManagedTokenSource>::parse_trait_item ()
{
- const_TokenPtr lifetime_tok = lexer.peek_token ();
- if (lifetime_tok->get_id () != LIFETIME)
+ // parse outer attributes (if they exist)
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+ auto vis_res = parse_visibility ();
+ if (!vis_res)
+ return nullptr;
+
+ auto vis = vis_res.value ();
+
+ // lookahead to determine what type of trait item to parse
+ const_TokenPtr tok = lexer.peek_token ();
+ switch (tok->get_id ())
{
- if (allow_elided)
- {
- return AST::Lifetime::elided ();
- }
+ case SUPER:
+ case SELF:
+ case CRATE:
+ case DOLLAR_SIGN:
+ // these seem to be SimplePath tokens, so this is a macro invocation
+ // semi
+ return parse_macro_invocation_semi (std::move (outer_attrs));
+ case IDENTIFIER:
+ if (lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
+ return parse_function (std::move (vis), std::move (outer_attrs));
else
+ return parse_macro_invocation_semi (std::move (outer_attrs));
+ case TYPE:
+ return parse_trait_type (std::move (outer_attrs), vis);
+ case CONST:
+ // disambiguate with function qualifier
+ if (lexer.peek_token (1)->get_id () == IDENTIFIER)
{
- return tl::make_unexpected<ParseLifetimeError> ({});
+ return parse_trait_const (std::move (outer_attrs));
}
+ // else, fallthrough to function
+ // TODO: find out how to disable gcc "implicit fallthrough" error
+ gcc_fallthrough ();
+ case ASYNC:
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs));
+ default:
+ break;
}
- lexer.skip_token ();
-
- return lifetime_from_token (lifetime_tok);
+ add_error (Error (tok->get_locus (),
+ "unrecognised token %qs for item in trait",
+ tok->get_token_description ()));
+ // skip?
+ return nullptr;
}
+// Parse a typedef trait item.
template <typename ManagedTokenSource>
-AST::Lifetime
-Parser<ManagedTokenSource>::lifetime_from_token (const_TokenPtr tok)
+std::unique_ptr<AST::TraitItemType>
+Parser<ManagedTokenSource>::parse_trait_type (AST::AttrVec outer_attrs,
+ AST::Visibility vis)
{
- location_t locus = tok->get_locus ();
- std::string lifetime_ident = tok->get_str ();
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (TYPE);
- if (lifetime_ident == "static")
- {
- return AST::Lifetime (AST::Lifetime::STATIC, "", locus);
- }
- else if (lifetime_ident == "_")
- {
- // Explicitly and implicitly elided lifetimes follow the same rules.
- return AST::Lifetime (AST::Lifetime::WILDCARD, "", locus);
- }
- else
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ return nullptr;
+
+ Identifier ident{ident_tok};
+
+ // Parse optional generic parameters for GATs (Generic Associated Types)
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
+ if (lexer.peek_token ()->get_id () == LEFT_ANGLE)
{
- return AST::Lifetime (AST::Lifetime::NAMED, std::move (lifetime_ident),
- locus);
+ generic_params = parse_generic_params_in_angles ();
}
-}
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExternalTypeItem>
-Parser<ManagedTokenSource>::parse_external_type_item (AST::Visibility vis,
- AST::AttrVec outer_attrs)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (TYPE);
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
- const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
- if (alias_name_tok == nullptr)
+ // parse optional colon
+ if (lexer.peek_token ()->get_id () == COLON)
{
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse identifier in external opaque type");
- add_error (std::move (error));
+ lexer.skip_token ();
- skip_after_semicolon ();
- return nullptr;
+ // parse optional type param bounds
+ bounds
+ = parse_type_param_bounds ([] (TokenId id) { return id == SEMICOLON; });
+ // bounds = parse_type_param_bounds ();
}
if (!skip_token (SEMICOLON))
- return nullptr;
+ {
+ // skip?
+ return nullptr;
+ }
- return std::unique_ptr<AST::ExternalTypeItem> (
- new AST::ExternalTypeItem (alias_name_tok->get_str (), std::move (vis),
- std::move (outer_attrs), std::move (locus)));
+ return std::unique_ptr<AST::TraitItemType> (
+ new AST::TraitItemType (std::move (ident), std::move (generic_params),
+ std::move (bounds), std::move (outer_attrs), vis,
+ locus));
}
-// Parses a "type alias" (typedef) item.
+// Parses a constant trait item.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeAlias>
-Parser<ManagedTokenSource>::parse_type_alias (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::ConstantItem>
+Parser<ManagedTokenSource>::parse_trait_const (AST::AttrVec outer_attrs)
{
location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (TYPE);
+ skip_token (CONST);
- // TODO: use this token for identifier when finished that
- const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
- if (alias_name_tok == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse identifier in type alias");
- add_error (std::move (error));
+ // parse constant item name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ return nullptr;
+
+ Identifier ident{ident_tok};
+ if (!skip_token (COLON))
+ {
skip_after_semicolon ();
return nullptr;
}
- Identifier alias_name{alias_name_tok};
-
- // parse generic params, which may not exist
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
- // parse where clause, which may not exist
- AST::WhereClause where_clause = parse_where_clause ();
+ // parse constant trait item type
+ std::unique_ptr<AST::Type> type = parse_type ();
- if (!skip_token (EQUAL))
+ // parse constant trait body expression, if it exists
+ std::unique_ptr<AST::Expr> const_body = nullptr;
+ if (lexer.peek_token ()->get_id () == EQUAL)
{
- skip_after_semicolon ();
- return nullptr;
- }
+ lexer.skip_token ();
- std::unique_ptr<AST::Type> type_to_alias = parse_type ();
+ // expression must exist, so parse it
+ const_body = parse_expr ();
+ }
if (!skip_token (SEMICOLON))
{
- // should be skipping past this, not the next line
+ // skip after something?
return nullptr;
}
- return std::unique_ptr<AST::TypeAlias> (
- new AST::TypeAlias (std::move (alias_name), std::move (generic_params),
- std::move (where_clause), std::move (type_to_alias),
- std::move (vis), std::move (outer_attrs), locus));
+ return std::unique_ptr<AST::ConstantItem> (new AST::ConstantItem (
+ std::move (ident), AST::Visibility::create_private (), std::move (type),
+ std::move (const_body), std::move (outer_attrs), locus));
}
-// Parse a struct item AST node.
+/* Parses a struct "impl" item (both inherent impl and trait impl can be
+ * parsed here), */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Struct>
-Parser<ManagedTokenSource>::parse_struct (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::Impl>
+Parser<ManagedTokenSource>::parse_impl (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
- /* TODO: determine best way to parse the proper struct vs tuple struct -
- * share most of initial constructs so lookahead might be impossible, and if
- * not probably too expensive. Best way is probably unified parsing for the
- * initial parts and then pass them in as params to more derived functions.
- * Alternatively, just parse everything in this one function - do this if
- * function not too long. */
-
- /* Proper struct <- 'struct' IDENTIFIER generic_params? where_clause? ( '{'
- * struct_fields? '}' | ';' ) */
- /* Tuple struct <- 'struct' IDENTIFIER generic_params? '(' tuple_fields? ')'
- * where_clause? ';' */
+ /* Note that only trait impls are allowed to be unsafe. So if unsafe, it
+ * must be a trait impl. However, this isn't enough for full disambiguation,
+ * so don't branch here. */
location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (STRUCT_KW);
-
- // parse struct name
- const_TokenPtr name_tok = expect_token (IDENTIFIER);
- if (name_tok == nullptr)
+ bool is_unsafe = false;
+ if (lexer.peek_token ()->get_id () == UNSAFE)
{
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse struct or tuple struct identifier");
- add_error (std::move (error));
+ lexer.skip_token ();
+ is_unsafe = true;
+ }
- // skip after somewhere?
+ if (!skip_token (IMPL))
+ {
+ skip_after_next_block ();
return nullptr;
}
- Identifier struct_name{name_tok};
- // parse generic params, which may or may not exist
+ // parse generic params (shared by trait and inherent impls)
std::vector<std::unique_ptr<AST::GenericParam>> generic_params
= parse_generic_params_in_angles ();
- // branch on next token - determines whether proper struct or tuple struct
- if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+ // Again, trait impl-only feature, but optional one, so can be used for
+ // branching yet.
+ bool has_exclam = false;
+ if (lexer.peek_token ()->get_id () == EXCLAM)
{
- // tuple struct
-
- // skip left parenthesis
lexer.skip_token ();
+ has_exclam = true;
+ }
- // parse tuple fields
- std::vector<AST::TupleField> tuple_fields;
- // Might be empty tuple for unit tuple struct.
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- tuple_fields = std::vector<AST::TupleField> ();
+ /* FIXME: code that doesn't look shit for TypePath. Also, make sure this
+ * doesn't parse too much and not work. */
+ AST::TypePath type_path = parse_type_path ();
+ if (type_path.is_error () || lexer.peek_token ()->get_id () != FOR)
+ {
+ /* cannot parse type path (or not for token next, at least), so must be
+ * inherent impl */
+
+ // hacky conversion of TypePath stack object to Type pointer
+ std::unique_ptr<AST::Type> type = nullptr;
+ if (!type_path.is_error ())
+ type = std::unique_ptr<AST::TypePath> (
+ new AST::TypePath (std::move (type_path)));
else
- tuple_fields = parse_tuple_fields ();
+ type = parse_type ();
- // tuple parameters must have closing parenthesis
- if (!skip_token (RIGHT_PAREN))
+ // Type is required, so error if null
+ if (type == nullptr)
{
- skip_after_semicolon ();
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse type in inherent impl");
+ add_error (std::move (error));
+
+ skip_after_next_block ();
return nullptr;
}
- // parse where clause, which is optional
+ // parse optional where clause
AST::WhereClause where_clause = parse_where_clause ();
- if (!skip_token (SEMICOLON))
+ if (!skip_token (LEFT_CURLY))
{
- // can't skip after semicolon because it's meant to be here
+ // TODO: does this still skip properly?
+ skip_after_end_block ();
return nullptr;
}
- return std::unique_ptr<AST::TupleStruct> (
- new AST::TupleStruct (std::move (tuple_fields), std::move (struct_name),
- std::move (generic_params),
- std::move (where_clause), std::move (vis),
- std::move (outer_attrs), locus));
- }
+ // parse inner attributes (optional)
+ AST::AttrVec inner_attrs = parse_inner_attributes ();
- // assume it is a proper struct being parsed and continue outside of switch
- // - label only here to suppress warning
+ // parse inherent impl items
+ std::vector<std::unique_ptr<AST::AssociatedItem>> impl_items;
- // parse where clause, which is optional
- AST::WhereClause where_clause = parse_where_clause ();
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY)
+ {
+ std::unique_ptr<AST::AssociatedItem> impl_item
+ = parse_inherent_impl_item ();
- // branch on next token - determines whether struct is a unit struct
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case LEFT_CURLY:
- {
- // struct with body
+ if (impl_item == nullptr)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse inherent impl item in inherent impl");
+ add_error (std::move (error));
- // skip curly bracket
- lexer.skip_token ();
+ return nullptr;
+ }
- // parse struct fields, if any
- std::vector<AST::StructField> struct_fields
- = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
+ impl_items.push_back (std::move (impl_item));
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere?
- return nullptr;
- }
+ t = lexer.peek_token ();
+ }
- return std::unique_ptr<AST::StructStruct> (new AST::StructStruct (
- std::move (struct_fields), std::move (struct_name),
- std::move (generic_params), std::move (where_clause), false,
- std::move (vis), std::move (outer_attrs), locus));
- }
- case SEMICOLON:
- // unit struct declaration
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere
+ return nullptr;
+ }
- lexer.skip_token ();
+ // DEBUG
+ rust_debug ("successfully parsed inherent impl");
- return std::unique_ptr<AST::StructStruct> (
- new AST::StructStruct (std::move (struct_name),
- std::move (generic_params),
- std::move (where_clause), std::move (vis),
- std::move (outer_attrs), locus));
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in struct declaration",
- t->get_token_description ()));
+ impl_items.shrink_to_fit ();
- // skip somewhere?
- return nullptr;
+ return std::unique_ptr<AST::InherentImpl> (new AST::InherentImpl (
+ std::move (impl_items), std::move (generic_params), std::move (type),
+ std::move (where_clause), std::move (vis), std::move (inner_attrs),
+ std::move (outer_attrs), locus));
}
-}
+ else
+ {
+ // type path must both be valid and next token is for, so trait impl
+ if (!skip_token (FOR))
+ {
+ skip_after_next_block ();
+ return nullptr;
+ }
-// Parses struct fields in struct declarations.
-template <typename ManagedTokenSource>
-std::vector<AST::StructField>
-Parser<ManagedTokenSource>::parse_struct_fields ()
-{
- std::vector<AST::StructField> fields;
+ // parse type
+ std::unique_ptr<AST::Type> type = parse_type ();
+ // ensure type is included as it is required
+ if (type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse type in trait impl");
+ add_error (std::move (error));
- AST::StructField initial_field = parse_struct_field ();
+ skip_after_next_block ();
+ return nullptr;
+ }
- // Return empty field list if no field there
- if (initial_field.is_error ())
- return fields;
+ // parse optional where clause
+ AST::WhereClause where_clause = parse_where_clause ();
- fields.push_back (std::move (initial_field));
+ if (!skip_token (LEFT_CURLY))
+ {
+ // TODO: does this still skip properly?
+ skip_after_end_block ();
+ return nullptr;
+ }
- while (lexer.peek_token ()->get_id () == COMMA)
- {
- lexer.skip_token ();
+ // parse inner attributes (optional)
+ AST::AttrVec inner_attrs = parse_inner_attributes ();
- AST::StructField field = parse_struct_field ();
+ // parse trait impl items
+ std::vector<std::unique_ptr<AST::AssociatedItem>> impl_items;
- if (field.is_error ())
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY)
{
- // would occur with trailing comma, so allowed
- break;
- }
+ std::unique_ptr<AST::AssociatedItem> impl_item
+ = parse_trait_impl_item ();
- fields.push_back (std::move (field));
- }
+ if (impl_item == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse trait impl item in trait impl");
+ add_error (std::move (error));
- fields.shrink_to_fit ();
- return fields;
- // TODO: template if possible (parse_non_ptr_seq)
-}
+ return nullptr;
+ }
-// Parses struct fields in struct declarations.
-template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<AST::StructField>
-Parser<ManagedTokenSource>::parse_struct_fields (EndTokenPred is_end_tok)
-{
- std::vector<AST::StructField> fields;
+ impl_items.push_back (std::move (impl_item));
- AST::StructField initial_field = parse_struct_field ();
+ t = lexer.peek_token ();
- // Return empty field list if no field there
- if (initial_field.is_error ())
- return fields;
+ // DEBUG
+ rust_debug ("successfully parsed a trait impl item");
+ }
+ // DEBUG
+ rust_debug ("successfully finished trait impl items");
- fields.push_back (std::move (initial_field));
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere
+ return nullptr;
+ }
- while (lexer.peek_token ()->get_id () == COMMA)
- {
- lexer.skip_token ();
+ // DEBUG
+ rust_debug ("successfully parsed trait impl");
- if (is_end_tok (lexer.peek_token ()->get_id ()))
- break;
+ impl_items.shrink_to_fit ();
- AST::StructField field = parse_struct_field ();
- if (field.is_error ())
- {
- /* TODO: should every field be ditched just because one couldn't be
- * parsed? */
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse struct field in struct fields");
- add_error (std::move (error));
-
- return {};
- }
-
- fields.push_back (std::move (field));
+ return std::unique_ptr<AST::TraitImpl> (
+ new AST::TraitImpl (std::move (type_path), is_unsafe, has_exclam,
+ std::move (impl_items), std::move (generic_params),
+ std::move (type), std::move (where_clause),
+ std::move (vis), std::move (inner_attrs),
+ std::move (outer_attrs), locus));
}
-
- fields.shrink_to_fit ();
- return fields;
- // TODO: template if possible (parse_non_ptr_seq)
}
-// Parses a single struct field (in a struct definition). Does not parse
-// commas.
+// Parses a single inherent impl item (item inside an inherent impl block).
template <typename ManagedTokenSource>
-AST::StructField
-Parser<ManagedTokenSource>::parse_struct_field ()
+std::unique_ptr<AST::AssociatedItem>
+Parser<ManagedTokenSource>::parse_inherent_impl_item ()
{
- // parse outer attributes, if they exist
+ // parse outer attributes (if they exist)
AST::AttrVec outer_attrs = parse_outer_attributes ();
- // parse visibility, if it exists
- auto vis = parse_visibility ();
- if (!vis)
- return AST::StructField::create_error ();
-
- location_t locus = lexer.peek_token ()->get_locus ();
+ // TODO: cleanup - currently an unreadable mess
- // parse field name
- const_TokenPtr field_name_tok = lexer.peek_token ();
- if (field_name_tok->get_id () != IDENTIFIER)
+ // branch on next token:
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
{
- // if not identifier, assumes there is no struct field and exits - not
- // necessarily error
- return AST::StructField::create_error ();
- }
- Identifier field_name{field_name_tok};
- lexer.skip_token ();
+ case IDENTIFIER:
+ // FIXME: Arthur: Do we need to some lookahead here?
+ return parse_macro_invocation_semi (outer_attrs);
+ case SUPER:
+ case SELF:
+ case CRATE:
+ case PUB:
+ {
+ // visibility, so not a macro invocation semi - must be constant,
+ // function, or method
+ auto vis_res = parse_visibility ();
+ if (!vis_res)
+ return nullptr;
+ auto vis = vis_res.value ();
- if (!skip_token (COLON))
- {
- // skip after somewhere?
- return AST::StructField::create_error ();
- }
+ // TODO: is a recursive call to parse_inherent_impl_item better?
+ switch (lexer.peek_token ()->get_id ())
+ {
+ case EXTERN_KW:
+ case UNSAFE:
+ case FN_KW:
+ // function or method
+ return parse_inherent_impl_function_or_method (std::move (vis),
+ std::move (
+ outer_attrs));
+ case CONST:
+ // lookahead to resolve production - could be function/method or
+ // const item
+ t = lexer.peek_token (1);
- // parse field type - this is required
- std::unique_ptr<AST::Type> field_type = parse_type ();
- if (field_type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse type in struct field definition");
- add_error (std::move (error));
+ switch (t->get_id ())
+ {
+ case IDENTIFIER:
+ case UNDERSCORE:
+ return parse_const_item (std::move (vis),
+ std::move (outer_attrs));
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_inherent_impl_function_or_method (std::move (vis),
+ std::move (
+ outer_attrs));
+ default:
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in some sort of const "
+ "item in inherent impl",
+ t->get_token_description ()));
- // skip after somewhere
- return AST::StructField::create_error ();
- }
+ lexer.skip_token (1); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ default:
+ add_error (
+ Error (t->get_locus (),
+ "unrecognised token %qs for item in inherent impl",
+ t->get_token_description ()));
+ // skip?
+ return nullptr;
+ }
+ }
+ case ASYNC:
+ case EXTERN_KW:
+ case UNSAFE:
+ case FN_KW:
+ // function or method
+ return parse_inherent_impl_function_or_method (
+ AST::Visibility::create_private (), std::move (outer_attrs));
+ case CONST:
+ /* lookahead to resolve production - could be function/method or const
+ * item */
+ t = lexer.peek_token (1);
- return AST::StructField (std::move (field_name), std::move (field_type),
- std::move (vis.value ()), locus,
- std::move (outer_attrs));
+ switch (t->get_id ())
+ {
+ case IDENTIFIER:
+ case UNDERSCORE:
+ return parse_const_item (AST::Visibility::create_private (),
+ std::move (outer_attrs));
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_inherent_impl_function_or_method (
+ AST::Visibility::create_private (), std::move (outer_attrs));
+ default:
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in some sort of const item "
+ "in inherent impl",
+ t->get_token_description ()));
+
+ lexer.skip_token (1); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ rust_unreachable ();
+ default:
+ add_error (Error (t->get_locus (),
+ "unrecognised token %qs for item in inherent impl",
+ t->get_token_description ()));
+
+ // skip?
+ return nullptr;
+ }
}
-// Parses tuple fields in tuple/tuple struct declarations.
+/* For internal use only by parse_inherent_impl_item() - splits giant method
+ * into smaller ones and prevents duplication of logic. Strictly, this parses
+ * a function or method item inside an inherent impl item block. */
+// TODO: make this a templated function with "return type" as type param -
+// InherentImplItem is this specialisation of the template while TraitImplItem
+// will be the other.
template <typename ManagedTokenSource>
-std::vector<AST::TupleField>
-Parser<ManagedTokenSource>::parse_tuple_fields ()
+std::unique_ptr<AST::AssociatedItem>
+Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (
+ AST::Visibility vis, AST::AttrVec outer_attrs)
{
- std::vector<AST::TupleField> fields;
+ location_t locus = lexer.peek_token ()->get_locus ();
+ // parse function or method qualifiers
+ AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
- AST::TupleField initial_field = parse_tuple_field ();
+ skip_token (FN_KW);
- // Return empty field list if no field there
- if (initial_field.is_error ())
- {
- return fields;
- }
+ // parse function or method name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ return nullptr;
- fields.push_back (std::move (initial_field));
+ Identifier ident{ident_tok};
- // maybe think of a better control structure here - do-while with an initial
- // error state? basically, loop through field list until can't find any more
- // params HACK: all current syntax uses of tuple fields have them ending
- // with a right paren token
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == COMMA)
- {
- // skip comma if applies - e.g. trailing comma
- lexer.skip_token ();
+ // parse generic params
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
- // break out due to right paren if it exists
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- {
- break;
- }
+ if (!skip_token (LEFT_PAREN))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
- AST::TupleField field = parse_tuple_field ();
- if (field.is_error ())
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse tuple field in tuple fields");
- add_error (std::move (error));
+ // now for function vs method disambiguation - method has opening "self"
+ // param
+ auto initial_param = parse_self_param ();
- return std::vector<AST::TupleField> ();
- }
+ if (!initial_param.has_value ()
+ && initial_param.error () != ParseSelfError::NOT_SELF)
+ return nullptr;
- fields.push_back (std::move (field));
+ /* FIXME: ensure that self param doesn't accidently consume tokens for a
+ * function one idea is to lookahead up to 4 tokens to see whether self is
+ * one of them */
+ bool is_method = false;
+ if (initial_param.has_value ())
+ {
+ if ((*initial_param)->is_self ())
+ is_method = true;
- t = lexer.peek_token ();
+ /* skip comma so function and method regular params can be parsed in
+ * same way */
+ if (lexer.peek_token ()->get_id () == COMMA)
+ lexer.skip_token ();
}
- fields.shrink_to_fit ();
- return fields;
+ // parse trait function params
+ std::vector<std::unique_ptr<AST::Param>> function_params
+ = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
- // TODO: this shares basically all code with function params and struct
- // fields
- // - templates?
-}
+ if (initial_param.has_value ())
+ function_params.insert (function_params.begin (),
+ std::move (*initial_param));
-/* Parses a single tuple struct field in a tuple struct definition. Does not
- * parse commas. */
-template <typename ManagedTokenSource>
-AST::TupleField
-Parser<ManagedTokenSource>::parse_tuple_field ()
-{
- // parse outer attributes if they exist
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // parse visibility if it exists
- auto visibility = parse_visibility ();
- if (!visibility)
- return AST::TupleField::create_error ();
-
- location_t locus = lexer.peek_token ()->get_locus ();
-
- // parse type, which is required
- std::unique_ptr<AST::Type> field_type = parse_type ();
- if (field_type == nullptr)
+ if (!skip_token (RIGHT_PAREN))
{
- // error if null
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse type in tuple struct field");
- add_error (std::move (error));
-
- // skip after something
- return AST::TupleField::create_error ();
+ skip_after_end_block ();
+ return nullptr;
}
- return AST::TupleField (std::move (field_type),
- std::move (visibility.value ()), locus,
- std::move (outer_attrs));
-}
-
-// Parses a Rust "enum" tagged union item definition.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Enum>
-Parser<ManagedTokenSource>::parse_enum (AST::Visibility vis,
- AST::AttrVec outer_attrs)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (ENUM_KW);
-
- // parse enum name
- const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
- if (enum_name_tok == nullptr)
- return nullptr;
-
- Identifier enum_name = {enum_name_tok};
-
- // parse generic params (of enum container, not enum variants) if they exist
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
+ // parse return type (optional)
+ std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
- // parse where clause if it exists
+ // parse where clause (optional)
AST::WhereClause where_clause = parse_where_clause ();
- if (!skip_token (LEFT_CURLY))
+ tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ lexer.skip_token ();
+ else
{
- skip_after_end_block ();
- return nullptr;
- }
+ auto result = parse_block_expr ();
- // parse actual enum variant definitions
- std::vector<std::unique_ptr<AST::EnumItem>> enum_items
- = parse_enum_items ([] (TokenId id) { return id == RIGHT_CURLY; });
+ if (result == nullptr)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "could not parse definition in inherent impl %s definition",
+ is_method ? "method" : "function");
+ add_error (std::move (error));
- if (!skip_token (RIGHT_CURLY))
- {
- skip_after_end_block ();
- return nullptr;
+ skip_after_end_block ();
+ return nullptr;
+ }
+ body = std::move (result);
}
- return std::unique_ptr<AST::Enum> (
- new AST::Enum (std::move (enum_name), std::move (vis),
- std::move (generic_params), std::move (where_clause),
- std::move (enum_items), std::move (outer_attrs), locus));
+ return std::unique_ptr<AST::Function> (
+ new AST::Function (std::move (ident), std::move (qualifiers),
+ std::move (generic_params), std::move (function_params),
+ std::move (return_type), std::move (where_clause),
+ std::move (body), std::move (vis),
+ std::move (outer_attrs), locus));
}
-// Parses the enum variants inside an enum definiton.
+// Parses a single trait impl item (item inside a trait impl block).
template <typename ManagedTokenSource>
-std::vector<std::unique_ptr<AST::EnumItem>>
-Parser<ManagedTokenSource>::parse_enum_items ()
+std::unique_ptr<AST::AssociatedItem>
+Parser<ManagedTokenSource>::parse_trait_impl_item ()
{
- std::vector<std::unique_ptr<AST::EnumItem>> items;
-
- std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
-
- // Return empty item list if no field there
- if (initial_item == nullptr)
- return items;
+ // parse outer attributes (if they exist)
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
- items.push_back (std::move (initial_item));
+ auto vis_res = parse_visibility ();
+ if (!vis_res)
+ return nullptr;
+ auto visibility = vis_res.value ();
- while (lexer.peek_token ()->get_id () == COMMA)
+ // branch on next token:
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
{
- lexer.skip_token ();
+ case SUPER:
+ case SELF:
+ case CRATE:
+ case DOLLAR_SIGN:
+ // these seem to be SimplePath tokens, so this is a macro invocation
+ // semi
+ return parse_macro_invocation_semi (std::move (outer_attrs));
+ case IDENTIFIER:
+ if (lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
+ return parse_trait_impl_function_or_method (visibility,
+ std::move (outer_attrs));
+ else
+ return parse_macro_invocation_semi (std::move (outer_attrs));
+ case TYPE:
+ return parse_type_alias (visibility, std::move (outer_attrs));
+ case EXTERN_KW:
+ case UNSAFE:
+ case FN_KW:
+ // function or method
+ return parse_trait_impl_function_or_method (visibility,
+ std::move (outer_attrs));
+ case ASYNC:
+ return parse_async_item (visibility, std::move (outer_attrs));
+ case CONST:
+ // lookahead to resolve production - could be function/method or const
+ // item
+ t = lexer.peek_token (1);
- std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
- if (item == nullptr)
+ switch (t->get_id ())
{
- // this would occur with a trailing comma, which is allowed
- break;
- }
+ case IDENTIFIER:
+ case UNDERSCORE:
+ return parse_const_item (visibility, std::move (outer_attrs));
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_trait_impl_function_or_method (visibility,
+ std::move (outer_attrs));
+ default:
+ add_error (Error (
+ t->get_locus (),
+ "unexpected token %qs in some sort of const item in trait impl",
+ t->get_token_description ()));
- items.push_back (std::move (item));
+ lexer.skip_token (1); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ rust_unreachable ();
+ default:
+ break;
}
+ add_error (Error (t->get_locus (),
+ "unrecognised token %qs for item in trait impl",
+ t->get_token_description ()));
- items.shrink_to_fit ();
- return items;
-
- /* TODO: use template if doable (parse_non_ptr_sequence) */
+ // skip?
+ return nullptr;
}
-// Parses the enum variants inside an enum definiton.
+/* For internal use only by parse_trait_impl_item() - splits giant method into
+ * smaller ones and prevents duplication of logic. Strictly, this parses a
+ * function or method item inside a trait impl item block. */
template <typename ManagedTokenSource>
-template <typename EndTokenPred>
-std::vector<std::unique_ptr<AST::EnumItem>>
-Parser<ManagedTokenSource>::parse_enum_items (EndTokenPred is_end_tok)
+std::unique_ptr<AST::AssociatedItem>
+Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (
+ AST::Visibility vis, AST::AttrVec outer_attrs)
{
- std::vector<std::unique_ptr<AST::EnumItem>> items;
+ // this shares virtually all logic with
+ // parse_inherent_impl_function_or_method
+ // - template?
+ location_t locus = lexer.peek_token ()->get_locus ();
- std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
+ auto is_default = false;
+ auto t = lexer.peek_token ();
+ if (t->get_id () == IDENTIFIER
+ && t->get_str () == Values::WeakKeywords::DEFAULT)
+ {
+ is_default = true;
+ lexer.skip_token ();
+ }
- // Return empty item list if no field there
- if (initial_item == nullptr)
- return items;
+ // parse function or method qualifiers
+ AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
- items.push_back (std::move (initial_item));
+ skip_token (FN_KW);
- while (lexer.peek_token ()->get_id () == COMMA)
+ // parse function or method name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
{
- lexer.skip_token ();
+ return nullptr;
+ }
+ Identifier ident{ident_tok};
- if (is_end_tok (lexer.peek_token ()->get_id ()))
- break;
+ // DEBUG:
+ rust_debug (
+ "about to start parsing generic params in trait impl function or method");
- std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
- if (item == nullptr)
- {
- /* TODO should this ignore all successfully parsed enum items just
- * because one failed? */
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse enum item in enum items");
- add_error (std::move (error));
+ // parse generic params
+ std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+ = parse_generic_params_in_angles ();
- return {};
- }
+ // DEBUG:
+ rust_debug (
+ "finished parsing generic params in trait impl function or method");
- items.push_back (std::move (item));
+ if (!skip_token (LEFT_PAREN))
+ {
+ // skip after somewhere?
+ return nullptr;
}
- items.shrink_to_fit ();
- return items;
+ // now for function vs method disambiguation - method has opening "self"
+ // param
+ auto initial_param = parse_self_param ();
- /* TODO: use template if doable (parse_non_ptr_sequence) */
-}
+ if (!initial_param.has_value ()
+ && initial_param.error () != ParseSelfError::NOT_SELF)
+ return nullptr;
-/* Parses a single enum variant item in an enum definition. Does not parse
- * commas. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::EnumItem>
-Parser<ManagedTokenSource>::parse_enum_item ()
-{
- // parse outer attributes if they exist
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // parse visibility, which may or may not exist
- auto vis_res = parse_visibility ();
- if (!vis_res)
- return nullptr;
- auto vis = vis_res.value ();
-
- // parse name for enum item, which is required
- const_TokenPtr item_name_tok = lexer.peek_token ();
- if (item_name_tok->get_id () != IDENTIFIER)
- {
- // this may not be an error but it means there is no enum item here
- return nullptr;
- }
- lexer.skip_token ();
- Identifier item_name{item_name_tok};
-
- // branch based on next token
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
+ // FIXME: ensure that self param doesn't accidently consume tokens for a
+ // function
+ bool is_method = false;
+ if (initial_param.has_value ())
{
- case LEFT_PAREN:
- {
- // tuple enum item
- lexer.skip_token ();
-
- std::vector<AST::TupleField> tuple_fields;
- // Might be empty tuple for unit tuple enum variant.
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- tuple_fields = std::vector<AST::TupleField> ();
- else
- tuple_fields = parse_tuple_fields ();
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip after somewhere
- return nullptr;
- }
+ if ((*initial_param)->is_self ())
+ is_method = true;
- return std::unique_ptr<AST::EnumItemTuple> (new AST::EnumItemTuple (
- std::move (item_name), std::move (vis), std::move (tuple_fields),
- std::move (outer_attrs), item_name_tok->get_locus ()));
- }
- case LEFT_CURLY:
- {
- // struct enum item
- lexer.skip_token ();
+ // skip comma so function and method regular params can be parsed in
+ // same way
+ if (lexer.peek_token ()->get_id () == COMMA)
+ {
+ lexer.skip_token ();
+ }
- std::vector<AST::StructField> struct_fields
- = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
+ // DEBUG
+ rust_debug ("successfully parsed self param in method trait impl item");
+ }
- if (!skip_token (RIGHT_CURLY))
- {
- // skip after somewhere
- return nullptr;
- }
+ // DEBUG
+ rust_debug (
+ "started to parse function params in function or method trait impl item");
- return std::unique_ptr<AST::EnumItemStruct> (new AST::EnumItemStruct (
- std::move (item_name), std::move (vis), std::move (struct_fields),
- std::move (outer_attrs), item_name_tok->get_locus ()));
- }
- case EQUAL:
- {
- // discriminant enum item
- lexer.skip_token ();
+ // parse trait function params (only if next token isn't right paren)
+ std::vector<std::unique_ptr<AST::Param>> function_params;
+ if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
+ {
+ function_params
+ = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
- std::unique_ptr<AST::Expr> discriminant_expr = parse_expr ();
+ if (function_params.empty ())
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse function params in trait impl %s definition",
+ is_method ? "method" : "function");
+ add_error (std::move (error));
- return std::unique_ptr<AST::EnumItemDiscriminant> (
- new AST::EnumItemDiscriminant (std::move (item_name), std::move (vis),
- std::move (discriminant_expr),
- std::move (outer_attrs),
- item_name_tok->get_locus ()));
- }
- default:
- // regular enum with just an identifier
- return std::unique_ptr<AST::EnumItem> (
- new AST::EnumItem (std::move (item_name), std::move (vis),
- std::move (outer_attrs),
- item_name_tok->get_locus ()));
+ skip_after_next_block ();
+ return nullptr;
+ }
}
-}
-// Parses a C-style (and C-compat) untagged union declaration.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Union>
-Parser<ManagedTokenSource>::parse_union (AST::Visibility vis,
- AST::AttrVec outer_attrs)
-{
- /* hack - "weak keyword" by finding identifier called "union" (lookahead in
- * item switch) */
- const_TokenPtr union_keyword = expect_token (IDENTIFIER);
- rust_assert (union_keyword->get_str () == Values::WeakKeywords::UNION);
- location_t locus = union_keyword->get_locus ();
+ if (initial_param.has_value ())
+ function_params.insert (function_params.begin (),
+ std::move (*initial_param));
- // parse actual union name
- const_TokenPtr union_name_tok = expect_token (IDENTIFIER);
- if (union_name_tok == nullptr)
+ // DEBUG
+ rust_debug ("successfully parsed function params in function or method "
+ "trait impl item");
+
+ if (!skip_token (RIGHT_PAREN))
{
skip_after_next_block ();
return nullptr;
}
- Identifier union_name{union_name_tok};
- // parse optional generic parameters
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
+ // parse return type (optional)
+ std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
- // parse optional where clause
+ // DEBUG
+ rust_debug (
+ "successfully parsed return type in function or method trait impl item");
+
+ // parse where clause (optional)
AST::WhereClause where_clause = parse_where_clause ();
- if (!skip_token (LEFT_CURLY))
- {
- skip_after_end_block ();
- return nullptr;
- }
+ // DEBUG
+ rust_debug (
+ "successfully parsed where clause in function or method trait impl item");
- /* parse union inner items as "struct fields" because hey, syntax reuse.
- * Spec said so. */
- std::vector<AST::StructField> union_fields
- = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
+ // parse function definition (in block) - semicolon not allowed
+ tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
- if (!skip_token (RIGHT_CURLY))
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ lexer.skip_token ();
+ else
{
- // skip after somewhere
- return nullptr;
+ auto result = parse_block_expr ();
+ if (result == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "could not parse definition in trait impl %s definition",
+ is_method ? "method" : "function");
+ add_error (std::move (error));
+
+ skip_after_end_block ();
+ return nullptr;
+ }
+ body = std::move (result);
}
- return std::unique_ptr<AST::Union> (
- new AST::Union (std::move (union_name), std::move (vis),
- std::move (generic_params), std::move (where_clause),
- std::move (union_fields), std::move (outer_attrs), locus));
+ return std::unique_ptr<AST::Function> (
+ new AST::Function (std::move (ident), std::move (qualifiers),
+ std::move (generic_params), std::move (function_params),
+ std::move (return_type), std::move (where_clause),
+ std::move (body), std::move (vis),
+ std::move (outer_attrs), locus, is_default));
}
-/* Parses a "constant item" (compile-time constant to maybe "inline"
- * throughout the program - like constexpr). */
+// Parses an extern block of declarations.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ConstantItem>
-Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::ExternBlock>
+Parser<ManagedTokenSource>::parse_extern_block (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
{
location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (CONST);
+ skip_token (EXTERN_KW);
- /* get constant identifier - this is either a proper identifier or the _
- * wildcard */
- const_TokenPtr ident_tok = lexer.peek_token ();
- // make default identifier the underscore wildcard one
- std::string ident (Values::Keywords::UNDERSCORE);
- switch (ident_tok->get_id ())
+ // detect optional abi name
+ std::string abi;
+ const_TokenPtr next_tok = lexer.peek_token ();
+ if (next_tok->get_id () == STRING_LITERAL)
{
- case IDENTIFIER:
- ident = ident_tok->get_str ();
- lexer.skip_token ();
- break;
- case UNDERSCORE:
- // do nothing - identifier is already "_"
lexer.skip_token ();
- break;
- default:
- add_error (
- Error (ident_tok->get_locus (),
- "expected item name (identifier or %<_%>) in constant item "
- "declaration - found %qs",
- ident_tok->get_token_description ()));
-
- skip_after_semicolon ();
- return nullptr;
+ abi = next_tok->get_str ();
}
- if (!skip_token (COLON))
+ if (!skip_token (LEFT_CURLY))
{
- skip_after_semicolon ();
+ skip_after_end_block ();
return nullptr;
}
- // parse constant type (required)
- std::unique_ptr<AST::Type> type = parse_type ();
+ AST::AttrVec inner_attrs = parse_inner_attributes ();
- // A const with no given expression value
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- {
- lexer.skip_token ();
- return std::unique_ptr<AST::ConstantItem> (
- new AST::ConstantItem (std::move (ident), std::move (vis),
- std::move (type), std::move (outer_attrs),
- locus));
- }
+ // parse declarations inside extern block
+ std::vector<std::unique_ptr<AST::ExternalItem>> extern_items;
- if (!skip_token (EQUAL))
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY)
{
- skip_after_semicolon ();
- return nullptr;
- }
+ std::unique_ptr<AST::ExternalItem> extern_item = parse_external_item ();
- // parse constant expression (required)
- std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (extern_item == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse external item despite not reaching "
+ "end of extern block");
+ add_error (std::move (error));
- if (!skip_token (SEMICOLON))
+ return nullptr;
+ }
+
+ extern_items.push_back (std::move (extern_item));
+
+ t = lexer.peek_token ();
+ }
+
+ if (!skip_token (RIGHT_CURLY))
{
- // skip somewhere?
+ // skip somewhere
return nullptr;
}
- return std::unique_ptr<AST::ConstantItem> (
- new AST::ConstantItem (std::move (ident), std::move (vis), std::move (type),
- std::move (expr), std::move (outer_attrs), locus));
+ extern_items.shrink_to_fit ();
+
+ return std::unique_ptr<AST::ExternBlock> (
+ new AST::ExternBlock (std::move (abi), std::move (extern_items),
+ std::move (vis), std::move (inner_attrs),
+ std::move (outer_attrs), locus));
}
-// Parses a "static item" (static storage item, with 'static lifetime).
+// Parses a single extern block item (static or function declaration).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::StaticItem>
-Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::ExternalItem>
+Parser<ManagedTokenSource>::parse_external_item ()
{
+ // parse optional outer attributes
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
+
location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (STATIC_KW);
- // determine whether static item is mutable
- bool is_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
+ // parse optional visibility
+ auto vis_res = parse_visibility ();
+ if (!vis_res)
+ return nullptr;
+ auto vis = vis_res.value ();
+
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
{
- is_mut = true;
- lexer.skip_token ();
- }
+ case IDENTIFIER:
+ return parse_macro_invocation_semi (outer_attrs);
+ case STATIC_KW:
+ {
+ // parse extern static item
+ lexer.skip_token ();
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- return nullptr;
+ // parse mut (optional)
+ bool has_mut = false;
+ if (lexer.peek_token ()->get_id () == MUT)
+ {
+ lexer.skip_token ();
+ has_mut = true;
+ }
- Identifier ident{ident_tok};
+ // parse identifier
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ {
+ skip_after_semicolon ();
+ return nullptr;
+ }
+ Identifier ident{ident_tok};
+
+ if (!skip_token (COLON))
+ {
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ // parse type (required)
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type in external static item");
+ add_error (std::move (error));
+
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ if (!skip_token (SEMICOLON))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::ExternalStaticItem> (
+ new AST::ExternalStaticItem (std::move (ident), std::move (type),
+ has_mut, std::move (vis),
+ std::move (outer_attrs), locus));
+ }
+ case FN_KW:
+ return parse_function (std::move (vis), std::move (outer_attrs), true);
+
+ case TYPE:
+ return parse_external_type_item (std::move (vis),
+ std::move (outer_attrs));
+ default:
+ // error
+ add_error (
+ Error (t->get_locus (),
+ "unrecognised token %qs in extern block item declaration",
+ t->get_token_description ()));
- if (!skip_token (COLON))
- {
skip_after_semicolon ();
return nullptr;
}
+}
- // parse static item type (required)
- std::unique_ptr<AST::Type> type = parse_type ();
-
- if (!skip_token (EQUAL))
+// Parses a statement (will further disambiguate any statement).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Stmt>
+Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
+{
+ // quick exit for empty statement
+ // FIXME: Can we have empty statements without semicolons? Just nothing?
+ const_TokenPtr t = lexer.peek_token ();
+ if (t->get_id () == SEMICOLON)
{
- skip_after_semicolon ();
- return nullptr;
+ lexer.skip_token ();
+ return std::unique_ptr<AST::EmptyStmt> (
+ new AST::EmptyStmt (t->get_locus ()));
}
- // parse static item expression (required)
- std::unique_ptr<AST::Expr> expr = parse_expr ();
+ // parse outer attributes
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
- if (!skip_token (SEMICOLON))
+ // parsing this will be annoying because of the many different possibilities
+ /* best may be just to copy paste in parse_item switch, and failing that try
+ * to parse outer attributes, and then pass them in to either a let
+ * statement or (fallback) expression statement. */
+ // FIXME: think of a way to do this without such a large switch?
+ t = lexer.peek_token ();
+ switch (t->get_id ())
{
- // skip after somewhere
- return nullptr;
+ case LET:
+ // let statement
+ return parse_let_stmt (std::move (outer_attrs), restrictions);
+ case PUB:
+ case MOD:
+ case EXTERN_KW:
+ case USE:
+ case FN_KW:
+ case TYPE:
+ case STRUCT_KW:
+ case ENUM_KW:
+ case CONST:
+ case STATIC_KW:
+ case AUTO:
+ case TRAIT:
+ case IMPL:
+ case MACRO:
+ /* TODO: implement union keyword but not really because of
+ * context-dependence crappy hack way to parse a union written below to
+ * separate it from the good code. */
+ // case UNION:
+ case UNSAFE: // maybe - unsafe traits are a thing
+ /* if any of these (should be all possible VisItem prefixes), parse a
+ * VisItem can't parse item because would require reparsing outer
+ * attributes */
+ // may also be unsafe block
+ if (lexer.peek_token (1)->get_id () == LEFT_CURLY)
+ {
+ return parse_expr_stmt (std::move (outer_attrs), restrictions);
+ }
+ else
+ {
+ return parse_vis_item (std::move (outer_attrs));
+ }
+ break;
+ // crappy hack to do union "keyword"
+ case IDENTIFIER:
+ if (t->get_str () == Values::WeakKeywords::UNION
+ && lexer.peek_token (1)->get_id () == IDENTIFIER)
+ {
+ return parse_vis_item (std::move (outer_attrs));
+ // or should this go straight to parsing union?
+ }
+ else if (is_macro_rules_def (t))
+ {
+ // macro_rules! macro item
+ return parse_macro_rules_def (std::move (outer_attrs));
+ }
+ gcc_fallthrough ();
+ // TODO: find out how to disable gcc "implicit fallthrough" warning
+ default:
+ // fallback: expression statement
+ return parse_expr_stmt (std::move (outer_attrs), restrictions);
+ break;
}
-
- return std::unique_ptr<AST::StaticItem> (
- new AST::StaticItem (std::move (ident), is_mut, std::move (type),
- std::move (expr), std::move (vis),
- std::move (outer_attrs), locus));
}
-// Parses a trait definition item, including unsafe ones.
+// Parses a let statement.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Trait>
-Parser<ManagedTokenSource>::parse_trait (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+std::unique_ptr<AST::LetStmt>
+Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
+ ParseRestrictions restrictions)
{
location_t locus = lexer.peek_token ()->get_locus ();
- bool is_unsafe = false;
- bool is_auto_trait = false;
+ skip_token (LET);
- if (lexer.peek_token ()->get_id () == UNSAFE)
+ // parse pattern (required)
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
{
- is_unsafe = true;
- lexer.skip_token ();
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in let statement");
+ add_error (std::move (error));
+
+ skip_after_semicolon ();
+ return nullptr;
}
- if (lexer.peek_token ()->get_id () == AUTO)
+ // parse type declaration (optional)
+ std::unique_ptr<AST::Type> type = nullptr;
+ if (lexer.peek_token ()->get_id () == COLON)
{
- is_auto_trait = true;
+ // must have a type declaration
lexer.skip_token ();
- }
-
- skip_token (TRAIT);
- // parse trait name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- return nullptr;
+ type = parse_type ();
+ if (type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type in let statement");
+ add_error (std::move (error));
- Identifier ident{ident_tok};
-
- // parse generic parameters (if they exist)
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
-
- // create placeholder type param bounds in case they don't exist
- std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
-
- // parse type param bounds (if they exist)
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
-
- type_param_bounds = parse_type_param_bounds (
- [] (TokenId id) { return id == WHERE || id == LEFT_CURLY; });
- // type_param_bounds = parse_type_param_bounds ();
- }
-
- // parse where clause (if it exists)
- AST::WhereClause where_clause = parse_where_clause ();
-
- if (!skip_token (LEFT_CURLY))
- {
- skip_after_end_block ();
- return nullptr;
+ skip_after_semicolon ();
+ return nullptr;
+ }
}
- // parse inner attrs (if they exist)
- AST::AttrVec inner_attrs = parse_inner_attributes ();
-
- // parse trait items
- std::vector<std::unique_ptr<AST::AssociatedItem>> trait_items;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
+ // parse expression to set variable to (optional)
+ std::unique_ptr<AST::Expr> expr = nullptr;
+ if (lexer.peek_token ()->get_id () == EQUAL)
{
- std::unique_ptr<AST::AssociatedItem> trait_item = parse_trait_item ();
+ // must have an expression
+ lexer.skip_token ();
- if (trait_item == nullptr)
+ expr = parse_expr ();
+ if (expr == nullptr)
{
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse trait item in trait");
- add_error (std::move (error));
-
+ skip_after_semicolon ();
return nullptr;
}
- trait_items.push_back (std::move (trait_item));
-
- t = lexer.peek_token ();
}
- if (!skip_token (RIGHT_CURLY))
+ tl::optional<std::unique_ptr<AST::Expr>> else_expr = tl::nullopt;
+ if (maybe_skip_token (ELSE))
+ else_expr = parse_block_expr ();
+
+ if (restrictions.consume_semi)
{
- // skip after something
- return nullptr;
+ // `stmt` macro variables are parsed without a semicolon, but should be
+ // parsed as a full statement when interpolated. This should be handled
+ // by having the interpolated statement be distinguishable from normal
+ // tokens, e.g. by NT tokens.
+ if (restrictions.allow_close_after_expr_stmt)
+ maybe_skip_token (SEMICOLON);
+ else if (!skip_token (SEMICOLON))
+ return nullptr;
}
- trait_items.shrink_to_fit ();
- return std::unique_ptr<AST::Trait> (
- new AST::Trait (std::move (ident), is_unsafe, is_auto_trait,
- std::move (generic_params), std::move (type_param_bounds),
- std::move (where_clause), std::move (trait_items),
- std::move (vis), std::move (outer_attrs),
- std::move (inner_attrs), locus));
+ return std::unique_ptr<AST::LetStmt> (
+ new AST::LetStmt (std::move (pattern), std::move (expr), std::move (type),
+ std::move (else_expr), std::move (outer_attrs), locus));
}
-// Parses a trait item used inside traits (not trait, the Item).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssociatedItem>
-Parser<ManagedTokenSource>::parse_trait_item ()
+tl::optional<AST::GenericArg>
+Parser<ManagedTokenSource>::parse_generic_arg ()
{
- // parse outer attributes (if they exist)
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- auto vis_res = parse_visibility ();
- if (!vis_res)
- return nullptr;
-
- auto vis = vis_res.value ();
+ auto tok = lexer.peek_token ();
+ std::unique_ptr<AST::Expr> expr = nullptr;
- // lookahead to determine what type of trait item to parse
- const_TokenPtr tok = lexer.peek_token ();
switch (tok->get_id ())
{
- case SUPER:
- case SELF:
- case CRATE:
- case DOLLAR_SIGN:
- // these seem to be SimplePath tokens, so this is a macro invocation
- // semi
- return parse_macro_invocation_semi (std::move (outer_attrs));
case IDENTIFIER:
- if (lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
- return parse_function (std::move (vis), std::move (outer_attrs));
- else
- return parse_macro_invocation_semi (std::move (outer_attrs));
- case TYPE:
- return parse_trait_type (std::move (outer_attrs), vis);
- case CONST:
- // disambiguate with function qualifier
- if (lexer.peek_token (1)->get_id () == IDENTIFIER)
- {
- return parse_trait_const (std::move (outer_attrs));
- }
- // else, fallthrough to function
- // TODO: find out how to disable gcc "implicit fallthrough" error
- gcc_fallthrough ();
- case ASYNC:
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs));
- default:
+ {
+ // This is a bit of a weird situation: With an identifier token, we
+ // could either have a valid type or a macro (FIXME: anything else?). So
+ // we need one bit of lookahead to differentiate if this is really
+ auto next_tok = lexer.peek_token (1);
+ if (next_tok->get_id () == LEFT_ANGLE
+ || next_tok->get_id () == SCOPE_RESOLUTION
+ || next_tok->get_id () == EXCLAM)
+ {
+ auto type = parse_type ();
+ if (type)
+ return AST::GenericArg::create_type (std::move (type));
+ else
+ return tl::nullopt;
+ }
+ else if (next_tok->get_id () == COLON)
+ {
+ lexer.skip_token (); // skip ident
+ lexer.skip_token (); // skip colon
+
+ auto tok = lexer.peek_token ();
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+ = parse_type_param_bounds ();
+
+ auto type = std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), tok->get_locus (),
+ false));
+ if (type)
+ return AST::GenericArg::create_type (std::move (type));
+ else
+ return tl::nullopt;
+ }
+ lexer.skip_token ();
+ return AST::GenericArg::create_ambiguous (tok->get_str (),
+ tok->get_locus ());
+ }
+ case LEFT_CURLY:
+ expr = parse_block_expr ();
+ break;
+ case MINUS:
+ case STRING_LITERAL:
+ case CHAR_LITERAL:
+ case INT_LITERAL:
+ case FLOAT_LITERAL:
+ case TRUE_LITERAL:
+ case FALSE_LITERAL:
+ expr = parse_literal_expr ();
break;
+ // FIXME: Because of this, error reporting is garbage for const generic
+ // parameter's default values
+ default:
+ {
+ auto type = parse_type ();
+ // FIXME: Find a better way to do this?
+ if (type)
+ return AST::GenericArg::create_type (std::move (type));
+ else
+ return tl::nullopt;
+ }
}
- add_error (Error (tok->get_locus (),
- "unrecognised token %qs for item in trait",
- tok->get_token_description ()));
- // skip?
- return nullptr;
+
+ if (!expr)
+ return tl::nullopt;
+
+ return AST::GenericArg::create_const (std::move (expr));
}
-// Parse a typedef trait item.
+// Parses the generic arguments in each path segment.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::TraitItemType>
-Parser<ManagedTokenSource>::parse_trait_type (AST::AttrVec outer_attrs,
- AST::Visibility vis)
+AST::GenericArgs
+Parser<ManagedTokenSource>::parse_path_generic_args ()
{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (TYPE);
-
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- return nullptr;
-
- Identifier ident{ident_tok};
+ if (lexer.peek_token ()->get_id () == LEFT_SHIFT)
+ lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
- // Parse optional generic parameters for GATs (Generic Associated Types)
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
- if (lexer.peek_token ()->get_id () == LEFT_ANGLE)
+ if (!skip_token (LEFT_ANGLE))
{
- generic_params = parse_generic_params_in_angles ();
+ // skip after somewhere?
+ return AST::GenericArgs::create_empty ();
}
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+ // We need to parse all lifetimes, then parse types and const generics in
+ // any order.
- // parse optional colon
- if (lexer.peek_token ()->get_id () == COLON)
+ // try to parse lifetimes first
+ std::vector<AST::Lifetime> lifetime_args;
+
+ const_TokenPtr t = lexer.peek_token ();
+ location_t locus = t->get_locus ();
+ while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
{
+ auto lifetime = parse_lifetime (false);
+ if (!lifetime)
+ {
+ // not necessarily an error
+ break;
+ }
+
+ lifetime_args.push_back (std::move (lifetime.value ()));
+
+ // if next token isn't comma, then it must be end of list
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ // skip comma
lexer.skip_token ();
- // parse optional type param bounds
- bounds
- = parse_type_param_bounds ([] (TokenId id) { return id == SEMICOLON; });
- // bounds = parse_type_param_bounds ();
+ t = lexer.peek_token ();
}
- if (!skip_token (SEMICOLON))
- {
- // skip?
- return nullptr;
- }
+ // try to parse types and const generics second
+ std::vector<AST::GenericArg> generic_args;
- return std::unique_ptr<AST::TraitItemType> (
- new AST::TraitItemType (std::move (ident), std::move (generic_params),
- std::move (bounds), std::move (outer_attrs), vis,
- locus));
-}
+ // TODO: think of better control structure
+ t = lexer.peek_token ();
+ while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
+ {
+ // FIXME: Is it fine to break if there is one binding? Can't there be
+ // bindings in between types?
-// Parses a constant trait item.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ConstantItem>
-Parser<ManagedTokenSource>::parse_trait_const (AST::AttrVec outer_attrs)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (CONST);
+ // ensure not binding being parsed as type accidently
+ if (t->get_id () == IDENTIFIER
+ && lexer.peek_token (1)->get_id () == EQUAL)
+ break;
- // parse constant item name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- return nullptr;
+ auto arg = parse_generic_arg ();
+ if (arg)
+ {
+ generic_args.emplace_back (std::move (arg.value ()));
+ }
- Identifier ident{ident_tok};
+ // FIXME: Do we need to break if we encounter an error?
- if (!skip_token (COLON))
- {
- skip_after_semicolon ();
- return nullptr;
+ // if next token isn't comma, then it must be end of list
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
+
+ // skip comma
+ lexer.skip_token ();
+ t = lexer.peek_token ();
}
- // parse constant trait item type
- std::unique_ptr<AST::Type> type = parse_type ();
+ // try to parse bindings third
+ std::vector<AST::GenericArgsBinding> binding_args;
- // parse constant trait body expression, if it exists
- std::unique_ptr<AST::Expr> const_body = nullptr;
- if (lexer.peek_token ()->get_id () == EQUAL)
+ // TODO: think of better control structure
+ t = lexer.peek_token ();
+ while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
{
+ AST::GenericArgsBinding binding = parse_generic_args_binding ();
+ if (binding.is_error ())
+ {
+ // not necessarily an error
+ break;
+ }
+
+ binding_args.push_back (std::move (binding));
+
+ // if next token isn't comma, then it must be end of list
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ // skip comma
lexer.skip_token ();
- // expression must exist, so parse it
- const_body = parse_expr ();
+ t = lexer.peek_token ();
}
- if (!skip_token (SEMICOLON))
- {
- // skip after something?
- return nullptr;
- }
+ // skip any trailing commas
+ if (lexer.peek_token ()->get_id () == COMMA)
+ lexer.skip_token ();
- return std::unique_ptr<AST::ConstantItem> (new AST::ConstantItem (
- std::move (ident), AST::Visibility::create_private (), std::move (type),
- std::move (const_body), std::move (outer_attrs), locus));
+ if (!skip_generics_right_angle ())
+ return AST::GenericArgs::create_empty ();
+
+ lifetime_args.shrink_to_fit ();
+ generic_args.shrink_to_fit ();
+ binding_args.shrink_to_fit ();
+
+ return AST::GenericArgs (std::move (lifetime_args), std::move (generic_args),
+ std::move (binding_args), locus);
}
-/* Parses a struct "impl" item (both inherent impl and trait impl can be
- * parsed here), */
+// Parses a binding in a generic args path segment.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Impl>
-Parser<ManagedTokenSource>::parse_impl (AST::Visibility vis,
- AST::AttrVec outer_attrs)
+AST::GenericArgsBinding
+Parser<ManagedTokenSource>::parse_generic_args_binding ()
{
- /* Note that only trait impls are allowed to be unsafe. So if unsafe, it
- * must be a trait impl. However, this isn't enough for full disambiguation,
- * so don't branch here. */
- location_t locus = lexer.peek_token ()->get_locus ();
- bool is_unsafe = false;
- if (lexer.peek_token ()->get_id () == UNSAFE)
+ const_TokenPtr ident_tok = lexer.peek_token ();
+ if (ident_tok->get_id () != IDENTIFIER)
{
- lexer.skip_token ();
- is_unsafe = true;
+ // allow non error-inducing use
+ // skip somewhere?
+ return AST::GenericArgsBinding::create_error ();
}
+ lexer.skip_token ();
+ Identifier ident{ident_tok};
- if (!skip_token (IMPL))
+ if (!skip_token (EQUAL))
{
- skip_after_next_block ();
- return nullptr;
+ // skip after somewhere?
+ return AST::GenericArgsBinding::create_error ();
}
- // parse generic params (shared by trait and inherent impls)
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
-
- // Again, trait impl-only feature, but optional one, so can be used for
- // branching yet.
- bool has_exclam = false;
- if (lexer.peek_token ()->get_id () == EXCLAM)
+ // parse type (required)
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
{
- lexer.skip_token ();
- has_exclam = true;
+ // skip somewhere?
+ return AST::GenericArgsBinding::create_error ();
}
- /* FIXME: code that doesn't look shit for TypePath. Also, make sure this
- * doesn't parse too much and not work. */
- AST::TypePath type_path = parse_type_path ();
- if (type_path.is_error () || lexer.peek_token ()->get_id () != FOR)
- {
- /* cannot parse type path (or not for token next, at least), so must be
- * inherent impl */
-
- // hacky conversion of TypePath stack object to Type pointer
- std::unique_ptr<AST::Type> type = nullptr;
- if (!type_path.is_error ())
- type = std::unique_ptr<AST::TypePath> (
- new AST::TypePath (std::move (type_path)));
- else
- type = parse_type ();
+ return AST::GenericArgsBinding (std::move (ident), std::move (type),
+ ident_tok->get_locus ());
+}
- // Type is required, so error if null
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse type in inherent impl");
- add_error (std::move (error));
+// Parses a self param. Also handles self param not existing.
+template <typename ManagedTokenSource>
+tl::expected<std::unique_ptr<AST::Param>, ParseSelfError>
+Parser<ManagedTokenSource>::parse_self_param ()
+{
+ bool has_reference = false;
+ AST::Lifetime lifetime = AST::Lifetime::elided ();
- skip_after_next_block ();
- return nullptr;
- }
+ location_t locus = lexer.peek_token ()->get_locus ();
- // parse optional where clause
- AST::WhereClause where_clause = parse_where_clause ();
+ // TODO: Feels off, find a better way to clearly express this
+ std::vector<std::vector<TokenId>> ptrs
+ = {{ASTERISK, SELF} /* *self */,
+ {ASTERISK, CONST, SELF} /* *const self */,
+ {ASTERISK, MUT, SELF} /* *mut self */};
- if (!skip_token (LEFT_CURLY))
+ for (auto &s : ptrs)
+ {
+ size_t i = 0;
+ for (i = 0; i < s.size (); i++)
+ if (lexer.peek_token (i)->get_id () != s[i])
+ break;
+ if (i == s.size ())
{
- // TODO: does this still skip properly?
- skip_after_end_block ();
- return nullptr;
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "cannot pass %<self%> by raw pointer");
+ return tl::make_unexpected (ParseSelfError::SELF_PTR);
}
+ }
- // parse inner attributes (optional)
- AST::AttrVec inner_attrs = parse_inner_attributes ();
+ // Trying to find those patterns:
+ //
+ // &'lifetime mut self
+ // &'lifetime self
+ // & mut self
+ // & self
+ // mut self
+ // self
+ //
+ // If not found, it is probably a function, exit and let function parsing
+ // handle it.
+ bool is_self = false;
+ for (size_t i = 0; i < 5; i++)
+ if (lexer.peek_token (i)->get_id () == SELF)
+ is_self = true;
- // parse inherent impl items
- std::vector<std::unique_ptr<AST::AssociatedItem>> impl_items;
+ if (!is_self)
+ return tl::make_unexpected (ParseSelfError::NOT_SELF);
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
- {
- std::unique_ptr<AST::AssociatedItem> impl_item
- = parse_inherent_impl_item ();
+ // test if self is a reference parameter
+ if (lexer.peek_token ()->get_id () == AMP)
+ {
+ has_reference = true;
+ lexer.skip_token ();
- if (impl_item == nullptr)
+ // now test whether it has a lifetime
+ if (lexer.peek_token ()->get_id () == LIFETIME)
+ {
+ // something went wrong somehow
+ if (auto parsed_lifetime = parse_lifetime (true))
{
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse inherent impl item in inherent impl");
+ lifetime = parsed_lifetime.value ();
+ }
+ else
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime in self param");
add_error (std::move (error));
- return nullptr;
+ // skip after somewhere?
+ return tl::make_unexpected (ParseSelfError::PARSING);
}
-
- impl_items.push_back (std::move (impl_item));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere
- return nullptr;
}
+ }
- // DEBUG
- rust_debug ("successfully parsed inherent impl");
-
- impl_items.shrink_to_fit ();
-
- return std::unique_ptr<AST::InherentImpl> (new AST::InherentImpl (
- std::move (impl_items), std::move (generic_params), std::move (type),
- std::move (where_clause), std::move (vis), std::move (inner_attrs),
- std::move (outer_attrs), locus));
+ // test for mut
+ bool has_mut = false;
+ if (lexer.peek_token ()->get_id () == MUT)
+ {
+ has_mut = true;
+ lexer.skip_token ();
}
- else
+
+ // skip self token
+ const_TokenPtr self_tok = lexer.peek_token ();
+ if (self_tok->get_id () != SELF)
{
- // type path must both be valid and next token is for, so trait impl
- if (!skip_token (FOR))
- {
- skip_after_next_block ();
- return nullptr;
- }
+ // skip after somewhere?
+ return tl::make_unexpected (ParseSelfError::NOT_SELF);
+ }
+ lexer.skip_token ();
- // parse type
- std::unique_ptr<AST::Type> type = parse_type ();
- // ensure type is included as it is required
+ // parse optional type
+ std::unique_ptr<AST::Type> type = nullptr;
+ if (lexer.peek_token ()->get_id () == COLON)
+ {
+ lexer.skip_token ();
+
+ // type is now required
+ type = parse_type ();
if (type == nullptr)
{
Error error (lexer.peek_token ()->get_locus (),
- "could not parse type in trait impl");
+ "could not parse type in self param");
add_error (std::move (error));
- skip_after_next_block ();
- return nullptr;
- }
-
- // parse optional where clause
- AST::WhereClause where_clause = parse_where_clause ();
-
- if (!skip_token (LEFT_CURLY))
- {
- // TODO: does this still skip properly?
- skip_after_end_block ();
- return nullptr;
- }
-
- // parse inner attributes (optional)
- AST::AttrVec inner_attrs = parse_inner_attributes ();
-
- // parse trait impl items
- std::vector<std::unique_ptr<AST::AssociatedItem>> impl_items;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
- {
- std::unique_ptr<AST::AssociatedItem> impl_item
- = parse_trait_impl_item ();
-
- if (impl_item == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse trait impl item in trait impl");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- impl_items.push_back (std::move (impl_item));
-
- t = lexer.peek_token ();
-
- // DEBUG
- rust_debug ("successfully parsed a trait impl item");
- }
- // DEBUG
- rust_debug ("successfully finished trait impl items");
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere
- return nullptr;
+ // skip after somewhere?
+ return tl::make_unexpected (ParseSelfError::PARSING);
}
+ }
- // DEBUG
- rust_debug ("successfully parsed trait impl");
+ // ensure that cannot have both type and reference
+ if (type != nullptr && has_reference)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "cannot have both a reference and a type specified in a self param");
+ add_error (std::move (error));
- impl_items.shrink_to_fit ();
+ // skip after somewhere?
+ return tl::make_unexpected (ParseSelfError::PARSING);
+ }
- return std::unique_ptr<AST::TraitImpl> (
- new AST::TraitImpl (std::move (type_path), is_unsafe, has_exclam,
- std::move (impl_items), std::move (generic_params),
- std::move (type), std::move (where_clause),
- std::move (vis), std::move (inner_attrs),
- std::move (outer_attrs), locus));
+ if (has_reference)
+ {
+ return std::make_unique<AST::SelfParam> (std::move (lifetime), has_mut,
+ locus);
+ }
+ else
+ {
+ // note that type may be nullptr here and that's fine
+ return std::make_unique<AST::SelfParam> (std::move (type), has_mut,
+ locus);
}
}
-// Parses a single inherent impl item (item inside an inherent impl block).
+/* Parses an expression or macro statement. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssociatedItem>
-Parser<ManagedTokenSource>::parse_inherent_impl_item ()
+std::unique_ptr<AST::Stmt>
+Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
+ ParseRestrictions restrictions)
{
- // parse outer attributes (if they exist)
- AST::AttrVec outer_attrs = parse_outer_attributes ();
+ location_t locus = lexer.peek_token ()->get_locus ();
- // TODO: cleanup - currently an unreadable mess
+ std::unique_ptr<AST::Expr> expr;
- // branch on next token:
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
+ switch (lexer.peek_token ()->get_id ())
{
case IDENTIFIER:
- // FIXME: Arthur: Do we need to some lookahead here?
- return parse_macro_invocation_semi (outer_attrs);
+ case CRATE:
case SUPER:
case SELF:
- case CRATE:
- case PUB:
+ case SELF_ALIAS:
+ case DOLLAR_SIGN:
+ case SCOPE_RESOLUTION:
{
- // visibility, so not a macro invocation semi - must be constant,
- // function, or method
- auto vis_res = parse_visibility ();
- if (!vis_res)
- return nullptr;
- auto vis = vis_res.value ();
+ AST::PathInExpression path = parse_path_in_expression ();
+ std::unique_ptr<AST::Expr> null_denotation;
- // TODO: is a recursive call to parse_inherent_impl_item better?
- switch (lexer.peek_token ()->get_id ())
+ if (lexer.peek_token ()->get_id () == EXCLAM)
{
- case EXTERN_KW:
- case UNSAFE:
- case FN_KW:
- // function or method
- return parse_inherent_impl_function_or_method (std::move (vis),
- std::move (
- outer_attrs));
- case CONST:
- // lookahead to resolve production - could be function/method or
- // const item
- t = lexer.peek_token (1);
+ std::unique_ptr<AST::MacroInvocation> invoc
+ = parse_macro_invocation_partial (std::move (path),
+ std::move (outer_attrs));
- switch (t->get_id ())
+ if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
{
- case IDENTIFIER:
- case UNDERSCORE:
- return parse_const_item (std::move (vis),
- std::move (outer_attrs));
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- return parse_inherent_impl_function_or_method (std::move (vis),
- std::move (
- outer_attrs));
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in some sort of const "
- "item in inherent impl",
- t->get_token_description ()));
+ invoc->add_semicolon ();
+ // Macro invocation with semicolon.
+ return invoc;
+ }
- lexer.skip_token (1); // TODO: is this right thing to do?
- return nullptr;
+ TokenId after_macro = lexer.peek_token ()->get_id ();
+
+ if (restrictions.allow_close_after_expr_stmt
+ && (after_macro == RIGHT_PAREN || after_macro == RIGHT_CURLY
+ || after_macro == RIGHT_SQUARE))
+ return invoc;
+
+ if (invoc->get_invoc_data ().get_delim_tok_tree ().get_delim_type ()
+ == AST::CURLY
+ && after_macro != DOT && after_macro != QUESTION_MARK)
+ {
+ rust_debug ("braced macro statement");
+ return invoc;
}
- default:
- add_error (
- Error (t->get_locus (),
- "unrecognised token %qs for item in inherent impl",
- t->get_token_description ()));
- // skip?
- return nullptr;
+
+ null_denotation = std::move (invoc);
+ }
+ else
+ {
+ null_denotation
+ = null_denotation_path (std::move (path), {}, restrictions);
}
+
+ expr = left_denotations (std::move (null_denotation), LBP_LOWEST,
+ std::move (outer_attrs), restrictions);
+ break;
}
- case ASYNC:
- case EXTERN_KW:
- case UNSAFE:
- case FN_KW:
- // function or method
- return parse_inherent_impl_function_or_method (
- AST::Visibility::create_private (), std::move (outer_attrs));
- case CONST:
- /* lookahead to resolve production - could be function/method or const
- * item */
- t = lexer.peek_token (1);
+ default:
+ restrictions.expr_can_be_stmt = true;
+ expr = parse_expr (std::move (outer_attrs), restrictions);
+ break;
+ }
- switch (t->get_id ())
+ if (expr == nullptr)
+ {
+ // expr is required, error
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse expr in expr statement");
+ add_error (std::move (error));
+
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ bool has_semi = false;
+
+ if (restrictions.consume_semi)
+ {
+ if (maybe_skip_token (SEMICOLON))
{
- case IDENTIFIER:
- case UNDERSCORE:
- return parse_const_item (AST::Visibility::create_private (),
- std::move (outer_attrs));
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- return parse_inherent_impl_function_or_method (
- AST::Visibility::create_private (), std::move (outer_attrs));
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in some sort of const item "
- "in inherent impl",
- t->get_token_description ()));
-
- lexer.skip_token (1); // TODO: is this right thing to do?
- return nullptr;
+ has_semi = true;
+ }
+ else if (expr->is_expr_without_block ())
+ {
+ if (restrictions.allow_close_after_expr_stmt)
+ {
+ TokenId id = lexer.peek_token ()->get_id ();
+ if (id != RIGHT_PAREN && id != RIGHT_CURLY && id != RIGHT_SQUARE)
+ {
+ expect_token (SEMICOLON);
+ return nullptr;
+ }
+ }
+ else
+ {
+ expect_token (SEMICOLON);
+ return nullptr;
+ }
}
- rust_unreachable ();
- default:
- add_error (Error (t->get_locus (),
- "unrecognised token %qs for item in inherent impl",
- t->get_token_description ()));
-
- // skip?
- return nullptr;
}
+
+ return std::unique_ptr<AST::ExprStmt> (
+ new AST::ExprStmt (std::move (expr), locus, has_semi));
}
-/* For internal use only by parse_inherent_impl_item() - splits giant method
- * into smaller ones and prevents duplication of logic. Strictly, this parses
- * a function or method item inside an inherent impl item block. */
-// TODO: make this a templated function with "return type" as type param -
-// InherentImplItem is this specialisation of the template while TraitImplItem
-// will be the other.
+// Parses a loop label used in loop expressions.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssociatedItem>
-Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (
- AST::Visibility vis, AST::AttrVec outer_attrs)
+tl::expected<AST::LoopLabel, ParseLoopLabelError>
+Parser<ManagedTokenSource>::parse_loop_label (const_TokenPtr tok)
{
- location_t locus = lexer.peek_token ()->get_locus ();
- // parse function or method qualifiers
- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+ // parse lifetime - if doesn't exist, assume no label
+ if (tok->get_id () != LIFETIME)
+ {
+ // not necessarily an error
+ return tl::unexpected<ParseLoopLabelError> (
+ ParseLoopLabelError::NOT_LOOP_LABEL);
+ }
+ /* FIXME: check for named lifetime requirement here? or check in semantic
+ * analysis phase? */
+ AST::Lifetime label = lifetime_from_token (tok);
- skip_token (FN_KW);
+ if (!skip_token (COLON))
+ {
+ // skip somewhere?
+ return tl::unexpected<ParseLoopLabelError> (
+ ParseLoopLabelError::MISSING_COLON);
+ }
- // parse function or method name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- return nullptr;
+ return tl::expected<AST::LoopLabel, ParseLoopLabelError> (
+ AST::LoopLabel (std::move (label), tok->get_locus ()));
+}
- Identifier ident{ident_tok};
+// Parses the "pattern" part of the match arm (the 'case x:' equivalent).
+template <typename ManagedTokenSource>
+AST::MatchArm
+Parser<ManagedTokenSource>::parse_match_arm ()
+{
+ // parse optional outer attributes
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
- // parse generic params
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
+ // DEBUG
+ rust_debug ("about to start parsing match arm patterns");
- if (!skip_token (LEFT_PAREN))
+ // break early if find right curly
+ if (lexer.peek_token ()->get_id () == RIGHT_CURLY)
{
- // skip after somewhere?
- return nullptr;
+ // not an error
+ return AST::MatchArm::create_error ();
}
- // now for function vs method disambiguation - method has opening "self"
- // param
- auto initial_param = parse_self_param ();
+ // parse match arm patterns - at least 1 is required
+ std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns
+ = parse_match_arm_patterns (RIGHT_CURLY);
+ if (match_arm_patterns.empty ())
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse any patterns in match arm");
+ add_error (std::move (error));
- if (!initial_param.has_value ()
- && initial_param.error () != ParseSelfError::NOT_SELF)
- return nullptr;
+ // skip somewhere?
+ return AST::MatchArm::create_error ();
+ }
- /* FIXME: ensure that self param doesn't accidently consume tokens for a
- * function one idea is to lookahead up to 4 tokens to see whether self is
- * one of them */
- bool is_method = false;
- if (initial_param.has_value ())
+ // DEBUG
+ rust_debug ("successfully parsed match arm patterns");
+
+ // parse match arm guard expr if it exists
+ std::unique_ptr<AST::Expr> guard_expr = nullptr;
+ if (lexer.peek_token ()->get_id () == IF)
{
- if ((*initial_param)->is_self ())
- is_method = true;
+ lexer.skip_token ();
- /* skip comma so function and method regular params can be parsed in
- * same way */
- if (lexer.peek_token ()->get_id () == COMMA)
- lexer.skip_token ();
+ guard_expr = parse_expr ();
+ if (guard_expr == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse guard expression in match arm");
+ add_error (std::move (error));
+
+ // skip somewhere?
+ return AST::MatchArm::create_error ();
+ }
}
- // parse trait function params
- std::vector<std::unique_ptr<AST::Param>> function_params
- = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
+ // DEBUG
+ rust_debug ("successfully parsed match arm");
- if (initial_param.has_value ())
- function_params.insert (function_params.begin (),
- std::move (*initial_param));
+ return AST::MatchArm (std::move (match_arm_patterns),
+ lexer.peek_token ()->get_locus (),
+ std::move (guard_expr), std::move (outer_attrs));
+}
- if (!skip_token (RIGHT_PAREN))
+/* Parses the patterns used in a match arm. End token id is the id of the
+ * token that would exist after the patterns are done (e.g. '}' for match
+ * expr, '=' for if let and while let). */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::Pattern>>
+Parser<ManagedTokenSource>::parse_match_arm_patterns (TokenId end_token_id)
+{
+ // skip optional leading '|'
+ if (lexer.peek_token ()->get_id () == PIPE)
+ lexer.skip_token ();
+ /* TODO: do I even need to store the result of this? can't be used.
+ * If semantically different, I need a wrapped "match arm patterns" object
+ * for this. */
+
+ std::vector<std::unique_ptr<AST::Pattern>> patterns;
+
+ // quick break out if end_token_id
+ if (lexer.peek_token ()->get_id () == end_token_id)
+ return patterns;
+
+ // parse required pattern - if doesn't exist, return empty
+ std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
+ if (initial_pattern == nullptr)
{
- skip_after_end_block ();
- return nullptr;
+ // FIXME: should this be an error?
+ return patterns;
}
+ patterns.push_back (std::move (initial_pattern));
- // parse return type (optional)
- std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
-
- // parse where clause (optional)
- AST::WhereClause where_clause = parse_where_clause ();
+ // DEBUG
+ rust_debug ("successfully parsed initial match arm pattern");
- tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- lexer.skip_token ();
- else
+ // parse new patterns as long as next char is '|'
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () == PIPE)
{
- auto result = parse_block_expr ();
+ // skip pipe token
+ lexer.skip_token ();
- if (result == nullptr)
+ // break if hit end token id
+ if (lexer.peek_token ()->get_id () == end_token_id)
+ break;
+
+ // parse pattern
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
{
- Error error (
- lexer.peek_token ()->get_locus (),
- "could not parse definition in inherent impl %s definition",
- is_method ? "method" : "function");
+ // this is an error
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in match arm patterns");
add_error (std::move (error));
- skip_after_end_block ();
- return nullptr;
+ // skip somewhere?
+ return {};
}
- body = std::move (result);
+
+ patterns.push_back (std::move (pattern));
+
+ t = lexer.peek_token ();
}
- return std::unique_ptr<AST::Function> (
- new AST::Function (std::move (ident), std::move (qualifiers),
- std::move (generic_params), std::move (function_params),
- std::move (return_type), std::move (where_clause),
- std::move (body), std::move (vis),
- std::move (outer_attrs), locus));
+ patterns.shrink_to_fit ();
+
+ return patterns;
}
-// Parses a single trait impl item (item inside a trait impl block).
+// Parses a single parameter used in a closure definition.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssociatedItem>
-Parser<ManagedTokenSource>::parse_trait_impl_item ()
+AST::ClosureParam
+Parser<ManagedTokenSource>::parse_closure_param ()
{
- // parse outer attributes (if they exist)
AST::AttrVec outer_attrs = parse_outer_attributes ();
- auto vis_res = parse_visibility ();
- if (!vis_res)
- return nullptr;
- auto visibility = vis_res.value ();
-
- // branch on next token:
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case SUPER:
- case SELF:
- case CRATE:
- case DOLLAR_SIGN:
- // these seem to be SimplePath tokens, so this is a macro invocation
- // semi
- return parse_macro_invocation_semi (std::move (outer_attrs));
- case IDENTIFIER:
- if (lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
- return parse_trait_impl_function_or_method (visibility,
- std::move (outer_attrs));
- else
- return parse_macro_invocation_semi (std::move (outer_attrs));
- case TYPE:
- return parse_type_alias (visibility, std::move (outer_attrs));
- case EXTERN_KW:
- case UNSAFE:
- case FN_KW:
- // function or method
- return parse_trait_impl_function_or_method (visibility,
- std::move (outer_attrs));
- case ASYNC:
- return parse_async_item (visibility, std::move (outer_attrs));
- case CONST:
- // lookahead to resolve production - could be function/method or const
- // item
- t = lexer.peek_token (1);
-
- switch (t->get_id ())
- {
- case IDENTIFIER:
- case UNDERSCORE:
- return parse_const_item (visibility, std::move (outer_attrs));
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- return parse_trait_impl_function_or_method (visibility,
- std::move (outer_attrs));
- default:
- add_error (Error (
- t->get_locus (),
- "unexpected token %qs in some sort of const item in trait impl",
- t->get_token_description ()));
-
- lexer.skip_token (1); // TODO: is this right thing to do?
- return nullptr;
- }
- rust_unreachable ();
- default:
- break;
- }
- add_error (Error (t->get_locus (),
- "unrecognised token %qs for item in trait impl",
- t->get_token_description ()));
-
- // skip?
- return nullptr;
-}
-
-/* For internal use only by parse_trait_impl_item() - splits giant method into
- * smaller ones and prevents duplication of logic. Strictly, this parses a
- * function or method item inside a trait impl item block. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssociatedItem>
-Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (
- AST::Visibility vis, AST::AttrVec outer_attrs)
-{
- // this shares virtually all logic with
- // parse_inherent_impl_function_or_method
- // - template?
- location_t locus = lexer.peek_token ()->get_locus ();
-
- auto is_default = false;
- auto t = lexer.peek_token ();
- if (t->get_id () == IDENTIFIER
- && t->get_str () == Values::WeakKeywords::DEFAULT)
- {
- is_default = true;
- lexer.skip_token ();
- }
-
- // parse function or method qualifiers
- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
-
- skip_token (FN_KW);
-
- // parse function or method name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- {
- return nullptr;
- }
- Identifier ident{ident_tok};
-
- // DEBUG:
- rust_debug (
- "about to start parsing generic params in trait impl function or method");
-
- // parse generic params
- std::vector<std::unique_ptr<AST::GenericParam>> generic_params
- = parse_generic_params_in_angles ();
-
- // DEBUG:
- rust_debug (
- "finished parsing generic params in trait impl function or method");
-
- if (!skip_token (LEFT_PAREN))
- {
- // skip after somewhere?
- return nullptr;
- }
-
- // now for function vs method disambiguation - method has opening "self"
- // param
- auto initial_param = parse_self_param ();
-
- if (!initial_param.has_value ()
- && initial_param.error () != ParseSelfError::NOT_SELF)
- return nullptr;
-
- // FIXME: ensure that self param doesn't accidently consume tokens for a
- // function
- bool is_method = false;
- if (initial_param.has_value ())
- {
- if ((*initial_param)->is_self ())
- is_method = true;
-
- // skip comma so function and method regular params can be parsed in
- // same way
- if (lexer.peek_token ()->get_id () == COMMA)
- {
- lexer.skip_token ();
- }
-
- // DEBUG
- rust_debug ("successfully parsed self param in method trait impl item");
- }
-
- // DEBUG
- rust_debug (
- "started to parse function params in function or method trait impl item");
-
- // parse trait function params (only if next token isn't right paren)
- std::vector<std::unique_ptr<AST::Param>> function_params;
- if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
- {
- function_params
- = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
-
- if (function_params.empty ())
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse function params in trait impl %s definition",
- is_method ? "method" : "function");
- add_error (std::move (error));
-
- skip_after_next_block ();
- return nullptr;
- }
- }
-
- if (initial_param.has_value ())
- function_params.insert (function_params.begin (),
- std::move (*initial_param));
-
- // DEBUG
- rust_debug ("successfully parsed function params in function or method "
- "trait impl item");
-
- if (!skip_token (RIGHT_PAREN))
- {
- skip_after_next_block ();
- return nullptr;
- }
-
- // parse return type (optional)
- std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
-
- // DEBUG
- rust_debug (
- "successfully parsed return type in function or method trait impl item");
-
- // parse where clause (optional)
- AST::WhereClause where_clause = parse_where_clause ();
-
- // DEBUG
- rust_debug (
- "successfully parsed where clause in function or method trait impl item");
-
- // parse function definition (in block) - semicolon not allowed
- tl::optional<std::unique_ptr<AST::BlockExpr>> body = tl::nullopt;
-
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- lexer.skip_token ();
- else
- {
- auto result = parse_block_expr ();
- if (result == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse definition in trait impl %s definition",
- is_method ? "method" : "function");
- add_error (std::move (error));
-
- skip_after_end_block ();
- return nullptr;
- }
- body = std::move (result);
- }
-
- return std::unique_ptr<AST::Function> (
- new AST::Function (std::move (ident), std::move (qualifiers),
- std::move (generic_params), std::move (function_params),
- std::move (return_type), std::move (where_clause),
- std::move (body), std::move (vis),
- std::move (outer_attrs), locus, is_default));
-}
-
-// Parses an extern block of declarations.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExternBlock>
-Parser<ManagedTokenSource>::parse_extern_block (AST::Visibility vis,
- AST::AttrVec outer_attrs)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (EXTERN_KW);
-
- // detect optional abi name
- std::string abi;
- const_TokenPtr next_tok = lexer.peek_token ();
- if (next_tok->get_id () == STRING_LITERAL)
- {
- lexer.skip_token ();
- abi = next_tok->get_str ();
- }
-
- if (!skip_token (LEFT_CURLY))
- {
- skip_after_end_block ();
- return nullptr;
- }
-
- AST::AttrVec inner_attrs = parse_inner_attributes ();
-
- // parse declarations inside extern block
- std::vector<std::unique_ptr<AST::ExternalItem>> extern_items;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
- {
- std::unique_ptr<AST::ExternalItem> extern_item = parse_external_item ();
-
- if (extern_item == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse external item despite not reaching "
- "end of extern block");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- extern_items.push_back (std::move (extern_item));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere
- return nullptr;
- }
-
- extern_items.shrink_to_fit ();
-
- return std::unique_ptr<AST::ExternBlock> (
- new AST::ExternBlock (std::move (abi), std::move (extern_items),
- std::move (vis), std::move (inner_attrs),
- std::move (outer_attrs), locus));
-}
-
-// Parses a single extern block item (static or function declaration).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExternalItem>
-Parser<ManagedTokenSource>::parse_external_item ()
-{
- // parse optional outer attributes
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- location_t locus = lexer.peek_token ()->get_locus ();
-
- // parse optional visibility
- auto vis_res = parse_visibility ();
- if (!vis_res)
- return nullptr;
- auto vis = vis_res.value ();
-
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case IDENTIFIER:
- return parse_macro_invocation_semi (outer_attrs);
- case STATIC_KW:
- {
- // parse extern static item
- lexer.skip_token ();
-
- // parse mut (optional)
- bool has_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
- {
- lexer.skip_token ();
- has_mut = true;
- }
-
- // parse identifier
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- {
- skip_after_semicolon ();
- return nullptr;
- }
- Identifier ident{ident_tok};
-
- if (!skip_token (COLON))
- {
- skip_after_semicolon ();
- return nullptr;
- }
-
- // parse type (required)
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type in external static item");
- add_error (std::move (error));
-
- skip_after_semicolon ();
- return nullptr;
- }
-
- if (!skip_token (SEMICOLON))
- {
- // skip after somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::ExternalStaticItem> (
- new AST::ExternalStaticItem (std::move (ident), std::move (type),
- has_mut, std::move (vis),
- std::move (outer_attrs), locus));
- }
- case FN_KW:
- return parse_function (std::move (vis), std::move (outer_attrs), true);
-
- case TYPE:
- return parse_external_type_item (std::move (vis),
- std::move (outer_attrs));
- default:
- // error
- add_error (
- Error (t->get_locus (),
- "unrecognised token %qs in extern block item declaration",
- t->get_token_description ()));
-
- skip_after_semicolon ();
- return nullptr;
- }
-}
-
-// Parses a statement (will further disambiguate any statement).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Stmt>
-Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
-{
- // quick exit for empty statement
- // FIXME: Can we have empty statements without semicolons? Just nothing?
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () == SEMICOLON)
- {
- lexer.skip_token ();
- return std::unique_ptr<AST::EmptyStmt> (
- new AST::EmptyStmt (t->get_locus ()));
- }
-
- // parse outer attributes
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // parsing this will be annoying because of the many different possibilities
- /* best may be just to copy paste in parse_item switch, and failing that try
- * to parse outer attributes, and then pass them in to either a let
- * statement or (fallback) expression statement. */
- // FIXME: think of a way to do this without such a large switch?
- t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case LET:
- // let statement
- return parse_let_stmt (std::move (outer_attrs), restrictions);
- case PUB:
- case MOD:
- case EXTERN_KW:
- case USE:
- case FN_KW:
- case TYPE:
- case STRUCT_KW:
- case ENUM_KW:
- case CONST:
- case STATIC_KW:
- case AUTO:
- case TRAIT:
- case IMPL:
- case MACRO:
- /* TODO: implement union keyword but not really because of
- * context-dependence crappy hack way to parse a union written below to
- * separate it from the good code. */
- // case UNION:
- case UNSAFE: // maybe - unsafe traits are a thing
- /* if any of these (should be all possible VisItem prefixes), parse a
- * VisItem can't parse item because would require reparsing outer
- * attributes */
- // may also be unsafe block
- if (lexer.peek_token (1)->get_id () == LEFT_CURLY)
- {
- return parse_expr_stmt (std::move (outer_attrs), restrictions);
- }
- else
- {
- return parse_vis_item (std::move (outer_attrs));
- }
- break;
- // crappy hack to do union "keyword"
- case IDENTIFIER:
- if (t->get_str () == Values::WeakKeywords::UNION
- && lexer.peek_token (1)->get_id () == IDENTIFIER)
- {
- return parse_vis_item (std::move (outer_attrs));
- // or should this go straight to parsing union?
- }
- else if (is_macro_rules_def (t))
- {
- // macro_rules! macro item
- return parse_macro_rules_def (std::move (outer_attrs));
- }
- gcc_fallthrough ();
- // TODO: find out how to disable gcc "implicit fallthrough" warning
- default:
- // fallback: expression statement
- return parse_expr_stmt (std::move (outer_attrs), restrictions);
- break;
- }
-}
-
-// Parses a let statement.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::LetStmt>
-Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
- ParseRestrictions restrictions)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (LET);
-
- // parse pattern (required)
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in let statement");
- add_error (std::move (error));
-
- skip_after_semicolon ();
- return nullptr;
- }
-
- // parse type declaration (optional)
- std::unique_ptr<AST::Type> type = nullptr;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- // must have a type declaration
- lexer.skip_token ();
-
- type = parse_type ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type in let statement");
- add_error (std::move (error));
-
- skip_after_semicolon ();
- return nullptr;
- }
- }
-
- // parse expression to set variable to (optional)
- std::unique_ptr<AST::Expr> expr = nullptr;
- if (lexer.peek_token ()->get_id () == EQUAL)
- {
- // must have an expression
- lexer.skip_token ();
-
- expr = parse_expr ();
- if (expr == nullptr)
- {
- skip_after_semicolon ();
- return nullptr;
- }
- }
-
- tl::optional<std::unique_ptr<AST::Expr>> else_expr = tl::nullopt;
- if (maybe_skip_token (ELSE))
- else_expr = parse_block_expr ();
-
- if (restrictions.consume_semi)
- {
- // `stmt` macro variables are parsed without a semicolon, but should be
- // parsed as a full statement when interpolated. This should be handled
- // by having the interpolated statement be distinguishable from normal
- // tokens, e.g. by NT tokens.
- if (restrictions.allow_close_after_expr_stmt)
- maybe_skip_token (SEMICOLON);
- else if (!skip_token (SEMICOLON))
- return nullptr;
- }
-
- return std::unique_ptr<AST::LetStmt> (
- new AST::LetStmt (std::move (pattern), std::move (expr), std::move (type),
- std::move (else_expr), std::move (outer_attrs), locus));
-}
-
-// Parses a type path.
-template <typename ManagedTokenSource>
-AST::TypePath
-Parser<ManagedTokenSource>::parse_type_path ()
-{
- bool has_opening_scope_resolution = false;
- location_t locus = lexer.peek_token ()->get_locus ();
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
- {
- has_opening_scope_resolution = true;
- lexer.skip_token ();
- }
-
- // create segment vector
- std::vector<std::unique_ptr<AST::TypePathSegment>> segments;
-
- // parse required initial segment
- std::unique_ptr<AST::TypePathSegment> initial_segment
- = parse_type_path_segment ();
- if (initial_segment == nullptr)
- {
- // skip after somewhere?
- // don't necessarily throw error but yeah
- return AST::TypePath::create_error ();
- }
- segments.push_back (std::move (initial_segment));
-
- // parse optional segments (as long as scope resolution operator exists)
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == SCOPE_RESOLUTION)
- {
- // skip scope resolution operator
- lexer.skip_token ();
-
- // parse the actual segment - it is an error if it doesn't exist now
- std::unique_ptr<AST::TypePathSegment> segment
- = parse_type_path_segment ();
- if (segment == nullptr)
- {
- // skip after somewhere?
- Error error (t->get_locus (), "could not parse type path segment");
- add_error (std::move (error));
-
- return AST::TypePath::create_error ();
- }
-
- segments.push_back (std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- segments.shrink_to_fit ();
-
- return AST::TypePath (std::move (segments), locus,
- has_opening_scope_resolution);
-}
-
-template <typename ManagedTokenSource>
-tl::optional<AST::GenericArg>
-Parser<ManagedTokenSource>::parse_generic_arg ()
-{
- auto tok = lexer.peek_token ();
- std::unique_ptr<AST::Expr> expr = nullptr;
-
- switch (tok->get_id ())
- {
- case IDENTIFIER:
- {
- // This is a bit of a weird situation: With an identifier token, we
- // could either have a valid type or a macro (FIXME: anything else?). So
- // we need one bit of lookahead to differentiate if this is really
- auto next_tok = lexer.peek_token (1);
- if (next_tok->get_id () == LEFT_ANGLE
- || next_tok->get_id () == SCOPE_RESOLUTION
- || next_tok->get_id () == EXCLAM)
- {
- auto type = parse_type ();
- if (type)
- return AST::GenericArg::create_type (std::move (type));
- else
- return tl::nullopt;
- }
- else if (next_tok->get_id () == COLON)
- {
- lexer.skip_token (); // skip ident
- lexer.skip_token (); // skip colon
-
- auto tok = lexer.peek_token ();
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
- = parse_type_param_bounds ();
-
- auto type = std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), tok->get_locus (),
- false));
- if (type)
- return AST::GenericArg::create_type (std::move (type));
- else
- return tl::nullopt;
- }
- lexer.skip_token ();
- return AST::GenericArg::create_ambiguous (tok->get_str (),
- tok->get_locus ());
- }
- case LEFT_CURLY:
- expr = parse_block_expr ();
- break;
- case MINUS:
- case STRING_LITERAL:
- case CHAR_LITERAL:
- case INT_LITERAL:
- case FLOAT_LITERAL:
- case TRUE_LITERAL:
- case FALSE_LITERAL:
- expr = parse_literal_expr ();
- break;
- // FIXME: Because of this, error reporting is garbage for const generic
- // parameter's default values
- default:
- {
- auto type = parse_type ();
- // FIXME: Find a better way to do this?
- if (type)
- return AST::GenericArg::create_type (std::move (type));
- else
- return tl::nullopt;
- }
- }
-
- if (!expr)
- return tl::nullopt;
-
- return AST::GenericArg::create_const (std::move (expr));
-}
-
-// Parses the generic arguments in each path segment.
-template <typename ManagedTokenSource>
-AST::GenericArgs
-Parser<ManagedTokenSource>::parse_path_generic_args ()
-{
- if (lexer.peek_token ()->get_id () == LEFT_SHIFT)
- lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
-
- if (!skip_token (LEFT_ANGLE))
- {
- // skip after somewhere?
- return AST::GenericArgs::create_empty ();
- }
-
- // We need to parse all lifetimes, then parse types and const generics in
- // any order.
-
- // try to parse lifetimes first
- std::vector<AST::Lifetime> lifetime_args;
-
- const_TokenPtr t = lexer.peek_token ();
- location_t locus = t->get_locus ();
- while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
- {
- auto lifetime = parse_lifetime (false);
- if (!lifetime)
- {
- // not necessarily an error
- break;
- }
-
- lifetime_args.push_back (std::move (lifetime.value ()));
-
- // if next token isn't comma, then it must be end of list
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- // skip comma
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- // try to parse types and const generics second
- std::vector<AST::GenericArg> generic_args;
-
- // TODO: think of better control structure
- t = lexer.peek_token ();
- while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
- {
- // FIXME: Is it fine to break if there is one binding? Can't there be
- // bindings in between types?
-
- // ensure not binding being parsed as type accidently
- if (t->get_id () == IDENTIFIER
- && lexer.peek_token (1)->get_id () == EQUAL)
- break;
-
- auto arg = parse_generic_arg ();
- if (arg)
- {
- generic_args.emplace_back (std::move (arg.value ()));
- }
-
- // FIXME: Do we need to break if we encounter an error?
-
- // if next token isn't comma, then it must be end of list
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- // skip comma
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- // try to parse bindings third
- std::vector<AST::GenericArgsBinding> binding_args;
-
- // TODO: think of better control structure
- t = lexer.peek_token ();
- while (!Parse::Utils::is_right_angle_tok (t->get_id ()))
- {
- AST::GenericArgsBinding binding = parse_generic_args_binding ();
- if (binding.is_error ())
- {
- // not necessarily an error
- break;
- }
-
- binding_args.push_back (std::move (binding));
-
- // if next token isn't comma, then it must be end of list
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- // skip comma
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- // skip any trailing commas
- if (lexer.peek_token ()->get_id () == COMMA)
- lexer.skip_token ();
-
- if (!skip_generics_right_angle ())
- return AST::GenericArgs::create_empty ();
-
- lifetime_args.shrink_to_fit ();
- generic_args.shrink_to_fit ();
- binding_args.shrink_to_fit ();
-
- return AST::GenericArgs (std::move (lifetime_args), std::move (generic_args),
- std::move (binding_args), locus);
-}
-
-// Parses a binding in a generic args path segment.
-template <typename ManagedTokenSource>
-AST::GenericArgsBinding
-Parser<ManagedTokenSource>::parse_generic_args_binding ()
-{
- const_TokenPtr ident_tok = lexer.peek_token ();
- if (ident_tok->get_id () != IDENTIFIER)
- {
- // allow non error-inducing use
- // skip somewhere?
- return AST::GenericArgsBinding::create_error ();
- }
- lexer.skip_token ();
- Identifier ident{ident_tok};
-
- if (!skip_token (EQUAL))
- {
- // skip after somewhere?
- return AST::GenericArgsBinding::create_error ();
- }
-
- // parse type (required)
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- // skip somewhere?
- return AST::GenericArgsBinding::create_error ();
- }
-
- return AST::GenericArgsBinding (std::move (ident), std::move (type),
- ident_tok->get_locus ());
-}
-
-/* Parses a single type path segment (not including opening scope resolution,
- * but includes any internal ones). Includes generic args or type path
- * functions too. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypePathSegment>
-Parser<ManagedTokenSource>::parse_type_path_segment ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- // parse ident segment part
- auto ident_segment_res = parse_path_ident_segment ();
- if (!ident_segment_res)
- {
- // not necessarily an error
- return nullptr;
- }
- auto ident_segment = ident_segment_res.value ();
-
- /* lookahead to determine if variants exist - only consume scope resolution
- * then */
- bool has_separating_scope_resolution = false;
- const_TokenPtr next = lexer.peek_token (1);
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION
- && (next->get_id () == LEFT_ANGLE || next->get_id () == LEFT_PAREN))
- {
- has_separating_scope_resolution = true;
- lexer.skip_token ();
- }
-
- // branch into variants on next token
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case LEFT_SHIFT:
- case LEFT_ANGLE:
- {
- // parse generic args
- AST::GenericArgs generic_args = parse_path_generic_args ();
-
- return std::unique_ptr<AST::TypePathSegmentGeneric> (
- new AST::TypePathSegmentGeneric (std::move (ident_segment),
- has_separating_scope_resolution,
- std::move (generic_args), locus));
- }
- case LEFT_PAREN:
- {
- // parse type path function
- AST::TypePathFunction type_path_function
- = parse_type_path_function (locus);
-
- if (type_path_function.is_error ())
- {
- // skip after somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::TypePathSegmentFunction> (
- new AST::TypePathSegmentFunction (std::move (ident_segment),
- has_separating_scope_resolution,
- std::move (type_path_function),
- locus));
- }
- default:
- // neither of them
- return std::unique_ptr<AST::TypePathSegment> (
- new AST::TypePathSegment (std::move (ident_segment),
- has_separating_scope_resolution, locus));
- }
- rust_unreachable ();
-}
-
-// Parses a function call representation inside a type path.
-template <typename ManagedTokenSource>
-AST::TypePathFunction
-Parser<ManagedTokenSource>::parse_type_path_function (location_t id_location)
-{
- if (!skip_token (LEFT_PAREN))
- {
- // skip somewhere?
- return AST::TypePathFunction::create_error ();
- }
-
- // parse function inputs
- std::vector<std::unique_ptr<AST::Type>> inputs;
-
- while (lexer.peek_token ()->get_id () != RIGHT_PAREN)
- {
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- /* this is an error as there should've been a ')' there if there
- * wasn't a type */
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse type in parameters of type path function");
- add_error (std::move (error));
-
- // skip somewhere?
- return AST::TypePathFunction::create_error ();
- }
-
- inputs.push_back (std::move (type));
-
- // skip commas, including trailing commas
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- lexer.skip_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip somewhere?
- return AST::TypePathFunction::create_error ();
- }
-
- // parse optional return type
- std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
-
- inputs.shrink_to_fit ();
- return AST::TypePathFunction (std::move (inputs), id_location,
- std::move (return_type));
-}
-
-// Parses a path inside an expression that allows generic arguments.
-template <typename ManagedTokenSource>
-AST::PathInExpression
-Parser<ManagedTokenSource>::parse_path_in_expression ()
-{
- location_t locus = UNKNOWN_LOCATION;
- bool has_opening_scope_resolution = false;
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
- {
- has_opening_scope_resolution = true;
-
- locus = lexer.peek_token ()->get_locus ();
-
- lexer.skip_token ();
- }
-
- // create segment vector
- std::vector<AST::PathExprSegment> segments;
-
- if (locus == UNKNOWN_LOCATION)
- {
- locus = lexer.peek_token ()->get_locus ();
- }
-
- // parse required initial segment
- AST::PathExprSegment initial_segment = parse_path_expr_segment ();
- if (initial_segment.is_error ())
- {
- // skip after somewhere?
- // don't necessarily throw error but yeah
- return AST::PathInExpression::create_error ();
- }
- segments.push_back (std::move (initial_segment));
-
- // parse optional segments (as long as scope resolution operator exists)
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == SCOPE_RESOLUTION)
- {
- // skip scope resolution operator
- lexer.skip_token ();
-
- // parse the actual segment - it is an error if it doesn't exist now
- AST::PathExprSegment segment = parse_path_expr_segment ();
- if (segment.is_error ())
- {
- // skip after somewhere?
- Error error (t->get_locus (),
- "could not parse path expression segment");
- add_error (std::move (error));
-
- return AST::PathInExpression::create_error ();
- }
-
- segments.push_back (std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- segments.shrink_to_fit ();
-
- return AST::PathInExpression (std::move (segments), {}, locus,
- has_opening_scope_resolution);
-}
-
-/* Parses a single path in expression path segment (including generic
- * arguments). */
-template <typename ManagedTokenSource>
-AST::PathExprSegment
-Parser<ManagedTokenSource>::parse_path_expr_segment ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- // parse ident segment
- auto ident_result = parse_path_ident_segment ();
- if (!ident_result)
- {
- // not necessarily an error?
- return AST::PathExprSegment::create_error ();
- }
- auto ident = ident_result.value ();
-
- // parse generic args (and turbofish), if they exist
- /* use lookahead to determine if they actually exist (don't want to
- * accidently parse over next ident segment) */
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION
- && (lexer.peek_token (1)->get_id () == LEFT_ANGLE
- || lexer.peek_token (1)->get_id () == LEFT_SHIFT))
- {
- // skip scope resolution
- lexer.skip_token ();
-
- // Let parse_path_generic_args split "<<" tokens
- AST::GenericArgs generic_args = parse_path_generic_args ();
-
- return AST::PathExprSegment (std::move (ident), locus,
- std::move (generic_args));
- }
-
- // return a generic parameter-less expr segment if not found
- return AST::PathExprSegment (std::move (ident), locus);
-}
-
-/* Parses a fully qualified path in expression (i.e. a pattern). FIXME does
- * not parse outer attrs. */
-template <typename ManagedTokenSource>
-AST::QualifiedPathInExpression
-Parser<ManagedTokenSource>::parse_qualified_path_in_expression (
- location_t pratt_parsed_loc)
-{
- /* Note: the Rust grammar is defined in such a way that it is impossible to
- * determine whether a prospective qualified path is a
- * QualifiedPathInExpression or QualifiedPathInType in all cases by the
- * rules themselves (the only possible difference is a TypePathSegment with
- * function, and lookahead to find this is too difficult). However, as this
- * is a pattern and QualifiedPathInType is a type, I believe it that their
- * construction will not be confused (due to rules regarding patterns vs
- * types).
- * As such, this function will not attempt to minimise errors created by
- * their confusion. */
-
- // parse the qualified path type (required)
- AST::QualifiedPathType qual_path_type
- = parse_qualified_path_type (pratt_parsed_loc);
- if (qual_path_type.is_error ())
- {
- // TODO: should this create a parse error?
- return AST::QualifiedPathInExpression::create_error ();
- }
- location_t locus = qual_path_type.get_locus ();
-
- // parse path segments
- std::vector<AST::PathExprSegment> segments;
-
- // parse initial required segment
- if (!expect_token (SCOPE_RESOLUTION))
- {
- // skip after somewhere?
-
- return AST::QualifiedPathInExpression::create_error ();
- }
- AST::PathExprSegment initial_segment = parse_path_expr_segment ();
- if (initial_segment.is_error ())
- {
- // skip after somewhere?
- Error error (lexer.peek_token ()->get_locus (),
- "required initial path expression segment in "
- "qualified path in expression could not be parsed");
- add_error (std::move (error));
-
- return AST::QualifiedPathInExpression::create_error ();
- }
- segments.push_back (std::move (initial_segment));
-
- // parse optional segments (as long as scope resolution operator exists)
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == SCOPE_RESOLUTION)
- {
- // skip scope resolution operator
- lexer.skip_token ();
-
- // parse the actual segment - it is an error if it doesn't exist now
- AST::PathExprSegment segment = parse_path_expr_segment ();
- if (segment.is_error ())
- {
- // skip after somewhere?
- Error error (t->get_locus (),
- "could not parse path expression segment in qualified "
- "path in expression");
- add_error (std::move (error));
-
- return AST::QualifiedPathInExpression::create_error ();
- }
-
- segments.push_back (std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- segments.shrink_to_fit ();
-
- // FIXME: outer attr parsing
- return AST::QualifiedPathInExpression (std::move (qual_path_type),
- std::move (segments), {}, locus);
-}
-
-// Parses the type syntactical construction at the start of a qualified path.
-template <typename ManagedTokenSource>
-AST::QualifiedPathType
-Parser<ManagedTokenSource>::parse_qualified_path_type (
- location_t pratt_parsed_loc)
-{
- location_t locus = pratt_parsed_loc;
- /* TODO: should this actually be error? is there anywhere where this could
- * be valid? */
- if (locus == UNKNOWN_LOCATION)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- if (lexer.peek_token ()->get_id () == LEFT_SHIFT)
- lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
-
- // skip after somewhere?
- if (!skip_token (LEFT_ANGLE))
- return AST::QualifiedPathType::create_error ();
- }
-
- // parse type (required)
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse type in qualified path type");
- add_error (std::move (error));
-
- // skip somewhere?
- return AST::QualifiedPathType::create_error ();
- }
-
- // parse optional as clause
- AST::TypePath as_type_path = AST::TypePath::create_error ();
- if (lexer.peek_token ()->get_id () == AS)
- {
- lexer.skip_token ();
-
- // parse type path, which is required now
- as_type_path = parse_type_path ();
- if (as_type_path.is_error ())
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "could not parse type path in as clause in qualified path type");
- add_error (std::move (error));
-
- // skip somewhere?
- return AST::QualifiedPathType::create_error ();
- }
- }
-
- /* NOTE: should actually be a right-angle token, so
- * skip_generics_right_angle shouldn't be required */
- if (!skip_token (RIGHT_ANGLE))
- {
- // skip after somewhere?
- return AST::QualifiedPathType::create_error ();
- }
-
- return AST::QualifiedPathType (std::move (type), locus,
- std::move (as_type_path));
-}
-
-// Parses a fully qualified path in type (i.e. a type).
-template <typename ManagedTokenSource>
-AST::QualifiedPathInType
-Parser<ManagedTokenSource>::parse_qualified_path_in_type ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- // parse the qualified path type (required)
- AST::QualifiedPathType qual_path_type = parse_qualified_path_type ();
- if (qual_path_type.is_error ())
- {
- // TODO: should this create a parse error?
- return AST::QualifiedPathInType::create_error ();
- }
-
- // parse initial required segment
- if (!expect_token (SCOPE_RESOLUTION))
- {
- // skip after somewhere?
-
- return AST::QualifiedPathInType::create_error ();
- }
- std::unique_ptr<AST::TypePathSegment> initial_segment
- = parse_type_path_segment ();
- if (initial_segment == nullptr)
- {
- // skip after somewhere?
- Error error (lexer.peek_token ()->get_locus (),
- "required initial type path segment in qualified path in "
- "type could not be parsed");
- add_error (std::move (error));
-
- return AST::QualifiedPathInType::create_error ();
- }
-
- // parse optional segments (as long as scope resolution operator exists)
- std::vector<std::unique_ptr<AST::TypePathSegment>> segments;
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == SCOPE_RESOLUTION)
- {
- // skip scope resolution operator
- lexer.skip_token ();
-
- // parse the actual segment - it is an error if it doesn't exist now
- std::unique_ptr<AST::TypePathSegment> segment
- = parse_type_path_segment ();
- if (segment == nullptr)
- {
- // skip after somewhere?
- Error error (
- t->get_locus (),
- "could not parse type path segment in qualified path in type");
- add_error (std::move (error));
-
- return AST::QualifiedPathInType::create_error ();
- }
-
- segments.push_back (std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- segments.shrink_to_fit ();
-
- return AST::QualifiedPathInType (std::move (qual_path_type),
- std::move (initial_segment),
- std::move (segments), locus);
-}
-
-// Parses a self param. Also handles self param not existing.
-template <typename ManagedTokenSource>
-tl::expected<std::unique_ptr<AST::Param>, ParseSelfError>
-Parser<ManagedTokenSource>::parse_self_param ()
-{
- bool has_reference = false;
- AST::Lifetime lifetime = AST::Lifetime::elided ();
-
- location_t locus = lexer.peek_token ()->get_locus ();
-
- // TODO: Feels off, find a better way to clearly express this
- std::vector<std::vector<TokenId>> ptrs
- = {{ASTERISK, SELF} /* *self */,
- {ASTERISK, CONST, SELF} /* *const self */,
- {ASTERISK, MUT, SELF} /* *mut self */};
-
- for (auto &s : ptrs)
- {
- size_t i = 0;
- for (i = 0; i < s.size (); i++)
- if (lexer.peek_token (i)->get_id () != s[i])
- break;
- if (i == s.size ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "cannot pass %<self%> by raw pointer");
- return tl::make_unexpected (ParseSelfError::SELF_PTR);
- }
- }
-
- // Trying to find those patterns:
- //
- // &'lifetime mut self
- // &'lifetime self
- // & mut self
- // & self
- // mut self
- // self
- //
- // If not found, it is probably a function, exit and let function parsing
- // handle it.
- bool is_self = false;
- for (size_t i = 0; i < 5; i++)
- if (lexer.peek_token (i)->get_id () == SELF)
- is_self = true;
-
- if (!is_self)
- return tl::make_unexpected (ParseSelfError::NOT_SELF);
-
- // test if self is a reference parameter
- if (lexer.peek_token ()->get_id () == AMP)
- {
- has_reference = true;
- lexer.skip_token ();
-
- // now test whether it has a lifetime
- if (lexer.peek_token ()->get_id () == LIFETIME)
- {
- // something went wrong somehow
- if (auto parsed_lifetime = parse_lifetime (true))
- {
- lifetime = parsed_lifetime.value ();
- }
- else
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime in self param");
- add_error (std::move (error));
-
- // skip after somewhere?
- return tl::make_unexpected (ParseSelfError::PARSING);
- }
- }
- }
-
- // test for mut
- bool has_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
- {
- has_mut = true;
- lexer.skip_token ();
- }
-
- // skip self token
- const_TokenPtr self_tok = lexer.peek_token ();
- if (self_tok->get_id () != SELF)
- {
- // skip after somewhere?
- return tl::make_unexpected (ParseSelfError::NOT_SELF);
- }
- lexer.skip_token ();
-
- // parse optional type
- std::unique_ptr<AST::Type> type = nullptr;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
-
- // type is now required
- type = parse_type ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "could not parse type in self param");
- add_error (std::move (error));
-
- // skip after somewhere?
- return tl::make_unexpected (ParseSelfError::PARSING);
- }
- }
-
- // ensure that cannot have both type and reference
- if (type != nullptr && has_reference)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "cannot have both a reference and a type specified in a self param");
- add_error (std::move (error));
-
- // skip after somewhere?
- return tl::make_unexpected (ParseSelfError::PARSING);
- }
-
- if (has_reference)
- {
- return std::make_unique<AST::SelfParam> (std::move (lifetime), has_mut,
- locus);
- }
- else
- {
- // note that type may be nullptr here and that's fine
- return std::make_unique<AST::SelfParam> (std::move (type), has_mut,
- locus);
- }
-}
-
-/* Parses an expression or macro statement. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Stmt>
-Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
- ParseRestrictions restrictions)
-{
- location_t locus = lexer.peek_token ()->get_locus ();
-
- std::unique_ptr<AST::Expr> expr;
-
- switch (lexer.peek_token ()->get_id ())
- {
- case IDENTIFIER:
- case CRATE:
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case DOLLAR_SIGN:
- case SCOPE_RESOLUTION:
- {
- AST::PathInExpression path = parse_path_in_expression ();
- std::unique_ptr<AST::Expr> null_denotation;
-
- if (lexer.peek_token ()->get_id () == EXCLAM)
- {
- std::unique_ptr<AST::MacroInvocation> invoc
- = parse_macro_invocation_partial (std::move (path),
- std::move (outer_attrs));
-
- if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
- {
- invoc->add_semicolon ();
- // Macro invocation with semicolon.
- return invoc;
- }
-
- TokenId after_macro = lexer.peek_token ()->get_id ();
-
- if (restrictions.allow_close_after_expr_stmt
- && (after_macro == RIGHT_PAREN || after_macro == RIGHT_CURLY
- || after_macro == RIGHT_SQUARE))
- return invoc;
-
- if (invoc->get_invoc_data ().get_delim_tok_tree ().get_delim_type ()
- == AST::CURLY
- && after_macro != DOT && after_macro != QUESTION_MARK)
- {
- rust_debug ("braced macro statement");
- return invoc;
- }
-
- null_denotation = std::move (invoc);
- }
- else
- {
- null_denotation
- = null_denotation_path (std::move (path), {}, restrictions);
- }
-
- expr = left_denotations (std::move (null_denotation), LBP_LOWEST,
- std::move (outer_attrs), restrictions);
- break;
- }
- default:
- restrictions.expr_can_be_stmt = true;
- expr = parse_expr (std::move (outer_attrs), restrictions);
- break;
- }
-
- if (expr == nullptr)
- {
- // expr is required, error
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse expr in expr statement");
- add_error (std::move (error));
-
- skip_after_semicolon ();
- return nullptr;
- }
-
- bool has_semi = false;
-
- if (restrictions.consume_semi)
- {
- if (maybe_skip_token (SEMICOLON))
- {
- has_semi = true;
- }
- else if (expr->is_expr_without_block ())
- {
- if (restrictions.allow_close_after_expr_stmt)
- {
- TokenId id = lexer.peek_token ()->get_id ();
- if (id != RIGHT_PAREN && id != RIGHT_CURLY && id != RIGHT_SQUARE)
- {
- expect_token (SEMICOLON);
- return nullptr;
- }
- }
- else
- {
- expect_token (SEMICOLON);
- return nullptr;
- }
- }
- }
-
- return std::unique_ptr<AST::ExprStmt> (
- new AST::ExprStmt (std::move (expr), locus, has_semi));
-}
-
-// Parses a block expression, including the curly braces at start and end.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::BlockExpr>
-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 nullptr;
- }
- }
-
- 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)
- {
- ExprOrStmt expr_or_stmt = parse_stmt_or_expr ();
- if (expr_or_stmt.is_error ())
- {
- skip_after_end_block ();
- return nullptr;
- }
-
- 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);
- break;
- }
- }
-
- location_t end_locus = t->get_locus ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- Error error (t->get_locus (),
- "error may be from having an expression (as opposed to "
- "statement) in the body of the function but not last");
- add_error (std::move (error));
-
- skip_after_end_block ();
- return nullptr;
- }
-
- // 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, AnonConstError>
-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 (AnonConstError::InvalidSizeExpr);
-
- return AST::AnonConst (std::move (expr), 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>
-std::unique_ptr<AST::ConstBlock>
-Parser<ManagedTokenSource>::parse_const_block_expr (AST::AttrVec outer_attrs,
- location_t locus)
-{
- auto block = parse_block_expr ();
-
- if (!block)
- {
- add_error (Error (locus, "failed to parse inner block in const block"));
- skip_after_end_block ();
-
- return nullptr;
- }
-
- 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>
-std::unique_ptr<AST::GroupedExpr>
-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
- std::unique_ptr<AST::Expr> expr_in_parens = parse_expr ();
- if (expr_in_parens == nullptr)
- {
- // skip after somewhere?
- // error?
- return nullptr;
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip after somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::GroupedExpr> (
- new AST::GroupedExpr (std::move (expr_in_parens), std::move (inner_attrs),
- std::move (outer_attrs), locus));
-}
-
-// Parses a closure expression (closure definition).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ClosureExpr>
-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 nullptr;
- }
-
- // 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 nullptr;
- }
-
- // parse block expr, which is required
- std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
- if (block == nullptr)
- {
- // error
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse block expr in closure");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::ClosureExprInnerTyped> (
- new AST::ClosureExprInnerTyped (std::move (type), std::move (block),
- std::move (params), locus, has_move,
- std::move (outer_attrs)));
- }
- else
- {
- // must be expr-only closure
-
- // parse expr, which is required
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse expression in closure");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::ClosureExprInner> (
- new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
- has_move, std::move (outer_attrs)));
- }
-}
-
-// Parses a literal token (to literal expression).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::LiteralExpr>
-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 nullptr;
- }
-
- // 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>
-std::unique_ptr<AST::BoxExpr>
-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;
-
- std::unique_ptr<AST::Expr> expr = parse_expr (AST::AttrVec (), restrictions);
-
- return std::unique_ptr<AST::BoxExpr> (
- new AST::BoxExpr (std::move (expr), std::move (outer_attrs), locus));
-}
-
-// Parses a return expression (including any expression to return).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ReturnExpr>
-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;
- std::unique_ptr<AST::Expr> returned_expr
- = parse_expr (AST::AttrVec (), restrictions);
-
- return std::unique_ptr<AST::ReturnExpr> (
- new AST::ReturnExpr (std::move (returned_expr), std::move (outer_attrs),
- locus));
-}
-
-// Parses a try expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TryExpr>
-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);
- }
-
- std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
-
- if (!block_expr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse try block expression");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::TryExpr> (
- new AST::TryExpr (std::move (block_expr), std::move (outer_attrs), locus));
-}
-
-/* Parses a break expression (including any label to break to AND any return
- * expression). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::BreakExpr>
-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;
- std::unique_ptr<AST::Expr> return_expr
- = parse_expr (AST::AttrVec (), restrictions);
-
- return std::unique_ptr<AST::BreakExpr> (
- new AST::BreakExpr (std::move (label), std::move (return_expr),
- std::move (outer_attrs), locus));
-}
-
-// 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::unique_ptr<AST::ContinueExpr> (
- new AST::ContinueExpr (std::move (label), std::move (outer_attrs), locus));
-}
-
-// Parses a loop label used in loop expressions.
-template <typename ManagedTokenSource>
-tl::expected<AST::LoopLabel, ParseLoopLabelError>
-Parser<ManagedTokenSource>::parse_loop_label (const_TokenPtr tok)
-{
- // parse lifetime - if doesn't exist, assume no label
- if (tok->get_id () != LIFETIME)
- {
- // not necessarily an error
- return tl::unexpected<ParseLoopLabelError> (
- ParseLoopLabelError::NOT_LOOP_LABEL);
- }
- /* FIXME: check for named lifetime requirement here? or check in semantic
- * analysis phase? */
- AST::Lifetime label = lifetime_from_token (tok);
-
- if (!skip_token (COLON))
- {
- // skip somewhere?
- return tl::unexpected<ParseLoopLabelError> (
- ParseLoopLabelError::MISSING_COLON);
- }
-
- return tl::expected<AST::LoopLabel, ParseLoopLabelError> (
- AST::LoopLabel (std::move (label), tok->get_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>
-std::unique_ptr<AST::IfExpr>
-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 nullptr;
- }
- }
-
- // 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 nullptr;
- }
-
- /* parse required condition expr - HACK to prevent struct expr from being
- * parsed */
- ParseRestrictions no_struct_expr;
- no_struct_expr.can_be_struct_expr = false;
- std::unique_ptr<AST::Expr> condition = parse_expr ({}, no_struct_expr);
- if (condition == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse condition expression in if expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- // parse required block expr
- std::unique_ptr<AST::BlockExpr> if_body = parse_block_expr ();
- if (if_body == nullptr)
- return nullptr;
-
- // 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), std::move (if_body),
- 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)
- std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr ();
- if (else_body == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::IfExprConseqElse> (
- new AST::IfExprConseqElse (std::move (condition),
- std::move (if_body),
- std::move (else_body),
- 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)
- std::unique_ptr<AST::IfLetExpr> if_let_expr
- = parse_if_let_expr ();
- if (if_let_expr == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::IfExprConseqElse> (
- new AST::IfExprConseqElse (std::move (condition),
- std::move (if_body),
- std::move (if_let_expr),
- std::move (outer_attrs), locus));
- }
- else
- {
- // parse if expr (required)
- std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
- if (if_expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse (else) if expression after "
- "if expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::IfExprConseqElse> (
- new AST::IfExprConseqElse (std::move (condition),
- std::move (if_body),
- std::move (if_expr),
- 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 nullptr;
- }
- }
-}
-
-/* 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>
-std::unique_ptr<AST::IfLetExpr>
-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 nullptr;
- }
- }
-
- // 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 nullptr;
- }
- lexer.skip_token ();
-
- // parse match arm patterns (which are required)
- std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns
- = parse_match_arm_patterns (EQUAL);
- if (match_arm_patterns.empty ())
- {
- 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 nullptr;
- }
-
- if (!skip_token (EQUAL))
- {
- // skip somewhere?
- return nullptr;
- }
-
- // parse expression (required) - HACK to prevent struct expr being parsed
- ParseRestrictions no_struct_expr;
- no_struct_expr.can_be_struct_expr = false;
- std::unique_ptr<AST::Expr> scrutinee_expr = parse_expr ({}, no_struct_expr);
- if (scrutinee_expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse scrutinee expression in if let expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- /* TODO: check for expression not being a struct expression or lazy boolean
- * expression here? or actually probably in semantic analysis. */
-
- // parse block expression (required)
- std::unique_ptr<AST::BlockExpr> if_let_body = parse_block_expr ();
- if (if_let_body == nullptr)
- {
- 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 nullptr;
- }
-
- // 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_patterns),
- std::move (scrutinee_expr), std::move (if_let_body),
- 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)
- std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr ();
- if (else_body == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::IfLetExprConseqElse> (
- new AST::IfLetExprConseqElse (std::move (match_arm_patterns),
- std::move (scrutinee_expr),
- std::move (if_let_body),
- std::move (else_body),
- 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)
- std::unique_ptr<AST::IfLetExpr> if_let_expr
- = parse_if_let_expr ();
- if (if_let_expr == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::IfLetExprConseqElse> (
- new AST::IfLetExprConseqElse (
- std::move (match_arm_patterns), std::move (scrutinee_expr),
- std::move (if_let_body), std::move (if_let_expr),
- std::move (outer_attrs), locus));
- }
- else
- {
- // parse if expr (required)
- std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
- if (if_expr == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::IfLetExprConseqElse> (
- new AST::IfLetExprConseqElse (
- std::move (match_arm_patterns), std::move (scrutinee_expr),
- std::move (if_let_body), std::move (if_expr),
- 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 nullptr;
- }
- }
-}
-
-/* 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>
-std::unique_ptr<AST::LoopExpr>
-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 nullptr;
- }
- }
- else
- {
- if (label)
- locus = label->get_locus ();
- }
-
- // parse loop body, which is required
- std::unique_ptr<AST::BlockExpr> loop_body = parse_block_expr ();
- if (loop_body == nullptr)
- return nullptr;
-
- return std::unique_ptr<AST::LoopExpr> (
- new AST::LoopExpr (std::move (loop_body), 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>
-std::unique_ptr<AST::WhileLoopExpr>
-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 nullptr;
- }
- }
- 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 nullptr;
- }
-
- // parse loop predicate (required) with HACK to prevent struct expr parsing
- ParseRestrictions no_struct_expr;
- no_struct_expr.can_be_struct_expr = false;
- std::unique_ptr<AST::Expr> predicate = parse_expr ({}, no_struct_expr);
- if (predicate == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse predicate expression in while loop");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- /* TODO: check that it isn't struct expression here? actually, probably in
- * semantic analysis */
-
- // parse loop body (required)
- std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
- if (body == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::WhileLoopExpr> (
- new AST::WhileLoopExpr (std::move (predicate), std::move (body), 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>
-std::unique_ptr<AST::WhileLetLoopExpr>
-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 nullptr;
- }
- // as this token is definitely let now, save the computation of comparison
- lexer.skip_token ();
-
- // parse predicate patterns
- std::vector<std::unique_ptr<AST::Pattern>> predicate_patterns
- = parse_match_arm_patterns (EQUAL);
- // ensure that there is at least 1 pattern
- if (predicate_patterns.empty ())
- {
- Error error (lexer.peek_token ()->get_locus (),
- "should be at least 1 pattern");
- add_error (std::move (error));
- return nullptr;
- }
-
- if (!skip_token (EQUAL))
- {
- // skip somewhere?
- return nullptr;
- }
-
- /* parse predicate expression, which is required (and HACK to prevent struct
- * expr) */
- ParseRestrictions no_struct_expr;
- no_struct_expr.can_be_struct_expr = false;
- std::unique_ptr<AST::Expr> predicate_expr = parse_expr ({}, no_struct_expr);
- if (predicate_expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse predicate expression in while let loop");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- /* TODO: ensure that struct expression is not parsed? Actually, probably in
- * semantic analysis. */
-
- // parse loop body, which is required
- std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
- if (body == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::WhileLetLoopExpr> (new AST::WhileLetLoopExpr (
- std::move (predicate_patterns), std::move (predicate_expr),
- std::move (body), 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>
-std::unique_ptr<AST::ForLoopExpr>
-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 == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse iterator pattern in for loop");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- if (!skip_token (IN))
- {
- // skip somewhere?
- return nullptr;
- }
-
- /* parse iterator expression, which is required - also HACK to prevent
- * struct expr */
- ParseRestrictions no_struct_expr;
- no_struct_expr.can_be_struct_expr = false;
- std::unique_ptr<AST::Expr> expr = parse_expr ({}, no_struct_expr);
- if (expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse iterator expression in for loop");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- // TODO: check to ensure this isn't struct expr? Or in semantic analysis.
-
- // parse loop body, which is required
- std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
- if (body == nullptr)
- {
- 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 nullptr;
- }
-
- return std::unique_ptr<AST::ForLoopExpr> (
- new AST::ForLoopExpr (std::move (pattern), std::move (expr),
- std::move (body), locus, std::move (label),
- std::move (outer_attrs)));
-}
-
-// Parses a loop expression with label (any kind of loop - disambiguates).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
-Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
- AST::AttrVec outer_attrs)
-{
- /* 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 (tok->get_id () != LIFETIME)
- {
- 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));
-
- // skip?
- return nullptr;
- }
-
- // parse loop label (required)
- // TODO: Convert this return type to tl::expected instead of tl::optional
- auto parsed_label = parse_loop_label (tok);
- if (!parsed_label)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse loop label in labelled loop expr");
- add_error (std::move (error));
-
- // skip?
- return nullptr;
- }
-
- 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 nullptr;
- }
-}
-
-// Parses a match expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::MatchExpr>
-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;
- std::unique_ptr<AST::Expr> scrutinee = parse_expr ({}, no_struct_expr);
- if (scrutinee == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse scrutinee expression in match expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- /* TODO: check for scrutinee expr not being struct expr? or do so in
- * semantic analysis */
-
- if (!skip_token (LEFT_CURLY))
- {
- // skip somewhere?
- return nullptr;
- }
-
- // 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 nullptr;
- }
-
- if (!skip_token (MATCH_ARROW))
- {
- // skip after somewhere?
- // TODO is returning here a good idea? or is break better?
- return nullptr;
- }
-
- ParseRestrictions restrictions;
- restrictions.expr_can_be_stmt = true;
-
- std::unique_ptr<AST::Expr> expr = parse_expr ({}, restrictions);
-
- if (expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse expr in match arm in match expr");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- bool is_expr_without_block = expr->is_expr_without_block ();
-
- match_arms.push_back (AST::MatchCase (std::move (arm), std::move (expr)));
-
- // 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 nullptr;
- }
- else
- {
- // otherwise, must be final case, so fine
- break;
- }
- }
- lexer.skip_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere?
- return nullptr;
- }
-
- match_arms.shrink_to_fit ();
-
- return std::unique_ptr<AST::MatchExpr> (
- new AST::MatchExpr (std::move (scrutinee), std::move (match_arms),
- std::move (inner_attrs), std::move (outer_attrs),
- locus));
-}
-
-// Parses the "pattern" part of the match arm (the 'case x:' equivalent).
-template <typename ManagedTokenSource>
-AST::MatchArm
-Parser<ManagedTokenSource>::parse_match_arm ()
-{
- // parse optional outer attributes
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // DEBUG
- rust_debug ("about to start parsing match arm patterns");
-
- // break early if find right curly
- if (lexer.peek_token ()->get_id () == RIGHT_CURLY)
- {
- // not an error
- return AST::MatchArm::create_error ();
- }
-
- // parse match arm patterns - at least 1 is required
- std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns
- = parse_match_arm_patterns (RIGHT_CURLY);
- if (match_arm_patterns.empty ())
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse any patterns in match arm");
- add_error (std::move (error));
-
- // skip somewhere?
- return AST::MatchArm::create_error ();
- }
-
- // DEBUG
- rust_debug ("successfully parsed match arm patterns");
-
- // parse match arm guard expr if it exists
- std::unique_ptr<AST::Expr> guard_expr = nullptr;
- if (lexer.peek_token ()->get_id () == IF)
- {
- lexer.skip_token ();
-
- guard_expr = parse_expr ();
- if (guard_expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse guard expression in match arm");
- add_error (std::move (error));
-
- // skip somewhere?
- return AST::MatchArm::create_error ();
- }
- }
-
- // DEBUG
- rust_debug ("successfully parsed match arm");
-
- return AST::MatchArm (std::move (match_arm_patterns),
- lexer.peek_token ()->get_locus (),
- std::move (guard_expr), std::move (outer_attrs));
-}
-
-/* Parses the patterns used in a match arm. End token id is the id of the
- * token that would exist after the patterns are done (e.g. '}' for match
- * expr, '=' for if let and while let). */
-template <typename ManagedTokenSource>
-std::vector<std::unique_ptr<AST::Pattern>>
-Parser<ManagedTokenSource>::parse_match_arm_patterns (TokenId end_token_id)
-{
- // skip optional leading '|'
- if (lexer.peek_token ()->get_id () == PIPE)
- lexer.skip_token ();
- /* TODO: do I even need to store the result of this? can't be used.
- * If semantically different, I need a wrapped "match arm patterns" object
- * for this. */
-
- std::vector<std::unique_ptr<AST::Pattern>> patterns;
-
- // quick break out if end_token_id
- if (lexer.peek_token ()->get_id () == end_token_id)
- return patterns;
-
- // parse required pattern - if doesn't exist, return empty
- std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
- if (initial_pattern == nullptr)
- {
- // FIXME: should this be an error?
- return patterns;
- }
- patterns.push_back (std::move (initial_pattern));
-
- // DEBUG
- rust_debug ("successfully parsed initial match arm pattern");
-
- // parse new patterns as long as next char is '|'
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == PIPE)
- {
- // skip pipe token
- lexer.skip_token ();
-
- // break if hit end token id
- if (lexer.peek_token ()->get_id () == end_token_id)
- break;
-
- // parse pattern
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- // this is an error
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in match arm patterns");
- add_error (std::move (error));
-
- // skip somewhere?
- return {};
- }
-
- patterns.push_back (std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- patterns.shrink_to_fit ();
-
- return patterns;
-}
-
-// Parses an async block expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::AsyncBlockExpr>
-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)
- std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
- if (block_expr == nullptr)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse block expression of async block expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::AsyncBlockExpr> (
- new AST::AsyncBlockExpr (std::move (block_expr), has_move,
- std::move (outer_attrs), locus));
-}
-
-// Parses an unsafe block expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::UnsafeBlockExpr>
-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)
- std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
- if (block_expr == nullptr)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse block expression of unsafe block expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::UnsafeBlockExpr> (
- new AST::UnsafeBlockExpr (std::move (block_expr), std::move (outer_attrs),
- locus));
-}
-
-// Parses an array definition expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArrayExpr>
-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
- std::unique_ptr<AST::Expr> initial_expr = parse_expr ();
- if (initial_expr == nullptr)
- {
- 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 nullptr;
- }
-
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- {
- // copy array elems
- lexer.skip_token ();
-
- // parse copy amount expression (required)
- std::unique_ptr<AST::Expr> copy_amount = parse_expr ();
- if (copy_amount == nullptr)
- {
- 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 nullptr;
- }
-
- skip_token (RIGHT_SQUARE);
-
- std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
- new AST::ArrayElemsCopied (std::move (initial_expr),
- std::move (copy_amount), 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));
- 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));
-
- 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)
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse element in array expression");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- exprs.push_back (std::move (expr));
-
- 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 nullptr;
- }
- }
-}
-
-// Parses a single parameter used in a closure definition.
-template <typename ManagedTokenSource>
-AST::ClosureParam
-Parser<ManagedTokenSource>::parse_closure_param ()
-{
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // parse pattern (which is required)
- std::unique_ptr<AST::Pattern> pattern = parse_pattern_no_alt ();
- if (pattern == nullptr)
- {
- // not necessarily an error
- return AST::ClosureParam::create_error ();
- }
-
- // parse optional type of param
- std::unique_ptr<AST::Type> type = nullptr;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
-
- // parse type, which is now required
- type = parse_type ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type in closure parameter");
- add_error (std::move (error));
-
- // skip somewhere?
- return AST::ClosureParam::create_error ();
- }
- }
-
- location_t loc = pattern->get_locus ();
- return AST::ClosureParam (std::move (pattern), loc, std::move (type),
- std::move (outer_attrs));
-}
-
-// Parses a grouped or tuple expression (disambiguates).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExprWithoutBlock>
-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)
- std::unique_ptr<AST::Expr> first_expr = parse_expr ();
- if (first_expr == nullptr)
- {
- 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 nullptr;
- }
-
- // 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), 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));
-
- // 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
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse expr in tuple expr");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- exprs.push_back (std::move (expr));
-
- 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 nullptr;
- }
-}
-
-// Parses a type (will further disambiguate any type).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Type>
-Parser<ManagedTokenSource>::parse_type (bool save_errors)
-{
- /* rules for all types:
- * NeverType: '!'
- * SliceType: '[' Type ']'
- * InferredType: '_'
- * MacroInvocation: SimplePath '!' DelimTokenTree
- * ParenthesisedType: '(' Type ')'
- * ImplTraitType: 'impl' TypeParamBounds
- * TypeParamBounds (not type) TypeParamBound ( '+' TypeParamBound )* '+'?
- * TypeParamBound Lifetime | TraitBound
- * ImplTraitTypeOneBound: 'impl' TraitBound
- * TraitObjectType: 'dyn'? TypeParamBounds
- * TraitObjectTypeOneBound: 'dyn'? TraitBound
- * TraitBound '?'? ForLifetimes? TypePath | '(' '?'?
- * ForLifetimes? TypePath ')' BareFunctionType: ForLifetimes?
- * FunctionQualifiers 'fn' etc. ForLifetimes (not type) 'for' '<'
- * LifetimeParams '>' FunctionQualifiers ( 'async' | 'const' )?
- * 'unsafe'?
- * ('extern' abi?)? QualifiedPathInType: '<' Type ( 'as' TypePath )? '>'
- * (
- * '::' TypePathSegment )+ TypePath: '::'? TypePathSegment (
- * '::' TypePathSegment)* ArrayType: '[' Type ';' Expr ']'
- * ReferenceType: '&' Lifetime? 'mut'? TypeNoBounds
- * RawPointerType: '*' ( 'mut' | 'const' ) TypeNoBounds
- * TupleType: '(' Type etc. - regular tuple stuff. Also
- * regular tuple vs parenthesised precedence
- *
- * Disambiguate between macro and type path via type path being parsed, and
- * then if '!' found, convert type path to simple path for macro. Usual
- * disambiguation for tuple vs parenthesised. For ImplTraitType and
- * TraitObjectType individual disambiguations, they seem more like "special
- * cases", so probably just try to parse the more general ImplTraitType or
- * TraitObjectType and return OneBound versions if they satisfy those
- * criteria. */
-
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case EXCLAM:
- // never type - can't be macro as no path beforehand
- lexer.skip_token ();
- return std::unique_ptr<AST::NeverType> (
- new AST::NeverType (t->get_locus ()));
- case LEFT_SQUARE:
- // slice type or array type - requires further disambiguation
- return parse_slice_or_array_type ();
- case LEFT_SHIFT:
- case LEFT_ANGLE:
- {
- // qualified path in type
- AST::QualifiedPathInType path = parse_qualified_path_in_type ();
- if (path.is_error ())
- {
- if (save_errors)
- {
- Error error (t->get_locus (),
- "failed to parse qualified path in type");
- add_error (std::move (error));
- }
-
- return nullptr;
- }
- return std::unique_ptr<AST::QualifiedPathInType> (
- new AST::QualifiedPathInType (std::move (path)));
- }
- case UNDERSCORE:
- // inferred type
- lexer.skip_token ();
- return std::unique_ptr<AST::InferredType> (
- new AST::InferredType (t->get_locus ()));
- case ASTERISK:
- // raw pointer type
- return parse_raw_pointer_type ();
- case AMP: // does this also include AMP_AMP?
- case LOGICAL_AND:
- // reference type
- return parse_reference_type ();
- case LIFETIME:
- {
- /* probably a lifetime bound, so probably type param bounds in
- * TraitObjectType */
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
- = parse_type_param_bounds ();
-
- return std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), t->get_locus (),
- false));
- }
- case IDENTIFIER:
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case DOLLAR_SIGN:
- case SCOPE_RESOLUTION:
- {
- // macro invocation or type path - requires further disambiguation.
- /* for parsing path component of each rule, perhaps parse it as a
- * typepath and attempt conversion to simplepath if a trailing '!' is
- * found */
- /* Type path also includes TraitObjectTypeOneBound BUT if it starts
- * with it, it is exactly the same as a TypePath syntactically, so
- * this is a syntactical ambiguity. As such, the parser will parse it
- * as a TypePath. This, however, does not prevent TraitObjectType from
- * starting with a typepath. */
-
- // parse path as type path
- AST::TypePath path = parse_type_path ();
- if (path.is_error ())
- {
- if (save_errors)
- {
- Error error (t->get_locus (),
- "failed to parse path as first component of type");
- add_error (std::move (error));
- }
-
- return nullptr;
- }
- location_t locus = path.get_locus ();
-
- // branch on next token
- t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case EXCLAM:
- {
- // macro invocation
- // convert to simple path
- AST::SimplePath macro_path = path.as_simple_path ();
- if (macro_path.is_empty ())
- {
- if (save_errors)
- {
- Error error (t->get_locus (),
- "failed to parse simple path in macro "
- "invocation (for type)");
- add_error (std::move (error));
- }
-
- return nullptr;
- }
-
- lexer.skip_token ();
-
- auto tok_tree = parse_delim_token_tree ();
- if (!tok_tree)
- return nullptr;
-
- return AST::MacroInvocation::Regular (
- AST::MacroInvocData (std::move (macro_path),
- std::move (tok_tree.value ())),
- {}, locus);
- }
- case PLUS:
- {
- // type param bounds
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
-
- // convert type path to trait bound
- std::unique_ptr<AST::TraitBound> path_bound (
- new AST::TraitBound (std::move (path), locus, false, false));
- bounds.push_back (std::move (path_bound));
-
- /* parse rest of bounds - FIXME: better way to find when to stop
- * parsing */
- while (t->get_id () == PLUS)
- {
- lexer.skip_token ();
-
- // parse bound if it exists - if not, assume end of sequence
- std::unique_ptr<AST::TypeParamBound> bound
- = parse_type_param_bound ();
- if (bound == nullptr)
- {
- break;
- }
- bounds.push_back (std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), locus, false));
- }
- default:
- // assume that this is a type path and not an error
- return std::unique_ptr<AST::TypePath> (
- new AST::TypePath (std::move (path)));
- }
- }
- case LEFT_PAREN:
- /* tuple type or parenthesised type - requires further disambiguation
- * (the usual). ok apparently can be a parenthesised TraitBound too, so
- * could be TraitObjectTypeOneBound or TraitObjectType */
- return parse_paren_prefixed_type ();
- case FOR:
- // TraitObjectTypeOneBound or BareFunctionType
- return parse_for_prefixed_type ();
- case ASYNC:
- case CONST:
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- // bare function type (with no for lifetimes)
- return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
- case IMPL:
- lexer.skip_token ();
- if (lexer.peek_token ()->get_id () == LIFETIME)
- {
- /* cannot be one bound because lifetime prevents it from being
- * traitbound */
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
- = parse_type_param_bounds ();
-
- return std::unique_ptr<AST::ImplTraitType> (
- new AST::ImplTraitType (std::move (bounds), t->get_locus ()));
- }
- else
- {
- // should be trait bound, so parse trait bound
- std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
- if (initial_bound == nullptr)
- {
- if (save_errors)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse ImplTraitType initial bound");
- add_error (std::move (error));
- }
-
- return nullptr;
- }
-
- location_t locus = t->get_locus ();
-
- // short cut if next token isn't '+'
- t = lexer.peek_token ();
- if (t->get_id () != PLUS)
- {
- return std::unique_ptr<AST::ImplTraitTypeOneBound> (
- new AST::ImplTraitTypeOneBound (std::move (initial_bound),
- locus));
- }
-
- // parse additional type param bounds
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
- bounds.push_back (std::move (initial_bound));
- while (t->get_id () == PLUS)
- {
- lexer.skip_token ();
-
- // parse bound if it exists
- std::unique_ptr<AST::TypeParamBound> bound
- = parse_type_param_bound ();
- if (bound == nullptr)
- {
- // not an error as trailing plus may exist
- break;
- }
- bounds.push_back (std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return std::unique_ptr<AST::ImplTraitType> (
- new AST::ImplTraitType (std::move (bounds), locus));
- }
- case DYN:
- case QUESTION_MARK:
- {
- // either TraitObjectType or TraitObjectTypeOneBound
- bool has_dyn = false;
- if (t->get_id () == DYN)
- {
- lexer.skip_token ();
- has_dyn = true;
- }
-
- if (lexer.peek_token ()->get_id () == LIFETIME)
- {
- /* cannot be one bound because lifetime prevents it from being
- * traitbound */
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
- = parse_type_param_bounds ();
-
- return std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), t->get_locus (),
- has_dyn));
- }
- else
- {
- // should be trait bound, so parse trait bound
- std::unique_ptr<AST::TraitBound> initial_bound
- = parse_trait_bound ();
- if (initial_bound == nullptr)
- {
- if (save_errors)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse TraitObjectType initial bound");
- add_error (std::move (error));
- }
-
- return nullptr;
- }
-
- // short cut if next token isn't '+'
- t = lexer.peek_token ();
- if (t->get_id () != PLUS)
- {
- // convert trait bound to value object
- AST::TraitBound value_bound (*initial_bound);
-
- // DEBUG: removed as unique ptr, so should auto delete
- // delete initial_bound;
-
- return std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (std::move (value_bound),
- t->get_locus (), has_dyn));
- }
-
- // parse additional type param bounds
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
- bounds.push_back (std::move (initial_bound));
- while (t->get_id () == PLUS)
- {
- lexer.skip_token ();
-
- // parse bound if it exists
- std::unique_ptr<AST::TypeParamBound> bound
- = parse_type_param_bound ();
- if (bound == nullptr)
- {
- // not an error as trailing plus may exist
- break;
- }
- bounds.push_back (std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), t->get_locus (),
- has_dyn));
- }
- }
- default:
- if (save_errors)
- add_error (Error (t->get_locus (), "unrecognised token %qs in type",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-/* Parses a type that has '(' as its first character. Returns a tuple type,
- * parenthesised type, TraitObjectTypeOneBound, or TraitObjectType depending
- * on following characters. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Type>
-Parser<ManagedTokenSource>::parse_paren_prefixed_type ()
-{
- /* NOTE: Syntactical ambiguity of a parenthesised trait bound is considered
- * a trait bound, not a parenthesised type, so that it can still be used in
- * type param bounds. */
-
- /* NOTE: this implementation is really shit but I couldn't think of a better
- * one. It requires essentially breaking polymorphism and downcasting via
- * virtual method abuse, as it was copied from the rustc implementation (in
- * which types are reified due to tagged union), after a more OOP attempt by
- * me failed. */
- location_t left_delim_locus = lexer.peek_token ()->get_locus ();
-
- // skip left delim
- lexer.skip_token ();
- /* while next token isn't close delim, parse comma-separated types, saving
- * whether trailing comma happens */
- const_TokenPtr t = lexer.peek_token ();
- bool trailing_comma = true;
- std::vector<std::unique_ptr<AST::Type>> types;
-
- while (t->get_id () != RIGHT_PAREN)
- {
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse type inside parentheses (probably "
- "tuple or parenthesised)");
- add_error (std::move (error));
-
- return nullptr;
- }
- types.push_back (std::move (type));
-
- t = lexer.peek_token ();
- if (t->get_id () != COMMA)
- {
- trailing_comma = false;
- break;
- }
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- return nullptr;
- }
-
- // if only one type and no trailing comma, then not a tuple type
- if (types.size () == 1 && !trailing_comma)
- {
- // must be a TraitObjectType (with more than one bound)
- if (lexer.peek_token ()->get_id () == PLUS)
- {
- // create type param bounds vector
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
-
- // HACK: convert type to traitbound and add to bounds
- std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
- std::unique_ptr<AST::TraitBound> converted_bound (
- released_ptr->to_trait_bound (true));
- if (converted_bound == nullptr)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to hackily converted parsed type to trait bound");
- add_error (std::move (error));
-
- return nullptr;
- }
- bounds.push_back (std::move (converted_bound));
-
- t = lexer.peek_token ();
- while (t->get_id () == PLUS)
- {
- lexer.skip_token ();
-
- // attempt to parse typeparambound
- std::unique_ptr<AST::TypeParamBound> bound
- = parse_type_param_bound ();
- if (bound == nullptr)
- {
- // not an error if null
- break;
- }
- bounds.push_back (std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), left_delim_locus,
- false));
- }
- else
- {
- // release vector pointer
- std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
- /* HACK: attempt to convert to trait bound. if fails, parenthesised
- * type */
- std::unique_ptr<AST::TraitBound> converted_bound (
- released_ptr->to_trait_bound (true));
- if (converted_bound == nullptr)
- {
- // parenthesised type
- return std::unique_ptr<AST::ParenthesisedType> (
- new AST::ParenthesisedType (std::move (released_ptr),
- left_delim_locus));
- }
- else
- {
- // trait object type (one bound)
-
- // get value semantics trait bound
- AST::TraitBound value_bound (*converted_bound);
-
- return std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (value_bound,
- left_delim_locus));
- }
- }
- }
- else
- {
- return std::unique_ptr<AST::TupleType> (
- new AST::TupleType (std::move (types), left_delim_locus));
- }
- /* TODO: ensure that this ensures that dynamic dispatch for traits is not
- * lost somehow */
-}
-
-/* Parses a type that has 'for' as its first character. This means it has a
- * "for lifetimes", so returns either a BareFunctionType, TraitObjectType, or
- * TraitObjectTypeOneBound depending on following characters. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Type>
-Parser<ManagedTokenSource>::parse_for_prefixed_type ()
-{
- location_t for_locus = lexer.peek_token ()->get_locus ();
- // parse for lifetimes in type
- std::vector<AST::LifetimeParam> for_lifetimes = parse_for_lifetimes ();
-
- // branch on next token - either function or a trait type
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case ASYNC:
- case CONST:
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- return parse_bare_function_type (std::move (for_lifetimes));
- case SCOPE_RESOLUTION:
- case IDENTIFIER:
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case DOLLAR_SIGN:
- {
- // path, so trait type
-
- // parse type path to finish parsing trait bound
- AST::TypePath path = parse_type_path ();
-
- t = lexer.peek_token ();
- if (t->get_id () != PLUS)
- {
- // must be one-bound trait type
- // create trait bound value object
- AST::TraitBound bound (std::move (path), for_locus, false, false,
- std::move (for_lifetimes));
-
- return std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (std::move (bound), for_locus));
- }
-
- /* more than one bound trait type (or at least parsed as it - could be
- * trailing '+') create trait bound pointer and bounds */
- std::unique_ptr<AST::TraitBound> initial_bound (
- new AST::TraitBound (std::move (path), for_locus, false, false,
- std::move (for_lifetimes)));
- std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
- bounds.push_back (std::move (initial_bound));
-
- while (t->get_id () == PLUS)
- {
- lexer.skip_token ();
-
- // parse type param bound if it exists
- std::unique_ptr<AST::TypeParamBound> bound
- = parse_type_param_bound ();
- if (bound == nullptr)
- {
- // not an error - e.g. trailing plus
- return nullptr;
- }
- bounds.push_back (std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (std::move (bounds), for_locus, false));
- }
- default:
- // error
- add_error (Error (t->get_locus (),
- "unrecognised token %qs in bare function type or trait "
- "object type or trait object type one bound",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-// Parses a maybe named param used in bare function types.
-template <typename ManagedTokenSource>
-AST::MaybeNamedParam
-Parser<ManagedTokenSource>::parse_maybe_named_param (AST::AttrVec outer_attrs)
-{
- /* Basically guess that param is named if first token is identifier or
- * underscore and second token is semicolon. This should probably have no
- * exceptions. rustc uses backtracking to parse these, but at the time of
- * writing gccrs has no backtracking capabilities. */
- const_TokenPtr current = lexer.peek_token ();
- const_TokenPtr next = lexer.peek_token (1);
-
- Identifier name;
- AST::MaybeNamedParam::ParamKind kind = AST::MaybeNamedParam::UNNAMED;
-
- if (current->get_id () == IDENTIFIER && next->get_id () == COLON)
- {
- // named param
- name = {current};
- kind = AST::MaybeNamedParam::IDENTIFIER;
- lexer.skip_token (1);
- }
- else if (current->get_id () == UNDERSCORE && next->get_id () == COLON)
- {
- // wildcard param
- name = {Values::Keywords::UNDERSCORE, current->get_locus ()};
- kind = AST::MaybeNamedParam::WILDCARD;
- lexer.skip_token (1);
- }
-
- // parse type (required)
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse type in maybe named param");
- add_error (std::move (error));
-
- return AST::MaybeNamedParam::create_error ();
- }
-
- return AST::MaybeNamedParam (std::move (name), kind, std::move (type),
- std::move (outer_attrs), current->get_locus ());
-}
-
-/* Parses a bare function type (with the given for lifetimes for convenience -
- * does not parse them itself). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::BareFunctionType>
-Parser<ManagedTokenSource>::parse_bare_function_type (
- std::vector<AST::LifetimeParam> for_lifetimes)
-{
- // TODO: pass in for lifetime location as param
- location_t best_try_locus = lexer.peek_token ()->get_locus ();
-
- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
-
- if (!skip_token (FN_KW))
- return nullptr;
-
- if (!skip_token (LEFT_PAREN))
- return nullptr;
-
- // parse function params, if they exist
- std::vector<AST::MaybeNamedParam> params;
- bool is_variadic = false;
- AST::AttrVec variadic_attrs;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_PAREN)
- {
- AST::AttrVec temp_attrs = parse_outer_attributes ();
-
- if (lexer.peek_token ()->get_id () == ELLIPSIS)
- {
- lexer.skip_token ();
- is_variadic = true;
- variadic_attrs = std::move (temp_attrs);
-
- t = lexer.peek_token ();
-
- if (t->get_id () != RIGHT_PAREN)
- {
- Error error (t->get_locus (),
- "expected right parentheses after variadic in maybe "
- "named function "
- "parameters, found %qs",
- t->get_token_description ());
- add_error (std::move (error));
-
- return nullptr;
- }
-
- break;
- }
-
- AST::MaybeNamedParam param
- = parse_maybe_named_param (std::move (temp_attrs));
- if (param.is_error ())
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse maybe named param in bare function type");
- add_error (std::move (error));
-
- return nullptr;
- }
- params.push_back (std::move (param));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- return nullptr;
-
- // bare function return type, if exists
- std::unique_ptr<AST::TypeNoBounds> return_type = nullptr;
- if (lexer.peek_token ()->get_id () == RETURN_TYPE)
- {
- lexer.skip_token ();
-
- // parse required TypeNoBounds
- return_type = parse_type_no_bounds ();
- if (return_type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse return type (type no bounds) in bare "
- "function type");
- add_error (std::move (error));
-
- return nullptr;
- }
- }
-
- return std::unique_ptr<AST::BareFunctionType> (
- new AST::BareFunctionType (std::move (for_lifetimes),
- std::move (qualifiers), std::move (params),
- is_variadic, std::move (variadic_attrs),
- std::move (return_type), best_try_locus));
-}
-
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ReferenceType>
-Parser<ManagedTokenSource>::parse_reference_type_inner (location_t locus)
-{
- // parse optional lifetime
- AST::Lifetime lifetime = AST::Lifetime::elided ();
- if (lexer.peek_token ()->get_id () == LIFETIME)
- {
- auto parsed_lifetime = parse_lifetime (true);
- if (parsed_lifetime)
- {
- lifetime = parsed_lifetime.value ();
- }
- else
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime in reference type");
- add_error (std::move (error));
-
- return nullptr;
- }
- }
-
- bool is_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
- {
- lexer.skip_token ();
- is_mut = true;
- }
-
- // parse type no bounds, which is required
- std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse referenced type in reference type");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::ReferenceType> (
- new AST::ReferenceType (is_mut, std::move (type), locus,
- std::move (lifetime)));
-}
-
-// Parses a reference type (mutable or immutable, with given lifetime).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ReferenceType>
-Parser<ManagedTokenSource>::parse_reference_type ()
-{
- auto t = lexer.peek_token ();
- auto locus = t->get_locus ();
-
- switch (t->get_id ())
- {
- case AMP:
- skip_token (AMP);
- return parse_reference_type_inner (locus);
- case LOGICAL_AND:
- skip_token (LOGICAL_AND);
- return std::unique_ptr<AST::ReferenceType> (
- new AST::ReferenceType (false, parse_reference_type_inner (locus),
- locus));
- default:
- rust_unreachable ();
- }
-}
-
-// Parses a raw (unsafe) pointer type.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::RawPointerType>
-Parser<ManagedTokenSource>::parse_raw_pointer_type ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (ASTERISK);
-
- AST::RawPointerType::PointerType kind = AST::RawPointerType::CONST;
-
- // branch on next token for pointer kind info
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case MUT:
- kind = AST::RawPointerType::MUT;
- lexer.skip_token ();
- break;
- case CONST:
- kind = AST::RawPointerType::CONST;
- lexer.skip_token ();
- break;
- default:
- add_error (Error (t->get_locus (),
- "unrecognised token %qs in raw pointer type",
- t->get_token_description ()));
-
- return nullptr;
- }
-
- // parse type no bounds (required)
- std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
- if (type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pointed type of raw pointer type");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::RawPointerType> (
- new AST::RawPointerType (kind, std::move (type), locus));
-}
-
-/* Parses a slice or array type, depending on following arguments (as
- * lookahead is not possible). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeNoBounds>
-Parser<ManagedTokenSource>::parse_slice_or_array_type ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
- skip_token (LEFT_SQUARE);
-
- // parse inner type (required)
- std::unique_ptr<AST::Type> inner_type = parse_type ();
- if (inner_type == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse inner type in slice or array type");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- // branch on next token
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case RIGHT_SQUARE:
- // slice type
- lexer.skip_token ();
-
- return std::unique_ptr<AST::SliceType> (
- new AST::SliceType (std::move (inner_type), locus));
- case SEMICOLON:
- {
- // array type
- lexer.skip_token ();
-
- // parse required array size expression
- auto size = parse_anon_const ();
-
- if (!size)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse size expression in array type");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- if (!skip_token (RIGHT_SQUARE))
- {
- return nullptr;
- }
-
- return std::unique_ptr<AST::ArrayType> (
- new AST::ArrayType (std::move (inner_type), std::move (*size),
- locus));
- }
- default:
- // error
- add_error (
- Error (t->get_locus (),
- "unrecognised token %qs in slice or array type after inner type",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-// Parses a type, taking into account type boundary disambiguation.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeNoBounds>
-Parser<ManagedTokenSource>::parse_type_no_bounds ()
-{
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case EXCLAM:
- // never type - can't be macro as no path beforehand
- lexer.skip_token ();
- return std::unique_ptr<AST::NeverType> (
- new AST::NeverType (t->get_locus ()));
- case LEFT_SQUARE:
- // slice type or array type - requires further disambiguation
- return parse_slice_or_array_type ();
- case LEFT_SHIFT:
- case LEFT_ANGLE:
- {
- // qualified path in type
- AST::QualifiedPathInType path = parse_qualified_path_in_type ();
- if (path.is_error ())
- {
- Error error (t->get_locus (),
- "failed to parse qualified path in type");
- add_error (std::move (error));
-
- return nullptr;
- }
- return std::unique_ptr<AST::QualifiedPathInType> (
- new AST::QualifiedPathInType (std::move (path)));
- }
- case UNDERSCORE:
- // inferred type
- lexer.skip_token ();
- return std::unique_ptr<AST::InferredType> (
- new AST::InferredType (t->get_locus ()));
- case ASTERISK:
- // raw pointer type
- return parse_raw_pointer_type ();
- case AMP: // does this also include AMP_AMP? Yes! Which is... LOGICAL_AND?
- case LOGICAL_AND:
- // reference type
- return parse_reference_type ();
- case LIFETIME:
- /* probably a lifetime bound, so probably type param bounds in
- * TraitObjectType. this is not allowed, but detection here for error
- * message */
- add_error (Error (t->get_locus (),
- "lifetime bounds (i.e. in type param bounds, in "
- "TraitObjectType) are not allowed as TypeNoBounds"));
-
- return nullptr;
- case IDENTIFIER:
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case DOLLAR_SIGN:
- case SCOPE_RESOLUTION:
- {
- // macro invocation or type path - requires further disambiguation.
- /* for parsing path component of each rule, perhaps parse it as a
- * typepath and attempt conversion to simplepath if a trailing '!' is
- * found */
- /* Type path also includes TraitObjectTypeOneBound BUT if it starts
- * with it, it is exactly the same as a TypePath syntactically, so
- * this is a syntactical ambiguity. As such, the parser will parse it
- * as a TypePath. This, however, does not prevent TraitObjectType from
- * starting with a typepath. */
-
- // parse path as type path
- AST::TypePath path = parse_type_path ();
- if (path.is_error ())
- {
- Error error (
- t->get_locus (),
- "failed to parse path as first component of type no bounds");
- add_error (std::move (error));
-
- return nullptr;
- }
- location_t locus = path.get_locus ();
-
- // branch on next token
- t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case EXCLAM:
- {
- // macro invocation
- // convert to simple path
- AST::SimplePath macro_path = path.as_simple_path ();
- if (macro_path.is_empty ())
- {
- Error error (t->get_locus (),
- "failed to parse simple path in macro "
- "invocation (for type)");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- lexer.skip_token ();
-
- auto tok_tree = parse_delim_token_tree ();
- if (!tok_tree)
- return nullptr;
-
- return AST::MacroInvocation::Regular (
- AST::MacroInvocData (std::move (macro_path),
- std::move (tok_tree.value ())),
- {}, locus);
- }
- default:
- // assume that this is a type path and not an error
- return std::unique_ptr<AST::TypePath> (
- new AST::TypePath (std::move (path)));
- }
- }
- case LEFT_PAREN:
- /* tuple type or parenthesised type - requires further disambiguation
- * (the usual). ok apparently can be a parenthesised TraitBound too, so
- * could be TraitObjectTypeOneBound */
- return parse_paren_prefixed_type_no_bounds ();
- case FOR:
- case ASYNC:
- case CONST:
- case UNSAFE:
- case EXTERN_KW:
- case FN_KW:
- // bare function type (with no for lifetimes)
- return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
- case IMPL:
- lexer.skip_token ();
- if (lexer.peek_token ()->get_id () == LIFETIME)
- {
- /* cannot be one bound because lifetime prevents it from being
- * traitbound not allowed as type no bounds, only here for error
- * message */
- Error error (
- lexer.peek_token ()->get_locus (),
- "lifetime (probably lifetime bound, in type param "
- "bounds, in ImplTraitType) is not allowed in TypeNoBounds");
- add_error (std::move (error));
-
- return nullptr;
- }
- else
- {
- // should be trait bound, so parse trait bound
- std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
- if (initial_bound == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse ImplTraitTypeOneBound bound");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- location_t locus = t->get_locus ();
-
- // ensure not a trait with multiple bounds
- t = lexer.peek_token ();
- if (t->get_id () == PLUS)
- {
- Error error (t->get_locus (),
- "plus after trait bound means an ImplTraitType, "
- "which is not allowed as a TypeNoBounds");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::ImplTraitTypeOneBound> (
- new AST::ImplTraitTypeOneBound (std::move (initial_bound), locus));
- }
- case DYN:
- case QUESTION_MARK:
- {
- // either TraitObjectTypeOneBound
- bool has_dyn = false;
- if (t->get_id () == DYN)
- {
- lexer.skip_token ();
- has_dyn = true;
- }
-
- if (lexer.peek_token ()->get_id () == LIFETIME)
- {
- /* means that cannot be TraitObjectTypeOneBound - so here for
- * error message */
- Error error (lexer.peek_token ()->get_locus (),
- "lifetime as bound in TraitObjectTypeOneBound "
- "is not allowed, so cannot be TypeNoBounds");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- // should be trait bound, so parse trait bound
- std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
- if (initial_bound == nullptr)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse TraitObjectTypeOneBound initial bound");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- location_t locus = t->get_locus ();
-
- // detect error with plus as next token
- t = lexer.peek_token ();
- if (t->get_id () == PLUS)
- {
- Error error (t->get_locus (),
- "plus after trait bound means a TraitObjectType, "
- "which is not allowed as a TypeNoBounds");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- // convert trait bound to value object
- AST::TraitBound value_bound (*initial_bound);
-
- return std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (std::move (value_bound), locus,
- has_dyn));
- }
- default:
- add_error (Error (t->get_locus (),
- "unrecognised token %qs in type no bounds",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-// Parses a type no bounds beginning with '('.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeNoBounds>
-Parser<ManagedTokenSource>::parse_paren_prefixed_type_no_bounds ()
-{
- /* NOTE: this could probably be parsed without the HACK solution of
- * parse_paren_prefixed_type, but I was lazy. So FIXME for future.*/
-
- /* NOTE: again, syntactical ambiguity of a parenthesised trait bound is
- * considered a trait bound, not a parenthesised type, so that it can still
- * be used in type param bounds. */
-
- location_t left_paren_locus = lexer.peek_token ()->get_locus ();
-
- // skip left delim
- lexer.skip_token ();
- /* while next token isn't close delim, parse comma-separated types, saving
- * whether trailing comma happens */
- const_TokenPtr t = lexer.peek_token ();
- bool trailing_comma = true;
- std::vector<std::unique_ptr<AST::Type>> types;
-
- while (t->get_id () != RIGHT_PAREN)
- {
- std::unique_ptr<AST::Type> type = parse_type ();
- if (type == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse type inside parentheses (probably "
- "tuple or parenthesised)");
- add_error (std::move (error));
-
- return nullptr;
- }
- types.push_back (std::move (type));
-
- t = lexer.peek_token ();
- if (t->get_id () != COMMA)
- {
- trailing_comma = false;
- break;
- }
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- return nullptr;
- }
-
- // if only one type and no trailing comma, then not a tuple type
- if (types.size () == 1 && !trailing_comma)
- {
- // must be a TraitObjectType (with more than one bound)
- if (lexer.peek_token ()->get_id () == PLUS)
- {
- // error - this is not allowed for type no bounds
- Error error (lexer.peek_token ()->get_locus (),
- "plus (implying TraitObjectType as type param "
- "bounds) is not allowed in type no bounds");
- add_error (std::move (error));
-
- return nullptr;
- }
- else
- {
- // release vector pointer
- std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
- /* HACK: attempt to convert to trait bound. if fails, parenthesised
- * type */
- std::unique_ptr<AST::TraitBound> converted_bound (
- released_ptr->to_trait_bound (true));
- if (converted_bound == nullptr)
- {
- // parenthesised type
- return std::unique_ptr<AST::ParenthesisedType> (
- new AST::ParenthesisedType (std::move (released_ptr),
- left_paren_locus));
- }
- else
- {
- // trait object type (one bound)
-
- // get value semantics trait bound
- AST::TraitBound value_bound (*converted_bound);
-
- return std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (value_bound,
- left_paren_locus));
- }
- }
- }
- else
- {
- return std::unique_ptr<AST::TupleType> (
- new AST::TupleType (std::move (types), left_paren_locus));
- }
- /* TODO: ensure that this ensures that dynamic dispatch for traits is not
- * lost somehow */
-}
-
-/* Parses a literal pattern or range pattern. Assumes that literals passed in
- * are valid range pattern bounds. Do not pass in paths in expressions, for
- * instance. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Pattern>
-Parser<ManagedTokenSource>::parse_literal_or_range_pattern ()
-{
- const_TokenPtr range_lower = lexer.peek_token ();
- AST::Literal::LitType type = AST::Literal::STRING;
- bool has_minus = false;
-
- // get lit type
- switch (range_lower->get_id ())
- {
- case CHAR_LITERAL:
- type = AST::Literal::CHAR;
- lexer.skip_token ();
- break;
- case BYTE_CHAR_LITERAL:
- type = AST::Literal::BYTE;
- lexer.skip_token ();
- break;
- case INT_LITERAL:
- type = AST::Literal::INT;
- lexer.skip_token ();
- break;
- case FLOAT_LITERAL:
- type = AST::Literal::FLOAT;
- lexer.skip_token ();
- break;
- case MINUS:
- // branch on next token
- range_lower = lexer.peek_token (1);
- switch (range_lower->get_id ())
- {
- case INT_LITERAL:
- type = AST::Literal::INT;
- has_minus = true;
- lexer.skip_token (1);
- break;
- case FLOAT_LITERAL:
- type = AST::Literal::FLOAT;
- has_minus = true;
- lexer.skip_token (1);
- break;
- default:
- add_error (Error (range_lower->get_locus (),
- "token type %qs cannot be parsed as range pattern "
- "bound or literal after minus symbol",
- range_lower->get_token_description ()));
-
- return nullptr;
- }
- break;
- default:
- add_error (
- Error (range_lower->get_locus (),
- "token type %qs cannot be parsed as range pattern bound",
- range_lower->get_token_description ()));
-
- return nullptr;
- }
-
- const_TokenPtr next = lexer.peek_token ();
- if (next->get_id () == DOT_DOT_EQ || next->get_id () == ELLIPSIS
- || next->get_id () == DOT_DOT)
- {
- AST::RangeKind kind = AST::tokenid_to_rangekind (next->get_id ());
- // range pattern
- lexer.skip_token ();
- std::unique_ptr<AST::RangePatternBound> lower (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), type,
- PrimitiveCoreType::CORETYPE_UNKNOWN),
- range_lower->get_locus (), has_minus));
-
- std::unique_ptr<AST::RangePatternBound> upper
- = parse_range_pattern_bound ();
- if (upper == nullptr)
- {
- Error error (next->get_locus (),
- "failed to parse range pattern bound in range pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::RangePattern> (
- new AST::RangePattern (std::move (lower), std::move (upper), kind,
- range_lower->get_locus ()));
- }
- else
- {
- // literal pattern
- return std::unique_ptr<AST::LiteralPattern> (
- new AST::LiteralPattern (range_lower->get_str (), type,
- range_lower->get_locus (),
- range_lower->get_type_hint (), has_minus));
- }
-}
-
-// Parses a range pattern bound (value only).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangePatternBound>
-Parser<ManagedTokenSource>::parse_range_pattern_bound ()
-{
- const_TokenPtr range_lower = lexer.peek_token ();
- location_t range_lower_locus = range_lower->get_locus ();
-
- // get lit type
- switch (range_lower->get_id ())
- {
- case CHAR_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::RangePatternBoundLiteral> (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), AST::Literal::CHAR,
- range_lower->get_type_hint ()),
- range_lower_locus));
- case BYTE_CHAR_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::RangePatternBoundLiteral> (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), AST::Literal::BYTE,
- range_lower->get_type_hint ()),
- range_lower_locus));
- case INT_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::RangePatternBoundLiteral> (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), AST::Literal::INT,
- range_lower->get_type_hint ()),
- range_lower_locus));
- case FLOAT_LITERAL:
- lexer.skip_token ();
- rust_debug ("warning: used deprecated float range pattern bound");
- return std::unique_ptr<AST::RangePatternBoundLiteral> (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), AST::Literal::FLOAT,
- range_lower->get_type_hint ()),
- range_lower_locus));
- case MINUS:
- // branch on next token
- range_lower = lexer.peek_token (1);
- switch (range_lower->get_id ())
- {
- case INT_LITERAL:
- lexer.skip_token (1);
- return std::unique_ptr<AST::RangePatternBoundLiteral> (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), AST::Literal::INT,
- range_lower->get_type_hint ()),
- range_lower_locus, true));
- case FLOAT_LITERAL:
- lexer.skip_token (1);
- rust_debug ("warning: used deprecated float range pattern bound");
- return std::unique_ptr<AST::RangePatternBoundLiteral> (
- new AST::RangePatternBoundLiteral (
- AST::Literal (range_lower->get_str (), AST::Literal::FLOAT,
- range_lower->get_type_hint ()),
- range_lower_locus, true));
- default:
- add_error (Error (range_lower->get_locus (),
- "token type %qs cannot be parsed as range pattern "
- "bound after minus symbol",
- range_lower->get_token_description ()));
-
- return nullptr;
- }
- case IDENTIFIER:
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case SCOPE_RESOLUTION:
- case DOLLAR_SIGN:
- {
- // path in expression
- AST::PathInExpression path = parse_path_in_expression ();
- if (path.is_error ())
- {
- Error error (
- range_lower->get_locus (),
- "failed to parse path in expression range pattern bound");
- add_error (std::move (error));
-
- return nullptr;
- }
- return std::unique_ptr<AST::RangePatternBoundPath> (
- new AST::RangePatternBoundPath (std::move (path)));
- }
- case LEFT_SHIFT:
- case LEFT_ANGLE:
- {
- // qualified path in expression
- AST::QualifiedPathInExpression path
- = parse_qualified_path_in_expression ();
- if (path.is_error ())
- {
- Error error (range_lower->get_locus (),
- "failed to parse qualified path in expression range "
- "pattern bound");
- add_error (std::move (error));
-
- return nullptr;
- }
- return std::unique_ptr<AST::RangePatternBoundQualPath> (
- new AST::RangePatternBoundQualPath (std::move (path)));
- }
- default:
- add_error (
- Error (range_lower->get_locus (),
- "token type %qs cannot be parsed as range pattern bound",
- range_lower->get_token_description ()));
-
- return nullptr;
- }
-}
-
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Pattern>
-Parser<ManagedTokenSource>::parse_pattern ()
-{
- location_t start_locus = lexer.peek_token ()->get_locus ();
-
- /* skip optional starting pipe */
- maybe_skip_token (PIPE);
-
- auto first = parse_pattern_no_alt ();
-
- if (lexer.peek_token ()->get_id () != PIPE)
- /* no alternates */
- return first;
-
- std::vector<std::unique_ptr<AST::Pattern>> alts;
- if (first != nullptr)
- alts.push_back (std::move (first));
-
- do
- {
- lexer.skip_token ();
- auto follow = parse_pattern_no_alt ();
- if (follow != nullptr)
- alts.push_back (std::move (follow));
- }
-
- while (lexer.peek_token ()->get_id () == PIPE);
-
- if (alts.empty ())
- return nullptr;
-
- /* alternates */
- return std::unique_ptr<AST::Pattern> (
- new AST::AltPattern (std::move (alts), start_locus));
-}
-
-// Parses a pattern without alternates ('|')
-// (will further disambiguate any pattern).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Pattern>
-Parser<ManagedTokenSource>::parse_pattern_no_alt ()
-{
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case TRUE_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::LiteralPattern> (
- new AST::LiteralPattern (Values::Keywords::TRUE_LITERAL,
- AST::Literal::BOOL, t->get_locus (),
- t->get_type_hint ()));
- case FALSE_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::LiteralPattern> (
- new AST::LiteralPattern (Values::Keywords::FALSE_LITERAL,
- AST::Literal::BOOL, t->get_locus (),
- t->get_type_hint ()));
- case CHAR_LITERAL:
- case BYTE_CHAR_LITERAL:
- case INT_LITERAL:
- case FLOAT_LITERAL:
- return parse_literal_or_range_pattern ();
- case STRING_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::LiteralPattern> (
- new AST::LiteralPattern (t->get_str (), AST::Literal::STRING,
- t->get_locus (), t->get_type_hint ()));
- case BYTE_STRING_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::LiteralPattern> (
- new AST::LiteralPattern (t->get_str (), AST::Literal::BYTE_STRING,
- t->get_locus (), t->get_type_hint ()));
- case RAW_STRING_LITERAL:
- lexer.skip_token ();
- return std::unique_ptr<AST::LiteralPattern> (
- new AST::LiteralPattern (t->get_str (), AST::Literal::RAW_STRING,
- t->get_locus (), t->get_type_hint ()));
- // raw string and raw byte string literals too if they are readded to
- // lexer
- case MINUS:
- if (lexer.peek_token (1)->get_id () == INT_LITERAL)
- {
- return parse_literal_or_range_pattern ();
- }
- else if (lexer.peek_token (1)->get_id () == FLOAT_LITERAL)
- {
- return parse_literal_or_range_pattern ();
- }
- else
- {
- Error error (t->get_locus (), "unexpected token %<-%> in pattern - "
- "did you forget an integer literal");
- add_error (std::move (error));
-
- return nullptr;
- }
- case UNDERSCORE:
- lexer.skip_token ();
- return std::unique_ptr<AST::WildcardPattern> (
- new AST::WildcardPattern (t->get_locus ()));
- case DOT_DOT:
- lexer.skip_token ();
- return std::unique_ptr<AST::RestPattern> (
- new AST::RestPattern (t->get_locus ()));
- case REF:
- case MUT:
- return parse_identifier_pattern ();
- case IDENTIFIER:
- /* if identifier with no scope resolution afterwards, identifier
- * pattern. if scope resolution afterwards, path pattern (or range
- * pattern or struct pattern or tuple struct pattern) or macro
- * invocation */
- return parse_ident_leading_pattern ();
- case AMP:
- case LOGICAL_AND:
- // reference pattern
- return parse_reference_pattern ();
- case LEFT_PAREN:
- // tuple pattern or grouped pattern
- return parse_grouped_or_tuple_pattern ();
- case LEFT_SQUARE:
- // slice pattern
- return parse_slice_pattern ();
- case LEFT_SHIFT:
- case LEFT_ANGLE:
- {
- // qualified path in expression or qualified range pattern bound
- AST::QualifiedPathInExpression path
- = parse_qualified_path_in_expression ();
-
- if (lexer.peek_token ()->get_id () == DOT_DOT_EQ
- || lexer.peek_token ()->get_id () == ELLIPSIS
- || lexer.peek_token ()->get_id () == DOT_DOT)
- {
- // qualified range pattern bound, so parse rest of range pattern
- AST::RangeKind kind
- = AST::tokenid_to_rangekind (lexer.peek_token ()->get_id ());
- lexer.skip_token ();
-
- std::unique_ptr<AST::RangePatternBoundQualPath> lower_bound (
- new AST::RangePatternBoundQualPath (std::move (path)));
- std::unique_ptr<AST::RangePatternBound> upper_bound
- = parse_range_pattern_bound ();
-
- return std::unique_ptr<AST::RangePattern> (
- new AST::RangePattern (std::move (lower_bound),
- std::move (upper_bound), kind,
- t->get_locus ()));
- }
- else
- {
- // just qualified path in expression
- return std::unique_ptr<AST::QualifiedPathInExpression> (
- new AST::QualifiedPathInExpression (std::move (path)));
- }
- }
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case SCOPE_RESOLUTION:
- case DOLLAR_SIGN:
- {
- // path in expression or range pattern bound
- AST::PathInExpression path = parse_path_in_expression ();
-
- const_TokenPtr next = lexer.peek_token ();
- switch (next->get_id ())
- {
- case DOT_DOT_EQ:
- case DOT_DOT:
- case ELLIPSIS:
- {
- // qualified range pattern bound, so parse rest of range pattern
- AST::RangeKind kind = AST::tokenid_to_rangekind (next->get_id ());
- lexer.skip_token ();
-
- std::unique_ptr<AST::RangePatternBoundPath> lower_bound (
- new AST::RangePatternBoundPath (std::move (path)));
- std::unique_ptr<AST::RangePatternBound> upper_bound
- = parse_range_pattern_bound ();
-
- return std::unique_ptr<AST::RangePattern> (
- new AST::RangePattern (std::move (lower_bound),
- std::move (upper_bound), kind,
- next->get_locus ()));
- }
- case EXCLAM:
- return parse_macro_invocation_partial (std::move (path),
- AST::AttrVec ());
- case LEFT_PAREN:
- {
- // tuple struct
- lexer.skip_token ();
-
- // parse items
- std::unique_ptr<AST::TupleStructItems> items
- = parse_tuple_struct_items ();
- if (items == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse tuple struct items");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- return nullptr;
- }
-
- return std::unique_ptr<AST::TupleStructPattern> (
- new AST::TupleStructPattern (std::move (path),
- std::move (items)));
- }
- case LEFT_CURLY:
- {
- // struct
- lexer.skip_token ();
-
- // parse elements (optional)
- AST::StructPatternElements elems = parse_struct_pattern_elems ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- return nullptr;
- }
-
- return std::unique_ptr<AST::StructPattern> (
- new AST::StructPattern (std::move (path), t->get_locus (),
- std::move (elems)));
- }
- default:
- // assume path in expression
- return std::unique_ptr<AST::PathInExpression> (
- new AST::PathInExpression (std::move (path)));
- }
- }
- default:
- add_error (Error (t->get_locus (), "unexpected token %qs in pattern",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-// Parses a single or double reference pattern.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ReferencePattern>
-Parser<ManagedTokenSource>::parse_reference_pattern ()
-{
- // parse double or single ref
- bool is_double_ref = false;
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case AMP:
- // still false
- lexer.skip_token ();
- break;
- case LOGICAL_AND:
- is_double_ref = true;
- lexer.skip_token ();
- break;
- default:
- add_error (Error (t->get_locus (),
- "unexpected token %qs in reference pattern",
- t->get_token_description ()));
-
- return nullptr;
- }
-
- // parse mut (if it exists)
- bool is_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
- {
- is_mut = true;
- lexer.skip_token ();
- }
-
- // parse pattern to get reference of (required)
- std::unique_ptr<AST::Pattern> pattern = parse_pattern_no_alt ();
- if (pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in reference pattern");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::ReferencePattern> (
- new AST::ReferencePattern (std::move (pattern), is_mut, is_double_ref,
- t->get_locus ()));
-}
-
-/* Parses a grouped pattern or tuple pattern. Prefers grouped over tuple if
- * only a single element with no commas. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Pattern>
-Parser<ManagedTokenSource>::parse_grouped_or_tuple_pattern ()
-{
- location_t paren_locus = lexer.peek_token ()->get_locus ();
- skip_token (LEFT_PAREN);
-
- // detect '..' token (ranged with no lower range)
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- lexer.skip_token ();
-
- // parse new patterns while next token is a comma
- std::vector<std::unique_ptr<AST::Pattern>> patterns;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == COMMA)
- {
- lexer.skip_token ();
-
- // break if next token is ')'
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- {
- break;
- }
-
- // parse pattern, which is required
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (
- lexer.peek_token ()->get_locus (),
- "failed to parse pattern inside ranged tuple pattern");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
- }
- patterns.push_back (std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip somewhere?
- return nullptr;
- }
-
- // create tuple pattern items with only upper pattern items
- std::unique_ptr<AST::TuplePatternItemsHasRest> items (
- new AST::TuplePatternItemsHasRest (
- std::vector<std::unique_ptr<AST::Pattern>> (), std::move (patterns)));
- return std::unique_ptr<AST::TuplePattern> (
- new AST::TuplePattern (std::move (items), paren_locus));
- }
- else if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- {
- skip_token (RIGHT_PAREN);
- auto items = std::unique_ptr<AST::TuplePatternItemsNoRest> (
- new AST::TuplePatternItemsNoRest (
- std::vector<std::unique_ptr<AST::Pattern>> ()));
- return std::unique_ptr<AST::TuplePattern> (
- new AST::TuplePattern (std::move (items), paren_locus));
- }
-
- // parse initial pattern (required)
- std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
- if (initial_pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in grouped or tuple pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- // branch on whether next token is a comma or not
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case RIGHT_PAREN:
- // grouped pattern
- lexer.skip_token ();
-
- return std::unique_ptr<AST::GroupedPattern> (
- new AST::GroupedPattern (std::move (initial_pattern), paren_locus));
- case COMMA:
- {
- // tuple pattern
- lexer.skip_token ();
-
- // create vector of patterns
- std::vector<std::unique_ptr<AST::Pattern>> patterns;
- patterns.push_back (std::move (initial_pattern));
-
- t = lexer.peek_token ();
- while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT)
- {
- // parse pattern (required)
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse pattern in tuple pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- patterns.push_back (std::move (pattern));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- t = lexer.peek_token ();
- if (t->get_id () == RIGHT_PAREN)
- {
- // non-ranged tuple pattern
- lexer.skip_token ();
-
- std::unique_ptr<AST::TuplePatternItemsNoRest> items (
- new AST::TuplePatternItemsNoRest (std::move (patterns)));
- return std::unique_ptr<AST::TuplePattern> (
- new AST::TuplePattern (std::move (items), paren_locus));
- }
- else if (t->get_id () == DOT_DOT)
- {
- // ranged tuple pattern
- lexer.skip_token ();
-
- // parse upper patterns
- std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
- t = lexer.peek_token ();
- while (t->get_id () == COMMA)
- {
- lexer.skip_token ();
-
- // break if end
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- break;
-
- // parse pattern (required)
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in tuple pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- upper_patterns.push_back (std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- return nullptr;
- }
-
- std::unique_ptr<AST::TuplePatternItemsHasRest> items (
- new AST::TuplePatternItemsHasRest (std::move (patterns),
- std::move (upper_patterns)));
- return std::unique_ptr<AST::TuplePattern> (
- new AST::TuplePattern (std::move (items), paren_locus));
- }
- else
- {
- // some kind of error
- Error error (t->get_locus (),
- "failed to parse tuple pattern (probably) or maybe "
- "grouped pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- }
- default:
- // error
- add_error (Error (t->get_locus (),
- "unrecognised token %qs in grouped or tuple pattern "
- "after first pattern",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-/* Parses a slice pattern that can match arrays or slices. Parses the square
- * brackets too. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::SlicePattern>
-Parser<ManagedTokenSource>::parse_slice_pattern ()
-{
- location_t square_locus = lexer.peek_token ()->get_locus ();
- std::vector<std::unique_ptr<AST::Pattern>> patterns;
- tl::optional<std::vector<std::unique_ptr<AST::Pattern>>> upper_patterns
- = tl::nullopt;
-
- // lambda function to determine which vector to push new patterns into
- auto get_pattern_ref
- = [&] () -> std::vector<std::unique_ptr<AST::Pattern>> & {
- return upper_patterns.has_value () ? upper_patterns.value () : patterns;
- };
-
- skip_token (LEFT_SQUARE);
-
- if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
- {
- skip_token (RIGHT_SQUARE);
- std::unique_ptr<AST::SlicePatternItemsNoRest> items (
- new AST::SlicePatternItemsNoRest (std::move (patterns)));
- return std::unique_ptr<AST::SlicePattern> (
- new AST::SlicePattern (std::move (items), square_locus));
- }
-
- // parse initial pattern (required)
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- lexer.skip_token ();
- upper_patterns = std::vector<std::unique_ptr<AST::Pattern>> ();
- }
- else
- {
- // Not a rest pattern `..`, parse normally
- std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
- if (initial_pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse initial pattern in slice pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- patterns.push_back (std::move (initial_pattern));
- }
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == COMMA)
- {
- lexer.skip_token ();
-
- // break if end bracket
- if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
- break;
-
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- if (upper_patterns.has_value ())
- {
- // DOT_DOT has been parsed before
- Error error (lexer.peek_token ()->get_locus (), "%s",
- "`..` can only be used once per slice pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- upper_patterns = std::vector<std::unique_ptr<AST::Pattern>> ();
- lexer.skip_token ();
- t = lexer.peek_token ();
- continue;
- }
-
- // parse pattern (required)
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in slice pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- get_pattern_ref ().push_back (std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_SQUARE))
- {
- return nullptr;
- }
-
- if (upper_patterns.has_value ())
- {
- // Slice pattern with rest
- std::unique_ptr<AST::SlicePatternItemsHasRest> items (
- new AST::SlicePatternItemsHasRest (
- std::move (patterns), std::move (upper_patterns.value ())));
- return std::unique_ptr<AST::SlicePattern> (
- new AST::SlicePattern (std::move (items), square_locus));
- }
-
- // Rest-less slice pattern
- std::unique_ptr<AST::SlicePatternItemsNoRest> items (
- new AST::SlicePatternItemsNoRest (std::move (patterns)));
- return std::unique_ptr<AST::SlicePattern> (
- new AST::SlicePattern (std::move (items), square_locus));
-}
-
-/* Parses an identifier pattern (pattern that binds a value matched to a
- * variable). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::IdentifierPattern>
-Parser<ManagedTokenSource>::parse_identifier_pattern ()
-{
- location_t locus = lexer.peek_token ()->get_locus ();
-
- bool has_ref = false;
- if (lexer.peek_token ()->get_id () == REF)
- {
- has_ref = true;
- lexer.skip_token ();
-
- // DEBUG
- rust_debug ("parsed ref in identifier pattern");
- }
-
- bool has_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
- {
- has_mut = true;
- lexer.skip_token ();
- }
-
- // parse identifier (required)
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- {
- // skip somewhere?
- return nullptr;
- }
- Identifier ident{ident_tok};
-
- // DEBUG
- rust_debug ("parsed identifier in identifier pattern");
-
- // parse optional pattern binding thing
- std::unique_ptr<AST::Pattern> bind_pattern = nullptr;
- if (lexer.peek_token ()->get_id () == PATTERN_BIND)
- {
- lexer.skip_token ();
-
- // parse required pattern to bind
- bind_pattern = parse_pattern_no_alt ();
- if (bind_pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern to bind in identifier pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- }
-
- // DEBUG
- rust_debug ("about to return identifier pattern");
-
- return std::unique_ptr<AST::IdentifierPattern> (
- new AST::IdentifierPattern (std::move (ident), locus, has_ref, has_mut,
- std::move (bind_pattern)));
-}
-
-/* Parses a pattern that opens with an identifier. This includes identifier
- * patterns, path patterns (and derivatives such as struct patterns, tuple
- * struct patterns, and macro invocations), and ranges. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Pattern>
-Parser<ManagedTokenSource>::parse_ident_leading_pattern ()
-{
- // ensure first token is actually identifier
- const_TokenPtr initial_tok = lexer.peek_token ();
- if (initial_tok->get_id () != IDENTIFIER)
- {
- return nullptr;
- }
-
- // save initial identifier as it may be useful (but don't skip)
- std::string initial_ident = initial_tok->get_str ();
-
- // parse next tokens as a PathInExpression
- AST::PathInExpression path = parse_path_in_expression ();
-
- // branch on next token
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case EXCLAM:
- return parse_macro_invocation_partial (std::move (path), AST::AttrVec ());
- case LEFT_PAREN:
- {
- // tuple struct
- lexer.skip_token ();
-
- // DEBUG
- rust_debug ("parsing tuple struct pattern");
-
- // parse items
- std::unique_ptr<AST::TupleStructItems> items
- = parse_tuple_struct_items ();
- if (items == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse tuple struct items");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- // DEBUG
- rust_debug ("successfully parsed tuple struct items");
-
- if (!skip_token (RIGHT_PAREN))
- {
- return nullptr;
- }
-
- // DEBUG
- rust_debug ("successfully parsed tuple struct pattern");
-
- return std::unique_ptr<AST::TupleStructPattern> (
- new AST::TupleStructPattern (std::move (path), std::move (items)));
- }
- case LEFT_CURLY:
- {
- // struct
- lexer.skip_token ();
-
- // parse elements (optional)
- AST::StructPatternElements elems = parse_struct_pattern_elems ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- return nullptr;
- }
-
- // DEBUG
- rust_debug ("successfully parsed struct pattern");
-
- return std::unique_ptr<AST::StructPattern> (
- new AST::StructPattern (std::move (path), initial_tok->get_locus (),
- std::move (elems)));
- }
- case DOT_DOT_EQ:
- case DOT_DOT:
- case ELLIPSIS:
- {
- // range
- AST::RangeKind kind
- = AST::tokenid_to_rangekind (lexer.peek_token ()->get_id ());
-
- lexer.skip_token ();
-
- std::unique_ptr<AST::RangePatternBoundPath> lower_bound (
- new AST::RangePatternBoundPath (std::move (path)));
- std::unique_ptr<AST::RangePatternBound> upper_bound
- = parse_range_pattern_bound ();
-
- return std::unique_ptr<AST::RangePattern> (
- new AST::RangePattern (std::move (lower_bound),
- std::move (upper_bound), kind,
- t->get_locus ()));
- }
- case PATTERN_BIND:
- {
- // only allow on single-segment paths
- if (path.is_single_segment ())
- {
- // identifier with pattern bind
- lexer.skip_token ();
-
- std::unique_ptr<AST::Pattern> bind_pattern
- = parse_pattern_no_alt ();
- if (bind_pattern == nullptr)
- {
- Error error (
- t->get_locus (),
- "failed to parse pattern to bind to identifier pattern");
- add_error (std::move (error));
-
- return nullptr;
- }
- return std::unique_ptr<AST::IdentifierPattern> (
- new AST::IdentifierPattern (std::move (initial_ident),
- initial_tok->get_locus (), false,
- false, std::move (bind_pattern)));
- }
- Error error (
- t->get_locus (),
- "failed to parse pattern bind to a path, not an identifier");
- add_error (std::move (error));
-
- return nullptr;
- }
- default:
- // assume identifier if single segment
- if (path.is_single_segment ())
- {
- return std::unique_ptr<AST::IdentifierPattern> (
- new AST::IdentifierPattern (std::move (initial_ident),
- initial_tok->get_locus ()));
- }
- // return path otherwise
- return std::unique_ptr<AST::PathInExpression> (
- new AST::PathInExpression (std::move (path)));
- }
-}
-
-// Parses tuple struct items if they exist. Does not parse parentheses.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TupleStructItems>
-Parser<ManagedTokenSource>::parse_tuple_struct_items ()
-{
- std::vector<std::unique_ptr<AST::Pattern>> lower_patterns;
-
- // DEBUG
- rust_debug ("started parsing tuple struct items");
-
- // check for '..' at front
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- // only parse upper patterns
- lexer.skip_token ();
-
- // DEBUG
- rust_debug ("'..' at front in tuple struct items detected");
-
- std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () == COMMA)
- {
- lexer.skip_token ();
-
- // break if right paren
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- break;
-
- // parse pattern, which is now required
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in tuple struct items");
- add_error (std::move (error));
-
- return nullptr;
- }
- upper_patterns.push_back (std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- // DEBUG
- rust_debug (
- "finished parsing tuple struct items ranged (upper/none only)");
-
- return std::unique_ptr<AST::TupleStructItemsHasRest> (
- new AST::TupleStructItemsHasRest (std::move (lower_patterns),
- std::move (upper_patterns)));
- }
-
- // has at least some lower patterns
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT)
- {
- // DEBUG
- rust_debug ("about to parse pattern in tuple struct items");
-
- // parse pattern, which is required
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse pattern in tuple struct items");
- add_error (std::move (error));
-
- return nullptr;
- }
- lower_patterns.push_back (std::move (pattern));
-
- // DEBUG
- rust_debug ("successfully parsed pattern in tuple struct items");
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- // DEBUG
- rust_debug ("broke out of parsing patterns in tuple struct "
- "items as no comma");
-
- break;
- }
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- // branch on next token
- t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case RIGHT_PAREN:
- return std::unique_ptr<AST::TupleStructItemsNoRest> (
- new AST::TupleStructItemsNoRest (std::move (lower_patterns)));
- case DOT_DOT:
- {
- // has an upper range that must be parsed separately
- lexer.skip_token ();
-
- std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
-
- t = lexer.peek_token ();
- while (t->get_id () == COMMA)
- {
- lexer.skip_token ();
-
- // break if next token is right paren
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- break;
-
- // parse pattern, which is required
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in tuple struct items");
- add_error (std::move (error));
-
- return nullptr;
- }
- upper_patterns.push_back (std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- return std::unique_ptr<AST::TupleStructItemsHasRest> (
- new AST::TupleStructItemsHasRest (std::move (lower_patterns),
- std::move (upper_patterns)));
- }
- default:
- // error
- add_error (Error (t->get_locus (),
- "unexpected token %qs in tuple struct items",
- t->get_token_description ()));
-
- return nullptr;
- }
-}
-
-// Parses struct pattern elements if they exist.
-template <typename ManagedTokenSource>
-AST::StructPatternElements
-Parser<ManagedTokenSource>::parse_struct_pattern_elems ()
-{
- std::vector<std::unique_ptr<AST::StructPatternField>> fields;
-
- AST::AttrVec etc_attrs;
- bool has_rest = false;
-
- // try parsing struct pattern fields
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY)
- {
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // parse etc (must be last in struct pattern, so breaks)
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- lexer.skip_token ();
- etc_attrs = std::move (outer_attrs);
- has_rest = true;
- break;
- }
-
- std::unique_ptr<AST::StructPatternField> field
- = parse_struct_pattern_field_partial (std::move (outer_attrs));
- if (field == nullptr)
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse struct pattern field");
- add_error (std::move (error));
-
- // skip after somewhere?
- return AST::StructPatternElements::create_empty ();
- }
- fields.push_back (std::move (field));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- // skip comma
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- if (has_rest)
- return AST::StructPatternElements (std::move (fields),
- std::move (etc_attrs));
- else
- return AST::StructPatternElements (std::move (fields));
-}
-
-/* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or
- * identifier). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::StructPatternField>
-Parser<ManagedTokenSource>::parse_struct_pattern_field ()
-{
- // parse outer attributes (if they exist)
- AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- return parse_struct_pattern_field_partial (std::move (outer_attrs));
-}
-
-/* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or
- * identifier), with outer attributes passed in. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::StructPatternField>
-Parser<ManagedTokenSource>::parse_struct_pattern_field_partial (
- AST::AttrVec outer_attrs)
-{
- // branch based on next token
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case INT_LITERAL:
- {
- // tuple index
- std::string index_str = t->get_str ();
- int index = atoi (index_str.c_str ());
-
- lexer.skip_token ();
-
- if (!skip_token (COLON))
- {
- return nullptr;
- }
-
- // parse required pattern
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (
- t->get_locus (),
- "failed to parse pattern in tuple index struct pattern field");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::StructPatternFieldTuplePat> (
- new AST::StructPatternFieldTuplePat (index, std::move (pattern),
- std::move (outer_attrs),
- t->get_locus ()));
- }
- case IDENTIFIER:
- // identifier-pattern OR only identifier
- // branch on next token
- switch (lexer.peek_token (1)->get_id ())
- {
- case COLON:
- {
- // identifier-pattern
- Identifier ident{t};
- lexer.skip_token ();
-
- skip_token (COLON);
-
- // parse required pattern
- std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse pattern in struct pattern field");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::StructPatternFieldIdentPat> (
- new AST::StructPatternFieldIdentPat (std::move (ident),
- std::move (pattern),
- std::move (outer_attrs),
- t->get_locus ()));
- }
- case COMMA:
- case RIGHT_CURLY:
- {
- // identifier only
- Identifier ident = {t};
- lexer.skip_token ();
-
- return std::unique_ptr<AST::StructPatternFieldIdent> (
- new AST::StructPatternFieldIdent (std::move (ident), false, false,
- std::move (outer_attrs),
- t->get_locus ()));
- }
- default:
- // error
- add_error (Error (t->get_locus (),
- "unrecognised token %qs in struct pattern field",
- t->get_token_description ()));
-
- return nullptr;
- }
- case REF:
- case MUT:
- {
- // only identifier
- bool has_ref = false;
- if (t->get_id () == REF)
- {
- has_ref = true;
- lexer.skip_token ();
- }
-
- bool has_mut = false;
- if (lexer.peek_token ()->get_id () == MUT)
- {
- has_mut = true;
- lexer.skip_token ();
- }
-
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == nullptr)
- {
- return nullptr;
- }
- Identifier ident{ident_tok};
-
- return std::unique_ptr<AST::StructPatternFieldIdent> (
- new AST::StructPatternFieldIdent (std::move (ident), has_ref, has_mut,
- std::move (outer_attrs),
- t->get_locus ()));
- }
- default:
- // not necessarily an error
- return nullptr;
- }
-}
-
-/* Parses a statement or expression (depending on whether a trailing semicolon
- * exists). Useful for block expressions where it cannot be determined through
- * lookahead whether it is a statement or expression to be parsed. */
-template <typename ManagedTokenSource>
-ExprOrStmt
-Parser<ManagedTokenSource>::parse_stmt_or_expr ()
-{
- // quick exit for empty statement
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () == SEMICOLON)
- {
- lexer.skip_token ();
- std::unique_ptr<AST::EmptyStmt> stmt (
- new AST::EmptyStmt (t->get_locus ()));
- return ExprOrStmt (std::move (stmt));
- }
-
- // parse outer attributes
- AST::AttrVec outer_attrs = parse_outer_attributes ();
- ParseRestrictions restrictions;
- restrictions.expr_can_be_stmt = true;
- std::unique_ptr<AST::Expr> expr;
-
- // parsing this will be annoying because of the many different possibilities
- /* best may be just to copy paste in parse_item switch, and failing that try
- * to parse outer attributes, and then pass them in to either a let
- * statement or (fallback) expression statement. */
- // FIXME: think of a way to do this without such a large switch?
-
- /* FIXME: for expressions at least, the only way that they can really be
- * parsed properly in this way is if they don't support operators on them.
- * They must be pratt-parsed otherwise. As such due to composability, only
- * explicit statements will have special cases here. This should roughly
- * correspond to "expr-with-block", but this warning is here in case it
- * isn't the case. */
- t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case LET:
- {
- // let statement
- std::unique_ptr<AST::LetStmt> stmt (
- parse_let_stmt (std::move (outer_attrs)));
- return ExprOrStmt (std::move (stmt));
- }
- case PUB:
- case MOD:
- case EXTERN_KW:
- case USE:
- case FN_KW:
- case TYPE:
- case STRUCT_KW:
- case ENUM_KW:
- case CONST:
- case STATIC_KW:
- case AUTO:
- case TRAIT:
- case IMPL:
- {
- std::unique_ptr<AST::VisItem> item (
- parse_vis_item (std::move (outer_attrs)));
- return ExprOrStmt (std::move (item));
- }
- /* TODO: implement union keyword but not really because of
- * context-dependence crappy hack way to parse a union written below to
- * separate it from the good code. */
- // case UNION:
- case UNSAFE:
- { // maybe - unsafe traits are a thing
- /* if any of these (should be all possible VisItem prefixes), parse a
- * VisItem - can't parse item because would require reparsing outer
- * attributes */
- const_TokenPtr t2 = lexer.peek_token (1);
- switch (t2->get_id ())
- {
- case LEFT_CURLY:
- {
- // unsafe block: parse as expression
- expr = parse_expr (std::move (outer_attrs), restrictions);
- break;
- }
- case AUTO:
- case TRAIT:
- {
- // unsafe trait
- std::unique_ptr<AST::VisItem> item (
- parse_vis_item (std::move (outer_attrs)));
- return ExprOrStmt (std::move (item));
- }
- case EXTERN_KW:
- case FN_KW:
- {
- // unsafe function
- std::unique_ptr<AST::VisItem> item (
- parse_vis_item (std::move (outer_attrs)));
- return ExprOrStmt (std::move (item));
- }
- case IMPL:
- {
- // unsafe trait impl
- std::unique_ptr<AST::VisItem> item (
- parse_vis_item (std::move (outer_attrs)));
- return ExprOrStmt (std::move (item));
- }
- default:
- add_error (Error (t2->get_locus (),
- "unrecognised token %qs after parsing unsafe - "
- "expected beginning of expression or statement",
- t->get_token_description ()));
-
- // skip somewhere?
- return ExprOrStmt::create_error ();
- }
- break;
- }
- /* FIXME: this is either a macro invocation or macro invocation semi.
- * start parsing to determine which one it is. */
- // FIXME: old code there
-
- // crappy hack to do union "keyword"
- case IDENTIFIER:
- if (t->get_str () == Values::WeakKeywords::UNION
- && lexer.peek_token (1)->get_id () == IDENTIFIER)
- {
- std::unique_ptr<AST::VisItem> item (
- parse_vis_item (std::move (outer_attrs)));
- return ExprOrStmt (std::move (item));
- // or should this go straight to parsing union?
- }
- else if (t->get_str () == Values::WeakKeywords::MACRO_RULES
- && lexer.peek_token (1)->get_id () == EXCLAM)
- {
- // macro_rules! macro item
- std::unique_ptr<AST::Item> item (
- parse_macro_rules_def (std::move (outer_attrs)));
- return ExprOrStmt (std::move (item));
- }
- gcc_fallthrough ();
- case SUPER:
- case SELF:
- case SELF_ALIAS:
- case CRATE:
- case SCOPE_RESOLUTION:
- case DOLLAR_SIGN:
- {
- AST::PathInExpression path = parse_path_in_expression ();
- std::unique_ptr<AST::Expr> null_denotation;
-
- if (lexer.peek_token ()->get_id () == EXCLAM)
- {
- std::unique_ptr<AST::MacroInvocation> invoc
- = parse_macro_invocation_partial (std::move (path),
- std::move (outer_attrs));
- if (invoc == nullptr)
- return ExprOrStmt::create_error ();
-
- if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
- {
- invoc->add_semicolon ();
- // Macro invocation with semicolon.
- return ExprOrStmt (
- std::unique_ptr<AST::Stmt> (std::move (invoc)));
- }
-
- TokenId after_macro = lexer.peek_token ()->get_id ();
-
- AST::DelimType delim_type = invoc->get_invoc_data ()
- .get_delim_tok_tree ()
- .get_delim_type ();
-
- if (delim_type == AST::CURLY && after_macro != DOT
- && after_macro != QUESTION_MARK)
- {
- rust_debug ("braced macro statement");
- return ExprOrStmt (
- std::unique_ptr<AST::Stmt> (std::move (invoc)));
- }
-
- null_denotation = std::move (invoc);
- }
- else
- {
- null_denotation
- = null_denotation_path (std::move (path), {}, restrictions);
- }
-
- expr = left_denotations (std::move (null_denotation), LBP_LOWEST,
- std::move (outer_attrs), restrictions);
- break;
- }
- default:
- /* expression statement or expression itself - parse
- * expression then make it statement if semi afterwards */
- expr = parse_expr (std::move (outer_attrs), restrictions);
- break;
- }
-
- const_TokenPtr after_expr = lexer.peek_token ();
- if (after_expr->get_id () == SEMICOLON)
- {
- // must be expression statement
- lexer.skip_token ();
-
- if (expr)
- {
- std::unique_ptr<AST::ExprStmt> stmt (
- new AST::ExprStmt (std::move (expr), t->get_locus (), true));
- return ExprOrStmt (std::move (stmt));
- }
- else
- {
- return ExprOrStmt::create_error ();
- }
- }
-
- if (expr && !expr->is_expr_without_block ()
- && after_expr->get_id () != RIGHT_CURLY)
- {
- // block expression statement.
- std::unique_ptr<AST::ExprStmt> stmt (
- new AST::ExprStmt (std::move (expr), t->get_locus (), false));
- return ExprOrStmt (std::move (stmt));
- }
-
- // return expression
- return ExprOrStmt (std::move (expr));
-}
-
-// Parses a struct expression field.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::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)
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse struct expression field with "
- "identifier and expression");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
- new AST::StructExprFieldIdentifierValue (std::move (ident),
- std::move (expr),
- 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 nullptr;
- }
-
- // parse field expression (required)
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse expr in struct (or enum) expr "
- "field with tuple index");
- add_error (std::move (error));
-
- return nullptr;
- }
-
- return std::unique_ptr<AST::StructExprFieldIndexValue> (
- new AST::StructExprFieldIndexValue (index, std::move (expr),
- 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 nullptr;
- 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 nullptr;
- }
-}
-
-// "Unexpected token" panic mode - flags gcc error at unexpected token
-// TODO: seems to be unused, remove?
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::unexpected_token (const_TokenPtr t)
-{
- Error error (t->get_locus (), "unexpected token %qs",
- t->get_token_description ());
- add_error (std::move (error));
-}
-
-/* Crappy "error recovery" performed after error by skipping tokens until a
- * semi-colon is found */
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::skip_after_semicolon ()
-{
- const_TokenPtr t = lexer.peek_token ();
-
- while (t->get_id () != END_OF_FILE && t->get_id () != SEMICOLON)
- {
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- if (t->get_id () == SEMICOLON)
- lexer.skip_token ();
-}
-
-/* Skips the current token */
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::skip_token ()
-{
- lexer.skip_token ();
-}
-
-/* Checks if current token has inputted id - skips it and returns true if so,
- * diagnoses an error and returns false otherwise. */
-template <typename ManagedTokenSource>
-bool
-Parser<ManagedTokenSource>::skip_token (TokenId token_id)
-{
- return expect_token (token_id) != const_TokenPtr ();
-}
-
-/* Checks if current token is similar to inputted token - skips it and returns
- * true if so, diagnoses an error and returns false otherwise. */
-template <typename ManagedTokenSource>
-bool
-Parser<ManagedTokenSource>::skip_token (const_TokenPtr token)
-{
- return expect_token (token) != const_TokenPtr ();
-}
-
-/* Checks if current token has inputted id - skips it and returns true if so,
- * returns false otherwise without diagnosing an error */
-template <typename ManagedTokenSource>
-bool
-Parser<ManagedTokenSource>::maybe_skip_token (TokenId token_id)
-{
- if (lexer.peek_token ()->get_id () != token_id)
- return false;
- else
- return skip_token (token_id);
-}
-
-/* Checks the current token - if id is same as expected, skips and returns it,
- * otherwise diagnoses error and returns null. */
-template <typename ManagedTokenSource>
-const_TokenPtr
-Parser<ManagedTokenSource>::expect_token (TokenId token_id)
-{
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () == token_id)
- {
- lexer.skip_token ();
- return t;
- }
- else
- {
- Error error (t->get_locus (), "expecting %qs but %qs found",
- get_token_description (token_id),
- t->get_token_description ());
- add_error (std::move (error));
-
- return const_TokenPtr ();
- }
-}
-
-/* Checks the current token - if same as expected, skips and returns it,
- * otherwise diagnoses error and returns null. */
-template <typename ManagedTokenSource>
-const_TokenPtr
-Parser<ManagedTokenSource>::expect_token (const_TokenPtr token_expect)
-{
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () == token_expect->get_id ()
- && (!t->should_have_str () || t->get_str () == token_expect->get_str ()))
- {
- lexer.skip_token ();
- return t;
- }
- else
- {
- Error error (t->get_locus (), "expecting %qs but %qs found",
- token_expect->get_token_description (),
- t->get_token_description ());
- add_error (std::move (error));
-
- return const_TokenPtr ();
- }
-}
-
-// Skips all tokens until EOF or }. Don't use.
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::skip_after_end ()
-{
- const_TokenPtr t = lexer.peek_token ();
-
- while (t->get_id () != END_OF_FILE && t->get_id () != RIGHT_CURLY)
- {
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- if (t->get_id () == RIGHT_CURLY)
- {
- lexer.skip_token ();
- }
-}
-
-/* A slightly more aware error-handler that skips all tokens until it reaches
- * the end of the block scope (i.e. when left curly brackets = right curly
- * brackets). Note: assumes currently in the middle of a block. Use
- * skip_after_next_block to skip based on the assumption that the block
- * has not been entered yet. */
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::skip_after_end_block ()
-{
- const_TokenPtr t = lexer.peek_token ();
- int curly_count = 1;
-
- while (curly_count > 0 && t->get_id () != END_OF_FILE)
- {
- switch (t->get_id ())
- {
- case LEFT_CURLY:
- curly_count++;
- break;
- case RIGHT_CURLY:
- curly_count--;
- break;
- default:
- break;
- }
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-}
-
-/* Skips tokens until the end of the next block. i.e. assumes that the block
- * has not been entered yet. */
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::skip_after_next_block ()
-{
- const_TokenPtr t = lexer.peek_token ();
-
- // initial loop - skip until EOF if no left curlies encountered
- while (t->get_id () != END_OF_FILE && t->get_id () != LEFT_CURLY)
- {
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- // if next token is left, skip it and then skip after the block ends
- if (t->get_id () == LEFT_CURLY)
- {
- lexer.skip_token ();
-
- skip_after_end_block ();
- }
- // otherwise, do nothing as EOF
-}
-
-/* Skips all tokens until ] (the end of an attribute) - does not skip the ]
- * (as designed for attribute body use) */
-template <typename ManagedTokenSource>
-void
-Parser<ManagedTokenSource>::skip_after_end_attribute ()
-{
- const_TokenPtr t = lexer.peek_token ();
-
- while (t->get_id () != RIGHT_SQUARE && t->get_id () != END_OF_FILE)
- {
- lexer.skip_token ();
- t = lexer.peek_token ();
- }
-
- // Don't skip the RIGHT_SQUARE token
-}
-
-/* Pratt parser impl of parse_expr. FIXME: this is only provisional and
- * probably will be changed. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::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 nullptr;
- }
-
- ParseRestrictions null_denotation_restrictions = restrictions;
- null_denotation_restrictions.expr_can_be_stmt = false;
-
- // parse null denotation (unary part of expression)
- std::unique_ptr<AST::Expr> expr
- = null_denotation ({}, null_denotation_restrictions);
- if (expr == nullptr)
- return nullptr;
-
- return left_denotations (std::move (expr), right_binding_power,
- std::move (outer_attrs), restrictions);
-}
-
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
-Parser<ManagedTokenSource>::left_denotations (std::unique_ptr<AST::Expr> expr,
- int right_binding_power,
- AST::AttrVec outer_attrs,
- ParseRestrictions restrictions)
-{
- if (expr == nullptr)
- {
- // DEBUG
- rust_debug ("null denotation is null; returning null for parse_expr");
- return nullptr;
- }
-
- const_TokenPtr current_token = lexer.peek_token ();
-
- if (restrictions.expr_can_be_stmt && !expr->is_expr_without_block ()
- && current_token->get_id () != DOT
- && current_token->get_id () != QUESTION_MARK)
+ // parse pattern (which is required)
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern_no_alt ();
+ if (pattern == nullptr)
{
- rust_debug ("statement expression with block");
- expr->set_outer_attrs (std::move (outer_attrs));
- return expr;
+ // not necessarily an error
+ return AST::ClosureParam::create_error ();
}
- 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))
+ // parse optional type of param
+ std::unique_ptr<AST::Type> type = nullptr;
+ if (lexer.peek_token ()->get_id () == COLON)
{
lexer.skip_token ();
- // FIXME attributes should generally be applied to the null denotation.
- expr = left_denotation (current_token, std::move (expr),
- std::move (outer_attrs), restrictions);
-
- if (expr == nullptr)
+ // parse type, which is now required
+ type = parse_type ();
+ if (type == nullptr)
{
- // DEBUG
- rust_debug ("left denotation is null; returning null for parse_expr");
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type in closure parameter");
+ add_error (std::move (error));
- return nullptr;
+ // skip somewhere?
+ return AST::ClosureParam::create_error ();
}
-
- current_token = lexer.peek_token ();
}
- return expr;
-}
-
-// Parse expression with lowest left binding power.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
-Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
- ParseRestrictions restrictions)
-{
- return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
-}
-
-/* Determines action to take when finding token at beginning of expression. */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::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);
- }
- 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);
- }
+ location_t loc = pattern->get_locus ();
+ return AST::ClosureParam (std::move (pattern), loc, std::move (type),
+ std::move (outer_attrs));
}
-// Handling of expresions that start with a path for `null_denotation`.
+// Parses a type (will further disambiguate any type).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
-Parser<ManagedTokenSource>::null_denotation_path (
- AST::PathInExpression path, AST::AttrVec outer_attrs,
- ParseRestrictions restrictions)
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_type (bool save_errors)
{
- 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::unique_ptr<AST::PathInExpression> (
- new AST::PathInExpression (std::move (path)));
- }
+ /* rules for all types:
+ * NeverType: '!'
+ * SliceType: '[' Type ']'
+ * InferredType: '_'
+ * MacroInvocation: SimplePath '!' DelimTokenTree
+ * ParenthesisedType: '(' Type ')'
+ * ImplTraitType: 'impl' TypeParamBounds
+ * TypeParamBounds (not type) TypeParamBound ( '+' TypeParamBound )* '+'?
+ * TypeParamBound Lifetime | TraitBound
+ * ImplTraitTypeOneBound: 'impl' TraitBound
+ * TraitObjectType: 'dyn'? TypeParamBounds
+ * TraitObjectTypeOneBound: 'dyn'? TraitBound
+ * TraitBound '?'? ForLifetimes? TypePath | '(' '?'?
+ * ForLifetimes? TypePath ')' BareFunctionType: ForLifetimes?
+ * FunctionQualifiers 'fn' etc. ForLifetimes (not type) 'for' '<'
+ * LifetimeParams '>' FunctionQualifiers ( 'async' | 'const' )?
+ * 'unsafe'?
+ * ('extern' abi?)? QualifiedPathInType: '<' Type ( 'as' TypePath )? '>'
+ * (
+ * '::' TypePathSegment )+ TypePath: '::'? TypePathSegment (
+ * '::' TypePathSegment)* ArrayType: '[' Type ';' Expr ']'
+ * ReferenceType: '&' Lifetime? 'mut'? TypeNoBounds
+ * RawPointerType: '*' ( 'mut' | 'const' ) TypeNoBounds
+ * TupleType: '(' Type etc. - regular tuple stuff. Also
+ * regular tuple vs parenthesised precedence
+ *
+ * Disambiguate between macro and type path via type path being parsed, and
+ * then if '!' found, convert type path to simple path for macro. Usual
+ * disambiguation for tuple vs parenthesised. For ImplTraitType and
+ * TraitObjectType individual disambiguations, they seem more like "special
+ * cases", so probably just try to parse the more general ImplTraitType or
+ * TraitObjectType and return OneBound versions if they satisfy those
+ * criteria. */
- // branch on next token
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
{
case EXCLAM:
- // macro
- return parse_macro_invocation_partial (std::move (path),
- std::move (outer_attrs));
- 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)));
- }
- return parse_struct_expr_struct_partial (std::move (path),
- std::move (outer_attrs));
- }
- 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::unique_ptr<AST::PathInExpression> (
- new AST::PathInExpression (std::move (path)));
- }
- return parse_struct_expr_tuple_partial (std::move (path),
- std::move (outer_attrs));
- 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>
-std::unique_ptr<AST::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?
+ // never type - can't be macro as no path beforehand
+ lexer.skip_token ();
+ return std::unique_ptr<AST::NeverType> (
+ new AST::NeverType (t->get_locus ()));
+ case LEFT_SQUARE:
+ // slice type or array type - requires further disambiguation
+ return parse_slice_or_array_type ();
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::unique_ptr<AST::QualifiedPathInExpression> (
- new 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:
- return parse_grouped_or_tuple_expr (std::move (outer_attrs),
- tok->get_locus ());
-
- /*case PLUS: { // unary plus operator
- // invoke parse_expr recursively with appropriate priority, etc. for
- below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS);
+ {
+ // qualified path in type
+ AST::QualifiedPathInType path = parse_qualified_path_in_type ();
+ if (path.is_error ())
+ {
+ if (save_errors)
+ {
+ Error error (t->get_locus (),
+ "failed to parse qualified path in type");
+ add_error (std::move (error));
+ }
- 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;
- std::unique_ptr<AST::Expr> expr
- = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
-
- 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
- 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::unique_ptr<AST::NegationExpr> (
- new AST::NegationExpr (std::move (expr), 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;
- std::unique_ptr<AST::Expr> expr
- = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
-
- if (expr == nullptr)
- return nullptr;
- // 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::unique_ptr<AST::NegationExpr> (
- new AST::NegationExpr (std::move (expr), NegationOperator::NOT,
- std::move (outer_attrs), tok->get_locus ()));
+ }
+ return std::unique_ptr<AST::QualifiedPathInType> (
+ new AST::QualifiedPathInType (std::move (path)));
}
+ case UNDERSCORE:
+ // inferred type
+ lexer.skip_token ();
+ return std::unique_ptr<AST::InferredType> (
+ new AST::InferredType (t->get_locus ()));
case ASTERISK:
+ // raw pointer type
+ return parse_raw_pointer_type ();
+ case AMP: // does this also include AMP_AMP?
+ case LOGICAL_AND:
+ // reference type
+ return parse_reference_type ();
+ case LIFETIME:
{
- /* 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;
- std::unique_ptr<AST::Expr> expr
- = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
- // FIXME: allow outer attributes on expression
- return std::unique_ptr<AST::DereferenceExpr> (
- new AST::DereferenceExpr (std::move (expr), std::move (outer_attrs),
- tok->get_locus ()));
+ /* probably a lifetime bound, so probably type param bounds in
+ * TraitObjectType */
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+ = parse_type_param_bounds ();
+
+ return std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), t->get_locus (),
+ false));
}
- case AMP:
+ case IDENTIFIER:
+ case SUPER:
+ case SELF:
+ case SELF_ALIAS:
+ case CRATE:
+ case DOLLAR_SIGN:
+ case SCOPE_RESOLUTION:
{
- // (single) "borrow" expression - shared (mutable) or immutable
- std::unique_ptr<AST::Expr> expr = nullptr;
- 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)))
+ // macro invocation or type path - requires further disambiguation.
+ /* for parsing path component of each rule, perhaps parse it as a
+ * typepath and attempt conversion to simplepath if a trailing '!' is
+ * found */
+ /* Type path also includes TraitObjectTypeOneBound BUT if it starts
+ * with it, it is exactly the same as a TypePath syntactically, so
+ * this is a syntactical ambiguity. As such, the parser will parse it
+ * as a TypePath. This, however, does not prevent TraitObjectType from
+ * starting with a typepath. */
+
+ // parse path as type path
+ AST::TypePath path = parse_type_path ();
+ if (path.is_error ())
{
- lexer.skip_token ();
- switch (lexer.peek_token ()->get_id ())
+ if (save_errors)
{
- 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");
+ Error error (t->get_locus (),
+ "failed to parse path as first component of type");
+ add_error (std::move (error));
}
- lexer.skip_token ();
- expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
- raw_borrow = true;
- }
- else if (t->get_id () == MUT)
- {
- lexer.skip_token ();
- expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
- mutability = Mutability::Mut;
- raw_borrow = false;
+
+ return nullptr;
}
- else
+ location_t locus = path.get_locus ();
+
+ // branch on next token
+ t = lexer.peek_token ();
+ switch (t->get_id ())
{
- expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
- raw_borrow = false;
- }
+ case EXCLAM:
+ {
+ // macro invocation
+ // convert to simple path
+ AST::SimplePath macro_path = path.as_simple_path ();
+ if (macro_path.is_empty ())
+ {
+ if (save_errors)
+ {
+ Error error (t->get_locus (),
+ "failed to parse simple path in macro "
+ "invocation (for type)");
+ add_error (std::move (error));
+ }
- // FIXME: allow outer attributes on expression
- return std::unique_ptr<AST::BorrowExpr> (
- new AST::BorrowExpr (std::move (expr), 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;
+ return nullptr;
+ }
- ParseRestrictions entered_from_unary;
- entered_from_unary.entered_from_unary = true;
+ lexer.skip_token ();
- if (lexer.peek_token ()->get_id () == MUT)
- {
- lexer.skip_token ();
- expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
- mutability = Mutability::Mut;
- }
- else
- {
- expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
- mutability = Mutability::Imm;
- }
+ auto tok_tree = parse_delim_token_tree ();
+ if (!tok_tree)
+ return nullptr;
+
+ return AST::MacroInvocation::Regular (
+ AST::MacroInvocData (std::move (macro_path),
+ std::move (tok_tree.value ())),
+ {}, locus);
+ }
+ case PLUS:
+ {
+ // type param bounds
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+
+ // convert type path to trait bound
+ std::unique_ptr<AST::TraitBound> path_bound (
+ new AST::TraitBound (std::move (path), locus, false, false));
+ bounds.push_back (std::move (path_bound));
+
+ /* parse rest of bounds - FIXME: better way to find when to stop
+ * parsing */
+ while (t->get_id () == PLUS)
+ {
+ lexer.skip_token ();
+
+ // parse bound if it exists - if not, assume end of sequence
+ std::unique_ptr<AST::TypeParamBound> bound
+ = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ break;
+ }
+ bounds.push_back (std::move (bound));
+
+ t = lexer.peek_token ();
+ }
- // FIXME: allow outer attributes on expression
- return std::unique_ptr<AST::BorrowExpr> (
- new AST::BorrowExpr (std::move (expr), mutability, false, true,
- std::move (outer_attrs), tok->get_locus ()));
+ return std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), locus, false));
+ }
+ default:
+ // assume that this is a type path and not an error
+ return std::unique_ptr<AST::TypePath> (
+ new AST::TypePath (std::move (path)));
+ }
}
- case OR:
- case PIPE:
- case MOVE:
- // closure expression
- return parse_closure_expr_pratt (tok, std::move (outer_attrs));
- case DOT_DOT:
- // either "range to" or "range full" expressions
- return parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
- case DOT_DOT_EQ:
- // range to inclusive expr
- return parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
- case RETURN_KW:
- // FIXME: is this really a null denotation expression?
- return parse_return_expr (std::move (outer_attrs), tok->get_locus ());
- case TRY:
- // FIXME: is this really a null denotation expression?
- return parse_try_expr (std::move (outer_attrs), tok->get_locus ());
- case BREAK:
- // FIXME: is this really a null denotation expression?
- return parse_break_expr (std::move (outer_attrs), tok->get_locus ());
- 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.
- return parse_block_expr (std::move (outer_attrs), tl::nullopt,
- tok->get_locus ());
- case IF:
- // if or if let, so more lookahead to find out
- if (lexer.peek_token ()->get_id () == LET)
- {
- // if let expr
- return parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
- }
- else
- {
- // if expr
- return parse_if_expr (std::move (outer_attrs), tok->get_locus ());
- }
- case LIFETIME:
- return parse_labelled_loop_expr (tok, std::move (outer_attrs));
- case LOOP:
- return parse_loop_expr (std::move (outer_attrs), tl::nullopt,
- tok->get_locus ());
- case WHILE:
- if (lexer.peek_token ()->get_id () == LET)
+ case LEFT_PAREN:
+ /* tuple type or parenthesised type - requires further disambiguation
+ * (the usual). ok apparently can be a parenthesised TraitBound too, so
+ * could be TraitObjectTypeOneBound or TraitObjectType */
+ return parse_paren_prefixed_type ();
+ case FOR:
+ // TraitObjectTypeOneBound or BareFunctionType
+ return parse_for_prefixed_type ();
+ case ASYNC:
+ case CONST:
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ // bare function type (with no for lifetimes)
+ return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
+ case IMPL:
+ lexer.skip_token ();
+ if (lexer.peek_token ()->get_id () == LIFETIME)
{
- return parse_while_let_loop_expr (std::move (outer_attrs));
+ /* cannot be one bound because lifetime prevents it from being
+ * traitbound */
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+ = parse_type_param_bounds ();
+
+ return std::unique_ptr<AST::ImplTraitType> (
+ new AST::ImplTraitType (std::move (bounds), t->get_locus ()));
}
else
{
- return parse_while_loop_expr (std::move (outer_attrs), tl::nullopt,
- tok->get_locus ());
- }
- case FOR:
- return parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
- case MATCH_KW:
- // also an expression with block
- return parse_match_expr (std::move (outer_attrs), tok->get_locus ());
- case LEFT_SQUARE:
- // array definition expr (not indexing)
- return parse_array_expr (std::move (outer_attrs), tok->get_locus ());
- case UNSAFE:
- return parse_unsafe_block_expr (std::move (outer_attrs),
- tok->get_locus ());
- case BOX:
- return parse_box_expr (std::move (outer_attrs), tok->get_locus ());
- 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 nullptr;
- case CONST:
- return parse_const_block_expr (std::move (outer_attrs),
- tok->get_locus ());
- 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 nullptr;
- }
-}
+ // should be trait bound, so parse trait bound
+ std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+ if (initial_bound == nullptr)
+ {
+ if (save_errors)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse ImplTraitType initial bound");
+ add_error (std::move (error));
+ }
-/* 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>
-std::unique_ptr<AST::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::unique_ptr<AST::ErrorPropagationExpr> (
- new 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 nullptr;
+ }
- return nullptr;
- 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 */
+ location_t locus = t->get_locus ();
- 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)
+ // short cut if next token isn't '+'
+ t = lexer.peek_token ();
+ if (t->get_id () != PLUS)
+ {
+ return std::unique_ptr<AST::ImplTraitTypeOneBound> (
+ new AST::ImplTraitTypeOneBound (std::move (initial_bound),
+ locus));
+ }
+
+ // parse additional type param bounds
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+ bounds.push_back (std::move (initial_bound));
+ while (t->get_id () == PLUS)
+ {
+ lexer.skip_token ();
+
+ // parse bound if it exists
+ std::unique_ptr<AST::TypeParamBound> bound
+ = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ // not an error as trailing plus may exist
+ break;
+ }
+ bounds.push_back (std::move (bound));
+
+ t = lexer.peek_token ();
+ }
+
+ return std::unique_ptr<AST::ImplTraitType> (
+ new AST::ImplTraitType (std::move (bounds), locus));
+ }
+ case DYN:
+ case QUESTION_MARK:
+ {
+ // either TraitObjectType or TraitObjectTypeOneBound
+ bool has_dyn = false;
+ if (t->get_id () == DYN)
{
- // 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);
+ lexer.skip_token ();
+ has_dyn = true;
}
- else if (next_tok->get_id () == IDENTIFIER
- && lexer.peek_token (1)->get_id () != LEFT_PAREN
- && lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION)
+
+ if (lexer.peek_token ()->get_id () == LIFETIME)
{
- /* 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);
+ /* cannot be one bound because lifetime prevents it from being
+ * traitbound */
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+ = parse_type_param_bounds ();
+
+ return std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), t->get_locus (),
+ has_dyn));
}
else
{
- // method call (probably)
- return parse_method_call_expr (tok, std::move (left),
- std::move (outer_attrs),
- restrictions);
+ // should be trait bound, so parse trait bound
+ std::unique_ptr<AST::TraitBound> initial_bound
+ = parse_trait_bound ();
+ if (initial_bound == nullptr)
+ {
+ if (save_errors)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse TraitObjectType initial bound");
+ add_error (std::move (error));
+ }
+
+ return nullptr;
+ }
+
+ // short cut if next token isn't '+'
+ t = lexer.peek_token ();
+ if (t->get_id () != PLUS)
+ {
+ // convert trait bound to value object
+ AST::TraitBound value_bound (*initial_bound);
+
+ // DEBUG: removed as unique ptr, so should auto delete
+ // delete initial_bound;
+
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (std::move (value_bound),
+ t->get_locus (), has_dyn));
+ }
+
+ // parse additional type param bounds
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+ bounds.push_back (std::move (initial_bound));
+ while (t->get_id () == PLUS)
+ {
+ lexer.skip_token ();
+
+ // parse bound if it exists
+ std::unique_ptr<AST::TypeParamBound> bound
+ = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ // not an error as trailing plus may exist
+ break;
+ }
+ bounds.push_back (std::move (bound));
+
+ t = lexer.peek_token ();
+ }
+
+ return std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), t->get_locus (),
+ has_dyn));
}
}
- 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 ()));
+ if (save_errors)
+ add_error (Error (t->get_locus (), "unrecognised token %qs in type",
+ t->get_token_description ()));
return nullptr;
}
}
-/* 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).
+/* Parses a type that has '(' as its first character. Returns a tuple type,
+ * parenthesised type, TraitObjectTypeOneBound, or TraitObjectType depending
+ * on following characters. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr (
- const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
- AST::ArithmeticOrLogicalExpr::ExprType expr_type,
- ParseRestrictions restrictions)
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_paren_prefixed_type ()
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
- AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
-
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ /* NOTE: Syntactical ambiguity of a parenthesised trait bound is considered
+ * a trait bound, not a parenthesised type, so that it can still be used in
+ * type param bounds. */
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- expr_type, locus));
-}
+ /* NOTE: this implementation is really shit but I couldn't think of a better
+ * one. It requires essentially breaking polymorphism and downcasting via
+ * virtual method abuse, as it was copied from the rustc implementation (in
+ * which types are reified due to tagged union), after a more OOP attempt by
+ * me failed. */
+ location_t left_delim_locus = lexer.peek_token ()->get_locus ();
-// Parses a binary addition expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ // skip left delim
+ lexer.skip_token ();
+ /* while next token isn't close delim, parse comma-separated types, saving
+ * whether trailing comma happens */
+ const_TokenPtr t = lexer.peek_token ();
+ bool trailing_comma = true;
+ std::vector<std::unique_ptr<AST::Type>> types;
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ while (t->get_id () != RIGHT_PAREN)
+ {
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse type inside parentheses (probably "
+ "tuple or parenthesised)");
+ add_error (std::move (error));
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::ADD, locus));
-}
+ return nullptr;
+ }
+ types.push_back (std::move (type));
-// Parses a binary subtraction expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ t = lexer.peek_token ();
+ if (t->get_id () != COMMA)
+ {
+ trailing_comma = false;
+ break;
+ }
+ lexer.skip_token ();
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ t = lexer.peek_token ();
+ }
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::SUBTRACT,
- locus));
-}
+ if (!skip_token (RIGHT_PAREN))
+ {
+ return nullptr;
+ }
-// Parses a binary multiplication expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ // if only one type and no trailing comma, then not a tuple type
+ if (types.size () == 1 && !trailing_comma)
+ {
+ // must be a TraitObjectType (with more than one bound)
+ if (lexer.peek_token ()->get_id () == PLUS)
+ {
+ // create type param bounds vector
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ // HACK: convert type to traitbound and add to bounds
+ std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
+ std::unique_ptr<AST::TraitBound> converted_bound (
+ released_ptr->to_trait_bound (true));
+ if (converted_bound == nullptr)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to hackily converted parsed type to trait bound");
+ add_error (std::move (error));
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::MULTIPLY,
- locus));
-}
+ return nullptr;
+ }
+ bounds.push_back (std::move (converted_bound));
-// Parses a binary division expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ t = lexer.peek_token ();
+ while (t->get_id () == PLUS)
+ {
+ lexer.skip_token ();
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ // attempt to parse typeparambound
+ std::unique_ptr<AST::TypeParamBound> bound
+ = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ // not an error if null
+ break;
+ }
+ bounds.push_back (std::move (bound));
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::DIVIDE,
- locus));
-}
+ t = lexer.peek_token ();
+ }
-// Parses a binary modulo expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ return std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), left_delim_locus,
+ false));
+ }
+ else
+ {
+ // release vector pointer
+ std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
+ /* HACK: attempt to convert to trait bound. if fails, parenthesised
+ * type */
+ std::unique_ptr<AST::TraitBound> converted_bound (
+ released_ptr->to_trait_bound (true));
+ if (converted_bound == nullptr)
+ {
+ // parenthesised type
+ return std::unique_ptr<AST::ParenthesisedType> (
+ new AST::ParenthesisedType (std::move (released_ptr),
+ left_delim_locus));
+ }
+ else
+ {
+ // trait object type (one bound)
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ // get value semantics trait bound
+ AST::TraitBound value_bound (*converted_bound);
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::MODULUS,
- locus));
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (value_bound,
+ left_delim_locus));
+ }
+ }
+ }
+ else
+ {
+ return std::unique_ptr<AST::TupleType> (
+ new AST::TupleType (std::move (types), left_delim_locus));
+ }
+ /* TODO: ensure that this ensures that dynamic dispatch for traits is not
+ * lost somehow */
}
-/* Parses a binary bitwise (or eager logical) and expression (with Pratt
- * parsing). */
+/* Parses a type that has 'for' as its first character. This means it has a
+ * "for lifetimes", so returns either a BareFunctionType, TraitObjectType, or
+ * TraitObjectTypeOneBound depending on following characters. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_for_prefixed_type ()
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
-
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ location_t for_locus = lexer.peek_token ()->get_locus ();
+ // parse for lifetimes in type
+ std::vector<AST::LifetimeParam> for_lifetimes = parse_for_lifetimes ();
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::BITWISE_AND,
- locus));
-}
+ // branch on next token - either function or a trait type
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case ASYNC:
+ case CONST:
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ return parse_bare_function_type (std::move (for_lifetimes));
+ case SCOPE_RESOLUTION:
+ case IDENTIFIER:
+ case SUPER:
+ case SELF:
+ case SELF_ALIAS:
+ case CRATE:
+ case DOLLAR_SIGN:
+ {
+ // path, so trait type
-/* Parses a binary bitwise (or eager logical) or expression (with Pratt
- * parsing). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ // parse type path to finish parsing trait bound
+ AST::TypePath path = parse_type_path ();
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ t = lexer.peek_token ();
+ if (t->get_id () != PLUS)
+ {
+ // must be one-bound trait type
+ // create trait bound value object
+ AST::TraitBound bound (std::move (path), for_locus, false, false,
+ std::move (for_lifetimes));
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::BITWISE_OR,
- locus));
-}
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (std::move (bound), for_locus));
+ }
-/* Parses a binary bitwise (or eager logical) xor expression (with Pratt
- * parsing). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ /* more than one bound trait type (or at least parsed as it - could be
+ * trailing '+') create trait bound pointer and bounds */
+ std::unique_ptr<AST::TraitBound> initial_bound (
+ new AST::TraitBound (std::move (path), for_locus, false, false,
+ std::move (for_lifetimes)));
+ std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+ bounds.push_back (std::move (initial_bound));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ while (t->get_id () == PLUS)
+ {
+ lexer.skip_token ();
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::BITWISE_XOR,
- locus));
-}
+ // parse type param bound if it exists
+ std::unique_ptr<AST::TypeParamBound> bound
+ = parse_type_param_bound ();
+ if (bound == nullptr)
+ {
+ // not an error - e.g. trailing plus
+ return nullptr;
+ }
+ bounds.push_back (std::move (bound));
-// Parses a binary left shift expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ t = lexer.peek_token ();
+ }
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return std::unique_ptr<AST::TraitObjectType> (
+ new AST::TraitObjectType (std::move (bounds), for_locus, false));
+ }
+ default:
+ // error
+ add_error (Error (t->get_locus (),
+ "unrecognised token %qs in bare function type or trait "
+ "object type or trait object type one bound",
+ t->get_token_description ()));
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::LEFT_SHIFT,
- locus));
+ return nullptr;
+ }
}
-// Parses a binary right shift expression (with Pratt parsing).
+// Parses a maybe named param used in bare function types.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-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)
+AST::MaybeNamedParam
+Parser<ManagedTokenSource>::parse_maybe_named_param (AST::AttrVec outer_attrs)
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
-
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ /* Basically guess that param is named if first token is identifier or
+ * underscore and second token is semicolon. This should probably have no
+ * exceptions. rustc uses backtracking to parse these, but at the time of
+ * writing gccrs has no backtracking capabilities. */
+ const_TokenPtr current = lexer.peek_token ();
+ const_TokenPtr next = lexer.peek_token (1);
- return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
- ArithmeticOrLogicalOperator::RIGHT_SHIFT,
- locus));
-}
+ Identifier name;
+ AST::MaybeNamedParam::ParamKind kind = AST::MaybeNamedParam::UNNAMED;
-/* 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)
+ if (current->get_id () == IDENTIFIER && next->get_id () == COLON)
{
- 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;
+ // named param
+ name = {current};
+ kind = AST::MaybeNamedParam::IDENTIFIER;
+ lexer.skip_token (1);
+ }
+ else if (current->get_id () == UNDERSCORE && next->get_id () == COLON)
+ {
+ // wildcard param
+ name = {Values::Keywords::UNDERSCORE, current->get_locus ()};
+ kind = AST::MaybeNamedParam::WILDCARD;
+ lexer.skip_token (1);
}
-}
-/* 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>
-std::unique_ptr<AST::ComparisonExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (get_lbp_for_comparison_expr (expr_type), AST::AttrVec (),
- restrictions);
- if (right == nullptr)
- return nullptr;
+ // parse type (required)
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse type in maybe named param");
+ add_error (std::move (error));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return AST::MaybeNamedParam::create_error ();
+ }
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right), expr_type,
- locus));
+ return AST::MaybeNamedParam (std::move (name), kind, std::move (type),
+ std::move (outer_attrs), current->get_locus ());
}
-// Parses a binary equal to expression (with Pratt parsing).
+/* Parses a bare function type (with the given for lifetimes for convenience -
+ * does not parse them itself). */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
-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)
+std::unique_ptr<AST::BareFunctionType>
+Parser<ManagedTokenSource>::parse_bare_function_type (
+ std::vector<AST::LifetimeParam> for_lifetimes)
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ // TODO: pass in for lifetime location as param
+ location_t best_try_locus = lexer.peek_token ()->get_locus ();
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right),
- ComparisonOperator::EQUAL, locus));
-}
+ if (!skip_token (FN_KW))
+ return nullptr;
-// Parses a binary not equal to expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
- if (right == nullptr)
+ if (!skip_token (LEFT_PAREN))
return nullptr;
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ // parse function params, if they exist
+ std::vector<AST::MaybeNamedParam> params;
+ bool is_variadic = false;
+ AST::AttrVec variadic_attrs;
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right),
- ComparisonOperator::NOT_EQUAL, locus));
-}
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_PAREN)
+ {
+ AST::AttrVec temp_attrs = parse_outer_attributes ();
-// Parses a binary greater than expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ if (lexer.peek_token ()->get_id () == ELLIPSIS)
+ {
+ lexer.skip_token ();
+ is_variadic = true;
+ variadic_attrs = std::move (temp_attrs);
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ t = lexer.peek_token ();
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right),
- ComparisonOperator::GREATER_THAN, locus));
-}
+ if (t->get_id () != RIGHT_PAREN)
+ {
+ Error error (t->get_locus (),
+ "expected right parentheses after variadic in maybe "
+ "named function "
+ "parameters, found %qs",
+ t->get_token_description ());
+ add_error (std::move (error));
-// Parses a binary less than expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ return nullptr;
+ }
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ break;
+ }
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right),
- ComparisonOperator::LESS_THAN, locus));
-}
+ AST::MaybeNamedParam param
+ = parse_maybe_named_param (std::move (temp_attrs));
+ if (param.is_error ())
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse maybe named param in bare function type");
+ add_error (std::move (error));
-// Parses a binary greater than or equal to expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ return nullptr;
+ }
+ params.push_back (std::move (param));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ if (lexer.peek_token ()->get_id () != COMMA)
+ break;
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right),
- ComparisonOperator::GREATER_OR_EQUAL, locus));
-}
+ lexer.skip_token ();
+ t = lexer.peek_token ();
+ }
-// Parses a binary less than or equal to expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
- if (right == nullptr)
+ if (!skip_token (RIGHT_PAREN))
return nullptr;
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
-
- return std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (std::move (left), std::move (right),
- ComparisonOperator::LESS_OR_EQUAL, locus));
-}
+ // bare function return type, if exists
+ std::unique_ptr<AST::TypeNoBounds> return_type = nullptr;
+ if (lexer.peek_token ()->get_id () == RETURN_TYPE)
+ {
+ lexer.skip_token ();
-// Parses a binary lazy boolean or expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::LazyBooleanExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ // parse required TypeNoBounds
+ return_type = parse_type_no_bounds ();
+ if (return_type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse return type (type no bounds) in bare "
+ "function type");
+ add_error (std::move (error));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return nullptr;
+ }
+ }
- return std::unique_ptr<AST::LazyBooleanExpr> (
- new AST::LazyBooleanExpr (std::move (left), std::move (right),
- LazyBooleanOperator::LOGICAL_OR, locus));
+ return std::unique_ptr<AST::BareFunctionType> (
+ new AST::BareFunctionType (std::move (for_lifetimes),
+ std::move (qualifiers), std::move (params),
+ is_variadic, std::move (variadic_attrs),
+ std::move (return_type), best_try_locus));
}
-// Parses a binary lazy boolean and expression (with Pratt parsing).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::LazyBooleanExpr>
-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)
+std::unique_ptr<AST::ReferenceType>
+Parser<ManagedTokenSource>::parse_reference_type_inner (location_t locus)
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
+ // parse optional lifetime
+ AST::Lifetime lifetime = AST::Lifetime::elided ();
+ if (lexer.peek_token ()->get_id () == LIFETIME)
+ {
+ auto parsed_lifetime = parse_lifetime (true);
+ if (parsed_lifetime)
+ {
+ lifetime = parsed_lifetime.value ();
+ }
+ else
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime in reference type");
+ add_error (std::move (error));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return nullptr;
+ }
+ }
- return std::unique_ptr<AST::LazyBooleanExpr> (
- new AST::LazyBooleanExpr (std::move (left), std::move (right),
- LazyBooleanOperator::LOGICAL_AND, locus));
-}
+ bool is_mut = false;
+ if (lexer.peek_token ()->get_id () == MUT)
+ {
+ lexer.skip_token ();
+ is_mut = true;
+ }
-// Parses a pseudo-binary infix type cast expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeCastExpr>
-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)
+ // parse type no bounds, which is required
std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
if (type == nullptr)
- return nullptr;
- // FIXME: how do I get precedence put in here?
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse referenced type in reference type");
+ add_error (std::move (error));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = expr_to_cast->get_locus ();
+ return nullptr;
+ }
- return std::unique_ptr<AST::TypeCastExpr> (
- new AST::TypeCastExpr (std::move (expr_to_cast), std::move (type), locus));
+ return std::unique_ptr<AST::ReferenceType> (
+ new AST::ReferenceType (is_mut, std::move (type), locus,
+ std::move (lifetime)));
}
-// Parses a binary assignment expression (with Pratt parsing).
+// Parses a reference type (mutable or immutable, with given lifetime).
template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssignmentExpr>
-Parser<ManagedTokenSource>::parse_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
- AST::AttrVec outer_attrs, ParseRestrictions restrictions)
+std::unique_ptr<AST::ReferenceType>
+Parser<ManagedTokenSource>::parse_reference_type ()
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // 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::unique_ptr<AST::AssignmentExpr> (
- new AST::AssignmentExpr (std::move (left), std::move (right),
- std::move (outer_attrs), locus));
-}
+ auto t = lexer.peek_token ();
+ auto locus = t->get_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)
+ switch (t->get_id ())
{
- 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;
+ case AMP:
+ skip_token (AMP);
+ return parse_reference_type_inner (locus);
+ case LOGICAL_AND:
+ skip_token (LOGICAL_AND);
+ return std::unique_ptr<AST::ReferenceType> (
+ new AST::ReferenceType (false, parse_reference_type_inner (locus),
+ locus));
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>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
- AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // 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::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- expr_type, locus));
-}
-
-// Parses a binary add-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // 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::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::ADD, locus));
-}
-
-// Parses a binary minus-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // 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::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::SUBTRACT,
- locus));
-}
-
-// Parses a binary multiplication-assignment expression (with Pratt parsing).
+// Parses a raw (unsafe) pointer type.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
+std::unique_ptr<AST::RawPointerType>
+Parser<ManagedTokenSource>::parse_raw_pointer_type ()
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // 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::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::MULTIPLY,
- locus));
-}
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (ASTERISK);
-// Parses a binary division-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+ AST::RawPointerType::PointerType kind = AST::RawPointerType::CONST;
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ // branch on next token for pointer kind info
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case MUT:
+ kind = AST::RawPointerType::MUT;
+ lexer.skip_token ();
+ break;
+ case CONST:
+ kind = AST::RawPointerType::CONST;
+ lexer.skip_token ();
+ break;
+ default:
+ add_error (Error (t->get_locus (),
+ "unrecognised token %qs in raw pointer type",
+ t->get_token_description ()));
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::DIVIDE,
- locus));
-}
+ return nullptr;
+ }
-// Parses a binary modulo-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+ // parse type no bounds (required)
+ std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+ if (type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse pointed type of raw pointer type");
+ add_error (std::move (error));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return nullptr;
+ }
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::MODULUS,
- locus));
+ return std::unique_ptr<AST::RawPointerType> (
+ new AST::RawPointerType (kind, std::move (type), locus));
}
-// Parses a binary and-assignment expression (with Pratt parsing).
+/* Parses a slice or array type, depending on following arguments (as
+ * lookahead is not possible). */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
+std::unique_ptr<AST::TypeNoBounds>
+Parser<ManagedTokenSource>::parse_slice_or_array_type ()
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // 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 ();
+ location_t locus = lexer.peek_token ()->get_locus ();
+ skip_token (LEFT_SQUARE);
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::BITWISE_AND,
- locus));
-}
+ // parse inner type (required)
+ std::unique_ptr<AST::Type> inner_type = parse_type ();
+ if (inner_type == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse inner type in slice or array type");
+ add_error (std::move (error));
-// Parses a binary or-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+ return nullptr;
+ }
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ // branch on next token
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case RIGHT_SQUARE:
+ // slice type
+ lexer.skip_token ();
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::BITWISE_OR,
- locus));
-}
+ return std::unique_ptr<AST::SliceType> (
+ new AST::SliceType (std::move (inner_type), locus));
+ case SEMICOLON:
+ {
+ // array type
+ lexer.skip_token ();
-// Parses a binary xor-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+ // parse required array size expression
+ auto size = parse_anon_const ();
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ if (!size)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse size expression in array type");
+ add_error (std::move (error));
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::BITWISE_XOR,
- locus));
-}
+ return nullptr;
+ }
-// Parses a binary left shift-assignment expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+ if (!skip_token (RIGHT_SQUARE))
+ {
+ return nullptr;
+ }
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return std::unique_ptr<AST::ArrayType> (
+ new AST::ArrayType (std::move (inner_type), std::move (*size),
+ locus));
+ }
+ default:
+ // error
+ add_error (
+ Error (t->get_locus (),
+ "unrecognised token %qs in slice or array type after inner type",
+ t->get_token_description ()));
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::LEFT_SHIFT,
- locus));
+ return nullptr;
+ }
}
-// Parses a binary right shift-assignment expression (with Pratt parsing).
+// Parses a type, taking into account type boundary disambiguation.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
-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)
+std::unique_ptr<AST::TypeNoBounds>
+Parser<ManagedTokenSource>::parse_type_no_bounds ()
{
- // parse RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case EXCLAM:
+ // never type - can't be macro as no path beforehand
+ lexer.skip_token ();
+ return std::unique_ptr<AST::NeverType> (
+ new AST::NeverType (t->get_locus ()));
+ case LEFT_SQUARE:
+ // slice type or array type - requires further disambiguation
+ return parse_slice_or_array_type ();
+ case LEFT_SHIFT:
+ case LEFT_ANGLE:
+ {
+ // qualified path in type
+ AST::QualifiedPathInType path = parse_qualified_path_in_type ();
+ if (path.is_error ())
+ {
+ Error error (t->get_locus (),
+ "failed to parse qualified path in type");
+ add_error (std::move (error));
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return nullptr;
+ }
+ return std::unique_ptr<AST::QualifiedPathInType> (
+ new AST::QualifiedPathInType (std::move (path)));
+ }
+ case UNDERSCORE:
+ // inferred type
+ lexer.skip_token ();
+ return std::unique_ptr<AST::InferredType> (
+ new AST::InferredType (t->get_locus ()));
+ case ASTERISK:
+ // raw pointer type
+ return parse_raw_pointer_type ();
+ case AMP: // does this also include AMP_AMP? Yes! Which is... LOGICAL_AND?
+ case LOGICAL_AND:
+ // reference type
+ return parse_reference_type ();
+ case LIFETIME:
+ /* probably a lifetime bound, so probably type param bounds in
+ * TraitObjectType. this is not allowed, but detection here for error
+ * message */
+ add_error (Error (t->get_locus (),
+ "lifetime bounds (i.e. in type param bounds, in "
+ "TraitObjectType) are not allowed as TypeNoBounds"));
- return std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
- CompoundAssignmentOperator::RIGHT_SHIFT,
- locus));
-}
+ return nullptr;
+ case IDENTIFIER:
+ case SUPER:
+ case SELF:
+ case SELF_ALIAS:
+ case CRATE:
+ case DOLLAR_SIGN:
+ case SCOPE_RESOLUTION:
+ {
+ // macro invocation or type path - requires further disambiguation.
+ /* for parsing path component of each rule, perhaps parse it as a
+ * typepath and attempt conversion to simplepath if a trailing '!' is
+ * found */
+ /* Type path also includes TraitObjectTypeOneBound BUT if it starts
+ * with it, it is exactly the same as a TypePath syntactically, so
+ * this is a syntactical ambiguity. As such, the parser will parse it
+ * as a TypePath. This, however, does not prevent TraitObjectType from
+ * starting with a typepath. */
-// Parses a postfix unary await expression (with Pratt parsing).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::AwaitExpr>
-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));
+ // parse path as type path
+ AST::TypePath path = parse_type_path ();
+ if (path.is_error ())
+ {
+ Error error (
+ t->get_locus (),
+ "failed to parse path as first component of type no bounds");
+ add_error (std::move (error));
- // skip somewhere?
- return nullptr;
- }
+ return nullptr;
+ }
+ location_t locus = path.get_locus ();
- // TODO: check inside async block in semantic analysis
- location_t locus = expr_to_await->get_locus ();
+ // branch on next token
+ t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case EXCLAM:
+ {
+ // macro invocation
+ // convert to simple path
+ AST::SimplePath macro_path = path.as_simple_path ();
+ if (macro_path.is_empty ())
+ {
+ Error error (t->get_locus (),
+ "failed to parse simple path in macro "
+ "invocation (for type)");
+ add_error (std::move (error));
- return std::unique_ptr<AST::AwaitExpr> (
- new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs),
- locus));
-}
+ return nullptr;
+ }
-/* Parses an exclusive range ('..') in left denotation position (i.e.
- * RangeFromExpr or RangeFromToExpr). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeExpr>
-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;
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+ lexer.skip_token ();
- location_t locus = left->get_locus ();
+ auto tok_tree = parse_delim_token_tree ();
+ if (!tok_tree)
+ return nullptr;
- if (right == nullptr)
- {
- // range from expr
- return std::unique_ptr<AST::RangeFromExpr> (
- new AST::RangeFromExpr (std::move (left), locus));
- }
- else
- {
- return std::unique_ptr<AST::RangeFromToExpr> (
- new AST::RangeFromToExpr (std::move (left), std::move (right), locus));
- }
- // FIXME: make non-associative
-}
+ return AST::MacroInvocation::Regular (
+ AST::MacroInvocData (std::move (macro_path),
+ std::move (tok_tree.value ())),
+ {}, locus);
+ }
+ default:
+ // assume that this is a type path and not an error
+ return std::unique_ptr<AST::TypePath> (
+ new AST::TypePath (std::move (path)));
+ }
+ }
+ case LEFT_PAREN:
+ /* tuple type or parenthesised type - requires further disambiguation
+ * (the usual). ok apparently can be a parenthesised TraitBound too, so
+ * could be TraitObjectTypeOneBound */
+ return parse_paren_prefixed_type_no_bounds ();
+ case FOR:
+ case ASYNC:
+ case CONST:
+ case UNSAFE:
+ case EXTERN_KW:
+ case FN_KW:
+ // bare function type (with no for lifetimes)
+ return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
+ case IMPL:
+ lexer.skip_token ();
+ if (lexer.peek_token ()->get_id () == LIFETIME)
+ {
+ /* cannot be one bound because lifetime prevents it from being
+ * traitbound not allowed as type no bounds, only here for error
+ * message */
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "lifetime (probably lifetime bound, in type param "
+ "bounds, in ImplTraitType) is not allowed in TypeNoBounds");
+ add_error (std::move (error));
-/* Parses an exclusive range ('..') in null denotation position (i.e.
- * RangeToExpr or RangeFullExpr). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeExpr>
-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;
+ return nullptr;
+ }
+ else
+ {
+ // should be trait bound, so parse trait bound
+ std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+ if (initial_bound == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse ImplTraitTypeOneBound bound");
+ add_error (std::move (error));
- // FIXME: this probably parses expressions accidently or whatever
- // try parsing RHS (as tok has already been consumed in parse_expression)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+ return nullptr;
+ }
- location_t locus = tok->get_locus ();
+ location_t locus = t->get_locus ();
- if (right == nullptr)
- {
- // range from expr
- return std::unique_ptr<AST::RangeFullExpr> (
- new AST::RangeFullExpr (locus));
- }
- else
- {
- return std::unique_ptr<AST::RangeToExpr> (
- new AST::RangeToExpr (std::move (right), locus));
- }
- // FIXME: make non-associative
-}
+ // ensure not a trait with multiple bounds
+ t = lexer.peek_token ();
+ if (t->get_id () == PLUS)
+ {
+ Error error (t->get_locus (),
+ "plus after trait bound means an ImplTraitType, "
+ "which is not allowed as a TypeNoBounds");
+ add_error (std::move (error));
-// Parses a full binary range inclusive expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeFromToInclExpr>
-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)
- std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
- if (right == nullptr)
- return nullptr;
- // FIXME: make non-associative
+ return nullptr;
+ }
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = left->get_locus ();
+ return std::unique_ptr<AST::ImplTraitTypeOneBound> (
+ new AST::ImplTraitTypeOneBound (std::move (initial_bound), locus));
+ }
+ case DYN:
+ case QUESTION_MARK:
+ {
+ // either TraitObjectTypeOneBound
+ bool has_dyn = false;
+ if (t->get_id () == DYN)
+ {
+ lexer.skip_token ();
+ has_dyn = true;
+ }
- return std::unique_ptr<AST::RangeFromToInclExpr> (
- new AST::RangeFromToInclExpr (std::move (left), std::move (right), locus));
-}
+ if (lexer.peek_token ()->get_id () == LIFETIME)
+ {
+ /* means that cannot be TraitObjectTypeOneBound - so here for
+ * error message */
+ Error error (lexer.peek_token ()->get_locus (),
+ "lifetime as bound in TraitObjectTypeOneBound "
+ "is not allowed, so cannot be TypeNoBounds");
+ add_error (std::move (error));
-// Parses an inclusive range-to prefix unary expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeToInclExpr>
-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)
- std::unique_ptr<AST::Expr> right = parse_expr (LBP_DOT_DOT_EQ);
- if (right == nullptr)
- return nullptr;
- // FIXME: make non-associative
+ return nullptr;
+ }
- // TODO: check types. actually, do so during semantic analysis
+ // should be trait bound, so parse trait bound
+ std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+ if (initial_bound == nullptr)
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse TraitObjectTypeOneBound initial bound");
+ add_error (std::move (error));
- return std::unique_ptr<AST::RangeToInclExpr> (
- new AST::RangeToInclExpr (std::move (right), tok->get_locus ()));
-}
+ return nullptr;
+ }
-// Parses a pseudo-binary infix tuple index expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::TupleIndexExpr>
-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 nullptr;
- }
- std::string index = index_tok->get_str ();
+ location_t locus = t->get_locus ();
- // 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 ());
+ // detect error with plus as next token
+ t = lexer.peek_token ();
+ if (t->get_id () == PLUS)
+ {
+ Error error (t->get_locus (),
+ "plus after trait bound means a TraitObjectType, "
+ "which is not allowed as a TypeNoBounds");
+ add_error (std::move (error));
- location_t locus = tuple_expr->get_locus ();
+ return nullptr;
+ }
- return std::unique_ptr<AST::TupleIndexExpr> (
- new AST::TupleIndexExpr (std::move (tuple_expr), index_int,
- std::move (outer_attrs), locus));
-}
+ // convert trait bound to value object
+ AST::TraitBound value_bound (*initial_bound);
-// Parses a pseudo-binary infix array (or slice) index expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArrayIndexExpr>
-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
- std::unique_ptr<AST::Expr> index_expr = parse_expr ();
- if (index_expr == nullptr)
- return nullptr;
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (std::move (value_bound), locus,
+ has_dyn));
+ }
+ default:
+ add_error (Error (t->get_locus (),
+ "unrecognised token %qs in type no bounds",
+ t->get_token_description ()));
- // skip ']' at end of array
- if (!skip_token (RIGHT_SQUARE))
- {
- // skip somewhere?
return nullptr;
}
-
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = array_expr->get_locus ();
-
- return std::unique_ptr<AST::ArrayIndexExpr> (
- new AST::ArrayIndexExpr (std::move (array_expr), std::move (index_expr),
- std::move (outer_attrs), locus));
-}
-
-// Parses a pseudo-binary infix struct field access expression.
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::FieldAccessExpr>
-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 nullptr;
-
- Identifier ident{ident_tok};
-
- location_t locus = struct_expr->get_locus ();
-
- // TODO: check types. actually, do so during semantic analysis
- return std::unique_ptr<AST::FieldAccessExpr> (
- new AST::FieldAccessExpr (std::move (struct_expr), std::move (ident),
- std::move (outer_attrs), locus));
}
-// Parses a pseudo-binary infix method call expression.
+// Parses a type no bounds beginning with '('.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MethodCallExpr>
-Parser<ManagedTokenSource>::parse_method_call_expr (
- const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
- AST::AttrVec outer_attrs, ParseRestrictions)
+std::unique_ptr<AST::TypeNoBounds>
+Parser<ManagedTokenSource>::parse_paren_prefixed_type_no_bounds ()
{
- // 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 nullptr;
- }
+ /* NOTE: this could probably be parsed without the HACK solution of
+ * parse_paren_prefixed_type, but I was lazy. So FIXME for future.*/
- // skip left parentheses
- if (!skip_token (LEFT_PAREN))
- {
- return nullptr;
- }
+ /* NOTE: again, syntactical ambiguity of a parenthesised trait bound is
+ * considered a trait bound, not a parenthesised type, so that it can still
+ * be used in type param bounds. */
- // parse method params (if they exist)
- std::vector<std::unique_ptr<AST::Expr>> params;
+ location_t left_paren_locus = lexer.peek_token ()->get_locus ();
+ // skip left delim
+ lexer.skip_token ();
+ /* while next token isn't close delim, parse comma-separated types, saving
+ * whether trailing comma happens */
const_TokenPtr t = lexer.peek_token ();
+ bool trailing_comma = true;
+ std::vector<std::unique_ptr<AST::Type>> types;
+
while (t->get_id () != RIGHT_PAREN)
{
- std::unique_ptr<AST::Expr> param = parse_expr ();
- if (param == nullptr)
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
{
Error error (t->get_locus (),
- "failed to parse method param in method call");
+ "failed to parse type inside parentheses (probably "
+ "tuple or parenthesised)");
add_error (std::move (error));
return nullptr;
}
- params.push_back (std::move (param));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ types.push_back (std::move (type));
+ t = lexer.peek_token ();
+ if (t->get_id () != COMMA)
+ {
+ trailing_comma = false;
+ break;
+ }
lexer.skip_token ();
+
t = lexer.peek_token ();
}
- // skip right paren
if (!skip_token (RIGHT_PAREN))
{
return nullptr;
}
- // TODO: check types. actually do so in semantic analysis pass.
- location_t locus = receiver_expr->get_locus ();
-
- return std::unique_ptr<AST::MethodCallExpr> (
- new 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>
-std::unique_ptr<AST::CallExpr>
-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)
+ // if only one type and no trailing comma, then not a tuple type
+ if (types.size () == 1 && !trailing_comma)
{
- std::unique_ptr<AST::Expr> param = parse_expr ();
- if (param == nullptr)
+ // must be a TraitObjectType (with more than one bound)
+ if (lexer.peek_token ()->get_id () == PLUS)
{
- Error error (t->get_locus (),
- "failed to parse function param in function call");
+ // error - this is not allowed for type no bounds
+ Error error (lexer.peek_token ()->get_locus (),
+ "plus (implying TraitObjectType as type param "
+ "bounds) is not allowed in type no bounds");
add_error (std::move (error));
return nullptr;
}
- params.push_back (std::move (param));
+ else
+ {
+ // release vector pointer
+ std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
+ /* HACK: attempt to convert to trait bound. if fails, parenthesised
+ * type */
+ std::unique_ptr<AST::TraitBound> converted_bound (
+ released_ptr->to_trait_bound (true));
+ if (converted_bound == nullptr)
+ {
+ // parenthesised type
+ return std::unique_ptr<AST::ParenthesisedType> (
+ new AST::ParenthesisedType (std::move (released_ptr),
+ left_paren_locus));
+ }
+ else
+ {
+ // trait object type (one bound)
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
+ // get value semantics trait bound
+ AST::TraitBound value_bound (*converted_bound);
- lexer.skip_token ();
- t = lexer.peek_token ();
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (value_bound,
+ left_paren_locus));
+ }
+ }
}
-
- // skip ')' at end of param list
- if (!skip_token (RIGHT_PAREN))
+ else
{
- // skip somewhere?
- return nullptr;
+ return std::unique_ptr<AST::TupleType> (
+ new AST::TupleType (std::move (types), left_paren_locus));
}
-
- // TODO: check types. actually, do so during semantic analysis
- location_t locus = function_expr->get_locus ();
-
- return std::unique_ptr<AST::CallExpr> (
- new AST::CallExpr (std::move (function_expr), std::move (params),
- std::move (outer_attrs), locus));
+ /* TODO: ensure that this ensures that dynamic dispatch for traits is not
+ * lost somehow */
}
-/* Parses a macro invocation with a path in expression already parsed (but not
- * '!' token). */
+// Parses tuple struct items if they exist. Does not parse parentheses.
template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroInvocation>
-Parser<ManagedTokenSource>::parse_macro_invocation_partial (
- AST::PathInExpression path, AST::AttrVec outer_attrs,
- ParseRestrictions restrictions)
+std::unique_ptr<AST::TupleStructItems>
+Parser<ManagedTokenSource>::parse_tuple_struct_items ()
{
- // macro invocation
- if (!skip_token (EXCLAM))
- {
- return nullptr;
- }
-
- // convert PathInExpression to SimplePath - if this isn't possible, error
- AST::SimplePath converted_path = path.as_simple_path ();
- if (converted_path.is_empty ())
- {
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse simple path in macro invocation");
- add_error (std::move (error));
+ std::vector<std::unique_ptr<AST::Pattern>> lower_patterns;
- return nullptr;
- }
+ // DEBUG
+ rust_debug ("started parsing tuple struct items");
- auto tok_tree = parse_delim_token_tree ();
- if (!tok_tree)
- return nullptr;
+ // check for '..' at front
+ if (lexer.peek_token ()->get_id () == DOT_DOT)
+ {
+ // only parse upper patterns
+ lexer.skip_token ();
- rust_debug ("successfully parsed macro invocation (via partial)");
+ // DEBUG
+ rust_debug ("'..' at front in tuple struct items detected");
- location_t macro_locus = converted_path.get_locus ();
+ std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
- return AST::MacroInvocation::Regular (
- AST::MacroInvocData (std::move (converted_path),
- std::move (tok_tree.value ())),
- std::move (outer_attrs), macro_locus);
-}
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () == COMMA)
+ {
+ lexer.skip_token ();
-/* Parses a struct expr struct with a path in expression already parsed (but
- * not
- * '{' token). */
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::StructExprStruct>
-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 nullptr;
- }
+ // break if right paren
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ break;
- // parse inner attributes
- AST::AttrVec inner_attrs = parse_inner_attributes ();
+ // parse pattern, which is now required
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
+ {
+ Error error (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in tuple struct items");
+ add_error (std::move (error));
- // 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 nullptr;
+ }
+ upper_patterns.push_back (std::move (pattern));
- return std::unique_ptr<AST::StructExprStruct> (
- new 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
+ t = lexer.peek_token ();
+ }
- // parse struct expr fields
- std::vector<std::unique_ptr<AST::StructExprField>> fields;
+ // DEBUG
+ rust_debug (
+ "finished parsing tuple struct items ranged (upper/none only)");
- while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
- {
- std::unique_ptr<AST::StructExprField> field
- = parse_struct_expr_field ();
- if (field == nullptr)
- {
- Error error (t->get_locus (),
- "failed to parse struct (or enum) expr field");
- add_error (std::move (error));
+ return std::unique_ptr<AST::TupleStructItemsHasRest> (
+ new AST::TupleStructItemsHasRest (std::move (lower_patterns),
+ std::move (upper_patterns)));
+ }
- return nullptr;
- }
+ // has at least some lower patterns
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT)
+ {
+ // DEBUG
+ rust_debug ("about to parse pattern in tuple struct items");
- // DEBUG:
- rust_debug ("struct/enum expr field validated to not be null");
+ // parse pattern, which is required
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
+ {
+ Error error (t->get_locus (),
+ "failed to parse pattern in tuple struct items");
+ add_error (std::move (error));
- fields.push_back (std::move (field));
+ return nullptr;
+ }
+ lower_patterns.push_back (std::move (pattern));
- // DEBUG:
- rust_debug ("struct/enum expr field pushed back");
+ // DEBUG
+ rust_debug ("successfully parsed pattern in tuple struct items");
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- // DEBUG:
- rust_debug ("lack of comma detected in struct/enum expr "
- "fields - break");
- break;
- }
- lexer.skip_token ();
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ // DEBUG
+ rust_debug ("broke out of parsing patterns in tuple struct "
+ "items as no comma");
- // DEBUG:
- rust_debug ("struct/enum expr fields comma skipped ");
+ break;
+ }
+ lexer.skip_token ();
+ t = lexer.peek_token ();
+ }
- t = lexer.peek_token ();
- }
+ // branch on next token
+ t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case RIGHT_PAREN:
+ return std::unique_ptr<AST::TupleStructItemsNoRest> (
+ new AST::TupleStructItemsNoRest (std::move (lower_patterns)));
+ case DOT_DOT:
+ {
+ // has an upper range that must be parsed separately
+ lexer.skip_token ();
- // DEBUG:
- rust_debug ("struct/enum expr about to parse struct base ");
+ std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
- // parse struct base if it exists
- AST::StructBase struct_base = AST::StructBase::error ();
- if (lexer.peek_token ()->get_id () == DOT_DOT)
+ t = lexer.peek_token ();
+ while (t->get_id () == COMMA)
{
- location_t dot_dot_location = lexer.peek_token ()->get_locus ();
lexer.skip_token ();
- // parse required struct base expr
- std::unique_ptr<AST::Expr> base_expr = parse_expr ();
- if (base_expr == nullptr)
+ // break if next token is right paren
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ break;
+
+ // parse pattern, which is required
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
{
Error error (lexer.peek_token ()->get_locus (),
- "failed to parse struct base expression in struct "
- "expression");
+ "failed to parse pattern in tuple struct items");
add_error (std::move (error));
return nullptr;
}
+ upper_patterns.push_back (std::move (pattern));
- // DEBUG:
- rust_debug ("struct/enum expr - parsed and validated base expr");
-
- struct_base
- = AST::StructBase (std::move (base_expr), dot_dot_location);
-
- // DEBUG:
- rust_debug ("assigned struct base to new struct base ");
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- return nullptr;
+ t = lexer.peek_token ();
}
- // DEBUG:
- rust_debug (
- "struct/enum expr skipped right curly - done and ready to return");
-
- return std::unique_ptr<AST::StructExprStructFields> (
- new 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 nullptr;
- }
-}
-
-/* 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>
-std::unique_ptr<AST::CallExpr>
-Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
- AST::PathInExpression path, AST::AttrVec outer_attrs)
-{
- if (!skip_token (LEFT_PAREN))
- {
- return nullptr;
- }
-
- 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)
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (t->get_locus (), "failed to parse expression in "
- "struct (or enum) expression tuple");
- add_error (std::move (error));
-
- return nullptr;
- }
- exprs.push_back (std::move (expr));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- break;
-
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
+ return std::unique_ptr<AST::TupleStructItemsHasRest> (
+ new AST::TupleStructItemsHasRest (std::move (lower_patterns),
+ std::move (upper_patterns)));
+ }
+ default:
+ // error
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs in tuple struct items",
+ t->get_token_description ()));
- if (!skip_token (RIGHT_PAREN))
- {
return nullptr;
}
-
- location_t path_locus = path.get_locus ();
-
- auto pathExpr = std::unique_ptr<AST::PathInExpression> (
- new AST::PathInExpression (std::move (path)));
-
- return std::unique_ptr<AST::CallExpr> (
- new AST::CallExpr (std::move (pathExpr), std::move (exprs),
- std::move (outer_attrs), path_locus));
}
-// Parses a closure expression with pratt parsing (from null denotation).
+/* Parses a statement or expression (depending on whether a trailing semicolon
+ * exists). Useful for block expressions where it cannot be determined through
+ * lookahead whether it is a statement or expression to be parsed. */
template <typename ManagedTokenSource>
-std::unique_ptr<AST::ClosureExpr>
-Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
- AST::AttrVec outer_attrs)
+ExprOrStmt
+Parser<ManagedTokenSource>::parse_stmt_or_expr ()
{
- // 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)
+ // quick exit for empty statement
+ const_TokenPtr t = lexer.peek_token ();
+ if (t->get_id () == SEMICOLON)
{
- has_move = true;
- tok = lexer.peek_token ();
lexer.skip_token ();
- // skip token and reassign
+ std::unique_ptr<AST::EmptyStmt> stmt (
+ new AST::EmptyStmt (t->get_locus ()));
+ return ExprOrStmt (std::move (stmt));
}
- // handle parameter list
- std::vector<AST::ClosureParam> params;
+ // parse outer attributes
+ AST::AttrVec outer_attrs = parse_outer_attributes ();
+ ParseRestrictions restrictions;
+ restrictions.expr_can_be_stmt = true;
+ std::unique_ptr<AST::Expr> expr;
- switch (tok->get_id ())
+ // parsing this will be annoying because of the many different possibilities
+ /* best may be just to copy paste in parse_item switch, and failing that try
+ * to parse outer attributes, and then pass them in to either a let
+ * statement or (fallback) expression statement. */
+ // FIXME: think of a way to do this without such a large switch?
+
+ /* FIXME: for expressions at least, the only way that they can really be
+ * parsed properly in this way is if they don't support operators on them.
+ * They must be pratt-parsed otherwise. As such due to composability, only
+ * explicit statements will have special cases here. This should roughly
+ * correspond to "expr-with-block", but this warning is here in case it
+ * isn't the case. */
+ t = lexer.peek_token ();
+ switch (t->get_id ())
{
- case OR:
- // no parameters, don't skip token
- break;
- case PIPE:
+ case LET:
+ {
+ // let statement
+ std::unique_ptr<AST::LetStmt> stmt (
+ parse_let_stmt (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (stmt));
+ }
+ case PUB:
+ case MOD:
+ case EXTERN_KW:
+ case USE:
+ case FN_KW:
+ case TYPE:
+ case STRUCT_KW:
+ case ENUM_KW:
+ case CONST:
+ case STATIC_KW:
+ case AUTO:
+ case TRAIT:
+ case IMPL:
{
- // actually may have parameters
- // don't skip token
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != PIPE)
+ std::unique_ptr<AST::VisItem> item (
+ parse_vis_item (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (item));
+ }
+ /* TODO: implement union keyword but not really because of
+ * context-dependence crappy hack way to parse a union written below to
+ * separate it from the good code. */
+ // case UNION:
+ case UNSAFE:
+ { // maybe - unsafe traits are a thing
+ /* if any of these (should be all possible VisItem prefixes), parse a
+ * VisItem - can't parse item because would require reparsing outer
+ * attributes */
+ const_TokenPtr t2 = lexer.peek_token (1);
+ switch (t2->get_id ())
{
- 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));
+ case LEFT_CURLY:
+ {
+ // unsafe block: parse as expression
+ expr = parse_expr (std::move (outer_attrs), restrictions);
+ break;
+ }
+ case AUTO:
+ case TRAIT:
+ {
+ // unsafe trait
+ std::unique_ptr<AST::VisItem> item (
+ parse_vis_item (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (item));
+ }
+ case EXTERN_KW:
+ case FN_KW:
+ {
+ // unsafe function
+ std::unique_ptr<AST::VisItem> item (
+ parse_vis_item (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (item));
+ }
+ case IMPL:
+ {
+ // unsafe trait impl
+ std::unique_ptr<AST::VisItem> item (
+ parse_vis_item (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (item));
+ }
+ default:
+ add_error (Error (t2->get_locus (),
+ "unrecognised token %qs after parsing unsafe - "
+ "expected beginning of expression or statement",
+ t->get_token_description ()));
- return nullptr;
- }
- params.push_back (std::move (param));
+ // skip somewhere?
+ return ExprOrStmt::create_error ();
+ }
+ break;
+ }
+ /* FIXME: this is either a macro invocation or macro invocation semi.
+ * start parsing to determine which one it is. */
+ // FIXME: old code there
+
+ // crappy hack to do union "keyword"
+ case IDENTIFIER:
+ if (t->get_str () == Values::WeakKeywords::UNION
+ && lexer.peek_token (1)->get_id () == IDENTIFIER)
+ {
+ std::unique_ptr<AST::VisItem> item (
+ parse_vis_item (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (item));
+ // or should this go straight to parsing union?
+ }
+ else if (t->get_str () == Values::WeakKeywords::MACRO_RULES
+ && lexer.peek_token (1)->get_id () == EXCLAM)
+ {
+ // macro_rules! macro item
+ std::unique_ptr<AST::Item> item (
+ parse_macro_rules_def (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (item));
+ }
+ gcc_fallthrough ();
+ case SUPER:
+ case SELF:
+ case SELF_ALIAS:
+ case CRATE:
+ case SCOPE_RESOLUTION:
+ case DOLLAR_SIGN:
+ {
+ AST::PathInExpression path = parse_path_in_expression ();
+ std::unique_ptr<AST::Expr> null_denotation;
+
+ if (lexer.peek_token ()->get_id () == EXCLAM)
+ {
+ std::unique_ptr<AST::MacroInvocation> invoc
+ = parse_macro_invocation_partial (std::move (path),
+ std::move (outer_attrs));
+ if (invoc == nullptr)
+ return ExprOrStmt::create_error ();
- if (lexer.peek_token ()->get_id () != COMMA)
+ if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
{
- if (lexer.peek_token ()->get_id () == OR)
- lexer.split_current_token (PIPE, PIPE);
- // not an error but means param list is done
- break;
+ invoc->add_semicolon ();
+ // Macro invocation with semicolon.
+ return ExprOrStmt (
+ std::unique_ptr<AST::Stmt> (std::move (invoc)));
}
- // skip comma
- lexer.skip_token ();
- if (lexer.peek_token ()->get_id () == OR)
- lexer.split_current_token (PIPE, PIPE);
+ TokenId after_macro = lexer.peek_token ()->get_id ();
- t = lexer.peek_token ();
- }
+ AST::DelimType delim_type = invoc->get_invoc_data ()
+ .get_delim_tok_tree ()
+ .get_delim_type ();
+
+ if (delim_type == AST::CURLY && after_macro != DOT
+ && after_macro != QUESTION_MARK)
+ {
+ rust_debug ("braced macro statement");
+ return ExprOrStmt (
+ std::unique_ptr<AST::Stmt> (std::move (invoc)));
+ }
- if (!skip_token (PIPE))
+ null_denotation = std::move (invoc);
+ }
+ else
{
- return nullptr;
+ null_denotation
+ = null_denotation_path (std::move (path), {}, restrictions);
}
+
+ expr = left_denotations (std::move (null_denotation), LBP_LOWEST,
+ std::move (outer_attrs), restrictions);
break;
}
default:
- add_error (Error (tok->get_locus (),
- "unexpected token %qs in closure expression - expected "
- "%<|%> or %<||%>",
- tok->get_token_description ()));
-
- // skip somewhere?
- return nullptr;
+ /* expression statement or expression itself - parse
+ * expression then make it statement if semi afterwards */
+ expr = parse_expr (std::move (outer_attrs), restrictions);
+ break;
}
- // again branch based on next token
- tok = lexer.peek_token ();
- if (tok->get_id () == RETURN_TYPE)
+ const_TokenPtr after_expr = lexer.peek_token ();
+ if (after_expr->get_id () == SEMICOLON)
{
- // must be return type closure with block expr
-
- // skip "return type" token
+ // must be expression statement
lexer.skip_token ();
- // parse actual type, which is required
- std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
- if (type == nullptr)
+ if (expr)
{
- // error
- Error error (tok->get_locus (), "failed to parse type for closure");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
+ std::unique_ptr<AST::ExprStmt> stmt (
+ new AST::ExprStmt (std::move (expr), t->get_locus (), true));
+ return ExprOrStmt (std::move (stmt));
}
-
- // parse block expr, which is required
- std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
- if (block == nullptr)
+ else
{
- // error
- Error error (lexer.peek_token ()->get_locus (),
- "failed to parse block expr in closure");
- add_error (std::move (error));
-
- // skip somewhere?
- return nullptr;
+ return ExprOrStmt::create_error ();
}
-
- return std::unique_ptr<AST::ClosureExprInnerTyped> (
- new AST::ClosureExprInnerTyped (std::move (type), std::move (block),
- std::move (params), locus, has_move,
- std::move (outer_attrs)));
}
- else
- {
- // must be expr-only closure
-
- // parse expr, which is required
- std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == nullptr)
- {
- Error error (tok->get_locus (),
- "failed to parse expression in closure");
- add_error (std::move (error));
- // skip somewhere?
- return nullptr;
- }
-
- return std::unique_ptr<AST::ClosureExprInner> (
- new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
- has_move, std::move (outer_attrs)));
+ if (expr && !expr->is_expr_without_block ()
+ && after_expr->get_id () != RIGHT_CURLY)
+ {
+ // block expression statement.
+ std::unique_ptr<AST::ExprStmt> stmt (
+ new AST::ExprStmt (std::move (expr), t->get_locus (), false));
+ return ExprOrStmt (std::move (stmt));
}
-}
-// Returns true if the next token is END, ELSE, or EOF;
-template <typename ManagedTokenSource>
-bool
-Parser<ManagedTokenSource>::done_end_or_else ()
-{
- const_TokenPtr t = lexer.peek_token ();
- return (t->get_id () == RIGHT_CURLY || t->get_id () == ELSE
- || t->get_id () == END_OF_FILE);
+ // return expression
+ return ExprOrStmt (std::move (expr));
}
-// Returns true if the next token is END or EOF.
-template <typename ManagedTokenSource>
-bool
-Parser<ManagedTokenSource>::done_end ()
-{
- const_TokenPtr t = lexer.peek_token ();
- return (t->get_id () == RIGHT_CURLY || t->get_id () == END_OF_FILE);
-}
} // namespace Rust
+
+#include "rust-parse-impl-utils.hxx"
+#include "rust-parse-impl-attribute.hxx"
+#include "rust-parse-impl-ttree.hxx"
+#include "rust-parse-impl-macro.hxx"
+#include "rust-parse-impl-path.hxx"
+#include "rust-parse-impl-pattern.hxx"
+#include "rust-parse-impl-expr.hxx"