diff --git a/preprocess.c b/preprocess.c index 36946c6a18a5d9e3c604689c821390a8326b31f6..e110a860e5216c04c938a0f2444932fd0a5b8808 100644 --- a/preprocess.c +++ b/preprocess.c @@ -23,11 +23,25 @@ #include "chibicc.h" +typedef struct MacroParam MacroParam; +struct MacroParam { + MacroParam *next; + char *name; +}; + +typedef struct MacroArg MacroArg; +struct MacroArg { + MacroArg *next; + char *name; + Token *tok; +}; + typedef struct Macro Macro; struct Macro { Macro *next; char *name; bool is_objlike; // Object-like or function-like + MacroParam *params; Token *body; bool deleted; }; @@ -226,6 +240,25 @@ static Macro *add_macro(char *name, bool is_objlike, Token *body) { return m; } +static MacroParam *read_macro_params(Token **rest, Token *tok) { + MacroParam head = {}; + MacroParam *cur = &head; + + while (!equal(tok, ")")) { + if (cur != &head) + tok = skip(tok, ","); + + if (tok->kind != TK_IDENT) + error_tok(tok, "expected an identifier"); + MacroParam *m = calloc(1, sizeof(MacroParam)); + m->name = strndup(tok->loc, tok->len); + cur = cur->next = m; + tok = tok->next; + } + *rest = tok->next; + return head.next; +} + static void read_macro_definition(Token **rest, Token *tok) { if (tok->kind != TK_IDENT) error_tok(tok, "macro name must be an identifier"); @@ -234,14 +267,90 @@ static void read_macro_definition(Token **rest, Token *tok) { if (!tok->has_space && equal(tok, "(")) { // Function-like macro - tok = skip(tok->next, ")"); - add_macro(name, false, copy_line(rest, tok)); + MacroParam *params = read_macro_params(&tok, tok->next); + Macro *m = add_macro(name, false, copy_line(rest, tok)); + m->params = params; } else { // Object-like macro add_macro(name, true, copy_line(rest, tok)); } } +static MacroArg *read_macro_arg_one(Token **rest, Token *tok) { + Token head = {}; + Token *cur = &head; + + while (!equal(tok, ",") && !equal(tok, ")")) { + if (tok->kind == TK_EOF) + error_tok(tok, "premature end of input"); + cur = cur->next = copy_token(tok); + tok = tok->next; + } + + cur->next = new_eof(tok); + + MacroArg *arg = calloc(1, sizeof(MacroArg)); + arg->tok = head.next; + *rest = tok; + return arg; +} + +static MacroArg *read_macro_args(Token **rest, Token *tok, MacroParam *params) { + Token *start = tok; + tok = tok->next->next; + + MacroArg head = {}; + MacroArg *cur = &head; + + MacroParam *pp = params; + for (; pp; pp = pp->next) { + if (cur != &head) + tok = skip(tok, ","); + cur = cur->next = read_macro_arg_one(&tok, tok); + cur->name = pp->name; + } + + if (pp) + error_tok(start, "too many arguments"); + *rest = skip(tok, ")"); + return head.next; +} + +static MacroArg *find_arg(MacroArg *args, Token *tok) { + for (MacroArg *ap = args; ap; ap = ap->next) + if (tok->len == strlen(ap->name) && !strncmp(tok->loc, ap->name, tok->len)) + return ap; + return NULL; +} + +// Replace func-like macro parameters with given arguments. +static Token *subst(Token *tok, MacroArg *args) { + Token head = {}; + Token *cur = &head; + + while (tok->kind != TK_EOF) { + MacroArg *arg = find_arg(args, tok); + + // Handle a macro token. Macro arguments are completely macro-expanded + // before they are substituted into a macro body. + if (arg) { + Token *t = preprocess2(arg->tok); + for (; t->kind != TK_EOF; t = t->next) + cur = cur->next = copy_token(t); + tok = tok->next; + continue; + } + + // Handle a non-macro token. + cur = cur->next = copy_token(tok); + tok = tok->next; + continue; + } + + cur->next = tok; + return head.next; +} + // If tok is a macro, expand it and return true. // Otherwise, do nothing and return false. static bool expand_macro(Token **rest, Token *tok) { @@ -266,8 +375,8 @@ static bool expand_macro(Token **rest, Token *tok) { return false; // Function-like macro application - tok = skip(tok->next->next, ")"); - *rest = append(m->body, tok); + MacroArg *args = read_macro_args(&tok, tok, m->params); + *rest = append(subst(m->body, args), tok); return true; } diff --git a/test/macro.c b/test/macro.c index 99e1a86716c78cbe4176afcacf50131e92c71e8f..c6508b1d4f512aafe4c84097ed6d8016a7107f97 100644 --- a/test/macro.c +++ b/test/macro.c @@ -204,6 +204,15 @@ int main() { #define M7 () assert(3, ret3 M7, "ret3 M7"); +#define M8(x,y) x+y + assert(7, M8(3, 4), "M8(3, 4)"); + +#define M8(x,y) x*y + assert(24, M8(3+4, 4+5), "M8(3+4, 4+5)"); + +#define M8(x,y) (x)*(y) + assert(63, M8(3+4, 4+5), "M8(3+4, 4+5)"); + printf("OK\n"); return 0; }