diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-11 15:11:00 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-11 15:11:00 +0000 |
| commit | 55247a3f12a9eee7ba3fd7ca6d8fcea7a82c20f3 (patch) | |
| tree | a2a71e2eb8ca0b2c483518c1902d89d18709c9ab /src/utils/utils.c | |
| parent | 2e7abed7cfe84a2c0df371cde35f8f68cfdca16c (diff) | |
Added src/ folder. Now I will add the rest.
Diffstat (limited to 'src/utils/utils.c')
| -rw-r--r-- | src/utils/utils.c | 740 |
1 files changed, 740 insertions, 0 deletions
diff --git a/src/utils/utils.c b/src/utils/utils.c new file mode 100644 index 0000000..08ae799 --- /dev/null +++ b/src/utils/utils.c @@ -0,0 +1,740 @@ + +#include "zprep.h" +#include "parser.h" + +char *g_current_filename = "unknown"; +ParserContext *g_parser_ctx = NULL; + +// ** Arena Implementation ** +#define ARENA_BLOCK_SIZE (1024 * 1024) + +typedef struct ArenaBlock +{ + struct ArenaBlock *next; + size_t used; + size_t cap; + char data[]; +} ArenaBlock; + +static ArenaBlock *current_block = NULL; + +static void *arena_alloc_raw(size_t size) +{ + size_t actual_size = size + sizeof(size_t); + actual_size = (actual_size + 7) & ~7; + + if (!current_block || (current_block->used + actual_size > current_block->cap)) + { + size_t block_size = actual_size > ARENA_BLOCK_SIZE ? actual_size : ARENA_BLOCK_SIZE; +#undef malloc + ArenaBlock *new_block = malloc(sizeof(ArenaBlock) + block_size); + if (!new_block) + { + fprintf(stderr, "Fatal: Out of memory\n"); + exit(1); + } + + new_block->cap = block_size; + new_block->used = 0; + new_block->next = current_block; + current_block = new_block; + } + + void *ptr = current_block->data + current_block->used; + current_block->used += actual_size; + *(size_t *)ptr = size; + return (char *)ptr + sizeof(size_t); +} + +void *xmalloc(size_t size) +{ + return arena_alloc_raw(size); +} + +void *xcalloc(size_t n, size_t size) +{ + size_t total = n * size; + void *p = arena_alloc_raw(total); + memset(p, 0, total); + return p; +} + +void *xrealloc(void *ptr, size_t new_size) +{ + if (!ptr) + { + return xmalloc(new_size); + } + size_t *header = (size_t *)((char *)ptr - sizeof(size_t)); + size_t old_size = *header; + if (new_size <= old_size) + { + return ptr; + } + void *new_ptr = xmalloc(new_size); + memcpy(new_ptr, ptr, old_size); + return new_ptr; +} + +char *xstrdup(const char *s) +{ + if (!s) + { + return NULL; + } + size_t len = strlen(s); + char *d = xmalloc(len + 1); + memcpy(d, s, len); + d[len] = 0; + return d; +} + +void zpanic(const char *fmt, ...) +{ + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); + exit(1); +} + +// Warning system (non-fatal). +void zwarn(const char *fmt, ...) +{ + if (g_config.quiet) + { + return; + } + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); +} + +void zwarn_at(Token t, const char *fmt, ...) +{ + if (g_config.quiet) + { + return; + } + // Header: 'warning: message'. + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_YELLOW "warning: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); + + // Location. + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line, + t.col); + + // Context. Only if token has valid data. + if (t.start) + { + const char *line_start = t.start - (t.col - 1); + const char *line_end = t.start; + while (*line_end && *line_end != '\n') + { + line_end++; + } + int line_len = line_end - line_start; + + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); + fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); + + // Caret. + for (int i = 0; i < t.col - 1; i++) + { + fprintf(stderr, " "); + } + fprintf(stderr, COLOR_YELLOW "^ here" COLOR_RESET "\n"); + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + } +} + +void zpanic_at(Token t, const char *fmt, ...) +{ + // Header: 'error: message'. + va_list a; + va_start(a, fmt); + fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD); + vfprintf(stderr, fmt, a); + fprintf(stderr, COLOR_RESET "\n"); + va_end(a); + + // Location: '--> file:line:col'. + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line, + t.col); + + // Context line. + const char *line_start = t.start - (t.col - 1); + const char *line_end = t.start; + while (*line_end && *line_end != '\n') + { + line_end++; + } + int line_len = line_end - line_start; + + // Visual bar. + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); + fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); + + // caret + for (int i = 0; i < t.col - 1; i++) + { + fprintf(stderr, " "); + } + fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n"); + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + + if (g_parser_ctx && g_parser_ctx->is_fault_tolerant && g_parser_ctx->on_error) + { + // Construct error message buffer + char msg[1024]; + va_list args2; + va_start(args2, fmt); + vsnprintf(msg, sizeof(msg), fmt, args2); + va_end(args2); + + g_parser_ctx->on_error(g_parser_ctx->error_callback_data, t, msg); + return; // Recover! + } + + exit(1); +} + +// Enhanced error with suggestion. +void zpanic_with_suggestion(Token t, const char *msg, const char *suggestion) +{ + // Header. + fprintf(stderr, COLOR_RED "error: " COLOR_RESET COLOR_BOLD "%s" COLOR_RESET "\n", msg); + + // Location. + fprintf(stderr, COLOR_BLUE " --> " COLOR_RESET "%s:%d:%d\n", g_current_filename, t.line, + t.col); + + // Context. + const char *line_start = t.start - (t.col - 1); + const char *line_end = t.start; + while (*line_end && *line_end != '\n') + { + line_end++; + } + int line_len = line_end - line_start; + + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_BLUE "%-3d| " COLOR_RESET "%.*s\n", t.line, line_len, line_start); + fprintf(stderr, COLOR_BLUE " | " COLOR_RESET); + for (int i = 0; i < t.col - 1; i++) + { + fprintf(stderr, " "); + } + fprintf(stderr, COLOR_RED "^ here" COLOR_RESET "\n"); + + // Suggestion. + if (suggestion) + { + fprintf(stderr, COLOR_BLUE " |\n" COLOR_RESET); + fprintf(stderr, COLOR_CYAN " = help: " COLOR_RESET "%s\n", suggestion); + } + + exit(1); +} + +// Specific error types with helpful messages. +void error_undefined_function(Token t, const char *func_name, const char *suggestion) +{ + char msg[256]; + sprintf(msg, "Undefined function '%s'", func_name); + + if (suggestion) + { + char help[512]; + sprintf(help, "Did you mean '%s'?", suggestion); + zpanic_with_suggestion(t, msg, help); + } + else + { + zpanic_with_suggestion(t, msg, "Check if the function is defined or imported"); + } +} + +void error_wrong_arg_count(Token t, const char *func_name, int expected, int got) +{ + char msg[256]; + sprintf(msg, "Wrong number of arguments to function '%s'", func_name); + + char help[256]; + sprintf(help, "Expected %d argument%s, but got %d", expected, expected == 1 ? "" : "s", got); + + zpanic_with_suggestion(t, msg, help); +} + +void error_undefined_field(Token t, const char *struct_name, const char *field_name, + const char *suggestion) +{ + char msg[256]; + sprintf(msg, "Struct '%s' has no field '%s'", struct_name, field_name); + + if (suggestion) + { + char help[256]; + sprintf(help, "Did you mean '%s'?", suggestion); + zpanic_with_suggestion(t, msg, help); + } + else + { + zpanic_with_suggestion(t, msg, "Check the struct definition"); + } +} + +void error_type_expected(Token t, const char *expected, const char *got) +{ + char msg[256]; + sprintf(msg, "Type mismatch"); + + char help[512]; + sprintf(help, "Expected type '%s', but found '%s'", expected, got); + + zpanic_with_suggestion(t, msg, help); +} + +void error_cannot_index(Token t, const char *type_name) +{ + char msg[256]; + sprintf(msg, "Cannot index into type '%s'", type_name); + + zpanic_with_suggestion(t, msg, "Only arrays and slices can be indexed"); +} + +void warn_unused_variable(Token t, const char *var_name) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Unused variable '%s'", var_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "Consider removing it or prefixing with '_'\n"); +} + +void warn_shadowing(Token t, const char *var_name) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Variable '%s' shadows a previous declaration", var_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This can lead to confusion\n"); +} + +void warn_unreachable_code(Token t) +{ + if (g_config.quiet) + { + return; + } + zwarn_at(t, "Unreachable code detected"); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This code will never execute\n"); +} + +void warn_implicit_conversion(Token t, const char *from_type, const char *to_type) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Implicit conversion from '%s' to '%s'", from_type, to_type); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Consider using an explicit cast\n"); +} + +void warn_missing_return(Token t, const char *func_name) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Function '%s' may not return a value in all paths", func_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Add a return statement or make the function return 'void'\n"); +} + +void warn_comparison_always_true(Token t, const char *reason) +{ + if (g_config.quiet) + { + return; + } + zwarn_at(t, "Comparison is always true"); + if (reason) + { + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason); + } +} + +void warn_comparison_always_false(Token t, const char *reason) +{ + if (g_config.quiet) + { + return; + } + zwarn_at(t, "Comparison is always false"); + if (reason) + { + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "%s\n", reason); + } +} + +void warn_unused_parameter(Token t, const char *param_name, const char *func_name) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Unused parameter '%s' in function '%s'", param_name, func_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Consider prefixing with '_' if intentionally unused\n"); +} + +void warn_narrowing_conversion(Token t, const char *from_type, const char *to_type) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Narrowing conversion from '%s' to '%s'", from_type, to_type); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "This may cause data loss\n"); +} + +void warn_division_by_zero(Token t) +{ + if (g_config.quiet) + { + return; + } + zwarn_at(t, "Division by zero"); + fprintf(stderr, + COLOR_CYAN " = note: " COLOR_RESET "This will cause undefined behavior at runtime\n"); +} + +void warn_integer_overflow(Token t, const char *type_name, long long value) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Integer literal %lld overflows type '%s'", value, type_name); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Value will be truncated\n"); +} + +void warn_array_bounds(Token t, int index, int size) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Array index %d is out of bounds for array of size %d", index, size); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Valid indices are 0 to %d\n", size - 1); +} + +void warn_format_string(Token t, int arg_num, const char *expected, const char *got) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Format argument %d: expected '%s', got '%s'", arg_num, expected, got); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET + "Mismatched format specifier may cause undefined behavior\n"); +} + +void warn_null_pointer(Token t, const char *expr) +{ + if (g_config.quiet) + { + return; + } + char msg[256]; + sprintf(msg, "Potential null pointer access in '%s'", expr); + zwarn_at(t, "%s", msg); + fprintf(stderr, COLOR_CYAN " = note: " COLOR_RESET "Add a null check before accessing\n"); +} + +char *load_file(const char *fn) +{ + FILE *f = fopen(fn, "rb"); + if (!f) + { + return 0; + } + fseek(f, 0, SEEK_END); + long l = ftell(f); + rewind(f); + char *b = xmalloc(l + 1); + fread(b, 1, l, f); + b[l] = 0; + fclose(f); + return b; +} + +// ** Build Directives ** +char g_link_flags[MAX_FLAGS_SIZE] = ""; +char g_cflags[MAX_FLAGS_SIZE] = ""; +CompilerConfig g_config = {0}; + +void scan_build_directives(ParserContext *ctx, const char *src) +{ + const char *p = src; + while (*p) + { + if (p[0] == '/' && p[1] == '/' && p[2] == '>') + { + p += 3; + while (*p == ' ') + { + p++; + } + + const char *start = p; + int len = 0; + while (p[len] && p[len] != '\n') + { + len++; + } + + char line[2048]; + if (len >= 2047) + { + len = 2047; + } + strncpy(line, start, len); + line[len] = 0; + + if (0 == strncmp(line, "link:", 5)) + { + if (strlen(g_link_flags) > 0) + { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, line + 5); + } + else if (0 == strncmp(line, "cflags:", 7)) + { + if (strlen(g_cflags) > 0) + { + strcat(g_cflags, " "); + } + strcat(g_cflags, line + 7); + } + else if (0 == strncmp(line, "include:", 8)) + { + char flags[2048]; + sprintf(flags, "-I%s", line + 8); + if (strlen(g_cflags) > 0) + { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); + } + else if (strncmp(line, "lib:", 4) == 0) + { + char flags[2048]; + sprintf(flags, "-L%s", line + 4); + if (strlen(g_link_flags) > 0) + { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, flags); + } + else if (strncmp(line, "define:", 7) == 0) + { + char flags[2048]; + sprintf(flags, "-D%s", line + 7); + if (strlen(g_cflags) > 0) + { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); + } + else if (0 == strncmp(line, "shell:", 6)) + { + char *cmd = line + 6; + // printf("[zprep] Running shell: %s\n", cmd); + int res = system(cmd); + if (res != 0) + { + zpanic("Shell directive failed: %s", cmd); + } + } + else if (strncmp(line, "get:", 4) == 0) + { + char *url = line + 4; + while (*url == ' ') + { + url++; + } + + char *filename = strrchr(url, '/'); + if (!filename) + { + filename = "downloaded_file"; + } + else + { + filename++; + } + + // Check if file exists to avoid redownloading. + FILE *f = fopen(filename, "r"); + if (f) + { + fclose(f); + } + else + { + printf("[zprep] Downloading %s...\n", filename); + char cmd[8192]; + // Try wget, then curl. + sprintf(cmd, "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", url, + filename, url, filename); + if (system(cmd) != 0) + { + zpanic("Failed to download %s", url); + } + } + } + else if (strncmp(line, "pkg-config:", 11) == 0) + { + char *libs = line + 11; + while (*libs == ' ') + { + libs++; + } + + char cmd[4096]; + sprintf(cmd, "pkg-config --cflags %s", libs); + FILE *fp = popen(cmd, "r"); + if (fp) + { + char flags[4096]; + flags[0] = 0; + fgets(flags, sizeof(flags), fp); + pclose(fp); + int len = strlen(flags); + if (len > 0 && flags[len - 1] == '\n') + { + flags[len - 1] = 0; + } + if (strlen(g_cflags) > 0) + { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); + } + + sprintf(cmd, "pkg-config --libs %s", libs); + fp = popen(cmd, "r"); + if (fp) + { + char flags[4096]; + flags[0] = 0; + fgets(flags, sizeof(flags), fp); + pclose(fp); + int len = strlen(flags); + if (len > 0 && flags[len - 1] == '\n') + { + flags[len - 1] = 0; + } + if (strlen(g_link_flags) > 0) + { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, flags); + } + } + else if (strncmp(line, "immutable-by-default", 20) == 0) + { + ctx->immutable_by_default = 1; + } + + p += len; + } + while (*p && *p != '\n') + { + p++; + } + + if (*p == '\n') + { + p++; + } + } +} + +// Levenshtein distance for "did you mean?" suggestions. +int levenshtein(const char *s1, const char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + // Quick optimization. + if (abs(len1 - len2) > 3) + { + return 999; + } + + int matrix[len1 + 1][len2 + 1]; + + for (int i = 0; i <= len1; i++) + { + matrix[i][0] = i; + } + for (int j = 0; j <= len2; j++) + { + matrix[0][j] = j; + } + + for (int i = 1; i <= len1; i++) + { + for (int j = 1; j <= len2; j++) + { + int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; + int del = matrix[i - 1][j] + 1; + int ins = matrix[i][j - 1] + 1; + int sub = matrix[i - 1][j - 1] + cost; + + matrix[i][j] = (del < ins) ? del : ins; + if (sub < matrix[i][j]) + { + matrix[i][j] = sub; + } + } + } + + return matrix[len1][len2]; +} |
