mirror of https://github.com/rui314/chibicc
Add macro token-pasting operator (##)
This commit is contained in:
parent
8f6f7925a0
commit
8f561aed9b
59
preprocess.c
59
preprocess.c
|
@ -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)
|
||||
|
|
20
test/macro.c
20
test/macro.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ static int read_punct(char *p) {
|
|||
static char *kw[] = {
|
||||
"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=",
|
||||
"-=", "*=", "/=", "++", "--", "%=", "&=", "|=", "^=", "&&",
|
||||
"||", "<<", ">>",
|
||||
"||", "<<", ">>", "##",
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
|
||||
|
|
Loading…
Reference in New Issue