mirror of
https://gitcode.com/JianFeeeee/LuaCangjia_api.git
synced 2026-09-19 16:42:48 +00:00
- 新增 loadFunction/callFunction/unloadFunction 预加载函数 API - 预加载语义:加载文件→顶层执行一次→返回函数存入Lua Registry→可多次调用 - 与管道模式(load/runScript)存储完全解耦:funcs[]+Registry vs pkgs[]+虚拟栈 - 新增错误码 5023/5024/5025 - 新增单元测试:仓颉侧 8 个 + C++ GTest 侧 11 个 - 本机以 Cangjie 1.1.0 实编译并通过功能运行验证 - README/doc 增加 AI 辅助编程标识及预加载函数模式说明
10 KiB
10 KiB
API 文档
类:LuaRunner
Lua 虚拟机的主要管理类。
构造函数
public init(input: IOCallback = defaultIO, output: IOCallback = defaultIO, pathio: String = "", pkgpath: String = "")
- 参数:
input:IOCallback类型。Lua 调用io.read时触发的回调函数。output:IOCallback类型。Lua 调用print或io.write时触发的回调函数。pathio:String类型。指定 I/O 重定向的文件交换目录路径。为空则不重定向。pkgpath:String类型。设置 Lua 的package.path,用于自定义模块搜索路径(如"./lib/?.lua")。
方法
| 方法 | 描述 | 返回值 |
|---|---|---|
load(path: String, name: String): This |
块压栈。加载 Lua 文件、编译成 chunk、压入虚拟栈并记录栈位置。name 仅作为内部标识。与管道模式配套使用。 |
实例本身 (This) |
loadFunction(path: String, name: String): This |
预加载函数到内存。加载文件 → 执行顶层代码(仅执行一次) → 捕获返回的函数对象 → 存入 Lua Registry。与 load 完全独立的两套存储(不共用 pkgs 栈),被加载的 Lua 文件顶层必须 return 一个函数。 |
实例本身 (This) |
callFunction(name: String, arg: String): String |
按名称调用预加载的函数。从 Registry 取出函数后 pcall,不消耗该函数,可多次重复调用。与管道模式互不干扰。 |
函数执行的字符串结果 |
unloadFunction(name: String): This |
卸载预加载的函数(释放 Registry 引用)。 | 实例本身 (This) |
unload(name: String): This |
从内部栈中卸载由 name 标识的块(仅适用于 load 加载的块)。 |
实例本身 (This) |
runScript(path: String, arg: String): String |
核心执行方法。 - 若 path 非空:加载并执行该脚本,arg 作为单参数传入。 - 若 path 为空:触发管道模式,调用栈顶函数,并按内部栈布局自动计算参数数量。 |
脚本执行的字符串结果 |
doString(target: String): String |
执行单条 Lua 代码字符串。target 为合法的 Lua 代码片段,必须返回字符串类型结果。 |
执行结果的字符串 |
clear(): This |
清理 Lua 状态机,清空全局变量和所有加载的库,恢复到初始状态。 | 实例本身 (This) |
result(): String |
获取最近一次 runScript 或 doString 成功执行的返回值。 |
字符串 |
error(): String |
获取最近一次错误的原始描述信息。 | 字符串 |
异常类:LuaError
所有 Lua 相关操作失败时抛出的异常。
- 属性:
code: Int32:错误码,用于程序逻辑判断。
- 方法:
getMessage(): String:获取错误的详细描述。
错误码对照表
| 错误码 | 宏定义 | 描述 |
|---|---|---|
| 5001 | NAPI_JSON_LOAD_FAIL |
JSON 文件加载失败。 |
| 5002 | NAPI_MALLOC_FAIL |
内存分配失败。 |
| 5003 | NAPI_LOAD_FILE_ERROR |
文件加载错误,包含路径、权限以及脚本编译期语法错误。 |
| 5004 | NAPI_SCRIPT_RUNNER_INVALID |
脚本运行器初始化失败或实例无效。 |
| 5005 | NAPI_LUA_STATE_ERROR |
Lua 状态机初始化失败或已损坏。 |
| 5006 | NAPI_JSON_RESOLVE_FAIL |
JSON 解析错误,可能是格式问题。 |
| 5007 | NAPI_JSON_FORMATE_ERROR |
JSON 格式错误,不符合 MCP 常见格式。 |
| 5008 | NAPI_MISSING_JSON_ARG |
无法从 JSON 中解析到所需参数。 |
| 5009 | NAPI_LUA_STACK_NOSPACE |
Lua 栈空间不足。 |
| 5010 | NAPI_SCRIPT_ERROR |
脚本内部错误,请通过 result() 获取详细信息。 |
| 5011 | NAPI_SCRIPT_BAD_RET |
脚本返回值错误,当前版本仅支持字符串返回。 |
| 5012 | NAPI_UNLOADLIB_FAIL |
尝试卸载未加载的库。 |
| 5013 | NAPI_LUAFUN_NOFOUND |
调用不符合单输入单输出约定。 |
| 5014 | NAPI_LUA_STACK_ERROR |
函数调用时栈大小小于最小约定大小。 |
| 5015 | NAPI_LUA_INITFAIL |
Lua 状态机初始化错误。 |
| 5016 | NAPI_LUALIB_LOAD_OVER_STACK |
加载的库数量超过上限(最大 20 个)。 |
| 5017 | NAPI_LUA_CLASS_LOST |
底层 LuaRunner 对象丢失。 |
| 5018 | NAPI_LUA_MISS_REDIRECT |
缺少用于重定向的文件路径。 |
| 5019 | NAPI_RESET_INPUT_FILE_ERROR |
重置输入文件失败,可能导致输入污染。 |
| 5020 | NAPI_ERROR_FUNCS |
回调函数执行过程中发生错误(含 doString 语法错误)。 |
| 5021 | NAPI_CHDIR_ERROR |
切换工作目录失败,可能导致包搜索错误,日志写入路径错误等。 |
| 5022 | NAPI_NOCHUNK_FOUND |
没有找到待调用的函数。 |
| 5023 | NAPI_FUNCTION_NOT_FOUND |
callFunction 调用时未找到指定名称的预加载函数。 |
| 5024 | NAPI_LOAD_FUNCTION_OVER |
预加载函数数量超过上限(最大 20 个)。 |
| 5025 | NAPI_FUNCTION_NOT_VALID |
预加载的文件顶层返回的不是函数。 |
预加载函数模式 (Preload Function Mode)
与管道模式不同,预加载模式适合“加载一次、多次调用”的场景。
调用规范
-
脚本编写:被预加载的 Lua 文件必须在顶层
return一个函数,函数的闭包状态(如计数器、配置)在预加载时初始化,后续调用不会重置。-- counter.lua local count = 0 -- 预加载时初始化一次 return function(arg) count = count + 1 return "arg=" .. arg .. " count=" .. count end -
预加载:
runner.loadFunction("counter.lua", "counter") -
多次调用:
runner.callFunction("counter", "x")可重复调用,count持续累加。 -
卸载:
runner.unloadFunction("counter")释放函数。
与管道模式的区别
| 维度 | load (管道模式) |
loadFunction (预加载模式) |
|---|---|---|
| 存储位置 | 虚拟栈(pkgs[],相对索引) |
Lua Registry(funcs[],绝对引用) |
| 顶层代码 | 每次调用都重新执行 | 预加载时执行一次 |
| 调用方式 | runScript("", arg) 触发 |
callFunction(name, arg) |
| 生命周期 | 调用后消耗(pkg_cont--) |
不消耗,可重复调用 |
| 与另一方关系 | 互不干扰(存储完全解耦) | 互不干扰(存储完全解耦) |
示例
let runner = LuaRunner()
// 预加载:顶层代码执行一次
runner.loadFunction("./scripts/counter.lua", "counter")
// 多次调用:计数器持续累加
println(runner.callFunction("counter", "a")) // arg=a count=1
println(runner.callFunction("counter", "b")) // arg=b count=2
println(runner.callFunction("counter", "c")) // arg=c count=3
// 卸载
runner.unloadFunction("counter")
管道模式 (Pipeline Mode)
这是该引擎的核心特性,允许数据在多个脚本间自动流转,无需在仓颉层进行手动传递。
调用规范
-
脚本编写:作为管道节点的 Lua 脚本,必须使用 Lua 的变长参数语法
...来接收上一个节点传递的数据。-- node_b.lua local input = ... return "处理结果: " .. input -
加载顺序(逆序):利用栈的后进先出(LIFO)特性,需按照执行顺序的逆序进行加载。
- 期望的执行顺序:
A->B->C - 仓颉侧加载顺序:
runner.load("C.lua", "C")runner.load("B.lua", "B")runner.load("A.lua", "A")
- 期望的执行顺序:
-
触发执行:
- 首先执行初始脚本,将其返回值压入栈顶。
- 然后调用
runScript("", ""),引擎将自动调用栈顶下方的函数(即最后加载的脚本B),并将栈顶数据作为参数传入。 B执行完毕后的返回值成为新的栈顶,等待下一次runScript调用。
运行时语义说明
- 当
path非空时,脚本会先完成编译并压入内部栈,再进入执行阶段。 - 若脚本在编译阶段发生语法错误,当前实现返回
NAPI_LOAD_FILE_ERROR,详细信息可通过result()获取。 - 在
require/package.path场景下,runScript返回当前 Lua 栈顶字符串结果;若栈顶内容是模块解析路径,则会直接返回该路径字符串。
管道模式示例
// 1. 按逆序加载:A -> B -> C
runner.load("./scripts/add_suffix.lua", "add_suffix") // 脚本 C: 添加后缀
runner.load("./scripts/to_upper.lua", "to_upper") // 脚本 B: 转大写
runner.load("./scripts/add_prefix.lua", "add_prefix") // 脚本 A: 添加前缀
// 2. 执行初始数据生成脚本
let initialData = runner.runScript("./scripts/generate_data.lua", "hello")
// 3. 依次触发管道节点
let afterA = runner.runScript("","")//数据经过A
let afterB = runner.runScript("", "") // 数据经过脚本 B (to_upper)
let finalResult = runner.runScript("", "") // 数据经过脚本 C (add_suffix)
println(finalResult) // 最终输出: [PREFIX] HELLO [SUFFIX]
I/O 重定向机制
当配置了 input、output 回调以及 pathio 路径时,Lua 的标准 I/O 将被重定向:
- 输出重定向:Lua 调用
print或io.write时,内容会写入{pathio}目录下的临时文件,并触发仓颉侧的output回调。您可以在回调中实现自己的输出逻辑(如写入日志、发送网络消息等)。 - 输入重定向:Lua 调用
io.read时,会触发仓颉侧的input回调。您需要在回调中准备并提供输入数据(例如从文件读取或返回模拟数据),供底层读取。
doString 使用说明
doString 允许直接执行 Lua 代码字符串,而无需写入文件。
基本用法
let runner = LuaRunner()
// 执行简单表达式
let res = runner.doString("return 1 + 1")
println(res) // 输出: 2
// 设置全局变量并读取
let res2 = runner.doString("g = 42; return g")
println(res2) // 输出: 42
注意事项
doString要求代码执行后栈顶必须是字符串类型的返回值- 若代码不返回字符串(如返回
nil、数字或无返回值),将抛出NAPI_SCRIPT_BAD_RET(5011) 错误 - 若代码存在语法错误,将抛出
NAPI_ERROR_FUNCS(5020) 错误 doString与runScript共享同一个 Lua 状态机,全局变量和已加载的模块可互相访问