From 92ab4b519d720c64008c86543184854e16fe0768 Mon Sep 17 00:00:00 2001 From: sfja Date: Sat, 26 Sep 2026 04:12:09 +0200 Subject: [PATCH] add parser + ast --- ast.h | 64 +++++++++++++++++++ main.c | 13 ++-- parse.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ parse.h | 13 +++- preproc.c | 26 ++++---- 5 files changed, 278 insertions(+), 21 deletions(-) create mode 100644 ast.h create mode 100644 parse.c diff --git a/ast.h b/ast.h new file mode 100644 index 0000000..0f87f59 --- /dev/null +++ b/ast.h @@ -0,0 +1,64 @@ +#ifndef AST_H +#define AST_H + +#include "collection.h" + +typedef struct { + int line; + int col; +} Loc; + +typedef enum : unsigned int { + AstSpec_Typedef = 1 << 0, + // AstSpec_Constexpr = 1 << 1, + // AstSpec_Auto = 1 << 2, + // AstSpec_Register = 1 << 3, + AstSpec_Static = 1 << 4, + // AstSpec_Extern = 1 << 5, + // AstSpec_ThreadLocal = 1 << 6, + AstSpec_Const = 1 << 7, + // AstSpec_Volatile = 1 << 8, + // AstSpec_Restrict = 1 << 9, + // AstSpec_Atomic = 1 << 10, + AstSpec_Inline = 1 << 11, +} AstSpec; + +typedef enum { + AstDeclar_Ident, + AstDeclar_Pointer, + AstDeclar_Array, +} AstDeclarTy; + +typedef struct AstDeclar AstDeclar; + +struct AstDeclar { + AstDeclarTy ty; + Loc loc; + union { + char* ident; + struct { + AstDeclar* declar; + AstSpec spec; + } pointer; + }; +}; + +typedef struct { + AstSpec spec; + unsigned long type_id; + ARRAY_FIELD(AstDeclar, declars); + Loc loc; +} AstDecl; + +typedef enum { + AstStmt_Decl, +} AstStmtTy; + +typedef struct { + AstStmtTy ty; + union { + AstDecl decl; + }; +} AstStmt; + +#endif diff --git a/main.c b/main.c index abffb05..1cce045 100644 --- a/main.c +++ b/main.c @@ -20,14 +20,13 @@ int main(int argc, char* argv[]) printf("--- text ---\n%s\n", text); - Arena arena; - arena_init(&arena, 4096); + Arena tok_arena; + arena_init(&tok_arena, 4096); - Preproc* preproc; - preproc_init(&preproc, strdup(filename), text, &arena); + Preproc* preproc = preproc_new(strdup(filename), text, &tok_arena); TokStream tokstream; - tokstream_init(&tokstream, preproc, &arena); + tokstream_init(&tokstream, preproc, &tok_arena); printf("--- tokens ---\n"); @@ -54,6 +53,6 @@ int main(int argc, char* argv[]) } fputs("\n", stdout); - preproc_deinit(preproc); - arena_deinit(&arena); + preproc_free(preproc); + arena_deinit(&tok_arena); } diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..7071fda --- /dev/null +++ b/parse.c @@ -0,0 +1,183 @@ +#include "parse.h" +#include "collection.h" +#include +#include +#include +#include + +typedef struct { + char ch; + bool is_term; + unsigned short val; +} KwNodeOpt; + +typedef struct { + ARRAY_FIELD(KwNodeOpt, opts); + size_t idx; +} KwNode; + +typedef struct { + TokStream* tokstream; + Arena arena; + ARRAY_FIELD(KwNode, nodes); + KwNode* root; +} KwMatcher; + +static KwNodeOpt* kwmatcher_create_path( + KwMatcher* m, KwNode* node, const char* kw) +{ + assert(kw[0]); + KwNodeOpt* opt = nullptr; + for (size_t i = 0; i < node->opts_size; ++i) { + if (node->opts[i].ch == kw[0]) { + opt = &node->opts[i]; + } + } + if (!opt) { + size_t new_idx = m->nodes_size; + + KwNode new_node = { + ARRAY_FIELD_INIT(opts), + .idx = new_idx, + }; + arena_array_push(&m->arena, + (void**)&(m)->nodes, + &(m)->nodes_cap, + &(m)->nodes_size, + (&new_node), + sizeof(*(&new_node))); + KwNode* node = &m->nodes[new_idx]; + + KwNodeOpt new_opt = { + .ch = kw[0], + .is_term = false, + .val = new_idx, + }; + arena_array_push(&m->arena, + (void**)&node->opts, + &node->opts_cap, + &node->opts_size, + (&new_opt), + sizeof(*(&new_opt))); + + opt = &node->opts[node->opts_size - 1]; + } + if (!kw[1]) + return opt; + + KwNode* next_node = &m->nodes[opt->val]; + return kwmatcher_create_path(m, next_node, &kw[1]); +} + +static void kwmatcher_add(KwMatcher* m, const char* kw, TokTy tokty) +{ + KwNodeOpt* opt = kwmatcher_create_path(m, m->root, kw); + opt->is_term = true; + opt->val = tokty; +} + +static KwNodeOpt* kwmatcher_find_node( + KwMatcher* m, KwNode* node, const char* kw) +{ + assert(kw[0]); + KwNodeOpt* opt = nullptr; + for (size_t i = 0; i < node->opts_size; ++i) { + if (node->opts[i].ch == kw[0]) { + opt = &node->opts[i]; + } + } + if (!opt) + return nullptr; + if (!kw[1]) + return opt; + return kwmatcher_find_node(m, &m->nodes[opt->val], &kw[1]); +} + +static TokTy kwmatcher_match(KwMatcher* m, const char* kw) +{ + KwNodeOpt* opt = kwmatcher_find_node(m, m->root, kw); + if (!opt->is_term) + return Tok_Ident; + return opt->val; +} + +static void kwmatcher_init(KwMatcher* m, TokStream* tokstream) +{ + *m = (KwMatcher) { + .tokstream = tokstream, + .arena = { }, + ARRAY_FIELD_INIT(nodes), + .root = nullptr, + }; + + arena_init(&m->arena, 512); + + arena_array_push(&m->arena, + (void**)&(m)->nodes, + &(m)->nodes_cap, + &(m)->nodes_size, + (&(KwNode) { .idx = 0 }), + sizeof(*(&(KwNode) { .idx = 0 }))); + + m->root = &m->nodes[0]; + + struct { + TokTy ty; + const char* kw; + } kws[] = { + { Tok_KwInt, "int" }, + { Tok_KwChar, "char" }, + { Tok_KwConst, "const" }, + }; + + for (size_t i = 0; i < sizeof(kws) / sizeof(kws[0]); ++i) { + kwmatcher_add(m, kws[i].kw, kws[i].ty); + } +} + +static void kwmatcher_deinit(KwMatcher* m) +{ + arena_deinit(&m->arena); +} + +static Tok kwmatcher_next(KwMatcher* m) +{ + Tok tok = tokstream_next(m->tokstream); + if (tok.ty != Tok_Ident) + return tok; + TokTy ty = kwmatcher_match(m, tok.text); + if (ty != Tok_Ident) + printf("info: matched '%s' on line %d\n", tok.text, tok.line); + tok.ty = ty; + return tok; +} + +struct Parser { + KwMatcher kwmatcher; + Arena* arena; + + Tok curr; +}; + +Parser* parser_new(TokStream* tokstream, Arena* arena) +{ + Parser* p = malloc(sizeof(Parser)); + + *p = (Parser) { + .kwmatcher = { }, + .arena = arena, + + .curr = { }, + }; + + kwmatcher_init(&p->kwmatcher, tokstream); + p->curr = kwmatcher_next(&p->kwmatcher); + + return p; +} + +void parser_free(Parser* p) +{ + kwmatcher_deinit(&p->kwmatcher); + free(p); +} diff --git a/parse.h b/parse.h index 7b06542..e694e09 100644 --- a/parse.h +++ b/parse.h @@ -57,6 +57,10 @@ typedef enum { Tok_Asterisk = '*', Tok_Hashtag = '#', + Tok_KwInt, + Tok_KwChar, + Tok_KwConst, + // used internally in preprocessor Tok_EndOfMacroInvocation, } TokTy; @@ -85,8 +89,8 @@ Tok lexer_next(Lexer* l); typedef struct Preproc Preproc; -void preproc_init(Preproc** p, char* filename, char* text, Arena* arena); -void preproc_deinit(Preproc* p); +Preproc* preproc_new(char* filename, char* text, Arena* arena); +void preproc_free(Preproc* p); Tok preproc_next(Preproc* p); // phase 5, 6 - preprocessor cleanup, concat strings @@ -100,4 +104,9 @@ typedef struct { void tokstream_init(TokStream* s, Preproc* preproc, Arena* arena); Tok tokstream_next(TokStream* s); +typedef struct Parser Parser; + +Parser* parser_new(TokStream* tokstream, Arena* arena); +void parser_free(Parser* p); + #endif diff --git a/preproc.c b/preproc.c index 001711d..8f42f61 100644 --- a/preproc.c +++ b/preproc.c @@ -121,11 +121,11 @@ struct Preproc { bool next_can_be_directive; }; -void preproc_init(Preproc** p, char* filename, char* text, Arena* arena) +Preproc* preproc_new(char* filename, char* text, Arena* arena) { - *p = malloc(sizeof(Preproc)); + Preproc* p = malloc(sizeof(Preproc)); - **p = (Preproc) { + *p = (Preproc) { .arena = arena, ARRAY_FIELD_INIT(files), @@ -140,7 +140,7 @@ void preproc_init(Preproc** p, char* filename, char* text, Arena* arena) .next_can_be_directive = true, }; - ARRAY_FIELD_PUSH(*p, + ARRAY_FIELD_PUSH(p, files, (&(File) { .filename = filename, @@ -150,19 +150,21 @@ void preproc_init(Preproc** p, char* filename, char* text, Arena* arena) .lexer = { }, })); - File* file = &(*p)->files[0]; + File* file = &p->files[0]; source_iter_init(&file->source_iter, text); lsl_iter_init(&file->lsl_iter, &file->source_iter); - lexer_init(&file->lexer, &file->lsl_iter, (*p)->arena); - (*p)->curr = lexer_next(&file->lexer); + lexer_init(&file->lexer, &file->lsl_iter, p->arena); + p->curr = lexer_next(&file->lexer); // null-definition at idx 0 - ARRAY_FIELD_PUSH(*p, defs, &(Def) { }); + ARRAY_FIELD_PUSH(p, defs, &(Def) { }); - syms_init(&(*p)->syms); + syms_init(&p->syms); + + return p; } -void preproc_deinit(Preproc* p) +void preproc_free(Preproc* p) { for (size_t i = 0; i < p->files_size; ++i) { free(p->files[i].filename); @@ -655,8 +657,8 @@ Tok tokstream_next(TokStream* s) char* text = arena_alloc(s->arena, strlen(curr.text) + strlen(next.text) + 1); text[0] = '\0'; - strcat(text, curr.text); - strcat(text, next.text); + strncat(text, curr.text, strlen(curr.text) - 1); + strcat(text, next.text + 1); s->curr = (Tok) { .ty = Tok_Str, text, curr.line, curr.col }; return tokstream_next(s); }