Add multi-arity funclike #define

This commit is contained in:
Rui Ueyama 2020-03-30 10:37:44 +09:00
parent dec3b3fa02
commit b9ad3e43cf
2 changed files with 122 additions and 4 deletions

View File

@ -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;
}

View File

@ -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;
}