aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/expand/rust-macro-builtins.cc
diff options
context:
space:
mode:
authorjjasmine <tanghocle456@gmail.com>2024-02-25 03:10:37 -0800
committerCohenArthur <arthur.cohen@embecosm.com>2024-03-21 23:07:03 +0000
commit2f334bb12e3ba947714771408b9d49d398abb5df (patch)
tree844658c94ccd0ebe9b2c5392a600e71740939135 /gcc/rust/expand/rust-macro-builtins.cc
parentb4c3a6ca4f83733f4122ec1e56b5bc69b9fab1b4 (diff)
downloadgcc-2f334bb12e3ba947714771408b9d49d398abb5df.zip
gcc-2f334bb12e3ba947714771408b9d49d398abb5df.tar.gz
gcc-2f334bb12e3ba947714771408b9d49d398abb5df.tar.bz2
Split up rust-macro-builtins.cc
Fixes issue #2855 gcc/rust/ChangeLog: * Make-lang.in: add new .o builds for new .cc files * expand/rust-cfg-strip.h (RUST_CFG_STRIP_H): Add include guards for rust-cfg-strip * expand/rust-macro-builtins.cc (make_macro_path_str): moved to new respective files (make_token): moved to new respective files (make_string): moved to new respective files (macro_end_token): moved to new respective files (try_extract_string_literal_from_fragment): moved to new respective files (try_expand_many_expr): moved to new respective files (parse_single_string_literal): moved to new respective files (source_relative_path): moved to new respective files (load_file_bytes): moved to new respective files (MacroBuiltin::assert_handler): moved to new respective files (MacroBuiltin::file_handler): moved to new respective files (MacroBuiltin::column_handler): moved to new respective files (MacroBuiltin::include_bytes_handler): moved to new respective files (MacroBuiltin::include_str_handler): moved to new respective files (MacroBuiltin::compile_error_handler): moved to new respective files (MacroBuiltin::concat_handler): moved to new respective files (MacroBuiltin::env_handler): moved to new respective files (MacroBuiltin::cfg_handler): moved to new respective files (MacroBuiltin::include_handler): moved to new respective files (MacroBuiltin::line_handler): moved to new respective files (MacroBuiltin::stringify_handler): moved to new respective files (struct FormatArgsInput): moved to new respective files (struct FormatArgsParseError): moved to new respective files (format_args_parse_arguments): moved to new respective files (MacroBuiltin::format_args_handler): moved to new respective files * expand/rust-macro-builtins.h (builtin_macro_from_string): merge tl::optional from master * expand/rust-macro-builtins-asm.cc: New file. * expand/rust-macro-builtins-format-args.cc: New file. * expand/rust-macro-builtins-helpers.cc: New file. * expand/rust-macro-builtins-helpers.h: New file. * expand/rust-macro-builtins-include.cc: New file. * expand/rust-macro-builtins-location.cc: New file. * expand/rust-macro-builtins-log-debug.cc: New file. * expand/rust-macro-builtins-test-bench.cc: New file. * expand/rust-macro-builtins-trait.cc: New file. * expand/rust-macro-builtins-utility.cc: New file.
Diffstat (limited to 'gcc/rust/expand/rust-macro-builtins.cc')
-rw-r--r--gcc/rust/expand/rust-macro-builtins.cc981
1 files changed, 1 insertions, 980 deletions
diff --git a/gcc/rust/expand/rust-macro-builtins.cc b/gcc/rust/expand/rust-macro-builtins.cc
index a33a577..bc5bc94 100644
--- a/gcc/rust/expand/rust-macro-builtins.cc
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -40,6 +40,7 @@
#include "rust-fmt.h"
#include "rust-token.h"
+#include "rust-macro-builtins-helpers.h"
namespace Rust {
const BiMap<std::string, BuiltinMacro> MacroBuiltin::builtins = {{
@@ -149,986 +150,6 @@ builtin_macro_from_string (const std::string &identifier)
return macro;
}
-namespace {
-std::string
-make_macro_path_str (BuiltinMacro kind)
-{
- auto str = MacroBuiltin::builtins.lookup (kind);
- rust_assert (str.has_value ());
-
- return str.value ();
-}
-
-static 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
- */
-static 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?
-static 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 */
-static inline 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 */
-static 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 ()));
-}
-
-static 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
-
-tl::optional<AST::Fragment>
-MacroBuiltin::assert_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- rust_debug ("assert!() called");
-
- return AST::Fragment::create_error ();
-}
-
-tl::optional<AST::Fragment>
-MacroBuiltin::file_handler (location_t invoc_locus, AST::MacroInvocData &)
-{
- auto current_file = LOCATION_FILE (invoc_locus);
- auto file_str = AST::SingleASTNode (make_string (invoc_locus, current_file));
- auto str_token
- = make_token (Token::make_string (invoc_locus, std::move (current_file)));
-
- return AST::Fragment ({file_str}, std::move (str_token));
-}
-
-tl::optional<AST::Fragment>
-MacroBuiltin::column_handler (location_t invoc_locus, AST::MacroInvocData &)
-{
- auto current_column = LOCATION_COLUMN (invoc_locus);
-
- auto column_tok = make_token (
- Token::make_int (invoc_locus, std::to_string (current_column)));
- auto column_no = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
- new AST::LiteralExpr (std::to_string (current_column), AST::Literal::INT,
- PrimitiveCoreType::CORETYPE_U32, {}, invoc_locus)));
-
- return AST::Fragment ({column_no}, std::move (column_tok));
-}
-
-/* Expand builtin macro include_bytes!("filename"), which includes the contents
- of the given file as reference to a byte array. Yields an expression of type
- &'static [u8; N]. */
-
-tl::optional<AST::Fragment>
-MacroBuiltin::include_bytes_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- /* Get target filename from the macro invocation, which is treated as a path
- relative to the include!-ing file (currently being compiled). */
- auto lit_expr
- = parse_single_string_literal (BuiltinMacro::IncludeBytes,
- invoc.get_delim_tok_tree (), invoc_locus,
- invoc.get_expander ());
- if (lit_expr == nullptr)
- return AST::Fragment::create_error ();
-
- rust_assert (lit_expr->is_literal ());
-
- std::string target_filename
- = source_relative_path (lit_expr->as_string (), invoc_locus);
-
- auto maybe_bytes = load_file_bytes (invoc_locus, target_filename.c_str ());
-
- if (!maybe_bytes.has_value ())
- return AST::Fragment::create_error ();
-
- std::vector<uint8_t> bytes = maybe_bytes.value ();
-
- /* Is there a more efficient way to do this? */
- std::vector<std::unique_ptr<AST::Expr>> elts;
-
- // We create the tokens for a borrow expression of a byte array, so
- // & [ <byte0>, <byte1>, ... ]
- std::vector<std::unique_ptr<AST::Token>> toks;
- toks.emplace_back (make_token (Token::make (AMP, invoc_locus)));
- toks.emplace_back (make_token (Token::make (LEFT_SQUARE, invoc_locus)));
-
- for (uint8_t b : bytes)
- {
- elts.emplace_back (
- new AST::LiteralExpr (std::string (1, (char) b), AST::Literal::BYTE,
- PrimitiveCoreType::CORETYPE_U8,
- {} /* outer_attrs */, invoc_locus));
- toks.emplace_back (make_token (Token::make_byte_char (invoc_locus, b)));
- toks.emplace_back (make_token (Token::make (COMMA, invoc_locus)));
- }
-
- toks.emplace_back (make_token (Token::make (RIGHT_SQUARE, invoc_locus)));
-
- auto elems = std::unique_ptr<AST::ArrayElems> (
- new AST::ArrayElemsValues (std::move (elts), invoc_locus));
-
- auto array = std::unique_ptr<AST::Expr> (
- new AST::ArrayExpr (std::move (elems), {}, {}, invoc_locus));
-
- auto borrow = std::unique_ptr<AST::Expr> (
- new AST::BorrowExpr (std::move (array), false, false, {}, invoc_locus));
-
- auto node = AST::SingleASTNode (std::move (borrow));
-
- return AST::Fragment ({node}, std::move (toks));
-} // namespace Rust
-
-/* Expand builtin macro include_str!("filename"), which includes the contents
- of the given file as a string. The file must be UTF-8 encoded. Yields an
- expression of type &'static str. */
-
-tl::optional<AST::Fragment>
-MacroBuiltin::include_str_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- /* Get target filename from the macro invocation, which is treated as a path
- relative to the include!-ing file (currently being compiled). */
- auto lit_expr
- = parse_single_string_literal (BuiltinMacro::IncludeStr,
- invoc.get_delim_tok_tree (), invoc_locus,
- invoc.get_expander ());
- if (lit_expr == nullptr)
- return AST::Fragment::create_error ();
-
- if (!lit_expr->is_literal ())
- {
- auto token_tree = invoc.get_delim_tok_tree ();
- return AST::Fragment ({AST::SingleASTNode (std::move (lit_expr))},
- token_tree.to_token_stream ());
- }
-
- std::string target_filename
- = source_relative_path (lit_expr->as_string (), invoc_locus);
-
- auto maybe_bytes = load_file_bytes (invoc_locus, target_filename.c_str ());
-
- if (!maybe_bytes.has_value ())
- return AST::Fragment::create_error ();
-
- std::vector<uint8_t> bytes = maybe_bytes.value ();
-
- /* FIXME: reuse lexer */
- int expect_single = 0;
- for (uint8_t b : bytes)
- {
- if (expect_single)
- {
- if ((b & 0xC0) != 0x80)
- /* character was truncated, exit with expect_single != 0 */
- break;
- expect_single--;
- }
- else if (b & 0x80)
- {
- if (b >= 0xF8)
- {
- /* more than 4 leading 1s */
- expect_single = 1;
- break;
- }
- else if (b >= 0xF0)
- {
- /* 4 leading 1s */
- expect_single = 3;
- }
- else if (b >= 0xE0)
- {
- /* 3 leading 1s */
- expect_single = 2;
- }
- else if (b >= 0xC0)
- {
- /* 2 leading 1s */
- expect_single = 1;
- }
- else
- {
- /* only 1 leading 1 */
- expect_single = 1;
- break;
- }
- }
- }
-
- std::string str;
- if (expect_single)
- rust_error_at (invoc_locus, "%s was not a valid utf-8 file",
- target_filename.c_str ());
- else
- str = std::string ((const char *) bytes.data (), bytes.size ());
-
- auto node = AST::SingleASTNode (make_string (invoc_locus, str));
- auto str_tok = make_token (Token::make_string (invoc_locus, std::move (str)));
-
- return AST::Fragment ({node}, std::move (str_tok));
-}
-
-/* Expand builtin macro compile_error!("error"), which forces a compile error
- during the compile time. */
-tl::optional<AST::Fragment>
-MacroBuiltin::compile_error_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- auto lit_expr
- = parse_single_string_literal (BuiltinMacro::CompileError,
- invoc.get_delim_tok_tree (), invoc_locus,
- invoc.get_expander ());
- if (lit_expr == nullptr)
- return AST::Fragment::create_error ();
-
- rust_assert (lit_expr->is_literal ());
-
- std::string error_string = lit_expr->as_string ();
- rust_error_at (invoc_locus, "%s", error_string.c_str ());
-
- return AST::Fragment::create_error ();
-}
-
-/* Expand builtin macro concat!(), which joins all the literal parameters
- into a string with no delimiter. */
-
-// This is a weird one. We want to do something where, if something cannot be
-// expanded yet (i.e. macro invocation?) we return the whole MacroInvocation
-// node again but expanded as much as possible.
-// Is that possible? How do we do that?
-//
-// Let's take a few examples:
-//
-// 1. concat!(1, 2, true);
-// 2. concat!(a!(), 2, true);
-// 3. concat!(concat!(1, false), 2, true);
-// 4. concat!(concat!(1, a!()), 2, true);
-//
-// 1. We simply want to return the new fragment: "12true"
-// 2. We want to return `concat!(a_expanded, 2, true)` as a fragment
-// 3. We want to return `concat!(1, false, 2, true)`
-// 4. We want to return `concat!(concat!(1, a_expanded), 2, true);
-//
-// How do we do that?
-//
-// For each (un)expanded fragment: we check if it is expanded fully
-//
-// 1. What is expanded fully?
-// 2. How to check?
-//
-// If it is expanded fully and not a literal, then we error out.
-// Otherwise we simply emplace it back and keep going.
-//
-// In the second case, we must mark that this concat invocation still has some
-// expansion to do: This allows us to return a `MacroInvocation { ... }` as an
-// AST fragment, instead of a completed string.
-//
-// This means that we must change all the `try_expand_many_*` APIs and so on to
-// return some sort of index or way to signify that we might want to reuse some
-// bits and pieces of the original token tree.
-//
-// Now, before that: How do we resolve the names used in a builtin macro
-// invocation?
-// Do we split the two passes of parsing the token tree and then expanding it?
-// Can we do that easily?
-tl::optional<AST::Fragment>
-MacroBuiltin::concat_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- auto invoc_token_tree = invoc.get_delim_tok_tree ();
- MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
- Parser<MacroInvocLexer> parser (lex);
-
- auto str = std::string ();
- bool has_error = false;
-
- auto last_token_id = macro_end_token (invoc_token_tree, parser);
-
- auto start = lex.get_offs ();
- /* NOTE: concat! could accept no argument, so we don't have any checks here */
- auto expanded_expr = try_expand_many_expr (parser, last_token_id,
- invoc.get_expander (), has_error);
- auto end = lex.get_offs ();
-
- auto tokens = lex.get_token_slice (start, end);
-
- auto pending_invocations = check_for_eager_invocations (expanded_expr);
- if (!pending_invocations.empty ())
- return make_eager_builtin_invocation (BuiltinMacro::Concat, invoc_locus,
- invoc.get_delim_tok_tree (),
- std::move (pending_invocations));
-
- for (auto &expr : expanded_expr)
- {
- if (!expr->is_literal ()
- && expr->get_ast_kind () != AST::Kind::MACRO_INVOCATION)
- {
- has_error = true;
- rust_error_at (expr->get_locus (), "expected a literal");
- // diagnostics copied from rustc
- rust_inform (expr->get_locus (),
- "only literals (like %<\"foo\"%>, %<42%> and "
- "%<3.14%>) can be passed to %<concat!()%>");
- continue;
- }
- auto *literal = static_cast<AST::LiteralExpr *> (expr.get ());
- if (literal->get_lit_type () == AST::Literal::BYTE
- || literal->get_lit_type () == AST::Literal::BYTE_STRING)
- {
- has_error = true;
- rust_error_at (expr->get_locus (),
- "cannot concatenate a byte string literal");
- continue;
- }
- str += literal->as_string ();
- }
-
- parser.skip_token (last_token_id);
-
- if (has_error)
- return AST::Fragment::create_error ();
-
- auto node = AST::SingleASTNode (make_string (invoc_locus, str));
- auto str_tok = make_token (Token::make_string (invoc_locus, std::move (str)));
-
- return AST::Fragment ({node}, std::move (str_tok));
-}
-
-/* Expand builtin macro env!(), which inspects an environment variable at
- compile time. */
-tl::optional<AST::Fragment>
-MacroBuiltin::env_handler (location_t invoc_locus, AST::MacroInvocData &invoc)
-{
- auto invoc_token_tree = invoc.get_delim_tok_tree ();
- 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> error_expr = nullptr;
- std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
- bool has_error = false;
-
- auto start = lex.get_offs ();
- auto expanded_expr = try_expand_many_expr (parser, last_token_id,
- invoc.get_expander (), has_error);
- auto end = lex.get_offs ();
-
- auto tokens = lex.get_token_slice (start, end);
-
- if (has_error)
- return AST::Fragment::create_error ();
-
- auto pending = check_for_eager_invocations (expanded_expr);
- if (!pending.empty ())
- return make_eager_builtin_invocation (BuiltinMacro::Env, invoc_locus,
- invoc_token_tree,
- std::move (pending));
-
- if (expanded_expr.size () < 1 || expanded_expr.size () > 2)
- {
- rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
- return AST::Fragment::create_error ();
- }
- if (expanded_expr.size () > 0)
- {
- if (!(lit_expr
- = try_extract_string_literal_from_fragment (invoc_locus,
- expanded_expr[0])))
- {
- return AST::Fragment::create_error ();
- }
- }
- if (expanded_expr.size () > 1)
- {
- if (!(error_expr
- = try_extract_string_literal_from_fragment (invoc_locus,
- expanded_expr[1])))
- {
- return AST::Fragment::create_error ();
- }
- }
-
- parser.skip_token (last_token_id);
-
- auto env_value = getenv (lit_expr->as_string ().c_str ());
-
- if (env_value == nullptr)
- {
- if (error_expr == nullptr)
- rust_error_at (invoc_locus, "environment variable %qs not defined",
- lit_expr->as_string ().c_str ());
- else
- rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
- return AST::Fragment::create_error ();
- }
-
- auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
- auto tok
- = make_token (Token::make_string (invoc_locus, std::move (env_value)));
-
- return AST::Fragment ({node}, std::move (tok));
-}
-
-tl::optional<AST::Fragment>
-MacroBuiltin::cfg_handler (location_t invoc_locus, AST::MacroInvocData &invoc)
-{
- // only parse if not already parsed
- if (!invoc.is_parsed ())
- {
- std::unique_ptr<AST::AttrInputMetaItemContainer> converted_input (
- invoc.get_delim_tok_tree ().parse_to_meta_item ());
-
- if (converted_input == nullptr)
- {
- rust_debug ("DEBUG: failed to parse macro to meta item");
- // TODO: do something now? is this an actual error?
- }
- else
- {
- std::vector<std::unique_ptr<AST::MetaItemInner>> meta_items (
- std::move (converted_input->get_items ()));
- invoc.set_meta_item_output (std::move (meta_items));
- }
- }
-
- /* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
- * attributes */
- if (invoc.get_meta_items ().size () != 1)
- return AST::Fragment::create_error ();
-
- bool result = invoc.get_meta_items ()[0]->check_cfg_predicate (
- Session::get_instance ());
- auto literal_exp = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
- new AST::LiteralExpr (result ? "true" : "false", AST::Literal::BOOL,
- PrimitiveCoreType::CORETYPE_BOOL, {}, invoc_locus)));
- auto tok = make_token (
- Token::make (result ? TRUE_LITERAL : FALSE_LITERAL, invoc_locus));
-
- return AST::Fragment ({literal_exp}, std::move (tok));
-}
-
-/* Expand builtin macro include!(), which includes a source file at the current
- scope compile time. */
-
-tl::optional<AST::Fragment>
-MacroBuiltin::include_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- /* Get target filename from the macro invocation, which is treated as a path
- relative to the include!-ing file (currently being compiled). */
- auto lit_expr
- = parse_single_string_literal (BuiltinMacro::Include,
- invoc.get_delim_tok_tree (), invoc_locus,
- invoc.get_expander ());
- if (lit_expr == nullptr)
- return AST::Fragment::create_error ();
-
- rust_assert (lit_expr->is_literal ());
-
- std::string filename
- = source_relative_path (lit_expr->as_string (), invoc_locus);
- auto target_filename
- = Rust::Session::get_instance ().include_extra_file (std::move (filename));
-
- RAIIFile target_file (target_filename);
- Linemap *linemap = Session::get_instance ().linemap;
-
- if (!target_file.ok ())
- {
- rust_error_at (lit_expr->get_locus (),
- "cannot open included file %qs: %m", target_filename);
- return AST::Fragment::create_error ();
- }
-
- rust_debug ("Attempting to parse included file %s", target_filename);
-
- Lexer lex (target_filename, std::move (target_file), linemap);
- Parser<Lexer> parser (lex);
-
- auto parsed_items = parser.parse_items ();
- bool has_error = !parser.get_errors ().empty ();
-
- for (const auto &error : parser.get_errors ())
- error.emit ();
-
- if (has_error)
- {
- // inform the user that the errors above are from a included file
- rust_inform (invoc_locus, "included from here");
- return AST::Fragment::create_error ();
- }
-
- std::vector<AST::SingleASTNode> nodes{};
- for (auto &item : parsed_items)
- {
- AST::SingleASTNode node (std::move (item));
- nodes.push_back (node);
- }
-
- // FIXME: This returns an empty vector of tokens and works fine, but is that
- // the expected behavior? `include` macros are a bit harder to reason about
- // since they include tokens. Furthermore, our lexer has no easy way to return
- // a slice of tokens like the MacroInvocLexer. So it gets even harder to
- // extrac tokens from here. For now, let's keep it that way and see if it
- // eventually breaks, but I don't expect it to cause many issues since the
- // list of tokens is only used when a macro invocation mixes eager
- // macro invocations and already expanded tokens. Think
- // `concat!(a!(), 15, b!())`. We need to be able to expand a!(), expand b!(),
- // and then insert the `15` token in between. In the case of `include!()`, we
- // only have one argument. So it's either going to be a macro invocation or a
- // string literal.
- return AST::Fragment (nodes, std::vector<std::unique_ptr<AST::Token>> ());
-}
-
-tl::optional<AST::Fragment>
-MacroBuiltin::line_handler (location_t invoc_locus, AST::MacroInvocData &)
-{
- auto current_line = LOCATION_LINE (invoc_locus);
-
- auto line_no = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
- new AST::LiteralExpr (std::to_string (current_line), AST::Literal::INT,
- PrimitiveCoreType::CORETYPE_U32, {}, invoc_locus)));
- auto tok
- = make_token (Token::make_int (invoc_locus, std::to_string (current_line)));
-
- return AST::Fragment ({line_no}, std::move (tok));
-}
-
-tl::optional<AST::Fragment>
-MacroBuiltin::stringify_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc)
-{
- std::string content;
- auto invoc_token_tree = invoc.get_delim_tok_tree ();
- auto tokens = invoc_token_tree.to_token_stream ();
-
- // Tokens stream includes the first and last delimiter
- // which we need to skip.
- for (auto token = tokens.cbegin () + 1; token < tokens.cend () - 1; token++)
- {
- // Rust stringify format has no garantees but the reference compiler
- // removes spaces before some tokens depending on the lexer's behavior,
- // let's mimick some of those behaviors.
- auto token_id = (*token)->get_id ();
- if (token_id != RIGHT_PAREN && token_id != EXCLAM
- && token != tokens.cbegin () + 1)
- {
- content.push_back (' ');
- }
- content += (*token)->as_string ();
- }
-
- auto node = AST::SingleASTNode (make_string (invoc_locus, content));
- auto token
- = make_token (Token::make_string (invoc_locus, std::move (content)));
- return AST::Fragment ({node}, std::move (token));
-}
-
-struct FormatArgsInput
-{
- std::string format_str;
- AST::FormatArguments args;
- // bool is_literal?
-};
-
-struct FormatArgsParseError
-{
- enum class Kind
- {
- MissingArguments
- } kind;
-};
-
-static tl::expected<FormatArgsInput, FormatArgsParseError>
-format_args_parse_arguments (AST::MacroInvocData &invoc)
-{
- MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
- Parser<MacroInvocLexer> parser (lex);
-
- // TODO: check if EOF - return that format_args!() requires at least one
- // argument
-
- auto args = AST::FormatArguments ();
- auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
- std::unique_ptr<AST::Expr> format_expr = nullptr;
-
- // TODO: Handle the case where we're not parsing a string literal (macro
- // invocation for e.g.)
- if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
- format_expr = parser.parse_literal_expr ();
-
- // TODO(Arthur): Clean this up - if we haven't parsed a string literal but a
- // macro invocation, what do we do here? return a tl::unexpected?
- auto format_str = static_cast<AST::LiteralExpr &> (*format_expr)
- .get_literal ()
- .as_string ();
-
- // TODO: Allow implicit captures ONLY if the the first arg is a string literal
- // and not a macro invocation
-
- // TODO: How to consume all of the arguments until the delimiter?
-
- // TODO: What we then want to do is as follows:
- // for each token, check if it is an identifier
- // yes? is the next token an equal sign (=)
- // yes?
- // -> if that identifier is already present in our map, error
- // out
- // -> parse an expression, return a FormatArgument::Named
- // no?
- // -> if there have been named arguments before, error out
- // (positional after named error)
- // -> parse an expression, return a FormatArgument::Normal
- while (parser.peek_current_token ()->get_id () != last_token_id)
- {
- parser.skip_token (COMMA);
-
- if (parser.peek_current_token ()->get_id () == IDENTIFIER
- && parser.peek (1)->get_id () == EQUAL)
- {
- // FIXME: This is ugly - just add a parser.parse_identifier()?
- auto ident_tok = parser.peek_current_token ();
- auto ident = Identifier (ident_tok);
-
- parser.skip_token (IDENTIFIER);
- parser.skip_token (EQUAL);
-
- auto expr = parser.parse_expr ();
-
- // TODO: Handle graciously
- if (!expr)
- rust_unreachable ();
-
- args.push (AST::FormatArgument::named (ident, std::move (expr)));
- }
- else
- {
- auto expr = parser.parse_expr ();
-
- // TODO: Handle graciously
- if (!expr)
- rust_unreachable ();
-
- args.push (AST::FormatArgument::normal (std::move (expr)));
- }
- // we need to skip commas, don't we?
- }
-
- return FormatArgsInput{std::move (format_str), std::move (args)};
-}
-
-tl::optional<AST::Fragment>
-MacroBuiltin::format_args_handler (location_t invoc_locus,
- AST::MacroInvocData &invoc,
- AST::FormatArgs::Newline nl)
-{
- auto input = format_args_parse_arguments (invoc);
-
- if (!input)
- {
- rust_error_at (invoc_locus,
- "could not parse arguments to %<format_args!()%>");
- return tl::nullopt;
- }
-
- // TODO(Arthur): We need to handle this
- // // if it is not a literal, it's an eager macro invocation - return it
- // if (!fmt_expr->is_literal ())
- // {
- // auto token_tree = invoc.get_delim_tok_tree ();
- // return AST::Fragment ({AST::SingleASTNode (std::move (fmt_expr))},
- // token_tree.to_token_stream ());
- // }
-
- // TODO(Arthur): Handle this as well - raw strings are special for the
- // format_args parser auto fmt_str = static_cast<AST::LiteralExpr &>
- // (*fmt_arg.get ()); Switch on the format string to know if the string is raw
- // or cooked switch (fmt_str.get_lit_type ())
- // {
- // // case AST::Literal::RAW_STRING:
- // case AST::Literal::STRING:
- // break;
- // case AST::Literal::CHAR:
- // case AST::Literal::BYTE:
- // case AST::Literal::BYTE_STRING:
- // case AST::Literal::INT:
- // case AST::Literal::FLOAT:
- // case AST::Literal::BOOL:
- // case AST::Literal::ERROR:
- // rust_unreachable ();
- // }
-
- bool append_newline = nl == AST::FormatArgs::Newline::Yes;
-
- auto fmt_str = std::move (input->format_str);
- if (append_newline)
- fmt_str += '\n';
-
- auto pieces = Fmt::Pieces::collect (fmt_str, append_newline);
-
- // TODO:
- // do the transformation into an AST::FormatArgs node
- // return that
- // expand it during lowering
-
- // TODO: we now need to take care of creating `unfinished_literal`? this is
- // for creating the `template`
-
- auto fmt_args_node = AST::FormatArgs (invoc_locus, std::move (pieces),
- std::move (input->args));
-
- auto expanded
- = Fmt::expand_format_args (fmt_args_node,
- invoc.get_delim_tok_tree ().to_token_stream ());
-
- if (!expanded.has_value ())
- return AST::Fragment::create_error ();
-
- return *expanded;
-
- // auto node = std::unique_ptr<AST::Expr> (fmt_args_node);
- // auto single_node = AST::SingleASTNode (std::move (node));
-
- // return AST::Fragment ({std::move (single_node)},
- // invoc.get_delim_tok_tree ().to_token_stream ());
-}
-
tl::optional<AST::Fragment>
MacroBuiltin::sorry (location_t invoc_locus, AST::MacroInvocData &invoc)
{