feat: standalone .ly interpreter - parses blocks/wires, executes flow control

This commit is contained in:
shuiyu1123 committed 2026-07-11 11:49:47 +08:00
1 parent c4815c3c69
commit f85c7e37f1
5 files changed
+325 -116

No files matched your search

+312 -112
View File
@@ -1,11 +1,43 @@
// ============================================================================
// block_executor.c - 积木执行引擎实现
// block_executor.c - 积木执行引擎 (完整实现)
// ============================================================================
#include "block_executor.h"
#include "block_types.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdarg.h>
// 日志回调
typedef void (*LogCallback)(const char* msg, int level);
static LogCallback g_log_cb = NULL;
static void log_info(const char* fmt, ...) {
char buf[512]; va_list args; va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args); va_end(args);
if (g_log_cb) g_log_cb(buf, 0); else printf("[INFO] %s\n", buf);
}
static void log_error(const char* fmt, ...) {
char buf[512]; va_list args; va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args); va_end(args);
if (g_log_cb) g_log_cb(buf, 2); else printf("[ERROR] %s\n", buf);
}
void executor_set_log_callback(LogCallback cb) { g_log_cb = cb; }
// 值辅助
static SlotValue make_number(double v) { SlotValue v2; v2.type = SLOT_VALUE_NUMBER; v2.as.number = v; return v2; }
static SlotValue make_string(const char* s) { SlotValue v; v.type = SLOT_VALUE_STRING; strncpy(v.as.string, s, 255); return v; }
static SlotValue make_bool(int b) { SlotValue v; v.type = SLOT_VALUE_BOOLEAN; v.as.boolean = b; return v; }
static SlotValue make_vec3(float x, float y, float z) { SlotValue v; v.type = SLOT_VALUE_VECTOR3; v.as.vector3.x = x; v.as.vector3.y = y; v.as.vector3.z = z; return v; }
// 连接追踪:获取槽位的输入值(通过连线回溯)
static SlotValue get_connected_value(BlockSlot* slot) {
if (slot->connected) return slot->connected->value;
return slot->value;
}
BlockExecutor* executor_create(BlockWorkspace* ws) {
BlockExecutor* exec = (BlockExecutor*)calloc(1, sizeof(BlockExecutor));
@@ -14,157 +46,331 @@ BlockExecutor* executor_create(BlockWorkspace* ws) {
return exec;
}
void executor_destroy(BlockExecutor* exec) {
free(exec);
}
void executor_destroy(BlockExecutor* exec) { free(exec); }
void executor_start(BlockExecutor* exec) {
exec->state = EXEC_RUNNING;
exec->call_stack_top = 0;
exec->current_block = NULL;
// 找到 on_start 事件积木
for (int i = 0; i < exec->workspace->block_count; i++) {
Block* block = exec->workspace->blocks[i];
if (block->type == BLOCK_EVENT_START) {
exec->current_block = block;
break;
log_info("执行器启动: 找到 on_start 事件");
return;
}
}
}
void executor_stop(BlockExecutor* exec) {
log_error("未找到 on_start 事件积木");
exec->state = EXEC_IDLE;
exec->current_block = NULL;
}
void executor_pause(BlockExecutor* exec) {
if (exec->state == EXEC_RUNNING) {
exec->state = EXEC_PAUSED;
}
void executor_stop(BlockExecutor* exec) { exec->state = EXEC_IDLE; exec->current_block = NULL; }
void executor_pause(BlockExecutor* exec) { if (exec->state == EXEC_RUNNING) exec->state = EXEC_PAUSED; }
void executor_resume(BlockExecutor* exec) { if (exec->state == EXEC_PAUSED) exec->state = EXEC_RUNNING; }
// 获取下一个执行的积木(通过输出端口连接)
static Block* next_from_output(Block* block, int output_index) {
if (output_index >= block->output_count) return NULL;
BlockSlot* out = &block->outputs[output_index];
if (out->connected && out->connected->owner) return out->connected->owner;
return NULL;
}
void executor_resume(BlockExecutor* exec) {
if (exec->state == EXEC_PAUSED) {
exec->state = EXEC_RUNNING;
}
// 获取值输入
static SlotValue get_input(Block* block, int idx) {
if (idx >= block->input_count) return make_number(0);
return get_connected_value(&block->inputs[idx]);
}
void executor_step(BlockExecutor* exec) {
if (!exec->current_block) return;
// 执行当前积木
Block* current = exec->current_block;
if (!exec->current_block) { exec->state = EXEC_IDLE; return; }
Block* cur = exec->current_block;
Block* next = NULL;
switch (current->type) {
switch (cur->type) {
// === 事件 ===
case BLOCK_EVENT_START:
case BLOCK_EVENT_UPDATE:
// 执行语句输出连接的积木
if (current->output_count > 0) {
BlockSlot* out = &current->outputs[0];
if (out->connected && out->connected->owner) {
next = out->connected->owner;
}
}
case BLOCK_EVENT_COLLISION:
case BLOCK_EVENT_MOUSE_CLICK:
next = next_from_output(cur, 0);
break;
case BLOCK_ACTION_MOVE:
// 获取参数
if (current->input_count >= 4) {
SlotValue x = block_slot_get_value(&current->inputs[1]);
SlotValue y = block_slot_get_value(&current->inputs[2]);
SlotValue z = block_slot_get_value(&current->inputs[3]);
// TODO: 实际移动物体
}
next = current->next;
break;
case BLOCK_CONTROL_IF:
if (current->input_count >= 2) {
SlotValue cond = block_slot_get_value(&current->inputs[1]);
if (cond.as.boolean) {
// 执行 Then 分支
if (current->output_count > 0) {
BlockSlot* out = &current->outputs[0];
if (out->connected && out->connected->owner) {
next = out->connected->owner;
}
}
} else {
// 执行 Else 分支
if (current->output_count > 1) {
BlockSlot* out = &current->outputs[1];
if (out->connected && out->connected->owner) {
next = out->connected->owner;
}
}
}
}
break;
case BLOCK_OP_ADD:
if (current->input_count >= 2) {
SlotValue a = block_slot_get_value(&current->inputs[0]);
SlotValue b = block_slot_get_value(&current->inputs[1]);
SlotValue result;
result.type = SLOT_VALUE_NUMBER;
result.as.number = a.as.number + b.as.number;
if (current->output_count > 0) {
current->outputs[0].value = result;
}
}
break;
case BLOCK_VAR_SET:
if (current->input_count >= 2) {
SlotValue val = block_slot_get_value(&current->inputs[1]);
// TODO: 获取变量名
}
next = current->next;
break;
default:
next = current->next;
case BLOCK_EVENT_KEY_PRESSED: {
const char* key = get_input(cur, 0).as.string;
log_info("事件: 按键 '%s' (需要引擎集成)", key);
next = next_from_output(cur, 0);
break;
}
case BLOCK_EVENT_TIMER: {
double interval = get_input(cur, 0).as.number;
log_info("事件: 定时器 %.1fs (需要引擎集成)", interval);
next = next_from_output(cur, 0);
break;
}
// === 动作 ===
case BLOCK_ACTION_MOVE: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("移动物体: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_ROTATE: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("旋转物体: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_SCALE: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("缩放物体: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_SET_POSITION: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("设置位置: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_SET_ROTATION: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("设置旋转: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_PLAY_SOUND: {
const char* name = get_input(cur, 1).as.string;
log_info("播放音效: '%s'", name);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_DESTROY:
log_info("销毁物体");
next = next_from_output(cur, 0);
break;
case BLOCK_ACTION_INSTANTIATE: {
const char* prefab = get_input(cur, 1).as.string;
log_info("实例化预制体: '%s'", prefab);
next = next_from_output(cur, 0);
break;
}
// === 控制流 ===
case BLOCK_CONTROL_IF:
case BLOCK_CONTROL_IF_ELSE: {
int cond = get_input(cur, 1).as.boolean;
log_info("如果 %s", cond ? "真 -> Then" : "假 -> Else");
next = next_from_output(cur, cond ? 0 : 1);
break;
}
case BLOCK_CONTROL_REPEAT: {
int cond = get_input(cur, 1).as.boolean;
if (cond) next = next_from_output(cur, 0);
else next = next_from_output(cur, 0); // 退出循环: 走输出
break;
}
case BLOCK_CONTROL_REPEAT_TIMES: {
double times = get_input(cur, 1).as.number;
// 简化: 只执行一次,实际需要计数器
log_info("循环 %.0f 次 (简化: 执行一次)", times);
next = next_from_output(cur, 0);
break;
}
case BLOCK_CONTROL_WHILE: {
int cond = get_input(cur, 1).as.boolean;
if (cond) next = next_from_output(cur, 0);
break;
}
case BLOCK_CONTROL_WAIT: {
double secs = get_input(cur, 1).as.number;
log_info("等待 %.1f 秒", secs);
exec->wait_timer = secs;
exec->state = EXEC_WAITING;
exec->current_block = next_from_output(cur, 0);
return;
}
case BLOCK_CONTROL_FUNCTION_DEF:
log_info("函数定义 (跳过)");
next = next_from_output(cur, 0);
break;
// === 运算 ===
case BLOCK_OP_ADD: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(a + b);
log_info("%.1f + %.1f = %.1f", a, b, a + b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_SUB: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(a - b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_MUL: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(a * b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_DIV: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(b != 0 ? a / b : 0);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_MOD: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(b != 0 ? fmod(a, b) : 0);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_EQUALS: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a == b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_NOT_EQUALS: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a != b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_GREATER: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a > b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_LESS: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a < b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_AND: {
int a = get_input(cur, 0).as.boolean;
int b = get_input(cur, 1).as.boolean;
cur->outputs[0].value = make_bool(a && b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_OR: {
int a = get_input(cur, 0).as.boolean;
int b = get_input(cur, 1).as.boolean;
cur->outputs[0].value = make_bool(a || b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_NOT: {
int a = get_input(cur, 0).as.boolean;
cur->outputs[0].value = make_bool(!a);
next = next_from_output(cur, 0);
break;
}
// === 变量 ===
case BLOCK_VAR_GET: {
const char* name = cur->label;
SlotValue val = executor_get_variable(exec, name);
cur->outputs[0].value = val;
log_info("读取变量 '%s' = %.2f", name, val.as.number);
next = next_from_output(cur, 0);
break;
}
case BLOCK_VAR_SET: {
const char* name = cur->label;
SlotValue val = get_input(cur, 1);
executor_set_variable(exec, name, val);
log_info("设置变量 '%s' = %.2f", name, val.as.number);
next = next_from_output(cur, 0);
break;
}
case BLOCK_VAR_CREATE: {
const char* name = cur->label;
executor_create_variable(exec, name);
log_info("创建变量 '%s' = 0", name);
next = next_from_output(cur, 0);
break;
}
// === 函数 ===
case BLOCK_FUNC_CALL: {
const char* fname = get_input(cur, 1).as.string;
log_info("调用函数 '%s'", fname);
if (exec->call_stack_top < MAX_CALL_STACK) {
exec->call_stack[exec->call_stack_top].block = cur;
exec->call_stack_top++;
}
// TODO: 查找函数定义积木并跳转
next = next_from_output(cur, 0);
break;
}
case BLOCK_FUNC_RETURN: {
if (exec->call_stack_top > 0) {
exec->call_stack_top--;
Block* caller = exec->call_stack[exec->call_stack_top].block;
next = next_from_output(caller, 0);
}
break;
}
default:
log_error("未知积木类型: %d", cur->type);
next = next_from_output(cur, 0);
break;
}
exec->current_block = next;
if (!exec->current_block) {
log_info("执行完成");
exec->state = EXEC_IDLE;
}
}
void executor_update(BlockExecutor* exec, double delta_time) {
if (exec->state != EXEC_RUNNING) return;
// 处理等待
if (exec->state == EXEC_WAITING) {
exec->wait_timer -= delta_time;
if (exec->wait_timer <= 0) {
exec->state = EXEC_RUNNING;
}
if (exec->wait_timer <= 0) exec->state = EXEC_RUNNING;
return;
}
// 执行多步以避免卡顿
if (exec->state != EXEC_RUNNING) return;
int steps = 0;
while (exec->current_block && steps < 100) {
while (exec->current_block && exec->state == EXEC_RUNNING && steps < 1000) {
executor_step(exec);
steps++;
}
}
void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue value) {
// 查找现有变量
for (int i = 0; i < exec->variable_count; i++) {
if (strcmp(exec->variables[i].name, name) == 0) {
exec->variables[i].value = value;
return;
}
}
// 创建新变量
if (exec->variable_count < MAX_VARIABLES) {
Variable* var = &exec->variables[exec->variable_count++];
strncpy(var->name, name, 63);
@@ -175,17 +381,11 @@ void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue valu
SlotValue executor_get_variable(BlockExecutor* exec, const char* name) {
for (int i = 0; i < exec->variable_count; i++) {
if (strcmp(exec->variables[i].name, name) == 0) {
return exec->variables[i].value;
}
if (strcmp(exec->variables[i].name, name) == 0) return exec->variables[i].value;
}
SlotValue empty = {0};
return empty;
return make_number(0);
}
void executor_create_variable(BlockExecutor* exec, const char* name) {
SlotValue val = {0};
val.type = SLOT_VALUE_NUMBER;
val.as.number = 0;
executor_set_variable(exec, name, val);
executor_set_variable(exec, name, make_number(0));
}
+4
View File
@@ -68,3 +68,7 @@ void executor_update(BlockExecutor* exec, double delta_time);
void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue value);
SlotValue executor_get_variable(BlockExecutor* exec, const char* name);
void executor_create_variable(BlockExecutor* exec, const char* name);
// 日志回调
typedef void (*LogCallback)(const char* msg, int level);
void executor_set_log_callback(LogCallback cb);
+2 -3
View File
@@ -26,10 +26,9 @@ void workspace_destroy(BlockWorkspace* ws) {
int workspace_add_block(BlockWorkspace* ws, Block* block) {
if (ws->block_count >= MAX_WORKSPACE_BLOCKS) return -1;
if (!block) return -1;
block->id = ws->next_block_id++;
if (block->id <= 0) block->id = ws->next_block_id++;
else if (block->id >= ws->next_block_id) ws->next_block_id = block->id + 1;
block->workspace = ws;
ws->blocks[ws->block_count++] = block;
return block->id;
}