// ============================================================================ // ly_interpreter.c - .ly 积木语言独立解释器 // 直接解析 .ly 格式,不依赖引擎 VisualScript 框架 // 用法: ly_interpreter.exe // ============================================================================ #include #include #include #include // === 数据结构 === typedef union { double num; char str[256]; int bool_val; } Value; typedef struct { int id; char name[64]; float x, y; char data[256]; int color; Value inputs[8]; int input_count; Value outputs[4]; int output_count; } Block; typedef struct { int from_block, from_slot; int to_block, to_slot; } Wire; #define MAX_BLOCKS 256 #define MAX_WIRES 256 static Block g_blocks[MAX_BLOCKS]; static int g_block_count = 0; static Wire g_wires[MAX_WIRES]; static int g_wire_count = 0; // === 解析 === static void trim(char* s) { char* p = s; while (*p == ' ' || *p == '\t') p++; if (p != s) memmove(s, p, strlen(p) + 1); int len = strlen(s); while (len > 0 && (s[len-1] == '\n' || s[len-1] == '\r' || s[len-1] == ' ')) s[--len] = 0; } static int parse_ly(const char* path) { FILE* f = fopen(path, "r"); if (!f) { fprintf(stderr, "Cannot open %s\n", path); return 0; } char line[1024]; int section = 0; // 0=none, 1=block, 2=wire int cur_bid = -1; while (fgets(line, sizeof(line), f)) { trim(line); if (line[0] == '#' || line[0] == 0) continue; if (strcmp(line, "[B]") == 0 || strcmp(line, "[Block]") == 0) { section = 1; cur_bid = -1; continue; } if (strcmp(line, "[W]") == 0 || strcmp(line, "[Wire]") == 0) { section = 2; continue; } if (line[0] == '[') { section = 0; continue; } if (section == 1) { char* eq = strchr(line, '='); if (!eq) continue; *eq = 0; char* key = line; char* val = eq + 1; if (strcmp(key, "id") == 0) { cur_bid = g_block_count; memset(&g_blocks[cur_bid], 0, sizeof(Block)); g_blocks[cur_bid].id = atoi(val); g_block_count++; } else if (cur_bid >= 0) { if (strcmp(key, "name") == 0) strncpy(g_blocks[cur_bid].name, val, 63); else if (strcmp(key, "p") == 0 || strcmp(key, "pos") == 0) sscanf(val, "%f,%f", &g_blocks[cur_bid].x, &g_blocks[cur_bid].y); else if (strcmp(key, "d") == 0 || strcmp(key, "data") == 0) strncpy(g_blocks[cur_bid].data, val, 255); else if (strcmp(key, "c") == 0 || strcmp(key, "color") == 0) g_blocks[cur_bid].color = atoi(val); } } else if (section == 2) { Wire* w = &g_wires[g_wire_count]; char* eq = strchr(line, '='); if (!eq) continue; *eq = 0; char* key = line; char* val = eq + 1; if (strcmp(key, "a") == 0 || strcmp(key, "from") == 0) { w->from_block = atoi(val) / 10000; w->from_slot = atoi(val) % 10000; } else if (strcmp(key, "b") == 0 || strcmp(key, "to") == 0) { w->to_block = atoi(val) / 10000; w->to_slot = atoi(val) % 10000; if (w->from_block > 0) g_wire_count++; } } } fclose(f); printf("Parsed: %d blocks, %d wires\n", g_block_count, g_wire_count); return 1; } // === 查找 === static Block* find_block(int id) { for (int i = 0; i < g_block_count; i++) if (g_blocks[i].id == id) return &g_blocks[i]; return NULL; } // 找到从某个 block 的 output_slot 出发,连接到的下一个 block static Block* follow_wire(int from_id, int from_slot) { for (int i = 0; i < g_wire_count; i++) { if (g_wires[i].from_block == from_id && g_wires[i].from_slot == from_slot) return find_block(g_wires[i].to_block); } return NULL; } // 获取连接到某个 block input_slot 的值 static Value get_input_value(int block_id, int slot) { for (int i = 0; i < g_wire_count; i++) { if (g_wires[i].to_block == block_id && g_wires[i].to_slot == slot) { Block* src = find_block(g_wires[i].from_block); if (src && src->output_count > g_wires[i].from_slot) return src->outputs[g_wires[i].from_slot]; } } Value v; v.num = 0; return v; } // === 解释执行 === static int g_variables[64]; // 简单变量存储 static char g_var_names[64][64]; static int g_var_count = 0; static int get_var(const char* name) { for (int i = 0; i < g_var_count; i++) if (strcmp(g_var_names[i], name) == 0) return g_variables[i]; return 0; } static void set_var(const char* name, int val) { for (int i = 0; i < g_var_count; i++) if (strcmp(g_var_names[i], name) == 0) { g_variables[i] = val; return; } if (g_var_count < 64) { strncpy(g_var_names[g_var_count], name, 63); g_variables[g_var_count++] = val; } } static void execute(Block* block, int depth); static void execute_block(Block* b, int depth) { if (!b) return; if (depth > 100) { printf("Stack overflow!\n"); return; } const char* n = b->name; if (strcmp(n, "开始") == 0) { // Follow output wire Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "移动物体") == 0) { Value dx = get_input_value(b->id, 1); Value dy = get_input_value(b->id, 2); Value dz = get_input_value(b->id, 3); printf(" [move] dx=%.1f dy=%.1f dz=%.1f\n", dx.num, dy.num, dz.num); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "旋转物体") == 0) { Value dx = get_input_value(b->id, 1); Value dy = get_input_value(b->id, 2); Value dz = get_input_value(b->id, 3); printf(" [rotate] rx=%.1f ry=%.1f rz=%.1f\n", dx.num, dy.num, dz.num); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "当按键") == 0) { // Event: just follow output (simulated - always fires) printf(" [key event] key='%s'\n", b->data); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "当点击") == 0) { printf(" [click event]\n"); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "打印") == 0 || strcmp(n, "print") == 0) { Value msg = get_input_value(b->id, 1); printf(" [print] %s\n", msg.str[0] ? msg.str : b->data); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "等待") == 0 || strcmp(n, "wait") == 0) { Value secs = get_input_value(b->id, 1); double s = secs.num > 0 ? secs.num : atof(b->data); printf(" [wait] %.1fs\n", s); // In real engine, this would pause. For now, just continue. Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "循环") == 0 || strcmp(n, "loop") == 0) { Value count = get_input_value(b->id, 1); int n = count.num > 0 ? (int)count.num : atoi(b->data); if (n <= 0) n = 1; printf(" [loop] %d times\n", n); for (int i = 0; i < n; i++) { Block* body = follow_wire(b->id, 0); execute(body, depth + 1); } } else if (strcmp(n, "如果") == 0 || strcmp(n, "if") == 0) { Value cond = get_input_value(b->id, 1); printf(" [if] condition=%d\n", cond.bool_val); Block* next = follow_wire(b->id, cond.bool_val ? 0 : 1); execute(next, depth + 1); } else if (strcmp(n, "数学运算") == 0 || strcmp(n, "+") == 0) { Value a = get_input_value(b->id, 0); Value b2 = get_input_value(b->id, 1); b->outputs[0].num = a.num + b2.num; printf(" [add] %.1f + %.1f = %.1f\n", a.num, b2.num, b->outputs[0].num); } else if (strcmp(n, "变量设置") == 0 || strcmp(n, "set_var") == 0) { Value v = get_input_value(b->id, 1); set_var(b->data, (int)v.num); printf(" [set_var] %s = %d\n", b->data, (int)v.num); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } else if (strcmp(n, "变量读取") == 0 || strcmp(n, "get_var") == 0) { int v = get_var(b->data); b->outputs[0].num = v; printf(" [get_var] %s = %d\n", b->data, v); } else { printf(" [unknown] '%s'\n", n); Block* next = follow_wire(b->id, 0); execute(next, depth + 1); } } static void execute(Block* block, int depth) { if (!block) return; execute_block(block, depth); } // === 主函数 === int main(int argc, char* argv[]) { const char* script = "Demo/test.ly"; if (argc > 1) script = argv[1]; printf("=== lity3d .ly Interpreter ===\n"); printf("Script: %s\n\n", script); if (!parse_ly(script)) return 1; printf("\n--- Blocks ---\n"); for (int i = 0; i < g_block_count; i++) printf(" [%d] %s data='%s' pos=(%.0f,%.0f)\n", g_blocks[i].id, g_blocks[i].name, g_blocks[i].data, g_blocks[i].x, g_blocks[i].y); printf("\n--- Wires ---\n"); for (int i = 0; i < g_wire_count; i++) printf(" [%d].out%d -> [%d].in%d\n", g_wires[i].from_block, g_wires[i].from_slot, g_wires[i].to_block, g_wires[i].to_slot); printf("\n--- Execute ---\n"); // Find start block Block* start = NULL; for (int i = 0; i < g_block_count; i++) if (strcmp(g_blocks[i].name, "开始") == 0) { start = &g_blocks[i]; break; } if (!start) { // Try to find any block with incoming wires as entry point // Or just execute the first block if (g_block_count > 0) start = &g_blocks[0]; } if (start) { execute(start, 0); } else { printf("No executable blocks found.\n"); } printf("\n--- Done ---\n"); return 0; }