| // Copyright (C) 2020-2023 Free Software Foundation, Inc. | |
| // This file is part of GCC. | |
| // GCC is free software; you can redistribute it and/or modify it under | |
| // the terms of the GNU General Public License as published by the Free | |
| // Software Foundation; either version 3, or (at your option) any later | |
| // version. | |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| // for more details. | |
| // You should have received a copy of the GNU General Public License | |
| // along with GCC; see the file COPYING3. If not see | |
| // <http://www.gnu.org/licenses/>. | |
| /* Template implementation for Rust::Parser. Previously in rust-parse.cc (before | |
| * Parser was template). Separated from rust-parse.h for readability. */ | |
| /* DO NOT INCLUDE ANYWHERE - this is automatically included with rust-parse.h | |
| * This is also the reason why there are no include guards. */ | |
| #define INCLUDE_ALGORITHM | |
| #include "rust-diagnostics.h" | |
| #include "rust-make-unique.h" | |
| namespace Rust { | |
| // Left binding powers of operations. | |
| enum binding_powers | |
| { | |
| // Highest priority | |
| LBP_HIGHEST = 100, | |
| LBP_PATH = 95, | |
| LBP_METHOD_CALL = 90, | |
| LBP_FIELD_EXPR = 85, | |
| LBP_FUNCTION_CALL = 80, | |
| LBP_ARRAY_REF = LBP_FUNCTION_CALL, | |
| LBP_QUESTION_MARK = 75, // unary postfix - counts as left | |
| LBP_UNARY_PLUS = 70, // Used only when the null denotation is + | |
| LBP_UNARY_MINUS = LBP_UNARY_PLUS, // Used only when the null denotation is - | |
| LBP_UNARY_ASTERISK = LBP_UNARY_PLUS, // deref operator - unary prefix | |
| LBP_UNARY_EXCLAM = LBP_UNARY_PLUS, | |
| LBP_UNARY_AMP = LBP_UNARY_PLUS, | |
| LBP_UNARY_AMP_MUT = LBP_UNARY_PLUS, | |
| LBP_AS = 65, | |
| LBP_MUL = 60, | |
| LBP_DIV = LBP_MUL, | |
| LBP_MOD = LBP_MUL, | |
| LBP_PLUS = 55, | |
| LBP_MINUS = LBP_PLUS, | |
| LBP_L_SHIFT = 50, | |
| LBP_R_SHIFT = LBP_L_SHIFT, | |
| LBP_AMP = 45, | |
| LBP_CARET = 40, | |
| LBP_PIPE = 35, | |
| LBP_EQUAL = 30, | |
| LBP_NOT_EQUAL = LBP_EQUAL, | |
| LBP_SMALLER_THAN = LBP_EQUAL, | |
| LBP_SMALLER_EQUAL = LBP_EQUAL, | |
| LBP_GREATER_THAN = LBP_EQUAL, | |
| LBP_GREATER_EQUAL = LBP_EQUAL, | |
| LBP_LOGICAL_AND = 25, | |
| LBP_LOGICAL_OR = 20, | |
| LBP_DOT_DOT = 15, | |
| LBP_DOT_DOT_EQ = LBP_DOT_DOT, | |
| // TODO: note all these assig operators are RIGHT associative! | |
| LBP_ASSIG = 10, | |
| LBP_PLUS_ASSIG = LBP_ASSIG, | |
| LBP_MINUS_ASSIG = LBP_ASSIG, | |
| LBP_MULT_ASSIG = LBP_ASSIG, | |
| LBP_DIV_ASSIG = LBP_ASSIG, | |
| LBP_MOD_ASSIG = LBP_ASSIG, | |
| LBP_AMP_ASSIG = LBP_ASSIG, | |
| LBP_PIPE_ASSIG = LBP_ASSIG, | |
| LBP_CARET_ASSIG = LBP_ASSIG, | |
| LBP_L_SHIFT_ASSIG = LBP_ASSIG, | |
| LBP_R_SHIFT_ASSIG = LBP_ASSIG, | |
| // return, break, and closures as lowest priority? | |
| LBP_RETURN = 5, | |
| LBP_BREAK = LBP_RETURN, | |
| LBP_CLOSURE = LBP_RETURN, // unary prefix operators | |
| #if 0 | |
| // rust precedences | |
| // used for closures | |
| PREC_CLOSURE = -40, | |
| // used for break, continue, return, and yield | |
| PREC_JUMP = -30, | |
| // used for range (although weird comment in rustc about this) | |
| PREC_RANGE = -10, | |
| // used for binary operators mentioned below - also cast, colon (type), | |
| // assign, assign_op | |
| PREC_BINOP = FROM_ASSOC_OP, | |
| // used for box, address_of, let, unary (again, weird comment on let) | |
| PREC_PREFIX = 50, | |
| // used for await, call, method call, field, index, try, | |
| // inline asm, macro invocation | |
| PREC_POSTFIX = 60, | |
| // used for array, repeat, tuple, literal, path, paren, if, | |
| // while, for, 'loop', match, block, try block, async, struct | |
| PREC_PAREN = 99, | |
| PREC_FORCE_PAREN = 100, | |
| #endif | |
| // lowest priority | |
| LBP_LOWEST = 0 | |
| }; | |
| /* Returns whether the token can start a type (i.e. there is a valid type | |
| * beginning with the token). */ | |
| inline bool | |
| can_tok_start_type (TokenId id) | |
| { | |
| switch (id) | |
| { | |
| case EXCLAM: | |
| case LEFT_SQUARE: | |
| case LEFT_ANGLE: | |
| case UNDERSCORE: | |
| case ASTERISK: | |
| case AMP: | |
| case LIFETIME: | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| case SCOPE_RESOLUTION: | |
| case LEFT_PAREN: | |
| case FOR: | |
| case ASYNC: | |
| case CONST: | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| case IMPL: | |
| case DYN: | |
| case QUESTION_MARK: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| /* Returns whether the token id is (or is likely to be) a right angle bracket. | |
| * i.e. '>', '>>', '>=' and '>>=' tokens. */ | |
| inline bool | |
| is_right_angle_tok (TokenId id) | |
| { | |
| switch (id) | |
| { | |
| case RIGHT_ANGLE: | |
| case RIGHT_SHIFT: | |
| case GREATER_OR_EQUAL: | |
| case RIGHT_SHIFT_EQ: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| /* HACK-y special handling for skipping a right angle token at the end of | |
| * generic arguments. | |
| * Currently, this replaces the "current token" with one that is identical | |
| * except has the leading '>' removed (e.g. '>>' becomes '>'). This is bad | |
| * for several reasons - it modifies the token stream to something that | |
| * actually doesn't make syntactic sense, it may not worked if the token | |
| * has already been skipped, etc. It was done because it would not | |
| * actually require inserting new items into the token stream (which I | |
| * thought would take more work to not mess up) and because I wasn't sure | |
| * if the "already seen right angle" flag in the parser would work | |
| * correctly. | |
| * Those two other approaches listed are in my opinion actually better | |
| * long-term - insertion is probably best as it reflects syntactically | |
| * what occurs. On the other hand, I need to do a code audit to make sure | |
| * that insertion doesn't mess anything up. So that's a FIXME. */ | |
| template <typename ManagedTokenSource> | |
| bool | |
| Parser<ManagedTokenSource>::skip_generics_right_angle () | |
| { | |
| /* OK, new great idea. Have a lexer method called | |
| * "split_current_token(TokenType newLeft, TokenType newRight)", which is | |
| * called here with whatever arguments are appropriate. That lexer method | |
| * handles "replacing" the current token with the "newLeft" and "inserting" | |
| * the next token with the "newRight" (and creating a location, etc. for it) | |
| */ | |
| /* HACK: special handling for right shift '>>', greater or equal '>=', and | |
| * right shift assig */ | |
| // '>>=' | |
| const_TokenPtr tok = lexer.peek_token (); | |
| switch (tok->get_id ()) | |
| { | |
| case RIGHT_ANGLE: | |
| // this is good - skip token | |
| lexer.skip_token (); | |
| return true; | |
| case RIGHT_SHIFT: { | |
| // new implementation that should be better | |
| lexer.split_current_token (RIGHT_ANGLE, RIGHT_ANGLE); | |
| lexer.skip_token (); | |
| return true; | |
| } | |
| case GREATER_OR_EQUAL: { | |
| // new implementation that should be better | |
| lexer.split_current_token (RIGHT_ANGLE, EQUAL); | |
| lexer.skip_token (); | |
| return true; | |
| } | |
| case RIGHT_SHIFT_EQ: { | |
| // new implementation that should be better | |
| lexer.split_current_token (RIGHT_ANGLE, GREATER_OR_EQUAL); | |
| lexer.skip_token (); | |
| return true; | |
| } | |
| default: | |
| add_error (Error (tok->get_locus (), | |
| "expected %<>%> at end of generic argument - found %qs", | |
| tok->get_token_description ())); | |
| return false; | |
| } | |
| } | |
| /* Gets left binding power for specified token. | |
| * Not suitable for use at the moment or possibly ever because binding power | |
| * cannot be purely determined from operator token with Rust grammar - e.g. | |
| * method call and field access have | |
| * different left binding powers but the same operator token. */ | |
| template <typename ManagedTokenSource> | |
| int | |
| Parser<ManagedTokenSource>::left_binding_power (const_TokenPtr token) | |
| { | |
| // HACK: called with "peek_token()", so lookahead is "peek_token(1)" | |
| switch (token->get_id ()) | |
| { | |
| /* TODO: issue here - distinguish between method calls and field access | |
| * somehow? Also would have to distinguish between paths and function | |
| * calls (:: operator), maybe more stuff. */ | |
| /* Current plan for tackling LBP - don't do it based on token, use | |
| * lookahead. Or alternatively, only use Pratt parsing for OperatorExpr | |
| * and handle other expressions without it. rustc only considers | |
| * arithmetic, logical/relational, 'as', | |
| * '?=', ranges, colons, and assignment to have operator precedence and | |
| * associativity rules applicable. It then has | |
| * a separate "ExprPrecedence" that also includes binary operators. */ | |
| // TODO: handle operator overloading - have a function replace the | |
| // operator? | |
| /*case DOT: | |
| return LBP_DOT;*/ | |
| case SCOPE_RESOLUTION: | |
| rust_debug ( | |
| "possible error - looked up LBP of scope resolution operator. should " | |
| "be handled elsewhere."); | |
| return LBP_PATH; | |
| /* Resolved by lookahead HACK that should work with current code. If next | |
| * token is identifier and token after that isn't parenthesised expression | |
| * list, it is a field reference. */ | |
| case DOT: | |
| if (lexer.peek_token (1)->get_id () == IDENTIFIER | |
| && lexer.peek_token (2)->get_id () != LEFT_PAREN) | |
| { | |
| return LBP_FIELD_EXPR; | |
| } | |
| return LBP_METHOD_CALL; | |
| case LEFT_PAREN: | |
| return LBP_FUNCTION_CALL; | |
| case LEFT_SQUARE: | |
| return LBP_ARRAY_REF; | |
| // postfix question mark (i.e. error propagation expression) | |
| case QUESTION_MARK: | |
| return LBP_QUESTION_MARK; | |
| case AS: | |
| return LBP_AS; | |
| case ASTERISK: | |
| return LBP_MUL; | |
| case DIV: | |
| return LBP_DIV; | |
| case PERCENT: | |
| return LBP_MOD; | |
| case PLUS: | |
| return LBP_PLUS; | |
| case MINUS: | |
| return LBP_MINUS; | |
| case LEFT_SHIFT: | |
| return LBP_L_SHIFT; | |
| case RIGHT_SHIFT: | |
| return LBP_R_SHIFT; | |
| // binary & operator | |
| case AMP: | |
| return LBP_AMP; | |
| // binary ^ operator | |
| case CARET: | |
| return LBP_CARET; | |
| // binary | operator | |
| case PIPE: | |
| return LBP_PIPE; | |
| case EQUAL_EQUAL: | |
| return LBP_EQUAL; | |
| case NOT_EQUAL: | |
| return LBP_NOT_EQUAL; | |
| case RIGHT_ANGLE: | |
| return LBP_GREATER_THAN; | |
| case GREATER_OR_EQUAL: | |
| return LBP_GREATER_EQUAL; | |
| case LEFT_ANGLE: | |
| return LBP_SMALLER_THAN; | |
| case LESS_OR_EQUAL: | |
| return LBP_SMALLER_EQUAL; | |
| case LOGICAL_AND: | |
| return LBP_LOGICAL_AND; | |
| case OR: | |
| return LBP_LOGICAL_OR; | |
| case DOT_DOT: | |
| return LBP_DOT_DOT; | |
| case DOT_DOT_EQ: | |
| return LBP_DOT_DOT_EQ; | |
| case EQUAL: | |
| return LBP_ASSIG; | |
| case PLUS_EQ: | |
| return LBP_PLUS_ASSIG; | |
| case MINUS_EQ: | |
| return LBP_MINUS_ASSIG; | |
| case ASTERISK_EQ: | |
| return LBP_MULT_ASSIG; | |
| case DIV_EQ: | |
| return LBP_DIV_ASSIG; | |
| case PERCENT_EQ: | |
| return LBP_MOD_ASSIG; | |
| case AMP_EQ: | |
| return LBP_AMP_ASSIG; | |
| case PIPE_EQ: | |
| return LBP_PIPE_ASSIG; | |
| case CARET_EQ: | |
| return LBP_CARET_ASSIG; | |
| case LEFT_SHIFT_EQ: | |
| return LBP_L_SHIFT_ASSIG; | |
| case RIGHT_SHIFT_EQ: | |
| return LBP_R_SHIFT_ASSIG; | |
| /* HACK: float literal due to lexer misidentifying a dot then an integer as | |
| * a float */ | |
| case FLOAT_LITERAL: | |
| return LBP_FIELD_EXPR; | |
| // field expr is same as tuple expr in precedence, i imagine | |
| // TODO: is this needed anymore? lexer shouldn't do that anymore | |
| // anything that can't appear in an infix position is given lowest priority | |
| default: | |
| return LBP_LOWEST; | |
| } | |
| } | |
| // Returns true when current token is EOF. | |
| template <typename ManagedTokenSource> | |
| bool | |
| Parser<ManagedTokenSource>::done_end_of_file () | |
| { | |
| return lexer.peek_token ()->get_id () == END_OF_FILE; | |
| } | |
| // Parses a sequence of items within a module or the implicit top-level module | |
| // in a crate | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::Item>> | |
| Parser<ManagedTokenSource>::parse_items () | |
| { | |
| std::vector<std::unique_ptr<AST::Item>> items; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != END_OF_FILE) | |
| { | |
| std::unique_ptr<AST::Item> item = parse_item (false); | |
| if (item == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse item in crate"); | |
| add_error (std::move (error)); | |
| // TODO: should all items be cleared? | |
| items = std::vector<std::unique_ptr<AST::Item>> (); | |
| break; | |
| } | |
| items.push_back (std::move (item)); | |
| t = lexer.peek_token (); | |
| } | |
| return items; | |
| } | |
| // Parses a crate (compilation unit) - entry point | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Crate> | |
| Parser<ManagedTokenSource>::parse_crate () | |
| { | |
| // parse inner attributes | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse items | |
| std::vector<std::unique_ptr<AST::Item>> items = parse_items (); | |
| // emit all errors | |
| for (const auto &error : error_table) | |
| error.emit_error (); | |
| return std::unique_ptr<AST::Crate> ( | |
| new AST::Crate (std::move (items), std::move (inner_attrs))); | |
| } | |
| // Parse a contiguous block of inner attributes. | |
| template <typename ManagedTokenSource> | |
| AST::AttrVec | |
| Parser<ManagedTokenSource>::parse_inner_attributes () | |
| { | |
| AST::AttrVec inner_attributes; | |
| // only try to parse it if it starts with "#!" not only "#" | |
| while ((lexer.peek_token ()->get_id () == HASH | |
| && lexer.peek_token (1)->get_id () == EXCLAM) | |
| || lexer.peek_token ()->get_id () == INNER_DOC_COMMENT) | |
| { | |
| AST::Attribute inner_attr = parse_inner_attribute (); | |
| /* Ensure only valid inner attributes are added to the inner_attributes | |
| * list */ | |
| if (!inner_attr.is_empty ()) | |
| { | |
| inner_attributes.push_back (std::move (inner_attr)); | |
| } | |
| else | |
| { | |
| /* If no more valid inner attributes, break out of loop (only | |
| * contiguous inner attributes parsed). */ | |
| break; | |
| } | |
| } | |
| inner_attributes.shrink_to_fit (); | |
| return inner_attributes; | |
| } | |
| // Parse a inner or outer doc comment into an doc attribute | |
| template <typename ManagedTokenSource> | |
| AST::Attribute | |
| Parser<ManagedTokenSource>::parse_doc_comment () | |
| { | |
| const_TokenPtr token = lexer.peek_token (); | |
| Location locus = token->get_locus (); | |
| AST::SimplePathSegment segment ("doc", locus); | |
| std::vector<AST::SimplePathSegment> segments; | |
| segments.push_back (std::move (segment)); | |
| AST::SimplePath attr_path (std::move (segments), false, locus); | |
| AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING, | |
| PrimitiveCoreType::CORETYPE_STR, {}, locus); | |
| std::unique_ptr<AST::AttrInput> attr_input ( | |
| new AST::AttrInputLiteral (std::move (lit_expr))); | |
| lexer.skip_token (); | |
| return AST::Attribute (std::move (attr_path), std::move (attr_input), locus); | |
| } | |
| // Parse a single inner attribute. | |
| template <typename ManagedTokenSource> | |
| AST::Attribute | |
| Parser<ManagedTokenSource>::parse_inner_attribute () | |
| { | |
| if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT) | |
| return parse_doc_comment (); | |
| if (lexer.peek_token ()->get_id () != HASH) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "BUG: token %<#%> is missing, but %<parse_inner_attribute%> " | |
| "was invoked"); | |
| add_error (std::move (error)); | |
| return AST::Attribute::create_empty (); | |
| } | |
| lexer.skip_token (); | |
| if (lexer.peek_token ()->get_id () != EXCLAM) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "expected %<!%> or %<[%> for inner attribute"); | |
| add_error (std::move (error)); | |
| return AST::Attribute::create_empty (); | |
| } | |
| lexer.skip_token (); | |
| if (!skip_token (LEFT_SQUARE)) | |
| return AST::Attribute::create_empty (); | |
| AST::Attribute actual_attribute = parse_attribute_body (); | |
| if (!skip_token (RIGHT_SQUARE)) | |
| return AST::Attribute::create_empty (); | |
| return actual_attribute; | |
| } | |
| // Parses the body of an attribute (inner or outer). | |
| template <typename ManagedTokenSource> | |
| AST::Attribute | |
| Parser<ManagedTokenSource>::parse_attribute_body () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| AST::SimplePath attr_path = parse_simple_path (); | |
| // ensure path is valid to parse attribute input | |
| if (attr_path.is_empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "empty simple path in attribute"); | |
| add_error (std::move (error)); | |
| // Skip past potential further info in attribute (i.e. attr_input) | |
| skip_after_end_attribute (); | |
| return AST::Attribute::create_empty (); | |
| } | |
| std::unique_ptr<AST::AttrInput> attr_input = parse_attr_input (); | |
| // AttrInput is allowed to be null, so no checks here | |
| return AST::Attribute (std::move (attr_path), std::move (attr_input), locus); | |
| } | |
| /* Determines whether token is a valid simple path segment. This does not | |
| * include scope resolution operators. */ | |
| inline bool | |
| is_simple_path_segment (TokenId id) | |
| { | |
| switch (id) | |
| { | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case CRATE: | |
| return true; | |
| case DOLLAR_SIGN: | |
| // assume that dollar sign leads to $crate | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| // Parses a SimplePath AST node, if it exists. Does nothing otherwise. | |
| template <typename ManagedTokenSource> | |
| AST::SimplePath | |
| Parser<ManagedTokenSource>::parse_simple_path () | |
| { | |
| bool has_opening_scope_resolution = false; | |
| Location locus = Linemap::unknown_location (); | |
| // don't parse anything if not a path upfront | |
| if (!is_simple_path_segment (lexer.peek_token ()->get_id ()) | |
| && !is_simple_path_segment (lexer.peek_token (1)->get_id ())) | |
| return AST::SimplePath::create_empty (); | |
| /* Checks for opening scope resolution (i.e. global scope fully-qualified | |
| * path) */ | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| has_opening_scope_resolution = true; | |
| locus = lexer.peek_token ()->get_locus (); | |
| lexer.skip_token (); | |
| } | |
| // Parse single required simple path segment | |
| AST::SimplePathSegment segment = parse_simple_path_segment (); | |
| // get location if not gotten already | |
| if (locus == Linemap::unknown_location ()) | |
| locus = segment.get_locus (); | |
| std::vector<AST::SimplePathSegment> segments; | |
| // Return empty vector if first, actually required segment is an error | |
| if (segment.is_error ()) | |
| return AST::SimplePath::create_empty (); | |
| segments.push_back (std::move (segment)); | |
| // Parse all other simple path segments | |
| while (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // Skip scope resolution operator | |
| lexer.skip_token (); | |
| AST::SimplePathSegment new_segment = parse_simple_path_segment (); | |
| // Return path as currently constructed if segment in error state. | |
| if (new_segment.is_error ()) | |
| break; | |
| segments.push_back (std::move (new_segment)); | |
| } | |
| // DEBUG: check for any empty segments | |
| for (const auto &seg : segments) | |
| { | |
| if (seg.is_error ()) | |
| { | |
| rust_debug ( | |
| "when parsing simple path, somehow empty path segment was " | |
| "not filtered out. Path begins with '%s'", | |
| segments.at (0).as_string ().c_str ()); | |
| } | |
| } | |
| return AST::SimplePath (std::move (segments), has_opening_scope_resolution, | |
| locus); | |
| /* TODO: now that is_simple_path_segment exists, could probably start | |
| * actually making errors upon parse failure of segments and whatever */ | |
| } | |
| /* Parses a single SimplePathSegment (does not handle the scope resolution | |
| * operators) */ | |
| template <typename ManagedTokenSource> | |
| AST::SimplePathSegment | |
| Parser<ManagedTokenSource>::parse_simple_path_segment () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| lexer.skip_token (); | |
| return AST::SimplePathSegment (t->get_str (), t->get_locus ()); | |
| case SUPER: | |
| lexer.skip_token (); | |
| return AST::SimplePathSegment ("super", t->get_locus ()); | |
| case SELF: | |
| lexer.skip_token (); | |
| return AST::SimplePathSegment ("self", t->get_locus ()); | |
| case CRATE: | |
| lexer.skip_token (); | |
| return AST::SimplePathSegment ("crate", t->get_locus ()); | |
| case DOLLAR_SIGN: | |
| if (lexer.peek_token (1)->get_id () == CRATE) | |
| { | |
| lexer.skip_token (1); | |
| return AST::SimplePathSegment ("$crate", t->get_locus ()); | |
| } | |
| gcc_fallthrough (); | |
| default: | |
| // do nothing but inactivates warning from gcc when compiling | |
| /* could put the rust_error_at thing here but fallthrough (from failing | |
| * $crate condition) isn't completely obvious if it is. */ | |
| // test prevent error | |
| return AST::SimplePathSegment::create_error (); | |
| } | |
| gcc_unreachable (); | |
| /*rust_error_at( | |
| t->get_locus(), "invalid token '%s' in simple path segment", | |
| t->get_token_description());*/ | |
| // this is not necessarily an error, e.g. end of path | |
| // return AST::SimplePathSegment::create_error(); | |
| } | |
| // Parses a PathIdentSegment - an identifier segment of a non-SimplePath path. | |
| template <typename ManagedTokenSource> | |
| AST::PathIdentSegment | |
| Parser<ManagedTokenSource>::parse_path_ident_segment () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment (t->get_str (), t->get_locus ()); | |
| case SUPER: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment ("super", t->get_locus ()); | |
| case SELF: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment ("self", t->get_locus ()); | |
| case SELF_ALIAS: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment ("Self", t->get_locus ()); | |
| case CRATE: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment ("crate", t->get_locus ()); | |
| case DOLLAR_SIGN: | |
| if (lexer.peek_token (1)->get_id () == CRATE) | |
| { | |
| lexer.skip_token (1); | |
| return AST::PathIdentSegment ("$crate", t->get_locus ()); | |
| } | |
| gcc_fallthrough (); | |
| default: | |
| /* do nothing but inactivates warning from gcc when compiling | |
| * could put the error_at thing here but fallthrough (from failing $crate | |
| * condition) isn't completely obvious if it is. */ | |
| // test prevent error | |
| return AST::PathIdentSegment::create_error (); | |
| } | |
| gcc_unreachable (); | |
| // not necessarily an error | |
| } | |
| // Parses an AttrInput AST node (polymorphic, as AttrInput is abstract) | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::AttrInput> | |
| Parser<ManagedTokenSource>::parse_attr_input () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| case LEFT_SQUARE: | |
| case LEFT_CURLY: { | |
| // must be a delimited token tree, so parse that | |
| std::unique_ptr<AST::AttrInput> input_tree ( | |
| new AST::DelimTokenTree (parse_delim_token_tree ())); | |
| // TODO: potential checks on DelimTokenTree before returning | |
| return input_tree; | |
| } | |
| case EQUAL: { | |
| // = LiteralExpr | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| /* Ensure token is a "literal expression" (literally only a literal | |
| * token of any type) */ | |
| if (!t->is_literal ()) | |
| { | |
| Error error ( | |
| t->get_locus (), | |
| "unknown token %qs in attribute body - literal expected", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| skip_after_end_attribute (); | |
| return nullptr; | |
| } | |
| AST::Literal::LitType lit_type = AST::Literal::STRING; | |
| // Crappy mapping of token type to literal type | |
| switch (t->get_id ()) | |
| { | |
| case INT_LITERAL: | |
| lit_type = AST::Literal::INT; | |
| break; | |
| case FLOAT_LITERAL: | |
| lit_type = AST::Literal::FLOAT; | |
| break; | |
| case CHAR_LITERAL: | |
| lit_type = AST::Literal::CHAR; | |
| break; | |
| case BYTE_CHAR_LITERAL: | |
| lit_type = AST::Literal::BYTE; | |
| break; | |
| case BYTE_STRING_LITERAL: | |
| lit_type = AST::Literal::BYTE_STRING; | |
| break; | |
| case STRING_LITERAL: | |
| default: | |
| lit_type = AST::Literal::STRING; | |
| break; // TODO: raw string? don't eliminate it from lexer? | |
| } | |
| // create actual LiteralExpr | |
| AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (), | |
| {}, t->get_locus ()); | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::AttrInput> attr_input_lit ( | |
| new AST::AttrInputLiteral (std::move (lit_expr))); | |
| // do checks or whatever? none required, really | |
| // FIXME: shouldn't a skip token be required here? | |
| return attr_input_lit; | |
| } | |
| break; | |
| case RIGHT_SQUARE: | |
| // means AttrInput is missing, which is allowed | |
| return nullptr; | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unknown token %qs in attribute body - attribute input or " | |
| "none expected", | |
| t->get_token_description ())); | |
| skip_after_end_attribute (); | |
| return nullptr; | |
| } | |
| gcc_unreachable (); | |
| // TODO: find out how to stop gcc error on "no return value" | |
| } | |
| /* Returns true if the token id matches the delimiter type. Note that this only | |
| * operates for END delimiter tokens. */ | |
| inline bool | |
| token_id_matches_delims (TokenId token_id, AST::DelimType delim_type) | |
| { | |
| return ((token_id == RIGHT_PAREN && delim_type == AST::PARENS) | |
| || (token_id == RIGHT_SQUARE && delim_type == AST::SQUARE) | |
| || (token_id == RIGHT_CURLY && delim_type == AST::CURLY)); | |
| } | |
| /* Returns true if the likely result of parsing the next few tokens is a path. | |
| * Not guaranteed, though, especially in the case of syntax errors. */ | |
| inline bool | |
| is_likely_path_next (TokenId next_token_id) | |
| { | |
| switch (next_token_id) | |
| { | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| // maybe - maybe do extra check. But then requires another TokenId. | |
| case DOLLAR_SIGN: | |
| case SCOPE_RESOLUTION: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| // Parses a delimited token tree | |
| template <typename ManagedTokenSource> | |
| AST::DelimTokenTree | |
| Parser<ManagedTokenSource>::parse_delim_token_tree () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| lexer.skip_token (); | |
| Location initial_loc = t->get_locus (); | |
| // save delim type to ensure it is reused later | |
| AST::DelimType delim_type = AST::PARENS; | |
| // Map tokens to DelimType | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| delim_type = AST::PARENS; | |
| break; | |
| case LEFT_SQUARE: | |
| delim_type = AST::SQUARE; | |
| break; | |
| case LEFT_CURLY: | |
| delim_type = AST::CURLY; | |
| break; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs - expecting delimiters (for a " | |
| "delimited token tree)", | |
| t->get_token_description ())); | |
| return AST::DelimTokenTree::create_empty (); | |
| } | |
| // parse actual token tree vector - 0 or more | |
| std::vector<std::unique_ptr<AST::TokenTree>> token_trees_in_tree; | |
| auto delim_open | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| token_trees_in_tree.push_back (std::move (delim_open)); | |
| // repeat loop until finding the matching delimiter | |
| t = lexer.peek_token (); | |
| while (!token_id_matches_delims (t->get_id (), delim_type) | |
| && t->get_id () != END_OF_FILE) | |
| { | |
| std::unique_ptr<AST::TokenTree> tok_tree = parse_token_tree (); | |
| if (tok_tree == nullptr) | |
| { | |
| // TODO: is this error handling appropriate? | |
| Error error ( | |
| t->get_locus (), | |
| "failed to parse token tree in delimited token tree - found %qs", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| return AST::DelimTokenTree::create_empty (); | |
| } | |
| token_trees_in_tree.push_back (std::move (tok_tree)); | |
| // lexer.skip_token(); | |
| t = lexer.peek_token (); | |
| } | |
| auto delim_close | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| token_trees_in_tree.push_back (std::move (delim_close)); | |
| AST::DelimTokenTree token_tree (delim_type, std::move (token_trees_in_tree), | |
| initial_loc); | |
| // parse end delimiters | |
| t = lexer.peek_token (); | |
| if (token_id_matches_delims (t->get_id (), delim_type)) | |
| { | |
| // tokens match opening delimiter, so skip. | |
| lexer.skip_token (); | |
| // DEBUG | |
| rust_debug ("finished parsing new delim token tree - peeked token is now " | |
| "'%s' while t is '%s'", | |
| lexer.peek_token ()->get_token_description (), | |
| t->get_token_description ()); | |
| return token_tree; | |
| } | |
| else | |
| { | |
| // tokens don't match opening delimiters, so produce error | |
| Error error (t->get_locus (), | |
| "unexpected token %qs - expecting closing delimiter %qs " | |
| "(for a delimited token tree)", | |
| t->get_token_description (), | |
| (delim_type == AST::PARENS | |
| ? ")" | |
| : (delim_type == AST::SQUARE ? "]" : "}"))); | |
| add_error (std::move (error)); | |
| /* return empty token tree despite possibly parsing valid token tree - | |
| * TODO is this a good idea? */ | |
| return AST::DelimTokenTree::create_empty (); | |
| } | |
| } | |
| /* Parses a TokenTree syntactical production. This is either a delimited token | |
| * tree or a non-delimiter token. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TokenTree> | |
| Parser<ManagedTokenSource>::parse_token_tree () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| case LEFT_SQUARE: | |
| case LEFT_CURLY: | |
| // Parse delimited token tree | |
| // TODO: use move rather than copy constructor | |
| return std::unique_ptr<AST::DelimTokenTree> ( | |
| new AST::DelimTokenTree (parse_delim_token_tree ())); | |
| case RIGHT_PAREN: | |
| case RIGHT_SQUARE: | |
| case RIGHT_CURLY: | |
| // error - should not be called when this a token | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected closing delimiter %qs - token tree requires " | |
| "either paired delimiters or non-delimiter tokens", | |
| t->get_token_description ())); | |
| lexer.skip_token (); | |
| return nullptr; | |
| default: | |
| // parse token itself as TokenTree | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| } | |
| } | |
| // Parses a single item | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Item> | |
| Parser<ManagedTokenSource>::parse_item (bool called_from_statement) | |
| { | |
| // has a "called_from_statement" parameter for better error message handling | |
| // parse outer attributes for item | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // TODO: decide how to deal with VisItem vs MacroItem dichotomy | |
| /* best current solution: catch all keywords that would imply a VisItem in a | |
| * switch and have MacroItem as a last resort */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case END_OF_FILE: | |
| // not necessarily an error, unless we just read outer | |
| // attributes which needs to be attached | |
| if (!outer_attrs.empty ()) | |
| { | |
| Rust::AST::Attribute attr = outer_attrs.back (); | |
| Error error (attr.get_locus (), | |
| "expected item after outer attribute or doc comment"); | |
| add_error (std::move (error)); | |
| } | |
| return nullptr; | |
| case PUB: | |
| case MOD: | |
| case EXTERN_TOK: | |
| case USE: | |
| case FN_TOK: | |
| case TYPE: | |
| case STRUCT_TOK: | |
| case ENUM_TOK: | |
| case CONST: | |
| case STATIC_TOK: | |
| case TRAIT: | |
| case IMPL: | |
| /* TODO: implement union keyword but not really because of | |
| * context-dependence crappy hack way to parse a union written below to | |
| * separate it from the good code. */ | |
| // case UNION: | |
| case UNSAFE: // maybe - unsafe traits are a thing | |
| // if any of these (should be all possible VisItem prefixes), parse a | |
| // VisItem | |
| return parse_vis_item (std::move (outer_attrs)); | |
| break; | |
| case SUPER: | |
| case SELF: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| // almost certainly macro invocation semi | |
| return parse_macro_item (std::move (outer_attrs)); | |
| break; | |
| // crappy hack to do union "keyword" | |
| case IDENTIFIER: | |
| // TODO: ensure std::string and literal comparison works | |
| if (t->get_str () == "union" | |
| && lexer.peek_token (1)->get_id () == IDENTIFIER) | |
| { | |
| return parse_vis_item (std::move (outer_attrs)); | |
| // or should this go straight to parsing union? | |
| } | |
| else if (t->get_str () == "macro_rules") | |
| { | |
| // macro_rules! macro item | |
| return parse_macro_item (std::move (outer_attrs)); | |
| } | |
| else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION | |
| || lexer.peek_token (1)->get_id () == EXCLAM) | |
| { | |
| /* path (probably) or macro invocation, so probably a macro invocation | |
| * semi */ | |
| return parse_macro_item (std::move (outer_attrs)); | |
| } | |
| gcc_fallthrough (); | |
| default: | |
| // otherwise unrecognised | |
| // return parse_macro_item(std::move(outer_attrs)); | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs for start of %s", | |
| t->get_token_description (), | |
| called_from_statement ? "statement" : "item")); | |
| // skip somewhere? | |
| return nullptr; | |
| break; | |
| } | |
| } | |
| // Parses a contiguous block of outer attributes. | |
| template <typename ManagedTokenSource> | |
| AST::AttrVec | |
| Parser<ManagedTokenSource>::parse_outer_attributes () | |
| { | |
| AST::AttrVec outer_attributes; | |
| while (lexer.peek_token ()->get_id () | |
| == HASH /* Can also be #!, which catches errors. */ | |
| || lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT | |
| || lexer.peek_token ()->get_id () | |
| == INNER_DOC_COMMENT) /* For error handling. */ | |
| { | |
| AST::Attribute outer_attr = parse_outer_attribute (); | |
| /* Ensure only valid outer attributes are added to the outer_attributes | |
| * list */ | |
| if (!outer_attr.is_empty ()) | |
| { | |
| outer_attributes.push_back (std::move (outer_attr)); | |
| } | |
| else | |
| { | |
| /* If no more valid outer attributes, break out of loop (only | |
| * contiguous outer attributes parsed). */ | |
| break; | |
| } | |
| } | |
| outer_attributes.shrink_to_fit (); | |
| return outer_attributes; | |
| /* TODO: this shares basically all code with parse_inner_attributes except | |
| * function call - find way of making it more modular? function pointer? */ | |
| } | |
| // Parse a single outer attribute. | |
| template <typename ManagedTokenSource> | |
| AST::Attribute | |
| Parser<ManagedTokenSource>::parse_outer_attribute () | |
| { | |
| if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT) | |
| return parse_doc_comment (); | |
| if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "inner doc (%<//!%> or %</*!%>) only allowed at start of item " | |
| "and before any outer attribute or doc (%<#[%>, %<///%> or %</**%>)"); | |
| add_error (std::move (error)); | |
| lexer.skip_token (); | |
| return AST::Attribute::create_empty (); | |
| } | |
| /* OuterAttribute -> '#' '[' Attr ']' */ | |
| if (lexer.peek_token ()->get_id () != HASH) | |
| return AST::Attribute::create_empty (); | |
| lexer.skip_token (); | |
| TokenId id = lexer.peek_token ()->get_id (); | |
| if (id != LEFT_SQUARE) | |
| { | |
| if (id == EXCLAM) | |
| { | |
| // this is inner attribute syntax, so throw error | |
| // inner attributes were either already parsed or not allowed here. | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "token %<!%> found, indicating inner attribute definition. Inner " | |
| "attributes are not possible at this location"); | |
| add_error (std::move (error)); | |
| } | |
| return AST::Attribute::create_empty (); | |
| } | |
| lexer.skip_token (); | |
| AST::Attribute actual_attribute = parse_attribute_body (); | |
| if (lexer.peek_token ()->get_id () != RIGHT_SQUARE) | |
| return AST::Attribute::create_empty (); | |
| lexer.skip_token (); | |
| return actual_attribute; | |
| } | |
| // Parses a VisItem (item that can have non-default visibility). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::VisItem> | |
| Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs) | |
| { | |
| // parse visibility, which may or may not exist | |
| AST::Visibility vis = parse_visibility (); | |
| // select VisItem to create depending on keyword | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case MOD: | |
| return parse_module (std::move (vis), std::move (outer_attrs)); | |
| case EXTERN_TOK: | |
| // lookahead to resolve syntactical production | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case CRATE: | |
| return parse_extern_crate (std::move (vis), std::move (outer_attrs)); | |
| case FN_TOK: // extern function | |
| return parse_function (std::move (vis), std::move (outer_attrs)); | |
| case LEFT_CURLY: // extern block | |
| return parse_extern_block (std::move (vis), std::move (outer_attrs)); | |
| case STRING_LITERAL: // for specifying extern ABI | |
| // could be extern block or extern function, so more lookahead | |
| t = lexer.peek_token (2); | |
| switch (t->get_id ()) | |
| { | |
| case FN_TOK: | |
| return parse_function (std::move (vis), std::move (outer_attrs)); | |
| case LEFT_CURLY: | |
| return parse_extern_block (std::move (vis), | |
| std::move (outer_attrs)); | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected token %qs in some sort of extern production", | |
| t->get_token_description ())); | |
| lexer.skip_token (2); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected token %qs in some sort of extern production", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| case USE: | |
| return parse_use_decl (std::move (vis), std::move (outer_attrs)); | |
| case FN_TOK: | |
| return parse_function (std::move (vis), std::move (outer_attrs)); | |
| case TYPE: | |
| return parse_type_alias (std::move (vis), std::move (outer_attrs)); | |
| case STRUCT_TOK: | |
| return parse_struct (std::move (vis), std::move (outer_attrs)); | |
| case ENUM_TOK: | |
| return parse_enum (std::move (vis), std::move (outer_attrs)); | |
| // TODO: implement union keyword but not really because of | |
| // context-dependence case UNION: crappy hack to do union "keyword" | |
| case IDENTIFIER: | |
| if (t->get_str () == "union" | |
| && lexer.peek_token (1)->get_id () == IDENTIFIER) | |
| { | |
| return parse_union (std::move (vis), std::move (outer_attrs)); | |
| // or should item switch go straight to parsing union? | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| case CONST: | |
| // lookahead to resolve syntactical production | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| case UNDERSCORE: | |
| return parse_const_item (std::move (vis), std::move (outer_attrs)); | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_function (std::move (vis), std::move (outer_attrs)); | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected token %qs in some sort of const production", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| case STATIC_TOK: | |
| return parse_static_item (std::move (vis), std::move (outer_attrs)); | |
| case TRAIT: | |
| return parse_trait (std::move (vis), std::move (outer_attrs)); | |
| case IMPL: | |
| return parse_impl (std::move (vis), std::move (outer_attrs)); | |
| case UNSAFE: // unsafe traits, unsafe functions, unsafe impls (trait impls), | |
| // lookahead to resolve syntactical production | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case TRAIT: | |
| return parse_trait (std::move (vis), std::move (outer_attrs)); | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_function (std::move (vis), std::move (outer_attrs)); | |
| case IMPL: | |
| return parse_impl (std::move (vis), std::move (outer_attrs)); | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected token %qs in some sort of unsafe production", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| default: | |
| // otherwise vis item clearly doesn't exist, which is not an error | |
| // has a catch-all post-switch return to allow other breaks to occur | |
| break; | |
| } | |
| return nullptr; | |
| } | |
| // Parses a MacroItem (either a MacroInvocationSemi or MacroRulesDefinition). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroItem> | |
| Parser<ManagedTokenSource>::parse_macro_item (AST::AttrVec outer_attrs) | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| /* dodgy way of detecting macro due to weird context-dependence thing. | |
| * probably can be improved */ | |
| // TODO: ensure that string compare works properly | |
| if (t->get_id () == IDENTIFIER && t->get_str () == "macro_rules") | |
| { | |
| return parse_macro_rules_def (std::move (outer_attrs)); | |
| } | |
| else | |
| { | |
| // DEBUG: TODO: remove | |
| rust_debug ( | |
| "DEBUG - parse_macro_item called and token is not macro_rules"); | |
| if (t->get_id () == IDENTIFIER) | |
| { | |
| rust_debug ("just add to last error: token is not macro_rules and is " | |
| "instead '%s'", | |
| t->get_str ().c_str ()); | |
| } | |
| else | |
| { | |
| rust_debug ("just add to last error: token is not macro_rules and is " | |
| "not an identifier either - it is '%s'", | |
| t->get_token_description ()); | |
| } | |
| return parse_macro_invocation_semi (std::move (outer_attrs)); | |
| } | |
| } | |
| // Parses a macro rules definition syntax extension whatever thing. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroRulesDefinition> | |
| Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs) | |
| { | |
| // ensure that first token is identifier saying "macro_rules" | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (t->get_id () != IDENTIFIER || t->get_str () != "macro_rules") | |
| { | |
| Error error ( | |
| t->get_locus (), | |
| "macro rules definition does not start with %<macro_rules%>"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| Location macro_locus = t->get_locus (); | |
| if (!skip_token (EXCLAM)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // parse macro name | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| return nullptr; | |
| } | |
| Identifier rule_name = ident_tok->get_str (); | |
| // DEBUG | |
| rust_debug ("in macro rules def, about to parse parens."); | |
| // save delim type to ensure it is reused later | |
| AST::DelimType delim_type = AST::PARENS; | |
| // Map tokens to DelimType | |
| t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| delim_type = AST::PARENS; | |
| break; | |
| case LEFT_SQUARE: | |
| delim_type = AST::SQUARE; | |
| break; | |
| case LEFT_CURLY: | |
| delim_type = AST::CURLY; | |
| break; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs - expecting delimiters (for a " | |
| "macro rules definition)", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| // parse actual macro rules | |
| std::vector<AST::MacroRule> macro_rules; | |
| // must be at least one macro rule, so parse it | |
| AST::MacroRule initial_rule = parse_macro_rule (); | |
| if (initial_rule.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "required first macro rule in macro rules definition " | |
| "could not be parsed"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| macro_rules.push_back (std::move (initial_rule)); | |
| // DEBUG | |
| rust_debug ("successfully pushed back initial macro rule"); | |
| t = lexer.peek_token (); | |
| // parse macro rules | |
| while (t->get_id () == SEMICOLON) | |
| { | |
| // skip semicolon | |
| lexer.skip_token (); | |
| // don't parse if end of macro rules | |
| if (token_id_matches_delims (lexer.peek_token ()->get_id (), delim_type)) | |
| { | |
| // DEBUG | |
| rust_debug ( | |
| "broke out of parsing macro rules loop due to finding delim"); | |
| break; | |
| } | |
| // try to parse next rule | |
| AST::MacroRule rule = parse_macro_rule (); | |
| if (rule.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse macro rule in macro rules definition"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| macro_rules.push_back (std::move (rule)); | |
| // DEBUG | |
| rust_debug ("successfully pushed back another macro rule"); | |
| t = lexer.peek_token (); | |
| } | |
| // parse end delimiters | |
| t = lexer.peek_token (); | |
| if (token_id_matches_delims (t->get_id (), delim_type)) | |
| { | |
| // tokens match opening delimiter, so skip. | |
| lexer.skip_token (); | |
| if (delim_type != AST::CURLY) | |
| { | |
| // skip semicolon at end of non-curly macro definitions | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // as this is the end, allow recovery (probably) - may change | |
| return std::unique_ptr<AST::MacroRulesDefinition> ( | |
| new AST::MacroRulesDefinition ( | |
| std::move (rule_name), delim_type, std::move (macro_rules), | |
| std::move (outer_attrs), macro_locus)); | |
| } | |
| } | |
| return std::unique_ptr<AST::MacroRulesDefinition> ( | |
| new AST::MacroRulesDefinition (std::move (rule_name), delim_type, | |
| std::move (macro_rules), | |
| std::move (outer_attrs), macro_locus)); | |
| } | |
| else | |
| { | |
| // tokens don't match opening delimiters, so produce error | |
| Error error (t->get_locus (), | |
| "unexpected token %qs - expecting closing delimiter %qs " | |
| "(for a macro rules definition)", | |
| t->get_token_description (), | |
| (delim_type == AST::PARENS | |
| ? ")" | |
| : (delim_type == AST::SQUARE ? "]" : "}"))); | |
| add_error (std::move (error)); | |
| /* return empty macro definiton despite possibly parsing mostly valid one | |
| * - TODO is this a good idea? */ | |
| return nullptr; | |
| } | |
| } | |
| // Parses a semi-coloned (except for full block) macro invocation item. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroInvocation> | |
| Parser<ManagedTokenSource>::parse_macro_invocation_semi ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location macro_locus = lexer.peek_token ()->get_locus (); | |
| AST::SimplePath path = parse_simple_path (); | |
| if (!skip_token (EXCLAM)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // save delim type to ensure it is reused later | |
| AST::DelimType delim_type = AST::PARENS; | |
| // Map tokens to DelimType | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| delim_type = AST::PARENS; | |
| break; | |
| case LEFT_SQUARE: | |
| delim_type = AST::SQUARE; | |
| break; | |
| case LEFT_CURLY: | |
| delim_type = AST::CURLY; | |
| break; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs - expecting delimiters (for a " | |
| "macro invocation semi body)", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| Location tok_tree_locus = t->get_locus (); | |
| lexer.skip_token (); | |
| // parse actual token trees | |
| std::vector<std::unique_ptr<AST::TokenTree>> token_trees; | |
| auto delim_open | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| token_trees.push_back (std::move (delim_open)); | |
| t = lexer.peek_token (); | |
| // parse token trees until the initial delimiter token is found again | |
| while (!token_id_matches_delims (t->get_id (), delim_type)) | |
| { | |
| std::unique_ptr<AST::TokenTree> tree = parse_token_tree (); | |
| if (tree == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse token tree for macro invocation semi " | |
| "- found %qs", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| token_trees.push_back (std::move (tree)); | |
| t = lexer.peek_token (); | |
| } | |
| auto delim_close | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| token_trees.push_back (std::move (delim_close)); | |
| AST::DelimTokenTree delim_tok_tree (delim_type, std::move (token_trees), | |
| tok_tree_locus); | |
| AST::MacroInvocData invoc_data (std::move (path), std::move (delim_tok_tree)); | |
| // parse end delimiters | |
| t = lexer.peek_token (); | |
| if (token_id_matches_delims (t->get_id (), delim_type)) | |
| { | |
| // tokens match opening delimiter, so skip. | |
| lexer.skip_token (); | |
| if (delim_type != AST::CURLY) | |
| { | |
| // skip semicolon at end of non-curly macro invocation semis | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // as this is the end, allow recovery (probably) - may change | |
| return std::unique_ptr<AST::MacroInvocation> ( | |
| new AST::MacroInvocation (std::move (invoc_data), | |
| std::move (outer_attrs), macro_locus, | |
| true)); | |
| } | |
| } | |
| // DEBUG: | |
| rust_debug ("skipped token is '%s', next token (current peek) is '%s'", | |
| t->get_token_description (), | |
| lexer.peek_token ()->get_token_description ()); | |
| return std::unique_ptr<AST::MacroInvocation> ( | |
| new AST::MacroInvocation (std::move (invoc_data), | |
| std::move (outer_attrs), macro_locus, true)); | |
| } | |
| else | |
| { | |
| // tokens don't match opening delimiters, so produce error | |
| Error error (t->get_locus (), | |
| "unexpected token %qs - expecting closing delimiter %qs " | |
| "(for a macro invocation semi)", | |
| t->get_token_description (), | |
| (delim_type == AST::PARENS | |
| ? ")" | |
| : (delim_type == AST::SQUARE ? "]" : "}"))); | |
| add_error (std::move (error)); | |
| /* return empty macro invocation despite possibly parsing mostly valid one | |
| * - TODO is this a good idea? */ | |
| return nullptr; | |
| } | |
| } | |
| // Parses a non-semicoloned macro invocation (i.e. as pattern or expression). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroInvocation> | |
| Parser<ManagedTokenSource>::parse_macro_invocation (AST::AttrVec outer_attrs) | |
| { | |
| // parse macro path | |
| AST::SimplePath macro_path = parse_simple_path (); | |
| if (macro_path.is_empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse macro invocation path"); | |
| add_error (std::move (error)); | |
| // skip? | |
| return nullptr; | |
| } | |
| if (!skip_token (EXCLAM)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // parse internal delim token tree | |
| AST::DelimTokenTree delim_tok_tree = parse_delim_token_tree (); | |
| Location macro_locus = macro_path.get_locus (); | |
| return std::unique_ptr<AST::MacroInvocation> ( | |
| new AST::MacroInvocation (AST::MacroInvocData (std::move (macro_path), | |
| std::move (delim_tok_tree)), | |
| std::move (outer_attrs), macro_locus)); | |
| } | |
| // Parses a macro rule definition - does not parse semicolons. | |
| template <typename ManagedTokenSource> | |
| AST::MacroRule | |
| Parser<ManagedTokenSource>::parse_macro_rule () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse macro matcher | |
| AST::MacroMatcher matcher = parse_macro_matcher (); | |
| if (matcher.is_error ()) | |
| return AST::MacroRule::create_error (locus); | |
| if (!skip_token (MATCH_ARROW)) | |
| { | |
| // skip after somewhere? | |
| return AST::MacroRule::create_error (locus); | |
| } | |
| // parse transcriber (this is just a delim token tree) | |
| Location token_tree_loc = lexer.peek_token ()->get_locus (); | |
| AST::MacroTranscriber transcriber (parse_delim_token_tree (), token_tree_loc); | |
| return AST::MacroRule (std::move (matcher), std::move (transcriber), locus); | |
| } | |
| // Parses a macro matcher (part of a macro rule definition). | |
| template <typename ManagedTokenSource> | |
| AST::MacroMatcher | |
| Parser<ManagedTokenSource>::parse_macro_matcher () | |
| { | |
| // save delim type to ensure it is reused later | |
| AST::DelimType delim_type = AST::PARENS; | |
| // DEBUG | |
| rust_debug ("begun parsing macro matcher"); | |
| // Map tokens to DelimType | |
| const_TokenPtr t = lexer.peek_token (); | |
| Location locus = t->get_locus (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| delim_type = AST::PARENS; | |
| break; | |
| case LEFT_SQUARE: | |
| delim_type = AST::SQUARE; | |
| break; | |
| case LEFT_CURLY: | |
| delim_type = AST::CURLY; | |
| break; | |
| default: | |
| add_error (Error ( | |
| t->get_locus (), | |
| "unexpected token %qs - expecting delimiters (for a macro matcher)", | |
| t->get_token_description ())); | |
| return AST::MacroMatcher::create_error (t->get_locus ()); | |
| } | |
| lexer.skip_token (); | |
| // parse actual macro matches | |
| std::vector<std::unique_ptr<AST::MacroMatch>> matches; | |
| // Set of possible preceding macro matches to make sure follow-set | |
| // restrictions are respected. | |
| // TODO: Consider using std::reference_wrapper instead of raw pointers? | |
| std::vector<const AST::MacroMatch *> last_matches; | |
| t = lexer.peek_token (); | |
| // parse token trees until the initial delimiter token is found again | |
| while (!token_id_matches_delims (t->get_id (), delim_type)) | |
| { | |
| std::unique_ptr<AST::MacroMatch> match = parse_macro_match (); | |
| if (match == nullptr) | |
| { | |
| Error error ( | |
| t->get_locus (), | |
| "failed to parse macro match for macro matcher - found %qs", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| return AST::MacroMatcher::create_error (t->get_locus ()); | |
| } | |
| if (matches.size () > 0) | |
| { | |
| const auto *last_match = matches.back ().get (); | |
| // We want to check if we are dealing with a zeroable repetition | |
| bool zeroable = false; | |
| if (last_match->get_macro_match_type () | |
| == AST::MacroMatch::MacroMatchType::Repetition) | |
| { | |
| auto repetition | |
| = static_cast<const AST::MacroMatchRepetition *> (last_match); | |
| if (repetition->get_op () | |
| != AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE) | |
| zeroable = true; | |
| } | |
| if (!zeroable) | |
| last_matches.clear (); | |
| last_matches.emplace_back (last_match); | |
| for (auto last : last_matches) | |
| if (!is_match_compatible (*last, *match)) | |
| return AST::MacroMatcher::create_error ( | |
| match->get_match_locus ()); | |
| } | |
| matches.push_back (std::move (match)); | |
| // DEBUG | |
| rust_debug ("pushed back a match in macro matcher"); | |
| t = lexer.peek_token (); | |
| } | |
| // parse end delimiters | |
| t = lexer.peek_token (); | |
| if (token_id_matches_delims (t->get_id (), delim_type)) | |
| { | |
| // tokens match opening delimiter, so skip. | |
| lexer.skip_token (); | |
| return AST::MacroMatcher (delim_type, std::move (matches), locus); | |
| } | |
| else | |
| { | |
| // tokens don't match opening delimiters, so produce error | |
| Error error (t->get_locus (), | |
| "unexpected token %qs - expecting closing delimiter %qs " | |
| "(for a macro matcher)", | |
| t->get_token_description (), | |
| (delim_type == AST::PARENS | |
| ? ")" | |
| : (delim_type == AST::SQUARE ? "]" : "}"))); | |
| add_error (std::move (error)); | |
| /* return error macro matcher despite possibly parsing mostly correct one? | |
| * TODO is this the best idea? */ | |
| return AST::MacroMatcher::create_error (t->get_locus ()); | |
| } | |
| } | |
| // Parses a macro match (syntax match inside a matcher in a macro rule). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroMatch> | |
| Parser<ManagedTokenSource>::parse_macro_match () | |
| { | |
| // branch based on token available | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| case LEFT_SQUARE: | |
| case LEFT_CURLY: { | |
| // must be macro matcher as delimited | |
| AST::MacroMatcher matcher = parse_macro_matcher (); | |
| if (matcher.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse macro matcher in macro match"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::MacroMatcher> ( | |
| new AST::MacroMatcher (std::move (matcher))); | |
| } | |
| case DOLLAR_SIGN: { | |
| // have to do more lookahead to determine if fragment or repetition | |
| const_TokenPtr t2 = lexer.peek_token (1); | |
| switch (t2->get_id ()) | |
| { | |
| case ABSTRACT: | |
| case AS: | |
| case ASYNC: | |
| case BECOME: | |
| case BOX: | |
| case BREAK: | |
| case CONST: | |
| case CONTINUE: | |
| case CRATE: | |
| case DO: | |
| case DYN: | |
| case ELSE: | |
| case ENUM_TOK: | |
| case EXTERN_TOK: | |
| case FALSE_LITERAL: | |
| case FINAL_TOK: | |
| case FN_TOK: | |
| case FOR: | |
| case IF: | |
| case IMPL: | |
| case IN: | |
| case LET: | |
| case LOOP: | |
| case MACRO: | |
| case MATCH_TOK: | |
| case MOD: | |
| case MOVE: | |
| case MUT: | |
| case OVERRIDE_TOK: | |
| case PRIV: | |
| case PUB: | |
| case REF: | |
| case RETURN_TOK: | |
| case SELF_ALIAS: | |
| case SELF: | |
| case STATIC_TOK: | |
| case STRUCT_TOK: | |
| case SUPER: | |
| case TRAIT: | |
| case TRUE_LITERAL: | |
| case TRY: | |
| case TYPE: | |
| case TYPEOF: | |
| case UNSAFE: | |
| case UNSIZED: | |
| case USE: | |
| case VIRTUAL: | |
| case WHERE: | |
| case WHILE: | |
| case YIELD: | |
| case IDENTIFIER: | |
| // macro fragment | |
| return parse_macro_match_fragment (); | |
| case LEFT_PAREN: | |
| // macro repetition | |
| return parse_macro_match_repetition (); | |
| default: | |
| // error: unrecognised | |
| add_error ( | |
| Error (t2->get_locus (), | |
| "unrecognised token combination %<$%s%> at start of " | |
| "macro match - did you mean %<$identifier%> or %<$(%>?", | |
| t2->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| } | |
| case RIGHT_PAREN: | |
| case RIGHT_SQUARE: | |
| case RIGHT_CURLY: | |
| // not allowed | |
| add_error (Error ( | |
| t->get_locus (), | |
| "closing delimiters like %qs are not allowed at the start of a macro " | |
| "match", | |
| t->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| default: | |
| // just the token | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| } | |
| } | |
| // Parses a fragment macro match. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroMatchFragment> | |
| Parser<ManagedTokenSource>::parse_macro_match_fragment () | |
| { | |
| Location fragment_locus = lexer.peek_token ()->get_locus (); | |
| skip_token (DOLLAR_SIGN); | |
| Identifier ident = ""; | |
| auto identifier = lexer.peek_token (); | |
| if (identifier->has_str ()) | |
| ident = identifier->get_str (); | |
| else | |
| ident = std::string (token_id_to_str (identifier->get_id ())); | |
| if (ident.empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "missing identifier in macro match fragment"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| skip_token (identifier->get_id ()); | |
| if (!skip_token (COLON)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // get MacroFragSpec for macro | |
| const_TokenPtr t = expect_token (IDENTIFIER); | |
| if (t == nullptr) | |
| return nullptr; | |
| AST::MacroFragSpec frag | |
| = AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ()); | |
| if (frag.is_error ()) | |
| { | |
| Error error (t->get_locus (), | |
| "invalid fragment specifier %qs in fragment macro match", | |
| t->get_str ().c_str ()); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::MacroMatchFragment> ( | |
| new AST::MacroMatchFragment (std::move (ident), frag, fragment_locus)); | |
| } | |
| // Parses a repetition macro match. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroMatchRepetition> | |
| Parser<ManagedTokenSource>::parse_macro_match_repetition () | |
| { | |
| skip_token (DOLLAR_SIGN); | |
| skip_token (LEFT_PAREN); | |
| std::vector<std::unique_ptr<AST::MacroMatch>> matches; | |
| // parse required first macro match | |
| std::unique_ptr<AST::MacroMatch> initial_match = parse_macro_match (); | |
| if (initial_match == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "could not parse required first macro match in macro match repetition"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| matches.push_back (std::move (initial_match)); | |
| // parse optional later macro matches | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::MacroMatch> match = parse_macro_match (); | |
| if (match == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse macro match in macro match repetition"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| matches.push_back (std::move (match)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| t = lexer.peek_token (); | |
| // see if separator token exists | |
| std::unique_ptr<AST::Token> separator = nullptr; | |
| switch (t->get_id ()) | |
| { | |
| // repetition operators | |
| case ASTERISK: | |
| case PLUS: | |
| case QUESTION_MARK: | |
| // delimiters | |
| case LEFT_PAREN: | |
| case LEFT_CURLY: | |
| case LEFT_SQUARE: | |
| case RIGHT_PAREN: | |
| case RIGHT_CURLY: | |
| case RIGHT_SQUARE: | |
| // separator does not exist, so still null and don't skip token | |
| break; | |
| default: | |
| // separator does exist | |
| separator = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); | |
| lexer.skip_token (); | |
| break; | |
| } | |
| // parse repetition operator | |
| t = lexer.peek_token (); | |
| AST::MacroMatchRepetition::MacroRepOp op = AST::MacroMatchRepetition::NONE; | |
| switch (t->get_id ()) | |
| { | |
| case ASTERISK: | |
| op = AST::MacroMatchRepetition::ANY; | |
| lexer.skip_token (); | |
| break; | |
| case PLUS: | |
| op = AST::MacroMatchRepetition::ONE_OR_MORE; | |
| lexer.skip_token (); | |
| break; | |
| case QUESTION_MARK: | |
| op = AST::MacroMatchRepetition::ZERO_OR_ONE; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "expected macro repetition operator (%<*%>, %<+%>, or %<?%>) in " | |
| "macro match - found %qs", | |
| t->get_token_description ())); | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::MacroMatchRepetition> ( | |
| new AST::MacroMatchRepetition (std::move (matches), op, | |
| std::move (separator), t->get_locus ())); | |
| } | |
| /* Parses a visibility syntactical production (i.e. creating a non-default | |
| * visibility) */ | |
| template <typename ManagedTokenSource> | |
| AST::Visibility | |
| Parser<ManagedTokenSource>::parse_visibility () | |
| { | |
| // check for no visibility | |
| if (lexer.peek_token ()->get_id () != PUB) | |
| { | |
| return AST::Visibility::create_private (); | |
| } | |
| lexer.skip_token (); | |
| // create simple pub visibility if no parentheses | |
| if (lexer.peek_token ()->get_id () != LEFT_PAREN) | |
| { | |
| return AST::Visibility::create_public (); | |
| // or whatever | |
| } | |
| lexer.skip_token (); | |
| const_TokenPtr t = lexer.peek_token (); | |
| auto path_loc = t->get_locus (); | |
| switch (t->get_id ()) | |
| { | |
| case CRATE: | |
| lexer.skip_token (); | |
| skip_token (RIGHT_PAREN); | |
| return AST::Visibility::create_crate (path_loc); | |
| case SELF: | |
| lexer.skip_token (); | |
| skip_token (RIGHT_PAREN); | |
| return AST::Visibility::create_self (path_loc); | |
| case SUPER: | |
| lexer.skip_token (); | |
| skip_token (RIGHT_PAREN); | |
| return AST::Visibility::create_super (path_loc); | |
| case IN: { | |
| lexer.skip_token (); | |
| // parse the "in" path as well | |
| AST::SimplePath path = parse_simple_path (); | |
| if (path.is_empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "missing path in pub(in path) visibility"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return AST::Visibility::create_error (); | |
| } | |
| skip_token (RIGHT_PAREN); | |
| return AST::Visibility::create_in_path (std::move (path)); | |
| } | |
| default: | |
| add_error (Error (t->get_locus (), "unexpected token %qs in visibility", | |
| t->get_token_description ())); | |
| lexer.skip_token (); | |
| return AST::Visibility::create_error (); | |
| } | |
| } | |
| // Parses a module - either a bodied module or a module defined in another file. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Module> | |
| Parser<ManagedTokenSource>::parse_module (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (MOD); | |
| const_TokenPtr module_name = expect_token (IDENTIFIER); | |
| if (module_name == nullptr) | |
| { | |
| return nullptr; | |
| } | |
| Identifier name = module_name->get_str (); | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case SEMICOLON: | |
| lexer.skip_token (); | |
| // Construct an external module | |
| return std::unique_ptr<AST::Module> ( | |
| new AST::Module (std::move (name), std::move (vis), | |
| std::move (outer_attrs), locus, lexer.get_filename (), | |
| inline_module_stack)); | |
| case LEFT_CURLY: { | |
| lexer.skip_token (); | |
| // parse inner attributes | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| std::string module_path_name | |
| = extract_module_path (inner_attrs, outer_attrs, name); | |
| InlineModuleStackScope scope (*this, std::move (module_path_name)); | |
| // parse items | |
| std::vector<std::unique_ptr<AST::Item>> items; | |
| const_TokenPtr tok = lexer.peek_token (); | |
| while (tok->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::Item> item = parse_item (false); | |
| if (item == nullptr) | |
| { | |
| Error error (tok->get_locus (), | |
| "failed to parse item in module"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| items.push_back (std::move (item)); | |
| tok = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::Module> ( | |
| new AST::Module (std::move (name), locus, std::move (items), | |
| std::move (vis), std::move (inner_attrs), | |
| std::move (outer_attrs))); // module name? | |
| } | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected token %qs in module declaration/definition item", | |
| t->get_token_description ())); | |
| lexer.skip_token (); | |
| return nullptr; | |
| } | |
| } | |
| // Parses an extern crate declaration (dependency on external crate) | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExternCrate> | |
| Parser<ManagedTokenSource>::parse_extern_crate (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| if (!skip_token (EXTERN_TOK)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| if (!skip_token (CRATE)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| /* parse crate reference name - this has its own syntactical rule in reference | |
| * but seems to not be used elsewhere, so i'm putting it here */ | |
| const_TokenPtr crate_name_tok = lexer.peek_token (); | |
| std::string crate_name; | |
| switch (crate_name_tok->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| crate_name = crate_name_tok->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case SELF: | |
| crate_name = "self"; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| add_error ( | |
| Error (crate_name_tok->get_locus (), | |
| "expecting crate name (identifier or %<self%>), found %qs", | |
| crate_name_tok->get_token_description ())); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // don't parse as clause if it doesn't exist | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::ExternCrate> ( | |
| new AST::ExternCrate (std::move (crate_name), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| /* parse as clause - this also has its own syntactical rule in reference and | |
| * also seems to not be used elsewhere, so including here again. */ | |
| if (!skip_token (AS)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| const_TokenPtr as_name_tok = lexer.peek_token (); | |
| std::string as_name; | |
| switch (as_name_tok->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| as_name = as_name_tok->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case UNDERSCORE: | |
| as_name = "_"; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| add_error ( | |
| Error (as_name_tok->get_locus (), | |
| "expecting as clause name (identifier or %<_%>), found %qs", | |
| as_name_tok->get_token_description ())); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ExternCrate> ( | |
| new AST::ExternCrate (std::move (crate_name), std::move (vis), | |
| std::move (outer_attrs), locus, std::move (as_name))); | |
| } | |
| // Parses a use declaration. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::UseDeclaration> | |
| Parser<ManagedTokenSource>::parse_use_decl (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| if (!skip_token (USE)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse use tree, which is required | |
| std::unique_ptr<AST::UseTree> use_tree = parse_use_tree (); | |
| if (use_tree == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse use tree in use declaration"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::UseDeclaration> ( | |
| new AST::UseDeclaration (std::move (use_tree), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a use tree (which can be recursive and is actually a base class). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::UseTree> | |
| Parser<ManagedTokenSource>::parse_use_tree () | |
| { | |
| /* potential syntax definitions in attempt to get algorithm: | |
| * Glob: | |
| * <- SimplePath :: * | |
| * <- :: * | |
| * <- * | |
| * Nested tree thing: | |
| * <- SimplePath :: { COMPLICATED_INNER_TREE_THING } | |
| * <- :: COMPLICATED_INNER_TREE_THING } | |
| * <- { COMPLICATED_INNER_TREE_THING } | |
| * Rebind thing: | |
| * <- SimplePath as IDENTIFIER | |
| * <- SimplePath as _ | |
| * <- SimplePath | |
| */ | |
| /* current plan of attack: try to parse SimplePath first - if fails, one of | |
| * top two then try parse :: - if fails, one of top two. Next is deciding | |
| * character for top two. */ | |
| /* Thus, parsing smaller parts of use tree may require feeding into function | |
| * via parameters (or could handle all in this single function because other | |
| * use tree types aren't recognised as separate in the spec) */ | |
| // TODO: I think this function is too complex, probably should split it | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // bool has_path = false; | |
| AST::SimplePath path = parse_simple_path (); | |
| if (path.is_empty ()) | |
| { | |
| // has no path, so must be glob or nested tree UseTree type | |
| bool is_global = false; | |
| // check for global scope resolution operator | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| lexer.skip_token (); | |
| is_global = true; | |
| } | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case ASTERISK: | |
| // glob UseTree type | |
| lexer.skip_token (); | |
| if (is_global) | |
| return std::unique_ptr<AST::UseTreeGlob> ( | |
| new AST::UseTreeGlob (AST::UseTreeGlob::GLOBAL, | |
| AST::SimplePath::create_empty (), locus)); | |
| else | |
| return std::unique_ptr<AST::UseTreeGlob> ( | |
| new AST::UseTreeGlob (AST::UseTreeGlob::NO_PATH, | |
| AST::SimplePath::create_empty (), locus)); | |
| case LEFT_CURLY: { | |
| // nested tree UseTree type | |
| lexer.skip_token (); | |
| std::vector<std::unique_ptr<AST::UseTree>> use_trees; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::UseTree> use_tree = parse_use_tree (); | |
| if (use_tree == nullptr) | |
| { | |
| break; | |
| } | |
| use_trees.push_back (std::move (use_tree)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // skip end curly delimiter | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| if (is_global) | |
| return std::unique_ptr<AST::UseTreeList> ( | |
| new AST::UseTreeList (AST::UseTreeList::GLOBAL, | |
| AST::SimplePath::create_empty (), | |
| std::move (use_trees), locus)); | |
| else | |
| return std::unique_ptr<AST::UseTreeList> ( | |
| new AST::UseTreeList (AST::UseTreeList::NO_PATH, | |
| AST::SimplePath::create_empty (), | |
| std::move (use_trees), locus)); | |
| } | |
| case AS: | |
| // this is not allowed | |
| add_error (Error ( | |
| t->get_locus (), | |
| "use declaration with rebind %<as%> requires a valid simple path - " | |
| "none found")); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in use tree with " | |
| "no valid simple path (i.e. list" | |
| " or glob use tree)", | |
| t->get_token_description ())); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| } | |
| else | |
| { | |
| /* Due to aforementioned implementation issues, the trailing :: token is | |
| * consumed by the path, so it can not be used as a disambiguator. | |
| * NOPE, not true anymore - TODO what are the consequences of this? */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case ASTERISK: | |
| // glob UseTree type | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::UseTreeGlob> ( | |
| new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED, | |
| std::move (path), locus)); | |
| case LEFT_CURLY: { | |
| // nested tree UseTree type | |
| lexer.skip_token (); | |
| std::vector<std::unique_ptr<AST::UseTree>> use_trees; | |
| // TODO: think of better control structure | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::UseTree> use_tree = parse_use_tree (); | |
| if (use_tree == nullptr) | |
| { | |
| break; | |
| } | |
| use_trees.push_back (std::move (use_tree)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // skip end curly delimiter | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::UseTreeList> ( | |
| new AST::UseTreeList (AST::UseTreeList::PATH_PREFIXED, | |
| std::move (path), std::move (use_trees), | |
| locus)); | |
| } | |
| case AS: { | |
| // rebind UseTree type | |
| lexer.skip_token (); | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| // skip lexer token | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::UseTreeRebind> ( | |
| new AST::UseTreeRebind (AST::UseTreeRebind::IDENTIFIER, | |
| std::move (path), locus, | |
| t->get_str ())); | |
| case UNDERSCORE: | |
| // skip lexer token | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::UseTreeRebind> ( | |
| new AST::UseTreeRebind (AST::UseTreeRebind::WILDCARD, | |
| std::move (path), locus, "_")); | |
| default: | |
| add_error (Error ( | |
| t->get_locus (), | |
| "unexpected token %qs in use tree with as clause - expected " | |
| "identifier or %<_%>", | |
| t->get_token_description ())); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| } | |
| case SEMICOLON: | |
| // rebind UseTree type without rebinding - path only | |
| // don't skip semicolon - handled in parse_use_tree | |
| // lexer.skip_token(); | |
| return std::unique_ptr<AST::UseTreeRebind> ( | |
| new AST::UseTreeRebind (AST::UseTreeRebind::NONE, std::move (path), | |
| locus)); | |
| case COMMA: | |
| case RIGHT_CURLY: | |
| // this may occur in recursive calls - assume it is ok and ignore it | |
| return std::unique_ptr<AST::UseTreeRebind> ( | |
| new AST::UseTreeRebind (AST::UseTreeRebind::NONE, std::move (path), | |
| locus)); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in use tree with valid path", | |
| t->get_token_description ())); | |
| // skip_after_semicolon(); | |
| return nullptr; | |
| } | |
| } | |
| } | |
| // Parses a function (not a method). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Function> | |
| Parser<ManagedTokenSource>::parse_function (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // Get qualifiers for function if they exist | |
| AST::FunctionQualifiers qualifiers = parse_function_qualifiers (); | |
| skip_token (FN_TOK); | |
| // Save function name token | |
| const_TokenPtr function_name_tok = expect_token (IDENTIFIER); | |
| if (function_name_tok == nullptr) | |
| { | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| Identifier function_name = function_name_tok->get_str (); | |
| // parse generic params - if exist | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "function declaration missing opening parentheses before " | |
| "parameter list"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse function parameters (only if next token isn't right paren) | |
| std::vector<AST::FunctionParam> function_params; | |
| if (lexer.peek_token ()->get_id () != RIGHT_PAREN) | |
| function_params | |
| = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; }); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "function declaration missing closing parentheses after " | |
| "parameter list"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse function return type - if exists | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| // parse where clause - if exists | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| // parse block expression | |
| std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr (); | |
| return std::unique_ptr<AST::Function> ( | |
| new AST::Function (std::move (function_name), std::move (qualifiers), | |
| std::move (generic_params), std::move (function_params), | |
| std::move (return_type), std::move (where_clause), | |
| std::move (block_expr), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses function or method qualifiers (i.e. const, unsafe, and extern). | |
| template <typename ManagedTokenSource> | |
| AST::FunctionQualifiers | |
| Parser<ManagedTokenSource>::parse_function_qualifiers () | |
| { | |
| AsyncConstStatus const_status = NONE; | |
| bool has_unsafe = false; | |
| bool has_extern = false; | |
| std::string abi; | |
| // Check in order of const, unsafe, then extern | |
| const_TokenPtr t = lexer.peek_token (); | |
| Location locus = t->get_locus (); | |
| switch (t->get_id ()) | |
| { | |
| case CONST: | |
| lexer.skip_token (); | |
| const_status = CONST_FN; | |
| break; | |
| case ASYNC: | |
| lexer.skip_token (); | |
| const_status = ASYNC_FN; | |
| break; | |
| default: | |
| // const status is still none | |
| break; | |
| } | |
| if (lexer.peek_token ()->get_id () == UNSAFE) | |
| { | |
| lexer.skip_token (); | |
| has_unsafe = true; | |
| } | |
| if (lexer.peek_token ()->get_id () == EXTERN_TOK) | |
| { | |
| lexer.skip_token (); | |
| has_extern = true; | |
| // detect optional abi name | |
| const_TokenPtr next_tok = lexer.peek_token (); | |
| if (next_tok->get_id () == STRING_LITERAL) | |
| { | |
| lexer.skip_token (); | |
| abi = next_tok->get_str (); | |
| } | |
| } | |
| return AST::FunctionQualifiers (locus, const_status, has_unsafe, has_extern, | |
| std::move (abi)); | |
| } | |
| // Parses generic (lifetime or type) params inside angle brackets (optional). | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::GenericParam>> | |
| Parser<ManagedTokenSource>::parse_generic_params_in_angles () | |
| { | |
| if (lexer.peek_token ()->get_id () != LEFT_ANGLE) | |
| { | |
| // seems to be no generic params, so exit with empty vector | |
| return std::vector<std::unique_ptr<AST::GenericParam>> (); | |
| } | |
| lexer.skip_token (); | |
| // DEBUG: | |
| rust_debug ("skipped left angle in generic param"); | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params (is_right_angle_tok); | |
| // DEBUG: | |
| rust_debug ("finished parsing actual generic params (i.e. inside angles)"); | |
| if (!skip_generics_right_angle ()) | |
| { | |
| // DEBUG | |
| rust_debug ("failed to skip generics right angle - returning empty " | |
| "generic params"); | |
| return std::vector<std::unique_ptr<AST::GenericParam>> (); | |
| } | |
| return generic_params; | |
| } | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::unique_ptr<AST::GenericParam> | |
| Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token) | |
| { | |
| auto token = lexer.peek_token (); | |
| auto outer_attrs = parse_outer_attribute (); | |
| std::unique_ptr<AST::GenericParam> param; | |
| switch (token->get_id ()) | |
| { | |
| case LIFETIME: { | |
| auto lifetime = parse_lifetime (); | |
| if (lifetime.is_error ()) | |
| { | |
| rust_error_at ( | |
| token->get_locus (), | |
| "failed to parse lifetime in generic parameter list"); | |
| return nullptr; | |
| } | |
| std::vector<AST::Lifetime> lifetime_bounds; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| // parse required bounds | |
| lifetime_bounds | |
| = parse_lifetime_bounds ([is_end_token] (TokenId id) { | |
| return is_end_token (id) || id == COMMA; | |
| }); | |
| } | |
| param = std::unique_ptr<AST::LifetimeParam> (new AST::LifetimeParam ( | |
| std::move (lifetime), std::move (lifetime_bounds), | |
| std::move (outer_attrs), token->get_locus ())); | |
| break; | |
| } | |
| case IDENTIFIER: { | |
| auto type_ident = token->get_str (); | |
| lexer.skip_token (); | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| // parse optional type param bounds | |
| type_param_bounds = parse_type_param_bounds (); | |
| } | |
| std::unique_ptr<AST::Type> type = nullptr; | |
| if (lexer.peek_token ()->get_id () == EQUAL) | |
| { | |
| lexer.skip_token (); | |
| // parse required type | |
| type = parse_type (); | |
| if (!type) | |
| { | |
| rust_error_at ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse type in type param in generic params"); | |
| return nullptr; | |
| } | |
| } | |
| param = std::unique_ptr<AST::TypeParam> ( | |
| new AST::TypeParam (std::move (type_ident), token->get_locus (), | |
| std::move (type_param_bounds), std::move (type), | |
| std::move (outer_attrs))); | |
| break; | |
| } | |
| case CONST: { | |
| lexer.skip_token (); | |
| auto name_token = expect_token (IDENTIFIER); | |
| if (!name_token || !expect_token (COLON)) | |
| return nullptr; | |
| auto type = parse_type (); | |
| if (!type) | |
| return nullptr; | |
| // optional default value | |
| auto default_expr = AST::GenericArg::create_error (); | |
| if (lexer.peek_token ()->get_id () == EQUAL) | |
| { | |
| lexer.skip_token (); | |
| auto tok = lexer.peek_token (); | |
| default_expr = parse_generic_arg (); | |
| if (default_expr.is_error ()) | |
| rust_error_at (tok->get_locus (), | |
| "invalid token for start of default value for " | |
| "const generic parameter: expected %<block%>, " | |
| "%<identifier%> or %<literal%>, got %qs", | |
| token_id_to_str (tok->get_id ())); | |
| // At this point, we *know* that we are parsing a const | |
| // expression | |
| if (default_expr.get_kind () == AST::GenericArg::Kind::Either) | |
| default_expr = default_expr.disambiguate_to_const (); | |
| } | |
| param = std::unique_ptr<AST::ConstGenericParam> ( | |
| new AST::ConstGenericParam (name_token->get_str (), std::move (type), | |
| default_expr, std::move (outer_attrs), | |
| token->get_locus ())); | |
| break; | |
| } | |
| default: | |
| // FIXME: Can we clean this last call with a method call? | |
| rust_error_at (token->get_locus (), | |
| "unexpected token when parsing generic parameters: %qs", | |
| token->get_str ().c_str ()); | |
| return nullptr; | |
| } | |
| return param; | |
| } | |
| /* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost | |
| * always parse_generic_params_in_angles is what is wanted. */ | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<std::unique_ptr<AST::GenericParam>> | |
| Parser<ManagedTokenSource>::parse_generic_params (EndTokenPred is_end_token) | |
| { | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params; | |
| /* can't parse lifetime and type params separately due to lookahead issues | |
| * thus, parse them all here */ | |
| /* HACK: used to retain attribute data if a lifetime param is tentatively | |
| * parsed but it turns out to be type param */ | |
| AST::Attribute parsed_outer_attr = AST::Attribute::create_empty (); | |
| // Did we parse a generic type param yet | |
| auto type_seen = false; | |
| // Did the user write a lifetime parameter after a type one | |
| auto order_error = false; | |
| // parse lifetime params | |
| while (!is_end_token (lexer.peek_token ()->get_id ())) | |
| { | |
| auto param = parse_generic_param (is_end_token); | |
| if (param) | |
| { | |
| // TODO: Handle `Const` here as well if necessary | |
| if (param->get_kind () == AST::GenericParam::Kind::Type) | |
| type_seen = true; | |
| else if (param->get_kind () == AST::GenericParam::Kind::Lifetime | |
| && type_seen) | |
| order_error = true; | |
| generic_params.emplace_back (std::move (param)); | |
| maybe_skip_token (COMMA); | |
| } | |
| } | |
| // FIXME: Add reordering hint | |
| if (order_error) | |
| rust_error_at (generic_params.front ()->get_locus (), | |
| "invalid order for generic parameters: lifetimes should " | |
| "always come before types"); | |
| generic_params.shrink_to_fit (); | |
| return generic_params; | |
| } | |
| /* Parses lifetime generic parameters (pointers). Will also consume any | |
| * trailing comma. No extra checks for end token. */ | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::LifetimeParam>> | |
| Parser<ManagedTokenSource>::parse_lifetime_params () | |
| { | |
| std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params; | |
| while (lexer.peek_token ()->get_id () != END_OF_FILE) | |
| { | |
| AST::LifetimeParam lifetime_param = parse_lifetime_param (); | |
| if (lifetime_param.is_error ()) | |
| { | |
| // can't treat as error as only way to get out with trailing comma | |
| break; | |
| } | |
| lifetime_params.push_back (std::unique_ptr<AST::LifetimeParam> ( | |
| new AST::LifetimeParam (std::move (lifetime_param)))); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| lifetime_params.shrink_to_fit (); | |
| return lifetime_params; | |
| } | |
| /* Parses lifetime generic parameters (pointers). Will also consume any | |
| * trailing comma. Has extra is_end_token predicate checking. */ | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<std::unique_ptr<AST::LifetimeParam>> | |
| Parser<ManagedTokenSource>::parse_lifetime_params (EndTokenPred is_end_token) | |
| { | |
| std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params; | |
| // if end_token is not specified, it defaults to EOF, so should work fine | |
| while (!is_end_token (lexer.peek_token ()->get_id ())) | |
| { | |
| AST::LifetimeParam lifetime_param = parse_lifetime_param (); | |
| if (lifetime_param.is_error ()) | |
| { | |
| /* TODO: is it worth throwing away all lifetime params just because | |
| * one failed? */ | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse lifetime param in lifetime params"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| lifetime_params.push_back (std::unique_ptr<AST::LifetimeParam> ( | |
| new AST::LifetimeParam (std::move (lifetime_param)))); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| lifetime_params.shrink_to_fit (); | |
| return lifetime_params; | |
| } | |
| /* Parses lifetime generic parameters (objects). Will also consume any | |
| * trailing comma. No extra checks for end token. | |
| * TODO: is this best solution? implements most of the same algorithm. */ | |
| template <typename ManagedTokenSource> | |
| std::vector<AST::LifetimeParam> | |
| Parser<ManagedTokenSource>::parse_lifetime_params_objs () | |
| { | |
| std::vector<AST::LifetimeParam> lifetime_params; | |
| // bad control structure as end token cannot be guaranteed | |
| while (true) | |
| { | |
| AST::LifetimeParam lifetime_param = parse_lifetime_param (); | |
| if (lifetime_param.is_error ()) | |
| { | |
| // not an error as only way to exit if trailing comma | |
| break; | |
| } | |
| lifetime_params.push_back (std::move (lifetime_param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| lifetime_params.shrink_to_fit (); | |
| return lifetime_params; | |
| } | |
| /* Parses lifetime generic parameters (objects). Will also consume any | |
| * trailing comma. Has extra is_end_token predicate checking. | |
| * TODO: is this best solution? implements most of the same algorithm. */ | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<AST::LifetimeParam> | |
| Parser<ManagedTokenSource>::parse_lifetime_params_objs ( | |
| EndTokenPred is_end_token) | |
| { | |
| std::vector<AST::LifetimeParam> lifetime_params; | |
| while (!is_end_token (lexer.peek_token ()->get_id ())) | |
| { | |
| AST::LifetimeParam lifetime_param = parse_lifetime_param (); | |
| if (lifetime_param.is_error ()) | |
| { | |
| /* TODO: is it worth throwing away all lifetime params just because | |
| * one failed? */ | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse lifetime param in lifetime params"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| lifetime_params.push_back (std::move (lifetime_param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| lifetime_params.shrink_to_fit (); | |
| return lifetime_params; | |
| } | |
| /* Parses a sequence of a certain grammar rule in object form (not pointer or | |
| * smart pointer), delimited by commas and ending when 'is_end_token' is | |
| * satisfied (templated). Will also consume any trailing comma. | |
| * FIXME: this cannot be used due to member function pointer problems (i.e. | |
| * parsing_function cannot be specified properly) */ | |
| template <typename ManagedTokenSource> | |
| template <typename ParseFunction, typename EndTokenPred> | |
| auto | |
| Parser<ManagedTokenSource>::parse_non_ptr_sequence ( | |
| ParseFunction parsing_function, EndTokenPred is_end_token, | |
| std::string error_msg) -> std::vector<decltype (parsing_function ())> | |
| { | |
| std::vector<decltype (parsing_function ())> params; | |
| while (!is_end_token (lexer.peek_token ()->get_id ())) | |
| { | |
| auto param = parsing_function (); | |
| if (param.is_error ()) | |
| { | |
| // TODO: is it worth throwing away all params just because one | |
| // failed? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| std::move (error_msg)); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| params.shrink_to_fit (); | |
| return params; | |
| } | |
| /* Parses a single lifetime generic parameter (not including comma). */ | |
| template <typename ManagedTokenSource> | |
| AST::LifetimeParam | |
| Parser<ManagedTokenSource>::parse_lifetime_param () | |
| { | |
| // parse outer attribute, which is optional and may not exist | |
| AST::Attribute outer_attr = parse_outer_attribute (); | |
| // save lifetime token - required | |
| const_TokenPtr lifetime_tok = lexer.peek_token (); | |
| if (lifetime_tok->get_id () != LIFETIME) | |
| { | |
| // if lifetime is missing, must not be a lifetime param, so return null | |
| return AST::LifetimeParam::create_error (); | |
| } | |
| lexer.skip_token (); | |
| /* TODO: does this always create a named lifetime? or can a different type | |
| * be made? */ | |
| AST::Lifetime lifetime (AST::Lifetime::NAMED, lifetime_tok->get_str (), | |
| lifetime_tok->get_locus ()); | |
| // parse lifetime bounds, if it exists | |
| std::vector<AST::Lifetime> lifetime_bounds; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| // parse lifetime bounds | |
| lifetime_bounds = parse_lifetime_bounds (); | |
| // TODO: have end token passed in? | |
| } | |
| return AST::LifetimeParam (std::move (lifetime), std::move (lifetime_bounds), | |
| std::move (outer_attr), | |
| lifetime_tok->get_locus ()); | |
| } | |
| // Parses type generic parameters. Will also consume any trailing comma. | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::TypeParam>> | |
| Parser<ManagedTokenSource>::parse_type_params () | |
| { | |
| std::vector<std::unique_ptr<AST::TypeParam>> type_params; | |
| // infinite loop with break on failure as no info on ending token | |
| while (true) | |
| { | |
| std::unique_ptr<AST::TypeParam> type_param = parse_type_param (); | |
| if (type_param == nullptr) | |
| { | |
| // break if fails to parse | |
| break; | |
| } | |
| type_params.push_back (std::move (type_param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| type_params.shrink_to_fit (); | |
| return type_params; | |
| } | |
| // Parses type generic parameters. Will also consume any trailing comma. | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<std::unique_ptr<AST::TypeParam>> | |
| Parser<ManagedTokenSource>::parse_type_params (EndTokenPred is_end_token) | |
| { | |
| std::vector<std::unique_ptr<AST::TypeParam>> type_params; | |
| while (!is_end_token (lexer.peek_token ()->get_id ())) | |
| { | |
| std::unique_ptr<AST::TypeParam> type_param = parse_type_param (); | |
| if (type_param == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type param in type params"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| type_params.push_back (std::move (type_param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip commas, including trailing commas | |
| lexer.skip_token (); | |
| } | |
| type_params.shrink_to_fit (); | |
| return type_params; | |
| /* TODO: this shares most code with parse_lifetime_params - good place to | |
| * use template (i.e. parse_non_ptr_sequence if doable) */ | |
| } | |
| /* Parses a single type (generic) parameter, not including commas. May change | |
| * to return value. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeParam> | |
| Parser<ManagedTokenSource>::parse_type_param () | |
| { | |
| // parse outer attribute, which is optional and may not exist | |
| AST::Attribute outer_attr = parse_outer_attribute (); | |
| const_TokenPtr identifier_tok = lexer.peek_token (); | |
| if (identifier_tok->get_id () != IDENTIFIER) | |
| { | |
| // return null as type param can't exist without this required | |
| // identifier | |
| return nullptr; | |
| } | |
| // TODO: create identifier from identifier token | |
| Identifier ident = identifier_tok->get_str (); | |
| lexer.skip_token (); | |
| // parse type param bounds (if they exist) | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| // parse type param bounds, which may or may not exist | |
| type_param_bounds = parse_type_param_bounds (); | |
| } | |
| // parse type (if it exists) | |
| std::unique_ptr<AST::Type> type = nullptr; | |
| if (lexer.peek_token ()->get_id () == EQUAL) | |
| { | |
| lexer.skip_token (); | |
| // parse type (now required) | |
| type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type in type param"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| } | |
| return std::unique_ptr<AST::TypeParam> ( | |
| new AST::TypeParam (std::move (ident), identifier_tok->get_locus (), | |
| std::move (type_param_bounds), std::move (type), | |
| std::move (outer_attr))); | |
| } | |
| /* Parses regular (i.e. non-generic) parameters in functions or methods. Also | |
| * has end token handling. */ | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<AST::FunctionParam> | |
| Parser<ManagedTokenSource>::parse_function_params (EndTokenPred is_end_token) | |
| { | |
| std::vector<AST::FunctionParam> params; | |
| if (is_end_token (lexer.peek_token ()->get_id ())) | |
| return params; | |
| AST::FunctionParam initial_param = parse_function_param (); | |
| // Return empty parameter list if no parameter there | |
| if (initial_param.is_error ()) | |
| { | |
| // TODO: is this an error? | |
| return params; | |
| } | |
| params.push_back (std::move (initial_param)); | |
| // maybe think of a better control structure here - do-while with an initial | |
| // error state? basically, loop through parameter list until can't find any | |
| // more params | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| // skip comma if applies | |
| lexer.skip_token (); | |
| // TODO: strictly speaking, shouldn't there be no trailing comma? | |
| if (is_end_token (lexer.peek_token ()->get_id ())) | |
| break; | |
| // now, as right paren would break, function param is required | |
| AST::FunctionParam param = parse_function_param (); | |
| if (param.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse function param (in function params)"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return std::vector<AST::FunctionParam> (); | |
| } | |
| params.push_back (std::move (param)); | |
| t = lexer.peek_token (); | |
| } | |
| params.shrink_to_fit (); | |
| return params; | |
| } | |
| /* Parses a single regular (i.e. non-generic) parameter in a function or | |
| * method, i.e. the "name: type" bit. Also handles it not existing. */ | |
| template <typename ManagedTokenSource> | |
| AST::FunctionParam | |
| Parser<ManagedTokenSource>::parse_function_param () | |
| { | |
| // parse outer attributes if they exist | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // TODO: should saved location be at start of outer attributes or pattern? | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| std::unique_ptr<AST::Pattern> param_pattern = parse_pattern (); | |
| // create error function param if it doesn't exist | |
| if (param_pattern == nullptr) | |
| { | |
| // skip after something | |
| return AST::FunctionParam::create_error (); | |
| } | |
| if (!skip_token (COLON)) | |
| { | |
| // skip after something | |
| return AST::FunctionParam::create_error (); | |
| } | |
| std::unique_ptr<AST::Type> param_type = parse_type (); | |
| if (param_type == nullptr) | |
| { | |
| // skip? | |
| return AST::FunctionParam::create_error (); | |
| } | |
| return AST::FunctionParam (std::move (param_pattern), std::move (param_type), | |
| std::move (outer_attrs), locus); | |
| } | |
| /* Parses a function or method return type syntactical construction. Also | |
| * handles a function return type not existing. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Type> | |
| Parser<ManagedTokenSource>::parse_function_return_type () | |
| { | |
| if (lexer.peek_token ()->get_id () != RETURN_TYPE) | |
| return nullptr; | |
| // skip return type, as it now obviously exists | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| return type; | |
| } | |
| /* Parses a "where clause" (in a function, struct, method, etc.). Also handles | |
| * a where clause not existing, in which it will return | |
| * WhereClause::create_empty(), which can be checked via | |
| * WhereClause::is_empty(). */ | |
| template <typename ManagedTokenSource> | |
| AST::WhereClause | |
| Parser<ManagedTokenSource>::parse_where_clause () | |
| { | |
| const_TokenPtr where_tok = lexer.peek_token (); | |
| if (where_tok->get_id () != WHERE) | |
| { | |
| // where clause doesn't exist, so create empty one | |
| return AST::WhereClause::create_empty (); | |
| } | |
| lexer.skip_token (); | |
| /* parse where clause items - this is not a separate rule in the reference | |
| * so won't be here */ | |
| std::vector<std::unique_ptr<AST::WhereClauseItem>> where_clause_items; | |
| /* HACK: where clauses end with a right curly or semicolon or equals in all | |
| * uses currently */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != LEFT_CURLY && t->get_id () != SEMICOLON | |
| && t->get_id () != EQUAL) | |
| { | |
| std::unique_ptr<AST::WhereClauseItem> where_clause_item | |
| = parse_where_clause_item (); | |
| if (where_clause_item == nullptr) | |
| { | |
| Error error (t->get_locus (), "failed to parse where clause item"); | |
| add_error (std::move (error)); | |
| return AST::WhereClause::create_empty (); | |
| } | |
| where_clause_items.push_back (std::move (where_clause_item)); | |
| // also skip comma if it exists | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| where_clause_items.shrink_to_fit (); | |
| return AST::WhereClause (std::move (where_clause_items)); | |
| } | |
| /* Parses a where clause item (lifetime or type bound). Does not parse any | |
| * commas. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::WhereClauseItem> | |
| Parser<ManagedTokenSource>::parse_where_clause_item () | |
| { | |
| // shitty cheat way of determining lifetime or type bound - test for | |
| // lifetime | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (t->get_id () == LIFETIME) | |
| return parse_lifetime_where_clause_item (); | |
| else | |
| return parse_type_bound_where_clause_item (); | |
| } | |
| // Parses a lifetime where clause item. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::LifetimeWhereClauseItem> | |
| Parser<ManagedTokenSource>::parse_lifetime_where_clause_item () | |
| { | |
| AST::Lifetime lifetime = parse_lifetime (); | |
| if (lifetime.is_error ()) | |
| { | |
| // TODO: error here? | |
| return nullptr; | |
| } | |
| if (!skip_token (COLON)) | |
| { | |
| // TODO: skip after somewhere | |
| return nullptr; | |
| } | |
| std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds (); | |
| // TODO: have end token passed in? | |
| Location locus = lifetime.get_locus (); | |
| return std::unique_ptr<AST::LifetimeWhereClauseItem> ( | |
| new AST::LifetimeWhereClauseItem (std::move (lifetime), | |
| std::move (lifetime_bounds), locus)); | |
| } | |
| // Parses a type bound where clause item. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeBoundWhereClauseItem> | |
| Parser<ManagedTokenSource>::parse_type_bound_where_clause_item () | |
| { | |
| // parse for lifetimes, if it exists | |
| std::vector<AST::LifetimeParam> for_lifetimes; | |
| if (lexer.peek_token ()->get_id () == FOR) | |
| for_lifetimes = parse_for_lifetimes (); | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| return nullptr; | |
| } | |
| if (!skip_token (COLON)) | |
| { | |
| // TODO: skip after somewhere | |
| return nullptr; | |
| } | |
| // parse type param bounds if they exist | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds | |
| = parse_type_param_bounds (); | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| return std::unique_ptr<AST::TypeBoundWhereClauseItem> ( | |
| new AST::TypeBoundWhereClauseItem (std::move (for_lifetimes), | |
| std::move (type), | |
| std::move (type_param_bounds), locus)); | |
| } | |
| // Parses a for lifetimes clause, including the for keyword and angle | |
| // brackets. | |
| template <typename ManagedTokenSource> | |
| std::vector<AST::LifetimeParam> | |
| Parser<ManagedTokenSource>::parse_for_lifetimes () | |
| { | |
| std::vector<AST::LifetimeParam> params; | |
| if (!skip_token (FOR)) | |
| { | |
| // skip after somewhere? | |
| return params; | |
| } | |
| if (!skip_token (LEFT_ANGLE)) | |
| { | |
| // skip after somewhere? | |
| return params; | |
| } | |
| /* cannot specify end token due to parsing problems with '>' tokens being | |
| * nested */ | |
| params = parse_lifetime_params_objs (is_right_angle_tok); | |
| if (!skip_generics_right_angle ()) | |
| { | |
| // DEBUG | |
| rust_debug ("failed to skip generics right angle after (supposedly) " | |
| "finished parsing where clause items"); | |
| // ok, well this gets called. | |
| // skip after somewhere? | |
| return params; | |
| } | |
| return params; | |
| } | |
| // Parses type parameter bounds in where clause or generic arguments. | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> | |
| Parser<ManagedTokenSource>::parse_type_param_bounds () | |
| { | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds; | |
| std::unique_ptr<AST::TypeParamBound> initial_bound | |
| = parse_type_param_bound (); | |
| // quick exit if null | |
| if (initial_bound == nullptr) | |
| { | |
| /* error? type param bounds must have at least one term, but are bounds | |
| * optional? */ | |
| return type_param_bounds; | |
| } | |
| type_param_bounds.push_back (std::move (initial_bound)); | |
| while (lexer.peek_token ()->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| /* not an error: bound is allowed to be null as trailing plus is | |
| * allowed */ | |
| return type_param_bounds; | |
| } | |
| type_param_bounds.push_back (std::move (bound)); | |
| } | |
| type_param_bounds.shrink_to_fit (); | |
| return type_param_bounds; | |
| } | |
| /* Parses type parameter bounds in where clause or generic arguments, with end | |
| * token handling. */ | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> | |
| Parser<ManagedTokenSource>::parse_type_param_bounds (EndTokenPred is_end_token) | |
| { | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds; | |
| std::unique_ptr<AST::TypeParamBound> initial_bound | |
| = parse_type_param_bound (); | |
| // quick exit if null | |
| if (initial_bound == nullptr) | |
| { | |
| /* error? type param bounds must have at least one term, but are bounds | |
| * optional? */ | |
| return type_param_bounds; | |
| } | |
| type_param_bounds.push_back (std::move (initial_bound)); | |
| while (lexer.peek_token ()->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| // break if end token character | |
| if (is_end_token (lexer.peek_token ()->get_id ())) | |
| break; | |
| std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| // TODO how wise is it to ditch all bounds if only one failed? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type param bound in type param bounds"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| type_param_bounds.push_back (std::move (bound)); | |
| } | |
| type_param_bounds.shrink_to_fit (); | |
| return type_param_bounds; | |
| } | |
| /* Parses a single type parameter bound in a where clause or generic argument. | |
| * Does not parse the '+' between arguments. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeParamBound> | |
| Parser<ManagedTokenSource>::parse_type_param_bound () | |
| { | |
| // shitty cheat way of determining lifetime or trait bound - test for | |
| // lifetime | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LIFETIME: | |
| return std::unique_ptr<AST::Lifetime> ( | |
| new AST::Lifetime (parse_lifetime ())); | |
| case LEFT_PAREN: | |
| case QUESTION_MARK: | |
| case FOR: | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| return parse_trait_bound (); | |
| default: | |
| // don't error - assume this is fine TODO | |
| return nullptr; | |
| } | |
| } | |
| // Parses a trait bound type param bound. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TraitBound> | |
| Parser<ManagedTokenSource>::parse_trait_bound () | |
| { | |
| bool has_parens = false; | |
| bool has_question_mark = false; | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // handle trait bound being in parentheses | |
| if (lexer.peek_token ()->get_id () == LEFT_PAREN) | |
| { | |
| has_parens = true; | |
| lexer.skip_token (); | |
| } | |
| // handle having question mark (optional) | |
| if (lexer.peek_token ()->get_id () == QUESTION_MARK) | |
| { | |
| has_question_mark = true; | |
| lexer.skip_token (); | |
| } | |
| /* parse for lifetimes, if it exists (although empty for lifetimes is ok to | |
| * handle this) */ | |
| std::vector<AST::LifetimeParam> for_lifetimes; | |
| if (lexer.peek_token ()->get_id () == FOR) | |
| for_lifetimes = parse_for_lifetimes (); | |
| // handle TypePath | |
| AST::TypePath type_path = parse_type_path (); | |
| // handle closing parentheses | |
| if (has_parens) | |
| { | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| } | |
| return std::unique_ptr<AST::TraitBound> ( | |
| new AST::TraitBound (std::move (type_path), locus, has_parens, | |
| has_question_mark, std::move (for_lifetimes))); | |
| } | |
| // Parses lifetime bounds. | |
| template <typename ManagedTokenSource> | |
| std::vector<AST::Lifetime> | |
| Parser<ManagedTokenSource>::parse_lifetime_bounds () | |
| { | |
| std::vector<AST::Lifetime> lifetime_bounds; | |
| while (true) | |
| { | |
| AST::Lifetime lifetime = parse_lifetime (); | |
| // quick exit for parsing failure | |
| if (lifetime.is_error ()) | |
| break; | |
| lifetime_bounds.push_back (std::move (lifetime)); | |
| /* plus is maybe not allowed at end - spec defines it weirdly, so | |
| * assuming allowed at end */ | |
| if (lexer.peek_token ()->get_id () != PLUS) | |
| break; | |
| lexer.skip_token (); | |
| } | |
| lifetime_bounds.shrink_to_fit (); | |
| return lifetime_bounds; | |
| } | |
| // Parses lifetime bounds, with added check for ending token. | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<AST::Lifetime> | |
| Parser<ManagedTokenSource>::parse_lifetime_bounds (EndTokenPred is_end_token) | |
| { | |
| std::vector<AST::Lifetime> lifetime_bounds; | |
| while (!is_end_token (lexer.peek_token ()->get_id ())) | |
| { | |
| AST::Lifetime lifetime = parse_lifetime (); | |
| if (lifetime.is_error ()) | |
| { | |
| /* TODO: is it worth throwing away all lifetime bound info just | |
| * because one failed? */ | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse lifetime in lifetime bounds"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| lifetime_bounds.push_back (std::move (lifetime)); | |
| /* plus is maybe not allowed at end - spec defines it weirdly, so | |
| * assuming allowed at end */ | |
| if (lexer.peek_token ()->get_id () != PLUS) | |
| break; | |
| lexer.skip_token (); | |
| } | |
| lifetime_bounds.shrink_to_fit (); | |
| return lifetime_bounds; | |
| } | |
| /* Parses a lifetime token (named, 'static, or '_). Also handles lifetime not | |
| * existing. */ | |
| template <typename ManagedTokenSource> | |
| AST::Lifetime | |
| Parser<ManagedTokenSource>::parse_lifetime () | |
| { | |
| const_TokenPtr lifetime_tok = lexer.peek_token (); | |
| Location locus = lifetime_tok->get_locus (); | |
| // create error lifetime if doesn't exist | |
| if (lifetime_tok->get_id () != LIFETIME) | |
| { | |
| return AST::Lifetime::error (); | |
| } | |
| lexer.skip_token (); | |
| std::string lifetime_ident = lifetime_tok->get_str (); | |
| if (lifetime_ident == "'static") | |
| { | |
| return AST::Lifetime (AST::Lifetime::STATIC, "", locus); | |
| } | |
| else if (lifetime_ident == "'_") | |
| { | |
| return AST::Lifetime (AST::Lifetime::WILDCARD, "", locus); | |
| } | |
| else | |
| { | |
| return AST::Lifetime (AST::Lifetime::NAMED, std::move (lifetime_ident), | |
| locus); | |
| } | |
| } | |
| // Parses a "type alias" (typedef) item. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeAlias> | |
| Parser<ManagedTokenSource>::parse_type_alias (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (TYPE); | |
| // TODO: use this token for identifier when finished that | |
| const_TokenPtr alias_name_tok = expect_token (IDENTIFIER); | |
| if (alias_name_tok == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse identifier in type alias"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| Identifier alias_name = alias_name_tok->get_str (); | |
| // parse generic params, which may not exist | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // parse where clause, which may not exist | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (EQUAL)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| std::unique_ptr<AST::Type> type_to_alias = parse_type (); | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // should be skipping past this, not the next line | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TypeAlias> ( | |
| new AST::TypeAlias (std::move (alias_name), std::move (generic_params), | |
| std::move (where_clause), std::move (type_to_alias), | |
| std::move (vis), std::move (outer_attrs), locus)); | |
| } | |
| // Parse a struct item AST node. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Struct> | |
| Parser<ManagedTokenSource>::parse_struct (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| /* TODO: determine best way to parse the proper struct vs tuple struct - | |
| * share most of initial constructs so lookahead might be impossible, and if | |
| * not probably too expensive. Best way is probably unified parsing for the | |
| * initial parts and then pass them in as params to more derived functions. | |
| * Alternatively, just parse everything in this one function - do this if | |
| * function not too long. */ | |
| /* Proper struct <- 'struct' IDENTIFIER generic_params? where_clause? ( '{' | |
| * struct_fields? '}' | ';' ) */ | |
| /* Tuple struct <- 'struct' IDENTIFIER generic_params? '(' tuple_fields? ')' | |
| * where_clause? ';' */ | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (STRUCT_TOK); | |
| // parse struct name | |
| const_TokenPtr name_tok = expect_token (IDENTIFIER); | |
| if (name_tok == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse struct or tuple struct identifier"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| Identifier struct_name = name_tok->get_str (); | |
| // parse generic params, which may or may not exist | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // branch on next token - determines whether proper struct or tuple struct | |
| if (lexer.peek_token ()->get_id () == LEFT_PAREN) | |
| { | |
| // tuple struct | |
| // skip left parenthesis | |
| lexer.skip_token (); | |
| // parse tuple fields | |
| std::vector<AST::TupleField> tuple_fields; | |
| // Might be empty tuple for unit tuple struct. | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| tuple_fields = std::vector<AST::TupleField> (); | |
| else | |
| tuple_fields = parse_tuple_fields (); | |
| // tuple parameters must have closing parenthesis | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse where clause, which is optional | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // can't skip after semicolon because it's meant to be here | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TupleStruct> ( | |
| new AST::TupleStruct (std::move (tuple_fields), std::move (struct_name), | |
| std::move (generic_params), | |
| std::move (where_clause), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // assume it is a proper struct being parsed and continue outside of switch | |
| // - label only here to suppress warning | |
| // parse where clause, which is optional | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| // branch on next token - determines whether struct is a unit struct | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_CURLY: { | |
| // struct with body | |
| // skip curly bracket | |
| lexer.skip_token (); | |
| // parse struct fields, if any | |
| std::vector<AST::StructField> struct_fields | |
| = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; }); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StructStruct> (new AST::StructStruct ( | |
| std::move (struct_fields), std::move (struct_name), | |
| std::move (generic_params), std::move (where_clause), false, | |
| std::move (vis), std::move (outer_attrs), locus)); | |
| } | |
| case SEMICOLON: | |
| // unit struct declaration | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::StructStruct> ( | |
| new AST::StructStruct (std::move (struct_name), | |
| std::move (generic_params), | |
| std::move (where_clause), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in struct declaration", | |
| t->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| } | |
| // Parses struct fields in struct declarations. | |
| template <typename ManagedTokenSource> | |
| std::vector<AST::StructField> | |
| Parser<ManagedTokenSource>::parse_struct_fields () | |
| { | |
| std::vector<AST::StructField> fields; | |
| AST::StructField initial_field = parse_struct_field (); | |
| // Return empty field list if no field there | |
| if (initial_field.is_error ()) | |
| return fields; | |
| fields.push_back (std::move (initial_field)); | |
| while (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| AST::StructField field = parse_struct_field (); | |
| if (field.is_error ()) | |
| { | |
| // would occur with trailing comma, so allowed | |
| break; | |
| } | |
| fields.push_back (std::move (field)); | |
| } | |
| fields.shrink_to_fit (); | |
| return fields; | |
| // TODO: template if possible (parse_non_ptr_seq) | |
| } | |
| // Parses struct fields in struct declarations. | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<AST::StructField> | |
| Parser<ManagedTokenSource>::parse_struct_fields (EndTokenPred is_end_tok) | |
| { | |
| std::vector<AST::StructField> fields; | |
| AST::StructField initial_field = parse_struct_field (); | |
| // Return empty field list if no field there | |
| if (initial_field.is_error ()) | |
| return fields; | |
| fields.push_back (std::move (initial_field)); | |
| while (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| if (is_end_tok (lexer.peek_token ()->get_id ())) | |
| break; | |
| AST::StructField field = parse_struct_field (); | |
| if (field.is_error ()) | |
| { | |
| /* TODO: should every field be ditched just because one couldn't be | |
| * parsed? */ | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse struct field in struct fields"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| fields.push_back (std::move (field)); | |
| } | |
| fields.shrink_to_fit (); | |
| return fields; | |
| // TODO: template if possible (parse_non_ptr_seq) | |
| } | |
| // Parses a single struct field (in a struct definition). Does not parse | |
| // commas. | |
| template <typename ManagedTokenSource> | |
| AST::StructField | |
| Parser<ManagedTokenSource>::parse_struct_field () | |
| { | |
| // parse outer attributes, if they exist | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parse visibility, if it exists | |
| AST::Visibility vis = parse_visibility (); | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse field name | |
| const_TokenPtr field_name_tok = lexer.peek_token (); | |
| if (field_name_tok->get_id () != IDENTIFIER) | |
| { | |
| // if not identifier, assumes there is no struct field and exits - not | |
| // necessarily error | |
| return AST::StructField::create_error (); | |
| } | |
| Identifier field_name = field_name_tok->get_str (); | |
| lexer.skip_token (); | |
| if (!skip_token (COLON)) | |
| { | |
| // skip after somewhere? | |
| return AST::StructField::create_error (); | |
| } | |
| // parse field type - this is required | |
| std::unique_ptr<AST::Type> field_type = parse_type (); | |
| if (field_type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in struct field definition"); | |
| add_error (std::move (error)); | |
| // skip after somewhere | |
| return AST::StructField::create_error (); | |
| } | |
| return AST::StructField (std::move (field_name), std::move (field_type), | |
| std::move (vis), locus, std::move (outer_attrs)); | |
| } | |
| // Parses tuple fields in tuple/tuple struct declarations. | |
| template <typename ManagedTokenSource> | |
| std::vector<AST::TupleField> | |
| Parser<ManagedTokenSource>::parse_tuple_fields () | |
| { | |
| std::vector<AST::TupleField> fields; | |
| AST::TupleField initial_field = parse_tuple_field (); | |
| // Return empty field list if no field there | |
| if (initial_field.is_error ()) | |
| { | |
| return fields; | |
| } | |
| fields.push_back (std::move (initial_field)); | |
| // maybe think of a better control structure here - do-while with an initial | |
| // error state? basically, loop through field list until can't find any more | |
| // params HACK: all current syntax uses of tuple fields have them ending | |
| // with a right paren token | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| // skip comma if applies - e.g. trailing comma | |
| lexer.skip_token (); | |
| // break out due to right paren if it exists | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| { | |
| break; | |
| } | |
| AST::TupleField field = parse_tuple_field (); | |
| if (field.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse tuple field in tuple fields"); | |
| add_error (std::move (error)); | |
| return std::vector<AST::TupleField> (); | |
| } | |
| fields.push_back (std::move (field)); | |
| t = lexer.peek_token (); | |
| } | |
| fields.shrink_to_fit (); | |
| return fields; | |
| // TODO: this shares basically all code with function params and struct | |
| // fields | |
| // - templates? | |
| } | |
| /* Parses a single tuple struct field in a tuple struct definition. Does not | |
| * parse commas. */ | |
| template <typename ManagedTokenSource> | |
| AST::TupleField | |
| Parser<ManagedTokenSource>::parse_tuple_field () | |
| { | |
| // parse outer attributes if they exist | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parse visibility if it exists | |
| AST::Visibility vis = parse_visibility (); | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse type, which is required | |
| std::unique_ptr<AST::Type> field_type = parse_type (); | |
| if (field_type == nullptr) | |
| { | |
| // error if null | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in tuple struct field"); | |
| add_error (std::move (error)); | |
| // skip after something | |
| return AST::TupleField::create_error (); | |
| } | |
| return AST::TupleField (std::move (field_type), std::move (vis), locus, | |
| std::move (outer_attrs)); | |
| } | |
| // Parses a Rust "enum" tagged union item definition. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Enum> | |
| Parser<ManagedTokenSource>::parse_enum (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (ENUM_TOK); | |
| // parse enum name | |
| const_TokenPtr enum_name_tok = expect_token (IDENTIFIER); | |
| if (enum_name_tok == nullptr) | |
| return nullptr; | |
| Identifier enum_name = enum_name_tok->get_str (); | |
| // parse generic params (of enum container, not enum variants) if they exist | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // parse where clause if it exists | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // parse actual enum variant definitions | |
| std::vector<std::unique_ptr<AST::EnumItem>> enum_items | |
| = parse_enum_items ([] (TokenId id) { return id == RIGHT_CURLY; }); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::Enum> ( | |
| new AST::Enum (std::move (enum_name), std::move (vis), | |
| std::move (generic_params), std::move (where_clause), | |
| std::move (enum_items), std::move (outer_attrs), locus)); | |
| } | |
| // Parses the enum variants inside an enum definiton. | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::EnumItem>> | |
| Parser<ManagedTokenSource>::parse_enum_items () | |
| { | |
| std::vector<std::unique_ptr<AST::EnumItem>> items; | |
| std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item (); | |
| // Return empty item list if no field there | |
| if (initial_item == nullptr) | |
| return items; | |
| items.push_back (std::move (initial_item)); | |
| while (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::EnumItem> item = parse_enum_item (); | |
| if (item == nullptr) | |
| { | |
| // this would occur with a trailing comma, which is allowed | |
| break; | |
| } | |
| items.push_back (std::move (item)); | |
| } | |
| items.shrink_to_fit (); | |
| return items; | |
| /* TODO: use template if doable (parse_non_ptr_sequence) */ | |
| } | |
| // Parses the enum variants inside an enum definiton. | |
| template <typename ManagedTokenSource> | |
| template <typename EndTokenPred> | |
| std::vector<std::unique_ptr<AST::EnumItem>> | |
| Parser<ManagedTokenSource>::parse_enum_items (EndTokenPred is_end_tok) | |
| { | |
| std::vector<std::unique_ptr<AST::EnumItem>> items; | |
| std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item (); | |
| // Return empty item list if no field there | |
| if (initial_item == nullptr) | |
| return items; | |
| items.push_back (std::move (initial_item)); | |
| while (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| if (is_end_tok (lexer.peek_token ()->get_id ())) | |
| break; | |
| std::unique_ptr<AST::EnumItem> item = parse_enum_item (); | |
| if (item == nullptr) | |
| { | |
| /* TODO should this ignore all successfully parsed enum items just | |
| * because one failed? */ | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse enum item in enum items"); | |
| add_error (std::move (error)); | |
| return {}; | |
| } | |
| items.push_back (std::move (item)); | |
| } | |
| items.shrink_to_fit (); | |
| return items; | |
| /* TODO: use template if doable (parse_non_ptr_sequence) */ | |
| } | |
| /* Parses a single enum variant item in an enum definition. Does not parse | |
| * commas. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::EnumItem> | |
| Parser<ManagedTokenSource>::parse_enum_item () | |
| { | |
| // parse outer attributes if they exist | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parse visibility, which may or may not exist | |
| AST::Visibility vis = parse_visibility (); | |
| // parse name for enum item, which is required | |
| const_TokenPtr item_name_tok = lexer.peek_token (); | |
| if (item_name_tok->get_id () != IDENTIFIER) | |
| { | |
| // this may not be an error but it means there is no enum item here | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| Identifier item_name = item_name_tok->get_str (); | |
| // branch based on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_PAREN: { | |
| // tuple enum item | |
| lexer.skip_token (); | |
| std::vector<AST::TupleField> tuple_fields; | |
| // Might be empty tuple for unit tuple enum variant. | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| tuple_fields = std::vector<AST::TupleField> (); | |
| else | |
| tuple_fields = parse_tuple_fields (); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip after somewhere | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::EnumItemTuple> (new AST::EnumItemTuple ( | |
| std::move (item_name), std::move (vis), std::move (tuple_fields), | |
| std::move (outer_attrs), item_name_tok->get_locus ())); | |
| } | |
| case LEFT_CURLY: { | |
| // struct enum item | |
| lexer.skip_token (); | |
| std::vector<AST::StructField> struct_fields | |
| = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; }); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip after somewhere | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::EnumItemStruct> (new AST::EnumItemStruct ( | |
| std::move (item_name), std::move (vis), std::move (struct_fields), | |
| std::move (outer_attrs), item_name_tok->get_locus ())); | |
| } | |
| case EQUAL: { | |
| // discriminant enum item | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::Expr> discriminant_expr = parse_expr (); | |
| return std::unique_ptr<AST::EnumItemDiscriminant> ( | |
| new AST::EnumItemDiscriminant (std::move (item_name), std::move (vis), | |
| std::move (discriminant_expr), | |
| std::move (outer_attrs), | |
| item_name_tok->get_locus ())); | |
| } | |
| default: | |
| // regular enum with just an identifier | |
| return std::unique_ptr<AST::EnumItem> ( | |
| new AST::EnumItem (std::move (item_name), std::move (vis), | |
| std::move (outer_attrs), | |
| item_name_tok->get_locus ())); | |
| } | |
| } | |
| // Parses a C-style (and C-compat) untagged union declaration. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Union> | |
| Parser<ManagedTokenSource>::parse_union (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| /* hack - "weak keyword" by finding identifier called "union" (lookahead in | |
| * item switch) */ | |
| const_TokenPtr union_keyword = expect_token (IDENTIFIER); | |
| rust_assert (union_keyword->get_str () == "union"); | |
| Location locus = union_keyword->get_locus (); | |
| // parse actual union name | |
| const_TokenPtr union_name_tok = expect_token (IDENTIFIER); | |
| if (union_name_tok == nullptr) | |
| { | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| Identifier union_name = union_name_tok->get_str (); | |
| // parse optional generic parameters | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // parse optional where clause | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| /* parse union inner items as "struct fields" because hey, syntax reuse. | |
| * Spec said so. */ | |
| std::vector<AST::StructField> union_fields | |
| = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; }); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip after somewhere | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::Union> ( | |
| new AST::Union (std::move (union_name), std::move (vis), | |
| std::move (generic_params), std::move (where_clause), | |
| std::move (union_fields), std::move (outer_attrs), locus)); | |
| } | |
| /* Parses a "constant item" (compile-time constant to maybe "inline" | |
| * throughout the program - like constexpr). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ConstantItem> | |
| Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (CONST); | |
| /* get constant identifier - this is either a proper identifier or the _ | |
| * wildcard */ | |
| const_TokenPtr ident_tok = lexer.peek_token (); | |
| // make default identifier the underscore wildcard one | |
| std::string ident ("_"); | |
| switch (ident_tok->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| ident = ident_tok->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case UNDERSCORE: | |
| // do nothing - identifier is already "_" | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| add_error ( | |
| Error (ident_tok->get_locus (), | |
| "expected item name (identifier or %<_%>) in constant item " | |
| "declaration - found %qs", | |
| ident_tok->get_token_description ())); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| if (!skip_token (COLON)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse constant type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (!skip_token (EQUAL)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse constant expression (required) | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ConstantItem> ( | |
| new AST::ConstantItem (std::move (ident), std::move (vis), std::move (type), | |
| std::move (expr), std::move (outer_attrs), locus)); | |
| } | |
| // Parses a "static item" (static storage item, with 'static lifetime). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::StaticItem> | |
| Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (STATIC_TOK); | |
| // determine whether static item is mutable | |
| bool is_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| is_mut = true; | |
| lexer.skip_token (); | |
| } | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| if (!skip_token (COLON)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse static item type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (!skip_token (EQUAL)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse static item expression (required) | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // skip after somewhere | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StaticItem> ( | |
| new AST::StaticItem (std::move (ident), is_mut, std::move (type), | |
| std::move (expr), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a trait definition item, including unsafe ones. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Trait> | |
| Parser<ManagedTokenSource>::parse_trait (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| bool is_unsafe = false; | |
| if (lexer.peek_token ()->get_id () == UNSAFE) | |
| { | |
| is_unsafe = true; | |
| lexer.skip_token (); | |
| } | |
| skip_token (TRAIT); | |
| // parse trait name | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| // parse generic parameters (if they exist) | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // create placeholder type param bounds in case they don't exist | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds; | |
| // parse type param bounds (if they exist) | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| type_param_bounds = parse_type_param_bounds ( | |
| [] (TokenId id) { return id == WHERE || id == LEFT_CURLY; }); | |
| // type_param_bounds = parse_type_param_bounds (); | |
| } | |
| // parse where clause (if it exists) | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // parse inner attrs (if they exist) | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse trait items | |
| std::vector<std::unique_ptr<AST::TraitItem>> trait_items; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::TraitItem> trait_item = parse_trait_item (); | |
| if (trait_item == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse trait item in trait"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| trait_items.push_back (std::move (trait_item)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip after something | |
| return nullptr; | |
| } | |
| trait_items.shrink_to_fit (); | |
| return std::unique_ptr<AST::Trait> ( | |
| new AST::Trait (std::move (ident), is_unsafe, std::move (generic_params), | |
| std::move (type_param_bounds), std::move (where_clause), | |
| std::move (trait_items), std::move (vis), | |
| std::move (outer_attrs), std::move (inner_attrs), locus)); | |
| } | |
| // Parses a trait item used inside traits (not trait, the Item). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TraitItem> | |
| Parser<ManagedTokenSource>::parse_trait_item () | |
| { | |
| // parse outer attributes (if they exist) | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // lookahead to determine what type of trait item to parse | |
| const_TokenPtr tok = lexer.peek_token (); | |
| switch (tok->get_id ()) | |
| { | |
| case TYPE: | |
| return parse_trait_type (std::move (outer_attrs)); | |
| case CONST: | |
| // disambiguate with function qualifier | |
| if (lexer.peek_token (1)->get_id () == IDENTIFIER) | |
| { | |
| return parse_trait_const (std::move (outer_attrs)); | |
| } | |
| // else, fallthrough to function | |
| // TODO: find out how to disable gcc "implicit fallthrough" error | |
| gcc_fallthrough (); | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: { | |
| /* function and method can't be disambiguated by lookahead alone | |
| * (without a lot of work and waste), so either make a | |
| * "parse_trait_function_or_method" or parse here mostly and pass in | |
| * most parameters (or if short enough, parse whole thing here). */ | |
| // parse function and method here | |
| // parse function or method qualifiers | |
| AST::FunctionQualifiers qualifiers = parse_function_qualifiers (); | |
| skip_token (FN_TOK); | |
| // parse function or method name | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| // parse generic params | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| /* now for function vs method disambiguation - method has opening | |
| * "self" param */ | |
| AST::SelfParam self_param = parse_self_param (); | |
| /* FIXME: ensure that self param doesn't accidently consume tokens for | |
| * a function */ | |
| bool is_method = false; | |
| if (!self_param.is_error ()) | |
| { | |
| is_method = true; | |
| /* skip comma so function and method regular params can be parsed | |
| * in same way */ | |
| if (lexer.peek_token ()->get_id () == COMMA) | |
| lexer.skip_token (); | |
| } | |
| // parse trait function params | |
| std::vector<AST::FunctionParam> function_params | |
| = parse_function_params ( | |
| [] (TokenId id) { return id == RIGHT_PAREN; }); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // parse return type (optional) | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| // parse where clause (optional) | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| // parse semicolon or function definition (in block) | |
| const_TokenPtr t = lexer.peek_token (); | |
| std::unique_ptr<AST::BlockExpr> definition = nullptr; | |
| switch (t->get_id ()) | |
| { | |
| case SEMICOLON: | |
| lexer.skip_token (); | |
| // definition is already nullptr, so don't need to change it | |
| break; | |
| case LEFT_CURLY: | |
| definition = parse_block_expr (); | |
| /* FIXME: are these outer attributes meant to be passed into the | |
| * block? */ | |
| break; | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "expected %<;%> or definiton at the end of trait %s " | |
| "definition - found %qs instead", | |
| is_method ? "method" : "function", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| // do actual if instead of ternary for return value optimisation | |
| if (is_method) | |
| { | |
| AST::TraitMethodDecl method_decl (std::move (ident), | |
| std::move (qualifiers), | |
| std::move (generic_params), | |
| std::move (self_param), | |
| std::move (function_params), | |
| std::move (return_type), | |
| std::move (where_clause)); | |
| // TODO: does this (method_decl) need move? | |
| return std::unique_ptr<AST::TraitItemMethod> ( | |
| new AST::TraitItemMethod (std::move (method_decl), | |
| std::move (definition), | |
| std::move (outer_attrs), | |
| tok->get_locus ())); | |
| } | |
| else | |
| { | |
| AST::TraitFunctionDecl function_decl (std::move (ident), | |
| std::move (qualifiers), | |
| std::move (generic_params), | |
| std::move (function_params), | |
| std::move (return_type), | |
| std::move (where_clause)); | |
| return std::unique_ptr<AST::TraitItemFunc> (new AST::TraitItemFunc ( | |
| std::move (function_decl), std::move (definition), | |
| std::move (outer_attrs), tok->get_locus ())); | |
| } | |
| } | |
| default: { | |
| // TODO: try and parse macro invocation semi - if fails, maybe error. | |
| std::unique_ptr<AST::TraitItem> macro_invoc | |
| = parse_macro_invocation_semi (outer_attrs); | |
| if (macro_invoc == nullptr) | |
| { | |
| // TODO: error? | |
| return nullptr; | |
| } | |
| else | |
| { | |
| return macro_invoc; | |
| } | |
| /* FIXME: macro invocations can only start with certain tokens. be | |
| * more picky with these? */ | |
| } | |
| } | |
| } | |
| // Parse a typedef trait item. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TraitItemType> | |
| Parser<ManagedTokenSource>::parse_trait_type (AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (TYPE); | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds; | |
| // parse optional colon | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| // parse optional type param bounds | |
| bounds | |
| = parse_type_param_bounds ([] (TokenId id) { return id == SEMICOLON; }); | |
| // bounds = parse_type_param_bounds (); | |
| } | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // skip? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TraitItemType> ( | |
| new AST::TraitItemType (std::move (ident), std::move (bounds), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a constant trait item. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TraitItemConst> | |
| Parser<ManagedTokenSource>::parse_trait_const (AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (CONST); | |
| // parse constant item name | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| if (!skip_token (COLON)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse constant trait item type | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| // parse constant trait body expression, if it exists | |
| std::unique_ptr<AST::Expr> const_body = nullptr; | |
| if (lexer.peek_token ()->get_id () == EQUAL) | |
| { | |
| lexer.skip_token (); | |
| // expression must exist, so parse it | |
| const_body = parse_expr (); | |
| } | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // skip after something? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TraitItemConst> ( | |
| new AST::TraitItemConst (std::move (ident), std::move (type), | |
| std::move (const_body), std::move (outer_attrs), | |
| locus)); | |
| } | |
| /* Parses a struct "impl" item (both inherent impl and trait impl can be | |
| * parsed here), */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Impl> | |
| Parser<ManagedTokenSource>::parse_impl (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| /* Note that only trait impls are allowed to be unsafe. So if unsafe, it | |
| * must be a trait impl. However, this isn't enough for full disambiguation, | |
| * so don't branch here. */ | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| bool is_unsafe = false; | |
| if (lexer.peek_token ()->get_id () == UNSAFE) | |
| { | |
| lexer.skip_token (); | |
| is_unsafe = true; | |
| } | |
| if (!skip_token (IMPL)) | |
| { | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse generic params (shared by trait and inherent impls) | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // Again, trait impl-only feature, but optional one, so can be used for | |
| // branching yet. | |
| bool has_exclam = false; | |
| if (lexer.peek_token ()->get_id () == EXCLAM) | |
| { | |
| lexer.skip_token (); | |
| has_exclam = true; | |
| } | |
| /* FIXME: code that doesn't look shit for TypePath. Also, make sure this | |
| * doesn't parse too much and not work. */ | |
| AST::TypePath type_path = parse_type_path (); | |
| if (type_path.is_error () || lexer.peek_token ()->get_id () != FOR) | |
| { | |
| /* cannot parse type path (or not for token next, at least), so must be | |
| * inherent impl */ | |
| // hacky conversion of TypePath stack object to Type pointer | |
| std::unique_ptr<AST::Type> type = nullptr; | |
| if (!type_path.is_error ()) | |
| type = std::unique_ptr<AST::TypePath> ( | |
| new AST::TypePath (std::move (type_path))); | |
| else | |
| type = parse_type (); | |
| // Type is required, so error if null | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in inherent impl"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse optional where clause | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| // TODO: does this still skip properly? | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // parse inner attributes (optional) | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse inherent impl items | |
| std::vector<std::unique_ptr<AST::InherentImplItem>> impl_items; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::InherentImplItem> impl_item | |
| = parse_inherent_impl_item (); | |
| if (impl_item == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse inherent impl item in inherent impl"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| impl_items.push_back (std::move (impl_item)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip somewhere | |
| return nullptr; | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed inherent impl"); | |
| impl_items.shrink_to_fit (); | |
| return std::unique_ptr<AST::InherentImpl> (new AST::InherentImpl ( | |
| std::move (impl_items), std::move (generic_params), std::move (type), | |
| std::move (where_clause), std::move (vis), std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // type path must both be valid and next token is for, so trait impl | |
| if (!skip_token (FOR)) | |
| { | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse type | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| // ensure type is included as it is required | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in trait impl"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse optional where clause | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| // TODO: does this still skip properly? | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // parse inner attributes (optional) | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse trait impl items | |
| std::vector<std::unique_ptr<AST::TraitImplItem>> impl_items; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::TraitImplItem> impl_item | |
| = parse_trait_impl_item (); | |
| if (impl_item == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse trait impl item in trait impl"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| impl_items.push_back (std::move (impl_item)); | |
| t = lexer.peek_token (); | |
| // DEBUG | |
| rust_debug ("successfully parsed a trait impl item"); | |
| } | |
| // DEBUG | |
| rust_debug ("successfully finished trait impl items"); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip somewhere | |
| return nullptr; | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed trait impl"); | |
| impl_items.shrink_to_fit (); | |
| return std::unique_ptr<AST::TraitImpl> ( | |
| new AST::TraitImpl (std::move (type_path), is_unsafe, has_exclam, | |
| std::move (impl_items), std::move (generic_params), | |
| std::move (type), std::move (where_clause), | |
| std::move (vis), std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| } | |
| // Parses a single inherent impl item (item inside an inherent impl block). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::InherentImplItem> | |
| Parser<ManagedTokenSource>::parse_inherent_impl_item () | |
| { | |
| // parse outer attributes (if they exist) | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // TODO: cleanup - currently an unreadable mess | |
| // branch on next token: | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| // FIXME: Arthur: Do we need to some lookahead here? | |
| return parse_macro_invocation_semi (outer_attrs); | |
| case SUPER: | |
| case SELF: | |
| case CRATE: | |
| case PUB: { | |
| // visibility, so not a macro invocation semi - must be constant, | |
| // function, or method | |
| AST::Visibility vis = parse_visibility (); | |
| // TODO: is a recursive call to parse_inherent_impl_item better? | |
| switch (lexer.peek_token ()->get_id ()) | |
| { | |
| case EXTERN_TOK: | |
| case UNSAFE: | |
| case FN_TOK: | |
| // function or method | |
| return parse_inherent_impl_function_or_method (std::move (vis), | |
| std::move ( | |
| outer_attrs)); | |
| case CONST: | |
| // lookahead to resolve production - could be function/method or | |
| // const item | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| case UNDERSCORE: | |
| return parse_const_item (std::move (vis), | |
| std::move (outer_attrs)); | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_inherent_impl_function_or_method (std::move (vis), | |
| std::move ( | |
| outer_attrs)); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in some sort of const " | |
| "item in inherent impl", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unrecognised token %qs for item in inherent impl", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| } | |
| case EXTERN_TOK: | |
| case UNSAFE: | |
| case FN_TOK: | |
| // function or method | |
| return parse_inherent_impl_function_or_method ( | |
| AST::Visibility::create_private (), std::move (outer_attrs)); | |
| case CONST: | |
| /* lookahead to resolve production - could be function/method or const | |
| * item */ | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| case UNDERSCORE: | |
| return parse_const_item (AST::Visibility::create_private (), | |
| std::move (outer_attrs)); | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_inherent_impl_function_or_method ( | |
| AST::Visibility::create_private (), std::move (outer_attrs)); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in some sort of const item " | |
| "in inherent impl", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| gcc_unreachable (); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs for item in inherent impl", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| } | |
| /* For internal use only by parse_inherent_impl_item() - splits giant method | |
| * into smaller ones and prevents duplication of logic. Strictly, this parses | |
| * a function or method item inside an inherent impl item block. */ | |
| // TODO: make this a templated function with "return type" as type param - | |
| // InherentImplItem is this specialisation of the template while TraitImplItem | |
| // will be the other. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::InherentImplItem> | |
| Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method ( | |
| AST::Visibility vis, AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse function or method qualifiers | |
| AST::FunctionQualifiers qualifiers = parse_function_qualifiers (); | |
| skip_token (FN_TOK); | |
| // parse function or method name | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| // parse generic params | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // now for function vs method disambiguation - method has opening "self" | |
| // param | |
| AST::SelfParam self_param = parse_self_param (); | |
| /* FIXME: ensure that self param doesn't accidently consume tokens for a | |
| * function one idea is to lookahead up to 4 tokens to see whether self is | |
| * one of them */ | |
| bool is_method = false; | |
| if (!self_param.is_error ()) | |
| { | |
| is_method = true; | |
| /* skip comma so function and method regular params can be parsed in | |
| * same way */ | |
| if (lexer.peek_token ()->get_id () == COMMA) | |
| lexer.skip_token (); | |
| } | |
| // parse trait function params | |
| std::vector<AST::FunctionParam> function_params | |
| = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; }); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // parse return type (optional) | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| // parse where clause (optional) | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| // parse function definition (in block) - semicolon not allowed | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "%s declaration in inherent impl not allowed - must have " | |
| "a definition", | |
| is_method ? "method" : "function"); | |
| add_error (std::move (error)); | |
| lexer.skip_token (); | |
| return nullptr; | |
| } | |
| std::unique_ptr<AST::BlockExpr> body = parse_block_expr (); | |
| if (body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse definition in inherent impl %s definition", | |
| is_method ? "method" : "function"); | |
| add_error (std::move (error)); | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // do actual if instead of ternary for return value optimisation | |
| if (is_method) | |
| { | |
| return std::unique_ptr<AST::Method> ( | |
| new AST::Method (std::move (ident), std::move (qualifiers), | |
| std::move (generic_params), std::move (self_param), | |
| std::move (function_params), std::move (return_type), | |
| std::move (where_clause), std::move (body), | |
| std::move (vis), std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| return std::unique_ptr<AST::Function> ( | |
| new AST::Function (std::move (ident), std::move (qualifiers), | |
| std::move (generic_params), | |
| std::move (function_params), std::move (return_type), | |
| std::move (where_clause), std::move (body), | |
| std::move (vis), std::move (outer_attrs), locus)); | |
| } | |
| } | |
| // Parses a single trait impl item (item inside a trait impl block). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TraitImplItem> | |
| Parser<ManagedTokenSource>::parse_trait_impl_item () | |
| { | |
| // parse outer attributes (if they exist) | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // TODO: clean this function up, it is basically unreadable hacks | |
| // branch on next token: | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| // these seem to be SimplePath tokens, so this is a macro invocation | |
| // semi | |
| return parse_macro_invocation_semi (std::move (outer_attrs)); | |
| case TYPE: | |
| return parse_type_alias (AST::Visibility::create_private (), | |
| std::move (outer_attrs)); | |
| case PUB: { | |
| // visibility, so not a macro invocation semi - must be constant, | |
| // function, or method | |
| AST::Visibility vis = parse_visibility (); | |
| // TODO: is a recursive call to parse_trait_impl_item better? | |
| switch (lexer.peek_token ()->get_id ()) | |
| { | |
| case TYPE: | |
| return parse_type_alias (std::move (vis), std::move (outer_attrs)); | |
| case EXTERN_TOK: | |
| case UNSAFE: | |
| case FN_TOK: | |
| // function or method | |
| return parse_trait_impl_function_or_method (std::move (vis), | |
| std::move ( | |
| outer_attrs)); | |
| case CONST: | |
| // lookahead to resolve production - could be function/method or | |
| // const item | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| case UNDERSCORE: | |
| return parse_const_item (std::move (vis), | |
| std::move (outer_attrs)); | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_trait_impl_function_or_method (std::move (vis), | |
| std::move ( | |
| outer_attrs)); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in some sort of const " | |
| "item in trait impl", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs for item in trait impl", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| } | |
| case EXTERN_TOK: | |
| case UNSAFE: | |
| case FN_TOK: | |
| // function or method | |
| return parse_trait_impl_function_or_method ( | |
| AST::Visibility::create_private (), std::move (outer_attrs)); | |
| case CONST: | |
| // lookahead to resolve production - could be function/method or const | |
| // item | |
| t = lexer.peek_token (1); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| case UNDERSCORE: | |
| return parse_const_item (AST::Visibility::create_private (), | |
| std::move (outer_attrs)); | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_trait_impl_function_or_method ( | |
| AST::Visibility::create_private (), std::move (outer_attrs)); | |
| default: | |
| add_error (Error ( | |
| t->get_locus (), | |
| "unexpected token %qs in some sort of const item in trait impl", | |
| t->get_token_description ())); | |
| lexer.skip_token (1); // TODO: is this right thing to do? | |
| return nullptr; | |
| } | |
| gcc_unreachable (); | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs for item in trait impl", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| } | |
| /* For internal use only by parse_trait_impl_item() - splits giant method into | |
| * smaller ones and prevents duplication of logic. Strictly, this parses a | |
| * function or method item inside a trait impl item block. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TraitImplItem> | |
| Parser<ManagedTokenSource>::parse_trait_impl_function_or_method ( | |
| AST::Visibility vis, AST::AttrVec outer_attrs) | |
| { | |
| // this shares virtually all logic with | |
| // parse_inherent_impl_function_or_method | |
| // - template? | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse function or method qualifiers | |
| AST::FunctionQualifiers qualifiers = parse_function_qualifiers (); | |
| skip_token (FN_TOK); | |
| // parse function or method name | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| return nullptr; | |
| } | |
| Identifier ident = ident_tok->get_str (); | |
| // DEBUG: | |
| rust_debug ( | |
| "about to start parsing generic params in trait impl function or method"); | |
| // parse generic params | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| // DEBUG: | |
| rust_debug ( | |
| "finished parsing generic params in trait impl function or method"); | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // now for function vs method disambiguation - method has opening "self" | |
| // param | |
| AST::SelfParam self_param = parse_self_param (); | |
| // FIXME: ensure that self param doesn't accidently consume tokens for a | |
| // function | |
| bool is_method = false; | |
| if (!self_param.is_error ()) | |
| { | |
| is_method = true; | |
| // skip comma so function and method regular params can be parsed in | |
| // same way | |
| if (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed self param in method trait impl item"); | |
| } | |
| // DEBUG | |
| rust_debug ( | |
| "started to parse function params in function or method trait impl item"); | |
| // parse trait function params (only if next token isn't right paren) | |
| std::vector<AST::FunctionParam> function_params; | |
| if (lexer.peek_token ()->get_id () != RIGHT_PAREN) | |
| { | |
| function_params | |
| = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; }); | |
| if (function_params.empty ()) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse function params in trait impl %s definition", | |
| is_method ? "method" : "function"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed function params in function or method " | |
| "trait impl item"); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // parse return type (optional) | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| // DEBUG | |
| rust_debug ( | |
| "successfully parsed return type in function or method trait impl item"); | |
| // parse where clause (optional) | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| // DEBUG | |
| rust_debug ( | |
| "successfully parsed where clause in function or method trait impl item"); | |
| // parse function definition (in block) - semicolon not allowed | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "%s declaration in trait impl not allowed - must have a definition", | |
| is_method ? "method" : "function"); | |
| add_error (std::move (error)); | |
| lexer.skip_token (); | |
| return nullptr; | |
| } | |
| std::unique_ptr<AST::BlockExpr> body = parse_block_expr (); | |
| if (body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse definition in trait impl %s definition", | |
| is_method ? "method" : "function"); | |
| add_error (std::move (error)); | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // do actual if instead of ternary for return value optimisation | |
| if (is_method) | |
| { | |
| return std::unique_ptr<AST::Method> ( | |
| new AST::Method (std::move (ident), std::move (qualifiers), | |
| std::move (generic_params), std::move (self_param), | |
| std::move (function_params), std::move (return_type), | |
| std::move (where_clause), std::move (body), | |
| std::move (vis), std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| return std::unique_ptr<AST::Function> ( | |
| new AST::Function (std::move (ident), std::move (qualifiers), | |
| std::move (generic_params), | |
| std::move (function_params), std::move (return_type), | |
| std::move (where_clause), std::move (body), | |
| std::move (vis), std::move (outer_attrs), locus)); | |
| } | |
| } | |
| // Parses an extern block of declarations. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExternBlock> | |
| Parser<ManagedTokenSource>::parse_extern_block (AST::Visibility vis, | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (EXTERN_TOK); | |
| // detect optional abi name | |
| std::string abi; | |
| const_TokenPtr next_tok = lexer.peek_token (); | |
| if (next_tok->get_id () == STRING_LITERAL) | |
| { | |
| lexer.skip_token (); | |
| abi = next_tok->get_str (); | |
| } | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse declarations inside extern block | |
| std::vector<std::unique_ptr<AST::ExternalItem>> extern_items; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| std::unique_ptr<AST::ExternalItem> extern_item = parse_external_item (); | |
| if (extern_item == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse external item despite not reaching " | |
| "end of extern block"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| extern_items.push_back (std::move (extern_item)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip somewhere | |
| return nullptr; | |
| } | |
| extern_items.shrink_to_fit (); | |
| return std::unique_ptr<AST::ExternBlock> ( | |
| new AST::ExternBlock (std::move (abi), std::move (extern_items), | |
| std::move (vis), std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a single extern block item (static or function declaration). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExternalItem> | |
| Parser<ManagedTokenSource>::parse_external_item () | |
| { | |
| // parse optional outer attributes | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse optional visibility | |
| AST::Visibility vis = parse_visibility (); | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| return parse_macro_invocation_semi (outer_attrs); | |
| case STATIC_TOK: { | |
| // parse extern static item | |
| lexer.skip_token (); | |
| // parse mut (optional) | |
| bool has_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| lexer.skip_token (); | |
| has_mut = true; | |
| } | |
| // parse identifier | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| Identifier ident = ident_tok->get_str (); | |
| if (!skip_token (COLON)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type in external static item"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ExternalStaticItem> ( | |
| new AST::ExternalStaticItem (std::move (ident), std::move (type), | |
| has_mut, std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| case FN_TOK: { | |
| // parse extern function declaration item | |
| // skip function token | |
| lexer.skip_token (); | |
| // parse identifier | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| Identifier ident = ident_tok->get_str (); | |
| // parse (optional) generic params | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse parameters | |
| std::vector<AST::NamedFunctionParam> function_params; | |
| bool is_variadic = false; | |
| AST::AttrVec variadic_attrs; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| AST::AttrVec maybe_variadic_attrs = parse_outer_attributes (); | |
| if (lexer.peek_token ()->get_id () == ELLIPSIS) | |
| { | |
| // variadic - use attrs for this | |
| lexer.skip_token (); | |
| is_variadic = true; | |
| variadic_attrs = std::move (maybe_variadic_attrs); | |
| t = lexer.peek_token (); | |
| if (t->get_id () != RIGHT_PAREN) | |
| { | |
| Error error (t->get_locus (), | |
| "expected right parentheses after variadic in " | |
| "named function " | |
| "parameters, found %qs", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| break; | |
| } | |
| AST::NamedFunctionParam param | |
| = parse_named_function_param (std::move (maybe_variadic_attrs)); | |
| if (param.is_error ()) | |
| { | |
| Error error (t->get_locus (), "could not parse named function " | |
| "parameter in external function"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| function_params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse (optional) return type | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| // parse (optional) where clause | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| if (!skip_token (SEMICOLON)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| function_params.shrink_to_fit (); | |
| return std::unique_ptr<AST::ExternalFunctionItem> ( | |
| new AST::ExternalFunctionItem ( | |
| std::move (ident), std::move (generic_params), | |
| std::move (return_type), std::move (where_clause), | |
| std::move (function_params), is_variadic, | |
| std::move (variadic_attrs), std::move (vis), | |
| std::move (outer_attrs), locus)); | |
| } | |
| default: | |
| // error | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unrecognised token %qs in extern block item declaration", | |
| t->get_token_description ())); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| } | |
| /* Parses an extern block function param (with "pattern" being _ or an | |
| * identifier). */ | |
| template <typename ManagedTokenSource> | |
| AST::NamedFunctionParam | |
| Parser<ManagedTokenSource>::parse_named_function_param ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| // parse identifier/_ | |
| std::string name; | |
| const_TokenPtr t = lexer.peek_token (); | |
| Location name_location = t->get_locus (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| name = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case UNDERSCORE: | |
| name = "_"; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| // this is not a function param, but not necessarily an error | |
| return AST::NamedFunctionParam::create_error (); | |
| } | |
| if (!skip_token (COLON)) | |
| { | |
| // skip after somewhere? | |
| return AST::NamedFunctionParam::create_error (); | |
| } | |
| // parse (required) type | |
| std::unique_ptr<AST::Type> param_type = parse_type (); | |
| if (param_type == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "could not parse param type in extern block function declaration"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return AST::NamedFunctionParam::create_error (); | |
| } | |
| return AST::NamedFunctionParam (std::move (name), std::move (param_type), | |
| std::move (outer_attrs), name_location); | |
| } | |
| // Parses a statement (will further disambiguate any statement). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Stmt> | |
| Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions) | |
| { | |
| // quick exit for empty statement | |
| // FIXME: Can we have empty statements without semicolons? Just nothing? | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (t->get_id () == SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::EmptyStmt> ( | |
| new AST::EmptyStmt (t->get_locus ())); | |
| } | |
| // parse outer attributes | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parsing this will be annoying because of the many different possibilities | |
| /* best may be just to copy paste in parse_item switch, and failing that try | |
| * to parse outer attributes, and then pass them in to either a let | |
| * statement or (fallback) expression statement. */ | |
| // FIXME: think of a way to do this without such a large switch? | |
| t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LET: | |
| // let statement | |
| return parse_let_stmt (std::move (outer_attrs), restrictions); | |
| case PUB: | |
| case MOD: | |
| case EXTERN_TOK: | |
| case USE: | |
| case FN_TOK: | |
| case TYPE: | |
| case STRUCT_TOK: | |
| case ENUM_TOK: | |
| case CONST: | |
| case STATIC_TOK: | |
| case TRAIT: | |
| case IMPL: | |
| /* TODO: implement union keyword but not really because of | |
| * context-dependence crappy hack way to parse a union written below to | |
| * separate it from the good code. */ | |
| // case UNION: | |
| case UNSAFE: // maybe - unsafe traits are a thing | |
| /* if any of these (should be all possible VisItem prefixes), parse a | |
| * VisItem can't parse item because would require reparsing outer | |
| * attributes */ | |
| return parse_vis_item (std::move (outer_attrs)); | |
| break; | |
| case SUPER: | |
| case SELF: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| // almost certainly macro invocation semi | |
| return parse_macro_item (std::move (outer_attrs)); | |
| break; | |
| // crappy hack to do union "keyword" | |
| case IDENTIFIER: | |
| if (t->get_str () == "union" | |
| && lexer.peek_token (1)->get_id () == IDENTIFIER) | |
| { | |
| return parse_vis_item (std::move (outer_attrs)); | |
| // or should this go straight to parsing union? | |
| } | |
| else if (t->get_str () == "macro_rules") | |
| { | |
| // macro_rules! macro item | |
| return parse_macro_item (std::move (outer_attrs)); | |
| } | |
| else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION | |
| || lexer.peek_token (1)->get_id () == EXCLAM) | |
| { | |
| // FIXME: ensure doesn't take any expressions by mistake | |
| /* path (probably) or macro invocation, so probably a macro | |
| * invocation semi */ | |
| return parse_macro_item (std::move (outer_attrs)); | |
| } | |
| gcc_fallthrough (); | |
| // TODO: find out how to disable gcc "implicit fallthrough" warning | |
| default: | |
| // fallback: expression statement | |
| return parse_expr_stmt (std::move (outer_attrs), restrictions); | |
| break; | |
| } | |
| } | |
| // Parses a let statement. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::LetStmt> | |
| Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LET); | |
| // parse pattern (required) | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in let statement"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| // parse type declaration (optional) | |
| std::unique_ptr<AST::Type> type = nullptr; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| // must have a type declaration | |
| lexer.skip_token (); | |
| type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type in let statement"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| } | |
| // parse expression to set variable to (optional) | |
| std::unique_ptr<AST::Expr> expr = nullptr; | |
| if (lexer.peek_token ()->get_id () == EQUAL) | |
| { | |
| // must have an expression | |
| lexer.skip_token (); | |
| expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse expression in let statement"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| } | |
| if (restrictions.consume_semi) | |
| if (!skip_token (SEMICOLON)) | |
| return nullptr; | |
| return std::unique_ptr<AST::LetStmt> ( | |
| new AST::LetStmt (std::move (pattern), std::move (expr), std::move (type), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a type path. | |
| template <typename ManagedTokenSource> | |
| AST::TypePath | |
| Parser<ManagedTokenSource>::parse_type_path () | |
| { | |
| bool has_opening_scope_resolution = false; | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| has_opening_scope_resolution = true; | |
| lexer.skip_token (); | |
| } | |
| // create segment vector | |
| std::vector<std::unique_ptr<AST::TypePathSegment>> segments; | |
| // parse required initial segment | |
| std::unique_ptr<AST::TypePathSegment> initial_segment | |
| = parse_type_path_segment (); | |
| if (initial_segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| // don't necessarily throw error but yeah | |
| return AST::TypePath::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| std::unique_ptr<AST::TypePathSegment> segment | |
| = parse_type_path_segment (); | |
| if (segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), "could not parse type path segment"); | |
| add_error (std::move (error)); | |
| return AST::TypePath::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| return AST::TypePath (std::move (segments), locus, | |
| has_opening_scope_resolution); | |
| } | |
| template <typename ManagedTokenSource> | |
| AST::GenericArg | |
| Parser<ManagedTokenSource>::parse_generic_arg () | |
| { | |
| auto tok = lexer.peek_token (); | |
| std::unique_ptr<AST::Expr> expr = nullptr; | |
| switch (tok->get_id ()) | |
| { | |
| case IDENTIFIER: { | |
| // This is a bit of a weird situation: With an identifier token, we | |
| // could either have a valid type or a macro (FIXME: anything else?). So | |
| // we need one bit of lookahead to differentiate if this is really | |
| auto next_tok = lexer.peek_token (1); | |
| if (next_tok->get_id () == EXCLAM) | |
| { | |
| auto type = parse_type (); | |
| if (type) | |
| return AST::GenericArg::create_type (std::move (type)); | |
| else | |
| return AST::GenericArg::create_error (); | |
| } | |
| lexer.skip_token (); | |
| return AST::GenericArg::create_ambiguous (tok->get_str (), | |
| tok->get_locus ()); | |
| } | |
| case LEFT_CURLY: | |
| expr = parse_block_expr (); | |
| break; | |
| case MINUS: | |
| case STRING_LITERAL: | |
| case CHAR_LITERAL: | |
| case INT_LITERAL: | |
| case FLOAT_LITERAL: | |
| case TRUE_LITERAL: | |
| case FALSE_LITERAL: | |
| expr = parse_literal_expr (); | |
| break; | |
| // FIXME: Because of this, error reporting is garbage for const generic | |
| // parameter's default values | |
| default: { | |
| auto type = parse_type (); | |
| // FIXME: Find a better way to do this? | |
| if (type) | |
| return AST::GenericArg::create_type (std::move (type)); | |
| else | |
| return AST::GenericArg::create_error (); | |
| } | |
| } | |
| if (!expr) | |
| return AST::GenericArg::create_error (); | |
| return AST::GenericArg::create_const (std::move (expr)); | |
| } | |
| // Parses the generic arguments in each path segment. | |
| template <typename ManagedTokenSource> | |
| AST::GenericArgs | |
| Parser<ManagedTokenSource>::parse_path_generic_args () | |
| { | |
| if (!skip_token (LEFT_ANGLE)) | |
| { | |
| // skip after somewhere? | |
| return AST::GenericArgs::create_empty (); | |
| } | |
| // We need to parse all lifetimes, then parse types and const generics in | |
| // any order. | |
| // try to parse lifetimes first | |
| std::vector<AST::Lifetime> lifetime_args; | |
| const_TokenPtr t = lexer.peek_token (); | |
| Location locus = t->get_locus (); | |
| while (!is_right_angle_tok (t->get_id ())) | |
| { | |
| AST::Lifetime lifetime = parse_lifetime (); | |
| if (lifetime.is_error ()) | |
| { | |
| // not necessarily an error | |
| break; | |
| } | |
| lifetime_args.push_back (std::move (lifetime)); | |
| // if next token isn't comma, then it must be end of list | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| break; | |
| } | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // try to parse types and const generics second | |
| std::vector<AST::GenericArg> generic_args; | |
| // TODO: think of better control structure | |
| t = lexer.peek_token (); | |
| while (!is_right_angle_tok (t->get_id ())) | |
| { | |
| // FIXME: Is it fine to break if there is one binding? Can't there be | |
| // bindings in between types? | |
| // ensure not binding being parsed as type accidently | |
| if (t->get_id () == IDENTIFIER | |
| && lexer.peek_token (1)->get_id () == EQUAL) | |
| break; | |
| auto arg = parse_generic_arg (); | |
| if (!arg.is_error ()) | |
| { | |
| generic_args.emplace_back (std::move (arg)); | |
| } | |
| // FIXME: Do we need to break if we encounter an error? | |
| // if next token isn't comma, then it must be end of list | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // try to parse bindings third | |
| std::vector<AST::GenericArgsBinding> binding_args; | |
| // TODO: think of better control structure | |
| t = lexer.peek_token (); | |
| while (!is_right_angle_tok (t->get_id ())) | |
| { | |
| AST::GenericArgsBinding binding = parse_generic_args_binding (); | |
| if (binding.is_error ()) | |
| { | |
| // not necessarily an error | |
| break; | |
| } | |
| binding_args.push_back (std::move (binding)); | |
| // if next token isn't comma, then it must be end of list | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| break; | |
| } | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // skip any trailing commas | |
| if (lexer.peek_token ()->get_id () == COMMA) | |
| lexer.skip_token (); | |
| if (!skip_generics_right_angle ()) | |
| return AST::GenericArgs::create_empty (); | |
| lifetime_args.shrink_to_fit (); | |
| generic_args.shrink_to_fit (); | |
| binding_args.shrink_to_fit (); | |
| return AST::GenericArgs (std::move (lifetime_args), std::move (generic_args), | |
| std::move (binding_args), locus); | |
| } | |
| // Parses a binding in a generic args path segment. | |
| template <typename ManagedTokenSource> | |
| AST::GenericArgsBinding | |
| Parser<ManagedTokenSource>::parse_generic_args_binding () | |
| { | |
| const_TokenPtr ident_tok = lexer.peek_token (); | |
| if (ident_tok->get_id () != IDENTIFIER) | |
| { | |
| // allow non error-inducing use | |
| // skip somewhere? | |
| return AST::GenericArgsBinding::create_error (); | |
| } | |
| lexer.skip_token (); | |
| Identifier ident = ident_tok->get_str (); | |
| if (!skip_token (EQUAL)) | |
| { | |
| // skip after somewhere? | |
| return AST::GenericArgsBinding::create_error (); | |
| } | |
| // parse type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| // skip somewhere? | |
| return AST::GenericArgsBinding::create_error (); | |
| } | |
| return AST::GenericArgsBinding (std::move (ident), std::move (type), | |
| ident_tok->get_locus ()); | |
| } | |
| /* Parses a single type path segment (not including opening scope resolution, | |
| * but includes any internal ones). Includes generic args or type path | |
| * functions too. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypePathSegment> | |
| Parser<ManagedTokenSource>::parse_type_path_segment () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse ident segment part | |
| AST::PathIdentSegment ident_segment = parse_path_ident_segment (); | |
| if (ident_segment.is_error ()) | |
| { | |
| // not necessarily an error | |
| return nullptr; | |
| } | |
| /* lookahead to determine if variants exist - only consume scope resolution | |
| * then */ | |
| bool has_separating_scope_resolution = false; | |
| const_TokenPtr next = lexer.peek_token (1); | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION | |
| && (next->get_id () == LEFT_ANGLE || next->get_id () == LEFT_PAREN)) | |
| { | |
| has_separating_scope_resolution = true; | |
| lexer.skip_token (); | |
| } | |
| // branch into variants on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_ANGLE: { | |
| // parse generic args | |
| AST::GenericArgs generic_args = parse_path_generic_args (); | |
| return std::unique_ptr<AST::TypePathSegmentGeneric> ( | |
| new AST::TypePathSegmentGeneric (std::move (ident_segment), | |
| has_separating_scope_resolution, | |
| std::move (generic_args), locus)); | |
| } | |
| case LEFT_PAREN: { | |
| // parse type path function | |
| AST::TypePathFunction type_path_function | |
| = parse_type_path_function (locus); | |
| if (type_path_function.is_error ()) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TypePathSegmentFunction> ( | |
| new AST::TypePathSegmentFunction (std::move (ident_segment), | |
| has_separating_scope_resolution, | |
| std::move (type_path_function), | |
| locus)); | |
| } | |
| default: | |
| // neither of them | |
| return std::unique_ptr<AST::TypePathSegment> ( | |
| new AST::TypePathSegment (std::move (ident_segment), | |
| has_separating_scope_resolution, locus)); | |
| } | |
| gcc_unreachable (); | |
| } | |
| // Parses a function call representation inside a type path. | |
| template <typename ManagedTokenSource> | |
| AST::TypePathFunction | |
| Parser<ManagedTokenSource>::parse_type_path_function (Location id_location) | |
| { | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| // skip somewhere? | |
| return AST::TypePathFunction::create_error (); | |
| } | |
| // parse function inputs | |
| std::vector<std::unique_ptr<AST::Type>> inputs; | |
| while (lexer.peek_token ()->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| /* this is an error as there should've been a ')' there if there | |
| * wasn't a type */ | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse type in parameters of type path function"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::TypePathFunction::create_error (); | |
| } | |
| inputs.push_back (std::move (type)); | |
| // skip commas, including trailing commas | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip somewhere? | |
| return AST::TypePathFunction::create_error (); | |
| } | |
| // parse optional return type | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| inputs.shrink_to_fit (); | |
| return AST::TypePathFunction (std::move (inputs), id_location, | |
| std::move (return_type)); | |
| } | |
| // Parses a path inside an expression that allows generic arguments. | |
| template <typename ManagedTokenSource> | |
| AST::PathInExpression | |
| Parser<ManagedTokenSource>::parse_path_in_expression () | |
| { | |
| Location locus = Linemap::unknown_location (); | |
| bool has_opening_scope_resolution = false; | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| has_opening_scope_resolution = true; | |
| locus = lexer.peek_token ()->get_locus (); | |
| lexer.skip_token (); | |
| } | |
| // create segment vector | |
| std::vector<AST::PathExprSegment> segments; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| } | |
| // parse required initial segment | |
| AST::PathExprSegment initial_segment = parse_path_expr_segment (); | |
| if (initial_segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| // don't necessarily throw error but yeah | |
| return AST::PathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| AST::PathExprSegment segment = parse_path_expr_segment (); | |
| if (segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), | |
| "could not parse path expression segment"); | |
| add_error (std::move (error)); | |
| return AST::PathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| return AST::PathInExpression (std::move (segments), {}, locus, | |
| has_opening_scope_resolution); | |
| } | |
| /* Parses a single path in expression path segment (including generic | |
| * arguments). */ | |
| template <typename ManagedTokenSource> | |
| AST::PathExprSegment | |
| Parser<ManagedTokenSource>::parse_path_expr_segment () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse ident segment | |
| AST::PathIdentSegment ident = parse_path_ident_segment (); | |
| if (ident.is_error ()) | |
| { | |
| // not necessarily an error? | |
| return AST::PathExprSegment::create_error (); | |
| } | |
| // parse generic args (and turbofish), if they exist | |
| /* use lookahead to determine if they actually exist (don't want to | |
| * accidently parse over next ident segment) */ | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION | |
| && lexer.peek_token (1)->get_id () == LEFT_ANGLE) | |
| { | |
| // skip scope resolution | |
| lexer.skip_token (); | |
| AST::GenericArgs generic_args = parse_path_generic_args (); | |
| return AST::PathExprSegment (std::move (ident), locus, | |
| std::move (generic_args)); | |
| } | |
| // return a generic parameter-less expr segment if not found | |
| return AST::PathExprSegment (std::move (ident), locus); | |
| } | |
| /* Parses a fully qualified path in expression (i.e. a pattern). FIXME does | |
| * not parse outer attrs. */ | |
| template <typename ManagedTokenSource> | |
| AST::QualifiedPathInExpression | |
| Parser<ManagedTokenSource>::parse_qualified_path_in_expression ( | |
| Location pratt_parsed_loc) | |
| { | |
| /* Note: the Rust grammar is defined in such a way that it is impossible to | |
| * determine whether a prospective qualified path is a | |
| * QualifiedPathInExpression or QualifiedPathInType in all cases by the | |
| * rules themselves (the only possible difference is a TypePathSegment with | |
| * function, and lookahead to find this is too difficult). However, as this | |
| * is a pattern and QualifiedPathInType is a type, I believe it that their | |
| * construction will not be confused (due to rules regarding patterns vs | |
| * types). | |
| * As such, this function will not attempt to minimise errors created by | |
| * their confusion. */ | |
| // parse the qualified path type (required) | |
| AST::QualifiedPathType qual_path_type | |
| = parse_qualified_path_type (pratt_parsed_loc); | |
| if (qual_path_type.is_error ()) | |
| { | |
| // TODO: should this create a parse error? | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| Location locus = qual_path_type.get_locus (); | |
| // parse path segments | |
| std::vector<AST::PathExprSegment> segments; | |
| // parse initial required segment | |
| if (!expect_token (SCOPE_RESOLUTION)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| AST::PathExprSegment initial_segment = parse_path_expr_segment (); | |
| if (initial_segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "required initial path expression segment in " | |
| "qualified path in expression could not be parsed"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| AST::PathExprSegment segment = parse_path_expr_segment (); | |
| if (segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), | |
| "could not parse path expression segment in qualified " | |
| "path in expression"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| // FIXME: outer attr parsing | |
| return AST::QualifiedPathInExpression (std::move (qual_path_type), | |
| std::move (segments), {}, locus); | |
| } | |
| // Parses the type syntactical construction at the start of a qualified path. | |
| template <typename ManagedTokenSource> | |
| AST::QualifiedPathType | |
| Parser<ManagedTokenSource>::parse_qualified_path_type ( | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| /* TODO: should this actually be error? is there anywhere where this could | |
| * be valid? */ | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| if (!skip_token (LEFT_ANGLE)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| } | |
| // parse type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in qualified path type"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| // parse optional as clause | |
| AST::TypePath as_type_path = AST::TypePath::create_error (); | |
| if (lexer.peek_token ()->get_id () == AS) | |
| { | |
| lexer.skip_token (); | |
| // parse type path, which is required now | |
| as_type_path = parse_type_path (); | |
| if (as_type_path.is_error ()) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "could not parse type path in as clause in qualified path type"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| } | |
| /* NOTE: should actually be a right-angle token, so | |
| * skip_generics_right_angle shouldn't be required */ | |
| if (!skip_token (RIGHT_ANGLE)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| return AST::QualifiedPathType (std::move (type), locus, | |
| std::move (as_type_path)); | |
| } | |
| // Parses a fully qualified path in type (i.e. a type). | |
| template <typename ManagedTokenSource> | |
| AST::QualifiedPathInType | |
| Parser<ManagedTokenSource>::parse_qualified_path_in_type () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // parse the qualified path type (required) | |
| AST::QualifiedPathType qual_path_type = parse_qualified_path_type (); | |
| if (qual_path_type.is_error ()) | |
| { | |
| // TODO: should this create a parse error? | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| // parse initial required segment | |
| if (!expect_token (SCOPE_RESOLUTION)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| std::unique_ptr<AST::TypePathSegment> initial_segment | |
| = parse_type_path_segment (); | |
| if (initial_segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "required initial type path segment in qualified path in " | |
| "type could not be parsed"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| // parse optional segments (as long as scope resolution operator exists) | |
| std::vector<std::unique_ptr<AST::TypePathSegment>> segments; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| std::unique_ptr<AST::TypePathSegment> segment | |
| = parse_type_path_segment (); | |
| if (segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| Error error ( | |
| t->get_locus (), | |
| "could not parse type path segment in qualified path in type"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| return AST::QualifiedPathInType (std::move (qual_path_type), | |
| std::move (initial_segment), | |
| std::move (segments), locus); | |
| } | |
| // Parses a self param. Also handles self param not existing. | |
| template <typename ManagedTokenSource> | |
| AST::SelfParam | |
| Parser<ManagedTokenSource>::parse_self_param () | |
| { | |
| bool has_reference = false; | |
| AST::Lifetime lifetime = AST::Lifetime::error (); | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // test if self is a reference parameter | |
| if (lexer.peek_token ()->get_id () == AMP) | |
| { | |
| has_reference = true; | |
| lexer.skip_token (); | |
| // now test whether it has a lifetime | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| lifetime = parse_lifetime (); | |
| // something went wrong somehow | |
| if (lifetime.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse lifetime in self param"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return AST::SelfParam::create_error (); | |
| } | |
| } | |
| } | |
| // test for mut | |
| bool has_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| has_mut = true; | |
| lexer.skip_token (); | |
| } | |
| // skip self token | |
| const_TokenPtr self_tok = lexer.peek_token (); | |
| if (self_tok->get_id () != SELF) | |
| { | |
| // skip after somewhere? | |
| return AST::SelfParam::create_error (); | |
| } | |
| lexer.skip_token (); | |
| // parse optional type | |
| std::unique_ptr<AST::Type> type = nullptr; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| // type is now required | |
| type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in self param"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return AST::SelfParam::create_error (); | |
| } | |
| } | |
| // ensure that cannot have both type and reference | |
| if (type != nullptr && has_reference) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "cannot have both a reference and a type specified in a self param"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return AST::SelfParam::create_error (); | |
| } | |
| if (has_reference) | |
| { | |
| return AST::SelfParam (std::move (lifetime), has_mut, locus); | |
| } | |
| else | |
| { | |
| // note that type may be nullptr here and that's fine | |
| return AST::SelfParam (std::move (type), has_mut, locus); | |
| } | |
| } | |
| /* Parses a method. Note that this function is probably useless because using | |
| * lookahead to determine whether a function is a method is a PITA (maybe not | |
| * even doable), so most places probably parse a "function or method" and then | |
| * resolve it into whatever it is afterward. As such, this is only here for | |
| * algorithmically defining the grammar rule. */ | |
| template <typename ManagedTokenSource> | |
| AST::Method | |
| Parser<ManagedTokenSource>::parse_method () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| /* Note: as a result of the above, this will not attempt to disambiguate a | |
| * function parse qualifiers */ | |
| AST::FunctionQualifiers qualifiers = parse_function_qualifiers (); | |
| skip_token (FN_TOK); | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| skip_after_next_block (); | |
| return AST::Method::create_error (); | |
| } | |
| Identifier method_name = ident_tok->get_str (); | |
| // parse generic params - if exist | |
| std::vector<std::unique_ptr<AST::GenericParam>> generic_params | |
| = parse_generic_params_in_angles (); | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "method missing opening parentheses before parameter list"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return AST::Method::create_error (); | |
| } | |
| // parse self param | |
| AST::SelfParam self_param = parse_self_param (); | |
| if (self_param.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse self param in method"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return AST::Method::create_error (); | |
| } | |
| // skip comma if it exists | |
| if (lexer.peek_token ()->get_id () == COMMA) | |
| lexer.skip_token (); | |
| // parse function parameters | |
| std::vector<AST::FunctionParam> function_params | |
| = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; }); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "method declaration missing closing parentheses after " | |
| "parameter list"); | |
| add_error (std::move (error)); | |
| skip_after_next_block (); | |
| return AST::Method::create_error (); | |
| } | |
| // parse function return type - if exists | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| // parse where clause - if exists | |
| AST::WhereClause where_clause = parse_where_clause (); | |
| // parse block expression | |
| std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr (); | |
| if (block_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "method declaration missing block expression"); | |
| add_error (std::move (error)); | |
| skip_after_end_block (); | |
| return AST::Method::create_error (); | |
| } | |
| // does not parse visibility, but this method isn't used, so doesn't matter | |
| return AST::Method (std::move (method_name), std::move (qualifiers), | |
| std::move (generic_params), std::move (self_param), | |
| std::move (function_params), std::move (return_type), | |
| std::move (where_clause), std::move (block_expr), | |
| AST::Visibility::create_error (), AST::AttrVec (), locus); | |
| } | |
| /* Parses an expression statement (disambiguates to expression with or without | |
| * block statement). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExprStmt> | |
| Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| /* potential thoughts - define new virtual method "has_block()" on expr. | |
| * parse expr and then determine whether semicolon is needed as a result of | |
| * this method. but then this would require dynamic_cast, which is not | |
| * allowed. */ | |
| /* okay new thought - big switch to disambiguate exprs with blocks - either | |
| * block expr, async block expr, unsafe block expr, loop expr, if expr, if | |
| * let expr, or match expr. So all others are exprs without block. */ | |
| /* new thought: possible initial tokens: 'loop', 'while', 'for', lifetime | |
| * (and then ':' and then loop), 'if', 'match', '{', 'async', 'unsafe' (and | |
| * then | |
| * '{')). This seems to have no ambiguity. */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| /* TODO: should the switch just directly call the individual parse methods | |
| * rather than adding another layer of indirection with | |
| * parse_expr_stmt_with_block()? */ | |
| switch (t->get_id ()) | |
| { | |
| case LOOP: | |
| case WHILE: | |
| case FOR: | |
| case IF: | |
| case MATCH_TOK: | |
| case LEFT_CURLY: | |
| case ASYNC: | |
| // expression with block | |
| return parse_expr_stmt_with_block (std::move (outer_attrs)); | |
| case LIFETIME: { | |
| /* FIXME: are there any expressions without blocks that can have | |
| * lifetime as their first token? Or is loop expr the only one? */ | |
| // safe side for now: | |
| if (lexer.peek_token (1)->get_id () == COLON | |
| && lexer.peek_token (2)->get_id () == LOOP) | |
| { | |
| return parse_expr_stmt_with_block (std::move (outer_attrs)); | |
| } | |
| else | |
| { | |
| return parse_expr_stmt_without_block (std::move (outer_attrs), | |
| restrictions); | |
| } | |
| } | |
| case UNSAFE: { | |
| /* FIXME: are there any expressions without blocks that can have | |
| * unsafe as their first token? Or is unsafe the only one? */ | |
| // safe side for now | |
| if (lexer.peek_token (1)->get_id () == LEFT_CURLY) | |
| { | |
| return parse_expr_stmt_with_block (std::move (outer_attrs)); | |
| } | |
| else | |
| { | |
| return parse_expr_stmt_without_block (std::move (outer_attrs), | |
| restrictions); | |
| } | |
| } | |
| default: | |
| // not a parse expr with block, so must be expr without block | |
| /* TODO: if possible, be more selective about possible expr without | |
| * block initial tokens in order to prevent more syntactical errors at | |
| * parse time. */ | |
| return parse_expr_stmt_without_block (std::move (outer_attrs), | |
| restrictions); | |
| } | |
| } | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExprWithBlock> | |
| Parser<ManagedTokenSource>::parse_expr_with_block (AST::AttrVec outer_attrs) | |
| { | |
| std::unique_ptr<AST::ExprWithBlock> expr_parsed = nullptr; | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IF: | |
| // if or if let, so more lookahead to find out | |
| if (lexer.peek_token (1)->get_id () == LET) | |
| { | |
| // if let expr | |
| expr_parsed = parse_if_let_expr (std::move (outer_attrs)); | |
| break; | |
| } | |
| else | |
| { | |
| // if expr | |
| expr_parsed = parse_if_expr (std::move (outer_attrs)); | |
| break; | |
| } | |
| case LOOP: | |
| // infinite loop | |
| expr_parsed = parse_loop_expr (std::move (outer_attrs)); | |
| break; | |
| case FOR: | |
| // "for" iterator loop | |
| expr_parsed = parse_for_loop_expr (std::move (outer_attrs)); | |
| break; | |
| case WHILE: { | |
| // while or while let, so more lookahead to find out | |
| if (lexer.peek_token (1)->get_id () == LET) | |
| { | |
| // while let loop expr | |
| expr_parsed = parse_while_let_loop_expr (std::move (outer_attrs)); | |
| break; | |
| } | |
| else | |
| { | |
| // while loop expr | |
| expr_parsed = parse_while_loop_expr (std::move (outer_attrs)); | |
| break; | |
| } | |
| } | |
| case MATCH_TOK: | |
| // match expression | |
| expr_parsed = parse_match_expr (std::move (outer_attrs)); | |
| break; | |
| case LEFT_CURLY: | |
| // block expression | |
| expr_parsed = parse_block_expr (std::move (outer_attrs)); | |
| break; | |
| case ASYNC: | |
| // async block expression | |
| expr_parsed = parse_async_block_expr (std::move (outer_attrs)); | |
| break; | |
| case UNSAFE: | |
| // unsafe block expression | |
| expr_parsed = parse_unsafe_block_expr (std::move (outer_attrs)); | |
| break; | |
| case LIFETIME: | |
| // some kind of loop expr (with loop label) | |
| expr_parsed = parse_labelled_loop_expr (std::move (outer_attrs)); | |
| break; | |
| default: | |
| add_error (Error ( | |
| t->get_locus (), | |
| "could not recognise expr beginning with %qs as an expr with block in" | |
| " parsing expr statement", | |
| t->get_token_description ())); | |
| skip_after_next_block (); | |
| return nullptr; | |
| } | |
| // ensure expr parsed exists | |
| if (expr_parsed == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse expr with block in parsing expr statement"); | |
| add_error (std::move (error)); | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| return expr_parsed; | |
| } | |
| /* Parses a expression statement containing an expression with block. | |
| * Disambiguates internally. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExprStmtWithBlock> | |
| Parser<ManagedTokenSource>::parse_expr_stmt_with_block ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| auto expr_parsed = parse_expr_with_block (std::move (outer_attrs)); | |
| auto locus = expr_parsed->get_locus (); | |
| // return expr stmt created from expr | |
| return std::unique_ptr<AST::ExprStmtWithBlock> ( | |
| new AST::ExprStmtWithBlock (std::move (expr_parsed), locus, | |
| lexer.peek_token ()->get_id () == SEMICOLON)); | |
| } | |
| /* Parses an expression statement containing an expression without block. | |
| * Disambiguates further. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> | |
| Parser<ManagedTokenSource>::parse_expr_stmt_without_block ( | |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions) | |
| { | |
| /* TODO: maybe move more logic for expr without block in here for better | |
| * error handling */ | |
| // attempt to parse via parse_expr_without_block - seems to work | |
| std::unique_ptr<AST::ExprWithoutBlock> expr = nullptr; | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| restrictions.expr_can_be_stmt = true; | |
| expr = parse_expr_without_block (std::move (outer_attrs), restrictions); | |
| if (expr == nullptr) | |
| { | |
| // expr is required, error | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse expr without block in expr statement"); | |
| add_error (std::move (error)); | |
| skip_after_semicolon (); | |
| return nullptr; | |
| } | |
| if (restrictions.consume_semi) | |
| if (!skip_token (SEMICOLON)) | |
| return nullptr; | |
| return std::unique_ptr<AST::ExprStmtWithoutBlock> ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), locus)); | |
| } | |
| /* Parses an expression without a block associated with it (further | |
| * disambiguates). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExprWithoutBlock> | |
| Parser<ManagedTokenSource>::parse_expr_without_block ( | |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions) | |
| { | |
| /* Notes on types of expr without block: | |
| * - literal expr tokens that are literals | |
| * - path expr path_in_expr or qual_path_in_expr | |
| * - operator expr many different types | |
| * unary: | |
| * borrow expr ( '&' | '&&' ) 'mut'? expr | |
| * dereference expr '*' expr | |
| * error propagation expr '?' | |
| * negation '-' expr | |
| * not '!' expr | |
| * binary: all start with expr | |
| * - grouped/paren expr '(' inner_attributes expr ')' | |
| * - array expr '[' inner_attributes array_elems? ']' | |
| * - await expr expr '.' 'await' | |
| * - (array/slice) index expr expr '[' expr ']' | |
| * - tuple expr '(' inner_attributes tuple_elems? ')' | |
| * note that a single elem tuple is distinguished from a grouped expr | |
| * by a trailing comma, i.e. a grouped expr is preferred over a tuple expr | |
| * - tuple index expr expr '.' tuple_index | |
| * - struct expr path_in_expr (and optional other stuff) | |
| * - enum variant expr path_in_expr (and optional other stuff) | |
| * this means that there is no syntactic difference between an enum | |
| * variant and a struct | |
| * - only name resolution can tell the difference. Thus, maybe rework | |
| * AST to take this into account ("struct or enum" nodes?) | |
| * - (function) call expr expr '(' call_params? ')' | |
| * - method call expr expr '.' path_expr_segment '(' call_params? ')' | |
| * - field expr expr '.' identifier | |
| * note that method call expr is preferred, i.e. field expr must not be | |
| * followed by parenthesised expression sequence. | |
| * - closure expr 'move'? ( '||' | '|' closure_params? '|' ) ( | |
| * expr | '->' type_no_bounds block_expr ) | |
| * - continue expr 'continue' labelled_lifetime? | |
| * - break expr 'break' labelled_lifetime? expr? | |
| * - range expr many different types but all involve '..' or | |
| * '..=' | |
| * - return expr 'return' as 1st tok | |
| * - macro invocation identifier then :: or identifier then ! | |
| * (simple_path '!') | |
| * | |
| * any that have rules beginning with 'expr' should probably be | |
| * pratt-parsed, | |
| * with parsing type to use determined by token AND lookahead. */ | |
| // ok well at least can do easy ones | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case RETURN_TOK: | |
| // return expr | |
| return parse_return_expr (std::move (outer_attrs)); | |
| case BREAK: | |
| // break expr | |
| return parse_break_expr (std::move (outer_attrs)); | |
| case CONTINUE: | |
| // continue expr | |
| return parse_continue_expr (std::move (outer_attrs)); | |
| case MOVE: | |
| // closure expr (though not all closure exprs require this) | |
| return parse_closure_expr (std::move (outer_attrs)); | |
| case LEFT_SQUARE: | |
| // array expr (creation, not index) | |
| return parse_array_expr (std::move (outer_attrs)); | |
| default: { | |
| /* HACK: piggyback on pratt parsed expr and abuse polymorphism to | |
| * essentially downcast */ | |
| std::unique_ptr<AST::Expr> expr | |
| = parse_expr (std::move (outer_attrs), restrictions); | |
| if (expr == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse expression for expression without " | |
| "block (pratt-parsed expression is null)"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| std::unique_ptr<AST::ExprWithoutBlock> expr_without_block ( | |
| expr->as_expr_without_block ()); | |
| if (expr_without_block != nullptr) | |
| { | |
| return expr_without_block; | |
| } | |
| else | |
| { | |
| Error error (t->get_locus (), | |
| "converted expr without block is null"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| } | |
| } | |
| } | |
| // Parses a block expression, including the curly braces at start and end. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::BlockExpr> | |
| Parser<ManagedTokenSource>::parse_block_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| } | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse statements and expression | |
| std::vector<std::unique_ptr<AST::Stmt>> stmts; | |
| std::unique_ptr<AST::Expr> expr = nullptr; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| ExprOrStmt expr_or_stmt = parse_stmt_or_expr_without_block (); | |
| if (expr_or_stmt.is_error ()) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse statement or expression without " | |
| "block in block expression"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| t = lexer.peek_token (); | |
| if (expr_or_stmt.stmt != nullptr) | |
| { | |
| stmts.push_back (std::move (expr_or_stmt.stmt)); | |
| } | |
| else | |
| { | |
| // assign to expression and end parsing inside | |
| expr = std::move (expr_or_stmt.expr); | |
| break; | |
| } | |
| } | |
| Location end_locus = t->get_locus (); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| Error error (t->get_locus (), | |
| "error may be from having an expression (as opposed to " | |
| "statement) in the body of the function but not last"); | |
| add_error (std::move (error)); | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| // grammar allows for empty block expressions | |
| stmts.shrink_to_fit (); | |
| return std::unique_ptr<AST::BlockExpr> ( | |
| new AST::BlockExpr (std::move (stmts), std::move (expr), | |
| std::move (inner_attrs), std::move (outer_attrs), locus, | |
| end_locus)); | |
| } | |
| /* Parses a "grouped" expression (expression in parentheses), used to control | |
| * precedence. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::GroupedExpr> | |
| Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LEFT_PAREN); | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse required expr inside parentheses | |
| std::unique_ptr<AST::Expr> expr_in_parens = parse_expr (); | |
| if (expr_in_parens == nullptr) | |
| { | |
| // skip after somewhere? | |
| // error? | |
| return nullptr; | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::GroupedExpr> ( | |
| new AST::GroupedExpr (std::move (expr_in_parens), std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a closure expression (closure definition). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ClosureExpr> | |
| Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| // detect optional "move" | |
| bool has_move = false; | |
| if (lexer.peek_token ()->get_id () == MOVE) | |
| { | |
| lexer.skip_token (); | |
| has_move = true; | |
| } | |
| // handle parameter list | |
| std::vector<AST::ClosureParam> params; | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case OR: | |
| // skip token, no parameters | |
| lexer.skip_token (); | |
| break; | |
| case PIPE: | |
| // actually may have parameters | |
| lexer.skip_token (); | |
| while (t->get_id () != PIPE) | |
| { | |
| AST::ClosureParam param = parse_closure_param (); | |
| if (param.is_error ()) | |
| { | |
| // TODO is this really an error? | |
| Error error (t->get_locus (), "could not parse closure param"); | |
| add_error (std::move (error)); | |
| break; | |
| } | |
| params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| // not an error but means param list is done | |
| break; | |
| } | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| params.shrink_to_fit (); | |
| break; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in closure expression - expected " | |
| "%<|%> or %<||%>", | |
| t->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // again branch based on next token | |
| t = lexer.peek_token (); | |
| if (t->get_id () == RETURN_TYPE) | |
| { | |
| // must be return type closure with block expr | |
| // skip "return type" token | |
| lexer.skip_token (); | |
| // parse actual type, which is required | |
| std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds (); | |
| if (type == nullptr) | |
| { | |
| // error | |
| Error error (t->get_locus (), "failed to parse type for closure"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse block expr, which is required | |
| std::unique_ptr<AST::BlockExpr> block = parse_block_expr (); | |
| if (block == nullptr) | |
| { | |
| // error | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse block expr in closure"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ClosureExprInnerTyped> ( | |
| new AST::ClosureExprInnerTyped (std::move (type), std::move (block), | |
| std::move (params), locus, has_move, | |
| std::move (outer_attrs))); | |
| } | |
| else | |
| { | |
| // must be expr-only closure | |
| // parse expr, which is required | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse expression in closure"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ClosureExprInner> ( | |
| new AST::ClosureExprInner (std::move (expr), std::move (params), locus, | |
| has_move, std::move (outer_attrs))); | |
| } | |
| } | |
| // Parses a literal token (to literal expression). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::LiteralExpr> | |
| Parser<ManagedTokenSource>::parse_literal_expr (AST::AttrVec outer_attrs) | |
| { | |
| // TODO: change if literal representation in lexer changes | |
| std::string literal_value; | |
| AST::Literal::LitType type = AST::Literal::STRING; | |
| // branch based on token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case CHAR_LITERAL: | |
| type = AST::Literal::CHAR; | |
| literal_value = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case STRING_LITERAL: | |
| type = AST::Literal::STRING; | |
| literal_value = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case BYTE_CHAR_LITERAL: | |
| type = AST::Literal::BYTE; | |
| literal_value = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case BYTE_STRING_LITERAL: | |
| type = AST::Literal::BYTE_STRING; | |
| literal_value = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case INT_LITERAL: | |
| type = AST::Literal::INT; | |
| literal_value = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| case FLOAT_LITERAL: | |
| type = AST::Literal::FLOAT; | |
| literal_value = t->get_str (); | |
| lexer.skip_token (); | |
| break; | |
| // case BOOL_LITERAL | |
| // use true and false keywords rather than "bool literal" Rust terminology | |
| case TRUE_LITERAL: | |
| type = AST::Literal::BOOL; | |
| literal_value = "true"; | |
| lexer.skip_token (); | |
| break; | |
| case FALSE_LITERAL: | |
| type = AST::Literal::BOOL; | |
| literal_value = "false"; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| // error - cannot be a literal expr | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs when parsing literal expression", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| // create literal based on stuff in switch | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (std::move (literal_value), std::move (type), | |
| t->get_type_hint (), std::move (outer_attrs), | |
| t->get_locus ())); | |
| } | |
| // Parses a return expression (including any expression to return). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ReturnExpr> | |
| Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (RETURN_TOK); | |
| } | |
| // parse expression to return, if it exists | |
| ParseRestrictions restrictions; | |
| restrictions.expr_can_be_null = true; | |
| std::unique_ptr<AST::Expr> returned_expr | |
| = parse_expr (AST::AttrVec (), restrictions); | |
| return std::unique_ptr<AST::ReturnExpr> ( | |
| new AST::ReturnExpr (std::move (returned_expr), std::move (outer_attrs), | |
| locus)); | |
| } | |
| /* Parses a break expression (including any label to break to AND any return | |
| * expression). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::BreakExpr> | |
| Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (BREAK); | |
| } | |
| // parse label (lifetime) if it exists - create dummy first | |
| AST::Lifetime label = AST::Lifetime::error (); | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| label = parse_lifetime (); | |
| } | |
| // parse break return expression if it exists | |
| ParseRestrictions restrictions; | |
| restrictions.expr_can_be_null = true; | |
| std::unique_ptr<AST::Expr> return_expr | |
| = parse_expr (AST::AttrVec (), restrictions); | |
| return std::unique_ptr<AST::BreakExpr> ( | |
| new AST::BreakExpr (std::move (label), std::move (return_expr), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a continue expression (including any label to continue from). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ContinueExpr> | |
| Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (CONTINUE); | |
| } | |
| // parse label (lifetime) if it exists - create dummy first | |
| AST::Lifetime label = AST::Lifetime::error (); | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| label = parse_lifetime (); | |
| } | |
| return std::unique_ptr<AST::ContinueExpr> ( | |
| new AST::ContinueExpr (std::move (label), std::move (outer_attrs), locus)); | |
| } | |
| // Parses a loop label used in loop expressions. | |
| template <typename ManagedTokenSource> | |
| AST::LoopLabel | |
| Parser<ManagedTokenSource>::parse_loop_label () | |
| { | |
| // parse lifetime - if doesn't exist, assume no label | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (t->get_id () != LIFETIME) | |
| { | |
| // not necessarily an error | |
| return AST::LoopLabel::error (); | |
| } | |
| /* FIXME: check for named lifetime requirement here? or check in semantic | |
| * analysis phase? */ | |
| AST::Lifetime label = parse_lifetime (); | |
| if (!skip_token (COLON)) | |
| { | |
| // skip somewhere? | |
| return AST::LoopLabel::error (); | |
| } | |
| return AST::LoopLabel (std::move (label), t->get_locus ()); | |
| } | |
| /* Parses an if expression of any kind, including with else, else if, else if | |
| * let, and neither. Note that any outer attributes will be ignored because if | |
| * expressions don't support them. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::IfExpr> | |
| Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| // TODO: make having outer attributes an error? | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| if (!skip_token (IF)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| } | |
| // detect accidental if let | |
| if (lexer.peek_token ()->get_id () == LET) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "if let expression probably exists, but is being parsed " | |
| "as an if expression. This may be a parser error"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* parse required condition expr - HACK to prevent struct expr from being | |
| * parsed */ | |
| ParseRestrictions no_struct_expr; | |
| no_struct_expr.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> condition = parse_expr ({}, no_struct_expr); | |
| if (condition == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse condition expression in if expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse required block expr | |
| std::unique_ptr<AST::BlockExpr> if_body = parse_block_expr (); | |
| if (if_body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse if body block expression in if expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // branch to parse end or else (and then else, else if, or else if let) | |
| if (lexer.peek_token ()->get_id () != ELSE) | |
| { | |
| // single selection - end of if expression | |
| return std::unique_ptr<AST::IfExpr> ( | |
| new AST::IfExpr (std::move (condition), std::move (if_body), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // double or multiple selection - branch on end, else if, or else if let | |
| // skip "else" | |
| lexer.skip_token (); | |
| // branch on whether next token is '{' or 'if' | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_CURLY: { | |
| // double selection - else | |
| // parse else block expr (required) | |
| std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr (); | |
| if (else_body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse else body block expression in " | |
| "if expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IfExprConseqElse> ( | |
| new AST::IfExprConseqElse (std::move (condition), | |
| std::move (if_body), | |
| std::move (else_body), | |
| std::move (outer_attrs), locus)); | |
| } | |
| case IF: { | |
| // multiple selection - else if or else if let | |
| // branch on whether next token is 'let' or not | |
| if (lexer.peek_token (1)->get_id () == LET) | |
| { | |
| // parse if let expr (required) | |
| std::unique_ptr<AST::IfLetExpr> if_let_expr | |
| = parse_if_let_expr (); | |
| if (if_let_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse (else) if let expression " | |
| "after if expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IfExprConseqIfLet> ( | |
| new AST::IfExprConseqIfLet (std::move (condition), | |
| std::move (if_body), | |
| std::move (if_let_expr), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // parse if expr (required) | |
| std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr (); | |
| if (if_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse (else) if expression after " | |
| "if expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IfExprConseqIf> ( | |
| new AST::IfExprConseqIf (std::move (condition), | |
| std::move (if_body), | |
| std::move (if_expr), | |
| std::move (outer_attrs), locus)); | |
| } | |
| } | |
| default: | |
| // error - invalid token | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs after else in if expression", | |
| t->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| } | |
| } | |
| /* Parses an if let expression of any kind, including with else, else if, else | |
| * if let, and none. Note that any outer attributes will be ignored as if let | |
| * expressions don't support them. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::IfLetExpr> | |
| Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| // TODO: make having outer attributes an error? | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| if (!skip_token (IF)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| } | |
| // detect accidental if expr parsed as if let expr | |
| if (lexer.peek_token ()->get_id () != LET) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "if expression probably exists, but is being parsed as an " | |
| "if let expression. This may be a parser error"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| // parse match arm patterns (which are required) | |
| std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns | |
| = parse_match_arm_patterns (EQUAL); | |
| if (match_arm_patterns.empty ()) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse any match arm patterns in if let expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| if (!skip_token (EQUAL)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse expression (required) - HACK to prevent struct expr being parsed | |
| ParseRestrictions no_struct_expr; | |
| no_struct_expr.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> scrutinee_expr = parse_expr ({}, no_struct_expr); | |
| if (scrutinee_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse scrutinee expression in if let expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* TODO: check for expression not being a struct expression or lazy boolean | |
| * expression here? or actually probably in semantic analysis. */ | |
| // parse block expression (required) | |
| std::unique_ptr<AST::BlockExpr> if_let_body = parse_block_expr (); | |
| if (if_let_body == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse if let body block expression in if let expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // branch to parse end or else (and then else, else if, or else if let) | |
| if (lexer.peek_token ()->get_id () != ELSE) | |
| { | |
| // single selection - end of if let expression | |
| return std::unique_ptr<AST::IfLetExpr> ( | |
| new AST::IfLetExpr (std::move (match_arm_patterns), | |
| std::move (scrutinee_expr), std::move (if_let_body), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // double or multiple selection - branch on end, else if, or else if let | |
| // skip "else" | |
| lexer.skip_token (); | |
| // branch on whether next token is '{' or 'if' | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_CURLY: { | |
| // double selection - else | |
| // parse else block expr (required) | |
| std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr (); | |
| if (else_body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse else body block expression in " | |
| "if let expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IfLetExprConseqElse> ( | |
| new AST::IfLetExprConseqElse (std::move (match_arm_patterns), | |
| std::move (scrutinee_expr), | |
| std::move (if_let_body), | |
| std::move (else_body), | |
| std::move (outer_attrs), locus)); | |
| } | |
| case IF: { | |
| // multiple selection - else if or else if let | |
| // branch on whether next token is 'let' or not | |
| if (lexer.peek_token (1)->get_id () == LET) | |
| { | |
| // parse if let expr (required) | |
| std::unique_ptr<AST::IfLetExpr> if_let_expr | |
| = parse_if_let_expr (); | |
| if (if_let_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse (else) if let expression " | |
| "after if let expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IfLetExprConseqIfLet> ( | |
| new AST::IfLetExprConseqIfLet ( | |
| std::move (match_arm_patterns), std::move (scrutinee_expr), | |
| std::move (if_let_body), std::move (if_let_expr), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // parse if expr (required) | |
| std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr (); | |
| if (if_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse (else) if expression after " | |
| "if let expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IfLetExprConseqIf> ( | |
| new AST::IfLetExprConseqIf (std::move (match_arm_patterns), | |
| std::move (scrutinee_expr), | |
| std::move (if_let_body), | |
| std::move (if_expr), | |
| std::move (outer_attrs), locus)); | |
| } | |
| } | |
| default: | |
| // error - invalid token | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unexpected token %qs after else in if let expression", | |
| t->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| } | |
| } | |
| /* TODO: possibly decide on different method of handling label (i.e. not | |
| * parameter) */ | |
| /* Parses a "loop" infinite loop expression. Label is not parsed and should be | |
| * parsed via parse_labelled_loop_expr, which would call this. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::LoopExpr> | |
| Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs, | |
| AST::LoopLabel label, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| if (label.is_error ()) | |
| locus = lexer.peek_token ()->get_locus (); | |
| else | |
| locus = label.get_locus (); | |
| if (!skip_token (LOOP)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| } | |
| else | |
| { | |
| if (!label.is_error ()) | |
| locus = label.get_locus (); | |
| } | |
| // parse loop body, which is required | |
| std::unique_ptr<AST::BlockExpr> loop_body = parse_block_expr (); | |
| if (loop_body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse loop body in (infinite) loop expression"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::LoopExpr> ( | |
| new AST::LoopExpr (std::move (loop_body), locus, std::move (label), | |
| std::move (outer_attrs))); | |
| } | |
| /* Parses a "while" loop expression. Label is not parsed and should be parsed | |
| * via parse_labelled_loop_expr, which would call this. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::WhileLoopExpr> | |
| Parser<ManagedTokenSource>::parse_while_loop_expr (AST::AttrVec outer_attrs, | |
| AST::LoopLabel label, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| if (label.is_error ()) | |
| locus = lexer.peek_token ()->get_locus (); | |
| else | |
| locus = label.get_locus (); | |
| if (!skip_token (WHILE)) | |
| { | |
| skip_after_end_block (); | |
| return nullptr; | |
| } | |
| } | |
| else | |
| { | |
| if (!label.is_error ()) | |
| locus = label.get_locus (); | |
| } | |
| // ensure it isn't a while let loop | |
| if (lexer.peek_token ()->get_id () == LET) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "appears to be while let loop but is being parsed by " | |
| "while loop - this may be a compiler issue"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse loop predicate (required) with HACK to prevent struct expr parsing | |
| ParseRestrictions no_struct_expr; | |
| no_struct_expr.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> predicate = parse_expr ({}, no_struct_expr); | |
| if (predicate == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse predicate expression in while loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* TODO: check that it isn't struct expression here? actually, probably in | |
| * semantic analysis */ | |
| // parse loop body (required) | |
| std::unique_ptr<AST::BlockExpr> body = parse_block_expr (); | |
| if (body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse loop body block expression in while loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::WhileLoopExpr> ( | |
| new AST::WhileLoopExpr (std::move (predicate), std::move (body), locus, | |
| std::move (label), std::move (outer_attrs))); | |
| } | |
| /* Parses a "while let" loop expression. Label is not parsed and should be | |
| * parsed via parse_labelled_loop_expr, which would call this. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::WhileLetLoopExpr> | |
| Parser<ManagedTokenSource>::parse_while_let_loop_expr (AST::AttrVec outer_attrs, | |
| AST::LoopLabel label) | |
| { | |
| Location locus = Linemap::unknown_location (); | |
| if (label.is_error ()) | |
| locus = lexer.peek_token ()->get_locus (); | |
| else | |
| locus = label.get_locus (); | |
| skip_token (WHILE); | |
| /* check for possible accidental recognition of a while loop as a while let | |
| * loop */ | |
| if (lexer.peek_token ()->get_id () != LET) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "appears to be a while loop but is being parsed by " | |
| "while let loop - this may be a compiler issue"); | |
| add_error (std::move (error)); | |
| // skip somewhere | |
| return nullptr; | |
| } | |
| // as this token is definitely let now, save the computation of comparison | |
| lexer.skip_token (); | |
| // parse predicate patterns | |
| std::vector<std::unique_ptr<AST::Pattern>> predicate_patterns | |
| = parse_match_arm_patterns (EQUAL); | |
| // TODO: have to ensure that there is at least 1 pattern? | |
| if (!skip_token (EQUAL)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* parse predicate expression, which is required (and HACK to prevent struct | |
| * expr) */ | |
| ParseRestrictions no_struct_expr; | |
| no_struct_expr.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> predicate_expr = parse_expr ({}, no_struct_expr); | |
| if (predicate_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse predicate expression in while let loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* TODO: ensure that struct expression is not parsed? Actually, probably in | |
| * semantic analysis. */ | |
| // parse loop body, which is required | |
| std::unique_ptr<AST::BlockExpr> body = parse_block_expr (); | |
| if (body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse block expr (loop body) of while let loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::WhileLetLoopExpr> (new AST::WhileLetLoopExpr ( | |
| std::move (predicate_patterns), std::move (predicate_expr), | |
| std::move (body), locus, std::move (label), std::move (outer_attrs))); | |
| } | |
| /* Parses a "for" iterative loop. Label is not parsed and should be parsed via | |
| * parse_labelled_loop_expr, which would call this. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ForLoopExpr> | |
| Parser<ManagedTokenSource>::parse_for_loop_expr (AST::AttrVec outer_attrs, | |
| AST::LoopLabel label) | |
| { | |
| Location locus = Linemap::unknown_location (); | |
| if (label.is_error ()) | |
| locus = lexer.peek_token ()->get_locus (); | |
| else | |
| locus = label.get_locus (); | |
| skip_token (FOR); | |
| // parse pattern, which is required | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse iterator pattern in for loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| if (!skip_token (IN)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* parse iterator expression, which is required - also HACK to prevent | |
| * struct expr */ | |
| ParseRestrictions no_struct_expr; | |
| no_struct_expr.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> expr = parse_expr ({}, no_struct_expr); | |
| if (expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse iterator expression in for loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // TODO: check to ensure this isn't struct expr? Or in semantic analysis. | |
| // parse loop body, which is required | |
| std::unique_ptr<AST::BlockExpr> body = parse_block_expr (); | |
| if (body == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse loop body block expression in for loop"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ForLoopExpr> ( | |
| new AST::ForLoopExpr (std::move (pattern), std::move (expr), | |
| std::move (body), locus, std::move (label), | |
| std::move (outer_attrs))); | |
| } | |
| // Parses a loop expression with label (any kind of loop - disambiguates). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::BaseLoopExpr> | |
| Parser<ManagedTokenSource>::parse_labelled_loop_expr (AST::AttrVec outer_attrs) | |
| { | |
| /* TODO: decide whether it should not work if there is no label, or parse it | |
| * with no label at the moment, I will make it not work with no label | |
| * because that's the implication. */ | |
| if (lexer.peek_token ()->get_id () != LIFETIME) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "expected lifetime in labelled loop expr (to parse loop " | |
| "label) - found %qs", | |
| lexer.peek_token ()->get_token_description ()); | |
| add_error (std::move (error)); | |
| // skip? | |
| return nullptr; | |
| } | |
| // parse loop label (required) | |
| AST::LoopLabel label = parse_loop_label (); | |
| if (label.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse loop label in labelled loop expr"); | |
| add_error (std::move (error)); | |
| // skip? | |
| return nullptr; | |
| } | |
| // branch on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LOOP: | |
| return parse_loop_expr (std::move (outer_attrs), std::move (label)); | |
| case FOR: | |
| return parse_for_loop_expr (std::move (outer_attrs), std::move (label)); | |
| case WHILE: | |
| // further disambiguate into while vs while let | |
| if (lexer.peek_token (1)->get_id () == LET) | |
| { | |
| return parse_while_let_loop_expr (std::move (outer_attrs), | |
| std::move (label)); | |
| } | |
| else | |
| { | |
| return parse_while_loop_expr (std::move (outer_attrs), | |
| std::move (label)); | |
| } | |
| default: | |
| // error | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs when parsing labelled loop", | |
| t->get_token_description ())); | |
| // skip? | |
| return nullptr; | |
| } | |
| } | |
| // Parses a match expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MatchExpr> | |
| Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (MATCH_TOK); | |
| } | |
| /* parse scrutinee expression, which is required (and HACK to prevent struct | |
| * expr) */ | |
| ParseRestrictions no_struct_expr; | |
| no_struct_expr.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> scrutinee = parse_expr ({}, no_struct_expr); | |
| if (scrutinee == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse scrutinee expression in match expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| /* TODO: check for scrutinee expr not being struct expr? or do so in | |
| * semantic analysis */ | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse inner attributes (if they exist) | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse match arms (if they exist) | |
| // std::vector<std::unique_ptr<AST::MatchCase> > match_arms; | |
| std::vector<AST::MatchCase> match_arms; | |
| // parse match cases | |
| while (lexer.peek_token ()->get_id () != RIGHT_CURLY) | |
| { | |
| // parse match arm itself, which is required | |
| AST::MatchArm arm = parse_match_arm (); | |
| if (arm.is_error ()) | |
| { | |
| // TODO is this worth throwing everything away? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse match arm in match arms"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| if (!skip_token (MATCH_ARROW)) | |
| { | |
| // skip after somewhere? | |
| // TODO is returning here a good idea? or is break better? | |
| return nullptr; | |
| } | |
| ParseRestrictions restrictions; | |
| restrictions.expr_can_be_stmt = true; | |
| restrictions.consume_semi = false; | |
| std::unique_ptr<AST::ExprStmt> expr = parse_expr_stmt ({}, restrictions); | |
| if (expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse expr in match arm in match expr"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| bool is_expr_without_block | |
| = expr->get_type () == AST::ExprStmt::ExprStmtType::WITHOUT_BLOCK; | |
| // construct match case expr and add to cases | |
| switch (expr->get_type ()) | |
| { | |
| case AST::ExprStmt::ExprStmtType::WITH_BLOCK: { | |
| AST::ExprStmtWithBlock *cast | |
| = static_cast<AST::ExprStmtWithBlock *> (expr.get ()); | |
| std::unique_ptr<AST::Expr> e = cast->get_expr ()->clone_expr (); | |
| match_arms.push_back ( | |
| AST::MatchCase (std::move (arm), std::move (e))); | |
| } | |
| break; | |
| case AST::ExprStmt::ExprStmtType::WITHOUT_BLOCK: { | |
| AST::ExprStmtWithoutBlock *cast | |
| = static_cast<AST::ExprStmtWithoutBlock *> (expr.get ()); | |
| std::unique_ptr<AST::Expr> e = cast->get_expr ()->clone_expr (); | |
| match_arms.push_back ( | |
| AST::MatchCase (std::move (arm), std::move (e))); | |
| } | |
| break; | |
| } | |
| // handle comma presence | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| if (!is_expr_without_block) | |
| { | |
| // allowed even if not final case | |
| continue; | |
| } | |
| else if (is_expr_without_block | |
| && lexer.peek_token ()->get_id () != RIGHT_CURLY) | |
| { | |
| // not allowed if not final case | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "exprwithoutblock requires comma after match case " | |
| "expression in match arm (if not final case)"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| else | |
| { | |
| // otherwise, must be final case, so fine | |
| break; | |
| } | |
| } | |
| lexer.skip_token (); | |
| } | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| match_arms.shrink_to_fit (); | |
| return std::unique_ptr<AST::MatchExpr> ( | |
| new AST::MatchExpr (std::move (scrutinee), std::move (match_arms), | |
| std::move (inner_attrs), std::move (outer_attrs), | |
| locus)); | |
| } | |
| // Parses the "pattern" part of the match arm (the 'case x:' equivalent). | |
| template <typename ManagedTokenSource> | |
| AST::MatchArm | |
| Parser<ManagedTokenSource>::parse_match_arm () | |
| { | |
| // parse optional outer attributes | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // DEBUG | |
| rust_debug ("about to start parsing match arm patterns"); | |
| // break early if find right curly | |
| if (lexer.peek_token ()->get_id () == RIGHT_CURLY) | |
| { | |
| // not an error | |
| return AST::MatchArm::create_error (); | |
| } | |
| // parse match arm patterns - at least 1 is required | |
| std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns | |
| = parse_match_arm_patterns (RIGHT_CURLY); | |
| if (match_arm_patterns.empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse any patterns in match arm"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::MatchArm::create_error (); | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed match arm patterns"); | |
| // parse match arm guard expr if it exists | |
| std::unique_ptr<AST::Expr> guard_expr = nullptr; | |
| if (lexer.peek_token ()->get_id () == IF) | |
| { | |
| lexer.skip_token (); | |
| guard_expr = parse_expr (); | |
| if (guard_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse guard expression in match arm"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::MatchArm::create_error (); | |
| } | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed match arm"); | |
| return AST::MatchArm (std::move (match_arm_patterns), | |
| lexer.peek_token ()->get_locus (), | |
| std::move (guard_expr), std::move (outer_attrs)); | |
| } | |
| /* Parses the patterns used in a match arm. End token id is the id of the | |
| * token that would exist after the patterns are done (e.g. '}' for match | |
| * expr, '=' for if let and while let). */ | |
| template <typename ManagedTokenSource> | |
| std::vector<std::unique_ptr<AST::Pattern>> | |
| Parser<ManagedTokenSource>::parse_match_arm_patterns (TokenId end_token_id) | |
| { | |
| // skip optional leading '|' | |
| if (lexer.peek_token ()->get_id () == PIPE) | |
| lexer.skip_token (); | |
| /* TODO: do I even need to store the result of this? can't be used. | |
| * If semantically different, I need a wrapped "match arm patterns" object | |
| * for this. */ | |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; | |
| // quick break out if end_token_id | |
| if (lexer.peek_token ()->get_id () == end_token_id) | |
| return patterns; | |
| // parse required pattern - if doesn't exist, return empty | |
| std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern (); | |
| if (initial_pattern == nullptr) | |
| { | |
| // FIXME: should this be an error? | |
| return patterns; | |
| } | |
| patterns.push_back (std::move (initial_pattern)); | |
| // DEBUG | |
| rust_debug ("successfully parsed initial match arm pattern"); | |
| // parse new patterns as long as next char is '|' | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == PIPE) | |
| { | |
| // skip pipe token | |
| lexer.skip_token (); | |
| // break if hit end token id | |
| if (lexer.peek_token ()->get_id () == end_token_id) | |
| break; | |
| // parse pattern | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| // this is an error | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in match arm patterns"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return {}; | |
| } | |
| patterns.push_back (std::move (pattern)); | |
| t = lexer.peek_token (); | |
| } | |
| patterns.shrink_to_fit (); | |
| return patterns; | |
| } | |
| // Parses an async block expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::AsyncBlockExpr> | |
| Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs) | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (ASYNC); | |
| // detect optional move token | |
| bool has_move = false; | |
| if (lexer.peek_token ()->get_id () == MOVE) | |
| { | |
| lexer.skip_token (); | |
| has_move = true; | |
| } | |
| // parse block expression (required) | |
| std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr (); | |
| if (block_expr == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse block expression of async block expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::AsyncBlockExpr> ( | |
| new AST::AsyncBlockExpr (std::move (block_expr), has_move, | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses an unsafe block expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::UnsafeBlockExpr> | |
| Parser<ManagedTokenSource>::parse_unsafe_block_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (UNSAFE); | |
| } | |
| // parse block expression (required) | |
| std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr (); | |
| if (block_expr == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse block expression of unsafe block expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::UnsafeBlockExpr> ( | |
| new AST::UnsafeBlockExpr (std::move (block_expr), std::move (outer_attrs), | |
| locus)); | |
| } | |
| // Parses an array definition expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArrayExpr> | |
| Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs, | |
| Location pratt_parsed_loc) | |
| { | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LEFT_SQUARE); | |
| } | |
| // parse optional inner attributes | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // parse the "array elements" section, which is optional | |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) | |
| { | |
| // no array elements | |
| lexer.skip_token (); | |
| std::vector<std::unique_ptr<AST::Expr>> exprs; | |
| auto array_elems | |
| = Rust::make_unique<AST::ArrayElemsValues> (std::move (exprs), locus); | |
| return Rust::make_unique<AST::ArrayExpr> (std::move (array_elems), | |
| std::move (inner_attrs), | |
| std::move (outer_attrs), locus); | |
| } | |
| else | |
| { | |
| // should have array elements | |
| // parse initial expression, which is required for either | |
| std::unique_ptr<AST::Expr> initial_expr = parse_expr (); | |
| if (initial_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse expression in array expression " | |
| "(even though arrayelems seems to be present)"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // copy array elems | |
| lexer.skip_token (); | |
| // parse copy amount expression (required) | |
| std::unique_ptr<AST::Expr> copy_amount = parse_expr (); | |
| if (copy_amount == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse copy amount expression in array " | |
| "expression (arrayelems)"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| skip_token (RIGHT_SQUARE); | |
| std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems ( | |
| new AST::ArrayElemsCopied (std::move (initial_expr), | |
| std::move (copy_amount), locus)); | |
| return std::unique_ptr<AST::ArrayExpr> ( | |
| new AST::ArrayExpr (std::move (copied_array_elems), | |
| std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) | |
| { | |
| // single-element array expression | |
| std::vector<std::unique_ptr<AST::Expr>> exprs; | |
| exprs.reserve (1); | |
| exprs.push_back (std::move (initial_expr)); | |
| exprs.shrink_to_fit (); | |
| skip_token (RIGHT_SQUARE); | |
| std::unique_ptr<AST::ArrayElemsValues> array_elems ( | |
| new AST::ArrayElemsValues (std::move (exprs), locus)); | |
| return std::unique_ptr<AST::ArrayExpr> ( | |
| new AST::ArrayExpr (std::move (array_elems), | |
| std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else if (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| // multi-element array expression (or trailing comma) | |
| std::vector<std::unique_ptr<AST::Expr>> exprs; | |
| exprs.push_back (std::move (initial_expr)); | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // quick break if right square bracket | |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) | |
| break; | |
| // parse expression (required) | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse element in array expression"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| exprs.push_back (std::move (expr)); | |
| t = lexer.peek_token (); | |
| } | |
| skip_token (RIGHT_SQUARE); | |
| exprs.shrink_to_fit (); | |
| std::unique_ptr<AST::ArrayElemsValues> array_elems ( | |
| new AST::ArrayElemsValues (std::move (exprs), locus)); | |
| return std::unique_ptr<AST::ArrayExpr> ( | |
| new AST::ArrayExpr (std::move (array_elems), | |
| std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // error | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "unexpected token %qs in array expression (arrayelems)", | |
| lexer.peek_token ()->get_token_description ()); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| } | |
| } | |
| // Parses a single parameter used in a closure definition. | |
| template <typename ManagedTokenSource> | |
| AST::ClosureParam | |
| Parser<ManagedTokenSource>::parse_closure_param () | |
| { | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parse pattern (which is required) | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| // not necessarily an error | |
| return AST::ClosureParam::create_error (); | |
| } | |
| // parse optional type of param | |
| std::unique_ptr<AST::Type> type = nullptr; | |
| if (lexer.peek_token ()->get_id () == COLON) | |
| { | |
| lexer.skip_token (); | |
| // parse type, which is now required | |
| type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type in closure parameter"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::ClosureParam::create_error (); | |
| } | |
| } | |
| Location loc = pattern->get_locus (); | |
| return AST::ClosureParam (std::move (pattern), loc, std::move (type), | |
| std::move (outer_attrs)); | |
| } | |
| // Parses a grouped or tuple expression (disambiguates). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ExprWithoutBlock> | |
| Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr ( | |
| AST::AttrVec outer_attrs, Location pratt_parsed_loc) | |
| { | |
| // adjustment to allow Pratt parsing to reuse function without copy-paste | |
| Location locus = pratt_parsed_loc; | |
| if (locus == Linemap::unknown_location ()) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LEFT_PAREN); | |
| } | |
| // parse optional inner attributes | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| { | |
| // must be empty tuple | |
| lexer.skip_token (); | |
| // create tuple with empty tuple elems | |
| return std::unique_ptr<AST::TupleExpr> ( | |
| new AST::TupleExpr (std::vector<std::unique_ptr<AST::Expr>> (), | |
| std::move (inner_attrs), std::move (outer_attrs), | |
| locus)); | |
| } | |
| // parse first expression (required) | |
| std::unique_ptr<AST::Expr> first_expr = parse_expr (); | |
| if (first_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse expression in grouped or tuple expression"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| // detect whether grouped expression with right parentheses as next token | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| { | |
| // must be grouped expr | |
| lexer.skip_token (); | |
| // create grouped expr | |
| return std::unique_ptr<AST::GroupedExpr> ( | |
| new AST::GroupedExpr (std::move (first_expr), std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else if (lexer.peek_token ()->get_id () == COMMA) | |
| { | |
| // tuple expr | |
| std::vector<std::unique_ptr<AST::Expr>> exprs; | |
| exprs.push_back (std::move (first_expr)); | |
| // parse potential other tuple exprs | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // break out if right paren | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| break; | |
| // parse expr, which is now required | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse expr in tuple expr"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| exprs.push_back (std::move (expr)); | |
| t = lexer.peek_token (); | |
| } | |
| // skip right paren | |
| skip_token (RIGHT_PAREN); | |
| return std::unique_ptr<AST::TupleExpr> ( | |
| new AST::TupleExpr (std::move (exprs), std::move (inner_attrs), | |
| std::move (outer_attrs), locus)); | |
| } | |
| else | |
| { | |
| // error | |
| const_TokenPtr t = lexer.peek_token (); | |
| Error error (t->get_locus (), | |
| "unexpected token %qs in grouped or tuple expression " | |
| "(parenthesised expression) - expected %<)%> for grouped " | |
| "expr and %<,%> for tuple expr", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| } | |
| // Parses a type (will further disambiguate any type). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Type> | |
| Parser<ManagedTokenSource>::parse_type (bool save_errors) | |
| { | |
| /* rules for all types: | |
| * NeverType: '!' | |
| * SliceType: '[' Type ']' | |
| * InferredType: '_' | |
| * MacroInvocation: SimplePath '!' DelimTokenTree | |
| * ParenthesisedType: '(' Type ')' | |
| * ImplTraitType: 'impl' TypeParamBounds | |
| * TypeParamBounds (not type) TypeParamBound ( '+' TypeParamBound )* '+'? | |
| * TypeParamBound Lifetime | TraitBound | |
| * ImplTraitTypeOneBound: 'impl' TraitBound | |
| * TraitObjectType: 'dyn'? TypeParamBounds | |
| * TraitObjectTypeOneBound: 'dyn'? TraitBound | |
| * TraitBound '?'? ForLifetimes? TypePath | '(' '?'? | |
| * ForLifetimes? TypePath ')' BareFunctionType: ForLifetimes? | |
| * FunctionQualifiers 'fn' etc. ForLifetimes (not type) 'for' '<' | |
| * LifetimeParams '>' FunctionQualifiers ( 'async' | 'const' )? | |
| * 'unsafe'? | |
| * ('extern' abi?)? QualifiedPathInType: '<' Type ( 'as' TypePath )? '>' | |
| * ( | |
| * '::' TypePathSegment )+ TypePath: '::'? TypePathSegment ( | |
| * '::' TypePathSegment)* ArrayType: '[' Type ';' Expr ']' | |
| * ReferenceType: '&' Lifetime? 'mut'? TypeNoBounds | |
| * RawPointerType: '*' ( 'mut' | 'const' ) TypeNoBounds | |
| * TupleType: '(' Type etc. - regular tuple stuff. Also | |
| * regular tuple vs parenthesised precedence | |
| * | |
| * Disambiguate between macro and type path via type path being parsed, and | |
| * then if '!' found, convert type path to simple path for macro. Usual | |
| * disambiguation for tuple vs parenthesised. For ImplTraitType and | |
| * TraitObjectType individual disambiguations, they seem more like "special | |
| * cases", so probably just try to parse the more general ImplTraitType or | |
| * TraitObjectType and return OneBound versions if they satisfy those | |
| * criteria. */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: | |
| // never type - can't be macro as no path beforehand | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::NeverType> ( | |
| new AST::NeverType (t->get_locus ())); | |
| case LEFT_SQUARE: | |
| // slice type or array type - requires further disambiguation | |
| return parse_slice_or_array_type (); | |
| case LEFT_ANGLE: { | |
| // qualified path in type | |
| AST::QualifiedPathInType path = parse_qualified_path_in_type (); | |
| if (path.is_error ()) | |
| { | |
| if (save_errors) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse qualified path in type"); | |
| add_error (std::move (error)); | |
| } | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::QualifiedPathInType> ( | |
| new AST::QualifiedPathInType (std::move (path))); | |
| } | |
| case UNDERSCORE: | |
| // inferred type | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::InferredType> ( | |
| new AST::InferredType (t->get_locus ())); | |
| case ASTERISK: | |
| // raw pointer type | |
| return parse_raw_pointer_type (); | |
| case AMP: // does this also include AMP_AMP? | |
| // reference type | |
| return parse_reference_type (); | |
| case LIFETIME: { | |
| /* probably a lifetime bound, so probably type param bounds in | |
| * TraitObjectType */ | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds | |
| = parse_type_param_bounds (); | |
| return std::unique_ptr<AST::TraitObjectType> ( | |
| new AST::TraitObjectType (std::move (bounds), t->get_locus (), | |
| false)); | |
| } | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| case SCOPE_RESOLUTION: { | |
| // macro invocation or type path - requires further disambiguation. | |
| /* for parsing path component of each rule, perhaps parse it as a | |
| * typepath and attempt conversion to simplepath if a trailing '!' is | |
| * found */ | |
| /* Type path also includes TraitObjectTypeOneBound BUT if it starts | |
| * with it, it is exactly the same as a TypePath syntactically, so | |
| * this is a syntactical ambiguity. As such, the parser will parse it | |
| * as a TypePath. This, however, does not prevent TraitObjectType from | |
| * starting with a typepath. */ | |
| // parse path as type path | |
| AST::TypePath path = parse_type_path (); | |
| if (path.is_error ()) | |
| { | |
| if (save_errors) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse path as first component of type"); | |
| add_error (std::move (error)); | |
| } | |
| return nullptr; | |
| } | |
| Location locus = path.get_locus (); | |
| // branch on next token | |
| t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: { | |
| // macro invocation | |
| // convert to simple path | |
| AST::SimplePath macro_path = path.as_simple_path (); | |
| if (macro_path.is_empty ()) | |
| { | |
| if (save_errors) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse simple path in macro " | |
| "invocation (for type)"); | |
| add_error (std::move (error)); | |
| } | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| AST::DelimTokenTree tok_tree = parse_delim_token_tree (); | |
| return std::unique_ptr<AST::MacroInvocation> ( | |
| new AST::MacroInvocation ( | |
| AST::MacroInvocData (std::move (macro_path), | |
| std::move (tok_tree)), | |
| {}, locus)); | |
| } | |
| case PLUS: { | |
| // type param bounds | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds; | |
| // convert type path to trait bound | |
| std::unique_ptr<AST::TraitBound> path_bound ( | |
| new AST::TraitBound (std::move (path), locus, false, false)); | |
| bounds.push_back (std::move (path_bound)); | |
| /* parse rest of bounds - FIXME: better way to find when to stop | |
| * parsing */ | |
| while (t->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| // parse bound if it exists - if not, assume end of sequence | |
| std::unique_ptr<AST::TypeParamBound> bound | |
| = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| break; | |
| } | |
| bounds.push_back (std::move (bound)); | |
| t = lexer.peek_token (); | |
| } | |
| return std::unique_ptr<AST::TraitObjectType> ( | |
| new AST::TraitObjectType (std::move (bounds), locus, false)); | |
| } | |
| default: | |
| // assume that this is a type path and not an error | |
| return std::unique_ptr<AST::TypePath> ( | |
| new AST::TypePath (std::move (path))); | |
| } | |
| } | |
| case LEFT_PAREN: | |
| /* tuple type or parenthesised type - requires further disambiguation | |
| * (the usual). ok apparently can be a parenthesised TraitBound too, so | |
| * could be TraitObjectTypeOneBound or TraitObjectType */ | |
| return parse_paren_prefixed_type (); | |
| case FOR: | |
| // TraitObjectTypeOneBound or BareFunctionType | |
| return parse_for_prefixed_type (); | |
| case ASYNC: | |
| case CONST: | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| // bare function type (with no for lifetimes) | |
| return parse_bare_function_type (std::vector<AST::LifetimeParam> ()); | |
| case IMPL: | |
| lexer.skip_token (); | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| /* cannot be one bound because lifetime prevents it from being | |
| * traitbound */ | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds | |
| = parse_type_param_bounds (); | |
| return std::unique_ptr<AST::ImplTraitType> ( | |
| new AST::ImplTraitType (std::move (bounds), t->get_locus ())); | |
| } | |
| else | |
| { | |
| // should be trait bound, so parse trait bound | |
| std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound (); | |
| if (initial_bound == nullptr) | |
| { | |
| if (save_errors) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse ImplTraitType initial bound"); | |
| add_error (std::move (error)); | |
| } | |
| return nullptr; | |
| } | |
| Location locus = t->get_locus (); | |
| // short cut if next token isn't '+' | |
| t = lexer.peek_token (); | |
| if (t->get_id () != PLUS) | |
| { | |
| // convert trait bound to value object | |
| AST::TraitBound value_bound (*initial_bound); | |
| // DEBUG: removed as unique ptr, so should auto-delete | |
| // delete initial_bound; | |
| return std::unique_ptr<AST::ImplTraitTypeOneBound> ( | |
| new AST::ImplTraitTypeOneBound (std::move (value_bound), | |
| locus)); | |
| } | |
| // parse additional type param bounds | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds; | |
| bounds.push_back (std::move (initial_bound)); | |
| while (t->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| // parse bound if it exists | |
| std::unique_ptr<AST::TypeParamBound> bound | |
| = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| // not an error as trailing plus may exist | |
| break; | |
| } | |
| bounds.push_back (std::move (bound)); | |
| t = lexer.peek_token (); | |
| } | |
| return std::unique_ptr<AST::ImplTraitType> ( | |
| new AST::ImplTraitType (std::move (bounds), locus)); | |
| } | |
| case DYN: | |
| case QUESTION_MARK: { | |
| // either TraitObjectType or TraitObjectTypeOneBound | |
| bool has_dyn = false; | |
| if (t->get_id () == DYN) | |
| { | |
| lexer.skip_token (); | |
| has_dyn = true; | |
| } | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| /* cannot be one bound because lifetime prevents it from being | |
| * traitbound */ | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds | |
| = parse_type_param_bounds (); | |
| return std::unique_ptr<AST::TraitObjectType> ( | |
| new AST::TraitObjectType (std::move (bounds), t->get_locus (), | |
| has_dyn)); | |
| } | |
| else | |
| { | |
| // should be trait bound, so parse trait bound | |
| std::unique_ptr<AST::TraitBound> initial_bound | |
| = parse_trait_bound (); | |
| if (initial_bound == nullptr) | |
| { | |
| if (save_errors) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse TraitObjectType initial bound"); | |
| add_error (std::move (error)); | |
| } | |
| return nullptr; | |
| } | |
| // short cut if next token isn't '+' | |
| t = lexer.peek_token (); | |
| if (t->get_id () != PLUS) | |
| { | |
| // convert trait bound to value object | |
| AST::TraitBound value_bound (*initial_bound); | |
| // DEBUG: removed as unique ptr, so should auto delete | |
| // delete initial_bound; | |
| return std::unique_ptr<AST::TraitObjectTypeOneBound> ( | |
| new AST::TraitObjectTypeOneBound (std::move (value_bound), | |
| t->get_locus (), has_dyn)); | |
| } | |
| // parse additional type param bounds | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds; | |
| bounds.push_back (std::move (initial_bound)); | |
| while (t->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| // parse bound if it exists | |
| std::unique_ptr<AST::TypeParamBound> bound | |
| = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| // not an error as trailing plus may exist | |
| break; | |
| } | |
| bounds.push_back (std::move (bound)); | |
| t = lexer.peek_token (); | |
| } | |
| return std::unique_ptr<AST::TraitObjectType> ( | |
| new AST::TraitObjectType (std::move (bounds), t->get_locus (), | |
| has_dyn)); | |
| } | |
| } | |
| default: | |
| if (save_errors) | |
| add_error (Error (t->get_locus (), "unrecognised token %qs in type", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| /* Parses a type that has '(' as its first character. Returns a tuple type, | |
| * parenthesised type, TraitObjectTypeOneBound, or TraitObjectType depending | |
| * on following characters. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Type> | |
| Parser<ManagedTokenSource>::parse_paren_prefixed_type () | |
| { | |
| /* NOTE: Syntactical ambiguity of a parenthesised trait bound is considered | |
| * a trait bound, not a parenthesised type, so that it can still be used in | |
| * type param bounds. */ | |
| /* NOTE: this implementation is really shit but I couldn't think of a better | |
| * one. It requires essentially breaking polymorphism and downcasting via | |
| * virtual method abuse, as it was copied from the rustc implementation (in | |
| * which types are reified due to tagged union), after a more OOP attempt by | |
| * me failed. */ | |
| Location left_delim_locus = lexer.peek_token ()->get_locus (); | |
| // skip left delim | |
| lexer.skip_token (); | |
| /* while next token isn't close delim, parse comma-separated types, saving | |
| * whether trailing comma happens */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| bool trailing_comma = true; | |
| std::vector<std::unique_ptr<AST::Type>> types; | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse type inside parentheses (probably " | |
| "tuple or parenthesised)"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| types.push_back (std::move (type)); | |
| t = lexer.peek_token (); | |
| if (t->get_id () != COMMA) | |
| { | |
| trailing_comma = false; | |
| break; | |
| } | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| // if only one type and no trailing comma, then not a tuple type | |
| if (types.size () == 1 && !trailing_comma) | |
| { | |
| // must be a TraitObjectType (with more than one bound) | |
| if (lexer.peek_token ()->get_id () == PLUS) | |
| { | |
| // create type param bounds vector | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds; | |
| // HACK: convert type to traitbound and add to bounds | |
| std::unique_ptr<AST::Type> released_ptr = std::move (types[0]); | |
| std::unique_ptr<AST::TraitBound> converted_bound ( | |
| released_ptr->to_trait_bound (true)); | |
| if (converted_bound == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to hackily converted parsed type to trait bound"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| bounds.push_back (std::move (converted_bound)); | |
| t = lexer.peek_token (); | |
| while (t->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| // attempt to parse typeparambound | |
| std::unique_ptr<AST::TypeParamBound> bound | |
| = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| // not an error if null | |
| break; | |
| } | |
| bounds.push_back (std::move (bound)); | |
| t = lexer.peek_token (); | |
| } | |
| return std::unique_ptr<AST::TraitObjectType> ( | |
| new AST::TraitObjectType (std::move (bounds), left_delim_locus, | |
| false)); | |
| } | |
| else | |
| { | |
| // release vector pointer | |
| std::unique_ptr<AST::Type> released_ptr = std::move (types[0]); | |
| /* HACK: attempt to convert to trait bound. if fails, parenthesised | |
| * type */ | |
| std::unique_ptr<AST::TraitBound> converted_bound ( | |
| released_ptr->to_trait_bound (true)); | |
| if (converted_bound == nullptr) | |
| { | |
| // parenthesised type | |
| return std::unique_ptr<AST::ParenthesisedType> ( | |
| new AST::ParenthesisedType (std::move (released_ptr), | |
| left_delim_locus)); | |
| } | |
| else | |
| { | |
| // trait object type (one bound) | |
| // get value semantics trait bound | |
| AST::TraitBound value_bound (*converted_bound); | |
| return std::unique_ptr<AST::TraitObjectTypeOneBound> ( | |
| new AST::TraitObjectTypeOneBound (value_bound, | |
| left_delim_locus)); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| return std::unique_ptr<AST::TupleType> ( | |
| new AST::TupleType (std::move (types), left_delim_locus)); | |
| } | |
| /* TODO: ensure that this ensures that dynamic dispatch for traits is not | |
| * lost somehow */ | |
| } | |
| /* Parses a type that has 'for' as its first character. This means it has a | |
| * "for lifetimes", so returns either a BareFunctionType, TraitObjectType, or | |
| * TraitObjectTypeOneBound depending on following characters. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Type> | |
| Parser<ManagedTokenSource>::parse_for_prefixed_type () | |
| { | |
| Location for_locus = lexer.peek_token ()->get_locus (); | |
| // parse for lifetimes in type | |
| std::vector<AST::LifetimeParam> for_lifetimes = parse_for_lifetimes (); | |
| // branch on next token - either function or a trait type | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case ASYNC: | |
| case CONST: | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| return parse_bare_function_type (std::move (for_lifetimes)); | |
| case SCOPE_RESOLUTION: | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case DOLLAR_SIGN: { | |
| // path, so trait type | |
| // parse type path to finish parsing trait bound | |
| AST::TypePath path = parse_type_path (); | |
| t = lexer.peek_token (); | |
| if (t->get_id () != PLUS) | |
| { | |
| // must be one-bound trait type | |
| // create trait bound value object | |
| AST::TraitBound bound (std::move (path), for_locus, false, false, | |
| std::move (for_lifetimes)); | |
| return std::unique_ptr<AST::TraitObjectTypeOneBound> ( | |
| new AST::TraitObjectTypeOneBound (std::move (bound), for_locus)); | |
| } | |
| /* more than one bound trait type (or at least parsed as it - could be | |
| * trailing '+') create trait bound pointer and bounds */ | |
| std::unique_ptr<AST::TraitBound> initial_bound ( | |
| new AST::TraitBound (std::move (path), for_locus, false, false, | |
| std::move (for_lifetimes))); | |
| std::vector<std::unique_ptr<AST::TypeParamBound>> bounds; | |
| bounds.push_back (std::move (initial_bound)); | |
| while (t->get_id () == PLUS) | |
| { | |
| lexer.skip_token (); | |
| // parse type param bound if it exists | |
| std::unique_ptr<AST::TypeParamBound> bound | |
| = parse_type_param_bound (); | |
| if (bound == nullptr) | |
| { | |
| // not an error - e.g. trailing plus | |
| return nullptr; | |
| } | |
| bounds.push_back (std::move (bound)); | |
| t = lexer.peek_token (); | |
| } | |
| return std::unique_ptr<AST::TraitObjectType> ( | |
| new AST::TraitObjectType (std::move (bounds), for_locus, false)); | |
| } | |
| default: | |
| // error | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs in bare function type or trait " | |
| "object type or trait object type one bound", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses a maybe named param used in bare function types. | |
| template <typename ManagedTokenSource> | |
| AST::MaybeNamedParam | |
| Parser<ManagedTokenSource>::parse_maybe_named_param (AST::AttrVec outer_attrs) | |
| { | |
| /* Basically guess that param is named if first token is identifier or | |
| * underscore and second token is semicolon. This should probably have no | |
| * exceptions. rustc uses backtracking to parse these, but at the time of | |
| * writing gccrs has no backtracking capabilities. */ | |
| const_TokenPtr current = lexer.peek_token (); | |
| const_TokenPtr next = lexer.peek_token (1); | |
| Identifier name; | |
| AST::MaybeNamedParam::ParamKind kind = AST::MaybeNamedParam::UNNAMED; | |
| if (current->get_id () == IDENTIFIER && next->get_id () == COLON) | |
| { | |
| // named param | |
| name = current->get_str (); | |
| kind = AST::MaybeNamedParam::IDENTIFIER; | |
| lexer.skip_token (1); | |
| } | |
| else if (current->get_id () == UNDERSCORE && next->get_id () == COLON) | |
| { | |
| // wildcard param | |
| name = "_"; | |
| kind = AST::MaybeNamedParam::WILDCARD; | |
| lexer.skip_token (1); | |
| } | |
| // parse type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse type in maybe named param"); | |
| add_error (std::move (error)); | |
| return AST::MaybeNamedParam::create_error (); | |
| } | |
| return AST::MaybeNamedParam (std::move (name), kind, std::move (type), | |
| std::move (outer_attrs), current->get_locus ()); | |
| } | |
| /* Parses a bare function type (with the given for lifetimes for convenience - | |
| * does not parse them itself). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::BareFunctionType> | |
| Parser<ManagedTokenSource>::parse_bare_function_type ( | |
| std::vector<AST::LifetimeParam> for_lifetimes) | |
| { | |
| // TODO: pass in for lifetime location as param | |
| Location best_try_locus = lexer.peek_token ()->get_locus (); | |
| AST::FunctionQualifiers qualifiers = parse_function_qualifiers (); | |
| if (!skip_token (FN_TOK)) | |
| return nullptr; | |
| if (!skip_token (LEFT_PAREN)) | |
| return nullptr; | |
| // parse function params, if they exist | |
| std::vector<AST::MaybeNamedParam> params; | |
| bool is_variadic = false; | |
| AST::AttrVec variadic_attrs; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| AST::AttrVec temp_attrs = parse_outer_attributes (); | |
| if (lexer.peek_token ()->get_id () == ELLIPSIS) | |
| { | |
| lexer.skip_token (); | |
| is_variadic = true; | |
| variadic_attrs = std::move (temp_attrs); | |
| t = lexer.peek_token (); | |
| if (t->get_id () != RIGHT_PAREN) | |
| { | |
| Error error (t->get_locus (), | |
| "expected right parentheses after variadic in maybe " | |
| "named function " | |
| "parameters, found %qs", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| break; | |
| } | |
| AST::MaybeNamedParam param | |
| = parse_maybe_named_param (std::move (temp_attrs)); | |
| if (param.is_error ()) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse maybe named param in bare function type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| return nullptr; | |
| // bare function return type, if exists | |
| std::unique_ptr<AST::TypeNoBounds> return_type = nullptr; | |
| if (lexer.peek_token ()->get_id () == RETURN_TYPE) | |
| { | |
| lexer.skip_token (); | |
| // parse required TypeNoBounds | |
| return_type = parse_type_no_bounds (); | |
| if (return_type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse return type (type no bounds) in bare " | |
| "function type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| } | |
| return std::unique_ptr<AST::BareFunctionType> ( | |
| new AST::BareFunctionType (std::move (for_lifetimes), | |
| std::move (qualifiers), std::move (params), | |
| is_variadic, std::move (variadic_attrs), | |
| std::move (return_type), best_try_locus)); | |
| } | |
| // Parses a reference type (mutable or immutable, with given lifetime). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ReferenceType> | |
| Parser<ManagedTokenSource>::parse_reference_type () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (AMP); | |
| // parse optional lifetime | |
| AST::Lifetime lifetime = AST::Lifetime::error (); | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| lifetime = parse_lifetime (); | |
| if (lifetime.is_error ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse lifetime in reference type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| } | |
| bool is_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| lexer.skip_token (); | |
| is_mut = true; | |
| } | |
| // parse type no bounds, which is required | |
| std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse referenced type in reference type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ReferenceType> ( | |
| new AST::ReferenceType (is_mut, std::move (type), locus, | |
| std::move (lifetime))); | |
| } | |
| // Parses a raw (unsafe) pointer type. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::RawPointerType> | |
| Parser<ManagedTokenSource>::parse_raw_pointer_type () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (ASTERISK); | |
| AST::RawPointerType::PointerType kind = AST::RawPointerType::CONST; | |
| // branch on next token for pointer kind info | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case MUT: | |
| kind = AST::RawPointerType::MUT; | |
| lexer.skip_token (); | |
| break; | |
| case CONST: | |
| kind = AST::RawPointerType::CONST; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs in raw pointer type", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| // parse type no bounds (required) | |
| std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pointed type of raw pointer type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::RawPointerType> ( | |
| new AST::RawPointerType (kind, std::move (type), locus)); | |
| } | |
| /* Parses a slice or array type, depending on following arguments (as | |
| * lookahead is not possible). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeNoBounds> | |
| Parser<ManagedTokenSource>::parse_slice_or_array_type () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LEFT_SQUARE); | |
| // parse inner type (required) | |
| std::unique_ptr<AST::Type> inner_type = parse_type (); | |
| if (inner_type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse inner type in slice or array type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // branch on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case RIGHT_SQUARE: | |
| // slice type | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::SliceType> ( | |
| new AST::SliceType (std::move (inner_type), locus)); | |
| case SEMICOLON: { | |
| // array type | |
| lexer.skip_token (); | |
| // parse required array size expression | |
| std::unique_ptr<AST::Expr> size = parse_expr (); | |
| if (size == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse size expression in array type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| if (!skip_token (RIGHT_SQUARE)) | |
| { | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ArrayType> ( | |
| new AST::ArrayType (std::move (inner_type), std::move (size), locus)); | |
| } | |
| default: | |
| // error | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unrecognised token %qs in slice or array type after inner type", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses a type, taking into account type boundary disambiguation. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeNoBounds> | |
| Parser<ManagedTokenSource>::parse_type_no_bounds () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: | |
| // never type - can't be macro as no path beforehand | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::NeverType> ( | |
| new AST::NeverType (t->get_locus ())); | |
| case LEFT_SQUARE: | |
| // slice type or array type - requires further disambiguation | |
| return parse_slice_or_array_type (); | |
| case LEFT_ANGLE: { | |
| // qualified path in type | |
| AST::QualifiedPathInType path = parse_qualified_path_in_type (); | |
| if (path.is_error ()) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse qualified path in type"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::QualifiedPathInType> ( | |
| new AST::QualifiedPathInType (std::move (path))); | |
| } | |
| case UNDERSCORE: | |
| // inferred type | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::InferredType> ( | |
| new AST::InferredType (t->get_locus ())); | |
| case ASTERISK: | |
| // raw pointer type | |
| return parse_raw_pointer_type (); | |
| case AMP: // does this also include AMP_AMP? | |
| // reference type | |
| return parse_reference_type (); | |
| case LIFETIME: | |
| /* probably a lifetime bound, so probably type param bounds in | |
| * TraitObjectType. this is not allowed, but detection here for error | |
| * message */ | |
| add_error (Error (t->get_locus (), | |
| "lifetime bounds (i.e. in type param bounds, in " | |
| "TraitObjectType) are not allowed as TypeNoBounds")); | |
| return nullptr; | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case DOLLAR_SIGN: | |
| case SCOPE_RESOLUTION: { | |
| // macro invocation or type path - requires further disambiguation. | |
| /* for parsing path component of each rule, perhaps parse it as a | |
| * typepath and attempt conversion to simplepath if a trailing '!' is | |
| * found */ | |
| /* Type path also includes TraitObjectTypeOneBound BUT if it starts | |
| * with it, it is exactly the same as a TypePath syntactically, so | |
| * this is a syntactical ambiguity. As such, the parser will parse it | |
| * as a TypePath. This, however, does not prevent TraitObjectType from | |
| * starting with a typepath. */ | |
| // parse path as type path | |
| AST::TypePath path = parse_type_path (); | |
| if (path.is_error ()) | |
| { | |
| Error error ( | |
| t->get_locus (), | |
| "failed to parse path as first component of type no bounds"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| Location locus = path.get_locus (); | |
| // branch on next token | |
| t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: { | |
| // macro invocation | |
| // convert to simple path | |
| AST::SimplePath macro_path = path.as_simple_path (); | |
| if (macro_path.is_empty ()) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse simple path in macro " | |
| "invocation (for type)"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| AST::DelimTokenTree tok_tree = parse_delim_token_tree (); | |
| return std::unique_ptr<AST::MacroInvocation> ( | |
| new AST::MacroInvocation ( | |
| AST::MacroInvocData (std::move (macro_path), | |
| std::move (tok_tree)), | |
| {}, locus)); | |
| } | |
| default: | |
| // assume that this is a type path and not an error | |
| return std::unique_ptr<AST::TypePath> ( | |
| new AST::TypePath (std::move (path))); | |
| } | |
| } | |
| case LEFT_PAREN: | |
| /* tuple type or parenthesised type - requires further disambiguation | |
| * (the usual). ok apparently can be a parenthesised TraitBound too, so | |
| * could be TraitObjectTypeOneBound */ | |
| return parse_paren_prefixed_type_no_bounds (); | |
| case FOR: | |
| case ASYNC: | |
| case CONST: | |
| case UNSAFE: | |
| case EXTERN_TOK: | |
| case FN_TOK: | |
| // bare function type (with no for lifetimes) | |
| return parse_bare_function_type (std::vector<AST::LifetimeParam> ()); | |
| case IMPL: | |
| lexer.skip_token (); | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| /* cannot be one bound because lifetime prevents it from being | |
| * traitbound not allowed as type no bounds, only here for error | |
| * message */ | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "lifetime (probably lifetime bound, in type param " | |
| "bounds, in ImplTraitType) is not allowed in TypeNoBounds"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| else | |
| { | |
| // should be trait bound, so parse trait bound | |
| std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound (); | |
| if (initial_bound == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse ImplTraitTypeOneBound bound"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| Location locus = t->get_locus (); | |
| // ensure not a trait with multiple bounds | |
| t = lexer.peek_token (); | |
| if (t->get_id () == PLUS) | |
| { | |
| Error error (t->get_locus (), | |
| "plus after trait bound means an ImplTraitType, " | |
| "which is not allowed as a TypeNoBounds"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // convert trait bound to value object | |
| AST::TraitBound value_bound (*initial_bound); | |
| return std::unique_ptr<AST::ImplTraitTypeOneBound> ( | |
| new AST::ImplTraitTypeOneBound (std::move (value_bound), locus)); | |
| } | |
| case DYN: | |
| case QUESTION_MARK: { | |
| // either TraitObjectTypeOneBound | |
| bool has_dyn = false; | |
| if (t->get_id () == DYN) | |
| { | |
| lexer.skip_token (); | |
| has_dyn = true; | |
| } | |
| if (lexer.peek_token ()->get_id () == LIFETIME) | |
| { | |
| /* means that cannot be TraitObjectTypeOneBound - so here for | |
| * error message */ | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "lifetime as bound in TraitObjectTypeOneBound " | |
| "is not allowed, so cannot be TypeNoBounds"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // should be trait bound, so parse trait bound | |
| std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound (); | |
| if (initial_bound == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse TraitObjectTypeOneBound initial bound"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| Location locus = t->get_locus (); | |
| // detect error with plus as next token | |
| t = lexer.peek_token (); | |
| if (t->get_id () == PLUS) | |
| { | |
| Error error (t->get_locus (), | |
| "plus after trait bound means a TraitObjectType, " | |
| "which is not allowed as a TypeNoBounds"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // convert trait bound to value object | |
| AST::TraitBound value_bound (*initial_bound); | |
| return std::unique_ptr<AST::TraitObjectTypeOneBound> ( | |
| new AST::TraitObjectTypeOneBound (std::move (value_bound), locus, | |
| has_dyn)); | |
| } | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs in type no bounds", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses a type no bounds beginning with '('. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeNoBounds> | |
| Parser<ManagedTokenSource>::parse_paren_prefixed_type_no_bounds () | |
| { | |
| /* NOTE: this could probably be parsed without the HACK solution of | |
| * parse_paren_prefixed_type, but I was lazy. So FIXME for future.*/ | |
| /* NOTE: again, syntactical ambiguity of a parenthesised trait bound is | |
| * considered a trait bound, not a parenthesised type, so that it can still | |
| * be used in type param bounds. */ | |
| Location left_paren_locus = lexer.peek_token ()->get_locus (); | |
| // skip left delim | |
| lexer.skip_token (); | |
| /* while next token isn't close delim, parse comma-separated types, saving | |
| * whether trailing comma happens */ | |
| const_TokenPtr t = lexer.peek_token (); | |
| bool trailing_comma = true; | |
| std::vector<std::unique_ptr<AST::Type>> types; | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse type inside parentheses (probably " | |
| "tuple or parenthesised)"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| types.push_back (std::move (type)); | |
| t = lexer.peek_token (); | |
| if (t->get_id () != COMMA) | |
| { | |
| trailing_comma = false; | |
| break; | |
| } | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| // if only one type and no trailing comma, then not a tuple type | |
| if (types.size () == 1 && !trailing_comma) | |
| { | |
| // must be a TraitObjectType (with more than one bound) | |
| if (lexer.peek_token ()->get_id () == PLUS) | |
| { | |
| // error - this is not allowed for type no bounds | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "plus (implying TraitObjectType as type param " | |
| "bounds) is not allowed in type no bounds"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| else | |
| { | |
| // release vector pointer | |
| std::unique_ptr<AST::Type> released_ptr = std::move (types[0]); | |
| /* HACK: attempt to convert to trait bound. if fails, parenthesised | |
| * type */ | |
| std::unique_ptr<AST::TraitBound> converted_bound ( | |
| released_ptr->to_trait_bound (true)); | |
| if (converted_bound == nullptr) | |
| { | |
| // parenthesised type | |
| return std::unique_ptr<AST::ParenthesisedType> ( | |
| new AST::ParenthesisedType (std::move (released_ptr), | |
| left_paren_locus)); | |
| } | |
| else | |
| { | |
| // trait object type (one bound) | |
| // get value semantics trait bound | |
| AST::TraitBound value_bound (*converted_bound); | |
| return std::unique_ptr<AST::TraitObjectTypeOneBound> ( | |
| new AST::TraitObjectTypeOneBound (value_bound, | |
| left_paren_locus)); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| return std::unique_ptr<AST::TupleType> ( | |
| new AST::TupleType (std::move (types), left_paren_locus)); | |
| } | |
| /* TODO: ensure that this ensures that dynamic dispatch for traits is not | |
| * lost somehow */ | |
| } | |
| /* Parses a literal pattern or range pattern. Assumes that literals passed in | |
| * are valid range pattern bounds. Do not pass in paths in expressions, for | |
| * instance. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Pattern> | |
| Parser<ManagedTokenSource>::parse_literal_or_range_pattern () | |
| { | |
| const_TokenPtr range_lower = lexer.peek_token (); | |
| AST::Literal::LitType type = AST::Literal::STRING; | |
| bool has_minus = false; | |
| // get lit type | |
| switch (range_lower->get_id ()) | |
| { | |
| case CHAR_LITERAL: | |
| type = AST::Literal::CHAR; | |
| lexer.skip_token (); | |
| break; | |
| case BYTE_CHAR_LITERAL: | |
| type = AST::Literal::BYTE; | |
| lexer.skip_token (); | |
| break; | |
| case INT_LITERAL: | |
| type = AST::Literal::INT; | |
| lexer.skip_token (); | |
| break; | |
| case FLOAT_LITERAL: | |
| type = AST::Literal::FLOAT; | |
| lexer.skip_token (); | |
| break; | |
| case MINUS: | |
| // branch on next token | |
| range_lower = lexer.peek_token (1); | |
| switch (range_lower->get_id ()) | |
| { | |
| case INT_LITERAL: | |
| type = AST::Literal::INT; | |
| has_minus = true; | |
| lexer.skip_token (1); | |
| break; | |
| case FLOAT_LITERAL: | |
| type = AST::Literal::FLOAT; | |
| has_minus = true; | |
| lexer.skip_token (1); | |
| break; | |
| default: | |
| add_error (Error (range_lower->get_locus (), | |
| "token type %qs cannot be parsed as range pattern " | |
| "bound or literal after minus symbol", | |
| range_lower->get_token_description ())); | |
| return nullptr; | |
| } | |
| break; | |
| default: | |
| add_error ( | |
| Error (range_lower->get_locus (), | |
| "token type %qs cannot be parsed as range pattern bound", | |
| range_lower->get_token_description ())); | |
| return nullptr; | |
| } | |
| const_TokenPtr next = lexer.peek_token (); | |
| if (next->get_id () == DOT_DOT_EQ || next->get_id () == ELLIPSIS) | |
| { | |
| // range pattern | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::RangePatternBound> lower ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), type, | |
| PrimitiveCoreType::CORETYPE_UNKNOWN), | |
| range_lower->get_locus (), has_minus)); | |
| std::unique_ptr<AST::RangePatternBound> upper | |
| = parse_range_pattern_bound (); | |
| if (upper == nullptr) | |
| { | |
| Error error (next->get_locus (), | |
| "failed to parse range pattern bound in range pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::RangePattern> ( | |
| new AST::RangePattern (std::move (lower), std::move (upper), | |
| range_lower->get_locus ())); | |
| } | |
| else | |
| { | |
| // literal pattern | |
| return std::unique_ptr<AST::LiteralPattern> ( | |
| new AST::LiteralPattern (range_lower->get_str (), type, | |
| range_lower->get_locus ())); | |
| } | |
| } | |
| // Parses a range pattern bound (value only). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::RangePatternBound> | |
| Parser<ManagedTokenSource>::parse_range_pattern_bound () | |
| { | |
| const_TokenPtr range_lower = lexer.peek_token (); | |
| Location range_lower_locus = range_lower->get_locus (); | |
| // get lit type | |
| switch (range_lower->get_id ()) | |
| { | |
| case CHAR_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), AST::Literal::CHAR, | |
| range_lower->get_type_hint ()), | |
| range_lower_locus)); | |
| case BYTE_CHAR_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), AST::Literal::BYTE, | |
| range_lower->get_type_hint ()), | |
| range_lower_locus)); | |
| case INT_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), AST::Literal::INT, | |
| range_lower->get_type_hint ()), | |
| range_lower_locus)); | |
| case FLOAT_LITERAL: | |
| lexer.skip_token (); | |
| rust_debug ("warning: used deprecated float range pattern bound"); | |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), AST::Literal::FLOAT, | |
| range_lower->get_type_hint ()), | |
| range_lower_locus)); | |
| case MINUS: | |
| // branch on next token | |
| range_lower = lexer.peek_token (1); | |
| switch (range_lower->get_id ()) | |
| { | |
| case INT_LITERAL: | |
| lexer.skip_token (1); | |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), AST::Literal::INT, | |
| range_lower->get_type_hint ()), | |
| range_lower_locus, true)); | |
| case FLOAT_LITERAL: | |
| lexer.skip_token (1); | |
| rust_debug ("warning: used deprecated float range pattern bound"); | |
| return std::unique_ptr<AST::RangePatternBoundLiteral> ( | |
| new AST::RangePatternBoundLiteral ( | |
| AST::Literal (range_lower->get_str (), AST::Literal::FLOAT, | |
| range_lower->get_type_hint ()), | |
| range_lower_locus, true)); | |
| default: | |
| add_error (Error (range_lower->get_locus (), | |
| "token type %qs cannot be parsed as range pattern " | |
| "bound after minus symbol", | |
| range_lower->get_token_description ())); | |
| return nullptr; | |
| } | |
| case IDENTIFIER: | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case SCOPE_RESOLUTION: | |
| case DOLLAR_SIGN: { | |
| // path in expression | |
| AST::PathInExpression path = parse_path_in_expression (); | |
| if (path.is_error ()) | |
| { | |
| Error error ( | |
| range_lower->get_locus (), | |
| "failed to parse path in expression range pattern bound"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::RangePatternBoundPath> ( | |
| new AST::RangePatternBoundPath (std::move (path))); | |
| } | |
| case LEFT_ANGLE: { | |
| // qualified path in expression | |
| AST::QualifiedPathInExpression path | |
| = parse_qualified_path_in_expression (); | |
| if (path.is_error ()) | |
| { | |
| Error error (range_lower->get_locus (), | |
| "failed to parse qualified path in expression range " | |
| "pattern bound"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::RangePatternBoundQualPath> ( | |
| new AST::RangePatternBoundQualPath (std::move (path))); | |
| } | |
| default: | |
| add_error ( | |
| Error (range_lower->get_locus (), | |
| "token type %qs cannot be parsed as range pattern bound", | |
| range_lower->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses a pattern (will further disambiguate any pattern). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Pattern> | |
| Parser<ManagedTokenSource>::parse_pattern () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case TRUE_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::LiteralPattern> ( | |
| new AST::LiteralPattern ("true", AST::Literal::BOOL, t->get_locus ())); | |
| case FALSE_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::LiteralPattern> ( | |
| new AST::LiteralPattern ("false", AST::Literal::BOOL, t->get_locus ())); | |
| case CHAR_LITERAL: | |
| case BYTE_CHAR_LITERAL: | |
| case INT_LITERAL: | |
| case FLOAT_LITERAL: | |
| return parse_literal_or_range_pattern (); | |
| case STRING_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::LiteralPattern> ( | |
| new AST::LiteralPattern (t->get_str (), AST::Literal::STRING, | |
| t->get_locus ())); | |
| case BYTE_STRING_LITERAL: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::LiteralPattern> ( | |
| new AST::LiteralPattern (t->get_str (), AST::Literal::BYTE_STRING, | |
| t->get_locus ())); | |
| // raw string and raw byte string literals too if they are readded to | |
| // lexer | |
| case MINUS: | |
| if (lexer.peek_token (1)->get_id () == INT_LITERAL) | |
| { | |
| return parse_literal_or_range_pattern (); | |
| } | |
| else if (lexer.peek_token (1)->get_id () == FLOAT_LITERAL) | |
| { | |
| return parse_literal_or_range_pattern (); | |
| } | |
| else | |
| { | |
| Error error (t->get_locus (), "unexpected token %<-%> in pattern - " | |
| "did you forget an integer literal"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| case UNDERSCORE: | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::WildcardPattern> ( | |
| new AST::WildcardPattern (t->get_locus ())); | |
| case REF: | |
| case MUT: | |
| return parse_identifier_pattern (); | |
| case IDENTIFIER: | |
| /* if identifier with no scope resolution afterwards, identifier | |
| * pattern. if scope resolution afterwards, path pattern (or range | |
| * pattern or struct pattern or tuple struct pattern) or macro | |
| * invocation */ | |
| return parse_ident_leading_pattern (); | |
| case AMP: | |
| case LOGICAL_AND: | |
| // reference pattern | |
| return parse_reference_pattern (); | |
| case LEFT_PAREN: | |
| // tuple pattern or grouped pattern | |
| return parse_grouped_or_tuple_pattern (); | |
| case LEFT_SQUARE: | |
| // slice pattern | |
| return parse_slice_pattern (); | |
| case LEFT_ANGLE: { | |
| // qualified path in expression or qualified range pattern bound | |
| AST::QualifiedPathInExpression path | |
| = parse_qualified_path_in_expression (); | |
| if (lexer.peek_token ()->get_id () == DOT_DOT_EQ | |
| || lexer.peek_token ()->get_id () == ELLIPSIS) | |
| { | |
| // qualified range pattern bound, so parse rest of range pattern | |
| bool has_ellipsis_syntax | |
| = lexer.peek_token ()->get_id () == ELLIPSIS; | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::RangePatternBoundQualPath> lower_bound ( | |
| new AST::RangePatternBoundQualPath (std::move (path))); | |
| std::unique_ptr<AST::RangePatternBound> upper_bound | |
| = parse_range_pattern_bound (); | |
| return std::unique_ptr<AST::RangePattern> ( | |
| new AST::RangePattern (std::move (lower_bound), | |
| std::move (upper_bound), t->get_locus (), | |
| has_ellipsis_syntax)); | |
| } | |
| else | |
| { | |
| // just qualified path in expression | |
| return std::unique_ptr<AST::QualifiedPathInExpression> ( | |
| new AST::QualifiedPathInExpression (std::move (path))); | |
| } | |
| } | |
| case SUPER: | |
| case SELF: | |
| case SELF_ALIAS: | |
| case CRATE: | |
| case SCOPE_RESOLUTION: | |
| case DOLLAR_SIGN: { | |
| // path in expression or range pattern bound | |
| AST::PathInExpression path = parse_path_in_expression (); | |
| const_TokenPtr next = lexer.peek_token (); | |
| switch (next->get_id ()) | |
| { | |
| case DOT_DOT_EQ: | |
| case ELLIPSIS: { | |
| // qualified range pattern bound, so parse rest of range pattern | |
| bool has_ellipsis_syntax | |
| = lexer.peek_token ()->get_id () == ELLIPSIS; | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::RangePatternBoundPath> lower_bound ( | |
| new AST::RangePatternBoundPath (std::move (path))); | |
| std::unique_ptr<AST::RangePatternBound> upper_bound | |
| = parse_range_pattern_bound (); | |
| return std::unique_ptr<AST::RangePattern> (new AST::RangePattern ( | |
| std::move (lower_bound), std::move (upper_bound), | |
| Linemap::unknown_location (), has_ellipsis_syntax)); | |
| } | |
| case EXCLAM: | |
| return parse_macro_invocation_partial (std::move (path), | |
| AST::AttrVec ()); | |
| case LEFT_PAREN: { | |
| // tuple struct | |
| lexer.skip_token (); | |
| // check if empty tuple | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| { | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::TupleStructPattern> ( | |
| new AST::TupleStructPattern (std::move (path), nullptr)); | |
| } | |
| // parse items | |
| std::unique_ptr<AST::TupleStructItems> items | |
| = parse_tuple_struct_items (); | |
| if (items == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse tuple struct items"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TupleStructPattern> ( | |
| new AST::TupleStructPattern (std::move (path), | |
| std::move (items))); | |
| } | |
| case LEFT_CURLY: { | |
| // struct | |
| lexer.skip_token (); | |
| // parse elements (optional) | |
| AST::StructPatternElements elems = parse_struct_pattern_elems (); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StructPattern> ( | |
| new AST::StructPattern (std::move (path), t->get_locus (), | |
| std::move (elems))); | |
| } | |
| default: | |
| // assume path in expression | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| } | |
| default: | |
| add_error (Error (t->get_locus (), "unexpected token %qs in pattern", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses a single or double reference pattern. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ReferencePattern> | |
| Parser<ManagedTokenSource>::parse_reference_pattern () | |
| { | |
| // parse double or single ref | |
| bool is_double_ref = false; | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case AMP: | |
| // still false | |
| lexer.skip_token (); | |
| break; | |
| case LOGICAL_AND: | |
| is_double_ref = true; | |
| lexer.skip_token (); | |
| break; | |
| default: | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in reference pattern", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| // parse mut (if it exists) | |
| bool is_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| is_mut = true; | |
| lexer.skip_token (); | |
| } | |
| // parse pattern to get reference of (required) | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in reference pattern"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ReferencePattern> ( | |
| new AST::ReferencePattern (std::move (pattern), is_mut, is_double_ref, | |
| t->get_locus ())); | |
| } | |
| /* Parses a grouped pattern or tuple pattern. Prefers grouped over tuple if | |
| * only a single element with no commas. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Pattern> | |
| Parser<ManagedTokenSource>::parse_grouped_or_tuple_pattern () | |
| { | |
| Location paren_locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LEFT_PAREN); | |
| // detect '..' token (ranged with no lower range) | |
| if (lexer.peek_token ()->get_id () == DOT_DOT) | |
| { | |
| lexer.skip_token (); | |
| // parse new patterns while next token is a comma | |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // break if next token is ')' | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| { | |
| break; | |
| } | |
| // parse pattern, which is required | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern inside ranged tuple pattern"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| patterns.push_back (std::move (pattern)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // create ranged tuple pattern items with only upper items | |
| std::unique_ptr<AST::TuplePatternItemsRanged> items ( | |
| new AST::TuplePatternItemsRanged ( | |
| std::vector<std::unique_ptr<AST::Pattern>> (), std::move (patterns))); | |
| return std::unique_ptr<AST::TuplePattern> ( | |
| new AST::TuplePattern (std::move (items), paren_locus)); | |
| } | |
| // parse initial pattern (required) | |
| std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern (); | |
| if (initial_pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in grouped or tuple pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // branch on whether next token is a comma or not | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case RIGHT_PAREN: | |
| // grouped pattern | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::GroupedPattern> ( | |
| new AST::GroupedPattern (std::move (initial_pattern), paren_locus)); | |
| case COMMA: { | |
| // tuple pattern | |
| lexer.skip_token (); | |
| // create vector of patterns | |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; | |
| patterns.push_back (std::move (initial_pattern)); | |
| t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT) | |
| { | |
| // parse pattern (required) | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse pattern in tuple pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| patterns.push_back (std::move (pattern)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| t = lexer.peek_token (); | |
| if (t->get_id () == RIGHT_PAREN) | |
| { | |
| // non-ranged tuple pattern | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::TuplePatternItemsMultiple> items ( | |
| new AST::TuplePatternItemsMultiple (std::move (patterns))); | |
| return std::unique_ptr<AST::TuplePattern> ( | |
| new AST::TuplePattern (std::move (items), paren_locus)); | |
| } | |
| else if (t->get_id () == DOT_DOT) | |
| { | |
| // ranged tuple pattern | |
| lexer.skip_token (); | |
| // parse upper patterns | |
| std::vector<std::unique_ptr<AST::Pattern>> upper_patterns; | |
| t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // break if end | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| break; | |
| // parse pattern (required) | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in tuple pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| upper_patterns.push_back (std::move (pattern)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| std::unique_ptr<AST::TuplePatternItemsRanged> items ( | |
| new AST::TuplePatternItemsRanged (std::move (patterns), | |
| std::move (upper_patterns))); | |
| return std::unique_ptr<AST::TuplePattern> ( | |
| new AST::TuplePattern (std::move (items), paren_locus)); | |
| } | |
| else | |
| { | |
| // some kind of error | |
| Error error (t->get_locus (), | |
| "failed to parse tuple pattern (probably) or maybe " | |
| "grouped pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| } | |
| default: | |
| // error | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs in grouped or tuple pattern " | |
| "after first pattern", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| /* Parses a slice pattern that can match arrays or slices. Parses the square | |
| * brackets too. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::SlicePattern> | |
| Parser<ManagedTokenSource>::parse_slice_pattern () | |
| { | |
| Location square_locus = lexer.peek_token ()->get_locus (); | |
| skip_token (LEFT_SQUARE); | |
| // parse initial pattern (required) | |
| std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern (); | |
| if (initial_pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse initial pattern in slice pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| std::vector<std::unique_ptr<AST::Pattern>> patterns; | |
| patterns.push_back (std::move (initial_pattern)); | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // break if end bracket | |
| if (lexer.peek_token ()->get_id () == RIGHT_SQUARE) | |
| break; | |
| // parse pattern (required) | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in slice pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| patterns.push_back (std::move (pattern)); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_SQUARE)) | |
| { | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::SlicePattern> ( | |
| new AST::SlicePattern (std::move (patterns), square_locus)); | |
| } | |
| /* Parses an identifier pattern (pattern that binds a value matched to a | |
| * variable). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::IdentifierPattern> | |
| Parser<ManagedTokenSource>::parse_identifier_pattern () | |
| { | |
| Location locus = lexer.peek_token ()->get_locus (); | |
| bool has_ref = false; | |
| if (lexer.peek_token ()->get_id () == REF) | |
| { | |
| has_ref = true; | |
| lexer.skip_token (); | |
| // DEBUG | |
| rust_debug ("parsed ref in identifier pattern"); | |
| } | |
| bool has_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| has_mut = true; | |
| lexer.skip_token (); | |
| } | |
| // parse identifier (required) | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| Identifier ident = ident_tok->get_str (); | |
| // DEBUG | |
| rust_debug ("parsed identifier in identifier pattern"); | |
| // parse optional pattern binding thing | |
| std::unique_ptr<AST::Pattern> bind_pattern = nullptr; | |
| if (lexer.peek_token ()->get_id () == PATTERN_BIND) | |
| { | |
| lexer.skip_token (); | |
| // parse required pattern to bind | |
| bind_pattern = parse_pattern (); | |
| if (bind_pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern to bind in identifier pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| } | |
| // DEBUG | |
| rust_debug ("about to return identifier pattern"); | |
| return std::unique_ptr<AST::IdentifierPattern> ( | |
| new AST::IdentifierPattern (std::move (ident), locus, has_ref, has_mut, | |
| std::move (bind_pattern))); | |
| } | |
| /* Parses a pattern that opens with an identifier. This includes identifier | |
| * patterns, path patterns (and derivatives such as struct patterns, tuple | |
| * struct patterns, and macro invocations), and ranges. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Pattern> | |
| Parser<ManagedTokenSource>::parse_ident_leading_pattern () | |
| { | |
| // ensure first token is actually identifier | |
| const_TokenPtr initial_tok = lexer.peek_token (); | |
| if (initial_tok->get_id () != IDENTIFIER) | |
| { | |
| return nullptr; | |
| } | |
| // save initial identifier as it may be useful (but don't skip) | |
| std::string initial_ident = initial_tok->get_str (); | |
| // parse next tokens as a PathInExpression | |
| AST::PathInExpression path = parse_path_in_expression (); | |
| // branch on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: | |
| return parse_macro_invocation_partial (std::move (path), AST::AttrVec ()); | |
| case LEFT_PAREN: { | |
| // tuple struct | |
| lexer.skip_token (); | |
| // DEBUG | |
| rust_debug ("parsing tuple struct pattern"); | |
| // check if empty tuple | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| { | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::TupleStructPattern> ( | |
| new AST::TupleStructPattern (std::move (path), nullptr)); | |
| } | |
| // parse items | |
| std::unique_ptr<AST::TupleStructItems> items | |
| = parse_tuple_struct_items (); | |
| if (items == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse tuple struct items"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed tuple struct items"); | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed tuple struct pattern"); | |
| return std::unique_ptr<AST::TupleStructPattern> ( | |
| new AST::TupleStructPattern (std::move (path), std::move (items))); | |
| } | |
| case LEFT_CURLY: { | |
| // struct | |
| lexer.skip_token (); | |
| // parse elements (optional) | |
| AST::StructPatternElements elems = parse_struct_pattern_elems (); | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| return nullptr; | |
| } | |
| // DEBUG | |
| rust_debug ("successfully parsed struct pattern"); | |
| return std::unique_ptr<AST::StructPattern> ( | |
| new AST::StructPattern (std::move (path), initial_tok->get_locus (), | |
| std::move (elems))); | |
| } | |
| case DOT_DOT_EQ: | |
| case ELLIPSIS: { | |
| // range | |
| bool has_ellipsis_syntax = lexer.peek_token ()->get_id () == ELLIPSIS; | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::RangePatternBoundPath> lower_bound ( | |
| new AST::RangePatternBoundPath (std::move (path))); | |
| std::unique_ptr<AST::RangePatternBound> upper_bound | |
| = parse_range_pattern_bound (); | |
| return std::unique_ptr<AST::RangePattern> (new AST::RangePattern ( | |
| std::move (lower_bound), std::move (upper_bound), | |
| Linemap::unknown_location (), has_ellipsis_syntax)); | |
| } | |
| case PATTERN_BIND: { | |
| // only allow on single-segment paths | |
| if (path.is_single_segment ()) | |
| { | |
| // identifier with pattern bind | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::Pattern> bind_pattern = parse_pattern (); | |
| if (bind_pattern == nullptr) | |
| { | |
| Error error ( | |
| t->get_locus (), | |
| "failed to parse pattern to bind to identifier pattern"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::IdentifierPattern> ( | |
| new AST::IdentifierPattern (std::move (initial_ident), | |
| initial_tok->get_locus (), false, | |
| false, std::move (bind_pattern))); | |
| } | |
| Error error ( | |
| t->get_locus (), | |
| "failed to parse pattern bind to a path, not an identifier"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| default: | |
| // assume identifier if single segment | |
| if (path.is_single_segment ()) | |
| { | |
| return std::unique_ptr<AST::IdentifierPattern> ( | |
| new AST::IdentifierPattern (std::move (initial_ident), | |
| initial_tok->get_locus ())); | |
| } | |
| // return path otherwise | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| } | |
| // Parses tuple struct items if they exist. Does not parse parentheses. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TupleStructItems> | |
| Parser<ManagedTokenSource>::parse_tuple_struct_items () | |
| { | |
| std::vector<std::unique_ptr<AST::Pattern>> lower_patterns; | |
| // DEBUG | |
| rust_debug ("started parsing tuple struct items"); | |
| // check for '..' at front | |
| if (lexer.peek_token ()->get_id () == DOT_DOT) | |
| { | |
| // only parse upper patterns | |
| lexer.skip_token (); | |
| // DEBUG | |
| rust_debug ("'..' at front in tuple struct items detected"); | |
| std::vector<std::unique_ptr<AST::Pattern>> upper_patterns; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // break if right paren | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| break; | |
| // parse pattern, which is now required | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in tuple struct items"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| upper_patterns.push_back (std::move (pattern)); | |
| t = lexer.peek_token (); | |
| } | |
| // DEBUG | |
| rust_debug ( | |
| "finished parsing tuple struct items ranged (upper/none only)"); | |
| return std::unique_ptr<AST::TupleStructItemsRange> ( | |
| new AST::TupleStructItemsRange (std::move (lower_patterns), | |
| std::move (upper_patterns))); | |
| } | |
| // has at least some lower patterns | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT) | |
| { | |
| // DEBUG | |
| rust_debug ("about to parse pattern in tuple struct items"); | |
| // parse pattern, which is required | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse pattern in tuple struct items"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| lower_patterns.push_back (std::move (pattern)); | |
| // DEBUG | |
| rust_debug ("successfully parsed pattern in tuple struct items"); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| // DEBUG | |
| rust_debug ("broke out of parsing patterns in tuple struct " | |
| "items as no comma"); | |
| break; | |
| } | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // branch on next token | |
| t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case RIGHT_PAREN: | |
| return std::unique_ptr<AST::TupleStructItemsNoRange> ( | |
| new AST::TupleStructItemsNoRange (std::move (lower_patterns))); | |
| case DOT_DOT: { | |
| // has an upper range that must be parsed separately | |
| lexer.skip_token (); | |
| std::vector<std::unique_ptr<AST::Pattern>> upper_patterns; | |
| t = lexer.peek_token (); | |
| while (t->get_id () == COMMA) | |
| { | |
| lexer.skip_token (); | |
| // break if next token is right paren | |
| if (lexer.peek_token ()->get_id () == RIGHT_PAREN) | |
| break; | |
| // parse pattern, which is required | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse pattern in tuple struct items"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| upper_patterns.push_back (std::move (pattern)); | |
| t = lexer.peek_token (); | |
| } | |
| return std::unique_ptr<AST::TupleStructItemsRange> ( | |
| new AST::TupleStructItemsRange (std::move (lower_patterns), | |
| std::move (upper_patterns))); | |
| } | |
| default: | |
| // error | |
| add_error (Error (t->get_locus (), | |
| "unexpected token %qs in tuple struct items", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses struct pattern elements if they exist. | |
| template <typename ManagedTokenSource> | |
| AST::StructPatternElements | |
| Parser<ManagedTokenSource>::parse_struct_pattern_elems () | |
| { | |
| std::vector<std::unique_ptr<AST::StructPatternField>> fields; | |
| AST::AttrVec etc_attrs; | |
| bool has_etc = false; | |
| // try parsing struct pattern fields | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_CURLY) | |
| { | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parse etc (must be last in struct pattern, so breaks) | |
| if (lexer.peek_token ()->get_id () == DOT_DOT) | |
| { | |
| lexer.skip_token (); | |
| etc_attrs = std::move (outer_attrs); | |
| has_etc = true; | |
| break; | |
| } | |
| std::unique_ptr<AST::StructPatternField> field | |
| = parse_struct_pattern_field_partial (std::move (outer_attrs)); | |
| if (field == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse struct pattern field"); | |
| add_error (std::move (error)); | |
| // skip after somewhere? | |
| return AST::StructPatternElements::create_empty (); | |
| } | |
| fields.push_back (std::move (field)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (has_etc) | |
| return AST::StructPatternElements (std::move (fields), | |
| std::move (etc_attrs)); | |
| else | |
| return AST::StructPatternElements (std::move (fields)); | |
| } | |
| /* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or | |
| * identifier). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::StructPatternField> | |
| Parser<ManagedTokenSource>::parse_struct_pattern_field () | |
| { | |
| // parse outer attributes (if they exist) | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| return parse_struct_pattern_field_partial (std::move (outer_attrs)); | |
| } | |
| /* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or | |
| * identifier), with outer attributes passed in. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::StructPatternField> | |
| Parser<ManagedTokenSource>::parse_struct_pattern_field_partial ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| // branch based on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case INT_LITERAL: { | |
| // tuple index | |
| std::string index_str = t->get_str (); | |
| int index = atoi (index_str.c_str ()); | |
| if (!skip_token (COLON)) | |
| { | |
| return nullptr; | |
| } | |
| // parse required pattern | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error ( | |
| t->get_locus (), | |
| "failed to parse pattern in tuple index struct pattern field"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StructPatternFieldTuplePat> ( | |
| new AST::StructPatternFieldTuplePat (index, std::move (pattern), | |
| std::move (outer_attrs), | |
| t->get_locus ())); | |
| } | |
| case IDENTIFIER: | |
| // identifier-pattern OR only identifier | |
| // branch on next token | |
| switch (lexer.peek_token (1)->get_id ()) | |
| { | |
| case COLON: { | |
| // identifier-pattern | |
| Identifier ident = t->get_str (); | |
| lexer.skip_token (); | |
| skip_token (COLON); | |
| // parse required pattern | |
| std::unique_ptr<AST::Pattern> pattern = parse_pattern (); | |
| if (pattern == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse pattern in struct pattern field"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StructPatternFieldIdentPat> ( | |
| new AST::StructPatternFieldIdentPat (std::move (ident), | |
| std::move (pattern), | |
| std::move (outer_attrs), | |
| t->get_locus ())); | |
| } | |
| case COMMA: | |
| case RIGHT_CURLY: { | |
| // identifier only | |
| Identifier ident = t->get_str (); | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::StructPatternFieldIdent> ( | |
| new AST::StructPatternFieldIdent (std::move (ident), false, false, | |
| std::move (outer_attrs), | |
| t->get_locus ())); | |
| } | |
| default: | |
| // error | |
| add_error (Error (t->get_locus (), | |
| "unrecognised token %qs in struct pattern field", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| case REF: | |
| case MUT: { | |
| // only identifier | |
| bool has_ref = false; | |
| if (t->get_id () == REF) | |
| { | |
| has_ref = true; | |
| lexer.skip_token (); | |
| } | |
| bool has_mut = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| has_mut = true; | |
| lexer.skip_token (); | |
| } | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| { | |
| return nullptr; | |
| } | |
| Identifier ident = ident_tok->get_str (); | |
| return std::unique_ptr<AST::StructPatternFieldIdent> ( | |
| new AST::StructPatternFieldIdent (std::move (ident), has_ref, has_mut, | |
| std::move (outer_attrs), | |
| t->get_locus ())); | |
| } | |
| default: | |
| // not necessarily an error | |
| return nullptr; | |
| } | |
| } | |
| template <typename ManagedTokenSource> | |
| ExprOrStmt | |
| Parser<ManagedTokenSource>::parse_stmt_or_expr_with_block ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| auto expr = parse_expr_with_block (std::move (outer_attrs)); | |
| if (expr == nullptr) | |
| return ExprOrStmt::create_error (); | |
| auto tok = lexer.peek_token (); | |
| // tail expr in a block expr | |
| if (tok->get_id () == RIGHT_CURLY) | |
| return ExprOrStmt (std::move (expr)); | |
| // internal block expr must either have semicolons followed, or evaluate to | |
| // () | |
| auto locus = expr->get_locus (); | |
| std::unique_ptr<AST::ExprStmtWithBlock> stmt ( | |
| new AST::ExprStmtWithBlock (std::move (expr), locus, | |
| tok->get_id () == SEMICOLON)); | |
| if (tok->get_id () == SEMICOLON) | |
| lexer.skip_token (); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| /* Parses a statement or expression (depending on whether a trailing semicolon | |
| * exists). Useful for block expressions where it cannot be determined through | |
| * lookahead whether it is a statement or expression to be parsed. */ | |
| template <typename ManagedTokenSource> | |
| ExprOrStmt | |
| Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block () | |
| { | |
| // quick exit for empty statement | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (t->get_id () == SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::EmptyStmt> stmt ( | |
| new AST::EmptyStmt (t->get_locus ())); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // parse outer attributes | |
| AST::AttrVec outer_attrs = parse_outer_attributes (); | |
| // parsing this will be annoying because of the many different possibilities | |
| /* best may be just to copy paste in parse_item switch, and failing that try | |
| * to parse outer attributes, and then pass them in to either a let | |
| * statement or (fallback) expression statement. */ | |
| // FIXME: think of a way to do this without such a large switch? | |
| /* FIXME: for expressions at least, the only way that they can really be | |
| * parsed properly in this way is if they don't support operators on them. | |
| * They must be pratt-parsed otherwise. As such due to composability, only | |
| * explicit statements will have special cases here. This should roughly | |
| * correspond to "expr-with-block", but this warning is here in case it | |
| * isn't the case. */ | |
| t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LET: { | |
| // let statement | |
| std::unique_ptr<AST::LetStmt> stmt ( | |
| parse_let_stmt (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| case PUB: | |
| case MOD: | |
| case EXTERN_TOK: | |
| case USE: | |
| case FN_TOK: | |
| case TYPE: | |
| case STRUCT_TOK: | |
| case ENUM_TOK: | |
| case CONST: | |
| case STATIC_TOK: | |
| case TRAIT: | |
| case IMPL: { | |
| std::unique_ptr<AST::VisItem> item ( | |
| parse_vis_item (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (item)); | |
| } | |
| /* TODO: implement union keyword but not really because of | |
| * context-dependence crappy hack way to parse a union written below to | |
| * separate it from the good code. */ | |
| // case UNION: | |
| case UNSAFE: { // maybe - unsafe traits are a thing | |
| /* if any of these (should be all possible VisItem prefixes), parse a | |
| * VisItem - can't parse item because would require reparsing outer | |
| * attributes */ | |
| const_TokenPtr t2 = lexer.peek_token (1); | |
| switch (t2->get_id ()) | |
| { | |
| case LEFT_CURLY: { | |
| // unsafe block | |
| return parse_stmt_or_expr_with_block (std::move (outer_attrs)); | |
| } | |
| case TRAIT: { | |
| // unsafe trait | |
| std::unique_ptr<AST::VisItem> item ( | |
| parse_vis_item (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (item)); | |
| } | |
| case EXTERN_TOK: | |
| case FN_TOK: { | |
| // unsafe function | |
| std::unique_ptr<AST::VisItem> item ( | |
| parse_vis_item (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (item)); | |
| } | |
| case IMPL: { | |
| // unsafe trait impl | |
| std::unique_ptr<AST::VisItem> item ( | |
| parse_vis_item (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (item)); | |
| } | |
| default: | |
| add_error (Error (t2->get_locus (), | |
| "unrecognised token %qs after parsing unsafe - " | |
| "expected beginning of expression or statement", | |
| t->get_token_description ())); | |
| // skip somewhere? | |
| return ExprOrStmt::create_error (); | |
| } | |
| } | |
| case SUPER: | |
| case SELF: | |
| case CRATE: | |
| case DOLLAR_SIGN: { | |
| /* path-based thing so struct/enum or path or macro invocation of a | |
| * kind. however, the expressions are composable (i think) */ | |
| std::unique_ptr<AST::ExprWithoutBlock> expr | |
| = parse_expr_without_block (); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // must be expression statement | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), | |
| t->get_locus ())); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // return expression | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| /* FIXME: this is either a macro invocation or macro invocation semi. | |
| * start parsing to determine which one it is. */ | |
| // FIXME: or this is another path-based thing - struct/enum or path | |
| // itself return parse_path_based_stmt_or_expr(std::move(outer_attrs)); | |
| // FIXME: old code there | |
| case LOOP: | |
| case WHILE: | |
| case FOR: | |
| case IF: | |
| case MATCH_TOK: | |
| case LEFT_CURLY: | |
| case ASYNC: { | |
| return parse_stmt_or_expr_with_block (std::move (outer_attrs)); | |
| } | |
| case LIFETIME: { | |
| /* FIXME: are there any expressions without blocks that can have | |
| * lifetime as their first token? Or is loop expr the only one? */ | |
| // safe side for now: | |
| const_TokenPtr t2 = lexer.peek_token (2); | |
| if (lexer.peek_token (1)->get_id () == COLON | |
| && (t2->get_id () == LOOP || t2->get_id () == WHILE | |
| || t2->get_id () == FOR)) | |
| { | |
| return parse_stmt_or_expr_with_block (std::move (outer_attrs)); | |
| } | |
| else | |
| { | |
| // should be expr without block | |
| std::unique_ptr<AST::ExprWithoutBlock> expr | |
| = parse_expr_without_block (std::move (outer_attrs)); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // must be expression statement | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), | |
| t->get_locus ())); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // return expression | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| } | |
| // crappy hack to do union "keyword" | |
| case IDENTIFIER: | |
| if (t->get_str () == "union" | |
| && lexer.peek_token (1)->get_id () == IDENTIFIER) | |
| { | |
| std::unique_ptr<AST::VisItem> item ( | |
| parse_vis_item (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (item)); | |
| // or should this go straight to parsing union? | |
| } | |
| else if (t->get_str () == "macro_rules") | |
| { | |
| // macro_rules! macro item | |
| std::unique_ptr<AST::MacroItem> item ( | |
| parse_macro_item (std::move (outer_attrs))); | |
| return ExprOrStmt (std::move (item)); | |
| } | |
| else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION | |
| || lexer.peek_token (1)->get_id () == EXCLAM | |
| || lexer.peek_token (1)->get_id () == LEFT_CURLY) | |
| { | |
| /* path (probably) or macro invocation or struct or enum, so | |
| * probably a macro invocation semi decide how to parse - probably | |
| * parse path and then get macro from it */ | |
| // FIXME: old code was good until composability was required | |
| // return parse_path_based_stmt_or_expr(std::move(outer_attrs)); | |
| std::unique_ptr<AST::ExprWithoutBlock> expr | |
| = parse_expr_without_block (std::move (outer_attrs)); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // must be expression statement | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), | |
| t->get_locus ())); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // return expression | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| gcc_fallthrough (); | |
| // TODO: find out how to disable gcc "implicit fallthrough" warning | |
| default: { | |
| /* expression statement (without block) or expression itself - parse | |
| * expression then make it statement if semi afterwards */ | |
| std::unique_ptr<AST::ExprWithoutBlock> expr | |
| = parse_expr_without_block (std::move (outer_attrs)); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // must be expression statement | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), | |
| t->get_locus ())); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // return expression | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| } | |
| } | |
| /* Parses a statement or expression beginning with a path (i.e. macro, | |
| * struct/enum, or path expr) */ | |
| template <typename ManagedTokenSource> | |
| ExprOrStmt | |
| Parser<ManagedTokenSource>::parse_path_based_stmt_or_expr ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| // attempt to parse path | |
| Location stmt_or_expr_loc = lexer.peek_token ()->get_locus (); | |
| AST::PathInExpression path = parse_path_in_expression (); | |
| // branch on next token | |
| const_TokenPtr t2 = lexer.peek_token (); | |
| switch (t2->get_id ()) | |
| { | |
| case EXCLAM: { | |
| /* macro invocation or macro invocation semi - depends on whether | |
| * there is a final ';' */ | |
| // convert path in expr to simple path (as that is used in macros) | |
| AST::SimplePath macro_path = path.as_simple_path (); | |
| if (macro_path.is_empty ()) | |
| { | |
| Error error (t2->get_locus (), | |
| "failed to convert parsed path to simple " | |
| "path (for macro invocation or semi)"); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| // skip exclamation mark | |
| lexer.skip_token (); | |
| const_TokenPtr t3 = lexer.peek_token (); | |
| Location tok_tree_loc = t3->get_locus (); | |
| AST::DelimType type = AST::PARENS; | |
| switch (t3->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| type = AST::PARENS; | |
| break; | |
| case LEFT_SQUARE: | |
| type = AST::SQUARE; | |
| break; | |
| case LEFT_CURLY: | |
| type = AST::CURLY; | |
| break; | |
| default: | |
| add_error ( | |
| Error (t3->get_locus (), | |
| "unrecognised token %qs in macro invocation - (opening) " | |
| "delimiter expected", | |
| t3->get_token_description ())); | |
| return ExprOrStmt::create_error (); | |
| } | |
| lexer.skip_token (); | |
| // parse actual token trees | |
| std::vector<std::unique_ptr<AST::TokenTree>> token_trees; | |
| auto delim_open | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); | |
| token_trees.push_back (std::move (delim_open)); | |
| t3 = lexer.peek_token (); | |
| // parse token trees until the initial delimiter token is found again | |
| while (!token_id_matches_delims (t3->get_id (), type)) | |
| { | |
| std::unique_ptr<AST::TokenTree> tree = parse_token_tree (); | |
| if (tree == nullptr) | |
| { | |
| Error error (t3->get_locus (), | |
| "failed to parse token tree for macro " | |
| "invocation (or semi) - " | |
| "found %qs", | |
| t3->get_token_description ()); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| token_trees.push_back (std::move (tree)); | |
| t3 = lexer.peek_token (); | |
| } | |
| auto delim_close | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); | |
| token_trees.push_back (std::move (delim_close)); | |
| // parse end delimiters | |
| t3 = lexer.peek_token (); | |
| if (token_id_matches_delims (t3->get_id (), type)) | |
| { | |
| // tokens match opening delimiter, so skip. | |
| lexer.skip_token (); | |
| /* with curly bracketed macros, assume it is a macro invocation | |
| * unless a semicolon is explicitly put at the end. this is not | |
| * necessarily true (i.e. context-dependence) and so may have to | |
| * be fixed up via HACKs in semantic analysis (by checking whether | |
| * it is the last elem in the vector). */ | |
| AST::DelimTokenTree delim_tok_tree (type, std::move (token_trees), | |
| tok_tree_loc); | |
| AST::MacroInvocData invoc_data (std::move (macro_path), | |
| std::move (delim_tok_tree)); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::MacroInvocation> stmt ( | |
| new AST::MacroInvocation (std::move (invoc_data), | |
| std::move (outer_attrs), | |
| stmt_or_expr_loc, true)); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // otherwise, create macro invocation | |
| std::unique_ptr<AST::MacroInvocation> expr ( | |
| new AST::MacroInvocation (std::move (invoc_data), | |
| std::move (outer_attrs), | |
| stmt_or_expr_loc, false)); | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| else | |
| { | |
| // tokens don't match opening delimiters, so produce error | |
| Error error ( | |
| t2->get_locus (), | |
| "unexpected token %qs - expecting closing delimiter %qs (for a " | |
| "macro invocation)", | |
| t2->get_token_description (), | |
| (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}"))); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| } | |
| case LEFT_CURLY: { | |
| /* definitely not a block: | |
| * path '{' ident ',' | |
| * path '{' ident ':' [anything] ',' | |
| * path '{' ident ':' [not a type] | |
| * otherwise, assume block expr and thus path */ | |
| bool not_a_block = lexer.peek_token (1)->get_id () == IDENTIFIER | |
| && (lexer.peek_token (2)->get_id () == COMMA | |
| || (lexer.peek_token (2)->get_id () == COLON | |
| && (lexer.peek_token (4)->get_id () == COMMA | |
| || !can_tok_start_type ( | |
| lexer.peek_token (3)->get_id ())))); | |
| std::unique_ptr<AST::ExprWithoutBlock> expr = nullptr; | |
| if (not_a_block) | |
| { | |
| /* assume struct expr struct (as struct-enum disambiguation | |
| * requires name lookup) again, make statement if final ';' */ | |
| expr = parse_struct_expr_struct_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| if (expr == nullptr) | |
| { | |
| Error error (t2->get_locus (), | |
| "failed to parse struct expr struct"); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| } | |
| else | |
| { | |
| // assume path - make statement if final ';' | |
| // lexer.skip_token(); | |
| // HACK: add outer attrs to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| expr = std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| // determine if statement if ends with semicolon | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // statement | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), | |
| stmt_or_expr_loc)); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // otherwise, expression | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| case LEFT_PAREN: { | |
| /* assume struct expr tuple (as struct-enum disambiguation requires | |
| * name lookup) again, make statement if final ';' */ | |
| std::unique_ptr<AST::CallExpr> struct_expr | |
| = parse_struct_expr_tuple_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| if (struct_expr == nullptr) | |
| { | |
| Error error (t2->get_locus (), "failed to parse struct expr tuple"); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| // determine if statement if ends with semicolon | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| // statement | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (struct_expr), | |
| stmt_or_expr_loc)); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // otherwise, expression | |
| return ExprOrStmt (std::move (struct_expr)); | |
| } | |
| default: { | |
| // assume path - make statement if final ';' | |
| // lexer.skip_token(); | |
| // HACK: replace outer attributes in path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| std::unique_ptr<AST::PathInExpression> expr ( | |
| new AST::PathInExpression (std::move (path))); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::ExprStmtWithoutBlock> stmt ( | |
| new AST::ExprStmtWithoutBlock (std::move (expr), | |
| stmt_or_expr_loc)); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| } | |
| } | |
| // Parses a struct expression field. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::StructExprField> | |
| Parser<ManagedTokenSource>::parse_struct_expr_field () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| if (lexer.peek_token (1)->get_id () == COLON) | |
| { | |
| // struct expr field with identifier and expr | |
| Identifier ident = t->get_str (); | |
| lexer.skip_token (1); | |
| // parse expression (required) | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse struct expression field with " | |
| "identifier and expression"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StructExprFieldIdentifierValue> ( | |
| new AST::StructExprFieldIdentifierValue (std::move (ident), | |
| std::move (expr), | |
| t->get_locus ())); | |
| } | |
| else | |
| { | |
| // struct expr field with identifier only | |
| Identifier ident = t->get_str (); | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::StructExprFieldIdentifier> ( | |
| new AST::StructExprFieldIdentifier (std::move (ident), | |
| t->get_locus ())); | |
| } | |
| case INT_LITERAL: { | |
| // parse tuple index field | |
| int index = atoi (t->get_str ().c_str ()); | |
| lexer.skip_token (); | |
| if (!skip_token (COLON)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse field expression (required) | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse expr in struct (or enum) expr " | |
| "field with tuple index"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::StructExprFieldIndexValue> ( | |
| new AST::StructExprFieldIndexValue (index, std::move (expr), | |
| t->get_locus ())); | |
| } | |
| case DOT_DOT: | |
| /* this is a struct base and can't be parsed here, so just return | |
| * nothing without erroring */ | |
| return nullptr; | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unrecognised token %qs as first token of struct expr field - " | |
| "expected identifier or integer literal", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| // Parses a macro invocation or macro invocation semi. | |
| template <typename ManagedTokenSource> | |
| ExprOrStmt | |
| Parser<ManagedTokenSource>::parse_macro_invocation_maybe_semi ( | |
| AST::AttrVec outer_attrs) | |
| { | |
| Location macro_locus = lexer.peek_token ()->get_locus (); | |
| AST::SimplePath macro_path = parse_simple_path (); | |
| if (macro_path.is_empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse simple path in macro invocation or semi"); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| if (!skip_token (EXCLAM)) | |
| { | |
| return ExprOrStmt::create_error (); | |
| } | |
| const_TokenPtr t3 = lexer.peek_token (); | |
| Location tok_tree_loc = t3->get_locus (); | |
| AST::DelimType type = AST::PARENS; | |
| switch (t3->get_id ()) | |
| { | |
| case LEFT_PAREN: | |
| type = AST::PARENS; | |
| break; | |
| case LEFT_SQUARE: | |
| type = AST::SQUARE; | |
| break; | |
| case LEFT_CURLY: | |
| type = AST::CURLY; | |
| break; | |
| default: | |
| add_error ( | |
| Error (t3->get_locus (), | |
| "unrecognised token %qs in macro invocation - (opening) " | |
| "delimiter expected", | |
| t3->get_token_description ())); | |
| return ExprOrStmt::create_error (); | |
| } | |
| lexer.skip_token (); | |
| // parse actual token trees | |
| std::vector<std::unique_ptr<AST::TokenTree>> token_trees; | |
| auto delim_open | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); | |
| token_trees.push_back (std::move (delim_open)); | |
| t3 = lexer.peek_token (); | |
| // parse token trees until the initial delimiter token is found again | |
| while (!token_id_matches_delims (t3->get_id (), type)) | |
| { | |
| std::unique_ptr<AST::TokenTree> tree = parse_token_tree (); | |
| if (tree == nullptr) | |
| { | |
| Error error (t3->get_locus (), | |
| "failed to parse token tree for macro invocation (or " | |
| "semi) - found %qs", | |
| t3->get_token_description ()); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| token_trees.push_back (std::move (tree)); | |
| t3 = lexer.peek_token (); | |
| } | |
| auto delim_close | |
| = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); | |
| token_trees.push_back (std::move (delim_close)); | |
| // parse end delimiters | |
| t3 = lexer.peek_token (); | |
| if (token_id_matches_delims (t3->get_id (), type)) | |
| { | |
| // tokens match opening delimiter, so skip. | |
| lexer.skip_token (); | |
| /* with curly bracketed macros, assume it is a macro invocation unless | |
| * a semicolon is explicitly put at the end. this is not necessarily | |
| * true (i.e. context-dependence) and so may have to be fixed up via | |
| * HACKs in semantic analysis (by checking whether it is the last elem | |
| * in the vector). */ | |
| AST::DelimTokenTree delim_tok_tree (type, std::move (token_trees), | |
| tok_tree_loc); | |
| AST::MacroInvocData invoc_data (std::move (macro_path), | |
| std::move (delim_tok_tree)); | |
| if (lexer.peek_token ()->get_id () == SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| std::unique_ptr<AST::MacroInvocation> stmt ( | |
| new AST::MacroInvocation (std::move (invoc_data), | |
| std::move (outer_attrs), macro_locus, | |
| true)); | |
| return ExprOrStmt (std::move (stmt)); | |
| } | |
| // otherwise, create macro invocation | |
| std::unique_ptr<AST::MacroInvocation> expr ( | |
| new AST::MacroInvocation (std::move (invoc_data), | |
| std::move (outer_attrs), macro_locus)); | |
| return ExprOrStmt (std::move (expr)); | |
| } | |
| else | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| // tokens don't match opening delimiters, so produce error | |
| Error error ( | |
| t->get_locus (), | |
| "unexpected token %qs - expecting closing delimiter %qs (for a " | |
| "macro invocation)", | |
| t->get_token_description (), | |
| (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}"))); | |
| add_error (std::move (error)); | |
| return ExprOrStmt::create_error (); | |
| } | |
| } | |
| // "Unexpected token" panic mode - flags gcc error at unexpected token | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::unexpected_token (const_TokenPtr t) | |
| { | |
| Error error (t->get_locus (), "unexpected token %qs\n", | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| } | |
| /* Crappy "error recovery" performed after error by skipping tokens until a | |
| * semi-colon is found */ | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::skip_after_semicolon () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != END_OF_FILE && t->get_id () != SEMICOLON) | |
| { | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (t->get_id () == SEMICOLON) | |
| lexer.skip_token (); | |
| } | |
| /* Checks if current token has inputted id - skips it and returns true if so, | |
| * diagnoses an error and returns false otherwise. */ | |
| template <typename ManagedTokenSource> | |
| bool | |
| Parser<ManagedTokenSource>::skip_token (TokenId token_id) | |
| { | |
| return expect_token (token_id) != const_TokenPtr (); | |
| } | |
| /* Checks if current token has inputted id - skips it and returns true if so, | |
| * returns false otherwise without diagnosing an error */ | |
| template <typename ManagedTokenSource> | |
| bool | |
| Parser<ManagedTokenSource>::maybe_skip_token (TokenId token_id) | |
| { | |
| if (lexer.peek_token ()->get_id () != token_id) | |
| return false; | |
| else | |
| return skip_token (token_id); | |
| } | |
| /* Checks the current token - if id is same as expected, skips and returns it, | |
| * otherwise diagnoses error and returns null. */ | |
| template <typename ManagedTokenSource> | |
| const_TokenPtr | |
| Parser<ManagedTokenSource>::expect_token (TokenId token_id) | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| if (t->get_id () == token_id) | |
| { | |
| lexer.skip_token (); | |
| return t; | |
| } | |
| else | |
| { | |
| Error error (t->get_locus (), "expecting %qs but %qs found", | |
| get_token_description (token_id), | |
| t->get_token_description ()); | |
| add_error (std::move (error)); | |
| return const_TokenPtr (); | |
| } | |
| } | |
| // Skips all tokens until EOF or }. Don't use. | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::skip_after_end () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != END_OF_FILE && t->get_id () != RIGHT_CURLY) | |
| { | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (t->get_id () == RIGHT_CURLY) | |
| { | |
| lexer.skip_token (); | |
| } | |
| } | |
| /* A slightly more aware error-handler that skips all tokens until it reaches | |
| * the end of the block scope (i.e. when left curly brackets = right curly | |
| * brackets). Note: assumes currently in the middle of a block. Use | |
| * skip_after_next_block to skip based on the assumption that the block | |
| * has not been entered yet. */ | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::skip_after_end_block () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| int curly_count = 1; | |
| while (curly_count > 0 && t->get_id () != END_OF_FILE) | |
| { | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_CURLY: | |
| curly_count++; | |
| break; | |
| case RIGHT_CURLY: | |
| curly_count--; | |
| break; | |
| default: | |
| break; | |
| } | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| } | |
| /* Skips tokens until the end of the next block. i.e. assumes that the block | |
| * has not been entered yet. */ | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::skip_after_next_block () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| // initial loop - skip until EOF if no left curlies encountered | |
| while (t->get_id () != END_OF_FILE && t->get_id () != LEFT_CURLY) | |
| { | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // if next token is left, skip it and then skip after the block ends | |
| if (t->get_id () == LEFT_CURLY) | |
| { | |
| lexer.skip_token (); | |
| skip_after_end_block (); | |
| } | |
| // otherwise, do nothing as EOF | |
| } | |
| /* Skips all tokens until ] (the end of an attribute) - does not skip the ] | |
| * (as designed for attribute body use) */ | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::skip_after_end_attribute () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_SQUARE) | |
| { | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // Don't skip the RIGHT_SQUARE token | |
| } | |
| /* Pratt parser impl of parse_expr. FIXME: this is only provisional and | |
| * probably will be changed. | |
| * FIXME: this may only parse expressions without blocks as they are the only | |
| * expressions to have precedence? */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Expr> | |
| Parser<ManagedTokenSource>::parse_expr (int right_binding_power, | |
| AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| const_TokenPtr current_token = lexer.peek_token (); | |
| // Special hack because we are allowed to return nullptr, in that case we | |
| // don't want to skip the token, since we don't actually parse it. But if | |
| // null isn't allowed it indicates an error, and we want to skip past that. | |
| // So return early if it is one of the tokens that ends an expression | |
| // (or at least cannot start a new expression). | |
| if (restrictions.expr_can_be_null) | |
| { | |
| TokenId id = current_token->get_id (); | |
| if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY | |
| || id == RIGHT_SQUARE) | |
| return nullptr; | |
| } | |
| lexer.skip_token (); | |
| // parse null denotation (unary part of expression) | |
| std::unique_ptr<AST::Expr> expr | |
| = null_denotation (current_token, {}, restrictions); | |
| if (expr == nullptr) | |
| { | |
| // DEBUG | |
| rust_debug ("null denotation is null; returning null for parse_expr"); | |
| return nullptr; | |
| } | |
| // stop parsing if find lower priority token - parse higher priority first | |
| while (right_binding_power < left_binding_power (lexer.peek_token ())) | |
| { | |
| current_token = lexer.peek_token (); | |
| lexer.skip_token (); | |
| expr = left_denotation (current_token, std::move (expr), | |
| std::move (outer_attrs), restrictions); | |
| if (expr == nullptr) | |
| { | |
| // DEBUG | |
| rust_debug ("left denotation is null; returning null for parse_expr"); | |
| return nullptr; | |
| } | |
| } | |
| return expr; | |
| } | |
| // Parse expression with lowest left binding power. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Expr> | |
| Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions); | |
| } | |
| /* Determines action to take when finding token at beginning of expression. | |
| * FIXME: this may only apply to precedence-capable expressions (which are all | |
| * expressions without blocks), so make return type ExprWithoutBlock? It would | |
| * simplify stuff. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Expr> | |
| Parser<ManagedTokenSource>::null_denotation (const_TokenPtr tok, | |
| AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| /* note: tok is previous character in input stream, not current one, as | |
| * parse_expr skips it before passing it in */ | |
| /* as a Pratt parser (which works by decomposing expressions into a null | |
| * denotation and then a left denotation), null denotations handle primaries | |
| * and unary operands (but only prefix unary operands) */ | |
| switch (tok->get_id ()) | |
| { | |
| case IDENTIFIER: { | |
| // DEBUG | |
| rust_debug ("beginning null denotation identifier handling"); | |
| /* best option: parse as path, then extract identifier, macro, | |
| * struct/enum, or just path info from it */ | |
| AST::PathInExpression path = parse_path_in_expression_pratt (tok); | |
| // DEBUG: | |
| rust_debug ("finished null denotation identifier path parsing - " | |
| "next is branching"); | |
| // branch on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: | |
| // macro | |
| return parse_macro_invocation_partial (std::move (path), | |
| std::move (outer_attrs), | |
| restrictions); | |
| case LEFT_CURLY: { | |
| bool not_a_block | |
| = lexer.peek_token (1)->get_id () == IDENTIFIER | |
| && (lexer.peek_token (2)->get_id () == COMMA | |
| || (lexer.peek_token (2)->get_id () == COLON | |
| && (lexer.peek_token (4)->get_id () == COMMA | |
| || !can_tok_start_type ( | |
| lexer.peek_token (3)->get_id ())))); | |
| /* definitely not a block: | |
| * path '{' ident ',' | |
| * path '{' ident ':' [anything] ',' | |
| * path '{' ident ':' [not a type] | |
| * otherwise, assume block expr and thus path */ | |
| // DEBUG | |
| rust_debug ("values of lookahead: '%s' '%s' '%s' '%s' ", | |
| lexer.peek_token (1)->get_token_description (), | |
| lexer.peek_token (2)->get_token_description (), | |
| lexer.peek_token (3)->get_token_description (), | |
| lexer.peek_token (4)->get_token_description ()); | |
| rust_debug ("can be struct expr: '%s', not a block: '%s'", | |
| restrictions.can_be_struct_expr ? "true" : "false", | |
| not_a_block ? "true" : "false"); | |
| // struct/enum expr struct | |
| if (!restrictions.can_be_struct_expr && !not_a_block) | |
| { | |
| // HACK: add outer attrs to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| return parse_struct_expr_struct_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| } | |
| case LEFT_PAREN: | |
| // struct/enum expr tuple | |
| if (!restrictions.can_be_struct_expr) | |
| { | |
| // HACK: add outer attrs to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| return parse_struct_expr_tuple_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| default: | |
| // assume path is returned if not single segment | |
| if (path.is_single_segment ()) | |
| { | |
| // have to return an identifier expression or something, idk | |
| /* HACK: may have to become permanent, but this is my current | |
| * identifier expression */ | |
| return std::unique_ptr<AST::IdentifierExpr> ( | |
| new AST::IdentifierExpr (tok->get_str (), {}, | |
| tok->get_locus ())); | |
| } | |
| // HACK: add outer attrs to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| gcc_unreachable (); | |
| } | |
| /* FIXME: delegate to parse_literal_expr instead? would have to rejig | |
| * tokens and whatever. */ | |
| /* FIXME: could also be path expression (and hence macro expression, | |
| * struct/enum expr) */ | |
| case LEFT_ANGLE: { | |
| // qualified path | |
| // HACK: add outer attrs to path | |
| AST::QualifiedPathInExpression path | |
| = parse_qualified_path_in_expression (tok->get_locus ()); | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::QualifiedPathInExpression> ( | |
| new AST::QualifiedPathInExpression (std::move (path))); | |
| } | |
| // FIXME: for literal exprs, should outer attrs be passed in or just | |
| // ignored? | |
| case INT_LITERAL: | |
| // we should check the range, but ignore for now | |
| // encode as int? | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::INT, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case FLOAT_LITERAL: | |
| // encode as float? | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case STRING_LITERAL: | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case BYTE_STRING_LITERAL: | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE_STRING, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case CHAR_LITERAL: | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case BYTE_CHAR_LITERAL: | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case TRUE_LITERAL: | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr ("true", AST::Literal::BOOL, tok->get_type_hint (), | |
| {}, tok->get_locus ())); | |
| case FALSE_LITERAL: | |
| return std::unique_ptr<AST::LiteralExpr> ( | |
| new AST::LiteralExpr ("false", AST::Literal::BOOL, | |
| tok->get_type_hint (), {}, tok->get_locus ())); | |
| case LEFT_PAREN: | |
| return parse_grouped_or_tuple_expr (std::move (outer_attrs), | |
| tok->get_locus ()); | |
| /*case PLUS: { // unary plus operator | |
| // invoke parse_expr recursively with appropriate priority, etc. for | |
| below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS); | |
| if (expr == nullptr) | |
| return nullptr; | |
| // can only apply to integer and float expressions | |
| if (expr->get_type() != integer_type_node || expr->get_type() != | |
| float_type_node) { rust_error_at(tok->get_locus(), "operand of unary | |
| plus must be int or float but it is %s", print_type(expr->get_type())); | |
| return nullptr; | |
| } | |
| return Tree(expr, tok->get_locus()); | |
| }*/ | |
| // Rust has no unary plus operator | |
| case MINUS: { // unary minus | |
| ParseRestrictions entered_from_unary; | |
| entered_from_unary.entered_from_unary = true; | |
| if (!restrictions.can_be_struct_expr) | |
| entered_from_unary.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> expr | |
| = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary); | |
| if (expr == nullptr) | |
| return nullptr; | |
| // can only apply to integer and float expressions | |
| /*if (expr.get_type() != integer_type_node || expr.get_type() != | |
| float_type_node) { rust_error_at(tok->get_locus(), "operand of unary | |
| minus must be int or float but it is %s", | |
| print_type(expr.get_type())); return Tree::error(); | |
| }*/ | |
| /* FIXME: when implemented the "get type" method on expr, ensure it is | |
| * int or float type (except unsigned int). Actually, this would | |
| * probably have to be done in semantic analysis (as type checking). | |
| */ | |
| /* FIXME: allow outer attributes on these expressions by having an | |
| * outer attrs parameter in function*/ | |
| return std::unique_ptr<AST::NegationExpr> ( | |
| new AST::NegationExpr (std::move (expr), NegationOperator::NEGATE, | |
| std::move (outer_attrs), tok->get_locus ())); | |
| } | |
| case EXCLAM: { // logical or bitwise not | |
| ParseRestrictions entered_from_unary; | |
| entered_from_unary.entered_from_unary = true; | |
| if (!restrictions.can_be_struct_expr) | |
| entered_from_unary.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> expr | |
| = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary); | |
| if (expr == nullptr) | |
| return nullptr; | |
| // can only apply to boolean expressions | |
| /*if (expr.get_type() != boolean_type_node) { | |
| rust_error_at(tok->get_locus(), | |
| "operand of logical not must be a boolean but it is %s", | |
| print_type(expr.get_type())); | |
| return Tree::error(); | |
| }*/ | |
| /* FIXME: type checking for boolean or integer expressions in semantic | |
| * analysis */ | |
| // FIXME: allow outer attributes on these expressions | |
| return std::unique_ptr<AST::NegationExpr> ( | |
| new AST::NegationExpr (std::move (expr), NegationOperator::NOT, | |
| std::move (outer_attrs), tok->get_locus ())); | |
| } | |
| case ASTERISK: { | |
| /* pointer dereference only - HACK: as struct expressions should | |
| * always be value expressions, cannot be dereferenced */ | |
| ParseRestrictions entered_from_unary; | |
| entered_from_unary.entered_from_unary = true; | |
| entered_from_unary.can_be_struct_expr = false; | |
| std::unique_ptr<AST::Expr> expr | |
| = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary); | |
| // FIXME: allow outer attributes on expression | |
| return std::unique_ptr<AST::DereferenceExpr> ( | |
| new AST::DereferenceExpr (std::move (expr), std::move (outer_attrs), | |
| tok->get_locus ())); | |
| } | |
| case AMP: { | |
| // (single) "borrow" expression - shared (mutable) or immutable | |
| std::unique_ptr<AST::Expr> expr = nullptr; | |
| bool is_mut_borrow = false; | |
| /* HACK: as struct expressions should always be value expressions, | |
| * cannot be referenced */ | |
| ParseRestrictions entered_from_unary; | |
| entered_from_unary.entered_from_unary = true; | |
| entered_from_unary.can_be_struct_expr = false; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| lexer.skip_token (); | |
| expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary); | |
| is_mut_borrow = true; | |
| } | |
| else | |
| { | |
| expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary); | |
| } | |
| // FIXME: allow outer attributes on expression | |
| return std::unique_ptr<AST::BorrowExpr> ( | |
| new AST::BorrowExpr (std::move (expr), is_mut_borrow, false, | |
| std::move (outer_attrs), tok->get_locus ())); | |
| } | |
| case LOGICAL_AND: { | |
| // (double) "borrow" expression - shared (mutable) or immutable | |
| std::unique_ptr<AST::Expr> expr = nullptr; | |
| bool is_mut_borrow = false; | |
| ParseRestrictions entered_from_unary; | |
| entered_from_unary.entered_from_unary = true; | |
| if (lexer.peek_token ()->get_id () == MUT) | |
| { | |
| lexer.skip_token (); | |
| expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary); | |
| is_mut_borrow = true; | |
| } | |
| else | |
| { | |
| expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary); | |
| } | |
| // FIXME: allow outer attributes on expression | |
| return std::unique_ptr<AST::BorrowExpr> ( | |
| new AST::BorrowExpr (std::move (expr), is_mut_borrow, true, | |
| std::move (outer_attrs), tok->get_locus ())); | |
| } | |
| case SCOPE_RESOLUTION: { | |
| // TODO: fix: this is for global paths, i.e. std::string::whatever | |
| Error error (tok->get_locus (), | |
| "found null denotation scope resolution operator, and " | |
| "have not written handling for it"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| case SELF: | |
| case SELF_ALIAS: | |
| case DOLLAR_SIGN: | |
| case CRATE: | |
| case SUPER: { | |
| // DEBUG | |
| rust_debug ("beginning null denotation " | |
| "self/self-alias/dollar/crate/super handling"); | |
| /* best option: parse as path, then extract identifier, macro, | |
| * struct/enum, or just path info from it */ | |
| AST::PathInExpression path = parse_path_in_expression_pratt (tok); | |
| // DEBUG | |
| rust_debug ( | |
| "just finished parsing path (going to disambiguate) - peeked " | |
| "token is '%s'", | |
| lexer.peek_token ()->get_token_description ()); | |
| // HACK: always make "self" by itself a path (regardless of next | |
| // tokens) | |
| if (tok->get_id () == SELF && path.is_single_segment ()) | |
| { | |
| // HACK: add outer attrs to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| // branch on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case EXCLAM: | |
| // macro | |
| return parse_macro_invocation_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| case LEFT_CURLY: { | |
| // struct/enum expr struct | |
| rust_debug ("can_be_struct_expr: %s", | |
| restrictions.can_be_struct_expr ? "true" : "false"); | |
| bool not_a_block | |
| = lexer.peek_token (1)->get_id () == IDENTIFIER | |
| && (lexer.peek_token (2)->get_id () == COMMA | |
| || (lexer.peek_token (2)->get_id () == COLON | |
| && (lexer.peek_token (4)->get_id () == COMMA | |
| || !can_tok_start_type ( | |
| lexer.peek_token (3)->get_id ())))); | |
| if (!restrictions.can_be_struct_expr && !not_a_block) | |
| { | |
| // assume path is returned | |
| // HACK: add outer attributes to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| return parse_struct_expr_struct_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| } | |
| case LEFT_PAREN: | |
| // struct/enum expr tuple | |
| if (!restrictions.can_be_struct_expr) | |
| { | |
| // assume path is returned | |
| // HACK: add outer attributes to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| return parse_struct_expr_tuple_partial (std::move (path), | |
| std::move (outer_attrs)); | |
| default: | |
| // assume path is returned | |
| // HACK: add outer attributes to path | |
| path.set_outer_attrs (std::move (outer_attrs)); | |
| return std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| } | |
| gcc_unreachable (); | |
| } | |
| case OR: | |
| case PIPE: | |
| case MOVE: | |
| // closure expression | |
| return parse_closure_expr_pratt (tok, std::move (outer_attrs)); | |
| case DOT_DOT: | |
| // either "range to" or "range full" expressions | |
| return parse_nud_range_exclusive_expr (tok, std::move (outer_attrs)); | |
| case DOT_DOT_EQ: | |
| // range to inclusive expr | |
| return parse_range_to_inclusive_expr (tok, std::move (outer_attrs)); | |
| case RETURN_TOK: | |
| // FIXME: is this really a null denotation expression? | |
| return parse_return_expr (std::move (outer_attrs), tok->get_locus ()); | |
| case BREAK: | |
| // FIXME: is this really a null denotation expression? | |
| return parse_break_expr (std::move (outer_attrs), tok->get_locus ()); | |
| case CONTINUE: | |
| return parse_continue_expr (std::move (outer_attrs), tok->get_locus ()); | |
| case LEFT_CURLY: | |
| // ok - this is an expression with block for once. | |
| return parse_block_expr (std::move (outer_attrs), tok->get_locus ()); | |
| case IF: | |
| // if or if let, so more lookahead to find out | |
| if (lexer.peek_token (1)->get_id () == LET) | |
| { | |
| // if let expr | |
| return parse_if_let_expr (std::move (outer_attrs), tok->get_locus ()); | |
| } | |
| else | |
| { | |
| // if expr | |
| return parse_if_expr (std::move (outer_attrs), tok->get_locus ()); | |
| } | |
| case LOOP: | |
| return parse_loop_expr (std::move (outer_attrs), AST::LoopLabel::error (), | |
| tok->get_locus ()); | |
| case WHILE: | |
| return parse_while_loop_expr (std::move (outer_attrs), | |
| AST::LoopLabel::error (), | |
| tok->get_locus ()); | |
| case MATCH_TOK: | |
| // also an expression with block | |
| return parse_match_expr (std::move (outer_attrs), tok->get_locus ()); | |
| case LEFT_SQUARE: | |
| // array definition expr (not indexing) | |
| return parse_array_expr (std::move (outer_attrs), tok->get_locus ()); | |
| case UNSAFE: | |
| return parse_unsafe_block_expr (std::move (outer_attrs), | |
| tok->get_locus ()); | |
| default: | |
| if (!restrictions.expr_can_be_null) | |
| add_error (Error (tok->get_locus (), | |
| "found unexpected token %qs in null denotation", | |
| tok->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| /* Called for each token that can appear in infix (between) position. Can be | |
| * operators or other punctuation. Returns a function pointer to member | |
| * function that implements the left denotation for the token given. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::Expr> | |
| Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok, | |
| std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| // Token passed in has already been skipped, so peek gives "next" token | |
| switch (tok->get_id ()) | |
| { | |
| // FIXME: allow for outer attributes to be applied | |
| case QUESTION_MARK: { | |
| Location left_locus = left->get_locus (); | |
| // error propagation expression - unary postfix | |
| return std::unique_ptr<AST::ErrorPropagationExpr> ( | |
| new AST::ErrorPropagationExpr (std::move (left), | |
| std::move (outer_attrs), left_locus)); | |
| } | |
| case PLUS: | |
| // sum expression - binary infix | |
| /*return parse_binary_plus_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::ADD, | |
| restrictions); | |
| case MINUS: | |
| // difference expression - binary infix | |
| /*return parse_binary_minus_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::SUBTRACT, restrictions); | |
| case ASTERISK: | |
| // product expression - binary infix | |
| /*return parse_binary_mult_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::MULTIPLY, restrictions); | |
| case DIV: | |
| // quotient expression - binary infix | |
| /*return parse_binary_div_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::DIVIDE, restrictions); | |
| case PERCENT: | |
| // modulo expression - binary infix | |
| /*return parse_binary_mod_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::MODULUS, restrictions); | |
| case AMP: | |
| // logical or bitwise and expression - binary infix | |
| /*return parse_bitwise_and_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::BITWISE_AND, restrictions); | |
| case PIPE: | |
| // logical or bitwise or expression - binary infix | |
| /*return parse_bitwise_or_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::BITWISE_OR, restrictions); | |
| case CARET: | |
| // logical or bitwise xor expression - binary infix | |
| /*return parse_bitwise_xor_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::BITWISE_XOR, restrictions); | |
| case LEFT_SHIFT: | |
| // left shift expression - binary infix | |
| /*return parse_left_shift_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::LEFT_SHIFT, restrictions); | |
| case RIGHT_SHIFT: | |
| // right shift expression - binary infix | |
| /*return parse_right_shift_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_arithmetic_or_logical_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| ArithmeticOrLogicalOperator::RIGHT_SHIFT, restrictions); | |
| case EQUAL_EQUAL: | |
| // equal to expression - binary infix (no associativity) | |
| /*return parse_binary_equal_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_comparison_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ComparisonOperator::EQUAL, restrictions); | |
| case NOT_EQUAL: | |
| // not equal to expression - binary infix (no associativity) | |
| /*return parse_binary_not_equal_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_comparison_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ComparisonOperator::NOT_EQUAL, | |
| restrictions); | |
| case RIGHT_ANGLE: | |
| // greater than expression - binary infix (no associativity) | |
| /*return parse_binary_greater_than_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_comparison_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ComparisonOperator::GREATER_THAN, | |
| restrictions); | |
| case LEFT_ANGLE: | |
| // less than expression - binary infix (no associativity) | |
| /*return parse_binary_less_than_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_comparison_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ComparisonOperator::LESS_THAN, | |
| restrictions); | |
| case GREATER_OR_EQUAL: | |
| // greater than or equal to expression - binary infix (no associativity) | |
| /*return parse_binary_greater_equal_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_comparison_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ComparisonOperator::GREATER_OR_EQUAL, | |
| restrictions); | |
| case LESS_OR_EQUAL: | |
| // less than or equal to expression - binary infix (no associativity) | |
| /*return parse_binary_less_equal_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_comparison_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| ComparisonOperator::LESS_OR_EQUAL, | |
| restrictions); | |
| case OR: | |
| // lazy logical or expression - binary infix | |
| return parse_lazy_or_expr (tok, std::move (left), std::move (outer_attrs), | |
| restrictions); | |
| case LOGICAL_AND: | |
| // lazy logical and expression - binary infix | |
| return parse_lazy_and_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions); | |
| case AS: | |
| /* type cast expression - kind of binary infix (RHS is actually a | |
| * TypeNoBounds) */ | |
| return parse_type_cast_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions); | |
| case EQUAL: | |
| // assignment expression - binary infix (note right-to-left | |
| // associativity) | |
| return parse_assig_expr (tok, std::move (left), std::move (outer_attrs), | |
| restrictions); | |
| case PLUS_EQ: | |
| /* plus-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_plus_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| CompoundAssignmentOperator::ADD, | |
| restrictions); | |
| case MINUS_EQ: | |
| /* minus-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_minus_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::SUBTRACT, restrictions); | |
| case ASTERISK_EQ: | |
| /* multiply-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_mult_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::MULTIPLY, restrictions); | |
| case DIV_EQ: | |
| /* division-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_div_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| CompoundAssignmentOperator::DIVIDE, | |
| restrictions); | |
| case PERCENT_EQ: | |
| /* modulo-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_mod_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::MODULUS, restrictions); | |
| case AMP_EQ: | |
| /* bitwise and-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_and_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::BITWISE_AND, restrictions); | |
| case PIPE_EQ: | |
| /* bitwise or-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_or_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::BITWISE_OR, restrictions); | |
| case CARET_EQ: | |
| /* bitwise xor-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_xor_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::BITWISE_XOR, restrictions); | |
| case LEFT_SHIFT_EQ: | |
| /* left shift-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_left_shift_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::LEFT_SHIFT, restrictions); | |
| case RIGHT_SHIFT_EQ: | |
| /* right shift-assignment expression - binary infix (note right-to-left | |
| * associativity) */ | |
| /*return parse_right_shift_assig_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions);*/ | |
| return parse_compound_assignment_expr ( | |
| tok, std::move (left), std::move (outer_attrs), | |
| CompoundAssignmentOperator::RIGHT_SHIFT, restrictions); | |
| case DOT_DOT: | |
| /* range exclusive expression - binary infix (no associativity) | |
| * either "range" or "range from" */ | |
| return parse_led_range_exclusive_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions); | |
| case DOT_DOT_EQ: | |
| /* range inclusive expression - binary infix (no associativity) | |
| * unambiguously RangeInclusiveExpr */ | |
| return parse_range_inclusive_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions); | |
| case SCOPE_RESOLUTION: | |
| // path expression - binary infix? FIXME should this even be parsed | |
| // here? | |
| add_error ( | |
| Error (tok->get_locus (), | |
| "found scope resolution operator in left denotation " | |
| "function - this should probably be handled elsewhere")); | |
| return nullptr; | |
| case DOT: { | |
| /* field expression or method call - relies on parentheses after next | |
| * identifier or await if token after is "await" (unary postfix) or | |
| * tuple index if token after is a decimal int literal */ | |
| const_TokenPtr next_tok = lexer.peek_token (); | |
| if (next_tok->get_id () == IDENTIFIER | |
| && next_tok->get_str () == "await") | |
| { | |
| // await expression | |
| return parse_await_expr (tok, std::move (left), | |
| std::move (outer_attrs)); | |
| } | |
| else if (next_tok->get_id () == INT_LITERAL) | |
| { | |
| // tuple index expression - TODO check for decimal int literal | |
| return parse_tuple_index_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions); | |
| } | |
| else if (next_tok->get_id () == IDENTIFIER | |
| && lexer.peek_token (1)->get_id () != LEFT_PAREN | |
| && lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION) | |
| { | |
| /* field expression (or should be) - FIXME: scope resolution right | |
| * after identifier should always be method, I'm pretty sure */ | |
| return parse_field_access_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions); | |
| } | |
| else | |
| { | |
| // method call (probably) | |
| return parse_method_call_expr (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions); | |
| } | |
| } | |
| case LEFT_PAREN: | |
| // function call - method call is based on dot notation first | |
| return parse_function_call_expr (tok, std::move (left), | |
| std::move (outer_attrs), restrictions); | |
| case LEFT_SQUARE: | |
| // array or slice index expression (pseudo binary infix) | |
| return parse_index_expr (tok, std::move (left), std::move (outer_attrs), | |
| restrictions); | |
| case FLOAT_LITERAL: | |
| /* HACK: get around lexer mis-identifying '.0' or '.1' or whatever as a | |
| * float literal - TODO does this happen anymore? It shouldn't. */ | |
| return parse_tuple_index_expr_float (tok, std::move (left), | |
| std::move (outer_attrs), | |
| restrictions); | |
| default: | |
| add_error (Error (tok->get_locus (), | |
| "found unexpected token %qs in left denotation", | |
| tok->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| /* Returns the left binding power for the given ArithmeticOrLogicalExpr type. | |
| * TODO make constexpr? Would that even do anything useful? */ | |
| inline binding_powers | |
| get_lbp_for_arithmetic_or_logical_expr ( | |
| AST::ArithmeticOrLogicalExpr::ExprType expr_type) | |
| { | |
| switch (expr_type) | |
| { | |
| case ArithmeticOrLogicalOperator::ADD: | |
| return LBP_PLUS; | |
| case ArithmeticOrLogicalOperator::SUBTRACT: | |
| return LBP_MINUS; | |
| case ArithmeticOrLogicalOperator::MULTIPLY: | |
| return LBP_MUL; | |
| case ArithmeticOrLogicalOperator::DIVIDE: | |
| return LBP_DIV; | |
| case ArithmeticOrLogicalOperator::MODULUS: | |
| return LBP_MOD; | |
| case ArithmeticOrLogicalOperator::BITWISE_AND: | |
| return LBP_AMP; | |
| case ArithmeticOrLogicalOperator::BITWISE_OR: | |
| return LBP_PIPE; | |
| case ArithmeticOrLogicalOperator::BITWISE_XOR: | |
| return LBP_CARET; | |
| case ArithmeticOrLogicalOperator::LEFT_SHIFT: | |
| return LBP_L_SHIFT; | |
| case ArithmeticOrLogicalOperator::RIGHT_SHIFT: | |
| return LBP_R_SHIFT; | |
| default: | |
| // WTF? should not happen, this is an error | |
| gcc_unreachable (); | |
| return LBP_PLUS; | |
| } | |
| } | |
| // Parses an arithmetic or logical expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr ( | |
| const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec, | |
| AST::ArithmeticOrLogicalExpr::ExprType expr_type, | |
| ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type), | |
| AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| expr_type, locus)); | |
| } | |
| // Parses a binary addition expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_binary_plus_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::ADD, locus)); | |
| } | |
| // Parses a binary subtraction expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_binary_minus_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::SUBTRACT, | |
| locus)); | |
| } | |
| // Parses a binary multiplication expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_binary_mult_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_MUL, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::MULTIPLY, | |
| locus)); | |
| } | |
| // Parses a binary division expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_binary_div_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_DIV, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::DIVIDE, | |
| locus)); | |
| } | |
| // Parses a binary modulo expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_binary_mod_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_MOD, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::MODULUS, | |
| locus)); | |
| } | |
| /* Parses a binary bitwise (or eager logical) and expression (with Pratt | |
| * parsing). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_bitwise_and_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_AMP, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::BITWISE_AND, | |
| locus)); | |
| } | |
| /* Parses a binary bitwise (or eager logical) or expression (with Pratt | |
| * parsing). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_bitwise_or_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::BITWISE_OR, | |
| locus)); | |
| } | |
| /* Parses a binary bitwise (or eager logical) xor expression (with Pratt | |
| * parsing). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_bitwise_xor_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_CARET, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::BITWISE_XOR, | |
| locus)); | |
| } | |
| // Parses a binary left shift expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_left_shift_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::LEFT_SHIFT, | |
| locus)); | |
| } | |
| // Parses a binary right shift expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArithmeticOrLogicalExpr> | |
| Parser<ManagedTokenSource>::parse_right_shift_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ArithmeticOrLogicalExpr> ( | |
| new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right), | |
| ArithmeticOrLogicalOperator::RIGHT_SHIFT, | |
| locus)); | |
| } | |
| /* Returns the left binding power for the given ComparisonExpr type. | |
| * TODO make constexpr? Would that even do anything useful? */ | |
| inline binding_powers | |
| get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type) | |
| { | |
| switch (expr_type) | |
| { | |
| case ComparisonOperator::EQUAL: | |
| return LBP_EQUAL; | |
| case ComparisonOperator::NOT_EQUAL: | |
| return LBP_NOT_EQUAL; | |
| case ComparisonOperator::GREATER_THAN: | |
| return LBP_GREATER_THAN; | |
| case ComparisonOperator::LESS_THAN: | |
| return LBP_SMALLER_THAN; | |
| case ComparisonOperator::GREATER_OR_EQUAL: | |
| return LBP_GREATER_EQUAL; | |
| case ComparisonOperator::LESS_OR_EQUAL: | |
| return LBP_SMALLER_EQUAL; | |
| default: | |
| // WTF? should not happen, this is an error | |
| gcc_unreachable (); | |
| return LBP_EQUAL; | |
| } | |
| } | |
| /* Parses a ComparisonExpr of given type and LBP. TODO find a way to only | |
| * specify one and have the other looked up - e.g. specify ExprType and | |
| * binding power is looked up? */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_comparison_expr ( | |
| const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec, | |
| AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (get_lbp_for_comparison_expr (expr_type), AST::AttrVec (), | |
| restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), expr_type, | |
| locus)); | |
| } | |
| // Parses a binary equal to expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_binary_equal_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), | |
| ComparisonOperator::EQUAL, locus)); | |
| } | |
| // Parses a binary not equal to expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_binary_not_equal_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), | |
| ComparisonOperator::NOT_EQUAL, locus)); | |
| } | |
| // Parses a binary greater than expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_binary_greater_than_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), | |
| ComparisonOperator::GREATER_THAN, locus)); | |
| } | |
| // Parses a binary less than expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_binary_less_than_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), | |
| ComparisonOperator::LESS_THAN, locus)); | |
| } | |
| // Parses a binary greater than or equal to expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_binary_greater_equal_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), | |
| ComparisonOperator::GREATER_OR_EQUAL, locus)); | |
| } | |
| // Parses a binary less than or equal to expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ComparisonExpr> | |
| Parser<ManagedTokenSource>::parse_binary_less_equal_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::ComparisonExpr> ( | |
| new AST::ComparisonExpr (std::move (left), std::move (right), | |
| ComparisonOperator::LESS_OR_EQUAL, locus)); | |
| } | |
| // Parses a binary lazy boolean or expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::LazyBooleanExpr> | |
| Parser<ManagedTokenSource>::parse_lazy_or_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::LazyBooleanExpr> ( | |
| new AST::LazyBooleanExpr (std::move (left), std::move (right), | |
| LazyBooleanOperator::LOGICAL_OR, locus)); | |
| } | |
| // Parses a binary lazy boolean and expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::LazyBooleanExpr> | |
| Parser<ManagedTokenSource>::parse_lazy_and_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::LazyBooleanExpr> ( | |
| new AST::LazyBooleanExpr (std::move (left), std::move (right), | |
| LazyBooleanOperator::LOGICAL_AND, locus)); | |
| } | |
| // Parses a pseudo-binary infix type cast expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypeCastExpr> | |
| Parser<ManagedTokenSource>::parse_type_cast_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, | |
| ParseRestrictions restrictions ATTRIBUTE_UNUSED) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds (); | |
| if (type == nullptr) | |
| return nullptr; | |
| // FIXME: how do I get precedence put in here? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = expr_to_cast->get_locus (); | |
| return std::unique_ptr<AST::TypeCastExpr> ( | |
| new AST::TypeCastExpr (std::move (expr_to_cast), std::move (type), locus)); | |
| } | |
| // Parses a binary assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::AssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::AssignmentExpr> ( | |
| new AST::AssignmentExpr (std::move (left), std::move (right), | |
| std::move (outer_attrs), locus)); | |
| } | |
| /* Returns the left binding power for the given CompoundAssignmentExpr type. | |
| * TODO make constexpr? Would that even do anything useful? */ | |
| inline binding_powers | |
| get_lbp_for_compound_assignment_expr ( | |
| AST::CompoundAssignmentExpr::ExprType expr_type) | |
| { | |
| switch (expr_type) | |
| { | |
| case CompoundAssignmentOperator::ADD: | |
| return LBP_PLUS; | |
| case CompoundAssignmentOperator::SUBTRACT: | |
| return LBP_MINUS; | |
| case CompoundAssignmentOperator::MULTIPLY: | |
| return LBP_MUL; | |
| case CompoundAssignmentOperator::DIVIDE: | |
| return LBP_DIV; | |
| case CompoundAssignmentOperator::MODULUS: | |
| return LBP_MOD; | |
| case CompoundAssignmentOperator::BITWISE_AND: | |
| return LBP_AMP; | |
| case CompoundAssignmentOperator::BITWISE_OR: | |
| return LBP_PIPE; | |
| case CompoundAssignmentOperator::BITWISE_XOR: | |
| return LBP_CARET; | |
| case CompoundAssignmentOperator::LEFT_SHIFT: | |
| return LBP_L_SHIFT; | |
| case CompoundAssignmentOperator::RIGHT_SHIFT: | |
| return LBP_R_SHIFT; | |
| default: | |
| // WTF? should not happen, this is an error | |
| gcc_unreachable (); | |
| return LBP_PLUS; | |
| } | |
| } | |
| // Parses a compound assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_compound_assignment_expr ( | |
| const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec, | |
| AST::CompoundAssignmentExpr::ExprType expr_type, | |
| ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1, | |
| AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| expr_type, locus)); | |
| } | |
| // Parses a binary add-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_plus_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::ADD, locus)); | |
| } | |
| // Parses a binary minus-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_minus_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::SUBTRACT, | |
| locus)); | |
| } | |
| // Parses a binary multiplication-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_mult_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::MULTIPLY, | |
| locus)); | |
| } | |
| // Parses a binary division-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_div_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::DIVIDE, | |
| locus)); | |
| } | |
| // Parses a binary modulo-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_mod_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::MODULUS, | |
| locus)); | |
| } | |
| // Parses a binary and-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_and_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::BITWISE_AND, | |
| locus)); | |
| } | |
| // Parses a binary or-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_or_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::BITWISE_OR, | |
| locus)); | |
| } | |
| // Parses a binary xor-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_xor_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::BITWISE_XOR, | |
| locus)); | |
| } | |
| // Parses a binary left shift-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_left_shift_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::LEFT_SHIFT, | |
| locus)); | |
| } | |
| // Parses a binary right shift-assignment expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CompoundAssignmentExpr> | |
| Parser<ManagedTokenSource>::parse_right_shift_assig_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: ensure right-associativity for this - 'LBP - 1' may do this? | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::CompoundAssignmentExpr> ( | |
| new AST::CompoundAssignmentExpr (std::move (left), std::move (right), | |
| CompoundAssignmentOperator::RIGHT_SHIFT, | |
| locus)); | |
| } | |
| // Parses a postfix unary await expression (with Pratt parsing). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::AwaitExpr> | |
| Parser<ManagedTokenSource>::parse_await_expr ( | |
| const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await, | |
| AST::AttrVec outer_attrs) | |
| { | |
| /* skip "await" identifier (as "." has already been consumed in | |
| * parse_expression) this assumes that the identifier was already identified | |
| * as await */ | |
| if (!skip_token (IDENTIFIER)) | |
| { | |
| Error error (tok->get_locus (), "failed to skip %<await%> in await expr " | |
| "- this is probably a deep issue"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // TODO: check inside async block in semantic analysis | |
| Location locus = expr_to_await->get_locus (); | |
| return std::unique_ptr<AST::AwaitExpr> ( | |
| new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs), | |
| locus)); | |
| } | |
| /* Parses an exclusive range ('..') in left denotation position (i.e. | |
| * RangeFromExpr or RangeFromToExpr). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::RangeExpr> | |
| Parser<ManagedTokenSource>::parse_led_range_exclusive_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // FIXME: this probably parses expressions accidently or whatever | |
| // try parsing RHS (as tok has already been consumed in parse_expression) | |
| // Can be nullptr, in which case it is a RangeFromExpr, otherwise a | |
| // RangeFromToExpr. | |
| restrictions.expr_can_be_null = true; | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions); | |
| Location locus = left->get_locus (); | |
| if (right == nullptr) | |
| { | |
| // range from expr | |
| return std::unique_ptr<AST::RangeFromExpr> ( | |
| new AST::RangeFromExpr (std::move (left), locus)); | |
| } | |
| else | |
| { | |
| return std::unique_ptr<AST::RangeFromToExpr> ( | |
| new AST::RangeFromToExpr (std::move (left), std::move (right), locus)); | |
| } | |
| // FIXME: make non-associative | |
| } | |
| /* Parses an exclusive range ('..') in null denotation position (i.e. | |
| * RangeToExpr or RangeFullExpr). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::RangeExpr> | |
| Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr ( | |
| const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED) | |
| { | |
| auto restrictions = ParseRestrictions (); | |
| restrictions.expr_can_be_null = true; | |
| // FIXME: this probably parses expressions accidently or whatever | |
| // try parsing RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions); | |
| Location locus = tok->get_locus (); | |
| if (right == nullptr) | |
| { | |
| // range from expr | |
| return std::unique_ptr<AST::RangeFullExpr> ( | |
| new AST::RangeFullExpr (locus)); | |
| } | |
| else | |
| { | |
| return std::unique_ptr<AST::RangeToExpr> ( | |
| new AST::RangeToExpr (std::move (right), locus)); | |
| } | |
| // FIXME: make non-associative | |
| } | |
| // Parses a full binary range inclusive expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::RangeFromToInclExpr> | |
| Parser<ManagedTokenSource>::parse_range_inclusive_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left, | |
| AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right | |
| = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: make non-associative | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = left->get_locus (); | |
| return std::unique_ptr<AST::RangeFromToInclExpr> ( | |
| new AST::RangeFromToInclExpr (std::move (left), std::move (right), locus)); | |
| } | |
| // Parses an inclusive range-to prefix unary expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::RangeToInclExpr> | |
| Parser<ManagedTokenSource>::parse_range_to_inclusive_expr ( | |
| const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| std::unique_ptr<AST::Expr> right = parse_expr (LBP_DOT_DOT_EQ); | |
| if (right == nullptr) | |
| return nullptr; | |
| // FIXME: make non-associative | |
| // TODO: check types. actually, do so during semantic analysis | |
| return std::unique_ptr<AST::RangeToInclExpr> ( | |
| new AST::RangeToInclExpr (std::move (right), tok->get_locus ())); | |
| } | |
| // Parses a pseudo-binary infix tuple index expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TupleIndexExpr> | |
| Parser<ManagedTokenSource>::parse_tuple_index_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr, | |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED) | |
| { | |
| // parse int literal (as token already skipped) | |
| const_TokenPtr index_tok = expect_token (INT_LITERAL); | |
| if (index_tok == nullptr) | |
| { | |
| return nullptr; | |
| } | |
| std::string index = index_tok->get_str (); | |
| // convert to integer | |
| if (!index_tok->is_pure_decimal ()) | |
| { | |
| Error error (index_tok->get_locus (), | |
| "tuple index should be a pure decimal literal"); | |
| add_error (std::move (error)); | |
| } | |
| int index_int = atoi (index.c_str ()); | |
| Location locus = tuple_expr->get_locus (); | |
| return std::unique_ptr<AST::TupleIndexExpr> ( | |
| new AST::TupleIndexExpr (std::move (tuple_expr), index_int, | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a pseudo-binary infix array (or slice) index expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ArrayIndexExpr> | |
| Parser<ManagedTokenSource>::parse_index_expr ( | |
| const_TokenPtr, std::unique_ptr<AST::Expr> array_expr, | |
| AST::AttrVec outer_attrs, ParseRestrictions) | |
| { | |
| // parse RHS (as tok has already been consumed in parse_expression) | |
| /*std::unique_ptr<AST::Expr> index_expr | |
| = parse_expr (LBP_ARRAY_REF, AST::AttrVec (), | |
| restrictions);*/ | |
| // TODO: conceptually, should treat [] as brackets, so just parse all expr | |
| std::unique_ptr<AST::Expr> index_expr = parse_expr (); | |
| if (index_expr == nullptr) | |
| return nullptr; | |
| // skip ']' at end of array | |
| if (!skip_token (RIGHT_SQUARE)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = array_expr->get_locus (); | |
| return std::unique_ptr<AST::ArrayIndexExpr> ( | |
| new AST::ArrayIndexExpr (std::move (array_expr), std::move (index_expr), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a pseudo-binary infix struct field access expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::FieldAccessExpr> | |
| Parser<ManagedTokenSource>::parse_field_access_expr ( | |
| const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr, | |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED) | |
| { | |
| /* get field name identifier (assume that this is a field access expr and | |
| * not await, for instance) */ | |
| const_TokenPtr ident_tok = expect_token (IDENTIFIER); | |
| if (ident_tok == nullptr) | |
| return nullptr; | |
| Identifier ident = ident_tok->get_str (); | |
| Location locus = struct_expr->get_locus (); | |
| // TODO: check types. actually, do so during semantic analysis | |
| return std::unique_ptr<AST::FieldAccessExpr> ( | |
| new AST::FieldAccessExpr (std::move (struct_expr), std::move (ident), | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Parses a pseudo-binary infix method call expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MethodCallExpr> | |
| Parser<ManagedTokenSource>::parse_method_call_expr ( | |
| const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr, | |
| AST::AttrVec outer_attrs, ParseRestrictions) | |
| { | |
| // parse path expr segment | |
| AST::PathExprSegment segment = parse_path_expr_segment (); | |
| if (segment.is_error ()) | |
| { | |
| Error error (tok->get_locus (), | |
| "failed to parse path expr segment of method call expr"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // skip left parentheses | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| // parse method params (if they exist) | |
| std::vector<std::unique_ptr<AST::Expr>> params; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::Expr> param = parse_expr (); | |
| if (param == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse method param in method call"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // skip right paren | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| // TODO: check types. actually do so in semantic analysis pass. | |
| Location locus = receiver_expr->get_locus (); | |
| return std::unique_ptr<AST::MethodCallExpr> ( | |
| new AST::MethodCallExpr (std::move (receiver_expr), std::move (segment), | |
| std::move (params), std::move (outer_attrs), | |
| locus)); | |
| } | |
| // Parses a pseudo-binary infix function call expression. | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CallExpr> | |
| Parser<ManagedTokenSource>::parse_function_call_expr ( | |
| const_TokenPtr, std::unique_ptr<AST::Expr> function_expr, | |
| AST::AttrVec outer_attrs, ParseRestrictions) | |
| { | |
| // parse function params (if they exist) | |
| std::vector<std::unique_ptr<AST::Expr>> params; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::Expr> param = parse_expr (); | |
| if (param == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse function param in function call"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| // skip ')' at end of param list | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // TODO: check types. actually, do so during semantic analysis | |
| Location locus = function_expr->get_locus (); | |
| return std::unique_ptr<AST::CallExpr> ( | |
| new AST::CallExpr (std::move (function_expr), std::move (params), | |
| std::move (outer_attrs), locus)); | |
| } | |
| /* Parses a macro invocation with a path in expression already parsed (but not | |
| * '!' token). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::MacroInvocation> | |
| Parser<ManagedTokenSource>::parse_macro_invocation_partial ( | |
| AST::PathInExpression path, AST::AttrVec outer_attrs, | |
| ParseRestrictions restrictions) | |
| { | |
| // macro invocation | |
| if (!skip_token (EXCLAM)) | |
| { | |
| return nullptr; | |
| } | |
| // convert PathInExpression to SimplePath - if this isn't possible, error | |
| AST::SimplePath converted_path = path.as_simple_path (); | |
| if (converted_path.is_empty ()) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse simple path in macro invocation"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| AST::DelimTokenTree tok_tree = parse_delim_token_tree (); | |
| rust_debug ("successfully parsed macro invocation (via partial)"); | |
| Location macro_locus = converted_path.get_locus (); | |
| return std::unique_ptr<AST::MacroInvocation> (new AST::MacroInvocation ( | |
| AST::MacroInvocData (std::move (converted_path), std::move (tok_tree)), | |
| std::move (outer_attrs), macro_locus, restrictions.expr_can_be_stmt)); | |
| } | |
| /* Parses a struct expr struct with a path in expression already parsed (but | |
| * not | |
| * '{' token). */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::StructExprStruct> | |
| Parser<ManagedTokenSource>::parse_struct_expr_struct_partial ( | |
| AST::PathInExpression path, AST::AttrVec outer_attrs) | |
| { | |
| // assume struct expr struct (as struct-enum disambiguation requires name | |
| // lookup) again, make statement if final ';' | |
| if (!skip_token (LEFT_CURLY)) | |
| { | |
| return nullptr; | |
| } | |
| // parse inner attributes | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| // branch based on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| Location path_locus = path.get_locus (); | |
| switch (t->get_id ()) | |
| { | |
| case RIGHT_CURLY: | |
| // struct with no body | |
| lexer.skip_token (); | |
| return std::unique_ptr<AST::StructExprStruct> ( | |
| new AST::StructExprStruct (std::move (path), std::move (inner_attrs), | |
| std::move (outer_attrs), path_locus)); | |
| case DOT_DOT: | |
| /* technically this would give a struct base-only struct, but this | |
| * algorithm should work too. As such, AST type not happening. */ | |
| case IDENTIFIER: | |
| case INT_LITERAL: { | |
| // struct with struct expr fields | |
| // parse struct expr fields | |
| std::vector<std::unique_ptr<AST::StructExprField>> fields; | |
| while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT) | |
| { | |
| std::unique_ptr<AST::StructExprField> field | |
| = parse_struct_expr_field (); | |
| if (field == nullptr) | |
| { | |
| Error error (t->get_locus (), | |
| "failed to parse struct (or enum) expr field"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // DEBUG: | |
| rust_debug ("struct/enum expr field validated to not be null"); | |
| fields.push_back (std::move (field)); | |
| // DEBUG: | |
| rust_debug ("struct/enum expr field pushed back"); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| // DEBUG: | |
| rust_debug ("lack of comma detected in struct/enum expr " | |
| "fields - break"); | |
| break; | |
| } | |
| lexer.skip_token (); | |
| // DEBUG: | |
| rust_debug ("struct/enum expr fields comma skipped "); | |
| t = lexer.peek_token (); | |
| } | |
| // DEBUG: | |
| rust_debug ("struct/enum expr about to parse struct base "); | |
| // parse struct base if it exists | |
| AST::StructBase struct_base = AST::StructBase::error (); | |
| if (lexer.peek_token ()->get_id () == DOT_DOT) | |
| { | |
| Location dot_dot_location = lexer.peek_token ()->get_locus (); | |
| lexer.skip_token (); | |
| // parse required struct base expr | |
| std::unique_ptr<AST::Expr> base_expr = parse_expr (); | |
| if (base_expr == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse struct base expression in struct " | |
| "expression"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| // DEBUG: | |
| rust_debug ("struct/enum expr - parsed and validated base expr"); | |
| struct_base | |
| = AST::StructBase (std::move (base_expr), dot_dot_location); | |
| // DEBUG: | |
| rust_debug ("assigned struct base to new struct base "); | |
| } | |
| if (!skip_token (RIGHT_CURLY)) | |
| { | |
| return nullptr; | |
| } | |
| // DEBUG: | |
| rust_debug ( | |
| "struct/enum expr skipped right curly - done and ready to return"); | |
| return std::unique_ptr<AST::StructExprStructFields> ( | |
| new AST::StructExprStructFields (std::move (path), std::move (fields), | |
| path_locus, std::move (struct_base), | |
| std::move (inner_attrs), | |
| std::move (outer_attrs))); | |
| } | |
| default: | |
| add_error ( | |
| Error (t->get_locus (), | |
| "unrecognised token %qs in struct (or enum) expression - " | |
| "expected %<}%>, identifier, integer literal, or %<..%>", | |
| t->get_token_description ())); | |
| return nullptr; | |
| } | |
| } | |
| /* Parses a struct expr tuple with a path in expression already parsed (but | |
| * not | |
| * '(' token). | |
| * FIXME: this currently outputs a call expr, as they cannot be disambiguated. | |
| * A better solution would be to just get this to call that function directly. | |
| * */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::CallExpr> | |
| Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial ( | |
| AST::PathInExpression path, AST::AttrVec outer_attrs) | |
| { | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| AST::AttrVec inner_attrs = parse_inner_attributes (); | |
| std::vector<std::unique_ptr<AST::Expr>> exprs; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != RIGHT_PAREN) | |
| { | |
| // parse expression (required) | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (t->get_locus (), "failed to parse expression in " | |
| "struct (or enum) expression tuple"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| exprs.push_back (std::move (expr)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| return nullptr; | |
| } | |
| Location path_locus = path.get_locus (); | |
| auto pathExpr = std::unique_ptr<AST::PathInExpression> ( | |
| new AST::PathInExpression (std::move (path))); | |
| return std::unique_ptr<AST::CallExpr> ( | |
| new AST::CallExpr (std::move (pathExpr), std::move (exprs), | |
| std::move (outer_attrs), path_locus)); | |
| } | |
| /* Parses a path in expression with the first token passed as a parameter (as | |
| * it is skipped in token stream). Note that this only parses segment-first | |
| * paths, not global ones. */ | |
| template <typename ManagedTokenSource> | |
| AST::PathInExpression | |
| Parser<ManagedTokenSource>::parse_path_in_expression_pratt (const_TokenPtr tok) | |
| { | |
| // HACK-y way of making up for pratt-parsing consuming first token | |
| // DEBUG | |
| rust_debug ("current peek token when starting path pratt parse: '%s'", | |
| lexer.peek_token ()->get_token_description ()); | |
| // create segment vector | |
| std::vector<AST::PathExprSegment> segments; | |
| std::string initial_str; | |
| switch (tok->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| initial_str = tok->get_str (); | |
| break; | |
| case SUPER: | |
| initial_str = "super"; | |
| break; | |
| case SELF: | |
| initial_str = "self"; | |
| break; | |
| case SELF_ALIAS: | |
| initial_str = "Self"; | |
| break; | |
| case CRATE: | |
| initial_str = "crate"; | |
| break; | |
| case DOLLAR_SIGN: | |
| if (lexer.peek_token ()->get_id () == CRATE) | |
| { | |
| initial_str = "$crate"; | |
| break; | |
| } | |
| gcc_fallthrough (); | |
| default: | |
| add_error (Error (tok->get_locus (), | |
| "unrecognised token %qs in path in expression", | |
| tok->get_token_description ())); | |
| return AST::PathInExpression::create_error (); | |
| } | |
| // parse required initial segment | |
| AST::PathExprSegment initial_segment (initial_str, tok->get_locus ()); | |
| // parse generic args (and turbofish), if they exist | |
| /* use lookahead to determine if they actually exist (don't want to | |
| * accidently parse over next ident segment) */ | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION | |
| && lexer.peek_token (1)->get_id () == LEFT_ANGLE) | |
| { | |
| // skip scope resolution | |
| lexer.skip_token (); | |
| AST::GenericArgs generic_args = parse_path_generic_args (); | |
| initial_segment | |
| = AST::PathExprSegment (AST::PathIdentSegment (initial_str, | |
| tok->get_locus ()), | |
| tok->get_locus (), std::move (generic_args)); | |
| } | |
| if (initial_segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| // don't necessarily throw error but yeah | |
| // DEBUG | |
| rust_debug ("initial segment is error - returning null"); | |
| return AST::PathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| AST::PathExprSegment segment = parse_path_expr_segment (); | |
| if (segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), | |
| "could not parse path expression segment"); | |
| add_error (std::move (error)); | |
| return AST::PathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| // DEBUG: | |
| rust_debug ( | |
| "current token (just about to return path to null denotation): '%s'", | |
| lexer.peek_token ()->get_token_description ()); | |
| return AST::PathInExpression (std::move (segments), {}, tok->get_locus (), | |
| false); | |
| } | |
| // Parses a closure expression with pratt parsing (from null denotation). | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::ClosureExpr> | |
| Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok, | |
| AST::AttrVec outer_attrs) | |
| { | |
| // TODO: does this need pratt parsing (for precedence)? probably not, but | |
| // idk | |
| Location locus = tok->get_locus (); | |
| bool has_move = false; | |
| if (tok->get_id () == MOVE) | |
| { | |
| has_move = true; | |
| tok = lexer.peek_token (); | |
| lexer.skip_token (); | |
| // skip token and reassign | |
| } | |
| // handle parameter list | |
| std::vector<AST::ClosureParam> params; | |
| switch (tok->get_id ()) | |
| { | |
| case OR: | |
| // no parameters, don't skip token | |
| break; | |
| case PIPE: { | |
| // actually may have parameters | |
| // don't skip token | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () != PIPE) | |
| { | |
| AST::ClosureParam param = parse_closure_param (); | |
| if (param.is_error ()) | |
| { | |
| // TODO is this really an error? | |
| Error error (t->get_locus (), "could not parse closure param"); | |
| add_error (std::move (error)); | |
| return nullptr; | |
| } | |
| params.push_back (std::move (param)); | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| { | |
| // not an error but means param list is done | |
| break; | |
| } | |
| // skip comma | |
| lexer.skip_token (); | |
| t = lexer.peek_token (); | |
| } | |
| if (!skip_token (PIPE)) | |
| { | |
| return nullptr; | |
| } | |
| break; | |
| } | |
| default: | |
| add_error (Error (tok->get_locus (), | |
| "unexpected token %qs in closure expression - expected " | |
| "%<|%> or %<||%>", | |
| tok->get_token_description ())); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // again branch based on next token | |
| tok = lexer.peek_token (); | |
| if (tok->get_id () == RETURN_TYPE) | |
| { | |
| // must be return type closure with block expr | |
| // skip "return type" token | |
| lexer.skip_token (); | |
| // parse actual type, which is required | |
| std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds (); | |
| if (type == nullptr) | |
| { | |
| // error | |
| Error error (tok->get_locus (), "failed to parse type for closure"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| // parse block expr, which is required | |
| std::unique_ptr<AST::BlockExpr> block = parse_block_expr (); | |
| if (block == nullptr) | |
| { | |
| // error | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "failed to parse block expr in closure"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ClosureExprInnerTyped> ( | |
| new AST::ClosureExprInnerTyped (std::move (type), std::move (block), | |
| std::move (params), locus, has_move, | |
| std::move (outer_attrs))); | |
| } | |
| else | |
| { | |
| // must be expr-only closure | |
| // parse expr, which is required | |
| std::unique_ptr<AST::Expr> expr = parse_expr (); | |
| if (expr == nullptr) | |
| { | |
| Error error (tok->get_locus (), | |
| "failed to parse expression in closure"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::ClosureExprInner> ( | |
| new AST::ClosureExprInner (std::move (expr), std::move (params), locus, | |
| has_move, std::move (outer_attrs))); | |
| } | |
| } | |
| /* Parses a tuple index expression (pratt-parsed) from a 'float' token as a | |
| * result of lexer misidentification. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TupleIndexExpr> | |
| Parser<ManagedTokenSource>::parse_tuple_index_expr_float ( | |
| const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr, | |
| AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED) | |
| { | |
| // only works on float literals | |
| if (tok->get_id () != FLOAT_LITERAL) | |
| return nullptr; | |
| // DEBUG: | |
| rust_debug ("exact string form of float: '%s'", tok->get_str ().c_str ()); | |
| // get float string and remove dot and initial 0 | |
| std::string index_str = tok->get_str (); | |
| index_str.erase (index_str.begin ()); | |
| // get int from string | |
| int index = atoi (index_str.c_str ()); | |
| Location locus = tuple_expr->get_locus (); | |
| return std::unique_ptr<AST::TupleIndexExpr> ( | |
| new AST::TupleIndexExpr (std::move (tuple_expr), index, | |
| std::move (outer_attrs), locus)); | |
| } | |
| // Returns true if the next token is END, ELSE, or EOF; | |
| template <typename ManagedTokenSource> | |
| bool | |
| Parser<ManagedTokenSource>::done_end_or_else () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| return (t->get_id () == RIGHT_CURLY || t->get_id () == ELSE | |
| || t->get_id () == END_OF_FILE); | |
| } | |
| // Returns true if the next token is END or EOF. | |
| template <typename ManagedTokenSource> | |
| bool | |
| Parser<ManagedTokenSource>::done_end () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| return (t->get_id () == RIGHT_CURLY || t->get_id () == END_OF_FILE); | |
| } | |
| // Dumps lexer output to stderr. | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::debug_dump_lex_output (std::ostream &out) | |
| { | |
| /* TODO: a better implementation of "lexer dump" (as in dump what was | |
| * actually tokenised) would actually be to "write" a token to a file every | |
| * time skip_token() here was called. This would reflect the parser | |
| * modifications to the token stream, such as fixing the template angle | |
| * brackets. */ | |
| const_TokenPtr tok = lexer.peek_token (); | |
| while (true) | |
| { | |
| if (tok->get_id () == Rust::END_OF_FILE) | |
| break; | |
| bool has_text = tok->get_id () == Rust::IDENTIFIER | |
| || tok->get_id () == Rust::INT_LITERAL | |
| || tok->get_id () == Rust::FLOAT_LITERAL | |
| || tok->get_id () == Rust::STRING_LITERAL | |
| || tok->get_id () == Rust::CHAR_LITERAL | |
| || tok->get_id () == Rust::BYTE_STRING_LITERAL | |
| || tok->get_id () == Rust::BYTE_CHAR_LITERAL; | |
| Location loc = tok->get_locus (); | |
| out << "<id="; | |
| out << tok->token_id_to_str (); | |
| out << has_text ? (std::string (", text=") + tok->get_str () | |
| + std::string (", typehint=") | |
| + std::string (tok->get_type_hint_str ())) | |
| : ""; | |
| out << lexer.get_line_map ()->to_string (loc); | |
| lexer.skip_token (); | |
| tok = lexer.peek_token (); | |
| } | |
| } | |
| // Parses crate and dumps AST to stderr, recursively. | |
| template <typename ManagedTokenSource> | |
| void | |
| Parser<ManagedTokenSource>::debug_dump_ast_output (AST::Crate &crate, | |
| std::ostream &out) | |
| { | |
| out << crate.as_string (); | |
| } | |
| } // namespace Rust |