#include "deps/utils/src/arena_alloc.h" #include #include #include #include #include void die(char* s) { printf("BuildSystem Error: %s\n", s); // printf("Last syscall error: %s", ERRNO); exit(1); } typedef struct { char* compiler_path; char* build_dir; char* target_exec; char* src_dir; char* version; } CompileOptions; void launch_compile(CompileOptions* 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"); pid_t p = fork(); if(p<0) die("launch_compile() fork error"); else if (p==0) { char* args[] = {"compile", "test.c", "-o", "test", NULL}; if((execvp(co->compiler_path, args)) == -1) die("launch_compile() execvp error"); } } bool detect_config_file() { if(access("IBUILD", F_OK | R_OK) == 0) return true; return false; } /*** configuration lexer ***/ typedef struct { char *src; Arena alloc; } Tokenizer; typedef enum { T_INVALID = -1, T_IDENTIFIER = 0, T_STRING = 1, T_IS = 2, T_EOF = 3, } TokenType; typedef struct { TokenType type; char* value; int line; } Token; #define TOKEN_CONST {T_INVALID, NULL, 0} bool is_alpha_uppercase(char s) { return (s >= 'A' && s <= 'Z'); } bool is_alpha_lowercase(char s) { return (s >= 'a' && s <= 'z'); } bool is_whitespace(char s) { return s == ' ' || s == '\t' || s == '\r' || s == '\n'; } bool is_part_of_key(char s) { return is_alpha_uppercase(s) || s == '_'; } size_t skip_group(Tokenizer* t, bool (*func)(char)) { if(func == NULL) die("skip_group() func invalid!"); size_t len = 0; while(func(*t->src)) { len++; t->src += 1; } return len; } 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); memcpy(buf, temp, len); buf[len] = '\0'; return (Token){T_IDENTIFIER, buf}; } Token tokenize_string(Tokenizer *t) { t->src += 1; char* temp = t->src; size_t len = 0; while(*t->src != '"' && *t->src != '\0') { len++; t->src += 1; } if(*t->src == '"') t->src++; char* buf = (char*)arena_alloc(&t->alloc, (sizeof(char) * len) + 1); memcpy(buf, temp, len); buf[len] = '\0'; return (Token){T_STRING, buf}; } Token tokenizer_next(Tokenizer* t) { if(is_whitespace(*t->src)) skip_group(t, &is_whitespace); if(is_alpha_uppercase(*t->src)) { return tokenize_identifier(t); } switch(*t->src) { case '"': return tokenize_string(t); case '=': t->src++; return (Token){T_IS, NULL}; } if(*t->src == '\0') return (Token){T_EOF, NULL}; t->src++; return (Token){T_INVALID, NULL}; } /*** configuration parser ***/ typedef enum { K_UNKNOWN = 0, K_COMPILER_PATH = 1, K_SRC_DIR = 2, K_BUILD_DIR = 3, } Key; typedef enum { N_STRING = 0, N_PAIR = 1 } NodeType; typedef struct { NodeType type; Key key; void* value; } Node; typedef struct { Tokenizer* tokenizer; size_t loc; size_t cap; Arena alloc; } Parser; typedef struct { Key key; const char* value; } KeyMap; static KeyMap keyword_mappings[] = { {K_COMPILER_PATH, "COMPILER_PATH"}, {K_SRC_DIR, "SRC_DIR"}, {K_BUILD_DIR, "BUILD_DIR"}, {K_UNKNOWN, NULL}, }; Key key_lookup(char* s) { if(s == NULL) return K_UNKNOWN; for(const KeyMap* ptr = keyword_mappings; ptr->value != NULL; ptr++) { if(strcmp(ptr->value, s) == 0) { return ptr->key; } } return K_UNKNOWN; } Token parser_peek(Parser* p, size_t o) { if (p->loc + o >= p->cap) { return ((Token*)p->tokenizer->alloc.start)[p->loc - 1]; } return ((Token*)p->tokenizer->alloc.start)[p->loc + o]; } Token parser_previous(Parser* p) { if(p->loc - 1 < 0) { die("parser logic error."); } return ((Token*)p->tokenizer->alloc.start)[p->loc - 1]; } Token parser_advance(Parser* p) { if(p->loc + 1 >= p->cap) { return ((Token*)p->tokenizer->alloc.start)[p->loc]; } p->loc++; return ((Token*)p->tokenizer->alloc.start)[p->loc]; } Node* parser_expression(Parser* p) { Token t = parser_peek(p,0); if(t.type == T_STRING) { Key k = key_lookup(t.value); if(k == K_UNKNOWN) die("Syntax error: Unexpected expression encountered"); Node* node = (Node*)arena_alloc(&p->alloc, sizeof(Node)); node->key = k; node->value = (void*)t.value; node->type = N_STRING; return node; } die("Syntax error: Invalid expression."); } Node* parser_statement(Parser* p) { Token t = parser_advance(p); if(t.type == T_IDENTIFIER) { Key k = key_lookup(t.value); if(k == K_UNKNOWN) die("Syntax error: Unexpected identifer encountered"); Node* expression = parser_expression(p); Node* node = (Node*)arena_alloc(&p->alloc, sizeof(Node)); node->key = k; node->value = (void*)expression; node->type = N_PAIR; } else { printf("%d", t.type); die("Syntax error: expected an identifier"); } } Node* parser_parse(Parser* p) { return parser_statement(p); } /*** 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; } int process_config(Arena* tokens) { if(!detect_config_file()) return 1; printf("IBUILD Configuration file detected.\n"); 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); 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(tokens, sizeof(Token)); *tm = token; t_count++; if(token.type == T_EOF) break; } Parser parser = {.tokenizer = &t, .loc = 0, .cap = t_count, .alloc = ARENA_CONST}; parser_parse(&parser); free(config_mem); return 0; } /*** build configuration process ***/ /* void populate_compile_options(CompileOptions* co, Token* t) { switch(t->type) { case T_KEY_COMPILER_PATH: co->compiler_path = t->value; break; case T_KEY_BUILD_DIR: co->build_dir = t->value; break; case T_KEY_TARGET_EXEC: co->target_exec = t->value; break; case T_KEY_SRC_DIR: co->compiler_path = t->value; break; case T_KEY_VERSION: co->version = t->value; break; } }*/ void build(Token* options) { CompileOptions co; co.compiler_path = "/usr/bin/gcc"; Token* temp = options; for(; temp->type != T_EOF; temp++) { // populate_compile_options(&co, temp); } launch_compile(&co); printf("Compilation finished.\n"); } int main(int argc, char** argv) { Arena a = ARENA_CONST; process_config(&a); Token* ptr = (Token*)a.start; for(; ptr->type != T_EOF; ptr++) { printf("TokenType: %d, Value: %s\n", ptr->type, ptr->value); } return 0; }