[GNU] Support GCC-style variadic macro

This commit is contained in:
Rui Ueyama 2020-08-30 01:28:04 +09:00
parent 1ceec75f4d
commit 2a38d6471a
2 changed files with 39 additions and 11 deletions

View File

@ -34,6 +34,7 @@ typedef struct MacroArg MacroArg;
struct MacroArg {
MacroArg *next;
char *name;
bool is_va_args;
Token *tok;
};
@ -45,7 +46,7 @@ struct Macro {
char *name;
bool is_objlike; // Object-like or function-like
MacroParam *params;
bool is_variadic;
char *va_args_name;
Token *body;
bool deleted;
macro_handler_fn *handler;
@ -349,7 +350,7 @@ static Macro *add_macro(char *name, bool is_objlike, Token *body) {
return m;
}
static MacroParam *read_macro_params(Token **rest, Token *tok, bool *is_variadic) {
static MacroParam *read_macro_params(Token **rest, Token *tok, char **va_args_name) {
MacroParam head = {};
MacroParam *cur = &head;
@ -358,13 +359,20 @@ static MacroParam *read_macro_params(Token **rest, Token *tok, bool *is_variadic
tok = skip(tok, ",");
if (equal(tok, "...")) {
*is_variadic = true;
*va_args_name = "__VA_ARGS__";
*rest = skip(tok->next, ")");
return head.next;
}
if (tok->kind != TK_IDENT)
error_tok(tok, "expected an identifier");
if (equal(tok->next, "...")) {
*va_args_name = strndup(tok->loc, tok->len);
*rest = skip(tok->next->next, ")");
return head.next;
}
MacroParam *m = calloc(1, sizeof(MacroParam));
m->name = strndup(tok->loc, tok->len);
cur = cur->next = m;
@ -383,12 +391,12 @@ static void read_macro_definition(Token **rest, Token *tok) {
if (!tok->has_space && equal(tok, "(")) {
// Function-like macro
bool is_variadic = false;
MacroParam *params = read_macro_params(&tok, tok->next, &is_variadic);
char *va_args_name = NULL;
MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name);
Macro *m = add_macro(name, false, copy_line(rest, tok));
m->params = params;
m->is_variadic = is_variadic;
m->va_args_name = va_args_name;
} else {
// Object-like macro
add_macro(name, true, copy_line(rest, tok));
@ -427,7 +435,7 @@ static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) {
}
static MacroArg *
read_macro_args(Token **rest, Token *tok, MacroParam *params, bool is_variadic) {
read_macro_args(Token **rest, Token *tok, MacroParam *params, char *va_args_name) {
Token *start = tok;
tok = tok->next->next;
@ -442,7 +450,7 @@ read_macro_args(Token **rest, Token *tok, MacroParam *params, bool is_variadic)
cur->name = pp->name;
}
if (is_variadic) {
if (va_args_name) {
MacroArg *arg;
if (equal(tok, ")")) {
arg = calloc(1, sizeof(MacroArg));
@ -452,7 +460,8 @@ read_macro_args(Token **rest, Token *tok, MacroParam *params, bool is_variadic)
tok = skip(tok, ",");
arg = read_macro_arg_one(&tok, tok, true);
}
arg->name = "__VA_ARGS__";
arg->name = va_args_name;;
arg->is_va_args = true;
cur = cur->next = arg;
} else if (pp) {
error_tok(start, "too many arguments");
@ -545,7 +554,7 @@ static Token *subst(Token *tok, MacroArg *args) {
// __VA_ARGS__.
if (equal(tok, ",") && equal(tok->next, "##")) {
MacroArg *arg = find_arg(args, tok->next->next);
if (arg && !strcmp(arg->name, "__VA_ARGS__")) {
if (arg && arg->is_va_args) {
if (arg->tok->kind == TK_EOF) {
tok = tok->next->next->next;
} else {
@ -671,7 +680,7 @@ static bool expand_macro(Token **rest, Token *tok) {
// Function-like macro application
Token *macro_token = tok;
MacroArg *args = read_macro_args(&tok, tok, m->params, m->is_variadic);
MacroArg *args = read_macro_args(&tok, tok, m->params, m->va_args_name);
Token *rparen = tok;
// Tokens that consist a func-like macro invocation may have different

View File

@ -359,9 +359,28 @@ int main() {
#define M14(x, ...) add6(1,2,x,__VA_ARGS__,6)
ASSERT(21, M14(3,4,5));
#define M14(args...) 3
ASSERT(3, M14());
#define M14(x, ...) x
ASSERT(5, M14(5));
#define M14(args...) args
ASSERT(2, M14() 2);
ASSERT(5, M14(5));
#define M14(args...) add2(args)
ASSERT(8, M14(2, 6));
#define M14(args...) add6(1,2,args,6)
ASSERT(21, M14(3,4,5));
#define M14(x, args...) add6(1,2,x,args,6)
ASSERT(21, M14(3,4,5));
#define M14(x, args...) x
ASSERT(5, M14(5));
#define CONCAT(x,y) x##y
ASSERT(5, ({ int f0zz=5; CONCAT(f,0zz); }));
ASSERT(5, ({ CONCAT(4,.57) + 0.5; }));