2026-01-09 21:29:18 +01:00
|
|
|
#include "deps/utils/src/arena_alloc.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
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};
|
2026-01-09 23:03:27 +01:00
|
|
|
printf("SET COMPILER: %s\n", co->compiler_path);
|
|
|
|
|
if((execvp(co->compiler_path, args)) == -1)
|
2026-01-09 21:29:18 +01:00
|
|
|
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
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
K_NA = -1,
|
2026-01-09 21:29:18 +01:00
|
|
|
K_UNKNOWN = 0,
|
|
|
|
|
K_COMPILER_PATH = 1,
|
|
|
|
|
K_SRC_DIR = 2,
|
|
|
|
|
K_BUILD_DIR = 3,
|
2026-01-09 23:03:27 +01:00
|
|
|
K_TARGET_EXEC = 4,
|
2026-01-09 21:29:18 +01:00
|
|
|
} Key;
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
N_STRING = 0,
|
|
|
|
|
N_PAIR = 1
|
|
|
|
|
} NodeType;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
NodeType type;
|
|
|
|
|
Key key;
|
|
|
|
|
void* value;
|
|
|
|
|
} Node;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
Arena* tokens_alloc;
|
2026-01-09 21:29:18 +01:00
|
|
|
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"},
|
2026-01-09 23:03:27 +01:00
|
|
|
{K_TARGET_EXEC, "TARGET_EXEC"},
|
2026-01-09 21:29:18 +01:00
|
|
|
{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)
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
return ((Token*)p->tokens_alloc->start)[p->loc - 1];
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
2026-01-09 23:03:27 +01:00
|
|
|
return ((Token*)p->tokens_alloc->start)[p->loc + o];
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Token parser_previous(Parser* p)
|
|
|
|
|
{
|
|
|
|
|
if(p->loc - 1 < 0)
|
|
|
|
|
{
|
|
|
|
|
die("parser logic error.");
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
return ((Token*)p->tokens_alloc->start)[p->loc - 1];
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Token parser_advance(Parser* p)
|
|
|
|
|
{
|
|
|
|
|
if(p->loc + 1 >= p->cap)
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
return ((Token*)p->tokens_alloc->start)[p->loc];
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
|
|
|
|
p->loc++;
|
2026-01-09 23:03:27 +01:00
|
|
|
return ((Token*)p->tokens_alloc->start)[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(parser_peek(p, 1).type != tt)
|
|
|
|
|
die(s);
|
|
|
|
|
|
|
|
|
|
return parser_advance(p);
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Node* parser_expression(Parser* p)
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
Token t = parser_advance(p);
|
2026-01-09 21:29:18 +01:00
|
|
|
if(t.type == T_STRING)
|
|
|
|
|
{
|
|
|
|
|
Node* node = (Node*)arena_alloc(&p->alloc, sizeof(Node));
|
2026-01-09 23:03:27 +01:00
|
|
|
node->key = K_NA;
|
2026-01-09 21:29:18 +01:00
|
|
|
node->value = (void*)t.value;
|
|
|
|
|
node->type = N_STRING;
|
2026-01-09 23:03:27 +01:00
|
|
|
parser_advance(p);
|
2026-01-09 21:29:18 +01:00
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
die("Syntax error: Invalid expression.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Node* parser_statement(Parser* p)
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
Token t = parser_peek(p, 0);
|
2026-01-09 21:29:18 +01:00
|
|
|
if(t.type == T_IDENTIFIER)
|
|
|
|
|
{
|
|
|
|
|
Key k = key_lookup(t.value);
|
|
|
|
|
if(k == K_UNKNOWN) die("Syntax error: Unexpected identifer encountered");
|
2026-01-09 23:03:27 +01:00
|
|
|
parser_expect(p, T_IS, "Syntax error: Expected '=' after identifier");
|
2026-01-09 21:29:18 +01:00
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
die("Syntax error: expected an identifier");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
size_t parser_parse(Parser* p, Node*** out)
|
2026-01-09 21:29:18 +01:00
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
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;
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
void debug_parser(Node* n, int indent)
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < indent; i++)
|
|
|
|
|
{
|
|
|
|
|
printf(" ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(n->type)
|
|
|
|
|
{
|
|
|
|
|
case N_PAIR:
|
|
|
|
|
printf("PAIR STATEMENT");
|
|
|
|
|
printf(" Key: %10d\n", n->key);
|
|
|
|
|
if(n->value != NULL)
|
|
|
|
|
debug_parser((Node*)n->value, 2);
|
|
|
|
|
break;
|
|
|
|
|
case N_STRING:
|
|
|
|
|
printf("STRING EXPRESSION");
|
|
|
|
|
printf(" Value: %s\n", (char*)n->value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 21:29:18 +01:00
|
|
|
/*** 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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
Arena tokens;
|
|
|
|
|
} ConfigMemory;
|
|
|
|
|
|
|
|
|
|
int process_config(Node*** ast)
|
2026-01-09 21:29:18 +01:00
|
|
|
{
|
|
|
|
|
if(!detect_config_file())
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
printf("IBUILD Configuration file detected.\n");
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
ConfigMemory cm = {ARENA_CONST};
|
|
|
|
|
|
2026-01-09 21:29:18 +01:00
|
|
|
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);
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
if(fs < 2) return 0;
|
|
|
|
|
|
2026-01-09 21:29:18 +01:00
|
|
|
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!");
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
Token* tm = arena_alloc(&cm.tokens, sizeof(Token));
|
2026-01-09 21:29:18 +01:00
|
|
|
*tm = token;
|
|
|
|
|
|
|
|
|
|
t_count++;
|
|
|
|
|
if(token.type == T_EOF) break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
Parser parser = {.tokens_alloc = &cm.tokens, .loc = 0, .cap = t_count, .alloc = ARENA_CONST};
|
2026-01-09 21:29:18 +01:00
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
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);
|
|
|
|
|
|
2026-01-09 21:29:18 +01:00
|
|
|
free(config_mem);
|
2026-01-09 23:03:27 +01:00
|
|
|
arena_free(&cm.tokens);
|
|
|
|
|
*ast = statements;
|
|
|
|
|
return statements_len;
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** build configuration process ***/
|
2026-01-09 23:03:27 +01:00
|
|
|
|
|
|
|
|
void set_compiler_option(CompileOptions* 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;
|
|
|
|
|
default:
|
|
|
|
|
die("Invalid compiler option was supplied!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void populate_compile_options(CompileOptions* co, Node* n)
|
2026-01-09 21:29:18 +01:00
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
switch(n->type)
|
2026-01-09 21:29:18 +01:00
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
case N_PAIR:
|
|
|
|
|
Key k = n->key;
|
|
|
|
|
set_compiler_option(co, k, (char*)((Node*)n->value)->value);
|
|
|
|
|
break;
|
2026-01-09 21:29:18 +01:00
|
|
|
}
|
2026-01-09 23:03:27 +01:00
|
|
|
}
|
2026-01-09 21:29:18 +01:00
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
void build(int len, Node** st)
|
2026-01-09 21:29:18 +01:00
|
|
|
{
|
|
|
|
|
CompileOptions co;
|
|
|
|
|
co.compiler_path = "/usr/bin/gcc";
|
2026-01-09 23:03:27 +01:00
|
|
|
co.build_dir = ".";
|
|
|
|
|
co.src_dir = ".";
|
|
|
|
|
co.target_exec = "iexec";
|
2026-01-09 21:29:18 +01:00
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
for (size_t i = 0; i < len; i++)
|
|
|
|
|
populate_compile_options(&co, st[i]);
|
2026-01-09 21:29:18 +01:00
|
|
|
|
|
|
|
|
launch_compile(&co);
|
|
|
|
|
printf("Compilation finished.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
|
{
|
2026-01-09 23:03:27 +01:00
|
|
|
Node** ast = NULL;
|
|
|
|
|
int len_ast = process_config(&ast);
|
2026-01-09 21:29:18 +01:00
|
|
|
|
2026-01-09 23:03:27 +01:00
|
|
|
build(len_ast, ast);
|
|
|
|
|
|
2026-01-09 21:29:18 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|