Files
lity3d/Demo/ly_interpreter.c
T

291 lines
10 KiB
C

// ============================================================================
// ly_interpreter.c - .ly 积木语言独立解释器
// 直接解析 .ly 格式,不依赖引擎 VisualScript 框架
// 用法: ly_interpreter.exe <script.ly>
// ============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// === 数据结构 ===
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;
}