From a89589a634af35b00920e41f86b1b173bbd9640d Mon Sep 17 00:00:00 2001 From: 0x221E Date: Tue, 13 Jan 2026 23:32:03 +0100 Subject: [PATCH] Refactor: Fragment to linear memory style. Remove: Allocator from parser, lexer, configuration --- ibuild.c | 315 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 175 insertions(+), 140 deletions(-) diff --git a/ibuild.c b/ibuild.c index 342ce73..2f99729 100644 --- a/ibuild.c +++ b/ibuild.c @@ -9,7 +9,39 @@ #include #define CONFIG_FILE "IBUILD" +#define MAX_STATEMENTS 50 +#define MAX_TOKENS 200 + #define DIE(fmt, ...) die_t(__func__, __LINE__, fmt, ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) log_write(__func__, fmt, ## __VA_ARGS__) +#define LOG_USER(fmt, ...) log_write(NULL, fmt, ## __VA_ARGS__) + +/*** allocated memory ***/ +// There shall never be another heap allocation outside of thy block. +// Each system and/or type of allocation shall have its own memory. +// There shall be no arena managed in this state, if the allocated memory is redundant and will not affect performance. +// Arenas shall never be arena_free'd during program execution, only before exit. Use arena_reset if you wish to achieve +// the same functionality without free() calls. + +typedef struct +{ + Arena file_contents; + Arena configuration; + Arena strings; + Arena parser; +} Memory; + +static Memory memory; + +void memory_free() +{ + arena_free(&memory.file_contents); + arena_free(&memory.configuration); + arena_free(&memory.strings); + arena_free(&memory.parser); +} + +/*** logging ***/ // Developer-reporting error interface, along with steps to reproduce the error. // Include this log in your issues @@ -17,25 +49,60 @@ void die_t(const char* func, int line, const char* fmt, ...) { va_list args; va_start(args, fmt); - printf("ibuild exception(f:%s-l:%d) ", func, line); + printf("ibuild exception(f:%s-l:%d): ", func, line); vprintf(fmt, args); printf("\n"); printf("******Last syscall error: %s\n", strerror(errno)); exit(1); } -// User-reporting error interface -// func_name(): ERRMSG -void log_error(const char* func, const char* fmt, ...) +// User-reporting interface +// func_name: MSG +void log_write(const char* func, const char* fmt, ...) { + if(fmt == NULL) DIE("fmt must not be null!"); va_list args; va_start(args, fmt); - printf("%s: ", func); + if(func != NULL) + printf("%s: ", func); vprintf(fmt, args); printf("\n"); va_end(args); } +/*** file management ***/ + + +long file_get_size(FILE* fd) +{ + if(fd == NULL) DIE("fd cannot be NULL."); + if(fseek(fd, 0, SEEK_END) != 0) DIE("file exists, fseek SEEK_END fail."); + long size = ftell(fd); + if(fseek(fd, 0, SEEK_SET) != 0) DIE("file exists, fseek SEEK_SET fail."); + return size; +} + +char* file_get_content(Arena* a, const char* fp) +{ + if(fp == NULL) DIE("fp cannot be NULL"); + FILE* fd = fopen(fp, "r"); + long size = file_get_size(fd); + char* buf = (char*)arena_alloc(a, size + 1); + size_t ret = fread(buf, size, 1, fd); + if (ret != 1) DIE("fread returned %d", ret); + buf[size] = '\0'; + return buf; +} + +bool file_has_r_access(const char* f) +{ + if(access(f, F_OK | R_OK) == 0) + return true; + return false; +} + +/*** compilation process ***/ + typedef struct { char* compiler_path; @@ -43,20 +110,20 @@ typedef struct char* target_exec; char* src_dir; char* version; - char** src_files; -} CompileOptions; + char* dep_searcher; + char* dep_searcher_flags; +} Configuration; -void launch_compile(CompileOptions* co) +void launch_compile(Configuration* co) { - if(co == NULL) DIE("launch_compile() co has to be valid"); - - if(co->compiler_path == NULL) DIE("launch_compile() requires a valid compiler path"); + if(co == NULL) DIE("co has to be valid"); + if(co->compiler_path == NULL) DIE("requires a valid compiler path"); pid_t p = fork(); if(p<0) { - DIE("launch_compile() fork error"); + DIE("fork error"); } else if (p==0) { @@ -67,19 +134,11 @@ void launch_compile(CompileOptions* co) } } -bool detect_config_file() -{ - if(access(CONFIG_FILE, F_OK | R_OK) == 0) - return true; - return false; -} - /*** configuration lexer ***/ typedef struct { char *src; - Arena alloc; } Tokenizer; typedef enum @@ -122,7 +181,7 @@ bool is_part_of_key(char s) size_t skip_group(Tokenizer* t, bool (*func)(char)) { - if(func == NULL) DIE("skip_group() func invalid!"); + if(func == NULL) DIE("func invalid!"); size_t len = 0; while(func(*t->src)) { @@ -137,7 +196,7 @@ Token tokenize_identifier(Tokenizer *t) { char* temp = t->src; size_t len = skip_group(t, &is_part_of_key); - char* buf = (char*)arena_alloc(&t->alloc, sizeof(char) * len + 1); + char* buf = (char*)arena_alloc(&memory.strings, sizeof(char) * len + 1); memcpy(buf, temp, len); buf[len] = '\0'; return (Token){T_IDENTIFIER, buf}; @@ -157,7 +216,7 @@ Token tokenize_string(Tokenizer *t) if(*t->src == '"') t->src++; - char* buf = (char*)arena_alloc(&t->alloc, (sizeof(char) * len) + 1); + char* buf = (char*)arena_alloc(&memory.strings, (sizeof(char) * len) + 1); memcpy(buf, temp, len); buf[len] = '\0'; return (Token){T_STRING, buf}; @@ -198,6 +257,9 @@ typedef enum K_SRC_FILES = 3, K_BUILD_DIR = 4, K_TARGET_EXEC = 5, + K_DEP_SEARCHER = 6, + K_DEP_SEARCHER_FLAGS = 7, + K_VERSION = 8, } Key; typedef enum @@ -216,10 +278,9 @@ typedef struct typedef struct { - Arena* tokens_alloc; + Token* tokens; size_t loc; size_t cap; - Arena alloc; } Parser; typedef struct @@ -234,6 +295,8 @@ static KeyMap keyword_mappings[] = { {K_SRC_FILES, "SRC_FILES"}, {K_BUILD_DIR, "BUILD_DIR"}, {K_TARGET_EXEC, "TARGET_EXEC"}, + {K_DEP_SEARCHER, "DEP_SEARCHER"}, + {K_DEP_SEARCHER_FLAGS, "DEP_SEARCHER_FLAGS"}, {K_UNKNOWN, NULL}, }; @@ -255,9 +318,10 @@ Token parser_peek(Parser* p, size_t o) { if (p->loc + o >= p->cap) { - return ((Token*)p->tokens_alloc->start)[p->loc - 1]; + return p->tokens[p->loc - 1]; } - return ((Token*)p->tokens_alloc->start)[p->loc + o]; + Token t = p->tokens[p->loc + o]; + return t; } Token parser_previous(Parser* p) @@ -267,23 +331,23 @@ Token parser_previous(Parser* p) DIE("parser logic error."); } - return ((Token*)p->tokens_alloc->start)[p->loc - 1]; + return p->tokens[p->loc - 1]; } Token parser_advance(Parser* p) { if(p->loc + 1 >= p->cap) { - return ((Token*)p->tokens_alloc->start)[p->loc]; + return p->tokens[p->loc]; } p->loc++; - return ((Token*)p->tokens_alloc->start)[p->loc]; + return p->tokens[p->loc]; } Token parser_expect(Parser* p, TokenType tt, char* s) { - if(p == NULL) DIE("parser_expect() p must not be NULL"); - if(p == NULL) DIE("parser_expect() s must not be NULL"); + if(p == NULL) DIE("p must not be NULL"); + if(p == NULL) DIE("s must not be NULL"); if(parser_peek(p, 1).type != tt) DIE(s); @@ -296,7 +360,7 @@ Node* parser_expression(Parser* p) Token t = parser_advance(p); if(t.type == T_STRING) { - Node* node = (Node*)arena_alloc(&p->alloc, sizeof(Node)); + Node* node = (Node*)arena_alloc(&memory.parser, sizeof(Node)); node->key = K_NA; node->value = (void*)t.value; node->type = N_STRING; @@ -316,35 +380,25 @@ Node* parser_statement(Parser* p) if(k == K_UNKNOWN) DIE("Syntax error: Unexpected identifer encountered"); parser_expect(p, T_IS, "Syntax error: Expected '=' after identifier"); Node* expression = parser_expression(p); - Node* node = (Node*)arena_alloc(&p->alloc, sizeof(Node)); + Node* node = (Node*)arena_alloc(&memory.parser, sizeof(Node)); node->key = k; node->value = (void*)expression; node->type = N_PAIR; + return node; } else { - DIE("Syntax error: expected an identifier"); + DIE("Syntax error: expected an identifier, got: %d", t.type); } } -size_t parser_parse(Parser* p, Node*** out) +Node* parser_parse_next(Parser* p) { - Arena statements_alloc = ARENA_CONST; - - size_t size = 0; - - while(p->loc < p->cap - 1) - { - Node** temp = (Node**)arena_alloc(&statements_alloc, sizeof(Node*)); - *temp = parser_statement(p); - size++; - } - - *out = (Node**)statements_alloc.start; - - return size; + return parser_statement(p); } +#ifdef DEBUG + void debug_parser(Node* n, int indent) { for(int i = 0; i < indent; i++) @@ -365,128 +419,109 @@ void debug_parser(Node* n, int indent) printf(" Value: %s\n", (char*)n->value); break; } -} - -/*** configuration file management ***/ - -long get_file_size(FILE* fd) -{ - if(fd == NULL) DIE("get_file_size() fd cannot be NULL."); - if(fseek(fd, 0, SEEK_END) != 0) DIE("process_config() file exists, fseek SEEK_END fail."); - long size = ftell(fd); - if(fseek(fd, 0, SEEK_SET) != 0) DIE("process_config() file exists, fseek SEEK_SET fail."); - return size; } -typedef struct -{ - Arena tokens; -} ConfigMemory; +#endif -int process_config(Node*** ast) -{ - if(!detect_config_file()) - return 1; +/*** configuration process ***/ - printf("IBUILD Configuration file detected.\n"); - - ConfigMemory cm = {ARENA_CONST}; - - FILE* fd = fopen("IBUILD", "r"); - - if(fd == NULL) DIE("process_config() file exists however fd is NULL"); - - long fs = get_file_size(fd); - - printf("IBUILD file of size: %d\n", fs); - - if(fs < 2) return 0; - - char* config_mem = malloc(fs + 1); - - fread(config_mem, sizeof(char), fs, fd); - config_mem[fs] = '\0'; - - printf("Config file:\n%s\n", config_mem); - - size_t t_count = 0; - - Tokenizer t = { .src = config_mem, .alloc = ARENA_CONST}; - while(1) - { - Token token = tokenizer_next(&t); - if(token.type == T_INVALID) DIE("illegal token!"); - - Token* tm = arena_alloc(&cm.tokens, sizeof(Token)); - *tm = token; - - t_count++; - if(token.type == T_EOF) break; - } - - Parser parser = {.tokens_alloc = &cm.tokens, .loc = 0, .cap = t_count, .alloc = ARENA_CONST}; - - Node** statements = NULL; - size_t statements_len = parser_parse(&parser, &statements); - - if(statements == NULL) DIE("parser return invalid results"); - - for (size_t i = 0; i < statements_len; i++) - debug_parser(statements[i], 0); - - free(config_mem); - arena_free(&cm.tokens); - *ast = statements; - return statements_len; -} - -/*** build configuration process ***/ - -void set_compiler_option(CompileOptions* co, Key k, char* val) +void set_configuration_option(Configuration* co, Key k, char* val) { switch(k) { case K_COMPILER_PATH: co->compiler_path = val; break; case K_BUILD_DIR: co->build_dir = val; break; case K_TARGET_EXEC: co->target_exec = val; break; + case K_DEP_SEARCHER: co->dep_searcher = val; break; + case K_DEP_SEARCHER_FLAGS: co->dep_searcher_flags = val; break; + case K_VERSION: co->version = val; break; default: DIE("Invalid compiler option was supplied!"); } } -void populate_compile_options(CompileOptions* co, Node* n) +void populate_configuration_node(Configuration* co, Node* n) { switch(n->type) { case N_PAIR: Key k = n->key; - set_compiler_option(co, k, (char*)((Node*)n->value)->value); + set_configuration_option(co, k, (char*)((Node*)n->value)->value); break; } } -void build(int len, Node** st) +Configuration setup_configuration(int len, Node** st) { - CompileOptions co; - co.compiler_path = "/usr/bin/gcc"; - co.build_dir = "."; - co.src_dir = "."; - co.target_exec = "iexec"; + Configuration config; + config.compiler_path = "/usr/bin/gcc"; + config.build_dir = "."; + config.src_dir = "."; + config.target_exec = "iexec"; + config.dep_searcher = "/usr/bin/gcc"; + config.dep_searcher_flags = "-MM"; for (size_t i = 0; i < len; i++) - populate_compile_options(&co, st[i]); + populate_configuration_node(&config, st[i]); - launch_compile(&co); - printf("Compilation finished.\n"); + LOG_USER("Configured IBUILD options."); + return config; } + +/*** configuration file management ***/ + +Configuration process_config() +{ + if(!file_has_r_access(CONFIG_FILE)) + { + return setup_configuration(0, NULL); + } + + LOG_USER("IBUILD Configuration file detected.\n"); + char* config_mem = file_get_content(&memory.file_contents, CONFIG_FILE); + size_t t_count = 0; + + Token tokens[MAX_TOKENS]; + Tokenizer t = { .src = config_mem }; + + while(1) + { + if(t_count > MAX_TOKENS) DIE("Maximum number of tokens reached! \n Could not read configuration file."); + Token token = tokenizer_next(&t); + if(token.type == T_INVALID) DIE("illegal token!"); + tokens[t_count] = token; + t_count++; + if(token.type == T_EOF) break; + } + + Parser parser = {.tokens = tokens, .loc = 0, .cap = t_count }; + Node* statements[MAX_STATEMENTS]; + size_t p_count = 0; + + while(p_count < MAX_STATEMENTS && parser.loc < parser.cap - 1) + { + statements[p_count] = parser_parse_next(&parser); + p_count++; + } + +#ifdef DEBUG + + for (size_t i = 0; i < p_count; i++) + debug_parser(statements[i], 0); + +#endif + + return setup_configuration(p_count, statements); +} + + int main(int argc, char** argv) { - printf("Build version: 0.0.1\n"); + LOG_USER("Build version: %s", VERSION); Node** ast = NULL; - int len_ast = process_config(&ast); - - build(len_ast, ast); - + Configuration c = process_config(); + launch_compile(&c); + memory_free(); return 0; }