]>
Commit | Line | Data |
---|---|---|
767698ff | 1 | // Copyright (C) 2020-2024 Free Software Foundation, Inc. |
10d501fa AC |
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 | ||
1841081a AC |
19 | #include "rust-macro-substitute-ctx.h" |
20 | ||
21 | namespace Rust { | |
22 | ||
57c5d234 OA |
23 | bool |
24 | SubstituteCtx::substitute_metavar ( | |
25 | std::unique_ptr<AST::Token> &metavar, | |
26 | std::vector<std::unique_ptr<AST::Token>> &expanded) | |
1841081a AC |
27 | { |
28 | auto metavar_name = metavar->get_str (); | |
29 | ||
1841081a AC |
30 | auto it = fragments.find (metavar_name); |
31 | if (it == fragments.end ()) | |
32 | { | |
57c5d234 | 33 | // fail to substitute |
b14efcea OA |
34 | |
35 | // HACK: substitute ($ crate) => (crate) | |
36 | if (metavar->get_id () != CRATE) | |
37 | return false; | |
38 | ||
39 | expanded.push_back (metavar->clone_token ()); | |
40 | return true; | |
1841081a AC |
41 | } |
42 | else | |
43 | { | |
44 | // If we are expanding a metavar which has a lof of matches, we are | |
45 | // currently expanding a repetition metavar - not a simple metavar. We | |
46 | // need to error out and inform the user. | |
47 | // Associated test case for an example: compile/macro-issue1224.rs | |
f1dbdb63 | 48 | if (!it->second->is_single_fragment ()) |
1841081a AC |
49 | { |
50 | rust_error_at (metavar->get_locus (), | |
51 | "metavariable is still repeating at this depth"); | |
52 | rust_inform ( | |
53 | metavar->get_locus (), | |
54 | "you probably forgot the repetition operator: %<%s%s%s%>", "$(", | |
55 | metavar->as_string ().c_str (), ")*"); | |
57c5d234 | 56 | return true; |
1841081a AC |
57 | } |
58 | ||
59 | // We only care about the vector when expanding repetitions. | |
60 | // Just access the first element of the vector. | |
f1dbdb63 | 61 | auto &frag = it->second->get_single_fragment (); |
1841081a AC |
62 | for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end; |
63 | offs++) | |
64 | { | |
65 | auto &tok = input.at (offs); | |
66 | expanded.push_back (tok->clone_token ()); | |
67 | } | |
68 | } | |
69 | ||
57c5d234 | 70 | return true; |
1841081a AC |
71 | } |
72 | ||
73 | bool | |
74 | SubstituteCtx::check_repetition_amount (size_t pattern_start, | |
75 | size_t pattern_end, | |
76 | size_t &expected_repetition_amount) | |
77 | { | |
78 | bool first_fragment_found = false; | |
79 | bool is_valid = true; | |
80 | ||
81 | for (size_t i = pattern_start; i < pattern_end; i++) | |
82 | { | |
83 | if (macro.at (i)->get_id () == DOLLAR_SIGN) | |
84 | { | |
85 | auto &frag_token = macro.at (i + 1); | |
dfd2e26b OA |
86 | if (token_id_is_keyword (frag_token->get_id ()) |
87 | || frag_token->get_id () == IDENTIFIER) | |
1841081a AC |
88 | { |
89 | auto it = fragments.find (frag_token->get_str ()); | |
90 | if (it == fragments.end ()) | |
91 | { | |
92 | // If the repetition is not anything we know (ie no declared | |
93 | // metavars, or metavars which aren't present in the | |
94 | // fragment), we can just error out. No need to paste the | |
95 | // tokens as if nothing had happened. | |
96 | rust_error_at (frag_token->get_locus (), | |
97 | "metavar %s used in repetition does not exist", | |
98 | frag_token->get_str ().c_str ()); | |
99 | ||
100 | is_valid = false; | |
101 | } | |
102 | ||
f1dbdb63 | 103 | auto &fragment = *it->second; |
1841081a | 104 | |
253c7343 | 105 | if (!fragment.is_single_fragment ()) |
1841081a | 106 | { |
f1dbdb63 OA |
107 | auto &fragment_rep |
108 | = static_cast<MatchedFragmentContainerRepetition &> ( | |
109 | fragment); | |
110 | size_t repeat_amount = fragment_rep.get_match_amount (); | |
253c7343 OA |
111 | if (!first_fragment_found) |
112 | { | |
113 | first_fragment_found = true; | |
114 | expected_repetition_amount = repeat_amount; | |
115 | } | |
116 | else | |
1841081a | 117 | { |
253c7343 OA |
118 | if (repeat_amount != expected_repetition_amount) |
119 | { | |
120 | rust_error_at ( | |
121 | frag_token->get_locus (), | |
122 | "different amount of matches used in merged " | |
123 | "repetitions: expected %lu, got %lu", | |
124 | (unsigned long) expected_repetition_amount, | |
125 | (unsigned long) repeat_amount); | |
126 | is_valid = false; | |
127 | } | |
1841081a AC |
128 | } |
129 | } | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
253c7343 | 134 | return is_valid && first_fragment_found; |
1841081a AC |
135 | } |
136 | ||
137 | std::vector<std::unique_ptr<AST::Token>> | |
138 | SubstituteCtx::substitute_repetition ( | |
139 | size_t pattern_start, size_t pattern_end, | |
140 | std::unique_ptr<AST::Token> separator_token) | |
141 | { | |
142 | rust_assert (pattern_end < macro.size ()); | |
143 | ||
144 | size_t repeat_amount = 0; | |
145 | if (!check_repetition_amount (pattern_start, pattern_end, repeat_amount)) | |
146 | return {}; | |
147 | ||
148 | rust_debug ("repetition amount to use: %lu", (unsigned long) repeat_amount); | |
149 | std::vector<std::unique_ptr<AST::Token>> expanded; | |
150 | std::vector<std::unique_ptr<AST::Token>> new_macro; | |
151 | ||
152 | // We want to generate a "new macro" to substitute with. This new macro | |
153 | // should contain only the tokens inside the pattern | |
154 | for (size_t tok_idx = pattern_start; tok_idx < pattern_end; tok_idx++) | |
155 | new_macro.emplace_back (macro.at (tok_idx)->clone_token ()); | |
156 | ||
157 | // Then, we want to create a subset of the matches so that | |
158 | // `substitute_tokens()` can only see one fragment per metavar. Let's say we | |
159 | // have the following user input: (1 145 'h') | |
160 | // on the following match arm: ($($lit:literal)*) | |
161 | // which causes the following matches: { "lit": [1, 145, 'h'] } | |
162 | // | |
163 | // The pattern (new_macro) is `$lit:literal` | |
164 | // The first time we expand it, we want $lit to have the following token: 1 | |
165 | // The second time, 145 | |
166 | // The third and final time, 'h' | |
167 | // | |
168 | // In order to do so we must create "sub maps", which only contain parts of | |
169 | // the original matches | |
170 | // sub-maps: [ { "lit": 1 }, { "lit": 145 }, { "lit": 'h' } ] | |
171 | // | |
172 | // and give them to `substitute_tokens` one by one. | |
173 | ||
174 | for (size_t i = 0; i < repeat_amount; i++) | |
175 | { | |
f1dbdb63 | 176 | std::map<std::string, MatchedFragmentContainer *> sub_map; |
1841081a AC |
177 | for (auto &kv_match : fragments) |
178 | { | |
f1dbdb63 OA |
179 | if (kv_match.second->is_single_fragment ()) |
180 | sub_map.emplace (kv_match.first, kv_match.second); | |
b911f7b8 OA |
181 | // Hack: A repeating meta variable might not be present in the new |
182 | // macro. Don't include this match if the fragment doesn't have enough | |
183 | // items, as check_repetition_amount should prevent repetition amount | |
184 | // mismatches anyway. | |
f1dbdb63 OA |
185 | else if (kv_match.second->get_fragments ().size () > i) |
186 | sub_map.emplace (kv_match.first, | |
187 | kv_match.second->get_fragments ().at (i).get ()); | |
1841081a AC |
188 | } |
189 | ||
190 | auto substitute_context = SubstituteCtx (input, new_macro, sub_map); | |
191 | auto new_tokens = substitute_context.substitute_tokens (); | |
192 | ||
193 | // Skip the first repetition, but add the separator to the expanded | |
194 | // tokens if it is present | |
195 | if (i != 0 && separator_token) | |
196 | expanded.emplace_back (separator_token->clone_token ()); | |
197 | ||
198 | for (auto &new_token : new_tokens) | |
199 | expanded.emplace_back (new_token->clone_token ()); | |
200 | } | |
201 | ||
202 | // FIXME: We also need to make sure that all subsequent fragments | |
203 | // contain the same amount of repetitions as the first one | |
204 | ||
205 | return expanded; | |
206 | } | |
207 | ||
208 | static bool | |
209 | is_rep_op (std::unique_ptr<AST::Token> &tok) | |
210 | { | |
211 | auto id = tok->get_id (); | |
212 | return id == QUESTION_MARK || id == ASTERISK || id == PLUS; | |
213 | } | |
214 | ||
215 | std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t> | |
216 | SubstituteCtx::substitute_token (size_t token_idx) | |
217 | { | |
218 | auto &token = macro.at (token_idx); | |
dfd2e26b | 219 | |
1841081a AC |
220 | switch (token->get_id ()) |
221 | { | |
dfd2e26b OA |
222 | default: |
223 | if (token_id_is_keyword (token->get_id ())) | |
224 | { | |
225 | case IDENTIFIER: | |
57c5d234 OA |
226 | std::vector<std::unique_ptr<AST::Token>> expanded; |
227 | ||
dfd2e26b | 228 | rust_debug ("expanding metavar: %s", token->get_str ().c_str ()); |
57c5d234 OA |
229 | |
230 | if (substitute_metavar (token, expanded)) | |
231 | return {std::move (expanded), 2}; | |
dfd2e26b | 232 | } |
57c5d234 OA |
233 | |
234 | // don't substitute, dollar sign is alone/metavar is unknown | |
235 | return {std::vector<std::unique_ptr<AST::Token>> (), 0}; | |
236 | ||
1841081a AC |
237 | case LEFT_PAREN: { |
238 | // We need to parse up until the closing delimiter and expand this | |
239 | // fragment->n times. | |
240 | rust_debug ("expanding repetition"); | |
241 | ||
242 | // We're in a context where macro repetitions have already been | |
243 | // parsed and validated: This means that | |
244 | // 1/ There will be no delimiters as that is an error | |
245 | // 2/ There are no fragment specifiers anymore, which prevents us | |
246 | // from reusing parser functions. | |
247 | // | |
248 | // Repetition patterns are also special in that they cannot contain | |
249 | // "rogue" delimiters: For example, this is invalid, as they are | |
250 | // parsed as MacroMatches and must contain a correct amount of | |
251 | // delimiters. | |
252 | // `$($e:expr ) )` | |
253 | // ^ rogue closing parenthesis | |
254 | // | |
255 | // With all of that in mind, we can simply skip ahead from one | |
256 | // parenthesis to the other to find the pattern to expand. Of course, | |
257 | // pairs of delimiters, including parentheses, are allowed. | |
258 | // `$($e:expr ( ) )` | |
259 | // Parentheses are the sole delimiter for which we need a special | |
260 | // behavior since they delimit the repetition pattern | |
261 | ||
262 | size_t pattern_start = token_idx + 1; | |
263 | size_t pattern_end = pattern_start; | |
264 | auto parentheses_stack = 0; | |
265 | for (size_t idx = pattern_start; idx < macro.size (); idx++) | |
266 | { | |
267 | if (macro.at (idx)->get_id () == LEFT_PAREN) | |
268 | { | |
269 | parentheses_stack++; | |
270 | } | |
271 | else if (macro.at (idx)->get_id () == RIGHT_PAREN) | |
272 | { | |
273 | if (parentheses_stack == 0) | |
274 | { | |
275 | pattern_end = idx; | |
276 | break; | |
277 | } | |
278 | parentheses_stack--; | |
279 | } | |
280 | } | |
281 | ||
282 | // Unreachable case, but let's make sure we don't ever run into it | |
283 | rust_assert (pattern_end != pattern_start); | |
284 | ||
285 | std::unique_ptr<AST::Token> separator_token = nullptr; | |
286 | if (pattern_end + 1 <= macro.size ()) | |
287 | { | |
288 | auto &post_pattern_token = macro.at (pattern_end + 1); | |
289 | if (!is_rep_op (post_pattern_token)) | |
290 | separator_token = post_pattern_token->clone_token (); | |
291 | } | |
292 | ||
293 | // Amount of tokens to skip | |
294 | auto to_skip = 0; | |
295 | // Parentheses | |
296 | to_skip += 2; | |
297 | // Repetition operator | |
298 | to_skip += 1; | |
299 | // Separator | |
300 | if (separator_token) | |
301 | to_skip += 1; | |
302 | ||
303 | return {substitute_repetition (pattern_start, pattern_end, | |
304 | std::move (separator_token)), | |
57c5d234 | 305 | pattern_end - pattern_start + to_skip + 1}; |
1841081a | 306 | } |
1841081a AC |
307 | } |
308 | ||
93866b6a | 309 | rust_unreachable (); |
1841081a AC |
310 | } |
311 | ||
312 | std::vector<std::unique_ptr<AST::Token>> | |
313 | SubstituteCtx::substitute_tokens () | |
314 | { | |
315 | std::vector<std::unique_ptr<AST::Token>> replaced_tokens; | |
316 | rust_debug ("expanding tokens"); | |
317 | ||
57c5d234 | 318 | for (size_t i = 0; i < macro.size ();) |
1841081a AC |
319 | { |
320 | auto &tok = macro.at (i); | |
321 | if (tok->get_id () == DOLLAR_SIGN) | |
322 | { | |
323 | // Aaaaah, if only we had C++17 :) | |
324 | // auto [expanded, tok_to_skip] = ... | |
325 | auto p = substitute_token (i + 1); | |
326 | auto expanded = std::move (p.first); | |
327 | auto tok_to_skip = p.second; | |
328 | ||
57c5d234 OA |
329 | if (!tok_to_skip) |
330 | { | |
331 | replaced_tokens.emplace_back (tok->clone_token ()); | |
332 | tok_to_skip++; | |
333 | } | |
334 | ||
1841081a AC |
335 | i += tok_to_skip; |
336 | ||
337 | for (auto &token : expanded) | |
338 | replaced_tokens.emplace_back (token->clone_token ()); | |
339 | } | |
340 | else | |
341 | { | |
342 | replaced_tokens.emplace_back (tok->clone_token ()); | |
57c5d234 | 343 | i++; |
1841081a AC |
344 | } |
345 | } | |
346 | ||
347 | return replaced_tokens; | |
348 | } | |
349 | ||
350 | } // namespace Rust |