Skip to content

Commit e82b59d

Browse files
Merge #981
981: macro-expand: Add SubstitutionCtx class in its own file r=CohenArthur a=CohenArthur The `MacroExpander` class had multiple static functions which were constantly passing the same parameters around for expansion. This refactor adds a new `SubstituteCtx` class which keeps track of the three common arguments given to the substitute functions, and offers these implementations in a new source file to keep the original expander light. Closes #957 Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
2 parents ed1a4dc + 27be628 commit e82b59d

File tree

5 files changed

+291
-275
lines changed

5 files changed

+291
-275
lines changed

gcc/rust/Make-lang.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ GRS_OBJS = \
7575
rust/rust-compile-resolve-path.o \
7676
rust/rust-macro-expand.o \
7777
rust/rust-macro-invoc-lexer.o \
78+
rust/rust-macro-substitute-ctx.o \
7879
rust/rust-macro-builtins.o \
7980
rust/rust-hir-full-test.o \
8081
rust/rust-hir-map.o \

gcc/rust/expand/rust-macro-expand.cc

Lines changed: 4 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// <http://www.gnu.org/licenses/>.
1818

1919
#include "rust-macro-expand.h"
20+
#include "rust-macro-substitute-ctx.h"
2021
#include "rust-ast-full.h"
2122
#include "rust-ast-visitor.h"
2223
#include "rust-diagnostics.h"
@@ -3773,8 +3774,10 @@ MacroExpander::transcribe_rule (
37733774
auto invoc_stream = invoc_token_tree.to_token_stream ();
37743775
auto macro_rule_tokens = transcribe_tree.to_token_stream ();
37753776

3777+
auto substitute_context
3778+
= SubstituteCtx (invoc_stream, macro_rule_tokens, matched_fragments);
37763779
std::vector<std::unique_ptr<AST::Token>> substituted_tokens
3777-
= substitute_tokens (invoc_stream, macro_rule_tokens, matched_fragments);
3780+
= substitute_context.substitute_tokens ();
37783781

37793782
// // handy for debugging
37803783
// for (auto &tok : substituted_tokens)
@@ -3896,220 +3899,4 @@ MacroExpander::transcribe_rule (
38963899

38973900
return AST::ASTFragment (std::move (nodes));
38983901
}
3899-
3900-
std::vector<std::unique_ptr<AST::Token>>
3901-
MacroExpander::substitute_metavar (
3902-
std::vector<std::unique_ptr<AST::Token>> &input,
3903-
std::map<std::string, std::vector<MatchedFragment>> &fragments,
3904-
std::unique_ptr<AST::Token> &metavar)
3905-
{
3906-
auto metavar_name = metavar->get_str ();
3907-
3908-
std::vector<std::unique_ptr<AST::Token>> expanded;
3909-
auto it = fragments.find (metavar_name);
3910-
if (it == fragments.end ())
3911-
{
3912-
// Return a copy of the original token
3913-
expanded.push_back (metavar->clone_token ());
3914-
}
3915-
else
3916-
{
3917-
// Replace
3918-
// We only care about the vector when expanding repetitions. Just access
3919-
// the first element of the vector.
3920-
// FIXME: Clean this up so it makes more sense
3921-
auto &frag = it->second[0];
3922-
for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
3923-
offs++)
3924-
{
3925-
auto &tok = input.at (offs);
3926-
expanded.push_back (tok->clone_token ());
3927-
}
3928-
}
3929-
3930-
return expanded;
3931-
}
3932-
3933-
std::vector<std::unique_ptr<AST::Token>>
3934-
MacroExpander::substitute_repetition (
3935-
std::vector<std::unique_ptr<AST::Token>> &input,
3936-
std::vector<std::unique_ptr<AST::Token>> &macro,
3937-
std::map<std::string, std::vector<MatchedFragment>> &fragments,
3938-
size_t pattern_start, size_t pattern_end)
3939-
{
3940-
rust_assert (pattern_end < macro.size ());
3941-
3942-
rust_debug ("pattern start: %lu", pattern_start);
3943-
rust_debug ("pattern end: %lu", pattern_end);
3944-
3945-
std::vector<std::unique_ptr<AST::Token>> expanded;
3946-
3947-
// Find the first fragment and get the amount of repetitions that we should
3948-
// perform
3949-
size_t repeat_amount = 0;
3950-
for (size_t i = pattern_start; i < pattern_end; i++)
3951-
{
3952-
if (macro.at (i)->get_id () == DOLLAR_SIGN)
3953-
{
3954-
auto &frag_token = macro.at (i + 1);
3955-
if (frag_token->get_id () == IDENTIFIER)
3956-
{
3957-
auto it = fragments.find (frag_token->get_str ());
3958-
if (it == fragments.end ())
3959-
{
3960-
// If the repetition is not anything we know (ie no declared
3961-
// metavars, or metavars which aren't present in the
3962-
// fragment), we can just error out. No need to paste the
3963-
// tokens as if nothing had happened.
3964-
rust_error_at (frag_token->get_locus (),
3965-
"metavar %s used in repetition does not exist",
3966-
frag_token->get_str ().c_str ());
3967-
// FIXME:
3968-
return expanded;
3969-
}
3970-
3971-
// FIXME: Refactor, ugly
3972-
repeat_amount = it->second[0].match_amount;
3973-
}
3974-
}
3975-
}
3976-
3977-
rust_debug ("repetition amount to use: %lu", repeat_amount);
3978-
std::vector<std::unique_ptr<AST::Token>> new_macro;
3979-
3980-
// We want to generate a "new macro" to substitute with. This new macro
3981-
// should contain only the tokens inside the pattern
3982-
for (size_t tok_idx = pattern_start; tok_idx < pattern_end; tok_idx++)
3983-
new_macro.emplace_back (macro.at (tok_idx)->clone_token ());
3984-
3985-
// Then, we want to create a subset of the matches so that
3986-
// `substitute_tokens()` can only see one fragment per metavar. Let's say we
3987-
// have the following user input: (1 145 'h')
3988-
// on the following match arm: ($($lit:literal)*)
3989-
// which causes the following matches: { "lit": [1, 145, 'h'] }
3990-
//
3991-
// The pattern (new_macro) is `$lit:literal`
3992-
// The first time we expand it, we want $lit to have the following token: 1
3993-
// The second time, 145
3994-
// The third and final time, 'h'
3995-
//
3996-
// In order to do so we must create "sub maps", which only contain parts of
3997-
// the original matches
3998-
// sub-maps: [ { "lit": 1 }, { "lit": 145 }, { "lit": 'h' } ]
3999-
//
4000-
// and give them to `substitute_tokens` one by one.
4001-
4002-
for (size_t i = 0; i < repeat_amount; i++)
4003-
{
4004-
std::map<std::string, std::vector<MatchedFragment>> sub_map;
4005-
for (auto &kv_match : fragments)
4006-
{
4007-
std::vector<MatchedFragment> sub_vec;
4008-
sub_vec.emplace_back (kv_match.second[i]);
4009-
4010-
sub_map.insert ({kv_match.first, sub_vec});
4011-
}
4012-
4013-
auto new_tokens = substitute_tokens (input, new_macro, sub_map);
4014-
4015-
for (auto &new_token : new_tokens)
4016-
expanded.emplace_back (new_token->clone_token ());
4017-
}
4018-
4019-
// FIXME: We also need to make sure that all subsequent fragments
4020-
// contain the same amount of repetitions as the first one
4021-
4022-
return expanded;
4023-
}
4024-
4025-
std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t>
4026-
MacroExpander::substitute_token (
4027-
std::vector<std::unique_ptr<AST::Token>> &input,
4028-
std::vector<std::unique_ptr<AST::Token>> &macro,
4029-
std::map<std::string, std::vector<MatchedFragment>> &fragments,
4030-
size_t token_idx)
4031-
{
4032-
auto &token = macro.at (token_idx);
4033-
switch (token->get_id ())
4034-
{
4035-
case IDENTIFIER:
4036-
rust_debug ("expanding metavar: %s", token->get_str ().c_str ());
4037-
return {substitute_metavar (input, fragments, token), 1};
4038-
case LEFT_PAREN: {
4039-
// We need to parse up until the closing delimiter and expand this
4040-
// fragment->n times.
4041-
rust_debug ("expanding repetition");
4042-
std::vector<std::unique_ptr<AST::Token>> repetition_pattern;
4043-
size_t pattern_start = token_idx + 1;
4044-
size_t pattern_end = pattern_start;
4045-
for (; pattern_end < macro.size ()
4046-
&& macro.at (pattern_end)->get_id () != RIGHT_PAREN;
4047-
pattern_end++)
4048-
;
4049-
4050-
// FIXME: This skips whitespaces... Is that okay??
4051-
// FIXME: Is there any existing parsing function that allows us to parse
4052-
// a macro pattern?
4053-
4054-
// FIXME: Add error handling in the case we haven't found a matching
4055-
// closing delimiter
4056-
4057-
// FIXME: We need to parse the repetition token now
4058-
4059-
return {
4060-
substitute_repetition (input, macro, fragments, pattern_start,
4061-
pattern_end),
4062-
// + 2 for the opening and closing parentheses which are mandatory
4063-
// + 1 for the repetitor (+, *, ?)
4064-
pattern_end - pattern_start + 3};
4065-
}
4066-
// TODO: We need to check if the $ was alone. In that case, do
4067-
// not error out: Simply act as if there was an empty identifier
4068-
// with no associated fragment and paste the dollar sign in the
4069-
// transcription. Unsure how to do that since we always have at
4070-
// least the closing curly brace after an empty $...
4071-
default:
4072-
rust_error_at (token->get_locus (),
4073-
"unexpected token in macro transcribe: expected "
4074-
"%<(%> or identifier after %<$%>, got %<%s%>",
4075-
get_token_description (token->get_id ()));
4076-
}
4077-
4078-
// FIXME: gcc_unreachable() error case?
4079-
return {std::vector<std::unique_ptr<AST::Token>> (), 0};
4080-
}
4081-
4082-
std::vector<std::unique_ptr<AST::Token>>
4083-
MacroExpander::substitute_tokens (
4084-
std::vector<std::unique_ptr<AST::Token>> &input,
4085-
std::vector<std::unique_ptr<AST::Token>> &macro,
4086-
std::map<std::string, std::vector<MatchedFragment>> &fragments)
4087-
{
4088-
std::vector<std::unique_ptr<AST::Token>> replaced_tokens;
4089-
4090-
for (size_t i = 0; i < macro.size (); i++)
4091-
{
4092-
auto &tok = macro.at (i);
4093-
if (tok->get_id () == DOLLAR_SIGN)
4094-
{
4095-
// Aaaaah, if only we had C++17 :)
4096-
// auto [expanded, tok_to_skip] = ...
4097-
auto p = substitute_token (input, macro, fragments, i + 1);
4098-
auto expanded = std::move (p.first);
4099-
auto tok_to_skip = p.second;
4100-
4101-
i += tok_to_skip;
4102-
4103-
for (auto &token : expanded)
4104-
replaced_tokens.emplace_back (token->clone_token ());
4105-
}
4106-
else
4107-
{
4108-
replaced_tokens.emplace_back (tok->clone_token ());
4109-
}
4110-
}
4111-
4112-
return replaced_tokens;
4113-
}
4114-
41153902
} // namespace Rust

