package LuaCangjie_api import std.collection.* // ==================== FFI底层 ==================== type IOCallback = CFunc<() -> Int32> @C foreign func init_lua_runner(pathio: CString, pkgpath: CString, input: IOCallback, output: IOCallback): CPointer @C foreign func load_lib(selfd: CPointer, path: CString, name: CString): Int32 @C foreign func unload_lib(selfd: CPointer, name: CString): Int32 @C foreign func run(selfd: CPointer, path: CString, arg: CString): Int32 @C foreign func cleanup(selfd: CPointer): Int32 @C foreign func get_errno(): Int32 @C foreign func getresult(selfd: CPointer): CString @C foreign func free_lua(selfd: CPointer): Int32 // ==================== 工具函数 ==================== @C func defaultIO(): Int32 { // 默认输入回调,返回0表示成功 0 } private func toCStr(s: String): CString{ if (s.isEmpty()) { var tmp = CString(CPointer()); return tmp } else { unsafe { LibC.mallocCString(s) } } } // CString转String:通过 CPointer 构造 CString 后调用 toString() private func toString(ptr: CString): String { if (ptr.isNull()) { return "" } // 将 CPointer 强制转换为 CPointer // 通过 CPointer 构造 CString return ptr.toString() } // ==================== 异常 ==================== public class LuaError <: Exception { public let code: Int32 public init(code: Int32, msg: String) { super(msg); this.code = code } } // ==================== 主类 ==================== public class LuaRunner { private var handle: CPointer private var cachedResult: String = "" private var ioInput: IOCallback private var ioOutput: IOCallback //pkgpath 重定向lua状态机包搜索路径 //pathio 重定向io输出到pathio路径下 //@param output 类型function 用于lua内部触发输出时调用的回调函数 //@param input 类型function 用于lua内部触发输入时的调用的回调函数 //@brief 构造lua状态机 public init( input!: IOCallback = defaultIO, output!: IOCallback = defaultIO,pathio!: String='' , pkgpath!: String='') { this.ioInput = input this.ioOutput = output var tmpio = toCStr(pathio); var tmppkg = toCStr(pkgpath); this.handle = unsafe { init_lua_runner(tmpio,tmppkg, input, output) } unsafe{ LibC.free(tmpio); LibC.free(tmppkg); } if (this.handle.isNull()) { throw LuaError(-1, "Init failed") } } ~init() { if (!this.handle.isNull()) { unsafe { free_lua(this.handle) } this.handle = CPointer() } } public func load(path: String, name: String): This { let result = unsafe { load_lib(this.handle, toCStr(path), toCStr(name)) } if (result != 0) { throw LuaError(unsafe { get_errno() }, toString(unsafe { getresult(this.handle) })) } return this } public func unload(name: String): This { let result = unsafe { unload_lib(this.handle, toCStr(name)) } if (result != 0) { throw LuaError(unsafe { get_errno() }, toString(unsafe { getresult(this.handle) })) } return this } public func runScript(path: String, arg: String): String { let code = unsafe { run(this.handle, toCStr(path), toCStr(arg)) } this.cachedResult = toString(unsafe { getresult(this.handle) }) if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) } return this.cachedResult } public func result(): String { this.cachedResult } public func error(): String { toString(unsafe { getresult(this.handle) }) } public func clear(): This { let result = unsafe { cleanup(this.handle) } if (result != 0) { throw LuaError(unsafe { get_errno() }, "Cleanup failed") } return this } }