Add macro token-pasting operator (##)

This commit is contained in:
Rui Ueyama 2020-08-30 01:05:28 +09:00
parent 8f6f7925a0
commit 8f561aed9b
3 changed files with 79 additions and 2 deletions

View File

@ -403,6 +403,18 @@ static Token *stringize(Token *hash, Token *arg) {
return new_str_token(s, hash);
}
// Concatenate two tokens to create a new token.
static Token *paste(Token *lhs, Token *rhs) {
// Paste the two tokens.
char *buf = format("%.*s%.*s", lhs->len, lhs->loc, rhs->len, rhs->loc);
// Tokenize the resulting string.
Token *tok = tokenize(new_file(lhs->file->name, lhs->file->file_no, buf));
if (tok->next->kind != TK_EOF)
error_tok(lhs, "pasting forms '%s', an invalid token", buf);
return tok;
}
// Replace func-like macro parameters with given arguments.
static Token *subst(Token *tok, MacroArg *args) {
Token head = {};
@ -419,9 +431,54 @@ static Token *subst(Token *tok, MacroArg *args) {
continue;
}
if (equal(tok, "##")) {
if (cur == &head)
error_tok(tok, "'##' cannot appear at start of macro expansion");
if (tok->next->kind == TK_EOF)
error_tok(tok, "'##' cannot appear at end of macro expansion");
MacroArg *arg = find_arg(args, tok->next);
if (arg) {
if (arg->tok->kind != TK_EOF) {
*cur = *paste(cur, arg->tok);
for (Token *t = arg->tok->next; t->kind != TK_EOF; t = t->next)
cur = cur->next = copy_token(t);
}
tok = tok->next->next;
continue;
}
*cur = *paste(cur, tok->next);
tok = tok->next->next;
continue;
}
MacroArg *arg = find_arg(args, tok);
if (arg && equal(tok->next, "##")) {
Token *rhs = tok->next->next;
if (arg->tok->kind == TK_EOF) {
MacroArg *arg2 = find_arg(args, rhs);
if (arg2) {
for (Token *t = arg2->tok; t->kind != TK_EOF; t = t->next)
cur = cur->next = copy_token(t);
} else {
cur = cur->next = copy_token(rhs);
}
tok = rhs->next;
continue;
}
for (Token *t = arg->tok; t->kind != TK_EOF; t = t->next)
cur = cur->next = copy_token(t);
tok = tok->next;
continue;
}
// Handle a macro token. Macro arguments are completely macro-expanded
// before they are substituted into a macro body.
MacroArg *arg = find_arg(args, tok);
if (arg) {
Token *t = preprocess2(arg->tok);
for (; t->kind != TK_EOF; t = t->next)

View File

@ -238,6 +238,26 @@ int main() {
assert('c', M11( a!b `""c)[7], "M11( a!b `\"\"c)[7]");
assert(0, M11( a!b `""c)[8], "M11( a!b `\"\"c)[8]");
#define paste(x,y) x##y
assert(15, paste(1,5), "paste(1,5)");
assert(255, paste(0,xff), "paste(0,xff)");
assert(3, ({ int foobar=3; paste(foo,bar); }), "({ int foobar=3; paste(foo,bar); })");
assert(5, paste(5,), "paste(5,)");
assert(5, paste(,5), "paste(,5)");
#define i 5
assert(101, ({ int i3=100; paste(1+i,3); }), "({ int i3=100; paste(1+i,3); })");
#undef i
#define paste2(x) x##5
assert(26, paste2(1+2), "paste2(1+2)");
#define paste3(x) 2##x
assert(23, paste3(1+2), "paste3(1+2)");
#define paste4(x, y, z) x##y##z
assert(123, paste4(1,2,3), "paste4(1,2,3)");
printf("OK\n");
return 0;
}

View File

@ -137,7 +137,7 @@ static int read_punct(char *p) {
static char *kw[] = {
"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=",
"-=", "*=", "/=", "++", "--", "%=", "&=", "|=", "^=", "&&",
"||", "<<", ">>",
"||", "<<", ">>", "##",
};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)