golang实现Lua 5.1虚拟机和编译器的插件库gopher-lua的使用
Golang 实现 Lua 5.1 虚拟机和编译器的插件库 gopher-lua 使用指南
GopherLua 是一个用 Go 语言实现的 Lua 5.1 虚拟机(包含 Lua 5.2 的 goto 语句)。它的目标与 Lua 相同:成为一个具有可扩展语义的脚本语言。它提供了 Go API,可以轻松地将脚本语言嵌入到您的 Go 宿主程序中。
设计原则
- 成为一个具有可扩展语义的脚本语言
- 用户友好的 Go API
- GopherLua API 不是基于栈的 API,而是优先考虑用户友好性而非性能
性能
GopherLua 的性能不算快但也不算太慢,在微基准测试中与 Python3 性能相当或略好。
安装
go get github.com/yuin/gopher-lua
GopherLua 支持 Go1.9 及以上版本。
基本用法
导入包
import (
"github.com/yuin/gopher-lua"
)
运行 Lua 脚本
L := lua.NewState()
defer L.Close()
if err := L.DoString(`print("hello")`); err != nil {
panic(err)
}
或者从文件运行:
L := lua.NewState()
defer L.Close()
if err := L.DoFile("hello.lua"); err != nil {
panic(err)
}
数据模型
GopherLua 中的所有数据都是 LValue
类型。LValue
是一个接口类型,具有以下方法:
String() string
Type() LValueType
数据类型
类型名 | Go 类型 | Type() 值 | 常量 |
---|---|---|---|
LNilType | (常量) | LTNil | LNil |
LBool | (常量) | LTBool | LTrue, LFalse |
LNumber | float64 | LTNumber | - |
LString | string | LTString | - |
LFunction | struct 指针 | LTFunction | - |
LUserData | struct 指针 | LTUserData | - |
LState | struct 指针 | LTThread | - |
LTable | struct 指针 | LTTable | - |
LChannel | chan LValue | LTChannel | - |
类型检查示例
lv := L.Get(-1) // 获取栈顶的值
if str, ok := lv.(lua.LString); ok {
// lv 是 LString
fmt.Println(string(str))
}
if lv.Type() != lua.LTString {
panic("string required.")
}
完整示例
从 Go 调用 Lua 函数
func main() {
L := lua.NewState()
defer L.Close()
// 加载并执行 Lua 脚本
if err := L.DoString(`
function add(a, b)
return a + b
end
`); err != nil {
panic(err)
}
// 调用 Lua 函数
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("add"), // 获取函数
NRet: 1, // 指定返回值数量
Protect: true, // 如果出错返回错误
}, lua.LNumber(10), lua.LNumber(20)); err != nil {
panic(err)
}
// 获取返回值
ret := L.Get(-1)
L.Pop(1) // 从栈中移除返回值
if num, ok := ret.(lua.LNumber); ok {
fmt.Println("Result:", float64(num)) // 输出: Result: 30
}
}
从 Lua 调用 Go 函数
func Double(L *lua.LState) int {
lv := L.ToInt(1) // 获取第一个参数
L.Push(lua.LNumber(lv * 2)) // 将结果压入栈
return 1 // 返回结果数量
}
func main() {
L := lua.NewState()
defer L.Close()
// 将 Go 函数注册为 Lua 全局函数
L.SetGlobal("double", L.NewFunction(Double))
// 调用 Lua 代码
if err := L.DoString(`print(double(20))`); err != nil {
panic(err)
}
// 输出: 40
}
创建自定义模块
mymodule.go:
package mymodule
import "github.com/yuin/gopher-lua"
func Loader(L *lua.LState) int {
// 注册函数到表
mod := L.SetFuncs(L.NewTable(), exports)
// 注册其他内容
L.SetField(mod, "name", lua.LString("value"))
// 返回模块
L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"myfunc": myfunc,
}
func myfunc(L *lua.LState) int {
return 0
}
main.go:
package main
import (
"./mymodule"
"github.com/yuin/gopher-lua"
)
func main() {
L := lua.NewState()
defer L.Close()
L.PreloadModule("mymodule", mymodule.Loader)
if err := L.DoFile("main.lua"); err != nil {
panic(err)
}
}
main.lua:
local m = require("mymodule")
m.myfunc()
print(m.name) -- 输出: value
协程支持
L := lua.NewState()
defer L.Close()
// 加载协程函数
L.DoString(`
function coro()
local i = 0
while true do
coroutine.yield(i)
i = i+1
end
return i
end
`)
// 创建新线程
co, _ := L.NewThread()
fn := L.GetGlobal("coro").(*lua.LFunction)
// 恢复协程执行
for {
st, err, values := L.Resume(co
更多关于golang实现Lua 5.1虚拟机和编译器的插件库gopher-lua的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现Lua 5.1虚拟机和编译器的插件库gopher-lua的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用gopher-lua实现Golang中的Lua 5.1虚拟机
gopher-lua是一个纯Go实现的Lua 5.1虚拟机,它允许在Go应用程序中嵌入Lua脚本功能。下面我将详细介绍如何使用这个库。
基本安装
首先安装gopher-lua:
go get github.com/yuin/gopher-lua
基本用法示例
package main
import (
"fmt"
"github.com/yuin/gopher-lua"
)
func main() {
// 创建新的Lua虚拟机
L := lua.NewState()
defer L.Close() // 确保虚拟机被关闭
// 执行简单的Lua代码
if err := L.DoString(`print("Hello from Lua!")`); err != nil {
panic(err)
}
// 更复杂的示例
script := `
function add(a, b)
return a + b
end
result = add(10, 20)
print("The result is: "..result)
`
if err := L.DoString(script); err != nil {
panic(err)
}
// 从Go中获取Lua变量
lv := L.GetGlobal("result")
if lv.Type() != lua.LTNumber {
panic("result is not a number")
}
if v, ok := lv.(lua.LNumber); ok {
fmt.Printf("Result from Go: %.0f\n", v)
}
}
Go与Lua交互
从Go调用Lua函数
func main() {
L := lua.NewState()
defer L.Close()
script := `
function greet(name)
return "Hello, " .. name
end
`
if err := L.DoString(script); err != nil {
panic(err)
}
// 调用Lua函数
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("greet"), // 获取函数
NRet: 1, // 指定返回值数量
Protect: true, // 如果为true,调用在panic时会被恢复
}, lua.LString("Gopher")); err != nil {
panic(err)
}
// 获取返回值
ret := L.Get(-1)
L.Pop(1) // 从栈中移除返回值
fmt.Println(ret.String())
}
从Lua调用Go函数
func main() {
L := lua.NewState()
defer L.Close()
// 注册Go函数到Lua
L.SetGlobal("add", L.NewFunction(func(L *lua.LState) int {
a := L.ToInt(1) // 获取第一个参数
b := L.ToInt(2) // 获取第二个参数
L.Push(lua.LNumber(a + b)) // 压入返回值
return 1 // 返回值的数量
}))
// 在Lua中调用Go函数
if err := L.DoString(`
local result = add(10, 20)
print("10 + 20 = " .. result)
`); err != nil {
panic(err)
}
}
加载Lua模块
gopher-lua支持加载预编译的Lua模块:
func main() {
L := lua.NewState()
defer L.Close()
// 预加载模块
L.PreloadModule("mymodule", func(L *lua.LState) int {
mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
"hello": func(L *lua.LState) int {
L.Push(lua.LString("Hello from module!"))
return 1
},
})
L.Push(mod)
return 1
})
// 在Lua中使用模块
if err := L.DoString(`
local m = require("mymodule")
print(m.hello())
`); err != nil {
panic(err)
}
}
性能考虑
- 复用Lua虚拟机:创建和销毁Lua虚拟机开销较大,应该尽可能复用
- 预编译脚本:对于频繁执行的脚本,可以预编译
- 限制执行时间:对于不可信脚本,应该设置执行超时
func main() {
L := lua.NewState()
defer L.Close()
// 预编译脚本
chunk, err := L.LoadString(`return 1 + 1`)
if err != nil {
panic(err)
}
// 多次执行预编译的脚本
for i := 0; i < 5; i++ {
L.Push(chunk)
if err := L.PCall(0, 1, nil); err != nil {
panic(err)
}
ret := L.Get(-1)
L.Pop(1)
fmt.Println(ret)
}
}
错误处理
func main() {
L := lua.NewState()
defer L.Close()
// 设置错误处理函数
L.SetGlobal("error_handler", L.NewFunction(func(L *lua.LState) int {
err := L.ToString(1)
fmt.Printf("Lua error: %s\n", err)
return 0
}))
// 在Lua中使用pcall捕获错误
if err := L.DoString(`
function risky()
error("something went wrong")
end
local ok, err = pcall(risky)
if not ok then
error_handler(err)
end
`); err != nil {
panic(err)
}
}
实际应用场景
gopher-lua适用于:
- 游戏脚本系统
- 业务规则引擎
- 动态配置系统
- 插件系统
通过gopher-lua,你可以轻松地在Go应用中嵌入Lua脚本功能,实现高度灵活的配置和扩展能力。