work on preproc

This commit is contained in:
sfja 2026-09-20 02:22:28 +02:00
parent 52ef4b4754
commit da32555e6e
7 changed files with 335 additions and 103 deletions

View File

@ -4,24 +4,55 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
void array_push( static void array_push_generic(void** data,
void** data, size_t* capacity, size_t* size, void* elem, size_t elem_size) size_t* capacity,
size_t* size,
void* elem,
size_t elem_size,
void* alloc_fn(void*, size_t),
void* realloc_fn(void*, void*, size_t),
void* allocator)
{ {
if (!*capacity) { if (!*capacity) {
*capacity = 4; *capacity = 4;
} }
if (!*data) { if (!*data) {
*data = malloc(*capacity * elem_size); *data = alloc_fn(allocator, *capacity * elem_size);
*size = 0; *size = 0;
} }
if (*size >= *capacity) { if (*size >= *capacity) {
*capacity *= 2; *capacity *= 2;
*data = realloc(*data, *capacity * elem_size); *data = realloc_fn(allocator, *data, *capacity * elem_size);
} }
memcpy((unsigned char*)*data + *size * elem_size, elem, elem_size); memcpy((unsigned char*)*data + *size * elem_size, elem, elem_size);
*size += 1; *size += 1;
} }
static void* malloc_wrap(void* self, size_t size)
{
(void)self;
return malloc(size);
}
static void* realloc_wrap(void* self, void* p, size_t size)
{
(void)self;
return realloc(p, size);
}
void array_push(
void** data, size_t* capacity, size_t* size, void* elem, size_t elem_size)
{
array_push_generic(data,
capacity,
size,
elem,
elem_size,
malloc_wrap,
realloc_wrap,
nullptr);
}
static void push_kwpair(Hashmap* m, KwPair* pair) static void push_kwpair(Hashmap* m, KwPair* pair)
{ {
array_push((void**)&m->entries, array_push((void**)&m->entries,
@ -134,3 +165,34 @@ void* arena_alloc(Arena* a, size_t size)
return ptr; return ptr;
} }
static void* arena_alloc_wrap(void* self, size_t size)
{
Arena* a = self;
return arena_alloc(a, size);
}
static void* arena_realloc_wrap(void* self, void* p, size_t size)
{
Arena* a = self;
void* new_buf = arena_alloc(a, size);
memcpy(new_buf, p, size);
return new_buf;
}
void arena_array_push(Arena* arena,
void** data,
size_t* capacity,
size_t* size,
void* elem,
size_t elem_size)
{
array_push_generic(data,
capacity,
size,
elem,
elem_size,
arena_alloc_wrap,
arena_realloc_wrap,
arena);
}

View File

@ -40,4 +40,11 @@ void arena_init(Arena* a, size_t bucket_cap);
void arena_deinit(Arena* a); void arena_deinit(Arena* a);
void* arena_alloc(Arena* a, size_t size); void* arena_alloc(Arena* a, size_t size);
void arena_array_push(Arena* arena,
void** data,
size_t* capacity,
size_t* size,
void* elem,
size_t elem_size);
#endif #endif

View File

@ -1,8 +1,8 @@
int puts(const char* __s); int puts(const char* __s);
#define PUTS puts #define PUTS(A, B) puts(A, B)
int main() int main()
{ {
PUTS("hello"); PUTS("hello", "world");
} }

2
lex.c
View File

@ -232,7 +232,7 @@ Tok lexer_next(Lexer* l)
return tok(l, Tok_Char, text, line, col); return tok(l, Tok_Char, text, line, col);
} }
if (strchr("\n;(){}*#", l->curr.ch)) { if (strchr("\n;(){},*#", l->curr.ch)) {
TokTy ty = (TokTy)l->curr.ch; TokTy ty = (TokTy)l->curr.ch;
step(l, text, &text_len); step(l, text, &text_len);
return tok(l, ty, text, line, col); return tok(l, ty, text, line, col);

10
main.c
View File

@ -57,7 +57,7 @@ int main(int argc, char* argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
printf("%s\n", text); printf("text:\n%s\n", text);
SourceIter source_chars; SourceIter source_chars;
source_iter_init(&source_chars, text); source_iter_init(&source_chars, text);
@ -74,10 +74,14 @@ int main(int argc, char* argv[])
Preproc preproc; Preproc preproc;
preproc_init(&preproc, &lexer); preproc_init(&preproc, &lexer);
printf("tokens:\n");
Tok tok; Tok tok;
while ((tok = preproc_next(&preproc)).ty != Tok_Eof) { while ((tok = preproc_next_with_newline(&preproc)).ty != Tok_Eof) {
printf("%s\t%s\n", tokty_str(tok.ty), tok.text); // printf("%s\t%s\n", tokty_str(tok.ty), tok.text);
printf("%s ", tok.text);
} }
fputs("\n", stdout);
preproc_deinit(&preproc); preproc_deinit(&preproc);
free(text); free(text);

19
parse.h
View File

@ -51,6 +51,7 @@ typedef enum {
Tok_RParen = ')', Tok_RParen = ')',
Tok_LBrace = '{', Tok_LBrace = '{',
Tok_RBrace = '}', Tok_RBrace = '}',
Tok_Comma = ',',
Tok_Asterisk = '*', Tok_Asterisk = '*',
Tok_Hashtag = '#', Tok_Hashtag = '#',
} TokTy; } TokTy;
@ -77,23 +78,27 @@ Tok lexer_next(Lexer* l);
// phase 4 - preprocessor // phase 4 - preprocessor
typedef struct DefToks DefToks; typedef struct MacroDef MacroDef;
typedef struct MacroInv MacroInv;
typedef struct { typedef struct {
Lexer* lexer; Lexer* lexer;
Tok curr; Tok curr;
DefToks* deftoks; MacroDef* defs;
size_t deftoks_cap; size_t defs_cap;
size_t deftoks_size; size_t defs_size;
Hashmap defs; Hashmap def_map;
DefToks* pasting_toks; Arena arg_arena;
size_t pasting_toks_count; MacroInv* macro_invs;
size_t macro_invs_cap;
size_t macro_invs_size;
} Preproc; } Preproc;
void preproc_init(Preproc* p, Lexer* lexer); void preproc_init(Preproc* p, Lexer* lexer);
void preproc_deinit(Preproc* p); void preproc_deinit(Preproc* p);
Tok preproc_next_with_newline(Preproc* p);
Tok preproc_next(Preproc* p); Tok preproc_next(Preproc* p);
#endif #endif

326
preproc.c
View File

@ -6,12 +6,26 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
struct DefToks { struct MacroDef {
Tok* toks; Tok* toks;
size_t toks_cap; size_t toks_cap;
size_t toks_size; size_t toks_size;
char** params;
size_t params_size;
}; };
struct MacroInv {
MacroDef* def;
size_t tok_idx;
Hashmap arg_map;
};
typedef struct {
Tok* toks;
size_t toks_size;
} MacroArg;
void preproc_init(Preproc* p, Lexer* lexer) void preproc_init(Preproc* p, Lexer* lexer)
{ {
Tok curr = lexer_next(lexer); Tok curr = lexer_next(lexer);
@ -19,26 +33,35 @@ void preproc_init(Preproc* p, Lexer* lexer)
*p = (Preproc) { *p = (Preproc) {
.lexer = lexer, .lexer = lexer,
.curr = curr, .curr = curr,
.deftoks = nullptr,
.deftoks_cap = 0, .defs = nullptr,
.deftoks_size = 0, .defs_cap = 0,
.defs = { }, .defs_size = 0,
.pasting_toks = nullptr, .def_map = { },
.pasting_toks_count = 0,
.arg_arena = { },
.macro_invs = nullptr,
.macro_invs_cap = 0,
.macro_invs_size = 0,
}; };
hashmap_init(&p->defs); hashmap_init(&p->def_map);
arena_init(&p->arg_arena, 512);
} }
void preproc_deinit(Preproc* p) void preproc_deinit(Preproc* p)
{ {
if (p->deftoks) { if (p->defs) {
for (size_t i = 0; i < p->deftoks_size; ++i) { for (size_t i = 0; i < p->defs_size; ++i) {
free(p->deftoks[i].toks); free(p->defs[i].toks);
if (p->defs[i].params) {
free(p->defs[i].params);
}
} }
free(p->deftoks); free(p->defs);
} }
hashmap_deinit(&p->defs); hashmap_deinit(&p->def_map);
arena_deinit(&p->arg_arena);
} }
static void step(Preproc* p) static void step(Preproc* p)
@ -60,30 +83,69 @@ static Tok p_define(Preproc* p)
step(p); step(p);
p->lexer->lex_whitespace = false; p->lexer->lex_whitespace = false;
MacroDef def = { };
if (p->curr.ty == '(') { if (p->curr.ty == '(') {
// function-like macro step(p);
assert(false && "not implemented");
char** params = nullptr;
size_t params_cap = 0;
size_t params_size = 0;
if (p->curr.ty != Tok_Eof && p->curr.ty != ')') {
if (p->curr.ty != Tok_Ident) {
fprintf(stderr,
"error: expected identifier on line %d\n",
p->curr.line);
exit(1);
}
array_push((void**)&params,
&params_cap,
&params_size,
p->curr.text,
sizeof(p->curr.text));
while (p->curr.ty == ',') {
step(p);
if (p->curr.ty != Tok_Ident) {
fprintf(stderr,
"error: expected identifier on line %d\n",
p->curr.line);
exit(1);
}
array_push((void**)&params,
&params_cap,
&params_size,
p->curr.text,
sizeof(p->curr.text));
}
}
if (p->curr.ty == Tok_Eof || p->curr.ty == ')') {
fprintf(stderr, "error: expected ')' on line %d\n", p->curr.line);
exit(1);
}
step(p);
def.params = params;
def.params_size = params_size;
} }
DefToks toks = { };
while (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) { while (p->curr.ty != Tok_Eof && p->curr.ty != Tok_Eol) {
array_push((void**)&toks.toks, array_push((void**)&def.toks,
&toks.toks_cap, &def.toks_cap,
&toks.toks_size, &def.toks_size,
&p->curr, &p->curr,
sizeof(p->curr)); sizeof(p->curr));
step(p); step(p);
} }
array_push((void**)&p->deftoks, array_push(
&p->deftoks_cap, (void**)&p->defs, &p->defs_cap, &p->defs_size, &def, sizeof(def));
&p->deftoks_size,
&toks,
sizeof(toks));
DefToks* deftoks = &p->deftoks[p->deftoks_size - 1]; MacroDef* deftoks = &p->defs[p->defs_size - 1];
hashmap_set(&p->defs, ident_tok.text, deftoks); hashmap_set(&p->def_map, ident_tok.text, deftoks);
return p->curr; return p->curr;
} }
@ -106,7 +168,7 @@ static Tok p_undef(Preproc* p)
exit(1); exit(1);
} }
hashmap_set(&p->defs, ident_tok.text, nullptr); hashmap_set(&p->def_map, ident_tok.text, nullptr);
return p->curr; return p->curr;
} }
@ -125,83 +187,175 @@ static Tok p_include(Preproc* p)
assert(false && "not implemented"); assert(false && "not implemented");
} }
static Tok preproc_next_nl(Preproc* p) static Tok p_paste_macro_body(Preproc* p)
{ {
if (p->pasting_toks) { MacroInv* inv = &p->macro_invs[p->macro_invs_size - 1];
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]; if (inv->tok_idx >= inv->def->toks_size) {
p->pasting_toks_count += 1; p->macro_invs_size -= 1;
return tok; return preproc_next_with_newline(p);
} }
Tok curr = p->curr; Tok tok = inv->def->toks[inv->tok_idx];
inv->tok_idx += 1;
return tok;
}
static void collect_arg_toks(Tok** toks, size_t* toks_size)
{
}
struct arg_def {
Tok* toks;
size_t toks_size;
};
static Tok p_detect_macro(Preproc* p, Tok head)
{
MacroDef* def = hashmap_get(&p->def_map, head.text);
if (!def)
return head;
MacroInv inv = {
.def = def,
.tok_idx = 0,
.arg_map = { },
};
hashmap_init(&inv.arg_map);
array_push(
(void**)&p->macro_invs, &p->defs_cap, &p->defs_size, &inv, sizeof(inv));
if (!def->params)
return preproc_next_with_newline(p);
if (p->curr.ty != '(')
return head;
step(p); step(p);
if (curr.ty == Tok_Ident) { struct arg_def* arg_defs;
DefToks* def = hashmap_get(&p->defs, curr.text); size_t arg_defs_cap = 0;
if (!def) size_t arg_defs_size = 0;
return curr;
step(p); size_t arg_idx = 0;
p->pasting_toks = def;
p->pasting_toks_count = 0;
return preproc_next_nl(p);
}
if (curr.ty == '#') { bool first = true;
Tok kw = p->curr;
if (strcmp(kw.text, "define") == 0) { while ((first && (p->curr.ty != Tok_Eof && p->curr.ty != ')'))
return p_define(p); || p->curr.ty != ',') {
} else if (strcmp(kw.text, "undef") == 0) { first = false;
return p_undef(p);
} else if (strcmp(kw.text, "include") == 0) { if (arg_idx + 1 >= def->params_size) {
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, fprintf(stderr,
"error: '%s' is not a preprocessor instruction on line %d\n", "error: too many arguments for macro line %d\n",
kw.text, p->curr.line);
kw.line);
exit(1); exit(1);
} }
Tok* toks;
size_t toks_size;
collect_arg_toks(&toks, &toks_size);
arena_array_push(&p->arg_arena,
(void**)&arg_defs,
&arg_defs_cap,
&arg_defs_size,
&(struct arg_def) { toks, toks_size },
sizeof(struct arg_def));
hashmap_set(
&inv.arg_map, def->params[arg_idx], &arg_defs[arg_defs_size - 1]);
arg_idx += 1;
} }
return curr; return preproc_next_with_newline(p);
}
static Tok p_interpret_directive(Preproc* p)
{
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);
}
assert(false && "unreachable");
}
static Tok replace_candidate_tok(Preproc* p, Tok tok, size_t inv_idx)
{
if (inv_idx == 0)
return tok;
MacroInv* inv = &p->macro_invs[inv_idx];
if (tok.ty != Tok_Ident)
return tok;
MacroArg* arg = hashmap_get(&inv->arg_map, tok.text);
if (!arg)
return replace_candidate_tok(p, tok, inv_idx - 1);
}
static Tok expand_tok(Preproc* p, Tok tok)
{
if (p->macro_invs_size == 0)
return tok;
MacroInv* inv = &p->macro_invs[p->macro_invs_size - 1];
if (inv->tok_idx >= inv->def->toks_size) {
p->macro_invs_size -= 1;
return expand_tok(p, tok);
}
}
Tok preproc_next_with_newline(Preproc* p)
{
Tok tok = p->curr;
step(p);
if (tok.ty == '#')
return p_interpret_directive(p);
return expand_tok(p, tok);
} }
Tok preproc_next(Preproc* p) Tok preproc_next(Preproc* p)
{ {
Tok tok; Tok tok;
while ((tok = preproc_next_nl(p)).ty == Tok_Eol while ((tok = preproc_next_with_newline(p)).ty == Tok_Eol
|| tok.ty == Tok_Whitespace) { } || tok.ty == Tok_Whitespace) { }
return tok; return tok;
} }