include, ifndef, endif

This commit is contained in:
sfja 2026-09-24 01:35:27 +02:00
parent a9d34f6725
commit 1c301c3fe3
5 changed files with 162 additions and 47 deletions

View File

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

9
examples/macro_usage.c Normal file
View File

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

View File

@ -1,2 +1,6 @@
#ifndef NOT_STDIO_H
#define NOT_STDIO_H
int puts(const char* __s);
#endif

3
main.c
View File

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

180
preproc.c
View File

@ -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);
if (p->curr.ty == Tok_Eol)
return p->curr;
if (p->curr.ty != Tok_Ident) {
fprintf(
stderr, "error: expected identifier on line %d\n", p->curr.line);
exit(1);
}
Tok kw = 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) {
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);
}
assert(false && "unreachable");
}
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);
}