From 2c4098fb603d0aec6b9eb3431236a8c74d2dfbf6 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Thu, 22 Jan 2026 19:40:13 +0100 Subject: [PATCH] feat: parse all structs in file and generate cmeta.h file --- Makefile | 2 +- src/cmeta.h | 19 +++++++ src/main.c | 139 ++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 src/cmeta.h diff --git a/Makefile b/Makefile index 763dfa8..8207cd2 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ all: build/cmeta build: mkdir -p build -build/cmeta: build third_party/stb_c_lexer.h src/main.c +build/cmeta: build third_party/stb_c_lexer.h src/cmeta.h src/main.c gcc -Wall -Wextra -pedantic -o build/cmeta -isystem third_party/ third_party/stb_c_lexer.h src/main.c run: build/cmeta diff --git a/src/cmeta.h b/src/cmeta.h new file mode 100644 index 0000000..4c3601f --- /dev/null +++ b/src/cmeta.h @@ -0,0 +1,19 @@ +#ifndef CMETA_H +#define CMETA_H + +#include + +typedef struct { + char* type; + char* name; +} Field_Info; + +typedef struct { + char* name; + size_t fields_count; + Field_Info *fields; +} Struct_Info; + +// GENERATE_HERE // + +#endif // CMETA_H \ No newline at end of file diff --git a/src/main.c b/src/main.c index b3d5185..183076b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,6 +7,8 @@ #define STB_C_LEXER_IMPLEMENTATION #include "stb_c_lexer.h" +#include "cmeta.h" + stb_lexer lexer = {0}; bool lexer_expect_keyword(const char* expected) { @@ -41,16 +44,18 @@ bool lexer_expect(long expected, const char* expected_str) { return true; } -typedef struct { - char* type; - char* name; -} Field_Info; +long lexer_peek() { + char* mark = lexer.parse_point; + if (!stb_c_lexer_get_token(&lexer)) { + lexer.parse_point = mark; + return CLEX_eof; + } -typedef struct { - char* name; - size_t fields_count; - Field_Info *fields; -} Struct_Info; + long token = lexer.token; + lexer.parse_point = mark; + + return token; +} // parses typedef struct { FIELDS } TYPE_NAME bool parse_struct(Struct_Info* info) { @@ -148,18 +153,112 @@ void generate_struct_info(FILE* stream, Struct_Info info) { free(lowercase_name); } -int main(void) -{ - const char *source = "typedef struct { int int_field; bool bool_field; } My_Struct;"; - char string_store[1024]; - stb_c_lexer_init(&lexer, source, source + strlen(source), string_store, sizeof(string_store) / sizeof(char)); +bool read_entire_file(const char* file_path, char** content) { + bool result = false; + FILE* file = fopen(file_path, "rb"); + if(file == NULL) goto fail; - Struct_Info info = {0}; - if (!parse_struct(&info)) return 1; - printf("--- Parsed ------------\n"); - print_struct(info); - printf("--- Generated ---------\n"); - generate_struct_info(stdout, info); + if(fseek(file, 0, SEEK_END) < 0) goto fail; + + long length = ftell(file); + if(length < 0) goto fail; + + if(fseek(file, 0, SEEK_SET) < 0) goto fail; + + *content = (char*) malloc((length + 1) * sizeof(char)); + fread(*content, 1, length, file); + + // TODO: will not set errno + if (ferror(file)) goto fail; + + (*content)[length] = '\0'; + + result = true; +fail: + if (!result) { + free(content); + fprintf(stderr, "ERROR: Could not read `%s`: %s\n", file_path, strerror(errno)); + } + if (file) fclose(file); + return result; +} + +bool generate_output_file(const char* output_path, Struct_Info *struct_infos, size_t struct_infos_count) { + bool result = false; + + char* header_content; + if (!read_entire_file("src/cmeta.h", &header_content)) goto fail; + + char* generate_at = strstr(header_content, "// GENERATE_HERE //"); + if (generate_at == NULL) { + fprintf(stderr, "ERROR: could not found generation mark in cmeta.h\n"); + goto fail; + } + + FILE* output_file = fopen(output_path, "wb"); + if (!output_file) { + fprintf(stderr, "ERROR: could not write to %s: %s\n", output_path, strerror(errno)); + goto fail; + } + + FILE* stream = output_file; + + // write up to the generation mark + fwrite(header_content, generate_at - header_content, 1, stream); + + gen("// Generated by cmeta"); + for (size_t i = 0; i < struct_infos_count; i += 1) { + generate_struct_info(stream, struct_infos[i]); + } + + // write the rest after the \n + generate_at = strchr(generate_at, '\n'); + fwrite(generate_at, strlen(generate_at), 1, stream); + + result = true; +fail: + free(header_content); + if (output_file) fclose(output_file); + + return result; +} + +int main(int argc, char** argv) +{ + const char* program_name = argv[0]; + if(argc == 1) { + fprintf(stderr, "ERROR: missing required argument \n"); + fprintf(stderr, "Usage: %s \n", program_name); + return 1; + } + + // read input file + char* input_content; + if(!read_entire_file(argv[1], &input_content)) return 1; + + // init lexer + char string_store[1024]; + stb_c_lexer_init(&lexer, input_content, input_content + strlen(input_content), string_store, sizeof(string_store) / sizeof(char)); + + // find and parse all structs + size_t struct_infos_count = 0; + Struct_Info struct_infos[16]; // TODO: use dynamic array + + while (true) { + char* mark = lexer.parse_point; + + if (!stb_c_lexer_get_token(&lexer)) break; + + if (lexer.token == CLEX_id && strcmp(lexer.string, "typedef") == 0) { + lexer.parse_point = mark; + + if (parse_struct(&struct_infos[struct_infos_count])) { + struct_infos_count += 1; + } + } + } + + if (!generate_output_file("test/01_simple_struct/cmeta.h", struct_infos, struct_infos_count)) return 1; return 0; }