Skip to content

Commit 7964655

Browse files
Merge #950
950: Match macro repetitions r=CohenArthur a=CohenArthur This PR adds support for matching macro invocations and counting the amount of times they've been matched Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
2 parents 4e096b1 + d0d4dcf commit 7964655

File tree

11 files changed

+265
-14
lines changed

11 files changed

+265
-14
lines changed

gcc/rust/ast/rust-ast-full-test.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,13 +2446,13 @@ MacroMatchRepetition::as_string () const
24462446
str += "\n Op: ";
24472447
switch (op)
24482448
{
2449-
case ASTERISK:
2449+
case ANY:
24502450
str += "*";
24512451
break;
2452-
case PLUS:
2452+
case ONE_OR_MORE:
24532453
str += "+";
24542454
break;
2455-
case QUESTION_MARK:
2455+
case ZERO_OR_ONE:
24562456
str += "?";
24572457
break;
24582458
case NONE:

gcc/rust/ast/rust-macro.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ class MacroMatchRepetition : public MacroMatch
134134
enum MacroRepOp
135135
{
136136
NONE,
137-
ASTERISK,
138-
PLUS,
139-
QUESTION_MARK
137+
ANY,
138+
ONE_OR_MORE,
139+
ZERO_OR_ONE,
140140
};
141141

142142
private:
@@ -206,6 +206,9 @@ class MacroMatchRepetition : public MacroMatch
206206
return MacroMatchType::Repetition;
207207
}
208208

