larpcc/preproc.c
2026-09-19 03:02:59 +02:00

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