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