209+
MacroRepOp get_op () const { return op; }
210+
std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
211+
209212
protected:
210213
/* Use covariance to implement clone function as returning this object rather
211214
* than base */

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

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,6 +3520,7 @@ MacroExpander::match_matcher (Parser<MacroInvocLexer> &parser,
35203520
for (auto &match : matcher.get_matches ())
35213521
{
35223522
size_t offs_begin = source.get_offs ();
3523+
35233524
switch (match->get_macro_match_type ())
35243525
{
35253526
case AST::MacroMatch::MacroMatchType::Fragment: {
@@ -3597,13 +3598,123 @@ MacroExpander::match_token (Parser<MacroInvocLexer> &parser, AST::Token &token)
35973598
return parser.skip_token (token.get_id ());
35983599
}
35993600

3601+
bool
3602+
MacroExpander::match_n_matches (
3603+
Parser<MacroInvocLexer> &parser,
3604+
std::vector<std::unique_ptr<AST::MacroMatch>> &matches, size_t &match_amount,
3605+
size_t lo_bound, size_t hi_bound)
3606+
{
3607+
match_amount = 0;
3608+
3609+
const MacroInvocLexer &source = parser.get_token_source ();
3610+
while (true)
3611+
{
3612+
// If the current token is a closing macro delimiter, break away.
3613+
// TODO: Is this correct?
3614+
auto t_id = parser.peek_current_token ()->get_id ();
3615+
if (t_id == RIGHT_PAREN || t_id == RIGHT_SQUARE || t_id == RIGHT_CURLY)
3616+
break;
3617+
3618+
bool valid_current_match = false;
3619+
for (auto &match : matches)
3620+
{
3621+
size_t offs_begin = source.get_offs ();
3622+
switch (match->get_macro_match_type ())
3623+
{
3624+
case AST::MacroMatch::MacroMatchType::Fragment: {
3625+
AST::MacroMatchFragment *fragment
3626+
= static_cast<AST::MacroMatchFragment *> (match.get ());
3627+
valid_current_match = match_fragment (parser, *fragment);
3628+
3629+
// matched fragment get the offset in the token stream
3630+
size_t offs_end = source.get_offs ();
3631+
sub_stack.peek ().insert (
3632+
{fragment->get_ident (),
3633+
{fragment->get_ident (), offs_begin, offs_end}});
3634+
}
3635+
break;
3636+
3637+
case AST::MacroMatch::MacroMatchType::Tok: {
3638+
AST::Token *tok = static_cast<AST::Token *> (match.get ());
3639+
valid_current_match = match_token (parser, *tok);
3640+
}
3641+
break;
3642+
3643+
case AST::MacroMatch::MacroMatchType::Repetition: {
3644+
AST::MacroMatchRepetition *rep
3645+
= static_cast<AST::MacroMatchRepetition *> (match.get ());
3646+
valid_current_match = match_repetition (parser, *rep);
3647+
}
3648+
break;
3649+
3650+
case AST::MacroMatch::MacroMatchType::Matcher: {
3651+
AST::MacroMatcher *m
3652+
= static_cast<AST::MacroMatcher *> (match.get ());
3653+
valid_current_match = match_matcher (parser, *m);
3654+
}
3655+
break;
3656+
}
3657+
}
3658+
// If we've encountered an error once, stop trying to match more
3659+
// repetitions
3660+
if (!valid_current_match)
3661+
break;
3662+
3663+
match_amount++;
3664+
3665+
// Break early if we notice there's too many expressions already
3666+
if (hi_bound && match_amount > hi_bound)
3667+
break;
3668+
}
3669+
3670+
// Check if the amount of matches we got is valid: Is it more than the lower
3671+
// bound and less than the higher bound?
3672+
if (!hi_bound) // infinite amount, no upper bound
3673+
return match_amount >= lo_bound;
3674+
else
3675+
return match_amount >= lo_bound && match_amount <= hi_bound;
3676+
}
3677+
36003678
bool
36013679
MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
36023680
AST::MacroMatchRepetition &rep)
36033681
{
3604-
// TODO
3605-
gcc_unreachable ();
3606-
return false;
3682+
size_t match_amount = 0;
3683+
bool res = false;
3684+
3685+
std::string lo_str;
3686+
std::string hi_str;
3687+
switch (rep.get_op ())
3688+
{
3689+
case AST::MacroMatchRepetition::MacroRepOp::ANY:
3690+
lo_str = "0";
3691+
hi_str = "+inf";
3692+
res = match_n_matches (parser, rep.get_matches (), match_amount);
3693+
break;
3694+
case AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE:
3695+
lo_str = "1";
3696+
hi_str = "+inf";
3697+
res = match_n_matches (parser, rep.get_matches (), match_amount, 1);
3698+
break;
3699+
case AST::MacroMatchRepetition::MacroRepOp::ZERO_OR_ONE:
3700+
lo_str = "0";
3701+
hi_str = "1";
3702+
res = match_n_matches (parser, rep.get_matches (), match_amount, 0, 1);
3703+
break;
3704+
default:
3705+
gcc_unreachable ();
3706+
}
3707+
3708+
if (!res)
3709+
rust_error_at (rep.get_match_locus (),
3710+
"invalid amount of matches for macro invocation. Expected "
3711+
"between %s and %s, got %lu",
3712+
lo_str.c_str (), hi_str.c_str (), match_amount);
3713+
3714+
rust_debug_loc (rep.get_match_locus (), "%s matched %lu times",
3715+
res ? "successfully" : "unsuccessfully", match_amount);
3716+
3717+
return res;
36073718
}
36083719

36093720
AST::ASTFragment

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,31 @@ struct MacroExpander
193193
bool match_matcher (Parser<MacroInvocLexer> &parser,
194194
AST::MacroMatcher &matcher);
195195

196+
/**
197+
* Match any amount of matches
198+
*
199+
* @param parser Parser to use for matching
200+
* @param matches All consecutive matches to identify
201+
* @param match_amount Reference in which to store the ammount of succesful
202+
* and valid matches
203+
*
204+
* @param lo_bound Lower bound of the matcher. When specified, the matcher
205+
* will only succeed if it parses at *least* `lo_bound` fragments. If
206+
* unspecified, the matcher could succeed when parsing 0 fragments.
207+
*
208+
* @param hi_bound Higher bound of the matcher. When specified, the matcher
209+
* will only succeed if it parses *less than* `hi_bound` fragments. If
210+
* unspecified, the matcher could succeed when parsing an infinity of
211+
* fragments.
212+
*
213+
* @return true if matching was successful and within the given limits, false
214+
* otherwise
215+
*/
216+
bool match_n_matches (Parser<MacroInvocLexer> &parser,
217+
std::vector<std::unique_ptr<AST::MacroMatch>> &matches,
218+
size_t &match_amount, size_t lo_bound = 0,
219+
size_t hi_bound = 0);
220+
196221
static std::vector<std::unique_ptr<AST::Token>>
197222
substitute_tokens (std::vector<std::unique_ptr<AST::Token>> &input,
198223
std::vector<std::unique_ptr<AST::Token>> &macro,

gcc/rust/parse/rust-parse-impl.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,20 +1991,19 @@ Parser<ManagedTokenSource>::parse_macro_match_repetition ()
19911991

19921992
// parse repetition operator
19931993
t = lexer.peek_token ();
1994-
AST::MacroMatchRepetition::MacroRepOp op
1995-
= AST::MacroMatchRepetition::ASTERISK;
1994+
AST::MacroMatchRepetition::MacroRepOp op = AST::MacroMatchRepetition::NONE;
19961995
switch (t->get_id ())
19971996
{
19981997
case ASTERISK:
1999-
op = AST::MacroMatchRepetition::ASTERISK;
1998+
op = AST::MacroMatchRepetition::ANY;
20001999
lexer.skip_token ();
20012000
break;
20022001
case PLUS:
2003-
op = AST::MacroMatchRepetition::PLUS;
2002+
op = AST::MacroMatchRepetition::ONE_OR_MORE;
20042003
lexer.skip_token ();
20052004
break;
20062005
case QUESTION_MARK:
2007-
op = AST::MacroMatchRepetition::QUESTION_MARK;
2006+
op = AST::MacroMatchRepetition::ZERO_OR_ONE;
20082007
lexer.skip_token ();
20092008
break;
20102009
default:

gcc/testsuite/rust/compile/macro6.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
macro_rules! zero_or_one {
2+
($($a:literal)?) => { // { dg-error "invalid amount of matches for macro invocation. Expected between 0 and 1, got 2" }
3+
f()
4+
}
5+
}
6+
7+
fn main() {
8+
zero_or_one!();
9+
zero_or_one!(14);
10+
zero_or_one!(125 12 "gcc"); // { dg-error "Failed to match any rule within macro" }
11+
}

gcc/testsuite/rust/compile/macro7.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fn f() {}
2+
3+
macro_rules! one_or_more {
4+
($($a:literal)+) => { // { dg-error "invalid amount of matches for macro invocation" }
5+
f()
6+
}
7+
}
8+
9+
fn main() {
10+
one_or_more!(1 1 1 1 1 1 1 1 1 1 1 "rust" 'c');
11+
one_or_more!(1);
12+
one_or_more!(); // { dg-error "Failed to match any rule within macro" }
13+
}

gcc/testsuite/rust/compile/macro8.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn f() {}
2+
3+
macro_rules! expr {
4+
($($a:expr)?) => {
5+
f()
6+
}
7+
}
8+
9+
fn main() {
10+
expr!();
11+
expr!(14);
12+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// { dg-output "any\nany\nany\n" }
2+
extern "C" {
3+
fn printf(s: *const i8, ...);
4+
}
5+
6+
fn f() {
7+
let r_s = "any\n\0";
8+
let s_p = r_s as *const str;
9+
let c_p = s_p as *const i8;
10+
11+
unsafe { printf(c_p); }
12+
}
13+
14+
macro_rules! any {
15+
($($a:expr)*) => {
16+
f()
17+
}
18+
}
19+
20+
fn main() -> i32 {
21+
any!();
22+
any!(a + b);
23+
any!(a + b 14 "gcc");
24+
25+
0
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// { dg-output "zo1\nzo1\n" }
2+
extern "C" {
3+
fn printf(s: *const i8, ...);
4+
}
5+
6+
fn f() {
7+
let r_s = "zo1\n\0";
8+
let s_p = r_s as *const str;
9+
let c_p = s_p as *const i8;
10+
11+
unsafe { printf(c_p); }
12+
}
13+
14+
macro_rules! zero_or_one {
15+
($($a:expr)?) => {
16+
f()
17+
}
18+
}
19+
20+
fn main() -> i32 {
21+
zero_or_one!();
22+
zero_or_one!(f());
23+
24+
0
25+
}

0 commit comments

Comments
 (0)