gcc/rust/expand/rust-macro-expand.h

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -213,64 +213,6 @@ struct MacroExpander
213213
size_t &match_amount, size_t lo_bound = 0,
214214
size_t hi_bound = 0);
215215

216-
/**
217-
* Substitute a metavariable by its given fragment in a transcribing context,
218-
* i.e. replacing $var with the associated fragment.
219-
*
220-
* @param input Tokens given to the transcribing context
221-
* @param fragments Fragments given to the macro substitution
222-
* @param metavar Metavariable to try and replace
223-
*
224-
* @return A token containing the associated fragment expanded into tokens if
225-
* any, or the cloned token if no fragment was associated
226-
*/
227-
static std::vector<std::unique_ptr<AST::Token>> substitute_metavar (
228-
std::vector<std::unique_ptr<AST::Token>> &input,
229-
std::map<std::string, std::vector<MatchedFragment>> &fragments,
230-
std::unique_ptr<AST::Token> &metavar);
231-
232-
/**
233-
* Substitute a macro repetition by its given fragments
234-
*
235-
* @param input Tokens given to the transcribing context
236-
* @param fragments Fragments given to the macro substitution
237-
* @param pattern_start Start index of the pattern tokens
238-
* @param pattern_end Index Amount of tokens in the pattern
239-
*
240-
* @return A vector containing the repeated pattern
241-
*/
242-
static std::vector<std::unique_ptr<AST::Token>> substitute_repetition (
243-
std::vector<std::unique_ptr<AST::Token>> &input,
244-
std::vector<std::unique_ptr<AST::Token>> &macro,
245-
std::map<std::string, std::vector<MatchedFragment>> &fragments,
246-
size_t pattern_start, size_t pattern_end);
247-
248-
/**
249-
* Substitute a given token by its appropriate representation
250-
*
251-
* @param macro Tokens used in the macro declaration
252-
* @param input Tokens given to the transcribing context
253-
* @param fragments Fragments given to the macro substitution
254-
* @param token Current token to try and substitute
255-
*
256-
* @return A token containing the associated fragment expanded into tokens if
257-
* any, or the cloned token if no fragment was associated, as well as the
258-
* amount of tokens that should be skipped before the next invocation. Since
259-
* this function may consume more than just one token, it is important to skip
260-
* ahead of the input to avoid mis-substitutions
261-
*/
262-
static std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t>
263-
substitute_token (
264-
std::vector<std::unique_ptr<AST::Token>> &input,
265-
std::vector<std::unique_ptr<AST::Token>> &macro,
266-
std::map<std::string, std::vector<MatchedFragment>> &fragments,
267-
size_t token_idx);
268-
269-
static std::vector<std::unique_ptr<AST::Token>> substitute_tokens (
270-
std::vector<std::unique_ptr<AST::Token>> &input,
271-
std::vector<std::unique_ptr<AST::Token>> &macro,
272-
std::map<std::string, std::vector<MatchedFragment>> &fragments);
273-
274216
void push_context (ContextType t) { context.push_back (t); }
275217

276218
ContextType pop_context ()

0 commit comments

Comments
 (0)