Skip to content

Commit fed5a41

Browse files
committed
macros: add concat! macro
Signed-off-by: Zixing Liu <liushuyu011@gmail.com>
1 parent e43a5c5 commit fed5a41

File tree

5 files changed

+102
-9
lines changed

5 files changed

+102
-9
lines changed

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

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,12 @@ make_string (Location locus, std::string value)
3434
PrimitiveCoreType::CORETYPE_STR, {}, locus));
3535
}
3636

37-
/* Parse a single string literal from the given delimited token tree,
38-
and return the LiteralExpr for it. Allow for an optional trailing comma,
39-
but otherwise enforce that these are the only tokens. */
37+
/* Match the end token of a macro given the start delimiter of the macro */
4038

41-
std::unique_ptr<AST::LiteralExpr>
42-
parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
43-
Location invoc_locus)
39+
static inline TokenId
40+
macro_end_token (AST::DelimTokenTree &invoc_token_tree,
41+
Parser<MacroInvocLexer> &parser)
4442
{
45-
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
46-
Parser<MacroInvocLexer> parser (std::move (lex));
47-
4843
auto last_token_id = TokenId::RIGHT_CURLY;
4944
switch (invoc_token_tree.get_delim_type ())
5045
{
@@ -63,6 +58,22 @@ parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
6358
break;
6459
}
6560

61+
return last_token_id;
62+
}
63+
64+
/* Parse a single string literal from the given delimited token tree,
65+
and return the LiteralExpr for it. Allow for an optional trailing comma,
66+
but otherwise enforce that these are the only tokens. */
67+
68+
std::unique_ptr<AST::LiteralExpr>
69+
parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
70+
Location invoc_locus)
71+
{
72+
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
73+
Parser<MacroInvocLexer> parser (std::move (lex));
74+
75+
auto last_token_id = macro_end_token (invoc_token_tree, parser);
76+
6677
std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
6778

6879
if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
@@ -252,4 +263,44 @@ MacroBuiltin::compile_error (Location invoc_locus, AST::MacroInvocData &invoc)
252263
return AST::ASTFragment::create_error ();
253264
}
254265

266+
/* Expand builtin macro concat!(), which joins all the literal parameters
267+
into a string with no delimiter. */
268+
269+
AST::ASTFragment
270+
MacroBuiltin::concat (Location invoc_locus, AST::MacroInvocData &invoc)
271+
{
272+
auto invoc_token_tree = invoc.get_delim_tok_tree ();
273+
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
274+
Parser<MacroInvocLexer> parser (std::move (lex));
275+
auto str = std::string ();
276+
bool has_error = false;
277+
278+
auto last_token_id = macro_end_token (invoc_token_tree, parser);
279+
280+
/* NOTE: concat! could accept no argument, so we don't have any checks here */
281+
while (parser.peek_current_token ()->get_id () != last_token_id)
282+
{
283+
auto lit_expr = parser.parse_literal_expr ();
284+
if (lit_expr)
285+
{
286+
str += lit_expr->as_string ();
287+
}
288+
else
289+
{
290+
rust_error_at (parser.peek_current_token ()->get_locus (),
291+
"argument must be a constant literal");
292+
has_error = true;
293+
}
294+
parser.maybe_skip_token (COMMA);
295+
}
296+
297+
parser.skip_token (last_token_id);
298+
299+
if (has_error)
300+
return AST::ASTFragment::create_error ();
301+
302+
auto node = AST::SingleASTNode (make_string (invoc_locus, str));
303+
return AST::ASTFragment ({node});
304+
}
305+
255306
} // namespace Rust

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class MacroBuiltin
8080

8181
static AST::ASTFragment compile_error (Location invoc_locus,
8282
AST::MacroInvocData &invoc);
83+
84+
static AST::ASTFragment concat (Location invoc_locus,
85+
AST::MacroInvocData &invoc);
8386
};
8487
} // namespace Rust
8588

gcc/rust/util/rust-hir-map.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ Mappings::insert_macro_def (AST::MacroRulesDefinition *macro)
754754
{"include_bytes", MacroBuiltin::include_bytes},
755755
{"include_str", MacroBuiltin::include_str},
756756
{"compile_error", MacroBuiltin::compile_error},
757+
{"concat", MacroBuiltin::concat},
757758
};
758759

759760
auto builtin = builtin_macros.find (macro->get_rule_name ());
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
macro_rules! concat {
2+
() => {{}};
3+
}
4+
5+
fn main () {
6+
let not_literal = "identifier";
7+
concat! ();
8+
concat! (,); // { dg-error "argument must be a constant literal" }
9+
concat! (not_literal); // { dg-error "argument must be a constant literal" }
10+
concat! ("message");
11+
concat! ("message",);
12+
concat! ("message",1, true, false, 1.0, 10usize, 2000u64);
13+
concat! ("message",1, true, false, 1.0, 10usize, 2000u64,);
14+
concat! ("m", not_literal); // { dg-error "argument must be a constant literal" }
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// { dg-output "\ntest10btrue2.15\ntest10bfalse2.151\n" }
2+
macro_rules! concat {
3+
() => {{}};
4+
}
5+
6+
extern "C" {
7+
fn printf(fmt: *const i8, ...);
8+
}
9+
10+
fn print(s: &str) {
11+
printf("%s\n" as *const str as *const i8, s as *const str as *const i8);
12+
}
13+
14+
fn main() -> i32 {
15+
let a = concat!();
16+
let b = concat!("test", 10, 'b', true, 2.15);
17+
let c = concat!("test", 10, 'b', false, 2.15, 1u64);
18+
print(a);
19+
print(b);
20+
print(c);
21+
22+
0
23+
}

0 commit comments

Comments
 (0)