diff --git a/.gitignore b/.gitignore index 378eac2..6f3bbe2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -build +**/cmeta +**/main \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 8207cd2..0000000 --- a/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: build/cmeta - -build: - mkdir -p build - -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 - build/cmeta diff --git a/src/main.c b/cmeta.h similarity index 61% rename from src/main.c rename to cmeta.h index 0105a42..f85911e 100644 --- a/src/main.c +++ b/cmeta.h @@ -1,3 +1,40 @@ +#ifndef CMETA_H +#define CMETA_H + +#include + +typedef struct { + const char* type; + const char* name; +} Field_Info; + +typedef struct { + const char* name; + size_t fields_count; + Field_Info *fields; +} Struct_Info; + +// AUTO GENERATED CODE // +static Struct_Info foo_struct_info = { // cmeta.h:214 + .name = "Foo_Struct", // cmeta.h:215 + .fields_count = 1, // cmeta.h:216 + .fields = (Field_Info[1]) { // cmeta.h:217 + { .type = "int", .name = "int_field" }, // cmeta.h:219 + }, // cmeta.h:221 +}; // cmeta.h:222 +static Struct_Info bar_struct_info = { // cmeta.h:214 + .name = "Bar_Struct", // cmeta.h:215 + .fields_count = 2, // cmeta.h:216 + .fields = (Field_Info[2]) { // cmeta.h:217 + { .type = "int", .name = "int_field" }, // cmeta.h:219 + { .type = "long", .name = "long_fields" }, // cmeta.h:219 + }, // cmeta.h:221 +}; // cmeta.h:222 +// AUTO GENERATED CODE // + +// #define CMETA_COMPTIME +#ifdef CMETA_COMPTIME + #include #include #include @@ -7,10 +44,43 @@ #define STB_C_LEXER_IMPLEMENTATION #include "stb_c_lexer.h" -#include "cmeta.h" - stb_lexer lexer = {0}; +typedef struct { + char* type; + char* name; +} Parsed_Field_Info; + +typedef struct { + char* name; + size_t fields_count; + Parsed_Field_Info *fields; +} Parsed_Struct_Info; + +typedef struct { + char* data; + size_t capacity; + size_t length; +} String_Builder; + +void sb_append(String_Builder* sb, const char* data) { + size_t data_len = strlen(data); + size_t total_len = sb->length + data_len; + + if(total_len + 1 > sb->capacity) { + size_t new_capacity = sb->capacity == 0 ? 64 : sb->capacity * 2; + while(new_capacity < total_len + 1) { + new_capacity *= 2; + } + sb->data = (char*) realloc(sb->data, new_capacity); + sb->capacity = new_capacity; + } + + memcpy(sb->data + sb->length, data, data_len); + sb->length = total_len; + sb->data[sb->length] = '\0'; +} + bool lexer_expect_keyword(const char* expected) { stb_c_lexer_get_token(&lexer); @@ -58,16 +128,17 @@ long lexer_peek() { } // parses typedef struct { FIELDS } TYPE_NAME -bool parse_struct(Struct_Info* info) { +bool parse_struct(Parsed_Struct_Info* info) { bool result = false; + char* name = NULL; + + size_t fields_count = 0; + Parsed_Field_Info fields[16] = {0}; // TODO: use dynamic array if (!lexer_expect_keyword("typedef")) goto fail; if (!lexer_expect_keyword("struct")) goto fail; if (!lexer_expect('{', NULL)) goto fail; - size_t fields_count = 0; - Field_Info fields[16] = {0}; // TODO: use dynamic array - while (true) { char* mark = lexer.parse_point; if (!stb_c_lexer_get_token(&lexer)) { @@ -92,19 +163,19 @@ bool parse_struct(Struct_Info* info) { } if (!lexer_expect(CLEX_id, "type name")) goto fail; - char* name = strdup(lexer.string); - + name = strdup(lexer.string); + + info->name = name; + info->fields_count = fields_count; + info->fields = (Parsed_Field_Info*)calloc(fields_count, sizeof(Parsed_Field_Info)); + for(size_t i = 0; i < fields_count; i += 1) { + info->fields[i].type = fields[i].type; + info->fields[i].name = fields[i].name; + } + result = true; fail: - if(result) { - info->name = name; - info->fields_count = fields_count; - info->fields = (Field_Info*)calloc(fields_count, sizeof(Field_Info)); - for(size_t i = 0; i < fields_count; i += 1) { - info->fields[i].type = fields[i].type; - info->fields[i].name = fields[i].name; - } - } else { + if(!result) { free(name); for(size_t i = 0; i < fields_count; i += 1) { free(fields[i].name); @@ -115,7 +186,7 @@ fail: return result; } -void print_struct(Struct_Info info) { +void print_struct(Parsed_Struct_Info info) { printf("struct_name = %s\n", info.name); printf("fields[%zu] = [\n", info.fields_count); for (size_t i = 0; i < info.fields_count; i += 1) { @@ -137,7 +208,7 @@ char* to_lowercase(char* str) { return str; } -void generate_struct_info(FILE* stream, Struct_Info info) { +void generate_struct_info(FILE* stream, Parsed_Struct_Info info) { char* lowercase_name = to_lowercase(strdup(info.name)); gen("static Struct_Info %s_info = {", lowercase_name); @@ -176,21 +247,31 @@ bool read_entire_file(const char* file_path, char** content) { result = true; fail: if (!result) { - free(content); + 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 generate_output_file(const char* output_path, Parsed_Struct_Info *struct_infos, size_t struct_infos_count) { + const char* GENERATION_MARK = "// AUTO GENERATED CODE //\n"; + const size_t GENERATION_MARK_LEN = strlen(GENERATION_MARK); + bool result = false; char* header_content; - if (!read_entire_file("src/cmeta.h", &header_content)) goto fail; + if (!read_entire_file(__FILE__, &header_content)) goto fail; - char* generate_at = strstr(header_content, "// GENERATE_HERE //"); - if (generate_at == NULL) { + // 1. find BEGIN an END + char* generate_begin = strstr(header_content, GENERATION_MARK); + if (generate_begin == NULL) { + fprintf(stderr, "ERROR: could not found generation mark in cmeta.h\n"); + goto fail; + } + + char* generate_end = strstr(generate_begin + GENERATION_MARK_LEN, GENERATION_MARK); + if (generate_end == NULL) { fprintf(stderr, "ERROR: could not found generation mark in cmeta.h\n"); goto fail; } @@ -203,17 +284,14 @@ bool generate_output_file(const char* output_path, Struct_Info *struct_infos, si FILE* stream = output_file; - // write up to the generation mark - fwrite(header_content, generate_at - header_content, 1, stream); + // write up to the generation mark, including it + fwrite(header_content, generate_begin + GENERATION_MARK_LEN - 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); + fwrite(generate_end, strlen(generate_end), 1, stream); result = true; fail: @@ -223,7 +301,7 @@ fail: return result; } -bool preprocess_file(const char* file_path, char** result) { +bool preprocess_file(const char* file_path, String_Builder* result) { char command[256] = {0}; sprintf(command, "gcc -E %s", file_path); @@ -239,7 +317,7 @@ bool preprocess_file(const char* file_path, char** result) { // TODO: use dynamic array instead size_t cursor = 0; - *result = (char*) malloc(2048 * sizeof(char)); + result->length = 0; bool collecting_content = false; @@ -250,12 +328,11 @@ bool preprocess_file(const char* file_path, char** result) { if (strcmp(file_name, file_path) == 0) { collecting_content = true; - continue;; + continue; } } else if(collecting_content) { - size_t line_len = strlen(line); - memcpy(*result + cursor, line, line_len); - cursor += line_len; + sb_append(result, line); + cursor += strlen(line); } } @@ -264,32 +341,23 @@ bool preprocess_file(const char* file_path, char** result) { return true; } -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; - } - +bool process_file(const char* input_file) { // read input file - char* input_content; - if (!preprocess_file(argv[1], &input_content)) return 1; + String_Builder input_content = {0}; + if (!preprocess_file(input_file, &input_content)) return false; // 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)); + char string_store[1024] = {0}; + stb_c_lexer_init(&lexer, input_content.data, input_content.data + input_content.length, 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 + Parsed_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; @@ -299,7 +367,11 @@ int main(int argc, char** argv) } } - if (!generate_output_file("test/01_simple_struct/cmeta.h", struct_infos, struct_infos_count)) return 1; + if (!generate_output_file(__FILE__, struct_infos, struct_infos_count)) return false; - return 0; + return true; } + +#endif // CMETA_COMPTIME + +#endif // CMETA_H \ No newline at end of file diff --git a/src/cmeta.h b/src/cmeta.h deleted file mode 100644 index 4c3601f..0000000 --- a/src/cmeta.h +++ /dev/null @@ -1,19 +0,0 @@ -#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