feat: standalone .ly interpreter - parses blocks/wires, executes flow control
This commit is contained in:
1 parent
c4815c3c69
commit
f85c7e37f1
5 files changed
+325
-116
No files matched your search
@@ -61,3 +61,7 @@ set_target_properties(lity3d_editor PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_
|
||||
add_executable(lity3d_compat WIN32 Demo/compat_test.c)
|
||||
target_link_libraries(lity3d_compat ${LINK_LIBS})
|
||||
set_target_properties(lity3d_compat PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(ly_interpreter Demo/ly_interpreter.c)
|
||||
target_link_libraries(ly_interpreter Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
set_target_properties(ly_interpreter PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
+3
-1
@@ -76,7 +76,9 @@ static void on_render(Engine* engine) {
|
||||
shader_set_matrix(g_shader, ctx, (float*)&proj_t);
|
||||
mesh_draw(g_tri, ctx);
|
||||
|
||||
ctx->lpVtbl->OMSetDepthStencilState(ctx, g_no_depth, 0);
|
||||
{
|
||||
ctx->lpVtbl->OMSetDepthStencilState(ctx, g_no_depth, 0);
|
||||
}
|
||||
|
||||
draw_quad(ctx, sw - 200, 10, 180, 60, 1, 1, 1, 1);
|
||||
|
||||
|
||||
@@ -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 = ¤t->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(¤t->inputs[1]);
|
||||
SlotValue y = block_slot_get_value(¤t->inputs[2]);
|
||||
SlotValue z = block_slot_get_value(¤t->inputs[3]);
|
||||
// TODO: 实际移动物体
|
||||
}
|
||||
next = current->next;
|
||||
break;
|
||||
|
||||
case BLOCK_CONTROL_IF:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue cond = block_slot_get_value(¤t->inputs[1]);
|
||||
if (cond.as.boolean) {
|
||||
// 执行 Then 分支
|
||||
if (current->output_count > 0) {
|
||||
BlockSlot* out = ¤t->outputs[0];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 执行 Else 分支
|
||||
if (current->output_count > 1) {
|
||||
BlockSlot* out = ¤t->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(¤t->inputs[0]);
|
||||
SlotValue b = block_slot_get_value(¤t->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(¤t->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));
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user