| // Copyright (C) 2025-2026 Free Software Foundation, Inc. |
| |
| // This file is part of GCC. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with GCC; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| /* DO NOT INCLUDE ANYWHERE - this is automatically included |
| * by rust-parse-impl.h |
| * This is also the reason why there are no include guards. */ |
| |
| #include "rust-parse.h" |
| |
| namespace Rust { |
| |
| // Parses a block expression, including the curly braces at start and end. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::BlockExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_block_expr ( |
| AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| if (!skip_token (LEFT_CURLY)) |
| { |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| // parse statements and expression |
| std::vector<std::unique_ptr<AST::Stmt>> stmts; |
| std::unique_ptr<AST::Expr> expr = nullptr; |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () != RIGHT_CURLY) |
| { |
| auto expr_or_stmt = parse_stmt_or_expr (); |
| if (!expr_or_stmt) |
| { |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| t = lexer.peek_token (); |
| |
| if (expr_or_stmt->stmt != nullptr) |
| { |
| stmts.push_back (std::move (expr_or_stmt->stmt)); |
| } |
| else |
| { |
| // assign to expression and end parsing inside |
| expr = std::move (expr_or_stmt->expr); |
| } |
| } |
| |
| location_t end_locus = t->get_locus (); |
| |
| if (!skip_token (RIGHT_CURLY)) |
| { |
| // We don't need to throw an error as it already reported by skip_token |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| // grammar allows for empty block expressions |
| |
| stmts.shrink_to_fit (); |
| |
| return std::unique_ptr<AST::BlockExpr> ( |
| new AST::BlockExpr (std::move (stmts), std::move (expr), |
| std::move (inner_attrs), std::move (outer_attrs), |
| std::move (label), locus, end_locus)); |
| } |
| |
| /* Parse an anonymous const expression. This can be a regular const expression |
| * or an underscore for deferred const inference */ |
| template <typename ManagedTokenSource> |
| tl::expected<AST::AnonConst, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_anon_const () |
| { |
| auto current = lexer.peek_token (); |
| auto locus = current->get_locus (); |
| |
| // Special case deferred inference constants |
| if (maybe_skip_token (UNDERSCORE)) |
| return AST::AnonConst (locus); |
| |
| auto expr = parse_expr (); |
| |
| if (!expr) |
| return tl::make_unexpected (Parse::Error::Node{}); |
| |
| return AST::AnonConst (std::move (expr.value ()), locus); |
| } |
| |
| /* Parse a "const block", a block preceded by the `const` keyword whose |
| * statements can be const evaluated and used in constant contexts */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ConstBlock>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_const_block_expr (AST::AttrVec outer_attrs, |
| location_t locus) |
| { |
| auto block_res = parse_block_expr (); |
| |
| if (!block_res) |
| { |
| add_error (Error (locus, "failed to parse inner block in const block")); |
| skip_after_end_block (); |
| |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| auto block = std::move (block_res.value ()); |
| |
| auto block_locus = block->get_locus (); |
| |
| return std::make_unique<AST::ConstBlock> (AST::AnonConst (std::move (block), |
| block_locus), |
| locus, std::move (outer_attrs)); |
| } |
| |
| /* Parses a "grouped" expression (expression in parentheses), used to control |
| * precedence. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::GroupedExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs) |
| { |
| location_t locus = lexer.peek_token ()->get_locus (); |
| skip_token (LEFT_PAREN); |
| |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| // parse required expr inside parentheses |
| auto expr_in_parens = parse_expr (); |
| if (!expr_in_parens) |
| { |
| // skip after somewhere? |
| // error? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| // skip after somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| return std::unique_ptr<AST::GroupedExpr> ( |
| new AST::GroupedExpr (std::move (expr_in_parens.value ()), |
| std::move (inner_attrs), std::move (outer_attrs), |
| locus)); |
| } |
| |
| // Parses a closure expression (closure definition). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs) |
| { |
| location_t locus = lexer.peek_token ()->get_locus (); |
| // detect optional "move" |
| bool has_move = false; |
| if (lexer.peek_token ()->get_id () == MOVE) |
| { |
| lexer.skip_token (); |
| has_move = true; |
| } |
| |
| // handle parameter list |
| std::vector<AST::ClosureParam> params; |
| |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case OR: |
| // skip token, no parameters |
| lexer.skip_token (); |
| break; |
| case PIPE: |
| // actually may have parameters |
| lexer.skip_token (); |
| t = lexer.peek_token (); |
| |
| while (t->get_id () != PIPE) |
| { |
| AST::ClosureParam param = parse_closure_param (); |
| if (param.is_error ()) |
| { |
| // TODO is this really an error? |
| Error error (t->get_locus (), "could not parse closure param"); |
| add_error (std::move (error)); |
| |
| break; |
| } |
| params.push_back (std::move (param)); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| { |
| lexer.skip_token (); |
| // not an error but means param list is done |
| break; |
| } |
| // skip comma |
| lexer.skip_token (); |
| |
| t = lexer.peek_token (); |
| } |
| params.shrink_to_fit (); |
| break; |
| default: |
| add_error (Error (t->get_locus (), |
| "unexpected token %qs in closure expression - expected " |
| "%<|%> or %<||%>", |
| t->get_token_description ())); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| // again branch based on next token |
| t = lexer.peek_token (); |
| if (t->get_id () == RETURN_TYPE) |
| { |
| // must be return type closure with block expr |
| |
| // skip "return type" token |
| lexer.skip_token (); |
| |
| // parse actual type, which is required |
| std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds (); |
| if (type == nullptr) |
| { |
| // error |
| Error error (t->get_locus (), "failed to parse type for closure"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| // parse block expr, which is required |
| auto block = parse_block_expr (); |
| if (!block) |
| { |
| // error |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse block expr in closure"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::ClosureExprInnerTyped> ( |
| new AST::ClosureExprInnerTyped (std::move (type), |
| std::move (block.value ()), |
| std::move (params), locus, has_move, |
| std::move (outer_attrs))); |
| } |
| else |
| { |
| // must be expr-only closure |
| |
| // parse expr, which is required |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (t->get_locus (), |
| "failed to parse expression in closure"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::ClosureExprInner> ( |
| new AST::ClosureExprInner (std::move (expr.value ()), |
| std::move (params), locus, has_move, |
| std::move (outer_attrs))); |
| } |
| } |
| |
| // Parses a literal token (to literal expression). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::LiteralExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_literal_expr (AST::AttrVec outer_attrs) |
| { |
| // TODO: change if literal representation in lexer changes |
| |
| std::string literal_value; |
| AST::Literal::LitType type = AST::Literal::STRING; |
| |
| // branch based on token |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case CHAR_LITERAL: |
| type = AST::Literal::CHAR; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| case STRING_LITERAL: |
| type = AST::Literal::STRING; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| case BYTE_CHAR_LITERAL: |
| type = AST::Literal::BYTE; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| case BYTE_STRING_LITERAL: |
| type = AST::Literal::BYTE_STRING; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| case RAW_STRING_LITERAL: |
| type = AST::Literal::RAW_STRING; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| case INT_LITERAL: |
| type = AST::Literal::INT; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| case FLOAT_LITERAL: |
| type = AST::Literal::FLOAT; |
| literal_value = t->get_str (); |
| lexer.skip_token (); |
| break; |
| // case BOOL_LITERAL |
| // use true and false keywords rather than "bool literal" Rust terminology |
| case TRUE_LITERAL: |
| type = AST::Literal::BOOL; |
| literal_value = Values::Keywords::TRUE_LITERAL; |
| lexer.skip_token (); |
| break; |
| case FALSE_LITERAL: |
| type = AST::Literal::BOOL; |
| literal_value = Values::Keywords::FALSE_LITERAL; |
| lexer.skip_token (); |
| break; |
| default: |
| // error - cannot be a literal expr |
| add_error (Error (t->get_locus (), |
| "unexpected token %qs when parsing literal expression", |
| t->get_token_description ())); |
| |
| // skip? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| // create literal based on stuff in switch |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (std::move (literal_value), std::move (type), |
| t->get_type_hint (), std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::BoxExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_box_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (BOX); |
| } |
| |
| ParseRestrictions restrictions; |
| restrictions.expr_can_be_null = false; |
| |
| auto expr = parse_expr (AST::AttrVec (), restrictions); |
| if (!expr) |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR); |
| |
| return std::unique_ptr<AST::BoxExpr> ( |
| new AST::BoxExpr (std::move (expr.value ()), std::move (outer_attrs), |
| locus)); |
| } |
| |
| // Parses a return expression (including any expression to return). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ReturnExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (RETURN_KW); |
| } |
| |
| // parse expression to return, if it exists |
| ParseRestrictions restrictions; |
| restrictions.expr_can_be_null = true; |
| auto returned_expr = parse_expr (AST::AttrVec (), restrictions); |
| tl::optional<std::unique_ptr<AST::Expr>> expr = tl::nullopt; |
| if (returned_expr) |
| expr = std::move (returned_expr.value ()); |
| |
| return std::make_unique<AST::ReturnExpr> (std::move (expr), |
| std::move (outer_attrs), locus); |
| } |
| |
| // Parses a try expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::TryExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_try_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (TRY); |
| } |
| |
| auto block_expr = parse_block_expr (); |
| |
| if (!block_expr) |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR); |
| |
| return std::unique_ptr<AST::TryExpr> ( |
| new AST::TryExpr (std::move (block_expr.value ()), std::move (outer_attrs), |
| locus)); |
| } |
| |
| /* Parses a break expression (including any label to break to AND any return |
| * expression). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::BreakExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (BREAK); |
| } |
| |
| auto parsed_label = parse_lifetime (false); |
| auto label = (parsed_label) |
| ? tl::optional<AST::Lifetime> (parsed_label.value ()) |
| : tl::nullopt; |
| |
| // parse break return expression if it exists |
| ParseRestrictions restrictions; |
| restrictions.expr_can_be_null = true; |
| auto return_expr = parse_expr (AST::AttrVec (), restrictions); |
| |
| if (return_expr) |
| return std::unique_ptr<AST::BreakExpr> ( |
| new AST::BreakExpr (std::move (label), std::move (return_expr.value ()), |
| std::move (outer_attrs), locus)); |
| else if (return_expr.error () == Parse::Error::Expr::NULL_EXPR) |
| return std::unique_ptr<AST::BreakExpr> ( |
| new AST::BreakExpr (std::move (label), tl::nullopt, |
| std::move (outer_attrs), locus)); |
| else |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| // Parses a continue expression (including any label to continue from). |
| template <typename ManagedTokenSource> |
| std::unique_ptr<AST::ContinueExpr> |
| Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (CONTINUE); |
| } |
| |
| auto parsed_label = parse_lifetime (false); |
| auto label = (parsed_label) |
| ? tl::optional<AST::Lifetime> (parsed_label.value ()) |
| : tl::nullopt; |
| |
| return std::make_unique<AST::ContinueExpr> (std::move (label), |
| std::move (outer_attrs), locus); |
| } |
| |
| /* Parses an if expression of any kind, including with else, else if, else if |
| * let, and neither. Note that any outer attributes will be ignored because if |
| * expressions don't support them. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::IfExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| // TODO: make having outer attributes an error? |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| if (!skip_token (IF)) |
| { |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| |
| // detect accidental if let |
| if (lexer.peek_token ()->get_id () == LET) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "if let expression probably exists, but is being parsed " |
| "as an if expression. This may be a parser error"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| /* parse required condition expr - HACK to prevent struct expr from being |
| * parsed */ |
| ParseRestrictions no_struct_expr; |
| no_struct_expr.can_be_struct_expr = false; |
| auto condition = parse_expr ({}, no_struct_expr); |
| if (!condition) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse condition expression in if expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| // parse required block expr |
| auto if_body = parse_block_expr (); |
| if (!if_body) |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR); |
| |
| // branch to parse end or else (and then else, else if, or else if let) |
| if (lexer.peek_token ()->get_id () != ELSE) |
| { |
| // single selection - end of if expression |
| return std::unique_ptr<AST::IfExpr> ( |
| new AST::IfExpr (std::move (condition.value ()), |
| std::move (if_body.value ()), std::move (outer_attrs), |
| locus)); |
| } |
| else |
| { |
| // double or multiple selection - branch on end, else if, or else if let |
| |
| // skip "else" |
| lexer.skip_token (); |
| |
| // branch on whether next token is '{' or 'if' |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case LEFT_CURLY: |
| { |
| // double selection - else |
| // parse else block expr (required) |
| auto else_body = parse_block_expr (); |
| if (!else_body) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse else body block expression in " |
| "if expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::IfExprConseqElse> ( |
| new AST::IfExprConseqElse (std::move (condition.value ()), |
| std::move (if_body.value ()), |
| std::move (else_body.value ()), |
| std::move (outer_attrs), locus)); |
| } |
| case IF: |
| { |
| // multiple selection - else if or else if let |
| // branch on whether next token is 'let' or not |
| if (lexer.peek_token (1)->get_id () == LET) |
| { |
| // parse if let expr (required) |
| auto if_let_expr = parse_if_let_expr (); |
| if (!if_let_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse (else) if let expression " |
| "after if expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::IfExprConseqElse> ( |
| new AST::IfExprConseqElse (std::move (condition.value ()), |
| std::move (if_body.value ()), |
| std::move (if_let_expr.value ()), |
| std::move (outer_attrs), locus)); |
| } |
| else |
| { |
| // parse if expr (required) |
| auto if_expr = parse_if_expr (); |
| if (!if_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse (else) if expression after " |
| "if expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::IfExprConseqElse> ( |
| new AST::IfExprConseqElse (std::move (condition.value ()), |
| std::move (if_body.value ()), |
| std::move (if_expr.value ()), |
| std::move (outer_attrs), locus)); |
| } |
| } |
| default: |
| // error - invalid token |
| add_error (Error (t->get_locus (), |
| "unexpected token %qs after else in if expression", |
| t->get_token_description ())); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| } |
| |
| /* Parses an if let expression of any kind, including with else, else if, else |
| * if let, and none. Note that any outer attributes will be ignored as if let |
| * expressions don't support them. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::IfLetExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| // TODO: make having outer attributes an error? |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| if (!skip_token (IF)) |
| { |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| |
| // detect accidental if expr parsed as if let expr |
| if (lexer.peek_token ()->get_id () != LET) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "if expression probably exists, but is being parsed as an " |
| "if let expression. This may be a parser error"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| lexer.skip_token (); |
| |
| // parse match arm patterns (which are required) |
| std::unique_ptr<AST::Pattern> match_arm_pattern |
| = parse_match_arm_pattern (EQUAL); |
| if (match_arm_pattern == nullptr) |
| { |
| Error error ( |
| lexer.peek_token ()->get_locus (), |
| "failed to parse any match arm patterns in if let expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| if (!skip_token (EQUAL)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| // parse expression (required) - HACK to prevent struct expr being parsed |
| ParseRestrictions no_struct_expr; |
| no_struct_expr.can_be_struct_expr = false; |
| auto scrutinee_expr = parse_expr ({}, no_struct_expr); |
| if (!scrutinee_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse scrutinee expression in if let expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| /* TODO: check for expression not being a struct expression or lazy boolean |
| * expression here? or actually probably in semantic analysis. */ |
| |
| // parse block expression (required) |
| auto if_let_body = parse_block_expr (); |
| if (!if_let_body) |
| { |
| Error error ( |
| lexer.peek_token ()->get_locus (), |
| "failed to parse if let body block expression in if let expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| // branch to parse end or else (and then else, else if, or else if let) |
| if (lexer.peek_token ()->get_id () != ELSE) |
| { |
| // single selection - end of if let expression |
| return std::unique_ptr<AST::IfLetExpr> (new AST::IfLetExpr ( |
| std::move (match_arm_pattern), std::move (scrutinee_expr.value ()), |
| std::move (if_let_body.value ()), std::move (outer_attrs), locus)); |
| } |
| else |
| { |
| // double or multiple selection - branch on end, else if, or else if let |
| |
| // skip "else" |
| lexer.skip_token (); |
| |
| // branch on whether next token is '{' or 'if' |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case LEFT_CURLY: |
| { |
| // double selection - else |
| // parse else block expr (required) |
| auto else_body = parse_block_expr (); |
| if (!else_body) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse else body block expression in " |
| "if let expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::IfLetExprConseqElse> ( |
| new AST::IfLetExprConseqElse (std::move (match_arm_pattern), |
| std::move (scrutinee_expr.value ()), |
| std::move (if_let_body.value ()), |
| std::move (else_body.value ()), |
| std::move (outer_attrs), locus)); |
| } |
| case IF: |
| { |
| // multiple selection - else if or else if let |
| // branch on whether next token is 'let' or not |
| if (lexer.peek_token (1)->get_id () == LET) |
| { |
| // parse if let expr (required) |
| auto if_let_expr = parse_if_let_expr (); |
| if (!if_let_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse (else) if let expression " |
| "after if let expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::IfLetExprConseqElse> ( |
| new AST::IfLetExprConseqElse ( |
| std::move (match_arm_pattern), |
| std::move (scrutinee_expr.value ()), |
| std::move (if_let_body.value ()), |
| std::move (if_let_expr.value ()), std::move (outer_attrs), |
| locus)); |
| } |
| else |
| { |
| // parse if expr (required) |
| auto if_expr = parse_if_expr (); |
| if (!if_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse (else) if expression after " |
| "if let expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::IfLetExprConseqElse> ( |
| new AST::IfLetExprConseqElse ( |
| std::move (match_arm_pattern), |
| std::move (scrutinee_expr.value ()), |
| std::move (if_let_body.value ()), |
| std::move (if_expr.value ()), std::move (outer_attrs), |
| locus)); |
| } |
| } |
| default: |
| // error - invalid token |
| add_error ( |
| Error (t->get_locus (), |
| "unexpected token %qs after else in if let expression", |
| t->get_token_description ())); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| } |
| |
| /* TODO: possibly decide on different method of handling label (i.e. not |
| * parameter) */ |
| |
| /* Parses a "loop" infinite loop expression. Label is not parsed and should be |
| * parsed via parse_labelled_loop_expr, which would call this. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::LoopExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs, |
| tl::optional<AST::LoopLabel> label, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| if (label) |
| locus = label->get_locus (); |
| else |
| locus = lexer.peek_token ()->get_locus (); |
| |
| if (!skip_token (LOOP)) |
| { |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| else |
| { |
| if (label) |
| locus = label->get_locus (); |
| } |
| |
| // parse loop body, which is required |
| auto loop_body = parse_block_expr (); |
| if (!loop_body) |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR); |
| |
| return std::unique_ptr<AST::LoopExpr> ( |
| new AST::LoopExpr (std::move (loop_body.value ()), locus, std::move (label), |
| std::move (outer_attrs))); |
| } |
| |
| /* Parses a "while" loop expression. Label is not parsed and should be parsed |
| * via parse_labelled_loop_expr, which would call this. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::WhileLoopExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_while_loop_expr ( |
| AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| if (label) |
| locus = label->get_locus (); |
| else |
| locus = lexer.peek_token ()->get_locus (); |
| |
| if (!skip_token (WHILE)) |
| { |
| skip_after_end_block (); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| else |
| { |
| if (label) |
| locus = label->get_locus (); |
| } |
| |
| // ensure it isn't a while let loop |
| if (lexer.peek_token ()->get_id () == LET) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "appears to be while let loop but is being parsed by " |
| "while loop - this may be a compiler issue"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| // parse loop predicate (required) with HACK to prevent struct expr parsing |
| ParseRestrictions no_struct_expr; |
| no_struct_expr.can_be_struct_expr = false; |
| auto predicate = parse_expr ({}, no_struct_expr); |
| if (!predicate) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse predicate expression in while loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| /* TODO: check that it isn't struct expression here? actually, probably in |
| * semantic analysis */ |
| |
| // parse loop body (required) |
| auto body = parse_block_expr (); |
| if (!body) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse loop body block expression in while loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::WhileLoopExpr> ( |
| new AST::WhileLoopExpr (std::move (predicate.value ()), |
| std::move (body.value ()), locus, std::move (label), |
| std::move (outer_attrs))); |
| } |
| |
| /* Parses a "while let" loop expression. Label is not parsed and should be |
| * parsed via parse_labelled_loop_expr, which would call this. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::WhileLetLoopExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_while_let_loop_expr ( |
| AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label) |
| { |
| location_t locus = UNKNOWN_LOCATION; |
| if (label) |
| locus = label->get_locus (); |
| else |
| locus = lexer.peek_token ()->get_locus (); |
| maybe_skip_token (WHILE); |
| |
| /* check for possible accidental recognition of a while loop as a while let |
| * loop */ |
| if (lexer.peek_token ()->get_id () != LET) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "appears to be a while loop but is being parsed by " |
| "while let loop - this may be a compiler issue"); |
| add_error (std::move (error)); |
| |
| // skip somewhere |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| // as this token is definitely let now, save the computation of comparison |
| lexer.skip_token (); |
| |
| // parse predicate patterns |
| std::unique_ptr<AST::Pattern> predicate_pattern |
| = parse_match_arm_pattern (EQUAL); |
| // ensure that there is at least 1 pattern |
| if (predicate_pattern == nullptr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "should be at least 1 pattern"); |
| add_error (std::move (error)); |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| if (!skip_token (EQUAL)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| /* parse predicate expression, which is required (and HACK to prevent struct |
| * expr) */ |
| ParseRestrictions no_struct_expr; |
| no_struct_expr.can_be_struct_expr = false; |
| auto predicate_expr = parse_expr ({}, no_struct_expr); |
| if (!predicate_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse predicate expression in while let loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| /* TODO: ensure that struct expression is not parsed? Actually, probably in |
| * semantic analysis. */ |
| |
| // parse loop body, which is required |
| auto body = parse_block_expr (); |
| if (!body) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse block expr (loop body) of while let loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::WhileLetLoopExpr> ( |
| new AST::WhileLetLoopExpr (std::move (predicate_pattern), |
| std::move (predicate_expr.value ()), |
| std::move (body.value ()), locus, |
| std::move (label), std::move (outer_attrs))); |
| } |
| |
| /* Parses a "for" iterative loop. Label is not parsed and should be parsed via |
| * parse_labelled_loop_expr, which would call this. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ForLoopExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_for_loop_expr ( |
| AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label) |
| { |
| location_t locus = UNKNOWN_LOCATION; |
| if (label) |
| locus = label->get_locus (); |
| else |
| locus = lexer.peek_token ()->get_locus (); |
| maybe_skip_token (FOR); |
| |
| // parse pattern, which is required |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); |
| if (!pattern) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse iterator pattern in for loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| if (!skip_token (IN)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| /* parse iterator expression, which is required - also HACK to prevent |
| * struct expr */ |
| ParseRestrictions no_struct_expr; |
| no_struct_expr.can_be_struct_expr = false; |
| auto expr = parse_expr ({}, no_struct_expr); |
| if (!expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse iterator expression in for loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| // TODO: check to ensure this isn't struct expr? Or in semantic analysis. |
| |
| // parse loop body, which is required |
| auto body = parse_block_expr (); |
| if (!body) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse loop body block expression in for loop"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| return std::unique_ptr<AST::ForLoopExpr> ( |
| new AST::ForLoopExpr (std::move (pattern), std::move (expr.value ()), |
| std::move (body.value ()), locus, std::move (label), |
| std::move (outer_attrs))); |
| } |
| |
| // Parses a loop expression with label (any kind of loop - disambiguates). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok, |
| AST::AttrVec outer_attrs) |
| { |
| // parse loop label (required) |
| auto parsed_label = parse_loop_label (tok); |
| if (!parsed_label) |
| { |
| /* TODO: decide whether it should not work if there is no label, or parse |
| * it with no label at the moment, I will make it not work with no label |
| * because that's the implication. */ |
| |
| if (parsed_label.error ().kind |
| == Parse::Error::LoopLabel::Kind::NOT_LOOP_LABEL) |
| { |
| Error error (tok->get_locus (), |
| "expected lifetime in labelled loop expr (to parse loop " |
| "label) - found %qs", |
| tok->get_token_description ()); |
| add_error (std::move (error)); |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| else |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse loop label in labelled loop expr"); |
| add_error (std::move (error)); |
| |
| // skip? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| } |
| |
| auto label = parsed_label |
| ? tl::optional<AST::LoopLabel> (parsed_label.value ()) |
| : tl::nullopt; |
| |
| // branch on next token |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case LOOP: |
| return parse_loop_expr (std::move (outer_attrs), std::move (label)); |
| case FOR: |
| return parse_for_loop_expr (std::move (outer_attrs), std::move (label)); |
| case WHILE: |
| // further disambiguate into while vs while let |
| if (lexer.peek_token (1)->get_id () == LET) |
| return parse_while_let_loop_expr (std::move (outer_attrs), |
| std::move (label)); |
| else |
| return parse_while_loop_expr (std::move (outer_attrs), |
| std::move (label)); |
| case LEFT_CURLY: |
| return parse_block_expr (std::move (outer_attrs), std::move (label)); |
| default: |
| // error |
| add_error (Error (t->get_locus (), |
| "unexpected token %qs when parsing labelled loop", |
| t->get_token_description ())); |
| |
| // skip? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| } |
| |
| // Parses a match expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::MatchExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (MATCH_KW); |
| } |
| |
| /* parse scrutinee expression, which is required (and HACK to prevent struct |
| * expr) */ |
| ParseRestrictions no_struct_expr; |
| no_struct_expr.can_be_struct_expr = false; |
| auto scrutinee = parse_expr ({}, no_struct_expr); |
| if (!scrutinee) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse scrutinee expression in match expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| /* TODO: check for scrutinee expr not being struct expr? or do so in |
| * semantic analysis */ |
| |
| if (!skip_token (LEFT_CURLY)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| // parse inner attributes (if they exist) |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| // parse match arms (if they exist) |
| // std::vector<std::unique_ptr<AST::MatchCase> > match_arms; |
| std::vector<AST::MatchCase> match_arms; |
| |
| // parse match cases |
| while (lexer.peek_token ()->get_id () != RIGHT_CURLY) |
| { |
| // parse match arm itself, which is required |
| AST::MatchArm arm = parse_match_arm (); |
| if (arm.is_error ()) |
| { |
| // TODO is this worth throwing everything away? |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse match arm in match arms"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| if (!skip_token (MATCH_ARROW)) |
| { |
| // skip after somewhere? |
| // TODO is returning here a good idea? or is break better? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| |
| ParseRestrictions restrictions; |
| restrictions.expr_can_be_stmt = true; |
| |
| auto expr = parse_expr ({}, restrictions); |
| |
| if (!expr) |
| { |
| /* We don't need to throw an error as it already reported by |
| * parse_expr |
| */ |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| bool is_expr_without_block = expr.value ()->is_expr_without_block (); |
| |
| match_arms.push_back ( |
| AST::MatchCase (std::move (arm), std::move (expr.value ()))); |
| |
| // handle comma presence |
| if (lexer.peek_token ()->get_id () != COMMA) |
| { |
| if (!is_expr_without_block) |
| { |
| // allowed even if not final case |
| continue; |
| } |
| else if (is_expr_without_block |
| && lexer.peek_token ()->get_id () != RIGHT_CURLY) |
| { |
| // not allowed if not final case |
| Error error (lexer.peek_token ()->get_locus (), |
| "exprwithoutblock requires comma after match case " |
| "expression in match arm (if not final case)"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| else |
| { |
| // otherwise, must be final case, so fine |
| break; |
| } |
| } |
| lexer.skip_token (); |
| } |
| |
| if (!skip_token (RIGHT_CURLY)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| |
| match_arms.shrink_to_fit (); |
| |
| return std::unique_ptr<AST::MatchExpr> ( |
| new AST::MatchExpr (std::move (scrutinee.value ()), std::move (match_arms), |
| std::move (inner_attrs), std::move (outer_attrs), |
| locus)); |
| } |
| |
| // Parses an async block expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::AsyncBlockExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs) |
| { |
| location_t locus = lexer.peek_token ()->get_locus (); |
| skip_token (ASYNC); |
| |
| // detect optional move token |
| bool has_move = false; |
| if (lexer.peek_token ()->get_id () == MOVE) |
| { |
| lexer.skip_token (); |
| has_move = true; |
| } |
| |
| // parse block expression (required) |
| auto block_expr = parse_block_expr (); |
| if (!block_expr) |
| { |
| Error error ( |
| lexer.peek_token ()->get_locus (), |
| "failed to parse block expression of async block expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::AsyncBlockExpr> ( |
| new AST::AsyncBlockExpr (std::move (block_expr.value ()), has_move, |
| std::move (outer_attrs), locus)); |
| } |
| |
| // Parses an unsafe block expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::UnsafeBlockExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_unsafe_block_expr ( |
| AST::AttrVec outer_attrs, location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (UNSAFE); |
| } |
| |
| // parse block expression (required) |
| auto block_expr = parse_block_expr (); |
| if (!block_expr) |
| { |
| Error error ( |
| lexer.peek_token ()->get_locus (), |
| "failed to parse block expression of unsafe block expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| return std::unique_ptr<AST::UnsafeBlockExpr> ( |
| new AST::UnsafeBlockExpr (std::move (block_expr.value ()), |
| std::move (outer_attrs), locus)); |
| } |
| |
| // Parses an array definition expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArrayExpr>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs, |
| location_t pratt_parsed_loc) |
| { |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (LEFT_SQUARE); |
| } |
| |
| // parse optional inner attributes |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| // parse the "array elements" section, which is optional |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) |
| { |
| // no array elements |
| lexer.skip_token (); |
| |
| std::vector<std::unique_ptr<AST::Expr>> exprs; |
| auto array_elems |
| = std::make_unique<AST::ArrayElemsValues> (std::move (exprs), locus); |
| return std::make_unique<AST::ArrayExpr> (std::move (array_elems), |
| std::move (inner_attrs), |
| std::move (outer_attrs), locus); |
| } |
| else |
| { |
| // should have array elements |
| // parse initial expression, which is required for either |
| auto initial_expr = parse_expr (); |
| if (!initial_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "could not parse expression in array expression " |
| "(even though arrayelems seems to be present)"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| if (lexer.peek_token ()->get_id () == SEMICOLON) |
| { |
| // copy array elems |
| lexer.skip_token (); |
| |
| // parse copy amount expression (required) |
| auto copy_amount = parse_expr (); |
| if (!copy_amount) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "could not parse copy amount expression in array " |
| "expression (arrayelems)"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| skip_token (RIGHT_SQUARE); |
| |
| std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems ( |
| new AST::ArrayElemsCopied (std::move (initial_expr.value ()), |
| std::move (copy_amount.value ()), |
| locus)); |
| return std::unique_ptr<AST::ArrayExpr> ( |
| new AST::ArrayExpr (std::move (copied_array_elems), |
| std::move (inner_attrs), |
| std::move (outer_attrs), locus)); |
| } |
| else if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) |
| { |
| // single-element array expression |
| std::vector<std::unique_ptr<AST::Expr>> exprs; |
| exprs.reserve (1); |
| exprs.push_back (std::move (initial_expr.value ())); |
| exprs.shrink_to_fit (); |
| |
| skip_token (RIGHT_SQUARE); |
| |
| std::unique_ptr<AST::ArrayElemsValues> array_elems ( |
| new AST::ArrayElemsValues (std::move (exprs), locus)); |
| return std::unique_ptr<AST::ArrayExpr> ( |
| new AST::ArrayExpr (std::move (array_elems), |
| std::move (inner_attrs), |
| std::move (outer_attrs), locus)); |
| } |
| else if (lexer.peek_token ()->get_id () == COMMA) |
| { |
| // multi-element array expression (or trailing comma) |
| std::vector<std::unique_ptr<AST::Expr>> exprs; |
| exprs.push_back (std::move (initial_expr.value ())); |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () == COMMA) |
| { |
| lexer.skip_token (); |
| |
| // quick break if right square bracket |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) |
| break; |
| |
| // parse expression (required) |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse element in array expression"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| exprs.push_back (std::move (expr.value ())); |
| |
| t = lexer.peek_token (); |
| } |
| |
| skip_token (RIGHT_SQUARE); |
| |
| exprs.shrink_to_fit (); |
| |
| std::unique_ptr<AST::ArrayElemsValues> array_elems ( |
| new AST::ArrayElemsValues (std::move (exprs), locus)); |
| return std::unique_ptr<AST::ArrayExpr> ( |
| new AST::ArrayExpr (std::move (array_elems), |
| std::move (inner_attrs), |
| std::move (outer_attrs), locus)); |
| } |
| else |
| { |
| // error |
| Error error (lexer.peek_token ()->get_locus (), |
| "unexpected token %qs in array expression (arrayelems)", |
| lexer.peek_token ()->get_token_description ()); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::MALFORMED); |
| } |
| } |
| } |
| |
| // Parses a grouped or tuple expression (disambiguates). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ExprWithoutBlock>, Parse::Error::Node> |
| Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr ( |
| AST::AttrVec outer_attrs, location_t pratt_parsed_loc) |
| { |
| // adjustment to allow Pratt parsing to reuse function without copy-paste |
| location_t locus = pratt_parsed_loc; |
| if (locus == UNKNOWN_LOCATION) |
| { |
| locus = lexer.peek_token ()->get_locus (); |
| skip_token (LEFT_PAREN); |
| } |
| |
| // parse optional inner attributes |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) |
| { |
| // must be empty tuple |
| lexer.skip_token (); |
| |
| // create tuple with empty tuple elems |
| return std::unique_ptr<AST::TupleExpr> ( |
| new AST::TupleExpr (std::vector<std::unique_ptr<AST::Expr>> (), |
| std::move (inner_attrs), std::move (outer_attrs), |
| locus)); |
| } |
| |
| // parse first expression (required) |
| auto first_expr = parse_expr (); |
| if (!first_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse expression in grouped or tuple expression"); |
| add_error (std::move (error)); |
| |
| // skip after somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| |
| // detect whether grouped expression with right parentheses as next token |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) |
| { |
| // must be grouped expr |
| lexer.skip_token (); |
| |
| // create grouped expr |
| return std::unique_ptr<AST::GroupedExpr> ( |
| new AST::GroupedExpr (std::move (first_expr.value ()), |
| std::move (inner_attrs), std::move (outer_attrs), |
| locus)); |
| } |
| else if (lexer.peek_token ()->get_id () == COMMA) |
| { |
| // tuple expr |
| std::vector<std::unique_ptr<AST::Expr>> exprs; |
| exprs.push_back (std::move (first_expr.value ())); |
| |
| // parse potential other tuple exprs |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () == COMMA) |
| { |
| lexer.skip_token (); |
| |
| // break out if right paren |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) |
| break; |
| |
| // parse expr, which is now required |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse expr in tuple expr"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> ( |
| Parse::Error::Node::CHILD_ERROR); |
| } |
| exprs.push_back (std::move (expr.value ())); |
| |
| t = lexer.peek_token (); |
| } |
| |
| // skip right paren |
| skip_token (RIGHT_PAREN); |
| |
| return std::unique_ptr<AST::TupleExpr> ( |
| new AST::TupleExpr (std::move (exprs), std::move (inner_attrs), |
| std::move (outer_attrs), locus)); |
| } |
| else |
| { |
| // error |
| const_TokenPtr t = lexer.peek_token (); |
| Error error (t->get_locus (), |
| "unexpected token %qs in grouped or tuple expression " |
| "(parenthesised expression) - expected %<)%> for grouped " |
| "expr and %<,%> for tuple expr", |
| t->get_token_description ()); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); |
| } |
| } |
| |
| // Parses a struct expression field. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::StructExprField>, |
| Parse::Error::StructExprField> |
| Parser<ManagedTokenSource>::parse_struct_expr_field () |
| { |
| AST::AttrVec outer_attrs = parse_outer_attributes (); |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case IDENTIFIER: |
| if (lexer.peek_token (1)->get_id () == COLON) |
| { |
| // struct expr field with identifier and expr |
| Identifier ident = {t}; |
| lexer.skip_token (1); |
| |
| // parse expression (required) |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (t->get_locus (), |
| "failed to parse struct expression field with " |
| "identifier and expression"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::StructExprField> ( |
| Parse::Error::StructExprField::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::StructExprFieldIdentifierValue> ( |
| new AST::StructExprFieldIdentifierValue (std::move (ident), |
| std::move (expr.value ()), |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| else |
| { |
| // struct expr field with identifier only |
| Identifier ident{t}; |
| lexer.skip_token (); |
| |
| return std::unique_ptr<AST::StructExprFieldIdentifier> ( |
| new AST::StructExprFieldIdentifier (std::move (ident), |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| case INT_LITERAL: |
| { |
| // parse tuple index field |
| int index = atoi (t->get_str ().c_str ()); |
| lexer.skip_token (); |
| |
| if (!skip_token (COLON)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::StructExprField> ( |
| Parse::Error::StructExprField::MALFORMED); |
| } |
| |
| // parse field expression (required) |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (t->get_locus (), |
| "failed to parse expr in struct (or enum) expr " |
| "field with tuple index"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::StructExprField> ( |
| Parse::Error::StructExprField::CHILD_ERROR); |
| } |
| |
| return std::unique_ptr<AST::StructExprFieldIndexValue> ( |
| new AST::StructExprFieldIndexValue (index, std::move (expr.value ()), |
| std::move (outer_attrs), |
| t->get_locus ())); |
| } |
| case DOT_DOT: |
| /* this is a struct base and can't be parsed here, so just return |
| * nothing without erroring */ |
| |
| return tl::unexpected<Parse::Error::StructExprField> ( |
| Parse::Error::StructExprField::STRUCT_BASE); |
| default: |
| add_error ( |
| Error (t->get_locus (), |
| "unrecognised token %qs as first token of struct expr field - " |
| "expected identifier or integer literal", |
| t->get_token_description ())); |
| |
| return tl::unexpected<Parse::Error::StructExprField> ( |
| Parse::Error::StructExprField::MALFORMED); |
| } |
| } |
| |
| /* Pratt parser impl of parse_expr. FIXME: this is only provisional and |
| * probably will be changed. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_expr (int right_binding_power, |
| AST::AttrVec outer_attrs, |
| ParseRestrictions restrictions) |
| { |
| const_TokenPtr current_token = lexer.peek_token (); |
| // Special hack because we are allowed to return nullptr, in that case we |
| // don't want to skip the token, since we don't actually parse it. But if |
| // null isn't allowed it indicates an error, and we want to skip past that. |
| // So return early if it is one of the tokens that ends an expression |
| // (or at least cannot start a new expression). |
| if (restrictions.expr_can_be_null) |
| { |
| TokenId id = current_token->get_id (); |
| if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY |
| || id == RIGHT_SQUARE || id == COMMA || id == LEFT_CURLY) |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::NULL_EXPR); |
| } |
| |
| ParseRestrictions null_denotation_restrictions = restrictions; |
| null_denotation_restrictions.expr_can_be_stmt = false; |
| |
| // parse null denotation (unary part of expression) |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr |
| = null_denotation ({}, null_denotation_restrictions); |
| if (!expr) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| if (expr.value () == nullptr) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| return left_denotations (std::move (expr), right_binding_power, |
| std::move (outer_attrs), restrictions); |
| } |
| |
| // Parse expression with lowest left binding power. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs, |
| ParseRestrictions restrictions) |
| { |
| return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions); |
| } |
| |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::left_denotations ( |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr, |
| int right_binding_power, AST::AttrVec outer_attrs, |
| ParseRestrictions restrictions) |
| { |
| if (!expr) |
| { |
| // DEBUG |
| rust_debug ("null denotation is null; returning null for parse_expr"); |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::NULL_DENOTATION); |
| } |
| |
| const_TokenPtr current_token = lexer.peek_token (); |
| |
| if (restrictions.expr_can_be_stmt && !expr.value ()->is_expr_without_block () |
| && current_token->get_id () != DOT |
| && current_token->get_id () != QUESTION_MARK) |
| { |
| rust_debug ("statement expression with block"); |
| expr.value ()->set_outer_attrs (std::move (outer_attrs)); |
| return expr; |
| } |
| |
| restrictions.expr_can_be_stmt = false; |
| |
| // stop parsing if find lower priority token - parse higher priority first |
| while (right_binding_power < left_binding_power (current_token)) |
| { |
| lexer.skip_token (); |
| |
| // FIXME attributes should generally be applied to the null denotation. |
| expr = left_denotation (current_token, std::move (expr.value ()), |
| std::move (outer_attrs), restrictions); |
| |
| if (!expr) |
| { |
| // DEBUG |
| rust_debug ("left denotation is null; returning null for parse_expr"); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::LEFT_DENOTATION); |
| } |
| |
| current_token = lexer.peek_token (); |
| } |
| |
| return expr; |
| } |
| |
| /* Determines action to take when finding token at beginning of expression. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::null_denotation (AST::AttrVec outer_attrs, |
| ParseRestrictions restrictions) |
| { |
| /* note: tok is previous character in input stream, not current one, as |
| * parse_expr skips it before passing it in */ |
| |
| /* as a Pratt parser (which works by decomposing expressions into a null |
| * denotation and then a left denotation), null denotations handle primaries |
| * and unary operands (but only prefix unary operands) */ |
| |
| auto tok = lexer.peek_token (); |
| |
| switch (tok->get_id ()) |
| { |
| case IDENTIFIER: |
| case SELF: |
| case SELF_ALIAS: |
| case DOLLAR_SIGN: |
| case CRATE: |
| case SUPER: |
| case SCOPE_RESOLUTION: |
| { |
| // DEBUG |
| rust_debug ("beginning null denotation identifier handling"); |
| |
| /* best option: parse as path, then extract identifier, macro, |
| * struct/enum, or just path info from it */ |
| AST::PathInExpression path = parse_path_in_expression (); |
| |
| return null_denotation_path (std::move (path), std::move (outer_attrs), |
| restrictions); |
| } |
| case HASH: |
| { |
| // Parse outer attributes and then the expression that follows |
| AST::AttrVec attrs = parse_outer_attributes (); |
| |
| // Merge with any existing outer attributes |
| if (!outer_attrs.empty ()) |
| attrs.insert (attrs.begin (), outer_attrs.begin (), |
| outer_attrs.end ()); |
| |
| // Try to parse the expression that should follow the attributes |
| auto expr = parse_expr (std::move (attrs), restrictions); |
| if (!expr) |
| { |
| /* If parsing failed and we're at a semicolon, provide a better |
| * error |
| */ |
| const_TokenPtr next_tok = lexer.peek_token (); |
| if (next_tok->get_id () == SEMICOLON) |
| add_error (Error (next_tok->get_locus (), |
| "expected expression, found %<;%>")); |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| return expr; |
| } |
| default: |
| if (tok->get_id () == LEFT_SHIFT) |
| { |
| lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE); |
| tok = lexer.peek_token (); |
| } |
| |
| lexer.skip_token (); |
| return null_denotation_not_path (std::move (tok), std::move (outer_attrs), |
| restrictions); |
| } |
| } |
| |
| // Handling of expresions that start with a path for `null_denotation`. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::null_denotation_path ( |
| AST::PathInExpression path, AST::AttrVec outer_attrs, |
| ParseRestrictions restrictions) |
| { |
| rust_debug ("parsing null denotation after path"); |
| |
| // HACK: always make "self" by itself a path (regardless of next |
| // tokens) |
| if (path.is_single_segment () && path.get_segments ()[0].is_lower_self_seg ()) |
| { |
| // HACK: add outer attrs to path |
| path.set_outer_attrs (std::move (outer_attrs)); |
| return std::make_unique<AST::PathInExpression> (std::move (path)); |
| } |
| |
| // branch on next token |
| const_TokenPtr t = lexer.peek_token (); |
| switch (t->get_id ()) |
| { |
| case EXCLAM: |
| { |
| // macro |
| auto macro = parse_macro_invocation_partial (std::move (path), |
| std::move (outer_attrs)); |
| if (macro == nullptr) |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| return std::unique_ptr<AST::Expr> (std::move (macro)); |
| } |
| case LEFT_CURLY: |
| { |
| bool not_a_block = lexer.peek_token (1)->get_id () == IDENTIFIER |
| && (lexer.peek_token (2)->get_id () == COMMA |
| || (lexer.peek_token (2)->get_id () == COLON |
| && (lexer.peek_token (4)->get_id () == COMMA |
| || !Parse::Utils::can_tok_start_type ( |
| lexer.peek_token (3)->get_id ())))); |
| |
| /* definitely not a block: |
| * path '{' ident ',' |
| * path '{' ident ':' [anything] ',' |
| * path '{' ident ':' [not a type] |
| * otherwise, assume block expr and thus path */ |
| // DEBUG |
| rust_debug ("values of lookahead: '%s' '%s' '%s' '%s' ", |
| lexer.peek_token (1)->get_token_description (), |
| lexer.peek_token (2)->get_token_description (), |
| lexer.peek_token (3)->get_token_description (), |
| lexer.peek_token (4)->get_token_description ()); |
| |
| rust_debug ("can be struct expr: '%s', not a block: '%s'", |
| restrictions.can_be_struct_expr ? "true" : "false", |
| not_a_block ? "true" : "false"); |
| |
| // struct/enum expr struct |
| if (!restrictions.can_be_struct_expr && !not_a_block) |
| { |
| // HACK: add outer attrs to path |
| path.set_outer_attrs (std::move (outer_attrs)); |
| return std::unique_ptr<AST::PathInExpression> ( |
| new AST::PathInExpression (std::move (path))); |
| } |
| auto struct_expr |
| = parse_struct_expr_struct_partial (std::move (path), |
| std::move (outer_attrs)); |
| if (struct_expr == nullptr) |
| { |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| return struct_expr; |
| } |
| case LEFT_PAREN: |
| { |
| // struct/enum expr tuple |
| if (!restrictions.can_be_struct_expr) |
| { |
| // assume path is returned |
| // HACK: add outer attributes to path |
| path.set_outer_attrs (std::move (outer_attrs)); |
| return std::make_unique<AST::PathInExpression> (std::move (path)); |
| } |
| auto tuple_expr |
| = parse_struct_expr_tuple_partial (std::move (path), |
| std::move (outer_attrs)); |
| if (tuple_expr == nullptr) |
| { |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| return tuple_expr; |
| } |
| default: |
| // assume path is returned if not single segment |
| if (path.is_single_segment ()) |
| { |
| // FIXME: This should probably be returned as a path. |
| /* HACK: may have to become permanent, but this is my current |
| * identifier expression */ |
| return std::unique_ptr<AST::IdentifierExpr> (new AST::IdentifierExpr ( |
| path.get_segments ()[0].get_ident_segment ().as_string (), {}, |
| path.get_locus ())); |
| } |
| // HACK: add outer attrs to path |
| path.set_outer_attrs (std::move (outer_attrs)); |
| return std::unique_ptr<AST::PathInExpression> ( |
| new AST::PathInExpression (std::move (path))); |
| } |
| rust_unreachable (); |
| } |
| |
| // Handling of expresions that do not start with a path for `null_denotation`. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::null_denotation_not_path ( |
| const_TokenPtr tok, AST::AttrVec outer_attrs, ParseRestrictions restrictions) |
| { |
| switch (tok->get_id ()) |
| { |
| // FIXME: Handle in null_denotation_path? |
| case LEFT_SHIFT: |
| case LEFT_ANGLE: |
| { |
| // qualified path |
| // HACK: add outer attrs to path |
| AST::QualifiedPathInExpression path |
| = parse_qualified_path_in_expression (tok->get_locus ()); |
| path.set_outer_attrs (std::move (outer_attrs)); |
| return std::make_unique<AST::QualifiedPathInExpression> ( |
| std::move (path)); |
| } |
| // FIXME: delegate to parse_literal_expr instead? would have to rejig |
| // tokens and whatever. |
| // FIXME: for literal exprs, outer attrs should be passed in, and later |
| // error if it does not make up the entire statement. |
| case INT_LITERAL: |
| // we should check the range, but ignore for now |
| // encode as int? |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::INT, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case FLOAT_LITERAL: |
| // encode as float? |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case STRING_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case BYTE_STRING_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE_STRING, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case RAW_STRING_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::RAW_STRING, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case CHAR_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case BYTE_CHAR_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE, |
| tok->get_type_hint (), {}, tok->get_locus ())); |
| case TRUE_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (Values::Keywords::TRUE_LITERAL, |
| AST::Literal::BOOL, tok->get_type_hint (), {}, |
| tok->get_locus ())); |
| case FALSE_LITERAL: |
| return std::unique_ptr<AST::LiteralExpr> ( |
| new AST::LiteralExpr (Values::Keywords::FALSE_LITERAL, |
| AST::Literal::BOOL, tok->get_type_hint (), {}, |
| tok->get_locus ())); |
| case LEFT_PAREN: |
| { |
| auto grouped_or_tuple_expr |
| = parse_grouped_or_tuple_expr (std::move (outer_attrs), |
| tok->get_locus ()); |
| if (grouped_or_tuple_expr) |
| return std::move (grouped_or_tuple_expr.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| /*case PLUS: { // unary plus operator |
| // invoke parse_expr recursively with appropriate priority, etc. for |
| below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS); |
| |
| if (expr == nullptr) |
| return nullptr; |
| // can only apply to integer and float expressions |
| if (expr->get_type() != integer_type_node || expr->get_type() != |
| float_type_node) { rust_error_at(tok->get_locus(), "operand of unary |
| plus must be int or float but it is %s", print_type(expr->get_type())); |
| return nullptr; |
| } |
| |
| return Tree(expr, tok->get_locus()); |
| }*/ |
| // Rust has no unary plus operator |
| case MINUS: |
| { // unary minus |
| ParseRestrictions entered_from_unary; |
| entered_from_unary.entered_from_unary = true; |
| if (!restrictions.can_be_struct_expr) |
| entered_from_unary.can_be_struct_expr = false; |
| auto expr = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary); |
| |
| if (!expr) |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| // can only apply to integer and float expressions |
| /*if (expr.get_type() != integer_type_node || expr.get_type() != |
| float_type_node) { rust_error_at(tok->get_locus(), "operand of unary |
| minus must be int or float but it is %s", |
| print_type(expr.get_type())); return Tree::error(); |
| }*/ |
| /* FIXME: when implemented the "get type" method on expr, ensure it is |
| * int or float type (except unsigned int). Actually, this would |
| * probably have to be done in semantic analysis (as type checking). |
| */ |
| |
| /* FIXME: allow outer attributes on these expressions by having an |
| * outer attrs parameter in function*/ |
| return std::make_unique<AST::NegationExpr> (std::move (expr.value ()), |
| NegationOperator::NEGATE, |
| std::move (outer_attrs), |
| tok->get_locus ()); |
| } |
| case EXCLAM: |
| { // logical or bitwise not |
| ParseRestrictions entered_from_unary; |
| entered_from_unary.entered_from_unary = true; |
| if (!restrictions.can_be_struct_expr) |
| entered_from_unary.can_be_struct_expr = false; |
| auto expr = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary); |
| |
| if (!expr) |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| // can only apply to boolean expressions |
| /*if (expr.get_type() != boolean_type_node) { |
| rust_error_at(tok->get_locus(), |
| "operand of logical not must be a boolean but it is %s", |
| print_type(expr.get_type())); |
| return Tree::error(); |
| }*/ |
| /* FIXME: type checking for boolean or integer expressions in semantic |
| * analysis */ |
| |
| // FIXME: allow outer attributes on these expressions |
| return std::make_unique<AST::NegationExpr> (std::move (expr.value ()), |
| NegationOperator::NOT, |
| std::move (outer_attrs), |
| tok->get_locus ()); |
| } |
| case ASTERISK: |
| { |
| /* pointer dereference only - HACK: as struct expressions should |
| * always be value expressions, cannot be dereferenced */ |
| ParseRestrictions entered_from_unary; |
| entered_from_unary.entered_from_unary = true; |
| entered_from_unary.can_be_struct_expr = false; |
| auto expr = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary); |
| if (!expr) |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: allow outer attributes on expression |
| return std::make_unique<AST::DereferenceExpr> (std::move ( |
| expr.value ()), |
| std::move (outer_attrs), |
| tok->get_locus ()); |
| } |
| case AMP: |
| { |
| // (single) "borrow" expression - shared (mutable) or immutable |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr |
| = tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| Mutability mutability = Mutability::Imm; |
| bool raw_borrow = false; |
| |
| ParseRestrictions entered_from_unary; |
| entered_from_unary.entered_from_unary = true; |
| if (!restrictions.can_be_struct_expr) |
| entered_from_unary.can_be_struct_expr = false; |
| |
| auto is_mutability = [] (const_TokenPtr token) { |
| return token->get_id () == CONST || token->get_id () == MUT; |
| }; |
| |
| auto t = lexer.peek_token (); |
| // Weak raw keyword, we look (1) ahead and treat it as an identifier if |
| // there is no mut nor const. |
| if (t->get_id () == IDENTIFIER |
| && t->get_str () == Values::WeakKeywords::RAW |
| && is_mutability (lexer.peek_token (1))) |
| { |
| lexer.skip_token (); |
| switch (lexer.peek_token ()->get_id ()) |
| { |
| case MUT: |
| mutability = Mutability::Mut; |
| break; |
| case CONST: |
| mutability = Mutability::Imm; |
| break; |
| default: |
| rust_error_at (lexer.peek_token ()->get_locus (), |
| "raw borrow should be either const or mut"); |
| } |
| lexer.skip_token (); |
| auto expr_result |
| = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary); |
| if (expr_result) |
| expr = std::move (expr_result.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| raw_borrow = true; |
| } |
| else if (t->get_id () == MUT) |
| { |
| lexer.skip_token (); |
| auto expr_result |
| = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary); |
| if (expr_result) |
| expr = std::move (expr_result.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| mutability = Mutability::Mut; |
| raw_borrow = false; |
| } |
| else |
| { |
| auto expr_result |
| = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary); |
| if (expr_result) |
| expr = std::move (expr_result.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| raw_borrow = false; |
| } |
| |
| // FIXME: allow outer attributes on expression |
| return std::make_unique<AST::BorrowExpr> (std::move (expr.value ()), |
| mutability, raw_borrow, false, |
| std::move (outer_attrs), |
| tok->get_locus ()); |
| } |
| case LOGICAL_AND: |
| { |
| // (double) "borrow" expression - shared (mutable) or immutable |
| std::unique_ptr<AST::Expr> expr = nullptr; |
| Mutability mutability = Mutability::Imm; |
| |
| ParseRestrictions entered_from_unary; |
| entered_from_unary.entered_from_unary = true; |
| |
| if (lexer.peek_token ()->get_id () == MUT) |
| { |
| lexer.skip_token (); |
| auto expr_res |
| = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary); |
| if (!expr_res) |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| expr = std::move (expr_res.value ()); |
| mutability = Mutability::Mut; |
| } |
| else |
| { |
| auto expr_result |
| = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary); |
| if (expr_result) |
| expr = std::move (expr_result.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| mutability = Mutability::Imm; |
| } |
| |
| // FIXME: allow outer attributes on expression |
| return std::make_unique<AST::BorrowExpr> (std::move (expr), mutability, |
| false, true, |
| std::move (outer_attrs), |
| tok->get_locus ()); |
| } |
| case OR: |
| case PIPE: |
| case MOVE: |
| // closure expression |
| { |
| auto ret = parse_closure_expr_pratt (tok, std::move (outer_attrs)); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case DOT_DOT: |
| // either "range to" or "range full" expressions |
| { |
| auto ret |
| = parse_nud_range_exclusive_expr (tok, std::move (outer_attrs)); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case DOT_DOT_EQ: |
| // range to inclusive expr |
| { |
| auto ret = parse_range_to_inclusive_expr (tok, std::move (outer_attrs)); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case RETURN_KW: |
| // FIXME: is this really a null denotation expression? |
| { |
| auto ret |
| = parse_return_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case TRY: |
| // FIXME: is this really a null denotation expression? |
| { |
| auto ret = parse_try_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case BREAK: |
| // FIXME: is this really a null denotation expression? |
| { |
| auto ret |
| = parse_break_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case CONTINUE: |
| return parse_continue_expr (std::move (outer_attrs), tok->get_locus ()); |
| case LEFT_CURLY: |
| // ok - this is an expression with block for once. |
| { |
| auto ret = parse_block_expr (std::move (outer_attrs), tl::nullopt, |
| tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case IF: |
| // if or if let, so more lookahead to find out |
| if (lexer.peek_token ()->get_id () == LET) |
| { |
| // if let expr |
| auto ret |
| = parse_if_let_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| else |
| { |
| // if expr |
| auto ret = parse_if_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case LIFETIME: |
| { |
| auto ret = parse_labelled_loop_expr (tok, std::move (outer_attrs)); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case LOOP: |
| { |
| auto ret = parse_loop_expr (std::move (outer_attrs), tl::nullopt, |
| tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case WHILE: |
| if (lexer.peek_token ()->get_id () == LET) |
| { |
| auto ret = parse_while_let_loop_expr (std::move (outer_attrs)); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| else |
| { |
| auto ret = parse_while_loop_expr (std::move (outer_attrs), |
| tl::nullopt, tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case FOR: |
| { |
| auto ret = parse_for_loop_expr (std::move (outer_attrs), tl::nullopt); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case MATCH_KW: |
| // also an expression with block |
| { |
| auto ret |
| = parse_match_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| case LEFT_SQUARE: |
| // array definition expr (not indexing) |
| { |
| auto ret |
| = parse_array_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case UNSAFE: |
| { |
| auto ret = parse_unsafe_block_expr (std::move (outer_attrs), |
| tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case BOX: |
| { |
| auto ret = parse_box_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| case UNDERSCORE: |
| add_error ( |
| Error (tok->get_locus (), |
| "use of %qs is not allowed on the right-side of an assignment", |
| tok->get_token_description ())); |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| case CONST: |
| { |
| auto ret |
| = parse_const_block_expr (std::move (outer_attrs), tok->get_locus ()); |
| if (ret) |
| return std::move (ret.value ()); |
| else |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| default: |
| if (!restrictions.expr_can_be_null) |
| add_error (Error (tok->get_locus (), |
| "found unexpected token %qs in null denotation", |
| tok->get_token_description ())); |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| } |
| |
| /* Called for each token that can appear in infix (between) position. Can be |
| * operators or other punctuation. Returns a function pointer to member |
| * function that implements the left denotation for the token given. */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok, |
| std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs, |
| ParseRestrictions restrictions) |
| { |
| // Token passed in has already been skipped, so peek gives "next" token |
| switch (tok->get_id ()) |
| { |
| // FIXME: allow for outer attributes to be applied |
| case QUESTION_MARK: |
| { |
| location_t left_locus = left->get_locus (); |
| // error propagation expression - unary postfix |
| return std::make_unique<AST::ErrorPropagationExpr> ( |
| std::move (left), std::move (outer_attrs), left_locus); |
| } |
| case PLUS: |
| // sum expression - binary infix |
| /*return parse_binary_plus_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::ADD, |
| restrictions); |
| case MINUS: |
| // difference expression - binary infix |
| /*return parse_binary_minus_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::SUBTRACT, restrictions); |
| case ASTERISK: |
| // product expression - binary infix |
| /*return parse_binary_mult_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::MULTIPLY, restrictions); |
| case DIV: |
| // quotient expression - binary infix |
| /*return parse_binary_div_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::DIVIDE, restrictions); |
| case PERCENT: |
| // modulo expression - binary infix |
| /*return parse_binary_mod_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::MODULUS, restrictions); |
| case AMP: |
| // logical or bitwise and expression - binary infix |
| /*return parse_bitwise_and_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::BITWISE_AND, restrictions); |
| case PIPE: |
| // logical or bitwise or expression - binary infix |
| /*return parse_bitwise_or_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::BITWISE_OR, restrictions); |
| case CARET: |
| // logical or bitwise xor expression - binary infix |
| /*return parse_bitwise_xor_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::BITWISE_XOR, restrictions); |
| case LEFT_SHIFT: |
| // left shift expression - binary infix |
| /*return parse_left_shift_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::LEFT_SHIFT, restrictions); |
| case RIGHT_SHIFT: |
| // right shift expression - binary infix |
| /*return parse_right_shift_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_arithmetic_or_logical_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| ArithmeticOrLogicalOperator::RIGHT_SHIFT, restrictions); |
| case EQUAL_EQUAL: |
| // equal to expression - binary infix (no associativity) |
| /*return parse_binary_equal_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_comparison_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ComparisonOperator::EQUAL, restrictions); |
| case NOT_EQUAL: |
| // not equal to expression - binary infix (no associativity) |
| /*return parse_binary_not_equal_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_comparison_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ComparisonOperator::NOT_EQUAL, |
| restrictions); |
| case RIGHT_ANGLE: |
| // greater than expression - binary infix (no associativity) |
| /*return parse_binary_greater_than_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_comparison_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ComparisonOperator::GREATER_THAN, |
| restrictions); |
| case LEFT_ANGLE: |
| // less than expression - binary infix (no associativity) |
| /*return parse_binary_less_than_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_comparison_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ComparisonOperator::LESS_THAN, |
| restrictions); |
| case GREATER_OR_EQUAL: |
| // greater than or equal to expression - binary infix (no associativity) |
| /*return parse_binary_greater_equal_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_comparison_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ComparisonOperator::GREATER_OR_EQUAL, |
| restrictions); |
| case LESS_OR_EQUAL: |
| // less than or equal to expression - binary infix (no associativity) |
| /*return parse_binary_less_equal_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_comparison_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| ComparisonOperator::LESS_OR_EQUAL, |
| restrictions); |
| case OR: |
| // lazy logical or expression - binary infix |
| return parse_lazy_or_expr (tok, std::move (left), std::move (outer_attrs), |
| restrictions); |
| case LOGICAL_AND: |
| // lazy logical and expression - binary infix |
| return parse_lazy_and_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions); |
| case AS: |
| /* type cast expression - kind of binary infix (RHS is actually a |
| * TypeNoBounds) */ |
| return parse_type_cast_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions); |
| case EQUAL: |
| // assignment expression - binary infix (note right-to-left |
| // associativity) |
| return parse_assig_expr (tok, std::move (left), std::move (outer_attrs), |
| restrictions); |
| case PLUS_EQ: |
| /* plus-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_plus_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| CompoundAssignmentOperator::ADD, |
| restrictions); |
| case MINUS_EQ: |
| /* minus-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_minus_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::SUBTRACT, restrictions); |
| case ASTERISK_EQ: |
| /* multiply-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_mult_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::MULTIPLY, restrictions); |
| case DIV_EQ: |
| /* division-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_div_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| CompoundAssignmentOperator::DIVIDE, |
| restrictions); |
| case PERCENT_EQ: |
| /* modulo-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_mod_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::MODULUS, restrictions); |
| case AMP_EQ: |
| /* bitwise and-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_and_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::BITWISE_AND, restrictions); |
| case PIPE_EQ: |
| /* bitwise or-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_or_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::BITWISE_OR, restrictions); |
| case CARET_EQ: |
| /* bitwise xor-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_xor_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::BITWISE_XOR, restrictions); |
| case LEFT_SHIFT_EQ: |
| /* left shift-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_left_shift_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::LEFT_SHIFT, restrictions); |
| case RIGHT_SHIFT_EQ: |
| /* right shift-assignment expression - binary infix (note right-to-left |
| * associativity) */ |
| /*return parse_right_shift_assig_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions);*/ |
| return parse_compound_assignment_expr ( |
| tok, std::move (left), std::move (outer_attrs), |
| CompoundAssignmentOperator::RIGHT_SHIFT, restrictions); |
| case DOT_DOT: |
| /* range exclusive expression - binary infix (no associativity) |
| * either "range" or "range from" */ |
| return parse_led_range_exclusive_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions); |
| case DOT_DOT_EQ: |
| /* range inclusive expression - binary infix (no associativity) |
| * unambiguously RangeInclusiveExpr */ |
| return parse_range_inclusive_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions); |
| case SCOPE_RESOLUTION: |
| // path expression - binary infix? FIXME should this even be parsed |
| // here? |
| add_error ( |
| Error (tok->get_locus (), |
| "found scope resolution operator in left denotation " |
| "function - this should probably be handled elsewhere")); |
| |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| case DOT: |
| { |
| /* field expression or method call - relies on parentheses after next |
| * identifier or await if token after is "await" (unary postfix) or |
| * tuple index if token after is a decimal int literal */ |
| |
| const_TokenPtr next_tok = lexer.peek_token (); |
| if (next_tok->get_id () == IDENTIFIER |
| && next_tok->get_str () == Values::Keywords::AWAIT) |
| { |
| // await expression |
| return parse_await_expr (tok, std::move (left), |
| std::move (outer_attrs)); |
| } |
| else if (next_tok->get_id () == INT_LITERAL) |
| { |
| // tuple index expression - TODO check for decimal int literal |
| return parse_tuple_index_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions); |
| } |
| else if (next_tok->get_id () == FLOAT_LITERAL) |
| { |
| // Lexer has misidentified a tuple index as a float literal |
| // eg: `(x, (y, z)).1.0` -> 1.0 has been identified as a float |
| // literal. This means we should split it into three new separate |
| // tokens, the first tuple index, the dot and the second tuple |
| // index. |
| auto current_loc = next_tok->get_locus (); |
| auto str = next_tok->get_str (); |
| auto dot_pos = str.find ("."); |
| auto prefix = str.substr (0, dot_pos); |
| auto suffix = str.substr (dot_pos + 1); |
| if (dot_pos == str.size () - 1) |
| lexer.split_current_token ( |
| {Token::make_int (current_loc, std::move (prefix), |
| CORETYPE_PURE_DECIMAL), |
| Token::make (DOT, current_loc + 1)}); |
| else |
| lexer.split_current_token ( |
| {Token::make_int (current_loc, std::move (prefix), |
| CORETYPE_PURE_DECIMAL), |
| Token::make (DOT, current_loc + 1), |
| Token::make_int (current_loc + 2, std::move (suffix), |
| CORETYPE_PURE_DECIMAL)}); |
| return parse_tuple_index_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions); |
| } |
| else if (next_tok->get_id () == IDENTIFIER |
| && lexer.peek_token (1)->get_id () != LEFT_PAREN |
| && lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION) |
| { |
| /* field expression (or should be) - FIXME: scope resolution right |
| * after identifier should always be method, I'm pretty sure */ |
| return parse_field_access_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions); |
| } |
| else |
| { |
| // method call (probably) |
| return parse_method_call_expr (tok, std::move (left), |
| std::move (outer_attrs), |
| restrictions); |
| } |
| } |
| case LEFT_PAREN: |
| // function call - method call is based on dot notation first |
| return parse_function_call_expr (tok, std::move (left), |
| std::move (outer_attrs), restrictions); |
| case LEFT_SQUARE: |
| // array or slice index expression (pseudo binary infix) |
| return parse_index_expr (tok, std::move (left), std::move (outer_attrs), |
| restrictions); |
| default: |
| add_error (Error (tok->get_locus (), |
| "found unexpected token %qs in left denotation", |
| tok->get_token_description ())); |
| |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| } |
| |
| /* Returns the left binding power for the given ArithmeticOrLogicalExpr type. |
| * TODO make constexpr? Would that even do anything useful? */ |
| inline binding_powers |
| get_lbp_for_arithmetic_or_logical_expr ( |
| AST::ArithmeticOrLogicalExpr::ExprType expr_type) |
| { |
| switch (expr_type) |
| { |
| case ArithmeticOrLogicalOperator::ADD: |
| return LBP_PLUS; |
| case ArithmeticOrLogicalOperator::SUBTRACT: |
| return LBP_MINUS; |
| case ArithmeticOrLogicalOperator::MULTIPLY: |
| return LBP_MUL; |
| case ArithmeticOrLogicalOperator::DIVIDE: |
| return LBP_DIV; |
| case ArithmeticOrLogicalOperator::MODULUS: |
| return LBP_MOD; |
| case ArithmeticOrLogicalOperator::BITWISE_AND: |
| return LBP_AMP; |
| case ArithmeticOrLogicalOperator::BITWISE_OR: |
| return LBP_PIPE; |
| case ArithmeticOrLogicalOperator::BITWISE_XOR: |
| return LBP_CARET; |
| case ArithmeticOrLogicalOperator::LEFT_SHIFT: |
| return LBP_L_SHIFT; |
| case ArithmeticOrLogicalOperator::RIGHT_SHIFT: |
| return LBP_R_SHIFT; |
| default: |
| // WTF? should not happen, this is an error |
| rust_unreachable (); |
| |
| return LBP_PLUS; |
| } |
| } |
| |
| // Parses an arithmetic or logical expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr ( |
| const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec, |
| AST::ArithmeticOrLogicalExpr::ExprType expr_type, |
| ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type), |
| AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), expr_type, locus); |
| } |
| |
| // Parses a binary addition expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_plus_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::ADD, locus); |
| } |
| |
| // Parses a binary subtraction expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_minus_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::SUBTRACT, locus); |
| } |
| |
| // Parses a binary multiplication expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_mult_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_MUL, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::MULTIPLY, locus); |
| } |
| |
| // Parses a binary division expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_div_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_DIV, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::DIVIDE, locus); |
| } |
| |
| // Parses a binary modulo expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_mod_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_MOD, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::MODULUS, locus); |
| } |
| |
| /* Parses a binary bitwise (or eager logical) and expression (with Pratt |
| * parsing). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_bitwise_and_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_AMP, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::BITWISE_AND, locus); |
| } |
| |
| /* Parses a binary bitwise (or eager logical) or expression (with Pratt |
| * parsing). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_bitwise_or_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::BITWISE_OR, locus); |
| } |
| |
| /* Parses a binary bitwise (or eager logical) xor expression (with Pratt |
| * parsing). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_bitwise_xor_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_CARET, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::BITWISE_XOR, locus); |
| } |
| |
| // Parses a binary left shift expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_left_shift_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::LEFT_SHIFT, locus); |
| } |
| |
| // Parses a binary right shift expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_right_shift_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ArithmeticOrLogicalExpr> ( |
| std::move (left), std::move (right.value ()), |
| ArithmeticOrLogicalOperator::RIGHT_SHIFT, locus); |
| } |
| |
| /* Returns the left binding power for the given ComparisonExpr type. |
| * TODO make constexpr? Would that even do anything useful? */ |
| inline binding_powers |
| get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type) |
| { |
| switch (expr_type) |
| { |
| case ComparisonOperator::EQUAL: |
| return LBP_EQUAL; |
| case ComparisonOperator::NOT_EQUAL: |
| return LBP_NOT_EQUAL; |
| case ComparisonOperator::GREATER_THAN: |
| return LBP_GREATER_THAN; |
| case ComparisonOperator::LESS_THAN: |
| return LBP_SMALLER_THAN; |
| case ComparisonOperator::GREATER_OR_EQUAL: |
| return LBP_GREATER_EQUAL; |
| case ComparisonOperator::LESS_OR_EQUAL: |
| return LBP_SMALLER_EQUAL; |
| default: |
| // WTF? should not happen, this is an error |
| rust_unreachable (); |
| |
| return LBP_EQUAL; |
| } |
| } |
| |
| /* Parses a ComparisonExpr of given type and LBP. TODO find a way to only |
| * specify one and have the other looked up - e.g. specify ExprType and |
| * binding power is looked up? */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_comparison_expr ( |
| const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec, |
| AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (get_lbp_for_comparison_expr (expr_type), |
| AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> (std::move (left), |
| std::move (right.value ()), |
| expr_type, locus); |
| } |
| |
| // Parses a binary equal to expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_equal_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> (std::move (left), |
| std::move (right.value ()), |
| ComparisonOperator::EQUAL, |
| locus); |
| } |
| |
| // Parses a binary not equal to expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_not_equal_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> (std::move (left), |
| std::move (right.value ()), |
| ComparisonOperator::NOT_EQUAL, |
| locus); |
| } |
| |
| // Parses a binary greater than expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_greater_than_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> ( |
| std::move (left), std::move (right.value ()), |
| ComparisonOperator::GREATER_THAN, locus); |
| } |
| |
| // Parses a binary less than expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_less_than_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> (std::move (left), |
| std::move (right.value ()), |
| ComparisonOperator::LESS_THAN, |
| locus); |
| } |
| |
| // Parses a binary greater than or equal to expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_greater_equal_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> ( |
| std::move (left), std::move (right.value ()), |
| ComparisonOperator::GREATER_OR_EQUAL, locus); |
| } |
| |
| // Parses a binary less than or equal to expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_binary_less_equal_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::ComparisonExpr> ( |
| std::move (left), std::move (right.value ()), |
| ComparisonOperator::LESS_OR_EQUAL, locus); |
| } |
| |
| // Parses a binary lazy boolean or expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_lazy_or_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::LazyBooleanExpr> ( |
| std::move (left), std::move (right.value ()), |
| LazyBooleanOperator::LOGICAL_OR, locus); |
| } |
| |
| // Parses a binary lazy boolean and expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_lazy_and_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::LazyBooleanExpr> ( |
| std::move (left), std::move (right.value ()), |
| LazyBooleanOperator::LOGICAL_AND, locus); |
| } |
| |
| // Parses a pseudo-binary infix type cast expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::TypeCastExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_type_cast_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, |
| ParseRestrictions restrictions ATTRIBUTE_UNUSED) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto type = parse_type_no_bounds (); |
| if (!type) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: how do I get precedence put in here? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = expr_to_cast->get_locus (); |
| |
| return std::make_unique<AST::TypeCastExpr> (std::move (expr_to_cast), |
| std::move (type), locus); |
| } |
| |
| // Parses a binary assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::AssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::AssignmentExpr> (std::move (left), |
| std::move (right.value ()), |
| std::move (outer_attrs), locus); |
| } |
| |
| /* Returns the left binding power for the given CompoundAssignmentExpr type. |
| * TODO make constexpr? Would that even do anything useful? */ |
| inline binding_powers |
| get_lbp_for_compound_assignment_expr ( |
| AST::CompoundAssignmentExpr::ExprType expr_type) |
| { |
| switch (expr_type) |
| { |
| case CompoundAssignmentOperator::ADD: |
| return LBP_PLUS; |
| case CompoundAssignmentOperator::SUBTRACT: |
| return LBP_MINUS; |
| case CompoundAssignmentOperator::MULTIPLY: |
| return LBP_MUL; |
| case CompoundAssignmentOperator::DIVIDE: |
| return LBP_DIV; |
| case CompoundAssignmentOperator::MODULUS: |
| return LBP_MOD; |
| case CompoundAssignmentOperator::BITWISE_AND: |
| return LBP_AMP; |
| case CompoundAssignmentOperator::BITWISE_OR: |
| return LBP_PIPE; |
| case CompoundAssignmentOperator::BITWISE_XOR: |
| return LBP_CARET; |
| case CompoundAssignmentOperator::LEFT_SHIFT: |
| return LBP_L_SHIFT; |
| case CompoundAssignmentOperator::RIGHT_SHIFT: |
| return LBP_R_SHIFT; |
| default: |
| // WTF? should not happen, this is an error |
| rust_unreachable (); |
| |
| return LBP_PLUS; |
| } |
| } |
| |
| // Parses a compound assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_compound_assignment_expr ( |
| const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec, |
| AST::CompoundAssignmentExpr::ExprType expr_type, |
| ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1, |
| AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), expr_type, locus); |
| } |
| |
| // Parses a binary add-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_plus_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::ADD, locus); |
| } |
| |
| // Parses a binary minus-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_minus_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::SUBTRACT, locus); |
| } |
| |
| // Parses a binary multiplication-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_mult_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::MULTIPLY, locus); |
| } |
| |
| // Parses a binary division-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_div_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::DIVIDE, locus); |
| } |
| |
| // Parses a binary modulo-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_mod_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::MODULUS, locus); |
| } |
| |
| // Parses a binary and-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_and_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::BITWISE_AND, locus); |
| } |
| |
| // Parses a binary or-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_or_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::BITWISE_OR, locus); |
| } |
| |
| // Parses a binary xor-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_xor_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::BITWISE_XOR, locus); |
| } |
| |
| // Parses a binary left shift-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_left_shift_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right |
| = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::LEFT_SHIFT, locus); |
| } |
| |
| // Parses a binary right shift-assignment expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_right_shift_assig_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right |
| = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::CompoundAssignmentExpr> ( |
| std::move (left), std::move (right.value ()), |
| CompoundAssignmentOperator::RIGHT_SHIFT, locus); |
| } |
| |
| // Parses a postfix unary await expression (with Pratt parsing). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::AwaitExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_await_expr ( |
| const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await, |
| AST::AttrVec outer_attrs) |
| { |
| /* skip "await" identifier (as "." has already been consumed in |
| * parse_expression) this assumes that the identifier was already identified |
| * as await */ |
| if (!skip_token (IDENTIFIER)) |
| { |
| Error error (tok->get_locus (), "failed to skip %<await%> in await expr " |
| "- this is probably a deep issue"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // TODO: check inside async block in semantic analysis |
| location_t locus = expr_to_await->get_locus (); |
| |
| return std::unique_ptr<AST::AwaitExpr> ( |
| new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs), |
| locus)); |
| } |
| |
| /* Parses an exclusive range ('..') in left denotation position (i.e. |
| * RangeFromExpr or RangeFromToExpr). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_led_range_exclusive_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // FIXME: this probably parses expressions accidently or whatever |
| // try parsing RHS (as tok has already been consumed in parse_expression) |
| // Can be nullptr, in which case it is a RangeFromExpr, otherwise a |
| // RangeFromToExpr. |
| restrictions.expr_can_be_null = true; |
| auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions); |
| |
| location_t locus = left->get_locus (); |
| |
| if (!right) |
| { |
| // range from expr |
| return std::make_unique<AST::RangeFromExpr> (std::move (left), locus); |
| } |
| else |
| { |
| return std::make_unique<AST::RangeFromToExpr> (std::move (left), |
| std::move (right.value ()), |
| locus); |
| } |
| // FIXME: make non-associative |
| } |
| |
| /* Parses an exclusive range ('..') in null denotation position (i.e. |
| * RangeToExpr or RangeFullExpr). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr ( |
| const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED) |
| { |
| auto restrictions = ParseRestrictions (); |
| restrictions.expr_can_be_null = true; |
| |
| // FIXME: this probably parses expressions accidently or whatever |
| // try parsing RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions); |
| |
| location_t locus = tok->get_locus (); |
| |
| if (!right) |
| { |
| // range from expr |
| return std::make_unique<AST::RangeFullExpr> (locus); |
| } |
| else |
| { |
| return std::make_unique<AST::RangeToExpr> (std::move (right.value ()), |
| locus); |
| } |
| // FIXME: make non-associative |
| } |
| |
| // Parses a full binary range inclusive expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::RangeFromToInclExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_range_inclusive_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: make non-associative |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = left->get_locus (); |
| |
| return std::make_unique<AST::RangeFromToInclExpr> (std::move (left), |
| std::move (right.value ()), |
| locus); |
| } |
| |
| // Parses an inclusive range-to prefix unary expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::RangeToInclExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_range_to_inclusive_expr ( |
| const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| auto right = parse_expr (LBP_DOT_DOT_EQ); |
| if (!right) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| // FIXME: make non-associative |
| |
| // TODO: check types. actually, do so during semantic analysis |
| |
| return std::make_unique<AST::RangeToInclExpr> (std::move (right.value ()), |
| tok->get_locus ()); |
| } |
| |
| // Parses a pseudo-binary infix tuple index expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::TupleIndexExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_tuple_index_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr, |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED) |
| { |
| // parse int literal (as token already skipped) |
| const_TokenPtr index_tok = expect_token (INT_LITERAL); |
| if (index_tok == nullptr) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| |
| std::string index = index_tok->get_str (); |
| |
| // convert to integer |
| if (!index_tok->is_pure_decimal ()) |
| { |
| Error error (index_tok->get_locus (), |
| "tuple index should be a pure decimal literal"); |
| add_error (std::move (error)); |
| } |
| int index_int = atoi (index.c_str ()); |
| |
| location_t locus = tuple_expr->get_locus (); |
| |
| return std::make_unique<AST::TupleIndexExpr> (std::move (tuple_expr), |
| index_int, |
| std::move (outer_attrs), locus); |
| } |
| |
| // Parses a pseudo-binary infix array (or slice) index expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ArrayIndexExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_index_expr ( |
| const_TokenPtr, std::unique_ptr<AST::Expr> array_expr, |
| AST::AttrVec outer_attrs, ParseRestrictions) |
| { |
| // parse RHS (as tok has already been consumed in parse_expression) |
| /*std::unique_ptr<AST::Expr> index_expr |
| = parse_expr (LBP_ARRAY_REF, AST::AttrVec (), |
| restrictions);*/ |
| // TODO: conceptually, should treat [] as brackets, so just parse all expr |
| auto index_expr = parse_expr (); |
| if (!index_expr) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR); |
| |
| // skip ']' at end of array |
| if (!skip_token (RIGHT_SQUARE)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = array_expr->get_locus (); |
| |
| return std::make_unique<AST::ArrayIndexExpr> (std::move (array_expr), |
| std::move (index_expr.value ()), |
| std::move (outer_attrs), locus); |
| } |
| |
| // Parses a pseudo-binary infix struct field access expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::FieldAccessExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_field_access_expr ( |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr, |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED) |
| { |
| /* get field name identifier (assume that this is a field access expr and |
| * not await, for instance) */ |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); |
| if (ident_tok == nullptr) |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| |
| Identifier ident{ident_tok}; |
| |
| location_t locus = struct_expr->get_locus (); |
| |
| // TODO: check types. actually, do so during semantic analysis |
| return std::make_unique<AST::FieldAccessExpr> (std::move (struct_expr), |
| std::move (ident), |
| std::move (outer_attrs), |
| locus); |
| } |
| |
| // Parses a pseudo-binary infix method call expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::MethodCallExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_method_call_expr ( |
| const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr, |
| AST::AttrVec outer_attrs, ParseRestrictions) |
| { |
| // parse path expr segment |
| AST::PathExprSegment segment = parse_path_expr_segment (); |
| if (segment.is_error ()) |
| { |
| Error error (tok->get_locus (), |
| "failed to parse path expr segment of method call expr"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| // skip left parentheses |
| if (!skip_token (LEFT_PAREN)) |
| { |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // parse method params (if they exist) |
| std::vector<std::unique_ptr<AST::Expr>> params; |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () != RIGHT_PAREN) |
| { |
| auto param = parse_expr (); |
| if (!param) |
| { |
| Error error (t->get_locus (), |
| "failed to parse method param in method call"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| params.push_back (std::move (param.value ())); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| break; |
| |
| lexer.skip_token (); |
| t = lexer.peek_token (); |
| } |
| |
| // skip right paren |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // TODO: check types. actually do so in semantic analysis pass. |
| location_t locus = receiver_expr->get_locus (); |
| |
| return std::make_unique<AST::MethodCallExpr> (std::move (receiver_expr), |
| std::move (segment), |
| std::move (params), |
| std::move (outer_attrs), locus); |
| } |
| |
| // Parses a pseudo-binary infix function call expression. |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_function_call_expr ( |
| const_TokenPtr, std::unique_ptr<AST::Expr> function_expr, |
| AST::AttrVec outer_attrs, ParseRestrictions) |
| { |
| // parse function params (if they exist) |
| std::vector<std::unique_ptr<AST::Expr>> params; |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () != RIGHT_PAREN) |
| { |
| auto param = parse_expr (); |
| if (!param) |
| { |
| Error error (t->get_locus (), |
| "failed to parse function param in function call"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| params.push_back (std::move (param.value ())); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| break; |
| |
| lexer.skip_token (); |
| t = lexer.peek_token (); |
| } |
| |
| // skip ')' at end of param list |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // TODO: check types. actually, do so during semantic analysis |
| location_t locus = function_expr->get_locus (); |
| |
| return std::make_unique<AST::CallExpr> (std::move (function_expr), |
| std::move (params), |
| std::move (outer_attrs), locus); |
| } |
| |
| /* Parses a struct expr struct with a path in expression already parsed (but |
| * not |
| * '{' token). */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::StructExprStruct>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_struct_expr_struct_partial ( |
| AST::PathInExpression path, AST::AttrVec outer_attrs) |
| { |
| // assume struct expr struct (as struct-enum disambiguation requires name |
| // lookup) again, make statement if final ';' |
| if (!skip_token (LEFT_CURLY)) |
| { |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // parse inner attributes |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| // branch based on next token |
| const_TokenPtr t = lexer.peek_token (); |
| location_t path_locus = path.get_locus (); |
| switch (t->get_id ()) |
| { |
| case RIGHT_CURLY: |
| // struct with no body |
| lexer.skip_token (); |
| |
| return std::make_unique<AST::StructExprStruct> (std::move (path), |
| std::move (inner_attrs), |
| std::move (outer_attrs), |
| path_locus); |
| case DOT_DOT: |
| /* technically this would give a struct base-only struct, but this |
| * algorithm should work too. As such, AST type not happening. */ |
| case IDENTIFIER: |
| case HASH: |
| case INT_LITERAL: |
| { |
| // struct with struct expr fields |
| |
| // parse struct expr fields |
| std::vector<std::unique_ptr<AST::StructExprField>> fields; |
| |
| while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT) |
| { |
| auto field = parse_struct_expr_field (); |
| if (!field |
| && field.error () != Parse::Error::StructExprField::STRUCT_BASE) |
| { |
| Error error (t->get_locus (), |
| "failed to parse struct (or enum) expr field"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| // DEBUG: |
| rust_debug ("struct/enum expr field validated to not be null"); |
| |
| fields.push_back (std::move (field.value ())); |
| |
| // DEBUG: |
| rust_debug ("struct/enum expr field pushed back"); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| { |
| // DEBUG: |
| rust_debug ("lack of comma detected in struct/enum expr " |
| "fields - break"); |
| break; |
| } |
| lexer.skip_token (); |
| |
| // DEBUG: |
| rust_debug ("struct/enum expr fields comma skipped "); |
| |
| t = lexer.peek_token (); |
| } |
| |
| // DEBUG: |
| rust_debug ("struct/enum expr about to parse struct base "); |
| |
| // parse struct base if it exists |
| AST::StructBase struct_base = AST::StructBase::error (); |
| if (lexer.peek_token ()->get_id () == DOT_DOT) |
| { |
| location_t dot_dot_location = lexer.peek_token ()->get_locus (); |
| lexer.skip_token (); |
| |
| // parse required struct base expr |
| auto base_expr = parse_expr (); |
| if (!base_expr) |
| { |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse struct base expression in struct " |
| "expression"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| // DEBUG: |
| rust_debug ("struct/enum expr - parsed and validated base expr"); |
| |
| struct_base = AST::StructBase (std::move (base_expr.value ()), |
| dot_dot_location); |
| |
| // DEBUG: |
| rust_debug ("assigned struct base to new struct base "); |
| } |
| |
| if (!skip_token (RIGHT_CURLY)) |
| { |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::MALFORMED); |
| } |
| |
| // DEBUG: |
| rust_debug ( |
| "struct/enum expr skipped right curly - done and ready to return"); |
| |
| return std::make_unique<AST::StructExprStructFields> ( |
| std::move (path), std::move (fields), path_locus, |
| std::move (struct_base), std::move (inner_attrs), |
| std::move (outer_attrs)); |
| } |
| default: |
| add_error ( |
| Error (t->get_locus (), |
| "unrecognised token %qs in struct (or enum) expression - " |
| "expected %<}%>, identifier, integer literal, or %<..%>", |
| t->get_token_description ())); |
| |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| } |
| |
| /* Parses a struct expr tuple with a path in expression already parsed (but |
| * not |
| * '(' token). |
| * FIXME: this currently outputs a call expr, as they cannot be disambiguated. |
| * A better solution would be to just get this to call that function directly. |
| * */ |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial ( |
| AST::PathInExpression path, AST::AttrVec outer_attrs) |
| { |
| if (!skip_token (LEFT_PAREN)) |
| { |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| AST::AttrVec inner_attrs = parse_inner_attributes (); |
| |
| std::vector<std::unique_ptr<AST::Expr>> exprs; |
| |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () != RIGHT_PAREN) |
| { |
| // parse expression (required) |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (t->get_locus (), "failed to parse expression in " |
| "struct (or enum) expression tuple"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| exprs.push_back (std::move (expr.value ())); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| break; |
| |
| lexer.skip_token (); |
| |
| t = lexer.peek_token (); |
| } |
| |
| if (!skip_token (RIGHT_PAREN)) |
| { |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| location_t path_locus = path.get_locus (); |
| |
| auto pathExpr = std::make_unique<AST::PathInExpression> (std::move (path)); |
| |
| return std::make_unique<AST::CallExpr> (std::move (pathExpr), |
| std::move (exprs), |
| std::move (outer_attrs), path_locus); |
| } |
| |
| // Parses a closure expression with pratt parsing (from null denotation). |
| template <typename ManagedTokenSource> |
| tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Expr> |
| Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok, |
| AST::AttrVec outer_attrs) |
| { |
| // TODO: does this need pratt parsing (for precedence)? probably not, but |
| // idk |
| location_t locus = tok->get_locus (); |
| bool has_move = false; |
| if (tok->get_id () == MOVE) |
| { |
| has_move = true; |
| tok = lexer.peek_token (); |
| lexer.skip_token (); |
| // skip token and reassign |
| } |
| |
| // handle parameter list |
| std::vector<AST::ClosureParam> params; |
| |
| switch (tok->get_id ()) |
| { |
| case OR: |
| // no parameters, don't skip token |
| break; |
| case PIPE: |
| { |
| // actually may have parameters |
| // don't skip token |
| const_TokenPtr t = lexer.peek_token (); |
| while (t->get_id () != PIPE) |
| { |
| AST::ClosureParam param = parse_closure_param (); |
| if (param.is_error ()) |
| { |
| // TODO is this really an error? |
| Error error (t->get_locus (), "could not parse closure param"); |
| add_error (std::move (error)); |
| |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| params.push_back (std::move (param)); |
| |
| if (lexer.peek_token ()->get_id () != COMMA) |
| { |
| if (lexer.peek_token ()->get_id () == OR) |
| lexer.split_current_token (PIPE, PIPE); |
| // not an error but means param list is done |
| break; |
| } |
| // skip comma |
| lexer.skip_token (); |
| |
| if (lexer.peek_token ()->get_id () == OR) |
| lexer.split_current_token (PIPE, PIPE); |
| |
| t = lexer.peek_token (); |
| } |
| |
| if (!skip_token (PIPE)) |
| { |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::MALFORMED); |
| } |
| break; |
| } |
| default: |
| add_error (Error (tok->get_locus (), |
| "unexpected token %qs in closure expression - expected " |
| "%<|%> or %<||%>", |
| tok->get_token_description ())); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED); |
| } |
| |
| // again branch based on next token |
| tok = lexer.peek_token (); |
| if (tok->get_id () == RETURN_TYPE) |
| { |
| // must be return type closure with block expr |
| |
| // skip "return type" token |
| lexer.skip_token (); |
| |
| // parse actual type, which is required |
| auto type = parse_type_no_bounds (); |
| if (!type) |
| { |
| // error |
| Error error (tok->get_locus (), "failed to parse type for closure"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| // parse block expr, which is required |
| auto block = parse_block_expr (); |
| if (!block) |
| { |
| // error |
| Error error (lexer.peek_token ()->get_locus (), |
| "failed to parse block expr in closure"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| return std::make_unique<AST::ClosureExprInnerTyped> ( |
| std::move (type), std::move (block.value ()), std::move (params), locus, |
| has_move, std::move (outer_attrs)); |
| } |
| else |
| { |
| // must be expr-only closure |
| |
| // parse expr, which is required |
| auto expr = parse_expr (); |
| if (!expr) |
| { |
| Error error (tok->get_locus (), |
| "failed to parse expression in closure"); |
| add_error (std::move (error)); |
| |
| // skip somewhere? |
| return tl::unexpected<Parse::Error::Expr> ( |
| Parse::Error::Expr::CHILD_ERROR); |
| } |
| |
| return std::make_unique<AST::ClosureExprInner> (std::move (expr.value ()), |
| std::move (params), locus, |
| has_move, |
| std::move (outer_attrs)); |
| } |
| } |
| |
| } // namespace Rust |