Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

1238 lines
55 KiB
C

#include "program_graph_lower.h"
#include "program_graph_adjacency.h"
#include "std_source.h"
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
typedef struct {
const ZProgramGraph *graph;
ZProgramGraphAdjacency adjacency;
ZDiag *diag;
bool allow_internal_symbols;
SourceInput *source;
const char *artifact_path;
const char *current_module_name;
const char *current_module_path;
size_t module_count;
int synthetic_line;
bool preserve_graph_types;
} GraphLower;
static void *lower_grow_items(void *items, size_t len, size_t *cap, size_t initial, size_t item_size) {
if (len + 1 > *cap) {
*cap = z_grow_capacity(*cap, len + 1, initial);
return z_checked_reallocarray(items, *cap, item_size);
}
return items;
}
static void lower_push_function(FunctionVec *vec, Function item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 8, sizeof(Function));
vec->items[vec->len++] = item;
}
static void lower_push_stmt(StmtVec *vec, Stmt *item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 8, sizeof(Stmt *));
vec->items[vec->len++] = item;
}
static void lower_push_expr(ExprVec *vec, Expr *item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(Expr *));
vec->items[vec->len++] = item;
}
static void lower_push_type_arg(TypeArgVec *vec, TypeArg item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(TypeArg));
vec->items[vec->len++] = item;
}
static void lower_push_field(FieldInitVec *vec, FieldInit item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(FieldInit));
vec->items[vec->len++] = item;
}
static void lower_push_param(ParamVec *vec, Param item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(Param));
vec->items[vec->len++] = item;
}
static void lower_push_use(UseImportVec *vec, UseImport item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(UseImport));
vec->items[vec->len++] = item;
}
static void lower_push_c_import(CImportVec *vec, CImport item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(CImport));
vec->items[vec->len++] = item;
}
static void lower_push_const(ConstVec *vec, ConstDecl item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(ConstDecl));
vec->items[vec->len++] = item;
}
static void lower_push_alias(TypeAliasVec *vec, TypeAlias item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(TypeAlias));
vec->items[vec->len++] = item;
}
static void lower_push_shape(ShapeVec *vec, Shape item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(Shape));
vec->items[vec->len++] = item;
}
static void lower_push_interface(InterfaceVec *vec, InterfaceDecl item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(InterfaceDecl));
vec->items[vec->len++] = item;
}
static void lower_push_enum(EnumVec *vec, EnumDecl item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(EnumDecl));
vec->items[vec->len++] = item;
}
static void lower_push_choice(ChoiceVec *vec, Choice item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(Choice));
vec->items[vec->len++] = item;
}
static void lower_push_match_arm(MatchArmVec *vec, MatchArm item) {
vec->items = lower_grow_items(vec->items, vec->len, &vec->cap, 4, sizeof(MatchArm));
vec->items[vec->len++] = item;
}
static bool lower_text_eq(const char *left, const char *right) {
return strcmp(left ? left : "", right ? right : "") == 0;
}
static void lower_source_push_string(char ***items, size_t *len, const char *value) {
*items = z_checked_reallocarray(*items, *len + 1, sizeof(char *));
(*items)[(*len)++] = z_strdup(value ? value : "");
}
static void lower_source_push_module(SourceInput *input, const char *name, const char *path) {
if (!input) return;
input->module_names = z_checked_reallocarray(input->module_names, input->module_count + 1, sizeof(char *));
input->module_paths = z_checked_reallocarray(input->module_paths, input->module_count + 1, sizeof(char *));
input->module_names[input->module_count] = z_strdup(name && name[0] ? name : "main");
input->module_paths[input->module_count] = z_strdup(path && path[0] ? path : (input->source_file ? input->source_file : ""));
input->module_count++;
}
static void lower_source_push_import_edge(SourceInput *input, const char *from, const char *to, const char *resolved_path, const char *source_path, int line, int column, int length) {
if (!input) return;
input->import_from = z_checked_reallocarray(input->import_from, input->import_edge_count + 1, sizeof(char *));
input->import_to = z_checked_reallocarray(input->import_to, input->import_edge_count + 1, sizeof(char *));
input->import_paths = z_checked_reallocarray(input->import_paths, input->import_edge_count + 1, sizeof(char *));
input->import_source_paths = z_checked_reallocarray(input->import_source_paths, input->import_edge_count + 1, sizeof(char *));
input->import_lines = z_checked_reallocarray(input->import_lines, input->import_edge_count + 1, sizeof(int));
input->import_columns = z_checked_reallocarray(input->import_columns, input->import_edge_count + 1, sizeof(int));
input->import_lengths = z_checked_reallocarray(input->import_lengths, input->import_edge_count + 1, sizeof(int));
input->import_from[input->import_edge_count] = z_strdup(from ? from : "");
input->import_to[input->import_edge_count] = z_strdup(to ? to : "");
input->import_paths[input->import_edge_count] = z_strdup(resolved_path ? resolved_path : "");
input->import_source_paths[input->import_edge_count] = z_strdup(source_path ? source_path : "");
input->import_lines[input->import_edge_count] = line > 0 ? line : 1;
input->import_columns[input->import_edge_count] = column > 0 ? column : 1;
input->import_lengths[input->import_edge_count] = length > 0 ? length : 1;
input->import_edge_count++;
}
static void lower_source_push_line(GraphLower *lower, const ZProgramGraphNode *node) {
if (!lower || !lower->source) return;
const char *path = node && node->path && node->path[0] ? node->path : lower->current_module_path;
if (!path || !path[0]) path = lower->artifact_path ? lower->artifact_path : "<program-graph>";
int original_line = node && node->line > 0 ? node->line : 1;
lower->source->source_line_paths = z_checked_reallocarray(lower->source->source_line_paths, lower->source->source_line_count + 1, sizeof(char *));
lower->source->source_line_numbers = z_checked_reallocarray(lower->source->source_line_numbers, lower->source->source_line_count + 1, sizeof(int));
lower->source->source_line_paths[lower->source->source_line_count] = z_strdup(path);
lower->source->source_line_numbers[lower->source->source_line_count] = original_line;
lower->source->source_line_count++;
}
static int lower_line(GraphLower *lower, const ZProgramGraphNode *node) {
if (!lower || !lower->source) return node && node->line > 0 ? node->line : 1;
if (lower->synthetic_line < INT_MAX) lower->synthetic_line++;
lower_source_push_line(lower, node);
return lower->synthetic_line > 0 ? lower->synthetic_line : 1;
}
static bool lower_has_diag(const GraphLower *lower) {
return lower && lower->diag && lower->diag->code != 0;
}
static bool lower_fail(GraphLower *lower, const ZProgramGraphNode *node, const char *message, const char *expected, const char *actual, const char *help) {
if (!lower || !lower->diag || lower->diag->code != 0) return false;
lower->diag->code = 2002;
lower->diag->path = node && node->path && node->path[0] ? node->path : "<program-graph>";
lower->diag->line = node && node->line > 0 ? node->line : 1;
lower->diag->column = node && node->column > 0 ? node->column : 1;
lower->diag->length = 1;
snprintf(lower->diag->message, sizeof(lower->diag->message), "%s", message ? message : "cannot lower program graph");
snprintf(lower->diag->expected, sizeof(lower->diag->expected), "%s", expected ? expected : "supported ProgramGraph shape");
snprintf(lower->diag->actual, sizeof(lower->diag->actual), "%s", actual ? actual : (node ? z_program_graph_node_kind_name(node->kind) : "missing graph node"));
if (help) snprintf(lower->diag->help, sizeof(lower->diag->help), "%s", help);
return false;
}
static const ZProgramGraphNode *lower_find_node(const GraphLower *lower, const char *id) {
return z_program_graph_adjacency_node(&lower->adjacency, id);
}
static const ZProgramGraphEdge *lower_ordered_edge(const GraphLower *lower, const char *from, const char *kind, size_t order) {
size_t start = 0;
size_t len = 0;
if (!kind) return NULL;
z_program_graph_adjacency_owner_run(&lower->adjacency, from, kind, &start, &len);
for (size_t i = start; i < start + len; i++) {
const ZProgramGraphEdge *edge = z_program_graph_adjacency_owner_edge_at(&lower->adjacency, i);
if (edge->order == order) return edge;
}
return NULL;
}
static const ZProgramGraphNode *lower_ordered_node(const GraphLower *lower, const char *from, const char *kind, size_t order) {
const ZProgramGraphEdge *edge = lower_ordered_edge(lower, from, kind, order);
return edge ? lower_find_node(lower, edge->to) : NULL;
}
static const ZProgramGraphEdge *lower_next_edge_by_order(const GraphLower *lower, const char *from, const char *kind, bool have_last, size_t last_order) {
size_t start = 0;
size_t len = 0;
if (!kind) return NULL;
z_program_graph_adjacency_owner_run(&lower->adjacency, from, kind, &start, &len);
for (size_t i = start; i < start + len; i++) {
const ZProgramGraphEdge *edge = z_program_graph_adjacency_owner_edge_at(&lower->adjacency, i);
if (!have_last || edge->order > last_order) return edge;
}
return NULL;
}
static bool lower_binary_operator(const char *text) {
const char *ops[] = {"+", "-", "*", "/", "%", "&&", "||", "==", "!=", "<", "<=", ">", ">=", "+%", "+|", NULL};
for (int i = 0; ops[i]; i++) {
if (lower_text_eq(text, ops[i])) return true;
}
return false;
}
static bool lower_literal_is_raw(const char *value) {
if (!value || !value[0]) return false;
if (lower_text_eq(value, "true") || lower_text_eq(value, "false") || lower_text_eq(value, "null")) return true;
const char *p = value;
if (*p == '-') p++;
bool digit = false;
while (*p) {
if (isdigit((unsigned char)*p)) {
digit = true;
p++;
continue;
}
if (*p == '_' || *p == '.' || isalpha((unsigned char)*p)) {
p++;
continue;
}
if ((*p == '-' || *p == '+') && p > value && (*(p - 1) == 'e' || *(p - 1) == 'E')) {
p++;
continue;
}
return false;
}
return digit;
}
static bool lower_starts_with(const char *text, const char *prefix) {
if (!text || !prefix) return false;
size_t len = strlen(prefix);
return strncmp(text, prefix, len) == 0;
}
static bool lower_path_is_absolute(const char *path) {
if (!path || !path[0]) return false;
return path[0] == '/' || (strlen(path) > 2 && path[1] == ':');
}
static bool lower_file_exists(const char *path) {
struct stat st;
return path && path[0] && stat(path, &st) == 0 && S_ISREG(st.st_mode);
}
static char *lower_dirname_of(const char *path) {
const char *slash = path ? strrchr(path, '/') : NULL;
if (!slash) return z_strdup(".");
if (slash == path) return z_strdup("/");
return z_strndup(path, (size_t)(slash - path));
}
static char *lower_join_path(const char *left, const char *right) {
ZBuf buf;
zbuf_init(&buf);
zbuf_append(&buf, left && left[0] ? left : ".");
if (buf.len == 0 || buf.data[buf.len - 1] != '/') zbuf_append_char(&buf, '/');
zbuf_append(&buf, right ? right : "");
return buf.data;
}
static char *lower_parent_dir(const char *dir) {
if (!dir || !dir[0] || strcmp(dir, ".") == 0 || strcmp(dir, "/") == 0) return NULL;
char *parent = lower_dirname_of(dir);
if (strcmp(parent, dir) == 0) {
free(parent);
return NULL;
}
return parent;
}
static char *lower_find_manifest_for_source_path(const char *path) {
if (!path || !path[0]) return NULL;
char *dir = lower_dirname_of(path);
while (dir && dir[0]) {
char *manifest = lower_join_path(dir, "zero.toml");
if (lower_file_exists(manifest)) {
free(dir);
return manifest;
}
free(manifest);
manifest = lower_join_path(dir, "zero.json");
if (lower_file_exists(manifest)) {
free(dir);
return manifest;
}
free(manifest);
char *parent = lower_parent_dir(dir);
free(dir);
dir = parent;
}
return NULL;
}
static void lower_source_seed_package_manifest(SourceInput *source, const ZProgramGraph *graph, const char *artifact_path) {
if (!source || source->manifest_path || !graph || !lower_starts_with(graph->module_identity, "package:")) return;
char *manifest = lower_find_manifest_for_source_path(artifact_path);
if (manifest) {
source->manifest_path = manifest;
source->package_root = lower_dirname_of(manifest);
return;
}
for (size_t i = 0; i < graph->node_len; i++) {
const ZProgramGraphNode *node = &graph->nodes[i];
if (!node->path || !node->path[0]) continue;
manifest = lower_find_manifest_for_source_path(node->path);
if (!manifest) continue;
source->manifest_path = manifest;
source->package_root = lower_dirname_of(manifest);
return;
}
}
static char *lower_resolve_c_import_header_path(const GraphLower *lower, const ZProgramGraphNode *node, const char *header) {
if (!header || !header[0]) return z_strdup(header ? header : "");
if (lower_path_is_absolute(header)) return z_strdup(header);
if (lower && lower->source && lower->source->package_root && lower->source->package_root[0]) {
char *package_path = lower_join_path(lower->source->package_root, header);
if (lower_file_exists(package_path)) return package_path;
free(package_path);
}
const char *source_path = node && node->path && node->path[0] ? node->path : (lower ? lower->current_module_path : NULL);
if (source_path && source_path[0]) {
char *dir = lower_dirname_of(source_path);
char *source_relative = lower_join_path(dir, header);
free(dir);
if (lower_file_exists(source_relative)) return source_relative;
free(source_relative);
}
if (lower_file_exists(header)) return z_strdup(header);
return z_strdup(header);
}
static bool lower_embedded_std_module(const ZProgramGraphNode *module) {
if (!module || module->kind != Z_PROGRAM_GRAPH_NODE_MODULE) return false;
for (size_t i = 0; i < z_std_source_module_count(); i++) {
const ZStdSourceModule *std_module = z_std_source_module_at(i);
if (!std_module) continue;
const char *short_name = strrchr(std_module->module, '.');
const char *module_name = short_name ? short_name + 1 : std_module->module;
const char *std_path_base = strrchr(std_module->path, '/');
std_path_base = std_path_base ? std_path_base + 1 : std_module->path;
bool name_matches = lower_text_eq(module->name, std_module->module) || lower_text_eq(module->name, module_name);
bool path_matches = !module->path ||
!module->path[0] ||
lower_text_eq(module->path, std_module->path) ||
(!strchr(module->path, '/') && lower_text_eq(module->path, std_path_base));
if (name_matches && path_matches) return true;
}
return false;
}
static bool lower_identifier_start(char ch) {
return isalpha((unsigned char)ch) || ch == '_';
}
static bool lower_identifier_part(char ch) {
return isalnum((unsigned char)ch) || ch == '_';
}
static bool lower_text_span_eq(const char *start, const char *end, const char *text) {
if (!start || !end || !text) return false;
while (start < end && *text) {
if (*start++ != *text++) return false;
}
return start == end && *text == '\0';
}
static bool lower_reserved_word_span(const char *start, const char *end) {
const char *keywords[] = {
"as", "break", "check", "choice", "const", "continue", "defer", "else", "enum", "export", "extern", "false",
"fn", "for", "fun", "if", "import", "in", "let", "match", "meta", "mut", "null", "packed", "pub",
"raise", "raises", "rescue", "ret", "return", "shape", "static", "test", "true", "type",
"use", "var", "while", NULL
};
for (size_t i = 0; keywords[i]; i++) {
if (lower_text_span_eq(start, end, keywords[i])) return true;
}
return false;
}
static bool lower_identifier_valid(const char *text) {
if (!text || !lower_identifier_start(*text)) return false;
const char *cursor = text + 1;
for (; *cursor; cursor++) {
if (!lower_identifier_part(*cursor)) return false;
}
return !lower_reserved_word_span(text, cursor);
}
static bool lower_require_identifier(GraphLower *lower, const ZProgramGraphNode *node, const char *text, const char *message) {
if (lower_identifier_valid(text)) return true;
return lower_fail(lower, node,
message ? message : "program graph name is not valid Zero identifier syntax",
"identifier",
text && text[0] ? text : "missing name",
"use a non-keyword identifier");
}
static bool lower_require_top_level_identifier(GraphLower *lower, const ZProgramGraphNode *node, const char *kind, const char *syntax_message, bool allow_test_name) {
const char *name = node ? node->name : NULL;
if (!lower_require_identifier(lower, node, name, syntax_message)) return false;
if ((lower && lower->allow_internal_symbols) || !lower_starts_with(name, "__zero_")) return true;
if (allow_test_name && lower_starts_with(name, "__zero_test_")) return true;
char expected[128];
snprintf(expected, sizeof(expected), "top-level %s name without the __zero_ prefix", kind ? kind : "declaration");
return lower_fail(lower,
node,
"program graph declaration uses a reserved compiler-internal symbol name",
expected,
name,
"rename the declaration; __zero_ names are reserved for compiler-provided helpers");
}
static bool lower_module_name_valid(const char *text) {
if (!text || !*text) return false;
const char *cursor = text;
for (;;) {
const char *segment = cursor;
if (!lower_identifier_start(*cursor)) return false;
cursor++;
while (lower_identifier_part(*cursor)) cursor++;
if (lower_reserved_word_span(segment, cursor)) return false;
if (*cursor == '\0') return true;
if (*cursor != '.') return false;
cursor++;
if (*cursor == '\0') return false;
}
}
static bool lower_module_is_stdlib(const char *module) { return module && strncmp(module, "std.", 4) == 0; }
static const ZProgramGraphNode *lower_find_module_named(const ZProgramGraph *graph, const char *name) {
for (size_t i = 0; graph && name && i < graph->node_len; i++) {
const ZProgramGraphNode *node = &graph->nodes[i];
if (node->kind == Z_PROGRAM_GRAPH_NODE_MODULE && lower_text_eq(node->name, name)) return node;
}
return NULL;
}
static bool lower_match_case_valid(const char *text) {
if (!text || !text[0]) return false;
if (lower_text_eq(text, "true") || lower_text_eq(text, "false")) return true;
if (lower_identifier_valid(text)) return true;
const char *cursor = text;
if (*cursor == '-') cursor++;
if (!isdigit((unsigned char)*cursor)) return false;
return lower_literal_is_raw(text);
}
static Expr *lower_new_expr(GraphLower *lower, ExprKind kind, const ZProgramGraphNode *node) {
Expr *expr = z_checked_malloc(sizeof(Expr));
memset(expr, 0, sizeof(*expr));
expr->kind = kind;
expr->line = lower_line(lower, node);
expr->column = node && node->column > 0 ? node->column : 1;
if (lower && lower->preserve_graph_types && node && node->type && node->type[0]) expr->resolved_type = z_strdup(node->type);
return expr;
}
static Stmt *lower_new_stmt(GraphLower *lower, StmtKind kind, const ZProgramGraphNode *node) {
Stmt *stmt = z_checked_malloc(sizeof(Stmt));
memset(stmt, 0, sizeof(*stmt));
stmt->kind = kind;
stmt->line = lower_line(lower, node);
stmt->column = node && node->column > 0 ? node->column : 1;
if (lower && lower->preserve_graph_types && node && node->type && node->type[0]) stmt->resolved_type = z_strdup(node->type);
if (node && node->id && node->id[0]) stmt->graph_id = z_strdup(node->id);
return stmt;
}
static char *lower_block_graph_id(GraphLower *lower, const ZProgramGraphNode *owner, const char *edge_kind, size_t order) {
const ZProgramGraphNode *block = lower_ordered_node(lower, owner ? owner->id : NULL, edge_kind, order);
return block && block->id && block->id[0] ? z_strdup(block->id) : NULL;
}
static Expr *lower_expr(GraphLower *lower, const ZProgramGraphNode *node);
static StmtVec lower_block(GraphLower *lower, const ZProgramGraphNode *block);
static Expr *lower_required_expr(GraphLower *lower, const ZProgramGraphNode *owner, const char *edge_kind, size_t order, const char *context) {
const ZProgramGraphNode *node = lower_ordered_node(lower, owner ? owner->id : NULL, edge_kind, order);
if (!node) {
lower_fail(lower, owner, "program graph is missing required expression edge", context ? context : "expression edge", edge_kind, NULL);
return NULL;
}
return lower_expr(lower, node);
}
static Expr *lower_optional_expr(GraphLower *lower, const ZProgramGraphNode *owner, const char *edge_kind, size_t order) {
const ZProgramGraphNode *node = lower_ordered_node(lower, owner ? owner->id : NULL, edge_kind, order);
return node ? lower_expr(lower, node) : NULL;
}
static void lower_type_args(GraphLower *lower, const ZProgramGraphNode *node, TypeArgVec *out) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, node ? node->id : NULL, "typeArg", have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *type = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
if (!type) continue;
lower_push_type_arg(out, (TypeArg){
.type = z_strdup(type->type && type->type[0] ? type->type : ""),
.line = lower_line(lower, type),
.column = type->column > 0 ? type->column : 1,
});
}
}
static void lower_args(GraphLower *lower, const ZProgramGraphNode *node, ExprVec *out) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, node ? node->id : NULL, "arg", have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *arg = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
if (!arg) continue;
lower_push_expr(out, lower_expr(lower, arg));
if (lower_has_diag(lower)) return;
}
}
static void lower_shape_fields(GraphLower *lower, const ZProgramGraphNode *node, FieldInitVec *out) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, node ? node->id : NULL, "field", have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *field = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
if (!field) continue;
if (!lower_require_identifier(lower, field, field->name, "program graph field initializer name is not valid Zero identifier syntax")) return;
Expr *value = lower_required_expr(lower, field, "value", 0, "field initializer value");
if (lower_has_diag(lower)) return;
lower_push_field(out, (FieldInit){
.name = z_strdup(field->name && field->name[0] ? field->name : ""),
.value = value,
.line = lower_line(lower, field),
.column = field->column > 0 ? field->column : 1,
});
}
}
static Expr *lower_literal_expr(GraphLower *lower, const ZProgramGraphNode *node) {
if (lower_text_eq(node->type, "String")) {
Expr *expr = lower_new_expr(lower, EXPR_STRING, node);
expr->text = z_strdup(node->value ? node->value : "");
return expr;
}
if (lower_text_eq(node->type, "char")) {
Expr *expr = lower_new_expr(lower, EXPR_CHAR, node);
expr->text = z_strdup(node->value ? node->value : "0");
return expr;
}
if (lower_text_eq(node->value, "true") || lower_text_eq(node->value, "false")) {
Expr *expr = lower_new_expr(lower, EXPR_BOOL, node);
expr->bool_value = lower_text_eq(node->value, "true");
return expr;
}
if (lower_text_eq(node->value, "null")) return lower_new_expr(lower, EXPR_NULL, node);
if (!lower_literal_is_raw(node->value)) {
Expr *expr = lower_new_expr(lower, EXPR_STRING, node);
expr->text = z_strdup(node->value ? node->value : "");
return expr;
}
Expr *expr = lower_new_expr(lower, EXPR_NUMBER, node);
expr->text = z_strdup(node->value ? node->value : "0");
return expr;
}
static Expr *lower_expr(GraphLower *lower, const ZProgramGraphNode *node) {
if (!node) {
lower_fail(lower, NULL, "program graph is missing expression node", "expression node", "missing node", NULL);
return NULL;
}
Expr *expr = NULL;
switch (node->kind) {
case Z_PROGRAM_GRAPH_NODE_IDENTIFIER:
if (!lower_require_identifier(lower, node, node->name, "program graph identifier name is not valid Zero identifier syntax")) return NULL;
expr = lower_new_expr(lower, EXPR_IDENT, node);
expr->text = z_strdup(node->name && node->name[0] ? node->name : "");
lower_type_args(lower, node, &expr->type_args);
return expr;
case Z_PROGRAM_GRAPH_NODE_LITERAL:
return lower_literal_expr(lower, node);
case Z_PROGRAM_GRAPH_NODE_FIELD_ACCESS:
if (!lower_require_identifier(lower, node, node->name, "program graph field access name is not valid Zero identifier syntax")) return NULL;
expr = lower_new_expr(lower, EXPR_MEMBER, node);
expr->text = z_strdup(node->name && node->name[0] ? node->name : "");
expr->left = lower_required_expr(lower, node, "left", 0, "field access receiver");
lower_type_args(lower, node, &expr->type_args);
return expr;
case Z_PROGRAM_GRAPH_NODE_INDEX_ACCESS:
expr = lower_new_expr(lower, EXPR_INDEX, node);
expr->left = lower_required_expr(lower, node, "left", 0, "index receiver");
expr->right = lower_required_expr(lower, node, "right", 1, "index expression");
return expr;
case Z_PROGRAM_GRAPH_NODE_SLICE:
expr = lower_new_expr(lower, EXPR_SLICE, node);
expr->left = lower_required_expr(lower, node, "left", 0, "slice receiver");
lower_push_expr(&expr->args, lower_optional_expr(lower, node, "arg", 0));
lower_push_expr(&expr->args, lower_optional_expr(lower, node, "arg", 1));
return expr;
case Z_PROGRAM_GRAPH_NODE_CALL:
case Z_PROGRAM_GRAPH_NODE_METHOD_CALL:
if (lower_binary_operator(node->name) &&
lower_ordered_node(lower, node->id, "left", 0) &&
lower_ordered_node(lower, node->id, "right", 1)) {
expr = lower_new_expr(lower, EXPR_BINARY, node);
expr->text = z_strdup(node->name);
expr->left = lower_required_expr(lower, node, "left", 0, "binary left operand");
expr->right = lower_required_expr(lower, node, "right", 1, "binary right operand");
return expr;
}
if (node->name && node->name[0] &&
lower_ordered_node(lower, node->id, "left", 0) &&
lower_ordered_node(lower, node->id, "right", 1)) {
lower_fail(lower, node,
"program graph call operator is not valid Zero operator syntax",
"supported binary operator",
node->name,
"use a supported operator or remove the right operand edge");
return NULL;
}
expr = lower_new_expr(lower, EXPR_CALL, node);
expr->left = lower_optional_expr(lower, node, "left", 0);
if (!expr->left && node->name && node->name[0]) {
if (!lower_require_identifier(lower, node, node->name, "program graph call callee name is not valid Zero identifier syntax")) return NULL;
expr->left = lower_new_expr(lower, EXPR_IDENT, node);
expr->left->text = z_strdup(node->name);
}
if (!expr->left) lower_fail(lower, node, "program graph call is missing callee", "left edge or callee name", "missing callee", NULL);
expr->prefix_deref = lower_text_eq(node->value, "prefix-deref");
lower_type_args(lower, node, &expr->type_args);
lower_args(lower, node, &expr->args);
return expr;
case Z_PROGRAM_GRAPH_NODE_CAST:
expr = lower_new_expr(lower, EXPR_CAST, node);
expr->text = z_strdup(node->name && node->name[0] ? node->name : (node->type ? node->type : ""));
expr->left = lower_required_expr(lower, node, "left", 0, "cast operand");
return expr;
case Z_PROGRAM_GRAPH_NODE_BORROW:
expr = lower_new_expr(lower, EXPR_BORROW, node);
expr->mutable_borrow = node->is_mutable;
expr->left = lower_required_expr(lower, node, "left", 0, "borrow operand");
return expr;
case Z_PROGRAM_GRAPH_NODE_CHECK:
expr = lower_new_expr(lower, EXPR_CHECK, node);
expr->left = lower_required_expr(lower, node, "left", 0, "checked expression");
return expr;
case Z_PROGRAM_GRAPH_NODE_RESCUE:
if (!lower_require_identifier(lower, node, node->name, "program graph rescue binding name is not valid Zero identifier syntax")) return NULL;
expr = lower_new_expr(lower, EXPR_RESCUE, node);
expr->text = z_strdup(node->name && node->name[0] ? node->name : "");
expr->left = lower_required_expr(lower, node, "left", 0, "rescue expression");
expr->right = lower_required_expr(lower, node, "right", 1, "rescue fallback expression");
return expr;
case Z_PROGRAM_GRAPH_NODE_META:
expr = lower_new_expr(lower, EXPR_META, node);
expr->text = z_strdup(node->name && node->name[0] ? node->name : "");
expr->left = lower_required_expr(lower, node, "left", 0, "meta expression");
return expr;
case Z_PROGRAM_GRAPH_NODE_SHAPE_LITERAL:
expr = lower_new_expr(lower, EXPR_SHAPE_LITERAL, node);
expr->text = z_strdup(node->name && node->name[0] ? node->name : "");
lower_shape_fields(lower, node, &expr->fields);
return expr;
case Z_PROGRAM_GRAPH_NODE_ARRAY_LITERAL:
expr = lower_new_expr(lower, EXPR_ARRAY_LITERAL, node);
expr->array_repeat = lower_text_eq(node->value, "repeat");
lower_args(lower, node, &expr->args);
return expr;
default:
lower_fail(lower, node, "program graph expression kind is not supported by direct lowering", "expression node", z_program_graph_node_kind_name(node->kind), NULL);
return NULL;
}
}
static MatchArm lower_match_arm(GraphLower *lower, const ZProgramGraphNode *node) {
if (!lower_match_case_valid(node ? node->name : NULL)) {
lower_fail(lower,
node,
"program graph match case name is not valid Zero match syntax",
"identifier, true, false, or number",
node && node->name ? node->name : "missing name",
"use a non-keyword case name");
}
if (!lower_has_diag(lower) && node && node->value && node->value[0]) {
lower_require_identifier(lower, node, node->value, "program graph match payload name is not valid Zero identifier syntax");
}
MatchArm arm = {
.case_name = z_strdup(node && node->name ? node->name : ""),
.payload_name = node && node->value && node->value[0] ? z_strdup(node->value) : NULL,
.line = lower_line(lower, node),
.column = node && node->column > 0 ? node->column : 1,
};
const ZProgramGraphNode *range_end = lower_ordered_node(lower, node ? node->id : NULL, "rangeEnd", 0);
if (range_end) arm.range_end = z_strdup(range_end->value && range_end->value[0] ? range_end->value : (range_end->name ? range_end->name : ""));
arm.guard = lower_optional_expr(lower, node, "guard", 0);
const ZProgramGraphNode *body = lower_ordered_node(lower, node ? node->id : NULL, "body", 0);
if (!body) lower_fail(lower, node, "program graph match arm is missing body block", "body block edge", "missing body", NULL);
else {
arm.body = lower_block(lower, body);
if (body->id && body->id[0]) arm.body_graph_id = z_strdup(body->id);
}
return arm;
}
static void lower_match_arms(GraphLower *lower, const ZProgramGraphNode *node, MatchArmVec *out) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, node ? node->id : NULL, "arm", have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *arm = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
if (!arm) continue;
lower_push_match_arm(out, lower_match_arm(lower, arm));
if (lower_has_diag(lower)) return;
}
}
static Stmt *lower_stmt(GraphLower *lower, const ZProgramGraphNode *node) {
if (!node) {
lower_fail(lower, NULL, "program graph is missing statement node", "statement node", "missing node", NULL);
return NULL;
}
Stmt *stmt = NULL;
switch (node->kind) {
case Z_PROGRAM_GRAPH_NODE_LET:
if (!lower_require_identifier(lower, node, node->name, "program graph binding name is not valid Zero identifier syntax")) return NULL;
stmt = lower_new_stmt(lower, STMT_LET, node);
stmt->name = z_strdup(node->name && node->name[0] ? node->name : "");
stmt->type = node->type && node->type[0] ? z_strdup(node->type) : NULL;
stmt->mutable_binding = node->is_mutable;
stmt->expr = lower_required_expr(lower, node, "expr", 0, "let initializer");
return stmt;
case Z_PROGRAM_GRAPH_NODE_ASSIGNMENT:
stmt = lower_new_stmt(lower, STMT_ASSIGN, node);
stmt->target = lower_required_expr(lower, node, "target", 0, "assignment target");
stmt->expr = lower_required_expr(lower, node, "expr", 0, "assignment value");
return stmt;
case Z_PROGRAM_GRAPH_NODE_DEFER:
stmt = lower_new_stmt(lower, STMT_DEFER, node);
stmt->expr = lower_required_expr(lower, node, "expr", 0, "defer expression");
return stmt;
case Z_PROGRAM_GRAPH_NODE_CHECK:
stmt = lower_new_stmt(lower, STMT_CHECK, node);
stmt->expr = lower_required_expr(lower, node, "expr", 0, "check expression");
return stmt;
case Z_PROGRAM_GRAPH_NODE_RETURN:
stmt = lower_new_stmt(lower, STMT_RETURN, node);
stmt->expr = lower_optional_expr(lower, node, "expr", 0);
return stmt;
case Z_PROGRAM_GRAPH_NODE_EXPRESSION_STATEMENT:
stmt = lower_new_stmt(lower, STMT_EXPR, node);
stmt->expr = lower_required_expr(lower, node, "expr", 0, "statement expression");
return stmt;
case Z_PROGRAM_GRAPH_NODE_IF:
stmt = lower_new_stmt(lower, STMT_IF, node);
stmt->expr = lower_required_expr(lower, node, "expr", 0, "if condition");
stmt->then_body = lower_block(lower, lower_ordered_node(lower, node->id, "then", 0));
stmt->else_body = lower_block(lower, lower_ordered_node(lower, node->id, "else", 1));
stmt->then_graph_id = lower_block_graph_id(lower, node, "then", 0);
stmt->else_graph_id = lower_block_graph_id(lower, node, "else", 1);
return stmt;
case Z_PROGRAM_GRAPH_NODE_WHILE:
stmt = lower_new_stmt(lower, STMT_WHILE, node);
stmt->expr = lower_required_expr(lower, node, "expr", 0, "while condition");
stmt->then_body = lower_block(lower, lower_ordered_node(lower, node->id, "then", 0));
stmt->then_graph_id = lower_block_graph_id(lower, node, "then", 0);
return stmt;
case Z_PROGRAM_GRAPH_NODE_FOR:
if (!lower_require_identifier(lower, node, node->name, "program graph loop binding name is not valid Zero identifier syntax")) return NULL;
stmt = lower_new_stmt(lower, STMT_FOR, node);
stmt->name = z_strdup(node->name && node->name[0] ? node->name : "");
stmt->expr = lower_required_expr(lower, node, "expr", 0, "for range start");
stmt->range_end = lower_required_expr(lower, node, "rangeEnd", 1, "for range end");
stmt->then_body = lower_block(lower, lower_ordered_node(lower, node->id, "then", 0));
stmt->then_graph_id = lower_block_graph_id(lower, node, "then", 0);
return stmt;
case Z_PROGRAM_GRAPH_NODE_BREAK:
return lower_new_stmt(lower, STMT_BREAK, node);
case Z_PROGRAM_GRAPH_NODE_CONTINUE:
return lower_new_stmt(lower, STMT_CONTINUE, node);
case Z_PROGRAM_GRAPH_NODE_MATCH:
stmt = lower_new_stmt(lower, STMT_MATCH, node);
stmt->expr = lower_required_expr(lower, node, "expr", 0, "match expression");
lower_match_arms(lower, node, &stmt->match_arms);
return stmt;
case Z_PROGRAM_GRAPH_NODE_RAISE:
if (!lower_require_identifier(lower, node, node->name, "program graph raise error name is not valid Zero identifier syntax")) return NULL;
stmt = lower_new_stmt(lower, STMT_RAISE, node);
stmt->name = z_strdup(node->name && node->name[0] ? node->name : "");
return stmt;
default:
lower_fail(lower, node, "program graph statement kind is not supported by direct lowering", "statement node", z_program_graph_node_kind_name(node->kind), NULL);
return NULL;
}
}
static StmtVec lower_block(GraphLower *lower, const ZProgramGraphNode *block) {
StmtVec body = {0};
if (!block) return body;
if (block->kind != Z_PROGRAM_GRAPH_NODE_BLOCK) {
lower_fail(lower, block, "program graph body edge does not point to a block", "Block node", z_program_graph_node_kind_name(block->kind), NULL);
return body;
}
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, block->id, "statement", have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *stmt_node = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
Stmt *stmt = lower_stmt(lower, stmt_node);
if (lower_has_diag(lower)) return body;
lower_push_stmt(&body, stmt);
}
return body;
}
static void lower_params(GraphLower *lower, const ZProgramGraphNode *owner, const char *edge_kind, ParamVec *out) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, owner ? owner->id : NULL, edge_kind, have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *node = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
if (!node) continue;
if (!lower_require_identifier(lower, node, node->name, "program graph parameter name is not valid Zero identifier syntax")) return;
Param param = {
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.type = node->type && node->type[0] ? z_strdup(node->type) : NULL,
.default_value = lower_optional_expr(lower, node, "default", 0),
.is_static = node->is_static,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
};
lower_push_param(out, param);
if (lower_has_diag(lower)) return;
}
}
static Function lower_function(GraphLower *lower, const ZProgramGraphNode *node, bool top_level) {
if (top_level) lower_require_top_level_identifier(lower, node, "function", "program graph function name is not valid Zero identifier syntax", true);
else lower_require_identifier(lower, node, node ? node->name : NULL, "program graph function name is not valid Zero identifier syntax");
Function fun = {
.name = z_strdup(node && node->name ? node->name : ""),
.test_name = node && lower_starts_with(node->name, "__zero_test_") ? z_strdup(node->value ? node->value : "") : NULL,
.return_type = z_strdup(node && node->type && node->type[0] ? node->type : "Void"),
.is_public = node && node->is_public,
.raises = node && node->fallible,
.is_test = node && lower_starts_with(node->name, "__zero_test_"),
.export_c = node && node->export_c,
.line = lower_line(lower, node),
.column = node && node->column > 0 ? node->column : 1,
};
lower_params(lower, node, "typeParam", &fun.type_params);
lower_params(lower, node, "param", &fun.params);
lower_params(lower, node, "error", &fun.errors);
fun.has_error_set = fun.errors.len > 0;
const ZProgramGraphNode *body = lower_ordered_node(lower, node ? node->id : NULL, "body", 0);
if (!body) lower_fail(lower, node, "program graph function is missing body block", "body block edge", "missing body", NULL);
else fun.body = lower_block(lower, body);
return fun;
}
static void lower_methods(GraphLower *lower, const ZProgramGraphNode *owner, FunctionVec *out) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, owner ? owner->id : NULL, "method", have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *method = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
if (!method) continue;
if (method->kind != Z_PROGRAM_GRAPH_NODE_FUNCTION) {
lower_fail(lower, method, "program graph method edge does not point to a function", "Function node", z_program_graph_node_kind_name(method->kind), NULL);
return;
}
lower_push_function(out, lower_function(lower, method, false));
if (lower_has_diag(lower)) return;
}
}
static void lower_import(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
const char *module = node && node->name ? node->name : "";
const char *alias = node && node->value && node->value[0] ? node->value : NULL;
if (!lower_module_name_valid(module)) {
lower_fail(lower, node, "program graph import module is not valid Zero import syntax", "dot-separated module identifiers", module, "use module names like std.mem or package.local");
return;
}
if (alias && !lower_identifier_valid(alias)) {
lower_fail(lower, node, "program graph import alias is not valid Zero identifier syntax", "identifier", alias, "use an identifier alias or clear the alias value");
return;
}
const ZProgramGraphNode *target_module = lower_module_is_stdlib(module) ? NULL : lower_find_module_named(lower->graph, module);
if (!lower_module_is_stdlib(module) && !target_module) {
lower_fail(lower, node, "program graph import target module is missing", "Module node for package-local import or std.* import", module, "add the imported module to the artifact or remove the import");
return;
}
int original_column = node && node->column > 0 ? node->column : 1;
int canonical_length = 4 + (int)strlen(module ? module : "");
int original_line = node && node->line > 0 ? node->line : 1;
lower_push_use(&program->use_imports, (UseImport){
.module = z_strdup(module),
.alias = alias ? z_strdup(alias) : NULL,
.line = lower_line(lower, node),
.column = original_column,
.end_column = original_column + canonical_length,
});
if (lower && lower->source) {
lower_source_push_import_edge(lower->source,
lower->current_module_name ? lower->current_module_name : "main",
module,
target_module && target_module->path ? target_module->path : "",
node && node->path ? node->path : lower->current_module_path,
original_line,
original_column,
canonical_length);
}
}
static void lower_c_import(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_identifier(lower, node, node ? node->name : NULL, "program graph C import alias is not valid Zero identifier syntax")) return;
const char *header = node->value && node->value[0] ? node->value : "";
lower_push_c_import(&program->c_imports, (CImport){
.header = z_strdup(header),
.resolved_header = lower_resolve_c_import_header_path(lower, node, header),
.alias = z_strdup(node->name && node->name[0] ? node->name : ""),
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
});
}
static void lower_const(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_top_level_identifier(lower, node, "const", "program graph const name is not valid Zero identifier syntax", false)) return;
ConstDecl item = {
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.type = node->type && node->type[0] ? z_strdup(node->type) : NULL,
.expr = lower_required_expr(lower, node, "value", 0, "const value"),
.is_public = node->is_public,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
};
lower_push_const(&program->consts, item);
}
static void lower_alias(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_top_level_identifier(lower, node, "type-alias", "program graph alias name is not valid Zero identifier syntax", false)) return;
lower_push_alias(&program->aliases, (TypeAlias){
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.target = z_strdup(node->type && node->type[0] ? node->type : ""),
.is_public = node->is_public,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
});
}
static void lower_shape(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_top_level_identifier(lower, node, "shape", "program graph type name is not valid Zero identifier syntax", false)) return;
Shape shape = {
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.layout = z_strdup(node->value && node->value[0] ? node->value : "auto"),
.is_public = node->is_public,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
};
lower_params(lower, node, "typeParam", &shape.type_params);
lower_params(lower, node, "field", &shape.fields);
lower_methods(lower, node, &shape.methods);
lower_push_shape(&program->shapes, shape);
}
static void lower_interface(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_top_level_identifier(lower, node, "interface", "program graph interface name is not valid Zero identifier syntax", false)) return;
InterfaceDecl item = {
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.is_public = node->is_public,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
};
lower_params(lower, node, "typeParam", &item.type_params);
lower_methods(lower, node, &item.methods);
lower_push_interface(&program->interfaces, item);
}
static void lower_enum(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_top_level_identifier(lower, node, "enum", "program graph enum name is not valid Zero identifier syntax", false)) return;
EnumDecl item = {
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.type = node->type && node->type[0] ? z_strdup(node->type) : NULL,
.is_public = node->is_public,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
};
lower_params(lower, node, "case", &item.cases);
lower_push_enum(&program->enums, item);
}
static void lower_choice(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!lower_require_top_level_identifier(lower, node, "choice", "program graph choice name is not valid Zero identifier syntax", false)) return;
Choice item = {
.name = z_strdup(node->name && node->name[0] ? node->name : ""),
.is_public = node->is_public,
.line = lower_line(lower, node),
.column = node->column > 0 ? node->column : 1,
};
lower_params(lower, node, "case", &item.cases);
lower_push_choice(&program->choices, item);
}
static void lower_top_level(GraphLower *lower, Program *program, const ZProgramGraphNode *node) {
if (!node || lower_has_diag(lower)) return;
switch (node->kind) {
case Z_PROGRAM_GRAPH_NODE_IMPORT:
lower_import(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_C_IMPORT:
lower_c_import(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_CONST:
lower_const(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_TYPE_ALIAS:
lower_alias(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_SHAPE:
lower_shape(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_INTERFACE:
lower_interface(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_ENUM:
lower_enum(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_CHOICE:
lower_choice(lower, program, node);
break;
case Z_PROGRAM_GRAPH_NODE_FUNCTION:
lower_push_function(&program->functions, lower_function(lower, node, true));
break;
default:
lower_fail(lower, node, "program graph declaration kind is not supported by direct lowering", "declaration node", z_program_graph_node_kind_name(node->kind), NULL);
break;
}
}
static void lower_top_level_edges(GraphLower *lower, Program *program, const ZProgramGraphNode *module, const char *edge_kind) {
bool have_last = false;
size_t last_order = 0;
for (;;) {
const ZProgramGraphEdge *edge = lower_next_edge_by_order(lower, module ? module->id : NULL, edge_kind, have_last, last_order);
if (!edge) break;
const ZProgramGraphNode *node = lower_find_node(lower, edge->to);
last_order = edge->order;
have_last = true;
lower_top_level(lower, program, node);
if (lower_has_diag(lower)) return;
}
}
static void lower_module(GraphLower *lower, Program *program, const ZProgramGraphNode *module) {
bool previous_allow_internal_symbols = lower->allow_internal_symbols;
const char *previous_module_name = lower->current_module_name;
const char *previous_module_path = lower->current_module_path;
ZBuf module_path;
zbuf_init(&module_path);
if (module && module->path && module->path[0]) {
zbuf_append(&module_path, module->path);
} else {
zbuf_append(&module_path, lower->artifact_path ? lower->artifact_path : "<program-graph>");
if (lower->module_count > 1) {
zbuf_append_char(&module_path, '#');
zbuf_append(&module_path, module && module->name && module->name[0] ? module->name : "main");
}
}
lower->allow_internal_symbols = previous_allow_internal_symbols || lower_embedded_std_module(module);
lower->current_module_name = module && module->name && module->name[0] ? module->name : "main";
lower->current_module_path = module_path.data;
lower_top_level_edges(lower, program, module, "cImport");
lower_top_level_edges(lower, program, module, "import");
lower_top_level_edges(lower, program, module, "const");
lower_top_level_edges(lower, program, module, "alias");
lower_top_level_edges(lower, program, module, "shape");
lower_top_level_edges(lower, program, module, "interface");
lower_top_level_edges(lower, program, module, "enum");
lower_top_level_edges(lower, program, module, "choice");
lower_top_level_edges(lower, program, module, "function");
lower->allow_internal_symbols = previous_allow_internal_symbols;
lower->current_module_name = previous_module_name;
lower->current_module_path = previous_module_path;
zbuf_free(&module_path);
}
static bool lower_source_has_file(const SourceInput *source, const char *path) {
for (size_t i = 0; source && path && i < source->source_file_count; i++) {
if (lower_text_eq(source->source_files[i], path)) return true;
}
return false;
}
static void lower_source_add_file(SourceInput *source, const char *path) {
if (!source) return;
const char *stable_path = path && path[0] ? path : (source->source_file ? source->source_file : "");
if (!lower_source_has_file(source, stable_path)) lower_source_push_string(&source->source_files, &source->source_file_count, stable_path);
}
static void lower_source_set_package_identity(SourceInput *source, const char *identity) {
const char *prefix = "package:";
size_t prefix_len = strlen(prefix);
if (!source || !identity || strncmp(identity, prefix, prefix_len) != 0) return;
const char *name = identity + prefix_len;
const char *version = strrchr(name, '@');
if (version && version > name) {
source->package_name = z_strndup(name, (size_t)(version - name));
source->package_version = z_strdup(version + 1);
} else {
source->package_name = z_strdup(name);
}
}
static void lower_init_source_from_graph(SourceInput *source, const ZProgramGraph *graph, const char *artifact_path) {
if (!source) return;
*source = (SourceInput){0};
source->source_file = z_strdup(artifact_path && artifact_path[0] ? artifact_path : "<program-graph>");
source->source = z_strdup("");
lower_source_set_package_identity(source, graph ? graph->module_identity : NULL);
lower_source_seed_package_manifest(source, graph, artifact_path);
size_t module_count = 0;
for (size_t i = 0; graph && i < graph->node_len; i++) {
if (graph->nodes[i].kind == Z_PROGRAM_GRAPH_NODE_MODULE) module_count++;
}
for (size_t i = 0; graph && i < graph->node_len; i++) {
const ZProgramGraphNode *node = &graph->nodes[i];
if (node->kind != Z_PROGRAM_GRAPH_NODE_MODULE) continue;
ZBuf synthetic_path;
zbuf_init(&synthetic_path);
if (node->path && node->path[0]) {
zbuf_append(&synthetic_path, node->path);
} else {
zbuf_append(&synthetic_path, source->source_file);
if (module_count > 1) {
zbuf_append_char(&synthetic_path, '#');
zbuf_append(&synthetic_path, node->name && node->name[0] ? node->name : "main");
}
}
const char *path = synthetic_path.data ? synthetic_path.data : source->source_file;
lower_source_add_file(source, path);
lower_source_push_module(source, node->name && node->name[0] ? node->name : "main", path);
zbuf_free(&synthetic_path);
}
if (source->module_count == 0) {
lower_source_add_file(source, source->source_file);
lower_source_push_module(source, "main", source->source_file);
}
}
static bool lower_to_program_modules(GraphLower *lower, Program *out) {
size_t module_count = 0;
for (size_t i = 0; i < lower->graph->node_len; i++) {
if (lower->graph->nodes[i].kind == Z_PROGRAM_GRAPH_NODE_MODULE) module_count++;
}
lower->module_count = module_count;
for (size_t i = 0; i < lower->graph->node_len; i++) {
const ZProgramGraphNode *node = &lower->graph->nodes[i];
if (node->kind != Z_PROGRAM_GRAPH_NODE_MODULE) continue;
lower_module(lower, out, node);
if (lower_has_diag(lower)) {
z_free_program(out);
return false;
}
}
if (module_count == 0) {
lower_fail(lower, NULL, "program graph has no module node", "at least one Module node", "missing module", NULL);
z_free_program(out);
return false;
}
return true;
}
static bool lower_to_program(GraphLower *lower, Program *out) {
if (!out) return false;
*out = (Program){0};
if (!lower || !lower->graph) return lower_fail(lower, NULL, "program graph is missing", "ProgramGraph input", "missing graph", NULL);
z_program_graph_adjacency_init(&lower->adjacency, lower->graph);
bool ok = lower_to_program_modules(lower, out);
z_program_graph_adjacency_free(&lower->adjacency);
return ok;
}
bool z_program_graph_lower_to_program(const ZProgramGraph *graph, Program *out, ZDiag *diag) {
GraphLower lower = {.graph = graph, .diag = diag};
return lower_to_program(&lower, out);
}
bool z_program_graph_lower_to_program_with_source(const ZProgramGraph *graph, const char *artifact_path, Program *out, SourceInput *source, ZDiag *diag) {
lower_init_source_from_graph(source, graph, artifact_path);
GraphLower lower = {.graph = graph, .diag = diag, .source = source, .artifact_path = artifact_path};
return lower_to_program(&lower, out);
}
bool z_program_graph_lower_to_program_for_roundtrip(const ZProgramGraph *graph, const char *artifact_path, Program *out, SourceInput *source, ZDiag *diag) {
lower_init_source_from_graph(source, graph, artifact_path);
GraphLower lower = {.graph = graph, .diag = diag, .source = source, .artifact_path = artifact_path, .preserve_graph_types = true, .allow_internal_symbols = true};
return lower_to_program(&lower, out);
}