GopherLua is a Lua5.1 VM and compiler written in Go. GopherLua has a same goal with Lua: Be a scripting language with extensible semantics . It provides a Go APIs that allow you to easily embed a scripting language to your Go host programs.
- Be a scripting language with extensible semantics.
- User-friendly Go API
- The stack besed API like the one used in the original Lua implementation will cause a performance improvements in GopherLua (It will reduce memory allocations and concrete type <-> interface conversions). GopherLua API is not the stack based API. GopherLua give preference to the user-friendliness over the performance.
Performance measurements in script languages on Go.
Summary
| prog | time |
|---|---|
| otto | 0m24.848s |
| anko | 0m20.207s |
| GopherLua | 0m1.248s |
GopherLua 20x faster than other implementations in this benchmark.
fib.js
function fib(n) {
if (n < 2) return n;
return fib(n - 2) + fib(n - 1);
}
console.log(fib(30));$ time otto fib.js
832040
real 0m24.848s
user 0m0.015s
sys 0m0.078sfib.ank
func fib(n) {
if n < 2 {
return n
}
return fib(n - 2) + fib(n - 1)
}
println(fib(30));
$ time anko fib.ank
832040
real 0m20.207s
user 0m0.030s
sys 0m0.078sfib.lua
local function fib(n)
if n < 2 then return n end
return fib(n - 2) + fib(n - 1)
end
print(fib(30))$ time glua fib.lua
832040
real 0m1.248s
user 0m0.015s
sys 0m0.187sgo get github.com/yuin/gopher-luaGopherLua APIs perform in much the same way as Lua, but the stack is used only for passing arguments and receiving returned values.
Import a package.
import (
"github.com/yuin/gopher-lua"
)Run scripts in the VM.
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)
}Refer to Lua Reference Manual and Go doc for further information.
All data in a GopherLua program is a LValue . LValue is an interface
type that has following methods.
String() stringType() LValueType
Objects implement a LValue interface are
| Type name | Go type | Type() value | Constants |
|---|---|---|---|
LNilType |
(constants) | LTNil |
LNil |
LBool |
(constants) | LTBool |
LTrue, LFalse |
LNumber |
float64 | LTNumber |
- |
LString |
string | LTString |
- |
LFunction |
struct pointer | LTFunction |
- |
LUserData |
struct pointer | LTUserData |
- |
LState |
struct pointer | LTThread |
- |
LTable |
struct pointer | LTTable |
- |
You can test an object type in Go way(type assertion) or using a Type() value.
lv := L.Get(-1) // get the value at the top of the stack
if str, ok := lv.(lua.LString); ok {
// lv is LString
fmt.Println(string(str))
}
if lv.Type() != lua.LTString {
panic("string required.")
}lv := L.Get(-1) // get the value at the top of the stack
if tbl, ok := lv.(*lua.LTable); ok {
// lv is LTable
fmt.Println(L.ObjLen(tbl))
}Note that LBool , LNumber , LString is not a pointer.
To test LNilType and LBool, You must use pre-defined constants.
lv := L.Get(-1) // get the value at the top of the stack
if lv == LTrue { // correct
}
if bl, ok == lv.(lua.LBool); ok && bool(bl) { // wrong
}In Lua, both nil and false make a condition false. LVIsFalse and LVAsBool implement this specification.
lv := L.Get(-1) // get the value at the top of the stack
if LVIsFalse(lv) { // lv is nil or false
}
if LVAsBool(lv) { // lv is neither nil nor false
}Objects that based on go structs(LFunction. LUserData, LTable)
have some public methods and fields. You can use these methods and fields for
performance and debugging, but there are some limitations.
- Metatable does not work.
- No error handlings.
Size of the callstack & registry is fixed for mainly performance. You can change the size of the callstack & registry.
lua.RegistrySize = 1024 * 20
lua.CallStackSize = 1024
L = lua.NewState()
defer L.Close()Refer to Lua Reference Manual and Go doc(LState methods) for further information.
func Double(L *lua.LState) int {
lv := L.ToInt(1) /* get argument */
L.Push(lua.LNumber(lv * 2)) /* push result */
return 1 /* number of results */
}
func main() {
L := lua.NewState()
defer L.Close()
L.SetGlobal("double", L.NewFunction(Double)) /* Original lua_setglobal uses stack... */
}print(double(20)) -- > "40"Any function registered with GopherLua is a lua.LGFunction, defined in value.go
type LGFunction func(*LState) intWorking with coroutines.
co := L.NewThread() /* create a new thread */
fn := L.GetGlobal("coro").(*lua.LFunction) /* get function from lua */
for {
st, err, values := L.Resume(co, fn)
if st == lua.ResumeError {
fmt.Println("yield break(error)")
fmt.Println(err.Error())
break
}
for i, lv := range values {
fmt.Printf("%v : %v\n", i, lv)
}
if st == lua.ResumeOK {
fmt.Println("yield break(ok)")
break
}
}mymodule.go
package mymodule
import (
"github.com/yuin/gopher-lua"
)
func Loader(L *lua.LState) int {
// register functions to the table
mod := L.SetFuncs(L.NewTable(), exports)
// register other stuff
L.SetField(mod, "name", lua.LString("value"))
// returns the module
L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"myfunc": myfunc,
}
func myfunc(L *lua.LState) int {
return 0
}mymain.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)L := lua.NewState()
defer L.Close()
if err := L.DoFile("double.lua"); err != nil {
panic(err)
}
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("double"),
NRet: 1,
Protect: true,
}, lua.LNumber(10)); err != nil {
panic(err)
}
ret := L.Get(-1) // returned value
L.Pop(1) // remove received valueIf Protect is false, GopherLua will panic instead of returning an error value.
- GopherLua uses the regexp package to implement the pattern match.
- The Pattern match only works for utf8 strings.
- The regexp package does not support back-references.
- The regexp package does not support position-captures.
GopherLua has an option to use the Go regexp syntax as a pattern match format.
lua.LuaRegex = false
L := lua.NewState()
defer L.Close()print(string.gsub("abc $!?", [[a(\w+)]], "${1}")) --> bc $!?string.dumpos.setlocalecollectgarbagelua_Debug.namewhatpackage.loadlib- debug hooks
file:setvbufdoes not support a line bufferring.
Lua has an interpreter called lua . GopherLua has an interpreter called glua .
go get github.com/yuin/gopher-lua/cmd/gluaglua has same options as lua .
MIT
Yusuke Inuzuka