include, ifndef, endif
This commit is contained in:
parent
a9d34f6725
commit
1c301c3fe3
@ -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
9
examples/macro_usage.c
Normal 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;
|
||||
}
|
||||
@ -1,2 +1,6 @@
|
||||
#ifndef NOT_STDIO_H
|
||||
#define NOT_STDIO_H
|
||||
|
||||
int puts(const char* __s);
|
||||
|
||||
#endif
|
||||
|
||||
3
main.c
3
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;
|
||||
|
||||
180
preproc.c
180
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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user