aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorSimplyTheOther <simplytheother@gmail.com>2020-09-10 22:22:06 +0800
committerPhilip Herron <philip.herron@embecosm.com>2020-11-28 21:13:21 +0000
commite0db6e13f05fb197621eca65d275094f2502473f (patch)
tree87292b66be33f12c44eafb75b326c9275f27ce7c /gcc
parentdaedbe3476556f0b39f595debeea6979382d296b (diff)
downloadgcc-e0db6e13f05fb197621eca65d275094f2502473f.zip
gcc-e0db6e13f05fb197621eca65d275094f2502473f.tar.gz
gcc-e0db6e13f05fb197621eca65d275094f2502473f.tar.bz2
Converted Parser to template in preparation for macro expansion
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/parse/rust-parse-impl.h13692
-rw-r--r--gcc/rust/parse/rust-parse.cc13574
-rw-r--r--gcc/rust/parse/rust-parse.h667
-rw-r--r--gcc/rust/rust-lang.cc8
-rw-r--r--gcc/rust/rust-linemap.h4
-rw-r--r--gcc/rust/rust-session-manager.cc280
-rw-r--r--gcc/rust/rust-session-manager.h101
7 files changed, 14220 insertions, 14106 deletions
diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h
new file mode 100644
index 0000000..1e30047
--- /dev/null
+++ b/gcc/rust/parse/rust-parse-impl.h
@@ -0,0 +1,13692 @@
+/* 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. */
+
+#include "rust-diagnostics.h"
+
+#include <algorithm> // for std::find
+
+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
+ PREC_CLOSURE = -40, // used for closures
+ PREC_JUMP = -30, // used for break, continue, return, and yield
+ PREC_RANGE = -10, // used for range (although weird comment in rustc about this)
+ PREC_BINOP = FROM_ASSOC_OP,
+ // used for binary operators mentioned below - also cast, colon (type), assign, assign_op
+ PREC_PREFIX = 50, // used for box, address_of, let, unary (again, weird comment on let)
+ PREC_POSTFIX = 60, // used for await, call, method call, field, index, try, inline asm, macro invocation
+ PREC_PAREN = 99, // used for array, repeat, tuple, literal, path, paren, if, while, for, 'loop', match, block, try block, async, struct
+ 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: {
+ /* shit. preferred HACK would be to replace this token in stream with
+ * '>', but may not be possible at this point. */
+ // FIXME: ensure locations aren't messed up
+ TokenPtr right_angle = Token::make (RIGHT_ANGLE, tok->get_locus () + 1);
+ lexer.replace_current_token (right_angle);
+
+ // new implementation that should be better
+#if 0
+ lexer.split_current_token (RIGHT_ANGLE, RIGHT_ANGLE);
+ lexer.skip_token ();
+#endif
+ return true;
+ }
+ case GREATER_OR_EQUAL: {
+ // another HACK - replace with equal (as assignment intended, probably)
+ /* FIXME: is this even required? how many people wouldn't leave a space?
+ * - apparently rustc has this feature */
+ // FIXME: ensure locations aren't messed up
+ TokenPtr equal = Token::make (EQUAL, tok->get_locus () + 1);
+ lexer.replace_current_token (equal);
+ return true;
+
+ // new implementation that should be better
+#if 0
+ lexer.split_current_token (RIGHT_ANGLE, EQUAL);
+ lexer.skip_token ();
+#endif
+ }
+ case RIGHT_SHIFT_EQ: {
+ // another HACK - replace with greater or equal
+ // FIXME: again, is this really required? rustc has the feature, though
+ // FIXME: ensure locations aren't messed up
+ TokenPtr greater_equal
+ = Token::make (GREATER_OR_EQUAL, tok->get_locus () + 1);
+ lexer.replace_current_token (greater_equal);
+ return true;
+
+ // new implementation that should be better
+#if 0
+ lexer.split_current_token (RIGHT_ANGLE, GREATER_OR_EQUAL);
+ lexer.skip_token ();
+#endif
+ }
+ default:
+ rust_error_at (tok->get_locus (),
+ "expected '>' at end of generic argument - found '%s'",
+ 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:
+ fprintf (
+ stderr,
+ "possible error - looked up LBP of scope resolution operator. should "
+ "be handled elsewhere. \n");
+ 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 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
+
+ // 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 crate (compilation unit) - entry point
+template <typename ManagedTokenSource>
+AST::Crate
+Parser<ManagedTokenSource>::parse_crate ()
+{
+ /* TODO: determine if has utf8bom and shebang. Currently, they are eliminated
+ * by the lexing phase. Neither are useful for the compiler anyway, so maybe a
+ * better idea would be to eliminate
+ * the has_utf8bom and has_shebang variables from the crate data structure. */
+ bool has_utf8bom = false;
+ bool has_shebang = false;
+
+ // parse inner attributes
+ std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
+
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse item in crate");
+ items = std::vector<std::unique_ptr<AST::Item> > ();
+ break;
+ }
+
+ items.push_back (std::move (item));
+
+ t = lexer.peek_token ();
+ }
+
+ return AST::Crate (std::move (items), std::move (inner_attrs), has_utf8bom,
+ has_shebang);
+}
+
+// Parse a contiguous block of inner attributes.
+template <typename ManagedTokenSource>
+std::vector<AST::Attribute>
+Parser<ManagedTokenSource>::parse_inner_attributes ()
+{
+ std::vector<AST::Attribute> inner_attributes;
+
+ while (lexer.peek_token ()->get_id () == HASH)
+ {
+ 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;
+ }
+ }
+
+ return inner_attributes;
+}
+
+// Parse a single inner attribute.
+template <typename ManagedTokenSource>
+AST::Attribute
+Parser<ManagedTokenSource>::parse_inner_attribute ()
+{
+ if (lexer.peek_token ()->get_id () != HASH)
+ return AST::Attribute::create_empty ();
+
+ lexer.skip_token ();
+
+ if (lexer.peek_token ()->get_id () != EXCLAM)
+ return AST::Attribute::create_empty ();
+
+ lexer.skip_token ();
+
+ if (lexer.peek_token ()->get_id () != LEFT_SQUARE)
+ 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 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "empty simple path in attribute");
+
+ // 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);
+}
+
+// Parses a SimplePath AST node
+template <typename ManagedTokenSource>
+AST::SimplePath
+Parser<ManagedTokenSource>::parse_simple_path ()
+{
+ bool has_opening_scope_resolution = false;
+ Location locus = Linemap::unknown_location ();
+
+ /* 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 (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 (new_segment);
+ }
+
+ // DEBUG: check for any empty segments
+ for (const auto &seg : segments)
+ {
+ if (seg.is_error ())
+ {
+ fprintf (stderr,
+ "when parsing simple path, somehow empty path segment was "
+ "not filtered out. Path "
+ "begins with '%s' \n",
+ segments.at (0).as_string ().c_str ());
+ }
+ }
+
+ return AST::SimplePath (std::move (segments), has_opening_scope_resolution,
+ locus);
+}
+
+/* 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 (std::string ("super"), t->get_locus ());
+ case SELF:
+ lexer.skip_token ();
+
+ return AST::SimplePathSegment (std::string ("self"), t->get_locus ());
+ case CRATE:
+ lexer.skip_token ();
+
+ return AST::SimplePathSegment (std::string ("crate"), t->get_locus ());
+ case DOLLAR_SIGN:
+ if (lexer.peek_token (1)->get_id () == CRATE)
+ {
+ lexer.skip_token (1);
+
+ return AST::SimplePathSegment (std::string ("$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 ());
+ case SUPER:
+ lexer.skip_token ();
+
+ return AST::PathIdentSegment (std::string ("super"));
+ case SELF:
+ lexer.skip_token ();
+
+ return AST::PathIdentSegment (std::string ("self"));
+ case SELF_ALIAS:
+ lexer.skip_token ();
+
+ return AST::PathIdentSegment (std::string ("Self"));
+ case CRATE:
+ lexer.skip_token ();
+
+ return AST::PathIdentSegment (std::string ("crate"));
+ case DOLLAR_SIGN:
+ if (lexer.peek_token (1)->get_id () == CRATE)
+ {
+ lexer.skip_token (1);
+
+ return AST::PathIdentSegment (std::string ("$crate"));
+ }
+ 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::DelimTokenTree> 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 ())
+ {
+ rust_error_at (
+ t->get_locus (),
+ "unknown token '%s' in attribute body - literal expected",
+ t->get_token_description ());
+ 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_locus ());
+
+ std::unique_ptr<AST::AttrInputLiteral> 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:
+ rust_error_at (t->get_locus (),
+ "unknown token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - 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;
+
+ // repeat loop until finding the matching delimiter
+ t = lexer.peek_token ();
+ while (!token_id_matches_delims (t->get_id (), delim_type))
+ {
+ std::unique_ptr<AST::TokenTree> tok_tree = parse_token_tree ();
+
+ if (tok_tree == nullptr)
+ {
+ // TODO: is this error handling appropriate?
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse token tree in delimited token tree - found '%s'",
+ t->get_token_description ());
+ return AST::DelimTokenTree::create_empty ();
+ }
+
+ token_trees_in_tree.push_back (std::move (tok_tree));
+
+ // lexer.skip_token();
+ t = lexer.peek_token ();
+ }
+
+ 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
+ fprintf (stderr,
+ "finished parsing new delim token tree - peeked token is now "
+ "'%s' while t is '%s'\n",
+ lexer.peek_token ()->get_token_description (),
+ t->get_token_description ());
+
+ return token_tree;
+ }
+ else
+ {
+ // tokens don't match opening delimiters, so produce error
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - expecting closing delimiter '%s' "
+ "(for a delimited token tree)",
+ t->get_token_description (),
+ (delim_type == AST::PARENS
+ ? ")"
+ : (delim_type == AST::SQUARE ? "]" : "}")));
+
+ /* 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
+ rust_error_at (t->get_locus (),
+ "unexpected closing delimiter '%s' - 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 ();
+ // TODO: fix that token constructor, possibly with c++11 features
+ return std::unique_ptr<AST::Token> (new AST::Token (t));
+ }
+}
+
+/* Parses a sequence of items within a module or the implicit top-level module
+ * in a crate. Note: this is not currently used as parsing an item sequence
+ * individually is pretty simple and allows for better error diagnostics and
+ * detection. */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::Item> >
+Parser<ManagedTokenSource>::parse_items ()
+{
+ std::vector<std::unique_ptr<AST::Item> > items;
+
+ // TODO: replace with do-while loop?
+ // infinite loop to save on comparisons (may be a tight loop) - breaks when
+ // next item is null
+ while (true)
+ {
+ std::unique_ptr<AST::Item> item = parse_item (false);
+
+ if (item != nullptr)
+ {
+ items.push_back (std::move (item));
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return items;
+}
+
+// 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
+ std::vector<AST::Attribute> 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
+ 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")
+ {
+ 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 ();
+ // TODO: find out how to disable gcc "implicit fallthrough" warning
+ default:
+ // otherwise unrecognised
+ // return parse_macro_item(std::move(outer_attrs));
+ rust_error_at (t->get_locus (), "unrecognised token '%s' 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>
+std::vector<AST::Attribute>
+Parser<ManagedTokenSource>::parse_outer_attributes ()
+{
+ std::vector<AST::Attribute> outer_attributes;
+
+ while (lexer.peek_token ()->get_id () == HASH)
+ {
+ 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;
+ }
+ }
+
+ 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 ()
+{
+ /* 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
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "token '!' found, indicating inner attribute "
+ "definition. Inner attributes are not "
+ "possible at this location.");
+ } // TODO: are there any cases where this wouldn't be an 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 (
+ std::vector<AST::Attribute> 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:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' in some sort of extern production",
+ t->get_token_description ());
+ lexer.skip_token (2); // TODO: is this right thing to do?
+ return nullptr;
+ }
+ default:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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:
+ // TODO: ensure std::string and literal comparison works
+ if (t->get_str () == "union")
+ {
+ 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:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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 (
+ std::vector<AST::Attribute> 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 () == std::string ("macro_rules"))
+ {
+ return parse_macro_rules_def (std::move (outer_attrs));
+ }
+ else
+ {
+ // DEBUG: TODO: remove
+ fprintf (stderr,
+ "DEBUG - parse_macro_item called and token is not macro_rules");
+ if (t->get_id () == IDENTIFIER)
+ {
+ fprintf (stderr,
+ "just add to last error: token is not macro_rules and is "
+ "instead '%s'",
+ t->get_str ().c_str ());
+ }
+ else
+ {
+ fprintf (stderr,
+ "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 (
+ std::vector<AST::Attribute> 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")
+ {
+ rust_error_at (
+ t->get_locus (),
+ "macro rules definition does not start with 'macro_rules'");
+ // 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
+ fprintf (stderr, "in macro rules def, about to parse parens.\n");
+
+ // 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "required first macro rule in macro rules definition "
+ "could not be parsed");
+ // skip after somewhere?
+ return nullptr;
+ }
+ macro_rules.push_back (std::move (initial_rule));
+
+ // DEBUG
+ fprintf (stderr, "successfully pushed back initial macro rule\n");
+
+ 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
+ fprintf (
+ stderr,
+ "broke out of parsing macro rules loop due to finding delim\n");
+
+ break;
+ }
+
+ // try to parse next rule
+ AST::MacroRule rule = parse_macro_rule ();
+ if (rule.is_error ())
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse macro rule in macro rules definition");
+ return nullptr;
+ }
+
+ macro_rules.push_back (std::move (rule));
+
+ // DEBUG
+ fprintf (stderr, "successfully pushed back another macro rule\n");
+
+ 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
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - expecting closing delimiter '%s' "
+ "(for a macro rules "
+ "definition)",
+ t->get_token_description (),
+ (delim_type == AST::PARENS
+ ? ")"
+ : (delim_type == AST::SQUARE ? "]" : "}")));
+
+ /* 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::MacroInvocationSemi>
+Parser<ManagedTokenSource>::parse_macro_invocation_semi (
+ std::vector<AST::Attribute> 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - expecting delimiters (for a "
+ "macro invocation semi body)",
+ t->get_token_description ());
+ return nullptr;
+ }
+ lexer.skip_token ();
+
+ // parse actual token trees
+ std::vector<std::unique_ptr<AST::TokenTree> > token_trees;
+
+ 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)
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse token tree for macro invocation semi - found '%s'",
+ t->get_token_description ());
+ return nullptr;
+ }
+
+ token_trees.push_back (std::move (tree));
+
+ 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 invocation semis
+ if (!skip_token (SEMICOLON))
+ {
+ // as this is the end, allow recovery (probably) - may change
+ return std::unique_ptr<AST::MacroInvocationSemi> (
+ new AST::MacroInvocationSemi (std::move (path), delim_type,
+ std::move (token_trees),
+ std::move (outer_attrs),
+ macro_locus));
+ }
+ }
+
+ // DEBUG:
+ fprintf (stderr,
+ "skipped token is '%s', next token (current peek) is '%s'\n",
+ t->get_token_description (),
+ lexer.peek_token ()->get_token_description ());
+
+ return std::unique_ptr<AST::MacroInvocationSemi> (
+ new AST::MacroInvocationSemi (std::move (path), delim_type,
+ std::move (token_trees),
+ std::move (outer_attrs), macro_locus));
+ }
+ else
+ {
+ // tokens don't match opening delimiters, so produce error
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - expecting closing delimiter '%s' "
+ "(for a macro invocation "
+ "semi)",
+ t->get_token_description (),
+ (delim_type == AST::PARENS
+ ? ")"
+ : (delim_type == AST::SQUARE ? "]" : "}")));
+
+ /* 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ // parse macro path
+ AST::SimplePath macro_path = parse_simple_path ();
+ if (macro_path.is_empty ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse macro invocation path");
+ // 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 (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 ()
+{
+ // parse macro matcher
+ AST::MacroMatcher matcher = parse_macro_matcher ();
+
+ if (matcher.is_error ())
+ {
+ return AST::MacroRule::create_error ();
+ }
+
+ if (!skip_token (MATCH_ARROW))
+ {
+ // skip after somewhere?
+ return AST::MacroRule::create_error ();
+ }
+
+ // parse transcriber (this is just a delim token tree)
+ AST::MacroTranscriber transcriber (parse_delim_token_tree ());
+
+ return AST::MacroRule (std::move (matcher), std::move (transcriber));
+}
+
+// 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
+ fprintf (stderr, "begun parsing macro matcher\n");
+
+ // 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:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' - expecting delimiters (for a macro matcher)",
+ t->get_token_description ());
+ return AST::MacroMatcher::create_error ();
+ }
+ lexer.skip_token ();
+
+ // parse actual macro matches
+ std::vector<std::unique_ptr<AST::MacroMatch> > 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)
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse macro match for macro matcher - found '%s'",
+ t->get_token_description ());
+ return AST::MacroMatcher::create_error ();
+ }
+
+ matches.push_back (std::move (match));
+
+ // DEBUG
+ fprintf (stderr, "pushed back a match in macro matcher\n");
+
+ 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));
+ }
+ else
+ {
+ // tokens don't match opening delimiters, so produce error
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' - expecting closing delimiter '%s' "
+ "(for a macro matcher)",
+ t->get_token_description (),
+ (delim_type == AST::PARENS
+ ? ")"
+ : (delim_type == AST::SQUARE ? "]" : "}")));
+
+ /* return error macro matcher despite possibly parsing mostly correct one?
+ * TODO is this the best idea? */
+ return AST::MacroMatcher::create_error ();
+ }
+}
+
+// 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse macro matcher in macro match");
+ 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 IDENTIFIER:
+ // macro fragment
+ return parse_macro_match_fragment ();
+ case LEFT_PAREN:
+ // macro repetition
+ return parse_macro_match_repetition ();
+ default:
+ // error: unrecognised
+ rust_error_at (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
+ rust_error_at (t->get_locus (),
+ "closing delimiters like '%s' 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 (t));
+ }
+}
+
+// Parses a fragment macro match.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroMatchFragment>
+Parser<ManagedTokenSource>::parse_macro_match_fragment ()
+{
+ skip_token (DOLLAR_SIGN);
+
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "missing identifier in macro match fragment");
+ return nullptr;
+ }
+ Identifier ident = ident_tok->get_str ();
+
+ if (!skip_token (COLON))
+ {
+ // skip after somewhere?
+ return nullptr;
+ }
+
+ // get MacroFragSpec for macro
+ const_TokenPtr t = expect_token (IDENTIFIER);
+ AST::MacroFragSpec frag = AST::get_frag_spec_from_str (t->get_str ());
+ if (frag == AST::INVALID)
+ {
+ rust_error_at (t->get_locus (),
+ "invalid fragment specifier '%s' in fragment macro match",
+ t->get_str ().c_str ());
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::MacroMatchFragment> (
+ new AST::MacroMatchFragment (std::move (ident), frag));
+}
+
+// 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "could not parse required first macro match in macro match repetition");
+ // 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse macro match in macro match repetition");
+ 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 (t));
+ lexer.skip_token ();
+ break;
+ }
+
+ // parse repetition operator
+ t = lexer.peek_token ();
+ AST::MacroMatchRepetition::MacroRepOp op
+ = AST::MacroMatchRepetition::ASTERISK;
+ switch (t->get_id ())
+ {
+ case ASTERISK:
+ op = AST::MacroMatchRepetition::ASTERISK;
+ lexer.skip_token ();
+ break;
+ case PLUS:
+ op = AST::MacroMatchRepetition::PLUS;
+ lexer.skip_token ();
+ break;
+ case QUESTION_MARK:
+ op = AST::MacroMatchRepetition::QUESTION_MARK;
+ lexer.skip_token ();
+ break;
+ default:
+ rust_error_at (t->get_locus (),
+ "expected macro repetition operator ('*', '+', or '?') in "
+ "macro match - found '%s'",
+ 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)));
+}
+
+/* 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_error ();
+ }
+
+ 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 ();
+
+ switch (t->get_id ())
+ {
+ case CRATE:
+ lexer.skip_token ();
+
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_crate ();
+ case SELF:
+ lexer.skip_token ();
+
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_self ();
+ case SUPER:
+ lexer.skip_token ();
+
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_super ();
+ case IN: {
+ lexer.skip_token ();
+
+ // parse the "in" path as well
+ AST::SimplePath path = parse_simple_path ();
+ if (path.is_empty ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "missing path in pub(in path) visibility");
+ // skip after somewhere?
+ return AST::Visibility::create_error ();
+ }
+
+ skip_token (RIGHT_PAREN);
+
+ return AST::Visibility::create_in_path (std::move (path));
+ }
+ default:
+ rust_error_at (t->get_locus (), "unexpected token '%s' 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, std::vector<AST::Attribute> 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 ();
+
+ return std::unique_ptr<AST::ModuleNoBody> (
+ new AST::ModuleNoBody (std::move (name), std::move (vis),
+ std::move (outer_attrs),
+ locus)); // module name?
+ case LEFT_CURLY: {
+ lexer.skip_token ();
+
+ // parse inner attributes
+ std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
+
+ // 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)
+ {
+ rust_error_at (tok->get_locus (),
+ "failed to parse item in module");
+ 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::ModuleBodied> (
+ new AST::ModuleBodied (std::move (name), locus, std::move (items),
+ std::move (vis), std::move (inner_attrs),
+ std::move (outer_attrs))); // module name?
+ }
+ default:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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, std::vector<AST::Attribute> 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 = std::string ("self");
+ lexer.skip_token ();
+ break;
+ default:
+ rust_error_at (crate_name_tok->get_locus (),
+ "expecting crate name (identifier or 'self'), found '%s'",
+ 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 = std::string ("_");
+ lexer.skip_token ();
+ break;
+ default:
+ rust_error_at (as_name_tok->get_locus (),
+ "expecting as clause name (identifier or '_'), found '%s'",
+ 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, std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse use tree in use declaration");
+ 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
+
+ /* due to implementation issues, parsing simple path removes any trailing
+ * scope resolutions (or any, actually, if the use tree has no path
+ * given), so we'll just assume that there's one there. */
+ // Check anyway, but optional.
+ if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+ {
+ lexer.skip_token ();
+ }
+ /* Note that this implementation issue also makes it impossible to
+ * determine at the moment whether the tree has GLOBAL or NO_PATH path
+ * type. */
+
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case ASTERISK:
+ // glob UseTree type
+ lexer.skip_token ();
+
+ // TODO: find way to determine whether GLOBAL or NO_PATH path type -
+ // placeholder
+ 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;
+ }
+
+ // TODO: find way to determine whether GLOBAL or NO_PATH path type -
+ // placeholder
+ 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
+ rust_error_at (t->get_locus (),
+ "use declaration with rebind 'as' requires a valid "
+ "simple path - none found.");
+ skip_after_semicolon ();
+ return nullptr;
+ default:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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. */
+
+ 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,
+ std::string ("_")));
+ default:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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, std::vector<AST::Attribute> 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))
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "function declaration missing opening parentheses before "
+ "parameter list");
+ 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 ();
+ }
+
+ if (!skip_token (RIGHT_PAREN))
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "function declaration missing closing parentheses after "
+ "parameter list");
+ 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 ()
+{
+ AST::FunctionQualifiers::AsyncConstStatus const_status
+ = AST::FunctionQualifiers::NONE;
+ // bool has_const = false;
+ 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 ();
+ switch (t->get_id ())
+ {
+ case CONST:
+ lexer.skip_token ();
+ const_status = AST::FunctionQualifiers::CONST;
+ break;
+ case ASYNC:
+ lexer.skip_token ();
+ const_status = AST::FunctionQualifiers::ASYNC;
+ 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 (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:
+ fprintf (stderr, "skipped left angle in generic param\n");
+
+ std::vector<std::unique_ptr<AST::GenericParam> > generic_params
+ = parse_generic_params ();
+
+ // DEBUG:
+ fprintf (stderr,
+ "finished parsing actual generic params (i.e. inside angles)\n");
+
+ if (!skip_generics_right_angle ())
+ {
+ // DEBUG
+ fprintf (stderr, "failed to skip generics right angle - returning empty "
+ "generic params\n");
+
+ return std::vector<std::unique_ptr<AST::GenericParam> > ();
+ }
+
+ return generic_params;
+}
+
+/* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost
+ * always parse_generic_params_in_angles is what is wanted. */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::GenericParam> >
+Parser<ManagedTokenSource>::parse_generic_params ()
+{
+ 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
+
+ // DEBUG
+ fprintf (stderr,
+ "starting to parse generic params (inside angle brackets)\n");
+
+ // 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 ();
+
+ // HACK: generic params always in angle brackets with current syntax, so have
+ // that as end char
+ const_TokenPtr t = lexer.peek_token ();
+ // parse lifetime params
+ while (!is_right_angle_tok (t->get_id ()))
+ {
+ // HACK: reimpl of lifetime param parsing
+ AST::Attribute outer_attr = parse_outer_attribute ();
+
+ // move attribute outward if type param
+ if (lexer.peek_token ()->get_id () != LIFETIME)
+ {
+ parsed_outer_attr = std::move (outer_attr);
+
+ // DEBUG
+ fprintf (
+ stderr,
+ "broke from parsing lifetime params as next token isn't lifetime - "
+ "saved attribute\n");
+
+ break;
+ }
+
+ Location locus = lexer.peek_token ()->get_locus ();
+ AST::Lifetime lifetime = parse_lifetime ();
+
+ // DEBUG
+ fprintf (stderr, "parsed lifetime in lifetime params\n");
+
+ // parse optional bounds
+ std::vector<AST::Lifetime> lifetime_bounds;
+ if (lexer.peek_token ()->get_id () == COLON)
+ {
+ lexer.skip_token ();
+ // parse required bounds
+ lifetime_bounds = parse_lifetime_bounds ();
+ }
+
+ std::unique_ptr<AST::LifetimeParam> param (
+ new AST::LifetimeParam (std::move (lifetime), locus,
+ std::move (lifetime_bounds),
+ std::move (outer_attr)));
+ generic_params.push_back (std::move (param));
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ lexer.skip_token ();
+
+ t = lexer.peek_token ();
+ }
+
+ // parse type params (reimpl required for first one but not others)
+ if (!is_right_angle_tok (lexer.peek_token ()->get_id ())
+ && !parsed_outer_attr.is_empty ())
+ {
+ // DEBUG
+ fprintf (stderr, "as parsed outer attr isn't empty, started parsing type "
+ "param reimpl\n");
+
+ // reimpl as type param definitely exists
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse identifier in type param in generic params");
+ return std::vector<std::unique_ptr<AST::GenericParam> > ();
+ }
+ Identifier ident = ident_tok->get_str ();
+
+ // parse optional bounds
+ 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 ();
+ }
+
+ // parse optional type
+ 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 == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse type in type param in generic params");
+ return std::vector<std::unique_ptr<AST::GenericParam> > ();
+ }
+ }
+
+ std::unique_ptr<AST::TypeParam> param (
+ new AST::TypeParam (std::move (ident), ident_tok->get_locus (),
+ std::move (type_param_bounds), std::move (type),
+ std::move (parsed_outer_attr)));
+ generic_params.push_back (std::move (param));
+
+ // handle comma
+ if (lexer.peek_token ()->get_id () == COMMA)
+ {
+ lexer.skip_token ();
+ }
+ }
+
+ // DEBUG
+ fprintf (
+ stderr,
+ "about to start parsing normally-parsed type params in generic params\n");
+
+ // parse rest of type params - reimpl due to right angle tokens
+ t = lexer.peek_token ();
+ while (!is_right_angle_tok (t->get_id ()))
+ {
+ std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
+
+ if (type_param == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse type param in generic params");
+ return std::vector<std::unique_ptr<AST::GenericParam> > ();
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed type param\n");
+
+ generic_params.push_back (std::move (type_param));
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ // skip commas, including trailing commas
+ lexer.skip_token ();
+
+ t = lexer.peek_token ();
+ }
+
+ // old code
+ /*
+ // parse lifetime params (optional), allowed to end with a trailing comma
+ std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params
+ = parse_lifetime_params();
+ if (!lifetime_params.empty()) {
+ // C++11 code:
+ generic_params.insert(generic_params.end(),
+ std::make_move_iterator(lifetime_params.begin()),
+ std::make_move_iterator(lifetime_params.end()));
+ }
+
+ // parse type params (optional)
+ std::vector<std::unique_ptr<AST::TypeParam>> type_params =
+ parse_type_params(); if (!type_params.empty()) {
+ // C++11 code:
+ generic_params.insert(generic_params.end(),
+ std::make_move_iterator(type_params.begin()),
+ std::make_move_iterator(type_params.end()));
+ }*/
+
+ return generic_params;
+}
+
+/* Parses lifetime generic parameters (pointers). Will also consume any trailing
+ * comma. */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::LifetimeParam> >
+Parser<ManagedTokenSource>::parse_lifetime_params ()
+{
+ std::vector<std::unique_ptr<AST::LifetimeParam> > lifetime_params;
+
+ // TODO: think of better control structure than infinite loop with break on
+ // failure?
+ while (true)
+ {
+ AST::LifetimeParam lifetime_param = parse_lifetime_param ();
+
+ if (lifetime_param.is_error ())
+ {
+ // break if fails to parse
+ break;
+ }
+
+ lifetime_params.push_back (std::unique_ptr<AST::LifetimeParam> (
+ new AST::LifetimeParam (lifetime_param)));
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ // skip commas, including trailing commas
+ lexer.skip_token ();
+ }
+
+ return lifetime_params;
+}
+
+/* Parses lifetime generic parameters (objects). Will also consume any trailing
+ * comma.
+ * 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;
+
+ // TODO: think of better control structure than infinite loop with break on
+ // failure?
+ while (true)
+ {
+ AST::LifetimeParam lifetime_param = parse_lifetime_param ();
+
+ if (lifetime_param.is_error ())
+ {
+ // break if fails to parse
+ break;
+ }
+
+ lifetime_params.push_back (lifetime_param);
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ // skip commas, including trailing commas
+ lexer.skip_token ();
+ }
+
+ return lifetime_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 ();
+ }
+ // 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 ();
+ }
+
+ return AST::LifetimeParam (std::move (lifetime), lifetime_tok->get_locus (),
+ std::move (lifetime_bounds),
+ std::move (outer_attr));
+}
+
+// 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;
+
+ // TODO: think of better control structure than infinite loop with break on
+ // failure?
+ 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 ();
+ }
+
+ return type_params;
+ // TODO: this shares most code with parse_lifetime_params - good place to use
+ // template?
+}
+
+/* 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse type in type param");
+ 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.
+template <typename ManagedTokenSource>
+std::vector<AST::FunctionParam>
+Parser<ManagedTokenSource>::parse_function_params ()
+{
+ std::vector<AST::FunctionParam> params;
+
+ // HACK: return early if RIGHT_PAREN is found
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ {
+ return params;
+ }
+
+ AST::FunctionParam initial_param = parse_function_param ();
+
+ // Return empty parameter list if no parameter there
+ if (initial_param.is_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 ();
+
+ /* HACK: break if next token is a right (closing) paren - this is not
+ * strictly true via grammar rule but seems to be true in practice (i.e.
+ * with trailing comma). */
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ {
+ break;
+ }
+
+ // now, as right paren would break, function param is required
+ AST::FunctionParam param = parse_function_param ();
+ if (param.is_error ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse function param (in function params)");
+ // skip somewhere?
+ return std::vector<AST::FunctionParam> ();
+ }
+
+ params.push_back (std::move (param));
+
+ t = lexer.peek_token ();
+ }
+
+ 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 ()
+{
+ 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),
+ 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)
+ {
+ rust_error_at (t->get_locus (), "failed to parse where clause item");
+ 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 ();
+ }
+
+ 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 ();
+
+ return std::unique_ptr<AST::LifetimeWhereClauseItem> (
+ new AST::LifetimeWhereClauseItem (std::move (lifetime),
+ std::move (lifetime_bounds)));
+}
+
+// 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 ();
+
+ return std::unique_ptr<AST::TypeBoundWhereClauseItem> (
+ new AST::TypeBoundWhereClauseItem (std::move (for_lifetimes),
+ std::move (type),
+ std::move (type_param_bounds)));
+}
+
+// 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;
+ }
+
+ params = parse_lifetime_params_objs ();
+
+ if (!skip_generics_right_angle ())
+ {
+ // 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));
+
+ // TODO think of better control structure than infinite loop
+ while (true)
+ {
+ // Quick exit for no more bounds
+ if (lexer.peek_token ()->get_id () != PLUS)
+ {
+ return type_param_bounds;
+ }
+ 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));
+ }
+
+ 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;
+
+ // TODO: think of better control structure
+ while (true)
+ {
+ AST::Lifetime lifetime = parse_lifetime ();
+
+ // quick exit for parsing failure
+ if (lifetime.is_error ())
+ {
+ return lifetime_bounds;
+ }
+
+ lifetime_bounds.push_back (std::move (lifetime));
+
+ // plus is maybe required - spec defines it poorly, so assuming not
+ // required
+ if (lexer.peek_token ()->get_id () != PLUS)
+ {
+ return lifetime_bounds;
+ }
+
+ lexer.skip_token ();
+ }
+
+ 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, std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse identifier in type alias");
+ 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, std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse struct or tuple struct identifier");
+ // 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 = 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 ();
+
+ 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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));
+
+ // 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
+ while (true)
+ {
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+
+ // skip comma if applies
+ lexer.skip_token ();
+
+ AST::StructField field = parse_struct_field ();
+
+ if (!field.is_error ())
+ {
+ fields.push_back (std::move (field));
+ }
+ else
+ {
+ // this would occur with a trailing comma, which is allowed
+ break;
+ }
+ }
+
+ return fields;
+
+ // TODO: this shares basically all code with function params and tuple fields
+ // - templates?
+}
+
+// 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
+ std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
+
+ // parse visibility, if it exists
+ AST::Visibility vis = parse_visibility ();
+
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse type in struct field definition");
+ // skip after somewhere
+ return AST::StructField::create_error ();
+ }
+
+ return AST::StructField (std::move (field_name), std::move (field_type),
+ std::move (vis), 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse tuple field in tuple fields");
+ return std::vector<AST::TupleField> ();
+ }
+
+ fields.push_back (std::move (field));
+
+ t = lexer.peek_token ();
+ }
+
+ 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
+ std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
+
+ // parse visibility if it exists
+ AST::Visibility vis = parse_visibility ();
+
+ // parse type, which is required
+ std::unique_ptr<AST::Type> field_type = parse_type ();
+ if (field_type == nullptr)
+ {
+ // error if null
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse type in tuple struct field");
+ // skip after something
+ return AST::TupleField::create_error ();
+ }
+
+ return AST::TupleField (std::move (field_type), std::move (vis),
+ 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,
+ std::vector<AST::Attribute> outer_attrs)
+{
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (ENUM_TOK);
+
+ // parse enum name
+ const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
+ 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 ();
+
+ 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));
+
+ // maybe think of a better control structure here - do-while with an initial
+ // error state? basically, loop through item list until can't find any more
+ // params
+ while (true)
+ {
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+
+ // skip comma if applies
+ lexer.skip_token ();
+
+ std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
+
+ if (item != nullptr)
+ {
+ items.push_back (std::move (item));
+ }
+ else
+ {
+ // this would occur with a trailing comma, which is allowed
+ break;
+ }
+ }
+
+ // TODO: does this need move?
+ return items;
+
+ // TODO: shares virtually all code with function params, tuple and struct
+ // fields - templates?
+}
+
+// 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
+ std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
+
+ // 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 = 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 (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 ();
+
+ 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 (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 (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 (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, std::vector<AST::Attribute> outer_attrs)
+{
+ // hack - "weak keyword" by finding identifier called "union" (lookahead in
+ // item switch)
+ Location locus = lexer.peek_token ()->get_locus ();
+ // skip union "identifier"
+ skip_token (IDENTIFIER);
+
+ // 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 ();
+
+ 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, std::vector<AST::Attribute> 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:
+ rust_error_at (ident_tok->get_locus (),
+ "expected item name (identifier or '_') in constant item "
+ "declaration - found '%s'",
+ 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, std::vector<AST::Attribute> 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);
+ 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, std::vector<AST::Attribute> 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);
+ 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 ();
+
+ // TODO: does this need move?
+ 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 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)
+ {
+ // TODO: this is probably an error as next character should equal
+ // RIGHT_CURLY
+ break;
+ }
+
+ trait_items.push_back (std::move (trait_item));
+
+ t = lexer.peek_token ();
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip after something
+ return nullptr;
+ }
+
+ 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), 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)
+ std::vector<AST::Attribute> 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);
+ 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 ();
+
+ 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:
+ rust_error_at (t->get_locus (),
+ "expected ';' or definiton at the end of trait %s "
+ "definition - found '%s' "
+ "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::MacroInvocationSemi> 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (TYPE);
+
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ Identifier ident = ident_tok->get_str ();
+
+ bool has_colon = false;
+ std::vector<std::unique_ptr<AST::TypeParamBound> > bounds;
+
+ // parse optional colon
+ if (lexer.peek_token ()->get_id () == COLON)
+ {
+ has_colon = true;
+ lexer.skip_token ();
+
+ // parse optional type param bounds
+ 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (CONST);
+
+ // parse constant item name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ 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,
+ std::vector<AST::Attribute> 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 ())
+ {
+ // TODO: would move work here?
+ type = std::unique_ptr<AST::TypePath> (new AST::TypePath (type_path));
+ }
+ else
+ {
+ type = parse_type ();
+ }
+ // Type is required, so error if null
+ if (type == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse type in inherent impl");
+ 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)
+ std::vector<AST::Attribute> 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)
+ {
+ // TODO: this is probably an error as next character should equal
+ // RIGHT_CURLY
+ fprintf (stderr, "impl item is null and next char wasn't "
+ "RIGHT_CURLY - probably an error");
+ break;
+ }
+
+ impl_items.push_back (std::move (impl_item));
+
+ t = lexer.peek_token ();
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere
+ return nullptr;
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed inherent impl\n");
+
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse type in trait impl");
+ 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)
+ std::vector<AST::Attribute> 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)
+ {
+ // DEBUG
+ fprintf (stderr, "break out of parsing trait impl items (due to "
+ "parse giving null)\n");
+
+ // TODO: this is probably an error as next character should equal
+ // RIGHT_CURLY
+ break;
+ }
+
+ impl_items.push_back (std::move (impl_item));
+
+ t = lexer.peek_token ();
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed a trait impl item\n");
+ }
+ // DEBUG
+ fprintf (stderr, "successfully finished trait impl items\n");
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere
+ return nullptr;
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed trait impl\n");
+
+ 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)
+ std::vector<AST::Attribute> 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:
+ 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 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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_error (), 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_error (),
+ std::move (outer_attrs));
+ case UNSAFE:
+ case EXTERN_TOK:
+ case FN_TOK:
+ return parse_inherent_impl_function_or_method (
+ AST::Visibility::create_error (), std::move (outer_attrs));
+ default:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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, std::vector<AST::Attribute> 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);
+ 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 ();
+
+ 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "%s declaration in inherent impl not allowed - must have a definition",
+ is_method ? "method" : "function");
+ lexer.skip_token ();
+ return nullptr;
+ }
+ std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+ if (body == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "could not parse definition in inherent impl %s definition",
+ is_method ? "method" : "function");
+ 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)
+ std::vector<AST::Attribute> 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_error (),
+ 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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_error (), 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_error (),
+ std::move (outer_attrs));
+ case UNSAFE:
+ case EXTERN_TOK:
+ case FN_TOK:
+ return parse_trait_impl_function_or_method (
+ AST::Visibility::create_error (), std::move (outer_attrs));
+ default:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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, std::vector<AST::Attribute> 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:
+ fprintf (
+ stderr,
+ "about to start parsing generic params in trait impl function or method\n");
+
+ // parse generic params
+ std::vector<std::unique_ptr<AST::GenericParam> > generic_params
+ = parse_generic_params_in_angles ();
+
+ // DEBUG:
+ fprintf (
+ stderr,
+ "finished parsing generic params in trait impl function or method\n");
+
+ 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
+ fprintf (stderr,
+ "successfully parsed self param in method trait impl item\n");
+ }
+
+ // DEBUG
+ fprintf (
+ stderr,
+ "started to parse function params in function or method trait impl item\n");
+
+ // 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 ();
+
+ if (function_params.empty ())
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse function params in trait impl %s definition",
+ is_method ? "method" : "function");
+ skip_after_next_block ();
+ return nullptr;
+ }
+ }
+
+ // FIXME: segfault occurs during parsing of function params
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed function params in function or method "
+ "trait impl item\n");
+
+ 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
+ fprintf (
+ stderr,
+ "successfully parsed return type in function or method trait impl item\n");
+
+ // parse where clause (optional)
+ AST::WhereClause where_clause = parse_where_clause ();
+
+ // DEBUG
+ fprintf (
+ stderr,
+ "successfully parsed where clause in function or method trait impl item\n");
+
+ // parse function definition (in block) - semicolon not allowed
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "%s declaration in trait impl not allowed - must have a definition",
+ is_method ? "method" : "function");
+ lexer.skip_token ();
+ return nullptr;
+ }
+ std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+ if (body == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse definition in trait impl %s definition",
+ is_method ? "method" : "function");
+ 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, std::vector<AST::Attribute> 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;
+ }
+
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse external item despite not reaching "
+ "end of extern block");
+ return nullptr;
+ }
+
+ extern_items.push_back (std::move (extern_item));
+
+ t = lexer.peek_token ();
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere
+ return nullptr;
+ }
+
+ 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
+ std::vector<AST::Attribute> 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 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse type in external static item");
+ 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;
+
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_PAREN)
+ {
+ AST::NamedFunctionParam param = parse_named_function_param ();
+
+ if (param.is_error ())
+ {
+ // is this an error? probably
+ rust_error_at (t->get_locus (),
+ "could not parse named function parameter in "
+ "external function");
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ function_params.push_back (std::move (param));
+
+ t = lexer.peek_token ();
+ if (t->get_id () != COMMA)
+ {
+ if (t->get_id () != RIGHT_PAREN)
+ {
+ rust_error_at (t->get_locus (),
+ "expected comma or right parentheses in "
+ "named function parameters, "
+ "found '%s'",
+ t->get_token_description ());
+ }
+ else
+ {
+ // end of loop
+ break;
+ }
+ }
+ // skip comma
+ lexer.skip_token ();
+
+ t = lexer.peek_token ();
+
+ // parse variadic ... if it exists
+ if (t->get_id () == ELLIPSIS
+ && lexer.peek_token (1)->get_id () == RIGHT_PAREN)
+ {
+ lexer.skip_token ();
+
+ is_variadic = true;
+
+ 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;
+ }
+
+ 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 (vis),
+ std::move (outer_attrs), locus));
+ }
+ default:
+ // error
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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 ()
+{
+ // parse identifier/_
+ Identifier name;
+
+ const_TokenPtr t = lexer.peek_token ();
+ 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "could not parse param type in extern block function declaration");
+ skip_after_semicolon ();
+ return AST::NamedFunctionParam::create_error ();
+ }
+
+ return AST::NamedFunctionParam (std::move (name), std::move (param_type));
+}
+
+// Parses a statement (will further disambiguate any statement).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Stmt>
+Parser<ManagedTokenSource>::parse_stmt ()
+{
+ // quick exit for empty statement
+ 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
+ std::vector<AST::Attribute> 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));
+ 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:
+ // TODO: ensure std::string and literal comparison works
+ if (t->get_str () == "union")
+ {
+ 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));
+ break;
+ }
+}
+
+// Parses a let statement.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LetStmt>
+Parser<ManagedTokenSource>::parse_let_stmt (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (LET);
+
+ // parse pattern (required)
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in let statement");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse type in let statement");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse expression in let statement");
+ skip_after_semicolon ();
+ return nullptr;
+ }
+ }
+
+ if (!skip_token (SEMICOLON))
+ {
+ // skip after somewhere
+ return nullptr;
+ /* TODO: how wise is it to ditch a mostly-valid let statement just because
+ * a semicolon is missing? */
+ }
+
+ 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;
+ 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?
+ rust_error_at (t->get_locus (), "could not parse type path segment");
+ return AST::TypePath::create_error ();
+ }
+
+ segments.push_back (std::move (segment));
+
+ t = lexer.peek_token ();
+ }
+
+ return AST::TypePath (std::move (segments), Linemap::unknown_location (),
+ has_opening_scope_resolution);
+}
+
+// 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 ();
+ }
+
+ // try to parse lifetimes first
+ std::vector<AST::Lifetime> lifetime_args;
+
+ const_TokenPtr t = lexer.peek_token ();
+ Location locus = t->get_locus ();
+ const_TokenPtr t2 = lexer.peek_token (1);
+ while (t->get_id () == LIFETIME
+ && (t2->get_id () == COMMA || !is_right_angle_tok (t2->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 ();
+ t2 = lexer.peek_token (1);
+ }
+
+ // try to parse types second
+ std::vector<std::unique_ptr<AST::Type> > type_args;
+
+ // TODO: think of better control structure
+ t = lexer.peek_token ();
+ while (!is_right_angle_tok (t->get_id ()))
+ {
+ // ensure not binding being parsed as type accidently
+ if (t->get_id () == IDENTIFIER
+ && lexer.peek_token (1)->get_id () == EQUAL)
+ {
+ break;
+ }
+
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
+ {
+ // not necessarily an error
+ break;
+ }
+
+ type_args.push_back (std::move (type));
+
+ // 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 ();
+ }
+
+ return AST::GenericArgs (std::move (lifetime_args), std::move (type_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 ();
+
+ 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 ()
+{
+ if (!skip_token (LEFT_PAREN))
+ {
+ // skip somewhere?
+ return AST::TypePathFunction::create_error ();
+ }
+
+ // parse function inputs
+ std::vector<std::unique_ptr<AST::Type> > inputs;
+
+ // TODO: think of better control structure
+ while (true)
+ {
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
+ {
+ // not necessarily an error
+ break;
+ }
+
+ inputs.push_back (std::move (type));
+
+ // skip commas, including trailing commas
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ else
+ {
+ 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 ();
+
+ return AST::TypePathFunction (std::move (inputs), 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?
+ rust_error_at (t->get_locus (),
+ "could not parse path expression segment");
+ return AST::PathInExpression::create_error ();
+ }
+
+ segments.push_back (std::move (segment));
+
+ t = lexer.peek_token ();
+ }
+
+ return AST::PathInExpression (std::move (segments), locus,
+ has_opening_scope_resolution,
+ std::vector<AST::Attribute> ());
+}
+
+/* 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 (
+ bool pratt_parse)
+{
+ /* 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_parse);
+ 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?
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "required initial path expression segment in "
+ "qualified path in expression could not be parsed");
+ 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?
+ rust_error_at (t->get_locus (),
+ "could not parse path expression segment in qualified "
+ "path in expression");
+ return AST::QualifiedPathInExpression::create_error ();
+ }
+
+ segments.push_back (std::move (segment));
+
+ t = lexer.peek_token ();
+ }
+
+ // FIXME: outer attr parsing
+ return AST::QualifiedPathInExpression (std::move (qual_path_type),
+ std::move (segments), locus,
+ std::vector<AST::Attribute> ());
+}
+
+// Parses the type syntactical construction at the start of a qualified path.
+template <typename ManagedTokenSource>
+AST::QualifiedPathType
+Parser<ManagedTokenSource>::parse_qualified_path_type (bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ // TODO: should this actually be error? is there anywhere where this could be
+ // valid?
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+ if (!skip_token (LEFT_ANGLE))
+ {
+ // skip after somewhere?
+ return AST::QualifiedPathType::create_error ();
+ }
+ }
+ else
+ {
+ // move back by 1 if pratt parsing due to skipping '<'
+ locus = lexer.peek_token ()->get_locus () - 1;
+ }
+
+ // parse type (required)
+ std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse type in qualified path type");
+ // 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 ())
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "could not parse type path in as clause in qualified path type");
+ // 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 path segments
+ std::vector<std::unique_ptr<AST::TypePathSegment> > segments;
+
+ // 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?
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "required initial type path segment in qualified path in "
+ "type could not be parsed");
+ return AST::QualifiedPathInType::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?
+ rust_error_at (
+ t->get_locus (),
+ "could not parse type path segment in qualified path in type");
+ return AST::QualifiedPathInType::create_error ();
+ }
+
+ segments.push_back (std::move (segment));
+
+ t = lexer.peek_token ();
+ }
+
+ return AST::QualifiedPathInType (std::move (qual_path_type),
+ 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime in self param");
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse type in self param");
+ // skip after somewhere?
+ return AST::SelfParam::create_error ();
+ }
+ }
+
+ // ensure that cannot have both type and reference
+ if (type != nullptr && has_reference)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "cannot have both a reference and a type specified in a self param");
+ // 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))
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "method missing opening parentheses before parameter list");
+ skip_after_next_block ();
+ return AST::Method::create_error ();
+ }
+
+ // parse self param
+ AST::SelfParam self_param = parse_self_param ();
+ if (self_param.is_error ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse self param in method");
+ 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 ();
+
+ if (!skip_token (RIGHT_PAREN))
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "method declaration missing closing parentheses after parameter list");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "method declaration missing block expression");
+ 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 (),
+ std::vector<AST::Attribute> (), 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ /* 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));
+ }
+ }
+ 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));
+ }
+ }
+ 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));
+ }
+}
+
+/* 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 (
+ std::vector<AST::Attribute> 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 ()->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:
+ rust_error_at (t->get_locus (),
+ "could not recognise expr beginning with '%s' 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)
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse expr with block in parsing expr statement");
+ skip_after_end_block ();
+ return nullptr;
+ }
+
+ // return expr stmt created from expr
+ return std::unique_ptr<AST::ExprStmtWithBlock> (
+ new AST::ExprStmtWithBlock (std::move (expr_parsed), t->get_locus ()));
+}
+
+/* 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ /* TODO: maybe move more logic for expr without block in here for better error
+ * handling */
+
+ // try to parse expr without block
+ /*AST::ExprWithoutBlock* expr = nullptr;
+ expr = parse_expr_without_block(std::move(outer_attrs));*/
+ // HACK: parse expression instead of expression without block, due to Pratt
+ // parsing issues
+ /*std::unique_ptr<AST::Expr> expr = nullptr;
+ Location locus = lexer.peek_token ()->get_locus ();
+ expr = parse_expr (std::move (outer_attrs));*/
+
+ // attempt to parse via parse_expr_without_block
+ std::unique_ptr<AST::ExprWithoutBlock> expr = nullptr;
+ Location locus = lexer.peek_token ()->get_locus ();
+ expr = parse_expr_without_block (std::move (outer_attrs));
+ if (expr == nullptr)
+ {
+ // expr is required, error
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse expr without block in expr statement");
+ skip_after_semicolon ();
+ return nullptr;
+ }
+
+ // skip semicolon at end that is required
+ if (!skip_token (SEMICOLON))
+ {
+ // skip somewhere?
+ 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ /* 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));
+ case LEFT_PAREN:
+ /* either grouped expr or tuple expr - depends on whether there is a comma
+ * inside the parentheses - if so, tuple expr, otherwise, grouped expr. */
+ return parse_grouped_or_tuple_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));
+
+ if (expr == nullptr)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse expression for expression without "
+ "block (pratt-parsed expression is null)");
+ 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
+ {
+ rust_error_at (t->get_locus (),
+ "converted expr without block is null");
+ 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 (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+ if (!skip_token (LEFT_CURLY))
+ {
+ skip_after_end_block ();
+ return nullptr;
+ }
+ }
+ else
+ {
+ locus = lexer.peek_token ()->get_locus () - 1;
+ }
+
+ std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
+
+ // parse statements and expression
+ std::vector<std::unique_ptr<AST::Stmt> > stmts;
+ std::unique_ptr<AST::ExprWithoutBlock> 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 ())
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse statement or expression without "
+ "block in block expression");
+ return nullptr;
+ }
+
+ 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;
+ }
+
+ t = lexer.peek_token ();
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ rust_error_at (t->get_locus (),
+ "error may be from having an expression (as opposed to "
+ "statement) in the body of the function but not last");
+ skip_after_end_block ();
+ return nullptr;
+ }
+
+ // grammar allows for empty block expressions
+
+ return std::unique_ptr<AST::BlockExpr> (
+ new AST::BlockExpr (std::move (stmts), std::move (expr),
+ std::move (inner_attrs), std::move (outer_attrs),
+ 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (LEFT_PAREN);
+
+ std::vector<AST::Attribute> 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 (
+ std::vector<AST::Attribute> 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?
+ rust_error_at (t->get_locus (), "could not parse closure param");
+ 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 ();
+ }
+ break;
+ default:
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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
+ rust_error_at (t->get_locus (), "failed to parse type for closure");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // parse block expr, which is required
+ std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
+ if (block == nullptr)
+ {
+ // error
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse block expr in closure");
+ // 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse expression in closure");
+ // 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 (
+ std::vector<AST::Attribute> 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 RAW_STRING_LITERAL:
+ // put here if lexer changes to have these
+ case BYTE_CHAR_LITERAL:
+ type = AST::Literal::BYTE;
+ literal_value = t->get_str ();
+ lexer.skip_token ();
+ break;
+ case BYTE_STRING_LITERAL:
+ type = AST::Literal::BYTE_STRING;
+ literal_value = t->get_str ();
+ lexer.skip_token ();
+ break;
+ // case RAW_BYTE_STRING_LITERAL:
+ 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 = std::string ("true");
+ lexer.skip_token ();
+ break;
+ case FALSE_LITERAL:
+ type = AST::Literal::BOOL;
+ literal_value = std::string ("false");
+ lexer.skip_token ();
+ break;
+ default:
+ // error - cannot be a literal expr
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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_locus (), std::move (outer_attrs)));
+}
+
+// Parses a return expression (including any expression to return).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ReturnExpr>
+Parser<ManagedTokenSource>::parse_return_expr (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+
+ skip_token (RETURN_TOK);
+ }
+ else
+ {
+ // minus 7 chars for 6 in return and a space
+ // or just TODO: pass in location data
+ locus = lexer.peek_token ()->get_locus () - 7;
+ }
+
+ // parse expression to return, if it exists
+ std::unique_ptr<AST::Expr> returned_expr = parse_expr ();
+ // FIXME: ensure this doesn't ruin the middle of any expressions or anything
+
+ return std::unique_ptr<AST::ReturnExpr> (
+ new AST::ReturnExpr (locus, std::move (returned_expr),
+ std::move (outer_attrs)));
+}
+
+/* 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 (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+
+ skip_token (BREAK);
+ }
+ else
+ {
+ // minus 6 chars for 5 in return and a space
+ // or just TODO: pass in location data
+ locus = lexer.peek_token ()->get_locus () - 6;
+ }
+
+ // 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
+ std::unique_ptr<AST::Expr> return_expr = parse_expr ();
+
+ return std::unique_ptr<AST::BreakExpr> (
+ new AST::BreakExpr (locus, std::move (label), std::move (return_expr),
+ std::move (outer_attrs)));
+}
+
+// Parses a continue expression (including any label to continue from).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ContinueExpr>
+Parser<ManagedTokenSource>::parse_continue_expr (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+
+ skip_token (CONTINUE);
+ }
+ else
+ {
+ // minus 9 chars for 8 in return and a space
+ // or just TODO: pass in location data
+ locus = lexer.peek_token ()->get_locus () - 9;
+ }
+
+ // 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 (locus, std::move (label), std::move (outer_attrs)));
+}
+
+// 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 (
+ std::vector<AST::Attribute> outer_attrs ATTRIBUTE_UNUSED)
+{
+ // TODO: make having outer attributes an error?
+
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (IF);
+
+ // detect accidental if let
+ if (lexer.peek_token ()->get_id () == LET)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "if let expression probably exists, but is being parsed "
+ "as an if expression. This may "
+ "be a parser 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 (std::vector<AST::Attribute> (), no_struct_expr);
+ if (condition == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse condition expression in if expression");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // parse required block expr
+ std::unique_ptr<AST::BlockExpr> if_body = parse_block_expr ();
+ if (if_body == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse if body block expression in if expression");
+ // 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), 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse else body block expression in "
+ "if expression");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::IfExprConseqElse> (
+ new AST::IfExprConseqElse (std::move (condition),
+ std::move (if_body),
+ std::move (else_body), 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse (else) if let expression "
+ "after if expression");
+ // 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), locus));
+ }
+ else
+ {
+ // parse if expr (required)
+ std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
+ if (if_expr == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse (else) if expression after "
+ "if expression");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::IfExprConseqIf> (
+ new AST::IfExprConseqIf (std::move (condition),
+ std::move (if_body),
+ std::move (if_expr), locus));
+ }
+ }
+ default:
+ // error - invalid token
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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 (
+ std::vector<AST::Attribute> outer_attrs ATTRIBUTE_UNUSED)
+{
+ // TODO: make having outer attributes an error?
+
+ Location locus = lexer.peek_token ()->get_locus ();
+ skip_token (IF);
+
+ // detect accidental if expr parsed as if let expr
+ if (lexer.peek_token ()->get_id () != LET)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "if expression probably exists, but is being parsed as an "
+ "if let expression. This may "
+ "be a parser 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 ())
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse any match arm patterns in if let expression");
+ // 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 (std::vector<AST::Attribute> (), no_struct_expr);
+ if (scrutinee_expr == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse scrutinee expression in if let expression");
+ // 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse if let body block expression in if let expression");
+ // 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),
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse else body block expression in "
+ "if let expression");
+ // 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), 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse (else) if let expression "
+ "after if let expression");
+ // 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), locus));
+ }
+ else
+ {
+ // parse if expr (required)
+ std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
+ if (if_expr == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse (else) if expression after "
+ "if let expression");
+ // 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), locus));
+ }
+ }
+ default:
+ // error - invalid token
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' 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 (
+ std::vector<AST::Attribute> 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 (LOOP);
+
+ // parse loop body, which is required
+ std::unique_ptr<AST::BlockExpr> loop_body = parse_block_expr ();
+ if (loop_body == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse loop body in (infinite) loop expression");
+ 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 (
+ std::vector<AST::Attribute> 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);
+
+ // ensure it isn't a while let loop
+ if (lexer.peek_token ()->get_id () == LET)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "appears to be while let loop but is being parsed by "
+ "while loop - this may be a compiler issue");
+ // 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 (std::vector<AST::Attribute> (), no_struct_expr);
+ if (predicate == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse predicate expression in while loop");
+ // 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse loop body block expression in while loop");
+ // 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 (
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (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");
+ // 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 (std::vector<AST::Attribute> (), no_struct_expr);
+ if (predicate_expr == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse predicate expression in while let loop");
+ // 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse block expr (loop body) of while let loop");
+ // 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 (
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse iterator pattern in for loop");
+ // 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 (std::vector<AST::Attribute> (), no_struct_expr);
+ if (expr == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse iterator expression in for loop");
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse loop body block expression in for loop");
+ // 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 (
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "expected lifetime in labelled loop expr (to parse loop "
+ "label) - found '%s'",
+ lexer.peek_token ()->get_token_description ());
+ // skip?
+ return nullptr;
+ }
+
+ // parse loop label (required)
+ AST::LoopLabel label = parse_loop_label ();
+ if (label.is_error ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse loop label in labelled loop expr");
+ // 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
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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 (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+
+ skip_token (MATCH_TOK);
+ }
+ else
+ {
+ // TODO: probably just pass in location data as param
+ // get current pos then move back 6 - 5 for match, 1 for space
+ locus = lexer.peek_token ()->get_locus () - 6;
+ }
+
+ /* 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 (std::vector<AST::Attribute> (), no_struct_expr);
+ if (scrutinee == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse scrutinee expression in match expression");
+ // 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)
+ std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
+
+ // parse match arms (if they exist)
+ std::vector<std::unique_ptr<AST::MatchCase> > match_arms;
+
+ // FIXME: absolute worst control structure ever
+ // parse match cases
+ while (true)
+ {
+ // parse match arm itself, which is required
+ AST::MatchArm arm = parse_match_arm ();
+ if (arm.is_error ())
+ {
+ // not necessarily an error
+ break;
+ }
+
+ if (!skip_token (MATCH_ARROW))
+ {
+ // skip after somewhere?
+ // TODO is returning here a good idea? or is break better?
+ return nullptr;
+ }
+
+ // branch on next token - if '{', block expr, otherwise just expr
+ if (lexer.peek_token ()->get_id () == LEFT_CURLY)
+ {
+ // block expr
+ std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+ if (block_expr == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse block expr in match arm in match expr");
+ // skip somewhere
+ return nullptr;
+ }
+
+ // create match case block expr and add to cases
+ std::unique_ptr<AST::MatchCaseBlockExpr> match_case_block (
+ new AST::MatchCaseBlockExpr (std::move (arm),
+ std::move (block_expr)));
+ match_arms.push_back (std::move (match_case_block));
+
+ // skip optional comma
+ if (lexer.peek_token ()->get_id () == COMMA)
+ {
+ lexer.skip_token ();
+ }
+ }
+ else
+ {
+ // regular expr
+ std::unique_ptr<AST::Expr> expr = parse_expr ();
+ if (expr == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse expr in match arm in match expr");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // construct match case expr and add to cases
+ std::unique_ptr<AST::MatchCaseExpr> match_case_expr (
+ new AST::MatchCaseExpr (std::move (arm), std::move (expr)));
+ match_arms.push_back (std::move (match_case_expr));
+
+ // skip REQUIRED comma - if no comma, break
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ // if no comma, must be end of cases
+ break;
+ }
+ lexer.skip_token ();
+ }
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ // skip somewhere?
+ return nullptr;
+ }
+
+ 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
+ std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
+
+ // DEBUG
+ fprintf (stderr, "about to start parsing match arm patterns\n");
+
+ // 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse any patterns in match arm");
+ // skip somewhere?
+ return AST::MatchArm::create_error ();
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed match arm patterns\n");
+
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse guard expression in match arm");
+ // skip somewhere?
+ return AST::MatchArm::create_error ();
+ }
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed match arm\n");
+
+ return AST::MatchArm (std::move (match_arm_patterns), 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 '|'
+ bool has_leading_pipe = false;
+ if (lexer.peek_token ()->get_id () == PIPE)
+ {
+ has_leading_pipe = true;
+ 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
+ fprintf (stderr, "successfully parsed initial match arm pattern\n");
+
+ // 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
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in match arm patterns");
+ // skip somewhere?
+ return std::vector<std::unique_ptr<AST::Pattern> > ();
+ }
+
+ patterns.push_back (std::move (pattern));
+
+ t = lexer.peek_token ();
+ }
+
+ return patterns;
+}
+
+// Parses an async block expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AsyncBlockExpr>
+Parser<ManagedTokenSource>::parse_async_block_expr (
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse block expression of async block expression");
+ // 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse block expression of unsafe block expression");
+ // 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 (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+
+ skip_token (LEFT_SQUARE);
+ }
+ else
+ {
+ locus = lexer.peek_token ()->get_locus () - 1;
+ }
+
+ // parse optional inner attributes
+ std::vector<AST::Attribute> 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 ();
+
+ return std::unique_ptr<AST::ArrayExpr> (
+ new AST::ArrayExpr (nullptr, 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse expression in array expression "
+ "(even though arrayelems seems to be present)");
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "could not parse copy amount expression in array "
+ "expression (arrayelems)");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
+ new AST::ArrayElemsCopied (std::move (initial_expr),
+ std::move (copy_amount)));
+ 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.push_back (std::move (initial_expr));
+
+ std::unique_ptr<AST::ArrayElemsValues> array_elems (
+ new AST::ArrayElemsValues (std::move (exprs)));
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse element in array expression");
+ // skip somewhere?
+ return nullptr;
+ }
+ exprs.push_back (std::move (expr));
+
+ t = lexer.peek_token ();
+ }
+
+ skip_token (RIGHT_SQUARE);
+
+ std::unique_ptr<AST::ArrayElemsValues> array_elems (
+ new AST::ArrayElemsValues (std::move (exprs)));
+ return std::unique_ptr<AST::ArrayExpr> (
+ new AST::ArrayExpr (std::move (array_elems),
+ std::move (inner_attrs),
+ std::move (outer_attrs), locus));
+ }
+ else
+ {
+ // error
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "unexpected token '%s' in array expression (arrayelems)",
+ lexer.peek_token ()->get_token_description ());
+ // skip somewhere?
+ return nullptr;
+ }
+ }
+}
+
+// Parses a single parameter used in a closure definition.
+template <typename ManagedTokenSource>
+AST::ClosureParam
+Parser<ManagedTokenSource>::parse_closure_param ()
+{
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse type in closure parameter");
+ // skip somewhere?
+ return AST::ClosureParam::create_error ();
+ }
+ }
+
+ return AST::ClosureParam (std::move (pattern), std::move (type));
+}
+
+// Parses a grouped or tuple expression (disambiguates).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprWithoutBlock>
+Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
+ std::vector<AST::Attribute> outer_attrs, bool pratt_parse)
+{
+ // adjustment to allow Pratt parsing to reuse function without copy-paste
+ Location locus = Linemap::unknown_location ();
+ if (!pratt_parse)
+ {
+ locus = lexer.peek_token ()->get_locus ();
+
+ skip_token (LEFT_PAREN);
+ }
+ else
+ {
+ locus = lexer.peek_token ()->get_locus () - 1;
+ }
+
+ // parse optional inner attributes
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse expression in grouped or tuple expression");
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse expr in tuple expr");
+ // 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 ();
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' in grouped or tuple expression "
+ "(parenthesised expression) - "
+ "expected ')' for grouped expr and ',' for tuple expr",
+ t->get_token_description ());
+ // skip somewhere?
+ return nullptr;
+ }
+}
+
+// Parses a type (will further disambiguate any type).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_type ()
+{
+ /* 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 ())
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse qualified path in type");
+ 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 ()));
+ }
+ 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 ())
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse path as first component of type");
+ 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 ())
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse simple path in macro "
+ "invocation (for type)");
+ return nullptr;
+ }
+
+ lexer.skip_token ();
+
+ AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
+
+ return std::unique_ptr<AST::MacroInvocation> (
+ new AST::MacroInvocation (std::move (macro_path),
+ std::move (tok_tree),
+ std::vector<AST::Attribute> (),
+ 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));
+ }
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse ImplTraitType initial bound");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse TraitObjectType initial bound");
+ 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:
+ rust_error_at (t->get_locus (), "unrecognised token '%s' 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse type inside parentheses (probably "
+ "tuple or parenthesised)");
+ 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
+ AST::Type *released_ptr = types[0].release ();
+ AST::TraitBound *converted_bound
+ = released_ptr->to_trait_bound (true);
+ delete released_ptr;
+ if (converted_bound == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to hackily converted parsed type to trait bound");
+ return nullptr;
+ }
+ bounds.push_back (std::unique_ptr<AST::TraitBound> (converted_bound));
+ // FIXME: possibly issues wrt memory here
+
+ 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));
+ }
+ else
+ {
+ // release vector pointer
+ std::unique_ptr<AST::Type> released_ptr (types[0].release ());
+ // 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)
+
+ // DEBUG: removed as unique_ptr should auto-delete
+ // delete released_ptr;
+
+ // get value semantics trait bound
+ AST::TraitBound value_bound (*converted_bound);
+
+ // DEBUG: removed as unique ptr should auto-delete
+ // delete converted_bound;
+
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (value_bound,
+ left_delim_locus));
+ }
+ // FIXME: may be issues wrt memory here
+ }
+ }
+ 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));
+ }
+ default:
+ // error
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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 ()
+{
+ /* 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse type in maybe named param");
+ return AST::MaybeNamedParam::create_error ();
+ }
+
+ return AST::MaybeNamedParam (std::move (name), kind, std::move (type),
+ 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;
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_PAREN)
+ {
+ // handle ellipsis (only if next character is right paren)
+ if (t->get_id () == ELLIPSIS)
+ {
+ if (lexer.peek_token (1)->get_id () == RIGHT_PAREN)
+ {
+ lexer.skip_token ();
+ is_variadic = true;
+ break;
+ }
+ else
+ {
+ rust_error_at (t->get_locus (),
+ "ellipsis (for variadic) can only go at end of "
+ "bare function type");
+ return nullptr;
+ }
+ }
+
+ // parse required param
+ AST::MaybeNamedParam param = parse_maybe_named_param ();
+ if (param.is_error ())
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse maybe named param in bare function type");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse return type (type no bounds) in bare "
+ "function type");
+ 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 (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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse lifetime in reference type");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse referenced type in reference type");
+ 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:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pointed type of raw pointer type");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse inner type in slice or array type");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse size expression in array type");
+ 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
+ rust_error_at (
+ t->get_locus (),
+ "unrecognised token '%s' 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 ())
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse qualified path in type");
+ 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
+ rust_error_at (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 ())
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse path as first component of type no bounds");
+ 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 ())
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse simple path in macro "
+ "invocation (for type)");
+ return nullptr;
+ }
+
+ lexer.skip_token ();
+
+ AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
+
+ return std::unique_ptr<AST::MacroInvocation> (
+ new AST::MacroInvocation (std::move (macro_path),
+ std::move (tok_tree),
+ std::vector<AST::Attribute> (),
+ locus));
+ }
+ case PLUS: {
+ // type param bounds - not allowed, here for error message
+ rust_error_at (t->get_locus (),
+ "type param bounds (in TraitObjectType) are not "
+ "allowed as TypeNoBounds");
+ return nullptr;
+ }
+ 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
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "lifetime (probably lifetime bound, in type param "
+ "bounds, in ImplTraitType) is "
+ "not allowed in TypeNoBounds");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse ImplTraitTypeOneBound bound");
+ return nullptr;
+ }
+
+ Location locus = t->get_locus ();
+
+ // ensure not a trait with multiple bounds
+ t = lexer.peek_token ();
+ if (t->get_id () == PLUS)
+ {
+ rust_error_at (t->get_locus (),
+ "plus after trait bound means an ImplTraitType, "
+ "which is not allowed as a TypeNoBounds");
+ return nullptr;
+ }
+
+ // 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));
+ }
+ 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
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "lifetime as bound in TraitObjectTypeOneBound "
+ "is not allowed, so cannot be TypeNoBounds");
+ return nullptr;
+ }
+
+ // should be trait bound, so parse trait bound
+ std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+ if (initial_bound == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse TraitObjectTypeOneBound initial bound");
+ return nullptr;
+ }
+
+ Location locus = t->get_locus ();
+
+ // detect error with plus as next token
+ t = lexer.peek_token ();
+ if (t->get_id () == PLUS)
+ {
+ rust_error_at (t->get_locus (),
+ "plus after trait bound means a TraitObjectType, "
+ "which is not allowed as a TypeNoBounds");
+ return nullptr;
+ }
+
+ // 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), locus,
+ has_dyn));
+ }
+ default:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse type inside parentheses (probably "
+ "tuple or parenthesised)");
+ 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
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "plus (implying TraitObjectType as type param "
+ "bounds) is not allowed in type no bounds");
+ return nullptr;
+ }
+ else
+ {
+ // release vector pointer
+ std::unique_ptr<AST::Type> released_ptr (types[0].release ());
+ // 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)
+
+ // DEBUG: removed as unique_ptr should auto-delete
+ // delete released_ptr;
+
+ // get value semantics trait bound
+ AST::TraitBound value_bound (*converted_bound);
+
+ // DEBUG: removed as unique_ptr should auto-delete
+ // delete converted_bound;
+
+ return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+ new AST::TraitObjectTypeOneBound (value_bound,
+ left_paren_locus));
+ }
+ // FIXME: memory safety issues here
+ }
+ }
+ 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:
+ rust_error_at (range_lower->get_locus (),
+ "token type '%s' cannot be parsed as range pattern "
+ "bound or literal after "
+ "minus "
+ "symbol",
+ range_lower->get_token_description ());
+ return nullptr;
+ }
+ break;
+ default:
+ rust_error_at (range_lower->get_locus (),
+ "token type '%s' 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),
+ range_lower->get_locus (), has_minus));
+
+ std::unique_ptr<AST::RangePatternBound> upper
+ = parse_range_pattern_bound ();
+ if (upper == nullptr)
+ {
+ rust_error_at (
+ next->get_locus (),
+ "failed to parse range pattern bound in range pattern");
+ 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 (), has_minus));
+ }
+}
+
+// Parses a range pattern bound (value only).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangePatternBound>
+Parser<ManagedTokenSource>::parse_range_pattern_bound ()
+{
+ const_TokenPtr range_lower = lexer.peek_token ();
+ Location 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_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_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_locus));
+ case FLOAT_LITERAL:
+ lexer.skip_token ();
+ fprintf (stderr, "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_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_locus, true));
+ case FLOAT_LITERAL:
+ lexer.skip_token (1);
+ fprintf (stderr,
+ "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_locus, true));
+ default:
+ rust_error_at (range_lower->get_locus (),
+ "token type '%s' 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 ())
+ {
+ rust_error_at (
+ range_lower->get_locus (),
+ "failed to parse path in expression range pattern bound");
+ 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 ())
+ {
+ rust_error_at (range_lower->get_locus (),
+ "failed to parse qualified path in expression range "
+ "pattern bound");
+ return nullptr;
+ }
+ return std::unique_ptr<AST::RangePatternBoundQualPath> (
+ new AST::RangePatternBoundQualPath (std::move (path)));
+ }
+ default:
+ rust_error_at (range_lower->get_locus (),
+ "token type '%s' 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
+ {
+ rust_error_at (t->get_locus (), "unexpected token '-' in pattern - "
+ "did you forget an integer literal?");
+ 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), std::vector<AST::Attribute> ());
+ case LEFT_PAREN: {
+ // tuple struct
+ lexer.skip_token ();
+
+ // parse items
+ std::unique_ptr<AST::TupleStructItems> items
+ = parse_tuple_struct_items ();
+ if (items == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse tuple struct items");
+ 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), std::move (elems)));
+ }
+ default:
+ // assume path in expression
+ return std::unique_ptr<AST::PathInExpression> (
+ new AST::PathInExpression (std::move (path)));
+ }
+ }
+ default:
+ rust_error_at (t->get_locus (), "unexpected token '%s' 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:
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in reference pattern");
+ // 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse pattern inside ranged tuple pattern");
+ // 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in grouped or tuple pattern");
+ 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse pattern in tuple pattern");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in tuple pattern");
+ 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
+ rust_error_at (t->get_locus (),
+ "failed to parse tuple pattern (probably) or maybe "
+ "grouped pattern");
+ return nullptr;
+ }
+ }
+ default:
+ // error
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse initial pattern in slice pattern");
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in slice pattern");
+ 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
+ fprintf (stderr, "parsed ref in identifier pattern\n");
+ }
+
+ 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
+ fprintf (stderr, "parsed identifier in identifier pattern\n");
+
+ // 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)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse pattern to bind in identifier pattern\n");
+ return nullptr;
+ }
+ }
+
+ // DEBUG
+ fprintf (stderr, "about to return identifier pattern\n");
+
+ 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),
+ std::vector<AST::Attribute> ());
+ case LEFT_PAREN: {
+ // tuple struct
+ lexer.skip_token ();
+
+ // DEBUG
+ fprintf (stderr, "parsing tuple struct pattern\n");
+
+ // parse items
+ std::unique_ptr<AST::TupleStructItems> items
+ = parse_tuple_struct_items ();
+ if (items == nullptr)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse tuple struct items");
+ return nullptr;
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed tuple struct items\n");
+
+ if (!skip_token (RIGHT_PAREN))
+ {
+ return nullptr;
+ }
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed tuple struct pattern\n");
+
+ 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
+ fprintf (stderr, "successfully parsed struct pattern\n");
+
+ return std::unique_ptr<AST::StructPattern> (
+ new AST::StructPattern (std::move (path), 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)
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse pattern to bind to identifier pattern");
+ 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)));
+ }
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse pattern bind to a path, not an identifier");
+ 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
+ fprintf (stderr, "started parsing tuple struct items\n");
+
+ // check for '..' at front
+ if (lexer.peek_token ()->get_id () == DOT_DOT)
+ {
+ // only parse upper patterns
+ lexer.skip_token ();
+
+ // DEBUG
+ fprintf (stderr, "'..' at front in tuple struct items detected\n");
+
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in tuple struct items");
+ return nullptr;
+ }
+ upper_patterns.push_back (std::move (pattern));
+
+ t = lexer.peek_token ();
+ }
+
+ // DEBUG
+ fprintf (
+ stderr,
+ "finished parsing tuple struct items ranged (upper/none only)\n");
+
+ 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
+ fprintf (stderr, "about to parse pattern in tuple struct items\n");
+
+ // parse pattern, which is required
+ std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+ if (pattern == nullptr)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse pattern in tuple struct items");
+ return nullptr;
+ }
+ lower_patterns.push_back (std::move (pattern));
+
+ // DEBUG
+ fprintf (stderr, "successfully parsed pattern in tuple struct items\n");
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ // DEBUG
+ fprintf (stderr, "broke out of parsing patterns in tuple struct "
+ "items as no comma \n");
+
+ 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)
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse pattern in tuple struct items");
+ 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
+ rust_error_at (t->get_locus (),
+ "unexpected token '%s' 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;
+
+ // try parsing struct pattern fields
+ const_TokenPtr t = lexer.peek_token ();
+ while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
+ {
+ std::unique_ptr<AST::StructPatternField> field
+ = parse_struct_pattern_field ();
+ if (field == nullptr)
+ {
+ // TODO: should this be an error?
+ // assuming that this means that it is a struct pattern etc instead
+
+ // DEBUG
+ fprintf (
+ stderr,
+ "failed to parse struct pattern field - breaking from loop\n");
+
+ break;
+ }
+
+ fields.push_back (std::move (field));
+
+ // DEBUG
+ fprintf (stderr, "successfully pushed back a struct pattern field\n");
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ lexer.skip_token ();
+
+ t = lexer.peek_token ();
+ }
+
+ // FIXME: this method of parsing prevents parsing any outer attributes on the
+ // .. also there seems to be no distinction between having etc and not having
+ // etc.
+ if (lexer.peek_token ()->get_id () == DOT_DOT)
+ {
+ lexer.skip_token ();
+
+ // as no outer attributes
+ AST::StructPatternEtc etc = AST::StructPatternEtc::create_empty ();
+
+ return AST::StructPatternElements (std::move (fields), std::move (etc));
+ }
+
+ 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)
+ std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
+
+ // 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)
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse pattern in tuple index struct pattern field");
+ 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)
+ {
+ rust_error_at (
+ t->get_locus (),
+ "failed to parse pattern in struct pattern field");
+ 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
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' 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;
+ }
+}
+
+/* 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
+ std::vector<AST::Attribute> 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
+ std::unique_ptr<AST::ExprStmtWithBlock> stmt (
+ parse_expr_stmt_with_block (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (stmt));
+ }
+ 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:
+ rust_error_at (t2->get_locus (),
+ "unrecognised token '%s' 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: {
+ // all expressions with block, so cannot be final expr without block in
+ // function
+ std::unique_ptr<AST::ExprStmtWithBlock> stmt (
+ parse_expr_stmt_with_block (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (stmt));
+ }
+ 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))
+ {
+ std::unique_ptr<AST::ExprStmtWithBlock> stmt (
+ parse_expr_stmt_with_block (std::move (outer_attrs)));
+ return ExprOrStmt (std::move (stmt));
+ }
+ else
+ {
+ // should be expr without block
+ 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));
+ }
+ }
+ // crappy hack to do union "keyword"
+ case IDENTIFIER:
+ // TODO: ensure std::string and literal comparison works
+ if (t->get_str () == "union")
+ {
+ 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 ();
+
+ 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 ();
+
+ 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 (
+ std::vector<AST::Attribute> 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 ())
+ {
+ rust_error_at (t2->get_locus (),
+ "failed to convert parsed path to simple "
+ "path (for macro invocation or semi)");
+ 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:
+ rust_error_at (
+ t3->get_locus (),
+ "unrecognised token '%s' 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;
+
+ 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)
+ {
+ rust_error_at (
+ t3->get_locus (),
+ "failed to parse token tree for macro invocation (or semi) - "
+ "found "
+ "'%s'",
+ t3->get_token_description ());
+ return ExprOrStmt::create_error ();
+ }
+
+ token_trees.push_back (std::move (tree));
+
+ t3 = lexer.peek_token ();
+ }
+
+ // 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).
+ */
+
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ {
+ lexer.skip_token ();
+
+ std::unique_ptr<AST::MacroInvocationSemi> stmt (
+ new AST::MacroInvocationSemi (std::move (macro_path), type,
+ std::move (token_trees),
+ std::move (outer_attrs),
+ stmt_or_expr_loc));
+ return ExprOrStmt (std::move (stmt));
+ }
+
+ // otherwise, create macro invocation
+ AST::DelimTokenTree delim_tok_tree (type, std::move (token_trees),
+ tok_tree_loc);
+
+ std::unique_ptr<AST::MacroInvocation> expr (
+ new AST::MacroInvocation (std::move (macro_path),
+ std::move (delim_tok_tree),
+ std::move (outer_attrs),
+ stmt_or_expr_loc));
+ return ExprOrStmt (std::move (expr));
+ }
+ else
+ {
+ // tokens don't match opening delimiters, so produce error
+ rust_error_at (
+ t2->get_locus (),
+ "unexpected token '%s' - expecting closing delimiter '%s' (for a "
+ "macro invocation)",
+ t2->get_token_description (),
+ (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}")));
+ 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)
+ {
+ rust_error_at (t2->get_locus (),
+ "failed to parse struct expr struct");
+ return ExprOrStmt::create_error ();
+ }
+ }
+ else
+ {
+ // assume path - make statement if final ';'
+ // lexer.skip_token();
+
+ // HACK: add outer attrs to path
+ path.replace_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)
+ {
+ rust_error_at (t2->get_locus (),
+ "failed to parse struct expr tuple");
+ 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.replace_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 ()
+{
+ // DEBUG:
+ fprintf (stderr, "beginning struct/enum expr field parsing \n");
+
+ 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse struct expression field with "
+ "identifier and expression");
+ return nullptr;
+ }
+
+ // DEBUG:
+ fprintf (
+ stderr,
+ "struct/enum expr field parsing field identifier value done \n");
+
+ return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
+ new AST::StructExprFieldIdentifierValue (std::move (ident),
+ std::move (expr)));
+ }
+ else
+ {
+ // struct expr field with identifier only
+ Identifier ident = t->get_str ();
+ lexer.skip_token ();
+
+ // DEBUG:
+ fprintf (stderr,
+ "struct/enum expr field parsing field identifier done \n");
+
+ return std::unique_ptr<AST::StructExprFieldIdentifier> (
+ new AST::StructExprFieldIdentifier (std::move (ident)));
+ }
+ 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse expr in struct (or enum) expr "
+ "field with tuple index");
+ return nullptr;
+ }
+
+ // DEBUG:
+ fprintf (stderr,
+ "struct/enum expr field parsing field index value done \n");
+
+ return std::unique_ptr<AST::StructExprFieldIndexValue> (
+ new AST::StructExprFieldIndexValue (index, std::move (expr)));
+ }
+ case DOT_DOT:
+ // this is a struct base and can't be parsed here, so just return nothing
+ // without erroring
+
+ // DEBUG:
+ fprintf (stderr, "struct/enum expr field parsing failed - '..' \n");
+
+ return nullptr;
+ default:
+ // DEBUG:
+ fprintf (stderr,
+ "struct/enum expr field parsing failed - unrecognised char \n");
+
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' as first token of struct expr "
+ "field - expected identifier "
+ "or int 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 (
+ std::vector<AST::Attribute> outer_attrs)
+{
+ Location macro_locus = lexer.peek_token ()->get_locus ();
+ AST::SimplePath macro_path = parse_simple_path ();
+ if (macro_path.is_empty ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse simple path in macro invocation or semi");
+ 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:
+ rust_error_at (t3->get_locus (),
+ "unrecognised token '%s' 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;
+
+ 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)
+ {
+ rust_error_at (t3->get_locus (),
+ "failed to parse token tree for macro invocation (or "
+ "semi) - found '%s'",
+ t3->get_token_description ());
+ return ExprOrStmt::create_error ();
+ }
+
+ token_trees.push_back (std::move (tree));
+
+ t3 = lexer.peek_token ();
+ }
+
+ // 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).
+ */
+
+ if (lexer.peek_token ()->get_id () == SEMICOLON)
+ {
+ lexer.skip_token ();
+
+ std::unique_ptr<AST::MacroInvocationSemi> stmt (
+ new AST::MacroInvocationSemi (std::move (macro_path), type,
+ std::move (token_trees),
+ std::move (outer_attrs),
+ macro_locus));
+ return ExprOrStmt (std::move (stmt));
+ }
+
+ // otherwise, create macro invocation
+ AST::DelimTokenTree delim_tok_tree (type, std::move (token_trees),
+ tok_tree_loc);
+
+ std::unique_ptr<AST::MacroInvocation> expr (
+ new AST::MacroInvocation (std::move (macro_path),
+ std::move (delim_tok_tree),
+ 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
+ rust_error_at (
+ t->get_locus (),
+ "unexpected token '%s' - expecting closing delimiter '%s' (for a "
+ "macro invocation)",
+ t->get_token_description (),
+ (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}")));
+ 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)
+{
+ rust_error_at (t->get_locus (), "unexpected %s\n",
+ t->get_token_description ());
+}
+
+/* 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 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
+ {
+ rust_error_at (t->get_locus (), "expecting %s but %s found!\n",
+ get_token_description (token_id),
+ t->get_token_description ());
+
+ 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,
+ std::vector<AST::Attribute> outer_attrs,
+ ParseRestrictions restrictions)
+{
+ const_TokenPtr current_token = lexer.peek_token ();
+ lexer.skip_token ();
+
+ // parse null denotation (unary part of expression)
+ std::unique_ptr<AST::Expr> expr
+ = null_denotation (current_token, std::move (outer_attrs),
+ restrictions);
+
+ if (expr == nullptr)
+ {
+ // DEBUG
+ fprintf (stderr,
+ "null denotation is null; returning null for parse_expr\n");
+ 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::vector<AST::Attribute> (), restrictions);
+
+ if (expr == nullptr)
+ {
+ // DEBUG
+ fprintf (stderr,
+ "left denotation is null; returning null for parse_expr\n");
+
+ return nullptr;
+ }
+ }
+
+ return expr;
+}
+
+/* Parse expression with lowest left binding power. FIXME: this may only apply
+ * to expressions without blocks as they are the only ones to have precedence?
+ */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::parse_expr (std::vector<AST::Attribute> outer_attrs,
+ ParseRestrictions restrictions)
+{
+ // HACK: only call parse_expr(LBP_LOWEST) after ensuring it is not an
+ // expression with block?
+ 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, std::vector<AST::Attribute> 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: {
+ // when encountering identifier, lookup in scope
+ SymbolPtr s = scope.lookup(tok->get_str());
+ if (s == nullptr) {
+ rust_error_at(tok->get_locus(), "variable '%s' not declared in the
+ current scope", tok->get_str().c_str());
+
+ return Tree::error();
+ }
+ // expression is just its VAR_DECL that was stored in the Symbol at
+ declaration return Tree(s->get_tree_decl(), tok->get_locus());
+ }*/
+ // symbol table must be created in semantic analysis pass, so can't use
+ // this
+ case IDENTIFIER: {
+ // DEBUG
+ fprintf (stderr, "beginning null denotation identifier handling\n");
+
+ // 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:
+ fprintf (stderr, "finished null denotation identifier path parsing - "
+ "next is branching \n");
+
+ // branch on next token
+ const_TokenPtr t = lexer.peek_token ();
+ switch (t->get_id ())
+ {
+ case EXCLAM:
+ // macro
+ return parse_macro_invocation_partial (std::move (path),
+ std::move (outer_attrs));
+ case LEFT_CURLY: {
+ bool not_a_block
+ = lexer.peek_token (1)->get_id () == IDENTIFIER
+ && (lexer.peek_token (2)->get_id () == COMMA
+ || (lexer.peek_token (2)->get_id () == COLON
+ && (lexer.peek_token (4)->get_id () == COMMA
+ || !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
+ fprintf (stderr, "values of lookahead: '%s' '%s' '%s' '%s' \n",
+ 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 ());
+
+ fprintf (stderr, "can be struct expr: '%s', not a block: '%s'\n",
+ 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)
+ {
+ // 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.replace_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 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.replace_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.replace_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 (true);
+ path.replace_outer_attrs (std::move (outer_attrs));
+ return std::unique_ptr<AST::QualifiedPathInExpression> (
+ new AST::QualifiedPathInExpression (std::move (path)));
+ }
+ 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_locus ()));
+ case FLOAT_LITERAL:
+ // encode as float?
+ return std::unique_ptr<AST::LiteralExpr> (
+ new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT,
+ tok->get_locus ()));
+ case STRING_LITERAL:
+ return std::unique_ptr<AST::LiteralExpr> (
+ new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
+ tok->get_locus ()));
+ case TRUE_LITERAL:
+ return std::unique_ptr<AST::LiteralExpr> (
+ new AST::LiteralExpr ("true", AST::Literal::BOOL, tok->get_locus ()));
+ case FALSE_LITERAL:
+ return std::unique_ptr<AST::LiteralExpr> (
+ new AST::LiteralExpr ("false", AST::Literal::BOOL, tok->get_locus ()));
+ case LEFT_PAREN: { // have to parse whole expression if inside brackets
+ /* recursively invoke parse_expression with lowest priority possible as
+ * it it were a top-level expression. */
+ /*AST::Expr* expr = parse_expr();
+ tok = lexer.peek_token();
+
+ // end of expression must be a close-bracket
+ if (tok->get_id() != RIGHT_PAREN)
+ rust_error_at(
+ tok->get_locus(), "expecting ')' but %s found\n",
+ tok->get_token_description()); else lexer.skip_token();
+
+ return expr;
+ // FIXME: this assumes grouped expression - could be tuple expression if
+ commas inside*/
+
+ return parse_grouped_or_tuple_expr (std::move (outer_attrs), true);
+ }
+ /*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;
+ std::unique_ptr<AST::Expr> expr
+ = parse_expr (LBP_UNARY_MINUS, std::vector<AST::Attribute> (),
+ 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), AST::NegationExpr::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;
+ std::unique_ptr<AST::Expr> expr
+ = parse_expr (LBP_UNARY_EXCLAM, std::vector<AST::Attribute> (),
+ 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), AST::NegationExpr::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, std::vector<AST::Attribute> (),
+ 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 = { .can_be_struct_expr = false, .entered_from_unary = true };
+ /*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, std::vector<AST::Attribute> (),
+ entered_from_unary);
+ is_mut_borrow = true;
+ }
+ else
+ {
+ expr = parse_expr (LBP_UNARY_AMP, std::vector<AST::Attribute> (),
+ 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, std::vector<AST::Attribute> (),
+ entered_from_unary);
+ is_mut_borrow = true;
+ }
+ else
+ {
+ expr = parse_expr (LBP_UNARY_AMP, std::vector<AST::Attribute> (),
+ 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
+ rust_error_at (tok->get_locus (),
+ "found null denotation scope resolution operator, and "
+ "haven't written handling for it.");
+ return nullptr;
+ }
+ case SELF:
+ case SELF_ALIAS:
+ case DOLLAR_SIGN:
+ case CRATE:
+ case SUPER: {
+ // DEBUG
+ fprintf (stderr, "beginning null denotation "
+ "self/self-alias/dollar/crate/super handling\n");
+
+ // 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
+ fprintf (stderr,
+ "just finished parsing path (going to disambiguate) - peeked "
+ "token is '%s'\n",
+ 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.replace_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
+ fprintf (stderr, "can_be_struct_expr: %s\n",
+ 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.replace_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.replace_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.replace_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), true);
+ case BREAK:
+ // FIXME: is this really a null denotation expression?
+ return parse_break_expr (std::move (outer_attrs), true);
+ case CONTINUE:
+ return parse_continue_expr (std::move (outer_attrs), true);
+ case LEFT_CURLY:
+ // ok - this is an expression with block for once.
+ return parse_block_expr (std::move (outer_attrs), true);
+ case MATCH_TOK:
+ // also an expression with block
+ return parse_match_expr (std::move (outer_attrs), true);
+ case LEFT_SQUARE:
+ // array definition expr (not indexing)
+ return parse_array_expr (std::move (outer_attrs), true);
+ default:
+ rust_error_at (tok->get_locus (),
+ "found unexpected token '%s' 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,
+ std::vector<AST::Attribute> 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_slow ();
+ // 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);
+ case MINUS:
+ // difference expression - binary infix
+ return parse_binary_minus_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case ASTERISK:
+ // product expression - binary infix
+ return parse_binary_mult_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case DIV:
+ // quotient expression - binary infix
+ return parse_binary_div_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case PERCENT:
+ // modulo expression - binary infix
+ return parse_binary_mod_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case AMP:
+ // logical or bitwise and expression - binary infix
+ return parse_bitwise_and_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case PIPE:
+ // logical or bitwise or expression - binary infix
+ return parse_bitwise_or_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case CARET:
+ // logical or bitwise xor expression - binary infix
+ return parse_bitwise_xor_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case LEFT_SHIFT:
+ // left shift expression - binary infix
+ return parse_left_shift_expr (tok, std::move (left),
+ std::move (outer_attrs), restrictions);
+ case RIGHT_SHIFT:
+ // right shift expression - binary infix
+ return parse_right_shift_expr (tok, std::move (left),
+ std::move (outer_attrs), 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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?
+ rust_error_at (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 */
+ return parse_tuple_index_expr_float (tok, std::move (left),
+ std::move (outer_attrs),
+ restrictions);
+ default:
+ rust_error_at (tok->get_locus (),
+ "found unexpected token '%s' in left denotation",
+ tok->get_token_description ());
+ return nullptr;
+ }
+}
+
+// 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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+ new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+ AST::ArithmeticOrLogicalExpr::RIGHT_SHIFT,
+ 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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ AST::ComparisonExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ AST::ComparisonExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ AST::ComparisonExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ AST::ComparisonExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ AST::ComparisonExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::ComparisonExpr> (
+ new AST::ComparisonExpr (std::move (left), std::move (right),
+ AST::ComparisonExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::LazyBooleanExpr> (
+ new AST::LazyBooleanExpr (std::move (left), std::move (right),
+ AST::LazyBooleanExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ restrictions);
+ if (right == nullptr)
+ return nullptr;
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ return std::unique_ptr<AST::LazyBooleanExpr> (
+ new AST::LazyBooleanExpr (std::move (left), std::move (right),
+ AST::LazyBooleanExpr::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,
+ std::vector<AST::Attribute> 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_slow ();
+
+ 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,
+ std::vector<AST::Attribute> 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_ASSIG - 1, std::vector<AST::Attribute> (), 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_slow ();
+
+ return std::unique_ptr<AST::AssignmentExpr> (
+ new AST::AssignmentExpr (std::move (left), std::move (right), 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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (),
+ 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_slow ();
+
+ return std::unique_ptr<AST::CompoundAssignmentExpr> (
+ new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+ AST::CompoundAssignmentExpr::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,
+ std::vector<AST::Attribute> 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))
+ {
+ rust_error_at (tok->get_locus (), "failed to skip 'await' in await expr "
+ "- this is probably a deep issue.");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // TODO: check inside async block in semantic analysis
+ Location locus = expr_to_await->get_locus_slow ();
+
+ 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,
+ std::vector<AST::Attribute> 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)
+ std::unique_ptr<AST::Expr> right
+ = parse_expr (LBP_DOT_DOT, std::vector<AST::Attribute> (), restrictions);
+
+ Location locus = left->get_locus_slow ();
+
+ 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, std::vector<AST::Attribute> outer_attrs ATTRIBUTE_UNUSED)
+{
+ // 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, std::vector<AST::Attribute> ());
+
+ 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,
+ std::vector<AST::Attribute> 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, std::vector<AST::Attribute> (), restrictions);
+ if (right == nullptr)
+ return nullptr;
+ // FIXME: make non-associative
+
+ // TODO: check types. actually, do so during semantic analysis
+ Location locus = left->get_locus_slow ();
+
+ 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, std::vector<AST::Attribute> 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,
+ std::vector<AST::Attribute> 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
+ int index_int = atoi (index.c_str ());
+
+ Location locus = tuple_expr->get_locus_slow ();
+
+ 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 tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> array_expr,
+ std::vector<AST::Attribute> outer_attrs, ParseRestrictions restrictions)
+{
+ // parse RHS (as tok has already been consumed in parse_expression)
+ std::unique_ptr<AST::Expr> index_expr
+ = parse_expr (LBP_ARRAY_REF, std::vector<AST::Attribute> (), restrictions);
+ 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_slow ();
+
+ 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,
+ std::vector<AST::Attribute> outer_attrs,
+ ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+ // get field name identifier (assume that this is a field access expr and not
+ // say await)
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ Identifier ident = ident_tok->get_str ();
+
+ Location locus = struct_expr->get_locus_slow ();
+
+ // 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,
+ std::vector<AST::Attribute> outer_attrs,
+ ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+ // parse path expr segment
+ AST::PathExprSegment segment = parse_path_expr_segment ();
+ if (segment.is_error ())
+ {
+ rust_error_at (tok->get_locus (),
+ "failed to parse path expr segment of method call expr");
+ 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse method param in method call");
+ 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_slow ();
+
+ 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 tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> function_expr,
+ std::vector<AST::Attribute> outer_attrs,
+ ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+ // 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse function param in function call");
+ 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_slow ();
+
+ 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, std::vector<AST::Attribute> outer_attrs)
+{
+ // 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 ())
+ {
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse simple path in macro invocation");
+ return nullptr;
+ }
+
+ AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
+
+ fprintf (stderr, "successfully parsed macro invocation (via partial)\n");
+
+ Location macro_locus = converted_path.get_locus ();
+
+ return std::unique_ptr<AST::MacroInvocation> (
+ new AST::MacroInvocation (std::move (converted_path), std::move (tok_tree),
+ std::move (outer_attrs), macro_locus));
+}
+
+/* Parses a struct expr struct with a path in expression already parsed (but not
+ * '{' token). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::StructExprStruct>
+Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
+ AST::PathInExpression path, std::vector<AST::Attribute> 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
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse struct (or enum) expr field");
+ return nullptr;
+ }
+
+ // DEBUG:
+ fprintf (stderr,
+ "struct/enum expr field validated to not be null \n");
+
+ fields.push_back (std::move (field));
+
+ // DEBUG:
+ fprintf (stderr, "struct/enum expr field pushed back \n");
+
+ if (lexer.peek_token ()->get_id () != COMMA)
+ {
+ // DEBUG:
+ fprintf (stderr, "lack of comma detected in struct/enum expr "
+ "fields - break \n");
+ break;
+ }
+ lexer.skip_token ();
+
+ // DEBUG:
+ fprintf (stderr, "struct/enum expr fields comma skipped \n");
+
+ t = lexer.peek_token ();
+ }
+
+ // DEBUG:
+ fprintf (stderr, "struct/enum expr about to parse struct base \n");
+
+ // parse struct base if it exists
+ AST::StructBase struct_base = AST::StructBase::error ();
+ if (lexer.peek_token ()->get_id () == DOT_DOT)
+ {
+ lexer.skip_token ();
+
+ // parse required struct base expr
+ std::unique_ptr<AST::Expr> base_expr = parse_expr ();
+ if (base_expr == nullptr)
+ {
+ rust_error_at (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse struct base expression in struct "
+ "expression");
+ return nullptr;
+ }
+
+ // DEBUG:
+ fprintf (stderr,
+ "struct/enum expr - parsed and validated base expr \n");
+
+ struct_base = AST::StructBase (std::move (base_expr));
+
+ // DEBUG:
+ fprintf (stderr, "assigned struct base to new struct base \n");
+ }
+
+ if (!skip_token (RIGHT_CURLY))
+ {
+ return nullptr;
+ }
+
+ // DEBUG:
+ fprintf (
+ stderr,
+ "struct/enum expr skipped right curly - done and ready to return \n");
+
+ 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:
+ rust_error_at (t->get_locus (),
+ "unrecognised token '%s' in struct (or enum) expression - "
+ "expected '}', identifier, int literal, or '..'",
+ t->get_token_description ());
+ return nullptr;
+ }
+}
+
+/* Parses a struct expr tuple with a path in expression already parsed (but not
+ * '(' token). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CallExpr>
+Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
+ AST::PathInExpression path, std::vector<AST::Attribute> outer_attrs)
+{
+ if (!skip_token (LEFT_PAREN))
+ {
+ return nullptr;
+ }
+
+ std::vector<AST::Attribute> 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)
+ {
+ rust_error_at (t->get_locus (),
+ "failed to parse expression in struct "
+ "(or enum) expression tuple");
+ 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
+ fprintf (stderr, "current peek token when starting path pratt parse: '%s'\n",
+ 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:
+ rust_error_at (tok->get_locus (),
+ "unrecognised token '%s' 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 (initial_str, tok->get_locus (),
+ std::move (generic_args));
+ }
+ if (initial_segment.is_error ())
+ {
+ // skip after somewhere?
+ // don't necessarily throw error but yeah
+
+ // DEBUG
+ fprintf (stderr, "initial segment is error - returning null\n");
+
+ 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?
+ rust_error_at (t->get_locus (),
+ "could not parse path expression segment");
+ return AST::PathInExpression::create_error ();
+ }
+
+ segments.push_back (std::move (segment));
+
+ t = lexer.peek_token ();
+ }
+
+ // DEBUG:
+ fprintf (
+ stderr,
+ "current token (just about to return path to null denotation): '%s'\n",
+ 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, std::vector<AST::Attribute> 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?
+ rust_error_at (t->get_locus (),
+ "could not parse closure param");
+ 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:
+ rust_error_at (
+ tok->get_locus (),
+ "unexpected token '%s' 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
+ rust_error_at (tok->get_locus (), "failed to parse type for closure");
+ // skip somewhere?
+ return nullptr;
+ }
+
+ // parse block expr, which is required
+ std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
+ if (block == nullptr)
+ {
+ // error
+ rust_error_at (lexer.peek_token ()->get_locus (),
+ "failed to parse block expr in closure");
+ // 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)
+ {
+ rust_error_at (tok->get_locus (),
+ "failed to parse expression in closure");
+ // 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,
+ std::vector<AST::Attribute> outer_attrs,
+ ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+ // only works on float literals
+ if (tok->get_id () != FLOAT_LITERAL)
+ {
+ return nullptr;
+ }
+
+ // DEBUG:
+ fprintf (stderr, "exact string form of float: '%s'\n",
+ 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_slow ();
+
+ 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 ()
+{
+ /* 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)
+ {
+ 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 ();
+
+ fprintf (stderr, "<id=%s%s, %s\n", tok->token_id_to_str (),
+ has_text ? (std::string (", text=") + tok->get_str ()
+ + std::string (", typehint=")
+ + std::string (tok->get_type_hint_str ()))
+ .c_str ()
+ : "",
+ lexer.get_line_map ()->to_string (loc).c_str ());
+
+ if (tok->get_id () == Rust::END_OF_FILE)
+ break;
+
+ 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)
+{
+ // print crate "as string", which then calls each item as string, etc.
+ fprintf (stderr, "%s", crate.as_string ().c_str ());
+}
+} // namespace Rust
diff --git a/gcc/rust/parse/rust-parse.cc b/gcc/rust/parse/rust-parse.cc
index 4624403..883735f 100644
--- a/gcc/rust/parse/rust-parse.cc
+++ b/gcc/rust/parse/rust-parse.cc
@@ -27,13577 +27,9 @@
#include <algorithm> // for std::find
-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_DOT = 80, /* method call and field expr have different precedence now
- // */
-
- 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
- PREC_CLOSURE = -40, // used for closures
- PREC_JUMP = -30, // used for break, continue, return, and yield
- PREC_RANGE = -10, // used for range (although weird comment in rustc about this)
- PREC_BINOP = FROM_ASSOC_OP,
- // used for binary operators mentioned below - also cast, colon (type), assign, assign_op
- PREC_PREFIX = 50, // used for box, address_of, let, unary (again, weird comment on let)
- PREC_POSTFIX = 60, // used for await, call, method call, field, index, try, inline asm, macro invocation
- PREC_PAREN = 99, // used for array, repeat, tuple, literal, path, paren, if, while, for, 'loop', match, block, try block, async, struct
- 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).
-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. */
-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.
-bool
-Parser::skip_generics_right_angle ()
-{
- // 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: {
- /* shit. preferred HACK would be to replace this token in stream with
- * '>', but may not be possible at this point. */
- // FIXME: ensure locations aren't messed up
- TokenPtr right_angle = Token::make (RIGHT_ANGLE, tok->get_locus () + 1);
- lexer.replace_current_token (right_angle);
- return true;
- }
- case GREATER_OR_EQUAL: {
- // another HACK - replace with equal (as assignment intended, probably)
- /* FIXME: is this even required? how many people wouldn't leave a space?
- * - apparently rustc has this feature */
- // FIXME: ensure locations aren't messed up
- TokenPtr equal = Token::make (EQUAL, tok->get_locus () + 1);
- lexer.replace_current_token (equal);
- return true;
- }
- case RIGHT_SHIFT_EQ: {
- // another HACK - replace with greater or equal
- // FIXME: again, is this really required? rustc has the feature, though
- // FIXME: ensure locations aren't messed up
- TokenPtr greater_equal
- = Token::make (GREATER_OR_EQUAL, tok->get_locus () + 1);
- lexer.replace_current_token (greater_equal);
- return true;
- }
- default:
- rust_error_at (tok->get_locus (),
- "expected '>' at end of generic argument - found '%s'",
- 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. */
-int
-Parser::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:
- fprintf (
- stderr,
- "possible error - looked up LBP of scope resolution operator. should "
- "be handled elsewhere. \n");
- 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 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
-
- // anything that can't appear in an infix position is given lowest priority
- default:
- return LBP_LOWEST;
- }
-}
-
-// Returns true when current token is EOF.
-bool
-Parser::done_end_of_file ()
-{
- const_TokenPtr t = lexer.peek_token ();
- return (t->get_id () == END_OF_FILE);
-}
-
-// Parses a crate (compilation unit) - entry point
-AST::Crate
-Parser::parse_crate ()
-{
- /* TODO: determine if has utf8bom and shebang. Currently, they are eliminated
- * by the lexing phase. Neither are useful for the compiler anyway, so maybe a
- * better idea would be to eliminate
- * the has_utf8bom and has_shebang variables from the crate data structure. */
- bool has_utf8bom = false;
- bool has_shebang = false;
-
- // parse inner attributes
- ::std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
-
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse item in crate");
- items = ::std::vector< ::std::unique_ptr<AST::Item> > ();
- break;
- }
-
- items.push_back (::std::move (item));
-
- t = lexer.peek_token ();
- }
-
- return AST::Crate (::std::move (items), ::std::move (inner_attrs),
- has_utf8bom, has_shebang);
-}
-
-// Parse a contiguous block of inner attributes.
-::std::vector<AST::Attribute>
-Parser::parse_inner_attributes ()
-{
- ::std::vector<AST::Attribute> inner_attributes;
-
- while (lexer.peek_token ()->get_id () == HASH)
- {
- 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;
- }
- }
-
- return inner_attributes;
-}
-
-// Parse a single inner attribute.
-AST::Attribute
-Parser::parse_inner_attribute ()
-{
- if (lexer.peek_token ()->get_id () != HASH)
- return AST::Attribute::create_empty ();
-
- lexer.skip_token ();
-
- if (lexer.peek_token ()->get_id () != EXCLAM)
- return AST::Attribute::create_empty ();
-
- lexer.skip_token ();
-
- if (lexer.peek_token ()->get_id () != LEFT_SQUARE)
- 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 the body of an attribute (inner or outer).
-AST::Attribute
-Parser::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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "empty simple path in attribute");
-
- // 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);
-}
-
-// Parses a SimplePath AST node
-AST::SimplePath
-Parser::parse_simple_path ()
-{
- bool has_opening_scope_resolution = false;
- Location locus = Linemap::unknown_location ();
-
- // 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 (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 (new_segment);
- }
-
- // DEBUG: check for any empty segments
- for (const auto &seg : segments)
- {
- if (seg.is_error ())
- {
- fprintf (stderr,
- "when parsing simple path, somehow empty path segment was "
- "not filtered out. Path "
- "begins with '%s' \n",
- segments.at (0).as_string ().c_str ());
- }
- }
-
- return AST::SimplePath (::std::move (segments), has_opening_scope_resolution,
- locus);
-}
-
-// Parses a single SimplePathSegment (does not handle the scope resolution
-// operators)
-AST::SimplePathSegment
-Parser::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 (::std::string ("super"), t->get_locus ());
- case SELF:
- lexer.skip_token ();
-
- return AST::SimplePathSegment (::std::string ("self"), t->get_locus ());
- case CRATE:
- lexer.skip_token ();
-
- return AST::SimplePathSegment (::std::string ("crate"), t->get_locus ());
- case DOLLAR_SIGN:
- if (lexer.peek_token (1)->get_id () == CRATE)
- {
- lexer.skip_token (1);
-
- return AST::SimplePathSegment (::std::string ("$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.
-AST::PathIdentSegment
-Parser::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 ());
- case SUPER:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (::std::string ("super"));
- case SELF:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (::std::string ("self"));
- case SELF_ALIAS:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (::std::string ("Self"));
- case CRATE:
- lexer.skip_token ();
-
- return AST::PathIdentSegment (::std::string ("crate"));
- case DOLLAR_SIGN:
- if (lexer.peek_token (1)->get_id () == CRATE)
- {
- lexer.skip_token (1);
-
- return AST::PathIdentSegment (::std::string ("$crate"));
- }
- 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)
-::std::unique_ptr<AST::AttrInput>
-Parser::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::DelimTokenTree> 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 ())
- {
- rust_error_at (
- t->get_locus (),
- "unknown token '%s' in attribute body - literal expected",
- t->get_token_description ());
- skip_after_end_attribute ();
- return NULL;
- }
-
- 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_locus ());
-
- ::std::unique_ptr<AST::AttrInputLiteral> 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 NULL;
- default:
- rust_error_at (t->get_locus (),
- "unknown token '%s' in attribute body - attribute input "
- "or none expected",
- t->get_token_description ());
- skip_after_end_attribute ();
- return NULL;
- }
- 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
-AST::DelimTokenTree
-Parser::parse_delim_token_tree ()
-{
- // DEBUG
- fprintf (stderr, "new delim token tree parsing begun\n");
-
- 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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - 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;
-
- // repeat loop until finding the matching delimiter
- t = lexer.peek_token ();
- while (!token_id_matches_delims (t->get_id (), delim_type))
- {
- ::std::unique_ptr<AST::TokenTree> tok_tree = parse_token_tree ();
-
- if (tok_tree == NULL)
- {
- // TODO: is this error handling appropriate?
- rust_error_at (
- t->get_locus (),
- "failed to parse token tree in delimited token tree - found '%s'",
- t->get_token_description ());
- return AST::DelimTokenTree::create_empty ();
- }
-
- token_trees_in_tree.push_back (::std::move (tok_tree));
-
- // lexer.skip_token();
- t = lexer.peek_token ();
- }
-
- 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
- fprintf (stderr,
- "finished parsing new delim token tree - peeked token is now "
- "'%s' while t is '%s'\n",
- lexer.peek_token ()->get_token_description (),
- t->get_token_description ());
-
- return token_tree;
- }
- else
- {
- // tokens don't match opening delimiters, so produce error
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - expecting closing delimiter '%s' "
- "(for a delimited token tree)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
-
- /* 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. */
-::std::unique_ptr<AST::TokenTree>
-Parser::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
- rust_error_at (t->get_locus (),
- "unexpected closing delimiter '%s' - token tree requires "
- "either paired delimiters "
- "or non-delimiter tokens",
- t->get_token_description ());
- lexer.skip_token ();
- return NULL;
- default:
- // parse token itself as TokenTree
- lexer.skip_token ();
- // TODO: fix that token constructor, possibly with c++11 features
- return ::std::unique_ptr<AST::Token> (new AST::Token (t));
- }
-}
-
-/* Parses a sequence of items within a module or the implicit top-level module
- * in a crate. Note: this is not currently used as parsing an item sequence
- * individually is pretty simple and allows for better error diagnostics and
- * detection. */
-::std::vector< ::std::unique_ptr<AST::Item> >
-Parser::parse_items ()
-{
- ::std::vector< ::std::unique_ptr<AST::Item> > items;
-
- // TODO: replace with do-while loop?
- // infinite loop to save on comparisons (may be a tight loop) - breaks when
- // next item is null
- while (true)
- {
- ::std::unique_ptr<AST::Item> item = parse_item (false);
-
- if (item != NULL)
- {
- items.push_back (::std::move (item));
- }
- else
- {
- break;
- }
- }
-
- return items;
-}
-
-// Parses a single item
-::std::unique_ptr<AST::Item>
-Parser::parse_item (bool called_from_statement)
-{
- // has a "called_from_statement" parameter for better error message handling
-
- // parse outer attributes for item
- ::std::vector<AST::Attribute> 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
- return NULL;
- 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")
- {
- 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 ();
- // TODO: find out how to disable gcc "implicit fallthrough" warning
- default:
- // otherwise unrecognised
- // return parse_macro_item(::std::move(outer_attrs));
- rust_error_at (t->get_locus (), "unrecognised token '%s' for start of %s",
- t->get_token_description (),
- called_from_statement ? "statement" : "item");
- // skip somewhere?
- return NULL;
- break;
- }
-}
-
-// Parses a contiguous block of outer attributes.
-::std::vector<AST::Attribute>
-Parser::parse_outer_attributes ()
-{
- ::std::vector<AST::Attribute> outer_attributes;
-
- while (lexer.peek_token ()->get_id () == HASH)
- {
- 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;
- }
- }
-
- return outer_attributes;
-
- // TODO: this shares basically all code with parse_inner_attributes except
- // function call find way of making it more modular?
-}
-
-// Parse a single outer attribute.
-AST::Attribute
-Parser::parse_outer_attribute ()
-{
- /* 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
- rust_error_at (lexer.peek_token ()->get_locus (),
- "token '!' found, indicating inner attribute "
- "definition. Inner attributes are not "
- "possible at this location.");
- } // TODO: are there any cases where this wouldn't be an 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).
-::std::unique_ptr<AST::VisItem>
-Parser::parse_vis_item (::std::vector<AST::Attribute> 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:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in some sort of extern production",
- t->get_token_description ());
- lexer.skip_token (2); // TODO: is this right thing to do?
- return NULL;
- }
- default:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in some sort of extern production",
- t->get_token_description ());
- lexer.skip_token (1); // TODO: is this right thing to do?
- return NULL;
- }
- 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:
- // TODO: ensure std::string and literal comparison works
- if (t->get_str () == "union")
- {
- 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:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in some sort of const production",
- t->get_token_description ());
- lexer.skip_token (1); // TODO: is this right thing to do?
- return NULL;
- }
- 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:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in some sort of unsafe production",
- t->get_token_description ());
- lexer.skip_token (1); // TODO: is this right thing to do?
- return NULL;
- }
- 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 NULL;
-}
-
-// Parses a MacroItem (either a MacroInvocationSemi or MacroRulesDefinition).
-::std::unique_ptr<AST::MacroItem>
-Parser::parse_macro_item (::std::vector<AST::Attribute> 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 () == ::std::string ("macro_rules"))
- {
- return parse_macro_rules_def (::std::move (outer_attrs));
- }
- else
- {
- // DEBUG: TODO: remove
- fprintf (stderr,
- "DEBUG - parse_macro_item called and token is not macro_rules");
- if (t->get_id () == IDENTIFIER)
- {
- fprintf (stderr,
- "just add to last error: token is not macro_rules and is "
- "instead '%s'",
- t->get_str ().c_str ());
- }
- else
- {
- fprintf (stderr,
- "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.
-::std::unique_ptr<AST::MacroRulesDefinition>
-Parser::parse_macro_rules_def (::std::vector<AST::Attribute> 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")
- {
- rust_error_at (
- t->get_locus (),
- "macro rules definition does not start with 'macro_rules'");
- // skip after somewhere?
- return NULL;
- }
- lexer.skip_token ();
- Location macro_locus = t->get_locus ();
-
- if (!skip_token (EXCLAM))
- {
- // skip after somewhere?
- return NULL;
- }
-
- // parse macro name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == NULL)
- {
- return NULL;
- }
- Identifier rule_name = ident_tok->get_str ();
-
- // DEBUG
- fprintf (stderr, "in macro rules def, about to parse parens.\n");
-
- // 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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - expecting delimiters (for a "
- "macro rules definition)",
- t->get_token_description ());
- return NULL;
- }
- 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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "required first macro rule in macro rules definition "
- "could not be parsed");
- // skip after somewhere?
- return NULL;
- }
- macro_rules.push_back (::std::move (initial_rule));
-
- // DEBUG
- fprintf (stderr, "successfully pushed back initial macro rule\n");
-
- 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
- fprintf (
- stderr,
- "broke out of parsing macro rules loop due to finding delim\n");
-
- break;
- }
-
- // try to parse next rule
- AST::MacroRule rule = parse_macro_rule ();
- if (rule.is_error ())
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse macro rule in macro rules definition");
- return NULL;
- }
-
- macro_rules.push_back (::std::move (rule));
-
- // DEBUG
- fprintf (stderr, "successfully pushed back another macro rule\n");
-
- 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
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - expecting closing delimiter '%s' "
- "(for a macro rules "
- "definition)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
-
- /* return empty macro definiton despite possibly parsing mostly valid one
- * - TODO is this a good idea? */
- return NULL;
- }
-}
-
-// Parses a semi-coloned (except for full block) macro invocation item.
-::std::unique_ptr<AST::MacroInvocationSemi>
-Parser::parse_macro_invocation_semi (::std::vector<AST::Attribute> outer_attrs)
-{
- Location macro_locus = lexer.peek_token ()->get_locus ();
- AST::SimplePath path = parse_simple_path ();
-
- if (!skip_token (EXCLAM))
- {
- // skip after somewhere?
- return NULL;
- }
-
- // 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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - expecting delimiters (for a "
- "macro invocation semi body)",
- t->get_token_description ());
- return NULL;
- }
- lexer.skip_token ();
-
- // parse actual token trees
- ::std::vector< ::std::unique_ptr<AST::TokenTree> > token_trees;
-
- 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 == NULL)
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse token tree for macro invocation semi - found '%s'",
- t->get_token_description ());
- return NULL;
- }
-
- token_trees.push_back (::std::move (tree));
-
- 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 invocation semis
- if (!skip_token (SEMICOLON))
- {
- // as this is the end, allow recovery (probably) - may change
- return ::std::unique_ptr<AST::MacroInvocationSemi> (
- new AST::MacroInvocationSemi (::std::move (path), delim_type,
- ::std::move (token_trees),
- ::std::move (outer_attrs),
- macro_locus));
- }
- }
-
- // DEBUG:
- fprintf (stderr,
- "skipped token is '%s', next token (current peek) is '%s'\n",
- t->get_token_description (),
- lexer.peek_token ()->get_token_description ());
-
- return ::std::unique_ptr<AST::MacroInvocationSemi> (
- new AST::MacroInvocationSemi (::std::move (path), delim_type,
- ::std::move (token_trees),
- ::std::move (outer_attrs), macro_locus));
- }
- else
- {
- // tokens don't match opening delimiters, so produce error
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - expecting closing delimiter '%s' "
- "(for a macro invocation "
- "semi)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
-
- /* return empty macro invocation despite possibly parsing mostly valid one
- * - TODO is this a good idea? */
- return NULL;
- }
-}
-
-// Parses a non-semicoloned macro invocation (i.e. as pattern or expression).
-::std::unique_ptr<AST::MacroInvocation>
-Parser::parse_macro_invocation (::std::vector<AST::Attribute> outer_attrs)
-{
- // parse macro path
- AST::SimplePath macro_path = parse_simple_path ();
- if (macro_path.is_empty ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse macro invocation path");
- // skip?
- return NULL;
- }
-
- if (!skip_token (EXCLAM))
- {
- // skip after somewhere?
- return NULL;
- }
-
- // 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 (::std::move (macro_path),
- ::std::move (delim_tok_tree),
- ::std::move (outer_attrs), macro_locus));
-}
-
-// Parses a macro rule definition - does not parse semicolons.
-AST::MacroRule
-Parser::parse_macro_rule ()
-{
- // DEBUG
- fprintf (stderr, "begun parsing macro rule\n");
-
- // parse macro matcher
- AST::MacroMatcher matcher = parse_macro_matcher ();
-
- // DEBUG
- fprintf (stderr, "managed to get past parsing macro matcher\n");
-
- if (matcher.is_error ())
- {
- return AST::MacroRule::create_error ();
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed macro matcher\n");
-
- if (!skip_token (MATCH_ARROW))
- {
- // skip after somewhere?
- return AST::MacroRule::create_error ();
- }
-
- // DEBUG
- fprintf (stderr, "successfully skipped match arrow\n");
-
- // parse transcriber (this is just a delim token tree)
- AST::DelimTokenTree transcribe_tree = parse_delim_token_tree ();
-
- // DEBUG
- fprintf (stderr, "successfully parsed transcribe tree\n");
-
- AST::MacroTranscriber transcriber (::std::move (transcribe_tree));
-
- // DEBUG
- fprintf (stderr,
- "successfully parsed macro transcriber - returning macro rule\n");
-
- return AST::MacroRule (::std::move (matcher), ::std::move (transcriber));
-}
-
-// Parses a macro matcher (part of a macro rule definition).
-AST::MacroMatcher
-Parser::parse_macro_matcher ()
-{
- // save delim type to ensure it is reused later
- AST::DelimType delim_type = AST::PARENS;
-
- // DEBUG
- fprintf (stderr, "begun parsing macro matcher\n");
-
- // 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:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' - expecting delimiters (for a macro matcher)",
- t->get_token_description ());
- return AST::MacroMatcher::create_error ();
- }
- lexer.skip_token ();
-
- // parse actual macro matches
- ::std::vector< ::std::unique_ptr<AST::MacroMatch> > 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 == NULL)
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse macro match for macro matcher - found '%s'",
- t->get_token_description ());
- return AST::MacroMatcher::create_error ();
- }
-
- matches.push_back (::std::move (match));
-
- // DEBUG
- fprintf (stderr, "pushed back a match in macro matcher\n");
-
- 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));
- }
- else
- {
- // tokens don't match opening delimiters, so produce error
- rust_error_at (t->get_locus (),
- "unexpected token '%s' - expecting closing delimiter '%s' "
- "(for a macro matcher)",
- t->get_token_description (),
- (delim_type == AST::PARENS
- ? ")"
- : (delim_type == AST::SQUARE ? "]" : "}")));
-
- /* return error macro matcher despite possibly parsing mostly correct one?
- * TODO is this the best idea? */
- return AST::MacroMatcher::create_error ();
- }
-}
-
-// Parses a macro match (syntax match inside a matcher in a macro rule).
-::std::unique_ptr<AST::MacroMatch>
-Parser::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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse macro matcher in macro match");
- return NULL;
- }
- 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 IDENTIFIER:
- // macro fragment
- return parse_macro_match_fragment ();
- case LEFT_PAREN:
- // macro repetition
- return parse_macro_match_repetition ();
- default:
- // error: unrecognised
- rust_error_at (t2->get_locus (),
- "unrecognised token combination '$%s' at start of "
- "macro match - did you "
- "mean '$identifier' or '$('?",
- t2->get_token_description ());
- // skip somewhere?
- return NULL;
- }
- }
- case RIGHT_PAREN:
- case RIGHT_SQUARE:
- case RIGHT_CURLY:
- // not allowed
- rust_error_at (t->get_locus (),
- "closing delimiters like '%s' are not allowed at the "
- "start of a macro match",
- t->get_token_description ());
- // skip somewhere?
- return NULL;
- default:
- // just the token
- lexer.skip_token ();
- return ::std::unique_ptr<AST::Token> (new AST::Token (t));
- }
-}
-
-// Parses a fragment macro match.
-::std::unique_ptr<AST::MacroMatchFragment>
-Parser::parse_macro_match_fragment ()
-{
- skip_token (DOLLAR_SIGN);
-
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "missing identifier in macro match fragment");
- return NULL;
- }
- Identifier ident = ident_tok->get_str ();
-
- if (!skip_token (COLON))
- {
- // skip after somewhere?
- return NULL;
- }
-
- // get MacroFragSpec for macro
- const_TokenPtr t = expect_token (IDENTIFIER);
- AST::MacroFragSpec frag = AST::get_frag_spec_from_str (t->get_str ());
- if (frag == AST::INVALID)
- {
- rust_error_at (t->get_locus (),
- "invalid fragment specifier '%s' in fragment macro match",
- t->get_str ().c_str ());
- return NULL;
- }
-
- return ::std::unique_ptr<AST::MacroMatchFragment> (
- new AST::MacroMatchFragment (::std::move (ident), frag));
-}
-
-// Parses a repetition macro match.
-::std::unique_ptr<AST::MacroMatchRepetition>
-Parser::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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "could not parse required first macro match in macro match repetition");
- // skip after somewhere?
- return NULL;
- }
- 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse macro match in macro match repetition");
- return NULL;
- }
-
- matches.push_back (::std::move (match));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip after somewhere?
- return NULL;
- }
-
- t = lexer.peek_token ();
- // see if separator token exists
- ::std::unique_ptr<AST::Token> separator = NULL;
- 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 (t));
- lexer.skip_token ();
- break;
- }
-
- // parse repetition operator
- t = lexer.peek_token ();
- AST::MacroMatchRepetition::MacroRepOp op
- = AST::MacroMatchRepetition::ASTERISK;
- switch (t->get_id ())
- {
- case ASTERISK:
- op = AST::MacroMatchRepetition::ASTERISK;
- lexer.skip_token ();
- break;
- case PLUS:
- op = AST::MacroMatchRepetition::PLUS;
- lexer.skip_token ();
- break;
- case QUESTION_MARK:
- op = AST::MacroMatchRepetition::QUESTION_MARK;
- lexer.skip_token ();
- break;
- default:
- rust_error_at (t->get_locus (),
- "expected macro repetition operator ('*', '+', or '?') in "
- "macro match - found '%s'",
- t->get_token_description ());
- // skip after somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::MacroMatchRepetition> (
- new AST::MacroMatchRepetition (::std::move (matches), op,
- ::std::move (separator)));
-}
-
-// Parses a visibility syntactical production (i.e. creating a non-default
-// visibility)
-AST::Visibility
-Parser::parse_visibility ()
-{
- // check for no visibility
- if (lexer.peek_token ()->get_id () != PUB)
- {
- return AST::Visibility::create_error ();
- }
-
- 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 ();
-
- switch (t->get_id ())
- {
- case CRATE:
- lexer.skip_token ();
-
- skip_token (RIGHT_PAREN);
-
- return AST::Visibility::create_crate ();
- case SELF:
- lexer.skip_token ();
-
- skip_token (RIGHT_PAREN);
-
- return AST::Visibility::create_self ();
- case SUPER:
- lexer.skip_token ();
-
- skip_token (RIGHT_PAREN);
-
- return AST::Visibility::create_super ();
- case IN: {
- lexer.skip_token ();
-
- // parse the "in" path as well
- AST::SimplePath path = parse_simple_path ();
- if (path.is_empty ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "missing path in pub(in path) visibility");
- // skip after somewhere?
- return AST::Visibility::create_error ();
- }
-
- skip_token (RIGHT_PAREN);
-
- return AST::Visibility::create_in_path (::std::move (path));
- }
- default:
- rust_error_at (t->get_locus (), "unexpected token '%s' 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.
-::std::unique_ptr<AST::Module>
-Parser::parse_module (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (MOD);
-
- const_TokenPtr module_name = expect_token (IDENTIFIER);
- if (module_name == NULL)
- {
- return NULL;
- }
- Identifier name = module_name->get_str ();
-
- const_TokenPtr t = lexer.peek_token ();
-
- switch (t->get_id ())
- {
- case SEMICOLON:
- lexer.skip_token ();
-
- return ::std::unique_ptr<AST::ModuleNoBody> (
- new AST::ModuleNoBody (::std::move (name), ::std::move (vis),
- ::std::move (outer_attrs),
- locus)); // module name?
- case LEFT_CURLY: {
- lexer.skip_token ();
-
- // parse inner attributes
- ::std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
-
- // 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 == NULL)
- {
- rust_error_at (tok->get_locus (),
- "failed to parse item in module");
- return NULL;
- }
-
- items.push_back (::std::move (item));
-
- tok = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::ModuleBodied> (
- new AST::ModuleBodied (::std::move (name), locus, ::std::move (items),
- ::std::move (vis), ::std::move (inner_attrs),
- ::std::move (outer_attrs))); // module name?
- }
- default:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in module declaration/definition item",
- t->get_token_description ());
- lexer.skip_token ();
- return NULL;
- }
-}
-
-// Parses an extern crate declaration (dependency on external crate)
-::std::unique_ptr<AST::ExternCrate>
-Parser::parse_extern_crate (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- if (!skip_token (EXTERN_TOK))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- if (!skip_token (CRATE))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- /* 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 = ::std::string ("self");
- lexer.skip_token ();
- break;
- default:
- rust_error_at (crate_name_tok->get_locus (),
- "expecting crate name (identifier or 'self'), found '%s'",
- crate_name_tok->get_token_description ());
- skip_after_semicolon ();
- return NULL;
- }
-
- // 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 NULL;
- }
-
- 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 = ::std::string ("_");
- lexer.skip_token ();
- break;
- default:
- rust_error_at (as_name_tok->get_locus (),
- "expecting as clause name (identifier or '_'), found '%s'",
- as_name_tok->get_token_description ());
- skip_after_semicolon ();
- return NULL;
- }
-
- if (!skip_token (SEMICOLON))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- 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.
-::std::unique_ptr<AST::UseDeclaration>
-Parser::parse_use_decl (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- if (!skip_token (USE))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse use tree, which is required
- ::std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
- if (use_tree == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse use tree in use declaration");
- skip_after_semicolon ();
- return NULL;
- }
-
- if (!skip_token (SEMICOLON))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::UseTree>
-Parser::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
-
- /* due to implementation issues, parsing simple path removes any trailing
- * scope resolutions (or any, actually, if the use tree has no path
- * given), so we'll just assume that there's one there. */
- // Check anyway, but optional.
- if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
- {
- lexer.skip_token ();
- }
- /* Note that this implementation issue also makes it impossible to
- * determine at the moment whether the tree has GLOBAL or NO_PATH path
- * type. */
-
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case ASTERISK:
- // glob UseTree type
- lexer.skip_token ();
-
- // TODO: find way to determine whether GLOBAL or NO_PATH path type -
- // placeholder
- 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 == NULL)
- {
- 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 NULL;
- }
-
- // TODO: find way to determine whether GLOBAL or NO_PATH path type -
- // placeholder
- 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
- rust_error_at (t->get_locus (),
- "use declaration with rebind 'as' requires a valid "
- "simple path - none found.");
- skip_after_semicolon ();
- return NULL;
- default:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' in use tree with no valid "
- "simple path (i.e. list or "
- "glob use tree)",
- t->get_token_description ());
- skip_after_semicolon ();
- return NULL;
- }
- }
- else
- {
- /* Due to aforementioned implementation issues, the trailing :: token is
- * consumed by the path, so it can not be used as a disambiguator. */
-
- 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 == NULL)
- {
- 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 NULL;
- }
-
- 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,
- ::std::string ("_")));
- default:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in use tree with as clause - expected "
- "identifier or '_'",
- t->get_token_description ());
- skip_after_semicolon ();
- return NULL;
- }
- }
- 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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' in use tree with valid path",
- t->get_token_description ());
- // skip_after_semicolon();
- return NULL;
- }
- }
-}
-
-// Parses a function (not a method).
-::std::unique_ptr<AST::Function>
-Parser::parse_function (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- skip_after_next_block ();
- return NULL;
- }
- 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))
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "function declaration missing opening parentheses before "
- "parameter list");
- skip_after_next_block ();
- return NULL;
- }
-
- // 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 ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "function declaration missing closing parentheses after "
- "parameter list");
- skip_after_next_block ();
- return NULL;
- }
-
- // 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).
-AST::FunctionQualifiers
-Parser::parse_function_qualifiers ()
-{
- AST::FunctionQualifiers::AsyncConstStatus const_status
- = AST::FunctionQualifiers::NONE;
- // bool has_const = false;
- 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 ();
- switch (t->get_id ())
- {
- case CONST:
- lexer.skip_token ();
- const_status = AST::FunctionQualifiers::CONST;
- break;
- case ASYNC:
- lexer.skip_token ();
- const_status = AST::FunctionQualifiers::ASYNC;
- 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 (const_status, has_unsafe, has_extern,
- ::std::move (abi));
-}
-
-// Parses generic (lifetime or type) params inside angle brackets (optional).
-::std::vector< ::std::unique_ptr<AST::GenericParam> >
-Parser::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:
- fprintf (stderr, "skipped left angle in generic param\n");
-
- ::std::vector< ::std::unique_ptr<AST::GenericParam> > generic_params
- = parse_generic_params ();
-
- // DEBUG:
- fprintf (stderr,
- "finished parsing actual generic params (i.e. inside angles)\n");
-
- if (!skip_generics_right_angle ())
- {
- // DEBUG
- fprintf (stderr, "failed to skip generics right angle - returning empty "
- "generic params\n");
-
- return ::std::vector< ::std::unique_ptr<AST::GenericParam> > ();
- }
-
- return generic_params;
-}
-
-/* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost
- * always parse_generic_params_in_angles is what is wanted. */
-::std::vector< ::std::unique_ptr<AST::GenericParam> >
-Parser::parse_generic_params ()
-{
- ::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
-
- // DEBUG
- fprintf (stderr,
- "starting to parse generic params (inside angle brackets)\n");
-
- // 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 ();
-
- // HACK: generic params always in angle brackets with current syntax, so have
- // that as end char
- const_TokenPtr t = lexer.peek_token ();
- // parse lifetime params
- while (!is_right_angle_tok (t->get_id ()))
- {
- // HACK: reimpl of lifetime param parsing
- AST::Attribute outer_attr = parse_outer_attribute ();
-
- // move attribute outward if type param
- if (lexer.peek_token ()->get_id () != LIFETIME)
- {
- parsed_outer_attr = ::std::move (outer_attr);
-
- // DEBUG
- fprintf (
- stderr,
- "broke from parsing lifetime params as next token isn't lifetime - "
- "saved attribute\n");
-
- break;
- }
-
- Location locus = lexer.peek_token ()->get_locus ();
- AST::Lifetime lifetime = parse_lifetime ();
-
- // DEBUG
- fprintf (stderr, "parsed lifetime in lifetime params\n");
-
- // parse optional bounds
- ::std::vector<AST::Lifetime> lifetime_bounds;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
- // parse required bounds
- lifetime_bounds = parse_lifetime_bounds ();
- }
-
- ::std::unique_ptr<AST::LifetimeParam> param (
- new AST::LifetimeParam (::std::move (lifetime), locus,
- ::std::move (lifetime_bounds),
- ::std::move (outer_attr)));
- generic_params.push_back (::std::move (param));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- // parse type params (reimpl required for first one but not others)
- if (!is_right_angle_tok (lexer.peek_token ()->get_id ())
- && !parsed_outer_attr.is_empty ())
- {
- // DEBUG
- fprintf (stderr, "as parsed outer attr isn't empty, started parsing type "
- "param reimpl\n");
-
- // reimpl as type param definitely exists
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- if (ident_tok == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse identifier in type param in generic params");
- return ::std::vector< ::std::unique_ptr<AST::GenericParam> > ();
- }
- Identifier ident = ident_tok->get_str ();
-
- // parse optional bounds
- ::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 ();
- }
-
- // parse optional type
- ::std::unique_ptr<AST::Type> type = NULL;
- if (lexer.peek_token ()->get_id () == EQUAL)
- {
- lexer.skip_token ();
-
- // parse required type
- type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse type in type param in generic params");
- return ::std::vector< ::std::unique_ptr<AST::GenericParam> > ();
- }
- }
-
- ::std::unique_ptr<AST::TypeParam> param (
- new AST::TypeParam (::std::move (ident), ident_tok->get_locus (),
- ::std::move (type_param_bounds), ::std::move (type),
- ::std::move (parsed_outer_attr)));
- generic_params.push_back (::std::move (param));
-
- // handle comma
- if (lexer.peek_token ()->get_id () == COMMA)
- {
- lexer.skip_token ();
- }
- }
-
- // DEBUG
- fprintf (
- stderr,
- "about to start parsing normally-parsed type params in generic params\n");
-
- // parse rest of type params - reimpl due to right angle tokens
- t = lexer.peek_token ();
- while (!is_right_angle_tok (t->get_id ()))
- {
- ::std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
-
- if (type_param == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse type param in generic params");
- return ::std::vector< ::std::unique_ptr<AST::GenericParam> > ();
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed type param\n");
-
- generic_params.push_back (::std::move (type_param));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- // skip commas, including trailing commas
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- // old code
- /*
- // parse lifetime params (optional), allowed to end with a trailing comma
- ::std::vector< ::std::unique_ptr<AST::LifetimeParam> > lifetime_params
- = parse_lifetime_params();
- if (!lifetime_params.empty()) {
- // C++11 code:
- generic_params.insert(generic_params.end(),
- ::std::make_move_iterator(lifetime_params.begin()),
- ::std::make_move_iterator(lifetime_params.end()));
- }
-
- // parse type params (optional)
- ::std::vector< ::std::unique_ptr<AST::TypeParam> > type_params =
- parse_type_params(); if (!type_params.empty()) {
- // C++11 code:
- generic_params.insert(generic_params.end(),
- ::std::make_move_iterator(type_params.begin()),
- ::std::make_move_iterator(type_params.end()));
- }*/
-
- return generic_params;
-}
-
-// Parses lifetime generic parameters (pointers). Will also consume any trailing
-// comma.
-::std::vector< ::std::unique_ptr<AST::LifetimeParam> >
-Parser::parse_lifetime_params ()
-{
- ::std::vector< ::std::unique_ptr<AST::LifetimeParam> > lifetime_params;
-
- // TODO: think of better control structure than infinite loop with break on
- // failure?
- while (true)
- {
- AST::LifetimeParam lifetime_param = parse_lifetime_param ();
-
- if (lifetime_param.is_error ())
- {
- // break if fails to parse
- break;
- }
-
- lifetime_params.push_back (::std::unique_ptr<AST::LifetimeParam> (
- new AST::LifetimeParam (lifetime_param)));
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- // skip commas, including trailing commas
- lexer.skip_token ();
- }
-
- return lifetime_params;
-}
-
-/* Parses lifetime generic parameters (objects). Will also consume any trailing
- * comma.
- * TODO: is this best solution? implements most of the same algorithm. */
-::std::vector<AST::LifetimeParam>
-Parser::parse_lifetime_params_objs ()
-{
- ::std::vector<AST::LifetimeParam> lifetime_params;
-
- // TODO: think of better control structure than infinite loop with break on
- // failure?
- while (true)
- {
- AST::LifetimeParam lifetime_param = parse_lifetime_param ();
-
- if (lifetime_param.is_error ())
- {
- // break if fails to parse
- break;
- }
-
- lifetime_params.push_back (lifetime_param);
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- // skip commas, including trailing commas
- lexer.skip_token ();
- }
-
- return lifetime_params;
-}
-
-/* Parses a single lifetime generic parameter (not including comma). */
-AST::LifetimeParam
-Parser::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 ();
- }
- // 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 ();
- }
-
- return AST::LifetimeParam (::std::move (lifetime), lifetime_tok->get_locus (),
- ::std::move (lifetime_bounds),
- ::std::move (outer_attr));
-}
-
-// Parses type generic parameters. Will also consume any trailing comma.
-::std::vector< ::std::unique_ptr<AST::TypeParam> >
-Parser::parse_type_params ()
-{
- ::std::vector< ::std::unique_ptr<AST::TypeParam> > type_params;
-
- // TODO: think of better control structure than infinite loop with break on
- // failure?
- while (true)
- {
- ::std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
-
- if (type_param == NULL)
- {
- // 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 ();
- }
-
- return type_params;
- // TODO: this shares most code with parse_lifetime_params - good place to use
- // template?
-}
-
-// Parses a single type (generic) parameter, not including commas. May change to
-// return value.
-::std::unique_ptr<AST::TypeParam>
-Parser::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 NULL;
- }
- // 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 = NULL;
- if (lexer.peek_token ()->get_id () == EQUAL)
- {
- lexer.skip_token ();
-
- // parse type (now required)
- type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse type in type param");
- return NULL;
- }
- }
-
- 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.
-::std::vector<AST::FunctionParam>
-Parser::parse_function_params ()
-{
- ::std::vector<AST::FunctionParam> params;
-
- // HACK: return early if RIGHT_PAREN is found
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- {
- return params;
- }
-
- AST::FunctionParam initial_param = parse_function_param ();
-
- // Return empty parameter list if no parameter there
- if (initial_param.is_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 ();
-
- /* HACK: break if next token is a right (closing) paren - this is not
- * strictly true via grammar rule but seems to be true in practice (i.e.
- * with trailing comma). */
- if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
- {
- break;
- }
-
- // now, as right paren would break, function param is required
- AST::FunctionParam param = parse_function_param ();
- if (param.is_error ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse function param (in function params)");
- // skip somewhere?
- return ::std::vector<AST::FunctionParam> ();
- }
-
- params.push_back (::std::move (param));
-
- t = lexer.peek_token ();
- }
-
- 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. */
-AST::FunctionParam
-Parser::parse_function_param ()
-{
- 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 == NULL)
- {
- // 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 == NULL)
- {
- // skip?
- return AST::FunctionParam::create_error ();
- }
-
- return AST::FunctionParam (::std::move (param_pattern),
- ::std::move (param_type), locus);
-}
-
-/* Parses a function or method return type syntactical construction. Also
- * handles a function return type not existing. */
-::std::unique_ptr<AST::Type>
-Parser::parse_function_return_type ()
-{
- if (lexer.peek_token ()->get_id () != RETURN_TYPE)
- {
- return NULL;
- }
- // 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(). */
-AST::WhereClause
-Parser::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 == NULL)
- {
- rust_error_at (t->get_locus (), "failed to parse where clause item");
- 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 ();
- }
-
- return AST::WhereClause (::std::move (where_clause_items));
-}
-
-// Parses a where clause item (lifetime or type bound). Does not parse any
-// commas.
-::std::unique_ptr<AST::WhereClauseItem>
-Parser::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.
-::std::unique_ptr<AST::LifetimeWhereClauseItem>
-Parser::parse_lifetime_where_clause_item ()
-{
- AST::Lifetime lifetime = parse_lifetime ();
- if (lifetime.is_error ())
- {
- // TODO: error here?
- return NULL;
- }
-
- if (!skip_token (COLON))
- {
- // TODO: skip after somewhere
- return NULL;
- }
-
- ::std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds ();
-
- return ::std::unique_ptr<AST::LifetimeWhereClauseItem> (
- new AST::LifetimeWhereClauseItem (::std::move (lifetime),
- ::std::move (lifetime_bounds)));
-}
-
-// Parses a type bound where clause item.
-::std::unique_ptr<AST::TypeBoundWhereClauseItem>
-Parser::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 == NULL)
- {
- return NULL;
- }
-
- if (!skip_token (COLON))
- {
- // TODO: skip after somewhere
- return NULL;
- }
-
- // parse type param bounds if they exist
- ::std::vector< ::std::unique_ptr<AST::TypeParamBound> > type_param_bounds
- = parse_type_param_bounds ();
-
- return ::std::unique_ptr<AST::TypeBoundWhereClauseItem> (
- new AST::TypeBoundWhereClauseItem (::std::move (for_lifetimes),
- ::std::move (type),
- ::std::move (type_param_bounds)));
-}
-
-// Parses a for lifetimes clause, including the for keyword and angle brackets.
-::std::vector<AST::LifetimeParam>
-Parser::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;
- }
-
- params = parse_lifetime_params_objs ();
-
- if (!skip_generics_right_angle ())
- {
- // skip after somewhere?
- return params;
- }
-
- return params;
-}
-
-// Parses type parameter bounds in where clause or generic arguments.
-::std::vector< ::std::unique_ptr<AST::TypeParamBound> >
-Parser::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 == NULL)
- {
- // 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));
-
- // TODO think of better control structure than infinite loop
- while (true)
- {
- // Quick exit for no more bounds
- if (lexer.peek_token ()->get_id () != PLUS)
- {
- return type_param_bounds;
- }
- lexer.skip_token ();
-
- ::std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
- if (bound == NULL)
- {
- // not an error: bound is allowed to be null as trailing plus is
- // allowed
- return type_param_bounds;
- }
-
- type_param_bounds.push_back (::std::move (bound));
- }
-
- return type_param_bounds;
-}
-
-/* Parses a single type parameter bound in a where clause or generic argument.
- * Does not parse the '+' between arguments. */
-::std::unique_ptr<AST::TypeParamBound>
-Parser::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 NULL;
- }
-}
-
-// Parses a trait bound type param bound.
-::std::unique_ptr<AST::TraitBound>
-Parser::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 NULL;
- }
- }
-
- 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.
-::std::vector<AST::Lifetime>
-Parser::parse_lifetime_bounds ()
-{
- ::std::vector<AST::Lifetime> lifetime_bounds;
-
- // TODO: think of better control structure
- while (true)
- {
- AST::Lifetime lifetime = parse_lifetime ();
-
- // quick exit for parsing failure
- if (lifetime.is_error ())
- {
- return lifetime_bounds;
- }
-
- lifetime_bounds.push_back (::std::move (lifetime));
-
- // plus is maybe required - spec defines it poorly, so assuming not
- // required
- if (lexer.peek_token ()->get_id () != PLUS)
- {
- return lifetime_bounds;
- }
-
- lexer.skip_token ();
- }
-
- return lifetime_bounds;
-}
-
-// Parses a lifetime token (named, 'static, or '_). Also handles lifetime not
-// existing.
-AST::Lifetime
-Parser::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.
-::std::unique_ptr<AST::TypeAlias>
-Parser::parse_type_alias (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse identifier in type alias");
- skip_after_semicolon ();
- return NULL;
- }
- 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 NULL;
- }
-
- ::std::unique_ptr<AST::Type> type_to_alias = parse_type ();
-
- if (!skip_token (SEMICOLON))
- {
- // should be skipping past this, not the next line
- return NULL;
- }
-
- 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.
-::std::unique_ptr<AST::Struct>
-Parser::parse_struct (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse struct or tuple struct identifier");
- // skip after somewhere?
- return NULL;
- }
- 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 = parse_tuple_fields ();
-
- // tuple parameters must have closing parenthesis
- if (!skip_token (RIGHT_PAREN))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // 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 NULL;
- }
-
- 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 ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere?
- return NULL;
- }
-
- 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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' in struct declaration",
- t->get_token_description ());
- // skip somewhere?
- return NULL;
- }
-}
-
-// Parses struct fields in struct declarations.
-::std::vector<AST::StructField>
-Parser::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));
-
- // 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
- while (true)
- {
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
-
- // skip comma if applies
- lexer.skip_token ();
-
- AST::StructField field = parse_struct_field ();
-
- if (!field.is_error ())
- {
- fields.push_back (::std::move (field));
- }
- else
- {
- // this would occur with a trailing comma, which is allowed
- break;
- }
- }
-
- return fields;
-
- // TODO: this shares basically all code with function params and tuple fields
- // - templates?
-}
-
-// Parses a single struct field (in a struct definition). Does not parse commas.
-AST::StructField
-Parser::parse_struct_field ()
-{
- // parse outer attributes, if they exist
- ::std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
-
- // parse visibility, if it exists
- AST::Visibility vis = parse_visibility ();
-
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse type in struct field definition");
- // skip after somewhere
- return AST::StructField::create_error ();
- }
-
- return AST::StructField (::std::move (field_name), ::std::move (field_type),
- ::std::move (vis), ::std::move (outer_attrs));
-}
-
-// Parses tuple fields in tuple/tuple struct declarations.
-::std::vector<AST::TupleField>
-Parser::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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse tuple field in tuple fields");
- return ::std::vector<AST::TupleField> ();
- }
-
- fields.push_back (::std::move (field));
-
- t = lexer.peek_token ();
- }
-
- 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.
-AST::TupleField
-Parser::parse_tuple_field ()
-{
- // parse outer attributes if they exist
- ::std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
-
- // parse visibility if it exists
- AST::Visibility vis = parse_visibility ();
-
- // parse type, which is required
- ::std::unique_ptr<AST::Type> field_type = parse_type ();
- if (field_type == NULL)
- {
- // error if null
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse type in tuple struct field");
- // skip after something
- return AST::TupleField::create_error ();
- }
-
- return AST::TupleField (::std::move (field_type), ::std::move (vis),
- ::std::move (outer_attrs));
-}
-
-// Parses a Rust "enum" tagged union item definition.
-::std::unique_ptr<AST::Enum>
-Parser::parse_enum (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (ENUM_TOK);
-
- // parse enum name
- const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
- 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 NULL;
- }
-
- // parse actual enum variant definitions
- ::std::vector< ::std::unique_ptr<AST::EnumItem> > enum_items
- = parse_enum_items ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- skip_after_end_block ();
- return NULL;
- }
-
- 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.
-::std::vector< ::std::unique_ptr<AST::EnumItem> >
-Parser::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 == NULL)
- {
- return items;
- }
-
- items.push_back (::std::move (initial_item));
-
- // maybe think of a better control structure here - do-while with an initial
- // error state? basically, loop through item list until can't find any more
- // params
- while (true)
- {
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
-
- // skip comma if applies
- lexer.skip_token ();
-
- ::std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
-
- if (item != NULL)
- {
- items.push_back (::std::move (item));
- }
- else
- {
- // this would occur with a trailing comma, which is allowed
- break;
- }
- }
-
- // TODO: does this need move?
- return items;
-
- // TODO: shares virtually all code with function params, tuple and struct
- // fields - templates?
-}
-
-// Parses a single enum variant item in an enum definition. Does not parse
-// commas.
-::std::unique_ptr<AST::EnumItem>
-Parser::parse_enum_item ()
-{
- // parse outer attributes if they exist
- ::std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
-
- // 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 NULL;
- }
- 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 = parse_tuple_fields ();
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip after somewhere
- return NULL;
- }
-
- return ::std::unique_ptr<AST::EnumItemTuple> (new AST::EnumItemTuple (
- ::std::move (item_name), ::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 ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip after somewhere
- return NULL;
- }
-
- return ::std::unique_ptr<AST::EnumItemStruct> (new AST::EnumItemStruct (
- ::std::move (item_name), ::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 (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 (outer_attrs),
- item_name_tok->get_locus ()));
- }
-}
-
-// Parses a C-style (and C-compat) untagged union declaration.
-::std::unique_ptr<AST::Union>
-Parser::parse_union (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs)
-{
- // hack - "weak keyword" by finding identifier called "union" (lookahead in
- // item switch)
- Location locus = lexer.peek_token ()->get_locus ();
- // skip union "identifier"
- skip_token (IDENTIFIER);
-
- // parse actual union name
- const_TokenPtr union_name_tok = expect_token (IDENTIFIER);
- if (union_name_tok == NULL)
- {
- skip_after_next_block ();
- return NULL;
- }
- 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 NULL;
- }
-
- // parse union inner items as "struct fields" because hey, syntax reuse. Spec
- // said so.
- ::std::vector<AST::StructField> union_fields = parse_struct_fields ();
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip after somewhere
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::ConstantItem>
-Parser::parse_const_item (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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:
- rust_error_at (ident_tok->get_locus (),
- "expected item name (identifier or '_') in constant item "
- "declaration - found '%s'",
- ident_tok->get_token_description ());
- skip_after_semicolon ();
- return NULL;
- }
-
- if (!skip_token (COLON))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse constant type (required)
- ::std::unique_ptr<AST::Type> type = parse_type ();
-
- if (!skip_token (EQUAL))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse constant expression (required)
- ::std::unique_ptr<AST::Expr> expr = parse_expr ();
-
- if (!skip_token (SEMICOLON))
- {
- // skip somewhere?
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::StaticItem>
-Parser::parse_static_item (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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);
- Identifier ident = ident_tok->get_str ();
-
- if (!skip_token (COLON))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse static item type (required)
- ::std::unique_ptr<AST::Type> type = parse_type ();
-
- if (!skip_token (EQUAL))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse static item expression (required)
- ::std::unique_ptr<AST::Expr> expr = parse_expr ();
-
- if (!skip_token (SEMICOLON))
- {
- // skip after somewhere
- return NULL;
- }
-
- 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.
-::std::unique_ptr<AST::Trait>
-Parser::parse_trait (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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);
- 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 ();
-
- // TODO: does this need move?
- 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 NULL;
- }
-
- // 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 == NULL)
- {
- // TODO: this is probably an error as next character should equal
- // RIGHT_CURLY
- break;
- }
-
- trait_items.push_back (::std::move (trait_item));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip after something
- return NULL;
- }
-
- 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), locus));
-}
-
-// Parses a trait item used inside traits (not trait, the Item).
-::std::unique_ptr<AST::TraitItem>
-Parser::parse_trait_item ()
-{
- // parse outer attributes (if they exist)
- ::std::vector<AST::Attribute> 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);
- 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 NULL;
- }
-
- // 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 ();
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip after somewhere?
- return NULL;
- }
-
- // 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 = NULL;
- switch (t->get_id ())
- {
- case SEMICOLON:
- lexer.skip_token ();
- // definition is already NULL, 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:
- rust_error_at (t->get_locus (),
- "expected ';' or definiton at the end of trait %s "
- "definition - found '%s' "
- "instead",
- is_method ? "method" : "function",
- t->get_token_description ());
- // skip?
- return NULL;
- }
-
- // 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::MacroInvocationSemi> macro_invoc
- = parse_macro_invocation_semi (outer_attrs);
-
- if (macro_invoc == NULL)
- {
- // TODO: error?
- return NULL;
- }
- else
- {
- return macro_invoc;
- }
- // FIXME: macro invocations can only start with certain tokens. be more
- // picky with these?
- }
- }
-}
-
-// Parse a typedef trait item.
-::std::unique_ptr<AST::TraitItemType>
-Parser::parse_trait_type (::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (TYPE);
-
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- Identifier ident = ident_tok->get_str ();
-
- bool has_colon = false;
- ::std::vector< ::std::unique_ptr<AST::TypeParamBound> > bounds;
-
- // parse optional colon
- if (lexer.peek_token ()->get_id () == COLON)
- {
- has_colon = true;
- lexer.skip_token ();
-
- // parse optional type param bounds
- bounds = parse_type_param_bounds ();
- }
-
- if (!skip_token (SEMICOLON))
- {
- // skip?
- return NULL;
- }
-
- 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.
-::std::unique_ptr<AST::TraitItemConst>
-Parser::parse_trait_const (::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (CONST);
-
- // parse constant item name
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- Identifier ident = ident_tok->get_str ();
-
- if (!skip_token (COLON))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // 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 = NULL;
- 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 NULL;
- }
-
- 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),
-::std::unique_ptr<AST::Impl>
-Parser::parse_impl (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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 NULL;
- }
-
- // 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 = NULL;
- if (!type_path.is_error ())
- {
- // TODO: would move work here?
- type
- = ::std::unique_ptr<AST::TypePath> (new AST::TypePath (type_path));
- }
- else
- {
- type = parse_type ();
- }
- // Type is required, so error if null
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse type in inherent impl");
- skip_after_next_block ();
- return NULL;
- }
-
- // 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 NULL;
- }
-
- // parse inner attributes (optional)
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- // TODO: this is probably an error as next character should equal
- // RIGHT_CURLY
- fprintf (stderr, "impl item is null and next char wasn't "
- "RIGHT_CURLY - probably an error");
- break;
- }
-
- impl_items.push_back (::std::move (impl_item));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed inherent impl\n");
-
- 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 NULL;
- }
-
- // parse type
- ::std::unique_ptr<AST::Type> type = parse_type ();
- // ensure type is included as it is required
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse type in trait impl");
- skip_after_next_block ();
- return NULL;
- }
-
- // 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 NULL;
- }
-
- // parse inner attributes (optional)
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- // DEBUG
- fprintf (stderr, "break out of parsing trait impl items (due to "
- "parse giving null)\n");
-
- // TODO: this is probably an error as next character should equal
- // RIGHT_CURLY
- break;
- }
-
- impl_items.push_back (::std::move (impl_item));
-
- t = lexer.peek_token ();
-
- // DEBUG
- fprintf (stderr, "successfully parsed a trait impl item\n");
- }
- // DEBUG
- fprintf (stderr, "successfully finished trait impl items\n");
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed trait impl\n");
-
- 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).
-::std::unique_ptr<AST::InherentImplItem>
-Parser::parse_inherent_impl_item ()
-{
- // parse outer attributes (if they exist)
- ::std::vector<AST::Attribute> 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:
- 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 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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' 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 NULL;
- }
- default:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' for item in inherent impl",
- t->get_token_description ());
- // skip?
- return NULL;
- }
- }
- case EXTERN_TOK:
- case UNSAFE:
- case FN_TOK:
- // function or method
- return parse_inherent_impl_function_or_method (
- AST::Visibility::create_error (), ::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_error (),
- ::std::move (outer_attrs));
- case UNSAFE:
- case EXTERN_TOK:
- case FN_TOK:
- return parse_inherent_impl_function_or_method (
- AST::Visibility::create_error (), ::std::move (outer_attrs));
- default:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' 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 NULL;
- }
- gcc_unreachable ();
- default:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' for item in inherent impl",
- t->get_token_description ());
- // skip?
- return NULL;
- }
-}
-
-/* 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.
-::std::unique_ptr<AST::InherentImplItem>
-Parser::parse_inherent_impl_function_or_method (
- AST::Visibility vis, ::std::vector<AST::Attribute> 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);
- 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 NULL;
- }
-
- // 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 ();
-
- if (!skip_token (RIGHT_PAREN))
- {
- skip_after_end_block ();
- return NULL;
- }
-
- // 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)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "%s declaration in inherent impl not allowed - must have a definition",
- is_method ? "method" : "function");
- lexer.skip_token ();
- return NULL;
- }
- ::std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
- if (body == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "could not parse definition in inherent impl %s definition",
- is_method ? "method" : "function");
- skip_after_end_block ();
- return NULL;
- }
-
- // 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).
-::std::unique_ptr<AST::TraitImplItem>
-Parser::parse_trait_impl_item ()
-{
- // parse outer attributes (if they exist)
- ::std::vector<AST::Attribute> 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_error (),
- ::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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' 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 NULL;
- }
- default:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' for item in trait impl",
- t->get_token_description ());
- // skip?
- return NULL;
- }
- }
- case EXTERN_TOK:
- case UNSAFE:
- case FN_TOK:
- // function or method
- return parse_trait_impl_function_or_method (
- AST::Visibility::create_error (), ::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_error (),
- ::std::move (outer_attrs));
- case UNSAFE:
- case EXTERN_TOK:
- case FN_TOK:
- return parse_trait_impl_function_or_method (
- AST::Visibility::create_error (), ::std::move (outer_attrs));
- default:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' 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 NULL;
- }
- gcc_unreachable ();
- default:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' for item in trait impl",
- t->get_token_description ());
- // skip?
- return NULL;
- }
-}
-
-/* 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. */
-::std::unique_ptr<AST::TraitImplItem>
-Parser::parse_trait_impl_function_or_method (
- AST::Visibility vis, ::std::vector<AST::Attribute> 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 == NULL)
- {
- return NULL;
- }
- Identifier ident = ident_tok->get_str ();
-
- // DEBUG:
- fprintf (
- stderr,
- "about to start parsing generic params in trait impl function or method\n");
-
- // parse generic params
- ::std::vector< ::std::unique_ptr<AST::GenericParam> > generic_params
- = parse_generic_params_in_angles ();
-
- // DEBUG:
- fprintf (
- stderr,
- "finished parsing generic params in trait impl function or method\n");
-
- if (!skip_token (LEFT_PAREN))
- {
- // skip after somewhere?
- return NULL;
- }
-
- // 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
- fprintf (stderr,
- "successfully parsed self param in method trait impl item\n");
- }
-
- // DEBUG
- fprintf (
- stderr,
- "started to parse function params in function or method trait impl item\n");
-
- // 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 ();
-
- if (function_params.empty ())
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse function params in trait impl %s definition",
- is_method ? "method" : "function");
- skip_after_next_block ();
- return NULL;
- }
- }
-
- // FIXME: segfault occurs during parsing of function params
-
- // DEBUG
- fprintf (stderr, "successfully parsed function params in function or method "
- "trait impl item\n");
-
- if (!skip_token (RIGHT_PAREN))
- {
- skip_after_next_block ();
- return NULL;
- }
-
- // parse return type (optional)
- ::std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
-
- // DEBUG
- fprintf (
- stderr,
- "successfully parsed return type in function or method trait impl item\n");
-
- // parse where clause (optional)
- AST::WhereClause where_clause = parse_where_clause ();
-
- // DEBUG
- fprintf (
- stderr,
- "successfully parsed where clause in function or method trait impl item\n");
-
- // parse function definition (in block) - semicolon not allowed
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "%s declaration in trait impl not allowed - must have a definition",
- is_method ? "method" : "function");
- lexer.skip_token ();
- return NULL;
- }
- ::std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
- if (body == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse definition in trait impl %s definition",
- is_method ? "method" : "function");
- skip_after_end_block ();
- return NULL;
- }
-
- // 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.
-::std::unique_ptr<AST::ExternBlock>
-Parser::parse_extern_block (AST::Visibility vis,
- ::std::vector<AST::Attribute> 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 NULL;
- }
-
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse external item despite not reaching "
- "end of extern block");
- return NULL;
- }
-
- extern_items.push_back (::std::move (extern_item));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::ExternalItem>
-Parser::parse_external_item ()
-{
- // parse optional outer attributes
- ::std::vector<AST::Attribute> 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 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 == NULL)
- {
- skip_after_semicolon ();
- return NULL;
- }
- Identifier ident = ident_tok->get_str ();
-
- if (!skip_token (COLON))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse type (required)
- ::std::unique_ptr<AST::Type> type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse type in external static item");
- skip_after_semicolon ();
- return NULL;
- }
-
- if (!skip_token (SEMICOLON))
- {
- // skip after somewhere?
- return NULL;
- }
-
- 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 == NULL)
- {
- skip_after_semicolon ();
- return NULL;
- }
- 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 NULL;
- }
-
- // parse parameters
- ::std::vector<AST::NamedFunctionParam> function_params;
- bool is_variadic = false;
-
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_PAREN)
- {
- AST::NamedFunctionParam param = parse_named_function_param ();
-
- if (param.is_error ())
- {
- // is this an error? probably
- rust_error_at (t->get_locus (),
- "could not parse named function parameter in "
- "external function");
- skip_after_semicolon ();
- return NULL;
- }
-
- function_params.push_back (::std::move (param));
-
- t = lexer.peek_token ();
- if (t->get_id () != COMMA)
- {
- if (t->get_id () != RIGHT_PAREN)
- {
- rust_error_at (t->get_locus (),
- "expected comma or right parentheses in "
- "named function parameters, "
- "found '%s'",
- t->get_token_description ());
- }
- else
- {
- // end of loop
- break;
- }
- }
- // skip comma
- lexer.skip_token ();
-
- t = lexer.peek_token ();
-
- // parse variadic ... if it exists
- if (t->get_id () == ELLIPSIS
- && lexer.peek_token (1)->get_id () == RIGHT_PAREN)
- {
- lexer.skip_token ();
-
- is_variadic = true;
-
- t = lexer.peek_token ();
- }
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- skip_after_semicolon ();
- return NULL;
- }
-
- // 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 NULL;
- }
-
- 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 (vis),
- ::std::move (outer_attrs), locus));
- }
- default:
- // error
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in extern block item declaration",
- t->get_token_description ());
- skip_after_semicolon ();
- return NULL;
- }
-}
-
-// Parses an extern block function param (with "pattern" being _ or an
-// identifier).
-AST::NamedFunctionParam
-Parser::parse_named_function_param ()
-{
- // parse identifier/_
- Identifier name;
-
- const_TokenPtr t = lexer.peek_token ();
- 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "could not parse param type in extern block function declaration");
- skip_after_semicolon ();
- return AST::NamedFunctionParam::create_error ();
- }
-
- return AST::NamedFunctionParam (::std::move (name), ::std::move (param_type));
-}
-
-// Parses a statement (will further disambiguate any statement).
-::std::unique_ptr<AST::Stmt>
-Parser::parse_stmt ()
-{
- // quick exit for empty statement
- 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
- ::std::vector<AST::Attribute> 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));
- 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:
- // TODO: ensure std::string and literal comparison works
- if (t->get_str () == "union")
- {
- 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));
- break;
- }
-}
-
-// Parses a let statement.
-::std::unique_ptr<AST::LetStmt>
-Parser::parse_let_stmt (::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (LET);
-
- // parse pattern (required)
- ::std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in let statement");
- skip_after_semicolon ();
- return NULL;
- }
-
- // parse type declaration (optional)
- ::std::unique_ptr<AST::Type> type = NULL;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- // must have a type declaration
- lexer.skip_token ();
-
- type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse type in let statement");
- skip_after_semicolon ();
- return NULL;
- }
- }
-
- // parse expression to set variable to (optional)
- ::std::unique_ptr<AST::Expr> expr = NULL;
- if (lexer.peek_token ()->get_id () == EQUAL)
- {
- // must have an expression
- lexer.skip_token ();
-
- expr = parse_expr ();
- if (expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse expression in let statement");
- skip_after_semicolon ();
- return NULL;
- }
- }
-
- if (!skip_token (SEMICOLON))
- {
- // skip after somewhere
- return NULL;
- // TODO: how wise is it to ditch a mostly-valid let statement just because
- // a semicolon is missing?
- }
-
- 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.
-AST::TypePath
-Parser::parse_type_path ()
-{
- bool has_opening_scope_resolution = false;
- 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 == NULL)
- {
- // 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 == NULL)
- {
- // skip after somewhere?
- rust_error_at (t->get_locus (), "could not parse type path segment");
- return AST::TypePath::create_error ();
- }
-
- segments.push_back (::std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- return AST::TypePath (::std::move (segments), Linemap::unknown_location (),
- has_opening_scope_resolution);
-}
-
-// Parses the generic arguments in each path segment.
-AST::GenericArgs
-Parser::parse_path_generic_args ()
-{
- if (!skip_token (LEFT_ANGLE))
- {
- // skip after somewhere?
- return AST::GenericArgs::create_empty ();
- }
-
- // try to parse lifetimes first
- ::std::vector<AST::Lifetime> lifetime_args;
-
- const_TokenPtr t = lexer.peek_token ();
- Location locus = t->get_locus ();
- const_TokenPtr t2 = lexer.peek_token (1);
- while (t->get_id () == LIFETIME
- && (t2->get_id () == COMMA || !is_right_angle_tok (t2->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 ();
- t2 = lexer.peek_token (1);
- }
-
- // try to parse types second
- ::std::vector< ::std::unique_ptr<AST::Type> > type_args;
-
- // TODO: think of better control structure
- t = lexer.peek_token ();
- while (!is_right_angle_tok (t->get_id ()))
- {
- // ensure not binding being parsed as type accidently
- if (t->get_id () == IDENTIFIER
- && lexer.peek_token (1)->get_id () == EQUAL)
- {
- break;
- }
-
- ::std::unique_ptr<AST::Type> type = parse_type ();
- if (type == NULL)
- {
- // not necessarily an error
- break;
- }
-
- type_args.push_back (::std::move (type));
-
- // 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 ();
- }
-
- return AST::GenericArgs (::std::move (lifetime_args), ::std::move (type_args),
- ::std::move (binding_args), locus);
-}
-
-// Parses a binding in a generic args path segment.
-AST::GenericArgsBinding
-Parser::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 == NULL)
- {
- // 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. */
-::std::unique_ptr<AST::TypePathSegment>
-Parser::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 NULL;
- }
-
- // 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 ();
-
- if (type_path_function.is_error ())
- {
- // skip after somewhere?
- return NULL;
- }
-
- 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.
-AST::TypePathFunction
-Parser::parse_type_path_function ()
-{
- if (!skip_token (LEFT_PAREN))
- {
- // skip somewhere?
- return AST::TypePathFunction::create_error ();
- }
-
- // parse function inputs
- ::std::vector< ::std::unique_ptr<AST::Type> > inputs;
-
- // TODO: think of better control structure
- while (true)
- {
- ::std::unique_ptr<AST::Type> type = parse_type ();
- if (type == NULL)
- {
- // not necessarily an error
- break;
- }
-
- inputs.push_back (::std::move (type));
-
- // skip commas, including trailing commas
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- else
- {
- 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 ();
-
- return AST::TypePathFunction (::std::move (inputs),
- ::std::move (return_type));
-}
-
-// Parses a path inside an expression that allows generic arguments.
-AST::PathInExpression
-Parser::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?
- rust_error_at (t->get_locus (),
- "could not parse path expression segment");
- return AST::PathInExpression::create_error ();
- }
-
- segments.push_back (::std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- return AST::PathInExpression (::std::move (segments), locus,
- has_opening_scope_resolution,
- ::std::vector<AST::Attribute> ());
-}
-
-// Parses a single path in expression path segment (including generic
-// arguments).
-AST::PathExprSegment
-Parser::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.
-AST::QualifiedPathInExpression
-Parser::parse_qualified_path_in_expression (bool pratt_parse)
-{
- /* 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_parse);
- 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?
- rust_error_at (lexer.peek_token ()->get_locus (),
- "required initial path expression segment in "
- "qualified path in expression could not be parsed");
- 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?
- rust_error_at (t->get_locus (),
- "could not parse path expression segment in qualified "
- "path in expression");
- return AST::QualifiedPathInExpression::create_error ();
- }
-
- segments.push_back (::std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- // FIXME: outer attr parsing
- return AST::QualifiedPathInExpression (::std::move (qual_path_type),
- ::std::move (segments), locus,
- ::std::vector<AST::Attribute> ());
-}
-
-// Parses the type syntactical construction at the start of a qualified path.
-AST::QualifiedPathType
-Parser::parse_qualified_path_type (bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- // TODO: should this actually be error? is there anywhere where this could be
- // valid?
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
- if (!skip_token (LEFT_ANGLE))
- {
- // skip after somewhere?
- return AST::QualifiedPathType::create_error ();
- }
- }
- else
- {
- // move back by 1 if pratt parsing due to skipping '<'
- locus = lexer.peek_token ()->get_locus () - 1;
- }
+/* TODO: move non-essential stuff back here from rust-parse-impl.h after
+ * confirming that it works */
- // parse type (required)
- ::std::unique_ptr<AST::Type> type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse type in qualified path type");
- // 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 ())
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "could not parse type path in as clause in qualified path type");
- // 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).
-AST::QualifiedPathInType
-Parser::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 path segments
- ::std::vector< ::std::unique_ptr<AST::TypePathSegment> > segments;
-
- // 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 == NULL)
- {
- // skip after somewhere?
- rust_error_at (lexer.peek_token ()->get_locus (),
- "required initial type path segment in qualified path in "
- "type could not be parsed");
- return AST::QualifiedPathInType::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 == NULL)
- {
- // skip after somewhere?
- rust_error_at (
- t->get_locus (),
- "could not parse type path segment in qualified path in type");
- return AST::QualifiedPathInType::create_error ();
- }
-
- segments.push_back (::std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- return AST::QualifiedPathInType (::std::move (qual_path_type),
- ::std::move (segments), locus);
-}
-
-// Parses a self param. Also handles self param not existing.
-AST::SelfParam
-Parser::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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime in self param");
- // 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 = NULL;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
-
- // type is now required
- type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse type in self param");
- // skip after somewhere?
- return AST::SelfParam::create_error ();
- }
- }
-
- // ensure that cannot have both type and reference
- if (type != NULL && has_reference)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "cannot have both a reference and a type specified in a self param");
- // 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 NULL 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. */
-AST::Method
-Parser::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 == NULL)
- {
- 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))
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "method missing opening parentheses before parameter list");
- skip_after_next_block ();
- return AST::Method::create_error ();
- }
-
- // parse self param
- AST::SelfParam self_param = parse_self_param ();
- if (self_param.is_error ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse self param in method");
- 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 ();
-
- if (!skip_token (RIGHT_PAREN))
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "method declaration missing closing parentheses after parameter list");
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "method declaration missing block expression");
- 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 (),
- ::std::vector<AST::Attribute> (), locus);
-}
-
-// Parses an expression statement (disambiguates to expression with or without
-// block statement).
-::std::unique_ptr<AST::ExprStmt>
-Parser::parse_expr_stmt (::std::vector<AST::Attribute> outer_attrs)
-{
- /* 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));
- }
- }
- 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));
- }
- }
- 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));
- }
-}
-
-// Parses a expression statement containing an expression with block.
-// Disambiguates internally.
-::std::unique_ptr<AST::ExprStmtWithBlock>
-Parser::parse_expr_stmt_with_block (::std::vector<AST::Attribute> outer_attrs)
-{
- ::std::unique_ptr<AST::ExprWithBlock> expr_parsed = NULL;
-
- 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 ()->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:
- rust_error_at (t->get_locus (),
- "could not recognise expr beginning with '%s' as an expr "
- "with block in parsing "
- "expr statement.",
- t->get_token_description ());
- skip_after_next_block ();
- return NULL;
- }
-
- // ensure expr parsed exists
- if (expr_parsed == NULL)
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse expr with block in parsing expr statement");
- skip_after_end_block ();
- return NULL;
- }
-
- // return expr stmt created from expr
- return ::std::unique_ptr<AST::ExprStmtWithBlock> (
- new AST::ExprStmtWithBlock (::std::move (expr_parsed), t->get_locus ()));
-}
-
-// Parses an expression statement containing an expression without block.
-// Disambiguates further.
-::std::unique_ptr<AST::ExprStmtWithoutBlock>
-Parser::parse_expr_stmt_without_block (
- ::std::vector<AST::Attribute> outer_attrs)
-{
- // TODO: maybe move more logic for expr without block in here for better error
- // handling
-
- // try to parse expr without block
- /*AST::ExprWithoutBlock* expr = NULL;
- expr = parse_expr_without_block(::std::move(outer_attrs));*/
- // HACK: parse expression instead of expression without block, due to Pratt
- // parsing issues
- ::std::unique_ptr<AST::Expr> expr = NULL;
- Location locus = lexer.peek_token ()->get_locus ();
- expr = parse_expr (::std::move (outer_attrs));
- if (expr == NULL)
- {
- // expr is required, error
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse expr without block in expr statement");
- skip_after_semicolon ();
- return NULL;
- }
-
- // skip semicolon at end that is required
- if (!skip_token (SEMICOLON))
- {
- // skip somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::ExprStmtWithoutBlock> (
- new AST::ExprStmtWithoutBlock (::std::move (expr), locus));
-}
-
-// Parses an expression without a block associated with it (further
-// disambiguates).
-::std::unique_ptr<AST::ExprWithoutBlock>
-Parser::parse_expr_without_block (::std::vector<AST::Attribute> outer_attrs)
-{
- /* 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));
- case LEFT_PAREN:
- /* either grouped expr or tuple expr - depends on whether there is a comma
- * inside the parentheses - if so, tuple expr, otherwise, grouped expr. */
- return parse_grouped_or_tuple_expr (::std::move (outer_attrs));
- default: {
- // HACK: piggyback on pratt parsed expr and abuse polymorphism to
- // essentially downcast
-
- // DEBUG
- fprintf (stderr,
- "about to parse expr (in expr without block method)\n");
-
- ::std::unique_ptr<AST::Expr> expr
- = parse_expr (::std::move (outer_attrs));
-
- // DEBUG
- fprintf (stderr,
- "successfully parsed expr (in expr without block method)\n");
-
- if (expr == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse expression for expression without "
- "block (pratt-parsed expression is null)");
- return NULL;
- }
-
- ::std::unique_ptr<AST::ExprWithoutBlock> expr_without_block (
- expr->as_expr_without_block ());
- // THIS IS THE CAUSE OF THE SEGFAULT
-
- // DEBUG
- fprintf (stderr,
- "expr to expr without block conversion didn't error\n");
-
- if (expr_without_block != NULL)
- {
- // DEBUG
- fprintf (stderr, "expr to expr without block conversion was "
- "successful; returning\n");
-
- return expr_without_block;
- }
- else
- {
- rust_error_at (t->get_locus (),
- "converted expr without block is null");
- return NULL;
- }
- }
- }
-}
-
-// Parses a block expression, including the curly braces at start and end.
-::std::unique_ptr<AST::BlockExpr>
-Parser::parse_block_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
- if (!skip_token (LEFT_CURLY))
- {
- skip_after_end_block ();
- return NULL;
- }
- }
- else
- {
- locus = lexer.peek_token ()->get_locus () - 1;
- }
-
- ::std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
-
- // parse statements and expression
- ::std::vector< ::std::unique_ptr<AST::Stmt> > stmts;
- ::std::unique_ptr<AST::ExprWithoutBlock> expr = NULL;
-
- 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 ())
- {
- rust_error_at (t->get_locus (),
- "failed to parse statement or expression without "
- "block in block expression");
- return NULL;
- }
-
- if (expr_or_stmt.stmt != NULL)
- {
- // FIXME: determine if this move works
- 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;
- }
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- rust_error_at (t->get_locus (),
- "error may be from having an expression (as opposed to "
- "statement) in the body of the function but not last");
- skip_after_end_block ();
- return NULL;
- }
-
- // ensure that there is at least either a statement or an expr
- /*if (stmts.empty() && expr == NULL) {
- rust_error_at(lexer.peek_token()->get_id(),
- "block expression requires statements or an expression without block -
- found neither"); skip_after_end_block(); return NULL;
- }*/
- // grammar allows for empty block expressions
-
- return ::std::unique_ptr<AST::BlockExpr> (
- new AST::BlockExpr (::std::move (stmts), ::std::move (expr),
- ::std::move (inner_attrs), ::std::move (outer_attrs),
- locus));
-}
-
-// Parses a "grouped" expression (expression in parentheses), used to control
-// precedence.
-::std::unique_ptr<AST::GroupedExpr>
-Parser::parse_grouped_expr (::std::vector<AST::Attribute> outer_attrs)
-{
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (LEFT_PAREN);
-
- ::std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
-
- // parse required expr inside parentheses
- ::std::unique_ptr<AST::Expr> expr_in_parens = parse_expr ();
- if (expr_in_parens == NULL)
- {
- // skip after somewhere?
- // error?
- return NULL;
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip after somewhere?
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::ClosureExpr>
-Parser::parse_closure_expr (::std::vector<AST::Attribute> 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?
- rust_error_at (t->get_locus (), "could not parse closure param");
- 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 ();
- }
- break;
- default:
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' in closure expression - expected '|' or '||'",
- t->get_token_description ());
- // skip somewhere?
- return NULL;
- }
-
- // 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 == NULL)
- {
- // error
- rust_error_at (t->get_locus (), "failed to parse type for closure");
- // skip somewhere?
- return NULL;
- }
-
- // parse block expr, which is required
- ::std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
- if (block == NULL)
- {
- // error
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse block expr in closure");
- // skip somewhere?
- return NULL;
- }
-
- 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse expression in closure");
- // skip somewhere?
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::LiteralExpr>
-Parser::parse_literal_expr (::std::vector<AST::Attribute> 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 RAW_STRING_LITERAL:
- // put here if lexer changes to have these
- case BYTE_CHAR_LITERAL:
- type = AST::Literal::BYTE;
- literal_value = t->get_str ();
- lexer.skip_token ();
- break;
- case BYTE_STRING_LITERAL:
- type = AST::Literal::BYTE_STRING;
- literal_value = t->get_str ();
- lexer.skip_token ();
- break;
- // case RAW_BYTE_STRING_LITERAL:
- 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 = ::std::string ("true");
- lexer.skip_token ();
- break;
- case FALSE_LITERAL:
- type = AST::Literal::BOOL;
- literal_value = ::std::string ("false");
- lexer.skip_token ();
- break;
- default:
- // error - cannot be a literal expr
- rust_error_at (t->get_locus (),
- "unexpected token '%s' when parsing literal expression",
- t->get_token_description ());
- // skip?
- return NULL;
- }
-
- // 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_locus (), ::std::move (outer_attrs)));
-}
-
-// Parses a return expression (including any expression to return).
-::std::unique_ptr<AST::ReturnExpr>
-Parser::parse_return_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- skip_token (RETURN_TOK);
- }
- else
- {
- // minus 7 chars for 6 in return and a space
- // or just TODO: pass in location data
- locus = lexer.peek_token ()->get_locus () - 7;
- }
-
- // parse expression to return, if it exists
- ::std::unique_ptr<AST::Expr> returned_expr = parse_expr ();
- // FIXME: ensure this doesn't ruin the middle of any expressions or anything
-
- return ::std::unique_ptr<AST::ReturnExpr> (
- new AST::ReturnExpr (locus, ::std::move (returned_expr),
- ::std::move (outer_attrs)));
-}
-
-// Parses a break expression (including any label to break to AND any return
-// expression).
-::std::unique_ptr<AST::BreakExpr>
-Parser::parse_break_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- skip_token (BREAK);
- }
- else
- {
- // minus 6 chars for 5 in return and a space
- // or just TODO: pass in location data
- locus = lexer.peek_token ()->get_locus () - 6;
- }
-
- // 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
- ::std::unique_ptr<AST::Expr> return_expr = parse_expr ();
-
- return ::std::unique_ptr<AST::BreakExpr> (
- new AST::BreakExpr (locus, ::std::move (label), ::std::move (return_expr),
- ::std::move (outer_attrs)));
-}
-
-// Parses a continue expression (including any label to continue from).
-::std::unique_ptr<AST::ContinueExpr>
-Parser::parse_continue_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- skip_token (CONTINUE);
- }
- else
- {
- // minus 9 chars for 8 in return and a space
- // or just TODO: pass in location data
- locus = lexer.peek_token ()->get_locus () - 9;
- }
-
- // 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 (locus, ::std::move (label),
- ::std::move (outer_attrs)));
-}
-
-// Parses a loop label used in loop expressions.
-AST::LoopLabel
-Parser::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. */
-::std::unique_ptr<AST::IfExpr>
-Parser::parse_if_expr (
- ::std::vector<AST::Attribute> outer_attrs ATTRIBUTE_UNUSED)
-{
- // TODO: make having outer attributes an error?
-
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (IF);
-
- // detect accidental if let
- if (lexer.peek_token ()->get_id () == LET)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "if let expression probably exists, but is being parsed "
- "as an if expression. This may "
- "be a parser error.");
- // skip somewhere?
- return NULL;
- }
-
- // 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 (::std::vector<AST::Attribute> (), no_struct_expr);
- if (condition == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse condition expression in if expression");
- // skip somewhere?
- return NULL;
- }
-
- // parse required block expr
- ::std::unique_ptr<AST::BlockExpr> if_body = parse_block_expr ();
- if (if_body == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse if body block expression in if expression");
- // skip somewhere?
- return NULL;
- }
-
- // 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),
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse else body block expression in "
- "if expression");
- // skip somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::IfExprConseqElse> (
- new AST::IfExprConseqElse (::std::move (condition),
- ::std::move (if_body),
- ::std::move (else_body), 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse (else) if let expression "
- "after if expression");
- // skip somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::IfExprConseqIfLet> (
- new AST::IfExprConseqIfLet (::std::move (condition),
- ::std::move (if_body),
- ::std::move (if_let_expr),
- locus));
- }
- else
- {
- // parse if expr (required)
- ::std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
- if (if_expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse (else) if expression after "
- "if expression");
- // skip somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::IfExprConseqIf> (
- new AST::IfExprConseqIf (::std::move (condition),
- ::std::move (if_body),
- ::std::move (if_expr), locus));
- }
- }
- default:
- // error - invalid token
- rust_error_at (t->get_locus (),
- "unexpected token '%s' after else in if expression",
- t->get_token_description ());
- // skip somewhere?
- return NULL;
- }
- }
-}
-
-/* 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. */
-::std::unique_ptr<AST::IfLetExpr>
-Parser::parse_if_let_expr (
- ::std::vector<AST::Attribute> outer_attrs ATTRIBUTE_UNUSED)
-{
- // TODO: make having outer attributes an error?
-
- Location locus = lexer.peek_token ()->get_locus ();
- skip_token (IF);
-
- // detect accidental if expr parsed as if let expr
- if (lexer.peek_token ()->get_id () != LET)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "if expression probably exists, but is being parsed as an "
- "if let expression. This may "
- "be a parser error.");
- // skip somewhere?
- return NULL;
- }
- 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 ())
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse any match arm patterns in if let expression");
- // skip somewhere?
- return NULL;
- }
-
- if (!skip_token (EQUAL))
- {
- // skip somewhere?
- return NULL;
- }
-
- // 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 (::std::vector<AST::Attribute> (), no_struct_expr);
- if (scrutinee_expr == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse scrutinee expression in if let expression");
- // skip somewhere?
- return NULL;
- }
- /* 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse if let body block expression in if let expression");
- // skip somewhere?
- return NULL;
- }
-
- // 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), 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse else body block expression in "
- "if let expression");
- // skip somewhere?
- return NULL;
- }
-
- 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), 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse (else) if let expression "
- "after if let expression");
- // skip somewhere?
- return NULL;
- }
-
- 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), locus));
- }
- else
- {
- // parse if expr (required)
- ::std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
- if (if_expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse (else) if expression after "
- "if let expression");
- // skip somewhere?
- return NULL;
- }
-
- 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), locus));
- }
- }
- default:
- // error - invalid token
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' after else in if let expression",
- t->get_token_description ());
- // skip somewhere?
- return NULL;
- }
- }
-}
-
-// 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. */
-::std::unique_ptr<AST::LoopExpr>
-Parser::parse_loop_expr (::std::vector<AST::Attribute> 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 (LOOP);
-
- // parse loop body, which is required
- ::std::unique_ptr<AST::BlockExpr> loop_body = parse_block_expr ();
- if (loop_body == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse loop body in (infinite) loop expression");
- return NULL;
- }
-
- 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. */
-::std::unique_ptr<AST::WhileLoopExpr>
-Parser::parse_while_loop_expr (::std::vector<AST::Attribute> 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);
-
- // ensure it isn't a while let loop
- if (lexer.peek_token ()->get_id () == LET)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "appears to be while let loop but is being parsed by "
- "while loop - this may be a compiler issue");
- // skip somewhere?
- return NULL;
- }
-
- // 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 (::std::vector<AST::Attribute> (), no_struct_expr);
- if (predicate == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse predicate expression in while loop");
- // skip somewhere?
- return NULL;
- }
- // 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse loop body block expression in while loop");
- // skip somewhere
- return NULL;
- }
-
- 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. */
-::std::unique_ptr<AST::WhileLetLoopExpr>
-Parser::parse_while_let_loop_expr (::std::vector<AST::Attribute> 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)
- {
- rust_error_at (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");
- // skip somewhere
- return NULL;
- }
- // 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 NULL;
- }
-
- // 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 (::std::vector<AST::Attribute> (), no_struct_expr);
- if (predicate_expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse predicate expression in while let loop");
- // skip somewhere?
- return NULL;
- }
- // 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse block expr (loop body) of while let loop");
- // skip somewhere?
- return NULL;
- }
-
- 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. */
-::std::unique_ptr<AST::ForLoopExpr>
-Parser::parse_for_loop_expr (::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse iterator pattern in for loop");
- // skip somewhere?
- return NULL;
- }
-
- if (!skip_token (IN))
- {
- // skip somewhere?
- return NULL;
- }
-
- // 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 (::std::vector<AST::Attribute> (), no_struct_expr);
- if (expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse iterator expression in for loop");
- // skip somewhere?
- return NULL;
- }
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse loop body block expression in for loop");
- // skip somewhere?
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::BaseLoopExpr>
-Parser::parse_labelled_loop_expr (::std::vector<AST::Attribute> 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)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "expected lifetime in labelled loop expr (to parse loop "
- "label) - found '%s'",
- lexer.peek_token ()->get_token_description ());
- // skip?
- return NULL;
- }
-
- // parse loop label (required)
- AST::LoopLabel label = parse_loop_label ();
- if (label.is_error ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse loop label in labelled loop expr");
- // skip?
- return NULL;
- }
-
- // 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
- rust_error_at (t->get_locus (),
- "unexpected token '%s' when parsing labelled loop",
- t->get_token_description ());
- // skip?
- return NULL;
- }
-}
-
-// Parses a match expression.
-::std::unique_ptr<AST::MatchExpr>
-Parser::parse_match_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- skip_token (MATCH_TOK);
- }
- else
- {
- // TODO: probably just pass in location data as param
- // get current pos then move back 6 - 5 for match, 1 for space
- locus = lexer.peek_token ()->get_locus () - 6;
- }
-
- // 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 (::std::vector<AST::Attribute> (), no_struct_expr);
- if (scrutinee == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse scrutinee expression in match expression");
- // skip somewhere?
- return NULL;
- }
- // TODO: check for scrutinee expr not being struct expr? or do so in semantic
- // analysis
-
- if (!skip_token (LEFT_CURLY))
- {
- // skip somewhere?
- return NULL;
- }
-
- // parse inner attributes (if they exist)
- ::std::vector<AST::Attribute> inner_attrs = parse_inner_attributes ();
-
- // parse match arms (if they exist)
- ::std::vector< ::std::unique_ptr<AST::MatchCase> > match_arms;
-
- // DEBUG
- fprintf (stderr, "about to start loop to start parsing match cases\n");
-
- // FIXME: absolute worst control structure ever
- // parse match cases
- while (true)
- {
- // parse match arm itself, which is required
- AST::MatchArm arm = parse_match_arm ();
- if (arm.is_error ())
- {
- // DEBUG
- fprintf (stderr, "broke loop on invalid match arm\n");
-
- // not necessarily an error
- break;
- }
-
- // DEBUG
- fprintf (stderr, "parsed valid match arm\n");
-
- if (!skip_token (MATCH_ARROW))
- {
- // skip after somewhere?
- // TODO is returning here a good idea? or is break better?
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr, "skipped match arrow\n");
-
- // branch on next token - if '{', block expr, otherwise just expr
- if (lexer.peek_token ()->get_id () == LEFT_CURLY)
- {
- // block expr
- ::std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
- if (block_expr == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse block expr in match arm in match expr");
- // skip somewhere
- return NULL;
- }
-
- // create match case block expr and add to cases
- ::std::unique_ptr<AST::MatchCaseBlockExpr> match_case_block (
- new AST::MatchCaseBlockExpr (::std::move (arm),
- ::std::move (block_expr)));
- match_arms.push_back (::std::move (match_case_block));
-
- // skip optional comma
- if (lexer.peek_token ()->get_id () == COMMA)
- {
- lexer.skip_token ();
- }
- }
- else
- {
- // regular expr
- ::std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse expr in match arm in match expr");
- // skip somewhere?
- return NULL;
- }
-
- // construct match case expr and add to cases
- ::std::unique_ptr<AST::MatchCaseExpr> match_case_expr (
- new AST::MatchCaseExpr (::std::move (arm), ::std::move (expr)));
- match_arms.push_back (::std::move (match_case_expr));
-
- // skip REQUIRED comma - if no comma, break
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- // if no comma, must be end of cases
- break;
- }
- lexer.skip_token ();
- }
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- // skip somewhere?
- return NULL;
- }
-
- 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).
-AST::MatchArm
-Parser::parse_match_arm ()
-{
- // parse optional outer attributes
- ::std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
-
- // DEBUG
- fprintf (stderr, "about to start parsing match arm patterns\n");
-
- // 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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse any patterns in match arm");
- // skip somewhere?
- return AST::MatchArm::create_error ();
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed match arm patterns\n");
-
- // parse match arm guard expr if it exists
- ::std::unique_ptr<AST::Expr> guard_expr = NULL;
- if (lexer.peek_token ()->get_id () == IF)
- {
- lexer.skip_token ();
-
- guard_expr = parse_expr ();
- if (guard_expr == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse guard expression in match arm");
- // skip somewhere?
- return AST::MatchArm::create_error ();
- }
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed match arm\n");
-
- return AST::MatchArm (::std::move (match_arm_patterns),
- ::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). */
-::std::vector< ::std::unique_ptr<AST::Pattern> >
-Parser::parse_match_arm_patterns (TokenId end_token_id)
-{
- // skip optional leading '|'
- bool has_leading_pipe = false;
- if (lexer.peek_token ()->get_id () == PIPE)
- {
- has_leading_pipe = true;
- 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 == NULL)
- {
- // FIXME: should this be an error?
- return patterns;
- }
- patterns.push_back (::std::move (initial_pattern));
-
- // DEBUG
- fprintf (stderr, "successfully parsed initial match arm pattern\n");
-
- // 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 == NULL)
- {
- // this is an error
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in match arm patterns");
- // skip somewhere?
- return ::std::vector< ::std::unique_ptr<AST::Pattern> > ();
- }
-
- patterns.push_back (::std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- return patterns;
-}
-
-// Parses an async block expression.
-::std::unique_ptr<AST::AsyncBlockExpr>
-Parser::parse_async_block_expr (::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse block expression of async block expression");
- // skip somewhere?
- return NULL;
- }
-
- 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.
-::std::unique_ptr<AST::UnsafeBlockExpr>
-Parser::parse_unsafe_block_expr (::std::vector<AST::Attribute> outer_attrs)
-{
- 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse block expression of unsafe block expression");
- // skip somewhere?
- return NULL;
- }
-
- return ::std::unique_ptr<AST::UnsafeBlockExpr> (
- new AST::UnsafeBlockExpr (::std::move (block_expr),
- ::std::move (outer_attrs), locus));
-}
-
-// Parses an array definition expression.
-::std::unique_ptr<AST::ArrayExpr>
-Parser::parse_array_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- skip_token (LEFT_SQUARE);
- }
- else
- {
- locus = lexer.peek_token ()->get_locus () - 1;
- }
-
- // parse optional inner attributes
- ::std::vector<AST::Attribute> 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 ();
-
- return ::std::unique_ptr<AST::ArrayExpr> (
- new AST::ArrayExpr (NULL, ::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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse expression in array expression "
- "(even though arrayelems seems to be present)");
- // skip somewhere?
- return NULL;
- }
-
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "could not parse copy amount expression in array "
- "expression (arrayelems)");
- // skip somewhere?
- return NULL;
- }
-
- ::std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
- new AST::ArrayElemsCopied (::std::move (initial_expr),
- ::std::move (copy_amount)));
- 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.push_back (::std::move (initial_expr));
-
- ::std::unique_ptr<AST::ArrayElemsValues> array_elems (
- new AST::ArrayElemsValues (::std::move (exprs)));
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse element in array expression");
- // skip somewhere?
- return NULL;
- }
- exprs.push_back (::std::move (expr));
-
- t = lexer.peek_token ();
- }
-
- skip_token (RIGHT_SQUARE);
-
- ::std::unique_ptr<AST::ArrayElemsValues> array_elems (
- new AST::ArrayElemsValues (::std::move (exprs)));
- return ::std::unique_ptr<AST::ArrayExpr> (
- new AST::ArrayExpr (::std::move (array_elems),
- ::std::move (inner_attrs),
- ::std::move (outer_attrs), locus));
- }
- else
- {
- // error
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "unexpected token '%s' in array expression (arrayelems)",
- lexer.peek_token ()->get_token_description ());
- // skip somewhere?
- return NULL;
- }
- }
-}
-
-// Parses a single parameter used in a closure definition.
-AST::ClosureParam
-Parser::parse_closure_param ()
-{
- // parse pattern (which is required)
- ::std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == NULL)
- {
- // not necessarily an error
- return AST::ClosureParam::create_error ();
- }
-
- // parse optional type of param
- ::std::unique_ptr<AST::Type> type = NULL;
- if (lexer.peek_token ()->get_id () == COLON)
- {
- lexer.skip_token ();
-
- // parse type, which is now required
- type = parse_type ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse type in closure parameter");
- // skip somewhere?
- return AST::ClosureParam::create_error ();
- }
- }
-
- return AST::ClosureParam (::std::move (pattern), ::std::move (type));
-}
-
-// Parses a grouped or tuple expression (disambiguates).
-::std::unique_ptr<AST::ExprWithoutBlock>
-Parser::parse_grouped_or_tuple_expr (::std::vector<AST::Attribute> outer_attrs,
- bool pratt_parse)
-{
- // adjustment to allow Pratt parsing to reuse function without copy-paste
- Location locus = Linemap::unknown_location ();
- if (!pratt_parse)
- {
- locus = lexer.peek_token ()->get_locus ();
-
- skip_token (LEFT_PAREN);
- }
- else
- {
- locus = lexer.peek_token ()->get_locus () - 1;
- }
-
- // parse optional inner attributes
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse expression in grouped or tuple expression");
- // skip after somewhere?
- return NULL;
- }
-
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse expr in tuple expr");
- // skip somewhere?
- return NULL;
- }
- 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 ();
- rust_error_at (t->get_locus (),
- "unexpected token '%s' in grouped or tuple expression "
- "(parenthesised expression) - "
- "expected ')' for grouped expr and ',' for tuple expr",
- t->get_token_description ());
- // skip somewhere?
- return NULL;
- }
-}
-
-// Parses a type (will further disambiguate any type).
-::std::unique_ptr<AST::Type>
-Parser::parse_type ()
-{
- /* 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 ())
- {
- rust_error_at (t->get_locus (),
- "failed to parse qualified path in type");
- return NULL;
- }
- 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 ()));
- }
- 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 ())
- {
- rust_error_at (t->get_locus (),
- "failed to parse path as first component of type");
- return NULL;
- }
- 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 ())
- {
- rust_error_at (t->get_locus (),
- "failed to parse simple path in macro "
- "invocation (for type)");
- return NULL;
- }
-
- lexer.skip_token ();
-
- AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
-
- return ::std::unique_ptr<AST::MacroInvocation> (
- new AST::MacroInvocation (::std::move (macro_path),
- ::std::move (tok_tree),
- ::std::vector<AST::Attribute> (),
- 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 == NULL)
- {
- break;
- }
- bounds.push_back (::std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return ::std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (::std::move (bounds), 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 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse ImplTraitType initial bound");
- return NULL;
- }
-
- 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 == NULL)
- {
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse TraitObjectType initial bound");
- return NULL;
- }
-
- // 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 == NULL)
- {
- // 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:
- rust_error_at (t->get_locus (), "unrecognised token '%s' in type",
- t->get_token_description ());
- return NULL;
- }
-}
-
-/* Parses a type that has '(' as its first character. Returns a tuple type,
- * parenthesised type,
- * TraitObjectTypeOneBound, or TraitObjectType depending on following
- * characters. */
-::std::unique_ptr<AST::Type>
-Parser::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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse type inside parentheses (probably "
- "tuple or parenthesised)");
- return NULL;
- }
- 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 NULL;
- }
-
- // 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
- AST::Type *released_ptr = types[0].release ();
- AST::TraitBound *converted_bound
- = released_ptr->to_trait_bound (true);
- delete released_ptr;
- if (converted_bound == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to hackily converted parsed type to trait bound");
- return NULL;
- }
- bounds.push_back (
- ::std::unique_ptr<AST::TraitBound> (converted_bound));
- // FIXME: possibly issues wrt memory here
-
- 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 == NULL)
- {
- // 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));
- }
- else
- {
- // release vector pointer
- ::std::unique_ptr<AST::Type> released_ptr (types[0].release ());
- // 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 == NULL)
- {
- // parenthesised type
- return ::std::unique_ptr<AST::ParenthesisedType> (
- new AST::ParenthesisedType (::std::move (released_ptr),
- left_delim_locus));
- }
- else
- {
- // trait object type (one bound)
-
- // DEBUG: removed as unique_ptr should auto-delete
- // delete released_ptr;
-
- // get value semantics trait bound
- AST::TraitBound value_bound (*converted_bound);
-
- // DEBUG: removed as unique ptr should auto-delete
- // delete converted_bound;
-
- return ::std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (value_bound,
- left_delim_locus));
- }
- // FIXME: may be issues wrt memory here
- }
- }
- 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. */
-::std::unique_ptr<AST::Type>
-Parser::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 == NULL)
- {
- // not an error - e.g. trailing plus
- return NULL;
- }
- bounds.push_back (::std::move (bound));
-
- t = lexer.peek_token ();
- }
-
- return ::std::unique_ptr<AST::TraitObjectType> (
- new AST::TraitObjectType (::std::move (bounds), for_locus));
- }
- default:
- // error
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in bare function type or trait "
- "object type or trait "
- "object type one bound",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a maybe named param used in bare function types.
-AST::MaybeNamedParam
-Parser::parse_maybe_named_param ()
-{
- /* 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse type in maybe named param");
- return AST::MaybeNamedParam::create_error ();
- }
-
- return AST::MaybeNamedParam (::std::move (name), kind, ::std::move (type),
- current->get_locus ());
-}
-
-/* Parses a bare function type (with the given for lifetimes for convenience -
- * does not parse them itself). */
-::std::unique_ptr<AST::BareFunctionType>
-Parser::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 NULL;
- }
-
- if (!skip_token (LEFT_PAREN))
- {
- return NULL;
- }
-
- // parse function params, if they exist
- ::std::vector<AST::MaybeNamedParam> params;
- bool is_variadic = false;
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_PAREN)
- {
- // handle ellipsis (only if next character is right paren)
- if (t->get_id () == ELLIPSIS)
- {
- if (lexer.peek_token (1)->get_id () == RIGHT_PAREN)
- {
- lexer.skip_token ();
- is_variadic = true;
- break;
- }
- else
- {
- rust_error_at (t->get_locus (),
- "ellipsis (for variadic) can only go at end of "
- "bare function type");
- return NULL;
- }
- }
-
- // parse required param
- AST::MaybeNamedParam param = parse_maybe_named_param ();
- if (param.is_error ())
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse maybe named param in bare function type");
- return NULL;
- }
- 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 NULL;
- }
-
- // bare function return type, if exists
- ::std::unique_ptr<AST::TypeNoBounds> return_type = NULL;
- if (lexer.peek_token ()->get_id () == RETURN_TYPE)
- {
- lexer.skip_token ();
-
- // parse required TypeNoBounds
- return_type = parse_type_no_bounds ();
- if (return_type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse return type (type no bounds) in bare "
- "function type");
- return NULL;
- }
- }
-
- return ::std::unique_ptr<AST::BareFunctionType> (new AST::BareFunctionType (
- ::std::move (for_lifetimes), ::std::move (qualifiers), ::std::move (params),
- is_variadic, ::std::move (return_type), best_try_locus));
-}
-
-// Parses a reference type (mutable or immutable, with given lifetime).
-::std::unique_ptr<AST::ReferenceType>
-Parser::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 ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse lifetime in reference type");
- return NULL;
- }
- }
-
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse referenced type in reference type");
- return NULL;
- }
-
- return ::std::unique_ptr<AST::ReferenceType> (
- new AST::ReferenceType (is_mut, ::std::move (type), locus,
- ::std::move (lifetime)));
-}
-
-// Parses a raw (unsafe) pointer type.
-::std::unique_ptr<AST::RawPointerType>
-Parser::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:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in raw pointer type",
- t->get_token_description ());
- return NULL;
- }
-
- // parse type no bounds (required)
- ::std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
- if (type == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pointed type of raw pointer type");
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::TypeNoBounds>
-Parser::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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse inner type in slice or array type");
- return NULL;
- }
-
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse size expression in array type");
- return NULL;
- }
-
- if (!skip_token (RIGHT_SQUARE))
- {
- return NULL;
- }
-
- return ::std::unique_ptr<AST::ArrayType> (
- new AST::ArrayType (::std::move (inner_type), ::std::move (size),
- locus));
- }
- default:
- // error
- rust_error_at (
- t->get_locus (),
- "unrecognised token '%s' in slice or array type after inner type",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a type, taking into account type boundary disambiguation.
-::std::unique_ptr<AST::TypeNoBounds>
-Parser::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 ())
- {
- rust_error_at (t->get_locus (),
- "failed to parse qualified path in type");
- return NULL;
- }
- 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
- rust_error_at (t->get_locus (),
- "lifetime bounds (i.e. in type param bounds, in "
- "TraitObjectType) are not allowed as TypeNoBounds");
- return NULL;
- }
- 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 ())
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse path as first component of type no bounds");
- return NULL;
- }
- 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 ())
- {
- rust_error_at (t->get_locus (),
- "failed to parse simple path in macro "
- "invocation (for type)");
- return NULL;
- }
-
- lexer.skip_token ();
-
- AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
-
- return ::std::unique_ptr<AST::MacroInvocation> (
- new AST::MacroInvocation (::std::move (macro_path),
- ::std::move (tok_tree),
- ::std::vector<AST::Attribute> (),
- locus));
- }
- case PLUS: {
- // type param bounds - not allowed, here for error message
- rust_error_at (t->get_locus (),
- "type param bounds (in TraitObjectType) are not "
- "allowed as TypeNoBounds");
- return NULL;
- }
- 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
- rust_error_at (lexer.peek_token ()->get_locus (),
- "lifetime (probably lifetime bound, in type param "
- "bounds, in ImplTraitType) is "
- "not allowed in TypeNoBounds");
- return NULL;
- }
- else
- {
- // should be trait bound, so parse trait bound
- ::std::unique_ptr<AST::TraitBound> initial_bound
- = parse_trait_bound ();
- if (initial_bound == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse ImplTraitTypeOneBound bound");
- return NULL;
- }
-
- Location locus = t->get_locus ();
-
- // ensure not a trait with multiple bounds
- t = lexer.peek_token ();
- if (t->get_id () == PLUS)
- {
- rust_error_at (t->get_locus (),
- "plus after trait bound means an ImplTraitType, "
- "which is not allowed as a TypeNoBounds");
- return NULL;
- }
-
- // 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));
- }
- 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
- rust_error_at (lexer.peek_token ()->get_locus (),
- "lifetime as bound in TraitObjectTypeOneBound "
- "is not allowed, so cannot be TypeNoBounds");
- return NULL;
- }
-
- // should be trait bound, so parse trait bound
- ::std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
- if (initial_bound == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse TraitObjectTypeOneBound initial bound");
- return NULL;
- }
-
- Location locus = t->get_locus ();
-
- // detect error with plus as next token
- t = lexer.peek_token ();
- if (t->get_id () == PLUS)
- {
- rust_error_at (t->get_locus (),
- "plus after trait bound means a TraitObjectType, "
- "which is not allowed as a TypeNoBounds");
- return NULL;
- }
-
- // 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), locus,
- has_dyn));
- }
- default:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in type no bounds",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a type no bounds beginning with '('.
-::std::unique_ptr<AST::TypeNoBounds>
-Parser::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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse type inside parentheses (probably "
- "tuple or parenthesised)");
- return NULL;
- }
- 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 NULL;
- }
-
- // 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
- rust_error_at (lexer.peek_token ()->get_locus (),
- "plus (implying TraitObjectType as type param "
- "bounds) is not allowed in type no bounds");
- return NULL;
- }
- else
- {
- // release vector pointer
- ::std::unique_ptr<AST::Type> released_ptr (types[0].release ());
- // 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 == NULL)
- {
- // parenthesised type
- return ::std::unique_ptr<AST::ParenthesisedType> (
- new AST::ParenthesisedType (::std::move (released_ptr),
- left_paren_locus));
- }
- else
- {
- // trait object type (one bound)
-
- // DEBUG: removed as unique_ptr should auto-delete
- // delete released_ptr;
-
- // get value semantics trait bound
- AST::TraitBound value_bound (*converted_bound);
-
- // DEBUG: removed as unique_ptr should auto-delete
- // delete converted_bound;
-
- return ::std::unique_ptr<AST::TraitObjectTypeOneBound> (
- new AST::TraitObjectTypeOneBound (value_bound,
- left_paren_locus));
- }
- // FIXME: memory safety issues here
- }
- }
- 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. */
-::std::unique_ptr<AST::Pattern>
-Parser::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:
- rust_error_at (range_lower->get_locus (),
- "token type '%s' cannot be parsed as range pattern "
- "bound or literal after "
- "minus "
- "symbol",
- range_lower->get_token_description ());
- return NULL;
- }
- break;
- default:
- rust_error_at (range_lower->get_locus (),
- "token type '%s' cannot be parsed as range pattern bound",
- range_lower->get_token_description ());
- return NULL;
- }
-
- 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),
- range_lower->get_locus (), has_minus));
-
- ::std::unique_ptr<AST::RangePatternBound> upper
- = parse_range_pattern_bound ();
- if (upper == NULL)
- {
- rust_error_at (
- next->get_locus (),
- "failed to parse range pattern bound in range pattern");
- return NULL;
- }
-
- 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 (), has_minus));
- }
-}
-
-// Parses a range pattern bound (value only).
-::std::unique_ptr<AST::RangePatternBound>
-Parser::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_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_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_locus));
- case FLOAT_LITERAL:
- lexer.skip_token ();
- fprintf (stderr, "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_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_locus, true));
- case FLOAT_LITERAL:
- lexer.skip_token (1);
- fprintf (stderr,
- "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_locus, true));
- default:
- rust_error_at (range_lower->get_locus (),
- "token type '%s' cannot be parsed as range pattern "
- "bound after minus "
- "symbol",
- range_lower->get_token_description ());
- return NULL;
- }
- 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 ())
- {
- rust_error_at (
- range_lower->get_locus (),
- "failed to parse path in expression range pattern bound");
- return NULL;
- }
- 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 ())
- {
- rust_error_at (range_lower->get_locus (),
- "failed to parse qualified path in expression range "
- "pattern bound");
- return NULL;
- }
- return ::std::unique_ptr<AST::RangePatternBoundQualPath> (
- new AST::RangePatternBoundQualPath (::std::move (path)));
- }
- default:
- rust_error_at (range_lower->get_locus (),
- "token type '%s' cannot be parsed as range pattern bound",
- range_lower->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a pattern (will further disambiguate any pattern).
-::std::unique_ptr<AST::Pattern>
-Parser::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
- {
- rust_error_at (t->get_locus (), "unexpected token '-' in pattern - "
- "did you forget an integer literal?");
- return NULL;
- }
- 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), ::std::vector<AST::Attribute> ());
- case LEFT_PAREN: {
- // tuple struct
- lexer.skip_token ();
-
- // parse items
- ::std::unique_ptr<AST::TupleStructItems> items
- = parse_tuple_struct_items ();
- if (items == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse tuple struct items");
- return NULL;
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- return NULL;
- }
-
- 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 NULL;
- }
-
- return ::std::unique_ptr<AST::StructPattern> (
- new AST::StructPattern (::std::move (path),
- ::std::move (elems)));
- }
- default:
- // assume path in expression
- return ::std::unique_ptr<AST::PathInExpression> (
- new AST::PathInExpression (::std::move (path)));
- }
- }
- default:
- rust_error_at (t->get_locus (), "unexpected token '%s' in pattern",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a single or double reference pattern.
-::std::unique_ptr<AST::ReferencePattern>
-Parser::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:
- rust_error_at (t->get_locus (),
- "unexpected token '%s' in reference pattern",
- t->get_token_description ());
- return NULL;
- }
-
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in reference pattern");
- // skip somewhere?
- return NULL;
- }
-
- 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. */
-::std::unique_ptr<AST::Pattern>
-Parser::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 == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse pattern inside ranged tuple pattern");
- // skip somewhere?
- return NULL;
- }
- patterns.push_back (::std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- // skip somewhere?
- return NULL;
- }
-
- // 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in grouped or tuple pattern");
- return NULL;
- }
-
- // 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse pattern in tuple pattern");
- return NULL;
- }
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in tuple pattern");
- return NULL;
- }
- upper_patterns.push_back (::std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_PAREN))
- {
- return NULL;
- }
-
- ::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
- rust_error_at (t->get_locus (),
- "failed to parse tuple pattern (probably) or maybe "
- "grouped pattern");
- return NULL;
- }
- }
- default:
- // error
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in grouped or tuple pattern "
- "after first pattern",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a slice pattern that can match arrays or slices. Parses the square
-// brackets too.
-::std::unique_ptr<AST::SlicePattern>
-Parser::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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse initial pattern in slice pattern");
- return NULL;
- }
-
- ::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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in slice pattern");
- return NULL;
- }
- patterns.push_back (::std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- if (!skip_token (RIGHT_SQUARE))
- {
- return NULL;
- }
-
- 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).
-::std::unique_ptr<AST::IdentifierPattern>
-Parser::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
- fprintf (stderr, "parsed ref in identifier pattern\n");
- }
-
- 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 == NULL)
- {
- // skip somewhere?
- return NULL;
- }
- Identifier ident = ident_tok->get_str ();
-
- // DEBUG
- fprintf (stderr, "parsed identifier in identifier pattern\n");
-
- // parse optional pattern binding thing
- ::std::unique_ptr<AST::Pattern> bind_pattern = NULL;
- if (lexer.peek_token ()->get_id () == PATTERN_BIND)
- {
- lexer.skip_token ();
-
- // parse required pattern to bind
- bind_pattern = parse_pattern ();
- if (bind_pattern == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse pattern to bind in identifier pattern\n");
- return NULL;
- }
- }
-
- // DEBUG
- fprintf (stderr, "about to return identifier pattern\n");
-
- 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. */
-::std::unique_ptr<AST::Pattern>
-Parser::parse_ident_leading_pattern ()
-{
- // ensure first token is actually identifier
- const_TokenPtr initial_tok = lexer.peek_token ();
- if (initial_tok->get_id () != IDENTIFIER)
- {
- return NULL;
- }
-
- // 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),
- ::std::vector<AST::Attribute> ());
- case LEFT_PAREN: {
- // tuple struct
- lexer.skip_token ();
-
- // DEBUG
- fprintf (stderr, "parsing tuple struct pattern\n");
-
- // parse items
- ::std::unique_ptr<AST::TupleStructItems> items
- = parse_tuple_struct_items ();
- if (items == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse tuple struct items");
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed tuple struct items\n");
-
- if (!skip_token (RIGHT_PAREN))
- {
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed tuple struct pattern\n");
-
- 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 NULL;
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed struct pattern\n");
-
- return ::std::unique_ptr<AST::StructPattern> (
- new AST::StructPattern (::std::move (path), ::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 == NULL)
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse pattern to bind to identifier pattern");
- return NULL;
- }
- return ::std::unique_ptr<AST::IdentifierPattern> (
- new AST::IdentifierPattern (::std::move (initial_ident),
- initial_tok->get_locus (), false,
- false, ::std::move (bind_pattern)));
- }
- rust_error_at (
- t->get_locus (),
- "failed to parse pattern bind to a path, not an identifier");
- return NULL;
- }
- 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.
-::std::unique_ptr<AST::TupleStructItems>
-Parser::parse_tuple_struct_items ()
-{
- ::std::vector< ::std::unique_ptr<AST::Pattern> > lower_patterns;
-
- // DEBUG
- fprintf (stderr, "started parsing tuple struct items\n");
-
- // check for '..' at front
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- // only parse upper patterns
- lexer.skip_token ();
-
- // DEBUG
- fprintf (stderr, "'..' at front in tuple struct items detected\n");
-
- ::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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in tuple struct items");
- return NULL;
- }
- upper_patterns.push_back (::std::move (pattern));
-
- t = lexer.peek_token ();
- }
-
- // DEBUG
- fprintf (
- stderr,
- "finished parsing tuple struct items ranged (upper/none only)\n");
-
- 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
- fprintf (stderr, "about to parse pattern in tuple struct items\n");
-
- // parse pattern, which is required
- ::std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse pattern in tuple struct items");
- return NULL;
- }
- lower_patterns.push_back (::std::move (pattern));
-
- // DEBUG
- fprintf (stderr, "successfully parsed pattern in tuple struct items\n");
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- // DEBUG
- fprintf (stderr, "broke out of parsing patterns in tuple struct "
- "items as no comma \n");
-
- 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 == NULL)
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse pattern in tuple struct items");
- return NULL;
- }
- 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
- rust_error_at (t->get_locus (),
- "unexpected token '%s' in tuple struct items",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses struct pattern elements if they exist.
-AST::StructPatternElements
-Parser::parse_struct_pattern_elems ()
-{
- ::std::vector< ::std::unique_ptr<AST::StructPatternField> > fields;
-
- // try parsing struct pattern fields
- const_TokenPtr t = lexer.peek_token ();
- while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
- {
- ::std::unique_ptr<AST::StructPatternField> field
- = parse_struct_pattern_field ();
- if (field == NULL)
- {
- // TODO: should this be an error?
- // assuming that this means that it is a struct pattern etc instead
-
- // DEBUG
- fprintf (
- stderr,
- "failed to parse struct pattern field - breaking from loop\n");
-
- break;
- }
-
- fields.push_back (::std::move (field));
-
- // DEBUG
- fprintf (stderr, "successfully pushed back a struct pattern field\n");
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- break;
- }
- lexer.skip_token ();
-
- t = lexer.peek_token ();
- }
-
- // FIXME: this method of parsing prevents parsing any outer attributes on the
- // .. also there seems to be no distinction between having etc and not having
- // etc.
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- lexer.skip_token ();
-
- // as no outer attributes
- AST::StructPatternEtc etc = AST::StructPatternEtc::create_empty ();
-
- return AST::StructPatternElements (::std::move (fields),
- ::std::move (etc));
- }
-
- return AST::StructPatternElements (::std::move (fields));
-}
-
-// Parses a struct pattern field (tuple index/pattern, identifier/pattern, or
-// identifier).
-::std::unique_ptr<AST::StructPatternField>
-Parser::parse_struct_pattern_field ()
-{
- // parse outer attributes (if they exist)
- ::std::vector<AST::Attribute> outer_attrs = parse_outer_attributes ();
-
- // 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 NULL;
- }
-
- // parse required pattern
- ::std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
- if (pattern == NULL)
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse pattern in tuple index struct pattern field");
- return NULL;
- }
-
- 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 == NULL)
- {
- rust_error_at (
- t->get_locus (),
- "failed to parse pattern in struct pattern field");
- return NULL;
- }
-
- 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
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in struct pattern field",
- t->get_token_description ());
- return NULL;
- }
- 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 == NULL)
- {
- return NULL;
- }
- 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 NULL;
- }
-}
-
-/* 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. */
-ExprOrStmt
-Parser::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
- ::std::vector<AST::Attribute> 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
- ::std::unique_ptr<AST::ExprStmtWithBlock> stmt (
- parse_expr_stmt_with_block (::std::move (outer_attrs)));
- return ExprOrStmt (::std::move (stmt));
- }
- 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:
- rust_error_at (t2->get_locus (),
- "unrecognised token '%s' 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: {
- // all expressions with block, so cannot be final expr without block in
- // function
- ::std::unique_ptr<AST::ExprStmtWithBlock> stmt (
- parse_expr_stmt_with_block (::std::move (outer_attrs)));
- return ExprOrStmt (::std::move (stmt));
- }
- 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))
- {
- ::std::unique_ptr<AST::ExprStmtWithBlock> stmt (
- parse_expr_stmt_with_block (::std::move (outer_attrs)));
- return ExprOrStmt (::std::move (stmt));
- }
- else
- {
- // should be expr without block
- ::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));
- }
- }
- // crappy hack to do union "keyword"
- case IDENTIFIER:
- // TODO: ensure std::string and literal comparison works
- if (t->get_str () == "union")
- {
- ::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 ();
-
- 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 ();
-
- 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)
-ExprOrStmt
-Parser::parse_path_based_stmt_or_expr (
- ::std::vector<AST::Attribute> 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 ())
- {
- rust_error_at (t2->get_locus (),
- "failed to convert parsed path to simple "
- "path (for macro invocation or semi)");
- 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:
- rust_error_at (
- t3->get_locus (),
- "unrecognised token '%s' 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;
-
- 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 == NULL)
- {
- rust_error_at (
- t3->get_locus (),
- "failed to parse token tree for macro invocation (or semi) - "
- "found "
- "'%s'",
- t3->get_token_description ());
- return ExprOrStmt::create_error ();
- }
-
- token_trees.push_back (::std::move (tree));
-
- t3 = lexer.peek_token ();
- }
-
- // 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).
- */
-
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- {
- lexer.skip_token ();
-
- ::std::unique_ptr<AST::MacroInvocationSemi> stmt (
- new AST::MacroInvocationSemi (::std::move (macro_path), type,
- ::std::move (token_trees),
- ::std::move (outer_attrs),
- stmt_or_expr_loc));
- return ExprOrStmt (::std::move (stmt));
- }
-
- // otherwise, create macro invocation
- AST::DelimTokenTree delim_tok_tree (type, ::std::move (token_trees),
- tok_tree_loc);
-
- ::std::unique_ptr<AST::MacroInvocation> expr (
- new AST::MacroInvocation (::std::move (macro_path),
- ::std::move (delim_tok_tree),
- ::std::move (outer_attrs),
- stmt_or_expr_loc));
- return ExprOrStmt (::std::move (expr));
- }
- else
- {
- // tokens don't match opening delimiters, so produce error
- rust_error_at (
- t2->get_locus (),
- "unexpected token '%s' - expecting closing delimiter '%s' (for a "
- "macro invocation)",
- t2->get_token_description (),
- (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}")));
- 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 = NULL;
-
- 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 == NULL)
- {
- rust_error_at (t2->get_locus (),
- "failed to parse struct expr struct");
- return ExprOrStmt::create_error ();
- }
- }
- else
- {
- // assume path - make statement if final ';'
- // lexer.skip_token();
-
- // HACK: add outer attrs to path
- path.replace_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 == NULL)
- {
- rust_error_at (t2->get_locus (),
- "failed to parse struct expr tuple");
- 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.replace_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.
-::std::unique_ptr<AST::StructExprField>
-Parser::parse_struct_expr_field ()
-{
- // DEBUG:
- fprintf (stderr, "beginning struct/enum expr field parsing \n");
-
- 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse struct expression field with "
- "identifier and expression");
- return NULL;
- }
-
- // DEBUG:
- fprintf (
- stderr,
- "struct/enum expr field parsing field identifier value done \n");
-
- return ::std::unique_ptr<AST::StructExprFieldIdentifierValue> (
- new AST::StructExprFieldIdentifierValue (::std::move (ident),
- ::std::move (expr)));
- }
- else
- {
- // struct expr field with identifier only
- Identifier ident = t->get_str ();
- lexer.skip_token ();
-
- // DEBUG:
- fprintf (stderr,
- "struct/enum expr field parsing field identifier done \n");
-
- return ::std::unique_ptr<AST::StructExprFieldIdentifier> (
- new AST::StructExprFieldIdentifier (::std::move (ident)));
- }
- 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 NULL;
- }
-
- // parse field expression (required)
- ::std::unique_ptr<AST::Expr> expr = parse_expr ();
- if (expr == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse expr in struct (or enum) expr "
- "field with tuple index");
- return NULL;
- }
-
- // DEBUG:
- fprintf (stderr,
- "struct/enum expr field parsing field index value done \n");
-
- return ::std::unique_ptr<AST::StructExprFieldIndexValue> (
- new AST::StructExprFieldIndexValue (index, ::std::move (expr)));
- }
- case DOT_DOT:
- // this is a struct base and can't be parsed here, so just return nothing
- // without erroring
-
- // DEBUG:
- fprintf (stderr, "struct/enum expr field parsing failed - '..' \n");
-
- return NULL;
- default:
- // DEBUG:
- fprintf (stderr,
- "struct/enum expr field parsing failed - unrecognised char \n");
-
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' as first token of struct expr "
- "field - expected identifier "
- "or int literal",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a macro invocation or macro invocation semi.
-ExprOrStmt
-Parser::parse_macro_invocation_maybe_semi (
- ::std::vector<AST::Attribute> outer_attrs)
-{
- Location macro_locus = lexer.peek_token ()->get_locus ();
- AST::SimplePath macro_path = parse_simple_path ();
- if (macro_path.is_empty ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse simple path in macro invocation or semi");
- 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:
- rust_error_at (t3->get_locus (),
- "unrecognised token '%s' 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;
-
- 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 == NULL)
- {
- rust_error_at (t3->get_locus (),
- "failed to parse token tree for macro invocation (or "
- "semi) - found '%s'",
- t3->get_token_description ());
- return ExprOrStmt::create_error ();
- }
-
- token_trees.push_back (::std::move (tree));
-
- t3 = lexer.peek_token ();
- }
-
- // 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).
- */
-
- if (lexer.peek_token ()->get_id () == SEMICOLON)
- {
- lexer.skip_token ();
-
- ::std::unique_ptr<AST::MacroInvocationSemi> stmt (
- new AST::MacroInvocationSemi (::std::move (macro_path), type,
- ::std::move (token_trees),
- ::std::move (outer_attrs),
- macro_locus));
- return ExprOrStmt (::std::move (stmt));
- }
-
- // otherwise, create macro invocation
- AST::DelimTokenTree delim_tok_tree (type, ::std::move (token_trees),
- tok_tree_loc);
-
- ::std::unique_ptr<AST::MacroInvocation> expr (
- new AST::MacroInvocation (::std::move (macro_path),
- ::std::move (delim_tok_tree),
- ::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
- rust_error_at (
- t->get_locus (),
- "unexpected token '%s' - expecting closing delimiter '%s' (for a "
- "macro invocation)",
- t->get_token_description (),
- (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}")));
- return ExprOrStmt::create_error ();
- }
-}
-
-// "Unexpected token" panic mode - flags gcc error at unexpected token
-void
-Parser::unexpected_token (const_TokenPtr t)
-{
- ::rust_error_at (t->get_locus (), "unexpected %s\n",
- t->get_token_description ());
-}
-
-// Crappy "error recovery" performed after error by skipping tokens until a
-// semi-colon is found
-void
-Parser::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. */
-bool
-Parser::skip_token (TokenId token_id)
-{
- return expect_token (token_id) != const_TokenPtr ();
-}
-
-/* Checks the current token - if id is same as expected, skips and returns it,
- * otherwise diagnoses error and returns null. */
-const_TokenPtr
-Parser::expect_token (TokenId token_id)
-{
- const_TokenPtr t = lexer.peek_token ();
- if (t->get_id () == token_id)
- {
- lexer.skip_token ();
- return t;
- }
- else
- {
- rust_error_at (t->get_locus (), "expecting %s but %s found!\n",
- get_token_description (token_id),
- t->get_token_description ());
-
- return const_TokenPtr ();
- }
-}
-
-// Skips all tokens until EOF or }. Don't use.
-void
-Parser::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. */
-void
-Parser::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. */
-void
-Parser::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)
-void
-Parser::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
- /*if (t->get_id() == RIGHT_SQUARE) {
- lexer.skip_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? */
-::std::unique_ptr<AST::Expr>
-Parser::parse_expr (int right_binding_power,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions)
-{
- const_TokenPtr current_token = lexer.peek_token ();
- lexer.skip_token ();
-
- // parse null denotation (unary part of expression)
- ::std::unique_ptr<AST::Expr> expr
- = null_denotation_NEW (current_token, ::std::move (outer_attrs),
- restrictions);
-
- // DEBUG
- fprintf (stderr, "finished parsing null denotation\n");
-
- if (expr == NULL)
- {
- // DEBUG
- fprintf (stderr,
- "null denotation is null; returning null for parse_expr\n");
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr,
- "null denotation is not null - going on to left denotation\n");
-
- // DEBUG
- fprintf (stderr, "initial rbp: '%i', initial lbp: '%i' (for '%s')\n",
- right_binding_power, left_binding_power (lexer.peek_token ()),
- lexer.peek_token ()->get_token_description ());
-
- // 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::vector<AST::Attribute> (), restrictions);
-
- // DEBUG
- fprintf (stderr, "successfully got left_denotation in parse_expr \n");
-
- if (expr == NULL)
- {
- // DEBUG
- fprintf (stderr,
- "left denotation is null; returning null for parse_expr\n");
-
- return NULL;
- }
-
- // DEBUG
- fprintf (stderr,
- "left denotation is not null - going to next iteration \n");
- }
-
- // DEBUG
- fprintf (stderr, "successfully parsed expr in parse_expr - returning \n");
-
- return expr;
-}
-
-/* Parse expression with lowest left binding power. FIXME: this may only apply
- * to expressions without blocks as they are the only ones to have precedence?
- */
-::std::unique_ptr<AST::Expr>
-Parser::parse_expr (::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions)
-{
- // HACK: only call parse_expr(LBP_LOWEST) after ensuring it is not an
- // expression with block?
- 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. */
-::std::unique_ptr<AST::Expr>
-Parser::null_denotation_NEW (const_TokenPtr tok,
- ::std::vector<AST::Attribute> 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: {
- // when encountering identifier, lookup in scope
- SymbolPtr s = scope.lookup(tok->get_str());
- if (s == NULL) {
- rust_error_at(tok->get_locus(), "variable '%s' not declared in the
- current scope", tok->get_str().c_str());
-
- return Tree::error();
- }
- // expression is just its VAR_DECL that was stored in the Symbol at
- declaration return Tree(s->get_tree_decl(), tok->get_locus());
- }*/
- // symbol table must be created in semantic analysis pass, so can't use
- // this
- case IDENTIFIER: {
- // DEBUG
- fprintf (stderr, "beginning null denotation identifier handling\n");
-
- // 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:
- fprintf (stderr, "finished null denotation identifier path parsing - "
- "next is branching \n");
-
- // branch on next token
- const_TokenPtr t = lexer.peek_token ();
- switch (t->get_id ())
- {
- case EXCLAM:
- // macro
- return parse_macro_invocation_partial (::std::move (path),
- ::std::move (outer_attrs));
- case LEFT_CURLY: {
- bool not_a_block
- = lexer.peek_token (1)->get_id () == IDENTIFIER
- && (lexer.peek_token (2)->get_id () == COMMA
- || (lexer.peek_token (2)->get_id () == COLON
- && (lexer.peek_token (4)->get_id () == COMMA
- || !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
- fprintf (stderr, "values of lookahead: '%s' '%s' '%s' '%s' \n",
- 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 ());
-
- fprintf (stderr, "can be struct expr: '%s', not a block: '%s'\n",
- 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)
- {
- // 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.replace_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 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.replace_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.replace_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 (true);
- path.replace_outer_attrs (::std::move (outer_attrs));
- return ::std::unique_ptr<AST::QualifiedPathInExpression> (
- new AST::QualifiedPathInExpression (::std::move (path)));
- }
- 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_locus ()));
- case FLOAT_LITERAL:
- // encode as float?
- return ::std::unique_ptr<AST::LiteralExpr> (
- new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT,
- tok->get_locus ()));
- case STRING_LITERAL:
- return ::std::unique_ptr<AST::LiteralExpr> (
- new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
- tok->get_locus ()));
- case TRUE_LITERAL:
- return ::std::unique_ptr<AST::LiteralExpr> (
- new AST::LiteralExpr ("true", AST::Literal::BOOL, tok->get_locus ()));
- case FALSE_LITERAL:
- return ::std::unique_ptr<AST::LiteralExpr> (
- new AST::LiteralExpr ("false", AST::Literal::BOOL, tok->get_locus ()));
- case LEFT_PAREN: { // have to parse whole expression if inside brackets
- /* recursively invoke parse_expression with lowest priority possible as
- * it it were a top-level expression. */
- /*AST::Expr* expr = parse_expr();
- tok = lexer.peek_token();
-
- // end of expression must be a close-bracket
- if (tok->get_id() != RIGHT_PAREN)
- rust_error_at(
- tok->get_locus(), "expecting ')' but %s found\n",
- tok->get_token_description()); else lexer.skip_token();
-
- return expr;
- // FIXME: this assumes grouped expression - could be tuple expression if
- commas inside*/
-
- return parse_grouped_or_tuple_expr (::std::move (outer_attrs), true);
- }
- /*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 == NULL)
- return NULL;
- // 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
- NULL;
- }
-
- 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;
- ::std::unique_ptr<AST::Expr> expr
- = parse_expr (LBP_UNARY_MINUS, ::std::vector<AST::Attribute> (),
- entered_from_unary);
-
- if (expr == NULL)
- return NULL;
- // 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), AST::NegationExpr::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;
- ::std::unique_ptr<AST::Expr> expr
- = parse_expr (LBP_UNARY_EXCLAM, ::std::vector<AST::Attribute> (),
- entered_from_unary);
-
- if (expr == NULL)
- return NULL;
- // 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), AST::NegationExpr::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, ::std::vector<AST::Attribute> (),
- 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 = NULL;
- 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, ::std::vector<AST::Attribute> (),
- entered_from_unary);
- is_mut_borrow = true;
- }
- else
- {
- expr = parse_expr (LBP_UNARY_AMP, ::std::vector<AST::Attribute> (),
- 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 = NULL;
- 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, ::std::vector<AST::Attribute> (),
- entered_from_unary);
- is_mut_borrow = true;
- }
- else
- {
- expr = parse_expr (LBP_UNARY_AMP, ::std::vector<AST::Attribute> (),
- 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
- rust_error_at (tok->get_locus (),
- "found null denotation scope resolution operator, and "
- "haven't written handling for it.");
- return NULL;
- }
- case SELF:
- case SELF_ALIAS:
- case DOLLAR_SIGN:
- case CRATE:
- case SUPER: {
- // DEBUG
- fprintf (stderr, "beginning null denotation "
- "self/self-alias/dollar/crate/super handling\n");
-
- // 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
- fprintf (stderr,
- "just finished parsing path (going to disambiguate) - peeked "
- "token is '%s'\n",
- 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.replace_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
- fprintf (stderr, "can_be_struct_expr: %s\n",
- 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.replace_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.replace_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.replace_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), true);
- case BREAK:
- // FIXME: is this really a null denotation expression?
- return parse_break_expr (::std::move (outer_attrs), true);
- case CONTINUE:
- return parse_continue_expr (::std::move (outer_attrs), true);
- case LEFT_CURLY:
- // ok - this is an expression with block for once.
- return parse_block_expr (::std::move (outer_attrs), true);
- case MATCH_TOK:
- // also an expression with block
- return parse_match_expr (::std::move (outer_attrs), true);
- case LEFT_SQUARE:
- // array definition expr (not indexing)
- return parse_array_expr (::std::move (outer_attrs), true);
- default:
- rust_error_at (tok->get_locus (),
- "found unexpected token '%s' in null denotation",
- tok->get_token_description ());
- return NULL;
- }
-}
-
-/* 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. */
-::std::unique_ptr<AST::Expr>
-Parser::left_denotation (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions)
-{
- // Token passed in has already been skipped, so peek gives "next" token
- /*BinaryHandler binary_handler = get_binary_handler(tok->get_id());
- if (binary_handler == NULL) {
- unexpected_token(tok);
- return NULL;
- }
-
- return (this->*binary_handler)(tok, left);*/
- // can't do with binary handler because same token used for several operators
-
- switch (tok->get_id ())
- {
- // FIXME: allow for outer attributes to be applied
- case QUESTION_MARK: {
- Location left_locus = left->get_locus_slow ();
- // 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);
- case MINUS:
- // difference expression - binary infix
- return parse_binary_minus_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case ASTERISK:
- // product expression - binary infix
- return parse_binary_mult_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case DIV:
- // quotient expression - binary infix
- return parse_binary_div_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case PERCENT:
- // modulo expression - binary infix
- return parse_binary_mod_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case AMP:
- // logical or bitwise and expression - binary infix
- return parse_bitwise_and_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case PIPE:
- // logical or bitwise or expression - binary infix
- return parse_bitwise_or_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case CARET:
- // logical or bitwise xor expression - binary infix
- return parse_bitwise_xor_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case LEFT_SHIFT:
- // left shift expression - binary infix
- return parse_left_shift_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), restrictions);
- case RIGHT_SHIFT:
- // right shift expression - binary infix
- return parse_right_shift_expr (tok, ::std::move (left),
- ::std::move (outer_attrs), 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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?
- rust_error_at (tok->get_locus (),
- "found scope resolution operator in left denotation "
- "function - this should probably be handled elsewhere.");
- return NULL;
- 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
- return parse_tuple_index_expr_float (tok, ::std::move (left),
- ::std::move (outer_attrs),
- restrictions);
- default:
- rust_error_at (tok->get_locus (),
- "found unexpected token '%s' in left denotation",
- tok->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a binary addition expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_binary_plus_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::ADD,
- locus));
-}
-
-// Parses a binary subtraction expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_binary_minus_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::SUBTRACT,
- locus));
-}
-
-// Parses a binary multiplication expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_binary_mult_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::MULTIPLY,
- locus));
-}
-
-// Parses a binary division expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_binary_div_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::DIVIDE,
- locus));
-}
-
-// Parses a binary modulo expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_binary_mod_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::MODULUS,
- locus));
-}
-
-// Parses a binary bitwise (or eager logical) and expression (with Pratt
-// parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_bitwise_and_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::BITWISE_AND,
- locus));
-}
-
-// Parses a binary bitwise (or eager logical) or expression (with Pratt
-// parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_bitwise_or_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::BITWISE_OR,
- locus));
-}
-
-// Parses a binary bitwise (or eager logical) xor expression (with Pratt
-// parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_bitwise_xor_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::BITWISE_XOR,
- locus));
-}
-
-// Parses a binary left shift expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_left_shift_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::LEFT_SHIFT,
- locus));
-}
-
-// Parses a binary right shift expression (with Pratt parsing).
-::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
-Parser::parse_right_shift_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
- new AST::ArithmeticOrLogicalExpr (::std::move (left), ::std::move (right),
- AST::ArithmeticOrLogicalExpr::RIGHT_SHIFT,
- locus));
-}
-
-// Parses a binary equal to expression (with Pratt parsing).
-::std::unique_ptr<AST::ComparisonExpr>
-Parser::parse_binary_equal_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (), restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (::std::move (left), ::std::move (right),
- AST::ComparisonExpr::EQUAL, locus));
-}
-
-// Parses a binary not equal to expression (with Pratt parsing).
-::std::unique_ptr<AST::ComparisonExpr>
-Parser::parse_binary_not_equal_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (::std::move (left), ::std::move (right),
- AST::ComparisonExpr::NOT_EQUAL, locus));
-}
-
-// Parses a binary greater than expression (with Pratt parsing).
-::std::unique_ptr<AST::ComparisonExpr>
-Parser::parse_binary_greater_than_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (::std::move (left), ::std::move (right),
- AST::ComparisonExpr::GREATER_THAN, locus));
-}
-
-// Parses a binary less than expression (with Pratt parsing).
-::std::unique_ptr<AST::ComparisonExpr>
-Parser::parse_binary_less_than_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (::std::move (left), ::std::move (right),
- AST::ComparisonExpr::LESS_THAN, locus));
-}
-
-// Parses a binary greater than or equal to expression (with Pratt parsing).
-::std::unique_ptr<AST::ComparisonExpr>
-Parser::parse_binary_greater_equal_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (::std::move (left), ::std::move (right),
- AST::ComparisonExpr::GREATER_OR_EQUAL, locus));
-}
-
-// Parses a binary less than or equal to expression (with Pratt parsing).
-::std::unique_ptr<AST::ComparisonExpr>
-Parser::parse_binary_less_equal_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::ComparisonExpr> (
- new AST::ComparisonExpr (::std::move (left), ::std::move (right),
- AST::ComparisonExpr::LESS_OR_EQUAL, locus));
-}
-
-// Parses a binary lazy boolean or expression (with Pratt parsing).
-::std::unique_ptr<AST::LazyBooleanExpr>
-Parser::parse_lazy_or_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::LazyBooleanExpr> (
- new AST::LazyBooleanExpr (::std::move (left), ::std::move (right),
- AST::LazyBooleanExpr::LOGICAL_OR, locus));
-}
-
-// Parses a binary lazy boolean and expression (with Pratt parsing).
-::std::unique_ptr<AST::LazyBooleanExpr>
-Parser::parse_lazy_and_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::LazyBooleanExpr> (
- new AST::LazyBooleanExpr (::std::move (left), ::std::move (right),
- AST::LazyBooleanExpr::LOGICAL_AND, locus));
-}
-
-// Parses a pseudo-binary infix type cast expression (with Pratt parsing).
-::std::unique_ptr<AST::TypeCastExpr>
-Parser::parse_type_cast_expr (const_TokenPtr tok ATTRIBUTE_UNUSED,
- ::std::unique_ptr<AST::Expr> expr_to_cast,
- ::std::vector<AST::Attribute> 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 == NULL)
- return NULL;
- // 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_slow ();
-
- 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).
-::std::unique_ptr<AST::AssignmentExpr>
-Parser::parse_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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_ASSIG - 1, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::AssignmentExpr> (
- new AST::AssignmentExpr (::std::move (left), ::std::move (right), locus));
-}
-
-// Parses a binary add-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_plus_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::ADD, locus));
-}
-
-// Parses a binary minus-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_minus_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::SUBTRACT,
- locus));
-}
-
-// Parses a binary multiplication-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_mult_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::MULTIPLY,
- locus));
-}
-
-// Parses a binary division-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_div_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::DIVIDE,
- locus));
-}
-
-// Parses a binary modulo-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_mod_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::MODULUS,
- locus));
-}
-
-// Parses a binary and-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_and_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::BITWISE_AND,
- locus));
-}
-
-// Parses a binary or-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_or_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::BITWISE_OR,
- locus));
-}
-
-// Parses a binary xor-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_xor_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::BITWISE_XOR,
- locus));
-}
-
-// Parses a binary left shift-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_left_shift_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::LEFT_SHIFT,
- locus));
-}
-
-// Parses a binary right shift-assignment expression (with Pratt parsing).
-::std::unique_ptr<AST::CompoundAssignmentExpr>
-Parser::parse_right_shift_assig_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // 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_slow ();
-
- return ::std::unique_ptr<AST::CompoundAssignmentExpr> (
- new AST::CompoundAssignmentExpr (::std::move (left), ::std::move (right),
- AST::CompoundAssignmentExpr::RIGHT_SHIFT,
- locus));
-}
-
-// Parses a postfix unary await expression (with Pratt parsing).
-::std::unique_ptr<AST::AwaitExpr>
-Parser::parse_await_expr (const_TokenPtr tok,
- ::std::unique_ptr<AST::Expr> expr_to_await,
- ::std::vector<AST::Attribute> 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))
- {
- rust_error_at (tok->get_locus (), "failed to skip 'await' in await expr "
- "- this is probably a deep issue.");
- // skip somewhere?
- return NULL;
- }
-
- // TODO: check inside async block in semantic analysis
- Location locus = expr_to_await->get_locus_slow ();
-
- 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). */
-::std::unique_ptr<AST::RangeExpr>
-Parser::parse_led_range_exclusive_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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)
- ::std::unique_ptr<AST::Expr> right
- = parse_expr (LBP_DOT_DOT, ::std::vector<AST::Attribute> (), restrictions);
-
- Location locus = left->get_locus_slow ();
-
- if (right == NULL)
- {
- // 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). */
-::std::unique_ptr<AST::RangeExpr>
-Parser::parse_nud_range_exclusive_expr (
- const_TokenPtr tok,
- ::std::vector<AST::Attribute> outer_attrs ATTRIBUTE_UNUSED)
-{
- // 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, ::std::vector<AST::Attribute> ());
-
- Location locus = tok->get_locus ();
-
- if (right == NULL)
- {
- // 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.
-::std::unique_ptr<AST::RangeFromToInclExpr>
-Parser::parse_range_inclusive_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> 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, ::std::vector<AST::Attribute> (),
- restrictions);
- if (right == NULL)
- return NULL;
- // FIXME: make non-associative
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = left->get_locus_slow ();
-
- return ::std::unique_ptr<AST::RangeFromToInclExpr> (
- new AST::RangeFromToInclExpr (::std::move (left), ::std::move (right),
- locus));
-}
-
-// Parses an inclusive range-to prefix unary expression.
-::std::unique_ptr<AST::RangeToInclExpr>
-Parser::parse_range_to_inclusive_expr (const_TokenPtr tok,
- ::std::vector<AST::Attribute> 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 == NULL)
- return NULL;
- // 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.
-::std::unique_ptr<AST::TupleIndexExpr>
-Parser::parse_tuple_index_expr (const_TokenPtr tok ATTRIBUTE_UNUSED,
- ::std::unique_ptr<AST::Expr> tuple_expr,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions ATTRIBUTE_UNUSED)
-{
- // parse int literal (as token already skipped)
- const_TokenPtr index_tok = expect_token (INT_LITERAL);
- if (index_tok == NULL)
- {
- return NULL;
- }
- ::std::string index = index_tok->get_str ();
-
- // convert to integer
- int index_int = atoi (index.c_str ());
-
- Location locus = tuple_expr->get_locus_slow ();
-
- 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.
-::std::unique_ptr<AST::ArrayIndexExpr>
-Parser::parse_index_expr (const_TokenPtr tok ATTRIBUTE_UNUSED,
- ::std::unique_ptr<AST::Expr> array_expr,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions)
-{
- // parse RHS (as tok has already been consumed in parse_expression)
- ::std::unique_ptr<AST::Expr> index_expr
- = parse_expr (LBP_ARRAY_REF, ::std::vector<AST::Attribute> (),
- restrictions);
- if (index_expr == NULL)
- return NULL;
-
- // skip ']' at end of array
- if (!skip_token (RIGHT_SQUARE))
- {
- // skip somewhere?
- return NULL;
- }
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = array_expr->get_locus_slow ();
-
- 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.
-::std::unique_ptr<AST::FieldAccessExpr>
-Parser::parse_field_access_expr (
- const_TokenPtr tok ATTRIBUTE_UNUSED, ::std::unique_ptr<AST::Expr> struct_expr,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions ATTRIBUTE_UNUSED)
-{
- // get field name identifier (assume that this is a field access expr and not
- // say await)
- const_TokenPtr ident_tok = expect_token (IDENTIFIER);
- Identifier ident = ident_tok->get_str ();
-
- Location locus = struct_expr->get_locus_slow ();
-
- // 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.
-::std::unique_ptr<AST::MethodCallExpr>
-Parser::parse_method_call_expr (const_TokenPtr tok,
- ::std::unique_ptr<AST::Expr> receiver_expr,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions ATTRIBUTE_UNUSED)
-{
- // parse path expr segment
- AST::PathExprSegment segment = parse_path_expr_segment ();
- if (segment.is_error ())
- {
- rust_error_at (tok->get_locus (),
- "failed to parse path expr segment of method call expr");
- return NULL;
- }
-
- // skip left parentheses
- if (!skip_token (LEFT_PAREN))
- {
- return NULL;
- }
-
- // 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse method param in method call");
- return NULL;
- }
- 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 NULL;
- }
-
- // TODO: check types. actually do so in semantic analysis pass.
- Location locus = receiver_expr->get_locus_slow ();
-
- 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.
-::std::unique_ptr<AST::CallExpr>
-Parser::parse_function_call_expr (const_TokenPtr tok ATTRIBUTE_UNUSED,
- ::std::unique_ptr<AST::Expr> function_expr,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions
- ATTRIBUTE_UNUSED)
-{
- // 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse function param in function call");
- return NULL;
- }
- 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 NULL;
- }
-
- // TODO: check types. actually, do so during semantic analysis
- Location locus = function_expr->get_locus_slow ();
-
- 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).
-::std::unique_ptr<AST::MacroInvocation>
-Parser::parse_macro_invocation_partial (
- AST::PathInExpression path, ::std::vector<AST::Attribute> outer_attrs)
-{
- // macro invocation
- if (!skip_token (EXCLAM))
- {
- return NULL;
- }
-
- // convert PathInExpression to SimplePath - if this isn't possible, error
- AST::SimplePath converted_path = path.as_simple_path ();
- if (converted_path.is_empty ())
- {
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse simple path in macro invocation");
- return NULL;
- }
-
- AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
-
- fprintf (stderr, "successfully parsed macro invocation (via partial)\n");
-
- Location macro_locus = converted_path.get_locus ();
-
- return ::std::unique_ptr<AST::MacroInvocation> (
- new AST::MacroInvocation (::std::move (converted_path),
- ::std::move (tok_tree), ::std::move (outer_attrs),
- macro_locus));
-}
-
-// Parses a struct expr struct with a path in expression already parsed (but not
-// '{' token).
-::std::unique_ptr<AST::StructExprStruct>
-Parser::parse_struct_expr_struct_partial (
- AST::PathInExpression path, ::std::vector<AST::Attribute> outer_attrs)
-{
- // assume struct expr struct (as struct-enum disambiguation requires name
- // lookup) again, make statement if final ';'
- if (!skip_token (LEFT_CURLY))
- {
- return NULL;
- }
-
- // parse inner attributes
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse struct (or enum) expr field");
- return NULL;
- }
-
- // DEBUG:
- fprintf (stderr,
- "struct/enum expr field validated to not be null \n");
-
- fields.push_back (::std::move (field));
-
- // DEBUG:
- fprintf (stderr, "struct/enum expr field pushed back \n");
-
- if (lexer.peek_token ()->get_id () != COMMA)
- {
- // DEBUG:
- fprintf (stderr, "lack of comma detected in struct/enum expr "
- "fields - break \n");
- break;
- }
- lexer.skip_token ();
-
- // DEBUG:
- fprintf (stderr, "struct/enum expr fields comma skipped \n");
-
- t = lexer.peek_token ();
- }
-
- // DEBUG:
- fprintf (stderr, "struct/enum expr about to parse struct base \n");
-
- // parse struct base if it exists
- AST::StructBase struct_base = AST::StructBase::error ();
- if (lexer.peek_token ()->get_id () == DOT_DOT)
- {
- lexer.skip_token ();
-
- // parse required struct base expr
- ::std::unique_ptr<AST::Expr> base_expr = parse_expr ();
- if (base_expr == NULL)
- {
- rust_error_at (
- lexer.peek_token ()->get_locus (),
- "failed to parse struct base expression in struct "
- "expression");
- return NULL;
- }
-
- // DEBUG:
- fprintf (stderr,
- "struct/enum expr - parsed and validated base expr \n");
-
- struct_base = AST::StructBase (::std::move (base_expr));
-
- // DEBUG:
- fprintf (stderr, "assigned struct base to new struct base \n");
- }
-
- if (!skip_token (RIGHT_CURLY))
- {
- return NULL;
- }
-
- // DEBUG:
- fprintf (
- stderr,
- "struct/enum expr skipped right curly - done and ready to return \n");
-
- 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:
- rust_error_at (t->get_locus (),
- "unrecognised token '%s' in struct (or enum) expression - "
- "expected '}', identifier, int literal, or '..'",
- t->get_token_description ());
- return NULL;
- }
-}
-
-// Parses a struct expr tuple with a path in expression already parsed (but not
-// '(' token).
-::std::unique_ptr<AST::CallExpr>
-Parser::parse_struct_expr_tuple_partial (
- AST::PathInExpression path, ::std::vector<AST::Attribute> outer_attrs)
-{
- if (!skip_token (LEFT_PAREN))
- {
- return NULL;
- }
-
- ::std::vector<AST::Attribute> 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 == NULL)
- {
- rust_error_at (t->get_locus (),
- "failed to parse expression in struct "
- "(or enum) expression tuple");
- return NULL;
- }
- 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 NULL;
- }
-
- 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. */
-AST::PathInExpression
-Parser::parse_path_in_expression_pratt (const_TokenPtr tok)
-{
- // HACK-y way of making up for pratt-parsing consuming first token
-
- // DEBUG
- fprintf (stderr, "current peek token when starting path pratt parse: '%s'\n",
- 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:
- rust_error_at (tok->get_locus (),
- "unrecognised token '%s' 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 (initial_str, tok->get_locus (),
- ::std::move (generic_args));
- }
- if (initial_segment.is_error ())
- {
- // skip after somewhere?
- // don't necessarily throw error but yeah
-
- // DEBUG
- fprintf (stderr, "initial segment is error - returning null\n");
-
- 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?
- rust_error_at (t->get_locus (),
- "could not parse path expression segment");
- return AST::PathInExpression::create_error ();
- }
-
- segments.push_back (::std::move (segment));
-
- t = lexer.peek_token ();
- }
-
- // DEBUG:
- fprintf (
- stderr,
- "current token (just about to return path to null denotation): '%s'\n",
- 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).
-::std::unique_ptr<AST::ClosureExpr>
-Parser::parse_closure_expr_pratt (const_TokenPtr tok,
- ::std::vector<AST::Attribute> 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?
- rust_error_at (t->get_locus (),
- "could not parse closure param");
- return NULL;
- }
- 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 NULL;
- }
- break;
- }
- default:
- rust_error_at (
- tok->get_locus (),
- "unexpected token '%s' in closure expression - expected '|' or '||'",
- tok->get_token_description ());
- // skip somewhere?
- return NULL;
- }
-
- // 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 == NULL)
- {
- // error
- rust_error_at (tok->get_locus (), "failed to parse type for closure");
- // skip somewhere?
- return NULL;
- }
-
- // parse block expr, which is required
- ::std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
- if (block == NULL)
- {
- // error
- rust_error_at (lexer.peek_token ()->get_locus (),
- "failed to parse block expr in closure");
- // skip somewhere?
- return NULL;
- }
-
- 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 == NULL)
- {
- rust_error_at (tok->get_locus (),
- "failed to parse expression in closure");
- // skip somewhere?
- return NULL;
- }
-
- 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. */
-::std::unique_ptr<AST::TupleIndexExpr>
-Parser::parse_tuple_index_expr_float (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> tuple_expr,
- ::std::vector<AST::Attribute> outer_attrs,
- ParseRestrictions restrictions ATTRIBUTE_UNUSED)
-{
- // only works on float literals
- if (tok->get_id () != FLOAT_LITERAL)
- {
- return NULL;
- }
-
- // DEBUG:
- fprintf (stderr, "exact string form of float: '%s'\n",
- 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_slow ();
-
- 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;
-bool
-Parser::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.
-bool
-Parser::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.
-void
-Parser::debug_dump_lex_output ()
-{
- Rust::const_TokenPtr tok = lexer.peek_token ();
-
- while (true)
- {
- 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 ();
-
- fprintf (stderr, "<id=%s%s, %s\n", tok->token_id_to_str (),
- has_text ? (std::string (", text=") + tok->get_str ()
- + std::string (", typehint=")
- + std::string (tok->get_type_hint_str ()))
- .c_str ()
- : "",
- lexer.get_line_map ()->to_string (loc).c_str ());
-
- if (tok->get_id () == Rust::END_OF_FILE)
- break;
-
- lexer.skip_token ();
- tok = lexer.peek_token ();
- }
-}
+namespace Rust {
-// Parses crate and dumps AST to stderr, recursively.
-void
-Parser::debug_dump_ast_output (AST::Crate &crate)
-{
- // print crate "as string", which then calls each item as string, etc.
- fprintf (stderr, "%s", crate.as_string ().c_str ());
-}
} // namespace Rust
diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h
index 5bbfefc..2cf503a 100644
--- a/gcc/rust/parse/rust-parse.h
+++ b/gcc/rust/parse/rust-parse.h
@@ -10,34 +10,34 @@ namespace Rust {
* probably take up the same amount of space. */
struct ExprOrStmt
{
- ::std::unique_ptr<AST::ExprWithoutBlock> expr;
- ::std::unique_ptr<AST::Stmt> stmt;
+ std::unique_ptr<AST::ExprWithoutBlock> expr;
+ std::unique_ptr<AST::Stmt> stmt;
/* I was going to resist the urge to make this a real class and make it POD,
* but construction in steps is too difficult. So it'll just also have a
* constructor. */
// expression constructor
- ExprOrStmt (::std::unique_ptr<AST::ExprWithoutBlock> expr)
- : expr (::std::move (expr))
+ ExprOrStmt (std::unique_ptr<AST::ExprWithoutBlock> expr)
+ : expr (std::move (expr))
{}
// statement constructor
- ExprOrStmt (::std::unique_ptr<AST::Stmt> stmt) : stmt (::std::move (stmt)) {}
+ ExprOrStmt (std::unique_ptr<AST::Stmt> stmt) : stmt (std::move (stmt)) {}
// Returns whether this object is in an error state.
- inline bool is_error () const
+ bool is_error () const
{
- return (expr == NULL && stmt == NULL) || (expr != NULL && stmt != NULL);
+ return (expr == nullptr && stmt == nullptr) || (expr != nullptr && stmt != nullptr);
}
// Returns an error state object.
- static ExprOrStmt create_error () { return ExprOrStmt (NULL, NULL); }
+ static ExprOrStmt create_error () { return ExprOrStmt (nullptr, nullptr); }
~ExprOrStmt () = default;
- // no copy constructors/assignment copy as simple object like this shouldn't
- // require it
+ /* no copy constructors/assignment as simple object like this shouldn't
+ * require it */
// move constructors
ExprOrStmt (ExprOrStmt &&other) = default;
@@ -58,7 +58,7 @@ private:
};
/* Restrictions on parsing used to signal that certain ambiguous grammar
- * features should be parsed in a certain way.*/
+ * features should be parsed in a certain way. */
struct ParseRestrictions
{
bool can_be_struct_expr = true;
@@ -68,6 +68,8 @@ struct ParseRestrictions
};
// Parser implementation for gccrs.
+// TODO: if updated to C++20, ManagedTokenSource would be useful as a concept
+template <typename ManagedTokenSource>
class Parser
{
private:
@@ -85,18 +87,18 @@ private:
void parse_statement_seq (bool (Parser::*done) ());
// AST-related stuff - maybe move or something?
- ::std::vector<AST::Attribute> parse_inner_attributes ();
+ std::vector<AST::Attribute> parse_inner_attributes ();
AST::Attribute parse_inner_attribute ();
- ::std::vector<AST::Attribute> parse_outer_attributes ();
+ std::vector<AST::Attribute> parse_outer_attributes ();
AST::Attribute parse_outer_attribute ();
AST::Attribute parse_attribute_body ();
- ::std::unique_ptr<AST::AttrInput> parse_attr_input ();
+ std::unique_ptr<AST::AttrInput> parse_attr_input ();
// Path-related
AST::SimplePath parse_simple_path ();
AST::SimplePathSegment parse_simple_path_segment ();
AST::TypePath parse_type_path ();
- ::std::unique_ptr<AST::TypePathSegment> parse_type_path_segment ();
+ std::unique_ptr<AST::TypePathSegment> parse_type_path_segment ();
AST::PathIdentSegment parse_path_ident_segment ();
AST::GenericArgs parse_path_generic_args ();
AST::GenericArgsBinding parse_generic_args_binding ();
@@ -110,436 +112,436 @@ private:
// Token tree or macro related
AST::DelimTokenTree parse_delim_token_tree ();
- ::std::unique_ptr<AST::TokenTree> parse_token_tree ();
- ::std::unique_ptr<AST::MacroRulesDefinition>
- parse_macro_rules_def (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::MacroInvocationSemi>
- parse_macro_invocation_semi (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::MacroInvocation>
- parse_macro_invocation (::std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::TokenTree> parse_token_tree ();
+ std::unique_ptr<AST::MacroRulesDefinition>
+ parse_macro_rules_def (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::MacroInvocationSemi>
+ parse_macro_invocation_semi (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::MacroInvocation>
+ parse_macro_invocation (std::vector<AST::Attribute> outer_attrs);
AST::MacroRule parse_macro_rule ();
AST::MacroMatcher parse_macro_matcher ();
- ::std::unique_ptr<AST::MacroMatch> parse_macro_match ();
- ::std::unique_ptr<AST::MacroMatchFragment> parse_macro_match_fragment ();
- ::std::unique_ptr<AST::MacroMatchRepetition> parse_macro_match_repetition ();
+ std::unique_ptr<AST::MacroMatch> parse_macro_match ();
+ std::unique_ptr<AST::MacroMatchFragment> parse_macro_match_fragment ();
+ std::unique_ptr<AST::MacroMatchRepetition> parse_macro_match_repetition ();
// Top-level item-related
- ::std::vector< ::std::unique_ptr<AST::Item> > parse_items ();
- ::std::unique_ptr<AST::Item> parse_item (bool called_from_statement);
- ::std::unique_ptr<AST::VisItem>
- parse_vis_item (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::MacroItem>
- parse_macro_item (::std::vector<AST::Attribute> outer_attrs);
+ std::vector< std::unique_ptr<AST::Item> > parse_items ();
+ std::unique_ptr<AST::Item> parse_item (bool called_from_statement);
+ std::unique_ptr<AST::VisItem>
+ parse_vis_item (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::MacroItem>
+ parse_macro_item (std::vector<AST::Attribute> outer_attrs);
AST::Visibility parse_visibility ();
// VisItem subclass-related
- ::std::unique_ptr<AST::Module>
- parse_module (AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ExternCrate>
+ std::unique_ptr<AST::Module>
+ parse_module (AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ExternCrate>
parse_extern_crate (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::UseDeclaration>
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::UseDeclaration>
parse_use_decl (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::UseTree> parse_use_tree ();
- ::std::unique_ptr<AST::Function>
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::UseTree> parse_use_tree ();
+ std::unique_ptr<AST::Function>
parse_function (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
+ std::vector<AST::Attribute> outer_attrs);
AST::FunctionQualifiers parse_function_qualifiers ();
- ::std::vector< ::std::unique_ptr<AST::GenericParam> >
+ std::vector< std::unique_ptr<AST::GenericParam> >
parse_generic_params_in_angles ();
- ::std::vector< ::std::unique_ptr<AST::GenericParam> > parse_generic_params ();
- ::std::vector< ::std::unique_ptr<AST::LifetimeParam> >
+ std::vector< std::unique_ptr<AST::GenericParam> > parse_generic_params ();
+ std::vector< std::unique_ptr<AST::LifetimeParam> >
parse_lifetime_params ();
- ::std::vector<AST::LifetimeParam> parse_lifetime_params_objs ();
+ std::vector<AST::LifetimeParam> parse_lifetime_params_objs ();
AST::LifetimeParam parse_lifetime_param ();
- ::std::vector< ::std::unique_ptr<AST::TypeParam> > parse_type_params ();
- ::std::unique_ptr<AST::TypeParam> parse_type_param ();
- ::std::vector<AST::FunctionParam> parse_function_params ();
+ std::vector< std::unique_ptr<AST::TypeParam> > parse_type_params ();
+ std::unique_ptr<AST::TypeParam> parse_type_param ();
+ std::vector<AST::FunctionParam> parse_function_params ();
AST::FunctionParam parse_function_param ();
- ::std::unique_ptr<AST::Type> parse_function_return_type ();
+ std::unique_ptr<AST::Type> parse_function_return_type ();
AST::WhereClause parse_where_clause ();
- ::std::unique_ptr<AST::WhereClauseItem> parse_where_clause_item ();
- ::std::unique_ptr<AST::LifetimeWhereClauseItem>
+ std::unique_ptr<AST::WhereClauseItem> parse_where_clause_item ();
+ std::unique_ptr<AST::LifetimeWhereClauseItem>
parse_lifetime_where_clause_item ();
- ::std::unique_ptr<AST::TypeBoundWhereClauseItem>
+ std::unique_ptr<AST::TypeBoundWhereClauseItem>
parse_type_bound_where_clause_item ();
- ::std::vector<AST::LifetimeParam> parse_for_lifetimes ();
- ::std::vector< ::std::unique_ptr<AST::TypeParamBound> >
+ std::vector<AST::LifetimeParam> parse_for_lifetimes ();
+ std::vector< std::unique_ptr<AST::TypeParamBound> >
parse_type_param_bounds ();
- ::std::unique_ptr<AST::TypeParamBound> parse_type_param_bound ();
- ::std::unique_ptr<AST::TraitBound> parse_trait_bound ();
- ::std::vector<AST::Lifetime> parse_lifetime_bounds ();
+ std::unique_ptr<AST::TypeParamBound> parse_type_param_bound ();
+ std::unique_ptr<AST::TraitBound> parse_trait_bound ();
+ std::vector<AST::Lifetime> parse_lifetime_bounds ();
AST::Lifetime parse_lifetime ();
- ::std::unique_ptr<AST::TypeAlias>
+ std::unique_ptr<AST::TypeAlias>
parse_type_alias (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::Struct>
- parse_struct (AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::vector<AST::StructField> parse_struct_fields ();
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::Struct>
+ parse_struct (AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::vector<AST::StructField> parse_struct_fields ();
AST::StructField parse_struct_field ();
- ::std::vector<AST::TupleField> parse_tuple_fields ();
+ std::vector<AST::TupleField> parse_tuple_fields ();
AST::TupleField parse_tuple_field ();
- ::std::unique_ptr<AST::Enum>
- parse_enum (AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::vector< ::std::unique_ptr<AST::EnumItem> > parse_enum_items ();
- ::std::unique_ptr<AST::EnumItem> parse_enum_item ();
- ::std::unique_ptr<AST::Union>
- parse_union (AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ConstantItem>
+ std::unique_ptr<AST::Enum>
+ parse_enum (AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::vector< std::unique_ptr<AST::EnumItem> > parse_enum_items ();
+ std::unique_ptr<AST::EnumItem> parse_enum_item ();
+ std::unique_ptr<AST::Union>
+ parse_union (AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ConstantItem>
parse_const_item (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::StaticItem>
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::StaticItem>
parse_static_item (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::Trait>
- parse_trait (AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::TraitItem> parse_trait_item ();
- ::std::unique_ptr<AST::TraitItemType>
- parse_trait_type (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::TraitItemConst>
- parse_trait_const (::std::vector<AST::Attribute> outer_attrs);
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::Trait>
+ parse_trait (AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::TraitItem> parse_trait_item ();
+ std::unique_ptr<AST::TraitItemType>
+ parse_trait_type (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::TraitItemConst>
+ parse_trait_const (std::vector<AST::Attribute> outer_attrs);
AST::SelfParam parse_self_param ();
- ::std::unique_ptr<AST::Impl>
- parse_impl (AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::InherentImplItem> parse_inherent_impl_item ();
- ::std::unique_ptr<AST::InherentImplItem>
+ std::unique_ptr<AST::Impl>
+ parse_impl (AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::InherentImplItem> parse_inherent_impl_item ();
+ std::unique_ptr<AST::InherentImplItem>
parse_inherent_impl_function_or_method (
- AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::TraitImplItem> parse_trait_impl_item ();
- ::std::unique_ptr<AST::TraitImplItem> parse_trait_impl_function_or_method (
- AST::Visibility vis, ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ExternBlock>
+ AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::TraitImplItem> parse_trait_impl_item ();
+ std::unique_ptr<AST::TraitImplItem> parse_trait_impl_function_or_method (
+ AST::Visibility vis, std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ExternBlock>
parse_extern_block (AST::Visibility vis,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ExternalItem> parse_external_item ();
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ExternalItem> parse_external_item ();
AST::NamedFunctionParam parse_named_function_param ();
AST::Method parse_method ();
// Expression-related (Pratt parsed)
- ::std::unique_ptr<AST::Expr>
- parse_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::Expr>
+ parse_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::Expr>
+ std::unique_ptr<AST::Expr>
parse_expr (int right_binding_power,
- ::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::Expr>
- null_denotation_NEW (const_TokenPtr t,
- ::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::Expr>
+ null_denotation (const_TokenPtr t,
+ std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::Expr>
- left_denotation (const_TokenPtr t, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::Expr>
+ left_denotation (const_TokenPtr t, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_binary_plus_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_binary_plus_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions
= ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr> parse_binary_minus_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr> parse_binary_minus_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_binary_mult_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_binary_mult_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions
= ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_binary_div_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_binary_div_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_binary_mod_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_binary_mod_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_bitwise_and_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_bitwise_and_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions
= ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_bitwise_or_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_bitwise_or_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_bitwise_xor_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_bitwise_xor_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions
= ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_left_shift_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_left_shift_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArithmeticOrLogicalExpr>
- parse_right_shift_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+ parse_right_shift_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions
= ParseRestrictions ());
- ::std::unique_ptr<AST::ComparisonExpr> parse_binary_equal_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ComparisonExpr> parse_binary_equal_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ComparisonExpr> parse_binary_not_equal_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ComparisonExpr> parse_binary_not_equal_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_than_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_than_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ComparisonExpr> parse_binary_less_than_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ComparisonExpr> parse_binary_less_than_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_equal_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_equal_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ComparisonExpr> parse_binary_less_equal_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ComparisonExpr> parse_binary_less_equal_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::LazyBooleanExpr>
- parse_lazy_or_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::LazyBooleanExpr>
+ parse_lazy_or_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::LazyBooleanExpr>
- parse_lazy_and_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::LazyBooleanExpr>
+ parse_lazy_and_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::TypeCastExpr>
+ std::unique_ptr<AST::TypeCastExpr>
parse_type_cast_expr (const_TokenPtr tok,
- ::std::unique_ptr<AST::Expr> expr_to_cast,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::Expr> expr_to_cast,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::AssignmentExpr>
- parse_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::AssignmentExpr>
+ parse_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_plus_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_plus_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_minus_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_minus_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions
= ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_mult_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_mult_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_div_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_div_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_mod_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_mod_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_and_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_and_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_or_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_or_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr>
- parse_xor_assig_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr>
+ parse_xor_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr> parse_left_shift_assig_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr> parse_left_shift_assig_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CompoundAssignmentExpr> parse_right_shift_assig_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CompoundAssignmentExpr> parse_right_shift_assig_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::AwaitExpr>
+ std::unique_ptr<AST::AwaitExpr>
parse_await_expr (const_TokenPtr tok,
- ::std::unique_ptr<AST::Expr> expr_to_await,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::MethodCallExpr> parse_method_call_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> receiver_expr,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::Expr> expr_to_await,
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::MethodCallExpr> parse_method_call_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::CallExpr> parse_function_call_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> function_expr,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::CallExpr> parse_function_call_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> function_expr,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::RangeExpr> parse_led_range_exclusive_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::RangeExpr> parse_led_range_exclusive_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::RangeExpr>
+ std::unique_ptr<AST::RangeExpr>
parse_nud_range_exclusive_expr (const_TokenPtr tok,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::RangeFromToInclExpr> parse_range_inclusive_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> left,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::RangeFromToInclExpr> parse_range_inclusive_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::RangeToInclExpr>
+ std::unique_ptr<AST::RangeToInclExpr>
parse_range_to_inclusive_expr (const_TokenPtr tok,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> tuple_expr,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::FieldAccessExpr> parse_field_access_expr (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> struct_expr,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::FieldAccessExpr> parse_field_access_expr (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> struct_expr,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::ArrayIndexExpr>
- parse_index_expr (const_TokenPtr tok, ::std::unique_ptr<AST::Expr> array_expr,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::unique_ptr<AST::ArrayIndexExpr>
+ parse_index_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> array_expr,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
- ::std::unique_ptr<AST::MacroInvocation>
+ std::unique_ptr<AST::MacroInvocation>
parse_macro_invocation_partial (AST::PathInExpression path,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::StructExprStruct>
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::StructExprStruct>
parse_struct_expr_struct_partial (AST::PathInExpression path,
- ::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::CallExpr>
+ std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::CallExpr>
parse_struct_expr_tuple_partial (AST::PathInExpression path,
- ::std::vector<AST::Attribute> outer_attrs);
+ std::vector<AST::Attribute> outer_attrs);
AST::PathInExpression parse_path_in_expression_pratt (const_TokenPtr tok);
- ::std::unique_ptr<AST::ClosureExpr>
+ std::unique_ptr<AST::ClosureExpr>
parse_closure_expr_pratt (const_TokenPtr tok,
- ::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr_float (
- const_TokenPtr tok, ::std::unique_ptr<AST::Expr> tuple_expr,
- ::std::vector<AST::Attribute> outer_attrs,
+ std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr_float (
+ const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr,
+ std::vector<AST::Attribute> outer_attrs,
ParseRestrictions restrictions = ParseRestrictions ());
// Expression-related (non-Pratt parsed)
- ::std::unique_ptr<AST::ExprWithoutBlock>
- parse_expr_without_block (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::BlockExpr>
- parse_block_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::ExprWithoutBlock>
+ parse_expr_without_block (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::BlockExpr>
+ parse_block_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
- ::std::unique_ptr<AST::IfExpr>
- parse_if_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::IfLetExpr>
- parse_if_let_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::LoopExpr>
- parse_loop_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::IfExpr>
+ parse_if_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::IfLetExpr>
+ parse_if_let_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::LoopExpr>
+ parse_loop_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
AST::LoopLabel label = AST::LoopLabel::error ());
- ::std::unique_ptr<AST::WhileLoopExpr>
- parse_while_loop_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::WhileLoopExpr>
+ parse_while_loop_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
AST::LoopLabel label = AST::LoopLabel::error ());
- ::std::unique_ptr<AST::WhileLetLoopExpr>
- parse_while_let_loop_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::WhileLetLoopExpr>
+ parse_while_let_loop_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
AST::LoopLabel label = AST::LoopLabel::error ());
- ::std::unique_ptr<AST::ForLoopExpr>
- parse_for_loop_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::ForLoopExpr>
+ parse_for_loop_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
AST::LoopLabel label = AST::LoopLabel::error ());
- ::std::unique_ptr<AST::MatchExpr>
- parse_match_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::MatchExpr>
+ parse_match_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
AST::MatchArm parse_match_arm ();
- ::std::vector< ::std::unique_ptr<AST::Pattern> >
+ std::vector< std::unique_ptr<AST::Pattern> >
parse_match_arm_patterns (TokenId end_token_id);
- ::std::unique_ptr<AST::BaseLoopExpr>
- parse_labelled_loop_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::BaseLoopExpr>
+ parse_labelled_loop_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
AST::LoopLabel parse_loop_label ();
- ::std::unique_ptr<AST::AsyncBlockExpr>
- parse_async_block_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::UnsafeBlockExpr>
- parse_unsafe_block_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::GroupedExpr>
- parse_grouped_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::ClosureExpr>
- parse_closure_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::AsyncBlockExpr>
+ parse_async_block_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::UnsafeBlockExpr>
+ parse_unsafe_block_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::GroupedExpr>
+ parse_grouped_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::ClosureExpr>
+ parse_closure_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
AST::ClosureParam parse_closure_param ();
- ::std::unique_ptr<AST::LiteralExpr>
- parse_literal_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> ());
- ::std::unique_ptr<AST::ReturnExpr>
- parse_return_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::LiteralExpr>
+ parse_literal_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> ());
+ std::unique_ptr<AST::ReturnExpr>
+ parse_return_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
- ::std::unique_ptr<AST::BreakExpr>
- parse_break_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::BreakExpr>
+ parse_break_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
- ::std::unique_ptr<AST::ContinueExpr>
- parse_continue_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::ContinueExpr>
+ parse_continue_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
- ::std::unique_ptr<AST::ArrayExpr>
- parse_array_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::ArrayExpr>
+ parse_array_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
- ::std::unique_ptr<AST::ExprWithoutBlock>
- parse_grouped_or_tuple_expr (::std::vector<AST::Attribute> outer_attrs
- = ::std::vector<AST::Attribute> (),
+ std::unique_ptr<AST::ExprWithoutBlock>
+ parse_grouped_or_tuple_expr (std::vector<AST::Attribute> outer_attrs
+ = std::vector<AST::Attribute> (),
bool pratt_parse = false);
- ::std::unique_ptr<AST::StructExprField> parse_struct_expr_field ();
+ std::unique_ptr<AST::StructExprField> parse_struct_expr_field ();
// Type-related
- ::std::unique_ptr<AST::Type> parse_type ();
- ::std::unique_ptr<AST::TypeNoBounds> parse_type_no_bounds ();
- ::std::unique_ptr<AST::TypeNoBounds> parse_slice_or_array_type ();
- ::std::unique_ptr<AST::RawPointerType> parse_raw_pointer_type ();
- ::std::unique_ptr<AST::ReferenceType> parse_reference_type ();
- ::std::unique_ptr<AST::BareFunctionType>
- parse_bare_function_type (::std::vector<AST::LifetimeParam> for_lifetimes);
- ::std::unique_ptr<AST::Type> parse_paren_prefixed_type ();
- ::std::unique_ptr<AST::TypeNoBounds> parse_paren_prefixed_type_no_bounds ();
- ::std::unique_ptr<AST::Type> parse_for_prefixed_type ();
+ std::unique_ptr<AST::Type> parse_type ();
+ std::unique_ptr<AST::TypeNoBounds> parse_type_no_bounds ();
+ std::unique_ptr<AST::TypeNoBounds> parse_slice_or_array_type ();
+ std::unique_ptr<AST::RawPointerType> parse_raw_pointer_type ();
+ std::unique_ptr<AST::ReferenceType> parse_reference_type ();
+ std::unique_ptr<AST::BareFunctionType>
+ parse_bare_function_type (std::vector<AST::LifetimeParam> for_lifetimes);
+ std::unique_ptr<AST::Type> parse_paren_prefixed_type ();
+ std::unique_ptr<AST::TypeNoBounds> parse_paren_prefixed_type_no_bounds ();
+ std::unique_ptr<AST::Type> parse_for_prefixed_type ();
AST::MaybeNamedParam parse_maybe_named_param ();
// Statement-related
- ::std::unique_ptr<AST::Stmt> parse_stmt ();
- ::std::unique_ptr<AST::LetStmt>
- parse_let_stmt (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ExprStmt>
- parse_expr_stmt (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ExprStmtWithBlock>
- parse_expr_stmt_with_block (::std::vector<AST::Attribute> outer_attrs);
- ::std::unique_ptr<AST::ExprStmtWithoutBlock>
- parse_expr_stmt_without_block (::std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::Stmt> parse_stmt ();
+ std::unique_ptr<AST::LetStmt>
+ parse_let_stmt (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ExprStmt>
+ parse_expr_stmt (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ExprStmtWithBlock>
+ parse_expr_stmt_with_block (std::vector<AST::Attribute> outer_attrs);
+ std::unique_ptr<AST::ExprStmtWithoutBlock>
+ parse_expr_stmt_without_block (std::vector<AST::Attribute> outer_attrs);
ExprOrStmt parse_stmt_or_expr_without_block ();
ExprOrStmt
- parse_macro_invocation_maybe_semi (::std::vector<AST::Attribute> outer_attrs);
+ parse_macro_invocation_maybe_semi (std::vector<AST::Attribute> outer_attrs);
ExprOrStmt
- parse_path_based_stmt_or_expr (::std::vector<AST::Attribute> outer_attrs);
+ parse_path_based_stmt_or_expr (std::vector<AST::Attribute> outer_attrs);
// Pattern-related
- ::std::unique_ptr<AST::Pattern> parse_pattern ();
- ::std::unique_ptr<AST::Pattern> parse_literal_or_range_pattern ();
- ::std::unique_ptr<AST::RangePatternBound> parse_range_pattern_bound ();
- ::std::unique_ptr<AST::ReferencePattern> parse_reference_pattern ();
- ::std::unique_ptr<AST::Pattern> parse_grouped_or_tuple_pattern ();
- ::std::unique_ptr<AST::SlicePattern> parse_slice_pattern ();
- ::std::unique_ptr<AST::IdentifierPattern> parse_identifier_pattern ();
- ::std::unique_ptr<AST::Pattern> parse_ident_leading_pattern ();
- ::std::unique_ptr<AST::TupleStructItems> parse_tuple_struct_items ();
+ std::unique_ptr<AST::Pattern> parse_pattern ();
+ std::unique_ptr<AST::Pattern> parse_literal_or_range_pattern ();
+ std::unique_ptr<AST::RangePatternBound> parse_range_pattern_bound ();
+ std::unique_ptr<AST::ReferencePattern> parse_reference_pattern ();
+ std::unique_ptr<AST::Pattern> parse_grouped_or_tuple_pattern ();
+ std::unique_ptr<AST::SlicePattern> parse_slice_pattern ();
+ std::unique_ptr<AST::IdentifierPattern> parse_identifier_pattern ();
+ std::unique_ptr<AST::Pattern> parse_ident_leading_pattern ();
+ std::unique_ptr<AST::TupleStructItems> parse_tuple_struct_items ();
AST::StructPatternElements parse_struct_pattern_elems ();
- ::std::unique_ptr<AST::StructPatternField> parse_struct_pattern_field ();
+ std::unique_ptr<AST::StructPatternField> parse_struct_pattern_field ();
int left_binding_power (const_TokenPtr token);
@@ -548,8 +550,8 @@ private:
bool done_end_of_file ();
public:
- // Construct parser with specified lexer reference.
- Parser (Lexer &parLexer) : lexer (parLexer) {}
+ // Construct parser with specified "managed" token source.
+ Parser (ManagedTokenSource tokenSource) : lexer (std::move(tokenSource)) {}
// Main entry point for parser.
AST::Crate parse_crate ();
@@ -559,9 +561,12 @@ public:
void debug_dump_ast_output (AST::Crate &crate);
private:
- // The lexer associated with the parser.
- Lexer &lexer;
+ // The token source (usually lexer) associated with the parser.
+ ManagedTokenSource lexer;
};
} // namespace Rust
+// as now template, include implementations of all methods
+#include "rust-parse-impl.h"
+
#endif // RUST_PARSE_H
diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc
index 91c77bc..2752497 100644
--- a/gcc/rust/rust-lang.cc
+++ b/gcc/rust/rust-lang.cc
@@ -34,17 +34,11 @@
// TODO: is this best way to do it? Is it allowed? (should be)
/* General TODOs:
- * - maybe convert all raw pointer-returning/passing functions that
- * conceptually return a unique pointer actually return a unique pointer. i.e.
- * parse methods and constructors for AST objects. make_unique should probably
- * be avoided to keep C++11 compatibility.
* - convert all copies of expensive-to-copy (deep copy) AST objects into
- * moves, if possible. Don't
- * remove clone functionality - it may be required for e.g. HIR conversion.
+ * moves, if possible. Don't remove clone functionality - it may be required for e.g. HIR conversion.
*/
#include "rust-system.h"
-#include "rust-parse.h"
#include "rust-session-manager.h"
#include "rust-target.h"
diff --git a/gcc/rust/rust-linemap.h b/gcc/rust/rust-linemap.h
index 73e6a0b..f6d8ac91 100644
--- a/gcc/rust/rust-linemap.h
+++ b/gcc/rust/rust-linemap.h
@@ -1,6 +1,6 @@
-// go-linemap.h -- interface to location tracking -*- C++ -*-
+// rust-linemap.h -- interface to location tracking -*- C++ -*-
-// Copyright 2011 The Rust Authors. All rights reserved.
+// Copyright 2020 The Rust Authors. All rights reserved.
// Use of this source code is rustverned by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
index 6ba496d7..d75204a 100644
--- a/gcc/rust/rust-session-manager.cc
+++ b/gcc/rust/rust-session-manager.cc
@@ -11,7 +11,8 @@
#include "rust-lex.h"
#include "rust-parse.h"
#include "rust-scan.h"
-#include "rust-resolution.h"
+#include "rust-name-resolution.h"
+#include "rust-type-resolution.h"
#include "rust-compile.h"
#include "rust-target.h"
@@ -33,7 +34,7 @@ Session::implicitly_enable_feature (std::string feature_name)
// TODO: is this really required since features added would be complete via
// target spec?
- if (!options.target_data.has_key_value_pair ("target_data", feature_name))
+ if (!options.target_data.has_key_value_pair ("target_feature", feature_name))
{
// if feature has dependencies, enable them
if (feature_name == "aes")
@@ -234,47 +235,45 @@ void
Session::init ()
{
#ifndef TARGET_RUST_OS_INFO
-#define TARGET_RUST_OS_INFO()
+# define TARGET_RUST_OS_INFO()
#endif
//#define builtin_rust_info(KEY, VALUE) rust_add_target_info (KEY, VALUE)
// might as well use c++ stuff
-#define builtin_rust_info(KEY, VALUE) \
- options.target_data.insert_key_value_pair (KEY, VALUE)
-
- // initialise target hooks
- // targetrustm.rust_cpu_info();
- // targetrustm.rust_os_info();
- // ok, that's not working too well TODO - see if can salvage old
- // implementation
- TARGET_RUST_CPU_INFO ();
- TARGET_RUST_OS_INFO ();
-
+#define builtin_rust_info(KEY, VALUE) options.target_data.insert_key_value_pair(KEY, VALUE)
+
+ // initialise target hooks
+ //targetrustm.rust_cpu_info();
+ //targetrustm.rust_os_info();
+ // ok, that's not working too well TODO - see if can salvage old implementation
+ TARGET_RUST_CPU_INFO ();
+ TARGET_RUST_OS_INFO ();
+
+ /* note that due to issues with gcc targets, some implementations of those two macros above
+ * (TARGET_RUST_CPU_INFO and TARGET_RUST_OS_INFO) are not function calls, but actually inline
+ * substitutions. As such, they can't be stored with a function pointer in a "real" target hook.
+ * At least, that's my current understanding of it. */
+
#undef builtin_rust_info
- // target-independent values that should exist in all targets
- options.target_data.insert_key_value_pair ("target_pointer_width",
- std::to_string (POINTER_SIZE));
- options.target_data.insert_key_value_pair ("target_endian", BYTES_BIG_ENDIAN
- ? "big"
- : "little");
+ // target-independent values that should exist in all targets
+ options.target_data.insert_key_value_pair ("target_pointer_width", std::to_string (POINTER_SIZE));
+ options.target_data.insert_key_value_pair ("target_endian", BYTES_BIG_ENDIAN ? "big" : "little");
- // TODO: find min atomic width and max atomic width
- // from it, add atomic-related stuff for sizes 8, 16, 32, 64, and 128 (if
- // inside bounds) in rustc, min atomic width is a known quantity (or 8 if not
- // known), and max is also a known quantity (or is pointer size if not known)
- // TODO: add atomic pointer if some criteria is satisfied
+ // TODO: find min atomic width and max atomic width
+ // from it, add atomic-related stuff for sizes 8, 16, 32, 64, and 128 (if inside bounds)
+ // in rustc, min atomic width is a known quantity (or 8 if not known), and max is also a known quantity (or is pointer size if not known)
+ // TODO: add atomic pointer if some criteria is satisfied
- // TODO: find whether target has "atomic cas"
+ // TODO: find whether target has "atomic cas"
- // add debug_assertions if enabled and proc_macro if crate type has it or
- // whatever
+ // add debug_assertions if enabled and proc_macro if crate type has it or whatever
- // derived values from hook
- options.target_data.init_derived_values ();
+ // derived values from hook
+ options.target_data.init_derived_values ();
}
-// Initialise default options. Actually called before handle_option, unlike init
-// itself.
+/* Initialise default options. Actually called before handle_option, unlike init
+ * itself. */
void
Session::init_options ()
{
@@ -302,7 +301,7 @@ Session::handle_option (
break;
case OPT_frust_dump_:
// enable dump and return whether this was successful
- if (arg != NULL)
+ if (arg != nullptr)
{
ret = enable_dump (std::string (arg));
}
@@ -325,12 +324,12 @@ Session::handle_option (
bool
Session::enable_dump (std::string arg)
{
- // FIXME: change dumping algorithm when new non-inhibiting dump system is
- // created
+ /* FIXME: change dumping algorithm when new non-inhibiting dump system is
+ * created */
if (arg == "all")
{
- error_at (
- UNKNOWN_LOCATION,
+ rust_error_at (
+ Location (),
"dumping all is not supported as of now. choose 'lex', 'parse', or 'target_options");
return false;
}
@@ -357,25 +356,23 @@ Session::enable_dump (std::string arg)
else if (arg == "resolution")
{
options.dump_option = CompileOptions::RESOLUTION_DUMP;
- }
- else if (arg == "target_options")
- {
+ }
+ else if (arg == "target_options") {
// special case - dump all target options, and then quit compilation
- // nope, option handling called before init, so have to make this an
- // actual compile option
- // options.target_data.dump_target_options();
- // return false;
+ // nope, option handling called before init, so have to make this an actual compile option
+ //options.target_data.dump_target_options();
+ //return false;
options.dump_option = CompileOptions::TARGET_OPTION_DUMP;
}
else if (arg == "")
{
- error_at (UNKNOWN_LOCATION,
+ rust_error_at (Location (),
"dump option was not given a name. choose 'lex', 'parse', or 'target_options'");
return false;
}
else
{
- error_at (UNKNOWN_LOCATION,
+ rust_error_at (Location (),
"dump option '%s' was unrecognised. choose 'lex', 'parse', or 'target_options",
arg.c_str ());
return false;
@@ -392,8 +389,8 @@ Session::parse_files (int num_files, const char **files)
{
parse_file (files[i]);
}
- // TODO: should semantic analysis be dealed with here? or per file? for now,
- // per-file.
+ /* TODO: should semantic analysis be dealed with here? or per file? for now,
+ * per-file. */
}
// Parses a single file with filename filename.
@@ -402,9 +399,9 @@ Session::parse_file (const char *filename)
{
RAIIFile file_wrap (filename);
- if (file_wrap.get_raw() == NULL)
+ if (file_wrap.get_raw() == nullptr)
{
- fatal_error (UNKNOWN_LOCATION, "cannot open filename %s: %m", filename);
+ rust_fatal_error (Location (), "cannot open filename %s: %m", filename);
}
Backend *backend = rust_get_backend ();
@@ -412,8 +409,8 @@ Session::parse_file (const char *filename)
// parse file here
/* create lexer and parser - these are file-specific and so aren't instance
* variables */
- Rust::Lexer lex (filename, std::move (file_wrap), rust_get_linemap ());
- Rust::Parser parser (/*std::move (*/lex/*)*/);
+ Lexer lex (filename, std::move (file_wrap), rust_get_linemap ());
+ Parser<Lexer> parser (std::move (lex));
// generate crate from parser
auto parsed_crate = parser.parse_crate ();
@@ -423,10 +420,14 @@ Session::parse_file (const char *filename)
{
case CompileOptions::LEXER_DUMP:
parser.debug_dump_lex_output ();
+ // TODO: rewrite lexer dump or something so that it allows for the crate to already be parsed
break;
case CompileOptions::PARSER_AST_DUMP:
parser.debug_dump_ast_output (parsed_crate);
break;
+ case CompileOptions::TARGET_OPTION_DUMP:
+ options.target_data.dump_target_options ();
+ return;
default:
break;
}
@@ -533,7 +534,7 @@ load_extern_crate (std::string crate_name ATTRIBUTE_UNUSED)
// TODO: lots of this code is probably actually useful outside of dumping, so
// maybe split off function
void
-Session::debug_dump_load_crates (Parser &parser)
+Session::debug_dump_load_crates (Parser<Lexer> &parser)
{
// parse crate as AST
AST::Crate crate = parser.parse_crate ();
@@ -582,9 +583,7 @@ contains_name (const std::vector<AST::Attribute> &attrs, std::string name)
for (const auto &attr : attrs)
{
if (attr.get_path () == name)
- {
- return true;
- }
+ return true;
}
return false;
@@ -677,7 +676,7 @@ Session::injection (AST::Crate &crate)
{
// create "macro use" attribute for use on extern crate item to enable
// loading macros from it
- AST::Attribute attr (AST::SimplePath::from_str ("macro_use"), NULL);
+ AST::Attribute attr (AST::SimplePath::from_str ("macro_use"), nullptr);
// create "extern crate" item with the name
std::unique_ptr<AST::ExternCrate> extern_crate (
@@ -700,7 +699,7 @@ Session::injection (AST::Crate &crate)
AST::SimplePath (std::move (segments)),
Location ()));
AST::Attribute prelude_attr (AST::SimplePath::from_str ("prelude_import"),
- NULL);
+ nullptr);
std::unique_ptr<AST::UseDeclaration> use_decl (
new AST::UseDeclaration (std::move (use_tree),
AST::Visibility::create_error (),
@@ -750,96 +749,93 @@ Session::resolution (AST::Crate &crate)
fprintf (stderr, "started name resolution\n");
Analysis::TopLevelScan toplevel (crate);
// Name resolution must be in front of type resolution
- Analysis::TypeResolution::ResolveNamesAndTypes (crate, toplevel);
+ Analysis::NameResolution::Resolve (crate, toplevel);
+ Analysis::TypeResolution::Resolve (crate, toplevel);
fprintf (stderr, "finished name resolution\n");
}
-void
-TargetOptions::dump_target_options () const
-{
- fprintf (stderr,
- "\033[0;31m--PREPARING TO DUMP ALL TARGET OPTIONS--\n\033[0m");
- for (const auto &pairs : features)
- {
- for (const auto &value : pairs.second)
- {
- fprintf (stderr, "%s: \"%s\"\n", pairs.first.c_str (),
- value.c_str ());
- }
- if (pairs.second.empty ())
- {
- fprintf (stderr, "%s\n", pairs.first.c_str ());
- }
- }
- if (features.empty ())
- {
- fprintf (stderr, "No target options available!\n");
- }
+void
+TargetOptions::dump_target_options () const
+ {
+ fprintf (stderr, "\033[0;31m--PREPARING TO DUMP ALL TARGET OPTIONS--\n\033[0m");
+ for (const auto& pairs : features)
+ {
+ for (const auto& value : pairs.second)
+ fprintf (stderr, "%s: \"%s\"\n", pairs.first.c_str (), value.c_str ());
+
+ if (pairs.second.empty ())
+ fprintf (stderr, "%s\n", pairs.first.c_str ());
+ }
+ if (features.empty ())
+ fprintf (stderr, "No target options available!\n");
- fprintf (stderr, "\033[0;31m--END OF TARGET OPTION DUMP--\n\033[0m");
-}
+ fprintf (stderr, "\033[0;31m--END OF TARGET OPTION DUMP--\n\033[0m");
+ }
-void
-TargetOptions::init_derived_values ()
-{
- // enable derived values based on target families
- if (has_key_value_pair ("target_family", "unix"))
- insert_key ("unix");
- if (has_key_value_pair ("target_family", "windows"))
- insert_key ("windows");
-
- // implicitly enable features
- if (has_key_value_pair ("target_feature", "aes"))
- enable_implicit_feature_reqs ("aes");
- if (has_key_value_pair ("target_feature", "avx"))
- enable_implicit_feature_reqs ("sse4.2");
- if (has_key_value_pair ("target_feature", "avx2"))
- enable_implicit_feature_reqs ("avx");
- if (has_key_value_pair ("target_feature", "pclmulqdq"))
- enable_implicit_feature_reqs ("sse2");
- if (has_key_value_pair ("target_feature", "sha"))
- enable_implicit_feature_reqs ("sse2");
- if (has_key_value_pair ("target_feature", "sse2"))
- enable_implicit_feature_reqs ("sse");
- if (has_key_value_pair ("target_feature", "sse3"))
- enable_implicit_feature_reqs ("sse2");
- if (has_key_value_pair ("target_feature", "sse4.1"))
- enable_implicit_feature_reqs ("sse3");
- if (has_key_value_pair ("target_feature", "sse4.2"))
- enable_implicit_feature_reqs ("sse4.1");
- if (has_key_value_pair ("target_feature", "ssse3"))
- enable_implicit_feature_reqs ("sse3");
-}
+void
+TargetOptions::init_derived_values ()
+ {
+ // enable derived values based on target families
+ if (has_key_value_pair ("target_family", "unix"))
+ insert_key ("unix");
+ if (has_key_value_pair ("target_family", "windows"))
+ insert_key ("windows");
+
+ // implicitly enable features - this should not be required in general
+ if (has_key_value_pair ("target_feature", "aes"))
+ enable_implicit_feature_reqs ("aes");
+ if (has_key_value_pair ("target_feature", "avx"))
+ enable_implicit_feature_reqs ("sse4.2");
+ if (has_key_value_pair ("target_feature", "avx2"))
+ enable_implicit_feature_reqs ("avx");
+ if (has_key_value_pair ("target_feature", "pclmulqdq"))
+ enable_implicit_feature_reqs ("sse2");
+ if (has_key_value_pair ("target_feature", "sha"))
+ enable_implicit_feature_reqs ("sse2");
+ if (has_key_value_pair ("target_feature", "sse2"))
+ enable_implicit_feature_reqs ("sse");
+ if (has_key_value_pair ("target_feature", "sse3"))
+ enable_implicit_feature_reqs ("sse2");
+ if (has_key_value_pair ("target_feature", "sse4.1"))
+ enable_implicit_feature_reqs ("sse3");
+ if (has_key_value_pair ("target_feature", "sse4.2"))
+ enable_implicit_feature_reqs ("sse4.1");
+ if (has_key_value_pair ("target_feature", "ssse3"))
+ enable_implicit_feature_reqs ("sse3");
+ }
-void
-TargetOptions::enable_implicit_feature_reqs (std::string feature)
-{
- if (feature == "aes")
- enable_implicit_feature_reqs ("sse2");
- else if (feature == "avx")
- enable_implicit_feature_reqs ("sse4.2");
- else if (feature == "avx2")
- enable_implicit_feature_reqs ("avx");
- else if (feature == "fma")
- enable_implicit_feature_reqs ("avx");
- else if (feature == "pclmulqdq")
- enable_implicit_feature_reqs ("sse2");
- else if (feature == "sha")
- enable_implicit_feature_reqs ("sse2");
- else if (feature == "sse2")
- enable_implicit_feature_reqs ("sse");
- else if (feature == "sse3")
- enable_implicit_feature_reqs ("sse2");
- else if (feature == "sse4.1")
- enable_implicit_feature_reqs ("sse3");
- else if (feature == "sse4.2")
- enable_implicit_feature_reqs ("sse4.1");
- else if (feature == "ssse3")
- enable_implicit_feature_reqs ("sse3");
-
- if (!has_key_value_pair ("target_feature", feature))
- insert_key_value_pair ("target_feature", feature);
-}
+void
+TargetOptions::enable_implicit_feature_reqs (std::string feature)
+ {
+ if (feature == "aes")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "avx")
+ enable_implicit_feature_reqs ("sse4.2");
+ else if (feature == "avx2")
+ enable_implicit_feature_reqs ("avx");
+ else if (feature == "fma")
+ enable_implicit_feature_reqs ("avx");
+ else if (feature == "pclmulqdq")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "sha")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "sse2")
+ enable_implicit_feature_reqs ("sse");
+ else if (feature == "sse3")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "sse4.1")
+ enable_implicit_feature_reqs ("sse3");
+ else if (feature == "sse4.2")
+ enable_implicit_feature_reqs ("sse4.1");
+ else if (feature == "ssse3")
+ enable_implicit_feature_reqs ("sse3");
+
+ if (!has_key_value_pair ("target_feature", feature)) {
+ insert_key_value_pair ("target_feature", feature);
+
+ fprintf (stderr, "had to implicitly enable feature '%s'!", feature.c_str ());
+ }
+ }
// NOTEs:
/* mrustc compile pipeline:
diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h
index 11bece8..bdeeeec 100644
--- a/gcc/rust/rust-session-manager.h
+++ b/gcc/rust/rust-session-manager.h
@@ -19,19 +19,20 @@
namespace Rust {
// parser forward decl
-class Parser;
+template <typename ManagedTokenSource> class Parser;
+class Lexer;
// crate forward decl
namespace AST {
struct Crate;
}
-// Data related to target, most useful for conditional compilation and
-// whatever.
+/* Data related to target, most useful for conditional compilation and
+ * whatever. */
struct TargetOptions
{
- // TODO: maybe make private and access through helpers to allow changes to
- // impl
- std::unordered_map<std::string, std::unordered_set<std::string> > features;
+ /* TODO: maybe make private and access through helpers to allow changes to
+ * impl */
+ std::unordered_map<std::string, std::unordered_set<std::string>> features;
public:
// Returns whether a key is defined in the feature set.
@@ -54,8 +55,8 @@ public:
return false;
}
- // Returns the singular value from the key, or if the key has multiple, an
- // empty string.
+ /* Returns the singular value from the key, or if the key has multiple, an
+ * empty string. */
std::string get_singular_value (std::string key) const
{
auto it = features.find (key);
@@ -68,22 +69,19 @@ public:
return "";
}
- // Returns all values associated with a key (including none), or an empty set
- // if no key is found.
- std::unordered_set< ::std::string> get_values_for_key (std::string key) const
+ /* Returns all values associated with a key (including none), or an empty
+ * set if no key is found. */
+ std::unordered_set<std::string> get_values_for_key (std::string key) const
{
auto it = features.find (key);
if (it != features.end ())
- {
- return it->second;
- }
+ return it->second;
return {};
}
- /* Inserts a key (no value) into the feature set. This will do nothing if the
- * key already exists.
- * This returns whether the insertion was successful (i.e. whether key already
- * existed). */
+ /* Inserts a key (no value) into the feature set. This will do nothing if
+ * the key already exists. This returns whether the insertion was successful
+ * (i.e. whether key already existed). */
bool insert_key (std::string key)
{
return features
@@ -102,26 +100,25 @@ public:
// Dump all target options to stderr.
void dump_target_options () const;
- // Creates derived values and implicit enables after all target info is added
- // (e.g. "unix").
+ /* Creates derived values and implicit enables after all target info is added
+ * (e.g. "unix"). */
void init_derived_values ();
- // Enables all requirements for the feature given, and will enable feature
- // itself if not enabled.
+ /* Enables all requirements for the feature given, and will enable feature
+ * itself if not enabled. */
void enable_implicit_feature_reqs (std::string feature);
/* According to reference, Rust uses either multi-map key-values or just
* values (although values may be aliases for a key-value value). This seems
* like overkill. Thus, depending on whether the attributes used in cfg are
* fixed or not, I think I'll either put each non-multimap "key-value" as a
- * separate field and have the multimap "key-values" in a regular map for that
- * one key, or actually use a multimap.
+ * separate field and have the multimap "key-values" in a regular map for
+ * that one key, or actually use a multimap.
*
- * rustc itself uses a set of key-value tuples where the second tuple element
- * is optional. This gets rid of the requirement to make a multi-map, I guess,
- * but seems like it might make
- * search slow (unless all "is defined"-only ones have empty string as second
- * element). */
+ * rustc itself uses a set of key-value tuples where the second tuple
+ * element is optional. This gets rid of the requirement to make a
+ * multi-map, I guess, but seems like it might make search slow (unless all
+ * "is defined"-only ones have empty string as second element). */
/* cfg attributes:
* - target_arch: single value
* - target_feature: multiple values possible
@@ -129,10 +126,10 @@ public:
* - target_family: single value (or no value?)
* - unix: set when target_family = "unix"
* - windows: set when target_family = "windows"
- * - if these are just syntactic sugar, then maybe have a separate set or map
- * for this kind of stuff
- * - target_env: set when needed for disambiguation about ABI - usually empty
- * string for GNU, complicated
+ * - if these are just syntactic sugar, then maybe have a separate set or
+ * map for this kind of stuff
+ * - target_env: set when needed for disambiguation about ABI - usually
+ * empty string for GNU, complicated
* - seems to be a single value (if any)
* - target_endian: single value; "little" or "big"
* - target_pointer_width: single value, "32" for 32-bit pointers, etc.
@@ -141,7 +138,8 @@ public:
* - again, seems similar to a "is defined" rather than "is equal to" like
* unix
* - debug_assertions: seems to "is defined"
- * - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is defined"
+ * - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is
+ * defined"
*/
};
@@ -150,8 +148,8 @@ struct CompileOptions
{
// TODO: use bitfield for smaller memory requirements?
- // FIXME: this is set up for "instead of" dumping - in future, dumps should
- // not inhibit compilation
+ /* FIXME: this is set up for "instead of" dumping - in future, dumps should
+ * not inhibit compilation */
enum DumpOptions
{
NO_DUMP,
@@ -160,15 +158,14 @@ struct CompileOptions
REGISTER_PLUGINS_DUMP,
INJECTION_DUMP,
EXPANSION_DUMP,
- NAME_RESOLUTION_DUMP,
- TARGET_OPTION_DUMP,
RESOLUTION_DUMP,
+ TARGET_OPTION_DUMP,
// TODO: add more?
} dump_option;
- // configuration options - actually useful for conditional compilation and
- // whatever data related to target arch, features, os, family, env, endian,
- // pointer width, vendor
+ /* configuration options - actually useful for conditional compilation and
+ * whatever data related to target arch, features, os, family, env, endian,
+ * pointer width, vendor */
TargetOptions target_data;
bool enable_test = false;
bool debug_assertions = false;
@@ -180,9 +177,9 @@ struct CompileOptions
struct Session
{
CompileOptions options;
- // This should really be in a per-crate storage area but it is wiped with
- // every file so eh.
- ::std::string injected_crate_name;
+ /* This should really be in a per-crate storage area but it is wiped with
+ * every file so eh. */
+ std::string injected_crate_name;
// backend wrapper to GCC GENERIC
Backend *backend;
@@ -205,18 +202,18 @@ public:
private:
// TODO: should this be private or public?
void parse_file (const char *filename);
- bool enable_dump (::std::string arg);
+ bool enable_dump (std::string arg);
- void debug_dump_load_crates (Parser &parser);
+ void debug_dump_load_crates (Parser<Lexer> &parser);
- void implicitly_enable_feature (::std::string feature_name);
+ void implicitly_enable_feature (std::string feature_name);
void enable_features ();
// pipeline stages - TODO maybe move?
/* Register plugins pipeline stage. TODO maybe move to another object?
- * Currently dummy stage. In future will handle attribute injection (top-level
- * inner attribute creation from command line arguments), setting options
- * maybe, registering lints maybe, loading plugins maybe. */
+ * Currently dummy stage. In future will handle attribute injection
+ * (top-level inner attribute creation from command line arguments), setting
+ * options maybe, registering lints maybe, loading plugins maybe. */
void register_plugins (AST::Crate &crate);
/* Injection pipeline stage. TODO maybe move to another object? Maybe have
* some lint checks (in future, obviously), register builtin macros, crate
@@ -226,11 +223,9 @@ private:
* macros, maybe build test harness in future, AST validation, maybe create
* macro crate (if not rustdoc).*/
void expansion (AST::Crate &crate);
-
/* Resolution pipeline stage. TODO maybe move to another object.
* Performs name resolution and type resolution, maybe complete gated
- * feature checking, maybe create buffered lints in future.
- */
+ * feature checking, maybe create buffered lints in future. */
void resolution (AST::Crate &crate);
};
} // namespace Rust