diff --git a/examples/hello.c b/examples/hello.c index 1cb8d44..6daaf9f 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -1,11 +1,6 @@ #include "not_stdio.h" -#define B(V, W) A(V) -#define D B -#define C D("hello world", 123) -#define A puts - int main() { - C; + puts("hello world"); } diff --git a/examples/macro_usage.c b/examples/macro_usage.c new file mode 100644 index 0000000..e72852f --- /dev/null +++ b/examples/macro_usage.c @@ -0,0 +1,9 @@ +#define B(V, W) A(V) +#define D B +#define C D("hello world", 123) +#define A puts + +int main() +{ + C; +} diff --git a/examples/not_stdio.h b/examples/not_stdio.h index 5423560..3107203 100644 --- a/examples/not_stdio.h +++ b/examples/not_stdio.h @@ -1,2 +1,6 @@ +#ifndef NOT_STDIO_H +#define NOT_STDIO_H int puts(const char* __s); + +#endif diff --git a/main.c b/main.c index bd75a84..c57a951 100644 --- a/main.c +++ b/main.c @@ -30,6 +30,9 @@ int main(int argc, char* argv[]) Tok tok; while ((tok = preproc_next(preproc)).ty != Tok_Eof) { // printf("%s\t%s\n", tokty_str(tok.ty), tok.text); + if (line > tok.line) { + line = 0; + } while (line < tok.line) { printf("\n"); line += 1; diff --git a/preproc.c b/preproc.c index 3673e61..e86ac86 100644 --- a/preproc.c +++ b/preproc.c @@ -68,6 +68,28 @@ static size_t* syms_get(Syms* s, const char* ident) return nullptr; } +typedef struct { + size_t conds; + size_t depth; +} CondStack; + +void condstack_push(CondStack* s, bool cond) +{ + if (cond) { + s->conds |= 1ul << s->depth; + } else { + s->conds &= ~(1ul << s->depth); + } + s->depth += 1; +} + +bool condstack_pop(CondStack* s) +{ + assert(s->depth > 0); + s->depth -= 1; + return (s->conds >> s->depth) & 1; +} + typedef struct { char* filename; char* text; @@ -94,6 +116,8 @@ struct Preproc { ARRAY_FIELD(Def, defs); Syms syms; + CondStack condstack; + bool next_can_be_directive; }; @@ -102,14 +126,16 @@ void preproc_init(Preproc** p, char* filename, char* text) *p = malloc(sizeof(Preproc)); **p = (Preproc) { + .tok_arena = { }, ARRAY_FIELD_INIT(files), + ARRAY_FIELD_INIT(tok_stack), .curr = { }, ARRAY_FIELD_INIT(defs), .syms = { }, - .tok_arena = { }, + .condstack = { }, .next_can_be_directive = true, }; @@ -409,6 +435,8 @@ static Tok do_undef(Preproc* p) return p->curr; } +Tok preproc_next_with_newline(Preproc* p); + static Tok do_include(Preproc* p) { File* curr_file = &p->files[p->files_size - 1]; @@ -470,60 +498,136 @@ static Tok do_include(Preproc* p) lexer_init(&file->lexer, &file->lsl_iter, &p->tok_arena); p->curr = lexer_next(&file->lexer); + p->next_can_be_directive = true; + + return preproc_next_with_newline(p); +} + +static Tok interpret_directive(Preproc* p); + +static Tok do_ifndef(Preproc* p) +{ + int original_line = p->curr.line; + + step(p); + if (p->curr.ty != Tok_Ident) { + fprintf( + stderr, "error: expected identifier on line %d\n", p->curr.line); + exit(1); + } + Tok ident = p->curr; + step(p); + + if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) { + fprintf(stderr, + "error: unexpected extra tokens on line %d\n", + p->curr.line); + exit(1); + } + + bool cond = syms_get(&p->syms, ident.text) == nullptr; + + condstack_push(&p->condstack, cond); + + if (!cond) { + + while (p->curr.ty != Tok_Eof) { + while (p->curr.ty != Tok_Eof + && (!p->next_can_be_directive || p->curr.ty != '#')) + step(p); + if (p->curr.ty == Tok_Eof) + break; + step(p); + if (p->curr.ty != Tok_Ident) + continue; + if (strcmp(p->curr.text, "endif") == 0) + return interpret_directive(p); + } + + if (p->curr.ty != Tok_Eof) { + fprintf(stderr, + "error: unclosed '#ifndef' on line %d\n", + original_line); + exit(1); + } + } + + return p->curr; +} + +static Tok do_endif(Preproc* p) +{ + step(p); + + if (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) { + fprintf(stderr, + "error: unexpected extra tokens on line %d\n", + p->curr.line); + exit(1); + } + + condstack_pop(&p->condstack); + return p->curr; } static Tok interpret_directive(Preproc* p) { - assert(p->curr.ty == '#'); - step(p); - Tok kw = p->curr; + if (p->curr.ty == Tok_Eol) + return p->curr; - if (strcmp(kw.text, "define") == 0) { - return do_define(p); - } else if (strcmp(kw.text, "undef") == 0) { - return do_undef(p); - } else if (strcmp(kw.text, "include") == 0) { - return do_include(p); - } else if (strcmp(kw.text, "if") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "ifdef") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "ifndef") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "else") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "elif") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "elifdef") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "elifndef") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "endif") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "line") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "embed") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "error") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "warning") == 0) { - assert(false && "not implemented"); - } else if (strcmp(kw.text, "pragma") == 0) { - fprintf(stderr, - "error: '%s' is not a preprocessor instruction on line %d\n", - kw.text, - kw.line); + if (p->curr.ty != Tok_Ident) { + fprintf( + stderr, "error: expected identifier on line %d\n", p->curr.line); exit(1); } - assert(false && "unreachable"); + Tok kw = p->curr; + + struct { + const char* keyword; + Tok (*fn)(Preproc* p); + } directives[] = { + { "define", do_define }, + { "undef", do_undef }, + { "include", do_include }, + { "ifndef", do_ifndef }, + { "endif", do_endif }, + + { "if", nullptr }, + { "ifdef", nullptr }, + { "else", nullptr }, + { "elif", nullptr }, + { "elifdef", nullptr }, + { "elifndef", nullptr }, + { "line", nullptr }, + { "embed", nullptr }, + { "error", nullptr }, + { "warning", nullptr }, + { "pragma", nullptr }, + }; + + for (size_t i = 0; i < sizeof(directives) / sizeof(directives[0]); ++i) { + if (strcmp(kw.text, directives[i].keyword) == 0) { + assert(directives[i].fn && "not implemented"); + + return directives[i].fn(p); + } + } + + fprintf(stderr, + "error: '%s' is not a preprocessor instruction on line %d\n", + kw.text, + kw.line); + exit(1); } Tok preproc_next_with_newline(Preproc* p) { - if (p->next_can_be_directive && p->curr.ty == '#') + if (p->next_can_be_directive && p->curr.ty == '#') { + step(p); return interpret_directive(p); + } return expand_tok(p); }