diff --git a/chibicc.h b/chibicc.h index 4a09b19..9a16d39 100644 --- a/chibicc.h +++ b/chibicc.h @@ -75,6 +75,7 @@ struct Token { bool at_bol; // True if this token is at beginning of line bool has_space; // True if this token follows a space character Hideset *hideset; // For macro expansion + Token *origin; // If this is expanded from a macro, the original token }; void error(char *fmt, ...); diff --git a/preprocess.c b/preprocess.c index 5ba97c7..b388dcd 100644 --- a/preprocess.c +++ b/preprocess.c @@ -37,6 +37,8 @@ struct MacroArg { Token *tok; }; +typedef Token *macro_handler_fn(Token *); + typedef struct Macro Macro; struct Macro { Macro *next; @@ -45,6 +47,7 @@ struct Macro { MacroParam *params; Token *body; bool deleted; + macro_handler_fn *handler; }; // `#if` can be nested, so we use a stack to manage nested `#if`s. @@ -560,10 +563,19 @@ static bool expand_macro(Token **rest, Token *tok) { if (!m) return false; + // Built-in dynamic macro application such as __LINE__ + if (m->handler) { + *rest = m->handler(tok); + (*rest)->next = tok->next; + return true; + } + // Object-like macro application if (m->is_objlike) { Hideset *hs = hideset_union(tok->hideset, new_hideset(m->name)); Token *body = add_hideset(m->body, hs); + for (Token *t = body; t->kind != TK_EOF; t = t->next) + t->origin = tok; *rest = append(body, tok->next); (*rest)->at_bol = tok->at_bol; (*rest)->has_space = tok->has_space; @@ -590,6 +602,8 @@ static bool expand_macro(Token **rest, Token *tok) { Token *body = subst(m->body, args); body = add_hideset(body, hs); + for (Token *t = body; t->kind != TK_EOF; t = t->next) + t->origin = macro_token; *rest = append(body, tok->next); (*rest)->at_bol = macro_token->at_bol; (*rest)->has_space = macro_token->has_space; @@ -788,6 +802,24 @@ static void define_macro(char *name, char *buf) { add_macro(name, true, tok); } +static Macro *add_builtin(char *name, macro_handler_fn *fn) { + Macro *m = add_macro(name, true, NULL); + m->handler = fn; + return m; +} + +static Token *file_macro(Token *tmpl) { + while (tmpl->origin) + tmpl = tmpl->origin; + return new_str_token(tmpl->file->name, tmpl); +} + +static Token *line_macro(Token *tmpl) { + while (tmpl->origin) + tmpl = tmpl->origin; + return new_num_token(tmpl->line_no, tmpl); +} + static void init_macros(void) { // Define predefined macros define_macro("_LP64", "1"); @@ -831,6 +863,9 @@ static void init_macros(void) { define_macro("__x86_64__", "1"); define_macro("linux", "1"); define_macro("unix", "1"); + + add_builtin("__FILE__", file_macro); + add_builtin("__LINE__", line_macro); } // Entry point function of the preprocessor. diff --git a/test/include1.h b/test/include1.h index 885be36..9381511 100644 --- a/test/include1.h +++ b/test/include1.h @@ -1,3 +1,6 @@ #include "include2.h" +char *include1_filename = __FILE__; +int include1_line = __LINE__; + int include1 = 5; diff --git a/test/macro.c b/test/macro.c index 444aebd..6d63eaf 100644 --- a/test/macro.c +++ b/test/macro.c @@ -1,6 +1,11 @@ #include "test.h" #include "include1.h" +char *main_filename1 = __FILE__; +int main_line1 = __LINE__; +#define LINE() __LINE__ +int main_line2 = LINE(); + # /* */ # @@ -324,6 +329,12 @@ int main() { ASSERT(1, __STDC__); + ASSERT(0, strcmp(main_filename1, "test/macro.c")); + ASSERT(5, main_line1); + ASSERT(7, main_line2); + ASSERT(0, strcmp(include1_filename, "test/include1.h")); + ASSERT(4, include1_line); + printf("OK\n"); return 0; }