macros: add concat! macro

Signed-off-by: Zixing Liu <liushuyu011@gmail.com>
This commit is contained in:
liushuyu 2022-04-05 01:19:45 -06:00
parent e43a5c5373
commit fed5a41fb1
No known key found for this signature in database
GPG Key ID: 23D1CE4534419437
5 changed files with 102 additions and 9 deletions

View File

@ -34,17 +34,12 @@ make_string (Location locus, std::string value)
PrimitiveCoreType::CORETYPE_STR, {}, locus));
}
/* Parse a single string literal from the given delimited token tree,
and return the LiteralExpr for it. Allow for an optional trailing comma,
but otherwise enforce that these are the only tokens. */
/* Match the end token of a macro given the start delimiter of the macro */
std::unique_ptr<AST::LiteralExpr>
parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
Location invoc_locus)
static inline TokenId
macro_end_token (AST::DelimTokenTree &invoc_token_tree,
Parser<MacroInvocLexer> &parser)
{
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
Parser<MacroInvocLexer> parser (std::move (lex));
auto last_token_id = TokenId::RIGHT_CURLY;
switch (invoc_token_tree.get_delim_type ())
{
@ -63,6 +58,22 @@ parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
break;
}
return last_token_id;
}
/* Parse a single string literal from the given delimited token tree,
and return the LiteralExpr for it. Allow for an optional trailing comma,
but otherwise enforce that these are the only tokens. */
std::unique_ptr<AST::LiteralExpr>
parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
Location invoc_locus)
{
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
Parser<MacroInvocLexer> parser (std::move (lex));
auto last_token_id = macro_end_token (invoc_token_tree, parser);
std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
@ -252,4 +263,44 @@ MacroBuiltin::compile_error (Location invoc_locus, AST::MacroInvocData &invoc)
return AST::ASTFragment::create_error ();
}
/* Expand builtin macro concat!(), which joins all the literal parameters
into a string with no delimiter. */
AST::ASTFragment
MacroBuiltin::concat (Location invoc_locus, AST::MacroInvocData &invoc)
{
auto invoc_token_tree = invoc.get_delim_tok_tree ();
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
Parser<MacroInvocLexer> parser (std::move (lex));
auto str = std::string ();
bool has_error = false;
auto last_token_id = macro_end_token (invoc_token_tree, parser);
/* NOTE: concat! could accept no argument, so we don't have any checks here */
while (parser.peek_current_token ()->get_id () != last_token_id)
{
auto lit_expr = parser.parse_literal_expr ();
if (lit_expr)
{
str += lit_expr->as_string ();
}
else
{
rust_error_at (parser.peek_current_token ()->get_locus (),
"argument must be a constant literal");
has_error = true;
}
parser.maybe_skip_token (COMMA);
}
parser.skip_token (last_token_id);
if (has_error)
return AST::ASTFragment::create_error ();
auto node = AST::SingleASTNode (make_string (invoc_locus, str));
return AST::ASTFragment ({node});
}
} // namespace Rust

View File

@ -80,6 +80,9 @@ public:
static AST::ASTFragment compile_error (Location invoc_locus,
AST::MacroInvocData &invoc);
static AST::ASTFragment concat (Location invoc_locus,
AST::MacroInvocData &invoc);
};
} // namespace Rust

View File

@ -754,6 +754,7 @@ Mappings::insert_macro_def (AST::MacroRulesDefinition *macro)
{"include_bytes", MacroBuiltin::include_bytes},
{"include_str", MacroBuiltin::include_str},
{"compile_error", MacroBuiltin::compile_error},
{"concat", MacroBuiltin::concat},
};
auto builtin = builtin_macros.find (macro->get_rule_name ());

View File

@ -0,0 +1,15 @@
macro_rules! concat {
() => {{}};
}
fn main () {
let not_literal = "identifier";
concat! ();
concat! (,); // { dg-error "argument must be a constant literal" }
concat! (not_literal); // { dg-error "argument must be a constant literal" }
concat! ("message");
concat! ("message",);
concat! ("message",1, true, false, 1.0, 10usize, 2000u64);
concat! ("message",1, true, false, 1.0, 10usize, 2000u64,);
concat! ("m", not_literal); // { dg-error "argument must be a constant literal" }
}

View File

@ -0,0 +1,23 @@
// { dg-output "\ntest10btrue2.15\ntest10bfalse2.151\n" }
macro_rules! concat {
() => {{}};
}
extern "C" {
fn printf(fmt: *const i8, ...);
}
fn print(s: &str) {
printf("%s\n" as *const str as *const i8, s as *const str as *const i8);
}
fn main() -> i32 {
let a = concat!();
let b = concat!("test", 10, 'b', true, 2.15);
let c = concat!("test", 10, 'b', false, 2.15, 1u64);
print(a);
print(b);
print(c);
0
}