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
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
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"
23 struct FormatArgsInput
25 std::string format_str
;
26 AST::FormatArguments args
;
30 struct FormatArgsParseError
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
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
)
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 (=)
71 // -> if that identifier is already present in our map, error
73 // -> parse an expression, return a FormatArgument::Named
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
98 args
.push (AST::FormatArgument::named (ident
, std::move (expr
)));
102 auto expr
= parser
.parse_expr ();
104 // TODO: Handle graciously
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
);
125 rust_error_at (invoc_locus
,
126 "could not parse arguments to %<format_args!()%>");
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 ())
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 ());
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 ())
144 // // case AST::Literal::RAW_STRING:
145 // case AST::Literal::STRING:
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 ();
157 bool append_newline
= nl
== AST::FormatArgs::Newline::Yes
;
159 auto fmt_str
= std::move (input
->format_str
);
163 auto pieces
= Fmt::Pieces::collect (fmt_str
, append_newline
);
166 // do the transformation into an AST::FormatArgs node
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
));
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 ();
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 ());