208 lines
5.1 KiB
C
208 lines
5.1 KiB
C
#include "collection.h"
|
|
#include "parse.h"
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct DefToks {
|
|
Tok* toks;
|
|
size_t toks_cap;
|
|
size_t toks_size;
|
|
};
|
|
|
|
void preproc_init(Preproc* p, Lexer* lexer)
|
|
{
|
|
Tok curr = lexer_next(lexer);
|
|
|
|
*p = (Preproc) {
|
|
.lexer = lexer,
|
|
.curr = curr,
|
|
.deftoks = nullptr,
|
|
.deftoks_cap = 0,
|
|
.deftoks_size = 0,
|
|
.defs = { },
|
|
.pasting_toks = nullptr,
|
|
.pasting_toks_count = 0,
|
|
};
|
|
|
|
hashmap_init(&p->defs);
|
|
}
|
|
|
|
void preproc_deinit(Preproc* p)
|
|
{
|
|
if (p->deftoks) {
|
|
for (size_t i = 0; i < p->deftoks_size; ++i) {
|
|
free(p->deftoks[i].toks);
|
|
}
|
|
free(p->deftoks);
|
|
}
|
|
hashmap_deinit(&p->defs);
|
|
}
|
|
|
|
static void step(Preproc* p)
|
|
{
|
|
p->curr = lexer_next(p->lexer);
|
|
}
|
|
|
|
static Tok p_define(Preproc* p)
|
|
{
|
|
step(p);
|
|
if (p->curr.ty != Tok_Ident) {
|
|
fprintf(
|
|
stderr, "error: expected identifier on line %d\n", p->curr.line);
|
|
exit(1);
|
|
}
|
|
Tok ident_tok = p->curr;
|
|
|
|
p->lexer->lex_whitespace = true;
|
|
step(p);
|
|
p->lexer->lex_whitespace = false;
|
|
|
|
if (p->curr.ty == '(') {
|
|
// function-like macro
|
|
assert(false && "not implemented");
|
|
}
|
|
|
|
DefToks toks = { };
|
|
|
|
while (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) {
|
|
array_push((void**)&toks.toks,
|
|
&toks.toks_cap,
|
|
&toks.toks_size,
|
|
&p->curr,
|
|
sizeof(p->curr));
|
|
step(p);
|
|
}
|
|
|
|
array_push((void**)&p->deftoks,
|
|
&p->deftoks_cap,
|
|
&p->deftoks_size,
|
|
&toks,
|
|
sizeof(toks));
|
|
|
|
DefToks* deftoks = &p->deftoks[p->deftoks_size - 1];
|
|
hashmap_set(&p->defs, ident_tok.text, deftoks);
|
|
|
|
return p->curr;
|
|
}
|
|
|
|
static Tok p_undef(Preproc* p)
|
|
{
|
|
step(p);
|
|
|
|
if (p->curr.ty != Tok_Ident) {
|
|
fprintf(
|
|
stderr, "error: expected identifier on line %d\n", p->curr.line);
|
|
exit(1);
|
|
}
|
|
Tok ident_tok = p->curr;
|
|
|
|
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);
|
|
}
|
|
|
|
hashmap_set(&p->defs, ident_tok.text, nullptr);
|
|
|
|
return p->curr;
|
|
}
|
|
|
|
static Tok p_include(Preproc* p)
|
|
{
|
|
p->lexer->lex_headername = true;
|
|
step(p);
|
|
p->lexer->lex_headername = false;
|
|
|
|
if (p->curr.ty != Tok_HeaderName) {
|
|
fprintf(stderr, "error: expected header name line %d\n", p->curr.line);
|
|
exit(1);
|
|
}
|
|
|
|
assert(false && "not implemented");
|
|
}
|
|
|
|
static Tok preproc_next_nl(Preproc* p)
|
|
{
|
|
if (p->pasting_toks) {
|
|
if (p->pasting_toks_count >= p->pasting_toks->toks_size) {
|
|
p->pasting_toks = nullptr;
|
|
p->pasting_toks_count = 0;
|
|
return preproc_next_nl(p);
|
|
}
|
|
|
|
Tok tok = p->pasting_toks->toks[p->pasting_toks_count];
|
|
p->pasting_toks_count += 1;
|
|
return tok;
|
|
}
|
|
|
|
Tok curr = p->curr;
|
|
step(p);
|
|
|
|
if (curr.ty == Tok_Ident) {
|
|
DefToks* def = hashmap_get(&p->defs, curr.text);
|
|
if (!def)
|
|
return curr;
|
|
|
|
step(p);
|
|
p->pasting_toks = def;
|
|
p->pasting_toks_count = 0;
|
|
return preproc_next_nl(p);
|
|
}
|
|
|
|
if (curr.ty == '#') {
|
|
Tok kw = p->curr;
|
|
|
|
if (strcmp(kw.text, "define") == 0) {
|
|
return p_define(p);
|
|
} else if (strcmp(kw.text, "undef") == 0) {
|
|
return p_undef(p);
|
|
} else if (strcmp(kw.text, "include") == 0) {
|
|
return p_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);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
return curr;
|
|
}
|
|
|
|
Tok preproc_next(Preproc* p)
|
|
{
|
|
Tok tok;
|
|
while ((tok = preproc_next_nl(p)).ty == Tok_Eol
|
|
|| tok.ty == Tok_Whitespace) { }
|
|
return tok;
|
|
}
|