libcpp, c, middle-end: Optimize initializers using #embed in C
[official-gcc.git] / gcc / rust / expand / rust-macro-builtins-format-args.cc
blobf42a07ff205dfd3f3b25cd17d4481b0300431117
1 // Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 // This file is part of GCC.
5 // GCC is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 3, or (at your option) any later
8 // version.
10 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 // for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with GCC; see the file COPYING3. If not see
17 // <http://www.gnu.org/licenses/>.
18 #include "rust-macro-builtins-helpers.h"
19 #include "rust-expand-format-args.h"
21 namespace Rust {
23 struct FormatArgsInput
25 std::string format_str;
26 AST::FormatArguments args;
27 // bool is_literal?
30 struct FormatArgsParseError
32 enum class Kind
34 MissingArguments
35 } kind;
38 static tl::expected<FormatArgsInput, FormatArgsParseError>
39 format_args_parse_arguments (AST::MacroInvocData &invoc)
41 MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
42 Parser<MacroInvocLexer> parser (lex);
44 // TODO: check if EOF - return that format_args!() requires at least one
45 // argument
47 auto args = AST::FormatArguments ();
48 auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
49 std::unique_ptr<AST::Expr> format_expr = nullptr;
51 // TODO: Handle the case where we're not parsing a string literal (macro
52 // invocation for e.g.)
53 if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
54 format_expr = parser.parse_literal_expr ();
56 // TODO(Arthur): Clean this up - if we haven't parsed a string literal but a
57 // macro invocation, what do we do here? return a tl::unexpected?
58 auto format_str = static_cast<AST::LiteralExpr &> (*format_expr)
59 .get_literal ()
60 .as_string ();
62 // TODO: Allow implicit captures ONLY if the the first arg is a string literal
63 // and not a macro invocation
65 // TODO: How to consume all of the arguments until the delimiter?
67 // TODO: What we then want to do is as follows:
68 // for each token, check if it is an identifier
69 // yes? is the next token an equal sign (=)
70 // yes?
71 // -> if that identifier is already present in our map, error
72 // out
73 // -> parse an expression, return a FormatArgument::Named
74 // no?
75 // -> if there have been named arguments before, error out
76 // (positional after named error)
77 // -> parse an expression, return a FormatArgument::Normal
78 while (parser.peek_current_token ()->get_id () != last_token_id)
80 parser.skip_token (COMMA);
82 if (parser.peek_current_token ()->get_id () == IDENTIFIER
83 && parser.peek (1)->get_id () == EQUAL)
85 // FIXME: This is ugly - just add a parser.parse_identifier()?
86 auto ident_tok = parser.peek_current_token ();
87 auto ident = Identifier (ident_tok);
89 parser.skip_token (IDENTIFIER);
90 parser.skip_token (EQUAL);
92 auto expr = parser.parse_expr ();
94 // TODO: Handle graciously
95 if (!expr)
96 rust_unreachable ();
98 args.push (AST::FormatArgument::named (ident, std::move (expr)));
100 else
102 auto expr = parser.parse_expr ();
104 // TODO: Handle graciously
105 if (!expr)
106 rust_unreachable ();
108 args.push (AST::FormatArgument::normal (std::move (expr)));
110 // we need to skip commas, don't we?
113 return FormatArgsInput{std::move (format_str), std::move (args)};
116 tl::optional<AST::Fragment>
117 MacroBuiltin::format_args_handler (location_t invoc_locus,
118 AST::MacroInvocData &invoc,
119 AST::FormatArgs::Newline nl)
121 auto input = format_args_parse_arguments (invoc);
123 if (!input)
125 rust_error_at (invoc_locus,
126 "could not parse arguments to %<format_args!()%>");
127 return tl::nullopt;
130 // TODO(Arthur): We need to handle this
131 // // if it is not a literal, it's an eager macro invocation - return it
132 // if (!fmt_expr->is_literal ())
133 // {
134 // auto token_tree = invoc.get_delim_tok_tree ();
135 // return AST::Fragment ({AST::SingleASTNode (std::move (fmt_expr))},
136 // token_tree.to_token_stream ());
137 // }
139 // TODO(Arthur): Handle this as well - raw strings are special for the
140 // format_args parser auto fmt_str = static_cast<AST::LiteralExpr &>
141 // (*fmt_arg.get ()); Switch on the format string to know if the string is raw
142 // or cooked switch (fmt_str.get_lit_type ())
143 // {
144 // // case AST::Literal::RAW_STRING:
145 // case AST::Literal::STRING:
146 // break;
147 // case AST::Literal::CHAR:
148 // case AST::Literal::BYTE:
149 // case AST::Literal::BYTE_STRING:
150 // case AST::Literal::INT:
151 // case AST::Literal::FLOAT:
152 // case AST::Literal::BOOL:
153 // case AST::Literal::ERROR:
154 // rust_unreachable ();
155 // }
157 bool append_newline = nl == AST::FormatArgs::Newline::Yes;
159 auto fmt_str = std::move (input->format_str);
160 if (append_newline)
161 fmt_str += '\n';
163 auto pieces = Fmt::Pieces::collect (fmt_str, append_newline);
165 // TODO:
166 // do the transformation into an AST::FormatArgs node
167 // return that
168 // expand it during lowering
170 // TODO: we now need to take care of creating `unfinished_literal`? this is
171 // for creating the `template`
173 auto fmt_args_node = AST::FormatArgs (invoc_locus, std::move (pieces),
174 std::move (input->args));
176 auto expanded
177 = Fmt::expand_format_args (fmt_args_node,
178 invoc.get_delim_tok_tree ().to_token_stream ());
180 if (!expanded.has_value ())
181 return AST::Fragment::create_error ();
183 return *expanded;
185 // auto node = std::unique_ptr<AST::Expr> (fmt_args_node);
186 // auto single_node = AST::SingleASTNode (std::move (node));
188 // return AST::Fragment ({std::move (single_node)},
189 // invoc.get_delim_tok_tree ().to_token_stream ());
192 } // namespace Rust