e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
751 lines
29 KiB
C
751 lines
29 KiB
C
#include "c_import.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static bool c_ident_char(char ch) {
|
|
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_';
|
|
}
|
|
|
|
static char *trim_span_copy(const char *start, const char *end) {
|
|
if (!start || !end || end < start) return z_strdup("");
|
|
while (start < end && isspace((unsigned char)*start)) start++;
|
|
while (end > start && isspace((unsigned char)end[-1])) end--;
|
|
return z_strndup(start, (size_t)(end - start));
|
|
}
|
|
|
|
char *z_c_header_strip_comments(const char *header) {
|
|
ZBuf out;
|
|
zbuf_init(&out);
|
|
const char *cursor = header ? header : "";
|
|
while (*cursor) {
|
|
if (cursor[0] == '/' && cursor[1] == '/') {
|
|
cursor += 2;
|
|
while (*cursor && *cursor != '\n') cursor++;
|
|
if (*cursor == '\n') zbuf_append_char(&out, *cursor++);
|
|
continue;
|
|
}
|
|
if (cursor[0] == '/' && cursor[1] == '*') {
|
|
zbuf_append_char(&out, ' ');
|
|
cursor += 2;
|
|
while (*cursor) {
|
|
if (cursor[0] == '*' && cursor[1] == '/') {
|
|
cursor += 2;
|
|
break;
|
|
}
|
|
if (*cursor == '\n') zbuf_append_char(&out, '\n');
|
|
cursor++;
|
|
}
|
|
continue;
|
|
}
|
|
if (*cursor == '"' || *cursor == '\'') {
|
|
char quote = *cursor;
|
|
zbuf_append_char(&out, *cursor++);
|
|
while (*cursor) {
|
|
char ch = *cursor++;
|
|
zbuf_append_char(&out, ch);
|
|
if (ch == '\\' && *cursor) {
|
|
zbuf_append_char(&out, *cursor++);
|
|
continue;
|
|
}
|
|
if (ch == quote) break;
|
|
}
|
|
continue;
|
|
}
|
|
zbuf_append_char(&out, *cursor++);
|
|
}
|
|
return out.data ? out.data : z_strdup("");
|
|
}
|
|
|
|
static const char *last_ident_start_before(const char *start, const char *end) {
|
|
const char *cursor = end;
|
|
while (cursor > start && !c_ident_char(cursor[-1])) cursor--;
|
|
const char *ident_end = cursor;
|
|
while (cursor > start && c_ident_char(cursor[-1])) cursor--;
|
|
return ident_end > cursor ? cursor : NULL;
|
|
}
|
|
|
|
static void c_import_function_push_param(ZCImportFunction *function, ZCImportParam param) {
|
|
if (function->param_len == function->param_cap) {
|
|
function->param_cap = z_grow_capacity(function->param_cap, function->param_len + 1, 4);
|
|
function->params = z_checked_reallocarray(function->params, function->param_cap, sizeof(ZCImportParam));
|
|
}
|
|
function->params[function->param_len++] = param;
|
|
}
|
|
|
|
static void c_import_function_vec_push(ZCImportFunctionVec *vec, ZCImportFunction function) {
|
|
if (vec->len == vec->cap) {
|
|
vec->cap = z_grow_capacity(vec->cap, vec->len + 1, 4);
|
|
vec->items = z_checked_reallocarray(vec->items, vec->cap, sizeof(ZCImportFunction));
|
|
}
|
|
vec->items[vec->len++] = function;
|
|
}
|
|
|
|
void z_c_import_function_free(ZCImportFunction *function) {
|
|
if (!function) return;
|
|
free(function->name);
|
|
free(function->import_header);
|
|
free(function->import_resolved_header);
|
|
free(function->return_c_type);
|
|
free(function->return_zero_type);
|
|
for (size_t i = 0; i < function->param_len; i++) {
|
|
free(function->params[i].name);
|
|
free(function->params[i].c_type);
|
|
free(function->params[i].zero_type);
|
|
}
|
|
free(function->params);
|
|
*function = (ZCImportFunction){0};
|
|
}
|
|
|
|
void z_c_import_function_vec_free(ZCImportFunctionVec *vec) {
|
|
if (!vec) return;
|
|
for (size_t i = 0; i < vec->len; i++) z_c_import_function_free(&vec->items[i]);
|
|
free(vec->items);
|
|
*vec = (ZCImportFunctionVec){0};
|
|
}
|
|
|
|
static bool normalized_c_type(const char *c_type, char *out, size_t out_len) {
|
|
if (!c_type || !out || out_len == 0) return false;
|
|
out[0] = 0;
|
|
if (strchr(c_type, '*') || strchr(c_type, '[') || strchr(c_type, ']')) return false;
|
|
char *copy = trim_span_copy(c_type, c_type + strlen(c_type));
|
|
size_t used = 0;
|
|
const char *cursor = copy;
|
|
while (*cursor) {
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
if (!*cursor) break;
|
|
const char *word_start = cursor;
|
|
while (*cursor && !isspace((unsigned char)*cursor)) cursor++;
|
|
char *word = z_strndup(word_start, (size_t)(cursor - word_start));
|
|
bool skip = strcmp(word, "const") == 0 || strcmp(word, "volatile") == 0 ||
|
|
strcmp(word, "register") == 0 || strcmp(word, "extern") == 0;
|
|
if (!skip) {
|
|
if (used > 0 && used + 1 < out_len) out[used++] = ' ';
|
|
for (size_t i = 0; word[i] && used + 1 < out_len; i++) out[used++] = word[i];
|
|
}
|
|
free(word);
|
|
}
|
|
out[used < out_len ? used : out_len - 1] = 0;
|
|
free(copy);
|
|
return out[0] != 0;
|
|
}
|
|
|
|
bool z_c_type_to_zero(const char *c_type, char *out, size_t out_len) {
|
|
if (!out || out_len == 0) return false;
|
|
out[0] = 0;
|
|
char normalized[128];
|
|
if (!normalized_c_type(c_type, normalized, sizeof(normalized))) return false;
|
|
if (strcmp(normalized, "void") == 0) snprintf(out, out_len, "Void");
|
|
else if (strcmp(normalized, "bool") == 0 || strcmp(normalized, "_Bool") == 0) snprintf(out, out_len, "Bool");
|
|
else if (strcmp(normalized, "char") == 0 || strcmp(normalized, "unsigned char") == 0 || strcmp(normalized, "uint8_t") == 0) snprintf(out, out_len, "u8");
|
|
else if (strcmp(normalized, "unsigned short") == 0 || strcmp(normalized, "uint16_t") == 0) snprintf(out, out_len, "u16");
|
|
else if (strcmp(normalized, "int") == 0 || strcmp(normalized, "signed int") == 0 || strcmp(normalized, "int32_t") == 0) snprintf(out, out_len, "i32");
|
|
else if (strcmp(normalized, "unsigned") == 0 || strcmp(normalized, "unsigned int") == 0 || strcmp(normalized, "uint32_t") == 0) snprintf(out, out_len, "u32");
|
|
else if (strcmp(normalized, "long long") == 0 || strcmp(normalized, "signed long long") == 0 || strcmp(normalized, "int64_t") == 0) snprintf(out, out_len, "i64");
|
|
else if (strcmp(normalized, "unsigned long long") == 0 || strcmp(normalized, "uint64_t") == 0) snprintf(out, out_len, "u64");
|
|
else if (strcmp(normalized, "size_t") == 0) snprintf(out, out_len, "usize");
|
|
else return false;
|
|
return true;
|
|
}
|
|
|
|
static bool c_import_parse_param(ZCImportFunction *function, const char *start, const char *end) {
|
|
char *param_text = trim_span_copy(start, end);
|
|
if (!param_text[0] || strcmp(param_text, "void") == 0) {
|
|
free(param_text);
|
|
return true;
|
|
}
|
|
char zero_type[128];
|
|
if (z_c_type_to_zero(param_text, zero_type, sizeof(zero_type))) {
|
|
char name[32];
|
|
snprintf(name, sizeof(name), "arg%zu", function->param_len);
|
|
c_import_function_push_param(function, (ZCImportParam){.name = z_strdup(name), .c_type = z_strdup(param_text), .zero_type = z_strdup(zero_type)});
|
|
free(param_text);
|
|
return true;
|
|
}
|
|
const char *param_end = param_text + strlen(param_text);
|
|
const char *name_start = last_ident_start_before(param_text, param_end);
|
|
if (!name_start) {
|
|
free(param_text);
|
|
return false;
|
|
}
|
|
const char *name_end = name_start;
|
|
while (*name_end && c_ident_char(*name_end)) name_end++;
|
|
char *name = z_strndup(name_start, (size_t)(name_end - name_start));
|
|
char *c_type = trim_span_copy(param_text, name_start);
|
|
char *mapped = z_c_type_to_zero(c_type, zero_type, sizeof(zero_type)) ? z_strdup(zero_type) : z_strdup("Unknown");
|
|
c_import_function_push_param(function, (ZCImportParam){.name = name, .c_type = c_type, .zero_type = mapped});
|
|
free(param_text);
|
|
return true;
|
|
}
|
|
|
|
static void c_import_parse_params(ZCImportFunction *function, const char *start, const char *end) {
|
|
const char *cursor = start;
|
|
while (cursor && cursor < end) {
|
|
const char *comma = cursor;
|
|
while (comma < end && *comma != ',') comma++;
|
|
if (!c_import_parse_param(function, cursor, comma)) break;
|
|
cursor = comma < end ? comma + 1 : end;
|
|
}
|
|
}
|
|
|
|
static const char *c_import_function_decl_close(const char *paren) {
|
|
if (!paren) return NULL;
|
|
int depth = 0;
|
|
for (const char *cursor = paren; *cursor; cursor++) {
|
|
if (*cursor == '(') depth++;
|
|
else if (*cursor == ')' && depth > 0) {
|
|
depth--;
|
|
if (depth == 0) {
|
|
const char *after = cursor + 1;
|
|
while (isspace((unsigned char)*after)) after++;
|
|
if (*after == ';') return cursor;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool c_import_parse_function_line(const char *line, ZCImportFunction *out) {
|
|
const char *paren = strchr(line, '(');
|
|
const char *close = c_import_function_decl_close(paren);
|
|
const char *name_start = paren ? last_ident_start_before(line, paren) : NULL;
|
|
if (!paren || !close || !name_start || strstr(line, "typedef")) return false;
|
|
const char *name_end = name_start;
|
|
while (*name_end && c_ident_char(*name_end)) name_end++;
|
|
char *name = z_strndup(name_start, (size_t)(name_end - name_start));
|
|
char *return_c_type = trim_span_copy(line, name_start);
|
|
char return_zero_type[128];
|
|
char *mapped_return = z_c_type_to_zero(return_c_type, return_zero_type, sizeof(return_zero_type)) ? z_strdup(return_zero_type) : z_strdup("Unknown");
|
|
*out = (ZCImportFunction){
|
|
.name = name,
|
|
.return_c_type = return_c_type,
|
|
.return_zero_type = mapped_return,
|
|
};
|
|
char *params_text = trim_span_copy(paren + 1, close);
|
|
out->old_style_params = params_text[0] == 0;
|
|
free(params_text);
|
|
if (!out->old_style_params) c_import_parse_params(out, paren + 1, close);
|
|
return true;
|
|
}
|
|
|
|
static void c_import_append_declaration_fragment(ZBuf *decl, const char *line) {
|
|
if (!decl || !line) return;
|
|
const char *cursor = line;
|
|
bool wrote_space = decl->len > 0;
|
|
while (*cursor) {
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
if (!*cursor) break;
|
|
const char *start = cursor;
|
|
while (*cursor && !isspace((unsigned char)*cursor)) cursor++;
|
|
if (wrote_space) zbuf_append_char(decl, ' ');
|
|
char *word = z_strndup(start, (size_t)(cursor - start));
|
|
zbuf_append(decl, word);
|
|
free(word);
|
|
wrote_space = true;
|
|
}
|
|
}
|
|
|
|
static void c_import_consume_complete_declarations(ZBuf *decl, ZCImportFunctionVec *out) {
|
|
if (!decl || !decl->data || !out) return;
|
|
const char *start = decl->data;
|
|
const char *cursor = decl->data;
|
|
bool consumed = false;
|
|
while (*cursor) {
|
|
if (*cursor == ';') {
|
|
char *text = trim_span_copy(start, cursor + 1);
|
|
if (text[0] && strchr(text, '(')) {
|
|
ZCImportFunction function = {0};
|
|
if (c_import_parse_function_line(text, &function)) c_import_function_vec_push(out, function);
|
|
}
|
|
free(text);
|
|
start = cursor + 1;
|
|
consumed = true;
|
|
}
|
|
cursor++;
|
|
}
|
|
if (!consumed) return;
|
|
char *tail = trim_span_copy(start, decl->data + strlen(decl->data));
|
|
zbuf_free(decl);
|
|
zbuf_init(decl);
|
|
if (tail[0]) zbuf_append(decl, tail);
|
|
free(tail);
|
|
}
|
|
|
|
typedef struct {
|
|
bool parent_active;
|
|
bool active;
|
|
bool branch_taken;
|
|
} CImportPreprocFrame;
|
|
|
|
static bool c_import_parse_directive_word(const char *line, char *word, size_t word_len, const char **rest) {
|
|
if (!line || line[0] != '#' || !word || word_len == 0) return false;
|
|
const char *cursor = line + 1;
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
const char *start = cursor;
|
|
while (c_ident_char(*cursor)) cursor++;
|
|
size_t len = (size_t)(cursor - start);
|
|
if (len == 0) return false;
|
|
size_t copy_len = len < word_len - 1 ? len : word_len - 1;
|
|
memcpy(word, start, copy_len);
|
|
word[copy_len] = 0;
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
if (rest) *rest = cursor;
|
|
return true;
|
|
}
|
|
|
|
static bool c_import_preproc_active(const CImportPreprocFrame *frames, size_t depth) {
|
|
return depth == 0 || (frames[depth - 1].parent_active && frames[depth - 1].active);
|
|
}
|
|
|
|
typedef struct {
|
|
char **items;
|
|
size_t len;
|
|
size_t cap;
|
|
} CImportNameVec;
|
|
|
|
typedef struct {
|
|
const ZTargetInfo *target;
|
|
CImportNameVec defines;
|
|
CImportNameVec truthy_defines;
|
|
CImportNameVec undefines;
|
|
} CImportPreprocEnv;
|
|
|
|
static void c_import_name_vec_free(CImportNameVec *vec) {
|
|
if (!vec) return;
|
|
for (size_t i = 0; i < vec->len; i++) free(vec->items[i]);
|
|
free(vec->items);
|
|
*vec = (CImportNameVec){0};
|
|
}
|
|
|
|
static bool c_import_name_vec_contains(const CImportNameVec *vec, const char *name) {
|
|
if (!vec || !name) return false;
|
|
for (size_t i = 0; i < vec->len; i++) {
|
|
if (vec->items[i] && strcmp(vec->items[i], name) == 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void c_import_name_vec_remove(CImportNameVec *vec, const char *name) {
|
|
if (!vec || !name) return;
|
|
for (size_t i = 0; i < vec->len; i++) {
|
|
if (!vec->items[i] || strcmp(vec->items[i], name) != 0) continue;
|
|
free(vec->items[i]);
|
|
if (i + 1 < vec->len) memmove(&vec->items[i], &vec->items[i + 1], (vec->len - i - 1) * sizeof(char *));
|
|
vec->len--;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void c_import_name_vec_add(CImportNameVec *vec, const char *name) {
|
|
if (!vec || !name || !name[0] || c_import_name_vec_contains(vec, name)) return;
|
|
if (vec->len == vec->cap) {
|
|
vec->cap = z_grow_capacity(vec->cap, vec->len + 1, 8);
|
|
vec->items = z_checked_reallocarray(vec->items, vec->cap, sizeof(char *));
|
|
}
|
|
vec->items[vec->len++] = z_strdup(name);
|
|
}
|
|
|
|
static void c_import_preproc_env_free(CImportPreprocEnv *env) {
|
|
if (!env) return;
|
|
c_import_name_vec_free(&env->defines);
|
|
c_import_name_vec_free(&env->truthy_defines);
|
|
c_import_name_vec_free(&env->undefines);
|
|
}
|
|
|
|
static char *c_import_parse_macro_name_copy(const char *text) {
|
|
const char *cursor = text ? text : "";
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
const char *start = cursor;
|
|
while (c_ident_char(*cursor)) cursor++;
|
|
return start < cursor ? z_strndup(start, (size_t)(cursor - start)) : z_strdup("");
|
|
}
|
|
|
|
static char *c_import_parse_macro_definition_copy(const char *text, char **out_value) {
|
|
const char *cursor = text ? text : "";
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
const char *start = cursor;
|
|
while (c_ident_char(*cursor)) cursor++;
|
|
char *name = start < cursor ? z_strndup(start, (size_t)(cursor - start)) : z_strdup("");
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
if (out_value) *out_value = trim_span_copy(cursor, cursor + strlen(cursor));
|
|
return name;
|
|
}
|
|
|
|
static bool c_import_string_eq(const char *left, const char *right) {
|
|
return left && right && strcmp(left, right) == 0;
|
|
}
|
|
|
|
static bool c_import_name_in_list(const char *name, const char *const *items, size_t len) {
|
|
if (!name) return false;
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (strcmp(name, items[i]) == 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool c_import_target_macro_defined(const ZTargetInfo *target, const char *name) {
|
|
if (!name || !name[0]) return false;
|
|
if (strcmp(name, "__cplusplus") == 0) return false;
|
|
if (strcmp(name, "__STDC__") == 0 || strcmp(name, "__STDC_HOSTED__") == 0) return true;
|
|
if (!target) return false;
|
|
|
|
const bool is_windows = c_import_string_eq(target->os, "windows");
|
|
const bool is_linux = c_import_string_eq(target->os, "linux");
|
|
const bool is_macos = c_import_string_eq(target->os, "macos");
|
|
const bool is_x86_64 = c_import_string_eq(target->arch, "x86_64");
|
|
const bool is_aarch64 = c_import_string_eq(target->arch, "aarch64");
|
|
const bool is_msvc = c_import_string_eq(target->abi, "msvc");
|
|
const bool is_gnu = c_import_string_eq(target->abi, "gnu");
|
|
|
|
static const char *const windows_names[] = {"_WIN32", "WIN32", "_WIN64", "WIN64", "__WIN32__", "__WIN64__"};
|
|
static const char *const linux_names[] = {"__linux__", "__linux", "linux"};
|
|
static const char *const macos_names[] = {"__APPLE__", "__MACH__", "__APPLE_CC__"};
|
|
static const char *const x86_64_names[] = {"__x86_64__", "__x86_64", "__amd64__", "__amd64"};
|
|
static const char *const msvc_x86_64_names[] = {"_M_X64", "_M_AMD64"};
|
|
static const char *const aarch64_names[] = {"__aarch64__"};
|
|
static const char *const macos_aarch64_names[] = {"__arm64__"};
|
|
static const char *const msvc_aarch64_names[] = {"_M_ARM64"};
|
|
|
|
if (is_windows && c_import_name_in_list(name, windows_names, sizeof(windows_names) / sizeof(windows_names[0]))) return true;
|
|
if (is_linux && c_import_name_in_list(name, linux_names, sizeof(linux_names) / sizeof(linux_names[0]))) return true;
|
|
if (is_macos && c_import_name_in_list(name, macos_names, sizeof(macos_names) / sizeof(macos_names[0]))) return true;
|
|
if (is_x86_64 && c_import_name_in_list(name, x86_64_names, sizeof(x86_64_names) / sizeof(x86_64_names[0]))) return true;
|
|
if (is_msvc && is_x86_64 && c_import_name_in_list(name, msvc_x86_64_names, sizeof(msvc_x86_64_names) / sizeof(msvc_x86_64_names[0]))) return true;
|
|
if (is_aarch64 && c_import_name_in_list(name, aarch64_names, sizeof(aarch64_names) / sizeof(aarch64_names[0]))) return true;
|
|
if (is_macos && is_aarch64 && c_import_name_in_list(name, macos_aarch64_names, sizeof(macos_aarch64_names) / sizeof(macos_aarch64_names[0]))) return true;
|
|
if (is_msvc && is_aarch64 && c_import_name_in_list(name, msvc_aarch64_names, sizeof(msvc_aarch64_names) / sizeof(msvc_aarch64_names[0]))) return true;
|
|
if (!is_windows && (strcmp(name, "__LP64__") == 0 || strcmp(name, "_LP64") == 0)) return true;
|
|
if (is_msvc && strcmp(name, "_MSC_VER") == 0) return true;
|
|
if (is_gnu && (strcmp(name, "__GLIBC__") == 0 || strcmp(name, "__gnu_linux__") == 0)) return true;
|
|
return false;
|
|
}
|
|
|
|
static bool c_import_macro_defined(const CImportPreprocEnv *env, const char *name) {
|
|
if (!env || !name || !name[0]) return false;
|
|
if (c_import_name_vec_contains(&env->undefines, name)) return false;
|
|
if (c_import_name_vec_contains(&env->defines, name)) return true;
|
|
return c_import_target_macro_defined(env->target, name);
|
|
}
|
|
|
|
static bool c_import_macro_expr_true(const CImportPreprocEnv *env, const char *name) {
|
|
if (!env || !name || !name[0]) return false;
|
|
if (c_import_name_vec_contains(&env->undefines, name)) return false;
|
|
if (c_import_target_macro_defined(env->target, name)) return true;
|
|
if (c_import_name_vec_contains(&env->defines, name)) return c_import_name_vec_contains(&env->truthy_defines, name);
|
|
return false;
|
|
}
|
|
|
|
static void c_import_define_macro(CImportPreprocEnv *env, const char *name, bool truthy) {
|
|
if (!env || !name || !name[0]) return;
|
|
c_import_name_vec_remove(&env->undefines, name);
|
|
c_import_name_vec_add(&env->defines, name);
|
|
if (truthy) c_import_name_vec_add(&env->truthy_defines, name);
|
|
else c_import_name_vec_remove(&env->truthy_defines, name);
|
|
}
|
|
|
|
static void c_import_undef_macro(CImportPreprocEnv *env, const char *name) {
|
|
if (!env || !name || !name[0]) return;
|
|
c_import_name_vec_remove(&env->defines, name);
|
|
c_import_name_vec_remove(&env->truthy_defines, name);
|
|
c_import_name_vec_add(&env->undefines, name);
|
|
}
|
|
|
|
typedef struct {
|
|
const char *cursor;
|
|
const CImportPreprocEnv *env;
|
|
bool valid;
|
|
} CImportPPExprParser;
|
|
|
|
static void c_import_pp_skip_space(CImportPPExprParser *parser) {
|
|
while (parser && isspace((unsigned char)*parser->cursor)) parser->cursor++;
|
|
}
|
|
|
|
static bool c_import_pp_consume(CImportPPExprParser *parser, const char *token) {
|
|
if (!parser || !token) return false;
|
|
c_import_pp_skip_space(parser);
|
|
size_t len = strlen(token);
|
|
if (strncmp(parser->cursor, token, len) != 0) return false;
|
|
parser->cursor += len;
|
|
return true;
|
|
}
|
|
|
|
static bool c_import_pp_parse_or(CImportPPExprParser *parser);
|
|
|
|
static bool c_import_pp_parse_macro_defined(CImportPPExprParser *parser) {
|
|
c_import_pp_skip_space(parser);
|
|
bool parenthesized = c_import_pp_consume(parser, "(");
|
|
c_import_pp_skip_space(parser);
|
|
const char *start = parser->cursor;
|
|
while (c_ident_char(*parser->cursor)) parser->cursor++;
|
|
if (start == parser->cursor) {
|
|
parser->valid = false;
|
|
return false;
|
|
}
|
|
char *name = z_strndup(start, (size_t)(parser->cursor - start));
|
|
bool defined = c_import_macro_defined(parser->env, name);
|
|
free(name);
|
|
if (parenthesized && !c_import_pp_consume(parser, ")")) parser->valid = false;
|
|
return defined;
|
|
}
|
|
|
|
static bool c_import_pp_parse_primary(CImportPPExprParser *parser) {
|
|
c_import_pp_skip_space(parser);
|
|
if (c_import_pp_consume(parser, "(")) {
|
|
bool value = c_import_pp_parse_or(parser);
|
|
if (!c_import_pp_consume(parser, ")")) parser->valid = false;
|
|
return value;
|
|
}
|
|
if (isdigit((unsigned char)*parser->cursor)) {
|
|
char *end = NULL;
|
|
unsigned long long value = strtoull(parser->cursor, &end, 0);
|
|
if (!end || end == parser->cursor) {
|
|
parser->valid = false;
|
|
return false;
|
|
}
|
|
parser->cursor = end;
|
|
return value != 0;
|
|
}
|
|
if (c_ident_char(*parser->cursor)) {
|
|
const char *start = parser->cursor;
|
|
while (c_ident_char(*parser->cursor)) parser->cursor++;
|
|
char *name = z_strndup(start, (size_t)(parser->cursor - start));
|
|
bool value = false;
|
|
if (strcmp(name, "defined") == 0) {
|
|
free(name);
|
|
return c_import_pp_parse_macro_defined(parser);
|
|
}
|
|
if (strcmp(name, "true") == 0) value = true;
|
|
else if (strcmp(name, "false") == 0) value = false;
|
|
else value = c_import_macro_expr_true(parser->env, name);
|
|
free(name);
|
|
return value;
|
|
}
|
|
parser->valid = false;
|
|
return false;
|
|
}
|
|
|
|
static bool c_import_pp_parse_unary(CImportPPExprParser *parser) {
|
|
if (c_import_pp_consume(parser, "!")) return !c_import_pp_parse_unary(parser);
|
|
return c_import_pp_parse_primary(parser);
|
|
}
|
|
|
|
static bool c_import_pp_parse_and(CImportPPExprParser *parser) {
|
|
bool value = c_import_pp_parse_unary(parser);
|
|
while (c_import_pp_consume(parser, "&&")) {
|
|
bool right = c_import_pp_parse_unary(parser);
|
|
value = value && right;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static bool c_import_pp_parse_or(CImportPPExprParser *parser) {
|
|
bool value = c_import_pp_parse_and(parser);
|
|
while (c_import_pp_consume(parser, "||")) {
|
|
bool right = c_import_pp_parse_and(parser);
|
|
value = value || right;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static bool c_import_pp_expr_active(const CImportPreprocEnv *env, const char *expr) {
|
|
if (!expr) return false;
|
|
CImportPPExprParser parser = {.cursor = expr, .env = env, .valid = true};
|
|
bool value = c_import_pp_parse_or(&parser);
|
|
c_import_pp_skip_space(&parser);
|
|
return parser.valid && *parser.cursor == 0 && value;
|
|
}
|
|
|
|
static void c_import_preproc_push(CImportPreprocFrame **frames, size_t *depth, size_t *cap, bool parent_active, bool active) {
|
|
if (*depth == *cap) {
|
|
*cap = z_grow_capacity(*cap, *depth + 1, 8);
|
|
*frames = z_checked_reallocarray(*frames, *cap, sizeof(CImportPreprocFrame));
|
|
}
|
|
(*frames)[(*depth)++] = (CImportPreprocFrame){.parent_active = parent_active, .active = active, .branch_taken = active};
|
|
}
|
|
|
|
static bool c_import_handle_preprocessor_line(const char *line, CImportPreprocFrame **frames, size_t *depth, size_t *cap, CImportPreprocEnv *env) {
|
|
char directive[16];
|
|
const char *rest = NULL;
|
|
if (!c_import_parse_directive_word(line, directive, sizeof(directive), &rest)) return false;
|
|
if (strcmp(directive, "if") == 0) {
|
|
bool parent_active = c_import_preproc_active(*frames, *depth);
|
|
c_import_preproc_push(frames, depth, cap, parent_active, parent_active && c_import_pp_expr_active(env, rest));
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "ifdef") == 0) {
|
|
bool parent_active = c_import_preproc_active(*frames, *depth);
|
|
char *name = c_import_parse_macro_name_copy(rest);
|
|
c_import_preproc_push(frames, depth, cap, parent_active, parent_active && c_import_macro_defined(env, name));
|
|
free(name);
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "ifndef") == 0) {
|
|
bool parent_active = c_import_preproc_active(*frames, *depth);
|
|
char *name = c_import_parse_macro_name_copy(rest);
|
|
c_import_preproc_push(frames, depth, cap, parent_active, parent_active && !c_import_macro_defined(env, name));
|
|
free(name);
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "elif") == 0 && *depth > 0) {
|
|
CImportPreprocFrame *frame = &(*frames)[*depth - 1];
|
|
bool active = frame->parent_active && !frame->branch_taken && c_import_pp_expr_active(env, rest);
|
|
frame->active = active;
|
|
frame->branch_taken = frame->branch_taken || active;
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "else") == 0 && *depth > 0) {
|
|
CImportPreprocFrame *frame = &(*frames)[*depth - 1];
|
|
frame->active = frame->parent_active && !frame->branch_taken;
|
|
frame->branch_taken = true;
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "endif") == 0 && *depth > 0) {
|
|
(*depth)--;
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "define") == 0 && c_import_preproc_active(*frames, *depth)) {
|
|
char *value = NULL;
|
|
char *name = c_import_parse_macro_definition_copy(rest, &value);
|
|
c_import_define_macro(env, name, value && value[0] ? c_import_pp_expr_active(env, value) : false);
|
|
free(value);
|
|
free(name);
|
|
return true;
|
|
}
|
|
if (strcmp(directive, "undef") == 0 && c_import_preproc_active(*frames, *depth)) {
|
|
char *name = c_import_parse_macro_name_copy(rest);
|
|
c_import_undef_macro(env, name);
|
|
free(name);
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool c_import_linkage_wrapper_line(const char *line) {
|
|
if (!line) return false;
|
|
if ((strstr(line, "extern \"C\"") || strstr(line, "extern \"C++\"")) && strchr(line, '{')) return true;
|
|
const char *cursor = line;
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
if (*cursor != '}') return false;
|
|
cursor++;
|
|
while (isspace((unsigned char)*cursor)) cursor++;
|
|
return *cursor == 0 || strncmp(cursor, "//", 2) == 0 || strncmp(cursor, "/*", 2) == 0;
|
|
}
|
|
|
|
bool z_c_header_parse_functions_for_target(const char *header, const ZTargetInfo *target, ZCImportFunctionVec *out) {
|
|
if (!out) return false;
|
|
char *stripped = z_c_header_strip_comments(header);
|
|
const char *cursor = stripped ? stripped : "";
|
|
CImportPreprocFrame *frames = NULL;
|
|
size_t preproc_depth = 0;
|
|
size_t preproc_cap = 0;
|
|
CImportPreprocEnv env = {.target = target};
|
|
ZBuf declaration;
|
|
zbuf_init(&declaration);
|
|
while (*cursor) {
|
|
const char *line_end = strchr(cursor, '\n');
|
|
if (!line_end) line_end = cursor + strlen(cursor);
|
|
char *line = trim_span_copy(cursor, line_end);
|
|
bool wrapper_line = c_import_linkage_wrapper_line(line);
|
|
if (line[0] == '#') {
|
|
c_import_handle_preprocessor_line(line, &frames, &preproc_depth, &preproc_cap, &env);
|
|
} else if (c_import_preproc_active(frames, preproc_depth) && !wrapper_line) {
|
|
if (line[0]) c_import_append_declaration_fragment(&declaration, line);
|
|
c_import_consume_complete_declarations(&declaration, out);
|
|
} else if (!c_import_preproc_active(frames, preproc_depth)) {
|
|
zbuf_free(&declaration);
|
|
zbuf_init(&declaration);
|
|
} else if (wrapper_line) {
|
|
zbuf_free(&declaration);
|
|
zbuf_init(&declaration);
|
|
}
|
|
free(line);
|
|
cursor = *line_end ? line_end + 1 : line_end;
|
|
}
|
|
zbuf_free(&declaration);
|
|
c_import_preproc_env_free(&env);
|
|
free(frames);
|
|
free(stripped);
|
|
return true;
|
|
}
|
|
|
|
bool z_c_header_parse_functions(const char *header, ZCImportFunctionVec *out) {
|
|
return z_c_header_parse_functions_for_target(header, NULL, out);
|
|
}
|
|
|
|
static void c_import_function_clone(ZCImportFunction *out, const ZCImportFunction *source) {
|
|
*out = (ZCImportFunction){
|
|
.name = z_strdup(source->name),
|
|
.import_header = source->import_header ? z_strdup(source->import_header) : NULL,
|
|
.import_resolved_header = source->import_resolved_header ? z_strdup(source->import_resolved_header) : NULL,
|
|
.return_c_type = z_strdup(source->return_c_type),
|
|
.return_zero_type = z_strdup(source->return_zero_type),
|
|
.old_style_params = source->old_style_params,
|
|
};
|
|
for (size_t i = 0; i < source->param_len; i++) {
|
|
c_import_function_push_param(out, (ZCImportParam){
|
|
.name = z_strdup(source->params[i].name),
|
|
.c_type = z_strdup(source->params[i].c_type),
|
|
.zero_type = z_strdup(source->params[i].zero_type),
|
|
});
|
|
}
|
|
}
|
|
|
|
bool z_c_import_alias_exists(const Program *program, const char *alias) {
|
|
if (!program || !alias) return false;
|
|
for (size_t i = 0; i < program->c_imports.len; i++) {
|
|
const CImport *import = &program->c_imports.items[i];
|
|
if (import->alias && strcmp(import->alias, alias) == 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static const char *c_import_read_path(const CImport *import) {
|
|
return import && import->resolved_header && import->resolved_header[0] ? import->resolved_header : (import ? import->header : NULL);
|
|
}
|
|
|
|
bool z_c_import_find_function_for_target(const Program *program, const ZTargetInfo *target, const char *alias, const char *symbol, ZCImportFunction *out, ZDiag *diag) {
|
|
if (!program || !alias || !symbol || !out) return false;
|
|
for (size_t i = 0; i < program->c_imports.len; i++) {
|
|
const CImport *import = &program->c_imports.items[i];
|
|
if (!import->alias || strcmp(import->alias, alias) != 0) continue;
|
|
ZDiag read_diag = {0};
|
|
const char *read_path = c_import_read_path(import);
|
|
char *header = z_read_file(read_path, &read_diag);
|
|
if (!header) {
|
|
if (diag) {
|
|
diag->code = 8001;
|
|
diag->line = import->line;
|
|
diag->column = import->column;
|
|
diag->length = 1;
|
|
snprintf(diag->message, sizeof(diag->message), "extern c header could not be read");
|
|
snprintf(diag->expected, sizeof(diag->expected), "readable C header path");
|
|
snprintf(diag->actual, sizeof(diag->actual), "%s", import->header ? import->header : "<missing>");
|
|
snprintf(diag->help, sizeof(diag->help), "make the header path package-relative and target-specific");
|
|
}
|
|
return false;
|
|
}
|
|
ZCImportFunctionVec functions = {0};
|
|
z_c_header_parse_functions_for_target(header, target, &functions);
|
|
free(header);
|
|
for (size_t fn = 0; fn < functions.len; fn++) {
|
|
if (functions.items[fn].name && strcmp(functions.items[fn].name, symbol) == 0) {
|
|
c_import_function_clone(out, &functions.items[fn]);
|
|
free(out->import_header);
|
|
free(out->import_resolved_header);
|
|
out->import_header = import->header ? z_strdup(import->header) : NULL;
|
|
out->import_resolved_header = import->resolved_header ? z_strdup(import->resolved_header) : NULL;
|
|
z_c_import_function_vec_free(&functions);
|
|
return true;
|
|
}
|
|
}
|
|
z_c_import_function_vec_free(&functions);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool z_c_import_find_function(const Program *program, const char *alias, const char *symbol, ZCImportFunction *out, ZDiag *diag) {
|
|
return z_c_import_find_function_for_target(program, NULL, alias, symbol, out, diag);
|
|
}
|