Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
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.
9 : :
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.
14 : :
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"
20 : :
21 : : namespace Rust {
22 : :
23 : 2 : struct FormatArgsInput
24 : : {
25 : : std::string format_str;
26 : : AST::FormatArguments args;
27 : : // bool is_literal?
28 : : };
29 : :
30 : : struct FormatArgsParseError
31 : : {
32 : : enum class Kind
33 : : {
34 : : MissingArguments
35 : : } kind;
36 : : };
37 : :
38 : : static tl::expected<FormatArgsInput, FormatArgsParseError>
39 : 1 : format_args_parse_arguments (AST::MacroInvocData &invoc)
40 : : {
41 : 1 : MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
42 : 1 : Parser<MacroInvocLexer> parser (lex);
43 : :
44 : : // TODO: check if EOF - return that format_args!() requires at least one
45 : : // argument
46 : :
47 : 1 : auto args = AST::FormatArguments ();
48 : 1 : auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
49 : 1 : std::unique_ptr<AST::Expr> format_expr = nullptr;
50 : :
51 : : // TODO: Handle the case where we're not parsing a string literal (macro
52 : : // invocation for e.g.)
53 : 2 : if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
54 : 1 : format_expr = parser.parse_literal_expr ();
55 : :
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 : 1 : auto format_str = static_cast<AST::LiteralExpr &> (*format_expr)
59 : 1 : .get_literal ()
60 : 1 : .as_string ();
61 : :
62 : : // TODO: Allow implicit captures ONLY if the the first arg is a string literal
63 : : // and not a macro invocation
64 : :
65 : : // TODO: How to consume all of the arguments until the delimiter?
66 : :
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 : 4 : while (parser.peek_current_token ()->get_id () != last_token_id)
79 : : {
80 : 1 : parser.skip_token (COMMA);
81 : :
82 : 1 : if (parser.peek_current_token ()->get_id () == IDENTIFIER
83 : 1 : && parser.peek (1)->get_id () == EQUAL)
84 : : {
85 : : // FIXME: This is ugly - just add a parser.parse_identifier()?
86 : 0 : auto ident_tok = parser.peek_current_token ();
87 : 0 : auto ident = Identifier (ident_tok);
88 : :
89 : 0 : parser.skip_token (IDENTIFIER);
90 : 0 : parser.skip_token (EQUAL);
91 : :
92 : 0 : auto expr = parser.parse_expr ();
93 : :
94 : : // TODO: Handle graciously
95 : 0 : if (!expr)
96 : 0 : rust_unreachable ();
97 : :
98 : 0 : args.push (AST::FormatArgument::named (ident, std::move (expr)));
99 : 0 : }
100 : : else
101 : : {
102 : 1 : auto expr = parser.parse_expr ();
103 : :
104 : : // TODO: Handle graciously
105 : 1 : if (!expr)
106 : 0 : rust_unreachable ();
107 : :
108 : 1 : args.push (AST::FormatArgument::normal (std::move (expr)));
109 : 1 : }
110 : : // we need to skip commas, don't we?
111 : : }
112 : :
113 : 2 : return FormatArgsInput{std::move (format_str), std::move (args)};
114 : 2 : }
115 : :
116 : : tl::optional<AST::Fragment>
117 : 1 : MacroBuiltin::format_args_handler (location_t invoc_locus,
118 : : AST::MacroInvocData &invoc,
119 : : AST::FormatArgs::Newline nl)
120 : : {
121 : 1 : auto input = format_args_parse_arguments (invoc);
122 : :
123 : 1 : if (!input)
124 : : {
125 : 0 : rust_error_at (invoc_locus,
126 : : "could not parse arguments to %<format_args!()%>");
127 : 0 : return tl::nullopt;
128 : : }
129 : :
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 : : // }
138 : :
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 : : // }
156 : :
157 : 1 : bool append_newline = nl == AST::FormatArgs::Newline::Yes;
158 : :
159 : 1 : auto fmt_str = std::move (input->format_str);
160 : 1 : if (append_newline)
161 : 0 : fmt_str += '\n';
162 : :
163 : 1 : auto pieces = Fmt::Pieces::collect (fmt_str, append_newline);
164 : :
165 : : // TODO:
166 : : // do the transformation into an AST::FormatArgs node
167 : : // return that
168 : : // expand it during lowering
169 : :
170 : : // TODO: we now need to take care of creating `unfinished_literal`? this is
171 : : // for creating the `template`
172 : :
173 : 1 : auto fmt_args_node = AST::FormatArgs (invoc_locus, std::move (pieces),
174 : 1 : std::move (input->args));
175 : :
176 : 1 : auto expanded
177 : : = Fmt::expand_format_args (fmt_args_node,
178 : 1 : invoc.get_delim_tok_tree ().to_token_stream ());
179 : :
180 : 1 : if (!expanded.has_value ())
181 : 0 : return AST::Fragment::create_error ();
182 : :
183 : 1 : return *expanded;
184 : :
185 : : // auto node = std::unique_ptr<AST::Expr> (fmt_args_node);
186 : : // auto single_node = AST::SingleASTNode (std::move (node));
187 : :
188 : : // return AST::Fragment ({std::move (single_node)},
189 : : // invoc.get_delim_tok_tree ().to_token_stream ());
190 : 1 : }
191 : :
192 : : } // namespace Rust
|