1 // Copyright (C) 2020-2023 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/>.
19 #include "rust-macro-substitute-ctx.h"
24 SubstituteCtx::substitute_metavar (
25 std::unique_ptr
<AST::Token
> &metavar
,
26 std::vector
<std::unique_ptr
<AST::Token
>> &expanded
)
28 auto metavar_name
= metavar
->get_str ();
30 auto it
= fragments
.find (metavar_name
);
31 if (it
== fragments
.end ())
35 // HACK: substitute ($ crate) => (crate)
36 if (metavar
->get_id () != CRATE
)
39 expanded
.push_back (metavar
->clone_token ());
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
48 if (!it
->second
->is_single_fragment ())
50 rust_error_at (metavar
->get_locus (),
51 "metavariable is still repeating at this depth");
53 metavar
->get_locus (),
54 "you probably forgot the repetition operator: %<%s%s%s%>", "$(",
55 metavar
->as_string ().c_str (), ")*");
59 // We only care about the vector when expanding repetitions.
60 // Just access the first element of the vector.
61 auto &frag
= it
->second
->get_single_fragment ();
62 for (size_t offs
= frag
.token_offset_begin
; offs
< frag
.token_offset_end
;
65 auto &tok
= input
.at (offs
);
66 expanded
.push_back (tok
->clone_token ());
74 SubstituteCtx::check_repetition_amount (size_t pattern_start
,
76 size_t &expected_repetition_amount
)
78 bool first_fragment_found
= false;
81 for (size_t i
= pattern_start
; i
< pattern_end
; i
++)
83 if (macro
.at (i
)->get_id () == DOLLAR_SIGN
)
85 auto &frag_token
= macro
.at (i
+ 1);
86 if (token_id_is_keyword (frag_token
->get_id ())
87 || frag_token
->get_id () == IDENTIFIER
)
89 auto it
= fragments
.find (frag_token
->get_str ());
90 if (it
== fragments
.end ())
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 ());
103 auto &fragment
= *it
->second
;
105 if (!fragment
.is_single_fragment ())
108 = static_cast<MatchedFragmentContainerRepetition
&> (
110 size_t repeat_amount
= fragment_rep
.get_match_amount ();
111 if (!first_fragment_found
)
113 first_fragment_found
= true;
114 expected_repetition_amount
= repeat_amount
;
118 if (repeat_amount
!= expected_repetition_amount
)
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
);
134 return is_valid
&& first_fragment_found
;
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
)
142 rust_assert (pattern_end
< macro
.size ());
144 size_t repeat_amount
= 0;
145 if (!check_repetition_amount (pattern_start
, pattern_end
, repeat_amount
))
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
;
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 ());
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'] }
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'
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' } ]
172 // and give them to `substitute_tokens` one by one.
174 for (size_t i
= 0; i
< repeat_amount
; i
++)
176 std::map
<std::string
, MatchedFragmentContainer
*> sub_map
;
177 for (auto &kv_match
: fragments
)
179 if (kv_match
.second
->is_single_fragment ())
180 sub_map
.emplace (kv_match
.first
, kv_match
.second
);
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.
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 ());
190 auto substitute_context
= SubstituteCtx (input
, new_macro
, sub_map
);
191 auto new_tokens
= substitute_context
.substitute_tokens ();
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 ());
198 for (auto &new_token
: new_tokens
)
199 expanded
.emplace_back (new_token
->clone_token ());
202 // FIXME: We also need to make sure that all subsequent fragments
203 // contain the same amount of repetitions as the first one
209 is_rep_op (std::unique_ptr
<AST::Token
> &tok
)
211 auto id
= tok
->get_id ();
212 return id
== QUESTION_MARK
|| id
== ASTERISK
|| id
== PLUS
;
215 std::pair
<std::vector
<std::unique_ptr
<AST::Token
>>, size_t>
216 SubstituteCtx::substitute_token (size_t token_idx
)
218 auto &token
= macro
.at (token_idx
);
220 switch (token
->get_id ())
223 if (token_id_is_keyword (token
->get_id ()))
226 std::vector
<std::unique_ptr
<AST::Token
>> expanded
;
228 rust_debug ("expanding metavar: %s", token
->get_str ().c_str ());
230 if (substitute_metavar (token
, expanded
))
231 return {std::move (expanded
), 2};
234 // don't substitute, dollar sign is alone/metavar is unknown
235 return {std::vector
<std::unique_ptr
<AST::Token
>> (), 0};
238 // We need to parse up until the closing delimiter and expand this
239 // fragment->n times.
240 rust_debug ("expanding repetition");
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.
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
253 // ^ rogue closing parenthesis
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.
259 // Parentheses are the sole delimiter for which we need a special
260 // behavior since they delimit the repetition pattern
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
++)
267 if (macro
.at (idx
)->get_id () == LEFT_PAREN
)
271 else if (macro
.at (idx
)->get_id () == RIGHT_PAREN
)
273 if (parentheses_stack
== 0)
282 // Unreachable case, but let's make sure we don't ever run into it
283 rust_assert (pattern_end
!= pattern_start
);
285 std::unique_ptr
<AST::Token
> separator_token
= nullptr;
286 if (pattern_end
+ 1 <= macro
.size ())
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 ();
293 // Amount of tokens to skip
297 // Repetition operator
303 return {substitute_repetition (pattern_start
, pattern_end
,
304 std::move (separator_token
)),
305 pattern_end
- pattern_start
+ to_skip
+ 1};
312 std::vector
<std::unique_ptr
<AST::Token
>>
313 SubstituteCtx::substitute_tokens ()
315 std::vector
<std::unique_ptr
<AST::Token
>> replaced_tokens
;
316 rust_debug ("expanding tokens");
318 for (size_t i
= 0; i
< macro
.size ();)
320 auto &tok
= macro
.at (i
);
321 if (tok
->get_id () == DOLLAR_SIGN
)
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
;
331 replaced_tokens
.emplace_back (tok
->clone_token ());
337 for (auto &token
: expanded
)
338 replaced_tokens
.emplace_back (token
->clone_token ());
342 replaced_tokens
.emplace_back (tok
->clone_token ());
347 return replaced_tokens
;