diff options
Diffstat (limited to 'gcc/rust/expand/rust-macro-builtins-helpers.cc')
-rw-r--r-- | gcc/rust/expand/rust-macro-builtins-helpers.cc | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/gcc/rust/expand/rust-macro-builtins-helpers.cc b/gcc/rust/expand/rust-macro-builtins-helpers.cc new file mode 100644 index 0000000..e9bf54f --- /dev/null +++ b/gcc/rust/expand/rust-macro-builtins-helpers.cc @@ -0,0 +1,284 @@ +// Copyright (C) 2020-2024 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-macro-builtins-helpers.h" + +namespace Rust { + +std::string +make_macro_path_str (BuiltinMacro kind) +{ + auto str = MacroBuiltin::builtins.lookup (kind); + rust_assert (str.has_value ()); + + return str.value (); +} + +std::vector<std::unique_ptr<AST::MacroInvocation>> +check_for_eager_invocations ( + std::vector<std::unique_ptr<AST::Expr>> &expressions) +{ + std::vector<std::unique_ptr<AST::MacroInvocation>> pending; + + for (auto &expr : expressions) + if (expr->get_ast_kind () == AST::Kind::MACRO_INVOCATION) + pending.emplace_back (std::unique_ptr<AST::MacroInvocation> ( + static_cast<AST::MacroInvocation *> (expr->clone_expr ().release ()))); + + return pending; +} + +// +// Shorthand function for creating unique_ptr tokens +// +std::unique_ptr<AST::Token> +make_token (const TokenPtr tok) +{ + return std::unique_ptr<AST::Token> (new AST::Token (tok)); +} + +std::unique_ptr<AST::Expr> +make_string (location_t locus, std::string value) +{ + return std::unique_ptr<AST::Expr> ( + new AST::LiteralExpr (value, AST::Literal::STRING, + PrimitiveCoreType::CORETYPE_STR, {}, locus)); +} + +// TODO: Is this correct? +AST::Fragment +make_eager_builtin_invocation ( + BuiltinMacro kind, location_t locus, AST::DelimTokenTree arguments, + std::vector<std::unique_ptr<AST::MacroInvocation>> &&pending_invocations) +{ + auto path_str = make_macro_path_str (kind); + + std::unique_ptr<AST::Expr> node = AST::MacroInvocation::Builtin ( + kind, + AST::MacroInvocData (AST::SimplePath ( + {AST::SimplePathSegment (path_str, locus)}), + std::move (arguments)), + {}, locus, std::move (pending_invocations)); + + return AST::Fragment ({AST::SingleASTNode (std::move (node))}, + arguments.to_token_stream ()); +} + +/* Match the end token of a macro given the start delimiter of the macro */ +TokenId +macro_end_token (AST::DelimTokenTree &invoc_token_tree, + Parser<MacroInvocLexer> &parser) +{ + auto last_token_id = TokenId::RIGHT_CURLY; + switch (invoc_token_tree.get_delim_type ()) + { + case AST::DelimType::PARENS: + last_token_id = TokenId::RIGHT_PAREN; + rust_assert (parser.skip_token (LEFT_PAREN)); + break; + + case AST::DelimType::CURLY: + rust_assert (parser.skip_token (LEFT_CURLY)); + break; + + case AST::DelimType::SQUARE: + last_token_id = TokenId::RIGHT_SQUARE; + rust_assert (parser.skip_token (LEFT_SQUARE)); + break; + } + + return last_token_id; +} + +// Expand and then extract a string literal from the macro +std::unique_ptr<AST::LiteralExpr> +try_extract_string_literal_from_fragment (const location_t &parent_locus, + std::unique_ptr<AST::Expr> &node) +{ + auto maybe_lit = static_cast<AST::LiteralExpr *> (node.get ()); + if (!node || !node->is_literal () + || maybe_lit->get_lit_type () != AST::Literal::STRING) + { + rust_error_at (parent_locus, "argument must be a string literal"); + if (node) + rust_inform (node->get_locus (), "expanded from here"); + return nullptr; + } + return std::unique_ptr<AST::LiteralExpr> ( + static_cast<AST::LiteralExpr *> (node->clone_expr ().release ())); +} + +std::vector<std::unique_ptr<AST::Expr>> +try_expand_many_expr (Parser<MacroInvocLexer> &parser, + const TokenId last_token_id, MacroExpander *expander, + bool &has_error) +{ + auto restrictions = Rust::ParseRestrictions (); + // stop parsing when encountered a braces/brackets + restrictions.expr_can_be_null = true; + // we can't use std::optional, so... + auto result = std::vector<std::unique_ptr<AST::Expr>> (); + auto empty_expr = std::vector<std::unique_ptr<AST::Expr>> (); + + auto first_token = parser.peek_current_token ()->get_id (); + if (first_token == COMMA) + { + rust_error_at (parser.peek_current_token ()->get_locus (), + "expected expression, found %<,%>"); + has_error = true; + return empty_expr; + } + + while (parser.peek_current_token ()->get_id () != last_token_id + && parser.peek_current_token ()->get_id () != END_OF_FILE) + { + auto expr = parser.parse_expr (AST::AttrVec (), restrictions); + // something must be so wrong that the expression could not be parsed + rust_assert (expr); + result.push_back (std::move (expr)); + + auto next_token = parser.peek_current_token (); + if (!parser.skip_token (COMMA) && next_token->get_id () != last_token_id) + { + rust_error_at (next_token->get_locus (), "expected token: %<,%>"); + // TODO: is this recoverable? to avoid crashing the parser in the next + // fragment we have to exit early here + has_error = true; + return empty_expr; + } + } + + return result; +} + +// Parse a single string literal from the given delimited token tree, +// and return the LiteralExpr for it. Allow for an optional trailing comma, +// but otherwise enforce that these are the only tokens. +// FIXME(Arthur): This function needs a rework - it should not emit errors, it +// should probably be smaller +std::unique_ptr<AST::Expr> +parse_single_string_literal (BuiltinMacro kind, + AST::DelimTokenTree &invoc_token_tree, + location_t invoc_locus, MacroExpander *expander) +{ + MacroInvocLexer lex (invoc_token_tree.to_token_stream ()); + Parser<MacroInvocLexer> parser (lex); + + auto last_token_id = macro_end_token (invoc_token_tree, parser); + + std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr; + std::unique_ptr<AST::MacroInvocation> macro_invoc = nullptr; + + if (parser.peek_current_token ()->get_id () == STRING_LITERAL) + { + lit_expr = parser.parse_literal_expr (); + parser.maybe_skip_token (COMMA); + if (parser.peek_current_token ()->get_id () != last_token_id) + { + lit_expr = nullptr; + rust_error_at (invoc_locus, "macro takes 1 argument"); + } + } + else if (parser.peek_current_token ()->get_id () == last_token_id) + rust_error_at (invoc_locus, "macro takes 1 argument"); + else + { + macro_invoc = parser.parse_macro_invocation (AST::AttrVec ()); + + parser.maybe_skip_token (COMMA); + if (parser.peek_current_token ()->get_id () != last_token_id) + { + lit_expr = nullptr; + rust_error_at (invoc_locus, "macro takes 1 argument"); + } + + if (macro_invoc != nullptr) + { + auto path_str = make_macro_path_str (kind); + + auto pending_invocations + = std::vector<std::unique_ptr<AST::MacroInvocation>> (); + + pending_invocations.push_back (std::move (macro_invoc)); + + return AST::MacroInvocation::Builtin ( + kind, + AST::MacroInvocData (AST::SimplePath ({AST::SimplePathSegment ( + path_str, invoc_locus)}), + std::move (invoc_token_tree)), + {}, invoc_locus, std::move (pending_invocations)); + } + else + { + rust_error_at (invoc_locus, "argument must be a string literal or a " + "macro which expands to a string"); + } + } + + parser.skip_token (last_token_id); + + return std::unique_ptr<AST::Expr> (std::move (lit_expr)); +} + +/* Treat PATH as a path relative to the source file currently being + compiled, and return the absolute path for it. */ +std::string +source_relative_path (std::string path, location_t locus) +{ + std::string compile_fname = LOCATION_FILE (locus); + + auto dir_separator_pos = compile_fname.rfind (file_separator); + + /* If there is no file_separator in the path, use current dir ('.'). */ + std::string dirname; + if (dir_separator_pos == std::string::npos) + dirname = std::string (".") + file_separator; + else + dirname = compile_fname.substr (0, dir_separator_pos) + file_separator; + + return dirname + path; +} + +/* Read the full contents of the file FILENAME and return them in a vector. + FIXME: platform specific. */ +tl::optional<std::vector<uint8_t>> +load_file_bytes (location_t invoc_locus, const char *filename) +{ + RAIIFile file_wrap (filename); + if (file_wrap.get_raw () == nullptr) + { + rust_error_at (invoc_locus, "cannot open filename %s: %m", filename); + return tl::nullopt; + } + + FILE *f = file_wrap.get_raw (); + fseek (f, 0L, SEEK_END); + long fsize = ftell (f); + fseek (f, 0L, SEEK_SET); + + std::vector<uint8_t> buf (fsize); + + if (fsize > 0 && fread (&buf[0], fsize, 1, f) != 1) + { + rust_error_at (invoc_locus, "error reading file %s: %m", filename); + return std::vector<uint8_t> (); + } + + return buf; +} +} // namespace Rust
\ No newline at end of file |