最近熟悉 go 项目时,发现项目中有用到 github.com/yuin/gopher-lua 这个包,之前并没有接触过,特意去看了官方文档和找了些网上的资料,特此记录下。
本次介绍计划分为两篇文章,这一次主要介绍 github.com/yuin/gopher-lua 这个包的介绍以及基础使用,下一边将介绍 github.com/yuin/gopher-lua 是如何在项目中使用的。如有不对的地方,请不吝赐教,谢谢。
文章中的 gopher-lua 如果没有特别说明,即为:github.com/yuin/gopher-lua。
1、 gopher-lua 基础介绍
我们先开看看官方是如何介绍自己的:
GopherLua is a Lua5.1(+ goto statement in Lua5.2) VM and compiler written in Go. GopherLua has a same goal with Lua: Be a scripting language with extensible semantics . It provides Go APIs that allow you to easily embed a scripting language to your Go host programs.
GopherLua是一个Lua5.1(Lua5.2中的+goto语句)虚拟机和用Go编写的编译器。GopherLua与Lua有着相同的目标:成为一种具有可扩展语义的脚本语言。它提供了Go API,允许您轻松地将脚本语言嵌入到Go主机程序中。
看上面的翻译还是有点抽象,说说自己的理解。 github.com/yuin/gopher-lua 是一个纯 Golang 实现的 Lua 虚拟机,它能够很轻松的在 go 写的程序中调用 lua 脚本。另外提一嘴,使用插件后,也能够在 lua 脚本中调用 go 写好的代码。挺秀的!
接下来我们看一看, github.com/yuin/gopher-lua 的性能如何,这里就直接引用官方自己做的测试来介绍。详情见 wiki page 链接。点进链接过后,发现性能还不错,执行效率和性能仅比 C 实现的 bindings 差点。
官方测试例子是生成斐波那契数列
,测试执行结果如下:
prog | time |
---|---|
anko | 182.73s |
otto | 173.32s |
go-lua | 8.13s |
Python3.4 | 5.84s |
GopherLua | 5.40s |
lua5.1.4 | 1.71s |
2、 gopher-lua 简单使用
下面的介绍,都是基于 v1.1.0
版本进行的。
go get github.com/yuin/gopher-lua@v1.1.0
Go
的版本需要 >= 1.9
2.1 gopher-lua 中的 hello world
这里写一个简单的程序,了解 gopher-lua 是如何使用的。
package main
import (
lua "github.com/yuin/gopher-lua"
)
func main() {
// 1、创建 lua 的虚拟机
L := lua.NewState()
// 执行完毕后关闭虚拟机
defer L.Close()
// 2、加载fib.lua
if err := L.DoString(`print("hello world")`); err != nil {
panic(err)
}
}
执行结果:
hello world
看到这里,感觉没啥特别的地方,接下来,我们看一看 gopher-lua
如何调用事先写好的 lua脚本
。
fib.lua
脚本内容:
function fib(n)
if n < 2 then return n end
return fib(n-1) + fib(n-2)
end
main.go
package main
import (
"fmt"
lua "github.com/yuin/gopher-lua"
)
func main() {
// 1、创建 lua 的虚拟机
L := lua.NewState()
defer L.Close()
// 加载fib.lua
if err := L.DoFile(`fib.lua`); err != nil {
panic(err)
}
// 调用fib(n)
err := L.CallByParam(lua.P{
Fn: L.GetGlobal("fib"), // 获取fib函数引用
NRet: 1, // 指定返回值数量
Protect: true, // 如果出现异常,是panic还是返回err
}, lua.LNumber(10)) // 传递输入参数n
if err != nil {
panic(err)
}
// 获取返回结果
ret := L.Get(-1)
// 从堆栈中扔掉返回结果
// 这里一定要注意,不调用此方法,后续再调用 L.Get(-1) 获取的还是上一次执行的结果
// 这里大家可以自己测试下
L.Pop(1)
// 打印结果
res, ok := ret.(lua.LNumber)
if ok {
fmt.Println(int(res))
} else {
fmt.Println("unexpected result")
}
}
执行结果:
55
从上面我们已经能够感受到部分 gopher-lua
的魅力了。接下来,我们就一起详细的学习学习 gopher-lua
。
2.2 gopher-lua 中的数据类型
All data in a GopherLua program is an LValue
. LValue
is an interface type that has following methods.
GopherLua程序中的所有数据都是一个LValue。LValue是一种具有以下方法的接口类型。
String() string
Type() LValueType
// value.go:29
type LValue interface {
String() string
Type() LValueType
// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).
assertFloat64() (float64, bool)
// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).
assertString() (string, bool)
// to reduce `runtime.assertI2T2` costs, this method should be used instead of the type assertion in heavy paths(typically inside the VM).
assertFunction() (*LFunction, bool)