aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorNala Ginrut <mulei@gnu.org>2020-04-24 01:23:13 +0800
committerPhilip Herron <philip.herron@embecosm.com>2020-11-27 17:07:11 +0000
commit3835d919db31892b755396b5ed8cf1257204389d (patch)
tree66da7098aac9d5508324ee8116af8b50d4b8c29b /gcc
parent9eb643921056cb021ae98ea9cef44cff51abe6da (diff)
downloadgcc-3835d919db31892b755396b5ed8cf1257204389d.zip
gcc-3835d919db31892b755396b5ed8cf1257204389d.tar.gz
gcc-3835d919db31892b755396b5ed8cf1257204389d.tar.bz2
Enhance AST dump
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/ast/rust-ast-full-test.cc10327
-rw-r--r--gcc/rust/ast/rust-item.h7064
-rw-r--r--gcc/rust/lex/rust-lex.cc4525
-rw-r--r--gcc/rust/parse/rust-parse.cc24717
4 files changed, 25723 insertions, 20910 deletions
diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc
index 77f1153..d1fbb95 100644
--- a/gcc/rust/ast/rust-ast-full-test.cc
+++ b/gcc/rust/ast/rust-ast-full-test.cc
@@ -1,4596 +1,6309 @@
+/*
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
#include "rust-ast-full.h"
#include "rust-diagnostics.h"
#include "rust-ast-visitor.h"
#include "rust-session-manager.h"
-/* Compilation unit used for various AST-related functions that would make the headers too long if
- * they were defined inline and don't receive any benefits from being defined inline because they are
- * virtual. Also used for various other stuff. */
+/* Compilation unit used for various AST-related functions that would make
+ * the headers too long if they were defined inline and don't receive any
+ * benefits from being defined inline because they are virtual. Also used
+ * for various other stuff. */
namespace Rust {
- namespace AST {
- // Gets a string in a certain delim type.
- ::std::string get_string_in_delims(::std::string str_input, DelimType delim_type) {
- switch (delim_type) {
- case PARENS:
- return "(" + str_input + ")";
- case SQUARE:
- return "[" + str_input + "]";
- case CURLY:
- return "{" + str_input + "}";
- default:
- return "ERROR-MARK-STRING (delims)";
- }
- gcc_unreachable();
- }
-
- // Converts a frag spec enum item to a string form.
- ::std::string frag_spec_to_str(MacroFragSpec frag_spec) {
- switch (frag_spec) {
- case BLOCK:
- return "block";
- case EXPR:
- return "expr";
- case IDENT:
- return "ident";
- case ITEM:
- return "item";
- case LIFETIME:
- return "lifetime";
- case LITERAL:
- return "literal";
- case META:
- return "meta";
- case PAT:
- return "pat";
- case PATH:
- return "path";
- case STMT:
- return "stmt";
- case TT:
- return "tt";
- case TY:
- return "ty";
- case VIS:
- return "vis";
- case INVALID:
- return "INVALID_FRAG_SPEC";
- default:
- return "ERROR_MARK_STRING - unknown frag spec";
- }
- }
-
- ::std::string Crate::as_string() const {
- fprintf(stderr, "beginning crate recursive as-string\n");
-
- ::std::string str("Crate: ");
- // add utf8bom and shebang
- if (has_utf8bom) {
- str += "\n has utf8bom";
- }
- if (has_shebang) {
- str += "\n has shebang";
- }
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- // items
- str += "\n items: ";
- if (items.empty()) {
- str += "none";
- } else {
- for (const auto& item : items) {
- // DEBUG: null pointer check
- if (item == NULL) {
- fprintf(stderr,
- "something really terrible has gone wrong - null pointer item in crate.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + item->as_string();
- }
- }
-
- return str + "\n";
- }
-
- ::std::string Attribute::as_string() const {
- ::std::string path_str = path.as_string();
- if (attr_input == NULL) {
- return path_str;
- } else {
- return path_str + attr_input->as_string();
- }
- }
-
- ::std::string DelimTokenTree::as_string() const {
- ::std::string start_delim;
- ::std::string end_delim;
- switch (delim_type) {
- case PARENS:
- start_delim = "(";
- end_delim = ")";
- break;
- case SQUARE:
- start_delim = "[";
- end_delim = "]";
- break;
- case CURLY:
- start_delim = "{";
- end_delim = "}";
- break;
- default:
- // error
- return "";
- }
- ::std::string str = start_delim;
- if (token_trees.empty()) {
- str += "none";
- } else {
- for (const auto& tree : token_trees) {
- // DEBUG: null pointer check
- if (tree == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "token tree in delim token tree.");
- return "NULL_POINTER_MARK";
- }
-
- str += tree->as_string() + ", ";
- }
- }
- str += end_delim;
-
- return str;
- }
-
- ::std::string Token::as_string() const {
- /* FIXME: only works when not identifier or literal or whatever, i.e. when doesn't store
- * string value */
- // return get_token_description(token_id);
-
- // maybe fixed - stores everything as string though, so storage-inefficient
- return str;
- }
-
- ::std::string SimplePathSegment::as_string() const {
- return segment_name;
- }
-
- ::std::string SimplePath::as_string() const {
- ::std::string path;
- if (has_opening_scope_resolution) {
- path = "::";
- }
-
- // crappy hack because doing proper for loop would be more code
- bool first_time = true;
- for (const auto& segment : segments) {
- if (first_time) {
- path += segment.as_string();
- first_time = false;
- } else {
- path += "::" + segment.as_string();
- }
-
- // DEBUG: remove later. Checks for path error.
- if (segment.is_error()) {
- fprintf(stderr,
- "segment in path is error - this should've been filtered out. first segment "
- "was '%s' \n",
- segments.at(0).as_string().c_str());
- }
- }
-
- return path;
- }
-
- ::std::string Visibility::as_string() const {
- switch (public_vis_type) {
- case NONE:
- return ::std::string("pub");
- case CRATE:
- return ::std::string("ub(crate)");
- case SELF:
- return ::std::string("pub(self)");
- case SUPER:
- return ::std::string("pub(super)");
- case IN_PATH:
- return ::std::string("pub(in ") + in_path.as_string() + ::std::string(")");
- default:
- gcc_unreachable();
- }
- }
-
- // Creates a string that reflects the visibility stored.
- ::std::string VisItem::as_string() const {
- // FIXME: can't do formatting on string to make identation occur.
- ::std::string str = Item::as_string();
-
- if (has_visibility()) {
- str = visibility.as_string() + " ";
- }
-
- return str;
- }
-
- // Creates a string that reflects the outer attributes stored.
- ::std::string Item::as_string() const {
- ::std::string str;
-
- if (!outer_attrs.empty()) {
- for (const auto& attr : outer_attrs) {
- str += attr.as_string() + "\n";
- }
- }
-
- return str;
- }
-
- ::std::string Module::as_string() const {
- ::std::string vis_item = VisItem::as_string();
-
- return vis_item + "mod " + module_name;
- }
-
- ::std::string ModuleBodied::as_string() const {
- // get module string for "[vis] mod [name]"
- ::std::string str = Module::as_string();
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- // items
- str += "\n items: ";
- if (items.empty()) {
- str += "none";
- } else {
- for (const auto& item : items) {
- // DEBUG: null pointer check
- if (item == NULL) {
- fprintf(stderr,
- "something really terrible has gone wrong - null pointer item in crate.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + item->as_string();
- }
- }
-
- return str + "\n";
- }
-
- ::std::string ModuleNoBody::as_string() const {
- ::std::string str = Module::as_string();
-
- str += "\n no body (reference to external file)";
-
- return str + "\n";
- }
-
- ::std::string StaticItem::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "static";
-
- if (has_mut) {
- str += " mut";
- }
-
- str += name;
-
- // DEBUG: null pointer check
- if (type == NULL) {
- fprintf(stderr,
- "something really terrible has gone wrong - null pointer type in static item.");
- return "NULL_POINTER_MARK";
- }
- str += "\n Type: " + type->as_string();
-
- // DEBUG: null pointer check
- if (expr == NULL) {
- fprintf(stderr,
- "something really terrible has gone wrong - null pointer expr in static item.");
- return "NULL_POINTER_MARK";
- }
- str += "\n Expression: " + expr->as_string();
-
- return str + "\n";
- }
-
- ::std::string ExternCrate::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "extern crate " + referenced_crate;
-
- if (has_as_clause()) {
- str += " as " + as_clause_name;
- }
-
- return str;
- }
-
- ::std::string TupleStruct::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "struct " + struct_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in enum.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- // tuple fields
- str += "\n Tuple fields: ";
- if (fields.empty()) {
- str += "none";
- } else {
- for (const auto& field : fields) {
- str += "\n " + field.as_string();
- }
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- return str;
- }
-
- ::std::string ConstantItem::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "const " + identifier;
-
- // DEBUG: null pointer check
- if (type == NULL) {
- fprintf(stderr,
- "something really terrible has gone wrong - null pointer type in const item.");
- return "NULL_POINTER_MARK";
- }
- str += "\n Type: " + type->as_string();
-
- // DEBUG: null pointer check
- if (const_expr == NULL) {
- fprintf(stderr,
- "something really terrible has gone wrong - null pointer expr in const item.");
- return "NULL_POINTER_MARK";
- }
- str += "\n Expression: " + const_expr->as_string();
-
- return str + "\n";
- }
-
- ::std::string InherentImpl::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "impl ";
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in inherent impl.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Type: " + trait_type->as_string();
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- // inherent impl items
- str += "\n Inherent impl items: ";
- if (!has_impl_items()) {
- str += "none";
- } else {
- for (const auto& item : impl_items) {
- str += "\n " + item->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string Method::as_string() const {
- ::std::string str("Method: \n ");
-
- str += vis.as_string() + " " + qualifiers.as_string();
-
- str += " fn " + method_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in method.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Self param: " + self_param.as_string();
-
- str += "\n Function params: ";
- if (function_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : function_params) {
- str += "\n " + param.as_string();
- }
- }
-
- str += "\n Return type: ";
- if (has_return_type()) {
- str += return_type->as_string();
- } else {
- str += "none (void)";
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- str += "\n Block expr (body): \n ";
- str += expr->as_string();
-
- return str;
- }
-
- ::std::string StructStruct::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "struct " + struct_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in enum.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- // struct fields
- str += "\n Struct fields: ";
- if (is_unit) {
- str += "none (unit)";
- } else if (fields.empty()) {
- str += "none (non-unit)";
- } else {
- for (const auto& field : fields) {
- str += "\n " + field.as_string();
- }
- }
-
- return str;
- }
-
- ::std::string UseDeclaration::as_string() const {
- ::std::string str = VisItem::as_string();
-
- // DEBUG: null pointer check
- if (use_tree == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer use tree in "
- "use declaration.");
- return "NULL_POINTER_MARK";
- }
-
- str += "use " + use_tree->as_string();
-
- return str;
- }
-
- ::std::string UseTreeGlob::as_string() const {
- switch (glob_type) {
- case NO_PATH:
- return "*";
- case GLOBAL:
- return "::*";
- case PATH_PREFIXED: {
- ::std::string path_str = path.as_string();
- return path_str + "::*";
- }
- default:
- // some kind of error
- return "ERROR-PATH";
- }
- gcc_unreachable();
- }
-
- ::std::string UseTreeList::as_string() const {
- ::std::string path_str;
- switch (path_type) {
- case NO_PATH:
- path_str = "{";
- break;
- case GLOBAL:
- path_str = "::{";
- break;
- case PATH_PREFIXED: {
- path_str = path.as_string() + "::{";
- break;
- }
- default:
- // some kind of error
- return "ERROR-PATH-LIST";
- }
-
- if (has_trees()) {
- for (const auto& tree : trees) {
- // DEBUG: null pointer check
- if (tree == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "tree in use tree list.");
- return "NULL_POINTER_MARK";
- }
-
- path_str += tree->as_string() + ", ";
- }
- } else {
- path_str += "none";
- }
-
- return path_str + "}";
- }
-
- ::std::string UseTreeRebind::as_string() const {
- ::std::string path_str = path.as_string();
-
- switch (bind_type) {
- case NONE:
- // nothing to add, just path
- break;
- case IDENTIFIER:
- path_str += " as " + identifier;
- break;
- case WILDCARD:
- path_str += " as _";
- break;
- default:
- // error
- return "ERROR-PATH-REBIND";
- }
-
- return path_str;
- }
-
- ::std::string Enum::as_string() const {
- ::std::string str = VisItem::as_string();
- str += enum_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in enum.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- // items
- str += "\n Items: ";
- if (items.empty()) {
- str += "none";
- } else {
- for (const auto& item : items) {
- // DEBUG: null pointer check
- if (item == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "enum item in enum.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + item->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string Trait::as_string() const {
- ::std::string str = VisItem::as_string();
-
- if (has_unsafe) {
- str += "unsafe ";
- }
-
- str += "trait " + name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in trait.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Type param bounds: ";
- if (!has_type_param_bounds()) {
- str += "none";
- } else {
- for (const auto& bound : type_param_bounds) {
- // DEBUG: null pointer check
- if (bound == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "type param bound in trait.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + bound->as_string();
- }
- }
-
- str += "\n Where clause: ";
- if (!has_where_clause()) {
- str += "none";
- } else {
- str += where_clause.as_string();
- }
-
- str += "\n Trait items: ";
- if (!has_trait_items()) {
- str += "none";
- } else {
- for (const auto& item : trait_items) {
- // DEBUG: null pointer check
- if (item == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "trait item in trait.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + item->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string Union::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "union " + union_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in union.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- // struct fields
- str += "\n Struct fields (variants): ";
- if (variants.empty()) {
- str += "none";
- } else {
- for (const auto& field : variants) {
- str += "\n " + field.as_string();
- }
- }
-
- return str;
- }
-
- ::std::string Function::as_string() const {
- ::std::string str = VisItem::as_string() + "Function: ";
- ::std::string qualifiers_str = "Qualifiers: " + qualifiers.as_string();
-
- ::std::string generic_params_str("Generic params: ");
- if (has_generics()) {
- for (const auto& generic_param : generic_params) {
- // DEBUG: null pointer check
- if (generic_param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in function item.");
- return "NULL_POINTER_MARK";
- }
-
- generic_params_str += generic_param->as_string() + ", ";
- }
- } else {
- generic_params_str += "none";
- }
-
- ::std::string function_params_str("Function params: ");
- if (has_function_params()) {
- for (const auto& param : function_params) {
- function_params_str += param.as_string() + ", ";
- }
- } else {
- function_params_str += "none";
- }
-
- ::std::string return_type_str("Return type: ");
- if (has_function_return_type()) {
- // DEBUG: null pointer check
- if (return_type == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer return "
- "type in function.");
- return "NULL_POINTER_MARK";
- }
-
- return_type_str += return_type->as_string();
- } else {
- return_type_str += "none (void)";
- }
-
- ::std::string where_clause_str("Where clause: ");
- if (has_where_clause()) {
- where_clause_str += where_clause.as_string();
- } else {
- where_clause_str += "none";
- }
-
- // DEBUG: null pointer check
- if (function_body == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer function "
- "body in function.");
- return "NULL_POINTER_MARK";
- }
- ::std::string body_str = "Body: " + function_body->as_string();
-
- str += "\n " + qualifiers_str + "\n " + generic_params_str + "\n "
- + function_params_str + "\n " + return_type_str + "\n " + where_clause_str
- + "\n " + body_str;
-
- return str;
- }
-
- ::std::string WhereClause::as_string() const {
- // just print where clause items, don't mention "where" or "where clause"
- ::std::string str;
-
- if (where_clause_items.empty()) {
- str = "none";
- } else {
- for (const auto& item : where_clause_items) {
- str += "\n " + item->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string BlockExpr::as_string() const {
- ::std::string str = "BlockExpr: ";
-
- // get outer attributes
- str += "\n " + Expr::as_string();
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- // statements
- str += "\n statements: ";
- if (statements.empty()) {
- str += "none";
- } else {
- for (const auto& stmt : statements) {
- // DEBUG: null pointer check
- if (stmt == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "stmt in block expr.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + stmt->as_string();
- }
- }
-
- // final expression
- str += "\n final expression: ";
- if (expr == NULL) {
- str += "none";
- } else {
- str += "\n " + expr->as_string();
- }
-
- return str;
- }
-
- ::std::string TraitImpl::as_string() const {
- ::std::string str = VisItem::as_string();
-
- if (has_unsafe) {
- str += "unsafe ";
- }
-
- str += "impl ";
-
- // generic params
- str += "\n Generic params: ";
- if (!has_generics()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Has exclam: ";
- if (has_exclam) {
- str += "true";
- } else {
- str += "false";
- }
-
- str += "\n TypePath (to trait): " + trait_path.as_string();
-
- str += "\n Type (struct to impl on): " + trait_type->as_string();
-
- str += "\n Where clause: ";
- if (!has_where_clause()) {
- str += "none";
- } else {
- str += where_clause.as_string();
- }
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n trait impl items: ";
- if (!has_impl_items()) {
- str += "none";
- } else {
- for (const auto& item : impl_items) {
- str += "\n " + item->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string TypeAlias::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += " " + new_type_name;
-
- // generic params
- str += "\n Generic params: ";
- if (!has_generics()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- str += param->as_string() + ", ";
- }
- }
-
- str += "\n Where clause: ";
- if (!has_where_clause()) {
- str += "none";
- } else {
- str += where_clause.as_string();
- }
-
- str += "\n Type: " + existing_type->as_string();
-
- return str;
- }
-
- ::std::string MacroInvocationSemi::as_string() const {
- // get outer attrs
- ::std::string str = MacroItem::as_string();
-
- str += "\n" + path.as_string() + "!";
-
- ::std::string tok_trees;
- if (token_trees.empty()) {
- tok_trees = "none";
- } else {
- for (const auto& tree : token_trees) {
- // DEBUG: null pointer check
- if (tree == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "token tree in macro invocation semi.");
- return "NULL_POINTER_MARK";
- }
-
- tok_trees += tree->as_string() + ", ";
- }
- }
-
- return str + get_string_in_delims(::std::move(tok_trees), delim_type);
- }
-
- ::std::string ExternBlock::as_string() const {
- ::std::string str = VisItem::as_string();
-
- str += "extern ";
- if (has_abi()) {
- str += "\"" + abi + "\" ";
- }
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n external items: ";
- if (!has_extern_items()) {
- str += "none";
- } else {
- for (const auto& item : extern_items) {
- str += "\n " + item->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string MacroRule::as_string() const {
- ::std::string str("Macro rule: ");
-
- str += "\n Matcher: \n ";
- str += matcher.as_string();
-
- str += "\n Transcriber: \n ";
- str += transcriber.as_string();
-
- return str;
- }
-
- ::std::string MacroRulesDefinition::as_string() const {
- ::std::string str("macro_rules!");
-
- str += rule_name;
-
- str += "\n Macro rules: ";
- if (rules.empty()) {
- str += "none";
- } else {
- for (const auto& rule : rules) {
- str += "\n " + rule.as_string();
- }
- }
-
- str += "\n Delim type: ";
- switch (delim_type) {
- case PARENS:
- str += "parentheses";
- break;
- case SQUARE:
- str += "square";
- break;
- case CURLY:
- str += "curly";
- break;
- default:
- return "ERROR_MARK_STRING - delim type in macro invocation";
- }
-
- return str;
- }
-
- ::std::string MacroInvocation::as_string() const {
- return path.as_string() + "!" + token_tree.as_string();
- }
-
- ::std::string PathInExpression::as_string() const {
- ::std::string str;
-
- if (has_opening_scope_resolution) {
- str = "::";
- }
-
- return str + PathPattern::as_string();
- }
-
- ::std::string ExprStmtWithBlock::as_string() const {
- ::std::string str("ExprStmtWithBlock: \n ");
-
- if (expr == NULL) {
- str += "none (this should not happen and is an error)";
- } else {
- str += expr->as_string();
- }
-
- return str;
- }
-
- ::std::string ClosureParam::as_string() const {
- ::std::string str(pattern->as_string());
-
- if (has_type_given()) {
- str += " : " + type->as_string();
- }
-
- return str;
- }
-
- ::std::string ClosureExpr::as_string() const {
- ::std::string str("ClosureExpr:\n Has move: ");
- if (has_move) {
- str += "true";
- } else {
- str += "false";
- }
+namespace AST {
+
+enum indent_mode
+{
+ enter,
+ out,
+ stay
+};
+
+::std::string
+indent_spaces (enum indent_mode mode)
+{
+ static int indent = 0;
+ ::std::string str = "";
+ if (out == mode)
+ indent--;
+ for (int i = 0; i < indent; i++)
+ str += " ";
+ if (enter == mode)
+ indent++;
+
+ return str;
+}
+
+// Gets a string in a certain delim type.
+::std::string
+get_string_in_delims (::std::string str_input, DelimType delim_type)
+{
+ switch (delim_type)
+ {
+ case PARENS:
+ return "(" + str_input + ")";
+ case SQUARE:
+ return "[" + str_input + "]";
+ case CURLY:
+ return "{" + str_input + "}";
+ default:
+ return "ERROR-MARK-STRING (delims)";
+ }
+ gcc_unreachable ();
+}
+
+// Converts a frag spec enum item to a string form.
+::std::string
+frag_spec_to_str (MacroFragSpec frag_spec)
+{
+ switch (frag_spec)
+ {
+ case BLOCK:
+ return "block";
+ case EXPR:
+ return "expr";
+ case IDENT:
+ return "ident";
+ case ITEM:
+ return "item";
+ case LIFETIME:
+ return "lifetime";
+ case LITERAL:
+ return "literal";
+ case META:
+ return "meta";
+ case PAT:
+ return "pat";
+ case PATH:
+ return "path";
+ case STMT:
+ return "stmt";
+ case TT:
+ return "tt";
+ case TY:
+ return "ty";
+ case VIS:
+ return "vis";
+ case INVALID:
+ return "INVALID_FRAG_SPEC";
+ default:
+ return "ERROR_MARK_STRING - unknown frag spec";
+ }
+}
+
+::std::string
+Crate::as_string () const
+{
+ fprintf (stderr, "beginning crate recursive as-string\n");
+
+ ::std::string str ("Crate: ");
+ // add utf8bom and shebang
+ if (has_utf8bom)
+ {
+ str += "\n has utf8bom";
+ }
+ if (has_shebang)
+ {
+ str += "\n has shebang";
+ }
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ // items
+ str += "\n items: ";
+ if (items.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : items)
+ {
+ // DEBUG: null pointer check
+ if (item == NULL)
+ {
+ fprintf (stderr, "something really terrible has gone wrong - "
+ "null pointer item in crate.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str + "\n";
+}
+
+::std::string
+Attribute::as_string () const
+{
+ ::std::string path_str = path.as_string ();
+ if (attr_input == NULL)
+ {
+ return path_str;
+ }
+ else
+ {
+ return path_str + attr_input->as_string ();
+ }
+}
+
+::std::string
+DelimTokenTree::as_string () const
+{
+ ::std::string start_delim;
+ ::std::string end_delim;
+ switch (delim_type)
+ {
+ case PARENS:
+ start_delim = "(";
+ end_delim = ")";
+ break;
+ case SQUARE:
+ start_delim = "[";
+ end_delim = "]";
+ break;
+ case CURLY:
+ start_delim = "{";
+ end_delim = "}";
+ break;
+ default:
+ // error
+ return "";
+ }
+ ::std::string str = start_delim;
+ if (token_trees.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ auto i = token_trees.begin ();
+ auto e = token_trees.end ();
+
+ // DEBUG: null pointer check
+ if (*i == NULL)
+ {
+ fprintf (stderr,
+ "something really terrible has gone wrong - null pointer "
+ "token tree in delim token tree.");
+ return "NULL_POINTER_MARK";
+ }
+
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ }
+ str += end_delim;
+
+ return str;
+}
+
+::std::string
+Token::as_string () const
+{
+ /* FIXME: only works when not identifier or literal or whatever, i.e. when
+ * doesn't store string value */
+ // return get_token_description(token_id);
+
+ // maybe fixed - stores everything as string though, so storage-inefficient
+ return str;
+}
+
+::std::string
+SimplePathSegment::as_string () const
+{
+ return segment_name;
+}
+
+::std::string
+SimplePath::as_string () const
+{
+ ::std::string path;
+ if (has_opening_scope_resolution)
+ {
+ path = "::";
+ }
+
+ // crappy hack because doing proper for loop would be more code
+ bool first_time = true;
+ for (const auto &segment : segments)
+ {
+ if (first_time)
+ {
+ path += segment.as_string ();
+ first_time = false;
+ }
+ else
+ {
+ path += "::" + segment.as_string ();
+ }
+
+ // DEBUG: remove later. Checks for path error.
+ if (segment.is_error ())
+ {
+ fprintf (stderr,
+ "segment in path is error - this should've been filtered "
+ "out. first segment "
+ "was '%s' \n",
+ segments.at (0).as_string ().c_str ());
+ }
+ }
+
+ return path;
+}
+
+::std::string
+Visibility::as_string () const
+{
+ switch (public_vis_type)
+ {
+ case NONE:
+ return ::std::string ("pub");
+ case CRATE:
+ return ::std::string ("ub(crate)");
+ case SELF:
+ return ::std::string ("pub(self)");
+ case SUPER:
+ return ::std::string ("pub(super)");
+ case IN_PATH:
+ return ::std::string ("pub(in ") + in_path.as_string ()
+ + ::std::string (")");
+ default:
+ gcc_unreachable ();
+ }
+}
+
+// Creates a string that reflects the visibility stored.
+::std::string
+VisItem::as_string () const
+{
+ // FIXME: can't do formatting on string to make identation occur.
+ ::std::string str = Item::as_string ();
+
+ if (has_visibility ())
+ {
+ str = visibility.as_string () + " ";
+ }
+
+ return str;
+}
+
+// Creates a string that reflects the outer attributes stored.
+::std::string
+Item::as_string () const
+{
+ ::std::string str;
+
+ if (!outer_attrs.empty ())
+ {
+ for (const auto &attr : outer_attrs)
+ {
+ str += attr.as_string () + "\n";
+ }
+ }
+
+ return str;
+}
+
+::std::string
+Module::as_string () const
+{
+ ::std::string vis_item = VisItem::as_string ();
+
+ return vis_item + "mod " + module_name;
+}
+
+::std::string
+ModuleBodied::as_string () const
+{
+ // get module string for "[vis] mod [name]"
+ ::std::string str = Module::as_string ();
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ // items
+ str += "\n items: ";
+ if (items.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : items)
+ {
+ // DEBUG: null pointer check
+ if (item == NULL)
+ {
+ fprintf (stderr, "something really terrible has gone wrong - "
+ "null pointer item in crate.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str + "\n";
+}
+
+::std::string
+ModuleNoBody::as_string () const
+{
+ ::std::string str = Module::as_string ();
+
+ str += "\n no body (reference to external file)";
+
+ return str + "\n";
+}
+
+::std::string
+StaticItem::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "static";
+
+ if (has_mut)
+ {
+ str += " mut";
+ }
+
+ str += name;
+
+ // DEBUG: null pointer check
+ if (type == NULL)
+ {
+ fprintf (stderr, "something really terrible has gone wrong - null "
+ "pointer type in static item.");
+ return "NULL_POINTER_MARK";
+ }
+ str += "\n Type: " + type->as_string ();
+
+ // DEBUG: null pointer check
+ if (expr == NULL)
+ {
+ fprintf (stderr, "something really terrible has gone wrong - null "
+ "pointer expr in static item.");
+ return "NULL_POINTER_MARK";
+ }
+ str += "\n Expression: " + expr->as_string ();
+
+ return str + "\n";
+}
+
+::std::string
+ExternCrate::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "extern crate " + referenced_crate;
+
+ if (has_as_clause ())
+ {
+ str += " as " + as_clause_name;
+ }
+
+ return str;
+}
+
+::std::string
+TupleStruct::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "struct " + struct_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in enum.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ // tuple fields
+ str += "\n Tuple fields: ";
+ if (fields.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &field : fields)
+ {
+ str += "\n " + field.as_string ();
+ }
+ }
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+
+ return str;
+}
+
+::std::string
+ConstantItem::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "const " + identifier;
+
+ // DEBUG: null pointer check
+ if (type == NULL)
+ {
+ fprintf (stderr, "something really terrible has gone wrong - null "
+ "pointer type in const item.");
+ return "NULL_POINTER_MARK";
+ }
+ str += "\n Type: " + type->as_string ();
+
+ // DEBUG: null pointer check
+ if (const_expr == NULL)
+ {
+ fprintf (stderr, "something really terrible has gone wrong - null "
+ "pointer expr in const item.");
+ return "NULL_POINTER_MARK";
+ }
+ str += "\n Expression: " + const_expr->as_string ();
+
+ return str + "\n";
+}
+
+::std::string
+InherentImpl::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "impl ";
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in inherent impl.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Type: " + trait_type->as_string ();
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ // inherent impl items
+ str += "\n Inherent impl items: ";
+ if (!has_impl_items ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : impl_items)
+ {
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+Method::as_string () const
+{
+ ::std::string str ("Method: \n ");
+
+ str += vis.as_string () + " " + qualifiers.as_string ();
+
+ str += " fn " + method_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in method.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Self param: " + self_param.as_string ();
+
+ str += "\n Function params: ";
+ if (function_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : function_params)
+ {
+ str += "\n " + param.as_string ();
+ }
+ }
+
+ str += "\n Return type: ";
+ if (has_return_type ())
+ {
+ str += return_type->as_string ();
+ }
+ else
+ {
+ str += "none (void)";
+ }
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+
+ str += "\n Block expr (body): \n ";
+ str += expr->as_string ();
+
+ return str;
+}
+
+::std::string
+StructStruct::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "struct " + struct_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in enum.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+
+ // struct fields
+ str += "\n Struct fields: ";
+ if (is_unit)
+ {
+ str += "none (unit)";
+ }
+ else if (fields.empty ())
+ {
+ str += "none (non-unit)";
+ }
+ else
+ {
+ for (const auto &field : fields)
+ {
+ str += "\n " + field.as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+UseDeclaration::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ // DEBUG: null pointer check
+ if (use_tree == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer use tree in "
+ "use declaration.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "use " + use_tree->as_string ();
+
+ return str;
+}
+
+::std::string
+UseTreeGlob::as_string () const
+{
+ switch (glob_type)
+ {
+ case NO_PATH:
+ return "*";
+ case GLOBAL:
+ return "::*";
+ case PATH_PREFIXED:
+ {
+ ::std::string path_str = path.as_string ();
+ return path_str + "::*";
+ }
+ default:
+ // some kind of error
+ return "ERROR-PATH";
+ }
+ gcc_unreachable ();
+}
+
+::std::string
+UseTreeList::as_string () const
+{
+ ::std::string path_str;
+ switch (path_type)
+ {
+ case NO_PATH:
+ path_str = "{";
+ break;
+ case GLOBAL:
+ path_str = "::{";
+ break;
+ case PATH_PREFIXED:
+ {
+ path_str = path.as_string () + "::{";
+ break;
+ }
+ default:
+ // some kind of error
+ return "ERROR-PATH-LIST";
+ }
+
+ if (has_trees ())
+ {
+ auto i = trees.begin ();
+ auto e = trees.end ();
+
+ // DEBUG: null pointer check
+ if (*i == NULL)
+ {
+ fprintf (stderr,
+ "something really terrible has gone wrong - null pointer "
+ "tree in use tree list.");
+ return "NULL_POINTER_MARK";
+ }
+
+ for (; i != e; i++)
+ {
+ path_str += (*i)->as_string ();
+ if (e != i + 1)
+ path_str += ", ";
+ }
+ }
+ else
+ {
+ path_str += "none";
+ }
+
+ return path_str + "}";
+}
+
+::std::string
+UseTreeRebind::as_string () const
+{
+ ::std::string path_str = path.as_string ();
+
+ switch (bind_type)
+ {
+ case NONE:
+ // nothing to add, just path
+ break;
+ case IDENTIFIER:
+ path_str += " as " + identifier;
+ break;
+ case WILDCARD:
+ path_str += " as _";
+ break;
+ default:
+ // error
+ return "ERROR-PATH-REBIND";
+ }
+
+ return path_str;
+}
+
+::std::string
+Enum::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+ str += enum_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in enum.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+
+ // items
+ str += "\n Items: ";
+ if (items.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : items)
+ {
+ // DEBUG: null pointer check
+ if (item == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "enum item in enum.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+Trait::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ if (has_unsafe)
+ {
+ str += "unsafe ";
+ }
+
+ str += "trait " + name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in trait.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Type param bounds: ";
+ if (!has_type_param_bounds ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &bound : type_param_bounds)
+ {
+ // DEBUG: null pointer check
+ if (bound == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "type param bound in trait.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + bound->as_string ();
+ }
+ }
+
+ str += "\n Where clause: ";
+ if (!has_where_clause ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += where_clause.as_string ();
+ }
+
+ str += "\n Trait items: ";
+ if (!has_trait_items ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : trait_items)
+ {
+ // DEBUG: null pointer check
+ if (item == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "trait item in trait.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+Union::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "union " + union_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in union.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+
+ // struct fields
+ str += "\n Struct fields (variants): ";
+ if (variants.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &field : variants)
+ {
+ str += "\n " + field.as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+Function::as_string () const
+{
+ ::std::string str = VisItem::as_string () + "\n";
+ ::std::string qstr = qualifiers.as_string ();
+ if ("" != qstr)
+ str += qstr + " ";
+
+ if (has_function_return_type ())
+ {
+ // DEBUG: null pointer check
+ if (return_type == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer return "
+ "type in function.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += return_type->as_string () + " ";
+ }
+ else
+ {
+ str += "void ";
+ }
+
+ str += function_name;
+
+ if (has_generics ())
+ {
+ str += "<";
+
+ auto i = generic_params.begin ();
+ auto e = generic_params.end ();
+
+ // DEBUG: null pointer check
+ if (i == e)
+ {
+ fprintf (stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in function item.");
+ return "NULL_POINTER_MARK";
+ }
+
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ str += ">";
+ }
+
+ if (has_function_params ())
+ {
+ auto i = function_params.begin ();
+ auto e = function_params.end ();
+ str += "(";
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ str += ")";
+ }
+ else
+ {
+ str += "()";
+ }
+
+ if (has_where_clause ())
+ {
+ str += " where " + where_clause.as_string ();
+ }
+
+ str += "\n";
+
+ // DEBUG: null pointer check
+ if (function_body == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer function "
+ "body in function.");
+ return "NULL_POINTER_MARK";
+ }
+ str += function_body->as_string () + "\n";
+
+ return str;
+}
+
+::std::string
+WhereClause::as_string () const
+{
+ // just print where clause items, don't mention "where" or "where clause"
+ ::std::string str;
+
+ if (where_clause_items.empty ())
+ {
+ str = "none";
+ }
+ else
+ {
+ for (const auto &item : where_clause_items)
+ {
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+BlockExpr::as_string () const
+{
+ ::std::string istr = indent_spaces (enter);
+ ::std::string str = istr + "BlockExpr:\n" + istr;
+ // get outer attributes
+ str += "{\n" + indent_spaces (stay) + Expr::as_string ();
+
+ // inner attributes
+ str += "\n" + indent_spaces (stay) + "inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n" + indent_spaces (stay) + attr.as_string ();
+ }
+ }
+
+ // statements
+ str += "\n" + indent_spaces (stay) + "statements: ";
+ if (statements.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &stmt : statements)
+ {
+ // DEBUG: null pointer check
+ if (stmt == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "stmt in block expr.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n" + indent_spaces (stay) + stmt->as_string ();
+ }
+ }
+
+ // final expression
+ str += "\n" + indent_spaces (stay) + "final expression: ";
+ if (expr == NULL)
+ {
+ str += "none";
+ }
+ else
+ {
+ str += "\n" + expr->as_string ();
+ }
+
+ str += "\n" + indent_spaces (out) + "}";
+ return str;
+}
+
+::std::string
+TraitImpl::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ if (has_unsafe)
+ {
+ str += "unsafe ";
+ }
+
+ str += "impl ";
+
+ // generic params
+ str += "\n Generic params: ";
+ if (!has_generics ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Has exclam: ";
+ if (has_exclam)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
+
+ str += "\n TypePath (to trait): " + trait_path.as_string ();
+
+ str += "\n Type (struct to impl on): " + trait_type->as_string ();
+
+ str += "\n Where clause: ";
+ if (!has_where_clause ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += where_clause.as_string ();
+ }
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ str += "\n trait impl items: ";
+ if (!has_impl_items ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : impl_items)
+ {
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+TypeAlias::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += " " + new_type_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (!has_generics ())
+ {
+ str += "none";
+ }
+ else
+ {
+ auto i = generic_params.begin ();
+ auto e = generic_params.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ }
+
+ str += "\n Where clause: ";
+ if (!has_where_clause ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += where_clause.as_string ();
+ }
+
+ str += "\n Type: " + existing_type->as_string ();
+
+ return str;
+}
+
+::std::string
+MacroInvocationSemi::as_string () const
+{
+ // get outer attrs
+ ::std::string str = MacroItem::as_string ();
+
+ str += "\n" + path.as_string () + "!";
+
+ ::std::string tok_trees;
+ if (token_trees.empty ())
+ {
+ tok_trees = "none";
+ }
+ else
+ {
+ auto i = token_trees.begin ();
+ auto e = token_trees.end ();
+
+ // DEBUG: null pointer check
+ if (i == e)
+ {
+ fprintf (stderr,
+ "something really terrible has gone wrong - null pointer "
+ "token tree in macro invocation semi.");
+ return "NULL_POINTER_MARK";
+ }
+
+ ::std::string s;
+ for (; i != e; i++)
+ {
+ s += (*i)->as_string ();
+ if (e != i + 1)
+ s += ", ";
+ }
+
+ tok_trees += get_string_in_delims (s, delim_type);
+ }
+
+ return str;
+}
+
+::std::string
+ExternBlock::as_string () const
+{
+ ::std::string str = VisItem::as_string ();
+
+ str += "extern ";
+ if (has_abi ())
+ {
+ str += "\"" + abi + "\" ";
+ }
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ str += "\n external items: ";
+ if (!has_extern_items ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : extern_items)
+ {
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+MacroRule::as_string () const
+{
+ ::std::string str ("Macro rule: ");
+
+ str += "\n Matcher: \n ";
+ str += matcher.as_string ();
+
+ str += "\n Transcriber: \n ";
+ str += transcriber.as_string ();
+
+ return str;
+}
+
+::std::string
+MacroRulesDefinition::as_string () const
+{
+ ::std::string str ("macro_rules!");
+
+ str += rule_name;
+
+ str += "\n Macro rules: ";
+ if (rules.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &rule : rules)
+ {
+ str += "\n " + rule.as_string ();
+ }
+ }
+
+ str += "\n Delim type: ";
+ switch (delim_type)
+ {
+ case PARENS:
+ str += "parentheses";
+ break;
+ case SQUARE:
+ str += "square";
+ break;
+ case CURLY:
+ str += "curly";
+ break;
+ default:
+ return "ERROR_MARK_STRING - delim type in macro invocation";
+ }
+
+ return str;
+}
+
+::std::string
+MacroInvocation::as_string () const
+{
+ return path.as_string () + "!" + token_tree.as_string ();
+}
+
+::std::string
+PathInExpression::as_string () const
+{
+ ::std::string str;
+
+ if (has_opening_scope_resolution)
+ {
+ str = "::";
+ }
+
+ return str + PathPattern::as_string ();
+}
+
+::std::string
+ExprStmtWithBlock::as_string () const
+{
+ ::std::string str = indent_spaces (enter) + "ExprStmtWithBlock: \n";
+
+ if (expr == NULL)
+ {
+ str += "none (this should not happen and is an error)";
+ }
+ else
+ {
+ indent_spaces (enter);
+ str += expr->as_string ();
+ indent_spaces (out);
+ }
+
+ indent_spaces (out);
+ return str;
+}
+
+::std::string
+ClosureParam::as_string () const
+{
+ ::std::string str (pattern->as_string ());
+
+ if (has_type_given ())
+ {
+ str += " : " + type->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+ClosureExpr::as_string () const
+{
+ ::std::string str ("ClosureExpr:\n Has move: ");
+ if (has_move)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
+
+ str += "\n Params: ";
+ if (params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : params)
+ {
+ str += "\n " + param.as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+ClosureExprInnerTyped::as_string () const
+{
+ ::std::string str = ClosureExpr::as_string ();
+
+ str += "\n Return type: " + return_type->as_string ();
+
+ str += "\n Body: " + expr->as_string ();
+
+ return str;
+}
+
+::std::string
+PathPattern::as_string () const
+{
+ ::std::string str;
+
+ for (const auto &segment : segments)
+ {
+ str += segment.as_string () + "::";
+ }
+
+ // basically a hack - remove last two characters of string (remove final ::)
+ str.erase (str.length () - 2);
+
+ return str;
+}
+
+::std::string
+QualifiedPathType::as_string () const
+{
+ ::std::string str ("<");
+ str += type_to_invoke_on->as_string ();
+
+ if (has_as_clause ())
+ {
+ str += " as " + trait_path.as_string ();
+ }
+
+ return str + ">";
+}
+
+::std::string
+QualifiedPathInExpression::as_string () const
+{
+ return path_type.as_string () + "::" + PathPattern::as_string ();
+}
+
+::std::string
+BorrowExpr::as_string () const
+{
+ ::std::string str ("&");
+
+ if (double_borrow)
+ {
+ str += "&";
+ }
+
+ if (is_mut)
+ {
+ str += "mut ";
+ }
+
+ str += main_or_left_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+ReturnExpr::as_string () const
+{
+ ::std::string str ("return ");
+
+ if (has_return_expr ())
+ {
+ str += return_expr->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+GroupedExpr::as_string () const
+{
+ ::std::string str ("Grouped expr:");
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ str += "\n Expr in parens: " + expr_in_parens->as_string ();
+
+ return str;
+}
+
+::std::string
+RangeToExpr::as_string () const
+{
+ return ".." + to->as_string ();
+}
+
+::std::string
+ContinueExpr::as_string () const
+{
+ ::std::string str ("continue ");
+
+ if (has_label ())
+ {
+ str += label.as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+NegationExpr::as_string () const
+{
+ ::std::string str;
+
+ switch (negation_type)
+ {
+ case NEGATE:
+ str = "-";
+ break;
+ case NOT:
+ str = "!";
+ break;
+ default:
+ return "ERROR_MARK_STRING - negation expr";
+ }
+
+ str += main_or_left_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+RangeFromExpr::as_string () const
+{
+ return from->as_string () + "..";
+}
+
+::std::string
+RangeFullExpr::as_string () const
+{
+ return "..";
+}
+
+::std::string
+ArrayIndexExpr::as_string () const
+{
+ return array_expr->as_string () + "[" + index_expr->as_string () + "]";
+}
+
+::std::string
+AssignmentExpr::as_string () const
+{
+ ::std::string str ("AssignmentExpr: ");
+
+ if (main_or_left_expr == NULL || right_expr == NULL)
+ {
+ str += "error (either or both expressions are null)";
+ }
+ else
+ {
+ // left expr
+ str += "\n left: " + main_or_left_expr->as_string ();
+
+ // right expr
+ str += "\n right: " + right_expr->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+AsyncBlockExpr::as_string () const
+{
+ ::std::string str = "AsyncBlockExpr: ";
+
+ // get outer attributes
+ str += "\n " + Expr::as_string ();
+
+ str += "\n Has move: ";
+ str += has_move ? "true" : "false";
+
+ return str + "\n" + block_expr->as_string ();
+}
+
+::std::string
+ComparisonExpr::as_string () const
+{
+ ::std::string str (main_or_left_expr->as_string ());
+
+ switch (expr_type)
+ {
+ case EQUAL:
+ str += " == ";
+ break;
+ case NOT_EQUAL:
+ str += " != ";
+ break;
+ case GREATER_THAN:
+ str += " > ";
+ break;
+ case LESS_THAN:
+ str += " < ";
+ break;
+ case GREATER_OR_EQUAL:
+ str += " >= ";
+ break;
+ case LESS_OR_EQUAL:
+ str += " <= ";
+ break;
+ default:
+ return "ERROR_MARK_STRING - comparison expr";
+ }
+
+ str += right_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+MethodCallExpr::as_string () const
+{
+ ::std::string str ("MethodCallExpr: \n Object (receiver) expr: ");
+
+ str += receiver->as_string ();
+
+ str += "\n Method path segment: \n";
+
+ str += method_name.as_string ();
+
+ str += "\n Call params:";
+ if (params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : params)
+ {
+ if (param == NULL)
+ {
+ return "ERROR_MARK_STRING - method call expr param is null";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+TupleIndexExpr::as_string () const
+{
+ return tuple_expr->as_string () + "." + ::std::to_string (tuple_index);
+}
+
+::std::string
+DereferenceExpr::as_string () const
+{
+ return "*" + main_or_left_expr->as_string ();
+}
+
+::std::string
+FieldAccessExpr::as_string () const
+{
+ return receiver->as_string () + "." + field;
+}
+
+::std::string
+LazyBooleanExpr::as_string () const
+{
+ ::std::string str (main_or_left_expr->as_string ());
+
+ switch (expr_type)
+ {
+ case LOGICAL_OR:
+ str += " || ";
+ break;
+ case LOGICAL_AND:
+ str += " && ";
+ break;
+ default:
+ return "ERROR_MARK_STRING - lazy boolean expr out of bounds";
+ }
+
+ str += right_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+RangeFromToExpr::as_string () const
+{
+ return from->as_string () + ".." + to->as_string ();
+}
+
+::std::string
+RangeToInclExpr::as_string () const
+{
+ return "..=" + to->as_string ();
+}
+
+::std::string
+UnsafeBlockExpr::as_string () const
+{
+ ::std::string istr = indent_spaces (enter);
+ ::std::string str = istr + "UnsafeBlockExpr:";
+ str += istr + "{";
+
+ // get outer attributes
+ str += "\n" + indent_spaces (stay) + Expr::as_string ();
+
+ return str + "\n" + indent_spaces (out) + "}\n" + expr->as_string ();
+}
+
+::std::string
+ClosureExprInner::as_string () const
+{
+ ::std::string str = ClosureExpr::as_string ();
+
+ str += "\n Expression: " + closure_inner->as_string ();
+
+ return str;
+}
+
+::std::string
+IfExpr::as_string () const
+{
+ ::std::string str ("IfExpr: ");
+
+ str += "\n Condition expr: " + condition->as_string ();
+
+ str += "\n If block expr: " + if_block->as_string ();
+
+ return str;
+}
+
+::std::string
+IfExprConseqElse::as_string () const
+{
+ ::std::string str = IfExpr::as_string ();
+
+ str += "\n Else block expr: " + else_block->as_string ();
+
+ return str;
+}
+
+::std::string
+IfExprConseqIf::as_string () const
+{
+ ::std::string str = IfExpr::as_string ();
+
+ str += "\n Else if expr: \n " + if_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+IfExprConseqIfLet::as_string () const
+{
+ ::std::string str = IfExpr::as_string ();
+
+ str += "\n Else if let expr: \n " + if_let_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+IfLetExpr::as_string () const
+{
+ ::std::string str ("IfLetExpr: ");
+
+ str += "\n Condition match arm patterns: ";
+ if (match_arm_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &pattern : match_arm_patterns)
+ {
+ str += "\n " + pattern->as_string ();
+ }
+ }
+
+ str += "\n Scrutinee expr: " + value->as_string ();
+
+ str += "\n If let block expr: " + if_block->as_string ();
+
+ return str;
+}
+
+::std::string
+IfLetExprConseqElse::as_string () const
+{
+ ::std::string str = IfLetExpr::as_string ();
+
+ str += "\n Else block expr: " + else_block->as_string ();
+
+ return str;
+}
+
+::std::string
+IfLetExprConseqIf::as_string () const
+{
+ ::std::string str = IfLetExpr::as_string ();
+
+ str += "\n Else if expr: \n " + if_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+IfLetExprConseqIfLet::as_string () const
+{
+ ::std::string str = IfLetExpr::as_string ();
+
+ str += "\n Else if let expr: \n " + if_let_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+RangeFromToInclExpr::as_string () const
+{
+ return from->as_string () + "..=" + to->as_string ();
+}
+
+::std::string
+ErrorPropagationExpr::as_string () const
+{
+ return main_or_left_expr->as_string () + "?";
+}
+
+::std::string
+CompoundAssignmentExpr::as_string () const
+{
+ ::std::string operator_str;
+ operator_str.reserve (1);
+
+ // get operator string
+ switch (expr_type)
+ {
+ case ADD:
+ operator_str = "+";
+ break;
+ case SUBTRACT:
+ operator_str = "-";
+ break;
+ case MULTIPLY:
+ operator_str = "*";
+ break;
+ case DIVIDE:
+ operator_str = "/";
+ break;
+ case MODULUS:
+ operator_str = "%";
+ break;
+ case BITWISE_AND:
+ operator_str = "&";
+ break;
+ case BITWISE_OR:
+ operator_str = "|";
+ break;
+ case BITWISE_XOR:
+ operator_str = "^";
+ break;
+ case LEFT_SHIFT:
+ operator_str = "<<";
+ break;
+ case RIGHT_SHIFT:
+ operator_str = ">>";
+ break;
+ default:
+ operator_str = "invalid operator. wtf";
+ break;
+ }
+
+ operator_str += "=";
+
+ ::std::string str ("CompoundAssignmentExpr: ");
+ if (main_or_left_expr == NULL || right_expr == NULL)
+ {
+ str += "error. this is probably a parsing failure.";
+ }
+ else
+ {
+ str += "\n left: " + main_or_left_expr->as_string ();
+ str += "\n right: " + right_expr->as_string ();
+ str += "\n operator: " + operator_str;
+ }
+
+ return str;
+}
+
+::std::string
+ArithmeticOrLogicalExpr::as_string () const
+{
+ ::std::string operator_str;
+ operator_str.reserve (1);
+
+ // get operator string
+ switch (expr_type)
+ {
+ case ADD:
+ operator_str = "+";
+ break;
+ case SUBTRACT:
+ operator_str = "-";
+ break;
+ case MULTIPLY:
+ operator_str = "*";
+ break;
+ case DIVIDE:
+ operator_str = "/";
+ break;
+ case MODULUS:
+ operator_str = "%";
+ break;
+ case BITWISE_AND:
+ operator_str = "&";
+ break;
+ case BITWISE_OR:
+ operator_str = "|";
+ break;
+ case BITWISE_XOR:
+ operator_str = "^";
+ break;
+ case LEFT_SHIFT:
+ operator_str = "<<";
+ break;
+ case RIGHT_SHIFT:
+ operator_str = ">>";
+ break;
+ default:
+ operator_str = "invalid operator. wtf";
+ break;
+ }
+
+ ::std::string str ("ArithmeticOrLogicalExpr: ");
+ if (main_or_left_expr == NULL || right_expr == NULL)
+ {
+ str += "error. this is probably a parsing failure.";
+ }
+ else
+ {
+ str += main_or_left_expr->as_string () + " ";
+ str += operator_str + " ";
+ str += right_expr->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+CallExpr::as_string () const
+{
+ ::std::string str ("CallExpr: \n Function expr: ");
+
+ str += function->as_string ();
+
+ str += "\n Call params:";
+ if (!has_params ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : params)
+ {
+ if (param == NULL)
+ {
+ return "ERROR_MARK_STRING - call expr param is null";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+WhileLoopExpr::as_string () const
+{
+ ::std::string str ("WhileLoopExpr: ");
+
+ str += "\n Label: ";
+ if (!has_loop_label ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += loop_label.as_string ();
+ }
+
+ str += "\n Conditional expr: " + condition->as_string ();
+
+ str += "\n Loop block: " + loop_block->as_string ();
+
+ return str;
+}
+
+::std::string
+WhileLetLoopExpr::as_string () const
+{
+ ::std::string str ("WhileLetLoopExpr: ");
+
+ str += "\n Label: ";
+ if (!has_loop_label ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += loop_label.as_string ();
+ }
+
+ str += "\n Match arm patterns: ";
+ if (match_arm_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &pattern : match_arm_patterns)
+ {
+ str += "\n " + pattern->as_string ();
+ }
+ }
+
+ str += "\n Scrutinee expr: " + condition->as_string ();
+
+ str += "\n Loop block: " + loop_block->as_string ();
+
+ return str;
+}
+
+::std::string
+LoopExpr::as_string () const
+{
+ ::std::string str ("LoopExpr: (infinite loop)");
+
+ str += "\n Label: ";
+ if (!has_loop_label ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += loop_label.as_string ();
+ }
+
+ str += "\n Loop block: " + loop_block->as_string ();
+
+ return str;
+}
+
+::std::string
+ArrayExpr::as_string () const
+{
+ ::std::string str ("ArrayExpr:");
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ str += "\n Array elems: ";
+ if (!has_array_elems ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += internal_elements->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+AwaitExpr::as_string () const
+{
+ return awaited_expr->as_string () + ".await";
+}
+
+::std::string
+BreakExpr::as_string () const
+{
+ ::std::string str ("break ");
+
+ if (has_label ())
+ {
+ str += label.as_string () + " ";
+ }
+
+ if (has_break_expr ())
+ {
+ str += break_expr->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+LoopLabel::as_string () const
+{
+ return label.as_string () + ": (label) ";
+}
+
+::std::string
+MatchArm::as_string () const
+{
+ // outer attributes
+ ::std::string str = "Outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ str += "\nPatterns: ";
+ if (match_arm_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &pattern : match_arm_patterns)
+ {
+ str += "\n " + pattern->as_string ();
+ }
+ }
+
+ str += "\nGuard expr: ";
+ if (!has_match_arm_guard ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += guard_expr->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+MatchCase::as_string () const
+{
+ ::std::string str ("MatchCase: (match arm) ");
+
+ str += "\n Match arm matcher: \n" + arm.as_string ();
+
+ return str;
+}
+
+::std::string
+MatchCaseBlockExpr::as_string () const
+{
+ ::std::string str = MatchCase::as_string ();
+
+ str += "\n Block expr: " + block_expr->as_string ();
+
+ return str;
+}
+
+::std::string
+MatchCaseExpr::as_string () const
+{
+ ::std::string str = MatchCase::as_string ();
+
+ str += "\n Expr: " + expr->as_string ();
+
+ return str;
+}
+
+::std::string
+MatchExpr::as_string () const
+{
+ ::std::string str ("MatchExpr:");
+
+ str += "\n Scrutinee expr: " + branch_value->as_string ();
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ // match arms
+ str += "\n Match arms: ";
+ if (match_arms.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &arm : match_arms)
+ {
+ str += "\n " + arm->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+TupleExpr::as_string () const
+{
+ ::std::string str ("TupleExpr:");
+
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
+
+ str += "\n Tuple elements: ";
+ if (tuple_elems.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &elem : tuple_elems)
+ {
+ str += "\n " + elem->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+ExprStmtWithoutBlock::as_string () const
+{
+ ::std::string str ("ExprStmtWithoutBlock:\n");
+ indent_spaces (enter);
+ str += indent_spaces (stay);
+
+ if (expr == NULL)
+ {
+ str += "none (this shouldn't happen and is probably an error)";
+ }
+ else
+ {
+ str += expr->as_string ();
+ }
+ indent_spaces (out);
+
+ return str;
+}
+
+::std::string
+FunctionParam::as_string () const
+{
+ return param_name->as_string () + " : " + type->as_string ();
+}
+
+::std::string
+FunctionQualifiers::as_string () const
+{
+ ::std::string str;
+
+ switch (const_status)
+ {
+ case NONE:
+ // do nothing
+ break;
+ case CONST:
+ str += "const ";
+ break;
+ case ASYNC:
+ str += "async ";
+ break;
+ default:
+ return "ERROR_MARK_STRING: async-const status failure";
+ }
+
+ if (has_unsafe)
+ {
+ str += "unsafe ";
+ }
+
+ if (has_extern)
+ {
+ str += "extern";
+ if (extern_abi != "")
+ {
+ str += " \"" + extern_abi + "\"";
+ }
+ }
+
+ return str;
+}
+
+::std::string
+TraitBound::as_string () const
+{
+ ::std::string str ("TraitBound:");
+
+ str += "\n Has opening question mark: ";
+ if (opening_question_mark)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
+
+ str += "\n For lifetimes: ";
+ if (!has_for_lifetimes ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &lifetime : for_lifetimes)
+ {
+ str += "\n " + lifetime.as_string ();
+ }
+ }
+
+ str += "\n Type path: " + type_path.as_string ();
+
+ return str;
+}
+
+::std::string
+MacroMatcher::as_string () const
+{
+ ::std::string str ("Macro matcher: ");
+
+ str += "\n Delim type: ";
+
+ switch (delim_type)
+ {
+ case PARENS:
+ str += "parentheses";
+ break;
+ case SQUARE:
+ str += "square";
+ break;
+ case CURLY:
+ str += "curly";
+ break;
+ default:
+ return "ERROR_MARK_STRING - macro matcher delim";
+ }
+
+ str += "\n Matches: ";
+
+ if (matches.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &match : matches)
+ {
+ str += "\n " + match->as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+LifetimeParam::as_string () const
+{
+ ::std::string str ("LifetimeParam: ");
+
+ str += "\n Outer attribute: ";
+ if (!has_outer_attribute ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += outer_attr.as_string ();
+ }
+
+ str += "\n Lifetime: " + lifetime.as_string ();
+
+ str += "\n Lifetime bounds: ";
+ if (!has_lifetime_bounds ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &bound : lifetime_bounds)
+ {
+ str += "\n " + bound.as_string ();
+ }
+ }
+
+ return str;
+}
+
+::std::string
+MacroMatchFragment::as_string () const
+{
+ return "$" + ident + ": " + frag_spec_to_str (frag_spec);
+}
+
+::std::string
+QualifiedPathInType::as_string () const
+{
+ ::std::string str = path_type.as_string ();
+
+ for (const auto &segment : segments)
+ {
+ str += "::" + segment->as_string ();
+ }
+
+ return str;
+}
+
+::std::string
+MacroMatchRepetition::as_string () const
+{
+ ::std::string str ("Macro match repetition: ");
+
+ str += "\n Matches: ";
+ if (matches.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &match : matches)
+ {
+ str += "\n " + match->as_string ();
+ }
+ }
+
+ str += "\n Sep: ";
+ if (!has_sep ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += sep->as_string ();
+ }
+
+ str += "\n Op: ";
+ switch (op)
+ {
+ case ASTERISK:
+ str += "*";
+ break;
+ case PLUS:
+ str += "+";
+ break;
+ case QUESTION_MARK:
+ str += "?";
+ break;
+ case NONE:
+ str += "no op? shouldn't be allowed";
+ break;
+ default:
+ return "ERROR_MARK_STRING - unknown op in macro match repetition";
+ }
+
+ return str;
+}
- str += "\n Params: ";
- if (params.empty()) {
- str += "none";
- } else {
- for (const auto& param : params) {
- str += "\n " + param.as_string();
- }
- }
+::std::string
+Lifetime::as_string () const
+{
+ if (is_error ())
+ {
+ return "error lifetime";
+ }
- return str;
- }
+ switch (lifetime_type)
+ {
+ case NAMED:
+ return "'" + lifetime_name;
+ case STATIC:
+ return "'static";
+ case WILDCARD:
+ return "'_";
+ default:
+ return "ERROR-MARK-STRING: lifetime type failure";
+ }
+}
- ::std::string ClosureExprInnerTyped::as_string() const {
- ::std::string str = ClosureExpr::as_string();
+::std::string
+TypePath::as_string () const
+{
+ ::std::string str;
- str += "\n Return type: " + return_type->as_string();
+ if (has_opening_scope_resolution)
+ {
+ str = "::";
+ }
- str += "\n Body: " + expr->as_string();
-
- return str;
- }
-
- ::std::string PathPattern::as_string() const {
- ::std::string str;
-
- for (const auto& segment : segments) {
- str += segment.as_string() + "::";
- }
-
- // basically a hack - remove last two characters of string (remove final ::)
- str.erase(str.length() - 2);
-
- return str;
- }
-
- ::std::string QualifiedPathType::as_string() const {
- ::std::string str("<");
- str += type_to_invoke_on->as_string();
-
- if (has_as_clause()) {
- str += " as " + trait_path.as_string();
- }
+ for (const auto &segment : segments)
+ {
+ str += segment->as_string () + "::";
+ }
- return str + ">";
- }
+ // kinda hack - remove last 2 '::' characters
+ str.erase (str.length () - 2);
- ::std::string QualifiedPathInExpression::as_string() const {
- return path_type.as_string() + "::" + PathPattern::as_string();
- }
+ return str;
+}
- ::std::string BorrowExpr::as_string() const {
- ::std::string str("&");
+::std::string
+TypeParam::as_string () const
+{
+ ::std::string str ("TypeParam: ");
- if (double_borrow) {
- str += "&";
- }
+ str += "\n Outer attribute: ";
+ if (!has_outer_attribute ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += outer_attr.as_string ();
+ }
- if (is_mut) {
- str += "mut ";
- }
+ str += "\n Identifier: " + type_representation;
- str += main_or_left_expr->as_string();
+ str += "\n Type param bounds: ";
+ if (!has_type_param_bounds ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &bound : type_param_bounds)
+ {
+ str += "\n " + bound->as_string ();
+ }
+ }
- return str;
- }
+ str += "\n Type: ";
+ if (!has_type ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += type->as_string ();
+ }
- ::std::string ReturnExpr::as_string() const {
- ::std::string str("return ");
+ return str;
+}
- if (has_return_expr()) {
- str += return_expr->as_string();
- }
+SimplePath
+PathPattern::convert_to_simple_path (bool with_opening_scope_resolution) const
+{
+ if (!has_segments ())
+ {
+ return SimplePath::create_empty ();
+ }
- return str;
- }
+ // create vector of reserved size (to minimise reallocations)
+ ::std::vector<SimplePathSegment> simple_segments;
+ simple_segments.reserve (segments.size ());
+
+ for (const auto &segment : segments)
+ {
+ // return empty path if doesn't meet simple path segment requirements
+ if (segment.is_error () || segment.has_generic_args ()
+ || segment.as_string () == "Self")
+ {
+ return SimplePath::create_empty ();
+ }
+
+ // create segment and add to vector
+ ::std::string segment_str = segment.as_string ();
+ simple_segments.push_back (
+ SimplePathSegment (::std::move (segment_str), segment.get_locus ()));
+ }
- ::std::string GroupedExpr::as_string() const {
- ::std::string str("Grouped expr:");
+ // kind of a HACK to get locus depending on opening scope resolution
+ Location locus = Linemap::unknown_location ();
+ if (with_opening_scope_resolution)
+ {
+ locus = simple_segments[0].get_locus () - 2; // minus 2 chars for ::
+ }
+ else
+ {
+ locus = simple_segments[0].get_locus ();
+ }
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n Expr in parens: " + expr_in_parens->as_string();
-
- return str;
- }
-
- ::std::string RangeToExpr::as_string() const {
- return ".." + to->as_string();
- }
+ return SimplePath (::std::move (simple_segments),
+ with_opening_scope_resolution, locus);
+}
- ::std::string ContinueExpr::as_string() const {
- ::std::string str("continue ");
+SimplePath
+TypePath::as_simple_path () const
+{
+ if (segments.empty ())
+ {
+ return SimplePath::create_empty ();
+ }
- if (has_label()) {
- str += label.as_string();
- }
+ // create vector of reserved size (to minimise reallocations)
+ ::std::vector<SimplePathSegment> simple_segments;
+ simple_segments.reserve (segments.size ());
+
+ for (const auto &segment : segments)
+ {
+ // return empty path if doesn't meet simple path segment requirements
+ if (segment == NULL || segment->is_error () || !segment->is_ident_only ()
+ || segment->as_string () == "Self")
+ {
+ return SimplePath::create_empty ();
+ }
+
+ // create segment and add to vector
+ ::std::string segment_str = segment->as_string ();
+ simple_segments.push_back (
+ SimplePathSegment (::std::move (segment_str), segment->get_locus ()));
+ }
- return str;
- }
+ return SimplePath (::std::move (simple_segments),
+ has_opening_scope_resolution, locus);
+}
- ::std::string NegationExpr::as_string() const {
- ::std::string str;
+::std::string
+PathExprSegment::as_string () const
+{
+ ::std::string ident_str = segment_name.as_string ();
+ if (has_generic_args ())
+ {
+ ident_str += "::<" + generic_args.as_string () + ">";
+ }
- switch (negation_type) {
- case NEGATE:
- str = "-";
- break;
- case NOT:
- str = "!";
- break;
- default:
- return "ERROR_MARK_STRING - negation expr";
- }
-
- str += main_or_left_expr->as_string();
+ return ident_str;
+}
- return str;
- }
+::std::string
+GenericArgs::as_string () const
+{
+ ::std::string args;
+
+ // lifetime args
+ if (!lifetime_args.empty ())
+ {
+ auto i = lifetime_args.begin ();
+ auto e = lifetime_args.end ();
+
+ for (; i != e; i++)
+ {
+ args += (*i).as_string ();
+ if (e != i + 1)
+ args += ", ";
+ }
+ }
- ::std::string RangeFromExpr::as_string() const {
- return from->as_string() + "..";
- }
+ // type args
+ if (!type_args.empty ())
+ {
+ auto i = type_args.begin ();
+ auto e = type_args.end ();
+
+ for (; i != e; i++)
+ {
+ args += (*i)->as_string ();
+ if (e != i + 1)
+ args += ", ";
+ }
+ }
- ::std::string RangeFullExpr::as_string() const {
- return "..";
- }
-
- ::std::string ArrayIndexExpr::as_string() const {
- return array_expr->as_string() + "[" + index_expr->as_string() + "]";
- }
-
- ::std::string AssignmentExpr::as_string() const {
- ::std::string str("AssignmentExpr: ");
-
- if (main_or_left_expr == NULL || right_expr == NULL) {
- str += "error (either or both expressions are null)";
- } else {
- // left expr
- str += "\n left: " + main_or_left_expr->as_string();
-
- // right expr
- str += "\n right: " + right_expr->as_string();
- }
-
- return str;
- }
-
- ::std::string AsyncBlockExpr::as_string() const {
- ::std::string str = "AsyncBlockExpr: ";
-
- // get outer attributes
- str += "\n " + Expr::as_string();
-
- str += "\n Has move: ";
- str += has_move ? "true" : "false";
-
- return str + "\n" + block_expr->as_string();
- }
-
- ::std::string ComparisonExpr::as_string() const {
- ::std::string str(main_or_left_expr->as_string());
-
- switch (expr_type) {
- case EQUAL:
- str += " == ";
- break;
- case NOT_EQUAL:
- str += " != ";
- break;
- case GREATER_THAN:
- str += " > ";
- break;
- case LESS_THAN:
- str += " < ";
- break;
- case GREATER_OR_EQUAL:
- str += " >= ";
- break;
- case LESS_OR_EQUAL:
- str += " <= ";
- break;
- default:
- return "ERROR_MARK_STRING - comparison expr";
- }
+ // binding args
+ if (!binding_args.empty ())
+ {
+ auto i = binding_args.begin ();
+ auto e = binding_args.end ();
+
+ for (; i != e; i++)
+ {
+ args += (*i).as_string ();
+ if (e != i + 1)
+ args += ", ";
+ }
+ }
- str += right_expr->as_string();
+ return args;
+}
- return str;
- }
+::std::string
+GenericArgsBinding::as_string () const
+{
+ return identifier + " = " + type->as_string ();
+}
- ::std::string MethodCallExpr::as_string() const {
- ::std::string str("MethodCallExpr: \n Object (receiver) expr: ");
+::std::string
+ForLoopExpr::as_string () const
+{
+ ::std::string str ("ForLoopExpr: ");
- str += receiver->as_string();
+ str += "\n Label: ";
+ if (!has_loop_label ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += loop_label.as_string ();
+ }
- str += "\n Method path segment: \n";
+ str += "\n Pattern: " + pattern->as_string ();
- str += method_name.as_string();
+ str += "\n Iterator expr: " + iterator_expr->as_string ();
- str += "\n Call params:";
- if (params.empty()) {
- str += "none";
- } else {
- for (const auto& param : params) {
- if (param == NULL) {
- return "ERROR_MARK_STRING - method call expr param is null";
- }
+ str += "\n Loop block: " + loop_block->as_string ();
- str += "\n " + param->as_string();
- }
- }
+ return str;
+}
- return str;
- }
+::std::string
+RangePattern::as_string () const
+{
+ if (has_ellipsis_syntax)
+ {
+ return lower->as_string () + "..." + upper->as_string ();
+ }
+ else
+ {
+ return lower->as_string () + "..=" + upper->as_string ();
+ }
+}
- ::std::string TupleIndexExpr::as_string() const {
- return tuple_expr->as_string() + "." + ::std::to_string(tuple_index);
- }
+::std::string
+RangePatternBoundLiteral::as_string () const
+{
+ ::std::string str;
- ::std::string DereferenceExpr::as_string() const {
- return "*" + main_or_left_expr->as_string();
- }
+ if (has_minus)
+ {
+ str += "-";
+ }
- ::std::string FieldAccessExpr::as_string() const {
- return receiver->as_string() + "." + field;
- }
+ str += literal.as_string ();
- ::std::string LazyBooleanExpr::as_string() const {
- ::std::string str(main_or_left_expr->as_string());
+ return str;
+}
- switch (expr_type) {
- case LOGICAL_OR:
- str += " || ";
- break;
- case LOGICAL_AND:
- str += " && ";
- break;
- default:
- return "ERROR_MARK_STRING - lazy boolean expr out of bounds";
- }
+::std::string
+SlicePattern::as_string () const
+{
+ ::std::string str ("SlicePattern: ");
- str += right_expr->as_string();
+ for (const auto &pattern : items)
+ {
+ str += "\n " + pattern->as_string ();
+ }
- return str;
- }
+ return str;
+}
- ::std::string RangeFromToExpr::as_string() const {
- return from->as_string() + ".." + to->as_string();
- }
+::std::string
+TuplePatternItemsMultiple::as_string () const
+{
+ ::std::string str;
- ::std::string RangeToInclExpr::as_string() const {
- return "..=" + to->as_string();
- }
+ for (const auto &pattern : patterns)
+ {
+ str += "\n " + pattern->as_string ();
+ }
- ::std::string UnsafeBlockExpr::as_string() const {
- ::std::string str = "UnsafeBlockExpr: ";
+ return str;
+}
- // get outer attributes
- str += "\n " + Expr::as_string();
+::std::string
+TuplePatternItemsRanged::as_string () const
+{
+ ::std::string str;
- return str + "\n" + expr->as_string();
- }
+ str += "\n Lower patterns: ";
+ if (lower_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &lower : lower_patterns)
+ {
+ str += "\n " + lower->as_string ();
+ }
+ }
- ::std::string ClosureExprInner::as_string() const {
- ::std::string str = ClosureExpr::as_string();
+ str += "\n Upper patterns: ";
+ if (upper_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &upper : upper_patterns)
+ {
+ str += "\n " + upper->as_string ();
+ }
+ }
- str += "\n Expression: " + closure_inner->as_string();
+ return str;
+}
- return str;
- }
+::std::string
+TuplePattern::as_string () const
+{
+ return "TuplePattern: " + items->as_string ();
+}
- ::std::string IfExpr::as_string() const {
- ::std::string str("IfExpr: ");
+::std::string
+StructPatternField::as_string () const
+{
+ // outer attributes
+ ::std::string str ("Outer attributes: ");
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- str += "\n Condition expr: " + condition->as_string();
+ return str;
+}
- str += "\n If block expr: " + if_block->as_string();
+::std::string
+StructPatternFieldIdent::as_string () const
+{
+ ::std::string str = StructPatternField::as_string ();
- return str;
- }
+ str += "\n";
- ::std::string IfExprConseqElse::as_string() const {
- ::std::string str = IfExpr::as_string();
+ if (has_ref)
+ {
+ str += "ref ";
+ }
- str += "\n Else block expr: " + else_block->as_string();
+ if (has_mut)
+ {
+ str += "mut ";
+ }
- return str;
- }
+ str += ident;
- ::std::string IfExprConseqIf::as_string() const {
- ::std::string str = IfExpr::as_string();
+ return str;
+}
- str += "\n Else if expr: \n " + if_expr->as_string();
+::std::string
+StructPatternFieldTuplePat::as_string () const
+{
+ ::std::string str = StructPatternField::as_string ();
- return str;
- }
+ str += "\n";
- ::std::string IfExprConseqIfLet::as_string() const {
- ::std::string str = IfExpr::as_string();
+ str += ::std::to_string (index) + " : " + tuple_pattern->as_string ();
- str += "\n Else if let expr: \n " + if_let_expr->as_string();
+ return str;
+}
- return str;
- }
+::std::string
+StructPatternFieldIdentPat::as_string () const
+{
+ ::std::string str = StructPatternField::as_string ();
- ::std::string IfLetExpr::as_string() const {
- ::std::string str("IfLetExpr: ");
+ str += "\n";
- str += "\n Condition match arm patterns: ";
- if (match_arm_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& pattern : match_arm_patterns) {
- str += "\n " + pattern->as_string();
- }
- }
+ str += ident + " : " + ident_pattern->as_string ();
- str += "\n Scrutinee expr: " + value->as_string();
+ return str;
+}
- str += "\n If let block expr: " + if_block->as_string();
-
- return str;
- }
-
- ::std::string IfLetExprConseqElse::as_string() const {
- ::std::string str = IfLetExpr::as_string();
-
- str += "\n Else block expr: " + else_block->as_string();
-
- return str;
- }
-
- ::std::string IfLetExprConseqIf::as_string() const {
- ::std::string str = IfLetExpr::as_string();
-
- str += "\n Else if expr: \n " + if_expr->as_string();
-
- return str;
- }
-
- ::std::string IfLetExprConseqIfLet::as_string() const {
- ::std::string str = IfLetExpr::as_string();
-
- str += "\n Else if let expr: \n " + if_let_expr->as_string();
-
- return str;
- }
-
- ::std::string RangeFromToInclExpr::as_string() const {
- return from->as_string() + "..=" + to->as_string();
- }
-
- ::std::string ErrorPropagationExpr::as_string() const {
- return main_or_left_expr->as_string() + "?";
- }
-
- ::std::string CompoundAssignmentExpr::as_string() const {
- ::std::string operator_str;
- operator_str.reserve(1);
-
- // get operator string
- switch (expr_type) {
- case ADD:
- operator_str = "+";
- break;
- case SUBTRACT:
- operator_str = "-";
- break;
- case MULTIPLY:
- operator_str = "*";
- break;
- case DIVIDE:
- operator_str = "/";
- break;
- case MODULUS:
- operator_str = "%";
- break;
- case BITWISE_AND:
- operator_str = "&";
- break;
- case BITWISE_OR:
- operator_str = "|";
- break;
- case BITWISE_XOR:
- operator_str = "^";
- break;
- case LEFT_SHIFT:
- operator_str = "<<";
- break;
- case RIGHT_SHIFT:
- operator_str = ">>";
- break;
- default:
- operator_str = "invalid operator. wtf";
- break;
- }
-
- operator_str += "=";
-
- ::std::string str("CompoundAssignmentExpr: ");
- if (main_or_left_expr == NULL || right_expr == NULL) {
- str += "error. this is probably a parsing failure.";
- } else {
- str += "\n left: " + main_or_left_expr->as_string();
- str += "\n right: " + right_expr->as_string();
- str += "\n operator: " + operator_str;
- }
-
- return str;
- }
-
- ::std::string ArithmeticOrLogicalExpr::as_string() const {
- ::std::string operator_str;
- operator_str.reserve(1);
-
- // get operator string
- switch (expr_type) {
- case ADD:
- operator_str = "+";
- break;
- case SUBTRACT:
- operator_str = "-";
- break;
- case MULTIPLY:
- operator_str = "*";
- break;
- case DIVIDE:
- operator_str = "/";
- break;
- case MODULUS:
- operator_str = "%";
- break;
- case BITWISE_AND:
- operator_str = "&";
- break;
- case BITWISE_OR:
- operator_str = "|";
- break;
- case BITWISE_XOR:
- operator_str = "^";
- break;
- case LEFT_SHIFT:
- operator_str = "<<";
- break;
- case RIGHT_SHIFT:
- operator_str = ">>";
- break;
- default:
- operator_str = "invalid operator. wtf";
- break;
- }
-
- ::std::string str("ArithmeticOrLogicalExpr: ");
- if (main_or_left_expr == NULL || right_expr == NULL) {
- str += "error. this is probably a parsing failure.";
- } else {
- str += "\n left: " + main_or_left_expr->as_string();
- str += "\n right: " + right_expr->as_string();
- str += "\n operator: " + operator_str;
- }
-
- return str;
- }
-
- ::std::string CallExpr::as_string() const {
- ::std::string str("CallExpr: \n Function expr: ");
-
- str += function->as_string();
-
- str += "\n Call params:";
- if (!has_params()) {
- str += "none";
- } else {
- for (const auto& param : params) {
- if (param == NULL) {
- return "ERROR_MARK_STRING - call expr param is null";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string WhileLoopExpr::as_string() const {
- ::std::string str("WhileLoopExpr: ");
-
- str += "\n Label: ";
- if (!has_loop_label()) {
- str += "none";
- } else {
- str += loop_label.as_string();
- }
-
- str += "\n Conditional expr: " + condition->as_string();
-
- str += "\n Loop block: " + loop_block->as_string();
-
- return str;
- }
-
- ::std::string WhileLetLoopExpr::as_string() const {
- ::std::string str("WhileLetLoopExpr: ");
-
- str += "\n Label: ";
- if (!has_loop_label()) {
- str += "none";
- } else {
- str += loop_label.as_string();
- }
-
- str += "\n Match arm patterns: ";
- if (match_arm_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& pattern : match_arm_patterns) {
- str += "\n " + pattern->as_string();
- }
- }
-
- str += "\n Scrutinee expr: " + condition->as_string();
-
- str += "\n Loop block: " + loop_block->as_string();
-
- return str;
- }
-
- ::std::string LoopExpr::as_string() const {
- ::std::string str("LoopExpr: (infinite loop)");
-
- str += "\n Label: ";
- if (!has_loop_label()) {
- str += "none";
- } else {
- str += loop_label.as_string();
- }
-
- str += "\n Loop block: " + loop_block->as_string();
-
- return str;
- }
-
- ::std::string ArrayExpr::as_string() const {
- ::std::string str("ArrayExpr:");
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n Array elems: ";
- if (!has_array_elems()) {
- str += "none";
- } else {
- str += internal_elements->as_string();
- }
-
- return str;
- }
-
- ::std::string AwaitExpr::as_string() const {
- return awaited_expr->as_string() + ".await";
- }
-
- ::std::string BreakExpr::as_string() const {
- ::std::string str("break ");
-
- if (has_label()) {
- str += label.as_string() + " ";
- }
-
- if (has_break_expr()) {
- str += break_expr->as_string();
- }
-
- return str;
- }
-
- ::std::string LoopLabel::as_string() const {
- return label.as_string() + ": (label) ";
- }
-
- ::std::string MatchArm::as_string() const {
- // outer attributes
- ::std::string str = "Outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\nPatterns: ";
- if (match_arm_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& pattern : match_arm_patterns) {
- str += "\n " + pattern->as_string();
- }
- }
-
- str += "\nGuard expr: ";
- if (!has_match_arm_guard()) {
- str += "none";
- } else {
- str += guard_expr->as_string();
- }
-
- return str;
- }
-
- ::std::string MatchCase::as_string() const {
- ::std::string str("MatchCase: (match arm) ");
-
- str += "\n Match arm matcher: \n" + arm.as_string();
-
- return str;
- }
-
- ::std::string MatchCaseBlockExpr::as_string() const {
- ::std::string str = MatchCase::as_string();
-
- str += "\n Block expr: " + block_expr->as_string();
-
- return str;
- }
-
- ::std::string MatchCaseExpr::as_string() const {
- ::std::string str = MatchCase::as_string();
-
- str += "\n Expr: " + expr->as_string();
-
- return str;
- }
-
- ::std::string MatchExpr::as_string() const {
- ::std::string str("MatchExpr:");
-
- str += "\n Scrutinee expr: " + branch_value->as_string();
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- // match arms
- str += "\n Match arms: ";
- if (match_arms.empty()) {
- str += "none";
- } else {
- for (const auto& arm : match_arms) {
- str += "\n " + arm->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string TupleExpr::as_string() const {
- ::std::string str("TupleExpr:");
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n Tuple elements: ";
- if (tuple_elems.empty()) {
- str += "none";
- } else {
- for (const auto& elem : tuple_elems) {
- str += "\n " + elem->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string ExprStmtWithoutBlock::as_string() const {
- ::std::string str("ExprStmtWithoutBlock: \n ");
-
- if (expr == NULL) {
- str += "none (this shouldn't happen and is probably an error)";
- } else {
- str += expr->as_string();
- }
-
- return str;
- }
-
- ::std::string FunctionParam::as_string() const {
- return param_name->as_string() + " : " + type->as_string();
- }
-
- ::std::string FunctionQualifiers::as_string() const {
- ::std::string str;
-
- switch (const_status) {
- case NONE:
- // do nothing
- break;
- case CONST:
- str += "const ";
- break;
- case ASYNC:
- str += "async ";
- break;
- default:
- return "ERROR_MARK_STRING: async-const status failure";
- }
-
- if (has_unsafe) {
- str += "unsafe ";
- }
-
- if (has_extern) {
- str += "extern";
- if (extern_abi != "") {
- str += " \"" + extern_abi + "\"";
- }
- }
-
- return str;
- }
-
- ::std::string TraitBound::as_string() const {
- ::std::string str("TraitBound:");
-
- str += "\n Has opening question mark: ";
- if (opening_question_mark) {
- str += "true";
- } else {
- str += "false";
- }
-
- str += "\n For lifetimes: ";
- if (!has_for_lifetimes()) {
- str += "none";
- } else {
- for (const auto& lifetime : for_lifetimes) {
- str += "\n " + lifetime.as_string();
- }
- }
-
- str += "\n Type path: " + type_path.as_string();
-
- return str;
- }
-
- ::std::string MacroMatcher::as_string() const {
- ::std::string str("Macro matcher: ");
-
- str += "\n Delim type: ";
-
- switch (delim_type) {
- case PARENS:
- str += "parentheses";
- break;
- case SQUARE:
- str += "square";
- break;
- case CURLY:
- str += "curly";
- break;
- default:
- return "ERROR_MARK_STRING - macro matcher delim";
- }
-
- str += "\n Matches: ";
-
- if (matches.empty()) {
- str += "none";
- } else {
- for (const auto& match : matches) {
- str += "\n " + match->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string LifetimeParam::as_string() const {
- ::std::string str("LifetimeParam: ");
-
- str += "\n Outer attribute: ";
- if (!has_outer_attribute()) {
- str += "none";
- } else {
- str += outer_attr.as_string();
- }
-
- str += "\n Lifetime: " + lifetime.as_string();
-
- str += "\n Lifetime bounds: ";
- if (!has_lifetime_bounds()) {
- str += "none";
- } else {
- for (const auto& bound : lifetime_bounds) {
- str += "\n " + bound.as_string();
- }
- }
-
- return str;
- }
-
- ::std::string MacroMatchFragment::as_string() const {
- return "$" + ident + ": " + frag_spec_to_str(frag_spec);
- }
-
- ::std::string QualifiedPathInType::as_string() const {
- ::std::string str = path_type.as_string();
-
- for (const auto& segment : segments) {
- str += "::" + segment->as_string();
- }
-
- return str;
- }
-
- ::std::string MacroMatchRepetition::as_string() const {
- ::std::string str("Macro match repetition: ");
-
- str += "\n Matches: ";
- if (matches.empty()) {
- str += "none";
- } else {
- for (const auto& match : matches) {
- str += "\n " + match->as_string();
- }
- }
-
- str += "\n Sep: ";
- if (!has_sep()) {
- str += "none";
- } else {
- str += sep->as_string();
- }
-
- str += "\n Op: ";
- switch (op) {
- case ASTERISK:
- str += "*";
- break;
- case PLUS:
- str += "+";
- break;
- case QUESTION_MARK:
- str += "?";
- break;
- case NONE:
- str += "no op? shouldn't be allowed";
- break;
- default:
- return "ERROR_MARK_STRING - unknown op in macro match repetition";
- }
-
- return str;
- }
-
- ::std::string Lifetime::as_string() const {
- if (is_error()) {
- return "error lifetime";
- }
-
- switch (lifetime_type) {
- case NAMED:
- return "'" + lifetime_name;
- case STATIC:
- return "'static";
- case WILDCARD:
- return "'_";
- default:
- return "ERROR-MARK-STRING: lifetime type failure";
- }
- }
-
- ::std::string TypePath::as_string() const {
- ::std::string str;
-
- if (has_opening_scope_resolution) {
- str = "::";
- }
-
- for (const auto& segment : segments) {
- str += segment->as_string() + "::";
- }
-
- // kinda hack - remove last 2 '::' characters
- str.erase(str.length() - 2);
-
- return str;
- }
-
- ::std::string TypeParam::as_string() const {
- ::std::string str("TypeParam: ");
-
- str += "\n Outer attribute: ";
- if (!has_outer_attribute()) {
- str += "none";
- } else {
- str += outer_attr.as_string();
- }
-
- str += "\n Identifier: " + type_representation;
-
- str += "\n Type param bounds: ";
- if (!has_type_param_bounds()) {
- str += "none";
- } else {
- for (const auto& bound : type_param_bounds) {
- str += "\n " + bound->as_string();
- }
- }
-
- str += "\n Type: ";
- if (!has_type()) {
- str += "none";
- } else {
- str += type->as_string();
- }
-
- return str;
- }
-
- SimplePath PathPattern::convert_to_simple_path(bool with_opening_scope_resolution) const {
- if (!has_segments()) {
- return SimplePath::create_empty();
- }
-
- // create vector of reserved size (to minimise reallocations)
- ::std::vector<SimplePathSegment> simple_segments;
- simple_segments.reserve(segments.size());
-
- for (const auto& segment : segments) {
- // return empty path if doesn't meet simple path segment requirements
- if (segment.is_error() || segment.has_generic_args()
- || segment.as_string() == "Self") {
- return SimplePath::create_empty();
- }
-
- // create segment and add to vector
- ::std::string segment_str = segment.as_string();
- simple_segments.push_back(
- SimplePathSegment(::std::move(segment_str), segment.get_locus()));
- }
-
- // kind of a HACK to get locus depending on opening scope resolution
- Location locus = Linemap::unknown_location();
- if (with_opening_scope_resolution) {
- locus = simple_segments[0].get_locus() - 2; // minus 2 chars for ::
- } else {
- locus = simple_segments[0].get_locus();
- }
-
- return SimplePath(::std::move(simple_segments), with_opening_scope_resolution, locus);
- }
-
- SimplePath TypePath::as_simple_path() const {
- if (segments.empty()) {
- return SimplePath::create_empty();
- }
-
- // create vector of reserved size (to minimise reallocations)
- ::std::vector<SimplePathSegment> simple_segments;
- simple_segments.reserve(segments.size());
-
- for (const auto& segment : segments) {
- // return empty path if doesn't meet simple path segment requirements
- if (segment == NULL || segment->is_error() || !segment->is_ident_only()
- || segment->as_string() == "Self") {
- return SimplePath::create_empty();
- }
-
- // create segment and add to vector
- ::std::string segment_str = segment->as_string();
- simple_segments.push_back(
- SimplePathSegment(::std::move(segment_str), segment->get_locus()));
- }
-
- return SimplePath(::std::move(simple_segments), has_opening_scope_resolution, locus);
- }
-
- ::std::string PathExprSegment::as_string() const {
- ::std::string ident_str = segment_name.as_string();
- if (has_generic_args()) {
- ident_str += "::<" + generic_args.as_string() + ">";
- }
-
- return ident_str;
- }
-
- ::std::string GenericArgs::as_string() const {
- ::std::string args;
-
- // lifetime args
- if (!lifetime_args.empty()) {
- for (const auto& lifetime_arg : lifetime_args) {
- args += lifetime_arg.as_string() + ", ";
- }
- }
-
- // type args
- if (!type_args.empty()) {
- for (const auto& type_arg : type_args) {
- args += type_arg->as_string() + ", ";
- }
- }
-
- // binding args
- if (!binding_args.empty()) {
- for (const auto& binding_arg : binding_args) {
- args += binding_arg.as_string() + ", ";
- }
- }
-
- return args;
- }
-
- ::std::string GenericArgsBinding::as_string() const {
- return identifier + " = " + type->as_string();
- }
-
- ::std::string ForLoopExpr::as_string() const {
- ::std::string str("ForLoopExpr: ");
-
- str += "\n Label: ";
- if (!has_loop_label()) {
- str += "none";
- } else {
- str += loop_label.as_string();
- }
-
- str += "\n Pattern: " + pattern->as_string();
-
- str += "\n Iterator expr: " + iterator_expr->as_string();
-
- str += "\n Loop block: " + loop_block->as_string();
-
- return str;
- }
-
- ::std::string RangePattern::as_string() const {
- if (has_ellipsis_syntax) {
- return lower->as_string() + "..." + upper->as_string();
- } else {
- return lower->as_string() + "..=" + upper->as_string();
- }
- }
-
- ::std::string RangePatternBoundLiteral::as_string() const {
- ::std::string str;
-
- if (has_minus) {
- str += "-";
- }
-
- str += literal.as_string();
-
- return str;
- }
+::std::string
+StructPatternElements::as_string () const
+{
+ ::std::string str ("\n Fields: ");
- ::std::string SlicePattern::as_string() const {
- ::std::string str("SlicePattern: ");
+ if (!has_struct_pattern_fields ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &field : fields)
+ {
+ str += "\n " + field->as_string ();
+ }
+ }
- for (const auto& pattern : items) {
- str += "\n " + pattern->as_string();
- }
+ str += "\n Etc: ";
+ if (has_struct_pattern_etc)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
- return str;
- }
+ return str;
+}
- ::std::string TuplePatternItemsMultiple::as_string() const {
- ::std::string str;
+::std::string
+StructPattern::as_string () const
+{
+ ::std::string str ("StructPattern: \n Path: ");
- for (const auto& pattern : patterns) {
- str += "\n " + pattern->as_string();
- }
+ str += path.as_string ();
- return str;
- }
+ str += "\n Struct pattern elems: ";
+ if (!has_struct_pattern_elems ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += elems.as_string ();
+ }
- ::std::string TuplePatternItemsRanged::as_string() const {
- ::std::string str;
+ return str;
+}
- str += "\n Lower patterns: ";
- if (lower_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& lower : lower_patterns) {
- str += "\n " + lower->as_string();
- }
- }
+::std::string
+LiteralPattern::as_string () const
+{
+ ::std::string str;
- str += "\n Upper patterns: ";
- if (upper_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& upper : upper_patterns) {
- str += "\n " + upper->as_string();
- }
- }
+ if (has_minus)
+ {
+ str += "-";
+ }
- return str;
- }
+ return str + lit.as_string ();
+}
- ::std::string TuplePattern::as_string() const {
- return "TuplePattern: " + items->as_string();
- }
+::std::string
+ReferencePattern::as_string () const
+{
+ ::std::string str ("&");
- ::std::string StructPatternField::as_string() const {
- // outer attributes
- ::std::string str("Outer attributes: ");
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
+ if (has_two_amps)
+ {
+ str += "&";
+ }
- return str;
- }
+ if (is_mut)
+ {
+ str += "mut ";
+ }
- ::std::string StructPatternFieldIdent::as_string() const {
- ::std::string str = StructPatternField::as_string();
+ str += pattern->as_string ();
- str += "\n";
+ return str;
+}
- if (has_ref) {
- str += "ref ";
- }
+::std::string
+IdentifierPattern::as_string () const
+{
+ ::std::string str;
- if (has_mut) {
- str += "mut ";
- }
+ if (is_ref)
+ {
+ str += "ref ";
+ }
- str += ident;
+ if (is_mut)
+ {
+ str += "mut ";
+ }
- return str;
- }
+ str += variable_ident;
- ::std::string StructPatternFieldTuplePat::as_string() const {
- ::std::string str = StructPatternField::as_string();
+ if (has_pattern_to_bind ())
+ {
+ str += " @ " + to_bind->as_string ();
+ }
- str += "\n";
+ return str;
+}
- str += ::std::to_string(index) + " : " + tuple_pattern->as_string();
+::std::string
+TupleStructItemsNoRange::as_string () const
+{
+ ::std::string str;
- return str;
- }
+ for (const auto &pattern : patterns)
+ {
+ str += "\n " + pattern->as_string ();
+ }
- ::std::string StructPatternFieldIdentPat::as_string() const {
- ::std::string str = StructPatternField::as_string();
+ return str;
+}
- str += "\n";
+::std::string
+TupleStructItemsRange::as_string () const
+{
+ ::std::string str ("\n Lower patterns: ");
- str += ident + " : " + ident_pattern->as_string();
+ if (lower_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &lower : lower_patterns)
+ {
+ str += "\n " + lower->as_string ();
+ }
+ }
- return str;
- }
+ str += "\n Upper patterns: ";
+ if (upper_patterns.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &upper : upper_patterns)
+ {
+ str += "\n " + upper->as_string ();
+ }
+ }
- ::std::string StructPatternElements::as_string() const {
- ::std::string str("\n Fields: ");
+ return str;
+}
- if (!has_struct_pattern_fields()) {
- str += "none";
- } else {
- for (const auto& field : fields) {
- str += "\n " + field->as_string();
- }
- }
+::std::string
+TupleStructPattern::as_string () const
+{
+ ::std::string str ("TupleStructPattern: \n Path: ");
- str += "\n Etc: ";
- if (has_struct_pattern_etc) {
- str += "true";
- } else {
- str += "false";
- }
+ str += path.as_string ();
- return str;
- }
+ str += "\n Tuple struct items: " + items->as_string ();
- ::std::string StructPattern::as_string() const {
- ::std::string str("StructPattern: \n Path: ");
+ return str;
+}
- str += path.as_string();
+::std::string
+LetStmt::as_string () const
+{
+ // outer attributes
+ ::std::string str = "Outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- str += "\n Struct pattern elems: ";
- if (!has_struct_pattern_elems()) {
- str += "none";
- } else {
- str += elems.as_string();
- }
+ str += "\nlet " + variables_pattern->as_string ();
- return str;
- }
+ if (has_type ())
+ {
+ str += " : " + type->as_string ();
+ }
- ::std::string LiteralPattern::as_string() const {
- ::std::string str;
+ if (has_init_expr ())
+ {
+ str += " = " + init_expr->as_string ();
+ }
- if (has_minus) {
- str += "-";
- }
+ return str;
+}
- return str + lit.as_string();
- }
+// Used to get outer attributes for expressions.
+::std::string
+Expr::as_string () const
+{
+ // outer attributes
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- ::std::string ReferencePattern::as_string() const {
- ::std::string str("&");
+ return str;
+}
- if (has_two_amps) {
- str += "&";
- }
+// hopefully definition here will prevent circular dependency issue
+TraitBound *
+TypePath::to_trait_bound (bool in_parens) const
+{
+ // create clone FIXME is this required? or is copy constructor automatically
+ // called?
+ TypePath copy (*this);
+ return new TraitBound (::std::move (copy), copy.get_locus (), in_parens);
+}
- if (is_mut) {
- str += "mut ";
- }
+::std::string
+InferredType::as_string () const
+{
+ return "_ (inferred)";
+}
- str += pattern->as_string();
+::std::string
+TypeCastExpr::as_string () const
+{
+ return main_or_left_expr->as_string () + " as "
+ + type_to_convert_to->as_string ();
+}
- return str;
- }
+::std::string
+ImplTraitType::as_string () const
+{
+ ::std::string str ("ImplTraitType: \n TypeParamBounds: ");
- ::std::string IdentifierPattern::as_string() const {
- ::std::string str;
+ if (type_param_bounds.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &bound : type_param_bounds)
+ {
+ str += "\n " + bound->as_string ();
+ }
+ }
- if (is_ref) {
- str += "ref ";
- }
+ return str;
+}
- if (is_mut) {
- str += "mut ";
- }
+::std::string
+ReferenceType::as_string () const
+{
+ ::std::string str ("&");
- str += variable_ident;
+ if (has_lifetime ())
+ {
+ str += lifetime.as_string () + " ";
+ }
- if (has_pattern_to_bind()) {
- str += " @ " + to_bind->as_string();
- }
+ if (has_mut)
+ {
+ str += "mut ";
+ }
- return str;
- }
+ str += type->as_string ();
- ::std::string TupleStructItemsNoRange::as_string() const {
- ::std::string str;
+ return str;
+}
- for (const auto& pattern : patterns) {
- str += "\n " + pattern->as_string();
- }
-
- return str;
- }
-
- ::std::string TupleStructItemsRange::as_string() const {
- ::std::string str("\n Lower patterns: ");
-
- if (lower_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& lower : lower_patterns) {
- str += "\n " + lower->as_string();
- }
- }
-
- str += "\n Upper patterns: ";
- if (upper_patterns.empty()) {
- str += "none";
- } else {
- for (const auto& upper : upper_patterns) {
- str += "\n " + upper->as_string();
- }
- }
-
- return str;
- }
+::std::string
+RawPointerType::as_string () const
+{
+ ::std::string str ("*");
+
+ switch (pointer_type)
+ {
+ case MUT:
+ str += "mut ";
+ break;
+ case CONST:
+ str += "const ";
+ break;
+ default:
+ return "ERROR_MARK_STRING - unknown pointer type in raw pointer type";
+ }
- ::std::string TupleStructPattern::as_string() const {
- ::std::string str("TupleStructPattern: \n Path: ");
+ str += type->as_string ();
- str += path.as_string();
-
- str += "\n Tuple struct items: " + items->as_string();
-
- return str;
- }
-
- ::std::string LetStmt::as_string() const {
- // outer attributes
- ::std::string str = "Outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\nlet " + variables_pattern->as_string();
-
- if (has_type()) {
- str += " : " + type->as_string();
- }
-
- if (has_init_expr()) {
- str += " = " + init_expr->as_string();
- }
-
- return str;
- }
-
- // Used to get outer attributes for expressions.
- ::std::string Expr::as_string() const {
- // outer attributes
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- return str;
- }
-
- // hopefully definition here will prevent circular dependency issue
- TraitBound* TypePath::to_trait_bound(bool in_parens) const {
- // create clone FIXME is this required? or is copy constructor automatically called?
- TypePath copy(*this);
- return new TraitBound(::std::move(copy), copy.get_locus(), in_parens);
- }
-
- ::std::string InferredType::as_string() const {
- return "_ (inferred)";
- }
-
- ::std::string TypeCastExpr::as_string() const {
- return main_or_left_expr->as_string() + " as " + type_to_convert_to->as_string();
- }
-
- ::std::string ImplTraitType::as_string() const {
- ::std::string str("ImplTraitType: \n TypeParamBounds: ");
-
- if (type_param_bounds.empty()) {
- str += "none";
- } else {
- for (const auto& bound : type_param_bounds) {
- str += "\n " + bound->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string ReferenceType::as_string() const {
- ::std::string str("&");
-
- if (has_lifetime()) {
- str += lifetime.as_string() + " ";
- }
-
- if (has_mut) {
- str += "mut ";
- }
-
- str += type->as_string();
-
- return str;
- }
-
- ::std::string RawPointerType::as_string() const {
- ::std::string str("*");
-
- switch (pointer_type) {
- case MUT:
- str += "mut ";
- break;
- case CONST:
- str += "const ";
- break;
- default:
- return "ERROR_MARK_STRING - unknown pointer type in raw pointer type";
- }
-
- str += type->as_string();
-
- return str;
- }
-
- ::std::string TraitObjectType::as_string() const {
- ::std::string str("TraitObjectType: \n Has dyn dispatch: ");
-
- if (has_dyn) {
- str += "true";
- } else {
- str += "false";
- }
-
- str += "\n TypeParamBounds: ";
- if (type_param_bounds.empty()) {
- str += "none";
- } else {
- for (const auto& bound : type_param_bounds) {
- str += "\n " + bound->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string BareFunctionType::as_string() const {
- ::std::string str("BareFunctionType: \n For lifetimes: ");
-
- if (!has_for_lifetimes()) {
- str += "none";
- } else {
- for (const auto& for_lifetime : for_lifetimes) {
- str += "\n " + for_lifetime.as_string();
- }
- }
-
- str += "\n Qualifiers: " + function_qualifiers.as_string();
-
- str += "\n Params: ";
- if (params.empty()) {
- str += "none";
- } else {
- for (const auto& param : params) {
- str += "\n " + param.as_string();
- }
- }
+ return str;
+}
- str += "\n Is variadic: ";
- if (is_variadic) {
- str += "true";
- } else {
- str += "false";
- }
-
- str += "\n Return type: ";
- if (!has_return_type()) {
- str += "none (void)";
- } else {
- str += return_type->as_string();
- }
-
- return str;
- }
-
- ::std::string ImplTraitTypeOneBound::as_string() const {
- ::std::string str("ImplTraitTypeOneBound: \n TraitBound: ");
-
- return str + trait_bound.as_string();
- }
-
- ::std::string TypePathSegmentGeneric::as_string() const {
- return TypePathSegment::as_string() + "<" + generic_args.as_string() + ">";
- }
-
- ::std::string TraitObjectTypeOneBound::as_string() const {
- ::std::string str("TraitObjectTypeOneBound: \n Has dyn dispatch: ");
-
- if (has_dyn) {
- str += "true";
- } else {
- str += "false";
- }
-
- str += "\n TraitBound: " + trait_bound.as_string();
-
- return str;
- }
-
- ::std::string TypePathFunction::as_string() const {
- ::std::string str("(");
-
- if (has_inputs()) {
- for (const auto& param : inputs) {
- str += param->as_string() + ", ";
- }
- }
-
- str += ")";
-
- if (has_return_type()) {
- str += " -> " + return_type->as_string();
- }
-
- return str;
- }
-
- ::std::string TypePathSegmentFunction::as_string() const {
- return TypePathSegment::as_string() + function_path.as_string();
- }
-
- ::std::string ArrayType::as_string() const {
- return "[" + elem_type->as_string() + "; " + size->as_string() + "]";
- }
-
- ::std::string SliceType::as_string() const {
- return "[" + elem_type->as_string() + "]";
- }
-
- ::std::string TupleType::as_string() const {
- ::std::string str("(");
-
- if (!is_unit_type()) {
- for (const auto& elem : elems) {
- str += elem->as_string() + ", ";
- }
- }
-
- str += ")";
-
- return str;
- }
-
- ::std::string StructExpr::as_string() const {
- ::std::string str = ExprWithoutBlock::as_string();
-
- str += "\nStructExpr";
-
- str += "\n PathInExpr: " + struct_name.as_string();
-
- return str;
- }
-
- ::std::string StructExprTuple::as_string() const {
- ::std::string str = StructExpr::as_string();
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n Tuple fields: ";
- if (exprs.empty()) {
- str += "none";
- } else {
- for (const auto& field : exprs) {
- // debug - null pointer check
- if (field == NULL) {
- return "ERROR_MARK_STRING - nullptr struct expr tuple field";
- }
-
- str += "\n " + field->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string StructExprStruct::as_string() const {
- ::std::string str("StructExprStruct (or subclass): ");
-
- str += "\n Path: " + get_struct_name().as_string();
-
- // inner attributes
- str += "\n inner attributes: ";
- if (inner_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "inner attribute" syntax - just the body
- for (const auto& attr : inner_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- return str;
- }
-
- ::std::string StructBase::as_string() const {
- if (base_struct != NULL) {
- return base_struct->as_string();
- } else {
- return "ERROR_MARK_STRING - invalid struct base had as string applied";
- }
- }
-
- ::std::string StructExprFieldWithVal::as_string() const {
- // used to get value string
- return value->as_string();
- }
-
- ::std::string StructExprFieldIdentifierValue::as_string() const {
- return field_name + " : " + StructExprFieldWithVal::as_string();
- }
-
- ::std::string StructExprFieldIndexValue::as_string() const {
- return ::std::to_string(index) + " : " + StructExprFieldWithVal::as_string();
- }
-
- ::std::string StructExprStructFields::as_string() const {
- ::std::string str = StructExprStruct::as_string();
-
- str += "\n Fields: ";
- if (fields.empty()) {
- str += "none";
- } else {
- for (const auto& field : fields) {
- str += "\n " + field->as_string();
- }
- }
-
- str += "\n Struct base: ";
- if (!has_struct_base()) {
- str += "none";
- } else {
- str += struct_base.as_string();
- }
-
- return str;
- }
-
- ::std::string EnumItem::as_string() const {
- // outer attributes
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n" + variant_name;
-
- return str;
- }
-
- ::std::string EnumItemTuple::as_string() const {
- ::std::string str = EnumItem::as_string();
-
- // add tuple opening parens
- str += "(";
-
- // tuple fields
- if (has_tuple_fields()) {
- for (const auto& field : tuple_fields) {
- str += field.as_string() + ", ";
- }
- }
-
- // add tuple closing parens
- str += ")";
-
- return str;
- }
-
- ::std::string TupleField::as_string() const {
- // outer attributes
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- if (has_visibility()) {
- str += "\n" + visibility.as_string();
- }
-
- str += " " + field_type->as_string();
-
- return str;
- }
-
- ::std::string EnumItemStruct::as_string() const {
- ::std::string str = EnumItem::as_string();
-
- // add struct opening parens
- str += "{";
-
- // tuple fields
- if (has_struct_fields()) {
- for (const auto& field : struct_fields) {
- str += field.as_string() + ", ";
- }
- }
-
- // add struct closing parens
- str += "}";
-
- return str;
- }
-
- ::std::string StructField::as_string() const {
- // outer attributes
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- if (has_visibility()) {
- str += "\n" + visibility.as_string();
- }
-
- str += " " + field_name + " : " + field_type->as_string();
-
- return str;
- }
-
- ::std::string EnumItemDiscriminant::as_string() const {
- ::std::string str = EnumItem::as_string();
-
- // add equal and expression
- str += " = " + expression->as_string();
-
- return str;
- }
-
- ::std::string ExternalItem::as_string() const {
- // outer attributes
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- // start visibility on new line and with a space
- str += "\n" + visibility.as_string() + " ";
-
- return str;
- }
-
- ::std::string ExternalStaticItem::as_string() const {
- ::std::string str = ExternalItem::as_string();
-
- str += "static ";
-
- if (has_mut) {
- str += "mut ";
- }
-
- // add name
- str += get_item_name();
-
- // add type on new line
- str += "\n Type: " + item_type->as_string();
-
- return str;
- }
-
- ::std::string ExternalFunctionItem::as_string() const {
- ::std::string str = ExternalItem::as_string();
-
- str += "fn ";
-
- // add name
- str += get_item_name();
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in external function item.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- // function params
- str += "\n Function params: ";
- if (function_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : function_params) {
- str += "\n " + param.as_string();
- }
- if (has_variadics) {
- str += "\n .. (variadic)";
- }
- }
-
- // add type on new line
- str += "\n (return) Type: " + return_type->as_string();
-
- // where clause
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- return str;
- }
-
- ::std::string NamedFunctionParam::as_string() const {
- ::std::string str = name;
-
- str += "\n Type: " + param_type->as_string();
-
- return str;
- }
-
- /*::std::string TraitItem::as_string() const {
- // outer attributes
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- return str;
- }*/
-
- ::std::string TraitItemFunc::as_string() const {
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n" + decl.as_string();
-
- str += "\n Definition (block expr): ";
- if (has_definition()) {
- str += block_expr->as_string();
- } else {
- str += "none";
- }
-
- return str;
- }
-
- ::std::string TraitFunctionDecl::as_string() const {
- ::std::string str = qualifiers.as_string() + "fn " + function_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in trait function decl.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Function params: ";
- if (has_params()) {
- for (const auto& param : function_params) {
- str += "\n " + param.as_string();
- }
- } else {
- str += "none";
- }
-
- str += "\n Return type: ";
- if (has_return_type()) {
- str += return_type->as_string();
- } else {
- str += "none (void)";
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- return str;
- }
-
- ::std::string TraitItemMethod::as_string() const {
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\n" + decl.as_string();
-
- str += "\n Definition (block expr): ";
- if (has_definition()) {
- str += block_expr->as_string();
- } else {
- str += "none";
- }
-
- return str;
- }
-
- ::std::string TraitMethodDecl::as_string() const {
- ::std::string str = qualifiers.as_string() + "fn " + function_name;
-
- // generic params
- str += "\n Generic params: ";
- if (generic_params.empty()) {
- str += "none";
- } else {
- for (const auto& param : generic_params) {
- // DEBUG: null pointer check
- if (param == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "generic param in trait function decl.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + param->as_string();
- }
- }
-
- str += "\n Self param: " + self_param.as_string();
-
- str += "\n Function params: ";
- if (has_params()) {
- for (const auto& param : function_params) {
- str += "\n " + param.as_string();
- }
- } else {
- str += "none";
- }
-
- str += "\n Return type: ";
- if (has_return_type()) {
- str += return_type->as_string();
- } else {
- str += "none (void)";
- }
-
- str += "\n Where clause: ";
- if (has_where_clause()) {
- str += where_clause.as_string();
- } else {
- str += "none";
- }
-
- return str;
- }
-
- ::std::string TraitItemConst::as_string() const {
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\nconst " + name + " : " + type->as_string();
-
- if (has_expression()) {
- str += " = " + expr->as_string();
- }
-
- return str;
- }
-
- ::std::string TraitItemType::as_string() const {
- ::std::string str = "outer attributes: ";
- if (outer_attrs.empty()) {
- str += "none";
- } else {
- // note that this does not print them with "outer attribute" syntax - just the body
- for (const auto& attr : outer_attrs) {
- str += "\n " + attr.as_string();
- }
- }
-
- str += "\ntype " + name;
-
- str += "\n Type param bounds: ";
- if (!has_type_param_bounds()) {
- str += "none";
- } else {
- for (const auto& bound : type_param_bounds) {
- // DEBUG: null pointer check
- if (bound == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "type param bound in trait item type.");
- return "NULL_POINTER_MARK";
- }
-
- str += "\n " + bound->as_string();
- }
- }
-
- return str;
- }
-
- ::std::string SelfParam::as_string() const {
- if (is_error()) {
- return "error";
- } else {
- if (has_type()) {
- // type (i.e. not ref, no lifetime)
- ::std::string str;
-
- if (is_mut) {
- str += "mut ";
- }
-
- str += "self : ";
-
- str += type->as_string();
-
- return str;
- } else if (has_lifetime()) {
- // ref and lifetime
- ::std::string str = "&" + lifetime.as_string() + " ";
-
- if (is_mut) {
- str += "mut ";
- }
-
- str += "self";
-
- return str;
- } else if (has_ref) {
- // ref with no lifetime
- ::std::string str = "&";
-
- if (is_mut) {
- str += " mut ";
- }
-
- str += "self";
-
- return str;
- } else {
- // no ref, no type
- ::std::string str;
-
- if (is_mut) {
- str += "mut ";
- }
-
- str += "self";
-
- return str;
- }
- }
- }
-
- ::std::string ArrayElemsCopied::as_string() const {
- return elem_to_copy->as_string() + "; " + num_copies->as_string();
- }
+::std::string
+TraitObjectType::as_string () const
+{
+ ::std::string str ("TraitObjectType: \n Has dyn dispatch: ");
- ::std::string LifetimeWhereClauseItem::as_string() const {
- ::std::string str("Lifetime: ");
+ if (has_dyn)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
- str += lifetime.as_string();
-
- str += "\nLifetime bounds: ";
+ str += "\n TypeParamBounds: ";
+ if (type_param_bounds.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &bound : type_param_bounds)
+ {
+ str += "\n " + bound->as_string ();
+ }
+ }
- for (const auto& bound : lifetime_bounds) {
- str += "\n " + bound.as_string();
- }
-
- return str;
- }
+ return str;
+}
- ::std::string TypeBoundWhereClauseItem::as_string() const {
- ::std::string str("For lifetimes: ");
+::std::string
+BareFunctionType::as_string () const
+{
+ ::std::string str ("BareFunctionType: \n For lifetimes: ");
- if (!has_for_lifetimes()) {
- str += "none";
- } else {
- for (const auto& for_lifetime : for_lifetimes) {
- str += "\n " + for_lifetime.as_string();
- }
- }
+ if (!has_for_lifetimes ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &for_lifetime : for_lifetimes)
+ {
+ str += "\n " + for_lifetime.as_string ();
+ }
+ }
- str += "\nType: " + bound_type->as_string();
+ str += "\n Qualifiers: " + function_qualifiers.as_string ();
- str += "\nType param bounds bounds: ";
+ str += "\n Params: ";
+ if (params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : params)
+ {
+ str += "\n " + param.as_string ();
+ }
+ }
- for (const auto& bound : type_param_bounds) {
- // debug null pointer check
- if (bound == NULL) {
- return "NULL_POINTER_MARK - type param bounds";
- }
+ str += "\n Is variadic: ";
+ if (is_variadic)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
- str += "\n " + bound->as_string();
- }
+ str += "\n Return type: ";
+ if (!has_return_type ())
+ {
+ str += "none (void)";
+ }
+ else
+ {
+ str += return_type->as_string ();
+ }
- return str;
- }
+ return str;
+}
- ::std::string ArrayElemsValues::as_string() const {
- ::std::string str;
+::std::string
+ImplTraitTypeOneBound::as_string () const
+{
+ ::std::string str ("ImplTraitTypeOneBound: \n TraitBound: ");
- for (const auto& expr : values) {
- // DEBUG: null pointer check
- if (expr == NULL) {
- fprintf(stderr, "something really terrible has gone wrong - null pointer "
- "expr in array elems values.");
- return "NULL_POINTER_MARK";
- }
+ return str + trait_bound.as_string ();
+}
- str += "\n " + expr->as_string();
- }
+::std::string
+TypePathSegmentGeneric::as_string () const
+{
+ return TypePathSegment::as_string () + "<" + generic_args.as_string () + ">";
+}
- return str;
- }
+::std::string
+TraitObjectTypeOneBound::as_string () const
+{
+ ::std::string str ("TraitObjectTypeOneBound: \n Has dyn dispatch: ");
- ::std::string MaybeNamedParam::as_string() const {
- ::std::string str;
+ if (has_dyn)
+ {
+ str += "true";
+ }
+ else
+ {
+ str += "false";
+ }
- switch (param_kind) {
- case UNNAMED:
- break;
- case IDENTIFIER:
- str = name + " : ";
- break;
- case WILDCARD:
- str = "_ : ";
- break;
- default:
- return "ERROR_MARK_STRING - maybe named param unrecognised param kind";
- }
-
- str += param_type->as_string();
-
- return str;
- }
-
- ::std::string MetaItemSeq::as_string() const {
- ::std::string path_str = path.as_string() + "(";
-
- for (const auto& item : seq) {
- path_str += item->as_string() + ", ";
- }
-
- return path_str + ")";
- }
-
- ::std::string MetaListPaths::as_string() const {
- ::std::string str = ident + "(";
-
- for (const auto& path : paths) {
- str += path.as_string() + ", ";
- }
-
- return str + ")";
- }
-
- ::std::string MetaListNameValueStr::as_string() const {
- ::std::string str = ident + "(";
-
- for (const auto& elem : strs) {
- str += elem.as_string() + ", ";
- }
-
- return str + ")";
- }
-
- ::std::string AttrInputMetaItemContainer::as_string() const {
- ::std::string str = "(";
-
- for (const auto& item : items) {
- str += item->as_string() + ", ";
- }
-
- return str + ")";
- }
-
- // Override that calls the function recursively on all items contained within the module.
- void ModuleBodied::add_crate_name(::std::vector< ::std::string>& names) const {
- /* TODO: test whether module has been 'cfg'-ed out to determine whether to exclude it
- * from search */
-
- for (const auto& item : items) {
- item->add_crate_name(names);
- }
- }
-
- void Attribute::parse_attr_to_meta_item() {
- // only parse if has attribute input
- if (!has_attr_input()) {
- return;
- }
-
- ::std::unique_ptr<AttrInput> converted_input(attr_input->parse_to_meta_item());
-
- if (converted_input != NULL) {
- attr_input = ::std::move(converted_input);
- }
- }
-
- AttrInput* DelimTokenTree::parse_to_meta_item() const {
- // must have token trees
- if (token_trees.empty()) {
- return NULL;
- }
-
- // assume top-level delim token tree in attribute - convert all nested ones to token
- // stream
- ::std::vector< ::std::unique_ptr<Token> > token_stream = to_token_stream();
-
- // TODO: replace this with a specialised converter that the token stream is moved into
- /*int i = 0;
- ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items(
- parse_meta_item_seq(token_stream, i));*/
- // something like:
- MacroParser parser(::std::move(token_stream));
- ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items(
- parser.parse_meta_item_seq());
-
- return new AttrInputMetaItemContainer(::std::move(meta_items));
- }
-
- ::std::unique_ptr<MetaItemInner> MacroParser::parse_meta_item_inner() {
- // if first tok not identifier, not a "special" case one
- if (peek_token()->get_id() != IDENTIFIER) {
- switch (peek_token()->get_id()) {
- case CHAR_LITERAL:
- case STRING_LITERAL:
- case BYTE_CHAR_LITERAL:
- case BYTE_STRING_LITERAL:
- case INT_LITERAL:
- case FLOAT_LITERAL:
- case TRUE_LITERAL:
- case FALSE_LITERAL:
- // stream_pos++;
- return parse_meta_item_lit();
- case SUPER:
- case SELF:
- case CRATE:
- case DOLLAR_SIGN:
- case SCOPE_RESOLUTION: {
- return parse_path_meta_item();
- }
- default:
- rust_error_at(peek_token()->get_locus(),
- "unrecognised token '%s' in meta item",
- get_token_description(peek_token()->get_id()));
- return NULL;
- }
- }
-
- // else, check for path
- if (peek_token(1)->get_id() == SCOPE_RESOLUTION) {
- // path
- return parse_path_meta_item();
- }
-
- Identifier ident = peek_token()->as_string();
- if (is_end_meta_item_tok(peek_token(1)->get_id())) {
- // meta word syntax
- skip_token();
- return ::std::unique_ptr<MetaWord>(new MetaWord(::std::move(ident)));
- }
-
- if (peek_token(1)->get_id() == EQUAL) {
- // maybe meta name value str syntax - check next 2 tokens
- if (peek_token(2)->get_id() == STRING_LITERAL
- && is_end_meta_item_tok(peek_token(3)->get_id())) {
- // meta name value str syntax
- ::std::string value = peek_token(2)->as_string();
-
- skip_token(2);
-
- return ::std::unique_ptr<MetaNameValueStr>(
- new MetaNameValueStr(::std::move(ident), ::std::move(value)));
- } else {
- // just interpret as path-based meta item
- return parse_path_meta_item();
- }
- }
-
- if (peek_token(1)->get_id() != LEFT_PAREN) {
- rust_error_at(peek_token(1)->get_locus(),
- "unexpected token '%s' after identifier in attribute",
- get_token_description(peek_token(1)->get_id()));
- return NULL;
- }
-
- // HACK: parse parenthesised sequence, and then try conversions to other stuff
- ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items = parse_meta_item_seq();
-
- // pass for meta name value str
- ::std::vector<MetaNameValueStr> meta_name_value_str_items;
- for (const auto& item : meta_items) {
- ::std::unique_ptr<MetaNameValueStr> converted_item(item->to_meta_name_value_str());
- if (converted_item == NULL) {
- meta_name_value_str_items.clear();
- break;
- }
- meta_name_value_str_items.push_back(::std::move(*converted_item));
- }
- // if valid, return this
- if (!meta_name_value_str_items.empty()) {
- return ::std::unique_ptr<MetaListNameValueStr>(new MetaListNameValueStr(
- ::std::move(ident), ::std::move(meta_name_value_str_items)));
- }
-
- // pass for meta list idents
- /*::std::vector<Identifier> ident_items;
- for (const auto& item : meta_items) {
- ::std::unique_ptr<Identifier> converted_ident(item->to_ident_item());
- if (converted_ident == NULL) {
- ident_items.clear();
- break;
- }
- ident_items.push_back(::std::move(*converted_ident));
- }
- // if valid return this
- if (!ident_items.empty()) {
- return ::std::unique_ptr<MetaListIdents>(new MetaListIdents(::std::move(ident),
- ::std::move(ident_items)));
- }*/
- // as currently no meta list ident, currently no path. may change in future
-
- // pass for meta list paths
- ::std::vector<SimplePath> path_items;
- for (const auto& item : meta_items) {
- SimplePath converted_path(item->to_path_item());
- if (converted_path.is_empty()) {
- path_items.clear();
- break;
- }
- path_items.push_back(::std::move(converted_path));
- }
- if (!path_items.empty()) {
- return ::std::unique_ptr<MetaListPaths>(
- new MetaListPaths(::std::move(ident), ::std::move(path_items)));
- }
-
- rust_error_at(Linemap::unknown_location(), "failed to parse any meta item inner");
- return NULL;
- }
-
- bool MacroParser::is_end_meta_item_tok(TokenId id) const {
- return id == COMMA || id == RIGHT_PAREN;
- }
-
- ::std::unique_ptr<MetaItem> MacroParser::parse_path_meta_item() {
- SimplePath path = parse_simple_path();
- if (path.is_empty()) {
- rust_error_at(peek_token()->get_locus(), "failed to parse simple path in attribute");
- return NULL;
- }
-
- switch (peek_token()->get_id()) {
- case LEFT_PAREN: {
- ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items
- = parse_meta_item_seq();
-
- return ::std::unique_ptr<MetaItemSeq>(
- new MetaItemSeq(::std::move(path), ::std::move(meta_items)));
- }
- case EQUAL: {
- skip_token();
-
- Location locus = peek_token()->get_locus();
- Literal lit = parse_literal();
- if (lit.is_error()) {
- rust_error_at(
- peek_token()->get_locus(), "failed to parse literal in attribute");
- return NULL;
- }
- LiteralExpr expr(::std::move(lit), locus);
- // stream_pos++;
- // shouldn't be required anymore due to parsing literal actually skipping the
- // token
- return ::std::unique_ptr<MetaItemPathLit>(
- new MetaItemPathLit(::std::move(path), ::std::move(expr)));
- }
- case COMMA:
- // just simple path
- return ::std::unique_ptr<MetaItemPath>(new MetaItemPath(::std::move(path)));
- default:
- rust_error_at(peek_token()->get_locus(), "unrecognised token '%s' in meta item",
- get_token_description(peek_token()->get_id()));
- return NULL;
- }
- }
-
- // Parses a parenthesised sequence of meta item inners. Parentheses are required here.
- ::std::vector< ::std::unique_ptr<MetaItemInner> > MacroParser::parse_meta_item_seq() {
- if (stream_pos != 0) {
- // warning?
- fprintf(stderr, "WARNING: stream pos for parse_meta_item_seq is not 0!\n");
- }
-
- // int i = 0;
- int vec_length = token_stream.size();
- ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items;
-
- if (peek_token()->get_id() != LEFT_PAREN) {
- rust_error_at(peek_token()->get_locus(), "missing left paren in delim token tree");
- return {};
- }
- skip_token();
-
- while (stream_pos < vec_length && peek_token()->get_id() != RIGHT_PAREN) {
- ::std::unique_ptr<MetaItemInner> inner = parse_meta_item_inner();
- if (inner == NULL) {
- rust_error_at(
- peek_token()->get_locus(), "failed to parse inner meta item in attribute");
- return {};
- }
- meta_items.push_back(::std::move(inner));
-
- if (peek_token()->get_id() != COMMA) {
- break;
- }
- skip_token();
- }
-
- if (peek_token()->get_id() != RIGHT_PAREN) {
- rust_error_at(peek_token()->get_locus(), "missing right paren in delim token tree");
- return {};
- }
- skip_token();
-
- return meta_items;
- }
-
- // Collects any nested token trees into a flat token stream, suitable for parsing.
- ::std::vector< ::std::unique_ptr<Token> > DelimTokenTree::to_token_stream() const {
- ::std::vector< ::std::unique_ptr<Token> > tokens;
-
- // simulate presence of delimiters
- tokens.push_back(::std::unique_ptr<Token>(
- new Token(LEFT_PAREN, Linemap::unknown_location(), "", CORETYPE_UNKNOWN)));
-
- for (const auto& tree : token_trees) {
- ::std::vector< ::std::unique_ptr<Token> > stream = tree->to_token_stream();
-
- tokens.insert(tokens.end(), ::std::make_move_iterator(stream.begin()),
- ::std::make_move_iterator(stream.end()));
- }
-
- tokens.push_back(::std::unique_ptr<Token>(
- new Token(RIGHT_PAREN, Linemap::unknown_location(), "", CORETYPE_UNKNOWN)));
-
- tokens.shrink_to_fit();
-
- return tokens;
- }
-
- Literal MacroParser::parse_literal() {
- const ::std::unique_ptr<Token>& tok = peek_token();
- switch (tok->get_id()) {
- case CHAR_LITERAL:
- skip_token();
- return Literal(tok->as_string(), Literal::CHAR);
- case STRING_LITERAL:
- skip_token();
- return Literal(tok->as_string(), Literal::STRING);
- case BYTE_CHAR_LITERAL:
- skip_token();
- return Literal(tok->as_string(), Literal::BYTE);
- case BYTE_STRING_LITERAL:
- skip_token();
- return Literal(tok->as_string(), Literal::BYTE_STRING);
- case INT_LITERAL:
- skip_token();
- return Literal(tok->as_string(), Literal::INT);
- case FLOAT_LITERAL:
- skip_token();
- return Literal(tok->as_string(), Literal::FLOAT);
- case TRUE_LITERAL:
- skip_token();
- return Literal("true", Literal::BOOL);
- case FALSE_LITERAL:
- skip_token();
- return Literal("false", Literal::BOOL);
- default:
- rust_error_at(tok->get_locus(), "expected literal - found '%s'",
- get_token_description(tok->get_id()));
- return Literal::create_error();
- }
- }
-
- SimplePath MacroParser::parse_simple_path() {
- bool has_opening_scope_res = false;
- if (peek_token()->get_id() == SCOPE_RESOLUTION) {
- has_opening_scope_res = true;
- skip_token();
- }
-
- ::std::vector<SimplePathSegment> segments;
-
- SimplePathSegment segment = parse_simple_path_segment();
- if (segment.is_error()) {
- rust_error_at(peek_token()->get_locus(),
- "failed to parse simple path segment in attribute simple path");
- return SimplePath::create_empty();
- }
- segments.push_back(::std::move(segment));
-
- while (peek_token()->get_id() == SCOPE_RESOLUTION) {
- skip_token();
-
- SimplePathSegment segment = parse_simple_path_segment();
- if (segment.is_error()) {
- rust_error_at(peek_token()->get_locus(),
- "failed to parse simple path segment in attribute simple path");
- return SimplePath::create_empty();
- }
- segments.push_back(::std::move(segment));
- }
- segments.shrink_to_fit();
-
- return SimplePath(::std::move(segments), has_opening_scope_res);
- }
-
- SimplePathSegment MacroParser::parse_simple_path_segment() {
- const ::std::unique_ptr<Token>& tok = peek_token();
- switch (tok->get_id()) {
- case IDENTIFIER:
- skip_token();
- return SimplePathSegment(tok->as_string(), tok->get_locus());
- case SUPER:
- skip_token();
- return SimplePathSegment("super", tok->get_locus());
- case SELF:
- skip_token();
- return SimplePathSegment("self", tok->get_locus());
- case CRATE:
- skip_token();
- return SimplePathSegment("crate", tok->get_locus());
- case DOLLAR_SIGN:
- if (peek_token(1)->get_id() == CRATE) {
- skip_token(1);
- return SimplePathSegment("$crate", tok->get_locus());
- }
- gcc_fallthrough();
- default:
- rust_error_at(tok->get_locus(), "unexpected token '%s' in simple path segment",
- get_token_description(tok->get_id()));
- return SimplePathSegment::create_error();
- }
- }
-
- ::std::unique_ptr<MetaItemLitExpr> MacroParser::parse_meta_item_lit() {
- Location locus = peek_token()->get_locus();
- LiteralExpr lit_expr(parse_literal(), locus);
- return ::std::unique_ptr<MetaItemLitExpr>(new MetaItemLitExpr(::std::move(lit_expr)));
- }
-
- bool AttrInputMetaItemContainer::check_cfg_predicate(const Session& session) const {
- // cfg value of container is purely based on cfg of each inner item - all must be true
- for (const auto& inner_item : items) {
- if (!inner_item->check_cfg_predicate(session)) {
- return false;
- }
- }
-
- /* TODO: as far as I can tell, there should only be a single element to check here, so
- * ensure there is only a single element in items too? */
-
- return true;
- }
-
- bool MetaItemLitExpr::check_cfg_predicate(const Session& session ATTRIBUTE_UNUSED) const {
- // as far as I can tell, a literal expr can never be a valid cfg body, so false
- return false;
- }
-
- bool MetaListNameValueStr::check_cfg_predicate(const Session& session) const {
- if (ident == "all") {
- for (const auto& str : strs) {
- if (!str.check_cfg_predicate(session)) {
- return false;
- }
- }
- return true;
- } else if (ident == "any") {
- for (const auto& str : strs) {
- if (str.check_cfg_predicate(session)) {
- return true;
- }
- }
- return false;
- } else if (ident == "not") {
- if (strs.size() != 1) {
- // HACK: convert vector platform-dependent size_type to string to use in printf
- rust_error_at(Linemap::unknown_location(),
- "cfg predicate could not be checked for MetaListNameValueStr with ident of "
- "'not' because there are '%s' elements, not '1'",
- ::std::to_string(strs.size()).c_str());
- return false;
- }
-
- return !strs[0].check_cfg_predicate(session);
- } else {
- rust_error_at(Linemap::unknown_location(),
- "cfg predicate could not be checked for MetaListNameValueStr with ident of "
- "'%s' - ident must be 'all' or 'any'",
- ident.c_str());
- return false;
- }
- }
-
- bool MetaListPaths::check_cfg_predicate(const Session& session) const {
- if (ident == "all") {
- for (const auto& path : paths) {
- if (!check_path_exists_in_cfg(session, path)) {
- return false;
- }
- }
- return true;
- } else if (ident == "any") {
- for (const auto& path : paths) {
- if (check_path_exists_in_cfg(session, path)) {
- return true;
- }
- }
- return false;
- } else if (ident == "not") {
- if (paths.size() != 1) {
- // HACK: convert vector platform-dependent size_type to string to use in printf
- rust_error_at(Linemap::unknown_location(),
- "cfg predicate could not be checked for MetaListPaths with ident of 'not' "
- "because there are '%s' elements, not '1'",
- ::std::to_string(paths.size()).c_str());
- return false;
- }
-
- return !check_path_exists_in_cfg(session, paths[0]);
- } else {
- rust_error_at(Linemap::unknown_location(),
- "cfg predicate could not be checked for MetaListNameValueStr with ident of "
- "'%s' - ident must be 'all' or 'any'",
- ident.c_str());
- return false;
- }
- }
-
- bool MetaListPaths::check_path_exists_in_cfg(
- const Session& session, const SimplePath& path) const {
- auto it = session.options.target_data.features.find(path.as_string());
- if (it != session.options.target_data.features.end()) {
- return true;
- }
- return false;
- }
-
- bool MetaItemSeq::check_cfg_predicate(const Session& session) const {
- if (path.as_string() == "all") {
- for (const auto& item : seq) {
- if (!item->check_cfg_predicate(session)) {
- return false;
- }
- }
- return true;
- } else if (path.as_string() == "any") {
- for (const auto& item : seq) {
- if (item->check_cfg_predicate(session)) {
- return true;
- }
- }
- return false;
- } else if (path.as_string() == "not") {
- if (seq.size() != 1) {
- // HACK: convert vector platform-dependent size_type to string to use in printf
- rust_error_at(Linemap::unknown_location(),
- "cfg predicate could not be checked for MetaItemSeq with ident of 'not' "
- "because there are '%s' elements, not '1'",
- ::std::to_string(seq.size()).c_str());
- return false;
- }
-
- return !seq[0]->check_cfg_predicate(session);
- } else {
- rust_error_at(Linemap::unknown_location(),
- "cfg predicate could not be checked for MetaItemSeq with path of "
- "'%s' - path must be 'all' or 'any'",
- path.as_string().c_str());
- return false;
- }
- }
-
- bool MetaWord::check_cfg_predicate(const Session& session) const {
- auto it = session.options.target_data.features.find(ident);
- if (it != session.options.target_data.features.end()) {
- return true;
- }
- return false;
- }
-
- bool MetaItemPath::check_cfg_predicate(const Session& session) const {
- /* Strictly speaking, this should always be false, but maybe do check relating to
- * SimplePath being identifier. Currently, it would return true if path as identifier
- * existed, and if the path in string form existed (though this shouldn't occur). */
- auto it = session.options.target_data.features.find(path.as_string());
- if (it != session.options.target_data.features.end()) {
- return true;
- }
- return false;
- }
-
- bool MetaNameValueStr::check_cfg_predicate(const Session& session) const {
- return session.options.target_data.has_key_value_pair(ident, str);
- }
-
- bool MetaItemPathLit::check_cfg_predicate(const Session& session) const {
- return session.options.target_data.has_key_value_pair(path.as_string(), lit.as_string());
- }
-
- ::std::vector< ::std::unique_ptr<Token> > Token::to_token_stream() const {
- // initialisation list doesn't work as it needs copy constructor, so have to do this
- ::std::vector< ::std::unique_ptr<Token> > dummy_vector;
- dummy_vector.reserve(1);
- dummy_vector.push_back(::std::unique_ptr<Token>(clone_token_impl()));
- return dummy_vector;
- }
-
- /* Visitor implementations - these are short but inlining can't happen anyway due to virtual
- * functions and I didn't want to make the ast header includes any longer than they already
- * are. */
-
- void Token::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void DelimTokenTree::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void IdentifierExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void Lifetime::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void LifetimeParam::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void MacroInvocationSemi::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void PathInExpression::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void TypePathSegment::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void TypePathSegmentGeneric::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void TypePathSegmentFunction::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void TypePath::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void QualifiedPathInExpression::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void QualifiedPathInType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void LiteralExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void AttrInputLiteral::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void MetaItemLitExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void MetaItemPathLit::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void BorrowExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void DereferenceExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
-
- void ErrorPropagationExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n TraitBound: " + trait_bound.as_string ();
- void NegationExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ArithmeticOrLogicalExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TypePathFunction::as_string () const
+{
+ ::std::string str ("(");
+
+ if (has_inputs ())
+ {
+ auto i = inputs.begin ();
+ auto e = inputs.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ }
- void ComparisonExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += ")";
- void LazyBooleanExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (has_return_type ())
+ {
+ str += " -> " + return_type->as_string ();
+ }
- void TypeCastExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void AssignmentExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TypePathSegmentFunction::as_string () const
+{
+ return TypePathSegment::as_string () + function_path.as_string ();
+}
- void CompoundAssignmentExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+ArrayType::as_string () const
+{
+ return "[" + elem_type->as_string () + "; " + size->as_string () + "]";
+}
- void GroupedExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+SliceType::as_string () const
+{
+ return "[" + elem_type->as_string () + "]";
+}
- void ArrayElemsValues::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TupleType::as_string () const
+{
+ ::std::string str ("(");
+
+ if (!is_unit_type ())
+ {
+ auto i = elems.begin ();
+ auto e = elems.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ }
- void ArrayElemsCopied::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += ")";
- void ArrayExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ArrayIndexExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExpr::as_string () const
+{
+ ::std::string str = ExprWithoutBlock::as_string ();
+ indent_spaces (enter);
+ str += "\n" + indent_spaces (stay) + "StructExpr:";
+ indent_spaces (enter);
+ str += "\n" + indent_spaces (stay) + "PathInExpr:\n";
+ str += indent_spaces (stay) + struct_name.as_string ();
+ indent_spaces (out);
+ indent_spaces (out);
+ return str;
+}
- void TupleExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExprTuple::as_string () const
+{
+ ::std::string str = StructExpr::as_string ();
- void TupleIndexExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (exprs.empty ())
+ {
+ str += "()";
+ }
+ else
+ {
+ auto i = exprs.begin ();
+ auto e = exprs.end ();
+
+ // debug - null pointer check
+ if (*i == NULL)
+ {
+ return "ERROR_MARK_STRING - nullptr struct expr tuple field";
+ }
+
+ str += '(';
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ str += ')';
+ }
- void StructExprStruct::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ indent_spaces (enter);
+ indent_spaces (enter);
+ // inner attributes
+ str += "\n" + indent_spaces (stay) + "inner attributes:";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n" + indent_spaces (stay) + attr.as_string ();
+ }
+ }
+ indent_spaces (out);
+ indent_spaces (out);
- void StructExprFieldIdentifier::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void StructExprFieldIdentifierValue::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExprStruct::as_string () const
+{
+ ::std::string str ("StructExprStruct (or subclass): ");
- void StructExprFieldIndexValue::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Path: " + get_struct_name ().as_string ();
- void StructExprStructFields::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // inner attributes
+ str += "\n inner attributes: ";
+ if (inner_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "inner attribute" syntax -
+ // just the body
+ for (const auto &attr : inner_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void StructExprStructBase::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void StructExprTuple::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructBase::as_string () const
+{
+ if (base_struct != NULL)
+ {
+ return base_struct->as_string ();
+ }
+ else
+ {
+ return "ERROR_MARK_STRING - invalid struct base had as string applied";
+ }
+}
- void StructExprUnit::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExprFieldWithVal::as_string () const
+{
+ // used to get value string
+ return value->as_string ();
+}
- void EnumExprFieldIdentifier::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExprFieldIdentifierValue::as_string () const
+{
+ return field_name + " : " + StructExprFieldWithVal::as_string ();
+}
- void EnumExprFieldIdentifierValue::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExprFieldIndexValue::as_string () const
+{
+ return ::std::to_string (index) + " : "
+ + StructExprFieldWithVal::as_string ();
+}
- void EnumExprFieldIndexValue::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructExprStructFields::as_string () const
+{
+ ::std::string str = StructExprStruct::as_string ();
- void EnumExprStruct::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Fields: ";
+ if (fields.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &field : fields)
+ {
+ str += "\n " + field->as_string ();
+ }
+ }
- void EnumExprTuple::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Struct base: ";
+ if (!has_struct_base ())
+ {
+ str += "none";
+ }
+ else
+ {
+ str += struct_base.as_string ();
+ }
- void EnumExprFieldless::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void CallExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+EnumItem::as_string () const
+{
+ // outer attributes
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void MethodCallExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n" + variant_name;
- void FieldAccessExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ClosureExprInner::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+EnumItemTuple::as_string () const
+{
+ ::std::string str = EnumItem::as_string ();
+
+ // add tuple opening parens
+ str += "(";
+
+ // tuple fields
+ if (has_tuple_fields ())
+ {
+ auto i = tuple_fields.begin ();
+ auto e = tuple_fields.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ }
- void BlockExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add tuple closing parens
+ str += ")";
- void ClosureExprInnerTyped::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ContinueExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TupleField::as_string () const
+{
+ // outer attributes
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void BreakExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (has_visibility ())
+ {
+ str += "\n" + visibility.as_string ();
+ }
- void RangeFromToExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += " " + field_type->as_string ();
- void RangeFromExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void RangeToExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+EnumItemStruct::as_string () const
+{
+ ::std::string str = EnumItem::as_string ();
+
+ // add struct opening parens
+ str += "{";
+
+ // tuple fields
+ if (has_struct_fields ())
+ {
+ auto i = struct_fields.begin ();
+ auto e = struct_fields.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+ }
- void RangeFullExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add struct closing parens
+ str += "}";
- void RangeFromToInclExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void RangeToInclExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+StructField::as_string () const
+{
+ // outer attributes
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void ReturnExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (has_visibility ())
+ {
+ str += "\n" + visibility.as_string ();
+ }
- void UnsafeBlockExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += " " + field_name + " : " + field_type->as_string ();
- void LoopExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void WhileLoopExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+EnumItemDiscriminant::as_string () const
+{
+ ::std::string str = EnumItem::as_string ();
- void WhileLetLoopExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add equal and expression
+ str += " = " + expression->as_string ();
- void ForLoopExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void IfExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+ExternalItem::as_string () const
+{
+ // outer attributes
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void IfExprConseqElse::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // start visibility on new line and with a space
+ str += "\n" + visibility.as_string () + " ";
- void IfExprConseqIf::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void IfExprConseqIfLet::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+ExternalStaticItem::as_string () const
+{
+ ::std::string str = ExternalItem::as_string ();
- void IfLetExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "static ";
- void IfLetExprConseqElse::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (has_mut)
+ {
+ str += "mut ";
+ }
- void IfLetExprConseqIf::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add name
+ str += get_item_name ();
- void IfLetExprConseqIfLet::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add type on new line
+ str += "\n Type: " + item_type->as_string ();
- void MatchCaseBlockExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void MatchCaseExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+ExternalFunctionItem::as_string () const
+{
+ ::std::string str = ExternalItem::as_string ();
- void MatchExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "fn ";
- void AwaitExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add name
+ str += get_item_name ();
- void AsyncBlockExpr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in external function item.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
- void TypeParam::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // function params
+ str += "\n Function params: ";
+ if (function_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : function_params)
+ {
+ str += "\n " + param.as_string ();
+ }
+ if (has_variadics)
+ {
+ str += "\n .. (variadic)";
+ }
+ }
- void LifetimeWhereClauseItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // add type on new line
+ str += "\n (return) Type: " + return_type->as_string ();
- void TypeBoundWhereClauseItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // where clause
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
- void Method::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ModuleBodied::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+NamedFunctionParam::as_string () const
+{
+ ::std::string str = name;
- void ModuleNoBody::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Type: " + param_type->as_string ();
- void ExternCrate::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void UseTreeGlob::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+/*::std::string TraitItem::as_string() const {
+ // outer attributes
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty()) {
+ str += "none";
+ } else {
+ // note that this does not print them with "outer attribute" syntax -
+just the body for (const auto& attr : outer_attrs) { str += "\n " +
+attr.as_string();
+ }
+ }
- void UseTreeList::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}*/
- void UseTreeRebind::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TraitItemFunc::as_string () const
+{
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void UseDeclaration::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n" + decl.as_string ();
- void Function::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Definition (block expr): ";
+ if (has_definition ())
+ {
+ str += block_expr->as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
- void TypeAlias::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void StructStruct::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TraitFunctionDecl::as_string () const
+{
+ ::std::string str = qualifiers.as_string () + "fn " + function_name;
- void TupleStruct::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in trait function decl.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
- void EnumItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Function params: ";
+ if (has_params ())
+ {
+ for (const auto &param : function_params)
+ {
+ str += "\n " + param.as_string ();
+ }
+ }
+ else
+ {
+ str += "none";
+ }
- void EnumItemTuple::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Return type: ";
+ if (has_return_type ())
+ {
+ str += return_type->as_string ();
+ }
+ else
+ {
+ str += "none (void)";
+ }
- void EnumItemStruct::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
- void EnumItemDiscriminant::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void Enum::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TraitItemMethod::as_string () const
+{
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void Union::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n" + decl.as_string ();
- void ConstantItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Definition (block expr): ";
+ if (has_definition ())
+ {
+ str += block_expr->as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
- void StaticItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void TraitItemFunc::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TraitMethodDecl::as_string () const
+{
+ ::std::string str = qualifiers.as_string () + "fn " + function_name;
- void TraitItemMethod::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &param : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "generic param in trait function decl.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
- void TraitItemConst::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Self param: " + self_param.as_string ();
- void TraitItemType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Function params: ";
+ if (has_params ())
+ {
+ for (const auto &param : function_params)
+ {
+ str += "\n " + param.as_string ();
+ }
+ }
+ else
+ {
+ str += "none";
+ }
- void Trait::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Return type: ";
+ if (has_return_type ())
+ {
+ str += return_type->as_string ();
+ }
+ else
+ {
+ str += "none (void)";
+ }
- void InherentImpl::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ {
+ str += where_clause.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
- void TraitImpl::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ExternalStaticItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TraitItemConst::as_string () const
+{
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void ExternalFunctionItem::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\nconst " + name + " : " + type->as_string ();
- void ExternBlock::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (has_expression ())
+ {
+ str += " = " + expr->as_string ();
+ }
- void MacroMatchFragment::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void MacroMatchRepetition::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TraitItemType::as_string () const
+{
+ ::std::string str = "outer attributes: ";
+ if (outer_attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ // note that this does not print them with "outer attribute" syntax -
+ // just the body
+ for (const auto &attr : outer_attrs)
+ {
+ str += "\n " + attr.as_string ();
+ }
+ }
- void MacroMatcher::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\ntype " + name;
- void MacroRulesDefinition::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n Type param bounds: ";
+ if (!has_type_param_bounds ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &bound : type_param_bounds)
+ {
+ // DEBUG: null pointer check
+ if (bound == NULL)
+ {
+ fprintf (
+ stderr,
+ "something really terrible has gone wrong - null pointer "
+ "type param bound in trait item type.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + bound->as_string ();
+ }
+ }
- void MacroInvocation::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void LiteralPattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+SelfParam::as_string () const
+{
+ if (is_error ())
+ {
+ return "error";
+ }
+ else
+ {
+ if (has_type ())
+ {
+ // type (i.e. not ref, no lifetime)
+ ::std::string str;
+
+ if (is_mut)
+ {
+ str += "mut ";
+ }
+
+ str += "self : ";
+
+ str += type->as_string ();
+
+ return str;
+ }
+ else if (has_lifetime ())
+ {
+ // ref and lifetime
+ ::std::string str = "&" + lifetime.as_string () + " ";
+
+ if (is_mut)
+ {
+ str += "mut ";
+ }
+
+ str += "self";
+
+ return str;
+ }
+ else if (has_ref)
+ {
+ // ref with no lifetime
+ ::std::string str = "&";
+
+ if (is_mut)
+ {
+ str += " mut ";
+ }
+
+ str += "self";
+
+ return str;
+ }
+ else
+ {
+ // no ref, no type
+ ::std::string str;
+
+ if (is_mut)
+ {
+ str += "mut ";
+ }
+
+ str += "self";
+
+ return str;
+ }
+ }
+}
- void IdentifierPattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+ArrayElemsCopied::as_string () const
+{
+ return elem_to_copy->as_string () + "; " + num_copies->as_string ();
+}
- void WildcardPattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+LifetimeWhereClauseItem::as_string () const
+{
+ ::std::string str ("Lifetime: ");
- void RangePatternBoundLiteral::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += lifetime.as_string ();
- void RangePatternBoundPath::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\nLifetime bounds: ";
- void RangePatternBoundQualPath::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ for (const auto &bound : lifetime_bounds)
+ {
+ str += "\n " + bound.as_string ();
+ }
- void RangePattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void ReferencePattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+TypeBoundWhereClauseItem::as_string () const
+{
+ ::std::string str ("For lifetimes: ");
- void StructPatternFieldTuplePat::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (!has_for_lifetimes ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &for_lifetime : for_lifetimes)
+ {
+ str += "\n " + for_lifetime.as_string ();
+ }
+ }
- void StructPatternFieldIdentPat::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\nType: " + bound_type->as_string ();
- void StructPatternFieldIdent::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\nType param bounds bounds: ";
- void StructPattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ for (const auto &bound : type_param_bounds)
+ {
+ // debug null pointer check
+ if (bound == NULL)
+ {
+ return "NULL_POINTER_MARK - type param bounds";
+ }
- void TupleStructItemsNoRange::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += "\n " + bound->as_string ();
+ }
- void TupleStructItemsRange::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void TupleStructPattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+ArrayElemsValues::as_string () const
+{
+ ::std::string str;
+
+ for (const auto &expr : values)
+ {
+ // DEBUG: null pointer check
+ if (expr == NULL)
+ {
+ fprintf (stderr,
+ "something really terrible has gone wrong - null pointer "
+ "expr in array elems values.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + expr->as_string ();
+ }
- void TuplePatternItemsMultiple::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void TuplePatternItemsRanged::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+MaybeNamedParam::as_string () const
+{
+ ::std::string str;
+
+ switch (param_kind)
+ {
+ case UNNAMED:
+ break;
+ case IDENTIFIER:
+ str = name + " : ";
+ break;
+ case WILDCARD:
+ str = "_ : ";
+ break;
+ default:
+ return "ERROR_MARK_STRING - maybe named param unrecognised param kind";
+ }
- void TuplePattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ str += param_type->as_string ();
- void GroupedPattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str;
+}
- void SlicePattern::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+MetaItemSeq::as_string () const
+{
+ ::std::string path_str = path.as_string () + "(";
- void EmptyStmt::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ auto i = seq.begin ();
+ auto e = seq.end ();
- void LetStmt::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ for (; i != e; i++)
+ {
+ path_str += (*i)->as_string ();
+ if (e != i + 1)
+ path_str += ", ";
+ }
- void ExprStmtWithoutBlock::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return path_str + ")";
+}
- void ExprStmtWithBlock::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+MetaListPaths::as_string () const
+{
+ ::std::string str = ident + "(";
- void TraitBound::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ auto i = paths.begin ();
+ auto e = paths.end ();
- void ImplTraitType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
- void TraitObjectType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str + ")";
+}
+
+::std::string
+MetaListNameValueStr::as_string () const
+{
+ ::std::string str = ident + "(";
- void ParenthesisedType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ auto i = strs.begin ();
+ auto e = strs.end ();
- void ImplTraitTypeOneBound::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+
+ return str + ")";
+}
- void TraitObjectTypeOneBound::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::string
+AttrInputMetaItemContainer::as_string () const
+{
+ ::std::string str = "(";
- void TupleType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ auto i = items.begin ();
+ auto e = items.end ();
- void NeverType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
- void RawPointerType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ return str + ")";
+}
- void ReferenceType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+// Override that calls the function recursively on all items contained within
+// the module.
+void
+ModuleBodied::add_crate_name (::std::vector< ::std::string> &names) const
+{
+ /* TODO: test whether module has been 'cfg'-ed out to determine whether to
+ * exclude it from search */
+
+ for (const auto &item : items)
+ {
+ item->add_crate_name (names);
+ }
+}
- void ArrayType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+void
+Attribute::parse_attr_to_meta_item ()
+{
+ // only parse if has attribute input
+ if (!has_attr_input ())
+ {
+ return;
+ }
- void SliceType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ ::std::unique_ptr<AttrInput> converted_input (
+ attr_input->parse_to_meta_item ());
- void InferredType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (converted_input != NULL)
+ {
+ attr_input = ::std::move (converted_input);
+ }
+}
- void BareFunctionType::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+AttrInput *
+DelimTokenTree::parse_to_meta_item () const
+{
+ // must have token trees
+ if (token_trees.empty ())
+ {
+ return NULL;
+ }
- void MetaItemSeq::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // assume top-level delim token tree in attribute - convert all nested ones
+ // to token stream
+ ::std::vector< ::std::unique_ptr<Token> > token_stream = to_token_stream ();
+
+ // TODO: replace this with a specialised converter that the token stream is
+ // moved into
+ /*int i = 0;
+ ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items(
+ parse_meta_item_seq(token_stream, i));*/
+ // something like:
+ MacroParser parser (::std::move (token_stream));
+ ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items (
+ parser.parse_meta_item_seq ());
+
+ return new AttrInputMetaItemContainer (::std::move (meta_items));
+}
- void MetaItemPath::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+::std::unique_ptr<MetaItemInner>
+MacroParser::parse_meta_item_inner ()
+{
+ // if first tok not identifier, not a "special" case one
+ if (peek_token ()->get_id () != IDENTIFIER)
+ {
+ switch (peek_token ()->get_id ())
+ {
+ case CHAR_LITERAL:
+ case STRING_LITERAL:
+ case BYTE_CHAR_LITERAL:
+ case BYTE_STRING_LITERAL:
+ case INT_LITERAL:
+ case FLOAT_LITERAL:
+ case TRUE_LITERAL:
+ case FALSE_LITERAL:
+ // stream_pos++;
+ return parse_meta_item_lit ();
+ case SUPER:
+ case SELF:
+ case CRATE:
+ case DOLLAR_SIGN:
+ case SCOPE_RESOLUTION:
+ {
+ return parse_path_meta_item ();
+ }
+ default:
+ rust_error_at (peek_token ()->get_locus (),
+ "unrecognised token '%s' in meta item",
+ get_token_description (peek_token ()->get_id ()));
+ return NULL;
+ }
+ }
- void MetaListPaths::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // else, check for path
+ if (peek_token (1)->get_id () == SCOPE_RESOLUTION)
+ {
+ // path
+ return parse_path_meta_item ();
+ }
- void MetaNameValueStr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ Identifier ident = peek_token ()->as_string ();
+ if (is_end_meta_item_tok (peek_token (1)->get_id ()))
+ {
+ // meta word syntax
+ skip_token ();
+ return ::std::unique_ptr<MetaWord> (new MetaWord (::std::move (ident)));
+ }
- void MetaListNameValueStr::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (peek_token (1)->get_id () == EQUAL)
+ {
+ // maybe meta name value str syntax - check next 2 tokens
+ if (peek_token (2)->get_id () == STRING_LITERAL
+ && is_end_meta_item_tok (peek_token (3)->get_id ()))
+ {
+ // meta name value str syntax
+ ::std::string value = peek_token (2)->as_string ();
+
+ skip_token (2);
+
+ return ::std::unique_ptr<MetaNameValueStr> (
+ new MetaNameValueStr (::std::move (ident), ::std::move (value)));
+ }
+ else
+ {
+ // just interpret as path-based meta item
+ return parse_path_meta_item ();
+ }
+ }
- void AttrInputMetaItemContainer::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ if (peek_token (1)->get_id () != LEFT_PAREN)
+ {
+ rust_error_at (peek_token (1)->get_locus (),
+ "unexpected token '%s' after identifier in attribute",
+ get_token_description (peek_token (1)->get_id ()));
+ return NULL;
+ }
- void MetaWord::accept_vis(ASTVisitor& vis) {
- vis.visit(*this);
- }
+ // HACK: parse parenthesised sequence, and then try conversions to other
+ // stuff
+ ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items
+ = parse_meta_item_seq ();
+
+ // pass for meta name value str
+ ::std::vector<MetaNameValueStr> meta_name_value_str_items;
+ for (const auto &item : meta_items)
+ {
+ ::std::unique_ptr<MetaNameValueStr> converted_item (
+ item->to_meta_name_value_str ());
+ if (converted_item == NULL)
+ {
+ meta_name_value_str_items.clear ();
+ break;
+ }
+ meta_name_value_str_items.push_back (::std::move (*converted_item));
+ }
+ // if valid, return this
+ if (!meta_name_value_str_items.empty ())
+ {
+ return ::std::unique_ptr<MetaListNameValueStr> (
+ new MetaListNameValueStr (::std::move (ident),
+ ::std::move (meta_name_value_str_items)));
}
+
+ // pass for meta list idents
+ /*::std::vector<Identifier> ident_items;
+ for (const auto& item : meta_items) {
+ ::std::unique_ptr<Identifier> converted_ident(item->to_ident_item());
+ if (converted_ident == NULL) {
+ ident_items.clear();
+ break;
+ }
+ ident_items.push_back(::std::move(*converted_ident));
+ }
+ // if valid return this
+ if (!ident_items.empty()) {
+ return ::std::unique_ptr<MetaListIdents>(new
+ MetaListIdents(::std::move(ident),
+ ::std::move(ident_items)));
+ }*/
+ // as currently no meta list ident, currently no path. may change in future
+
+ // pass for meta list paths
+ ::std::vector<SimplePath> path_items;
+ for (const auto &item : meta_items)
+ {
+ SimplePath converted_path (item->to_path_item ());
+ if (converted_path.is_empty ())
+ {
+ path_items.clear ();
+ break;
+ }
+ path_items.push_back (::std::move (converted_path));
+ }
+ if (!path_items.empty ())
+ {
+ return ::std::unique_ptr<MetaListPaths> (
+ new MetaListPaths (::std::move (ident), ::std::move (path_items)));
+ }
+
+ rust_error_at (Linemap::unknown_location (),
+ "failed to parse any meta item inner");
+ return NULL;
+}
+
+bool
+MacroParser::is_end_meta_item_tok (TokenId id) const
+{
+ return id == COMMA || id == RIGHT_PAREN;
+}
+
+::std::unique_ptr<MetaItem>
+MacroParser::parse_path_meta_item ()
+{
+ SimplePath path = parse_simple_path ();
+ if (path.is_empty ())
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "failed to parse simple path in attribute");
+ return NULL;
+ }
+
+ switch (peek_token ()->get_id ())
+ {
+ case LEFT_PAREN:
+ {
+ ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items
+ = parse_meta_item_seq ();
+
+ return ::std::unique_ptr<MetaItemSeq> (
+ new MetaItemSeq (::std::move (path), ::std::move (meta_items)));
+ }
+ case EQUAL:
+ {
+ skip_token ();
+
+ Location locus = peek_token ()->get_locus ();
+ Literal lit = parse_literal ();
+ if (lit.is_error ())
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "failed to parse literal in attribute");
+ return NULL;
+ }
+ LiteralExpr expr (::std::move (lit), locus);
+ // stream_pos++;
+ // shouldn't be required anymore due to parsing literal actually
+ // skipping the token
+ return ::std::unique_ptr<MetaItemPathLit> (
+ new MetaItemPathLit (::std::move (path), ::std::move (expr)));
+ }
+ case COMMA:
+ // just simple path
+ return ::std::unique_ptr<MetaItemPath> (
+ new MetaItemPath (::std::move (path)));
+ default:
+ rust_error_at (peek_token ()->get_locus (),
+ "unrecognised token '%s' in meta item",
+ get_token_description (peek_token ()->get_id ()));
+ return NULL;
+ }
+}
+
+// Parses a parenthesised sequence of meta item inners. Parentheses are
+// required here.
+::std::vector< ::std::unique_ptr<MetaItemInner> >
+MacroParser::parse_meta_item_seq ()
+{
+ if (stream_pos != 0)
+ {
+ // warning?
+ fprintf (stderr,
+ "WARNING: stream pos for parse_meta_item_seq is not 0!\n");
+ }
+
+ // int i = 0;
+ int vec_length = token_stream.size ();
+ ::std::vector< ::std::unique_ptr<MetaItemInner> > meta_items;
+
+ if (peek_token ()->get_id () != LEFT_PAREN)
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "missing left paren in delim token tree");
+ return {};
+ }
+ skip_token ();
+
+ while (stream_pos < vec_length && peek_token ()->get_id () != RIGHT_PAREN)
+ {
+ ::std::unique_ptr<MetaItemInner> inner = parse_meta_item_inner ();
+ if (inner == NULL)
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "failed to parse inner meta item in attribute");
+ return {};
+ }
+ meta_items.push_back (::std::move (inner));
+
+ if (peek_token ()->get_id () != COMMA)
+ {
+ break;
+ }
+ skip_token ();
+ }
+
+ if (peek_token ()->get_id () != RIGHT_PAREN)
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "missing right paren in delim token tree");
+ return {};
+ }
+ skip_token ();
+
+ return meta_items;
+}
+
+// Collects any nested token trees into a flat token stream, suitable for
+// parsing.
+::std::vector< ::std::unique_ptr<Token> >
+DelimTokenTree::to_token_stream () const
+{
+ ::std::vector< ::std::unique_ptr<Token> > tokens;
+
+ // simulate presence of delimiters
+ tokens.push_back (::std::unique_ptr<Token> (
+ new Token (LEFT_PAREN, Linemap::unknown_location (), "",
+ CORETYPE_UNKNOWN)));
+
+ for (const auto &tree : token_trees)
+ {
+ ::std::vector< ::std::unique_ptr<Token> > stream
+ = tree->to_token_stream ();
+
+ tokens.insert (tokens.end (), ::std::make_move_iterator (stream.begin ()),
+ ::std::make_move_iterator (stream.end ()));
+ }
+
+ tokens.push_back (::std::unique_ptr<Token> (
+ new Token (RIGHT_PAREN, Linemap::unknown_location (), "",
+ CORETYPE_UNKNOWN)));
+
+ tokens.shrink_to_fit ();
+
+ return tokens;
+}
+
+Literal
+MacroParser::parse_literal ()
+{
+ const ::std::unique_ptr<Token> &tok = peek_token ();
+ switch (tok->get_id ())
+ {
+ case CHAR_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::CHAR);
+ case STRING_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::STRING);
+ case BYTE_CHAR_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::BYTE);
+ case BYTE_STRING_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::BYTE_STRING);
+ case INT_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::INT);
+ case FLOAT_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::FLOAT);
+ case TRUE_LITERAL:
+ skip_token ();
+ return Literal ("true", Literal::BOOL);
+ case FALSE_LITERAL:
+ skip_token ();
+ return Literal ("false", Literal::BOOL);
+ default:
+ rust_error_at (tok->get_locus (), "expected literal - found '%s'",
+ get_token_description (tok->get_id ()));
+ return Literal::create_error ();
+ }
+}
+
+SimplePath
+MacroParser::parse_simple_path ()
+{
+ bool has_opening_scope_res = false;
+ if (peek_token ()->get_id () == SCOPE_RESOLUTION)
+ {
+ has_opening_scope_res = true;
+ skip_token ();
+ }
+
+ ::std::vector<SimplePathSegment> segments;
+
+ SimplePathSegment segment = parse_simple_path_segment ();
+ if (segment.is_error ())
+ {
+ rust_error_at (
+ peek_token ()->get_locus (),
+ "failed to parse simple path segment in attribute simple path");
+ return SimplePath::create_empty ();
+ }
+ segments.push_back (::std::move (segment));
+
+ while (peek_token ()->get_id () == SCOPE_RESOLUTION)
+ {
+ skip_token ();
+
+ SimplePathSegment segment = parse_simple_path_segment ();
+ if (segment.is_error ())
+ {
+ rust_error_at (
+ peek_token ()->get_locus (),
+ "failed to parse simple path segment in attribute simple path");
+ return SimplePath::create_empty ();
+ }
+ segments.push_back (::std::move (segment));
+ }
+ segments.shrink_to_fit ();
+
+ return SimplePath (::std::move (segments), has_opening_scope_res);
+}
+
+SimplePathSegment
+MacroParser::parse_simple_path_segment ()
+{
+ const ::std::unique_ptr<Token> &tok = peek_token ();
+ switch (tok->get_id ())
+ {
+ case IDENTIFIER:
+ skip_token ();
+ return SimplePathSegment (tok->as_string (), tok->get_locus ());
+ case SUPER:
+ skip_token ();
+ return SimplePathSegment ("super", tok->get_locus ());
+ case SELF:
+ skip_token ();
+ return SimplePathSegment ("self", tok->get_locus ());
+ case CRATE:
+ skip_token ();
+ return SimplePathSegment ("crate", tok->get_locus ());
+ case DOLLAR_SIGN:
+ if (peek_token (1)->get_id () == CRATE)
+ {
+ skip_token (1);
+ return SimplePathSegment ("$crate", tok->get_locus ());
+ }
+ gcc_fallthrough ();
+ default:
+ rust_error_at (tok->get_locus (),
+ "unexpected token '%s' in simple path segment",
+ get_token_description (tok->get_id ()));
+ return SimplePathSegment::create_error ();
+ }
+}
+
+::std::unique_ptr<MetaItemLitExpr>
+MacroParser::parse_meta_item_lit ()
+{
+ Location locus = peek_token ()->get_locus ();
+ LiteralExpr lit_expr (parse_literal (), locus);
+ return ::std::unique_ptr<MetaItemLitExpr> (
+ new MetaItemLitExpr (::std::move (lit_expr)));
+}
+
+bool
+AttrInputMetaItemContainer::check_cfg_predicate (const Session &session) const
+{
+ // cfg value of container is purely based on cfg of each inner item - all
+ // must be true
+ for (const auto &inner_item : items)
+ {
+ if (!inner_item->check_cfg_predicate (session))
+ {
+ return false;
+ }
+ }
+
+ /* TODO: as far as I can tell, there should only be a single element to
+ * check here, so ensure there is only a single element in items too? */
+
+ return true;
+}
+
+bool
+MetaItemLitExpr::check_cfg_predicate (
+ const Session &session ATTRIBUTE_UNUSED) const
+{
+ // as far as I can tell, a literal expr can never be a valid cfg body, so
+ // false
+ return false;
+}
+
+bool
+MetaListNameValueStr::check_cfg_predicate (const Session &session) const
+{
+ if (ident == "all")
+ {
+ for (const auto &str : strs)
+ {
+ if (!str.check_cfg_predicate (session))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (ident == "any")
+ {
+ for (const auto &str : strs)
+ {
+ if (str.check_cfg_predicate (session))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ else if (ident == "not")
+ {
+ if (strs.size () != 1)
+ {
+ // HACK: convert vector platform-dependent size_type to string to
+ // use in printf
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for "
+ "MetaListNameValueStr with ident of "
+ "'not' because there are '%s' elements, not '1'",
+ ::std::to_string (strs.size ()).c_str ());
+ return false;
+ }
+
+ return !strs[0].check_cfg_predicate (session);
+ }
+ else
+ {
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for "
+ "MetaListNameValueStr with ident of "
+ "'%s' - ident must be 'all' or 'any'",
+ ident.c_str ());
+ return false;
+ }
+}
+
+bool
+MetaListPaths::check_cfg_predicate (const Session &session) const
+{
+ if (ident == "all")
+ {
+ for (const auto &path : paths)
+ {
+ if (!check_path_exists_in_cfg (session, path))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (ident == "any")
+ {
+ for (const auto &path : paths)
+ {
+ if (check_path_exists_in_cfg (session, path))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ else if (ident == "not")
+ {
+ if (paths.size () != 1)
+ {
+ // HACK: convert vector platform-dependent size_type to string to
+ // use in printf
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for MetaListPaths "
+ "with ident of 'not' "
+ "because there are '%s' elements, not '1'",
+ ::std::to_string (paths.size ()).c_str ());
+ return false;
+ }
+
+ return !check_path_exists_in_cfg (session, paths[0]);
+ }
+ else
+ {
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for "
+ "MetaListNameValueStr with ident of "
+ "'%s' - ident must be 'all' or 'any'",
+ ident.c_str ());
+ return false;
+ }
+}
+
+bool
+MetaListPaths::check_path_exists_in_cfg (const Session &session,
+ const SimplePath &path) const
+{
+ auto it = session.options.target_data.features.find (path.as_string ());
+ if (it != session.options.target_data.features.end ())
+ {
+ return true;
+ }
+ return false;
+}
+
+bool
+MetaItemSeq::check_cfg_predicate (const Session &session) const
+{
+ if (path.as_string () == "all")
+ {
+ for (const auto &item : seq)
+ {
+ if (!item->check_cfg_predicate (session))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (path.as_string () == "any")
+ {
+ for (const auto &item : seq)
+ {
+ if (item->check_cfg_predicate (session))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ else if (path.as_string () == "not")
+ {
+ if (seq.size () != 1)
+ {
+ // HACK: convert vector platform-dependent size_type to string to
+ // use in printf
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for MetaItemSeq "
+ "with ident of 'not' "
+ "because there are '%s' elements, not '1'",
+ ::std::to_string (seq.size ()).c_str ());
+ return false;
+ }
+
+ return !seq[0]->check_cfg_predicate (session);
+ }
+ else
+ {
+ rust_error_at (
+ Linemap::unknown_location (),
+ "cfg predicate could not be checked for MetaItemSeq with path of "
+ "'%s' - path must be 'all' or 'any'",
+ path.as_string ().c_str ());
+ return false;
+ }
+}
+
+bool
+MetaWord::check_cfg_predicate (const Session &session) const
+{
+ auto it = session.options.target_data.features.find (ident);
+ if (it != session.options.target_data.features.end ())
+ {
+ return true;
+ }
+ return false;
+}
+
+bool
+MetaItemPath::check_cfg_predicate (const Session &session) const
+{
+ /* Strictly speaking, this should always be false, but maybe do check
+ * relating to SimplePath being identifier. Currently, it would return true
+ * if path as identifier existed, and if the path in string form existed
+ * (though this shouldn't occur). */
+ auto it = session.options.target_data.features.find (path.as_string ());
+ if (it != session.options.target_data.features.end ())
+ {
+ return true;
+ }
+ return false;
+}
+
+bool
+MetaNameValueStr::check_cfg_predicate (const Session &session) const
+{
+ return session.options.target_data.has_key_value_pair (ident, str);
+}
+
+bool
+MetaItemPathLit::check_cfg_predicate (const Session &session) const
+{
+ return session.options.target_data.has_key_value_pair (path.as_string (),
+ lit.as_string ());
+}
+
+::std::vector< ::std::unique_ptr<Token> >
+Token::to_token_stream () const
+{
+ // initialisation list doesn't work as it needs copy constructor, so have to
+ // do this
+ ::std::vector< ::std::unique_ptr<Token> > dummy_vector;
+ dummy_vector.reserve (1);
+ dummy_vector.push_back (::std::unique_ptr<Token> (clone_token_impl ()));
+ return dummy_vector;
+}
+
+/* Visitor implementations - these are short but inlining can't happen anyway
+ * due to virtual functions and I didn't want to make the ast header includes
+ * any longer than they already are. */
+
+void
+Token::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+DelimTokenTree::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IdentifierExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Lifetime::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LifetimeParam::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroInvocationSemi::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePathSegment::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePathSegmentGeneric::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePathSegmentFunction::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AttrInputLiteral::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemLitExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemPathLit::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayElemsValues::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayElemsCopied::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprStructBase::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprTuple::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprUnit::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumExprFieldIdentifier::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumExprFieldIdentifierValue::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumExprFieldIndexValue::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumExprStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumExprTuple::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumExprFieldless::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MatchCaseBlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MatchCaseExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeParam::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LifetimeWhereClauseItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeBoundWhereClauseItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Method::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ModuleBodied::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ModuleNoBody::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseTreeGlob::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseTreeList::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseTreeRebind::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Function::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Enum::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Union::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemMethod::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Trait::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+InherentImpl::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitImpl::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroMatchFragment::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroMatchRepetition::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroMatcher::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroRulesDefinition::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroInvocation::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePatternBoundLiteral::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePatternBoundPath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePatternBoundQualPath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPatternFieldTuplePat::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPatternFieldIdentPat::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPatternFieldIdent::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStructItemsNoRange::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStructItemsRange::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TuplePatternItemsMultiple::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TuplePatternItemsRanged::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitBound::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitObjectTypeOneBound::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemSeq::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemPath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaListPaths::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaNameValueStr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaListNameValueStr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AttrInputMetaItemContainer::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaWord::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
}
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h
index 8d732a7..669c262 100644
--- a/gcc/rust/ast/rust-item.h
+++ b/gcc/rust/ast/rust-item.h
@@ -1,3384 +1,3702 @@
#ifndef RUST_AST_ITEM_H
#define RUST_AST_ITEM_H
+/*
+Copyright (C) 2009-2020 Free Software Foundation, Inc.
-#include "rust-ast.h"
-#include "rust-path.h"
-
-namespace Rust {
- namespace AST {
- // forward decls
- // struct Lifetime;
- // struct LifetimeBounds;
- // struct TypeParamBounds;
- class BlockExpr;
- // class Expr;
- // class Type;
- class TypePath;
- // class Pattern;
- class MacroInvocationSemi;
-
- // TODO: inline?
- /*struct AbiName {
- ::std::string abi_name;
- // Technically is meant to be STRING_LITERAL or RAW_STRING_LITERAL
-
- public:
- // Returns whether abi name is empty, i.e. doesn't exist.
- inline bool is_empty() const {
- return abi_name.empty();
- }
-
- AbiName(::std::string name) : abi_name(::std::move(name)) {}
-
- // Empty AbiName constructor
- AbiName() {}
- };*/
-
- // A type generic parameter (as opposed to a lifetime generic parameter)
- class TypeParam : public GenericParam {
- // bool has_outer_attribute;
- //::std::unique_ptr<Attribute> outer_attr;
- Attribute outer_attr;
-
- Identifier type_representation;
-
- // bool has_type_param_bounds;
- // TypeParamBounds type_param_bounds;
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds; // inlined form
-
- // bool has_type;
- // Type type;
- ::std::unique_ptr<Type> type;
-
- Location locus;
-
- public:
- // Returns whether the type of the type param has been specified.
- inline bool has_type() const {
- return type != NULL;
- }
-
- // Returns whether the type param has type param bounds.
- inline bool has_type_param_bounds() const {
- return !type_param_bounds.empty();
- }
-
- // Returns whether the type param has an outer attribute.
- inline bool has_outer_attribute() const {
- return !outer_attr.is_empty();
- }
-
- TypeParam(Identifier type_representation, Location locus = Location(),
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds
- = ::std::vector< ::std::unique_ptr<TypeParamBound> >(),
- ::std::unique_ptr<Type> type = NULL, Attribute outer_attr = Attribute::create_empty()) :
- outer_attr(::std::move(outer_attr)),
- type_representation(::std::move(type_representation)),
- type_param_bounds(::std::move(type_param_bounds)), type(::std::move(type)),
- locus(locus) {}
-
- // Copy constructor uses clone
- TypeParam(TypeParam const& other) :
- outer_attr(other.outer_attr), type_representation(other.type_representation),
- /*type_param_bounds(other.type_param_bounds),*/ type(other.type->clone_type()),
- locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
- }
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- TypeParam& operator=(TypeParam const& other) {
- type_representation = other.type_representation;
- // type_param_bounds = other.type_param_bounds;
- type = other.type->clone_type();
- outer_attr = other.outer_attr;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
-
- return *this;
- }
-
- // move constructors
- TypeParam(TypeParam&& other) = default;
- TypeParam& operator=(TypeParam&& other) = default;
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual TypeParam* clone_generic_param_impl() const {
- return new TypeParam(*this);
- }
- };
-
- // "where" clause item base. Abstract - use LifetimeWhereClauseItem, TypeBoundWhereClauseItem
- class WhereClauseItem {
- public:
- virtual ~WhereClauseItem() {}
-
- // Unique pointer custom clone function
- ::std::unique_ptr<WhereClauseItem> clone_where_clause_item() const {
- return ::std::unique_ptr<WhereClauseItem>(clone_where_clause_item_impl());
- }
-
- virtual ::std::string as_string() const = 0;
-
- virtual void accept_vis(ASTVisitor& vis) = 0;
-
- protected:
- // Clone function implementation as pure virtual method
- virtual WhereClauseItem* clone_where_clause_item_impl() const = 0;
- };
-
- // A lifetime where clause item
- class LifetimeWhereClauseItem : public WhereClauseItem {
- Lifetime lifetime;
-
- // LifetimeBounds lifetime_bounds;
- ::std::vector<Lifetime> lifetime_bounds; // inlined lifetime bounds
-
- // should this store location info?
-
- public:
- LifetimeWhereClauseItem(Lifetime lifetime, ::std::vector<Lifetime> lifetime_bounds) :
- lifetime(::std::move(lifetime)), lifetime_bounds(::std::move(lifetime_bounds)) {}
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual LifetimeWhereClauseItem* clone_where_clause_item_impl() const {
- return new LifetimeWhereClauseItem(*this);
- }
- };
-
- // A type bound where clause item
- class TypeBoundWhereClauseItem : public WhereClauseItem {
- // bool has_for_lifetimes;
- // LifetimeParams for_lifetimes;
- ::std::vector<LifetimeParam> for_lifetimes; // inlined
-
- // Type bound_type;
- ::std::unique_ptr<Type> bound_type;
-
- // bool has_type_param_bounds;
- // TypeParamBounds type_param_bounds;
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds; // inlined form
-
- // should this store location info?
-
- public:
- // Returns whether the item has ForLifetimes
- inline bool has_for_lifetimes() const {
- return !for_lifetimes.empty();
- }
-
- // Returns whether the item has type param bounds
- inline bool has_type_param_bounds() const {
- return !type_param_bounds.empty();
- }
-
- TypeBoundWhereClauseItem(::std::vector<LifetimeParam> for_lifetimes,
- ::std::unique_ptr<Type> bound_type,
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds) :
- for_lifetimes(::std::move(for_lifetimes)),
- bound_type(::std::move(bound_type)), type_param_bounds(::std::move(type_param_bounds)) {
- }
-
- // Copy constructor requires clone
- TypeBoundWhereClauseItem(TypeBoundWhereClauseItem const& other) :
- for_lifetimes(other.for_lifetimes), bound_type(other.bound_type->clone_type()) /*,
- type_param_bounds(other.type_param_bounds)*/
- {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
- }
-
- // Destructor - define here if required
-
- // Overload assignment operator to clone
- TypeBoundWhereClauseItem& operator=(TypeBoundWhereClauseItem const& other) {
- for_lifetimes = other.for_lifetimes;
- bound_type = other.bound_type->clone_type();
- // type_param_bounds = other.type_param_bounds;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
-
- return *this;
- }
-
- // move constructors
- TypeBoundWhereClauseItem(TypeBoundWhereClauseItem&& other) = default;
- TypeBoundWhereClauseItem& operator=(TypeBoundWhereClauseItem&& other) = default;
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual TypeBoundWhereClauseItem* clone_where_clause_item_impl() const {
- return new TypeBoundWhereClauseItem(*this);
- }
- };
-
- // A where clause
- struct WhereClause {
- private:
- //::std::vector<WhereClauseItem> where_clause_items;
- ::std::vector< ::std::unique_ptr<WhereClauseItem> > where_clause_items;
-
- // should this store location info?
-
- public:
- WhereClause(::std::vector< ::std::unique_ptr<WhereClauseItem> > where_clause_items) :
- where_clause_items(::std::move(where_clause_items)) {}
-
- // copy constructor with vector clone
- WhereClause(WhereClause const& other) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- where_clause_items.reserve(other.where_clause_items.size());
-
- for (const auto& e : other.where_clause_items) {
- where_clause_items.push_back(e->clone_where_clause_item());
- }
- }
-
- // overloaded assignment operator with vector clone
- WhereClause& operator=(WhereClause const& other) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- where_clause_items.reserve(other.where_clause_items.size());
-
- for (const auto& e : other.where_clause_items) {
- where_clause_items.push_back(e->clone_where_clause_item());
- }
-
- return *this;
- }
-
- // move constructors
- WhereClause(WhereClause&& other) = default;
- WhereClause& operator=(WhereClause&& other) = default;
-
- // Creates a WhereClause with no items.
- static WhereClause create_empty() {
- return WhereClause(::std::vector< ::std::unique_ptr<WhereClauseItem> >());
- }
-
- // Returns whether the WhereClause has no items.
- inline bool is_empty() const {
- return where_clause_items.empty();
- }
-
- ::std::string as_string() const;
- };
-
- // A self parameter in a method
- struct SelfParam {
- private:
- bool has_ref;
- bool is_mut;
- // bool has_lifetime; // only possible if also ref
- Lifetime lifetime;
-
- // bool has_type; // only possible if not ref
- // Type type;
- ::std::unique_ptr<Type> type;
-
- Location locus;
-
- // Unrestricted constructor used for error state
- SelfParam(Lifetime lifetime, bool has_ref, bool is_mut, Type* type) :
- has_ref(has_ref), is_mut(is_mut), lifetime(::std::move(lifetime)), type(type) {}
- // this is ok as no outside classes can ever call this
-
- public:
- // Returns whether the self-param has a type field.
- inline bool has_type() const {
- return type != NULL;
- }
-
- // Returns whether the self-param has a valid lifetime.
- inline bool has_lifetime() const {
- return !lifetime.is_error();
- }
-
- // Returns whether the self-param is in an error state.
- inline bool is_error() const {
- return has_type() && has_lifetime();
- // not having either is not an error
- }
-
- // Creates an error state self-param.
- static SelfParam create_error() {
- /* HACK: creates a dummy type. Since it's a unique pointer, it should clean it up, but
- * it still allocates memory, which is not ideal. */
- return SelfParam(Lifetime(Lifetime::STATIC), false, false,
- new QualifiedPathInType(QualifiedPathInType::create_error()));
- }
-
- // Type-based self parameter (not ref, no lifetime)
- SelfParam(::std::unique_ptr<Type> type, bool is_mut, Location locus) :
- has_ref(false), is_mut(is_mut), lifetime(Lifetime::error()), type(::std::move(type)),
- locus(locus) {}
-
- // Lifetime-based self parameter (is ref, no type)
- SelfParam(Lifetime lifetime, bool is_mut, Location locus) :
- /*type(NULL), */ has_ref(true), is_mut(is_mut), lifetime(::std::move(lifetime)),
- locus(locus) {}
-
- // Copy constructor requires clone
- SelfParam(SelfParam const& other) :
- has_ref(other.has_ref), is_mut(other.is_mut), lifetime(other.lifetime),
- type(other.type->clone_type()), locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overload assignment operator to use clone
- SelfParam& operator=(SelfParam const& other) {
- type = other.type->clone_type();
- is_mut = other.is_mut;
- has_ref = other.has_ref;
- lifetime = other.lifetime;
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- SelfParam(SelfParam&& other) = default;
- SelfParam& operator=(SelfParam&& other) = default;
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
- };
-
- // Qualifiers for function, i.e. const, unsafe, extern etc.
- struct FunctionQualifiers {
- public:
- // Whether the function is neither const nor async, const only, or async only.
- enum AsyncConstStatus { NONE, CONST, ASYNC };
-
- private:
- AsyncConstStatus const_status;
- bool has_unsafe;
- bool has_extern;
- ::std::string extern_abi; // e.g. extern "C" fn() -> i32 {}
- // TODO: maybe ensure that extern_abi only exists if extern exists?
-
- // should this store location info?
-
- public:
- // Constructor with no extern (and hence no extern abi)
- FunctionQualifiers(AsyncConstStatus const_status, bool has_unsafe) :
- const_status(const_status), has_unsafe(has_unsafe), has_extern(false),
- extern_abi(::std::string("")) {}
-
- // Constructor with extern abi (and thus extern)
- FunctionQualifiers(
- AsyncConstStatus const_status, bool has_unsafe, ::std::string extern_abi) :
- const_status(const_status),
- has_unsafe(has_unsafe), has_extern(true), extern_abi(::std::move(extern_abi)) {}
-
- // Constructor with all possible options (DON'T HAVE EXTERN_ABI WITHOUT EXTERN!)
- FunctionQualifiers(AsyncConstStatus const_status, bool has_unsafe, bool has_extern,
- ::std::string extern_abi) :
- const_status(const_status),
- has_unsafe(has_unsafe), has_extern(has_extern), extern_abi(::std::move(extern_abi)) {}
-
- ::std::string as_string() const;
- };
-
- // Forward decl FunctionParams
- // struct FunctionParams;
-
- // A function parameter
- struct FunctionParam {
- private:
- // Pattern* param_name;
- ::std::unique_ptr<Pattern> param_name;
- // Type type;
- ::std::unique_ptr<Type> type;
-
- Location locus;
-
- public:
- FunctionParam(::std::unique_ptr<Pattern> param_name, ::std::unique_ptr<Type> param_type,
- Location locus) :
- param_name(::std::move(param_name)),
- type(::std::move(param_type)), locus(locus) {}
-
- // Copy constructor uses clone
- FunctionParam(FunctionParam const& other) :
- param_name(other.param_name->clone_pattern()), type(other.type->clone_type()),
- locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overload assignment operator to use clone
- FunctionParam& operator=(FunctionParam const& other) {
- param_name = other.param_name->clone_pattern();
- type = other.type->clone_type();
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- FunctionParam(FunctionParam&& other) = default;
- FunctionParam& operator=(FunctionParam&& other) = default;
-
- // Returns whether FunctionParam is in an invalid state.
- inline bool is_error() const {
- return param_name == NULL || type == NULL;
- }
-
- // Creates an error FunctionParam.
- static FunctionParam create_error() {
- return FunctionParam(NULL, NULL, Location());
- }
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
- };
-
- // Visibility of item - if the item has it, then it is some form of public
- struct Visibility {
- public:
- enum PublicVisType { NONE, CRATE, SELF, SUPER, IN_PATH };
-
- private:
- // bool is_pub;
-
- // if vis is public, one of these
- PublicVisType public_vis_type;
-
- // Only assigned if public_vis_type is IN_PATH
- SimplePath in_path;
-
- // should this store location info?
-
- public:
- // Creates a Visibility - TODO make constructor protected or private?
- Visibility(PublicVisType public_vis_type, SimplePath in_path) :
- public_vis_type(public_vis_type), in_path(::std::move(in_path)) {
- if (public_vis_type != IN_PATH && !in_path.is_empty()) {
- // error - invalid state
-
- // just ignore path if vis type is not that
- }
- }
-
- // Returns whether visibility is in an error state.
- inline bool is_error() const {
- return public_vis_type == IN_PATH && in_path.is_empty();
- }
-
- // Creates an error visibility.
- static Visibility create_error() {
- return Visibility(IN_PATH, SimplePath::create_empty());
- }
-
- // Unique pointer custom clone function
- /*::std::unique_ptr<Visibility> clone_visibility() const {
- return ::std::unique_ptr<Visibility>(clone_visibility_impl());
- }*/
-
- /* TODO: think of a way to only allow valid Visibility states - polymorphism is one
- * idea but may be too resource-intensive. */
-
- // Creates a public visibility with no further features/arguments.
- static Visibility create_public() {
- return Visibility(NONE, SimplePath::create_empty());
- }
-
- // Creates a public visibility with crate-relative paths or whatever.
- static Visibility create_crate() {
- return Visibility(CRATE, SimplePath::create_empty());
- }
-
- // Creates a public visibility with self-relative paths or whatever.
- static Visibility create_self() {
- return Visibility(SELF, SimplePath::create_empty());
- }
-
- // Creates a public visibility with parent module-relative paths or whatever.
- static Visibility create_super() {
- return Visibility(SUPER, SimplePath::create_empty());
- }
-
- // Creates a public visibility with a given path or whatever.
- static Visibility create_in_path(SimplePath in_path) {
- return Visibility(IN_PATH, ::std::move(in_path));
- }
-
- ::std::string as_string() const;
-
- protected:
- // Clone function implementation - not currently virtual but may be if polymorphism used
- /*virtual*/ Visibility* clone_visibility_impl() const {
- return new Visibility(*this);
- }
- };
-
- // A method (function belonging to a type)
- class Method
- : public InherentImplItem
- , public TraitImplItem {
- // moved from impl items for consistency
- ::std::vector<Attribute> outer_attrs;
- Visibility vis;
-
- FunctionQualifiers qualifiers;
- Identifier method_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- SelfParam self_param;
-
- // bool has_params;
- // FunctionParams function_params;
- ::std::vector<FunctionParam> function_params; // inlined
-
- // bool has_return_type;
- // FunctionReturnType return_type;
- ::std::unique_ptr<Type> return_type; // inlined
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // BlockExpr* expr;
- ::std::unique_ptr<BlockExpr> expr;
-
- Location locus;
-
- public:
- /*~Method() {
- delete expr;
- }*/
-
- // Returns whether the method is in an error state.
- inline bool is_error() const {
- return expr == NULL || method_name.empty() || self_param.is_error();
- }
-
- // Creates an error state method.
- static Method create_error() {
- return Method("", FunctionQualifiers(FunctionQualifiers::NONE, true),
- ::std::vector< ::std::unique_ptr<GenericParam> >(), SelfParam::create_error(),
- ::std::vector<FunctionParam>(), NULL, WhereClause::create_empty(), NULL,
- Visibility::create_error(), ::std::vector<Attribute>());
- }
-
- // Returns whether the method has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether the method has parameters.
- inline bool has_params() const {
- return !function_params.empty();
- }
-
- // Returns whether the method has a return type (void otherwise).
- inline bool has_return_type() const {
- return return_type != NULL;
- }
-
- // Returns whether the where clause exists (i.e. has items)
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Returns whether method has a non-default visibility.
- inline bool has_visibility() const {
- return !vis.is_error();
- }
-
- // Mega-constructor with all possible fields
- Method(Identifier method_name, FunctionQualifiers qualifiers,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params, SelfParam self_param,
- ::std::vector<FunctionParam> function_params, ::std::unique_ptr<Type> return_type,
- WhereClause where_clause, ::std::unique_ptr<BlockExpr> function_body, Visibility vis,
- ::std::vector<Attribute> outer_attrs, Location locus = Location()) :
- outer_attrs(::std::move(outer_attrs)),
- vis(::std::move(vis)), qualifiers(::std::move(qualifiers)),
- method_name(::std::move(method_name)), generic_params(::std::move(generic_params)),
- self_param(::std::move(self_param)), function_params(::std::move(function_params)),
- return_type(::std::move(return_type)), where_clause(::std::move(where_clause)),
- expr(::std::move(function_body)), locus(locus) {}
-
- // TODO: add constructor with less fields
-
- // Copy constructor with clone
- Method(Method const& other) :
- outer_attrs(other.outer_attrs), vis(other.vis), qualifiers(other.qualifiers),
- method_name(other.method_name),
- /*generic_params(other.generic_params),*/ self_param(other.self_param),
- function_params(other.function_params), return_type(other.return_type->clone_type()),
- where_clause(other.where_clause), expr(other.expr->clone_block_expr()),
- locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- //~Method() = default;
-
- // Overloaded assignment operator to clone
- Method& operator=(Method const& other) {
- method_name = other.method_name;
- outer_attrs = other.outer_attrs;
- vis = other.vis;
- qualifiers = other.qualifiers;
- // generic_params = other.generic_params;
- self_param = other.self_param;
- function_params = other.function_params;
- return_type = other.return_type->clone_type();
- where_clause = other.where_clause;
- expr = other.expr->clone_block_expr();
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- Method(Method&& other) = default;
- Method& operator=(Method&& other) = default;
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual Method* clone_inherent_impl_item_impl() const OVERRIDE {
- return new Method(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- virtual Method* clone_trait_impl_item_impl() const OVERRIDE {
- return new Method(*this);
- }
- };
-
- // Item that supports visibility - abstract base class
- class VisItem : public Item {
- Visibility visibility;
-
- protected:
- // Visibility constructor
- VisItem(Visibility visibility,
- ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute>()) :
- Item(::std::move(outer_attrs)),
- visibility(::std::move(visibility)) {}
-
- // Visibility copy constructor
- VisItem(VisItem const& other) : Item(other), visibility(other.visibility) {}
-
- // Destructor - define here if required
-
- // Overload assignment operator to clone
- VisItem& operator=(VisItem const& other) {
- Item::operator=(other);
- visibility = other.visibility;
- // outer_attrs = other.outer_attrs;
-
- return *this;
- }
-
- // move constructors
- VisItem(VisItem&& other) = default;
- VisItem& operator=(VisItem&& other) = default;
-
- public:
- // Does the item have some kind of public visibility (non-default visibility)?
- inline bool has_visibility() const {
- return !visibility.is_error();
- }
-
- virtual ::std::string as_string() const;
- };
-
- // Rust module item - abstract base class
- class Module : public VisItem {
- Identifier module_name;
-
- Location locus;
-
- protected:
- // Protected constructor
- Module(Identifier module_name, Visibility visibility, Location locus,
- ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute>()) :
- VisItem(::std::move(visibility), ::std::move(outer_attrs)),
- module_name(module_name), locus(locus) {}
-
- public:
- virtual ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
- };
-
- // Module with a body, defined in file
- class ModuleBodied : public Module {
- // bool has_inner_attrs;
- ::std::vector<Attribute> inner_attrs;
- // bool has_items;
- //::std::vector<Item> items;
- ::std::vector< ::std::unique_ptr<Item> > items;
-
- public:
- virtual ::std::string as_string() const;
-
- // Returns whether the module has items in its body.
- inline bool has_items() const {
- return !items.empty();
- }
-
- // Returns whether the module has any inner attributes.
- inline bool has_inner_attrs() const {
- return !inner_attrs.empty();
- }
-
- // Full constructor
- ModuleBodied(Identifier name, Location locus,
- ::std::vector< ::std::unique_ptr<Item> > items
- = ::std::vector< ::std::unique_ptr<Item> >(),
- Visibility visibility = Visibility::create_error(),
- ::std::vector<Attribute> inner_attrs = ::std::vector<Attribute>(),
- ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute>()) :
- Module(::std::move(name), ::std::move(visibility), locus, ::std::move(outer_attrs)),
- inner_attrs(::std::move(inner_attrs)), items(::std::move(items)) {}
-
- // Copy constructor with vector clone
- ModuleBodied(ModuleBodied const& other) : Module(other), inner_attrs(other.inner_attrs) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- items.reserve(other.items.size());
-
- for (const auto& e : other.items) {
- items.push_back(e->clone_item());
- }
- }
-
- // Overloaded assignment operator with vector clone
- ModuleBodied& operator=(ModuleBodied const& other) {
- Module::operator=(other);
- inner_attrs = other.inner_attrs;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- items.reserve(other.items.size());
-
- for (const auto& e : other.items) {
- items.push_back(e->clone_item());
- }
-
- return *this;
- }
-
- // move constructors
- ModuleBodied(ModuleBodied&& other) = default;
- ModuleBodied& operator=(ModuleBodied&& other) = default;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- // Override that runs the function recursively on all items contained within the module.
- virtual void add_crate_name(::std::vector< ::std::string>& names) const OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ModuleBodied* clone_item_impl() const OVERRIDE {
- return new ModuleBodied(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual ModuleBodied* clone_statement_impl() const OVERRIDE {
- return new ModuleBodied(*this);
- }*/
- };
-
- // Module without a body, loaded from external file
- class ModuleNoBody : public Module {
- public:
- ::std::string as_string() const;
-
- // Full constructor
- ModuleNoBody(Identifier name, Visibility visibility, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- Module(::std::move(name), ::std::move(visibility), locus, ::std::move(outer_attrs)) {}
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ModuleNoBody* clone_item_impl() const OVERRIDE {
- return new ModuleNoBody(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual ModuleNoBody* clone_statement_impl() const OVERRIDE {
- return new ModuleNoBody(*this);
- }*/
- };
-
- // Rust extern crate declaration AST node
- class ExternCrate : public VisItem {
- // this is either an identifier or "self", with self parsed to string
- ::std::string referenced_crate;
- // bool has_as_clause;
- // AsClause as_clause;
- // this is either an identifier or "_", with _ parsed to string
- ::std::string as_clause_name;
-
- Location locus;
-
- /* e.g.
- "extern crate foo as _"
- "extern crate foo"
- "extern crate std as cool_std" */
- public:
- ::std::string as_string() const;
-
- // Returns whether extern crate declaration has an as clause.
- inline bool has_as_clause() const {
- return !as_clause_name.empty();
- }
-
- // Returns whether extern crate declaration references the current crate (i.e. self).
- inline bool references_self() const {
- return referenced_crate == "self";
- }
-
- // Constructor
- ExternCrate(::std::string referenced_crate, Visibility visibility,
- ::std::vector<Attribute> outer_attrs, Location locus,
- ::std::string as_clause_name = ::std::string()) :
- VisItem(::std::move(visibility), ::std::move(outer_attrs)),
- referenced_crate(::std::move(referenced_crate)),
- as_clause_name(::std::move(as_clause_name)), locus(locus) {}
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- // Override that adds extern crate name in decl to passed list of names.
- virtual void add_crate_name(::std::vector< ::std::string>& names) const OVERRIDE {
- names.push_back(referenced_crate);
- }
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ExternCrate* clone_item_impl() const OVERRIDE {
- return new ExternCrate(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual ExternCrate* clone_statement_impl() const OVERRIDE {
- return new ExternCrate(*this);
- }*/
- };
-
- // The path-ish thing referred to in a use declaration - abstract base class
- class UseTree {
- Location locus;
-
- public:
- virtual ~UseTree() {}
-
- // Unique pointer custom clone function
- ::std::unique_ptr<UseTree> clone_use_tree() const {
- return ::std::unique_ptr<UseTree>(clone_use_tree_impl());
- }
-
- virtual ::std::string as_string() const = 0;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) = 0;
-
- protected:
- // Clone function implementation as pure virtual method
- virtual UseTree* clone_use_tree_impl() const = 0;
-
- UseTree(Location locus) : locus(locus) {}
- };
-
- // Use tree with a glob (wildcard) operator
- class UseTreeGlob : public UseTree {
- public:
- enum PathType { NO_PATH, GLOBAL, PATH_PREFIXED };
-
- private:
- PathType glob_type;
- SimplePath path;
-
- public:
- UseTreeGlob(PathType glob_type, SimplePath path, Location locus) :
- UseTree(locus), glob_type(glob_type), path(::std::move(path)) {}
-
- // Returns whether has path. Should be made redundant by PathType PATH_PREFIXED.
- inline bool has_path() const {
- return !path.is_empty();
- }
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- // TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory methods?
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual UseTreeGlob* clone_use_tree_impl() const OVERRIDE {
- return new UseTreeGlob(*this);
- }
- };
-
- // Use tree with a list of paths with a common prefix
- class UseTreeList : public UseTree {
- public:
- enum PathType { NO_PATH, GLOBAL, PATH_PREFIXED };
-
- private:
- PathType path_type;
- SimplePath path;
-
- ::std::vector< ::std::unique_ptr<UseTree> > trees;
-
- public:
- UseTreeList(PathType path_type, SimplePath path,
- ::std::vector< ::std::unique_ptr<UseTree> > trees, Location locus) :
- UseTree(locus),
- path_type(path_type), path(::std::move(path)), trees(::std::move(trees)) {}
-
- // copy constructor with vector clone
- UseTreeList(UseTreeList const& other) :
- UseTree(other), path_type(other.path_type), path(other.path) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- trees.reserve(other.trees.size());
-
- for (const auto& e : other.trees) {
- trees.push_back(e->clone_use_tree());
- }
- }
-
- // overloaded assignment operator with vector clone
- UseTreeList& operator=(UseTreeList const& other) {
- UseTree::operator=(other);
- path_type = other.path_type;
- path = other.path;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- trees.reserve(other.trees.size());
-
- for (const auto& e : other.trees) {
- trees.push_back(e->clone_use_tree());
- }
-
- return *this;
- }
-
- // move constructors
- UseTreeList(UseTreeList&& other) = default;
- UseTreeList& operator=(UseTreeList&& other) = default;
-
- // Returns whether has path. Should be made redundant by path_type.
- inline bool has_path() const {
- return !path.is_empty();
- }
-
- // Returns whether has inner tree elements.
- inline bool has_trees() const {
- return !trees.empty();
- }
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory methods?
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual UseTreeList* clone_use_tree_impl() const OVERRIDE {
- return new UseTreeList(*this);
- }
- };
-
- // Use tree where it rebinds the module name as something else
- class UseTreeRebind : public UseTree {
- public:
- enum NewBindType { NONE, IDENTIFIER, WILDCARD };
-
- private:
- SimplePath path;
-
- NewBindType bind_type;
- Identifier identifier; // only if NewBindType is IDENTIFIER
-
- public:
- UseTreeRebind(NewBindType bind_type, SimplePath path, Location locus,
- Identifier identifier = ::std::string()) :
- UseTree(locus),
- path(::std::move(path)), bind_type(bind_type), identifier(::std::move(identifier)) {}
-
- // Returns whether has path (this should always be true).
- inline bool has_path() const {
- return !path.is_empty();
- }
-
- // Returns whether has identifier (or, rather, is allowed to).
- inline bool has_identifier() const {
- return bind_type == IDENTIFIER;
- }
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory methods?
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual UseTreeRebind* clone_use_tree_impl() const OVERRIDE {
- return new UseTreeRebind(*this);
- }
- };
-
- // Rust use declaration (i.e. for modules) AST node
- class UseDeclaration : public VisItem {
- ::std::unique_ptr<UseTree> use_tree;
-
- Location locus;
-
- public:
- ::std::string as_string() const;
-
- UseDeclaration(::std::unique_ptr<UseTree> use_tree, Visibility visibility,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- VisItem(::std::move(visibility), ::std::move(outer_attrs)),
- use_tree(::std::move(use_tree)), locus(locus) {}
-
- // Copy constructor with clone
- UseDeclaration(UseDeclaration const& other) :
- VisItem(other), use_tree(other.use_tree->clone_use_tree()), locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- UseDeclaration& operator=(UseDeclaration const& other) {
- VisItem::operator=(other);
- use_tree = other.use_tree->clone_use_tree();
- // visibility = other.visibility->clone_visibility();
- // outer_attrs = other.outer_attrs;
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- UseDeclaration(UseDeclaration&& other) = default;
- UseDeclaration& operator=(UseDeclaration&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual UseDeclaration* clone_item_impl() const OVERRIDE {
- return new UseDeclaration(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual UseDeclaration* clone_statement_impl() const OVERRIDE {
- return new UseDeclaration(*this);
- }*/
- };
-
- // Parameters used in a function - TODO inline?
- /*struct FunctionParams {
- ::std::vector<FunctionParam> function_params;
- };*/
-
- // Rust function declaration AST node
- class Function
- : public VisItem
- , public InherentImplItem
- , public TraitImplItem {
- FunctionQualifiers qualifiers;
-
- Identifier function_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_function_params;
- // FunctionParams function_params;
- ::std::vector<FunctionParam> function_params; // inlined
-
- // bool has_function_return_type;
- // Type return_type;
- ::std::unique_ptr<Type> return_type;
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // BlockExpr* function_body;
- ::std::unique_ptr<BlockExpr> function_body;
-
- Location locus;
-
- public:
- /*~Function() {
- delete function_body;
- }*/
- ::std::string as_string() const;
-
- // Returns whether function has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether function has regular parameters.
- inline bool has_function_params() const {
- return !function_params.empty();
- }
-
- // Returns whether function has return type - if not, it is void.
- inline bool has_function_return_type() const {
- return return_type != NULL;
- }
-
- // Returns whether function has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Mega-constructor with all possible fields
- Function(Identifier function_name, FunctionQualifiers qualifiers,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::vector<FunctionParam> function_params, ::std::unique_ptr<Type> return_type,
- WhereClause where_clause, ::std::unique_ptr<BlockExpr> function_body, Visibility vis,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- qualifiers(::std::move(qualifiers)), function_name(::std::move(function_name)),
- generic_params(::std::move(generic_params)),
- function_params(::std::move(function_params)), return_type(::std::move(return_type)),
- where_clause(::std::move(where_clause)), function_body(::std::move(function_body)),
- locus(locus) {}
-
- // TODO: add constructor with less fields
-
- // Copy constructor with clone
- Function(Function const& other) :
- VisItem(other), qualifiers(other.qualifiers), function_name(other.function_name),
- /*generic_params(other.generic_params),*/ function_params(other.function_params),
- return_type(other.return_type->clone_type()), where_clause(other.where_clause),
- function_body(other.function_body->clone_block_expr()), locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- Function& operator=(Function const& other) {
- VisItem::operator=(other);
- function_name = other.function_name;
- qualifiers = other.qualifiers;
- // generic_params = other.generic_params;
- function_params = other.function_params;
- return_type = other.return_type->clone_type();
- where_clause = other.where_clause;
- function_body = other.function_body->clone_block_expr();
- // visibility = other.visibility->clone_visibility();
- // outer_attrs = other.outer_attrs;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- Function(Function&& other) = default;
- Function& operator=(Function&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual Function* clone_item_impl() const OVERRIDE {
- return new Function(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- virtual Function* clone_inherent_impl_item_impl() const OVERRIDE {
- return new Function(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- virtual Function* clone_trait_impl_item_impl() const OVERRIDE {
- return new Function(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual Function* clone_statement_impl() const OVERRIDE {
- return new Function(*this);
- }*/
- };
-
- // Rust type alias (i.e. typedef) AST node
- class TypeAlias
- : public VisItem
- , public TraitImplItem {
- Identifier new_type_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // Type exiting_type;
- ::std::unique_ptr<Type> existing_type;
-
- Location locus;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether type alias has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether type alias has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Mega-constructor with all possible fields
- TypeAlias(Identifier new_type_name,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, ::std::unique_ptr<Type> existing_type, Visibility vis,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- new_type_name(::std::move(new_type_name)), generic_params(::std::move(generic_params)),
- where_clause(::std::move(where_clause)), existing_type(::std::move(existing_type)),
- locus(locus) {}
-
- // Copy constructor
- TypeAlias(TypeAlias const& other) :
- VisItem(other),
- new_type_name(other.new_type_name), /*generic_params(other.generic_params),*/
- where_clause(other.where_clause), existing_type(other.existing_type->clone_type()),
- locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- TypeAlias& operator=(TypeAlias const& other) {
- VisItem::operator=(other);
- new_type_name = other.new_type_name;
- // generic_params = other.generic_params;
- where_clause = other.where_clause;
- existing_type = other.existing_type->clone_type();
- // visibility = other.visibility->clone_visibility();
- // outer_attrs = other.outer_attrs;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- TypeAlias(TypeAlias&& other) = default;
- TypeAlias& operator=(TypeAlias&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual TypeAlias* clone_item_impl() const OVERRIDE {
- return new TypeAlias(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- virtual TypeAlias* clone_trait_impl_item_impl() const OVERRIDE {
- return new TypeAlias(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual TypeAlias* clone_statement_impl() const OVERRIDE {
- return new TypeAlias(*this);
- }*/
- };
-
- // Rust base struct declaration AST node - abstract base class
- class Struct : public VisItem {
- protected:
- // protected to enable access by derived classes - allows better as_string
- Identifier struct_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- Location locus;
-
- public:
- // Returns whether struct has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether struct has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- Location get_locus() const {
- return locus;
- }
-
- protected:
- Struct(Identifier struct_name,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, Visibility vis, Location locus,
- ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute>()) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- struct_name(::std::move(struct_name)), generic_params(::std::move(generic_params)),
- where_clause(::std::move(where_clause)), locus(locus) {}
-
- // Copy constructor with vector clone
- Struct(Struct const& other) :
- VisItem(other), struct_name(other.struct_name), where_clause(other.where_clause),
- locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- // Overloaded assignment operator with vector clone
- Struct& operator=(Struct const& other) {
- VisItem::operator=(other);
- struct_name = other.struct_name;
- where_clause = other.where_clause;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- Struct(Struct&& other) = default;
- Struct& operator=(Struct&& other) = default;
- };
-
- // A single field in a struct
- struct StructField {
- private:
- // bool has_outer_attributes;
- ::std::vector<Attribute> outer_attrs;
-
- // bool has_visibility;
- Visibility visibility;
-
- Identifier field_name;
- // Type field_type;
- ::std::unique_ptr<Type> field_type;
-
- // should this store location info?
-
- public:
- // Returns whether struct field has any outer attributes.
- inline bool has_outer_attributes() const {
- return !outer_attrs.empty();
- }
-
- // Returns whether struct field has a non-private (non-default) visibility.
- inline bool has_visibility() const {
- return !visibility.is_error();
- }
-
- StructField(Identifier field_name, ::std::unique_ptr<Type> field_type, Visibility vis,
- ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute>()) :
- outer_attrs(::std::move(outer_attrs)),
- visibility(::std::move(vis)), field_name(::std::move(field_name)),
- field_type(::std::move(field_type)) {}
-
- // Copy constructor
- StructField(StructField const& other) :
- outer_attrs(other.outer_attrs), visibility(other.visibility),
- field_name(other.field_name), field_type(other.field_type->clone_type()) {}
-
- ~StructField() = default;
-
- // Overloaded assignment operator to clone
- StructField& operator=(StructField const& other) {
- field_name = other.field_name;
- field_type = other.field_type->clone_type();
- visibility = other.visibility;
- outer_attrs = other.outer_attrs;
-
- return *this;
- }
-
- // move constructors
- StructField(StructField&& other) = default;
- StructField& operator=(StructField&& other) = default;
-
- // Returns whether struct field is in an error state.
- inline bool is_error() const {
- return field_name.empty() && field_type == NULL;
- // this should really be an or since neither are allowed
- }
-
- // Creates an error state struct field.
- static StructField create_error() {
- return StructField(::std::string(""), NULL, Visibility::create_error());
- }
-
- ::std::string as_string() const;
- };
-
- // Rust struct declaration with true struct type AST node
- class StructStruct : public Struct {
- ::std::vector<StructField> fields;
- bool is_unit;
-
- public:
- ::std::string as_string() const;
-
- // Mega-constructor with all possible fields
- StructStruct(::std::vector<StructField> fields, Identifier struct_name,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, bool is_unit, Visibility vis,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- Struct(::std::move(struct_name), ::std::move(generic_params), ::std::move(where_clause),
- ::std::move(vis), locus, ::std::move(outer_attrs)),
- fields(::std::move(fields)), is_unit(is_unit) {}
-
- // Unit struct constructor
- StructStruct(Identifier struct_name,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, Visibility vis, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- Struct(::std::move(struct_name), ::std::move(generic_params), ::std::move(where_clause),
- ::std::move(vis), locus, ::std::move(outer_attrs)),
- is_unit(true) {}
- // TODO: can a unit struct have generic fields? assuming yes for now.
-
- /* Returns whether the struct is a unit struct - struct defined without fields. This is
- * important because it also means an implicit constant of its type is defined. */
- inline bool is_unit_struct() const {
- return is_unit;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual StructStruct* clone_item_impl() const OVERRIDE {
- return new StructStruct(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual StructStruct* clone_statement_impl() const OVERRIDE {
- return new StructStruct(*this);
- }*/
- };
-
- // A single field in a tuple
- struct TupleField {
- private:
- // bool has_outer_attributes;
- ::std::vector<Attribute> outer_attrs;
-
- // bool has_visibility;
- Visibility visibility;
-
- // Type field_type;
- ::std::unique_ptr<Type> field_type;
-
- // should this store location info?
-
- public:
- // Returns whether tuple field has outer attributes.
- inline bool has_outer_attributes() const {
- return !outer_attrs.empty();
- }
-
- // Returns whether tuple field has a non-default visibility (i.e. a public one)
- inline bool has_visibility() const {
- return !visibility.is_error();
- }
-
- // Complete constructor
- TupleField(::std::unique_ptr<Type> field_type, Visibility vis,
- ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute>()) :
- outer_attrs(::std::move(outer_attrs)),
- visibility(::std::move(vis)), field_type(::std::move(field_type)) {}
-
- // Copy constructor with clone
- TupleField(TupleField const& other) :
- outer_attrs(other.outer_attrs), visibility(other.visibility),
- field_type(other.field_type->clone_type()) {}
-
- ~TupleField() = default;
-
- // Overloaded assignment operator to clone
- TupleField& operator=(TupleField const& other) {
- field_type = other.field_type->clone_type();
- visibility = other.visibility;
- outer_attrs = other.outer_attrs;
-
- return *this;
- }
-
- // move constructors
- TupleField(TupleField&& other) = default;
- TupleField& operator=(TupleField&& other) = default;
-
- // Returns whether tuple field is in an error state.
- inline bool is_error() const {
- return field_type == NULL;
- }
-
- // Creates an error state tuple field.
- static TupleField create_error() {
- return TupleField(NULL, Visibility::create_error());
- }
-
- ::std::string as_string() const;
- };
-
- // Rust tuple declared using struct keyword AST node
- class TupleStruct : public Struct {
- ::std::vector<TupleField> fields;
-
- public:
- ::std::string as_string() const;
-
- // Mega-constructor with all possible fields
- TupleStruct(::std::vector<TupleField> fields, Identifier struct_name,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, Visibility vis, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- Struct(::std::move(struct_name), ::std::move(generic_params), ::std::move(where_clause),
- ::std::move(vis), locus, ::std::move(outer_attrs)),
- fields(::std::move(fields)) {}
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual TupleStruct* clone_item_impl() const OVERRIDE {
- return new TupleStruct(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual TupleStruct* clone_statement_impl() const OVERRIDE {
- return new TupleStruct(*this);
- }*/
- };
-
- // An item used in an "enum" tagged union - not abstract: base represents a name-only enum
- class EnumItem {
- // bool has_attrs;
- ::std::vector<Attribute> outer_attrs;
-
- Identifier variant_name;
-
- Location locus;
-
- public:
- virtual ~EnumItem() {}
-
- // Returns whether enum item has outer attributes.
- inline bool has_outer_attrs() const {
- return !outer_attrs.empty();
- }
-
- EnumItem(Identifier variant_name, ::std::vector<Attribute> outer_attrs, Location locus) :
- outer_attrs(::std::move(outer_attrs)), variant_name(::std::move(variant_name)),
- locus(locus) {}
-
- // Unique pointer custom clone function
- ::std::unique_ptr<EnumItem> clone_enum_item() const {
- return ::std::unique_ptr<EnumItem>(clone_enum_item_impl());
- }
-
- virtual ::std::string as_string() const;
-
- // not pure virtual as not abstract
- virtual void accept_vis(ASTVisitor& vis);
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual EnumItem* clone_enum_item_impl() const {
- return new EnumItem(*this);
- }
- };
-
- // A tuple item used in an "enum" tagged union
- class EnumItemTuple : public EnumItem {
- // bool has_tuple_fields;
- ::std::vector<TupleField> tuple_fields;
-
- public:
- // Returns whether tuple enum item has tuple fields.
- inline bool has_tuple_fields() const {
- return !tuple_fields.empty();
- }
-
- EnumItemTuple(Identifier variant_name, ::std::vector<TupleField> tuple_fields,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- EnumItem(::std::move(variant_name), ::std::move(outer_attrs), locus),
- tuple_fields(::std::move(tuple_fields)) {}
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual EnumItemTuple* clone_enum_item_impl() const {
- return new EnumItemTuple(*this);
- }
- };
-
- // A struct item used in an "enum" tagged union
- class EnumItemStruct : public EnumItem {
- // bool has_struct_fields;
- ::std::vector<StructField> struct_fields;
-
- public:
- // Returns whether struct enum item has struct fields.
- inline bool has_struct_fields() const {
- return !struct_fields.empty();
- }
-
- EnumItemStruct(Identifier variant_name, ::std::vector<StructField> struct_fields,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- EnumItem(::std::move(variant_name), ::std::move(outer_attrs), locus),
- struct_fields(::std::move(struct_fields)) {}
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual EnumItemStruct* clone_enum_item_impl() const {
- return new EnumItemStruct(*this);
- }
- };
-
- // A discriminant (numbered enum) item used in an "enum" tagged union
- class EnumItemDiscriminant : public EnumItem {
- // Expr* expression;
- ::std::unique_ptr<Expr> expression;
-
- public:
- /*~EnumItemDiscriminant() {
- delete expression;
- }*/
-
- EnumItemDiscriminant(Identifier variant_name, ::std::unique_ptr<Expr> expr,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- EnumItem(::std::move(variant_name), ::std::move(outer_attrs), locus),
- expression(::std::move(expr)) {}
-
- // Copy constructor with clone
- EnumItemDiscriminant(EnumItemDiscriminant const& other) :
- EnumItem(other), expression(other.expression->clone_expr()) {}
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- EnumItemDiscriminant& operator=(EnumItemDiscriminant const& other) {
- EnumItem::operator=(other);
- expression = other.expression->clone_expr();
- // variant_name = other.variant_name;
- // outer_attrs = other.outer_attrs;
-
- return *this;
- }
-
- // move constructors
- EnumItemDiscriminant(EnumItemDiscriminant&& other) = default;
- EnumItemDiscriminant& operator=(EnumItemDiscriminant&& other) = default;
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual EnumItemDiscriminant* clone_enum_item_impl() const {
- return new EnumItemDiscriminant(*this);
- }
- };
-
- // AST node for Rust "enum" - tagged union
- class Enum : public VisItem {
- Identifier enum_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- ::std::vector< ::std::unique_ptr<EnumItem> > items;
-
- Location locus;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether "enum" has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether "enum" has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- /* Returns whether enum is a "zero-variant" (no possible variant) enum, which cannot be
- * instantiated.*/
- inline bool is_zero_variant() const {
- return items.empty();
- }
-
- // Mega-constructor
- Enum(Identifier enum_name, Visibility vis,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, ::std::vector< ::std::unique_ptr<EnumItem> > items,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- enum_name(::std::move(enum_name)), generic_params(::std::move(generic_params)),
- where_clause(::std::move(where_clause)), items(::std::move(items)), locus(locus) {}
-
- // TODO: constructor with less arguments
-
- // Copy constructor with vector clone
- Enum(Enum const& other) :
- VisItem(other), enum_name(other.enum_name), where_clause(other.where_clause),
- locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- items.reserve(other.items.size());
-
- for (const auto& e : other.items) {
- items.push_back(e->clone_enum_item());
- }
- }
-
- // Overloaded assignment operator with vector clone
- Enum& operator=(Enum const& other) {
- VisItem::operator=(other);
- enum_name = other.enum_name;
- where_clause = other.where_clause;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- items.reserve(other.items.size());
-
- for (const auto& e : other.items) {
- items.push_back(e->clone_enum_item());
- }
-
- return *this;
- }
-
- // Move constructors
- Enum(Enum&& other) = default;
- Enum& operator=(Enum&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual Enum* clone_item_impl() const OVERRIDE {
- return new Enum(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual Enum* clone_statement_impl() const OVERRIDE {
- return new Enum(*this);
- }*/
- };
-
- // Rust untagged union used for C compat AST node
- class Union : public VisItem {
- Identifier union_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- ::std::vector<StructField> variants;
-
- Location locus;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether union has generic params.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether union has where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- Union(Identifier union_name, Visibility vis,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- WhereClause where_clause, ::std::vector<StructField> variants,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- union_name(::std::move(union_name)), generic_params(::std::move(generic_params)),
- where_clause(::std::move(where_clause)), variants(::std::move(variants)), locus(locus) {
- }
-
- // copy constructor with vector clone
- Union(Union const& other) :
- VisItem(other), union_name(other.union_name), where_clause(other.where_clause),
- variants(other.variants), locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- // overloaded assignment operator with vector clone
- Union& operator=(Union const& other) {
- VisItem::operator=(other);
- union_name = other.union_name;
- where_clause = other.where_clause;
- variants = other.variants;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- Union(Union&& other) = default;
- Union& operator=(Union&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual Union* clone_item_impl() const OVERRIDE {
- return new Union(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual Union* clone_statement_impl() const OVERRIDE {
- return new Union(*this);
- }*/
- };
-
- // "Constant item" AST node - used for constant, compile-time expressions within module scope
- class ConstantItem
- : public VisItem
- , public InherentImplItem
- , public TraitImplItem {
- // either has an identifier or "_" - maybe handle in identifier?
- // bool identifier_is_underscore;
- // if no identifier declared, identifier will be "_"
- Identifier identifier;
-
- // Type type;
- ::std::unique_ptr<Type> type;
-
- // Expr* const_expr;
- ::std::unique_ptr<Expr> const_expr;
-
- Location locus;
-
- public:
- /*~ConstantItem() {
- delete const_expr;
- }*/
-
- ::std::string as_string() const;
-
- ConstantItem(Identifier ident, Visibility vis, ::std::unique_ptr<Type> type,
- ::std::unique_ptr<Expr> const_expr, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- identifier(::std::move(ident)), type(::std::move(type)),
- const_expr(::std::move(const_expr)), locus(locus) {}
-
- ConstantItem(ConstantItem const& other) :
- VisItem(other), identifier(other.identifier), type(other.type->clone_type()),
- const_expr(other.const_expr->clone_expr()), locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overload assignment operator to clone
- ConstantItem& operator=(ConstantItem const& other) {
- VisItem::operator=(other);
- identifier = other.identifier;
- type = other.type->clone_type();
- const_expr = other.const_expr->clone_expr();
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- ConstantItem(ConstantItem&& other) = default;
- ConstantItem& operator=(ConstantItem&& other) = default;
-
- /* Returns whether constant item is an "unnamed" (wildcard underscore used as identifier)
- * constant. */
- inline bool is_unnamed() const {
- return identifier == ::std::string("_");
- }
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ConstantItem* clone_item_impl() const OVERRIDE {
- return new ConstantItem(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- virtual ConstantItem* clone_inherent_impl_item_impl() const OVERRIDE {
- return new ConstantItem(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- virtual ConstantItem* clone_trait_impl_item_impl() const OVERRIDE {
- return new ConstantItem(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual ConstantItem* clone_statement_impl() const OVERRIDE {
- return new ConstantItem(*this);
- }*/
- };
-
- // Static item AST node - items within module scope with fixed storage duration?
- class StaticItem : public VisItem {
- bool has_mut;
-
- Identifier name;
-
- // Type type;
- ::std::unique_ptr<Type> type;
-
- // Expr* expr;
- ::std::unique_ptr<Expr> expr;
-
- Location locus;
-
- public:
- /*~StaticItem() {
- delete expr;
- }*/
-
- ::std::string as_string() const;
-
- StaticItem(Identifier name, bool is_mut, ::std::unique_ptr<Type> type,
- ::std::unique_ptr<Expr> expr, Visibility vis, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- has_mut(is_mut), name(::std::move(name)), type(::std::move(type)),
- expr(::std::move(expr)), locus(locus) {}
-
- // Copy constructor with clone
- StaticItem(StaticItem const& other) :
- VisItem(other), has_mut(other.has_mut), name(other.name),
- type(other.type->clone_type()), expr(other.expr->clone_expr()), locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- StaticItem& operator=(StaticItem const& other) {
- VisItem::operator=(other);
- name = other.name;
- has_mut = other.has_mut;
- type = other.type->clone_type();
- expr = other.expr->clone_expr();
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- StaticItem(StaticItem&& other) = default;
- StaticItem& operator=(StaticItem&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual StaticItem* clone_item_impl() const OVERRIDE {
- return new StaticItem(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual StaticItem* clone_statement_impl() const OVERRIDE {
- return new StaticItem(*this);
- }*/
- };
-
- // Function declaration in traits
- struct TraitFunctionDecl {
- private:
- // TODO: delete and replace with Function decl item? no as no body in this.
- FunctionQualifiers qualifiers;
- Identifier function_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_params;
- // FunctionParams function_params;
- ::std::vector<FunctionParam> function_params; // inlined
-
- // bool has_return_type;
- // Type return_type;
- ::std::unique_ptr<Type> return_type;
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // should this store location info?
-
- public:
- // Returns whether function decl has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether function decl has regular parameters.
- inline bool has_params() const {
- return !function_params.empty();
- }
-
- // Returns whether function has return type (otherwise is void).
- inline bool has_return_type() const {
- return return_type != NULL;
- }
-
- // Returns whether function has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Mega-constructor
- TraitFunctionDecl(Identifier function_name, FunctionQualifiers qualifiers,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::vector<FunctionParam> function_params, ::std::unique_ptr<Type> return_type,
- WhereClause where_clause) :
- qualifiers(::std::move(qualifiers)),
- function_name(::std::move(function_name)), generic_params(::std::move(generic_params)),
- function_params(::std::move(function_params)), return_type(::std::move(return_type)),
- where_clause(::std::move(where_clause)) {}
-
- // Copy constructor with clone
- TraitFunctionDecl(TraitFunctionDecl const& other) :
- qualifiers(other.qualifiers), function_name(other.function_name),
- /*generic_params(other.generic_params),*/ function_params(other.function_params),
- return_type(other.return_type->clone_type()), where_clause(other.where_clause) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- ~TraitFunctionDecl() = default;
-
- // Overloaded assignment operator with clone
- TraitFunctionDecl& operator=(TraitFunctionDecl const& other) {
- function_name = other.function_name;
- qualifiers = other.qualifiers;
- // generic_params = other.generic_params;
- function_params = other.function_params;
- return_type = other.return_type->clone_type();
- where_clause = other.where_clause;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- TraitFunctionDecl(TraitFunctionDecl&& other) = default;
- TraitFunctionDecl& operator=(TraitFunctionDecl&& other) = default;
-
- ::std::string as_string() const;
- };
-
- // Actual trait item function declaration within traits
- class TraitItemFunc : public TraitItem {
- ::std::vector<Attribute> outer_attrs;
- TraitFunctionDecl decl;
- // BlockExpr* block_expr;
- ::std::unique_ptr<BlockExpr> block_expr;
-
- Location locus;
-
- public:
- /*~TraitItemFunc() {
- delete block_expr;
- }*/
-
- // Returns whether function has a definition or is just a declaration.
- inline bool has_definition() const {
- return block_expr != NULL;
- }
-
- TraitItemFunc(TraitFunctionDecl decl, ::std::unique_ptr<BlockExpr> block_expr,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- outer_attrs(::std::move(outer_attrs)),
- decl(::std::move(decl)), block_expr(::std::move(block_expr)), locus(locus) {}
-
- // Copy constructor with clone
- TraitItemFunc(TraitItemFunc const& other) :
- outer_attrs(other.outer_attrs),
- decl(other.decl) /*, block_expr(other.block_expr->clone_block_expr())*/,
- locus(other.locus) {
- if (other.block_expr != NULL) {
- block_expr = other.block_expr->clone_block_expr();
- }
- }
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- TraitItemFunc& operator=(TraitItemFunc const& other) {
- TraitItem::operator=(other);
- outer_attrs = other.outer_attrs;
- decl = other.decl;
- locus = other.locus;
- if (other.block_expr != NULL) {
- block_expr = other.block_expr->clone_block_expr();
- }
-
- return *this;
- }
-
- // move constructors
- TraitItemFunc(TraitItemFunc&& other) = default;
- TraitItemFunc& operator=(TraitItemFunc&& other) = default;
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual TraitItemFunc* clone_trait_item_impl() const {
- return new TraitItemFunc(*this);
- }
- };
-
- // Method declaration within traits
- struct TraitMethodDecl {
- private:
- // TODO: delete and replace with Function decl item? no as no body.
- FunctionQualifiers qualifiers;
- Identifier function_name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- SelfParam self_param;
-
- // bool has_params;
- // FunctionParams function_params;
- ::std::vector<FunctionParam> function_params; // inlined
-
- // bool has_return_type;
- // Type return_type;
- ::std::unique_ptr<Type> return_type;
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // should this store location info?
-
- public:
- // Returns whether method decl has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether method decl has regular parameters.
- inline bool has_params() const {
- return !function_params.empty();
- }
-
- // Returns whether method has return type (otherwise is void).
- inline bool has_return_type() const {
- return return_type != NULL;
- }
-
- // Returns whether method has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Mega-constructor
- TraitMethodDecl(Identifier function_name, FunctionQualifiers qualifiers,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params, SelfParam self_param,
- ::std::vector<FunctionParam> function_params, ::std::unique_ptr<Type> return_type,
- WhereClause where_clause) :
- qualifiers(::std::move(qualifiers)),
- function_name(::std::move(function_name)), generic_params(::std::move(generic_params)),
- self_param(::std::move(self_param)), function_params(::std::move(function_params)),
- return_type(::std::move(return_type)), where_clause(::std::move(where_clause)) {}
-
- // Copy constructor with clone
- TraitMethodDecl(TraitMethodDecl const& other) :
- qualifiers(other.qualifiers), function_name(other.function_name),
- /*generic_params(other.generic_params),*/ self_param(other.self_param),
- function_params(other.function_params), return_type(other.return_type->clone_type()),
- where_clause(other.where_clause) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- ~TraitMethodDecl() = default;
-
- // Overloaded assignment operator with clone
- TraitMethodDecl& operator=(TraitMethodDecl const& other) {
- function_name = other.function_name;
- qualifiers = other.qualifiers;
- // generic_params = other.generic_params;
- self_param = other.self_param;
- function_params = other.function_params;
- return_type = other.return_type->clone_type();
- where_clause = other.where_clause;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- TraitMethodDecl(TraitMethodDecl&& other) = default;
- TraitMethodDecl& operator=(TraitMethodDecl&& other) = default;
-
- ::std::string as_string() const;
- };
-
- // Actual trait item method declaration within traits
- class TraitItemMethod : public TraitItem {
- ::std::vector<Attribute> outer_attrs;
- TraitMethodDecl decl;
- // BlockExpr* block_expr;
- ::std::unique_ptr<BlockExpr> block_expr;
-
- Location locus;
-
- public:
- /*~TraitItemMethod() {
- delete block_expr;
- }*/
-
- // Returns whether method has a definition or is just a declaration.
- inline bool has_definition() const {
- return block_expr != NULL;
- }
-
- TraitItemMethod(TraitMethodDecl decl, ::std::unique_ptr<BlockExpr> block_expr,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- outer_attrs(::std::move(outer_attrs)),
- decl(::std::move(decl)), block_expr(::std::move(block_expr)), locus(locus) {}
-
- // Copy constructor with clone
- TraitItemMethod(TraitItemMethod const& other) :
- outer_attrs(other.outer_attrs), decl(other.decl),
- block_expr(other.block_expr->clone_block_expr()), locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- TraitItemMethod& operator=(TraitItemMethod const& other) {
- TraitItem::operator=(other);
- outer_attrs = other.outer_attrs;
- decl = other.decl;
- block_expr = other.block_expr->clone_block_expr();
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- TraitItemMethod(TraitItemMethod&& other) = default;
- TraitItemMethod& operator=(TraitItemMethod&& other) = default;
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual TraitItemMethod* clone_trait_item_impl() const {
- return new TraitItemMethod(*this);
- }
- };
-
- // Constant item within traits
- class TraitItemConst : public TraitItem {
- ::std::vector<Attribute> outer_attrs;
- Identifier name;
- // Type type;
- ::std::unique_ptr<Type> type;
-
- // bool has_expression;
- // Expr* expr;
- ::std::unique_ptr<Expr> expr;
-
- Location locus;
-
- public:
- /*~TraitItemConst() {
- delete expr;
- }*/
-
- // Whether the constant item has an associated expression.
- inline bool has_expression() const {
- return expr != NULL;
- }
-
- TraitItemConst(Identifier name, ::std::unique_ptr<Type> type,
- ::std::unique_ptr<Expr> expr, ::std::vector<Attribute> outer_attrs, Location locus) :
- outer_attrs(::std::move(outer_attrs)),
- name(::std::move(name)), type(::std::move(type)), expr(::std::move(expr)),
- locus(locus) {}
-
- // Copy constructor with clones
- TraitItemConst(TraitItemConst const& other) :
- outer_attrs(other.outer_attrs), name(other.name), type(other.type->clone_type()),
- expr(other.expr->clone_expr()), locus(other.locus) {}
-
- // Destructor - define here if required
-
- // Overloaded assignment operator to clone
- TraitItemConst& operator=(TraitItemConst const& other) {
- TraitItem::operator=(other);
- outer_attrs = other.outer_attrs;
- name = other.name;
- type = other.type->clone_type();
- expr = other.expr->clone_expr();
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- TraitItemConst(TraitItemConst&& other) = default;
- TraitItemConst& operator=(TraitItemConst&& other) = default;
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual TraitItemConst* clone_trait_item_impl() const {
- return new TraitItemConst(*this);
- }
- };
-
- // Type items within traits
- class TraitItemType : public TraitItem {
- ::std::vector<Attribute> outer_attrs;
-
- Identifier name;
-
- // bool has_type_param_bounds;
- // TypeParamBounds type_param_bounds;
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds; // inlined form
-
- Location locus;
-
- public:
- // Returns whether trait item type has type param bounds.
- inline bool has_type_param_bounds() const {
- return !type_param_bounds.empty();
- }
-
- TraitItemType(Identifier name,
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- outer_attrs(::std::move(outer_attrs)),
- name(::std::move(name)), type_param_bounds(::std::move(type_param_bounds)),
- locus(locus) {}
-
- // Copy constructor with vector clone
- TraitItemType(TraitItemType const& other) :
- outer_attrs(other.outer_attrs), name(other.name), locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
- }
-
- // Overloaded assignment operator with vector clone
- TraitItemType& operator=(TraitItemType const& other) {
- TraitItem::operator=(other);
- outer_attrs = other.outer_attrs;
- name = other.name;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
-
- return *this;
- }
-
- // default move constructors
- TraitItemType(TraitItemType&& other) = default;
- TraitItemType& operator=(TraitItemType&& other) = default;
-
- ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Clone function implementation as (not pure) virtual method
- virtual TraitItemType* clone_trait_item_impl() const {
- return new TraitItemType(*this);
- }
- };
-
- // Macro invocation items within traits - TODO is this approach better or is making
- // MacroInvocationSemi itself implement TraitItem better? Leaning toward latter.
- /*class TraitItemMacroInvoc : public TraitItem {
- MacroInvocationSemi macro_invoc;
-
- public:
- TraitItemMacroInvoc(
- MacroInvocationSemi macro_invoc, ::std::vector<Attribute> outer_attrs) :
- macro_invoc(macro_invoc),
- TraitItem(outer_attrs) {}
- };*/
- // replaced with MacroInvocationSemi implementing TraitItem
-
- // Rust trait item declaration AST node
- class Trait : public VisItem {
- bool has_unsafe;
-
- Identifier name;
-
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_type_param_bounds;
- // TypeParamBounds type_param_bounds;
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds; // inlined form
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // bool has_trait_items;
- ::std::vector< ::std::unique_ptr<TraitItem> > trait_items;
-
- Location locus;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether trait has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether trait has type parameter bounds.
- inline bool has_type_param_bounds() const {
- return !type_param_bounds.empty();
- }
-
- // Returns whether trait has where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Returns whether trait has trait items.
- inline bool has_trait_items() const {
- return !trait_items.empty();
- }
-
- // Mega-constructor
- Trait(Identifier name, bool is_unsafe,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds,
- WhereClause where_clause, ::std::vector< ::std::unique_ptr<TraitItem> > trait_items,
- Visibility vis, ::std::vector<Attribute> outer_attrs, Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- has_unsafe(is_unsafe), name(::std::move(name)),
- generic_params(::std::move(generic_params)),
- type_param_bounds(::std::move(type_param_bounds)),
- where_clause(::std::move(where_clause)), trait_items(::std::move(trait_items)),
- locus(locus) {}
-
- // Copy constructor with vector clone
- Trait(Trait const& other) :
- VisItem(other), has_unsafe(other.has_unsafe), name(other.name),
- where_clause(other.where_clause), locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- // again for type param bounds
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
-
- // again for trait items
- trait_items.reserve(other.trait_items.size());
-
- for (const auto& e : other.trait_items) {
- trait_items.push_back(e->clone_trait_item());
- }
- }
-
- // Overloaded assignment operator with vector clone
- Trait& operator=(Trait const& other) {
- VisItem::operator=(other);
- name = other.name;
- has_unsafe = other.has_unsafe;
- where_clause = other.where_clause;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- // again for type param bounds
- type_param_bounds.reserve(other.type_param_bounds.size());
-
- for (const auto& e : other.type_param_bounds) {
- type_param_bounds.push_back(e->clone_type_param_bound());
- }
-
- // again for trait items
- trait_items.reserve(other.trait_items.size());
-
- for (const auto& e : other.trait_items) {
- trait_items.push_back(e->clone_trait_item());
- }
-
- return *this;
- }
-
- // default move constructors
- Trait(Trait&& other) = default;
- Trait& operator=(Trait&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual Trait* clone_item_impl() const OVERRIDE {
- return new Trait(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual Trait* clone_statement_impl() const OVERRIDE {
- return new Trait(*this);
- }*/
- };
-
- // Implementation item declaration AST node - abstract base class
- class Impl : public VisItem {
- // must be protected to allow subclasses to access them properly
- protected:
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // Type trait_type;
- ::std::unique_ptr<Type> trait_type;
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- // bool has_inner_attrs;
- ::std::vector<Attribute> inner_attrs;
-
- private:
- // doesn't really need to be protected as write access probably not needed
- Location locus;
-
- public:
- // Returns whether impl has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether impl has where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- // Returns whether impl has inner attributes.
- inline bool has_inner_attrs() const {
- return !inner_attrs.empty();
- }
-
- Location get_locus() const {
- return locus;
- }
-
- protected:
- // Mega-constructor
- Impl(::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::unique_ptr<Type> trait_type, WhereClause where_clause, Visibility vis,
- ::std::vector<Attribute> inner_attrs, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- generic_params(::std::move(generic_params)), trait_type(::std::move(trait_type)),
- where_clause(::std::move(where_clause)), inner_attrs(::std::move(inner_attrs)),
- locus(locus) {}
-
- // Copy constructor
- Impl(Impl const& other) :
- VisItem(other),
- /*generic_params(other.generic_params),*/ trait_type(other.trait_type->clone_type()),
- where_clause(other.where_clause), inner_attrs(other.inner_attrs), locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- // Destructor - define here if required
-
- // Assignment operator overload with cloning
- Impl& operator=(Impl const& other) {
- VisItem::operator=(other);
- // generic_params = other.generic_params;
- trait_type = other.trait_type->clone_type();
- where_clause = other.where_clause;
- inner_attrs = other.inner_attrs;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- Impl(Impl&& other) = default;
- Impl& operator=(Impl&& other) = default;
- };
-
- // Regular "impl foo" impl block declaration AST node
- class InherentImpl : public Impl {
- // bool has_impl_items;
- ::std::vector< ::std::unique_ptr<InherentImplItem> > impl_items;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether inherent impl block has inherent impl items.
- inline bool has_impl_items() const {
- return !impl_items.empty();
- }
-
- // Mega-constructor
- InherentImpl(::std::vector< ::std::unique_ptr<InherentImplItem> > impl_items,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::unique_ptr<Type> trait_type, WhereClause where_clause, Visibility vis,
- ::std::vector<Attribute> inner_attrs, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- Impl(::std::move(generic_params), ::std::move(trait_type), ::std::move(where_clause),
- ::std::move(vis), ::std::move(inner_attrs), ::std::move(outer_attrs), locus),
- impl_items(::std::move(impl_items)) {}
-
- // Copy constructor with vector clone
- InherentImpl(InherentImpl const& other) : Impl(other) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- impl_items.reserve(other.impl_items.size());
-
- for (const auto& e : other.impl_items) {
- impl_items.push_back(e->clone_inherent_impl_item());
- }
- }
-
- // Overloaded assignment operator with vector clone
- InherentImpl& operator=(InherentImpl const& other) {
- Impl::operator=(other);
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- impl_items.reserve(other.impl_items.size());
-
- for (const auto& e : other.impl_items) {
- impl_items.push_back(e->clone_inherent_impl_item());
- }
-
- return *this;
- }
-
- // default move constructors
- InherentImpl(InherentImpl&& other) = default;
- InherentImpl& operator=(InherentImpl&& other) = default;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual InherentImpl* clone_item_impl() const OVERRIDE {
- return new InherentImpl(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual InherentImpl* clone_statement_impl() const OVERRIDE {
- return new InherentImpl(*this);
- }*/
- };
-
- // The "impl footrait for foo" impl block declaration AST node
- class TraitImpl : public Impl {
- bool has_unsafe;
-
- bool has_exclam;
-
- TypePath trait_path;
-
- // bool has_impl_items;
- ::std::vector< ::std::unique_ptr<TraitImplItem> > impl_items;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether trait impl has impl items.
- inline bool has_impl_items() const {
- return !impl_items.empty();
- }
-
- // Mega-constructor
- TraitImpl(TypePath trait_path, bool is_unsafe, bool has_exclam,
- ::std::vector< ::std::unique_ptr<TraitImplItem> > impl_items,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::unique_ptr<Type> trait_type, WhereClause where_clause, Visibility vis,
- ::std::vector<Attribute> inner_attrs, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- Impl(::std::move(generic_params), ::std::move(trait_type), ::std::move(where_clause),
- ::std::move(vis), ::std::move(inner_attrs), ::std::move(outer_attrs), locus),
- has_unsafe(is_unsafe), has_exclam(has_exclam), trait_path(::std::move(trait_path)),
- impl_items(::std::move(impl_items)) {}
-
- // TODO: constructors with less params
-
- // Copy constructor with vector clone
- TraitImpl(TraitImpl const& other) :
- Impl(other), has_unsafe(other.has_unsafe), has_exclam(other.has_exclam),
- trait_path(other.trait_path) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- impl_items.reserve(other.impl_items.size());
-
- for (const auto& e : other.impl_items) {
- impl_items.push_back(e->clone_trait_impl_item());
- }
- }
-
- // Overloaded assignment operator with vector clone
- TraitImpl& operator=(TraitImpl const& other) {
- Impl::operator=(other);
- trait_path = other.trait_path;
- has_unsafe = other.has_unsafe;
- has_exclam = other.has_exclam;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- impl_items.reserve(other.impl_items.size());
-
- for (const auto& e : other.impl_items) {
- impl_items.push_back(e->clone_trait_impl_item());
- }
-
- return *this;
- }
-
- // move constructors
- TraitImpl(TraitImpl&& other) = default;
- TraitImpl& operator=(TraitImpl&& other) = default;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual TraitImpl* clone_item_impl() const OVERRIDE {
- return new TraitImpl(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual TraitImpl* clone_statement_impl() const OVERRIDE {
- return new TraitImpl(*this);
- }*/
- };
-
- // Abstract base class for an item used inside an extern block
- class ExternalItem {
- // bool has_outer_attrs;
- ::std::vector<Attribute> outer_attrs;
-
- // bool has_visibility;
- Visibility visibility;
-
- Identifier item_name;
-
- Location locus;
-
- public:
- virtual ~ExternalItem() {}
-
- // Returns whether item has outer attributes.
- inline bool has_outer_attrs() const {
- return !outer_attrs.empty();
- }
-
- // Returns whether item has non-default visibility.
- inline bool has_visibility() const {
- return !visibility.is_error();
- }
-
- // Unique pointer custom clone function
- ::std::unique_ptr<ExternalItem> clone_external_item() const {
- return ::std::unique_ptr<ExternalItem>(clone_external_item_impl());
- }
-
- virtual ::std::string as_string() const;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) = 0;
-
- protected:
- ExternalItem(Identifier item_name, Visibility vis, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- outer_attrs(::std::move(outer_attrs)),
- visibility(::std::move(vis)), item_name(::std::move(item_name)), locus(locus) {}
-
- // Copy constructor
- ExternalItem(ExternalItem const& other) :
- outer_attrs(other.outer_attrs), visibility(other.visibility),
- item_name(other.item_name), locus(other.locus) {}
-
- // Overloaded assignment operator to clone
- ExternalItem& operator=(ExternalItem const& other) {
- item_name = other.item_name;
- visibility = other.visibility;
- outer_attrs = other.outer_attrs;
- locus = other.locus;
-
- return *this;
- }
-
- // move constructors
- ExternalItem(ExternalItem&& other) = default;
- ExternalItem& operator=(ExternalItem&& other) = default;
-
- // Clone function implementation as pure virtual method
- virtual ExternalItem* clone_external_item_impl() const = 0;
-
- // possibly make this public if required
- ::std::string get_item_name() const {
- return item_name;
- }
- };
-
- // A static item used in an extern block
- class ExternalStaticItem : public ExternalItem {
- bool has_mut;
-
- // Type item_type;
- ::std::unique_ptr<Type> item_type;
+This file is part of GCC.
- public:
- ExternalStaticItem(Identifier item_name, ::std::unique_ptr<Type> item_type, bool is_mut,
- Visibility vis, ::std::vector<Attribute> outer_attrs, Location locus) :
- ExternalItem(::std::move(item_name), ::std::move(vis), ::std::move(outer_attrs), locus),
- has_mut(is_mut), item_type(::std::move(item_type)) {}
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
- // Copy constructor
- ExternalStaticItem(ExternalStaticItem const& other) :
- ExternalItem(other), has_mut(other.has_mut), item_type(other.item_type->clone_type()) {}
-
- // Destructor - define here if required
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
- // Overloaded assignment operator to clone
- ExternalStaticItem& operator=(ExternalStaticItem const& other) {
- ExternalItem::operator=(other);
- item_type = other.item_type->clone_type();
- has_mut = other.has_mut;
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
- return *this;
- }
-
- // move constructors
- ExternalStaticItem(ExternalStaticItem&& other) = default;
- ExternalStaticItem& operator=(ExternalStaticItem&& other) = default;
+#include "rust-ast.h"
+#include "rust-path.h"
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ExternalStaticItem* clone_external_item_impl() const OVERRIDE {
- return new ExternalStaticItem(*this);
- }
- };
-
- // A named function parameter used in external functions
- struct NamedFunctionParam {
- private:
- // bool has_name; // otherwise is _
- Identifier name; // TODO: handle wildcard in identifier?
-
- // Type param_type;
- ::std::unique_ptr<Type> param_type;
-
- // TODO: should this store location data?
-
- public:
- // Returns whether the named function parameter has a name (i.e. name is not '_').
- inline bool has_name() const {
- return name != "_";
- }
-
- // Returns whether the named function parameter is in an error state.
- inline bool is_error() const {
- // also if identifier is "" but that is probably more costly to compute
- return param_type == NULL;
- }
-
- // Creates an error state named function parameter.
- static NamedFunctionParam create_error() {
- return NamedFunctionParam("", NULL);
- }
-
- NamedFunctionParam(Identifier name, ::std::unique_ptr<Type> param_type) :
- name(::std::move(name)), param_type(::std::move(param_type)) {}
-
- // Copy constructor
- NamedFunctionParam(NamedFunctionParam const& other) :
- name(other.name), param_type(other.param_type->clone_type()) {}
-
- ~NamedFunctionParam() = default;
-
- // Overloaded assignment operator to clone
- NamedFunctionParam& operator=(NamedFunctionParam const& other) {
- name = other.name;
- param_type = other.param_type->clone_type();
- // has_name = other.has_name;
-
- return *this;
- }
-
- // move constructors
- NamedFunctionParam(NamedFunctionParam&& other) = default;
- NamedFunctionParam& operator=(NamedFunctionParam&& other) = default;
-
- ::std::string as_string() const;
- };
-
- // A function item used in an extern block
- class ExternalFunctionItem : public ExternalItem {
- // bool has_generics;
- // Generics generic_params;
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
-
- // bool has_return_type;
- // FunctionReturnType return_type;
- ::std::unique_ptr<Type> return_type; // inlined
-
- // bool has_where_clause;
- WhereClause where_clause;
-
- ::std::vector<NamedFunctionParam> function_params;
-
- bool has_variadics;
-
- public:
- // Returns whether item has generic parameters.
- inline bool has_generics() const {
- return !generic_params.empty();
- }
-
- // Returns whether item has a return type (otherwise void).
- inline bool has_return_type() const {
- return return_type != NULL;
- }
-
- // Returns whether item has a where clause.
- inline bool has_where_clause() const {
- return !where_clause.is_empty();
- }
-
- ExternalFunctionItem(Identifier item_name,
- ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
- ::std::unique_ptr<Type> return_type, WhereClause where_clause,
- ::std::vector<NamedFunctionParam> function_params, bool has_variadics, Visibility vis,
- ::std::vector<Attribute> outer_attrs, Location locus) :
- ExternalItem(::std::move(item_name), ::std::move(vis), ::std::move(outer_attrs), locus),
- generic_params(::std::move(generic_params)), return_type(::std::move(return_type)),
- where_clause(::std::move(where_clause)), function_params(::std::move(function_params)),
- has_variadics(has_variadics) {}
-
- // Copy constructor with clone
- ExternalFunctionItem(ExternalFunctionItem const& other) :
- ExternalItem(other),
- /*generic_params(other.generic_params),*/ return_type(other.return_type->clone_type()),
- where_clause(other.where_clause), function_params(other.function_params),
- has_variadics(other.has_variadics) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
- }
-
- // Destructor - define here if required
-
- // Overloaded assignment operator with clone
- ExternalFunctionItem& operator=(ExternalFunctionItem const& other) {
- ExternalItem::operator=(other);
- // generic_params = other.generic_params;
- return_type = other.return_type->clone_type();
- where_clause = other.where_clause;
- function_params = other.function_params;
- has_variadics = other.has_variadics;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- generic_params.reserve(other.generic_params.size());
-
- for (const auto& e : other.generic_params) {
- generic_params.push_back(e->clone_generic_param());
- }
-
- return *this;
- }
-
- // move constructors
- ExternalFunctionItem(ExternalFunctionItem&& other) = default;
- ExternalFunctionItem& operator=(ExternalFunctionItem&& other) = default;
-
- ::std::string as_string() const;
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ExternalFunctionItem* clone_external_item_impl() const OVERRIDE {
- return new ExternalFunctionItem(*this);
- }
- };
-
- // An extern block AST node
- class ExternBlock : public VisItem {
- // bool has_abi;
- ::std::string abi;
-
- // bool has_inner_attrs;
- ::std::vector<Attribute> inner_attrs;
-
- // bool has_extern_items;
- ::std::vector< ::std::unique_ptr<ExternalItem> > extern_items;
-
- Location locus;
-
- public:
- ::std::string as_string() const;
-
- // Returns whether extern block has inner attributes.
- inline bool has_inner_attrs() const {
- return !inner_attrs.empty();
- }
-
- // Returns whether extern block has extern items.
- inline bool has_extern_items() const {
- return !extern_items.empty();
- }
-
- // Returns whether extern block has ABI name.
- inline bool has_abi() const {
- return !abi.empty();
- }
-
- ExternBlock(::std::string abi,
- ::std::vector< ::std::unique_ptr<ExternalItem> > extern_items, Visibility vis,
- ::std::vector<Attribute> inner_attrs, ::std::vector<Attribute> outer_attrs,
- Location locus) :
- VisItem(::std::move(vis), ::std::move(outer_attrs)),
- abi(::std::move(abi)), inner_attrs(::std::move(inner_attrs)),
- extern_items(::std::move(extern_items)), locus(locus) {}
-
- // Copy constructor with vector clone
- ExternBlock(ExternBlock const& other) :
- VisItem(other), abi(other.abi), inner_attrs(other.inner_attrs), locus(other.locus) {
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- extern_items.reserve(other.extern_items.size());
-
- for (const auto& e : other.extern_items) {
- extern_items.push_back(e->clone_external_item());
- }
- }
-
- // Overloaded assignment operator with vector clone
- ExternBlock& operator=(ExternBlock const& other) {
- VisItem::operator=(other);
- abi = other.abi;
- inner_attrs = other.inner_attrs;
- locus = other.locus;
-
- // crappy vector unique pointer clone - TODO is there a better way of doing this?
- extern_items.reserve(other.extern_items.size());
-
- for (const auto& e : other.extern_items) {
- extern_items.push_back(e->clone_external_item());
- }
-
- return *this;
- }
-
- // move constructors
- ExternBlock(ExternBlock&& other) = default;
- ExternBlock& operator=(ExternBlock&& other) = default;
-
- Location get_locus() const {
- return locus;
- }
-
- virtual void accept_vis(ASTVisitor& vis) OVERRIDE;
-
- protected:
- // Use covariance to implement clone function as returning this object rather than base
- virtual ExternBlock* clone_item_impl() const OVERRIDE {
- return new ExternBlock(*this);
- }
-
- // Use covariance to implement clone function as returning this object rather than base
- /*virtual ExternBlock* clone_statement_impl() const OVERRIDE {
- return new ExternBlock(*this);
- }*/
- };
-
- // Replaced with forward decls - defined in "rust-macro.h"
- class MacroItem;
- class MacroInvocationSemi;
- class MacroRulesDefinition;
+namespace Rust {
+namespace AST {
+// forward decls
+// struct Lifetime;
+// struct LifetimeBounds;
+// struct TypeParamBounds;
+class BlockExpr;
+// class Expr;
+// class Type;
+class TypePath;
+// class Pattern;
+class MacroInvocationSemi;
+
+// TODO: inline?
+/*struct AbiName {
+ ::std::string abi_name;
+ // Technically is meant to be STRING_LITERAL or RAW_STRING_LITERAL
+
+ public:
+ // Returns whether abi name is empty, i.e. doesn't exist.
+ inline bool is_empty() const {
+ return abi_name.empty();
}
-}
+
+ AbiName(::std::string name) : abi_name(::std::move(name)) {}
+
+ // Empty AbiName constructor
+ AbiName() {}
+};*/
+
+// A type generic parameter (as opposed to a lifetime generic parameter)
+class TypeParam : public GenericParam
+{
+ // bool has_outer_attribute;
+ //::std::unique_ptr<Attribute> outer_attr;
+ Attribute outer_attr;
+
+ Identifier type_representation;
+
+ // bool has_type_param_bounds;
+ // TypeParamBounds type_param_bounds;
+ ::std::vector< ::std::unique_ptr<TypeParamBound> >
+ type_param_bounds; // inlined form
+
+ // bool has_type;
+ // Type type;
+ ::std::unique_ptr<Type> type;
+
+ Location locus;
+
+public:
+ // Returns whether the type of the type param has been specified.
+ inline bool has_type () const { return type != NULL; }
+
+ // Returns whether the type param has type param bounds.
+ inline bool has_type_param_bounds () const
+ {
+ return !type_param_bounds.empty ();
+ }
+
+ // Returns whether the type param has an outer attribute.
+ inline bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+ TypeParam (
+ Identifier type_representation, Location locus = Location (),
+ ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds
+ = ::std::vector< ::std::unique_ptr<TypeParamBound> > (),
+ ::std::unique_ptr<Type> type = NULL,
+ Attribute outer_attr = Attribute::create_empty ())
+ : outer_attr (::std::move (outer_attr)),
+ type_representation (::std::move (type_representation)),
+ type_param_bounds (::std::move (type_param_bounds)),
+ type (::std::move (type)), locus (locus)
+ {}
+
+ // Copy constructor uses clone
+ TypeParam (TypeParam const &other)
+ : outer_attr (other.outer_attr),
+ type_representation (other.type_representation),
+ /*type_param_bounds(other.type_param_bounds),*/ type (
+ other.type->clone_type ()),
+ locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ TypeParam &operator= (TypeParam const &other)
+ {
+ type_representation = other.type_representation;
+ // type_param_bounds = other.type_param_bounds;
+ type = other.type->clone_type ();
+ outer_attr = other.outer_attr;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TypeParam (TypeParam &&other) = default;
+ TypeParam &operator= (TypeParam &&other) = default;
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual TypeParam *clone_generic_param_impl () const
+ {
+ return new TypeParam (*this);
+ }
+};
+
+// "where" clause item base. Abstract - use LifetimeWhereClauseItem,
+// TypeBoundWhereClauseItem
+class WhereClauseItem
+{
+public:
+ virtual ~WhereClauseItem () {}
+
+ // Unique pointer custom clone function
+ ::std::unique_ptr<WhereClauseItem> clone_where_clause_item () const
+ {
+ return ::std::unique_ptr<WhereClauseItem> (clone_where_clause_item_impl ());
+ }
+
+ virtual ::std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+protected:
+ // Clone function implementation as pure virtual method
+ virtual WhereClauseItem *clone_where_clause_item_impl () const = 0;
+};
+
+// A lifetime where clause item
+class LifetimeWhereClauseItem : public WhereClauseItem
+{
+ Lifetime lifetime;
+
+ // LifetimeBounds lifetime_bounds;
+ ::std::vector<Lifetime> lifetime_bounds; // inlined lifetime bounds
+
+ // should this store location info?
+
+public:
+ LifetimeWhereClauseItem (Lifetime lifetime,
+ ::std::vector<Lifetime> lifetime_bounds)
+ : lifetime (::std::move (lifetime)),
+ lifetime_bounds (::std::move (lifetime_bounds))
+ {}
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual LifetimeWhereClauseItem *clone_where_clause_item_impl () const
+ {
+ return new LifetimeWhereClauseItem (*this);
+ }
+};
+
+// A type bound where clause item
+class TypeBoundWhereClauseItem : public WhereClauseItem
+{
+ // bool has_for_lifetimes;
+ // LifetimeParams for_lifetimes;
+ ::std::vector<LifetimeParam> for_lifetimes; // inlined
+
+ // Type bound_type;
+ ::std::unique_ptr<Type> bound_type;
+
+ // bool has_type_param_bounds;
+ // TypeParamBounds type_param_bounds;
+ ::std::vector< ::std::unique_ptr<TypeParamBound> >
+ type_param_bounds; // inlined form
+
+ // should this store location info?
+
+public:
+ // Returns whether the item has ForLifetimes
+ inline bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+ // Returns whether the item has type param bounds
+ inline bool has_type_param_bounds () const
+ {
+ return !type_param_bounds.empty ();
+ }
+
+ TypeBoundWhereClauseItem (
+ ::std::vector<LifetimeParam> for_lifetimes,
+ ::std::unique_ptr<Type> bound_type,
+ ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds)
+ : for_lifetimes (::std::move (for_lifetimes)),
+ bound_type (::std::move (bound_type)),
+ type_param_bounds (::std::move (type_param_bounds))
+ {}
+
+ // Copy constructor requires clone
+ TypeBoundWhereClauseItem (TypeBoundWhereClauseItem const &other)
+ : for_lifetimes (other.for_lifetimes),
+ bound_type (other.bound_type->clone_type ()) /*,
+type_param_bounds(other.type_param_bounds)*/
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Overload assignment operator to clone
+ TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem const &other)
+ {
+ for_lifetimes = other.for_lifetimes;
+ bound_type = other.bound_type->clone_type ();
+ // type_param_bounds = other.type_param_bounds;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TypeBoundWhereClauseItem (TypeBoundWhereClauseItem &&other) = default;
+ TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem &&other)
+ = default;
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual TypeBoundWhereClauseItem *clone_where_clause_item_impl () const
+ {
+ return new TypeBoundWhereClauseItem (*this);
+ }
+};
+
+// A where clause
+struct WhereClause
+{
+private:
+ //::std::vector<WhereClauseItem> where_clause_items;
+ ::std::vector< ::std::unique_ptr<WhereClauseItem> > where_clause_items;
+
+ // should this store location info?
+
+public:
+ WhereClause (
+ ::std::vector< ::std::unique_ptr<WhereClauseItem> > where_clause_items)
+ : where_clause_items (::std::move (where_clause_items))
+ {}
+
+ // copy constructor with vector clone
+ WhereClause (WhereClause const &other)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ where_clause_items.reserve (other.where_clause_items.size ());
+
+ for (const auto &e : other.where_clause_items)
+ {
+ where_clause_items.push_back (e->clone_where_clause_item ());
+ }
+ }
+
+ // overloaded assignment operator with vector clone
+ WhereClause &operator= (WhereClause const &other)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ where_clause_items.reserve (other.where_clause_items.size ());
+
+ for (const auto &e : other.where_clause_items)
+ {
+ where_clause_items.push_back (e->clone_where_clause_item ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ WhereClause (WhereClause &&other) = default;
+ WhereClause &operator= (WhereClause &&other) = default;
+
+ // Creates a WhereClause with no items.
+ static WhereClause create_empty ()
+ {
+ return WhereClause (::std::vector< ::std::unique_ptr<WhereClauseItem> > ());
+ }
+
+ // Returns whether the WhereClause has no items.
+ inline bool is_empty () const { return where_clause_items.empty (); }
+
+ ::std::string as_string () const;
+};
+
+// A self parameter in a method
+struct SelfParam
+{
+private:
+ bool has_ref;
+ bool is_mut;
+ // bool has_lifetime; // only possible if also ref
+ Lifetime lifetime;
+
+ // bool has_type; // only possible if not ref
+ // Type type;
+ ::std::unique_ptr<Type> type;
+
+ Location locus;
+
+ // Unrestricted constructor used for error state
+ SelfParam (Lifetime lifetime, bool has_ref, bool is_mut, Type *type)
+ : has_ref (has_ref), is_mut (is_mut), lifetime (::std::move (lifetime)),
+ type (type)
+ {}
+ // this is ok as no outside classes can ever call this
+
+public:
+ // Returns whether the self-param has a type field.
+ inline bool has_type () const { return type != NULL; }
+
+ // Returns whether the self-param has a valid lifetime.
+ inline bool has_lifetime () const { return !lifetime.is_error (); }
+
+ // Returns whether the self-param is in an error state.
+ inline bool is_error () const
+ {
+ return has_type () && has_lifetime ();
+ // not having either is not an error
+ }
+
+ // Creates an error state self-param.
+ static SelfParam create_error ()
+ {
+ /* HACK: creates a dummy type. Since it's a unique pointer, it should
+ * clean it up, but it still allocates memory, which is not ideal. */
+ return SelfParam (Lifetime (Lifetime::STATIC), false, false,
+ new QualifiedPathInType (
+ QualifiedPathInType::create_error ()));
+ }
+
+ // Type-based self parameter (not ref, no lifetime)
+ SelfParam (::std::unique_ptr<Type> type, bool is_mut, Location locus)
+ : has_ref (false), is_mut (is_mut), lifetime (Lifetime::error ()),
+ type (::std::move (type)), locus (locus)
+ {}
+
+ // Lifetime-based self parameter (is ref, no type)
+ SelfParam (Lifetime lifetime, bool is_mut, Location locus)
+ : /*type(NULL), */ has_ref (true), is_mut (is_mut),
+ lifetime (::std::move (lifetime)), locus (locus)
+ {}
+
+ // Copy constructor requires clone
+ SelfParam (SelfParam const &other)
+ : has_ref (other.has_ref), is_mut (other.is_mut), lifetime (other.lifetime),
+ type (other.type->clone_type ()), locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overload assignment operator to use clone
+ SelfParam &operator= (SelfParam const &other)
+ {
+ type = other.type->clone_type ();
+ is_mut = other.is_mut;
+ has_ref = other.has_ref;
+ lifetime = other.lifetime;
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ SelfParam (SelfParam &&other) = default;
+ SelfParam &operator= (SelfParam &&other) = default;
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+};
+
+// Qualifiers for function, i.e. const, unsafe, extern etc.
+struct FunctionQualifiers
+{
+public:
+ // Whether the function is neither const nor async, const only, or async
+ // only.
+ enum AsyncConstStatus
+ {
+ NONE,
+ CONST,
+ ASYNC
+ };
+
+private:
+ AsyncConstStatus const_status;
+ bool has_unsafe;
+ bool has_extern;
+ ::std::string extern_abi; // e.g. extern "C" fn() -> i32 {}
+ // TODO: maybe ensure that extern_abi only exists if extern exists?
+
+ // should this store location info?
+
+public:
+ // Constructor with no extern (and hence no extern abi)
+ FunctionQualifiers (AsyncConstStatus const_status, bool has_unsafe)
+ : const_status (const_status), has_unsafe (has_unsafe), has_extern (false),
+ extern_abi (::std::string (""))
+ {}
+
+ // Constructor with extern abi (and thus extern)
+ FunctionQualifiers (AsyncConstStatus const_status, bool has_unsafe,
+ ::std::string extern_abi)
+ : const_status (const_status), has_unsafe (has_unsafe), has_extern (true),
+ extern_abi (::std::move (extern_abi))
+ {}
+
+ // Constructor with all possible options (DON'T HAVE EXTERN_ABI WITHOUT
+ // EXTERN!)
+ FunctionQualifiers (AsyncConstStatus const_status, bool has_unsafe,
+ bool has_extern, ::std::string extern_abi)
+ : const_status (const_status), has_unsafe (has_unsafe),
+ has_extern (has_extern), extern_abi (::std::move (extern_abi))
+ {}
+
+ ::std::string as_string () const;
+};
+
+// Forward decl FunctionParams
+// struct FunctionParams;
+
+// A function parameter
+struct FunctionParam
+{
+private:
+ // Pattern* param_name;
+ ::std::unique_ptr<Pattern> param_name;
+ // Type type;
+ ::std::unique_ptr<Type> type;
+
+ Location locus;
+
+public:
+ FunctionParam (::std::unique_ptr<Pattern> param_name,
+ ::std::unique_ptr<Type> param_type, Location locus)
+ : param_name (::std::move (param_name)), type (::std::move (param_type)),
+ locus (locus)
+ {}
+
+ // Copy constructor uses clone
+ FunctionParam (FunctionParam const &other)
+ : param_name (other.param_name->clone_pattern ()),
+ type (other.type->clone_type ()), locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overload assignment operator to use clone
+ FunctionParam &operator= (FunctionParam const &other)
+ {
+ param_name = other.param_name->clone_pattern ();
+ type = other.type->clone_type ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ FunctionParam (FunctionParam &&other) = default;
+ FunctionParam &operator= (FunctionParam &&other) = default;
+
+ // Returns whether FunctionParam is in an invalid state.
+ inline bool is_error () const { return param_name == NULL || type == NULL; }
+
+ // Creates an error FunctionParam.
+ static FunctionParam create_error ()
+ {
+ return FunctionParam (NULL, NULL, Location ());
+ }
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+};
+
+// Visibility of item - if the item has it, then it is some form of public
+struct Visibility
+{
+public:
+ enum PublicVisType
+ {
+ NONE,
+ CRATE,
+ SELF,
+ SUPER,
+ IN_PATH
+ };
+
+private:
+ // bool is_pub;
+
+ // if vis is public, one of these
+ PublicVisType public_vis_type;
+
+ // Only assigned if public_vis_type is IN_PATH
+ SimplePath in_path;
+
+ // should this store location info?
+
+public:
+ // Creates a Visibility - TODO make constructor protected or private?
+ Visibility (PublicVisType public_vis_type, SimplePath in_path)
+ : public_vis_type (public_vis_type), in_path (::std::move (in_path))
+ {
+ if (public_vis_type != IN_PATH && !in_path.is_empty ())
+ {
+ // error - invalid state
+
+ // just ignore path if vis type is not that
+ }
+ }
+
+ // Returns whether visibility is in an error state.
+ inline bool is_error () const
+ {
+ return public_vis_type == IN_PATH && in_path.is_empty ();
+ }
+
+ // Creates an error visibility.
+ static Visibility create_error ()
+ {
+ return Visibility (IN_PATH, SimplePath::create_empty ());
+ }
+
+ // Unique pointer custom clone function
+ /*::std::unique_ptr<Visibility> clone_visibility() const {
+ return ::std::unique_ptr<Visibility>(clone_visibility_impl());
+ }*/
+
+ /* TODO: think of a way to only allow valid Visibility states - polymorphism
+ * is one idea but may be too resource-intensive. */
+
+ // Creates a public visibility with no further features/arguments.
+ static Visibility create_public ()
+ {
+ return Visibility (NONE, SimplePath::create_empty ());
+ }
+
+ // Creates a public visibility with crate-relative paths or whatever.
+ static Visibility create_crate ()
+ {
+ return Visibility (CRATE, SimplePath::create_empty ());
+ }
+
+ // Creates a public visibility with self-relative paths or whatever.
+ static Visibility create_self ()
+ {
+ return Visibility (SELF, SimplePath::create_empty ());
+ }
+
+ // Creates a public visibility with parent module-relative paths or
+ // whatever.
+ static Visibility create_super ()
+ {
+ return Visibility (SUPER, SimplePath::create_empty ());
+ }
+
+ // Creates a public visibility with a given path or whatever.
+ static Visibility create_in_path (SimplePath in_path)
+ {
+ return Visibility (IN_PATH, ::std::move (in_path));
+ }
+
+ ::std::string as_string () const;
+
+protected:
+ // Clone function implementation - not currently virtual but may be if
+ // polymorphism used
+ /*virtual*/ Visibility *clone_visibility_impl () const
+ {
+ return new Visibility (*this);
+ }
+};
+
+// A method (function belonging to a type)
+class Method : public InherentImplItem, public TraitImplItem
+{
+ // moved from impl items for consistency
+ ::std::vector<Attribute> outer_attrs;
+ Visibility vis;
+
+ FunctionQualifiers qualifiers;
+ Identifier method_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ SelfParam self_param;
+
+ // bool has_params;
+ // FunctionParams function_params;
+ ::std::vector<FunctionParam> function_params; // inlined
+
+ // bool has_return_type;
+ // FunctionReturnType return_type;
+ ::std::unique_ptr<Type> return_type; // inlined
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // BlockExpr* expr;
+ ::std::unique_ptr<BlockExpr> expr;
+
+ Location locus;
+
+public:
+ /*~Method() {
+ delete expr;
+ }*/
+
+ // Returns whether the method is in an error state.
+ inline bool is_error () const
+ {
+ return expr == NULL || method_name.empty () || self_param.is_error ();
+ }
+
+ // Creates an error state method.
+ static Method create_error ()
+ {
+ return Method ("", FunctionQualifiers (FunctionQualifiers::NONE, true),
+ ::std::vector< ::std::unique_ptr<GenericParam> > (),
+ SelfParam::create_error (), ::std::vector<FunctionParam> (),
+ NULL, WhereClause::create_empty (), NULL,
+ Visibility::create_error (), ::std::vector<Attribute> ());
+ }
+
+ // Returns whether the method has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether the method has parameters.
+ inline bool has_params () const { return !function_params.empty (); }
+
+ // Returns whether the method has a return type (void otherwise).
+ inline bool has_return_type () const { return return_type != NULL; }
+
+ // Returns whether the where clause exists (i.e. has items)
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Returns whether method has a non-default visibility.
+ inline bool has_visibility () const { return !vis.is_error (); }
+
+ // Mega-constructor with all possible fields
+ Method (Identifier method_name, FunctionQualifiers qualifiers,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ SelfParam self_param, ::std::vector<FunctionParam> function_params,
+ ::std::unique_ptr<Type> return_type, WhereClause where_clause,
+ ::std::unique_ptr<BlockExpr> function_body, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus = Location ())
+ : outer_attrs (::std::move (outer_attrs)), vis (::std::move (vis)),
+ qualifiers (::std::move (qualifiers)),
+ method_name (::std::move (method_name)),
+ generic_params (::std::move (generic_params)),
+ self_param (::std::move (self_param)),
+ function_params (::std::move (function_params)),
+ return_type (::std::move (return_type)),
+ where_clause (::std::move (where_clause)),
+ expr (::std::move (function_body)), locus (locus)
+ {}
+
+ // TODO: add constructor with less fields
+
+ // Copy constructor with clone
+ Method (Method const &other)
+ : outer_attrs (other.outer_attrs), vis (other.vis),
+ qualifiers (other.qualifiers), method_name (other.method_name),
+ /*generic_params(other.generic_params),*/ self_param (other.self_param),
+ function_params (other.function_params),
+ return_type (other.return_type->clone_type ()),
+ where_clause (other.where_clause), expr (other.expr->clone_block_expr ()),
+ locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ //~Method() = default;
+
+ // Overloaded assignment operator to clone
+ Method &operator= (Method const &other)
+ {
+ method_name = other.method_name;
+ outer_attrs = other.outer_attrs;
+ vis = other.vis;
+ qualifiers = other.qualifiers;
+ // generic_params = other.generic_params;
+ self_param = other.self_param;
+ function_params = other.function_params;
+ return_type = other.return_type->clone_type ();
+ where_clause = other.where_clause;
+ expr = other.expr->clone_block_expr ();
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ Method (Method &&other) = default;
+ Method &operator= (Method &&other) = default;
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Method *clone_inherent_impl_item_impl () const OVERRIDE
+ {
+ return new Method (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Method *clone_trait_impl_item_impl () const OVERRIDE
+ {
+ return new Method (*this);
+ }
+};
+
+// Item that supports visibility - abstract base class
+class VisItem : public Item
+{
+ Visibility visibility;
+
+protected:
+ // Visibility constructor
+ VisItem (Visibility visibility,
+ ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute> ())
+ : Item (::std::move (outer_attrs)), visibility (::std::move (visibility))
+ {}
+
+ // Visibility copy constructor
+ VisItem (VisItem const &other) : Item (other), visibility (other.visibility)
+ {}
+
+ // Destructor - define here if required
+
+ // Overload assignment operator to clone
+ VisItem &operator= (VisItem const &other)
+ {
+ Item::operator= (other);
+ visibility = other.visibility;
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ VisItem (VisItem &&other) = default;
+ VisItem &operator= (VisItem &&other) = default;
+
+public:
+ // Does the item have some kind of public visibility (non-default
+ // visibility)?
+ inline bool has_visibility () const { return !visibility.is_error (); }
+
+ virtual ::std::string as_string () const;
+};
+
+// Rust module item - abstract base class
+class Module : public VisItem
+{
+ Identifier module_name;
+
+ Location locus;
+
+protected:
+ // Protected constructor
+ Module (Identifier module_name, Visibility visibility, Location locus,
+ ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute> ())
+ : VisItem (::std::move (visibility), ::std::move (outer_attrs)),
+ module_name (module_name), locus (locus)
+ {}
+
+public:
+ virtual ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+};
+
+// Module with a body, defined in file
+class ModuleBodied : public Module
+{
+ // bool has_inner_attrs;
+ ::std::vector<Attribute> inner_attrs;
+ // bool has_items;
+ //::std::vector<Item> items;
+ ::std::vector< ::std::unique_ptr<Item> > items;
+
+public:
+ virtual ::std::string as_string () const;
+
+ // Returns whether the module has items in its body.
+ inline bool has_items () const { return !items.empty (); }
+
+ // Returns whether the module has any inner attributes.
+ inline bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+ // Full constructor
+ ModuleBodied (Identifier name, Location locus,
+ ::std::vector< ::std::unique_ptr<Item> > items
+ = ::std::vector< ::std::unique_ptr<Item> > (),
+ Visibility visibility = Visibility::create_error (),
+ ::std::vector<Attribute> inner_attrs
+ = ::std::vector<Attribute> (),
+ ::std::vector<Attribute> outer_attrs
+ = ::std::vector<Attribute> ())
+ : Module (::std::move (name), ::std::move (visibility), locus,
+ ::std::move (outer_attrs)),
+ inner_attrs (::std::move (inner_attrs)), items (::std::move (items))
+ {}
+
+ // Copy constructor with vector clone
+ ModuleBodied (ModuleBodied const &other)
+ : Module (other), inner_attrs (other.inner_attrs)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ items.reserve (other.items.size ());
+
+ for (const auto &e : other.items)
+ {
+ items.push_back (e->clone_item ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ ModuleBodied &operator= (ModuleBodied const &other)
+ {
+ Module::operator= (other);
+ inner_attrs = other.inner_attrs;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ items.reserve (other.items.size ());
+
+ for (const auto &e : other.items)
+ {
+ items.push_back (e->clone_item ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ ModuleBodied (ModuleBodied &&other) = default;
+ ModuleBodied &operator= (ModuleBodied &&other) = default;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+ // Override that runs the function recursively on all items contained within
+ // the module.
+ virtual void
+ add_crate_name (::std::vector< ::std::string> &names) const OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ModuleBodied *clone_item_impl () const OVERRIDE
+ {
+ return new ModuleBodied (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual ModuleBodied* clone_statement_impl() const OVERRIDE {
+ return new ModuleBodied(*this);
+ }*/
+};
+
+// Module without a body, loaded from external file
+class ModuleNoBody : public Module
+{
+public:
+ ::std::string as_string () const;
+
+ // Full constructor
+ ModuleNoBody (Identifier name, Visibility visibility,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : Module (::std::move (name), ::std::move (visibility), locus,
+ ::std::move (outer_attrs))
+ {}
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ModuleNoBody *clone_item_impl () const OVERRIDE
+ {
+ return new ModuleNoBody (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual ModuleNoBody* clone_statement_impl() const OVERRIDE {
+ return new ModuleNoBody(*this);
+ }*/
+};
+
+// Rust extern crate declaration AST node
+class ExternCrate : public VisItem
+{
+ // this is either an identifier or "self", with self parsed to string
+ ::std::string referenced_crate;
+ // bool has_as_clause;
+ // AsClause as_clause;
+ // this is either an identifier or "_", with _ parsed to string
+ ::std::string as_clause_name;
+
+ Location locus;
+
+ /* e.g.
+ "extern crate foo as _"
+ "extern crate foo"
+ "extern crate std as cool_std" */
+public:
+ ::std::string as_string () const;
+
+ // Returns whether extern crate declaration has an as clause.
+ inline bool has_as_clause () const { return !as_clause_name.empty (); }
+
+ // Returns whether extern crate declaration references the current crate
+ // (i.e. self).
+ inline bool references_self () const { return referenced_crate == "self"; }
+
+ // Constructor
+ ExternCrate (::std::string referenced_crate, Visibility visibility,
+ ::std::vector<Attribute> outer_attrs, Location locus,
+ ::std::string as_clause_name = ::std::string ())
+ : VisItem (::std::move (visibility), ::std::move (outer_attrs)),
+ referenced_crate (::std::move (referenced_crate)),
+ as_clause_name (::std::move (as_clause_name)), locus (locus)
+ {}
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+ // Override that adds extern crate name in decl to passed list of names.
+ virtual void
+ add_crate_name (::std::vector< ::std::string> &names) const OVERRIDE
+ {
+ names.push_back (referenced_crate);
+ }
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ExternCrate *clone_item_impl () const OVERRIDE
+ {
+ return new ExternCrate (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual ExternCrate* clone_statement_impl() const OVERRIDE {
+ return new ExternCrate(*this);
+ }*/
+};
+
+// The path-ish thing referred to in a use declaration - abstract base class
+class UseTree
+{
+ Location locus;
+
+public:
+ virtual ~UseTree () {}
+
+ // Unique pointer custom clone function
+ ::std::unique_ptr<UseTree> clone_use_tree () const
+ {
+ return ::std::unique_ptr<UseTree> (clone_use_tree_impl ());
+ }
+
+ virtual ::std::string as_string () const = 0;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+protected:
+ // Clone function implementation as pure virtual method
+ virtual UseTree *clone_use_tree_impl () const = 0;
+
+ UseTree (Location locus) : locus (locus) {}
+};
+
+// Use tree with a glob (wildcard) operator
+class UseTreeGlob : public UseTree
+{
+public:
+ enum PathType
+ {
+ NO_PATH,
+ GLOBAL,
+ PATH_PREFIXED
+ };
+
+private:
+ PathType glob_type;
+ SimplePath path;
+
+public:
+ UseTreeGlob (PathType glob_type, SimplePath path, Location locus)
+ : UseTree (locus), glob_type (glob_type), path (::std::move (path))
+ {}
+
+ // Returns whether has path. Should be made redundant by PathType
+ // PATH_PREFIXED.
+ inline bool has_path () const { return !path.is_empty (); }
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+ // TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory
+ // methods?
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual UseTreeGlob *clone_use_tree_impl () const OVERRIDE
+ {
+ return new UseTreeGlob (*this);
+ }
+};
+
+// Use tree with a list of paths with a common prefix
+class UseTreeList : public UseTree
+{
+public:
+ enum PathType
+ {
+ NO_PATH,
+ GLOBAL,
+ PATH_PREFIXED
+ };
+
+private:
+ PathType path_type;
+ SimplePath path;
+
+ ::std::vector< ::std::unique_ptr<UseTree> > trees;
+
+public:
+ UseTreeList (PathType path_type, SimplePath path,
+ ::std::vector< ::std::unique_ptr<UseTree> > trees,
+ Location locus)
+ : UseTree (locus), path_type (path_type), path (::std::move (path)),
+ trees (::std::move (trees))
+ {}
+
+ // copy constructor with vector clone
+ UseTreeList (UseTreeList const &other)
+ : UseTree (other), path_type (other.path_type), path (other.path)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ trees.reserve (other.trees.size ());
+
+ for (const auto &e : other.trees)
+ {
+ trees.push_back (e->clone_use_tree ());
+ }
+ }
+
+ // overloaded assignment operator with vector clone
+ UseTreeList &operator= (UseTreeList const &other)
+ {
+ UseTree::operator= (other);
+ path_type = other.path_type;
+ path = other.path;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ trees.reserve (other.trees.size ());
+
+ for (const auto &e : other.trees)
+ {
+ trees.push_back (e->clone_use_tree ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ UseTreeList (UseTreeList &&other) = default;
+ UseTreeList &operator= (UseTreeList &&other) = default;
+
+ // Returns whether has path. Should be made redundant by path_type.
+ inline bool has_path () const { return !path.is_empty (); }
+
+ // Returns whether has inner tree elements.
+ inline bool has_trees () const { return !trees.empty (); }
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+ // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+ // methods?
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual UseTreeList *clone_use_tree_impl () const OVERRIDE
+ {
+ return new UseTreeList (*this);
+ }
+};
+
+// Use tree where it rebinds the module name as something else
+class UseTreeRebind : public UseTree
+{
+public:
+ enum NewBindType
+ {
+ NONE,
+ IDENTIFIER,
+ WILDCARD
+ };
+
+private:
+ SimplePath path;
+
+ NewBindType bind_type;
+ Identifier identifier; // only if NewBindType is IDENTIFIER
+
+public:
+ UseTreeRebind (NewBindType bind_type, SimplePath path, Location locus,
+ Identifier identifier = ::std::string ())
+ : UseTree (locus), path (::std::move (path)), bind_type (bind_type),
+ identifier (::std::move (identifier))
+ {}
+
+ // Returns whether has path (this should always be true).
+ inline bool has_path () const { return !path.is_empty (); }
+
+ // Returns whether has identifier (or, rather, is allowed to).
+ inline bool has_identifier () const { return bind_type == IDENTIFIER; }
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+ // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+ // methods?
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual UseTreeRebind *clone_use_tree_impl () const OVERRIDE
+ {
+ return new UseTreeRebind (*this);
+ }
+};
+
+// Rust use declaration (i.e. for modules) AST node
+class UseDeclaration : public VisItem
+{
+ ::std::unique_ptr<UseTree> use_tree;
+
+ Location locus;
+
+public:
+ ::std::string as_string () const;
+
+ UseDeclaration (::std::unique_ptr<UseTree> use_tree, Visibility visibility,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (visibility), ::std::move (outer_attrs)),
+ use_tree (::std::move (use_tree)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ UseDeclaration (UseDeclaration const &other)
+ : VisItem (other), use_tree (other.use_tree->clone_use_tree ()),
+ locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ UseDeclaration &operator= (UseDeclaration const &other)
+ {
+ VisItem::operator= (other);
+ use_tree = other.use_tree->clone_use_tree ();
+ // visibility = other.visibility->clone_visibility();
+ // outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ UseDeclaration (UseDeclaration &&other) = default;
+ UseDeclaration &operator= (UseDeclaration &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual UseDeclaration *clone_item_impl () const OVERRIDE
+ {
+ return new UseDeclaration (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual UseDeclaration* clone_statement_impl() const OVERRIDE {
+ return new UseDeclaration(*this);
+ }*/
+};
+
+// Parameters used in a function - TODO inline?
+/*struct FunctionParams {
+ ::std::vector<FunctionParam> function_params;
+};*/
+
+// Rust function declaration AST node
+class Function : public VisItem, public InherentImplItem, public TraitImplItem
+{
+ FunctionQualifiers qualifiers;
+
+ Identifier function_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_function_params;
+ // FunctionParams function_params;
+ ::std::vector<FunctionParam> function_params; // inlined
+
+ // bool has_function_return_type;
+ // Type return_type;
+ ::std::unique_ptr<Type> return_type;
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // BlockExpr* function_body;
+ ::std::unique_ptr<BlockExpr> function_body;
+
+ Location locus;
+
+public:
+ /*~Function() {
+ delete function_body;
+ }*/
+ ::std::string as_string () const;
+
+ // Returns whether function has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether function has regular parameters.
+ inline bool has_function_params () const { return !function_params.empty (); }
+
+ // Returns whether function has return type - if not, it is void.
+ inline bool has_function_return_type () const { return return_type != NULL; }
+
+ // Returns whether function has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Mega-constructor with all possible fields
+ Function (Identifier function_name, FunctionQualifiers qualifiers,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::vector<FunctionParam> function_params,
+ ::std::unique_ptr<Type> return_type, WhereClause where_clause,
+ ::std::unique_ptr<BlockExpr> function_body, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ qualifiers (::std::move (qualifiers)),
+ function_name (::std::move (function_name)),
+ generic_params (::std::move (generic_params)),
+ function_params (::std::move (function_params)),
+ return_type (::std::move (return_type)),
+ where_clause (::std::move (where_clause)),
+ function_body (::std::move (function_body)), locus (locus)
+ {}
+
+ // TODO: add constructor with less fields
+
+ // Copy constructor with clone
+ Function (Function const &other)
+ : VisItem (other), qualifiers (other.qualifiers),
+ function_name (other.function_name),
+ /*generic_params(other.generic_params),*/ function_params (
+ other.function_params),
+ return_type (other.return_type->clone_type ()),
+ where_clause (other.where_clause),
+ function_body (other.function_body->clone_block_expr ()),
+ locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ Function &operator= (Function const &other)
+ {
+ VisItem::operator= (other);
+ function_name = other.function_name;
+ qualifiers = other.qualifiers;
+ // generic_params = other.generic_params;
+ function_params = other.function_params;
+ return_type = other.return_type->clone_type ();
+ where_clause = other.where_clause;
+ function_body = other.function_body->clone_block_expr ();
+ // visibility = other.visibility->clone_visibility();
+ // outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ Function (Function &&other) = default;
+ Function &operator= (Function &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Function *clone_item_impl () const OVERRIDE
+ {
+ return new Function (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Function *clone_inherent_impl_item_impl () const OVERRIDE
+ {
+ return new Function (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Function *clone_trait_impl_item_impl () const OVERRIDE
+ {
+ return new Function (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual Function* clone_statement_impl() const OVERRIDE {
+ return new Function(*this);
+ }*/
+};
+
+// Rust type alias (i.e. typedef) AST node
+class TypeAlias : public VisItem, public TraitImplItem
+{
+ Identifier new_type_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // Type exiting_type;
+ ::std::unique_ptr<Type> existing_type;
+
+ Location locus;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether type alias has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether type alias has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Mega-constructor with all possible fields
+ TypeAlias (Identifier new_type_name,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause, ::std::unique_ptr<Type> existing_type,
+ Visibility vis, ::std::vector<Attribute> outer_attrs,
+ Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ new_type_name (::std::move (new_type_name)),
+ generic_params (::std::move (generic_params)),
+ where_clause (::std::move (where_clause)),
+ existing_type (::std::move (existing_type)), locus (locus)
+ {}
+
+ // Copy constructor
+ TypeAlias (TypeAlias const &other)
+ : VisItem (other),
+ new_type_name (
+ other.new_type_name), /*generic_params(other.generic_params),*/
+ where_clause (other.where_clause),
+ existing_type (other.existing_type->clone_type ()), locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ TypeAlias &operator= (TypeAlias const &other)
+ {
+ VisItem::operator= (other);
+ new_type_name = other.new_type_name;
+ // generic_params = other.generic_params;
+ where_clause = other.where_clause;
+ existing_type = other.existing_type->clone_type ();
+ // visibility = other.visibility->clone_visibility();
+ // outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TypeAlias (TypeAlias &&other) = default;
+ TypeAlias &operator= (TypeAlias &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual TypeAlias *clone_item_impl () const OVERRIDE
+ {
+ return new TypeAlias (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual TypeAlias *clone_trait_impl_item_impl () const OVERRIDE
+ {
+ return new TypeAlias (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual TypeAlias* clone_statement_impl() const OVERRIDE {
+ return new TypeAlias(*this);
+ }*/
+};
+
+// Rust base struct declaration AST node - abstract base class
+class Struct : public VisItem
+{
+protected:
+ // protected to enable access by derived classes - allows better as_string
+ Identifier struct_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ Location locus;
+
+public:
+ // Returns whether struct has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether struct has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ Location get_locus () const { return locus; }
+
+protected:
+ Struct (Identifier struct_name,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause, Visibility vis, Location locus,
+ ::std::vector<Attribute> outer_attrs = ::std::vector<Attribute> ())
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ struct_name (::std::move (struct_name)),
+ generic_params (::std::move (generic_params)),
+ where_clause (::std::move (where_clause)), locus (locus)
+ {}
+
+ // Copy constructor with vector clone
+ Struct (Struct const &other)
+ : VisItem (other), struct_name (other.struct_name),
+ where_clause (other.where_clause), locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ Struct &operator= (Struct const &other)
+ {
+ VisItem::operator= (other);
+ struct_name = other.struct_name;
+ where_clause = other.where_clause;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ Struct (Struct &&other) = default;
+ Struct &operator= (Struct &&other) = default;
+};
+
+// A single field in a struct
+struct StructField
+{
+private:
+ // bool has_outer_attributes;
+ ::std::vector<Attribute> outer_attrs;
+
+ // bool has_visibility;
+ Visibility visibility;
+
+ Identifier field_name;
+ // Type field_type;
+ ::std::unique_ptr<Type> field_type;
+
+ // should this store location info?
+
+public:
+ // Returns whether struct field has any outer attributes.
+ inline bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+ // Returns whether struct field has a non-private (non-default) visibility.
+ inline bool has_visibility () const { return !visibility.is_error (); }
+
+ StructField (Identifier field_name, ::std::unique_ptr<Type> field_type,
+ Visibility vis,
+ ::std::vector<Attribute> outer_attrs
+ = ::std::vector<Attribute> ())
+ : outer_attrs (::std::move (outer_attrs)), visibility (::std::move (vis)),
+ field_name (::std::move (field_name)),
+ field_type (::std::move (field_type))
+ {}
+
+ // Copy constructor
+ StructField (StructField const &other)
+ : outer_attrs (other.outer_attrs), visibility (other.visibility),
+ field_name (other.field_name),
+ field_type (other.field_type->clone_type ())
+ {}
+
+ ~StructField () = default;
+
+ // Overloaded assignment operator to clone
+ StructField &operator= (StructField const &other)
+ {
+ field_name = other.field_name;
+ field_type = other.field_type->clone_type ();
+ visibility = other.visibility;
+ outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ StructField (StructField &&other) = default;
+ StructField &operator= (StructField &&other) = default;
+
+ // Returns whether struct field is in an error state.
+ inline bool is_error () const
+ {
+ return field_name.empty () && field_type == NULL;
+ // this should really be an or since neither are allowed
+ }
+
+ // Creates an error state struct field.
+ static StructField create_error ()
+ {
+ return StructField (::std::string (""), NULL, Visibility::create_error ());
+ }
+
+ ::std::string as_string () const;
+};
+
+// Rust struct declaration with true struct type AST node
+class StructStruct : public Struct
+{
+ ::std::vector<StructField> fields;
+ bool is_unit;
+
+public:
+ ::std::string as_string () const;
+
+ // Mega-constructor with all possible fields
+ StructStruct (::std::vector<StructField> fields, Identifier struct_name,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause, bool is_unit, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : Struct (::std::move (struct_name), ::std::move (generic_params),
+ ::std::move (where_clause), ::std::move (vis), locus,
+ ::std::move (outer_attrs)),
+ fields (::std::move (fields)), is_unit (is_unit)
+ {}
+
+ // Unit struct constructor
+ StructStruct (Identifier struct_name,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : Struct (::std::move (struct_name), ::std::move (generic_params),
+ ::std::move (where_clause), ::std::move (vis), locus,
+ ::std::move (outer_attrs)),
+ is_unit (true)
+ {}
+ // TODO: can a unit struct have generic fields? assuming yes for now.
+
+ /* Returns whether the struct is a unit struct - struct defined without
+ * fields. This is
+ * important because it also means an implicit constant of its type is
+ * defined. */
+ inline bool is_unit_struct () const { return is_unit; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual StructStruct *clone_item_impl () const OVERRIDE
+ {
+ return new StructStruct (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual StructStruct* clone_statement_impl() const OVERRIDE {
+ return new StructStruct(*this);
+ }*/
+};
+
+// A single field in a tuple
+struct TupleField
+{
+private:
+ // bool has_outer_attributes;
+ ::std::vector<Attribute> outer_attrs;
+
+ // bool has_visibility;
+ Visibility visibility;
+
+ // Type field_type;
+ ::std::unique_ptr<Type> field_type;
+
+ // should this store location info?
+
+public:
+ // Returns whether tuple field has outer attributes.
+ inline bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+ // Returns whether tuple field has a non-default visibility (i.e. a public
+ // one)
+ inline bool has_visibility () const { return !visibility.is_error (); }
+
+ // Complete constructor
+ TupleField (::std::unique_ptr<Type> field_type, Visibility vis,
+ ::std::vector<Attribute> outer_attrs
+ = ::std::vector<Attribute> ())
+ : outer_attrs (::std::move (outer_attrs)), visibility (::std::move (vis)),
+ field_type (::std::move (field_type))
+ {}
+
+ // Copy constructor with clone
+ TupleField (TupleField const &other)
+ : outer_attrs (other.outer_attrs), visibility (other.visibility),
+ field_type (other.field_type->clone_type ())
+ {}
+
+ ~TupleField () = default;
+
+ // Overloaded assignment operator to clone
+ TupleField &operator= (TupleField const &other)
+ {
+ field_type = other.field_type->clone_type ();
+ visibility = other.visibility;
+ outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ TupleField (TupleField &&other) = default;
+ TupleField &operator= (TupleField &&other) = default;
+
+ // Returns whether tuple field is in an error state.
+ inline bool is_error () const { return field_type == NULL; }
+
+ // Creates an error state tuple field.
+ static TupleField create_error ()
+ {
+ return TupleField (NULL, Visibility::create_error ());
+ }
+
+ ::std::string as_string () const;
+};
+
+// Rust tuple declared using struct keyword AST node
+class TupleStruct : public Struct
+{
+ ::std::vector<TupleField> fields;
+
+public:
+ ::std::string as_string () const;
+
+ // Mega-constructor with all possible fields
+ TupleStruct (::std::vector<TupleField> fields, Identifier struct_name,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : Struct (::std::move (struct_name), ::std::move (generic_params),
+ ::std::move (where_clause), ::std::move (vis), locus,
+ ::std::move (outer_attrs)),
+ fields (::std::move (fields))
+ {}
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual TupleStruct *clone_item_impl () const OVERRIDE
+ {
+ return new TupleStruct (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual TupleStruct* clone_statement_impl() const OVERRIDE {
+ return new TupleStruct(*this);
+ }*/
+};
+
+// An item used in an "enum" tagged union - not abstract: base represents a
+// name-only enum
+class EnumItem
+{
+ // bool has_attrs;
+ ::std::vector<Attribute> outer_attrs;
+
+ Identifier variant_name;
+
+ Location locus;
+
+public:
+ virtual ~EnumItem () {}
+
+ // Returns whether enum item has outer attributes.
+ inline bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+ EnumItem (Identifier variant_name, ::std::vector<Attribute> outer_attrs,
+ Location locus)
+ : outer_attrs (::std::move (outer_attrs)),
+ variant_name (::std::move (variant_name)), locus (locus)
+ {}
+
+ // Unique pointer custom clone function
+ ::std::unique_ptr<EnumItem> clone_enum_item () const
+ {
+ return ::std::unique_ptr<EnumItem> (clone_enum_item_impl ());
+ }
+
+ virtual ::std::string as_string () const;
+
+ // not pure virtual as not abstract
+ virtual void accept_vis (ASTVisitor &vis);
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual EnumItem *clone_enum_item_impl () const
+ {
+ return new EnumItem (*this);
+ }
+};
+
+// A tuple item used in an "enum" tagged union
+class EnumItemTuple : public EnumItem
+{
+ // bool has_tuple_fields;
+ ::std::vector<TupleField> tuple_fields;
+
+public:
+ // Returns whether tuple enum item has tuple fields.
+ inline bool has_tuple_fields () const { return !tuple_fields.empty (); }
+
+ EnumItemTuple (Identifier variant_name,
+ ::std::vector<TupleField> tuple_fields,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : EnumItem (::std::move (variant_name), ::std::move (outer_attrs), locus),
+ tuple_fields (::std::move (tuple_fields))
+ {}
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual EnumItemTuple *clone_enum_item_impl () const
+ {
+ return new EnumItemTuple (*this);
+ }
+};
+
+// A struct item used in an "enum" tagged union
+class EnumItemStruct : public EnumItem
+{
+ // bool has_struct_fields;
+ ::std::vector<StructField> struct_fields;
+
+public:
+ // Returns whether struct enum item has struct fields.
+ inline bool has_struct_fields () const { return !struct_fields.empty (); }
+
+ EnumItemStruct (Identifier variant_name,
+ ::std::vector<StructField> struct_fields,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : EnumItem (::std::move (variant_name), ::std::move (outer_attrs), locus),
+ struct_fields (::std::move (struct_fields))
+ {}
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual EnumItemStruct *clone_enum_item_impl () const
+ {
+ return new EnumItemStruct (*this);
+ }
+};
+
+// A discriminant (numbered enum) item used in an "enum" tagged union
+class EnumItemDiscriminant : public EnumItem
+{
+ // Expr* expression;
+ ::std::unique_ptr<Expr> expression;
+
+public:
+ /*~EnumItemDiscriminant() {
+ delete expression;
+ }*/
+
+ EnumItemDiscriminant (Identifier variant_name, ::std::unique_ptr<Expr> expr,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : EnumItem (::std::move (variant_name), ::std::move (outer_attrs), locus),
+ expression (::std::move (expr))
+ {}
+
+ // Copy constructor with clone
+ EnumItemDiscriminant (EnumItemDiscriminant const &other)
+ : EnumItem (other), expression (other.expression->clone_expr ())
+ {}
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ EnumItemDiscriminant &operator= (EnumItemDiscriminant const &other)
+ {
+ EnumItem::operator= (other);
+ expression = other.expression->clone_expr ();
+ // variant_name = other.variant_name;
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ EnumItemDiscriminant (EnumItemDiscriminant &&other) = default;
+ EnumItemDiscriminant &operator= (EnumItemDiscriminant &&other) = default;
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual EnumItemDiscriminant *clone_enum_item_impl () const
+ {
+ return new EnumItemDiscriminant (*this);
+ }
+};
+
+// AST node for Rust "enum" - tagged union
+class Enum : public VisItem
+{
+ Identifier enum_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ ::std::vector< ::std::unique_ptr<EnumItem> > items;
+
+ Location locus;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether "enum" has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether "enum" has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ /* Returns whether enum is a "zero-variant" (no possible variant) enum,
+ * which cannot be instantiated.*/
+ inline bool is_zero_variant () const { return items.empty (); }
+
+ // Mega-constructor
+ Enum (Identifier enum_name, Visibility vis,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause,
+ ::std::vector< ::std::unique_ptr<EnumItem> > items,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ enum_name (::std::move (enum_name)),
+ generic_params (::std::move (generic_params)),
+ where_clause (::std::move (where_clause)), items (::std::move (items)),
+ locus (locus)
+ {}
+
+ // TODO: constructor with less arguments
+
+ // Copy constructor with vector clone
+ Enum (Enum const &other)
+ : VisItem (other), enum_name (other.enum_name),
+ where_clause (other.where_clause), locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ items.reserve (other.items.size ());
+
+ for (const auto &e : other.items)
+ {
+ items.push_back (e->clone_enum_item ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ Enum &operator= (Enum const &other)
+ {
+ VisItem::operator= (other);
+ enum_name = other.enum_name;
+ where_clause = other.where_clause;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ items.reserve (other.items.size ());
+
+ for (const auto &e : other.items)
+ {
+ items.push_back (e->clone_enum_item ());
+ }
+
+ return *this;
+ }
+
+ // Move constructors
+ Enum (Enum &&other) = default;
+ Enum &operator= (Enum &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Enum *clone_item_impl () const OVERRIDE { return new Enum (*this); }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual Enum* clone_statement_impl() const OVERRIDE {
+ return new Enum(*this);
+ }*/
+};
+
+// Rust untagged union used for C compat AST node
+class Union : public VisItem
+{
+ Identifier union_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ ::std::vector<StructField> variants;
+
+ Location locus;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether union has generic params.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether union has where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ Union (Identifier union_name, Visibility vis,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ WhereClause where_clause, ::std::vector<StructField> variants,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ union_name (::std::move (union_name)),
+ generic_params (::std::move (generic_params)),
+ where_clause (::std::move (where_clause)),
+ variants (::std::move (variants)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ Union (Union const &other)
+ : VisItem (other), union_name (other.union_name),
+ where_clause (other.where_clause), variants (other.variants),
+ locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ // overloaded assignment operator with vector clone
+ Union &operator= (Union const &other)
+ {
+ VisItem::operator= (other);
+ union_name = other.union_name;
+ where_clause = other.where_clause;
+ variants = other.variants;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ Union (Union &&other) = default;
+ Union &operator= (Union &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Union *clone_item_impl () const OVERRIDE { return new Union (*this); }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual Union* clone_statement_impl() const OVERRIDE {
+ return new Union(*this);
+ }*/
+};
+
+// "Constant item" AST node - used for constant, compile-time expressions
+// within module scope
+class ConstantItem : public VisItem,
+ public InherentImplItem,
+ public TraitImplItem
+{
+ // either has an identifier or "_" - maybe handle in identifier?
+ // bool identifier_is_underscore;
+ // if no identifier declared, identifier will be "_"
+ Identifier identifier;
+
+ // Type type;
+ ::std::unique_ptr<Type> type;
+
+ // Expr* const_expr;
+ ::std::unique_ptr<Expr> const_expr;
+
+ Location locus;
+
+public:
+ /*~ConstantItem() {
+ delete const_expr;
+ }*/
+
+ ::std::string as_string () const;
+
+ ConstantItem (Identifier ident, Visibility vis, ::std::unique_ptr<Type> type,
+ ::std::unique_ptr<Expr> const_expr,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ identifier (::std::move (ident)), type (::std::move (type)),
+ const_expr (::std::move (const_expr)), locus (locus)
+ {}
+
+ ConstantItem (ConstantItem const &other)
+ : VisItem (other), identifier (other.identifier),
+ type (other.type->clone_type ()),
+ const_expr (other.const_expr->clone_expr ()), locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overload assignment operator to clone
+ ConstantItem &operator= (ConstantItem const &other)
+ {
+ VisItem::operator= (other);
+ identifier = other.identifier;
+ type = other.type->clone_type ();
+ const_expr = other.const_expr->clone_expr ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ ConstantItem (ConstantItem &&other) = default;
+ ConstantItem &operator= (ConstantItem &&other) = default;
+
+ /* Returns whether constant item is an "unnamed" (wildcard underscore used
+ * as identifier) constant. */
+ inline bool is_unnamed () const { return identifier == ::std::string ("_"); }
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ConstantItem *clone_item_impl () const OVERRIDE
+ {
+ return new ConstantItem (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ConstantItem *clone_inherent_impl_item_impl () const OVERRIDE
+ {
+ return new ConstantItem (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ConstantItem *clone_trait_impl_item_impl () const OVERRIDE
+ {
+ return new ConstantItem (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual ConstantItem* clone_statement_impl() const OVERRIDE {
+ return new ConstantItem(*this);
+ }*/
+};
+
+// Static item AST node - items within module scope with fixed storage
+// duration?
+class StaticItem : public VisItem
+{
+ bool has_mut;
+
+ Identifier name;
+
+ // Type type;
+ ::std::unique_ptr<Type> type;
+
+ // Expr* expr;
+ ::std::unique_ptr<Expr> expr;
+
+ Location locus;
+
+public:
+ /*~StaticItem() {
+ delete expr;
+ }*/
+
+ ::std::string as_string () const;
+
+ StaticItem (Identifier name, bool is_mut, ::std::unique_ptr<Type> type,
+ ::std::unique_ptr<Expr> expr, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)), has_mut (is_mut),
+ name (::std::move (name)), type (::std::move (type)),
+ expr (::std::move (expr)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ StaticItem (StaticItem const &other)
+ : VisItem (other), has_mut (other.has_mut), name (other.name),
+ type (other.type->clone_type ()), expr (other.expr->clone_expr ()),
+ locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ StaticItem &operator= (StaticItem const &other)
+ {
+ VisItem::operator= (other);
+ name = other.name;
+ has_mut = other.has_mut;
+ type = other.type->clone_type ();
+ expr = other.expr->clone_expr ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ StaticItem (StaticItem &&other) = default;
+ StaticItem &operator= (StaticItem &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual StaticItem *clone_item_impl () const OVERRIDE
+ {
+ return new StaticItem (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual StaticItem* clone_statement_impl() const OVERRIDE {
+ return new StaticItem(*this);
+ }*/
+};
+
+// Function declaration in traits
+struct TraitFunctionDecl
+{
+private:
+ // TODO: delete and replace with Function decl item? no as no body in this.
+ FunctionQualifiers qualifiers;
+ Identifier function_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_params;
+ // FunctionParams function_params;
+ ::std::vector<FunctionParam> function_params; // inlined
+
+ // bool has_return_type;
+ // Type return_type;
+ ::std::unique_ptr<Type> return_type;
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // should this store location info?
+
+public:
+ // Returns whether function decl has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether function decl has regular parameters.
+ inline bool has_params () const { return !function_params.empty (); }
+
+ // Returns whether function has return type (otherwise is void).
+ inline bool has_return_type () const { return return_type != NULL; }
+
+ // Returns whether function has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Mega-constructor
+ TraitFunctionDecl (
+ Identifier function_name, FunctionQualifiers qualifiers,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::vector<FunctionParam> function_params,
+ ::std::unique_ptr<Type> return_type, WhereClause where_clause)
+ : qualifiers (::std::move (qualifiers)),
+ function_name (::std::move (function_name)),
+ generic_params (::std::move (generic_params)),
+ function_params (::std::move (function_params)),
+ return_type (::std::move (return_type)),
+ where_clause (::std::move (where_clause))
+ {}
+
+ // Copy constructor with clone
+ TraitFunctionDecl (TraitFunctionDecl const &other)
+ : qualifiers (other.qualifiers), function_name (other.function_name),
+ /*generic_params(other.generic_params),*/ function_params (
+ other.function_params),
+ return_type (other.return_type->clone_type ()),
+ where_clause (other.where_clause)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ ~TraitFunctionDecl () = default;
+
+ // Overloaded assignment operator with clone
+ TraitFunctionDecl &operator= (TraitFunctionDecl const &other)
+ {
+ function_name = other.function_name;
+ qualifiers = other.qualifiers;
+ // generic_params = other.generic_params;
+ function_params = other.function_params;
+ return_type = other.return_type->clone_type ();
+ where_clause = other.where_clause;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TraitFunctionDecl (TraitFunctionDecl &&other) = default;
+ TraitFunctionDecl &operator= (TraitFunctionDecl &&other) = default;
+
+ ::std::string as_string () const;
+};
+
+// Actual trait item function declaration within traits
+class TraitItemFunc : public TraitItem
+{
+ ::std::vector<Attribute> outer_attrs;
+ TraitFunctionDecl decl;
+ // BlockExpr* block_expr;
+ ::std::unique_ptr<BlockExpr> block_expr;
+
+ Location locus;
+
+public:
+ /*~TraitItemFunc() {
+ delete block_expr;
+ }*/
+
+ // Returns whether function has a definition or is just a declaration.
+ inline bool has_definition () const { return block_expr != NULL; }
+
+ TraitItemFunc (TraitFunctionDecl decl,
+ ::std::unique_ptr<BlockExpr> block_expr,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (::std::move (outer_attrs)), decl (::std::move (decl)),
+ block_expr (::std::move (block_expr)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ TraitItemFunc (TraitItemFunc const &other)
+ : outer_attrs (other.outer_attrs),
+ decl (other.decl) /*, block_expr(other.block_expr->clone_block_expr())*/,
+ locus (other.locus)
+ {
+ if (other.block_expr != NULL)
+ {
+ block_expr = other.block_expr->clone_block_expr ();
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ TraitItemFunc &operator= (TraitItemFunc const &other)
+ {
+ TraitItem::operator= (other);
+ outer_attrs = other.outer_attrs;
+ decl = other.decl;
+ locus = other.locus;
+ if (other.block_expr != NULL)
+ {
+ block_expr = other.block_expr->clone_block_expr ();
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TraitItemFunc (TraitItemFunc &&other) = default;
+ TraitItemFunc &operator= (TraitItemFunc &&other) = default;
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual TraitItemFunc *clone_trait_item_impl () const
+ {
+ return new TraitItemFunc (*this);
+ }
+};
+
+// Method declaration within traits
+struct TraitMethodDecl
+{
+private:
+ // TODO: delete and replace with Function decl item? no as no body.
+ FunctionQualifiers qualifiers;
+ Identifier function_name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ SelfParam self_param;
+
+ // bool has_params;
+ // FunctionParams function_params;
+ ::std::vector<FunctionParam> function_params; // inlined
+
+ // bool has_return_type;
+ // Type return_type;
+ ::std::unique_ptr<Type> return_type;
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // should this store location info?
+
+public:
+ // Returns whether method decl has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether method decl has regular parameters.
+ inline bool has_params () const { return !function_params.empty (); }
+
+ // Returns whether method has return type (otherwise is void).
+ inline bool has_return_type () const { return return_type != NULL; }
+
+ // Returns whether method has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Mega-constructor
+ TraitMethodDecl (
+ Identifier function_name, FunctionQualifiers qualifiers,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ SelfParam self_param, ::std::vector<FunctionParam> function_params,
+ ::std::unique_ptr<Type> return_type, WhereClause where_clause)
+ : qualifiers (::std::move (qualifiers)),
+ function_name (::std::move (function_name)),
+ generic_params (::std::move (generic_params)),
+ self_param (::std::move (self_param)),
+ function_params (::std::move (function_params)),
+ return_type (::std::move (return_type)),
+ where_clause (::std::move (where_clause))
+ {}
+
+ // Copy constructor with clone
+ TraitMethodDecl (TraitMethodDecl const &other)
+ : qualifiers (other.qualifiers), function_name (other.function_name),
+ /*generic_params(other.generic_params),*/ self_param (other.self_param),
+ function_params (other.function_params),
+ return_type (other.return_type->clone_type ()),
+ where_clause (other.where_clause)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ ~TraitMethodDecl () = default;
+
+ // Overloaded assignment operator with clone
+ TraitMethodDecl &operator= (TraitMethodDecl const &other)
+ {
+ function_name = other.function_name;
+ qualifiers = other.qualifiers;
+ // generic_params = other.generic_params;
+ self_param = other.self_param;
+ function_params = other.function_params;
+ return_type = other.return_type->clone_type ();
+ where_clause = other.where_clause;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TraitMethodDecl (TraitMethodDecl &&other) = default;
+ TraitMethodDecl &operator= (TraitMethodDecl &&other) = default;
+
+ ::std::string as_string () const;
+};
+
+// Actual trait item method declaration within traits
+class TraitItemMethod : public TraitItem
+{
+ ::std::vector<Attribute> outer_attrs;
+ TraitMethodDecl decl;
+ // BlockExpr* block_expr;
+ ::std::unique_ptr<BlockExpr> block_expr;
+
+ Location locus;
+
+public:
+ /*~TraitItemMethod() {
+ delete block_expr;
+ }*/
+
+ // Returns whether method has a definition or is just a declaration.
+ inline bool has_definition () const { return block_expr != NULL; }
+
+ TraitItemMethod (TraitMethodDecl decl,
+ ::std::unique_ptr<BlockExpr> block_expr,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (::std::move (outer_attrs)), decl (::std::move (decl)),
+ block_expr (::std::move (block_expr)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ TraitItemMethod (TraitItemMethod const &other)
+ : outer_attrs (other.outer_attrs), decl (other.decl),
+ block_expr (other.block_expr->clone_block_expr ()), locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ TraitItemMethod &operator= (TraitItemMethod const &other)
+ {
+ TraitItem::operator= (other);
+ outer_attrs = other.outer_attrs;
+ decl = other.decl;
+ block_expr = other.block_expr->clone_block_expr ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ TraitItemMethod (TraitItemMethod &&other) = default;
+ TraitItemMethod &operator= (TraitItemMethod &&other) = default;
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual TraitItemMethod *clone_trait_item_impl () const
+ {
+ return new TraitItemMethod (*this);
+ }
+};
+
+// Constant item within traits
+class TraitItemConst : public TraitItem
+{
+ ::std::vector<Attribute> outer_attrs;
+ Identifier name;
+ // Type type;
+ ::std::unique_ptr<Type> type;
+
+ // bool has_expression;
+ // Expr* expr;
+ ::std::unique_ptr<Expr> expr;
+
+ Location locus;
+
+public:
+ /*~TraitItemConst() {
+ delete expr;
+ }*/
+
+ // Whether the constant item has an associated expression.
+ inline bool has_expression () const { return expr != NULL; }
+
+ TraitItemConst (Identifier name, ::std::unique_ptr<Type> type,
+ ::std::unique_ptr<Expr> expr,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (::std::move (outer_attrs)), name (::std::move (name)),
+ type (::std::move (type)), expr (::std::move (expr)), locus (locus)
+ {}
+
+ // Copy constructor with clones
+ TraitItemConst (TraitItemConst const &other)
+ : outer_attrs (other.outer_attrs), name (other.name),
+ type (other.type->clone_type ()), expr (other.expr->clone_expr ()),
+ locus (other.locus)
+ {}
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ TraitItemConst &operator= (TraitItemConst const &other)
+ {
+ TraitItem::operator= (other);
+ outer_attrs = other.outer_attrs;
+ name = other.name;
+ type = other.type->clone_type ();
+ expr = other.expr->clone_expr ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ TraitItemConst (TraitItemConst &&other) = default;
+ TraitItemConst &operator= (TraitItemConst &&other) = default;
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual TraitItemConst *clone_trait_item_impl () const
+ {
+ return new TraitItemConst (*this);
+ }
+};
+
+// Type items within traits
+class TraitItemType : public TraitItem
+{
+ ::std::vector<Attribute> outer_attrs;
+
+ Identifier name;
+
+ // bool has_type_param_bounds;
+ // TypeParamBounds type_param_bounds;
+ ::std::vector< ::std::unique_ptr<TypeParamBound> >
+ type_param_bounds; // inlined form
+
+ Location locus;
+
+public:
+ // Returns whether trait item type has type param bounds.
+ inline bool has_type_param_bounds () const
+ {
+ return !type_param_bounds.empty ();
+ }
+
+ TraitItemType (
+ Identifier name,
+ ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (::std::move (outer_attrs)), name (::std::move (name)),
+ type_param_bounds (::std::move (type_param_bounds)), locus (locus)
+ {}
+
+ // Copy constructor with vector clone
+ TraitItemType (TraitItemType const &other)
+ : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ TraitItemType &operator= (TraitItemType const &other)
+ {
+ TraitItem::operator= (other);
+ outer_attrs = other.outer_attrs;
+ name = other.name;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ return *this;
+ }
+
+ // default move constructors
+ TraitItemType (TraitItemType &&other) = default;
+ TraitItemType &operator= (TraitItemType &&other) = default;
+
+ ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Clone function implementation as (not pure) virtual method
+ virtual TraitItemType *clone_trait_item_impl () const
+ {
+ return new TraitItemType (*this);
+ }
+};
+
+// Macro invocation items within traits - TODO is this approach better or is
+// making MacroInvocationSemi itself implement TraitItem better? Leaning
+// toward latter.
+/*class TraitItemMacroInvoc : public TraitItem {
+ MacroInvocationSemi macro_invoc;
+
+ public:
+ TraitItemMacroInvoc(
+ MacroInvocationSemi macro_invoc, ::std::vector<Attribute> outer_attrs) :
+ macro_invoc(macro_invoc),
+ TraitItem(outer_attrs) {}
+};*/
+// replaced with MacroInvocationSemi implementing TraitItem
+
+// Rust trait item declaration AST node
+class Trait : public VisItem
+{
+ bool has_unsafe;
+
+ Identifier name;
+
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_type_param_bounds;
+ // TypeParamBounds type_param_bounds;
+ ::std::vector< ::std::unique_ptr<TypeParamBound> >
+ type_param_bounds; // inlined form
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // bool has_trait_items;
+ ::std::vector< ::std::unique_ptr<TraitItem> > trait_items;
+
+ Location locus;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether trait has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether trait has type parameter bounds.
+ inline bool has_type_param_bounds () const
+ {
+ return !type_param_bounds.empty ();
+ }
+
+ // Returns whether trait has where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Returns whether trait has trait items.
+ inline bool has_trait_items () const { return !trait_items.empty (); }
+
+ // Mega-constructor
+ Trait (Identifier name, bool is_unsafe,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::vector< ::std::unique_ptr<TypeParamBound> > type_param_bounds,
+ WhereClause where_clause,
+ ::std::vector< ::std::unique_ptr<TraitItem> > trait_items,
+ Visibility vis, ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ has_unsafe (is_unsafe), name (::std::move (name)),
+ generic_params (::std::move (generic_params)),
+ type_param_bounds (::std::move (type_param_bounds)),
+ where_clause (::std::move (where_clause)),
+ trait_items (::std::move (trait_items)), locus (locus)
+ {}
+
+ // Copy constructor with vector clone
+ Trait (Trait const &other)
+ : VisItem (other), has_unsafe (other.has_unsafe), name (other.name),
+ where_clause (other.where_clause), locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ // again for type param bounds
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ // again for trait items
+ trait_items.reserve (other.trait_items.size ());
+
+ for (const auto &e : other.trait_items)
+ {
+ trait_items.push_back (e->clone_trait_item ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ Trait &operator= (Trait const &other)
+ {
+ VisItem::operator= (other);
+ name = other.name;
+ has_unsafe = other.has_unsafe;
+ where_clause = other.where_clause;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ // again for type param bounds
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+
+ for (const auto &e : other.type_param_bounds)
+ {
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ // again for trait items
+ trait_items.reserve (other.trait_items.size ());
+
+ for (const auto &e : other.trait_items)
+ {
+ trait_items.push_back (e->clone_trait_item ());
+ }
+
+ return *this;
+ }
+
+ // default move constructors
+ Trait (Trait &&other) = default;
+ Trait &operator= (Trait &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual Trait *clone_item_impl () const OVERRIDE { return new Trait (*this); }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual Trait* clone_statement_impl() const OVERRIDE {
+ return new Trait(*this);
+ }*/
+};
+
+// Implementation item declaration AST node - abstract base class
+class Impl : public VisItem
+{
+ // must be protected to allow subclasses to access them properly
+protected:
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // Type trait_type;
+ ::std::unique_ptr<Type> trait_type;
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ // bool has_inner_attrs;
+ ::std::vector<Attribute> inner_attrs;
+
+private:
+ // doesn't really need to be protected as write access probably not needed
+ Location locus;
+
+public:
+ // Returns whether impl has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether impl has where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ // Returns whether impl has inner attributes.
+ inline bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+ Location get_locus () const { return locus; }
+
+protected:
+ // Mega-constructor
+ Impl (::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::unique_ptr<Type> trait_type, WhereClause where_clause,
+ Visibility vis, ::std::vector<Attribute> inner_attrs,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ generic_params (::std::move (generic_params)),
+ trait_type (::std::move (trait_type)),
+ where_clause (::std::move (where_clause)),
+ inner_attrs (::std::move (inner_attrs)), locus (locus)
+ {}
+
+ // Copy constructor
+ Impl (Impl const &other)
+ : VisItem (other),
+ /*generic_params(other.generic_params),*/ trait_type (
+ other.trait_type->clone_type ()),
+ where_clause (other.where_clause), inner_attrs (other.inner_attrs),
+ locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Assignment operator overload with cloning
+ Impl &operator= (Impl const &other)
+ {
+ VisItem::operator= (other);
+ // generic_params = other.generic_params;
+ trait_type = other.trait_type->clone_type ();
+ where_clause = other.where_clause;
+ inner_attrs = other.inner_attrs;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ Impl (Impl &&other) = default;
+ Impl &operator= (Impl &&other) = default;
+};
+
+// Regular "impl foo" impl block declaration AST node
+class InherentImpl : public Impl
+{
+ // bool has_impl_items;
+ ::std::vector< ::std::unique_ptr<InherentImplItem> > impl_items;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether inherent impl block has inherent impl items.
+ inline bool has_impl_items () const { return !impl_items.empty (); }
+
+ // Mega-constructor
+ InherentImpl (::std::vector< ::std::unique_ptr<InherentImplItem> > impl_items,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::unique_ptr<Type> trait_type, WhereClause where_clause,
+ Visibility vis, ::std::vector<Attribute> inner_attrs,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : Impl (::std::move (generic_params), ::std::move (trait_type),
+ ::std::move (where_clause), ::std::move (vis),
+ ::std::move (inner_attrs), ::std::move (outer_attrs), locus),
+ impl_items (::std::move (impl_items))
+ {}
+
+ // Copy constructor with vector clone
+ InherentImpl (InherentImpl const &other) : Impl (other)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ impl_items.reserve (other.impl_items.size ());
+
+ for (const auto &e : other.impl_items)
+ {
+ impl_items.push_back (e->clone_inherent_impl_item ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ InherentImpl &operator= (InherentImpl const &other)
+ {
+ Impl::operator= (other);
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ impl_items.reserve (other.impl_items.size ());
+
+ for (const auto &e : other.impl_items)
+ {
+ impl_items.push_back (e->clone_inherent_impl_item ());
+ }
+
+ return *this;
+ }
+
+ // default move constructors
+ InherentImpl (InherentImpl &&other) = default;
+ InherentImpl &operator= (InherentImpl &&other) = default;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual InherentImpl *clone_item_impl () const OVERRIDE
+ {
+ return new InherentImpl (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual InherentImpl* clone_statement_impl() const OVERRIDE {
+ return new InherentImpl(*this);
+ }*/
+};
+
+// The "impl footrait for foo" impl block declaration AST node
+class TraitImpl : public Impl
+{
+ bool has_unsafe;
+
+ bool has_exclam;
+
+ TypePath trait_path;
+
+ // bool has_impl_items;
+ ::std::vector< ::std::unique_ptr<TraitImplItem> > impl_items;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether trait impl has impl items.
+ inline bool has_impl_items () const { return !impl_items.empty (); }
+
+ // Mega-constructor
+ TraitImpl (TypePath trait_path, bool is_unsafe, bool has_exclam,
+ ::std::vector< ::std::unique_ptr<TraitImplItem> > impl_items,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::unique_ptr<Type> trait_type, WhereClause where_clause,
+ Visibility vis, ::std::vector<Attribute> inner_attrs,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : Impl (::std::move (generic_params), ::std::move (trait_type),
+ ::std::move (where_clause), ::std::move (vis),
+ ::std::move (inner_attrs), ::std::move (outer_attrs), locus),
+ has_unsafe (is_unsafe), has_exclam (has_exclam),
+ trait_path (::std::move (trait_path)),
+ impl_items (::std::move (impl_items))
+ {}
+
+ // TODO: constructors with less params
+
+ // Copy constructor with vector clone
+ TraitImpl (TraitImpl const &other)
+ : Impl (other), has_unsafe (other.has_unsafe),
+ has_exclam (other.has_exclam), trait_path (other.trait_path)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ impl_items.reserve (other.impl_items.size ());
+
+ for (const auto &e : other.impl_items)
+ {
+ impl_items.push_back (e->clone_trait_impl_item ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ TraitImpl &operator= (TraitImpl const &other)
+ {
+ Impl::operator= (other);
+ trait_path = other.trait_path;
+ has_unsafe = other.has_unsafe;
+ has_exclam = other.has_exclam;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ impl_items.reserve (other.impl_items.size ());
+
+ for (const auto &e : other.impl_items)
+ {
+ impl_items.push_back (e->clone_trait_impl_item ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ TraitImpl (TraitImpl &&other) = default;
+ TraitImpl &operator= (TraitImpl &&other) = default;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual TraitImpl *clone_item_impl () const OVERRIDE
+ {
+ return new TraitImpl (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual TraitImpl* clone_statement_impl() const OVERRIDE {
+ return new TraitImpl(*this);
+ }*/
+};
+
+// Abstract base class for an item used inside an extern block
+class ExternalItem
+{
+ // bool has_outer_attrs;
+ ::std::vector<Attribute> outer_attrs;
+
+ // bool has_visibility;
+ Visibility visibility;
+
+ Identifier item_name;
+
+ Location locus;
+
+public:
+ virtual ~ExternalItem () {}
+
+ // Returns whether item has outer attributes.
+ inline bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+ // Returns whether item has non-default visibility.
+ inline bool has_visibility () const { return !visibility.is_error (); }
+
+ // Unique pointer custom clone function
+ ::std::unique_ptr<ExternalItem> clone_external_item () const
+ {
+ return ::std::unique_ptr<ExternalItem> (clone_external_item_impl ());
+ }
+
+ virtual ::std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+protected:
+ ExternalItem (Identifier item_name, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (::std::move (outer_attrs)), visibility (::std::move (vis)),
+ item_name (::std::move (item_name)), locus (locus)
+ {}
+
+ // Copy constructor
+ ExternalItem (ExternalItem const &other)
+ : outer_attrs (other.outer_attrs), visibility (other.visibility),
+ item_name (other.item_name), locus (other.locus)
+ {}
+
+ // Overloaded assignment operator to clone
+ ExternalItem &operator= (ExternalItem const &other)
+ {
+ item_name = other.item_name;
+ visibility = other.visibility;
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ ExternalItem (ExternalItem &&other) = default;
+ ExternalItem &operator= (ExternalItem &&other) = default;
+
+ // Clone function implementation as pure virtual method
+ virtual ExternalItem *clone_external_item_impl () const = 0;
+
+ // possibly make this public if required
+ ::std::string get_item_name () const { return item_name; }
+};
+
+// A static item used in an extern block
+class ExternalStaticItem : public ExternalItem
+{
+ bool has_mut;
+
+ // Type item_type;
+ ::std::unique_ptr<Type> item_type;
+
+public:
+ ExternalStaticItem (Identifier item_name, ::std::unique_ptr<Type> item_type,
+ bool is_mut, Visibility vis,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : ExternalItem (::std::move (item_name), ::std::move (vis),
+ ::std::move (outer_attrs), locus),
+ has_mut (is_mut), item_type (::std::move (item_type))
+ {}
+
+ // Copy constructor
+ ExternalStaticItem (ExternalStaticItem const &other)
+ : ExternalItem (other), has_mut (other.has_mut),
+ item_type (other.item_type->clone_type ())
+ {}
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator to clone
+ ExternalStaticItem &operator= (ExternalStaticItem const &other)
+ {
+ ExternalItem::operator= (other);
+ item_type = other.item_type->clone_type ();
+ has_mut = other.has_mut;
+
+ return *this;
+ }
+
+ // move constructors
+ ExternalStaticItem (ExternalStaticItem &&other) = default;
+ ExternalStaticItem &operator= (ExternalStaticItem &&other) = default;
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ExternalStaticItem *clone_external_item_impl () const OVERRIDE
+ {
+ return new ExternalStaticItem (*this);
+ }
+};
+
+// A named function parameter used in external functions
+struct NamedFunctionParam
+{
+private:
+ // bool has_name; // otherwise is _
+ Identifier name; // TODO: handle wildcard in identifier?
+
+ // Type param_type;
+ ::std::unique_ptr<Type> param_type;
+
+ // TODO: should this store location data?
+
+public:
+ // Returns whether the named function parameter has a name (i.e. name is not
+ // '_').
+ inline bool has_name () const { return name != "_"; }
+
+ // Returns whether the named function parameter is in an error state.
+ inline bool is_error () const
+ {
+ // also if identifier is "" but that is probably more costly to compute
+ return param_type == NULL;
+ }
+
+ // Creates an error state named function parameter.
+ static NamedFunctionParam create_error ()
+ {
+ return NamedFunctionParam ("", NULL);
+ }
+
+ NamedFunctionParam (Identifier name, ::std::unique_ptr<Type> param_type)
+ : name (::std::move (name)), param_type (::std::move (param_type))
+ {}
+
+ // Copy constructor
+ NamedFunctionParam (NamedFunctionParam const &other)
+ : name (other.name), param_type (other.param_type->clone_type ())
+ {}
+
+ ~NamedFunctionParam () = default;
+
+ // Overloaded assignment operator to clone
+ NamedFunctionParam &operator= (NamedFunctionParam const &other)
+ {
+ name = other.name;
+ param_type = other.param_type->clone_type ();
+ // has_name = other.has_name;
+
+ return *this;
+ }
+
+ // move constructors
+ NamedFunctionParam (NamedFunctionParam &&other) = default;
+ NamedFunctionParam &operator= (NamedFunctionParam &&other) = default;
+
+ ::std::string as_string () const;
+};
+
+// A function item used in an extern block
+class ExternalFunctionItem : public ExternalItem
+{
+ // bool has_generics;
+ // Generics generic_params;
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params; // inlined
+
+ // bool has_return_type;
+ // FunctionReturnType return_type;
+ ::std::unique_ptr<Type> return_type; // inlined
+
+ // bool has_where_clause;
+ WhereClause where_clause;
+
+ ::std::vector<NamedFunctionParam> function_params;
+
+ bool has_variadics;
+
+public:
+ // Returns whether item has generic parameters.
+ inline bool has_generics () const { return !generic_params.empty (); }
+
+ // Returns whether item has a return type (otherwise void).
+ inline bool has_return_type () const { return return_type != NULL; }
+
+ // Returns whether item has a where clause.
+ inline bool has_where_clause () const { return !where_clause.is_empty (); }
+
+ ExternalFunctionItem (
+ Identifier item_name,
+ ::std::vector< ::std::unique_ptr<GenericParam> > generic_params,
+ ::std::unique_ptr<Type> return_type, WhereClause where_clause,
+ ::std::vector<NamedFunctionParam> function_params, bool has_variadics,
+ Visibility vis, ::std::vector<Attribute> outer_attrs, Location locus)
+ : ExternalItem (::std::move (item_name), ::std::move (vis),
+ ::std::move (outer_attrs), locus),
+ generic_params (::std::move (generic_params)),
+ return_type (::std::move (return_type)),
+ where_clause (::std::move (where_clause)),
+ function_params (::std::move (function_params)),
+ has_variadics (has_variadics)
+ {}
+
+ // Copy constructor with clone
+ ExternalFunctionItem (ExternalFunctionItem const &other)
+ : ExternalItem (other),
+ /*generic_params(other.generic_params),*/ return_type (
+ other.return_type->clone_type ()),
+ where_clause (other.where_clause),
+ function_params (other.function_params),
+ has_variadics (other.has_variadics)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+ }
+
+ // Destructor - define here if required
+
+ // Overloaded assignment operator with clone
+ ExternalFunctionItem &operator= (ExternalFunctionItem const &other)
+ {
+ ExternalItem::operator= (other);
+ // generic_params = other.generic_params;
+ return_type = other.return_type->clone_type ();
+ where_clause = other.where_clause;
+ function_params = other.function_params;
+ has_variadics = other.has_variadics;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ generic_params.reserve (other.generic_params.size ());
+
+ for (const auto &e : other.generic_params)
+ {
+ generic_params.push_back (e->clone_generic_param ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ ExternalFunctionItem (ExternalFunctionItem &&other) = default;
+ ExternalFunctionItem &operator= (ExternalFunctionItem &&other) = default;
+
+ ::std::string as_string () const;
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ExternalFunctionItem *clone_external_item_impl () const OVERRIDE
+ {
+ return new ExternalFunctionItem (*this);
+ }
+};
+
+// An extern block AST node
+class ExternBlock : public VisItem
+{
+ // bool has_abi;
+ ::std::string abi;
+
+ // bool has_inner_attrs;
+ ::std::vector<Attribute> inner_attrs;
+
+ // bool has_extern_items;
+ ::std::vector< ::std::unique_ptr<ExternalItem> > extern_items;
+
+ Location locus;
+
+public:
+ ::std::string as_string () const;
+
+ // Returns whether extern block has inner attributes.
+ inline bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+ // Returns whether extern block has extern items.
+ inline bool has_extern_items () const { return !extern_items.empty (); }
+
+ // Returns whether extern block has ABI name.
+ inline bool has_abi () const { return !abi.empty (); }
+
+ ExternBlock (::std::string abi,
+ ::std::vector< ::std::unique_ptr<ExternalItem> > extern_items,
+ Visibility vis, ::std::vector<Attribute> inner_attrs,
+ ::std::vector<Attribute> outer_attrs, Location locus)
+ : VisItem (::std::move (vis), ::std::move (outer_attrs)),
+ abi (::std::move (abi)), inner_attrs (::std::move (inner_attrs)),
+ extern_items (::std::move (extern_items)), locus (locus)
+ {}
+
+ // Copy constructor with vector clone
+ ExternBlock (ExternBlock const &other)
+ : VisItem (other), abi (other.abi), inner_attrs (other.inner_attrs),
+ locus (other.locus)
+ {
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ extern_items.reserve (other.extern_items.size ());
+
+ for (const auto &e : other.extern_items)
+ {
+ extern_items.push_back (e->clone_external_item ());
+ }
+ }
+
+ // Overloaded assignment operator with vector clone
+ ExternBlock &operator= (ExternBlock const &other)
+ {
+ VisItem::operator= (other);
+ abi = other.abi;
+ inner_attrs = other.inner_attrs;
+ locus = other.locus;
+
+ // crappy vector unique pointer clone - TODO is there a better way of
+ // doing this?
+ extern_items.reserve (other.extern_items.size ());
+
+ for (const auto &e : other.extern_items)
+ {
+ extern_items.push_back (e->clone_external_item ());
+ }
+
+ return *this;
+ }
+
+ // move constructors
+ ExternBlock (ExternBlock &&other) = default;
+ ExternBlock &operator= (ExternBlock &&other) = default;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) OVERRIDE;
+
+protected:
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ virtual ExternBlock *clone_item_impl () const OVERRIDE
+ {
+ return new ExternBlock (*this);
+ }
+
+ // Use covariance to implement clone function as returning this object
+ // rather than base
+ /*virtual ExternBlock* clone_statement_impl() const OVERRIDE {
+ return new ExternBlock(*this);
+ }*/
+};
+
+// Replaced with forward decls - defined in "rust-macro.h"
+class MacroItem;
+class MacroInvocationSemi;
+class MacroRulesDefinition;
+} // namespace AST
+} // namespace Rust
#endif
diff --git a/gcc/rust/lex/rust-lex.cc b/gcc/rust/lex/rust-lex.cc
index 8b2a058..dc51b01 100644
--- a/gcc/rust/lex/rust-lex.cc
+++ b/gcc/rust/lex/rust-lex.cc
@@ -7,1853 +7,2247 @@
#include <sstream> // for ostringstream
namespace Rust {
- // TODO: move to separate compilation unit?
- // overload += for uint32_t to allow 32-bit encoded utf-8 to be added
- ::std::string& operator+=(::std::string& str, Codepoint char32) {
- if (char32.value < 0x80) {
- str += static_cast<char>(char32.value);
- } else if (char32.value < (0x1F + 1) << (1 * 6)) {
- str += static_cast<char>(0xC0 | ((char32.value >> 6) & 0x1F));
- str += static_cast<char>(0x80 | ((char32.value >> 0) & 0x3F));
- } else if (char32.value < (0x0F + 1) << (2 * 6)) {
- str += static_cast<char>(0xE0 | ((char32.value >> 12) & 0x0F));
- str += static_cast<char>(0x80 | ((char32.value >> 6) & 0x3F));
- str += static_cast<char>(0x80 | ((char32.value >> 0) & 0x3F));
- } else if (char32.value < (0x07 + 1) << (3 * 6)) {
- str += static_cast<char>(0xF0 | ((char32.value >> 18) & 0x07));
- str += static_cast<char>(0x80 | ((char32.value >> 12) & 0x3F));
- str += static_cast<char>(0x80 | ((char32.value >> 6) & 0x3F));
- str += static_cast<char>(0x80 | ((char32.value >> 0) & 0x3F));
- } else {
- fprintf(stderr, "Invalid unicode codepoint found: '%u' \n", char32.value);
- // rust_error_at(get_current_location(), "Invalid unicode codepoint found: '%u'",
- // char32.value);
- }
- return str;
+// TODO: move to separate compilation unit?
+// overload += for uint32_t to allow 32-bit encoded utf-8 to be added
+::std::string &
+operator+= (::std::string &str, Codepoint char32)
+{
+ if (char32.value < 0x80)
+ {
+ str += static_cast<char> (char32.value);
}
-
- ::std::string Codepoint::as_string() {
- std::string str;
-
- // do i need to do this? or can i just do str += value due to op overloading?
-
- // ok can't figure out how to just convert to codepoint or use "this" so create new one
- str += Codepoint(value);
-
- /*if (value < 0x80) {
- str += static_cast<char>(value);
- } else if (value < (0x1F + 1) << (1 * 6)) {
- str += static_cast<char>(0xC0 | ((value >> 6) & 0x1F));
- str += static_cast<char>(0x80 | ((value >> 0) & 0x3F));
- } else if (value < (0x0F + 1) << (2 * 6)) {
- str += static_cast<char>(0xE0 | ((value >> 12) & 0x0F));
- str += static_cast<char>(0x80 | ((value >> 6) & 0x3F));
- str += static_cast<char>(0x80 | ((value >> 0) & 0x3F));
- } else if (value < (0x07 + 1) << (3 * 6)) {
- str += static_cast<char>(0xF0 | ((value >> 18) & 0x07));
- str += static_cast<char>(0x80 | ((value >> 12) & 0x3F));
- str += static_cast<char>(0x80 | ((value >> 6) & 0x3F));
- str += static_cast<char>(0x80 | ((value >> 0) & 0x3F));
- } else {
- rust_error_at(get_current_location(), "Invalid unicode codepoint found: '%u'", value);
- }*/
- return str;
+ else if (char32.value < (0x1F + 1) << (1 * 6))
+ {
+ str += static_cast<char> (0xC0 | ((char32.value >> 6) & 0x1F));
+ str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
}
-
- // Includes all allowable float digits EXCEPT _ and . as that needs lookahead for handling.
- inline bool is_float_digit(char number) {
- return ISDIGIT(number) || number == 'E' || number == 'e';
+ else if (char32.value < (0x0F + 1) << (2 * 6))
+ {
+ str += static_cast<char> (0xE0 | ((char32.value >> 12) & 0x0F));
+ str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
+ str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
}
-
- // Basically ISXDIGIT from safe-ctype but may change if Rust's encoding or whatever is different
- inline bool is_x_digit(char number) {
- return ISXDIGIT(number);
+ else if (char32.value < (0x07 + 1) << (3 * 6))
+ {
+ str += static_cast<char> (0xF0 | ((char32.value >> 18) & 0x07));
+ str += static_cast<char> (0x80 | ((char32.value >> 12) & 0x3F));
+ str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
+ str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
}
-
- inline bool is_octal_digit(char number) {
- return number >= '0' && number <= '7';
+ else
+ {
+ fprintf (stderr, "Invalid unicode codepoint found: '%u' \n",
+ char32.value);
+ // rust_error_at(get_current_location(), "Invalid unicode codepoint found:
+ // '%u'", char32.value);
}
+ return str;
+}
- inline bool is_bin_digit(char number) {
- return number == '0' || number == '1';
- }
+::std::string
+Codepoint::as_string ()
+{
+ std::string str;
+
+ // do i need to do this? or can i just do str += value due to op overloading?
+
+ // ok can't figure out how to just convert to codepoint or use "this" so
+ // create new one
+ str += Codepoint (value);
+
+ /*if (value < 0x80) {
+ str += static_cast<char>(value);
+ } else if (value < (0x1F + 1) << (1 * 6)) {
+ str += static_cast<char>(0xC0 | ((value >> 6) & 0x1F));
+ str += static_cast<char>(0x80 | ((value >> 0) & 0x3F));
+ } else if (value < (0x0F + 1) << (2 * 6)) {
+ str += static_cast<char>(0xE0 | ((value >> 12) & 0x0F));
+ str += static_cast<char>(0x80 | ((value >> 6) & 0x3F));
+ str += static_cast<char>(0x80 | ((value >> 0) & 0x3F));
+ } else if (value < (0x07 + 1) << (3 * 6)) {
+ str += static_cast<char>(0xF0 | ((value >> 18) & 0x07));
+ str += static_cast<char>(0x80 | ((value >> 12) & 0x3F));
+ str += static_cast<char>(0x80 | ((value >> 6) & 0x3F));
+ str += static_cast<char>(0x80 | ((value >> 0) & 0x3F));
+ } else {
+ rust_error_at(get_current_location(), "Invalid unicode codepoint found:
+ '%u'", value);
+ }*/
+ return str;
+}
- inline bool check_valid_float_dot_end(char character) {
- return character != '.' && character != '_' && !ISALPHA(character);
- }
+// Includes all allowable float digits EXCEPT _ and . as that needs lookahead
+// for handling.
+inline bool
+is_float_digit (char number)
+{
+ return ISDIGIT (number) || number == 'E' || number == 'e';
+}
- // ISSPACE from safe-ctype but may change in future
- inline bool is_whitespace(char character) {
- return ISSPACE(character);
- }
+// Basically ISXDIGIT from safe-ctype but may change if Rust's encoding or
+// whatever is different
+inline bool
+is_x_digit (char number)
+{
+ return ISXDIGIT (number);
+}
- Lexer::Lexer(const char* filename, FILE* input, Linemap* linemap) :
- input(input), current_line(1), current_column(1), line_map(linemap), input_source(input),
- input_queue(input_source), token_source(this), token_queue(token_source) {
- // inform line_table that file is being entered and is in line 1
- line_map->start_file(filename, current_line);
- }
+inline bool
+is_octal_digit (char number)
+{
+ return number >= '0' && number <= '7';
+}
- Lexer::~Lexer() {
- /* ok apparently stop (which is equivalent of original code in destructor) is meant to be
- * called after all files have finished parsing, for cleanup. On the other hand, actual code
- * that it calls to leave a certain line map is mentioned in GCC docs as being useful for
- * "just leaving an included header" and stuff like that, so this line mapping functionality
- * may need fixing.
- * FIXME: find out whether this occurs. */
- // line_map->stop();
- }
+inline bool
+is_bin_digit (char number)
+{
+ return number == '0' || number == '1';
+}
- // TODO: need to optimise somehow to avoid the virtual function call in the tight loop.
- // Best idea at the moment is CRTP, but that might make lexer implementation annoying when storing
- // the "base class" (i.e. would need template parameter everywhere), although in practice it would
- // mostly just look ugly and make enclosing classes like Parser also require a type parameter.
- // At this point a macro might be better.
- // OK I guess macros can be replaced by constexpr if or something if possible.
- Location Lexer::get_current_location() {
- return line_map->get_location(current_column);
- }
+inline bool
+check_valid_float_dot_end (char character)
+{
+ return character != '.' && character != '_' && !ISALPHA (character);
+}
- int Lexer::peek_input(int n) {
- return input_queue.peek(n);
- }
+// ISSPACE from safe-ctype but may change in future
+inline bool
+is_whitespace (char character)
+{
+ return ISSPACE (character);
+}
- int Lexer::peek_input() {
- return peek_input(0);
- }
+Lexer::Lexer (const char *filename, FILE *input, Linemap *linemap)
+ : input (input), current_line (1), current_column (1), line_map (linemap),
+ input_source (input), input_queue (input_source), token_source (this),
+ token_queue (token_source)
+{
+ // inform line_table that file is being entered and is in line 1
+ line_map->start_file (filename, current_line);
+}
- void Lexer::skip_input(int n) {
- input_queue.skip(n);
- }
+Lexer::~Lexer ()
+{
+ /* ok apparently stop (which is equivalent of original code in destructor) is
+ * meant to be called after all files have finished parsing, for cleanup. On
+ * the other hand, actual code that it calls to leave a certain line map is
+ * mentioned in GCC docs as being useful for "just leaving an included header"
+ * and stuff like that, so this line mapping functionality may need fixing.
+ * FIXME: find out whether this occurs. */
+ // line_map->stop();
+}
- void Lexer::skip_input() {
- skip_input(0);
- }
+// TODO: need to optimise somehow to avoid the virtual function call in the
+// tight loop. Best idea at the moment is CRTP, but that might make lexer
+// implementation annoying when storing the "base class" (i.e. would need
+// template parameter everywhere), although in practice it would mostly just
+// look ugly and make enclosing classes like Parser also require a type
+// parameter. At this point a macro might be better. OK I guess macros can be
+// replaced by constexpr if or something if possible.
+Location
+Lexer::get_current_location ()
+{
+ return line_map->get_location (current_column);
+}
- const_TokenPtr Lexer::peek_token(int n) {
- return token_queue.peek(n);
- }
+int
+Lexer::peek_input (int n)
+{
+ return input_queue.peek (n);
+}
- const_TokenPtr Lexer::peek_token() {
- return peek_token(0);
- }
+int
+Lexer::peek_input ()
+{
+ return peek_input (0);
+}
- void Lexer::skip_token(int n) {
- token_queue.skip(n);
- }
+void
+Lexer::skip_input (int n)
+{
+ input_queue.skip (n);
+}
- void Lexer::skip_token() {
- skip_token(0);
- }
+void
+Lexer::skip_input ()
+{
+ skip_input (0);
+}
- void Lexer::replace_current_token(TokenPtr replacement) {
- token_queue.replace_current_value(replacement);
- }
+const_TokenPtr
+Lexer::peek_token (int n)
+{
+ return token_queue.peek (n);
+}
+
+const_TokenPtr
+Lexer::peek_token ()
+{
+ return peek_token (0);
+}
- /* shitty anonymous namespace that can only be accessed inside the compilation unit - used for
- * classify_keyword
- * Binary search in sorted array of keywords created with x-macros. */
- namespace {
- const std::string keyword_index[] = {
+void
+Lexer::skip_token (int n)
+{
+ token_queue.skip (n);
+}
+
+void
+Lexer::skip_token ()
+{
+ skip_token (0);
+}
+
+void
+Lexer::replace_current_token (TokenPtr replacement)
+{
+ token_queue.replace_current_value (replacement);
+}
+
+/* shitty anonymous namespace that can only be accessed inside the compilation
+ * unit - used for classify_keyword Binary search in sorted array of keywords
+ * created with x-macros. */
+namespace {
+const std::string keyword_index[] = {
#define RS_TOKEN(x, y)
#define RS_TOKEN_KEYWORD(name, keyword) keyword,
- RS_TOKEN_LIST
+ RS_TOKEN_LIST
#undef RS_TOKEN_KEYWORD
#undef RS_TOKEN
- };
+};
- TokenId keyword_keys[] = {
+TokenId keyword_keys[] = {
#define RS_TOKEN(x, y)
#define RS_TOKEN_KEYWORD(name, keyword) name,
- RS_TOKEN_LIST
+ RS_TOKEN_LIST
#undef RS_TOKEN_KEYWORD
#undef RS_TOKEN
- };
-
- const int num_keywords = sizeof(keyword_index) / sizeof(*keyword_index);
+};
+
+const int num_keywords = sizeof (keyword_index) / sizeof (*keyword_index);
+} // namespace
+
+/* Determines whether the string passed in is a keyword or not. If it is, it
+ * returns the keyword name. */
+TokenId
+Lexer::classify_keyword (const std::string &str)
+{
+ const std::string *last = keyword_index + num_keywords;
+ const std::string *idx = std::lower_bound (keyword_index, last, str);
+
+ if (idx == last || str != *idx)
+ {
+ return IDENTIFIER;
}
-
- /* Determines whether the string passed in is a keyword or not. If it is, it returns the keyword
- * name. */
- TokenId Lexer::classify_keyword(const std::string& str) {
- const std::string* last = keyword_index + num_keywords;
- const std::string* idx = std::lower_bound(keyword_index, last, str);
-
- if (idx == last || str != *idx) {
- return IDENTIFIER;
- } else {
- return keyword_keys[idx - keyword_index];
- }
+ else
+ {
+ return keyword_keys[idx - keyword_index];
}
+}
- TokenPtr Lexer::build_token() {
- // loop to go through multiple characters to build a single token
- while (true) {
- Location loc = get_current_location();
- /*int */ current_char = peek_input();
- skip_input();
-
- // return end of file token if end of file
- if (current_char == EOF) {
- return Token::make(END_OF_FILE, loc);
- }
-
- // detect shebang
- if (loc == 1 && current_line == 1 && current_char == '#') {
- current_char = peek_input();
-
- if (current_char == '!') {
- skip_input();
- current_char = peek_input();
-
- switch (current_char) {
- case '/':
- // shebang
-
- skip_input();
-
- // ignore rest of line
- while (current_char != '\n') {
- current_char = peek_input();
- skip_input();
- }
-
- // newline
- current_line++;
- current_column = 1;
- // tell line_table that new line starts
- line_map->start_line(current_line, max_column_hint);
- continue;
- }
- }
- }
-
- // if not end of file, start tokenising
- switch (current_char) {
- // ignore whitespace characters for tokens but continue updating location
- case '\n': // newline
- current_line++;
- current_column = 1;
- // tell line_table that new line starts
- linemap_line_start(::line_table, current_line, max_column_hint);
- continue;
- case ' ': // space
- current_column++;
- continue;
- case '\t': // tab
- // width of a tab is not well-defined, assume 8 spaces
- current_column += 8;
- continue;
-
- // punctuation - actual tokens
- case '=':
- if (peek_input() == '>') {
- // match arm arrow
- skip_input();
- current_column += 2;
-
- return Token::make(MATCH_ARROW, loc);
- } else if (peek_input() == '=') {
- // equality operator
- skip_input();
- current_column += 2;
-
- return Token::make(EQUAL_EQUAL, loc);
- } else {
- // assignment operator
- current_column++;
- return Token::make(EQUAL, loc);
- }
- case '(':
- current_column++;
- return Token::make(LEFT_PAREN, loc);
- case '-':
- if (peek_input() == '>') {
- // return type specifier
- skip_input();
- current_column += 2;
-
- return Token::make(RETURN_TYPE, loc);
- } else if (peek_input() == '=') {
- // minus-assign
- skip_input();
- current_column += 2;
-
- return Token::make(MINUS_EQ, loc);
- } else {
- // minus
- current_column++;
- return Token::make(MINUS, loc);
- }
- case '+':
- if (peek_input() == '=') {
- // add-assign
- skip_input();
- current_column += 2;
-
- return Token::make(PLUS_EQ, loc);
- } else {
- // add
- current_column++;
- return Token::make(PLUS, loc);
- }
- case ')':
- current_column++;
- return Token::make(RIGHT_PAREN, loc);
- case ';':
- current_column++;
- return Token::make(SEMICOLON, loc);
- case '*':
- if (peek_input() == '=') {
- // multiplication-assign
- skip_input();
- current_column += 2;
-
- return Token::make(ASTERISK_EQ, loc);
- } else {
- // multiplication
- current_column++;
- return Token::make(ASTERISK, loc);
- }
- case ',':
- current_column++;
- return Token::make(COMMA, loc);
- case '/':
- if (peek_input() == '=') {
- // division-assign
- skip_input();
- current_column += 2;
-
- return Token::make(DIV_EQ, loc);
- } else if (peek_input() == '/') {
- // TODO: single-line doc comments
-
- // single line comment
- skip_input();
- current_column += 2;
-
- // basically ignore until line finishes
- while (current_char != '\n' && current_char != EOF) {
- skip_input();
- current_column++; // not used
- current_char = peek_input();
- }
- continue;
- break;
- } else if (peek_input() == '*') {
- // block comment
- skip_input();
- current_column += 2;
-
- // TODO: block doc comments
-
- current_char = peek_input();
-
- int level = 1;
- while (level > 0) {
- skip_input();
- current_column++; // for error-handling
- current_char = peek_input();
-
- // if /* found
- if (current_char == '/') {
- if (peek_input(1) == '*') {
- // skip /* characters
- skip_input(1);
-
- current_column += 2;
-
- level += 1;
- }
- }
-
- // ignore until */ is found
- if (current_char == '*') {
- if (peek_input(1) == '/') {
- // skip */ characters
- skip_input(1);
-
- current_column += 2;
- // should only break inner loop here - seems to do so
- // break;
-
- level -= 1;
- }
- }
- }
-
- // refresh new token
- continue;
- break;
- } else {
- // division
- current_column++;
- return Token::make(DIV, loc);
- }
- case '%':
- if (peek_input() == '=') {
- // modulo-assign
- current_column += 2;
- return Token::make(PERCENT_EQ, loc);
- } else {
- // modulo
- current_column++;
- return Token::make(PERCENT, loc);
- }
- case '^':
- if (peek_input() == '=') {
- // xor-assign?
- current_column += 2;
- return Token::make(CARET_EQ, loc);
- } else {
- // xor?
- current_column++;
- return Token::make(CARET, loc);
- }
- case '<':
- if (peek_input() == '<') {
- if (peek_input(1) == '=') {
- // left-shift assign
- skip_input(1);
- current_column += 3;
-
- return Token::make(LEFT_SHIFT_EQ, loc);
- } else {
- // left-shift
- skip_input();
- current_column += 2;
-
- return Token::make(LEFT_SHIFT, loc);
- }
- } else if (peek_input() == '=') {
- // smaller than or equal to
- skip_input();
- current_column += 2;
-
- return Token::make(LESS_OR_EQUAL, loc);
- } else {
- // smaller than
- current_column++;
- return Token::make(LEFT_ANGLE, loc);
- }
- break;
- case '>':
- if (peek_input() == '>') {
- if (peek_input(1) == '=') {
- // right-shift-assign
- skip_input(1);
- current_column += 3;
-
- return Token::make(RIGHT_SHIFT_EQ, loc);
- } else {
- // right-shift
- skip_input();
- current_column += 2;
-
- return Token::make(RIGHT_SHIFT, loc);
- }
- } else if (peek_input() == '=') {
- // larger than or equal to
- skip_input();
- current_column += 2;
-
- return Token::make(GREATER_OR_EQUAL, loc);
- } else {
- // larger than
- current_column++;
- return Token::make(RIGHT_ANGLE, loc);
- }
- case ':':
- if (peek_input() == ':') {
- // scope resolution ::
- skip_input();
- current_column += 2;
-
- return Token::make(SCOPE_RESOLUTION, loc);
- } else {
- // single colon :
- current_column++;
- return Token::make(COLON, loc);
- }
- case '!':
- // no special handling for macros in lexer?
- if (peek_input() == '=') {
- // not equal boolean operator
- skip_input();
- current_column += 2;
-
- return Token::make(NOT_EQUAL, loc);
- } else {
- // not equal unary operator
- current_column++;
-
- return Token::make(EXCLAM, loc);
- }
- case '?':
- current_column++;
- return Token::make(QUESTION_MARK, loc);
- case '#':
- current_column++;
- return Token::make(HASH, loc);
- case '[':
- current_column++;
- return Token::make(LEFT_SQUARE, loc);
- case ']':
- current_column++;
- return Token::make(RIGHT_SQUARE, loc);
- case '{':
- current_column++;
- return Token::make(LEFT_CURLY, loc);
- case '}':
- current_column++;
- return Token::make(RIGHT_CURLY, loc);
- case '@':
- // TODO: i don't know what this does, does it need special handling?
- current_column++;
- return Token::make(PATTERN_BIND, loc);
- case '$':
- // TODO: i don't know what this does, does it need special handling?
- current_column++;
- return Token::make(DOLLAR_SIGN, loc);
- case '~':
- // TODO: i don't know what this does, does it need special handling?
- current_column++;
- return Token::make(TILDE, loc);
- case '\\':
- // TODO: i don't know what this does, does it need special handling?
- current_column++;
- return Token::make(BACKSLASH, loc);
- case '`':
- // TODO: i don't know what this does, does it need special handling?
- current_column++;
- return Token::make(BACKTICK, loc);
- case '|':
- if (peek_input() == '=') {
- // bitwise or-assign?
- skip_input();
- current_column += 2;
-
- return Token::make(PIPE_EQ, loc);
- } else if (peek_input() == '|') {
- // logical or
- skip_input();
- current_column += 2;
-
- return Token::make(OR, loc);
- } else {
- // bitwise or
- current_column++;
-
- return Token::make(PIPE, loc);
- }
- case '&':
- if (peek_input() == '=') {
- // bitwise and-assign?
- skip_input();
- current_column += 2;
-
- return Token::make(AMP_EQ, loc);
- } else if (peek_input() == '&') {
- // logical and
- skip_input();
- current_column += 2;
-
- return Token::make(LOGICAL_AND, loc);
- } else {
- // bitwise and/reference
- current_column++;
-
- return Token::make(AMP, loc);
- }
- case '.':
- if (peek_input() == '.') {
- if (peek_input(1) == '.') {
- // ellipsis
- skip_input(1);
- current_column += 3;
-
- return Token::make(ELLIPSIS, loc);
- } else if (peek_input(1) == '=') {
- // ..=
- skip_input(1);
- current_column += 3;
-
- return Token::make(DOT_DOT_EQ, loc);
- } else {
- // ..
- skip_input();
- current_column += 2;
-
- return Token::make(DOT_DOT, loc);
- }
- } else if (!ISDIGIT(peek_input())) {
- // single dot .
- // Only if followed by a non-number
- current_column++;
- return Token::make(DOT, loc);
- }
- }
- // TODO: special handling of _ in the lexer? instead of being identifier
-
- // byte and byte string test
- if (current_char == 'b') {
- if (peek_input() == '\'') {
- // byte - allows any ascii or escapes
- // would also have to take into account escapes: \x hex_digit hex_digit,
- // \n, \r, \t, \\, \0
-
- int length = 1;
-
- // char to save
- char byte_char;
-
- skip_input();
- // make current char the next character
- current_char = peek_input();
-
- // detect escapes
- if (current_char == '\\') {
- /*skip_input();
-
- // make current_char next character (letter)
- current_char = peek_input();*/
-
- parse_escape(length, byte_char, '\'');
-
- if (byte_char > 127) {
- rust_error_at(
- get_current_location(), "byte char '%c' out of range", byte_char);
- byte_char = 0;
- }
-
- // skip_input();
- current_char = peek_input();
- length++;
-
- if (current_char != '\'') {
- rust_error_at(get_current_location(), "unclosed byte char");
- }
-
- // TODO: ensure skipping is needed here
- skip_input();
- current_char = peek_input();
- length++; // go to next char
- } else if (current_char != '\'') {
- // otherwise, get character from direct input character
- byte_char = current_char;
-
- skip_input();
- current_char = peek_input();
-
- if (current_char != '\'') {
- rust_error_at(get_current_location(), "unclosed byte char");
- }
-
- // TODO: ensure skipping is needed here
- skip_input();
- current_char = peek_input();
- length++; // go to next char
- } else {
- rust_error_at(get_current_location(), "no character inside '' for byte char");
- }
-
- current_column += length;
-
- return Token::make_byte_char(loc, byte_char);
- } else if (peek_input() == '"') {
- // byte string
-
- // skip quote character
- skip_input();
-
- std::string str;
- str.reserve(16); // some sensible default
-
- int length = 1;
- current_char = peek_input();
- // TODO: handle escapes properly
-
- while (current_char != '"' && current_char != '\n') {
- if (current_char == '\\') {
- char output_char = 0;
- parse_escape(length, output_char, '"');
-
- if (output_char > 127) {
- rust_error_at(get_current_location(),
- "char '%c' in byte string out of range", output_char);
- output_char = 0;
- }
-
- str += output_char;
-
- continue;
- }
-
- length++;
-
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- if (current_char == '\n') {
- rust_error_at(get_current_location(), "unended byte string literal");
- } else if (current_char == '"') {
- skip_input();
- current_char = peek_input();
- } else {
- gcc_unreachable();
- }
-
- return Token::make_byte_string(loc, str);
- // TODO: ensure escapes and string continue work properly
- } else if (peek_input() == 'r' && (peek_input(1) == '#' || peek_input(1) == '"')) {
- // raw byte string literals
- std::string str;
- str.reserve(16); // some sensible default
-
- int length = 1;
- int hash_count = 0;
-
- // get hash count at beginnning
- skip_input();
- current_char = peek_input();
- while (current_char == '#') {
- hash_count++;
- length++;
-
- skip_input();
- current_char = peek_input();
- }
-
- if (current_char != '"') {
- rust_error_at(get_current_location(), "raw byte string has no opening '\"'");
- }
-
- skip_input();
- current_char = peek_input();
-
- while (true) {
- if (current_char == '"') {
- bool enough_hashes = true;
-
- for (int i = 0; i < hash_count; i++) {
- if (peek_input(i + 1) != '#') {
- enough_hashes
- = false; // could continue here - improve performance
- }
- }
-
- if (enough_hashes) {
- // skip enough input and peek enough input
- skip_input(hash_count); // is this enough?
- current_char = peek_input();
- length += hash_count + 1;
- break;
- }
- }
-
- length++;
-
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- return Token::make_byte_string(loc, str); // TODO: does this work properly
- }
- }
-
- // raw stuff
- if (current_char == 'r') {
- int peek = peek_input();
- int peek1 = peek_input(1);
-
- if (peek == '#' && (ISALPHA(peek1) || peek1 == '_')) {
- // raw identifier
- std::string str;
- str.reserve(16); // default
-
- skip_input();
- current_char = peek_input();
-
- current_column += 2;
-
- str += current_char;
-
- bool first_is_underscore = current_char == '_';
-
- int length = 1;
- current_char = peek_input();
- // loop through entire name
- while (ISALPHA(current_char) || ISDIGIT(current_char) || current_char == '_') {
- length++;
-
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- // if just a single underscore, not an identifier
- if (first_is_underscore && length == 1) {
- rust_error_at(get_current_location(), "'_' is not a valid raw identifier");
- }
-
- if (str == "crate" || str == "extern" || str == "self" || str == "super"
- || str == "Self") {
- rust_error_at(
- get_current_location(), "'%s' is a forbidden raw identifier", str.c_str());
- } else {
- return Token::make_identifier(loc, str);
- }
- } else if (peek == '"' || (peek == '#' && (ISALPHA(peek1) || peek1 == '_'))) {
- // raw string literals
- std::string str;
- str.reserve(16); // some sensible default
-
- int length = 1;
- int hash_count = 0;
-
- // get hash count at beginnning
- current_char = peek;
- while (current_char == '#') {
- hash_count++;
- length++;
-
- skip_input();
- current_char = peek_input();
- }
-
- if (current_char != '"') {
- rust_error_at(get_current_location(), "raw string has no opening '\"'");
- }
-
- skip_input();
- Codepoint current_char32 = test_peek_codepoint_input();
-
- while (true) {
- if (current_char32.value == '"') {
- bool enough_hashes = true;
-
- for (int i = 0; i < hash_count; i++) {
- // if (test_peek_codepoint_input(i + 1) != '#') {
- // TODO: ensure this is a good enough replacement
- if (peek_input(i + 1) != '#') {
- enough_hashes
- = false; // could continue here - improve performance
- }
- }
-
- if (enough_hashes) {
- // skip enough input and peek enough input
- skip_input(hash_count); // is this enough?
- current_char = peek_input();
- length += hash_count + 1;
- break;
- }
- }
-
- length++;
-
- str += current_char32;
- test_skip_codepoint_input();
- current_char32 = test_peek_codepoint_input();
- }
-
- current_column += length;
-
- return Token::make_string(loc, str); // TODO: does this work properly
- }
- }
-
- // find identifiers and keywords
- if (ISALPHA(current_char) || current_char == '_') {
- std::string str;
- str.reserve(16); // default
- str += current_char;
-
- bool first_is_underscore = current_char == '_';
-
- int length = 1;
- current_char = peek_input();
- // loop through entire name
- while (ISALPHA(current_char) || ISDIGIT(current_char) || current_char == '_') {
- length++;
-
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- // if just a single underscore, not an identifier
- if (first_is_underscore && length == 1) {
- return Token::make(UNDERSCORE, loc);
- }
-
- TokenId keyword = classify_keyword(str);
- if (keyword == IDENTIFIER) {
- return Token::make_identifier(loc, str);
- } else {
- return Token::make(keyword, loc);
- }
- }
-
- // identify literals
- // int or float literals - not processed properly
- if (ISDIGIT(current_char) || current_char == '.') { // _ not allowed as first char
- std::string str;
- str.reserve(16); // some sensible default
- str += current_char;
-
- PrimitiveCoreType type_hint = CORETYPE_UNKNOWN;
-
- bool is_real = (current_char == '.');
-
- int length = 1;
-
- // handle binary, octal, hex literals
- if (current_char == '0' && !ISDIGIT(peek_input())) {
- current_char = peek_input();
-
- if (current_char == 'x') {
- // hex (integer only)
-
- skip_input();
- current_char = peek_input();
-
- length++;
-
- // add 'x' to string after 0 so it is 0xFFAA or whatever
- str += 'x';
-
- // loop through to add entire hex number to string
- while (is_x_digit(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to number
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- // add raw hex numbers
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- // convert hex value to decimal representation
- long hex_num = ::std::strtol(str.c_str(), NULL, 16);
-
- // create output string stream for hex value to be converted to string
- // again
- // TODO: if too slow, use sprintf
- ::std::ostringstream ostr;
- ostr << hex_num;
-
- // reassign string representation to converted value
- str = ostr.str();
-
- // parse in type suffix if it exists
- parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for integer (hex) literal",
- get_type_hint_string(type_hint));
- }
- } else if (current_char == 'o') {
- // octal (integer only)
-
- skip_input();
- current_char = peek_input();
-
- length++;
-
- // don't add any characters as C octals are just 0124 or whatever
-
- // loop through to add entire octal number to string
- while (is_octal_digit(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to number
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- // add raw octal numbers
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- // convert octal value to decimal representation
- long octal_num = ::std::strtol(str.c_str(), NULL, 8);
-
- // create output string stream for octal value to be converted to
- // string again
- // TODO: if too slow, use sprintf
- ::std::ostringstream ostr;
- ostr << octal_num;
-
- // reassign string representation to converted value
- str = ostr.str();
-
- // parse in type suffix if it exists
- parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for integer (octal) literal",
- get_type_hint_string(type_hint));
- }
- } else if (current_char == 'b') {
- // binary (integer only)
-
- skip_input();
- current_char = peek_input();
-
- length++;
-
- // don't add any characters as C binary numbers are not really
- // supported
-
- // loop through to add entire binary number to string
- while (is_bin_digit(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to number
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- // add raw binary numbers
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- current_column += length;
-
- // convert binary value to decimal representation
- long bin_num = ::std::strtol(str.c_str(), NULL, 2);
-
- // create output string stream for binary value to be converted to
- // string again
- // TODO: if too slow, use sprintf
- ::std::ostringstream ostr;
- ostr << bin_num;
-
- // reassign string representation to converted value
- str = ostr.str();
-
- // parse in type suffix if it exists
- parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for integer (binary) literal",
- get_type_hint_string(type_hint));
- }
- }
- } else {
- // handle decimals (integer or float)
-
- current_char = peek_input();
-
- // parse initial decimal literal - assuming integer
- // TODO: test if works
- parse_in_decimal(/*current_char, */ str, length);
-
- // detect float literal - TODO: fix: "242." is not recognised as a float literal
- if (current_char == '.' && is_float_digit(peek_input(1))) {
- // float with a '.', parse another decimal into it
-
- is_real = true;
-
- // add . to str
- str += current_char;
- skip_input();
- current_char = peek_input();
-
- length++;
-
- // parse another decimal number for float
- // TODO: test if works
- parse_in_decimal(/*current_char, */ str, length);
-
- // parse in exponent part if it exists
- // test to see if this works:
- parse_in_exponent_part(/*current_char, */ str, length);
-
- // parse in type suffix if it exists
- // TODO: see if works:
- parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
- && type_hint != CORETYPE_UNKNOWN) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for float literal",
- get_type_hint_string(type_hint));
- }
-
- } else if (current_char == '.' && check_valid_float_dot_end(peek_input(1))) {
- is_real = true;
-
- // add . to str
- str += current_char;
- skip_input();
- current_char = peek_input();
- length++;
-
- // add a '0' after the . to stop ambiguity
- str += '0';
-
- // don't parse another decimal number for float
-
- // parse in exponent part if it exists - shouldn't exist?
- // parse_in_exponent_part(/*current_char, */ str, length);
-
- // parse in type suffix if it exists - shouldn't exist?
- // TODO: see if works:
- // parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
- && type_hint != CORETYPE_UNKNOWN) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for float literal",
- get_type_hint_string(type_hint));
- }
- } else if (current_char == 'E' || current_char == 'e') {
- is_real = true;
-
- // parse exponent part
- parse_in_exponent_part(/*current_char, */ str, length);
-
- // parse in type suffix if it exists
- parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
- && type_hint != CORETYPE_UNKNOWN) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for float literal",
- get_type_hint_string(type_hint));
- }
- } else {
- // is an integer
-
- // parse in type suffix if it exists
- parse_in_type_suffix(/*current_char, */ type_hint, length);
-
- if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64) {
- rust_error_at(get_current_location(),
- "invalid type suffix '%s' for integer (decimal) literal",
- get_type_hint_string(type_hint));
- }
- }
-
- current_column += length;
- }
-
- // actually make the tokens
- if (is_real) {
- return Token::make_float(loc, str, type_hint);
- } else {
- return Token::make_int(loc, str, type_hint);
- }
- }
-
- // string literals - not processed properly
- if (current_char == '"') {
- Codepoint current_char32;
-
- std::string str;
- str.reserve(16); // some sensible default
-
- int length = 1;
- current_char32 = test_peek_codepoint_input();
-
- // ok initial peek_codepoint seems to work without "too long"
-
- while (current_char32.value != '\n' && current_char32.value != '"') {
- // TODO: handle escapes and string continue
- if (current_char32.value == '\\') {
- // parse escape
- parse_utf8_escape(length, current_char32, '\'');
-
- // TODO: find a way to parse additional characters after the escape?
- // return after parsing escape?
-
- str += current_char32;
-
- // required as parsing utf8 escape only changes current_char or something
- current_char32 = test_peek_codepoint_input();
-
- continue;
- }
-
- length += test_get_input_codepoint_length();
-
- // does this work? not technically a char. maybe have to convert to char series
- str += current_char32;
- test_skip_codepoint_input();
- current_char32 = test_peek_codepoint_input();
- }
-
- current_column += length;
-
- if (current_char32.value == '\n') {
- rust_error_at(get_current_location(), "unended string literal");
- } else if (current_char32.value == '"') {
- skip_input();
-
- current_char = peek_input();
- } else {
- gcc_unreachable();
- }
-
- return Token::make_string(loc, str);
- // TODO: account for escapes and string continue
- // also, in rust a string is a series of unicode characters (4 bytes)
- }
-
- // char literal attempt
- if (current_char == '\'') {
- // rust chars are 4 bytes and have some weird unicode representation thing
- Codepoint current_char32;
-
- int length = 1;
-
- current_char32 = test_peek_codepoint_input();
-
- // parse escaped char literal
- if (current_char32.value == '\\') {
- // parse escape
- parse_utf8_escape(length, current_char32, '\'');
-
- // TODO - this skip may not be needed?
- // test_skip_codepoint_input();
-
- if (test_peek_codepoint_input().value != '\'') {
- rust_error_at(get_current_location(), "unended char literal");
- } else {
- test_skip_codepoint_input();
- current_char = peek_input();
- length++;
- }
-
- current_column += length;
-
- // TODO: FIX - char is actually 4 bytes in Rust (uint32) due to unicode
- return Token::make_char(loc, current_char32);
- } else {
- // current_char32 = test_peek_codepoint_input();
- test_skip_codepoint_input();
-
- if (test_peek_codepoint_input().value == '\'') {
- // parse normal char literal
- // TODO: FIX - char is actually 4 bytes in Rust (uint32) due to unicode
-
- // skip the ' character
- skip_input();
- current_char = peek_input();
-
- // TODO fix due to different widths of utf-8 chars
- current_column += 3;
-
- return Token::make_char(loc, current_char32);
- } else if (ISDIGIT(current_char32.value) || ISALPHA(current_char32.value)
- || current_char32.value == '_') {
- // parse lifetime name
- ::std::string str;
- // TODO: does this work properly?
- str += current_char32;
-
- // TODO: fix lifetime name thing - actually, why am I even using utf-8 here?
-
- int length = 1;
-
- current_char32 = test_peek_codepoint_input();
-
- while (ISDIGIT(current_char32.value) || ISALPHA(current_char32.value)
- || current_char32.value == '_') {
- length += test_get_input_codepoint_length();
-
- str += current_char32;
- test_skip_codepoint_input();
- current_char32 = test_peek_codepoint_input();
- }
-
- current_column += length;
-
- return Token::make_lifetime(loc, str);
- } else {
- rust_error_at(get_current_location(), "expected ' after character constant");
- }
- }
- }
-
- // didn't match anything so error
- rust_error_at(loc, "unexpected character '%x'", current_char);
- current_column++;
- }
+TokenPtr
+Lexer::build_token ()
+{
+ // loop to go through multiple characters to build a single token
+ while (true)
+ {
+ Location loc = get_current_location ();
+ /*int */ current_char = peek_input ();
+ skip_input ();
+
+ // return end of file token if end of file
+ if (current_char == EOF)
+ {
+ return Token::make (END_OF_FILE, loc);
+ }
+
+ // detect shebang
+ if (loc == 1 && current_line == 1 && current_char == '#')
+ {
+ current_char = peek_input ();
+
+ if (current_char == '!')
+ {
+ skip_input ();
+ current_char = peek_input ();
+
+ switch (current_char)
+ {
+ case '/':
+ // shebang
+
+ skip_input ();
+
+ // ignore rest of line
+ while (current_char != '\n')
+ {
+ current_char = peek_input ();
+ skip_input ();
+ }
+
+ // newline
+ current_line++;
+ current_column = 1;
+ // tell line_table that new line starts
+ line_map->start_line (current_line, max_column_hint);
+ continue;
+ }
+ }
+ }
+
+ // if not end of file, start tokenising
+ switch (current_char)
+ {
+ // ignore whitespace characters for tokens but continue updating
+ // location
+ case '\n': // newline
+ current_line++;
+ current_column = 1;
+ // tell line_table that new line starts
+ linemap_line_start (::line_table, current_line, max_column_hint);
+ continue;
+ case ' ': // space
+ current_column++;
+ continue;
+ case '\t': // tab
+ // width of a tab is not well-defined, assume 8 spaces
+ current_column += 8;
+ continue;
+
+ // punctuation - actual tokens
+ case '=':
+ if (peek_input () == '>')
+ {
+ // match arm arrow
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (MATCH_ARROW, loc);
+ }
+ else if (peek_input () == '=')
+ {
+ // equality operator
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (EQUAL_EQUAL, loc);
+ }
+ else
+ {
+ // assignment operator
+ current_column++;
+ return Token::make (EQUAL, loc);
+ }
+ case '(':
+ current_column++;
+ return Token::make (LEFT_PAREN, loc);
+ case '-':
+ if (peek_input () == '>')
+ {
+ // return type specifier
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (RETURN_TYPE, loc);
+ }
+ else if (peek_input () == '=')
+ {
+ // minus-assign
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (MINUS_EQ, loc);
+ }
+ else
+ {
+ // minus
+ current_column++;
+ return Token::make (MINUS, loc);
+ }
+ case '+':
+ if (peek_input () == '=')
+ {
+ // add-assign
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (PLUS_EQ, loc);
+ }
+ else
+ {
+ // add
+ current_column++;
+ return Token::make (PLUS, loc);
+ }
+ case ')':
+ current_column++;
+ return Token::make (RIGHT_PAREN, loc);
+ case ';':
+ current_column++;
+ return Token::make (SEMICOLON, loc);
+ case '*':
+ if (peek_input () == '=')
+ {
+ // multiplication-assign
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (ASTERISK_EQ, loc);
+ }
+ else
+ {
+ // multiplication
+ current_column++;
+ return Token::make (ASTERISK, loc);
+ }
+ case ',':
+ current_column++;
+ return Token::make (COMMA, loc);
+ case '/':
+ if (peek_input () == '=')
+ {
+ // division-assign
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (DIV_EQ, loc);
+ }
+ else if (peek_input () == '/')
+ {
+ // TODO: single-line doc comments
+
+ // single line comment
+ skip_input ();
+ current_column += 2;
+
+ // basically ignore until line finishes
+ while (current_char != '\n' && current_char != EOF)
+ {
+ skip_input ();
+ current_column++; // not used
+ current_char = peek_input ();
+ }
+ continue;
+ break;
+ }
+ else if (peek_input () == '*')
+ {
+ // block comment
+ skip_input ();
+ current_column += 2;
+
+ // TODO: block doc comments
+
+ current_char = peek_input ();
+
+ int level = 1;
+ while (level > 0)
+ {
+ skip_input ();
+ current_column++; // for error-handling
+ current_char = peek_input ();
+
+ // if /* found
+ if (current_char == '/')
+ {
+ if (peek_input (1) == '*')
+ {
+ // skip /* characters
+ skip_input (1);
+
+ current_column += 2;
+
+ level += 1;
+ }
+ }
+
+ // ignore until */ is found
+ if (current_char == '*')
+ {
+ if (peek_input (1) == '/')
+ {
+ // skip */ characters
+ skip_input (1);
+
+ current_column += 2;
+ // should only break inner loop here - seems to do so
+ // break;
+
+ level -= 1;
+ }
+ }
+ }
+
+ // refresh new token
+ continue;
+ break;
+ }
+ else
+ {
+ // division
+ current_column++;
+ return Token::make (DIV, loc);
+ }
+ case '%':
+ if (peek_input () == '=')
+ {
+ // modulo-assign
+ current_column += 2;
+ return Token::make (PERCENT_EQ, loc);
+ }
+ else
+ {
+ // modulo
+ current_column++;
+ return Token::make (PERCENT, loc);
+ }
+ case '^':
+ if (peek_input () == '=')
+ {
+ // xor-assign?
+ current_column += 2;
+ return Token::make (CARET_EQ, loc);
+ }
+ else
+ {
+ // xor?
+ current_column++;
+ return Token::make (CARET, loc);
+ }
+ case '<':
+ if (peek_input () == '<')
+ {
+ if (peek_input (1) == '=')
+ {
+ // left-shift assign
+ skip_input (1);
+ current_column += 3;
+
+ return Token::make (LEFT_SHIFT_EQ, loc);
+ }
+ else
+ {
+ // left-shift
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (LEFT_SHIFT, loc);
+ }
+ }
+ else if (peek_input () == '=')
+ {
+ // smaller than or equal to
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (LESS_OR_EQUAL, loc);
+ }
+ else
+ {
+ // smaller than
+ current_column++;
+ return Token::make (LEFT_ANGLE, loc);
+ }
+ break;
+ case '>':
+ if (peek_input () == '>')
+ {
+ if (peek_input (1) == '=')
+ {
+ // right-shift-assign
+ skip_input (1);
+ current_column += 3;
+
+ return Token::make (RIGHT_SHIFT_EQ, loc);
+ }
+ else
+ {
+ // right-shift
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (RIGHT_SHIFT, loc);
+ }
+ }
+ else if (peek_input () == '=')
+ {
+ // larger than or equal to
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (GREATER_OR_EQUAL, loc);
+ }
+ else
+ {
+ // larger than
+ current_column++;
+ return Token::make (RIGHT_ANGLE, loc);
+ }
+ case ':':
+ if (peek_input () == ':')
+ {
+ // scope resolution ::
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (SCOPE_RESOLUTION, loc);
+ }
+ else
+ {
+ // single colon :
+ current_column++;
+ return Token::make (COLON, loc);
+ }
+ case '!':
+ // no special handling for macros in lexer?
+ if (peek_input () == '=')
+ {
+ // not equal boolean operator
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (NOT_EQUAL, loc);
+ }
+ else
+ {
+ // not equal unary operator
+ current_column++;
+
+ return Token::make (EXCLAM, loc);
+ }
+ case '?':
+ current_column++;
+ return Token::make (QUESTION_MARK, loc);
+ case '#':
+ current_column++;
+ return Token::make (HASH, loc);
+ case '[':
+ current_column++;
+ return Token::make (LEFT_SQUARE, loc);
+ case ']':
+ current_column++;
+ return Token::make (RIGHT_SQUARE, loc);
+ case '{':
+ current_column++;
+ return Token::make (LEFT_CURLY, loc);
+ case '}':
+ current_column++;
+ return Token::make (RIGHT_CURLY, loc);
+ case '@':
+ // TODO: i don't know what this does, does it need special handling?
+ current_column++;
+ return Token::make (PATTERN_BIND, loc);
+ case '$':
+ // TODO: i don't know what this does, does it need special handling?
+ current_column++;
+ return Token::make (DOLLAR_SIGN, loc);
+ case '~':
+ // TODO: i don't know what this does, does it need special handling?
+ current_column++;
+ return Token::make (TILDE, loc);
+ case '\\':
+ // TODO: i don't know what this does, does it need special handling?
+ current_column++;
+ return Token::make (BACKSLASH, loc);
+ case '`':
+ // TODO: i don't know what this does, does it need special handling?
+ current_column++;
+ return Token::make (BACKTICK, loc);
+ case '|':
+ if (peek_input () == '=')
+ {
+ // bitwise or-assign?
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (PIPE_EQ, loc);
+ }
+ else if (peek_input () == '|')
+ {
+ // logical or
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (OR, loc);
+ }
+ else
+ {
+ // bitwise or
+ current_column++;
+
+ return Token::make (PIPE, loc);
+ }
+ case '&':
+ if (peek_input () == '=')
+ {
+ // bitwise and-assign?
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (AMP_EQ, loc);
+ }
+ else if (peek_input () == '&')
+ {
+ // logical and
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (LOGICAL_AND, loc);
+ }
+ else
+ {
+ // bitwise and/reference
+ current_column++;
+
+ return Token::make (AMP, loc);
+ }
+ case '.':
+ if (peek_input () == '.')
+ {
+ if (peek_input (1) == '.')
+ {
+ // ellipsis
+ skip_input (1);
+ current_column += 3;
+
+ return Token::make (ELLIPSIS, loc);
+ }
+ else if (peek_input (1) == '=')
+ {
+ // ..=
+ skip_input (1);
+ current_column += 3;
+
+ return Token::make (DOT_DOT_EQ, loc);
+ }
+ else
+ {
+ // ..
+ skip_input ();
+ current_column += 2;
+
+ return Token::make (DOT_DOT, loc);
+ }
+ }
+ else if (!ISDIGIT (peek_input ()))
+ {
+ // single dot .
+ // Only if followed by a non-number
+ current_column++;
+ return Token::make (DOT, loc);
+ }
+ }
+ // TODO: special handling of _ in the lexer? instead of being identifier
+
+ // byte and byte string test
+ if (current_char == 'b')
+ {
+ if (peek_input () == '\'')
+ {
+ // byte - allows any ascii or escapes
+ // would also have to take into account escapes: \x hex_digit
+ // hex_digit, \n, \r, \t, \\, \0
+
+ int length = 1;
+
+ // char to save
+ char byte_char;
+
+ skip_input ();
+ // make current char the next character
+ current_char = peek_input ();
+
+ // detect escapes
+ if (current_char == '\\')
+ {
+ /*skip_input();
+
+ // make current_char next character (letter)
+ current_char = peek_input();*/
+
+ parse_escape (length, byte_char, '\'');
+
+ if (byte_char > 127)
+ {
+ rust_error_at (get_current_location (),
+ "byte char '%c' out of range", byte_char);
+ byte_char = 0;
+ }
+
+ // skip_input();
+ current_char = peek_input ();
+ length++;
+
+ if (current_char != '\'')
+ {
+ rust_error_at (get_current_location (),
+ "unclosed byte char");
+ }
+
+ // TODO: ensure skipping is needed here
+ skip_input ();
+ current_char = peek_input ();
+ length++; // go to next char
+ }
+ else if (current_char != '\'')
+ {
+ // otherwise, get character from direct input character
+ byte_char = current_char;
+
+ skip_input ();
+ current_char = peek_input ();
+
+ if (current_char != '\'')
+ {
+ rust_error_at (get_current_location (),
+ "unclosed byte char");
+ }
+
+ // TODO: ensure skipping is needed here
+ skip_input ();
+ current_char = peek_input ();
+ length++; // go to next char
+ }
+ else
+ {
+ rust_error_at (get_current_location (),
+ "no character inside '' for byte char");
+ }
+
+ current_column += length;
+
+ return Token::make_byte_char (loc, byte_char);
+ }
+ else if (peek_input () == '"')
+ {
+ // byte string
+
+ // skip quote character
+ skip_input ();
+
+ std::string str;
+ str.reserve (16); // some sensible default
+
+ int length = 1;
+ current_char = peek_input ();
+ // TODO: handle escapes properly
+
+ while (current_char != '"' && current_char != '\n')
+ {
+ if (current_char == '\\')
+ {
+ char output_char = 0;
+ parse_escape (length, output_char, '"');
+
+ if (output_char > 127)
+ {
+ rust_error_at (
+ get_current_location (),
+ "char '%c' in byte string out of range",
+ output_char);
+ output_char = 0;
+ }
+
+ str += output_char;
+
+ continue;
+ }
+
+ length++;
+
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ if (current_char == '\n')
+ {
+ rust_error_at (get_current_location (),
+ "unended byte string literal");
+ }
+ else if (current_char == '"')
+ {
+ skip_input ();
+ current_char = peek_input ();
+ }
+ else
+ {
+ gcc_unreachable ();
+ }
+
+ return Token::make_byte_string (loc, str);
+ // TODO: ensure escapes and string continue work properly
+ }
+ else if (peek_input () == 'r'
+ && (peek_input (1) == '#' || peek_input (1) == '"'))
+ {
+ // raw byte string literals
+ std::string str;
+ str.reserve (16); // some sensible default
+
+ int length = 1;
+ int hash_count = 0;
+
+ // get hash count at beginnning
+ skip_input ();
+ current_char = peek_input ();
+ while (current_char == '#')
+ {
+ hash_count++;
+ length++;
+
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ if (current_char != '"')
+ {
+ rust_error_at (get_current_location (),
+ "raw byte string has no opening '\"'");
+ }
+
+ skip_input ();
+ current_char = peek_input ();
+
+ while (true)
+ {
+ if (current_char == '"')
+ {
+ bool enough_hashes = true;
+
+ for (int i = 0; i < hash_count; i++)
+ {
+ if (peek_input (i + 1) != '#')
+ {
+ enough_hashes = false; // could continue here -
+ // improve performance
+ }
+ }
+
+ if (enough_hashes)
+ {
+ // skip enough input and peek enough input
+ skip_input (hash_count); // is this enough?
+ current_char = peek_input ();
+ length += hash_count + 1;
+ break;
+ }
+ }
+
+ length++;
+
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ // TODO: does this work properly
+ return Token::make_byte_string (loc, str);
+ }
+ }
+
+ // raw stuff
+ if (current_char == 'r')
+ {
+ int peek = peek_input ();
+ int peek1 = peek_input (1);
+
+ if (peek == '#' && (ISALPHA (peek1) || peek1 == '_'))
+ {
+ // raw identifier
+ std::string str;
+ str.reserve (16); // default
+
+ skip_input ();
+ current_char = peek_input ();
+
+ current_column += 2;
+
+ str += current_char;
+
+ bool first_is_underscore = current_char == '_';
+
+ int length = 1;
+ current_char = peek_input ();
+ // loop through entire name
+ while (ISALPHA (current_char) || ISDIGIT (current_char)
+ || current_char == '_')
+ {
+ length++;
+
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ // if just a single underscore, not an identifier
+ if (first_is_underscore && length == 1)
+ {
+ rust_error_at (get_current_location (),
+ "'_' is not a valid raw identifier");
+ }
+
+ if (str == "crate" || str == "extern" || str == "self"
+ || str == "super" || str == "Self")
+ {
+ rust_error_at (get_current_location (),
+ "'%s' is a forbidden raw identifier",
+ str.c_str ());
+ }
+ else
+ {
+ return Token::make_identifier (loc, str);
+ }
+ }
+ else if (peek == '"'
+ || (peek == '#' && (ISALPHA (peek1) || peek1 == '_')))
+ {
+ // raw string literals
+ std::string str;
+ str.reserve (16); // some sensible default
+
+ int length = 1;
+ int hash_count = 0;
+
+ // get hash count at beginnning
+ current_char = peek;
+ while (current_char == '#')
+ {
+ hash_count++;
+ length++;
+
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ if (current_char != '"')
+ {
+ rust_error_at (get_current_location (),
+ "raw string has no opening '\"'");
+ }
+
+ skip_input ();
+ Codepoint current_char32 = test_peek_codepoint_input ();
+
+ while (true)
+ {
+ if (current_char32.value == '"')
+ {
+ bool enough_hashes = true;
+
+ for (int i = 0; i < hash_count; i++)
+ {
+ // if (test_peek_codepoint_input(i + 1) != '#') {
+ // TODO: ensure this is a good enough replacement
+ if (peek_input (i + 1) != '#')
+ {
+ enough_hashes = false; // could continue here -
+ // improve performance
+ }
+ }
+
+ if (enough_hashes)
+ {
+ // skip enough input and peek enough input
+ skip_input (hash_count); // is this enough?
+ current_char = peek_input ();
+ length += hash_count + 1;
+ break;
+ }
+ }
+
+ length++;
+
+ str += current_char32;
+ test_skip_codepoint_input ();
+ current_char32 = test_peek_codepoint_input ();
+ }
+
+ current_column += length;
+
+ // TODO: does this work properly
+ return Token::make_string (loc, str);
+ }
+ }
+
+ // find identifiers and keywords
+ if (ISALPHA (current_char) || current_char == '_')
+ {
+ std::string str;
+ str.reserve (16); // default
+ str += current_char;
+
+ bool first_is_underscore = current_char == '_';
+
+ int length = 1;
+ current_char = peek_input ();
+ // loop through entire name
+ while (ISALPHA (current_char) || ISDIGIT (current_char)
+ || current_char == '_')
+ {
+ length++;
+
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ // if just a single underscore, not an identifier
+ if (first_is_underscore && length == 1)
+ {
+ return Token::make (UNDERSCORE, loc);
+ }
+
+ TokenId keyword = classify_keyword (str);
+ if (keyword == IDENTIFIER)
+ {
+ return Token::make_identifier (loc, str);
+ }
+ else
+ {
+ return Token::make (keyword, loc);
+ }
+ }
+
+ // identify literals
+ // int or float literals - not processed properly
+ if (ISDIGIT (current_char) || current_char == '.')
+ { // _ not allowed as first char
+ std::string str;
+ str.reserve (16); // some sensible default
+ str += current_char;
+
+ PrimitiveCoreType type_hint = CORETYPE_UNKNOWN;
+
+ bool is_real = (current_char == '.');
+
+ int length = 1;
+
+ // handle binary, octal, hex literals
+ if (current_char == '0' && !ISDIGIT (peek_input ()))
+ {
+ current_char = peek_input ();
+
+ if (current_char == 'x')
+ {
+ // hex (integer only)
+
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ // add 'x' to string after 0 so it is 0xFFAA or whatever
+ str += 'x';
+
+ // loop through to add entire hex number to string
+ while (is_x_digit (current_char) || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to number
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ continue;
+ }
+
+ length++;
+
+ // add raw hex numbers
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ // convert hex value to decimal representation
+ long hex_num = ::std::strtol (str.c_str (), NULL, 16);
+
+ // create output string stream for hex value to be converted
+ // to string again
+ // TODO: if too slow, use sprintf
+ ::std::ostringstream ostr;
+ ostr << hex_num;
+
+ // reassign string representation to converted value
+ str = ostr.str ();
+
+ // parse in type suffix if it exists
+ parse_in_type_suffix (/*current_char, */ type_hint, length);
+
+ if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
+ {
+ rust_error_at (
+ get_current_location (),
+ "invalid type suffix '%s' for integer (hex) literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+ else if (current_char == 'o')
+ {
+ // octal (integer only)
+
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ // don't add any characters as C octals are just 0124 or
+ // whatever
+
+ // loop through to add entire octal number to string
+ while (is_octal_digit (current_char) || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to number
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ continue;
+ }
+
+ length++;
+
+ // add raw octal numbers
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ // convert octal value to decimal representation
+ long octal_num = ::std::strtol (str.c_str (), NULL, 8);
+
+ // create output string stream for octal value to be converted
+ // to string again
+ // TODO: if too slow, use sprintf
+ ::std::ostringstream ostr;
+ ostr << octal_num;
+
+ // reassign string representation to converted value
+ str = ostr.str ();
+
+ // parse in type suffix if it exists
+ parse_in_type_suffix (/*current_char, */ type_hint, length);
+
+ if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
+ {
+ rust_error_at (
+ get_current_location (),
+ "invalid type suffix '%s' for integer (octal) literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+ else if (current_char == 'b')
+ {
+ // binary (integer only)
+
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ // don't add any characters as C binary numbers are not really
+ // supported
+
+ // loop through to add entire binary number to string
+ while (is_bin_digit (current_char) || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to number
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ continue;
+ }
+
+ length++;
+
+ // add raw binary numbers
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ current_column += length;
+
+ // convert binary value to decimal representation
+ long bin_num = ::std::strtol (str.c_str (), NULL, 2);
+
+ // create output string stream for binary value to be
+ // converted to string again
+ // TODO: if too slow, use sprintf
+ ::std::ostringstream ostr;
+ ostr << bin_num;
+
+ // reassign string representation to converted value
+ str = ostr.str ();
+
+ // parse in type suffix if it exists
+ parse_in_type_suffix (/*current_char, */ type_hint, length);
+
+ if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
+ {
+ rust_error_at (
+ get_current_location (),
+ "invalid type suffix '%s' for integer (binary) literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+ }
+ else
+ {
+ // handle decimals (integer or float)
+
+ current_char = peek_input ();
+
+ // parse initial decimal literal - assuming integer
+ // TODO: test if works
+ parse_in_decimal (/*current_char, */ str, length);
+
+ // detect float literal - TODO: fix: "242." is not recognised as a
+ // float literal
+ if (current_char == '.' && is_float_digit (peek_input (1)))
+ {
+ // float with a '.', parse another decimal into it
+
+ is_real = true;
+
+ // add . to str
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ // parse another decimal number for float
+ // TODO: test if works
+ parse_in_decimal (/*current_char, */ str, length);
+
+ // parse in exponent part if it exists
+ // test to see if this works:
+ parse_in_exponent_part (/*current_char, */ str, length);
+
+ // parse in type suffix if it exists
+ // TODO: see if works:
+ parse_in_type_suffix (/*current_char, */ type_hint, length);
+
+ if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
+ && type_hint != CORETYPE_UNKNOWN)
+ {
+ rust_error_at (
+ get_current_location (),
+ "invalid type suffix '%s' for float literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+ else if (current_char == '.'
+ && check_valid_float_dot_end (peek_input (1)))
+ {
+ is_real = true;
+
+ // add . to str
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ // add a '0' after the . to stop ambiguity
+ str += '0';
+
+ // don't parse another decimal number for float
+
+ // parse in exponent part if it exists - shouldn't exist?
+ // parse_in_exponent_part(/*current_char, */ str, length);
+
+ // parse in type suffix if it exists - shouldn't exist?
+ // TODO: see if works:
+ // parse_in_type_suffix(/*current_char, */ type_hint, length);
+
+ if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
+ && type_hint != CORETYPE_UNKNOWN)
+ {
+ rust_error_at (
+ get_current_location (),
+ "invalid type suffix '%s' for float literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+ else if (current_char == 'E' || current_char == 'e')
+ {
+ is_real = true;
+
+ // parse exponent part
+ parse_in_exponent_part (/*current_char, */ str, length);
+
+ // parse in type suffix if it exists
+ parse_in_type_suffix (/*current_char, */ type_hint, length);
+
+ if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
+ && type_hint != CORETYPE_UNKNOWN)
+ {
+ rust_error_at (
+ get_current_location (),
+ "invalid type suffix '%s' for float literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+ else
+ {
+ // is an integer
+
+ // parse in type suffix if it exists
+ parse_in_type_suffix (/*current_char, */ type_hint, length);
+
+ if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
+ {
+ rust_error_at (get_current_location (),
+ "invalid type suffix '%s' for integer "
+ "(decimal) literal",
+ get_type_hint_string (type_hint));
+ }
+ }
+
+ current_column += length;
+ }
+
+ // actually make the tokens
+ if (is_real)
+ {
+ return Token::make_float (loc, str, type_hint);
+ }
+ else
+ {
+ return Token::make_int (loc, str, type_hint);
+ }
+ }
+
+ // string literals - not processed properly
+ if (current_char == '"')
+ {
+ Codepoint current_char32;
+
+ std::string str;
+ str.reserve (16); // some sensible default
+
+ int length = 1;
+ current_char32 = test_peek_codepoint_input ();
+
+ // ok initial peek_codepoint seems to work without "too long"
+
+ while (current_char32.value != '\n' && current_char32.value != '"')
+ {
+ // TODO: handle escapes and string continue
+ if (current_char32.value == '\\')
+ {
+ // parse escape
+ parse_utf8_escape (length, current_char32, '\'');
+
+ // TODO: find a way to parse additional characters after the
+ // escape? return after parsing escape?
+
+ str += current_char32;
+
+ // required as parsing utf8 escape only changes current_char
+ // or something
+ current_char32 = test_peek_codepoint_input ();
+
+ continue;
+ }
+
+ length += test_get_input_codepoint_length ();
+
+ // does this work? not technically a char. maybe have to convert
+ // to char series
+ str += current_char32;
+ test_skip_codepoint_input ();
+ current_char32 = test_peek_codepoint_input ();
+ }
+
+ current_column += length;
+
+ if (current_char32.value == '\n')
+ {
+ rust_error_at (get_current_location (), "unended string literal");
+ }
+ else if (current_char32.value == '"')
+ {
+ skip_input ();
+
+ current_char = peek_input ();
+ }
+ else
+ {
+ gcc_unreachable ();
+ }
+
+ return Token::make_string (loc, str);
+ // TODO: account for escapes and string continue
+ // also, in rust a string is a series of unicode characters (4 bytes)
+ }
+
+ // char literal attempt
+ if (current_char == '\'')
+ {
+ // rust chars are 4 bytes and have some weird unicode representation
+ // thing
+ Codepoint current_char32;
+
+ int length = 1;
+
+ current_char32 = test_peek_codepoint_input ();
+
+ // parse escaped char literal
+ if (current_char32.value == '\\')
+ {
+ // parse escape
+ parse_utf8_escape (length, current_char32, '\'');
+
+ // TODO - this skip may not be needed?
+ // test_skip_codepoint_input();
+
+ if (test_peek_codepoint_input ().value != '\'')
+ {
+ rust_error_at (get_current_location (),
+ "unended char literal");
+ }
+ else
+ {
+ test_skip_codepoint_input ();
+ current_char = peek_input ();
+ length++;
+ }
+
+ current_column += length;
+
+ // TODO: FIX - char is actually 4 bytes in Rust (uint32) due to
+ // unicode
+ return Token::make_char (loc, current_char32);
+ }
+ else
+ {
+ // current_char32 = test_peek_codepoint_input();
+ test_skip_codepoint_input ();
+
+ if (test_peek_codepoint_input ().value == '\'')
+ {
+ // parse normal char literal
+ // TODO: FIX - char is actually 4 bytes in Rust (uint32) due
+ // to unicode
+
+ // skip the ' character
+ skip_input ();
+ current_char = peek_input ();
+
+ // TODO fix due to different widths of utf-8 chars
+ current_column += 3;
+
+ return Token::make_char (loc, current_char32);
+ }
+ else if (ISDIGIT (current_char32.value)
+ || ISALPHA (current_char32.value)
+ || current_char32.value == '_')
+ {
+ // parse lifetime name
+ ::std::string str;
+ // TODO: does this work properly?
+ str += current_char32;
+
+ // TODO: fix lifetime name thing - actually, why am I even
+ // using utf-8 here?
+
+ int length = 1;
+
+ current_char32 = test_peek_codepoint_input ();
+
+ while (ISDIGIT (current_char32.value)
+ || ISALPHA (current_char32.value)
+ || current_char32.value == '_')
+ {
+ length += test_get_input_codepoint_length ();
+
+ str += current_char32;
+ test_skip_codepoint_input ();
+ current_char32 = test_peek_codepoint_input ();
+ }
+
+ current_column += length;
+
+ return Token::make_lifetime (loc, str);
+ }
+ else
+ {
+ rust_error_at (get_current_location (),
+ "expected ' after character constant");
+ }
+ }
+ }
+
+ // didn't match anything so error
+ rust_error_at (loc, "unexpected character '%x'", current_char);
+ current_column++;
}
+}
- // Shitty pass-by-reference way of parsing in type suffix.
- bool Lexer::parse_in_type_suffix(
- /*char& current_char, */ PrimitiveCoreType& type_hint, int& length) {
- ::std::string suffix;
- suffix.reserve(5);
-
- // get suffix
- while (ISALPHA(current_char) || ISDIGIT(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to suffix
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- suffix += current_char;
- skip_input();
- current_char = peek_input();
- }
-
- if (suffix.empty()) {
- // no type suffix: do nothing but also no error
- return false;
- } else if (suffix == "f32") {
- type_hint = CORETYPE_F32;
- } else if (suffix == "f64") {
- type_hint = CORETYPE_F64;
- } else if (suffix == "i8") {
- type_hint = CORETYPE_I8;
- } else if (suffix == "i16") {
- type_hint = CORETYPE_I16;
- } else if (suffix == "i32") {
- type_hint = CORETYPE_I32;
- } else if (suffix == "i64") {
- type_hint = CORETYPE_I64;
- } else if (suffix == "i128") {
- type_hint = CORETYPE_I128;
- } else if (suffix == "isize") {
- type_hint = CORETYPE_ISIZE;
- } else if (suffix == "u8") {
- type_hint = CORETYPE_U8;
- } else if (suffix == "u16") {
- type_hint = CORETYPE_U16;
- } else if (suffix == "u32") {
- type_hint = CORETYPE_U32;
- } else if (suffix == "u64") {
- type_hint = CORETYPE_U64;
- } else if (suffix == "u128") {
- type_hint = CORETYPE_U128;
- } else if (suffix == "usize") {
- type_hint = CORETYPE_USIZE;
- } else {
- rust_error_at(get_current_location(), "unknown number suffix '%s'", suffix.c_str());
-
- return false;
- }
-
- return true;
+// Shitty pass-by-reference way of parsing in type suffix.
+bool
+Lexer::parse_in_type_suffix (
+ /*char& current_char, */ PrimitiveCoreType &type_hint, int &length)
+{
+ ::std::string suffix;
+ suffix.reserve (5);
+
+ // get suffix
+ while (ISALPHA (current_char) || ISDIGIT (current_char)
+ || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to suffix
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ continue;
+ }
+
+ length++;
+
+ suffix += current_char;
+ skip_input ();
+ current_char = peek_input ();
}
- void Lexer::parse_in_exponent_part(/*char& current_char, */ std::string& str, int& length) {
- if (current_char == 'E' || current_char == 'e') {
- // add exponent to string as strtod works with it
- str += current_char;
- skip_input();
- current_char = peek_input();
-
- length++;
-
- // special - and + handling
- if (current_char == '-') {
- str += '-';
-
- skip_input();
- current_char = peek_input();
-
- length++;
- } else if (current_char == '+') {
- // don't add + but still skip input
- skip_input();
- current_char = peek_input();
-
- length++;
- }
-
- // parse another decimal number for exponent
- parse_in_decimal(/*current_char, */ str, length);
- }
+ if (suffix.empty ())
+ {
+ // no type suffix: do nothing but also no error
+ return false;
}
-
- void Lexer::parse_in_decimal(/*char& current_char, */ std::string& str, int& length) {
- while (ISDIGIT(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to number
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- str += current_char;
- skip_input();
- current_char = peek_input();
- }
+ else if (suffix == "f32")
+ {
+ type_hint = CORETYPE_F32;
}
-
- // Replace all assorted parse_x_escape with this? Avoids the backwards/peek issue.
- bool Lexer::parse_escape(int& length, char& output_char, char opening_char) {
- // skip to actual letter
- skip_input();
- current_char = peek_input();
- length++;
-
- switch (current_char) {
- case 'x': {
- // hex char string (null-terminated)
- char hexNum[3] = { 0, 0, 0 };
-
- // first hex char
- skip_input();
- current_char = peek_input();
- length++;
-
- if (!ISXDIGIT(current_char)) {
- rust_error_at(get_current_location(), "invalid character '\\x%c' in \\x sequence",
- current_char);
- }
- hexNum[0] = current_char;
-
- // second hex char
- skip_input();
- current_char = peek_input();
- length++;
-
- if (!ISXDIGIT(current_char)) {
- rust_error_at(get_current_location(), "invalid character '\\x%c' in \\x sequence",
- current_char);
- }
- hexNum[1] = current_char;
-
- long hexLong = ::std::strtol(hexNum, NULL, 16);
-
- if (hexLong > 127)
- rust_error_at(get_current_location(),
- "ascii \\x escape '\\x%s' out of range - allows up to '\\x7F'", hexNum);
- // gcc_assert(hexLong < 128); // as ascii
- char hexChar = static_cast<char>(hexLong);
-
- // TODO: fix - does this actually give the right character?
- output_char = hexChar;
- } break;
- case 'n':
- output_char = '\n';
- break;
- case 'r':
- output_char = '\r';
- break;
- case 't':
- output_char = '\t';
- break;
- case '\\':
- output_char = '\\';
- break;
- case '0':
- output_char = '\0';
- break;
- case '\'':
- output_char = '\'';
- break;
- case '"':
- output_char = '"';
- break;
- case 'u': {
- // TODO: shouldn't be used with this - use parse_utf8_escape
-
- skip_input();
- current_char = peek_input();
- length++;
-
- bool need_close_brace = false;
-
- // TODO: rustc lexer doesn't seem to allow not having { but mrustc lexer does? look at
- // spec?
- if (current_char == '{') {
- need_close_brace = true;
-
- skip_input();
- current_char = peek_input();
- length++;
- }
-
- // parse unicode escape
- // 1-6 hex digits?
- ::std::string num_str;
- num_str.reserve(6);
-
- // test adding number directly
- uint32_t test_val;
-
- // loop through to add entire hex number to string
- while (is_x_digit(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to number
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- // add raw hex numbers
- num_str += current_char;
-
- // test adding number directly
- char tmp[2] = { current_char, 0 };
- test_val *= 16;
- test_val += ::std::strtol(tmp, NULL, 16);
-
- skip_input();
- current_char = peek_input();
- }
-
- // ensure closing brace
- if (need_close_brace && current_char != '}') {
- // actually an error
- rust_error_at(
- get_current_location(), "expected terminating '}' in unicode escape");
- return false;
- }
-
- // ensure 1-6 hex characters
- if (num_str.length() > 6 || num_str.length() < 1) {
- rust_error_at(get_current_location(),
- "unicode escape should be between 1 and 6 hex characters; it is %lu",
- num_str.length());
- return false;
- }
-
- long hex_num = ::std::strtol(num_str.c_str(), NULL, 16);
-
- // as debug, check hex_num = test_val
- if (hex_num > 255) {
- rust_error_at(
- get_current_location(), "non-ascii chars not implemented yet, defaulting to 0");
- hex_num = 0;
- }
-
- // make output_char the value - UTF-8?
- // TODO: actually make this work - output char must be 4 bytes, do I need a string for
- // this?
- output_char = static_cast</*uint32_t*/ char>(hex_num);
-
- return true;
- } break;
- case '\r':
- case '\n':
- // string continue
- while (is_whitespace(current_char)) {
- if (current_char == '\n') {
- current_line++;
- current_column = 1;
- // tell line_table that new line starts
- linemap_line_start(::line_table, current_line, max_column_hint);
-
- // reset "length"
- length = 1;
-
- // get next char
- skip_input();
- current_char = peek_input();
-
- continue;
- }
-
- skip_input();
- current_char = peek_input();
- length++;
- }
-
- if (current_char == '\\') {
- parse_escape(length, output_char, opening_char);
- return true;
- } else if (current_char == opening_char) {
- // TODO: does this skip the ' or " character? It shouldn't.
- output_char = 0;
- return true;
- } else {
- output_char = current_char;
-
- // TODO: test has right result
- /*skip_input();
- current_char = peek_input();*/
-
- return true;
- }
- default:
- rust_error_at(get_current_location(), "unknown escape sequence '\\%c'", current_char);
- // returns false if no parsing could be done
- return false;
- break;
- }
- // all non-special cases (unicode, string continue) should skip their used char
- skip_input();
- current_char = peek_input();
- length++;
-
- // returns true if parsing was successful
- return true;
+ else if (suffix == "f64")
+ {
+ type_hint = CORETYPE_F64;
}
+ else if (suffix == "i8")
+ {
+ type_hint = CORETYPE_I8;
+ }
+ else if (suffix == "i16")
+ {
+ type_hint = CORETYPE_I16;
+ }
+ else if (suffix == "i32")
+ {
+ type_hint = CORETYPE_I32;
+ }
+ else if (suffix == "i64")
+ {
+ type_hint = CORETYPE_I64;
+ }
+ else if (suffix == "i128")
+ {
+ type_hint = CORETYPE_I128;
+ }
+ else if (suffix == "isize")
+ {
+ type_hint = CORETYPE_ISIZE;
+ }
+ else if (suffix == "u8")
+ {
+ type_hint = CORETYPE_U8;
+ }
+ else if (suffix == "u16")
+ {
+ type_hint = CORETYPE_U16;
+ }
+ else if (suffix == "u32")
+ {
+ type_hint = CORETYPE_U32;
+ }
+ else if (suffix == "u64")
+ {
+ type_hint = CORETYPE_U64;
+ }
+ else if (suffix == "u128")
+ {
+ type_hint = CORETYPE_U128;
+ }
+ else if (suffix == "usize")
+ {
+ type_hint = CORETYPE_USIZE;
+ }
+ else
+ {
+ rust_error_at (get_current_location (), "unknown number suffix '%s'",
+ suffix.c_str ());
- bool Lexer::parse_utf8_escape(int& length, Codepoint& output_char, char opening_char) {
- // skip to actual letter
- skip_input();
- current_char = peek_input();
- length++;
-
- switch (current_char) {
- case 'x': {
- // hex char string (null-terminated)
- char hexNum[3] = { 0, 0, 0 };
-
- // first hex char
- skip_input();
- current_char = peek_input();
- length++;
-
- if (!ISXDIGIT(current_char)) {
- rust_error_at(get_current_location(), "invalid character '\\x%c' in \\x sequence",
- current_char);
- }
- hexNum[0] = current_char;
-
- // second hex char
- skip_input();
- current_char = peek_input();
- length++;
-
- if (!ISXDIGIT(current_char)) {
- rust_error_at(get_current_location(), "invalid character '\\x%c' in \\x sequence",
- current_char);
- }
- hexNum[1] = current_char;
-
- long hexLong = ::std::strtol(hexNum, NULL, 16);
-
- if (hexLong > 127)
- rust_error_at(get_current_location(),
- "ascii \\x escape '\\x%s' out of range - allows up to '\\x7F'", hexNum);
- // gcc_assert(hexLong < 128); // as ascii
- char hexChar = static_cast<char>(hexLong);
-
- // TODO: fix - does this actually give the right character?
- output_char = hexChar;
- } break;
- case 'n':
- output_char = '\n';
- break;
- case 'r':
- output_char = '\r';
- break;
- case 't':
- output_char = '\t';
- break;
- case '\\':
- output_char = '\\';
- break;
- case '0':
- output_char = '\0';
- break;
- case '\'':
- output_char = '\'';
- break;
- case '"':
- output_char = '"';
- break;
- case 'u': {
- skip_input();
- current_char = peek_input();
- length++;
-
- bool need_close_brace = false;
-
- // TODO: rustc lexer doesn't seem to allow not having { but mrustc lexer does? look at
- // spec?
- if (current_char == '{') {
- need_close_brace = true;
-
- skip_input();
- current_char = peek_input();
- length++;
- }
-
- // parse unicode escape
- // 1-6 hex digits?
- ::std::string num_str;
- num_str.reserve(6);
-
- // test adding number directly
- uint32_t test_val;
-
- // loop through to add entire hex number to string
- while (is_x_digit(current_char) || current_char == '_') {
- if (current_char == '_') {
- // don't add _ to number
- skip_input();
- current_char = peek_input();
-
- length++;
-
- continue;
- }
-
- length++;
-
- // add raw hex numbers
- num_str += current_char;
-
- // test adding number directly
- char tmp[2] = { current_char, 0 };
- test_val *= 16;
- test_val += ::std::strtol(tmp, NULL, 16);
-
- skip_input();
- current_char = peek_input();
- }
+ return false;
+ }
- // ensure closing brace if required
- if (need_close_brace) {
- if (current_char == '}') {
- skip_input();
- current_char = peek_input();
- length++;
- } else {
- // actually an error
- rust_error_at(
- get_current_location(), "expected terminating '}' in unicode escape");
- return false;
- }
- }
+ return true;
+}
- // ensure 1-6 hex characters
- if (num_str.length() > 6 || num_str.length() < 1) {
- rust_error_at(get_current_location(),
- "unicode escape should be between 1 and 6 hex characters; it is %lu",
- num_str.length());
- return false;
- }
+void
+Lexer::parse_in_exponent_part (/*char& current_char, */ std::string &str,
+ int &length)
+{
+ if (current_char == 'E' || current_char == 'e')
+ {
+ // add exponent to string as strtod works with it
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ // special - and + handling
+ if (current_char == '-')
+ {
+ str += '-';
+
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+ }
+ else if (current_char == '+')
+ {
+ // don't add + but still skip input
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+ }
+
+ // parse another decimal number for exponent
+ parse_in_decimal (/*current_char, */ str, length);
+ }
+}
- long hex_num = ::std::strtol(num_str.c_str(), NULL, 16);
+void
+Lexer::parse_in_decimal (/*char& current_char, */ std::string &str, int &length)
+{
+ while (ISDIGIT (current_char) || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to number
+ skip_input ();
+ current_char = peek_input ();
- // assert fits a uint32_t
- gcc_assert(hex_num < 4294967296);
+ length++;
- // ok can't figure out how to just convert to codepoint or use "this" so create new
- // one
- output_char = Codepoint(static_cast<uint32_t>(hex_num));
+ continue;
+ }
- // TODO: what is being outputted? the escape code for the unicode char (unicode
- // number) or the character number?
+ length++;
- return true;
- } break;
- case '\r':
- case '\n':
- // string continue
- while (is_whitespace(current_char)) {
- if (current_char == '\n') {
- current_line++;
- current_column = 1;
- // tell line_table that new line starts
- linemap_line_start(::line_table, current_line, max_column_hint);
-
- // reset "length"
- length = 1;
-
- // get next char
- skip_input();
- current_char = peek_input();
-
- continue;
- }
-
- skip_input();
- current_char = peek_input();
- length++;
- }
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+}
- if (current_char == '\\') {
- parse_utf8_escape(length, output_char, opening_char);
- return true;
- } else if (current_char == opening_char) {
- // TODO: does this skip the ' or " character? It shouldn't.
- output_char = 0;
- return true;
- } else {
- output_char = current_char;
-
- // TODO: test has right result
- /*skip_input();
- current_char = peek_input();*/
-
- return true;
- }
- default:
- rust_error_at(get_current_location(), "unknown escape sequence '\\%c'", current_char);
- // returns false if no parsing could be done
- return false;
- break;
- }
- // all non-special cases (unicode, string continue) should skip their used char
- skip_input();
- current_char = peek_input();
- length++;
+// Replace all assorted parse_x_escape with this? Avoids the backwards/peek
+// issue.
+bool
+Lexer::parse_escape (int &length, char &output_char, char opening_char)
+{
+ // skip to actual letter
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ switch (current_char)
+ {
+ case 'x':
+ {
+ // hex char string (null-terminated)
+ char hexNum[3] = {0, 0, 0};
+
+ // first hex char
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ if (!ISXDIGIT (current_char))
+ {
+ rust_error_at (get_current_location (),
+ "invalid character '\\x%c' in \\x sequence",
+ current_char);
+ }
+ hexNum[0] = current_char;
+
+ // second hex char
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ if (!ISXDIGIT (current_char))
+ {
+ rust_error_at (get_current_location (),
+ "invalid character '\\x%c' in \\x sequence",
+ current_char);
+ }
+ hexNum[1] = current_char;
+
+ long hexLong = ::std::strtol (hexNum, NULL, 16);
+
+ if (hexLong > 127)
+ rust_error_at (
+ get_current_location (),
+ "ascii \\x escape '\\x%s' out of range - allows up to '\\x7F'",
+ hexNum);
+ // gcc_assert(hexLong < 128); // as ascii
+ char hexChar = static_cast<char> (hexLong);
+
+ // TODO: fix - does this actually give the right character?
+ output_char = hexChar;
+ }
+ break;
+ case 'n':
+ output_char = '\n';
+ break;
+ case 'r':
+ output_char = '\r';
+ break;
+ case 't':
+ output_char = '\t';
+ break;
+ case '\\':
+ output_char = '\\';
+ break;
+ case '0':
+ output_char = '\0';
+ break;
+ case '\'':
+ output_char = '\'';
+ break;
+ case '"':
+ output_char = '"';
+ break;
+ case 'u':
+ {
+ // TODO: shouldn't be used with this - use parse_utf8_escape
+
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ bool need_close_brace = false;
+
+ // TODO: rustc lexer doesn't seem to allow not having { but mrustc lexer
+ // does? look at spec?
+ if (current_char == '{')
+ {
+ need_close_brace = true;
+
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+ }
+
+ // parse unicode escape
+ // 1-6 hex digits?
+ ::std::string num_str;
+ num_str.reserve (6);
+
+ // test adding number directly
+ uint32_t test_val;
+
+ // loop through to add entire hex number to string
+ while (is_x_digit (current_char) || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to number
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ continue;
+ }
+
+ length++;
+
+ // add raw hex numbers
+ num_str += current_char;
+
+ // test adding number directly
+ char tmp[2] = {current_char, 0};
+ test_val *= 16;
+ test_val += ::std::strtol (tmp, NULL, 16);
+
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ // ensure closing brace
+ if (need_close_brace && current_char != '}')
+ {
+ // actually an error
+ rust_error_at (get_current_location (),
+ "expected terminating '}' in unicode escape");
+ return false;
+ }
+
+ // ensure 1-6 hex characters
+ if (num_str.length () > 6 || num_str.length () < 1)
+ {
+ rust_error_at (get_current_location (),
+ "unicode escape should be between 1 and 6 hex "
+ "characters; it is %lu",
+ num_str.length ());
+ return false;
+ }
+
+ long hex_num = ::std::strtol (num_str.c_str (), NULL, 16);
+
+ // as debug, check hex_num = test_val
+ if (hex_num > 255)
+ {
+ rust_error_at (
+ get_current_location (),
+ "non-ascii chars not implemented yet, defaulting to 0");
+ hex_num = 0;
+ }
+
+ // make output_char the value - UTF-8?
+ // TODO: actually make this work - output char must be 4 bytes, do I
+ // need a string for this?
+ output_char = static_cast</*uint32_t*/ char> (hex_num);
+
+ return true;
+ }
+ break;
+ case '\r':
+ case '\n':
+ // string continue
+ while (is_whitespace (current_char))
+ {
+ if (current_char == '\n')
+ {
+ current_line++;
+ current_column = 1;
+ // tell line_table that new line starts
+ linemap_line_start (::line_table, current_line, max_column_hint);
+
+ // reset "length"
+ length = 1;
+
+ // get next char
+ skip_input ();
+ current_char = peek_input ();
+
+ continue;
+ }
+
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+ }
+
+ if (current_char == '\\')
+ {
+ parse_escape (length, output_char, opening_char);
+ return true;
+ }
+ else if (current_char == opening_char)
+ {
+ // TODO: does this skip the ' or " character? It shouldn't.
+ output_char = 0;
+ return true;
+ }
+ else
+ {
+ output_char = current_char;
+
+ // TODO: test has right result
+ /*skip_input();
+ current_char = peek_input();*/
+
+ return true;
+ }
+ default:
+ rust_error_at (get_current_location (), "unknown escape sequence '\\%c'",
+ current_char);
+ // returns false if no parsing could be done
+ return false;
+ break;
+ }
+ // all non-special cases (unicode, string continue) should skip their used
+ // char
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ // returns true if parsing was successful
+ return true;
+}
- // returns true if parsing was successful
- return true;
+bool
+Lexer::parse_utf8_escape (int &length, Codepoint &output_char,
+ char opening_char)
+{
+ // skip to actual letter
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ switch (current_char)
+ {
+ case 'x':
+ {
+ // hex char string (null-terminated)
+ char hexNum[3] = {0, 0, 0};
+
+ // first hex char
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ if (!ISXDIGIT (current_char))
+ {
+ rust_error_at (get_current_location (),
+ "invalid character '\\x%c' in \\x sequence",
+ current_char);
+ }
+ hexNum[0] = current_char;
+
+ // second hex char
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ if (!ISXDIGIT (current_char))
+ {
+ rust_error_at (get_current_location (),
+ "invalid character '\\x%c' in \\x sequence",
+ current_char);
+ }
+ hexNum[1] = current_char;
+
+ long hexLong = ::std::strtol (hexNum, NULL, 16);
+
+ if (hexLong > 127)
+ rust_error_at (
+ get_current_location (),
+ "ascii \\x escape '\\x%s' out of range - allows up to '\\x7F'",
+ hexNum);
+ // gcc_assert(hexLong < 128); // as ascii
+ char hexChar = static_cast<char> (hexLong);
+
+ // TODO: fix - does this actually give the right character?
+ output_char = hexChar;
+ }
+ break;
+ case 'n':
+ output_char = '\n';
+ break;
+ case 'r':
+ output_char = '\r';
+ break;
+ case 't':
+ output_char = '\t';
+ break;
+ case '\\':
+ output_char = '\\';
+ break;
+ case '0':
+ output_char = '\0';
+ break;
+ case '\'':
+ output_char = '\'';
+ break;
+ case '"':
+ output_char = '"';
+ break;
+ case 'u':
+ {
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ bool need_close_brace = false;
+
+ // TODO: rustc lexer doesn't seem to allow not having { but mrustc lexer
+ // does? look at spec?
+ if (current_char == '{')
+ {
+ need_close_brace = true;
+
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+ }
+
+ // parse unicode escape
+ // 1-6 hex digits?
+ ::std::string num_str;
+ num_str.reserve (6);
+
+ // test adding number directly
+ uint32_t test_val;
+
+ // loop through to add entire hex number to string
+ while (is_x_digit (current_char) || current_char == '_')
+ {
+ if (current_char == '_')
+ {
+ // don't add _ to number
+ skip_input ();
+ current_char = peek_input ();
+
+ length++;
+
+ continue;
+ }
+
+ length++;
+
+ // add raw hex numbers
+ num_str += current_char;
+
+ // test adding number directly
+ char tmp[2] = {current_char, 0};
+ test_val *= 16;
+ test_val += ::std::strtol (tmp, NULL, 16);
+
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ // ensure closing brace if required
+ if (need_close_brace)
+ {
+ if (current_char == '}')
+ {
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+ }
+ else
+ {
+ // actually an error
+ rust_error_at (get_current_location (),
+ "expected terminating '}' in unicode escape");
+ return false;
+ }
+ }
+
+ // ensure 1-6 hex characters
+ if (num_str.length () > 6 || num_str.length () < 1)
+ {
+ rust_error_at (get_current_location (),
+ "unicode escape should be between 1 and 6 hex "
+ "characters; it is %lu",
+ num_str.length ());
+ return false;
+ }
+
+ long hex_num = ::std::strtol (num_str.c_str (), NULL, 16);
+
+ // assert fits a uint32_t
+ gcc_assert (hex_num < 4294967296);
+
+ // ok can't figure out how to just convert to codepoint or use "this" so
+ // create new one
+ output_char = Codepoint (static_cast<uint32_t> (hex_num));
+
+ // TODO: what is being outputted? the escape code for the unicode char
+ // (unicode number) or the character number?
+
+ return true;
+ }
+ break;
+ case '\r':
+ case '\n':
+ // string continue
+ while (is_whitespace (current_char))
+ {
+ if (current_char == '\n')
+ {
+ current_line++;
+ current_column = 1;
+ // tell line_table that new line starts
+ linemap_line_start (::line_table, current_line, max_column_hint);
+
+ // reset "length"
+ length = 1;
+
+ // get next char
+ skip_input ();
+ current_char = peek_input ();
+
+ continue;
+ }
+
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+ }
+
+ if (current_char == '\\')
+ {
+ parse_utf8_escape (length, output_char, opening_char);
+ return true;
+ }
+ else if (current_char == opening_char)
+ {
+ // TODO: does this skip the ' or " character? It shouldn't.
+ output_char = 0;
+ return true;
+ }
+ else
+ {
+ output_char = current_char;
+
+ // TODO: test has right result
+ /*skip_input();
+ current_char = peek_input();*/
+
+ return true;
+ }
+ default:
+ rust_error_at (get_current_location (), "unknown escape sequence '\\%c'",
+ current_char);
+ // returns false if no parsing could be done
+ return false;
+ break;
}
+ // all non-special cases (unicode, string continue) should skip their used
+ // char
+ skip_input ();
+ current_char = peek_input ();
+ length++;
+
+ // returns true if parsing was successful
+ return true;
+}
#if 0
bool Lexer::parse_ascii_escape(/*char& current_char, */ int& length, char& output_char) {
@@ -2101,262 +2495,305 @@ namespace Rust {
}
#endif
- int Lexer::test_get_input_codepoint_length() {
- uint8_t input = peek_input();
-
- if (input < 128) {
- // ascii -- 1 byte
- // return input;
-
- return 1;
- } else if ((input & 0xC0) == 0x80) {
- // invalid (continuation; can't be first char)
- // return 0xFFFE;
-
- return 0;
- } else if ((input & 0xE0) == 0xC0) {
- // 2 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return 0;
- // return 0xFFFE;
-
- // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
- // return output;
- return 2;
- } else if ((input & 0xF0) == 0xE0) {
- // 3 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return 0;
- // return 0xFFFE;
-
- uint8_t input3 = peek_input(2);
- if ((input3 & 0xC0) != 0x80)
- return 0;
- // return 0xFFFE;
-
- /*uint32_t output
- = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) << 0);
- return output;*/
- return 3;
- } else if ((input & 0xF8) == 0xF0) {
- // 4 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return 0;
- // return 0xFFFE;
-
- uint8_t input3 = peek_input(2);
- if ((input3 & 0xC0) != 0x80)
- return 0;
- // return 0xFFFE;
-
- uint8_t input4 = peek_input(3);
- if ((input4 & 0xC0) != 0x80)
- return 0;
- // return 0xFFFE;
-
- /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
- | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
- return output;*/
- return 4;
- } else {
- rust_error_at(get_current_location(), "invalid UTF-8 (too long)");
- return 0;
- }
+int
+Lexer::test_get_input_codepoint_length ()
+{
+ uint8_t input = peek_input ();
+
+ if (input < 128)
+ {
+ // ascii -- 1 byte
+ // return input;
+
+ return 1;
}
+ else if ((input & 0xC0) == 0x80)
+ {
+ // invalid (continuation; can't be first char)
+ // return 0xFFFE;
- // TODO: rewrite lexing system to use utf-8 "codepoints" rather than bytes?
- Codepoint Lexer::test_peek_codepoint_input() {
- uint8_t input = peek_input();
-
- if (input < 128) {
- // ascii -- 1 byte
- return { input };
- } else if ((input & 0xC0) == 0x80) {
- // invalid (continuation; can't be first char)
- return { 0xFFFE };
- } else if ((input & 0xE0) == 0xC0) {
- // 2 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return { 0xFFFE };
-
- uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
- return { output };
- } else if ((input & 0xF0) == 0xE0) {
- // 3 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return { 0xFFFE };
-
- uint8_t input3 = peek_input(2);
- if ((input3 & 0xC0) != 0x80)
- return { 0xFFFE };
-
- uint32_t output
- = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) << 0);
- return { output };
- } else if ((input & 0xF8) == 0xF0) {
- // 4 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return { 0xFFFE };
-
- uint8_t input3 = peek_input(2);
- if ((input3 & 0xC0) != 0x80)
- return { 0xFFFE };
-
- uint8_t input4 = peek_input(3);
- if ((input4 & 0xC0) != 0x80)
- return { 0xFFFE };
-
- uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
- | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
- return { output };
- } else {
- rust_error_at(get_current_location(), "invalid UTF-8 (too long)");
- return { 0xFFFE };
- }
+ return 0;
+ }
+ else if ((input & 0xE0) == 0xC0)
+ {
+ // 2 bytes
+ uint8_t input2 = peek_input (1);
+ if ((input2 & 0xC0) != 0x80)
+ return 0;
+ // return 0xFFFE;
+
+ // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+ // return output;
+ return 2;
+ }
+ else if ((input & 0xF0) == 0xE0)
+ {
+ // 3 bytes
+ uint8_t input2 = peek_input (1);
+ if ((input2 & 0xC0) != 0x80)
+ return 0;
+ // return 0xFFFE;
+
+ uint8_t input3 = peek_input (2);
+ if ((input3 & 0xC0) != 0x80)
+ return 0;
+ // return 0xFFFE;
+
+ /*uint32_t output
+ = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) <<
+ 0); return output;*/
+ return 3;
}
+ else if ((input & 0xF8) == 0xF0)
+ {
+ // 4 bytes
+ uint8_t input2 = peek_input (1);
+ if ((input2 & 0xC0) != 0x80)
+ return 0;
+ // return 0xFFFE;
+
+ uint8_t input3 = peek_input (2);
+ if ((input3 & 0xC0) != 0x80)
+ return 0;
+ // return 0xFFFE;
+
+ uint8_t input4 = peek_input (3);
+ if ((input4 & 0xC0) != 0x80)
+ return 0;
+ // return 0xFFFE;
+
+ /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+ | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
+ return output;*/
+ return 4;
+ }
+ else
+ {
+ rust_error_at (get_current_location (), "invalid UTF-8 (too long)");
+ return 0;
+ }
+}
- void Lexer::test_skip_codepoint_input() {
- int toSkip = test_get_input_codepoint_length();
- gcc_assert(toSkip >= 1);
+// TODO: rewrite lexing system to use utf-8 "codepoints" rather than bytes?
+Codepoint
+Lexer::test_peek_codepoint_input ()
+{
+ uint8_t input = peek_input ();
- skip_input(toSkip - 1);
+ if (input < 128)
+ {
+ // ascii -- 1 byte
+ return {input};
}
-
- int Lexer::test_get_input_codepoint_n_length(int n_start_offset) {
- uint8_t input = peek_input(n_start_offset);
-
- if (input < 128) {
- // ascii -- 1 byte
- // return input;
- return 1;
- } else if ((input & 0xC0) == 0x80) {
- // invalid (continuation; can't be first char)
- // return 0xFFFE;
- return 0;
- } else if ((input & 0xE0) == 0xC0) {
- // 2 bytes
- uint8_t input2 = peek_input(n_start_offset + 1);
- if ((input2 & 0xC0) != 0x80)
- // return 0xFFFE;
- return 0;
-
- // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
- // return output;
- return 2;
- } else if ((input & 0xF0) == 0xE0) {
- // 3 bytes
- uint8_t input2 = peek_input(n_start_offset + 1);
- if ((input2 & 0xC0) != 0x80)
- // return 0xFFFE;
- return 0;
-
- uint8_t input3 = peek_input(n_start_offset + 2);
- if ((input3 & 0xC0) != 0x80)
- // return 0xFFFE;
- return 0;
-
- /*uint32_t output
- = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) << 0);
- return output;*/
- return 3;
- } else if ((input & 0xF8) == 0xF0) {
- // 4 bytes
- uint8_t input2 = peek_input(n_start_offset + 1);
- if ((input2 & 0xC0) != 0x80)
- // return 0xFFFE;
- return 0;
-
- uint8_t input3 = peek_input(n_start_offset + 2);
- if ((input3 & 0xC0) != 0x80)
- // return 0xFFFE;
- return 0;
-
- uint8_t input4 = peek_input(n_start_offset + 3);
- if ((input4 & 0xC0) != 0x80)
- // return 0xFFFE;
- return 0;
-
- /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
- | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
- return output;*/
- return 4;
- } else {
- rust_error_at(get_current_location(), "invalid UTF-8 (too long)");
- return 0;
- }
+ else if ((input & 0xC0) == 0x80)
+ {
+ // invalid (continuation; can't be first char)
+ return {0xFFFE};
+ }
+ else if ((input & 0xE0) == 0xC0)
+ {
+ // 2 bytes
+ uint8_t input2 = peek_input (1);
+ if ((input2 & 0xC0) != 0x80)
+ return {0xFFFE};
+
+ uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+ return {output};
+ }
+ else if ((input & 0xF0) == 0xE0)
+ {
+ // 3 bytes
+ uint8_t input2 = peek_input (1);
+ if ((input2 & 0xC0) != 0x80)
+ return {0xFFFE};
+
+ uint8_t input3 = peek_input (2);
+ if ((input3 & 0xC0) != 0x80)
+ return {0xFFFE};
+
+ uint32_t output = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6)
+ | ((input3 & 0x3F) << 0);
+ return {output};
}
+ else if ((input & 0xF8) == 0xF0)
+ {
+ // 4 bytes
+ uint8_t input2 = peek_input (1);
+ if ((input2 & 0xC0) != 0x80)
+ return {0xFFFE};
+
+ uint8_t input3 = peek_input (2);
+ if ((input3 & 0xC0) != 0x80)
+ return {0xFFFE};
+
+ uint8_t input4 = peek_input (3);
+ if ((input4 & 0xC0) != 0x80)
+ return {0xFFFE};
+
+ uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+ | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
+ return {output};
+ }
+ else
+ {
+ rust_error_at (get_current_location (), "invalid UTF-8 (too long)");
+ return {0xFFFE};
+ }
+}
- // peeks the codepoint input at n codepoints ahead of current codepoint - try not to use
- Codepoint Lexer::test_peek_codepoint_input(int n) {
- int totalOffset = 0;
+void
+Lexer::test_skip_codepoint_input ()
+{
+ int toSkip = test_get_input_codepoint_length ();
+ gcc_assert (toSkip >= 1);
- // add up all offsets into total offset? does this do what I want?
- for (int i = 0; i < n; i++) {
- totalOffset += test_get_input_codepoint_n_length(totalOffset);
- }
- // issues: this would have (at least) O(n) lookup time, not O(1) like the rest?
-
- // TODO: implement if still needed
-
- // error out of function as it is not implemented
- gcc_assert(1 == 0);
- return { 0 };
- /*
- uint8_t input = peek_input();
-
- if (input < 128) {
- // ascii -- 1 byte
- return input;
- } else if ((input & 0xC0) == 0x80) {
- // invalid (continuation; can't be first char)
- return 0xFFFE;
- } else if ((input & 0xE0) == 0xC0) {
- // 2 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return 0xFFFE;
-
- uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
- return output;
- } else if ((input & 0xF0) == 0xE0) {
- // 3 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return 0xFFFE;
-
- uint8_t input3 = peek_input(2);
- if ((input3 & 0xC0) != 0x80)
- return 0xFFFE;
-
- uint32_t output
- = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) << 0);
- return output;
- } else if ((input & 0xF8) == 0xF0) {
- // 4 bytes
- uint8_t input2 = peek_input(1);
- if ((input2 & 0xC0) != 0x80)
- return 0xFFFE;
-
- uint8_t input3 = peek_input(2);
- if ((input3 & 0xC0) != 0x80)
- return 0xFFFE;
-
- uint8_t input4 = peek_input(3);
- if ((input4 & 0xC0) != 0x80)
- return 0xFFFE;
-
- uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
- | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
- return output;
- } else {
- rust_error_at(get_current_location(), "invalid UTF-8 (too long)");
- return 0xFFFE;
- }*/
+ skip_input (toSkip - 1);
+}
+
+int
+Lexer::test_get_input_codepoint_n_length (int n_start_offset)
+{
+ uint8_t input = peek_input (n_start_offset);
+
+ if (input < 128)
+ {
+ // ascii -- 1 byte
+ // return input;
+ return 1;
+ }
+ else if ((input & 0xC0) == 0x80)
+ {
+ // invalid (continuation; can't be first char)
+ // return 0xFFFE;
+ return 0;
+ }
+ else if ((input & 0xE0) == 0xC0)
+ {
+ // 2 bytes
+ uint8_t input2 = peek_input (n_start_offset + 1);
+ if ((input2 & 0xC0) != 0x80)
+ // return 0xFFFE;
+ return 0;
+
+ // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+ // return output;
+ return 2;
+ }
+ else if ((input & 0xF0) == 0xE0)
+ {
+ // 3 bytes
+ uint8_t input2 = peek_input (n_start_offset + 1);
+ if ((input2 & 0xC0) != 0x80)
+ // return 0xFFFE;
+ return 0;
+
+ uint8_t input3 = peek_input (n_start_offset + 2);
+ if ((input3 & 0xC0) != 0x80)
+ // return 0xFFFE;
+ return 0;
+
+ /*uint32_t output
+ = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) <<
+ 0); return output;*/
+ return 3;
+ }
+ else if ((input & 0xF8) == 0xF0)
+ {
+ // 4 bytes
+ uint8_t input2 = peek_input (n_start_offset + 1);
+ if ((input2 & 0xC0) != 0x80)
+ // return 0xFFFE;
+ return 0;
+
+ uint8_t input3 = peek_input (n_start_offset + 2);
+ if ((input3 & 0xC0) != 0x80)
+ // return 0xFFFE;
+ return 0;
+
+ uint8_t input4 = peek_input (n_start_offset + 3);
+ if ((input4 & 0xC0) != 0x80)
+ // return 0xFFFE;
+ return 0;
+
+ /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+ | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
+ return output;*/
+ return 4;
+ }
+ else
+ {
+ rust_error_at (get_current_location (), "invalid UTF-8 (too long)");
+ return 0;
+ }
+}
+
+// peeks the codepoint input at n codepoints ahead of current codepoint - try
+// not to use
+Codepoint
+Lexer::test_peek_codepoint_input (int n)
+{
+ int totalOffset = 0;
+
+ // add up all offsets into total offset? does this do what I want?
+ for (int i = 0; i < n; i++)
+ {
+ totalOffset += test_get_input_codepoint_n_length (totalOffset);
}
+ // issues: this would have (at least) O(n) lookup time, not O(1) like the
+ // rest?
+
+ // TODO: implement if still needed
+
+ // error out of function as it is not implemented
+ gcc_assert (1 == 0);
+ return {0};
+ /*
+ uint8_t input = peek_input();
+
+ if (input < 128) {
+ // ascii -- 1 byte
+ return input;
+ } else if ((input & 0xC0) == 0x80) {
+ // invalid (continuation; can't be first char)
+ return 0xFFFE;
+ } else if ((input & 0xE0) == 0xC0) {
+ // 2 bytes
+ uint8_t input2 = peek_input(1);
+ if ((input2 & 0xC0) != 0x80)
+ return 0xFFFE;
+
+ uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+ return output;
+ } else if ((input & 0xF0) == 0xE0) {
+ // 3 bytes
+ uint8_t input2 = peek_input(1);
+ if ((input2 & 0xC0) != 0x80)
+ return 0xFFFE;
+
+ uint8_t input3 = peek_input(2);
+ if ((input3 & 0xC0) != 0x80)
+ return 0xFFFE;
+
+ uint32_t output
+ = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 &
+ 0x3F) << 0); return output; } else if ((input & 0xF8) == 0xF0) {
+ // 4 bytes
+ uint8_t input2 = peek_input(1);
+ if ((input2 & 0xC0) != 0x80)
+ return 0xFFFE;
+
+ uint8_t input3 = peek_input(2);
+ if ((input3 & 0xC0) != 0x80)
+ return 0xFFFE;
+
+ uint8_t input4 = peek_input(3);
+ if ((input4 & 0xC0) != 0x80)
+ return 0xFFFE;
+
+ uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+ | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) <<
+ 0); return output; } else { rust_error_at(get_current_location(), "invalid
+ UTF-8 (too long)"); return 0xFFFE;
+ }*/
}
+} // namespace Rust
diff --git a/gcc/rust/parse/rust-parse.cc b/gcc/rust/parse/rust-parse.cc
index cef28f7..4d15ac7 100644
--- a/gcc/rust/parse/rust-parse.cc
+++ b/gcc/rust/parse/rust-parse.cc
@@ -28,87 +28,89 @@
#include <algorithm> // for std::find
namespace Rust {
- // Left binding powers of operations.
- enum binding_powers {
- // Highest priority
- LBP_HIGHEST = 100,
+// Left binding powers of operations.
+enum binding_powers
+{
+ // Highest priority
+ LBP_HIGHEST = 100,
- LBP_PATH = 95,
+ LBP_PATH = 95,
- LBP_METHOD_CALL = 90,
+ LBP_METHOD_CALL = 90,
- LBP_FIELD_EXPR = 85,
+ LBP_FIELD_EXPR = 85,
- // LBP_DOT = 80, /* method call and field expr have different precedence now */
+ // LBP_DOT = 80, /* method call and field expr have different precedence now
+ // */
- LBP_FUNCTION_CALL = 80,
- LBP_ARRAY_REF = LBP_FUNCTION_CALL,
+ LBP_FUNCTION_CALL = 80,
+ LBP_ARRAY_REF = LBP_FUNCTION_CALL,
- LBP_QUESTION_MARK = 75, // unary postfix - counts as left
+ 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_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_AS = 65,
- LBP_MUL = 60,
- LBP_DIV = LBP_MUL,
- LBP_MOD = LBP_MUL,
+ LBP_MUL = 60,
+ LBP_DIV = LBP_MUL,
+ LBP_MOD = LBP_MUL,
- LBP_PLUS = 55,
- LBP_MINUS = LBP_PLUS,
+ LBP_PLUS = 55,
+ LBP_MINUS = LBP_PLUS,
- LBP_L_SHIFT = 50,
- LBP_R_SHIFT = LBP_L_SHIFT,
+ LBP_L_SHIFT = 50,
+ LBP_R_SHIFT = LBP_L_SHIFT,
- LBP_AMP = 45,
+ LBP_AMP = 45,
- LBP_CARET = 40,
+ LBP_CARET = 40,
- LBP_PIPE = 35,
+ 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_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_AND = 25,
- LBP_LOGICAL_OR = 20,
+ LBP_LOGICAL_OR = 20,
- LBP_DOT_DOT = 15,
- LBP_DOT_DOT_EQ = LBP_DOT_DOT,
+ 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,
+ // 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
+ // 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,
+ 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
@@ -116,11237 +118,13580 @@ namespace Rust {
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);
+ // 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 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();
+// 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;
+ }
- // parse items
- ::std::vector< ::std::unique_ptr<AST::Item> > items;
+ // 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> ();
+ }
- 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;
- }
+ params.push_back (::std::move (param));
- items.push_back(::std::move(item));
+ t = lexer.peek_token ();
+ }
+
+ return params;
+}
- t = lexer.peek_token();
- }
+/* 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 ();
- return AST::Crate(::std::move(items), ::std::move(inner_attrs), has_utf8bom, has_shebang);
+ // create error function param if it doesn't exist
+ if (param_pattern == NULL)
+ {
+ // skip after something
+ return AST::FunctionParam::create_error ();
}
- // Parse a contiguous block of inner attributes.
- ::std::vector<AST::Attribute> Parser::parse_inner_attributes() {
- ::std::vector<AST::Attribute> inner_attributes;
+ if (!skip_token (COLON))
+ {
+ // skip after something
+ return AST::FunctionParam::create_error ();
+ }
- while (lexer.peek_token()->get_id() == HASH) {
- AST::Attribute inner_attr = parse_inner_attribute();
+ ::std::unique_ptr<AST::Type> param_type = parse_type ();
+ if (param_type == NULL)
+ {
+ // skip?
+ return AST::FunctionParam::create_error ();
+ }
- // 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 AST::FunctionParam (::std::move (param_pattern),
+ ::std::move (param_type), locus);
+}
- return inner_attributes;
+/* 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 ();
- // Parse a single inner attribute.
- AST::Attribute Parser::parse_inner_attribute() {
- if (lexer.peek_token()->get_id() != HASH)
- return AST::Attribute::create_empty();
+ ::std::unique_ptr<AST::Type> type = parse_type ();
- lexer.skip_token();
+ 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));
+}
- if (lexer.peek_token()->get_id() != EXCLAM)
- return AST::Attribute::create_empty();
+// 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 ();
+ }
+}
- lexer.skip_token();
+// 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 (lexer.peek_token()->get_id() != LEFT_SQUARE)
- return AST::Attribute::create_empty();
+ if (!skip_token (COLON))
+ {
+ // TODO: skip after somewhere
+ return NULL;
+ }
- lexer.skip_token();
+ ::std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds ();
- AST::Attribute actual_attribute = parse_attribute_body();
+ return ::std::unique_ptr<AST::LifetimeWhereClauseItem> (
+ new AST::LifetimeWhereClauseItem (::std::move (lifetime),
+ ::std::move (lifetime_bounds)));
+}
- if (lexer.peek_token()->get_id() != RIGHT_SQUARE)
- return AST::Attribute::create_empty();
+// 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 ();
+ }
- lexer.skip_token();
+ ::std::unique_ptr<AST::Type> type = parse_type ();
+ if (type == NULL)
+ {
+ return NULL;
+ }
- return actual_attribute;
+ if (!skip_token (COLON))
+ {
+ // TODO: skip after somewhere
+ return NULL;
}
- // Parses the body of an attribute (inner or outer).
- AST::Attribute Parser::parse_attribute_body() {
- Location locus = lexer.peek_token()->get_locus();
+ // parse type param bounds if they exist
+ ::std::vector< ::std::unique_ptr<AST::TypeParamBound> > type_param_bounds
+ = parse_type_param_bounds ();
- 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");
+ 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;
+ }
- // Skip past potential further info in attribute (i.e. attr_input)
- skip_after_end_attribute();
- return AST::Attribute::create_empty();
- }
+ if (!skip_token (LEFT_ANGLE))
+ {
+ // skip after somewhere?
+ return params;
+ }
- ::std::unique_ptr<AST::AttrInput> attr_input = parse_attr_input();
- // AttrInput is allowed to be null, so no checks here
+ params = parse_lifetime_params_objs ();
- return AST::Attribute(::std::move(attr_path), ::std::move(attr_input), locus);
+ if (!skip_generics_right_angle ())
+ {
+ // skip after somewhere?
+ return params;
}
- // Parses a SimplePath AST node
- AST::SimplePath Parser::parse_simple_path() {
- bool has_opening_scope_resolution = false;
- Location locus = Linemap::unknown_location();
+ 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;
+ }
- // 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;
+ type_param_bounds.push_back (::std::move (initial_bound));
- locus = lexer.peek_token()->get_locus();
+ // 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 ();
- 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;
+ }
- // Parse single required simple path segment
- AST::SimplePathSegment segment = parse_simple_path_segment();
+ type_param_bounds.push_back (::std::move (bound));
+ }
- // get location if not gotten already
- if (locus == Linemap::unknown_location()) {
- locus = segment.get_locus();
- }
+ return type_param_bounds;
+}
- ::std::vector<AST::SimplePathSegment> segments;
+/* 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;
+ }
+}
- // Return empty vector if first, actually required segment is an error
- if (segment.is_error()) {
- return AST::SimplePath::create_empty();
- }
+// 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;
- segments.push_back(segment);
+ Location locus = lexer.peek_token ()->get_locus ();
- // Parse all other simple path segments
- while (lexer.peek_token()->get_id() == SCOPE_RESOLUTION) {
- // Skip scope resolution operator
- lexer.skip_token();
+ // handle trait bound being in parentheses
+ if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+ {
+ has_parens = true;
+ lexer.skip_token ();
+ }
- AST::SimplePathSegment new_segment = parse_simple_path_segment();
+ // handle having question mark (optional)
+ if (lexer.peek_token ()->get_id () == QUESTION_MARK)
+ {
+ has_question_mark = true;
+ lexer.skip_token ();
+ }
- // Return path as currently constructed if segment in error state.
- if (new_segment.is_error()) {
- break;
- }
- segments.push_back(new_segment);
- }
+ // 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 ();
+ }
- // 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());
- }
- }
+ // handle TypePath
+ AST::TypePath type_path = parse_type_path ();
- return AST::SimplePath(::std::move(segments), has_opening_scope_resolution, locus);
+ // handle closing parentheses
+ if (has_parens)
+ {
+ if (!skip_token (RIGHT_PAREN))
+ {
+ return NULL;
+ }
}
- // 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 ::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 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 lifetime_bounds;
+}
- return type;
+// 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 ();
- /* 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();
- }
+ ::std::string lifetime_ident = lifetime_tok->get_str ();
- lexer.skip_token();
+ 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);
+ }
+}
- // 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;
+// 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);
- // 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();
+ // 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 ();
- if (where_clause_item == NULL) {
- rust_error_at(t->get_locus(), "failed to parse where clause item");
- return AST::WhereClause::create_empty();
- }
+ // parse generic params, which may not exist
+ ::std::vector< ::std::unique_ptr<AST::GenericParam> > generic_params
+ = parse_generic_params_in_angles ();
- where_clause_items.push_back(::std::move(where_clause_item));
+ // parse where clause, which may not exist
+ AST::WhereClause where_clause = parse_where_clause ();
- // also skip comma if it exists
- if (lexer.peek_token()->get_id() != COMMA) {
- break;
- }
- lexer.skip_token();
+ if (!skip_token (EQUAL))
+ {
+ skip_after_semicolon ();
+ return NULL;
+ }
- t = lexer.peek_token();
- }
+ ::std::unique_ptr<AST::Type> type_to_alias = parse_type ();
- return AST::WhereClause(::std::move(where_clause_items));
+ if (!skip_token (SEMICOLON))
+ {
+ // should be skipping past this, not the next line
+ return NULL;
}
- // 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();
+ 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));
+}
- if (t->get_id() == LIFETIME) {
- return parse_lifetime_where_clause_item();
- } else {
- return parse_type_bound_where_clause_item();
- }
+// 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;
- // 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;
- }
+ AST::StructField initial_field = parse_struct_field ();
- if (!skip_token(COLON)) {
- // TODO: skip after somewhere
- return NULL;
- }
+ // Return empty field list if no field there
+ if (initial_field.is_error ())
+ {
+ return fields;
+ }
- ::std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds();
+ fields.push_back (::std::move (initial_field));
- 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;
- }
+ // 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;
+ }
- 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();
+ // skip comma if applies
+ lexer.skip_token ();
- AST::StructField field = parse_struct_field();
+ 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?
+ if (!field.is_error ())
+ {
+ fields.push_back (::std::move (field));
+ }
+ else
+ {
+ // this would occur with a trailing comma, which is allowed
+ break;
+ }
}
- // 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();
+ 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 ();
- 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));
+ // Return empty field list if no field there
+ if (initial_field.is_error ())
+ {
+ return fields;
}
- // Parses tuple fields in tuple/tuple struct declarations.
- ::std::vector<AST::TupleField> Parser::parse_tuple_fields() {
- ::std::vector<AST::TupleField> fields;
+ fields.push_back (::std::move (initial_field));
- AST::TupleField initial_field = parse_tuple_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 ();
- // Return empty field list if no field there
- if (initial_field.is_error()) {
- return fields;
- }
+ // break out due to right paren if it exists
+ if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+ {
+ break;
+ }
- fields.push_back(::std::move(initial_field));
+ 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> ();
+ }
- // 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();
+ fields.push_back (::std::move (field));
- // break out due to right paren if it exists
- if (lexer.peek_token()->get_id() == RIGHT_PAREN) {
- break;
- }
+ 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));
+}
- 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>();
- }
+// 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);
- fields.push_back(::std::move(field));
+ // parse enum name
+ const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
+ Identifier enum_name = enum_name_tok->get_str ();
- t = lexer.peek_token();
- }
+ // 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 ();
- return fields;
+ // parse where clause if it exists
+ AST::WhereClause where_clause = parse_where_clause ();
- // TODO: this shares basically all code with function params and struct fields - templates?
+ if (!skip_token (LEFT_CURLY))
+ {
+ skip_after_end_block ();
+ return NULL;
}
- // 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 actual enum variant definitions
+ ::std::vector< ::std::unique_ptr<AST::EnumItem> > enum_items
+ = parse_enum_items ();
- // parse visibility if it exists
- AST::Visibility vis = parse_visibility();
+ 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));
+}
- // 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();
- }
+// 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;
- return AST::TupleField(::std::move(field_type), ::std::move(vis), ::std::move(outer_attrs));
+ ::std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
+
+ // Return empty item list if no field there
+ if (initial_item == NULL)
+ {
+ return items;
}
- // 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();
+ 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 ();
- 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;
- }
+ ::std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
- 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;
- }
-
- // 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::StructExprTuple> 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();
+ 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?
+}
- skip_after_end_block();
- }
- // otherwise, do nothing as EOF
+// 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));
+}
- // 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::StructExprTuple> 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();
-
- return ::std::unique_ptr<AST::StructExprTuple>(new AST::StructExprTuple(::std::move(path),
- ::std::move(exprs), ::std::move(inner_attrs), ::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());
+// 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);
- if (tok->get_id() == Rust::END_OF_FILE)
- break;
+ // 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 ();
- lexer.skip_token();
- tok = lexer.peek_token();
- }
+ if (!skip_token (COLON))
+ {
+ skip_after_semicolon ();
+ return NULL;
}
- // Parses crate and dumps AST to stderr, recursively.
- void Parser::debug_dump_ast_output() {
- AST::Crate crate = parse_crate();
+ // parse static item type (required)
+ ::std::unique_ptr<AST::Type> type = parse_type ();
- // print crate "as string", which then calls each item as string, etc.
- fprintf(stderr, "%s", crate.as_string().c_str());
+ 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;
+ }
+
+ // 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::StructExprTuple> 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::StructExprTuple>
+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 ();
+
+ return ::std::unique_ptr<AST::StructExprTuple> (
+ new AST::StructExprTuple (::std::move (path), ::std::move (exprs),
+ ::std::move (inner_attrs),
+ ::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 ();
+ }
+}
+
+// Parses crate and dumps AST to stderr, recursively.
+void
+Parser::debug_dump_ast_output ()
+{
+ AST::Crate crate = parse_crate ();
+
+ // print crate "as string", which then calls each item as string, etc.
+ fprintf (stderr, "%s", crate.as_string ().c_str ());
}
+} // namespace Rust