macros: Only expand merged repetitions if they contain the same amount

of matches

Forbid merging repetitions if the matched fragments do not contain the
same amount of repetitions
This commit is contained in:
Arthur Cohen 2022-03-18 13:20:09 +01:00
parent 1bb9a29688
commit f8c550f7e1
4 changed files with 71 additions and 14 deletions

View File

@ -30,18 +30,14 @@ SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
return expanded;
}
std::vector<std::unique_ptr<AST::Token>>
SubstituteCtx::substitute_repetition (
size_t pattern_start, size_t pattern_end,
std::unique_ptr<AST::Token> separator_token)
bool
SubstituteCtx::check_repetition_amount (size_t pattern_start,
size_t pattern_end,
size_t &expected_repetition_amount)
{
rust_assert (pattern_end < macro.size ());
bool first_fragment_found = false;
bool is_valid = true;
std::vector<std::unique_ptr<AST::Token>> expanded;
// Find the first fragment and get the amount of repetitions that we should
// perform
size_t repeat_amount = 0;
for (size_t i = pattern_start; i < pattern_end; i++)
{
if (macro.at (i)->get_id () == DOLLAR_SIGN)
@ -59,17 +55,48 @@ SubstituteCtx::substitute_repetition (
rust_error_at (frag_token->get_locus (),
"metavar %s used in repetition does not exist",
frag_token->get_str ().c_str ());
// FIXME:
return expanded;
is_valid = false;
}
// FIXME: Refactor, ugly
repeat_amount = it->second.get_match_amount ();
size_t repeat_amount = it->second.get_match_amount ();
if (!first_fragment_found)
{
first_fragment_found = true;
expected_repetition_amount = repeat_amount;
}
else
{
if (repeat_amount != expected_repetition_amount)
{
rust_error_at (
frag_token->get_locus (),
"different amount of matches used in merged "
"repetitions: expected %ld, got %ld",
expected_repetition_amount, repeat_amount);
is_valid = false;
}
}
}
}
}
return is_valid;
}
std::vector<std::unique_ptr<AST::Token>>
SubstituteCtx::substitute_repetition (
size_t pattern_start, size_t pattern_end,
std::unique_ptr<AST::Token> separator_token)
{
rust_assert (pattern_end < macro.size ());
size_t repeat_amount = 0;
if (!check_repetition_amount (pattern_start, pattern_end, repeat_amount))
return {};
rust_debug ("repetition amount to use: %lu", repeat_amount);
std::vector<std::unique_ptr<AST::Token>> expanded;
std::vector<std::unique_ptr<AST::Token>> new_macro;
// We want to generate a "new macro" to substitute with. This new macro

View File

@ -26,6 +26,17 @@ class SubstituteCtx
std::vector<std::unique_ptr<AST::Token>> &macro;
std::map<std::string, MatchedFragmentContainer> &fragments;
/**
* Find the repetition amount to use when expanding a repetition, and
* check that all fragments used respect that repetition amount
*
* @param pattern_start Start of the repetition pattern
* @param pattern_end End of the repetition pattern
* @param repeat_amount Reference to fill with the matched repetition amount
*/
bool check_repetition_amount (size_t pattern_start, size_t pattern_end,
size_t &repeat_amount);
public:
SubstituteCtx (std::vector<std::unique_ptr<AST::Token>> &input,
std::vector<std::unique_ptr<AST::Token>> &macro,

View File

@ -0,0 +1,10 @@
macro_rules! repeat {
( $( $i:literal ),* ; $( $j:literal ),* ) => (( $( ($i,$j) ),* ))
// { dg-error "different amount of matches used in merged repetitions" "" { target *-*-* } .-1 }
}
fn main() -> i32 {
let _ = repeat!(1, 2, 3; 2, 3);
0
}

View File

@ -0,0 +1,9 @@
macro_rules! repeat {
( $( $i:literal ),* ; $( $j:literal ),* ) => (( $( ($i,$j) ),* ))
}
fn main() -> i32 {
let t = repeat!(1, 1; 2, 2);
t.0 .0 + t.0 .1 + t.1 .0 + t.1 .1 - 6
}