feat: parse all structs in file and generate cmeta.h file

This commit is contained in:
2026-01-22 19:40:13 +01:00
parent 3f5b9c0bc8
commit 2c4098fb60
3 changed files with 139 additions and 21 deletions

View File

@@ -3,7 +3,7 @@ all: build/cmeta
build: build:
mkdir -p 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 gcc -Wall -Wextra -pedantic -o build/cmeta -isystem third_party/ third_party/stb_c_lexer.h src/main.c
run: build/cmeta run: build/cmeta

19
src/cmeta.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef CMETA_H
#define CMETA_H
#include <stddef.h>
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

View File

@@ -1,4 +1,5 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -6,6 +7,8 @@
#define STB_C_LEXER_IMPLEMENTATION #define STB_C_LEXER_IMPLEMENTATION
#include "stb_c_lexer.h" #include "stb_c_lexer.h"
#include "cmeta.h"
stb_lexer lexer = {0}; stb_lexer lexer = {0};
bool lexer_expect_keyword(const char* expected) { bool lexer_expect_keyword(const char* expected) {
@@ -41,16 +44,18 @@ bool lexer_expect(long expected, const char* expected_str) {
return true; return true;
} }
typedef struct { long lexer_peek() {
char* type; char* mark = lexer.parse_point;
char* name; if (!stb_c_lexer_get_token(&lexer)) {
} Field_Info; lexer.parse_point = mark;
return CLEX_eof;
}
typedef struct { long token = lexer.token;
char* name; lexer.parse_point = mark;
size_t fields_count;
Field_Info *fields; return token;
} Struct_Info; }
// parses typedef struct { FIELDS } TYPE_NAME // parses typedef struct { FIELDS } TYPE_NAME
bool parse_struct(Struct_Info* info) { bool parse_struct(Struct_Info* info) {
@@ -148,18 +153,112 @@ void generate_struct_info(FILE* stream, Struct_Info info) {
free(lowercase_name); free(lowercase_name);
} }
int main(void) bool read_entire_file(const char* file_path, char** content) {
{ bool result = false;
const char *source = "typedef struct { int int_field; bool bool_field; } My_Struct;"; FILE* file = fopen(file_path, "rb");
char string_store[1024]; if(file == NULL) goto fail;
stb_c_lexer_init(&lexer, source, source + strlen(source), string_store, sizeof(string_store) / sizeof(char));
Struct_Info info = {0}; if(fseek(file, 0, SEEK_END) < 0) goto fail;
if (!parse_struct(&info)) return 1;
printf("--- Parsed ------------\n"); long length = ftell(file);
print_struct(info); if(length < 0) goto fail;
printf("--- Generated ---------\n");
generate_struct_info(stdout, info); 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 <input_file>\n");
fprintf(stderr, "Usage: %s <input_file>\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; return 0;
} }