[GNU] Support GCC-style variadic macro

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

View File

@ -34,6 +34,7 @@ typedef struct MacroArg MacroArg;
struct MacroArg { struct MacroArg {
MacroArg *next; MacroArg *next;
char *name; char *name;
bool is_va_args;
Token *tok; Token *tok;
}; };
@ -45,7 +46,7 @@ struct Macro {
char *name; char *name;
bool is_objlike; // Object-like or function-like bool is_objlike; // Object-like or function-like
MacroParam *params; MacroParam *params;
bool is_variadic; char *va_args_name;
Token *body; Token *body;
bool deleted; bool deleted;
macro_handler_fn *handler; macro_handler_fn *handler;
@ -336,7 +337,7 @@ static Macro *add_macro(char *name, bool is_objlike, Token *body) {
return m; 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 head = {};
MacroParam *cur = &head; MacroParam *cur = &head;
@ -345,13 +346,20 @@ static MacroParam *read_macro_params(Token **rest, Token *tok, bool *is_variadic
tok = skip(tok, ","); tok = skip(tok, ",");
if (equal(tok, "...")) { if (equal(tok, "...")) {
*is_variadic = true; *va_args_name = "__VA_ARGS__";
*rest = skip(tok->next, ")"); *rest = skip(tok->next, ")");
return head.next; return head.next;
} }
if (tok->kind != TK_IDENT) if (tok->kind != TK_IDENT)
error_tok(tok, "expected an identifier"); 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)); MacroParam *m = calloc(1, sizeof(MacroParam));
m->name = strndup(tok->loc, tok->len); m->name = strndup(tok->loc, tok->len);
cur = cur->next = m; cur = cur->next = m;
@ -370,12 +378,12 @@ static void read_macro_definition(Token **rest, Token *tok) {
if (!tok->has_space && equal(tok, "(")) { if (!tok->has_space && equal(tok, "(")) {
// Function-like macro // Function-like macro
bool is_variadic = false; char *va_args_name = NULL;
MacroParam *params = read_macro_params(&tok, tok->next, &is_variadic); MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name);
Macro *m = add_macro(name, false, copy_line(rest, tok)); Macro *m = add_macro(name, false, copy_line(rest, tok));
m->params = params; m->params = params;
m->is_variadic = is_variadic; m->va_args_name = va_args_name;
} else { } else {
// Object-like macro // Object-like macro
add_macro(name, true, copy_line(rest, tok)); add_macro(name, true, copy_line(rest, tok));
@ -414,7 +422,7 @@ static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) {
} }
static MacroArg * 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; Token *start = tok;
tok = tok->next->next; tok = tok->next->next;
@ -429,7 +437,7 @@ read_macro_args(Token **rest, Token *tok, MacroParam *params, bool is_variadic)
cur->name = pp->name; cur->name = pp->name;
} }
if (is_variadic) { if (va_args_name) {
MacroArg *arg; MacroArg *arg;
if (equal(tok, ")")) { if (equal(tok, ")")) {
arg = calloc(1, sizeof(MacroArg)); arg = calloc(1, sizeof(MacroArg));
@ -439,7 +447,8 @@ read_macro_args(Token **rest, Token *tok, MacroParam *params, bool is_variadic)
tok = skip(tok, ","); tok = skip(tok, ",");
arg = read_macro_arg_one(&tok, tok, true); 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; cur = cur->next = arg;
} else if (pp) { } else if (pp) {
error_tok(start, "too many arguments"); error_tok(start, "too many arguments");
@ -531,7 +540,7 @@ static Token *subst(Token *tok, MacroArg *args) {
// __VA_ARGS__. // __VA_ARGS__.
if (equal(tok, ",") && equal(tok->next, "##")) { if (equal(tok, ",") && equal(tok->next, "##")) {
MacroArg *arg = find_arg(args, tok->next->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) { if (arg->tok->kind == TK_EOF) {
tok = tok->next->next->next; tok = tok->next->next->next;
} else { } else {
@ -657,7 +666,7 @@ static bool expand_macro(Token **rest, Token *tok) {
// Function-like macro application // Function-like macro application
Token *macro_token = tok; 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; Token *rparen = tok;
// Tokens that consist a func-like macro invocation may have different // 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) #define M14(x, ...) add6(1,2,x,__VA_ARGS__,6)
ASSERT(21, M14(3,4,5)); ASSERT(21, M14(3,4,5));
#define M14(args...) 3
ASSERT(3, M14());
#define M14(x, ...) x #define M14(x, ...) x
ASSERT(5, M14(5)); 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 #define CONCAT(x,y) x##y
ASSERT(5, ({ int f0zz=5; CONCAT(f,0zz); })); ASSERT(5, ({ int f0zz=5; CONCAT(f,0zz); }));
ASSERT(5, ({ CONCAT(4,.57) + 0.5; })); ASSERT(5, ({ CONCAT(4,.57) + 0.5; }));