LCOV - code coverage report
Current view: top level - gcc/rust/parse - rust-parse-impl-expr.hxx (source / functions) Coverage Total Hit
Test: gcc.info Lines: 64.2 % 1963 1260
Test Date: 2026-02-28 14:20:25 Functions: 48.1 % 156 75
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2025-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : /* DO NOT INCLUDE ANYWHERE - this is automatically included
      20              :  *   by rust-parse-impl.h
      21              :  * This is also the reason why there are no include guards. */
      22              : 
      23              : #include "rust-parse.h"
      24              : 
      25              : namespace Rust {
      26              : 
      27              : // Parses a block expression, including the curly braces at start and end.
      28              : template <typename ManagedTokenSource>
      29              : tl::expected<std::unique_ptr<AST::BlockExpr>, Parse::Error::Node>
      30        22194 : Parser<ManagedTokenSource>::parse_block_expr (
      31              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label,
      32              :   location_t pratt_parsed_loc)
      33              : {
      34        22194 :   location_t locus = pratt_parsed_loc;
      35        22194 :   if (locus == UNKNOWN_LOCATION)
      36              :     {
      37        20667 :       locus = lexer.peek_token ()->get_locus ();
      38        20667 :       if (!skip_token (LEFT_CURLY))
      39              :         {
      40            0 :           skip_after_end_block ();
      41              :           return tl::unexpected<Parse::Error::Node> (
      42            0 :             Parse::Error::Node::MALFORMED);
      43              :         }
      44              :     }
      45              : 
      46        22194 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
      47              : 
      48              :   // parse statements and expression
      49        22194 :   std::vector<std::unique_ptr<AST::Stmt>> stmts;
      50        22194 :   std::unique_ptr<AST::Expr> expr = nullptr;
      51              : 
      52        22194 :   const_TokenPtr t = lexer.peek_token ();
      53        60820 :   while (t->get_id () != RIGHT_CURLY)
      54              :     {
      55        38626 :       auto expr_or_stmt = parse_stmt_or_expr ();
      56        38626 :       if (!expr_or_stmt)
      57              :         {
      58           32 :           skip_after_end_block ();
      59              :           return tl::unexpected<Parse::Error::Node> (
      60           32 :             Parse::Error::Node::CHILD_ERROR);
      61              :         }
      62              : 
      63        38594 :       t = lexer.peek_token ();
      64              : 
      65        38594 :       if (expr_or_stmt->stmt != nullptr)
      66              :         {
      67        22606 :           stmts.push_back (std::move (expr_or_stmt->stmt));
      68              :         }
      69              :       else
      70              :         {
      71              :           // assign to expression and end parsing inside
      72        15988 :           expr = std::move (expr_or_stmt->expr);
      73              :         }
      74              :     }
      75              : 
      76        22162 :   location_t end_locus = t->get_locus ();
      77              : 
      78        22162 :   if (!skip_token (RIGHT_CURLY))
      79              :     {
      80              :       // We don't need to throw an error as it already reported by skip_token
      81            0 :       skip_after_end_block ();
      82            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
      83              :     }
      84              : 
      85              :   // grammar allows for empty block expressions
      86              : 
      87        22162 :   stmts.shrink_to_fit ();
      88              : 
      89        22162 :   return std::unique_ptr<AST::BlockExpr> (
      90        22162 :     new AST::BlockExpr (std::move (stmts), std::move (expr),
      91              :                         std::move (inner_attrs), std::move (outer_attrs),
      92        22162 :                         std::move (label), locus, end_locus));
      93        22194 : }
      94              : 
      95              : /* Parse an anonymous const expression. This can be a regular const expression
      96              :  * or an underscore for deferred const inference */
      97              : template <typename ManagedTokenSource>
      98              : tl::expected<AST::AnonConst, Parse::Error::Node>
      99          668 : Parser<ManagedTokenSource>::parse_anon_const ()
     100              : {
     101          668 :   auto current = lexer.peek_token ();
     102          668 :   auto locus = current->get_locus ();
     103              : 
     104              :   // Special case deferred inference constants
     105          668 :   if (maybe_skip_token (UNDERSCORE))
     106           11 :     return AST::AnonConst (locus);
     107              : 
     108          657 :   auto expr = parse_expr ();
     109              : 
     110          657 :   if (!expr)
     111            1 :     return tl::make_unexpected (Parse::Error::Node{});
     112              : 
     113         1312 :   return AST::AnonConst (std::move (expr.value ()), locus);
     114          657 : }
     115              : 
     116              : /* Parse a "const block", a block preceded by the `const` keyword whose
     117              :  * statements can be const evaluated and used in constant contexts */
     118              : template <typename ManagedTokenSource>
     119              : tl::expected<std::unique_ptr<AST::ConstBlock>, Parse::Error::Node>
     120           15 : Parser<ManagedTokenSource>::parse_const_block_expr (AST::AttrVec outer_attrs,
     121              :                                                     location_t locus)
     122              : {
     123           15 :   auto block_res = parse_block_expr ();
     124              : 
     125           15 :   if (!block_res)
     126              :     {
     127            0 :       add_error (Error (locus, "failed to parse inner block in const block"));
     128            0 :       skip_after_end_block ();
     129              : 
     130              :       return tl::unexpected<Parse::Error::Node> (
     131            0 :         Parse::Error::Node::CHILD_ERROR);
     132              :     }
     133           15 :   auto block = std::move (block_res.value ());
     134              : 
     135           15 :   auto block_locus = block->get_locus ();
     136              : 
     137           30 :   return std::make_unique<AST::ConstBlock> (AST::AnonConst (std::move (block),
     138              :                                                             block_locus),
     139           15 :                                             locus, std::move (outer_attrs));
     140           15 : }
     141              : 
     142              : /* Parses a "grouped" expression (expression in parentheses), used to control
     143              :  * precedence. */
     144              : template <typename ManagedTokenSource>
     145              : tl::expected<std::unique_ptr<AST::GroupedExpr>, Parse::Error::Node>
     146            0 : Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs)
     147              : {
     148            0 :   location_t locus = lexer.peek_token ()->get_locus ();
     149            0 :   skip_token (LEFT_PAREN);
     150              : 
     151            0 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
     152              : 
     153              :   // parse required expr inside parentheses
     154            0 :   auto expr_in_parens = parse_expr ();
     155            0 :   if (!expr_in_parens)
     156              :     {
     157              :       // skip after somewhere?
     158              :       // error?
     159              :       return tl::unexpected<Parse::Error::Node> (
     160            0 :         Parse::Error::Node::CHILD_ERROR);
     161              :     }
     162              : 
     163            0 :   if (!skip_token (RIGHT_PAREN))
     164              :     {
     165              :       // skip after somewhere?
     166            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     167              :     }
     168              : 
     169            0 :   return std::unique_ptr<AST::GroupedExpr> (
     170            0 :     new AST::GroupedExpr (std::move (expr_in_parens.value ()),
     171              :                           std::move (inner_attrs), std::move (outer_attrs),
     172            0 :                           locus));
     173            0 : }
     174              : 
     175              : // Parses a closure expression (closure definition).
     176              : template <typename ManagedTokenSource>
     177              : tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Node>
     178            0 : Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs)
     179              : {
     180            0 :   location_t locus = lexer.peek_token ()->get_locus ();
     181              :   // detect optional "move"
     182            0 :   bool has_move = false;
     183            0 :   if (lexer.peek_token ()->get_id () == MOVE)
     184              :     {
     185            0 :       lexer.skip_token ();
     186            0 :       has_move = true;
     187              :     }
     188              : 
     189              :   // handle parameter list
     190            0 :   std::vector<AST::ClosureParam> params;
     191              : 
     192            0 :   const_TokenPtr t = lexer.peek_token ();
     193            0 :   switch (t->get_id ())
     194              :     {
     195            0 :     case OR:
     196              :       // skip token, no parameters
     197            0 :       lexer.skip_token ();
     198              :       break;
     199            0 :     case PIPE:
     200              :       // actually may have parameters
     201            0 :       lexer.skip_token ();
     202            0 :       t = lexer.peek_token ();
     203              : 
     204            0 :       while (t->get_id () != PIPE)
     205              :         {
     206            0 :           AST::ClosureParam param = parse_closure_param ();
     207            0 :           if (param.is_error ())
     208              :             {
     209              :               // TODO is this really an error?
     210            0 :               Error error (t->get_locus (), "could not parse closure param");
     211            0 :               add_error (std::move (error));
     212              : 
     213              :               break;
     214            0 :             }
     215            0 :           params.push_back (std::move (param));
     216              : 
     217            0 :           if (lexer.peek_token ()->get_id () != COMMA)
     218              :             {
     219            0 :               lexer.skip_token ();
     220              :               // not an error but means param list is done
     221              :               break;
     222              :             }
     223              :           // skip comma
     224            0 :           lexer.skip_token ();
     225              : 
     226            0 :           t = lexer.peek_token ();
     227              :         }
     228            0 :       params.shrink_to_fit ();
     229              :       break;
     230            0 :     default:
     231            0 :       add_error (Error (t->get_locus (),
     232              :                         "unexpected token %qs in closure expression - expected "
     233              :                         "%<|%> or %<||%>",
     234              :                         t->get_token_description ()));
     235              : 
     236              :       // skip somewhere?
     237            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     238              :     }
     239              : 
     240              :   // again branch based on next token
     241            0 :   t = lexer.peek_token ();
     242            0 :   if (t->get_id () == RETURN_TYPE)
     243              :     {
     244              :       // must be return type closure with block expr
     245              : 
     246              :       // skip "return type" token
     247            0 :       lexer.skip_token ();
     248              : 
     249              :       // parse actual type, which is required
     250            0 :       std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
     251            0 :       if (type == nullptr)
     252              :         {
     253              :           // error
     254            0 :           Error error (t->get_locus (), "failed to parse type for closure");
     255            0 :           add_error (std::move (error));
     256              : 
     257              :           // skip somewhere?
     258              :           return tl::unexpected<Parse::Error::Node> (
     259            0 :             Parse::Error::Node::CHILD_ERROR);
     260            0 :         }
     261              : 
     262              :       // parse block expr, which is required
     263            0 :       auto block = parse_block_expr ();
     264            0 :       if (!block)
     265              :         {
     266              :           // error
     267            0 :           Error error (lexer.peek_token ()->get_locus (),
     268              :                        "failed to parse block expr in closure");
     269            0 :           add_error (std::move (error));
     270              : 
     271              :           // skip somewhere?
     272              :           return tl::unexpected<Parse::Error::Node> (
     273            0 :             Parse::Error::Node::CHILD_ERROR);
     274            0 :         }
     275              : 
     276            0 :       return std::unique_ptr<AST::ClosureExprInnerTyped> (
     277            0 :         new AST::ClosureExprInnerTyped (std::move (type),
     278            0 :                                         std::move (block.value ()),
     279              :                                         std::move (params), locus, has_move,
     280            0 :                                         std::move (outer_attrs)));
     281            0 :     }
     282              :   else
     283              :     {
     284              :       // must be expr-only closure
     285              : 
     286              :       // parse expr, which is required
     287            0 :       auto expr = parse_expr ();
     288            0 :       if (!expr)
     289              :         {
     290            0 :           Error error (t->get_locus (),
     291              :                        "failed to parse expression in closure");
     292            0 :           add_error (std::move (error));
     293              : 
     294              :           // skip somewhere?
     295              :           return tl::unexpected<Parse::Error::Node> (
     296            0 :             Parse::Error::Node::CHILD_ERROR);
     297            0 :         }
     298              : 
     299            0 :       return std::unique_ptr<AST::ClosureExprInner> (
     300            0 :         new AST::ClosureExprInner (std::move (expr.value ()),
     301              :                                    std::move (params), locus, has_move,
     302            0 :                                    std::move (outer_attrs)));
     303            0 :     }
     304            0 : }
     305              : 
     306              : // Parses a literal token (to literal expression).
     307              : template <typename ManagedTokenSource>
     308              : tl::expected<std::unique_ptr<AST::LiteralExpr>, Parse::Error::Node>
     309         3690 : Parser<ManagedTokenSource>::parse_literal_expr (AST::AttrVec outer_attrs)
     310              : {
     311              :   // TODO: change if literal representation in lexer changes
     312              : 
     313         3690 :   std::string literal_value;
     314         3690 :   AST::Literal::LitType type = AST::Literal::STRING;
     315              : 
     316              :   // branch based on token
     317         3690 :   const_TokenPtr t = lexer.peek_token ();
     318         3690 :   switch (t->get_id ())
     319              :     {
     320            2 :     case CHAR_LITERAL:
     321            2 :       type = AST::Literal::CHAR;
     322            2 :       literal_value = t->get_str ();
     323            2 :       lexer.skip_token ();
     324              :       break;
     325          314 :     case STRING_LITERAL:
     326          314 :       type = AST::Literal::STRING;
     327          314 :       literal_value = t->get_str ();
     328          314 :       lexer.skip_token ();
     329              :       break;
     330            0 :     case BYTE_CHAR_LITERAL:
     331            0 :       type = AST::Literal::BYTE;
     332            0 :       literal_value = t->get_str ();
     333            0 :       lexer.skip_token ();
     334              :       break;
     335            1 :     case BYTE_STRING_LITERAL:
     336            1 :       type = AST::Literal::BYTE_STRING;
     337            1 :       literal_value = t->get_str ();
     338            1 :       lexer.skip_token ();
     339              :       break;
     340            0 :     case RAW_STRING_LITERAL:
     341            0 :       type = AST::Literal::RAW_STRING;
     342            0 :       literal_value = t->get_str ();
     343            0 :       lexer.skip_token ();
     344              :       break;
     345         3365 :     case INT_LITERAL:
     346         3365 :       type = AST::Literal::INT;
     347         3365 :       literal_value = t->get_str ();
     348         3365 :       lexer.skip_token ();
     349              :       break;
     350            1 :     case FLOAT_LITERAL:
     351            1 :       type = AST::Literal::FLOAT;
     352            1 :       literal_value = t->get_str ();
     353            1 :       lexer.skip_token ();
     354              :       break;
     355              :     // case BOOL_LITERAL
     356              :     // use true and false keywords rather than "bool literal" Rust terminology
     357            0 :     case TRUE_LITERAL:
     358            0 :       type = AST::Literal::BOOL;
     359            0 :       literal_value = Values::Keywords::TRUE_LITERAL;
     360            0 :       lexer.skip_token ();
     361              :       break;
     362            2 :     case FALSE_LITERAL:
     363            2 :       type = AST::Literal::BOOL;
     364            2 :       literal_value = Values::Keywords::FALSE_LITERAL;
     365            2 :       lexer.skip_token ();
     366              :       break;
     367            5 :     default:
     368              :       // error - cannot be a literal expr
     369            5 :       add_error (Error (t->get_locus (),
     370              :                         "unexpected token %qs when parsing literal expression",
     371              :                         t->get_token_description ()));
     372              : 
     373              :       // skip?
     374            5 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     375              :     }
     376              : 
     377              :   // create literal based on stuff in switch
     378         3685 :   return std::unique_ptr<AST::LiteralExpr> (
     379         7690 :     new AST::LiteralExpr (std::move (literal_value), std::move (type),
     380              :                           t->get_type_hint (), std::move (outer_attrs),
     381         3685 :                           t->get_locus ()));
     382         3690 : }
     383              : 
     384              : template <typename ManagedTokenSource>
     385              : tl::expected<std::unique_ptr<AST::BoxExpr>, Parse::Error::Node>
     386            2 : Parser<ManagedTokenSource>::parse_box_expr (AST::AttrVec outer_attrs,
     387              :                                             location_t pratt_parsed_loc)
     388              : {
     389            2 :   location_t locus = pratt_parsed_loc;
     390            2 :   if (locus == UNKNOWN_LOCATION)
     391              :     {
     392            0 :       locus = lexer.peek_token ()->get_locus ();
     393            0 :       skip_token (BOX);
     394              :     }
     395              : 
     396            2 :   ParseRestrictions restrictions;
     397              :   restrictions.expr_can_be_null = false;
     398              : 
     399            2 :   auto expr = parse_expr (AST::AttrVec (), restrictions);
     400            2 :   if (!expr)
     401            0 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     402              : 
     403            2 :   return std::unique_ptr<AST::BoxExpr> (
     404            2 :     new AST::BoxExpr (std::move (expr.value ()), std::move (outer_attrs),
     405            2 :                       locus));
     406            2 : }
     407              : 
     408              : // Parses a return expression (including any expression to return).
     409              : template <typename ManagedTokenSource>
     410              : tl::expected<std::unique_ptr<AST::ReturnExpr>, Parse::Error::Node>
     411          546 : Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs,
     412              :                                                location_t pratt_parsed_loc)
     413              : {
     414          546 :   location_t locus = pratt_parsed_loc;
     415          546 :   if (locus == UNKNOWN_LOCATION)
     416              :     {
     417            0 :       locus = lexer.peek_token ()->get_locus ();
     418            0 :       skip_token (RETURN_KW);
     419              :     }
     420              : 
     421              :   // parse expression to return, if it exists
     422          546 :   ParseRestrictions restrictions;
     423          546 :   restrictions.expr_can_be_null = true;
     424          546 :   auto returned_expr = parse_expr (AST::AttrVec (), restrictions);
     425          546 :   tl::optional<std::unique_ptr<AST::Expr>> expr = tl::nullopt;
     426          546 :   if (returned_expr)
     427          513 :     expr = std::move (returned_expr.value ());
     428              : 
     429          546 :   return std::make_unique<AST::ReturnExpr> (std::move (expr),
     430          546 :                                             std::move (outer_attrs), locus);
     431          546 : }
     432              : 
     433              : // Parses a try expression.
     434              : template <typename ManagedTokenSource>
     435              : tl::expected<std::unique_ptr<AST::TryExpr>, Parse::Error::Node>
     436            1 : Parser<ManagedTokenSource>::parse_try_expr (AST::AttrVec outer_attrs,
     437              :                                             location_t pratt_parsed_loc)
     438              : {
     439            1 :   location_t locus = pratt_parsed_loc;
     440            1 :   if (locus == UNKNOWN_LOCATION)
     441              :     {
     442            0 :       locus = lexer.peek_token ()->get_locus ();
     443            0 :       skip_token (TRY);
     444              :     }
     445              : 
     446            1 :   auto block_expr = parse_block_expr ();
     447              : 
     448            1 :   if (!block_expr)
     449            0 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     450              : 
     451            1 :   return std::unique_ptr<AST::TryExpr> (
     452            1 :     new AST::TryExpr (std::move (block_expr.value ()), std::move (outer_attrs),
     453            1 :                       locus));
     454            1 : }
     455              : 
     456              : /* Parses a break expression (including any label to break to AND any return
     457              :  * expression). */
     458              : template <typename ManagedTokenSource>
     459              : tl::expected<std::unique_ptr<AST::BreakExpr>, Parse::Error::Node>
     460           79 : Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs,
     461              :                                               location_t pratt_parsed_loc)
     462              : {
     463           79 :   location_t locus = pratt_parsed_loc;
     464           79 :   if (locus == UNKNOWN_LOCATION)
     465              :     {
     466            0 :       locus = lexer.peek_token ()->get_locus ();
     467            0 :       skip_token (BREAK);
     468              :     }
     469              : 
     470           79 :   auto parsed_label = parse_lifetime (false);
     471           79 :   auto label = (parsed_label)
     472           79 :                  ? tl::optional<AST::Lifetime> (parsed_label.value ())
     473              :                  : tl::nullopt;
     474              : 
     475              :   // parse break return expression if it exists
     476           79 :   ParseRestrictions restrictions;
     477           79 :   restrictions.expr_can_be_null = true;
     478           79 :   auto return_expr = parse_expr (AST::AttrVec (), restrictions);
     479              : 
     480           79 :   if (return_expr)
     481           22 :     return std::unique_ptr<AST::BreakExpr> (
     482           66 :       new AST::BreakExpr (std::move (label), std::move (return_expr.value ()),
     483           22 :                           std::move (outer_attrs), locus));
     484           57 :   else if (return_expr.error () == Parse::Error::Expr::NULL_EXPR)
     485           57 :     return std::unique_ptr<AST::BreakExpr> (
     486          114 :       new AST::BreakExpr (std::move (label), tl::nullopt,
     487           57 :                           std::move (outer_attrs), locus));
     488              :   else
     489            0 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     490          102 : }
     491              : 
     492              : // Parses a continue expression (including any label to continue from).
     493              : template <typename ManagedTokenSource>
     494              : std::unique_ptr<AST::ContinueExpr>
     495           17 : Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs,
     496              :                                                  location_t pratt_parsed_loc)
     497              : {
     498           17 :   location_t locus = pratt_parsed_loc;
     499           17 :   if (locus == UNKNOWN_LOCATION)
     500              :     {
     501            0 :       locus = lexer.peek_token ()->get_locus ();
     502            0 :       skip_token (CONTINUE);
     503              :     }
     504              : 
     505           17 :   auto parsed_label = parse_lifetime (false);
     506           17 :   auto label = (parsed_label)
     507           17 :                  ? tl::optional<AST::Lifetime> (parsed_label.value ())
     508              :                  : tl::nullopt;
     509              : 
     510              :   return std::make_unique<AST::ContinueExpr> (std::move (label),
     511           17 :                                               std::move (outer_attrs), locus);
     512           17 : }
     513              : 
     514              : /* Parses an if expression of any kind, including with else, else if, else if
     515              :  * let, and neither. Note that any outer attributes will be ignored because if
     516              :  * expressions don't support them. */
     517              : template <typename ManagedTokenSource>
     518              : tl::expected<std::unique_ptr<AST::IfExpr>, Parse::Error::Node>
     519         1694 : Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
     520              :                                            location_t pratt_parsed_loc)
     521              : {
     522              :   // TODO: make having outer attributes an error?
     523         1694 :   location_t locus = pratt_parsed_loc;
     524         1694 :   if (locus == UNKNOWN_LOCATION)
     525              :     {
     526          323 :       locus = lexer.peek_token ()->get_locus ();
     527          323 :       if (!skip_token (IF))
     528              :         {
     529            0 :           skip_after_end_block ();
     530              :           return tl::unexpected<Parse::Error::Node> (
     531            0 :             Parse::Error::Node::MALFORMED);
     532              :         }
     533              :     }
     534              : 
     535              :   // detect accidental if let
     536         3388 :   if (lexer.peek_token ()->get_id () == LET)
     537              :     {
     538            0 :       Error error (lexer.peek_token ()->get_locus (),
     539              :                    "if let expression probably exists, but is being parsed "
     540              :                    "as an if expression. This may be a parser error");
     541            0 :       add_error (std::move (error));
     542              : 
     543              :       // skip somewhere?
     544            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     545            0 :     }
     546              : 
     547              :   /* parse required condition expr - HACK to prevent struct expr from being
     548              :    * parsed */
     549         1694 :   ParseRestrictions no_struct_expr;
     550         1694 :   no_struct_expr.can_be_struct_expr = false;
     551         1694 :   auto condition = parse_expr ({}, no_struct_expr);
     552         1694 :   if (!condition)
     553              :     {
     554            0 :       Error error (lexer.peek_token ()->get_locus (),
     555              :                    "failed to parse condition expression in if expression");
     556            0 :       add_error (std::move (error));
     557              : 
     558              :       // skip somewhere?
     559              :       return tl::unexpected<Parse::Error::Node> (
     560            0 :         Parse::Error::Node::CHILD_ERROR);
     561            0 :     }
     562              : 
     563              :   // parse required block expr
     564         1694 :   auto if_body = parse_block_expr ();
     565         1694 :   if (!if_body)
     566            1 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     567              : 
     568              :   // branch to parse end or else (and then else, else if, or else if let)
     569         3386 :   if (lexer.peek_token ()->get_id () != ELSE)
     570              :     {
     571              :       // single selection - end of if expression
     572          480 :       return std::unique_ptr<AST::IfExpr> (
     573          480 :         new AST::IfExpr (std::move (condition.value ()),
     574          480 :                          std::move (if_body.value ()), std::move (outer_attrs),
     575          480 :                          locus));
     576              :     }
     577              :   else
     578              :     {
     579              :       // double or multiple selection - branch on end, else if, or else if let
     580              : 
     581              :       // skip "else"
     582         1213 :       lexer.skip_token ();
     583              : 
     584              :       // branch on whether next token is '{' or 'if'
     585         1213 :       const_TokenPtr t = lexer.peek_token ();
     586         1213 :       switch (t->get_id ())
     587              :         {
     588          890 :         case LEFT_CURLY:
     589              :           {
     590              :             // double selection - else
     591              :             // parse else block expr (required)
     592          890 :             auto else_body = parse_block_expr ();
     593          890 :             if (!else_body)
     594              :               {
     595            0 :                 Error error (lexer.peek_token ()->get_locus (),
     596              :                              "failed to parse else body block expression in "
     597              :                              "if expression");
     598            0 :                 add_error (std::move (error));
     599              : 
     600              :                 // skip somewhere?
     601              :                 return tl::unexpected<Parse::Error::Node> (
     602            0 :                   Parse::Error::Node::CHILD_ERROR);
     603            0 :               }
     604              : 
     605          890 :             return std::unique_ptr<AST::IfExprConseqElse> (
     606         1780 :               new AST::IfExprConseqElse (std::move (condition.value ()),
     607          890 :                                          std::move (if_body.value ()),
     608          890 :                                          std::move (else_body.value ()),
     609          890 :                                          std::move (outer_attrs), locus));
     610          890 :           }
     611          323 :         case IF:
     612              :           {
     613              :             // multiple selection - else if or else if let
     614              :             // branch on whether next token is 'let' or not
     615          646 :             if (lexer.peek_token (1)->get_id () == LET)
     616              :               {
     617              :                 // parse if let expr (required)
     618            1 :                 auto if_let_expr = parse_if_let_expr ();
     619            1 :                 if (!if_let_expr)
     620              :                   {
     621            0 :                     Error error (lexer.peek_token ()->get_locus (),
     622              :                                  "failed to parse (else) if let expression "
     623              :                                  "after if expression");
     624            0 :                     add_error (std::move (error));
     625              : 
     626              :                     // skip somewhere?
     627              :                     return tl::unexpected<Parse::Error::Node> (
     628            0 :                       Parse::Error::Node::CHILD_ERROR);
     629            0 :                   }
     630              : 
     631            1 :                 return std::unique_ptr<AST::IfExprConseqElse> (
     632            2 :                   new AST::IfExprConseqElse (std::move (condition.value ()),
     633            1 :                                              std::move (if_body.value ()),
     634            1 :                                              std::move (if_let_expr.value ()),
     635            1 :                                              std::move (outer_attrs), locus));
     636            1 :               }
     637              :             else
     638              :               {
     639              :                 // parse if expr (required)
     640          322 :                 auto if_expr = parse_if_expr ();
     641          322 :                 if (!if_expr)
     642              :                   {
     643            0 :                     Error error (lexer.peek_token ()->get_locus (),
     644              :                                  "failed to parse (else) if expression after "
     645              :                                  "if expression");
     646            0 :                     add_error (std::move (error));
     647              : 
     648              :                     // skip somewhere?
     649              :                     return tl::unexpected<Parse::Error::Node> (
     650            0 :                       Parse::Error::Node::CHILD_ERROR);
     651            0 :                   }
     652              : 
     653          322 :                 return std::unique_ptr<AST::IfExprConseqElse> (
     654          644 :                   new AST::IfExprConseqElse (std::move (condition.value ()),
     655          322 :                                              std::move (if_body.value ()),
     656          322 :                                              std::move (if_expr.value ()),
     657          322 :                                              std::move (outer_attrs), locus));
     658          322 :               }
     659              :           }
     660            0 :         default:
     661              :           // error - invalid token
     662            0 :           add_error (Error (t->get_locus (),
     663              :                             "unexpected token %qs after else in if expression",
     664              :                             t->get_token_description ()));
     665              : 
     666              :           // skip somewhere?
     667              :           return tl::unexpected<Parse::Error::Node> (
     668            0 :             Parse::Error::Node::MALFORMED);
     669              :         }
     670         1213 :     }
     671         3388 : }
     672              : 
     673              : /* Parses an if let expression of any kind, including with else, else if, else
     674              :  * if let, and none. Note that any outer attributes will be ignored as if let
     675              :  * expressions don't support them. */
     676              : template <typename ManagedTokenSource>
     677              : tl::expected<std::unique_ptr<AST::IfLetExpr>, Parse::Error::Node>
     678           31 : Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
     679              :                                                location_t pratt_parsed_loc)
     680              : {
     681              :   // TODO: make having outer attributes an error?
     682           31 :   location_t locus = pratt_parsed_loc;
     683           31 :   if (locus == UNKNOWN_LOCATION)
     684              :     {
     685            1 :       locus = lexer.peek_token ()->get_locus ();
     686            1 :       if (!skip_token (IF))
     687              :         {
     688            0 :           skip_after_end_block ();
     689              :           return tl::unexpected<Parse::Error::Node> (
     690            0 :             Parse::Error::Node::MALFORMED);
     691              :         }
     692              :     }
     693              : 
     694              :   // detect accidental if expr parsed as if let expr
     695           62 :   if (lexer.peek_token ()->get_id () != LET)
     696              :     {
     697            0 :       Error error (lexer.peek_token ()->get_locus (),
     698              :                    "if expression probably exists, but is being parsed as an "
     699              :                    "if let expression. This may be a parser error");
     700            0 :       add_error (std::move (error));
     701              : 
     702              :       // skip somewhere?
     703            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     704            0 :     }
     705           31 :   lexer.skip_token ();
     706              : 
     707              :   // parse match arm patterns (which are required)
     708           31 :   std::unique_ptr<AST::Pattern> match_arm_pattern
     709              :     = parse_match_arm_pattern (EQUAL);
     710           31 :   if (match_arm_pattern == nullptr)
     711              :     {
     712            0 :       Error error (
     713            0 :         lexer.peek_token ()->get_locus (),
     714              :         "failed to parse any match arm patterns in if let expression");
     715            0 :       add_error (std::move (error));
     716              : 
     717              :       // skip somewhere?
     718              :       return tl::unexpected<Parse::Error::Node> (
     719            0 :         Parse::Error::Node::CHILD_ERROR);
     720            0 :     }
     721              : 
     722           31 :   if (!skip_token (EQUAL))
     723              :     {
     724              :       // skip somewhere?
     725            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     726              :     }
     727              : 
     728              :   // parse expression (required) - HACK to prevent struct expr being parsed
     729           31 :   ParseRestrictions no_struct_expr;
     730           31 :   no_struct_expr.can_be_struct_expr = false;
     731           31 :   auto scrutinee_expr = parse_expr ({}, no_struct_expr);
     732           31 :   if (!scrutinee_expr)
     733              :     {
     734            0 :       Error error (lexer.peek_token ()->get_locus (),
     735              :                    "failed to parse scrutinee expression in if let expression");
     736            0 :       add_error (std::move (error));
     737              : 
     738              :       // skip somewhere?
     739              :       return tl::unexpected<Parse::Error::Node> (
     740            0 :         Parse::Error::Node::CHILD_ERROR);
     741            0 :     }
     742              :   /* TODO: check for expression not being a struct expression or lazy boolean
     743              :    * expression here? or actually probably in semantic analysis. */
     744              : 
     745              :   // parse block expression (required)
     746           31 :   auto if_let_body = parse_block_expr ();
     747           31 :   if (!if_let_body)
     748              :     {
     749            0 :       Error error (
     750            0 :         lexer.peek_token ()->get_locus (),
     751              :         "failed to parse if let body block expression in if let expression");
     752            0 :       add_error (std::move (error));
     753              : 
     754              :       // skip somewhere?
     755              :       return tl::unexpected<Parse::Error::Node> (
     756            0 :         Parse::Error::Node::CHILD_ERROR);
     757            0 :     }
     758              : 
     759              :   // branch to parse end or else (and then else, else if, or else if let)
     760           62 :   if (lexer.peek_token ()->get_id () != ELSE)
     761              :     {
     762              :       // single selection - end of if let expression
     763           38 :       return std::unique_ptr<AST::IfLetExpr> (new AST::IfLetExpr (
     764           19 :         std::move (match_arm_pattern), std::move (scrutinee_expr.value ()),
     765           38 :         std::move (if_let_body.value ()), std::move (outer_attrs), locus));
     766              :     }
     767              :   else
     768              :     {
     769              :       // double or multiple selection - branch on end, else if, or else if let
     770              : 
     771              :       // skip "else"
     772           12 :       lexer.skip_token ();
     773              : 
     774              :       // branch on whether next token is '{' or 'if'
     775           12 :       const_TokenPtr t = lexer.peek_token ();
     776           12 :       switch (t->get_id ())
     777              :         {
     778           11 :         case LEFT_CURLY:
     779              :           {
     780              :             // double selection - else
     781              :             // parse else block expr (required)
     782           11 :             auto else_body = parse_block_expr ();
     783           11 :             if (!else_body)
     784              :               {
     785            0 :                 Error error (lexer.peek_token ()->get_locus (),
     786              :                              "failed to parse else body block expression in "
     787              :                              "if let expression");
     788            0 :                 add_error (std::move (error));
     789              : 
     790              :                 // skip somewhere?
     791              :                 return tl::unexpected<Parse::Error::Node> (
     792            0 :                   Parse::Error::Node::CHILD_ERROR);
     793            0 :               }
     794              : 
     795           11 :             return std::unique_ptr<AST::IfLetExprConseqElse> (
     796           22 :               new AST::IfLetExprConseqElse (std::move (match_arm_pattern),
     797           11 :                                             std::move (scrutinee_expr.value ()),
     798           11 :                                             std::move (if_let_body.value ()),
     799           11 :                                             std::move (else_body.value ()),
     800           11 :                                             std::move (outer_attrs), locus));
     801           11 :           }
     802            1 :         case IF:
     803              :           {
     804              :             // multiple selection - else if or else if let
     805              :             // branch on whether next token is 'let' or not
     806            2 :             if (lexer.peek_token (1)->get_id () == LET)
     807              :               {
     808              :                 // parse if let expr (required)
     809            0 :                 auto if_let_expr = parse_if_let_expr ();
     810            0 :                 if (!if_let_expr)
     811              :                   {
     812            0 :                     Error error (lexer.peek_token ()->get_locus (),
     813              :                                  "failed to parse (else) if let expression "
     814              :                                  "after if let expression");
     815            0 :                     add_error (std::move (error));
     816              : 
     817              :                     // skip somewhere?
     818              :                     return tl::unexpected<Parse::Error::Node> (
     819            0 :                       Parse::Error::Node::CHILD_ERROR);
     820            0 :                   }
     821              : 
     822            0 :                 return std::unique_ptr<AST::IfLetExprConseqElse> (
     823            0 :                   new AST::IfLetExprConseqElse (
     824              :                     std::move (match_arm_pattern),
     825            0 :                     std::move (scrutinee_expr.value ()),
     826            0 :                     std::move (if_let_body.value ()),
     827            0 :                     std::move (if_let_expr.value ()), std::move (outer_attrs),
     828            0 :                     locus));
     829            0 :               }
     830              :             else
     831              :               {
     832              :                 // parse if expr (required)
     833            1 :                 auto if_expr = parse_if_expr ();
     834            1 :                 if (!if_expr)
     835              :                   {
     836            0 :                     Error error (lexer.peek_token ()->get_locus (),
     837              :                                  "failed to parse (else) if expression after "
     838              :                                  "if let expression");
     839            0 :                     add_error (std::move (error));
     840              : 
     841              :                     // skip somewhere?
     842              :                     return tl::unexpected<Parse::Error::Node> (
     843            0 :                       Parse::Error::Node::CHILD_ERROR);
     844            0 :                   }
     845              : 
     846            1 :                 return std::unique_ptr<AST::IfLetExprConseqElse> (
     847            2 :                   new AST::IfLetExprConseqElse (
     848              :                     std::move (match_arm_pattern),
     849            1 :                     std::move (scrutinee_expr.value ()),
     850            1 :                     std::move (if_let_body.value ()),
     851            1 :                     std::move (if_expr.value ()), std::move (outer_attrs),
     852            1 :                     locus));
     853            1 :               }
     854              :           }
     855            0 :         default:
     856              :           // error - invalid token
     857            0 :           add_error (
     858            0 :             Error (t->get_locus (),
     859              :                    "unexpected token %qs after else in if let expression",
     860              :                    t->get_token_description ()));
     861              : 
     862              :           // skip somewhere?
     863              :           return tl::unexpected<Parse::Error::Node> (
     864            0 :             Parse::Error::Node::MALFORMED);
     865              :         }
     866           12 :     }
     867           62 : }
     868              : 
     869              : /* TODO: possibly decide on different method of handling label (i.e. not
     870              :  * parameter) */
     871              : 
     872              : /* Parses a "loop" infinite loop expression. Label is not parsed and should be
     873              :  * parsed via parse_labelled_loop_expr, which would call this. */
     874              : template <typename ManagedTokenSource>
     875              : tl::expected<std::unique_ptr<AST::LoopExpr>, Parse::Error::Node>
     876          115 : Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
     877              :                                              tl::optional<AST::LoopLabel> label,
     878              :                                              location_t pratt_parsed_loc)
     879              : {
     880          115 :   location_t locus = pratt_parsed_loc;
     881          115 :   if (locus == UNKNOWN_LOCATION)
     882              :     {
     883           38 :       if (label)
     884           38 :         locus = label->get_locus ();
     885              :       else
     886            0 :         locus = lexer.peek_token ()->get_locus ();
     887              : 
     888           38 :       if (!skip_token (LOOP))
     889              :         {
     890            0 :           skip_after_end_block ();
     891              :           return tl::unexpected<Parse::Error::Node> (
     892            0 :             Parse::Error::Node::MALFORMED);
     893              :         }
     894              :     }
     895              :   else
     896              :     {
     897           77 :       if (label)
     898            0 :         locus = label->get_locus ();
     899              :     }
     900              : 
     901              :   // parse loop body, which is required
     902          115 :   auto loop_body = parse_block_expr ();
     903          115 :   if (!loop_body)
     904            1 :     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR);
     905              : 
     906          114 :   return std::unique_ptr<AST::LoopExpr> (
     907          152 :     new AST::LoopExpr (std::move (loop_body.value ()), locus, std::move (label),
     908          114 :                        std::move (outer_attrs)));
     909          115 : }
     910              : 
     911              : /* Parses a "while" loop expression. Label is not parsed and should be parsed
     912              :  * via parse_labelled_loop_expr, which would call this. */
     913              : template <typename ManagedTokenSource>
     914              : tl::expected<std::unique_ptr<AST::WhileLoopExpr>, Parse::Error::Node>
     915           78 : Parser<ManagedTokenSource>::parse_while_loop_expr (
     916              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label,
     917              :   location_t pratt_parsed_loc)
     918              : {
     919           78 :   location_t locus = pratt_parsed_loc;
     920           78 :   if (locus == UNKNOWN_LOCATION)
     921              :     {
     922            2 :       if (label)
     923            2 :         locus = label->get_locus ();
     924              :       else
     925            0 :         locus = lexer.peek_token ()->get_locus ();
     926              : 
     927            2 :       if (!skip_token (WHILE))
     928              :         {
     929            0 :           skip_after_end_block ();
     930              :           return tl::unexpected<Parse::Error::Node> (
     931            0 :             Parse::Error::Node::MALFORMED);
     932              :         }
     933              :     }
     934              :   else
     935              :     {
     936           76 :       if (label)
     937            0 :         locus = label->get_locus ();
     938              :     }
     939              : 
     940              :   // ensure it isn't a while let loop
     941          156 :   if (lexer.peek_token ()->get_id () == LET)
     942              :     {
     943            0 :       Error error (lexer.peek_token ()->get_locus (),
     944              :                    "appears to be while let loop but is being parsed by "
     945              :                    "while loop - this may be a compiler issue");
     946            0 :       add_error (std::move (error));
     947              : 
     948              :       // skip somewhere?
     949            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
     950            0 :     }
     951              : 
     952              :   // parse loop predicate (required) with HACK to prevent struct expr parsing
     953           78 :   ParseRestrictions no_struct_expr;
     954           78 :   no_struct_expr.can_be_struct_expr = false;
     955           78 :   auto predicate = parse_expr ({}, no_struct_expr);
     956           78 :   if (!predicate)
     957              :     {
     958            0 :       Error error (lexer.peek_token ()->get_locus (),
     959              :                    "failed to parse predicate expression in while loop");
     960            0 :       add_error (std::move (error));
     961              : 
     962              :       // skip somewhere?
     963              :       return tl::unexpected<Parse::Error::Node> (
     964            0 :         Parse::Error::Node::CHILD_ERROR);
     965            0 :     }
     966              :   /* TODO: check that it isn't struct expression here? actually, probably in
     967              :    * semantic analysis */
     968              : 
     969              :   // parse loop body (required)
     970           78 :   auto body = parse_block_expr ();
     971           78 :   if (!body)
     972              :     {
     973            0 :       Error error (lexer.peek_token ()->get_locus (),
     974              :                    "failed to parse loop body block expression in while loop");
     975            0 :       add_error (std::move (error));
     976              : 
     977              :       // skip somewhere
     978              :       return tl::unexpected<Parse::Error::Node> (
     979            0 :         Parse::Error::Node::CHILD_ERROR);
     980            0 :     }
     981              : 
     982           78 :   return std::unique_ptr<AST::WhileLoopExpr> (
     983          158 :     new AST::WhileLoopExpr (std::move (predicate.value ()),
     984           78 :                             std::move (body.value ()), locus, std::move (label),
     985           78 :                             std::move (outer_attrs)));
     986          156 : }
     987              : 
     988              : /* Parses a "while let" loop expression. Label is not parsed and should be
     989              :  * parsed via parse_labelled_loop_expr, which would call this. */
     990              : template <typename ManagedTokenSource>
     991              : tl::expected<std::unique_ptr<AST::WhileLetLoopExpr>, Parse::Error::Node>
     992            4 : Parser<ManagedTokenSource>::parse_while_let_loop_expr (
     993              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label)
     994              : {
     995            4 :   location_t locus = UNKNOWN_LOCATION;
     996            4 :   if (label)
     997            0 :     locus = label->get_locus ();
     998              :   else
     999            8 :     locus = lexer.peek_token ()->get_locus ();
    1000            4 :   maybe_skip_token (WHILE);
    1001              : 
    1002              :   /* check for possible accidental recognition of a while loop as a while let
    1003              :    * loop */
    1004            8 :   if (lexer.peek_token ()->get_id () != LET)
    1005              :     {
    1006            0 :       Error error (lexer.peek_token ()->get_locus (),
    1007              :                    "appears to be a while loop but is being parsed by "
    1008              :                    "while let loop - this may be a compiler issue");
    1009            0 :       add_error (std::move (error));
    1010              : 
    1011              :       // skip somewhere
    1012            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1013            0 :     }
    1014              :   // as this token is definitely let now, save the computation of comparison
    1015            4 :   lexer.skip_token ();
    1016              : 
    1017              :   // parse predicate patterns
    1018            4 :   std::unique_ptr<AST::Pattern> predicate_pattern
    1019              :     = parse_match_arm_pattern (EQUAL);
    1020              :   // ensure that there is at least 1 pattern
    1021            4 :   if (predicate_pattern == nullptr)
    1022              :     {
    1023            1 :       Error error (lexer.peek_token ()->get_locus (),
    1024              :                    "should be at least 1 pattern");
    1025            1 :       add_error (std::move (error));
    1026            1 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1027            1 :     }
    1028              : 
    1029            3 :   if (!skip_token (EQUAL))
    1030              :     {
    1031              :       // skip somewhere?
    1032            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1033              :     }
    1034              : 
    1035              :   /* parse predicate expression, which is required (and HACK to prevent struct
    1036              :    * expr) */
    1037            3 :   ParseRestrictions no_struct_expr;
    1038            3 :   no_struct_expr.can_be_struct_expr = false;
    1039            3 :   auto predicate_expr = parse_expr ({}, no_struct_expr);
    1040            3 :   if (!predicate_expr)
    1041              :     {
    1042            0 :       Error error (lexer.peek_token ()->get_locus (),
    1043              :                    "failed to parse predicate expression in while let loop");
    1044            0 :       add_error (std::move (error));
    1045              : 
    1046              :       // skip somewhere?
    1047              :       return tl::unexpected<Parse::Error::Node> (
    1048            0 :         Parse::Error::Node::CHILD_ERROR);
    1049            0 :     }
    1050              :   /* TODO: ensure that struct expression is not parsed? Actually, probably in
    1051              :    * semantic analysis. */
    1052              : 
    1053              :   // parse loop body, which is required
    1054            3 :   auto body = parse_block_expr ();
    1055            3 :   if (!body)
    1056              :     {
    1057            0 :       Error error (lexer.peek_token ()->get_locus (),
    1058              :                    "failed to parse block expr (loop body) of while let loop");
    1059            0 :       add_error (std::move (error));
    1060              : 
    1061              :       // skip somewhere?
    1062              :       return tl::unexpected<Parse::Error::Node> (
    1063            0 :         Parse::Error::Node::CHILD_ERROR);
    1064            0 :     }
    1065              : 
    1066            3 :   return std::unique_ptr<AST::WhileLetLoopExpr> (
    1067            6 :     new AST::WhileLetLoopExpr (std::move (predicate_pattern),
    1068            3 :                                std::move (predicate_expr.value ()),
    1069            3 :                                std::move (body.value ()), locus,
    1070            3 :                                std::move (label), std::move (outer_attrs)));
    1071            7 : }
    1072              : 
    1073              : /* Parses a "for" iterative loop. Label is not parsed and should be parsed via
    1074              :  * parse_labelled_loop_expr, which would call this. */
    1075              : template <typename ManagedTokenSource>
    1076              : tl::expected<std::unique_ptr<AST::ForLoopExpr>, Parse::Error::Node>
    1077           19 : Parser<ManagedTokenSource>::parse_for_loop_expr (
    1078              :   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label)
    1079              : {
    1080           19 :   location_t locus = UNKNOWN_LOCATION;
    1081           19 :   if (label)
    1082            0 :     locus = label->get_locus ();
    1083              :   else
    1084           38 :     locus = lexer.peek_token ()->get_locus ();
    1085           19 :   maybe_skip_token (FOR);
    1086              : 
    1087              :   // parse pattern, which is required
    1088           19 :   std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
    1089           19 :   if (!pattern)
    1090              :     {
    1091            0 :       Error error (lexer.peek_token ()->get_locus (),
    1092              :                    "failed to parse iterator pattern in for loop");
    1093            0 :       add_error (std::move (error));
    1094              : 
    1095              :       // skip somewhere?
    1096              :       return tl::unexpected<Parse::Error::Node> (
    1097            0 :         Parse::Error::Node::CHILD_ERROR);
    1098            0 :     }
    1099              : 
    1100           19 :   if (!skip_token (IN))
    1101              :     {
    1102              :       // skip somewhere?
    1103            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1104              :     }
    1105              : 
    1106              :   /* parse iterator expression, which is required - also HACK to prevent
    1107              :    * struct expr */
    1108           19 :   ParseRestrictions no_struct_expr;
    1109           19 :   no_struct_expr.can_be_struct_expr = false;
    1110           19 :   auto expr = parse_expr ({}, no_struct_expr);
    1111           19 :   if (!expr)
    1112              :     {
    1113            0 :       Error error (lexer.peek_token ()->get_locus (),
    1114              :                    "failed to parse iterator expression in for loop");
    1115            0 :       add_error (std::move (error));
    1116              : 
    1117              :       // skip somewhere?
    1118              :       return tl::unexpected<Parse::Error::Node> (
    1119            0 :         Parse::Error::Node::CHILD_ERROR);
    1120            0 :     }
    1121              :   // TODO: check to ensure this isn't struct expr? Or in semantic analysis.
    1122              : 
    1123              :   // parse loop body, which is required
    1124           19 :   auto body = parse_block_expr ();
    1125           19 :   if (!body)
    1126              :     {
    1127            0 :       Error error (lexer.peek_token ()->get_locus (),
    1128              :                    "failed to parse loop body block expression in for loop");
    1129            0 :       add_error (std::move (error));
    1130              : 
    1131              :       // skip somewhere?
    1132              :       return tl::unexpected<Parse::Error::Node> (
    1133            0 :         Parse::Error::Node::CHILD_ERROR);
    1134            0 :     }
    1135           19 :   return std::unique_ptr<AST::ForLoopExpr> (
    1136           38 :     new AST::ForLoopExpr (std::move (pattern), std::move (expr.value ()),
    1137           19 :                           std::move (body.value ()), locus, std::move (label),
    1138           19 :                           std::move (outer_attrs)));
    1139           38 : }
    1140              : 
    1141              : // Parses a loop expression with label (any kind of loop - disambiguates).
    1142              : template <typename ManagedTokenSource>
    1143              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Node>
    1144           40 : Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
    1145              :                                                       AST::AttrVec outer_attrs)
    1146              : {
    1147              :   // parse loop label (required)
    1148           80 :   auto parsed_label = parse_loop_label (tok);
    1149           40 :   if (!parsed_label)
    1150              :     {
    1151              :       /* TODO: decide whether it should not work if there is no label, or parse
    1152              :        * it with no label at the moment, I will make it not work with no label
    1153              :        * because that's the implication. */
    1154              : 
    1155            0 :       if (parsed_label.error ().kind
    1156              :           == Parse::Error::LoopLabel::Kind::NOT_LOOP_LABEL)
    1157              :         {
    1158            0 :           Error error (tok->get_locus (),
    1159              :                        "expected lifetime in labelled loop expr (to parse loop "
    1160              :                        "label) - found %qs",
    1161              :                        tok->get_token_description ());
    1162            0 :           add_error (std::move (error));
    1163              :           return tl::unexpected<Parse::Error::Node> (
    1164            0 :             Parse::Error::Node::CHILD_ERROR);
    1165            0 :         }
    1166              : 
    1167              :       else
    1168              :         {
    1169            0 :           Error error (lexer.peek_token ()->get_locus (),
    1170              :                        "failed to parse loop label in labelled loop expr");
    1171            0 :           add_error (std::move (error));
    1172              : 
    1173              :           // skip?
    1174              :           return tl::unexpected<Parse::Error::Node> (
    1175            0 :             Parse::Error::Node::CHILD_ERROR);
    1176            0 :         }
    1177              :     }
    1178              : 
    1179           40 :   auto label = parsed_label
    1180              :                  ? tl::optional<AST::LoopLabel> (parsed_label.value ())
    1181              :                  : tl::nullopt;
    1182              : 
    1183              :   // branch on next token
    1184           40 :   const_TokenPtr t = lexer.peek_token ();
    1185           40 :   switch (t->get_id ())
    1186              :     {
    1187           38 :     case LOOP:
    1188          114 :       return parse_loop_expr (std::move (outer_attrs), std::move (label));
    1189            0 :     case FOR:
    1190            0 :       return parse_for_loop_expr (std::move (outer_attrs), std::move (label));
    1191            2 :     case WHILE:
    1192              :       // further disambiguate into while vs while let
    1193            4 :       if (lexer.peek_token (1)->get_id () == LET)
    1194            0 :         return parse_while_let_loop_expr (std::move (outer_attrs),
    1195            0 :                                           std::move (label));
    1196              :       else
    1197            6 :         return parse_while_loop_expr (std::move (outer_attrs),
    1198            2 :                                       std::move (label));
    1199            0 :     case LEFT_CURLY:
    1200            0 :       return parse_block_expr (std::move (outer_attrs), std::move (label));
    1201            0 :     default:
    1202              :       // error
    1203            0 :       add_error (Error (t->get_locus (),
    1204              :                         "unexpected token %qs when parsing labelled loop",
    1205              :                         t->get_token_description ()));
    1206              : 
    1207              :       // skip?
    1208              :       return tl::unexpected<Parse::Error::Node> (
    1209            0 :         Parse::Error::Node::CHILD_ERROR);
    1210              :     }
    1211           80 : }
    1212              : 
    1213              : // Parses a match expression.
    1214              : template <typename ManagedTokenSource>
    1215              : tl::expected<std::unique_ptr<AST::MatchExpr>, Parse::Error::Node>
    1216          906 : Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
    1217              :                                               location_t pratt_parsed_loc)
    1218              : {
    1219          906 :   location_t locus = pratt_parsed_loc;
    1220          906 :   if (locus == UNKNOWN_LOCATION)
    1221              :     {
    1222            0 :       locus = lexer.peek_token ()->get_locus ();
    1223            0 :       skip_token (MATCH_KW);
    1224              :     }
    1225              : 
    1226              :   /* parse scrutinee expression, which is required (and HACK to prevent struct
    1227              :    * expr) */
    1228          906 :   ParseRestrictions no_struct_expr;
    1229          906 :   no_struct_expr.can_be_struct_expr = false;
    1230          906 :   auto scrutinee = parse_expr ({}, no_struct_expr);
    1231          906 :   if (!scrutinee)
    1232              :     {
    1233            1 :       Error error (lexer.peek_token ()->get_locus (),
    1234              :                    "failed to parse scrutinee expression in match expression");
    1235            1 :       add_error (std::move (error));
    1236              : 
    1237              :       // skip somewhere?
    1238              :       return tl::unexpected<Parse::Error::Node> (
    1239            1 :         Parse::Error::Node::CHILD_ERROR);
    1240            1 :     }
    1241              :   /* TODO: check for scrutinee expr not being struct expr? or do so in
    1242              :    * semantic analysis */
    1243              : 
    1244          905 :   if (!skip_token (LEFT_CURLY))
    1245              :     {
    1246              :       // skip somewhere?
    1247            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1248              :     }
    1249              : 
    1250              :   // parse inner attributes (if they exist)
    1251          905 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    1252              : 
    1253              :   // parse match arms (if they exist)
    1254              :   // std::vector<std::unique_ptr<AST::MatchCase> > match_arms;
    1255          905 :   std::vector<AST::MatchCase> match_arms;
    1256              : 
    1257              :   // parse match cases
    1258         8182 :   while (lexer.peek_token ()->get_id () != RIGHT_CURLY)
    1259              :     {
    1260              :       // parse match arm itself, which is required
    1261         2136 :       AST::MatchArm arm = parse_match_arm ();
    1262         2136 :       if (arm.is_error ())
    1263              :         {
    1264              :           // TODO is this worth throwing everything away?
    1265            0 :           Error error (lexer.peek_token ()->get_locus (),
    1266              :                        "failed to parse match arm in match arms");
    1267            0 :           add_error (std::move (error));
    1268              : 
    1269              :           return tl::unexpected<Parse::Error::Node> (
    1270            0 :             Parse::Error::Node::CHILD_ERROR);
    1271            0 :         }
    1272              : 
    1273         2136 :       if (!skip_token (MATCH_ARROW))
    1274              :         {
    1275              :           // skip after somewhere?
    1276              :           // TODO is returning here a good idea? or is break better?
    1277              :           return tl::unexpected<Parse::Error::Node> (
    1278            0 :             Parse::Error::Node::MALFORMED);
    1279              :         }
    1280              : 
    1281         2136 :       ParseRestrictions restrictions;
    1282         2136 :       restrictions.expr_can_be_stmt = true;
    1283              : 
    1284         2136 :       auto expr = parse_expr ({}, restrictions);
    1285              : 
    1286         2136 :       if (!expr)
    1287              :         {
    1288              :           /* We don't need to throw an error as it already reported by
    1289              :            * parse_expr
    1290              :            */
    1291              :           return tl::unexpected<Parse::Error::Node> (
    1292            1 :             Parse::Error::Node::CHILD_ERROR);
    1293              :         }
    1294              : 
    1295         2135 :       bool is_expr_without_block = expr.value ()->is_expr_without_block ();
    1296              : 
    1297         2135 :       match_arms.push_back (
    1298         4270 :         AST::MatchCase (std::move (arm), std::move (expr.value ())));
    1299              : 
    1300              :       // handle comma presence
    1301         4270 :       if (lexer.peek_token ()->get_id () != COMMA)
    1302              :         {
    1303          559 :           if (!is_expr_without_block)
    1304              :             {
    1305              :               // allowed even if not final case
    1306              :               continue;
    1307              :             }
    1308           17 :           else if (is_expr_without_block
    1309           34 :                    && lexer.peek_token ()->get_id () != RIGHT_CURLY)
    1310              :             {
    1311              :               // not allowed if not final case
    1312            1 :               Error error (lexer.peek_token ()->get_locus (),
    1313              :                            "exprwithoutblock requires comma after match case "
    1314              :                            "expression in match arm (if not final case)");
    1315            1 :               add_error (std::move (error));
    1316              : 
    1317              :               return tl::unexpected<Parse::Error::Node> (
    1318            1 :                 Parse::Error::Node::MALFORMED);
    1319            1 :             }
    1320              :           else
    1321              :             {
    1322              :               // otherwise, must be final case, so fine
    1323              :               break;
    1324              :             }
    1325              :         }
    1326         1576 :       lexer.skip_token ();
    1327              :     }
    1328              : 
    1329          903 :   if (!skip_token (RIGHT_CURLY))
    1330              :     {
    1331              :       // skip somewhere?
    1332            0 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1333              :     }
    1334              : 
    1335          903 :   match_arms.shrink_to_fit ();
    1336              : 
    1337          903 :   return std::unique_ptr<AST::MatchExpr> (
    1338          903 :     new AST::MatchExpr (std::move (scrutinee.value ()), std::move (match_arms),
    1339              :                         std::move (inner_attrs), std::move (outer_attrs),
    1340          903 :                         locus));
    1341          905 : }
    1342              : 
    1343              : // Parses an async block expression.
    1344              : template <typename ManagedTokenSource>
    1345              : tl::expected<std::unique_ptr<AST::AsyncBlockExpr>, Parse::Error::Node>
    1346            0 : Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs)
    1347              : {
    1348            0 :   location_t locus = lexer.peek_token ()->get_locus ();
    1349            0 :   skip_token (ASYNC);
    1350              : 
    1351              :   // detect optional move token
    1352            0 :   bool has_move = false;
    1353            0 :   if (lexer.peek_token ()->get_id () == MOVE)
    1354              :     {
    1355            0 :       lexer.skip_token ();
    1356            0 :       has_move = true;
    1357              :     }
    1358              : 
    1359              :   // parse block expression (required)
    1360            0 :   auto block_expr = parse_block_expr ();
    1361            0 :   if (!block_expr)
    1362              :     {
    1363            0 :       Error error (
    1364            0 :         lexer.peek_token ()->get_locus (),
    1365              :         "failed to parse block expression of async block expression");
    1366            0 :       add_error (std::move (error));
    1367              : 
    1368              :       // skip somewhere?
    1369              :       return tl::unexpected<Parse::Error::Node> (
    1370            0 :         Parse::Error::Node::CHILD_ERROR);
    1371            0 :     }
    1372              : 
    1373            0 :   return std::unique_ptr<AST::AsyncBlockExpr> (
    1374            0 :     new AST::AsyncBlockExpr (std::move (block_expr.value ()), has_move,
    1375            0 :                              std::move (outer_attrs), locus));
    1376            0 : }
    1377              : 
    1378              : // Parses an unsafe block expression.
    1379              : template <typename ManagedTokenSource>
    1380              : tl::expected<std::unique_ptr<AST::UnsafeBlockExpr>, Parse::Error::Node>
    1381         3544 : Parser<ManagedTokenSource>::parse_unsafe_block_expr (
    1382              :   AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
    1383              : {
    1384         3544 :   location_t locus = pratt_parsed_loc;
    1385         3544 :   if (locus == UNKNOWN_LOCATION)
    1386              :     {
    1387            0 :       locus = lexer.peek_token ()->get_locus ();
    1388            0 :       skip_token (UNSAFE);
    1389              :     }
    1390              : 
    1391              :   // parse block expression (required)
    1392         3544 :   auto block_expr = parse_block_expr ();
    1393         3544 :   if (!block_expr)
    1394              :     {
    1395            0 :       Error error (
    1396            0 :         lexer.peek_token ()->get_locus (),
    1397              :         "failed to parse block expression of unsafe block expression");
    1398            0 :       add_error (std::move (error));
    1399              : 
    1400              :       // skip somewhere?
    1401              :       return tl::unexpected<Parse::Error::Node> (
    1402            0 :         Parse::Error::Node::CHILD_ERROR);
    1403            0 :     }
    1404         3544 :   return std::unique_ptr<AST::UnsafeBlockExpr> (
    1405         3544 :     new AST::UnsafeBlockExpr (std::move (block_expr.value ()),
    1406         3544 :                               std::move (outer_attrs), locus));
    1407         3544 : }
    1408              : 
    1409              : // Parses an array definition expression.
    1410              : template <typename ManagedTokenSource>
    1411              : tl::expected<std::unique_ptr<AST::ArrayExpr>, Parse::Error::Node>
    1412          402 : Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
    1413              :                                               location_t pratt_parsed_loc)
    1414              : {
    1415          402 :   location_t locus = pratt_parsed_loc;
    1416          402 :   if (locus == UNKNOWN_LOCATION)
    1417              :     {
    1418            0 :       locus = lexer.peek_token ()->get_locus ();
    1419            0 :       skip_token (LEFT_SQUARE);
    1420              :     }
    1421              : 
    1422              :   // parse optional inner attributes
    1423          402 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    1424              : 
    1425              :   // parse the "array elements" section, which is optional
    1426          804 :   if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
    1427              :     {
    1428              :       // no array elements
    1429            1 :       lexer.skip_token ();
    1430              : 
    1431            1 :       std::vector<std::unique_ptr<AST::Expr>> exprs;
    1432            1 :       auto array_elems
    1433              :         = std::make_unique<AST::ArrayElemsValues> (std::move (exprs), locus);
    1434            1 :       return std::make_unique<AST::ArrayExpr> (std::move (array_elems),
    1435              :                                                std::move (inner_attrs),
    1436            1 :                                                std::move (outer_attrs), locus);
    1437            1 :     }
    1438              :   else
    1439              :     {
    1440              :       // should have array elements
    1441              :       // parse initial expression, which is required for either
    1442          401 :       auto initial_expr = parse_expr ();
    1443          401 :       if (!initial_expr)
    1444              :         {
    1445            0 :           Error error (lexer.peek_token ()->get_locus (),
    1446              :                        "could not parse expression in array expression "
    1447              :                        "(even though arrayelems seems to be present)");
    1448            0 :           add_error (std::move (error));
    1449              : 
    1450              :           // skip somewhere?
    1451              :           return tl::unexpected<Parse::Error::Node> (
    1452            0 :             Parse::Error::Node::CHILD_ERROR);
    1453            0 :         }
    1454              : 
    1455          802 :       if (lexer.peek_token ()->get_id () == SEMICOLON)
    1456              :         {
    1457              :           // copy array elems
    1458          123 :           lexer.skip_token ();
    1459              : 
    1460              :           // parse copy amount expression (required)
    1461          123 :           auto copy_amount = parse_expr ();
    1462          123 :           if (!copy_amount)
    1463              :             {
    1464            0 :               Error error (lexer.peek_token ()->get_locus (),
    1465              :                            "could not parse copy amount expression in array "
    1466              :                            "expression (arrayelems)");
    1467            0 :               add_error (std::move (error));
    1468              : 
    1469              :               // skip somewhere?
    1470              :               return tl::unexpected<Parse::Error::Node> (
    1471            0 :                 Parse::Error::Node::CHILD_ERROR);
    1472            0 :             }
    1473              : 
    1474          123 :           skip_token (RIGHT_SQUARE);
    1475              : 
    1476          123 :           std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
    1477          246 :             new AST::ArrayElemsCopied (std::move (initial_expr.value ()),
    1478          123 :                                        std::move (copy_amount.value ()),
    1479              :                                        locus));
    1480          123 :           return std::unique_ptr<AST::ArrayExpr> (
    1481          123 :             new AST::ArrayExpr (std::move (copied_array_elems),
    1482              :                                 std::move (inner_attrs),
    1483          123 :                                 std::move (outer_attrs), locus));
    1484          246 :         }
    1485          556 :       else if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
    1486              :         {
    1487              :           // single-element array expression
    1488           34 :           std::vector<std::unique_ptr<AST::Expr>> exprs;
    1489           34 :           exprs.reserve (1);
    1490           34 :           exprs.push_back (std::move (initial_expr.value ()));
    1491           34 :           exprs.shrink_to_fit ();
    1492              : 
    1493           34 :           skip_token (RIGHT_SQUARE);
    1494              : 
    1495           34 :           std::unique_ptr<AST::ArrayElemsValues> array_elems (
    1496           34 :             new AST::ArrayElemsValues (std::move (exprs), locus));
    1497           34 :           return std::unique_ptr<AST::ArrayExpr> (
    1498           34 :             new AST::ArrayExpr (std::move (array_elems),
    1499              :                                 std::move (inner_attrs),
    1500           34 :                                 std::move (outer_attrs), locus));
    1501           34 :         }
    1502          488 :       else if (lexer.peek_token ()->get_id () == COMMA)
    1503              :         {
    1504              :           // multi-element array expression (or trailing comma)
    1505          244 :           std::vector<std::unique_ptr<AST::Expr>> exprs;
    1506          244 :           exprs.push_back (std::move (initial_expr.value ()));
    1507              : 
    1508          244 :           const_TokenPtr t = lexer.peek_token ();
    1509         1090 :           while (t->get_id () == COMMA)
    1510              :             {
    1511          853 :               lexer.skip_token ();
    1512              : 
    1513              :               // quick break if right square bracket
    1514         1706 :               if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
    1515              :                 break;
    1516              : 
    1517              :               // parse expression (required)
    1518          846 :               auto expr = parse_expr ();
    1519          846 :               if (!expr)
    1520              :                 {
    1521            0 :                   Error error (lexer.peek_token ()->get_locus (),
    1522              :                                "failed to parse element in array expression");
    1523            0 :                   add_error (std::move (error));
    1524              : 
    1525              :                   // skip somewhere?
    1526              :                   return tl::unexpected<Parse::Error::Node> (
    1527            0 :                     Parse::Error::Node::CHILD_ERROR);
    1528            0 :                 }
    1529          846 :               exprs.push_back (std::move (expr.value ()));
    1530              : 
    1531          846 :               t = lexer.peek_token ();
    1532              :             }
    1533              : 
    1534          244 :           skip_token (RIGHT_SQUARE);
    1535              : 
    1536          244 :           exprs.shrink_to_fit ();
    1537              : 
    1538          244 :           std::unique_ptr<AST::ArrayElemsValues> array_elems (
    1539          244 :             new AST::ArrayElemsValues (std::move (exprs), locus));
    1540          244 :           return std::unique_ptr<AST::ArrayExpr> (
    1541          244 :             new AST::ArrayExpr (std::move (array_elems),
    1542              :                                 std::move (inner_attrs),
    1543          244 :                                 std::move (outer_attrs), locus));
    1544          488 :         }
    1545              :       else
    1546              :         {
    1547              :           // error
    1548            0 :           Error error (lexer.peek_token ()->get_locus (),
    1549              :                        "unexpected token %qs in array expression (arrayelems)",
    1550            0 :                        lexer.peek_token ()->get_token_description ());
    1551            0 :           add_error (std::move (error));
    1552              : 
    1553              :           // skip somewhere?
    1554              :           return tl::unexpected<Parse::Error::Node> (
    1555            0 :             Parse::Error::Node::MALFORMED);
    1556            0 :         }
    1557          401 :     }
    1558          402 : }
    1559              : 
    1560              : // Parses a grouped or tuple expression (disambiguates).
    1561              : template <typename ManagedTokenSource>
    1562              : tl::expected<std::unique_ptr<AST::ExprWithoutBlock>, Parse::Error::Node>
    1563          849 : Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
    1564              :   AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
    1565              : {
    1566              :   // adjustment to allow Pratt parsing to reuse function without copy-paste
    1567          849 :   location_t locus = pratt_parsed_loc;
    1568          849 :   if (locus == UNKNOWN_LOCATION)
    1569              :     {
    1570            0 :       locus = lexer.peek_token ()->get_locus ();
    1571            0 :       skip_token (LEFT_PAREN);
    1572              :     }
    1573              : 
    1574              :   // parse optional inner attributes
    1575          849 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    1576              : 
    1577         1698 :   if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    1578              :     {
    1579              :       // must be empty tuple
    1580          149 :       lexer.skip_token ();
    1581              : 
    1582              :       // create tuple with empty tuple elems
    1583          149 :       return std::unique_ptr<AST::TupleExpr> (
    1584          149 :         new AST::TupleExpr (std::vector<std::unique_ptr<AST::Expr>> (),
    1585              :                             std::move (inner_attrs), std::move (outer_attrs),
    1586          149 :                             locus));
    1587              :     }
    1588              : 
    1589              :   // parse first expression (required)
    1590          700 :   auto first_expr = parse_expr ();
    1591          700 :   if (!first_expr)
    1592              :     {
    1593            0 :       Error error (lexer.peek_token ()->get_locus (),
    1594              :                    "failed to parse expression in grouped or tuple expression");
    1595            0 :       add_error (std::move (error));
    1596              : 
    1597              :       // skip after somewhere?
    1598              :       return tl::unexpected<Parse::Error::Node> (
    1599            0 :         Parse::Error::Node::CHILD_ERROR);
    1600            0 :     }
    1601              : 
    1602              :   // detect whether grouped expression with right parentheses as next token
    1603         1400 :   if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    1604              :     {
    1605              :       // must be grouped expr
    1606          313 :       lexer.skip_token ();
    1607              : 
    1608              :       // create grouped expr
    1609          313 :       return std::unique_ptr<AST::GroupedExpr> (
    1610          313 :         new AST::GroupedExpr (std::move (first_expr.value ()),
    1611              :                               std::move (inner_attrs), std::move (outer_attrs),
    1612          313 :                               locus));
    1613              :     }
    1614          774 :   else if (lexer.peek_token ()->get_id () == COMMA)
    1615              :     {
    1616              :       // tuple expr
    1617          386 :       std::vector<std::unique_ptr<AST::Expr>> exprs;
    1618          386 :       exprs.push_back (std::move (first_expr.value ()));
    1619              : 
    1620              :       // parse potential other tuple exprs
    1621          386 :       const_TokenPtr t = lexer.peek_token ();
    1622          915 :       while (t->get_id () == COMMA)
    1623              :         {
    1624          558 :           lexer.skip_token ();
    1625              : 
    1626              :           // break out if right paren
    1627         1116 :           if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
    1628              :             break;
    1629              : 
    1630              :           // parse expr, which is now required
    1631          529 :           auto expr = parse_expr ();
    1632          529 :           if (!expr)
    1633              :             {
    1634            0 :               Error error (lexer.peek_token ()->get_locus (),
    1635              :                            "failed to parse expr in tuple expr");
    1636            0 :               add_error (std::move (error));
    1637              : 
    1638              :               // skip somewhere?
    1639              :               return tl::unexpected<Parse::Error::Node> (
    1640            0 :                 Parse::Error::Node::CHILD_ERROR);
    1641            0 :             }
    1642          529 :           exprs.push_back (std::move (expr.value ()));
    1643              : 
    1644          529 :           t = lexer.peek_token ();
    1645              :         }
    1646              : 
    1647              :       // skip right paren
    1648          386 :       skip_token (RIGHT_PAREN);
    1649              : 
    1650          386 :       return std::unique_ptr<AST::TupleExpr> (
    1651          386 :         new AST::TupleExpr (std::move (exprs), std::move (inner_attrs),
    1652          386 :                             std::move (outer_attrs), locus));
    1653          386 :     }
    1654              :   else
    1655              :     {
    1656              :       // error
    1657            1 :       const_TokenPtr t = lexer.peek_token ();
    1658            1 :       Error error (t->get_locus (),
    1659              :                    "unexpected token %qs in grouped or tuple expression "
    1660              :                    "(parenthesised expression) - expected %<)%> for grouped "
    1661              :                    "expr and %<,%> for tuple expr",
    1662              :                    t->get_token_description ());
    1663            1 :       add_error (std::move (error));
    1664              : 
    1665              :       // skip somewhere?
    1666            1 :       return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);
    1667            2 :     }
    1668          849 : }
    1669              : 
    1670              : // Parses a struct expression field.
    1671              : template <typename ManagedTokenSource>
    1672              : tl::expected<std::unique_ptr<AST::StructExprField>,
    1673              :              Parse::Error::StructExprField>
    1674         2241 : Parser<ManagedTokenSource>::parse_struct_expr_field ()
    1675              : {
    1676         2241 :   AST::AttrVec outer_attrs = parse_outer_attributes ();
    1677         2241 :   const_TokenPtr t = lexer.peek_token ();
    1678         2241 :   switch (t->get_id ())
    1679              :     {
    1680         2197 :     case IDENTIFIER:
    1681         4394 :       if (lexer.peek_token (1)->get_id () == COLON)
    1682              :         {
    1683              :           // struct expr field with identifier and expr
    1684         1981 :           Identifier ident = {t};
    1685         1981 :           lexer.skip_token (1);
    1686              : 
    1687              :           // parse expression (required)
    1688         1981 :           auto expr = parse_expr ();
    1689         1981 :           if (!expr)
    1690              :             {
    1691            0 :               Error error (t->get_locus (),
    1692              :                            "failed to parse struct expression field with "
    1693              :                            "identifier and expression");
    1694            0 :               add_error (std::move (error));
    1695              : 
    1696              :               return tl::unexpected<Parse::Error::StructExprField> (
    1697            0 :                 Parse::Error::StructExprField::CHILD_ERROR);
    1698            0 :             }
    1699              : 
    1700         1981 :           return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
    1701         3962 :             new AST::StructExprFieldIdentifierValue (std::move (ident),
    1702         1981 :                                                      std::move (expr.value ()),
    1703              :                                                      std::move (outer_attrs),
    1704         1981 :                                                      t->get_locus ()));
    1705         1981 :         }
    1706              :       else
    1707              :         {
    1708              :           // struct expr field with identifier only
    1709          216 :           Identifier ident{t};
    1710          216 :           lexer.skip_token ();
    1711              : 
    1712          216 :           return std::unique_ptr<AST::StructExprFieldIdentifier> (
    1713          432 :             new AST::StructExprFieldIdentifier (std::move (ident),
    1714              :                                                 std::move (outer_attrs),
    1715          216 :                                                 t->get_locus ()));
    1716          216 :         }
    1717           44 :     case INT_LITERAL:
    1718              :       {
    1719              :         // parse tuple index field
    1720           44 :         int index = atoi (t->get_str ().c_str ());
    1721           44 :         lexer.skip_token ();
    1722              : 
    1723           44 :         if (!skip_token (COLON))
    1724              :           {
    1725              :             // skip somewhere?
    1726              :             return tl::unexpected<Parse::Error::StructExprField> (
    1727            0 :               Parse::Error::StructExprField::MALFORMED);
    1728              :           }
    1729              : 
    1730              :         // parse field expression (required)
    1731           44 :         auto expr = parse_expr ();
    1732           44 :         if (!expr)
    1733              :           {
    1734            0 :             Error error (t->get_locus (),
    1735              :                          "failed to parse expr in struct (or enum) expr "
    1736              :                          "field with tuple index");
    1737            0 :             add_error (std::move (error));
    1738              : 
    1739              :             return tl::unexpected<Parse::Error::StructExprField> (
    1740            0 :               Parse::Error::StructExprField::CHILD_ERROR);
    1741            0 :           }
    1742              : 
    1743           44 :         return std::unique_ptr<AST::StructExprFieldIndexValue> (
    1744           44 :           new AST::StructExprFieldIndexValue (index, std::move (expr.value ()),
    1745              :                                               std::move (outer_attrs),
    1746           44 :                                               t->get_locus ()));
    1747           44 :       }
    1748            0 :     case DOT_DOT:
    1749              :       /* this is a struct base and can't be parsed here, so just return
    1750              :        * nothing without erroring */
    1751              : 
    1752              :       return tl::unexpected<Parse::Error::StructExprField> (
    1753            0 :         Parse::Error::StructExprField::STRUCT_BASE);
    1754            0 :     default:
    1755            0 :       add_error (
    1756            0 :         Error (t->get_locus (),
    1757              :                "unrecognised token %qs as first token of struct expr field - "
    1758              :                "expected identifier or integer literal",
    1759              :                t->get_token_description ()));
    1760              : 
    1761              :       return tl::unexpected<Parse::Error::StructExprField> (
    1762            0 :         Parse::Error::StructExprField::MALFORMED);
    1763              :     }
    1764         2241 : }
    1765              : 
    1766              : /* Pratt parser impl of parse_expr. FIXME: this is only provisional and
    1767              :  * probably will be changed. */
    1768              : template <typename ManagedTokenSource>
    1769              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1770        69318 : Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
    1771              :                                         AST::AttrVec outer_attrs,
    1772              :                                         ParseRestrictions restrictions)
    1773              : {
    1774        69318 :   const_TokenPtr current_token = lexer.peek_token ();
    1775              :   // Special hack because we are allowed to return nullptr, in that case we
    1776              :   // don't want to skip the token, since we don't actually parse it. But if
    1777              :   // null isn't allowed it indicates an error, and we want to skip past that.
    1778              :   // So return early if it is one of the tokens that ends an expression
    1779              :   // (or at least cannot start a new expression).
    1780        69318 :   if (restrictions.expr_can_be_null)
    1781              :     {
    1782         1363 :       TokenId id = current_token->get_id ();
    1783         1363 :       if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY
    1784              :           || id == RIGHT_SQUARE || id == COMMA || id == LEFT_CURLY)
    1785              :         return tl::unexpected<Parse::Error::Expr> (
    1786          100 :           Parse::Error::Expr::NULL_EXPR);
    1787              :     }
    1788              : 
    1789        69218 :   ParseRestrictions null_denotation_restrictions = restrictions;
    1790        69218 :   null_denotation_restrictions.expr_can_be_stmt = false;
    1791              : 
    1792              :   // parse null denotation (unary part of expression)
    1793        69218 :   tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
    1794        69218 :     = null_denotation ({}, null_denotation_restrictions);
    1795        69218 :   if (!expr)
    1796           64 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    1797        69154 :   if (expr.value () == nullptr)
    1798            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    1799              :   
    1800       138308 :   return left_denotations (std::move (expr), right_binding_power,
    1801        69154 :                            std::move (outer_attrs), restrictions);
    1802        69218 : }
    1803              : 
    1804              : // Parse expression with lowest left binding power.
    1805              : template <typename ManagedTokenSource>
    1806              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1807        52644 : Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
    1808              :                                         ParseRestrictions restrictions)
    1809              : {
    1810        52644 :   return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
    1811              : }
    1812              : 
    1813              : template <typename ManagedTokenSource>
    1814              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1815        83560 : Parser<ManagedTokenSource>::left_denotations (
    1816              :   tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr,
    1817              :   int right_binding_power, AST::AttrVec outer_attrs,
    1818              :   ParseRestrictions restrictions)
    1819              : {
    1820        83560 :   if (!expr)
    1821              :     {
    1822              :       // DEBUG
    1823            0 :       rust_debug ("null denotation is null; returning null for parse_expr");
    1824              :       return tl::unexpected<Parse::Error::Expr> (
    1825            0 :         Parse::Error::Expr::NULL_DENOTATION);
    1826              :     }
    1827              : 
    1828        83560 :   const_TokenPtr current_token = lexer.peek_token ();
    1829              : 
    1830        27323 :   if (restrictions.expr_can_be_stmt && !expr.value ()->is_expr_without_block ()
    1831         6827 :       && current_token->get_id () != DOT
    1832        90382 :       && current_token->get_id () != QUESTION_MARK)
    1833              :     {
    1834         6822 :       rust_debug ("statement expression with block");
    1835         6822 :       expr.value ()->set_outer_attrs (std::move (outer_attrs));
    1836        83560 :       return expr;
    1837              :     }
    1838              : 
    1839       101918 :   restrictions.expr_can_be_stmt = false;
    1840              : 
    1841              :   // stop parsing if find lower priority token - parse higher priority first
    1842       305754 :   while (right_binding_power < left_binding_power (current_token))
    1843              :     {
    1844        25182 :       lexer.skip_token ();
    1845              : 
    1846              :       // FIXME attributes should generally be applied to the null denotation.
    1847       100726 :       expr = left_denotation (current_token, std::move (expr.value ()),
    1848              :                               std::move (outer_attrs), restrictions);
    1849              : 
    1850        25182 :       if (!expr)
    1851              :         {
    1852              :           // DEBUG
    1853            2 :           rust_debug ("left denotation is null; returning null for parse_expr");
    1854              : 
    1855              :           return tl::unexpected<Parse::Error::Expr> (
    1856            2 :             Parse::Error::Expr::LEFT_DENOTATION);
    1857              :         }
    1858              : 
    1859        25180 :       current_token = lexer.peek_token ();
    1860              :     }
    1861              : 
    1862        83560 :   return expr;
    1863        83560 : }
    1864              : 
    1865              : /* Determines action to take when finding token at beginning of expression. */
    1866              : template <typename ManagedTokenSource>
    1867              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1868        69218 : Parser<ManagedTokenSource>::null_denotation (AST::AttrVec outer_attrs,
    1869              :                                              ParseRestrictions restrictions)
    1870              : {
    1871              :   /* note: tok is previous character in input stream, not current one, as
    1872              :    * parse_expr skips it before passing it in */
    1873              : 
    1874              :   /* as a Pratt parser (which works by decomposing expressions into a null
    1875              :    * denotation and then a left denotation), null denotations handle primaries
    1876              :    * and unary operands (but only prefix unary operands) */
    1877              : 
    1878        69218 :   auto tok = lexer.peek_token ();
    1879              : 
    1880        69218 :   switch (tok->get_id ())
    1881              :     {
    1882        33319 :     case IDENTIFIER:
    1883              :     case SELF:
    1884              :     case SELF_ALIAS:
    1885              :     case DOLLAR_SIGN:
    1886              :     case CRATE:
    1887              :     case SUPER:
    1888              :     case SCOPE_RESOLUTION:
    1889              :       {
    1890              :         // DEBUG
    1891        33319 :         rust_debug ("beginning null denotation identifier handling");
    1892              : 
    1893              :         /* best option: parse as path, then extract identifier, macro,
    1894              :          * struct/enum, or just path info from it */
    1895        33319 :         AST::PathInExpression path = parse_path_in_expression ();
    1896              : 
    1897        66638 :         return null_denotation_path (std::move (path), std::move (outer_attrs),
    1898        33319 :                                      restrictions);
    1899        33319 :       }
    1900            2 :     case HASH:
    1901              :       {
    1902              :         // Parse outer attributes and then the expression that follows
    1903            2 :         AST::AttrVec attrs = parse_outer_attributes ();
    1904              : 
    1905              :         // Merge with any existing outer attributes
    1906            2 :         if (!outer_attrs.empty ())
    1907            0 :           attrs.insert (attrs.begin (), outer_attrs.begin (),
    1908              :                         outer_attrs.end ());
    1909              : 
    1910              :         // Try to parse the expression that should follow the attributes
    1911            2 :         auto expr = parse_expr (std::move (attrs), restrictions);
    1912            2 :         if (!expr)
    1913              :           {
    1914              :             /* If parsing failed and we're at a semicolon, provide a better
    1915              :              * error
    1916              :              */
    1917            2 :             const_TokenPtr next_tok = lexer.peek_token ();
    1918            2 :             if (next_tok->get_id () == SEMICOLON)
    1919            0 :               add_error (Error (next_tok->get_locus (),
    1920              :                                 "expected expression, found %<;%>"));
    1921              :             return tl::unexpected<Parse::Error::Expr> (
    1922            2 :               Parse::Error::Expr::CHILD_ERROR);
    1923            2 :           }
    1924            0 :         return expr;
    1925            2 :       }
    1926        35897 :     default:
    1927        35897 :       if (tok->get_id () == LEFT_SHIFT)
    1928              :         {
    1929            2 :           lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE);
    1930            2 :           tok = lexer.peek_token ();
    1931              :         }
    1932              : 
    1933        35897 :       lexer.skip_token ();
    1934        71794 :       return null_denotation_not_path (std::move (tok), std::move (outer_attrs),
    1935        35897 :                                        restrictions);
    1936              :     }
    1937        69218 : }
    1938              : 
    1939              : // Handling of expresions that start with a path for `null_denotation`.
    1940              : template <typename ManagedTokenSource>
    1941              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    1942        47539 : Parser<ManagedTokenSource>::null_denotation_path (
    1943              :   AST::PathInExpression path, AST::AttrVec outer_attrs,
    1944              :   ParseRestrictions restrictions)
    1945              : {
    1946        47539 :   rust_debug ("parsing null denotation after path");
    1947              : 
    1948              :   // HACK: always make "self" by itself a path (regardless of next
    1949              :   // tokens)
    1950        47539 :   if (path.is_single_segment () && path.get_segments ()[0].is_lower_self_seg ())
    1951              :     {
    1952              :       // HACK: add outer attrs to path
    1953         6889 :       path.set_outer_attrs (std::move (outer_attrs));
    1954        13778 :       return std::make_unique<AST::PathInExpression> (std::move (path));
    1955              :     }
    1956              : 
    1957              :   // branch on next token
    1958        40650 :   const_TokenPtr t = lexer.peek_token ();
    1959        40650 :   switch (t->get_id ())
    1960              :     {
    1961         1803 :     case EXCLAM:
    1962              :       {
    1963              :         // macro
    1964         1803 :         auto macro = parse_macro_invocation_partial (std::move (path),
    1965              :                                                      std::move (outer_attrs));
    1966         1803 :         if (macro == nullptr)
    1967              :           return tl::unexpected<Parse::Error::Expr> (
    1968            2 :             Parse::Error::Expr::CHILD_ERROR);
    1969         1801 :         return std::unique_ptr<AST::Expr> (std::move (macro));
    1970         1803 :       }
    1971         2986 :     case LEFT_CURLY:
    1972              :       {
    1973         2986 :         bool not_a_block = lexer.peek_token (1)->get_id () == IDENTIFIER
    1974         8252 :                            && (lexer.peek_token (2)->get_id () == COMMA
    1975         7512 :                                || (lexer.peek_token (2)->get_id () == COLON
    1976         4778 :                                    && (lexer.peek_token (4)->get_id () == COMMA
    1977          632 :                                        || !Parse::Utils::can_tok_start_type (
    1978         1414 :                                          lexer.peek_token (3)->get_id ()))));
    1979              : 
    1980              :         /* definitely not a block:
    1981              :          *  path '{' ident ','
    1982              :          *  path '{' ident ':' [anything] ','
    1983              :          *  path '{' ident ':' [not a type]
    1984              :          * otherwise, assume block expr and thus path */
    1985              :         // DEBUG
    1986        11944 :         rust_debug ("values of lookahead: '%s' '%s' '%s' '%s' ",
    1987              :                     lexer.peek_token (1)->get_token_description (),
    1988              :                     lexer.peek_token (2)->get_token_description (),
    1989              :                     lexer.peek_token (3)->get_token_description (),
    1990              :                     lexer.peek_token (4)->get_token_description ());
    1991              : 
    1992         6575 :         rust_debug ("can be struct expr: '%s', not a block: '%s'",
    1993              :                     restrictions.can_be_struct_expr ? "true" : "false",
    1994              :                     not_a_block ? "true" : "false");
    1995              : 
    1996              :         // struct/enum expr struct
    1997         2986 :         if (!restrictions.can_be_struct_expr && !not_a_block)
    1998              :           {
    1999              :             // HACK: add outer attrs to path
    2000         1615 :             path.set_outer_attrs (std::move (outer_attrs));
    2001         1615 :             return std::unique_ptr<AST::PathInExpression> (
    2002         1615 :               new AST::PathInExpression (std::move (path)));
    2003              :           }
    2004         1371 :         auto struct_expr
    2005         2742 :           = parse_struct_expr_struct_partial (std::move (path),
    2006              :                                               std::move (outer_attrs));
    2007         1371 :         if (struct_expr == nullptr)
    2008              :           {
    2009              :             return tl::unexpected<Parse::Error::Expr> (
    2010            0 :               Parse::Error::Expr::CHILD_ERROR);
    2011              :           }
    2012         1371 :         return struct_expr;
    2013         1371 :       }
    2014        10675 :     case LEFT_PAREN:
    2015              :       {
    2016              :         // struct/enum expr tuple
    2017        10675 :         if (!restrictions.can_be_struct_expr)
    2018              :           {
    2019              :             // assume path is returned
    2020              :             // HACK: add outer attributes to path
    2021          169 :             path.set_outer_attrs (std::move (outer_attrs));
    2022          338 :             return std::make_unique<AST::PathInExpression> (std::move (path));
    2023              :           }
    2024        10506 :         auto tuple_expr
    2025        21012 :           = parse_struct_expr_tuple_partial (std::move (path),
    2026              :                                              std::move (outer_attrs));
    2027        10506 :         if (tuple_expr == nullptr)
    2028              :           {
    2029              :             return tl::unexpected<Parse::Error::Expr> (
    2030            0 :               Parse::Error::Expr::CHILD_ERROR);
    2031              :           }
    2032        10506 :         return tuple_expr;
    2033        10506 :       }
    2034        25186 :     default:
    2035              :       // assume path is returned if not single segment
    2036        25186 :       if (path.is_single_segment ())
    2037              :         {
    2038              :           // FIXME: This should probably be returned as a path.
    2039              :           /* HACK: may have to become permanent, but this is my current
    2040              :            * identifier expression */
    2041        72024 :           return std::unique_ptr<AST::IdentifierExpr> (new AST::IdentifierExpr (
    2042        96032 :             path.get_segments ()[0].get_ident_segment ().as_string (), {},
    2043        24008 :             path.get_locus ()));
    2044              :         }
    2045              :       // HACK: add outer attrs to path
    2046         1178 :       path.set_outer_attrs (std::move (outer_attrs));
    2047         1178 :       return std::unique_ptr<AST::PathInExpression> (
    2048         1178 :         new AST::PathInExpression (std::move (path)));
    2049              :     }
    2050              :   rust_unreachable ();
    2051        40650 : }
    2052              : 
    2053              : // Handling of expresions that do not start with a path for `null_denotation`.
    2054              : template <typename ManagedTokenSource>
    2055              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    2056        35897 : Parser<ManagedTokenSource>::null_denotation_not_path (
    2057              :   const_TokenPtr tok, AST::AttrVec outer_attrs, ParseRestrictions restrictions)
    2058              : {
    2059        35897 :   switch (tok->get_id ())
    2060              :     {
    2061              :     // FIXME: Handle in null_denotation_path?
    2062           97 :     case LEFT_SHIFT:
    2063              :     case LEFT_ANGLE:
    2064              :       {
    2065              :         // qualified path
    2066              :         // HACK: add outer attrs to path
    2067           97 :         AST::QualifiedPathInExpression path
    2068              :           = parse_qualified_path_in_expression (tok->get_locus ());
    2069           97 :         path.set_outer_attrs (std::move (outer_attrs));
    2070           97 :         return std::make_unique<AST::QualifiedPathInExpression> (
    2071           97 :           std::move (path));
    2072           97 :       }
    2073              :     // FIXME: delegate to parse_literal_expr instead? would have to rejig
    2074              :     // tokens and whatever.
    2075              :     // FIXME: for literal exprs, outer attrs should be passed in, and later
    2076              :     // error if it does not make up the entire statement.
    2077        15772 :     case INT_LITERAL:
    2078              :       // we should check the range, but ignore for now
    2079              :       // encode as int?
    2080        15772 :       return std::unique_ptr<AST::LiteralExpr> (
    2081        48607 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::INT,
    2082        15772 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2083          344 :     case FLOAT_LITERAL:
    2084              :       // encode as float?
    2085          344 :       return std::unique_ptr<AST::LiteralExpr> (
    2086         1376 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT,
    2087          344 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2088         2494 :     case STRING_LITERAL:
    2089         2494 :       return std::unique_ptr<AST::LiteralExpr> (
    2090         9976 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
    2091         2494 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2092           82 :     case BYTE_STRING_LITERAL:
    2093           82 :       return std::unique_ptr<AST::LiteralExpr> (
    2094          328 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE_STRING,
    2095           82 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2096           25 :     case RAW_STRING_LITERAL:
    2097           25 :       return std::unique_ptr<AST::LiteralExpr> (
    2098          100 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::RAW_STRING,
    2099           25 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2100          190 :     case CHAR_LITERAL:
    2101          190 :       return std::unique_ptr<AST::LiteralExpr> (
    2102          760 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR,
    2103          190 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2104           57 :     case BYTE_CHAR_LITERAL:
    2105           57 :       return std::unique_ptr<AST::LiteralExpr> (
    2106          228 :         new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE,
    2107           57 :                               tok->get_type_hint (), {}, tok->get_locus ()));
    2108          714 :     case TRUE_LITERAL:
    2109          714 :       return std::unique_ptr<AST::LiteralExpr> (
    2110         2142 :         new AST::LiteralExpr (Values::Keywords::TRUE_LITERAL,
    2111              :                               AST::Literal::BOOL, tok->get_type_hint (), {},
    2112          714 :                               tok->get_locus ()));
    2113          533 :     case FALSE_LITERAL:
    2114          533 :       return std::unique_ptr<AST::LiteralExpr> (
    2115         1599 :         new AST::LiteralExpr (Values::Keywords::FALSE_LITERAL,
    2116              :                               AST::Literal::BOOL, tok->get_type_hint (), {},
    2117          533 :                               tok->get_locus ()));
    2118          849 :     case LEFT_PAREN:
    2119              :       {
    2120          849 :         auto grouped_or_tuple_expr
    2121          849 :           = parse_grouped_or_tuple_expr (std::move (outer_attrs),
    2122              :                                          tok->get_locus ());
    2123          849 :         if (grouped_or_tuple_expr)
    2124          848 :           return std::move (grouped_or_tuple_expr.value ());
    2125              :         else
    2126              :           return tl::unexpected<Parse::Error::Expr> (
    2127            1 :             Parse::Error::Expr::CHILD_ERROR);
    2128          849 :       }
    2129              : 
    2130              :     /*case PLUS: { // unary plus operator
    2131              :         // invoke parse_expr recursively with appropriate priority, etc. for
    2132              :     below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS);
    2133              : 
    2134              :         if (expr == nullptr)
    2135              :             return nullptr;
    2136              :         // can only apply to integer and float expressions
    2137              :         if (expr->get_type() != integer_type_node || expr->get_type() !=
    2138              :     float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
    2139              :     plus must be int or float but it is %s", print_type(expr->get_type()));
    2140              :     return nullptr;
    2141              :         }
    2142              : 
    2143              :         return Tree(expr, tok->get_locus());
    2144              :     }*/
    2145              :     // Rust has no unary plus operator
    2146          301 :     case MINUS:
    2147              :       { // unary minus
    2148          301 :         ParseRestrictions entered_from_unary;
    2149          301 :         entered_from_unary.entered_from_unary = true;
    2150          301 :         if (!restrictions.can_be_struct_expr)
    2151           14 :           entered_from_unary.can_be_struct_expr = false;
    2152          301 :         auto expr = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
    2153              : 
    2154          301 :         if (!expr)
    2155              :           return tl::unexpected<Parse::Error::Expr> (
    2156            0 :             Parse::Error::Expr::CHILD_ERROR);
    2157              :         // can only apply to integer and float expressions
    2158              :         /*if (expr.get_type() != integer_type_node || expr.get_type() !=
    2159              :         float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
    2160              :         minus must be int or float but it is %s",
    2161              :         print_type(expr.get_type())); return Tree::error();
    2162              :         }*/
    2163              :         /* FIXME: when implemented the "get type" method on expr, ensure it is
    2164              :          * int or float type (except unsigned int). Actually, this would
    2165              :          * probably have to be done in semantic analysis (as type checking).
    2166              :          */
    2167              : 
    2168              :         /* FIXME: allow outer attributes on these expressions by having an
    2169              :          * outer attrs parameter in function*/
    2170          301 :         return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
    2171          301 :                                                     NegationOperator::NEGATE,
    2172              :                                                     std::move (outer_attrs),
    2173          602 :                                                     tok->get_locus ());
    2174          301 :       }
    2175          234 :     case EXCLAM:
    2176              :       { // logical or bitwise not
    2177          234 :         ParseRestrictions entered_from_unary;
    2178          234 :         entered_from_unary.entered_from_unary = true;
    2179          234 :         if (!restrictions.can_be_struct_expr)
    2180           74 :           entered_from_unary.can_be_struct_expr = false;
    2181          234 :         auto expr = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
    2182              : 
    2183          234 :         if (!expr)
    2184              :           return tl::unexpected<Parse::Error::Expr> (
    2185            0 :             Parse::Error::Expr::CHILD_ERROR);
    2186              :         // can only apply to boolean expressions
    2187              :         /*if (expr.get_type() != boolean_type_node) {
    2188              :             rust_error_at(tok->get_locus(),
    2189              :               "operand of logical not must be a boolean but it is %s",
    2190              :               print_type(expr.get_type()));
    2191              :             return Tree::error();
    2192              :         }*/
    2193              :         /* FIXME: type checking for boolean or integer expressions in semantic
    2194              :          * analysis */
    2195              : 
    2196              :         // FIXME: allow outer attributes on these expressions
    2197          234 :         return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
    2198          234 :                                                     NegationOperator::NOT,
    2199              :                                                     std::move (outer_attrs),
    2200          468 :                                                     tok->get_locus ());
    2201          234 :       }
    2202         3964 :     case ASTERISK:
    2203              :       {
    2204              :         /* pointer dereference only - HACK: as struct expressions should
    2205              :          * always be value expressions, cannot be dereferenced */
    2206         3964 :         ParseRestrictions entered_from_unary;
    2207         3964 :         entered_from_unary.entered_from_unary = true;
    2208         3964 :         entered_from_unary.can_be_struct_expr = false;
    2209         3964 :         auto expr = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
    2210         3964 :         if (!expr)
    2211              :           return tl::unexpected<Parse::Error::Expr> (
    2212            1 :             Parse::Error::Expr::CHILD_ERROR);
    2213              :         // FIXME: allow outer attributes on expression
    2214         3963 :         return std::make_unique<AST::DereferenceExpr> (std::move (
    2215         3963 :                                                          expr.value ()),
    2216              :                                                        std::move (outer_attrs),
    2217         7926 :                                                        tok->get_locus ());
    2218         3964 :       }
    2219         1430 :     case AMP:
    2220              :       {
    2221              :         // (single) "borrow" expression - shared (mutable) or immutable
    2222         1430 :         tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
    2223              :           = tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2224         1430 :         Mutability mutability = Mutability::Imm;
    2225              :         bool raw_borrow = false;
    2226              : 
    2227         1430 :         ParseRestrictions entered_from_unary;
    2228         1430 :         entered_from_unary.entered_from_unary = true;
    2229         1430 :         if (!restrictions.can_be_struct_expr)
    2230            0 :           entered_from_unary.can_be_struct_expr = false;
    2231              : 
    2232            9 :         auto is_mutability = [] (const_TokenPtr token) {
    2233            9 :           return token->get_id () == CONST || token->get_id () == MUT;
    2234              :         };
    2235              : 
    2236         1430 :         auto t = lexer.peek_token ();
    2237              :         // Weak raw keyword, we look (1) ahead and treat it as an identifier if
    2238              :         // there is no mut nor const.
    2239         2833 :         if (t->get_id () == IDENTIFIER
    2240          741 :             && t->get_str () == Values::WeakKeywords::RAW
    2241         1483 :             && is_mutability (lexer.peek_token (1)))
    2242              :           {
    2243            7 :             lexer.skip_token ();
    2244           14 :             switch (lexer.peek_token ()->get_id ())
    2245              :               {
    2246            6 :               case MUT:
    2247            6 :                 mutability = Mutability::Mut;
    2248            6 :                 break;
    2249              :               case CONST:
    2250              :                 mutability = Mutability::Imm;
    2251              :                 break;
    2252            0 :               default:
    2253            0 :                 rust_error_at (lexer.peek_token ()->get_locus (),
    2254              :                                "raw borrow should be either const or mut");
    2255              :               }
    2256            7 :             lexer.skip_token ();
    2257            7 :             auto expr_result
    2258            7 :               = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
    2259            7 :             if (expr_result)
    2260            7 :               expr = std::move (expr_result.value ());
    2261              :             else
    2262              :               return tl::unexpected<Parse::Error::Expr> (
    2263            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2264            7 :             raw_borrow = true;
    2265            7 :           }
    2266         1423 :         else if (t->get_id () == MUT)
    2267              :           {
    2268          401 :             lexer.skip_token ();
    2269          401 :             auto expr_result
    2270          401 :               = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
    2271          401 :             if (expr_result)
    2272          401 :               expr = std::move (expr_result.value ());
    2273              :             else
    2274              :               return tl::unexpected<Parse::Error::Expr> (
    2275            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2276          401 :             mutability = Mutability::Mut;
    2277          401 :             raw_borrow = false;
    2278          401 :           }
    2279              :         else
    2280              :           {
    2281         1022 :             auto expr_result
    2282         1022 :               = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
    2283         1022 :             if (expr_result)
    2284         1021 :               expr = std::move (expr_result.value ());
    2285              :             else
    2286              :               return tl::unexpected<Parse::Error::Expr> (
    2287            1 :                 Parse::Error::Expr::CHILD_ERROR);
    2288         1021 :             raw_borrow = false;
    2289         1022 :           }
    2290              : 
    2291              :         // FIXME: allow outer attributes on expression
    2292         1429 :         return std::make_unique<AST::BorrowExpr> (std::move (expr.value ()),
    2293         1429 :                                                   mutability, raw_borrow, false,
    2294              :                                                   std::move (outer_attrs),
    2295         2858 :                                                   tok->get_locus ());
    2296         2859 :       }
    2297           23 :     case LOGICAL_AND:
    2298              :       {
    2299              :         // (double) "borrow" expression - shared (mutable) or immutable
    2300           23 :         std::unique_ptr<AST::Expr> expr = nullptr;
    2301           23 :         Mutability mutability = Mutability::Imm;
    2302              : 
    2303           23 :         ParseRestrictions entered_from_unary;
    2304           23 :         entered_from_unary.entered_from_unary = true;
    2305              : 
    2306           46 :         if (lexer.peek_token ()->get_id () == MUT)
    2307              :           {
    2308            0 :             lexer.skip_token ();
    2309            0 :             auto expr_res
    2310            0 :               = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
    2311            0 :             if (!expr_res)
    2312              :               return tl::unexpected<Parse::Error::Expr> (
    2313            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2314            0 :             expr = std::move (expr_res.value ());
    2315            0 :             mutability = Mutability::Mut;
    2316            0 :           }
    2317              :         else
    2318              :           {
    2319           23 :             auto expr_result
    2320           23 :               = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
    2321           23 :             if (expr_result)
    2322           23 :               expr = std::move (expr_result.value ());
    2323              :             else
    2324              :               return tl::unexpected<Parse::Error::Expr> (
    2325            0 :                 Parse::Error::Expr::CHILD_ERROR);
    2326           23 :             mutability = Mutability::Imm;
    2327           23 :           }
    2328              : 
    2329              :         // FIXME: allow outer attributes on expression
    2330           23 :         return std::make_unique<AST::BorrowExpr> (std::move (expr), mutability,
    2331           23 :                                                   false, true,
    2332              :                                                   std::move (outer_attrs),
    2333           23 :                                                   tok->get_locus ());
    2334           23 :       }
    2335           73 :     case OR:
    2336              :     case PIPE:
    2337              :     case MOVE:
    2338              :       // closure expression
    2339              :       {
    2340          219 :         auto ret = parse_closure_expr_pratt (tok, std::move (outer_attrs));
    2341           73 :         if (ret)
    2342           73 :           return std::move (ret.value ());
    2343              :         else
    2344              :           return tl::unexpected<Parse::Error::Expr> (
    2345            0 :             Parse::Error::Expr::CHILD_ERROR);
    2346           73 :       }
    2347            9 :     case DOT_DOT:
    2348              :       // either "range to" or "range full" expressions
    2349              :       {
    2350            9 :         auto ret
    2351           27 :           = parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
    2352            9 :         if (ret)
    2353            9 :           return std::move (ret.value ());
    2354              :         else
    2355              :           return tl::unexpected<Parse::Error::Expr> (
    2356            0 :             Parse::Error::Expr::CHILD_ERROR);
    2357            9 :       }
    2358            0 :     case DOT_DOT_EQ:
    2359              :       // range to inclusive expr
    2360              :       {
    2361            0 :         auto ret = parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
    2362            0 :         if (ret)
    2363            0 :           return std::move (ret.value ());
    2364              :         else
    2365              :           return tl::unexpected<Parse::Error::Expr> (
    2366            0 :             Parse::Error::Expr::CHILD_ERROR);
    2367            0 :       }
    2368          546 :     case RETURN_KW:
    2369              :       // FIXME: is this really a null denotation expression?
    2370              :       {
    2371          546 :         auto ret
    2372          546 :           = parse_return_expr (std::move (outer_attrs), tok->get_locus ());
    2373          546 :         if (ret)
    2374          546 :           return std::move (ret.value ());
    2375              :         else
    2376              :           return tl::unexpected<Parse::Error::Expr> (
    2377            0 :             Parse::Error::Expr::CHILD_ERROR);
    2378          546 :       }
    2379            1 :     case TRY:
    2380              :       // FIXME: is this really a null denotation expression?
    2381              :       {
    2382            1 :         auto ret = parse_try_expr (std::move (outer_attrs), tok->get_locus ());
    2383            1 :         if (ret)
    2384            1 :           return std::move (ret.value ());
    2385              :         else
    2386              :           return tl::unexpected<Parse::Error::Expr> (
    2387            0 :             Parse::Error::Expr::CHILD_ERROR);
    2388            1 :       }
    2389           79 :     case BREAK:
    2390              :       // FIXME: is this really a null denotation expression?
    2391              :       {
    2392           79 :         auto ret
    2393           79 :           = parse_break_expr (std::move (outer_attrs), tok->get_locus ());
    2394           79 :         if (ret)
    2395           79 :           return std::move (ret.value ());
    2396              :         else
    2397              :           return tl::unexpected<Parse::Error::Expr> (
    2398            0 :             Parse::Error::Expr::CHILD_ERROR);
    2399           79 :       }
    2400           17 :     case CONTINUE:
    2401           17 :       return parse_continue_expr (std::move (outer_attrs), tok->get_locus ());
    2402         1527 :     case LEFT_CURLY:
    2403              :       // ok - this is an expression with block for once.
    2404              :       {
    2405         1527 :         auto ret = parse_block_expr (std::move (outer_attrs), tl::nullopt,
    2406              :                                      tok->get_locus ());
    2407         1527 :         if (ret)
    2408         1526 :           return std::move (ret.value ());
    2409              :         else
    2410              :           return tl::unexpected<Parse::Error::Expr> (
    2411            1 :             Parse::Error::Expr::CHILD_ERROR);
    2412         1527 :       }
    2413         1401 :     case IF:
    2414              :       // if or if let, so more lookahead to find out
    2415         2802 :       if (lexer.peek_token ()->get_id () == LET)
    2416              :         {
    2417              :           // if let expr
    2418           30 :           auto ret
    2419           30 :             = parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
    2420           30 :           if (ret)
    2421           30 :             return std::move (ret.value ());
    2422              :           else
    2423              :             return tl::unexpected<Parse::Error::Expr> (
    2424            0 :               Parse::Error::Expr::CHILD_ERROR);
    2425           30 :         }
    2426              :       else
    2427              :         {
    2428              :           // if expr
    2429         1371 :           auto ret = parse_if_expr (std::move (outer_attrs), tok->get_locus ());
    2430         1371 :           if (ret)
    2431         1370 :             return std::move (ret.value ());
    2432              :           else
    2433              :             return tl::unexpected<Parse::Error::Expr> (
    2434            1 :               Parse::Error::Expr::CHILD_ERROR);
    2435         1371 :         }
    2436           40 :     case LIFETIME:
    2437              :       {
    2438          120 :         auto ret = parse_labelled_loop_expr (tok, std::move (outer_attrs));
    2439           40 :         if (ret)
    2440           40 :           return std::move (ret.value ());
    2441              :         else
    2442              :           return tl::unexpected<Parse::Error::Expr> (
    2443            0 :             Parse::Error::Expr::CHILD_ERROR);
    2444           40 :       }
    2445           77 :     case LOOP:
    2446              :       {
    2447           77 :         auto ret = parse_loop_expr (std::move (outer_attrs), tl::nullopt,
    2448              :                                     tok->get_locus ());
    2449           77 :         if (ret)
    2450           76 :           return std::move (ret.value ());
    2451              :         else
    2452              :           return tl::unexpected<Parse::Error::Expr> (
    2453            1 :             Parse::Error::Expr::CHILD_ERROR);
    2454           77 :       }
    2455           80 :     case WHILE:
    2456          160 :       if (lexer.peek_token ()->get_id () == LET)
    2457              :         {
    2458            4 :           auto ret = parse_while_let_loop_expr (std::move (outer_attrs));
    2459            4 :           if (ret)
    2460            3 :             return std::move (ret.value ());
    2461              :           else
    2462              :             return tl::unexpected<Parse::Error::Expr> (
    2463            1 :               Parse::Error::Expr::CHILD_ERROR);
    2464            4 :         }
    2465              :       else
    2466              :         {
    2467           76 :           auto ret = parse_while_loop_expr (std::move (outer_attrs),
    2468          152 :                                             tl::nullopt, tok->get_locus ());
    2469           76 :           if (ret)
    2470           76 :             return std::move (ret.value ());
    2471              :           else
    2472              :             return tl::unexpected<Parse::Error::Expr> (
    2473            0 :               Parse::Error::Expr::CHILD_ERROR);
    2474           76 :         }
    2475           19 :     case FOR:
    2476              :       {
    2477           19 :         auto ret = parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
    2478           19 :         if (ret)
    2479           19 :           return std::move (ret.value ());
    2480              :         else
    2481              :           return tl::unexpected<Parse::Error::Expr> (
    2482            0 :             Parse::Error::Expr::CHILD_ERROR);
    2483           19 :       }
    2484          906 :     case MATCH_KW:
    2485              :       // also an expression with block
    2486              :       {
    2487          906 :         auto ret
    2488          906 :           = parse_match_expr (std::move (outer_attrs), tok->get_locus ());
    2489          906 :         if (ret)
    2490          903 :           return std::move (ret.value ());
    2491              :         else
    2492              :           return tl::unexpected<Parse::Error::Expr> (
    2493            3 :             Parse::Error::Expr::CHILD_ERROR);
    2494          906 :       }
    2495              : 
    2496          402 :     case LEFT_SQUARE:
    2497              :       // array definition expr (not indexing)
    2498              :       {
    2499          402 :         auto ret
    2500          402 :           = parse_array_expr (std::move (outer_attrs), tok->get_locus ());
    2501          402 :         if (ret)
    2502          402 :           return std::move (ret.value ());
    2503              :         else
    2504              :           return tl::unexpected<Parse::Error::Expr> (
    2505            0 :             Parse::Error::Expr::CHILD_ERROR);
    2506          402 :       }
    2507         3544 :     case UNSAFE:
    2508              :       {
    2509         3544 :         auto ret = parse_unsafe_block_expr (std::move (outer_attrs),
    2510              :                                             tok->get_locus ());
    2511         3544 :         if (ret)
    2512         3544 :           return std::move (ret.value ());
    2513              :         else
    2514              :           return tl::unexpected<Parse::Error::Expr> (
    2515            0 :             Parse::Error::Expr::CHILD_ERROR);
    2516         3544 :       }
    2517            2 :     case BOX:
    2518              :       {
    2519            2 :         auto ret = parse_box_expr (std::move (outer_attrs), tok->get_locus ());
    2520            2 :         if (ret)
    2521            2 :           return std::move (ret.value ());
    2522              :         else
    2523              :           return tl::unexpected<Parse::Error::Expr> (
    2524            0 :             Parse::Error::Expr::CHILD_ERROR);
    2525            2 :       }
    2526            1 :     case UNDERSCORE:
    2527            1 :       add_error (
    2528            1 :         Error (tok->get_locus (),
    2529              :                "use of %qs is not allowed on the right-side of an assignment",
    2530              :                tok->get_token_description ()));
    2531            1 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2532           15 :     case CONST:
    2533              :       {
    2534           15 :         auto ret
    2535           15 :           = parse_const_block_expr (std::move (outer_attrs), tok->get_locus ());
    2536           15 :         if (ret)
    2537           15 :           return std::move (ret.value ());
    2538              :         else
    2539              :           return tl::unexpected<Parse::Error::Expr> (
    2540            0 :             Parse::Error::Expr::CHILD_ERROR);
    2541           15 :       }
    2542           49 :     default:
    2543           49 :       if (!restrictions.expr_can_be_null)
    2544           49 :         add_error (Error (tok->get_locus (),
    2545              :                           "found unexpected token %qs in null denotation",
    2546              :                           tok->get_token_description ()));
    2547           49 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2548              :     }
    2549              : }
    2550              : 
    2551              : /* Called for each token that can appear in infix (between) position. Can be
    2552              :  * operators or other punctuation. Returns a function pointer to member
    2553              :  * function that implements the left denotation for the token given. */
    2554              : template <typename ManagedTokenSource>
    2555              : tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
    2556        25182 : Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
    2557              :                                              std::unique_ptr<AST::Expr> left,
    2558              :                                              AST::AttrVec outer_attrs,
    2559              :                                              ParseRestrictions restrictions)
    2560              : {
    2561              :   // Token passed in has already been skipped, so peek gives "next" token
    2562        25182 :   switch (tok->get_id ())
    2563              :     {
    2564              :     // FIXME: allow for outer attributes to be applied
    2565            1 :     case QUESTION_MARK:
    2566              :       {
    2567            1 :         location_t left_locus = left->get_locus ();
    2568              :         // error propagation expression - unary postfix
    2569            1 :         return std::make_unique<AST::ErrorPropagationExpr> (
    2570            1 :           std::move (left), std::move (outer_attrs), left_locus);
    2571              :       }
    2572         3040 :     case PLUS:
    2573              :       // sum expression - binary infix
    2574              :       /*return parse_binary_plus_expr (tok, std::move (left),
    2575              :                                      std::move (outer_attrs), restrictions);*/
    2576        12160 :       return parse_arithmetic_or_logical_expr (tok, std::move (left),
    2577              :                                                std::move (outer_attrs),
    2578              :                                                ArithmeticOrLogicalOperator::ADD,
    2579         3040 :                                                restrictions);
    2580         1027 :     case MINUS:
    2581              :       // difference expression - binary infix
    2582              :       /*return parse_binary_minus_expr (tok, std::move (left),
    2583              :                                       std::move (outer_attrs),
    2584              :          restrictions);*/
    2585         4108 :       return parse_arithmetic_or_logical_expr (
    2586              :         tok, std::move (left), std::move (outer_attrs),
    2587         1027 :         ArithmeticOrLogicalOperator::SUBTRACT, restrictions);
    2588          226 :     case ASTERISK:
    2589              :       // product expression - binary infix
    2590              :       /*return parse_binary_mult_expr (tok, std::move (left),
    2591              :                                      std::move (outer_attrs), restrictions);*/
    2592          904 :       return parse_arithmetic_or_logical_expr (
    2593              :         tok, std::move (left), std::move (outer_attrs),
    2594          226 :         ArithmeticOrLogicalOperator::MULTIPLY, restrictions);
    2595           34 :     case DIV:
    2596              :       // quotient expression - binary infix
    2597              :       /*return parse_binary_div_expr (tok, std::move (left),
    2598              :                                     std::move (outer_attrs), restrictions);*/
    2599          136 :       return parse_arithmetic_or_logical_expr (
    2600              :         tok, std::move (left), std::move (outer_attrs),
    2601           34 :         ArithmeticOrLogicalOperator::DIVIDE, restrictions);
    2602           36 :     case PERCENT:
    2603              :       // modulo expression - binary infix
    2604              :       /*return parse_binary_mod_expr (tok, std::move (left),
    2605              :                                     std::move (outer_attrs), restrictions);*/
    2606          144 :       return parse_arithmetic_or_logical_expr (
    2607              :         tok, std::move (left), std::move (outer_attrs),
    2608           36 :         ArithmeticOrLogicalOperator::MODULUS, restrictions);
    2609           52 :     case AMP:
    2610              :       // logical or bitwise and expression - binary infix
    2611              :       /*return parse_bitwise_and_expr (tok, std::move (left),
    2612              :                                      std::move (outer_attrs), restrictions);*/
    2613          208 :       return parse_arithmetic_or_logical_expr (
    2614              :         tok, std::move (left), std::move (outer_attrs),
    2615           52 :         ArithmeticOrLogicalOperator::BITWISE_AND, restrictions);
    2616           25 :     case PIPE:
    2617              :       // logical or bitwise or expression - binary infix
    2618              :       /*return parse_bitwise_or_expr (tok, std::move (left),
    2619              :                                     std::move (outer_attrs), restrictions);*/
    2620          100 :       return parse_arithmetic_or_logical_expr (
    2621              :         tok, std::move (left), std::move (outer_attrs),
    2622           25 :         ArithmeticOrLogicalOperator::BITWISE_OR, restrictions);
    2623           63 :     case CARET:
    2624              :       // logical or bitwise xor expression - binary infix
    2625              :       /*return parse_bitwise_xor_expr (tok, std::move (left),
    2626              :                                      std::move (outer_attrs), restrictions);*/
    2627          252 :       return parse_arithmetic_or_logical_expr (
    2628              :         tok, std::move (left), std::move (outer_attrs),
    2629           63 :         ArithmeticOrLogicalOperator::BITWISE_XOR, restrictions);
    2630           51 :     case LEFT_SHIFT:
    2631              :       // left shift expression - binary infix
    2632              :       /*return parse_left_shift_expr (tok, std::move (left),
    2633              :                                     std::move (outer_attrs), restrictions);*/
    2634          204 :       return parse_arithmetic_or_logical_expr (
    2635              :         tok, std::move (left), std::move (outer_attrs),
    2636           51 :         ArithmeticOrLogicalOperator::LEFT_SHIFT, restrictions);
    2637           16 :     case RIGHT_SHIFT:
    2638              :       // right shift expression - binary infix
    2639              :       /*return parse_right_shift_expr (tok, std::move (left),
    2640              :                                      std::move (outer_attrs), restrictions);*/
    2641           64 :       return parse_arithmetic_or_logical_expr (
    2642              :         tok, std::move (left), std::move (outer_attrs),
    2643           16 :         ArithmeticOrLogicalOperator::RIGHT_SHIFT, restrictions);
    2644          670 :     case EQUAL_EQUAL:
    2645              :       // equal to expression - binary infix (no associativity)
    2646              :       /*return parse_binary_equal_expr (tok, std::move (left),
    2647              :                                       std::move (outer_attrs),
    2648              :          restrictions);*/
    2649         2680 :       return parse_comparison_expr (tok, std::move (left),
    2650              :                                     std::move (outer_attrs),
    2651          670 :                                     ComparisonOperator::EQUAL, restrictions);
    2652          191 :     case NOT_EQUAL:
    2653              :       // not equal to expression - binary infix (no associativity)
    2654              :       /*return parse_binary_not_equal_expr (tok, std::move (left),
    2655              :                                           std::move (outer_attrs),
    2656              :                                           restrictions);*/
    2657          764 :       return parse_comparison_expr (tok, std::move (left),
    2658              :                                     std::move (outer_attrs),
    2659              :                                     ComparisonOperator::NOT_EQUAL,
    2660          191 :                                     restrictions);
    2661          643 :     case RIGHT_ANGLE:
    2662              :       // greater than expression - binary infix (no associativity)
    2663              :       /*return parse_binary_greater_than_expr (tok, std::move (left),
    2664              :                                              std::move (outer_attrs),
    2665              :                                              restrictions);*/
    2666         2572 :       return parse_comparison_expr (tok, std::move (left),
    2667              :                                     std::move (outer_attrs),
    2668              :                                     ComparisonOperator::GREATER_THAN,
    2669          643 :                                     restrictions);
    2670          622 :     case LEFT_ANGLE:
    2671              :       // less than expression - binary infix (no associativity)
    2672              :       /*return parse_binary_less_than_expr (tok, std::move (left),
    2673              :                                           std::move (outer_attrs),
    2674              :                                           restrictions);*/
    2675         2488 :       return parse_comparison_expr (tok, std::move (left),
    2676              :                                     std::move (outer_attrs),
    2677              :                                     ComparisonOperator::LESS_THAN,
    2678          622 :                                     restrictions);
    2679          190 :     case GREATER_OR_EQUAL:
    2680              :       // greater than or equal to expression - binary infix (no associativity)
    2681              :       /*return parse_binary_greater_equal_expr (tok, std::move (left),
    2682              :                                               std::move (outer_attrs),
    2683              :                                               restrictions);*/
    2684          760 :       return parse_comparison_expr (tok, std::move (left),
    2685              :                                     std::move (outer_attrs),
    2686              :                                     ComparisonOperator::GREATER_OR_EQUAL,
    2687          190 :                                     restrictions);
    2688          224 :     case LESS_OR_EQUAL:
    2689              :       // less than or equal to expression - binary infix (no associativity)
    2690              :       /*return parse_binary_less_equal_expr (tok, std::move (left),
    2691              :                                            std::move (outer_attrs),
    2692              :                                            restrictions);*/
    2693          896 :       return parse_comparison_expr (tok, std::move (left),
    2694              :                                     std::move (outer_attrs),
    2695              :                                     ComparisonOperator::LESS_OR_EQUAL,
    2696          224 :                                     restrictions);
    2697           56 :     case OR:
    2698              :       // lazy logical or expression - binary infix
    2699          224 :       return parse_lazy_or_expr (tok, std::move (left), std::move (outer_attrs),
    2700           56 :                                  restrictions);
    2701          260 :     case LOGICAL_AND:
    2702              :       // lazy logical and expression - binary infix
    2703         1040 :       return parse_lazy_and_expr (tok, std::move (left),
    2704          260 :                                   std::move (outer_attrs), restrictions);
    2705         5160 :     case AS:
    2706              :       /* type cast expression - kind of binary infix (RHS is actually a
    2707              :        * TypeNoBounds) */
    2708        20640 :       return parse_type_cast_expr (tok, std::move (left),
    2709         5160 :                                    std::move (outer_attrs), restrictions);
    2710         2520 :     case EQUAL:
    2711              :       // assignment expression - binary infix (note right-to-left
    2712              :       // associativity)
    2713        10080 :       return parse_assig_expr (tok, std::move (left), std::move (outer_attrs),
    2714         2520 :                                restrictions);
    2715          157 :     case PLUS_EQ:
    2716              :       /* plus-assignment expression - binary infix (note right-to-left
    2717              :        * associativity) */
    2718              :       /*return parse_plus_assig_expr (tok, std::move (left),
    2719              :                                     std::move (outer_attrs), restrictions);*/
    2720          628 :       return parse_compound_assignment_expr (tok, std::move (left),
    2721              :                                              std::move (outer_attrs),
    2722              :                                              CompoundAssignmentOperator::ADD,
    2723          157 :                                              restrictions);
    2724          105 :     case MINUS_EQ:
    2725              :       /* minus-assignment expression - binary infix (note right-to-left
    2726              :        * associativity) */
    2727              :       /*return parse_minus_assig_expr (tok, std::move (left),
    2728              :                                      std::move (outer_attrs), restrictions);*/
    2729          420 :       return parse_compound_assignment_expr (
    2730              :         tok, std::move (left), std::move (outer_attrs),
    2731          105 :         CompoundAssignmentOperator::SUBTRACT, restrictions);
    2732            7 :     case ASTERISK_EQ:
    2733              :       /* multiply-assignment expression - binary infix (note right-to-left
    2734              :        * associativity) */
    2735              :       /*return parse_mult_assig_expr (tok, std::move (left),
    2736              :                                     std::move (outer_attrs), restrictions);*/
    2737           28 :       return parse_compound_assignment_expr (
    2738              :         tok, std::move (left), std::move (outer_attrs),
    2739            7 :         CompoundAssignmentOperator::MULTIPLY, restrictions);
    2740            7 :     case DIV_EQ:
    2741              :       /* division-assignment expression - binary infix (note right-to-left
    2742              :        * associativity) */
    2743              :       /*return parse_div_assig_expr (tok, std::move (left),
    2744              :                                    std::move (outer_attrs), restrictions);*/
    2745           28 :       return parse_compound_assignment_expr (tok, std::move (left),
    2746              :                                              std::move (outer_attrs),
    2747              :                                              CompoundAssignmentOperator::DIVIDE,
    2748            7 :                                              restrictions);
    2749            7 :     case PERCENT_EQ:
    2750              :       /* modulo-assignment expression - binary infix (note right-to-left
    2751              :        * associativity) */
    2752              :       /*return parse_mod_assig_expr (tok, std::move (left),
    2753              :                                    std::move (outer_attrs), restrictions);*/
    2754           28 :       return parse_compound_assignment_expr (
    2755              :         tok, std::move (left), std::move (outer_attrs),
    2756            7 :         CompoundAssignmentOperator::MODULUS, restrictions);
    2757           21 :     case AMP_EQ:
    2758              :       /* bitwise and-assignment expression - binary infix (note right-to-left
    2759              :        * associativity) */
    2760              :       /*return parse_and_assig_expr (tok, std::move (left),
    2761              :                                    std::move (outer_attrs), restrictions);*/
    2762           84 :       return parse_compound_assignment_expr (
    2763              :         tok, std::move (left), std::move (outer_attrs),
    2764           21 :         CompoundAssignmentOperator::BITWISE_AND, restrictions);
    2765           28 :     case PIPE_EQ:
    2766              :       /* bitwise or-assignment expression - binary infix (note right-to-left
    2767              :        * associativity) */
    2768              :       /*return parse_or_assig_expr (tok, std::move (left),
    2769              :                                   std::move (outer_attrs), restrictions);*/
    2770          112 :       return parse_compound_assignment_expr (
    2771              :         tok, std::move (left), std::move (outer_attrs),
    2772           28 :         CompoundAssignmentOperator::BITWISE_OR, restrictions);
    2773          336 :     case CARET_EQ:
    2774              :       /* bitwise xor-assignment expression - binary infix (note right-to-left
    2775              :        * associativity) */
    2776              :       /*return parse_xor_assig_expr (tok, std::move (left),
    2777              :                                    std::move (outer_attrs), restrictions);*/
    2778         1344 :       return parse_compound_assignment_expr (
    2779              :         tok, std::move (left), std::move (outer_attrs),
    2780          336 :         CompoundAssignmentOperator::BITWISE_XOR, restrictions);
    2781            7 :     case LEFT_SHIFT_EQ:
    2782              :       /* left shift-assignment expression - binary infix (note right-to-left
    2783              :        * associativity) */
    2784              :       /*return parse_left_shift_assig_expr (tok, std::move (left),
    2785              :                                           std::move (outer_attrs),
    2786              :                                           restrictions);*/
    2787           28 :       return parse_compound_assignment_expr (
    2788              :         tok, std::move (left), std::move (outer_attrs),
    2789            7 :         CompoundAssignmentOperator::LEFT_SHIFT, restrictions);
    2790            7 :     case RIGHT_SHIFT_EQ:
    2791              :       /* right shift-assignment expression - binary infix (note right-to-left
    2792              :        * associativity) */
    2793              :       /*return parse_right_shift_assig_expr (tok, std::move (left),
    2794              :                                            std::move (outer_attrs),
    2795              :                                            restrictions);*/
    2796           28 :       return parse_compound_assignment_expr (
    2797              :         tok, std::move (left), std::move (outer_attrs),
    2798            7 :         CompoundAssignmentOperator::RIGHT_SHIFT, restrictions);
    2799           78 :     case DOT_DOT:
    2800              :       /* range exclusive expression - binary infix (no associativity)
    2801              :        * either "range" or "range from" */
    2802          312 :       return parse_led_range_exclusive_expr (tok, std::move (left),
    2803              :                                              std::move (outer_attrs),
    2804           78 :                                              restrictions);
    2805            7 :     case DOT_DOT_EQ:
    2806              :       /* range inclusive expression - binary infix (no associativity)
    2807              :        * unambiguously RangeInclusiveExpr */
    2808           28 :       return parse_range_inclusive_expr (tok, std::move (left),
    2809            7 :                                          std::move (outer_attrs), restrictions);
    2810            0 :     case SCOPE_RESOLUTION:
    2811              :       // path expression - binary infix? FIXME should this even be parsed
    2812              :       // here?
    2813            0 :       add_error (
    2814            0 :         Error (tok->get_locus (),
    2815              :                "found scope resolution operator in left denotation "
    2816              :                "function - this should probably be handled elsewhere"));
    2817              : 
    2818            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2819         8785 :     case DOT:
    2820              :       {
    2821              :         /* field expression or method call - relies on parentheses after next
    2822              :          * identifier or await if token after is "await" (unary postfix) or
    2823              :          * tuple index if token after is a decimal int literal */
    2824              : 
    2825         8785 :         const_TokenPtr next_tok = lexer.peek_token ();
    2826         8785 :         if (next_tok->get_id () == IDENTIFIER
    2827         8785 :             && next_tok->get_str () == Values::Keywords::AWAIT)
    2828              :           {
    2829              :             // await expression
    2830            0 :             return parse_await_expr (tok, std::move (left),
    2831            0 :                                      std::move (outer_attrs));
    2832              :           }
    2833         8785 :         else if (next_tok->get_id () == INT_LITERAL)
    2834              :           {
    2835              :             // tuple index expression - TODO check for decimal int literal
    2836         3600 :             return parse_tuple_index_expr (tok, std::move (left),
    2837              :                                            std::move (outer_attrs),
    2838          900 :                                            restrictions);
    2839              :           }
    2840         7885 :         else if (next_tok->get_id () == FLOAT_LITERAL)
    2841              :           {
    2842              :             // Lexer has misidentified a tuple index as a float literal
    2843              :             // eg: `(x, (y, z)).1.0` -> 1.0 has been identified as a float
    2844              :             // literal. This means we should split it into three new separate
    2845              :             // tokens, the first tuple index, the dot and the second tuple
    2846              :             // index.
    2847            2 :             auto current_loc = next_tok->get_locus ();
    2848            2 :             auto str = next_tok->get_str ();
    2849            2 :             auto dot_pos = str.find (".");
    2850            2 :             auto prefix = str.substr (0, dot_pos);
    2851            2 :             auto suffix = str.substr (dot_pos + 1);
    2852            2 :             if (dot_pos == str.size () - 1)
    2853            3 :               lexer.split_current_token (
    2854            2 :                 {Token::make_int (current_loc, std::move (prefix),
    2855              :                                   CORETYPE_PURE_DECIMAL),
    2856              :                  Token::make (DOT, current_loc + 1)});
    2857              :             else
    2858            5 :               lexer.split_current_token (
    2859            2 :                 {Token::make_int (current_loc, std::move (prefix),
    2860              :                                   CORETYPE_PURE_DECIMAL),
    2861              :                  Token::make (DOT, current_loc + 1),
    2862            2 :                  Token::make_int (current_loc + 2, std::move (suffix),
    2863              :                                   CORETYPE_PURE_DECIMAL)});
    2864            8 :             return parse_tuple_index_expr (tok, std::move (left),
    2865              :                                            std::move (outer_attrs),
    2866            2 :                                            restrictions);
    2867            2 :           }
    2868        12227 :         else if (next_tok->get_id () == IDENTIFIER
    2869        18061 :                  && lexer.peek_token (1)->get_id () != LEFT_PAREN
    2870        18602 :                  && lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION)
    2871              :           {
    2872              :             /* field expression (or should be) - FIXME: scope resolution right
    2873              :              * after identifier should always be method, I'm pretty sure */
    2874        19452 :             return parse_field_access_expr (tok, std::move (left),
    2875              :                                             std::move (outer_attrs),
    2876         4863 :                                             restrictions);
    2877              :           }
    2878              :         else
    2879              :           {
    2880              :             // method call (probably)
    2881        12080 :             return parse_method_call_expr (tok, std::move (left),
    2882              :                                            std::move (outer_attrs),
    2883         3020 :                                            restrictions);
    2884              :           }
    2885         8785 :       }
    2886          269 :     case LEFT_PAREN:
    2887              :       // function call - method call is based on dot notation first
    2888         1076 :       return parse_function_call_expr (tok, std::move (left),
    2889          269 :                                        std::move (outer_attrs), restrictions);
    2890          254 :     case LEFT_SQUARE:
    2891              :       // array or slice index expression (pseudo binary infix)
    2892         1016 :       return parse_index_expr (tok, std::move (left), std::move (outer_attrs),
    2893          254 :                                restrictions);
    2894            0 :     default:
    2895            0 :       add_error (Error (tok->get_locus (),
    2896              :                         "found unexpected token %qs in left denotation",
    2897              :                         tok->get_token_description ()));
    2898              : 
    2899            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    2900              :     }
    2901              : }
    2902              : 
    2903              : /* Returns the left binding power for the given ArithmeticOrLogicalExpr type.
    2904              :  * TODO make constexpr? Would that even do anything useful? */
    2905              : inline binding_powers
    2906         4570 : get_lbp_for_arithmetic_or_logical_expr (
    2907              :   AST::ArithmeticOrLogicalExpr::ExprType expr_type)
    2908              : {
    2909         4570 :   switch (expr_type)
    2910              :     {
    2911              :     case ArithmeticOrLogicalOperator::ADD:
    2912              :       return LBP_PLUS;
    2913              :     case ArithmeticOrLogicalOperator::SUBTRACT:
    2914              :       return LBP_MINUS;
    2915              :     case ArithmeticOrLogicalOperator::MULTIPLY:
    2916              :       return LBP_MUL;
    2917              :     case ArithmeticOrLogicalOperator::DIVIDE:
    2918              :       return LBP_DIV;
    2919              :     case ArithmeticOrLogicalOperator::MODULUS:
    2920              :       return LBP_MOD;
    2921              :     case ArithmeticOrLogicalOperator::BITWISE_AND:
    2922              :       return LBP_AMP;
    2923              :     case ArithmeticOrLogicalOperator::BITWISE_OR:
    2924              :       return LBP_PIPE;
    2925              :     case ArithmeticOrLogicalOperator::BITWISE_XOR:
    2926              :       return LBP_CARET;
    2927              :     case ArithmeticOrLogicalOperator::LEFT_SHIFT:
    2928              :       return LBP_L_SHIFT;
    2929              :     case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
    2930              :       return LBP_R_SHIFT;
    2931            0 :     default:
    2932              :       // WTF? should not happen, this is an error
    2933            0 :       rust_unreachable ();
    2934              : 
    2935              :       return LBP_PLUS;
    2936              :     }
    2937              : }
    2938              : 
    2939              : // Parses an arithmetic or logical expression (with Pratt parsing).
    2940              : template <typename ManagedTokenSource>
    2941              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    2942         4570 : Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr (
    2943              :   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
    2944              :   AST::ArithmeticOrLogicalExpr::ExprType expr_type,
    2945              :   ParseRestrictions restrictions)
    2946              : {
    2947              :   // parse RHS (as tok has already been consumed in parse_expression)
    2948         4570 :   auto right = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
    2949         9140 :                            AST::AttrVec (), restrictions);
    2950         4570 :   if (!right)
    2951            2 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    2952              : 
    2953              :   // TODO: check types. actually, do so during semantic analysis
    2954         4568 :   location_t locus = left->get_locus ();
    2955              : 
    2956         4568 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    2957         9136 :     std::move (left), std::move (right.value ()), expr_type, locus);
    2958         4570 : }
    2959              : 
    2960              : // Parses a binary addition expression (with Pratt parsing).
    2961              : template <typename ManagedTokenSource>
    2962              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    2963            0 : Parser<ManagedTokenSource>::parse_binary_plus_expr (
    2964              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    2965              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    2966              : {
    2967              :   // parse RHS (as tok has already been consumed in parse_expression)
    2968            0 :   auto right = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
    2969            0 :   if (!right)
    2970            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    2971              : 
    2972              :   // TODO: check types. actually, do so during semantic analysis
    2973            0 :   location_t locus = left->get_locus ();
    2974              : 
    2975            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    2976            0 :     std::move (left), std::move (right.value ()),
    2977            0 :     ArithmeticOrLogicalOperator::ADD, locus);
    2978            0 : }
    2979              : 
    2980              : // Parses a binary subtraction expression (with Pratt parsing).
    2981              : template <typename ManagedTokenSource>
    2982              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    2983            0 : Parser<ManagedTokenSource>::parse_binary_minus_expr (
    2984              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    2985              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    2986              : {
    2987              :   // parse RHS (as tok has already been consumed in parse_expression)
    2988            0 :   auto right = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
    2989            0 :   if (!right)
    2990            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    2991              : 
    2992              :   // TODO: check types. actually, do so during semantic analysis
    2993            0 :   location_t locus = left->get_locus ();
    2994              : 
    2995            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    2996            0 :     std::move (left), std::move (right.value ()),
    2997            0 :     ArithmeticOrLogicalOperator::SUBTRACT, locus);
    2998            0 : }
    2999              : 
    3000              : // Parses a binary multiplication expression (with Pratt parsing).
    3001              : template <typename ManagedTokenSource>
    3002              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3003            0 : Parser<ManagedTokenSource>::parse_binary_mult_expr (
    3004              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3005              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3006              : {
    3007              :   // parse RHS (as tok has already been consumed in parse_expression)
    3008            0 :   auto right = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
    3009            0 :   if (!right)
    3010            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3011              : 
    3012              :   // TODO: check types. actually, do so during semantic analysis
    3013            0 :   location_t locus = left->get_locus ();
    3014              : 
    3015            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3016            0 :     std::move (left), std::move (right.value ()),
    3017            0 :     ArithmeticOrLogicalOperator::MULTIPLY, locus);
    3018            0 : }
    3019              : 
    3020              : // Parses a binary division expression (with Pratt parsing).
    3021              : template <typename ManagedTokenSource>
    3022              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3023            0 : Parser<ManagedTokenSource>::parse_binary_div_expr (
    3024              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3025              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3026              : {
    3027              :   // parse RHS (as tok has already been consumed in parse_expression)
    3028            0 :   auto right = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
    3029            0 :   if (!right)
    3030            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3031              : 
    3032              :   // TODO: check types. actually, do so during semantic analysis
    3033            0 :   location_t locus = left->get_locus ();
    3034              : 
    3035            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3036            0 :     std::move (left), std::move (right.value ()),
    3037            0 :     ArithmeticOrLogicalOperator::DIVIDE, locus);
    3038            0 : }
    3039              : 
    3040              : // Parses a binary modulo expression (with Pratt parsing).
    3041              : template <typename ManagedTokenSource>
    3042              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3043            0 : Parser<ManagedTokenSource>::parse_binary_mod_expr (
    3044              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3045              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3046              : {
    3047              :   // parse RHS (as tok has already been consumed in parse_expression)
    3048            0 :   auto right = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
    3049            0 :   if (!right)
    3050            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3051              : 
    3052              :   // TODO: check types. actually, do so during semantic analysis
    3053            0 :   location_t locus = left->get_locus ();
    3054              : 
    3055            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3056            0 :     std::move (left), std::move (right.value ()),
    3057            0 :     ArithmeticOrLogicalOperator::MODULUS, locus);
    3058            0 : }
    3059              : 
    3060              : /* Parses a binary bitwise (or eager logical) and expression (with Pratt
    3061              :  * parsing). */
    3062              : template <typename ManagedTokenSource>
    3063              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3064            0 : Parser<ManagedTokenSource>::parse_bitwise_and_expr (
    3065              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3066              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3067              : {
    3068              :   // parse RHS (as tok has already been consumed in parse_expression)
    3069            0 :   auto right = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
    3070            0 :   if (!right)
    3071            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3072              : 
    3073              :   // TODO: check types. actually, do so during semantic analysis
    3074            0 :   location_t locus = left->get_locus ();
    3075              : 
    3076            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3077            0 :     std::move (left), std::move (right.value ()),
    3078            0 :     ArithmeticOrLogicalOperator::BITWISE_AND, locus);
    3079            0 : }
    3080              : 
    3081              : /* Parses a binary bitwise (or eager logical) or expression (with Pratt
    3082              :  * parsing). */
    3083              : template <typename ManagedTokenSource>
    3084              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3085            0 : Parser<ManagedTokenSource>::parse_bitwise_or_expr (
    3086              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3087              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3088              : {
    3089              :   // parse RHS (as tok has already been consumed in parse_expression)
    3090            0 :   auto right = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
    3091            0 :   if (!right)
    3092            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3093              : 
    3094              :   // TODO: check types. actually, do so during semantic analysis
    3095            0 :   location_t locus = left->get_locus ();
    3096              : 
    3097            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3098            0 :     std::move (left), std::move (right.value ()),
    3099            0 :     ArithmeticOrLogicalOperator::BITWISE_OR, locus);
    3100            0 : }
    3101              : 
    3102              : /* Parses a binary bitwise (or eager logical) xor expression (with Pratt
    3103              :  * parsing). */
    3104              : template <typename ManagedTokenSource>
    3105              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3106            0 : Parser<ManagedTokenSource>::parse_bitwise_xor_expr (
    3107              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3108              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3109              : {
    3110              :   // parse RHS (as tok has already been consumed in parse_expression)
    3111            0 :   auto right = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
    3112            0 :   if (!right)
    3113            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3114              : 
    3115              :   // TODO: check types. actually, do so during semantic analysis
    3116            0 :   location_t locus = left->get_locus ();
    3117              : 
    3118            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3119            0 :     std::move (left), std::move (right.value ()),
    3120            0 :     ArithmeticOrLogicalOperator::BITWISE_XOR, locus);
    3121            0 : }
    3122              : 
    3123              : // Parses a binary left shift expression (with Pratt parsing).
    3124              : template <typename ManagedTokenSource>
    3125              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3126            0 : Parser<ManagedTokenSource>::parse_left_shift_expr (
    3127              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3128              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3129              : {
    3130              :   // parse RHS (as tok has already been consumed in parse_expression)
    3131            0 :   auto right = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
    3132            0 :   if (!right)
    3133            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3134              : 
    3135              :   // TODO: check types. actually, do so during semantic analysis
    3136            0 :   location_t locus = left->get_locus ();
    3137              : 
    3138            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3139            0 :     std::move (left), std::move (right.value ()),
    3140            0 :     ArithmeticOrLogicalOperator::LEFT_SHIFT, locus);
    3141            0 : }
    3142              : 
    3143              : // Parses a binary right shift expression (with Pratt parsing).
    3144              : template <typename ManagedTokenSource>
    3145              : tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
    3146            0 : Parser<ManagedTokenSource>::parse_right_shift_expr (
    3147              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3148              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3149              : {
    3150              :   // parse RHS (as tok has already been consumed in parse_expression)
    3151            0 :   auto right = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
    3152            0 :   if (!right)
    3153            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3154              : 
    3155              :   // TODO: check types. actually, do so during semantic analysis
    3156            0 :   location_t locus = left->get_locus ();
    3157              : 
    3158            0 :   return std::make_unique<AST::ArithmeticOrLogicalExpr> (
    3159            0 :     std::move (left), std::move (right.value ()),
    3160            0 :     ArithmeticOrLogicalOperator::RIGHT_SHIFT, locus);
    3161            0 : }
    3162              : 
    3163              : /* Returns the left binding power for the given ComparisonExpr type.
    3164              :  * TODO make constexpr? Would that even do anything useful? */
    3165              : inline binding_powers
    3166         2540 : get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type)
    3167              : {
    3168         2540 :   switch (expr_type)
    3169              :     {
    3170              :     case ComparisonOperator::EQUAL:
    3171              :       return LBP_EQUAL;
    3172              :     case ComparisonOperator::NOT_EQUAL:
    3173              :       return LBP_NOT_EQUAL;
    3174              :     case ComparisonOperator::GREATER_THAN:
    3175              :       return LBP_GREATER_THAN;
    3176              :     case ComparisonOperator::LESS_THAN:
    3177              :       return LBP_SMALLER_THAN;
    3178              :     case ComparisonOperator::GREATER_OR_EQUAL:
    3179              :       return LBP_GREATER_EQUAL;
    3180              :     case ComparisonOperator::LESS_OR_EQUAL:
    3181              :       return LBP_SMALLER_EQUAL;
    3182            0 :     default:
    3183              :       // WTF? should not happen, this is an error
    3184            0 :       rust_unreachable ();
    3185              : 
    3186              :       return LBP_EQUAL;
    3187              :     }
    3188              : }
    3189              : 
    3190              : /* Parses a ComparisonExpr of given type and LBP. TODO find a way to only
    3191              :  * specify one and have the other looked up - e.g. specify ExprType and
    3192              :  * binding power is looked up? */
    3193              : template <typename ManagedTokenSource>
    3194              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3195         2540 : Parser<ManagedTokenSource>::parse_comparison_expr (
    3196              :   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
    3197              :   AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions)
    3198              : {
    3199              :   // parse RHS (as tok has already been consumed in parse_expression)
    3200         2540 :   auto right = parse_expr (get_lbp_for_comparison_expr (expr_type),
    3201         5080 :                            AST::AttrVec (), restrictions);
    3202         2540 :   if (!right)
    3203            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3204              : 
    3205              :   // TODO: check types. actually, do so during semantic analysis
    3206         2540 :   location_t locus = left->get_locus ();
    3207              : 
    3208         2540 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3209         2540 :                                                 std::move (right.value ()),
    3210         2540 :                                                 expr_type, locus);
    3211         2540 : }
    3212              : 
    3213              : // Parses a binary equal to expression (with Pratt parsing).
    3214              : template <typename ManagedTokenSource>
    3215              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3216            0 : Parser<ManagedTokenSource>::parse_binary_equal_expr (
    3217              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3218              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3219              : {
    3220              :   // parse RHS (as tok has already been consumed in parse_expression)
    3221            0 :   auto right = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
    3222            0 :   if (!right)
    3223            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3224              : 
    3225              :   // TODO: check types. actually, do so during semantic analysis
    3226            0 :   location_t locus = left->get_locus ();
    3227              : 
    3228            0 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3229            0 :                                                 std::move (right.value ()),
    3230            0 :                                                 ComparisonOperator::EQUAL,
    3231            0 :                                                 locus);
    3232            0 : }
    3233              : 
    3234              : // Parses a binary not equal to expression (with Pratt parsing).
    3235              : template <typename ManagedTokenSource>
    3236              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3237            0 : Parser<ManagedTokenSource>::parse_binary_not_equal_expr (
    3238              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3239              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3240              : {
    3241              :   // parse RHS (as tok has already been consumed in parse_expression)
    3242            0 :   auto right = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
    3243            0 :   if (!right)
    3244            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3245              : 
    3246              :   // TODO: check types. actually, do so during semantic analysis
    3247            0 :   location_t locus = left->get_locus ();
    3248              : 
    3249            0 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3250            0 :                                                 std::move (right.value ()),
    3251            0 :                                                 ComparisonOperator::NOT_EQUAL,
    3252            0 :                                                 locus);
    3253            0 : }
    3254              : 
    3255              : // Parses a binary greater than expression (with Pratt parsing).
    3256              : template <typename ManagedTokenSource>
    3257              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3258            0 : Parser<ManagedTokenSource>::parse_binary_greater_than_expr (
    3259              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3260              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3261              : {
    3262              :   // parse RHS (as tok has already been consumed in parse_expression)
    3263            0 :   auto right = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
    3264            0 :   if (!right)
    3265            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3266              : 
    3267              :   // TODO: check types. actually, do so during semantic analysis
    3268            0 :   location_t locus = left->get_locus ();
    3269              : 
    3270            0 :   return std::make_unique<AST::ComparisonExpr> (
    3271            0 :     std::move (left), std::move (right.value ()),
    3272            0 :     ComparisonOperator::GREATER_THAN, locus);
    3273            0 : }
    3274              : 
    3275              : // Parses a binary less than expression (with Pratt parsing).
    3276              : template <typename ManagedTokenSource>
    3277              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3278            0 : Parser<ManagedTokenSource>::parse_binary_less_than_expr (
    3279              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3280              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3281              : {
    3282              :   // parse RHS (as tok has already been consumed in parse_expression)
    3283            0 :   auto right = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
    3284            0 :   if (!right)
    3285            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3286              : 
    3287              :   // TODO: check types. actually, do so during semantic analysis
    3288            0 :   location_t locus = left->get_locus ();
    3289              : 
    3290            0 :   return std::make_unique<AST::ComparisonExpr> (std::move (left),
    3291            0 :                                                 std::move (right.value ()),
    3292            0 :                                                 ComparisonOperator::LESS_THAN,
    3293            0 :                                                 locus);
    3294            0 : }
    3295              : 
    3296              : // Parses a binary greater than or equal to expression (with Pratt parsing).
    3297              : template <typename ManagedTokenSource>
    3298              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3299            0 : Parser<ManagedTokenSource>::parse_binary_greater_equal_expr (
    3300              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3301              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3302              : {
    3303              :   // parse RHS (as tok has already been consumed in parse_expression)
    3304            0 :   auto right = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
    3305            0 :   if (!right)
    3306            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3307              : 
    3308              :   // TODO: check types. actually, do so during semantic analysis
    3309            0 :   location_t locus = left->get_locus ();
    3310              : 
    3311            0 :   return std::make_unique<AST::ComparisonExpr> (
    3312            0 :     std::move (left), std::move (right.value ()),
    3313            0 :     ComparisonOperator::GREATER_OR_EQUAL, locus);
    3314            0 : }
    3315              : 
    3316              : // Parses a binary less than or equal to expression (with Pratt parsing).
    3317              : template <typename ManagedTokenSource>
    3318              : tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
    3319            0 : Parser<ManagedTokenSource>::parse_binary_less_equal_expr (
    3320              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3321              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3322              : {
    3323              :   // parse RHS (as tok has already been consumed in parse_expression)
    3324            0 :   auto right = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
    3325            0 :   if (!right)
    3326            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3327              : 
    3328              :   // TODO: check types. actually, do so during semantic analysis
    3329            0 :   location_t locus = left->get_locus ();
    3330              : 
    3331            0 :   return std::make_unique<AST::ComparisonExpr> (
    3332            0 :     std::move (left), std::move (right.value ()),
    3333            0 :     ComparisonOperator::LESS_OR_EQUAL, locus);
    3334            0 : }
    3335              : 
    3336              : // Parses a binary lazy boolean or expression (with Pratt parsing).
    3337              : template <typename ManagedTokenSource>
    3338              : tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
    3339           56 : Parser<ManagedTokenSource>::parse_lazy_or_expr (
    3340              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3341              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3342              : {
    3343              :   // parse RHS (as tok has already been consumed in parse_expression)
    3344           56 :   auto right = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
    3345           56 :   if (!right)
    3346            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3347              : 
    3348              :   // TODO: check types. actually, do so during semantic analysis
    3349           56 :   location_t locus = left->get_locus ();
    3350              : 
    3351           56 :   return std::make_unique<AST::LazyBooleanExpr> (
    3352           56 :     std::move (left), std::move (right.value ()),
    3353          112 :     LazyBooleanOperator::LOGICAL_OR, locus);
    3354           56 : }
    3355              : 
    3356              : // Parses a binary lazy boolean and expression (with Pratt parsing).
    3357              : template <typename ManagedTokenSource>
    3358              : tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
    3359          260 : Parser<ManagedTokenSource>::parse_lazy_and_expr (
    3360              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3361              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3362              : {
    3363              :   // parse RHS (as tok has already been consumed in parse_expression)
    3364          260 :   auto right = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
    3365          260 :   if (!right)
    3366            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3367              : 
    3368              :   // TODO: check types. actually, do so during semantic analysis
    3369          260 :   location_t locus = left->get_locus ();
    3370              : 
    3371          260 :   return std::make_unique<AST::LazyBooleanExpr> (
    3372          260 :     std::move (left), std::move (right.value ()),
    3373          520 :     LazyBooleanOperator::LOGICAL_AND, locus);
    3374          260 : }
    3375              : 
    3376              : // Parses a pseudo-binary infix type cast expression (with Pratt parsing).
    3377              : template <typename ManagedTokenSource>
    3378              : tl::expected<std::unique_ptr<AST::TypeCastExpr>, Parse::Error::Expr>
    3379         5160 : Parser<ManagedTokenSource>::parse_type_cast_expr (
    3380              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast,
    3381              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED,
    3382              :   ParseRestrictions restrictions ATTRIBUTE_UNUSED)
    3383              : {
    3384              :   // parse RHS (as tok has already been consumed in parse_expression)
    3385         5160 :   auto type = parse_type_no_bounds ();
    3386         5160 :   if (!type)
    3387            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3388              :   // FIXME: how do I get precedence put in here?
    3389              : 
    3390              :   // TODO: check types. actually, do so during semantic analysis
    3391         5160 :   location_t locus = expr_to_cast->get_locus ();
    3392              : 
    3393         5160 :   return std::make_unique<AST::TypeCastExpr> (std::move (expr_to_cast),
    3394         5160 :                                               std::move (type), locus);
    3395         5160 : }
    3396              : 
    3397              : // Parses a binary assignment expression (with Pratt parsing).
    3398              : template <typename ManagedTokenSource>
    3399              : tl::expected<std::unique_ptr<AST::AssignmentExpr>, Parse::Error::Expr>
    3400         2520 : Parser<ManagedTokenSource>::parse_assig_expr (
    3401              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3402              :   AST::AttrVec outer_attrs, ParseRestrictions restrictions)
    3403              : {
    3404              :   // parse RHS (as tok has already been consumed in parse_expression)
    3405         2520 :   auto right = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
    3406         2520 :   if (!right)
    3407            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3408              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3409              : 
    3410         2520 :   location_t locus = left->get_locus ();
    3411              : 
    3412         2520 :   return std::make_unique<AST::AssignmentExpr> (std::move (left),
    3413         2520 :                                                 std::move (right.value ()),
    3414         2520 :                                                 std::move (outer_attrs), locus);
    3415         2520 : }
    3416              : 
    3417              : /* Returns the left binding power for the given CompoundAssignmentExpr type.
    3418              :  * TODO make constexpr? Would that even do anything useful? */
    3419              : inline binding_powers
    3420          682 : get_lbp_for_compound_assignment_expr (
    3421              :   AST::CompoundAssignmentExpr::ExprType expr_type)
    3422              : {
    3423          682 :   switch (expr_type)
    3424              :     {
    3425              :     case CompoundAssignmentOperator::ADD:
    3426              :       return LBP_PLUS;
    3427              :     case CompoundAssignmentOperator::SUBTRACT:
    3428              :       return LBP_MINUS;
    3429              :     case CompoundAssignmentOperator::MULTIPLY:
    3430              :       return LBP_MUL;
    3431              :     case CompoundAssignmentOperator::DIVIDE:
    3432              :       return LBP_DIV;
    3433              :     case CompoundAssignmentOperator::MODULUS:
    3434              :       return LBP_MOD;
    3435              :     case CompoundAssignmentOperator::BITWISE_AND:
    3436              :       return LBP_AMP;
    3437              :     case CompoundAssignmentOperator::BITWISE_OR:
    3438              :       return LBP_PIPE;
    3439              :     case CompoundAssignmentOperator::BITWISE_XOR:
    3440              :       return LBP_CARET;
    3441              :     case CompoundAssignmentOperator::LEFT_SHIFT:
    3442              :       return LBP_L_SHIFT;
    3443              :     case CompoundAssignmentOperator::RIGHT_SHIFT:
    3444              :       return LBP_R_SHIFT;
    3445            0 :     default:
    3446              :       // WTF? should not happen, this is an error
    3447            0 :       rust_unreachable ();
    3448              : 
    3449              :       return LBP_PLUS;
    3450              :     }
    3451              : }
    3452              : 
    3453              : // Parses a compound assignment expression (with Pratt parsing).
    3454              : template <typename ManagedTokenSource>
    3455              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3456          682 : Parser<ManagedTokenSource>::parse_compound_assignment_expr (
    3457              :   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
    3458              :   AST::CompoundAssignmentExpr::ExprType expr_type,
    3459              :   ParseRestrictions restrictions)
    3460              : {
    3461              :   // parse RHS (as tok has already been consumed in parse_expression)
    3462          682 :   auto right = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
    3463         1364 :                            AST::AttrVec (), restrictions);
    3464          682 :   if (!right)
    3465            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3466              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3467              : 
    3468              :   // TODO: check types. actually, do so during semantic analysis
    3469          682 :   location_t locus = left->get_locus ();
    3470              : 
    3471          682 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3472         1364 :     std::move (left), std::move (right.value ()), expr_type, locus);
    3473          682 : }
    3474              : 
    3475              : // Parses a binary add-assignment expression (with Pratt parsing).
    3476              : template <typename ManagedTokenSource>
    3477              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3478            0 : Parser<ManagedTokenSource>::parse_plus_assig_expr (
    3479              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3480              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3481              : {
    3482              :   // parse RHS (as tok has already been consumed in parse_expression)
    3483            0 :   auto right = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
    3484            0 :   if (!right)
    3485            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3486              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3487              : 
    3488              :   // TODO: check types. actually, do so during semantic analysis
    3489            0 :   location_t locus = left->get_locus ();
    3490              : 
    3491            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3492            0 :     std::move (left), std::move (right.value ()),
    3493            0 :     CompoundAssignmentOperator::ADD, locus);
    3494            0 : }
    3495              : 
    3496              : // Parses a binary minus-assignment expression (with Pratt parsing).
    3497              : template <typename ManagedTokenSource>
    3498              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3499            0 : Parser<ManagedTokenSource>::parse_minus_assig_expr (
    3500              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3501              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3502              : {
    3503              :   // parse RHS (as tok has already been consumed in parse_expression)
    3504            0 :   auto right = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
    3505            0 :   if (!right)
    3506            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3507              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3508              : 
    3509              :   // TODO: check types. actually, do so during semantic analysis
    3510            0 :   location_t locus = left->get_locus ();
    3511              : 
    3512            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3513            0 :     std::move (left), std::move (right.value ()),
    3514            0 :     CompoundAssignmentOperator::SUBTRACT, locus);
    3515            0 : }
    3516              : 
    3517              : // Parses a binary multiplication-assignment expression (with Pratt parsing).
    3518              : template <typename ManagedTokenSource>
    3519              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3520            0 : Parser<ManagedTokenSource>::parse_mult_assig_expr (
    3521              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3522              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3523              : {
    3524              :   // parse RHS (as tok has already been consumed in parse_expression)
    3525            0 :   auto right = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
    3526            0 :   if (!right)
    3527            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3528              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3529              : 
    3530              :   // TODO: check types. actually, do so during semantic analysis
    3531            0 :   location_t locus = left->get_locus ();
    3532              : 
    3533            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3534            0 :     std::move (left), std::move (right.value ()),
    3535            0 :     CompoundAssignmentOperator::MULTIPLY, locus);
    3536            0 : }
    3537              : 
    3538              : // Parses a binary division-assignment expression (with Pratt parsing).
    3539              : template <typename ManagedTokenSource>
    3540              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3541            0 : Parser<ManagedTokenSource>::parse_div_assig_expr (
    3542              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3543              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3544              : {
    3545              :   // parse RHS (as tok has already been consumed in parse_expression)
    3546            0 :   auto right = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
    3547            0 :   if (!right)
    3548            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3549              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3550              : 
    3551              :   // TODO: check types. actually, do so during semantic analysis
    3552            0 :   location_t locus = left->get_locus ();
    3553              : 
    3554            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3555            0 :     std::move (left), std::move (right.value ()),
    3556            0 :     CompoundAssignmentOperator::DIVIDE, locus);
    3557            0 : }
    3558              : 
    3559              : // Parses a binary modulo-assignment expression (with Pratt parsing).
    3560              : template <typename ManagedTokenSource>
    3561              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3562            0 : Parser<ManagedTokenSource>::parse_mod_assig_expr (
    3563              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3564              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3565              : {
    3566              :   // parse RHS (as tok has already been consumed in parse_expression)
    3567            0 :   auto right = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
    3568            0 :   if (!right)
    3569            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3570              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3571              : 
    3572              :   // TODO: check types. actually, do so during semantic analysis
    3573            0 :   location_t locus = left->get_locus ();
    3574              : 
    3575            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3576            0 :     std::move (left), std::move (right.value ()),
    3577            0 :     CompoundAssignmentOperator::MODULUS, locus);
    3578            0 : }
    3579              : 
    3580              : // Parses a binary and-assignment expression (with Pratt parsing).
    3581              : template <typename ManagedTokenSource>
    3582              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3583            0 : Parser<ManagedTokenSource>::parse_and_assig_expr (
    3584              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3585              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3586              : {
    3587              :   // parse RHS (as tok has already been consumed in parse_expression)
    3588            0 :   auto right = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
    3589            0 :   if (!right)
    3590            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3591              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3592              : 
    3593              :   // TODO: check types. actually, do so during semantic analysis
    3594            0 :   location_t locus = left->get_locus ();
    3595              : 
    3596            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3597            0 :     std::move (left), std::move (right.value ()),
    3598            0 :     CompoundAssignmentOperator::BITWISE_AND, locus);
    3599            0 : }
    3600              : 
    3601              : // Parses a binary or-assignment expression (with Pratt parsing).
    3602              : template <typename ManagedTokenSource>
    3603              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3604            0 : Parser<ManagedTokenSource>::parse_or_assig_expr (
    3605              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3606              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3607              : {
    3608              :   // parse RHS (as tok has already been consumed in parse_expression)
    3609            0 :   auto right = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
    3610            0 :   if (!right)
    3611            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3612              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3613              : 
    3614              :   // TODO: check types. actually, do so during semantic analysis
    3615            0 :   location_t locus = left->get_locus ();
    3616              : 
    3617            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3618            0 :     std::move (left), std::move (right.value ()),
    3619            0 :     CompoundAssignmentOperator::BITWISE_OR, locus);
    3620            0 : }
    3621              : 
    3622              : // Parses a binary xor-assignment expression (with Pratt parsing).
    3623              : template <typename ManagedTokenSource>
    3624              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3625            0 : Parser<ManagedTokenSource>::parse_xor_assig_expr (
    3626              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3627              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3628              : {
    3629              :   // parse RHS (as tok has already been consumed in parse_expression)
    3630            0 :   auto right = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
    3631            0 :   if (!right)
    3632            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3633              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3634              : 
    3635              :   // TODO: check types. actually, do so during semantic analysis
    3636            0 :   location_t locus = left->get_locus ();
    3637              : 
    3638            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3639            0 :     std::move (left), std::move (right.value ()),
    3640            0 :     CompoundAssignmentOperator::BITWISE_XOR, locus);
    3641            0 : }
    3642              : 
    3643              : // Parses a binary left shift-assignment expression (with Pratt parsing).
    3644              : template <typename ManagedTokenSource>
    3645              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3646            0 : Parser<ManagedTokenSource>::parse_left_shift_assig_expr (
    3647              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3648              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3649              : {
    3650              :   // parse RHS (as tok has already been consumed in parse_expression)
    3651            0 :   auto right
    3652            0 :     = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
    3653            0 :   if (!right)
    3654            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3655              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3656              : 
    3657              :   // TODO: check types. actually, do so during semantic analysis
    3658            0 :   location_t locus = left->get_locus ();
    3659              : 
    3660            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3661            0 :     std::move (left), std::move (right.value ()),
    3662            0 :     CompoundAssignmentOperator::LEFT_SHIFT, locus);
    3663            0 : }
    3664              : 
    3665              : // Parses a binary right shift-assignment expression (with Pratt parsing).
    3666              : template <typename ManagedTokenSource>
    3667              : tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
    3668            0 : Parser<ManagedTokenSource>::parse_right_shift_assig_expr (
    3669              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3670              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3671              : {
    3672              :   // parse RHS (as tok has already been consumed in parse_expression)
    3673            0 :   auto right
    3674            0 :     = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
    3675            0 :   if (!right)
    3676            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3677              :   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
    3678              : 
    3679              :   // TODO: check types. actually, do so during semantic analysis
    3680            0 :   location_t locus = left->get_locus ();
    3681              : 
    3682            0 :   return std::make_unique<AST::CompoundAssignmentExpr> (
    3683            0 :     std::move (left), std::move (right.value ()),
    3684            0 :     CompoundAssignmentOperator::RIGHT_SHIFT, locus);
    3685            0 : }
    3686              : 
    3687              : // Parses a postfix unary await expression (with Pratt parsing).
    3688              : template <typename ManagedTokenSource>
    3689              : tl::expected<std::unique_ptr<AST::AwaitExpr>, Parse::Error::Expr>
    3690            0 : Parser<ManagedTokenSource>::parse_await_expr (
    3691              :   const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await,
    3692              :   AST::AttrVec outer_attrs)
    3693              : {
    3694              :   /* skip "await" identifier (as "." has already been consumed in
    3695              :    * parse_expression) this assumes that the identifier was already identified
    3696              :    * as await */
    3697            0 :   if (!skip_token (IDENTIFIER))
    3698              :     {
    3699            0 :       Error error (tok->get_locus (), "failed to skip %<await%> in await expr "
    3700              :                                       "- this is probably a deep issue");
    3701            0 :       add_error (std::move (error));
    3702              : 
    3703              :       // skip somewhere?
    3704            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3705            0 :     }
    3706              : 
    3707              :   // TODO: check inside async block in semantic analysis
    3708            0 :   location_t locus = expr_to_await->get_locus ();
    3709              : 
    3710            0 :   return std::unique_ptr<AST::AwaitExpr> (
    3711            0 :     new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs),
    3712            0 :                         locus));
    3713              : }
    3714              : 
    3715              : /* Parses an exclusive range ('..') in left denotation position (i.e.
    3716              :  * RangeFromExpr or RangeFromToExpr). */
    3717              : template <typename ManagedTokenSource>
    3718              : tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
    3719           78 : Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
    3720              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3721              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3722              : {
    3723              :   // FIXME: this probably parses expressions accidently or whatever
    3724              :   // try parsing RHS (as tok has already been consumed in parse_expression)
    3725              :   // Can be nullptr, in which case it is a RangeFromExpr, otherwise a
    3726              :   // RangeFromToExpr.
    3727           78 :   restrictions.expr_can_be_null = true;
    3728           78 :   auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
    3729              : 
    3730           78 :   location_t locus = left->get_locus ();
    3731              : 
    3732           78 :   if (!right)
    3733              :     {
    3734              :       // range from expr
    3735            9 :       return std::make_unique<AST::RangeFromExpr> (std::move (left), locus);
    3736              :     }
    3737              :   else
    3738              :     {
    3739           69 :       return std::make_unique<AST::RangeFromToExpr> (std::move (left),
    3740           69 :                                                      std::move (right.value ()),
    3741           69 :                                                      locus);
    3742              :     }
    3743              :   // FIXME: make non-associative
    3744           78 : }
    3745              : 
    3746              : /* Parses an exclusive range ('..') in null denotation position (i.e.
    3747              :  * RangeToExpr or RangeFullExpr). */
    3748              : template <typename ManagedTokenSource>
    3749              : tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
    3750            9 : Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr (
    3751              :   const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
    3752              : {
    3753            9 :   auto restrictions = ParseRestrictions ();
    3754            9 :   restrictions.expr_can_be_null = true;
    3755              : 
    3756              :   // FIXME: this probably parses expressions accidently or whatever
    3757              :   // try parsing RHS (as tok has already been consumed in parse_expression)
    3758            9 :   auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
    3759              : 
    3760            9 :   location_t locus = tok->get_locus ();
    3761              : 
    3762            9 :   if (!right)
    3763              :     {
    3764              :       // range from expr
    3765            1 :       return std::make_unique<AST::RangeFullExpr> (locus);
    3766              :     }
    3767              :   else
    3768              :     {
    3769            8 :       return std::make_unique<AST::RangeToExpr> (std::move (right.value ()),
    3770            8 :                                                  locus);
    3771              :     }
    3772              :   // FIXME: make non-associative
    3773            9 : }
    3774              : 
    3775              : // Parses a full binary range inclusive expression.
    3776              : template <typename ManagedTokenSource>
    3777              : tl::expected<std::unique_ptr<AST::RangeFromToInclExpr>, Parse::Error::Expr>
    3778            7 : Parser<ManagedTokenSource>::parse_range_inclusive_expr (
    3779              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
    3780              :   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
    3781              : {
    3782              :   // parse RHS (as tok has already been consumed in parse_expression)
    3783            7 :   auto right = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
    3784            7 :   if (!right)
    3785            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3786              :   // FIXME: make non-associative
    3787              : 
    3788              :   // TODO: check types. actually, do so during semantic analysis
    3789            7 :   location_t locus = left->get_locus ();
    3790              : 
    3791            7 :   return std::make_unique<AST::RangeFromToInclExpr> (std::move (left),
    3792            7 :                                                      std::move (right.value ()),
    3793            7 :                                                      locus);
    3794            7 : }
    3795              : 
    3796              : // Parses an inclusive range-to prefix unary expression.
    3797              : template <typename ManagedTokenSource>
    3798              : tl::expected<std::unique_ptr<AST::RangeToInclExpr>, Parse::Error::Expr>
    3799            0 : Parser<ManagedTokenSource>::parse_range_to_inclusive_expr (
    3800              :   const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
    3801              : {
    3802              :   // parse RHS (as tok has already been consumed in parse_expression)
    3803            0 :   auto right = parse_expr (LBP_DOT_DOT_EQ);
    3804            0 :   if (!right)
    3805            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3806              :   // FIXME: make non-associative
    3807              : 
    3808              :   // TODO: check types. actually, do so during semantic analysis
    3809              : 
    3810            0 :   return std::make_unique<AST::RangeToInclExpr> (std::move (right.value ()),
    3811            0 :                                                  tok->get_locus ());
    3812            0 : }
    3813              : 
    3814              : // Parses a pseudo-binary infix tuple index expression.
    3815              : template <typename ManagedTokenSource>
    3816              : tl::expected<std::unique_ptr<AST::TupleIndexExpr>, Parse::Error::Expr>
    3817          902 : Parser<ManagedTokenSource>::parse_tuple_index_expr (
    3818              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr,
    3819              :   AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
    3820              : {
    3821              :   // parse int literal (as token already skipped)
    3822          902 :   const_TokenPtr index_tok = expect_token (INT_LITERAL);
    3823          902 :   if (index_tok == nullptr)
    3824            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3825              : 
    3826         1804 :   std::string index = index_tok->get_str ();
    3827              : 
    3828              :   // convert to integer
    3829          902 :   if (!index_tok->is_pure_decimal ())
    3830              :     {
    3831           27 :       Error error (index_tok->get_locus (),
    3832              :                    "tuple index should be a pure decimal literal");
    3833           27 :       add_error (std::move (error));
    3834           27 :     }
    3835          902 :   int index_int = atoi (index.c_str ());
    3836              : 
    3837          902 :   location_t locus = tuple_expr->get_locus ();
    3838              : 
    3839          902 :   return std::make_unique<AST::TupleIndexExpr> (std::move (tuple_expr),
    3840              :                                                 index_int,
    3841          902 :                                                 std::move (outer_attrs), locus);
    3842          902 : }
    3843              : 
    3844              : // Parses a pseudo-binary infix array (or slice) index expression.
    3845              : template <typename ManagedTokenSource>
    3846              : tl::expected<std::unique_ptr<AST::ArrayIndexExpr>, Parse::Error::Expr>
    3847          254 : Parser<ManagedTokenSource>::parse_index_expr (
    3848              :   const_TokenPtr, std::unique_ptr<AST::Expr> array_expr,
    3849              :   AST::AttrVec outer_attrs, ParseRestrictions)
    3850              : {
    3851              :   // parse RHS (as tok has already been consumed in parse_expression)
    3852              :   /*std::unique_ptr<AST::Expr> index_expr
    3853              :     = parse_expr (LBP_ARRAY_REF, AST::AttrVec (),
    3854              :     restrictions);*/
    3855              :   // TODO: conceptually, should treat [] as brackets, so just parse all expr
    3856          254 :   auto index_expr = parse_expr ();
    3857          254 :   if (!index_expr)
    3858            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::CHILD_ERROR);
    3859              : 
    3860              :   // skip ']' at end of array
    3861          254 :   if (!skip_token (RIGHT_SQUARE))
    3862              :     {
    3863              :       // skip somewhere?
    3864            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3865              :     }
    3866              : 
    3867              :   // TODO: check types. actually, do so during semantic analysis
    3868          254 :   location_t locus = array_expr->get_locus ();
    3869              : 
    3870          254 :   return std::make_unique<AST::ArrayIndexExpr> (std::move (array_expr),
    3871          254 :                                                 std::move (index_expr.value ()),
    3872          254 :                                                 std::move (outer_attrs), locus);
    3873          254 : }
    3874              : 
    3875              : // Parses a pseudo-binary infix struct field access expression.
    3876              : template <typename ManagedTokenSource>
    3877              : tl::expected<std::unique_ptr<AST::FieldAccessExpr>, Parse::Error::Expr>
    3878         4863 : Parser<ManagedTokenSource>::parse_field_access_expr (
    3879              :   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr,
    3880              :   AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
    3881              : {
    3882              :   /* get field name identifier (assume that this is a field access expr and
    3883              :    * not await, for instance) */
    3884         4863 :   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
    3885         4863 :   if (ident_tok == nullptr)
    3886            0 :     return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3887              : 
    3888         9726 :   Identifier ident{ident_tok};
    3889              : 
    3890         4863 :   location_t locus = struct_expr->get_locus ();
    3891              : 
    3892              :   // TODO: check types. actually, do so during semantic analysis
    3893         4863 :   return std::make_unique<AST::FieldAccessExpr> (std::move (struct_expr),
    3894              :                                                  std::move (ident),
    3895              :                                                  std::move (outer_attrs),
    3896         4863 :                                                  locus);
    3897         4863 : }
    3898              : 
    3899              : // Parses a pseudo-binary infix method call expression.
    3900              : template <typename ManagedTokenSource>
    3901              : tl::expected<std::unique_ptr<AST::MethodCallExpr>, Parse::Error::Expr>
    3902         3020 : Parser<ManagedTokenSource>::parse_method_call_expr (
    3903              :   const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
    3904              :   AST::AttrVec outer_attrs, ParseRestrictions)
    3905              : {
    3906              :   // parse path expr segment
    3907         3020 :   AST::PathExprSegment segment = parse_path_expr_segment ();
    3908         3020 :   if (segment.is_error ())
    3909              :     {
    3910            0 :       Error error (tok->get_locus (),
    3911              :                    "failed to parse path expr segment of method call expr");
    3912            0 :       add_error (std::move (error));
    3913              : 
    3914              :       return tl::unexpected<Parse::Error::Expr> (
    3915            0 :         Parse::Error::Expr::CHILD_ERROR);
    3916            0 :     }
    3917              : 
    3918              :   // skip left parentheses
    3919         3020 :   if (!skip_token (LEFT_PAREN))
    3920              :     {
    3921            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3922              :     }
    3923              : 
    3924              :   // parse method params (if they exist)
    3925         3020 :   std::vector<std::unique_ptr<AST::Expr>> params;
    3926              : 
    3927         3020 :   const_TokenPtr t = lexer.peek_token ();
    3928         5058 :   while (t->get_id () != RIGHT_PAREN)
    3929              :     {
    3930         2038 :       auto param = parse_expr ();
    3931         2038 :       if (!param)
    3932              :         {
    3933            0 :           Error error (t->get_locus (),
    3934              :                        "failed to parse method param in method call");
    3935            0 :           add_error (std::move (error));
    3936              : 
    3937              :           return tl::unexpected<Parse::Error::Expr> (
    3938            0 :             Parse::Error::Expr::CHILD_ERROR);
    3939            0 :         }
    3940         2038 :       params.push_back (std::move (param.value ()));
    3941              : 
    3942         4076 :       if (lexer.peek_token ()->get_id () != COMMA)
    3943              :         break;
    3944              : 
    3945            0 :       lexer.skip_token ();
    3946            0 :       t = lexer.peek_token ();
    3947              :     }
    3948              : 
    3949              :   // skip right paren
    3950         3020 :   if (!skip_token (RIGHT_PAREN))
    3951              :     {
    3952            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    3953              :     }
    3954              : 
    3955              :   // TODO: check types. actually do so in semantic analysis pass.
    3956         3020 :   location_t locus = receiver_expr->get_locus ();
    3957              : 
    3958         3020 :   return std::make_unique<AST::MethodCallExpr> (std::move (receiver_expr),
    3959              :                                                 std::move (segment),
    3960              :                                                 std::move (params),
    3961         3020 :                                                 std::move (outer_attrs), locus);
    3962         3020 : }
    3963              : 
    3964              : // Parses a pseudo-binary infix function call expression.
    3965              : template <typename ManagedTokenSource>
    3966              : tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
    3967          269 : Parser<ManagedTokenSource>::parse_function_call_expr (
    3968              :   const_TokenPtr, std::unique_ptr<AST::Expr> function_expr,
    3969              :   AST::AttrVec outer_attrs, ParseRestrictions)
    3970              : {
    3971              :   // parse function params (if they exist)
    3972          269 :   std::vector<std::unique_ptr<AST::Expr>> params;
    3973              : 
    3974          269 :   const_TokenPtr t = lexer.peek_token ();
    3975          440 :   while (t->get_id () != RIGHT_PAREN)
    3976              :     {
    3977          171 :       auto param = parse_expr ();
    3978          171 :       if (!param)
    3979              :         {
    3980            0 :           Error error (t->get_locus (),
    3981              :                        "failed to parse function param in function call");
    3982            0 :           add_error (std::move (error));
    3983              : 
    3984              :           return tl::unexpected<Parse::Error::Expr> (
    3985            0 :             Parse::Error::Expr::CHILD_ERROR);
    3986            0 :         }
    3987          171 :       params.push_back (std::move (param.value ()));
    3988              : 
    3989          342 :       if (lexer.peek_token ()->get_id () != COMMA)
    3990              :         break;
    3991              : 
    3992           24 :       lexer.skip_token ();
    3993           24 :       t = lexer.peek_token ();
    3994              :     }
    3995              : 
    3996              :   // skip ')' at end of param list
    3997          269 :   if (!skip_token (RIGHT_PAREN))
    3998              :     {
    3999              :       // skip somewhere?
    4000            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4001              :     }
    4002              : 
    4003              :   // TODO: check types. actually, do so during semantic analysis
    4004          269 :   location_t locus = function_expr->get_locus ();
    4005              : 
    4006          269 :   return std::make_unique<AST::CallExpr> (std::move (function_expr),
    4007              :                                           std::move (params),
    4008          269 :                                           std::move (outer_attrs), locus);
    4009          269 : }
    4010              : 
    4011              : /* Parses a struct expr struct with a path in expression already parsed (but
    4012              :  * not
    4013              :  * '{' token). */
    4014              : template <typename ManagedTokenSource>
    4015              : tl::expected<std::unique_ptr<AST::StructExprStruct>, Parse::Error::Expr>
    4016         1371 : Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
    4017              :   AST::PathInExpression path, AST::AttrVec outer_attrs)
    4018              : {
    4019              :   // assume struct expr struct (as struct-enum disambiguation requires name
    4020              :   // lookup) again, make statement if final ';'
    4021         1371 :   if (!skip_token (LEFT_CURLY))
    4022              :     {
    4023            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4024              :     }
    4025              : 
    4026              :   // parse inner attributes
    4027         1371 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    4028              : 
    4029              :   // branch based on next token
    4030         1371 :   const_TokenPtr t = lexer.peek_token ();
    4031         1371 :   location_t path_locus = path.get_locus ();
    4032         1371 :   switch (t->get_id ())
    4033              :     {
    4034           78 :     case RIGHT_CURLY:
    4035              :       // struct with no body
    4036           78 :       lexer.skip_token ();
    4037              : 
    4038           78 :       return std::make_unique<AST::StructExprStruct> (std::move (path),
    4039              :                                                       std::move (inner_attrs),
    4040              :                                                       std::move (outer_attrs),
    4041           78 :                                                       path_locus);
    4042         1293 :     case DOT_DOT:
    4043              :       /* technically this would give a struct base-only struct, but this
    4044              :        * algorithm should work too. As such, AST type not happening. */
    4045              :     case IDENTIFIER:
    4046              :     case HASH:
    4047              :     case INT_LITERAL:
    4048              :       {
    4049              :         // struct with struct expr fields
    4050              : 
    4051              :         // parse struct expr fields
    4052         1293 :         std::vector<std::unique_ptr<AST::StructExprField>> fields;
    4053              : 
    4054         3534 :         while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
    4055              :           {
    4056         2241 :             auto field = parse_struct_expr_field ();
    4057         2241 :             if (!field
    4058         2241 :                 && field.error () != Parse::Error::StructExprField::STRUCT_BASE)
    4059              :               {
    4060            0 :                 Error error (t->get_locus (),
    4061              :                              "failed to parse struct (or enum) expr field");
    4062            0 :                 add_error (std::move (error));
    4063              : 
    4064              :                 return tl::unexpected<Parse::Error::Expr> (
    4065            0 :                   Parse::Error::Expr::CHILD_ERROR);
    4066            0 :               }
    4067              : 
    4068              :             // DEBUG:
    4069         2241 :             rust_debug ("struct/enum expr field validated to not be null");
    4070              : 
    4071         2241 :             fields.push_back (std::move (field.value ()));
    4072              : 
    4073              :             // DEBUG:
    4074         2241 :             rust_debug ("struct/enum expr field pushed back");
    4075              : 
    4076         4482 :             if (lexer.peek_token ()->get_id () != COMMA)
    4077              :               {
    4078              :                 // DEBUG:
    4079         1062 :                 rust_debug ("lack of comma detected in struct/enum expr "
    4080              :                             "fields - break");
    4081              :                 break;
    4082              :               }
    4083         1179 :             lexer.skip_token ();
    4084              : 
    4085              :             // DEBUG:
    4086         1179 :             rust_debug ("struct/enum expr fields comma skipped ");
    4087              : 
    4088         1179 :             t = lexer.peek_token ();
    4089              :           }
    4090              : 
    4091              :         // DEBUG:
    4092         1293 :         rust_debug ("struct/enum expr about to parse struct base ");
    4093              : 
    4094              :         // parse struct base if it exists
    4095              :         AST::StructBase struct_base = AST::StructBase::error ();
    4096         2586 :         if (lexer.peek_token ()->get_id () == DOT_DOT)
    4097              :           {
    4098           63 :             location_t dot_dot_location = lexer.peek_token ()->get_locus ();
    4099           63 :             lexer.skip_token ();
    4100              : 
    4101              :             // parse required struct base expr
    4102           63 :             auto base_expr = parse_expr ();
    4103           63 :             if (!base_expr)
    4104              :               {
    4105            0 :                 Error error (lexer.peek_token ()->get_locus (),
    4106              :                              "failed to parse struct base expression in struct "
    4107              :                              "expression");
    4108            0 :                 add_error (std::move (error));
    4109              : 
    4110              :                 return tl::unexpected<Parse::Error::Expr> (
    4111            0 :                   Parse::Error::Expr::CHILD_ERROR);
    4112            0 :               }
    4113              : 
    4114              :             // DEBUG:
    4115           63 :             rust_debug ("struct/enum expr - parsed and validated base expr");
    4116              : 
    4117          126 :             struct_base = AST::StructBase (std::move (base_expr.value ()),
    4118           63 :                                            dot_dot_location);
    4119              : 
    4120              :             // DEBUG:
    4121           63 :             rust_debug ("assigned struct base to new struct base ");
    4122           63 :           }
    4123              : 
    4124         1293 :         if (!skip_token (RIGHT_CURLY))
    4125              :           {
    4126              :             return tl::unexpected<Parse::Error::Expr> (
    4127            0 :               Parse::Error::Expr::MALFORMED);
    4128              :           }
    4129              : 
    4130              :         // DEBUG:
    4131         1293 :         rust_debug (
    4132              :           "struct/enum expr skipped right curly - done and ready to return");
    4133              : 
    4134         1293 :         return std::make_unique<AST::StructExprStructFields> (
    4135              :           std::move (path), std::move (fields), path_locus,
    4136              :           std::move (struct_base), std::move (inner_attrs),
    4137         1293 :           std::move (outer_attrs));
    4138         1293 :       }
    4139            0 :     default:
    4140            0 :       add_error (
    4141            0 :         Error (t->get_locus (),
    4142              :                "unrecognised token %qs in struct (or enum) expression - "
    4143              :                "expected %<}%>, identifier, integer literal, or %<..%>",
    4144              :                t->get_token_description ()));
    4145              : 
    4146            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4147              :     }
    4148         1371 : }
    4149              : 
    4150              : /* Parses a struct expr tuple with a path in expression already parsed (but
    4151              :  * not
    4152              :  * '(' token).
    4153              :  * FIXME: this currently outputs a call expr, as they cannot be disambiguated.
    4154              :  * A better solution would be to just get this to call that function directly.
    4155              :  * */
    4156              : template <typename ManagedTokenSource>
    4157              : tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
    4158        10506 : Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
    4159              :   AST::PathInExpression path, AST::AttrVec outer_attrs)
    4160              : {
    4161        10506 :   if (!skip_token (LEFT_PAREN))
    4162              :     {
    4163            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4164              :     }
    4165              : 
    4166        10506 :   AST::AttrVec inner_attrs = parse_inner_attributes ();
    4167              : 
    4168        10506 :   std::vector<std::unique_ptr<AST::Expr>> exprs;
    4169              : 
    4170        10506 :   const_TokenPtr t = lexer.peek_token ();
    4171        22961 :   while (t->get_id () != RIGHT_PAREN)
    4172              :     {
    4173              :       // parse expression (required)
    4174        12455 :       auto expr = parse_expr ();
    4175        12455 :       if (!expr)
    4176              :         {
    4177            0 :           Error error (t->get_locus (), "failed to parse expression in "
    4178              :                                         "struct (or enum) expression tuple");
    4179            0 :           add_error (std::move (error));
    4180              : 
    4181              :           return tl::unexpected<Parse::Error::Expr> (
    4182            0 :             Parse::Error::Expr::CHILD_ERROR);
    4183            0 :         }
    4184        12455 :       exprs.push_back (std::move (expr.value ()));
    4185              : 
    4186        24910 :       if (lexer.peek_token ()->get_id () != COMMA)
    4187              :         break;
    4188              : 
    4189         4001 :       lexer.skip_token ();
    4190              : 
    4191         4001 :       t = lexer.peek_token ();
    4192              :     }
    4193              : 
    4194        10506 :   if (!skip_token (RIGHT_PAREN))
    4195              :     {
    4196            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4197              :     }
    4198              : 
    4199        10506 :   location_t path_locus = path.get_locus ();
    4200              : 
    4201        10506 :   auto pathExpr = std::make_unique<AST::PathInExpression> (std::move (path));
    4202              : 
    4203        10506 :   return std::make_unique<AST::CallExpr> (std::move (pathExpr),
    4204              :                                           std::move (exprs),
    4205        10506 :                                           std::move (outer_attrs), path_locus);
    4206        21012 : }
    4207              : 
    4208              : // Parses a closure expression with pratt parsing (from null denotation).
    4209              : template <typename ManagedTokenSource>
    4210              : tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Expr>
    4211           73 : Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
    4212              :                                                       AST::AttrVec outer_attrs)
    4213              : {
    4214              :   // TODO: does this need pratt parsing (for precedence)? probably not, but
    4215              :   // idk
    4216           73 :   location_t locus = tok->get_locus ();
    4217           73 :   bool has_move = false;
    4218           73 :   if (tok->get_id () == MOVE)
    4219              :     {
    4220            1 :       has_move = true;
    4221            1 :       tok = lexer.peek_token ();
    4222            1 :       lexer.skip_token ();
    4223              :       // skip token and reassign
    4224              :     }
    4225              : 
    4226              :   // handle parameter list
    4227           73 :   std::vector<AST::ClosureParam> params;
    4228              : 
    4229           73 :   switch (tok->get_id ())
    4230              :     {
    4231              :     case OR:
    4232              :       // no parameters, don't skip token
    4233              :       break;
    4234           61 :     case PIPE:
    4235              :       {
    4236              :         // actually may have parameters
    4237              :         // don't skip token
    4238           61 :         const_TokenPtr t = lexer.peek_token ();
    4239          133 :         while (t->get_id () != PIPE)
    4240              :           {
    4241           72 :             AST::ClosureParam param = parse_closure_param ();
    4242           72 :             if (param.is_error ())
    4243              :               {
    4244              :                 // TODO is this really an error?
    4245            0 :                 Error error (t->get_locus (), "could not parse closure param");
    4246            0 :                 add_error (std::move (error));
    4247              : 
    4248              :                 return tl::unexpected<Parse::Error::Expr> (
    4249            0 :                   Parse::Error::Expr::CHILD_ERROR);
    4250            0 :               }
    4251           72 :             params.push_back (std::move (param));
    4252              : 
    4253          144 :             if (lexer.peek_token ()->get_id () != COMMA)
    4254              :               {
    4255          122 :                 if (lexer.peek_token ()->get_id () == OR)
    4256            1 :                   lexer.split_current_token (PIPE, PIPE);
    4257              :                 // not an error but means param list is done
    4258              :                 break;
    4259              :               }
    4260              :             // skip comma
    4261           11 :             lexer.skip_token ();
    4262              : 
    4263           22 :             if (lexer.peek_token ()->get_id () == OR)
    4264            0 :               lexer.split_current_token (PIPE, PIPE);
    4265              : 
    4266           11 :             t = lexer.peek_token ();
    4267              :           }
    4268              : 
    4269           61 :         if (!skip_token (PIPE))
    4270              :           {
    4271              :             return tl::unexpected<Parse::Error::Expr> (
    4272            0 :               Parse::Error::Expr::MALFORMED);
    4273              :           }
    4274              :         break;
    4275           61 :       }
    4276            0 :     default:
    4277            0 :       add_error (Error (tok->get_locus (),
    4278              :                         "unexpected token %qs in closure expression - expected "
    4279              :                         "%<|%> or %<||%>",
    4280              :                         tok->get_token_description ()));
    4281              : 
    4282              :       // skip somewhere?
    4283            0 :       return tl::unexpected<Parse::Error::Expr> (Parse::Error::Expr::MALFORMED);
    4284              :     }
    4285              : 
    4286              :   // again branch based on next token
    4287           73 :   tok = lexer.peek_token ();
    4288           73 :   if (tok->get_id () == RETURN_TYPE)
    4289              :     {
    4290              :       // must be return type closure with block expr
    4291              : 
    4292              :       // skip "return type" token
    4293           31 :       lexer.skip_token ();
    4294              : 
    4295              :       // parse actual type, which is required
    4296           31 :       auto type = parse_type_no_bounds ();
    4297           31 :       if (!type)
    4298              :         {
    4299              :           // error
    4300            0 :           Error error (tok->get_locus (), "failed to parse type for closure");
    4301            0 :           add_error (std::move (error));
    4302              : 
    4303              :           // skip somewhere?
    4304              :           return tl::unexpected<Parse::Error::Expr> (
    4305            0 :             Parse::Error::Expr::CHILD_ERROR);
    4306            0 :         }
    4307              : 
    4308              :       // parse block expr, which is required
    4309           31 :       auto block = parse_block_expr ();
    4310           31 :       if (!block)
    4311              :         {
    4312              :           // error
    4313            0 :           Error error (lexer.peek_token ()->get_locus (),
    4314              :                        "failed to parse block expr in closure");
    4315            0 :           add_error (std::move (error));
    4316              : 
    4317              :           // skip somewhere?
    4318              :           return tl::unexpected<Parse::Error::Expr> (
    4319            0 :             Parse::Error::Expr::CHILD_ERROR);
    4320            0 :         }
    4321              : 
    4322           31 :       return std::make_unique<AST::ClosureExprInnerTyped> (
    4323           31 :         std::move (type), std::move (block.value ()), std::move (params), locus,
    4324           31 :         has_move, std::move (outer_attrs));
    4325           31 :     }
    4326              :   else
    4327              :     {
    4328              :       // must be expr-only closure
    4329              : 
    4330              :       // parse expr, which is required
    4331           42 :       auto expr = parse_expr ();
    4332           42 :       if (!expr)
    4333              :         {
    4334            0 :           Error error (tok->get_locus (),
    4335              :                        "failed to parse expression in closure");
    4336            0 :           add_error (std::move (error));
    4337              : 
    4338              :           // skip somewhere?
    4339              :           return tl::unexpected<Parse::Error::Expr> (
    4340            0 :             Parse::Error::Expr::CHILD_ERROR);
    4341            0 :         }
    4342              : 
    4343           42 :       return std::make_unique<AST::ClosureExprInner> (std::move (expr.value ()),
    4344              :                                                       std::move (params), locus,
    4345              :                                                       has_move,
    4346           42 :                                                       std::move (outer_attrs));
    4347           42 :     }
    4348           73 : }
    4349              : 
    4350              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.