diff --git a/collection.c b/collection.c index caa52cc..1c37588 100644 --- a/collection.c +++ b/collection.c @@ -4,24 +4,55 @@ #include #include -void array_push( - void** data, size_t* capacity, size_t* size, void* elem, size_t elem_size) +static void array_push_generic(void** data, + 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) { *capacity = 4; } if (!*data) { - *data = malloc(*capacity * elem_size); + *data = alloc_fn(allocator, *capacity * elem_size); *size = 0; } if (*size >= *capacity) { *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); *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) { array_push((void**)&m->entries, @@ -134,3 +165,34 @@ void* arena_alloc(Arena* a, size_t size) 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); +} diff --git a/collection.h b/collection.h index b4c9c24..4068a47 100644 --- a/collection.h +++ b/collection.h @@ -40,4 +40,11 @@ void arena_init(Arena* a, size_t bucket_cap); void arena_deinit(Arena* a); 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 diff --git a/examples/hello.c b/examples/hello.c index ab894e4..94d8622 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -1,8 +1,8 @@ int puts(const char* __s); -#define PUTS puts +#define PUTS(A, B) puts(A, B) int main() { - PUTS("hello"); + PUTS("hello", "world"); } diff --git a/lex.c b/lex.c index 55cf610..76678a9 100644 --- a/lex.c +++ b/lex.c @@ -232,7 +232,7 @@ Tok lexer_next(Lexer* l) 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; step(l, text, &text_len); return tok(l, ty, text, line, col); diff --git a/main.c b/main.c index ddf6236..28e431f 100644 --- a/main.c +++ b/main.c @@ -57,7 +57,7 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } - printf("%s\n", text); + printf("text:\n%s\n", text); SourceIter source_chars; source_iter_init(&source_chars, text); @@ -74,10 +74,14 @@ int main(int argc, char* argv[]) Preproc preproc; preproc_init(&preproc, &lexer); + printf("tokens:\n"); + Tok tok; - while ((tok = preproc_next(&preproc)).ty != Tok_Eof) { - printf("%s\t%s\n", tokty_str(tok.ty), tok.text); + while ((tok = preproc_next_with_newline(&preproc)).ty != Tok_Eof) { + // printf("%s\t%s\n", tokty_str(tok.ty), tok.text); + printf("%s ", tok.text); } + fputs("\n", stdout); preproc_deinit(&preproc); free(text); diff --git a/parse.h b/parse.h index 617cc3e..1e43a60 100644 --- a/parse.h +++ b/parse.h @@ -51,6 +51,7 @@ typedef enum { Tok_RParen = ')', Tok_LBrace = '{', Tok_RBrace = '}', + Tok_Comma = ',', Tok_Asterisk = '*', Tok_Hashtag = '#', } TokTy; @@ -77,23 +78,27 @@ Tok lexer_next(Lexer* l); // phase 4 - preprocessor -typedef struct DefToks DefToks; +typedef struct MacroDef MacroDef; +typedef struct MacroInv MacroInv; typedef struct { Lexer* lexer; Tok curr; - DefToks* deftoks; - size_t deftoks_cap; - size_t deftoks_size; - Hashmap defs; + MacroDef* defs; + size_t defs_cap; + size_t defs_size; + Hashmap def_map; - DefToks* pasting_toks; - size_t pasting_toks_count; + Arena arg_arena; + MacroInv* macro_invs; + size_t macro_invs_cap; + size_t macro_invs_size; } Preproc; void preproc_init(Preproc* p, Lexer* lexer); void preproc_deinit(Preproc* p); +Tok preproc_next_with_newline(Preproc* p); Tok preproc_next(Preproc* p); #endif diff --git a/preproc.c b/preproc.c index c0ee07f..a269604 100644 --- a/preproc.c +++ b/preproc.c @@ -6,12 +6,26 @@ #include #include -struct DefToks { +struct MacroDef { Tok* toks; size_t toks_cap; 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) { Tok curr = lexer_next(lexer); @@ -19,26 +33,35 @@ void preproc_init(Preproc* p, Lexer* lexer) *p = (Preproc) { .lexer = lexer, .curr = curr, - .deftoks = nullptr, - .deftoks_cap = 0, - .deftoks_size = 0, - .defs = { }, - .pasting_toks = nullptr, - .pasting_toks_count = 0, + + .defs = nullptr, + .defs_cap = 0, + .defs_size = 0, + .def_map = { }, + + .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) { - if (p->deftoks) { - for (size_t i = 0; i < p->deftoks_size; ++i) { - free(p->deftoks[i].toks); + if (p->defs) { + for (size_t i = 0; i < p->defs_size; ++i) { + 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) @@ -60,30 +83,69 @@ static Tok p_define(Preproc* p) step(p); p->lexer->lex_whitespace = false; + MacroDef def = { }; + if (p->curr.ty == '(') { - // function-like macro - assert(false && "not implemented"); + step(p); + + 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**)¶ms, + ¶ms_cap, + ¶ms_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**)¶ms, + ¶ms_cap, + ¶ms_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) { - array_push((void**)&toks.toks, - &toks.toks_cap, - &toks.toks_size, + array_push((void**)&def.toks, + &def.toks_cap, + &def.toks_size, &p->curr, sizeof(p->curr)); step(p); } - array_push((void**)&p->deftoks, - &p->deftoks_cap, - &p->deftoks_size, - &toks, - sizeof(toks)); + array_push( + (void**)&p->defs, &p->defs_cap, &p->defs_size, &def, sizeof(def)); - DefToks* deftoks = &p->deftoks[p->deftoks_size - 1]; - hashmap_set(&p->defs, ident_tok.text, deftoks); + MacroDef* deftoks = &p->defs[p->defs_size - 1]; + hashmap_set(&p->def_map, ident_tok.text, deftoks); return p->curr; } @@ -106,7 +168,7 @@ static Tok p_undef(Preproc* p) exit(1); } - hashmap_set(&p->defs, ident_tok.text, nullptr); + hashmap_set(&p->def_map, ident_tok.text, nullptr); return p->curr; } @@ -125,83 +187,175 @@ static Tok p_include(Preproc* p) assert(false && "not implemented"); } -static Tok preproc_next_nl(Preproc* p) +static Tok p_paste_macro_body(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); - } + MacroInv* inv = &p->macro_invs[p->macro_invs_size - 1]; - Tok tok = p->pasting_toks->toks[p->pasting_toks_count]; - p->pasting_toks_count += 1; - return tok; + if (inv->tok_idx >= inv->def->toks_size) { + p->macro_invs_size -= 1; + 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); - if (curr.ty == Tok_Ident) { - DefToks* def = hashmap_get(&p->defs, curr.text); - if (!def) - return curr; + struct arg_def* arg_defs; + size_t arg_defs_cap = 0; + size_t arg_defs_size = 0; - step(p); - p->pasting_toks = def; - p->pasting_toks_count = 0; - return preproc_next_nl(p); - } + size_t arg_idx = 0; - if (curr.ty == '#') { - Tok kw = p->curr; + bool first = true; - 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) { + while ((first && (p->curr.ty != Tok_Eof && p->curr.ty != ')')) + || p->curr.ty != ',') { + first = false; + + if (arg_idx + 1 >= def->params_size) { fprintf(stderr, - "error: '%s' is not a preprocessor instruction on line %d\n", - kw.text, - kw.line); + "error: too many arguments for macro line %d\n", + p->curr.line); 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 tok; - while ((tok = preproc_next_nl(p)).ty == Tok_Eol + while ((tok = preproc_next_with_newline(p)).ty == Tok_Eol || tok.ty == Tok_Whitespace) { } return tok; }